diff options
| author | Quentin Smith <[email protected]> | 2023-07-17 21:31:43 -0400 |
|---|---|---|
| committer | Quentin Smith <[email protected]> | 2023-07-17 21:31:43 -0400 |
| commit | 6f02403184eb7fb7990fb88fc9df9c4328a690a3 (patch) | |
| tree | 748f510e190bb2724750507a6e69ed1a8e08cb20 /examples | |
| parent | d896f80405aa8963877049ed999e4aba25d6e2bb (diff) | |
| parent | 6b5df4523aa1c4902f02e803450ae4b418e0e3ca (diff) | |
Merge remote-tracking branch 'origin/main' into nrf-pdm
Diffstat (limited to 'examples')
330 files changed, 12366 insertions, 1953 deletions
diff --git a/examples/.cargo/config.toml b/examples/.cargo/config.toml new file mode 100644 index 000000000..84d266320 --- /dev/null +++ b/examples/.cargo/config.toml | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | [profile.release] | ||
| 2 | # Allows defmt to display log locations even in release | ||
| 3 | debug = true \ No newline at end of file | ||
diff --git a/examples/boot/application/nrf/.cargo/config.toml b/examples/boot/application/nrf/.cargo/config.toml index 8ca28df39..17616a054 100644 --- a/examples/boot/application/nrf/.cargo/config.toml +++ b/examples/boot/application/nrf/.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 nRF82840_xxAA with your chip as listed in `probe-run --list-chips` | 2 | # replace nRF82840_xxAA with your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip nRF52840_xxAA" | 3 | runner = "probe-rs run --chip nRF52840_xxAA" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv7em-none-eabi" | 6 | target = "thumbv7em-none-eabi" |
diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index b9ff92578..2a0cf7818 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml | |||
| @@ -2,19 +2,26 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-boot-nrf-examples" | 3 | name = "embassy-boot-nrf-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } | 8 | embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } | 10 | embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly"] } |
| 10 | embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly", "nrf52840"] } | 11 | embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] } |
| 11 | embassy-boot-nrf = { version = "0.1.0", path = "../../../../embassy-boot/nrf" } | 12 | embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot", features = ["nightly"] } |
| 13 | embassy-boot-nrf = { version = "0.1.0", path = "../../../../embassy-boot/nrf", features = ["nightly"] } | ||
| 12 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } | 14 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } |
| 13 | 15 | ||
| 14 | defmt = { version = "0.3", optional = true } | 16 | defmt = { version = "0.3", optional = true } |
| 15 | defmt-rtt = { version = "0.3", optional = true } | 17 | defmt-rtt = { version = "0.4", optional = true } |
| 16 | panic-reset = { version = "0.1.1" } | 18 | panic-reset = { version = "0.1.1" } |
| 17 | embedded-hal = { version = "0.2.6" } | 19 | embedded-hal = { version = "0.2.6" } |
| 18 | 20 | ||
| 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 22 | cortex-m-rt = "0.7.0" |
| 23 | |||
| 24 | [features] | ||
| 25 | ed25519-dalek = ["embassy-boot/ed25519-dalek"] | ||
| 26 | ed25519-salty = ["embassy-boot/ed25519-salty"] | ||
| 27 | skip-include = [] | ||
diff --git a/examples/boot/application/nrf/README.md b/examples/boot/application/nrf/README.md index 703377a20..9d6d20336 100644 --- a/examples/boot/application/nrf/README.md +++ b/examples/boot/application/nrf/README.md | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | # Examples using bootloader | 1 | # Examples using bootloader |
| 2 | 2 | ||
| 3 | Example for nRF52 demonstrating the bootloader. The example consists of application binaries, 'a' | 3 | Example for nRF demonstrating the bootloader. The example consists of application binaries, 'a' |
| 4 | which allows you to press a button to start the DFU process, and 'b' which is the updated | 4 | which allows you to press a button to start the DFU process, and 'b' which is the updated |
| 5 | application. | 5 | application. |
| 6 | 6 | ||
| @@ -20,15 +20,19 @@ application. | |||
| 20 | cp memory-bl.x ../../bootloader/nrf/memory.x | 20 | cp memory-bl.x ../../bootloader/nrf/memory.x |
| 21 | 21 | ||
| 22 | # Flash bootloader | 22 | # Flash bootloader |
| 23 | cargo flash --manifest-path ../../bootloader/nrf/Cargo.toml --features embassy-nrf/nrf52840 --release --chip nRF52840_xxAA | 23 | cargo flash --manifest-path ../../bootloader/nrf/Cargo.toml --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi --release --chip nRF52840_xxAA |
| 24 | # Build 'b' | 24 | # Build 'b' |
| 25 | cargo build --release --bin b | 25 | cargo build --release --bin b --features embassy-nrf/nrf52840 |
| 26 | # Generate binary for 'b' | 26 | # Generate binary for 'b' |
| 27 | cargo objcopy --release --bin b -- -O binary b.bin | 27 | cargo objcopy --release --bin b --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi -- -O binary b.bin |
| 28 | ``` | 28 | ``` |
| 29 | 29 | ||
| 30 | # Flash `a` (which includes b.bin) | 30 | # Flash `a` (which includes b.bin) |
| 31 | 31 | ||
| 32 | ``` | 32 | ``` |
| 33 | cargo flash --release --bin a --chip nRF52840_xxAA | 33 | cargo flash --release --bin a --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi --chip nRF52840_xxAA |
| 34 | ``` | 34 | ``` |
| 35 | |||
| 36 | You should then see a solid LED. Pressing button 1 will cause the DFU to be loaded by the bootloader. Upon | ||
| 37 | successfully loading, you'll see the LED flash. After 5 seconds, because there is no petting of the watchdog, | ||
| 38 | you'll see the LED go solid again. This indicates that the bootloader has reverted the update. | ||
diff --git a/examples/boot/application/nrf/memory-bl-nrf91.x b/examples/boot/application/nrf/memory-bl-nrf91.x new file mode 100644 index 000000000..14ceffa73 --- /dev/null +++ b/examples/boot/application/nrf/memory-bl-nrf91.x | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | /* NOTE 1 K = 1 KiBi = 1024 bytes */ | ||
| 4 | /* Assumes Secure Partition Manager (SPM) flashed at the start */ | ||
| 5 | FLASH : ORIGIN = 0x00050000, LENGTH = 24K | ||
| 6 | BOOTLOADER_STATE : ORIGIN = 0x00056000, LENGTH = 4K | ||
| 7 | ACTIVE : ORIGIN = 0x00057000, LENGTH = 64K | ||
| 8 | DFU : ORIGIN = 0x00067000, LENGTH = 68K | ||
| 9 | RAM (rwx) : ORIGIN = 0x20018000, LENGTH = 32K | ||
| 10 | } | ||
| 11 | |||
| 12 | __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); | ||
| 13 | __bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE); | ||
| 14 | |||
| 15 | __bootloader_active_start = ORIGIN(ACTIVE); | ||
| 16 | __bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE); | ||
| 17 | |||
| 18 | __bootloader_dfu_start = ORIGIN(DFU); | ||
| 19 | __bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU); | ||
diff --git a/examples/boot/application/nrf/memory-bl.x b/examples/boot/application/nrf/memory-bl.x index 8a32b905f..257d65644 100644 --- a/examples/boot/application/nrf/memory-bl.x +++ b/examples/boot/application/nrf/memory-bl.x | |||
| @@ -5,7 +5,7 @@ MEMORY | |||
| 5 | BOOTLOADER_STATE : ORIGIN = 0x00006000, LENGTH = 4K | 5 | BOOTLOADER_STATE : ORIGIN = 0x00006000, LENGTH = 4K |
| 6 | ACTIVE : ORIGIN = 0x00007000, LENGTH = 64K | 6 | ACTIVE : ORIGIN = 0x00007000, LENGTH = 64K |
| 7 | DFU : ORIGIN = 0x00017000, LENGTH = 68K | 7 | DFU : ORIGIN = 0x00017000, LENGTH = 68K |
| 8 | RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 32K | 8 | RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K |
| 9 | } | 9 | } |
| 10 | 10 | ||
| 11 | __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); | 11 | __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); |
diff --git a/examples/boot/application/nrf/memory-nrf91.x b/examples/boot/application/nrf/memory-nrf91.x new file mode 100644 index 000000000..2bc13c0d6 --- /dev/null +++ b/examples/boot/application/nrf/memory-nrf91.x | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | /* NOTE 1 K = 1 KiBi = 1024 bytes */ | ||
| 4 | /* Assumes Secure Partition Manager (SPM) flashed at the start */ | ||
| 5 | BOOTLOADER : ORIGIN = 0x00050000, LENGTH = 24K | ||
| 6 | BOOTLOADER_STATE : ORIGIN = 0x00056000, LENGTH = 4K | ||
| 7 | FLASH : ORIGIN = 0x00057000, LENGTH = 64K | ||
| 8 | DFU : ORIGIN = 0x00067000, LENGTH = 68K | ||
| 9 | RAM (rwx) : ORIGIN = 0x20018000, LENGTH = 32K | ||
| 10 | } | ||
| 11 | |||
| 12 | __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); | ||
| 13 | __bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE); | ||
| 14 | |||
| 15 | __bootloader_dfu_start = ORIGIN(DFU); | ||
| 16 | __bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU); | ||
diff --git a/examples/boot/application/nrf/memory.x b/examples/boot/application/nrf/memory.x index 3a54ca460..c6926e422 100644 --- a/examples/boot/application/nrf/memory.x +++ b/examples/boot/application/nrf/memory.x | |||
| @@ -5,7 +5,7 @@ MEMORY | |||
| 5 | BOOTLOADER_STATE : ORIGIN = 0x00006000, LENGTH = 4K | 5 | BOOTLOADER_STATE : ORIGIN = 0x00006000, LENGTH = 4K |
| 6 | FLASH : ORIGIN = 0x00007000, LENGTH = 64K | 6 | FLASH : ORIGIN = 0x00007000, LENGTH = 64K |
| 7 | DFU : ORIGIN = 0x00017000, LENGTH = 68K | 7 | DFU : ORIGIN = 0x00017000, LENGTH = 68K |
| 8 | RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 32K | 8 | RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K |
| 9 | } | 9 | } |
| 10 | 10 | ||
| 11 | __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); | 11 | __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); |
diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index bd8fa3246..021d77f3b 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs | |||
| @@ -1,42 +1,71 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![macro_use] | 3 | #![macro_use] |
| 4 | #![feature(generic_associated_types)] | ||
| 5 | #![feature(type_alias_impl_trait)] | 4 | #![feature(type_alias_impl_trait)] |
| 6 | 5 | ||
| 7 | use embassy_boot_nrf::FirmwareUpdater; | 6 | use embassy_boot_nrf::{FirmwareUpdater, FirmwareUpdaterConfig}; |
| 8 | use embassy_embedded_hal::adapter::BlockingAsync; | 7 | use embassy_embedded_hal::adapter::BlockingAsync; |
| 9 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 10 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; | 9 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; |
| 11 | use embassy_nrf::nvmc::Nvmc; | 10 | use embassy_nrf::nvmc::Nvmc; |
| 11 | use embassy_nrf::wdt::{self, Watchdog}; | ||
| 12 | use embassy_sync::mutex::Mutex; | ||
| 12 | use panic_reset as _; | 13 | use panic_reset as _; |
| 13 | 14 | ||
| 15 | #[cfg(feature = "skip-include")] | ||
| 16 | static APP_B: &[u8] = &[0, 1, 2, 3]; | ||
| 17 | #[cfg(not(feature = "skip-include"))] | ||
| 14 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | 18 | static APP_B: &[u8] = include_bytes!("../../b.bin"); |
| 15 | 19 | ||
| 16 | #[embassy_executor::main] | 20 | #[embassy_executor::main] |
| 17 | async fn main(_spawner: Spawner) { | 21 | async fn main(_spawner: Spawner) { |
| 18 | let p = embassy_nrf::init(Default::default()); | 22 | let p = embassy_nrf::init(Default::default()); |
| 23 | |||
| 19 | let mut button = Input::new(p.P0_11, Pull::Up); | 24 | let mut button = Input::new(p.P0_11, Pull::Up); |
| 20 | let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); | 25 | let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); |
| 26 | |||
| 21 | //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); | 27 | //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); |
| 22 | //let mut button = Input::new(p.P1_02, Pull::Up); | 28 | //let mut button = Input::new(p.P1_02, Pull::Up); |
| 23 | 29 | ||
| 30 | // nRF91 DK | ||
| 31 | // let mut led = Output::new(p.P0_02, Level::Low, OutputDrive::Standard); | ||
| 32 | // let mut button = Input::new(p.P0_06, Pull::Up); | ||
| 33 | |||
| 34 | // The following code block illustrates how to obtain a watchdog that is configured | ||
| 35 | // as per the existing watchdog. Ordinarily, we'd use the handle returned to "pet" the | ||
| 36 | // watchdog periodically. If we don't, and we're not going to for this example, then | ||
| 37 | // the watchdog will cause the device to reset as per its configured timeout in the bootloader. | ||
| 38 | // This helps is avoid a situation where new firmware might be bad and block our executor. | ||
| 39 | // If firmware is bad in this way then the bootloader will revert to any previous version. | ||
| 40 | let wdt_config = wdt::Config::try_new(&p.WDT).unwrap(); | ||
| 41 | let (_wdt, [_wdt_handle]) = match Watchdog::try_new(p.WDT, wdt_config) { | ||
| 42 | Ok(x) => x, | ||
| 43 | Err(_) => { | ||
| 44 | // Watchdog already active with the wrong number of handles, waiting for it to timeout... | ||
| 45 | loop { | ||
| 46 | cortex_m::asm::wfe(); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | }; | ||
| 50 | |||
| 24 | let nvmc = Nvmc::new(p.NVMC); | 51 | let nvmc = Nvmc::new(p.NVMC); |
| 25 | let mut nvmc = BlockingAsync::new(nvmc); | 52 | let nvmc = Mutex::new(BlockingAsync::new(nvmc)); |
| 26 | 53 | ||
| 27 | let mut updater = FirmwareUpdater::default(); | 54 | let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc); |
| 55 | let mut updater = FirmwareUpdater::new(config); | ||
| 28 | loop { | 56 | loop { |
| 29 | led.set_low(); | 57 | led.set_low(); |
| 30 | button.wait_for_any_edge().await; | 58 | button.wait_for_any_edge().await; |
| 31 | if button.is_low() { | 59 | if button.is_low() { |
| 32 | let mut offset = 0; | 60 | let mut offset = 0; |
| 61 | let mut magic = [0; 4]; | ||
| 33 | for chunk in APP_B.chunks(4096) { | 62 | for chunk in APP_B.chunks(4096) { |
| 34 | let mut buf: [u8; 4096] = [0; 4096]; | 63 | let mut buf: [u8; 4096] = [0; 4096]; |
| 35 | buf[..chunk.len()].copy_from_slice(chunk); | 64 | buf[..chunk.len()].copy_from_slice(chunk); |
| 36 | updater.write_firmware(offset, &buf, &mut nvmc, 4096).await.unwrap(); | 65 | updater.write_firmware(&mut magic, offset, &buf).await.unwrap(); |
| 37 | offset += chunk.len(); | 66 | offset += chunk.len(); |
| 38 | } | 67 | } |
| 39 | updater.update(&mut nvmc).await.unwrap(); | 68 | updater.mark_updated(&mut magic).await.unwrap(); |
| 40 | led.set_high(); | 69 | led.set_high(); |
| 41 | cortex_m::peripheral::SCB::sys_reset(); | 70 | cortex_m::peripheral::SCB::sys_reset(); |
| 42 | } | 71 | } |
diff --git a/examples/boot/application/nrf/src/bin/b.rs b/examples/boot/application/nrf/src/bin/b.rs index 5394bf0c7..15ebce5fa 100644 --- a/examples/boot/application/nrf/src/bin/b.rs +++ b/examples/boot/application/nrf/src/bin/b.rs | |||
| @@ -1,7 +1,6 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![macro_use] | 3 | #![macro_use] |
| 4 | #![feature(generic_associated_types)] | ||
| 5 | #![feature(type_alias_impl_trait)] | 4 | #![feature(type_alias_impl_trait)] |
| 6 | 5 | ||
| 7 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| @@ -13,7 +12,10 @@ use panic_reset as _; | |||
| 13 | async fn main(_spawner: Spawner) { | 12 | async fn main(_spawner: Spawner) { |
| 14 | let p = embassy_nrf::init(Default::default()); | 13 | let p = embassy_nrf::init(Default::default()); |
| 15 | let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); | 14 | let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); |
| 16 | //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); | 15 | // let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); |
| 16 | |||
| 17 | // nRF91 DK | ||
| 18 | // let mut led = Output::new(p.P0_02, Level::Low, OutputDrive::Standard); | ||
| 17 | 19 | ||
| 18 | loop { | 20 | loop { |
| 19 | led.set_high(); | 21 | led.set_high(); |
diff --git a/examples/boot/application/rp/.cargo/config.toml b/examples/boot/application/rp/.cargo/config.toml new file mode 100644 index 000000000..cd8d1ef02 --- /dev/null +++ b/examples/boot/application/rp/.cargo/config.toml | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | [unstable] | ||
| 2 | build-std = ["core"] | ||
| 3 | build-std-features = ["panic_immediate_abort"] | ||
| 4 | |||
| 5 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||
| 6 | runner = "probe-rs run --chip RP2040" | ||
| 7 | |||
| 8 | [build] | ||
| 9 | target = "thumbv6m-none-eabi" | ||
| 10 | |||
| 11 | [env] | ||
| 12 | DEFMT_LOG = "trace" | ||
diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml new file mode 100644 index 000000000..95b2da954 --- /dev/null +++ b/examples/boot/application/rp/Cargo.toml | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-boot-rp-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } | ||
| 9 | embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } | ||
| 10 | embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly"] } | ||
| 11 | embassy-rp = { version = "0.1.0", path = "../../../../embassy-rp", features = ["time-driver", "unstable-traits", "nightly"] } | ||
| 12 | embassy-boot-rp = { version = "0.1.0", path = "../../../../embassy-boot/rp", features = ["nightly"] } | ||
| 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } | ||
| 14 | |||
| 15 | defmt = "0.3" | ||
| 16 | defmt-rtt = "0.4" | ||
| 17 | panic-probe = { version = "0.3", features = ["print-defmt"], optional = true } | ||
| 18 | panic-reset = { version = "0.1.1", optional = true } | ||
| 19 | embedded-hal = { version = "0.2.6" } | ||
| 20 | |||
| 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 22 | cortex-m-rt = "0.7.0" | ||
| 23 | embedded-storage = "0.3.0" | ||
| 24 | |||
| 25 | [features] | ||
| 26 | default = ["panic-reset"] | ||
| 27 | debug = [ | ||
| 28 | "embassy-rp/defmt", | ||
| 29 | "embassy-boot-rp/defmt", | ||
| 30 | "panic-probe" | ||
| 31 | ] | ||
| 32 | skip-include = [] | ||
| 33 | |||
| 34 | [profile.release] | ||
| 35 | debug = true | ||
diff --git a/examples/boot/application/rp/README.md b/examples/boot/application/rp/README.md new file mode 100644 index 000000000..41304c526 --- /dev/null +++ b/examples/boot/application/rp/README.md | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | # Examples using bootloader | ||
| 2 | |||
| 3 | Example for Raspberry Pi Pico demonstrating the bootloader. The example consists of application binaries, 'a' | ||
| 4 | which waits for 5 seconds before flashing the 'b' binary, which blinks the LED. | ||
| 5 | |||
| 6 | NOTE: The 'b' binary does not mark the new binary as active, so if you reset the device, it will roll back to the 'a' binary before automatically updating it again. | ||
| 7 | |||
| 8 | ## Prerequisites | ||
| 9 | |||
| 10 | * `cargo-binutils` | ||
| 11 | * `cargo-flash` | ||
| 12 | * `embassy-boot-rp` | ||
| 13 | |||
| 14 | ## Usage | ||
| 15 | |||
| 16 | ``` | ||
| 17 | # Flash bootloader | ||
| 18 | cargo flash --manifest-path ../../bootloader/rp/Cargo.toml --release --chip RP2040 | ||
| 19 | |||
| 20 | # Build 'b' | ||
| 21 | cargo build --release --bin b | ||
| 22 | |||
| 23 | # Generate binary for 'b' | ||
| 24 | cargo objcopy --release --bin b -- -O binary b.bin | ||
| 25 | |||
| 26 | # Flash `a` (which includes b.bin) | ||
| 27 | cargo flash --release --bin a --chip RP2040 | ||
| 28 | ``` | ||
diff --git a/examples/nrf/build.rs b/examples/boot/application/rp/build.rs index 30691aa97..30691aa97 100644 --- a/examples/nrf/build.rs +++ b/examples/boot/application/rp/build.rs | |||
diff --git a/examples/boot/application/rp/memory.x b/examples/boot/application/rp/memory.x new file mode 100644 index 000000000..c19473114 --- /dev/null +++ b/examples/boot/application/rp/memory.x | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | /* NOTE 1 K = 1 KiBi = 1024 bytes */ | ||
| 4 | BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 | ||
| 5 | BOOTLOADER_STATE : ORIGIN = 0x10006000, LENGTH = 4K | ||
| 6 | FLASH : ORIGIN = 0x10007000, LENGTH = 512K | ||
| 7 | DFU : ORIGIN = 0x10087000, LENGTH = 516K | ||
| 8 | RAM : ORIGIN = 0x20000000, LENGTH = 256K | ||
| 9 | } | ||
| 10 | |||
| 11 | __bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOT2); | ||
| 12 | __bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOT2); | ||
| 13 | |||
| 14 | __bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOT2); | ||
| 15 | __bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOT2); | ||
diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs new file mode 100644 index 000000000..c8497494c --- /dev/null +++ b/examples/boot/application/rp/src/bin/a.rs | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::cell::RefCell; | ||
| 6 | |||
| 7 | use defmt_rtt as _; | ||
| 8 | use embassy_boot_rp::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_rp::flash::Flash; | ||
| 11 | use embassy_rp::gpio::{Level, Output}; | ||
| 12 | use embassy_rp::watchdog::Watchdog; | ||
| 13 | use embassy_sync::blocking_mutex::Mutex; | ||
| 14 | use embassy_time::{Duration, Timer}; | ||
| 15 | use embedded_storage::nor_flash::NorFlash; | ||
| 16 | #[cfg(feature = "panic-probe")] | ||
| 17 | use panic_probe as _; | ||
| 18 | #[cfg(feature = "panic-reset")] | ||
| 19 | use panic_reset as _; | ||
| 20 | |||
| 21 | #[cfg(feature = "skip-include")] | ||
| 22 | static APP_B: &[u8] = &[0, 1, 2, 3]; | ||
| 23 | #[cfg(not(feature = "skip-include"))] | ||
| 24 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | ||
| 25 | |||
| 26 | const FLASH_SIZE: usize = 2 * 1024 * 1024; | ||
| 27 | |||
| 28 | #[embassy_executor::main] | ||
| 29 | async fn main(_s: Spawner) { | ||
| 30 | let p = embassy_rp::init(Default::default()); | ||
| 31 | let mut led = Output::new(p.PIN_25, Level::Low); | ||
| 32 | |||
| 33 | // Override bootloader watchdog | ||
| 34 | let mut watchdog = Watchdog::new(p.WATCHDOG); | ||
| 35 | watchdog.start(Duration::from_secs(8)); | ||
| 36 | |||
| 37 | let flash: Flash<_, FLASH_SIZE> = Flash::new(p.FLASH); | ||
| 38 | let flash = Mutex::new(RefCell::new(flash)); | ||
| 39 | |||
| 40 | let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); | ||
| 41 | let mut updater = BlockingFirmwareUpdater::new(config); | ||
| 42 | |||
| 43 | Timer::after(Duration::from_secs(5)).await; | ||
| 44 | watchdog.feed(); | ||
| 45 | led.set_high(); | ||
| 46 | let mut offset = 0; | ||
| 47 | let mut buf: AlignedBuffer<4096> = AlignedBuffer([0; 4096]); | ||
| 48 | defmt::info!("preparing update"); | ||
| 49 | let writer = updater | ||
| 50 | .prepare_update(&mut buf.0[..1]) | ||
| 51 | .map_err(|e| defmt::warn!("E: {:?}", defmt::Debug2Format(&e))) | ||
| 52 | .unwrap(); | ||
| 53 | defmt::info!("writer created, starting write"); | ||
| 54 | for chunk in APP_B.chunks(4096) { | ||
| 55 | buf.0[..chunk.len()].copy_from_slice(chunk); | ||
| 56 | defmt::info!("writing block at offset {}", offset); | ||
| 57 | writer.write(offset, &buf.0[..]).unwrap(); | ||
| 58 | offset += chunk.len() as u32; | ||
| 59 | } | ||
| 60 | watchdog.feed(); | ||
| 61 | defmt::info!("firmware written, marking update"); | ||
| 62 | updater.mark_updated(&mut buf.0[..1]).unwrap(); | ||
| 63 | Timer::after(Duration::from_secs(2)).await; | ||
| 64 | led.set_low(); | ||
| 65 | defmt::info!("update marked, resetting"); | ||
| 66 | cortex_m::peripheral::SCB::sys_reset(); | ||
| 67 | } | ||
diff --git a/examples/boot/application/rp/src/bin/b.rs b/examples/boot/application/rp/src/bin/b.rs new file mode 100644 index 000000000..47dec329c --- /dev/null +++ b/examples/boot/application/rp/src/bin/b.rs | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_rp::gpio; | ||
| 7 | use embassy_time::{Duration, Timer}; | ||
| 8 | use gpio::{Level, Output}; | ||
| 9 | use {defmt_rtt as _, panic_reset as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_s: Spawner) { | ||
| 13 | let p = embassy_rp::init(Default::default()); | ||
| 14 | let mut led = Output::new(p.PIN_25, Level::Low); | ||
| 15 | |||
| 16 | loop { | ||
| 17 | led.set_high(); | ||
| 18 | Timer::after(Duration::from_millis(100)).await; | ||
| 19 | |||
| 20 | led.set_low(); | ||
| 21 | Timer::after(Duration::from_millis(100)).await; | ||
| 22 | } | ||
| 23 | } | ||
diff --git a/examples/boot/application/stm32f3/.cargo/config.toml b/examples/boot/application/stm32f3/.cargo/config.toml index a76d6cab4..4a7ec0a5b 100644 --- a/examples/boot/application/stm32f3/.cargo/config.toml +++ b/examples/boot/application/stm32f3/.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 STM32F429ZITx with your chip as listed in `probe-run --list-chips` | 2 | # replace STM32F429ZITx with your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32F303VCTx" | 3 | runner = "probe-rs run --chip STM32F303VCTx" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv7em-none-eabihf" | 6 | target = "thumbv7em-none-eabihf" |
diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index f143d1e8d..3b0fc4d9d 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml | |||
| @@ -2,21 +2,22 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-boot-stm32f3-examples" | 3 | name = "embassy-boot-stm32f3-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] } |
| 11 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } | 12 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } |
| 12 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } |
| 13 | 14 | ||
| 14 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "0.3", optional = true } |
| 15 | defmt-rtt = { version = "0.3", optional = true } | 16 | defmt-rtt = { version = "0.4", optional = true } |
| 16 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 17 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 18 | 19 | ||
| 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 21 | cortex-m-rt = "0.7.0" |
| 21 | 22 | ||
| 22 | [features] | 23 | [features] |
| @@ -25,3 +26,4 @@ defmt = [ | |||
| 25 | "embassy-stm32/defmt", | 26 | "embassy-stm32/defmt", |
| 26 | "embassy-boot-stm32/defmt", | 27 | "embassy-boot-stm32/defmt", |
| 27 | ] | 28 | ] |
| 29 | skip-include = [] | ||
diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index 11eecc5e2..c0a11d699 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs | |||
| @@ -4,21 +4,25 @@ | |||
| 4 | 4 | ||
| 5 | #[cfg(feature = "defmt-rtt")] | 5 | #[cfg(feature = "defmt-rtt")] |
| 6 | use defmt_rtt::*; | 6 | use defmt_rtt::*; |
| 7 | use embassy_boot_stm32::FirmwareUpdater; | 7 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; |
| 8 | use embassy_embedded_hal::adapter::BlockingAsync; | 8 | use embassy_embedded_hal::adapter::BlockingAsync; |
| 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::flash::Flash; | 11 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; |
| 12 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; | 12 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; |
| 13 | use embassy_sync::mutex::Mutex; | ||
| 13 | use panic_reset as _; | 14 | use panic_reset as _; |
| 14 | 15 | ||
| 16 | #[cfg(feature = "skip-include")] | ||
| 17 | static APP_B: &[u8] = &[0, 1, 2, 3]; | ||
| 18 | #[cfg(not(feature = "skip-include"))] | ||
| 15 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | 19 | static APP_B: &[u8] = include_bytes!("../../b.bin"); |
| 16 | 20 | ||
| 17 | #[embassy_executor::main] | 21 | #[embassy_executor::main] |
| 18 | async fn main(_spawner: Spawner) { | 22 | async fn main(_spawner: Spawner) { |
| 19 | let p = embassy_stm32::init(Default::default()); | 23 | let p = embassy_stm32::init(Default::default()); |
| 20 | let flash = Flash::unlock(p.FLASH); | 24 | let flash = Flash::new_blocking(p.FLASH); |
| 21 | let mut flash = BlockingAsync::new(flash); | 25 | let flash = Mutex::new(BlockingAsync::new(flash)); |
| 22 | 26 | ||
| 23 | let button = Input::new(p.PC13, Pull::Up); | 27 | let button = Input::new(p.PC13, Pull::Up); |
| 24 | let mut button = ExtiInput::new(button, p.EXTI13); | 28 | let mut button = ExtiInput::new(button, p.EXTI13); |
| @@ -26,16 +30,18 @@ async fn main(_spawner: Spawner) { | |||
| 26 | let mut led = Output::new(p.PA5, Level::Low, Speed::Low); | 30 | let mut led = Output::new(p.PA5, Level::Low, Speed::Low); |
| 27 | led.set_high(); | 31 | led.set_high(); |
| 28 | 32 | ||
| 29 | let mut updater = FirmwareUpdater::default(); | 33 | let config = FirmwareUpdaterConfig::from_linkerfile(&flash); |
| 34 | let mut updater = FirmwareUpdater::new(config); | ||
| 30 | button.wait_for_falling_edge().await; | 35 | button.wait_for_falling_edge().await; |
| 31 | let mut offset = 0; | 36 | let mut offset = 0; |
| 37 | let mut magic = AlignedBuffer([0; WRITE_SIZE]); | ||
| 32 | for chunk in APP_B.chunks(2048) { | 38 | for chunk in APP_B.chunks(2048) { |
| 33 | let mut buf: [u8; 2048] = [0; 2048]; | 39 | let mut buf: [u8; 2048] = [0; 2048]; |
| 34 | buf[..chunk.len()].copy_from_slice(chunk); | 40 | buf[..chunk.len()].copy_from_slice(chunk); |
| 35 | updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); | 41 | updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); |
| 36 | offset += chunk.len(); | 42 | offset += chunk.len(); |
| 37 | } | 43 | } |
| 38 | updater.update(&mut flash).await.unwrap(); | 44 | updater.mark_updated(magic.as_mut()).await.unwrap(); |
| 39 | led.set_low(); | 45 | led.set_low(); |
| 40 | cortex_m::peripheral::SCB::sys_reset(); | 46 | cortex_m::peripheral::SCB::sys_reset(); |
| 41 | } | 47 | } |
diff --git a/examples/boot/application/stm32f7/.cargo/config.toml b/examples/boot/application/stm32f7/.cargo/config.toml index a90e1ccbb..9088eea6e 100644 --- a/examples/boot/application/stm32f7/.cargo/config.toml +++ b/examples/boot/application/stm32f7/.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 STM32F429ZITx with your chip as listed in `probe-run --list-chips` | 2 | # replace STM32F429ZITx with your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32F767ZITx -v" | 3 | runner = "probe-rs run --chip STM32F767ZITx" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv7em-none-eabihf" | 6 | target = "thumbv7em-none-eabihf" |
diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 29c87eee1..323b4ab2c 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml | |||
| @@ -2,21 +2,23 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-boot-stm32f7-examples" | 3 | name = "embassy-boot-stm32f7-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] } |
| 11 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } | 12 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } |
| 12 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } |
| 13 | 14 | ||
| 14 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "0.3", optional = true } |
| 15 | defmt-rtt = { version = "0.3", optional = true } | 16 | defmt-rtt = { version = "0.4", optional = true } |
| 16 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 17 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | embedded-storage = "0.3.0" | ||
| 18 | 20 | ||
| 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 22 | cortex-m-rt = "0.7.0" |
| 21 | 23 | ||
| 22 | [features] | 24 | [features] |
| @@ -25,3 +27,4 @@ defmt = [ | |||
| 25 | "embassy-stm32/defmt", | 27 | "embassy-stm32/defmt", |
| 26 | "embassy-boot-stm32/defmt", | 28 | "embassy-boot-stm32/defmt", |
| 27 | ] | 29 | ] |
| 30 | skip-include = [] | ||
diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index a3b66e7c9..dea682a96 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs | |||
| @@ -2,23 +2,29 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | 4 | ||
| 5 | use core::cell::RefCell; | ||
| 6 | |||
| 5 | #[cfg(feature = "defmt-rtt")] | 7 | #[cfg(feature = "defmt-rtt")] |
| 6 | use defmt_rtt::*; | 8 | use defmt_rtt::*; |
| 7 | use embassy_boot_stm32::FirmwareUpdater; | 9 | use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; |
| 8 | use embassy_embedded_hal::adapter::BlockingAsync; | ||
| 9 | use embassy_executor::Spawner; | 10 | use embassy_executor::Spawner; |
| 10 | use embassy_stm32::exti::ExtiInput; | 11 | use embassy_stm32::exti::ExtiInput; |
| 11 | use embassy_stm32::flash::Flash; | 12 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; |
| 12 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; | 13 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; |
| 14 | use embassy_sync::blocking_mutex::Mutex; | ||
| 15 | use embedded_storage::nor_flash::NorFlash; | ||
| 13 | use panic_reset as _; | 16 | use panic_reset as _; |
| 14 | 17 | ||
| 18 | #[cfg(feature = "skip-include")] | ||
| 19 | static APP_B: &[u8] = &[0, 1, 2, 3]; | ||
| 20 | #[cfg(not(feature = "skip-include"))] | ||
| 15 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | 21 | static APP_B: &[u8] = include_bytes!("../../b.bin"); |
| 16 | 22 | ||
| 17 | #[embassy_executor::main] | 23 | #[embassy_executor::main] |
| 18 | async fn main(_spawner: Spawner) { | 24 | async fn main(_spawner: Spawner) { |
| 19 | let p = embassy_stm32::init(Default::default()); | 25 | let p = embassy_stm32::init(Default::default()); |
| 20 | let flash = Flash::unlock(p.FLASH); | 26 | let flash = Flash::new_blocking(p.FLASH); |
| 21 | let mut flash = BlockingAsync::new(flash); | 27 | let flash = Mutex::new(RefCell::new(flash)); |
| 22 | 28 | ||
| 23 | let button = Input::new(p.PC13, Pull::Down); | 29 | let button = Input::new(p.PC13, Pull::Down); |
| 24 | let mut button = ExtiInput::new(button, p.EXTI13); | 30 | let mut button = ExtiInput::new(button, p.EXTI13); |
| @@ -26,16 +32,19 @@ async fn main(_spawner: Spawner) { | |||
| 26 | let mut led = Output::new(p.PB7, Level::Low, Speed::Low); | 32 | let mut led = Output::new(p.PB7, Level::Low, Speed::Low); |
| 27 | led.set_high(); | 33 | led.set_high(); |
| 28 | 34 | ||
| 29 | let mut updater = FirmwareUpdater::default(); | 35 | let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); |
| 36 | let mut updater = BlockingFirmwareUpdater::new(config); | ||
| 37 | let mut magic = AlignedBuffer([0; WRITE_SIZE]); | ||
| 38 | let writer = updater.prepare_update(magic.as_mut()).unwrap(); | ||
| 30 | button.wait_for_rising_edge().await; | 39 | button.wait_for_rising_edge().await; |
| 31 | let mut offset = 0; | 40 | let mut offset = 0; |
| 32 | let mut buf: [u8; 256 * 1024] = [0; 256 * 1024]; | 41 | let mut buf = AlignedBuffer([0; 4096]); |
| 33 | for chunk in APP_B.chunks(256 * 1024) { | 42 | for chunk in APP_B.chunks(4096) { |
| 34 | buf[..chunk.len()].copy_from_slice(chunk); | 43 | buf.as_mut()[..chunk.len()].copy_from_slice(chunk); |
| 35 | updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); | 44 | writer.write(offset, buf.as_ref()).unwrap(); |
| 36 | offset += chunk.len(); | 45 | offset += chunk.len() as u32; |
| 37 | } | 46 | } |
| 38 | updater.update(&mut flash).await.unwrap(); | 47 | updater.mark_updated(magic.as_mut()).unwrap(); |
| 39 | led.set_low(); | 48 | led.set_low(); |
| 40 | cortex_m::peripheral::SCB::sys_reset(); | 49 | cortex_m::peripheral::SCB::sys_reset(); |
| 41 | } | 50 | } |
diff --git a/examples/boot/application/stm32h7/.cargo/config.toml b/examples/boot/application/stm32h7/.cargo/config.toml index fefdd370e..caa0d3a93 100644 --- a/examples/boot/application/stm32h7/.cargo/config.toml +++ b/examples/boot/application/stm32h7/.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 STM32F429ZITx with your chip as listed in `probe-run --list-chips` | 2 | # replace STM32F429ZITx with your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32H743ZITx" | 3 | runner = "probe-rs run --chip STM32H743ZITx" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv7em-none-eabihf" | 6 | target = "thumbv7em-none-eabihf" |
diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index 5669527fe..b2abdc891 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml | |||
| @@ -2,21 +2,23 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-boot-stm32h7-examples" | 3 | name = "embassy-boot-stm32h7-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } |
| 11 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } | 12 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } |
| 12 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } |
| 13 | 14 | ||
| 14 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "0.3", optional = true } |
| 15 | defmt-rtt = { version = "0.3", optional = true } | 16 | defmt-rtt = { version = "0.4", optional = true } |
| 16 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 17 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | embedded-storage = "0.3.0" | ||
| 18 | 20 | ||
| 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 22 | cortex-m-rt = "0.7.0" |
| 21 | 23 | ||
| 22 | [features] | 24 | [features] |
| @@ -25,3 +27,4 @@ defmt = [ | |||
| 25 | "embassy-stm32/defmt", | 27 | "embassy-stm32/defmt", |
| 26 | "embassy-boot-stm32/defmt", | 28 | "embassy-boot-stm32/defmt", |
| 27 | ] | 29 | ] |
| 30 | skip-include = [] | ||
diff --git a/examples/boot/application/stm32h7/flash-boot.sh b/examples/boot/application/stm32h7/flash-boot.sh index debdb17a7..4912a50b7 100755 --- a/examples/boot/application/stm32h7/flash-boot.sh +++ b/examples/boot/application/stm32h7/flash-boot.sh | |||
| @@ -1,8 +1,9 @@ | |||
| 1 | #!/bin/bash | 1 | #!/bin/bash |
| 2 | probe-rs erase --chip STM32H743ZITx | ||
| 2 | mv ../../bootloader/stm32/memory.x ../../bootloader/stm32/memory-old.x | 3 | mv ../../bootloader/stm32/memory.x ../../bootloader/stm32/memory-old.x |
| 3 | cp memory-bl.x ../../bootloader/stm32/memory.x | 4 | cp memory-bl.x ../../bootloader/stm32/memory.x |
| 4 | 5 | ||
| 5 | cargo flash --manifest-path ../../bootloader/stm32/Cargo.toml --release --features embassy-stm32/stm32f767zi --chip STM32F767ZITx --target thumbv7em-none-eabihf | 6 | cargo flash --manifest-path ../../bootloader/stm32/Cargo.toml --release --features embassy-stm32/stm32h743zi --chip STM32H743ZITx --target thumbv7em-none-eabihf |
| 6 | 7 | ||
| 7 | rm ../../bootloader/stm32/memory.x | 8 | rm ../../bootloader/stm32/memory.x |
| 8 | mv ../../bootloader/stm32/memory-old.x ../../bootloader/stm32/memory.x | 9 | mv ../../bootloader/stm32/memory-old.x ../../bootloader/stm32/memory.x |
diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index 0ecf60348..719176692 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs | |||
| @@ -2,23 +2,29 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | 4 | ||
| 5 | use core::cell::RefCell; | ||
| 6 | |||
| 5 | #[cfg(feature = "defmt-rtt")] | 7 | #[cfg(feature = "defmt-rtt")] |
| 6 | use defmt_rtt::*; | 8 | use defmt_rtt::*; |
| 7 | use embassy_boot_stm32::FirmwareUpdater; | 9 | use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; |
| 8 | use embassy_embedded_hal::adapter::BlockingAsync; | ||
| 9 | use embassy_executor::Spawner; | 10 | use embassy_executor::Spawner; |
| 10 | use embassy_stm32::exti::ExtiInput; | 11 | use embassy_stm32::exti::ExtiInput; |
| 11 | use embassy_stm32::flash::Flash; | 12 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; |
| 12 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; | 13 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; |
| 14 | use embassy_sync::blocking_mutex::Mutex; | ||
| 15 | use embedded_storage::nor_flash::NorFlash; | ||
| 13 | use panic_reset as _; | 16 | use panic_reset as _; |
| 14 | 17 | ||
| 18 | #[cfg(feature = "skip-include")] | ||
| 19 | static APP_B: &[u8] = &[0, 1, 2, 3]; | ||
| 20 | #[cfg(not(feature = "skip-include"))] | ||
| 15 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | 21 | static APP_B: &[u8] = include_bytes!("../../b.bin"); |
| 16 | 22 | ||
| 17 | #[embassy_executor::main] | 23 | #[embassy_executor::main] |
| 18 | async fn main(_spawner: Spawner) { | 24 | async fn main(_spawner: Spawner) { |
| 19 | let p = embassy_stm32::init(Default::default()); | 25 | let p = embassy_stm32::init(Default::default()); |
| 20 | let flash = Flash::unlock(p.FLASH); | 26 | let flash = Flash::new_blocking(p.FLASH); |
| 21 | let mut flash = BlockingAsync::new(flash); | 27 | let flash = Mutex::new(RefCell::new(flash)); |
| 22 | 28 | ||
| 23 | let button = Input::new(p.PC13, Pull::Down); | 29 | let button = Input::new(p.PC13, Pull::Down); |
| 24 | let mut button = ExtiInput::new(button, p.EXTI13); | 30 | let mut button = ExtiInput::new(button, p.EXTI13); |
| @@ -26,16 +32,19 @@ async fn main(_spawner: Spawner) { | |||
| 26 | let mut led = Output::new(p.PB14, Level::Low, Speed::Low); | 32 | let mut led = Output::new(p.PB14, Level::Low, Speed::Low); |
| 27 | led.set_high(); | 33 | led.set_high(); |
| 28 | 34 | ||
| 29 | let mut updater = FirmwareUpdater::default(); | 35 | let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); |
| 36 | let mut magic = AlignedBuffer([0; WRITE_SIZE]); | ||
| 37 | let mut updater = BlockingFirmwareUpdater::new(config); | ||
| 38 | let writer = updater.prepare_update(magic.as_mut()).unwrap(); | ||
| 30 | button.wait_for_rising_edge().await; | 39 | button.wait_for_rising_edge().await; |
| 31 | let mut offset = 0; | 40 | let mut offset = 0; |
| 32 | let mut buf: [u8; 128 * 1024] = [0; 128 * 1024]; | 41 | let mut buf = AlignedBuffer([0; 4096]); |
| 33 | for chunk in APP_B.chunks(128 * 1024) { | 42 | for chunk in APP_B.chunks(4096) { |
| 34 | buf[..chunk.len()].copy_from_slice(chunk); | 43 | buf.as_mut()[..chunk.len()].copy_from_slice(chunk); |
| 35 | updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); | 44 | writer.write(offset, buf.as_ref()).unwrap(); |
| 36 | offset += chunk.len(); | 45 | offset += chunk.len() as u32; |
| 37 | } | 46 | } |
| 38 | updater.update(&mut flash).await.unwrap(); | 47 | updater.mark_updated(magic.as_mut()).unwrap(); |
| 39 | led.set_low(); | 48 | led.set_low(); |
| 40 | cortex_m::peripheral::SCB::sys_reset(); | 49 | cortex_m::peripheral::SCB::sys_reset(); |
| 41 | } | 50 | } |
diff --git a/examples/boot/application/stm32l0/.cargo/config.toml b/examples/boot/application/stm32l0/.cargo/config.toml index 2627967ab..6099f015c 100644 --- a/examples/boot/application/stm32l0/.cargo/config.toml +++ b/examples/boot/application/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-run --list-chips` | 2 | # replace your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32L072CZTx" | 3 | runner = "probe-rs run --chip STM32L072CZTx" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv6m-none-eabi" | 6 | target = "thumbv6m-none-eabi" |
diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 48624d5ec..0b7e72d5e 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml | |||
| @@ -2,21 +2,22 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-boot-stm32l0-examples" | 3 | name = "embassy-boot-stm32l0-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } |
| 11 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } | 12 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } |
| 12 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } |
| 13 | 14 | ||
| 14 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "0.3", optional = true } |
| 15 | defmt-rtt = { version = "0.3", optional = true } | 16 | defmt-rtt = { version = "0.4", optional = true } |
| 16 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 17 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 18 | 19 | ||
| 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 21 | cortex-m-rt = "0.7.0" |
| 21 | 22 | ||
| 22 | [features] | 23 | [features] |
| @@ -25,3 +26,4 @@ defmt = [ | |||
| 25 | "embassy-stm32/defmt", | 26 | "embassy-stm32/defmt", |
| 26 | "embassy-boot-stm32/defmt", | 27 | "embassy-boot-stm32/defmt", |
| 27 | ] | 28 | ] |
| 29 | skip-include = [] | ||
diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs index f4f1d7119..ce80056e6 100644 --- a/examples/boot/application/stm32l0/src/bin/a.rs +++ b/examples/boot/application/stm32l0/src/bin/a.rs | |||
| @@ -4,22 +4,26 @@ | |||
| 4 | 4 | ||
| 5 | #[cfg(feature = "defmt-rtt")] | 5 | #[cfg(feature = "defmt-rtt")] |
| 6 | use defmt_rtt::*; | 6 | use defmt_rtt::*; |
| 7 | use embassy_boot_stm32::FirmwareUpdater; | 7 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; |
| 8 | use embassy_embedded_hal::adapter::BlockingAsync; | 8 | use embassy_embedded_hal::adapter::BlockingAsync; |
| 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::flash::Flash; | 11 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; |
| 12 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; | 12 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; |
| 13 | use embassy_sync::mutex::Mutex; | ||
| 13 | use embassy_time::{Duration, Timer}; | 14 | use embassy_time::{Duration, Timer}; |
| 14 | use panic_reset as _; | 15 | use panic_reset as _; |
| 15 | 16 | ||
| 17 | #[cfg(feature = "skip-include")] | ||
| 18 | static APP_B: &[u8] = &[0, 1, 2, 3]; | ||
| 19 | #[cfg(not(feature = "skip-include"))] | ||
| 16 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | 20 | static APP_B: &[u8] = include_bytes!("../../b.bin"); |
| 17 | 21 | ||
| 18 | #[embassy_executor::main] | 22 | #[embassy_executor::main] |
| 19 | async fn main(_spawner: Spawner) { | 23 | async fn main(_spawner: Spawner) { |
| 20 | let p = embassy_stm32::init(Default::default()); | 24 | let p = embassy_stm32::init(Default::default()); |
| 21 | let flash = Flash::unlock(p.FLASH); | 25 | let flash = Flash::new_blocking(p.FLASH); |
| 22 | let mut flash = BlockingAsync::new(flash); | 26 | let flash = Mutex::new(BlockingAsync::new(flash)); |
| 23 | 27 | ||
| 24 | let button = Input::new(p.PB2, Pull::Up); | 28 | let button = Input::new(p.PB2, Pull::Up); |
| 25 | let mut button = ExtiInput::new(button, p.EXTI2); | 29 | let mut button = ExtiInput::new(button, p.EXTI2); |
| @@ -28,17 +32,19 @@ async fn main(_spawner: Spawner) { | |||
| 28 | 32 | ||
| 29 | led.set_high(); | 33 | led.set_high(); |
| 30 | 34 | ||
| 31 | let mut updater = FirmwareUpdater::default(); | 35 | let config = FirmwareUpdaterConfig::from_linkerfile(&flash); |
| 36 | let mut updater = FirmwareUpdater::new(config); | ||
| 32 | button.wait_for_falling_edge().await; | 37 | button.wait_for_falling_edge().await; |
| 33 | let mut offset = 0; | 38 | let mut offset = 0; |
| 39 | let mut magic = AlignedBuffer([0; WRITE_SIZE]); | ||
| 34 | for chunk in APP_B.chunks(128) { | 40 | for chunk in APP_B.chunks(128) { |
| 35 | let mut buf: [u8; 128] = [0; 128]; | 41 | let mut buf: [u8; 128] = [0; 128]; |
| 36 | buf[..chunk.len()].copy_from_slice(chunk); | 42 | buf[..chunk.len()].copy_from_slice(chunk); |
| 37 | updater.write_firmware(offset, &buf, &mut flash, 128).await.unwrap(); | 43 | updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); |
| 38 | offset += chunk.len(); | 44 | offset += chunk.len(); |
| 39 | } | 45 | } |
| 40 | 46 | ||
| 41 | updater.update(&mut flash).await.unwrap(); | 47 | updater.mark_updated(magic.as_mut()).await.unwrap(); |
| 42 | led.set_low(); | 48 | led.set_low(); |
| 43 | Timer::after(Duration::from_secs(1)).await; | 49 | Timer::after(Duration::from_secs(1)).await; |
| 44 | cortex_m::peripheral::SCB::sys_reset(); | 50 | cortex_m::peripheral::SCB::sys_reset(); |
diff --git a/examples/boot/application/stm32l1/.cargo/config.toml b/examples/boot/application/stm32l1/.cargo/config.toml index 404b6b55c..9cabd14ba 100644 --- a/examples/boot/application/stm32l1/.cargo/config.toml +++ b/examples/boot/application/stm32l1/.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-run --list-chips` | 2 | # replace your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32L151CBxxA" | 3 | runner = "probe-rs run --chip STM32L151CBxxA" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv7m-none-eabi" | 6 | target = "thumbv7m-none-eabi" |
diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 00b638ca5..5f3f365c1 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml | |||
| @@ -2,21 +2,22 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-boot-stm32l1-examples" | 3 | name = "embassy-boot-stm32l1-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] } |
| 11 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } | 12 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } |
| 12 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } |
| 13 | 14 | ||
| 14 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "0.3", optional = true } |
| 15 | defmt-rtt = { version = "0.3", optional = true } | 16 | defmt-rtt = { version = "0.4", optional = true } |
| 16 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 17 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 18 | 19 | ||
| 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 21 | cortex-m-rt = "0.7.0" |
| 21 | 22 | ||
| 22 | [features] | 23 | [features] |
| @@ -25,3 +26,4 @@ defmt = [ | |||
| 25 | "embassy-stm32/defmt", | 26 | "embassy-stm32/defmt", |
| 26 | "embassy-boot-stm32/defmt", | 27 | "embassy-boot-stm32/defmt", |
| 27 | ] | 28 | ] |
| 29 | skip-include = [] | ||
diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index f4f1d7119..1e9bf3cb9 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs | |||
| @@ -4,22 +4,26 @@ | |||
| 4 | 4 | ||
| 5 | #[cfg(feature = "defmt-rtt")] | 5 | #[cfg(feature = "defmt-rtt")] |
| 6 | use defmt_rtt::*; | 6 | use defmt_rtt::*; |
| 7 | use embassy_boot_stm32::FirmwareUpdater; | 7 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; |
| 8 | use embassy_embedded_hal::adapter::BlockingAsync; | 8 | use embassy_embedded_hal::adapter::BlockingAsync; |
| 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::flash::Flash; | 11 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; |
| 12 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; | 12 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; |
| 13 | use embassy_sync::mutex::Mutex; | ||
| 13 | use embassy_time::{Duration, Timer}; | 14 | use embassy_time::{Duration, Timer}; |
| 14 | use panic_reset as _; | 15 | use panic_reset as _; |
| 15 | 16 | ||
| 17 | #[cfg(feature = "skip-include")] | ||
| 18 | static APP_B: &[u8] = &[0, 1, 2, 3]; | ||
| 19 | #[cfg(not(feature = "skip-include"))] | ||
| 16 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | 20 | static APP_B: &[u8] = include_bytes!("../../b.bin"); |
| 17 | 21 | ||
| 18 | #[embassy_executor::main] | 22 | #[embassy_executor::main] |
| 19 | async fn main(_spawner: Spawner) { | 23 | async fn main(_spawner: Spawner) { |
| 20 | let p = embassy_stm32::init(Default::default()); | 24 | let p = embassy_stm32::init(Default::default()); |
| 21 | let flash = Flash::unlock(p.FLASH); | 25 | let flash = Flash::new_blocking(p.FLASH); |
| 22 | let mut flash = BlockingAsync::new(flash); | 26 | let flash = Mutex::new(BlockingAsync::new(flash)); |
| 23 | 27 | ||
| 24 | let button = Input::new(p.PB2, Pull::Up); | 28 | let button = Input::new(p.PB2, Pull::Up); |
| 25 | let mut button = ExtiInput::new(button, p.EXTI2); | 29 | let mut button = ExtiInput::new(button, p.EXTI2); |
| @@ -28,17 +32,19 @@ async fn main(_spawner: Spawner) { | |||
| 28 | 32 | ||
| 29 | led.set_high(); | 33 | led.set_high(); |
| 30 | 34 | ||
| 31 | let mut updater = FirmwareUpdater::default(); | 35 | let config = FirmwareUpdaterConfig::from_linkerfile(&flash); |
| 36 | let mut updater = FirmwareUpdater::new(config); | ||
| 32 | button.wait_for_falling_edge().await; | 37 | button.wait_for_falling_edge().await; |
| 38 | let mut magic = AlignedBuffer([0; WRITE_SIZE]); | ||
| 33 | let mut offset = 0; | 39 | let mut offset = 0; |
| 34 | for chunk in APP_B.chunks(128) { | 40 | for chunk in APP_B.chunks(128) { |
| 35 | let mut buf: [u8; 128] = [0; 128]; | 41 | let mut buf: [u8; 128] = [0; 128]; |
| 36 | buf[..chunk.len()].copy_from_slice(chunk); | 42 | buf[..chunk.len()].copy_from_slice(chunk); |
| 37 | updater.write_firmware(offset, &buf, &mut flash, 128).await.unwrap(); | 43 | updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); |
| 38 | offset += chunk.len(); | 44 | offset += chunk.len(); |
| 39 | } | 45 | } |
| 40 | 46 | ||
| 41 | updater.update(&mut flash).await.unwrap(); | 47 | updater.mark_updated(magic.as_mut()).await.unwrap(); |
| 42 | led.set_low(); | 48 | led.set_low(); |
| 43 | Timer::after(Duration::from_secs(1)).await; | 49 | Timer::after(Duration::from_secs(1)).await; |
| 44 | cortex_m::peripheral::SCB::sys_reset(); | 50 | cortex_m::peripheral::SCB::sys_reset(); |
diff --git a/examples/boot/application/stm32l4/.cargo/config.toml b/examples/boot/application/stm32l4/.cargo/config.toml index 43520e323..c803215f6 100644 --- a/examples/boot/application/stm32l4/.cargo/config.toml +++ b/examples/boot/application/stm32l4/.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-run --list-chips` | 2 | # replace your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32L475VG" | 3 | runner = "probe-rs run --chip STM32L475VG" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv7em-none-eabihf" | 6 | target = "thumbv7em-none-eabihf" |
diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 51ba730d5..44eb5aba8 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml | |||
| @@ -2,21 +2,22 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-boot-stm32l4-examples" | 3 | name = "embassy-boot-stm32l4-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] } |
| 11 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } | 12 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } |
| 12 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } |
| 13 | 14 | ||
| 14 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "0.3", optional = true } |
| 15 | defmt-rtt = { version = "0.3", optional = true } | 16 | defmt-rtt = { version = "0.4", optional = true } |
| 16 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 17 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 18 | 19 | ||
| 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 21 | cortex-m-rt = "0.7.0" |
| 21 | 22 | ||
| 22 | [features] | 23 | [features] |
| @@ -25,3 +26,4 @@ defmt = [ | |||
| 25 | "embassy-stm32/defmt", | 26 | "embassy-stm32/defmt", |
| 26 | "embassy-boot-stm32/defmt", | 27 | "embassy-boot-stm32/defmt", |
| 27 | ] | 28 | ] |
| 29 | skip-include = [] | ||
diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index 178b2e04a..a514ab5be 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs | |||
| @@ -4,21 +4,25 @@ | |||
| 4 | 4 | ||
| 5 | #[cfg(feature = "defmt-rtt")] | 5 | #[cfg(feature = "defmt-rtt")] |
| 6 | use defmt_rtt::*; | 6 | use defmt_rtt::*; |
| 7 | use embassy_boot_stm32::FirmwareUpdater; | 7 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; |
| 8 | use embassy_embedded_hal::adapter::BlockingAsync; | 8 | use embassy_embedded_hal::adapter::BlockingAsync; |
| 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::flash::Flash; | 11 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; |
| 12 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; | 12 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; |
| 13 | use embassy_sync::mutex::Mutex; | ||
| 13 | use panic_reset as _; | 14 | use panic_reset as _; |
| 14 | 15 | ||
| 16 | #[cfg(feature = "skip-include")] | ||
| 17 | static APP_B: &[u8] = &[0, 1, 2, 3]; | ||
| 18 | #[cfg(not(feature = "skip-include"))] | ||
| 15 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | 19 | static APP_B: &[u8] = include_bytes!("../../b.bin"); |
| 16 | 20 | ||
| 17 | #[embassy_executor::main] | 21 | #[embassy_executor::main] |
| 18 | async fn main(_spawner: Spawner) { | 22 | async fn main(_spawner: Spawner) { |
| 19 | let p = embassy_stm32::init(Default::default()); | 23 | let p = embassy_stm32::init(Default::default()); |
| 20 | let flash = Flash::unlock(p.FLASH); | 24 | let flash = Flash::new_blocking(p.FLASH); |
| 21 | let mut flash = BlockingAsync::new(flash); | 25 | let flash = Mutex::new(BlockingAsync::new(flash)); |
| 22 | 26 | ||
| 23 | let button = Input::new(p.PC13, Pull::Up); | 27 | let button = Input::new(p.PC13, Pull::Up); |
| 24 | let mut button = ExtiInput::new(button, p.EXTI13); | 28 | let mut button = ExtiInput::new(button, p.EXTI13); |
| @@ -26,16 +30,18 @@ async fn main(_spawner: Spawner) { | |||
| 26 | let mut led = Output::new(p.PB14, Level::Low, Speed::Low); | 30 | let mut led = Output::new(p.PB14, Level::Low, Speed::Low); |
| 27 | led.set_high(); | 31 | led.set_high(); |
| 28 | 32 | ||
| 29 | let mut updater = FirmwareUpdater::default(); | 33 | let config = FirmwareUpdaterConfig::from_linkerfile(&flash); |
| 34 | let mut updater = FirmwareUpdater::new(config); | ||
| 30 | button.wait_for_falling_edge().await; | 35 | button.wait_for_falling_edge().await; |
| 36 | let mut magic = AlignedBuffer([0; WRITE_SIZE]); | ||
| 31 | let mut offset = 0; | 37 | let mut offset = 0; |
| 32 | for chunk in APP_B.chunks(2048) { | 38 | for chunk in APP_B.chunks(2048) { |
| 33 | let mut buf: [u8; 2048] = [0; 2048]; | 39 | let mut buf: [u8; 2048] = [0; 2048]; |
| 34 | buf[..chunk.len()].copy_from_slice(chunk); | 40 | buf[..chunk.len()].copy_from_slice(chunk); |
| 35 | updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); | 41 | updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); |
| 36 | offset += chunk.len(); | 42 | offset += chunk.len(); |
| 37 | } | 43 | } |
| 38 | updater.update(&mut flash).await.unwrap(); | 44 | updater.mark_updated(magic.as_mut()).await.unwrap(); |
| 39 | led.set_low(); | 45 | led.set_low(); |
| 40 | cortex_m::peripheral::SCB::sys_reset(); | 46 | cortex_m::peripheral::SCB::sys_reset(); |
| 41 | } | 47 | } |
diff --git a/examples/boot/application/stm32wl/.cargo/config.toml b/examples/boot/application/stm32wl/.cargo/config.toml index e395d75b4..4f8094ff2 100644 --- a/examples/boot/application/stm32wl/.cargo/config.toml +++ b/examples/boot/application/stm32wl/.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-run --list-chips` | 2 | # replace your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32WLE5JCIx" | 3 | runner = "probe-rs run --chip STM32WLE5JCIx" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv7em-none-eabihf" | 6 | target = "thumbv7em-none-eabihf" |
diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index 182acf694..fdad55060 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml | |||
| @@ -2,21 +2,22 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-boot-stm32wl-examples" | 3 | name = "embassy-boot-stm32wl-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] } |
| 11 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } | 12 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } |
| 12 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } |
| 13 | 14 | ||
| 14 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "0.3", optional = true } |
| 15 | defmt-rtt = { version = "0.3", optional = true } | 16 | defmt-rtt = { version = "0.4", optional = true } |
| 16 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 17 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 18 | 19 | ||
| 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 21 | cortex-m-rt = "0.7.0" |
| 21 | 22 | ||
| 22 | [features] | 23 | [features] |
| @@ -25,3 +26,4 @@ defmt = [ | |||
| 25 | "embassy-stm32/defmt", | 26 | "embassy-stm32/defmt", |
| 26 | "embassy-boot-stm32/defmt", | 27 | "embassy-boot-stm32/defmt", |
| 27 | ] | 28 | ] |
| 29 | skip-include = [] | ||
diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index c71a42654..52a197a5c 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs | |||
| @@ -4,21 +4,25 @@ | |||
| 4 | 4 | ||
| 5 | #[cfg(feature = "defmt-rtt")] | 5 | #[cfg(feature = "defmt-rtt")] |
| 6 | use defmt_rtt::*; | 6 | use defmt_rtt::*; |
| 7 | use embassy_boot_stm32::FirmwareUpdater; | 7 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; |
| 8 | use embassy_embedded_hal::adapter::BlockingAsync; | 8 | use embassy_embedded_hal::adapter::BlockingAsync; |
| 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::flash::Flash; | 11 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; |
| 12 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; | 12 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; |
| 13 | use embassy_sync::mutex::Mutex; | ||
| 13 | use panic_reset as _; | 14 | use panic_reset as _; |
| 14 | 15 | ||
| 16 | #[cfg(feature = "skip-include")] | ||
| 17 | static APP_B: &[u8] = &[0, 1, 2, 3]; | ||
| 18 | #[cfg(not(feature = "skip-include"))] | ||
| 15 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | 19 | static APP_B: &[u8] = include_bytes!("../../b.bin"); |
| 16 | 20 | ||
| 17 | #[embassy_executor::main] | 21 | #[embassy_executor::main] |
| 18 | async fn main(_spawner: Spawner) { | 22 | async fn main(_spawner: Spawner) { |
| 19 | let p = embassy_stm32::init(Default::default()); | 23 | let p = embassy_stm32::init(Default::default()); |
| 20 | let flash = Flash::unlock(p.FLASH); | 24 | let flash = Flash::new_blocking(p.FLASH); |
| 21 | let mut flash = BlockingAsync::new(flash); | 25 | let flash = Mutex::new(BlockingAsync::new(flash)); |
| 22 | 26 | ||
| 23 | let button = Input::new(p.PA0, Pull::Up); | 27 | let button = Input::new(p.PA0, Pull::Up); |
| 24 | let mut button = ExtiInput::new(button, p.EXTI0); | 28 | let mut button = ExtiInput::new(button, p.EXTI0); |
| @@ -26,18 +30,20 @@ async fn main(_spawner: Spawner) { | |||
| 26 | let mut led = Output::new(p.PB9, Level::Low, Speed::Low); | 30 | let mut led = Output::new(p.PB9, Level::Low, Speed::Low); |
| 27 | led.set_high(); | 31 | led.set_high(); |
| 28 | 32 | ||
| 29 | let mut updater = FirmwareUpdater::default(); | 33 | let config = FirmwareUpdaterConfig::from_linkerfile(&flash); |
| 34 | let mut updater = FirmwareUpdater::new(config); | ||
| 30 | button.wait_for_falling_edge().await; | 35 | button.wait_for_falling_edge().await; |
| 31 | //defmt::info!("Starting update"); | 36 | //defmt::info!("Starting update"); |
| 37 | let mut magic = AlignedBuffer([0; WRITE_SIZE]); | ||
| 32 | let mut offset = 0; | 38 | let mut offset = 0; |
| 33 | for chunk in APP_B.chunks(2048) { | 39 | for chunk in APP_B.chunks(2048) { |
| 34 | let mut buf: [u8; 2048] = [0; 2048]; | 40 | let mut buf: [u8; 2048] = [0; 2048]; |
| 35 | buf[..chunk.len()].copy_from_slice(chunk); | 41 | buf[..chunk.len()].copy_from_slice(chunk); |
| 36 | // defmt::info!("Writing chunk at 0x{:x}", offset); | 42 | // defmt::info!("Writing chunk at 0x{:x}", offset); |
| 37 | updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); | 43 | updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); |
| 38 | offset += chunk.len(); | 44 | offset += chunk.len(); |
| 39 | } | 45 | } |
| 40 | updater.update(&mut flash).await.unwrap(); | 46 | updater.mark_updated(magic.as_mut()).await.unwrap(); |
| 41 | //defmt::info!("Marked as updated"); | 47 | //defmt::info!("Marked as updated"); |
| 42 | led.set_low(); | 48 | led.set_low(); |
| 43 | cortex_m::peripheral::SCB::sys_reset(); | 49 | cortex_m::peripheral::SCB::sys_reset(); |
diff --git a/examples/boot/bootloader/nrf/.cargo/config.toml b/examples/boot/bootloader/nrf/.cargo/config.toml index 1060800a3..c292846aa 100644 --- a/examples/boot/bootloader/nrf/.cargo/config.toml +++ b/examples/boot/bootloader/nrf/.cargo/config.toml | |||
| @@ -4,7 +4,7 @@ build-std-features = ["panic_immediate_abort"] | |||
| 4 | 4 | ||
| 5 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | 5 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] |
| 6 | #runner = "./fruitrunner" | 6 | #runner = "./fruitrunner" |
| 7 | runner = "probe-run --chip nrf52840_xxAA" | 7 | runner = "probe-rs run --chip nrf52840_xxAA" |
| 8 | 8 | ||
| 9 | rustflags = [ | 9 | rustflags = [ |
| 10 | # Code-size optimizations. | 10 | # Code-size optimizations. |
diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml index aa2a13ecb..40656f359 100644 --- a/examples/boot/bootloader/nrf/Cargo.toml +++ b/examples/boot/bootloader/nrf/Cargo.toml | |||
| @@ -3,14 +3,16 @@ edition = "2021" | |||
| 3 | name = "nrf-bootloader-example" | 3 | name = "nrf-bootloader-example" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | description = "Bootloader for nRF chips" | 5 | description = "Bootloader for nRF chips" |
| 6 | license = "MIT OR Apache-2.0" | ||
| 6 | 7 | ||
| 7 | [dependencies] | 8 | [dependencies] |
| 8 | defmt = { version = "0.3", optional = true } | 9 | defmt = { version = "0.3", optional = true } |
| 9 | defmt-rtt = { version = "0.3", optional = true } | 10 | defmt-rtt = { version = "0.4", optional = true } |
| 10 | 11 | ||
| 11 | embassy-nrf = { path = "../../../../embassy-nrf", default-features = false, features = ["nightly"] } | 12 | embassy-nrf = { path = "../../../../embassy-nrf", features = ["nightly"] } |
| 12 | embassy-boot-nrf = { path = "../../../../embassy-boot/nrf", default-features = false } | 13 | embassy-boot-nrf = { path = "../../../../embassy-boot/nrf" } |
| 13 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 14 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 15 | embassy-sync = { path = "../../../../embassy-sync" } | ||
| 14 | cortex-m-rt = { version = "0.7" } | 16 | cortex-m-rt = { version = "0.7" } |
| 15 | cfg-if = "1.0.0" | 17 | cfg-if = "1.0.0" |
| 16 | 18 | ||
diff --git a/examples/boot/bootloader/nrf/memory-bm.x b/examples/boot/bootloader/nrf/memory-bm.x index 8a32b905f..257d65644 100644 --- a/examples/boot/bootloader/nrf/memory-bm.x +++ b/examples/boot/bootloader/nrf/memory-bm.x | |||
| @@ -5,7 +5,7 @@ MEMORY | |||
| 5 | BOOTLOADER_STATE : ORIGIN = 0x00006000, LENGTH = 4K | 5 | BOOTLOADER_STATE : ORIGIN = 0x00006000, LENGTH = 4K |
| 6 | ACTIVE : ORIGIN = 0x00007000, LENGTH = 64K | 6 | ACTIVE : ORIGIN = 0x00007000, LENGTH = 64K |
| 7 | DFU : ORIGIN = 0x00017000, LENGTH = 68K | 7 | DFU : ORIGIN = 0x00017000, LENGTH = 68K |
| 8 | RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 32K | 8 | RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K |
| 9 | } | 9 | } |
| 10 | 10 | ||
| 11 | __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); | 11 | __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); |
diff --git a/examples/boot/bootloader/nrf/memory.x b/examples/boot/bootloader/nrf/memory.x index 8a32b905f..257d65644 100644 --- a/examples/boot/bootloader/nrf/memory.x +++ b/examples/boot/bootloader/nrf/memory.x | |||
| @@ -5,7 +5,7 @@ MEMORY | |||
| 5 | BOOTLOADER_STATE : ORIGIN = 0x00006000, LENGTH = 4K | 5 | BOOTLOADER_STATE : ORIGIN = 0x00006000, LENGTH = 4K |
| 6 | ACTIVE : ORIGIN = 0x00007000, LENGTH = 64K | 6 | ACTIVE : ORIGIN = 0x00007000, LENGTH = 64K |
| 7 | DFU : ORIGIN = 0x00017000, LENGTH = 68K | 7 | DFU : ORIGIN = 0x00017000, LENGTH = 68K |
| 8 | RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 32K | 8 | RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K |
| 9 | } | 9 | } |
| 10 | 10 | ||
| 11 | __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); | 11 | __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); |
diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index bc7e0755f..72c95c02a 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs | |||
| @@ -1,11 +1,15 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::cell::RefCell; | ||
| 5 | |||
| 4 | use cortex_m_rt::{entry, exception}; | 6 | use cortex_m_rt::{entry, exception}; |
| 5 | #[cfg(feature = "defmt")] | 7 | #[cfg(feature = "defmt")] |
| 6 | use defmt_rtt as _; | 8 | use defmt_rtt as _; |
| 7 | use embassy_boot_nrf::*; | 9 | use embassy_boot_nrf::*; |
| 8 | use embassy_nrf::nvmc::Nvmc; | 10 | use embassy_nrf::nvmc::Nvmc; |
| 11 | use embassy_nrf::wdt; | ||
| 12 | use embassy_sync::blocking_mutex::Mutex; | ||
| 9 | 13 | ||
| 10 | #[entry] | 14 | #[entry] |
| 11 | fn main() -> ! { | 15 | fn main() -> ! { |
| @@ -19,13 +23,21 @@ fn main() -> ! { | |||
| 19 | } | 23 | } |
| 20 | */ | 24 | */ |
| 21 | 25 | ||
| 22 | let mut bl = BootLoader::default(); | 26 | let mut wdt_config = wdt::Config::default(); |
| 23 | let start = bl.prepare(&mut SingleFlashProvider::new(&mut WatchdogFlash::start( | 27 | wdt_config.timeout_ticks = 32768 * 5; // timeout seconds |
| 24 | Nvmc::new(p.NVMC), | 28 | wdt_config.run_during_sleep = true; |
| 25 | p.WDT, | 29 | wdt_config.run_during_debug_halt = false; |
| 26 | 5, | 30 | |
| 27 | ))); | 31 | let flash = WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config); |
| 28 | unsafe { bl.load(start) } | 32 | let flash = Mutex::new(RefCell::new(flash)); |
| 33 | |||
| 34 | let config = BootLoaderConfig::from_linkerfile_blocking(&flash); | ||
| 35 | let active_offset = config.active.offset(); | ||
| 36 | let mut bl: BootLoader<_, _, _> = BootLoader::new(config); | ||
| 37 | |||
| 38 | bl.prepare(); | ||
| 39 | |||
| 40 | unsafe { bl.load(active_offset) } | ||
| 29 | } | 41 | } |
| 30 | 42 | ||
| 31 | #[no_mangle] | 43 | #[no_mangle] |
diff --git a/examples/boot/bootloader/rp/.cargo/config.toml b/examples/boot/bootloader/rp/.cargo/config.toml new file mode 100644 index 000000000..9d48ecdc9 --- /dev/null +++ b/examples/boot/bootloader/rp/.cargo/config.toml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||
| 2 | runner = "probe-rs run --chip RP2040" | ||
| 3 | |||
| 4 | [build] | ||
| 5 | target = "thumbv6m-none-eabi" | ||
| 6 | |||
| 7 | [env] | ||
| 8 | DEFMT_LOG = "trace" | ||
diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml new file mode 100644 index 000000000..c1dc99eec --- /dev/null +++ b/examples/boot/bootloader/rp/Cargo.toml | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "rp-bootloader-example" | ||
| 4 | version = "0.1.0" | ||
| 5 | description = "Example bootloader for RP2040 chips" | ||
| 6 | license = "MIT OR Apache-2.0" | ||
| 7 | |||
| 8 | [dependencies] | ||
| 9 | defmt = { version = "0.3", optional = true } | ||
| 10 | defmt-rtt = { version = "0.4", optional = true } | ||
| 11 | |||
| 12 | embassy-rp = { path = "../../../../embassy-rp", features = ["nightly"] } | ||
| 13 | embassy-boot-rp = { path = "../../../../embassy-boot/rp" } | ||
| 14 | embassy-sync = { path = "../../../../embassy-sync" } | ||
| 15 | embassy-time = { path = "../../../../embassy-time", features = ["nightly"] } | ||
| 16 | |||
| 17 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 18 | cortex-m-rt = { version = "0.7" } | ||
| 19 | embedded-storage = "0.3.0" | ||
| 20 | embedded-storage-async = "0.4.0" | ||
| 21 | cfg-if = "1.0.0" | ||
| 22 | |||
| 23 | [features] | ||
| 24 | defmt = [ | ||
| 25 | "dep:defmt", | ||
| 26 | "embassy-boot-rp/defmt", | ||
| 27 | "embassy-rp/defmt", | ||
| 28 | ] | ||
| 29 | debug = ["defmt-rtt", "defmt"] | ||
| 30 | |||
| 31 | [profile.release] | ||
| 32 | debug = true | ||
| 33 | opt-level = 's' | ||
diff --git a/examples/boot/bootloader/rp/README.md b/examples/boot/bootloader/rp/README.md new file mode 100644 index 000000000..064e87273 --- /dev/null +++ b/examples/boot/bootloader/rp/README.md | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # Bootloader for RP2040 | ||
| 2 | |||
| 3 | The bootloader uses `embassy-boot` to interact with the flash. | ||
| 4 | |||
| 5 | # Usage | ||
| 6 | |||
| 7 | Flashing the bootloader | ||
| 8 | |||
| 9 | ``` | ||
| 10 | cargo flash --release --chip RP2040 | ||
| 11 | ``` | ||
| 12 | |||
| 13 | To debug, use `cargo run` and enable the debug feature flag | ||
| 14 | |||
| 15 | ``` rust | ||
| 16 | cargo run --release --features debug | ||
| 17 | ``` | ||
diff --git a/examples/boot/bootloader/rp/build.rs b/examples/boot/bootloader/rp/build.rs new file mode 100644 index 000000000..c201704ad --- /dev/null +++ b/examples/boot/bootloader/rp/build.rs | |||
| @@ -0,0 +1,28 @@ | |||
| 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 | println!("cargo:rustc-link-arg-bins=--nmagic"); | ||
| 23 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | ||
| 24 | println!("cargo:rustc-link-arg-bins=-Tlink-rp.x"); | ||
| 25 | if env::var("CARGO_FEATURE_DEFMT").is_ok() { | ||
| 26 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 27 | } | ||
| 28 | } | ||
diff --git a/examples/boot/bootloader/rp/memory.x b/examples/boot/bootloader/rp/memory.x new file mode 100644 index 000000000..d6ef38469 --- /dev/null +++ b/examples/boot/bootloader/rp/memory.x | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | /* NOTE 1 K = 1 KiBi = 1024 bytes */ | ||
| 4 | BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 | ||
| 5 | FLASH : ORIGIN = 0x10000100, LENGTH = 24K | ||
| 6 | BOOTLOADER_STATE : ORIGIN = 0x10006000, LENGTH = 4K | ||
| 7 | ACTIVE : ORIGIN = 0x10007000, LENGTH = 512K | ||
| 8 | DFU : ORIGIN = 0x10087000, LENGTH = 516K | ||
| 9 | RAM : ORIGIN = 0x20000000, LENGTH = 256K | ||
| 10 | } | ||
| 11 | |||
| 12 | __bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOT2); | ||
| 13 | __bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOT2); | ||
| 14 | |||
| 15 | __bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(BOOT2); | ||
| 16 | __bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(BOOT2); | ||
| 17 | |||
| 18 | __bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOT2); | ||
| 19 | __bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOT2); | ||
diff --git a/examples/boot/bootloader/rp/src/main.rs b/examples/boot/bootloader/rp/src/main.rs new file mode 100644 index 000000000..6a81db804 --- /dev/null +++ b/examples/boot/bootloader/rp/src/main.rs | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use core::cell::RefCell; | ||
| 5 | |||
| 6 | use cortex_m_rt::{entry, exception}; | ||
| 7 | #[cfg(feature = "defmt")] | ||
| 8 | use defmt_rtt as _; | ||
| 9 | use embassy_boot_rp::*; | ||
| 10 | use embassy_sync::blocking_mutex::Mutex; | ||
| 11 | use embassy_time::Duration; | ||
| 12 | |||
| 13 | const FLASH_SIZE: usize = 2 * 1024 * 1024; | ||
| 14 | |||
| 15 | #[entry] | ||
| 16 | fn main() -> ! { | ||
| 17 | let p = embassy_rp::init(Default::default()); | ||
| 18 | |||
| 19 | // Uncomment this if you are debugging the bootloader with debugger/RTT attached, | ||
| 20 | // as it prevents a hard fault when accessing flash 'too early' after boot. | ||
| 21 | /* | ||
| 22 | for i in 0..10000000 { | ||
| 23 | cortex_m::asm::nop(); | ||
| 24 | } | ||
| 25 | */ | ||
| 26 | |||
| 27 | let flash = WatchdogFlash::<FLASH_SIZE>::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8)); | ||
| 28 | let flash = Mutex::new(RefCell::new(flash)); | ||
| 29 | |||
| 30 | let config = BootLoaderConfig::from_linkerfile_blocking(&flash); | ||
| 31 | let active_offset = config.active.offset(); | ||
| 32 | let mut bl: BootLoader<_, _, _> = BootLoader::new(config); | ||
| 33 | |||
| 34 | bl.prepare(); | ||
| 35 | |||
| 36 | unsafe { bl.load(embassy_rp::flash::FLASH_BASE as u32 + active_offset) } | ||
| 37 | } | ||
| 38 | |||
| 39 | #[no_mangle] | ||
| 40 | #[cfg_attr(target_os = "none", link_section = ".HardFault.user")] | ||
| 41 | unsafe extern "C" fn HardFault() { | ||
| 42 | cortex_m::peripheral::SCB::sys_reset(); | ||
| 43 | } | ||
| 44 | |||
| 45 | #[exception] | ||
| 46 | unsafe fn DefaultHandler(_: i16) -> ! { | ||
| 47 | const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32; | ||
| 48 | let irqn = core::ptr::read_volatile(SCB_ICSR) as u8 as i16 - 16; | ||
| 49 | |||
| 50 | panic!("DefaultHandler #{:?}", irqn); | ||
| 51 | } | ||
| 52 | |||
| 53 | #[panic_handler] | ||
| 54 | fn panic(_info: &core::panic::PanicInfo) -> ! { | ||
| 55 | cortex_m::asm::udf(); | ||
| 56 | } | ||
diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index 491777103..6436f2fee 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml | |||
| @@ -3,17 +3,19 @@ edition = "2021" | |||
| 3 | name = "stm32-bootloader-example" | 3 | name = "stm32-bootloader-example" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | description = "Example bootloader for STM32 chips" | 5 | description = "Example bootloader for STM32 chips" |
| 6 | license = "MIT OR Apache-2.0" | ||
| 6 | 7 | ||
| 7 | [dependencies] | 8 | [dependencies] |
| 8 | defmt = { version = "0.3", optional = true } | 9 | defmt = { version = "0.3", optional = true } |
| 9 | defmt-rtt = { version = "0.3", optional = true } | 10 | defmt-rtt = { version = "0.4", optional = true } |
| 10 | 11 | ||
| 11 | embassy-stm32 = { path = "../../../../embassy-stm32", default-features = false, features = ["nightly"] } | 12 | embassy-stm32 = { path = "../../../../embassy-stm32", features = ["nightly"] } |
| 12 | embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32", default-features = false } | 13 | embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32" } |
| 13 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 14 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 15 | embassy-sync = { path = "../../../../embassy-sync" } | ||
| 14 | cortex-m-rt = { version = "0.7" } | 16 | cortex-m-rt = { version = "0.7" } |
| 15 | embedded-storage = "0.3.0" | 17 | embedded-storage = "0.3.0" |
| 16 | embedded-storage-async = "0.3.0" | 18 | embedded-storage-async = "0.4.0" |
| 17 | cfg-if = "1.0.0" | 19 | cfg-if = "1.0.0" |
| 18 | 20 | ||
| 19 | [features] | 21 | [features] |
diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index 45c511ced..262eed200 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs | |||
| @@ -1,11 +1,14 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::cell::RefCell; | ||
| 5 | |||
| 4 | use cortex_m_rt::{entry, exception}; | 6 | use cortex_m_rt::{entry, exception}; |
| 5 | #[cfg(feature = "defmt")] | 7 | #[cfg(feature = "defmt")] |
| 6 | use defmt_rtt as _; | 8 | use defmt_rtt as _; |
| 7 | use embassy_boot_stm32::*; | 9 | use embassy_boot_stm32::*; |
| 8 | use embassy_stm32::flash::{Flash, ERASE_SIZE}; | 10 | use embassy_stm32::flash::{Flash, BANK1_REGION}; |
| 11 | use embassy_sync::blocking_mutex::Mutex; | ||
| 9 | 12 | ||
| 10 | #[entry] | 13 | #[entry] |
| 11 | fn main() -> ! { | 14 | fn main() -> ! { |
| @@ -19,11 +22,16 @@ fn main() -> ! { | |||
| 19 | } | 22 | } |
| 20 | */ | 23 | */ |
| 21 | 24 | ||
| 22 | let mut bl: BootLoader<ERASE_SIZE> = BootLoader::default(); | 25 | let layout = Flash::new_blocking(p.FLASH).into_blocking_regions(); |
| 23 | let mut flash = Flash::unlock(p.FLASH); | 26 | let flash = Mutex::new(RefCell::new(layout.bank1_region)); |
| 24 | let start = bl.prepare(&mut SingleFlashProvider::new(&mut flash)); | 27 | |
| 25 | core::mem::drop(flash); | 28 | let config = BootLoaderConfig::from_linkerfile_blocking(&flash); |
| 26 | unsafe { bl.load(start) } | 29 | let active_offset = config.active.offset(); |
| 30 | let mut bl: BootLoader<_, _, _, 2048> = BootLoader::new(config); | ||
| 31 | |||
| 32 | bl.prepare(); | ||
| 33 | |||
| 34 | unsafe { bl.load(BANK1_REGION.base + active_offset) } | ||
| 27 | } | 35 | } |
| 28 | 36 | ||
| 29 | #[no_mangle] | 37 | #[no_mangle] |
diff --git a/examples/nrf-rtos-trace/.cargo/config.toml b/examples/nrf-rtos-trace/.cargo/config.toml index 8ca28df39..17616a054 100644 --- a/examples/nrf-rtos-trace/.cargo/config.toml +++ b/examples/nrf-rtos-trace/.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 nRF82840_xxAA with your chip as listed in `probe-run --list-chips` | 2 | # replace nRF82840_xxAA with your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip nRF52840_xxAA" | 3 | runner = "probe-rs run --chip nRF52840_xxAA" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv7em-none-eabi" | 6 | target = "thumbv7em-none-eabi" |
diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index 87c9f33f5..30b67b7b2 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-nrf-rtos-trace-examples" | 3 | name = "embassy-nrf-rtos-trace-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [features] | 7 | [features] |
| 7 | default = ["log", "nightly"] | 8 | default = ["log", "nightly"] |
| @@ -15,12 +16,12 @@ log = [ | |||
| 15 | ] | 16 | ] |
| 16 | 17 | ||
| 17 | [dependencies] | 18 | [dependencies] |
| 18 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } | 19 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } |
| 19 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features=["rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } | 20 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } |
| 20 | embassy-time = { version = "0.1.0", path = "../../embassy-time" } | 21 | embassy-time = { version = "0.1.2", path = "../../embassy-time" } |
| 21 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } | 22 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } |
| 22 | 23 | ||
| 23 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 24 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 24 | cortex-m-rt = "0.7.0" | 25 | cortex-m-rt = "0.7.0" |
| 25 | panic-probe = { version = "0.3" } | 26 | panic-probe = { version = "0.3" } |
| 26 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 27 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } |
diff --git a/examples/nrf-rtos-trace/src/bin/rtos_trace.rs b/examples/nrf-rtos-trace/src/bin/rtos_trace.rs index 7d1ad87c8..cf8b2f808 100644 --- a/examples/nrf-rtos-trace/src/bin/rtos_trace.rs +++ b/examples/nrf-rtos-trace/src/bin/rtos_trace.rs | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | 4 | ||
| 5 | use core::future::poll_fn; | ||
| 5 | use core::task::Poll; | 6 | use core::task::Poll; |
| 6 | 7 | ||
| 7 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| @@ -46,7 +47,7 @@ async fn run2() { | |||
| 46 | 47 | ||
| 47 | #[embassy_executor::task] | 48 | #[embassy_executor::task] |
| 48 | async fn run3() { | 49 | async fn run3() { |
| 49 | futures::future::poll_fn(|cx| { | 50 | poll_fn(|cx| { |
| 50 | cx.waker().wake_by_ref(); | 51 | cx.waker().wake_by_ref(); |
| 51 | Poll::<()>::Pending | 52 | Poll::<()>::Pending |
| 52 | }) | 53 | }) |
diff --git a/examples/nrf/src/bin/awaitable_timer.rs b/examples/nrf/src/bin/awaitable_timer.rs deleted file mode 100644 index b32af236c..000000000 --- a/examples/nrf/src/bin/awaitable_timer.rs +++ /dev/null | |||
| @@ -1,26 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::info; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::interrupt; | ||
| 8 | use embassy_nrf::timer::Timer; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_nrf::init(Default::default()); | ||
| 14 | let mut t = Timer::new_awaitable(p.TIMER0, interrupt::take!(TIMER0)); | ||
| 15 | // default frequency is 1MHz, so this triggers every second | ||
| 16 | t.cc(0).write(1_000_000); | ||
| 17 | // clear the timer value on cc[0] compare match | ||
| 18 | t.cc(0).short_compare_clear(); | ||
| 19 | t.start(); | ||
| 20 | |||
| 21 | loop { | ||
| 22 | // wait for compare match | ||
| 23 | t.cc(0).wait().await; | ||
| 24 | info!("hardware timer tick"); | ||
| 25 | } | ||
| 26 | } | ||
diff --git a/examples/nrf/src/bin/pdm.rs b/examples/nrf/src/bin/pdm.rs deleted file mode 100644 index 605eca59e..000000000 --- a/examples/nrf/src/bin/pdm.rs +++ /dev/null | |||
| @@ -1,46 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::info; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::interrupt; | ||
| 8 | use embassy_nrf::pdm::{Config, Channels, Pdm}; | ||
| 9 | use embassy_time::{Duration, Timer}; | ||
| 10 | use fixed::types::I7F1; | ||
| 11 | use num_integer::Roots; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | #[embassy_executor::main] | ||
| 15 | async fn main(_p: Spawner) { | ||
| 16 | let mut p = embassy_nrf::init(Default::default()); | ||
| 17 | let mut config = Config::default(); | ||
| 18 | // Pins are correct for the onboard microphone on the Feather nRF52840 Sense. | ||
| 19 | config.channels = Channels::Mono; | ||
| 20 | config.gain_left = I7F1::from_bits(5); // 2.5 dB | ||
| 21 | let mut pdm = Pdm::new(p.PDM, interrupt::take!(PDM), &mut p.P0_00, &mut p.P0_01, config); | ||
| 22 | |||
| 23 | loop { | ||
| 24 | for gain in [I7F1::from_num(-20), I7F1::from_num(0), I7F1::from_num(20)] { | ||
| 25 | pdm.set_gain(gain, gain); | ||
| 26 | info!("Gain = {} dB", defmt::Debug2Format(&gain)); | ||
| 27 | for _ in 0..10 { | ||
| 28 | let mut buf = [0; 1500]; | ||
| 29 | pdm.sample(5, &mut buf).await; | ||
| 30 | let mean = (buf.iter().map(|v| i32::from(*v)).sum::<i32>() / buf.len() as i32) as i16; | ||
| 31 | info!( | ||
| 32 | "{} samples, min {=i16}, max {=i16}, mean {=i16}, AC RMS {=i16}", | ||
| 33 | buf.len(), | ||
| 34 | buf.iter().min().unwrap(), | ||
| 35 | buf.iter().max().unwrap(), | ||
| 36 | mean, | ||
| 37 | ( | ||
| 38 | buf.iter().map(|v| i32::from(*v - mean).pow(2)).fold(0i32, |a,b| a.saturating_add(b)) | ||
| 39 | / buf.len() as i32).sqrt() as i16, | ||
| 40 | ); | ||
| 41 | info!("samples = {}", &buf); | ||
| 42 | Timer::after(Duration::from_millis(100)).await; | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | } | ||
diff --git a/examples/nrf/src/bin/usb_ethernet.rs b/examples/nrf/src/bin/usb_ethernet.rs deleted file mode 100644 index ca6c7e0d1..000000000 --- a/examples/nrf/src/bin/usb_ethernet.rs +++ /dev/null | |||
| @@ -1,275 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(generic_associated_types)] | ||
| 4 | #![feature(type_alias_impl_trait)] | ||
| 5 | |||
| 6 | use core::mem; | ||
| 7 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 8 | use core::task::Waker; | ||
| 9 | |||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_net::tcp::TcpSocket; | ||
| 13 | use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; | ||
| 14 | use embassy_nrf::rng::Rng; | ||
| 15 | use embassy_nrf::usb::{Driver, PowerUsb}; | ||
| 16 | use embassy_nrf::{interrupt, pac, peripherals}; | ||
| 17 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | ||
| 18 | use embassy_sync::channel::Channel; | ||
| 19 | use embassy_usb::{Builder, Config, UsbDevice}; | ||
| 20 | use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; | ||
| 21 | use embedded_io::asynch::{Read, Write}; | ||
| 22 | use static_cell::StaticCell; | ||
| 23 | use {defmt_rtt as _, panic_probe as _}; | ||
| 24 | |||
| 25 | type MyDriver = Driver<'static, peripherals::USBD, PowerUsb>; | ||
| 26 | |||
| 27 | macro_rules! singleton { | ||
| 28 | ($val:expr) => {{ | ||
| 29 | type T = impl Sized; | ||
| 30 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | ||
| 31 | STATIC_CELL.init_with(move || $val) | ||
| 32 | }}; | ||
| 33 | } | ||
| 34 | |||
| 35 | #[embassy_executor::task] | ||
| 36 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { | ||
| 37 | device.run().await | ||
| 38 | } | ||
| 39 | |||
| 40 | #[embassy_executor::task] | ||
| 41 | async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { | ||
| 42 | loop { | ||
| 43 | warn!("WAITING for connection"); | ||
| 44 | LINK_UP.store(false, Ordering::Relaxed); | ||
| 45 | |||
| 46 | class.wait_connection().await.unwrap(); | ||
| 47 | |||
| 48 | warn!("Connected"); | ||
| 49 | LINK_UP.store(true, Ordering::Relaxed); | ||
| 50 | |||
| 51 | loop { | ||
| 52 | let mut p = unwrap!(PacketBox::new(embassy_net::Packet::new())); | ||
| 53 | let n = match class.read_packet(&mut p[..]).await { | ||
| 54 | Ok(n) => n, | ||
| 55 | Err(e) => { | ||
| 56 | warn!("error reading packet: {:?}", e); | ||
| 57 | break; | ||
| 58 | } | ||
| 59 | }; | ||
| 60 | |||
| 61 | let buf = p.slice(0..n); | ||
| 62 | if RX_CHANNEL.try_send(buf).is_err() { | ||
| 63 | warn!("Failed pushing rx'd packet to channel."); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | #[embassy_executor::task] | ||
| 70 | async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { | ||
| 71 | loop { | ||
| 72 | let pkt = TX_CHANNEL.recv().await; | ||
| 73 | if let Err(e) = class.write_packet(&pkt[..]).await { | ||
| 74 | warn!("Failed to TX packet: {:?}", e); | ||
| 75 | } | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | #[embassy_executor::task] | ||
| 80 | async fn net_task(stack: &'static Stack<Device>) -> ! { | ||
| 81 | stack.run().await | ||
| 82 | } | ||
| 83 | |||
| 84 | #[embassy_executor::main] | ||
| 85 | async fn main(spawner: Spawner) { | ||
| 86 | let p = embassy_nrf::init(Default::default()); | ||
| 87 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 88 | |||
| 89 | info!("Enabling ext hfosc..."); | ||
| 90 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | ||
| 91 | while clock.events_hfclkstarted.read().bits() != 1 {} | ||
| 92 | |||
| 93 | // Create the driver, from the HAL. | ||
| 94 | let irq = interrupt::take!(USBD); | ||
| 95 | let power_irq = interrupt::take!(POWER_CLOCK); | ||
| 96 | let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); | ||
| 97 | |||
| 98 | // Create embassy-usb Config | ||
| 99 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 100 | config.manufacturer = Some("Embassy"); | ||
| 101 | config.product = Some("USB-Ethernet example"); | ||
| 102 | config.serial_number = Some("12345678"); | ||
| 103 | config.max_power = 100; | ||
| 104 | config.max_packet_size_0 = 64; | ||
| 105 | |||
| 106 | // Required for Windows support. | ||
| 107 | config.composite_with_iads = true; | ||
| 108 | config.device_class = 0xEF; | ||
| 109 | config.device_sub_class = 0x02; | ||
| 110 | config.device_protocol = 0x01; | ||
| 111 | |||
| 112 | struct Resources { | ||
| 113 | device_descriptor: [u8; 256], | ||
| 114 | config_descriptor: [u8; 256], | ||
| 115 | bos_descriptor: [u8; 256], | ||
| 116 | control_buf: [u8; 128], | ||
| 117 | serial_state: State<'static>, | ||
| 118 | } | ||
| 119 | let res: &mut Resources = singleton!(Resources { | ||
| 120 | device_descriptor: [0; 256], | ||
| 121 | config_descriptor: [0; 256], | ||
| 122 | bos_descriptor: [0; 256], | ||
| 123 | control_buf: [0; 128], | ||
| 124 | serial_state: State::new(), | ||
| 125 | }); | ||
| 126 | |||
| 127 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 128 | let mut builder = Builder::new( | ||
| 129 | driver, | ||
| 130 | config, | ||
| 131 | &mut res.device_descriptor, | ||
| 132 | &mut res.config_descriptor, | ||
| 133 | &mut res.bos_descriptor, | ||
| 134 | &mut res.control_buf, | ||
| 135 | None, | ||
| 136 | ); | ||
| 137 | |||
| 138 | // WARNINGS for Android ethernet tethering: | ||
| 139 | // - On Pixel 4a, it refused to work on Android 11, worked on Android 12. | ||
| 140 | // - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), | ||
| 141 | // it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. | ||
| 142 | // This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 | ||
| 143 | // and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 | ||
| 144 | |||
| 145 | // Our MAC addr. | ||
| 146 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; | ||
| 147 | // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. | ||
| 148 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; | ||
| 149 | |||
| 150 | // Create classes on the builder. | ||
| 151 | let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64); | ||
| 152 | |||
| 153 | // Build the builder. | ||
| 154 | let usb = builder.build(); | ||
| 155 | |||
| 156 | unwrap!(spawner.spawn(usb_task(usb))); | ||
| 157 | |||
| 158 | let (tx, rx) = class.split(); | ||
| 159 | unwrap!(spawner.spawn(usb_ncm_rx_task(rx))); | ||
| 160 | unwrap!(spawner.spawn(usb_ncm_tx_task(tx))); | ||
| 161 | |||
| 162 | let config = embassy_net::ConfigStrategy::Dhcp; | ||
| 163 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { | ||
| 164 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | ||
| 165 | // dns_servers: Vec::new(), | ||
| 166 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | ||
| 167 | //}); | ||
| 168 | |||
| 169 | // Generate random seed | ||
| 170 | let mut rng = Rng::new(p.RNG, interrupt::take!(RNG)); | ||
| 171 | let mut seed = [0; 8]; | ||
| 172 | rng.blocking_fill_bytes(&mut seed); | ||
| 173 | let seed = u64::from_le_bytes(seed); | ||
| 174 | |||
| 175 | // Init network stack | ||
| 176 | let device = Device { mac_addr: our_mac_addr }; | ||
| 177 | let stack = &*singleton!(Stack::new( | ||
| 178 | device, | ||
| 179 | config, | ||
| 180 | singleton!(StackResources::<1, 2, 8>::new()), | ||
| 181 | seed | ||
| 182 | )); | ||
| 183 | |||
| 184 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 185 | |||
| 186 | // And now we can use it! | ||
| 187 | |||
| 188 | let mut rx_buffer = [0; 4096]; | ||
| 189 | let mut tx_buffer = [0; 4096]; | ||
| 190 | let mut buf = [0; 4096]; | ||
| 191 | |||
| 192 | loop { | ||
| 193 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 194 | socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); | ||
| 195 | |||
| 196 | info!("Listening on TCP:1234..."); | ||
| 197 | if let Err(e) = socket.accept(1234).await { | ||
| 198 | warn!("accept error: {:?}", e); | ||
| 199 | continue; | ||
| 200 | } | ||
| 201 | |||
| 202 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 203 | |||
| 204 | loop { | ||
| 205 | let n = match socket.read(&mut buf).await { | ||
| 206 | Ok(0) => { | ||
| 207 | warn!("read EOF"); | ||
| 208 | break; | ||
| 209 | } | ||
| 210 | Ok(n) => n, | ||
| 211 | Err(e) => { | ||
| 212 | warn!("read error: {:?}", e); | ||
| 213 | break; | ||
| 214 | } | ||
| 215 | }; | ||
| 216 | |||
| 217 | info!("rxd {:02x}", &buf[..n]); | ||
| 218 | |||
| 219 | match socket.write_all(&buf[..n]).await { | ||
| 220 | Ok(()) => {} | ||
| 221 | Err(e) => { | ||
| 222 | warn!("write error: {:?}", e); | ||
| 223 | break; | ||
| 224 | } | ||
| 225 | }; | ||
| 226 | } | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | static TX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new(); | ||
| 231 | static RX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new(); | ||
| 232 | static LINK_UP: AtomicBool = AtomicBool::new(false); | ||
| 233 | |||
| 234 | struct Device { | ||
| 235 | mac_addr: [u8; 6], | ||
| 236 | } | ||
| 237 | |||
| 238 | impl embassy_net::Device for Device { | ||
| 239 | fn register_waker(&mut self, waker: &Waker) { | ||
| 240 | // loopy loopy wakey wakey | ||
| 241 | waker.wake_by_ref() | ||
| 242 | } | ||
| 243 | |||
| 244 | fn link_state(&mut self) -> embassy_net::LinkState { | ||
| 245 | match LINK_UP.load(Ordering::Relaxed) { | ||
| 246 | true => embassy_net::LinkState::Up, | ||
| 247 | false => embassy_net::LinkState::Down, | ||
| 248 | } | ||
| 249 | } | ||
| 250 | |||
| 251 | fn capabilities(&self) -> embassy_net::DeviceCapabilities { | ||
| 252 | let mut caps = embassy_net::DeviceCapabilities::default(); | ||
| 253 | caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header | ||
| 254 | caps.medium = embassy_net::Medium::Ethernet; | ||
| 255 | caps | ||
| 256 | } | ||
| 257 | |||
| 258 | fn is_transmit_ready(&mut self) -> bool { | ||
| 259 | true | ||
| 260 | } | ||
| 261 | |||
| 262 | fn transmit(&mut self, pkt: PacketBuf) { | ||
| 263 | if TX_CHANNEL.try_send(pkt).is_err() { | ||
| 264 | warn!("TX failed") | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | fn receive<'a>(&mut self) -> Option<PacketBuf> { | ||
| 269 | RX_CHANNEL.try_recv().ok() | ||
| 270 | } | ||
| 271 | |||
| 272 | fn ethernet_address(&self) -> [u8; 6] { | ||
| 273 | self.mac_addr | ||
| 274 | } | ||
| 275 | } | ||
diff --git a/examples/nrf/.cargo/config.toml b/examples/nrf52840-rtic/.cargo/config.toml index 8ca28df39..17616a054 100644 --- a/examples/nrf/.cargo/config.toml +++ b/examples/nrf52840-rtic/.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 nRF82840_xxAA with your chip as listed in `probe-run --list-chips` | 2 | # replace nRF82840_xxAA with your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip nRF52840_xxAA" | 3 | runner = "probe-rs run --chip nRF52840_xxAA" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv7em-none-eabi" | 6 | target = "thumbv7em-none-eabi" |
diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml new file mode 100644 index 000000000..ded3b7db8 --- /dev/null +++ b/examples/nrf52840-rtic/Cargo.toml | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-nrf52840-rtic-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | rtic = { version = "2", features = ["thumbv7-backend"] } | ||
| 9 | |||
| 10 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||
| 11 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 12 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "generic-queue"] } | ||
| 13 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nightly", "unstable-traits", "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | ||
| 14 | |||
| 15 | defmt = "0.3" | ||
| 16 | defmt-rtt = "0.4" | ||
| 17 | |||
| 18 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 19 | cortex-m-rt = "0.7.0" | ||
| 20 | panic-probe = { version = "0.3", features = ["print-defmt"] } | ||
| 21 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | ||
diff --git a/examples/nrf52840-rtic/build.rs b/examples/nrf52840-rtic/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/nrf52840-rtic/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/nrf/memory.x b/examples/nrf52840-rtic/memory.x index 9b04edec0..9b04edec0 100644 --- a/examples/nrf/memory.x +++ b/examples/nrf52840-rtic/memory.x | |||
diff --git a/examples/nrf52840-rtic/src/bin/blinky.rs b/examples/nrf52840-rtic/src/bin/blinky.rs new file mode 100644 index 000000000..a682c1932 --- /dev/null +++ b/examples/nrf52840-rtic/src/bin/blinky.rs | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use {defmt_rtt as _, panic_probe as _}; | ||
| 6 | |||
| 7 | #[rtic::app(device = embassy_nrf, peripherals = false, dispatchers = [SWI0_EGU0, SWI1_EGU1])] | ||
| 8 | mod app { | ||
| 9 | use defmt::info; | ||
| 10 | use embassy_nrf::gpio::{Level, Output, OutputDrive}; | ||
| 11 | use embassy_nrf::peripherals; | ||
| 12 | use embassy_time::{Duration, Timer}; | ||
| 13 | |||
| 14 | #[shared] | ||
| 15 | struct Shared {} | ||
| 16 | |||
| 17 | #[local] | ||
| 18 | struct Local {} | ||
| 19 | |||
| 20 | #[init] | ||
| 21 | fn init(_: init::Context) -> (Shared, Local) { | ||
| 22 | info!("Hello World!"); | ||
| 23 | |||
| 24 | let p = embassy_nrf::init(Default::default()); | ||
| 25 | blink::spawn(p.P0_13).map_err(|_| ()).unwrap(); | ||
| 26 | |||
| 27 | (Shared {}, Local {}) | ||
| 28 | } | ||
| 29 | |||
| 30 | #[task(priority = 1)] | ||
| 31 | async fn blink(_cx: blink::Context, pin: peripherals::P0_13) { | ||
| 32 | let mut led = Output::new(pin, Level::Low, OutputDrive::Standard); | ||
| 33 | |||
| 34 | loop { | ||
| 35 | info!("off!"); | ||
| 36 | led.set_high(); | ||
| 37 | Timer::after(Duration::from_millis(300)).await; | ||
| 38 | info!("on!"); | ||
| 39 | led.set_low(); | ||
| 40 | Timer::after(Duration::from_millis(300)).await; | ||
| 41 | } | ||
| 42 | } | ||
| 43 | } | ||
diff --git a/examples/nrf52840/.cargo/config.toml b/examples/nrf52840/.cargo/config.toml new file mode 100644 index 000000000..17616a054 --- /dev/null +++ b/examples/nrf52840/.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 nRF52840_xxAA" | ||
| 4 | |||
| 5 | [build] | ||
| 6 | target = "thumbv7em-none-eabi" | ||
| 7 | |||
| 8 | [env] | ||
| 9 | DEFMT_LOG = "trace" | ||
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml new file mode 100644 index 000000000..9b41ec5ab --- /dev/null +++ b/examples/nrf52840/Cargo.toml | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-nrf52840-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [features] | ||
| 8 | default = ["nightly"] | ||
| 9 | nightly = [ | ||
| 10 | "embedded-hal-async", | ||
| 11 | "embassy-executor/nightly", | ||
| 12 | "embassy-nrf/nightly", | ||
| 13 | "embassy-net/nightly", | ||
| 14 | "embassy-net-esp-hosted", | ||
| 15 | "embassy-nrf/unstable-traits", | ||
| 16 | "embassy-time/nightly", | ||
| 17 | "embassy-time/unstable-traits", | ||
| 18 | "static_cell/nightly", | ||
| 19 | "embassy-usb", | ||
| 20 | "embedded-io/async", | ||
| 21 | "embassy-net", | ||
| 22 | "embassy-lora", | ||
| 23 | "lora-phy", | ||
| 24 | "lorawan-device", | ||
| 25 | "lorawan", | ||
| 26 | ] | ||
| 27 | |||
| 28 | [dependencies] | ||
| 29 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||
| 30 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 31 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | ||
| 32 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | ||
| 33 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | ||
| 34 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } | ||
| 35 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } | ||
| 36 | embedded-io = "0.4.0" | ||
| 37 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true } | ||
| 38 | lora-phy = { version = "1", optional = true } | ||
| 39 | lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } | ||
| 40 | lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } | ||
| 41 | embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"], optional = true } | ||
| 42 | |||
| 43 | defmt = "0.3" | ||
| 44 | defmt-rtt = "0.4" | ||
| 45 | |||
| 46 | fixed = "1.10.0" | ||
| 47 | static_cell = "1.1" | ||
| 48 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 49 | cortex-m-rt = "0.7.0" | ||
| 50 | panic-probe = { version = "0.3", features = ["print-defmt"] } | ||
| 51 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | ||
| 52 | rand = { version = "0.8.4", default-features = false } | ||
| 53 | embedded-storage = "0.3.0" | ||
| 54 | usbd-hid = "0.6.0" | ||
| 55 | serde = { version = "1.0.136", default-features = false } | ||
| 56 | embedded-hal-async = { version = "0.2.0-alpha.2", optional = true } | ||
| 57 | num-integer = { version = "0.1.45", default-features = false } | ||
| 58 | microfft = "0.5.0" | ||
| 59 | |||
| 60 | [patch.crates-io] | ||
| 61 | lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } | ||
diff --git a/examples/nrf52840/build.rs b/examples/nrf52840/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/nrf52840/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/nrf52840/memory.x b/examples/nrf52840/memory.x new file mode 100644 index 000000000..9b04edec0 --- /dev/null +++ b/examples/nrf52840/memory.x | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | /* NOTE 1 K = 1 KiBi = 1024 bytes */ | ||
| 4 | /* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */ | ||
| 5 | FLASH : ORIGIN = 0x00000000, LENGTH = 1024K | ||
| 6 | RAM : ORIGIN = 0x20000000, LENGTH = 256K | ||
| 7 | } | ||
diff --git a/examples/nrf/src/bin/blinky.rs b/examples/nrf52840/src/bin/blinky.rs index 513f6cd82..513f6cd82 100644 --- a/examples/nrf/src/bin/blinky.rs +++ b/examples/nrf52840/src/bin/blinky.rs | |||
diff --git a/examples/nrf/src/bin/buffered_uart.rs b/examples/nrf52840/src/bin/buffered_uart.rs index ea566f4b2..238695371 100644 --- a/examples/nrf/src/bin/buffered_uart.rs +++ b/examples/nrf52840/src/bin/buffered_uart.rs | |||
| @@ -4,12 +4,15 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_nrf::buffered_uarte::{BufferedUarte, State}; | 7 | use embassy_nrf::buffered_uarte::{self, BufferedUarte}; |
| 8 | use embassy_nrf::{interrupt, uarte}; | 8 | use embassy_nrf::{bind_interrupts, peripherals, uarte}; |
| 9 | use embedded_io::asynch::{BufRead, Write}; | 9 | use embedded_io::asynch::Write; |
| 10 | use futures::pin_mut; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 11 | ||
| 12 | bind_interrupts!(struct Irqs { | ||
| 13 | UARTE0_UART0 => buffered_uarte::InterruptHandler<peripherals::UARTE0>; | ||
| 14 | }); | ||
| 15 | |||
| 13 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 14 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 15 | let p = embassy_nrf::init(Default::default()); | 18 | let p = embassy_nrf::init(Default::default()); |
| @@ -20,25 +23,19 @@ async fn main(_spawner: Spawner) { | |||
| 20 | let mut tx_buffer = [0u8; 4096]; | 23 | let mut tx_buffer = [0u8; 4096]; |
| 21 | let mut rx_buffer = [0u8; 4096]; | 24 | let mut rx_buffer = [0u8; 4096]; |
| 22 | 25 | ||
| 23 | let irq = interrupt::take!(UARTE0_UART0); | 26 | let mut u = BufferedUarte::new( |
| 24 | let mut state = State::new(); | ||
| 25 | // Please note - important to have hardware flow control (https://github.com/embassy-rs/embassy/issues/536) | ||
| 26 | let u = BufferedUarte::new( | ||
| 27 | &mut state, | ||
| 28 | p.UARTE0, | 27 | p.UARTE0, |
| 29 | p.TIMER0, | 28 | p.TIMER0, |
| 30 | p.PPI_CH0, | 29 | p.PPI_CH0, |
| 31 | p.PPI_CH1, | 30 | p.PPI_CH1, |
| 32 | irq, | 31 | p.PPI_GROUP0, |
| 32 | Irqs, | ||
| 33 | p.P0_08, | 33 | p.P0_08, |
| 34 | p.P0_06, | 34 | p.P0_06, |
| 35 | p.P0_07, | ||
| 36 | p.P0_05, | ||
| 37 | config, | 35 | config, |
| 38 | &mut rx_buffer, | 36 | &mut rx_buffer, |
| 39 | &mut tx_buffer, | 37 | &mut tx_buffer, |
| 40 | ); | 38 | ); |
| 41 | pin_mut!(u); | ||
| 42 | 39 | ||
| 43 | info!("uarte initialized!"); | 40 | info!("uarte initialized!"); |
| 44 | 41 | ||
diff --git a/examples/nrf/src/bin/channel.rs b/examples/nrf52840/src/bin/channel.rs index d782a79e7..d782a79e7 100644 --- a/examples/nrf/src/bin/channel.rs +++ b/examples/nrf52840/src/bin/channel.rs | |||
diff --git a/examples/nrf/src/bin/channel_sender_receiver.rs b/examples/nrf52840/src/bin/channel_sender_receiver.rs index fcccdaed5..fcccdaed5 100644 --- a/examples/nrf/src/bin/channel_sender_receiver.rs +++ b/examples/nrf52840/src/bin/channel_sender_receiver.rs | |||
diff --git a/examples/nrf/src/bin/executor_fairness_test.rs b/examples/nrf52840/src/bin/executor_fairness_test.rs index 9ae030d07..2a28f2763 100644 --- a/examples/nrf/src/bin/executor_fairness_test.rs +++ b/examples/nrf52840/src/bin/executor_fairness_test.rs | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | 4 | ||
| 5 | use core::future::poll_fn; | ||
| 5 | use core::task::Poll; | 6 | use core::task::Poll; |
| 6 | 7 | ||
| 7 | use defmt::{info, unwrap}; | 8 | use defmt::{info, unwrap}; |
| @@ -26,7 +27,7 @@ async fn run2() { | |||
| 26 | 27 | ||
| 27 | #[embassy_executor::task] | 28 | #[embassy_executor::task] |
| 28 | async fn run3() { | 29 | async fn run3() { |
| 29 | futures::future::poll_fn(|cx| { | 30 | poll_fn(|cx| { |
| 30 | cx.waker().wake_by_ref(); | 31 | cx.waker().wake_by_ref(); |
| 31 | Poll::<()>::Pending | 32 | Poll::<()>::Pending |
| 32 | }) | 33 | }) |
diff --git a/examples/nrf/src/bin/gpiote_channel.rs b/examples/nrf52840/src/bin/gpiote_channel.rs index 5bfd02465..5bfd02465 100644 --- a/examples/nrf/src/bin/gpiote_channel.rs +++ b/examples/nrf52840/src/bin/gpiote_channel.rs | |||
diff --git a/examples/nrf/src/bin/gpiote_port.rs b/examples/nrf52840/src/bin/gpiote_port.rs index 0155d539e..0155d539e 100644 --- a/examples/nrf/src/bin/gpiote_port.rs +++ b/examples/nrf52840/src/bin/gpiote_port.rs | |||
diff --git a/examples/nrf52840/src/bin/i2s_effect.rs b/examples/nrf52840/src/bin/i2s_effect.rs new file mode 100644 index 000000000..391514d93 --- /dev/null +++ b/examples/nrf52840/src/bin/i2s_effect.rs | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::f32::consts::PI; | ||
| 6 | |||
| 7 | use defmt::{error, info}; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_nrf::i2s::{self, Channels, Config, MasterClock, MultiBuffering, Sample as _, SampleWidth, I2S}; | ||
| 10 | use embassy_nrf::{bind_interrupts, peripherals}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | type Sample = i16; | ||
| 14 | |||
| 15 | const NUM_BUFFERS: usize = 2; | ||
| 16 | const NUM_SAMPLES: usize = 4; | ||
| 17 | |||
| 18 | bind_interrupts!(struct Irqs { | ||
| 19 | I2S => i2s::InterruptHandler<peripherals::I2S>; | ||
| 20 | }); | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(_spawner: Spawner) { | ||
| 24 | let p = embassy_nrf::init(Default::default()); | ||
| 25 | |||
| 26 | let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into(); | ||
| 27 | |||
| 28 | let sample_rate = master_clock.sample_rate(); | ||
| 29 | info!("Sample rate: {}", sample_rate); | ||
| 30 | |||
| 31 | let mut config = Config::default(); | ||
| 32 | config.sample_width = SampleWidth::_16bit; | ||
| 33 | config.channels = Channels::MonoLeft; | ||
| 34 | |||
| 35 | let buffers_out = MultiBuffering::<Sample, NUM_BUFFERS, NUM_SAMPLES>::new(); | ||
| 36 | let buffers_in = MultiBuffering::<Sample, NUM_BUFFERS, NUM_SAMPLES>::new(); | ||
| 37 | let mut full_duplex_stream = I2S::new_master(p.I2S, Irqs, p.P0_25, p.P0_26, p.P0_27, master_clock, config) | ||
| 38 | .full_duplex(p.P0_29, p.P0_28, buffers_out, buffers_in); | ||
| 39 | |||
| 40 | let mut modulator = SineOsc::new(); | ||
| 41 | modulator.set_frequency(8.0, 1.0 / sample_rate as f32); | ||
| 42 | modulator.set_amplitude(1.0); | ||
| 43 | |||
| 44 | full_duplex_stream.start().await.expect("I2S Start"); | ||
| 45 | |||
| 46 | loop { | ||
| 47 | let (buff_out, buff_in) = full_duplex_stream.buffers(); | ||
| 48 | for i in 0..NUM_SAMPLES { | ||
| 49 | let modulation = (Sample::SCALE as f32 * bipolar_to_unipolar(modulator.generate())) as Sample; | ||
| 50 | buff_out[i] = buff_in[i] * modulation; | ||
| 51 | } | ||
| 52 | |||
| 53 | if let Err(err) = full_duplex_stream.send_and_receive().await { | ||
| 54 | error!("{}", err); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | struct SineOsc { | ||
| 60 | amplitude: f32, | ||
| 61 | modulo: f32, | ||
| 62 | phase_inc: f32, | ||
| 63 | } | ||
| 64 | |||
| 65 | impl SineOsc { | ||
| 66 | const B: f32 = 4.0 / PI; | ||
| 67 | const C: f32 = -4.0 / (PI * PI); | ||
| 68 | const P: f32 = 0.225; | ||
| 69 | |||
| 70 | pub fn new() -> Self { | ||
| 71 | Self { | ||
| 72 | amplitude: 1.0, | ||
| 73 | modulo: 0.0, | ||
| 74 | phase_inc: 0.0, | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | pub fn set_frequency(&mut self, freq: f32, inv_sample_rate: f32) { | ||
| 79 | self.phase_inc = freq * inv_sample_rate; | ||
| 80 | } | ||
| 81 | |||
| 82 | pub fn set_amplitude(&mut self, amplitude: f32) { | ||
| 83 | self.amplitude = amplitude; | ||
| 84 | } | ||
| 85 | |||
| 86 | pub fn generate(&mut self) -> f32 { | ||
| 87 | let signal = self.parabolic_sin(self.modulo); | ||
| 88 | self.modulo += self.phase_inc; | ||
| 89 | if self.modulo < 0.0 { | ||
| 90 | self.modulo += 1.0; | ||
| 91 | } else if self.modulo > 1.0 { | ||
| 92 | self.modulo -= 1.0; | ||
| 93 | } | ||
| 94 | signal * self.amplitude | ||
| 95 | } | ||
| 96 | |||
| 97 | fn parabolic_sin(&mut self, modulo: f32) -> f32 { | ||
| 98 | let angle = PI - modulo * 2.0 * PI; | ||
| 99 | let y = Self::B * angle + Self::C * angle * abs(angle); | ||
| 100 | Self::P * (y * abs(y) - y) + y | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | #[inline] | ||
| 105 | fn abs(value: f32) -> f32 { | ||
| 106 | if value < 0.0 { | ||
| 107 | -value | ||
| 108 | } else { | ||
| 109 | value | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | #[inline] | ||
| 114 | fn bipolar_to_unipolar(value: f32) -> f32 { | ||
| 115 | (value + 1.0) / 2.0 | ||
| 116 | } | ||
diff --git a/examples/nrf52840/src/bin/i2s_monitor.rs b/examples/nrf52840/src/bin/i2s_monitor.rs new file mode 100644 index 000000000..4ed597c0d --- /dev/null +++ b/examples/nrf52840/src/bin/i2s_monitor.rs | |||
| @@ -0,0 +1,118 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{debug, error, info}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, MasterClock, Sample as _, SampleWidth, I2S}; | ||
| 8 | use embassy_nrf::pwm::{Prescaler, SimplePwm}; | ||
| 9 | use embassy_nrf::{bind_interrupts, peripherals}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | type Sample = i16; | ||
| 13 | |||
| 14 | const NUM_SAMPLES: usize = 500; | ||
| 15 | |||
| 16 | bind_interrupts!(struct Irqs { | ||
| 17 | I2S => i2s::InterruptHandler<peripherals::I2S>; | ||
| 18 | }); | ||
| 19 | |||
| 20 | #[embassy_executor::main] | ||
| 21 | async fn main(_spawner: Spawner) { | ||
| 22 | let p = embassy_nrf::init(Default::default()); | ||
| 23 | |||
| 24 | let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into(); | ||
| 25 | |||
| 26 | let sample_rate = master_clock.sample_rate(); | ||
| 27 | info!("Sample rate: {}", sample_rate); | ||
| 28 | |||
| 29 | let mut config = Config::default(); | ||
| 30 | config.sample_width = SampleWidth::_16bit; | ||
| 31 | config.channels = Channels::MonoLeft; | ||
| 32 | |||
| 33 | let buffers = DoubleBuffering::<Sample, NUM_SAMPLES>::new(); | ||
| 34 | let mut input_stream = | ||
| 35 | I2S::new_master(p.I2S, Irqs, p.P0_25, p.P0_26, p.P0_27, master_clock, config).input(p.P0_29, buffers); | ||
| 36 | |||
| 37 | // Configure the PWM to use the pins corresponding to the RGB leds | ||
| 38 | let mut pwm = SimplePwm::new_3ch(p.PWM0, p.P0_23, p.P0_22, p.P0_24); | ||
| 39 | pwm.set_prescaler(Prescaler::Div1); | ||
| 40 | pwm.set_max_duty(255); | ||
| 41 | |||
| 42 | let mut rms_online = RmsOnline::<NUM_SAMPLES>::default(); | ||
| 43 | |||
| 44 | input_stream.start().await.expect("I2S Start"); | ||
| 45 | |||
| 46 | loop { | ||
| 47 | let rms = rms_online.process(input_stream.buffer()); | ||
| 48 | let rgb = rgb_from_rms(rms); | ||
| 49 | |||
| 50 | debug!("RMS: {}, RGB: {:?}", rms, rgb); | ||
| 51 | for i in 0..3 { | ||
| 52 | pwm.set_duty(i, rgb[i].into()); | ||
| 53 | } | ||
| 54 | |||
| 55 | if let Err(err) = input_stream.receive().await { | ||
| 56 | error!("{}", err); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | /// RMS from 0.0 until 0.75 will give green with a proportional intensity | ||
| 62 | /// RMS from 0.75 until 0.9 will give a blend between orange and red proportionally to the intensity | ||
| 63 | /// RMS above 0.9 will give a red with a proportional intensity | ||
| 64 | fn rgb_from_rms(rms: f32) -> [u8; 3] { | ||
| 65 | if rms < 0.75 { | ||
| 66 | let intensity = rms / 0.75; | ||
| 67 | [0, (intensity * 165.0) as u8, 0] | ||
| 68 | } else if rms < 0.9 { | ||
| 69 | let intensity = (rms - 0.75) / 0.15; | ||
| 70 | [200, 165 - (165.0 * intensity) as u8, 0] | ||
| 71 | } else { | ||
| 72 | let intensity = (rms - 0.9) / 0.1; | ||
| 73 | [200 + (55.0 * intensity) as u8, 0, 0] | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | pub struct RmsOnline<const N: usize> { | ||
| 78 | pub squares: [f32; N], | ||
| 79 | pub head: usize, | ||
| 80 | } | ||
| 81 | |||
| 82 | impl<const N: usize> Default for RmsOnline<N> { | ||
| 83 | fn default() -> Self { | ||
| 84 | RmsOnline { | ||
| 85 | squares: [0.0; N], | ||
| 86 | head: 0, | ||
| 87 | } | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | impl<const N: usize> RmsOnline<N> { | ||
| 92 | pub fn reset(&mut self) { | ||
| 93 | self.squares = [0.0; N]; | ||
| 94 | self.head = 0; | ||
| 95 | } | ||
| 96 | |||
| 97 | pub fn process(&mut self, buf: &[Sample]) -> f32 { | ||
| 98 | buf.iter() | ||
| 99 | .for_each(|sample| self.push(*sample as f32 / Sample::SCALE as f32)); | ||
| 100 | |||
| 101 | let sum_of_squares = self.squares.iter().fold(0.0, |acc, v| acc + *v); | ||
| 102 | Self::approx_sqrt(sum_of_squares / N as f32) | ||
| 103 | } | ||
| 104 | |||
| 105 | pub fn push(&mut self, signal: f32) { | ||
| 106 | let square = signal * signal; | ||
| 107 | self.squares[self.head] = square; | ||
| 108 | self.head = (self.head + 1) % N; | ||
| 109 | } | ||
| 110 | |||
| 111 | /// Approximated sqrt taken from [micromath] | ||
| 112 | /// | ||
| 113 | /// [micromath]: https://docs.rs/micromath/latest/src/micromath/float/sqrt.rs.html#11-17 | ||
| 114 | /// | ||
| 115 | fn approx_sqrt(value: f32) -> f32 { | ||
| 116 | f32::from_bits((value.to_bits() + 0x3f80_0000) >> 1) | ||
| 117 | } | ||
| 118 | } | ||
diff --git a/examples/nrf52840/src/bin/i2s_waveform.rs b/examples/nrf52840/src/bin/i2s_waveform.rs new file mode 100644 index 000000000..f2c1166b1 --- /dev/null +++ b/examples/nrf52840/src/bin/i2s_waveform.rs | |||
| @@ -0,0 +1,154 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::f32::consts::PI; | ||
| 6 | |||
| 7 | use defmt::{error, info}; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, MasterClock, Sample as _, SampleWidth, I2S}; | ||
| 10 | use embassy_nrf::{bind_interrupts, peripherals}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | type Sample = i16; | ||
| 14 | |||
| 15 | const NUM_SAMPLES: usize = 50; | ||
| 16 | |||
| 17 | bind_interrupts!(struct Irqs { | ||
| 18 | I2S => i2s::InterruptHandler<peripherals::I2S>; | ||
| 19 | }); | ||
| 20 | |||
| 21 | #[embassy_executor::main] | ||
| 22 | async fn main(_spawner: Spawner) { | ||
| 23 | let p = embassy_nrf::init(Default::default()); | ||
| 24 | |||
| 25 | let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into(); | ||
| 26 | |||
| 27 | let sample_rate = master_clock.sample_rate(); | ||
| 28 | info!("Sample rate: {}", sample_rate); | ||
| 29 | |||
| 30 | let mut config = Config::default(); | ||
| 31 | config.sample_width = SampleWidth::_16bit; | ||
| 32 | config.channels = Channels::MonoLeft; | ||
| 33 | |||
| 34 | let buffers = DoubleBuffering::<Sample, NUM_SAMPLES>::new(); | ||
| 35 | let mut output_stream = | ||
| 36 | I2S::new_master(p.I2S, Irqs, p.P0_25, p.P0_26, p.P0_27, master_clock, config).output(p.P0_28, buffers); | ||
| 37 | |||
| 38 | let mut waveform = Waveform::new(1.0 / sample_rate as f32); | ||
| 39 | |||
| 40 | waveform.process(output_stream.buffer()); | ||
| 41 | |||
| 42 | output_stream.start().await.expect("I2S Start"); | ||
| 43 | |||
| 44 | loop { | ||
| 45 | waveform.process(output_stream.buffer()); | ||
| 46 | |||
| 47 | if let Err(err) = output_stream.send().await { | ||
| 48 | error!("{}", err); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | struct Waveform { | ||
| 54 | inv_sample_rate: f32, | ||
| 55 | carrier: SineOsc, | ||
| 56 | freq_mod: SineOsc, | ||
| 57 | amp_mod: SineOsc, | ||
| 58 | } | ||
| 59 | |||
| 60 | impl Waveform { | ||
| 61 | fn new(inv_sample_rate: f32) -> Self { | ||
| 62 | let mut carrier = SineOsc::new(); | ||
| 63 | carrier.set_frequency(110.0, inv_sample_rate); | ||
| 64 | |||
| 65 | let mut freq_mod = SineOsc::new(); | ||
| 66 | freq_mod.set_frequency(1.0, inv_sample_rate); | ||
| 67 | freq_mod.set_amplitude(1.0); | ||
| 68 | |||
| 69 | let mut amp_mod = SineOsc::new(); | ||
| 70 | amp_mod.set_frequency(16.0, inv_sample_rate); | ||
| 71 | amp_mod.set_amplitude(0.5); | ||
| 72 | |||
| 73 | Self { | ||
| 74 | inv_sample_rate, | ||
| 75 | carrier, | ||
| 76 | freq_mod, | ||
| 77 | amp_mod, | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | fn process(&mut self, buf: &mut [Sample]) { | ||
| 82 | for sample in buf.chunks_mut(1) { | ||
| 83 | let freq_modulation = bipolar_to_unipolar(self.freq_mod.generate()); | ||
| 84 | self.carrier | ||
| 85 | .set_frequency(110.0 + 440.0 * freq_modulation, self.inv_sample_rate); | ||
| 86 | |||
| 87 | let amp_modulation = bipolar_to_unipolar(self.amp_mod.generate()); | ||
| 88 | self.carrier.set_amplitude(amp_modulation); | ||
| 89 | |||
| 90 | let signal = self.carrier.generate(); | ||
| 91 | |||
| 92 | sample[0] = (Sample::SCALE as f32 * signal) as Sample; | ||
| 93 | } | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | struct SineOsc { | ||
| 98 | amplitude: f32, | ||
| 99 | modulo: f32, | ||
| 100 | phase_inc: f32, | ||
| 101 | } | ||
| 102 | |||
| 103 | impl SineOsc { | ||
| 104 | const B: f32 = 4.0 / PI; | ||
| 105 | const C: f32 = -4.0 / (PI * PI); | ||
| 106 | const P: f32 = 0.225; | ||
| 107 | |||
| 108 | pub fn new() -> Self { | ||
| 109 | Self { | ||
| 110 | amplitude: 1.0, | ||
| 111 | modulo: 0.0, | ||
| 112 | phase_inc: 0.0, | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | pub fn set_frequency(&mut self, freq: f32, inv_sample_rate: f32) { | ||
| 117 | self.phase_inc = freq * inv_sample_rate; | ||
| 118 | } | ||
| 119 | |||
| 120 | pub fn set_amplitude(&mut self, amplitude: f32) { | ||
| 121 | self.amplitude = amplitude; | ||
| 122 | } | ||
| 123 | |||
| 124 | pub fn generate(&mut self) -> f32 { | ||
| 125 | let signal = self.parabolic_sin(self.modulo); | ||
| 126 | self.modulo += self.phase_inc; | ||
| 127 | if self.modulo < 0.0 { | ||
| 128 | self.modulo += 1.0; | ||
| 129 | } else if self.modulo > 1.0 { | ||
| 130 | self.modulo -= 1.0; | ||
| 131 | } | ||
| 132 | signal * self.amplitude | ||
| 133 | } | ||
| 134 | |||
| 135 | fn parabolic_sin(&mut self, modulo: f32) -> f32 { | ||
| 136 | let angle = PI - modulo * 2.0 * PI; | ||
| 137 | let y = Self::B * angle + Self::C * angle * abs(angle); | ||
| 138 | Self::P * (y * abs(y) - y) + y | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | #[inline] | ||
| 143 | fn abs(value: f32) -> f32 { | ||
| 144 | if value < 0.0 { | ||
| 145 | -value | ||
| 146 | } else { | ||
| 147 | value | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | #[inline] | ||
| 152 | fn bipolar_to_unipolar(value: f32) -> f32 { | ||
| 153 | (value + 1.0) / 2.0 | ||
| 154 | } | ||
diff --git a/examples/nrf52840/src/bin/lora_cad.rs b/examples/nrf52840/src/bin/lora_cad.rs new file mode 100644 index 000000000..beca061ed --- /dev/null +++ b/examples/nrf52840/src/bin/lora_cad.rs | |||
| @@ -0,0 +1,99 @@ | |||
| 1 | //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. | ||
| 2 | //! Other nrf/sx126x combinations may work with appropriate pin modifications. | ||
| 3 | //! It demonstrates LORA CAD functionality. | ||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![macro_use] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||
| 12 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; | ||
| 13 | use embassy_nrf::{bind_interrupts, peripherals, spim}; | ||
| 14 | use embassy_time::{Delay, Duration, Timer}; | ||
| 15 | use lora_phy::mod_params::*; | ||
| 16 | use lora_phy::sx1261_2::SX1261_2; | ||
| 17 | use lora_phy::LoRa; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||
| 21 | |||
| 22 | bind_interrupts!(struct Irqs { | ||
| 23 | SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>; | ||
| 24 | }); | ||
| 25 | |||
| 26 | #[embassy_executor::main] | ||
| 27 | async fn main(_spawner: Spawner) { | ||
| 28 | let p = embassy_nrf::init(Default::default()); | ||
| 29 | let mut spi_config = spim::Config::default(); | ||
| 30 | spi_config.frequency = spim::Frequency::M16; | ||
| 31 | |||
| 32 | let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); | ||
| 33 | |||
| 34 | let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); | ||
| 35 | let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); | ||
| 36 | let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); | ||
| 37 | let busy = Input::new(p.P1_14.degrade(), Pull::Down); | ||
| 38 | let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); | ||
| 39 | let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); | ||
| 40 | |||
| 41 | let iv = | ||
| 42 | GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); | ||
| 43 | |||
| 44 | let mut delay = Delay; | ||
| 45 | |||
| 46 | let mut lora = { | ||
| 47 | match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, &mut delay).await { | ||
| 48 | Ok(l) => l, | ||
| 49 | Err(err) => { | ||
| 50 | info!("Radio error = {}", err); | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | } | ||
| 54 | }; | ||
| 55 | |||
| 56 | let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); | ||
| 57 | let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); | ||
| 58 | |||
| 59 | start_indicator.set_high(); | ||
| 60 | Timer::after(Duration::from_secs(5)).await; | ||
| 61 | start_indicator.set_low(); | ||
| 62 | |||
| 63 | let mdltn_params = { | ||
| 64 | match lora.create_modulation_params( | ||
| 65 | SpreadingFactor::_10, | ||
| 66 | Bandwidth::_250KHz, | ||
| 67 | CodingRate::_4_8, | ||
| 68 | LORA_FREQUENCY_IN_HZ, | ||
| 69 | ) { | ||
| 70 | Ok(mp) => mp, | ||
| 71 | Err(err) => { | ||
| 72 | info!("Radio error = {}", err); | ||
| 73 | return; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | }; | ||
| 77 | |||
| 78 | match lora.prepare_for_cad(&mdltn_params, true).await { | ||
| 79 | Ok(()) => {} | ||
| 80 | Err(err) => { | ||
| 81 | info!("Radio error = {}", err); | ||
| 82 | return; | ||
| 83 | } | ||
| 84 | }; | ||
| 85 | |||
| 86 | match lora.cad().await { | ||
| 87 | Ok(cad_activity_detected) => { | ||
| 88 | if cad_activity_detected { | ||
| 89 | info!("cad successful with activity detected") | ||
| 90 | } else { | ||
| 91 | info!("cad successful without activity detected") | ||
| 92 | } | ||
| 93 | debug_indicator.set_high(); | ||
| 94 | Timer::after(Duration::from_secs(5)).await; | ||
| 95 | debug_indicator.set_low(); | ||
| 96 | } | ||
| 97 | Err(err) => info!("cad unsuccessful = {}", err), | ||
| 98 | } | ||
| 99 | } | ||
diff --git a/examples/nrf52840/src/bin/lora_lorawan.rs b/examples/nrf52840/src/bin/lora_lorawan.rs new file mode 100644 index 000000000..c953680c6 --- /dev/null +++ b/examples/nrf52840/src/bin/lora_lorawan.rs | |||
| @@ -0,0 +1,83 @@ | |||
| 1 | //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. | ||
| 2 | //! Other nrf/sx126x combinations may work with appropriate pin modifications. | ||
| 3 | //! It demonstrates LoRaWAN join functionality. | ||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![macro_use] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||
| 12 | use embassy_lora::LoraTimer; | ||
| 13 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; | ||
| 14 | use embassy_nrf::rng::Rng; | ||
| 15 | use embassy_nrf::{bind_interrupts, peripherals, rng, spim}; | ||
| 16 | use embassy_time::Delay; | ||
| 17 | use lora_phy::mod_params::*; | ||
| 18 | use lora_phy::sx1261_2::SX1261_2; | ||
| 19 | use lora_phy::LoRa; | ||
| 20 | use lorawan::default_crypto::DefaultFactory as Crypto; | ||
| 21 | use lorawan_device::async_device::lora_radio::LoRaRadio; | ||
| 22 | use lorawan_device::async_device::{region, Device, JoinMode}; | ||
| 23 | use {defmt_rtt as _, panic_probe as _}; | ||
| 24 | |||
| 25 | const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region | ||
| 26 | |||
| 27 | bind_interrupts!(struct Irqs { | ||
| 28 | SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>; | ||
| 29 | RNG => rng::InterruptHandler<peripherals::RNG>; | ||
| 30 | }); | ||
| 31 | |||
| 32 | #[embassy_executor::main] | ||
| 33 | async fn main(_spawner: Spawner) { | ||
| 34 | let p = embassy_nrf::init(Default::default()); | ||
| 35 | let mut spi_config = spim::Config::default(); | ||
| 36 | spi_config.frequency = spim::Frequency::M16; | ||
| 37 | |||
| 38 | let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); | ||
| 39 | |||
| 40 | let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); | ||
| 41 | let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); | ||
| 42 | let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); | ||
| 43 | let busy = Input::new(p.P1_14.degrade(), Pull::Down); | ||
| 44 | let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); | ||
| 45 | let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); | ||
| 46 | |||
| 47 | let iv = | ||
| 48 | GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); | ||
| 49 | |||
| 50 | let mut delay = Delay; | ||
| 51 | |||
| 52 | let lora = { | ||
| 53 | match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), true, &mut delay).await { | ||
| 54 | Ok(l) => l, | ||
| 55 | Err(err) => { | ||
| 56 | info!("Radio error = {}", err); | ||
| 57 | return; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | }; | ||
| 61 | |||
| 62 | let radio = LoRaRadio::new(lora); | ||
| 63 | let region: region::Configuration = region::Configuration::new(LORAWAN_REGION); | ||
| 64 | let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG, Irqs)); | ||
| 65 | |||
| 66 | defmt::info!("Joining LoRaWAN network"); | ||
| 67 | |||
| 68 | // TODO: Adjust the EUI and Keys according to your network credentials | ||
| 69 | match device | ||
| 70 | .join(&JoinMode::OTAA { | ||
| 71 | deveui: [0, 0, 0, 0, 0, 0, 0, 0], | ||
| 72 | appeui: [0, 0, 0, 0, 0, 0, 0, 0], | ||
| 73 | appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], | ||
| 74 | }) | ||
| 75 | .await | ||
| 76 | { | ||
| 77 | Ok(()) => defmt::info!("LoRaWAN network joined"), | ||
| 78 | Err(err) => { | ||
| 79 | info!("Radio error = {}", err); | ||
| 80 | return; | ||
| 81 | } | ||
| 82 | }; | ||
| 83 | } | ||
diff --git a/examples/nrf52840/src/bin/lora_p2p_receive.rs b/examples/nrf52840/src/bin/lora_p2p_receive.rs new file mode 100644 index 000000000..563fe42ec --- /dev/null +++ b/examples/nrf52840/src/bin/lora_p2p_receive.rs | |||
| @@ -0,0 +1,121 @@ | |||
| 1 | //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. | ||
| 2 | //! Other nrf/sx126x combinations may work with appropriate pin modifications. | ||
| 3 | //! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example. | ||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![macro_use] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||
| 12 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; | ||
| 13 | use embassy_nrf::{bind_interrupts, peripherals, spim}; | ||
| 14 | use embassy_time::{Delay, Duration, Timer}; | ||
| 15 | use lora_phy::mod_params::*; | ||
| 16 | use lora_phy::sx1261_2::SX1261_2; | ||
| 17 | use lora_phy::LoRa; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||
| 21 | |||
| 22 | bind_interrupts!(struct Irqs { | ||
| 23 | SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>; | ||
| 24 | }); | ||
| 25 | |||
| 26 | #[embassy_executor::main] | ||
| 27 | async fn main(_spawner: Spawner) { | ||
| 28 | let p = embassy_nrf::init(Default::default()); | ||
| 29 | let mut spi_config = spim::Config::default(); | ||
| 30 | spi_config.frequency = spim::Frequency::M16; | ||
| 31 | |||
| 32 | let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); | ||
| 33 | |||
| 34 | let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); | ||
| 35 | let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); | ||
| 36 | let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); | ||
| 37 | let busy = Input::new(p.P1_14.degrade(), Pull::Down); | ||
| 38 | let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); | ||
| 39 | let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); | ||
| 40 | |||
| 41 | let iv = | ||
| 42 | GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); | ||
| 43 | |||
| 44 | let mut delay = Delay; | ||
| 45 | |||
| 46 | let mut lora = { | ||
| 47 | match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, &mut delay).await { | ||
| 48 | Ok(l) => l, | ||
| 49 | Err(err) => { | ||
| 50 | info!("Radio error = {}", err); | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | } | ||
| 54 | }; | ||
| 55 | |||
| 56 | let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); | ||
| 57 | let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); | ||
| 58 | |||
| 59 | start_indicator.set_high(); | ||
| 60 | Timer::after(Duration::from_secs(5)).await; | ||
| 61 | start_indicator.set_low(); | ||
| 62 | |||
| 63 | let mut receiving_buffer = [00u8; 100]; | ||
| 64 | |||
| 65 | let mdltn_params = { | ||
| 66 | match lora.create_modulation_params( | ||
| 67 | SpreadingFactor::_10, | ||
| 68 | Bandwidth::_250KHz, | ||
| 69 | CodingRate::_4_8, | ||
| 70 | LORA_FREQUENCY_IN_HZ, | ||
| 71 | ) { | ||
| 72 | Ok(mp) => mp, | ||
| 73 | Err(err) => { | ||
| 74 | info!("Radio error = {}", err); | ||
| 75 | return; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | }; | ||
| 79 | |||
| 80 | let rx_pkt_params = { | ||
| 81 | match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) { | ||
| 82 | Ok(pp) => pp, | ||
| 83 | Err(err) => { | ||
| 84 | info!("Radio error = {}", err); | ||
| 85 | return; | ||
| 86 | } | ||
| 87 | } | ||
| 88 | }; | ||
| 89 | |||
| 90 | match lora | ||
| 91 | .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, true, false, 0, 0x00ffffffu32) | ||
| 92 | .await | ||
| 93 | { | ||
| 94 | Ok(()) => {} | ||
| 95 | Err(err) => { | ||
| 96 | info!("Radio error = {}", err); | ||
| 97 | return; | ||
| 98 | } | ||
| 99 | }; | ||
| 100 | |||
| 101 | loop { | ||
| 102 | receiving_buffer = [00u8; 100]; | ||
| 103 | match lora.rx(&rx_pkt_params, &mut receiving_buffer).await { | ||
| 104 | Ok((received_len, _rx_pkt_status)) => { | ||
| 105 | if (received_len == 3) | ||
| 106 | && (receiving_buffer[0] == 0x01u8) | ||
| 107 | && (receiving_buffer[1] == 0x02u8) | ||
| 108 | && (receiving_buffer[2] == 0x03u8) | ||
| 109 | { | ||
| 110 | info!("rx successful"); | ||
| 111 | debug_indicator.set_high(); | ||
| 112 | Timer::after(Duration::from_secs(5)).await; | ||
| 113 | debug_indicator.set_low(); | ||
| 114 | } else { | ||
| 115 | info!("rx unknown packet"); | ||
| 116 | } | ||
| 117 | } | ||
| 118 | Err(err) => info!("rx unsuccessful = {}", err), | ||
| 119 | } | ||
| 120 | } | ||
| 121 | } | ||
diff --git a/examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs b/examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs new file mode 100644 index 000000000..1fd8f61a2 --- /dev/null +++ b/examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs | |||
| @@ -0,0 +1,131 @@ | |||
| 1 | //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. | ||
| 2 | //! Other nrf/sx126x combinations may work with appropriate pin modifications. | ||
| 3 | //! It demonstrates LoRa Rx duty cycle functionality in conjunction with the lora_p2p_send example. | ||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![macro_use] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||
| 12 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; | ||
| 13 | use embassy_nrf::{bind_interrupts, peripherals, spim}; | ||
| 14 | use embassy_time::{Delay, Duration, Timer}; | ||
| 15 | use lora_phy::mod_params::*; | ||
| 16 | use lora_phy::sx1261_2::SX1261_2; | ||
| 17 | use lora_phy::LoRa; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||
| 21 | |||
| 22 | bind_interrupts!(struct Irqs { | ||
| 23 | SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>; | ||
| 24 | }); | ||
| 25 | |||
| 26 | #[embassy_executor::main] | ||
| 27 | async fn main(_spawner: Spawner) { | ||
| 28 | let p = embassy_nrf::init(Default::default()); | ||
| 29 | let mut spi_config = spim::Config::default(); | ||
| 30 | spi_config.frequency = spim::Frequency::M16; | ||
| 31 | |||
| 32 | let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); | ||
| 33 | |||
| 34 | let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); | ||
| 35 | let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); | ||
| 36 | let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); | ||
| 37 | let busy = Input::new(p.P1_14.degrade(), Pull::Down); | ||
| 38 | let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); | ||
| 39 | let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); | ||
| 40 | |||
| 41 | let iv = | ||
| 42 | GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); | ||
| 43 | |||
| 44 | let mut delay = Delay; | ||
| 45 | |||
| 46 | let mut lora = { | ||
| 47 | match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, &mut delay).await { | ||
| 48 | Ok(l) => l, | ||
| 49 | Err(err) => { | ||
| 50 | info!("Radio error = {}", err); | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | } | ||
| 54 | }; | ||
| 55 | |||
| 56 | let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); | ||
| 57 | let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); | ||
| 58 | |||
| 59 | start_indicator.set_high(); | ||
| 60 | Timer::after(Duration::from_secs(5)).await; | ||
| 61 | start_indicator.set_low(); | ||
| 62 | |||
| 63 | let mut receiving_buffer = [00u8; 100]; | ||
| 64 | |||
| 65 | let mdltn_params = { | ||
| 66 | match lora.create_modulation_params( | ||
| 67 | SpreadingFactor::_10, | ||
| 68 | Bandwidth::_250KHz, | ||
| 69 | CodingRate::_4_8, | ||
| 70 | LORA_FREQUENCY_IN_HZ, | ||
| 71 | ) { | ||
| 72 | Ok(mp) => mp, | ||
| 73 | Err(err) => { | ||
| 74 | info!("Radio error = {}", err); | ||
| 75 | return; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | }; | ||
| 79 | |||
| 80 | let rx_pkt_params = { | ||
| 81 | match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) { | ||
| 82 | Ok(pp) => pp, | ||
| 83 | Err(err) => { | ||
| 84 | info!("Radio error = {}", err); | ||
| 85 | return; | ||
| 86 | } | ||
| 87 | } | ||
| 88 | }; | ||
| 89 | |||
| 90 | // See "RM0453 Reference manual STM32WL5x advanced Arm®-based 32-bit MCUs with sub-GHz radio solution" for the best explanation of Rx duty cycle processing. | ||
| 91 | match lora | ||
| 92 | .prepare_for_rx( | ||
| 93 | &mdltn_params, | ||
| 94 | &rx_pkt_params, | ||
| 95 | Some(&DutyCycleParams { | ||
| 96 | rx_time: 300_000, // 300_000 units * 15.625 us/unit = 4.69 s | ||
| 97 | sleep_time: 200_000, // 200_000 units * 15.625 us/unit = 3.13 s | ||
| 98 | }), | ||
| 99 | false, | ||
| 100 | false, | ||
| 101 | 0, | ||
| 102 | 0, | ||
| 103 | ) | ||
| 104 | .await | ||
| 105 | { | ||
| 106 | Ok(()) => {} | ||
| 107 | Err(err) => { | ||
| 108 | info!("Radio error = {}", err); | ||
| 109 | return; | ||
| 110 | } | ||
| 111 | }; | ||
| 112 | |||
| 113 | receiving_buffer = [00u8; 100]; | ||
| 114 | match lora.rx(&rx_pkt_params, &mut receiving_buffer).await { | ||
| 115 | Ok((received_len, _rx_pkt_status)) => { | ||
| 116 | if (received_len == 3) | ||
| 117 | && (receiving_buffer[0] == 0x01u8) | ||
| 118 | && (receiving_buffer[1] == 0x02u8) | ||
| 119 | && (receiving_buffer[2] == 0x03u8) | ||
| 120 | { | ||
| 121 | info!("rx successful"); | ||
| 122 | debug_indicator.set_high(); | ||
| 123 | Timer::after(Duration::from_secs(5)).await; | ||
| 124 | debug_indicator.set_low(); | ||
| 125 | } else { | ||
| 126 | info!("rx unknown packet") | ||
| 127 | } | ||
| 128 | } | ||
| 129 | Err(err) => info!("rx unsuccessful = {}", err), | ||
| 130 | } | ||
| 131 | } | ||
diff --git a/examples/nrf52840/src/bin/lora_p2p_send.rs b/examples/nrf52840/src/bin/lora_p2p_send.rs new file mode 100644 index 000000000..1c8bbc27a --- /dev/null +++ b/examples/nrf52840/src/bin/lora_p2p_send.rs | |||
| @@ -0,0 +1,104 @@ | |||
| 1 | //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. | ||
| 2 | //! Other nrf/sx126x combinations may work with appropriate pin modifications. | ||
| 3 | //! It demonstrates LORA P2P send functionality. | ||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![macro_use] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||
| 12 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; | ||
| 13 | use embassy_nrf::{bind_interrupts, peripherals, spim}; | ||
| 14 | use embassy_time::Delay; | ||
| 15 | use lora_phy::mod_params::*; | ||
| 16 | use lora_phy::sx1261_2::SX1261_2; | ||
| 17 | use lora_phy::LoRa; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||
| 21 | |||
| 22 | bind_interrupts!(struct Irqs { | ||
| 23 | SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>; | ||
| 24 | }); | ||
| 25 | |||
| 26 | #[embassy_executor::main] | ||
| 27 | async fn main(_spawner: Spawner) { | ||
| 28 | let p = embassy_nrf::init(Default::default()); | ||
| 29 | let mut spi_config = spim::Config::default(); | ||
| 30 | spi_config.frequency = spim::Frequency::M16; | ||
| 31 | |||
| 32 | let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); | ||
| 33 | |||
| 34 | let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); | ||
| 35 | let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); | ||
| 36 | let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); | ||
| 37 | let busy = Input::new(p.P1_14.degrade(), Pull::Down); | ||
| 38 | let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); | ||
| 39 | let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); | ||
| 40 | |||
| 41 | let iv = | ||
| 42 | GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); | ||
| 43 | |||
| 44 | let mut delay = Delay; | ||
| 45 | |||
| 46 | let mut lora = { | ||
| 47 | match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, &mut delay).await { | ||
| 48 | Ok(l) => l, | ||
| 49 | Err(err) => { | ||
| 50 | info!("Radio error = {}", err); | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | } | ||
| 54 | }; | ||
| 55 | |||
| 56 | let mdltn_params = { | ||
| 57 | match lora.create_modulation_params( | ||
| 58 | SpreadingFactor::_10, | ||
| 59 | Bandwidth::_250KHz, | ||
| 60 | CodingRate::_4_8, | ||
| 61 | LORA_FREQUENCY_IN_HZ, | ||
| 62 | ) { | ||
| 63 | Ok(mp) => mp, | ||
| 64 | Err(err) => { | ||
| 65 | info!("Radio error = {}", err); | ||
| 66 | return; | ||
| 67 | } | ||
| 68 | } | ||
| 69 | }; | ||
| 70 | |||
| 71 | let mut tx_pkt_params = { | ||
| 72 | match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) { | ||
| 73 | Ok(pp) => pp, | ||
| 74 | Err(err) => { | ||
| 75 | info!("Radio error = {}", err); | ||
| 76 | return; | ||
| 77 | } | ||
| 78 | } | ||
| 79 | }; | ||
| 80 | |||
| 81 | match lora.prepare_for_tx(&mdltn_params, 20, false).await { | ||
| 82 | Ok(()) => {} | ||
| 83 | Err(err) => { | ||
| 84 | info!("Radio error = {}", err); | ||
| 85 | return; | ||
| 86 | } | ||
| 87 | }; | ||
| 88 | |||
| 89 | let buffer = [0x01u8, 0x02u8, 0x03u8]; | ||
| 90 | match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await { | ||
| 91 | Ok(()) => { | ||
| 92 | info!("TX DONE"); | ||
| 93 | } | ||
| 94 | Err(err) => { | ||
| 95 | info!("Radio error = {}", err); | ||
| 96 | return; | ||
| 97 | } | ||
| 98 | }; | ||
| 99 | |||
| 100 | match lora.sleep(&mut delay).await { | ||
| 101 | Ok(()) => info!("Sleep successful"), | ||
| 102 | Err(err) => info!("Sleep unsuccessful = {}", err), | ||
| 103 | } | ||
| 104 | } | ||
diff --git a/examples/nrf52840/src/bin/manually_create_executor.rs b/examples/nrf52840/src/bin/manually_create_executor.rs new file mode 100644 index 000000000..12ce660f9 --- /dev/null +++ b/examples/nrf52840/src/bin/manually_create_executor.rs | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | // This example showcases how to manually create an executor. | ||
| 2 | // This is what the #[embassy::main] macro does behind the scenes. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | |||
| 8 | use cortex_m_rt::entry; | ||
| 9 | use defmt::{info, unwrap}; | ||
| 10 | use embassy_executor::Executor; | ||
| 11 | use embassy_time::{Duration, Timer}; | ||
| 12 | use static_cell::StaticCell; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | #[embassy_executor::task] | ||
| 16 | async fn run1() { | ||
| 17 | loop { | ||
| 18 | info!("BIG INFREQUENT TICK"); | ||
| 19 | Timer::after(Duration::from_ticks(64000)).await; | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | #[embassy_executor::task] | ||
| 24 | async fn run2() { | ||
| 25 | loop { | ||
| 26 | info!("tick"); | ||
| 27 | Timer::after(Duration::from_ticks(13000)).await; | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | static EXECUTOR: StaticCell<Executor> = StaticCell::new(); | ||
| 32 | |||
| 33 | #[entry] | ||
| 34 | fn main() -> ! { | ||
| 35 | info!("Hello World!"); | ||
| 36 | |||
| 37 | let _p = embassy_nrf::init(Default::default()); | ||
| 38 | |||
| 39 | // Create the executor and put it in a StaticCell, because `run` needs `&'static mut Executor`. | ||
| 40 | let executor = EXECUTOR.init(Executor::new()); | ||
| 41 | |||
| 42 | // Run it. | ||
| 43 | // `run` calls the closure then runs the executor forever. It never returns. | ||
| 44 | executor.run(|spawner| { | ||
| 45 | // Here we get access to a spawner to spawn the initial tasks. | ||
| 46 | unwrap!(spawner.spawn(run1())); | ||
| 47 | unwrap!(spawner.spawn(run2())); | ||
| 48 | }); | ||
| 49 | } | ||
diff --git a/examples/nrf/src/bin/multiprio.rs b/examples/nrf52840/src/bin/multiprio.rs index 25806ae48..aab819117 100644 --- a/examples/nrf/src/bin/multiprio.rs +++ b/examples/nrf52840/src/bin/multiprio.rs | |||
| @@ -59,9 +59,9 @@ | |||
| 59 | 59 | ||
| 60 | use cortex_m_rt::entry; | 60 | use cortex_m_rt::entry; |
| 61 | use defmt::{info, unwrap}; | 61 | use defmt::{info, unwrap}; |
| 62 | use embassy_nrf::executor::{Executor, InterruptExecutor}; | 62 | use embassy_executor::{Executor, InterruptExecutor}; |
| 63 | use embassy_nrf::interrupt; | 63 | use embassy_nrf::interrupt; |
| 64 | use embassy_nrf::interrupt::InterruptExt; | 64 | use embassy_nrf::interrupt::{InterruptExt, Priority}; |
| 65 | use embassy_time::{Duration, Instant, Timer}; | 65 | use embassy_time::{Duration, Instant, Timer}; |
| 66 | use static_cell::StaticCell; | 66 | use static_cell::StaticCell; |
| 67 | use {defmt_rtt as _, panic_probe as _}; | 67 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -108,10 +108,20 @@ async fn run_low() { | |||
| 108 | } | 108 | } |
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | static EXECUTOR_HIGH: StaticCell<InterruptExecutor<interrupt::SWI1_EGU1>> = StaticCell::new(); | 111 | static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); |
| 112 | static EXECUTOR_MED: StaticCell<InterruptExecutor<interrupt::SWI0_EGU0>> = StaticCell::new(); | 112 | static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); |
| 113 | static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new(); | 113 | static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new(); |
| 114 | 114 | ||
| 115 | #[interrupt] | ||
| 116 | unsafe fn SWI1_EGU1() { | ||
| 117 | EXECUTOR_HIGH.on_interrupt() | ||
| 118 | } | ||
| 119 | |||
| 120 | #[interrupt] | ||
| 121 | unsafe fn SWI0_EGU0() { | ||
| 122 | EXECUTOR_MED.on_interrupt() | ||
| 123 | } | ||
| 124 | |||
| 115 | #[entry] | 125 | #[entry] |
| 116 | fn main() -> ! { | 126 | fn main() -> ! { |
| 117 | info!("Hello World!"); | 127 | info!("Hello World!"); |
| @@ -119,17 +129,13 @@ fn main() -> ! { | |||
| 119 | let _p = embassy_nrf::init(Default::default()); | 129 | let _p = embassy_nrf::init(Default::default()); |
| 120 | 130 | ||
| 121 | // High-priority executor: SWI1_EGU1, priority level 6 | 131 | // High-priority executor: SWI1_EGU1, priority level 6 |
| 122 | let irq = interrupt::take!(SWI1_EGU1); | 132 | interrupt::SWI1_EGU1.set_priority(Priority::P6); |
| 123 | irq.set_priority(interrupt::Priority::P6); | 133 | let spawner = EXECUTOR_HIGH.start(interrupt::SWI1_EGU1); |
| 124 | let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq)); | ||
| 125 | let spawner = executor.start(); | ||
| 126 | unwrap!(spawner.spawn(run_high())); | 134 | unwrap!(spawner.spawn(run_high())); |
| 127 | 135 | ||
| 128 | // Medium-priority executor: SWI0_EGU0, priority level 7 | 136 | // Medium-priority executor: SWI0_EGU0, priority level 7 |
| 129 | let irq = interrupt::take!(SWI0_EGU0); | 137 | interrupt::SWI0_EGU0.set_priority(Priority::P7); |
| 130 | irq.set_priority(interrupt::Priority::P7); | 138 | let spawner = EXECUTOR_MED.start(interrupt::SWI0_EGU0); |
| 131 | let executor = EXECUTOR_MED.init(InterruptExecutor::new(irq)); | ||
| 132 | let spawner = executor.start(); | ||
| 133 | unwrap!(spawner.spawn(run_med())); | 139 | unwrap!(spawner.spawn(run_med())); |
| 134 | 140 | ||
| 135 | // Low priority executor: runs in thread mode, using WFE/SEV | 141 | // Low priority executor: runs in thread mode, using WFE/SEV |
diff --git a/examples/nrf/src/bin/mutex.rs b/examples/nrf52840/src/bin/mutex.rs index c402c6ba1..c402c6ba1 100644 --- a/examples/nrf/src/bin/mutex.rs +++ b/examples/nrf52840/src/bin/mutex.rs | |||
diff --git a/examples/nrf/src/bin/nvmc.rs b/examples/nrf52840/src/bin/nvmc.rs index 75d090fbb..31c6fe4b6 100644 --- a/examples/nrf/src/bin/nvmc.rs +++ b/examples/nrf52840/src/bin/nvmc.rs | |||
| @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { | |||
| 14 | let p = embassy_nrf::init(Default::default()); | 14 | let p = embassy_nrf::init(Default::default()); |
| 15 | info!("Hello NVMC!"); | 15 | info!("Hello NVMC!"); |
| 16 | 16 | ||
| 17 | // probe-run breaks without this, I'm not sure why. | 17 | // probe-rs run breaks without this, I'm not sure why. |
| 18 | Timer::after(Duration::from_secs(1)).await; | 18 | Timer::after(Duration::from_secs(1)).await; |
| 19 | 19 | ||
| 20 | let mut f = Nvmc::new(p.NVMC); | 20 | let mut f = Nvmc::new(p.NVMC); |
diff --git a/examples/nrf52840/src/bin/pdm.rs b/examples/nrf52840/src/bin/pdm.rs new file mode 100644 index 000000000..47fe67733 --- /dev/null +++ b/examples/nrf52840/src/bin/pdm.rs | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::info; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::pdm::{self, Config, Pdm}; | ||
| 8 | use embassy_nrf::{bind_interrupts, peripherals}; | ||
| 9 | use embassy_time::{Duration, Timer}; | ||
| 10 | use fixed::types::I7F1; | ||
| 11 | use num_integer::Roots; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | bind_interrupts!(struct Irqs { | ||
| 15 | PDM => pdm::InterruptHandler<peripherals::PDM>; | ||
| 16 | }); | ||
| 17 | |||
| 18 | #[embassy_executor::main] | ||
| 19 | async fn main(_p: Spawner) { | ||
| 20 | let p = embassy_nrf::init(Default::default()); | ||
| 21 | let config = Config::default(); | ||
| 22 | let mut pdm = Pdm::new(p.PDM, Irqs, p.P0_01, p.P0_00, config); | ||
| 23 | |||
| 24 | loop { | ||
| 25 | for gain in [I7F1::from_num(-20), I7F1::from_num(0), I7F1::from_num(20)] { | ||
| 26 | pdm.set_gain(gain, gain); | ||
| 27 | info!("Gain = {} dB", defmt::Debug2Format(&gain)); | ||
| 28 | pdm.start().await; | ||
| 29 | |||
| 30 | // wait some time till the microphon settled | ||
| 31 | Timer::after(Duration::from_millis(1000)).await; | ||
| 32 | |||
| 33 | const SAMPLES: usize = 2048; | ||
| 34 | let mut buf = [0i16; SAMPLES]; | ||
| 35 | pdm.sample(&mut buf).await.unwrap(); | ||
| 36 | |||
| 37 | let mean = (buf.iter().map(|v| i32::from(*v)).sum::<i32>() / buf.len() as i32) as i16; | ||
| 38 | info!( | ||
| 39 | "{} samples, min {=i16}, max {=i16}, mean {=i16}, AC RMS {=i16}", | ||
| 40 | buf.len(), | ||
| 41 | buf.iter().min().unwrap(), | ||
| 42 | buf.iter().max().unwrap(), | ||
| 43 | mean, | ||
| 44 | ( | ||
| 45 | buf.iter().map(|v| i32::from(*v - mean).pow(2)).fold(0i32, |a,b| a.saturating_add(b)) | ||
| 46 | / buf.len() as i32).sqrt() as i16, | ||
| 47 | ); | ||
| 48 | |||
| 49 | info!("samples: {:?}", &buf); | ||
| 50 | |||
| 51 | pdm.stop().await; | ||
| 52 | Timer::after(Duration::from_millis(100)).await; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
diff --git a/examples/nrf/src/bin/pdm_continuous.rs b/examples/nrf52840/src/bin/pdm_continuous.rs index 284a68af2..9eaf30717 100644 --- a/examples/nrf/src/bin/pdm_continuous.rs +++ b/examples/nrf52840/src/bin/pdm_continuous.rs | |||
| @@ -5,8 +5,8 @@ | |||
| 5 | use defmt::info; | 5 | use defmt::info; |
| 6 | use core::cmp::Ordering; | 6 | use core::cmp::Ordering; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 8 | use embassy_nrf::interrupt; | 8 | use embassy_nrf::{bind_interrupts, peripherals}; |
| 9 | use embassy_nrf::pdm::{Config, Channels, Pdm, SamplerState, Frequency, Ratio}; | 9 | use embassy_nrf::pdm::{self, Config, OperationMode, Pdm, SamplerState, Frequency, Ratio}; |
| 10 | use fixed::types::I7F1; | 10 | use fixed::types::I7F1; |
| 11 | use num_integer::Roots; | 11 | use num_integer::Roots; |
| 12 | use microfft::real::rfft_1024; | 12 | use microfft::real::rfft_1024; |
| @@ -14,6 +14,10 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 14 | 14 | ||
| 15 | // Demonstrates both continuous sampling and scanning multiple channels driven by a PPI linked timer | 15 | // Demonstrates both continuous sampling and scanning multiple channels driven by a PPI linked timer |
| 16 | 16 | ||
| 17 | bind_interrupts!(struct Irqs { | ||
| 18 | PDM => pdm::InterruptHandler<peripherals::PDM>; | ||
| 19 | }); | ||
| 20 | |||
| 17 | #[embassy_executor::main] | 21 | #[embassy_executor::main] |
| 18 | async fn main(_p: Spawner) { | 22 | async fn main(_p: Spawner) { |
| 19 | let mut p = embassy_nrf::init(Default::default()); | 23 | let mut p = embassy_nrf::init(Default::default()); |
| @@ -21,9 +25,9 @@ async fn main(_p: Spawner) { | |||
| 21 | // 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. |
| 22 | config.frequency = Frequency::_1280K; // 16 kHz sample rate | 26 | config.frequency = Frequency::_1280K; // 16 kHz sample rate |
| 23 | config.ratio = Ratio::RATIO80; | 27 | config.ratio = Ratio::RATIO80; |
| 24 | config.channels = Channels::Mono; | 28 | config.operation_mode = OperationMode::Mono; |
| 25 | config.gain_left = I7F1::from_bits(5); // 2.5 dB | 29 | config.gain_left = I7F1::from_bits(5); // 2.5 dB |
| 26 | let mut pdm = Pdm::new(p.PDM, interrupt::take!(PDM), &mut p.P0_00, &mut p.P0_01, config); | 30 | let mut pdm = Pdm::new(p.PDM, Irqs, &mut p.P0_00, &mut p.P0_01, config); |
| 27 | 31 | ||
| 28 | let mut bufs = [[0; 1024]; 2]; | 32 | let mut bufs = [[0; 1024]; 2]; |
| 29 | 33 | ||
| @@ -54,7 +58,7 @@ async fn main(_p: Spawner) { | |||
| 54 | SamplerState::Sampled | 58 | SamplerState::Sampled |
| 55 | }, | 59 | }, |
| 56 | ) | 60 | ) |
| 57 | .await; | 61 | .await.unwrap(); |
| 58 | } | 62 | } |
| 59 | 63 | ||
| 60 | fn fft_peak_freq(input: &[i16; 1024]) -> (usize, u32) { | 64 | fn fft_peak_freq(input: &[i16; 1024]) -> (usize, u32) { |
diff --git a/examples/nrf/src/bin/ppi.rs b/examples/nrf52840/src/bin/ppi.rs index d74ce4064..d74ce4064 100644 --- a/examples/nrf/src/bin/ppi.rs +++ b/examples/nrf52840/src/bin/ppi.rs | |||
diff --git a/examples/nrf/src/bin/pubsub.rs b/examples/nrf52840/src/bin/pubsub.rs index 688e6d075..cca60ebc9 100644 --- a/examples/nrf/src/bin/pubsub.rs +++ b/examples/nrf52840/src/bin/pubsub.rs | |||
| @@ -74,9 +74,9 @@ async fn fast_logger(mut messages: Subscriber<'static, ThreadModeRawMutex, Messa | |||
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | /// A logger task that awaits the messages, but also does some other work. | 76 | /// A logger task that awaits the messages, but also does some other work. |
| 77 | /// Because of this, depeding on how the messages were published, the subscriber might miss some messages | 77 | /// Because of this, depending on how the messages were published, the subscriber might miss some messages. |
| 78 | /// | 78 | /// |
| 79 | /// This takes the dynamic `DynSubscriber`. This is not as performant as the generic version, but let's you ignore some of the generics | 79 | /// This takes the dynamic `DynSubscriber`. This is not as performant as the generic version, but let's you ignore some of the generics. |
| 80 | #[embassy_executor::task] | 80 | #[embassy_executor::task] |
| 81 | async fn slow_logger(mut messages: DynSubscriber<'static, Message>) { | 81 | async fn slow_logger(mut messages: DynSubscriber<'static, Message>) { |
| 82 | loop { | 82 | loop { |
diff --git a/examples/nrf/src/bin/pwm.rs b/examples/nrf52840/src/bin/pwm.rs index 1698c0bc8..1698c0bc8 100644 --- a/examples/nrf/src/bin/pwm.rs +++ b/examples/nrf52840/src/bin/pwm.rs | |||
diff --git a/examples/nrf/src/bin/pwm_double_sequence.rs b/examples/nrf52840/src/bin/pwm_double_sequence.rs index 16e50e909..16e50e909 100644 --- a/examples/nrf/src/bin/pwm_double_sequence.rs +++ b/examples/nrf52840/src/bin/pwm_double_sequence.rs | |||
diff --git a/examples/nrf/src/bin/pwm_sequence.rs b/examples/nrf52840/src/bin/pwm_sequence.rs index b9aca9aaa..b9aca9aaa 100644 --- a/examples/nrf/src/bin/pwm_sequence.rs +++ b/examples/nrf52840/src/bin/pwm_sequence.rs | |||
diff --git a/examples/nrf/src/bin/pwm_sequence_ppi.rs b/examples/nrf52840/src/bin/pwm_sequence_ppi.rs index 6594fa348..6594fa348 100644 --- a/examples/nrf/src/bin/pwm_sequence_ppi.rs +++ b/examples/nrf52840/src/bin/pwm_sequence_ppi.rs | |||
diff --git a/examples/nrf/src/bin/pwm_sequence_ws2812b.rs b/examples/nrf52840/src/bin/pwm_sequence_ws2812b.rs index 711c8a17b..711c8a17b 100644 --- a/examples/nrf/src/bin/pwm_sequence_ws2812b.rs +++ b/examples/nrf52840/src/bin/pwm_sequence_ws2812b.rs | |||
diff --git a/examples/nrf/src/bin/pwm_servo.rs b/examples/nrf52840/src/bin/pwm_servo.rs index 19228f433..19228f433 100644 --- a/examples/nrf/src/bin/pwm_servo.rs +++ b/examples/nrf52840/src/bin/pwm_servo.rs | |||
diff --git a/examples/nrf/src/bin/qdec.rs b/examples/nrf52840/src/bin/qdec.rs index 600bba07a..59783d312 100644 --- a/examples/nrf/src/bin/qdec.rs +++ b/examples/nrf52840/src/bin/qdec.rs | |||
| @@ -4,16 +4,19 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::info; | 5 | use defmt::info; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_nrf::interrupt; | ||
| 8 | use embassy_nrf::qdec::{self, Qdec}; | 7 | use embassy_nrf::qdec::{self, Qdec}; |
| 8 | use embassy_nrf::{bind_interrupts, peripherals}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 10 | ||
| 11 | bind_interrupts!(struct Irqs { | ||
| 12 | QDEC => qdec::InterruptHandler<peripherals::QDEC>; | ||
| 13 | }); | ||
| 14 | |||
| 11 | #[embassy_executor::main] | 15 | #[embassy_executor::main] |
| 12 | async fn main(_spawner: Spawner) { | 16 | async fn main(_spawner: Spawner) { |
| 13 | let p = embassy_nrf::init(Default::default()); | 17 | let p = embassy_nrf::init(Default::default()); |
| 14 | let irq = interrupt::take!(QDEC); | ||
| 15 | let config = qdec::Config::default(); | 18 | let config = qdec::Config::default(); |
| 16 | let mut rotary_enc = Qdec::new(p.QDEC, irq, p.P0_31, p.P0_30, config); | 19 | let mut rotary_enc = Qdec::new(p.QDEC, Irqs, p.P0_31, p.P0_30, config); |
| 17 | 20 | ||
| 18 | info!("Turn rotary encoder!"); | 21 | info!("Turn rotary encoder!"); |
| 19 | let mut value = 0; | 22 | let mut value = 0; |
diff --git a/examples/nrf/src/bin/qspi.rs b/examples/nrf52840/src/bin/qspi.rs index bdcf710b8..9e8a01f4e 100644 --- a/examples/nrf/src/bin/qspi.rs +++ b/examples/nrf52840/src/bin/qspi.rs | |||
| @@ -4,7 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::{assert_eq, info, unwrap}; | 5 | use defmt::{assert_eq, info, unwrap}; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_nrf::{interrupt, qspi}; | 7 | use embassy_nrf::qspi::Frequency; |
| 8 | use embassy_nrf::{bind_interrupts, peripherals, qspi}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 10 | const PAGE_SIZE: usize = 4096; | 11 | const PAGE_SIZE: usize = 4096; |
| @@ -14,18 +15,23 @@ const PAGE_SIZE: usize = 4096; | |||
| 14 | #[repr(C, align(4))] | 15 | #[repr(C, align(4))] |
| 15 | struct AlignedBuf([u8; 4096]); | 16 | struct AlignedBuf([u8; 4096]); |
| 16 | 17 | ||
| 18 | bind_interrupts!(struct Irqs { | ||
| 19 | QSPI => qspi::InterruptHandler<peripherals::QSPI>; | ||
| 20 | }); | ||
| 21 | |||
| 17 | #[embassy_executor::main] | 22 | #[embassy_executor::main] |
| 18 | async fn main(_spawner: Spawner) { | 23 | async fn main(_spawner: Spawner) { |
| 19 | let p = embassy_nrf::init(Default::default()); | 24 | let p = embassy_nrf::init(Default::default()); |
| 20 | // Config for the MX25R64 present in the nRF52840 DK | 25 | // Config for the MX25R64 present in the nRF52840 DK |
| 21 | let mut config = qspi::Config::default(); | 26 | let mut config = qspi::Config::default(); |
| 27 | config.capacity = 8 * 1024 * 1024; // 8 MB | ||
| 28 | config.frequency = Frequency::M32; | ||
| 22 | config.read_opcode = qspi::ReadOpcode::READ4IO; | 29 | config.read_opcode = qspi::ReadOpcode::READ4IO; |
| 23 | config.write_opcode = qspi::WriteOpcode::PP4IO; | 30 | config.write_opcode = qspi::WriteOpcode::PP4IO; |
| 24 | config.write_page_size = qspi::WritePageSize::_256BYTES; | 31 | config.write_page_size = qspi::WritePageSize::_256BYTES; |
| 25 | 32 | ||
| 26 | let irq = interrupt::take!(QSPI); | 33 | let mut q = qspi::Qspi::new( |
| 27 | let mut q: qspi::Qspi<_, 67108864> = qspi::Qspi::new( | 34 | p.QSPI, Irqs, p.P0_19, p.P0_17, p.P0_20, p.P0_21, p.P0_22, p.P0_23, config, |
| 28 | p.QSPI, irq, p.P0_19, p.P0_17, p.P0_20, p.P0_21, p.P0_22, p.P0_23, config, | ||
| 29 | ); | 35 | ); |
| 30 | 36 | ||
| 31 | let mut id = [1; 3]; | 37 | let mut id = [1; 3]; |
| @@ -52,23 +58,23 @@ async fn main(_spawner: Spawner) { | |||
| 52 | 58 | ||
| 53 | for i in 0..8 { | 59 | for i in 0..8 { |
| 54 | info!("page {:?}: erasing... ", i); | 60 | info!("page {:?}: erasing... ", i); |
| 55 | unwrap!(q.erase(i * PAGE_SIZE).await); | 61 | unwrap!(q.erase(i * PAGE_SIZE as u32).await); |
| 56 | 62 | ||
| 57 | for j in 0..PAGE_SIZE { | 63 | for j in 0..PAGE_SIZE { |
| 58 | buf.0[j] = pattern((j + i * PAGE_SIZE) as u32); | 64 | buf.0[j] = pattern((j as u32 + i * PAGE_SIZE as u32) as u32); |
| 59 | } | 65 | } |
| 60 | 66 | ||
| 61 | info!("programming..."); | 67 | info!("programming..."); |
| 62 | unwrap!(q.write(i * PAGE_SIZE, &buf.0).await); | 68 | unwrap!(q.write(i * PAGE_SIZE as u32, &buf.0).await); |
| 63 | } | 69 | } |
| 64 | 70 | ||
| 65 | for i in 0..8 { | 71 | for i in 0..8 { |
| 66 | info!("page {:?}: reading... ", i); | 72 | info!("page {:?}: reading... ", i); |
| 67 | unwrap!(q.read(i * PAGE_SIZE, &mut buf.0).await); | 73 | unwrap!(q.read(i * PAGE_SIZE as u32, &mut buf.0).await); |
| 68 | 74 | ||
| 69 | info!("verifying..."); | 75 | info!("verifying..."); |
| 70 | for j in 0..PAGE_SIZE { | 76 | for j in 0..PAGE_SIZE { |
| 71 | assert_eq!(buf.0[j], pattern((j + i * PAGE_SIZE) as u32)); | 77 | assert_eq!(buf.0[j], pattern((j as u32 + i * PAGE_SIZE as u32) as u32)); |
| 72 | } | 78 | } |
| 73 | } | 79 | } |
| 74 | 80 | ||
diff --git a/examples/nrf/src/bin/qspi_lowpower.rs b/examples/nrf52840/src/bin/qspi_lowpower.rs index 9341a2376..22a5c0c6d 100644 --- a/examples/nrf/src/bin/qspi_lowpower.rs +++ b/examples/nrf52840/src/bin/qspi_lowpower.rs | |||
| @@ -6,7 +6,8 @@ use core::mem; | |||
| 6 | 6 | ||
| 7 | use defmt::{info, unwrap}; | 7 | use defmt::{info, unwrap}; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_nrf::{interrupt, qspi}; | 9 | use embassy_nrf::qspi::Frequency; |
| 10 | use embassy_nrf::{bind_interrupts, peripherals, qspi}; | ||
| 10 | use embassy_time::{Duration, Timer}; | 11 | use embassy_time::{Duration, Timer}; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 13 | ||
| @@ -15,14 +16,19 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 15 | #[repr(C, align(4))] | 16 | #[repr(C, align(4))] |
| 16 | struct AlignedBuf([u8; 64]); | 17 | struct AlignedBuf([u8; 64]); |
| 17 | 18 | ||
| 19 | bind_interrupts!(struct Irqs { | ||
| 20 | QSPI => qspi::InterruptHandler<peripherals::QSPI>; | ||
| 21 | }); | ||
| 22 | |||
| 18 | #[embassy_executor::main] | 23 | #[embassy_executor::main] |
| 19 | async fn main(_p: Spawner) { | 24 | async fn main(_p: Spawner) { |
| 20 | let mut p = embassy_nrf::init(Default::default()); | 25 | let mut p = embassy_nrf::init(Default::default()); |
| 21 | let mut irq = interrupt::take!(QSPI); | ||
| 22 | 26 | ||
| 23 | loop { | 27 | loop { |
| 24 | // Config for the MX25R64 present in the nRF52840 DK | 28 | // Config for the MX25R64 present in the nRF52840 DK |
| 25 | let mut config = qspi::Config::default(); | 29 | let mut config = qspi::Config::default(); |
| 30 | config.capacity = 8 * 1024 * 1024; // 8 MB | ||
| 31 | config.frequency = Frequency::M32; | ||
| 26 | config.read_opcode = qspi::ReadOpcode::READ4IO; | 32 | config.read_opcode = qspi::ReadOpcode::READ4IO; |
| 27 | config.write_opcode = qspi::WriteOpcode::PP4IO; | 33 | config.write_opcode = qspi::WriteOpcode::PP4IO; |
| 28 | config.write_page_size = qspi::WritePageSize::_256BYTES; | 34 | config.write_page_size = qspi::WritePageSize::_256BYTES; |
| @@ -31,9 +37,9 @@ async fn main(_p: Spawner) { | |||
| 31 | exit_time: 3, // tRDP = 35uS | 37 | exit_time: 3, // tRDP = 35uS |
| 32 | }); | 38 | }); |
| 33 | 39 | ||
| 34 | let mut q: qspi::Qspi<_, 67108864> = qspi::Qspi::new( | 40 | let mut q = qspi::Qspi::new( |
| 35 | &mut p.QSPI, | 41 | &mut p.QSPI, |
| 36 | &mut irq, | 42 | Irqs, |
| 37 | &mut p.P0_19, | 43 | &mut p.P0_19, |
| 38 | &mut p.P0_17, | 44 | &mut p.P0_17, |
| 39 | &mut p.P0_20, | 45 | &mut p.P0_20, |
diff --git a/examples/nrf/src/bin/raw_spawn.rs b/examples/nrf52840/src/bin/raw_spawn.rs index 1b067f5e4..1b067f5e4 100644 --- a/examples/nrf/src/bin/raw_spawn.rs +++ b/examples/nrf52840/src/bin/raw_spawn.rs | |||
diff --git a/examples/nrf/src/bin/rng.rs b/examples/nrf52840/src/bin/rng.rs index 647073949..855743f50 100644 --- a/examples/nrf/src/bin/rng.rs +++ b/examples/nrf52840/src/bin/rng.rs | |||
| @@ -3,15 +3,19 @@ | |||
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | 4 | ||
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_nrf::interrupt; | ||
| 7 | use embassy_nrf::rng::Rng; | 6 | use embassy_nrf::rng::Rng; |
| 7 | use embassy_nrf::{bind_interrupts, peripherals, rng}; | ||
| 8 | use rand::Rng as _; | 8 | use rand::Rng as _; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 10 | ||
| 11 | bind_interrupts!(struct Irqs { | ||
| 12 | RNG => rng::InterruptHandler<peripherals::RNG>; | ||
| 13 | }); | ||
| 14 | |||
| 11 | #[embassy_executor::main] | 15 | #[embassy_executor::main] |
| 12 | async fn main(_spawner: Spawner) { | 16 | async fn main(_spawner: Spawner) { |
| 13 | let p = embassy_nrf::init(Default::default()); | 17 | let p = embassy_nrf::init(Default::default()); |
| 14 | let mut rng = Rng::new(p.RNG, interrupt::take!(RNG)); | 18 | let mut rng = Rng::new(p.RNG, Irqs); |
| 15 | 19 | ||
| 16 | // Async API | 20 | // Async API |
| 17 | let mut bytes = [0; 4]; | 21 | let mut bytes = [0; 4]; |
diff --git a/examples/nrf/src/bin/saadc.rs b/examples/nrf52840/src/bin/saadc.rs index 7cf588090..ffd9a7f4b 100644 --- a/examples/nrf/src/bin/saadc.rs +++ b/examples/nrf52840/src/bin/saadc.rs | |||
| @@ -4,17 +4,21 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::info; | 5 | use defmt::info; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_nrf::interrupt; | ||
| 8 | use embassy_nrf::saadc::{ChannelConfig, Config, Saadc}; | 7 | use embassy_nrf::saadc::{ChannelConfig, Config, Saadc}; |
| 8 | use embassy_nrf::{bind_interrupts, saadc}; | ||
| 9 | use embassy_time::{Duration, Timer}; | 9 | use embassy_time::{Duration, Timer}; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 11 | ||
| 12 | bind_interrupts!(struct Irqs { | ||
| 13 | SAADC => saadc::InterruptHandler; | ||
| 14 | }); | ||
| 15 | |||
| 12 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 13 | async fn main(_p: Spawner) { | 17 | async fn main(_p: Spawner) { |
| 14 | let mut p = embassy_nrf::init(Default::default()); | 18 | let mut p = embassy_nrf::init(Default::default()); |
| 15 | let config = Config::default(); | 19 | let config = Config::default(); |
| 16 | let channel_config = ChannelConfig::single_ended(&mut p.P0_02); | 20 | let channel_config = ChannelConfig::single_ended(&mut p.P0_02); |
| 17 | let mut saadc = Saadc::new(p.SAADC, interrupt::take!(SAADC), config, [channel_config]); | 21 | let mut saadc = Saadc::new(p.SAADC, Irqs, config, [channel_config]); |
| 18 | 22 | ||
| 19 | loop { | 23 | loop { |
| 20 | let mut buf = [0; 1]; | 24 | let mut buf = [0; 1]; |
diff --git a/examples/nrf/src/bin/saadc_continuous.rs b/examples/nrf52840/src/bin/saadc_continuous.rs index bb50ac65e..a25e17465 100644 --- a/examples/nrf/src/bin/saadc_continuous.rs +++ b/examples/nrf52840/src/bin/saadc_continuous.rs | |||
| @@ -4,14 +4,18 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::info; | 5 | use defmt::info; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_nrf::interrupt; | 7 | use embassy_nrf::saadc::{CallbackResult, ChannelConfig, Config, Saadc}; |
| 8 | use embassy_nrf::saadc::{ChannelConfig, Config, Saadc, SamplerState}; | ||
| 9 | use embassy_nrf::timer::Frequency; | 8 | use embassy_nrf::timer::Frequency; |
| 9 | use embassy_nrf::{bind_interrupts, saadc}; | ||
| 10 | use embassy_time::Duration; | 10 | use embassy_time::Duration; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 12 | ||
| 13 | // Demonstrates both continuous sampling and scanning multiple channels driven by a PPI linked timer | 13 | // Demonstrates both continuous sampling and scanning multiple channels driven by a PPI linked timer |
| 14 | 14 | ||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | SAADC => saadc::InterruptHandler; | ||
| 17 | }); | ||
| 18 | |||
| 15 | #[embassy_executor::main] | 19 | #[embassy_executor::main] |
| 16 | async fn main(_p: Spawner) { | 20 | async fn main(_p: Spawner) { |
| 17 | let mut p = embassy_nrf::init(Default::default()); | 21 | let mut p = embassy_nrf::init(Default::default()); |
| @@ -21,7 +25,7 @@ async fn main(_p: Spawner) { | |||
| 21 | let channel_3_config = ChannelConfig::single_ended(&mut p.P0_04); | 25 | let channel_3_config = ChannelConfig::single_ended(&mut p.P0_04); |
| 22 | let mut saadc = Saadc::new( | 26 | let mut saadc = Saadc::new( |
| 23 | p.SAADC, | 27 | p.SAADC, |
| 24 | interrupt::take!(SAADC), | 28 | Irqs, |
| 25 | config, | 29 | config, |
| 26 | [channel_1_config, channel_2_config, channel_3_config], | 30 | [channel_1_config, channel_2_config, channel_3_config], |
| 27 | ); | 31 | ); |
| @@ -61,7 +65,7 @@ async fn main(_p: Spawner) { | |||
| 61 | c = 0; | 65 | c = 0; |
| 62 | a = 0; | 66 | a = 0; |
| 63 | } | 67 | } |
| 64 | SamplerState::Sampled | 68 | CallbackResult::Continue |
| 65 | }, | 69 | }, |
| 66 | ) | 70 | ) |
| 67 | .await; | 71 | .await; |
diff --git a/examples/nrf/src/bin/self_spawn.rs b/examples/nrf52840/src/bin/self_spawn.rs index 196255a52..31ea6c81e 100644 --- a/examples/nrf/src/bin/self_spawn.rs +++ b/examples/nrf52840/src/bin/self_spawn.rs | |||
| @@ -7,7 +7,11 @@ use embassy_executor::Spawner; | |||
| 7 | use embassy_time::{Duration, Timer}; | 7 | use embassy_time::{Duration, Timer}; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| 10 | #[embassy_executor::task(pool_size = 2)] | 10 | mod config { |
| 11 | pub const MY_TASK_POOL_SIZE: usize = 2; | ||
| 12 | } | ||
| 13 | |||
| 14 | #[embassy_executor::task(pool_size = config::MY_TASK_POOL_SIZE)] | ||
| 11 | async fn my_task(spawner: Spawner, n: u32) { | 15 | async fn my_task(spawner: Spawner, n: u32) { |
| 12 | Timer::after(Duration::from_secs(1)).await; | 16 | Timer::after(Duration::from_secs(1)).await; |
| 13 | info!("Spawning self! {}", n); | 17 | info!("Spawning self! {}", n); |
diff --git a/examples/nrf/src/bin/self_spawn_current_executor.rs b/examples/nrf52840/src/bin/self_spawn_current_executor.rs index 8a179886c..8a179886c 100644 --- a/examples/nrf/src/bin/self_spawn_current_executor.rs +++ b/examples/nrf52840/src/bin/self_spawn_current_executor.rs | |||
diff --git a/examples/nrf/src/bin/spim.rs b/examples/nrf52840/src/bin/spim.rs index 132e01660..9d1843a8f 100644 --- a/examples/nrf/src/bin/spim.rs +++ b/examples/nrf52840/src/bin/spim.rs | |||
| @@ -5,9 +5,13 @@ | |||
| 5 | use defmt::{info, unwrap}; | 5 | use defmt::{info, unwrap}; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_nrf::gpio::{Level, Output, OutputDrive}; | 7 | use embassy_nrf::gpio::{Level, Output, OutputDrive}; |
| 8 | use embassy_nrf::{interrupt, spim}; | 8 | use embassy_nrf::{bind_interrupts, peripherals, spim}; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 10 | ||
| 11 | bind_interrupts!(struct Irqs { | ||
| 12 | SPIM3 => spim::InterruptHandler<peripherals::SPI3>; | ||
| 13 | }); | ||
| 14 | |||
| 11 | #[embassy_executor::main] | 15 | #[embassy_executor::main] |
| 12 | async fn main(_spawner: Spawner) { | 16 | async fn main(_spawner: Spawner) { |
| 13 | let p = embassy_nrf::init(Default::default()); | 17 | let p = embassy_nrf::init(Default::default()); |
| @@ -16,8 +20,7 @@ async fn main(_spawner: Spawner) { | |||
| 16 | let mut config = spim::Config::default(); | 20 | let mut config = spim::Config::default(); |
| 17 | config.frequency = spim::Frequency::M16; | 21 | config.frequency = spim::Frequency::M16; |
| 18 | 22 | ||
| 19 | let irq = interrupt::take!(SPIM3); | 23 | let mut spim = spim::Spim::new(p.SPI3, Irqs, p.P0_29, p.P0_28, p.P0_30, config); |
| 20 | let mut spim = spim::Spim::new(p.SPI3, irq, p.P0_29, p.P0_28, p.P0_30, config); | ||
| 21 | 24 | ||
| 22 | let mut ncs = Output::new(p.P0_31, Level::High, OutputDrive::Standard); | 25 | let mut ncs = Output::new(p.P0_31, Level::High, OutputDrive::Standard); |
| 23 | 26 | ||
diff --git a/examples/nrf52840/src/bin/spis.rs b/examples/nrf52840/src/bin/spis.rs new file mode 100644 index 000000000..77b6e8b64 --- /dev/null +++ b/examples/nrf52840/src/bin/spis.rs | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::info; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::spis::{Config, Spis}; | ||
| 8 | use embassy_nrf::{bind_interrupts, peripherals, spis}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | bind_interrupts!(struct Irqs { | ||
| 12 | SPIM2_SPIS2_SPI2 => spis::InterruptHandler<peripherals::SPI2>; | ||
| 13 | }); | ||
| 14 | |||
| 15 | #[embassy_executor::main] | ||
| 16 | async fn main(_spawner: Spawner) { | ||
| 17 | let p = embassy_nrf::init(Default::default()); | ||
| 18 | info!("Running!"); | ||
| 19 | |||
| 20 | let mut spis = Spis::new(p.SPI2, Irqs, p.P0_31, p.P0_29, p.P0_28, p.P0_30, Config::default()); | ||
| 21 | |||
| 22 | loop { | ||
| 23 | let mut rx_buf = [0_u8; 64]; | ||
| 24 | let tx_buf = [1_u8, 2, 3, 4, 5, 6, 7, 8]; | ||
| 25 | if let Ok((n_rx, n_tx)) = spis.transfer(&mut rx_buf, &tx_buf).await { | ||
| 26 | info!("RX: {:?}", rx_buf[..n_rx]); | ||
| 27 | info!("TX: {:?}", tx_buf[..n_tx]); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
diff --git a/examples/nrf/src/bin/temp.rs b/examples/nrf52840/src/bin/temp.rs index b06ac709e..70957548f 100644 --- a/examples/nrf/src/bin/temp.rs +++ b/examples/nrf52840/src/bin/temp.rs | |||
| @@ -4,16 +4,19 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::info; | 5 | use defmt::info; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_nrf::interrupt; | ||
| 8 | use embassy_nrf::temp::Temp; | 7 | use embassy_nrf::temp::Temp; |
| 8 | use embassy_nrf::{bind_interrupts, temp}; | ||
| 9 | use embassy_time::{Duration, Timer}; | 9 | use embassy_time::{Duration, Timer}; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 11 | ||
| 12 | bind_interrupts!(struct Irqs { | ||
| 13 | TEMP => temp::InterruptHandler; | ||
| 14 | }); | ||
| 15 | |||
| 12 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 13 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 14 | let p = embassy_nrf::init(Default::default()); | 18 | let p = embassy_nrf::init(Default::default()); |
| 15 | let irq = interrupt::take!(TEMP); | 19 | let mut temp = Temp::new(p.TEMP, Irqs); |
| 16 | let mut temp = Temp::new(p.TEMP, irq); | ||
| 17 | 20 | ||
| 18 | loop { | 21 | loop { |
| 19 | let value = temp.read().await; | 22 | let value = temp.read().await; |
diff --git a/examples/nrf/src/bin/timer.rs b/examples/nrf52840/src/bin/timer.rs index c22b5acd5..c22b5acd5 100644 --- a/examples/nrf/src/bin/timer.rs +++ b/examples/nrf52840/src/bin/timer.rs | |||
diff --git a/examples/nrf/src/bin/twim.rs b/examples/nrf52840/src/bin/twim.rs index a027cc1e7..959e3a4be 100644 --- a/examples/nrf/src/bin/twim.rs +++ b/examples/nrf52840/src/bin/twim.rs | |||
| @@ -8,19 +8,22 @@ | |||
| 8 | 8 | ||
| 9 | use defmt::*; | 9 | use defmt::*; |
| 10 | use embassy_executor::Spawner; | 10 | use embassy_executor::Spawner; |
| 11 | use embassy_nrf::interrupt; | ||
| 12 | use embassy_nrf::twim::{self, Twim}; | 11 | use embassy_nrf::twim::{self, Twim}; |
| 12 | use embassy_nrf::{bind_interrupts, peripherals}; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 14 | 14 | ||
| 15 | const ADDRESS: u8 = 0x50; | 15 | const ADDRESS: u8 = 0x50; |
| 16 | 16 | ||
| 17 | bind_interrupts!(struct Irqs { | ||
| 18 | SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler<peripherals::TWISPI0>; | ||
| 19 | }); | ||
| 20 | |||
| 17 | #[embassy_executor::main] | 21 | #[embassy_executor::main] |
| 18 | async fn main(_spawner: Spawner) { | 22 | async fn main(_spawner: Spawner) { |
| 19 | let p = embassy_nrf::init(Default::default()); | 23 | let p = embassy_nrf::init(Default::default()); |
| 20 | info!("Initializing TWI..."); | 24 | info!("Initializing TWI..."); |
| 21 | let config = twim::Config::default(); | 25 | let config = twim::Config::default(); |
| 22 | let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); | 26 | let mut twi = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config); |
| 23 | let mut twi = Twim::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config); | ||
| 24 | 27 | ||
| 25 | info!("Reading..."); | 28 | info!("Reading..."); |
| 26 | 29 | ||
diff --git a/examples/nrf/src/bin/twim_lowpower.rs b/examples/nrf52840/src/bin/twim_lowpower.rs index e30cc9688..0970d3c3c 100644 --- a/examples/nrf/src/bin/twim_lowpower.rs +++ b/examples/nrf52840/src/bin/twim_lowpower.rs | |||
| @@ -12,25 +12,28 @@ use core::mem; | |||
| 12 | 12 | ||
| 13 | use defmt::*; | 13 | use defmt::*; |
| 14 | use embassy_executor::Spawner; | 14 | use embassy_executor::Spawner; |
| 15 | use embassy_nrf::interrupt; | ||
| 16 | use embassy_nrf::twim::{self, Twim}; | 15 | use embassy_nrf::twim::{self, Twim}; |
| 16 | use embassy_nrf::{bind_interrupts, peripherals}; | ||
| 17 | use embassy_time::{Duration, Timer}; | 17 | use embassy_time::{Duration, Timer}; |
| 18 | use {defmt_rtt as _, panic_probe as _}; | 18 | use {defmt_rtt as _, panic_probe as _}; |
| 19 | 19 | ||
| 20 | const ADDRESS: u8 = 0x50; | 20 | const ADDRESS: u8 = 0x50; |
| 21 | 21 | ||
| 22 | bind_interrupts!(struct Irqs { | ||
| 23 | SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler<peripherals::TWISPI0>; | ||
| 24 | }); | ||
| 25 | |||
| 22 | #[embassy_executor::main] | 26 | #[embassy_executor::main] |
| 23 | async fn main(_p: Spawner) { | 27 | async fn main(_p: Spawner) { |
| 24 | let mut p = embassy_nrf::init(Default::default()); | 28 | let mut p = embassy_nrf::init(Default::default()); |
| 25 | info!("Started!"); | 29 | info!("Started!"); |
| 26 | let mut irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); | ||
| 27 | 30 | ||
| 28 | loop { | 31 | loop { |
| 29 | info!("Initializing TWI..."); | 32 | info!("Initializing TWI..."); |
| 30 | let config = twim::Config::default(); | 33 | let config = twim::Config::default(); |
| 31 | 34 | ||
| 32 | // 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. |
| 33 | let mut twi = Twim::new(&mut p.TWISPI0, &mut irq, &mut p.P0_03, &mut p.P0_04, config); | 36 | let mut twi = Twim::new(&mut p.TWISPI0, Irqs, &mut p.P0_03, &mut p.P0_04, config); |
| 34 | 37 | ||
| 35 | info!("Reading..."); | 38 | info!("Reading..."); |
| 36 | 39 | ||
diff --git a/examples/nrf52840/src/bin/twis.rs b/examples/nrf52840/src/bin/twis.rs new file mode 100644 index 000000000..aa42b679e --- /dev/null +++ b/examples/nrf52840/src/bin/twis.rs | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | //! TWIS example | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | #![feature(type_alias_impl_trait)] | ||
| 6 | |||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_nrf::twis::{self, Command, Twis}; | ||
| 10 | use embassy_nrf::{bind_interrupts, peripherals}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | bind_interrupts!(struct Irqs { | ||
| 14 | SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twis::InterruptHandler<peripherals::TWISPI0>; | ||
| 15 | }); | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | let p = embassy_nrf::init(Default::default()); | ||
| 20 | |||
| 21 | let mut config = twis::Config::default(); | ||
| 22 | config.address0 = 0x55; // Set i2c address | ||
| 23 | let mut i2c = Twis::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config); | ||
| 24 | |||
| 25 | info!("Listening..."); | ||
| 26 | loop { | ||
| 27 | let response = [1, 2, 3, 4, 5, 6, 7, 8]; | ||
| 28 | // This buffer is used if the i2c master performs a Write or WriteRead | ||
| 29 | let mut buf = [0u8; 16]; | ||
| 30 | match i2c.listen(&mut buf).await { | ||
| 31 | Ok(Command::Read) => { | ||
| 32 | info!("Got READ command. Respond with data:\n{:?}\n", response); | ||
| 33 | if let Err(e) = i2c.respond_to_read(&response).await { | ||
| 34 | error!("{:?}", e); | ||
| 35 | } | ||
| 36 | } | ||
| 37 | Ok(Command::Write(n)) => info!("Got WRITE command with data:\n{:?}\n", buf[..n]), | ||
| 38 | Ok(Command::WriteRead(n)) => { | ||
| 39 | info!("Got WRITE/READ command with data:\n{:?}", buf[..n]); | ||
| 40 | info!("Respond with data:\n{:?}\n", response); | ||
| 41 | if let Err(e) = i2c.respond_to_read(&response).await { | ||
| 42 | error!("{:?}", e); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | Err(e) => error!("{:?}", e), | ||
| 46 | } | ||
| 47 | } | ||
| 48 | } | ||
diff --git a/examples/nrf/src/bin/uart.rs b/examples/nrf52840/src/bin/uart.rs index 600f7a6ef..50d5cab8c 100644 --- a/examples/nrf/src/bin/uart.rs +++ b/examples/nrf52840/src/bin/uart.rs | |||
| @@ -4,9 +4,13 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_nrf::{interrupt, uarte}; | 7 | use embassy_nrf::{bind_interrupts, peripherals, uarte}; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| 10 | bind_interrupts!(struct Irqs { | ||
| 11 | UARTE0_UART0 => uarte::InterruptHandler<peripherals::UARTE0>; | ||
| 12 | }); | ||
| 13 | |||
| 10 | #[embassy_executor::main] | 14 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 15 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_nrf::init(Default::default()); | 16 | let p = embassy_nrf::init(Default::default()); |
| @@ -14,8 +18,7 @@ async fn main(_spawner: Spawner) { | |||
| 14 | config.parity = uarte::Parity::EXCLUDED; | 18 | config.parity = uarte::Parity::EXCLUDED; |
| 15 | config.baudrate = uarte::Baudrate::BAUD115200; | 19 | config.baudrate = uarte::Baudrate::BAUD115200; |
| 16 | 20 | ||
| 17 | let irq = interrupt::take!(UARTE0_UART0); | 21 | let mut uart = uarte::Uarte::new(p.UARTE0, Irqs, p.P0_08, p.P0_06, config); |
| 18 | let mut uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config); | ||
| 19 | 22 | ||
| 20 | info!("uarte initialized!"); | 23 | info!("uarte initialized!"); |
| 21 | 24 | ||
diff --git a/examples/nrf52840/src/bin/uart_idle.rs b/examples/nrf52840/src/bin/uart_idle.rs new file mode 100644 index 000000000..e1f42fa6c --- /dev/null +++ b/examples/nrf52840/src/bin/uart_idle.rs | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::peripherals::UARTE0; | ||
| 8 | use embassy_nrf::{bind_interrupts, uarte}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | bind_interrupts!(struct Irqs { | ||
| 12 | UARTE0_UART0 => uarte::InterruptHandler<UARTE0>; | ||
| 13 | }); | ||
| 14 | |||
| 15 | #[embassy_executor::main] | ||
| 16 | async fn main(_spawner: Spawner) { | ||
| 17 | let p = embassy_nrf::init(Default::default()); | ||
| 18 | let mut config = uarte::Config::default(); | ||
| 19 | config.parity = uarte::Parity::EXCLUDED; | ||
| 20 | config.baudrate = uarte::Baudrate::BAUD115200; | ||
| 21 | |||
| 22 | let uart = uarte::Uarte::new(p.UARTE0, Irqs, p.P0_08, p.P0_06, config); | ||
| 23 | let (mut tx, mut rx) = uart.split_with_idle(p.TIMER0, p.PPI_CH0, p.PPI_CH1); | ||
| 24 | |||
| 25 | info!("uarte initialized!"); | ||
| 26 | |||
| 27 | // Message must be in SRAM | ||
| 28 | let mut buf = [0; 8]; | ||
| 29 | buf.copy_from_slice(b"Hello!\r\n"); | ||
| 30 | |||
| 31 | unwrap!(tx.write(&buf).await); | ||
| 32 | info!("wrote hello in uart!"); | ||
| 33 | |||
| 34 | loop { | ||
| 35 | info!("reading..."); | ||
| 36 | let n = unwrap!(rx.read_until_idle(&mut buf).await); | ||
| 37 | info!("got {} bytes", n); | ||
| 38 | } | ||
| 39 | } | ||
diff --git a/examples/nrf/src/bin/uart_split.rs b/examples/nrf52840/src/bin/uart_split.rs index 1adaf53fd..9979a1d53 100644 --- a/examples/nrf/src/bin/uart_split.rs +++ b/examples/nrf52840/src/bin/uart_split.rs | |||
| @@ -6,13 +6,17 @@ use defmt::*; | |||
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_nrf::peripherals::UARTE0; | 7 | use embassy_nrf::peripherals::UARTE0; |
| 8 | use embassy_nrf::uarte::UarteRx; | 8 | use embassy_nrf::uarte::UarteRx; |
| 9 | use embassy_nrf::{interrupt, uarte}; | 9 | use embassy_nrf::{bind_interrupts, uarte}; |
| 10 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | 10 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; |
| 11 | use embassy_sync::channel::Channel; | 11 | use embassy_sync::channel::Channel; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 13 | ||
| 14 | static CHANNEL: Channel<ThreadModeRawMutex, [u8; 8], 1> = Channel::new(); | 14 | static CHANNEL: Channel<ThreadModeRawMutex, [u8; 8], 1> = Channel::new(); |
| 15 | 15 | ||
| 16 | bind_interrupts!(struct Irqs { | ||
| 17 | UARTE0_UART0 => uarte::InterruptHandler<UARTE0>; | ||
| 18 | }); | ||
| 19 | |||
| 16 | #[embassy_executor::main] | 20 | #[embassy_executor::main] |
| 17 | async fn main(spawner: Spawner) { | 21 | async fn main(spawner: Spawner) { |
| 18 | let p = embassy_nrf::init(Default::default()); | 22 | let p = embassy_nrf::init(Default::default()); |
| @@ -20,8 +24,7 @@ async fn main(spawner: Spawner) { | |||
| 20 | config.parity = uarte::Parity::EXCLUDED; | 24 | config.parity = uarte::Parity::EXCLUDED; |
| 21 | config.baudrate = uarte::Baudrate::BAUD115200; | 25 | config.baudrate = uarte::Baudrate::BAUD115200; |
| 22 | 26 | ||
| 23 | let irq = interrupt::take!(UARTE0_UART0); | 27 | let uart = uarte::Uarte::new(p.UARTE0, Irqs, p.P0_08, p.P0_06, config); |
| 24 | let uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config); | ||
| 25 | let (mut tx, rx) = uart.split(); | 28 | let (mut tx, rx) = uart.split(); |
| 26 | 29 | ||
| 27 | info!("uarte initialized!"); | 30 | info!("uarte initialized!"); |
diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs new file mode 100644 index 000000000..f527c0d7f --- /dev/null +++ b/examples/nrf52840/src/bin/usb_ethernet.rs | |||
| @@ -0,0 +1,165 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::mem; | ||
| 6 | |||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_net::tcp::TcpSocket; | ||
| 10 | use embassy_net::{Stack, StackResources}; | ||
| 11 | use embassy_nrf::rng::Rng; | ||
| 12 | use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; | ||
| 13 | use embassy_nrf::usb::Driver; | ||
| 14 | use embassy_nrf::{bind_interrupts, pac, peripherals, rng, usb}; | ||
| 15 | use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; | ||
| 16 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; | ||
| 17 | use embassy_usb::{Builder, Config, UsbDevice}; | ||
| 18 | use embedded_io::asynch::Write; | ||
| 19 | use static_cell::make_static; | ||
| 20 | use {defmt_rtt as _, panic_probe as _}; | ||
| 21 | |||
| 22 | bind_interrupts!(struct Irqs { | ||
| 23 | USBD => usb::InterruptHandler<peripherals::USBD>; | ||
| 24 | POWER_CLOCK => usb::vbus_detect::InterruptHandler; | ||
| 25 | RNG => rng::InterruptHandler<peripherals::RNG>; | ||
| 26 | }); | ||
| 27 | |||
| 28 | type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; | ||
| 29 | |||
| 30 | const MTU: usize = 1514; | ||
| 31 | |||
| 32 | #[embassy_executor::task] | ||
| 33 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { | ||
| 34 | device.run().await | ||
| 35 | } | ||
| 36 | |||
| 37 | #[embassy_executor::task] | ||
| 38 | async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { | ||
| 39 | class.run().await | ||
| 40 | } | ||
| 41 | |||
| 42 | #[embassy_executor::task] | ||
| 43 | async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! { | ||
| 44 | stack.run().await | ||
| 45 | } | ||
| 46 | |||
| 47 | #[embassy_executor::main] | ||
| 48 | async fn main(spawner: Spawner) { | ||
| 49 | let p = embassy_nrf::init(Default::default()); | ||
| 50 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 51 | |||
| 52 | info!("Enabling ext hfosc..."); | ||
| 53 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | ||
| 54 | while clock.events_hfclkstarted.read().bits() != 1 {} | ||
| 55 | |||
| 56 | // Create the driver, from the HAL. | ||
| 57 | let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); | ||
| 58 | |||
| 59 | // Create embassy-usb Config | ||
| 60 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 61 | config.manufacturer = Some("Embassy"); | ||
| 62 | config.product = Some("USB-Ethernet example"); | ||
| 63 | config.serial_number = Some("12345678"); | ||
| 64 | config.max_power = 100; | ||
| 65 | config.max_packet_size_0 = 64; | ||
| 66 | |||
| 67 | // Required for Windows support. | ||
| 68 | config.composite_with_iads = true; | ||
| 69 | config.device_class = 0xEF; | ||
| 70 | config.device_sub_class = 0x02; | ||
| 71 | config.device_protocol = 0x01; | ||
| 72 | |||
| 73 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 74 | let mut builder = Builder::new( | ||
| 75 | driver, | ||
| 76 | config, | ||
| 77 | &mut make_static!([0; 256])[..], | ||
| 78 | &mut make_static!([0; 256])[..], | ||
| 79 | &mut make_static!([0; 256])[..], | ||
| 80 | &mut make_static!([0; 128])[..], | ||
| 81 | &mut make_static!([0; 128])[..], | ||
| 82 | ); | ||
| 83 | |||
| 84 | // Our MAC addr. | ||
| 85 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; | ||
| 86 | // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. | ||
| 87 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; | ||
| 88 | |||
| 89 | // Create classes on the builder. | ||
| 90 | let class = CdcNcmClass::new(&mut builder, make_static!(State::new()), host_mac_addr, 64); | ||
| 91 | |||
| 92 | // Build the builder. | ||
| 93 | let usb = builder.build(); | ||
| 94 | |||
| 95 | unwrap!(spawner.spawn(usb_task(usb))); | ||
| 96 | |||
| 97 | let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(make_static!(NetState::new()), our_mac_addr); | ||
| 98 | unwrap!(spawner.spawn(usb_ncm_task(runner))); | ||
| 99 | |||
| 100 | let config = embassy_net::Config::dhcpv4(Default::default()); | ||
| 101 | // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { | ||
| 102 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | ||
| 103 | // dns_servers: Vec::new(), | ||
| 104 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | ||
| 105 | // }); | ||
| 106 | |||
| 107 | // Generate random seed | ||
| 108 | let mut rng = Rng::new(p.RNG, Irqs); | ||
| 109 | let mut seed = [0; 8]; | ||
| 110 | rng.blocking_fill_bytes(&mut seed); | ||
| 111 | let seed = u64::from_le_bytes(seed); | ||
| 112 | |||
| 113 | // Init network stack | ||
| 114 | let stack = &*make_static!(Stack::new( | ||
| 115 | device, | ||
| 116 | config, | ||
| 117 | make_static!(StackResources::<2>::new()), | ||
| 118 | seed | ||
| 119 | )); | ||
| 120 | |||
| 121 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 122 | |||
| 123 | // And now we can use it! | ||
| 124 | |||
| 125 | let mut rx_buffer = [0; 4096]; | ||
| 126 | let mut tx_buffer = [0; 4096]; | ||
| 127 | let mut buf = [0; 4096]; | ||
| 128 | |||
| 129 | loop { | ||
| 130 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 131 | socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); | ||
| 132 | |||
| 133 | info!("Listening on TCP:1234..."); | ||
| 134 | if let Err(e) = socket.accept(1234).await { | ||
| 135 | warn!("accept error: {:?}", e); | ||
| 136 | continue; | ||
| 137 | } | ||
| 138 | |||
| 139 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 140 | |||
| 141 | loop { | ||
| 142 | let n = match socket.read(&mut buf).await { | ||
| 143 | Ok(0) => { | ||
| 144 | warn!("read EOF"); | ||
| 145 | break; | ||
| 146 | } | ||
| 147 | Ok(n) => n, | ||
| 148 | Err(e) => { | ||
| 149 | warn!("read error: {:?}", e); | ||
| 150 | break; | ||
| 151 | } | ||
| 152 | }; | ||
| 153 | |||
| 154 | info!("rxd {:02x}", &buf[..n]); | ||
| 155 | |||
| 156 | match socket.write_all(&buf[..n]).await { | ||
| 157 | Ok(()) => {} | ||
| 158 | Err(e) => { | ||
| 159 | warn!("write error: {:?}", e); | ||
| 160 | break; | ||
| 161 | } | ||
| 162 | }; | ||
| 163 | } | ||
| 164 | } | ||
| 165 | } | ||
diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index ba2159c72..7ccd2946a 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(generic_associated_types)] | ||
| 4 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 5 | 4 | ||
| 6 | use core::mem; | 5 | use core::mem; |
| @@ -8,18 +7,25 @@ use core::sync::atomic::{AtomicBool, Ordering}; | |||
| 8 | 7 | ||
| 9 | use defmt::*; | 8 | use defmt::*; |
| 10 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 11 | use embassy_futures::{select, Either}; | 10 | use embassy_futures::join::join; |
| 11 | use embassy_futures::select::{select, Either}; | ||
| 12 | use embassy_nrf::gpio::{Input, Pin, Pull}; | 12 | use embassy_nrf::gpio::{Input, Pin, Pull}; |
| 13 | use embassy_nrf::usb::{Driver, PowerUsb}; | 13 | use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; |
| 14 | use embassy_nrf::{interrupt, pac}; | 14 | use embassy_nrf::usb::Driver; |
| 15 | use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; | ||
| 16 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 15 | use embassy_sync::signal::Signal; | 17 | use embassy_sync::signal::Signal; |
| 18 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; | ||
| 16 | use embassy_usb::control::OutResponse; | 19 | use embassy_usb::control::OutResponse; |
| 17 | use embassy_usb::{Builder, Config, DeviceStateHandler}; | 20 | use embassy_usb::{Builder, Config, Handler}; |
| 18 | use embassy_usb_hid::{HidReaderWriter, ReportId, RequestHandler, State}; | ||
| 19 | use futures::future::join; | ||
| 20 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; | 21 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; |
| 21 | use {defmt_rtt as _, panic_probe as _}; | 22 | use {defmt_rtt as _, panic_probe as _}; |
| 22 | 23 | ||
| 24 | bind_interrupts!(struct Irqs { | ||
| 25 | USBD => usb::InterruptHandler<peripherals::USBD>; | ||
| 26 | POWER_CLOCK => usb::vbus_detect::InterruptHandler; | ||
| 27 | }); | ||
| 28 | |||
| 23 | static SUSPENDED: AtomicBool = AtomicBool::new(false); | 29 | static SUSPENDED: AtomicBool = AtomicBool::new(false); |
| 24 | 30 | ||
| 25 | #[embassy_executor::main] | 31 | #[embassy_executor::main] |
| @@ -32,9 +38,7 @@ async fn main(_spawner: Spawner) { | |||
| 32 | while clock.events_hfclkstarted.read().bits() != 1 {} | 38 | while clock.events_hfclkstarted.read().bits() != 1 {} |
| 33 | 39 | ||
| 34 | // Create the driver, from the HAL. | 40 | // Create the driver, from the HAL. |
| 35 | let irq = interrupt::take!(USBD); | 41 | let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); |
| 36 | let power_irq = interrupt::take!(POWER_CLOCK); | ||
| 37 | let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); | ||
| 38 | 42 | ||
| 39 | // Create embassy-usb Config | 43 | // Create embassy-usb Config |
| 40 | let mut config = Config::new(0xc0de, 0xcafe); | 44 | let mut config = Config::new(0xc0de, 0xcafe); |
| @@ -50,9 +54,10 @@ async fn main(_spawner: Spawner) { | |||
| 50 | let mut device_descriptor = [0; 256]; | 54 | let mut device_descriptor = [0; 256]; |
| 51 | let mut config_descriptor = [0; 256]; | 55 | let mut config_descriptor = [0; 256]; |
| 52 | let mut bos_descriptor = [0; 256]; | 56 | let mut bos_descriptor = [0; 256]; |
| 57 | let mut msos_descriptor = [0; 256]; | ||
| 53 | let mut control_buf = [0; 64]; | 58 | let mut control_buf = [0; 64]; |
| 54 | let request_handler = MyRequestHandler {}; | 59 | let request_handler = MyRequestHandler {}; |
| 55 | let device_state_handler = MyDeviceStateHandler::new(); | 60 | let mut device_handler = MyDeviceHandler::new(); |
| 56 | 61 | ||
| 57 | let mut state = State::new(); | 62 | let mut state = State::new(); |
| 58 | 63 | ||
| @@ -62,12 +67,14 @@ async fn main(_spawner: Spawner) { | |||
| 62 | &mut device_descriptor, | 67 | &mut device_descriptor, |
| 63 | &mut config_descriptor, | 68 | &mut config_descriptor, |
| 64 | &mut bos_descriptor, | 69 | &mut bos_descriptor, |
| 70 | &mut msos_descriptor, | ||
| 65 | &mut control_buf, | 71 | &mut control_buf, |
| 66 | Some(&device_state_handler), | ||
| 67 | ); | 72 | ); |
| 68 | 73 | ||
| 74 | builder.handler(&mut device_handler); | ||
| 75 | |||
| 69 | // Create classes on the builder. | 76 | // Create classes on the builder. |
| 70 | let config = embassy_usb_hid::Config { | 77 | let config = embassy_usb::class::hid::Config { |
| 71 | report_descriptor: KeyboardReport::desc(), | 78 | report_descriptor: KeyboardReport::desc(), |
| 72 | request_handler: Some(&request_handler), | 79 | request_handler: Some(&request_handler), |
| 73 | poll_ms: 60, | 80 | poll_ms: 60, |
| @@ -78,7 +85,7 @@ async fn main(_spawner: Spawner) { | |||
| 78 | // Build the builder. | 85 | // Build the builder. |
| 79 | let mut usb = builder.build(); | 86 | let mut usb = builder.build(); |
| 80 | 87 | ||
| 81 | let remote_wakeup = Signal::new(); | 88 | let remote_wakeup: Signal<CriticalSectionRawMutex, _> = Signal::new(); |
| 82 | 89 | ||
| 83 | // Run the USB device. | 90 | // Run the USB device. |
| 84 | let usb_fut = async { | 91 | let usb_fut = async { |
| @@ -164,20 +171,20 @@ impl RequestHandler for MyRequestHandler { | |||
| 164 | } | 171 | } |
| 165 | } | 172 | } |
| 166 | 173 | ||
| 167 | struct MyDeviceStateHandler { | 174 | struct MyDeviceHandler { |
| 168 | configured: AtomicBool, | 175 | configured: AtomicBool, |
| 169 | } | 176 | } |
| 170 | 177 | ||
| 171 | impl MyDeviceStateHandler { | 178 | impl MyDeviceHandler { |
| 172 | fn new() -> Self { | 179 | fn new() -> Self { |
| 173 | MyDeviceStateHandler { | 180 | MyDeviceHandler { |
| 174 | configured: AtomicBool::new(false), | 181 | configured: AtomicBool::new(false), |
| 175 | } | 182 | } |
| 176 | } | 183 | } |
| 177 | } | 184 | } |
| 178 | 185 | ||
| 179 | impl DeviceStateHandler for MyDeviceStateHandler { | 186 | impl Handler for MyDeviceHandler { |
| 180 | fn enabled(&self, enabled: bool) { | 187 | fn enabled(&mut self, enabled: bool) { |
| 181 | self.configured.store(false, Ordering::Relaxed); | 188 | self.configured.store(false, Ordering::Relaxed); |
| 182 | SUSPENDED.store(false, Ordering::Release); | 189 | SUSPENDED.store(false, Ordering::Release); |
| 183 | if enabled { | 190 | if enabled { |
| @@ -187,17 +194,17 @@ impl DeviceStateHandler for MyDeviceStateHandler { | |||
| 187 | } | 194 | } |
| 188 | } | 195 | } |
| 189 | 196 | ||
| 190 | fn reset(&self) { | 197 | fn reset(&mut self) { |
| 191 | self.configured.store(false, Ordering::Relaxed); | 198 | self.configured.store(false, Ordering::Relaxed); |
| 192 | info!("Bus reset, the Vbus current limit is 100mA"); | 199 | info!("Bus reset, the Vbus current limit is 100mA"); |
| 193 | } | 200 | } |
| 194 | 201 | ||
| 195 | fn addressed(&self, addr: u8) { | 202 | fn addressed(&mut self, addr: u8) { |
| 196 | self.configured.store(false, Ordering::Relaxed); | 203 | self.configured.store(false, Ordering::Relaxed); |
| 197 | info!("USB address set to: {}", addr); | 204 | info!("USB address set to: {}", addr); |
| 198 | } | 205 | } |
| 199 | 206 | ||
| 200 | fn configured(&self, configured: bool) { | 207 | fn configured(&mut self, configured: bool) { |
| 201 | self.configured.store(configured, Ordering::Relaxed); | 208 | self.configured.store(configured, Ordering::Relaxed); |
| 202 | if configured { | 209 | if configured { |
| 203 | info!("Device configured, it may now draw up to the configured current limit from Vbus.") | 210 | info!("Device configured, it may now draw up to the configured current limit from Vbus.") |
| @@ -206,7 +213,7 @@ impl DeviceStateHandler for MyDeviceStateHandler { | |||
| 206 | } | 213 | } |
| 207 | } | 214 | } |
| 208 | 215 | ||
| 209 | fn suspended(&self, suspended: bool) { | 216 | fn suspended(&mut self, suspended: bool) { |
| 210 | if suspended { | 217 | if suspended { |
| 211 | info!("Device suspended, the Vbus current limit is 500µA (or 2.5mA for high-power devices with remote wakeup enabled)."); | 218 | info!("Device suspended, the Vbus current limit is 500µA (or 2.5mA for high-power devices with remote wakeup enabled)."); |
| 212 | SUSPENDED.store(true, Ordering::Release); | 219 | SUSPENDED.store(true, Ordering::Release); |
diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index 7cd2ece17..edf634a5e 100644 --- a/examples/nrf/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs | |||
| @@ -1,22 +1,27 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(generic_associated_types)] | ||
| 4 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 5 | 4 | ||
| 6 | use core::mem; | 5 | use core::mem; |
| 7 | 6 | ||
| 8 | use defmt::*; | 7 | use defmt::*; |
| 9 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 10 | use embassy_nrf::usb::{Driver, PowerUsb}; | 9 | use embassy_futures::join::join; |
| 11 | use embassy_nrf::{interrupt, pac}; | 10 | use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; |
| 11 | use embassy_nrf::usb::Driver; | ||
| 12 | use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; | ||
| 12 | use embassy_time::{Duration, Timer}; | 13 | use embassy_time::{Duration, Timer}; |
| 14 | use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; | ||
| 13 | use embassy_usb::control::OutResponse; | 15 | use embassy_usb::control::OutResponse; |
| 14 | use embassy_usb::{Builder, Config}; | 16 | use embassy_usb::{Builder, Config}; |
| 15 | use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State}; | ||
| 16 | use futures::future::join; | ||
| 17 | use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; | 17 | use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; |
| 18 | use {defmt_rtt as _, panic_probe as _}; | 18 | use {defmt_rtt as _, panic_probe as _}; |
| 19 | 19 | ||
| 20 | bind_interrupts!(struct Irqs { | ||
| 21 | USBD => usb::InterruptHandler<peripherals::USBD>; | ||
| 22 | POWER_CLOCK => usb::vbus_detect::InterruptHandler; | ||
| 23 | }); | ||
| 24 | |||
| 20 | #[embassy_executor::main] | 25 | #[embassy_executor::main] |
| 21 | async fn main(_spawner: Spawner) { | 26 | async fn main(_spawner: Spawner) { |
| 22 | let p = embassy_nrf::init(Default::default()); | 27 | let p = embassy_nrf::init(Default::default()); |
| @@ -27,9 +32,7 @@ async fn main(_spawner: Spawner) { | |||
| 27 | while clock.events_hfclkstarted.read().bits() != 1 {} | 32 | while clock.events_hfclkstarted.read().bits() != 1 {} |
| 28 | 33 | ||
| 29 | // Create the driver, from the HAL. | 34 | // Create the driver, from the HAL. |
| 30 | let irq = interrupt::take!(USBD); | 35 | let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); |
| 31 | let power_irq = interrupt::take!(POWER_CLOCK); | ||
| 32 | let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); | ||
| 33 | 36 | ||
| 34 | // Create embassy-usb Config | 37 | // Create embassy-usb Config |
| 35 | let mut config = Config::new(0xc0de, 0xcafe); | 38 | let mut config = Config::new(0xc0de, 0xcafe); |
| @@ -44,6 +47,7 @@ async fn main(_spawner: Spawner) { | |||
| 44 | let mut device_descriptor = [0; 256]; | 47 | let mut device_descriptor = [0; 256]; |
| 45 | let mut config_descriptor = [0; 256]; | 48 | let mut config_descriptor = [0; 256]; |
| 46 | let mut bos_descriptor = [0; 256]; | 49 | let mut bos_descriptor = [0; 256]; |
| 50 | let mut msos_descriptor = [0; 256]; | ||
| 47 | let mut control_buf = [0; 64]; | 51 | let mut control_buf = [0; 64]; |
| 48 | let request_handler = MyRequestHandler {}; | 52 | let request_handler = MyRequestHandler {}; |
| 49 | 53 | ||
| @@ -55,12 +59,12 @@ async fn main(_spawner: Spawner) { | |||
| 55 | &mut device_descriptor, | 59 | &mut device_descriptor, |
| 56 | &mut config_descriptor, | 60 | &mut config_descriptor, |
| 57 | &mut bos_descriptor, | 61 | &mut bos_descriptor, |
| 62 | &mut msos_descriptor, | ||
| 58 | &mut control_buf, | 63 | &mut control_buf, |
| 59 | None, | ||
| 60 | ); | 64 | ); |
| 61 | 65 | ||
| 62 | // Create classes on the builder. | 66 | // Create classes on the builder. |
| 63 | let config = embassy_usb_hid::Config { | 67 | let config = embassy_usb::class::hid::Config { |
| 64 | report_descriptor: MouseReport::desc(), | 68 | report_descriptor: MouseReport::desc(), |
| 65 | request_handler: Some(&request_handler), | 69 | request_handler: Some(&request_handler), |
| 66 | poll_ms: 60, | 70 | poll_ms: 60, |
diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs index a68edb329..dc95cde84 100644 --- a/examples/nrf/src/bin/usb_serial.rs +++ b/examples/nrf52840/src/bin/usb_serial.rs | |||
| @@ -1,20 +1,25 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(generic_associated_types)] | ||
| 4 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 5 | 4 | ||
| 6 | use core::mem; | 5 | use core::mem; |
| 7 | 6 | ||
| 8 | use defmt::{info, panic}; | 7 | use defmt::{info, panic}; |
| 9 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 10 | use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply}; | 9 | use embassy_futures::join::join; |
| 11 | use embassy_nrf::{interrupt, pac}; | 10 | use embassy_nrf::usb::vbus_detect::{HardwareVbusDetect, VbusDetect}; |
| 11 | use embassy_nrf::usb::{Driver, Instance}; | ||
| 12 | use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; | ||
| 13 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 12 | use embassy_usb::driver::EndpointError; | 14 | use embassy_usb::driver::EndpointError; |
| 13 | use embassy_usb::{Builder, Config}; | 15 | use embassy_usb::{Builder, Config}; |
| 14 | use embassy_usb_serial::{CdcAcmClass, State}; | ||
| 15 | use futures::future::join; | ||
| 16 | use {defmt_rtt as _, panic_probe as _}; | 16 | use {defmt_rtt as _, panic_probe as _}; |
| 17 | 17 | ||
| 18 | bind_interrupts!(struct Irqs { | ||
| 19 | USBD => usb::InterruptHandler<peripherals::USBD>; | ||
| 20 | POWER_CLOCK => usb::vbus_detect::InterruptHandler; | ||
| 21 | }); | ||
| 22 | |||
| 18 | #[embassy_executor::main] | 23 | #[embassy_executor::main] |
| 19 | async fn main(_spawner: Spawner) { | 24 | async fn main(_spawner: Spawner) { |
| 20 | let p = embassy_nrf::init(Default::default()); | 25 | let p = embassy_nrf::init(Default::default()); |
| @@ -25,9 +30,7 @@ async fn main(_spawner: Spawner) { | |||
| 25 | while clock.events_hfclkstarted.read().bits() != 1 {} | 30 | while clock.events_hfclkstarted.read().bits() != 1 {} |
| 26 | 31 | ||
| 27 | // Create the driver, from the HAL. | 32 | // Create the driver, from the HAL. |
| 28 | let irq = interrupt::take!(USBD); | 33 | let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); |
| 29 | let power_irq = interrupt::take!(POWER_CLOCK); | ||
| 30 | let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); | ||
| 31 | 34 | ||
| 32 | // Create embassy-usb Config | 35 | // Create embassy-usb Config |
| 33 | let mut config = Config::new(0xc0de, 0xcafe); | 36 | let mut config = Config::new(0xc0de, 0xcafe); |
| @@ -37,7 +40,7 @@ async fn main(_spawner: Spawner) { | |||
| 37 | config.max_power = 100; | 40 | config.max_power = 100; |
| 38 | config.max_packet_size_0 = 64; | 41 | config.max_packet_size_0 = 64; |
| 39 | 42 | ||
| 40 | // Required for windows compatiblity. | 43 | // Required for windows compatibility. |
| 41 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | 44 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help |
| 42 | config.device_class = 0xEF; | 45 | config.device_class = 0xEF; |
| 43 | config.device_sub_class = 0x02; | 46 | config.device_sub_class = 0x02; |
| @@ -49,6 +52,7 @@ async fn main(_spawner: Spawner) { | |||
| 49 | let mut device_descriptor = [0; 256]; | 52 | let mut device_descriptor = [0; 256]; |
| 50 | let mut config_descriptor = [0; 256]; | 53 | let mut config_descriptor = [0; 256]; |
| 51 | let mut bos_descriptor = [0; 256]; | 54 | let mut bos_descriptor = [0; 256]; |
| 55 | let mut msos_descriptor = [0; 256]; | ||
| 52 | let mut control_buf = [0; 64]; | 56 | let mut control_buf = [0; 64]; |
| 53 | 57 | ||
| 54 | let mut state = State::new(); | 58 | let mut state = State::new(); |
| @@ -59,8 +63,8 @@ async fn main(_spawner: Spawner) { | |||
| 59 | &mut device_descriptor, | 63 | &mut device_descriptor, |
| 60 | &mut config_descriptor, | 64 | &mut config_descriptor, |
| 61 | &mut bos_descriptor, | 65 | &mut bos_descriptor, |
| 66 | &mut msos_descriptor, | ||
| 62 | &mut control_buf, | 67 | &mut control_buf, |
| 63 | None, | ||
| 64 | ); | 68 | ); |
| 65 | 69 | ||
| 66 | // Create classes on the builder. | 70 | // Create classes on the builder. |
| @@ -98,7 +102,7 @@ impl From<EndpointError> for Disconnected { | |||
| 98 | } | 102 | } |
| 99 | } | 103 | } |
| 100 | 104 | ||
| 101 | async fn echo<'d, T: Instance + 'd, P: UsbSupply + 'd>( | 105 | async fn echo<'d, T: Instance + 'd, P: VbusDetect + 'd>( |
| 102 | class: &mut CdcAcmClass<'d, Driver<'d, T, P>>, | 106 | class: &mut CdcAcmClass<'d, Driver<'d, T, P>>, |
| 103 | ) -> Result<(), Disconnected> { | 107 | ) -> Result<(), Disconnected> { |
| 104 | let mut buf = [0; 64]; | 108 | let mut buf = [0; 64]; |
diff --git a/examples/nrf/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs index d62d7e520..cd4392903 100644 --- a/examples/nrf/src/bin/usb_serial_multitask.rs +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs | |||
| @@ -1,21 +1,26 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(generic_associated_types)] | ||
| 4 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 5 | 4 | ||
| 6 | use core::mem; | 5 | use core::mem; |
| 7 | 6 | ||
| 8 | use defmt::{info, panic, unwrap}; | 7 | use defmt::{info, panic, unwrap}; |
| 9 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 10 | use embassy_nrf::usb::{Driver, PowerUsb}; | 9 | use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; |
| 11 | use embassy_nrf::{interrupt, pac, peripherals}; | 10 | use embassy_nrf::usb::Driver; |
| 11 | use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; | ||
| 12 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 12 | use embassy_usb::driver::EndpointError; | 13 | use embassy_usb::driver::EndpointError; |
| 13 | use embassy_usb::{Builder, Config, UsbDevice}; | 14 | use embassy_usb::{Builder, Config, UsbDevice}; |
| 14 | use embassy_usb_serial::{CdcAcmClass, State}; | 15 | use static_cell::make_static; |
| 15 | use static_cell::StaticCell; | ||
| 16 | use {defmt_rtt as _, panic_probe as _}; | 16 | use {defmt_rtt as _, panic_probe as _}; |
| 17 | 17 | ||
| 18 | type MyDriver = Driver<'static, peripherals::USBD, PowerUsb>; | 18 | bind_interrupts!(struct Irqs { |
| 19 | USBD => usb::InterruptHandler<peripherals::USBD>; | ||
| 20 | POWER_CLOCK => usb::vbus_detect::InterruptHandler; | ||
| 21 | }); | ||
| 22 | |||
| 23 | type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; | ||
| 19 | 24 | ||
| 20 | #[embassy_executor::task] | 25 | #[embassy_executor::task] |
| 21 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) { | 26 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) { |
| @@ -40,10 +45,9 @@ async fn main(spawner: Spawner) { | |||
| 40 | info!("Enabling ext hfosc..."); | 45 | info!("Enabling ext hfosc..."); |
| 41 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | 46 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); |
| 42 | while clock.events_hfclkstarted.read().bits() != 1 {} | 47 | while clock.events_hfclkstarted.read().bits() != 1 {} |
| 48 | |||
| 43 | // Create the driver, from the HAL. | 49 | // Create the driver, from the HAL. |
| 44 | let irq = interrupt::take!(USBD); | 50 | let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); |
| 45 | let power_irq = interrupt::take!(POWER_CLOCK); | ||
| 46 | let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); | ||
| 47 | 51 | ||
| 48 | // Create embassy-usb Config | 52 | // Create embassy-usb Config |
| 49 | let mut config = Config::new(0xc0de, 0xcafe); | 53 | let mut config = Config::new(0xc0de, 0xcafe); |
| @@ -53,42 +57,28 @@ async fn main(spawner: Spawner) { | |||
| 53 | config.max_power = 100; | 57 | config.max_power = 100; |
| 54 | config.max_packet_size_0 = 64; | 58 | config.max_packet_size_0 = 64; |
| 55 | 59 | ||
| 56 | // Required for windows compatiblity. | 60 | // Required for windows compatibility. |
| 57 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | 61 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help |
| 58 | config.device_class = 0xEF; | 62 | config.device_class = 0xEF; |
| 59 | config.device_sub_class = 0x02; | 63 | config.device_sub_class = 0x02; |
| 60 | config.device_protocol = 0x01; | 64 | config.device_protocol = 0x01; |
| 61 | config.composite_with_iads = true; | 65 | config.composite_with_iads = true; |
| 62 | 66 | ||
| 63 | struct Resources { | 67 | let state = make_static!(State::new()); |
| 64 | device_descriptor: [u8; 256], | ||
| 65 | config_descriptor: [u8; 256], | ||
| 66 | bos_descriptor: [u8; 256], | ||
| 67 | control_buf: [u8; 64], | ||
| 68 | serial_state: State<'static>, | ||
| 69 | } | ||
| 70 | static RESOURCES: StaticCell<Resources> = StaticCell::new(); | ||
| 71 | let res = RESOURCES.init(Resources { | ||
| 72 | device_descriptor: [0; 256], | ||
| 73 | config_descriptor: [0; 256], | ||
| 74 | bos_descriptor: [0; 256], | ||
| 75 | control_buf: [0; 64], | ||
| 76 | serial_state: State::new(), | ||
| 77 | }); | ||
| 78 | 68 | ||
| 79 | // Create embassy-usb DeviceBuilder using the driver and config. | 69 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 80 | let mut builder = Builder::new( | 70 | let mut builder = Builder::new( |
| 81 | driver, | 71 | driver, |
| 82 | config, | 72 | config, |
| 83 | &mut res.device_descriptor, | 73 | &mut make_static!([0; 256])[..], |
| 84 | &mut res.config_descriptor, | 74 | &mut make_static!([0; 256])[..], |
| 85 | &mut res.bos_descriptor, | 75 | &mut make_static!([0; 256])[..], |
| 86 | &mut res.control_buf, | 76 | &mut make_static!([0; 128])[..], |
| 87 | None, | 77 | &mut make_static!([0; 128])[..], |
| 88 | ); | 78 | ); |
| 89 | 79 | ||
| 90 | // Create classes on the builder. | 80 | // Create classes on the builder. |
| 91 | let class = CdcAcmClass::new(&mut builder, &mut res.serial_state, 64); | 81 | let class = CdcAcmClass::new(&mut builder, state, 64); |
| 92 | 82 | ||
| 93 | // Build the builder. | 83 | // Build the builder. |
| 94 | let usb = builder.build(); | 84 | let usb = builder.build(); |
diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs new file mode 100644 index 000000000..1d39d3841 --- /dev/null +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::mem; | ||
| 6 | |||
| 7 | use defmt::{info, panic}; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_futures::join::join; | ||
| 10 | use embassy_nrf::usb::vbus_detect::{HardwareVbusDetect, VbusDetect}; | ||
| 11 | use embassy_nrf::usb::{Driver, Instance}; | ||
| 12 | use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; | ||
| 13 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 14 | use embassy_usb::driver::EndpointError; | ||
| 15 | use embassy_usb::msos::{self, windows_version}; | ||
| 16 | use embassy_usb::types::InterfaceNumber; | ||
| 17 | use embassy_usb::{Builder, Config}; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | bind_interrupts!(struct Irqs { | ||
| 21 | USBD => usb::InterruptHandler<peripherals::USBD>; | ||
| 22 | POWER_CLOCK => usb::vbus_detect::InterruptHandler; | ||
| 23 | }); | ||
| 24 | |||
| 25 | // This is a randomly generated GUID to allow clients on Windows to find our device | ||
| 26 | const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321}"]; | ||
| 27 | |||
| 28 | #[embassy_executor::main] | ||
| 29 | async fn main(_spawner: Spawner) { | ||
| 30 | let p = embassy_nrf::init(Default::default()); | ||
| 31 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 32 | |||
| 33 | info!("Enabling ext hfosc..."); | ||
| 34 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | ||
| 35 | while clock.events_hfclkstarted.read().bits() != 1 {} | ||
| 36 | |||
| 37 | // Create the driver, from the HAL. | ||
| 38 | let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); | ||
| 39 | |||
| 40 | // Create embassy-usb Config | ||
| 41 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 42 | config.manufacturer = Some("Embassy"); | ||
| 43 | config.product = Some("USB-serial example"); | ||
| 44 | config.serial_number = Some("12345678"); | ||
| 45 | config.max_power = 100; | ||
| 46 | config.max_packet_size_0 = 64; | ||
| 47 | |||
| 48 | // Required for windows compatibility. | ||
| 49 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 50 | config.device_class = 0xEF; | ||
| 51 | config.device_sub_class = 0x02; | ||
| 52 | config.device_protocol = 0x01; | ||
| 53 | config.composite_with_iads = true; | ||
| 54 | |||
| 55 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 56 | // It needs some buffers for building the descriptors. | ||
| 57 | let mut device_descriptor = [0; 256]; | ||
| 58 | let mut config_descriptor = [0; 256]; | ||
| 59 | let mut bos_descriptor = [0; 256]; | ||
| 60 | let mut msos_descriptor = [0; 256]; | ||
| 61 | let mut control_buf = [0; 64]; | ||
| 62 | |||
| 63 | let mut state = State::new(); | ||
| 64 | |||
| 65 | let mut builder = Builder::new( | ||
| 66 | driver, | ||
| 67 | config, | ||
| 68 | &mut device_descriptor, | ||
| 69 | &mut config_descriptor, | ||
| 70 | &mut bos_descriptor, | ||
| 71 | &mut msos_descriptor, | ||
| 72 | &mut control_buf, | ||
| 73 | ); | ||
| 74 | |||
| 75 | builder.msos_descriptor(windows_version::WIN8_1, 2); | ||
| 76 | |||
| 77 | // Create classes on the builder. | ||
| 78 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 79 | |||
| 80 | // Since we want to create MS OS feature descriptors that apply to a function that has already been added to the | ||
| 81 | // builder, need to get the MsOsDescriptorWriter from the builder and manually add those descriptors. | ||
| 82 | // Inside a class constructor, you would just need to call `FunctionBuilder::msos_feature` instead. | ||
| 83 | let msos_writer = builder.msos_writer(); | ||
| 84 | msos_writer.configuration(0); | ||
| 85 | msos_writer.function(InterfaceNumber(0)); | ||
| 86 | msos_writer.function_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); | ||
| 87 | msos_writer.function_feature(msos::RegistryPropertyFeatureDescriptor::new( | ||
| 88 | "DeviceInterfaceGUIDs", | ||
| 89 | msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS), | ||
| 90 | )); | ||
| 91 | |||
| 92 | // Build the builder. | ||
| 93 | let mut usb = builder.build(); | ||
| 94 | |||
| 95 | // Run the USB device. | ||
| 96 | let usb_fut = usb.run(); | ||
| 97 | |||
| 98 | // Do stuff with the class! | ||
| 99 | let echo_fut = async { | ||
| 100 | loop { | ||
| 101 | class.wait_connection().await; | ||
| 102 | info!("Connected"); | ||
| 103 | let _ = echo(&mut class).await; | ||
| 104 | info!("Disconnected"); | ||
| 105 | } | ||
| 106 | }; | ||
| 107 | |||
| 108 | // Run everything concurrently. | ||
| 109 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 110 | join(usb_fut, echo_fut).await; | ||
| 111 | } | ||
| 112 | |||
| 113 | struct Disconnected {} | ||
| 114 | |||
| 115 | impl From<EndpointError> for Disconnected { | ||
| 116 | fn from(val: EndpointError) -> Self { | ||
| 117 | match val { | ||
| 118 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 119 | EndpointError::Disabled => Disconnected {}, | ||
| 120 | } | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | async fn echo<'d, T: Instance + 'd, P: VbusDetect + 'd>( | ||
| 125 | class: &mut CdcAcmClass<'d, Driver<'d, T, P>>, | ||
| 126 | ) -> 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/nrf/src/bin/wdt.rs b/examples/nrf52840/src/bin/wdt.rs index b0b9c3b81..058746518 100644 --- a/examples/nrf/src/bin/wdt.rs +++ b/examples/nrf52840/src/bin/wdt.rs | |||
| @@ -16,7 +16,7 @@ async fn main(_spawner: Spawner) { | |||
| 16 | let mut config = Config::default(); | 16 | let mut config = Config::default(); |
| 17 | config.timeout_ticks = 32768 * 3; // 3 seconds | 17 | config.timeout_ticks = 32768 * 3; // 3 seconds |
| 18 | 18 | ||
| 19 | // This is needed for `probe-run` to be able to catch the panic message | 19 | // This is needed for `probe-rs run` to be able to catch the panic message |
| 20 | // in the WDT interrupt. The core resets 2 ticks after firing the interrupt. | 20 | // in the WDT interrupt. The core resets 2 ticks after firing the interrupt. |
| 21 | config.run_during_debug_halt = false; | 21 | config.run_during_debug_halt = false; |
| 22 | 22 | ||
diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs new file mode 100644 index 000000000..112e41bcd --- /dev/null +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs | |||
| @@ -0,0 +1,143 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{info, unwrap, warn}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_net::tcp::TcpSocket; | ||
| 8 | use embassy_net::{Stack, StackResources}; | ||
| 9 | use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin, Pull}; | ||
| 10 | use embassy_nrf::rng::Rng; | ||
| 11 | use embassy_nrf::spim::{self, Spim}; | ||
| 12 | use embassy_nrf::{bind_interrupts, peripherals}; | ||
| 13 | use embassy_time::Delay; | ||
| 14 | use embedded_hal_async::spi::ExclusiveDevice; | ||
| 15 | use embedded_io::asynch::Write; | ||
| 16 | use static_cell::make_static; | ||
| 17 | use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; | ||
| 18 | |||
| 19 | const WIFI_NETWORK: &str = "EmbassyTest"; | ||
| 20 | const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; | ||
| 21 | |||
| 22 | bind_interrupts!(struct Irqs { | ||
| 23 | SPIM3 => spim::InterruptHandler<peripherals::SPI3>; | ||
| 24 | RNG => embassy_nrf::rng::InterruptHandler<peripherals::RNG>; | ||
| 25 | }); | ||
| 26 | |||
| 27 | #[embassy_executor::task] | ||
| 28 | async fn wifi_task( | ||
| 29 | runner: hosted::Runner< | ||
| 30 | 'static, | ||
| 31 | ExclusiveDevice<Spim<'static, peripherals::SPI3>, Output<'static, peripherals::P0_31>, Delay>, | ||
| 32 | Input<'static, AnyPin>, | ||
| 33 | Output<'static, peripherals::P1_05>, | ||
| 34 | >, | ||
| 35 | ) -> ! { | ||
| 36 | runner.run().await | ||
| 37 | } | ||
| 38 | |||
| 39 | #[embassy_executor::task] | ||
| 40 | async fn net_task(stack: &'static Stack<hosted::NetDriver<'static>>) -> ! { | ||
| 41 | stack.run().await | ||
| 42 | } | ||
| 43 | |||
| 44 | #[embassy_executor::main] | ||
| 45 | async fn main(spawner: Spawner) { | ||
| 46 | info!("Hello World!"); | ||
| 47 | |||
| 48 | let p = embassy_nrf::init(Default::default()); | ||
| 49 | |||
| 50 | let miso = p.P0_28; | ||
| 51 | let sck = p.P0_29; | ||
| 52 | let mosi = p.P0_30; | ||
| 53 | let cs = Output::new(p.P0_31, Level::High, OutputDrive::HighDrive); | ||
| 54 | let handshake = Input::new(p.P1_01.degrade(), Pull::Up); | ||
| 55 | let ready = Input::new(p.P1_04.degrade(), Pull::None); | ||
| 56 | let reset = Output::new(p.P1_05, Level::Low, OutputDrive::Standard); | ||
| 57 | |||
| 58 | let mut config = spim::Config::default(); | ||
| 59 | config.frequency = spim::Frequency::M32; | ||
| 60 | config.mode = spim::MODE_2; // !!! | ||
| 61 | let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config); | ||
| 62 | let spi = ExclusiveDevice::new(spi, cs, Delay); | ||
| 63 | |||
| 64 | let (device, mut control, runner) = embassy_net_esp_hosted::new( | ||
| 65 | make_static!(embassy_net_esp_hosted::State::new()), | ||
| 66 | spi, | ||
| 67 | handshake, | ||
| 68 | ready, | ||
| 69 | reset, | ||
| 70 | ) | ||
| 71 | .await; | ||
| 72 | |||
| 73 | unwrap!(spawner.spawn(wifi_task(runner))); | ||
| 74 | |||
| 75 | control.init().await; | ||
| 76 | control.join(WIFI_NETWORK, WIFI_PASSWORD).await; | ||
| 77 | |||
| 78 | let config = embassy_net::Config::dhcpv4(Default::default()); | ||
| 79 | // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { | ||
| 80 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | ||
| 81 | // dns_servers: Vec::new(), | ||
| 82 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | ||
| 83 | // }); | ||
| 84 | |||
| 85 | // Generate random seed | ||
| 86 | let mut rng = Rng::new(p.RNG, Irqs); | ||
| 87 | let mut seed = [0; 8]; | ||
| 88 | rng.blocking_fill_bytes(&mut seed); | ||
| 89 | let seed = u64::from_le_bytes(seed); | ||
| 90 | |||
| 91 | // Init network stack | ||
| 92 | let stack = &*make_static!(Stack::new( | ||
| 93 | device, | ||
| 94 | config, | ||
| 95 | make_static!(StackResources::<2>::new()), | ||
| 96 | seed | ||
| 97 | )); | ||
| 98 | |||
| 99 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 100 | |||
| 101 | // And now we can use it! | ||
| 102 | |||
| 103 | let mut rx_buffer = [0; 4096]; | ||
| 104 | let mut tx_buffer = [0; 4096]; | ||
| 105 | let mut buf = [0; 4096]; | ||
| 106 | |||
| 107 | loop { | ||
| 108 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 109 | socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); | ||
| 110 | |||
| 111 | info!("Listening on TCP:1234..."); | ||
| 112 | if let Err(e) = socket.accept(1234).await { | ||
| 113 | warn!("accept error: {:?}", e); | ||
| 114 | continue; | ||
| 115 | } | ||
| 116 | |||
| 117 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 118 | |||
| 119 | loop { | ||
| 120 | let n = match socket.read(&mut buf).await { | ||
| 121 | Ok(0) => { | ||
| 122 | warn!("read EOF"); | ||
| 123 | break; | ||
| 124 | } | ||
| 125 | Ok(n) => n, | ||
| 126 | Err(e) => { | ||
| 127 | warn!("read error: {:?}", e); | ||
| 128 | break; | ||
| 129 | } | ||
| 130 | }; | ||
| 131 | |||
| 132 | info!("rxd {:02x}", &buf[..n]); | ||
| 133 | |||
| 134 | match socket.write_all(&buf[..n]).await { | ||
| 135 | Ok(()) => {} | ||
| 136 | Err(e) => { | ||
| 137 | warn!("write error: {:?}", e); | ||
| 138 | break; | ||
| 139 | } | ||
| 140 | }; | ||
| 141 | } | ||
| 142 | } | ||
| 143 | } | ||
diff --git a/examples/nrf5340/.cargo/config.toml b/examples/nrf5340/.cargo/config.toml new file mode 100644 index 000000000..4c3cf3d32 --- /dev/null +++ b/examples/nrf5340/.cargo/config.toml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||
| 2 | # replace nRF5340_xxAA with your chip as listed in `probe-rs chip list` | ||
| 3 | runner = "probe-rs run --chip nRF5340_xxAA" | ||
| 4 | |||
| 5 | [build] | ||
| 6 | target = "thumbv8m.main-none-eabihf" | ||
| 7 | |||
| 8 | [env] | ||
| 9 | DEFMT_LOG = "trace" | ||
diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml new file mode 100644 index 000000000..f1d45f336 --- /dev/null +++ b/examples/nrf5340/Cargo.toml | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-nrf5340-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||
| 9 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = [ | ||
| 10 | "defmt", | ||
| 11 | ] } | ||
| 12 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", | ||
| 13 | "nightly", | ||
| 14 | "defmt", | ||
| 15 | "integrated-timers", | ||
| 16 | ] } | ||
| 17 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = [ | ||
| 18 | "defmt", | ||
| 19 | "defmt-timestamp-uptime", | ||
| 20 | ] } | ||
| 21 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = [ | ||
| 22 | "nightly", | ||
| 23 | "unstable-traits", | ||
| 24 | "defmt", | ||
| 25 | "nrf5340-app-s", | ||
| 26 | "time-driver-rtc1", | ||
| 27 | "gpiote", | ||
| 28 | "unstable-pac", | ||
| 29 | ] } | ||
| 30 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = [ | ||
| 31 | "nightly", | ||
| 32 | "defmt", | ||
| 33 | "tcp", | ||
| 34 | "dhcpv4", | ||
| 35 | "medium-ethernet", | ||
| 36 | ] } | ||
| 37 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = [ | ||
| 38 | "defmt", | ||
| 39 | ] } | ||
| 40 | embedded-io = { version = "0.4.0", features = [ "async" ]} | ||
| 41 | |||
| 42 | defmt = "0.3" | ||
| 43 | defmt-rtt = "0.4" | ||
| 44 | |||
| 45 | static_cell = { version = "1.1", features = ["nightly"]} | ||
| 46 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 47 | cortex-m-rt = "0.7.0" | ||
| 48 | panic-probe = { version = "0.3", features = ["print-defmt"] } | ||
| 49 | futures = { version = "0.3.17", default-features = false, features = [ | ||
| 50 | "async-await", | ||
| 51 | ] } | ||
| 52 | rand = { version = "0.8.4", default-features = false } | ||
| 53 | embedded-storage = "0.3.0" | ||
| 54 | usbd-hid = "0.6.0" | ||
| 55 | serde = { version = "1.0.136", default-features = false } | ||
diff --git a/examples/nrf5340/build.rs b/examples/nrf5340/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/nrf5340/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/nrf5340/memory.x b/examples/nrf5340/memory.x new file mode 100644 index 000000000..a122dc24a --- /dev/null +++ b/examples/nrf5340/memory.x | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | /* NOTE 1 K = 1 KiBi = 1024 bytes */ | ||
| 4 | /* These values correspond to the NRF5340 */ | ||
| 5 | FLASH : ORIGIN = 0x00000000, LENGTH = 1024K | ||
| 6 | RAM : ORIGIN = 0x20000000, LENGTH = 256K | ||
| 7 | } | ||
diff --git a/examples/nrf5340/src/bin/blinky.rs b/examples/nrf5340/src/bin/blinky.rs new file mode 100644 index 000000000..3422cedf0 --- /dev/null +++ b/examples/nrf5340/src/bin/blinky.rs | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_nrf::gpio::{Level, Output, OutputDrive}; | ||
| 7 | use embassy_time::{Duration, 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.P0_28, Level::Low, OutputDrive::Standard); | ||
| 14 | |||
| 15 | loop { | ||
| 16 | led.set_high(); | ||
| 17 | Timer::after(Duration::from_millis(300)).await; | ||
| 18 | led.set_low(); | ||
| 19 | Timer::after(Duration::from_millis(300)).await; | ||
| 20 | } | ||
| 21 | } | ||
diff --git a/examples/nrf5340/src/bin/gpiote_channel.rs b/examples/nrf5340/src/bin/gpiote_channel.rs new file mode 100644 index 000000000..ceab1194a --- /dev/null +++ b/examples/nrf5340/src/bin/gpiote_channel.rs | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::info; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::gpio::{Input, Pull}; | ||
| 8 | use embassy_nrf::gpiote::{InputChannel, InputChannelPolarity}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_nrf::init(Default::default()); | ||
| 14 | info!("Starting!"); | ||
| 15 | |||
| 16 | let ch1 = InputChannel::new( | ||
| 17 | p.GPIOTE_CH0, | ||
| 18 | Input::new(p.P0_23, Pull::Up), | ||
| 19 | InputChannelPolarity::HiToLo, | ||
| 20 | ); | ||
| 21 | let ch2 = InputChannel::new( | ||
| 22 | p.GPIOTE_CH1, | ||
| 23 | Input::new(p.P0_24, Pull::Up), | ||
| 24 | InputChannelPolarity::LoToHi, | ||
| 25 | ); | ||
| 26 | let ch3 = InputChannel::new( | ||
| 27 | p.GPIOTE_CH2, | ||
| 28 | Input::new(p.P0_08, Pull::Up), | ||
| 29 | InputChannelPolarity::Toggle, | ||
| 30 | ); | ||
| 31 | let ch4 = InputChannel::new( | ||
| 32 | p.GPIOTE_CH3, | ||
| 33 | Input::new(p.P0_09, Pull::Up), | ||
| 34 | InputChannelPolarity::Toggle, | ||
| 35 | ); | ||
| 36 | |||
| 37 | let button1 = async { | ||
| 38 | loop { | ||
| 39 | ch1.wait().await; | ||
| 40 | info!("Button 1 pressed") | ||
| 41 | } | ||
| 42 | }; | ||
| 43 | |||
| 44 | let button2 = async { | ||
| 45 | loop { | ||
| 46 | ch2.wait().await; | ||
| 47 | info!("Button 2 released") | ||
| 48 | } | ||
| 49 | }; | ||
| 50 | |||
| 51 | let button3 = async { | ||
| 52 | loop { | ||
| 53 | ch3.wait().await; | ||
| 54 | info!("Button 3 toggled") | ||
| 55 | } | ||
| 56 | }; | ||
| 57 | |||
| 58 | let button4 = async { | ||
| 59 | loop { | ||
| 60 | ch4.wait().await; | ||
| 61 | info!("Button 4 toggled") | ||
| 62 | } | ||
| 63 | }; | ||
| 64 | |||
| 65 | futures::join!(button1, button2, button3, button4); | ||
| 66 | } | ||
diff --git a/examples/nrf/src/bin/uart_idle.rs b/examples/nrf5340/src/bin/uart.rs index 09ec624c0..d68539702 100644 --- a/examples/nrf/src/bin/uart_idle.rs +++ b/examples/nrf5340/src/bin/uart.rs | |||
| @@ -4,9 +4,14 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_nrf::{interrupt, uarte}; | 7 | use embassy_nrf::peripherals::SERIAL0; |
| 8 | use embassy_nrf::{bind_interrupts, uarte}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 11 | bind_interrupts!(struct Irqs { | ||
| 12 | SERIAL0 => uarte::InterruptHandler<SERIAL0>; | ||
| 13 | }); | ||
| 14 | |||
| 10 | #[embassy_executor::main] | 15 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 16 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_nrf::init(Default::default()); | 17 | let p = embassy_nrf::init(Default::default()); |
| @@ -14,8 +19,7 @@ async fn main(_spawner: Spawner) { | |||
| 14 | config.parity = uarte::Parity::EXCLUDED; | 19 | config.parity = uarte::Parity::EXCLUDED; |
| 15 | config.baudrate = uarte::Baudrate::BAUD115200; | 20 | config.baudrate = uarte::Baudrate::BAUD115200; |
| 16 | 21 | ||
| 17 | let irq = interrupt::take!(UARTE0_UART0); | 22 | let mut uart = uarte::Uarte::new(p.SERIAL0, Irqs, p.P1_00, p.P1_01, config); |
| 18 | let mut uart = uarte::UarteWithIdle::new(p.UARTE0, p.TIMER0, p.PPI_CH0, p.PPI_CH1, irq, p.P0_08, p.P0_06, config); | ||
| 19 | 23 | ||
| 20 | info!("uarte initialized!"); | 24 | info!("uarte initialized!"); |
| 21 | 25 | ||
| @@ -28,7 +32,8 @@ async fn main(_spawner: Spawner) { | |||
| 28 | 32 | ||
| 29 | loop { | 33 | loop { |
| 30 | info!("reading..."); | 34 | info!("reading..."); |
| 31 | let n = unwrap!(uart.read_until_idle(&mut buf).await); | 35 | unwrap!(uart.read(&mut buf).await); |
| 32 | info!("got {} bytes", n); | 36 | info!("writing..."); |
| 37 | unwrap!(uart.write(&buf).await); | ||
| 33 | } | 38 | } |
| 34 | } | 39 | } |
diff --git a/examples/rp/.cargo/config.toml b/examples/rp/.cargo/config.toml index 3d6051389..3d7d61740 100644 --- a/examples/rp/.cargo/config.toml +++ b/examples/rp/.cargo/config.toml | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] |
| 2 | runner = "probe-run --chip RP2040" | 2 | runner = "probe-rs run --chip RP2040" |
| 3 | 3 | ||
| 4 | [build] | 4 | [build] |
| 5 | target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ | 5 | target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ |
| 6 | 6 | ||
| 7 | [env] | 7 | [env] |
| 8 | DEFMT_LOG = "trace" | 8 | DEFMT_LOG = "debug" |
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index d804a660b..c812cb3ee 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml | |||
| @@ -2,18 +2,34 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-rp-examples" | 3 | name = "embassy-rp-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | 7 | ||
| 7 | [dependencies] | 8 | [dependencies] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 9 | embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 10 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 11 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } |
| 11 | embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] } | 12 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } |
| 13 | embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "critical-section-impl"] } | ||
| 14 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 15 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } | ||
| 16 | embassy-net-w5500 = { version = "0.1.0", path = "../../embassy-net-w5500", features = ["defmt"] } | ||
| 17 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||
| 18 | embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } | ||
| 19 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"] } | ||
| 20 | lora-phy = { version = "1" } | ||
| 21 | lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"] } | ||
| 22 | lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"] } | ||
| 23 | cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] } | ||
| 24 | cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] } | ||
| 12 | 25 | ||
| 13 | defmt = "0.3" | 26 | defmt = "0.3" |
| 14 | defmt-rtt = "0.3" | 27 | defmt-rtt = "0.4" |
| 28 | fixed = "1.23.1" | ||
| 29 | fixed-macro = "1.2" | ||
| 15 | 30 | ||
| 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 31 | #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 32 | cortex-m = { version = "0.7.6", features = ["inline-asm"] } | ||
| 17 | cortex-m-rt = "0.7.0" | 33 | cortex-m-rt = "0.7.0" |
| 18 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 34 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 19 | futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } | 35 | futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } |
| @@ -22,6 +38,22 @@ embedded-graphics = "0.7.1" | |||
| 22 | st7789 = "0.6.1" | 38 | st7789 = "0.6.1" |
| 23 | display-interface = "0.4.1" | 39 | display-interface = "0.4.1" |
| 24 | byte-slice-cast = { version = "1.2.0", default-features = false } | 40 | byte-slice-cast = { version = "1.2.0", default-features = false } |
| 41 | smart-leds = "0.3.0" | ||
| 42 | heapless = "0.7.15" | ||
| 43 | usbd-hid = "0.6.1" | ||
| 25 | 44 | ||
| 26 | embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } | 45 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } |
| 27 | embedded-hal-async = { version = "0.1.0-alpha.1" } | 46 | embedded-hal-async = "0.2.0-alpha.2" |
| 47 | embedded-io = { version = "0.4.0", features = ["async", "defmt"] } | ||
| 48 | embedded-storage = { version = "0.3" } | ||
| 49 | static_cell = { version = "1.1", features = ["nightly"]} | ||
| 50 | log = "0.4" | ||
| 51 | pio-proc = "0.2" | ||
| 52 | pio = "0.2.1" | ||
| 53 | rand = { version = "0.8.5", default-features = false } | ||
| 54 | |||
| 55 | [profile.release] | ||
| 56 | debug = true | ||
| 57 | |||
| 58 | [patch.crates-io] | ||
| 59 | lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } | ||
diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs new file mode 100644 index 000000000..81a8b8340 --- /dev/null +++ b/examples/rp/src/bin/adc.rs | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | //! This example test the ADC (Analog to Digital Conversion) of the RS2040 pin 26, 27 and 28. | ||
| 2 | //! It also reads the temperature sensor in the chip. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_rp::adc::{Adc, Config, InterruptHandler, Pin}; | ||
| 11 | use embassy_rp::bind_interrupts; | ||
| 12 | use embassy_rp::gpio::Pull; | ||
| 13 | use embassy_time::{Duration, Timer}; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | bind_interrupts!(struct Irqs { | ||
| 17 | ADC_IRQ_FIFO => InterruptHandler; | ||
| 18 | }); | ||
| 19 | |||
| 20 | #[embassy_executor::main] | ||
| 21 | async fn main(_spawner: Spawner) { | ||
| 22 | let p = embassy_rp::init(Default::default()); | ||
| 23 | let mut adc = Adc::new(p.ADC, Irqs, Config::default()); | ||
| 24 | |||
| 25 | let mut p26 = Pin::new(p.PIN_26, Pull::None); | ||
| 26 | let mut p27 = Pin::new(p.PIN_27, Pull::None); | ||
| 27 | let mut p28 = Pin::new(p.PIN_28, Pull::None); | ||
| 28 | |||
| 29 | loop { | ||
| 30 | let level = adc.read(&mut p26).await.unwrap(); | ||
| 31 | info!("Pin 26 ADC: {}", level); | ||
| 32 | let level = adc.read(&mut p27).await.unwrap(); | ||
| 33 | info!("Pin 27 ADC: {}", level); | ||
| 34 | let level = adc.read(&mut p28).await.unwrap(); | ||
| 35 | info!("Pin 28 ADC: {}", level); | ||
| 36 | let temp = adc.read_temperature().await.unwrap(); | ||
| 37 | info!("Temp: {} degrees", convert_to_celsius(temp)); | ||
| 38 | Timer::after(Duration::from_secs(1)).await; | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | fn convert_to_celsius(raw_temp: u16) -> f32 { | ||
| 43 | // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet | ||
| 44 | let temp = 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721; | ||
| 45 | let sign = if temp < 0.0 { -1.0 } else { 1.0 }; | ||
| 46 | let rounded_temp_x10: i16 = ((temp * 10.0) + 0.5 * sign) as i16; | ||
| 47 | (rounded_temp_x10 as f32) / 10.0 | ||
| 48 | } | ||
diff --git a/examples/rp/src/bin/blinky.rs b/examples/rp/src/bin/blinky.rs index 7aa36a19f..295b000f3 100644 --- a/examples/rp/src/bin/blinky.rs +++ b/examples/rp/src/bin/blinky.rs | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | //! This example test the RP Pico on board LED. | ||
| 2 | //! | ||
| 3 | //! It does not work with the RP Pico W board. See wifi_blinky.rs. | ||
| 4 | |||
| 1 | #![no_std] | 5 | #![no_std] |
| 2 | #![no_main] | 6 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 7 | #![feature(type_alias_impl_trait)] |
diff --git a/examples/rp/src/bin/button.rs b/examples/rp/src/bin/button.rs index c5422c616..d7aa89410 100644 --- a/examples/rp/src/bin/button.rs +++ b/examples/rp/src/bin/button.rs | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | //! This example uses the RP Pico on board LED to test input pin 28. This is not the button on the board. | ||
| 2 | //! | ||
| 3 | //! It does not work with the RP Pico W board. Use wifi_blinky.rs and add input pin. | ||
| 4 | |||
| 1 | #![no_std] | 5 | #![no_std] |
| 2 | #![no_main] | 6 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 7 | #![feature(type_alias_impl_trait)] |
| @@ -9,9 +13,12 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 9 | #[embassy_executor::main] | 13 | #[embassy_executor::main] |
| 10 | async fn main(_spawner: Spawner) { | 14 | async fn main(_spawner: Spawner) { |
| 11 | let p = embassy_rp::init(Default::default()); | 15 | let p = embassy_rp::init(Default::default()); |
| 12 | let button = Input::new(p.PIN_28, Pull::Up); | ||
| 13 | let mut led = Output::new(p.PIN_25, Level::Low); | 16 | let mut led = Output::new(p.PIN_25, Level::Low); |
| 14 | 17 | ||
| 18 | // Use PIN_28, Pin34 on J0 for RP Pico, as a input. | ||
| 19 | // You need to add your own button. | ||
| 20 | let button = Input::new(p.PIN_28, Pull::Up); | ||
| 21 | |||
| 15 | loop { | 22 | loop { |
| 16 | if button.is_high() { | 23 | if button.is_high() { |
| 17 | led.set_high(); | 24 | led.set_high(); |
diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs new file mode 100644 index 000000000..e81da177b --- /dev/null +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | //! This example shows how you can allow multiple simultaneous TCP connections, by having multiple sockets listening on the same port. | ||
| 2 | //! | ||
| 3 | //! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_futures::yield_now; | ||
| 12 | use embassy_net::{Stack, StackResources}; | ||
| 13 | use embassy_net_w5500::*; | ||
| 14 | use embassy_rp::clocks::RoscRng; | ||
| 15 | use embassy_rp::gpio::{Input, Level, Output, Pull}; | ||
| 16 | use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; | ||
| 17 | use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; | ||
| 18 | use embassy_time::{Delay, Duration}; | ||
| 19 | use embedded_hal_async::spi::ExclusiveDevice; | ||
| 20 | use embedded_io::asynch::Write; | ||
| 21 | use rand::RngCore; | ||
| 22 | use static_cell::make_static; | ||
| 23 | use {defmt_rtt as _, panic_probe as _}; | ||
| 24 | |||
| 25 | #[embassy_executor::task] | ||
| 26 | async fn ethernet_task( | ||
| 27 | runner: Runner< | ||
| 28 | 'static, | ||
| 29 | ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static, PIN_17>, Delay>, | ||
| 30 | Input<'static, PIN_21>, | ||
| 31 | Output<'static, PIN_20>, | ||
| 32 | >, | ||
| 33 | ) -> ! { | ||
| 34 | runner.run().await | ||
| 35 | } | ||
| 36 | |||
| 37 | #[embassy_executor::task] | ||
| 38 | async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { | ||
| 39 | stack.run().await | ||
| 40 | } | ||
| 41 | |||
| 42 | #[embassy_executor::main] | ||
| 43 | async fn main(spawner: Spawner) { | ||
| 44 | let p = embassy_rp::init(Default::default()); | ||
| 45 | let mut rng = RoscRng; | ||
| 46 | |||
| 47 | let mut spi_cfg = SpiConfig::default(); | ||
| 48 | spi_cfg.frequency = 50_000_000; | ||
| 49 | let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); | ||
| 50 | let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); | ||
| 51 | let cs = Output::new(p.PIN_17, Level::High); | ||
| 52 | let w5500_int = Input::new(p.PIN_21, Pull::Up); | ||
| 53 | let w5500_reset = Output::new(p.PIN_20, Level::High); | ||
| 54 | |||
| 55 | let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; | ||
| 56 | let state = make_static!(State::<8, 8>::new()); | ||
| 57 | let (device, runner) = embassy_net_w5500::new( | ||
| 58 | mac_addr, | ||
| 59 | state, | ||
| 60 | ExclusiveDevice::new(spi, cs, Delay), | ||
| 61 | w5500_int, | ||
| 62 | w5500_reset, | ||
| 63 | ) | ||
| 64 | .await; | ||
| 65 | unwrap!(spawner.spawn(ethernet_task(runner))); | ||
| 66 | |||
| 67 | // Generate random seed | ||
| 68 | let seed = rng.next_u64(); | ||
| 69 | |||
| 70 | // Init network stack | ||
| 71 | let stack = &*make_static!(Stack::new( | ||
| 72 | device, | ||
| 73 | embassy_net::Config::dhcpv4(Default::default()), | ||
| 74 | make_static!(StackResources::<3>::new()), | ||
| 75 | seed | ||
| 76 | )); | ||
| 77 | |||
| 78 | // Launch network task | ||
| 79 | unwrap!(spawner.spawn(net_task(&stack))); | ||
| 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 | // Create two sockets listening to the same port, to handle simultaneous connections | ||
| 87 | unwrap!(spawner.spawn(listen_task(&stack, 0, 1234))); | ||
| 88 | unwrap!(spawner.spawn(listen_task(&stack, 1, 1234))); | ||
| 89 | } | ||
| 90 | |||
| 91 | #[embassy_executor::task(pool_size = 2)] | ||
| 92 | async fn listen_task(stack: &'static Stack<Device<'static>>, id: u8, port: u16) { | ||
| 93 | let mut rx_buffer = [0; 4096]; | ||
| 94 | let mut tx_buffer = [0; 4096]; | ||
| 95 | let mut buf = [0; 4096]; | ||
| 96 | loop { | ||
| 97 | let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 98 | socket.set_timeout(Some(Duration::from_secs(10))); | ||
| 99 | |||
| 100 | info!("SOCKET {}: Listening on TCP:{}...", id, port); | ||
| 101 | if let Err(e) = socket.accept(port).await { | ||
| 102 | warn!("accept error: {:?}", e); | ||
| 103 | continue; | ||
| 104 | } | ||
| 105 | info!("SOCKET {}: Received connection from {:?}", id, socket.remote_endpoint()); | ||
| 106 | |||
| 107 | loop { | ||
| 108 | let n = match socket.read(&mut buf).await { | ||
| 109 | Ok(0) => { | ||
| 110 | warn!("read EOF"); | ||
| 111 | break; | ||
| 112 | } | ||
| 113 | Ok(n) => n, | ||
| 114 | Err(e) => { | ||
| 115 | warn!("SOCKET {}: {:?}", id, e); | ||
| 116 | break; | ||
| 117 | } | ||
| 118 | }; | ||
| 119 | info!("SOCKET {}: rxd {}", id, core::str::from_utf8(&buf[..n]).unwrap()); | ||
| 120 | |||
| 121 | if let Err(e) = socket.write_all(&buf[..n]).await { | ||
| 122 | warn!("write error: {:?}", e); | ||
| 123 | break; | ||
| 124 | } | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { | ||
| 130 | loop { | ||
| 131 | if let Some(config) = stack.config_v4() { | ||
| 132 | return config.clone(); | ||
| 133 | } | ||
| 134 | yield_now().await; | ||
| 135 | } | ||
| 136 | } | ||
diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs new file mode 100644 index 000000000..9dd7ae973 --- /dev/null +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs | |||
| @@ -0,0 +1,124 @@ | |||
| 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 | //! | ||
| 3 | //! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use core::str::FromStr; | ||
| 10 | |||
| 11 | use defmt::*; | ||
| 12 | use embassy_executor::Spawner; | ||
| 13 | use embassy_futures::yield_now; | ||
| 14 | use embassy_net::{Stack, StackResources}; | ||
| 15 | use embassy_net_w5500::*; | ||
| 16 | use embassy_rp::clocks::RoscRng; | ||
| 17 | use embassy_rp::gpio::{Input, Level, Output, Pull}; | ||
| 18 | use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; | ||
| 19 | use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; | ||
| 20 | use embassy_time::{Delay, Duration, Timer}; | ||
| 21 | use embedded_hal_async::spi::ExclusiveDevice; | ||
| 22 | use embedded_io::asynch::Write; | ||
| 23 | use rand::RngCore; | ||
| 24 | use static_cell::make_static; | ||
| 25 | use {defmt_rtt as _, panic_probe as _}; | ||
| 26 | |||
| 27 | #[embassy_executor::task] | ||
| 28 | async fn ethernet_task( | ||
| 29 | runner: Runner< | ||
| 30 | 'static, | ||
| 31 | ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static, PIN_17>, Delay>, | ||
| 32 | Input<'static, PIN_21>, | ||
| 33 | Output<'static, PIN_20>, | ||
| 34 | >, | ||
| 35 | ) -> ! { | ||
| 36 | runner.run().await | ||
| 37 | } | ||
| 38 | |||
| 39 | #[embassy_executor::task] | ||
| 40 | async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { | ||
| 41 | stack.run().await | ||
| 42 | } | ||
| 43 | |||
| 44 | #[embassy_executor::main] | ||
| 45 | async fn main(spawner: Spawner) { | ||
| 46 | let p = embassy_rp::init(Default::default()); | ||
| 47 | let mut rng = RoscRng; | ||
| 48 | let mut led = Output::new(p.PIN_25, Level::Low); | ||
| 49 | |||
| 50 | let mut spi_cfg = SpiConfig::default(); | ||
| 51 | spi_cfg.frequency = 50_000_000; | ||
| 52 | let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); | ||
| 53 | let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); | ||
| 54 | let cs = Output::new(p.PIN_17, Level::High); | ||
| 55 | let w5500_int = Input::new(p.PIN_21, Pull::Up); | ||
| 56 | let w5500_reset = Output::new(p.PIN_20, Level::High); | ||
| 57 | |||
| 58 | let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; | ||
| 59 | let state = make_static!(State::<8, 8>::new()); | ||
| 60 | let (device, runner) = embassy_net_w5500::new( | ||
| 61 | mac_addr, | ||
| 62 | state, | ||
| 63 | ExclusiveDevice::new(spi, cs, Delay), | ||
| 64 | w5500_int, | ||
| 65 | w5500_reset, | ||
| 66 | ) | ||
| 67 | .await; | ||
| 68 | unwrap!(spawner.spawn(ethernet_task(runner))); | ||
| 69 | |||
| 70 | // Generate random seed | ||
| 71 | let seed = rng.next_u64(); | ||
| 72 | |||
| 73 | // Init network stack | ||
| 74 | let stack = &*make_static!(Stack::new( | ||
| 75 | device, | ||
| 76 | embassy_net::Config::dhcpv4(Default::default()), | ||
| 77 | make_static!(StackResources::<2>::new()), | ||
| 78 | seed | ||
| 79 | )); | ||
| 80 | |||
| 81 | // Launch network task | ||
| 82 | unwrap!(spawner.spawn(net_task(&stack))); | ||
| 83 | |||
| 84 | info!("Waiting for DHCP..."); | ||
| 85 | let cfg = wait_for_config(stack).await; | ||
| 86 | let local_addr = cfg.address.address(); | ||
| 87 | info!("IP address: {:?}", local_addr); | ||
| 88 | |||
| 89 | let mut rx_buffer = [0; 4096]; | ||
| 90 | let mut tx_buffer = [0; 4096]; | ||
| 91 | loop { | ||
| 92 | let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 93 | socket.set_timeout(Some(Duration::from_secs(10))); | ||
| 94 | |||
| 95 | led.set_low(); | ||
| 96 | info!("Connecting..."); | ||
| 97 | let host_addr = embassy_net::Ipv4Address::from_str("192.168.1.110").unwrap(); | ||
| 98 | if let Err(e) = socket.connect((host_addr, 1234)).await { | ||
| 99 | warn!("connect error: {:?}", e); | ||
| 100 | continue; | ||
| 101 | } | ||
| 102 | info!("Connected to {:?}", socket.remote_endpoint()); | ||
| 103 | led.set_high(); | ||
| 104 | |||
| 105 | let msg = b"Hello world!\n"; | ||
| 106 | loop { | ||
| 107 | if let Err(e) = socket.write_all(msg).await { | ||
| 108 | warn!("write error: {:?}", e); | ||
| 109 | break; | ||
| 110 | } | ||
| 111 | info!("txd: {}", core::str::from_utf8(msg).unwrap()); | ||
| 112 | Timer::after(Duration::from_secs(1)).await; | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { | ||
| 118 | loop { | ||
| 119 | if let Some(config) = stack.config_v4() { | ||
| 120 | return config.clone(); | ||
| 121 | } | ||
| 122 | yield_now().await; | ||
| 123 | } | ||
| 124 | } | ||
diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs new file mode 100644 index 000000000..db21c2b6f --- /dev/null +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs | |||
| @@ -0,0 +1,132 @@ | |||
| 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. | ||
| 3 | //! | ||
| 4 | //! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. | ||
| 5 | |||
| 6 | #![no_std] | ||
| 7 | #![no_main] | ||
| 8 | #![feature(type_alias_impl_trait)] | ||
| 9 | |||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_futures::yield_now; | ||
| 13 | use embassy_net::{Stack, StackResources}; | ||
| 14 | use embassy_net_w5500::*; | ||
| 15 | use embassy_rp::clocks::RoscRng; | ||
| 16 | use embassy_rp::gpio::{Input, Level, Output, Pull}; | ||
| 17 | use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; | ||
| 18 | use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; | ||
| 19 | use embassy_time::{Delay, Duration}; | ||
| 20 | use embedded_hal_async::spi::ExclusiveDevice; | ||
| 21 | use embedded_io::asynch::Write; | ||
| 22 | use rand::RngCore; | ||
| 23 | use static_cell::make_static; | ||
| 24 | use {defmt_rtt as _, panic_probe as _}; | ||
| 25 | #[embassy_executor::task] | ||
| 26 | async fn ethernet_task( | ||
| 27 | runner: Runner< | ||
| 28 | 'static, | ||
| 29 | ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static, PIN_17>, Delay>, | ||
| 30 | Input<'static, PIN_21>, | ||
| 31 | Output<'static, PIN_20>, | ||
| 32 | >, | ||
| 33 | ) -> ! { | ||
| 34 | runner.run().await | ||
| 35 | } | ||
| 36 | |||
| 37 | #[embassy_executor::task] | ||
| 38 | async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { | ||
| 39 | stack.run().await | ||
| 40 | } | ||
| 41 | |||
| 42 | #[embassy_executor::main] | ||
| 43 | async fn main(spawner: Spawner) { | ||
| 44 | let p = embassy_rp::init(Default::default()); | ||
| 45 | let mut rng = RoscRng; | ||
| 46 | let mut led = Output::new(p.PIN_25, Level::Low); | ||
| 47 | |||
| 48 | let mut spi_cfg = SpiConfig::default(); | ||
| 49 | spi_cfg.frequency = 50_000_000; | ||
| 50 | let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); | ||
| 51 | let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); | ||
| 52 | let cs = Output::new(p.PIN_17, Level::High); | ||
| 53 | let w5500_int = Input::new(p.PIN_21, Pull::Up); | ||
| 54 | let w5500_reset = Output::new(p.PIN_20, Level::High); | ||
| 55 | |||
| 56 | let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; | ||
| 57 | let state = make_static!(State::<8, 8>::new()); | ||
| 58 | let (device, runner) = embassy_net_w5500::new( | ||
| 59 | mac_addr, | ||
| 60 | state, | ||
| 61 | ExclusiveDevice::new(spi, cs, Delay), | ||
| 62 | w5500_int, | ||
| 63 | w5500_reset, | ||
| 64 | ) | ||
| 65 | .await; | ||
| 66 | unwrap!(spawner.spawn(ethernet_task(runner))); | ||
| 67 | |||
| 68 | // Generate random seed | ||
| 69 | let seed = rng.next_u64(); | ||
| 70 | |||
| 71 | // Init network stack | ||
| 72 | let stack = &*make_static!(Stack::new( | ||
| 73 | device, | ||
| 74 | embassy_net::Config::dhcpv4(Default::default()), | ||
| 75 | make_static!(StackResources::<2>::new()), | ||
| 76 | seed | ||
| 77 | )); | ||
| 78 | |||
| 79 | // Launch network task | ||
| 80 | unwrap!(spawner.spawn(net_task(&stack))); | ||
| 81 | |||
| 82 | info!("Waiting for DHCP..."); | ||
| 83 | let cfg = wait_for_config(stack).await; | ||
| 84 | let local_addr = cfg.address.address(); | ||
| 85 | info!("IP address: {:?}", local_addr); | ||
| 86 | |||
| 87 | let mut rx_buffer = [0; 4096]; | ||
| 88 | let mut tx_buffer = [0; 4096]; | ||
| 89 | let mut buf = [0; 4096]; | ||
| 90 | loop { | ||
| 91 | let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 92 | socket.set_timeout(Some(Duration::from_secs(10))); | ||
| 93 | |||
| 94 | led.set_low(); | ||
| 95 | info!("Listening on TCP:1234..."); | ||
| 96 | if let Err(e) = socket.accept(1234).await { | ||
| 97 | warn!("accept error: {:?}", e); | ||
| 98 | continue; | ||
| 99 | } | ||
| 100 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 101 | led.set_high(); | ||
| 102 | |||
| 103 | loop { | ||
| 104 | let n = match socket.read(&mut buf).await { | ||
| 105 | Ok(0) => { | ||
| 106 | warn!("read EOF"); | ||
| 107 | break; | ||
| 108 | } | ||
| 109 | Ok(n) => n, | ||
| 110 | Err(e) => { | ||
| 111 | warn!("{:?}", e); | ||
| 112 | break; | ||
| 113 | } | ||
| 114 | }; | ||
| 115 | info!("rxd {}", core::str::from_utf8(&buf[..n]).unwrap()); | ||
| 116 | |||
| 117 | if let Err(e) = socket.write_all(&buf[..n]).await { | ||
| 118 | warn!("write error: {:?}", e); | ||
| 119 | break; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { | ||
| 126 | loop { | ||
| 127 | if let Some(config) = stack.config_v4() { | ||
| 128 | return config.clone(); | ||
| 129 | } | ||
| 130 | yield_now().await; | ||
| 131 | } | ||
| 132 | } | ||
diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs new file mode 100644 index 000000000..038432b17 --- /dev/null +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | //! This example implements a UDP server listening on port 1234 and echoing back the data. | ||
| 2 | //! | ||
| 3 | //! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_futures::yield_now; | ||
| 12 | use embassy_net::udp::{PacketMetadata, UdpSocket}; | ||
| 13 | use embassy_net::{Stack, StackResources}; | ||
| 14 | use embassy_net_w5500::*; | ||
| 15 | use embassy_rp::clocks::RoscRng; | ||
| 16 | use embassy_rp::gpio::{Input, Level, Output, Pull}; | ||
| 17 | use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; | ||
| 18 | use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; | ||
| 19 | use embassy_time::Delay; | ||
| 20 | use embedded_hal_async::spi::ExclusiveDevice; | ||
| 21 | use rand::RngCore; | ||
| 22 | use static_cell::make_static; | ||
| 23 | use {defmt_rtt as _, panic_probe as _}; | ||
| 24 | #[embassy_executor::task] | ||
| 25 | async fn ethernet_task( | ||
| 26 | runner: Runner< | ||
| 27 | 'static, | ||
| 28 | ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static, PIN_17>, Delay>, | ||
| 29 | Input<'static, PIN_21>, | ||
| 30 | Output<'static, PIN_20>, | ||
| 31 | >, | ||
| 32 | ) -> ! { | ||
| 33 | runner.run().await | ||
| 34 | } | ||
| 35 | |||
| 36 | #[embassy_executor::task] | ||
| 37 | async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { | ||
| 38 | stack.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 | let state = make_static!(State::<8, 8>::new()); | ||
| 56 | let (device, runner) = embassy_net_w5500::new( | ||
| 57 | mac_addr, | ||
| 58 | state, | ||
| 59 | ExclusiveDevice::new(spi, cs, Delay), | ||
| 60 | w5500_int, | ||
| 61 | w5500_reset, | ||
| 62 | ) | ||
| 63 | .await; | ||
| 64 | unwrap!(spawner.spawn(ethernet_task(runner))); | ||
| 65 | |||
| 66 | // Generate random seed | ||
| 67 | let seed = rng.next_u64(); | ||
| 68 | |||
| 69 | // Init network stack | ||
| 70 | let stack = &*make_static!(Stack::new( | ||
| 71 | device, | ||
| 72 | embassy_net::Config::dhcpv4(Default::default()), | ||
| 73 | make_static!(StackResources::<2>::new()), | ||
| 74 | seed | ||
| 75 | )); | ||
| 76 | |||
| 77 | // Launch network task | ||
| 78 | unwrap!(spawner.spawn(net_task(&stack))); | ||
| 79 | |||
| 80 | info!("Waiting for DHCP..."); | ||
| 81 | let cfg = wait_for_config(stack).await; | ||
| 82 | let local_addr = cfg.address.address(); | ||
| 83 | info!("IP address: {:?}", local_addr); | ||
| 84 | |||
| 85 | // Then we can use it! | ||
| 86 | let mut rx_buffer = [0; 4096]; | ||
| 87 | let mut tx_buffer = [0; 4096]; | ||
| 88 | let mut rx_meta = [PacketMetadata::EMPTY; 16]; | ||
| 89 | let mut tx_meta = [PacketMetadata::EMPTY; 16]; | ||
| 90 | let mut buf = [0; 4096]; | ||
| 91 | loop { | ||
| 92 | let mut socket = UdpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer); | ||
| 93 | socket.bind(1234).unwrap(); | ||
| 94 | |||
| 95 | loop { | ||
| 96 | let (n, ep) = socket.recv_from(&mut buf).await.unwrap(); | ||
| 97 | if let Ok(s) = core::str::from_utf8(&buf[..n]) { | ||
| 98 | info!("rxd from {}: {}", ep, s); | ||
| 99 | } | ||
| 100 | socket.send_to(&buf[..n], ep).await.unwrap(); | ||
| 101 | } | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { | ||
| 106 | loop { | ||
| 107 | if let Some(config) = stack.config_v4() { | ||
| 108 | return config.clone(); | ||
| 109 | } | ||
| 110 | yield_now().await; | ||
| 111 | } | ||
| 112 | } | ||
diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs new file mode 100644 index 000000000..4c4982acc --- /dev/null +++ b/examples/rp/src/bin/flash.rs | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | //! This example test the flash connected to the RP2040 chip. | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | #![feature(type_alias_impl_trait)] | ||
| 6 | |||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; | ||
| 10 | use embassy_rp::peripherals::FLASH; | ||
| 11 | use embassy_time::{Duration, Timer}; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | const ADDR_OFFSET: u32 = 0x100000; | ||
| 15 | const FLASH_SIZE: usize = 2 * 1024 * 1024; | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | let p = embassy_rp::init(Default::default()); | ||
| 20 | info!("Hello World!"); | ||
| 21 | |||
| 22 | // add some delay to give an attached debug probe time to parse the | ||
| 23 | // defmt RTT header. Reading that header might touch flash memory, which | ||
| 24 | // interferes with flash write operations. | ||
| 25 | // https://github.com/knurling-rs/defmt/pull/683 | ||
| 26 | Timer::after(Duration::from_millis(10)).await; | ||
| 27 | |||
| 28 | let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH); | ||
| 29 | |||
| 30 | // Get JEDEC id | ||
| 31 | let jedec = flash.jedec_id().unwrap(); | ||
| 32 | info!("jedec id: 0x{:x}", jedec); | ||
| 33 | |||
| 34 | // Get unique id | ||
| 35 | let mut uid = [0; 8]; | ||
| 36 | flash.unique_id(&mut uid).unwrap(); | ||
| 37 | info!("unique id: {:?}", uid); | ||
| 38 | |||
| 39 | erase_write_sector(&mut flash, 0x00); | ||
| 40 | |||
| 41 | multiwrite_bytes(&mut flash, ERASE_SIZE as u32); | ||
| 42 | |||
| 43 | loop {} | ||
| 44 | } | ||
| 45 | |||
| 46 | fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { | ||
| 47 | info!(">>>> [multiwrite_bytes]"); | ||
| 48 | let mut read_buf = [0u8; ERASE_SIZE]; | ||
| 49 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); | ||
| 50 | |||
| 51 | info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); | ||
| 52 | info!("Contents start with {=[u8]}", read_buf[0..4]); | ||
| 53 | |||
| 54 | defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); | ||
| 55 | |||
| 56 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); | ||
| 57 | info!("Contents after erase starts with {=[u8]}", read_buf[0..4]); | ||
| 58 | if read_buf.iter().any(|x| *x != 0xFF) { | ||
| 59 | defmt::panic!("unexpected"); | ||
| 60 | } | ||
| 61 | |||
| 62 | defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &[0x01])); | ||
| 63 | defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 1, &[0x02])); | ||
| 64 | defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 2, &[0x03])); | ||
| 65 | defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 3, &[0x04])); | ||
| 66 | |||
| 67 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); | ||
| 68 | info!("Contents after write starts with {=[u8]}", read_buf[0..4]); | ||
| 69 | if &read_buf[0..4] != &[0x01, 0x02, 0x03, 0x04] { | ||
| 70 | defmt::panic!("unexpected"); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { | ||
| 75 | info!(">>>> [erase_write_sector]"); | ||
| 76 | let mut buf = [0u8; ERASE_SIZE]; | ||
| 77 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); | ||
| 78 | |||
| 79 | info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); | ||
| 80 | info!("Contents start with {=[u8]}", buf[0..4]); | ||
| 81 | |||
| 82 | defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); | ||
| 83 | |||
| 84 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); | ||
| 85 | info!("Contents after erase starts with {=[u8]}", buf[0..4]); | ||
| 86 | if buf.iter().any(|x| *x != 0xFF) { | ||
| 87 | defmt::panic!("unexpected"); | ||
| 88 | } | ||
| 89 | |||
| 90 | for b in buf.iter_mut() { | ||
| 91 | *b = 0xDA; | ||
| 92 | } | ||
| 93 | |||
| 94 | defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &buf)); | ||
| 95 | |||
| 96 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); | ||
| 97 | info!("Contents after write starts with {=[u8]}", buf[0..4]); | ||
| 98 | if buf.iter().any(|x| *x != 0xDA) { | ||
| 99 | defmt::panic!("unexpected"); | ||
| 100 | } | ||
| 101 | } | ||
diff --git a/examples/rp/src/bin/gpio_async.rs b/examples/rp/src/bin/gpio_async.rs index 52d13a9d5..bf58044d5 100644 --- a/examples/rp/src/bin/gpio_async.rs +++ b/examples/rp/src/bin/gpio_async.rs | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | //! This example shows how async gpio can be used with a RP2040. | ||
| 2 | //! | ||
| 3 | //! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs. | ||
| 4 | |||
| 1 | #![no_std] | 5 | #![no_std] |
| 2 | #![no_main] | 6 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 7 | #![feature(type_alias_impl_trait)] |
| @@ -9,8 +13,6 @@ use embassy_time::{Duration, Timer}; | |||
| 9 | use gpio::{Input, Level, Output, Pull}; | 13 | use gpio::{Input, Level, Output, Pull}; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 15 | ||
| 12 | /// This example shows how async gpio can be used with a RP2040. | ||
| 13 | /// | ||
| 14 | /// It requires an external signal to be manually triggered on PIN 16. For | 16 | /// It requires an external signal to be manually triggered on PIN 16. For |
| 15 | /// example, this could be accomplished using an external power source with a | 17 | /// example, this could be accomplished using an external power source with a |
| 16 | /// button so that it is possible to toggle the signal from low to high. | 18 | /// button so that it is possible to toggle the signal from low to high. |
diff --git a/examples/rp/src/bin/gpout.rs b/examples/rp/src/bin/gpout.rs new file mode 100644 index 000000000..0a3b5fa98 --- /dev/null +++ b/examples/rp/src/bin/gpout.rs | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | //! This example shows how GPOUT (General purpose clock outputs) can toggle a output pin. | ||
| 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 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_rp::clocks; | ||
| 12 | use embassy_time::{Duration, Timer}; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | #[embassy_executor::main] | ||
| 16 | async fn main(_spawner: Spawner) { | ||
| 17 | let p = embassy_rp::init(Default::default()); | ||
| 18 | |||
| 19 | let gpout3 = clocks::Gpout::new(p.PIN_25); | ||
| 20 | gpout3.set_div(1000, 0); | ||
| 21 | gpout3.enable(); | ||
| 22 | |||
| 23 | loop { | ||
| 24 | gpout3.set_src(clocks::GpoutSrc::Sys); | ||
| 25 | info!( | ||
| 26 | "Pin 25 is now outputing CLK_SYS/1000, should be toggling at {}", | ||
| 27 | gpout3.get_freq() | ||
| 28 | ); | ||
| 29 | Timer::after(Duration::from_secs(2)).await; | ||
| 30 | |||
| 31 | gpout3.set_src(clocks::GpoutSrc::Ref); | ||
| 32 | info!( | ||
| 33 | "Pin 25 is now outputing CLK_REF/1000, should be toggling at {}", | ||
| 34 | gpout3.get_freq() | ||
| 35 | ); | ||
| 36 | Timer::after(Duration::from_secs(2)).await; | ||
| 37 | } | ||
| 38 | } | ||
diff --git a/examples/rp/src/bin/i2c_async.rs b/examples/rp/src/bin/i2c_async.rs new file mode 100644 index 000000000..93224bc43 --- /dev/null +++ b/examples/rp/src/bin/i2c_async.rs | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | //! This example shows how to communicate asynchronous using i2c with external chips. | ||
| 2 | //! | ||
| 3 | //! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip. | ||
| 4 | //! (https://www.microchip.com/en-us/product/mcp23017) | ||
| 5 | |||
| 6 | #![no_std] | ||
| 7 | #![no_main] | ||
| 8 | #![feature(type_alias_impl_trait)] | ||
| 9 | |||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_rp::bind_interrupts; | ||
| 13 | use embassy_rp::i2c::{self, Config, InterruptHandler}; | ||
| 14 | use embassy_rp::peripherals::I2C1; | ||
| 15 | use embassy_time::{Duration, Timer}; | ||
| 16 | use embedded_hal_async::i2c::I2c; | ||
| 17 | use {defmt_rtt as _, panic_probe as _}; | ||
| 18 | |||
| 19 | bind_interrupts!(struct Irqs { | ||
| 20 | I2C1_IRQ => InterruptHandler<I2C1>; | ||
| 21 | }); | ||
| 22 | |||
| 23 | #[allow(dead_code)] | ||
| 24 | mod mcp23017 { | ||
| 25 | pub const ADDR: u8 = 0x20; // default addr | ||
| 26 | |||
| 27 | macro_rules! mcpregs { | ||
| 28 | ($($name:ident : $val:expr),* $(,)?) => { | ||
| 29 | $( | ||
| 30 | pub const $name: u8 = $val; | ||
| 31 | )* | ||
| 32 | |||
| 33 | pub fn regname(reg: u8) -> &'static str { | ||
| 34 | match reg { | ||
| 35 | $( | ||
| 36 | $val => stringify!($name), | ||
| 37 | )* | ||
| 38 | _ => panic!("bad reg"), | ||
| 39 | } | ||
| 40 | } | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | // These are correct for IOCON.BANK=0 | ||
| 45 | mcpregs! { | ||
| 46 | IODIRA: 0x00, | ||
| 47 | IPOLA: 0x02, | ||
| 48 | GPINTENA: 0x04, | ||
| 49 | DEFVALA: 0x06, | ||
| 50 | INTCONA: 0x08, | ||
| 51 | IOCONA: 0x0A, | ||
| 52 | GPPUA: 0x0C, | ||
| 53 | INTFA: 0x0E, | ||
| 54 | INTCAPA: 0x10, | ||
| 55 | GPIOA: 0x12, | ||
| 56 | OLATA: 0x14, | ||
| 57 | IODIRB: 0x01, | ||
| 58 | IPOLB: 0x03, | ||
| 59 | GPINTENB: 0x05, | ||
| 60 | DEFVALB: 0x07, | ||
| 61 | INTCONB: 0x09, | ||
| 62 | IOCONB: 0x0B, | ||
| 63 | GPPUB: 0x0D, | ||
| 64 | INTFB: 0x0F, | ||
| 65 | INTCAPB: 0x11, | ||
| 66 | GPIOB: 0x13, | ||
| 67 | OLATB: 0x15, | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | #[embassy_executor::main] | ||
| 72 | async fn main(_spawner: Spawner) { | ||
| 73 | let p = embassy_rp::init(Default::default()); | ||
| 74 | |||
| 75 | let sda = p.PIN_14; | ||
| 76 | let scl = p.PIN_15; | ||
| 77 | |||
| 78 | info!("set up i2c "); | ||
| 79 | let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, Config::default()); | ||
| 80 | |||
| 81 | use mcp23017::*; | ||
| 82 | |||
| 83 | info!("init mcp23017 config for IxpandO"); | ||
| 84 | // init - a outputs, b inputs | ||
| 85 | i2c.write(ADDR, &[IODIRA, 0x00]).await.unwrap(); | ||
| 86 | i2c.write(ADDR, &[IODIRB, 0xff]).await.unwrap(); | ||
| 87 | i2c.write(ADDR, &[GPPUB, 0xff]).await.unwrap(); // pullups | ||
| 88 | |||
| 89 | let mut val = 1; | ||
| 90 | loop { | ||
| 91 | let mut portb = [0]; | ||
| 92 | |||
| 93 | i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).await.unwrap(); | ||
| 94 | info!("portb = {:02x}", portb[0]); | ||
| 95 | i2c.write(mcp23017::ADDR, &[GPIOA, val | portb[0]]).await.unwrap(); | ||
| 96 | val = val.rotate_left(1); | ||
| 97 | |||
| 98 | // get a register dump | ||
| 99 | info!("getting register dump"); | ||
| 100 | let mut regs = [0; 22]; | ||
| 101 | i2c.write_read(ADDR, &[0], &mut regs).await.unwrap(); | ||
| 102 | // always get the regdump but only display it if portb'0 is set | ||
| 103 | if portb[0] & 1 != 0 { | ||
| 104 | for (idx, reg) in regs.into_iter().enumerate() { | ||
| 105 | info!("{} => {:02x}", regname(idx as u8), reg); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | Timer::after(Duration::from_millis(100)).await; | ||
| 110 | } | ||
| 111 | } | ||
diff --git a/examples/rp/src/bin/i2c_blocking.rs b/examples/rp/src/bin/i2c_blocking.rs new file mode 100644 index 000000000..1c8c2039d --- /dev/null +++ b/examples/rp/src/bin/i2c_blocking.rs | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | //! This example shows how to communicate using i2c with external chips. | ||
| 2 | //! | ||
| 3 | //! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip. | ||
| 4 | //! (https://www.microchip.com/en-us/product/mcp23017) | ||
| 5 | |||
| 6 | #![no_std] | ||
| 7 | #![no_main] | ||
| 8 | #![feature(type_alias_impl_trait)] | ||
| 9 | |||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_rp::i2c::{self, Config}; | ||
| 13 | use embassy_time::{Duration, Timer}; | ||
| 14 | use embedded_hal_1::i2c::I2c; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | #[allow(dead_code)] | ||
| 18 | mod mcp23017 { | ||
| 19 | pub const ADDR: u8 = 0x20; // default addr | ||
| 20 | |||
| 21 | pub const IODIRA: u8 = 0x00; | ||
| 22 | pub const IPOLA: u8 = 0x02; | ||
| 23 | pub const GPINTENA: u8 = 0x04; | ||
| 24 | pub const DEFVALA: u8 = 0x06; | ||
| 25 | pub const INTCONA: u8 = 0x08; | ||
| 26 | pub const IOCONA: u8 = 0x0A; | ||
| 27 | pub const GPPUA: u8 = 0x0C; | ||
| 28 | pub const INTFA: u8 = 0x0E; | ||
| 29 | pub const INTCAPA: u8 = 0x10; | ||
| 30 | pub const GPIOA: u8 = 0x12; | ||
| 31 | pub const OLATA: u8 = 0x14; | ||
| 32 | pub const IODIRB: u8 = 0x01; | ||
| 33 | pub const IPOLB: u8 = 0x03; | ||
| 34 | pub const GPINTENB: u8 = 0x05; | ||
| 35 | pub const DEFVALB: u8 = 0x07; | ||
| 36 | pub const INTCONB: u8 = 0x09; | ||
| 37 | pub const IOCONB: u8 = 0x0B; | ||
| 38 | pub const GPPUB: u8 = 0x0D; | ||
| 39 | pub const INTFB: u8 = 0x0F; | ||
| 40 | pub const INTCAPB: u8 = 0x11; | ||
| 41 | pub const GPIOB: u8 = 0x13; | ||
| 42 | pub const OLATB: u8 = 0x15; | ||
| 43 | } | ||
| 44 | |||
| 45 | #[embassy_executor::main] | ||
| 46 | async fn main(_spawner: Spawner) { | ||
| 47 | let p = embassy_rp::init(Default::default()); | ||
| 48 | |||
| 49 | let sda = p.PIN_14; | ||
| 50 | let scl = p.PIN_15; | ||
| 51 | |||
| 52 | info!("set up i2c "); | ||
| 53 | let mut i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default()); | ||
| 54 | |||
| 55 | use mcp23017::*; | ||
| 56 | |||
| 57 | info!("init mcp23017 config for IxpandO"); | ||
| 58 | // init - a outputs, b inputs | ||
| 59 | i2c.write(ADDR, &[IODIRA, 0x00]).unwrap(); | ||
| 60 | i2c.write(ADDR, &[IODIRB, 0xff]).unwrap(); | ||
| 61 | i2c.write(ADDR, &[GPPUB, 0xff]).unwrap(); // pullups | ||
| 62 | |||
| 63 | let mut val = 0xaa; | ||
| 64 | loop { | ||
| 65 | let mut portb = [0]; | ||
| 66 | |||
| 67 | i2c.write(mcp23017::ADDR, &[GPIOA, val]).unwrap(); | ||
| 68 | i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).unwrap(); | ||
| 69 | |||
| 70 | info!("portb = {:02x}", portb[0]); | ||
| 71 | val = !val; | ||
| 72 | |||
| 73 | Timer::after(Duration::from_secs(1)).await; | ||
| 74 | } | ||
| 75 | } | ||
diff --git a/examples/rp/src/bin/lora_lorawan.rs b/examples/rp/src/bin/lora_lorawan.rs new file mode 100644 index 000000000..d631fafa1 --- /dev/null +++ b/examples/rp/src/bin/lora_lorawan.rs | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. | ||
| 2 | //! It demonstrates LoRaWAN join functionality. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![macro_use] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||
| 12 | use embassy_lora::LoraTimer; | ||
| 13 | use embassy_rp::gpio::{Input, Level, Output, Pin, Pull}; | ||
| 14 | use embassy_rp::spi::{Config, Spi}; | ||
| 15 | use embassy_time::Delay; | ||
| 16 | use lora_phy::mod_params::*; | ||
| 17 | use lora_phy::sx1261_2::SX1261_2; | ||
| 18 | use lora_phy::LoRa; | ||
| 19 | use lorawan::default_crypto::DefaultFactory as Crypto; | ||
| 20 | use lorawan_device::async_device::lora_radio::LoRaRadio; | ||
| 21 | use lorawan_device::async_device::{region, Device, JoinMode}; | ||
| 22 | use {defmt_rtt as _, panic_probe as _}; | ||
| 23 | |||
| 24 | const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region | ||
| 25 | |||
| 26 | #[embassy_executor::main] | ||
| 27 | async fn main(_spawner: Spawner) { | ||
| 28 | let p = embassy_rp::init(Default::default()); | ||
| 29 | |||
| 30 | let miso = p.PIN_12; | ||
| 31 | let mosi = p.PIN_11; | ||
| 32 | let clk = p.PIN_10; | ||
| 33 | let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); | ||
| 34 | |||
| 35 | let nss = Output::new(p.PIN_3.degrade(), Level::High); | ||
| 36 | let reset = Output::new(p.PIN_15.degrade(), Level::High); | ||
| 37 | let dio1 = Input::new(p.PIN_20.degrade(), Pull::None); | ||
| 38 | let busy = Input::new(p.PIN_2.degrade(), Pull::None); | ||
| 39 | |||
| 40 | let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap(); | ||
| 41 | |||
| 42 | let mut delay = Delay; | ||
| 43 | |||
| 44 | let lora = { | ||
| 45 | match LoRa::new( | ||
| 46 | SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), | ||
| 47 | true, | ||
| 48 | &mut delay, | ||
| 49 | ) | ||
| 50 | .await | ||
| 51 | { | ||
| 52 | Ok(l) => l, | ||
| 53 | Err(err) => { | ||
| 54 | info!("Radio error = {}", err); | ||
| 55 | return; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | }; | ||
| 59 | |||
| 60 | let radio = LoRaRadio::new(lora); | ||
| 61 | let region: region::Configuration = region::Configuration::new(LORAWAN_REGION); | ||
| 62 | let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), embassy_rp::clocks::RoscRng); | ||
| 63 | |||
| 64 | defmt::info!("Joining LoRaWAN network"); | ||
| 65 | |||
| 66 | // TODO: Adjust the EUI and Keys according to your network credentials | ||
| 67 | match device | ||
| 68 | .join(&JoinMode::OTAA { | ||
| 69 | deveui: [0, 0, 0, 0, 0, 0, 0, 0], | ||
| 70 | appeui: [0, 0, 0, 0, 0, 0, 0, 0], | ||
| 71 | appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], | ||
| 72 | }) | ||
| 73 | .await | ||
| 74 | { | ||
| 75 | Ok(()) => defmt::info!("LoRaWAN network joined"), | ||
| 76 | Err(err) => { | ||
| 77 | info!("Radio error = {}", err); | ||
| 78 | return; | ||
| 79 | } | ||
| 80 | }; | ||
| 81 | } | ||
diff --git a/examples/rp/src/bin/lora_p2p_receive.rs b/examples/rp/src/bin/lora_p2p_receive.rs new file mode 100644 index 000000000..396d669de --- /dev/null +++ b/examples/rp/src/bin/lora_p2p_receive.rs | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. | ||
| 2 | //! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![macro_use] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||
| 12 | use embassy_rp::gpio::{Input, Level, Output, Pin, Pull}; | ||
| 13 | use embassy_rp::spi::{Config, Spi}; | ||
| 14 | use embassy_time::{Delay, Duration, Timer}; | ||
| 15 | use lora_phy::mod_params::*; | ||
| 16 | use lora_phy::sx1261_2::SX1261_2; | ||
| 17 | use lora_phy::LoRa; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(_spawner: Spawner) { | ||
| 24 | let p = embassy_rp::init(Default::default()); | ||
| 25 | |||
| 26 | let miso = p.PIN_12; | ||
| 27 | let mosi = p.PIN_11; | ||
| 28 | let clk = p.PIN_10; | ||
| 29 | let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); | ||
| 30 | |||
| 31 | let nss = Output::new(p.PIN_3.degrade(), Level::High); | ||
| 32 | let reset = Output::new(p.PIN_15.degrade(), Level::High); | ||
| 33 | let dio1 = Input::new(p.PIN_20.degrade(), Pull::None); | ||
| 34 | let busy = Input::new(p.PIN_2.degrade(), Pull::None); | ||
| 35 | |||
| 36 | let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap(); | ||
| 37 | |||
| 38 | let mut delay = Delay; | ||
| 39 | |||
| 40 | let mut lora = { | ||
| 41 | match LoRa::new( | ||
| 42 | SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), | ||
| 43 | false, | ||
| 44 | &mut delay, | ||
| 45 | ) | ||
| 46 | .await | ||
| 47 | { | ||
| 48 | Ok(l) => l, | ||
| 49 | Err(err) => { | ||
| 50 | info!("Radio error = {}", err); | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | } | ||
| 54 | }; | ||
| 55 | |||
| 56 | let mut debug_indicator = Output::new(p.PIN_25, Level::Low); | ||
| 57 | |||
| 58 | let mut receiving_buffer = [00u8; 100]; | ||
| 59 | |||
| 60 | let mdltn_params = { | ||
| 61 | match lora.create_modulation_params( | ||
| 62 | SpreadingFactor::_10, | ||
| 63 | Bandwidth::_250KHz, | ||
| 64 | CodingRate::_4_8, | ||
| 65 | LORA_FREQUENCY_IN_HZ, | ||
| 66 | ) { | ||
| 67 | Ok(mp) => mp, | ||
| 68 | Err(err) => { | ||
| 69 | info!("Radio error = {}", err); | ||
| 70 | return; | ||
| 71 | } | ||
| 72 | } | ||
| 73 | }; | ||
| 74 | |||
| 75 | let rx_pkt_params = { | ||
| 76 | match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) { | ||
| 77 | Ok(pp) => pp, | ||
| 78 | Err(err) => { | ||
| 79 | info!("Radio error = {}", err); | ||
| 80 | return; | ||
| 81 | } | ||
| 82 | } | ||
| 83 | }; | ||
| 84 | |||
| 85 | match lora | ||
| 86 | .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, true, false, 0, 0x00ffffffu32) | ||
| 87 | .await | ||
| 88 | { | ||
| 89 | Ok(()) => {} | ||
| 90 | Err(err) => { | ||
| 91 | info!("Radio error = {}", err); | ||
| 92 | return; | ||
| 93 | } | ||
| 94 | }; | ||
| 95 | |||
| 96 | loop { | ||
| 97 | receiving_buffer = [00u8; 100]; | ||
| 98 | match lora.rx(&rx_pkt_params, &mut receiving_buffer).await { | ||
| 99 | Ok((received_len, _rx_pkt_status)) => { | ||
| 100 | if (received_len == 3) | ||
| 101 | && (receiving_buffer[0] == 0x01u8) | ||
| 102 | && (receiving_buffer[1] == 0x02u8) | ||
| 103 | && (receiving_buffer[2] == 0x03u8) | ||
| 104 | { | ||
| 105 | info!("rx successful"); | ||
| 106 | debug_indicator.set_high(); | ||
| 107 | Timer::after(Duration::from_secs(5)).await; | ||
| 108 | debug_indicator.set_low(); | ||
| 109 | } else { | ||
| 110 | info!("rx unknown packet"); | ||
| 111 | } | ||
| 112 | } | ||
| 113 | Err(err) => info!("rx unsuccessful = {}", err), | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
diff --git a/examples/rp/src/bin/lora_p2p_send.rs b/examples/rp/src/bin/lora_p2p_send.rs new file mode 100644 index 000000000..a0f70fa5c --- /dev/null +++ b/examples/rp/src/bin/lora_p2p_send.rs | |||
| @@ -0,0 +1,104 @@ | |||
| 1 | //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. | ||
| 2 | //! It demonstrates LORA P2P send functionality. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![macro_use] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||
| 12 | use embassy_rp::gpio::{Input, Level, Output, Pin, Pull}; | ||
| 13 | use embassy_rp::spi::{Config, Spi}; | ||
| 14 | use embassy_time::Delay; | ||
| 15 | use lora_phy::mod_params::*; | ||
| 16 | use lora_phy::sx1261_2::SX1261_2; | ||
| 17 | use lora_phy::LoRa; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(_spawner: Spawner) { | ||
| 24 | let p = embassy_rp::init(Default::default()); | ||
| 25 | |||
| 26 | let miso = p.PIN_12; | ||
| 27 | let mosi = p.PIN_11; | ||
| 28 | let clk = p.PIN_10; | ||
| 29 | let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); | ||
| 30 | |||
| 31 | let nss = Output::new(p.PIN_3.degrade(), Level::High); | ||
| 32 | let reset = Output::new(p.PIN_15.degrade(), Level::High); | ||
| 33 | let dio1 = Input::new(p.PIN_20.degrade(), Pull::None); | ||
| 34 | let busy = Input::new(p.PIN_2.degrade(), Pull::None); | ||
| 35 | |||
| 36 | let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap(); | ||
| 37 | |||
| 38 | let mut delay = Delay; | ||
| 39 | |||
| 40 | let mut lora = { | ||
| 41 | match LoRa::new( | ||
| 42 | SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), | ||
| 43 | false, | ||
| 44 | &mut delay, | ||
| 45 | ) | ||
| 46 | .await | ||
| 47 | { | ||
| 48 | Ok(l) => l, | ||
| 49 | Err(err) => { | ||
| 50 | info!("Radio error = {}", err); | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | } | ||
| 54 | }; | ||
| 55 | |||
| 56 | let mdltn_params = { | ||
| 57 | match lora.create_modulation_params( | ||
| 58 | SpreadingFactor::_10, | ||
| 59 | Bandwidth::_250KHz, | ||
| 60 | CodingRate::_4_8, | ||
| 61 | LORA_FREQUENCY_IN_HZ, | ||
| 62 | ) { | ||
| 63 | Ok(mp) => mp, | ||
| 64 | Err(err) => { | ||
| 65 | info!("Radio error = {}", err); | ||
| 66 | return; | ||
| 67 | } | ||
| 68 | } | ||
| 69 | }; | ||
| 70 | |||
| 71 | let mut tx_pkt_params = { | ||
| 72 | match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) { | ||
| 73 | Ok(pp) => pp, | ||
| 74 | Err(err) => { | ||
| 75 | info!("Radio error = {}", err); | ||
| 76 | return; | ||
| 77 | } | ||
| 78 | } | ||
| 79 | }; | ||
| 80 | |||
| 81 | match lora.prepare_for_tx(&mdltn_params, 20, false).await { | ||
| 82 | Ok(()) => {} | ||
| 83 | Err(err) => { | ||
| 84 | info!("Radio error = {}", err); | ||
| 85 | return; | ||
| 86 | } | ||
| 87 | }; | ||
| 88 | |||
| 89 | let buffer = [0x01u8, 0x02u8, 0x03u8]; | ||
| 90 | match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await { | ||
| 91 | Ok(()) => { | ||
| 92 | info!("TX DONE"); | ||
| 93 | } | ||
| 94 | Err(err) => { | ||
| 95 | info!("Radio error = {}", err); | ||
| 96 | return; | ||
| 97 | } | ||
| 98 | }; | ||
| 99 | |||
| 100 | match lora.sleep(&mut delay).await { | ||
| 101 | Ok(()) => info!("Sleep successful"), | ||
| 102 | Err(err) => info!("Sleep unsuccessful = {}", err), | ||
| 103 | } | ||
| 104 | } | ||
diff --git a/examples/rp/src/bin/lora_p2p_send_multicore.rs b/examples/rp/src/bin/lora_p2p_send_multicore.rs new file mode 100644 index 000000000..89a62818d --- /dev/null +++ b/examples/rp/src/bin/lora_p2p_send_multicore.rs | |||
| @@ -0,0 +1,140 @@ | |||
| 1 | //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. | ||
| 2 | //! It demonstrates LORA P2P send functionality using the second core, with data provided by the first core. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![macro_use] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Executor; | ||
| 11 | use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||
| 12 | use embassy_rp::gpio::{AnyPin, Input, Level, Output, Pin, Pull}; | ||
| 13 | use embassy_rp::multicore::{spawn_core1, Stack}; | ||
| 14 | use embassy_rp::peripherals::SPI1; | ||
| 15 | use embassy_rp::spi::{Async, Config, Spi}; | ||
| 16 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 17 | use embassy_sync::channel::Channel; | ||
| 18 | use embassy_time::{Delay, Duration, Timer}; | ||
| 19 | use lora_phy::mod_params::*; | ||
| 20 | use lora_phy::sx1261_2::SX1261_2; | ||
| 21 | use lora_phy::LoRa; | ||
| 22 | use static_cell::StaticCell; | ||
| 23 | use {defmt_rtt as _, panic_probe as _}; | ||
| 24 | |||
| 25 | static mut CORE1_STACK: Stack<4096> = Stack::new(); | ||
| 26 | static EXECUTOR0: StaticCell<Executor> = StaticCell::new(); | ||
| 27 | static EXECUTOR1: StaticCell<Executor> = StaticCell::new(); | ||
| 28 | static CHANNEL: Channel<CriticalSectionRawMutex, [u8; 3], 1> = Channel::new(); | ||
| 29 | |||
| 30 | const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||
| 31 | |||
| 32 | #[cortex_m_rt::entry] | ||
| 33 | fn main() -> ! { | ||
| 34 | let p = embassy_rp::init(Default::default()); | ||
| 35 | |||
| 36 | let miso = p.PIN_12; | ||
| 37 | let mosi = p.PIN_11; | ||
| 38 | let clk = p.PIN_10; | ||
| 39 | let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); | ||
| 40 | |||
| 41 | let nss = Output::new(p.PIN_3.degrade(), Level::High); | ||
| 42 | let reset = Output::new(p.PIN_15.degrade(), Level::High); | ||
| 43 | let dio1 = Input::new(p.PIN_20.degrade(), Pull::None); | ||
| 44 | let busy = Input::new(p.PIN_2.degrade(), Pull::None); | ||
| 45 | |||
| 46 | let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap(); | ||
| 47 | |||
| 48 | spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { | ||
| 49 | let executor1 = EXECUTOR1.init(Executor::new()); | ||
| 50 | executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(spi, iv)))); | ||
| 51 | }); | ||
| 52 | |||
| 53 | let executor0 = EXECUTOR0.init(Executor::new()); | ||
| 54 | executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); | ||
| 55 | } | ||
| 56 | |||
| 57 | #[embassy_executor::task] | ||
| 58 | async fn core0_task() { | ||
| 59 | info!("Hello from core 0"); | ||
| 60 | loop { | ||
| 61 | CHANNEL.send([0x01u8, 0x02u8, 0x03u8]).await; | ||
| 62 | Timer::after(Duration::from_millis(60 * 1000)).await; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | #[embassy_executor::task] | ||
| 67 | async fn core1_task( | ||
| 68 | spi: Spi<'static, SPI1, Async>, | ||
| 69 | iv: GenericSx126xInterfaceVariant<Output<'static, AnyPin>, Input<'static, AnyPin>>, | ||
| 70 | ) { | ||
| 71 | info!("Hello from core 1"); | ||
| 72 | let mut delay = Delay; | ||
| 73 | |||
| 74 | let mut lora = { | ||
| 75 | match LoRa::new( | ||
| 76 | SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), | ||
| 77 | false, | ||
| 78 | &mut delay, | ||
| 79 | ) | ||
| 80 | .await | ||
| 81 | { | ||
| 82 | Ok(l) => l, | ||
| 83 | Err(err) => { | ||
| 84 | info!("Radio error = {}", err); | ||
| 85 | return; | ||
| 86 | } | ||
| 87 | } | ||
| 88 | }; | ||
| 89 | |||
| 90 | let mdltn_params = { | ||
| 91 | match lora.create_modulation_params( | ||
| 92 | SpreadingFactor::_10, | ||
| 93 | Bandwidth::_250KHz, | ||
| 94 | CodingRate::_4_8, | ||
| 95 | LORA_FREQUENCY_IN_HZ, | ||
| 96 | ) { | ||
| 97 | Ok(mp) => mp, | ||
| 98 | Err(err) => { | ||
| 99 | info!("Radio error = {}", err); | ||
| 100 | return; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | }; | ||
| 104 | |||
| 105 | let mut tx_pkt_params = { | ||
| 106 | match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) { | ||
| 107 | Ok(pp) => pp, | ||
| 108 | Err(err) => { | ||
| 109 | info!("Radio error = {}", err); | ||
| 110 | return; | ||
| 111 | } | ||
| 112 | } | ||
| 113 | }; | ||
| 114 | |||
| 115 | loop { | ||
| 116 | let buffer: [u8; 3] = CHANNEL.recv().await; | ||
| 117 | match lora.prepare_for_tx(&mdltn_params, 20, false).await { | ||
| 118 | Ok(()) => {} | ||
| 119 | Err(err) => { | ||
| 120 | info!("Radio error = {}", err); | ||
| 121 | return; | ||
| 122 | } | ||
| 123 | }; | ||
| 124 | |||
| 125 | match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await { | ||
| 126 | Ok(()) => { | ||
| 127 | info!("TX DONE"); | ||
| 128 | } | ||
| 129 | Err(err) => { | ||
| 130 | info!("Radio error = {}", err); | ||
| 131 | return; | ||
| 132 | } | ||
| 133 | }; | ||
| 134 | |||
| 135 | match lora.sleep(&mut delay).await { | ||
| 136 | Ok(()) => info!("Sleep successful"), | ||
| 137 | Err(err) => info!("Sleep unsuccessful = {}", err), | ||
| 138 | } | ||
| 139 | } | ||
| 140 | } | ||
diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs new file mode 100644 index 000000000..893b724bf --- /dev/null +++ b/examples/rp/src/bin/multicore.rs | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | //! This example shows how to send messages between the two cores in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Executor; | ||
| 11 | use embassy_rp::gpio::{Level, Output}; | ||
| 12 | use embassy_rp::multicore::{spawn_core1, Stack}; | ||
| 13 | use embassy_rp::peripherals::PIN_25; | ||
| 14 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 15 | use embassy_sync::channel::Channel; | ||
| 16 | use embassy_time::{Duration, Timer}; | ||
| 17 | use static_cell::StaticCell; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | static mut CORE1_STACK: Stack<4096> = Stack::new(); | ||
| 21 | static EXECUTOR0: StaticCell<Executor> = StaticCell::new(); | ||
| 22 | static EXECUTOR1: StaticCell<Executor> = StaticCell::new(); | ||
| 23 | static CHANNEL: Channel<CriticalSectionRawMutex, LedState, 1> = Channel::new(); | ||
| 24 | |||
| 25 | enum LedState { | ||
| 26 | On, | ||
| 27 | Off, | ||
| 28 | } | ||
| 29 | |||
| 30 | #[cortex_m_rt::entry] | ||
| 31 | fn main() -> ! { | ||
| 32 | let p = embassy_rp::init(Default::default()); | ||
| 33 | let led = Output::new(p.PIN_25, Level::Low); | ||
| 34 | |||
| 35 | spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { | ||
| 36 | let executor1 = EXECUTOR1.init(Executor::new()); | ||
| 37 | executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led)))); | ||
| 38 | }); | ||
| 39 | |||
| 40 | let executor0 = EXECUTOR0.init(Executor::new()); | ||
| 41 | executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); | ||
| 42 | } | ||
| 43 | |||
| 44 | #[embassy_executor::task] | ||
| 45 | async fn core0_task() { | ||
| 46 | info!("Hello from core 0"); | ||
| 47 | loop { | ||
| 48 | CHANNEL.send(LedState::On).await; | ||
| 49 | Timer::after(Duration::from_millis(100)).await; | ||
| 50 | CHANNEL.send(LedState::Off).await; | ||
| 51 | Timer::after(Duration::from_millis(400)).await; | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | #[embassy_executor::task] | ||
| 56 | async fn core1_task(mut led: Output<'static, PIN_25>) { | ||
| 57 | info!("Hello from core 1"); | ||
| 58 | loop { | ||
| 59 | match CHANNEL.recv().await { | ||
| 60 | LedState::On => led.set_high(), | ||
| 61 | LedState::Off => led.set_low(), | ||
| 62 | } | ||
| 63 | } | ||
| 64 | } | ||
diff --git a/examples/rp/src/bin/multiprio.rs b/examples/rp/src/bin/multiprio.rs new file mode 100644 index 000000000..9ace4cd68 --- /dev/null +++ b/examples/rp/src/bin/multiprio.rs | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | //! This example showcases how to create multiple Executor instances to run tasks at | ||
| 2 | //! different priority levels. | ||
| 3 | //! | ||
| 4 | //! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling | ||
| 5 | //! there's work in the queue, and `wfe` for waiting for work. | ||
| 6 | //! | ||
| 7 | //! Medium and high priority executors run in two interrupts with different priorities. | ||
| 8 | //! Signaling work is done by pending the interrupt. No "waiting" needs to be done explicitly, since | ||
| 9 | //! when there's work the interrupt will trigger and run the executor. | ||
| 10 | //! | ||
| 11 | //! Sample output below. Note that high priority ticks can interrupt everything else, and | ||
| 12 | //! medium priority computations can interrupt low priority computations, making them to appear | ||
| 13 | //! to take significantly longer time. | ||
| 14 | //! | ||
| 15 | //! ```not_rust | ||
| 16 | //! [med] Starting long computation | ||
| 17 | //! [med] done in 992 ms | ||
| 18 | //! [high] tick! | ||
| 19 | //! [low] Starting long computation | ||
| 20 | //! [med] Starting long computation | ||
| 21 | //! [high] tick! | ||
| 22 | //! [high] tick! | ||
| 23 | //! [med] done in 993 ms | ||
| 24 | //! [med] Starting long computation | ||
| 25 | //! [high] tick! | ||
| 26 | //! [high] tick! | ||
| 27 | //! [med] done in 993 ms | ||
| 28 | //! [low] done in 3972 ms | ||
| 29 | //! [med] Starting long computation | ||
| 30 | //! [high] tick! | ||
| 31 | //! [high] tick! | ||
| 32 | //! [med] done in 993 ms | ||
| 33 | //! ``` | ||
| 34 | //! | ||
| 35 | //! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor. | ||
| 36 | //! You will get an output like the following. Note that no computation is ever interrupted. | ||
| 37 | //! | ||
| 38 | //! ```not_rust | ||
| 39 | //! [high] tick! | ||
| 40 | //! [med] Starting long computation | ||
| 41 | //! [med] done in 496 ms | ||
| 42 | //! [low] Starting long computation | ||
| 43 | //! [low] done in 992 ms | ||
| 44 | //! [med] Starting long computation | ||
| 45 | //! [med] done in 496 ms | ||
| 46 | //! [high] tick! | ||
| 47 | //! [low] Starting long computation | ||
| 48 | //! [low] done in 992 ms | ||
| 49 | //! [high] tick! | ||
| 50 | //! [med] Starting long computation | ||
| 51 | //! [med] done in 496 ms | ||
| 52 | //! [high] tick! | ||
| 53 | //! ``` | ||
| 54 | //! | ||
| 55 | |||
| 56 | #![no_std] | ||
| 57 | #![no_main] | ||
| 58 | #![feature(type_alias_impl_trait)] | ||
| 59 | |||
| 60 | use cortex_m_rt::entry; | ||
| 61 | use defmt::{info, unwrap}; | ||
| 62 | use embassy_executor::{Executor, InterruptExecutor}; | ||
| 63 | use embassy_rp::interrupt; | ||
| 64 | use embassy_rp::interrupt::{InterruptExt, Priority}; | ||
| 65 | use embassy_time::{Duration, Instant, Timer, TICK_HZ}; | ||
| 66 | use static_cell::StaticCell; | ||
| 67 | use {defmt_rtt as _, panic_probe as _}; | ||
| 68 | |||
| 69 | #[embassy_executor::task] | ||
| 70 | async fn run_high() { | ||
| 71 | loop { | ||
| 72 | info!(" [high] tick!"); | ||
| 73 | Timer::after(Duration::from_ticks(673740)).await; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | #[embassy_executor::task] | ||
| 78 | async fn run_med() { | ||
| 79 | loop { | ||
| 80 | let start = Instant::now(); | ||
| 81 | info!(" [med] Starting long computation"); | ||
| 82 | |||
| 83 | // Spin-wait to simulate a long CPU computation | ||
| 84 | cortex_m::asm::delay(125_000_000); // ~1 second | ||
| 85 | |||
| 86 | let end = Instant::now(); | ||
| 87 | let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ; | ||
| 88 | info!(" [med] done in {} ms", ms); | ||
| 89 | |||
| 90 | Timer::after(Duration::from_ticks(53421)).await; | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | #[embassy_executor::task] | ||
| 95 | async fn run_low() { | ||
| 96 | loop { | ||
| 97 | let start = Instant::now(); | ||
| 98 | info!("[low] Starting long computation"); | ||
| 99 | |||
| 100 | // Spin-wait to simulate a long CPU computation | ||
| 101 | cortex_m::asm::delay(250_000_000); // ~2 seconds | ||
| 102 | |||
| 103 | let end = Instant::now(); | ||
| 104 | let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ; | ||
| 105 | info!("[low] done in {} ms", ms); | ||
| 106 | |||
| 107 | Timer::after(Duration::from_ticks(82983)).await; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); | ||
| 112 | static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); | ||
| 113 | static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new(); | ||
| 114 | |||
| 115 | #[interrupt] | ||
| 116 | unsafe fn SWI_IRQ_1() { | ||
| 117 | EXECUTOR_HIGH.on_interrupt() | ||
| 118 | } | ||
| 119 | |||
| 120 | #[interrupt] | ||
| 121 | unsafe fn SWI_IRQ_0() { | ||
| 122 | EXECUTOR_MED.on_interrupt() | ||
| 123 | } | ||
| 124 | |||
| 125 | #[entry] | ||
| 126 | fn main() -> ! { | ||
| 127 | info!("Hello World!"); | ||
| 128 | |||
| 129 | let _p = embassy_rp::init(Default::default()); | ||
| 130 | |||
| 131 | // High-priority executor: SWI_IRQ_1, priority level 2 | ||
| 132 | interrupt::SWI_IRQ_1.set_priority(Priority::P2); | ||
| 133 | let spawner = EXECUTOR_HIGH.start(interrupt::SWI_IRQ_1); | ||
| 134 | unwrap!(spawner.spawn(run_high())); | ||
| 135 | |||
| 136 | // Medium-priority executor: SWI_IRQ_0, priority level 3 | ||
| 137 | interrupt::SWI_IRQ_0.set_priority(Priority::P3); | ||
| 138 | let spawner = EXECUTOR_MED.start(interrupt::SWI_IRQ_0); | ||
| 139 | unwrap!(spawner.spawn(run_med())); | ||
| 140 | |||
| 141 | // Low priority executor: runs in thread mode, using WFE/SEV | ||
| 142 | let executor = EXECUTOR_LOW.init(Executor::new()); | ||
| 143 | executor.run(|spawner| { | ||
| 144 | unwrap!(spawner.spawn(run_low())); | ||
| 145 | }); | ||
| 146 | } | ||
diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs new file mode 100644 index 000000000..c001d6440 --- /dev/null +++ b/examples/rp/src/bin/pio_async.rs | |||
| @@ -0,0 +1,135 @@ | |||
| 1 | //! This example shows powerful PIO module in the RP2040 chip. | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | #![feature(type_alias_impl_trait)] | ||
| 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::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine}; | ||
| 11 | use embassy_rp::relocate::RelocatedProgram; | ||
| 12 | use fixed::traits::ToFixed; | ||
| 13 | use fixed_macro::types::U56F8; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | bind_interrupts!(struct Irqs { | ||
| 17 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 18 | }); | ||
| 19 | |||
| 20 | fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) { | ||
| 21 | // Setup sm0 | ||
| 22 | |||
| 23 | // Send data serially to pin | ||
| 24 | let prg = pio_proc::pio_asm!( | ||
| 25 | ".origin 16", | ||
| 26 | "set pindirs, 1", | ||
| 27 | ".wrap_target", | ||
| 28 | "out pins,1 [19]", | ||
| 29 | ".wrap", | ||
| 30 | ); | ||
| 31 | |||
| 32 | let relocated = RelocatedProgram::new(&prg.program); | ||
| 33 | let mut cfg = Config::default(); | ||
| 34 | cfg.use_program(&pio.load_program(&relocated), &[]); | ||
| 35 | let out_pin = pio.make_pio_pin(pin); | ||
| 36 | cfg.set_out_pins(&[&out_pin]); | ||
| 37 | cfg.set_set_pins(&[&out_pin]); | ||
| 38 | cfg.clock_divider = (U56F8!(125_000_000) / 20 / 200).to_fixed(); | ||
| 39 | cfg.shift_out.auto_fill = true; | ||
| 40 | sm.set_config(&cfg); | ||
| 41 | } | ||
| 42 | |||
| 43 | #[embassy_executor::task] | ||
| 44 | async fn pio_task_sm0(mut sm: StateMachine<'static, PIO0, 0>) { | ||
| 45 | sm.set_enable(true); | ||
| 46 | |||
| 47 | let mut v = 0x0f0caffa; | ||
| 48 | loop { | ||
| 49 | sm.tx().wait_push(v).await; | ||
| 50 | v ^= 0xffff; | ||
| 51 | info!("Pushed {:032b} to FIFO", v); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 1>) { | ||
| 56 | // Setupm sm1 | ||
| 57 | |||
| 58 | // Read 0b10101 repeatedly until ISR is full | ||
| 59 | let prg = pio_proc::pio_asm!( | ||
| 60 | // | ||
| 61 | ".origin 8", | ||
| 62 | "set x, 0x15", | ||
| 63 | ".wrap_target", | ||
| 64 | "in x, 5 [31]", | ||
| 65 | ".wrap", | ||
| 66 | ); | ||
| 67 | |||
| 68 | let relocated = RelocatedProgram::new(&prg.program); | ||
| 69 | let mut cfg = Config::default(); | ||
| 70 | cfg.use_program(&pio.load_program(&relocated), &[]); | ||
| 71 | cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); | ||
| 72 | cfg.shift_in.auto_fill = true; | ||
| 73 | cfg.shift_in.direction = ShiftDirection::Right; | ||
| 74 | sm.set_config(&cfg); | ||
| 75 | } | ||
| 76 | |||
| 77 | #[embassy_executor::task] | ||
| 78 | async fn pio_task_sm1(mut sm: StateMachine<'static, PIO0, 1>) { | ||
| 79 | sm.set_enable(true); | ||
| 80 | loop { | ||
| 81 | let rx = sm.rx().wait_pull().await; | ||
| 82 | info!("Pulled {:032b} from FIFO", rx); | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 2>) { | ||
| 87 | // Setup sm2 | ||
| 88 | |||
| 89 | // Repeatedly trigger IRQ 3 | ||
| 90 | let prg = pio_proc::pio_asm!( | ||
| 91 | ".origin 0", | ||
| 92 | ".wrap_target", | ||
| 93 | "set x,10", | ||
| 94 | "delay:", | ||
| 95 | "jmp x-- delay [15]", | ||
| 96 | "irq 3 [15]", | ||
| 97 | ".wrap", | ||
| 98 | ); | ||
| 99 | let relocated = RelocatedProgram::new(&prg.program); | ||
| 100 | let mut cfg = Config::default(); | ||
| 101 | cfg.use_program(&pio.load_program(&relocated), &[]); | ||
| 102 | cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); | ||
| 103 | sm.set_config(&cfg); | ||
| 104 | } | ||
| 105 | |||
| 106 | #[embassy_executor::task] | ||
| 107 | async fn pio_task_sm2(mut irq: Irq<'static, PIO0, 3>, mut sm: StateMachine<'static, PIO0, 2>) { | ||
| 108 | sm.set_enable(true); | ||
| 109 | loop { | ||
| 110 | irq.wait().await; | ||
| 111 | info!("IRQ trigged"); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | #[embassy_executor::main] | ||
| 116 | async fn main(spawner: Spawner) { | ||
| 117 | let p = embassy_rp::init(Default::default()); | ||
| 118 | let pio = p.PIO0; | ||
| 119 | |||
| 120 | let Pio { | ||
| 121 | mut common, | ||
| 122 | irq3, | ||
| 123 | mut sm0, | ||
| 124 | mut sm1, | ||
| 125 | mut sm2, | ||
| 126 | .. | ||
| 127 | } = Pio::new(pio, Irqs); | ||
| 128 | |||
| 129 | setup_pio_task_sm0(&mut common, &mut sm0, p.PIN_0); | ||
| 130 | setup_pio_task_sm1(&mut common, &mut sm1); | ||
| 131 | setup_pio_task_sm2(&mut common, &mut sm2); | ||
| 132 | spawner.spawn(pio_task_sm0(sm0)).unwrap(); | ||
| 133 | spawner.spawn(pio_task_sm1(sm1)).unwrap(); | ||
| 134 | spawner.spawn(pio_task_sm2(irq3, sm2)).unwrap(); | ||
| 135 | } | ||
diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs new file mode 100644 index 000000000..9ab72e1f3 --- /dev/null +++ b/examples/rp/src/bin/pio_dma.rs | |||
| @@ -0,0 +1,86 @@ | |||
| 1 | //! This example shows powerful PIO module in the RP2040 chip. | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | #![feature(type_alias_impl_trait)] | ||
| 6 | use defmt::info; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_futures::join::join; | ||
| 9 | use embassy_rp::peripherals::PIO0; | ||
| 10 | use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; | ||
| 11 | use embassy_rp::relocate::RelocatedProgram; | ||
| 12 | use embassy_rp::{bind_interrupts, Peripheral}; | ||
| 13 | use fixed::traits::ToFixed; | ||
| 14 | use fixed_macro::types::U56F8; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | bind_interrupts!(struct Irqs { | ||
| 18 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 19 | }); | ||
| 20 | |||
| 21 | fn swap_nibbles(v: u32) -> u32 { | ||
| 22 | let v = (v & 0x0f0f_0f0f) << 4 | (v & 0xf0f0_f0f0) >> 4; | ||
| 23 | let v = (v & 0x00ff_00ff) << 8 | (v & 0xff00_ff00) >> 8; | ||
| 24 | (v & 0x0000_ffff) << 16 | (v & 0xffff_0000) >> 16 | ||
| 25 | } | ||
| 26 | |||
| 27 | #[embassy_executor::main] | ||
| 28 | async fn main(_spawner: Spawner) { | ||
| 29 | let p = embassy_rp::init(Default::default()); | ||
| 30 | let pio = p.PIO0; | ||
| 31 | let Pio { | ||
| 32 | mut common, | ||
| 33 | sm0: mut sm, | ||
| 34 | .. | ||
| 35 | } = Pio::new(pio, Irqs); | ||
| 36 | |||
| 37 | let prg = pio_proc::pio_asm!( | ||
| 38 | ".origin 0", | ||
| 39 | "set pindirs,1", | ||
| 40 | ".wrap_target", | ||
| 41 | "set y,7", | ||
| 42 | "loop:", | ||
| 43 | "out x,4", | ||
| 44 | "in x,4", | ||
| 45 | "jmp y--, loop", | ||
| 46 | ".wrap", | ||
| 47 | ); | ||
| 48 | |||
| 49 | let relocated = RelocatedProgram::new(&prg.program); | ||
| 50 | let mut cfg = Config::default(); | ||
| 51 | cfg.use_program(&common.load_program(&relocated), &[]); | ||
| 52 | cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed(); | ||
| 53 | cfg.shift_in = ShiftConfig { | ||
| 54 | auto_fill: true, | ||
| 55 | threshold: 32, | ||
| 56 | direction: ShiftDirection::Left, | ||
| 57 | }; | ||
| 58 | cfg.shift_out = ShiftConfig { | ||
| 59 | auto_fill: true, | ||
| 60 | threshold: 32, | ||
| 61 | direction: ShiftDirection::Right, | ||
| 62 | }; | ||
| 63 | |||
| 64 | sm.set_config(&cfg); | ||
| 65 | sm.set_enable(true); | ||
| 66 | |||
| 67 | let mut dma_out_ref = p.DMA_CH0.into_ref(); | ||
| 68 | let mut dma_in_ref = p.DMA_CH1.into_ref(); | ||
| 69 | let mut dout = [0x12345678u32; 29]; | ||
| 70 | for i in 1..dout.len() { | ||
| 71 | dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7; | ||
| 72 | } | ||
| 73 | let mut din = [0u32; 29]; | ||
| 74 | loop { | ||
| 75 | let (rx, tx) = sm.rx_tx(); | ||
| 76 | join( | ||
| 77 | tx.dma_push(dma_out_ref.reborrow(), &dout), | ||
| 78 | rx.dma_pull(dma_in_ref.reborrow(), &mut din), | ||
| 79 | ) | ||
| 80 | .await; | ||
| 81 | for i in 0..din.len() { | ||
| 82 | assert_eq!(din[i], swap_nibbles(dout[i])); | ||
| 83 | } | ||
| 84 | info!("Swapped {} words", dout.len()); | ||
| 85 | } | ||
| 86 | } | ||
diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs new file mode 100644 index 000000000..8aedd24b6 --- /dev/null +++ b/examples/rp/src/bin/pio_hd44780.rs | |||
| @@ -0,0 +1,244 @@ | |||
| 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 | #![feature(type_alias_impl_trait)] | ||
| 7 | |||
| 8 | use core::fmt::Write; | ||
| 9 | |||
| 10 | use embassy_executor::Spawner; | ||
| 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::relocate::RelocatedProgram; | ||
| 18 | use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef}; | ||
| 19 | use embassy_time::{Duration, Instant, Timer}; | ||
| 20 | use {defmt_rtt as _, panic_probe as _}; | ||
| 21 | |||
| 22 | bind_interrupts!(pub struct Irqs { | ||
| 23 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 24 | }); | ||
| 25 | |||
| 26 | #[embassy_executor::main] | ||
| 27 | async fn main(_spawner: Spawner) { | ||
| 28 | // this test assumes a 2x16 HD44780 display attached as follow: | ||
| 29 | // rs = PIN0 | ||
| 30 | // rw = PIN1 | ||
| 31 | // e = PIN2 | ||
| 32 | // db4 = PIN3 | ||
| 33 | // db5 = PIN4 | ||
| 34 | // db6 = PIN5 | ||
| 35 | // db7 = PIN6 | ||
| 36 | // additionally a pwm signal for a bias voltage charge pump is provided on pin 15, | ||
| 37 | // allowing direct connection of the display to the RP2040 without level shifters. | ||
| 38 | let p = embassy_rp::init(Default::default()); | ||
| 39 | |||
| 40 | let _pwm = Pwm::new_output_b(p.PWM_CH7, p.PIN_15, { | ||
| 41 | let mut c = pwm::Config::default(); | ||
| 42 | c.divider = 125.into(); | ||
| 43 | c.top = 100; | ||
| 44 | c.compare_b = 50; | ||
| 45 | c | ||
| 46 | }); | ||
| 47 | |||
| 48 | let mut hd = HD44780::new( | ||
| 49 | p.PIO0, Irqs, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6, | ||
| 50 | ) | ||
| 51 | .await; | ||
| 52 | |||
| 53 | loop { | ||
| 54 | struct Buf<const N: usize>([u8; N], usize); | ||
| 55 | impl<const N: usize> Write for Buf<N> { | ||
| 56 | fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { | ||
| 57 | for b in s.as_bytes() { | ||
| 58 | if self.1 >= N { | ||
| 59 | return Err(core::fmt::Error); | ||
| 60 | } | ||
| 61 | self.0[self.1] = *b; | ||
| 62 | self.1 += 1; | ||
| 63 | } | ||
| 64 | Ok(()) | ||
| 65 | } | ||
| 66 | } | ||
| 67 | let mut buf = Buf([0; 16], 0); | ||
| 68 | write!(buf, "up {}s", Instant::now().as_micros() as f32 / 1e6).unwrap(); | ||
| 69 | hd.add_line(&buf.0[0..buf.1]).await; | ||
| 70 | Timer::after(Duration::from_secs(1)).await; | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | pub struct HD44780<'l> { | ||
| 75 | dma: PeripheralRef<'l, AnyChannel>, | ||
| 76 | sm: StateMachine<'l, PIO0, 0>, | ||
| 77 | |||
| 78 | buf: [u8; 40], | ||
| 79 | } | ||
| 80 | |||
| 81 | impl<'l> HD44780<'l> { | ||
| 82 | pub async fn new( | ||
| 83 | pio: impl Peripheral<P = PIO0> + 'l, | ||
| 84 | irq: Irqs, | ||
| 85 | dma: impl Peripheral<P = impl Channel> + 'l, | ||
| 86 | rs: impl PioPin, | ||
| 87 | rw: impl PioPin, | ||
| 88 | e: impl PioPin, | ||
| 89 | db4: impl PioPin, | ||
| 90 | db5: impl PioPin, | ||
| 91 | db6: impl PioPin, | ||
| 92 | db7: impl PioPin, | ||
| 93 | ) -> HD44780<'l> { | ||
| 94 | into_ref!(dma); | ||
| 95 | |||
| 96 | let Pio { | ||
| 97 | mut common, | ||
| 98 | mut irq0, | ||
| 99 | mut sm0, | ||
| 100 | .. | ||
| 101 | } = Pio::new(pio, irq); | ||
| 102 | |||
| 103 | // takes command words (<wait:24> <command:4> <0:4>) | ||
| 104 | let prg = pio_proc::pio_asm!( | ||
| 105 | r#" | ||
| 106 | .side_set 1 opt | ||
| 107 | .origin 20 | ||
| 108 | |||
| 109 | loop: | ||
| 110 | out x, 24 | ||
| 111 | delay: | ||
| 112 | jmp x--, delay | ||
| 113 | out pins, 4 side 1 | ||
| 114 | out null, 4 side 0 | ||
| 115 | jmp !osre, loop | ||
| 116 | irq 0 | ||
| 117 | "#, | ||
| 118 | ); | ||
| 119 | |||
| 120 | let rs = common.make_pio_pin(rs); | ||
| 121 | let rw = common.make_pio_pin(rw); | ||
| 122 | let e = common.make_pio_pin(e); | ||
| 123 | let db4 = common.make_pio_pin(db4); | ||
| 124 | let db5 = common.make_pio_pin(db5); | ||
| 125 | let db6 = common.make_pio_pin(db6); | ||
| 126 | let db7 = common.make_pio_pin(db7); | ||
| 127 | |||
| 128 | sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]); | ||
| 129 | |||
| 130 | let relocated = RelocatedProgram::new(&prg.program); | ||
| 131 | let mut cfg = Config::default(); | ||
| 132 | cfg.use_program(&common.load_program(&relocated), &[&e]); | ||
| 133 | cfg.clock_divider = 125u8.into(); | ||
| 134 | cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); | ||
| 135 | cfg.shift_out = ShiftConfig { | ||
| 136 | auto_fill: true, | ||
| 137 | direction: ShiftDirection::Left, | ||
| 138 | threshold: 32, | ||
| 139 | }; | ||
| 140 | cfg.fifo_join = FifoJoin::TxOnly; | ||
| 141 | sm0.set_config(&cfg); | ||
| 142 | |||
| 143 | sm0.set_enable(true); | ||
| 144 | // init to 8 bit thrice | ||
| 145 | sm0.tx().push((50000 << 8) | 0x30); | ||
| 146 | sm0.tx().push((5000 << 8) | 0x30); | ||
| 147 | sm0.tx().push((200 << 8) | 0x30); | ||
| 148 | // init 4 bit | ||
| 149 | sm0.tx().push((200 << 8) | 0x20); | ||
| 150 | // set font and lines | ||
| 151 | sm0.tx().push((50 << 8) | 0x20); | ||
| 152 | sm0.tx().push(0b1100_0000); | ||
| 153 | |||
| 154 | irq0.wait().await; | ||
| 155 | sm0.set_enable(false); | ||
| 156 | |||
| 157 | // takes command sequences (<rs:1> <count:7>, data...) | ||
| 158 | // many side sets are only there to free up a delay bit! | ||
| 159 | let prg = pio_proc::pio_asm!( | ||
| 160 | r#" | ||
| 161 | .origin 27 | ||
| 162 | .side_set 1 | ||
| 163 | |||
| 164 | .wrap_target | ||
| 165 | pull side 0 | ||
| 166 | out x 1 side 0 ; !rs | ||
| 167 | out y 7 side 0 ; #data - 1 | ||
| 168 | |||
| 169 | ; rs/rw to e: >= 60ns | ||
| 170 | ; e high time: >= 500ns | ||
| 171 | ; e low time: >= 500ns | ||
| 172 | ; read data valid after e falling: ~5ns | ||
| 173 | ; write data hold after e falling: ~10ns | ||
| 174 | |||
| 175 | loop: | ||
| 176 | pull side 0 | ||
| 177 | jmp !x data side 0 | ||
| 178 | command: | ||
| 179 | set pins 0b00 side 0 | ||
| 180 | jmp shift side 0 | ||
| 181 | data: | ||
| 182 | set pins 0b01 side 0 | ||
| 183 | shift: | ||
| 184 | out pins 4 side 1 [9] | ||
| 185 | nop side 0 [9] | ||
| 186 | out pins 4 side 1 [9] | ||
| 187 | mov osr null side 0 [7] | ||
| 188 | out pindirs 4 side 0 | ||
| 189 | set pins 0b10 side 0 | ||
| 190 | busy: | ||
| 191 | nop side 1 [9] | ||
| 192 | jmp pin more side 0 [9] | ||
| 193 | mov osr ~osr side 1 [9] | ||
| 194 | nop side 0 [4] | ||
| 195 | out pindirs 4 side 0 | ||
| 196 | jmp y-- loop side 0 | ||
| 197 | .wrap | ||
| 198 | more: | ||
| 199 | nop side 1 [9] | ||
| 200 | jmp busy side 0 [9] | ||
| 201 | "# | ||
| 202 | ); | ||
| 203 | |||
| 204 | let relocated = RelocatedProgram::new(&prg.program); | ||
| 205 | let mut cfg = Config::default(); | ||
| 206 | cfg.use_program(&common.load_program(&relocated), &[&e]); | ||
| 207 | cfg.clock_divider = 8u8.into(); // ~64ns/insn | ||
| 208 | cfg.set_jmp_pin(&db7); | ||
| 209 | cfg.set_set_pins(&[&rs, &rw]); | ||
| 210 | cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); | ||
| 211 | cfg.shift_out.direction = ShiftDirection::Left; | ||
| 212 | cfg.fifo_join = FifoJoin::TxOnly; | ||
| 213 | sm0.set_config(&cfg); | ||
| 214 | |||
| 215 | sm0.set_enable(true); | ||
| 216 | |||
| 217 | // display on and cursor on and blinking, reset display | ||
| 218 | sm0.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await; | ||
| 219 | |||
| 220 | Self { | ||
| 221 | dma: dma.map_into(), | ||
| 222 | sm: sm0, | ||
| 223 | buf: [0x20; 40], | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | pub async fn add_line(&mut self, s: &[u8]) { | ||
| 228 | // move cursor to 0:0, prepare 16 characters | ||
| 229 | self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]); | ||
| 230 | // move line 2 up | ||
| 231 | self.buf.copy_within(22..38, 3); | ||
| 232 | // move cursor to 1:0, prepare 16 characters | ||
| 233 | self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]); | ||
| 234 | // file line 2 with spaces | ||
| 235 | self.buf[22..38].fill(0x20); | ||
| 236 | // copy input line | ||
| 237 | let len = s.len().min(16); | ||
| 238 | self.buf[22..22 + len].copy_from_slice(&s[0..len]); | ||
| 239 | // set cursor to 1:15 | ||
| 240 | self.buf[38..].copy_from_slice(&[0x80, 0xcf]); | ||
| 241 | |||
| 242 | self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await; | ||
| 243 | } | ||
| 244 | } | ||
diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs new file mode 100644 index 000000000..3de2bd48d --- /dev/null +++ b/examples/rp/src/bin/pio_ws2812.rs | |||
| @@ -0,0 +1,160 @@ | |||
| 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 | #![feature(type_alias_impl_trait)] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 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::relocate::RelocatedProgram; | ||
| 16 | use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef}; | ||
| 17 | use embassy_time::{Duration, Timer}; | ||
| 18 | use fixed::types::U24F8; | ||
| 19 | use fixed_macro::fixed; | ||
| 20 | use smart_leds::RGB8; | ||
| 21 | use {defmt_rtt as _, panic_probe as _}; | ||
| 22 | |||
| 23 | bind_interrupts!(struct Irqs { | ||
| 24 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 25 | }); | ||
| 26 | |||
| 27 | pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> { | ||
| 28 | dma: PeripheralRef<'d, AnyChannel>, | ||
| 29 | sm: StateMachine<'d, P, S>, | ||
| 30 | } | ||
| 31 | |||
| 32 | impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> { | ||
| 33 | pub fn new( | ||
| 34 | pio: &mut Common<'d, P>, | ||
| 35 | mut sm: StateMachine<'d, P, S>, | ||
| 36 | dma: impl Peripheral<P = impl Channel> + 'd, | ||
| 37 | pin: impl PioPin, | ||
| 38 | ) -> Self { | ||
| 39 | into_ref!(dma); | ||
| 40 | |||
| 41 | // Setup sm0 | ||
| 42 | |||
| 43 | // prepare the PIO program | ||
| 44 | let side_set = pio::SideSet::new(false, 1, false); | ||
| 45 | let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set); | ||
| 46 | |||
| 47 | const T1: u8 = 2; // start bit | ||
| 48 | const T2: u8 = 5; // data bit | ||
| 49 | const T3: u8 = 3; // stop bit | ||
| 50 | const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32; | ||
| 51 | |||
| 52 | let mut wrap_target = a.label(); | ||
| 53 | let mut wrap_source = a.label(); | ||
| 54 | let mut do_zero = a.label(); | ||
| 55 | a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0); | ||
| 56 | a.bind(&mut wrap_target); | ||
| 57 | // Do stop bit | ||
| 58 | a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0); | ||
| 59 | // Do start bit | ||
| 60 | a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1); | ||
| 61 | // Do data bit = 1 | ||
| 62 | a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1); | ||
| 63 | a.bind(&mut do_zero); | ||
| 64 | // Do data bit = 0 | ||
| 65 | a.nop_with_delay_and_side_set(T2 - 1, 0); | ||
| 66 | a.bind(&mut wrap_source); | ||
| 67 | |||
| 68 | let prg = a.assemble_with_wrap(wrap_source, wrap_target); | ||
| 69 | let mut cfg = Config::default(); | ||
| 70 | |||
| 71 | // Pin config | ||
| 72 | let out_pin = pio.make_pio_pin(pin); | ||
| 73 | cfg.set_out_pins(&[&out_pin]); | ||
| 74 | cfg.set_set_pins(&[&out_pin]); | ||
| 75 | |||
| 76 | let relocated = RelocatedProgram::new(&prg); | ||
| 77 | cfg.use_program(&pio.load_program(&relocated), &[&out_pin]); | ||
| 78 | |||
| 79 | // Clock config, measured in kHz to avoid overflows | ||
| 80 | // TODO CLOCK_FREQ should come from embassy_rp | ||
| 81 | let clock_freq = U24F8::from_num(clocks::clk_sys_freq() / 1000); | ||
| 82 | let ws2812_freq = fixed!(800: U24F8); | ||
| 83 | let bit_freq = ws2812_freq * CYCLES_PER_BIT; | ||
| 84 | cfg.clock_divider = clock_freq / bit_freq; | ||
| 85 | |||
| 86 | // FIFO config | ||
| 87 | cfg.fifo_join = FifoJoin::TxOnly; | ||
| 88 | cfg.shift_out = ShiftConfig { | ||
| 89 | auto_fill: true, | ||
| 90 | threshold: 24, | ||
| 91 | direction: ShiftDirection::Left, | ||
| 92 | }; | ||
| 93 | |||
| 94 | sm.set_config(&cfg); | ||
| 95 | sm.set_enable(true); | ||
| 96 | |||
| 97 | Self { | ||
| 98 | dma: dma.map_into(), | ||
| 99 | sm, | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | pub async fn write(&mut self, colors: &[RGB8; N]) { | ||
| 104 | // Precompute the word bytes from the colors | ||
| 105 | let mut words = [0u32; N]; | ||
| 106 | for i in 0..N { | ||
| 107 | let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8); | ||
| 108 | words[i] = word; | ||
| 109 | } | ||
| 110 | |||
| 111 | // DMA transfer | ||
| 112 | self.sm.tx().dma_push(self.dma.reborrow(), &words).await; | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | /// Input a value 0 to 255 to get a color value | ||
| 117 | /// The colours are a transition r - g - b - back to r. | ||
| 118 | fn wheel(mut wheel_pos: u8) -> RGB8 { | ||
| 119 | wheel_pos = 255 - wheel_pos; | ||
| 120 | if wheel_pos < 85 { | ||
| 121 | return (255 - wheel_pos * 3, 0, wheel_pos * 3).into(); | ||
| 122 | } | ||
| 123 | if wheel_pos < 170 { | ||
| 124 | wheel_pos -= 85; | ||
| 125 | return (0, wheel_pos * 3, 255 - wheel_pos * 3).into(); | ||
| 126 | } | ||
| 127 | wheel_pos -= 170; | ||
| 128 | (wheel_pos * 3, 255 - wheel_pos * 3, 0).into() | ||
| 129 | } | ||
| 130 | |||
| 131 | #[embassy_executor::main] | ||
| 132 | async fn main(_spawner: Spawner) { | ||
| 133 | info!("Start"); | ||
| 134 | let p = embassy_rp::init(Default::default()); | ||
| 135 | |||
| 136 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); | ||
| 137 | |||
| 138 | // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit | ||
| 139 | // feather boards for the 2040 both have one built in. | ||
| 140 | const NUM_LEDS: usize = 1; | ||
| 141 | let mut data = [RGB8::default(); NUM_LEDS]; | ||
| 142 | |||
| 143 | // For the thing plus, use pin 8 | ||
| 144 | // For the feather, use pin 16 | ||
| 145 | let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16); | ||
| 146 | |||
| 147 | // Loop forever making RGB values and pushing them out to the WS2812. | ||
| 148 | loop { | ||
| 149 | for j in 0..(256 * 5) { | ||
| 150 | debug!("New Colors:"); | ||
| 151 | for i in 0..NUM_LEDS { | ||
| 152 | data[i] = wheel((((i * 256) as u16 / NUM_LEDS as u16 + j as u16) & 255) as u8); | ||
| 153 | debug!("R: {} G: {} B: {}", data[i].r, data[i].g, data[i].b); | ||
| 154 | } | ||
| 155 | ws2812.write(&data).await; | ||
| 156 | |||
| 157 | Timer::after(Duration::from_micros(5)).await; | ||
| 158 | } | ||
| 159 | } | ||
| 160 | } | ||
diff --git a/examples/rp/src/bin/pwm.rs b/examples/rp/src/bin/pwm.rs new file mode 100644 index 000000000..9d919287c --- /dev/null +++ b/examples/rp/src/bin/pwm.rs | |||
| @@ -0,0 +1,30 @@ | |||
| 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 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_rp::pwm::{Config, Pwm}; | ||
| 12 | use embassy_time::{Duration, Timer}; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | #[embassy_executor::main] | ||
| 16 | async fn main(_spawner: Spawner) { | ||
| 17 | let p = embassy_rp::init(Default::default()); | ||
| 18 | |||
| 19 | let mut c: Config = Default::default(); | ||
| 20 | c.top = 0x8000; | ||
| 21 | c.compare_b = 8; | ||
| 22 | let mut pwm = Pwm::new_output_b(p.PWM_CH4, p.PIN_25, c.clone()); | ||
| 23 | |||
| 24 | loop { | ||
| 25 | info!("current LED duty cycle: {}/32768", c.compare_b); | ||
| 26 | Timer::after(Duration::from_secs(1)).await; | ||
| 27 | c.compare_b = c.compare_b.rotate_left(4); | ||
| 28 | pwm.set_config(&c); | ||
| 29 | } | ||
| 30 | } | ||
diff --git a/examples/rp/src/bin/rtc.rs b/examples/rp/src/bin/rtc.rs new file mode 100644 index 000000000..15aa8243f --- /dev/null +++ b/examples/rp/src/bin/rtc.rs | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | //! This example shows how to use RTC (Real Time Clock) in the RP2040 chip. | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | #![feature(type_alias_impl_trait)] | ||
| 6 | |||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_rp::rtc::{DateTime, DayOfWeek, Rtc}; | ||
| 10 | use embassy_time::{Duration, Timer}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | #[embassy_executor::main] | ||
| 14 | async fn main(_spawner: Spawner) { | ||
| 15 | let p = embassy_rp::init(Default::default()); | ||
| 16 | info!("Wait for 20s"); | ||
| 17 | |||
| 18 | let mut rtc = Rtc::new(p.RTC); | ||
| 19 | |||
| 20 | if !rtc.is_running() { | ||
| 21 | info!("Start RTC"); | ||
| 22 | let now = DateTime { | ||
| 23 | year: 2000, | ||
| 24 | month: 1, | ||
| 25 | day: 1, | ||
| 26 | day_of_week: DayOfWeek::Saturday, | ||
| 27 | hour: 0, | ||
| 28 | minute: 0, | ||
| 29 | second: 0, | ||
| 30 | }; | ||
| 31 | rtc.set_datetime(now).unwrap(); | ||
| 32 | } | ||
| 33 | |||
| 34 | Timer::after(Duration::from_millis(20000)).await; | ||
| 35 | |||
| 36 | if let Ok(dt) = rtc.now() { | ||
| 37 | info!( | ||
| 38 | "Now: {}-{:02}-{:02} {}:{:02}:{:02}", | ||
| 39 | dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, | ||
| 40 | ); | ||
| 41 | } | ||
| 42 | |||
| 43 | info!("Reboot."); | ||
| 44 | Timer::after(Duration::from_millis(200)).await; | ||
| 45 | cortex_m::peripheral::SCB::sys_reset(); | ||
| 46 | } | ||
diff --git a/examples/rp/src/bin/spi.rs b/examples/rp/src/bin/spi.rs index 88003ee17..602348f7a 100644 --- a/examples/rp/src/bin/spi.rs +++ b/examples/rp/src/bin/spi.rs | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | //! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! Example for resistive touch sensor in Waveshare Pico-ResTouch | ||
| 4 | |||
| 1 | #![no_std] | 5 | #![no_std] |
| 2 | #![no_main] | 6 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 7 | #![feature(type_alias_impl_trait)] |
| @@ -24,7 +28,7 @@ async fn main(_spawner: Spawner) { | |||
| 24 | // create SPI | 28 | // create SPI |
| 25 | let mut config = spi::Config::default(); | 29 | let mut config = spi::Config::default(); |
| 26 | config.frequency = 2_000_000; | 30 | config.frequency = 2_000_000; |
| 27 | let mut spi = Spi::new(p.SPI1, clk, mosi, miso, config); | 31 | let mut spi = Spi::new_blocking(p.SPI1, clk, mosi, miso, config); |
| 28 | 32 | ||
| 29 | // Configure CS | 33 | // Configure CS |
| 30 | let mut cs = Output::new(touch_cs, Level::Low); | 34 | let mut cs = Output::new(touch_cs, Level::Low); |
diff --git a/examples/rp/src/bin/spi_async.rs b/examples/rp/src/bin/spi_async.rs new file mode 100644 index 000000000..328074e8b --- /dev/null +++ b/examples/rp/src/bin/spi_async.rs | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | //! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip. | ||
| 2 | //! No specific hardware is specified in this example. If you connect pin 11 and 12 you should get the same data back. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_rp::spi::{Config, Spi}; | ||
| 11 | use embassy_time::{Duration, Timer}; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | #[embassy_executor::main] | ||
| 15 | async fn main(_spawner: Spawner) { | ||
| 16 | let p = embassy_rp::init(Default::default()); | ||
| 17 | info!("Hello World!"); | ||
| 18 | |||
| 19 | let miso = p.PIN_12; | ||
| 20 | let mosi = p.PIN_11; | ||
| 21 | let clk = p.PIN_10; | ||
| 22 | |||
| 23 | let mut spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); | ||
| 24 | |||
| 25 | loop { | ||
| 26 | let tx_buf = [1_u8, 2, 3, 4, 5, 6]; | ||
| 27 | let mut rx_buf = [0_u8; 6]; | ||
| 28 | spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); | ||
| 29 | info!("{:?}", rx_buf); | ||
| 30 | Timer::after(Duration::from_secs(1)).await; | ||
| 31 | } | ||
| 32 | } | ||
diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs index f0e54d87f..26c258e1c 100644 --- a/examples/rp/src/bin/spi_display.rs +++ b/examples/rp/src/bin/spi_display.rs | |||
| @@ -1,3 +1,8 @@ | |||
| 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 | |||
| 1 | #![no_std] | 6 | #![no_std] |
| 2 | #![no_main] | 7 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 8 | #![feature(type_alias_impl_trait)] |
| @@ -5,10 +10,13 @@ | |||
| 5 | use core::cell::RefCell; | 10 | use core::cell::RefCell; |
| 6 | 11 | ||
| 7 | use defmt::*; | 12 | use defmt::*; |
| 13 | use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig; | ||
| 8 | use embassy_executor::Spawner; | 14 | use embassy_executor::Spawner; |
| 9 | use embassy_rp::gpio::{Level, Output}; | 15 | use embassy_rp::gpio::{Level, Output}; |
| 10 | use embassy_rp::spi; | 16 | use embassy_rp::spi; |
| 11 | use embassy_rp::spi::Spi; | 17 | use embassy_rp::spi::{Blocking, Spi}; |
| 18 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 19 | use embassy_sync::blocking_mutex::Mutex; | ||
| 12 | use embassy_time::Delay; | 20 | use embassy_time::Delay; |
| 13 | use embedded_graphics::image::{Image, ImageRawLE}; | 21 | use embedded_graphics::image::{Image, ImageRawLE}; |
| 14 | use embedded_graphics::mono_font::ascii::FONT_10X20; | 22 | use embedded_graphics::mono_font::ascii::FONT_10X20; |
| @@ -21,10 +29,9 @@ use st7789::{Orientation, ST7789}; | |||
| 21 | use {defmt_rtt as _, panic_probe as _}; | 29 | use {defmt_rtt as _, panic_probe as _}; |
| 22 | 30 | ||
| 23 | use crate::my_display_interface::SPIDeviceInterface; | 31 | use crate::my_display_interface::SPIDeviceInterface; |
| 24 | use crate::shared_spi::SpiDeviceWithCs; | ||
| 25 | use crate::touch::Touch; | 32 | use crate::touch::Touch; |
| 26 | 33 | ||
| 27 | //const DISPLAY_FREQ: u32 = 64_000_000; | 34 | const DISPLAY_FREQ: u32 = 64_000_000; |
| 28 | const TOUCH_FREQ: u32 = 200_000; | 35 | const TOUCH_FREQ: u32 = 200_000; |
| 29 | 36 | ||
| 30 | #[embassy_executor::main] | 37 | #[embassy_executor::main] |
| @@ -43,15 +50,20 @@ async fn main(_spawner: Spawner) { | |||
| 43 | //let touch_irq = p.PIN_17; | 50 | //let touch_irq = p.PIN_17; |
| 44 | 51 | ||
| 45 | // create SPI | 52 | // create SPI |
| 46 | let mut config = spi::Config::default(); | 53 | let mut display_config = spi::Config::default(); |
| 47 | config.frequency = TOUCH_FREQ; // use the lowest freq | 54 | display_config.frequency = DISPLAY_FREQ; |
| 48 | config.phase = spi::Phase::CaptureOnSecondTransition; | 55 | display_config.phase = spi::Phase::CaptureOnSecondTransition; |
| 49 | config.polarity = spi::Polarity::IdleHigh; | 56 | display_config.polarity = spi::Polarity::IdleHigh; |
| 57 | let mut touch_config = spi::Config::default(); | ||
| 58 | touch_config.frequency = TOUCH_FREQ; | ||
| 59 | touch_config.phase = spi::Phase::CaptureOnSecondTransition; | ||
| 60 | touch_config.polarity = spi::Polarity::IdleHigh; | ||
| 50 | 61 | ||
| 51 | let spi_bus = RefCell::new(Spi::new(p.SPI1, clk, mosi, miso, config)); | 62 | let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone()); |
| 63 | let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi)); | ||
| 52 | 64 | ||
| 53 | let display_spi = SpiDeviceWithCs::new(&spi_bus, Output::new(display_cs, Level::High)); | 65 | let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config); |
| 54 | let touch_spi = SpiDeviceWithCs::new(&spi_bus, Output::new(touch_cs, Level::High)); | 66 | let touch_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(touch_cs, Level::High), touch_config); |
| 55 | 67 | ||
| 56 | let mut touch = Touch::new(touch_spi); | 68 | let mut touch = Touch::new(touch_spi); |
| 57 | 69 | ||
| @@ -103,85 +115,9 @@ async fn main(_spawner: Spawner) { | |||
| 103 | } | 115 | } |
| 104 | } | 116 | } |
| 105 | 117 | ||
| 106 | mod shared_spi { | ||
| 107 | use core::cell::RefCell; | ||
| 108 | use core::fmt::Debug; | ||
| 109 | |||
| 110 | use embedded_hal_1::digital::blocking::OutputPin; | ||
| 111 | use embedded_hal_1::spi; | ||
| 112 | use embedded_hal_1::spi::blocking::SpiDevice; | ||
| 113 | |||
| 114 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 115 | pub enum SpiDeviceWithCsError<BUS, CS> { | ||
| 116 | #[allow(unused)] // will probably use in the future when adding a flush() to SpiBus | ||
| 117 | Spi(BUS), | ||
| 118 | Cs(CS), | ||
| 119 | } | ||
| 120 | |||
| 121 | impl<BUS, CS> spi::Error for SpiDeviceWithCsError<BUS, CS> | ||
| 122 | where | ||
| 123 | BUS: spi::Error + Debug, | ||
| 124 | CS: Debug, | ||
| 125 | { | ||
| 126 | fn kind(&self) -> spi::ErrorKind { | ||
| 127 | match self { | ||
| 128 | Self::Spi(e) => e.kind(), | ||
| 129 | Self::Cs(_) => spi::ErrorKind::Other, | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | pub struct SpiDeviceWithCs<'a, BUS, CS> { | ||
| 135 | bus: &'a RefCell<BUS>, | ||
| 136 | cs: CS, | ||
| 137 | } | ||
| 138 | |||
| 139 | impl<'a, BUS, CS> SpiDeviceWithCs<'a, BUS, CS> { | ||
| 140 | pub fn new(bus: &'a RefCell<BUS>, cs: CS) -> Self { | ||
| 141 | Self { bus, cs } | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | impl<'a, BUS, CS> spi::ErrorType for SpiDeviceWithCs<'a, BUS, CS> | ||
| 146 | where | ||
| 147 | BUS: spi::ErrorType, | ||
| 148 | CS: OutputPin, | ||
| 149 | { | ||
| 150 | type Error = SpiDeviceWithCsError<BUS::Error, CS::Error>; | ||
| 151 | } | ||
| 152 | |||
| 153 | impl<'a, BUS, CS> SpiDevice for SpiDeviceWithCs<'a, BUS, CS> | ||
| 154 | where | ||
| 155 | BUS: spi::blocking::SpiBusFlush, | ||
| 156 | CS: OutputPin, | ||
| 157 | { | ||
| 158 | type Bus = BUS; | ||
| 159 | |||
| 160 | fn transaction<R>( | ||
| 161 | &mut self, | ||
| 162 | f: impl FnOnce(&mut Self::Bus) -> Result<R, BUS::Error>, | ||
| 163 | ) -> Result<R, Self::Error> { | ||
| 164 | let mut bus = self.bus.borrow_mut(); | ||
| 165 | self.cs.set_low().map_err(SpiDeviceWithCsError::Cs)?; | ||
| 166 | |||
| 167 | let f_res = f(&mut bus); | ||
| 168 | |||
| 169 | // On failure, it's important to still flush and deassert CS. | ||
| 170 | let flush_res = bus.flush(); | ||
| 171 | let cs_res = self.cs.set_high(); | ||
| 172 | |||
| 173 | let f_res = f_res.map_err(SpiDeviceWithCsError::Spi)?; | ||
| 174 | flush_res.map_err(SpiDeviceWithCsError::Spi)?; | ||
| 175 | cs_res.map_err(SpiDeviceWithCsError::Cs)?; | ||
| 176 | |||
| 177 | Ok(f_res) | ||
| 178 | } | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | /// Driver for the XPT2046 resistive touchscreen sensor | 118 | /// Driver for the XPT2046 resistive touchscreen sensor |
| 183 | mod touch { | 119 | mod touch { |
| 184 | use embedded_hal_1::spi::blocking::{SpiBus, SpiBusRead, SpiBusWrite, SpiDevice}; | 120 | use embedded_hal_1::spi::{Operation, SpiDevice}; |
| 185 | 121 | ||
| 186 | struct Calibration { | 122 | struct Calibration { |
| 187 | x1: i32, | 123 | x1: i32, |
| @@ -208,7 +144,6 @@ mod touch { | |||
| 208 | impl<SPI> Touch<SPI> | 144 | impl<SPI> Touch<SPI> |
| 209 | where | 145 | where |
| 210 | SPI: SpiDevice, | 146 | SPI: SpiDevice, |
| 211 | SPI::Bus: SpiBus, | ||
| 212 | { | 147 | { |
| 213 | pub fn new(spi: SPI) -> Self { | 148 | pub fn new(spi: SPI) -> Self { |
| 214 | Self { spi } | 149 | Self { spi } |
| @@ -218,13 +153,12 @@ mod touch { | |||
| 218 | let mut x = [0; 2]; | 153 | let mut x = [0; 2]; |
| 219 | let mut y = [0; 2]; | 154 | let mut y = [0; 2]; |
| 220 | self.spi | 155 | self.spi |
| 221 | .transaction(|bus| { | 156 | .transaction(&mut [ |
| 222 | bus.write(&[0x90])?; | 157 | Operation::Write(&[0x90]), |
| 223 | bus.read(&mut x)?; | 158 | Operation::Read(&mut x), |
| 224 | bus.write(&[0xd0])?; | 159 | Operation::Write(&[0xd0]), |
| 225 | bus.read(&mut y)?; | 160 | Operation::Read(&mut y), |
| 226 | Ok(()) | 161 | ]) |
| 227 | }) | ||
| 228 | .unwrap(); | 162 | .unwrap(); |
| 229 | 163 | ||
| 230 | let x = (u16::from_be_bytes(x) >> 3) as i32; | 164 | let x = (u16::from_be_bytes(x) >> 3) as i32; |
| @@ -245,8 +179,8 @@ mod touch { | |||
| 245 | 179 | ||
| 246 | mod my_display_interface { | 180 | mod my_display_interface { |
| 247 | use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; | 181 | use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; |
| 248 | use embedded_hal_1::digital::blocking::OutputPin; | 182 | use embedded_hal_1::digital::OutputPin; |
| 249 | use embedded_hal_1::spi::blocking::{SpiBusWrite, SpiDevice}; | 183 | use embedded_hal_1::spi::SpiDevice; |
| 250 | 184 | ||
| 251 | /// SPI display interface. | 185 | /// SPI display interface. |
| 252 | /// | 186 | /// |
| @@ -259,7 +193,6 @@ mod my_display_interface { | |||
| 259 | impl<SPI, DC> SPIDeviceInterface<SPI, DC> | 193 | impl<SPI, DC> SPIDeviceInterface<SPI, DC> |
| 260 | where | 194 | where |
| 261 | SPI: SpiDevice, | 195 | SPI: SpiDevice, |
| 262 | SPI::Bus: SpiBusWrite, | ||
| 263 | DC: OutputPin, | 196 | DC: OutputPin, |
| 264 | { | 197 | { |
| 265 | /// Create new SPI interface for communciation with a display driver | 198 | /// Create new SPI interface for communciation with a display driver |
| @@ -271,41 +204,26 @@ mod my_display_interface { | |||
| 271 | impl<SPI, DC> WriteOnlyDataCommand for SPIDeviceInterface<SPI, DC> | 204 | impl<SPI, DC> WriteOnlyDataCommand for SPIDeviceInterface<SPI, DC> |
| 272 | where | 205 | where |
| 273 | SPI: SpiDevice, | 206 | SPI: SpiDevice, |
| 274 | SPI::Bus: SpiBusWrite, | ||
| 275 | DC: OutputPin, | 207 | DC: OutputPin, |
| 276 | { | 208 | { |
| 277 | fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> { | 209 | fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> { |
| 278 | let r = self.spi.transaction(|bus| { | 210 | // 1 = data, 0 = command |
| 279 | // 1 = data, 0 = command | 211 | self.dc.set_low().map_err(|_| DisplayError::DCError)?; |
| 280 | if let Err(_) = self.dc.set_low() { | ||
| 281 | return Ok(Err(DisplayError::DCError)); | ||
| 282 | } | ||
| 283 | |||
| 284 | // Send words over SPI | ||
| 285 | send_u8(bus, cmds)?; | ||
| 286 | 212 | ||
| 287 | Ok(Ok(())) | 213 | send_u8(&mut self.spi, cmds).map_err(|_| DisplayError::BusWriteError)?; |
| 288 | }); | 214 | Ok(()) |
| 289 | r.map_err(|_| DisplayError::BusWriteError)? | ||
| 290 | } | 215 | } |
| 291 | 216 | ||
| 292 | fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> { | 217 | fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> { |
| 293 | let r = self.spi.transaction(|bus| { | 218 | // 1 = data, 0 = command |
| 294 | // 1 = data, 0 = command | 219 | self.dc.set_high().map_err(|_| DisplayError::DCError)?; |
| 295 | if let Err(_) = self.dc.set_high() { | ||
| 296 | return Ok(Err(DisplayError::DCError)); | ||
| 297 | } | ||
| 298 | |||
| 299 | // Send words over SPI | ||
| 300 | send_u8(bus, buf)?; | ||
| 301 | 220 | ||
| 302 | Ok(Ok(())) | 221 | send_u8(&mut self.spi, buf).map_err(|_| DisplayError::BusWriteError)?; |
| 303 | }); | 222 | Ok(()) |
| 304 | r.map_err(|_| DisplayError::BusWriteError)? | ||
| 305 | } | 223 | } |
| 306 | } | 224 | } |
| 307 | 225 | ||
| 308 | fn send_u8<T: SpiBusWrite>(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> { | 226 | fn send_u8<T: SpiDevice>(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> { |
| 309 | match words { | 227 | match words { |
| 310 | DataFormat::U8(slice) => spi.write(slice), | 228 | DataFormat::U8(slice) => spi.write(slice), |
| 311 | DataFormat::U16(slice) => { | 229 | DataFormat::U16(slice) => { |
diff --git a/examples/rp/src/bin/uart.rs b/examples/rp/src/bin/uart.rs index c63b31cae..451c3c396 100644 --- a/examples/rp/src/bin/uart.rs +++ b/examples/rp/src/bin/uart.rs | |||
| @@ -1,3 +1,9 @@ | |||
| 1 | //! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip. | ||
| 2 | //! | ||
| 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 | ||
| 5 | //! with its UART port. | ||
| 6 | |||
| 1 | #![no_std] | 7 | #![no_std] |
| 2 | #![no_main] | 8 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 9 | #![feature(type_alias_impl_trait)] |
| @@ -10,7 +16,7 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 10 | async fn main(_spawner: Spawner) { | 16 | async fn main(_spawner: Spawner) { |
| 11 | let p = embassy_rp::init(Default::default()); | 17 | let p = embassy_rp::init(Default::default()); |
| 12 | let config = uart::Config::default(); | 18 | let config = uart::Config::default(); |
| 13 | let mut uart = uart::Uart::new_with_rtscts(p.UART0, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, config); | 19 | let mut uart = uart::Uart::new_with_rtscts_blocking(p.UART0, p.PIN_0, p.PIN_1, p.PIN_3, p.PIN_2, config); |
| 14 | uart.blocking_write("Hello World!\r\n".as_bytes()).unwrap(); | 20 | uart.blocking_write("Hello World!\r\n".as_bytes()).unwrap(); |
| 15 | 21 | ||
| 16 | loop { | 22 | loop { |
diff --git a/examples/rp/src/bin/uart_buffered_split.rs b/examples/rp/src/bin/uart_buffered_split.rs new file mode 100644 index 000000000..735201718 --- /dev/null +++ b/examples/rp/src/bin/uart_buffered_split.rs | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | //! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip. | ||
| 2 | //! | ||
| 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 | ||
| 5 | //! with its UART port. | ||
| 6 | |||
| 7 | #![no_std] | ||
| 8 | #![no_main] | ||
| 9 | #![feature(type_alias_impl_trait)] | ||
| 10 | |||
| 11 | use defmt::*; | ||
| 12 | use embassy_executor::Spawner; | ||
| 13 | use embassy_rp::bind_interrupts; | ||
| 14 | use embassy_rp::peripherals::UART0; | ||
| 15 | use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config}; | ||
| 16 | use embassy_time::{Duration, Timer}; | ||
| 17 | use embedded_io::asynch::{Read, Write}; | ||
| 18 | use static_cell::make_static; | ||
| 19 | use {defmt_rtt as _, panic_probe as _}; | ||
| 20 | |||
| 21 | bind_interrupts!(struct Irqs { | ||
| 22 | UART0_IRQ => BufferedInterruptHandler<UART0>; | ||
| 23 | }); | ||
| 24 | |||
| 25 | #[embassy_executor::main] | ||
| 26 | async fn main(spawner: Spawner) { | ||
| 27 | let p = embassy_rp::init(Default::default()); | ||
| 28 | let (tx_pin, rx_pin, uart) = (p.PIN_0, p.PIN_1, p.UART0); | ||
| 29 | |||
| 30 | let tx_buf = &mut make_static!([0u8; 16])[..]; | ||
| 31 | let rx_buf = &mut make_static!([0u8; 16])[..]; | ||
| 32 | let uart = BufferedUart::new(uart, Irqs, tx_pin, rx_pin, tx_buf, rx_buf, Config::default()); | ||
| 33 | let (rx, mut tx) = uart.split(); | ||
| 34 | |||
| 35 | unwrap!(spawner.spawn(reader(rx))); | ||
| 36 | |||
| 37 | info!("Writing..."); | ||
| 38 | loop { | ||
| 39 | let data = [ | ||
| 40 | 1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, | ||
| 41 | 29, 30, 31, | ||
| 42 | ]; | ||
| 43 | info!("TX {:?}", data); | ||
| 44 | tx.write_all(&data).await.unwrap(); | ||
| 45 | Timer::after(Duration::from_secs(1)).await; | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | #[embassy_executor::task] | ||
| 50 | async fn reader(mut rx: BufferedUartRx<'static, UART0>) { | ||
| 51 | info!("Reading..."); | ||
| 52 | loop { | ||
| 53 | let mut buf = [0; 31]; | ||
| 54 | rx.read_exact(&mut buf).await.unwrap(); | ||
| 55 | info!("RX {:?}", buf); | ||
| 56 | } | ||
| 57 | } | ||
diff --git a/examples/rp/src/bin/uart_unidir.rs b/examples/rp/src/bin/uart_unidir.rs new file mode 100644 index 000000000..c1515a911 --- /dev/null +++ b/examples/rp/src/bin/uart_unidir.rs | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | //! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! Test TX-only and RX-only on two different UARTs. You need to connect GPIO0 to GPIO5 for | ||
| 4 | //! this to work | ||
| 5 | //! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used | ||
| 6 | //! with its UART port. | ||
| 7 | |||
| 8 | #![no_std] | ||
| 9 | #![no_main] | ||
| 10 | #![feature(type_alias_impl_trait)] | ||
| 11 | |||
| 12 | use defmt::*; | ||
| 13 | use embassy_executor::Spawner; | ||
| 14 | use embassy_rp::bind_interrupts; | ||
| 15 | use embassy_rp::peripherals::UART1; | ||
| 16 | use embassy_rp::uart::{Async, Config, InterruptHandler, UartRx, UartTx}; | ||
| 17 | use embassy_time::{Duration, Timer}; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | bind_interrupts!(struct Irqs { | ||
| 21 | UART1_IRQ => InterruptHandler<UART1>; | ||
| 22 | }); | ||
| 23 | |||
| 24 | #[embassy_executor::main] | ||
| 25 | async fn main(spawner: Spawner) { | ||
| 26 | let p = embassy_rp::init(Default::default()); | ||
| 27 | |||
| 28 | let mut uart_tx = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Config::default()); | ||
| 29 | let uart_rx = UartRx::new(p.UART1, p.PIN_5, Irqs, p.DMA_CH1, Config::default()); | ||
| 30 | |||
| 31 | unwrap!(spawner.spawn(reader(uart_rx))); | ||
| 32 | |||
| 33 | info!("Writing..."); | ||
| 34 | loop { | ||
| 35 | let data = [1u8, 2, 3, 4, 5, 6, 7, 8]; | ||
| 36 | info!("TX {:?}", data); | ||
| 37 | uart_tx.write(&data).await.unwrap(); | ||
| 38 | Timer::after(Duration::from_secs(1)).await; | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | #[embassy_executor::task] | ||
| 43 | async fn reader(mut rx: UartRx<'static, UART1, Async>) { | ||
| 44 | info!("Reading..."); | ||
| 45 | loop { | ||
| 46 | // read a total of 4 transmissions (32 / 8) and then print the result | ||
| 47 | let mut buf = [0; 32]; | ||
| 48 | rx.read(&mut buf).await.unwrap(); | ||
| 49 | info!("RX {:?}", buf); | ||
| 50 | } | ||
| 51 | } | ||
diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs new file mode 100644 index 000000000..0a08f667e --- /dev/null +++ b/examples/rp/src/bin/usb_ethernet.rs | |||
| @@ -0,0 +1,155 @@ | |||
| 1 | //! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! This is a CDC-NCM class implementation, aka Ethernet over USB. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_net::tcp::TcpSocket; | ||
| 12 | use embassy_net::{Stack, StackResources}; | ||
| 13 | use embassy_rp::peripherals::USB; | ||
| 14 | use embassy_rp::usb::{Driver, InterruptHandler}; | ||
| 15 | use embassy_rp::{bind_interrupts, peripherals}; | ||
| 16 | use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; | ||
| 17 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; | ||
| 18 | use embassy_usb::{Builder, Config, UsbDevice}; | ||
| 19 | use embedded_io::asynch::Write; | ||
| 20 | use static_cell::make_static; | ||
| 21 | use {defmt_rtt as _, panic_probe as _}; | ||
| 22 | |||
| 23 | bind_interrupts!(struct Irqs { | ||
| 24 | USBCTRL_IRQ => InterruptHandler<USB>; | ||
| 25 | }); | ||
| 26 | |||
| 27 | type MyDriver = Driver<'static, peripherals::USB>; | ||
| 28 | |||
| 29 | const MTU: usize = 1514; | ||
| 30 | |||
| 31 | #[embassy_executor::task] | ||
| 32 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { | ||
| 33 | device.run().await | ||
| 34 | } | ||
| 35 | |||
| 36 | #[embassy_executor::task] | ||
| 37 | async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { | ||
| 38 | class.run().await | ||
| 39 | } | ||
| 40 | |||
| 41 | #[embassy_executor::task] | ||
| 42 | async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! { | ||
| 43 | stack.run().await | ||
| 44 | } | ||
| 45 | |||
| 46 | #[embassy_executor::main] | ||
| 47 | async fn main(spawner: Spawner) { | ||
| 48 | let p = embassy_rp::init(Default::default()); | ||
| 49 | |||
| 50 | // Create the driver, from the HAL. | ||
| 51 | let driver = Driver::new(p.USB, Irqs); | ||
| 52 | |||
| 53 | // Create embassy-usb Config | ||
| 54 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 55 | config.manufacturer = Some("Embassy"); | ||
| 56 | config.product = Some("USB-Ethernet example"); | ||
| 57 | config.serial_number = Some("12345678"); | ||
| 58 | config.max_power = 100; | ||
| 59 | config.max_packet_size_0 = 64; | ||
| 60 | |||
| 61 | // Required for Windows support. | ||
| 62 | config.composite_with_iads = true; | ||
| 63 | config.device_class = 0xEF; | ||
| 64 | config.device_sub_class = 0x02; | ||
| 65 | config.device_protocol = 0x01; | ||
| 66 | |||
| 67 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 68 | let mut builder = Builder::new( | ||
| 69 | driver, | ||
| 70 | config, | ||
| 71 | &mut make_static!([0; 256])[..], | ||
| 72 | &mut make_static!([0; 256])[..], | ||
| 73 | &mut make_static!([0; 256])[..], | ||
| 74 | &mut make_static!([0; 128])[..], | ||
| 75 | ); | ||
| 76 | |||
| 77 | // Our MAC addr. | ||
| 78 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; | ||
| 79 | // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. | ||
| 80 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; | ||
| 81 | |||
| 82 | // Create classes on the builder. | ||
| 83 | let class = CdcNcmClass::new(&mut builder, make_static!(State::new()), host_mac_addr, 64); | ||
| 84 | |||
| 85 | // Build the builder. | ||
| 86 | let usb = builder.build(); | ||
| 87 | |||
| 88 | unwrap!(spawner.spawn(usb_task(usb))); | ||
| 89 | |||
| 90 | let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(make_static!(NetState::new()), our_mac_addr); | ||
| 91 | unwrap!(spawner.spawn(usb_ncm_task(runner))); | ||
| 92 | |||
| 93 | let config = embassy_net::Config::dhcpv4(Default::default()); | ||
| 94 | //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { | ||
| 95 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | ||
| 96 | // dns_servers: Vec::new(), | ||
| 97 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | ||
| 98 | //}); | ||
| 99 | |||
| 100 | // Generate random seed | ||
| 101 | let seed = 1234; // guaranteed random, chosen by a fair dice roll | ||
| 102 | |||
| 103 | // Init network stack | ||
| 104 | let stack = &*make_static!(Stack::new( | ||
| 105 | device, | ||
| 106 | config, | ||
| 107 | make_static!(StackResources::<2>::new()), | ||
| 108 | seed | ||
| 109 | )); | ||
| 110 | |||
| 111 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 112 | |||
| 113 | // And now we can use it! | ||
| 114 | |||
| 115 | let mut rx_buffer = [0; 4096]; | ||
| 116 | let mut tx_buffer = [0; 4096]; | ||
| 117 | let mut buf = [0; 4096]; | ||
| 118 | |||
| 119 | loop { | ||
| 120 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 121 | socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); | ||
| 122 | |||
| 123 | info!("Listening on TCP:1234..."); | ||
| 124 | if let Err(e) = socket.accept(1234).await { | ||
| 125 | warn!("accept error: {:?}", e); | ||
| 126 | continue; | ||
| 127 | } | ||
| 128 | |||
| 129 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 130 | |||
| 131 | loop { | ||
| 132 | let n = match socket.read(&mut buf).await { | ||
| 133 | Ok(0) => { | ||
| 134 | warn!("read EOF"); | ||
| 135 | break; | ||
| 136 | } | ||
| 137 | Ok(n) => n, | ||
| 138 | Err(e) => { | ||
| 139 | warn!("read error: {:?}", e); | ||
| 140 | break; | ||
| 141 | } | ||
| 142 | }; | ||
| 143 | |||
| 144 | info!("rxd {:02x}", &buf[..n]); | ||
| 145 | |||
| 146 | match socket.write_all(&buf[..n]).await { | ||
| 147 | Ok(()) => {} | ||
| 148 | Err(e) => { | ||
| 149 | warn!("write error: {:?}", e); | ||
| 150 | break; | ||
| 151 | } | ||
| 152 | }; | ||
| 153 | } | ||
| 154 | } | ||
| 155 | } | ||
diff --git a/examples/rp/src/bin/usb_hid_keyboard.rs b/examples/rp/src/bin/usb_hid_keyboard.rs new file mode 100644 index 000000000..99af1f02f --- /dev/null +++ b/examples/rp/src/bin/usb_hid_keyboard.rs | |||
| @@ -0,0 +1,188 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 6 | |||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_futures::join::join; | ||
| 10 | use embassy_rp::bind_interrupts; | ||
| 11 | use embassy_rp::gpio::{Input, Pull}; | ||
| 12 | use embassy_rp::peripherals::USB; | ||
| 13 | use embassy_rp::usb::{Driver, InterruptHandler}; | ||
| 14 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; | ||
| 15 | use embassy_usb::control::OutResponse; | ||
| 16 | use embassy_usb::{Builder, Config, Handler}; | ||
| 17 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | bind_interrupts!(struct Irqs { | ||
| 21 | USBCTRL_IRQ => InterruptHandler<USB>; | ||
| 22 | }); | ||
| 23 | |||
| 24 | #[embassy_executor::main] | ||
| 25 | async fn main(_spawner: Spawner) { | ||
| 26 | let p = embassy_rp::init(Default::default()); | ||
| 27 | // Create the driver, from the HAL. | ||
| 28 | let driver = Driver::new(p.USB, Irqs); | ||
| 29 | |||
| 30 | // Create embassy-usb Config | ||
| 31 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 32 | config.manufacturer = Some("Embassy"); | ||
| 33 | config.product = Some("HID keyboard example"); | ||
| 34 | config.serial_number = Some("12345678"); | ||
| 35 | config.max_power = 100; | ||
| 36 | config.max_packet_size_0 = 64; | ||
| 37 | |||
| 38 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 39 | // It needs some buffers for building the descriptors. | ||
| 40 | let mut device_descriptor = [0; 256]; | ||
| 41 | let mut config_descriptor = [0; 256]; | ||
| 42 | let mut bos_descriptor = [0; 256]; | ||
| 43 | // You can also add a Microsoft OS descriptor. | ||
| 44 | // let mut msos_descriptor = [0; 256]; | ||
| 45 | let mut control_buf = [0; 64]; | ||
| 46 | let request_handler = MyRequestHandler {}; | ||
| 47 | let mut device_handler = MyDeviceHandler::new(); | ||
| 48 | |||
| 49 | let mut state = State::new(); | ||
| 50 | |||
| 51 | let mut builder = Builder::new( | ||
| 52 | driver, | ||
| 53 | config, | ||
| 54 | &mut device_descriptor, | ||
| 55 | &mut config_descriptor, | ||
| 56 | &mut bos_descriptor, | ||
| 57 | // &mut msos_descriptor, | ||
| 58 | &mut control_buf, | ||
| 59 | ); | ||
| 60 | |||
| 61 | builder.handler(&mut device_handler); | ||
| 62 | |||
| 63 | // Create classes on the builder. | ||
| 64 | let config = embassy_usb::class::hid::Config { | ||
| 65 | report_descriptor: KeyboardReport::desc(), | ||
| 66 | request_handler: Some(&request_handler), | ||
| 67 | poll_ms: 60, | ||
| 68 | max_packet_size: 64, | ||
| 69 | }; | ||
| 70 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); | ||
| 71 | |||
| 72 | // Build the builder. | ||
| 73 | let mut usb = builder.build(); | ||
| 74 | |||
| 75 | // Run the USB device. | ||
| 76 | let usb_fut = usb.run(); | ||
| 77 | |||
| 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 | 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, &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(&self, id: ReportId, _buf: &mut [u8]) -> Option<usize> { | ||
| 129 | info!("Get report for {:?}", id); | ||
| 130 | None | ||
| 131 | } | ||
| 132 | |||
| 133 | fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse { | ||
| 134 | info!("Set report for {:?}: {=[u8]}", id, data); | ||
| 135 | OutResponse::Accepted | ||
| 136 | } | ||
| 137 | |||
| 138 | fn set_idle_ms(&self, id: Option<ReportId>, dur: u32) { | ||
| 139 | info!("Set idle rate for {:?} to {:?}", id, dur); | ||
| 140 | } | ||
| 141 | |||
| 142 | fn get_idle_ms(&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/rp/src/bin/usb_logger.rs b/examples/rp/src/bin/usb_logger.rs new file mode 100644 index 000000000..9c5e6897d --- /dev/null +++ b/examples/rp/src/bin/usb_logger.rs | |||
| @@ -0,0 +1,37 @@ | |||
| 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 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_rp::bind_interrupts; | ||
| 11 | use embassy_rp::peripherals::USB; | ||
| 12 | use embassy_rp::usb::{Driver, InterruptHandler}; | ||
| 13 | use embassy_time::{Duration, Timer}; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | bind_interrupts!(struct Irqs { | ||
| 17 | USBCTRL_IRQ => InterruptHandler<USB>; | ||
| 18 | }); | ||
| 19 | |||
| 20 | #[embassy_executor::task] | ||
| 21 | async fn logger_task(driver: Driver<'static, USB>) { | ||
| 22 | embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver); | ||
| 23 | } | ||
| 24 | |||
| 25 | #[embassy_executor::main] | ||
| 26 | async fn main(spawner: Spawner) { | ||
| 27 | let p = embassy_rp::init(Default::default()); | ||
| 28 | let driver = Driver::new(p.USB, Irqs); | ||
| 29 | spawner.spawn(logger_task(driver)).unwrap(); | ||
| 30 | |||
| 31 | let mut counter = 0; | ||
| 32 | loop { | ||
| 33 | counter += 1; | ||
| 34 | log::info!("Tick {}", counter); | ||
| 35 | Timer::after(Duration::from_secs(1)).await; | ||
| 36 | } | ||
| 37 | } | ||
diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs new file mode 100644 index 000000000..164e2052d --- /dev/null +++ b/examples/rp/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,109 @@ | |||
| 1 | //! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! This creates a USB serial port that echos. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::{info, panic}; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_futures::join::join; | ||
| 12 | use embassy_rp::bind_interrupts; | ||
| 13 | use embassy_rp::peripherals::USB; | ||
| 14 | use embassy_rp::usb::{Driver, Instance, InterruptHandler}; | ||
| 15 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 16 | use embassy_usb::driver::EndpointError; | ||
| 17 | use embassy_usb::{Builder, Config}; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | bind_interrupts!(struct Irqs { | ||
| 21 | USBCTRL_IRQ => InterruptHandler<USB>; | ||
| 22 | }); | ||
| 23 | |||
| 24 | #[embassy_executor::main] | ||
| 25 | async fn main(_spawner: Spawner) { | ||
| 26 | info!("Hello there!"); | ||
| 27 | |||
| 28 | let p = embassy_rp::init(Default::default()); | ||
| 29 | |||
| 30 | // Create the driver, from the HAL. | ||
| 31 | let driver = Driver::new(p.USB, Irqs); | ||
| 32 | |||
| 33 | // Create embassy-usb Config | ||
| 34 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 35 | config.manufacturer = Some("Embassy"); | ||
| 36 | config.product = Some("USB-serial example"); | ||
| 37 | config.serial_number = Some("12345678"); | ||
| 38 | config.max_power = 100; | ||
| 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 | |||
| 48 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 49 | // It needs some buffers for building the descriptors. | ||
| 50 | let mut device_descriptor = [0; 256]; | ||
| 51 | let mut config_descriptor = [0; 256]; | ||
| 52 | let mut bos_descriptor = [0; 256]; | ||
| 53 | let mut control_buf = [0; 64]; | ||
| 54 | |||
| 55 | let mut state = State::new(); | ||
| 56 | |||
| 57 | let mut builder = Builder::new( | ||
| 58 | driver, | ||
| 59 | config, | ||
| 60 | &mut device_descriptor, | ||
| 61 | &mut config_descriptor, | ||
| 62 | &mut bos_descriptor, | ||
| 63 | &mut control_buf, | ||
| 64 | ); | ||
| 65 | |||
| 66 | // Create classes on the builder. | ||
| 67 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 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 | // Do stuff with the class! | ||
| 76 | let echo_fut = async { | ||
| 77 | loop { | ||
| 78 | class.wait_connection().await; | ||
| 79 | info!("Connected"); | ||
| 80 | let _ = echo(&mut class).await; | ||
| 81 | info!("Disconnected"); | ||
| 82 | } | ||
| 83 | }; | ||
| 84 | |||
| 85 | // Run everything concurrently. | ||
| 86 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 87 | join(usb_fut, echo_fut).await; | ||
| 88 | } | ||
| 89 | |||
| 90 | struct Disconnected {} | ||
| 91 | |||
| 92 | impl From<EndpointError> for Disconnected { | ||
| 93 | fn from(val: EndpointError) -> Self { | ||
| 94 | match val { | ||
| 95 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 96 | EndpointError::Disabled => Disconnected {}, | ||
| 97 | } | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 102 | let mut buf = [0; 64]; | ||
| 103 | loop { | ||
| 104 | let n = class.read_packet(&mut buf).await?; | ||
| 105 | let data = &buf[..n]; | ||
| 106 | info!("data: {:x}", data); | ||
| 107 | class.write_packet(data).await?; | ||
| 108 | } | ||
| 109 | } | ||
diff --git a/examples/rp/src/bin/watchdog.rs b/examples/rp/src/bin/watchdog.rs new file mode 100644 index 000000000..fe5eaf926 --- /dev/null +++ b/examples/rp/src/bin/watchdog.rs | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | //! This example shows how to use Watchdog in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! It does not work with the RP Pico W board. See wifi_blinky.rs or connect external LED and resistor. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::info; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_rp::gpio; | ||
| 12 | use embassy_rp::watchdog::*; | ||
| 13 | use embassy_time::{Duration, Timer}; | ||
| 14 | use gpio::{Level, Output}; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | let p = embassy_rp::init(Default::default()); | ||
| 20 | info!("Hello world!"); | ||
| 21 | |||
| 22 | let mut watchdog = Watchdog::new(p.WATCHDOG); | ||
| 23 | let mut led = Output::new(p.PIN_25, Level::Low); | ||
| 24 | |||
| 25 | // Set the LED high for 2 seconds so we know when we're about to start the watchdog | ||
| 26 | led.set_high(); | ||
| 27 | Timer::after(Duration::from_secs(2)).await; | ||
| 28 | |||
| 29 | // Set to watchdog to reset if it's not fed within 1.05 seconds, and start it | ||
| 30 | watchdog.start(Duration::from_millis(1_050)); | ||
| 31 | info!("Started the watchdog timer"); | ||
| 32 | |||
| 33 | // Blink once a second for 5 seconds, feed the watchdog timer once a second to avoid a reset | ||
| 34 | for _ in 1..=5 { | ||
| 35 | led.set_low(); | ||
| 36 | Timer::after(Duration::from_millis(500)).await; | ||
| 37 | led.set_high(); | ||
| 38 | Timer::after(Duration::from_millis(500)).await; | ||
| 39 | info!("Feeding watchdog"); | ||
| 40 | watchdog.feed(); | ||
| 41 | } | ||
| 42 | |||
| 43 | info!("Stopped feeding, device will reset in 1.05 seconds"); | ||
| 44 | // Blink 10 times per second, not feeding the watchdog. | ||
| 45 | // The processor should reset in 1.05 seconds. | ||
| 46 | loop { | ||
| 47 | led.set_low(); | ||
| 48 | Timer::after(Duration::from_millis(100)).await; | ||
| 49 | led.set_high(); | ||
| 50 | Timer::after(Duration::from_millis(100)).await; | ||
| 51 | } | ||
| 52 | } | ||
diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs new file mode 100644 index 000000000..e3e393445 --- /dev/null +++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs | |||
| @@ -0,0 +1,139 @@ | |||
| 1 | //! This example uses the RP Pico W board Wifi chip (cyw43). | ||
| 2 | //! Creates an Access point Wifi network and creates a TCP endpoint on port 1234. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | #![feature(async_fn_in_trait)] | ||
| 8 | #![allow(incomplete_features)] | ||
| 9 | |||
| 10 | use core::str::from_utf8; | ||
| 11 | |||
| 12 | use cyw43_pio::PioSpi; | ||
| 13 | use defmt::*; | ||
| 14 | use embassy_executor::Spawner; | ||
| 15 | use embassy_net::tcp::TcpSocket; | ||
| 16 | use embassy_net::{Config, Stack, StackResources}; | ||
| 17 | use embassy_rp::bind_interrupts; | ||
| 18 | use embassy_rp::gpio::{Level, Output}; | ||
| 19 | use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; | ||
| 20 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 21 | use embassy_time::Duration; | ||
| 22 | use embedded_io::asynch::Write; | ||
| 23 | use static_cell::make_static; | ||
| 24 | use {defmt_rtt 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 wifi_task( | ||
| 32 | runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, | ||
| 33 | ) -> ! { | ||
| 34 | runner.run().await | ||
| 35 | } | ||
| 36 | |||
| 37 | #[embassy_executor::task] | ||
| 38 | async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! { | ||
| 39 | stack.run().await | ||
| 40 | } | ||
| 41 | |||
| 42 | #[embassy_executor::main] | ||
| 43 | async fn main(spawner: Spawner) { | ||
| 44 | info!("Hello World!"); | ||
| 45 | |||
| 46 | let p = embassy_rp::init(Default::default()); | ||
| 47 | |||
| 48 | let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); | ||
| 49 | let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); | ||
| 50 | |||
| 51 | // To make flashing faster for development, you may want to flash the firmwares independently | ||
| 52 | // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: | ||
| 53 | // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 | ||
| 54 | // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 | ||
| 55 | //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; | ||
| 56 | //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; | ||
| 57 | |||
| 58 | let pwr = Output::new(p.PIN_23, Level::Low); | ||
| 59 | let cs = Output::new(p.PIN_25, Level::High); | ||
| 60 | let mut pio = Pio::new(p.PIO0, Irqs); | ||
| 61 | let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); | ||
| 62 | |||
| 63 | let state = make_static!(cyw43::State::new()); | ||
| 64 | let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; | ||
| 65 | unwrap!(spawner.spawn(wifi_task(runner))); | ||
| 66 | |||
| 67 | control.init(clm).await; | ||
| 68 | control | ||
| 69 | .set_power_management(cyw43::PowerManagementMode::PowerSave) | ||
| 70 | .await; | ||
| 71 | |||
| 72 | // Use a link-local address for communication without DHCP server | ||
| 73 | let config = Config::ipv4_static(embassy_net::StaticConfigV4 { | ||
| 74 | address: embassy_net::Ipv4Cidr::new(embassy_net::Ipv4Address::new(169, 254, 1, 1), 16), | ||
| 75 | dns_servers: heapless::Vec::new(), | ||
| 76 | gateway: None, | ||
| 77 | }); | ||
| 78 | |||
| 79 | // Generate random seed | ||
| 80 | let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. | ||
| 81 | |||
| 82 | // Init network stack | ||
| 83 | let stack = &*make_static!(Stack::new( | ||
| 84 | net_device, | ||
| 85 | config, | ||
| 86 | make_static!(StackResources::<2>::new()), | ||
| 87 | seed | ||
| 88 | )); | ||
| 89 | |||
| 90 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 91 | |||
| 92 | //control.start_ap_open("cyw43", 5).await; | ||
| 93 | control.start_ap_wpa2("cyw43", "password", 5).await; | ||
| 94 | |||
| 95 | // And now we can use it! | ||
| 96 | |||
| 97 | let mut rx_buffer = [0; 4096]; | ||
| 98 | let mut tx_buffer = [0; 4096]; | ||
| 99 | let mut buf = [0; 4096]; | ||
| 100 | |||
| 101 | loop { | ||
| 102 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 103 | socket.set_timeout(Some(Duration::from_secs(10))); | ||
| 104 | |||
| 105 | control.gpio_set(0, false).await; | ||
| 106 | info!("Listening on TCP:1234..."); | ||
| 107 | if let Err(e) = socket.accept(1234).await { | ||
| 108 | warn!("accept error: {:?}", e); | ||
| 109 | continue; | ||
| 110 | } | ||
| 111 | |||
| 112 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 113 | control.gpio_set(0, true).await; | ||
| 114 | |||
| 115 | loop { | ||
| 116 | let n = match socket.read(&mut buf).await { | ||
| 117 | Ok(0) => { | ||
| 118 | warn!("read EOF"); | ||
| 119 | break; | ||
| 120 | } | ||
| 121 | Ok(n) => n, | ||
| 122 | Err(e) => { | ||
| 123 | warn!("read error: {:?}", e); | ||
| 124 | break; | ||
| 125 | } | ||
| 126 | }; | ||
| 127 | |||
| 128 | info!("rxd {}", from_utf8(&buf[..n]).unwrap()); | ||
| 129 | |||
| 130 | match socket.write_all(&buf[..n]).await { | ||
| 131 | Ok(()) => {} | ||
| 132 | Err(e) => { | ||
| 133 | warn!("write error: {:?}", e); | ||
| 134 | break; | ||
| 135 | } | ||
| 136 | }; | ||
| 137 | } | ||
| 138 | } | ||
| 139 | } | ||
diff --git a/examples/rp/src/bin/wifi_blinky.rs b/examples/rp/src/bin/wifi_blinky.rs new file mode 100644 index 000000000..33d43788c --- /dev/null +++ b/examples/rp/src/bin/wifi_blinky.rs | |||
| @@ -0,0 +1,68 @@ | |||
| 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 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use cyw43_pio::PioSpi; | ||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_rp::bind_interrupts; | ||
| 13 | use embassy_rp::gpio::{Level, Output}; | ||
| 14 | use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; | ||
| 15 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 16 | use embassy_time::{Duration, Timer}; | ||
| 17 | use static_cell::make_static; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | bind_interrupts!(struct Irqs { | ||
| 21 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 22 | }); | ||
| 23 | |||
| 24 | #[embassy_executor::task] | ||
| 25 | async fn wifi_task( | ||
| 26 | runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, | ||
| 27 | ) -> ! { | ||
| 28 | runner.run().await | ||
| 29 | } | ||
| 30 | |||
| 31 | #[embassy_executor::main] | ||
| 32 | async fn main(spawner: Spawner) { | ||
| 33 | let p = embassy_rp::init(Default::default()); | ||
| 34 | let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); | ||
| 35 | let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); | ||
| 36 | |||
| 37 | // To make flashing faster for development, you may want to flash the firmwares independently | ||
| 38 | // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: | ||
| 39 | // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 | ||
| 40 | // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 | ||
| 41 | //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; | ||
| 42 | //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; | ||
| 43 | |||
| 44 | let pwr = Output::new(p.PIN_23, Level::Low); | ||
| 45 | let cs = Output::new(p.PIN_25, Level::High); | ||
| 46 | let mut pio = Pio::new(p.PIO0, Irqs); | ||
| 47 | let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); | ||
| 48 | |||
| 49 | let state = make_static!(cyw43::State::new()); | ||
| 50 | let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; | ||
| 51 | unwrap!(spawner.spawn(wifi_task(runner))); | ||
| 52 | |||
| 53 | control.init(clm).await; | ||
| 54 | control | ||
| 55 | .set_power_management(cyw43::PowerManagementMode::PowerSave) | ||
| 56 | .await; | ||
| 57 | |||
| 58 | let delay = Duration::from_secs(1); | ||
| 59 | loop { | ||
| 60 | info!("led on!"); | ||
| 61 | control.gpio_set(0, true).await; | ||
| 62 | Timer::after(delay).await; | ||
| 63 | |||
| 64 | info!("led off!"); | ||
| 65 | control.gpio_set(0, false).await; | ||
| 66 | Timer::after(delay).await; | ||
| 67 | } | ||
| 68 | } | ||
diff --git a/examples/rp/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs new file mode 100644 index 000000000..743fab617 --- /dev/null +++ b/examples/rp/src/bin/wifi_scan.rs | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | //! This example uses the RP Pico W board Wifi chip (cyw43). | ||
| 2 | //! Scans Wifi for ssid names. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | #![feature(async_fn_in_trait)] | ||
| 8 | #![allow(incomplete_features)] | ||
| 9 | |||
| 10 | use core::str; | ||
| 11 | |||
| 12 | use cyw43_pio::PioSpi; | ||
| 13 | use defmt::*; | ||
| 14 | use embassy_executor::Spawner; | ||
| 15 | use embassy_net::Stack; | ||
| 16 | use embassy_rp::bind_interrupts; | ||
| 17 | use embassy_rp::gpio::{Level, Output}; | ||
| 18 | use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; | ||
| 19 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 20 | use static_cell::make_static; | ||
| 21 | use {defmt_rtt as _, panic_probe as _}; | ||
| 22 | |||
| 23 | bind_interrupts!(struct Irqs { | ||
| 24 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 25 | }); | ||
| 26 | |||
| 27 | #[embassy_executor::task] | ||
| 28 | async fn wifi_task( | ||
| 29 | runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, | ||
| 30 | ) -> ! { | ||
| 31 | runner.run().await | ||
| 32 | } | ||
| 33 | |||
| 34 | #[embassy_executor::task] | ||
| 35 | async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! { | ||
| 36 | stack.run().await | ||
| 37 | } | ||
| 38 | |||
| 39 | #[embassy_executor::main] | ||
| 40 | async fn main(spawner: Spawner) { | ||
| 41 | info!("Hello World!"); | ||
| 42 | |||
| 43 | let p = embassy_rp::init(Default::default()); | ||
| 44 | |||
| 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 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 | ||
| 51 | // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 | ||
| 52 | //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; | ||
| 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(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); | ||
| 59 | |||
| 60 | let state = make_static!(cyw43::State::new()); | ||
| 61 | let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; | ||
| 62 | unwrap!(spawner.spawn(wifi_task(runner))); | ||
| 63 | |||
| 64 | control.init(clm).await; | ||
| 65 | control | ||
| 66 | .set_power_management(cyw43::PowerManagementMode::PowerSave) | ||
| 67 | .await; | ||
| 68 | |||
| 69 | let mut scanner = control.scan().await; | ||
| 70 | while let Some(bss) = scanner.next().await { | ||
| 71 | if let Ok(ssid_str) = str::from_utf8(&bss.ssid) { | ||
| 72 | info!("scanned {} == {:x}", ssid_str, bss.bssid); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | ||
diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs new file mode 100644 index 000000000..0223a3636 --- /dev/null +++ b/examples/rp/src/bin/wifi_tcp_server.rs | |||
| @@ -0,0 +1,149 @@ | |||
| 1 | //! This example uses the RP Pico W board Wifi chip (cyw43). | ||
| 2 | //! Connects to specified Wifi network and creates a TCP endpoint on port 1234. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | #![feature(async_fn_in_trait)] | ||
| 8 | #![allow(incomplete_features)] | ||
| 9 | |||
| 10 | use core::str::from_utf8; | ||
| 11 | |||
| 12 | use cyw43_pio::PioSpi; | ||
| 13 | use defmt::*; | ||
| 14 | use embassy_executor::Spawner; | ||
| 15 | use embassy_net::tcp::TcpSocket; | ||
| 16 | use embassy_net::{Config, Stack, StackResources}; | ||
| 17 | use embassy_rp::bind_interrupts; | ||
| 18 | use embassy_rp::gpio::{Level, Output}; | ||
| 19 | use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; | ||
| 20 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 21 | use embassy_time::Duration; | ||
| 22 | use embedded_io::asynch::Write; | ||
| 23 | use static_cell::make_static; | ||
| 24 | use {defmt_rtt as _, panic_probe as _}; | ||
| 25 | |||
| 26 | bind_interrupts!(struct Irqs { | ||
| 27 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 28 | }); | ||
| 29 | |||
| 30 | const WIFI_NETWORK: &str = "EmbassyTest"; | ||
| 31 | const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; | ||
| 32 | |||
| 33 | #[embassy_executor::task] | ||
| 34 | async fn wifi_task( | ||
| 35 | runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, | ||
| 36 | ) -> ! { | ||
| 37 | runner.run().await | ||
| 38 | } | ||
| 39 | |||
| 40 | #[embassy_executor::task] | ||
| 41 | async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! { | ||
| 42 | stack.run().await | ||
| 43 | } | ||
| 44 | |||
| 45 | #[embassy_executor::main] | ||
| 46 | async fn main(spawner: Spawner) { | ||
| 47 | info!("Hello World!"); | ||
| 48 | |||
| 49 | let p = embassy_rp::init(Default::default()); | ||
| 50 | |||
| 51 | let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); | ||
| 52 | let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); | ||
| 53 | |||
| 54 | // To make flashing faster for development, you may want to flash the firmwares independently | ||
| 55 | // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: | ||
| 56 | // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 | ||
| 57 | // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 | ||
| 58 | //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; | ||
| 59 | //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; | ||
| 60 | |||
| 61 | let pwr = Output::new(p.PIN_23, Level::Low); | ||
| 62 | let cs = Output::new(p.PIN_25, Level::High); | ||
| 63 | let mut pio = Pio::new(p.PIO0, Irqs); | ||
| 64 | let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); | ||
| 65 | |||
| 66 | let state = make_static!(cyw43::State::new()); | ||
| 67 | let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; | ||
| 68 | unwrap!(spawner.spawn(wifi_task(runner))); | ||
| 69 | |||
| 70 | control.init(clm).await; | ||
| 71 | control | ||
| 72 | .set_power_management(cyw43::PowerManagementMode::PowerSave) | ||
| 73 | .await; | ||
| 74 | |||
| 75 | let config = Config::dhcpv4(Default::default()); | ||
| 76 | //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { | ||
| 77 | // address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), | ||
| 78 | // dns_servers: Vec::new(), | ||
| 79 | // gateway: Some(Ipv4Address::new(192, 168, 69, 1)), | ||
| 80 | //}); | ||
| 81 | |||
| 82 | // Generate random seed | ||
| 83 | let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. | ||
| 84 | |||
| 85 | // Init network stack | ||
| 86 | let stack = &*make_static!(Stack::new( | ||
| 87 | net_device, | ||
| 88 | config, | ||
| 89 | make_static!(StackResources::<2>::new()), | ||
| 90 | seed | ||
| 91 | )); | ||
| 92 | |||
| 93 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 94 | |||
| 95 | loop { | ||
| 96 | //control.join_open(WIFI_NETWORK).await; | ||
| 97 | match control.join_wpa2(WIFI_NETWORK, WIFI_PASSWORD).await { | ||
| 98 | Ok(_) => break, | ||
| 99 | Err(err) => { | ||
| 100 | info!("join failed with status={}", err.status); | ||
| 101 | } | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | // And now we can use it! | ||
| 106 | |||
| 107 | let mut rx_buffer = [0; 4096]; | ||
| 108 | let mut tx_buffer = [0; 4096]; | ||
| 109 | let mut buf = [0; 4096]; | ||
| 110 | |||
| 111 | loop { | ||
| 112 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 113 | socket.set_timeout(Some(Duration::from_secs(10))); | ||
| 114 | |||
| 115 | control.gpio_set(0, false).await; | ||
| 116 | info!("Listening on TCP:1234..."); | ||
| 117 | if let Err(e) = socket.accept(1234).await { | ||
| 118 | warn!("accept error: {:?}", e); | ||
| 119 | continue; | ||
| 120 | } | ||
| 121 | |||
| 122 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 123 | control.gpio_set(0, true).await; | ||
| 124 | |||
| 125 | loop { | ||
| 126 | let n = match socket.read(&mut buf).await { | ||
| 127 | Ok(0) => { | ||
| 128 | warn!("read EOF"); | ||
| 129 | break; | ||
| 130 | } | ||
| 131 | Ok(n) => n, | ||
| 132 | Err(e) => { | ||
| 133 | warn!("read error: {:?}", e); | ||
| 134 | break; | ||
| 135 | } | ||
| 136 | }; | ||
| 137 | |||
| 138 | info!("rxd {}", from_utf8(&buf[..n]).unwrap()); | ||
| 139 | |||
| 140 | match socket.write_all(&buf[..n]).await { | ||
| 141 | Ok(()) => {} | ||
| 142 | Err(e) => { | ||
| 143 | warn!("write error: {:?}", e); | ||
| 144 | break; | ||
| 145 | } | ||
| 146 | }; | ||
| 147 | } | ||
| 148 | } | ||
| 149 | } | ||
diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index c7cec6b19..92933ab50 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml | |||
| @@ -2,22 +2,24 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-std-examples" | 3 | name = "embassy-std-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["log"] } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "nightly", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } | 10 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["log", "std", "nightly"] } |
| 10 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "log", "medium-ethernet", "tcp", "udp", "dhcpv4", "pool-16"] } | 11 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] } |
| 11 | embedded-io = { version = "0.3.0", features = ["async", "std", "futures"] } | 12 | embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" } |
| 13 | embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] } | ||
| 12 | critical-section = { version = "1.1", features = ["std"] } | 14 | critical-section = { version = "1.1", features = ["std"] } |
| 13 | 15 | ||
| 14 | async-io = "1.6.0" | 16 | async-io = "1.6.0" |
| 15 | env_logger = "0.9.0" | 17 | env_logger = "0.9.0" |
| 16 | futures = { version = "0.3.17" } | 18 | futures = { version = "0.3.17" } |
| 17 | log = "0.4.14" | 19 | log = "0.4.14" |
| 18 | nix = "0.22.1" | 20 | nix = "0.26.2" |
| 19 | libc = "0.2.101" | 21 | libc = "0.2.101" |
| 20 | clap = { version = "3.0.0-beta.5", features = ["derive"] } | 22 | clap = { version = "3.0.0-beta.5", features = ["derive"] } |
| 21 | rand_core = { version = "0.6.3", features = ["std"] } | 23 | rand_core = { version = "0.6.3", features = ["std"] } |
| 22 | heapless = { version = "0.7.5", default-features = false } | 24 | heapless = { version = "0.7.5", default-features = false } |
| 23 | static_cell = "1.0" | 25 | static_cell = { version = "1.1", features = ["nightly"]} |
diff --git a/examples/std/README.md b/examples/std/README.md new file mode 100644 index 000000000..adc795928 --- /dev/null +++ b/examples/std/README.md | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | |||
| 2 | ## Running the `embassy-net` examples | ||
| 3 | |||
| 4 | First, create the tap0 interface. You only need to do this once. | ||
| 5 | |||
| 6 | ```sh | ||
| 7 | sudo ip tuntap add name tap0 mode tap user $USER | ||
| 8 | sudo ip link set tap0 up | ||
| 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 | ``` | ||
| 15 | |||
| 16 | Second, have something listening there. For example `nc -l 8000` | ||
| 17 | |||
| 18 | Then run the example located in the `examples` folder: | ||
| 19 | |||
| 20 | ```sh | ||
| 21 | cd $EMBASSY_ROOT/examples/std/ | ||
| 22 | cargo run --bin net -- --static-ip | ||
| 23 | ``` \ No newline at end of file | ||
diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index 9b1450b72..3aadb029d 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs | |||
| @@ -1,28 +1,22 @@ | |||
| 1 | #![feature(type_alias_impl_trait)] | 1 | #![feature(type_alias_impl_trait)] |
| 2 | 2 | ||
| 3 | use std::default::Default; | ||
| 4 | |||
| 3 | use clap::Parser; | 5 | use clap::Parser; |
| 4 | use embassy_executor::{Executor, Spawner}; | 6 | use embassy_executor::{Executor, Spawner}; |
| 5 | use embassy_net::tcp::TcpSocket; | 7 | use embassy_net::tcp::TcpSocket; |
| 6 | use embassy_net::{ConfigStrategy, Ipv4Address, Ipv4Cidr, Stack, StackResources}; | 8 | use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; |
| 9 | use embassy_time::Duration; | ||
| 7 | use embedded_io::asynch::Write; | 10 | use embedded_io::asynch::Write; |
| 8 | use heapless::Vec; | 11 | use heapless::Vec; |
| 9 | use log::*; | 12 | use log::*; |
| 10 | use rand_core::{OsRng, RngCore}; | 13 | use rand_core::{OsRng, RngCore}; |
| 11 | use static_cell::StaticCell; | 14 | use static_cell::{make_static, StaticCell}; |
| 12 | 15 | ||
| 13 | #[path = "../tuntap.rs"] | 16 | #[path = "../tuntap.rs"] |
| 14 | mod tuntap; | 17 | mod tuntap; |
| 15 | 18 | ||
| 16 | use crate::tuntap::TunTapDevice; | 19 | use crate::tuntap::TunTapDevice; |
| 17 | |||
| 18 | macro_rules! singleton { | ||
| 19 | ($val:expr) => {{ | ||
| 20 | type T = impl Sized; | ||
| 21 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | ||
| 22 | STATIC_CELL.init_with(move || $val) | ||
| 23 | }}; | ||
| 24 | } | ||
| 25 | |||
| 26 | #[derive(Parser)] | 20 | #[derive(Parser)] |
| 27 | #[clap(version = "1.0")] | 21 | #[clap(version = "1.0")] |
| 28 | struct Opts { | 22 | struct Opts { |
| @@ -48,13 +42,13 @@ async fn main_task(spawner: Spawner) { | |||
| 48 | 42 | ||
| 49 | // Choose between dhcp or static ip | 43 | // Choose between dhcp or static ip |
| 50 | let config = if opts.static_ip { | 44 | let config = if opts.static_ip { |
| 51 | ConfigStrategy::Static(embassy_net::Config { | 45 | Config::ipv4_static(embassy_net::StaticConfigV4 { |
| 52 | address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), | 46 | address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), |
| 53 | dns_servers: Vec::new(), | 47 | dns_servers: Vec::new(), |
| 54 | gateway: Some(Ipv4Address::new(192, 168, 69, 1)), | 48 | gateway: Some(Ipv4Address::new(192, 168, 69, 1)), |
| 55 | }) | 49 | }) |
| 56 | } else { | 50 | } else { |
| 57 | ConfigStrategy::Dhcp | 51 | Config::dhcpv4(Default::default()) |
| 58 | }; | 52 | }; |
| 59 | 53 | ||
| 60 | // Generate random seed | 54 | // Generate random seed |
| @@ -63,10 +57,10 @@ async fn main_task(spawner: Spawner) { | |||
| 63 | let seed = u64::from_le_bytes(seed); | 57 | let seed = u64::from_le_bytes(seed); |
| 64 | 58 | ||
| 65 | // Init network stack | 59 | // Init network stack |
| 66 | let stack = &*singleton!(Stack::new( | 60 | let stack = &*make_static!(Stack::new( |
| 67 | device, | 61 | device, |
| 68 | config, | 62 | config, |
| 69 | singleton!(StackResources::<1, 2, 8>::new()), | 63 | make_static!(StackResources::<3>::new()), |
| 70 | seed | 64 | seed |
| 71 | )); | 65 | )); |
| 72 | 66 | ||
| @@ -78,7 +72,7 @@ async fn main_task(spawner: Spawner) { | |||
| 78 | let mut tx_buffer = [0; 4096]; | 72 | let mut tx_buffer = [0; 4096]; |
| 79 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | 73 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); |
| 80 | 74 | ||
| 81 | socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); | 75 | socket.set_timeout(Some(Duration::from_secs(10))); |
| 82 | 76 | ||
| 83 | let remote_endpoint = (Ipv4Address::new(192, 168, 69, 100), 8000); | 77 | let remote_endpoint = (Ipv4Address::new(192, 168, 69, 100), 8000); |
| 84 | info!("connecting to {:?}...", remote_endpoint); | 78 | info!("connecting to {:?}...", remote_endpoint); |
diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs new file mode 100644 index 000000000..65b5a2cd9 --- /dev/null +++ b/examples/std/src/bin/net_dns.rs | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | #![feature(type_alias_impl_trait)] | ||
| 2 | |||
| 3 | use std::default::Default; | ||
| 4 | |||
| 5 | use clap::Parser; | ||
| 6 | use embassy_executor::{Executor, Spawner}; | ||
| 7 | use embassy_net::dns::DnsQueryType; | ||
| 8 | use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; | ||
| 9 | use heapless::Vec; | ||
| 10 | use log::*; | ||
| 11 | use rand_core::{OsRng, RngCore}; | ||
| 12 | use static_cell::{make_static, StaticCell}; | ||
| 13 | |||
| 14 | #[path = "../tuntap.rs"] | ||
| 15 | mod tuntap; | ||
| 16 | |||
| 17 | use crate::tuntap::TunTapDevice; | ||
| 18 | #[derive(Parser)] | ||
| 19 | #[clap(version = "1.0")] | ||
| 20 | struct Opts { | ||
| 21 | /// TAP device name | ||
| 22 | #[clap(long, default_value = "tap0")] | ||
| 23 | tap: String, | ||
| 24 | /// use a static IP instead of DHCP | ||
| 25 | #[clap(long)] | ||
| 26 | static_ip: bool, | ||
| 27 | } | ||
| 28 | |||
| 29 | #[embassy_executor::task] | ||
| 30 | async fn net_task(stack: &'static Stack<TunTapDevice>) -> ! { | ||
| 31 | stack.run().await | ||
| 32 | } | ||
| 33 | |||
| 34 | #[embassy_executor::task] | ||
| 35 | async fn main_task(spawner: Spawner) { | ||
| 36 | let opts: Opts = Opts::parse(); | ||
| 37 | |||
| 38 | // Init network device | ||
| 39 | let device = TunTapDevice::new(&opts.tap).unwrap(); | ||
| 40 | |||
| 41 | // Choose between dhcp or static ip | ||
| 42 | let config = if opts.static_ip { | ||
| 43 | Config::ipv4_static(embassy_net::StaticConfigV4 { | ||
| 44 | address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 1), 24), | ||
| 45 | dns_servers: Vec::from_slice(&[Ipv4Address::new(8, 8, 4, 4).into(), Ipv4Address::new(8, 8, 8, 8).into()]) | ||
| 46 | .unwrap(), | ||
| 47 | gateway: Some(Ipv4Address::new(192, 168, 69, 100)), | ||
| 48 | }) | ||
| 49 | } else { | ||
| 50 | Config::dhcpv4(Default::default()) | ||
| 51 | }; | ||
| 52 | |||
| 53 | // Generate random seed | ||
| 54 | let mut seed = [0; 8]; | ||
| 55 | OsRng.fill_bytes(&mut seed); | ||
| 56 | let seed = u64::from_le_bytes(seed); | ||
| 57 | |||
| 58 | // Init network stack | ||
| 59 | let stack: &Stack<_> = &*make_static!(Stack::new( | ||
| 60 | device, | ||
| 61 | config, | ||
| 62 | make_static!(StackResources::<3>::new()), | ||
| 63 | seed | ||
| 64 | )); | ||
| 65 | |||
| 66 | // Launch network task | ||
| 67 | spawner.spawn(net_task(stack)).unwrap(); | ||
| 68 | |||
| 69 | let host = "example.com"; | ||
| 70 | info!("querying host {:?}...", host); | ||
| 71 | match stack.dns_query(host, DnsQueryType::A).await { | ||
| 72 | Ok(r) => { | ||
| 73 | info!("query response: {:?}", r); | ||
| 74 | } | ||
| 75 | Err(e) => { | ||
| 76 | warn!("query error: {:?}", e); | ||
| 77 | } | ||
| 78 | }; | ||
| 79 | } | ||
| 80 | |||
| 81 | static EXECUTOR: StaticCell<Executor> = StaticCell::new(); | ||
| 82 | |||
| 83 | fn main() { | ||
| 84 | env_logger::builder() | ||
| 85 | .filter_level(log::LevelFilter::Debug) | ||
| 86 | .filter_module("async_io", log::LevelFilter::Info) | ||
| 87 | .format_timestamp_nanos() | ||
| 88 | .init(); | ||
| 89 | |||
| 90 | let executor = EXECUTOR.init(Executor::new()); | ||
| 91 | executor.run(|spawner| { | ||
| 92 | spawner.spawn(main_task(spawner)).unwrap(); | ||
| 93 | }); | ||
| 94 | } | ||
diff --git a/examples/std/src/bin/net_udp.rs b/examples/std/src/bin/net_udp.rs index 392a97f0d..3fc46156c 100644 --- a/examples/std/src/bin/net_udp.rs +++ b/examples/std/src/bin/net_udp.rs | |||
| @@ -2,26 +2,17 @@ | |||
| 2 | 2 | ||
| 3 | use clap::Parser; | 3 | use clap::Parser; |
| 4 | use embassy_executor::{Executor, Spawner}; | 4 | use embassy_executor::{Executor, Spawner}; |
| 5 | use embassy_net::udp::UdpSocket; | 5 | use embassy_net::udp::{PacketMetadata, UdpSocket}; |
| 6 | use embassy_net::{ConfigStrategy, Ipv4Address, Ipv4Cidr, PacketMetadata, Stack, StackResources}; | 6 | use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; |
| 7 | use heapless::Vec; | 7 | use heapless::Vec; |
| 8 | use log::*; | 8 | use log::*; |
| 9 | use rand_core::{OsRng, RngCore}; | 9 | use rand_core::{OsRng, RngCore}; |
| 10 | use static_cell::StaticCell; | 10 | use static_cell::{make_static, StaticCell}; |
| 11 | 11 | ||
| 12 | #[path = "../tuntap.rs"] | 12 | #[path = "../tuntap.rs"] |
| 13 | mod tuntap; | 13 | mod tuntap; |
| 14 | 14 | ||
| 15 | use crate::tuntap::TunTapDevice; | 15 | use crate::tuntap::TunTapDevice; |
| 16 | |||
| 17 | macro_rules! singleton { | ||
| 18 | ($val:expr) => {{ | ||
| 19 | type T = impl Sized; | ||
| 20 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | ||
| 21 | STATIC_CELL.init_with(move || $val) | ||
| 22 | }}; | ||
| 23 | } | ||
| 24 | |||
| 25 | #[derive(Parser)] | 16 | #[derive(Parser)] |
| 26 | #[clap(version = "1.0")] | 17 | #[clap(version = "1.0")] |
| 27 | struct Opts { | 18 | struct Opts { |
| @@ -47,13 +38,13 @@ async fn main_task(spawner: Spawner) { | |||
| 47 | 38 | ||
| 48 | // Choose between dhcp or static ip | 39 | // Choose between dhcp or static ip |
| 49 | let config = if opts.static_ip { | 40 | let config = if opts.static_ip { |
| 50 | ConfigStrategy::Static(embassy_net::Config { | 41 | Config::ipv4_static(embassy_net::StaticConfigV4 { |
| 51 | address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), | 42 | address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), |
| 52 | dns_servers: Vec::new(), | 43 | dns_servers: Vec::new(), |
| 53 | gateway: Some(Ipv4Address::new(192, 168, 69, 1)), | 44 | gateway: Some(Ipv4Address::new(192, 168, 69, 1)), |
| 54 | }) | 45 | }) |
| 55 | } else { | 46 | } else { |
| 56 | ConfigStrategy::Dhcp | 47 | Config::dhcpv4(Default::default()) |
| 57 | }; | 48 | }; |
| 58 | 49 | ||
| 59 | // Generate random seed | 50 | // Generate random seed |
| @@ -62,10 +53,10 @@ async fn main_task(spawner: Spawner) { | |||
| 62 | let seed = u64::from_le_bytes(seed); | 53 | let seed = u64::from_le_bytes(seed); |
| 63 | 54 | ||
| 64 | // Init network stack | 55 | // Init network stack |
| 65 | let stack = &*singleton!(Stack::new( | 56 | let stack = &*make_static!(Stack::new( |
| 66 | device, | 57 | device, |
| 67 | config, | 58 | config, |
| 68 | singleton!(StackResources::<1, 2, 8>::new()), | 59 | make_static!(StackResources::<3>::new()), |
| 69 | seed | 60 | seed |
| 70 | )); | 61 | )); |
| 71 | 62 | ||
diff --git a/examples/std/src/bin/tcp_accept.rs b/examples/std/src/bin/tcp_accept.rs new file mode 100644 index 000000000..df09986ac --- /dev/null +++ b/examples/std/src/bin/tcp_accept.rs | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | #![feature(type_alias_impl_trait)] | ||
| 2 | |||
| 3 | use core::fmt::Write as _; | ||
| 4 | use std::default::Default; | ||
| 5 | |||
| 6 | use clap::Parser; | ||
| 7 | use embassy_executor::{Executor, Spawner}; | ||
| 8 | use embassy_net::tcp::TcpSocket; | ||
| 9 | use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; | ||
| 10 | use embassy_time::{Duration, Timer}; | ||
| 11 | use embedded_io::asynch::Write as _; | ||
| 12 | use heapless::Vec; | ||
| 13 | use log::*; | ||
| 14 | use rand_core::{OsRng, RngCore}; | ||
| 15 | use static_cell::{make_static, StaticCell}; | ||
| 16 | |||
| 17 | #[path = "../tuntap.rs"] | ||
| 18 | mod tuntap; | ||
| 19 | |||
| 20 | use crate::tuntap::TunTapDevice; | ||
| 21 | #[derive(Parser)] | ||
| 22 | #[clap(version = "1.0")] | ||
| 23 | struct Opts { | ||
| 24 | /// TAP device name | ||
| 25 | #[clap(long, default_value = "tap0")] | ||
| 26 | tap: String, | ||
| 27 | /// use a static IP instead of DHCP | ||
| 28 | #[clap(long)] | ||
| 29 | static_ip: bool, | ||
| 30 | } | ||
| 31 | |||
| 32 | #[embassy_executor::task] | ||
| 33 | async fn net_task(stack: &'static Stack<TunTapDevice>) -> ! { | ||
| 34 | stack.run().await | ||
| 35 | } | ||
| 36 | |||
| 37 | #[derive(Default)] | ||
| 38 | struct StrWrite(pub heapless::Vec<u8, 30>); | ||
| 39 | |||
| 40 | impl core::fmt::Write for StrWrite { | ||
| 41 | fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { | ||
| 42 | self.0.extend_from_slice(s.as_bytes()).unwrap(); | ||
| 43 | Ok(()) | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | #[embassy_executor::task] | ||
| 48 | async fn main_task(spawner: Spawner) { | ||
| 49 | let opts: Opts = Opts::parse(); | ||
| 50 | |||
| 51 | // Init network device | ||
| 52 | let device = TunTapDevice::new(&opts.tap).unwrap(); | ||
| 53 | |||
| 54 | // Choose between dhcp or static ip | ||
| 55 | let config = if opts.static_ip { | ||
| 56 | Config::ipv4_static(embassy_net::StaticConfigV4 { | ||
| 57 | address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), | ||
| 58 | dns_servers: Vec::new(), | ||
| 59 | gateway: Some(Ipv4Address::new(192, 168, 69, 1)), | ||
| 60 | }) | ||
| 61 | } else { | ||
| 62 | Config::dhcpv4(Default::default()) | ||
| 63 | }; | ||
| 64 | |||
| 65 | // Generate random seed | ||
| 66 | let mut seed = [0; 8]; | ||
| 67 | OsRng.fill_bytes(&mut seed); | ||
| 68 | let seed = u64::from_le_bytes(seed); | ||
| 69 | |||
| 70 | // Init network stack | ||
| 71 | let stack = &*make_static!(Stack::new( | ||
| 72 | device, | ||
| 73 | config, | ||
| 74 | make_static!(StackResources::<3>::new()), | ||
| 75 | seed | ||
| 76 | )); | ||
| 77 | |||
| 78 | // Launch network task | ||
| 79 | spawner.spawn(net_task(stack)).unwrap(); | ||
| 80 | |||
| 81 | // Then we can use it! | ||
| 82 | let mut rx_buffer = [0; 4096]; | ||
| 83 | let mut tx_buffer = [0; 4096]; | ||
| 84 | |||
| 85 | loop { | ||
| 86 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 87 | socket.set_timeout(Some(Duration::from_secs(10))); | ||
| 88 | info!("Listening on TCP:9999..."); | ||
| 89 | if let Err(_) = socket.accept(9999).await { | ||
| 90 | warn!("accept error"); | ||
| 91 | continue; | ||
| 92 | } | ||
| 93 | |||
| 94 | info!("Accepted a connection"); | ||
| 95 | |||
| 96 | // Write some quick output | ||
| 97 | for i in 1..=5 { | ||
| 98 | let mut w = StrWrite::default(); | ||
| 99 | write!(w, "{}! ", i).unwrap(); | ||
| 100 | let r = socket.write_all(&w.0).await; | ||
| 101 | if let Err(e) = r { | ||
| 102 | warn!("write error: {:?}", e); | ||
| 103 | return; | ||
| 104 | } | ||
| 105 | |||
| 106 | Timer::after(Duration::from_millis(500)).await; | ||
| 107 | } | ||
| 108 | info!("Closing the connection"); | ||
| 109 | socket.abort(); | ||
| 110 | info!("Flushing the RST out..."); | ||
| 111 | _ = socket.flush().await; | ||
| 112 | info!("Finished with the socket"); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | static EXECUTOR: StaticCell<Executor> = StaticCell::new(); | ||
| 117 | |||
| 118 | fn main() { | ||
| 119 | env_logger::builder() | ||
| 120 | .filter_level(log::LevelFilter::Debug) | ||
| 121 | .filter_module("async_io", log::LevelFilter::Info) | ||
| 122 | .format_timestamp_nanos() | ||
| 123 | .init(); | ||
| 124 | |||
| 125 | let executor = EXECUTOR.init(Executor::new()); | ||
| 126 | executor.run(|spawner| { | ||
| 127 | spawner.spawn(main_task(spawner)).unwrap(); | ||
| 128 | }); | ||
| 129 | } | ||
diff --git a/examples/std/src/tuntap.rs b/examples/std/src/tuntap.rs index a0cace7f7..d918a2e62 100644 --- a/examples/std/src/tuntap.rs +++ b/examples/std/src/tuntap.rs | |||
| @@ -1,8 +1,10 @@ | |||
| 1 | use std::io; | 1 | use std::io; |
| 2 | use std::io::{Read, Write}; | 2 | use std::io::{Read, Write}; |
| 3 | use std::os::unix::io::{AsRawFd, RawFd}; | 3 | use std::os::unix::io::{AsRawFd, RawFd}; |
| 4 | use std::task::Context; | ||
| 4 | 5 | ||
| 5 | use async_io::Async; | 6 | use async_io::Async; |
| 7 | use embassy_net_driver::{self, Capabilities, Driver, LinkState}; | ||
| 6 | use log::*; | 8 | use log::*; |
| 7 | 9 | ||
| 8 | pub const SIOCGIFMTU: libc::c_ulong = 0x8921; | 10 | pub const SIOCGIFMTU: libc::c_ulong = 0x8921; |
| @@ -125,54 +127,35 @@ impl io::Write for TunTap { | |||
| 125 | 127 | ||
| 126 | pub struct TunTapDevice { | 128 | pub struct TunTapDevice { |
| 127 | device: Async<TunTap>, | 129 | device: Async<TunTap>, |
| 128 | waker: Option<Waker>, | ||
| 129 | } | 130 | } |
| 130 | 131 | ||
| 131 | impl TunTapDevice { | 132 | impl TunTapDevice { |
| 132 | pub fn new(name: &str) -> io::Result<TunTapDevice> { | 133 | pub fn new(name: &str) -> io::Result<TunTapDevice> { |
| 133 | Ok(Self { | 134 | Ok(Self { |
| 134 | device: Async::new(TunTap::new(name)?)?, | 135 | device: Async::new(TunTap::new(name)?)?, |
| 135 | waker: None, | ||
| 136 | }) | 136 | }) |
| 137 | } | 137 | } |
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | use core::task::Waker; | 140 | impl Driver for TunTapDevice { |
| 141 | use std::task::Context; | 141 | type RxToken<'a> = RxToken where Self: 'a; |
| 142 | 142 | type TxToken<'a> = TxToken<'a> where Self: 'a; | |
| 143 | use embassy_net::{Device, DeviceCapabilities, LinkState, Packet, PacketBox, PacketBoxExt, PacketBuf}; | ||
| 144 | |||
| 145 | impl Device for TunTapDevice { | ||
| 146 | fn is_transmit_ready(&mut self) -> bool { | ||
| 147 | true | ||
| 148 | } | ||
| 149 | 143 | ||
| 150 | fn transmit(&mut self, pkt: PacketBuf) { | 144 | fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { |
| 151 | // todo handle WouldBlock | 145 | let mut buf = vec![0; self.device.get_ref().mtu]; |
| 152 | match self.device.get_mut().write(&pkt) { | ||
| 153 | Ok(_) => {} | ||
| 154 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => { | ||
| 155 | info!("transmit WouldBlock"); | ||
| 156 | } | ||
| 157 | Err(e) => panic!("transmit error: {:?}", e), | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | fn receive(&mut self) -> Option<PacketBuf> { | ||
| 162 | let mut pkt = PacketBox::new(Packet::new()).unwrap(); | ||
| 163 | loop { | 146 | loop { |
| 164 | match self.device.get_mut().read(&mut pkt[..]) { | 147 | match self.device.get_mut().read(&mut buf) { |
| 165 | Ok(n) => { | 148 | Ok(n) => { |
| 166 | return Some(pkt.slice(0..n)); | 149 | buf.truncate(n); |
| 150 | return Some(( | ||
| 151 | RxToken { buffer: buf }, | ||
| 152 | TxToken { | ||
| 153 | device: &mut self.device, | ||
| 154 | }, | ||
| 155 | )); | ||
| 167 | } | 156 | } |
| 168 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => { | 157 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => { |
| 169 | let ready = if let Some(w) = self.waker.as_ref() { | 158 | if !self.device.poll_readable(cx).is_ready() { |
| 170 | let mut cx = Context::from_waker(w); | ||
| 171 | self.device.poll_readable(&mut cx).is_ready() | ||
| 172 | } else { | ||
| 173 | false | ||
| 174 | }; | ||
| 175 | if !ready { | ||
| 176 | return None; | 159 | return None; |
| 177 | } | 160 | } |
| 178 | } | 161 | } |
| @@ -181,37 +164,19 @@ impl Device for TunTapDevice { | |||
| 181 | } | 164 | } |
| 182 | } | 165 | } |
| 183 | 166 | ||
| 184 | fn register_waker(&mut self, w: &Waker) { | 167 | fn transmit(&mut self, _cx: &mut Context) -> Option<Self::TxToken<'_>> { |
| 185 | match self.waker { | 168 | Some(TxToken { |
| 186 | // Optimization: If both the old and new Wakers wake the same task, we can simply | 169 | device: &mut self.device, |
| 187 | // keep the old waker, skipping the clone. (In most executor implementations, | 170 | }) |
| 188 | // cloning a waker is somewhat expensive, comparable to cloning an Arc). | ||
| 189 | Some(ref w2) if (w2.will_wake(w)) => {} | ||
| 190 | _ => { | ||
| 191 | // clone the new waker and store it | ||
| 192 | if let Some(old_waker) = core::mem::replace(&mut self.waker, Some(w.clone())) { | ||
| 193 | // We had a waker registered for another task. Wake it, so the other task can | ||
| 194 | // reregister itself if it's still interested. | ||
| 195 | // | ||
| 196 | // If two tasks are waiting on the same thing concurrently, this will cause them | ||
| 197 | // to wake each other in a loop fighting over this WakerRegistration. This wastes | ||
| 198 | // CPU but things will still work. | ||
| 199 | // | ||
| 200 | // If the user wants to have two tasks waiting on the same thing they should use | ||
| 201 | // a more appropriate primitive that can store multiple wakers. | ||
| 202 | old_waker.wake() | ||
| 203 | } | ||
| 204 | } | ||
| 205 | } | ||
| 206 | } | 171 | } |
| 207 | 172 | ||
| 208 | fn capabilities(&self) -> DeviceCapabilities { | 173 | fn capabilities(&self) -> Capabilities { |
| 209 | let mut caps = DeviceCapabilities::default(); | 174 | let mut caps = Capabilities::default(); |
| 210 | caps.max_transmission_unit = self.device.get_ref().mtu; | 175 | caps.max_transmission_unit = self.device.get_ref().mtu; |
| 211 | caps | 176 | caps |
| 212 | } | 177 | } |
| 213 | 178 | ||
| 214 | fn link_state(&mut self) -> LinkState { | 179 | fn link_state(&mut self, _cx: &mut Context) -> LinkState { |
| 215 | LinkState::Up | 180 | LinkState::Up |
| 216 | } | 181 | } |
| 217 | 182 | ||
| @@ -219,3 +184,41 @@ impl Device for TunTapDevice { | |||
| 219 | [0x02, 0x03, 0x04, 0x05, 0x06, 0x07] | 184 | [0x02, 0x03, 0x04, 0x05, 0x06, 0x07] |
| 220 | } | 185 | } |
| 221 | } | 186 | } |
| 187 | |||
| 188 | #[doc(hidden)] | ||
| 189 | pub struct RxToken { | ||
| 190 | buffer: Vec<u8>, | ||
| 191 | } | ||
| 192 | |||
| 193 | impl embassy_net_driver::RxToken for RxToken { | ||
| 194 | fn consume<R, F>(mut self, f: F) -> R | ||
| 195 | where | ||
| 196 | F: FnOnce(&mut [u8]) -> R, | ||
| 197 | { | ||
| 198 | f(&mut self.buffer) | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 202 | #[doc(hidden)] | ||
| 203 | pub struct TxToken<'a> { | ||
| 204 | device: &'a mut Async<TunTap>, | ||
| 205 | } | ||
| 206 | |||
| 207 | impl<'a> embassy_net_driver::TxToken for TxToken<'a> { | ||
| 208 | fn consume<R, F>(self, len: usize, f: F) -> R | ||
| 209 | where | ||
| 210 | F: FnOnce(&mut [u8]) -> R, | ||
| 211 | { | ||
| 212 | let mut buffer = vec![0; len]; | ||
| 213 | let result = f(&mut buffer); | ||
| 214 | |||
| 215 | // todo handle WouldBlock with async | ||
| 216 | match self.device.get_mut().write(&buffer) { | ||
| 217 | Ok(_) => {} | ||
| 218 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => info!("transmit WouldBlock"), | ||
| 219 | Err(e) => panic!("transmit error: {:?}", e), | ||
| 220 | } | ||
| 221 | |||
| 222 | result | ||
| 223 | } | ||
| 224 | } | ||
diff --git a/examples/stm32c0/.cargo/config.toml b/examples/stm32c0/.cargo/config.toml new file mode 100644 index 000000000..29a8be7e1 --- /dev/null +++ b/examples/stm32c0/.cargo/config.toml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||
| 2 | # replace STM32G071C8Rx with your chip as listed in `probe-rs chip list` | ||
| 3 | runner = "probe-rs run --speed 100 --chip STM32c031c6tx" | ||
| 4 | |||
| 5 | [build] | ||
| 6 | target = "thumbv6m-none-eabi" | ||
| 7 | |||
| 8 | [env] | ||
| 9 | DEFMT_LOG = "trace" | ||
diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml new file mode 100644 index 000000000..26837abef --- /dev/null +++ b/examples/stm32c0/Cargo.toml | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-stm32c0-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 9 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | ||
| 10 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | ||
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } | ||
| 12 | |||
| 13 | defmt = "0.3" | ||
| 14 | defmt-rtt = "0.4" | ||
| 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 | panic-probe = { version = "0.3", features = ["print-defmt"] } | ||
| 20 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | ||
| 21 | heapless = { version = "0.7.5", default-features = false } | ||
diff --git a/examples/stm32c0/build.rs b/examples/stm32c0/build.rs new file mode 100644 index 000000000..8cd32d7ed --- /dev/null +++ b/examples/stm32c0/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/stm32c0/src/bin/blinky.rs b/examples/stm32c0/src/bin/blinky.rs new file mode 100644 index 000000000..8a65b0692 --- /dev/null +++ b/examples/stm32c0/src/bin/blinky.rs | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 8 | use embassy_time::{Duration, Timer}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_stm32::init(Default::default()); | ||
| 14 | info!("Hello World!"); | ||
| 15 | |||
| 16 | let mut led = Output::new(p.PA5, Level::High, Speed::Low); | ||
| 17 | |||
| 18 | loop { | ||
| 19 | info!("high"); | ||
| 20 | led.set_high(); | ||
| 21 | Timer::after(Duration::from_millis(300)).await; | ||
| 22 | |||
| 23 | info!("low"); | ||
| 24 | led.set_low(); | ||
| 25 | Timer::after(Duration::from_millis(300)).await; | ||
| 26 | } | ||
| 27 | } | ||
diff --git a/examples/stm32c0/src/bin/button.rs b/examples/stm32c0/src/bin/button.rs new file mode 100644 index 000000000..72a3f5cbf --- /dev/null +++ b/examples/stm32c0/src/bin/button.rs | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use cortex_m_rt::entry; | ||
| 6 | use defmt::*; | ||
| 7 | use embassy_stm32::gpio::{Input, Pull}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[entry] | ||
| 11 | fn main() -> ! { | ||
| 12 | info!("Hello World!"); | ||
| 13 | |||
| 14 | let p = embassy_stm32::init(Default::default()); | ||
| 15 | |||
| 16 | let button = Input::new(p.PC13, Pull::Up); | ||
| 17 | |||
| 18 | loop { | ||
| 19 | if button.is_high() { | ||
| 20 | info!("high"); | ||
| 21 | } else { | ||
| 22 | info!("low"); | ||
| 23 | } | ||
| 24 | } | ||
| 25 | } | ||
diff --git a/examples/stm32c0/src/bin/button_exti.rs b/examples/stm32c0/src/bin/button_exti.rs new file mode 100644 index 000000000..ef32d4c4a --- /dev/null +++ b/examples/stm32c0/src/bin/button_exti.rs | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::exti::ExtiInput; | ||
| 8 | use embassy_stm32::gpio::{Input, Pull}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_stm32::init(Default::default()); | ||
| 14 | info!("Hello World!"); | ||
| 15 | |||
| 16 | let button = Input::new(p.PC13, Pull::Up); | ||
| 17 | let mut button = ExtiInput::new(button, p.EXTI13); | ||
| 18 | |||
| 19 | info!("Press the USER button..."); | ||
| 20 | |||
| 21 | loop { | ||
| 22 | button.wait_for_falling_edge().await; | ||
| 23 | info!("Pressed!"); | ||
| 24 | button.wait_for_rising_edge().await; | ||
| 25 | info!("Released!"); | ||
| 26 | } | ||
| 27 | } | ||
diff --git a/examples/stm32f0/.cargo/config.toml b/examples/stm32f0/.cargo/config.toml index d1b1cd0bf..def4c8c92 100644 --- a/examples/stm32f0/.cargo/config.toml +++ b/examples/stm32f0/.cargo/config.toml | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | [target.thumbv6m-none-eabi] | 1 | [target.thumbv6m-none-eabi] |
| 2 | runner = 'probe-run --chip STM32F030F4Px' | 2 | runner = 'probe-rs run --chip STM32F091RCTX' |
| 3 | 3 | ||
| 4 | [build] | 4 | [build] |
| 5 | target = "thumbv6m-none-eabi" | 5 | target = "thumbv6m-none-eabi" |
diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index cd2995d2c..b7b5eaa99 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml | |||
| @@ -2,17 +2,18 @@ | |||
| 2 | name = "embassy-stm32f0-examples" | 2 | name = "embassy-stm32f0-examples" |
| 3 | version = "0.1.0" | 3 | version = "0.1.0" |
| 4 | edition = "2021" | 4 | edition = "2021" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
| 7 | 8 | ||
| 8 | [dependencies] | 9 | [dependencies] |
| 9 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 10 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 10 | cortex-m-rt = "0.7.0" | 11 | cortex-m-rt = "0.7.0" |
| 11 | defmt = "0.3" | 12 | defmt = "0.3" |
| 12 | defmt-rtt = "0.3" | 13 | defmt-rtt = "0.4" |
| 13 | panic-probe = "0.3" | 14 | panic-probe = "0.3" |
| 14 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 15 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 15 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 16 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } |
| 16 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-32768hz"] } | 17 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 17 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f030f4", "time-driver-any"] } | 18 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } |
| 18 | 19 | static_cell = { version = "1.1", features = ["nightly"]} | |
diff --git a/examples/stm32f0/src/bin/adc.rs b/examples/stm32f0/src/bin/adc.rs new file mode 100644 index 000000000..8ed9f98f8 --- /dev/null +++ b/examples/stm32f0/src/bin/adc.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::adc::{Adc, SampleTime}; | ||
| 8 | use embassy_time::{Delay, Duration, Timer}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_stm32::init(Default::default()); | ||
| 14 | info!("Hello World!"); | ||
| 15 | |||
| 16 | let mut adc = Adc::new(p.ADC, &mut Delay); | ||
| 17 | adc.set_sample_time(SampleTime::Cycles71_5); | ||
| 18 | let mut pin = p.PA1; | ||
| 19 | |||
| 20 | let mut vrefint = adc.enable_vref(&mut Delay); | ||
| 21 | let vrefint_sample = adc.read_internal(&mut vrefint); | ||
| 22 | let convert_to_millivolts = |sample| { | ||
| 23 | // From https://www.st.com/resource/en/datasheet/stm32f031c6.pdf | ||
| 24 | // 6.3.4 Embedded reference voltage | ||
| 25 | const VREFINT_MV: u32 = 1230; // mV | ||
| 26 | |||
| 27 | (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 | ||
| 28 | }; | ||
| 29 | |||
| 30 | loop { | ||
| 31 | let v = adc.read(&mut pin); | ||
| 32 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); | ||
| 33 | Timer::after(Duration::from_millis(100)).await; | ||
| 34 | } | ||
| 35 | } | ||
diff --git a/examples/stm32f0/src/bin/blinky.rs b/examples/stm32f0/src/bin/blinky.rs new file mode 100644 index 000000000..9f923399c --- /dev/null +++ b/examples/stm32f0/src/bin/blinky.rs | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 8 | use embassy_time::{Duration, Timer}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | // main is itself an async function. | ||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) { | ||
| 14 | let p = embassy_stm32::init(Default::default()); | ||
| 15 | info!("Hello World!"); | ||
| 16 | //PA5 is the onboard LED on the Nucleo F091RC | ||
| 17 | let mut led = Output::new(p.PA5, Level::High, Speed::Low); | ||
| 18 | |||
| 19 | loop { | ||
| 20 | info!("high"); | ||
| 21 | led.set_high(); | ||
| 22 | Timer::after(Duration::from_millis(300)).await; | ||
| 23 | |||
| 24 | info!("low"); | ||
| 25 | led.set_low(); | ||
| 26 | Timer::after(Duration::from_millis(300)).await; | ||
| 27 | } | ||
| 28 | } | ||
diff --git a/examples/stm32f0/src/bin/button_controlled_blink.rs b/examples/stm32f0/src/bin/button_controlled_blink.rs new file mode 100644 index 000000000..f362c53f5 --- /dev/null +++ b/examples/stm32f0/src/bin/button_controlled_blink.rs | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | //! This example showcases how to create task | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | #![feature(type_alias_impl_trait)] | ||
| 6 | |||
| 7 | use core::sync::atomic::{AtomicU32, Ordering}; | ||
| 8 | |||
| 9 | use defmt::info; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_stm32::exti::ExtiInput; | ||
| 12 | use embassy_stm32::gpio::{AnyPin, Input, Level, Output, Pin, Pull, Speed}; | ||
| 13 | use embassy_time::{Duration, Timer}; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | static BLINK_MS: AtomicU32 = AtomicU32::new(0); | ||
| 17 | |||
| 18 | #[embassy_executor::task] | ||
| 19 | async fn led_task(led: AnyPin) { | ||
| 20 | // Configure the LED pin as a push pull output and obtain handler. | ||
| 21 | // On the Nucleo F091RC there's an on-board LED connected to pin PA5. | ||
| 22 | let mut led = Output::new(led, Level::Low, Speed::Low); | ||
| 23 | |||
| 24 | loop { | ||
| 25 | let del = BLINK_MS.load(Ordering::Relaxed); | ||
| 26 | info!("Value of del is {}", del); | ||
| 27 | Timer::after(Duration::from_millis(del.into())).await; | ||
| 28 | info!("LED toggling"); | ||
| 29 | led.toggle(); | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | #[embassy_executor::main] | ||
| 34 | async fn main(spawner: Spawner) { | ||
| 35 | // Initialize and create handle for devicer peripherals | ||
| 36 | let p = embassy_stm32::init(Default::default()); | ||
| 37 | |||
| 38 | // Configure the button pin and obtain handler. | ||
| 39 | // On the Nucleo F091RC there is a button connected to pin PC13. | ||
| 40 | let button = Input::new(p.PC13, Pull::None); | ||
| 41 | let mut button = ExtiInput::new(button, p.EXTI13); | ||
| 42 | |||
| 43 | // Create and initialize a delay variable to manage delay loop | ||
| 44 | let mut del_var = 2000; | ||
| 45 | |||
| 46 | // Blink duration value to global context | ||
| 47 | BLINK_MS.store(del_var, Ordering::Relaxed); | ||
| 48 | |||
| 49 | // Spawn LED blinking task | ||
| 50 | spawner.spawn(led_task(p.PA5.degrade())).unwrap(); | ||
| 51 | |||
| 52 | loop { | ||
| 53 | // Check if button got pressed | ||
| 54 | button.wait_for_rising_edge().await; | ||
| 55 | info!("rising_edge"); | ||
| 56 | del_var = del_var - 200; | ||
| 57 | // If updated delay value drops below 200 then reset it back to starting value | ||
| 58 | if del_var < 200 { | ||
| 59 | del_var = 2000; | ||
| 60 | } | ||
| 61 | // Updated delay value to global context | ||
| 62 | BLINK_MS.store(del_var, Ordering::Relaxed); | ||
| 63 | } | ||
| 64 | } | ||
diff --git a/examples/stm32f0/src/bin/button_exti.rs b/examples/stm32f0/src/bin/button_exti.rs new file mode 100644 index 000000000..40c0d5848 --- /dev/null +++ b/examples/stm32f0/src/bin/button_exti.rs | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::exti::ExtiInput; | ||
| 8 | use embassy_stm32::gpio::{Input, Pull}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | // Initialize and create handle for devicer peripherals | ||
| 14 | let p = embassy_stm32::init(Default::default()); | ||
| 15 | // Configure the button pin and obtain handler. | ||
| 16 | // On the Nucleo F091RC there is a button connected to pin PC13. | ||
| 17 | let button = Input::new(p.PC13, Pull::Down); | ||
| 18 | let mut button = ExtiInput::new(button, p.EXTI13); | ||
| 19 | |||
| 20 | info!("Press the USER button..."); | ||
| 21 | loop { | ||
| 22 | button.wait_for_falling_edge().await; | ||
| 23 | info!("Pressed!"); | ||
| 24 | button.wait_for_rising_edge().await; | ||
| 25 | info!("Released!"); | ||
| 26 | } | ||
| 27 | } | ||
diff --git a/examples/stm32f0/src/bin/multiprio.rs b/examples/stm32f0/src/bin/multiprio.rs new file mode 100644 index 000000000..988ffeef1 --- /dev/null +++ b/examples/stm32f0/src/bin/multiprio.rs | |||
| @@ -0,0 +1,145 @@ | |||
| 1 | //! This example showcases how to create multiple Executor instances to run tasks at | ||
| 2 | //! different priority levels. | ||
| 3 | //! | ||
| 4 | //! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling | ||
| 5 | //! there's work in the queue, and `wfe` for waiting for work. | ||
| 6 | //! | ||
| 7 | //! Medium and high priority executors run in two interrupts with different priorities. | ||
| 8 | //! Signaling work is done by pending the interrupt. No "waiting" needs to be done explicitly, since | ||
| 9 | //! when there's work the interrupt will trigger and run the executor. | ||
| 10 | //! | ||
| 11 | //! Sample output below. Note that high priority ticks can interrupt everything else, and | ||
| 12 | //! medium priority computations can interrupt low priority computations, making them to appear | ||
| 13 | //! to take significantly longer time. | ||
| 14 | //! | ||
| 15 | //! ```not_rust | ||
| 16 | //! [med] Starting long computation | ||
| 17 | //! [med] done in 992 ms | ||
| 18 | //! [high] tick! | ||
| 19 | //! [low] Starting long computation | ||
| 20 | //! [med] Starting long computation | ||
| 21 | //! [high] tick! | ||
| 22 | //! [high] tick! | ||
| 23 | //! [med] done in 993 ms | ||
| 24 | //! [med] Starting long computation | ||
| 25 | //! [high] tick! | ||
| 26 | //! [high] tick! | ||
| 27 | //! [med] done in 993 ms | ||
| 28 | //! [low] done in 3972 ms | ||
| 29 | //! [med] Starting long computation | ||
| 30 | //! [high] tick! | ||
| 31 | //! [high] tick! | ||
| 32 | //! [med] done in 993 ms | ||
| 33 | //! ``` | ||
| 34 | //! | ||
| 35 | //! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor. | ||
| 36 | //! You will get an output like the following. Note that no computation is ever interrupted. | ||
| 37 | //! | ||
| 38 | //! ```not_rust | ||
| 39 | //! [high] tick! | ||
| 40 | //! [med] Starting long computation | ||
| 41 | //! [med] done in 496 ms | ||
| 42 | //! [low] Starting long computation | ||
| 43 | //! [low] done in 992 ms | ||
| 44 | //! [med] Starting long computation | ||
| 45 | //! [med] done in 496 ms | ||
| 46 | //! [high] tick! | ||
| 47 | //! [low] Starting long computation | ||
| 48 | //! [low] done in 992 ms | ||
| 49 | //! [high] tick! | ||
| 50 | //! [med] Starting long computation | ||
| 51 | //! [med] done in 496 ms | ||
| 52 | //! [high] tick! | ||
| 53 | //! ``` | ||
| 54 | //! | ||
| 55 | |||
| 56 | #![no_std] | ||
| 57 | #![no_main] | ||
| 58 | #![feature(type_alias_impl_trait)] | ||
| 59 | |||
| 60 | use cortex_m_rt::entry; | ||
| 61 | use defmt::*; | ||
| 62 | use embassy_executor::{Executor, InterruptExecutor}; | ||
| 63 | use embassy_stm32::interrupt; | ||
| 64 | use embassy_stm32::interrupt::{InterruptExt, Priority}; | ||
| 65 | use embassy_time::{Duration, Instant, Timer}; | ||
| 66 | use static_cell::StaticCell; | ||
| 67 | use {defmt_rtt as _, panic_probe as _}; | ||
| 68 | |||
| 69 | #[embassy_executor::task] | ||
| 70 | async fn run_high() { | ||
| 71 | loop { | ||
| 72 | // info!(" [high] tick!"); | ||
| 73 | Timer::after(Duration::from_ticks(27374)).await; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | #[embassy_executor::task] | ||
| 78 | async fn run_med() { | ||
| 79 | loop { | ||
| 80 | let start = Instant::now(); | ||
| 81 | info!(" [med] Starting long computation"); | ||
| 82 | |||
| 83 | // Spin-wait to simulate a long CPU computation | ||
| 84 | cortex_m::asm::delay(8_000_000); // ~1 second | ||
| 85 | |||
| 86 | let end = Instant::now(); | ||
| 87 | let ms = end.duration_since(start).as_ticks() / 33; | ||
| 88 | info!(" [med] done in {} ms", ms); | ||
| 89 | |||
| 90 | Timer::after(Duration::from_ticks(23421)).await; | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | #[embassy_executor::task] | ||
| 95 | async fn run_low() { | ||
| 96 | loop { | ||
| 97 | let start = Instant::now(); | ||
| 98 | info!("[low] Starting long computation"); | ||
| 99 | |||
| 100 | // Spin-wait to simulate a long CPU computation | ||
| 101 | cortex_m::asm::delay(16_000_000); // ~2 seconds | ||
| 102 | |||
| 103 | let end = Instant::now(); | ||
| 104 | let ms = end.duration_since(start).as_ticks() / 33; | ||
| 105 | info!("[low] done in {} ms", ms); | ||
| 106 | |||
| 107 | Timer::after(Duration::from_ticks(32983)).await; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); | ||
| 112 | static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); | ||
| 113 | static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new(); | ||
| 114 | |||
| 115 | #[interrupt] | ||
| 116 | unsafe fn USART1() { | ||
| 117 | EXECUTOR_HIGH.on_interrupt() | ||
| 118 | } | ||
| 119 | |||
| 120 | #[interrupt] | ||
| 121 | unsafe fn USART2() { | ||
| 122 | EXECUTOR_MED.on_interrupt() | ||
| 123 | } | ||
| 124 | |||
| 125 | #[entry] | ||
| 126 | fn main() -> ! { | ||
| 127 | // Initialize and create handle for devicer peripherals | ||
| 128 | let _p = embassy_stm32::init(Default::default()); | ||
| 129 | |||
| 130 | // High-priority executor: USART1, priority level 6 | ||
| 131 | interrupt::USART1.set_priority(Priority::P6); | ||
| 132 | let spawner = EXECUTOR_HIGH.start(interrupt::USART1); | ||
| 133 | unwrap!(spawner.spawn(run_high())); | ||
| 134 | |||
| 135 | // Medium-priority executor: USART2, priority level 7 | ||
| 136 | interrupt::USART2.set_priority(Priority::P7); | ||
| 137 | let spawner = EXECUTOR_MED.start(interrupt::USART2); | ||
| 138 | unwrap!(spawner.spawn(run_med())); | ||
| 139 | |||
| 140 | // Low priority executor: runs in thread mode, using WFE/SEV | ||
| 141 | let executor = EXECUTOR_LOW.init(Executor::new()); | ||
| 142 | executor.run(|spawner| { | ||
| 143 | unwrap!(spawner.spawn(run_low())); | ||
| 144 | }); | ||
| 145 | } | ||
diff --git a/examples/stm32f0/src/bin/wdg.rs b/examples/stm32f0/src/bin/wdg.rs new file mode 100644 index 000000000..a44b17528 --- /dev/null +++ b/examples/stm32f0/src/bin/wdg.rs | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::wdg::IndependentWatchdog; | ||
| 8 | use embassy_time::{Duration, Timer}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | // Initialize and create handle for devicer peripherals | ||
| 14 | let p = embassy_stm32::init(Default::default()); | ||
| 15 | // Configure the independent watchdog timer | ||
| 16 | let mut wdg = IndependentWatchdog::new(p.IWDG, 20_000_00); | ||
| 17 | |||
| 18 | info!("Watchdog start"); | ||
| 19 | wdg.unleash(); | ||
| 20 | |||
| 21 | loop { | ||
| 22 | Timer::after(Duration::from_secs(1)).await; | ||
| 23 | wdg.pet(); | ||
| 24 | } | ||
| 25 | } | ||
diff --git a/examples/stm32f1/.cargo/config.toml b/examples/stm32f1/.cargo/config.toml index e61e739fe..ce6fef11b 100644 --- a/examples/stm32f1/.cargo/config.toml +++ b/examples/stm32f1/.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 STM32F103C8 with your chip as listed in `probe-run --list-chips` | 2 | # replace STM32F103C8 with your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32F103C8" | 3 | runner = "probe-rs run --chip STM32F103C8" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv7m-none-eabi" | 6 | target = "thumbv7m-none-eabi" |
diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 8660e743d..29cad5b67 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml | |||
| @@ -2,19 +2,20 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-stm32f1-examples" | 3 | name = "embassy-stm32f1-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any", "unstable-traits" ] } |
| 11 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | 12 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } |
| 12 | embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } | 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 13 | 14 | ||
| 14 | defmt = "0.3" | 15 | defmt = "0.3" |
| 15 | defmt-rtt = "0.3" | 16 | defmt-rtt = "0.4" |
| 16 | 17 | ||
| 17 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 18 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 18 | cortex-m-rt = "0.7.0" | 19 | cortex-m-rt = "0.7.0" |
| 19 | embedded-hal = "0.2.6" | 20 | embedded-hal = "0.2.6" |
| 20 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 21 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
diff --git a/examples/stm32f1/src/bin/adc.rs b/examples/stm32f1/src/bin/adc.rs index 2d6b4a0e9..ed59e2799 100644 --- a/examples/stm32f1/src/bin/adc.rs +++ b/examples/stm32f1/src/bin/adc.rs | |||
| @@ -16,11 +16,19 @@ async fn main(_spawner: Spawner) { | |||
| 16 | let mut adc = Adc::new(p.ADC1, &mut Delay); | 16 | let mut adc = Adc::new(p.ADC1, &mut Delay); |
| 17 | let mut pin = p.PB1; | 17 | let mut pin = p.PB1; |
| 18 | 18 | ||
| 19 | let mut vref = adc.enable_vref(&mut Delay); | 19 | let mut vrefint = adc.enable_vref(&mut Delay); |
| 20 | adc.calibrate(&mut vref); | 20 | let vrefint_sample = adc.read(&mut vrefint); |
| 21 | let convert_to_millivolts = |sample| { | ||
| 22 | // From http://www.st.com/resource/en/datasheet/CD00161566.pdf | ||
| 23 | // 5.3.4 Embedded reference voltage | ||
| 24 | const VREFINT_MV: u32 = 1200; // mV | ||
| 25 | |||
| 26 | (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 | ||
| 27 | }; | ||
| 28 | |||
| 21 | loop { | 29 | loop { |
| 22 | let v = adc.read(&mut pin); | 30 | let v = adc.read(&mut pin); |
| 23 | info!("--> {} - {} mV", v, adc.to_millivolts(v)); | 31 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); |
| 24 | Timer::after(Duration::from_millis(100)).await; | 32 | Timer::after(Duration::from_millis(100)).await; |
| 25 | } | 33 | } |
| 26 | } | 34 | } |
diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs index a9c46068f..663099ff7 100644 --- a/examples/stm32f1/src/bin/usb_serial.rs +++ b/examples/stm32f1/src/bin/usb_serial.rs | |||
| @@ -4,17 +4,21 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::{panic, *}; | 5 | use defmt::{panic, *}; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_futures::join::join; | ||
| 7 | use embassy_stm32::gpio::{Level, Output, Speed}; | 8 | use embassy_stm32::gpio::{Level, Output, Speed}; |
| 8 | use embassy_stm32::time::Hertz; | 9 | use embassy_stm32::time::Hertz; |
| 9 | use embassy_stm32::usb::{Driver, Instance}; | 10 | use embassy_stm32::usb::{Driver, Instance}; |
| 10 | use embassy_stm32::{interrupt, Config}; | 11 | use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; |
| 11 | use embassy_time::{Duration, Timer}; | 12 | use embassy_time::{Duration, Timer}; |
| 13 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 12 | use embassy_usb::driver::EndpointError; | 14 | use embassy_usb::driver::EndpointError; |
| 13 | use embassy_usb::Builder; | 15 | use embassy_usb::Builder; |
| 14 | use embassy_usb_serial::{CdcAcmClass, State}; | ||
| 15 | use futures::future::join; | ||
| 16 | use {defmt_rtt as _, panic_probe as _}; | 16 | use {defmt_rtt as _, panic_probe as _}; |
| 17 | 17 | ||
| 18 | bind_interrupts!(struct Irqs { | ||
| 19 | USB_LP_CAN1_RX0 => usb::InterruptHandler<peripherals::USB>; | ||
| 20 | }); | ||
| 21 | |||
| 18 | #[embassy_executor::main] | 22 | #[embassy_executor::main] |
| 19 | async fn main(_spawner: Spawner) { | 23 | async fn main(_spawner: Spawner) { |
| 20 | let mut config = Config::default(); | 24 | let mut config = Config::default(); |
| @@ -35,8 +39,7 @@ async fn main(_spawner: Spawner) { | |||
| 35 | } | 39 | } |
| 36 | 40 | ||
| 37 | // Create the driver, from the HAL. | 41 | // Create the driver, from the HAL. |
| 38 | let irq = interrupt::take!(USB_LP_CAN1_RX0); | 42 | let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); |
| 39 | let driver = Driver::new(p.USB, irq, p.PA12, p.PA11); | ||
| 40 | 43 | ||
| 41 | // Create embassy-usb Config | 44 | // Create embassy-usb Config |
| 42 | let config = embassy_usb::Config::new(0xc0de, 0xcafe); | 45 | let config = embassy_usb::Config::new(0xc0de, 0xcafe); |
| @@ -58,7 +61,6 @@ async fn main(_spawner: Spawner) { | |||
| 58 | &mut config_descriptor, | 61 | &mut config_descriptor, |
| 59 | &mut bos_descriptor, | 62 | &mut bos_descriptor, |
| 60 | &mut control_buf, | 63 | &mut control_buf, |
| 61 | None, | ||
| 62 | ); | 64 | ); |
| 63 | 65 | ||
| 64 | // Create classes on the builder. | 66 | // Create classes on the builder. |
diff --git a/examples/stm32f2/.cargo/config.toml b/examples/stm32f2/.cargo/config.toml index 197fadf92..1198fcab8 100644 --- a/examples/stm32f2/.cargo/config.toml +++ b/examples/stm32f2/.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 STM32F207ZGTx with your chip as listed in `probe-run --list-chips` | 2 | # replace STM32F207ZGTx with your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32F207ZGTx" | 3 | runner = "probe-rs run --chip STM32F207ZGTx" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv7m-none-eabi" | 6 | target = "thumbv7m-none-eabi" |
diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index b4bff4d85..652210c7f 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml | |||
| @@ -2,17 +2,18 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-stm32f2-examples" | 3 | name = "embassy-stm32f2-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } |
| 11 | 12 | ||
| 12 | defmt = "0.3" | 13 | defmt = "0.3" |
| 13 | defmt-rtt = "0.3" | 14 | defmt-rtt = "0.4" |
| 14 | 15 | ||
| 15 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 16 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 16 | cortex-m-rt = "0.7.0" | 17 | cortex-m-rt = "0.7.0" |
| 17 | embedded-hal = "0.2.6" | 18 | embedded-hal = "0.2.6" |
| 18 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 19 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
diff --git a/examples/stm32f3/.cargo/config.toml b/examples/stm32f3/.cargo/config.toml index d4bcd263d..cb8a7c5af 100644 --- a/examples/stm32f3/.cargo/config.toml +++ b/examples/stm32f3/.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 STM32F429ZITx with your chip as listed in `probe-run --list-chips` | 2 | # replace STM32F429ZITx with your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32F303ZETx" | 3 | runner = "probe-rs run --chip STM32F303ZETx" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv7em-none-eabihf" | 6 | target = "thumbv7em-none-eabihf" |
diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index d152b145f..489d0ff4c 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml | |||
| @@ -2,20 +2,20 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-stm32f3-examples" | 3 | name = "embassy-stm32f3-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } |
| 11 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | 12 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } |
| 12 | embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } | 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 13 | embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } | ||
| 14 | 14 | ||
| 15 | defmt = "0.3" | 15 | defmt = "0.3" |
| 16 | defmt-rtt = "0.3" | 16 | defmt-rtt = "0.4" |
| 17 | 17 | ||
| 18 | cortex-m = { version = "0.7.6", features = ["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 = "0.3", features = ["print-defmt"] } |
| @@ -23,4 +23,4 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa | |||
| 23 | heapless = { version = "0.7.5", default-features = false } | 23 | heapless = { version = "0.7.5", default-features = false } |
| 24 | nb = "1.0.0" | 24 | nb = "1.0.0" |
| 25 | embedded-storage = "0.3.0" | 25 | embedded-storage = "0.3.0" |
| 26 | static_cell = "1.0" | 26 | static_cell = { version = "1.1", features = ["nightly"]} |
diff --git a/examples/stm32f3/src/bin/flash.rs b/examples/stm32f3/src/bin/flash.rs index 2cf24dbd3..236fb36c1 100644 --- a/examples/stm32f3/src/bin/flash.rs +++ b/examples/stm32f3/src/bin/flash.rs | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | use defmt::{info, unwrap}; | 5 | use defmt::{info, unwrap}; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::flash::Flash; | 7 | use embassy_stm32::flash::Flash; |
| 8 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 9 | ||
| 11 | #[embassy_executor::main] | 10 | #[embassy_executor::main] |
| @@ -15,27 +14,27 @@ async fn main(_spawner: Spawner) { | |||
| 15 | 14 | ||
| 16 | const ADDR: u32 = 0x26000; | 15 | const ADDR: u32 = 0x26000; |
| 17 | 16 | ||
| 18 | let mut f = Flash::unlock(p.FLASH); | 17 | let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region; |
| 19 | 18 | ||
| 20 | info!("Reading..."); | 19 | info!("Reading..."); |
| 21 | let mut buf = [0u8; 8]; | 20 | let mut buf = [0u8; 8]; |
| 22 | unwrap!(f.read(ADDR, &mut buf)); | 21 | unwrap!(f.blocking_read(ADDR, &mut buf)); |
| 23 | info!("Read: {=[u8]:x}", buf); | 22 | info!("Read: {=[u8]:x}", buf); |
| 24 | 23 | ||
| 25 | info!("Erasing..."); | 24 | info!("Erasing..."); |
| 26 | unwrap!(f.erase(ADDR, ADDR + 2048)); | 25 | unwrap!(f.blocking_erase(ADDR, ADDR + 2048)); |
| 27 | 26 | ||
| 28 | info!("Reading..."); | 27 | info!("Reading..."); |
| 29 | let mut buf = [0u8; 8]; | 28 | let mut buf = [0u8; 8]; |
| 30 | unwrap!(f.read(ADDR, &mut buf)); | 29 | unwrap!(f.blocking_read(ADDR, &mut buf)); |
| 31 | info!("Read after erase: {=[u8]:x}", buf); | 30 | info!("Read after erase: {=[u8]:x}", buf); |
| 32 | 31 | ||
| 33 | info!("Writing..."); | 32 | info!("Writing..."); |
| 34 | unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); | 33 | unwrap!(f.blocking_write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); |
| 35 | 34 | ||
| 36 | info!("Reading..."); | 35 | info!("Reading..."); |
| 37 | let mut buf = [0u8; 8]; | 36 | let mut buf = [0u8; 8]; |
| 38 | unwrap!(f.read(ADDR, &mut buf)); | 37 | unwrap!(f.blocking_read(ADDR, &mut buf)); |
| 39 | info!("Read: {=[u8]:x}", buf); | 38 | info!("Read: {=[u8]:x}", buf); |
| 40 | assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); | 39 | assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); |
| 41 | } | 40 | } |
diff --git a/examples/stm32f3/src/bin/multiprio.rs b/examples/stm32f3/src/bin/multiprio.rs index 9e8228a4b..80bf59deb 100644 --- a/examples/stm32f3/src/bin/multiprio.rs +++ b/examples/stm32f3/src/bin/multiprio.rs | |||
| @@ -59,9 +59,9 @@ | |||
| 59 | 59 | ||
| 60 | use cortex_m_rt::entry; | 60 | use cortex_m_rt::entry; |
| 61 | use defmt::*; | 61 | use defmt::*; |
| 62 | use embassy_stm32::executor::{Executor, InterruptExecutor}; | 62 | use embassy_executor::{Executor, InterruptExecutor}; |
| 63 | use embassy_stm32::interrupt; | 63 | use embassy_stm32::interrupt; |
| 64 | use embassy_stm32::interrupt::InterruptExt; | 64 | use embassy_stm32::interrupt::{InterruptExt, Priority}; |
| 65 | use embassy_time::{Duration, Instant, Timer}; | 65 | use embassy_time::{Duration, Instant, Timer}; |
| 66 | use static_cell::StaticCell; | 66 | use static_cell::StaticCell; |
| 67 | use {defmt_rtt as _, panic_probe as _}; | 67 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -108,28 +108,34 @@ async fn run_low() { | |||
| 108 | } | 108 | } |
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | static EXECUTOR_HIGH: StaticCell<InterruptExecutor<interrupt::UART4>> = StaticCell::new(); | 111 | static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); |
| 112 | static EXECUTOR_MED: StaticCell<InterruptExecutor<interrupt::UART5>> = StaticCell::new(); | 112 | static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); |
| 113 | static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new(); | 113 | static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new(); |
| 114 | 114 | ||
| 115 | #[interrupt] | ||
| 116 | unsafe fn UART4() { | ||
| 117 | EXECUTOR_HIGH.on_interrupt() | ||
| 118 | } | ||
| 119 | |||
| 120 | #[interrupt] | ||
| 121 | unsafe fn UART5() { | ||
| 122 | EXECUTOR_MED.on_interrupt() | ||
| 123 | } | ||
| 124 | |||
| 115 | #[entry] | 125 | #[entry] |
| 116 | fn main() -> ! { | 126 | fn main() -> ! { |
| 117 | info!("Hello World!"); | 127 | info!("Hello World!"); |
| 118 | 128 | ||
| 119 | let _p = embassy_stm32::init(Default::default()); | 129 | let _p = embassy_stm32::init(Default::default()); |
| 120 | 130 | ||
| 121 | // High-priority executor: SWI1_EGU1, priority level 6 | 131 | // High-priority executor: UART4, priority level 6 |
| 122 | let irq = interrupt::take!(UART4); | 132 | interrupt::UART4.set_priority(Priority::P6); |
| 123 | irq.set_priority(interrupt::Priority::P6); | 133 | let spawner = EXECUTOR_HIGH.start(interrupt::UART4); |
| 124 | let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq)); | ||
| 125 | let spawner = executor.start(); | ||
| 126 | unwrap!(spawner.spawn(run_high())); | 134 | unwrap!(spawner.spawn(run_high())); |
| 127 | 135 | ||
| 128 | // Medium-priority executor: SWI0_EGU0, priority level 7 | 136 | // Medium-priority executor: UART5, priority level 7 |
| 129 | let irq = interrupt::take!(UART5); | 137 | interrupt::UART5.set_priority(Priority::P7); |
| 130 | irq.set_priority(interrupt::Priority::P7); | 138 | let spawner = EXECUTOR_MED.start(interrupt::UART5); |
| 131 | let executor = EXECUTOR_MED.init(InterruptExecutor::new(irq)); | ||
| 132 | let spawner = executor.start(); | ||
| 133 | unwrap!(spawner.spawn(run_med())); | 139 | unwrap!(spawner.spawn(run_med())); |
| 134 | 140 | ||
| 135 | // Low priority executor: runs in thread mode, using WFE/SEV | 141 | // Low priority executor: runs in thread mode, using WFE/SEV |
diff --git a/examples/stm32f3/src/bin/usart_dma.rs b/examples/stm32f3/src/bin/usart_dma.rs index 3bc5a287f..85f01a69e 100644 --- a/examples/stm32f3/src/bin/usart_dma.rs +++ b/examples/stm32f3/src/bin/usart_dma.rs | |||
| @@ -8,16 +8,21 @@ use defmt::*; | |||
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_stm32::dma::NoDma; | 9 | use embassy_stm32::dma::NoDma; |
| 10 | use embassy_stm32::usart::{Config, Uart}; | 10 | use embassy_stm32::usart::{Config, Uart}; |
| 11 | use embassy_stm32::{bind_interrupts, peripherals, usart}; | ||
| 11 | use heapless::String; | 12 | use heapless::String; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 14 | ||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | USART1 => usart::InterruptHandler<peripherals::USART1>; | ||
| 17 | }); | ||
| 18 | |||
| 14 | #[embassy_executor::main] | 19 | #[embassy_executor::main] |
| 15 | async fn main(_spawner: Spawner) { | 20 | async fn main(_spawner: Spawner) { |
| 16 | let p = embassy_stm32::init(Default::default()); | 21 | let p = embassy_stm32::init(Default::default()); |
| 17 | info!("Hello World!"); | 22 | info!("Hello World!"); |
| 18 | 23 | ||
| 19 | let config = Config::default(); | 24 | let config = Config::default(); |
| 20 | let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, p.DMA1_CH4, NoDma, config); | 25 | let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, Irqs, p.DMA1_CH4, NoDma, config); |
| 21 | 26 | ||
| 22 | for n in 0u32.. { | 27 | for n in 0u32.. { |
| 23 | let mut s: String<128> = String::new(); | 28 | let mut s: String<128> = String::new(); |
diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs index d3702fc35..f15f333b7 100644 --- a/examples/stm32f3/src/bin/usb_serial.rs +++ b/examples/stm32f3/src/bin/usb_serial.rs | |||
| @@ -4,17 +4,21 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::{panic, *}; | 5 | use defmt::{panic, *}; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_futures::join::join; | ||
| 7 | use embassy_stm32::gpio::{Level, Output, Speed}; | 8 | use embassy_stm32::gpio::{Level, Output, Speed}; |
| 8 | use embassy_stm32::time::mhz; | 9 | use embassy_stm32::time::mhz; |
| 9 | use embassy_stm32::usb::{Driver, Instance}; | 10 | use embassy_stm32::usb::{Driver, Instance}; |
| 10 | use embassy_stm32::{interrupt, Config}; | 11 | use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; |
| 11 | use embassy_time::{Duration, Timer}; | 12 | use embassy_time::{Duration, Timer}; |
| 13 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 12 | use embassy_usb::driver::EndpointError; | 14 | use embassy_usb::driver::EndpointError; |
| 13 | use embassy_usb::Builder; | 15 | use embassy_usb::Builder; |
| 14 | use embassy_usb_serial::{CdcAcmClass, State}; | ||
| 15 | use futures::future::join; | ||
| 16 | use {defmt_rtt as _, panic_probe as _}; | 16 | use {defmt_rtt as _, panic_probe as _}; |
| 17 | 17 | ||
| 18 | bind_interrupts!(struct Irqs { | ||
| 19 | USB_LP_CAN_RX0 => usb::InterruptHandler<peripherals::USB>; | ||
| 20 | }); | ||
| 21 | |||
| 18 | #[embassy_executor::main] | 22 | #[embassy_executor::main] |
| 19 | async fn main(_spawner: Spawner) { | 23 | async fn main(_spawner: Spawner) { |
| 20 | let mut config = Config::default(); | 24 | let mut config = Config::default(); |
| @@ -33,8 +37,7 @@ async fn main(_spawner: Spawner) { | |||
| 33 | dp_pullup.set_high(); | 37 | dp_pullup.set_high(); |
| 34 | 38 | ||
| 35 | // Create the driver, from the HAL. | 39 | // Create the driver, from the HAL. |
| 36 | let irq = interrupt::take!(USB_LP_CAN_RX0); | 40 | let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); |
| 37 | let driver = Driver::new(p.USB, irq, p.PA12, p.PA11); | ||
| 38 | 41 | ||
| 39 | // Create embassy-usb Config | 42 | // Create embassy-usb Config |
| 40 | let config = embassy_usb::Config::new(0xc0de, 0xcafe); | 43 | let config = embassy_usb::Config::new(0xc0de, 0xcafe); |
| @@ -55,7 +58,6 @@ async fn main(_spawner: Spawner) { | |||
| 55 | &mut config_descriptor, | 58 | &mut config_descriptor, |
| 56 | &mut bos_descriptor, | 59 | &mut bos_descriptor, |
| 57 | &mut control_buf, | 60 | &mut control_buf, |
| 58 | None, | ||
| 59 | ); | 61 | ); |
| 60 | 62 | ||
| 61 | // Create classes on the builder. | 63 | // Create classes on the builder. |
diff --git a/examples/stm32f4/.cargo/config.toml b/examples/stm32f4/.cargo/config.toml index 4d4363c05..16efa8e6f 100644 --- a/examples/stm32f4/.cargo/config.toml +++ b/examples/stm32f4/.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 STM32F429ZITx with your chip as listed in `probe-run --list-chips` | 2 | # replace STM32F429ZITx with your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32F429ZITx" | 3 | runner = "probe-rs run --chip STM32F429ZITx" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv7em-none-eabi" | 6 | target = "thumbv7em-none-eabi" |
diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 9bfdda92d..c1c821364 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml | |||
| @@ -2,28 +2,31 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-stm32f4-examples" | 3 | name = "embassy-stm32f4-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | 5 | license = "MIT OR Apache-2.0" | |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc", "chrono"] } |
| 12 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 13 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } | ||
| 12 | 14 | ||
| 13 | defmt = "0.3" | 15 | defmt = "0.3" |
| 14 | defmt-rtt = "0.3" | 16 | defmt-rtt = "0.4" |
| 15 | 17 | ||
| 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 18 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 17 | cortex-m-rt = "0.7.0" | 19 | cortex-m-rt = "0.7.0" |
| 18 | embedded-hal = "0.2.6" | 20 | embedded-hal = "0.2.6" |
| 19 | embedded-io = "0.3.0" | 21 | embedded-io = "0.4.0" |
| 20 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 22 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 21 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 23 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } |
| 22 | heapless = { version = "0.7.5", default-features = false } | 24 | heapless = { version = "0.7.5", default-features = false } |
| 23 | nb = "1.0.0" | 25 | nb = "1.0.0" |
| 24 | embedded-storage = "0.3.0" | 26 | embedded-storage = "0.3.0" |
| 25 | micromath = "2.0.0" | 27 | micromath = "2.0.0" |
| 26 | static_cell = "1.0" | 28 | static_cell = { version = "1.1", features = ["nightly"]} |
| 29 | chrono = { version = "^0.4", default-features = false} | ||
| 27 | 30 | ||
| 28 | usb-device = "0.2" | 31 | [profile.release] |
| 29 | usbd-serial = "0.1.1" | 32 | debug = 2 |
diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 871185074..1c9a0b35d 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs | |||
| @@ -2,9 +2,10 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | 4 | ||
| 5 | use cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs; | ||
| 5 | use defmt::*; | 6 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::adc::Adc; | 8 | use embassy_stm32::adc::{Adc, Temperature, VrefInt}; |
| 8 | use embassy_time::{Delay, Duration, Timer}; | 9 | use embassy_time::{Delay, Duration, Timer}; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 11 | ||
| @@ -13,12 +14,55 @@ async fn main(_spawner: Spawner) { | |||
| 13 | let p = embassy_stm32::init(Default::default()); | 14 | let p = embassy_stm32::init(Default::default()); |
| 14 | info!("Hello World!"); | 15 | info!("Hello World!"); |
| 15 | 16 | ||
| 16 | let mut adc = Adc::new(p.ADC1, &mut Delay); | 17 | let mut delay = Delay; |
| 18 | let mut adc = Adc::new(p.ADC1, &mut delay); | ||
| 17 | let mut pin = p.PC1; | 19 | let mut pin = p.PC1; |
| 18 | 20 | ||
| 21 | let mut vrefint = adc.enable_vrefint(); | ||
| 22 | let mut temp = adc.enable_temperature(); | ||
| 23 | |||
| 24 | // Startup delay can be combined to the maximum of either | ||
| 25 | delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us())); | ||
| 26 | |||
| 27 | let vrefint_sample = adc.read_internal(&mut vrefint); | ||
| 28 | |||
| 29 | let convert_to_millivolts = |sample| { | ||
| 30 | // From http://www.st.com/resource/en/datasheet/DM00071990.pdf | ||
| 31 | // 6.3.24 Reference voltage | ||
| 32 | const VREFINT_MV: u32 = 1210; // mV | ||
| 33 | |||
| 34 | (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 | ||
| 35 | }; | ||
| 36 | |||
| 37 | let convert_to_celcius = |sample| { | ||
| 38 | // From http://www.st.com/resource/en/datasheet/DM00071990.pdf | ||
| 39 | // 6.3.22 Temperature sensor characteristics | ||
| 40 | const V25: i32 = 760; // mV | ||
| 41 | const AVG_SLOPE: f32 = 2.5; // mV/C | ||
| 42 | |||
| 43 | let sample_mv = convert_to_millivolts(sample) as i32; | ||
| 44 | |||
| 45 | (sample_mv - V25) as f32 / AVG_SLOPE + 25.0 | ||
| 46 | }; | ||
| 47 | |||
| 48 | info!("VrefInt: {}", vrefint_sample); | ||
| 49 | const MAX_ADC_SAMPLE: u16 = (1 << 12) - 1; | ||
| 50 | info!("VCCA: {} mV", convert_to_millivolts(MAX_ADC_SAMPLE)); | ||
| 51 | |||
| 19 | loop { | 52 | loop { |
| 53 | // Read pin | ||
| 20 | let v = adc.read(&mut pin); | 54 | let v = adc.read(&mut pin); |
| 21 | info!("--> {} - {} mV", v, adc.to_millivolts(v)); | 55 | info!("PC1: {} ({} mV)", v, convert_to_millivolts(v)); |
| 56 | |||
| 57 | // Read internal temperature | ||
| 58 | let v = adc.read_internal(&mut temp); | ||
| 59 | let celcius = convert_to_celcius(v); | ||
| 60 | info!("Internal temp: {} ({} C)", v, celcius); | ||
| 61 | |||
| 62 | // Read internal voltage reference | ||
| 63 | let v = adc.read_internal(&mut vrefint); | ||
| 64 | info!("VrefInt: {}", v); | ||
| 65 | |||
| 22 | Timer::after(Duration::from_millis(100)).await; | 66 | Timer::after(Duration::from_millis(100)).await; |
| 23 | } | 67 | } |
| 24 | } | 68 | } |
diff --git a/examples/stm32f4/src/bin/can.rs b/examples/stm32f4/src/bin/can.rs index e8377b9a1..08bed88db 100644 --- a/examples/stm32f4/src/bin/can.rs +++ b/examples/stm32f4/src/bin/can.rs | |||
| @@ -2,16 +2,25 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | 4 | ||
| 5 | use cortex_m_rt::entry; | ||
| 6 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::bind_interrupts; | ||
| 7 | use embassy_stm32::can::bxcan::filter::Mask32; | 8 | use embassy_stm32::can::bxcan::filter::Mask32; |
| 8 | use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; | 9 | use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; |
| 9 | use embassy_stm32::can::Can; | 10 | use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; |
| 10 | use embassy_stm32::gpio::{Input, Pull}; | 11 | use embassy_stm32::gpio::{Input, Pull}; |
| 12 | use embassy_stm32::peripherals::CAN1; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 14 | ||
| 13 | #[entry] | 15 | bind_interrupts!(struct Irqs { |
| 14 | fn main() -> ! { | 16 | CAN1_RX0 => Rx0InterruptHandler<CAN1>; |
| 17 | CAN1_RX1 => Rx1InterruptHandler<CAN1>; | ||
| 18 | CAN1_SCE => SceInterruptHandler<CAN1>; | ||
| 19 | CAN1_TX => TxInterruptHandler<CAN1>; | ||
| 20 | }); | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(_spawner: Spawner) { | ||
| 15 | info!("Hello World!"); | 24 | info!("Hello World!"); |
| 16 | 25 | ||
| 17 | let mut p = embassy_stm32::init(Default::default()); | 26 | let mut p = embassy_stm32::init(Default::default()); |
| @@ -23,11 +32,14 @@ fn main() -> ! { | |||
| 23 | let rx_pin = Input::new(&mut p.PA11, Pull::Up); | 32 | let rx_pin = Input::new(&mut p.PA11, Pull::Up); |
| 24 | core::mem::forget(rx_pin); | 33 | core::mem::forget(rx_pin); |
| 25 | 34 | ||
| 26 | let mut can = Can::new(p.CAN1, p.PA11, p.PA12); | 35 | let mut can = Can::new(p.CAN1, p.PA11, p.PA12, Irqs); |
| 27 | 36 | ||
| 28 | can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); | 37 | can.as_mut() |
| 38 | .modify_filters() | ||
| 39 | .enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); | ||
| 29 | 40 | ||
| 30 | can.modify_config() | 41 | can.as_mut() |
| 42 | .modify_config() | ||
| 31 | .set_bit_timing(0x001c0003) // http://www.bittiming.can-wiki.info/ | 43 | .set_bit_timing(0x001c0003) // http://www.bittiming.can-wiki.info/ |
| 32 | .set_loopback(true) // Receive own frames | 44 | .set_loopback(true) // Receive own frames |
| 33 | .set_silent(true) | 45 | .set_silent(true) |
| @@ -36,9 +48,8 @@ fn main() -> ! { | |||
| 36 | let mut i: u8 = 0; | 48 | let mut i: u8 = 0; |
| 37 | loop { | 49 | loop { |
| 38 | let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), [i]); | 50 | let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), [i]); |
| 39 | unwrap!(nb::block!(can.transmit(&tx_frame))); | 51 | can.write(&tx_frame).await; |
| 40 | while !can.is_transmitter_idle() {} | 52 | let (_, rx_frame) = can.read().await.unwrap(); |
| 41 | let rx_frame = unwrap!(nb::block!(can.receive())); | ||
| 42 | info!("loopback frame {=u8}", unwrap!(rx_frame.data())[0]); | 53 | info!("loopback frame {=u8}", unwrap!(rx_frame.data())[0]); |
| 43 | i += 1; | 54 | i += 1; |
| 44 | } | 55 | } |
diff --git a/examples/stm32f4/src/bin/dac.rs b/examples/stm32f4/src/bin/dac.rs index d97ae7082..3a6216712 100644 --- a/examples/stm32f4/src/bin/dac.rs +++ b/examples/stm32f4/src/bin/dac.rs | |||
| @@ -4,7 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::dac::{Channel, Dac, Value}; | 7 | use embassy_stm32::dac::{DacCh1, DacChannel, Value}; |
| 8 | use embassy_stm32::dma::NoDma; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 10 | #[embassy_executor::main] | 11 | #[embassy_executor::main] |
| @@ -12,12 +13,12 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 12 | let p = embassy_stm32::init(Default::default()); | 13 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World, dude!"); | 14 | info!("Hello World, dude!"); |
| 14 | 15 | ||
| 15 | let mut dac = Dac::new_1ch(p.DAC, p.PA4); | 16 | let mut dac = DacCh1::new(p.DAC, NoDma, p.PA4); |
| 16 | 17 | ||
| 17 | loop { | 18 | loop { |
| 18 | for v in 0..=255 { | 19 | for v in 0..=255 { |
| 19 | unwrap!(dac.set(Channel::Ch1, Value::Bit8(to_sine_wave(v)))); | 20 | unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); |
| 20 | unwrap!(dac.trigger(Channel::Ch1)); | 21 | dac.trigger(); |
| 21 | } | 22 | } |
| 22 | } | 23 | } |
| 23 | } | 24 | } |
diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs new file mode 100644 index 000000000..d0b164393 --- /dev/null +++ b/examples/stm32f4/src/bin/eth.rs | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_net::tcp::TcpSocket; | ||
| 8 | use embassy_net::{Ipv4Address, Stack, StackResources}; | ||
| 9 | use embassy_stm32::eth::generic_smi::GenericSMI; | ||
| 10 | use embassy_stm32::eth::{Ethernet, PacketQueue}; | ||
| 11 | use embassy_stm32::peripherals::ETH; | ||
| 12 | use embassy_stm32::rng::Rng; | ||
| 13 | use embassy_stm32::time::mhz; | ||
| 14 | use embassy_stm32::{bind_interrupts, eth, Config}; | ||
| 15 | use embassy_time::{Duration, Timer}; | ||
| 16 | use embedded_io::asynch::Write; | ||
| 17 | use static_cell::make_static; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | bind_interrupts!(struct Irqs { | ||
| 20 | ETH => eth::InterruptHandler; | ||
| 21 | }); | ||
| 22 | |||
| 23 | type Device = Ethernet<'static, ETH, GenericSMI>; | ||
| 24 | |||
| 25 | #[embassy_executor::task] | ||
| 26 | async fn net_task(stack: &'static Stack<Device>) -> ! { | ||
| 27 | stack.run().await | ||
| 28 | } | ||
| 29 | |||
| 30 | #[embassy_executor::main] | ||
| 31 | async fn main(spawner: Spawner) -> ! { | ||
| 32 | let mut config = Config::default(); | ||
| 33 | config.rcc.sys_ck = Some(mhz(200)); | ||
| 34 | let p = embassy_stm32::init(config); | ||
| 35 | |||
| 36 | info!("Hello World!"); | ||
| 37 | |||
| 38 | // Generate random seed. | ||
| 39 | let mut rng = Rng::new(p.RNG); | ||
| 40 | let mut seed = [0; 8]; | ||
| 41 | let _ = rng.async_fill_bytes(&mut seed).await; | ||
| 42 | let seed = u64::from_le_bytes(seed); | ||
| 43 | |||
| 44 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; | ||
| 45 | |||
| 46 | let device = Ethernet::new( | ||
| 47 | make_static!(PacketQueue::<16, 16>::new()), | ||
| 48 | p.ETH, | ||
| 49 | Irqs, | ||
| 50 | p.PA1, | ||
| 51 | p.PA2, | ||
| 52 | p.PC1, | ||
| 53 | p.PA7, | ||
| 54 | p.PC4, | ||
| 55 | p.PC5, | ||
| 56 | p.PG13, | ||
| 57 | p.PB13, | ||
| 58 | p.PG11, | ||
| 59 | GenericSMI::new(), | ||
| 60 | mac_addr, | ||
| 61 | 0, | ||
| 62 | ); | ||
| 63 | |||
| 64 | let config = embassy_net::Config::dhcpv4(Default::default()); | ||
| 65 | //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { | ||
| 66 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | ||
| 67 | // dns_servers: Vec::new(), | ||
| 68 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | ||
| 69 | //}); | ||
| 70 | |||
| 71 | // Init network stack | ||
| 72 | let stack = &*make_static!(Stack::new( | ||
| 73 | device, | ||
| 74 | config, | ||
| 75 | make_static!(StackResources::<2>::new()), | ||
| 76 | seed | ||
| 77 | )); | ||
| 78 | |||
| 79 | // Launch network task | ||
| 80 | unwrap!(spawner.spawn(net_task(&stack))); | ||
| 81 | |||
| 82 | info!("Network task initialized"); | ||
| 83 | |||
| 84 | // Then we can use it! | ||
| 85 | let mut rx_buffer = [0; 4096]; | ||
| 86 | let mut tx_buffer = [0; 4096]; | ||
| 87 | |||
| 88 | loop { | ||
| 89 | let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); | ||
| 90 | |||
| 91 | socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); | ||
| 92 | |||
| 93 | let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); | ||
| 94 | info!("connecting..."); | ||
| 95 | let r = socket.connect(remote_endpoint).await; | ||
| 96 | if let Err(e) = r { | ||
| 97 | info!("connect error: {:?}", e); | ||
| 98 | continue; | ||
| 99 | } | ||
| 100 | info!("connected!"); | ||
| 101 | let buf = [0; 1024]; | ||
| 102 | loop { | ||
| 103 | let r = socket.write_all(&buf).await; | ||
| 104 | if let Err(e) = r { | ||
| 105 | info!("write error: {:?}", e); | ||
| 106 | continue; | ||
| 107 | } | ||
| 108 | Timer::after(Duration::from_secs(1)).await; | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
diff --git a/examples/stm32f4/src/bin/flash.rs b/examples/stm32f4/src/bin/flash.rs index 393d61e86..93c54e943 100644 --- a/examples/stm32f4/src/bin/flash.rs +++ b/examples/stm32f4/src/bin/flash.rs | |||
| @@ -4,8 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::{info, unwrap}; | 5 | use defmt::{info, unwrap}; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::flash::Flash; | 7 | use embassy_stm32::flash::{Blocking, Flash}; |
| 8 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 9 | ||
| 11 | #[embassy_executor::main] | 10 | #[embassy_executor::main] |
| @@ -13,7 +12,9 @@ async fn main(_spawner: Spawner) { | |||
| 13 | let p = embassy_stm32::init(Default::default()); | 12 | let p = embassy_stm32::init(Default::default()); |
| 14 | info!("Hello Flash!"); | 13 | info!("Hello Flash!"); |
| 15 | 14 | ||
| 16 | let mut f = Flash::unlock(p.FLASH); | 15 | // Once can also call `into_regions()` to get access to NorFlash implementations |
| 16 | // for each of the unique characteristics. | ||
| 17 | let mut f = Flash::new_blocking(p.FLASH); | ||
| 17 | 18 | ||
| 18 | // Sector 5 | 19 | // Sector 5 |
| 19 | test_flash(&mut f, 128 * 1024, 128 * 1024); | 20 | test_flash(&mut f, 128 * 1024, 128 * 1024); |
| @@ -25,7 +26,7 @@ async fn main(_spawner: Spawner) { | |||
| 25 | test_flash(&mut f, (2048 - 128) * 1024, 128 * 1024); | 26 | test_flash(&mut f, (2048 - 128) * 1024, 128 * 1024); |
| 26 | } | 27 | } |
| 27 | 28 | ||
| 28 | fn test_flash(f: &mut Flash, offset: u32, size: u32) { | 29 | fn test_flash(f: &mut Flash<'_, Blocking>, offset: u32, size: u32) { |
| 29 | info!("Testing offset: {=u32:#X}, size: {=u32:#X}", offset, size); | 30 | info!("Testing offset: {=u32:#X}, size: {=u32:#X}", offset, size); |
| 30 | 31 | ||
| 31 | info!("Reading..."); | 32 | info!("Reading..."); |
| @@ -34,7 +35,7 @@ fn test_flash(f: &mut Flash, offset: u32, size: u32) { | |||
| 34 | info!("Read: {=[u8]:x}", buf); | 35 | info!("Read: {=[u8]:x}", buf); |
| 35 | 36 | ||
| 36 | info!("Erasing..."); | 37 | info!("Erasing..."); |
| 37 | unwrap!(f.erase(offset, offset + size)); | 38 | unwrap!(f.blocking_erase(offset, offset + size)); |
| 38 | 39 | ||
| 39 | info!("Reading..."); | 40 | info!("Reading..."); |
| 40 | let mut buf = [0u8; 32]; | 41 | let mut buf = [0u8; 32]; |
| @@ -42,7 +43,7 @@ fn test_flash(f: &mut Flash, offset: u32, size: u32) { | |||
| 42 | info!("Read after erase: {=[u8]:x}", buf); | 43 | info!("Read after erase: {=[u8]:x}", buf); |
| 43 | 44 | ||
| 44 | info!("Writing..."); | 45 | info!("Writing..."); |
| 45 | unwrap!(f.write( | 46 | unwrap!(f.blocking_write( |
| 46 | offset, | 47 | offset, |
| 47 | &[ | 48 | &[ |
| 48 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, | 49 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, |
diff --git a/examples/stm32f4/src/bin/flash_async.rs b/examples/stm32f4/src/bin/flash_async.rs new file mode 100644 index 000000000..6c9689d9c --- /dev/null +++ b/examples/stm32f4/src/bin/flash_async.rs | |||
| @@ -0,0 +1,85 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{info, unwrap}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::bind_interrupts; | ||
| 8 | use embassy_stm32::flash::{Flash, InterruptHandler}; | ||
| 9 | use embassy_stm32::gpio::{AnyPin, Level, Output, Pin, Speed}; | ||
| 10 | use embassy_time::{Duration, Timer}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | bind_interrupts!(struct Irqs { | ||
| 14 | FLASH => InterruptHandler; | ||
| 15 | }); | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(spawner: Spawner) { | ||
| 19 | let p = embassy_stm32::init(Default::default()); | ||
| 20 | info!("Hello Flash!"); | ||
| 21 | |||
| 22 | let mut f = Flash::new(p.FLASH, Irqs); | ||
| 23 | |||
| 24 | // Led should blink uninterrupted during ~2sec erase operation | ||
| 25 | spawner.spawn(blinky(p.PB7.degrade())).unwrap(); | ||
| 26 | |||
| 27 | // Test on bank 2 in order not to stall CPU. | ||
| 28 | test_flash(&mut f, 1024 * 1024, 128 * 1024).await; | ||
| 29 | } | ||
| 30 | |||
| 31 | #[embassy_executor::task] | ||
| 32 | async fn blinky(p: AnyPin) { | ||
| 33 | let mut led = Output::new(p, Level::High, Speed::Low); | ||
| 34 | |||
| 35 | loop { | ||
| 36 | info!("high"); | ||
| 37 | led.set_high(); | ||
| 38 | Timer::after(Duration::from_millis(300)).await; | ||
| 39 | |||
| 40 | info!("low"); | ||
| 41 | led.set_low(); | ||
| 42 | Timer::after(Duration::from_millis(300)).await; | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | async fn test_flash<'a>(f: &mut Flash<'a>, offset: u32, size: u32) { | ||
| 47 | info!("Testing offset: {=u32:#X}, size: {=u32:#X}", offset, size); | ||
| 48 | |||
| 49 | info!("Reading..."); | ||
| 50 | let mut buf = [0u8; 32]; | ||
| 51 | unwrap!(f.read(offset, &mut buf)); | ||
| 52 | info!("Read: {=[u8]:x}", buf); | ||
| 53 | |||
| 54 | info!("Erasing..."); | ||
| 55 | unwrap!(f.erase(offset, offset + size).await); | ||
| 56 | |||
| 57 | info!("Reading..."); | ||
| 58 | let mut buf = [0u8; 32]; | ||
| 59 | unwrap!(f.read(offset, &mut buf)); | ||
| 60 | info!("Read after erase: {=[u8]:x}", buf); | ||
| 61 | |||
| 62 | info!("Writing..."); | ||
| 63 | unwrap!( | ||
| 64 | f.write( | ||
| 65 | offset, | ||
| 66 | &[ | ||
| 67 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, | ||
| 68 | 29, 30, 31, 32 | ||
| 69 | ] | ||
| 70 | ) | ||
| 71 | .await | ||
| 72 | ); | ||
| 73 | |||
| 74 | info!("Reading..."); | ||
| 75 | let mut buf = [0u8; 32]; | ||
| 76 | unwrap!(f.read(offset, &mut buf)); | ||
| 77 | info!("Read: {=[u8]:x}", buf); | ||
| 78 | assert_eq!( | ||
| 79 | &buf[..], | ||
| 80 | &[ | ||
| 81 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, | ||
| 82 | 30, 31, 32 | ||
| 83 | ] | ||
| 84 | ); | ||
| 85 | } | ||
diff --git a/examples/stm32f4/src/bin/i2c.rs b/examples/stm32f4/src/bin/i2c.rs new file mode 100644 index 000000000..a92957325 --- /dev/null +++ b/examples/stm32f4/src/bin/i2c.rs | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::dma::NoDma; | ||
| 8 | use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; | ||
| 9 | use embassy_stm32::time::Hertz; | ||
| 10 | use embassy_stm32::{bind_interrupts, i2c, peripherals}; | ||
| 11 | use embassy_time::Duration; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | const ADDRESS: u8 = 0x5F; | ||
| 15 | const WHOAMI: u8 = 0x0F; | ||
| 16 | |||
| 17 | bind_interrupts!(struct Irqs { | ||
| 18 | I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>; | ||
| 19 | }); | ||
| 20 | |||
| 21 | #[embassy_executor::main] | ||
| 22 | async fn main(_spawner: Spawner) { | ||
| 23 | info!("Hello world!"); | ||
| 24 | let p = embassy_stm32::init(Default::default()); | ||
| 25 | |||
| 26 | let mut i2c = I2c::new( | ||
| 27 | p.I2C2, | ||
| 28 | p.PB10, | ||
| 29 | p.PB11, | ||
| 30 | Irqs, | ||
| 31 | NoDma, | ||
| 32 | NoDma, | ||
| 33 | Hertz(100_000), | ||
| 34 | Default::default(), | ||
| 35 | ); | ||
| 36 | |||
| 37 | // I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long. | ||
| 38 | // TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay. | ||
| 39 | let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); | ||
| 40 | |||
| 41 | let mut data = [0u8; 1]; | ||
| 42 | |||
| 43 | match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { | ||
| 44 | Ok(()) => info!("Whoami: {}", data[0]), | ||
| 45 | Err(Error::Timeout) => error!("Operation timed out"), | ||
| 46 | Err(e) => error!("I2c Error: {:?}", e), | ||
| 47 | } | ||
| 48 | } | ||
diff --git a/examples/stm32f4/src/bin/i2s_dma.rs b/examples/stm32f4/src/bin/i2s_dma.rs new file mode 100644 index 000000000..e8d7b5f77 --- /dev/null +++ b/examples/stm32f4/src/bin/i2s_dma.rs | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::fmt::Write; | ||
| 6 | |||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_stm32::i2s::{Config, I2S}; | ||
| 10 | use embassy_stm32::time::Hertz; | ||
| 11 | use heapless::String; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | #[embassy_executor::main] | ||
| 15 | async fn main(_spawner: Spawner) { | ||
| 16 | let p = embassy_stm32::init(Default::default()); | ||
| 17 | info!("Hello World!"); | ||
| 18 | |||
| 19 | let mut i2s = I2S::new( | ||
| 20 | p.SPI2, | ||
| 21 | p.PC3, // sd | ||
| 22 | p.PB12, // ws | ||
| 23 | p.PB10, // ck | ||
| 24 | p.PC6, // mck | ||
| 25 | p.DMA1_CH4, | ||
| 26 | p.DMA1_CH3, | ||
| 27 | Hertz(1_000_000), | ||
| 28 | Config::default(), | ||
| 29 | ); | ||
| 30 | |||
| 31 | for n in 0u32.. { | ||
| 32 | let mut write: String<128> = String::new(); | ||
| 33 | core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap(); | ||
| 34 | i2s.write(&mut write.as_bytes()).await.ok(); | ||
| 35 | } | ||
| 36 | } | ||
diff --git a/examples/stm32f4/src/bin/mco.rs b/examples/stm32f4/src/bin/mco.rs new file mode 100644 index 000000000..2b9ceebc3 --- /dev/null +++ b/examples/stm32f4/src/bin/mco.rs | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 8 | use embassy_stm32::rcc::{Mco, Mco1Source, Mco2Source, McoClock}; | ||
| 9 | use embassy_time::{Duration, Timer}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) { | ||
| 14 | let p = embassy_stm32::init(Default::default()); | ||
| 15 | info!("Hello World!"); | ||
| 16 | |||
| 17 | let _mco1 = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::DIV1); | ||
| 18 | let _mco2 = Mco::new(p.MCO2, p.PC9, Mco2Source::Pll, McoClock::DIV4); | ||
| 19 | let mut led = Output::new(p.PB7, Level::High, Speed::Low); | ||
| 20 | |||
| 21 | loop { | ||
| 22 | info!("high"); | ||
| 23 | led.set_high(); | ||
| 24 | Timer::after(Duration::from_millis(300)).await; | ||
| 25 | |||
| 26 | info!("low"); | ||
| 27 | led.set_low(); | ||
| 28 | Timer::after(Duration::from_millis(300)).await; | ||
| 29 | } | ||
| 30 | } | ||
diff --git a/examples/stm32f4/src/bin/multiprio.rs b/examples/stm32f4/src/bin/multiprio.rs index 9e8228a4b..80bf59deb 100644 --- a/examples/stm32f4/src/bin/multiprio.rs +++ b/examples/stm32f4/src/bin/multiprio.rs | |||
| @@ -59,9 +59,9 @@ | |||
| 59 | 59 | ||
| 60 | use cortex_m_rt::entry; | 60 | use cortex_m_rt::entry; |
| 61 | use defmt::*; | 61 | use defmt::*; |
| 62 | use embassy_stm32::executor::{Executor, InterruptExecutor}; | 62 | use embassy_executor::{Executor, InterruptExecutor}; |
| 63 | use embassy_stm32::interrupt; | 63 | use embassy_stm32::interrupt; |
| 64 | use embassy_stm32::interrupt::InterruptExt; | 64 | use embassy_stm32::interrupt::{InterruptExt, Priority}; |
| 65 | use embassy_time::{Duration, Instant, Timer}; | 65 | use embassy_time::{Duration, Instant, Timer}; |
| 66 | use static_cell::StaticCell; | 66 | use static_cell::StaticCell; |
| 67 | use {defmt_rtt as _, panic_probe as _}; | 67 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -108,28 +108,34 @@ async fn run_low() { | |||
| 108 | } | 108 | } |
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | static EXECUTOR_HIGH: StaticCell<InterruptExecutor<interrupt::UART4>> = StaticCell::new(); | 111 | static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); |
| 112 | static EXECUTOR_MED: StaticCell<InterruptExecutor<interrupt::UART5>> = StaticCell::new(); | 112 | static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); |
| 113 | static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new(); | 113 | static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new(); |
| 114 | 114 | ||
| 115 | #[interrupt] | ||
| 116 | unsafe fn UART4() { | ||
| 117 | EXECUTOR_HIGH.on_interrupt() | ||
| 118 | } | ||
| 119 | |||
| 120 | #[interrupt] | ||
| 121 | unsafe fn UART5() { | ||
| 122 | EXECUTOR_MED.on_interrupt() | ||
| 123 | } | ||
| 124 | |||
| 115 | #[entry] | 125 | #[entry] |
| 116 | fn main() -> ! { | 126 | fn main() -> ! { |
| 117 | info!("Hello World!"); | 127 | info!("Hello World!"); |
| 118 | 128 | ||
| 119 | let _p = embassy_stm32::init(Default::default()); | 129 | let _p = embassy_stm32::init(Default::default()); |
| 120 | 130 | ||
| 121 | // High-priority executor: SWI1_EGU1, priority level 6 | 131 | // High-priority executor: UART4, priority level 6 |
| 122 | let irq = interrupt::take!(UART4); | 132 | interrupt::UART4.set_priority(Priority::P6); |
| 123 | irq.set_priority(interrupt::Priority::P6); | 133 | let spawner = EXECUTOR_HIGH.start(interrupt::UART4); |
| 124 | let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq)); | ||
| 125 | let spawner = executor.start(); | ||
| 126 | unwrap!(spawner.spawn(run_high())); | 134 | unwrap!(spawner.spawn(run_high())); |
| 127 | 135 | ||
| 128 | // Medium-priority executor: SWI0_EGU0, priority level 7 | 136 | // Medium-priority executor: UART5, priority level 7 |
| 129 | let irq = interrupt::take!(UART5); | 137 | interrupt::UART5.set_priority(Priority::P7); |
| 130 | irq.set_priority(interrupt::Priority::P7); | 138 | let spawner = EXECUTOR_MED.start(interrupt::UART5); |
| 131 | let executor = EXECUTOR_MED.init(InterruptExecutor::new(irq)); | ||
| 132 | let spawner = executor.start(); | ||
| 133 | unwrap!(spawner.spawn(run_med())); | 139 | unwrap!(spawner.spawn(run_med())); |
| 134 | 140 | ||
| 135 | // Low priority executor: runs in thread mode, using WFE/SEV | 141 | // Low priority executor: runs in thread mode, using WFE/SEV |
diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs new file mode 100644 index 000000000..a8a68ed6e --- /dev/null +++ b/examples/stm32f4/src/bin/pwm_complementary.rs | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::pwm::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin}; | ||
| 8 | use embassy_stm32::pwm::simple_pwm::PwmPin; | ||
| 9 | use embassy_stm32::pwm::Channel; | ||
| 10 | use embassy_stm32::time::khz; | ||
| 11 | use embassy_time::{Duration, Timer}; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | #[embassy_executor::main] | ||
| 15 | async fn main(_spawner: Spawner) { | ||
| 16 | let p = embassy_stm32::init(Default::default()); | ||
| 17 | info!("Hello World!"); | ||
| 18 | |||
| 19 | let ch1 = PwmPin::new_ch1(p.PE9); | ||
| 20 | let ch1n = ComplementaryPwmPin::new_ch1(p.PA7); | ||
| 21 | let mut pwm = ComplementaryPwm::new( | ||
| 22 | p.TIM1, | ||
| 23 | Some(ch1), | ||
| 24 | Some(ch1n), | ||
| 25 | None, | ||
| 26 | None, | ||
| 27 | None, | ||
| 28 | None, | ||
| 29 | None, | ||
| 30 | None, | ||
| 31 | khz(10), | ||
| 32 | ); | ||
| 33 | |||
| 34 | let max = pwm.get_max_duty(); | ||
| 35 | pwm.set_dead_time(max / 1024); | ||
| 36 | |||
| 37 | pwm.enable(Channel::Ch1); | ||
| 38 | |||
| 39 | info!("PWM initialized"); | ||
| 40 | info!("PWM max duty {}", max); | ||
| 41 | |||
| 42 | loop { | ||
| 43 | pwm.set_duty(Channel::Ch1, 0); | ||
| 44 | Timer::after(Duration::from_millis(300)).await; | ||
| 45 | pwm.set_duty(Channel::Ch1, max / 4); | ||
| 46 | Timer::after(Duration::from_millis(300)).await; | ||
| 47 | pwm.set_duty(Channel::Ch1, max / 2); | ||
| 48 | Timer::after(Duration::from_millis(300)).await; | ||
| 49 | pwm.set_duty(Channel::Ch1, max - 1); | ||
| 50 | Timer::after(Duration::from_millis(300)).await; | ||
| 51 | } | ||
| 52 | } | ||
diff --git a/examples/stm32f4/src/bin/rtc.rs b/examples/stm32f4/src/bin/rtc.rs new file mode 100644 index 000000000..0eca58203 --- /dev/null +++ b/examples/stm32f4/src/bin/rtc.rs | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use chrono::{NaiveDate, NaiveDateTime}; | ||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_stm32::rtc::{Rtc, RtcConfig}; | ||
| 9 | use embassy_time::{Duration, Timer}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) { | ||
| 14 | let p = embassy_stm32::init(Default::default()); | ||
| 15 | info!("Hello World!"); | ||
| 16 | |||
| 17 | let now = NaiveDate::from_ymd_opt(2020, 5, 15) | ||
| 18 | .unwrap() | ||
| 19 | .and_hms_opt(10, 30, 15) | ||
| 20 | .unwrap(); | ||
| 21 | |||
| 22 | let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); | ||
| 23 | |||
| 24 | rtc.set_datetime(now.into()).expect("datetime not set"); | ||
| 25 | |||
| 26 | // In reality the delay would be much longer | ||
| 27 | Timer::after(Duration::from_millis(20000)).await; | ||
| 28 | |||
| 29 | let _then: NaiveDateTime = rtc.now().unwrap().into(); | ||
| 30 | } | ||
diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index 0edd8a61a..6ec7d0fec 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs | |||
| @@ -4,23 +4,30 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::sdmmc::Sdmmc; | 7 | use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; |
| 8 | use embassy_stm32::time::mhz; | 8 | use embassy_stm32::time::mhz; |
| 9 | use embassy_stm32::{interrupt, Config}; | 9 | use embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config}; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 11 | ||
| 12 | /// This is a safeguard to not overwrite any data on the SD card. | ||
| 13 | /// If you don't care about SD card contents, set this to `true` to test writes. | ||
| 14 | const ALLOW_WRITES: bool = false; | ||
| 15 | |||
| 16 | bind_interrupts!(struct Irqs { | ||
| 17 | SDIO => sdmmc::InterruptHandler<peripherals::SDIO>; | ||
| 18 | }); | ||
| 19 | |||
| 12 | #[embassy_executor::main] | 20 | #[embassy_executor::main] |
| 13 | async fn main(_spawner: Spawner) -> ! { | 21 | async fn main(_spawner: Spawner) { |
| 14 | let mut config = Config::default(); | 22 | let mut config = Config::default(); |
| 15 | config.rcc.sys_ck = Some(mhz(48)); | 23 | config.rcc.sys_ck = Some(mhz(48)); |
| 24 | config.rcc.pll48 = true; | ||
| 16 | let p = embassy_stm32::init(config); | 25 | let p = embassy_stm32::init(config); |
| 17 | info!("Hello World!"); | 26 | info!("Hello World!"); |
| 18 | 27 | ||
| 19 | let irq = interrupt::take!(SDIO); | ||
| 20 | |||
| 21 | let mut sdmmc = Sdmmc::new_4bit( | 28 | let mut sdmmc = Sdmmc::new_4bit( |
| 22 | p.SDIO, | 29 | p.SDIO, |
| 23 | irq, | 30 | Irqs, |
| 24 | p.DMA2_CH3, | 31 | p.DMA2_CH3, |
| 25 | p.PC12, | 32 | p.PC12, |
| 26 | p.PD2, | 33 | p.PD2, |
| @@ -34,11 +41,51 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 34 | // Should print 400kHz for initialization | 41 | // Should print 400kHz for initialization |
| 35 | info!("Configured clock: {}", sdmmc.clock().0); | 42 | info!("Configured clock: {}", sdmmc.clock().0); |
| 36 | 43 | ||
| 37 | unwrap!(sdmmc.init_card(mhz(25)).await); | 44 | let mut err = None; |
| 45 | loop { | ||
| 46 | match sdmmc.init_card(mhz(24)).await { | ||
| 47 | Ok(_) => break, | ||
| 48 | Err(e) => { | ||
| 49 | if err != Some(e) { | ||
| 50 | info!("waiting for card error, retrying: {:?}", e); | ||
| 51 | err = Some(e); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 38 | 56 | ||
| 39 | let card = unwrap!(sdmmc.card()); | 57 | let card = unwrap!(sdmmc.card()); |
| 40 | 58 | ||
| 41 | info!("Card: {:#?}", Debug2Format(card)); | 59 | info!("Card: {:#?}", Debug2Format(card)); |
| 60 | info!("Clock: {}", sdmmc.clock()); | ||
| 61 | |||
| 62 | // Arbitrary block index | ||
| 63 | let block_idx = 16; | ||
| 64 | |||
| 65 | // SDMMC uses `DataBlock` instead of `&[u8]` to ensure 4 byte alignment required by the hardware. | ||
| 66 | let mut block = DataBlock([0u8; 512]); | ||
| 67 | |||
| 68 | sdmmc.read_block(block_idx, &mut block).await.unwrap(); | ||
| 69 | info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); | ||
| 70 | |||
| 71 | if !ALLOW_WRITES { | ||
| 72 | info!("Writing is disabled."); | ||
| 73 | loop {} | ||
| 74 | } | ||
| 75 | |||
| 76 | info!("Filling block with 0x55"); | ||
| 77 | block.fill(0x55); | ||
| 78 | sdmmc.write_block(block_idx, &block).await.unwrap(); | ||
| 79 | info!("Write done"); | ||
| 80 | |||
| 81 | sdmmc.read_block(block_idx, &mut block).await.unwrap(); | ||
| 82 | info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); | ||
| 83 | |||
| 84 | info!("Filling block with 0xAA"); | ||
| 85 | block.fill(0xAA); | ||
| 86 | sdmmc.write_block(block_idx, &block).await.unwrap(); | ||
| 87 | info!("Write done"); | ||
| 42 | 88 | ||
| 43 | loop {} | 89 | sdmmc.read_block(block_idx, &mut block).await.unwrap(); |
| 90 | info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); | ||
| 44 | } | 91 | } |
diff --git a/examples/stm32f4/src/bin/usart.rs b/examples/stm32f4/src/bin/usart.rs index 90ad882b8..7680fe845 100644 --- a/examples/stm32f4/src/bin/usart.rs +++ b/examples/stm32f4/src/bin/usart.rs | |||
| @@ -6,8 +6,13 @@ use cortex_m_rt::entry; | |||
| 6 | use defmt::*; | 6 | use defmt::*; |
| 7 | use embassy_stm32::dma::NoDma; | 7 | use embassy_stm32::dma::NoDma; |
| 8 | use embassy_stm32::usart::{Config, Uart}; | 8 | use embassy_stm32::usart::{Config, Uart}; |
| 9 | use embassy_stm32::{bind_interrupts, peripherals, usart}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 11 | ||
| 12 | bind_interrupts!(struct Irqs { | ||
| 13 | USART3 => usart::InterruptHandler<peripherals::USART3>; | ||
| 14 | }); | ||
| 15 | |||
| 11 | #[entry] | 16 | #[entry] |
| 12 | fn main() -> ! { | 17 | fn main() -> ! { |
| 13 | info!("Hello World!"); | 18 | info!("Hello World!"); |
| @@ -15,7 +20,7 @@ fn main() -> ! { | |||
| 15 | let p = embassy_stm32::init(Default::default()); | 20 | let p = embassy_stm32::init(Default::default()); |
| 16 | 21 | ||
| 17 | let config = Config::default(); | 22 | let config = Config::default(); |
| 18 | let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, NoDma, NoDma, config); | 23 | let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, Irqs, NoDma, NoDma, config); |
| 19 | 24 | ||
| 20 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); | 25 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); |
| 21 | info!("wrote Hello, starting echo"); | 26 | info!("wrote Hello, starting echo"); |
diff --git a/examples/stm32f4/src/bin/usart_buffered.rs b/examples/stm32f4/src/bin/usart_buffered.rs index 7bcecbd26..c573dc3a3 100644 --- a/examples/stm32f4/src/bin/usart_buffered.rs +++ b/examples/stm32f4/src/bin/usart_buffered.rs | |||
| @@ -4,25 +4,25 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::dma::NoDma; | 7 | use embassy_stm32::usart::{BufferedUart, Config}; |
| 8 | use embassy_stm32::interrupt; | 8 | use embassy_stm32::{bind_interrupts, peripherals, usart}; |
| 9 | use embassy_stm32::usart::{BufferedUart, Config, State, Uart}; | ||
| 10 | use embedded_io::asynch::BufRead; | 9 | use embedded_io::asynch::BufRead; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 11 | ||
| 12 | bind_interrupts!(struct Irqs { | ||
| 13 | USART3 => usart::BufferedInterruptHandler<peripherals::USART3>; | ||
| 14 | }); | ||
| 15 | |||
| 13 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 14 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 15 | let p = embassy_stm32::init(Default::default()); | 18 | let p = embassy_stm32::init(Default::default()); |
| 16 | info!("Hello World!"); | 19 | info!("Hello World!"); |
| 17 | 20 | ||
| 18 | let config = Config::default(); | 21 | let config = Config::default(); |
| 19 | let usart = Uart::new(p.USART3, p.PD9, p.PD8, NoDma, NoDma, config); | ||
| 20 | 22 | ||
| 21 | let mut state = State::new(); | ||
| 22 | let irq = interrupt::take!(USART3); | ||
| 23 | let mut tx_buf = [0u8; 32]; | 23 | let mut tx_buf = [0u8; 32]; |
| 24 | let mut rx_buf = [0u8; 32]; | 24 | let mut rx_buf = [0u8; 32]; |
| 25 | let mut buf_usart = BufferedUart::new(&mut state, usart, irq, &mut tx_buf, &mut rx_buf); | 25 | let mut buf_usart = BufferedUart::new(p.USART3, Irqs, p.PD9, p.PD8, &mut tx_buf, &mut rx_buf, config); |
| 26 | 26 | ||
| 27 | loop { | 27 | loop { |
| 28 | let buf = buf_usart.fill_buf().await.unwrap(); | 28 | let buf = buf_usart.fill_buf().await.unwrap(); |
diff --git a/examples/stm32f4/src/bin/usart_dma.rs b/examples/stm32f4/src/bin/usart_dma.rs index bb41b8b4f..3408ec370 100644 --- a/examples/stm32f4/src/bin/usart_dma.rs +++ b/examples/stm32f4/src/bin/usart_dma.rs | |||
| @@ -8,16 +8,21 @@ use defmt::*; | |||
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_stm32::dma::NoDma; | 9 | use embassy_stm32::dma::NoDma; |
| 10 | use embassy_stm32::usart::{Config, Uart}; | 10 | use embassy_stm32::usart::{Config, Uart}; |
| 11 | use embassy_stm32::{bind_interrupts, peripherals, usart}; | ||
| 11 | use heapless::String; | 12 | use heapless::String; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 14 | ||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | USART3 => usart::InterruptHandler<peripherals::USART3>; | ||
| 17 | }); | ||
| 18 | |||
| 14 | #[embassy_executor::main] | 19 | #[embassy_executor::main] |
| 15 | async fn main(_spawner: Spawner) { | 20 | async fn main(_spawner: Spawner) { |
| 16 | let p = embassy_stm32::init(Default::default()); | 21 | let p = embassy_stm32::init(Default::default()); |
| 17 | info!("Hello World!"); | 22 | info!("Hello World!"); |
| 18 | 23 | ||
| 19 | let config = Config::default(); | 24 | let config = Config::default(); |
| 20 | let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, p.DMA1_CH3, NoDma, config); | 25 | let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, Irqs, p.DMA1_CH3, NoDma, config); |
| 21 | 26 | ||
| 22 | for n in 0u32.. { | 27 | for n in 0u32.. { |
| 23 | let mut s: String<128> = String::new(); | 28 | let mut s: String<128> = String::new(); |
diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs new file mode 100644 index 000000000..b1f01417c --- /dev/null +++ b/examples/stm32f4/src/bin/usb_ethernet.rs | |||
| @@ -0,0 +1,164 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_net::tcp::TcpSocket; | ||
| 8 | use embassy_net::{Stack, StackResources}; | ||
| 9 | use embassy_stm32::rng::Rng; | ||
| 10 | use embassy_stm32::time::mhz; | ||
| 11 | use embassy_stm32::usb_otg::Driver; | ||
| 12 | use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; | ||
| 13 | use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; | ||
| 14 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; | ||
| 15 | use embassy_usb::{Builder, UsbDevice}; | ||
| 16 | use embedded_io::asynch::Write; | ||
| 17 | use static_cell::make_static; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | type UsbDriver = Driver<'static, embassy_stm32::peripherals::USB_OTG_FS>; | ||
| 21 | |||
| 22 | const MTU: usize = 1514; | ||
| 23 | |||
| 24 | #[embassy_executor::task] | ||
| 25 | async fn usb_task(mut device: UsbDevice<'static, UsbDriver>) -> ! { | ||
| 26 | device.run().await | ||
| 27 | } | ||
| 28 | |||
| 29 | #[embassy_executor::task] | ||
| 30 | async fn usb_ncm_task(class: Runner<'static, UsbDriver, MTU>) -> ! { | ||
| 31 | class.run().await | ||
| 32 | } | ||
| 33 | |||
| 34 | #[embassy_executor::task] | ||
| 35 | async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! { | ||
| 36 | stack.run().await | ||
| 37 | } | ||
| 38 | |||
| 39 | bind_interrupts!(struct Irqs { | ||
| 40 | OTG_FS => usb_otg::InterruptHandler<peripherals::USB_OTG_FS>; | ||
| 41 | }); | ||
| 42 | |||
| 43 | #[embassy_executor::main] | ||
| 44 | async fn main(spawner: Spawner) { | ||
| 45 | info!("Hello World!"); | ||
| 46 | |||
| 47 | let mut config = Config::default(); | ||
| 48 | config.rcc.pll48 = true; | ||
| 49 | config.rcc.sys_ck = Some(mhz(48)); | ||
| 50 | |||
| 51 | let p = embassy_stm32::init(config); | ||
| 52 | |||
| 53 | // Create the driver, from the HAL. | ||
| 54 | let ep_out_buffer = &mut make_static!([0; 256])[..]; | ||
| 55 | let mut config = embassy_stm32::usb_otg::Config::default(); | ||
| 56 | config.vbus_detection = true; | ||
| 57 | let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, ep_out_buffer, config); | ||
| 58 | |||
| 59 | // Create embassy-usb Config | ||
| 60 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 61 | config.manufacturer = Some("Embassy"); | ||
| 62 | config.product = Some("USB-Ethernet example"); | ||
| 63 | config.serial_number = Some("12345678"); | ||
| 64 | config.max_power = 100; | ||
| 65 | config.max_packet_size_0 = 64; | ||
| 66 | |||
| 67 | // Required for Windows support. | ||
| 68 | config.composite_with_iads = true; | ||
| 69 | config.device_class = 0xEF; | ||
| 70 | config.device_sub_class = 0x02; | ||
| 71 | config.device_protocol = 0x01; | ||
| 72 | |||
| 73 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 74 | let mut builder = Builder::new( | ||
| 75 | driver, | ||
| 76 | config, | ||
| 77 | &mut make_static!([0; 256])[..], | ||
| 78 | &mut make_static!([0; 256])[..], | ||
| 79 | &mut make_static!([0; 256])[..], | ||
| 80 | &mut make_static!([0; 128])[..], | ||
| 81 | ); | ||
| 82 | |||
| 83 | // Our MAC addr. | ||
| 84 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; | ||
| 85 | // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. | ||
| 86 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; | ||
| 87 | |||
| 88 | // Create classes on the builder. | ||
| 89 | let class = CdcNcmClass::new(&mut builder, make_static!(State::new()), host_mac_addr, 64); | ||
| 90 | |||
| 91 | // Build the builder. | ||
| 92 | let usb = builder.build(); | ||
| 93 | |||
| 94 | unwrap!(spawner.spawn(usb_task(usb))); | ||
| 95 | |||
| 96 | let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(make_static!(NetState::new()), our_mac_addr); | ||
| 97 | unwrap!(spawner.spawn(usb_ncm_task(runner))); | ||
| 98 | |||
| 99 | let config = embassy_net::Config::dhcpv4(Default::default()); | ||
| 100 | //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { | ||
| 101 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | ||
| 102 | // dns_servers: Vec::new(), | ||
| 103 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | ||
| 104 | //}); | ||
| 105 | |||
| 106 | // Generate random seed | ||
| 107 | let mut rng = Rng::new(p.RNG); | ||
| 108 | let mut seed = [0; 8]; | ||
| 109 | unwrap!(rng.async_fill_bytes(&mut seed).await); | ||
| 110 | let seed = u64::from_le_bytes(seed); | ||
| 111 | |||
| 112 | // Init network stack | ||
| 113 | let stack = &*make_static!(Stack::new( | ||
| 114 | device, | ||
| 115 | config, | ||
| 116 | make_static!(StackResources::<2>::new()), | ||
| 117 | seed | ||
| 118 | )); | ||
| 119 | |||
| 120 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 121 | |||
| 122 | // And now we can use it! | ||
| 123 | |||
| 124 | let mut rx_buffer = [0; 4096]; | ||
| 125 | let mut tx_buffer = [0; 4096]; | ||
| 126 | let mut buf = [0; 4096]; | ||
| 127 | |||
| 128 | loop { | ||
| 129 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 130 | socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); | ||
| 131 | |||
| 132 | info!("Listening on TCP:1234..."); | ||
| 133 | if let Err(e) = socket.accept(1234).await { | ||
| 134 | warn!("accept error: {:?}", e); | ||
| 135 | continue; | ||
| 136 | } | ||
| 137 | |||
| 138 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 139 | |||
| 140 | loop { | ||
| 141 | let n = match socket.read(&mut buf).await { | ||
| 142 | Ok(0) => { | ||
| 143 | warn!("read EOF"); | ||
| 144 | break; | ||
| 145 | } | ||
| 146 | Ok(n) => n, | ||
| 147 | Err(e) => { | ||
| 148 | warn!("read error: {:?}", e); | ||
| 149 | break; | ||
| 150 | } | ||
| 151 | }; | ||
| 152 | |||
| 153 | info!("rxd {:02x}", &buf[..n]); | ||
| 154 | |||
| 155 | match socket.write_all(&buf[..n]).await { | ||
| 156 | Ok(()) => {} | ||
| 157 | Err(e) => { | ||
| 158 | warn!("write error: {:?}", e); | ||
| 159 | break; | ||
| 160 | } | ||
| 161 | }; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | } | ||
diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs new file mode 100644 index 000000000..4ff6452ef --- /dev/null +++ b/examples/stm32f4/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,110 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{panic, *}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::time::mhz; | ||
| 8 | use embassy_stm32::usb_otg::{Driver, Instance}; | ||
| 9 | use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; | ||
| 10 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 11 | use embassy_usb::driver::EndpointError; | ||
| 12 | use embassy_usb::Builder; | ||
| 13 | use futures::future::join; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | bind_interrupts!(struct Irqs { | ||
| 17 | OTG_FS => usb_otg::InterruptHandler<peripherals::USB_OTG_FS>; | ||
| 18 | }); | ||
| 19 | |||
| 20 | #[embassy_executor::main] | ||
| 21 | async fn main(_spawner: Spawner) { | ||
| 22 | info!("Hello World!"); | ||
| 23 | |||
| 24 | let mut config = Config::default(); | ||
| 25 | config.rcc.pll48 = true; | ||
| 26 | config.rcc.sys_ck = Some(mhz(48)); | ||
| 27 | |||
| 28 | let p = embassy_stm32::init(config); | ||
| 29 | |||
| 30 | // Create the driver, from the HAL. | ||
| 31 | let mut ep_out_buffer = [0u8; 256]; | ||
| 32 | let mut config = embassy_stm32::usb_otg::Config::default(); | ||
| 33 | config.vbus_detection = true; | ||
| 34 | let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); | ||
| 35 | |||
| 36 | // Create embassy-usb Config | ||
| 37 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 38 | config.manufacturer = Some("Embassy"); | ||
| 39 | config.product = Some("USB-serial example"); | ||
| 40 | config.serial_number = Some("12345678"); | ||
| 41 | |||
| 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. | ||
| 50 | // It needs some buffers for building the descriptors. | ||
| 51 | let mut device_descriptor = [0; 256]; | ||
| 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 device_descriptor, | ||
| 62 | &mut config_descriptor, | ||
| 63 | &mut bos_descriptor, | ||
| 64 | &mut control_buf, | ||
| 65 | ); | ||
| 66 | |||
| 67 | // Create classes on the builder. | ||
| 68 | let mut 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 | // Do stuff with the class! | ||
| 77 | let echo_fut = async { | ||
| 78 | loop { | ||
| 79 | class.wait_connection().await; | ||
| 80 | info!("Connected"); | ||
| 81 | let _ = echo(&mut class).await; | ||
| 82 | info!("Disconnected"); | ||
| 83 | } | ||
| 84 | }; | ||
| 85 | |||
| 86 | // Run everything concurrently. | ||
| 87 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 88 | join(usb_fut, echo_fut).await; | ||
| 89 | } | ||
| 90 | |||
| 91 | struct Disconnected {} | ||
| 92 | |||
| 93 | impl From<EndpointError> for Disconnected { | ||
| 94 | fn from(val: EndpointError) -> Self { | ||
| 95 | match val { | ||
| 96 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 97 | EndpointError::Disabled => Disconnected {}, | ||
| 98 | } | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 103 | let mut buf = [0; 64]; | ||
| 104 | loop { | ||
| 105 | let n = class.read_packet(&mut buf).await?; | ||
| 106 | let data = &buf[..n]; | ||
| 107 | info!("data: {:x}", data); | ||
| 108 | class.write_packet(data).await?; | ||
| 109 | } | ||
| 110 | } | ||
diff --git a/examples/stm32f4/src/bin/wdt.rs b/examples/stm32f4/src/bin/wdt.rs index b2c587fa1..e5d122af7 100644 --- a/examples/stm32f4/src/bin/wdt.rs +++ b/examples/stm32f4/src/bin/wdt.rs | |||
| @@ -17,9 +17,7 @@ async fn main(_spawner: Spawner) { | |||
| 17 | let mut led = Output::new(p.PB7, Level::High, Speed::Low); | 17 | let mut led = Output::new(p.PB7, Level::High, Speed::Low); |
| 18 | 18 | ||
| 19 | let mut wdt = IndependentWatchdog::new(p.IWDG, 1_000_000); | 19 | let mut wdt = IndependentWatchdog::new(p.IWDG, 1_000_000); |
| 20 | unsafe { | 20 | wdt.unleash(); |
| 21 | wdt.unleash(); | ||
| 22 | } | ||
| 23 | 21 | ||
| 24 | let mut i = 0; | 22 | let mut i = 0; |
| 25 | 23 | ||
| @@ -36,9 +34,7 @@ async fn main(_spawner: Spawner) { | |||
| 36 | // MCU should restart in 1 second after the last pet. | 34 | // MCU should restart in 1 second after the last pet. |
| 37 | if i < 5 { | 35 | if i < 5 { |
| 38 | info!("Petting watchdog"); | 36 | info!("Petting watchdog"); |
| 39 | unsafe { | 37 | wdt.pet(); |
| 40 | wdt.pet(); | ||
| 41 | } | ||
| 42 | } | 38 | } |
| 43 | 39 | ||
| 44 | i += 1; | 40 | i += 1; |
diff --git a/examples/stm32f7/.cargo/config.toml b/examples/stm32f7/.cargo/config.toml index b07ad158c..9088eea6e 100644 --- a/examples/stm32f7/.cargo/config.toml +++ b/examples/stm32f7/.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 STM32F429ZITx with your chip as listed in `probe-run --list-chips` | 2 | # replace STM32F429ZITx with your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32F767ZITx" | 3 | runner = "probe-rs run --chip STM32F767ZITx" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv7em-none-eabihf" | 6 | target = "thumbv7em-none-eabihf" |
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index a446fe3fb..84d7b79c5 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml | |||
| @@ -2,19 +2,21 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-stm32f7-examples" | 3 | name = "embassy-stm32f7-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } |
| 11 | embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } | 12 | embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } |
| 12 | embedded-io = { version = "0.3.0", features = ["async"] } | 13 | embedded-io = { version = "0.4.0", features = ["async"] } |
| 14 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 13 | 15 | ||
| 14 | defmt = "0.3" | 16 | defmt = "0.3" |
| 15 | defmt-rtt = "0.3" | 17 | defmt-rtt = "0.4" |
| 16 | 18 | ||
| 17 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 19 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 18 | cortex-m-rt = "0.7.0" | 20 | cortex-m-rt = "0.7.0" |
| 19 | embedded-hal = "0.2.6" | 21 | embedded-hal = "0.2.6" |
| 20 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 22 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| @@ -24,4 +26,4 @@ nb = "1.0.0" | |||
| 24 | rand_core = "0.6.3" | 26 | rand_core = "0.6.3" |
| 25 | critical-section = "1.1" | 27 | critical-section = "1.1" |
| 26 | embedded-storage = "0.3.0" | 28 | embedded-storage = "0.3.0" |
| 27 | static_cell = "1.0" | 29 | static_cell = { version = "1.1", features = ["nightly"]} |
diff --git a/examples/stm32f7/build.rs b/examples/stm32f7/build.rs index c4e15f19c..2b5d412a9 100644 --- a/examples/stm32f7/build.rs +++ b/examples/stm32f7/build.rs | |||
| @@ -1,9 +1,8 @@ | |||
| 1 | //! adapted from https://github.com/stm32-rs/stm32f7xx-hal/blob/master/build.rs | 1 | //! adapted from https://github.com/stm32-rs/stm32f7xx-hal/blob/master/build.rs |
| 2 | use std::env; | ||
| 3 | use std::fs::File; | 2 | use std::fs::File; |
| 4 | use std::io::prelude::*; | 3 | use std::io::prelude::*; |
| 5 | use std::io::{self}; | ||
| 6 | use std::path::PathBuf; | 4 | use std::path::PathBuf; |
| 5 | use std::{env, io}; | ||
| 7 | 6 | ||
| 8 | #[derive(Debug)] | 7 | #[derive(Debug)] |
| 9 | enum Error { | 8 | enum Error { |
diff --git a/examples/stm32f7/src/bin/adc.rs b/examples/stm32f7/src/bin/adc.rs index 80fad8c41..70b3b2a75 100644 --- a/examples/stm32f7/src/bin/adc.rs +++ b/examples/stm32f7/src/bin/adc.rs | |||
| @@ -16,9 +16,19 @@ async fn main(_spawner: Spawner) { | |||
| 16 | let mut adc = Adc::new(p.ADC1, &mut Delay); | 16 | let mut adc = Adc::new(p.ADC1, &mut Delay); |
| 17 | let mut pin = p.PA3; | 17 | let mut pin = p.PA3; |
| 18 | 18 | ||
| 19 | let mut vrefint = adc.enable_vrefint(); | ||
| 20 | let vrefint_sample = adc.read_internal(&mut vrefint); | ||
| 21 | let convert_to_millivolts = |sample| { | ||
| 22 | // From http://www.st.com/resource/en/datasheet/DM00273119.pdf | ||
| 23 | // 6.3.27 Reference voltage | ||
| 24 | const VREFINT_MV: u32 = 1210; // mV | ||
| 25 | |||
| 26 | (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 | ||
| 27 | }; | ||
| 28 | |||
| 19 | loop { | 29 | loop { |
| 20 | let v = adc.read(&mut pin); | 30 | let v = adc.read(&mut pin); |
| 21 | info!("--> {} - {} mV", v, adc.to_millivolts(v)); | 31 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); |
| 22 | Timer::after(Duration::from_millis(100)).await; | 32 | Timer::after(Duration::from_millis(100)).await; |
| 23 | } | 33 | } |
| 24 | } | 34 | } |
diff --git a/examples/stm32f7/src/bin/can.rs b/examples/stm32f7/src/bin/can.rs new file mode 100644 index 000000000..1b5b377ea --- /dev/null +++ b/examples/stm32f7/src/bin/can.rs | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::bind_interrupts; | ||
| 8 | use embassy_stm32::can::bxcan::filter::Mask32; | ||
| 9 | use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; | ||
| 10 | use embassy_stm32::can::{ | ||
| 11 | Can, CanTx, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler, | ||
| 12 | }; | ||
| 13 | use embassy_stm32::gpio::{Input, Pull}; | ||
| 14 | use embassy_stm32::peripherals::CAN3; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | bind_interrupts!(struct Irqs { | ||
| 18 | CAN3_RX0 => Rx0InterruptHandler<CAN3>; | ||
| 19 | CAN3_RX1 => Rx1InterruptHandler<CAN3>; | ||
| 20 | CAN3_SCE => SceInterruptHandler<CAN3>; | ||
| 21 | CAN3_TX => TxInterruptHandler<CAN3>; | ||
| 22 | }); | ||
| 23 | |||
| 24 | #[embassy_executor::task] | ||
| 25 | pub async fn send_can_message(tx: &'static mut CanTx<'static, 'static, CAN3>) { | ||
| 26 | loop { | ||
| 27 | let frame = Frame::new_data(unwrap!(StandardId::new(0 as _)), [0]); | ||
| 28 | tx.write(&frame).await; | ||
| 29 | embassy_time::Timer::after(embassy_time::Duration::from_secs(1)).await; | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | #[embassy_executor::main] | ||
| 34 | async fn main(spawner: Spawner) { | ||
| 35 | info!("Hello World!"); | ||
| 36 | |||
| 37 | let mut p = embassy_stm32::init(Default::default()); | ||
| 38 | |||
| 39 | // The next two lines are a workaround for testing without transceiver. | ||
| 40 | // To synchronise to the bus the RX input needs to see a high level. | ||
| 41 | // Use `mem::forget()` to release the borrow on the pin but keep the | ||
| 42 | // pull-up resistor enabled. | ||
| 43 | let rx_pin = Input::new(&mut p.PA15, Pull::Up); | ||
| 44 | core::mem::forget(rx_pin); | ||
| 45 | |||
| 46 | let can: &'static mut Can<'static, CAN3> = static_cell::make_static!(Can::new(p.CAN3, p.PA8, p.PA15, Irqs)); | ||
| 47 | can.as_mut() | ||
| 48 | .modify_filters() | ||
| 49 | .enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); | ||
| 50 | |||
| 51 | can.as_mut() | ||
| 52 | .modify_config() | ||
| 53 | .set_bit_timing(0x001c0001) // http://www.bittiming.can-wiki.info/ | ||
| 54 | .set_loopback(true) | ||
| 55 | .enable(); | ||
| 56 | |||
| 57 | let (tx, mut rx) = can.split(); | ||
| 58 | |||
| 59 | let tx: &'static mut CanTx<'static, 'static, CAN3> = static_cell::make_static!(tx); | ||
| 60 | spawner.spawn(send_can_message(tx)).unwrap(); | ||
| 61 | |||
| 62 | loop { | ||
| 63 | let frame = rx.read().await.unwrap(); | ||
| 64 | println!("Received: {:?}", frame); | ||
| 65 | } | ||
| 66 | } | ||
diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 5202edf62..c6b2ba45c 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs | |||
| @@ -7,26 +7,21 @@ use embassy_executor::Spawner; | |||
| 7 | use embassy_net::tcp::TcpSocket; | 7 | use embassy_net::tcp::TcpSocket; |
| 8 | use embassy_net::{Ipv4Address, Stack, StackResources}; | 8 | use embassy_net::{Ipv4Address, Stack, StackResources}; |
| 9 | use embassy_stm32::eth::generic_smi::GenericSMI; | 9 | use embassy_stm32::eth::generic_smi::GenericSMI; |
| 10 | use embassy_stm32::eth::{Ethernet, State}; | 10 | use embassy_stm32::eth::{Ethernet, PacketQueue}; |
| 11 | use embassy_stm32::peripherals::ETH; | 11 | use embassy_stm32::peripherals::ETH; |
| 12 | use embassy_stm32::rng::Rng; | 12 | use embassy_stm32::rng::Rng; |
| 13 | use embassy_stm32::time::mhz; | 13 | use embassy_stm32::time::mhz; |
| 14 | use embassy_stm32::{interrupt, Config}; | 14 | use embassy_stm32::{bind_interrupts, eth, Config}; |
| 15 | use embassy_time::{Duration, Timer}; | 15 | use embassy_time::{Duration, Timer}; |
| 16 | use embedded_io::asynch::Write; | 16 | use embedded_io::asynch::Write; |
| 17 | use rand_core::RngCore; | 17 | use rand_core::RngCore; |
| 18 | use static_cell::StaticCell; | 18 | use static_cell::make_static; |
| 19 | use {defmt_rtt as _, panic_probe as _}; | 19 | use {defmt_rtt as _, panic_probe as _}; |
| 20 | bind_interrupts!(struct Irqs { | ||
| 21 | ETH => eth::InterruptHandler; | ||
| 22 | }); | ||
| 20 | 23 | ||
| 21 | macro_rules! singleton { | 24 | type Device = Ethernet<'static, ETH, GenericSMI>; |
| 22 | ($val:expr) => {{ | ||
| 23 | type T = impl Sized; | ||
| 24 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | ||
| 25 | STATIC_CELL.init_with(move || $val) | ||
| 26 | }}; | ||
| 27 | } | ||
| 28 | |||
| 29 | type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; | ||
| 30 | 25 | ||
| 31 | #[embassy_executor::task] | 26 | #[embassy_executor::task] |
| 32 | async fn net_task(stack: &'static Stack<Device>) -> ! { | 27 | async fn net_task(stack: &'static Stack<Device>) -> ! { |
| @@ -47,41 +42,38 @@ async fn main(spawner: Spawner) -> ! { | |||
| 47 | rng.fill_bytes(&mut seed); | 42 | rng.fill_bytes(&mut seed); |
| 48 | let seed = u64::from_le_bytes(seed); | 43 | let seed = u64::from_le_bytes(seed); |
| 49 | 44 | ||
| 50 | let eth_int = interrupt::take!(ETH); | ||
| 51 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; | 45 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; |
| 52 | 46 | ||
| 53 | let device = unsafe { | 47 | let device = Ethernet::new( |
| 54 | Ethernet::new( | 48 | make_static!(PacketQueue::<16, 16>::new()), |
| 55 | singleton!(State::new()), | 49 | p.ETH, |
| 56 | p.ETH, | 50 | Irqs, |
| 57 | eth_int, | 51 | p.PA1, |
| 58 | p.PA1, | 52 | p.PA2, |
| 59 | p.PA2, | 53 | p.PC1, |
| 60 | p.PC1, | 54 | p.PA7, |
| 61 | p.PA7, | 55 | p.PC4, |
| 62 | p.PC4, | 56 | p.PC5, |
| 63 | p.PC5, | 57 | p.PG13, |
| 64 | p.PG13, | 58 | p.PB13, |
| 65 | p.PB13, | 59 | p.PG11, |
| 66 | p.PG11, | 60 | GenericSMI::new(), |
| 67 | GenericSMI, | 61 | mac_addr, |
| 68 | mac_addr, | 62 | 0, |
| 69 | 0, | 63 | ); |
| 70 | ) | 64 | |
| 71 | }; | 65 | let config = embassy_net::Config::dhcpv4(Default::default()); |
| 72 | 66 | //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { | |
| 73 | let config = embassy_net::ConfigStrategy::Dhcp; | ||
| 74 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { | ||
| 75 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | 67 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), |
| 76 | // dns_servers: Vec::new(), | 68 | // dns_servers: Vec::new(), |
| 77 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | 69 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), |
| 78 | //}); | 70 | //}); |
| 79 | 71 | ||
| 80 | // Init network stack | 72 | // Init network stack |
| 81 | let stack = &*singleton!(Stack::new( | 73 | let stack = &*make_static!(Stack::new( |
| 82 | device, | 74 | device, |
| 83 | config, | 75 | config, |
| 84 | singleton!(StackResources::<1, 2, 8>::new()), | 76 | make_static!(StackResources::<2>::new()), |
| 85 | seed | 77 | seed |
| 86 | )); | 78 | )); |
| 87 | 79 | ||
| @@ -91,13 +83,13 @@ async fn main(spawner: Spawner) -> ! { | |||
| 91 | info!("Network task initialized"); | 83 | info!("Network task initialized"); |
| 92 | 84 | ||
| 93 | // Then we can use it! | 85 | // Then we can use it! |
| 94 | let mut rx_buffer = [0; 1024]; | 86 | let mut rx_buffer = [0; 4096]; |
| 95 | let mut tx_buffer = [0; 1024]; | 87 | let mut tx_buffer = [0; 4096]; |
| 96 | 88 | ||
| 97 | loop { | 89 | loop { |
| 98 | let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); | 90 | let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); |
| 99 | 91 | ||
| 100 | socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); | 92 | socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); |
| 101 | 93 | ||
| 102 | let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); | 94 | let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); |
| 103 | info!("connecting..."); | 95 | info!("connecting..."); |
| @@ -107,11 +99,12 @@ async fn main(spawner: Spawner) -> ! { | |||
| 107 | continue; | 99 | continue; |
| 108 | } | 100 | } |
| 109 | info!("connected!"); | 101 | info!("connected!"); |
| 102 | let buf = [0; 1024]; | ||
| 110 | loop { | 103 | loop { |
| 111 | let r = socket.write_all(b"Hello\n").await; | 104 | let r = socket.write_all(&buf).await; |
| 112 | if let Err(e) = r { | 105 | if let Err(e) = r { |
| 113 | info!("write error: {:?}", e); | 106 | info!("write error: {:?}", e); |
| 114 | return; | 107 | continue; |
| 115 | } | 108 | } |
| 116 | Timer::after(Duration::from_secs(1)).await; | 109 | Timer::after(Duration::from_secs(1)).await; |
| 117 | } | 110 | } |
diff --git a/examples/stm32f7/src/bin/flash.rs b/examples/stm32f7/src/bin/flash.rs index c10781d0c..35d3059be 100644 --- a/examples/stm32f7/src/bin/flash.rs +++ b/examples/stm32f7/src/bin/flash.rs | |||
| @@ -6,7 +6,6 @@ use defmt::{info, unwrap}; | |||
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::flash::Flash; | 7 | use embassy_stm32::flash::Flash; |
| 8 | use embassy_time::{Duration, Timer}; | 8 | use embassy_time::{Duration, Timer}; |
| 9 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 10 | ||
| 12 | #[embassy_executor::main] | 11 | #[embassy_executor::main] |
| @@ -14,28 +13,28 @@ async fn main(_spawner: Spawner) { | |||
| 14 | let p = embassy_stm32::init(Default::default()); | 13 | let p = embassy_stm32::init(Default::default()); |
| 15 | info!("Hello Flash!"); | 14 | info!("Hello Flash!"); |
| 16 | 15 | ||
| 17 | const ADDR: u32 = 0x8_0000; | 16 | const ADDR: u32 = 0x8_0000; // This is the offset into the third region, the absolute address is 4x32K + 128K + 0x8_0000. |
| 18 | 17 | ||
| 19 | // wait a bit before accessing the flash | 18 | // wait a bit before accessing the flash |
| 20 | Timer::after(Duration::from_millis(300)).await; | 19 | Timer::after(Duration::from_millis(300)).await; |
| 21 | 20 | ||
| 22 | let mut f = Flash::unlock(p.FLASH); | 21 | let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region3; |
| 23 | 22 | ||
| 24 | info!("Reading..."); | 23 | info!("Reading..."); |
| 25 | let mut buf = [0u8; 32]; | 24 | let mut buf = [0u8; 32]; |
| 26 | unwrap!(f.read(ADDR, &mut buf)); | 25 | unwrap!(f.blocking_read(ADDR, &mut buf)); |
| 27 | info!("Read: {=[u8]:x}", buf); | 26 | info!("Read: {=[u8]:x}", buf); |
| 28 | 27 | ||
| 29 | info!("Erasing..."); | 28 | info!("Erasing..."); |
| 30 | unwrap!(f.erase(ADDR, ADDR + 256 * 1024)); | 29 | unwrap!(f.blocking_erase(ADDR, ADDR + 256 * 1024)); |
| 31 | 30 | ||
| 32 | info!("Reading..."); | 31 | info!("Reading..."); |
| 33 | let mut buf = [0u8; 32]; | 32 | let mut buf = [0u8; 32]; |
| 34 | unwrap!(f.read(ADDR, &mut buf)); | 33 | unwrap!(f.blocking_read(ADDR, &mut buf)); |
| 35 | info!("Read after erase: {=[u8]:x}", buf); | 34 | info!("Read after erase: {=[u8]:x}", buf); |
| 36 | 35 | ||
| 37 | info!("Writing..."); | 36 | info!("Writing..."); |
| 38 | unwrap!(f.write( | 37 | unwrap!(f.blocking_write( |
| 39 | ADDR, | 38 | ADDR, |
| 40 | &[ | 39 | &[ |
| 41 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, | 40 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, |
| @@ -45,7 +44,7 @@ async fn main(_spawner: Spawner) { | |||
| 45 | 44 | ||
| 46 | info!("Reading..."); | 45 | info!("Reading..."); |
| 47 | let mut buf = [0u8; 32]; | 46 | let mut buf = [0u8; 32]; |
| 48 | unwrap!(f.read(ADDR, &mut buf)); | 47 | unwrap!(f.blocking_read(ADDR, &mut buf)); |
| 49 | info!("Read: {=[u8]:x}", buf); | 48 | info!("Read: {=[u8]:x}", buf); |
| 50 | assert_eq!( | 49 | assert_eq!( |
| 51 | &buf[..], | 50 | &buf[..], |
diff --git a/examples/stm32f7/src/bin/sdmmc.rs b/examples/stm32f7/src/bin/sdmmc.rs index 3bf427eca..9d43892a0 100644 --- a/examples/stm32f7/src/bin/sdmmc.rs +++ b/examples/stm32f7/src/bin/sdmmc.rs | |||
| @@ -6,22 +6,25 @@ use defmt::*; | |||
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::sdmmc::Sdmmc; | 7 | use embassy_stm32::sdmmc::Sdmmc; |
| 8 | use embassy_stm32::time::mhz; | 8 | use embassy_stm32::time::mhz; |
| 9 | use embassy_stm32::{interrupt, Config}; | 9 | use embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config}; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 11 | ||
| 12 | bind_interrupts!(struct Irqs { | ||
| 13 | SDMMC1 => sdmmc::InterruptHandler<peripherals::SDMMC1>; | ||
| 14 | }); | ||
| 15 | |||
| 12 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 13 | async fn main(_spawner: Spawner) -> ! { | 17 | async fn main(_spawner: Spawner) { |
| 14 | let mut config = Config::default(); | 18 | let mut config = Config::default(); |
| 15 | config.rcc.sys_ck = Some(mhz(200)); | 19 | config.rcc.sys_ck = Some(mhz(200)); |
| 20 | config.rcc.pll48 = true; | ||
| 16 | let p = embassy_stm32::init(config); | 21 | let p = embassy_stm32::init(config); |
| 17 | 22 | ||
| 18 | info!("Hello World!"); | 23 | info!("Hello World!"); |
| 19 | 24 | ||
| 20 | let irq = interrupt::take!(SDMMC1); | ||
| 21 | |||
| 22 | let mut sdmmc = Sdmmc::new_4bit( | 25 | let mut sdmmc = Sdmmc::new_4bit( |
| 23 | p.SDMMC1, | 26 | p.SDMMC1, |
| 24 | irq, | 27 | Irqs, |
| 25 | p.DMA2_CH3, | 28 | p.DMA2_CH3, |
| 26 | p.PC12, | 29 | p.PC12, |
| 27 | p.PD2, | 30 | p.PD2, |
| @@ -40,6 +43,4 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 40 | let card = unwrap!(sdmmc.card()); | 43 | let card = unwrap!(sdmmc.card()); |
| 41 | 44 | ||
| 42 | info!("Card: {:#?}", Debug2Format(card)); | 45 | info!("Card: {:#?}", Debug2Format(card)); |
| 43 | |||
| 44 | loop {} | ||
| 45 | } | 46 | } |
diff --git a/examples/stm32f7/src/bin/usart_dma.rs b/examples/stm32f7/src/bin/usart_dma.rs index 07270479c..4700287a7 100644 --- a/examples/stm32f7/src/bin/usart_dma.rs +++ b/examples/stm32f7/src/bin/usart_dma.rs | |||
| @@ -8,14 +8,19 @@ use defmt::*; | |||
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_stm32::dma::NoDma; | 9 | use embassy_stm32::dma::NoDma; |
| 10 | use embassy_stm32::usart::{Config, Uart}; | 10 | use embassy_stm32::usart::{Config, Uart}; |
| 11 | use embassy_stm32::{bind_interrupts, peripherals, usart}; | ||
| 11 | use heapless::String; | 12 | use heapless::String; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 14 | ||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | UART7 => usart::InterruptHandler<peripherals::UART7>; | ||
| 17 | }); | ||
| 18 | |||
| 14 | #[embassy_executor::main] | 19 | #[embassy_executor::main] |
| 15 | async fn main(_spawner: Spawner) { | 20 | async fn main(_spawner: Spawner) { |
| 16 | let p = embassy_stm32::init(Default::default()); | 21 | let p = embassy_stm32::init(Default::default()); |
| 17 | let config = Config::default(); | 22 | let config = Config::default(); |
| 18 | let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, p.DMA1_CH1, NoDma, config); | 23 | let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, Irqs, p.DMA1_CH1, NoDma, config); |
| 19 | 24 | ||
| 20 | for n in 0u32.. { | 25 | for n in 0u32.. { |
| 21 | let mut s: String<128> = String::new(); | 26 | let mut s: String<128> = String::new(); |
diff --git a/examples/stm32f7/src/bin/usb_serial.rs b/examples/stm32f7/src/bin/usb_serial.rs new file mode 100644 index 000000000..a2c76178b --- /dev/null +++ b/examples/stm32f7/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{panic, *}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::time::mhz; | ||
| 8 | use embassy_stm32::usb_otg::{Driver, Instance}; | ||
| 9 | use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; | ||
| 10 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 11 | use embassy_usb::driver::EndpointError; | ||
| 12 | use embassy_usb::Builder; | ||
| 13 | use futures::future::join; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | bind_interrupts!(struct Irqs { | ||
| 17 | OTG_FS => usb_otg::InterruptHandler<peripherals::USB_OTG_FS>; | ||
| 18 | }); | ||
| 19 | |||
| 20 | #[embassy_executor::main] | ||
| 21 | async fn main(_spawner: Spawner) { | ||
| 22 | info!("Hello World!"); | ||
| 23 | |||
| 24 | let mut config = Config::default(); | ||
| 25 | config.rcc.hse = Some(mhz(8)); | ||
| 26 | config.rcc.pll48 = true; | ||
| 27 | config.rcc.sys_ck = Some(mhz(200)); | ||
| 28 | |||
| 29 | let p = embassy_stm32::init(config); | ||
| 30 | |||
| 31 | // Create the driver, from the HAL. | ||
| 32 | let mut ep_out_buffer = [0u8; 256]; | ||
| 33 | let mut config = embassy_stm32::usb_otg::Config::default(); | ||
| 34 | config.vbus_detection = true; | ||
| 35 | let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); | ||
| 36 | |||
| 37 | // Create embassy-usb Config | ||
| 38 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 39 | config.manufacturer = Some("Embassy"); | ||
| 40 | config.product = Some("USB-serial example"); | ||
| 41 | config.serial_number = Some("12345678"); | ||
| 42 | |||
| 43 | // Required for windows compatibility. | ||
| 44 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 45 | config.device_class = 0xEF; | ||
| 46 | config.device_sub_class = 0x02; | ||
| 47 | config.device_protocol = 0x01; | ||
| 48 | config.composite_with_iads = true; | ||
| 49 | |||
| 50 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 51 | // It needs some buffers for building the descriptors. | ||
| 52 | let mut device_descriptor = [0; 256]; | ||
| 53 | let mut config_descriptor = [0; 256]; | ||
| 54 | let mut bos_descriptor = [0; 256]; | ||
| 55 | let mut control_buf = [0; 64]; | ||
| 56 | |||
| 57 | let mut state = State::new(); | ||
| 58 | |||
| 59 | let mut builder = Builder::new( | ||
| 60 | driver, | ||
| 61 | config, | ||
| 62 | &mut device_descriptor, | ||
| 63 | &mut config_descriptor, | ||
| 64 | &mut bos_descriptor, | ||
| 65 | &mut control_buf, | ||
| 66 | ); | ||
| 67 | |||
| 68 | // Create classes on the builder. | ||
| 69 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 70 | |||
| 71 | // Build the builder. | ||
| 72 | let mut usb = builder.build(); | ||
| 73 | |||
| 74 | // Run the USB device. | ||
| 75 | let usb_fut = usb.run(); | ||
| 76 | |||
| 77 | // Do stuff with the class! | ||
| 78 | let echo_fut = async { | ||
| 79 | loop { | ||
| 80 | class.wait_connection().await; | ||
| 81 | info!("Connected"); | ||
| 82 | let _ = echo(&mut class).await; | ||
| 83 | info!("Disconnected"); | ||
| 84 | } | ||
| 85 | }; | ||
| 86 | |||
| 87 | // Run everything concurrently. | ||
| 88 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 89 | join(usb_fut, echo_fut).await; | ||
| 90 | } | ||
| 91 | |||
| 92 | struct Disconnected {} | ||
| 93 | |||
| 94 | impl From<EndpointError> for Disconnected { | ||
| 95 | fn from(val: EndpointError) -> Self { | ||
| 96 | match val { | ||
| 97 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 98 | EndpointError::Disabled => Disconnected {}, | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 104 | let mut buf = [0; 64]; | ||
| 105 | loop { | ||
| 106 | let n = class.read_packet(&mut buf).await?; | ||
| 107 | let data = &buf[..n]; | ||
| 108 | info!("data: {:x}", data); | ||
| 109 | class.write_packet(data).await?; | ||
| 110 | } | ||
| 111 | } | ||
diff --git a/examples/stm32g0/.cargo/config.toml b/examples/stm32g0/.cargo/config.toml index 4f776d68f..35cca5412 100644 --- a/examples/stm32g0/.cargo/config.toml +++ b/examples/stm32g0/.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 STM32G071C8Rx with your chip as listed in `probe-run --list-chips` | 2 | # replace STM32G071C8Rx with your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32G071RBTx" | 3 | runner = "probe-rs run --chip STM32G071RBTx" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv6m-none-eabi" | 6 | target = "thumbv6m-none-eabi" |
diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index 30f2b86f8..c88282d91 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml | |||
| @@ -2,17 +2,18 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-stm32g0-examples" | 3 | name = "embassy-stm32g0-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] } |
| 11 | 12 | ||
| 12 | defmt = "0.3" | 13 | defmt = "0.3" |
| 13 | defmt-rtt = "0.3" | 14 | defmt-rtt = "0.4" |
| 14 | 15 | ||
| 15 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 16 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 16 | cortex-m-rt = "0.7.0" | 17 | cortex-m-rt = "0.7.0" |
| 17 | embedded-hal = "0.2.6" | 18 | embedded-hal = "0.2.6" |
| 18 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 19 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
diff --git a/examples/stm32g0/src/bin/spi_neopixel.rs b/examples/stm32g0/src/bin/spi_neopixel.rs new file mode 100644 index 000000000..81fdd15cb --- /dev/null +++ b/examples/stm32g0/src/bin/spi_neopixel.rs | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::dma::word::U5; | ||
| 8 | use embassy_stm32::dma::NoDma; | ||
| 9 | use embassy_stm32::spi::{Config, Spi}; | ||
| 10 | use embassy_stm32::time::Hertz; | ||
| 11 | use embassy_time::{Duration, Timer}; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | const NR_PIXELS: usize = 15; | ||
| 15 | const BITS_PER_PIXEL: usize = 24; // 24 for rgb, 32 for rgbw | ||
| 16 | const TOTAL_BITS: usize = NR_PIXELS * BITS_PER_PIXEL; | ||
| 17 | |||
| 18 | struct RGB { | ||
| 19 | r: u8, | ||
| 20 | g: u8, | ||
| 21 | b: u8, | ||
| 22 | } | ||
| 23 | impl Default for RGB { | ||
| 24 | fn default() -> RGB { | ||
| 25 | RGB { r: 0, g: 0, b: 0 } | ||
| 26 | } | ||
| 27 | } | ||
| 28 | pub struct Ws2812 { | ||
| 29 | // Note that the U5 type controls the selection of 5 bits to output | ||
| 30 | bitbuffer: [U5; TOTAL_BITS], | ||
| 31 | } | ||
| 32 | |||
| 33 | impl Ws2812 { | ||
| 34 | pub fn new() -> Ws2812 { | ||
| 35 | Ws2812 { | ||
| 36 | bitbuffer: [U5(0); TOTAL_BITS], | ||
| 37 | } | ||
| 38 | } | ||
| 39 | fn len(&self) -> usize { | ||
| 40 | return NR_PIXELS; | ||
| 41 | } | ||
| 42 | fn set(&mut self, idx: usize, rgb: RGB) { | ||
| 43 | self.render_color(idx, 0, rgb.g); | ||
| 44 | self.render_color(idx, 8, rgb.r); | ||
| 45 | self.render_color(idx, 16, rgb.b); | ||
| 46 | } | ||
| 47 | // transform one color byte into an array of 8 byte. Each byte in the array does represent 1 neopixel bit pattern | ||
| 48 | fn render_color(&mut self, pixel_idx: usize, offset: usize, color: u8) { | ||
| 49 | let mut bits = color as usize; | ||
| 50 | let mut idx = pixel_idx * BITS_PER_PIXEL + offset; | ||
| 51 | |||
| 52 | // render one bit in one spi byte. High time first, then the low time | ||
| 53 | // clock should be 4 Mhz, 5 bits, each bit is 0.25 us. | ||
| 54 | // a one bit is send as a pulse of 0.75 high -- 0.50 low | ||
| 55 | // a zero bit is send as a pulse of 0.50 high -- 0.75 low | ||
| 56 | // clock frequency for the neopixel is exact 800 khz | ||
| 57 | // note that the mosi output should have a resistor to ground of 10k, | ||
| 58 | // to assure that between the bursts the line is low | ||
| 59 | for _i in 0..8 { | ||
| 60 | if idx >= TOTAL_BITS { | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | let pattern = match bits & 0x80 { | ||
| 64 | 0x80 => 0b0000_1110, | ||
| 65 | _ => 0b000_1100, | ||
| 66 | }; | ||
| 67 | bits = bits << 1; | ||
| 68 | self.bitbuffer[idx] = U5(pattern); | ||
| 69 | idx += 1; | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | #[embassy_executor::main] | ||
| 75 | async fn main(_spawner: Spawner) { | ||
| 76 | let p = embassy_stm32::init(Default::default()); | ||
| 77 | info!("Start test using spi as neopixel driver"); | ||
| 78 | |||
| 79 | let mut spi = Spi::new_txonly_nosck(p.SPI1, p.PB5, p.DMA1_CH3, NoDma, Hertz(4_000_000), Config::default()); | ||
| 80 | |||
| 81 | let mut neopixels = Ws2812::new(); | ||
| 82 | |||
| 83 | loop { | ||
| 84 | let mut cnt: usize = 0; | ||
| 85 | for _i in 0..10 { | ||
| 86 | for idx in 0..neopixels.len() { | ||
| 87 | let color = match (cnt + idx) % 3 { | ||
| 88 | 0 => RGB { r: 0x21, g: 0, b: 0 }, | ||
| 89 | 1 => RGB { r: 0, g: 0x31, b: 0 }, | ||
| 90 | _ => RGB { r: 0, g: 0, b: 0x41 }, | ||
| 91 | }; | ||
| 92 | neopixels.set(idx, color); | ||
| 93 | } | ||
| 94 | cnt += 1; | ||
| 95 | // start sending the neopixel bit patters over spi to the neopixel string | ||
| 96 | spi.write(&neopixels.bitbuffer).await.ok(); | ||
| 97 | Timer::after(Duration::from_millis(500)).await; | ||
| 98 | } | ||
| 99 | Timer::after(Duration::from_millis(1000)).await; | ||
| 100 | } | ||
| 101 | } | ||
diff --git a/examples/stm32g4/.cargo/config.toml b/examples/stm32g4/.cargo/config.toml index 99feae119..d28ad069e 100644 --- a/examples/stm32g4/.cargo/config.toml +++ b/examples/stm32g4/.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 STM32G071C8Rx with your chip as listed in `probe-run --list-chips` | 2 | # replace STM32G071C8Rx with your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32G484VETx" | 3 | runner = "probe-rs run --chip STM32G484VETx" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv7em-none-eabi" | 6 | target = "thumbv7em-none-eabi" |
diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index f81df0b70..18bd03c39 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml | |||
| @@ -2,16 +2,18 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-stm32g4-examples" | 3 | name = "embassy-stm32g4-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } |
| 11 | embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } | 12 | embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } |
| 13 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 12 | 14 | ||
| 13 | defmt = "0.3" | 15 | defmt = "0.3" |
| 14 | defmt-rtt = "0.3" | 16 | defmt-rtt = "0.4" |
| 15 | 17 | ||
| 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 18 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 17 | cortex-m-rt = "0.7.0" | 19 | cortex-m-rt = "0.7.0" |
diff --git a/examples/stm32g4/src/bin/pll.rs b/examples/stm32g4/src/bin/pll.rs new file mode 100644 index 000000000..ef7d4800c --- /dev/null +++ b/examples/stm32g4/src/bin/pll.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::rcc::{ClockSrc, Pll, PllM, PllN, PllR, PllSrc}; | ||
| 8 | use embassy_stm32::Config; | ||
| 9 | use embassy_time::{Duration, Timer}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) { | ||
| 14 | let mut config = Config::default(); | ||
| 15 | |||
| 16 | config.rcc.pll = Some(Pll { | ||
| 17 | source: PllSrc::HSI16, | ||
| 18 | prediv_m: PllM::Div4, | ||
| 19 | mul_n: PllN::Mul85, | ||
| 20 | div_p: None, | ||
| 21 | div_q: None, | ||
| 22 | // Main system clock at 170 MHz | ||
| 23 | div_r: Some(PllR::Div2), | ||
| 24 | }); | ||
| 25 | |||
| 26 | config.rcc.mux = ClockSrc::PLL; | ||
| 27 | |||
| 28 | let _p = embassy_stm32::init(config); | ||
| 29 | info!("Hello World!"); | ||
| 30 | |||
| 31 | loop { | ||
| 32 | Timer::after(Duration::from_millis(1000)).await; | ||
| 33 | info!("1s elapsed"); | ||
| 34 | } | ||
| 35 | } | ||
diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs index 017e89e41..8f7842ed7 100644 --- a/examples/stm32g4/src/bin/pwm.rs +++ b/examples/stm32g4/src/bin/pwm.rs | |||
| @@ -15,8 +15,8 @@ async fn main(_spawner: Spawner) { | |||
| 15 | let p = embassy_stm32::init(Default::default()); | 15 | let p = embassy_stm32::init(Default::default()); |
| 16 | info!("Hello World!"); | 16 | info!("Hello World!"); |
| 17 | 17 | ||
| 18 | let ch1 = PwmPin::new_ch1(p.PA5); | 18 | let ch1 = PwmPin::new_ch1(p.PC0); |
| 19 | let mut pwm = SimplePwm::new(p.TIM2, Some(ch1), None, None, None, khz(10)); | 19 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10)); |
| 20 | let max = pwm.get_max_duty(); | 20 | let max = pwm.get_max_duty(); |
| 21 | pwm.enable(Channel::Ch1); | 21 | pwm.enable(Channel::Ch1); |
| 22 | 22 | ||
diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs new file mode 100644 index 000000000..77cfa67d3 --- /dev/null +++ b/examples/stm32g4/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,120 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{panic, *}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::rcc::{Clock48MhzSrc, ClockSrc, CrsConfig, CrsSyncSource, Pll, PllM, PllN, PllQ, PllR, PllSrc}; | ||
| 8 | use embassy_stm32::time::Hertz; | ||
| 9 | use embassy_stm32::usb::{self, Driver, Instance}; | ||
| 10 | use embassy_stm32::{bind_interrupts, peripherals, Config}; | ||
| 11 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 12 | use embassy_usb::driver::EndpointError; | ||
| 13 | use embassy_usb::Builder; | ||
| 14 | use futures::future::join; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | bind_interrupts!(struct Irqs { | ||
| 18 | USB_LP => usb::InterruptHandler<peripherals::USB>; | ||
| 19 | }); | ||
| 20 | |||
| 21 | #[embassy_executor::main] | ||
| 22 | async fn main(_spawner: Spawner) { | ||
| 23 | let mut config = Config::default(); | ||
| 24 | |||
| 25 | // Change this to `false` to use the HSE clock source for the USB. This example assumes an 8MHz HSE. | ||
| 26 | const USE_HSI48: bool = true; | ||
| 27 | |||
| 28 | let pllq_div = if USE_HSI48 { None } else { Some(PllQ::Div6) }; | ||
| 29 | |||
| 30 | config.rcc.pll = Some(Pll { | ||
| 31 | source: PllSrc::HSE(Hertz(8_000_000)), | ||
| 32 | prediv_m: PllM::Div2, | ||
| 33 | mul_n: PllN::Mul72, | ||
| 34 | div_p: None, | ||
| 35 | div_q: pllq_div, | ||
| 36 | // Main system clock at 144 MHz | ||
| 37 | div_r: Some(PllR::Div2), | ||
| 38 | }); | ||
| 39 | |||
| 40 | config.rcc.mux = ClockSrc::PLL; | ||
| 41 | |||
| 42 | if USE_HSI48 { | ||
| 43 | // Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator. | ||
| 44 | config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::Hsi48(Some(CrsConfig { | ||
| 45 | sync_src: CrsSyncSource::Usb, | ||
| 46 | }))); | ||
| 47 | } else { | ||
| 48 | config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::PllQ); | ||
| 49 | } | ||
| 50 | |||
| 51 | let p = embassy_stm32::init(config); | ||
| 52 | |||
| 53 | info!("Hello World!"); | ||
| 54 | |||
| 55 | let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); | ||
| 56 | |||
| 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("123456"); | ||
| 61 | |||
| 62 | config.device_class = 0xEF; | ||
| 63 | config.device_sub_class = 0x02; | ||
| 64 | config.device_protocol = 0x01; | ||
| 65 | config.composite_with_iads = true; | ||
| 66 | |||
| 67 | let mut device_descriptor = [0; 256]; | ||
| 68 | let mut config_descriptor = [0; 256]; | ||
| 69 | let mut bos_descriptor = [0; 256]; | ||
| 70 | let mut control_buf = [0; 64]; | ||
| 71 | |||
| 72 | let mut state = State::new(); | ||
| 73 | |||
| 74 | let mut builder = Builder::new( | ||
| 75 | driver, | ||
| 76 | config, | ||
| 77 | &mut device_descriptor, | ||
| 78 | &mut config_descriptor, | ||
| 79 | &mut bos_descriptor, | ||
| 80 | &mut control_buf, | ||
| 81 | ); | ||
| 82 | |||
| 83 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 84 | |||
| 85 | let mut usb = builder.build(); | ||
| 86 | |||
| 87 | let usb_fut = usb.run(); | ||
| 88 | |||
| 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 | join(usb_fut, echo_fut).await; | ||
| 99 | } | ||
| 100 | |||
| 101 | struct Disconnected {} | ||
| 102 | |||
| 103 | impl From<EndpointError> for Disconnected { | ||
| 104 | fn from(val: EndpointError) -> Self { | ||
| 105 | match val { | ||
| 106 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 107 | EndpointError::Disabled => Disconnected {}, | ||
| 108 | } | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 113 | let mut buf = [0; 64]; | ||
| 114 | loop { | ||
| 115 | let n = class.read_packet(&mut buf).await?; | ||
| 116 | let data = &buf[..n]; | ||
| 117 | info!("data: {:x}", data); | ||
| 118 | class.write_packet(data).await?; | ||
| 119 | } | ||
| 120 | } | ||
diff --git a/examples/stm32h5/.cargo/config.toml b/examples/stm32h5/.cargo/config.toml new file mode 100644 index 000000000..478146142 --- /dev/null +++ b/examples/stm32h5/.cargo/config.toml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | [target.thumbv8m.main-none-eabihf] | ||
| 2 | runner = 'probe-rs run --chip STM32H563ZITx' | ||
| 3 | |||
| 4 | [build] | ||
| 5 | target = "thumbv8m.main-none-eabihf" | ||
| 6 | |||
| 7 | [env] | ||
| 8 | DEFMT_LOG = "trace" | ||
diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml new file mode 100644 index 000000000..227bc28b4 --- /dev/null +++ b/examples/stm32h5/Cargo.toml | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-stm32h5-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 9 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | ||
| 10 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } | ||
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h563zi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } | ||
| 12 | embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } | ||
| 13 | embedded-io = { version = "0.4.0", features = ["async"] } | ||
| 14 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 15 | |||
| 16 | defmt = "0.3" | ||
| 17 | defmt-rtt = "0.4" | ||
| 18 | |||
| 19 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 20 | cortex-m-rt = "0.7.0" | ||
| 21 | embedded-hal = "0.2.6" | ||
| 22 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } | ||
| 23 | embedded-hal-async = { version = "=0.2.0-alpha.2" } | ||
| 24 | embedded-nal-async = "0.4.0" | ||
| 25 | panic-probe = { version = "0.3", features = ["print-defmt"] } | ||
| 26 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | ||
| 27 | heapless = { version = "0.7.5", default-features = false } | ||
| 28 | rand_core = "0.6.3" | ||
| 29 | critical-section = "1.1" | ||
| 30 | micromath = "2.0.0" | ||
| 31 | stm32-fmc = "0.2.4" | ||
| 32 | embedded-storage = "0.3.0" | ||
| 33 | static_cell = { version = "1.1", features = ["nightly"]} | ||
| 34 | |||
| 35 | # cargo build/run | ||
| 36 | [profile.dev] | ||
| 37 | codegen-units = 1 | ||
| 38 | debug = 2 | ||
| 39 | debug-assertions = true # <- | ||
| 40 | incremental = false | ||
| 41 | opt-level = 3 # <- | ||
| 42 | overflow-checks = true # <- | ||
| 43 | |||
| 44 | # cargo test | ||
| 45 | [profile.test] | ||
| 46 | codegen-units = 1 | ||
| 47 | debug = 2 | ||
| 48 | debug-assertions = true # <- | ||
| 49 | incremental = false | ||
| 50 | opt-level = 3 # <- | ||
| 51 | overflow-checks = true # <- | ||
| 52 | |||
| 53 | # cargo build/run --release | ||
| 54 | [profile.release] | ||
| 55 | codegen-units = 1 | ||
| 56 | debug = 2 | ||
| 57 | debug-assertions = false # <- | ||
| 58 | incremental = false | ||
| 59 | lto = 'fat' | ||
| 60 | opt-level = 3 # <- | ||
| 61 | overflow-checks = false # <- | ||
| 62 | |||
| 63 | # cargo test --release | ||
| 64 | [profile.bench] | ||
| 65 | codegen-units = 1 | ||
| 66 | debug = 2 | ||
| 67 | debug-assertions = false # <- | ||
| 68 | incremental = false | ||
| 69 | lto = 'fat' | ||
| 70 | opt-level = 3 # <- | ||
| 71 | overflow-checks = false # <- | ||
diff --git a/examples/stm32h5/build.rs b/examples/stm32h5/build.rs new file mode 100644 index 000000000..8cd32d7ed --- /dev/null +++ b/examples/stm32h5/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/stm32h5/memory.x b/examples/stm32h5/memory.x new file mode 100644 index 000000000..456061509 --- /dev/null +++ b/examples/stm32h5/memory.x | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | FLASH : ORIGIN = 0x08000000, LENGTH = 0x200000 | ||
| 4 | RAM : ORIGIN = 0x20000000, LENGTH = 0x50000 | ||
| 5 | } | ||
diff --git a/examples/stm32h5/src/bin/blinky.rs b/examples/stm32h5/src/bin/blinky.rs new file mode 100644 index 000000000..f9bf90d2e --- /dev/null +++ b/examples/stm32h5/src/bin/blinky.rs | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 8 | use embassy_time::{Duration, Timer}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_stm32::init(Default::default()); | ||
| 14 | info!("Hello World!"); | ||
| 15 | |||
| 16 | let mut led = Output::new(p.PB0, Level::High, Speed::Low); | ||
| 17 | |||
| 18 | loop { | ||
| 19 | info!("high"); | ||
| 20 | led.set_high(); | ||
| 21 | Timer::after(Duration::from_millis(500)).await; | ||
| 22 | |||
| 23 | info!("low"); | ||
| 24 | led.set_low(); | ||
| 25 | Timer::after(Duration::from_millis(500)).await; | ||
| 26 | } | ||
| 27 | } | ||
diff --git a/examples/stm32h5/src/bin/button_exti.rs b/examples/stm32h5/src/bin/button_exti.rs new file mode 100644 index 000000000..dfe587d41 --- /dev/null +++ b/examples/stm32h5/src/bin/button_exti.rs | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::exti::ExtiInput; | ||
| 8 | use embassy_stm32::gpio::{Input, Pull}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_stm32::init(Default::default()); | ||
| 14 | info!("Hello World!"); | ||
| 15 | |||
| 16 | let button = Input::new(p.PC13, Pull::Down); | ||
| 17 | let mut button = ExtiInput::new(button, p.EXTI13); | ||
| 18 | |||
| 19 | info!("Press the USER button..."); | ||
| 20 | |||
| 21 | loop { | ||
| 22 | button.wait_for_rising_edge().await; | ||
| 23 | info!("Pressed!"); | ||
| 24 | button.wait_for_falling_edge().await; | ||
| 25 | info!("Released!"); | ||
| 26 | } | ||
| 27 | } | ||
diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs new file mode 100644 index 000000000..0bff85ed8 --- /dev/null +++ b/examples/stm32h5/src/bin/eth.rs | |||
| @@ -0,0 +1,131 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_net::tcp::TcpSocket; | ||
| 8 | use embassy_net::{Ipv4Address, Stack, StackResources}; | ||
| 9 | use embassy_stm32::eth::generic_smi::GenericSMI; | ||
| 10 | use embassy_stm32::eth::{Ethernet, PacketQueue}; | ||
| 11 | use embassy_stm32::peripherals::ETH; | ||
| 12 | use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllSource, Sysclk, VoltageScale}; | ||
| 13 | use embassy_stm32::rng::Rng; | ||
| 14 | use embassy_stm32::time::Hertz; | ||
| 15 | use embassy_stm32::{bind_interrupts, eth, Config}; | ||
| 16 | use embassy_time::{Duration, Timer}; | ||
| 17 | use embedded_io::asynch::Write; | ||
| 18 | use rand_core::RngCore; | ||
| 19 | use static_cell::make_static; | ||
| 20 | use {defmt_rtt as _, panic_probe as _}; | ||
| 21 | bind_interrupts!(struct Irqs { | ||
| 22 | ETH => eth::InterruptHandler; | ||
| 23 | }); | ||
| 24 | |||
| 25 | type Device = Ethernet<'static, ETH, GenericSMI>; | ||
| 26 | |||
| 27 | #[embassy_executor::task] | ||
| 28 | async fn net_task(stack: &'static Stack<Device>) -> ! { | ||
| 29 | stack.run().await | ||
| 30 | } | ||
| 31 | |||
| 32 | #[embassy_executor::main] | ||
| 33 | async fn main(spawner: Spawner) -> ! { | ||
| 34 | let mut config = Config::default(); | ||
| 35 | config.rcc.hsi = None; | ||
| 36 | config.rcc.hsi48 = true; // needed for rng | ||
| 37 | config.rcc.hse = Some(Hse { | ||
| 38 | freq: Hertz(8_000_000), | ||
| 39 | mode: HseMode::BypassDigital, | ||
| 40 | }); | ||
| 41 | config.rcc.pll1 = Some(Pll { | ||
| 42 | source: PllSource::Hse, | ||
| 43 | prediv: 2, | ||
| 44 | mul: 125, | ||
| 45 | divp: Some(2), | ||
| 46 | divq: Some(2), | ||
| 47 | divr: None, | ||
| 48 | }); | ||
| 49 | config.rcc.ahb_pre = AHBPrescaler::NotDivided; | ||
| 50 | config.rcc.apb1_pre = APBPrescaler::NotDivided; | ||
| 51 | config.rcc.apb2_pre = APBPrescaler::NotDivided; | ||
| 52 | config.rcc.apb3_pre = APBPrescaler::NotDivided; | ||
| 53 | config.rcc.sys = Sysclk::Pll1P; | ||
| 54 | config.rcc.voltage_scale = VoltageScale::Scale0; | ||
| 55 | let p = embassy_stm32::init(config); | ||
| 56 | info!("Hello World!"); | ||
| 57 | |||
| 58 | // Generate random seed. | ||
| 59 | let mut rng = Rng::new(p.RNG); | ||
| 60 | let mut seed = [0; 8]; | ||
| 61 | rng.fill_bytes(&mut seed); | ||
| 62 | let seed = u64::from_le_bytes(seed); | ||
| 63 | |||
| 64 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; | ||
| 65 | |||
| 66 | let device = Ethernet::new( | ||
| 67 | make_static!(PacketQueue::<4, 4>::new()), | ||
| 68 | p.ETH, | ||
| 69 | Irqs, | ||
| 70 | p.PA1, | ||
| 71 | p.PA2, | ||
| 72 | p.PC1, | ||
| 73 | p.PA7, | ||
| 74 | p.PC4, | ||
| 75 | p.PC5, | ||
| 76 | p.PG13, | ||
| 77 | p.PB15, | ||
| 78 | p.PG11, | ||
| 79 | GenericSMI::new(), | ||
| 80 | mac_addr, | ||
| 81 | 0, | ||
| 82 | ); | ||
| 83 | |||
| 84 | let config = embassy_net::Config::dhcpv4(Default::default()); | ||
| 85 | //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { | ||
| 86 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | ||
| 87 | // dns_servers: Vec::new(), | ||
| 88 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | ||
| 89 | //}); | ||
| 90 | |||
| 91 | // Init network stack | ||
| 92 | let stack = &*make_static!(Stack::new( | ||
| 93 | device, | ||
| 94 | config, | ||
| 95 | make_static!(StackResources::<2>::new()), | ||
| 96 | seed | ||
| 97 | )); | ||
| 98 | |||
| 99 | // Launch network task | ||
| 100 | unwrap!(spawner.spawn(net_task(&stack))); | ||
| 101 | |||
| 102 | info!("Network task initialized"); | ||
| 103 | |||
| 104 | // Then we can use it! | ||
| 105 | let mut rx_buffer = [0; 1024]; | ||
| 106 | let mut tx_buffer = [0; 1024]; | ||
| 107 | |||
| 108 | loop { | ||
| 109 | let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); | ||
| 110 | |||
| 111 | socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); | ||
| 112 | |||
| 113 | let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); | ||
| 114 | info!("connecting..."); | ||
| 115 | let r = socket.connect(remote_endpoint).await; | ||
| 116 | if let Err(e) = r { | ||
| 117 | info!("connect error: {:?}", e); | ||
| 118 | Timer::after(Duration::from_secs(3)).await; | ||
| 119 | continue; | ||
| 120 | } | ||
| 121 | info!("connected!"); | ||
| 122 | loop { | ||
| 123 | let r = socket.write_all(b"Hello\n").await; | ||
| 124 | if let Err(e) = r { | ||
| 125 | info!("write error: {:?}", e); | ||
| 126 | continue; | ||
| 127 | } | ||
| 128 | Timer::after(Duration::from_secs(1)).await; | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
diff --git a/examples/stm32h5/src/bin/i2c.rs b/examples/stm32h5/src/bin/i2c.rs new file mode 100644 index 000000000..8b6fe71ae --- /dev/null +++ b/examples/stm32h5/src/bin/i2c.rs | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; | ||
| 8 | use embassy_stm32::time::Hertz; | ||
| 9 | use embassy_stm32::{bind_interrupts, i2c, peripherals}; | ||
| 10 | use embassy_time::Duration; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | const ADDRESS: u8 = 0x5F; | ||
| 14 | const WHOAMI: u8 = 0x0F; | ||
| 15 | |||
| 16 | bind_interrupts!(struct Irqs { | ||
| 17 | I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>; | ||
| 18 | }); | ||
| 19 | |||
| 20 | #[embassy_executor::main] | ||
| 21 | async fn main(_spawner: Spawner) { | ||
| 22 | info!("Hello world!"); | ||
| 23 | let p = embassy_stm32::init(Default::default()); | ||
| 24 | |||
| 25 | let mut i2c = I2c::new( | ||
| 26 | p.I2C2, | ||
| 27 | p.PB10, | ||
| 28 | p.PB11, | ||
| 29 | Irqs, | ||
| 30 | p.GPDMA1_CH4, | ||
| 31 | p.GPDMA1_CH5, | ||
| 32 | Hertz(100_000), | ||
| 33 | Default::default(), | ||
| 34 | ); | ||
| 35 | |||
| 36 | // I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long. | ||
| 37 | // TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay. | ||
| 38 | let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); | ||
| 39 | |||
| 40 | let mut data = [0u8; 1]; | ||
| 41 | |||
| 42 | match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { | ||
| 43 | Ok(()) => info!("Whoami: {}", data[0]), | ||
| 44 | Err(Error::Timeout) => error!("Operation timed out"), | ||
| 45 | Err(e) => error!("I2c Error: {:?}", e), | ||
| 46 | } | ||
| 47 | } | ||
diff --git a/examples/stm32h5/src/bin/rng.rs b/examples/stm32h5/src/bin/rng.rs new file mode 100644 index 000000000..af9be0b62 --- /dev/null +++ b/examples/stm32h5/src/bin/rng.rs | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::rng::Rng; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async fn main(_spawner: Spawner) { | ||
| 12 | let p = embassy_stm32::init(Default::default()); | ||
| 13 | info!("Hello World!"); | ||
| 14 | |||
| 15 | let mut rng = Rng::new(p.RNG); | ||
| 16 | |||
| 17 | let mut buf = [0u8; 16]; | ||
| 18 | unwrap!(rng.async_fill_bytes(&mut buf).await); | ||
| 19 | info!("random bytes: {:02x}", buf); | ||
| 20 | } | ||
diff --git a/examples/stm32h5/src/bin/usart.rs b/examples/stm32h5/src/bin/usart.rs new file mode 100644 index 000000000..0abb94abb --- /dev/null +++ b/examples/stm32h5/src/bin/usart.rs | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use cortex_m_rt::entry; | ||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Executor; | ||
| 8 | use embassy_stm32::dma::NoDma; | ||
| 9 | use embassy_stm32::usart::{Config, Uart}; | ||
| 10 | use embassy_stm32::{bind_interrupts, peripherals, usart}; | ||
| 11 | use static_cell::StaticCell; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | bind_interrupts!(struct Irqs { | ||
| 15 | UART7 => usart::InterruptHandler<peripherals::UART7>; | ||
| 16 | }); | ||
| 17 | |||
| 18 | #[embassy_executor::task] | ||
| 19 | async fn main_task() { | ||
| 20 | let p = embassy_stm32::init(Default::default()); | ||
| 21 | |||
| 22 | let config = Config::default(); | ||
| 23 | let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, NoDma, NoDma, config); | ||
| 24 | |||
| 25 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); | ||
| 26 | info!("wrote Hello, starting echo"); | ||
| 27 | |||
| 28 | let mut buf = [0u8; 1]; | ||
| 29 | loop { | ||
| 30 | unwrap!(usart.blocking_read(&mut buf)); | ||
| 31 | unwrap!(usart.blocking_write(&buf)); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | static EXECUTOR: StaticCell<Executor> = StaticCell::new(); | ||
| 36 | |||
| 37 | #[entry] | ||
| 38 | fn main() -> ! { | ||
| 39 | info!("Hello World!"); | ||
| 40 | |||
| 41 | let executor = EXECUTOR.init(Executor::new()); | ||
| 42 | |||
| 43 | executor.run(|spawner| { | ||
| 44 | unwrap!(spawner.spawn(main_task())); | ||
| 45 | }) | ||
| 46 | } | ||
diff --git a/examples/stm32h5/src/bin/usart_dma.rs b/examples/stm32h5/src/bin/usart_dma.rs new file mode 100644 index 000000000..48264f884 --- /dev/null +++ b/examples/stm32h5/src/bin/usart_dma.rs | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::fmt::Write; | ||
| 6 | |||
| 7 | use cortex_m_rt::entry; | ||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Executor; | ||
| 10 | use embassy_stm32::dma::NoDma; | ||
| 11 | use embassy_stm32::usart::{Config, Uart}; | ||
| 12 | use embassy_stm32::{bind_interrupts, peripherals, usart}; | ||
| 13 | use heapless::String; | ||
| 14 | use static_cell::StaticCell; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | bind_interrupts!(struct Irqs { | ||
| 18 | UART7 => usart::InterruptHandler<peripherals::UART7>; | ||
| 19 | }); | ||
| 20 | |||
| 21 | #[embassy_executor::task] | ||
| 22 | async fn main_task() { | ||
| 23 | let p = embassy_stm32::init(Default::default()); | ||
| 24 | |||
| 25 | let config = Config::default(); | ||
| 26 | let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.GPDMA1_CH0, NoDma, config); | ||
| 27 | |||
| 28 | for n in 0u32.. { | ||
| 29 | let mut s: String<128> = String::new(); | ||
| 30 | core::write!(&mut s, "Hello DMA World {}!\r\n", n).unwrap(); | ||
| 31 | |||
| 32 | usart.write(s.as_bytes()).await.ok(); | ||
| 33 | |||
| 34 | info!("wrote DMA"); | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | static EXECUTOR: StaticCell<Executor> = StaticCell::new(); | ||
| 39 | |||
| 40 | #[entry] | ||
| 41 | fn main() -> ! { | ||
| 42 | info!("Hello World!"); | ||
| 43 | |||
| 44 | let executor = EXECUTOR.init(Executor::new()); | ||
| 45 | |||
| 46 | executor.run(|spawner| { | ||
| 47 | unwrap!(spawner.spawn(main_task())); | ||
| 48 | }) | ||
| 49 | } | ||
diff --git a/examples/stm32h5/src/bin/usart_split.rs b/examples/stm32h5/src/bin/usart_split.rs new file mode 100644 index 000000000..debd6f454 --- /dev/null +++ b/examples/stm32h5/src/bin/usart_split.rs | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::dma::NoDma; | ||
| 8 | use embassy_stm32::peripherals::{GPDMA1_CH1, UART7}; | ||
| 9 | use embassy_stm32::usart::{Config, Uart, UartRx}; | ||
| 10 | use embassy_stm32::{bind_interrupts, peripherals, usart}; | ||
| 11 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | ||
| 12 | use embassy_sync::channel::Channel; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | UART7 => usart::InterruptHandler<peripherals::UART7>; | ||
| 17 | }); | ||
| 18 | |||
| 19 | #[embassy_executor::task] | ||
| 20 | async fn writer(mut usart: Uart<'static, UART7, NoDma, NoDma>) { | ||
| 21 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); | ||
| 22 | info!("wrote Hello, starting echo"); | ||
| 23 | |||
| 24 | let mut buf = [0u8; 1]; | ||
| 25 | loop { | ||
| 26 | unwrap!(usart.blocking_read(&mut buf)); | ||
| 27 | unwrap!(usart.blocking_write(&buf)); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | static CHANNEL: Channel<ThreadModeRawMutex, [u8; 8], 1> = Channel::new(); | ||
| 32 | |||
| 33 | #[embassy_executor::main] | ||
| 34 | async fn main(spawner: Spawner) -> ! { | ||
| 35 | let p = embassy_stm32::init(Default::default()); | ||
| 36 | info!("Hello World!"); | ||
| 37 | |||
| 38 | let config = Config::default(); | ||
| 39 | let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.GPDMA1_CH0, p.GPDMA1_CH1, config); | ||
| 40 | unwrap!(usart.blocking_write(b"Type 8 chars to echo!\r\n")); | ||
| 41 | |||
| 42 | let (mut tx, rx) = usart.split(); | ||
| 43 | |||
| 44 | unwrap!(spawner.spawn(reader(rx))); | ||
| 45 | |||
| 46 | loop { | ||
| 47 | let buf = CHANNEL.recv().await; | ||
| 48 | info!("writing..."); | ||
| 49 | unwrap!(tx.write(&buf).await); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | #[embassy_executor::task] | ||
| 54 | async fn reader(mut rx: UartRx<'static, UART7, GPDMA1_CH1>) { | ||
| 55 | let mut buf = [0; 8]; | ||
| 56 | loop { | ||
| 57 | info!("reading..."); | ||
| 58 | unwrap!(rx.read(&mut buf).await); | ||
| 59 | CHANNEL.send(buf).await; | ||
| 60 | } | ||
| 61 | } | ||
diff --git a/examples/stm32h5/src/bin/usb_serial.rs b/examples/stm32h5/src/bin/usb_serial.rs new file mode 100644 index 000000000..336eed644 --- /dev/null +++ b/examples/stm32h5/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{panic, *}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllSource, Sysclk, VoltageScale}; | ||
| 8 | use embassy_stm32::time::Hertz; | ||
| 9 | use embassy_stm32::usb::{Driver, Instance}; | ||
| 10 | use embassy_stm32::{bind_interrupts, pac, peripherals, usb, Config}; | ||
| 11 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 12 | use embassy_usb::driver::EndpointError; | ||
| 13 | use embassy_usb::Builder; | ||
| 14 | use futures::future::join; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | bind_interrupts!(struct Irqs { | ||
| 18 | USB_DRD_FS => usb::InterruptHandler<peripherals::USB>; | ||
| 19 | }); | ||
| 20 | |||
| 21 | #[embassy_executor::main] | ||
| 22 | async fn main(_spawner: Spawner) { | ||
| 23 | let mut config = Config::default(); | ||
| 24 | config.rcc.hsi = None; | ||
| 25 | config.rcc.hsi48 = true; // needed for usb | ||
| 26 | config.rcc.hse = Some(Hse { | ||
| 27 | freq: Hertz(8_000_000), | ||
| 28 | mode: HseMode::BypassDigital, | ||
| 29 | }); | ||
| 30 | config.rcc.pll1 = Some(Pll { | ||
| 31 | source: PllSource::Hse, | ||
| 32 | prediv: 2, | ||
| 33 | mul: 125, | ||
| 34 | divp: Some(2), // 250mhz | ||
| 35 | divq: None, | ||
| 36 | divr: None, | ||
| 37 | }); | ||
| 38 | config.rcc.ahb_pre = AHBPrescaler::Div2; | ||
| 39 | config.rcc.apb1_pre = APBPrescaler::Div4; | ||
| 40 | config.rcc.apb2_pre = APBPrescaler::Div2; | ||
| 41 | config.rcc.apb3_pre = APBPrescaler::Div4; | ||
| 42 | config.rcc.sys = Sysclk::Pll1P; | ||
| 43 | config.rcc.voltage_scale = VoltageScale::Scale0; | ||
| 44 | let p = embassy_stm32::init(config); | ||
| 45 | |||
| 46 | info!("Hello World!"); | ||
| 47 | |||
| 48 | pac::RCC.ccipr4().write(|w| { | ||
| 49 | w.set_usbsel(pac::rcc::vals::Usbsel::HSI48); | ||
| 50 | }); | ||
| 51 | |||
| 52 | // Create the driver, from the HAL. | ||
| 53 | let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); | ||
| 54 | |||
| 55 | // Create embassy-usb Config | ||
| 56 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 57 | config.manufacturer = Some("Embassy"); | ||
| 58 | config.product = Some("USB-serial example"); | ||
| 59 | config.serial_number = Some("12345678"); | ||
| 60 | |||
| 61 | // Required for windows compatibility. | ||
| 62 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 63 | config.device_class = 0xEF; | ||
| 64 | config.device_sub_class = 0x02; | ||
| 65 | config.device_protocol = 0x01; | ||
| 66 | config.composite_with_iads = true; | ||
| 67 | |||
| 68 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 69 | // It needs some buffers for building the descriptors. | ||
| 70 | let mut device_descriptor = [0; 256]; | ||
| 71 | let mut config_descriptor = [0; 256]; | ||
| 72 | let mut bos_descriptor = [0; 256]; | ||
| 73 | let mut control_buf = [0; 64]; | ||
| 74 | |||
| 75 | let mut state = State::new(); | ||
| 76 | |||
| 77 | let mut builder = Builder::new( | ||
| 78 | driver, | ||
| 79 | config, | ||
| 80 | &mut device_descriptor, | ||
| 81 | &mut config_descriptor, | ||
| 82 | &mut bos_descriptor, | ||
| 83 | &mut control_buf, | ||
| 84 | ); | ||
| 85 | |||
| 86 | // Create classes on the builder. | ||
| 87 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 88 | |||
| 89 | // Build the builder. | ||
| 90 | let mut usb = builder.build(); | ||
| 91 | |||
| 92 | // Run the USB device. | ||
| 93 | let usb_fut = usb.run(); | ||
| 94 | |||
| 95 | // Do stuff with the class! | ||
| 96 | let echo_fut = async { | ||
| 97 | loop { | ||
| 98 | class.wait_connection().await; | ||
| 99 | info!("Connected"); | ||
| 100 | let _ = echo(&mut class).await; | ||
| 101 | info!("Disconnected"); | ||
| 102 | } | ||
| 103 | }; | ||
| 104 | |||
| 105 | // Run everything concurrently. | ||
| 106 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 107 | join(usb_fut, echo_fut).await; | ||
| 108 | } | ||
| 109 | |||
| 110 | struct Disconnected {} | ||
| 111 | |||
| 112 | impl From<EndpointError> for Disconnected { | ||
| 113 | fn from(val: EndpointError) -> Self { | ||
| 114 | match val { | ||
| 115 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 116 | EndpointError::Disabled => Disconnected {}, | ||
| 117 | } | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 122 | let mut buf = [0; 64]; | ||
| 123 | loop { | ||
| 124 | let n = class.read_packet(&mut buf).await?; | ||
| 125 | let data = &buf[..n]; | ||
| 126 | info!("data: {:x}", data); | ||
| 127 | class.write_packet(data).await?; | ||
| 128 | } | ||
| 129 | } | ||
diff --git a/examples/stm32h7/.cargo/config.toml b/examples/stm32h7/.cargo/config.toml index d38be23e0..5f680dbce 100644 --- a/examples/stm32h7/.cargo/config.toml +++ b/examples/stm32h7/.cargo/config.toml | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | [target.thumbv7em-none-eabihf] | 1 | [target.thumbv7em-none-eabihf] |
| 2 | runner = 'probe-run --chip STM32H743ZITx' | 2 | runner = 'probe-rs run --chip STM32H743ZITx' |
| 3 | 3 | ||
| 4 | [build] | 4 | [build] |
| 5 | target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) | 5 | target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) |
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 0f76f3226..768702fa9 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml | |||
| @@ -2,24 +2,26 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-stm32h7-examples" | 3 | name = "embassy-stm32h7-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } |
| 11 | embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16", "unstable-traits"] } | 12 | embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } |
| 12 | embedded-io = { version = "0.3.0", features = ["async"] } | 13 | embedded-io = { version = "0.4.0", features = ["async"] } |
| 14 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 13 | 15 | ||
| 14 | defmt = "0.3" | 16 | defmt = "0.3" |
| 15 | defmt-rtt = "0.3" | 17 | defmt-rtt = "0.4" |
| 16 | 18 | ||
| 17 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 19 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 18 | cortex-m-rt = "0.7.0" | 20 | cortex-m-rt = "0.7.0" |
| 19 | embedded-hal = "0.2.6" | 21 | embedded-hal = "0.2.6" |
| 20 | embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } | 22 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } |
| 21 | embedded-hal-async = { version = "0.1.0-alpha.1" } | 23 | embedded-hal-async = { version = "=0.2.0-alpha.2" } |
| 22 | embedded-nal-async = "0.2.0" | 24 | embedded-nal-async = "0.4.0" |
| 23 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 25 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 24 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 26 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } |
| 25 | heapless = { version = "0.7.5", default-features = false } | 27 | heapless = { version = "0.7.5", default-features = false } |
| @@ -28,7 +30,7 @@ critical-section = "1.1" | |||
| 28 | micromath = "2.0.0" | 30 | micromath = "2.0.0" |
| 29 | stm32-fmc = "0.2.4" | 31 | stm32-fmc = "0.2.4" |
| 30 | embedded-storage = "0.3.0" | 32 | embedded-storage = "0.3.0" |
| 31 | static_cell = "1.0" | 33 | static_cell = { version = "1.1", features = ["nightly"]} |
| 32 | 34 | ||
| 33 | # cargo build/run | 35 | # cargo build/run |
| 34 | [profile.dev] | 36 | [profile.dev] |
diff --git a/examples/stm32h7/src/bin/camera.rs b/examples/stm32h7/src/bin/camera.rs index 9c443b83a..6f75a0630 100644 --- a/examples/stm32h7/src/bin/camera.rs +++ b/examples/stm32h7/src/bin/camera.rs | |||
| @@ -8,7 +8,7 @@ use embassy_stm32::gpio::{Level, Output, Speed}; | |||
| 8 | use embassy_stm32::i2c::I2c; | 8 | use embassy_stm32::i2c::I2c; |
| 9 | use embassy_stm32::rcc::{Mco, Mco1Source, McoClock}; | 9 | use embassy_stm32::rcc::{Mco, Mco1Source, McoClock}; |
| 10 | use embassy_stm32::time::{khz, mhz}; | 10 | use embassy_stm32::time::{khz, mhz}; |
| 11 | use embassy_stm32::{interrupt, Config}; | 11 | use embassy_stm32::{bind_interrupts, i2c, peripherals, Config}; |
| 12 | use embassy_time::{Duration, Timer}; | 12 | use embassy_time::{Duration, Timer}; |
| 13 | use ov7725::*; | 13 | use ov7725::*; |
| 14 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -18,6 +18,11 @@ const HEIGHT: usize = 100; | |||
| 18 | 18 | ||
| 19 | static mut FRAME: [u32; WIDTH * HEIGHT / 2] = [0u32; WIDTH * HEIGHT / 2]; | 19 | static mut FRAME: [u32; WIDTH * HEIGHT / 2] = [0u32; WIDTH * HEIGHT / 2]; |
| 20 | 20 | ||
| 21 | bind_interrupts!(struct Irqs { | ||
| 22 | I2C1_EV => i2c::InterruptHandler<peripherals::I2C1>; | ||
| 23 | DCMI => dcmi::InterruptHandler<peripherals::DCMI>; | ||
| 24 | }); | ||
| 25 | |||
| 21 | #[embassy_executor::main] | 26 | #[embassy_executor::main] |
| 22 | async fn main(_spawner: Spawner) { | 27 | async fn main(_spawner: Spawner) { |
| 23 | let mut config = Config::default(); | 28 | let mut config = Config::default(); |
| @@ -34,12 +39,11 @@ async fn main(_spawner: Spawner) { | |||
| 34 | let mco = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::Divided(3)); | 39 | let mco = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::Divided(3)); |
| 35 | 40 | ||
| 36 | let mut led = Output::new(p.PE3, Level::High, Speed::Low); | 41 | let mut led = Output::new(p.PE3, Level::High, Speed::Low); |
| 37 | let i2c_irq = interrupt::take!(I2C1_EV); | ||
| 38 | let cam_i2c = I2c::new( | 42 | let cam_i2c = I2c::new( |
| 39 | p.I2C1, | 43 | p.I2C1, |
| 40 | p.PB8, | 44 | p.PB8, |
| 41 | p.PB9, | 45 | p.PB9, |
| 42 | i2c_irq, | 46 | Irqs, |
| 43 | p.DMA1_CH1, | 47 | p.DMA1_CH1, |
| 44 | p.DMA1_CH2, | 48 | p.DMA1_CH2, |
| 45 | khz(100), | 49 | khz(100), |
| @@ -55,11 +59,9 @@ async fn main(_spawner: Spawner) { | |||
| 55 | 59 | ||
| 56 | defmt::info!("manufacturer: 0x{:x}, pid: 0x{:x}", manufacturer_id, camera_id); | 60 | defmt::info!("manufacturer: 0x{:x}, pid: 0x{:x}", manufacturer_id, camera_id); |
| 57 | 61 | ||
| 58 | let dcmi_irq = interrupt::take!(DCMI); | ||
| 59 | let config = dcmi::Config::default(); | 62 | let config = dcmi::Config::default(); |
| 60 | let mut dcmi = Dcmi::new_8bit( | 63 | let mut dcmi = Dcmi::new_8bit( |
| 61 | p.DCMI, p.DMA1_CH0, dcmi_irq, p.PC6, p.PC7, p.PE0, p.PE1, p.PE4, p.PD3, p.PE5, p.PE6, p.PB7, p.PA4, p.PA6, | 64 | p.DCMI, p.DMA1_CH0, Irqs, p.PC6, p.PC7, p.PE0, p.PE1, p.PE4, p.PD3, p.PE5, p.PE6, p.PB7, p.PA4, p.PA6, config, |
| 62 | config, | ||
| 63 | ); | 65 | ); |
| 64 | 66 | ||
| 65 | defmt::info!("attempting capture"); | 67 | defmt::info!("attempting capture"); |
diff --git a/examples/stm32h7/src/bin/dac.rs b/examples/stm32h7/src/bin/dac.rs index f12716370..586b4154b 100644 --- a/examples/stm32h7/src/bin/dac.rs +++ b/examples/stm32h7/src/bin/dac.rs | |||
| @@ -4,7 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | use cortex_m_rt::entry; | 5 | use cortex_m_rt::entry; |
| 6 | use defmt::*; | 6 | use defmt::*; |
| 7 | use embassy_stm32::dac::{Channel, Dac, Value}; | 7 | use embassy_stm32::dac::{DacCh1, DacChannel, Value}; |
| 8 | use embassy_stm32::dma::NoDma; | ||
| 8 | use embassy_stm32::time::mhz; | 9 | use embassy_stm32::time::mhz; |
| 9 | use embassy_stm32::Config; | 10 | use embassy_stm32::Config; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -19,12 +20,12 @@ fn main() -> ! { | |||
| 19 | config.rcc.pll1.q_ck = Some(mhz(100)); | 20 | config.rcc.pll1.q_ck = Some(mhz(100)); |
| 20 | let p = embassy_stm32::init(config); | 21 | let p = embassy_stm32::init(config); |
| 21 | 22 | ||
| 22 | let mut dac = Dac::new_1ch(p.DAC1, p.PA4); | 23 | let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); |
| 23 | 24 | ||
| 24 | loop { | 25 | loop { |
| 25 | for v in 0..=255 { | 26 | for v in 0..=255 { |
| 26 | unwrap!(dac.set(Channel::Ch1, Value::Bit8(to_sine_wave(v)))); | 27 | unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); |
| 27 | unwrap!(dac.trigger(Channel::Ch1)); | 28 | dac.trigger(); |
| 28 | } | 29 | } |
| 29 | } | 30 | } |
| 30 | } | 31 | } |
diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 4ccc0b5ef..cfafcaed1 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs | |||
| @@ -7,26 +7,21 @@ use embassy_executor::Spawner; | |||
| 7 | use embassy_net::tcp::TcpSocket; | 7 | use embassy_net::tcp::TcpSocket; |
| 8 | use embassy_net::{Ipv4Address, Stack, StackResources}; | 8 | use embassy_net::{Ipv4Address, Stack, StackResources}; |
| 9 | use embassy_stm32::eth::generic_smi::GenericSMI; | 9 | use embassy_stm32::eth::generic_smi::GenericSMI; |
| 10 | use embassy_stm32::eth::{Ethernet, State}; | 10 | use embassy_stm32::eth::{Ethernet, PacketQueue}; |
| 11 | use embassy_stm32::peripherals::ETH; | 11 | use embassy_stm32::peripherals::ETH; |
| 12 | use embassy_stm32::rng::Rng; | 12 | use embassy_stm32::rng::Rng; |
| 13 | use embassy_stm32::time::mhz; | 13 | use embassy_stm32::time::mhz; |
| 14 | use embassy_stm32::{interrupt, Config}; | 14 | use embassy_stm32::{bind_interrupts, eth, Config}; |
| 15 | use embassy_time::{Duration, Timer}; | 15 | use embassy_time::{Duration, Timer}; |
| 16 | use embedded_io::asynch::Write; | 16 | use embedded_io::asynch::Write; |
| 17 | use rand_core::RngCore; | 17 | use rand_core::RngCore; |
| 18 | use static_cell::StaticCell; | 18 | use static_cell::make_static; |
| 19 | use {defmt_rtt as _, panic_probe as _}; | 19 | use {defmt_rtt as _, panic_probe as _}; |
| 20 | bind_interrupts!(struct Irqs { | ||
| 21 | ETH => eth::InterruptHandler; | ||
| 22 | }); | ||
| 20 | 23 | ||
| 21 | macro_rules! singleton { | 24 | type Device = Ethernet<'static, ETH, GenericSMI>; |
| 22 | ($val:expr) => {{ | ||
| 23 | type T = impl Sized; | ||
| 24 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | ||
| 25 | STATIC_CELL.init_with(move || $val) | ||
| 26 | }}; | ||
| 27 | } | ||
| 28 | |||
| 29 | type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; | ||
| 30 | 25 | ||
| 31 | #[embassy_executor::task] | 26 | #[embassy_executor::task] |
| 32 | async fn net_task(stack: &'static Stack<Device>) -> ! { | 27 | async fn net_task(stack: &'static Stack<Device>) -> ! { |
| @@ -48,41 +43,38 @@ async fn main(spawner: Spawner) -> ! { | |||
| 48 | rng.fill_bytes(&mut seed); | 43 | rng.fill_bytes(&mut seed); |
| 49 | let seed = u64::from_le_bytes(seed); | 44 | let seed = u64::from_le_bytes(seed); |
| 50 | 45 | ||
| 51 | let eth_int = interrupt::take!(ETH); | ||
| 52 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; | 46 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; |
| 53 | 47 | ||
| 54 | let device = unsafe { | 48 | let device = Ethernet::new( |
| 55 | Ethernet::new( | 49 | make_static!(PacketQueue::<16, 16>::new()), |
| 56 | singleton!(State::new()), | 50 | p.ETH, |
| 57 | p.ETH, | 51 | Irqs, |
| 58 | eth_int, | 52 | p.PA1, |
| 59 | p.PA1, | 53 | p.PA2, |
| 60 | p.PA2, | 54 | p.PC1, |
| 61 | p.PC1, | 55 | p.PA7, |
| 62 | p.PA7, | 56 | p.PC4, |
| 63 | p.PC4, | 57 | p.PC5, |
| 64 | p.PC5, | 58 | p.PG13, |
| 65 | p.PG13, | 59 | p.PB13, |
| 66 | p.PB13, | 60 | p.PG11, |
| 67 | p.PG11, | 61 | GenericSMI::new(), |
| 68 | GenericSMI, | 62 | mac_addr, |
| 69 | mac_addr, | 63 | 0, |
| 70 | 0, | 64 | ); |
| 71 | ) | 65 | |
| 72 | }; | 66 | let config = embassy_net::Config::dhcpv4(Default::default()); |
| 73 | 67 | //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { | |
| 74 | let config = embassy_net::ConfigStrategy::Dhcp; | ||
| 75 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { | ||
| 76 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | 68 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), |
| 77 | // dns_servers: Vec::new(), | 69 | // dns_servers: Vec::new(), |
| 78 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | 70 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), |
| 79 | //}); | 71 | //}); |
| 80 | 72 | ||
| 81 | // Init network stack | 73 | // Init network stack |
| 82 | let stack = &*singleton!(Stack::new( | 74 | let stack = &*make_static!(Stack::new( |
| 83 | device, | 75 | device, |
| 84 | config, | 76 | config, |
| 85 | singleton!(StackResources::<1, 2, 8>::new()), | 77 | make_static!(StackResources::<2>::new()), |
| 86 | seed | 78 | seed |
| 87 | )); | 79 | )); |
| 88 | 80 | ||
| @@ -98,7 +90,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 98 | loop { | 90 | loop { |
| 99 | let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); | 91 | let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); |
| 100 | 92 | ||
| 101 | socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); | 93 | socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); |
| 102 | 94 | ||
| 103 | let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); | 95 | let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); |
| 104 | info!("connecting..."); | 96 | info!("connecting..."); |
| @@ -112,7 +104,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 112 | let r = socket.write_all(b"Hello\n").await; | 104 | let r = socket.write_all(b"Hello\n").await; |
| 113 | if let Err(e) = r { | 105 | if let Err(e) = r { |
| 114 | info!("write error: {:?}", e); | 106 | info!("write error: {:?}", e); |
| 115 | return; | 107 | continue; |
| 116 | } | 108 | } |
| 117 | Timer::after(Duration::from_secs(1)).await; | 109 | Timer::after(Duration::from_secs(1)).await; |
| 118 | } | 110 | } |
diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 64fd84141..4ed737578 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs | |||
| @@ -7,27 +7,22 @@ use embassy_executor::Spawner; | |||
| 7 | use embassy_net::tcp::client::{TcpClient, TcpClientState}; | 7 | use embassy_net::tcp::client::{TcpClient, TcpClientState}; |
| 8 | use embassy_net::{Stack, StackResources}; | 8 | use embassy_net::{Stack, StackResources}; |
| 9 | use embassy_stm32::eth::generic_smi::GenericSMI; | 9 | use embassy_stm32::eth::generic_smi::GenericSMI; |
| 10 | use embassy_stm32::eth::{Ethernet, State}; | 10 | use embassy_stm32::eth::{Ethernet, PacketQueue}; |
| 11 | use embassy_stm32::peripherals::ETH; | 11 | use embassy_stm32::peripherals::ETH; |
| 12 | use embassy_stm32::rng::Rng; | 12 | use embassy_stm32::rng::Rng; |
| 13 | use embassy_stm32::time::mhz; | 13 | use embassy_stm32::time::mhz; |
| 14 | use embassy_stm32::{interrupt, Config}; | 14 | use embassy_stm32::{bind_interrupts, eth, Config}; |
| 15 | use embassy_time::{Duration, Timer}; | 15 | use embassy_time::{Duration, Timer}; |
| 16 | use embedded_io::asynch::Write; | 16 | use embedded_io::asynch::Write; |
| 17 | use embedded_nal_async::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpConnect}; | 17 | use embedded_nal_async::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpConnect}; |
| 18 | use rand_core::RngCore; | 18 | use rand_core::RngCore; |
| 19 | use static_cell::StaticCell; | 19 | use static_cell::make_static; |
| 20 | use {defmt_rtt as _, panic_probe as _}; | 20 | use {defmt_rtt as _, panic_probe as _}; |
| 21 | bind_interrupts!(struct Irqs { | ||
| 22 | ETH => eth::InterruptHandler; | ||
| 23 | }); | ||
| 21 | 24 | ||
| 22 | macro_rules! singleton { | 25 | type Device = Ethernet<'static, ETH, GenericSMI>; |
| 23 | ($val:expr) => {{ | ||
| 24 | type T = impl Sized; | ||
| 25 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | ||
| 26 | STATIC_CELL.init_with(move || $val) | ||
| 27 | }}; | ||
| 28 | } | ||
| 29 | |||
| 30 | type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; | ||
| 31 | 26 | ||
| 32 | #[embassy_executor::task] | 27 | #[embassy_executor::task] |
| 33 | async fn net_task(stack: &'static Stack<Device>) -> ! { | 28 | async fn net_task(stack: &'static Stack<Device>) -> ! { |
| @@ -49,41 +44,38 @@ async fn main(spawner: Spawner) -> ! { | |||
| 49 | rng.fill_bytes(&mut seed); | 44 | rng.fill_bytes(&mut seed); |
| 50 | let seed = u64::from_le_bytes(seed); | 45 | let seed = u64::from_le_bytes(seed); |
| 51 | 46 | ||
| 52 | let eth_int = interrupt::take!(ETH); | ||
| 53 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; | 47 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; |
| 54 | 48 | ||
| 55 | let device = unsafe { | 49 | let device = Ethernet::new( |
| 56 | Ethernet::new( | 50 | make_static!(PacketQueue::<16, 16>::new()), |
| 57 | singleton!(State::new()), | 51 | p.ETH, |
| 58 | p.ETH, | 52 | Irqs, |
| 59 | eth_int, | 53 | p.PA1, |
| 60 | p.PA1, | 54 | p.PA2, |
| 61 | p.PA2, | 55 | p.PC1, |
| 62 | p.PC1, | 56 | p.PA7, |
| 63 | p.PA7, | 57 | p.PC4, |
| 64 | p.PC4, | 58 | p.PC5, |
| 65 | p.PC5, | 59 | p.PG13, |
| 66 | p.PG13, | 60 | p.PB13, |
| 67 | p.PB13, | 61 | p.PG11, |
| 68 | p.PG11, | 62 | GenericSMI::new(), |
| 69 | GenericSMI, | 63 | mac_addr, |
| 70 | mac_addr, | 64 | 0, |
| 71 | 0, | 65 | ); |
| 72 | ) | 66 | |
| 73 | }; | 67 | let config = embassy_net::Config::dhcpv4(Default::default()); |
| 74 | 68 | //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { | |
| 75 | let config = embassy_net::ConfigStrategy::Dhcp; | ||
| 76 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { | ||
| 77 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | 69 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), |
| 78 | // dns_servers: Vec::new(), | 70 | // dns_servers: Vec::new(), |
| 79 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | 71 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), |
| 80 | //}); | 72 | //}); |
| 81 | 73 | ||
| 82 | // Init network stack | 74 | // Init network stack |
| 83 | let stack = &*singleton!(Stack::new( | 75 | let stack = &*make_static!(Stack::new( |
| 84 | device, | 76 | device, |
| 85 | config, | 77 | config, |
| 86 | singleton!(StackResources::<1, 2, 8>::new()), | 78 | make_static!(StackResources::<2>::new()), |
| 87 | seed | 79 | seed |
| 88 | )); | 80 | )); |
| 89 | 81 | ||
| @@ -114,7 +106,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 114 | let r = connection.write_all(b"Hello\n").await; | 106 | let r = connection.write_all(b"Hello\n").await; |
| 115 | if let Err(e) = r { | 107 | if let Err(e) = r { |
| 116 | info!("write error: {:?}", e); | 108 | info!("write error: {:?}", e); |
| 117 | return; | 109 | continue; |
| 118 | } | 110 | } |
| 119 | Timer::after(Duration::from_secs(1)).await; | 111 | Timer::after(Duration::from_secs(1)).await; |
| 120 | } | 112 | } |
diff --git a/examples/stm32h7/src/bin/flash.rs b/examples/stm32h7/src/bin/flash.rs index 6682c64d5..f66df770b 100644 --- a/examples/stm32h7/src/bin/flash.rs +++ b/examples/stm32h7/src/bin/flash.rs | |||
| @@ -6,7 +6,6 @@ use defmt::{info, unwrap}; | |||
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::flash::Flash; | 7 | use embassy_stm32::flash::Flash; |
| 8 | use embassy_time::{Duration, Timer}; | 8 | use embassy_time::{Duration, Timer}; |
| 9 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 10 | ||
| 12 | #[embassy_executor::main] | 11 | #[embassy_executor::main] |
| @@ -14,28 +13,28 @@ async fn main(_spawner: Spawner) { | |||
| 14 | let p = embassy_stm32::init(Default::default()); | 13 | let p = embassy_stm32::init(Default::default()); |
| 15 | info!("Hello Flash!"); | 14 | info!("Hello Flash!"); |
| 16 | 15 | ||
| 17 | const ADDR: u32 = 0x08_0000; | 16 | const ADDR: u32 = 0; // This is the offset into bank 2, the absolute address is 0x8_0000 |
| 18 | 17 | ||
| 19 | // wait a bit before accessing the flash | 18 | // wait a bit before accessing the flash |
| 20 | Timer::after(Duration::from_millis(300)).await; | 19 | Timer::after(Duration::from_millis(300)).await; |
| 21 | 20 | ||
| 22 | let mut f = Flash::unlock(p.FLASH); | 21 | let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank2_region; |
| 23 | 22 | ||
| 24 | info!("Reading..."); | 23 | info!("Reading..."); |
| 25 | let mut buf = [0u8; 32]; | 24 | let mut buf = [0u8; 32]; |
| 26 | unwrap!(f.read(ADDR, &mut buf)); | 25 | unwrap!(f.blocking_read(ADDR, &mut buf)); |
| 27 | info!("Read: {=[u8]:x}", buf); | 26 | info!("Read: {=[u8]:x}", buf); |
| 28 | 27 | ||
| 29 | info!("Erasing..."); | 28 | info!("Erasing..."); |
| 30 | unwrap!(f.erase(ADDR, ADDR + 128 * 1024)); | 29 | unwrap!(f.blocking_erase(ADDR, ADDR + 128 * 1024)); |
| 31 | 30 | ||
| 32 | info!("Reading..."); | 31 | info!("Reading..."); |
| 33 | let mut buf = [0u8; 32]; | 32 | let mut buf = [0u8; 32]; |
| 34 | unwrap!(f.read(ADDR, &mut buf)); | 33 | unwrap!(f.blocking_read(ADDR, &mut buf)); |
| 35 | info!("Read after erase: {=[u8]:x}", buf); | 34 | info!("Read after erase: {=[u8]:x}", buf); |
| 36 | 35 | ||
| 37 | info!("Writing..."); | 36 | info!("Writing..."); |
| 38 | unwrap!(f.write( | 37 | unwrap!(f.blocking_write( |
| 39 | ADDR, | 38 | ADDR, |
| 40 | &[ | 39 | &[ |
| 41 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, | 40 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, |
| @@ -45,7 +44,7 @@ async fn main(_spawner: Spawner) { | |||
| 45 | 44 | ||
| 46 | info!("Reading..."); | 45 | info!("Reading..."); |
| 47 | let mut buf = [0u8; 32]; | 46 | let mut buf = [0u8; 32]; |
| 48 | unwrap!(f.read(ADDR, &mut buf)); | 47 | unwrap!(f.blocking_read(ADDR, &mut buf)); |
| 49 | info!("Read: {=[u8]:x}", buf); | 48 | info!("Read: {=[u8]:x}", buf); |
| 50 | assert_eq!( | 49 | assert_eq!( |
| 51 | &buf[..], | 50 | &buf[..], |
diff --git a/examples/stm32h7/src/bin/i2c.rs b/examples/stm32h7/src/bin/i2c.rs new file mode 100644 index 000000000..c2979c59b --- /dev/null +++ b/examples/stm32h7/src/bin/i2c.rs | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; | ||
| 8 | use embassy_stm32::time::Hertz; | ||
| 9 | use embassy_stm32::{bind_interrupts, i2c, peripherals}; | ||
| 10 | use embassy_time::Duration; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | const ADDRESS: u8 = 0x5F; | ||
| 14 | const WHOAMI: u8 = 0x0F; | ||
| 15 | |||
| 16 | bind_interrupts!(struct Irqs { | ||
| 17 | I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>; | ||
| 18 | }); | ||
| 19 | |||
| 20 | #[embassy_executor::main] | ||
| 21 | async fn main(_spawner: Spawner) { | ||
| 22 | info!("Hello world!"); | ||
| 23 | let p = embassy_stm32::init(Default::default()); | ||
| 24 | |||
| 25 | let mut i2c = I2c::new( | ||
| 26 | p.I2C2, | ||
| 27 | p.PB10, | ||
| 28 | p.PB11, | ||
| 29 | Irqs, | ||
| 30 | p.DMA1_CH4, | ||
| 31 | p.DMA1_CH5, | ||
| 32 | Hertz(100_000), | ||
| 33 | Default::default(), | ||
| 34 | ); | ||
| 35 | |||
| 36 | // I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long. | ||
| 37 | // TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay. | ||
| 38 | let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); | ||
| 39 | |||
| 40 | let mut data = [0u8; 1]; | ||
| 41 | |||
| 42 | match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { | ||
| 43 | Ok(()) => info!("Whoami: {}", data[0]), | ||
| 44 | Err(Error::Timeout) => error!("Operation timed out"), | ||
| 45 | Err(e) => error!("I2c Error: {:?}", e), | ||
| 46 | } | ||
| 47 | } | ||
diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs index 1972f8ff2..d360df085 100644 --- a/examples/stm32h7/src/bin/low_level_timer_api.rs +++ b/examples/stm32h7/src/bin/low_level_timer_api.rs | |||
| @@ -62,49 +62,39 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> { | |||
| 62 | T::enable(); | 62 | T::enable(); |
| 63 | <T as embassy_stm32::rcc::low_level::RccPeripheral>::reset(); | 63 | <T as embassy_stm32::rcc::low_level::RccPeripheral>::reset(); |
| 64 | 64 | ||
| 65 | unsafe { | 65 | ch1.set_speed(Speed::VeryHigh); |
| 66 | ch1.set_speed(Speed::VeryHigh); | 66 | ch1.set_as_af(ch1.af_num(), AFType::OutputPushPull); |
| 67 | ch1.set_as_af(ch1.af_num(), AFType::OutputPushPull); | 67 | ch2.set_speed(Speed::VeryHigh); |
| 68 | ch2.set_speed(Speed::VeryHigh); | 68 | ch2.set_as_af(ch1.af_num(), AFType::OutputPushPull); |
| 69 | ch2.set_as_af(ch1.af_num(), AFType::OutputPushPull); | 69 | ch3.set_speed(Speed::VeryHigh); |
| 70 | ch3.set_speed(Speed::VeryHigh); | 70 | ch3.set_as_af(ch1.af_num(), AFType::OutputPushPull); |
| 71 | ch3.set_as_af(ch1.af_num(), AFType::OutputPushPull); | 71 | ch4.set_speed(Speed::VeryHigh); |
| 72 | ch4.set_speed(Speed::VeryHigh); | 72 | ch4.set_as_af(ch1.af_num(), AFType::OutputPushPull); |
| 73 | ch4.set_as_af(ch1.af_num(), AFType::OutputPushPull); | ||
| 74 | } | ||
| 75 | 73 | ||
| 76 | let mut this = Self { inner: tim }; | 74 | let mut this = Self { inner: tim }; |
| 77 | 75 | ||
| 78 | this.set_freq(freq); | 76 | this.set_freq(freq); |
| 79 | this.inner.start(); | 77 | this.inner.start(); |
| 80 | 78 | ||
| 81 | unsafe { | 79 | let r = T::regs_gp32(); |
| 82 | T::regs_gp32() | 80 | r.ccmr_output(0) |
| 83 | .ccmr_output(0) | 81 | .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into())); |
| 84 | .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into())); | 82 | r.ccmr_output(0) |
| 85 | T::regs_gp32() | 83 | .modify(|w| w.set_ocm(1, OutputCompareMode::PwmMode1.into())); |
| 86 | .ccmr_output(0) | 84 | r.ccmr_output(1) |
| 87 | .modify(|w| w.set_ocm(1, OutputCompareMode::PwmMode1.into())); | 85 | .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into())); |
| 88 | T::regs_gp32() | 86 | r.ccmr_output(1) |
| 89 | .ccmr_output(1) | 87 | .modify(|w| w.set_ocm(1, OutputCompareMode::PwmMode1.into())); |
| 90 | .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into())); | 88 | |
| 91 | T::regs_gp32() | ||
| 92 | .ccmr_output(1) | ||
| 93 | .modify(|w| w.set_ocm(1, OutputCompareMode::PwmMode1.into())); | ||
| 94 | } | ||
| 95 | this | 89 | this |
| 96 | } | 90 | } |
| 97 | 91 | ||
| 98 | pub fn enable(&mut self, channel: Channel) { | 92 | pub fn enable(&mut self, channel: Channel) { |
| 99 | unsafe { | 93 | T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), true)); |
| 100 | T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), true)); | ||
| 101 | } | ||
| 102 | } | 94 | } |
| 103 | 95 | ||
| 104 | pub fn disable(&mut self, channel: Channel) { | 96 | pub fn disable(&mut self, channel: Channel) { |
| 105 | unsafe { | 97 | T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), false)); |
| 106 | T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), false)); | ||
| 107 | } | ||
| 108 | } | 98 | } |
| 109 | 99 | ||
| 110 | pub fn set_freq(&mut self, freq: Hertz) { | 100 | pub fn set_freq(&mut self, freq: Hertz) { |
| @@ -112,11 +102,11 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> { | |||
| 112 | } | 102 | } |
| 113 | 103 | ||
| 114 | pub fn get_max_duty(&self) -> u32 { | 104 | pub fn get_max_duty(&self) -> u32 { |
| 115 | unsafe { T::regs_gp32().arr().read().arr() } | 105 | T::regs_gp32().arr().read().arr() |
| 116 | } | 106 | } |
| 117 | 107 | ||
| 118 | pub fn set_duty(&mut self, channel: Channel, duty: u32) { | 108 | pub fn set_duty(&mut self, channel: Channel, duty: u32) { |
| 119 | defmt::assert!(duty < self.get_max_duty()); | 109 | defmt::assert!(duty < self.get_max_duty()); |
| 120 | unsafe { T::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(duty)) } | 110 | T::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(duty)) |
| 121 | } | 111 | } |
| 122 | } | 112 | } |
diff --git a/examples/stm32h7/src/bin/sdmmc.rs b/examples/stm32h7/src/bin/sdmmc.rs index 26d1db01e..ce91b6b1c 100644 --- a/examples/stm32h7/src/bin/sdmmc.rs +++ b/examples/stm32h7/src/bin/sdmmc.rs | |||
| @@ -6,9 +6,13 @@ use defmt::*; | |||
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::sdmmc::Sdmmc; | 7 | use embassy_stm32::sdmmc::Sdmmc; |
| 8 | use embassy_stm32::time::mhz; | 8 | use embassy_stm32::time::mhz; |
| 9 | use embassy_stm32::{interrupt, Config}; | 9 | use embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config}; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 11 | ||
| 12 | bind_interrupts!(struct Irqs { | ||
| 13 | SDMMC1 => sdmmc::InterruptHandler<peripherals::SDMMC1>; | ||
| 14 | }); | ||
| 15 | |||
| 12 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 13 | async fn main(_spawner: Spawner) -> ! { | 17 | async fn main(_spawner: Spawner) -> ! { |
| 14 | let mut config = Config::default(); | 18 | let mut config = Config::default(); |
| @@ -16,11 +20,9 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 16 | let p = embassy_stm32::init(config); | 20 | let p = embassy_stm32::init(config); |
| 17 | info!("Hello World!"); | 21 | info!("Hello World!"); |
| 18 | 22 | ||
| 19 | let irq = interrupt::take!(SDMMC1); | ||
| 20 | |||
| 21 | let mut sdmmc = Sdmmc::new_4bit( | 23 | let mut sdmmc = Sdmmc::new_4bit( |
| 22 | p.SDMMC1, | 24 | p.SDMMC1, |
| 23 | irq, | 25 | Irqs, |
| 24 | p.PC12, | 26 | p.PC12, |
| 25 | p.PD2, | 27 | p.PD2, |
| 26 | p.PC8, | 28 | p.PC8, |
diff --git a/examples/stm32h7/src/bin/signal.rs b/examples/stm32h7/src/bin/signal.rs index cc3e4e3ca..6d7c168d5 100644 --- a/examples/stm32h7/src/bin/signal.rs +++ b/examples/stm32h7/src/bin/signal.rs | |||
| @@ -4,11 +4,12 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::{info, unwrap}; | 5 | use defmt::{info, unwrap}; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 7 | use embassy_sync::signal::Signal; | 8 | use embassy_sync::signal::Signal; |
| 8 | use embassy_time::{Duration, Timer}; | 9 | use embassy_time::{Duration, Timer}; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 11 | ||
| 11 | static SIGNAL: Signal<u32> = Signal::new(); | 12 | static SIGNAL: Signal<CriticalSectionRawMutex, u32> = Signal::new(); |
| 12 | 13 | ||
| 13 | #[embassy_executor::task] | 14 | #[embassy_executor::task] |
| 14 | async fn my_sending_task() { | 15 | async fn my_sending_task() { |
diff --git a/examples/stm32h7/src/bin/usart.rs b/examples/stm32h7/src/bin/usart.rs index 87c2b1253..0abb94abb 100644 --- a/examples/stm32h7/src/bin/usart.rs +++ b/examples/stm32h7/src/bin/usart.rs | |||
| @@ -7,15 +7,20 @@ use defmt::*; | |||
| 7 | use embassy_executor::Executor; | 7 | use embassy_executor::Executor; |
| 8 | use embassy_stm32::dma::NoDma; | 8 | use embassy_stm32::dma::NoDma; |
| 9 | use embassy_stm32::usart::{Config, Uart}; | 9 | use embassy_stm32::usart::{Config, Uart}; |
| 10 | use embassy_stm32::{bind_interrupts, peripherals, usart}; | ||
| 10 | use static_cell::StaticCell; | 11 | use static_cell::StaticCell; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 13 | ||
| 14 | bind_interrupts!(struct Irqs { | ||
| 15 | UART7 => usart::InterruptHandler<peripherals::UART7>; | ||
| 16 | }); | ||
| 17 | |||
| 13 | #[embassy_executor::task] | 18 | #[embassy_executor::task] |
| 14 | async fn main_task() { | 19 | async fn main_task() { |
| 15 | let p = embassy_stm32::init(Default::default()); | 20 | let p = embassy_stm32::init(Default::default()); |
| 16 | 21 | ||
| 17 | let config = Config::default(); | 22 | let config = Config::default(); |
| 18 | let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, NoDma, NoDma, config); | 23 | let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, NoDma, NoDma, config); |
| 19 | 24 | ||
| 20 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); | 25 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); |
| 21 | info!("wrote Hello, starting echo"); | 26 | info!("wrote Hello, starting echo"); |
diff --git a/examples/stm32h7/src/bin/usart_dma.rs b/examples/stm32h7/src/bin/usart_dma.rs index 3adffcbeb..f1fe7fce6 100644 --- a/examples/stm32h7/src/bin/usart_dma.rs +++ b/examples/stm32h7/src/bin/usart_dma.rs | |||
| @@ -9,16 +9,21 @@ use defmt::*; | |||
| 9 | use embassy_executor::Executor; | 9 | use embassy_executor::Executor; |
| 10 | use embassy_stm32::dma::NoDma; | 10 | use embassy_stm32::dma::NoDma; |
| 11 | use embassy_stm32::usart::{Config, Uart}; | 11 | use embassy_stm32::usart::{Config, Uart}; |
| 12 | use embassy_stm32::{bind_interrupts, peripherals, usart}; | ||
| 12 | use heapless::String; | 13 | use heapless::String; |
| 13 | use static_cell::StaticCell; | 14 | use static_cell::StaticCell; |
| 14 | use {defmt_rtt as _, panic_probe as _}; | 15 | use {defmt_rtt as _, panic_probe as _}; |
| 15 | 16 | ||
| 17 | bind_interrupts!(struct Irqs { | ||
| 18 | UART7 => usart::InterruptHandler<peripherals::UART7>; | ||
| 19 | }); | ||
| 20 | |||
| 16 | #[embassy_executor::task] | 21 | #[embassy_executor::task] |
| 17 | async fn main_task() { | 22 | async fn main_task() { |
| 18 | let p = embassy_stm32::init(Default::default()); | 23 | let p = embassy_stm32::init(Default::default()); |
| 19 | 24 | ||
| 20 | let config = Config::default(); | 25 | let config = Config::default(); |
| 21 | let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, p.DMA1_CH0, NoDma, config); | 26 | let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.DMA1_CH0, NoDma, config); |
| 22 | 27 | ||
| 23 | for n in 0u32.. { | 28 | for n in 0u32.. { |
| 24 | let mut s: String<128> = String::new(); | 29 | let mut s: String<128> = String::new(); |
diff --git a/examples/stm32h7/src/bin/usart_split.rs b/examples/stm32h7/src/bin/usart_split.rs index df2b600f8..330d1ce09 100644 --- a/examples/stm32h7/src/bin/usart_split.rs +++ b/examples/stm32h7/src/bin/usart_split.rs | |||
| @@ -7,10 +7,15 @@ use embassy_executor::Spawner; | |||
| 7 | use embassy_stm32::dma::NoDma; | 7 | use embassy_stm32::dma::NoDma; |
| 8 | use embassy_stm32::peripherals::{DMA1_CH1, UART7}; | 8 | use embassy_stm32::peripherals::{DMA1_CH1, UART7}; |
| 9 | use embassy_stm32::usart::{Config, Uart, UartRx}; | 9 | use embassy_stm32::usart::{Config, Uart, UartRx}; |
| 10 | use embassy_stm32::{bind_interrupts, peripherals, usart}; | ||
| 10 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | 11 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; |
| 11 | use embassy_sync::channel::Channel; | 12 | use embassy_sync::channel::Channel; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 14 | ||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | UART7 => usart::InterruptHandler<peripherals::UART7>; | ||
| 17 | }); | ||
| 18 | |||
| 14 | #[embassy_executor::task] | 19 | #[embassy_executor::task] |
| 15 | async fn writer(mut usart: Uart<'static, UART7, NoDma, NoDma>) { | 20 | async fn writer(mut usart: Uart<'static, UART7, NoDma, NoDma>) { |
| 16 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); | 21 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); |
| @@ -31,7 +36,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 31 | info!("Hello World!"); | 36 | info!("Hello World!"); |
| 32 | 37 | ||
| 33 | let config = Config::default(); | 38 | let config = Config::default(); |
| 34 | let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, p.DMA1_CH0, p.DMA1_CH1, config); | 39 | let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.DMA1_CH0, p.DMA1_CH1, config); |
| 35 | unwrap!(usart.blocking_write(b"Type 8 chars to echo!\r\n")); | 40 | unwrap!(usart.blocking_write(b"Type 8 chars to echo!\r\n")); |
| 36 | 41 | ||
| 37 | let (mut tx, rx) = usart.split(); | 42 | let (mut tx, rx) = usart.split(); |
diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs new file mode 100644 index 000000000..97291f60c --- /dev/null +++ b/examples/stm32h7/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,110 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{panic, *}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::time::mhz; | ||
| 8 | use embassy_stm32::usb_otg::{Driver, Instance}; | ||
| 9 | use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; | ||
| 10 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 11 | use embassy_usb::driver::EndpointError; | ||
| 12 | use embassy_usb::Builder; | ||
| 13 | use futures::future::join; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | bind_interrupts!(struct Irqs { | ||
| 17 | OTG_FS => usb_otg::InterruptHandler<peripherals::USB_OTG_FS>; | ||
| 18 | }); | ||
| 19 | |||
| 20 | #[embassy_executor::main] | ||
| 21 | async fn main(_spawner: Spawner) { | ||
| 22 | info!("Hello World!"); | ||
| 23 | |||
| 24 | let mut config = Config::default(); | ||
| 25 | config.rcc.sys_ck = Some(mhz(400)); | ||
| 26 | config.rcc.hclk = Some(mhz(200)); | ||
| 27 | config.rcc.pll1.q_ck = Some(mhz(100)); | ||
| 28 | let p = embassy_stm32::init(config); | ||
| 29 | |||
| 30 | // Create the driver, from the HAL. | ||
| 31 | let mut ep_out_buffer = [0u8; 256]; | ||
| 32 | let mut config = embassy_stm32::usb_otg::Config::default(); | ||
| 33 | config.vbus_detection = true; | ||
| 34 | let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); | ||
| 35 | |||
| 36 | // Create embassy-usb Config | ||
| 37 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 38 | config.manufacturer = Some("Embassy"); | ||
| 39 | config.product = Some("USB-serial example"); | ||
| 40 | config.serial_number = Some("12345678"); | ||
| 41 | |||
| 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. | ||
| 50 | // It needs some buffers for building the descriptors. | ||
| 51 | let mut device_descriptor = [0; 256]; | ||
| 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 device_descriptor, | ||
| 62 | &mut config_descriptor, | ||
| 63 | &mut bos_descriptor, | ||
| 64 | &mut control_buf, | ||
| 65 | ); | ||
| 66 | |||
| 67 | // Create classes on the builder. | ||
| 68 | let mut 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 | // Do stuff with the class! | ||
| 77 | let echo_fut = async { | ||
| 78 | loop { | ||
| 79 | class.wait_connection().await; | ||
| 80 | info!("Connected"); | ||
| 81 | let _ = echo(&mut class).await; | ||
| 82 | info!("Disconnected"); | ||
| 83 | } | ||
| 84 | }; | ||
| 85 | |||
| 86 | // Run everything concurrently. | ||
| 87 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 88 | join(usb_fut, echo_fut).await; | ||
| 89 | } | ||
| 90 | |||
| 91 | struct Disconnected {} | ||
| 92 | |||
| 93 | impl From<EndpointError> for Disconnected { | ||
| 94 | fn from(val: EndpointError) -> Self { | ||
| 95 | match val { | ||
| 96 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 97 | EndpointError::Disabled => Disconnected {}, | ||
| 98 | } | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 103 | let mut buf = [0; 64]; | ||
| 104 | loop { | ||
| 105 | let n = class.read_packet(&mut buf).await?; | ||
| 106 | let data = &buf[..n]; | ||
| 107 | info!("data: {:x}", data); | ||
| 108 | class.write_packet(data).await?; | ||
| 109 | } | ||
| 110 | } | ||
diff --git a/examples/stm32h7/src/bin/wdg.rs b/examples/stm32h7/src/bin/wdg.rs new file mode 100644 index 000000000..9181dfd67 --- /dev/null +++ b/examples/stm32h7/src/bin/wdg.rs | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::wdg::IndependentWatchdog; | ||
| 8 | use embassy_time::{Duration, Timer}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_stm32::init(Default::default()); | ||
| 14 | info!("Hello World!"); | ||
| 15 | |||
| 16 | let mut wdg = IndependentWatchdog::new(p.IWDG1, 20_000_000); | ||
| 17 | |||
| 18 | wdg.unleash(); | ||
| 19 | |||
| 20 | loop { | ||
| 21 | Timer::after(Duration::from_secs(1)).await; | ||
| 22 | wdg.pet(); | ||
| 23 | } | ||
| 24 | } | ||
diff --git a/examples/stm32l0/.cargo/config.toml b/examples/stm32l0/.cargo/config.toml index a81a48f97..b050334b2 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-run --list-chips` | 2 | # replace your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32L053R8Tx" | 3 | runner = "probe-rs run --chip STM32L053R8Tx" |
| 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 11751a21d..747cec7bf 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml | |||
| @@ -2,31 +2,36 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-stm32l0-examples" | 3 | name = "embassy-stm32l0-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [features] | 7 | [features] |
| 7 | default = ["nightly"] | 8 | default = ["nightly"] |
| 8 | nightly = ["embassy-stm32/nightly", "embassy-lora", "lorawan-device", "lorawan", "embedded-io/async"] | 9 | nightly = ["embassy-stm32/nightly", "embassy-time/nightly", "embassy-time/unstable-traits", "embassy-executor/nightly", |
| 10 | "embassy-lora", "lora-phy", "lorawan-device", "lorawan", "embedded-io/async"] | ||
| 9 | 11 | ||
| 10 | [dependencies] | 12 | [dependencies] |
| 11 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 13 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 12 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 14 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 13 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-32768hz"] } | 15 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 14 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } | 16 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } |
| 15 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} | 17 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true } |
| 16 | 18 | lora-phy = { version = "1", optional = true } | |
| 17 | lorawan-device = { version = "0.7.1", default-features = false, features = ["async"], optional = true } | 19 | lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } |
| 18 | lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } | 20 | lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } |
| 19 | 21 | ||
| 20 | defmt = "0.3" | 22 | defmt = "0.3" |
| 21 | defmt-rtt = "0.3" | 23 | defmt-rtt = "0.4" |
| 22 | 24 | ||
| 23 | embedded-storage = "0.3.0" | 25 | embedded-storage = "0.3.0" |
| 24 | embedded-io = "0.3.0" | 26 | embedded-io = "0.4.0" |
| 25 | 27 | ||
| 26 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 28 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 27 | cortex-m-rt = "0.7.0" | 29 | cortex-m-rt = "0.7.0" |
| 28 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 30 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 29 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 31 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } |
| 30 | heapless = { version = "0.7.5", default-features = false } | 32 | heapless = { version = "0.7.5", default-features = false } |
| 31 | embedded-hal = "0.2.6" | 33 | embedded-hal = "0.2.6" |
| 32 | static_cell = "1.0" | 34 | static_cell = "1.1" |
| 35 | |||
| 36 | [patch.crates-io] | ||
| 37 | lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } | ||
diff --git a/examples/stm32l0/src/bin/flash.rs b/examples/stm32l0/src/bin/flash.rs index 867cb4d3e..86f6c70b9 100644 --- a/examples/stm32l0/src/bin/flash.rs +++ b/examples/stm32l0/src/bin/flash.rs | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | use defmt::{info, unwrap}; | 5 | use defmt::{info, unwrap}; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::flash::Flash; | 7 | use embassy_stm32::flash::Flash; |
| 8 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 9 | ||
| 11 | #[embassy_executor::main] | 10 | #[embassy_executor::main] |
| @@ -15,27 +14,27 @@ async fn main(_spawner: Spawner) { | |||
| 15 | 14 | ||
| 16 | const ADDR: u32 = 0x26000; | 15 | const ADDR: u32 = 0x26000; |
| 17 | 16 | ||
| 18 | let mut f = Flash::unlock(p.FLASH); | 17 | let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region; |
| 19 | 18 | ||
| 20 | info!("Reading..."); | 19 | info!("Reading..."); |
| 21 | let mut buf = [0u8; 8]; | 20 | let mut buf = [0u8; 8]; |
| 22 | unwrap!(f.read(ADDR, &mut buf)); | 21 | unwrap!(f.blocking_read(ADDR, &mut buf)); |
| 23 | info!("Read: {=[u8]:x}", buf); | 22 | info!("Read: {=[u8]:x}", buf); |
| 24 | 23 | ||
| 25 | info!("Erasing..."); | 24 | info!("Erasing..."); |
| 26 | unwrap!(f.erase(ADDR, ADDR + 128)); | 25 | unwrap!(f.blocking_erase(ADDR, ADDR + 128)); |
| 27 | 26 | ||
| 28 | info!("Reading..."); | 27 | info!("Reading..."); |
| 29 | let mut buf = [0u8; 8]; | 28 | let mut buf = [0u8; 8]; |
| 30 | unwrap!(f.read(ADDR, &mut buf)); | 29 | unwrap!(f.blocking_read(ADDR, &mut buf)); |
| 31 | info!("Read after erase: {=[u8]:x}", buf); | 30 | info!("Read after erase: {=[u8]:x}", buf); |
| 32 | 31 | ||
| 33 | info!("Writing..."); | 32 | info!("Writing..."); |
| 34 | unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); | 33 | unwrap!(f.blocking_write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); |
| 35 | 34 | ||
| 36 | info!("Reading..."); | 35 | info!("Reading..."); |
| 37 | let mut buf = [0u8; 8]; | 36 | let mut buf = [0u8; 8]; |
| 38 | unwrap!(f.read(ADDR, &mut buf)); | 37 | unwrap!(f.blocking_read(ADDR, &mut buf)); |
| 39 | info!("Read: {=[u8]:x}", buf); | 38 | info!("Read: {=[u8]:x}", buf); |
| 40 | assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); | 39 | assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); |
| 41 | } | 40 | } |
diff --git a/examples/stm32l0/src/bin/lora_cad.rs b/examples/stm32l0/src/bin/lora_cad.rs new file mode 100644 index 000000000..588cea1e5 --- /dev/null +++ b/examples/stm32l0/src/bin/lora_cad.rs | |||
| @@ -0,0 +1,105 @@ | |||
| 1 | //! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio. | ||
| 2 | //! It demonstrates LORA P2P CAD functionality. | ||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | #![macro_use] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_lora::iv::Stm32l0InterfaceVariant; | ||
| 11 | use embassy_stm32::exti::{Channel, ExtiInput}; | ||
| 12 | use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed}; | ||
| 13 | use embassy_stm32::spi; | ||
| 14 | use embassy_stm32::time::khz; | ||
| 15 | use embassy_time::{Delay, Duration, Timer}; | ||
| 16 | use lora_phy::mod_params::*; | ||
| 17 | use lora_phy::sx1276_7_8_9::SX1276_7_8_9; | ||
| 18 | use lora_phy::LoRa; | ||
| 19 | use {defmt_rtt as _, panic_probe as _}; | ||
| 20 | |||
| 21 | const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||
| 22 | |||
| 23 | #[embassy_executor::main] | ||
| 24 | async fn main(_spawner: Spawner) { | ||
| 25 | let mut config = embassy_stm32::Config::default(); | ||
| 26 | config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16; | ||
| 27 | config.rcc.enable_hsi48 = true; | ||
| 28 | let p = embassy_stm32::init(config); | ||
| 29 | |||
| 30 | // SPI for sx1276 | ||
| 31 | let spi = spi::Spi::new( | ||
| 32 | p.SPI1, | ||
| 33 | p.PB3, | ||
| 34 | p.PA7, | ||
| 35 | p.PA6, | ||
| 36 | p.DMA1_CH3, | ||
| 37 | p.DMA1_CH2, | ||
| 38 | khz(200), | ||
| 39 | spi::Config::default(), | ||
| 40 | ); | ||
| 41 | |||
| 42 | let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); | ||
| 43 | let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); | ||
| 44 | |||
| 45 | let irq_pin = Input::new(p.PB4.degrade(), Pull::Up); | ||
| 46 | let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade()); | ||
| 47 | |||
| 48 | let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap(); | ||
| 49 | |||
| 50 | let mut delay = Delay; | ||
| 51 | |||
| 52 | let mut lora = { | ||
| 53 | match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, &mut delay).await { | ||
| 54 | Ok(l) => l, | ||
| 55 | Err(err) => { | ||
| 56 | info!("Radio error = {}", err); | ||
| 57 | return; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | }; | ||
| 61 | |||
| 62 | let mut debug_indicator = Output::new(p.PB5, Level::Low, Speed::Low); | ||
| 63 | let mut start_indicator = Output::new(p.PB6, Level::Low, Speed::Low); | ||
| 64 | |||
| 65 | start_indicator.set_high(); | ||
| 66 | Timer::after(Duration::from_secs(5)).await; | ||
| 67 | start_indicator.set_low(); | ||
| 68 | |||
| 69 | let mdltn_params = { | ||
| 70 | match lora.create_modulation_params( | ||
| 71 | SpreadingFactor::_10, | ||
| 72 | Bandwidth::_250KHz, | ||
| 73 | CodingRate::_4_8, | ||
| 74 | LORA_FREQUENCY_IN_HZ, | ||
| 75 | ) { | ||
| 76 | Ok(mp) => mp, | ||
| 77 | Err(err) => { | ||
| 78 | info!("Radio error = {}", err); | ||
| 79 | return; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | }; | ||
| 83 | |||
| 84 | match lora.prepare_for_cad(&mdltn_params, true).await { | ||
| 85 | Ok(()) => {} | ||
| 86 | Err(err) => { | ||
| 87 | info!("Radio error = {}", err); | ||
| 88 | return; | ||
| 89 | } | ||
| 90 | }; | ||
| 91 | |||
| 92 | match lora.cad().await { | ||
| 93 | Ok(cad_activity_detected) => { | ||
| 94 | if cad_activity_detected { | ||
| 95 | info!("cad successful with activity detected") | ||
| 96 | } else { | ||
| 97 | info!("cad successful without activity detected") | ||
| 98 | } | ||
| 99 | debug_indicator.set_high(); | ||
| 100 | Timer::after(Duration::from_secs(5)).await; | ||
| 101 | debug_indicator.set_low(); | ||
| 102 | } | ||
| 103 | Err(err) => info!("cad unsuccessful = {}", err), | ||
| 104 | } | ||
| 105 | } | ||
diff --git a/examples/stm32l0/src/bin/lora_lorawan.rs b/examples/stm32l0/src/bin/lora_lorawan.rs new file mode 100644 index 000000000..c397edd58 --- /dev/null +++ b/examples/stm32l0/src/bin/lora_lorawan.rs | |||
| @@ -0,0 +1,88 @@ | |||
| 1 | //! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio. | ||
| 2 | //! It demonstrates LoRaWAN join functionality. | ||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | #![macro_use] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_lora::iv::Stm32l0InterfaceVariant; | ||
| 11 | use embassy_lora::LoraTimer; | ||
| 12 | use embassy_stm32::exti::{Channel, ExtiInput}; | ||
| 13 | use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed}; | ||
| 14 | use embassy_stm32::rng::Rng; | ||
| 15 | use embassy_stm32::spi; | ||
| 16 | use embassy_stm32::time::khz; | ||
| 17 | use embassy_time::Delay; | ||
| 18 | use lora_phy::mod_params::*; | ||
| 19 | use lora_phy::sx1276_7_8_9::SX1276_7_8_9; | ||
| 20 | use lora_phy::LoRa; | ||
| 21 | use lorawan::default_crypto::DefaultFactory as Crypto; | ||
| 22 | use lorawan_device::async_device::lora_radio::LoRaRadio; | ||
| 23 | use lorawan_device::async_device::{region, Device, JoinMode}; | ||
| 24 | use {defmt_rtt as _, panic_probe as _}; | ||
| 25 | |||
| 26 | const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region | ||
| 27 | |||
| 28 | #[embassy_executor::main] | ||
| 29 | async fn main(_spawner: Spawner) { | ||
| 30 | let mut config = embassy_stm32::Config::default(); | ||
| 31 | config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16; | ||
| 32 | config.rcc.enable_hsi48 = true; | ||
| 33 | let p = embassy_stm32::init(config); | ||
| 34 | |||
| 35 | // SPI for sx1276 | ||
| 36 | let spi = spi::Spi::new( | ||
| 37 | p.SPI1, | ||
| 38 | p.PB3, | ||
| 39 | p.PA7, | ||
| 40 | p.PA6, | ||
| 41 | p.DMA1_CH3, | ||
| 42 | p.DMA1_CH2, | ||
| 43 | khz(200), | ||
| 44 | spi::Config::default(), | ||
| 45 | ); | ||
| 46 | |||
| 47 | let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); | ||
| 48 | let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); | ||
| 49 | |||
| 50 | let irq_pin = Input::new(p.PB4.degrade(), Pull::Up); | ||
| 51 | let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade()); | ||
| 52 | |||
| 53 | let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap(); | ||
| 54 | |||
| 55 | let mut delay = Delay; | ||
| 56 | |||
| 57 | let lora = { | ||
| 58 | match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), true, &mut delay).await { | ||
| 59 | Ok(l) => l, | ||
| 60 | Err(err) => { | ||
| 61 | info!("Radio error = {}", err); | ||
| 62 | return; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | }; | ||
| 66 | |||
| 67 | let radio = LoRaRadio::new(lora); | ||
| 68 | let region: region::Configuration = region::Configuration::new(LORAWAN_REGION); | ||
| 69 | let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); | ||
| 70 | |||
| 71 | defmt::info!("Joining LoRaWAN network"); | ||
| 72 | |||
| 73 | // TODO: Adjust the EUI and Keys according to your network credentials | ||
| 74 | match device | ||
| 75 | .join(&JoinMode::OTAA { | ||
| 76 | deveui: [0, 0, 0, 0, 0, 0, 0, 0], | ||
| 77 | appeui: [0, 0, 0, 0, 0, 0, 0, 0], | ||
| 78 | appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], | ||
| 79 | }) | ||
| 80 | .await | ||
| 81 | { | ||
| 82 | Ok(()) => defmt::info!("LoRaWAN network joined"), | ||
| 83 | Err(err) => { | ||
| 84 | info!("Radio error = {}", err); | ||
| 85 | return; | ||
| 86 | } | ||
| 87 | }; | ||
| 88 | } | ||
diff --git a/examples/stm32l0/src/bin/lora_p2p_receive.rs b/examples/stm32l0/src/bin/lora_p2p_receive.rs new file mode 100644 index 000000000..bb7509509 --- /dev/null +++ b/examples/stm32l0/src/bin/lora_p2p_receive.rs | |||
| @@ -0,0 +1,127 @@ | |||
| 1 | //! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio. | ||
| 2 | //! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example. | ||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | #![macro_use] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_lora::iv::Stm32l0InterfaceVariant; | ||
| 11 | use embassy_stm32::exti::{Channel, ExtiInput}; | ||
| 12 | use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed}; | ||
| 13 | use embassy_stm32::spi; | ||
| 14 | use embassy_stm32::time::khz; | ||
| 15 | use embassy_time::{Delay, Duration, Timer}; | ||
| 16 | use lora_phy::mod_params::*; | ||
| 17 | use lora_phy::sx1276_7_8_9::SX1276_7_8_9; | ||
| 18 | use lora_phy::LoRa; | ||
| 19 | use {defmt_rtt as _, panic_probe as _}; | ||
| 20 | |||
| 21 | const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||
| 22 | |||
| 23 | #[embassy_executor::main] | ||
| 24 | async fn main(_spawner: Spawner) { | ||
| 25 | let mut config = embassy_stm32::Config::default(); | ||
| 26 | config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16; | ||
| 27 | config.rcc.enable_hsi48 = true; | ||
| 28 | let p = embassy_stm32::init(config); | ||
| 29 | |||
| 30 | // SPI for sx1276 | ||
| 31 | let spi = spi::Spi::new( | ||
| 32 | p.SPI1, | ||
| 33 | p.PB3, | ||
| 34 | p.PA7, | ||
| 35 | p.PA6, | ||
| 36 | p.DMA1_CH3, | ||
| 37 | p.DMA1_CH2, | ||
| 38 | khz(200), | ||
| 39 | spi::Config::default(), | ||
| 40 | ); | ||
| 41 | |||
| 42 | let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); | ||
| 43 | let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); | ||
| 44 | |||
| 45 | let irq_pin = Input::new(p.PB4.degrade(), Pull::Up); | ||
| 46 | let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade()); | ||
| 47 | |||
| 48 | let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap(); | ||
| 49 | |||
| 50 | let mut delay = Delay; | ||
| 51 | |||
| 52 | let mut lora = { | ||
| 53 | match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, &mut delay).await { | ||
| 54 | Ok(l) => l, | ||
| 55 | Err(err) => { | ||
| 56 | info!("Radio error = {}", err); | ||
| 57 | return; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | }; | ||
| 61 | |||
| 62 | let mut debug_indicator = Output::new(p.PB5, Level::Low, Speed::Low); | ||
| 63 | let mut start_indicator = Output::new(p.PB6, Level::Low, Speed::Low); | ||
| 64 | |||
| 65 | start_indicator.set_high(); | ||
| 66 | Timer::after(Duration::from_secs(5)).await; | ||
| 67 | start_indicator.set_low(); | ||
| 68 | |||
| 69 | let mut receiving_buffer = [00u8; 100]; | ||
| 70 | |||
| 71 | let mdltn_params = { | ||
| 72 | match lora.create_modulation_params( | ||
| 73 | SpreadingFactor::_10, | ||
| 74 | Bandwidth::_250KHz, | ||
| 75 | CodingRate::_4_8, | ||
| 76 | LORA_FREQUENCY_IN_HZ, | ||
| 77 | ) { | ||
| 78 | Ok(mp) => mp, | ||
| 79 | Err(err) => { | ||
| 80 | info!("Radio error = {}", err); | ||
| 81 | return; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | }; | ||
| 85 | |||
| 86 | let rx_pkt_params = { | ||
| 87 | match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) { | ||
| 88 | Ok(pp) => pp, | ||
| 89 | Err(err) => { | ||
| 90 | info!("Radio error = {}", err); | ||
| 91 | return; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | }; | ||
| 95 | |||
| 96 | match lora | ||
| 97 | .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, true, false, 0, 0x00ffffffu32) | ||
| 98 | .await | ||
| 99 | { | ||
| 100 | Ok(()) => {} | ||
| 101 | Err(err) => { | ||
| 102 | info!("Radio error = {}", err); | ||
| 103 | return; | ||
| 104 | } | ||
| 105 | }; | ||
| 106 | |||
| 107 | loop { | ||
| 108 | receiving_buffer = [00u8; 100]; | ||
| 109 | match lora.rx(&rx_pkt_params, &mut receiving_buffer).await { | ||
| 110 | Ok((received_len, _rx_pkt_status)) => { | ||
| 111 | if (received_len == 3) | ||
| 112 | && (receiving_buffer[0] == 0x01u8) | ||
| 113 | && (receiving_buffer[1] == 0x02u8) | ||
| 114 | && (receiving_buffer[2] == 0x03u8) | ||
| 115 | { | ||
| 116 | info!("rx successful"); | ||
| 117 | debug_indicator.set_high(); | ||
| 118 | Timer::after(Duration::from_secs(5)).await; | ||
| 119 | debug_indicator.set_low(); | ||
| 120 | } else { | ||
| 121 | info!("rx unknown packet"); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | Err(err) => info!("rx unsuccessful = {}", err), | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
diff --git a/examples/stm32l0/src/bin/lora_p2p_send.rs b/examples/stm32l0/src/bin/lora_p2p_send.rs new file mode 100644 index 000000000..e6fadc01d --- /dev/null +++ b/examples/stm32l0/src/bin/lora_p2p_send.rs | |||
| @@ -0,0 +1,110 @@ | |||
| 1 | //! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio. | ||
| 2 | //! It demonstrates LORA P2P send functionality. | ||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | #![macro_use] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_lora::iv::Stm32l0InterfaceVariant; | ||
| 11 | use embassy_stm32::exti::{Channel, ExtiInput}; | ||
| 12 | use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed}; | ||
| 13 | use embassy_stm32::spi; | ||
| 14 | use embassy_stm32::time::khz; | ||
| 15 | use embassy_time::Delay; | ||
| 16 | use lora_phy::mod_params::*; | ||
| 17 | use lora_phy::sx1276_7_8_9::SX1276_7_8_9; | ||
| 18 | use lora_phy::LoRa; | ||
| 19 | use {defmt_rtt as _, panic_probe as _}; | ||
| 20 | |||
| 21 | const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||
| 22 | |||
| 23 | #[embassy_executor::main] | ||
| 24 | async fn main(_spawner: Spawner) { | ||
| 25 | let mut config = embassy_stm32::Config::default(); | ||
| 26 | config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16; | ||
| 27 | config.rcc.enable_hsi48 = true; | ||
| 28 | let p = embassy_stm32::init(config); | ||
| 29 | |||
| 30 | // SPI for sx1276 | ||
| 31 | let spi = spi::Spi::new( | ||
| 32 | p.SPI1, | ||
| 33 | p.PB3, | ||
| 34 | p.PA7, | ||
| 35 | p.PA6, | ||
| 36 | p.DMA1_CH3, | ||
| 37 | p.DMA1_CH2, | ||
| 38 | khz(200), | ||
| 39 | spi::Config::default(), | ||
| 40 | ); | ||
| 41 | |||
| 42 | let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); | ||
| 43 | let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); | ||
| 44 | |||
| 45 | let irq_pin = Input::new(p.PB4.degrade(), Pull::Up); | ||
| 46 | let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade()); | ||
| 47 | |||
| 48 | let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap(); | ||
| 49 | |||
| 50 | let mut delay = Delay; | ||
| 51 | |||
| 52 | let mut lora = { | ||
| 53 | match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, &mut delay).await { | ||
| 54 | Ok(l) => l, | ||
| 55 | Err(err) => { | ||
| 56 | info!("Radio error = {}", err); | ||
| 57 | return; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | }; | ||
| 61 | |||
| 62 | let mdltn_params = { | ||
| 63 | match lora.create_modulation_params( | ||
| 64 | SpreadingFactor::_10, | ||
| 65 | Bandwidth::_250KHz, | ||
| 66 | CodingRate::_4_8, | ||
| 67 | LORA_FREQUENCY_IN_HZ, | ||
| 68 | ) { | ||
| 69 | Ok(mp) => mp, | ||
| 70 | Err(err) => { | ||
| 71 | info!("Radio error = {}", err); | ||
| 72 | return; | ||
| 73 | } | ||
| 74 | } | ||
| 75 | }; | ||
| 76 | |||
| 77 | let mut tx_pkt_params = { | ||
| 78 | match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) { | ||
| 79 | Ok(pp) => pp, | ||
| 80 | Err(err) => { | ||
| 81 | info!("Radio error = {}", err); | ||
| 82 | return; | ||
| 83 | } | ||
| 84 | } | ||
| 85 | }; | ||
| 86 | |||
| 87 | match lora.prepare_for_tx(&mdltn_params, 17, true).await { | ||
| 88 | Ok(()) => {} | ||
| 89 | Err(err) => { | ||
| 90 | info!("Radio error = {}", err); | ||
| 91 | return; | ||
| 92 | } | ||
| 93 | }; | ||
| 94 | |||
| 95 | let buffer = [0x01u8, 0x02u8, 0x03u8]; | ||
| 96 | match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await { | ||
| 97 | Ok(()) => { | ||
| 98 | info!("TX DONE"); | ||
| 99 | } | ||
| 100 | Err(err) => { | ||
| 101 | info!("Radio error = {}", err); | ||
| 102 | return; | ||
| 103 | } | ||
| 104 | }; | ||
| 105 | |||
| 106 | match lora.sleep(&mut delay).await { | ||
| 107 | Ok(()) => info!("Sleep successful"), | ||
| 108 | Err(err) => info!("Sleep unsuccessful = {}", err), | ||
| 109 | } | ||
| 110 | } | ||
diff --git a/examples/stm32l0/src/bin/lorawan.rs b/examples/stm32l0/src/bin/lorawan.rs deleted file mode 100644 index 303558b96..000000000 --- a/examples/stm32l0/src/bin/lorawan.rs +++ /dev/null | |||
| @@ -1,75 +0,0 @@ | |||
| 1 | //! This example runs on the STM32 LoRa Discovery board which has a builtin Semtech Sx127x radio | ||
| 2 | #![no_std] | ||
| 3 | #![no_main] | ||
| 4 | #![macro_use] | ||
| 5 | #![allow(dead_code)] | ||
| 6 | #![feature(generic_associated_types)] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_lora::sx127x::*; | ||
| 11 | use embassy_lora::LoraTimer; | ||
| 12 | use embassy_stm32::exti::ExtiInput; | ||
| 13 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; | ||
| 14 | use embassy_stm32::rng::Rng; | ||
| 15 | use embassy_stm32::spi; | ||
| 16 | use embassy_stm32::time::khz; | ||
| 17 | use lorawan::default_crypto::DefaultFactory as Crypto; | ||
| 18 | use lorawan_device::async_device::{region, Device, JoinMode}; | ||
| 19 | use {defmt_rtt as _, panic_probe as _}; | ||
| 20 | |||
| 21 | #[embassy_executor::main] | ||
| 22 | async fn main(_spawner: Spawner) { | ||
| 23 | let mut config = embassy_stm32::Config::default(); | ||
| 24 | config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16; | ||
| 25 | config.rcc.enable_hsi48 = true; | ||
| 26 | let p = embassy_stm32::init(config); | ||
| 27 | |||
| 28 | // SPI for sx127x | ||
| 29 | let spi = spi::Spi::new( | ||
| 30 | p.SPI1, | ||
| 31 | p.PB3, | ||
| 32 | p.PA7, | ||
| 33 | p.PA6, | ||
| 34 | p.DMA1_CH3, | ||
| 35 | p.DMA1_CH2, | ||
| 36 | khz(200), | ||
| 37 | spi::Config::default(), | ||
| 38 | ); | ||
| 39 | |||
| 40 | let cs = Output::new(p.PA15, Level::High, Speed::Low); | ||
| 41 | let reset = Output::new(p.PC0, Level::High, Speed::Low); | ||
| 42 | let _ = Input::new(p.PB1, Pull::None); | ||
| 43 | |||
| 44 | let ready = Input::new(p.PB4, Pull::Up); | ||
| 45 | let ready_pin = ExtiInput::new(ready, p.EXTI4); | ||
| 46 | |||
| 47 | let radio = Sx127xRadio::new(spi, cs, reset, ready_pin, DummySwitch).await.unwrap(); | ||
| 48 | |||
| 49 | let region = region::EU868::default().into(); | ||
| 50 | let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer, Rng::new(p.RNG)); | ||
| 51 | |||
| 52 | defmt::info!("Joining LoRaWAN network"); | ||
| 53 | |||
| 54 | // TODO: Adjust the EUI and Keys according to your network credentials | ||
| 55 | device | ||
| 56 | .join(&JoinMode::OTAA { | ||
| 57 | deveui: [0, 0, 0, 0, 0, 0, 0, 0], | ||
| 58 | appeui: [0, 0, 0, 0, 0, 0, 0, 0], | ||
| 59 | appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], | ||
| 60 | }) | ||
| 61 | .await | ||
| 62 | .ok() | ||
| 63 | .unwrap(); | ||
| 64 | defmt::info!("LoRaWAN network joined"); | ||
| 65 | |||
| 66 | defmt::info!("Sending 'PING'"); | ||
| 67 | device.send(b"PING", 1, false).await.ok().unwrap(); | ||
| 68 | defmt::info!("Message sent!"); | ||
| 69 | } | ||
| 70 | |||
| 71 | pub struct DummySwitch; | ||
| 72 | impl RadioSwitch for DummySwitch { | ||
| 73 | fn set_rx(&mut self) {} | ||
| 74 | fn set_tx(&mut self) {} | ||
| 75 | } | ||
diff --git a/examples/stm32l0/src/bin/usart_dma.rs b/examples/stm32l0/src/bin/usart_dma.rs index 66657d0f0..eae8f3452 100644 --- a/examples/stm32l0/src/bin/usart_dma.rs +++ b/examples/stm32l0/src/bin/usart_dma.rs | |||
| @@ -5,12 +5,17 @@ | |||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::usart::{Config, Uart}; | 7 | use embassy_stm32::usart::{Config, Uart}; |
| 8 | use embassy_stm32::{bind_interrupts, peripherals, usart}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 11 | bind_interrupts!(struct Irqs { | ||
| 12 | USART1 => usart::InterruptHandler<peripherals::USART1>; | ||
| 13 | }); | ||
| 14 | |||
| 10 | #[embassy_executor::main] | 15 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 16 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_stm32::init(Default::default()); | 17 | let p = embassy_stm32::init(Default::default()); |
| 13 | let mut usart = Uart::new(p.USART1, p.PB7, p.PB6, p.DMA1_CH2, p.DMA1_CH3, Config::default()); | 18 | let mut usart = Uart::new(p.USART1, p.PB7, p.PB6, Irqs, p.DMA1_CH2, p.DMA1_CH3, Config::default()); |
| 14 | 19 | ||
| 15 | usart.write(b"Hello Embassy World!\r\n").await.unwrap(); | 20 | usart.write(b"Hello Embassy World!\r\n").await.unwrap(); |
| 16 | info!("wrote Hello, starting echo"); | 21 | info!("wrote Hello, starting echo"); |
diff --git a/examples/stm32l0/src/bin/usart_irq.rs b/examples/stm32l0/src/bin/usart_irq.rs index 0e2237388..f2c72a107 100644 --- a/examples/stm32l0/src/bin/usart_irq.rs +++ b/examples/stm32l0/src/bin/usart_irq.rs | |||
| @@ -4,12 +4,15 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::dma::NoDma; | 7 | use embassy_stm32::usart::{BufferedUart, Config}; |
| 8 | use embassy_stm32::interrupt; | 8 | use embassy_stm32::{bind_interrupts, peripherals, usart}; |
| 9 | use embassy_stm32::usart::{BufferedUart, Config, State, Uart}; | ||
| 10 | use embedded_io::asynch::{Read, Write}; | 9 | use embedded_io::asynch::{Read, Write}; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 11 | ||
| 12 | bind_interrupts!(struct Irqs { | ||
| 13 | USART2 => usart::BufferedInterruptHandler<peripherals::USART2>; | ||
| 14 | }); | ||
| 15 | |||
| 13 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 14 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 15 | let p = embassy_stm32::init(Default::default()); | 18 | let p = embassy_stm32::init(Default::default()); |
| @@ -21,17 +24,7 @@ async fn main(_spawner: Spawner) { | |||
| 21 | let mut config = Config::default(); | 24 | let mut config = Config::default(); |
| 22 | config.baudrate = 9600; | 25 | config.baudrate = 9600; |
| 23 | 26 | ||
| 24 | let usart = Uart::new(p.USART2, p.PA3, p.PA2, NoDma, NoDma, config); | 27 | let mut usart = unsafe { BufferedUart::new(p.USART2, Irqs, p.PA3, p.PA2, &mut TX_BUFFER, &mut RX_BUFFER, config) }; |
| 25 | let mut state = State::new(); | ||
| 26 | let mut usart = unsafe { | ||
| 27 | BufferedUart::new( | ||
| 28 | &mut state, | ||
| 29 | usart, | ||
| 30 | interrupt::take!(USART2), | ||
| 31 | &mut TX_BUFFER, | ||
| 32 | &mut RX_BUFFER, | ||
| 33 | ) | ||
| 34 | }; | ||
| 35 | 28 | ||
| 36 | usart.write_all(b"Hello Embassy World!\r\n").await.unwrap(); | 29 | usart.write_all(b"Hello Embassy World!\r\n").await.unwrap(); |
| 37 | info!("wrote Hello, starting echo"); | 30 | info!("wrote Hello, starting echo"); |
diff --git a/examples/stm32l1/.cargo/config.toml b/examples/stm32l1/.cargo/config.toml index 404b6b55c..9cabd14ba 100644 --- a/examples/stm32l1/.cargo/config.toml +++ b/examples/stm32l1/.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-run --list-chips` | 2 | # replace your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32L151CBxxA" | 3 | runner = "probe-rs run --chip STM32L151CBxxA" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv7m-none-eabi" | 6 | target = "thumbv7m-none-eabi" |
diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 18b35b305..dcca1cc3d 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml | |||
| @@ -2,17 +2,18 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-stm32l1-examples" | 3 | name = "embassy-stm32l1-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } |
| 11 | 12 | ||
| 12 | defmt = "0.3" | 13 | defmt = "0.3" |
| 13 | defmt-rtt = "0.3" | 14 | defmt-rtt = "0.4" |
| 14 | 15 | ||
| 15 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 16 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 16 | cortex-m-rt = "0.7.0" | 17 | cortex-m-rt = "0.7.0" |
| 17 | embedded-hal = "0.2.6" | 18 | embedded-hal = "0.2.6" |
| 18 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 19 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
diff --git a/examples/stm32l1/src/bin/flash.rs b/examples/stm32l1/src/bin/flash.rs index a76b9879f..aeb535cca 100644 --- a/examples/stm32l1/src/bin/flash.rs +++ b/examples/stm32l1/src/bin/flash.rs | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | use defmt::{info, unwrap}; | 5 | use defmt::{info, unwrap}; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::flash::Flash; | 7 | use embassy_stm32::flash::Flash; |
| 8 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 9 | ||
| 11 | #[embassy_executor::main] | 10 | #[embassy_executor::main] |
| @@ -15,27 +14,27 @@ async fn main(_spawner: Spawner) { | |||
| 15 | 14 | ||
| 16 | const ADDR: u32 = 0x26000; | 15 | const ADDR: u32 = 0x26000; |
| 17 | 16 | ||
| 18 | let mut f = Flash::unlock(p.FLASH); | 17 | let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region; |
| 19 | 18 | ||
| 20 | info!("Reading..."); | 19 | info!("Reading..."); |
| 21 | let mut buf = [0u8; 8]; | 20 | let mut buf = [0u8; 8]; |
| 22 | unwrap!(f.read(ADDR, &mut buf)); | 21 | unwrap!(f.blocking_read(ADDR, &mut buf)); |
| 23 | info!("Read: {=[u8]:x}", buf); | 22 | info!("Read: {=[u8]:x}", buf); |
| 24 | 23 | ||
| 25 | info!("Erasing..."); | 24 | info!("Erasing..."); |
| 26 | unwrap!(f.erase(ADDR, ADDR + 256)); | 25 | unwrap!(f.blocking_erase(ADDR, ADDR + 256)); |
| 27 | 26 | ||
| 28 | info!("Reading..."); | 27 | info!("Reading..."); |
| 29 | let mut buf = [0u8; 8]; | 28 | let mut buf = [0u8; 8]; |
| 30 | unwrap!(f.read(ADDR, &mut buf)); | 29 | unwrap!(f.blocking_read(ADDR, &mut buf)); |
| 31 | info!("Read after erase: {=[u8]:x}", buf); | 30 | info!("Read after erase: {=[u8]:x}", buf); |
| 32 | 31 | ||
| 33 | info!("Writing..."); | 32 | info!("Writing..."); |
| 34 | unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); | 33 | unwrap!(f.blocking_write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); |
| 35 | 34 | ||
| 36 | info!("Reading..."); | 35 | info!("Reading..."); |
| 37 | let mut buf = [0u8; 8]; | 36 | let mut buf = [0u8; 8]; |
| 38 | unwrap!(f.read(ADDR, &mut buf)); | 37 | unwrap!(f.blocking_read(ADDR, &mut buf)); |
| 39 | info!("Read: {=[u8]:x}", buf); | 38 | info!("Read: {=[u8]:x}", buf); |
| 40 | assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); | 39 | assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); |
| 41 | } | 40 | } |
diff --git a/examples/stm32l4/.cargo/config.toml b/examples/stm32l4/.cargo/config.toml index 5534053c5..36e74e5a5 100644 --- a/examples/stm32l4/.cargo/config.toml +++ b/examples/stm32l4/.cargo/config.toml | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] |
| 2 | # replace STM32F429ZITx with your chip as listed in `probe-run --list-chips` | 2 | # replace STM32F429ZITx with your chip as listed in `probe-rs chip list` |
| 3 | #runner = "probe-run --chip STM32L475VGT6" | 3 | #runner = "probe-rs run --chip STM32L475VGT6" |
| 4 | #runner = "probe-run --chip STM32L475VG" | 4 | #runner = "probe-rs run --chip STM32L475VG" |
| 5 | runner = "probe-run --chip STM32L4S5VI" | 5 | runner = "probe-rs run --chip STM32L4S5VI" |
| 6 | 6 | ||
| 7 | [build] | 7 | [build] |
| 8 | target = "thumbv7em-none-eabi" | 8 | target = "thumbv7em-none-eabi" |
diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index cb7238e4c..c55558518 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml | |||
| @@ -2,29 +2,27 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-stm32l4-examples" | 3 | name = "embassy-stm32l4-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | 5 | license = "MIT OR Apache-2.0" | |
| 6 | [features] | ||
| 7 | 6 | ||
| 8 | [dependencies] | 7 | [dependencies] |
| 9 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 10 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 11 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 12 | embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } | 11 | embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } |
| 13 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] } | 12 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits", "chrono"] } |
| 13 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 14 | 14 | ||
| 15 | defmt = "0.3" | 15 | defmt = "0.3" |
| 16 | defmt-rtt = "0.3" | 16 | defmt-rtt = "0.4" |
| 17 | 17 | ||
| 18 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 18 | cortex-m = { version = "0.7.6", features = ["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-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } | 21 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } |
| 22 | embedded-hal-async = { version = "0.1.0-alpha.1" } | 22 | embedded-hal-async = { version = "=0.2.0-alpha.2" } |
| 23 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 23 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 24 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 24 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } |
| 25 | heapless = { version = "0.7.5", default-features = false } | 25 | heapless = { version = "0.7.5", default-features = false } |
| 26 | chrono = { version = "^0.4", default-features = false } | ||
| 26 | 27 | ||
| 27 | micromath = "2.0.0" | 28 | micromath = "2.0.0" |
| 28 | usb-device = "0.2" | ||
| 29 | usbd-serial = "0.1.1" | ||
| 30 | |||
diff --git a/examples/stm32l4/src/bin/adc.rs b/examples/stm32l4/src/bin/adc.rs index 281346e5f..1771e5202 100644 --- a/examples/stm32l4/src/bin/adc.rs +++ b/examples/stm32l4/src/bin/adc.rs | |||
| @@ -12,12 +12,10 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 12 | fn main() -> ! { | 12 | fn main() -> ! { |
| 13 | info!("Hello World!"); | 13 | info!("Hello World!"); |
| 14 | 14 | ||
| 15 | unsafe { | 15 | pac::RCC.ccipr().modify(|w| { |
| 16 | pac::RCC.ccipr().modify(|w| { | 16 | w.set_adcsel(0b11); |
| 17 | w.set_adcsel(0b11); | 17 | }); |
| 18 | }); | 18 | pac::RCC.ahb2enr().modify(|w| w.set_adcen(true)); |
| 19 | pac::RCC.ahb2enr().modify(|w| w.set_adcen(true)); | ||
| 20 | } | ||
| 21 | 19 | ||
| 22 | let p = embassy_stm32::init(Default::default()); | 20 | let p = embassy_stm32::init(Default::default()); |
| 23 | 21 | ||
diff --git a/examples/stm32l4/src/bin/dac.rs b/examples/stm32l4/src/bin/dac.rs index d6e744aa6..ade43eb35 100644 --- a/examples/stm32l4/src/bin/dac.rs +++ b/examples/stm32l4/src/bin/dac.rs | |||
| @@ -3,28 +3,21 @@ | |||
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | 4 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_stm32::dac::{Channel, Dac, Value}; | 6 | use embassy_stm32::dac::{DacCh1, DacChannel, Value}; |
| 7 | use embassy_stm32::pac; | 7 | use embassy_stm32::dma::NoDma; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| 10 | #[cortex_m_rt::entry] | 10 | #[cortex_m_rt::entry] |
| 11 | fn main() -> ! { | 11 | fn main() -> ! { |
| 12 | info!("Hello World!"); | ||
| 13 | |||
| 14 | unsafe { | ||
| 15 | pac::RCC.apb1enr1().modify(|w| { | ||
| 16 | w.set_dac1en(true); | ||
| 17 | }); | ||
| 18 | } | ||
| 19 | |||
| 20 | let p = embassy_stm32::init(Default::default()); | 12 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | ||
| 21 | 14 | ||
| 22 | let mut dac = Dac::new_1ch(p.DAC1, p.PA4); | 15 | let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); |
| 23 | 16 | ||
| 24 | loop { | 17 | loop { |
| 25 | for v in 0..=255 { | 18 | for v in 0..=255 { |
| 26 | unwrap!(dac.set(Channel::Ch1, Value::Bit8(to_sine_wave(v)))); | 19 | unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); |
| 27 | unwrap!(dac.trigger(Channel::Ch1)); | 20 | dac.trigger(); |
| 28 | } | 21 | } |
| 29 | } | 22 | } |
| 30 | } | 23 | } |
diff --git a/examples/stm32l4/src/bin/dac_dma.rs b/examples/stm32l4/src/bin/dac_dma.rs new file mode 100644 index 000000000..c27cc03e1 --- /dev/null +++ b/examples/stm32l4/src/bin/dac_dma.rs | |||
| @@ -0,0 +1,137 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::dac::{DacChannel, ValueArray}; | ||
| 8 | use embassy_stm32::pac::timer::vals::{Mms, Opm}; | ||
| 9 | use embassy_stm32::peripherals::{TIM6, TIM7}; | ||
| 10 | use embassy_stm32::rcc::low_level::RccPeripheral; | ||
| 11 | use embassy_stm32::time::Hertz; | ||
| 12 | use embassy_stm32::timer::low_level::Basic16bitInstance; | ||
| 13 | use micromath::F32Ext; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | pub type Dac1Type = | ||
| 17 | embassy_stm32::dac::DacCh1<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>; | ||
| 18 | |||
| 19 | pub type Dac2Type = | ||
| 20 | embassy_stm32::dac::DacCh2<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>; | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(spawner: Spawner) { | ||
| 24 | let config = embassy_stm32::Config::default(); | ||
| 25 | |||
| 26 | // Initialize the board and obtain a Peripherals instance | ||
| 27 | let p: embassy_stm32::Peripherals = embassy_stm32::init(config); | ||
| 28 | |||
| 29 | // Obtain two independent channels (p.DAC1 can only be consumed once, though!) | ||
| 30 | let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split(); | ||
| 31 | |||
| 32 | spawner.spawn(dac_task1(dac_ch1)).ok(); | ||
| 33 | spawner.spawn(dac_task2(dac_ch2)).ok(); | ||
| 34 | } | ||
| 35 | |||
| 36 | #[embassy_executor::task] | ||
| 37 | async fn dac_task1(mut dac: Dac1Type) { | ||
| 38 | let data: &[u8; 256] = &calculate_array::<256>(); | ||
| 39 | |||
| 40 | info!("TIM6 frequency is {}", TIM6::frequency()); | ||
| 41 | const FREQUENCY: Hertz = Hertz::hz(200); | ||
| 42 | |||
| 43 | // Compute the reload value such that we obtain the FREQUENCY for the sine | ||
| 44 | let reload: u32 = (TIM6::frequency().0 / FREQUENCY.0) / data.len() as u32; | ||
| 45 | |||
| 46 | // Depends on your clock and on the specific chip used, you may need higher or lower values here | ||
| 47 | if reload < 10 { | ||
| 48 | error!("Reload value {} below threshold!", reload); | ||
| 49 | } | ||
| 50 | |||
| 51 | dac.select_trigger(embassy_stm32::dac::Ch1Trigger::Tim6).unwrap(); | ||
| 52 | dac.enable_channel().unwrap(); | ||
| 53 | |||
| 54 | TIM6::enable(); | ||
| 55 | TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); | ||
| 56 | TIM6::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE)); | ||
| 57 | TIM6::regs().cr1().modify(|w| { | ||
| 58 | w.set_opm(Opm::DISABLED); | ||
| 59 | w.set_cen(true); | ||
| 60 | }); | ||
| 61 | |||
| 62 | debug!( | ||
| 63 | "TIM6 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", | ||
| 64 | TIM6::frequency(), | ||
| 65 | FREQUENCY, | ||
| 66 | reload, | ||
| 67 | reload as u16, | ||
| 68 | data.len() | ||
| 69 | ); | ||
| 70 | |||
| 71 | // Loop technically not necessary if DMA circular mode is enabled | ||
| 72 | loop { | ||
| 73 | info!("Loop DAC1"); | ||
| 74 | if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { | ||
| 75 | error!("Could not write to dac: {}", e); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | #[embassy_executor::task] | ||
| 81 | async fn dac_task2(mut dac: Dac2Type) { | ||
| 82 | let data: &[u8; 256] = &calculate_array::<256>(); | ||
| 83 | |||
| 84 | info!("TIM7 frequency is {}", TIM7::frequency()); | ||
| 85 | |||
| 86 | const FREQUENCY: Hertz = Hertz::hz(600); | ||
| 87 | let reload: u32 = (TIM7::frequency().0 / FREQUENCY.0) / data.len() as u32; | ||
| 88 | |||
| 89 | if reload < 10 { | ||
| 90 | error!("Reload value {} below threshold!", reload); | ||
| 91 | } | ||
| 92 | |||
| 93 | TIM7::enable(); | ||
| 94 | TIM7::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); | ||
| 95 | TIM7::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE)); | ||
| 96 | TIM7::regs().cr1().modify(|w| { | ||
| 97 | w.set_opm(Opm::DISABLED); | ||
| 98 | w.set_cen(true); | ||
| 99 | }); | ||
| 100 | |||
| 101 | dac.select_trigger(embassy_stm32::dac::Ch2Trigger::Tim7).unwrap(); | ||
| 102 | |||
| 103 | debug!( | ||
| 104 | "TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", | ||
| 105 | TIM7::frequency(), | ||
| 106 | FREQUENCY, | ||
| 107 | reload, | ||
| 108 | reload as u16, | ||
| 109 | data.len() | ||
| 110 | ); | ||
| 111 | |||
| 112 | if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { | ||
| 113 | error!("Could not write to dac: {}", e); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | fn to_sine_wave(v: u8) -> u8 { | ||
| 118 | if v >= 128 { | ||
| 119 | // top half | ||
| 120 | let r = 3.14 * ((v - 128) as f32 / 128.0); | ||
| 121 | (r.sin() * 128.0 + 127.0) as u8 | ||
| 122 | } else { | ||
| 123 | // bottom half | ||
| 124 | let r = 3.14 + 3.14 * (v as f32 / 128.0); | ||
| 125 | (r.sin() * 128.0 + 127.0) as u8 | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | fn calculate_array<const N: usize>() -> [u8; N] { | ||
| 130 | let mut res = [0; N]; | ||
| 131 | let mut i = 0; | ||
| 132 | while i < N { | ||
| 133 | res[i] = to_sine_wave(i as u8); | ||
| 134 | i += 1; | ||
| 135 | } | ||
| 136 | res | ||
| 137 | } | ||
diff --git a/examples/stm32l4/src/bin/i2c.rs b/examples/stm32l4/src/bin/i2c.rs index d54c080c7..d0060d20c 100644 --- a/examples/stm32l4/src/bin/i2c.rs +++ b/examples/stm32l4/src/bin/i2c.rs | |||
| @@ -6,22 +6,25 @@ use defmt::*; | |||
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::dma::NoDma; | 7 | use embassy_stm32::dma::NoDma; |
| 8 | use embassy_stm32::i2c::I2c; | 8 | use embassy_stm32::i2c::I2c; |
| 9 | use embassy_stm32::interrupt; | ||
| 10 | use embassy_stm32::time::Hertz; | 9 | use embassy_stm32::time::Hertz; |
| 10 | use embassy_stm32::{bind_interrupts, i2c, peripherals}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 12 | ||
| 13 | const ADDRESS: u8 = 0x5F; | 13 | const ADDRESS: u8 = 0x5F; |
| 14 | const WHOAMI: u8 = 0x0F; | 14 | const WHOAMI: u8 = 0x0F; |
| 15 | 15 | ||
| 16 | bind_interrupts!(struct Irqs { | ||
| 17 | I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>; | ||
| 18 | }); | ||
| 19 | |||
| 16 | #[embassy_executor::main] | 20 | #[embassy_executor::main] |
| 17 | async fn main(_spawner: Spawner) -> ! { | 21 | async fn main(_spawner: Spawner) { |
| 18 | let p = embassy_stm32::init(Default::default()); | 22 | let p = embassy_stm32::init(Default::default()); |
| 19 | let irq = interrupt::take!(I2C2_EV); | ||
| 20 | let mut i2c = I2c::new( | 23 | let mut i2c = I2c::new( |
| 21 | p.I2C2, | 24 | p.I2C2, |
| 22 | p.PB10, | 25 | p.PB10, |
| 23 | p.PB11, | 26 | p.PB11, |
| 24 | irq, | 27 | Irqs, |
| 25 | NoDma, | 28 | NoDma, |
| 26 | NoDma, | 29 | NoDma, |
| 27 | Hertz(100_000), | 30 | Hertz(100_000), |
diff --git a/examples/stm32l4/src/bin/i2c_blocking_async.rs b/examples/stm32l4/src/bin/i2c_blocking_async.rs index 35a86660d..eca59087b 100644 --- a/examples/stm32l4/src/bin/i2c_blocking_async.rs +++ b/examples/stm32l4/src/bin/i2c_blocking_async.rs | |||
| @@ -7,23 +7,26 @@ use embassy_embedded_hal::adapter::BlockingAsync; | |||
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 8 | use embassy_stm32::dma::NoDma; | 8 | use embassy_stm32::dma::NoDma; |
| 9 | use embassy_stm32::i2c::I2c; | 9 | use embassy_stm32::i2c::I2c; |
| 10 | use embassy_stm32::interrupt; | ||
| 11 | use embassy_stm32::time::Hertz; | 10 | use embassy_stm32::time::Hertz; |
| 11 | use embassy_stm32::{bind_interrupts, i2c, peripherals}; | ||
| 12 | use embedded_hal_async::i2c::I2c as I2cTrait; | 12 | use embedded_hal_async::i2c::I2c as I2cTrait; |
| 13 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 14 | 14 | ||
| 15 | const ADDRESS: u8 = 0x5F; | 15 | const ADDRESS: u8 = 0x5F; |
| 16 | const WHOAMI: u8 = 0x0F; | 16 | const WHOAMI: u8 = 0x0F; |
| 17 | 17 | ||
| 18 | bind_interrupts!(struct Irqs { | ||
| 19 | I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>; | ||
| 20 | }); | ||
| 21 | |||
| 18 | #[embassy_executor::main] | 22 | #[embassy_executor::main] |
| 19 | async fn main(_spawner: Spawner) -> ! { | 23 | async fn main(_spawner: Spawner) { |
| 20 | let p = embassy_stm32::init(Default::default()); | 24 | let p = embassy_stm32::init(Default::default()); |
| 21 | let irq = interrupt::take!(I2C2_EV); | ||
| 22 | let i2c = I2c::new( | 25 | let i2c = I2c::new( |
| 23 | p.I2C2, | 26 | p.I2C2, |
| 24 | p.PB10, | 27 | p.PB10, |
| 25 | p.PB11, | 28 | p.PB11, |
| 26 | irq, | 29 | Irqs, |
| 27 | NoDma, | 30 | NoDma, |
| 28 | NoDma, | 31 | NoDma, |
| 29 | Hertz(100_000), | 32 | Hertz(100_000), |
diff --git a/examples/stm32l4/src/bin/i2c_dma.rs b/examples/stm32l4/src/bin/i2c_dma.rs index 3ce9398a4..cf6f3da67 100644 --- a/examples/stm32l4/src/bin/i2c_dma.rs +++ b/examples/stm32l4/src/bin/i2c_dma.rs | |||
| @@ -5,22 +5,25 @@ | |||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::i2c::I2c; | 7 | use embassy_stm32::i2c::I2c; |
| 8 | use embassy_stm32::interrupt; | ||
| 9 | use embassy_stm32::time::Hertz; | 8 | use embassy_stm32::time::Hertz; |
| 9 | use embassy_stm32::{bind_interrupts, i2c, peripherals}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 11 | ||
| 12 | const ADDRESS: u8 = 0x5F; | 12 | const ADDRESS: u8 = 0x5F; |
| 13 | const WHOAMI: u8 = 0x0F; | 13 | const WHOAMI: u8 = 0x0F; |
| 14 | 14 | ||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>; | ||
| 17 | }); | ||
| 18 | |||
| 15 | #[embassy_executor::main] | 19 | #[embassy_executor::main] |
| 16 | async fn main(_spawner: Spawner) -> ! { | 20 | async fn main(_spawner: Spawner) { |
| 17 | let p = embassy_stm32::init(Default::default()); | 21 | let p = embassy_stm32::init(Default::default()); |
| 18 | let irq = interrupt::take!(I2C2_EV); | ||
| 19 | let mut i2c = I2c::new( | 22 | let mut i2c = I2c::new( |
| 20 | p.I2C2, | 23 | p.I2C2, |
| 21 | p.PB10, | 24 | p.PB10, |
| 22 | p.PB11, | 25 | p.PB11, |
| 23 | irq, | 26 | Irqs, |
| 24 | p.DMA1_CH4, | 27 | p.DMA1_CH4, |
| 25 | p.DMA1_CH5, | 28 | p.DMA1_CH5, |
| 26 | Hertz(100_000), | 29 | Hertz(100_000), |
diff --git a/examples/stm32l4/src/bin/mco.rs b/examples/stm32l4/src/bin/mco.rs new file mode 100644 index 000000000..dea0c66e0 --- /dev/null +++ b/examples/stm32l4/src/bin/mco.rs | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 8 | use embassy_stm32::rcc::{Mco, Mco1Source, McoClock}; | ||
| 9 | use embassy_time::{Duration, Timer}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) { | ||
| 14 | let p = embassy_stm32::init(Default::default()); | ||
| 15 | info!("Hello World!"); | ||
| 16 | |||
| 17 | let _mco = Mco::new(p.MCO, p.PA8, Mco1Source::Hsi16, McoClock::DIV1); | ||
| 18 | |||
| 19 | let mut led = Output::new(p.PB14, Level::High, Speed::Low); | ||
| 20 | |||
| 21 | loop { | ||
| 22 | led.set_high(); | ||
| 23 | Timer::after(Duration::from_millis(300)).await; | ||
| 24 | led.set_low(); | ||
| 25 | Timer::after(Duration::from_millis(300)).await; | ||
| 26 | } | ||
| 27 | } | ||
diff --git a/examples/stm32l4/src/bin/rtc.rs b/examples/stm32l4/src/bin/rtc.rs new file mode 100644 index 000000000..d72d5ddb6 --- /dev/null +++ b/examples/stm32l4/src/bin/rtc.rs | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use chrono::{NaiveDate, NaiveDateTime}; | ||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_stm32::rcc::{self, ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv}; | ||
| 9 | use embassy_stm32::rtc::{Rtc, RtcConfig}; | ||
| 10 | use embassy_stm32::time::Hertz; | ||
| 11 | use embassy_stm32::Config; | ||
| 12 | use embassy_time::{Duration, Timer}; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | #[embassy_executor::main] | ||
| 16 | async fn main(_spawner: Spawner) { | ||
| 17 | let p = { | ||
| 18 | let mut config = Config::default(); | ||
| 19 | config.rcc.mux = ClockSrc::PLL( | ||
| 20 | PLLSource::HSE(Hertz::mhz(8)), | ||
| 21 | PLLClkDiv::Div2, | ||
| 22 | PLLSrcDiv::Div1, | ||
| 23 | PLLMul::Mul20, | ||
| 24 | None, | ||
| 25 | ); | ||
| 26 | config.rcc.rtc_mux = rcc::RtcClockSource::LSE32; | ||
| 27 | embassy_stm32::init(config) | ||
| 28 | }; | ||
| 29 | info!("Hello World!"); | ||
| 30 | |||
| 31 | let now = NaiveDate::from_ymd_opt(2020, 5, 15) | ||
| 32 | .unwrap() | ||
| 33 | .and_hms_opt(10, 30, 15) | ||
| 34 | .unwrap(); | ||
| 35 | |||
| 36 | let mut rtc = Rtc::new( | ||
| 37 | p.RTC, | ||
| 38 | RtcConfig::default().clock_config(embassy_stm32::rtc::RtcClockSource::LSE), | ||
| 39 | ); | ||
| 40 | info!("Got RTC! {:?}", now.timestamp()); | ||
| 41 | |||
| 42 | rtc.set_datetime(now.into()).expect("datetime not set"); | ||
| 43 | |||
| 44 | // In reality the delay would be much longer | ||
| 45 | Timer::after(Duration::from_millis(20000)).await; | ||
| 46 | |||
| 47 | let then: NaiveDateTime = rtc.now().unwrap().into(); | ||
| 48 | info!("Got RTC! {:?}", then.timestamp()); | ||
| 49 | } | ||
diff --git a/examples/stm32l4/src/bin/usart.rs b/examples/stm32l4/src/bin/usart.rs index 4a4b46c53..beb5ec558 100644 --- a/examples/stm32l4/src/bin/usart.rs +++ b/examples/stm32l4/src/bin/usart.rs | |||
| @@ -5,8 +5,13 @@ | |||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_stm32::dma::NoDma; | 6 | use embassy_stm32::dma::NoDma; |
| 7 | use embassy_stm32::usart::{Config, Uart}; | 7 | use embassy_stm32::usart::{Config, Uart}; |
| 8 | use embassy_stm32::{bind_interrupts, peripherals, usart}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 11 | bind_interrupts!(struct Irqs { | ||
| 12 | UART4 => usart::InterruptHandler<peripherals::UART4>; | ||
| 13 | }); | ||
| 14 | |||
| 10 | #[cortex_m_rt::entry] | 15 | #[cortex_m_rt::entry] |
| 11 | fn main() -> ! { | 16 | fn main() -> ! { |
| 12 | info!("Hello World!"); | 17 | info!("Hello World!"); |
| @@ -14,7 +19,7 @@ fn main() -> ! { | |||
| 14 | let p = embassy_stm32::init(Default::default()); | 19 | let p = embassy_stm32::init(Default::default()); |
| 15 | 20 | ||
| 16 | let config = Config::default(); | 21 | let config = Config::default(); |
| 17 | let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, NoDma, NoDma, config); | 22 | let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, Irqs, NoDma, NoDma, config); |
| 18 | 23 | ||
| 19 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); | 24 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); |
| 20 | info!("wrote Hello, starting echo"); | 25 | info!("wrote Hello, starting echo"); |
diff --git a/examples/stm32l4/src/bin/usart_dma.rs b/examples/stm32l4/src/bin/usart_dma.rs index 728906897..b7d4cb01e 100644 --- a/examples/stm32l4/src/bin/usart_dma.rs +++ b/examples/stm32l4/src/bin/usart_dma.rs | |||
| @@ -8,16 +8,21 @@ use defmt::*; | |||
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_stm32::dma::NoDma; | 9 | use embassy_stm32::dma::NoDma; |
| 10 | use embassy_stm32::usart::{Config, Uart}; | 10 | use embassy_stm32::usart::{Config, Uart}; |
| 11 | use embassy_stm32::{bind_interrupts, peripherals, usart}; | ||
| 11 | use heapless::String; | 12 | use heapless::String; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 14 | ||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | UART4 => usart::InterruptHandler<peripherals::UART4>; | ||
| 17 | }); | ||
| 18 | |||
| 14 | #[embassy_executor::main] | 19 | #[embassy_executor::main] |
| 15 | async fn main(_spawner: Spawner) { | 20 | async fn main(_spawner: Spawner) { |
| 16 | let p = embassy_stm32::init(Default::default()); | 21 | let p = embassy_stm32::init(Default::default()); |
| 17 | info!("Hello World!"); | 22 | info!("Hello World!"); |
| 18 | 23 | ||
| 19 | let config = Config::default(); | 24 | let config = Config::default(); |
| 20 | let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, p.DMA1_CH3, NoDma, config); | 25 | let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, Irqs, p.DMA1_CH3, NoDma, config); |
| 21 | 26 | ||
| 22 | for n in 0u32.. { | 27 | for n in 0u32.. { |
| 23 | let mut s: String<128> = String::new(); | 28 | let mut s: String<128> = String::new(); |
diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs new file mode 100644 index 000000000..410d6891b --- /dev/null +++ b/examples/stm32l4/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{panic, *}; | ||
| 6 | use defmt_rtt as _; // global logger | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_stm32::rcc::*; | ||
| 9 | use embassy_stm32::usb_otg::{Driver, Instance}; | ||
| 10 | use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; | ||
| 11 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 12 | use embassy_usb::driver::EndpointError; | ||
| 13 | use embassy_usb::Builder; | ||
| 14 | use futures::future::join; | ||
| 15 | use panic_probe as _; | ||
| 16 | |||
| 17 | bind_interrupts!(struct Irqs { | ||
| 18 | OTG_FS => usb_otg::InterruptHandler<peripherals::USB_OTG_FS>; | ||
| 19 | }); | ||
| 20 | |||
| 21 | #[embassy_executor::main] | ||
| 22 | async fn main(_spawner: Spawner) { | ||
| 23 | info!("Hello World!"); | ||
| 24 | |||
| 25 | let mut config = Config::default(); | ||
| 26 | config.rcc.mux = ClockSrc::PLL(PLLSource::HSI16, PLLClkDiv::Div2, PLLSrcDiv::Div1, PLLMul::Mul10, None); | ||
| 27 | config.rcc.hsi48 = true; | ||
| 28 | |||
| 29 | let p = embassy_stm32::init(config); | ||
| 30 | |||
| 31 | // Create the driver, from the HAL. | ||
| 32 | let mut ep_out_buffer = [0u8; 256]; | ||
| 33 | let mut config = embassy_stm32::usb_otg::Config::default(); | ||
| 34 | config.vbus_detection = true; | ||
| 35 | let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); | ||
| 36 | |||
| 37 | // Create embassy-usb Config | ||
| 38 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 39 | config.max_packet_size_0 = 64; | ||
| 40 | config.manufacturer = Some("Embassy"); | ||
| 41 | config.product = Some("USB-serial example"); | ||
| 42 | config.serial_number = Some("12345678"); | ||
| 43 | |||
| 44 | // Required for windows compatibility. | ||
| 45 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 46 | config.device_class = 0xEF; | ||
| 47 | config.device_sub_class = 0x02; | ||
| 48 | config.device_protocol = 0x01; | ||
| 49 | config.composite_with_iads = true; | ||
| 50 | |||
| 51 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 52 | // It needs some buffers for building the descriptors. | ||
| 53 | let mut device_descriptor = [0; 256]; | ||
| 54 | let mut config_descriptor = [0; 256]; | ||
| 55 | let mut bos_descriptor = [0; 256]; | ||
| 56 | let mut control_buf = [0; 64]; | ||
| 57 | |||
| 58 | let mut state = State::new(); | ||
| 59 | |||
| 60 | let mut builder = Builder::new( | ||
| 61 | driver, | ||
| 62 | config, | ||
| 63 | &mut device_descriptor, | ||
| 64 | &mut config_descriptor, | ||
| 65 | &mut bos_descriptor, | ||
| 66 | &mut control_buf, | ||
| 67 | ); | ||
| 68 | |||
| 69 | // Create classes on the builder. | ||
| 70 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 71 | |||
| 72 | // Build the builder. | ||
| 73 | let mut usb = builder.build(); | ||
| 74 | |||
| 75 | // Run the USB device. | ||
| 76 | let usb_fut = usb.run(); | ||
| 77 | |||
| 78 | // Do stuff with the class! | ||
| 79 | let echo_fut = async { | ||
| 80 | loop { | ||
| 81 | class.wait_connection().await; | ||
| 82 | info!("Connected"); | ||
| 83 | let _ = echo(&mut class).await; | ||
| 84 | info!("Disconnected"); | ||
| 85 | } | ||
| 86 | }; | ||
| 87 | |||
| 88 | // Run everything concurrently. | ||
| 89 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 90 | join(usb_fut, echo_fut).await; | ||
| 91 | } | ||
| 92 | |||
| 93 | struct Disconnected {} | ||
| 94 | |||
| 95 | impl From<EndpointError> for Disconnected { | ||
| 96 | fn from(val: EndpointError) -> Self { | ||
| 97 | match val { | ||
| 98 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 99 | EndpointError::Disabled => Disconnected {}, | ||
| 100 | } | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 105 | let mut buf = [0; 64]; | ||
| 106 | loop { | ||
| 107 | let n = class.read_packet(&mut buf).await?; | ||
| 108 | let data = &buf[..n]; | ||
| 109 | info!("data: {:x}", data); | ||
| 110 | class.write_packet(data).await?; | ||
| 111 | } | ||
| 112 | } | ||
diff --git a/examples/stm32l5/.cargo/config.toml b/examples/stm32l5/.cargo/config.toml index f2af6b556..86a145a27 100644 --- a/examples/stm32l5/.cargo/config.toml +++ b/examples/stm32l5/.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 STM32L552ZETxQ with your chip as listed in `probe-run --list-chips` | 2 | # replace STM32L552ZETxQ with your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32L552ZETxQ" | 3 | runner = "probe-rs run --chip STM32L552ZETxQ" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv8m.main-none-eabihf" | 6 | target = "thumbv8m.main-none-eabihf" |
diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 624c73c26..54911482e 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml | |||
| @@ -2,30 +2,27 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-stm32l5-examples" | 3 | name = "embassy-stm32l5-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | 5 | license = "MIT OR Apache-2.0" | |
| 6 | [features] | ||
| 7 | 6 | ||
| 8 | [dependencies] | 7 | [dependencies] |
| 9 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 10 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 11 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 12 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } |
| 13 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | 12 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } |
| 14 | embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } | 13 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } |
| 15 | embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } | 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 16 | embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] } | 15 | usbd-hid = "0.6.0" |
| 17 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } | ||
| 18 | usbd-hid = "0.5.2" | ||
| 19 | 16 | ||
| 20 | defmt = "0.3" | 17 | defmt = "0.3" |
| 21 | defmt-rtt = "0.3" | 18 | defmt-rtt = "0.4" |
| 22 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 19 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 23 | 20 | ||
| 24 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 25 | cortex-m-rt = "0.7.0" | 22 | cortex-m-rt = "0.7.0" |
| 26 | embedded-hal = "0.2.6" | 23 | embedded-hal = "0.2.6" |
| 27 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 24 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } |
| 28 | heapless = { version = "0.7.5", default-features = false } | 25 | heapless = { version = "0.7.5", default-features = false } |
| 29 | rand_core = { version = "0.6.3", default-features = false } | 26 | rand_core = { version = "0.6.3", default-features = false } |
| 30 | embedded-io = { version = "0.3.0", features = ["async"] } | 27 | embedded-io = { version = "0.4.0", features = ["async"] } |
| 31 | static_cell = "1.0" | 28 | static_cell = { version = "1.1", features = ["nightly"]} |
diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index 3286f5c4d..32eba4277 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs | |||
| @@ -1,38 +1,30 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(generic_associated_types)] | ||
| 4 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 5 | 4 | ||
| 6 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 7 | use core::task::Waker; | ||
| 8 | |||
| 9 | use defmt::*; | 5 | use defmt::*; |
| 10 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 11 | use embassy_net::tcp::TcpSocket; | 7 | use embassy_net::tcp::TcpSocket; |
| 12 | use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; | 8 | use embassy_net::{Stack, StackResources}; |
| 13 | use embassy_stm32::rcc::*; | 9 | use embassy_stm32::rcc::*; |
| 14 | use embassy_stm32::rng::Rng; | 10 | use embassy_stm32::rng::Rng; |
| 15 | use embassy_stm32::time::Hertz; | ||
| 16 | use embassy_stm32::usb::Driver; | 11 | use embassy_stm32::usb::Driver; |
| 17 | use embassy_stm32::{interrupt, Config}; | 12 | use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; |
| 18 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | 13 | use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; |
| 19 | use embassy_sync::channel::Channel; | 14 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; |
| 20 | use embassy_usb::{Builder, UsbDevice}; | 15 | use embassy_usb::{Builder, UsbDevice}; |
| 21 | use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; | 16 | use embedded_io::asynch::Write; |
| 22 | use embedded_io::asynch::{Read, Write}; | ||
| 23 | use rand_core::RngCore; | 17 | use rand_core::RngCore; |
| 24 | use static_cell::StaticCell; | 18 | use static_cell::make_static; |
| 25 | use {defmt_rtt as _, panic_probe as _}; | 19 | use {defmt_rtt as _, panic_probe as _}; |
| 26 | 20 | ||
| 27 | type MyDriver = Driver<'static, embassy_stm32::peripherals::USB>; | 21 | type MyDriver = Driver<'static, embassy_stm32::peripherals::USB>; |
| 28 | 22 | ||
| 29 | macro_rules! singleton { | 23 | const MTU: usize = 1514; |
| 30 | ($val:expr) => {{ | 24 | |
| 31 | type T = impl Sized; | 25 | bind_interrupts!(struct Irqs { |
| 32 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | 26 | USB_FS => usb::InterruptHandler<peripherals::USB>; |
| 33 | STATIC_CELL.init_with(move || $val) | 27 | }); |
| 34 | }}; | ||
| 35 | } | ||
| 36 | 28 | ||
| 37 | #[embassy_executor::task] | 29 | #[embassy_executor::task] |
| 38 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { | 30 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { |
| @@ -40,46 +32,12 @@ async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { | |||
| 40 | } | 32 | } |
| 41 | 33 | ||
| 42 | #[embassy_executor::task] | 34 | #[embassy_executor::task] |
| 43 | async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { | 35 | async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { |
| 44 | loop { | 36 | class.run().await |
| 45 | warn!("WAITING for connection"); | ||
| 46 | LINK_UP.store(false, Ordering::Relaxed); | ||
| 47 | |||
| 48 | class.wait_connection().await.unwrap(); | ||
| 49 | |||
| 50 | warn!("Connected"); | ||
| 51 | LINK_UP.store(true, Ordering::Relaxed); | ||
| 52 | |||
| 53 | loop { | ||
| 54 | let mut p = unwrap!(PacketBox::new(embassy_net::Packet::new())); | ||
| 55 | let n = match class.read_packet(&mut p[..]).await { | ||
| 56 | Ok(n) => n, | ||
| 57 | Err(e) => { | ||
| 58 | warn!("error reading packet: {:?}", e); | ||
| 59 | break; | ||
| 60 | } | ||
| 61 | }; | ||
| 62 | |||
| 63 | let buf = p.slice(0..n); | ||
| 64 | if RX_CHANNEL.try_send(buf).is_err() { | ||
| 65 | warn!("Failed pushing rx'd packet to channel."); | ||
| 66 | } | ||
| 67 | } | ||
| 68 | } | ||
| 69 | } | 37 | } |
| 70 | 38 | ||
| 71 | #[embassy_executor::task] | 39 | #[embassy_executor::task] |
| 72 | async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { | 40 | async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! { |
| 73 | loop { | ||
| 74 | let pkt = TX_CHANNEL.recv().await; | ||
| 75 | if let Err(e) = class.write_packet(&pkt[..]).await { | ||
| 76 | warn!("Failed to TX packet: {:?}", e); | ||
| 77 | } | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | #[embassy_executor::task] | ||
| 82 | async fn net_task(stack: &'static Stack<Device>) -> ! { | ||
| 83 | stack.run().await | 41 | stack.run().await |
| 84 | } | 42 | } |
| 85 | 43 | ||
| @@ -91,8 +49,7 @@ async fn main(spawner: Spawner) { | |||
| 91 | let p = embassy_stm32::init(config); | 49 | let p = embassy_stm32::init(config); |
| 92 | 50 | ||
| 93 | // Create the driver, from the HAL. | 51 | // Create the driver, from the HAL. |
| 94 | let irq = interrupt::take!(USB_FS); | 52 | let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); |
| 95 | let driver = Driver::new(p.USB, irq, p.PA12, p.PA11); | ||
| 96 | 53 | ||
| 97 | // Create embassy-usb Config | 54 | // Create embassy-usb Config |
| 98 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | 55 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); |
| @@ -108,58 +65,34 @@ async fn main(spawner: Spawner) { | |||
| 108 | config.device_sub_class = 0x02; | 65 | config.device_sub_class = 0x02; |
| 109 | config.device_protocol = 0x01; | 66 | config.device_protocol = 0x01; |
| 110 | 67 | ||
| 111 | struct Resources { | ||
| 112 | device_descriptor: [u8; 256], | ||
| 113 | config_descriptor: [u8; 256], | ||
| 114 | bos_descriptor: [u8; 256], | ||
| 115 | control_buf: [u8; 128], | ||
| 116 | serial_state: State<'static>, | ||
| 117 | } | ||
| 118 | let res: &mut Resources = singleton!(Resources { | ||
| 119 | device_descriptor: [0; 256], | ||
| 120 | config_descriptor: [0; 256], | ||
| 121 | bos_descriptor: [0; 256], | ||
| 122 | control_buf: [0; 128], | ||
| 123 | serial_state: State::new(), | ||
| 124 | }); | ||
| 125 | |||
| 126 | // Create embassy-usb DeviceBuilder using the driver and config. | 68 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 127 | let mut builder = Builder::new( | 69 | let mut builder = Builder::new( |
| 128 | driver, | 70 | driver, |
| 129 | config, | 71 | config, |
| 130 | &mut res.device_descriptor, | 72 | &mut make_static!([0; 256])[..], |
| 131 | &mut res.config_descriptor, | 73 | &mut make_static!([0; 256])[..], |
| 132 | &mut res.bos_descriptor, | 74 | &mut make_static!([0; 256])[..], |
| 133 | &mut res.control_buf, | 75 | &mut make_static!([0; 128])[..], |
| 134 | None, | ||
| 135 | ); | 76 | ); |
| 136 | 77 | ||
| 137 | // WARNINGS for Android ethernet tethering: | ||
| 138 | // - On Pixel 4a, it refused to work on Android 11, worked on Android 12. | ||
| 139 | // - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), | ||
| 140 | // it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. | ||
| 141 | // This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 | ||
| 142 | // and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 | ||
| 143 | |||
| 144 | // Our MAC addr. | 78 | // Our MAC addr. |
| 145 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; | 79 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; |
| 146 | // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. | 80 | // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. |
| 147 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; | 81 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; |
| 148 | 82 | ||
| 149 | // Create classes on the builder. | 83 | // Create classes on the builder. |
| 150 | let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64); | 84 | let class = CdcNcmClass::new(&mut builder, make_static!(State::new()), host_mac_addr, 64); |
| 151 | 85 | ||
| 152 | // Build the builder. | 86 | // Build the builder. |
| 153 | let usb = builder.build(); | 87 | let usb = builder.build(); |
| 154 | 88 | ||
| 155 | unwrap!(spawner.spawn(usb_task(usb))); | 89 | unwrap!(spawner.spawn(usb_task(usb))); |
| 156 | 90 | ||
| 157 | let (tx, rx) = class.split(); | 91 | let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(make_static!(NetState::new()), our_mac_addr); |
| 158 | unwrap!(spawner.spawn(usb_ncm_rx_task(rx))); | 92 | unwrap!(spawner.spawn(usb_ncm_task(runner))); |
| 159 | unwrap!(spawner.spawn(usb_ncm_tx_task(tx))); | ||
| 160 | 93 | ||
| 161 | let config = embassy_net::ConfigStrategy::Dhcp; | 94 | let config = embassy_net::Config::dhcpv4(Default::default()); |
| 162 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { | 95 | //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { |
| 163 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | 96 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), |
| 164 | // dns_servers: Vec::new(), | 97 | // dns_servers: Vec::new(), |
| 165 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | 98 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), |
| @@ -170,11 +103,10 @@ async fn main(spawner: Spawner) { | |||
| 170 | let seed = rng.next_u64(); | 103 | let seed = rng.next_u64(); |
| 171 | 104 | ||
| 172 | // Init network stack | 105 | // Init network stack |
| 173 | let device = Device { mac_addr: our_mac_addr }; | 106 | let stack = &*make_static!(Stack::new( |
| 174 | let stack = &*singleton!(Stack::new( | ||
| 175 | device, | 107 | device, |
| 176 | config, | 108 | config, |
| 177 | singleton!(StackResources::<1, 2, 8>::new()), | 109 | make_static!(StackResources::<2>::new()), |
| 178 | seed | 110 | seed |
| 179 | )); | 111 | )); |
| 180 | 112 | ||
| @@ -188,7 +120,7 @@ async fn main(spawner: Spawner) { | |||
| 188 | 120 | ||
| 189 | loop { | 121 | loop { |
| 190 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | 122 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); |
| 191 | socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); | 123 | socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); |
| 192 | 124 | ||
| 193 | info!("Listening on TCP:1234..."); | 125 | info!("Listening on TCP:1234..."); |
| 194 | if let Err(e) = socket.accept(1234).await { | 126 | if let Err(e) = socket.accept(1234).await { |
| @@ -223,50 +155,3 @@ async fn main(spawner: Spawner) { | |||
| 223 | } | 155 | } |
| 224 | } | 156 | } |
| 225 | } | 157 | } |
| 226 | |||
| 227 | static TX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new(); | ||
| 228 | static RX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new(); | ||
| 229 | static LINK_UP: AtomicBool = AtomicBool::new(false); | ||
| 230 | |||
| 231 | struct Device { | ||
| 232 | mac_addr: [u8; 6], | ||
| 233 | } | ||
| 234 | |||
| 235 | impl embassy_net::Device for Device { | ||
| 236 | fn register_waker(&mut self, waker: &Waker) { | ||
| 237 | // loopy loopy wakey wakey | ||
| 238 | waker.wake_by_ref() | ||
| 239 | } | ||
| 240 | |||
| 241 | fn link_state(&mut self) -> embassy_net::LinkState { | ||
| 242 | match LINK_UP.load(Ordering::Relaxed) { | ||
| 243 | true => embassy_net::LinkState::Up, | ||
| 244 | false => embassy_net::LinkState::Down, | ||
| 245 | } | ||
| 246 | } | ||
| 247 | |||
| 248 | fn capabilities(&self) -> embassy_net::DeviceCapabilities { | ||
| 249 | let mut caps = embassy_net::DeviceCapabilities::default(); | ||
| 250 | caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header | ||
| 251 | caps.medium = embassy_net::Medium::Ethernet; | ||
| 252 | caps | ||
| 253 | } | ||
| 254 | |||
| 255 | fn is_transmit_ready(&mut self) -> bool { | ||
| 256 | true | ||
| 257 | } | ||
| 258 | |||
| 259 | fn transmit(&mut self, pkt: PacketBuf) { | ||
| 260 | if TX_CHANNEL.try_send(pkt).is_err() { | ||
| 261 | warn!("TX failed") | ||
| 262 | } | ||
| 263 | } | ||
| 264 | |||
| 265 | fn receive<'a>(&mut self) -> Option<PacketBuf> { | ||
| 266 | RX_CHANNEL.try_recv().ok() | ||
| 267 | } | ||
| 268 | |||
| 269 | fn ethernet_address(&self) -> [u8; 6] { | ||
| 270 | self.mac_addr | ||
| 271 | } | ||
| 272 | } | ||
diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index f7e3d93e3..7e894e407 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs | |||
| @@ -1,22 +1,24 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(generic_associated_types)] | ||
| 4 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 5 | 4 | ||
| 6 | use defmt::*; | 5 | use defmt::*; |
| 7 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_futures::join::join; | ||
| 8 | use embassy_stm32::rcc::*; | 8 | use embassy_stm32::rcc::*; |
| 9 | use embassy_stm32::time::Hertz; | ||
| 10 | use embassy_stm32::usb::Driver; | 9 | use embassy_stm32::usb::Driver; |
| 11 | use embassy_stm32::{interrupt, Config, Peripherals}; | 10 | use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; |
| 12 | use embassy_time::{Duration, Timer}; | 11 | use embassy_time::{Duration, Timer}; |
| 12 | use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; | ||
| 13 | use embassy_usb::control::OutResponse; | 13 | use embassy_usb::control::OutResponse; |
| 14 | use embassy_usb::Builder; | 14 | use embassy_usb::Builder; |
| 15 | use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State}; | ||
| 16 | use futures::future::join; | ||
| 17 | use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; | 15 | use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; |
| 18 | use {defmt_rtt as _, panic_probe as _}; | 16 | use {defmt_rtt as _, panic_probe as _}; |
| 19 | 17 | ||
| 18 | bind_interrupts!(struct Irqs { | ||
| 19 | USB_FS => usb::InterruptHandler<peripherals::USB>; | ||
| 20 | }); | ||
| 21 | |||
| 20 | #[embassy_executor::main] | 22 | #[embassy_executor::main] |
| 21 | async fn main(_spawner: Spawner) { | 23 | async fn main(_spawner: Spawner) { |
| 22 | let mut config = Config::default(); | 24 | let mut config = Config::default(); |
| @@ -25,8 +27,7 @@ async fn main(_spawner: Spawner) { | |||
| 25 | let p = embassy_stm32::init(config); | 27 | let p = embassy_stm32::init(config); |
| 26 | 28 | ||
| 27 | // Create the driver, from the HAL. | 29 | // Create the driver, from the HAL. |
| 28 | let irq = interrupt::take!(USB_FS); | 30 | let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); |
| 29 | let driver = Driver::new(p.USB, irq, p.PA12, p.PA11); | ||
| 30 | 31 | ||
| 31 | // Create embassy-usb Config | 32 | // Create embassy-usb Config |
| 32 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | 33 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); |
| @@ -53,11 +54,10 @@ async fn main(_spawner: Spawner) { | |||
| 53 | &mut config_descriptor, | 54 | &mut config_descriptor, |
| 54 | &mut bos_descriptor, | 55 | &mut bos_descriptor, |
| 55 | &mut control_buf, | 56 | &mut control_buf, |
| 56 | None, | ||
| 57 | ); | 57 | ); |
| 58 | 58 | ||
| 59 | // Create classes on the builder. | 59 | // Create classes on the builder. |
| 60 | let config = embassy_usb_hid::Config { | 60 | let config = embassy_usb::class::hid::Config { |
| 61 | report_descriptor: MouseReport::desc(), | 61 | report_descriptor: MouseReport::desc(), |
| 62 | request_handler: Some(&request_handler), | 62 | request_handler: Some(&request_handler), |
| 63 | poll_ms: 60, | 63 | poll_ms: 60, |
| @@ -111,11 +111,11 @@ impl RequestHandler for MyRequestHandler { | |||
| 111 | OutResponse::Accepted | 111 | OutResponse::Accepted |
| 112 | } | 112 | } |
| 113 | 113 | ||
| 114 | fn set_idle(&self, id: Option<ReportId>, dur: Duration) { | 114 | fn set_idle_ms(&self, id: Option<ReportId>, dur: u32) { |
| 115 | info!("Set idle rate for {:?} to {:?}", id, dur); | 115 | info!("Set idle rate for {:?} to {:?}", id, dur); |
| 116 | } | 116 | } |
| 117 | 117 | ||
| 118 | fn get_idle(&self, id: Option<ReportId>) -> Option<Duration> { | 118 | fn get_idle_ms(&self, id: Option<ReportId>) -> Option<u32> { |
| 119 | info!("Get idle rate for {:?}", id); | 119 | info!("Get idle rate for {:?}", id); |
| 120 | None | 120 | None |
| 121 | } | 121 | } |
diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs index 323db6557..0c719560f 100644 --- a/examples/stm32l5/src/bin/usb_serial.rs +++ b/examples/stm32l5/src/bin/usb_serial.rs | |||
| @@ -4,16 +4,19 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::{panic, *}; | 5 | use defmt::{panic, *}; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_futures::join::join; | ||
| 7 | use embassy_stm32::rcc::*; | 8 | use embassy_stm32::rcc::*; |
| 8 | use embassy_stm32::time::Hertz; | ||
| 9 | use embassy_stm32::usb::{Driver, Instance}; | 9 | use embassy_stm32::usb::{Driver, Instance}; |
| 10 | use embassy_stm32::{interrupt, Config}; | 10 | use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; |
| 11 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 11 | use embassy_usb::driver::EndpointError; | 12 | use embassy_usb::driver::EndpointError; |
| 12 | use embassy_usb::Builder; | 13 | use embassy_usb::Builder; |
| 13 | use embassy_usb_serial::{CdcAcmClass, State}; | ||
| 14 | use futures::future::join; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {defmt_rtt as _, panic_probe as _}; |
| 16 | 15 | ||
| 16 | bind_interrupts!(struct Irqs { | ||
| 17 | USB_FS => usb::InterruptHandler<peripherals::USB>; | ||
| 18 | }); | ||
| 19 | |||
| 17 | #[embassy_executor::main] | 20 | #[embassy_executor::main] |
| 18 | async fn main(_spawner: Spawner) { | 21 | async fn main(_spawner: Spawner) { |
| 19 | let mut config = Config::default(); | 22 | let mut config = Config::default(); |
| @@ -24,8 +27,7 @@ async fn main(_spawner: Spawner) { | |||
| 24 | info!("Hello World!"); | 27 | info!("Hello World!"); |
| 25 | 28 | ||
| 26 | // Create the driver, from the HAL. | 29 | // Create the driver, from the HAL. |
| 27 | let irq = interrupt::take!(USB_FS); | 30 | let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); |
| 28 | let driver = Driver::new(p.USB, irq, p.PA12, p.PA11); | ||
| 29 | 31 | ||
| 30 | // Create embassy-usb Config | 32 | // Create embassy-usb Config |
| 31 | let config = embassy_usb::Config::new(0xc0de, 0xcafe); | 33 | let config = embassy_usb::Config::new(0xc0de, 0xcafe); |
| @@ -47,7 +49,6 @@ async fn main(_spawner: Spawner) { | |||
| 47 | &mut config_descriptor, | 49 | &mut config_descriptor, |
| 48 | &mut bos_descriptor, | 50 | &mut bos_descriptor, |
| 49 | &mut control_buf, | 51 | &mut control_buf, |
| 50 | None, | ||
| 51 | ); | 52 | ); |
| 52 | 53 | ||
| 53 | // Create classes on the builder. | 54 | // Create classes on the builder. |
diff --git a/examples/stm32u5/.cargo/config.toml b/examples/stm32u5/.cargo/config.toml index 975630a14..36c5b63a6 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-run --list-chips` | 2 | # replace STM32U585AIIx with your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32U585AIIx" | 3 | runner = "probe-rs run --chip STM32U585AIIx" |
| 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 ff0ec9f42..835e32940 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml | |||
| @@ -2,17 +2,19 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-stm32u5-examples" | 3 | name = "embassy-stm32u5-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } |
| 12 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 11 | 13 | ||
| 12 | defmt = "0.3" | 14 | defmt = "0.3" |
| 13 | defmt-rtt = "0.3" | 15 | defmt-rtt = "0.4" |
| 14 | 16 | ||
| 15 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 17 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 16 | cortex-m-rt = "0.7.0" | 18 | cortex-m-rt = "0.7.0" |
| 17 | embedded-hal = "0.2.6" | 19 | embedded-hal = "0.2.6" |
| 18 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 20 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| @@ -20,8 +22,3 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa | |||
| 20 | heapless = { version = "0.7.5", default-features = false } | 22 | heapless = { version = "0.7.5", default-features = false } |
| 21 | 23 | ||
| 22 | micromath = "2.0.0" | 24 | micromath = "2.0.0" |
| 23 | |||
| 24 | #[patch.crates-io] | ||
| 25 | #defmt = { git="https://github.com/knurling-rs/defmt.git" } | ||
| 26 | #defmt-rtt = { git="https://github.com/knurling-rs/defmt.git" } | ||
| 27 | |||
diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs new file mode 100644 index 000000000..9e47fb18a --- /dev/null +++ b/examples/stm32u5/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{panic, *}; | ||
| 6 | use defmt_rtt as _; // global logger | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_stm32::rcc::*; | ||
| 9 | use embassy_stm32::usb_otg::{Driver, Instance}; | ||
| 10 | use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; | ||
| 11 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 12 | use embassy_usb::driver::EndpointError; | ||
| 13 | use embassy_usb::Builder; | ||
| 14 | use futures::future::join; | ||
| 15 | use panic_probe as _; | ||
| 16 | |||
| 17 | bind_interrupts!(struct Irqs { | ||
| 18 | OTG_FS => usb_otg::InterruptHandler<peripherals::USB_OTG_FS>; | ||
| 19 | }); | ||
| 20 | |||
| 21 | #[embassy_executor::main] | ||
| 22 | async fn main(_spawner: Spawner) { | ||
| 23 | info!("Hello World!"); | ||
| 24 | |||
| 25 | let mut config = Config::default(); | ||
| 26 | config.rcc.mux = ClockSrc::PLL1R(PllSrc::HSI16, PllM::Div2, PllN::Mul10, PllClkDiv::NotDivided); | ||
| 27 | //config.rcc.mux = ClockSrc::MSI(MSIRange::Range48mhz); | ||
| 28 | config.rcc.hsi48 = true; | ||
| 29 | |||
| 30 | let p = embassy_stm32::init(config); | ||
| 31 | |||
| 32 | // Create the driver, from the HAL. | ||
| 33 | let mut ep_out_buffer = [0u8; 256]; | ||
| 34 | let mut config = embassy_stm32::usb_otg::Config::default(); | ||
| 35 | config.vbus_detection = true; | ||
| 36 | let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); | ||
| 37 | |||
| 38 | // Create embassy-usb Config | ||
| 39 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 40 | config.manufacturer = Some("Embassy"); | ||
| 41 | config.product = Some("USB-serial example"); | ||
| 42 | config.serial_number = Some("12345678"); | ||
| 43 | |||
| 44 | // Required for windows compatibility. | ||
| 45 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 46 | config.device_class = 0xEF; | ||
| 47 | config.device_sub_class = 0x02; | ||
| 48 | config.device_protocol = 0x01; | ||
| 49 | config.composite_with_iads = true; | ||
| 50 | |||
| 51 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 52 | // It needs some buffers for building the descriptors. | ||
| 53 | let mut device_descriptor = [0; 256]; | ||
| 54 | let mut config_descriptor = [0; 256]; | ||
| 55 | let mut bos_descriptor = [0; 256]; | ||
| 56 | let mut control_buf = [0; 64]; | ||
| 57 | |||
| 58 | let mut state = State::new(); | ||
| 59 | |||
| 60 | let mut builder = Builder::new( | ||
| 61 | driver, | ||
| 62 | config, | ||
| 63 | &mut device_descriptor, | ||
| 64 | &mut config_descriptor, | ||
| 65 | &mut bos_descriptor, | ||
| 66 | &mut control_buf, | ||
| 67 | ); | ||
| 68 | |||
| 69 | // Create classes on the builder. | ||
| 70 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 71 | |||
| 72 | // Build the builder. | ||
| 73 | let mut usb = builder.build(); | ||
| 74 | |||
| 75 | // Run the USB device. | ||
| 76 | let usb_fut = usb.run(); | ||
| 77 | |||
| 78 | // Do stuff with the class! | ||
| 79 | let echo_fut = async { | ||
| 80 | loop { | ||
| 81 | class.wait_connection().await; | ||
| 82 | info!("Connected"); | ||
| 83 | let _ = echo(&mut class).await; | ||
| 84 | info!("Disconnected"); | ||
| 85 | } | ||
| 86 | }; | ||
| 87 | |||
| 88 | // Run everything concurrently. | ||
| 89 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 90 | join(usb_fut, echo_fut).await; | ||
| 91 | } | ||
| 92 | |||
| 93 | struct Disconnected {} | ||
| 94 | |||
| 95 | impl From<EndpointError> for Disconnected { | ||
| 96 | fn from(val: EndpointError) -> Self { | ||
| 97 | match val { | ||
| 98 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 99 | EndpointError::Disabled => Disconnected {}, | ||
| 100 | } | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 105 | let mut buf = [0; 64]; | ||
| 106 | loop { | ||
| 107 | let n = class.read_packet(&mut buf).await?; | ||
| 108 | let data = &buf[..n]; | ||
| 109 | info!("data: {:x}", data); | ||
| 110 | class.write_packet(data).await?; | ||
| 111 | } | ||
| 112 | } | ||
diff --git a/examples/stm32wb/.cargo/config.toml b/examples/stm32wb/.cargo/config.toml index fcf95741a..51c499ee7 100644 --- a/examples/stm32wb/.cargo/config.toml +++ b/examples/stm32wb/.cargo/config.toml | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] |
| 2 | # replace STM32WB55CCUx with your chip as listed in `probe-run --list-chips` | 2 | # replace STM32WB55CCUx with your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32WB55CCUx --speed 1000 --connect-under-reset" | 3 | # runner = "probe-run --chip STM32WB55RGVx --speed 1000 --connect-under-reset" |
| 4 | runner = "teleprobe local run --chip STM32WB55RG --elf" | ||
| 4 | 5 | ||
| 5 | [build] | 6 | [build] |
| 6 | target = "thumbv7em-none-eabihf" | 7 | target = "thumbv7em-none-eabihf" |
diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 3b10da0ad..becf2d3fb 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml | |||
| @@ -2,19 +2,47 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-stm32wb-examples" | 3 | name = "embassy-stm32wb-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55cc", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } |
| 12 | embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } | ||
| 11 | 13 | ||
| 12 | defmt = "0.3" | 14 | defmt = "0.3" |
| 13 | defmt-rtt = "0.3" | 15 | defmt-rtt = "0.4" |
| 14 | 16 | ||
| 15 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 17 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 16 | cortex-m-rt = "0.7.0" | 18 | cortex-m-rt = "0.7.0" |
| 17 | embedded-hal = "0.2.6" | 19 | embedded-hal = "0.2.6" |
| 18 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 20 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 19 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 21 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } |
| 20 | heapless = { version = "0.7.5", default-features = false } | 22 | heapless = { version = "0.7.5", default-features = false } |
| 23 | |||
| 24 | |||
| 25 | [features] | ||
| 26 | default = ["ble", "mac"] | ||
| 27 | mac = ["embassy-stm32-wpan/mac"] | ||
| 28 | ble = ["embassy-stm32-wpan/ble"] | ||
| 29 | |||
| 30 | [[bin]] | ||
| 31 | name = "tl_mbox_ble" | ||
| 32 | required-features = ["ble"] | ||
| 33 | |||
| 34 | [[bin]] | ||
| 35 | name = "tl_mbox_mac" | ||
| 36 | required-features = ["mac"] | ||
| 37 | |||
| 38 | [[bin]] | ||
| 39 | name = "mac_ffd" | ||
| 40 | required-features = ["mac"] | ||
| 41 | |||
| 42 | [[bin]] | ||
| 43 | name = "eddystone_beacon" | ||
| 44 | required-features = ["ble"] | ||
| 45 | |||
| 46 | [[bin]] | ||
| 47 | name = "gatt_server" | ||
| 48 | required-features = ["ble"] \ No newline at end of file | ||
diff --git a/examples/stm32wb/build.rs b/examples/stm32wb/build.rs index 30691aa97..29b3a9b2a 100644 --- a/examples/stm32wb/build.rs +++ b/examples/stm32wb/build.rs | |||
| @@ -1,35 +1,11 @@ | |||
| 1 | //! This build script copies the `memory.x` file from the crate root into | 1 | use std::error::Error; |
| 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 | 2 | ||
| 3 | fn main() -> Result<(), Box<dyn Error>> { | ||
| 32 | println!("cargo:rustc-link-arg-bins=--nmagic"); | 4 | println!("cargo:rustc-link-arg-bins=--nmagic"); |
| 33 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | 5 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); |
| 6 | println!("cargo:rerun-if-changed=link.x"); | ||
| 7 | println!("cargo:rustc-link-arg-bins=-Ttl_mbox.x"); | ||
| 34 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | 8 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); |
| 9 | |||
| 10 | Ok(()) | ||
| 35 | } | 11 | } |
diff --git a/examples/stm32wb/memory.x b/examples/stm32wb/memory.x deleted file mode 100644 index 2b4dcce34..000000000 --- a/examples/stm32wb/memory.x +++ /dev/null | |||
| @@ -1,41 +0,0 @@ | |||
| 1 | /* | ||
| 2 | The size of this file must be exactly the same as in other memory_xx.x files. | ||
| 3 | Memory size for STM32WB55xC with 256K FLASH | ||
| 4 | */ | ||
| 5 | |||
| 6 | MEMORY | ||
| 7 | { | ||
| 8 | FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K | ||
| 9 | RAM (xrw) : ORIGIN = 0x20000004, LENGTH = 191K | ||
| 10 | RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K | ||
| 11 | } | ||
| 12 | |||
| 13 | /* Place stack at the end of SRAM1 */ | ||
| 14 | _stack_start = ORIGIN(RAM) + LENGTH(RAM); | ||
| 15 | |||
| 16 | /* | ||
| 17 | * Scatter the mailbox interface memory sections in shared memory | ||
| 18 | */ | ||
| 19 | SECTIONS { | ||
| 20 | TL_REF_TABLE (NOLOAD) : { *(TL_REF_TABLE) } >RAM_SHARED | ||
| 21 | |||
| 22 | TL_DEVICE_INFO_TABLE 0x2003001c (NOLOAD) : { *(TL_DEVICE_INFO_TABLE) } >RAM_SHARED | ||
| 23 | TL_BLE_TABLE 0x2003003c (NOLOAD) : { *(TL_BLE_TABLE) } >RAM_SHARED | ||
| 24 | TL_THREAD_TABLE 0x2003004c (NOLOAD) : { *(TL_THREAD_TABLE) } >RAM_SHARED | ||
| 25 | TL_SYS_TABLE 0x20030058 (NOLOAD) : { *(TL_SYS_TABLE) } >RAM_SHARED | ||
| 26 | TL_MEM_MANAGER_TABLE 0x20030060 (NOLOAD) : { *(TL_MEM_MANAGER_TABLE) } >RAM_SHARED | ||
| 27 | TL_TRACES_TABLE 0x2003007c (NOLOAD) : { *(TL_TRACES_TABLE) } >RAM_SHARED | ||
| 28 | TL_MAC_802_15_4_TABLE 0x20030080 (NOLOAD) : { *(TL_MAC_802_15_4_TABLE) } >RAM_SHARED | ||
| 29 | |||
| 30 | HCI_ACL_DATA_BUFFER 0x20030a08 (NOLOAD) : { *(HCI_ACL_DATA_BUFFER) } >RAM_SHARED | ||
| 31 | BLE_CMD_BUFFER 0x200308fc (NOLOAD) : { *(BLE_CMD_BUFFER) } >RAM_SHARED | ||
| 32 | BLE_SPARE_EVT_BUF 0x200301a8 (NOLOAD) : { *(BLE_SPARE_EVT_BUF) } >RAM_SHARED | ||
| 33 | SYS_SPARE_EVT_BUF 0x200302b4 (NOLOAD) : { *(SYS_SPARE_EVT_BUF) } >RAM_SHARED | ||
| 34 | EVT_POOL 0x200303c0 (NOLOAD) : { *(EVT_POOL) } >RAM_SHARED | ||
| 35 | SYS_CMD_BUF 0x2003009c (NOLOAD) : { *(SYS_CMD_BUF) } >RAM_SHARED | ||
| 36 | SYSTEM_EVT_QUEUE 0x20030b28 (NOLOAD) : { *(SYSTEM_EVT_QUEUE) } >RAM_SHARED | ||
| 37 | EVT_QUEUE 0x20030b10 (NOLOAD) : { *(EVT_QUEUE) } >RAM_SHARED | ||
| 38 | CS_BUFFER 0x20030b18 (NOLOAD) : { *(CS_BUFFER) } >RAM_SHARED | ||
| 39 | TRACES_EVT_QUEUE 0x20030094 (NOLOAD) : { *(TRACES_EVT_QUEUE) } >RAM_SHARED | ||
| 40 | FREE_BUF_QUEUE 0x2003008c (NOLOAD) : { *(FREE_BUF_QUEUE) } >RAM_SHARED | ||
| 41 | } | ||
diff --git a/examples/stm32wb/src/bin/eddystone_beacon.rs b/examples/stm32wb/src/bin/eddystone_beacon.rs new file mode 100644 index 000000000..451bd7d29 --- /dev/null +++ b/examples/stm32wb/src/bin/eddystone_beacon.rs | |||
| @@ -0,0 +1,249 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::time::Duration; | ||
| 6 | |||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_stm32::bind_interrupts; | ||
| 10 | use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; | ||
| 11 | use embassy_stm32_wpan::hci::host::uart::UartHci; | ||
| 12 | use embassy_stm32_wpan::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType}; | ||
| 13 | use embassy_stm32_wpan::hci::types::AdvertisingType; | ||
| 14 | use embassy_stm32_wpan::hci::vendor::stm32wb::command::gap::{ | ||
| 15 | AdvertisingDataType, DiscoverableParameters, GapCommands, Role, | ||
| 16 | }; | ||
| 17 | use embassy_stm32_wpan::hci::vendor::stm32wb::command::gatt::GattCommands; | ||
| 18 | use embassy_stm32_wpan::hci::vendor::stm32wb::command::hal::{ConfigData, HalCommands, PowerLevel}; | ||
| 19 | use embassy_stm32_wpan::hci::BdAddr; | ||
| 20 | use embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp; | ||
| 21 | use embassy_stm32_wpan::TlMbox; | ||
| 22 | use {defmt_rtt as _, panic_probe as _}; | ||
| 23 | |||
| 24 | bind_interrupts!(struct Irqs{ | ||
| 25 | IPCC_C1_RX => ReceiveInterruptHandler; | ||
| 26 | IPCC_C1_TX => TransmitInterruptHandler; | ||
| 27 | }); | ||
| 28 | |||
| 29 | const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7; | ||
| 30 | |||
| 31 | #[embassy_executor::main] | ||
| 32 | async fn main(_spawner: Spawner) { | ||
| 33 | /* | ||
| 34 | How to make this work: | ||
| 35 | |||
| 36 | - Obtain a NUCLEO-STM32WB55 from your preferred supplier. | ||
| 37 | - Download and Install STM32CubeProgrammer. | ||
| 38 | - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from | ||
| 39 | gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x | ||
| 40 | - Open STM32CubeProgrammer | ||
| 41 | - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. | ||
| 42 | - Once complete, click connect to connect to the device. | ||
| 43 | - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". | ||
| 44 | - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file | ||
| 45 | - Select that file, the memory address, "verify download", and then "Firmware Upgrade". | ||
| 46 | - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the | ||
| 47 | stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. | ||
| 48 | - Select that file, the memory address, "verify download", and then "Firmware Upgrade". | ||
| 49 | - Select "Start Wireless Stack". | ||
| 50 | - Disconnect from the device. | ||
| 51 | - In the examples folder for stm32wb, modify the memory.x file to match your target device. | ||
| 52 | - Run this example. | ||
| 53 | |||
| 54 | Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. | ||
| 55 | */ | ||
| 56 | |||
| 57 | let p = embassy_stm32::init(Default::default()); | ||
| 58 | info!("Hello World!"); | ||
| 59 | |||
| 60 | let config = Config::default(); | ||
| 61 | let mut mbox = TlMbox::init(p.IPCC, Irqs, config); | ||
| 62 | |||
| 63 | let sys_event = mbox.sys_subsystem.read().await; | ||
| 64 | info!("sys event: {}", sys_event.payload()); | ||
| 65 | |||
| 66 | let _ = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; | ||
| 67 | |||
| 68 | info!("resetting BLE..."); | ||
| 69 | mbox.ble_subsystem.reset().await; | ||
| 70 | let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 71 | defmt::info!("{}", response); | ||
| 72 | |||
| 73 | info!("config public address..."); | ||
| 74 | mbox.ble_subsystem | ||
| 75 | .write_config_data(&ConfigData::public_address(get_bd_addr()).build()) | ||
| 76 | .await; | ||
| 77 | let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 78 | defmt::info!("{}", response); | ||
| 79 | |||
| 80 | info!("config random address..."); | ||
| 81 | mbox.ble_subsystem | ||
| 82 | .write_config_data(&ConfigData::random_address(get_random_addr()).build()) | ||
| 83 | .await; | ||
| 84 | let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 85 | defmt::info!("{}", response); | ||
| 86 | |||
| 87 | info!("config identity root..."); | ||
| 88 | mbox.ble_subsystem | ||
| 89 | .write_config_data(&ConfigData::identity_root(&get_irk()).build()) | ||
| 90 | .await; | ||
| 91 | let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 92 | defmt::info!("{}", response); | ||
| 93 | |||
| 94 | info!("config encryption root..."); | ||
| 95 | mbox.ble_subsystem | ||
| 96 | .write_config_data(&ConfigData::encryption_root(&get_erk()).build()) | ||
| 97 | .await; | ||
| 98 | let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 99 | defmt::info!("{}", response); | ||
| 100 | |||
| 101 | info!("config tx power level..."); | ||
| 102 | mbox.ble_subsystem.set_tx_power_level(PowerLevel::ZerodBm).await; | ||
| 103 | let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 104 | defmt::info!("{}", response); | ||
| 105 | |||
| 106 | info!("GATT init..."); | ||
| 107 | mbox.ble_subsystem.init_gatt().await; | ||
| 108 | let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 109 | defmt::info!("{}", response); | ||
| 110 | |||
| 111 | info!("GAP init..."); | ||
| 112 | mbox.ble_subsystem | ||
| 113 | .init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH) | ||
| 114 | .await; | ||
| 115 | let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 116 | defmt::info!("{}", response); | ||
| 117 | |||
| 118 | // info!("set scan response..."); | ||
| 119 | // mbox.ble_subsystem.le_set_scan_response_data(&[]).await.unwrap(); | ||
| 120 | // let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 121 | // defmt::info!("{}", response); | ||
| 122 | |||
| 123 | info!("set discoverable..."); | ||
| 124 | mbox.ble_subsystem | ||
| 125 | .set_discoverable(&DiscoverableParameters { | ||
| 126 | advertising_type: AdvertisingType::NonConnectableUndirected, | ||
| 127 | advertising_interval: Some((Duration::from_millis(250), Duration::from_millis(250))), | ||
| 128 | address_type: OwnAddressType::Public, | ||
| 129 | filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan, | ||
| 130 | local_name: None, | ||
| 131 | advertising_data: &[], | ||
| 132 | conn_interval: (None, None), | ||
| 133 | }) | ||
| 134 | .await | ||
| 135 | .unwrap(); | ||
| 136 | |||
| 137 | let response = mbox.ble_subsystem.read().await; | ||
| 138 | defmt::info!("{}", response); | ||
| 139 | |||
| 140 | // remove some advertisement to decrease the packet size | ||
| 141 | info!("delete tx power ad type..."); | ||
| 142 | mbox.ble_subsystem | ||
| 143 | .delete_ad_type(AdvertisingDataType::TxPowerLevel) | ||
| 144 | .await; | ||
| 145 | let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 146 | defmt::info!("{}", response); | ||
| 147 | |||
| 148 | info!("delete conn interval ad type..."); | ||
| 149 | mbox.ble_subsystem | ||
| 150 | .delete_ad_type(AdvertisingDataType::PeripheralConnectionInterval) | ||
| 151 | .await; | ||
| 152 | let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 153 | defmt::info!("{}", response); | ||
| 154 | |||
| 155 | info!("update advertising data..."); | ||
| 156 | mbox.ble_subsystem | ||
| 157 | .update_advertising_data(&eddystone_advertising_data()) | ||
| 158 | .await | ||
| 159 | .unwrap(); | ||
| 160 | let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 161 | defmt::info!("{}", response); | ||
| 162 | |||
| 163 | info!("update advertising data type..."); | ||
| 164 | mbox.ble_subsystem | ||
| 165 | .update_advertising_data(&[3, AdvertisingDataType::UuidCompleteList16 as u8, 0xaa, 0xfe]) | ||
| 166 | .await | ||
| 167 | .unwrap(); | ||
| 168 | let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 169 | defmt::info!("{}", response); | ||
| 170 | |||
| 171 | info!("update advertising data flags..."); | ||
| 172 | mbox.ble_subsystem | ||
| 173 | .update_advertising_data(&[ | ||
| 174 | 2, | ||
| 175 | AdvertisingDataType::Flags as u8, | ||
| 176 | (0x02 | 0x04) as u8, // BLE general discoverable, without BR/EDR support | ||
| 177 | ]) | ||
| 178 | .await | ||
| 179 | .unwrap(); | ||
| 180 | let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 181 | defmt::info!("{}", response); | ||
| 182 | |||
| 183 | cortex_m::asm::wfi(); | ||
| 184 | } | ||
| 185 | |||
| 186 | fn get_bd_addr() -> BdAddr { | ||
| 187 | let mut bytes = [0u8; 6]; | ||
| 188 | |||
| 189 | let lhci_info = LhciC1DeviceInformationCcrp::new(); | ||
| 190 | bytes[0] = (lhci_info.uid64 & 0xff) as u8; | ||
| 191 | bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8; | ||
| 192 | bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8; | ||
| 193 | bytes[3] = lhci_info.device_type_id; | ||
| 194 | bytes[4] = (lhci_info.st_company_id & 0xff) as u8; | ||
| 195 | bytes[5] = (lhci_info.st_company_id >> 8 & 0xff) as u8; | ||
| 196 | |||
| 197 | BdAddr(bytes) | ||
| 198 | } | ||
| 199 | |||
| 200 | fn get_random_addr() -> BdAddr { | ||
| 201 | let mut bytes = [0u8; 6]; | ||
| 202 | |||
| 203 | let lhci_info = LhciC1DeviceInformationCcrp::new(); | ||
| 204 | bytes[0] = (lhci_info.uid64 & 0xff) as u8; | ||
| 205 | bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8; | ||
| 206 | bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8; | ||
| 207 | bytes[3] = 0; | ||
| 208 | bytes[4] = 0x6E; | ||
| 209 | bytes[5] = 0xED; | ||
| 210 | |||
| 211 | BdAddr(bytes) | ||
| 212 | } | ||
| 213 | |||
| 214 | const BLE_CFG_IRK: [u8; 16] = [ | ||
| 215 | 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, | ||
| 216 | ]; | ||
| 217 | const BLE_CFG_ERK: [u8; 16] = [ | ||
| 218 | 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, | ||
| 219 | ]; | ||
| 220 | |||
| 221 | fn get_irk() -> EncryptionKey { | ||
| 222 | EncryptionKey(BLE_CFG_IRK) | ||
| 223 | } | ||
| 224 | |||
| 225 | fn get_erk() -> EncryptionKey { | ||
| 226 | EncryptionKey(BLE_CFG_ERK) | ||
| 227 | } | ||
| 228 | |||
| 229 | fn eddystone_advertising_data() -> [u8; 24] { | ||
| 230 | const EDDYSTONE_URL: &[u8] = b"www.rust-lang.com"; | ||
| 231 | |||
| 232 | let mut service_data = [0u8; 24]; | ||
| 233 | let url_len = EDDYSTONE_URL.len(); | ||
| 234 | |||
| 235 | service_data[0] = 6 + url_len as u8; | ||
| 236 | service_data[1] = AdvertisingDataType::ServiceData as u8; | ||
| 237 | |||
| 238 | // 16-bit eddystone uuid | ||
| 239 | service_data[2] = 0xaa; | ||
| 240 | service_data[3] = 0xFE; | ||
| 241 | |||
| 242 | service_data[4] = 0x10; // URL frame type | ||
| 243 | service_data[5] = 22_i8 as u8; // calibrated TX power at 0m | ||
| 244 | service_data[6] = 0x03; // eddystone url prefix = https | ||
| 245 | |||
| 246 | service_data[7..(7 + url_len)].copy_from_slice(EDDYSTONE_URL); | ||
| 247 | |||
| 248 | service_data | ||
| 249 | } | ||
diff --git a/examples/stm32wb/src/bin/gatt_server.rs b/examples/stm32wb/src/bin/gatt_server.rs new file mode 100644 index 000000000..0f6419d45 --- /dev/null +++ b/examples/stm32wb/src/bin/gatt_server.rs | |||
| @@ -0,0 +1,397 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::time::Duration; | ||
| 6 | |||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_stm32::bind_interrupts; | ||
| 10 | use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; | ||
| 11 | use embassy_stm32_wpan::hci::event::command::{CommandComplete, ReturnParameters}; | ||
| 12 | use embassy_stm32_wpan::hci::host::uart::{Packet, UartHci}; | ||
| 13 | use embassy_stm32_wpan::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType}; | ||
| 14 | use embassy_stm32_wpan::hci::types::AdvertisingType; | ||
| 15 | use embassy_stm32_wpan::hci::vendor::stm32wb::command::gap::{ | ||
| 16 | AddressType, AuthenticationRequirements, DiscoverableParameters, GapCommands, IoCapability, LocalName, Pin, Role, | ||
| 17 | SecureConnectionSupport, | ||
| 18 | }; | ||
| 19 | use embassy_stm32_wpan::hci::vendor::stm32wb::command::gatt::{ | ||
| 20 | AddCharacteristicParameters, AddServiceParameters, CharacteristicEvent, CharacteristicPermission, | ||
| 21 | CharacteristicProperty, EncryptionKeySize, GattCommands, ServiceType, UpdateCharacteristicValueParameters, Uuid, | ||
| 22 | WriteResponseParameters, | ||
| 23 | }; | ||
| 24 | use embassy_stm32_wpan::hci::vendor::stm32wb::command::hal::{ConfigData, HalCommands, PowerLevel}; | ||
| 25 | use embassy_stm32_wpan::hci::vendor::stm32wb::event::{self, AttributeHandle, Stm32Wb5xEvent}; | ||
| 26 | use embassy_stm32_wpan::hci::{BdAddr, Event}; | ||
| 27 | use embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp; | ||
| 28 | use embassy_stm32_wpan::sub::ble::Ble; | ||
| 29 | use embassy_stm32_wpan::TlMbox; | ||
| 30 | use {defmt_rtt as _, panic_probe as _}; | ||
| 31 | |||
| 32 | bind_interrupts!(struct Irqs{ | ||
| 33 | IPCC_C1_RX => ReceiveInterruptHandler; | ||
| 34 | IPCC_C1_TX => TransmitInterruptHandler; | ||
| 35 | }); | ||
| 36 | |||
| 37 | const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7; | ||
| 38 | |||
| 39 | #[embassy_executor::main] | ||
| 40 | async fn main(_spawner: Spawner) { | ||
| 41 | /* | ||
| 42 | How to make this work: | ||
| 43 | |||
| 44 | - Obtain a NUCLEO-STM32WB55 from your preferred supplier. | ||
| 45 | - Download and Install STM32CubeProgrammer. | ||
| 46 | - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from | ||
| 47 | gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x | ||
| 48 | - Open STM32CubeProgrammer | ||
| 49 | - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. | ||
| 50 | - Once complete, click connect to connect to the device. | ||
| 51 | - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". | ||
| 52 | - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file | ||
| 53 | - Select that file, the memory address, "verify download", and then "Firmware Upgrade". | ||
| 54 | - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the | ||
| 55 | stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. | ||
| 56 | - Select that file, the memory address, "verify download", and then "Firmware Upgrade". | ||
| 57 | - Select "Start Wireless Stack". | ||
| 58 | - Disconnect from the device. | ||
| 59 | - In the examples folder for stm32wb, modify the memory.x file to match your target device. | ||
| 60 | - Run this example. | ||
| 61 | |||
| 62 | Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. | ||
| 63 | */ | ||
| 64 | |||
| 65 | let p = embassy_stm32::init(Default::default()); | ||
| 66 | info!("Hello World!"); | ||
| 67 | |||
| 68 | let config = Config::default(); | ||
| 69 | let mut mbox = TlMbox::init(p.IPCC, Irqs, config); | ||
| 70 | |||
| 71 | let sys_event = mbox.sys_subsystem.read().await; | ||
| 72 | info!("sys event: {}", sys_event.payload()); | ||
| 73 | |||
| 74 | let _ = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; | ||
| 75 | |||
| 76 | info!("resetting BLE..."); | ||
| 77 | mbox.ble_subsystem.reset().await; | ||
| 78 | let response = mbox.ble_subsystem.read().await; | ||
| 79 | defmt::debug!("{}", response); | ||
| 80 | |||
| 81 | info!("config public address..."); | ||
| 82 | mbox.ble_subsystem | ||
| 83 | .write_config_data(&ConfigData::public_address(get_bd_addr()).build()) | ||
| 84 | .await; | ||
| 85 | let response = mbox.ble_subsystem.read().await; | ||
| 86 | defmt::debug!("{}", response); | ||
| 87 | |||
| 88 | info!("config random address..."); | ||
| 89 | mbox.ble_subsystem | ||
| 90 | .write_config_data(&ConfigData::random_address(get_random_addr()).build()) | ||
| 91 | .await; | ||
| 92 | let response = mbox.ble_subsystem.read().await; | ||
| 93 | defmt::debug!("{}", response); | ||
| 94 | |||
| 95 | info!("config identity root..."); | ||
| 96 | mbox.ble_subsystem | ||
| 97 | .write_config_data(&ConfigData::identity_root(&get_irk()).build()) | ||
| 98 | .await; | ||
| 99 | let response = mbox.ble_subsystem.read().await; | ||
| 100 | defmt::debug!("{}", response); | ||
| 101 | |||
| 102 | info!("config encryption root..."); | ||
| 103 | mbox.ble_subsystem | ||
| 104 | .write_config_data(&ConfigData::encryption_root(&get_erk()).build()) | ||
| 105 | .await; | ||
| 106 | let response = mbox.ble_subsystem.read().await; | ||
| 107 | defmt::debug!("{}", response); | ||
| 108 | |||
| 109 | info!("config tx power level..."); | ||
| 110 | mbox.ble_subsystem.set_tx_power_level(PowerLevel::ZerodBm).await; | ||
| 111 | let response = mbox.ble_subsystem.read().await; | ||
| 112 | defmt::debug!("{}", response); | ||
| 113 | |||
| 114 | info!("GATT init..."); | ||
| 115 | mbox.ble_subsystem.init_gatt().await; | ||
| 116 | let response = mbox.ble_subsystem.read().await; | ||
| 117 | defmt::debug!("{}", response); | ||
| 118 | |||
| 119 | info!("GAP init..."); | ||
| 120 | mbox.ble_subsystem | ||
| 121 | .init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH) | ||
| 122 | .await; | ||
| 123 | let response = mbox.ble_subsystem.read().await; | ||
| 124 | defmt::debug!("{}", response); | ||
| 125 | |||
| 126 | info!("set IO capabilities..."); | ||
| 127 | mbox.ble_subsystem.set_io_capability(IoCapability::DisplayConfirm).await; | ||
| 128 | let response = mbox.ble_subsystem.read().await; | ||
| 129 | defmt::debug!("{}", response); | ||
| 130 | |||
| 131 | info!("set authentication requirements..."); | ||
| 132 | mbox.ble_subsystem | ||
| 133 | .set_authentication_requirement(&AuthenticationRequirements { | ||
| 134 | bonding_required: false, | ||
| 135 | keypress_notification_support: false, | ||
| 136 | mitm_protection_required: false, | ||
| 137 | encryption_key_size_range: (8, 16), | ||
| 138 | fixed_pin: Pin::Requested, | ||
| 139 | identity_address_type: AddressType::Public, | ||
| 140 | secure_connection_support: SecureConnectionSupport::Optional, | ||
| 141 | }) | ||
| 142 | .await | ||
| 143 | .unwrap(); | ||
| 144 | let response = mbox.ble_subsystem.read().await; | ||
| 145 | defmt::debug!("{}", response); | ||
| 146 | |||
| 147 | info!("set scan response data..."); | ||
| 148 | mbox.ble_subsystem.le_set_scan_response_data(b"TXTX").await.unwrap(); | ||
| 149 | let response = mbox.ble_subsystem.read().await; | ||
| 150 | defmt::debug!("{}", response); | ||
| 151 | |||
| 152 | info!("set scan response data..."); | ||
| 153 | mbox.ble_subsystem.le_set_scan_response_data(b"TXTX").await.unwrap(); | ||
| 154 | let response = mbox.ble_subsystem.read().await; | ||
| 155 | defmt::debug!("{}", response); | ||
| 156 | |||
| 157 | defmt::info!("initializing services and characteristics..."); | ||
| 158 | let mut ble_context = init_gatt_services(&mut mbox.ble_subsystem).await.unwrap(); | ||
| 159 | defmt::info!("{}", ble_context); | ||
| 160 | |||
| 161 | let discovery_params = DiscoverableParameters { | ||
| 162 | advertising_type: AdvertisingType::ConnectableUndirected, | ||
| 163 | advertising_interval: Some((Duration::from_millis(100), Duration::from_millis(100))), | ||
| 164 | address_type: OwnAddressType::Public, | ||
| 165 | filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan, | ||
| 166 | local_name: Some(LocalName::Complete(b"TXTX")), | ||
| 167 | advertising_data: &[], | ||
| 168 | conn_interval: (None, None), | ||
| 169 | }; | ||
| 170 | |||
| 171 | info!("set discoverable..."); | ||
| 172 | mbox.ble_subsystem.set_discoverable(&discovery_params).await.unwrap(); | ||
| 173 | let response = mbox.ble_subsystem.read().await; | ||
| 174 | defmt::debug!("{}", response); | ||
| 175 | |||
| 176 | loop { | ||
| 177 | let response = mbox.ble_subsystem.read().await; | ||
| 178 | defmt::debug!("{}", response); | ||
| 179 | |||
| 180 | if let Ok(Packet::Event(event)) = response { | ||
| 181 | match event { | ||
| 182 | Event::LeConnectionComplete(_) => { | ||
| 183 | defmt::info!("connected"); | ||
| 184 | } | ||
| 185 | Event::DisconnectionComplete(_) => { | ||
| 186 | defmt::info!("disconnected"); | ||
| 187 | ble_context.is_subscribed = false; | ||
| 188 | mbox.ble_subsystem.set_discoverable(&discovery_params).await.unwrap(); | ||
| 189 | } | ||
| 190 | Event::Vendor(vendor_event) => match vendor_event { | ||
| 191 | Stm32Wb5xEvent::AttReadPermitRequest(read_req) => { | ||
| 192 | defmt::info!("read request received {}, allowing", read_req); | ||
| 193 | mbox.ble_subsystem.allow_read(read_req.conn_handle).await | ||
| 194 | } | ||
| 195 | Stm32Wb5xEvent::AttWritePermitRequest(write_req) => { | ||
| 196 | defmt::info!("write request received {}, allowing", write_req); | ||
| 197 | mbox.ble_subsystem | ||
| 198 | .write_response(&WriteResponseParameters { | ||
| 199 | conn_handle: write_req.conn_handle, | ||
| 200 | attribute_handle: write_req.attribute_handle, | ||
| 201 | status: Ok(()), | ||
| 202 | value: write_req.value(), | ||
| 203 | }) | ||
| 204 | .await | ||
| 205 | .unwrap() | ||
| 206 | } | ||
| 207 | Stm32Wb5xEvent::GattAttributeModified(attribute) => { | ||
| 208 | defmt::info!("{}", ble_context); | ||
| 209 | if attribute.attr_handle.0 == ble_context.chars.notify.0 + 2 { | ||
| 210 | if attribute.data()[0] == 0x01 { | ||
| 211 | defmt::info!("subscribed"); | ||
| 212 | ble_context.is_subscribed = true; | ||
| 213 | } else { | ||
| 214 | defmt::info!("unsubscribed"); | ||
| 215 | ble_context.is_subscribed = false; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | } | ||
| 219 | _ => {} | ||
| 220 | }, | ||
| 221 | _ => {} | ||
| 222 | } | ||
| 223 | } | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | fn get_bd_addr() -> BdAddr { | ||
| 228 | let mut bytes = [0u8; 6]; | ||
| 229 | |||
| 230 | let lhci_info = LhciC1DeviceInformationCcrp::new(); | ||
| 231 | bytes[0] = (lhci_info.uid64 & 0xff) as u8; | ||
| 232 | bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8; | ||
| 233 | bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8; | ||
| 234 | bytes[3] = lhci_info.device_type_id; | ||
| 235 | bytes[4] = (lhci_info.st_company_id & 0xff) as u8; | ||
| 236 | bytes[5] = (lhci_info.st_company_id >> 8 & 0xff) as u8; | ||
| 237 | |||
| 238 | BdAddr(bytes) | ||
| 239 | } | ||
| 240 | |||
| 241 | fn get_random_addr() -> BdAddr { | ||
| 242 | let mut bytes = [0u8; 6]; | ||
| 243 | |||
| 244 | let lhci_info = LhciC1DeviceInformationCcrp::new(); | ||
| 245 | bytes[0] = (lhci_info.uid64 & 0xff) as u8; | ||
| 246 | bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8; | ||
| 247 | bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8; | ||
| 248 | bytes[3] = 0; | ||
| 249 | bytes[4] = 0x6E; | ||
| 250 | bytes[5] = 0xED; | ||
| 251 | |||
| 252 | BdAddr(bytes) | ||
| 253 | } | ||
| 254 | |||
| 255 | const BLE_CFG_IRK: [u8; 16] = [ | ||
| 256 | 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, | ||
| 257 | ]; | ||
| 258 | const BLE_CFG_ERK: [u8; 16] = [ | ||
| 259 | 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, | ||
| 260 | ]; | ||
| 261 | |||
| 262 | fn get_irk() -> EncryptionKey { | ||
| 263 | EncryptionKey(BLE_CFG_IRK) | ||
| 264 | } | ||
| 265 | |||
| 266 | fn get_erk() -> EncryptionKey { | ||
| 267 | EncryptionKey(BLE_CFG_ERK) | ||
| 268 | } | ||
| 269 | |||
| 270 | #[derive(defmt::Format)] | ||
| 271 | pub struct BleContext { | ||
| 272 | pub service_handle: AttributeHandle, | ||
| 273 | pub chars: CharHandles, | ||
| 274 | pub is_subscribed: bool, | ||
| 275 | } | ||
| 276 | |||
| 277 | #[derive(defmt::Format)] | ||
| 278 | pub struct CharHandles { | ||
| 279 | pub read: AttributeHandle, | ||
| 280 | pub write: AttributeHandle, | ||
| 281 | pub notify: AttributeHandle, | ||
| 282 | } | ||
| 283 | |||
| 284 | pub async fn init_gatt_services(ble_subsystem: &mut Ble) -> Result<BleContext, ()> { | ||
| 285 | let service_handle = gatt_add_service(ble_subsystem, Uuid::Uuid16(0x500)).await?; | ||
| 286 | |||
| 287 | let read = gatt_add_char( | ||
| 288 | ble_subsystem, | ||
| 289 | service_handle, | ||
| 290 | Uuid::Uuid16(0x501), | ||
| 291 | CharacteristicProperty::READ, | ||
| 292 | Some(b"Hello from embassy!"), | ||
| 293 | ) | ||
| 294 | .await?; | ||
| 295 | |||
| 296 | let write = gatt_add_char( | ||
| 297 | ble_subsystem, | ||
| 298 | service_handle, | ||
| 299 | Uuid::Uuid16(0x502), | ||
| 300 | CharacteristicProperty::WRITE_WITHOUT_RESPONSE | CharacteristicProperty::WRITE | CharacteristicProperty::READ, | ||
| 301 | None, | ||
| 302 | ) | ||
| 303 | .await?; | ||
| 304 | |||
| 305 | let notify = gatt_add_char( | ||
| 306 | ble_subsystem, | ||
| 307 | service_handle, | ||
| 308 | Uuid::Uuid16(0x503), | ||
| 309 | CharacteristicProperty::NOTIFY | CharacteristicProperty::READ, | ||
| 310 | None, | ||
| 311 | ) | ||
| 312 | .await?; | ||
| 313 | |||
| 314 | Ok(BleContext { | ||
| 315 | service_handle, | ||
| 316 | is_subscribed: false, | ||
| 317 | chars: CharHandles { read, write, notify }, | ||
| 318 | }) | ||
| 319 | } | ||
| 320 | |||
| 321 | async fn gatt_add_service(ble_subsystem: &mut Ble, uuid: Uuid) -> Result<AttributeHandle, ()> { | ||
| 322 | ble_subsystem | ||
| 323 | .add_service(&AddServiceParameters { | ||
| 324 | uuid, | ||
| 325 | service_type: ServiceType::Primary, | ||
| 326 | max_attribute_records: 8, | ||
| 327 | }) | ||
| 328 | .await; | ||
| 329 | let response = ble_subsystem.read().await; | ||
| 330 | defmt::debug!("{}", response); | ||
| 331 | |||
| 332 | if let Ok(Packet::Event(Event::CommandComplete(CommandComplete { | ||
| 333 | return_params: | ||
| 334 | ReturnParameters::Vendor(event::command::ReturnParameters::GattAddService(event::command::GattService { | ||
| 335 | service_handle, | ||
| 336 | .. | ||
| 337 | })), | ||
| 338 | .. | ||
| 339 | }))) = response | ||
| 340 | { | ||
| 341 | Ok(service_handle) | ||
| 342 | } else { | ||
| 343 | Err(()) | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
| 347 | async fn gatt_add_char( | ||
| 348 | ble_subsystem: &mut Ble, | ||
| 349 | service_handle: AttributeHandle, | ||
| 350 | characteristic_uuid: Uuid, | ||
| 351 | characteristic_properties: CharacteristicProperty, | ||
| 352 | default_value: Option<&[u8]>, | ||
| 353 | ) -> Result<AttributeHandle, ()> { | ||
| 354 | ble_subsystem | ||
| 355 | .add_characteristic(&AddCharacteristicParameters { | ||
| 356 | service_handle, | ||
| 357 | characteristic_uuid, | ||
| 358 | characteristic_properties, | ||
| 359 | characteristic_value_len: 32, | ||
| 360 | security_permissions: CharacteristicPermission::empty(), | ||
| 361 | gatt_event_mask: CharacteristicEvent::all(), | ||
| 362 | encryption_key_size: EncryptionKeySize::with_value(7).unwrap(), | ||
| 363 | is_variable: true, | ||
| 364 | }) | ||
| 365 | .await; | ||
| 366 | let response = ble_subsystem.read().await; | ||
| 367 | defmt::debug!("{}", response); | ||
| 368 | |||
| 369 | if let Ok(Packet::Event(Event::CommandComplete(CommandComplete { | ||
| 370 | return_params: | ||
| 371 | ReturnParameters::Vendor(event::command::ReturnParameters::GattAddCharacteristic( | ||
| 372 | event::command::GattCharacteristic { | ||
| 373 | characteristic_handle, .. | ||
| 374 | }, | ||
| 375 | )), | ||
| 376 | .. | ||
| 377 | }))) = response | ||
| 378 | { | ||
| 379 | if let Some(value) = default_value { | ||
| 380 | ble_subsystem | ||
| 381 | .update_characteristic_value(&UpdateCharacteristicValueParameters { | ||
| 382 | service_handle, | ||
| 383 | characteristic_handle, | ||
| 384 | offset: 0, | ||
| 385 | value, | ||
| 386 | }) | ||
| 387 | .await | ||
| 388 | .unwrap(); | ||
| 389 | |||
| 390 | let response = ble_subsystem.read().await; | ||
| 391 | defmt::debug!("{}", response); | ||
| 392 | } | ||
| 393 | Ok(characteristic_handle) | ||
| 394 | } else { | ||
| 395 | Err(()) | ||
| 396 | } | ||
| 397 | } | ||
diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs new file mode 100644 index 000000000..bc71e29aa --- /dev/null +++ b/examples/stm32wb/src/bin/mac_ffd.rs | |||
| @@ -0,0 +1,206 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::bind_interrupts; | ||
| 8 | use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; | ||
| 9 | use embassy_stm32_wpan::mac::commands::{AssociateResponse, ResetRequest, SetRequest, StartRequest}; | ||
| 10 | use embassy_stm32_wpan::mac::event::MacEvent; | ||
| 11 | use embassy_stm32_wpan::mac::typedefs::{MacChannel, MacStatus, PanId, PibId, SecurityLevel}; | ||
| 12 | use embassy_stm32_wpan::sub::mm; | ||
| 13 | use embassy_stm32_wpan::TlMbox; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | bind_interrupts!(struct Irqs{ | ||
| 17 | IPCC_C1_RX => ReceiveInterruptHandler; | ||
| 18 | IPCC_C1_TX => TransmitInterruptHandler; | ||
| 19 | }); | ||
| 20 | |||
| 21 | #[embassy_executor::task] | ||
| 22 | async fn run_mm_queue(memory_manager: mm::MemoryManager) { | ||
| 23 | memory_manager.run_queue().await; | ||
| 24 | } | ||
| 25 | |||
| 26 | #[embassy_executor::main] | ||
| 27 | async fn main(spawner: Spawner) { | ||
| 28 | /* | ||
| 29 | How to make this work: | ||
| 30 | |||
| 31 | - Obtain a NUCLEO-STM32WB55 from your preferred supplier. | ||
| 32 | - Download and Install STM32CubeProgrammer. | ||
| 33 | - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from | ||
| 34 | gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x | ||
| 35 | - Open STM32CubeProgrammer | ||
| 36 | - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. | ||
| 37 | - Once complete, click connect to connect to the device. | ||
| 38 | - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". | ||
| 39 | - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file | ||
| 40 | - Select that file, the memory address, "verify download", and then "Firmware Upgrade". | ||
| 41 | - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the | ||
| 42 | stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. | ||
| 43 | - Select that file, the memory address, "verify download", and then "Firmware Upgrade". | ||
| 44 | - Select "Start Wireless Stack". | ||
| 45 | - Disconnect from the device. | ||
| 46 | - In the examples folder for stm32wb, modify the memory.x file to match your target device. | ||
| 47 | - Run this example. | ||
| 48 | |||
| 49 | Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. | ||
| 50 | */ | ||
| 51 | |||
| 52 | let p = embassy_stm32::init(Default::default()); | ||
| 53 | info!("Hello World!"); | ||
| 54 | |||
| 55 | let config = Config::default(); | ||
| 56 | let mbox = TlMbox::init(p.IPCC, Irqs, config); | ||
| 57 | |||
| 58 | spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); | ||
| 59 | |||
| 60 | let sys_event = mbox.sys_subsystem.read().await; | ||
| 61 | info!("sys event: {}", sys_event.payload()); | ||
| 62 | |||
| 63 | core::mem::drop(sys_event); | ||
| 64 | |||
| 65 | let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; | ||
| 66 | info!("initialized mac: {}", result); | ||
| 67 | |||
| 68 | info!("resetting"); | ||
| 69 | mbox.mac_subsystem | ||
| 70 | .send_command(&ResetRequest { | ||
| 71 | set_default_pib: true, | ||
| 72 | ..Default::default() | ||
| 73 | }) | ||
| 74 | .await | ||
| 75 | .unwrap(); | ||
| 76 | { | ||
| 77 | let evt = mbox.mac_subsystem.read().await; | ||
| 78 | defmt::info!("{:#x}", evt.mac_event()); | ||
| 79 | } | ||
| 80 | |||
| 81 | info!("setting extended address"); | ||
| 82 | let extended_address: u64 = 0xACDE480000000001; | ||
| 83 | mbox.mac_subsystem | ||
| 84 | .send_command(&SetRequest { | ||
| 85 | pib_attribute_ptr: &extended_address as *const _ as *const u8, | ||
| 86 | pib_attribute: PibId::ExtendedAddress, | ||
| 87 | }) | ||
| 88 | .await | ||
| 89 | .unwrap(); | ||
| 90 | { | ||
| 91 | let evt = mbox.mac_subsystem.read().await; | ||
| 92 | defmt::info!("{:#x}", evt.mac_event()); | ||
| 93 | } | ||
| 94 | |||
| 95 | info!("setting short address"); | ||
| 96 | let short_address: u16 = 0x1122; | ||
| 97 | mbox.mac_subsystem | ||
| 98 | .send_command(&SetRequest { | ||
| 99 | pib_attribute_ptr: &short_address as *const _ as *const u8, | ||
| 100 | pib_attribute: PibId::ShortAddress, | ||
| 101 | }) | ||
| 102 | .await | ||
| 103 | .unwrap(); | ||
| 104 | { | ||
| 105 | let evt = mbox.mac_subsystem.read().await; | ||
| 106 | defmt::info!("{:#x}", evt.mac_event()); | ||
| 107 | } | ||
| 108 | |||
| 109 | info!("setting association permit"); | ||
| 110 | let association_permit: bool = true; | ||
| 111 | mbox.mac_subsystem | ||
| 112 | .send_command(&SetRequest { | ||
| 113 | pib_attribute_ptr: &association_permit as *const _ as *const u8, | ||
| 114 | pib_attribute: PibId::AssociationPermit, | ||
| 115 | }) | ||
| 116 | .await | ||
| 117 | .unwrap(); | ||
| 118 | { | ||
| 119 | let evt = mbox.mac_subsystem.read().await; | ||
| 120 | defmt::info!("{:#x}", evt.mac_event()); | ||
| 121 | } | ||
| 122 | |||
| 123 | info!("setting TX power"); | ||
| 124 | let transmit_power: i8 = 2; | ||
| 125 | mbox.mac_subsystem | ||
| 126 | .send_command(&SetRequest { | ||
| 127 | pib_attribute_ptr: &transmit_power as *const _ as *const u8, | ||
| 128 | pib_attribute: PibId::TransmitPower, | ||
| 129 | }) | ||
| 130 | .await | ||
| 131 | .unwrap(); | ||
| 132 | { | ||
| 133 | let evt = mbox.mac_subsystem.read().await; | ||
| 134 | defmt::info!("{:#x}", evt.mac_event()); | ||
| 135 | } | ||
| 136 | |||
| 137 | info!("starting FFD device"); | ||
| 138 | mbox.mac_subsystem | ||
| 139 | .send_command(&StartRequest { | ||
| 140 | pan_id: PanId([0x1A, 0xAA]), | ||
| 141 | channel_number: MacChannel::Channel16, | ||
| 142 | beacon_order: 0x0F, | ||
| 143 | superframe_order: 0x0F, | ||
| 144 | pan_coordinator: true, | ||
| 145 | battery_life_extension: false, | ||
| 146 | ..Default::default() | ||
| 147 | }) | ||
| 148 | .await | ||
| 149 | .unwrap(); | ||
| 150 | { | ||
| 151 | let evt = mbox.mac_subsystem.read().await; | ||
| 152 | defmt::info!("{:#x}", evt.mac_event()); | ||
| 153 | } | ||
| 154 | |||
| 155 | info!("setting RX on when idle"); | ||
| 156 | let rx_on_while_idle: bool = true; | ||
| 157 | mbox.mac_subsystem | ||
| 158 | .send_command(&SetRequest { | ||
| 159 | pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8, | ||
| 160 | pib_attribute: PibId::RxOnWhenIdle, | ||
| 161 | }) | ||
| 162 | .await | ||
| 163 | .unwrap(); | ||
| 164 | { | ||
| 165 | let evt = mbox.mac_subsystem.read().await; | ||
| 166 | defmt::info!("{:#x}", evt.mac_event()); | ||
| 167 | } | ||
| 168 | |||
| 169 | loop { | ||
| 170 | let evt = mbox.mac_subsystem.read().await; | ||
| 171 | if let Ok(evt) = evt.mac_event() { | ||
| 172 | defmt::info!("parsed mac event"); | ||
| 173 | defmt::info!("{:#x}", evt); | ||
| 174 | |||
| 175 | match evt { | ||
| 176 | MacEvent::MlmeAssociateInd(association) => mbox | ||
| 177 | .mac_subsystem | ||
| 178 | .send_command(&AssociateResponse { | ||
| 179 | device_address: association.device_address, | ||
| 180 | assoc_short_address: [0x33, 0x44], | ||
| 181 | status: MacStatus::Success, | ||
| 182 | security_level: SecurityLevel::Unsecure, | ||
| 183 | ..Default::default() | ||
| 184 | }) | ||
| 185 | .await | ||
| 186 | .unwrap(), | ||
| 187 | MacEvent::McpsDataInd(data_ind) => { | ||
| 188 | let payload = data_ind.payload(); | ||
| 189 | let ref_payload = b"Hello from embassy!"; | ||
| 190 | info!("{}", payload); | ||
| 191 | |||
| 192 | if payload == ref_payload { | ||
| 193 | info!("success"); | ||
| 194 | } else { | ||
| 195 | info!("ref payload: {}", ref_payload); | ||
| 196 | } | ||
| 197 | } | ||
| 198 | _ => { | ||
| 199 | defmt::info!("other mac event"); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | } else { | ||
| 203 | defmt::info!("failed to parse mac event"); | ||
| 204 | } | ||
| 205 | } | ||
| 206 | } | ||
diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs new file mode 100644 index 000000000..7cb401d89 --- /dev/null +++ b/examples/stm32wb/src/bin/mac_rfd.rs | |||
| @@ -0,0 +1,186 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::bind_interrupts; | ||
| 8 | use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; | ||
| 9 | use embassy_stm32_wpan::mac::commands::{AssociateRequest, DataRequest, GetRequest, ResetRequest, SetRequest}; | ||
| 10 | use embassy_stm32_wpan::mac::event::MacEvent; | ||
| 11 | use embassy_stm32_wpan::mac::typedefs::{ | ||
| 12 | AddressMode, Capabilities, KeyIdMode, MacAddress, MacChannel, PanId, PibId, SecurityLevel, | ||
| 13 | }; | ||
| 14 | use embassy_stm32_wpan::sub::mm; | ||
| 15 | use embassy_stm32_wpan::TlMbox; | ||
| 16 | use {defmt_rtt as _, panic_probe as _}; | ||
| 17 | |||
| 18 | bind_interrupts!(struct Irqs{ | ||
| 19 | IPCC_C1_RX => ReceiveInterruptHandler; | ||
| 20 | IPCC_C1_TX => TransmitInterruptHandler; | ||
| 21 | }); | ||
| 22 | |||
| 23 | #[embassy_executor::task] | ||
| 24 | async fn run_mm_queue(memory_manager: mm::MemoryManager) { | ||
| 25 | memory_manager.run_queue().await; | ||
| 26 | } | ||
| 27 | |||
| 28 | #[embassy_executor::main] | ||
| 29 | async fn main(spawner: Spawner) { | ||
| 30 | /* | ||
| 31 | How to make this work: | ||
| 32 | |||
| 33 | - Obtain a NUCLEO-STM32WB55 from your preferred supplier. | ||
| 34 | - Download and Install STM32CubeProgrammer. | ||
| 35 | - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from | ||
| 36 | gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x | ||
| 37 | - Open STM32CubeProgrammer | ||
| 38 | - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. | ||
| 39 | - Once complete, click connect to connect to the device. | ||
| 40 | - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". | ||
| 41 | - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file | ||
| 42 | - Select that file, the memory address, "verify download", and then "Firmware Upgrade". | ||
| 43 | - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the | ||
| 44 | stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. | ||
| 45 | - Select that file, the memory address, "verify download", and then "Firmware Upgrade". | ||
| 46 | - Select "Start Wireless Stack". | ||
| 47 | - Disconnect from the device. | ||
| 48 | - In the examples folder for stm32wb, modify the memory.x file to match your target device. | ||
| 49 | - Run this example. | ||
| 50 | |||
| 51 | Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. | ||
| 52 | */ | ||
| 53 | |||
| 54 | let p = embassy_stm32::init(Default::default()); | ||
| 55 | info!("Hello World!"); | ||
| 56 | |||
| 57 | let config = Config::default(); | ||
| 58 | let mbox = TlMbox::init(p.IPCC, Irqs, config); | ||
| 59 | |||
| 60 | spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); | ||
| 61 | |||
| 62 | let sys_event = mbox.sys_subsystem.read().await; | ||
| 63 | info!("sys event: {}", sys_event.payload()); | ||
| 64 | |||
| 65 | core::mem::drop(sys_event); | ||
| 66 | |||
| 67 | let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; | ||
| 68 | info!("initialized mac: {}", result); | ||
| 69 | |||
| 70 | info!("resetting"); | ||
| 71 | mbox.mac_subsystem | ||
| 72 | .send_command(&ResetRequest { | ||
| 73 | set_default_pib: true, | ||
| 74 | ..Default::default() | ||
| 75 | }) | ||
| 76 | .await | ||
| 77 | .unwrap(); | ||
| 78 | { | ||
| 79 | let evt = mbox.mac_subsystem.read().await; | ||
| 80 | defmt::info!("{:#x}", evt.mac_event()); | ||
| 81 | } | ||
| 82 | |||
| 83 | info!("setting extended address"); | ||
| 84 | let extended_address: u64 = 0xACDE480000000002; | ||
| 85 | mbox.mac_subsystem | ||
| 86 | .send_command(&SetRequest { | ||
| 87 | pib_attribute_ptr: &extended_address as *const _ as *const u8, | ||
| 88 | pib_attribute: PibId::ExtendedAddress, | ||
| 89 | }) | ||
| 90 | .await | ||
| 91 | .unwrap(); | ||
| 92 | { | ||
| 93 | let evt = mbox.mac_subsystem.read().await; | ||
| 94 | defmt::info!("{:#x}", evt.mac_event()); | ||
| 95 | } | ||
| 96 | |||
| 97 | info!("getting extended address"); | ||
| 98 | mbox.mac_subsystem | ||
| 99 | .send_command(&GetRequest { | ||
| 100 | pib_attribute: PibId::ExtendedAddress, | ||
| 101 | ..Default::default() | ||
| 102 | }) | ||
| 103 | .await | ||
| 104 | .unwrap(); | ||
| 105 | |||
| 106 | { | ||
| 107 | let evt = mbox.mac_subsystem.read().await; | ||
| 108 | info!("{:#x}", evt.mac_event()); | ||
| 109 | |||
| 110 | if let Ok(MacEvent::MlmeGetCnf(evt)) = evt.mac_event() { | ||
| 111 | if evt.pib_attribute_value_len == 8 { | ||
| 112 | let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; | ||
| 113 | |||
| 114 | info!("value {:#x}", value) | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | info!("assocation request"); | ||
| 120 | let a = AssociateRequest { | ||
| 121 | channel_number: MacChannel::Channel16, | ||
| 122 | channel_page: 0, | ||
| 123 | coord_addr_mode: AddressMode::Short, | ||
| 124 | coord_address: MacAddress { short: [34, 17] }, | ||
| 125 | capability_information: Capabilities::ALLOCATE_ADDRESS, | ||
| 126 | coord_pan_id: PanId([0x1A, 0xAA]), | ||
| 127 | security_level: SecurityLevel::Unsecure, | ||
| 128 | key_id_mode: KeyIdMode::Implicite, | ||
| 129 | key_source: [0; 8], | ||
| 130 | key_index: 152, | ||
| 131 | }; | ||
| 132 | info!("{}", a); | ||
| 133 | mbox.mac_subsystem.send_command(&a).await.unwrap(); | ||
| 134 | let short_addr = { | ||
| 135 | let evt = mbox.mac_subsystem.read().await; | ||
| 136 | info!("{:#x}", evt.mac_event()); | ||
| 137 | |||
| 138 | if let Ok(MacEvent::MlmeAssociateCnf(conf)) = evt.mac_event() { | ||
| 139 | conf.assoc_short_address | ||
| 140 | } else { | ||
| 141 | defmt::panic!() | ||
| 142 | } | ||
| 143 | }; | ||
| 144 | |||
| 145 | info!("setting short address"); | ||
| 146 | mbox.mac_subsystem | ||
| 147 | .send_command(&SetRequest { | ||
| 148 | pib_attribute_ptr: &short_addr as *const _ as *const u8, | ||
| 149 | pib_attribute: PibId::ShortAddress, | ||
| 150 | }) | ||
| 151 | .await | ||
| 152 | .unwrap(); | ||
| 153 | { | ||
| 154 | let evt = mbox.mac_subsystem.read().await; | ||
| 155 | info!("{:#x}", evt.mac_event()); | ||
| 156 | } | ||
| 157 | |||
| 158 | info!("sending data"); | ||
| 159 | let data = b"Hello from embassy!"; | ||
| 160 | mbox.mac_subsystem | ||
| 161 | .send_command( | ||
| 162 | DataRequest { | ||
| 163 | src_addr_mode: AddressMode::Short, | ||
| 164 | dst_addr_mode: AddressMode::Short, | ||
| 165 | dst_pan_id: PanId([0x1A, 0xAA]), | ||
| 166 | dst_address: MacAddress::BROADCAST, | ||
| 167 | msdu_handle: 0x02, | ||
| 168 | ack_tx: 0x00, | ||
| 169 | gts_tx: false, | ||
| 170 | security_level: SecurityLevel::Unsecure, | ||
| 171 | ..Default::default() | ||
| 172 | } | ||
| 173 | .set_buffer(data), | ||
| 174 | ) | ||
| 175 | .await | ||
| 176 | .unwrap(); | ||
| 177 | { | ||
| 178 | let evt = mbox.mac_subsystem.read().await; | ||
| 179 | info!("{:#x}", evt.mac_event()); | ||
| 180 | } | ||
| 181 | |||
| 182 | loop { | ||
| 183 | let evt = mbox.mac_subsystem.read().await; | ||
| 184 | info!("{:#x}", evt.mac_event()); | ||
| 185 | } | ||
| 186 | } | ||
diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs new file mode 100644 index 000000000..9fc4b8aac --- /dev/null +++ b/examples/stm32wb/src/bin/tl_mbox.rs | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::bind_interrupts; | ||
| 8 | use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; | ||
| 9 | use embassy_stm32_wpan::TlMbox; | ||
| 10 | use embassy_time::{Duration, Timer}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | bind_interrupts!(struct Irqs{ | ||
| 14 | IPCC_C1_RX => ReceiveInterruptHandler; | ||
| 15 | IPCC_C1_TX => TransmitInterruptHandler; | ||
| 16 | }); | ||
| 17 | |||
| 18 | #[embassy_executor::main] | ||
| 19 | async fn main(_spawner: Spawner) { | ||
| 20 | /* | ||
| 21 | How to make this work: | ||
| 22 | |||
| 23 | - Obtain a NUCLEO-STM32WB55 from your preferred supplier. | ||
| 24 | - Download and Install STM32CubeProgrammer. | ||
| 25 | - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from | ||
| 26 | gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x | ||
| 27 | - Open STM32CubeProgrammer | ||
| 28 | - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. | ||
| 29 | - Once complete, click connect to connect to the device. | ||
| 30 | - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". | ||
| 31 | - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file | ||
| 32 | - Select that file, the memory address, "verify download", and then "Firmware Upgrade". | ||
| 33 | - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the | ||
| 34 | stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. | ||
| 35 | - Select that file, the memory address, "verify download", and then "Firmware Upgrade". | ||
| 36 | - Select "Start Wireless Stack". | ||
| 37 | - Disconnect from the device. | ||
| 38 | - In the examples folder for stm32wb, modify the memory.x file to match your target device. | ||
| 39 | - Run this example. | ||
| 40 | |||
| 41 | Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. | ||
| 42 | */ | ||
| 43 | |||
| 44 | let p = embassy_stm32::init(Default::default()); | ||
| 45 | info!("Hello World!"); | ||
| 46 | |||
| 47 | let config = Config::default(); | ||
| 48 | let mbox = TlMbox::init(p.IPCC, Irqs, config); | ||
| 49 | |||
| 50 | loop { | ||
| 51 | let wireless_fw_info = mbox.sys_subsystem.wireless_fw_info(); | ||
| 52 | match wireless_fw_info { | ||
| 53 | None => info!("not yet initialized"), | ||
| 54 | Some(fw_info) => { | ||
| 55 | let version_major = fw_info.version_major(); | ||
| 56 | let version_minor = fw_info.version_minor(); | ||
| 57 | let subversion = fw_info.subversion(); | ||
| 58 | |||
| 59 | let sram2a_size = fw_info.sram2a_size(); | ||
| 60 | let sram2b_size = fw_info.sram2b_size(); | ||
| 61 | |||
| 62 | info!( | ||
| 63 | "version {}.{}.{} - SRAM2a {} - SRAM2b {}", | ||
| 64 | version_major, version_minor, subversion, sram2a_size, sram2b_size | ||
| 65 | ); | ||
| 66 | |||
| 67 | break; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | Timer::after(Duration::from_millis(50)).await; | ||
| 72 | } | ||
| 73 | |||
| 74 | info!("Test OK"); | ||
| 75 | cortex_m::asm::bkpt(); | ||
| 76 | } | ||
diff --git a/examples/stm32wb/src/bin/tl_mbox_ble.rs b/examples/stm32wb/src/bin/tl_mbox_ble.rs new file mode 100644 index 000000000..90349422e --- /dev/null +++ b/examples/stm32wb/src/bin/tl_mbox_ble.rs | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::bind_interrupts; | ||
| 8 | use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; | ||
| 9 | use embassy_stm32_wpan::TlMbox; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | bind_interrupts!(struct Irqs{ | ||
| 13 | IPCC_C1_RX => ReceiveInterruptHandler; | ||
| 14 | IPCC_C1_TX => TransmitInterruptHandler; | ||
| 15 | }); | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | /* | ||
| 20 | How to make this work: | ||
| 21 | |||
| 22 | - Obtain a NUCLEO-STM32WB55 from your preferred supplier. | ||
| 23 | - Download and Install STM32CubeProgrammer. | ||
| 24 | - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from | ||
| 25 | gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x | ||
| 26 | - Open STM32CubeProgrammer | ||
| 27 | - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. | ||
| 28 | - Once complete, click connect to connect to the device. | ||
| 29 | - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". | ||
| 30 | - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file | ||
| 31 | - Select that file, the memory address, "verify download", and then "Firmware Upgrade". | ||
| 32 | - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the | ||
| 33 | stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. | ||
| 34 | - Select that file, the memory address, "verify download", and then "Firmware Upgrade". | ||
| 35 | - Select "Start Wireless Stack". | ||
| 36 | - Disconnect from the device. | ||
| 37 | - In the examples folder for stm32wb, modify the memory.x file to match your target device. | ||
| 38 | - Run this example. | ||
| 39 | |||
| 40 | Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. | ||
| 41 | */ | ||
| 42 | |||
| 43 | let p = embassy_stm32::init(Default::default()); | ||
| 44 | info!("Hello World!"); | ||
| 45 | |||
| 46 | let config = Config::default(); | ||
| 47 | let mbox = TlMbox::init(p.IPCC, Irqs, config); | ||
| 48 | |||
| 49 | let sys_event = mbox.sys_subsystem.read().await; | ||
| 50 | info!("sys event: {}", sys_event.payload()); | ||
| 51 | |||
| 52 | let _ = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; | ||
| 53 | |||
| 54 | info!("starting ble..."); | ||
| 55 | mbox.ble_subsystem.tl_write(0x0c, &[]).await; | ||
| 56 | |||
| 57 | info!("waiting for ble..."); | ||
| 58 | let ble_event = mbox.ble_subsystem.tl_read().await; | ||
| 59 | |||
| 60 | info!("ble event: {}", ble_event.payload()); | ||
| 61 | |||
| 62 | info!("Test OK"); | ||
| 63 | cortex_m::asm::bkpt(); | ||
| 64 | } | ||
diff --git a/examples/stm32wb/src/bin/tl_mbox_mac.rs b/examples/stm32wb/src/bin/tl_mbox_mac.rs new file mode 100644 index 000000000..5931c392b --- /dev/null +++ b/examples/stm32wb/src/bin/tl_mbox_mac.rs | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::bind_interrupts; | ||
| 8 | use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; | ||
| 9 | use embassy_stm32_wpan::sub::mm; | ||
| 10 | use embassy_stm32_wpan::TlMbox; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | bind_interrupts!(struct Irqs{ | ||
| 14 | IPCC_C1_RX => ReceiveInterruptHandler; | ||
| 15 | IPCC_C1_TX => TransmitInterruptHandler; | ||
| 16 | }); | ||
| 17 | |||
| 18 | #[embassy_executor::task] | ||
| 19 | async fn run_mm_queue(memory_manager: mm::MemoryManager) { | ||
| 20 | memory_manager.run_queue().await; | ||
| 21 | } | ||
| 22 | |||
| 23 | #[embassy_executor::main] | ||
| 24 | async fn main(spawner: Spawner) { | ||
| 25 | /* | ||
| 26 | How to make this work: | ||
| 27 | |||
| 28 | - Obtain a NUCLEO-STM32WB55 from your preferred supplier. | ||
| 29 | - Download and Install STM32CubeProgrammer. | ||
| 30 | - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from | ||
| 31 | gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x | ||
| 32 | - Open STM32CubeProgrammer | ||
| 33 | - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. | ||
| 34 | - Once complete, click connect to connect to the device. | ||
| 35 | - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". | ||
| 36 | - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file | ||
| 37 | - Select that file, the memory address, "verify download", and then "Firmware Upgrade". | ||
| 38 | - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the | ||
| 39 | stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. | ||
| 40 | - Select that file, the memory address, "verify download", and then "Firmware Upgrade". | ||
| 41 | - Select "Start Wireless Stack". | ||
| 42 | - Disconnect from the device. | ||
| 43 | - In the examples folder for stm32wb, modify the memory.x file to match your target device. | ||
| 44 | - Run this example. | ||
| 45 | |||
| 46 | Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. | ||
| 47 | */ | ||
| 48 | |||
| 49 | let p = embassy_stm32::init(Default::default()); | ||
| 50 | info!("Hello World!"); | ||
| 51 | |||
| 52 | let config = Config::default(); | ||
| 53 | let mbox = TlMbox::init(p.IPCC, Irqs, config); | ||
| 54 | |||
| 55 | spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); | ||
| 56 | |||
| 57 | let sys_event = mbox.sys_subsystem.read().await; | ||
| 58 | info!("sys event: {}", sys_event.payload()); | ||
| 59 | |||
| 60 | core::mem::drop(sys_event); | ||
| 61 | |||
| 62 | let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; | ||
| 63 | info!("initialized mac: {}", result); | ||
| 64 | |||
| 65 | // | ||
| 66 | // info!("starting ble..."); | ||
| 67 | // mbox.ble_subsystem.t_write(0x0c, &[]).await; | ||
| 68 | // | ||
| 69 | // info!("waiting for ble..."); | ||
| 70 | // let ble_event = mbox.ble_subsystem.tl_read().await; | ||
| 71 | // | ||
| 72 | // info!("ble event: {}", ble_event.payload()); | ||
| 73 | |||
| 74 | info!("Test OK"); | ||
| 75 | cortex_m::asm::bkpt(); | ||
| 76 | } | ||
diff --git a/examples/stm32wl/.cargo/config.toml b/examples/stm32wl/.cargo/config.toml index e395d75b4..ee416fcbc 100644 --- a/examples/stm32wl/.cargo/config.toml +++ b/examples/stm32wl/.cargo/config.toml | |||
| @@ -1,9 +1,9 @@ | |||
| 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-run --list-chips` | 2 | # replace your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-run --chip STM32WLE5JCIx" | 3 | runner = "probe-rs run --chip STM32WLE5JCIx" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv7em-none-eabihf" | 6 | target = "thumbv7em-none-eabi" |
| 7 | 7 | ||
| 8 | [env] | 8 | [env] |
| 9 | DEFMT_LOG = "trace" | 9 | DEFMT_LOG = "trace" |
diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 5f6679f4b..e2c66f456 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml | |||
| @@ -2,24 +2,30 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-stm32wl-examples" | 3 | name = "embassy-stm32wl-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [dependencies] | 7 | [dependencies] |
| 7 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-32768hz"] } | 10 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti", "chrono"] } |
| 12 | embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } | ||
| 11 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } | 13 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } |
| 12 | 14 | lora-phy = { version = "1" } | |
| 13 | lorawan-device = { version = "0.7.1", default-features = false, features = ["async"] } | 15 | lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"] } |
| 14 | lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"] } | 16 | lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"] } |
| 15 | 17 | ||
| 16 | defmt = "0.3" | 18 | defmt = "0.3" |
| 17 | defmt-rtt = "0.3" | 19 | defmt-rtt = "0.4" |
| 18 | 20 | ||
| 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 22 | cortex-m-rt = "0.7.0" |
| 21 | embedded-hal = "0.2.6" | 23 | embedded-hal = "0.2.6" |
| 22 | embedded-storage = "0.3.0" | 24 | embedded-storage = "0.3.0" |
| 23 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 25 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 24 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 26 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } |
| 25 | heapless = { version = "0.7.5", default-features = false } | 27 | heapless = { version = "0.7.5", default-features = false } |
| 28 | chrono = { version = "^0.4", default-features = false } | ||
| 29 | |||
| 30 | [patch.crates-io] | ||
| 31 | lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } | ||
diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs index eb7489760..5e52d49ec 100644 --- a/examples/stm32wl/src/bin/flash.rs +++ b/examples/stm32wl/src/bin/flash.rs | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | use defmt::{info, unwrap}; | 5 | use defmt::{info, unwrap}; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::flash::Flash; | 7 | use embassy_stm32::flash::Flash; |
| 8 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 9 | ||
| 11 | #[embassy_executor::main] | 10 | #[embassy_executor::main] |
| @@ -15,27 +14,27 @@ async fn main(_spawner: Spawner) { | |||
| 15 | 14 | ||
| 16 | const ADDR: u32 = 0x36000; | 15 | const ADDR: u32 = 0x36000; |
| 17 | 16 | ||
| 18 | let mut f = Flash::unlock(p.FLASH); | 17 | let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region; |
| 19 | 18 | ||
| 20 | info!("Reading..."); | 19 | info!("Reading..."); |
| 21 | let mut buf = [0u8; 8]; | 20 | let mut buf = [0u8; 8]; |
| 22 | unwrap!(f.read(ADDR, &mut buf)); | 21 | unwrap!(f.blocking_read(ADDR, &mut buf)); |
| 23 | info!("Read: {=[u8]:x}", buf); | 22 | info!("Read: {=[u8]:x}", buf); |
| 24 | 23 | ||
| 25 | info!("Erasing..."); | 24 | info!("Erasing..."); |
| 26 | unwrap!(f.erase(ADDR, ADDR + 2048)); | 25 | unwrap!(f.blocking_erase(ADDR, ADDR + 2048)); |
| 27 | 26 | ||
| 28 | info!("Reading..."); | 27 | info!("Reading..."); |
| 29 | let mut buf = [0u8; 8]; | 28 | let mut buf = [0u8; 8]; |
| 30 | unwrap!(f.read(ADDR, &mut buf)); | 29 | unwrap!(f.blocking_read(ADDR, &mut buf)); |
| 31 | info!("Read: {=[u8]:x}", buf); | 30 | info!("Read: {=[u8]:x}", buf); |
| 32 | 31 | ||
| 33 | info!("Writing..."); | 32 | info!("Writing..."); |
| 34 | unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); | 33 | unwrap!(f.blocking_write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); |
| 35 | 34 | ||
| 36 | info!("Reading..."); | 35 | info!("Reading..."); |
| 37 | let mut buf = [0u8; 8]; | 36 | let mut buf = [0u8; 8]; |
| 38 | unwrap!(f.read(ADDR, &mut buf)); | 37 | unwrap!(f.blocking_read(ADDR, &mut buf)); |
| 39 | info!("Read: {=[u8]:x}", buf); | 38 | info!("Read: {=[u8]:x}", buf); |
| 40 | assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); | 39 | assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); |
| 41 | } | 40 | } |
diff --git a/examples/stm32wl/src/bin/lora_lorawan.rs b/examples/stm32wl/src/bin/lora_lorawan.rs new file mode 100644 index 000000000..805d21418 --- /dev/null +++ b/examples/stm32wl/src/bin/lora_lorawan.rs | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | //! This example runs on a STM32WL board, which has a builtin Semtech Sx1262 radio. | ||
| 2 | //! It demonstrates LoRaWAN join functionality. | ||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | #![macro_use] | ||
| 6 | #![feature(type_alias_impl_trait, async_fn_in_trait)] | ||
| 7 | |||
| 8 | use defmt::info; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_lora::iv::{InterruptHandler, Stm32wlInterfaceVariant}; | ||
| 11 | use embassy_lora::LoraTimer; | ||
| 12 | use embassy_stm32::gpio::{Level, Output, Pin, Speed}; | ||
| 13 | use embassy_stm32::rng::Rng; | ||
| 14 | use embassy_stm32::spi::Spi; | ||
| 15 | use embassy_stm32::{bind_interrupts, pac}; | ||
| 16 | use embassy_time::Delay; | ||
| 17 | use lora_phy::mod_params::*; | ||
| 18 | use lora_phy::sx1261_2::SX1261_2; | ||
| 19 | use lora_phy::LoRa; | ||
| 20 | use lorawan::default_crypto::DefaultFactory as Crypto; | ||
| 21 | use lorawan_device::async_device::lora_radio::LoRaRadio; | ||
| 22 | use lorawan_device::async_device::{region, Device, JoinMode}; | ||
| 23 | use {defmt_rtt as _, panic_probe as _}; | ||
| 24 | |||
| 25 | const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region | ||
| 26 | |||
| 27 | bind_interrupts!(struct Irqs{ | ||
| 28 | SUBGHZ_RADIO => InterruptHandler; | ||
| 29 | }); | ||
| 30 | |||
| 31 | #[embassy_executor::main] | ||
| 32 | async fn main(_spawner: Spawner) { | ||
| 33 | let mut config = embassy_stm32::Config::default(); | ||
| 34 | config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; | ||
| 35 | config.rcc.enable_lsi = true; // enable RNG | ||
| 36 | let p = embassy_stm32::init(config); | ||
| 37 | |||
| 38 | pac::RCC.ccipr().modify(|w| w.set_rngsel(0b01)); | ||
| 39 | |||
| 40 | let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2); | ||
| 41 | |||
| 42 | // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx | ||
| 43 | let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High); | ||
| 44 | let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High); | ||
| 45 | let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High); | ||
| 46 | let iv = Stm32wlInterfaceVariant::new(Irqs, None, Some(ctrl2)).unwrap(); | ||
| 47 | |||
| 48 | let mut delay = Delay; | ||
| 49 | |||
| 50 | let lora = { | ||
| 51 | match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), true, &mut delay).await { | ||
| 52 | Ok(l) => l, | ||
| 53 | Err(err) => { | ||
| 54 | info!("Radio error = {}", err); | ||
| 55 | return; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | }; | ||
| 59 | let radio = LoRaRadio::new(lora); | ||
| 60 | let region: region::Configuration = region::Configuration::new(LORAWAN_REGION); | ||
| 61 | let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); | ||
| 62 | |||
| 63 | defmt::info!("Joining LoRaWAN network"); | ||
| 64 | |||
| 65 | // TODO: Adjust the EUI and Keys according to your network credentials | ||
| 66 | match device | ||
| 67 | .join(&JoinMode::OTAA { | ||
| 68 | deveui: [0, 0, 0, 0, 0, 0, 0, 0], | ||
| 69 | appeui: [0, 0, 0, 0, 0, 0, 0, 0], | ||
| 70 | appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], | ||
| 71 | }) | ||
| 72 | .await | ||
| 73 | { | ||
| 74 | Ok(()) => defmt::info!("LoRaWAN network joined"), | ||
| 75 | Err(err) => { | ||
| 76 | info!("Radio error = {}", err); | ||
| 77 | return; | ||
| 78 | } | ||
| 79 | }; | ||
| 80 | } | ||
diff --git a/examples/stm32wl/src/bin/lora_p2p_receive.rs b/examples/stm32wl/src/bin/lora_p2p_receive.rs new file mode 100644 index 000000000..d3f051b1c --- /dev/null +++ b/examples/stm32wl/src/bin/lora_p2p_receive.rs | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | //! This example runs on the STM32WL board, which has a builtin Semtech Sx1262 radio. | ||
| 2 | //! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example. | ||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | #![macro_use] | ||
| 6 | #![feature(type_alias_impl_trait, async_fn_in_trait)] | ||
| 7 | |||
| 8 | use defmt::info; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_lora::iv::{InterruptHandler, Stm32wlInterfaceVariant}; | ||
| 11 | use embassy_stm32::bind_interrupts; | ||
| 12 | use embassy_stm32::gpio::{Level, Output, Pin, Speed}; | ||
| 13 | use embassy_stm32::spi::Spi; | ||
| 14 | use embassy_time::{Delay, Duration, Timer}; | ||
| 15 | use lora_phy::mod_params::*; | ||
| 16 | use lora_phy::sx1261_2::SX1261_2; | ||
| 17 | use lora_phy::LoRa; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||
| 21 | |||
| 22 | bind_interrupts!(struct Irqs{ | ||
| 23 | SUBGHZ_RADIO => InterruptHandler; | ||
| 24 | }); | ||
| 25 | |||
| 26 | #[embassy_executor::main] | ||
| 27 | async fn main(_spawner: Spawner) { | ||
| 28 | let mut config = embassy_stm32::Config::default(); | ||
| 29 | config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; | ||
| 30 | let p = embassy_stm32::init(config); | ||
| 31 | |||
| 32 | let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2); | ||
| 33 | |||
| 34 | // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx | ||
| 35 | let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High); | ||
| 36 | let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High); | ||
| 37 | let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High); | ||
| 38 | let iv = Stm32wlInterfaceVariant::new(Irqs, None, Some(ctrl2)).unwrap(); | ||
| 39 | |||
| 40 | let mut delay = Delay; | ||
| 41 | |||
| 42 | let mut lora = { | ||
| 43 | match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), false, &mut delay).await { | ||
| 44 | Ok(l) => l, | ||
| 45 | Err(err) => { | ||
| 46 | info!("Radio error = {}", err); | ||
| 47 | return; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | }; | ||
| 51 | |||
| 52 | let mut debug_indicator = Output::new(p.PB9, Level::Low, Speed::Low); | ||
| 53 | let mut start_indicator = Output::new(p.PB15, Level::Low, Speed::Low); | ||
| 54 | |||
| 55 | start_indicator.set_high(); | ||
| 56 | Timer::after(Duration::from_secs(5)).await; | ||
| 57 | start_indicator.set_low(); | ||
| 58 | |||
| 59 | let mut receiving_buffer = [00u8; 100]; | ||
| 60 | |||
| 61 | let mdltn_params = { | ||
| 62 | match lora.create_modulation_params( | ||
| 63 | SpreadingFactor::_10, | ||
| 64 | Bandwidth::_250KHz, | ||
| 65 | CodingRate::_4_8, | ||
| 66 | LORA_FREQUENCY_IN_HZ, | ||
| 67 | ) { | ||
| 68 | Ok(mp) => mp, | ||
| 69 | Err(err) => { | ||
| 70 | info!("Radio error = {}", err); | ||
| 71 | return; | ||
| 72 | } | ||
| 73 | } | ||
| 74 | }; | ||
| 75 | |||
| 76 | let rx_pkt_params = { | ||
| 77 | match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) { | ||
| 78 | Ok(pp) => pp, | ||
| 79 | Err(err) => { | ||
| 80 | info!("Radio error = {}", err); | ||
| 81 | return; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | }; | ||
| 85 | |||
| 86 | match lora | ||
| 87 | .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, true, false, 0, 0x00ffffffu32) | ||
| 88 | .await | ||
| 89 | { | ||
| 90 | Ok(()) => {} | ||
| 91 | Err(err) => { | ||
| 92 | info!("Radio error = {}", err); | ||
| 93 | return; | ||
| 94 | } | ||
| 95 | }; | ||
| 96 | |||
| 97 | loop { | ||
| 98 | receiving_buffer = [00u8; 100]; | ||
| 99 | match lora.rx(&rx_pkt_params, &mut receiving_buffer).await { | ||
| 100 | Ok((received_len, _rx_pkt_status)) => { | ||
| 101 | if (received_len == 3) | ||
| 102 | && (receiving_buffer[0] == 0x01u8) | ||
| 103 | && (receiving_buffer[1] == 0x02u8) | ||
| 104 | && (receiving_buffer[2] == 0x03u8) | ||
| 105 | { | ||
| 106 | info!("rx successful"); | ||
| 107 | debug_indicator.set_high(); | ||
| 108 | Timer::after(Duration::from_secs(5)).await; | ||
| 109 | debug_indicator.set_low(); | ||
| 110 | } else { | ||
| 111 | info!("rx unknown packet"); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | Err(err) => info!("rx unsuccessful = {}", err), | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
diff --git a/examples/stm32wl/src/bin/lora_p2p_send.rs b/examples/stm32wl/src/bin/lora_p2p_send.rs new file mode 100644 index 000000000..fc5205c85 --- /dev/null +++ b/examples/stm32wl/src/bin/lora_p2p_send.rs | |||
| @@ -0,0 +1,100 @@ | |||
| 1 | //! This example runs on a STM32WL board, which has a builtin Semtech Sx1262 radio. | ||
| 2 | //! It demonstrates LORA P2P send functionality. | ||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | #![macro_use] | ||
| 6 | #![feature(type_alias_impl_trait, async_fn_in_trait)] | ||
| 7 | |||
| 8 | use defmt::info; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_lora::iv::{InterruptHandler, Stm32wlInterfaceVariant}; | ||
| 11 | use embassy_stm32::bind_interrupts; | ||
| 12 | use embassy_stm32::gpio::{Level, Output, Pin, Speed}; | ||
| 13 | use embassy_stm32::spi::Spi; | ||
| 14 | use embassy_time::Delay; | ||
| 15 | use lora_phy::mod_params::*; | ||
| 16 | use lora_phy::sx1261_2::SX1261_2; | ||
| 17 | use lora_phy::LoRa; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||
| 21 | |||
| 22 | bind_interrupts!(struct Irqs{ | ||
| 23 | SUBGHZ_RADIO => InterruptHandler; | ||
| 24 | }); | ||
| 25 | |||
| 26 | #[embassy_executor::main] | ||
| 27 | async fn main(_spawner: Spawner) { | ||
| 28 | let mut config = embassy_stm32::Config::default(); | ||
| 29 | config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; | ||
| 30 | let p = embassy_stm32::init(config); | ||
| 31 | |||
| 32 | let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2); | ||
| 33 | |||
| 34 | // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx | ||
| 35 | let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High); | ||
| 36 | let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High); | ||
| 37 | let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High); | ||
| 38 | let iv = Stm32wlInterfaceVariant::new(Irqs, None, Some(ctrl2)).unwrap(); | ||
| 39 | |||
| 40 | let mut delay = Delay; | ||
| 41 | |||
| 42 | let mut lora = { | ||
| 43 | match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), false, &mut delay).await { | ||
| 44 | Ok(l) => l, | ||
| 45 | Err(err) => { | ||
| 46 | info!("Radio error = {}", err); | ||
| 47 | return; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | }; | ||
| 51 | |||
| 52 | let mdltn_params = { | ||
| 53 | match lora.create_modulation_params( | ||
| 54 | SpreadingFactor::_10, | ||
| 55 | Bandwidth::_250KHz, | ||
| 56 | CodingRate::_4_8, | ||
| 57 | LORA_FREQUENCY_IN_HZ, | ||
| 58 | ) { | ||
| 59 | Ok(mp) => mp, | ||
| 60 | Err(err) => { | ||
| 61 | info!("Radio error = {}", err); | ||
| 62 | return; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | }; | ||
| 66 | |||
| 67 | let mut tx_pkt_params = { | ||
| 68 | match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) { | ||
| 69 | Ok(pp) => pp, | ||
| 70 | Err(err) => { | ||
| 71 | info!("Radio error = {}", err); | ||
| 72 | return; | ||
| 73 | } | ||
| 74 | } | ||
| 75 | }; | ||
| 76 | |||
| 77 | match lora.prepare_for_tx(&mdltn_params, 20, false).await { | ||
| 78 | Ok(()) => {} | ||
| 79 | Err(err) => { | ||
| 80 | info!("Radio error = {}", err); | ||
| 81 | return; | ||
| 82 | } | ||
| 83 | }; | ||
| 84 | |||
| 85 | let buffer = [0x01u8, 0x02u8, 0x03u8]; | ||
| 86 | match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await { | ||
| 87 | Ok(()) => { | ||
| 88 | info!("TX DONE"); | ||
| 89 | } | ||
| 90 | Err(err) => { | ||
| 91 | info!("Radio error = {}", err); | ||
| 92 | return; | ||
| 93 | } | ||
| 94 | }; | ||
| 95 | |||
| 96 | match lora.sleep(&mut delay).await { | ||
| 97 | Ok(()) => info!("Sleep successful"), | ||
| 98 | Err(err) => info!("Sleep unsuccessful = {}", err), | ||
| 99 | } | ||
| 100 | } | ||
diff --git a/examples/stm32wl/src/bin/lorawan.rs b/examples/stm32wl/src/bin/lorawan.rs deleted file mode 100644 index 7e8a8946d..000000000 --- a/examples/stm32wl/src/bin/lorawan.rs +++ /dev/null | |||
| @@ -1,78 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![macro_use] | ||
| 4 | #![allow(dead_code)] | ||
| 5 | #![feature(generic_associated_types)] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | |||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_lora::stm32wl::*; | ||
| 10 | use embassy_lora::LoraTimer; | ||
| 11 | use embassy_stm32::dma::NoDma; | ||
| 12 | use embassy_stm32::gpio::{Level, Output, Pin, Speed}; | ||
| 13 | use embassy_stm32::rng::Rng; | ||
| 14 | use embassy_stm32::subghz::*; | ||
| 15 | use embassy_stm32::{interrupt, pac}; | ||
| 16 | use lorawan::default_crypto::DefaultFactory as Crypto; | ||
| 17 | use lorawan_device::async_device::{region, Device, JoinMode}; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | #[embassy_executor::main] | ||
| 21 | async fn main(_spawner: Spawner) { | ||
| 22 | let mut config = embassy_stm32::Config::default(); | ||
| 23 | config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16; | ||
| 24 | config.rcc.enable_lsi = true; | ||
| 25 | let p = embassy_stm32::init(config); | ||
| 26 | |||
| 27 | unsafe { pac::RCC.ccipr().modify(|w| w.set_rngsel(0b01)) } | ||
| 28 | |||
| 29 | let ctrl1 = Output::new(p.PC3.degrade(), Level::High, Speed::High); | ||
| 30 | let ctrl2 = Output::new(p.PC4.degrade(), Level::High, Speed::High); | ||
| 31 | let ctrl3 = Output::new(p.PC5.degrade(), Level::High, Speed::High); | ||
| 32 | let rfs = RadioSwitch::new(ctrl1, ctrl2, ctrl3); | ||
| 33 | |||
| 34 | let radio = SubGhz::new(p.SUBGHZSPI, p.PA5, p.PA7, p.PA6, NoDma, NoDma); | ||
| 35 | |||
| 36 | let irq = interrupt::take!(SUBGHZ_RADIO); | ||
| 37 | static mut RADIO_STATE: SubGhzState<'static> = SubGhzState::new(); | ||
| 38 | let radio = unsafe { SubGhzRadio::new(&mut RADIO_STATE, radio, rfs, irq) }; | ||
| 39 | |||
| 40 | let mut region: region::Configuration = region::EU868::default().into(); | ||
| 41 | |||
| 42 | // NOTE: This is specific for TTN, as they have a special RX1 delay | ||
| 43 | region.set_receive_delay1(5000); | ||
| 44 | |||
| 45 | let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer, Rng::new(p.RNG)); | ||
| 46 | |||
| 47 | // Depending on network, this might be part of JOIN | ||
| 48 | device.set_datarate(region::DR::_0); // SF12 | ||
| 49 | |||
| 50 | // device.set_datarate(region::DR::_1); // SF11 | ||
| 51 | // device.set_datarate(region::DR::_2); // SF10 | ||
| 52 | // device.set_datarate(region::DR::_3); // SF9 | ||
| 53 | // device.set_datarate(region::DR::_4); // SF8 | ||
| 54 | // device.set_datarate(region::DR::_5); // SF7 | ||
| 55 | |||
| 56 | defmt::info!("Joining LoRaWAN network"); | ||
| 57 | |||
| 58 | // TODO: Adjust the EUI and Keys according to your network credentials | ||
| 59 | device | ||
| 60 | .join(&JoinMode::OTAA { | ||
| 61 | deveui: [0, 0, 0, 0, 0, 0, 0, 0], | ||
| 62 | appeui: [0, 0, 0, 0, 0, 0, 0, 0], | ||
| 63 | appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], | ||
| 64 | }) | ||
| 65 | .await | ||
| 66 | .ok() | ||
| 67 | .unwrap(); | ||
| 68 | defmt::info!("LoRaWAN network joined"); | ||
| 69 | |||
| 70 | let mut rx: [u8; 255] = [0; 255]; | ||
| 71 | defmt::info!("Sending 'PING'"); | ||
| 72 | let len = device.send_recv(b"PING", &mut rx[..], 1, true).await.ok().unwrap(); | ||
| 73 | if len > 0 { | ||
| 74 | defmt::info!("Message sent, received downlink: {:?}", &rx[..len]); | ||
| 75 | } else { | ||
| 76 | defmt::info!("Message sent!"); | ||
| 77 | } | ||
| 78 | } | ||
diff --git a/examples/stm32wl/src/bin/random.rs b/examples/stm32wl/src/bin/random.rs new file mode 100644 index 000000000..d8562fca5 --- /dev/null +++ b/examples/stm32wl/src/bin/random.rs | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::pac; | ||
| 8 | use embassy_stm32::rng::Rng; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let mut config = embassy_stm32::Config::default(); | ||
| 14 | config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; | ||
| 15 | config.rcc.enable_lsi = true; //Needed for RNG to work | ||
| 16 | |||
| 17 | let p = embassy_stm32::init(config); | ||
| 18 | pac::RCC.ccipr().modify(|w| { | ||
| 19 | w.set_rngsel(0b01); | ||
| 20 | }); | ||
| 21 | |||
| 22 | info!("Hello World!"); | ||
| 23 | |||
| 24 | let mut rng = Rng::new(p.RNG); | ||
| 25 | |||
| 26 | let mut buf = [0u8; 16]; | ||
| 27 | unwrap!(rng.async_fill_bytes(&mut buf).await); | ||
| 28 | info!("random bytes: {:02x}", buf); | ||
| 29 | |||
| 30 | loop {} | ||
| 31 | } | ||
diff --git a/examples/stm32wl/src/bin/rtc.rs b/examples/stm32wl/src/bin/rtc.rs new file mode 100644 index 000000000..e11825499 --- /dev/null +++ b/examples/stm32wl/src/bin/rtc.rs | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use chrono::{NaiveDate, NaiveDateTime}; | ||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_stm32::rcc::{self, ClockSrc}; | ||
| 9 | use embassy_stm32::rtc::{Rtc, RtcConfig}; | ||
| 10 | use embassy_stm32::Config; | ||
| 11 | use embassy_time::{Duration, Timer}; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | #[embassy_executor::main] | ||
| 15 | async fn main(_spawner: Spawner) { | ||
| 16 | let p = { | ||
| 17 | let mut config = Config::default(); | ||
| 18 | config.rcc.mux = ClockSrc::HSE32; | ||
| 19 | config.rcc.rtc_mux = rcc::RtcClockSource::LSE32; | ||
| 20 | config.rcc.enable_rtc_apb = true; | ||
| 21 | embassy_stm32::init(config) | ||
| 22 | }; | ||
| 23 | info!("Hello World!"); | ||
| 24 | |||
| 25 | let now = NaiveDate::from_ymd_opt(2020, 5, 15) | ||
| 26 | .unwrap() | ||
| 27 | .and_hms_opt(10, 30, 15) | ||
| 28 | .unwrap(); | ||
| 29 | |||
| 30 | let mut rtc = Rtc::new( | ||
| 31 | p.RTC, | ||
| 32 | RtcConfig::default().clock_config(embassy_stm32::rtc::RtcClockSource::LSE), | ||
| 33 | ); | ||
| 34 | info!("Got RTC! {:?}", now.timestamp()); | ||
| 35 | |||
| 36 | rtc.set_datetime(now.into()).expect("datetime not set"); | ||
| 37 | |||
| 38 | // In reality the delay would be much longer | ||
| 39 | Timer::after(Duration::from_millis(20000)).await; | ||
| 40 | |||
| 41 | let then: NaiveDateTime = rtc.now().unwrap().into(); | ||
| 42 | info!("Got RTC! {:?}", then.timestamp()); | ||
| 43 | } | ||
diff --git a/examples/stm32wl/src/bin/subghz.rs b/examples/stm32wl/src/bin/subghz.rs deleted file mode 100644 index c5e9bb597..000000000 --- a/examples/stm32wl/src/bin/subghz.rs +++ /dev/null | |||
| @@ -1,119 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![macro_use] | ||
| 4 | #![allow(dead_code)] | ||
| 5 | #![feature(generic_associated_types)] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_stm32::dma::NoDma; | ||
| 11 | use embassy_stm32::exti::ExtiInput; | ||
| 12 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; | ||
| 13 | use embassy_stm32::interrupt; | ||
| 14 | use embassy_stm32::interrupt::{Interrupt, InterruptExt}; | ||
| 15 | use embassy_stm32::subghz::*; | ||
| 16 | use embassy_sync::signal::Signal; | ||
| 17 | use {defmt_rtt as _, panic_probe as _}; | ||
| 18 | |||
| 19 | const PING_DATA: &str = "PING"; | ||
| 20 | const DATA_LEN: u8 = PING_DATA.len() as u8; | ||
| 21 | const PING_DATA_BYTES: &[u8] = PING_DATA.as_bytes(); | ||
| 22 | const PREAMBLE_LEN: u16 = 5 * 8; | ||
| 23 | |||
| 24 | const RF_FREQ: RfFreq = RfFreq::from_frequency(867_500_000); | ||
| 25 | |||
| 26 | const SYNC_WORD: [u8; 8] = [0x79, 0x80, 0x0C, 0xC0, 0x29, 0x95, 0xF8, 0x4A]; | ||
| 27 | const SYNC_WORD_LEN: u8 = SYNC_WORD.len() as u8; | ||
| 28 | const SYNC_WORD_LEN_BITS: u8 = SYNC_WORD_LEN * 8; | ||
| 29 | |||
| 30 | const TX_BUF_OFFSET: u8 = 128; | ||
| 31 | const RX_BUF_OFFSET: u8 = 0; | ||
| 32 | const LORA_PACKET_PARAMS: LoRaPacketParams = LoRaPacketParams::new() | ||
| 33 | .set_crc_en(true) | ||
| 34 | .set_preamble_len(PREAMBLE_LEN) | ||
| 35 | .set_payload_len(DATA_LEN) | ||
| 36 | .set_invert_iq(false) | ||
| 37 | .set_header_type(HeaderType::Fixed); | ||
| 38 | |||
| 39 | const LORA_MOD_PARAMS: LoRaModParams = LoRaModParams::new() | ||
| 40 | .set_bw(LoRaBandwidth::Bw125) | ||
| 41 | .set_cr(CodingRate::Cr45) | ||
| 42 | .set_ldro_en(true) | ||
| 43 | .set_sf(SpreadingFactor::Sf7); | ||
| 44 | |||
| 45 | // configuration for +10 dBm output power | ||
| 46 | // see table 35 "PA optimal setting and operating modes" | ||
| 47 | const PA_CONFIG: PaConfig = PaConfig::new().set_pa_duty_cycle(0x1).set_hp_max(0x0).set_pa(PaSel::Lp); | ||
| 48 | |||
| 49 | const TCXO_MODE: TcxoMode = TcxoMode::new() | ||
| 50 | .set_txco_trim(TcxoTrim::Volts1pt7) | ||
| 51 | .set_timeout(Timeout::from_duration_sat(core::time::Duration::from_millis(10))); | ||
| 52 | |||
| 53 | const TX_PARAMS: TxParams = TxParams::new().set_power(0x0D).set_ramp_time(RampTime::Micros40); | ||
| 54 | |||
| 55 | #[embassy_executor::main] | ||
| 56 | async fn main(_spawner: Spawner) { | ||
| 57 | let mut config = embassy_stm32::Config::default(); | ||
| 58 | config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; | ||
| 59 | let p = embassy_stm32::init(config); | ||
| 60 | |||
| 61 | let mut led1 = Output::new(p.PB15, Level::High, Speed::Low); | ||
| 62 | let mut led2 = Output::new(p.PB9, Level::Low, Speed::Low); | ||
| 63 | let mut led3 = Output::new(p.PB11, Level::Low, Speed::Low); | ||
| 64 | |||
| 65 | let button = Input::new(p.PA0, Pull::Up); | ||
| 66 | let mut pin = ExtiInput::new(button, p.EXTI0); | ||
| 67 | |||
| 68 | static IRQ_SIGNAL: Signal<()> = Signal::new(); | ||
| 69 | let radio_irq = interrupt::take!(SUBGHZ_RADIO); | ||
| 70 | radio_irq.set_handler(|_| { | ||
| 71 | IRQ_SIGNAL.signal(()); | ||
| 72 | unsafe { interrupt::SUBGHZ_RADIO::steal() }.disable(); | ||
| 73 | }); | ||
| 74 | |||
| 75 | let mut radio = SubGhz::new(p.SUBGHZSPI, p.PA5, p.PA7, p.PA6, NoDma, NoDma); | ||
| 76 | |||
| 77 | defmt::info!("Radio ready for use"); | ||
| 78 | |||
| 79 | led1.set_low(); | ||
| 80 | |||
| 81 | led2.set_high(); | ||
| 82 | |||
| 83 | unwrap!(radio.set_standby(StandbyClk::Rc)); | ||
| 84 | unwrap!(radio.set_tcxo_mode(&TCXO_MODE)); | ||
| 85 | unwrap!(radio.set_standby(StandbyClk::Hse)); | ||
| 86 | unwrap!(radio.set_regulator_mode(RegMode::Ldo)); | ||
| 87 | unwrap!(radio.set_buffer_base_address(TX_BUF_OFFSET, RX_BUF_OFFSET)); | ||
| 88 | unwrap!(radio.set_pa_config(&PA_CONFIG)); | ||
| 89 | unwrap!(radio.set_pa_ocp(Ocp::Max60m)); | ||
| 90 | unwrap!(radio.set_tx_params(&TX_PARAMS)); | ||
| 91 | unwrap!(radio.set_packet_type(PacketType::LoRa)); | ||
| 92 | unwrap!(radio.set_lora_sync_word(LoRaSyncWord::Public)); | ||
| 93 | unwrap!(radio.set_lora_mod_params(&LORA_MOD_PARAMS)); | ||
| 94 | unwrap!(radio.set_lora_packet_params(&LORA_PACKET_PARAMS)); | ||
| 95 | unwrap!(radio.calibrate_image(CalibrateImage::ISM_863_870)); | ||
| 96 | unwrap!(radio.set_rf_frequency(&RF_FREQ)); | ||
| 97 | |||
| 98 | defmt::info!("Status: {:?}", unwrap!(radio.status())); | ||
| 99 | |||
| 100 | led2.set_low(); | ||
| 101 | |||
| 102 | loop { | ||
| 103 | pin.wait_for_rising_edge().await; | ||
| 104 | led3.set_high(); | ||
| 105 | unwrap!(radio.set_irq_cfg(&CfgIrq::new().irq_enable_all(Irq::TxDone))); | ||
| 106 | unwrap!(radio.write_buffer(TX_BUF_OFFSET, PING_DATA_BYTES)); | ||
| 107 | unwrap!(radio.set_tx(Timeout::DISABLED)); | ||
| 108 | |||
| 109 | radio_irq.enable(); | ||
| 110 | IRQ_SIGNAL.wait().await; | ||
| 111 | |||
| 112 | let (_, irq_status) = unwrap!(radio.irq_status()); | ||
| 113 | if irq_status & Irq::TxDone.mask() != 0 { | ||
| 114 | defmt::info!("TX done"); | ||
| 115 | } | ||
| 116 | unwrap!(radio.clear_irq_status(irq_status)); | ||
| 117 | led3.set_low(); | ||
| 118 | } | ||
| 119 | } | ||
diff --git a/examples/stm32wl/src/bin/uart_async.rs b/examples/stm32wl/src/bin/uart_async.rs new file mode 100644 index 000000000..07b0f9d2c --- /dev/null +++ b/examples/stm32wl/src/bin/uart_async.rs | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::usart::{Config, InterruptHandler, Uart}; | ||
| 8 | use embassy_stm32::{bind_interrupts, peripherals}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | bind_interrupts!(struct Irqs{ | ||
| 12 | USART1 => InterruptHandler<peripherals::USART1>; | ||
| 13 | LPUART1 => InterruptHandler<peripherals::LPUART1>; | ||
| 14 | }); | ||
| 15 | |||
| 16 | /* | ||
| 17 | Pass Incoming data from LPUART1 to USART1 | ||
| 18 | Example is written for the LoRa-E5 mini v1.0, | ||
| 19 | but can be surely changed for your needs. | ||
| 20 | */ | ||
| 21 | #[embassy_executor::main] | ||
| 22 | async fn main(_spawner: Spawner) { | ||
| 23 | let mut config = embassy_stm32::Config::default(); | ||
| 24 | config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; | ||
| 25 | let p = embassy_stm32::init(config); | ||
| 26 | |||
| 27 | defmt::info!("Starting system"); | ||
| 28 | |||
| 29 | let mut config1 = Config::default(); | ||
| 30 | config1.baudrate = 9600; | ||
| 31 | |||
| 32 | let mut config2 = Config::default(); | ||
| 33 | config2.baudrate = 9600; | ||
| 34 | |||
| 35 | //RX/TX connected to USB/UART Bridge on LoRa-E5 mini v1.0 | ||
| 36 | let mut usart1 = Uart::new(p.USART1, p.PB7, p.PB6, Irqs, p.DMA1_CH3, p.DMA1_CH4, config1); | ||
| 37 | |||
| 38 | //RX1/TX1 (LPUART) on LoRa-E5 mini v1.0 | ||
| 39 | let mut usart2 = Uart::new(p.LPUART1, p.PC0, p.PC1, Irqs, p.DMA1_CH5, p.DMA1_CH6, config2); | ||
| 40 | |||
| 41 | unwrap!(usart1.write(b"Hello Embassy World!\r\n").await); | ||
| 42 | unwrap!(usart2.write(b"Hello Embassy World!\r\n").await); | ||
| 43 | |||
| 44 | let mut buf = [0u8; 300]; | ||
| 45 | loop { | ||
| 46 | let result = usart2.read_until_idle(&mut buf).await; | ||
| 47 | match result { | ||
| 48 | Ok(size) => { | ||
| 49 | match usart1.write(&buf[0..size]).await { | ||
| 50 | Ok(()) => { | ||
| 51 | //Write suc. | ||
| 52 | } | ||
| 53 | Err(..) => { | ||
| 54 | //Wasn't able to write | ||
| 55 | } | ||
| 56 | } | ||
| 57 | } | ||
| 58 | Err(_err) => { | ||
| 59 | //Ignore eg. framing errors | ||
| 60 | } | ||
| 61 | } | ||
| 62 | } | ||
| 63 | } | ||
diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index ea61fb921..3679e3857 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml | |||
| @@ -2,14 +2,15 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-wasm-example" | 3 | name = "embassy-wasm-example" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | [lib] | 7 | [lib] |
| 7 | crate-type = ["cdylib"] | 8 | crate-type = ["cdylib"] |
| 8 | 9 | ||
| 9 | [dependencies] | 10 | [dependencies] |
| 10 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } | 11 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["log"] } |
| 11 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "wasm", "nightly", "integrated-timers"] } | 12 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "nightly", "integrated-timers"] } |
| 12 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "wasm", "nightly"] } | 13 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["log", "wasm", "nightly"] } |
| 13 | 14 | ||
| 14 | wasm-logger = "0.2.0" | 15 | wasm-logger = "0.2.0" |
| 15 | wasm-bindgen = "0.2" | 16 | wasm-bindgen = "0.2" |
diff --git a/examples/wasm/src/lib.rs b/examples/wasm/src/lib.rs index d44c020b6..edfe8bafc 100644 --- a/examples/wasm/src/lib.rs +++ b/examples/wasm/src/lib.rs | |||
| @@ -1,5 +1,4 @@ | |||
| 1 | #![feature(type_alias_impl_trait)] | 1 | #![feature(type_alias_impl_trait)] |
| 2 | #![allow(incomplete_features)] | ||
| 3 | 2 | ||
| 4 | use embassy_executor::Spawner; | 3 | use embassy_executor::Spawner; |
| 5 | use embassy_time::{Duration, Timer}; | 4 | use embassy_time::{Duration, Timer}; |
