diff options
| author | Mathias <[email protected]> | 2023-02-13 14:55:15 +0100 |
|---|---|---|
| committer | Mathias <[email protected]> | 2023-02-13 14:55:15 +0100 |
| commit | 218b44652c149f895919b606a660b6eff30e8177 (patch) | |
| tree | 5f985f6edd12926a6f374c17a3a0c3a4226088e7 /examples | |
| parent | 86113e199f37fe0888979608a08bfdaf21bff19a (diff) | |
| parent | 41a563aae3e474955892b27487e185f5f486f525 (diff) | |
Rebase on master
Diffstat (limited to 'examples')
178 files changed, 4196 insertions, 852 deletions
diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index a5d82b601..888993255 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml | |||
| @@ -8,14 +8,19 @@ license = "MIT OR Apache-2.0" | |||
| 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } | 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } | 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } |
| 11 | 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"] } |
| 12 | embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot" } | ||
| 12 | embassy-boot-nrf = { version = "0.1.0", path = "../../../../embassy-boot/nrf" } | 13 | embassy-boot-nrf = { version = "0.1.0", path = "../../../../embassy-boot/nrf" } |
| 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } | 14 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } |
| 14 | 15 | ||
| 15 | defmt = { version = "0.3", optional = true } | 16 | defmt = { version = "0.3", optional = true } |
| 16 | defmt-rtt = { version = "0.3", optional = true } | 17 | defmt-rtt = { version = "0.4", optional = true } |
| 17 | panic-reset = { version = "0.1.1" } | 18 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 19 | embedded-hal = { version = "0.2.6" } |
| 19 | 20 | ||
| 20 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 21 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 21 | 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"] \ No newline at end of file | ||
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 7a404a914..090a05b23 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs | |||
| @@ -8,6 +8,7 @@ use embassy_embedded_hal::adapter::BlockingAsync; | |||
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; | 9 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; |
| 10 | use embassy_nrf::nvmc::Nvmc; | 10 | use embassy_nrf::nvmc::Nvmc; |
| 11 | use embassy_nrf::wdt::{self, Watchdog}; | ||
| 11 | use panic_reset as _; | 12 | use panic_reset as _; |
| 12 | 13 | ||
| 13 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | 14 | static APP_B: &[u8] = include_bytes!("../../b.bin"); |
| @@ -15,11 +16,34 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); | |||
| 15 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 16 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 17 | let p = embassy_nrf::init(Default::default()); | 18 | let p = embassy_nrf::init(Default::default()); |
| 19 | |||
| 18 | let mut button = Input::new(p.P0_11, Pull::Up); | 20 | let mut button = Input::new(p.P0_11, Pull::Up); |
| 19 | let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); | 21 | let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); |
| 22 | |||
| 20 | //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); | 23 | //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); |
| 21 | //let mut button = Input::new(p.P1_02, Pull::Up); | 24 | //let mut button = Input::new(p.P1_02, Pull::Up); |
| 22 | 25 | ||
| 26 | // nRF91 DK | ||
| 27 | // let mut led = Output::new(p.P0_02, Level::Low, OutputDrive::Standard); | ||
| 28 | // let mut button = Input::new(p.P0_06, Pull::Up); | ||
| 29 | |||
| 30 | // The following code block illustrates how to obtain a watchdog that is configured | ||
| 31 | // as per the existing watchdog. Ordinarily, we'd use the handle returned to "pet" the | ||
| 32 | // watchdog periodically. If we don't, and we're not going to for this example, then | ||
| 33 | // the watchdog will cause the device to reset as per its configured timeout in the bootloader. | ||
| 34 | // This helps is avoid a situation where new firmware might be bad and block our executor. | ||
| 35 | // If firmware is bad in this way then the bootloader will revert to any previous version. | ||
| 36 | let wdt_config = wdt::Config::try_new(&p.WDT).unwrap(); | ||
| 37 | let (_wdt, [_wdt_handle]) = match Watchdog::try_new(p.WDT, wdt_config) { | ||
| 38 | Ok(x) => x, | ||
| 39 | Err(_) => { | ||
| 40 | // Watchdog already active with the wrong number of handles, waiting for it to timeout... | ||
| 41 | loop { | ||
| 42 | cortex_m::asm::wfe(); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | }; | ||
| 46 | |||
| 23 | let nvmc = Nvmc::new(p.NVMC); | 47 | let nvmc = Nvmc::new(p.NVMC); |
| 24 | let mut nvmc = BlockingAsync::new(nvmc); | 48 | let mut nvmc = BlockingAsync::new(nvmc); |
| 25 | 49 | ||
diff --git a/examples/boot/application/nrf/src/bin/b.rs b/examples/boot/application/nrf/src/bin/b.rs index 1373f277d..15ebce5fa 100644 --- a/examples/boot/application/nrf/src/bin/b.rs +++ b/examples/boot/application/nrf/src/bin/b.rs | |||
| @@ -12,7 +12,10 @@ use panic_reset as _; | |||
| 12 | async fn main(_spawner: Spawner) { | 12 | async fn main(_spawner: Spawner) { |
| 13 | let p = embassy_nrf::init(Default::default()); | 13 | let p = embassy_nrf::init(Default::default()); |
| 14 | 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); |
| 15 | //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); | ||
| 16 | 19 | ||
| 17 | loop { | 20 | loop { |
| 18 | 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..edbd0a867 --- /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-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..8d826790b --- /dev/null +++ b/examples/boot/application/rp/Cargo.toml | |||
| @@ -0,0 +1,33 @@ | |||
| 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.1.0", path = "../../../../embassy-sync" } | ||
| 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } | ||
| 10 | embassy-time = { version = "0.1.0", 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" } | ||
| 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 = ["critical-section-single-core"] } | ||
| 22 | cortex-m-rt = "0.7.0" | ||
| 23 | |||
| 24 | [features] | ||
| 25 | default = ["panic-reset"] | ||
| 26 | debug = [ | ||
| 27 | "embassy-rp/defmt", | ||
| 28 | "embassy-boot-rp/defmt", | ||
| 29 | "panic-probe" | ||
| 30 | ] | ||
| 31 | |||
| 32 | [profile.release] | ||
| 33 | 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..e3ac634c2 --- /dev/null +++ b/examples/boot/application/rp/src/bin/a.rs | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt_rtt as _; | ||
| 6 | use embassy_boot_rp::*; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_rp::flash::Flash; | ||
| 9 | use embassy_rp::gpio::{Level, Output}; | ||
| 10 | use embassy_rp::watchdog::Watchdog; | ||
| 11 | use embassy_time::{Duration, Timer}; | ||
| 12 | #[cfg(feature = "panic-probe")] | ||
| 13 | use panic_probe as _; | ||
| 14 | #[cfg(feature = "panic-reset")] | ||
| 15 | use panic_reset as _; | ||
| 16 | |||
| 17 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | ||
| 18 | const FLASH_SIZE: usize = 2 * 1024 * 1024; | ||
| 19 | |||
| 20 | #[embassy_executor::main] | ||
| 21 | async fn main(_s: Spawner) { | ||
| 22 | let p = embassy_rp::init(Default::default()); | ||
| 23 | let mut led = Output::new(p.PIN_25, Level::Low); | ||
| 24 | |||
| 25 | // Override bootloader watchdog | ||
| 26 | let mut watchdog = Watchdog::new(p.WATCHDOG); | ||
| 27 | watchdog.start(Duration::from_secs(8)); | ||
| 28 | |||
| 29 | let mut flash: Flash<_, FLASH_SIZE> = Flash::new(p.FLASH); | ||
| 30 | |||
| 31 | let mut updater = FirmwareUpdater::default(); | ||
| 32 | |||
| 33 | Timer::after(Duration::from_secs(5)).await; | ||
| 34 | watchdog.feed(); | ||
| 35 | led.set_high(); | ||
| 36 | let mut offset = 0; | ||
| 37 | let mut buf: AlignedBuffer<4096> = AlignedBuffer([0; 4096]); | ||
| 38 | defmt::info!("preparing update"); | ||
| 39 | let mut writer = updater | ||
| 40 | .prepare_update_blocking(&mut flash) | ||
| 41 | .map_err(|e| defmt::warn!("E: {:?}", defmt::Debug2Format(&e))) | ||
| 42 | .unwrap(); | ||
| 43 | defmt::info!("writer created, starting write"); | ||
| 44 | for chunk in APP_B.chunks(4096) { | ||
| 45 | buf.0[..chunk.len()].copy_from_slice(chunk); | ||
| 46 | defmt::info!("writing block at offset {}", offset); | ||
| 47 | writer | ||
| 48 | .write_block_blocking(offset, &buf.0[..], &mut flash, 256) | ||
| 49 | .unwrap(); | ||
| 50 | offset += chunk.len(); | ||
| 51 | } | ||
| 52 | watchdog.feed(); | ||
| 53 | defmt::info!("firmware written, marking update"); | ||
| 54 | updater.mark_updated_blocking(&mut flash, &mut buf.0[..1]).unwrap(); | ||
| 55 | Timer::after(Duration::from_secs(2)).await; | ||
| 56 | led.set_low(); | ||
| 57 | defmt::info!("update marked, resetting"); | ||
| 58 | cortex_m::peripheral::SCB::sys_reset(); | ||
| 59 | } | ||
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.toml b/examples/boot/application/stm32f3/Cargo.toml index 3a1843562..aa279fb76 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml | |||
| @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 | |||
| 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "0.3", optional = true } |
| 16 | defmt-rtt = { version = "0.3", optional = true } | 16 | defmt-rtt = { version = "0.4", optional = true } |
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | 19 | ||
diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 8d9c4490e..1ec0643a6 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml | |||
| @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 | |||
| 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "0.3", optional = true } |
| 16 | defmt-rtt = { version = "0.3", optional = true } | 16 | defmt-rtt = { version = "0.4", optional = true } |
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | 19 | ||
diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index b4314aa72..a4eefe2a5 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml | |||
| @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 | |||
| 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "0.3", optional = true } |
| 16 | defmt-rtt = { version = "0.3", optional = true } | 16 | defmt-rtt = { version = "0.4", optional = true } |
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | 19 | ||
diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index a17d336a6..36eada29b 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml | |||
| @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 | |||
| 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "0.3", optional = true } |
| 16 | defmt-rtt = { version = "0.3", optional = true } | 16 | defmt-rtt = { version = "0.4", optional = true } |
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | 19 | ||
diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 683f2c860..67efda748 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml | |||
| @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 | |||
| 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "0.3", optional = true } |
| 16 | defmt-rtt = { version = "0.3", optional = true } | 16 | defmt-rtt = { version = "0.4", optional = true } |
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | 19 | ||
diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index b879c0d76..4b2e02dd2 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml | |||
| @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 | |||
| 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "0.3", optional = true } |
| 16 | defmt-rtt = { version = "0.3", optional = true } | 16 | defmt-rtt = { version = "0.4", optional = true } |
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | 19 | ||
diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index e3bc0e49c..fecbfc51d 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml | |||
| @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 | |||
| 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "0.3", optional = true } |
| 16 | defmt-rtt = { version = "0.3", optional = true } | 16 | defmt-rtt = { version = "0.4", optional = true } |
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | 19 | ||
diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml index b417a40d1..8a6f53643 100644 --- a/examples/boot/bootloader/nrf/Cargo.toml +++ b/examples/boot/bootloader/nrf/Cargo.toml | |||
| @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" | |||
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | defmt = { version = "0.3", optional = true } | 9 | defmt = { version = "0.3", optional = true } |
| 10 | defmt-rtt = { version = "0.3", optional = true } | 10 | defmt-rtt = { version = "0.4", optional = true } |
| 11 | 11 | ||
| 12 | embassy-nrf = { path = "../../../../embassy-nrf", default-features = false, features = ["nightly"] } | 12 | embassy-nrf = { path = "../../../../embassy-nrf", default-features = false, features = ["nightly"] } |
| 13 | embassy-boot-nrf = { path = "../../../../embassy-boot/nrf", default-features = false } | 13 | embassy-boot-nrf = { path = "../../../../embassy-boot/nrf", default-features = false } |
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 8266206b3..aca3b857a 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs | |||
| @@ -6,6 +6,7 @@ use cortex_m_rt::{entry, exception}; | |||
| 6 | use defmt_rtt as _; | 6 | use defmt_rtt as _; |
| 7 | use embassy_boot_nrf::*; | 7 | use embassy_boot_nrf::*; |
| 8 | use embassy_nrf::nvmc::Nvmc; | 8 | use embassy_nrf::nvmc::Nvmc; |
| 9 | use embassy_nrf::wdt; | ||
| 9 | 10 | ||
| 10 | #[entry] | 11 | #[entry] |
| 11 | fn main() -> ! { | 12 | fn main() -> ! { |
| @@ -20,8 +21,14 @@ fn main() -> ! { | |||
| 20 | */ | 21 | */ |
| 21 | 22 | ||
| 22 | let mut bl = BootLoader::default(); | 23 | let mut bl = BootLoader::default(); |
| 24 | |||
| 25 | let mut wdt_config = wdt::Config::default(); | ||
| 26 | wdt_config.timeout_ticks = 32768 * 5; // timeout seconds | ||
| 27 | wdt_config.run_during_sleep = true; | ||
| 28 | wdt_config.run_during_debug_halt = false; | ||
| 29 | |||
| 23 | let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::<_, 4096>::new( | 30 | let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::<_, 4096>::new( |
| 24 | WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, 5), | 31 | WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config), |
| 25 | ))); | 32 | ))); |
| 26 | unsafe { bl.load(start) } | 33 | unsafe { bl.load(start) } |
| 27 | } | 34 | } |
diff --git a/examples/boot/bootloader/rp/.cargo/config.toml b/examples/boot/bootloader/rp/.cargo/config.toml new file mode 100644 index 000000000..18bd4dfe8 --- /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-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..c0b576cff --- /dev/null +++ b/examples/boot/bootloader/rp/Cargo.toml | |||
| @@ -0,0 +1,31 @@ | |||
| 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", default-features = false, features = ["nightly"] } | ||
| 13 | embassy-boot-rp = { path = "../../../../embassy-boot/rp", default-features = false } | ||
| 14 | embassy-time = { path = "../../../../embassy-time", features = ["nightly"] } | ||
| 15 | |||
| 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | ||
| 17 | cortex-m-rt = { version = "0.7" } | ||
| 18 | embedded-storage = "0.3.0" | ||
| 19 | embedded-storage-async = "0.3.0" | ||
| 20 | cfg-if = "1.0.0" | ||
| 21 | |||
| 22 | [features] | ||
| 23 | defmt = [ | ||
| 24 | "dep:defmt", | ||
| 25 | "embassy-boot-rp/defmt", | ||
| 26 | "embassy-rp/defmt", | ||
| 27 | ] | ||
| 28 | debug = ["defmt-rtt", "defmt"] | ||
| 29 | |||
| 30 | [profile.release] | ||
| 31 | debug = true | ||
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..fb7f0522b --- /dev/null +++ b/examples/boot/bootloader/rp/src/main.rs | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use cortex_m_rt::{entry, exception}; | ||
| 5 | #[cfg(feature = "defmt")] | ||
| 6 | use defmt_rtt as _; | ||
| 7 | use embassy_boot_rp::*; | ||
| 8 | use embassy_rp::flash::ERASE_SIZE; | ||
| 9 | use embassy_time::Duration; | ||
| 10 | |||
| 11 | const FLASH_SIZE: usize = 2 * 1024 * 1024; | ||
| 12 | |||
| 13 | #[entry] | ||
| 14 | fn main() -> ! { | ||
| 15 | let p = embassy_rp::init(Default::default()); | ||
| 16 | |||
| 17 | // Uncomment this if you are debugging the bootloader with debugger/RTT attached, | ||
| 18 | // as it prevents a hard fault when accessing flash 'too early' after boot. | ||
| 19 | /* | ||
| 20 | for i in 0..10000000 { | ||
| 21 | cortex_m::asm::nop(); | ||
| 22 | } | ||
| 23 | */ | ||
| 24 | |||
| 25 | let mut bl: BootLoader = BootLoader::default(); | ||
| 26 | let flash = WatchdogFlash::<FLASH_SIZE>::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8)); | ||
| 27 | let mut flash = BootFlash::<_, ERASE_SIZE>::new(flash); | ||
| 28 | let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); | ||
| 29 | core::mem::drop(flash); | ||
| 30 | |||
| 31 | unsafe { bl.load(start) } | ||
| 32 | } | ||
| 33 | |||
| 34 | #[no_mangle] | ||
| 35 | #[cfg_attr(target_os = "none", link_section = ".HardFault.user")] | ||
| 36 | unsafe extern "C" fn HardFault() { | ||
| 37 | cortex_m::peripheral::SCB::sys_reset(); | ||
| 38 | } | ||
| 39 | |||
| 40 | #[exception] | ||
| 41 | unsafe fn DefaultHandler(_: i16) -> ! { | ||
| 42 | const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32; | ||
| 43 | let irqn = core::ptr::read_volatile(SCB_ICSR) as u8 as i16 - 16; | ||
| 44 | |||
| 45 | panic!("DefaultHandler #{:?}", irqn); | ||
| 46 | } | ||
| 47 | |||
| 48 | #[panic_handler] | ||
| 49 | fn panic(_info: &core::panic::PanicInfo) -> ! { | ||
| 50 | cortex_m::asm::udf(); | ||
| 51 | } | ||
diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index 4ddd1c99c..be659e02a 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml | |||
| @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" | |||
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | defmt = { version = "0.3", optional = true } | 9 | defmt = { version = "0.3", optional = true } |
| 10 | defmt-rtt = { version = "0.3", optional = true } | 10 | defmt-rtt = { version = "0.4", optional = true } |
| 11 | 11 | ||
| 12 | embassy-stm32 = { path = "../../../../embassy-stm32", default-features = false, features = ["nightly"] } | 12 | embassy-stm32 = { path = "../../../../embassy-stm32", default-features = false, features = ["nightly"] } |
| 13 | embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32", default-features = false } | 13 | embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32", default-features = false } |
diff --git a/examples/nrf/src/bin/usb_ethernet.rs b/examples/nrf/src/bin/usb_ethernet.rs deleted file mode 100644 index de93a2b45..000000000 --- a/examples/nrf/src/bin/usb_ethernet.rs +++ /dev/null | |||
| @@ -1,274 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::mem; | ||
| 6 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 7 | use core::task::Waker; | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_net::tcp::TcpSocket; | ||
| 12 | use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; | ||
| 13 | use embassy_nrf::rng::Rng; | ||
| 14 | use embassy_nrf::usb::{Driver, PowerUsb}; | ||
| 15 | use embassy_nrf::{interrupt, pac, peripherals}; | ||
| 16 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | ||
| 17 | use embassy_sync::channel::Channel; | ||
| 18 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State}; | ||
| 19 | use embassy_usb::{Builder, Config, UsbDevice}; | ||
| 20 | use embedded_io::asynch::Write; | ||
| 21 | use static_cell::StaticCell; | ||
| 22 | use {defmt_rtt as _, panic_probe as _}; | ||
| 23 | |||
| 24 | type MyDriver = Driver<'static, peripherals::USBD, PowerUsb>; | ||
| 25 | |||
| 26 | macro_rules! singleton { | ||
| 27 | ($val:expr) => {{ | ||
| 28 | type T = impl Sized; | ||
| 29 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | ||
| 30 | STATIC_CELL.init_with(move || $val) | ||
| 31 | }}; | ||
| 32 | } | ||
| 33 | |||
| 34 | #[embassy_executor::task] | ||
| 35 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { | ||
| 36 | device.run().await | ||
| 37 | } | ||
| 38 | |||
| 39 | #[embassy_executor::task] | ||
| 40 | async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { | ||
| 41 | loop { | ||
| 42 | warn!("WAITING for connection"); | ||
| 43 | LINK_UP.store(false, Ordering::Relaxed); | ||
| 44 | |||
| 45 | class.wait_connection().await.unwrap(); | ||
| 46 | |||
| 47 | warn!("Connected"); | ||
| 48 | LINK_UP.store(true, Ordering::Relaxed); | ||
| 49 | |||
| 50 | loop { | ||
| 51 | let mut p = unwrap!(PacketBox::new(embassy_net::Packet::new())); | ||
| 52 | let n = match class.read_packet(&mut p[..]).await { | ||
| 53 | Ok(n) => n, | ||
| 54 | Err(e) => { | ||
| 55 | warn!("error reading packet: {:?}", e); | ||
| 56 | break; | ||
| 57 | } | ||
| 58 | }; | ||
| 59 | |||
| 60 | let buf = p.slice(0..n); | ||
| 61 | if RX_CHANNEL.try_send(buf).is_err() { | ||
| 62 | warn!("Failed pushing rx'd packet to channel."); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | #[embassy_executor::task] | ||
| 69 | async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { | ||
| 70 | loop { | ||
| 71 | let pkt = TX_CHANNEL.recv().await; | ||
| 72 | if let Err(e) = class.write_packet(&pkt[..]).await { | ||
| 73 | warn!("Failed to TX packet: {:?}", e); | ||
| 74 | } | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | #[embassy_executor::task] | ||
| 79 | async fn net_task(stack: &'static Stack<Device>) -> ! { | ||
| 80 | stack.run().await | ||
| 81 | } | ||
| 82 | |||
| 83 | #[embassy_executor::main] | ||
| 84 | async fn main(spawner: Spawner) { | ||
| 85 | let p = embassy_nrf::init(Default::default()); | ||
| 86 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 87 | |||
| 88 | info!("Enabling ext hfosc..."); | ||
| 89 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | ||
| 90 | while clock.events_hfclkstarted.read().bits() != 1 {} | ||
| 91 | |||
| 92 | // Create the driver, from the HAL. | ||
| 93 | let irq = interrupt::take!(USBD); | ||
| 94 | let power_irq = interrupt::take!(POWER_CLOCK); | ||
| 95 | let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); | ||
| 96 | |||
| 97 | // Create embassy-usb Config | ||
| 98 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 99 | config.manufacturer = Some("Embassy"); | ||
| 100 | config.product = Some("USB-Ethernet example"); | ||
| 101 | config.serial_number = Some("12345678"); | ||
| 102 | config.max_power = 100; | ||
| 103 | config.max_packet_size_0 = 64; | ||
| 104 | |||
| 105 | // Required for Windows support. | ||
| 106 | config.composite_with_iads = true; | ||
| 107 | config.device_class = 0xEF; | ||
| 108 | config.device_sub_class = 0x02; | ||
| 109 | config.device_protocol = 0x01; | ||
| 110 | |||
| 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. | ||
| 127 | let mut builder = Builder::new( | ||
| 128 | driver, | ||
| 129 | config, | ||
| 130 | &mut res.device_descriptor, | ||
| 131 | &mut res.config_descriptor, | ||
| 132 | &mut res.bos_descriptor, | ||
| 133 | &mut res.control_buf, | ||
| 134 | None, | ||
| 135 | ); | ||
| 136 | |||
| 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. | ||
| 145 | 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. | ||
| 147 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; | ||
| 148 | |||
| 149 | // Create classes on the builder. | ||
| 150 | let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64); | ||
| 151 | |||
| 152 | // Build the builder. | ||
| 153 | let usb = builder.build(); | ||
| 154 | |||
| 155 | unwrap!(spawner.spawn(usb_task(usb))); | ||
| 156 | |||
| 157 | let (tx, rx) = class.split(); | ||
| 158 | unwrap!(spawner.spawn(usb_ncm_rx_task(rx))); | ||
| 159 | unwrap!(spawner.spawn(usb_ncm_tx_task(tx))); | ||
| 160 | |||
| 161 | let config = embassy_net::ConfigStrategy::Dhcp; | ||
| 162 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { | ||
| 163 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | ||
| 164 | // dns_servers: Vec::new(), | ||
| 165 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | ||
| 166 | //}); | ||
| 167 | |||
| 168 | // Generate random seed | ||
| 169 | let mut rng = Rng::new(p.RNG, interrupt::take!(RNG)); | ||
| 170 | let mut seed = [0; 8]; | ||
| 171 | rng.blocking_fill_bytes(&mut seed); | ||
| 172 | let seed = u64::from_le_bytes(seed); | ||
| 173 | |||
| 174 | // Init network stack | ||
| 175 | let device = Device { mac_addr: our_mac_addr }; | ||
| 176 | let stack = &*singleton!(Stack::new( | ||
| 177 | device, | ||
| 178 | config, | ||
| 179 | singleton!(StackResources::<1, 2, 8>::new()), | ||
| 180 | seed | ||
| 181 | )); | ||
| 182 | |||
| 183 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 184 | |||
| 185 | // And now we can use it! | ||
| 186 | |||
| 187 | let mut rx_buffer = [0; 4096]; | ||
| 188 | let mut tx_buffer = [0; 4096]; | ||
| 189 | let mut buf = [0; 4096]; | ||
| 190 | |||
| 191 | loop { | ||
| 192 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 193 | socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); | ||
| 194 | |||
| 195 | info!("Listening on TCP:1234..."); | ||
| 196 | if let Err(e) = socket.accept(1234).await { | ||
| 197 | warn!("accept error: {:?}", e); | ||
| 198 | continue; | ||
| 199 | } | ||
| 200 | |||
| 201 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 202 | |||
| 203 | loop { | ||
| 204 | let n = match socket.read(&mut buf).await { | ||
| 205 | Ok(0) => { | ||
| 206 | warn!("read EOF"); | ||
| 207 | break; | ||
| 208 | } | ||
| 209 | Ok(n) => n, | ||
| 210 | Err(e) => { | ||
| 211 | warn!("read error: {:?}", e); | ||
| 212 | break; | ||
| 213 | } | ||
| 214 | }; | ||
| 215 | |||
| 216 | info!("rxd {:02x}", &buf[..n]); | ||
| 217 | |||
| 218 | match socket.write_all(&buf[..n]).await { | ||
| 219 | Ok(()) => {} | ||
| 220 | Err(e) => { | ||
| 221 | warn!("write error: {:?}", e); | ||
| 222 | break; | ||
| 223 | } | ||
| 224 | }; | ||
| 225 | } | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | static TX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new(); | ||
| 230 | static RX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new(); | ||
| 231 | static LINK_UP: AtomicBool = AtomicBool::new(false); | ||
| 232 | |||
| 233 | struct Device { | ||
| 234 | mac_addr: [u8; 6], | ||
| 235 | } | ||
| 236 | |||
| 237 | impl embassy_net::Device for Device { | ||
| 238 | fn register_waker(&mut self, waker: &Waker) { | ||
| 239 | // loopy loopy wakey wakey | ||
| 240 | waker.wake_by_ref() | ||
| 241 | } | ||
| 242 | |||
| 243 | fn link_state(&mut self) -> embassy_net::LinkState { | ||
| 244 | match LINK_UP.load(Ordering::Relaxed) { | ||
| 245 | true => embassy_net::LinkState::Up, | ||
| 246 | false => embassy_net::LinkState::Down, | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | fn capabilities(&self) -> embassy_net::DeviceCapabilities { | ||
| 251 | let mut caps = embassy_net::DeviceCapabilities::default(); | ||
| 252 | caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header | ||
| 253 | caps.medium = embassy_net::Medium::Ethernet; | ||
| 254 | caps | ||
| 255 | } | ||
| 256 | |||
| 257 | fn is_transmit_ready(&mut self) -> bool { | ||
| 258 | true | ||
| 259 | } | ||
| 260 | |||
| 261 | fn transmit(&mut self, pkt: PacketBuf) { | ||
| 262 | if TX_CHANNEL.try_send(pkt).is_err() { | ||
| 263 | warn!("TX failed") | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | fn receive<'a>(&mut self) -> Option<PacketBuf> { | ||
| 268 | RX_CHANNEL.try_recv().ok() | ||
| 269 | } | ||
| 270 | |||
| 271 | fn ethernet_address(&self) -> [u8; 6] { | ||
| 272 | self.mac_addr | ||
| 273 | } | ||
| 274 | } | ||
diff --git a/examples/nrf/.cargo/config.toml b/examples/nrf52840/.cargo/config.toml index 8ca28df39..8ca28df39 100644 --- a/examples/nrf/.cargo/config.toml +++ b/examples/nrf52840/.cargo/config.toml | |||
diff --git a/examples/nrf/Cargo.toml b/examples/nrf52840/Cargo.toml index 9ebd04845..cfdda076e 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf52840/Cargo.toml | |||
| @@ -1,25 +1,31 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-nrf-examples" | 3 | name = "embassy-nrf52840-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | [features] | 7 | [features] |
| 8 | default = ["nightly"] | 8 | default = ["nightly"] |
| 9 | nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net"] | 9 | msos-descriptor = ["embassy-usb/msos-descriptor"] |
| 10 | nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net", | ||
| 11 | "embassy-lora", "lorawan-device", "lorawan"] | ||
| 10 | 12 | ||
| 11 | [dependencies] | 13 | [dependencies] |
| 12 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 13 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 15 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 14 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 16 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } |
| 15 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 17 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 16 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } | 18 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 17 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true } | 19 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } |
| 18 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } | 20 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } |
| 19 | embedded-io = "0.3.0" | 21 | embedded-io = "0.4.0" |
| 22 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } | ||
| 23 | |||
| 24 | lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true } | ||
| 25 | lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } | ||
| 20 | 26 | ||
| 21 | defmt = "0.3" | 27 | defmt = "0.3" |
| 22 | defmt-rtt = "0.3" | 28 | defmt-rtt = "0.4" |
| 23 | 29 | ||
| 24 | static_cell = "1.0" | 30 | static_cell = "1.0" |
| 25 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 31 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| @@ -30,3 +36,7 @@ rand = { version = "0.8.4", default-features = false } | |||
| 30 | embedded-storage = "0.3.0" | 36 | embedded-storage = "0.3.0" |
| 31 | usbd-hid = "0.6.0" | 37 | usbd-hid = "0.6.0" |
| 32 | serde = { version = "1.0.136", default-features = false } | 38 | serde = { version = "1.0.136", default-features = false } |
| 39 | |||
| 40 | [[bin]] | ||
| 41 | name = "usb_serial_winusb" | ||
| 42 | required-features = ["msos-descriptor"] | ||
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/nrf/memory.x b/examples/nrf52840/memory.x index 9b04edec0..9b04edec0 100644 --- a/examples/nrf/memory.x +++ b/examples/nrf52840/memory.x | |||
diff --git a/examples/nrf/src/bin/awaitable_timer.rs b/examples/nrf52840/src/bin/awaitable_timer.rs index b32af236c..b32af236c 100644 --- a/examples/nrf/src/bin/awaitable_timer.rs +++ b/examples/nrf52840/src/bin/awaitable_timer.rs | |||
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..ea566f4b2 100644 --- a/examples/nrf/src/bin/buffered_uart.rs +++ b/examples/nrf52840/src/bin/buffered_uart.rs | |||
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 2a28f2763..2a28f2763 100644 --- a/examples/nrf/src/bin/executor_fairness_test.rs +++ b/examples/nrf52840/src/bin/executor_fairness_test.rs | |||
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..52d46e4f9 --- /dev/null +++ b/examples/nrf52840/src/bin/i2s_effect.rs | |||
| @@ -0,0 +1,117 @@ | |||
| 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::interrupt; | ||
| 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 | #[embassy_executor::main] | ||
| 19 | async fn main(_spawner: Spawner) { | ||
| 20 | let p = embassy_nrf::init(Default::default()); | ||
| 21 | |||
| 22 | let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into(); | ||
| 23 | |||
| 24 | let sample_rate = master_clock.sample_rate(); | ||
| 25 | info!("Sample rate: {}", sample_rate); | ||
| 26 | |||
| 27 | let mut config = Config::default(); | ||
| 28 | config.sample_width = SampleWidth::_16bit; | ||
| 29 | config.channels = Channels::MonoLeft; | ||
| 30 | |||
| 31 | let irq = interrupt::take!(I2S); | ||
| 32 | let buffers_out = MultiBuffering::<Sample, NUM_BUFFERS, NUM_SAMPLES>::new(); | ||
| 33 | let buffers_in = MultiBuffering::<Sample, NUM_BUFFERS, NUM_SAMPLES>::new(); | ||
| 34 | let mut full_duplex_stream = I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).full_duplex( | ||
| 35 | p.P0_29, | ||
| 36 | p.P0_28, | ||
| 37 | buffers_out, | ||
| 38 | buffers_in, | ||
| 39 | ); | ||
| 40 | |||
| 41 | let mut modulator = SineOsc::new(); | ||
| 42 | modulator.set_frequency(8.0, 1.0 / sample_rate as f32); | ||
| 43 | modulator.set_amplitude(1.0); | ||
| 44 | |||
| 45 | full_duplex_stream.start().await.expect("I2S Start"); | ||
| 46 | |||
| 47 | loop { | ||
| 48 | let (buff_out, buff_in) = full_duplex_stream.buffers(); | ||
| 49 | for i in 0..NUM_SAMPLES { | ||
| 50 | let modulation = (Sample::SCALE as f32 * bipolar_to_unipolar(modulator.generate())) as Sample; | ||
| 51 | buff_out[i] = buff_in[i] * modulation; | ||
| 52 | } | ||
| 53 | |||
| 54 | if let Err(err) = full_duplex_stream.send_and_receive().await { | ||
| 55 | error!("{}", err); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | struct SineOsc { | ||
| 61 | amplitude: f32, | ||
| 62 | modulo: f32, | ||
| 63 | phase_inc: f32, | ||
| 64 | } | ||
| 65 | |||
| 66 | impl SineOsc { | ||
| 67 | const B: f32 = 4.0 / PI; | ||
| 68 | const C: f32 = -4.0 / (PI * PI); | ||
| 69 | const P: f32 = 0.225; | ||
| 70 | |||
| 71 | pub fn new() -> Self { | ||
| 72 | Self { | ||
| 73 | amplitude: 1.0, | ||
| 74 | modulo: 0.0, | ||
| 75 | phase_inc: 0.0, | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | pub fn set_frequency(&mut self, freq: f32, inv_sample_rate: f32) { | ||
| 80 | self.phase_inc = freq * inv_sample_rate; | ||
| 81 | } | ||
| 82 | |||
| 83 | pub fn set_amplitude(&mut self, amplitude: f32) { | ||
| 84 | self.amplitude = amplitude; | ||
| 85 | } | ||
| 86 | |||
| 87 | pub fn generate(&mut self) -> f32 { | ||
| 88 | let signal = self.parabolic_sin(self.modulo); | ||
| 89 | self.modulo += self.phase_inc; | ||
| 90 | if self.modulo < 0.0 { | ||
| 91 | self.modulo += 1.0; | ||
| 92 | } else if self.modulo > 1.0 { | ||
| 93 | self.modulo -= 1.0; | ||
| 94 | } | ||
| 95 | signal * self.amplitude | ||
| 96 | } | ||
| 97 | |||
| 98 | fn parabolic_sin(&mut self, modulo: f32) -> f32 { | ||
| 99 | let angle = PI - modulo * 2.0 * PI; | ||
| 100 | let y = Self::B * angle + Self::C * angle * abs(angle); | ||
| 101 | Self::P * (y * abs(y) - y) + y | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | #[inline] | ||
| 106 | fn abs(value: f32) -> f32 { | ||
| 107 | if value < 0.0 { | ||
| 108 | -value | ||
| 109 | } else { | ||
| 110 | value | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | #[inline] | ||
| 115 | fn bipolar_to_unipolar(value: f32) -> f32 { | ||
| 116 | (value + 1.0) / 2.0 | ||
| 117 | } | ||
diff --git a/examples/nrf52840/src/bin/i2s_monitor.rs b/examples/nrf52840/src/bin/i2s_monitor.rs new file mode 100644 index 000000000..5ebfd9542 --- /dev/null +++ b/examples/nrf52840/src/bin/i2s_monitor.rs | |||
| @@ -0,0 +1,115 @@ | |||
| 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::interrupt; | ||
| 9 | use embassy_nrf::pwm::{Prescaler, SimplePwm}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | type Sample = i16; | ||
| 13 | |||
| 14 | const NUM_SAMPLES: usize = 500; | ||
| 15 | |||
| 16 | #[embassy_executor::main] | ||
| 17 | async fn main(_spawner: Spawner) { | ||
| 18 | let p = embassy_nrf::init(Default::default()); | ||
| 19 | |||
| 20 | let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into(); | ||
| 21 | |||
| 22 | let sample_rate = master_clock.sample_rate(); | ||
| 23 | info!("Sample rate: {}", sample_rate); | ||
| 24 | |||
| 25 | let mut config = Config::default(); | ||
| 26 | config.sample_width = SampleWidth::_16bit; | ||
| 27 | config.channels = Channels::MonoLeft; | ||
| 28 | |||
| 29 | let irq = interrupt::take!(I2S); | ||
| 30 | let buffers = DoubleBuffering::<Sample, NUM_SAMPLES>::new(); | ||
| 31 | let mut input_stream = | ||
| 32 | I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).input(p.P0_29, buffers); | ||
| 33 | |||
| 34 | // Configure the PWM to use the pins corresponding to the RGB leds | ||
| 35 | let mut pwm = SimplePwm::new_3ch(p.PWM0, p.P0_23, p.P0_22, p.P0_24); | ||
| 36 | pwm.set_prescaler(Prescaler::Div1); | ||
| 37 | pwm.set_max_duty(255); | ||
| 38 | |||
| 39 | let mut rms_online = RmsOnline::<NUM_SAMPLES>::default(); | ||
| 40 | |||
| 41 | input_stream.start().await.expect("I2S Start"); | ||
| 42 | |||
| 43 | loop { | ||
| 44 | let rms = rms_online.process(input_stream.buffer()); | ||
| 45 | let rgb = rgb_from_rms(rms); | ||
| 46 | |||
| 47 | debug!("RMS: {}, RGB: {:?}", rms, rgb); | ||
| 48 | for i in 0..3 { | ||
| 49 | pwm.set_duty(i, rgb[i].into()); | ||
| 50 | } | ||
| 51 | |||
| 52 | if let Err(err) = input_stream.receive().await { | ||
| 53 | error!("{}", err); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | /// RMS from 0.0 until 0.75 will give green with a proportional intensity | ||
| 59 | /// RMS from 0.75 until 0.9 will give a blend between orange and red proportionally to the intensity | ||
| 60 | /// RMS above 0.9 will give a red with a proportional intensity | ||
| 61 | fn rgb_from_rms(rms: f32) -> [u8; 3] { | ||
| 62 | if rms < 0.75 { | ||
| 63 | let intensity = rms / 0.75; | ||
| 64 | [0, (intensity * 165.0) as u8, 0] | ||
| 65 | } else if rms < 0.9 { | ||
| 66 | let intensity = (rms - 0.75) / 0.15; | ||
| 67 | [200, 165 - (165.0 * intensity) as u8, 0] | ||
| 68 | } else { | ||
| 69 | let intensity = (rms - 0.9) / 0.1; | ||
| 70 | [200 + (55.0 * intensity) as u8, 0, 0] | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | pub struct RmsOnline<const N: usize> { | ||
| 75 | pub squares: [f32; N], | ||
| 76 | pub head: usize, | ||
| 77 | } | ||
| 78 | |||
| 79 | impl<const N: usize> Default for RmsOnline<N> { | ||
| 80 | fn default() -> Self { | ||
| 81 | RmsOnline { | ||
| 82 | squares: [0.0; N], | ||
| 83 | head: 0, | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | impl<const N: usize> RmsOnline<N> { | ||
| 89 | pub fn reset(&mut self) { | ||
| 90 | self.squares = [0.0; N]; | ||
| 91 | self.head = 0; | ||
| 92 | } | ||
| 93 | |||
| 94 | pub fn process(&mut self, buf: &[Sample]) -> f32 { | ||
| 95 | buf.iter() | ||
| 96 | .for_each(|sample| self.push(*sample as f32 / Sample::SCALE as f32)); | ||
| 97 | |||
| 98 | let sum_of_squares = self.squares.iter().fold(0.0, |acc, v| acc + *v); | ||
| 99 | Self::approx_sqrt(sum_of_squares / N as f32) | ||
| 100 | } | ||
| 101 | |||
| 102 | pub fn push(&mut self, signal: f32) { | ||
| 103 | let square = signal * signal; | ||
| 104 | self.squares[self.head] = square; | ||
| 105 | self.head = (self.head + 1) % N; | ||
| 106 | } | ||
| 107 | |||
| 108 | /// Approximated sqrt taken from [micromath] | ||
| 109 | /// | ||
| 110 | /// [micromath]: https://docs.rs/micromath/latest/src/micromath/float/sqrt.rs.html#11-17 | ||
| 111 | /// | ||
| 112 | fn approx_sqrt(value: f32) -> f32 { | ||
| 113 | f32::from_bits((value.to_bits() + 0x3f80_0000) >> 1) | ||
| 114 | } | ||
| 115 | } | ||
diff --git a/examples/nrf52840/src/bin/i2s_waveform.rs b/examples/nrf52840/src/bin/i2s_waveform.rs new file mode 100644 index 000000000..eda930677 --- /dev/null +++ b/examples/nrf52840/src/bin/i2s_waveform.rs | |||
| @@ -0,0 +1,151 @@ | |||
| 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::interrupt; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | type Sample = i16; | ||
| 14 | |||
| 15 | const NUM_SAMPLES: usize = 50; | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | let p = embassy_nrf::init(Default::default()); | ||
| 20 | |||
| 21 | let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into(); | ||
| 22 | |||
| 23 | let sample_rate = master_clock.sample_rate(); | ||
| 24 | info!("Sample rate: {}", sample_rate); | ||
| 25 | |||
| 26 | let mut config = Config::default(); | ||
| 27 | config.sample_width = SampleWidth::_16bit; | ||
| 28 | config.channels = Channels::MonoLeft; | ||
| 29 | |||
| 30 | let irq = interrupt::take!(I2S); | ||
| 31 | let buffers = DoubleBuffering::<Sample, NUM_SAMPLES>::new(); | ||
| 32 | let mut output_stream = | ||
| 33 | I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).output(p.P0_28, buffers); | ||
| 34 | |||
| 35 | let mut waveform = Waveform::new(1.0 / sample_rate as f32); | ||
| 36 | |||
| 37 | waveform.process(output_stream.buffer()); | ||
| 38 | |||
| 39 | output_stream.start().await.expect("I2S Start"); | ||
| 40 | |||
| 41 | loop { | ||
| 42 | waveform.process(output_stream.buffer()); | ||
| 43 | |||
| 44 | if let Err(err) = output_stream.send().await { | ||
| 45 | error!("{}", err); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | struct Waveform { | ||
| 51 | inv_sample_rate: f32, | ||
| 52 | carrier: SineOsc, | ||
| 53 | freq_mod: SineOsc, | ||
| 54 | amp_mod: SineOsc, | ||
| 55 | } | ||
| 56 | |||
| 57 | impl Waveform { | ||
| 58 | fn new(inv_sample_rate: f32) -> Self { | ||
| 59 | let mut carrier = SineOsc::new(); | ||
| 60 | carrier.set_frequency(110.0, inv_sample_rate); | ||
| 61 | |||
| 62 | let mut freq_mod = SineOsc::new(); | ||
| 63 | freq_mod.set_frequency(1.0, inv_sample_rate); | ||
| 64 | freq_mod.set_amplitude(1.0); | ||
| 65 | |||
| 66 | let mut amp_mod = SineOsc::new(); | ||
| 67 | amp_mod.set_frequency(16.0, inv_sample_rate); | ||
| 68 | amp_mod.set_amplitude(0.5); | ||
| 69 | |||
| 70 | Self { | ||
| 71 | inv_sample_rate, | ||
| 72 | carrier, | ||
| 73 | freq_mod, | ||
| 74 | amp_mod, | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | fn process(&mut self, buf: &mut [Sample]) { | ||
| 79 | for sample in buf.chunks_mut(1) { | ||
| 80 | let freq_modulation = bipolar_to_unipolar(self.freq_mod.generate()); | ||
| 81 | self.carrier | ||
| 82 | .set_frequency(110.0 + 440.0 * freq_modulation, self.inv_sample_rate); | ||
| 83 | |||
| 84 | let amp_modulation = bipolar_to_unipolar(self.amp_mod.generate()); | ||
| 85 | self.carrier.set_amplitude(amp_modulation); | ||
| 86 | |||
| 87 | let signal = self.carrier.generate(); | ||
| 88 | |||
| 89 | sample[0] = (Sample::SCALE as f32 * signal) as Sample; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | struct SineOsc { | ||
| 95 | amplitude: f32, | ||
| 96 | modulo: f32, | ||
| 97 | phase_inc: f32, | ||
| 98 | } | ||
| 99 | |||
| 100 | impl SineOsc { | ||
| 101 | const B: f32 = 4.0 / PI; | ||
| 102 | const C: f32 = -4.0 / (PI * PI); | ||
| 103 | const P: f32 = 0.225; | ||
| 104 | |||
| 105 | pub fn new() -> Self { | ||
| 106 | Self { | ||
| 107 | amplitude: 1.0, | ||
| 108 | modulo: 0.0, | ||
| 109 | phase_inc: 0.0, | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | pub fn set_frequency(&mut self, freq: f32, inv_sample_rate: f32) { | ||
| 114 | self.phase_inc = freq * inv_sample_rate; | ||
| 115 | } | ||
| 116 | |||
| 117 | pub fn set_amplitude(&mut self, amplitude: f32) { | ||
| 118 | self.amplitude = amplitude; | ||
| 119 | } | ||
| 120 | |||
| 121 | pub fn generate(&mut self) -> f32 { | ||
| 122 | let signal = self.parabolic_sin(self.modulo); | ||
| 123 | self.modulo += self.phase_inc; | ||
| 124 | if self.modulo < 0.0 { | ||
| 125 | self.modulo += 1.0; | ||
| 126 | } else if self.modulo > 1.0 { | ||
| 127 | self.modulo -= 1.0; | ||
| 128 | } | ||
| 129 | signal * self.amplitude | ||
| 130 | } | ||
| 131 | |||
| 132 | fn parabolic_sin(&mut self, modulo: f32) -> f32 { | ||
| 133 | let angle = PI - modulo * 2.0 * PI; | ||
| 134 | let y = Self::B * angle + Self::C * angle * abs(angle); | ||
| 135 | Self::P * (y * abs(y) - y) + y | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | #[inline] | ||
| 140 | fn abs(value: f32) -> f32 { | ||
| 141 | if value < 0.0 { | ||
| 142 | -value | ||
| 143 | } else { | ||
| 144 | value | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | #[inline] | ||
| 149 | fn bipolar_to_unipolar(value: f32) -> f32 { | ||
| 150 | (value + 1.0) / 2.0 | ||
| 151 | } | ||
diff --git a/examples/nrf52840/src/bin/lora_p2p_report.rs b/examples/nrf52840/src/bin/lora_p2p_report.rs new file mode 100644 index 000000000..d512b83f6 --- /dev/null +++ b/examples/nrf52840/src/bin/lora_p2p_report.rs | |||
| @@ -0,0 +1,78 @@ | |||
| 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 demonstates LORA P2P functionality in conjunction with example lora_p2p_sense.rs. | ||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![macro_use] | ||
| 7 | #![allow(dead_code)] | ||
| 8 | #![feature(type_alias_impl_trait)] | ||
| 9 | |||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_lora::sx126x::*; | ||
| 13 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; | ||
| 14 | use embassy_nrf::{interrupt, spim}; | ||
| 15 | use embassy_time::{Duration, Timer}; | ||
| 16 | use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor}; | ||
| 17 | use {defmt_rtt as _, panic_probe as _}; | ||
| 18 | |||
| 19 | #[embassy_executor::main] | ||
| 20 | async fn main(_spawner: Spawner) { | ||
| 21 | let p = embassy_nrf::init(Default::default()); | ||
| 22 | let mut spi_config = spim::Config::default(); | ||
| 23 | spi_config.frequency = spim::Frequency::M16; | ||
| 24 | |||
| 25 | let mut radio = { | ||
| 26 | let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); | ||
| 27 | let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); | ||
| 28 | |||
| 29 | let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); | ||
| 30 | let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); | ||
| 31 | let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); | ||
| 32 | let busy = Input::new(p.P1_14.degrade(), Pull::Down); | ||
| 33 | let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); | ||
| 34 | let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); | ||
| 35 | |||
| 36 | match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { | ||
| 37 | Ok(r) => r, | ||
| 38 | Err(err) => { | ||
| 39 | info!("Sx126xRadio error = {}", err); | ||
| 40 | return; | ||
| 41 | } | ||
| 42 | } | ||
| 43 | }; | ||
| 44 | |||
| 45 | let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); | ||
| 46 | let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); | ||
| 47 | |||
| 48 | start_indicator.set_high(); | ||
| 49 | Timer::after(Duration::from_secs(5)).await; | ||
| 50 | start_indicator.set_low(); | ||
| 51 | |||
| 52 | loop { | ||
| 53 | let rf_config = RfConfig { | ||
| 54 | frequency: 903900000, // channel in Hz | ||
| 55 | bandwidth: Bandwidth::_250KHz, | ||
| 56 | spreading_factor: SpreadingFactor::_10, | ||
| 57 | coding_rate: CodingRate::_4_8, | ||
| 58 | }; | ||
| 59 | |||
| 60 | let mut buffer = [00u8; 100]; | ||
| 61 | |||
| 62 | // P2P receive | ||
| 63 | match radio.rx(rf_config, &mut buffer).await { | ||
| 64 | Ok((buffer_len, rx_quality)) => info!( | ||
| 65 | "RX received = {:?} with length = {} rssi = {} snr = {}", | ||
| 66 | &buffer[0..buffer_len], | ||
| 67 | buffer_len, | ||
| 68 | rx_quality.rssi(), | ||
| 69 | rx_quality.snr() | ||
| 70 | ), | ||
| 71 | Err(err) => info!("RX error = {}", err), | ||
| 72 | } | ||
| 73 | |||
| 74 | debug_indicator.set_high(); | ||
| 75 | Timer::after(Duration::from_secs(2)).await; | ||
| 76 | debug_indicator.set_low(); | ||
| 77 | } | ||
| 78 | } | ||
diff --git a/examples/nrf52840/src/bin/lora_p2p_sense.rs b/examples/nrf52840/src/bin/lora_p2p_sense.rs new file mode 100644 index 000000000..b9768874b --- /dev/null +++ b/examples/nrf52840/src/bin/lora_p2p_sense.rs | |||
| @@ -0,0 +1,125 @@ | |||
| 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 demonstates LORA P2P functionality in conjunction with example lora_p2p_report.rs. | ||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![macro_use] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | #![feature(alloc_error_handler)] | ||
| 9 | #![allow(incomplete_features)] | ||
| 10 | |||
| 11 | use defmt::*; | ||
| 12 | use embassy_executor::Spawner; | ||
| 13 | use embassy_lora::sx126x::*; | ||
| 14 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; | ||
| 15 | use embassy_nrf::{interrupt, spim}; | ||
| 16 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 17 | use embassy_sync::pubsub::{PubSubChannel, Publisher}; | ||
| 18 | use embassy_time::{Duration, Timer}; | ||
| 19 | use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor, TxConfig}; | ||
| 20 | use {defmt_rtt as _, panic_probe as _, panic_probe as _}; | ||
| 21 | |||
| 22 | // Message bus: queue of 2, 1 subscriber (Lora P2P), 2 publishers (temperature, motion detection) | ||
| 23 | static MESSAGE_BUS: PubSubChannel<CriticalSectionRawMutex, Message, 2, 1, 2> = PubSubChannel::new(); | ||
| 24 | |||
| 25 | #[derive(Clone, defmt::Format)] | ||
| 26 | enum Message { | ||
| 27 | Temperature(i32), | ||
| 28 | MotionDetected, | ||
| 29 | } | ||
| 30 | |||
| 31 | #[embassy_executor::task] | ||
| 32 | async fn temperature_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) { | ||
| 33 | // Publish a fake temperature every 43 seconds, minimizing LORA traffic. | ||
| 34 | loop { | ||
| 35 | Timer::after(Duration::from_secs(43)).await; | ||
| 36 | publisher.publish(Message::Temperature(9)).await; | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | #[embassy_executor::task] | ||
| 41 | async fn motion_detection_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) { | ||
| 42 | // Publish a fake motion detection every 79 seconds, minimizing LORA traffic. | ||
| 43 | loop { | ||
| 44 | Timer::after(Duration::from_secs(79)).await; | ||
| 45 | publisher.publish(Message::MotionDetected).await; | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | #[embassy_executor::main] | ||
| 50 | async fn main(spawner: Spawner) { | ||
| 51 | let p = embassy_nrf::init(Default::default()); | ||
| 52 | // set up to funnel temperature and motion detection events to the Lora Tx task | ||
| 53 | let mut lora_tx_subscriber = unwrap!(MESSAGE_BUS.subscriber()); | ||
| 54 | let temperature_publisher = unwrap!(MESSAGE_BUS.publisher()); | ||
| 55 | let motion_detection_publisher = unwrap!(MESSAGE_BUS.publisher()); | ||
| 56 | |||
| 57 | let mut spi_config = spim::Config::default(); | ||
| 58 | spi_config.frequency = spim::Frequency::M16; | ||
| 59 | |||
| 60 | let mut radio = { | ||
| 61 | let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); | ||
| 62 | let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); | ||
| 63 | |||
| 64 | let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); | ||
| 65 | let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); | ||
| 66 | let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); | ||
| 67 | let busy = Input::new(p.P1_14.degrade(), Pull::Down); | ||
| 68 | let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); | ||
| 69 | let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); | ||
| 70 | |||
| 71 | match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { | ||
| 72 | Ok(r) => r, | ||
| 73 | Err(err) => { | ||
| 74 | info!("Sx126xRadio error = {}", err); | ||
| 75 | return; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | }; | ||
| 79 | |||
| 80 | let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); | ||
| 81 | |||
| 82 | start_indicator.set_high(); | ||
| 83 | Timer::after(Duration::from_secs(5)).await; | ||
| 84 | start_indicator.set_low(); | ||
| 85 | |||
| 86 | match radio.lora.sleep().await { | ||
| 87 | Ok(()) => info!("Sleep successful"), | ||
| 88 | Err(err) => info!("Sleep unsuccessful = {}", err), | ||
| 89 | } | ||
| 90 | |||
| 91 | unwrap!(spawner.spawn(temperature_task(temperature_publisher))); | ||
| 92 | unwrap!(spawner.spawn(motion_detection_task(motion_detection_publisher))); | ||
| 93 | |||
| 94 | loop { | ||
| 95 | let message = lora_tx_subscriber.next_message_pure().await; | ||
| 96 | |||
| 97 | let tx_config = TxConfig { | ||
| 98 | // 11 byte maximum payload for Bandwidth 125 and SF 10 | ||
| 99 | pw: 10, // up to 20 | ||
| 100 | rf: RfConfig { | ||
| 101 | frequency: 903900000, // channel in Hz, not MHz | ||
| 102 | bandwidth: Bandwidth::_250KHz, | ||
| 103 | spreading_factor: SpreadingFactor::_10, | ||
| 104 | coding_rate: CodingRate::_4_8, | ||
| 105 | }, | ||
| 106 | }; | ||
| 107 | |||
| 108 | let mut buffer = [0x00u8]; | ||
| 109 | match message { | ||
| 110 | Message::Temperature(temperature) => buffer[0] = temperature as u8, | ||
| 111 | Message::MotionDetected => buffer[0] = 0x01u8, | ||
| 112 | }; | ||
| 113 | |||
| 114 | // unencrypted | ||
| 115 | match radio.tx(tx_config, &buffer).await { | ||
| 116 | Ok(ret_val) => info!("TX ret_val = {}", ret_val), | ||
| 117 | Err(err) => info!("TX error = {}", err), | ||
| 118 | } | ||
| 119 | |||
| 120 | match radio.lora.sleep().await { | ||
| 121 | Ok(()) => info!("Sleep successful"), | ||
| 122 | Err(err) => info!("Sleep unsuccessful = {}", err), | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
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..25806ae48 100644 --- a/examples/nrf/src/bin/multiprio.rs +++ b/examples/nrf52840/src/bin/multiprio.rs | |||
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..75d090fbb 100644 --- a/examples/nrf/src/bin/nvmc.rs +++ b/examples/nrf52840/src/bin/nvmc.rs | |||
diff --git a/examples/nrf52840/src/bin/pdm.rs b/examples/nrf52840/src/bin/pdm.rs new file mode 100644 index 000000000..7388580fb --- /dev/null +++ b/examples/nrf52840/src/bin/pdm.rs | |||
| @@ -0,0 +1,33 @@ | |||
| 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, Pdm}; | ||
| 9 | use embassy_time::{Duration, Timer}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_p: Spawner) { | ||
| 14 | let p = embassy_nrf::init(Default::default()); | ||
| 15 | let config = Config::default(); | ||
| 16 | let mut pdm = Pdm::new(p.PDM, interrupt::take!(PDM), p.P0_01, p.P0_00, config); | ||
| 17 | |||
| 18 | loop { | ||
| 19 | pdm.start().await; | ||
| 20 | |||
| 21 | // wait some time till the microphon settled | ||
| 22 | Timer::after(Duration::from_millis(1000)).await; | ||
| 23 | |||
| 24 | const SAMPLES: usize = 2048; | ||
| 25 | let mut buf = [0i16; SAMPLES]; | ||
| 26 | pdm.sample(&mut buf).await.unwrap(); | ||
| 27 | |||
| 28 | info!("samples: {:?}", &buf); | ||
| 29 | |||
| 30 | pdm.stop().await; | ||
| 31 | Timer::after(Duration::from_millis(100)).await; | ||
| 32 | } | ||
| 33 | } | ||
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..688e6d075 100644 --- a/examples/nrf/src/bin/pubsub.rs +++ b/examples/nrf52840/src/bin/pubsub.rs | |||
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..600bba07a 100644 --- a/examples/nrf/src/bin/qdec.rs +++ b/examples/nrf52840/src/bin/qdec.rs | |||
diff --git a/examples/nrf/src/bin/qspi.rs b/examples/nrf52840/src/bin/qspi.rs index bdcf710b8..bdcf710b8 100644 --- a/examples/nrf/src/bin/qspi.rs +++ b/examples/nrf52840/src/bin/qspi.rs | |||
diff --git a/examples/nrf/src/bin/qspi_lowpower.rs b/examples/nrf52840/src/bin/qspi_lowpower.rs index 9341a2376..9341a2376 100644 --- a/examples/nrf/src/bin/qspi_lowpower.rs +++ b/examples/nrf52840/src/bin/qspi_lowpower.rs | |||
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..647073949 100644 --- a/examples/nrf/src/bin/rng.rs +++ b/examples/nrf52840/src/bin/rng.rs | |||
diff --git a/examples/nrf/src/bin/saadc.rs b/examples/nrf52840/src/bin/saadc.rs index 7cf588090..7cf588090 100644 --- a/examples/nrf/src/bin/saadc.rs +++ b/examples/nrf52840/src/bin/saadc.rs | |||
diff --git a/examples/nrf/src/bin/saadc_continuous.rs b/examples/nrf52840/src/bin/saadc_continuous.rs index bb50ac65e..2551d15fd 100644 --- a/examples/nrf/src/bin/saadc_continuous.rs +++ b/examples/nrf52840/src/bin/saadc_continuous.rs | |||
| @@ -5,7 +5,7 @@ | |||
| 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::interrupt; |
| 8 | use embassy_nrf::saadc::{ChannelConfig, Config, Saadc, SamplerState}; | 8 | use embassy_nrf::saadc::{CallbackResult, ChannelConfig, Config, Saadc}; |
| 9 | use embassy_nrf::timer::Frequency; | 9 | use embassy_nrf::timer::Frequency; |
| 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 _}; |
| @@ -61,7 +61,7 @@ async fn main(_p: Spawner) { | |||
| 61 | c = 0; | 61 | c = 0; |
| 62 | a = 0; | 62 | a = 0; |
| 63 | } | 63 | } |
| 64 | SamplerState::Sampled | 64 | CallbackResult::Continue |
| 65 | }, | 65 | }, |
| 66 | ) | 66 | ) |
| 67 | .await; | 67 | .await; |
diff --git a/examples/nrf/src/bin/self_spawn.rs b/examples/nrf52840/src/bin/self_spawn.rs index 196255a52..196255a52 100644 --- a/examples/nrf/src/bin/self_spawn.rs +++ b/examples/nrf52840/src/bin/self_spawn.rs | |||
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..132e01660 100644 --- a/examples/nrf/src/bin/spim.rs +++ b/examples/nrf52840/src/bin/spim.rs | |||
diff --git a/examples/nrf52840/src/bin/spis.rs b/examples/nrf52840/src/bin/spis.rs new file mode 100644 index 000000000..fe3b0c53d --- /dev/null +++ b/examples/nrf52840/src/bin/spis.rs | |||
| @@ -0,0 +1,27 @@ | |||
| 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::spis::{Config, Spis}; | ||
| 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!("Running!"); | ||
| 15 | |||
| 16 | let irq = interrupt::take!(SPIM2_SPIS2_SPI2); | ||
| 17 | let mut spis = Spis::new(p.SPI2, irq, p.P0_31, p.P0_29, p.P0_28, p.P0_30, Config::default()); | ||
| 18 | |||
| 19 | loop { | ||
| 20 | let mut rx_buf = [0_u8; 64]; | ||
| 21 | let tx_buf = [1_u8, 2, 3, 4, 5, 6, 7, 8]; | ||
| 22 | if let Ok((n_rx, n_tx)) = spis.transfer(&mut rx_buf, &tx_buf).await { | ||
| 23 | info!("RX: {:?}", rx_buf[..n_rx]); | ||
| 24 | info!("TX: {:?}", tx_buf[..n_tx]); | ||
| 25 | } | ||
| 26 | } | ||
| 27 | } | ||
diff --git a/examples/nrf/src/bin/temp.rs b/examples/nrf52840/src/bin/temp.rs index b06ac709e..b06ac709e 100644 --- a/examples/nrf/src/bin/temp.rs +++ b/examples/nrf52840/src/bin/temp.rs | |||
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..a027cc1e7 100644 --- a/examples/nrf/src/bin/twim.rs +++ b/examples/nrf52840/src/bin/twim.rs | |||
diff --git a/examples/nrf/src/bin/twim_lowpower.rs b/examples/nrf52840/src/bin/twim_lowpower.rs index e30cc9688..e30cc9688 100644 --- a/examples/nrf/src/bin/twim_lowpower.rs +++ b/examples/nrf52840/src/bin/twim_lowpower.rs | |||
diff --git a/examples/nrf52840/src/bin/twis.rs b/examples/nrf52840/src/bin/twis.rs new file mode 100644 index 000000000..54cba9494 --- /dev/null +++ b/examples/nrf52840/src/bin/twis.rs | |||
| @@ -0,0 +1,46 @@ | |||
| 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::interrupt; | ||
| 10 | use embassy_nrf::twis::{self, Command, Twis}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | #[embassy_executor::main] | ||
| 14 | async fn main(_spawner: Spawner) { | ||
| 15 | let p = embassy_nrf::init(Default::default()); | ||
| 16 | |||
| 17 | let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); | ||
| 18 | let mut config = twis::Config::default(); | ||
| 19 | // Set i2c address | ||
| 20 | config.address0 = 0x55; | ||
| 21 | let mut i2c = Twis::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config); | ||
| 22 | |||
| 23 | info!("Listening..."); | ||
| 24 | loop { | ||
| 25 | let response = [1, 2, 3, 4, 5, 6, 7, 8]; | ||
| 26 | // This buffer is used if the i2c master performs a Write or WriteRead | ||
| 27 | let mut buf = [0u8; 16]; | ||
| 28 | match i2c.listen(&mut buf).await { | ||
| 29 | Ok(Command::Read) => { | ||
| 30 | info!("Got READ command. Respond with data:\n{:?}\n", response); | ||
| 31 | if let Err(e) = i2c.respond_to_read(&response).await { | ||
| 32 | error!("{:?}", e); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | Ok(Command::Write(n)) => info!("Got WRITE command with data:\n{:?}\n", buf[..n]), | ||
| 36 | Ok(Command::WriteRead(n)) => { | ||
| 37 | info!("Got WRITE/READ command with data:\n{:?}", buf[..n]); | ||
| 38 | info!("Respond with data:\n{:?}\n", response); | ||
| 39 | if let Err(e) = i2c.respond_to_read(&response).await { | ||
| 40 | error!("{:?}", e); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | Err(e) => error!("{:?}", e), | ||
| 44 | } | ||
| 45 | } | ||
| 46 | } | ||
diff --git a/examples/nrf/src/bin/uart.rs b/examples/nrf52840/src/bin/uart.rs index 600f7a6ef..600f7a6ef 100644 --- a/examples/nrf/src/bin/uart.rs +++ b/examples/nrf52840/src/bin/uart.rs | |||
diff --git a/examples/nrf/src/bin/uart_idle.rs b/examples/nrf52840/src/bin/uart_idle.rs index 09ec624c0..6af4f7097 100644 --- a/examples/nrf/src/bin/uart_idle.rs +++ b/examples/nrf52840/src/bin/uart_idle.rs | |||
| @@ -15,7 +15,8 @@ async fn main(_spawner: Spawner) { | |||
| 15 | config.baudrate = uarte::Baudrate::BAUD115200; | 15 | config.baudrate = uarte::Baudrate::BAUD115200; |
| 16 | 16 | ||
| 17 | let irq = interrupt::take!(UARTE0_UART0); | 17 | let irq = interrupt::take!(UARTE0_UART0); |
| 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); | 18 | let uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config); |
| 19 | let (mut tx, mut rx) = uart.split_with_idle(p.TIMER0, p.PPI_CH0, p.PPI_CH1); | ||
| 19 | 20 | ||
| 20 | info!("uarte initialized!"); | 21 | info!("uarte initialized!"); |
| 21 | 22 | ||
| @@ -23,12 +24,12 @@ async fn main(_spawner: Spawner) { | |||
| 23 | let mut buf = [0; 8]; | 24 | let mut buf = [0; 8]; |
| 24 | buf.copy_from_slice(b"Hello!\r\n"); | 25 | buf.copy_from_slice(b"Hello!\r\n"); |
| 25 | 26 | ||
| 26 | unwrap!(uart.write(&buf).await); | 27 | unwrap!(tx.write(&buf).await); |
| 27 | info!("wrote hello in uart!"); | 28 | info!("wrote hello in uart!"); |
| 28 | 29 | ||
| 29 | loop { | 30 | loop { |
| 30 | info!("reading..."); | 31 | info!("reading..."); |
| 31 | let n = unwrap!(uart.read_until_idle(&mut buf).await); | 32 | let n = unwrap!(rx.read_until_idle(&mut buf).await); |
| 32 | info!("got {} bytes", n); | 33 | info!("got {} bytes", n); |
| 33 | } | 34 | } |
| 34 | } | 35 | } |
diff --git a/examples/nrf/src/bin/uart_split.rs b/examples/nrf52840/src/bin/uart_split.rs index 1adaf53fd..1adaf53fd 100644 --- a/examples/nrf/src/bin/uart_split.rs +++ b/examples/nrf52840/src/bin/uart_split.rs | |||
diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs new file mode 100644 index 000000000..430468adf --- /dev/null +++ b/examples/nrf52840/src/bin/usb_ethernet.rs | |||
| @@ -0,0 +1,186 @@ | |||
| 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::{Driver, HardwareVbusDetect}; | ||
| 13 | use embassy_nrf::{interrupt, pac, peripherals}; | ||
| 14 | use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; | ||
| 15 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; | ||
| 16 | use embassy_usb::{Builder, Config, UsbDevice}; | ||
| 17 | use embedded_io::asynch::Write; | ||
| 18 | use static_cell::StaticCell; | ||
| 19 | use {defmt_rtt as _, panic_probe as _}; | ||
| 20 | |||
| 21 | type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; | ||
| 22 | |||
| 23 | macro_rules! singleton { | ||
| 24 | ($val:expr) => {{ | ||
| 25 | type T = impl Sized; | ||
| 26 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | ||
| 27 | let (x,) = STATIC_CELL.init(($val,)); | ||
| 28 | x | ||
| 29 | }}; | ||
| 30 | } | ||
| 31 | |||
| 32 | const MTU: usize = 1514; | ||
| 33 | |||
| 34 | #[embassy_executor::task] | ||
| 35 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { | ||
| 36 | device.run().await | ||
| 37 | } | ||
| 38 | |||
| 39 | #[embassy_executor::task] | ||
| 40 | async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { | ||
| 41 | class.run().await | ||
| 42 | } | ||
| 43 | |||
| 44 | #[embassy_executor::task] | ||
| 45 | async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! { | ||
| 46 | stack.run().await | ||
| 47 | } | ||
| 48 | |||
| 49 | #[inline(never)] | ||
| 50 | pub fn test_function() -> (usize, u32, [u32; 2]) { | ||
| 51 | let mut array = [3; 2]; | ||
| 52 | |||
| 53 | let mut index = 0; | ||
| 54 | let mut result = 0; | ||
| 55 | |||
| 56 | for x in [1, 2] { | ||
| 57 | if x == 1 { | ||
| 58 | array[1] = 99; | ||
| 59 | } else { | ||
| 60 | index = if x == 2 { 1 } else { 0 }; | ||
| 61 | |||
| 62 | // grabs value from array[0], not array[1] | ||
| 63 | result = array[index]; | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | (index, result, array) | ||
| 68 | } | ||
| 69 | |||
| 70 | #[embassy_executor::main] | ||
| 71 | async fn main(spawner: Spawner) { | ||
| 72 | info!("{:?}", test_function()); | ||
| 73 | |||
| 74 | let p = embassy_nrf::init(Default::default()); | ||
| 75 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 76 | |||
| 77 | info!("Enabling ext hfosc..."); | ||
| 78 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | ||
| 79 | while clock.events_hfclkstarted.read().bits() != 1 {} | ||
| 80 | |||
| 81 | // Create the driver, from the HAL. | ||
| 82 | let irq = interrupt::take!(USBD); | ||
| 83 | let power_irq = interrupt::take!(POWER_CLOCK); | ||
| 84 | let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); | ||
| 85 | |||
| 86 | // Create embassy-usb Config | ||
| 87 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 88 | config.manufacturer = Some("Embassy"); | ||
| 89 | config.product = Some("USB-Ethernet example"); | ||
| 90 | config.serial_number = Some("12345678"); | ||
| 91 | config.max_power = 100; | ||
| 92 | config.max_packet_size_0 = 64; | ||
| 93 | |||
| 94 | // Required for Windows support. | ||
| 95 | config.composite_with_iads = true; | ||
| 96 | config.device_class = 0xEF; | ||
| 97 | config.device_sub_class = 0x02; | ||
| 98 | config.device_protocol = 0x01; | ||
| 99 | |||
| 100 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 101 | let mut builder = Builder::new( | ||
| 102 | driver, | ||
| 103 | config, | ||
| 104 | &mut singleton!([0; 256])[..], | ||
| 105 | &mut singleton!([0; 256])[..], | ||
| 106 | &mut singleton!([0; 256])[..], | ||
| 107 | &mut singleton!([0; 128])[..], | ||
| 108 | ); | ||
| 109 | |||
| 110 | // Our MAC addr. | ||
| 111 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; | ||
| 112 | // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. | ||
| 113 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; | ||
| 114 | |||
| 115 | // Create classes on the builder. | ||
| 116 | let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); | ||
| 117 | |||
| 118 | // Build the builder. | ||
| 119 | let usb = builder.build(); | ||
| 120 | |||
| 121 | unwrap!(spawner.spawn(usb_task(usb))); | ||
| 122 | |||
| 123 | let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(singleton!(NetState::new()), our_mac_addr); | ||
| 124 | unwrap!(spawner.spawn(usb_ncm_task(runner))); | ||
| 125 | |||
| 126 | let config = embassy_net::Config::Dhcp(Default::default()); | ||
| 127 | //let config = embassy_net::Config::Static(embassy_net::StaticConfig { | ||
| 128 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | ||
| 129 | // dns_servers: Vec::new(), | ||
| 130 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | ||
| 131 | //}); | ||
| 132 | |||
| 133 | // Generate random seed | ||
| 134 | let mut rng = Rng::new(p.RNG, interrupt::take!(RNG)); | ||
| 135 | let mut seed = [0; 8]; | ||
| 136 | rng.blocking_fill_bytes(&mut seed); | ||
| 137 | let seed = u64::from_le_bytes(seed); | ||
| 138 | |||
| 139 | // Init network stack | ||
| 140 | let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); | ||
| 141 | |||
| 142 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 143 | |||
| 144 | // And now we can use it! | ||
| 145 | |||
| 146 | let mut rx_buffer = [0; 4096]; | ||
| 147 | let mut tx_buffer = [0; 4096]; | ||
| 148 | let mut buf = [0; 4096]; | ||
| 149 | |||
| 150 | loop { | ||
| 151 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 152 | socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); | ||
| 153 | |||
| 154 | info!("Listening on TCP:1234..."); | ||
| 155 | if let Err(e) = socket.accept(1234).await { | ||
| 156 | warn!("accept error: {:?}", e); | ||
| 157 | continue; | ||
| 158 | } | ||
| 159 | |||
| 160 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 161 | |||
| 162 | loop { | ||
| 163 | let n = match socket.read(&mut buf).await { | ||
| 164 | Ok(0) => { | ||
| 165 | warn!("read EOF"); | ||
| 166 | break; | ||
| 167 | } | ||
| 168 | Ok(n) => n, | ||
| 169 | Err(e) => { | ||
| 170 | warn!("read error: {:?}", e); | ||
| 171 | break; | ||
| 172 | } | ||
| 173 | }; | ||
| 174 | |||
| 175 | info!("rxd {:02x}", &buf[..n]); | ||
| 176 | |||
| 177 | match socket.write_all(&buf[..n]).await { | ||
| 178 | Ok(()) => {} | ||
| 179 | Err(e) => { | ||
| 180 | warn!("write error: {:?}", e); | ||
| 181 | break; | ||
| 182 | } | ||
| 183 | }; | ||
| 184 | } | ||
| 185 | } | ||
| 186 | } | ||
diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index 76e198719..3d8a114cd 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs | |||
| @@ -10,13 +10,13 @@ use embassy_executor::Spawner; | |||
| 10 | use embassy_futures::join::join; | 10 | use embassy_futures::join::join; |
| 11 | use embassy_futures::select::{select, Either}; | 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::{Driver, HardwareVbusDetect}; |
| 14 | use embassy_nrf::{interrupt, pac}; | 14 | use embassy_nrf::{interrupt, pac}; |
| 15 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 15 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 16 | use embassy_sync::signal::Signal; | 16 | use embassy_sync::signal::Signal; |
| 17 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; | 17 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; |
| 18 | use embassy_usb::control::OutResponse; | 18 | use embassy_usb::control::OutResponse; |
| 19 | use embassy_usb::{Builder, Config, DeviceStateHandler}; | 19 | use embassy_usb::{Builder, Config, Handler}; |
| 20 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; | 20 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; |
| 21 | use {defmt_rtt as _, panic_probe as _}; | 21 | use {defmt_rtt as _, panic_probe as _}; |
| 22 | 22 | ||
| @@ -34,7 +34,7 @@ async fn main(_spawner: Spawner) { | |||
| 34 | // Create the driver, from the HAL. | 34 | // Create the driver, from the HAL. |
| 35 | let irq = interrupt::take!(USBD); | 35 | let irq = interrupt::take!(USBD); |
| 36 | let power_irq = interrupt::take!(POWER_CLOCK); | 36 | let power_irq = interrupt::take!(POWER_CLOCK); |
| 37 | let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); | 37 | let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); |
| 38 | 38 | ||
| 39 | // Create embassy-usb Config | 39 | // Create embassy-usb Config |
| 40 | let mut config = Config::new(0xc0de, 0xcafe); | 40 | let mut config = Config::new(0xc0de, 0xcafe); |
| @@ -52,7 +52,7 @@ async fn main(_spawner: Spawner) { | |||
| 52 | let mut bos_descriptor = [0; 256]; | 52 | let mut bos_descriptor = [0; 256]; |
| 53 | let mut control_buf = [0; 64]; | 53 | let mut control_buf = [0; 64]; |
| 54 | let request_handler = MyRequestHandler {}; | 54 | let request_handler = MyRequestHandler {}; |
| 55 | let device_state_handler = MyDeviceStateHandler::new(); | 55 | let mut device_handler = MyDeviceHandler::new(); |
| 56 | 56 | ||
| 57 | let mut state = State::new(); | 57 | let mut state = State::new(); |
| 58 | 58 | ||
| @@ -63,9 +63,10 @@ async fn main(_spawner: Spawner) { | |||
| 63 | &mut config_descriptor, | 63 | &mut config_descriptor, |
| 64 | &mut bos_descriptor, | 64 | &mut bos_descriptor, |
| 65 | &mut control_buf, | 65 | &mut control_buf, |
| 66 | Some(&device_state_handler), | ||
| 67 | ); | 66 | ); |
| 68 | 67 | ||
| 68 | builder.handler(&mut device_handler); | ||
| 69 | |||
| 69 | // Create classes on the builder. | 70 | // Create classes on the builder. |
| 70 | let config = embassy_usb::class::hid::Config { | 71 | let config = embassy_usb::class::hid::Config { |
| 71 | report_descriptor: KeyboardReport::desc(), | 72 | report_descriptor: KeyboardReport::desc(), |
| @@ -164,20 +165,20 @@ impl RequestHandler for MyRequestHandler { | |||
| 164 | } | 165 | } |
| 165 | } | 166 | } |
| 166 | 167 | ||
| 167 | struct MyDeviceStateHandler { | 168 | struct MyDeviceHandler { |
| 168 | configured: AtomicBool, | 169 | configured: AtomicBool, |
| 169 | } | 170 | } |
| 170 | 171 | ||
| 171 | impl MyDeviceStateHandler { | 172 | impl MyDeviceHandler { |
| 172 | fn new() -> Self { | 173 | fn new() -> Self { |
| 173 | MyDeviceStateHandler { | 174 | MyDeviceHandler { |
| 174 | configured: AtomicBool::new(false), | 175 | configured: AtomicBool::new(false), |
| 175 | } | 176 | } |
| 176 | } | 177 | } |
| 177 | } | 178 | } |
| 178 | 179 | ||
| 179 | impl DeviceStateHandler for MyDeviceStateHandler { | 180 | impl Handler for MyDeviceHandler { |
| 180 | fn enabled(&self, enabled: bool) { | 181 | fn enabled(&mut self, enabled: bool) { |
| 181 | self.configured.store(false, Ordering::Relaxed); | 182 | self.configured.store(false, Ordering::Relaxed); |
| 182 | SUSPENDED.store(false, Ordering::Release); | 183 | SUSPENDED.store(false, Ordering::Release); |
| 183 | if enabled { | 184 | if enabled { |
| @@ -187,17 +188,17 @@ impl DeviceStateHandler for MyDeviceStateHandler { | |||
| 187 | } | 188 | } |
| 188 | } | 189 | } |
| 189 | 190 | ||
| 190 | fn reset(&self) { | 191 | fn reset(&mut self) { |
| 191 | self.configured.store(false, Ordering::Relaxed); | 192 | self.configured.store(false, Ordering::Relaxed); |
| 192 | info!("Bus reset, the Vbus current limit is 100mA"); | 193 | info!("Bus reset, the Vbus current limit is 100mA"); |
| 193 | } | 194 | } |
| 194 | 195 | ||
| 195 | fn addressed(&self, addr: u8) { | 196 | fn addressed(&mut self, addr: u8) { |
| 196 | self.configured.store(false, Ordering::Relaxed); | 197 | self.configured.store(false, Ordering::Relaxed); |
| 197 | info!("USB address set to: {}", addr); | 198 | info!("USB address set to: {}", addr); |
| 198 | } | 199 | } |
| 199 | 200 | ||
| 200 | fn configured(&self, configured: bool) { | 201 | fn configured(&mut self, configured: bool) { |
| 201 | self.configured.store(configured, Ordering::Relaxed); | 202 | self.configured.store(configured, Ordering::Relaxed); |
| 202 | if configured { | 203 | if configured { |
| 203 | info!("Device configured, it may now draw up to the configured current limit from Vbus.") | 204 | info!("Device configured, it may now draw up to the configured current limit from Vbus.") |
| @@ -206,7 +207,7 @@ impl DeviceStateHandler for MyDeviceStateHandler { | |||
| 206 | } | 207 | } |
| 207 | } | 208 | } |
| 208 | 209 | ||
| 209 | fn suspended(&self, suspended: bool) { | 210 | fn suspended(&mut self, suspended: bool) { |
| 210 | if suspended { | 211 | if suspended { |
| 211 | info!("Device suspended, the Vbus current limit is 500µA (or 2.5mA for high-power devices with remote wakeup enabled)."); | 212 | 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); | 213 | 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 4916a38d4..d7c9d55b7 100644 --- a/examples/nrf/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs | |||
| @@ -7,7 +7,7 @@ use core::mem; | |||
| 7 | use defmt::*; | 7 | use defmt::*; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_futures::join::join; | 9 | use embassy_futures::join::join; |
| 10 | use embassy_nrf::usb::{Driver, PowerUsb}; | 10 | use embassy_nrf::usb::{Driver, HardwareVbusDetect}; |
| 11 | use embassy_nrf::{interrupt, pac}; | 11 | use embassy_nrf::{interrupt, pac}; |
| 12 | use embassy_time::{Duration, Timer}; | 12 | use embassy_time::{Duration, Timer}; |
| 13 | use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; | 13 | use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; |
| @@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) { | |||
| 28 | // Create the driver, from the HAL. | 28 | // Create the driver, from the HAL. |
| 29 | let irq = interrupt::take!(USBD); | 29 | let irq = interrupt::take!(USBD); |
| 30 | let power_irq = interrupt::take!(POWER_CLOCK); | 30 | let power_irq = interrupt::take!(POWER_CLOCK); |
| 31 | let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); | 31 | let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); |
| 32 | 32 | ||
| 33 | // Create embassy-usb Config | 33 | // Create embassy-usb Config |
| 34 | let mut config = Config::new(0xc0de, 0xcafe); | 34 | let mut config = Config::new(0xc0de, 0xcafe); |
| @@ -55,7 +55,6 @@ async fn main(_spawner: Spawner) { | |||
| 55 | &mut config_descriptor, | 55 | &mut config_descriptor, |
| 56 | &mut bos_descriptor, | 56 | &mut bos_descriptor, |
| 57 | &mut control_buf, | 57 | &mut control_buf, |
| 58 | None, | ||
| 59 | ); | 58 | ); |
| 60 | 59 | ||
| 61 | // Create classes on the builder. | 60 | // Create classes on the builder. |
diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs index 7c9c4184b..102d7ea60 100644 --- a/examples/nrf/src/bin/usb_serial.rs +++ b/examples/nrf52840/src/bin/usb_serial.rs | |||
| @@ -7,7 +7,7 @@ use core::mem; | |||
| 7 | use defmt::{info, panic}; | 7 | use defmt::{info, panic}; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_futures::join::join; | 9 | use embassy_futures::join::join; |
| 10 | use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply}; | 10 | use embassy_nrf::usb::{Driver, HardwareVbusDetect, Instance, VbusDetect}; |
| 11 | use embassy_nrf::{interrupt, pac}; | 11 | use embassy_nrf::{interrupt, pac}; |
| 12 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | 12 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; |
| 13 | use embassy_usb::driver::EndpointError; | 13 | use embassy_usb::driver::EndpointError; |
| @@ -26,7 +26,7 @@ async fn main(_spawner: Spawner) { | |||
| 26 | // Create the driver, from the HAL. | 26 | // Create the driver, from the HAL. |
| 27 | let irq = interrupt::take!(USBD); | 27 | let irq = interrupt::take!(USBD); |
| 28 | let power_irq = interrupt::take!(POWER_CLOCK); | 28 | let power_irq = interrupt::take!(POWER_CLOCK); |
| 29 | let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); | 29 | let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); |
| 30 | 30 | ||
| 31 | // Create embassy-usb Config | 31 | // Create embassy-usb Config |
| 32 | let mut config = Config::new(0xc0de, 0xcafe); | 32 | let mut config = Config::new(0xc0de, 0xcafe); |
| @@ -59,7 +59,6 @@ async fn main(_spawner: Spawner) { | |||
| 59 | &mut config_descriptor, | 59 | &mut config_descriptor, |
| 60 | &mut bos_descriptor, | 60 | &mut bos_descriptor, |
| 61 | &mut control_buf, | 61 | &mut control_buf, |
| 62 | None, | ||
| 63 | ); | 62 | ); |
| 64 | 63 | ||
| 65 | // Create classes on the builder. | 64 | // Create classes on the builder. |
| @@ -97,7 +96,7 @@ impl From<EndpointError> for Disconnected { | |||
| 97 | } | 96 | } |
| 98 | } | 97 | } |
| 99 | 98 | ||
| 100 | async fn echo<'d, T: Instance + 'd, P: UsbSupply + 'd>( | 99 | async fn echo<'d, T: Instance + 'd, P: VbusDetect + 'd>( |
| 101 | class: &mut CdcAcmClass<'d, Driver<'d, T, P>>, | 100 | class: &mut CdcAcmClass<'d, Driver<'d, T, P>>, |
| 102 | ) -> Result<(), Disconnected> { | 101 | ) -> Result<(), Disconnected> { |
| 103 | let mut buf = [0; 64]; | 102 | 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 93efc2fe6..558d4ba60 100644 --- a/examples/nrf/src/bin/usb_serial_multitask.rs +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs | |||
| @@ -6,7 +6,7 @@ use core::mem; | |||
| 6 | 6 | ||
| 7 | use defmt::{info, panic, unwrap}; | 7 | use defmt::{info, panic, unwrap}; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_nrf::usb::{Driver, PowerUsb}; | 9 | use embassy_nrf::usb::{Driver, HardwareVbusDetect}; |
| 10 | use embassy_nrf::{interrupt, pac, peripherals}; | 10 | use embassy_nrf::{interrupt, pac, peripherals}; |
| 11 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | 11 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; |
| 12 | use embassy_usb::driver::EndpointError; | 12 | use embassy_usb::driver::EndpointError; |
| @@ -14,7 +14,7 @@ use embassy_usb::{Builder, Config, UsbDevice}; | |||
| 14 | use static_cell::StaticCell; | 14 | use static_cell::StaticCell; |
| 15 | use {defmt_rtt as _, panic_probe as _}; | 15 | use {defmt_rtt as _, panic_probe as _}; |
| 16 | 16 | ||
| 17 | type MyDriver = Driver<'static, peripherals::USBD, PowerUsb>; | 17 | type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; |
| 18 | 18 | ||
| 19 | #[embassy_executor::task] | 19 | #[embassy_executor::task] |
| 20 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) { | 20 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) { |
| @@ -42,7 +42,7 @@ async fn main(spawner: Spawner) { | |||
| 42 | // Create the driver, from the HAL. | 42 | // Create the driver, from the HAL. |
| 43 | let irq = interrupt::take!(USBD); | 43 | let irq = interrupt::take!(USBD); |
| 44 | let power_irq = interrupt::take!(POWER_CLOCK); | 44 | let power_irq = interrupt::take!(POWER_CLOCK); |
| 45 | let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); | 45 | let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); |
| 46 | 46 | ||
| 47 | // Create embassy-usb Config | 47 | // Create embassy-usb Config |
| 48 | let mut config = Config::new(0xc0de, 0xcafe); | 48 | let mut config = Config::new(0xc0de, 0xcafe); |
| @@ -83,7 +83,6 @@ async fn main(spawner: Spawner) { | |||
| 83 | &mut res.config_descriptor, | 83 | &mut res.config_descriptor, |
| 84 | &mut res.bos_descriptor, | 84 | &mut res.bos_descriptor, |
| 85 | &mut res.control_buf, | 85 | &mut res.control_buf, |
| 86 | None, | ||
| 87 | ); | 86 | ); |
| 88 | 87 | ||
| 89 | // Create classes on the builder. | 88 | // Create classes on the builder. |
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..6561fc3b4 --- /dev/null +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs | |||
| @@ -0,0 +1,130 @@ | |||
| 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::{Driver, HardwareVbusDetect, Instance, VbusDetect}; | ||
| 11 | use embassy_nrf::{interrupt, pac}; | ||
| 12 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 13 | use embassy_usb::driver::EndpointError; | ||
| 14 | use embassy_usb::msos::{self, windows_version}; | ||
| 15 | use embassy_usb::types::InterfaceNumber; | ||
| 16 | use embassy_usb::{Builder, Config}; | ||
| 17 | use {defmt_rtt as _, panic_probe as _}; | ||
| 18 | |||
| 19 | // This is a randomly generated GUID to allow clients on Windows to find our device | ||
| 20 | const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321}"]; | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(_spawner: Spawner) { | ||
| 24 | let p = embassy_nrf::init(Default::default()); | ||
| 25 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 26 | |||
| 27 | info!("Enabling ext hfosc..."); | ||
| 28 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | ||
| 29 | while clock.events_hfclkstarted.read().bits() != 1 {} | ||
| 30 | |||
| 31 | // Create the driver, from the HAL. | ||
| 32 | let irq = interrupt::take!(USBD); | ||
| 33 | let power_irq = interrupt::take!(POWER_CLOCK); | ||
| 34 | let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); | ||
| 35 | |||
| 36 | // Create embassy-usb Config | ||
| 37 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 38 | config.manufacturer = Some("Embassy"); | ||
| 39 | config.product = Some("USB-serial example"); | ||
| 40 | config.serial_number = Some("12345678"); | ||
| 41 | config.max_power = 100; | ||
| 42 | config.max_packet_size_0 = 64; | ||
| 43 | |||
| 44 | // Required for windows compatiblity. | ||
| 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 msos_descriptor = [0; 256]; | ||
| 57 | let mut control_buf = [0; 64]; | ||
| 58 | |||
| 59 | let mut state = State::new(); | ||
| 60 | |||
| 61 | let mut builder = Builder::new( | ||
| 62 | driver, | ||
| 63 | config, | ||
| 64 | &mut device_descriptor, | ||
| 65 | &mut config_descriptor, | ||
| 66 | &mut bos_descriptor, | ||
| 67 | &mut msos_descriptor, | ||
| 68 | &mut control_buf, | ||
| 69 | ); | ||
| 70 | |||
| 71 | builder.msos_descriptor(windows_version::WIN8_1, 2); | ||
| 72 | |||
| 73 | // Create classes on the builder. | ||
| 74 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 75 | |||
| 76 | // Since we want to create MS OS feature descriptors that apply to a function that has already been added to the | ||
| 77 | // builder, need to get the MsOsDescriptorWriter from the builder and manually add those descriptors. | ||
| 78 | // Inside a class constructor, you would just need to call `FunctionBuilder::msos_feature` instead. | ||
| 79 | let msos_writer = builder.msos_writer(); | ||
| 80 | msos_writer.configuration(0); | ||
| 81 | msos_writer.function(InterfaceNumber(0)); | ||
| 82 | msos_writer.function_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); | ||
| 83 | msos_writer.function_feature(msos::RegistryPropertyFeatureDescriptor::new( | ||
| 84 | "DeviceInterfaceGUIDs", | ||
| 85 | msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS), | ||
| 86 | )); | ||
| 87 | |||
| 88 | // Build the builder. | ||
| 89 | let mut usb = builder.build(); | ||
| 90 | |||
| 91 | // Run the USB device. | ||
| 92 | let usb_fut = usb.run(); | ||
| 93 | |||
| 94 | // Do stuff with the class! | ||
| 95 | let echo_fut = async { | ||
| 96 | loop { | ||
| 97 | class.wait_connection().await; | ||
| 98 | info!("Connected"); | ||
| 99 | let _ = echo(&mut class).await; | ||
| 100 | info!("Disconnected"); | ||
| 101 | } | ||
| 102 | }; | ||
| 103 | |||
| 104 | // Run everything concurrently. | ||
| 105 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 106 | join(usb_fut, echo_fut).await; | ||
| 107 | } | ||
| 108 | |||
| 109 | struct Disconnected {} | ||
| 110 | |||
| 111 | impl From<EndpointError> for Disconnected { | ||
| 112 | fn from(val: EndpointError) -> Self { | ||
| 113 | match val { | ||
| 114 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 115 | EndpointError::Disabled => Disconnected {}, | ||
| 116 | } | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | async fn echo<'d, T: Instance + 'd, P: VbusDetect + 'd>( | ||
| 121 | class: &mut CdcAcmClass<'d, Driver<'d, T, P>>, | ||
| 122 | ) -> Result<(), Disconnected> { | ||
| 123 | let mut buf = [0; 64]; | ||
| 124 | loop { | ||
| 125 | let n = class.read_packet(&mut buf).await?; | ||
| 126 | let data = &buf[..n]; | ||
| 127 | info!("data: {:x}", data); | ||
| 128 | class.write_packet(data).await?; | ||
| 129 | } | ||
| 130 | } | ||
diff --git a/examples/nrf/src/bin/wdt.rs b/examples/nrf52840/src/bin/wdt.rs index b0b9c3b81..b0b9c3b81 100644 --- a/examples/nrf/src/bin/wdt.rs +++ b/examples/nrf52840/src/bin/wdt.rs | |||
diff --git a/examples/nrf5340/.cargo/config.toml b/examples/nrf5340/.cargo/config.toml new file mode 100644 index 000000000..ff0879c8c --- /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-run --list-chips` | ||
| 3 | runner = "probe-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..eed493012 --- /dev/null +++ b/examples/nrf5340/Cargo.toml | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-nrf5340-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [features] | ||
| 8 | default = ["nightly"] | ||
| 9 | nightly = [ | ||
| 10 | "embassy-executor/nightly", | ||
| 11 | "embassy-nrf/nightly", | ||
| 12 | "embassy-net/nightly", | ||
| 13 | "embassy-nrf/unstable-traits", | ||
| 14 | "embassy-usb", | ||
| 15 | "embedded-io/async", | ||
| 16 | "embassy-net", | ||
| 17 | ] | ||
| 18 | |||
| 19 | [dependencies] | ||
| 20 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||
| 21 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = [ | ||
| 22 | "defmt", | ||
| 23 | ] } | ||
| 24 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = [ | ||
| 25 | "defmt", | ||
| 26 | "integrated-timers", | ||
| 27 | ] } | ||
| 28 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = [ | ||
| 29 | "defmt", | ||
| 30 | "defmt-timestamp-uptime", | ||
| 31 | ] } | ||
| 32 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = [ | ||
| 33 | "defmt", | ||
| 34 | "nrf5340-app-s", | ||
| 35 | "time-driver-rtc1", | ||
| 36 | "gpiote", | ||
| 37 | "unstable-pac", | ||
| 38 | ] } | ||
| 39 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = [ | ||
| 40 | "defmt", | ||
| 41 | "tcp", | ||
| 42 | "dhcpv4", | ||
| 43 | "medium-ethernet", | ||
| 44 | ], optional = true } | ||
| 45 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = [ | ||
| 46 | "defmt", | ||
| 47 | ], optional = true } | ||
| 48 | embedded-io = "0.4.0" | ||
| 49 | |||
| 50 | |||
| 51 | defmt = "0.3" | ||
| 52 | defmt-rtt = "0.4" | ||
| 53 | |||
| 54 | static_cell = "1.0" | ||
| 55 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | ||
| 56 | cortex-m-rt = "0.7.0" | ||
| 57 | panic-probe = { version = "0.3", features = ["print-defmt"] } | ||
| 58 | futures = { version = "0.3.17", default-features = false, features = [ | ||
| 59 | "async-await", | ||
| 60 | ] } | ||
| 61 | rand = { version = "0.8.4", default-features = false } | ||
| 62 | embedded-storage = "0.3.0" | ||
| 63 | usbd-hid = "0.6.0" | ||
| 64 | 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/nrf5340/src/bin/uart.rs b/examples/nrf5340/src/bin/uart.rs new file mode 100644 index 000000000..0f2b7b1e3 --- /dev/null +++ b/examples/nrf5340/src/bin/uart.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_nrf::{interrupt, uarte}; | ||
| 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 config = uarte::Config::default(); | ||
| 14 | config.parity = uarte::Parity::EXCLUDED; | ||
| 15 | config.baudrate = uarte::Baudrate::BAUD115200; | ||
| 16 | |||
| 17 | let irq = interrupt::take!(SERIAL0); | ||
| 18 | let mut uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P1_00, p.P1_01, config); | ||
| 19 | |||
| 20 | info!("uarte initialized!"); | ||
| 21 | |||
| 22 | // Message must be in SRAM | ||
| 23 | let mut buf = [0; 8]; | ||
| 24 | buf.copy_from_slice(b"Hello!\r\n"); | ||
| 25 | |||
| 26 | unwrap!(uart.write(&buf).await); | ||
| 27 | info!("wrote hello in uart!"); | ||
| 28 | |||
| 29 | loop { | ||
| 30 | info!("reading..."); | ||
| 31 | unwrap!(uart.read(&mut buf).await); | ||
| 32 | info!("writing..."); | ||
| 33 | unwrap!(uart.write(&buf).await); | ||
| 34 | } | ||
| 35 | } | ||
diff --git a/examples/rp/.cargo/config.toml b/examples/rp/.cargo/config.toml index 3d6051389..d1c8c1c5a 100644 --- a/examples/rp/.cargo/config.toml +++ b/examples/rp/.cargo/config.toml | |||
| @@ -5,4 +5,4 @@ runner = "probe-run --chip RP2040" | |||
| 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 a5af8b2f0..f07684f29 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml | |||
| @@ -9,15 +9,17 @@ license = "MIT OR Apache-2.0" | |||
| 9 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 9 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 10 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 10 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } |
| 11 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 11 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 12 | embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } | 12 | embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } |
| 13 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | 13 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } |
| 14 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } | 14 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } |
| 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 16 | embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } | ||
| 16 | 17 | ||
| 17 | defmt = "0.3" | 18 | defmt = "0.3" |
| 18 | defmt-rtt = "0.3" | 19 | defmt-rtt = "0.4" |
| 19 | 20 | ||
| 20 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 21 | #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 22 | cortex-m = { version = "0.7.6" } | ||
| 21 | cortex-m-rt = "0.7.0" | 23 | cortex-m-rt = "0.7.0" |
| 22 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 24 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 23 | futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } | 25 | futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } |
| @@ -28,6 +30,13 @@ display-interface = "0.4.1" | |||
| 28 | byte-slice-cast = { version = "1.2.0", default-features = false } | 30 | byte-slice-cast = { version = "1.2.0", default-features = false } |
| 29 | 31 | ||
| 30 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } | 32 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } |
| 31 | embedded-hal-async = { version = "0.1.0-alpha.1" } | 33 | embedded-hal-async = "0.2.0-alpha.0" |
| 32 | embedded-io = { version = "0.3.0", features = ["async", "defmt"] } | 34 | embedded-io = { version = "0.4.0", features = ["async", "defmt"] } |
| 35 | embedded-storage = { version = "0.3" } | ||
| 33 | static_cell = "1.0.0" | 36 | static_cell = "1.0.0" |
| 37 | log = "0.4" | ||
| 38 | pio-proc = "0.2" | ||
| 39 | pio = "0.2.1" | ||
| 40 | |||
| 41 | [profile.release] | ||
| 42 | debug = true | ||
diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs new file mode 100644 index 000000000..4202fd394 --- /dev/null +++ b/examples/rp/src/bin/adc.rs | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_rp::adc::{Adc, Config}; | ||
| 8 | use embassy_rp::interrupt; | ||
| 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_rp::init(Default::default()); | ||
| 15 | let irq = interrupt::take!(ADC_IRQ_FIFO); | ||
| 16 | let mut adc = Adc::new(p.ADC, irq, Config::default()); | ||
| 17 | |||
| 18 | let mut p26 = p.PIN_26; | ||
| 19 | let mut p27 = p.PIN_27; | ||
| 20 | let mut p28 = p.PIN_28; | ||
| 21 | |||
| 22 | loop { | ||
| 23 | let level = adc.read(&mut p26).await; | ||
| 24 | info!("Pin 26 ADC: {}", level); | ||
| 25 | let level = adc.read(&mut p27).await; | ||
| 26 | info!("Pin 27 ADC: {}", level); | ||
| 27 | let level = adc.read(&mut p28).await; | ||
| 28 | info!("Pin 28 ADC: {}", level); | ||
| 29 | let temp = adc.read_temperature().await; | ||
| 30 | info!("Temp: {} degrees", convert_to_celsius(temp)); | ||
| 31 | Timer::after(Duration::from_secs(1)).await; | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | fn convert_to_celsius(raw_temp: u16) -> f32 { | ||
| 36 | // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet | ||
| 37 | 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721 as f32 | ||
| 38 | } | ||
diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs new file mode 100644 index 000000000..8d6b379f4 --- /dev/null +++ b/examples/rp/src/bin/flash.rs | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; | ||
| 8 | use embassy_rp::peripherals::FLASH; | ||
| 9 | use embassy_time::{Duration, Timer}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | const ADDR_OFFSET: u32 = 0x100000; | ||
| 13 | const FLASH_SIZE: usize = 2 * 1024 * 1024; | ||
| 14 | |||
| 15 | #[embassy_executor::main] | ||
| 16 | async fn main(_spawner: Spawner) { | ||
| 17 | let p = embassy_rp::init(Default::default()); | ||
| 18 | info!("Hello World!"); | ||
| 19 | |||
| 20 | // add some delay to give an attached debug probe time to parse the | ||
| 21 | // defmt RTT header. Reading that header might touch flash memory, which | ||
| 22 | // interferes with flash write operations. | ||
| 23 | // https://github.com/knurling-rs/defmt/pull/683 | ||
| 24 | Timer::after(Duration::from_millis(10)).await; | ||
| 25 | |||
| 26 | let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH); | ||
| 27 | erase_write_sector(&mut flash, 0x00); | ||
| 28 | |||
| 29 | multiwrite_bytes(&mut flash, ERASE_SIZE as u32); | ||
| 30 | |||
| 31 | loop {} | ||
| 32 | } | ||
| 33 | |||
| 34 | fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { | ||
| 35 | info!(">>>> [multiwrite_bytes]"); | ||
| 36 | let mut read_buf = [0u8; ERASE_SIZE]; | ||
| 37 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); | ||
| 38 | |||
| 39 | info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); | ||
| 40 | info!("Contents start with {=[u8]}", read_buf[0..4]); | ||
| 41 | |||
| 42 | defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); | ||
| 43 | |||
| 44 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); | ||
| 45 | info!("Contents after erase starts with {=[u8]}", read_buf[0..4]); | ||
| 46 | if read_buf.iter().any(|x| *x != 0xFF) { | ||
| 47 | defmt::panic!("unexpected"); | ||
| 48 | } | ||
| 49 | |||
| 50 | defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &[0x01])); | ||
| 51 | defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 1, &[0x02])); | ||
| 52 | defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 2, &[0x03])); | ||
| 53 | defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 3, &[0x04])); | ||
| 54 | |||
| 55 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); | ||
| 56 | info!("Contents after write starts with {=[u8]}", read_buf[0..4]); | ||
| 57 | if &read_buf[0..4] != &[0x01, 0x02, 0x03, 0x04] { | ||
| 58 | defmt::panic!("unexpected"); | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { | ||
| 63 | info!(">>>> [erase_write_sector]"); | ||
| 64 | let mut buf = [0u8; ERASE_SIZE]; | ||
| 65 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); | ||
| 66 | |||
| 67 | info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); | ||
| 68 | info!("Contents start with {=[u8]}", buf[0..4]); | ||
| 69 | |||
| 70 | defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); | ||
| 71 | |||
| 72 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); | ||
| 73 | info!("Contents after erase starts with {=[u8]}", buf[0..4]); | ||
| 74 | if buf.iter().any(|x| *x != 0xFF) { | ||
| 75 | defmt::panic!("unexpected"); | ||
| 76 | } | ||
| 77 | |||
| 78 | for b in buf.iter_mut() { | ||
| 79 | *b = 0xDA; | ||
| 80 | } | ||
| 81 | |||
| 82 | defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &buf)); | ||
| 83 | |||
| 84 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); | ||
| 85 | info!("Contents after write starts with {=[u8]}", buf[0..4]); | ||
| 86 | if buf.iter().any(|x| *x != 0xDA) { | ||
| 87 | defmt::panic!("unexpected"); | ||
| 88 | } | ||
| 89 | } | ||
diff --git a/examples/rp/src/bin/i2c_async.rs b/examples/rp/src/bin/i2c_async.rs new file mode 100644 index 000000000..d1a2e3cd7 --- /dev/null +++ b/examples/rp/src/bin/i2c_async.rs | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_rp::i2c::{self, Config}; | ||
| 8 | use embassy_rp::interrupt; | ||
| 9 | use embassy_time::{Duration, Timer}; | ||
| 10 | use embedded_hal_async::i2c::I2c; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | #[allow(dead_code)] | ||
| 14 | mod mcp23017 { | ||
| 15 | pub const ADDR: u8 = 0x20; // default addr | ||
| 16 | |||
| 17 | macro_rules! mcpregs { | ||
| 18 | ($($name:ident : $val:expr),* $(,)?) => { | ||
| 19 | $( | ||
| 20 | pub const $name: u8 = $val; | ||
| 21 | )* | ||
| 22 | |||
| 23 | pub fn regname(reg: u8) -> &'static str { | ||
| 24 | match reg { | ||
| 25 | $( | ||
| 26 | $val => stringify!($name), | ||
| 27 | )* | ||
| 28 | _ => panic!("bad reg"), | ||
| 29 | } | ||
| 30 | } | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | // These are correct for IOCON.BANK=0 | ||
| 35 | mcpregs! { | ||
| 36 | IODIRA: 0x00, | ||
| 37 | IPOLA: 0x02, | ||
| 38 | GPINTENA: 0x04, | ||
| 39 | DEFVALA: 0x06, | ||
| 40 | INTCONA: 0x08, | ||
| 41 | IOCONA: 0x0A, | ||
| 42 | GPPUA: 0x0C, | ||
| 43 | INTFA: 0x0E, | ||
| 44 | INTCAPA: 0x10, | ||
| 45 | GPIOA: 0x12, | ||
| 46 | OLATA: 0x14, | ||
| 47 | IODIRB: 0x01, | ||
| 48 | IPOLB: 0x03, | ||
| 49 | GPINTENB: 0x05, | ||
| 50 | DEFVALB: 0x07, | ||
| 51 | INTCONB: 0x09, | ||
| 52 | IOCONB: 0x0B, | ||
| 53 | GPPUB: 0x0D, | ||
| 54 | INTFB: 0x0F, | ||
| 55 | INTCAPB: 0x11, | ||
| 56 | GPIOB: 0x13, | ||
| 57 | OLATB: 0x15, | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | #[embassy_executor::main] | ||
| 62 | async fn main(_spawner: Spawner) { | ||
| 63 | let p = embassy_rp::init(Default::default()); | ||
| 64 | |||
| 65 | let sda = p.PIN_14; | ||
| 66 | let scl = p.PIN_15; | ||
| 67 | let irq = interrupt::take!(I2C1_IRQ); | ||
| 68 | |||
| 69 | info!("set up i2c "); | ||
| 70 | let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, irq, Config::default()); | ||
| 71 | |||
| 72 | use mcp23017::*; | ||
| 73 | |||
| 74 | info!("init mcp23017 config for IxpandO"); | ||
| 75 | // init - a outputs, b inputs | ||
| 76 | i2c.write(ADDR, &[IODIRA, 0x00]).await.unwrap(); | ||
| 77 | i2c.write(ADDR, &[IODIRB, 0xff]).await.unwrap(); | ||
| 78 | i2c.write(ADDR, &[GPPUB, 0xff]).await.unwrap(); // pullups | ||
| 79 | |||
| 80 | let mut val = 1; | ||
| 81 | loop { | ||
| 82 | let mut portb = [0]; | ||
| 83 | |||
| 84 | i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).await.unwrap(); | ||
| 85 | info!("portb = {:02x}", portb[0]); | ||
| 86 | i2c.write(mcp23017::ADDR, &[GPIOA, val | portb[0]]).await.unwrap(); | ||
| 87 | val = val.rotate_left(1); | ||
| 88 | |||
| 89 | // get a register dump | ||
| 90 | info!("getting register dump"); | ||
| 91 | let mut regs = [0; 22]; | ||
| 92 | i2c.write_read(ADDR, &[0], &mut regs).await.unwrap(); | ||
| 93 | // always get the regdump but only display it if portb'0 is set | ||
| 94 | if portb[0] & 1 != 0 { | ||
| 95 | for (idx, reg) in regs.into_iter().enumerate() { | ||
| 96 | info!("{} => {:02x}", regname(idx as u8), reg); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | Timer::after(Duration::from_millis(100)).await; | ||
| 101 | } | ||
| 102 | } | ||
diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs new file mode 100644 index 000000000..376b2b61e --- /dev/null +++ b/examples/rp/src/bin/multicore.rs | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Executor; | ||
| 7 | use embassy_executor::_export::StaticCell; | ||
| 8 | use embassy_rp::gpio::{Level, Output}; | ||
| 9 | use embassy_rp::multicore::{spawn_core1, Stack}; | ||
| 10 | use embassy_rp::peripherals::PIN_25; | ||
| 11 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 12 | use embassy_sync::channel::Channel; | ||
| 13 | use embassy_time::{Duration, Timer}; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | static mut CORE1_STACK: Stack<4096> = Stack::new(); | ||
| 17 | static EXECUTOR0: StaticCell<Executor> = StaticCell::new(); | ||
| 18 | static EXECUTOR1: StaticCell<Executor> = StaticCell::new(); | ||
| 19 | static CHANNEL: Channel<CriticalSectionRawMutex, LedState, 1> = Channel::new(); | ||
| 20 | |||
| 21 | enum LedState { | ||
| 22 | On, | ||
| 23 | Off, | ||
| 24 | } | ||
| 25 | |||
| 26 | #[cortex_m_rt::entry] | ||
| 27 | fn main() -> ! { | ||
| 28 | let p = embassy_rp::init(Default::default()); | ||
| 29 | let led = Output::new(p.PIN_25, Level::Low); | ||
| 30 | |||
| 31 | spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { | ||
| 32 | let executor1 = EXECUTOR1.init(Executor::new()); | ||
| 33 | executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led)))); | ||
| 34 | }); | ||
| 35 | |||
| 36 | let executor0 = EXECUTOR0.init(Executor::new()); | ||
| 37 | executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); | ||
| 38 | } | ||
| 39 | |||
| 40 | #[embassy_executor::task] | ||
| 41 | async fn core0_task() { | ||
| 42 | info!("Hello from core 0"); | ||
| 43 | loop { | ||
| 44 | CHANNEL.send(LedState::On).await; | ||
| 45 | Timer::after(Duration::from_millis(100)).await; | ||
| 46 | CHANNEL.send(LedState::Off).await; | ||
| 47 | Timer::after(Duration::from_millis(400)).await; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | #[embassy_executor::task] | ||
| 52 | async fn core1_task(mut led: Output<'static, PIN_25>) { | ||
| 53 | info!("Hello from core 1"); | ||
| 54 | loop { | ||
| 55 | match CHANNEL.recv().await { | ||
| 56 | LedState::On => led.set_high(), | ||
| 57 | LedState::Off => led.set_low(), | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } | ||
diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs new file mode 100644 index 000000000..45a8c73f7 --- /dev/null +++ b/examples/rp/src/bin/pio_async.rs | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | use defmt::info; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_rp::gpio::{AnyPin, Pin}; | ||
| 7 | use embassy_rp::pio::{Pio0, PioPeripherial, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2}; | ||
| 8 | use embassy_rp::pio_instr_util; | ||
| 9 | use embassy_rp::relocate::RelocatedProgram; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::task] | ||
| 13 | async fn pio_task_sm0(mut sm: PioStateMachineInstance<Pio0, Sm0>, pin: AnyPin) { | ||
| 14 | // Setup sm0 | ||
| 15 | |||
| 16 | // Send data serially to pin | ||
| 17 | let prg = pio_proc::pio_asm!( | ||
| 18 | ".origin 16", | ||
| 19 | "set pindirs, 1", | ||
| 20 | ".wrap_target", | ||
| 21 | "out pins,1 [19]", | ||
| 22 | ".wrap", | ||
| 23 | ); | ||
| 24 | |||
| 25 | let relocated = RelocatedProgram::new(&prg.program); | ||
| 26 | let out_pin = sm.make_pio_pin(pin); | ||
| 27 | let pio_pins = [&out_pin]; | ||
| 28 | sm.set_out_pins(&pio_pins); | ||
| 29 | sm.write_instr(relocated.origin() as usize, relocated.code()); | ||
| 30 | pio_instr_util::exec_jmp(&mut sm, relocated.origin()); | ||
| 31 | sm.set_clkdiv((125e6 / 20.0 / 2e2 * 256.0) as u32); | ||
| 32 | sm.set_set_range(0, 1); | ||
| 33 | let pio::Wrap { source, target } = relocated.wrap(); | ||
| 34 | sm.set_wrap(source, target); | ||
| 35 | |||
| 36 | sm.set_autopull(true); | ||
| 37 | sm.set_out_shift_dir(ShiftDirection::Left); | ||
| 38 | |||
| 39 | sm.set_enable(true); | ||
| 40 | |||
| 41 | let mut v = 0x0f0caffa; | ||
| 42 | loop { | ||
| 43 | sm.wait_push(v).await; | ||
| 44 | v ^= 0xffff; | ||
| 45 | info!("Pushed {:032b} to FIFO", v); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | #[embassy_executor::task] | ||
| 50 | async fn pio_task_sm1(mut sm: PioStateMachineInstance<Pio0, Sm1>) { | ||
| 51 | // Setupm sm1 | ||
| 52 | |||
| 53 | // Read 0b10101 repeatedly until ISR is full | ||
| 54 | let prg = pio_proc::pio_asm!(".origin 8", "set x, 0x15", ".wrap_target", "in x, 5 [31]", ".wrap",); | ||
| 55 | |||
| 56 | let relocated = RelocatedProgram::new(&prg.program); | ||
| 57 | sm.write_instr(relocated.origin() as usize, relocated.code()); | ||
| 58 | pio_instr_util::exec_jmp(&mut sm, relocated.origin()); | ||
| 59 | sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); | ||
| 60 | sm.set_set_range(0, 0); | ||
| 61 | let pio::Wrap { source, target } = relocated.wrap(); | ||
| 62 | sm.set_wrap(source, target); | ||
| 63 | |||
| 64 | sm.set_autopush(true); | ||
| 65 | sm.set_in_shift_dir(ShiftDirection::Right); | ||
| 66 | sm.set_enable(true); | ||
| 67 | loop { | ||
| 68 | let rx = sm.wait_pull().await; | ||
| 69 | info!("Pulled {:032b} from FIFO", rx); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | #[embassy_executor::task] | ||
| 74 | async fn pio_task_sm2(mut sm: PioStateMachineInstance<Pio0, Sm2>) { | ||
| 75 | // Setup sm2 | ||
| 76 | |||
| 77 | // Repeatedly trigger IRQ 3 | ||
| 78 | let prg = pio_proc::pio_asm!( | ||
| 79 | ".origin 0", | ||
| 80 | ".wrap_target", | ||
| 81 | "set x,10", | ||
| 82 | "delay:", | ||
| 83 | "jmp x-- delay [15]", | ||
| 84 | "irq 3 [15]", | ||
| 85 | ".wrap", | ||
| 86 | ); | ||
| 87 | let relocated = RelocatedProgram::new(&prg.program); | ||
| 88 | sm.write_instr(relocated.origin() as usize, relocated.code()); | ||
| 89 | |||
| 90 | let pio::Wrap { source, target } = relocated.wrap(); | ||
| 91 | sm.set_wrap(source, target); | ||
| 92 | |||
| 93 | pio_instr_util::exec_jmp(&mut sm, relocated.origin()); | ||
| 94 | sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); | ||
| 95 | sm.set_enable(true); | ||
| 96 | loop { | ||
| 97 | sm.wait_irq(3).await; | ||
| 98 | info!("IRQ trigged"); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | #[embassy_executor::main] | ||
| 103 | async fn main(spawner: Spawner) { | ||
| 104 | let p = embassy_rp::init(Default::default()); | ||
| 105 | let pio = p.PIO0; | ||
| 106 | |||
| 107 | let (_, sm0, sm1, sm2, ..) = pio.split(); | ||
| 108 | |||
| 109 | spawner.spawn(pio_task_sm0(sm0, p.PIN_0.degrade())).unwrap(); | ||
| 110 | spawner.spawn(pio_task_sm1(sm1)).unwrap(); | ||
| 111 | spawner.spawn(pio_task_sm2(sm2)).unwrap(); | ||
| 112 | } | ||
diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs new file mode 100644 index 000000000..b19ef4083 --- /dev/null +++ b/examples/rp/src/bin/pio_dma.rs | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | use defmt::info; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_futures::join::join; | ||
| 7 | use embassy_rp::pio::{PioPeripherial, PioStateMachine, ShiftDirection}; | ||
| 8 | use embassy_rp::relocate::RelocatedProgram; | ||
| 9 | use embassy_rp::{pio_instr_util, Peripheral}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | fn swap_nibbles(v: u32) -> u32 { | ||
| 13 | let v = (v & 0x0f0f_0f0f) << 4 | (v & 0xf0f0_f0f0) >> 4; | ||
| 14 | let v = (v & 0x00ff_00ff) << 8 | (v & 0xff00_ff00) >> 8; | ||
| 15 | (v & 0x0000_ffff) << 16 | (v & 0xffff_0000) >> 16 | ||
| 16 | } | ||
| 17 | |||
| 18 | #[embassy_executor::main] | ||
| 19 | async fn main(_spawner: Spawner) { | ||
| 20 | let p = embassy_rp::init(Default::default()); | ||
| 21 | let pio = p.PIO0; | ||
| 22 | let (_, mut sm, ..) = pio.split(); | ||
| 23 | |||
| 24 | let prg = pio_proc::pio_asm!( | ||
| 25 | ".origin 0", | ||
| 26 | "set pindirs,1", | ||
| 27 | ".wrap_target", | ||
| 28 | "set y,7", | ||
| 29 | "loop:", | ||
| 30 | "out x,4", | ||
| 31 | "in x,4", | ||
| 32 | "jmp y--, loop", | ||
| 33 | ".wrap", | ||
| 34 | ); | ||
| 35 | |||
| 36 | let relocated = RelocatedProgram::new(&prg.program); | ||
| 37 | sm.write_instr(relocated.origin() as usize, relocated.code()); | ||
| 38 | pio_instr_util::exec_jmp(&mut sm, relocated.origin()); | ||
| 39 | sm.set_clkdiv((125e6 / 10e3 * 256.0) as u32); | ||
| 40 | let pio::Wrap { source, target } = relocated.wrap(); | ||
| 41 | sm.set_wrap(source, target); | ||
| 42 | sm.set_autopull(true); | ||
| 43 | sm.set_autopush(true); | ||
| 44 | sm.set_pull_threshold(32); | ||
| 45 | sm.set_push_threshold(32); | ||
| 46 | sm.set_out_shift_dir(ShiftDirection::Right); | ||
| 47 | sm.set_in_shift_dir(ShiftDirection::Left); | ||
| 48 | |||
| 49 | sm.set_enable(true); | ||
| 50 | |||
| 51 | let mut dma_out_ref = p.DMA_CH0.into_ref(); | ||
| 52 | let mut dma_in_ref = p.DMA_CH1.into_ref(); | ||
| 53 | let mut dout = [0x12345678u32; 29]; | ||
| 54 | for i in 1..dout.len() { | ||
| 55 | dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7; | ||
| 56 | } | ||
| 57 | let mut din = [0u32; 29]; | ||
| 58 | loop { | ||
| 59 | join( | ||
| 60 | sm.dma_push(dma_out_ref.reborrow(), &dout), | ||
| 61 | sm.dma_pull(dma_in_ref.reborrow(), &mut din), | ||
| 62 | ) | ||
| 63 | .await; | ||
| 64 | for i in 0..din.len() { | ||
| 65 | assert_eq!(din[i], swap_nibbles(dout[i])); | ||
| 66 | } | ||
| 67 | info!("Swapped {} words", dout.len()); | ||
| 68 | } | ||
| 69 | } | ||
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..a8a682274 --- /dev/null +++ b/examples/rp/src/bin/uart_buffered_split.rs | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_executor::_export::StaticCell; | ||
| 8 | use embassy_rp::interrupt; | ||
| 9 | use embassy_rp::peripherals::UART0; | ||
| 10 | use embassy_rp::uart::{BufferedUart, BufferedUartRx, Config}; | ||
| 11 | use embassy_time::{Duration, Timer}; | ||
| 12 | use embedded_io::asynch::{Read, Write}; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | macro_rules! singleton { | ||
| 16 | ($val:expr) => {{ | ||
| 17 | type T = impl Sized; | ||
| 18 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | ||
| 19 | let (x,) = STATIC_CELL.init(($val,)); | ||
| 20 | x | ||
| 21 | }}; | ||
| 22 | } | ||
| 23 | |||
| 24 | #[embassy_executor::main] | ||
| 25 | async fn main(spawner: Spawner) { | ||
| 26 | let p = embassy_rp::init(Default::default()); | ||
| 27 | let (tx_pin, rx_pin, uart) = (p.PIN_0, p.PIN_1, p.UART0); | ||
| 28 | |||
| 29 | let irq = interrupt::take!(UART0_IRQ); | ||
| 30 | let tx_buf = &mut singleton!([0u8; 16])[..]; | ||
| 31 | let rx_buf = &mut singleton!([0u8; 16])[..]; | ||
| 32 | let uart = BufferedUart::new(uart, irq, 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..f56e7009f --- /dev/null +++ b/examples/rp/src/bin/uart_unidir.rs | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | //! test TX-only and RX-only UARTs. You need to connect GPIO0 to GPIO5 for | ||
| 2 | //! this to work | ||
| 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::peripherals::UART1; | ||
| 11 | use embassy_rp::uart::{Async, Config, UartRx, UartTx}; | ||
| 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 uart_tx = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Config::default()); | ||
| 20 | let uart_rx = UartRx::new(p.UART1, p.PIN_5, p.DMA_CH1, Config::default()); | ||
| 21 | |||
| 22 | unwrap!(spawner.spawn(reader(uart_rx))); | ||
| 23 | |||
| 24 | info!("Writing..."); | ||
| 25 | loop { | ||
| 26 | let data = [1u8, 2, 3, 4, 5, 6, 7, 8]; | ||
| 27 | info!("TX {:?}", data); | ||
| 28 | uart_tx.write(&data).await.unwrap(); | ||
| 29 | Timer::after(Duration::from_secs(1)).await; | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | #[embassy_executor::task] | ||
| 34 | async fn reader(mut rx: UartRx<'static, UART1, Async>) { | ||
| 35 | info!("Reading..."); | ||
| 36 | loop { | ||
| 37 | // read a total of 4 transmissions (32 / 8) and then print the result | ||
| 38 | let mut buf = [0; 32]; | ||
| 39 | rx.read(&mut buf).await.unwrap(); | ||
| 40 | info!("RX {:?}", buf); | ||
| 41 | } | ||
| 42 | } | ||
diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 1057fe7fd..66a6ed4d0 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs | |||
| @@ -2,18 +2,14 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | 4 | ||
| 5 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 6 | use core::task::Waker; | ||
| 7 | |||
| 8 | use defmt::*; | 5 | use defmt::*; |
| 9 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 10 | use embassy_net::tcp::TcpSocket; | 7 | use embassy_net::tcp::TcpSocket; |
| 11 | use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; | 8 | use embassy_net::{Stack, StackResources}; |
| 12 | use embassy_rp::usb::Driver; | 9 | use embassy_rp::usb::Driver; |
| 13 | use embassy_rp::{interrupt, peripherals}; | 10 | use embassy_rp::{interrupt, peripherals}; |
| 14 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | 11 | use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; |
| 15 | use embassy_sync::channel::Channel; | 12 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; |
| 16 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State}; | ||
| 17 | use embassy_usb::{Builder, Config, UsbDevice}; | 13 | use embassy_usb::{Builder, Config, UsbDevice}; |
| 18 | use embedded_io::asynch::Write; | 14 | use embedded_io::asynch::Write; |
| 19 | use static_cell::StaticCell; | 15 | use static_cell::StaticCell; |
| @@ -25,56 +21,25 @@ macro_rules! singleton { | |||
| 25 | ($val:expr) => {{ | 21 | ($val:expr) => {{ |
| 26 | type T = impl Sized; | 22 | type T = impl Sized; |
| 27 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | 23 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); |
| 28 | STATIC_CELL.init_with(move || $val) | 24 | let (x,) = STATIC_CELL.init(($val,)); |
| 25 | x | ||
| 29 | }}; | 26 | }}; |
| 30 | } | 27 | } |
| 31 | 28 | ||
| 29 | const MTU: usize = 1514; | ||
| 30 | |||
| 32 | #[embassy_executor::task] | 31 | #[embassy_executor::task] |
| 33 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { | 32 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { |
| 34 | device.run().await | 33 | device.run().await |
| 35 | } | 34 | } |
| 36 | 35 | ||
| 37 | #[embassy_executor::task] | 36 | #[embassy_executor::task] |
| 38 | async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { | 37 | async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { |
| 39 | loop { | 38 | class.run().await |
| 40 | warn!("WAITING for connection"); | ||
| 41 | LINK_UP.store(false, Ordering::Relaxed); | ||
| 42 | |||
| 43 | class.wait_connection().await.unwrap(); | ||
| 44 | |||
| 45 | warn!("Connected"); | ||
| 46 | LINK_UP.store(true, Ordering::Relaxed); | ||
| 47 | |||
| 48 | loop { | ||
| 49 | let mut p = unwrap!(PacketBox::new(embassy_net::Packet::new())); | ||
| 50 | let n = match class.read_packet(&mut p[..]).await { | ||
| 51 | Ok(n) => n, | ||
| 52 | Err(e) => { | ||
| 53 | warn!("error reading packet: {:?}", e); | ||
| 54 | break; | ||
| 55 | } | ||
| 56 | }; | ||
| 57 | |||
| 58 | let buf = p.slice(0..n); | ||
| 59 | if RX_CHANNEL.try_send(buf).is_err() { | ||
| 60 | warn!("Failed pushing rx'd packet to channel."); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | } | ||
| 64 | } | 39 | } |
| 65 | 40 | ||
| 66 | #[embassy_executor::task] | 41 | #[embassy_executor::task] |
| 67 | async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { | 42 | async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! { |
| 68 | loop { | ||
| 69 | let pkt = TX_CHANNEL.recv().await; | ||
| 70 | if let Err(e) = class.write_packet(&pkt[..]).await { | ||
| 71 | warn!("Failed to TX packet: {:?}", e); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | #[embassy_executor::task] | ||
| 77 | async fn net_task(stack: &'static Stack<Device>) -> ! { | ||
| 78 | stack.run().await | 43 | stack.run().await |
| 79 | } | 44 | } |
| 80 | 45 | ||
| @@ -100,58 +65,34 @@ async fn main(spawner: Spawner) { | |||
| 100 | config.device_sub_class = 0x02; | 65 | config.device_sub_class = 0x02; |
| 101 | config.device_protocol = 0x01; | 66 | config.device_protocol = 0x01; |
| 102 | 67 | ||
| 103 | struct Resources { | ||
| 104 | device_descriptor: [u8; 256], | ||
| 105 | config_descriptor: [u8; 256], | ||
| 106 | bos_descriptor: [u8; 256], | ||
| 107 | control_buf: [u8; 128], | ||
| 108 | serial_state: State<'static>, | ||
| 109 | } | ||
| 110 | let res: &mut Resources = singleton!(Resources { | ||
| 111 | device_descriptor: [0; 256], | ||
| 112 | config_descriptor: [0; 256], | ||
| 113 | bos_descriptor: [0; 256], | ||
| 114 | control_buf: [0; 128], | ||
| 115 | serial_state: State::new(), | ||
| 116 | }); | ||
| 117 | |||
| 118 | // Create embassy-usb DeviceBuilder using the driver and config. | 68 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 119 | let mut builder = Builder::new( | 69 | let mut builder = Builder::new( |
| 120 | driver, | 70 | driver, |
| 121 | config, | 71 | config, |
| 122 | &mut res.device_descriptor, | 72 | &mut singleton!([0; 256])[..], |
| 123 | &mut res.config_descriptor, | 73 | &mut singleton!([0; 256])[..], |
| 124 | &mut res.bos_descriptor, | 74 | &mut singleton!([0; 256])[..], |
| 125 | &mut res.control_buf, | 75 | &mut singleton!([0; 128])[..], |
| 126 | None, | ||
| 127 | ); | 76 | ); |
| 128 | 77 | ||
| 129 | // WARNINGS for Android ethernet tethering: | ||
| 130 | // - On Pixel 4a, it refused to work on Android 11, worked on Android 12. | ||
| 131 | // - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), | ||
| 132 | // it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. | ||
| 133 | // 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 | ||
| 134 | // and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 | ||
| 135 | |||
| 136 | // Our MAC addr. | 78 | // Our MAC addr. |
| 137 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; | 79 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; |
| 138 | // 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. |
| 139 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; | 81 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; |
| 140 | 82 | ||
| 141 | // Create classes on the builder. | 83 | // Create classes on the builder. |
| 142 | let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64); | 84 | let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); |
| 143 | 85 | ||
| 144 | // Build the builder. | 86 | // Build the builder. |
| 145 | let usb = builder.build(); | 87 | let usb = builder.build(); |
| 146 | 88 | ||
| 147 | unwrap!(spawner.spawn(usb_task(usb))); | 89 | unwrap!(spawner.spawn(usb_task(usb))); |
| 148 | 90 | ||
| 149 | let (tx, rx) = class.split(); | 91 | let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(singleton!(NetState::new()), our_mac_addr); |
| 150 | unwrap!(spawner.spawn(usb_ncm_rx_task(rx))); | 92 | unwrap!(spawner.spawn(usb_ncm_task(runner))); |
| 151 | unwrap!(spawner.spawn(usb_ncm_tx_task(tx))); | ||
| 152 | 93 | ||
| 153 | let config = embassy_net::ConfigStrategy::Dhcp; | 94 | let config = embassy_net::Config::Dhcp(Default::default()); |
| 154 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { | 95 | //let config = embassy_net::Config::Static(embassy_net::StaticConfig { |
| 155 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | 96 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), |
| 156 | // dns_servers: Vec::new(), | 97 | // dns_servers: Vec::new(), |
| 157 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | 98 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), |
| @@ -161,13 +102,7 @@ async fn main(spawner: Spawner) { | |||
| 161 | let seed = 1234; // guaranteed random, chosen by a fair dice roll | 102 | let seed = 1234; // guaranteed random, chosen by a fair dice roll |
| 162 | 103 | ||
| 163 | // Init network stack | 104 | // Init network stack |
| 164 | let device = Device { mac_addr: our_mac_addr }; | 105 | let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); |
| 165 | let stack = &*singleton!(Stack::new( | ||
| 166 | device, | ||
| 167 | config, | ||
| 168 | singleton!(StackResources::<1, 2, 8>::new()), | ||
| 169 | seed | ||
| 170 | )); | ||
| 171 | 106 | ||
| 172 | unwrap!(spawner.spawn(net_task(stack))); | 107 | unwrap!(spawner.spawn(net_task(stack))); |
| 173 | 108 | ||
| @@ -214,50 +149,3 @@ async fn main(spawner: Spawner) { | |||
| 214 | } | 149 | } |
| 215 | } | 150 | } |
| 216 | } | 151 | } |
| 217 | |||
| 218 | static TX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new(); | ||
| 219 | static RX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new(); | ||
| 220 | static LINK_UP: AtomicBool = AtomicBool::new(false); | ||
| 221 | |||
| 222 | struct Device { | ||
| 223 | mac_addr: [u8; 6], | ||
| 224 | } | ||
| 225 | |||
| 226 | impl embassy_net::Device for Device { | ||
| 227 | fn register_waker(&mut self, waker: &Waker) { | ||
| 228 | // loopy loopy wakey wakey | ||
| 229 | waker.wake_by_ref() | ||
| 230 | } | ||
| 231 | |||
| 232 | fn link_state(&mut self) -> embassy_net::LinkState { | ||
| 233 | match LINK_UP.load(Ordering::Relaxed) { | ||
| 234 | true => embassy_net::LinkState::Up, | ||
| 235 | false => embassy_net::LinkState::Down, | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | fn capabilities(&self) -> embassy_net::DeviceCapabilities { | ||
| 240 | let mut caps = embassy_net::DeviceCapabilities::default(); | ||
| 241 | caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header | ||
| 242 | caps.medium = embassy_net::Medium::Ethernet; | ||
| 243 | caps | ||
| 244 | } | ||
| 245 | |||
| 246 | fn is_transmit_ready(&mut self) -> bool { | ||
| 247 | true | ||
| 248 | } | ||
| 249 | |||
| 250 | fn transmit(&mut self, pkt: PacketBuf) { | ||
| 251 | if TX_CHANNEL.try_send(pkt).is_err() { | ||
| 252 | warn!("TX failed") | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | fn receive<'a>(&mut self) -> Option<PacketBuf> { | ||
| 257 | RX_CHANNEL.try_recv().ok() | ||
| 258 | } | ||
| 259 | |||
| 260 | fn ethernet_address(&self) -> [u8; 6] { | ||
| 261 | self.mac_addr | ||
| 262 | } | ||
| 263 | } | ||
diff --git a/examples/rp/src/bin/usb_logger.rs b/examples/rp/src/bin/usb_logger.rs new file mode 100644 index 000000000..52417a02e --- /dev/null +++ b/examples/rp/src/bin/usb_logger.rs | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_rp::interrupt; | ||
| 7 | use embassy_rp::peripherals::USB; | ||
| 8 | use embassy_rp::usb::Driver; | ||
| 9 | use embassy_time::{Duration, Timer}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::task] | ||
| 13 | async fn logger_task(driver: Driver<'static, USB>) { | ||
| 14 | embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver); | ||
| 15 | } | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(spawner: Spawner) { | ||
| 19 | let p = embassy_rp::init(Default::default()); | ||
| 20 | let irq = interrupt::take!(USBCTRL_IRQ); | ||
| 21 | let driver = Driver::new(p.USB, irq); | ||
| 22 | spawner.spawn(logger_task(driver)).unwrap(); | ||
| 23 | |||
| 24 | let mut counter = 0; | ||
| 25 | loop { | ||
| 26 | counter += 1; | ||
| 27 | log::info!("Tick {}", counter); | ||
| 28 | Timer::after(Duration::from_secs(1)).await; | ||
| 29 | } | ||
| 30 | } | ||
diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index b7d6493b4..a991082ee 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs | |||
| @@ -53,7 +53,6 @@ async fn main(_spawner: Spawner) { | |||
| 53 | &mut config_descriptor, | 53 | &mut config_descriptor, |
| 54 | &mut bos_descriptor, | 54 | &mut bos_descriptor, |
| 55 | &mut control_buf, | 55 | &mut control_buf, |
| 56 | None, | ||
| 57 | ); | 56 | ); |
| 58 | 57 | ||
| 59 | // Create classes on the builder. | 58 | // Create classes on the builder. |
diff --git a/examples/rp/src/bin/watchdog.rs b/examples/rp/src/bin/watchdog.rs new file mode 100644 index 000000000..ece5cfe38 --- /dev/null +++ b/examples/rp/src/bin/watchdog.rs | |||
| @@ -0,0 +1,48 @@ | |||
| 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_rp::gpio; | ||
| 8 | use embassy_rp::watchdog::*; | ||
| 9 | use embassy_time::{Duration, Timer}; | ||
| 10 | use gpio::{Level, Output}; | ||
| 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!("Hello world!"); | ||
| 17 | |||
| 18 | let mut watchdog = Watchdog::new(p.WATCHDOG); | ||
| 19 | let mut led = Output::new(p.PIN_25, Level::Low); | ||
| 20 | |||
| 21 | // Set the LED high for 2 seconds so we know when we're about to start the watchdog | ||
| 22 | led.set_high(); | ||
| 23 | Timer::after(Duration::from_secs(2)).await; | ||
| 24 | |||
| 25 | // Set to watchdog to reset if it's not fed within 1.05 seconds, and start it | ||
| 26 | watchdog.start(Duration::from_millis(1_050)); | ||
| 27 | info!("Started the watchdog timer"); | ||
| 28 | |||
| 29 | // Blink once a second for 5 seconds, feed the watchdog timer once a second to avoid a reset | ||
| 30 | for _ in 1..=5 { | ||
| 31 | led.set_low(); | ||
| 32 | Timer::after(Duration::from_millis(500)).await; | ||
| 33 | led.set_high(); | ||
| 34 | Timer::after(Duration::from_millis(500)).await; | ||
| 35 | info!("Feeding watchdog"); | ||
| 36 | watchdog.feed(); | ||
| 37 | } | ||
| 38 | |||
| 39 | info!("Stopped feeding, device will reset in 1.05 seconds"); | ||
| 40 | // Blink 10 times per second, not feeding the watchdog. | ||
| 41 | // The processor should reset in 1.05 seconds. | ||
| 42 | loop { | ||
| 43 | led.set_low(); | ||
| 44 | Timer::after(Duration::from_millis(100)).await; | ||
| 45 | led.set_high(); | ||
| 46 | Timer::after(Duration::from_millis(100)).await; | ||
| 47 | } | ||
| 48 | } | ||
diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index b9bd1e718..8087df09a 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml | |||
| @@ -8,15 +8,16 @@ license = "MIT OR Apache-2.0" | |||
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } |
| 11 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "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"] } |
| 12 | 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"] } | ||
| 13 | critical-section = { version = "1.1", features = ["std"] } | 14 | critical-section = { version = "1.1", features = ["std"] } |
| 14 | 15 | ||
| 15 | async-io = "1.6.0" | 16 | async-io = "1.6.0" |
| 16 | env_logger = "0.9.0" | 17 | env_logger = "0.9.0" |
| 17 | futures = { version = "0.3.17" } | 18 | futures = { version = "0.3.17" } |
| 18 | log = "0.4.14" | 19 | log = "0.4.14" |
| 19 | nix = "0.22.1" | 20 | nix = "0.26.2" |
| 20 | libc = "0.2.101" | 21 | libc = "0.2.101" |
| 21 | clap = { version = "3.0.0-beta.5", features = ["derive"] } | 22 | clap = { version = "3.0.0-beta.5", features = ["derive"] } |
| 22 | rand_core = { version = "0.6.3", features = ["std"] } | 23 | rand_core = { version = "0.6.3", features = ["std"] } |
diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index 9b1450b72..451850d99 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs | |||
| @@ -1,9 +1,11 @@ | |||
| 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}; |
| 7 | use embedded_io::asynch::Write; | 9 | use embedded_io::asynch::Write; |
| 8 | use heapless::Vec; | 10 | use heapless::Vec; |
| 9 | use log::*; | 11 | use log::*; |
| @@ -48,13 +50,13 @@ async fn main_task(spawner: Spawner) { | |||
| 48 | 50 | ||
| 49 | // Choose between dhcp or static ip | 51 | // Choose between dhcp or static ip |
| 50 | let config = if opts.static_ip { | 52 | let config = if opts.static_ip { |
| 51 | ConfigStrategy::Static(embassy_net::Config { | 53 | Config::Static(embassy_net::StaticConfig { |
| 52 | address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), | 54 | address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), |
| 53 | dns_servers: Vec::new(), | 55 | dns_servers: Vec::new(), |
| 54 | gateway: Some(Ipv4Address::new(192, 168, 69, 1)), | 56 | gateway: Some(Ipv4Address::new(192, 168, 69, 1)), |
| 55 | }) | 57 | }) |
| 56 | } else { | 58 | } else { |
| 57 | ConfigStrategy::Dhcp | 59 | Config::Dhcp(Default::default()) |
| 58 | }; | 60 | }; |
| 59 | 61 | ||
| 60 | // Generate random seed | 62 | // Generate random seed |
| @@ -63,12 +65,7 @@ async fn main_task(spawner: Spawner) { | |||
| 63 | let seed = u64::from_le_bytes(seed); | 65 | let seed = u64::from_le_bytes(seed); |
| 64 | 66 | ||
| 65 | // Init network stack | 67 | // Init network stack |
| 66 | let stack = &*singleton!(Stack::new( | 68 | let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); |
| 67 | device, | ||
| 68 | config, | ||
| 69 | singleton!(StackResources::<1, 2, 8>::new()), | ||
| 70 | seed | ||
| 71 | )); | ||
| 72 | 69 | ||
| 73 | // Launch network task | 70 | // Launch network task |
| 74 | spawner.spawn(net_task(stack)).unwrap(); | 71 | spawner.spawn(net_task(stack)).unwrap(); |
diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs new file mode 100644 index 000000000..e1cc45a38 --- /dev/null +++ b/examples/std/src/bin/net_dns.rs | |||
| @@ -0,0 +1,98 @@ | |||
| 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::StaticCell; | ||
| 13 | |||
| 14 | #[path = "../tuntap.rs"] | ||
| 15 | mod tuntap; | ||
| 16 | |||
| 17 | use crate::tuntap::TunTapDevice; | ||
| 18 | |||
| 19 | macro_rules! singleton { | ||
| 20 | ($val:expr) => {{ | ||
| 21 | type T = impl Sized; | ||
| 22 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | ||
| 23 | STATIC_CELL.init_with(move || $val) | ||
| 24 | }}; | ||
| 25 | } | ||
| 26 | |||
| 27 | #[derive(Parser)] | ||
| 28 | #[clap(version = "1.0")] | ||
| 29 | struct Opts { | ||
| 30 | /// TAP device name | ||
| 31 | #[clap(long, default_value = "tap0")] | ||
| 32 | tap: String, | ||
| 33 | /// use a static IP instead of DHCP | ||
| 34 | #[clap(long)] | ||
| 35 | static_ip: bool, | ||
| 36 | } | ||
| 37 | |||
| 38 | #[embassy_executor::task] | ||
| 39 | async fn net_task(stack: &'static Stack<TunTapDevice>) -> ! { | ||
| 40 | stack.run().await | ||
| 41 | } | ||
| 42 | |||
| 43 | #[embassy_executor::task] | ||
| 44 | async fn main_task(spawner: Spawner) { | ||
| 45 | let opts: Opts = Opts::parse(); | ||
| 46 | |||
| 47 | // Init network device | ||
| 48 | let device = TunTapDevice::new(&opts.tap).unwrap(); | ||
| 49 | |||
| 50 | // Choose between dhcp or static ip | ||
| 51 | let config = if opts.static_ip { | ||
| 52 | Config::Static(embassy_net::StaticConfig { | ||
| 53 | address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 1), 24), | ||
| 54 | dns_servers: Vec::from_slice(&[Ipv4Address::new(8, 8, 4, 4).into(), Ipv4Address::new(8, 8, 8, 8).into()]) | ||
| 55 | .unwrap(), | ||
| 56 | gateway: Some(Ipv4Address::new(192, 168, 69, 100)), | ||
| 57 | }) | ||
| 58 | } else { | ||
| 59 | Config::Dhcp(Default::default()) | ||
| 60 | }; | ||
| 61 | |||
| 62 | // Generate random seed | ||
| 63 | let mut seed = [0; 8]; | ||
| 64 | OsRng.fill_bytes(&mut seed); | ||
| 65 | let seed = u64::from_le_bytes(seed); | ||
| 66 | |||
| 67 | // Init network stack | ||
| 68 | let stack: &Stack<_> = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); | ||
| 69 | |||
| 70 | // Launch network task | ||
| 71 | spawner.spawn(net_task(stack)).unwrap(); | ||
| 72 | |||
| 73 | let host = "example.com"; | ||
| 74 | info!("querying host {:?}...", host); | ||
| 75 | match stack.dns_query(host, DnsQueryType::A).await { | ||
| 76 | Ok(r) => { | ||
| 77 | info!("query response: {:?}", r); | ||
| 78 | } | ||
| 79 | Err(e) => { | ||
| 80 | warn!("query error: {:?}", e); | ||
| 81 | } | ||
| 82 | }; | ||
| 83 | } | ||
| 84 | |||
| 85 | static EXECUTOR: StaticCell<Executor> = StaticCell::new(); | ||
| 86 | |||
| 87 | fn main() { | ||
| 88 | env_logger::builder() | ||
| 89 | .filter_level(log::LevelFilter::Debug) | ||
| 90 | .filter_module("async_io", log::LevelFilter::Info) | ||
| 91 | .format_timestamp_nanos() | ||
| 92 | .init(); | ||
| 93 | |||
| 94 | let executor = EXECUTOR.init(Executor::new()); | ||
| 95 | executor.run(|spawner| { | ||
| 96 | spawner.spawn(main_task(spawner)).unwrap(); | ||
| 97 | }); | ||
| 98 | } | ||
diff --git a/examples/std/src/bin/net_udp.rs b/examples/std/src/bin/net_udp.rs index 392a97f0d..f1923f180 100644 --- a/examples/std/src/bin/net_udp.rs +++ b/examples/std/src/bin/net_udp.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 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::UdpSocket; |
| 6 | use embassy_net::{ConfigStrategy, Ipv4Address, Ipv4Cidr, PacketMetadata, Stack, StackResources}; | 6 | use embassy_net::{Config, Ipv4Address, Ipv4Cidr, PacketMetadata, 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}; |
| @@ -47,13 +47,13 @@ async fn main_task(spawner: Spawner) { | |||
| 47 | 47 | ||
| 48 | // Choose between dhcp or static ip | 48 | // Choose between dhcp or static ip |
| 49 | let config = if opts.static_ip { | 49 | let config = if opts.static_ip { |
| 50 | ConfigStrategy::Static(embassy_net::Config { | 50 | Config::Static(embassy_net::StaticConfig { |
| 51 | address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), | 51 | address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), |
| 52 | dns_servers: Vec::new(), | 52 | dns_servers: Vec::new(), |
| 53 | gateway: Some(Ipv4Address::new(192, 168, 69, 1)), | 53 | gateway: Some(Ipv4Address::new(192, 168, 69, 1)), |
| 54 | }) | 54 | }) |
| 55 | } else { | 55 | } else { |
| 56 | ConfigStrategy::Dhcp | 56 | Config::Dhcp(Default::default()) |
| 57 | }; | 57 | }; |
| 58 | 58 | ||
| 59 | // Generate random seed | 59 | // Generate random seed |
| @@ -62,12 +62,7 @@ async fn main_task(spawner: Spawner) { | |||
| 62 | let seed = u64::from_le_bytes(seed); | 62 | let seed = u64::from_le_bytes(seed); |
| 63 | 63 | ||
| 64 | // Init network stack | 64 | // Init network stack |
| 65 | let stack = &*singleton!(Stack::new( | 65 | let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); |
| 66 | device, | ||
| 67 | config, | ||
| 68 | singleton!(StackResources::<1, 2, 8>::new()), | ||
| 69 | seed | ||
| 70 | )); | ||
| 71 | 66 | ||
| 72 | // Launch network task | 67 | // Launch network task |
| 73 | spawner.spawn(net_task(stack)).unwrap(); | 68 | spawner.spawn(net_task(stack)).unwrap(); |
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..eb07f6190 --- /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-run --list-chips` | ||
| 3 | runner = "probe-rs-cli 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..0095a680c --- /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.1.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | ||
| 10 | embassy-time = { version = "0.1.0", 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..16abc29bc 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-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 a56c546ee..d4afbb8f8 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml | |||
| @@ -10,10 +10,10 @@ license = "MIT OR Apache-2.0" | |||
| 10 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 10 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 11 | cortex-m-rt = "0.7.0" | 11 | cortex-m-rt = "0.7.0" |
| 12 | defmt = "0.3" | 12 | defmt = "0.3" |
| 13 | defmt-rtt = "0.3" | 13 | defmt-rtt = "0.4" |
| 14 | panic-probe = "0.3" | 14 | panic-probe = "0.3" |
| 15 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 15 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 16 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 16 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } |
| 17 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 17 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 18 | 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"] } |
| 19 | 19 | static_cell = "1.0" | |
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..e1f223433 --- /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 ouput and obtain handler. | ||
| 21 | // On the Nucleo F091RC theres 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/priority.rs b/examples/stm32f0/src/bin/priority.rs new file mode 100644 index 000000000..7fed6a773 --- /dev/null +++ b/examples/stm32f0/src/bin/priority.rs | |||
| @@ -0,0 +1,139 @@ | |||
| 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_stm32::executor::{Executor, InterruptExecutor}; | ||
| 63 | use embassy_stm32::interrupt; | ||
| 64 | use embassy_stm32::interrupt::InterruptExt; | ||
| 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: StaticCell<InterruptExecutor<interrupt::USART1>> = StaticCell::new(); | ||
| 112 | static EXECUTOR_MED: StaticCell<InterruptExecutor<interrupt::USART2>> = StaticCell::new(); | ||
| 113 | static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new(); | ||
| 114 | |||
| 115 | #[entry] | ||
| 116 | fn main() -> ! { | ||
| 117 | // Initialize and create handle for devicer peripherals | ||
| 118 | let _p = embassy_stm32::init(Default::default()); | ||
| 119 | |||
| 120 | // High-priority executor: USART1, priority level 6 | ||
| 121 | let irq = interrupt::take!(USART1); | ||
| 122 | irq.set_priority(interrupt::Priority::P6); | ||
| 123 | let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq)); | ||
| 124 | let spawner = executor.start(); | ||
| 125 | unwrap!(spawner.spawn(run_high())); | ||
| 126 | |||
| 127 | // Medium-priority executor: USART2, priority level 7 | ||
| 128 | let irq = interrupt::take!(USART2); | ||
| 129 | irq.set_priority(interrupt::Priority::P7); | ||
| 130 | let executor = EXECUTOR_MED.init(InterruptExecutor::new(irq)); | ||
| 131 | let spawner = executor.start(); | ||
| 132 | unwrap!(spawner.spawn(run_med())); | ||
| 133 | |||
| 134 | // Low priority executor: runs in thread mode, using WFE/SEV | ||
| 135 | let executor = EXECUTOR_LOW.init(Executor::new()); | ||
| 136 | executor.run(|spawner| { | ||
| 137 | unwrap!(spawner.spawn(run_low())); | ||
| 138 | }); | ||
| 139 | } | ||
diff --git a/examples/stm32f0/src/bin/wdg.rs b/examples/stm32f0/src/bin/wdg.rs new file mode 100644 index 000000000..80e76f901 --- /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 | unsafe { wdg.unleash() }; | ||
| 20 | |||
| 21 | loop { | ||
| 22 | Timer::after(Duration::from_secs(1)).await; | ||
| 23 | unsafe { wdg.pet() }; | ||
| 24 | } | ||
| 25 | } | ||
diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 6be131f30..53f369b3a 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml | |||
| @@ -13,7 +13,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm | |||
| 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 14 | 14 | ||
| 15 | defmt = "0.3" | 15 | defmt = "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" |
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 ad92cdeb2..07cad84ef 100644 --- a/examples/stm32f1/src/bin/usb_serial.rs +++ b/examples/stm32f1/src/bin/usb_serial.rs | |||
| @@ -58,7 +58,6 @@ async fn main(_spawner: Spawner) { | |||
| 58 | &mut config_descriptor, | 58 | &mut config_descriptor, |
| 59 | &mut bos_descriptor, | 59 | &mut bos_descriptor, |
| 60 | &mut control_buf, | 60 | &mut control_buf, |
| 61 | None, | ||
| 62 | ); | 61 | ); |
| 63 | 62 | ||
| 64 | // Create classes on the builder. | 63 | // Create classes on the builder. |
diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index f6adda2a3..afaf9a0c9 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml | |||
| @@ -11,7 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de | |||
| 11 | 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"] } |
| 12 | 12 | ||
| 13 | defmt = "0.3" | 13 | defmt = "0.3" |
| 14 | defmt-rtt = "0.3" | 14 | defmt-rtt = "0.4" |
| 15 | 15 | ||
| 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 17 | cortex-m-rt = "0.7.0" | 17 | cortex-m-rt = "0.7.0" |
diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 27188dd19..69ebef786 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml | |||
| @@ -13,7 +13,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm | |||
| 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 14 | 14 | ||
| 15 | defmt = "0.3" | 15 | defmt = "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" |
diff --git a/examples/stm32f3/src/bin/usart_dma.rs b/examples/stm32f3/src/bin/usart_dma.rs index 3bc5a287f..47121acf1 100644 --- a/examples/stm32f3/src/bin/usart_dma.rs +++ b/examples/stm32f3/src/bin/usart_dma.rs | |||
| @@ -7,6 +7,7 @@ use core::fmt::Write; | |||
| 7 | use defmt::*; | 7 | 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::interrupt; | ||
| 10 | use embassy_stm32::usart::{Config, Uart}; | 11 | use embassy_stm32::usart::{Config, Uart}; |
| 11 | use heapless::String; | 12 | use heapless::String; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -17,7 +18,8 @@ async fn main(_spawner: Spawner) { | |||
| 17 | info!("Hello World!"); | 18 | info!("Hello World!"); |
| 18 | 19 | ||
| 19 | let config = Config::default(); | 20 | let config = Config::default(); |
| 20 | let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, p.DMA1_CH4, NoDma, config); | 21 | let irq = interrupt::take!(USART1); |
| 22 | let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, irq, p.DMA1_CH4, NoDma, config); | ||
| 21 | 23 | ||
| 22 | for n in 0u32.. { | 24 | for n in 0u32.. { |
| 23 | let mut s: String<128> = String::new(); | 25 | 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 f6d27c860..5b4e0a91a 100644 --- a/examples/stm32f3/src/bin/usb_serial.rs +++ b/examples/stm32f3/src/bin/usb_serial.rs | |||
| @@ -55,7 +55,6 @@ async fn main(_spawner: Spawner) { | |||
| 55 | &mut config_descriptor, | 55 | &mut config_descriptor, |
| 56 | &mut bos_descriptor, | 56 | &mut bos_descriptor, |
| 57 | &mut control_buf, | 57 | &mut control_buf, |
| 58 | None, | ||
| 59 | ); | 58 | ); |
| 60 | 59 | ||
| 61 | // Create classes on the builder. | 60 | // Create classes on the builder. |
diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 6d4f09fba..e2b17bfcb 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml | |||
| @@ -4,20 +4,21 @@ name = "embassy-stm32f4-examples" | |||
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | |||
| 8 | [dependencies] | 7 | [dependencies] |
| 9 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.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.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } |
| 11 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } |
| 12 | 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"] } |
| 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"], optional = true } | ||
| 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 = ["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 | embedded-io = "0.3.0" | 21 | embedded-io = "0.4.0" |
| 21 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 22 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 22 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 23 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } |
| 23 | heapless = { version = "0.7.5", default-features = false } | 24 | heapless = { version = "0.7.5", default-features = false } |
| @@ -26,5 +27,9 @@ embedded-storage = "0.3.0" | |||
| 26 | micromath = "2.0.0" | 27 | micromath = "2.0.0" |
| 27 | static_cell = "1.0" | 28 | static_cell = "1.0" |
| 28 | 29 | ||
| 29 | usb-device = "0.2" | 30 | [[bin]] |
| 30 | usbd-serial = "0.1.1" | 31 | name = "usb_ethernet" |
| 32 | required-features = ["embassy-net"] | ||
| 33 | |||
| 34 | [profile.release] | ||
| 35 | debug = 2 | ||
diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 1d030f7dc..1c9a0b35d 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs | |||
| @@ -24,19 +24,44 @@ async fn main(_spawner: Spawner) { | |||
| 24 | // Startup delay can be combined to the maximum of either | 24 | // Startup delay can be combined to the maximum of either |
| 25 | delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us())); | 25 | delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us())); |
| 26 | 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 | |||
| 27 | loop { | 52 | loop { |
| 28 | // Read pin | 53 | // Read pin |
| 29 | let v = adc.read(&mut pin); | 54 | let v = adc.read(&mut pin); |
| 30 | info!("PC1: {} ({} mV)", v, adc.to_millivolts(v)); | 55 | info!("PC1: {} ({} mV)", v, convert_to_millivolts(v)); |
| 31 | 56 | ||
| 32 | // Read internal temperature | 57 | // Read internal temperature |
| 33 | let v = adc.read_internal(&mut temp); | 58 | let v = adc.read_internal(&mut temp); |
| 34 | let celcius = Temperature::to_celcius(adc.to_millivolts(v)); | 59 | let celcius = convert_to_celcius(v); |
| 35 | info!("Internal temp: {} ({} C)", v, celcius); | 60 | info!("Internal temp: {} ({} C)", v, celcius); |
| 36 | 61 | ||
| 37 | // Read internal voltage reference | 62 | // Read internal voltage reference |
| 38 | let v = adc.read_internal(&mut vrefint); | 63 | let v = adc.read_internal(&mut vrefint); |
| 39 | info!("VrefInt: {} ({} mV)", v, adc.to_millivolts(v)); | 64 | info!("VrefInt: {}", v); |
| 40 | 65 | ||
| 41 | Timer::after(Duration::from_millis(100)).await; | 66 | Timer::after(Duration::from_millis(100)).await; |
| 42 | } | 67 | } |
diff --git a/examples/stm32f4/src/bin/i2c.rs b/examples/stm32f4/src/bin/i2c.rs new file mode 100644 index 000000000..6e51c211d --- /dev/null +++ b/examples/stm32f4/src/bin/i2c.rs | |||
| @@ -0,0 +1,45 @@ | |||
| 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::interrupt; | ||
| 10 | use embassy_stm32::time::Hertz; | ||
| 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 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) -> ! { | ||
| 19 | info!("Hello world!"); | ||
| 20 | let p = embassy_stm32::init(Default::default()); | ||
| 21 | |||
| 22 | let irq = interrupt::take!(I2C2_EV); | ||
| 23 | let mut i2c = I2c::new( | ||
| 24 | p.I2C2, | ||
| 25 | p.PB10, | ||
| 26 | p.PB11, | ||
| 27 | irq, | ||
| 28 | NoDma, | ||
| 29 | NoDma, | ||
| 30 | Hertz(100_000), | ||
| 31 | Default::default(), | ||
| 32 | ); | ||
| 33 | |||
| 34 | // I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long. | ||
| 35 | // TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay. | ||
| 36 | let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); | ||
| 37 | |||
| 38 | let mut data = [0u8; 1]; | ||
| 39 | |||
| 40 | match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { | ||
| 41 | Ok(()) => info!("Whoami: {}", data[0]), | ||
| 42 | Err(Error::Timeout) => error!("Operation timed out"), | ||
| 43 | Err(e) => error!("I2c Error: {:?}", e), | ||
| 44 | } | ||
| 45 | } | ||
diff --git a/examples/stm32f4/src/bin/usart.rs b/examples/stm32f4/src/bin/usart.rs index 90ad882b8..8f41bb6c4 100644 --- a/examples/stm32f4/src/bin/usart.rs +++ b/examples/stm32f4/src/bin/usart.rs | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | use cortex_m_rt::entry; | 5 | 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::interrupt; | ||
| 8 | use embassy_stm32::usart::{Config, Uart}; | 9 | use embassy_stm32::usart::{Config, Uart}; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 11 | ||
| @@ -15,7 +16,8 @@ fn main() -> ! { | |||
| 15 | let p = embassy_stm32::init(Default::default()); | 16 | let p = embassy_stm32::init(Default::default()); |
| 16 | 17 | ||
| 17 | let config = Config::default(); | 18 | let config = Config::default(); |
| 18 | let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, NoDma, NoDma, config); | 19 | let irq = interrupt::take!(USART3); |
| 20 | let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, irq, NoDma, NoDma, config); | ||
| 19 | 21 | ||
| 20 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); | 22 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); |
| 21 | info!("wrote Hello, starting echo"); | 23 | 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..dd171fe13 100644 --- a/examples/stm32f4/src/bin/usart_buffered.rs +++ b/examples/stm32f4/src/bin/usart_buffered.rs | |||
| @@ -4,9 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::dma::NoDma; | ||
| 8 | use embassy_stm32::interrupt; | 7 | use embassy_stm32::interrupt; |
| 9 | use embassy_stm32::usart::{BufferedUart, Config, State, Uart}; | 8 | use embassy_stm32::usart::{BufferedUart, Config, State}; |
| 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 | ||
| @@ -16,13 +15,21 @@ async fn main(_spawner: Spawner) { | |||
| 16 | info!("Hello World!"); | 15 | info!("Hello World!"); |
| 17 | 16 | ||
| 18 | let config = Config::default(); | 17 | let config = Config::default(); |
| 19 | let usart = Uart::new(p.USART3, p.PD9, p.PD8, NoDma, NoDma, config); | ||
| 20 | 18 | ||
| 21 | let mut state = State::new(); | 19 | let mut state = State::new(); |
| 22 | let irq = interrupt::take!(USART3); | 20 | let irq = interrupt::take!(USART3); |
| 23 | let mut tx_buf = [0u8; 32]; | 21 | let mut tx_buf = [0u8; 32]; |
| 24 | let mut rx_buf = [0u8; 32]; | 22 | let mut rx_buf = [0u8; 32]; |
| 25 | let mut buf_usart = BufferedUart::new(&mut state, usart, irq, &mut tx_buf, &mut rx_buf); | 23 | let mut buf_usart = BufferedUart::new( |
| 24 | &mut state, | ||
| 25 | p.USART3, | ||
| 26 | p.PD9, | ||
| 27 | p.PD8, | ||
| 28 | irq, | ||
| 29 | &mut tx_buf, | ||
| 30 | &mut rx_buf, | ||
| 31 | config, | ||
| 32 | ); | ||
| 26 | 33 | ||
| 27 | loop { | 34 | loop { |
| 28 | let buf = buf_usart.fill_buf().await.unwrap(); | 35 | 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..78baeaa0d 100644 --- a/examples/stm32f4/src/bin/usart_dma.rs +++ b/examples/stm32f4/src/bin/usart_dma.rs | |||
| @@ -7,6 +7,7 @@ use core::fmt::Write; | |||
| 7 | use defmt::*; | 7 | 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::interrupt; | ||
| 10 | use embassy_stm32::usart::{Config, Uart}; | 11 | use embassy_stm32::usart::{Config, Uart}; |
| 11 | use heapless::String; | 12 | use heapless::String; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -17,7 +18,8 @@ async fn main(_spawner: Spawner) { | |||
| 17 | info!("Hello World!"); | 18 | info!("Hello World!"); |
| 18 | 19 | ||
| 19 | let config = Config::default(); | 20 | let config = Config::default(); |
| 20 | let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, p.DMA1_CH3, NoDma, config); | 21 | let irq = interrupt::take!(USART3); |
| 22 | let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, irq, p.DMA1_CH3, NoDma, config); | ||
| 21 | 23 | ||
| 22 | for n in 0u32.. { | 24 | for n in 0u32.. { |
| 23 | let mut s: String<128> = String::new(); | 25 | 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..4a16aac07 --- /dev/null +++ b/examples/stm32f4/src/bin/usb_ethernet.rs | |||
| @@ -0,0 +1,168 @@ | |||
| 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::{interrupt, 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::StaticCell; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | type UsbDriver = Driver<'static, embassy_stm32::peripherals::USB_OTG_FS>; | ||
| 21 | |||
| 22 | macro_rules! singleton { | ||
| 23 | ($val:expr) => {{ | ||
| 24 | type T = impl Sized; | ||
| 25 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | ||
| 26 | let (x,) = STATIC_CELL.init(($val,)); | ||
| 27 | x | ||
| 28 | }}; | ||
| 29 | } | ||
| 30 | |||
| 31 | const MTU: usize = 1514; | ||
| 32 | |||
| 33 | #[embassy_executor::task] | ||
| 34 | async fn usb_task(mut device: UsbDevice<'static, UsbDriver>) -> ! { | ||
| 35 | device.run().await | ||
| 36 | } | ||
| 37 | |||
| 38 | #[embassy_executor::task] | ||
| 39 | async fn usb_ncm_task(class: Runner<'static, UsbDriver, MTU>) -> ! { | ||
| 40 | class.run().await | ||
| 41 | } | ||
| 42 | |||
| 43 | #[embassy_executor::task] | ||
| 44 | async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! { | ||
| 45 | stack.run().await | ||
| 46 | } | ||
| 47 | |||
| 48 | #[embassy_executor::main] | ||
| 49 | async fn main(spawner: Spawner) { | ||
| 50 | info!("Hello World!"); | ||
| 51 | |||
| 52 | let mut config = Config::default(); | ||
| 53 | config.rcc.pll48 = true; | ||
| 54 | config.rcc.sys_ck = Some(mhz(48)); | ||
| 55 | |||
| 56 | let p = embassy_stm32::init(config); | ||
| 57 | |||
| 58 | // Create the driver, from the HAL. | ||
| 59 | let irq = interrupt::take!(OTG_FS); | ||
| 60 | let ep_out_buffer = &mut singleton!([0; 256])[..]; | ||
| 61 | let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, ep_out_buffer); | ||
| 62 | |||
| 63 | // Create embassy-usb Config | ||
| 64 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 65 | config.manufacturer = Some("Embassy"); | ||
| 66 | config.product = Some("USB-Ethernet example"); | ||
| 67 | config.serial_number = Some("12345678"); | ||
| 68 | config.max_power = 100; | ||
| 69 | config.max_packet_size_0 = 64; | ||
| 70 | |||
| 71 | // Required for Windows support. | ||
| 72 | config.composite_with_iads = true; | ||
| 73 | config.device_class = 0xEF; | ||
| 74 | config.device_sub_class = 0x02; | ||
| 75 | config.device_protocol = 0x01; | ||
| 76 | |||
| 77 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 78 | let mut builder = Builder::new( | ||
| 79 | driver, | ||
| 80 | config, | ||
| 81 | &mut singleton!([0; 256])[..], | ||
| 82 | &mut singleton!([0; 256])[..], | ||
| 83 | &mut singleton!([0; 256])[..], | ||
| 84 | &mut singleton!([0; 128])[..], | ||
| 85 | ); | ||
| 86 | |||
| 87 | // Our MAC addr. | ||
| 88 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; | ||
| 89 | // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. | ||
| 90 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; | ||
| 91 | |||
| 92 | // Create classes on the builder. | ||
| 93 | let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); | ||
| 94 | |||
| 95 | // Build the builder. | ||
| 96 | let usb = builder.build(); | ||
| 97 | |||
| 98 | unwrap!(spawner.spawn(usb_task(usb))); | ||
| 99 | |||
| 100 | let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(singleton!(NetState::new()), our_mac_addr); | ||
| 101 | unwrap!(spawner.spawn(usb_ncm_task(runner))); | ||
| 102 | |||
| 103 | let config = embassy_net::ConfigStrategy::Dhcp; | ||
| 104 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { | ||
| 105 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | ||
| 106 | // dns_servers: Vec::new(), | ||
| 107 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | ||
| 108 | //}); | ||
| 109 | |||
| 110 | // Generate random seed | ||
| 111 | let mut rng = Rng::new(p.RNG); | ||
| 112 | let mut seed = [0; 8]; | ||
| 113 | unwrap!(rng.async_fill_bytes(&mut seed).await); | ||
| 114 | let seed = u64::from_le_bytes(seed); | ||
| 115 | |||
| 116 | // Init network stack | ||
| 117 | let stack = &*singleton!(Stack::new( | ||
| 118 | device, | ||
| 119 | config, | ||
| 120 | singleton!(StackResources::<1, 2, 8>::new()), | ||
| 121 | seed | ||
| 122 | )); | ||
| 123 | |||
| 124 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 125 | |||
| 126 | // And now we can use it! | ||
| 127 | |||
| 128 | let mut rx_buffer = [0; 4096]; | ||
| 129 | let mut tx_buffer = [0; 4096]; | ||
| 130 | let mut buf = [0; 4096]; | ||
| 131 | |||
| 132 | loop { | ||
| 133 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 134 | socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); | ||
| 135 | |||
| 136 | info!("Listening on TCP:1234..."); | ||
| 137 | if let Err(e) = socket.accept(1234).await { | ||
| 138 | warn!("accept error: {:?}", e); | ||
| 139 | continue; | ||
| 140 | } | ||
| 141 | |||
| 142 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 143 | |||
| 144 | loop { | ||
| 145 | let n = match socket.read(&mut buf).await { | ||
| 146 | Ok(0) => { | ||
| 147 | warn!("read EOF"); | ||
| 148 | break; | ||
| 149 | } | ||
| 150 | Ok(n) => n, | ||
| 151 | Err(e) => { | ||
| 152 | warn!("read error: {:?}", e); | ||
| 153 | break; | ||
| 154 | } | ||
| 155 | }; | ||
| 156 | |||
| 157 | info!("rxd {:02x}", &buf[..n]); | ||
| 158 | |||
| 159 | match socket.write_all(&buf[..n]).await { | ||
| 160 | Ok(()) => {} | ||
| 161 | Err(e) => { | ||
| 162 | warn!("write error: {:?}", e); | ||
| 163 | break; | ||
| 164 | } | ||
| 165 | }; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | } | ||
diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs new file mode 100644 index 000000000..baabc1a2d --- /dev/null +++ b/examples/stm32f4/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,105 @@ | |||
| 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::{interrupt, 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 | #[embassy_executor::main] | ||
| 17 | async fn main(_spawner: Spawner) { | ||
| 18 | info!("Hello World!"); | ||
| 19 | |||
| 20 | let mut config = Config::default(); | ||
| 21 | config.rcc.pll48 = true; | ||
| 22 | config.rcc.sys_ck = Some(mhz(48)); | ||
| 23 | |||
| 24 | let p = embassy_stm32::init(config); | ||
| 25 | |||
| 26 | // Create the driver, from the HAL. | ||
| 27 | let irq = interrupt::take!(OTG_FS); | ||
| 28 | let mut ep_out_buffer = [0u8; 256]; | ||
| 29 | let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); | ||
| 30 | |||
| 31 | // Create embassy-usb Config | ||
| 32 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 33 | config.manufacturer = Some("Embassy"); | ||
| 34 | config.product = Some("USB-serial example"); | ||
| 35 | config.serial_number = Some("12345678"); | ||
| 36 | |||
| 37 | // Required for windows compatiblity. | ||
| 38 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 39 | config.device_class = 0xEF; | ||
| 40 | config.device_sub_class = 0x02; | ||
| 41 | config.device_protocol = 0x01; | ||
| 42 | config.composite_with_iads = true; | ||
| 43 | |||
| 44 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 45 | // It needs some buffers for building the descriptors. | ||
| 46 | let mut device_descriptor = [0; 256]; | ||
| 47 | let mut config_descriptor = [0; 256]; | ||
| 48 | let mut bos_descriptor = [0; 256]; | ||
| 49 | let mut control_buf = [0; 64]; | ||
| 50 | |||
| 51 | let mut state = State::new(); | ||
| 52 | |||
| 53 | let mut builder = Builder::new( | ||
| 54 | driver, | ||
| 55 | config, | ||
| 56 | &mut device_descriptor, | ||
| 57 | &mut config_descriptor, | ||
| 58 | &mut bos_descriptor, | ||
| 59 | &mut control_buf, | ||
| 60 | ); | ||
| 61 | |||
| 62 | // Create classes on the builder. | ||
| 63 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 64 | |||
| 65 | // Build the builder. | ||
| 66 | let mut usb = builder.build(); | ||
| 67 | |||
| 68 | // Run the USB device. | ||
| 69 | let usb_fut = usb.run(); | ||
| 70 | |||
| 71 | // Do stuff with the class! | ||
| 72 | let echo_fut = async { | ||
| 73 | loop { | ||
| 74 | class.wait_connection().await; | ||
| 75 | info!("Connected"); | ||
| 76 | let _ = echo(&mut class).await; | ||
| 77 | info!("Disconnected"); | ||
| 78 | } | ||
| 79 | }; | ||
| 80 | |||
| 81 | // Run everything concurrently. | ||
| 82 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 83 | join(usb_fut, echo_fut).await; | ||
| 84 | } | ||
| 85 | |||
| 86 | struct Disconnected {} | ||
| 87 | |||
| 88 | impl From<EndpointError> for Disconnected { | ||
| 89 | fn from(val: EndpointError) -> Self { | ||
| 90 | match val { | ||
| 91 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 92 | EndpointError::Disabled => Disconnected {}, | ||
| 93 | } | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 98 | let mut buf = [0; 64]; | ||
| 99 | loop { | ||
| 100 | let n = class.read_packet(&mut buf).await?; | ||
| 101 | let data = &buf[..n]; | ||
| 102 | info!("data: {:x}", data); | ||
| 103 | class.write_packet(data).await?; | ||
| 104 | } | ||
| 105 | } | ||
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index dad92c0fc..ea4cbd808 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml | |||
| @@ -8,12 +8,13 @@ license = "MIT OR Apache-2.0" | |||
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.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.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", 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", "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"] } |
| 12 | embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } | 12 | embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } |
| 13 | 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"] } | ||
| 14 | 15 | ||
| 15 | defmt = "0.3" | 16 | defmt = "0.3" |
| 16 | defmt-rtt = "0.3" | 17 | defmt-rtt = "0.4" |
| 17 | 18 | ||
| 18 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 19 | cortex-m-rt = "0.7.0" | 20 | cortex-m-rt = "0.7.0" |
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/eth.rs b/examples/stm32f7/src/bin/eth.rs index 5202edf62..571a6c1b9 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs | |||
| @@ -7,7 +7,7 @@ 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; |
| @@ -22,11 +22,12 @@ macro_rules! singleton { | |||
| 22 | ($val:expr) => {{ | 22 | ($val:expr) => {{ |
| 23 | type T = impl Sized; | 23 | type T = impl Sized; |
| 24 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | 24 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); |
| 25 | STATIC_CELL.init_with(move || $val) | 25 | let (x,) = STATIC_CELL.init(($val,)); |
| 26 | x | ||
| 26 | }}; | 27 | }}; |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; | 30 | type Device = Ethernet<'static, ETH, GenericSMI>; |
| 30 | 31 | ||
| 31 | #[embassy_executor::task] | 32 | #[embassy_executor::task] |
| 32 | async fn net_task(stack: &'static Stack<Device>) -> ! { | 33 | async fn net_task(stack: &'static Stack<Device>) -> ! { |
| @@ -50,40 +51,33 @@ async fn main(spawner: Spawner) -> ! { | |||
| 50 | let eth_int = interrupt::take!(ETH); | 51 | let eth_int = interrupt::take!(ETH); |
| 51 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; | 52 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; |
| 52 | 53 | ||
| 53 | let device = unsafe { | 54 | let device = Ethernet::new( |
| 54 | Ethernet::new( | 55 | singleton!(PacketQueue::<16, 16>::new()), |
| 55 | singleton!(State::new()), | 56 | p.ETH, |
| 56 | p.ETH, | 57 | eth_int, |
| 57 | eth_int, | 58 | p.PA1, |
| 58 | p.PA1, | 59 | p.PA2, |
| 59 | p.PA2, | 60 | p.PC1, |
| 60 | p.PC1, | 61 | p.PA7, |
| 61 | p.PA7, | 62 | p.PC4, |
| 62 | p.PC4, | 63 | p.PC5, |
| 63 | p.PC5, | 64 | p.PG13, |
| 64 | p.PG13, | 65 | p.PB13, |
| 65 | p.PB13, | 66 | p.PG11, |
| 66 | p.PG11, | 67 | GenericSMI, |
| 67 | GenericSMI, | 68 | mac_addr, |
| 68 | mac_addr, | 69 | 0, |
| 69 | 0, | 70 | ); |
| 70 | ) | 71 | |
| 71 | }; | 72 | let config = embassy_net::Config::Dhcp(Default::default()); |
| 72 | 73 | //let config = embassy_net::Config::Static(embassy_net::StaticConfig { | |
| 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), | 74 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), |
| 76 | // dns_servers: Vec::new(), | 75 | // dns_servers: Vec::new(), |
| 77 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | 76 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), |
| 78 | //}); | 77 | //}); |
| 79 | 78 | ||
| 80 | // Init network stack | 79 | // Init network stack |
| 81 | let stack = &*singleton!(Stack::new( | 80 | let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); |
| 82 | device, | ||
| 83 | config, | ||
| 84 | singleton!(StackResources::<1, 2, 8>::new()), | ||
| 85 | seed | ||
| 86 | )); | ||
| 87 | 81 | ||
| 88 | // Launch network task | 82 | // Launch network task |
| 89 | unwrap!(spawner.spawn(net_task(&stack))); | 83 | unwrap!(spawner.spawn(net_task(&stack))); |
| @@ -91,8 +85,8 @@ async fn main(spawner: Spawner) -> ! { | |||
| 91 | info!("Network task initialized"); | 85 | info!("Network task initialized"); |
| 92 | 86 | ||
| 93 | // Then we can use it! | 87 | // Then we can use it! |
| 94 | let mut rx_buffer = [0; 1024]; | 88 | let mut rx_buffer = [0; 4096]; |
| 95 | let mut tx_buffer = [0; 1024]; | 89 | let mut tx_buffer = [0; 4096]; |
| 96 | 90 | ||
| 97 | loop { | 91 | loop { |
| 98 | let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); | 92 | let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); |
| @@ -107,8 +101,9 @@ async fn main(spawner: Spawner) -> ! { | |||
| 107 | continue; | 101 | continue; |
| 108 | } | 102 | } |
| 109 | info!("connected!"); | 103 | info!("connected!"); |
| 104 | let buf = [0; 1024]; | ||
| 110 | loop { | 105 | loop { |
| 111 | let r = socket.write_all(b"Hello\n").await; | 106 | let r = socket.write_all(&buf).await; |
| 112 | if let Err(e) = r { | 107 | if let Err(e) = r { |
| 113 | info!("write error: {:?}", e); | 108 | info!("write error: {:?}", e); |
| 114 | return; | 109 | return; |
diff --git a/examples/stm32f7/src/bin/usart_dma.rs b/examples/stm32f7/src/bin/usart_dma.rs index 07270479c..4827c52ae 100644 --- a/examples/stm32f7/src/bin/usart_dma.rs +++ b/examples/stm32f7/src/bin/usart_dma.rs | |||
| @@ -7,6 +7,7 @@ use core::fmt::Write; | |||
| 7 | use defmt::*; | 7 | 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::interrupt; | ||
| 10 | use embassy_stm32::usart::{Config, Uart}; | 11 | use embassy_stm32::usart::{Config, Uart}; |
| 11 | use heapless::String; | 12 | use heapless::String; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -15,7 +16,8 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 15 | async fn main(_spawner: Spawner) { | 16 | async fn main(_spawner: Spawner) { |
| 16 | let p = embassy_stm32::init(Default::default()); | 17 | let p = embassy_stm32::init(Default::default()); |
| 17 | let config = Config::default(); | 18 | let config = Config::default(); |
| 18 | let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, p.DMA1_CH1, NoDma, config); | 19 | let irq = interrupt::take!(UART7); |
| 20 | let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, irq, p.DMA1_CH1, NoDma, config); | ||
| 19 | 21 | ||
| 20 | for n in 0u32.. { | 22 | for n in 0u32.. { |
| 21 | let mut s: String<128> = String::new(); | 23 | 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..5fd9d2ec9 --- /dev/null +++ b/examples/stm32f7/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,106 @@ | |||
| 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::{interrupt, 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 | #[embassy_executor::main] | ||
| 17 | async fn main(_spawner: Spawner) { | ||
| 18 | info!("Hello World!"); | ||
| 19 | |||
| 20 | let mut config = Config::default(); | ||
| 21 | config.rcc.hse = Some(mhz(8)); | ||
| 22 | config.rcc.pll48 = true; | ||
| 23 | config.rcc.sys_ck = Some(mhz(200)); | ||
| 24 | |||
| 25 | let p = embassy_stm32::init(config); | ||
| 26 | |||
| 27 | // Create the driver, from the HAL. | ||
| 28 | let irq = interrupt::take!(OTG_FS); | ||
| 29 | let mut ep_out_buffer = [0u8; 256]; | ||
| 30 | let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); | ||
| 31 | |||
| 32 | // Create embassy-usb Config | ||
| 33 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 34 | config.manufacturer = Some("Embassy"); | ||
| 35 | config.product = Some("USB-serial example"); | ||
| 36 | config.serial_number = Some("12345678"); | ||
| 37 | |||
| 38 | // Required for windows compatiblity. | ||
| 39 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 40 | config.device_class = 0xEF; | ||
| 41 | config.device_sub_class = 0x02; | ||
| 42 | config.device_protocol = 0x01; | ||
| 43 | config.composite_with_iads = true; | ||
| 44 | |||
| 45 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 46 | // It needs some buffers for building the descriptors. | ||
| 47 | let mut device_descriptor = [0; 256]; | ||
| 48 | let mut config_descriptor = [0; 256]; | ||
| 49 | let mut bos_descriptor = [0; 256]; | ||
| 50 | let mut control_buf = [0; 64]; | ||
| 51 | |||
| 52 | let mut state = State::new(); | ||
| 53 | |||
| 54 | let mut builder = Builder::new( | ||
| 55 | driver, | ||
| 56 | config, | ||
| 57 | &mut device_descriptor, | ||
| 58 | &mut config_descriptor, | ||
| 59 | &mut bos_descriptor, | ||
| 60 | &mut control_buf, | ||
| 61 | ); | ||
| 62 | |||
| 63 | // Create classes on the builder. | ||
| 64 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 65 | |||
| 66 | // Build the builder. | ||
| 67 | let mut usb = builder.build(); | ||
| 68 | |||
| 69 | // Run the USB device. | ||
| 70 | let usb_fut = usb.run(); | ||
| 71 | |||
| 72 | // Do stuff with the class! | ||
| 73 | let echo_fut = async { | ||
| 74 | loop { | ||
| 75 | class.wait_connection().await; | ||
| 76 | info!("Connected"); | ||
| 77 | let _ = echo(&mut class).await; | ||
| 78 | info!("Disconnected"); | ||
| 79 | } | ||
| 80 | }; | ||
| 81 | |||
| 82 | // Run everything concurrently. | ||
| 83 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 84 | join(usb_fut, echo_fut).await; | ||
| 85 | } | ||
| 86 | |||
| 87 | struct Disconnected {} | ||
| 88 | |||
| 89 | impl From<EndpointError> for Disconnected { | ||
| 90 | fn from(val: EndpointError) -> Self { | ||
| 91 | match val { | ||
| 92 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 93 | EndpointError::Disabled => Disconnected {}, | ||
| 94 | } | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 99 | let mut buf = [0; 64]; | ||
| 100 | loop { | ||
| 101 | let n = class.read_packet(&mut buf).await?; | ||
| 102 | let data = &buf[..n]; | ||
| 103 | info!("data: {:x}", data); | ||
| 104 | class.write_packet(data).await?; | ||
| 105 | } | ||
| 106 | } | ||
diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index f5673718d..e7273c9fc 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml | |||
| @@ -11,7 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de | |||
| 11 | 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"] } |
| 12 | 12 | ||
| 13 | defmt = "0.3" | 13 | defmt = "0.3" |
| 14 | defmt-rtt = "0.3" | 14 | defmt-rtt = "0.4" |
| 15 | 15 | ||
| 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 17 | cortex-m-rt = "0.7.0" | 17 | cortex-m-rt = "0.7.0" |
diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index ecda28805..8a57a8ef0 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml | |||
| @@ -12,7 +12,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" | |||
| 12 | embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } | 12 | embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } |
| 13 | 13 | ||
| 14 | defmt = "0.3" | 14 | defmt = "0.3" |
| 15 | defmt-rtt = "0.3" | 15 | defmt-rtt = "0.4" |
| 16 | 16 | ||
| 17 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 17 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 18 | cortex-m-rt = "0.7.0" | 18 | cortex-m-rt = "0.7.0" |
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 1a05b9ecb..a04134789 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml | |||
| @@ -8,19 +8,20 @@ license = "MIT OR Apache-2.0" | |||
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.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.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", 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", "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"] } |
| 12 | embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "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"] } |
| 13 | 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"] } | ||
| 14 | 15 | ||
| 15 | defmt = "0.3" | 16 | defmt = "0.3" |
| 16 | defmt-rtt = "0.3" | 17 | defmt-rtt = "0.4" |
| 17 | 18 | ||
| 18 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 19 | cortex-m-rt = "0.7.0" | 20 | cortex-m-rt = "0.7.0" |
| 20 | embedded-hal = "0.2.6" | 21 | embedded-hal = "0.2.6" |
| 21 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } | 22 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } |
| 22 | embedded-hal-async = { version = "=0.1.0-alpha.2" } | 23 | embedded-hal-async = { version = "=0.2.0-alpha.0" } |
| 23 | embedded-nal-async = "0.2.0" | 24 | embedded-nal-async = "0.4.0" |
| 24 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 25 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 25 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 26 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } |
| 26 | heapless = { version = "0.7.5", default-features = false } | 27 | heapless = { version = "0.7.5", default-features = false } |
diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 4ccc0b5ef..cb245c325 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs | |||
| @@ -7,7 +7,7 @@ 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; |
| @@ -22,11 +22,12 @@ macro_rules! singleton { | |||
| 22 | ($val:expr) => {{ | 22 | ($val:expr) => {{ |
| 23 | type T = impl Sized; | 23 | type T = impl Sized; |
| 24 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | 24 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); |
| 25 | STATIC_CELL.init_with(move || $val) | 25 | let (x,) = STATIC_CELL.init(($val,)); |
| 26 | x | ||
| 26 | }}; | 27 | }}; |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; | 30 | type Device = Ethernet<'static, ETH, GenericSMI>; |
| 30 | 31 | ||
| 31 | #[embassy_executor::task] | 32 | #[embassy_executor::task] |
| 32 | async fn net_task(stack: &'static Stack<Device>) -> ! { | 33 | async fn net_task(stack: &'static Stack<Device>) -> ! { |
| @@ -51,40 +52,33 @@ async fn main(spawner: Spawner) -> ! { | |||
| 51 | let eth_int = interrupt::take!(ETH); | 52 | let eth_int = interrupt::take!(ETH); |
| 52 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; | 53 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; |
| 53 | 54 | ||
| 54 | let device = unsafe { | 55 | let device = Ethernet::new( |
| 55 | Ethernet::new( | 56 | singleton!(PacketQueue::<16, 16>::new()), |
| 56 | singleton!(State::new()), | 57 | p.ETH, |
| 57 | p.ETH, | 58 | eth_int, |
| 58 | eth_int, | 59 | p.PA1, |
| 59 | p.PA1, | 60 | p.PA2, |
| 60 | p.PA2, | 61 | p.PC1, |
| 61 | p.PC1, | 62 | p.PA7, |
| 62 | p.PA7, | 63 | p.PC4, |
| 63 | p.PC4, | 64 | p.PC5, |
| 64 | p.PC5, | 65 | p.PG13, |
| 65 | p.PG13, | 66 | p.PB13, |
| 66 | p.PB13, | 67 | p.PG11, |
| 67 | p.PG11, | 68 | GenericSMI, |
| 68 | GenericSMI, | 69 | mac_addr, |
| 69 | mac_addr, | 70 | 0, |
| 70 | 0, | 71 | ); |
| 71 | ) | 72 | |
| 72 | }; | 73 | let config = embassy_net::Config::Dhcp(Default::default()); |
| 73 | 74 | //let config = embassy_net::Config::Static(embassy_net::StaticConfig { | |
| 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), | 75 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), |
| 77 | // dns_servers: Vec::new(), | 76 | // dns_servers: Vec::new(), |
| 78 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | 77 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), |
| 79 | //}); | 78 | //}); |
| 80 | 79 | ||
| 81 | // Init network stack | 80 | // Init network stack |
| 82 | let stack = &*singleton!(Stack::new( | 81 | let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); |
| 83 | device, | ||
| 84 | config, | ||
| 85 | singleton!(StackResources::<1, 2, 8>::new()), | ||
| 86 | seed | ||
| 87 | )); | ||
| 88 | 82 | ||
| 89 | // Launch network task | 83 | // Launch network task |
| 90 | unwrap!(spawner.spawn(net_task(&stack))); | 84 | unwrap!(spawner.spawn(net_task(&stack))); |
diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 64fd84141..cce85a083 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs | |||
| @@ -7,7 +7,7 @@ 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; |
| @@ -23,11 +23,12 @@ macro_rules! singleton { | |||
| 23 | ($val:expr) => {{ | 23 | ($val:expr) => {{ |
| 24 | type T = impl Sized; | 24 | type T = impl Sized; |
| 25 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | 25 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); |
| 26 | STATIC_CELL.init_with(move || $val) | 26 | let (x,) = STATIC_CELL.init(($val,)); |
| 27 | x | ||
| 27 | }}; | 28 | }}; |
| 28 | } | 29 | } |
| 29 | 30 | ||
| 30 | type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; | 31 | type Device = Ethernet<'static, ETH, GenericSMI>; |
| 31 | 32 | ||
| 32 | #[embassy_executor::task] | 33 | #[embassy_executor::task] |
| 33 | async fn net_task(stack: &'static Stack<Device>) -> ! { | 34 | async fn net_task(stack: &'static Stack<Device>) -> ! { |
| @@ -52,40 +53,33 @@ async fn main(spawner: Spawner) -> ! { | |||
| 52 | let eth_int = interrupt::take!(ETH); | 53 | let eth_int = interrupt::take!(ETH); |
| 53 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; | 54 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; |
| 54 | 55 | ||
| 55 | let device = unsafe { | 56 | let device = Ethernet::new( |
| 56 | Ethernet::new( | 57 | singleton!(PacketQueue::<16, 16>::new()), |
| 57 | singleton!(State::new()), | 58 | p.ETH, |
| 58 | p.ETH, | 59 | eth_int, |
| 59 | eth_int, | 60 | p.PA1, |
| 60 | p.PA1, | 61 | p.PA2, |
| 61 | p.PA2, | 62 | p.PC1, |
| 62 | p.PC1, | 63 | p.PA7, |
| 63 | p.PA7, | 64 | p.PC4, |
| 64 | p.PC4, | 65 | p.PC5, |
| 65 | p.PC5, | 66 | p.PG13, |
| 66 | p.PG13, | 67 | p.PB13, |
| 67 | p.PB13, | 68 | p.PG11, |
| 68 | p.PG11, | 69 | GenericSMI, |
| 69 | GenericSMI, | 70 | mac_addr, |
| 70 | mac_addr, | 71 | 0, |
| 71 | 0, | 72 | ); |
| 72 | ) | 73 | |
| 73 | }; | 74 | let config = embassy_net::Config::Dhcp(Default::default()); |
| 74 | 75 | //let config = embassy_net::Config::StaticConfig(embassy_net::Config { | |
| 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), | 76 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), |
| 78 | // dns_servers: Vec::new(), | 77 | // dns_servers: Vec::new(), |
| 79 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | 78 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), |
| 80 | //}); | 79 | //}); |
| 81 | 80 | ||
| 82 | // Init network stack | 81 | // Init network stack |
| 83 | let stack = &*singleton!(Stack::new( | 82 | let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); |
| 84 | device, | ||
| 85 | config, | ||
| 86 | singleton!(StackResources::<1, 2, 8>::new()), | ||
| 87 | seed | ||
| 88 | )); | ||
| 89 | 83 | ||
| 90 | // Launch network task | 84 | // Launch network task |
| 91 | unwrap!(spawner.spawn(net_task(&stack))); | 85 | unwrap!(spawner.spawn(net_task(&stack))); |
diff --git a/examples/stm32h7/src/bin/i2c.rs b/examples/stm32h7/src/bin/i2c.rs new file mode 100644 index 000000000..d44319ae6 --- /dev/null +++ b/examples/stm32h7/src/bin/i2c.rs | |||
| @@ -0,0 +1,44 @@ | |||
| 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::interrupt; | ||
| 9 | use embassy_stm32::time::Hertz; | ||
| 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 | #[embassy_executor::main] | ||
| 17 | async fn main(_spawner: Spawner) -> ! { | ||
| 18 | info!("Hello world!"); | ||
| 19 | let p = embassy_stm32::init(Default::default()); | ||
| 20 | |||
| 21 | let irq = interrupt::take!(I2C2_EV); | ||
| 22 | let mut i2c = I2c::new( | ||
| 23 | p.I2C2, | ||
| 24 | p.PB10, | ||
| 25 | p.PB11, | ||
| 26 | irq, | ||
| 27 | p.DMA1_CH4, | ||
| 28 | p.DMA1_CH5, | ||
| 29 | Hertz(100_000), | ||
| 30 | Default::default(), | ||
| 31 | ); | ||
| 32 | |||
| 33 | // I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long. | ||
| 34 | // TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay. | ||
| 35 | let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); | ||
| 36 | |||
| 37 | let mut data = [0u8; 1]; | ||
| 38 | |||
| 39 | match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { | ||
| 40 | Ok(()) => info!("Whoami: {}", data[0]), | ||
| 41 | Err(Error::Timeout) => error!("Operation timed out"), | ||
| 42 | Err(e) => error!("I2c Error: {:?}", e), | ||
| 43 | } | ||
| 44 | } | ||
diff --git a/examples/stm32h7/src/bin/usart.rs b/examples/stm32h7/src/bin/usart.rs index 87c2b1253..405f18ec7 100644 --- a/examples/stm32h7/src/bin/usart.rs +++ b/examples/stm32h7/src/bin/usart.rs | |||
| @@ -6,6 +6,7 @@ use cortex_m_rt::entry; | |||
| 6 | use defmt::*; | 6 | 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::interrupt; | ||
| 9 | use embassy_stm32::usart::{Config, Uart}; | 10 | use embassy_stm32::usart::{Config, Uart}; |
| 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 _}; |
| @@ -15,7 +16,8 @@ async fn main_task() { | |||
| 15 | let p = embassy_stm32::init(Default::default()); | 16 | let p = embassy_stm32::init(Default::default()); |
| 16 | 17 | ||
| 17 | let config = Config::default(); | 18 | let config = Config::default(); |
| 18 | let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, NoDma, NoDma, config); | 19 | let irq = interrupt::take!(UART7); |
| 20 | let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, NoDma, NoDma, config); | ||
| 19 | 21 | ||
| 20 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); | 22 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); |
| 21 | info!("wrote Hello, starting echo"); | 23 | 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..6e3491e55 100644 --- a/examples/stm32h7/src/bin/usart_dma.rs +++ b/examples/stm32h7/src/bin/usart_dma.rs | |||
| @@ -8,6 +8,7 @@ use cortex_m_rt::entry; | |||
| 8 | use defmt::*; | 8 | 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::interrupt; | ||
| 11 | use embassy_stm32::usart::{Config, Uart}; | 12 | use embassy_stm32::usart::{Config, Uart}; |
| 12 | use heapless::String; | 13 | use heapless::String; |
| 13 | use static_cell::StaticCell; | 14 | use static_cell::StaticCell; |
| @@ -18,7 +19,8 @@ async fn main_task() { | |||
| 18 | let p = embassy_stm32::init(Default::default()); | 19 | let p = embassy_stm32::init(Default::default()); |
| 19 | 20 | ||
| 20 | let config = Config::default(); | 21 | let config = Config::default(); |
| 21 | let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, p.DMA1_CH0, NoDma, config); | 22 | let irq = interrupt::take!(UART7); |
| 23 | let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.DMA1_CH0, NoDma, config); | ||
| 22 | 24 | ||
| 23 | for n in 0u32.. { | 25 | for n in 0u32.. { |
| 24 | let mut s: String<128> = String::new(); | 26 | 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..f97176ecb 100644 --- a/examples/stm32h7/src/bin/usart_split.rs +++ b/examples/stm32h7/src/bin/usart_split.rs | |||
| @@ -5,6 +5,7 @@ | |||
| 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::dma::NoDma; |
| 8 | use embassy_stm32::interrupt; | ||
| 8 | use embassy_stm32::peripherals::{DMA1_CH1, UART7}; | 9 | use embassy_stm32::peripherals::{DMA1_CH1, UART7}; |
| 9 | use embassy_stm32::usart::{Config, Uart, UartRx}; | 10 | use embassy_stm32::usart::{Config, Uart, UartRx}; |
| 10 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | 11 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; |
| @@ -31,7 +32,8 @@ async fn main(spawner: Spawner) -> ! { | |||
| 31 | info!("Hello World!"); | 32 | info!("Hello World!"); |
| 32 | 33 | ||
| 33 | let config = Config::default(); | 34 | let config = Config::default(); |
| 34 | let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, p.DMA1_CH0, p.DMA1_CH1, config); | 35 | let irq = interrupt::take!(UART7); |
| 36 | let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.DMA1_CH0, p.DMA1_CH1, config); | ||
| 35 | unwrap!(usart.blocking_write(b"Type 8 chars to echo!\r\n")); | 37 | unwrap!(usart.blocking_write(b"Type 8 chars to echo!\r\n")); |
| 36 | 38 | ||
| 37 | let (mut tx, rx) = usart.split(); | 39 | 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..9ef520ae2 --- /dev/null +++ b/examples/stm32h7/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,105 @@ | |||
| 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::{interrupt, 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 | #[embassy_executor::main] | ||
| 17 | async fn main(_spawner: Spawner) { | ||
| 18 | info!("Hello World!"); | ||
| 19 | |||
| 20 | let mut config = Config::default(); | ||
| 21 | config.rcc.sys_ck = Some(mhz(400)); | ||
| 22 | config.rcc.hclk = Some(mhz(200)); | ||
| 23 | config.rcc.pll1.q_ck = Some(mhz(100)); | ||
| 24 | let p = embassy_stm32::init(config); | ||
| 25 | |||
| 26 | // Create the driver, from the HAL. | ||
| 27 | let irq = interrupt::take!(OTG_FS); | ||
| 28 | let mut ep_out_buffer = [0u8; 256]; | ||
| 29 | let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); | ||
| 30 | |||
| 31 | // Create embassy-usb Config | ||
| 32 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 33 | config.manufacturer = Some("Embassy"); | ||
| 34 | config.product = Some("USB-serial example"); | ||
| 35 | config.serial_number = Some("12345678"); | ||
| 36 | |||
| 37 | // Required for windows compatiblity. | ||
| 38 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 39 | config.device_class = 0xEF; | ||
| 40 | config.device_sub_class = 0x02; | ||
| 41 | config.device_protocol = 0x01; | ||
| 42 | config.composite_with_iads = true; | ||
| 43 | |||
| 44 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 45 | // It needs some buffers for building the descriptors. | ||
| 46 | let mut device_descriptor = [0; 256]; | ||
| 47 | let mut config_descriptor = [0; 256]; | ||
| 48 | let mut bos_descriptor = [0; 256]; | ||
| 49 | let mut control_buf = [0; 64]; | ||
| 50 | |||
| 51 | let mut state = State::new(); | ||
| 52 | |||
| 53 | let mut builder = Builder::new( | ||
| 54 | driver, | ||
| 55 | config, | ||
| 56 | &mut device_descriptor, | ||
| 57 | &mut config_descriptor, | ||
| 58 | &mut bos_descriptor, | ||
| 59 | &mut control_buf, | ||
| 60 | ); | ||
| 61 | |||
| 62 | // Create classes on the builder. | ||
| 63 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 64 | |||
| 65 | // Build the builder. | ||
| 66 | let mut usb = builder.build(); | ||
| 67 | |||
| 68 | // Run the USB device. | ||
| 69 | let usb_fut = usb.run(); | ||
| 70 | |||
| 71 | // Do stuff with the class! | ||
| 72 | let echo_fut = async { | ||
| 73 | loop { | ||
| 74 | class.wait_connection().await; | ||
| 75 | info!("Connected"); | ||
| 76 | let _ = echo(&mut class).await; | ||
| 77 | info!("Disconnected"); | ||
| 78 | } | ||
| 79 | }; | ||
| 80 | |||
| 81 | // Run everything concurrently. | ||
| 82 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 83 | join(usb_fut, echo_fut).await; | ||
| 84 | } | ||
| 85 | |||
| 86 | struct Disconnected {} | ||
| 87 | |||
| 88 | impl From<EndpointError> for Disconnected { | ||
| 89 | fn from(val: EndpointError) -> Self { | ||
| 90 | match val { | ||
| 91 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 92 | EndpointError::Disabled => Disconnected {}, | ||
| 93 | } | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 98 | let mut buf = [0; 64]; | ||
| 99 | loop { | ||
| 100 | let n = class.read_packet(&mut buf).await?; | ||
| 101 | let data = &buf[..n]; | ||
| 102 | info!("data: {:x}", data); | ||
| 103 | class.write_packet(data).await?; | ||
| 104 | } | ||
| 105 | } | ||
diff --git a/examples/stm32h7/src/bin/wdg.rs b/examples/stm32h7/src/bin/wdg.rs new file mode 100644 index 000000000..2b0301aad --- /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 | unsafe { wdg.unleash() }; | ||
| 19 | |||
| 20 | loop { | ||
| 21 | Timer::after(Duration::from_secs(1)).await; | ||
| 22 | unsafe { wdg.pet() }; | ||
| 23 | } | ||
| 24 | } | ||
diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 7e1120f48..86933a629 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml | |||
| @@ -19,10 +19,10 @@ lorawan-device = { version = "0.8.0", default-features = false, features = ["asy | |||
| 19 | lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } | 19 | lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } |
| 20 | 20 | ||
| 21 | defmt = "0.3" | 21 | defmt = "0.3" |
| 22 | defmt-rtt = "0.3" | 22 | defmt-rtt = "0.4" |
| 23 | 23 | ||
| 24 | embedded-storage = "0.3.0" | 24 | embedded-storage = "0.3.0" |
| 25 | embedded-io = "0.3.0" | 25 | embedded-io = "0.4.0" |
| 26 | 26 | ||
| 27 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 27 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 28 | cortex-m-rt = "0.7.0" | 28 | cortex-m-rt = "0.7.0" |
diff --git a/examples/stm32l0/src/bin/usart_dma.rs b/examples/stm32l0/src/bin/usart_dma.rs index 66657d0f0..c307f857a 100644 --- a/examples/stm32l0/src/bin/usart_dma.rs +++ b/examples/stm32l0/src/bin/usart_dma.rs | |||
| @@ -4,13 +4,15 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::interrupt; | ||
| 7 | use embassy_stm32::usart::{Config, Uart}; | 8 | use embassy_stm32::usart::{Config, Uart}; |
| 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] |
| 11 | async fn main(_spawner: Spawner) { | 12 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_stm32::init(Default::default()); | 13 | 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()); | 14 | let irq = interrupt::take!(USART1); |
| 15 | let mut usart = Uart::new(p.USART1, p.PB7, p.PB6, irq, p.DMA1_CH2, p.DMA1_CH3, Config::default()); | ||
| 14 | 16 | ||
| 15 | usart.write(b"Hello Embassy World!\r\n").await.unwrap(); | 17 | usart.write(b"Hello Embassy World!\r\n").await.unwrap(); |
| 16 | info!("wrote Hello, starting echo"); | 18 | 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..8e84cd092 100644 --- a/examples/stm32l0/src/bin/usart_irq.rs +++ b/examples/stm32l0/src/bin/usart_irq.rs | |||
| @@ -4,9 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::dma::NoDma; | ||
| 8 | use embassy_stm32::interrupt; | 7 | use embassy_stm32::interrupt; |
| 9 | use embassy_stm32::usart::{BufferedUart, Config, State, Uart}; | 8 | use embassy_stm32::usart::{BufferedUart, Config, State}; |
| 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 | ||
| @@ -21,15 +20,18 @@ async fn main(_spawner: Spawner) { | |||
| 21 | let mut config = Config::default(); | 20 | let mut config = Config::default(); |
| 22 | config.baudrate = 9600; | 21 | config.baudrate = 9600; |
| 23 | 22 | ||
| 24 | let usart = Uart::new(p.USART2, p.PA3, p.PA2, NoDma, NoDma, config); | ||
| 25 | let mut state = State::new(); | 23 | let mut state = State::new(); |
| 24 | let irq = interrupt::take!(USART2); | ||
| 26 | let mut usart = unsafe { | 25 | let mut usart = unsafe { |
| 27 | BufferedUart::new( | 26 | BufferedUart::new( |
| 28 | &mut state, | 27 | &mut state, |
| 29 | usart, | 28 | p.USART2, |
| 30 | interrupt::take!(USART2), | 29 | p.PA3, |
| 30 | p.PA2, | ||
| 31 | irq, | ||
| 31 | &mut TX_BUFFER, | 32 | &mut TX_BUFFER, |
| 32 | &mut RX_BUFFER, | 33 | &mut RX_BUFFER, |
| 34 | config, | ||
| 33 | ) | 35 | ) |
| 34 | }; | 36 | }; |
| 35 | 37 | ||
diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 9460febf5..6e3b2103c 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml | |||
| @@ -11,7 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de | |||
| 11 | 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"] } |
| 12 | 12 | ||
| 13 | defmt = "0.3" | 13 | defmt = "0.3" |
| 14 | defmt-rtt = "0.3" | 14 | defmt-rtt = "0.4" |
| 15 | 15 | ||
| 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 17 | cortex-m-rt = "0.7.0" | 17 | cortex-m-rt = "0.7.0" |
diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 657605ebe..5627760ef 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml | |||
| @@ -12,20 +12,18 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature | |||
| 12 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } |
| 14 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] } | 14 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] } |
| 15 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 15 | 16 | ||
| 16 | defmt = "0.3" | 17 | defmt = "0.3" |
| 17 | defmt-rtt = "0.3" | 18 | defmt-rtt = "0.4" |
| 18 | 19 | ||
| 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 21 | cortex-m-rt = "0.7.0" |
| 21 | embedded-hal = "0.2.6" | 22 | embedded-hal = "0.2.6" |
| 22 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } | 23 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } |
| 23 | embedded-hal-async = { version = "=0.1.0-alpha.2" } | 24 | embedded-hal-async = { version = "=0.2.0-alpha.0" } |
| 24 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 25 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 25 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 26 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } |
| 26 | heapless = { version = "0.7.5", default-features = false } | 27 | heapless = { version = "0.7.5", default-features = false } |
| 27 | 28 | ||
| 28 | micromath = "2.0.0" | 29 | micromath = "2.0.0" |
| 29 | usb-device = "0.2" | ||
| 30 | usbd-serial = "0.1.1" | ||
| 31 | |||
diff --git a/examples/stm32l4/src/bin/usart.rs b/examples/stm32l4/src/bin/usart.rs index 4a4b46c53..7d874d9d7 100644 --- a/examples/stm32l4/src/bin/usart.rs +++ b/examples/stm32l4/src/bin/usart.rs | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_stm32::dma::NoDma; | 6 | use embassy_stm32::dma::NoDma; |
| 7 | use embassy_stm32::interrupt; | ||
| 7 | use embassy_stm32::usart::{Config, Uart}; | 8 | use embassy_stm32::usart::{Config, Uart}; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| @@ -14,7 +15,8 @@ fn main() -> ! { | |||
| 14 | let p = embassy_stm32::init(Default::default()); | 15 | let p = embassy_stm32::init(Default::default()); |
| 15 | 16 | ||
| 16 | let config = Config::default(); | 17 | let config = Config::default(); |
| 17 | let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, NoDma, NoDma, config); | 18 | let irq = interrupt::take!(UART4); |
| 19 | let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, irq, NoDma, NoDma, config); | ||
| 18 | 20 | ||
| 19 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); | 21 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); |
| 20 | info!("wrote Hello, starting echo"); | 22 | 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..452bede30 100644 --- a/examples/stm32l4/src/bin/usart_dma.rs +++ b/examples/stm32l4/src/bin/usart_dma.rs | |||
| @@ -7,6 +7,7 @@ use core::fmt::Write; | |||
| 7 | use defmt::*; | 7 | 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::interrupt; | ||
| 10 | use embassy_stm32::usart::{Config, Uart}; | 11 | use embassy_stm32::usart::{Config, Uart}; |
| 11 | use heapless::String; | 12 | use heapless::String; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -17,7 +18,8 @@ async fn main(_spawner: Spawner) { | |||
| 17 | info!("Hello World!"); | 18 | info!("Hello World!"); |
| 18 | 19 | ||
| 19 | let config = Config::default(); | 20 | let config = Config::default(); |
| 20 | let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, p.DMA1_CH3, NoDma, config); | 21 | let irq = interrupt::take!(UART4); |
| 22 | let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, irq, p.DMA1_CH3, NoDma, config); | ||
| 21 | 23 | ||
| 22 | for n in 0u32.. { | 24 | for n in 0u32.. { |
| 23 | let mut s: String<128> = String::new(); | 25 | 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..663f60d52 --- /dev/null +++ b/examples/stm32l4/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,107 @@ | |||
| 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::{interrupt, 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 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | info!("Hello World!"); | ||
| 20 | |||
| 21 | let mut config = Config::default(); | ||
| 22 | config.rcc.mux = ClockSrc::PLL(PLLSource::HSI16, PLLClkDiv::Div2, PLLSrcDiv::Div1, PLLMul::Mul10, None); | ||
| 23 | config.rcc.hsi48 = true; | ||
| 24 | |||
| 25 | let p = embassy_stm32::init(config); | ||
| 26 | |||
| 27 | // Create the driver, from the HAL. | ||
| 28 | let irq = interrupt::take!(OTG_FS); | ||
| 29 | let mut ep_out_buffer = [0u8; 256]; | ||
| 30 | let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); | ||
| 31 | |||
| 32 | // Create embassy-usb Config | ||
| 33 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 34 | config.max_packet_size_0 = 64; | ||
| 35 | config.manufacturer = Some("Embassy"); | ||
| 36 | config.product = Some("USB-serial example"); | ||
| 37 | config.serial_number = Some("12345678"); | ||
| 38 | |||
| 39 | // Required for windows compatiblity. | ||
| 40 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 41 | config.device_class = 0xEF; | ||
| 42 | config.device_sub_class = 0x02; | ||
| 43 | config.device_protocol = 0x01; | ||
| 44 | config.composite_with_iads = true; | ||
| 45 | |||
| 46 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 47 | // It needs some buffers for building the descriptors. | ||
| 48 | let mut device_descriptor = [0; 256]; | ||
| 49 | let mut config_descriptor = [0; 256]; | ||
| 50 | let mut bos_descriptor = [0; 256]; | ||
| 51 | let mut control_buf = [0; 64]; | ||
| 52 | |||
| 53 | let mut state = State::new(); | ||
| 54 | |||
| 55 | let mut builder = Builder::new( | ||
| 56 | driver, | ||
| 57 | config, | ||
| 58 | &mut device_descriptor, | ||
| 59 | &mut config_descriptor, | ||
| 60 | &mut bos_descriptor, | ||
| 61 | &mut control_buf, | ||
| 62 | ); | ||
| 63 | |||
| 64 | // Create classes on the builder. | ||
| 65 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 66 | |||
| 67 | // Build the builder. | ||
| 68 | let mut usb = builder.build(); | ||
| 69 | |||
| 70 | // Run the USB device. | ||
| 71 | let usb_fut = usb.run(); | ||
| 72 | |||
| 73 | // Do stuff with the class! | ||
| 74 | let echo_fut = async { | ||
| 75 | loop { | ||
| 76 | class.wait_connection().await; | ||
| 77 | info!("Connected"); | ||
| 78 | let _ = echo(&mut class).await; | ||
| 79 | info!("Disconnected"); | ||
| 80 | } | ||
| 81 | }; | ||
| 82 | |||
| 83 | // Run everything concurrently. | ||
| 84 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 85 | join(usb_fut, echo_fut).await; | ||
| 86 | } | ||
| 87 | |||
| 88 | struct Disconnected {} | ||
| 89 | |||
| 90 | impl From<EndpointError> for Disconnected { | ||
| 91 | fn from(val: EndpointError) -> Self { | ||
| 92 | match val { | ||
| 93 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 94 | EndpointError::Disabled => Disconnected {}, | ||
| 95 | } | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 100 | let mut buf = [0; 64]; | ||
| 101 | loop { | ||
| 102 | let n = class.read_packet(&mut buf).await?; | ||
| 103 | let data = &buf[..n]; | ||
| 104 | info!("data: {:x}", data); | ||
| 105 | class.write_packet(data).await?; | ||
| 106 | } | ||
| 107 | } | ||
diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 63eac3ed2..c0accb0d6 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml | |||
| @@ -12,12 +12,12 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature | |||
| 12 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | 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-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } |
| 14 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | 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", "dhcpv4", "medium-ethernet", "pool-16"] } | 15 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } |
| 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 17 | usbd-hid = "0.6.0" | 17 | usbd-hid = "0.6.0" |
| 18 | 18 | ||
| 19 | defmt = "0.3" | 19 | defmt = "0.3" |
| 20 | defmt-rtt = "0.3" | 20 | defmt-rtt = "0.4" |
| 21 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 21 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 22 | 22 | ||
| 23 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 23 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| @@ -26,5 +26,5 @@ embedded-hal = "0.2.6" | |||
| 26 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 26 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } |
| 27 | heapless = { version = "0.7.5", default-features = false } | 27 | heapless = { version = "0.7.5", default-features = false } |
| 28 | rand_core = { version = "0.6.3", default-features = false } | 28 | rand_core = { version = "0.6.3", default-features = false } |
| 29 | embedded-io = { version = "0.3.0", features = ["async"] } | 29 | embedded-io = { version = "0.4.0", features = ["async"] } |
| 30 | static_cell = "1.0" | 30 | static_cell = "1.0" |
diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index 4f36d3f5a..98ec0e836 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs | |||
| @@ -2,20 +2,16 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | 4 | ||
| 5 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 6 | use core::task::Waker; | ||
| 7 | |||
| 8 | use defmt::*; | 5 | use defmt::*; |
| 9 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 10 | use embassy_net::tcp::TcpSocket; | 7 | use embassy_net::tcp::TcpSocket; |
| 11 | use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; | 8 | use embassy_net::{Stack, StackResources}; |
| 12 | use embassy_stm32::rcc::*; | 9 | use embassy_stm32::rcc::*; |
| 13 | use embassy_stm32::rng::Rng; | 10 | use embassy_stm32::rng::Rng; |
| 14 | use embassy_stm32::usb::Driver; | 11 | use embassy_stm32::usb::Driver; |
| 15 | use embassy_stm32::{interrupt, Config}; | 12 | use embassy_stm32::{interrupt, Config}; |
| 16 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | 13 | use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; |
| 17 | use embassy_sync::channel::Channel; | 14 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; |
| 18 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State}; | ||
| 19 | use embassy_usb::{Builder, UsbDevice}; | 15 | use embassy_usb::{Builder, UsbDevice}; |
| 20 | use embedded_io::asynch::Write; | 16 | use embedded_io::asynch::Write; |
| 21 | use rand_core::RngCore; | 17 | use rand_core::RngCore; |
| @@ -28,56 +24,25 @@ macro_rules! singleton { | |||
| 28 | ($val:expr) => {{ | 24 | ($val:expr) => {{ |
| 29 | type T = impl Sized; | 25 | type T = impl Sized; |
| 30 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | 26 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); |
| 31 | STATIC_CELL.init_with(move || $val) | 27 | let (x,) = STATIC_CELL.init(($val,)); |
| 28 | x | ||
| 32 | }}; | 29 | }}; |
| 33 | } | 30 | } |
| 34 | 31 | ||
| 32 | const MTU: usize = 1514; | ||
| 33 | |||
| 35 | #[embassy_executor::task] | 34 | #[embassy_executor::task] |
| 36 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { | 35 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { |
| 37 | device.run().await | 36 | device.run().await |
| 38 | } | 37 | } |
| 39 | 38 | ||
| 40 | #[embassy_executor::task] | 39 | #[embassy_executor::task] |
| 41 | async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { | 40 | async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { |
| 42 | loop { | 41 | class.run().await |
| 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 | } | 42 | } |
| 68 | 43 | ||
| 69 | #[embassy_executor::task] | 44 | #[embassy_executor::task] |
| 70 | async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { | 45 | async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! { |
| 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 | 46 | stack.run().await |
| 82 | } | 47 | } |
| 83 | 48 | ||
| @@ -106,58 +71,34 @@ async fn main(spawner: Spawner) { | |||
| 106 | config.device_sub_class = 0x02; | 71 | config.device_sub_class = 0x02; |
| 107 | config.device_protocol = 0x01; | 72 | config.device_protocol = 0x01; |
| 108 | 73 | ||
| 109 | struct Resources { | ||
| 110 | device_descriptor: [u8; 256], | ||
| 111 | config_descriptor: [u8; 256], | ||
| 112 | bos_descriptor: [u8; 256], | ||
| 113 | control_buf: [u8; 128], | ||
| 114 | serial_state: State<'static>, | ||
| 115 | } | ||
| 116 | let res: &mut Resources = singleton!(Resources { | ||
| 117 | device_descriptor: [0; 256], | ||
| 118 | config_descriptor: [0; 256], | ||
| 119 | bos_descriptor: [0; 256], | ||
| 120 | control_buf: [0; 128], | ||
| 121 | serial_state: State::new(), | ||
| 122 | }); | ||
| 123 | |||
| 124 | // Create embassy-usb DeviceBuilder using the driver and config. | 74 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 125 | let mut builder = Builder::new( | 75 | let mut builder = Builder::new( |
| 126 | driver, | 76 | driver, |
| 127 | config, | 77 | config, |
| 128 | &mut res.device_descriptor, | 78 | &mut singleton!([0; 256])[..], |
| 129 | &mut res.config_descriptor, | 79 | &mut singleton!([0; 256])[..], |
| 130 | &mut res.bos_descriptor, | 80 | &mut singleton!([0; 256])[..], |
| 131 | &mut res.control_buf, | 81 | &mut singleton!([0; 128])[..], |
| 132 | None, | ||
| 133 | ); | 82 | ); |
| 134 | 83 | ||
| 135 | // WARNINGS for Android ethernet tethering: | ||
| 136 | // - On Pixel 4a, it refused to work on Android 11, worked on Android 12. | ||
| 137 | // - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), | ||
| 138 | // it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. | ||
| 139 | // 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 | ||
| 140 | // and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 | ||
| 141 | |||
| 142 | // Our MAC addr. | 84 | // Our MAC addr. |
| 143 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; | 85 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; |
| 144 | // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. | 86 | // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. |
| 145 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; | 87 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; |
| 146 | 88 | ||
| 147 | // Create classes on the builder. | 89 | // Create classes on the builder. |
| 148 | let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64); | 90 | let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); |
| 149 | 91 | ||
| 150 | // Build the builder. | 92 | // Build the builder. |
| 151 | let usb = builder.build(); | 93 | let usb = builder.build(); |
| 152 | 94 | ||
| 153 | unwrap!(spawner.spawn(usb_task(usb))); | 95 | unwrap!(spawner.spawn(usb_task(usb))); |
| 154 | 96 | ||
| 155 | let (tx, rx) = class.split(); | 97 | let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(singleton!(NetState::new()), our_mac_addr); |
| 156 | unwrap!(spawner.spawn(usb_ncm_rx_task(rx))); | 98 | unwrap!(spawner.spawn(usb_ncm_task(runner))); |
| 157 | unwrap!(spawner.spawn(usb_ncm_tx_task(tx))); | ||
| 158 | 99 | ||
| 159 | let config = embassy_net::ConfigStrategy::Dhcp; | 100 | let config = embassy_net::Config::Dhcp(Default::default()); |
| 160 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { | 101 | //let config = embassy_net::Config::Static(embassy_net::StaticConfig { |
| 161 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | 102 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), |
| 162 | // dns_servers: Vec::new(), | 103 | // dns_servers: Vec::new(), |
| 163 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | 104 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), |
| @@ -168,13 +109,7 @@ async fn main(spawner: Spawner) { | |||
| 168 | let seed = rng.next_u64(); | 109 | let seed = rng.next_u64(); |
| 169 | 110 | ||
| 170 | // Init network stack | 111 | // Init network stack |
| 171 | let device = Device { mac_addr: our_mac_addr }; | 112 | let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); |
| 172 | let stack = &*singleton!(Stack::new( | ||
| 173 | device, | ||
| 174 | config, | ||
| 175 | singleton!(StackResources::<1, 2, 8>::new()), | ||
| 176 | seed | ||
| 177 | )); | ||
| 178 | 113 | ||
| 179 | unwrap!(spawner.spawn(net_task(stack))); | 114 | unwrap!(spawner.spawn(net_task(stack))); |
| 180 | 115 | ||
| @@ -221,50 +156,3 @@ async fn main(spawner: Spawner) { | |||
| 221 | } | 156 | } |
| 222 | } | 157 | } |
| 223 | } | 158 | } |
| 224 | |||
| 225 | static TX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new(); | ||
| 226 | static RX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new(); | ||
| 227 | static LINK_UP: AtomicBool = AtomicBool::new(false); | ||
| 228 | |||
| 229 | struct Device { | ||
| 230 | mac_addr: [u8; 6], | ||
| 231 | } | ||
| 232 | |||
| 233 | impl embassy_net::Device for Device { | ||
| 234 | fn register_waker(&mut self, waker: &Waker) { | ||
| 235 | // loopy loopy wakey wakey | ||
| 236 | waker.wake_by_ref() | ||
| 237 | } | ||
| 238 | |||
| 239 | fn link_state(&mut self) -> embassy_net::LinkState { | ||
| 240 | match LINK_UP.load(Ordering::Relaxed) { | ||
| 241 | true => embassy_net::LinkState::Up, | ||
| 242 | false => embassy_net::LinkState::Down, | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | fn capabilities(&self) -> embassy_net::DeviceCapabilities { | ||
| 247 | let mut caps = embassy_net::DeviceCapabilities::default(); | ||
| 248 | caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header | ||
| 249 | caps.medium = embassy_net::Medium::Ethernet; | ||
| 250 | caps | ||
| 251 | } | ||
| 252 | |||
| 253 | fn is_transmit_ready(&mut self) -> bool { | ||
| 254 | true | ||
| 255 | } | ||
| 256 | |||
| 257 | fn transmit(&mut self, pkt: PacketBuf) { | ||
| 258 | if TX_CHANNEL.try_send(pkt).is_err() { | ||
| 259 | warn!("TX failed") | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | fn receive<'a>(&mut self) -> Option<PacketBuf> { | ||
| 264 | RX_CHANNEL.try_recv().ok() | ||
| 265 | } | ||
| 266 | |||
| 267 | fn ethernet_address(&self) -> [u8; 6] { | ||
| 268 | self.mac_addr | ||
| 269 | } | ||
| 270 | } | ||
diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index d38ed7496..e3bbe9d09 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs | |||
| @@ -51,7 +51,6 @@ async fn main(_spawner: Spawner) { | |||
| 51 | &mut config_descriptor, | 51 | &mut config_descriptor, |
| 52 | &mut bos_descriptor, | 52 | &mut bos_descriptor, |
| 53 | &mut control_buf, | 53 | &mut control_buf, |
| 54 | None, | ||
| 55 | ); | 54 | ); |
| 56 | 55 | ||
| 57 | // Create classes on the builder. | 56 | // Create classes on the builder. |
diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs index 7562a4e96..66ccacb73 100644 --- a/examples/stm32l5/src/bin/usb_serial.rs +++ b/examples/stm32l5/src/bin/usb_serial.rs | |||
| @@ -46,7 +46,6 @@ async fn main(_spawner: Spawner) { | |||
| 46 | &mut config_descriptor, | 46 | &mut config_descriptor, |
| 47 | &mut bos_descriptor, | 47 | &mut bos_descriptor, |
| 48 | &mut control_buf, | 48 | &mut control_buf, |
| 49 | None, | ||
| 50 | ); | 49 | ); |
| 51 | 50 | ||
| 52 | // Create classes on the builder. | 51 | // Create classes on the builder. |
diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 3d704011b..2b02eda92 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml | |||
| @@ -9,9 +9,10 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de | |||
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", 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", "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"] } | ||
| 12 | 13 | ||
| 13 | defmt = "0.3" | 14 | defmt = "0.3" |
| 14 | defmt-rtt = "0.3" | 15 | defmt-rtt = "0.4" |
| 15 | 16 | ||
| 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 17 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 17 | cortex-m-rt = "0.7.0" | 18 | cortex-m-rt = "0.7.0" |
| @@ -21,8 +22,3 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa | |||
| 21 | heapless = { version = "0.7.5", default-features = false } | 22 | heapless = { version = "0.7.5", default-features = false } |
| 22 | 23 | ||
| 23 | micromath = "2.0.0" | 24 | micromath = "2.0.0" |
| 24 | |||
| 25 | #[patch.crates-io] | ||
| 26 | #defmt = { git="https://github.com/knurling-rs/defmt.git" } | ||
| 27 | #defmt-rtt = { git="https://github.com/knurling-rs/defmt.git" } | ||
| 28 | |||
diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs new file mode 100644 index 000000000..8cd3bf2f4 --- /dev/null +++ b/examples/stm32u5/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,107 @@ | |||
| 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::{interrupt, 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 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | info!("Hello World!"); | ||
| 20 | |||
| 21 | let mut config = Config::default(); | ||
| 22 | config.rcc.mux = ClockSrc::PLL1R(PllSrc::HSI16, PllM::Div2, PllN::Mul10, PllClkDiv::NotDivided); | ||
| 23 | //config.rcc.mux = ClockSrc::MSI(MSIRange::Range48mhz); | ||
| 24 | config.rcc.hsi48 = true; | ||
| 25 | |||
| 26 | let p = embassy_stm32::init(config); | ||
| 27 | |||
| 28 | // Create the driver, from the HAL. | ||
| 29 | let irq = interrupt::take!(OTG_FS); | ||
| 30 | let mut ep_out_buffer = [0u8; 256]; | ||
| 31 | let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); | ||
| 32 | |||
| 33 | // Create embassy-usb Config | ||
| 34 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 35 | config.manufacturer = Some("Embassy"); | ||
| 36 | config.product = Some("USB-serial example"); | ||
| 37 | config.serial_number = Some("12345678"); | ||
| 38 | |||
| 39 | // Required for windows compatiblity. | ||
| 40 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 41 | config.device_class = 0xEF; | ||
| 42 | config.device_sub_class = 0x02; | ||
| 43 | config.device_protocol = 0x01; | ||
| 44 | config.composite_with_iads = true; | ||
| 45 | |||
| 46 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 47 | // It needs some buffers for building the descriptors. | ||
| 48 | let mut device_descriptor = [0; 256]; | ||
| 49 | let mut config_descriptor = [0; 256]; | ||
| 50 | let mut bos_descriptor = [0; 256]; | ||
| 51 | let mut control_buf = [0; 64]; | ||
| 52 | |||
| 53 | let mut state = State::new(); | ||
| 54 | |||
| 55 | let mut builder = Builder::new( | ||
| 56 | driver, | ||
| 57 | config, | ||
| 58 | &mut device_descriptor, | ||
| 59 | &mut config_descriptor, | ||
| 60 | &mut bos_descriptor, | ||
| 61 | &mut control_buf, | ||
| 62 | ); | ||
| 63 | |||
| 64 | // Create classes on the builder. | ||
| 65 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 66 | |||
| 67 | // Build the builder. | ||
| 68 | let mut usb = builder.build(); | ||
| 69 | |||
| 70 | // Run the USB device. | ||
| 71 | let usb_fut = usb.run(); | ||
| 72 | |||
| 73 | // Do stuff with the class! | ||
| 74 | let echo_fut = async { | ||
| 75 | loop { | ||
| 76 | class.wait_connection().await; | ||
| 77 | info!("Connected"); | ||
| 78 | let _ = echo(&mut class).await; | ||
| 79 | info!("Disconnected"); | ||
| 80 | } | ||
| 81 | }; | ||
| 82 | |||
| 83 | // Run everything concurrently. | ||
| 84 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 85 | join(usb_fut, echo_fut).await; | ||
| 86 | } | ||
| 87 | |||
| 88 | struct Disconnected {} | ||
| 89 | |||
| 90 | impl From<EndpointError> for Disconnected { | ||
| 91 | fn from(val: EndpointError) -> Self { | ||
| 92 | match val { | ||
| 93 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 94 | EndpointError::Disabled => Disconnected {}, | ||
| 95 | } | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 100 | let mut buf = [0; 64]; | ||
| 101 | loop { | ||
| 102 | let n = class.read_packet(&mut buf).await?; | ||
| 103 | let data = &buf[..n]; | ||
| 104 | info!("data: {:x}", data); | ||
| 105 | class.write_packet(data).await?; | ||
| 106 | } | ||
| 107 | } | ||
diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 5b96fa191..e27b4527c 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml | |||
| @@ -11,7 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de | |||
| 11 | 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", "stm32wb55cc", "time-driver-any", "exti"] } |
| 12 | 12 | ||
| 13 | defmt = "0.3" | 13 | defmt = "0.3" |
| 14 | defmt-rtt = "0.3" | 14 | defmt-rtt = "0.4" |
| 15 | 15 | ||
| 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 17 | cortex-m-rt = "0.7.0" | 17 | cortex-m-rt = "0.7.0" |
diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index c827d2b71..690481bbf 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml | |||
| @@ -15,7 +15,7 @@ lorawan-device = { version = "0.8.0", default-features = false, features = ["asy | |||
| 15 | lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"] } | 15 | lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"] } |
| 16 | 16 | ||
| 17 | defmt = "0.3" | 17 | defmt = "0.3" |
| 18 | defmt-rtt = "0.3" | 18 | defmt-rtt = "0.4" |
| 19 | 19 | ||
| 20 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 21 | cortex-m-rt = "0.7.0" | 21 | cortex-m-rt = "0.7.0" |
diff --git a/examples/stm32wl/src/bin/random.rs b/examples/stm32wl/src/bin/random.rs new file mode 100644 index 000000000..182c607f9 --- /dev/null +++ b/examples/stm32wl/src/bin/random.rs | |||
| @@ -0,0 +1,33 @@ | |||
| 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 | unsafe { | ||
| 19 | pac::RCC.ccipr().modify(|w| { | ||
| 20 | w.set_rngsel(0b01); | ||
| 21 | }); | ||
| 22 | } | ||
| 23 | |||
| 24 | info!("Hello World!"); | ||
| 25 | |||
| 26 | let mut rng = Rng::new(p.RNG); | ||
| 27 | |||
| 28 | let mut buf = [0u8; 16]; | ||
| 29 | unwrap!(rng.async_fill_bytes(&mut buf).await); | ||
| 30 | info!("random bytes: {:02x}", buf); | ||
| 31 | |||
| 32 | loop {} | ||
| 33 | } | ||
diff --git a/examples/stm32wl/src/bin/uart_async.rs b/examples/stm32wl/src/bin/uart_async.rs new file mode 100644 index 000000000..f12fec4c8 --- /dev/null +++ b/examples/stm32wl/src/bin/uart_async.rs | |||
| @@ -0,0 +1,60 @@ | |||
| 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::interrupt; | ||
| 8 | use embassy_stm32::usart::{Config, Uart}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | /* | ||
| 12 | Pass Incoming data from LPUART1 to USART1 | ||
| 13 | Example is written for the LoRa-E5 mini v1.0, | ||
| 14 | but can be surely changed for your needs. | ||
| 15 | */ | ||
| 16 | #[embassy_executor::main] | ||
| 17 | async fn main(_spawner: Spawner) { | ||
| 18 | let mut config = embassy_stm32::Config::default(); | ||
| 19 | config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; | ||
| 20 | let p = embassy_stm32::init(config); | ||
| 21 | |||
| 22 | defmt::info!("Starting system"); | ||
| 23 | |||
| 24 | let mut config1 = Config::default(); | ||
| 25 | config1.baudrate = 9600; | ||
| 26 | |||
| 27 | let mut config2 = Config::default(); | ||
| 28 | config2.baudrate = 9600; | ||
| 29 | |||
| 30 | //RX/TX connected to USB/UART Bridge on LoRa-E5 mini v1.0 | ||
| 31 | let irq = interrupt::take!(USART1); | ||
| 32 | let mut usart1 = Uart::new(p.USART1, p.PB7, p.PB6, irq, p.DMA1_CH3, p.DMA1_CH4, config1); | ||
| 33 | |||
| 34 | //RX1/TX1 (LPUART) on LoRa-E5 mini v1.0 | ||
| 35 | let irq = interrupt::take!(LPUART1); | ||
| 36 | let mut usart2 = Uart::new(p.LPUART1, p.PC0, p.PC1, irq, p.DMA1_CH5, p.DMA1_CH6, config2); | ||
| 37 | |||
| 38 | unwrap!(usart1.write(b"Hello Embassy World!\r\n").await); | ||
| 39 | unwrap!(usart2.write(b"Hello Embassy World!\r\n").await); | ||
| 40 | |||
| 41 | let mut buf = [0u8; 300]; | ||
| 42 | loop { | ||
| 43 | let result = usart2.read_until_idle(&mut buf).await; | ||
| 44 | match result { | ||
| 45 | Ok(size) => { | ||
| 46 | match usart1.write(&buf[0..size]).await { | ||
| 47 | Ok(()) => { | ||
| 48 | //Write suc. | ||
| 49 | } | ||
| 50 | Err(..) => { | ||
| 51 | //Wasnt able to write | ||
| 52 | } | ||
| 53 | } | ||
| 54 | } | ||
| 55 | Err(_err) => { | ||
| 56 | //Ignore eg. framing errors | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } | ||
