aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2024-08-12 11:22:19 +0000
committerGitHub <[email protected]>2024-08-12 11:22:19 +0000
commit66a5a33da9b8686265521bcd4a50bbe1a56cbe93 (patch)
tree5bdbae99a7fe8df884d8d9a3fc5b980eba5e54de /examples
parentfdc34b69ffc292427f50f35209f095bb5a50bb82 (diff)
parent13cb4314725bb60d2252575f8fd06828e8661064 (diff)
Merge pull request #3243 from HellbenderInc/rp2350
Initial rp235x support
Diffstat (limited to 'examples')
-rw-r--r--examples/boot/application/rp/Cargo.toml2
-rw-r--r--examples/boot/bootloader/rp/Cargo.toml2
-rw-r--r--examples/rp/Cargo.toml4
-rw-r--r--examples/rp23/.cargo/config.toml9
-rw-r--r--examples/rp23/Cargo.toml80
-rw-r--r--examples/rp23/assets/ferris.rawbin0 -> 11008 bytes
-rw-r--r--examples/rp23/build.rs35
-rw-r--r--examples/rp23/memory.x74
-rw-r--r--examples/rp23/src/bin/adc.rs63
-rw-r--r--examples/rp23/src/bin/adc_dma.rs69
-rw-r--r--examples/rp23/src/bin/assign_resources.rs94
-rw-r--r--examples/rp23/src/bin/blinky.rs44
-rw-r--r--examples/rp23/src/bin/blinky_two_channels.rs65
-rw-r--r--examples/rp23/src/bin/blinky_two_tasks.rs64
-rw-r--r--examples/rp23/src/bin/button.rs43
-rw-r--r--examples/rp23/src/bin/debounce.rs95
-rw-r--r--examples/rp23/src/bin/flash.rs149
-rw-r--r--examples/rp23/src/bin/gpio_async.rs55
-rw-r--r--examples/rp23/src/bin/gpout.rs52
-rw-r--r--examples/rp23/src/bin/i2c_async.rs125
-rw-r--r--examples/rp23/src/bin/i2c_async_embassy.rs100
-rw-r--r--examples/rp23/src/bin/i2c_blocking.rs89
-rw-r--r--examples/rp23/src/bin/i2c_slave.rs132
-rw-r--r--examples/rp23/src/bin/interrupt.rs109
-rw-r--r--examples/rp23/src/bin/multicore.rs81
-rw-r--r--examples/rp23/src/bin/multiprio.rs160
-rw-r--r--examples/rp23/src/bin/pio_async.rs145
-rw-r--r--examples/rp23/src/bin/pio_dma.rs98
-rw-r--r--examples/rp23/src/bin/pio_hd44780.rs255
-rw-r--r--examples/rp23/src/bin/pio_i2s.rs140
-rw-r--r--examples/rp23/src/bin/pio_pwm.rs133
-rw-r--r--examples/rp23/src/bin/pio_rotary_encoder.rs95
-rw-r--r--examples/rp23/src/bin/pio_servo.rs223
-rw-r--r--examples/rp23/src/bin/pio_stepper.rs183
-rw-r--r--examples/rp23/src/bin/pio_ws2812.rs176
-rw-r--r--examples/rp23/src/bin/pwm.rs44
-rw-r--r--examples/rp23/src/bin/pwm_input.rs41
-rw-r--r--examples/rp23/src/bin/rosc.rs46
-rw-r--r--examples/rp23/src/bin/shared_bus.rs130
-rw-r--r--examples/rp23/src/bin/sharing.rs165
-rw-r--r--examples/rp23/src/bin/spi.rs61
-rw-r--r--examples/rp23/src/bin/spi_async.rs46
-rw-r--r--examples/rp23/src/bin/spi_display.rs327
-rw-r--r--examples/rp23/src/bin/spi_sdmmc.rs98
-rw-r--r--examples/rp23/src/bin/uart.rs40
-rw-r--r--examples/rp23/src/bin/uart_buffered_split.rs73
-rw-r--r--examples/rp23/src/bin/uart_r503.rs173
-rw-r--r--examples/rp23/src/bin/uart_unidir.rs65
-rw-r--r--examples/rp23/src/bin/usb_webusb.rs170
-rw-r--r--examples/rp23/src/bin/watchdog.rs66
-rw-r--r--examples/rp23/src/bin/zerocopy.rs109
51 files changed, 4893 insertions, 4 deletions
diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml
index c109c0732..8bb8afdfe 100644
--- a/examples/boot/application/rp/Cargo.toml
+++ b/examples/boot/application/rp/Cargo.toml
@@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
8embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } 9embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] } 10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] }
11embassy-rp = { version = "0.2.0", path = "../../../../embassy-rp", features = ["time-driver", ] } 11embassy-rp = { version = "0.2.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] }
12embassy-boot-rp = { version = "0.3.0", path = "../../../../embassy-boot-rp", features = [] } 12embassy-boot-rp = { version = "0.3.0", path = "../../../../embassy-boot-rp", features = [] }
13embassy-embedded-hal = { version = "0.2.0", path = "../../../../embassy-embedded-hal" } 13embassy-embedded-hal = { version = "0.2.0", path = "../../../../embassy-embedded-hal" }
14 14
diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml
index c15c980ca..9df396e5e 100644
--- a/examples/boot/bootloader/rp/Cargo.toml
+++ b/examples/boot/bootloader/rp/Cargo.toml
@@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
9defmt = { version = "0.3", optional = true } 9defmt = { version = "0.3", optional = true }
10defmt-rtt = { version = "0.4", optional = true } 10defmt-rtt = { version = "0.4", optional = true }
11 11
12embassy-rp = { path = "../../../../embassy-rp", features = [] } 12embassy-rp = { path = "../../../../embassy-rp", features = ["rp2040"] }
13embassy-boot-rp = { path = "../../../../embassy-boot-rp" } 13embassy-boot-rp = { path = "../../../../embassy-boot-rp" }
14embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } 14embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" }
15embassy-time = { path = "../../../../embassy-time", features = [] } 15embassy-time = { path = "../../../../embassy-time", features = [] }
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index 031f68253..83d5792b6 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -10,7 +10,7 @@ embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal",
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
13embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl"] } 13embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] }
14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } 15embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] }
16embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } 16embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
@@ -51,7 +51,7 @@ embedded-hal-async = "1.0"
51embedded-hal-bus = { version = "0.1", features = ["async"] } 51embedded-hal-bus = { version = "0.1", features = ["async"] }
52embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } 52embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
53embedded-storage = { version = "0.3" } 53embedded-storage = { version = "0.3" }
54static_cell = "2" 54static_cell = "2.1"
55portable-atomic = { version = "1.5", features = ["critical-section"] } 55portable-atomic = { version = "1.5", features = ["critical-section"] }
56log = "0.4" 56log = "0.4"
57pio-proc = "0.2" 57pio-proc = "0.2"
diff --git a/examples/rp23/.cargo/config.toml b/examples/rp23/.cargo/config.toml
new file mode 100644
index 000000000..f77e004dc
--- /dev/null
+++ b/examples/rp23/.cargo/config.toml
@@ -0,0 +1,9 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2#runner = "probe-rs run --chip RP2040"
3runner = "elf2uf2-rs -d"
4
5[build]
6target = "thumbv8m.main-none-eabihf"
7
8[env]
9DEFMT_LOG = "debug"
diff --git a/examples/rp23/Cargo.toml b/examples/rp23/Cargo.toml
new file mode 100644
index 000000000..8f8d6ff10
--- /dev/null
+++ b/examples/rp23/Cargo.toml
@@ -0,0 +1,80 @@
1[package]
2edition = "2021"
3name = "embassy-rp2350-examples"
4version = "0.1.0"
5license = "MIT OR Apache-2.0"
6
7
8[dependencies]
9embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
13embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] }
14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] }
16embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
17embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
18embassy-usb-logger = { version = "0.2.0", path = "../../embassy-usb-logger" }
19cyw43 = { version = "0.2.0", path = "../../cyw43", features = ["defmt", "firmware-logs", "bluetooth"] }
20cyw43-pio = { version = "0.2.0", path = "../../cyw43-pio", features = ["defmt"] }
21
22defmt = "0.3"
23defmt-rtt = "0.4"
24fixed = "1.23.1"
25fixed-macro = "1.2"
26
27# for web request example
28reqwless = { version = "0.12.0", features = ["defmt",]}
29serde = { version = "1.0.203", default-features = false, features = ["derive"] }
30serde-json-core = "0.5.1"
31
32# for assign resources example
33assign-resources = { git = "https://github.com/adamgreig/assign-resources", rev = "94ad10e2729afdf0fd5a77cd12e68409a982f58a" }
34
35#cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
36cortex-m = { version = "0.7.6", features = ["inline-asm"] }
37cortex-m-rt = "0.7.0"
38critical-section = "1.1"
39panic-probe = { version = "0.3", features = ["print-defmt"] }
40display-interface-spi = "0.4.1"
41embedded-graphics = "0.7.1"
42st7789 = "0.6.1"
43display-interface = "0.4.1"
44byte-slice-cast = { version = "1.2.0", default-features = false }
45smart-leds = "0.3.0"
46heapless = "0.8"
47usbd-hid = "0.8.1"
48
49embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
50embedded-hal-async = "1.0"
51embedded-hal-bus = { version = "0.1", features = ["async"] }
52embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
53embedded-storage = { version = "0.3" }
54static_cell = "2.1"
55portable-atomic = { version = "1.5", features = ["critical-section"] }
56log = "0.4"
57pio-proc = "0.2"
58pio = "0.2.1"
59rand = { version = "0.8.5", default-features = false }
60embedded-sdmmc = "0.7.0"
61
62bt-hci = { version = "0.1.0", default-features = false, features = ["defmt"] }
63trouble-host = { version = "0.1.0", features = ["defmt", "gatt"] }
64
65[profile.release]
66debug = 2
67
68[profile.dev]
69lto = true
70opt-level = "z"
71
72[patch.crates-io]
73trouble-host = { git = "https://github.com/embassy-rs/trouble.git", rev = "4b8c0f499b34e46ca23a56e2d1640ede371722cf" }
74bt-hci = { git = "https://github.com/alexmoon/bt-hci.git", rev = "b9cd5954f6bd89b535cad9c418e9fdf12812d7c3" }
75embassy-executor = { path = "../../embassy-executor" }
76embassy-sync = { path = "../../embassy-sync" }
77embassy-futures = { path = "../../embassy-futures" }
78embassy-time = { path = "../../embassy-time" }
79embassy-time-driver = { path = "../../embassy-time-driver" }
80embassy-embedded-hal = { path = "../../embassy-embedded-hal" }
diff --git a/examples/rp23/assets/ferris.raw b/examples/rp23/assets/ferris.raw
new file mode 100644
index 000000000..9733889c5
--- /dev/null
+++ b/examples/rp23/assets/ferris.raw
Binary files differ
diff --git a/examples/rp23/build.rs b/examples/rp23/build.rs
new file mode 100644
index 000000000..30691aa97
--- /dev/null
+++ b/examples/rp23/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
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
35}
diff --git a/examples/rp23/memory.x b/examples/rp23/memory.x
new file mode 100644
index 000000000..777492062
--- /dev/null
+++ b/examples/rp23/memory.x
@@ -0,0 +1,74 @@
1MEMORY {
2 /*
3 * The RP2350 has either external or internal flash.
4 *
5 * 2 MiB is a safe default here, although a Pico 2 has 4 MiB.
6 */
7 FLASH : ORIGIN = 0x10000000, LENGTH = 2048K
8 /*
9 * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping.
10 * This is usually good for performance, as it distributes load on
11 * those banks evenly.
12 */
13 RAM : ORIGIN = 0x20000000, LENGTH = 512K
14 /*
15 * RAM banks 8 and 9 use a direct mapping. They can be used to have
16 * memory areas dedicated for some specific job, improving predictability
17 * of access times.
18 * Example: Separate stacks for core0 and core1.
19 */
20 SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K
21 SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K
22}
23
24SECTIONS {
25 /* ### Boot ROM info
26 *
27 * Goes after .vector_table, to keep it in the first 4K of flash
28 * where the Boot ROM (and picotool) can find it
29 */
30 .start_block : ALIGN(4)
31 {
32 __start_block_addr = .;
33 KEEP(*(.start_block));
34 } > FLASH
35
36} INSERT AFTER .vector_table;
37
38/* move .text to start /after/ the boot info */
39_stext = ADDR(.start_block) + SIZEOF(.start_block);
40
41SECTIONS {
42 /* ### Picotool 'Binary Info' Entries
43 *
44 * Picotool looks through this block (as we have pointers to it in our
45 * header) to find interesting information.
46 */
47 .bi_entries : ALIGN(4)
48 {
49 /* We put this in the header */
50 __bi_entries_start = .;
51 /* Here are the entries */
52 KEEP(*(.bi_entries));
53 /* Keep this block a nice round size */
54 . = ALIGN(4);
55 /* We put this in the header */
56 __bi_entries_end = .;
57 } > FLASH
58} INSERT AFTER .text;
59
60SECTIONS {
61 /* ### Boot ROM extra info
62 *
63 * Goes after everything in our program, so it can contain a signature.
64 */
65 .end_block : ALIGN(4)
66 {
67 __end_block_addr = .;
68 KEEP(*(.end_block));
69 } > FLASH
70
71} INSERT AFTER .uninit;
72
73PROVIDE(start_to_end = __end_block_addr - __start_block_addr);
74PROVIDE(end_to_start = __start_block_addr - __end_block_addr);
diff --git a/examples/rp23/src/bin/adc.rs b/examples/rp23/src/bin/adc.rs
new file mode 100644
index 000000000..19872607e
--- /dev/null
+++ b/examples/rp23/src/bin/adc.rs
@@ -0,0 +1,63 @@
1//! This example test the ADC (Analog to Digital Conversion) of the RS2040 pin 26, 27 and 28.
2//! It also reads the temperature sensor in the chip.
3
4#![no_std]
5#![no_main]
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler};
10use embassy_rp::bind_interrupts;
11use embassy_rp::block::ImageDef;
12use embassy_rp::gpio::Pull;
13use embassy_time::Timer;
14use {defmt_rtt as _, panic_probe as _};
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30bind_interrupts!(struct Irqs {
31 ADC_IRQ_FIFO => InterruptHandler;
32});
33
34#[embassy_executor::main]
35async fn main(_spawner: Spawner) {
36 let p = embassy_rp::init(Default::default());
37 let mut adc = Adc::new(p.ADC, Irqs, Config::default());
38
39 let mut p26 = Channel::new_pin(p.PIN_26, Pull::None);
40 let mut p27 = Channel::new_pin(p.PIN_27, Pull::None);
41 let mut p28 = Channel::new_pin(p.PIN_28, Pull::None);
42 let mut ts = Channel::new_temp_sensor(p.ADC_TEMP_SENSOR);
43
44 loop {
45 let level = adc.read(&mut p26).await.unwrap();
46 info!("Pin 26 ADC: {}", level);
47 let level = adc.read(&mut p27).await.unwrap();
48 info!("Pin 27 ADC: {}", level);
49 let level = adc.read(&mut p28).await.unwrap();
50 info!("Pin 28 ADC: {}", level);
51 let temp = adc.read(&mut ts).await.unwrap();
52 info!("Temp: {} degrees", convert_to_celsius(temp));
53 Timer::after_secs(1).await;
54 }
55}
56
57fn convert_to_celsius(raw_temp: u16) -> f32 {
58 // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet
59 let temp = 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721;
60 let sign = if temp < 0.0 { -1.0 } else { 1.0 };
61 let rounded_temp_x10: i16 = ((temp * 10.0) + 0.5 * sign) as i16;
62 (rounded_temp_x10 as f32) / 10.0
63}
diff --git a/examples/rp23/src/bin/adc_dma.rs b/examples/rp23/src/bin/adc_dma.rs
new file mode 100644
index 000000000..d538ddaa2
--- /dev/null
+++ b/examples/rp23/src/bin/adc_dma.rs
@@ -0,0 +1,69 @@
1//! This example shows how to use the RP2040 ADC with DMA, both single- and multichannel reads.
2//! For multichannel, the samples are interleaved in the buffer:
3//! `[ch1, ch2, ch3, ch4, ch1, ch2, ch3, ch4, ...]`
4#![no_std]
5#![no_main]
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler};
10use embassy_rp::bind_interrupts;
11use embassy_rp::block::ImageDef;
12use embassy_rp::gpio::Pull;
13use embassy_time::{Duration, Ticker};
14use {defmt_rtt as _, panic_probe as _};
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30bind_interrupts!(struct Irqs {
31 ADC_IRQ_FIFO => InterruptHandler;
32});
33
34#[embassy_executor::main]
35async fn main(_spawner: Spawner) {
36 let p = embassy_rp::init(Default::default());
37 info!("Here we go!");
38
39 let mut adc = Adc::new(p.ADC, Irqs, Config::default());
40 let mut dma = p.DMA_CH0;
41 let mut pin = Channel::new_pin(p.PIN_26, Pull::Up);
42 let mut pins = [
43 Channel::new_pin(p.PIN_27, Pull::Down),
44 Channel::new_pin(p.PIN_28, Pull::None),
45 Channel::new_pin(p.PIN_29, Pull::Up),
46 Channel::new_temp_sensor(p.ADC_TEMP_SENSOR),
47 ];
48
49 const BLOCK_SIZE: usize = 100;
50 const NUM_CHANNELS: usize = 4;
51 let mut ticker = Ticker::every(Duration::from_secs(1));
52 loop {
53 // Read 100 samples from a single channel
54 let mut buf = [0_u16; BLOCK_SIZE];
55 let div = 479; // 100kHz sample rate (48Mhz / 100kHz - 1)
56 adc.read_many(&mut pin, &mut buf, div, &mut dma).await.unwrap();
57 info!("single: {:?} ...etc", buf[..8]);
58
59 // Read 100 samples from 4 channels interleaved
60 let mut buf = [0_u16; { BLOCK_SIZE * NUM_CHANNELS }];
61 let div = 119; // 100kHz sample rate (48Mhz / 100kHz * 4ch - 1)
62 adc.read_many_multichannel(&mut pins, &mut buf, div, &mut dma)
63 .await
64 .unwrap();
65 info!("multi: {:?} ...etc", buf[..NUM_CHANNELS * 2]);
66
67 ticker.next().await;
68 }
69}
diff --git a/examples/rp23/src/bin/assign_resources.rs b/examples/rp23/src/bin/assign_resources.rs
new file mode 100644
index 000000000..923c13514
--- /dev/null
+++ b/examples/rp23/src/bin/assign_resources.rs
@@ -0,0 +1,94 @@
1//! This example demonstrates how to assign resources to multiple tasks by splitting up the peripherals.
2//! It is not about sharing the same resources between tasks, see sharing.rs for that or head to https://embassy.dev/book/#_sharing_peripherals_between_tasks)
3//! Of course splitting up resources and sharing resources can be combined, yet this example is only about splitting up resources.
4//!
5//! There are basically two ways we demonstrate here:
6//! 1) Assigning resources to a task by passing parts of the peripherals
7//! 2) Assigning resources to a task by passing a struct with the split up peripherals, using the assign-resources macro
8//!
9//! using four LEDs on Pins 10, 11, 20 and 21
10
11#![no_std]
12#![no_main]
13
14use assign_resources::assign_resources;
15use defmt::*;
16use embassy_executor::Spawner;
17use embassy_rp::block::ImageDef;
18use embassy_rp::gpio::{Level, Output};
19use embassy_rp::peripherals::{self, PIN_20, PIN_21};
20use embassy_time::Timer;
21use {defmt_rtt as _, panic_probe as _};
22
23#[link_section = ".start_block"]
24#[used]
25pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
26
27// Program metadata for `picotool info`
28#[link_section = ".bi_entries"]
29#[used]
30pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
31 embassy_rp::binary_info_rp_cargo_bin_name!(),
32 embassy_rp::binary_info_rp_cargo_version!(),
33 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
34 embassy_rp::binary_info_rp_program_build_attribute!(),
35];
36
37#[embassy_executor::main]
38async fn main(spawner: Spawner) {
39 // initialize the peripherals
40 let p = embassy_rp::init(Default::default());
41
42 // 1) Assigning a resource to a task by passing parts of the peripherals.
43 spawner
44 .spawn(double_blinky_manually_assigned(spawner, p.PIN_20, p.PIN_21))
45 .unwrap();
46
47 // 2) Using the assign-resources macro to assign resources to a task.
48 // we perform the split, see further below for the definition of the resources struct
49 let r = split_resources!(p);
50 // and then we can use them
51 spawner.spawn(double_blinky_macro_assigned(spawner, r.leds)).unwrap();
52}
53
54// 1) Assigning a resource to a task by passing parts of the peripherals.
55#[embassy_executor::task]
56async fn double_blinky_manually_assigned(_spawner: Spawner, pin_20: PIN_20, pin_21: PIN_21) {
57 let mut led_20 = Output::new(pin_20, Level::Low);
58 let mut led_21 = Output::new(pin_21, Level::High);
59
60 loop {
61 info!("toggling leds");
62 led_20.toggle();
63 led_21.toggle();
64 Timer::after_secs(1).await;
65 }
66}
67
68// 2) Using the assign-resources macro to assign resources to a task.
69// first we define the resources we want to assign to the task using the assign_resources! macro
70// basically this will split up the peripherals struct into smaller structs, that we define here
71// naming is up to you, make sure your future self understands what you did here
72assign_resources! {
73 leds: Leds{
74 led_10: PIN_10,
75 led_11: PIN_11,
76 }
77 // add more resources to more structs if needed, for example defining one struct for each task
78}
79// this could be done in another file and imported here, but for the sake of simplicity we do it here
80// see https://github.com/adamgreig/assign-resources for more information
81
82// 2) Using the split resources in a task
83#[embassy_executor::task]
84async fn double_blinky_macro_assigned(_spawner: Spawner, r: Leds) {
85 let mut led_10 = Output::new(r.led_10, Level::Low);
86 let mut led_11 = Output::new(r.led_11, Level::High);
87
88 loop {
89 info!("toggling leds");
90 led_10.toggle();
91 led_11.toggle();
92 Timer::after_secs(1).await;
93 }
94}
diff --git a/examples/rp23/src/bin/blinky.rs b/examples/rp23/src/bin/blinky.rs
new file mode 100644
index 000000000..02bdf9b3d
--- /dev/null
+++ b/examples/rp23/src/bin/blinky.rs
@@ -0,0 +1,44 @@
1//! This example test the RP Pico on board LED.
2//!
3//! It does not work with the RP Pico W board. See wifi_blinky.rs.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::gpio;
12use embassy_time::Timer;
13use gpio::{Level, Output};
14use {defmt_rtt as _, panic_probe as _};
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30#[embassy_executor::main]
31async fn main(_spawner: Spawner) {
32 let p = embassy_rp::init(Default::default());
33 let mut led = Output::new(p.PIN_2, Level::Low);
34
35 loop {
36 info!("led on!");
37 led.set_high();
38 Timer::after_millis(250).await;
39
40 info!("led off!");
41 led.set_low();
42 Timer::after_millis(250).await;
43 }
44}
diff --git a/examples/rp23/src/bin/blinky_two_channels.rs b/examples/rp23/src/bin/blinky_two_channels.rs
new file mode 100644
index 000000000..4d7dc89fa
--- /dev/null
+++ b/examples/rp23/src/bin/blinky_two_channels.rs
@@ -0,0 +1,65 @@
1#![no_std]
2#![no_main]
3/// This example demonstrates how to access a given pin from more than one embassy task
4/// The on-board LED is toggled by two tasks with slightly different periods, leading to the
5/// apparent duty cycle of the LED increasing, then decreasing, linearly. The phenomenon is similar
6/// to interference and the 'beats' you can hear if you play two frequencies close to one another
7/// [Link explaining it](https://www.physicsclassroom.com/class/sound/Lesson-3/Interference-and-Beats)
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::gpio;
12use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
13use embassy_sync::channel::{Channel, Sender};
14use embassy_time::{Duration, Ticker};
15use gpio::{AnyPin, Level, Output};
16use {defmt_rtt as _, panic_probe as _};
17
18#[link_section = ".start_block"]
19#[used]
20pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
21
22// Program metadata for `picotool info`
23#[link_section = ".bi_entries"]
24#[used]
25pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
26 embassy_rp::binary_info_rp_cargo_bin_name!(),
27 embassy_rp::binary_info_rp_cargo_version!(),
28 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
29 embassy_rp::binary_info_rp_program_build_attribute!(),
30];
31
32enum LedState {
33 Toggle,
34}
35static CHANNEL: Channel<ThreadModeRawMutex, LedState, 64> = Channel::new();
36
37#[embassy_executor::main]
38async fn main(spawner: Spawner) {
39 let p = embassy_rp::init(Default::default());
40 let mut led = Output::new(AnyPin::from(p.PIN_25), Level::High);
41
42 let dt = 100 * 1_000_000;
43 let k = 1.003;
44
45 unwrap!(spawner.spawn(toggle_led(CHANNEL.sender(), Duration::from_nanos(dt))));
46 unwrap!(spawner.spawn(toggle_led(
47 CHANNEL.sender(),
48 Duration::from_nanos((dt as f64 * k) as u64)
49 )));
50
51 loop {
52 match CHANNEL.receive().await {
53 LedState::Toggle => led.toggle(),
54 }
55 }
56}
57
58#[embassy_executor::task(pool_size = 2)]
59async fn toggle_led(control: Sender<'static, ThreadModeRawMutex, LedState, 64>, delay: Duration) {
60 let mut ticker = Ticker::every(delay);
61 loop {
62 control.send(LedState::Toggle).await;
63 ticker.next().await;
64 }
65}
diff --git a/examples/rp23/src/bin/blinky_two_tasks.rs b/examples/rp23/src/bin/blinky_two_tasks.rs
new file mode 100644
index 000000000..24b960242
--- /dev/null
+++ b/examples/rp23/src/bin/blinky_two_tasks.rs
@@ -0,0 +1,64 @@
1#![no_std]
2#![no_main]
3/// This example demonstrates how to access a given pin from more than one embassy task
4/// The on-board LED is toggled by two tasks with slightly different periods, leading to the
5/// apparent duty cycle of the LED increasing, then decreasing, linearly. The phenomenon is similar
6/// to interference and the 'beats' you can hear if you play two frequencies close to one another
7/// [Link explaining it](https://www.physicsclassroom.com/class/sound/Lesson-3/Interference-and-Beats)
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::gpio;
12use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
13use embassy_sync::mutex::Mutex;
14use embassy_time::{Duration, Ticker};
15use gpio::{AnyPin, Level, Output};
16use {defmt_rtt as _, panic_probe as _};
17
18#[link_section = ".start_block"]
19#[used]
20pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
21
22// Program metadata for `picotool info`
23#[link_section = ".bi_entries"]
24#[used]
25pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
26 embassy_rp::binary_info_rp_cargo_bin_name!(),
27 embassy_rp::binary_info_rp_cargo_version!(),
28 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
29 embassy_rp::binary_info_rp_program_build_attribute!(),
30];
31
32type LedType = Mutex<ThreadModeRawMutex, Option<Output<'static>>>;
33static LED: LedType = Mutex::new(None);
34
35#[embassy_executor::main]
36async fn main(spawner: Spawner) {
37 let p = embassy_rp::init(Default::default());
38 // set the content of the global LED reference to the real LED pin
39 let led = Output::new(AnyPin::from(p.PIN_25), Level::High);
40 // inner scope is so that once the mutex is written to, the MutexGuard is dropped, thus the
41 // Mutex is released
42 {
43 *(LED.lock().await) = Some(led);
44 }
45 let dt = 100 * 1_000_000;
46 let k = 1.003;
47
48 unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos(dt))));
49 unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos((dt as f64 * k) as u64))));
50}
51
52#[embassy_executor::task(pool_size = 2)]
53async fn toggle_led(led: &'static LedType, delay: Duration) {
54 let mut ticker = Ticker::every(delay);
55 loop {
56 {
57 let mut led_unlocked = led.lock().await;
58 if let Some(pin_ref) = led_unlocked.as_mut() {
59 pin_ref.toggle();
60 }
61 }
62 ticker.next().await;
63 }
64}
diff --git a/examples/rp23/src/bin/button.rs b/examples/rp23/src/bin/button.rs
new file mode 100644
index 000000000..0a0559397
--- /dev/null
+++ b/examples/rp23/src/bin/button.rs
@@ -0,0 +1,43 @@
1//! This example uses the RP Pico on board LED to test input pin 28. This is not the button on the board.
2//!
3//! It does not work with the RP Pico W board. Use wifi_blinky.rs and add input pin.
4
5#![no_std]
6#![no_main]
7
8use embassy_executor::Spawner;
9use embassy_rp::block::ImageDef;
10use embassy_rp::gpio::{Input, Level, Output, Pull};
11use {defmt_rtt as _, panic_probe as _};
12
13#[link_section = ".start_block"]
14#[used]
15pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
16
17// Program metadata for `picotool info`
18#[link_section = ".bi_entries"]
19#[used]
20pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
21 embassy_rp::binary_info_rp_cargo_bin_name!(),
22 embassy_rp::binary_info_rp_cargo_version!(),
23 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
24 embassy_rp::binary_info_rp_program_build_attribute!(),
25];
26
27#[embassy_executor::main]
28async fn main(_spawner: Spawner) {
29 let p = embassy_rp::init(Default::default());
30 let mut led = Output::new(p.PIN_25, Level::Low);
31
32 // Use PIN_28, Pin34 on J0 for RP Pico, as a input.
33 // You need to add your own button.
34 let button = Input::new(p.PIN_28, Pull::Up);
35
36 loop {
37 if button.is_high() {
38 led.set_high();
39 } else {
40 led.set_low();
41 }
42 }
43}
diff --git a/examples/rp23/src/bin/debounce.rs b/examples/rp23/src/bin/debounce.rs
new file mode 100644
index 000000000..e82e71f61
--- /dev/null
+++ b/examples/rp23/src/bin/debounce.rs
@@ -0,0 +1,95 @@
1//! This example shows the ease of debouncing a button with async rust.
2//! Hook up a button or switch between pin 9 and ground.
3
4#![no_std]
5#![no_main]
6
7use defmt::info;
8use embassy_executor::Spawner;
9use embassy_rp::block::ImageDef;
10use embassy_rp::gpio::{Input, Level, Pull};
11use embassy_time::{with_deadline, Duration, Instant, Timer};
12use {defmt_rtt as _, panic_probe as _};
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28pub struct Debouncer<'a> {
29 input: Input<'a>,
30 debounce: Duration,
31}
32
33impl<'a> Debouncer<'a> {
34 pub fn new(input: Input<'a>, debounce: Duration) -> Self {
35 Self { input, debounce }
36 }
37
38 pub async fn debounce(&mut self) -> Level {
39 loop {
40 let l1 = self.input.get_level();
41
42 self.input.wait_for_any_edge().await;
43
44 Timer::after(self.debounce).await;
45
46 let l2 = self.input.get_level();
47 if l1 != l2 {
48 break l2;
49 }
50 }
51 }
52}
53
54#[embassy_executor::main]
55async fn main(_spawner: Spawner) {
56 let p = embassy_rp::init(Default::default());
57 let mut btn = Debouncer::new(Input::new(p.PIN_9, Pull::Up), Duration::from_millis(20));
58
59 info!("Debounce Demo");
60
61 loop {
62 // button pressed
63 btn.debounce().await;
64 let start = Instant::now();
65 info!("Button Press");
66
67 match with_deadline(start + Duration::from_secs(1), btn.debounce()).await {
68 // Button Released < 1s
69 Ok(_) => {
70 info!("Button pressed for: {}ms", start.elapsed().as_millis());
71 continue;
72 }
73 // button held for > 1s
74 Err(_) => {
75 info!("Button Held");
76 }
77 }
78
79 match with_deadline(start + Duration::from_secs(5), btn.debounce()).await {
80 // Button released <5s
81 Ok(_) => {
82 info!("Button pressed for: {}ms", start.elapsed().as_millis());
83 continue;
84 }
85 // button held for > >5s
86 Err(_) => {
87 info!("Button Long Held");
88 }
89 }
90
91 // wait for button release before handling another press
92 btn.debounce().await;
93 info!("Button pressed for: {}ms", start.elapsed().as_millis());
94 }
95}
diff --git a/examples/rp23/src/bin/flash.rs b/examples/rp23/src/bin/flash.rs
new file mode 100644
index 000000000..2917dda0b
--- /dev/null
+++ b/examples/rp23/src/bin/flash.rs
@@ -0,0 +1,149 @@
1//! This example test the flash connected to the RP2040 chip.
2
3#![no_std]
4#![no_main]
5
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_rp::block::ImageDef;
9use embassy_rp::flash::{Async, ERASE_SIZE, FLASH_BASE};
10use embassy_rp::peripherals::FLASH;
11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _};
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28const ADDR_OFFSET: u32 = 0x100000;
29const FLASH_SIZE: usize = 2 * 1024 * 1024;
30
31#[embassy_executor::main]
32async fn main(_spawner: Spawner) {
33 let p = embassy_rp::init(Default::default());
34 info!("Hello World!");
35
36 // add some delay to give an attached debug probe time to parse the
37 // defmt RTT header. Reading that header might touch flash memory, which
38 // interferes with flash write operations.
39 // https://github.com/knurling-rs/defmt/pull/683
40 Timer::after_millis(10).await;
41
42 let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0);
43
44 // Get JEDEC id
45 let jedec = flash.blocking_jedec_id().unwrap();
46 info!("jedec id: 0x{:x}", jedec);
47
48 // Get unique id
49 let mut uid = [0; 8];
50 flash.blocking_unique_id(&mut uid).unwrap();
51 info!("unique id: {:?}", uid);
52
53 erase_write_sector(&mut flash, 0x00);
54
55 multiwrite_bytes(&mut flash, ERASE_SIZE as u32);
56
57 background_read(&mut flash, (ERASE_SIZE * 2) as u32).await;
58
59 loop {}
60}
61
62fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
63 info!(">>>> [multiwrite_bytes]");
64 let mut read_buf = [0u8; ERASE_SIZE];
65 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
66
67 info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
68 info!("Contents start with {=[u8]}", read_buf[0..4]);
69
70 defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
71
72 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
73 info!("Contents after erase starts with {=[u8]}", read_buf[0..4]);
74 if read_buf.iter().any(|x| *x != 0xFF) {
75 defmt::panic!("unexpected");
76 }
77
78 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &[0x01]));
79 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 1, &[0x02]));
80 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 2, &[0x03]));
81 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 3, &[0x04]));
82
83 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
84 info!("Contents after write starts with {=[u8]}", read_buf[0..4]);
85 if &read_buf[0..4] != &[0x01, 0x02, 0x03, 0x04] {
86 defmt::panic!("unexpected");
87 }
88}
89
90fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
91 info!(">>>> [erase_write_sector]");
92 let mut buf = [0u8; ERASE_SIZE];
93 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));
94
95 info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
96 info!("Contents start with {=[u8]}", buf[0..4]);
97
98 defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
99
100 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));
101 info!("Contents after erase starts with {=[u8]}", buf[0..4]);
102 if buf.iter().any(|x| *x != 0xFF) {
103 defmt::panic!("unexpected");
104 }
105
106 for b in buf.iter_mut() {
107 *b = 0xDA;
108 }
109
110 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &buf));
111
112 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));
113 info!("Contents after write starts with {=[u8]}", buf[0..4]);
114 if buf.iter().any(|x| *x != 0xDA) {
115 defmt::panic!("unexpected");
116 }
117}
118
119async fn background_read(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
120 info!(">>>> [background_read]");
121
122 let mut buf = [0u32; 8];
123 defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
124
125 info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
126 info!("Contents start with {=u32:x}", buf[0]);
127
128 defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
129
130 defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
131 info!("Contents after erase starts with {=u32:x}", buf[0]);
132 if buf.iter().any(|x| *x != 0xFFFFFFFF) {
133 defmt::panic!("unexpected");
134 }
135
136 for b in buf.iter_mut() {
137 *b = 0xDABA1234;
138 }
139
140 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, unsafe {
141 core::slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len() * 4)
142 }));
143
144 defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
145 info!("Contents after write starts with {=u32:x}", buf[0]);
146 if buf.iter().any(|x| *x != 0xDABA1234) {
147 defmt::panic!("unexpected");
148 }
149}
diff --git a/examples/rp23/src/bin/gpio_async.rs b/examples/rp23/src/bin/gpio_async.rs
new file mode 100644
index 000000000..1618f7c8b
--- /dev/null
+++ b/examples/rp23/src/bin/gpio_async.rs
@@ -0,0 +1,55 @@
1//! This example shows how async gpio can be used with a RP2040.
2//!
3//! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::gpio;
12use embassy_time::Timer;
13use gpio::{Input, Level, Output, Pull};
14use {defmt_rtt as _, panic_probe as _};
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30/// It requires an external signal to be manually triggered on PIN 16. For
31/// example, this could be accomplished using an external power source with a
32/// button so that it is possible to toggle the signal from low to high.
33///
34/// This example will begin with turning on the LED on the board and wait for a
35/// high signal on PIN 16. Once the high event/signal occurs the program will
36/// continue and turn off the LED, and then wait for 2 seconds before completing
37/// the loop and starting over again.
38#[embassy_executor::main]
39async fn main(_spawner: Spawner) {
40 let p = embassy_rp::init(Default::default());
41 let mut led = Output::new(p.PIN_25, Level::Low);
42 let mut async_input = Input::new(p.PIN_16, Pull::None);
43
44 loop {
45 info!("wait_for_high. Turn on LED");
46 led.set_high();
47
48 async_input.wait_for_high().await;
49
50 info!("done wait_for_high. Turn off LED");
51 led.set_low();
52
53 Timer::after_secs(2).await;
54 }
55}
diff --git a/examples/rp23/src/bin/gpout.rs b/examples/rp23/src/bin/gpout.rs
new file mode 100644
index 000000000..b15963f02
--- /dev/null
+++ b/examples/rp23/src/bin/gpout.rs
@@ -0,0 +1,52 @@
1//! This example shows how GPOUT (General purpose clock outputs) can toggle a output pin.
2//!
3//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::clocks;
12use embassy_time::Timer;
13use {defmt_rtt as _, panic_probe as _};
14
15#[link_section = ".start_block"]
16#[used]
17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
18
19// Program metadata for `picotool info`
20#[link_section = ".bi_entries"]
21#[used]
22pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
23 embassy_rp::binary_info_rp_cargo_bin_name!(),
24 embassy_rp::binary_info_rp_cargo_version!(),
25 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
26 embassy_rp::binary_info_rp_program_build_attribute!(),
27];
28
29#[embassy_executor::main]
30async fn main(_spawner: Spawner) {
31 let p = embassy_rp::init(Default::default());
32
33 let gpout3 = clocks::Gpout::new(p.PIN_25);
34 gpout3.set_div(1000, 0);
35 gpout3.enable();
36
37 loop {
38 gpout3.set_src(clocks::GpoutSrc::Sys);
39 info!(
40 "Pin 25 is now outputing CLK_SYS/1000, should be toggling at {}",
41 gpout3.get_freq()
42 );
43 Timer::after_secs(2).await;
44
45 gpout3.set_src(clocks::GpoutSrc::Ref);
46 info!(
47 "Pin 25 is now outputing CLK_REF/1000, should be toggling at {}",
48 gpout3.get_freq()
49 );
50 Timer::after_secs(2).await;
51 }
52}
diff --git a/examples/rp23/src/bin/i2c_async.rs b/examples/rp23/src/bin/i2c_async.rs
new file mode 100644
index 000000000..2528fe1d2
--- /dev/null
+++ b/examples/rp23/src/bin/i2c_async.rs
@@ -0,0 +1,125 @@
1//! This example shows how to communicate asynchronous using i2c with external chips.
2//!
3//! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip.
4//! (https://www.microchip.com/en-us/product/mcp23017)
5
6#![no_std]
7#![no_main]
8
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_rp::bind_interrupts;
12use embassy_rp::block::ImageDef;
13use embassy_rp::i2c::{self, Config, InterruptHandler};
14use embassy_rp::peripherals::I2C1;
15use embassy_time::Timer;
16use embedded_hal_async::i2c::I2c;
17use {defmt_rtt as _, panic_probe as _};
18
19#[link_section = ".start_block"]
20#[used]
21pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
22
23// Program metadata for `picotool info`
24#[link_section = ".bi_entries"]
25#[used]
26pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
27 embassy_rp::binary_info_rp_cargo_bin_name!(),
28 embassy_rp::binary_info_rp_cargo_version!(),
29 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
30 embassy_rp::binary_info_rp_program_build_attribute!(),
31];
32
33bind_interrupts!(struct Irqs {
34 I2C1_IRQ => InterruptHandler<I2C1>;
35});
36
37#[allow(dead_code)]
38mod mcp23017 {
39 pub const ADDR: u8 = 0x20; // default addr
40
41 macro_rules! mcpregs {
42 ($($name:ident : $val:expr),* $(,)?) => {
43 $(
44 pub const $name: u8 = $val;
45 )*
46
47 pub fn regname(reg: u8) -> &'static str {
48 match reg {
49 $(
50 $val => stringify!($name),
51 )*
52 _ => panic!("bad reg"),
53 }
54 }
55 }
56 }
57
58 // These are correct for IOCON.BANK=0
59 mcpregs! {
60 IODIRA: 0x00,
61 IPOLA: 0x02,
62 GPINTENA: 0x04,
63 DEFVALA: 0x06,
64 INTCONA: 0x08,
65 IOCONA: 0x0A,
66 GPPUA: 0x0C,
67 INTFA: 0x0E,
68 INTCAPA: 0x10,
69 GPIOA: 0x12,
70 OLATA: 0x14,
71 IODIRB: 0x01,
72 IPOLB: 0x03,
73 GPINTENB: 0x05,
74 DEFVALB: 0x07,
75 INTCONB: 0x09,
76 IOCONB: 0x0B,
77 GPPUB: 0x0D,
78 INTFB: 0x0F,
79 INTCAPB: 0x11,
80 GPIOB: 0x13,
81 OLATB: 0x15,
82 }
83}
84
85#[embassy_executor::main]
86async fn main(_spawner: Spawner) {
87 let p = embassy_rp::init(Default::default());
88
89 let sda = p.PIN_14;
90 let scl = p.PIN_15;
91
92 info!("set up i2c ");
93 let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, Config::default());
94
95 use mcp23017::*;
96
97 info!("init mcp23017 config for IxpandO");
98 // init - a outputs, b inputs
99 i2c.write(ADDR, &[IODIRA, 0x00]).await.unwrap();
100 i2c.write(ADDR, &[IODIRB, 0xff]).await.unwrap();
101 i2c.write(ADDR, &[GPPUB, 0xff]).await.unwrap(); // pullups
102
103 let mut val = 1;
104 loop {
105 let mut portb = [0];
106
107 i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).await.unwrap();
108 info!("portb = {:02x}", portb[0]);
109 i2c.write(mcp23017::ADDR, &[GPIOA, val | portb[0]]).await.unwrap();
110 val = val.rotate_left(1);
111
112 // get a register dump
113 info!("getting register dump");
114 let mut regs = [0; 22];
115 i2c.write_read(ADDR, &[0], &mut regs).await.unwrap();
116 // always get the regdump but only display it if portb'0 is set
117 if portb[0] & 1 != 0 {
118 for (idx, reg) in regs.into_iter().enumerate() {
119 info!("{} => {:02x}", regname(idx as u8), reg);
120 }
121 }
122
123 Timer::after_millis(100).await;
124 }
125}
diff --git a/examples/rp23/src/bin/i2c_async_embassy.rs b/examples/rp23/src/bin/i2c_async_embassy.rs
new file mode 100644
index 000000000..461b1d171
--- /dev/null
+++ b/examples/rp23/src/bin/i2c_async_embassy.rs
@@ -0,0 +1,100 @@
1//! This example shows how to communicate asynchronous using i2c with external chip.
2//!
3//! It's using embassy's functions directly instead of traits from embedded_hal_async::i2c::I2c.
4//! While most of i2c devices are addressed using 7 bits, an extension allows 10 bits too.
5
6#![no_std]
7#![no_main]
8
9use defmt::*;
10use embassy_rp::block::ImageDef;
11use embassy_rp::i2c::InterruptHandler;
12use {defmt_rtt as _, panic_probe as _};
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28// Our anonymous hypotetical temperature sensor could be:
29// a 12-bit sensor, with 100ms startup time, range of -40*C - 125*C, and precision 0.25*C
30// It requires no configuration or calibration, works with all i2c bus speeds,
31// never stretches clock or does anything complicated. Replies with one u16.
32// It requires only one write to take it out of suspend mode, and stays on.
33// Often result would be just on 12 bits, but here we'll simplify it to 16.
34
35enum UncomplicatedSensorId {
36 A(UncomplicatedSensorU8),
37 B(UncomplicatedSensorU16),
38}
39enum UncomplicatedSensorU8 {
40 First = 0x48,
41}
42enum UncomplicatedSensorU16 {
43 Other = 0x0049,
44}
45
46impl Into<u16> for UncomplicatedSensorU16 {
47 fn into(self) -> u16 {
48 self as u16
49 }
50}
51impl Into<u16> for UncomplicatedSensorU8 {
52 fn into(self) -> u16 {
53 0x48
54 }
55}
56impl From<UncomplicatedSensorId> for u16 {
57 fn from(t: UncomplicatedSensorId) -> Self {
58 match t {
59 UncomplicatedSensorId::A(x) => x.into(),
60 UncomplicatedSensorId::B(x) => x.into(),
61 }
62 }
63}
64
65embassy_rp::bind_interrupts!(struct Irqs {
66 I2C1_IRQ => InterruptHandler<embassy_rp::peripherals::I2C1>;
67});
68
69#[embassy_executor::main]
70async fn main(_task_spawner: embassy_executor::Spawner) {
71 let p = embassy_rp::init(Default::default());
72 let sda = p.PIN_14;
73 let scl = p.PIN_15;
74 let config = embassy_rp::i2c::Config::default();
75 let mut bus = embassy_rp::i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, config);
76
77 const WAKEYWAKEY: u16 = 0xBABE;
78 let mut result: [u8; 2] = [0, 0];
79 // wait for sensors to initialize
80 embassy_time::Timer::after(embassy_time::Duration::from_millis(100)).await;
81
82 let _res_1 = bus
83 .write_async(UncomplicatedSensorU8::First, WAKEYWAKEY.to_be_bytes())
84 .await;
85 let _res_2 = bus
86 .write_async(UncomplicatedSensorU16::Other, WAKEYWAKEY.to_be_bytes())
87 .await;
88
89 loop {
90 let s1 = UncomplicatedSensorId::A(UncomplicatedSensorU8::First);
91 let s2 = UncomplicatedSensorId::B(UncomplicatedSensorU16::Other);
92 let sensors = [s1, s2];
93 for sensor in sensors {
94 if bus.read_async(sensor, &mut result).await.is_ok() {
95 info!("Result {}", u16::from_be_bytes(result.into()));
96 }
97 }
98 embassy_time::Timer::after(embassy_time::Duration::from_millis(200)).await;
99 }
100}
diff --git a/examples/rp23/src/bin/i2c_blocking.rs b/examples/rp23/src/bin/i2c_blocking.rs
new file mode 100644
index 000000000..6d36d1890
--- /dev/null
+++ b/examples/rp23/src/bin/i2c_blocking.rs
@@ -0,0 +1,89 @@
1//! This example shows how to communicate using i2c with external chips.
2//!
3//! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip.
4//! (https://www.microchip.com/en-us/product/mcp23017)
5
6#![no_std]
7#![no_main]
8
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_rp::block::ImageDef;
12use embassy_rp::i2c::{self, Config};
13use embassy_time::Timer;
14use embedded_hal_1::i2c::I2c;
15use {defmt_rtt as _, panic_probe as _};
16
17#[link_section = ".start_block"]
18#[used]
19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
20
21// Program metadata for `picotool info`
22#[link_section = ".bi_entries"]
23#[used]
24pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
25 embassy_rp::binary_info_rp_cargo_bin_name!(),
26 embassy_rp::binary_info_rp_cargo_version!(),
27 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
28 embassy_rp::binary_info_rp_program_build_attribute!(),
29];
30
31#[allow(dead_code)]
32mod mcp23017 {
33 pub const ADDR: u8 = 0x20; // default addr
34
35 pub const IODIRA: u8 = 0x00;
36 pub const IPOLA: u8 = 0x02;
37 pub const GPINTENA: u8 = 0x04;
38 pub const DEFVALA: u8 = 0x06;
39 pub const INTCONA: u8 = 0x08;
40 pub const IOCONA: u8 = 0x0A;
41 pub const GPPUA: u8 = 0x0C;
42 pub const INTFA: u8 = 0x0E;
43 pub const INTCAPA: u8 = 0x10;
44 pub const GPIOA: u8 = 0x12;
45 pub const OLATA: u8 = 0x14;
46 pub const IODIRB: u8 = 0x01;
47 pub const IPOLB: u8 = 0x03;
48 pub const GPINTENB: u8 = 0x05;
49 pub const DEFVALB: u8 = 0x07;
50 pub const INTCONB: u8 = 0x09;
51 pub const IOCONB: u8 = 0x0B;
52 pub const GPPUB: u8 = 0x0D;
53 pub const INTFB: u8 = 0x0F;
54 pub const INTCAPB: u8 = 0x11;
55 pub const GPIOB: u8 = 0x13;
56 pub const OLATB: u8 = 0x15;
57}
58
59#[embassy_executor::main]
60async fn main(_spawner: Spawner) {
61 let p = embassy_rp::init(Default::default());
62
63 let sda = p.PIN_14;
64 let scl = p.PIN_15;
65
66 info!("set up i2c ");
67 let mut i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default());
68
69 use mcp23017::*;
70
71 info!("init mcp23017 config for IxpandO");
72 // init - a outputs, b inputs
73 i2c.write(ADDR, &[IODIRA, 0x00]).unwrap();
74 i2c.write(ADDR, &[IODIRB, 0xff]).unwrap();
75 i2c.write(ADDR, &[GPPUB, 0xff]).unwrap(); // pullups
76
77 let mut val = 0xaa;
78 loop {
79 let mut portb = [0];
80
81 i2c.write(mcp23017::ADDR, &[GPIOA, val]).unwrap();
82 i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).unwrap();
83
84 info!("portb = {:02x}", portb[0]);
85 val = !val;
86
87 Timer::after_secs(1).await;
88 }
89}
diff --git a/examples/rp23/src/bin/i2c_slave.rs b/examples/rp23/src/bin/i2c_slave.rs
new file mode 100644
index 000000000..1f3408cf3
--- /dev/null
+++ b/examples/rp23/src/bin/i2c_slave.rs
@@ -0,0 +1,132 @@
1//! This example shows how to use the 2040 as an i2c slave.
2#![no_std]
3#![no_main]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_rp::block::ImageDef;
8use embassy_rp::peripherals::{I2C0, I2C1};
9use embassy_rp::{bind_interrupts, i2c, i2c_slave};
10use embassy_time::Timer;
11use embedded_hal_async::i2c::I2c;
12use {defmt_rtt as _, panic_probe as _};
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28bind_interrupts!(struct Irqs {
29 I2C0_IRQ => i2c::InterruptHandler<I2C0>;
30 I2C1_IRQ => i2c::InterruptHandler<I2C1>;
31});
32
33const DEV_ADDR: u8 = 0x42;
34
35#[embassy_executor::task]
36async fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! {
37 info!("Device start");
38
39 let mut state = 0;
40
41 loop {
42 let mut buf = [0u8; 128];
43 match dev.listen(&mut buf).await {
44 Ok(i2c_slave::Command::GeneralCall(len)) => info!("Device received general call write: {}", buf[..len]),
45 Ok(i2c_slave::Command::Read) => loop {
46 match dev.respond_to_read(&[state]).await {
47 Ok(x) => match x {
48 i2c_slave::ReadStatus::Done => break,
49 i2c_slave::ReadStatus::NeedMoreBytes => (),
50 i2c_slave::ReadStatus::LeftoverBytes(x) => {
51 info!("tried to write {} extra bytes", x);
52 break;
53 }
54 },
55 Err(e) => error!("error while responding {}", e),
56 }
57 },
58 Ok(i2c_slave::Command::Write(len)) => info!("Device received write: {}", buf[..len]),
59 Ok(i2c_slave::Command::WriteRead(len)) => {
60 info!("device received write read: {:x}", buf[..len]);
61 match buf[0] {
62 // Set the state
63 0xC2 => {
64 state = buf[1];
65 match dev.respond_and_fill(&[state], 0x00).await {
66 Ok(read_status) => info!("response read status {}", read_status),
67 Err(e) => error!("error while responding {}", e),
68 }
69 }
70 // Reset State
71 0xC8 => {
72 state = 0;
73 match dev.respond_and_fill(&[state], 0x00).await {
74 Ok(read_status) => info!("response read status {}", read_status),
75 Err(e) => error!("error while responding {}", e),
76 }
77 }
78 x => error!("Invalid Write Read {:x}", x),
79 }
80 }
81 Err(e) => error!("{}", e),
82 }
83 }
84}
85
86#[embassy_executor::task]
87async fn controller_task(mut con: i2c::I2c<'static, I2C0, i2c::Async>) {
88 info!("Controller start");
89
90 loop {
91 let mut resp_buff = [0u8; 2];
92 for i in 0..10 {
93 match con.write_read(DEV_ADDR, &[0xC2, i], &mut resp_buff).await {
94 Ok(_) => info!("write_read response: {}", resp_buff),
95 Err(e) => error!("Error writing {}", e),
96 }
97
98 Timer::after_millis(100).await;
99 }
100 match con.read(DEV_ADDR, &mut resp_buff).await {
101 Ok(_) => info!("read response: {}", resp_buff),
102 Err(e) => error!("Error writing {}", e),
103 }
104 match con.write_read(DEV_ADDR, &[0xC8], &mut resp_buff).await {
105 Ok(_) => info!("write_read response: {}", resp_buff),
106 Err(e) => error!("Error writing {}", e),
107 }
108 Timer::after_millis(100).await;
109 }
110}
111
112#[embassy_executor::main]
113async fn main(spawner: Spawner) {
114 let p = embassy_rp::init(Default::default());
115 info!("Hello World!");
116
117 let d_sda = p.PIN_3;
118 let d_scl = p.PIN_2;
119 let mut config = i2c_slave::Config::default();
120 config.addr = DEV_ADDR as u16;
121 let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config);
122
123 unwrap!(spawner.spawn(device_task(device)));
124
125 let c_sda = p.PIN_1;
126 let c_scl = p.PIN_0;
127 let mut config = i2c::Config::default();
128 config.frequency = 1_000_000;
129 let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config);
130
131 unwrap!(spawner.spawn(controller_task(controller)));
132}
diff --git a/examples/rp23/src/bin/interrupt.rs b/examples/rp23/src/bin/interrupt.rs
new file mode 100644
index 000000000..6184b1bd7
--- /dev/null
+++ b/examples/rp23/src/bin/interrupt.rs
@@ -0,0 +1,109 @@
1//! This example shows how you can use raw interrupt handlers alongside embassy.
2//! The example also showcases some of the options available for sharing resources/data.
3//!
4//! In the example, an ADC reading is triggered every time the PWM wraps around.
5//! The sample data is sent down a channel, to be processed inside a low priority task.
6//! The processed data is then used to adjust the PWM duty cycle, once every second.
7
8#![no_std]
9#![no_main]
10
11use core::cell::{Cell, RefCell};
12
13use defmt::*;
14use embassy_executor::Spawner;
15use embassy_rp::adc::{self, Adc, Blocking};
16use embassy_rp::block::ImageDef;
17use embassy_rp::gpio::Pull;
18use embassy_rp::interrupt;
19use embassy_rp::pwm::{Config, Pwm};
20use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
21use embassy_sync::blocking_mutex::Mutex;
22use embassy_sync::channel::Channel;
23use embassy_time::{Duration, Ticker};
24use portable_atomic::{AtomicU32, Ordering};
25use static_cell::StaticCell;
26use {defmt_rtt as _, panic_probe as _};
27
28#[link_section = ".start_block"]
29#[used]
30pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
31
32// Program metadata for `picotool info`
33#[link_section = ".bi_entries"]
34#[used]
35pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
36 embassy_rp::binary_info_rp_cargo_bin_name!(),
37 embassy_rp::binary_info_rp_cargo_version!(),
38 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
39 embassy_rp::binary_info_rp_program_build_attribute!(),
40];
41
42static COUNTER: AtomicU32 = AtomicU32::new(0);
43static PWM: Mutex<CriticalSectionRawMutex, RefCell<Option<Pwm>>> = Mutex::new(RefCell::new(None));
44static ADC: Mutex<CriticalSectionRawMutex, RefCell<Option<(Adc<Blocking>, adc::Channel)>>> =
45 Mutex::new(RefCell::new(None));
46static ADC_VALUES: Channel<CriticalSectionRawMutex, u16, 2048> = Channel::new();
47
48#[embassy_executor::main]
49async fn main(spawner: Spawner) {
50 embassy_rp::pac::SIO.spinlock(31).write_value(1);
51 let p = embassy_rp::init(Default::default());
52
53 let adc = Adc::new_blocking(p.ADC, Default::default());
54 let p26 = adc::Channel::new_pin(p.PIN_26, Pull::None);
55 ADC.lock(|a| a.borrow_mut().replace((adc, p26)));
56
57 let pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, Default::default());
58 PWM.lock(|p| p.borrow_mut().replace(pwm));
59
60 // Enable the interrupt for pwm slice 4
61 embassy_rp::pac::PWM.irq0_inte().modify(|w| w.set_ch4(true));
62 unsafe {
63 cortex_m::peripheral::NVIC::unmask(interrupt::PWM_IRQ_WRAP_0);
64 }
65
66 // Tasks require their resources to have 'static lifetime
67 // No Mutex needed when sharing within the same executor/prio level
68 static AVG: StaticCell<Cell<u32>> = StaticCell::new();
69 let avg = AVG.init(Default::default());
70 spawner.must_spawn(processing(avg));
71
72 let mut ticker = Ticker::every(Duration::from_secs(1));
73 loop {
74 ticker.next().await;
75 let freq = COUNTER.swap(0, Ordering::Relaxed);
76 info!("pwm freq: {:?} Hz", freq);
77 info!("adc average: {:?}", avg.get());
78
79 // Update the pwm duty cycle, based on the averaged adc reading
80 let mut config = Config::default();
81 config.compare_b = ((avg.get() as f32 / 4095.0) * config.top as f32) as _;
82 PWM.lock(|p| p.borrow_mut().as_mut().unwrap().set_config(&config));
83 }
84}
85
86#[embassy_executor::task]
87async fn processing(avg: &'static Cell<u32>) {
88 let mut buffer: heapless::HistoryBuffer<u16, 100> = Default::default();
89 loop {
90 let val = ADC_VALUES.receive().await;
91 buffer.write(val);
92 let sum: u32 = buffer.iter().map(|x| *x as u32).sum();
93 avg.set(sum / buffer.len() as u32);
94 }
95}
96
97#[interrupt]
98fn PWM_IRQ_WRAP_0() {
99 critical_section::with(|cs| {
100 let mut adc = ADC.borrow(cs).borrow_mut();
101 let (adc, p26) = adc.as_mut().unwrap();
102 let val = adc.blocking_read(p26).unwrap();
103 ADC_VALUES.try_send(val).ok();
104
105 // Clear the interrupt, so we don't immediately re-enter this irq handler
106 PWM.borrow(cs).borrow_mut().as_mut().unwrap().clear_wrapped();
107 });
108 COUNTER.fetch_add(1, Ordering::Relaxed);
109}
diff --git a/examples/rp23/src/bin/multicore.rs b/examples/rp23/src/bin/multicore.rs
new file mode 100644
index 000000000..8649143e1
--- /dev/null
+++ b/examples/rp23/src/bin/multicore.rs
@@ -0,0 +1,81 @@
1//! This example shows how to send messages between the two cores in the RP2040 chip.
2//!
3//! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Executor;
10use embassy_rp::block::ImageDef;
11use embassy_rp::gpio::{Level, Output};
12use embassy_rp::multicore::{spawn_core1, Stack};
13use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
14use embassy_sync::channel::Channel;
15use embassy_time::Timer;
16use static_cell::StaticCell;
17use {defmt_rtt as _, panic_probe as _};
18
19#[link_section = ".start_block"]
20#[used]
21pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
22
23// Program metadata for `picotool info`
24#[link_section = ".bi_entries"]
25#[used]
26pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
27 embassy_rp::binary_info_rp_cargo_bin_name!(),
28 embassy_rp::binary_info_rp_cargo_version!(),
29 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
30 embassy_rp::binary_info_rp_program_build_attribute!(),
31];
32
33static mut CORE1_STACK: Stack<4096> = Stack::new();
34static EXECUTOR0: StaticCell<Executor> = StaticCell::new();
35static EXECUTOR1: StaticCell<Executor> = StaticCell::new();
36static CHANNEL: Channel<CriticalSectionRawMutex, LedState, 1> = Channel::new();
37
38enum LedState {
39 On,
40 Off,
41}
42
43#[cortex_m_rt::entry]
44fn main() -> ! {
45 let p = embassy_rp::init(Default::default());
46 let led = Output::new(p.PIN_25, Level::Low);
47
48 spawn_core1(
49 p.CORE1,
50 unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) },
51 move || {
52 let executor1 = EXECUTOR1.init(Executor::new());
53 executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led))));
54 },
55 );
56
57 let executor0 = EXECUTOR0.init(Executor::new());
58 executor0.run(|spawner| unwrap!(spawner.spawn(core0_task())));
59}
60
61#[embassy_executor::task]
62async fn core0_task() {
63 info!("Hello from core 0");
64 loop {
65 CHANNEL.send(LedState::On).await;
66 Timer::after_millis(100).await;
67 CHANNEL.send(LedState::Off).await;
68 Timer::after_millis(400).await;
69 }
70}
71
72#[embassy_executor::task]
73async fn core1_task(mut led: Output<'static>) {
74 info!("Hello from core 1");
75 loop {
76 match CHANNEL.receive().await {
77 LedState::On => led.set_high(),
78 LedState::Off => led.set_low(),
79 }
80 }
81}
diff --git a/examples/rp23/src/bin/multiprio.rs b/examples/rp23/src/bin/multiprio.rs
new file mode 100644
index 000000000..7590fb431
--- /dev/null
+++ b/examples/rp23/src/bin/multiprio.rs
@@ -0,0 +1,160 @@
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
59use cortex_m_rt::entry;
60use defmt::{info, unwrap};
61use embassy_executor::{Executor, InterruptExecutor};
62use embassy_rp::block::ImageDef;
63use embassy_rp::interrupt;
64use embassy_rp::interrupt::{InterruptExt, Priority};
65use embassy_time::{Instant, Timer, TICK_HZ};
66use static_cell::StaticCell;
67use {defmt_rtt as _, panic_probe as _};
68
69#[link_section = ".start_block"]
70#[used]
71pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
72
73// Program metadata for `picotool info`
74#[link_section = ".bi_entries"]
75#[used]
76pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
77 embassy_rp::binary_info_rp_cargo_bin_name!(),
78 embassy_rp::binary_info_rp_cargo_version!(),
79 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
80 embassy_rp::binary_info_rp_program_build_attribute!(),
81];
82
83#[embassy_executor::task]
84async fn run_high() {
85 loop {
86 info!(" [high] tick!");
87 Timer::after_ticks(673740).await;
88 }
89}
90
91#[embassy_executor::task]
92async fn run_med() {
93 loop {
94 let start = Instant::now();
95 info!(" [med] Starting long computation");
96
97 // Spin-wait to simulate a long CPU computation
98 embassy_time::block_for(embassy_time::Duration::from_secs(1)); // ~1 second
99
100 let end = Instant::now();
101 let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ;
102 info!(" [med] done in {} ms", ms);
103
104 Timer::after_ticks(53421).await;
105 }
106}
107
108#[embassy_executor::task]
109async fn run_low() {
110 loop {
111 let start = Instant::now();
112 info!("[low] Starting long computation");
113
114 // Spin-wait to simulate a long CPU computation
115 embassy_time::block_for(embassy_time::Duration::from_secs(2)); // ~2 seconds
116
117 let end = Instant::now();
118 let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ;
119 info!("[low] done in {} ms", ms);
120
121 Timer::after_ticks(82983).await;
122 }
123}
124
125static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new();
126static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new();
127static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();
128
129#[interrupt]
130unsafe fn SWI_IRQ_1() {
131 EXECUTOR_HIGH.on_interrupt()
132}
133
134#[interrupt]
135unsafe fn SWI_IRQ_0() {
136 EXECUTOR_MED.on_interrupt()
137}
138
139#[entry]
140fn main() -> ! {
141 info!("Hello World!");
142
143 let _p = embassy_rp::init(Default::default());
144
145 // High-priority executor: SWI_IRQ_1, priority level 2
146 interrupt::SWI_IRQ_1.set_priority(Priority::P2);
147 let spawner = EXECUTOR_HIGH.start(interrupt::SWI_IRQ_1);
148 unwrap!(spawner.spawn(run_high()));
149
150 // Medium-priority executor: SWI_IRQ_0, priority level 3
151 interrupt::SWI_IRQ_0.set_priority(Priority::P3);
152 let spawner = EXECUTOR_MED.start(interrupt::SWI_IRQ_0);
153 unwrap!(spawner.spawn(run_med()));
154
155 // Low priority executor: runs in thread mode, using WFE/SEV
156 let executor = EXECUTOR_LOW.init(Executor::new());
157 executor.run(|spawner| {
158 unwrap!(spawner.spawn(run_low()));
159 });
160}
diff --git a/examples/rp23/src/bin/pio_async.rs b/examples/rp23/src/bin/pio_async.rs
new file mode 100644
index 000000000..005708bc2
--- /dev/null
+++ b/examples/rp23/src/bin/pio_async.rs
@@ -0,0 +1,145 @@
1//! This example shows powerful PIO module in the RP2040 chip.
2
3#![no_std]
4#![no_main]
5use defmt::info;
6use embassy_executor::Spawner;
7use embassy_rp::bind_interrupts;
8use embassy_rp::block::ImageDef;
9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine};
11use fixed::traits::ToFixed;
12use fixed_macro::types::U56F8;
13use {defmt_rtt as _, panic_probe as _};
14
15#[link_section = ".start_block"]
16#[used]
17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
18
19// Program metadata for `picotool info`
20#[link_section = ".bi_entries"]
21#[used]
22pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
23 embassy_rp::binary_info_rp_cargo_bin_name!(),
24 embassy_rp::binary_info_rp_cargo_version!(),
25 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
26 embassy_rp::binary_info_rp_program_build_attribute!(),
27];
28
29bind_interrupts!(struct Irqs {
30 PIO0_IRQ_0 => InterruptHandler<PIO0>;
31});
32
33fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) {
34 // Setup sm0
35
36 // Send data serially to pin
37 let prg = pio_proc::pio_asm!(
38 ".origin 16",
39 "set pindirs, 1",
40 ".wrap_target",
41 "out pins,1 [19]",
42 ".wrap",
43 );
44
45 let mut cfg = Config::default();
46 cfg.use_program(&pio.load_program(&prg.program), &[]);
47 let out_pin = pio.make_pio_pin(pin);
48 cfg.set_out_pins(&[&out_pin]);
49 cfg.set_set_pins(&[&out_pin]);
50 cfg.clock_divider = (U56F8!(125_000_000) / 20 / 200).to_fixed();
51 cfg.shift_out.auto_fill = true;
52 sm.set_config(&cfg);
53}
54
55#[embassy_executor::task]
56async fn pio_task_sm0(mut sm: StateMachine<'static, PIO0, 0>) {
57 sm.set_enable(true);
58
59 let mut v = 0x0f0caffa;
60 loop {
61 sm.tx().wait_push(v).await;
62 v ^= 0xffff;
63 info!("Pushed {:032b} to FIFO", v);
64 }
65}
66
67fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 1>) {
68 // Setupm sm1
69
70 // Read 0b10101 repeatedly until ISR is full
71 let prg = pio_proc::pio_asm!(
72 //
73 ".origin 8",
74 "set x, 0x15",
75 ".wrap_target",
76 "in x, 5 [31]",
77 ".wrap",
78 );
79
80 let mut cfg = Config::default();
81 cfg.use_program(&pio.load_program(&prg.program), &[]);
82 cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
83 cfg.shift_in.auto_fill = true;
84 cfg.shift_in.direction = ShiftDirection::Right;
85 sm.set_config(&cfg);
86}
87
88#[embassy_executor::task]
89async fn pio_task_sm1(mut sm: StateMachine<'static, PIO0, 1>) {
90 sm.set_enable(true);
91 loop {
92 let rx = sm.rx().wait_pull().await;
93 info!("Pulled {:032b} from FIFO", rx);
94 }
95}
96
97fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 2>) {
98 // Setup sm2
99
100 // Repeatedly trigger IRQ 3
101 let prg = pio_proc::pio_asm!(
102 ".origin 0",
103 ".wrap_target",
104 "set x,10",
105 "delay:",
106 "jmp x-- delay [15]",
107 "irq 3 [15]",
108 ".wrap",
109 );
110 let mut cfg = Config::default();
111 cfg.use_program(&pio.load_program(&prg.program), &[]);
112 cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
113 sm.set_config(&cfg);
114}
115
116#[embassy_executor::task]
117async fn pio_task_sm2(mut irq: Irq<'static, PIO0, 3>, mut sm: StateMachine<'static, PIO0, 2>) {
118 sm.set_enable(true);
119 loop {
120 irq.wait().await;
121 info!("IRQ trigged");
122 }
123}
124
125#[embassy_executor::main]
126async fn main(spawner: Spawner) {
127 let p = embassy_rp::init(Default::default());
128 let pio = p.PIO0;
129
130 let Pio {
131 mut common,
132 irq3,
133 mut sm0,
134 mut sm1,
135 mut sm2,
136 ..
137 } = Pio::new(pio, Irqs);
138
139 setup_pio_task_sm0(&mut common, &mut sm0, p.PIN_0);
140 setup_pio_task_sm1(&mut common, &mut sm1);
141 setup_pio_task_sm2(&mut common, &mut sm2);
142 spawner.spawn(pio_task_sm0(sm0)).unwrap();
143 spawner.spawn(pio_task_sm1(sm1)).unwrap();
144 spawner.spawn(pio_task_sm2(irq3, sm2)).unwrap();
145}
diff --git a/examples/rp23/src/bin/pio_dma.rs b/examples/rp23/src/bin/pio_dma.rs
new file mode 100644
index 000000000..48fd9123f
--- /dev/null
+++ b/examples/rp23/src/bin/pio_dma.rs
@@ -0,0 +1,98 @@
1//! This example shows powerful PIO module in the RP2040 chip.
2
3#![no_std]
4#![no_main]
5use defmt::info;
6use embassy_executor::Spawner;
7use embassy_futures::join::join;
8use embassy_rp::block::ImageDef;
9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection};
11use embassy_rp::{bind_interrupts, Peripheral};
12use fixed::traits::ToFixed;
13use fixed_macro::types::U56F8;
14use {defmt_rtt as _, panic_probe as _};
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30bind_interrupts!(struct Irqs {
31 PIO0_IRQ_0 => InterruptHandler<PIO0>;
32});
33
34fn swap_nibbles(v: u32) -> u32 {
35 let v = (v & 0x0f0f_0f0f) << 4 | (v & 0xf0f0_f0f0) >> 4;
36 let v = (v & 0x00ff_00ff) << 8 | (v & 0xff00_ff00) >> 8;
37 (v & 0x0000_ffff) << 16 | (v & 0xffff_0000) >> 16
38}
39
40#[embassy_executor::main]
41async fn main(_spawner: Spawner) {
42 let p = embassy_rp::init(Default::default());
43 let pio = p.PIO0;
44 let Pio {
45 mut common,
46 sm0: mut sm,
47 ..
48 } = Pio::new(pio, Irqs);
49
50 let prg = pio_proc::pio_asm!(
51 ".origin 0",
52 "set pindirs,1",
53 ".wrap_target",
54 "set y,7",
55 "loop:",
56 "out x,4",
57 "in x,4",
58 "jmp y--, loop",
59 ".wrap",
60 );
61
62 let mut cfg = Config::default();
63 cfg.use_program(&common.load_program(&prg.program), &[]);
64 cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed();
65 cfg.shift_in = ShiftConfig {
66 auto_fill: true,
67 threshold: 32,
68 direction: ShiftDirection::Left,
69 };
70 cfg.shift_out = ShiftConfig {
71 auto_fill: true,
72 threshold: 32,
73 direction: ShiftDirection::Right,
74 };
75
76 sm.set_config(&cfg);
77 sm.set_enable(true);
78
79 let mut dma_out_ref = p.DMA_CH0.into_ref();
80 let mut dma_in_ref = p.DMA_CH1.into_ref();
81 let mut dout = [0x12345678u32; 29];
82 for i in 1..dout.len() {
83 dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7;
84 }
85 let mut din = [0u32; 29];
86 loop {
87 let (rx, tx) = sm.rx_tx();
88 join(
89 tx.dma_push(dma_out_ref.reborrow(), &dout),
90 rx.dma_pull(dma_in_ref.reborrow(), &mut din),
91 )
92 .await;
93 for i in 0..din.len() {
94 assert_eq!(din[i], swap_nibbles(dout[i]));
95 }
96 info!("Swapped {} words", dout.len());
97 }
98}
diff --git a/examples/rp23/src/bin/pio_hd44780.rs b/examples/rp23/src/bin/pio_hd44780.rs
new file mode 100644
index 000000000..fc658267d
--- /dev/null
+++ b/examples/rp23/src/bin/pio_hd44780.rs
@@ -0,0 +1,255 @@
1//! This example shows powerful PIO module in the RP2040 chip to communicate with a HD44780 display.
2//! See (https://www.sparkfun.com/datasheets/LCD/HD44780.pdf)
3
4#![no_std]
5#![no_main]
6
7use core::fmt::Write;
8
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::dma::{AnyChannel, Channel};
12use embassy_rp::peripherals::PIO0;
13use embassy_rp::pio::{
14 Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
15};
16use embassy_rp::pwm::{self, Pwm};
17use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef};
18use embassy_time::{Instant, Timer};
19use {defmt_rtt as _, panic_probe as _};
20
21#[link_section = ".start_block"]
22#[used]
23pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
24
25// Program metadata for `picotool info`
26#[link_section = ".bi_entries"]
27#[used]
28pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
29 embassy_rp::binary_info_rp_cargo_bin_name!(),
30 embassy_rp::binary_info_rp_cargo_version!(),
31 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
32 embassy_rp::binary_info_rp_program_build_attribute!(),
33];
34
35bind_interrupts!(pub struct Irqs {
36 PIO0_IRQ_0 => InterruptHandler<PIO0>;
37});
38
39#[embassy_executor::main]
40async fn main(_spawner: Spawner) {
41 // this test assumes a 2x16 HD44780 display attached as follow:
42 // rs = PIN0
43 // rw = PIN1
44 // e = PIN2
45 // db4 = PIN3
46 // db5 = PIN4
47 // db6 = PIN5
48 // db7 = PIN6
49 // additionally a pwm signal for a bias voltage charge pump is provided on pin 15,
50 // allowing direct connection of the display to the RP2040 without level shifters.
51 let p = embassy_rp::init(Default::default());
52
53 let _pwm = Pwm::new_output_b(p.PWM_SLICE7, p.PIN_15, {
54 let mut c = pwm::Config::default();
55 c.divider = 125.into();
56 c.top = 100;
57 c.compare_b = 50;
58 c
59 });
60
61 let mut hd = HD44780::new(
62 p.PIO0, Irqs, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6,
63 )
64 .await;
65
66 loop {
67 struct Buf<const N: usize>([u8; N], usize);
68 impl<const N: usize> Write for Buf<N> {
69 fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
70 for b in s.as_bytes() {
71 if self.1 >= N {
72 return Err(core::fmt::Error);
73 }
74 self.0[self.1] = *b;
75 self.1 += 1;
76 }
77 Ok(())
78 }
79 }
80 let mut buf = Buf([0; 16], 0);
81 write!(buf, "up {}s", Instant::now().as_micros() as f32 / 1e6).unwrap();
82 hd.add_line(&buf.0[0..buf.1]).await;
83 Timer::after_secs(1).await;
84 }
85}
86
87pub struct HD44780<'l> {
88 dma: PeripheralRef<'l, AnyChannel>,
89 sm: StateMachine<'l, PIO0, 0>,
90
91 buf: [u8; 40],
92}
93
94impl<'l> HD44780<'l> {
95 pub async fn new(
96 pio: impl Peripheral<P = PIO0> + 'l,
97 irq: Irqs,
98 dma: impl Peripheral<P = impl Channel> + 'l,
99 rs: impl PioPin,
100 rw: impl PioPin,
101 e: impl PioPin,
102 db4: impl PioPin,
103 db5: impl PioPin,
104 db6: impl PioPin,
105 db7: impl PioPin,
106 ) -> HD44780<'l> {
107 into_ref!(dma);
108
109 let Pio {
110 mut common,
111 mut irq0,
112 mut sm0,
113 ..
114 } = Pio::new(pio, irq);
115
116 // takes command words (<wait:24> <command:4> <0:4>)
117 let prg = pio_proc::pio_asm!(
118 r#"
119 .side_set 1 opt
120 .origin 20
121
122 loop:
123 out x, 24
124 delay:
125 jmp x--, delay
126 out pins, 4 side 1
127 out null, 4 side 0
128 jmp !osre, loop
129 irq 0
130 "#,
131 );
132
133 let rs = common.make_pio_pin(rs);
134 let rw = common.make_pio_pin(rw);
135 let e = common.make_pio_pin(e);
136 let db4 = common.make_pio_pin(db4);
137 let db5 = common.make_pio_pin(db5);
138 let db6 = common.make_pio_pin(db6);
139 let db7 = common.make_pio_pin(db7);
140
141 sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]);
142
143 let mut cfg = Config::default();
144 cfg.use_program(&common.load_program(&prg.program), &[&e]);
145 cfg.clock_divider = 125u8.into();
146 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
147 cfg.shift_out = ShiftConfig {
148 auto_fill: true,
149 direction: ShiftDirection::Left,
150 threshold: 32,
151 };
152 cfg.fifo_join = FifoJoin::TxOnly;
153 sm0.set_config(&cfg);
154
155 sm0.set_enable(true);
156 // init to 8 bit thrice
157 sm0.tx().push((50000 << 8) | 0x30);
158 sm0.tx().push((5000 << 8) | 0x30);
159 sm0.tx().push((200 << 8) | 0x30);
160 // init 4 bit
161 sm0.tx().push((200 << 8) | 0x20);
162 // set font and lines
163 sm0.tx().push((50 << 8) | 0x20);
164 sm0.tx().push(0b1100_0000);
165
166 irq0.wait().await;
167 sm0.set_enable(false);
168
169 // takes command sequences (<rs:1> <count:7>, data...)
170 // many side sets are only there to free up a delay bit!
171 let prg = pio_proc::pio_asm!(
172 r#"
173 .origin 27
174 .side_set 1
175
176 .wrap_target
177 pull side 0
178 out x 1 side 0 ; !rs
179 out y 7 side 0 ; #data - 1
180
181 ; rs/rw to e: >= 60ns
182 ; e high time: >= 500ns
183 ; e low time: >= 500ns
184 ; read data valid after e falling: ~5ns
185 ; write data hold after e falling: ~10ns
186
187 loop:
188 pull side 0
189 jmp !x data side 0
190 command:
191 set pins 0b00 side 0
192 jmp shift side 0
193 data:
194 set pins 0b01 side 0
195 shift:
196 out pins 4 side 1 [9]
197 nop side 0 [9]
198 out pins 4 side 1 [9]
199 mov osr null side 0 [7]
200 out pindirs 4 side 0
201 set pins 0b10 side 0
202 busy:
203 nop side 1 [9]
204 jmp pin more side 0 [9]
205 mov osr ~osr side 1 [9]
206 nop side 0 [4]
207 out pindirs 4 side 0
208 jmp y-- loop side 0
209 .wrap
210 more:
211 nop side 1 [9]
212 jmp busy side 0 [9]
213 "#
214 );
215
216 let mut cfg = Config::default();
217 cfg.use_program(&common.load_program(&prg.program), &[&e]);
218 cfg.clock_divider = 8u8.into(); // ~64ns/insn
219 cfg.set_jmp_pin(&db7);
220 cfg.set_set_pins(&[&rs, &rw]);
221 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
222 cfg.shift_out.direction = ShiftDirection::Left;
223 cfg.fifo_join = FifoJoin::TxOnly;
224 sm0.set_config(&cfg);
225
226 sm0.set_enable(true);
227
228 // display on and cursor on and blinking, reset display
229 sm0.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await;
230
231 Self {
232 dma: dma.map_into(),
233 sm: sm0,
234 buf: [0x20; 40],
235 }
236 }
237
238 pub async fn add_line(&mut self, s: &[u8]) {
239 // move cursor to 0:0, prepare 16 characters
240 self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]);
241 // move line 2 up
242 self.buf.copy_within(22..38, 3);
243 // move cursor to 1:0, prepare 16 characters
244 self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]);
245 // file line 2 with spaces
246 self.buf[22..38].fill(0x20);
247 // copy input line
248 let len = s.len().min(16);
249 self.buf[22..22 + len].copy_from_slice(&s[0..len]);
250 // set cursor to 1:15
251 self.buf[38..].copy_from_slice(&[0x80, 0xcf]);
252
253 self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await;
254 }
255}
diff --git a/examples/rp23/src/bin/pio_i2s.rs b/examples/rp23/src/bin/pio_i2s.rs
new file mode 100644
index 000000000..5a3bde759
--- /dev/null
+++ b/examples/rp23/src/bin/pio_i2s.rs
@@ -0,0 +1,140 @@
1//! This example shows generating audio and sending it to a connected i2s DAC using the PIO
2//! module of the RP2040.
3//!
4//! Connect the i2s DAC as follows:
5//! bclk : GPIO 18
6//! lrc : GPIO 19
7//! din : GPIO 20
8//! Then hold down the boot select button to trigger a rising triangle waveform.
9
10#![no_std]
11#![no_main]
12
13use core::mem;
14
15use embassy_executor::Spawner;
16use embassy_rp::block::ImageDef;
17use embassy_rp::peripherals::PIO0;
18use embassy_rp::pio::{Config, FifoJoin, InterruptHandler, Pio, ShiftConfig, ShiftDirection};
19use embassy_rp::{bind_interrupts, Peripheral};
20use fixed::traits::ToFixed;
21use static_cell::StaticCell;
22use {defmt_rtt as _, panic_probe as _};
23
24#[link_section = ".start_block"]
25#[used]
26pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
27
28// Program metadata for `picotool info`
29#[link_section = ".bi_entries"]
30#[used]
31pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
32 embassy_rp::binary_info_rp_cargo_bin_name!(),
33 embassy_rp::binary_info_rp_cargo_version!(),
34 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
35 embassy_rp::binary_info_rp_program_build_attribute!(),
36];
37
38bind_interrupts!(struct Irqs {
39 PIO0_IRQ_0 => InterruptHandler<PIO0>;
40});
41
42const SAMPLE_RATE: u32 = 48_000;
43
44#[embassy_executor::main]
45async fn main(_spawner: Spawner) {
46 let p = embassy_rp::init(Default::default());
47
48 // Setup pio state machine for i2s output
49 let mut pio = Pio::new(p.PIO0, Irqs);
50
51 #[rustfmt::skip]
52 let pio_program = pio_proc::pio_asm!(
53 ".side_set 2",
54 " set x, 14 side 0b01", // side 0bWB - W = Word Clock, B = Bit Clock
55 "left_data:",
56 " out pins, 1 side 0b00",
57 " jmp x-- left_data side 0b01",
58 " out pins 1 side 0b10",
59 " set x, 14 side 0b11",
60 "right_data:",
61 " out pins 1 side 0b10",
62 " jmp x-- right_data side 0b11",
63 " out pins 1 side 0b00",
64 );
65
66 let bit_clock_pin = p.PIN_18;
67 let left_right_clock_pin = p.PIN_19;
68 let data_pin = p.PIN_20;
69
70 let data_pin = pio.common.make_pio_pin(data_pin);
71 let bit_clock_pin = pio.common.make_pio_pin(bit_clock_pin);
72 let left_right_clock_pin = pio.common.make_pio_pin(left_right_clock_pin);
73
74 let cfg = {
75 let mut cfg = Config::default();
76 cfg.use_program(
77 &pio.common.load_program(&pio_program.program),
78 &[&bit_clock_pin, &left_right_clock_pin],
79 );
80 cfg.set_out_pins(&[&data_pin]);
81 const BIT_DEPTH: u32 = 16;
82 const CHANNELS: u32 = 2;
83 let clock_frequency = SAMPLE_RATE * BIT_DEPTH * CHANNELS;
84 cfg.clock_divider = (125_000_000. / clock_frequency as f64 / 2.).to_fixed();
85 cfg.shift_out = ShiftConfig {
86 threshold: 32,
87 direction: ShiftDirection::Left,
88 auto_fill: true,
89 };
90 // join fifos to have twice the time to start the next dma transfer
91 cfg.fifo_join = FifoJoin::TxOnly;
92 cfg
93 };
94 pio.sm0.set_config(&cfg);
95 pio.sm0.set_pin_dirs(
96 embassy_rp::pio::Direction::Out,
97 &[&data_pin, &left_right_clock_pin, &bit_clock_pin],
98 );
99
100 // create two audio buffers (back and front) which will take turns being
101 // filled with new audio data and being sent to the pio fifo using dma
102 const BUFFER_SIZE: usize = 960;
103 static DMA_BUFFER: StaticCell<[u32; BUFFER_SIZE * 2]> = StaticCell::new();
104 let dma_buffer = DMA_BUFFER.init_with(|| [0u32; BUFFER_SIZE * 2]);
105 let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE);
106
107 // start pio state machine
108 pio.sm0.set_enable(true);
109 let tx = pio.sm0.tx();
110 let mut dma_ref = p.DMA_CH0.into_ref();
111
112 let mut fade_value: i32 = 0;
113 let mut phase: i32 = 0;
114
115 loop {
116 // trigger transfer of front buffer data to the pio fifo
117 // but don't await the returned future, yet
118 let dma_future = tx.dma_push(dma_ref.reborrow(), front_buffer);
119
120 // fade in audio
121 let fade_target = i32::MAX;
122
123 // fill back buffer with fresh audio samples before awaiting the dma future
124 for s in back_buffer.iter_mut() {
125 // exponential approach of fade_value => fade_target
126 fade_value += (fade_target - fade_value) >> 14;
127 // generate triangle wave with amplitude and frequency based on fade value
128 phase = (phase + (fade_value >> 22)) & 0xffff;
129 let triangle_sample = (phase as i16 as i32).abs() - 16384;
130 let sample = (triangle_sample * (fade_value >> 15)) >> 16;
131 // duplicate mono sample into lower and upper half of dma word
132 *s = (sample as u16 as u32) * 0x10001;
133 }
134
135 // now await the dma future. once the dma finishes, the next buffer needs to be queued
136 // within DMA_DEPTH / SAMPLE_RATE = 8 / 48000 seconds = 166us
137 dma_future.await;
138 mem::swap(&mut back_buffer, &mut front_buffer);
139 }
140}
diff --git a/examples/rp23/src/bin/pio_pwm.rs b/examples/rp23/src/bin/pio_pwm.rs
new file mode 100644
index 000000000..7c5eefc45
--- /dev/null
+++ b/examples/rp23/src/bin/pio_pwm.rs
@@ -0,0 +1,133 @@
1//! This example shows how to create a pwm using the PIO module in the RP2040 chip.
2
3#![no_std]
4#![no_main]
5use core::time::Duration;
6
7use embassy_executor::Spawner;
8use embassy_rp::block::ImageDef;
9use embassy_rp::gpio::Level;
10use embassy_rp::peripherals::PIO0;
11use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine};
12use embassy_rp::{bind_interrupts, clocks};
13use embassy_time::Timer;
14use pio::InstructionOperands;
15use {defmt_rtt as _, panic_probe as _};
16
17#[link_section = ".start_block"]
18#[used]
19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
20
21// Program metadata for `picotool info`
22#[link_section = ".bi_entries"]
23#[used]
24pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
25 embassy_rp::binary_info_rp_cargo_bin_name!(),
26 embassy_rp::binary_info_rp_cargo_version!(),
27 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
28 embassy_rp::binary_info_rp_program_build_attribute!(),
29];
30
31const REFRESH_INTERVAL: u64 = 20000;
32
33bind_interrupts!(struct Irqs {
34 PIO0_IRQ_0 => InterruptHandler<PIO0>;
35});
36
37pub fn to_pio_cycles(duration: Duration) -> u32 {
38 (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
39}
40
41pub struct PwmPio<'d, T: Instance, const SM: usize> {
42 sm: StateMachine<'d, T, SM>,
43}
44
45impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
46 pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
47 let prg = pio_proc::pio_asm!(
48 ".side_set 1 opt"
49 "pull noblock side 0"
50 "mov x, osr"
51 "mov y, isr"
52 "countloop:"
53 "jmp x!=y noset"
54 "jmp skip side 1"
55 "noset:"
56 "nop"
57 "skip:"
58 "jmp y-- countloop"
59 );
60
61 pio.load_program(&prg.program);
62 let pin = pio.make_pio_pin(pin);
63 sm.set_pins(Level::High, &[&pin]);
64 sm.set_pin_dirs(Direction::Out, &[&pin]);
65
66 let mut cfg = Config::default();
67 cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
68
69 sm.set_config(&cfg);
70
71 Self { sm }
72 }
73
74 pub fn start(&mut self) {
75 self.sm.set_enable(true);
76 }
77
78 pub fn stop(&mut self) {
79 self.sm.set_enable(false);
80 }
81
82 pub fn set_period(&mut self, duration: Duration) {
83 let is_enabled = self.sm.is_enabled();
84 while !self.sm.tx().empty() {} // Make sure that the queue is empty
85 self.sm.set_enable(false);
86 self.sm.tx().push(to_pio_cycles(duration));
87 unsafe {
88 self.sm.exec_instr(
89 InstructionOperands::PULL {
90 if_empty: false,
91 block: false,
92 }
93 .encode(),
94 );
95 self.sm.exec_instr(
96 InstructionOperands::OUT {
97 destination: ::pio::OutDestination::ISR,
98 bit_count: 32,
99 }
100 .encode(),
101 );
102 };
103 if is_enabled {
104 self.sm.set_enable(true) // Enable if previously enabled
105 }
106 }
107
108 pub fn set_level(&mut self, level: u32) {
109 self.sm.tx().push(level);
110 }
111
112 pub fn write(&mut self, duration: Duration) {
113 self.set_level(to_pio_cycles(duration));
114 }
115}
116
117#[embassy_executor::main]
118async fn main(_spawner: Spawner) {
119 let p = embassy_rp::init(Default::default());
120 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
121
122 // Note that PIN_25 is the led pin on the Pico
123 let mut pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_25);
124 pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL));
125 pwm_pio.start();
126
127 let mut duration = 0;
128 loop {
129 duration = (duration + 1) % 1000;
130 pwm_pio.write(Duration::from_micros(duration));
131 Timer::after_millis(1).await;
132 }
133}
diff --git a/examples/rp23/src/bin/pio_rotary_encoder.rs b/examples/rp23/src/bin/pio_rotary_encoder.rs
new file mode 100644
index 000000000..287992a83
--- /dev/null
+++ b/examples/rp23/src/bin/pio_rotary_encoder.rs
@@ -0,0 +1,95 @@
1//! This example shows how to use the PIO module in the RP2040 to read a quadrature rotary encoder.
2
3#![no_std]
4#![no_main]
5
6use defmt::info;
7use embassy_executor::Spawner;
8use embassy_rp::block::ImageDef;
9use embassy_rp::gpio::Pull;
10use embassy_rp::peripherals::PIO0;
11use embassy_rp::{bind_interrupts, pio};
12use fixed::traits::ToFixed;
13use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine};
14use {defmt_rtt as _, panic_probe as _};
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30bind_interrupts!(struct Irqs {
31 PIO0_IRQ_0 => InterruptHandler<PIO0>;
32});
33
34pub struct PioEncoder<'d, T: Instance, const SM: usize> {
35 sm: StateMachine<'d, T, SM>,
36}
37
38impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> {
39 pub fn new(
40 pio: &mut Common<'d, T>,
41 mut sm: StateMachine<'d, T, SM>,
42 pin_a: impl PioPin,
43 pin_b: impl PioPin,
44 ) -> Self {
45 let mut pin_a = pio.make_pio_pin(pin_a);
46 let mut pin_b = pio.make_pio_pin(pin_b);
47 pin_a.set_pull(Pull::Up);
48 pin_b.set_pull(Pull::Up);
49 sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]);
50
51 let prg = pio_proc::pio_asm!("wait 1 pin 1", "wait 0 pin 1", "in pins, 2", "push",);
52
53 let mut cfg = Config::default();
54 cfg.set_in_pins(&[&pin_a, &pin_b]);
55 cfg.fifo_join = FifoJoin::RxOnly;
56 cfg.shift_in.direction = ShiftDirection::Left;
57 cfg.clock_divider = 10_000.to_fixed();
58 cfg.use_program(&pio.load_program(&prg.program), &[]);
59 sm.set_config(&cfg);
60 sm.set_enable(true);
61 Self { sm }
62 }
63
64 pub async fn read(&mut self) -> Direction {
65 loop {
66 match self.sm.rx().wait_pull().await {
67 0 => return Direction::CounterClockwise,
68 1 => return Direction::Clockwise,
69 _ => {}
70 }
71 }
72 }
73}
74
75pub enum Direction {
76 Clockwise,
77 CounterClockwise,
78}
79
80#[embassy_executor::main]
81async fn main(_spawner: Spawner) {
82 let p = embassy_rp::init(Default::default());
83 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
84
85 let mut encoder = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5);
86
87 let mut count = 0;
88 loop {
89 info!("Count: {}", count);
90 count += match encoder.read().await {
91 Direction::Clockwise => 1,
92 Direction::CounterClockwise => -1,
93 };
94 }
95}
diff --git a/examples/rp23/src/bin/pio_servo.rs b/examples/rp23/src/bin/pio_servo.rs
new file mode 100644
index 000000000..1dec86927
--- /dev/null
+++ b/examples/rp23/src/bin/pio_servo.rs
@@ -0,0 +1,223 @@
1//! This example shows how to create a pwm using the PIO module in the RP2040 chip.
2
3#![no_std]
4#![no_main]
5use core::time::Duration;
6
7use embassy_executor::Spawner;
8use embassy_rp::block::ImageDef;
9use embassy_rp::gpio::Level;
10use embassy_rp::peripherals::PIO0;
11use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine};
12use embassy_rp::{bind_interrupts, clocks};
13use embassy_time::Timer;
14use pio::InstructionOperands;
15use {defmt_rtt as _, panic_probe as _};
16
17#[link_section = ".start_block"]
18#[used]
19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
20
21// Program metadata for `picotool info`
22#[link_section = ".bi_entries"]
23#[used]
24pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
25 embassy_rp::binary_info_rp_cargo_bin_name!(),
26 embassy_rp::binary_info_rp_cargo_version!(),
27 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
28 embassy_rp::binary_info_rp_program_build_attribute!(),
29];
30
31const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo
32const DEFAULT_MAX_PULSE_WIDTH: u64 = 2000; // uncalibrated default, the longest duty cycle sent to a servo
33const DEFAULT_MAX_DEGREE_ROTATION: u64 = 160; // 160 degrees is typical
34const REFRESH_INTERVAL: u64 = 20000; // The period of each cycle
35
36bind_interrupts!(struct Irqs {
37 PIO0_IRQ_0 => InterruptHandler<PIO0>;
38});
39
40pub fn to_pio_cycles(duration: Duration) -> u32 {
41 (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
42}
43
44pub struct PwmPio<'d, T: Instance, const SM: usize> {
45 sm: StateMachine<'d, T, SM>,
46}
47
48impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
49 pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
50 let prg = pio_proc::pio_asm!(
51 ".side_set 1 opt"
52 "pull noblock side 0"
53 "mov x, osr"
54 "mov y, isr"
55 "countloop:"
56 "jmp x!=y noset"
57 "jmp skip side 1"
58 "noset:"
59 "nop"
60 "skip:"
61 "jmp y-- countloop"
62 );
63
64 pio.load_program(&prg.program);
65 let pin = pio.make_pio_pin(pin);
66 sm.set_pins(Level::High, &[&pin]);
67 sm.set_pin_dirs(Direction::Out, &[&pin]);
68
69 let mut cfg = Config::default();
70 cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
71
72 sm.set_config(&cfg);
73
74 Self { sm }
75 }
76
77 pub fn start(&mut self) {
78 self.sm.set_enable(true);
79 }
80
81 pub fn stop(&mut self) {
82 self.sm.set_enable(false);
83 }
84
85 pub fn set_period(&mut self, duration: Duration) {
86 let is_enabled = self.sm.is_enabled();
87 while !self.sm.tx().empty() {} // Make sure that the queue is empty
88 self.sm.set_enable(false);
89 self.sm.tx().push(to_pio_cycles(duration));
90 unsafe {
91 self.sm.exec_instr(
92 InstructionOperands::PULL {
93 if_empty: false,
94 block: false,
95 }
96 .encode(),
97 );
98 self.sm.exec_instr(
99 InstructionOperands::OUT {
100 destination: ::pio::OutDestination::ISR,
101 bit_count: 32,
102 }
103 .encode(),
104 );
105 };
106 if is_enabled {
107 self.sm.set_enable(true) // Enable if previously enabled
108 }
109 }
110
111 pub fn set_level(&mut self, level: u32) {
112 self.sm.tx().push(level);
113 }
114
115 pub fn write(&mut self, duration: Duration) {
116 self.set_level(to_pio_cycles(duration));
117 }
118}
119
120pub struct ServoBuilder<'d, T: Instance, const SM: usize> {
121 pwm: PwmPio<'d, T, SM>,
122 period: Duration,
123 min_pulse_width: Duration,
124 max_pulse_width: Duration,
125 max_degree_rotation: u64,
126}
127
128impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> {
129 pub fn new(pwm: PwmPio<'d, T, SM>) -> Self {
130 Self {
131 pwm,
132 period: Duration::from_micros(REFRESH_INTERVAL),
133 min_pulse_width: Duration::from_micros(DEFAULT_MIN_PULSE_WIDTH),
134 max_pulse_width: Duration::from_micros(DEFAULT_MAX_PULSE_WIDTH),
135 max_degree_rotation: DEFAULT_MAX_DEGREE_ROTATION,
136 }
137 }
138
139 pub fn set_period(mut self, duration: Duration) -> Self {
140 self.period = duration;
141 self
142 }
143
144 pub fn set_min_pulse_width(mut self, duration: Duration) -> Self {
145 self.min_pulse_width = duration;
146 self
147 }
148
149 pub fn set_max_pulse_width(mut self, duration: Duration) -> Self {
150 self.max_pulse_width = duration;
151 self
152 }
153
154 pub fn set_max_degree_rotation(mut self, degree: u64) -> Self {
155 self.max_degree_rotation = degree;
156 self
157 }
158
159 pub fn build(mut self) -> Servo<'d, T, SM> {
160 self.pwm.set_period(self.period);
161 Servo {
162 pwm: self.pwm,
163 min_pulse_width: self.min_pulse_width,
164 max_pulse_width: self.max_pulse_width,
165 max_degree_rotation: self.max_degree_rotation,
166 }
167 }
168}
169
170pub struct Servo<'d, T: Instance, const SM: usize> {
171 pwm: PwmPio<'d, T, SM>,
172 min_pulse_width: Duration,
173 max_pulse_width: Duration,
174 max_degree_rotation: u64,
175}
176
177impl<'d, T: Instance, const SM: usize> Servo<'d, T, SM> {
178 pub fn start(&mut self) {
179 self.pwm.start();
180 }
181
182 pub fn stop(&mut self) {
183 self.pwm.stop();
184 }
185
186 pub fn write_time(&mut self, duration: Duration) {
187 self.pwm.write(duration);
188 }
189
190 pub fn rotate(&mut self, degree: u64) {
191 let degree_per_nano_second = (self.max_pulse_width.as_nanos() as u64 - self.min_pulse_width.as_nanos() as u64)
192 / self.max_degree_rotation;
193 let mut duration =
194 Duration::from_nanos(degree * degree_per_nano_second + self.min_pulse_width.as_nanos() as u64);
195 if self.max_pulse_width < duration {
196 duration = self.max_pulse_width;
197 }
198
199 self.pwm.write(duration);
200 }
201}
202
203#[embassy_executor::main]
204async fn main(_spawner: Spawner) {
205 let p = embassy_rp::init(Default::default());
206 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
207
208 let pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_1);
209 let mut servo = ServoBuilder::new(pwm_pio)
210 .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo
211 .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment.
212 .set_max_pulse_width(Duration::from_micros(2600)) // Along with this value.
213 .build();
214
215 servo.start();
216
217 let mut degree = 0;
218 loop {
219 degree = (degree + 1) % 120;
220 servo.rotate(degree);
221 Timer::after_millis(50).await;
222 }
223}
diff --git a/examples/rp23/src/bin/pio_stepper.rs b/examples/rp23/src/bin/pio_stepper.rs
new file mode 100644
index 000000000..8b52dc37a
--- /dev/null
+++ b/examples/rp23/src/bin/pio_stepper.rs
@@ -0,0 +1,183 @@
1//! This example shows how to use the PIO module in the RP2040 to implement a stepper motor driver
2//! for a 5-wire stepper such as the 28BYJ-48. You can halt an ongoing rotation by dropping the future.
3
4#![no_std]
5#![no_main]
6use core::mem::{self, MaybeUninit};
7
8use defmt::info;
9use embassy_executor::Spawner;
10use embassy_rp::bind_interrupts;
11use embassy_rp::block::ImageDef;
12use embassy_rp::peripherals::PIO0;
13use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Irq, Pio, PioPin, StateMachine};
14use embassy_time::{with_timeout, Duration, Timer};
15use fixed::traits::ToFixed;
16use fixed::types::extra::U8;
17use fixed::FixedU32;
18use {defmt_rtt as _, panic_probe as _};
19
20#[link_section = ".start_block"]
21#[used]
22pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
23
24// Program metadata for `picotool info`
25#[link_section = ".bi_entries"]
26#[used]
27pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
28 embassy_rp::binary_info_rp_cargo_bin_name!(),
29 embassy_rp::binary_info_rp_cargo_version!(),
30 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
31 embassy_rp::binary_info_rp_program_build_attribute!(),
32];
33
34bind_interrupts!(struct Irqs {
35 PIO0_IRQ_0 => InterruptHandler<PIO0>;
36});
37
38pub struct PioStepper<'d, T: Instance, const SM: usize> {
39 irq: Irq<'d, T, SM>,
40 sm: StateMachine<'d, T, SM>,
41}
42
43impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> {
44 pub fn new(
45 pio: &mut Common<'d, T>,
46 mut sm: StateMachine<'d, T, SM>,
47 irq: Irq<'d, T, SM>,
48 pin0: impl PioPin,
49 pin1: impl PioPin,
50 pin2: impl PioPin,
51 pin3: impl PioPin,
52 ) -> Self {
53 let prg = pio_proc::pio_asm!(
54 "pull block",
55 "mov x, osr",
56 "pull block",
57 "mov y, osr",
58 "jmp !x end",
59 "loop:",
60 "jmp !osre step",
61 "mov osr, y",
62 "step:",
63 "out pins, 4 [31]"
64 "jmp x-- loop",
65 "end:",
66 "irq 0 rel"
67 );
68 let pin0 = pio.make_pio_pin(pin0);
69 let pin1 = pio.make_pio_pin(pin1);
70 let pin2 = pio.make_pio_pin(pin2);
71 let pin3 = pio.make_pio_pin(pin3);
72 sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]);
73 let mut cfg = Config::default();
74 cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]);
75 cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed();
76 cfg.use_program(&pio.load_program(&prg.program), &[]);
77 sm.set_config(&cfg);
78 sm.set_enable(true);
79 Self { irq, sm }
80 }
81
82 // Set pulse frequency
83 pub fn set_frequency(&mut self, freq: u32) {
84 let clock_divider: FixedU32<U8> = (125_000_000 / (freq * 136)).to_fixed();
85 assert!(clock_divider <= 65536, "clkdiv must be <= 65536");
86 assert!(clock_divider >= 1, "clkdiv must be >= 1");
87 self.sm.set_clock_divider(clock_divider);
88 self.sm.clkdiv_restart();
89 }
90
91 // Full step, one phase
92 pub async fn step(&mut self, steps: i32) {
93 if steps > 0 {
94 self.run(steps, 0b1000_0100_0010_0001_1000_0100_0010_0001).await
95 } else {
96 self.run(-steps, 0b0001_0010_0100_1000_0001_0010_0100_1000).await
97 }
98 }
99
100 // Full step, two phase
101 pub async fn step2(&mut self, steps: i32) {
102 if steps > 0 {
103 self.run(steps, 0b1001_1100_0110_0011_1001_1100_0110_0011).await
104 } else {
105 self.run(-steps, 0b0011_0110_1100_1001_0011_0110_1100_1001).await
106 }
107 }
108
109 // Half step
110 pub async fn step_half(&mut self, steps: i32) {
111 if steps > 0 {
112 self.run(steps, 0b1001_1000_1100_0100_0110_0010_0011_0001).await
113 } else {
114 self.run(-steps, 0b0001_0011_0010_0110_0100_1100_1000_1001).await
115 }
116 }
117
118 async fn run(&mut self, steps: i32, pattern: u32) {
119 self.sm.tx().wait_push(steps as u32).await;
120 self.sm.tx().wait_push(pattern).await;
121 let drop = OnDrop::new(|| {
122 self.sm.clear_fifos();
123 unsafe {
124 self.sm.exec_instr(
125 pio::InstructionOperands::JMP {
126 address: 0,
127 condition: pio::JmpCondition::Always,
128 }
129 .encode(),
130 );
131 }
132 });
133 self.irq.wait().await;
134 drop.defuse();
135 }
136}
137
138struct OnDrop<F: FnOnce()> {
139 f: MaybeUninit<F>,
140}
141
142impl<F: FnOnce()> OnDrop<F> {
143 pub fn new(f: F) -> Self {
144 Self { f: MaybeUninit::new(f) }
145 }
146
147 pub fn defuse(self) {
148 mem::forget(self)
149 }
150}
151
152impl<F: FnOnce()> Drop for OnDrop<F> {
153 fn drop(&mut self) {
154 unsafe { self.f.as_ptr().read()() }
155 }
156}
157
158#[embassy_executor::main]
159async fn main(_spawner: Spawner) {
160 let p = embassy_rp::init(Default::default());
161 let Pio {
162 mut common, irq0, sm0, ..
163 } = Pio::new(p.PIO0, Irqs);
164
165 let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7);
166 stepper.set_frequency(120);
167 loop {
168 info!("CW full steps");
169 stepper.step(1000).await;
170
171 info!("CCW full steps, drop after 1 sec");
172 if let Err(_) = with_timeout(Duration::from_secs(1), stepper.step(i32::MIN)).await {
173 info!("Time's up!");
174 Timer::after(Duration::from_secs(1)).await;
175 }
176
177 info!("CW half steps");
178 stepper.step_half(1000).await;
179
180 info!("CCW half steps");
181 stepper.step_half(-1000).await;
182 }
183}
diff --git a/examples/rp23/src/bin/pio_ws2812.rs b/examples/rp23/src/bin/pio_ws2812.rs
new file mode 100644
index 000000000..99d9cf7e6
--- /dev/null
+++ b/examples/rp23/src/bin/pio_ws2812.rs
@@ -0,0 +1,176 @@
1//! This example shows powerful PIO module in the RP2040 chip to communicate with WS2812 LED modules.
2//! See (https://www.sparkfun.com/categories/tags/ws2812)
3
4#![no_std]
5#![no_main]
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_rp::block::ImageDef;
10use embassy_rp::dma::{AnyChannel, Channel};
11use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{
13 Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
14};
15use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef};
16use embassy_time::{Duration, Ticker, Timer};
17use fixed::types::U24F8;
18use fixed_macro::fixed;
19use smart_leds::RGB8;
20use {defmt_rtt as _, panic_probe as _};
21
22#[link_section = ".start_block"]
23#[used]
24pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
25
26// Program metadata for `picotool info`
27#[link_section = ".bi_entries"]
28#[used]
29pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
30 embassy_rp::binary_info_rp_cargo_bin_name!(),
31 embassy_rp::binary_info_rp_cargo_version!(),
32 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
33 embassy_rp::binary_info_rp_program_build_attribute!(),
34];
35
36bind_interrupts!(struct Irqs {
37 PIO0_IRQ_0 => InterruptHandler<PIO0>;
38});
39
40pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> {
41 dma: PeripheralRef<'d, AnyChannel>,
42 sm: StateMachine<'d, P, S>,
43}
44
45impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> {
46 pub fn new(
47 pio: &mut Common<'d, P>,
48 mut sm: StateMachine<'d, P, S>,
49 dma: impl Peripheral<P = impl Channel> + 'd,
50 pin: impl PioPin,
51 ) -> Self {
52 into_ref!(dma);
53
54 // Setup sm0
55
56 // prepare the PIO program
57 let side_set = pio::SideSet::new(false, 1, false);
58 let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set);
59
60 const T1: u8 = 2; // start bit
61 const T2: u8 = 5; // data bit
62 const T3: u8 = 3; // stop bit
63 const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32;
64
65 let mut wrap_target = a.label();
66 let mut wrap_source = a.label();
67 let mut do_zero = a.label();
68 a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0);
69 a.bind(&mut wrap_target);
70 // Do stop bit
71 a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0);
72 // Do start bit
73 a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1);
74 // Do data bit = 1
75 a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1);
76 a.bind(&mut do_zero);
77 // Do data bit = 0
78 a.nop_with_delay_and_side_set(T2 - 1, 0);
79 a.bind(&mut wrap_source);
80
81 let prg = a.assemble_with_wrap(wrap_source, wrap_target);
82 let mut cfg = Config::default();
83
84 // Pin config
85 let out_pin = pio.make_pio_pin(pin);
86 cfg.set_out_pins(&[&out_pin]);
87 cfg.set_set_pins(&[&out_pin]);
88
89 cfg.use_program(&pio.load_program(&prg), &[&out_pin]);
90
91 // Clock config, measured in kHz to avoid overflows
92 // TODO CLOCK_FREQ should come from embassy_rp
93 let clock_freq = U24F8::from_num(clocks::clk_sys_freq() / 1000);
94 let ws2812_freq = fixed!(800: U24F8);
95 let bit_freq = ws2812_freq * CYCLES_PER_BIT;
96 cfg.clock_divider = clock_freq / bit_freq;
97
98 // FIFO config
99 cfg.fifo_join = FifoJoin::TxOnly;
100 cfg.shift_out = ShiftConfig {
101 auto_fill: true,
102 threshold: 24,
103 direction: ShiftDirection::Left,
104 };
105
106 sm.set_config(&cfg);
107 sm.set_enable(true);
108
109 Self {
110 dma: dma.map_into(),
111 sm,
112 }
113 }
114
115 pub async fn write(&mut self, colors: &[RGB8; N]) {
116 // Precompute the word bytes from the colors
117 let mut words = [0u32; N];
118 for i in 0..N {
119 let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8);
120 words[i] = word;
121 }
122
123 // DMA transfer
124 self.sm.tx().dma_push(self.dma.reborrow(), &words).await;
125
126 Timer::after_micros(55).await;
127 }
128}
129
130/// Input a value 0 to 255 to get a color value
131/// The colours are a transition r - g - b - back to r.
132fn wheel(mut wheel_pos: u8) -> RGB8 {
133 wheel_pos = 255 - wheel_pos;
134 if wheel_pos < 85 {
135 return (255 - wheel_pos * 3, 0, wheel_pos * 3).into();
136 }
137 if wheel_pos < 170 {
138 wheel_pos -= 85;
139 return (0, wheel_pos * 3, 255 - wheel_pos * 3).into();
140 }
141 wheel_pos -= 170;
142 (wheel_pos * 3, 255 - wheel_pos * 3, 0).into()
143}
144
145#[embassy_executor::main]
146async fn main(_spawner: Spawner) {
147 info!("Start");
148 let p = embassy_rp::init(Default::default());
149
150 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
151
152 // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit
153 // feather boards for the 2040 both have one built in.
154 const NUM_LEDS: usize = 1;
155 let mut data = [RGB8::default(); NUM_LEDS];
156
157 // Common neopixel pins:
158 // Thing plus: 8
159 // Adafruit Feather: 16; Adafruit Feather+RFM95: 4
160 let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16);
161
162 // Loop forever making RGB values and pushing them out to the WS2812.
163 let mut ticker = Ticker::every(Duration::from_millis(10));
164 loop {
165 for j in 0..(256 * 5) {
166 debug!("New Colors:");
167 for i in 0..NUM_LEDS {
168 data[i] = wheel((((i * 256) as u16 / NUM_LEDS as u16 + j as u16) & 255) as u8);
169 debug!("R: {} G: {} B: {}", data[i].r, data[i].g, data[i].b);
170 }
171 ws2812.write(&data).await;
172
173 ticker.next().await;
174 }
175 }
176}
diff --git a/examples/rp23/src/bin/pwm.rs b/examples/rp23/src/bin/pwm.rs
new file mode 100644
index 000000000..4cd3b3eb4
--- /dev/null
+++ b/examples/rp23/src/bin/pwm.rs
@@ -0,0 +1,44 @@
1//! This example shows how to use PWM (Pulse Width Modulation) in the RP2040 chip.
2//!
3//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::pwm::{Config, Pwm};
12use embassy_time::Timer;
13use {defmt_rtt as _, panic_probe as _};
14
15#[link_section = ".start_block"]
16#[used]
17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
18
19// Program metadata for `picotool info`
20#[link_section = ".bi_entries"]
21#[used]
22pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
23 embassy_rp::binary_info_rp_cargo_bin_name!(),
24 embassy_rp::binary_info_rp_cargo_version!(),
25 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
26 embassy_rp::binary_info_rp_program_build_attribute!(),
27];
28
29#[embassy_executor::main]
30async fn main(_spawner: Spawner) {
31 let p = embassy_rp::init(Default::default());
32
33 let mut c: Config = Default::default();
34 c.top = 0x8000;
35 c.compare_b = 8;
36 let mut pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, c.clone());
37
38 loop {
39 info!("current LED duty cycle: {}/32768", c.compare_b);
40 Timer::after_secs(1).await;
41 c.compare_b = c.compare_b.rotate_left(4);
42 pwm.set_config(&c);
43 }
44}
diff --git a/examples/rp23/src/bin/pwm_input.rs b/examples/rp23/src/bin/pwm_input.rs
new file mode 100644
index 000000000..b75d04963
--- /dev/null
+++ b/examples/rp23/src/bin/pwm_input.rs
@@ -0,0 +1,41 @@
1//! This example shows how to use the PWM module to measure the frequency of an input signal.
2
3#![no_std]
4#![no_main]
5
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_rp::block::ImageDef;
9use embassy_rp::gpio::Pull;
10use embassy_rp::pwm::{Config, InputMode, Pwm};
11use embassy_time::{Duration, Ticker};
12use {defmt_rtt as _, panic_probe as _};
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28#[embassy_executor::main]
29async fn main(_spawner: Spawner) {
30 let p = embassy_rp::init(Default::default());
31
32 let cfg: Config = Default::default();
33 let pwm = Pwm::new_input(p.PWM_SLICE2, p.PIN_5, Pull::None, InputMode::RisingEdge, cfg);
34
35 let mut ticker = Ticker::every(Duration::from_secs(1));
36 loop {
37 info!("Input frequency: {} Hz", pwm.counter());
38 pwm.set_counter(0);
39 ticker.next().await;
40 }
41}
diff --git a/examples/rp23/src/bin/rosc.rs b/examples/rp23/src/bin/rosc.rs
new file mode 100644
index 000000000..28c778f51
--- /dev/null
+++ b/examples/rp23/src/bin/rosc.rs
@@ -0,0 +1,46 @@
1//! This example test the RP Pico on board LED.
2//!
3//! It does not work with the RP Pico W board. See wifi_blinky.rs.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::{clocks, gpio};
12use embassy_time::Timer;
13use gpio::{Level, Output};
14use {defmt_rtt as _, panic_probe as _};
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30#[embassy_executor::main]
31async fn main(_spawner: Spawner) {
32 let mut config = embassy_rp::config::Config::default();
33 config.clocks = clocks::ClockConfig::rosc();
34 let p = embassy_rp::init(config);
35 let mut led = Output::new(p.PIN_25, Level::Low);
36
37 loop {
38 info!("led on!");
39 led.set_high();
40 Timer::after_secs(1).await;
41
42 info!("led off!");
43 led.set_low();
44 Timer::after_secs(1).await;
45 }
46}
diff --git a/examples/rp23/src/bin/shared_bus.rs b/examples/rp23/src/bin/shared_bus.rs
new file mode 100644
index 000000000..00e65f80d
--- /dev/null
+++ b/examples/rp23/src/bin/shared_bus.rs
@@ -0,0 +1,130 @@
1//! This example shows how to share (async) I2C and SPI buses between multiple devices.
2
3#![no_std]
4#![no_main]
5
6use defmt::*;
7use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice;
8use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
9use embassy_executor::Spawner;
10use embassy_rp::bind_interrupts;
11use embassy_rp::block::ImageDef;
12use embassy_rp::gpio::{AnyPin, Level, Output};
13use embassy_rp::i2c::{self, I2c, InterruptHandler};
14use embassy_rp::peripherals::{I2C1, SPI1};
15use embassy_rp::spi::{self, Spi};
16use embassy_sync::blocking_mutex::raw::NoopRawMutex;
17use embassy_sync::mutex::Mutex;
18use embassy_time::Timer;
19use static_cell::StaticCell;
20use {defmt_rtt as _, panic_probe as _};
21
22#[link_section = ".start_block"]
23#[used]
24pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
25
26// Program metadata for `picotool info`
27#[link_section = ".bi_entries"]
28#[used]
29pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
30 embassy_rp::binary_info_rp_cargo_bin_name!(),
31 embassy_rp::binary_info_rp_cargo_version!(),
32 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
33 embassy_rp::binary_info_rp_program_build_attribute!(),
34];
35
36type Spi1Bus = Mutex<NoopRawMutex, Spi<'static, SPI1, spi::Async>>;
37type I2c1Bus = Mutex<NoopRawMutex, I2c<'static, I2C1, i2c::Async>>;
38
39bind_interrupts!(struct Irqs {
40 I2C1_IRQ => InterruptHandler<I2C1>;
41});
42
43#[embassy_executor::main]
44async fn main(spawner: Spawner) {
45 let p = embassy_rp::init(Default::default());
46 info!("Here we go!");
47
48 // Shared I2C bus
49 let i2c = I2c::new_async(p.I2C1, p.PIN_15, p.PIN_14, Irqs, i2c::Config::default());
50 static I2C_BUS: StaticCell<I2c1Bus> = StaticCell::new();
51 let i2c_bus = I2C_BUS.init(Mutex::new(i2c));
52
53 spawner.must_spawn(i2c_task_a(i2c_bus));
54 spawner.must_spawn(i2c_task_b(i2c_bus));
55
56 // Shared SPI bus
57 let spi_cfg = spi::Config::default();
58 let spi = Spi::new(p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, p.DMA_CH0, p.DMA_CH1, spi_cfg);
59 static SPI_BUS: StaticCell<Spi1Bus> = StaticCell::new();
60 let spi_bus = SPI_BUS.init(Mutex::new(spi));
61
62 // Chip select pins for the SPI devices
63 let cs_a = Output::new(AnyPin::from(p.PIN_0), Level::High);
64 let cs_b = Output::new(AnyPin::from(p.PIN_1), Level::High);
65
66 spawner.must_spawn(spi_task_a(spi_bus, cs_a));
67 spawner.must_spawn(spi_task_b(spi_bus, cs_b));
68}
69
70#[embassy_executor::task]
71async fn i2c_task_a(i2c_bus: &'static I2c1Bus) {
72 let i2c_dev = I2cDevice::new(i2c_bus);
73 let _sensor = DummyI2cDeviceDriver::new(i2c_dev, 0xC0);
74 loop {
75 info!("i2c task A");
76 Timer::after_secs(1).await;
77 }
78}
79
80#[embassy_executor::task]
81async fn i2c_task_b(i2c_bus: &'static I2c1Bus) {
82 let i2c_dev = I2cDevice::new(i2c_bus);
83 let _sensor = DummyI2cDeviceDriver::new(i2c_dev, 0xDE);
84 loop {
85 info!("i2c task B");
86 Timer::after_secs(1).await;
87 }
88}
89
90#[embassy_executor::task]
91async fn spi_task_a(spi_bus: &'static Spi1Bus, cs: Output<'static>) {
92 let spi_dev = SpiDevice::new(spi_bus, cs);
93 let _sensor = DummySpiDeviceDriver::new(spi_dev);
94 loop {
95 info!("spi task A");
96 Timer::after_secs(1).await;
97 }
98}
99
100#[embassy_executor::task]
101async fn spi_task_b(spi_bus: &'static Spi1Bus, cs: Output<'static>) {
102 let spi_dev = SpiDevice::new(spi_bus, cs);
103 let _sensor = DummySpiDeviceDriver::new(spi_dev);
104 loop {
105 info!("spi task B");
106 Timer::after_secs(1).await;
107 }
108}
109
110// Dummy I2C device driver, using `embedded-hal-async`
111struct DummyI2cDeviceDriver<I2C: embedded_hal_async::i2c::I2c> {
112 _i2c: I2C,
113}
114
115impl<I2C: embedded_hal_async::i2c::I2c> DummyI2cDeviceDriver<I2C> {
116 fn new(i2c_dev: I2C, _address: u8) -> Self {
117 Self { _i2c: i2c_dev }
118 }
119}
120
121// Dummy SPI device driver, using `embedded-hal-async`
122struct DummySpiDeviceDriver<SPI: embedded_hal_async::spi::SpiDevice> {
123 _spi: SPI,
124}
125
126impl<SPI: embedded_hal_async::spi::SpiDevice> DummySpiDeviceDriver<SPI> {
127 fn new(spi_dev: SPI) -> Self {
128 Self { _spi: spi_dev }
129 }
130}
diff --git a/examples/rp23/src/bin/sharing.rs b/examples/rp23/src/bin/sharing.rs
new file mode 100644
index 000000000..b5ef08147
--- /dev/null
+++ b/examples/rp23/src/bin/sharing.rs
@@ -0,0 +1,165 @@
1//! This example shows some common strategies for sharing resources between tasks.
2//!
3//! We demonstrate five different ways of sharing, covering different use cases:
4//! - Atomics: This method is used for simple values, such as bool and u8..u32
5//! - Blocking Mutex: This is used for sharing non-async things, using Cell/RefCell for interior mutability.
6//! - Async Mutex: This is used for sharing async resources, where you need to hold the lock across await points.
7//! The async Mutex has interior mutability built-in, so no RefCell is needed.
8//! - Cell: For sharing Copy types between tasks running on the same executor.
9//! - RefCell: When you want &mut access to a value shared between tasks running on the same executor.
10//!
11//! More information: https://embassy.dev/book/#_sharing_peripherals_between_tasks
12
13#![no_std]
14#![no_main]
15
16use core::cell::{Cell, RefCell};
17use core::sync::atomic::{AtomicU32, Ordering};
18
19use cortex_m_rt::entry;
20use defmt::info;
21use embassy_executor::{Executor, InterruptExecutor};
22use embassy_rp::block::ImageDef;
23use embassy_rp::clocks::RoscRng;
24use embassy_rp::interrupt::{InterruptExt, Priority};
25use embassy_rp::peripherals::UART0;
26use embassy_rp::uart::{self, InterruptHandler, UartTx};
27use embassy_rp::{bind_interrupts, interrupt};
28use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
29use embassy_sync::{blocking_mutex, mutex};
30use embassy_time::{Duration, Ticker};
31use rand::RngCore;
32use static_cell::{ConstStaticCell, StaticCell};
33use {defmt_rtt as _, panic_probe as _};
34
35#[link_section = ".start_block"]
36#[used]
37pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
38
39// Program metadata for `picotool info`
40#[link_section = ".bi_entries"]
41#[used]
42pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
43 embassy_rp::binary_info_rp_cargo_bin_name!(),
44 embassy_rp::binary_info_rp_cargo_version!(),
45 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
46 embassy_rp::binary_info_rp_program_build_attribute!(),
47];
48
49type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, UART0, uart::Async>>;
50
51struct MyType {
52 inner: u32,
53}
54
55static EXECUTOR_HI: InterruptExecutor = InterruptExecutor::new();
56static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();
57
58// Use Atomics for simple values
59static ATOMIC: AtomicU32 = AtomicU32::new(0);
60
61// Use blocking Mutex with Cell/RefCell for sharing non-async things
62static MUTEX_BLOCKING: blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<MyType>> =
63 blocking_mutex::Mutex::new(RefCell::new(MyType { inner: 0 }));
64
65bind_interrupts!(struct Irqs {
66 UART0_IRQ => InterruptHandler<UART0>;
67});
68
69#[interrupt]
70unsafe fn SWI_IRQ_0() {
71 EXECUTOR_HI.on_interrupt()
72}
73
74#[entry]
75fn main() -> ! {
76 let p = embassy_rp::init(Default::default());
77 info!("Here we go!");
78
79 let uart = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, uart::Config::default());
80 // Use the async Mutex for sharing async things (built-in interior mutability)
81 static UART: StaticCell<UartAsyncMutex> = StaticCell::new();
82 let uart = UART.init(mutex::Mutex::new(uart));
83
84 // High-priority executor: runs in interrupt mode
85 interrupt::SWI_IRQ_0.set_priority(Priority::P3);
86 let spawner = EXECUTOR_HI.start(interrupt::SWI_IRQ_0);
87 spawner.must_spawn(task_a(uart));
88
89 // Low priority executor: runs in thread mode
90 let executor = EXECUTOR_LOW.init(Executor::new());
91 executor.run(|spawner| {
92 // No Mutex needed when sharing between tasks running on the same executor
93
94 // Use Cell for Copy-types
95 static CELL: ConstStaticCell<Cell<[u8; 4]>> = ConstStaticCell::new(Cell::new([0; 4]));
96 let cell = CELL.take();
97
98 // Use RefCell for &mut access
99 static REF_CELL: ConstStaticCell<RefCell<MyType>> = ConstStaticCell::new(RefCell::new(MyType { inner: 0 }));
100 let ref_cell = REF_CELL.take();
101
102 spawner.must_spawn(task_b(uart, cell, ref_cell));
103 spawner.must_spawn(task_c(cell, ref_cell));
104 });
105}
106
107#[embassy_executor::task]
108async fn task_a(uart: &'static UartAsyncMutex) {
109 let mut ticker = Ticker::every(Duration::from_secs(1));
110 loop {
111 let random = RoscRng.next_u32();
112
113 {
114 let mut uart = uart.lock().await;
115 uart.write(b"task a").await.unwrap();
116 // The uart lock is released when it goes out of scope
117 }
118
119 ATOMIC.store(random, Ordering::Relaxed);
120
121 MUTEX_BLOCKING.lock(|x| x.borrow_mut().inner = random);
122
123 ticker.next().await;
124 }
125}
126
127#[embassy_executor::task]
128async fn task_b(uart: &'static UartAsyncMutex, cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {
129 let mut ticker = Ticker::every(Duration::from_secs(1));
130 loop {
131 let random = RoscRng.next_u32();
132
133 uart.lock().await.write(b"task b").await.unwrap();
134
135 cell.set(random.to_be_bytes());
136
137 ref_cell.borrow_mut().inner = random;
138
139 ticker.next().await;
140 }
141}
142
143#[embassy_executor::task]
144async fn task_c(cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {
145 let mut ticker = Ticker::every(Duration::from_secs(1));
146 loop {
147 info!("=======================");
148
149 let atomic_val = ATOMIC.load(Ordering::Relaxed);
150 info!("atomic: {}", atomic_val);
151
152 MUTEX_BLOCKING.lock(|x| {
153 let val = x.borrow().inner;
154 info!("blocking mutex: {}", val);
155 });
156
157 let cell_val = cell.get();
158 info!("cell: {:?}", cell_val);
159
160 let ref_cell_val = ref_cell.borrow().inner;
161 info!("ref_cell: {:?}", ref_cell_val);
162
163 ticker.next().await;
164 }
165}
diff --git a/examples/rp23/src/bin/spi.rs b/examples/rp23/src/bin/spi.rs
new file mode 100644
index 000000000..98aa7622c
--- /dev/null
+++ b/examples/rp23/src/bin/spi.rs
@@ -0,0 +1,61 @@
1//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.
2//!
3//! Example for resistive touch sensor in Waveshare Pico-ResTouch
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::spi::Spi;
12use embassy_rp::{gpio, spi};
13use gpio::{Level, Output};
14use {defmt_rtt as _, panic_probe as _};
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30#[embassy_executor::main]
31async fn main(_spawner: Spawner) {
32 let p = embassy_rp::init(Default::default());
33 info!("Hello World!");
34
35 // Example for resistive touch sensor in Waveshare Pico-ResTouch
36
37 let miso = p.PIN_12;
38 let mosi = p.PIN_11;
39 let clk = p.PIN_10;
40 let touch_cs = p.PIN_16;
41
42 // create SPI
43 let mut config = spi::Config::default();
44 config.frequency = 2_000_000;
45 let mut spi = Spi::new_blocking(p.SPI1, clk, mosi, miso, config);
46
47 // Configure CS
48 let mut cs = Output::new(touch_cs, Level::Low);
49
50 loop {
51 cs.set_low();
52 let mut buf = [0x90, 0x00, 0x00, 0xd0, 0x00, 0x00];
53 spi.blocking_transfer_in_place(&mut buf).unwrap();
54 cs.set_high();
55
56 let x = (buf[1] as u32) << 5 | (buf[2] as u32) >> 3;
57 let y = (buf[4] as u32) << 5 | (buf[5] as u32) >> 3;
58
59 info!("touch: {=u32} {=u32}", x, y);
60 }
61}
diff --git a/examples/rp23/src/bin/spi_async.rs b/examples/rp23/src/bin/spi_async.rs
new file mode 100644
index 000000000..71eaa5c05
--- /dev/null
+++ b/examples/rp23/src/bin/spi_async.rs
@@ -0,0 +1,46 @@
1//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.
2//! No specific hardware is specified in this example. If you connect pin 11 and 12 you should get the same data back.
3
4#![no_std]
5#![no_main]
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_rp::block::ImageDef;
10use embassy_rp::spi::{Config, Spi};
11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _};
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28#[embassy_executor::main]
29async fn main(_spawner: Spawner) {
30 let p = embassy_rp::init(Default::default());
31 info!("Hello World!");
32
33 let miso = p.PIN_12;
34 let mosi = p.PIN_11;
35 let clk = p.PIN_10;
36
37 let mut spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default());
38
39 loop {
40 let tx_buf = [1_u8, 2, 3, 4, 5, 6];
41 let mut rx_buf = [0_u8; 6];
42 spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();
43 info!("{:?}", rx_buf);
44 Timer::after_secs(1).await;
45 }
46}
diff --git a/examples/rp23/src/bin/spi_display.rs b/examples/rp23/src/bin/spi_display.rs
new file mode 100644
index 000000000..2441b1186
--- /dev/null
+++ b/examples/rp23/src/bin/spi_display.rs
@@ -0,0 +1,327 @@
1//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.
2//!
3//! Example written for a display using the ST7789 chip. Possibly the Waveshare Pico-ResTouch
4//! (https://www.waveshare.com/wiki/Pico-ResTouch-LCD-2.8)
5
6#![no_std]
7#![no_main]
8
9use core::cell::RefCell;
10
11use defmt::*;
12use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig;
13use embassy_executor::Spawner;
14use embassy_rp::block::ImageDef;
15use embassy_rp::gpio::{Level, Output};
16use embassy_rp::spi;
17use embassy_rp::spi::{Blocking, Spi};
18use embassy_sync::blocking_mutex::raw::NoopRawMutex;
19use embassy_sync::blocking_mutex::Mutex;
20use embassy_time::Delay;
21use embedded_graphics::image::{Image, ImageRawLE};
22use embedded_graphics::mono_font::ascii::FONT_10X20;
23use embedded_graphics::mono_font::MonoTextStyle;
24use embedded_graphics::pixelcolor::Rgb565;
25use embedded_graphics::prelude::*;
26use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle};
27use embedded_graphics::text::Text;
28use st7789::{Orientation, ST7789};
29use {defmt_rtt as _, panic_probe as _};
30
31#[link_section = ".start_block"]
32#[used]
33pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
34
35// Program metadata for `picotool info`
36#[link_section = ".bi_entries"]
37#[used]
38pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
39 embassy_rp::binary_info_rp_cargo_bin_name!(),
40 embassy_rp::binary_info_rp_cargo_version!(),
41 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
42 embassy_rp::binary_info_rp_program_build_attribute!(),
43];
44
45use crate::my_display_interface::SPIDeviceInterface;
46use crate::touch::Touch;
47
48const DISPLAY_FREQ: u32 = 64_000_000;
49const TOUCH_FREQ: u32 = 200_000;
50
51#[embassy_executor::main]
52async fn main(_spawner: Spawner) {
53 let p = embassy_rp::init(Default::default());
54 info!("Hello World!");
55
56 let bl = p.PIN_13;
57 let rst = p.PIN_15;
58 let display_cs = p.PIN_9;
59 let dcx = p.PIN_8;
60 let miso = p.PIN_12;
61 let mosi = p.PIN_11;
62 let clk = p.PIN_10;
63 let touch_cs = p.PIN_16;
64 //let touch_irq = p.PIN_17;
65
66 // create SPI
67 let mut display_config = spi::Config::default();
68 display_config.frequency = DISPLAY_FREQ;
69 display_config.phase = spi::Phase::CaptureOnSecondTransition;
70 display_config.polarity = spi::Polarity::IdleHigh;
71 let mut touch_config = spi::Config::default();
72 touch_config.frequency = TOUCH_FREQ;
73 touch_config.phase = spi::Phase::CaptureOnSecondTransition;
74 touch_config.polarity = spi::Polarity::IdleHigh;
75
76 let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone());
77 let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi));
78
79 let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config);
80 let touch_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(touch_cs, Level::High), touch_config);
81
82 let mut touch = Touch::new(touch_spi);
83
84 let dcx = Output::new(dcx, Level::Low);
85 let rst = Output::new(rst, Level::Low);
86 // dcx: 0 = command, 1 = data
87
88 // Enable LCD backlight
89 let _bl = Output::new(bl, Level::High);
90
91 // display interface abstraction from SPI and DC
92 let di = SPIDeviceInterface::new(display_spi, dcx);
93
94 // create driver
95 let mut display = ST7789::new(di, rst, 240, 320);
96
97 // initialize
98 display.init(&mut Delay).unwrap();
99
100 // set default orientation
101 display.set_orientation(Orientation::Landscape).unwrap();
102
103 display.clear(Rgb565::BLACK).unwrap();
104
105 let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86);
106 let ferris = Image::new(&raw_image_data, Point::new(34, 68));
107
108 // Display the image
109 ferris.draw(&mut display).unwrap();
110
111 let style = MonoTextStyle::new(&FONT_10X20, Rgb565::GREEN);
112 Text::new(
113 "Hello embedded_graphics \n + embassy + RP2040!",
114 Point::new(20, 200),
115 style,
116 )
117 .draw(&mut display)
118 .unwrap();
119
120 loop {
121 if let Some((x, y)) = touch.read() {
122 let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::BLUE).build();
123
124 Rectangle::new(Point::new(x - 1, y - 1), Size::new(3, 3))
125 .into_styled(style)
126 .draw(&mut display)
127 .unwrap();
128 }
129 }
130}
131
132/// Driver for the XPT2046 resistive touchscreen sensor
133mod touch {
134 use embedded_hal_1::spi::{Operation, SpiDevice};
135
136 struct Calibration {
137 x1: i32,
138 x2: i32,
139 y1: i32,
140 y2: i32,
141 sx: i32,
142 sy: i32,
143 }
144
145 const CALIBRATION: Calibration = Calibration {
146 x1: 3880,
147 x2: 340,
148 y1: 262,
149 y2: 3850,
150 sx: 320,
151 sy: 240,
152 };
153
154 pub struct Touch<SPI: SpiDevice> {
155 spi: SPI,
156 }
157
158 impl<SPI> Touch<SPI>
159 where
160 SPI: SpiDevice,
161 {
162 pub fn new(spi: SPI) -> Self {
163 Self { spi }
164 }
165
166 pub fn read(&mut self) -> Option<(i32, i32)> {
167 let mut x = [0; 2];
168 let mut y = [0; 2];
169 self.spi
170 .transaction(&mut [
171 Operation::Write(&[0x90]),
172 Operation::Read(&mut x),
173 Operation::Write(&[0xd0]),
174 Operation::Read(&mut y),
175 ])
176 .unwrap();
177
178 let x = (u16::from_be_bytes(x) >> 3) as i32;
179 let y = (u16::from_be_bytes(y) >> 3) as i32;
180
181 let cal = &CALIBRATION;
182
183 let x = ((x - cal.x1) * cal.sx / (cal.x2 - cal.x1)).clamp(0, cal.sx);
184 let y = ((y - cal.y1) * cal.sy / (cal.y2 - cal.y1)).clamp(0, cal.sy);
185 if x == 0 && y == 0 {
186 None
187 } else {
188 Some((x, y))
189 }
190 }
191 }
192}
193
194mod my_display_interface {
195 use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand};
196 use embedded_hal_1::digital::OutputPin;
197 use embedded_hal_1::spi::SpiDevice;
198
199 /// SPI display interface.
200 ///
201 /// This combines the SPI peripheral and a data/command pin
202 pub struct SPIDeviceInterface<SPI, DC> {
203 spi: SPI,
204 dc: DC,
205 }
206
207 impl<SPI, DC> SPIDeviceInterface<SPI, DC>
208 where
209 SPI: SpiDevice,
210 DC: OutputPin,
211 {
212 /// Create new SPI interface for communciation with a display driver
213 pub fn new(spi: SPI, dc: DC) -> Self {
214 Self { spi, dc }
215 }
216 }
217
218 impl<SPI, DC> WriteOnlyDataCommand for SPIDeviceInterface<SPI, DC>
219 where
220 SPI: SpiDevice,
221 DC: OutputPin,
222 {
223 fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> {
224 // 1 = data, 0 = command
225 self.dc.set_low().map_err(|_| DisplayError::DCError)?;
226
227 send_u8(&mut self.spi, cmds).map_err(|_| DisplayError::BusWriteError)?;
228 Ok(())
229 }
230
231 fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> {
232 // 1 = data, 0 = command
233 self.dc.set_high().map_err(|_| DisplayError::DCError)?;
234
235 send_u8(&mut self.spi, buf).map_err(|_| DisplayError::BusWriteError)?;
236 Ok(())
237 }
238 }
239
240 fn send_u8<T: SpiDevice>(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> {
241 match words {
242 DataFormat::U8(slice) => spi.write(slice),
243 DataFormat::U16(slice) => {
244 use byte_slice_cast::*;
245 spi.write(slice.as_byte_slice())
246 }
247 DataFormat::U16LE(slice) => {
248 use byte_slice_cast::*;
249 for v in slice.as_mut() {
250 *v = v.to_le();
251 }
252 spi.write(slice.as_byte_slice())
253 }
254 DataFormat::U16BE(slice) => {
255 use byte_slice_cast::*;
256 for v in slice.as_mut() {
257 *v = v.to_be();
258 }
259 spi.write(slice.as_byte_slice())
260 }
261 DataFormat::U8Iter(iter) => {
262 let mut buf = [0; 32];
263 let mut i = 0;
264
265 for v in iter.into_iter() {
266 buf[i] = v;
267 i += 1;
268
269 if i == buf.len() {
270 spi.write(&buf)?;
271 i = 0;
272 }
273 }
274
275 if i > 0 {
276 spi.write(&buf[..i])?;
277 }
278
279 Ok(())
280 }
281 DataFormat::U16LEIter(iter) => {
282 use byte_slice_cast::*;
283 let mut buf = [0; 32];
284 let mut i = 0;
285
286 for v in iter.map(u16::to_le) {
287 buf[i] = v;
288 i += 1;
289
290 if i == buf.len() {
291 spi.write(&buf.as_byte_slice())?;
292 i = 0;
293 }
294 }
295
296 if i > 0 {
297 spi.write(&buf[..i].as_byte_slice())?;
298 }
299
300 Ok(())
301 }
302 DataFormat::U16BEIter(iter) => {
303 use byte_slice_cast::*;
304 let mut buf = [0; 64];
305 let mut i = 0;
306 let len = buf.len();
307
308 for v in iter.map(u16::to_be) {
309 buf[i] = v;
310 i += 1;
311
312 if i == len {
313 spi.write(&buf.as_byte_slice())?;
314 i = 0;
315 }
316 }
317
318 if i > 0 {
319 spi.write(&buf[..i].as_byte_slice())?;
320 }
321
322 Ok(())
323 }
324 _ => unimplemented!(),
325 }
326 }
327}
diff --git a/examples/rp23/src/bin/spi_sdmmc.rs b/examples/rp23/src/bin/spi_sdmmc.rs
new file mode 100644
index 000000000..d7af77a30
--- /dev/null
+++ b/examples/rp23/src/bin/spi_sdmmc.rs
@@ -0,0 +1,98 @@
1//! This example shows how to use `embedded-sdmmc` with the RP2040 chip, over SPI.
2//!
3//! The example will attempt to read a file `MY_FILE.TXT` from the root directory
4//! of the SD card and print its contents.
5
6#![no_std]
7#![no_main]
8
9use defmt::*;
10use embassy_embedded_hal::SetConfig;
11use embassy_executor::Spawner;
12use embassy_rp::block::ImageDef;
13use embassy_rp::spi::Spi;
14use embassy_rp::{gpio, spi};
15use embedded_hal_bus::spi::ExclusiveDevice;
16use embedded_sdmmc::sdcard::{DummyCsPin, SdCard};
17use gpio::{Level, Output};
18use {defmt_rtt as _, panic_probe as _};
19
20#[link_section = ".start_block"]
21#[used]
22pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
23
24// Program metadata for `picotool info`
25#[link_section = ".bi_entries"]
26#[used]
27pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
28 embassy_rp::binary_info_rp_cargo_bin_name!(),
29 embassy_rp::binary_info_rp_cargo_version!(),
30 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
31 embassy_rp::binary_info_rp_program_build_attribute!(),
32];
33
34struct DummyTimesource();
35
36impl embedded_sdmmc::TimeSource for DummyTimesource {
37 fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {
38 embedded_sdmmc::Timestamp {
39 year_since_1970: 0,
40 zero_indexed_month: 0,
41 zero_indexed_day: 0,
42 hours: 0,
43 minutes: 0,
44 seconds: 0,
45 }
46 }
47}
48
49#[embassy_executor::main]
50async fn main(_spawner: Spawner) {
51 embassy_rp::pac::SIO.spinlock(31).write_value(1);
52 let p = embassy_rp::init(Default::default());
53
54 // SPI clock needs to be running at <= 400kHz during initialization
55 let mut config = spi::Config::default();
56 config.frequency = 400_000;
57 let spi = Spi::new_blocking(p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, config);
58 // Use a dummy cs pin here, for embedded-hal SpiDevice compatibility reasons
59 let spi_dev = ExclusiveDevice::new_no_delay(spi, DummyCsPin);
60 // Real cs pin
61 let cs = Output::new(p.PIN_16, Level::High);
62
63 let sdcard = SdCard::new(spi_dev, cs, embassy_time::Delay);
64 info!("Card size is {} bytes", sdcard.num_bytes().unwrap());
65
66 // Now that the card is initialized, the SPI clock can go faster
67 let mut config = spi::Config::default();
68 config.frequency = 16_000_000;
69 sdcard.spi(|dev| dev.bus_mut().set_config(&config)).ok();
70
71 // Now let's look for volumes (also known as partitions) on our block device.
72 // To do this we need a Volume Manager. It will take ownership of the block device.
73 let mut volume_mgr = embedded_sdmmc::VolumeManager::new(sdcard, DummyTimesource());
74
75 // Try and access Volume 0 (i.e. the first partition).
76 // The volume object holds information about the filesystem on that volume.
77 let mut volume0 = volume_mgr.open_volume(embedded_sdmmc::VolumeIdx(0)).unwrap();
78 info!("Volume 0: {:?}", defmt::Debug2Format(&volume0));
79
80 // Open the root directory (mutably borrows from the volume).
81 let mut root_dir = volume0.open_root_dir().unwrap();
82
83 // Open a file called "MY_FILE.TXT" in the root directory
84 // This mutably borrows the directory.
85 let mut my_file = root_dir
86 .open_file_in_dir("MY_FILE.TXT", embedded_sdmmc::Mode::ReadOnly)
87 .unwrap();
88
89 // Print the contents of the file
90 while !my_file.is_eof() {
91 let mut buf = [0u8; 32];
92 if let Ok(n) = my_file.read(&mut buf) {
93 info!("{:a}", buf[..n]);
94 }
95 }
96
97 loop {}
98}
diff --git a/examples/rp23/src/bin/uart.rs b/examples/rp23/src/bin/uart.rs
new file mode 100644
index 000000000..ae00f36dc
--- /dev/null
+++ b/examples/rp23/src/bin/uart.rs
@@ -0,0 +1,40 @@
1//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip.
2//!
3//! No specific hardware is specified in this example. Only output on pin 0 is tested.
4//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used
5//! with its UART port.
6
7#![no_std]
8#![no_main]
9
10use embassy_executor::Spawner;
11use embassy_rp::block::ImageDef;
12use embassy_rp::uart;
13use {defmt_rtt as _, panic_probe as _};
14
15#[link_section = ".start_block"]
16#[used]
17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
18
19// Program metadata for `picotool info`
20#[link_section = ".bi_entries"]
21#[used]
22pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
23 embassy_rp::binary_info_rp_cargo_bin_name!(),
24 embassy_rp::binary_info_rp_cargo_version!(),
25 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
26 embassy_rp::binary_info_rp_program_build_attribute!(),
27];
28
29#[embassy_executor::main]
30async fn main(_spawner: Spawner) {
31 let p = embassy_rp::init(Default::default());
32 let config = uart::Config::default();
33 let mut uart = uart::Uart::new_blocking(p.UART1, p.PIN_4, p.PIN_5, config);
34 uart.blocking_write("Hello World!\r\n".as_bytes()).unwrap();
35
36 loop {
37 uart.blocking_write("hello there!\r\n".as_bytes()).unwrap();
38 cortex_m::asm::delay(1_000_000);
39 }
40}
diff --git a/examples/rp23/src/bin/uart_buffered_split.rs b/examples/rp23/src/bin/uart_buffered_split.rs
new file mode 100644
index 000000000..2b14520d5
--- /dev/null
+++ b/examples/rp23/src/bin/uart_buffered_split.rs
@@ -0,0 +1,73 @@
1//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip.
2//!
3//! No specific hardware is specified in this example. If you connect pin 0 and 1 you should get the same data back.
4//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used
5//! with its UART port.
6
7#![no_std]
8#![no_main]
9
10use defmt::*;
11use embassy_executor::Spawner;
12use embassy_rp::bind_interrupts;
13use embassy_rp::block::ImageDef;
14use embassy_rp::peripherals::UART0;
15use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config};
16use embassy_time::Timer;
17use embedded_io_async::{Read, Write};
18use static_cell::StaticCell;
19use {defmt_rtt as _, panic_probe as _};
20
21#[link_section = ".start_block"]
22#[used]
23pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
24
25// Program metadata for `picotool info`
26#[link_section = ".bi_entries"]
27#[used]
28pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
29 embassy_rp::binary_info_rp_cargo_bin_name!(),
30 embassy_rp::binary_info_rp_cargo_version!(),
31 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
32 embassy_rp::binary_info_rp_program_build_attribute!(),
33];
34
35bind_interrupts!(struct Irqs {
36 UART0_IRQ => BufferedInterruptHandler<UART0>;
37});
38
39#[embassy_executor::main]
40async fn main(spawner: Spawner) {
41 let p = embassy_rp::init(Default::default());
42 let (tx_pin, rx_pin, uart) = (p.PIN_0, p.PIN_1, p.UART0);
43
44 static TX_BUF: StaticCell<[u8; 16]> = StaticCell::new();
45 let tx_buf = &mut TX_BUF.init([0; 16])[..];
46 static RX_BUF: StaticCell<[u8; 16]> = StaticCell::new();
47 let rx_buf = &mut RX_BUF.init([0; 16])[..];
48 let uart = BufferedUart::new(uart, Irqs, tx_pin, rx_pin, tx_buf, rx_buf, Config::default());
49 let (mut tx, rx) = uart.split();
50
51 unwrap!(spawner.spawn(reader(rx)));
52
53 info!("Writing...");
54 loop {
55 let data = [
56 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,
57 29, 30, 31,
58 ];
59 info!("TX {:?}", data);
60 tx.write_all(&data).await.unwrap();
61 Timer::after_secs(1).await;
62 }
63}
64
65#[embassy_executor::task]
66async fn reader(mut rx: BufferedUartRx<'static, UART0>) {
67 info!("Reading...");
68 loop {
69 let mut buf = [0; 31];
70 rx.read_exact(&mut buf).await.unwrap();
71 info!("RX {:?}", buf);
72 }
73}
diff --git a/examples/rp23/src/bin/uart_r503.rs b/examples/rp23/src/bin/uart_r503.rs
new file mode 100644
index 000000000..39a17d305
--- /dev/null
+++ b/examples/rp23/src/bin/uart_r503.rs
@@ -0,0 +1,173 @@
1#![no_std]
2#![no_main]
3
4use defmt::{debug, error, info};
5use embassy_executor::Spawner;
6use embassy_rp::bind_interrupts;
7use embassy_rp::block::ImageDef;
8use embassy_rp::peripherals::UART0;
9use embassy_rp::uart::{Config, DataBits, InterruptHandler as UARTInterruptHandler, Parity, StopBits, Uart};
10use embassy_time::{with_timeout, Duration, Timer};
11use heapless::Vec;
12use {defmt_rtt as _, panic_probe as _};
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28bind_interrupts!(pub struct Irqs {
29 UART0_IRQ => UARTInterruptHandler<UART0>;
30});
31
32const START: u16 = 0xEF01;
33const ADDRESS: u32 = 0xFFFFFFFF;
34
35// ================================================================================
36
37// Data package format
38// Name Length Description
39// ==========================================================================================================
40// Start 2 bytes Fixed value of 0xEF01; High byte transferred first.
41// Address 4 bytes Default value is 0xFFFFFFFF, which can be modified by command.
42// High byte transferred first and at wrong adder value, module
43// will reject to transfer.
44// PID 1 byte 01H Command packet;
45// 02H Data packet; Data packet shall not appear alone in executing
46// processs, must follow command packet or acknowledge packet.
47// 07H Acknowledge packet;
48// 08H End of Data packet.
49// LENGTH 2 bytes Refers to the length of package content (command packets and data packets)
50// plus the length of Checksum (2 bytes). Unit is byte. Max length is 256 bytes.
51// And high byte is transferred first.
52// DATA - It can be commands, data, command’s parameters, acknowledge result, etc.
53// (fingerprint character value, template are all deemed as data);
54// SUM 2 bytes The arithmetic sum of package identifier, package length and all package
55// contens. Overflowing bits are omitted. high byte is transferred first.
56
57// ================================================================================
58
59// Checksum is calculated on 'length (2 bytes) + data (??)'.
60fn compute_checksum(buf: Vec<u8, 32>) -> u16 {
61 let mut checksum = 0u16;
62
63 let check_end = buf.len();
64 let checked_bytes = &buf[6..check_end];
65 for byte in checked_bytes {
66 checksum += (*byte) as u16;
67 }
68 return checksum;
69}
70
71#[embassy_executor::main]
72async fn main(_spawner: Spawner) {
73 info!("Start");
74
75 let p = embassy_rp::init(Default::default());
76
77 // Initialize the fingerprint scanner.
78 let mut config = Config::default();
79 config.baudrate = 57600;
80 config.stop_bits = StopBits::STOP1;
81 config.data_bits = DataBits::DataBits8;
82 config.parity = Parity::ParityNone;
83
84 let (uart, tx_pin, tx_dma, rx_pin, rx_dma) = (p.UART0, p.PIN_16, p.DMA_CH0, p.PIN_17, p.DMA_CH1);
85 let uart = Uart::new(uart, tx_pin, rx_pin, Irqs, tx_dma, rx_dma, config);
86 let (mut tx, mut rx) = uart.split();
87
88 let mut vec_buf: Vec<u8, 32> = heapless::Vec::new();
89 let mut data: Vec<u8, 32> = heapless::Vec::new();
90
91 let mut speeds: Vec<u8, 3> = heapless::Vec::new();
92 let _ = speeds.push(0xC8); // Slow
93 let _ = speeds.push(0x20); // Medium
94 let _ = speeds.push(0x02); // Fast
95
96 // Cycle through the three colours Red, Blue and Purple forever.
97 loop {
98 for colour in 1..=3 {
99 for speed in &speeds {
100 // Set the data first, because the length is dependent on that.
101 // However, we write the length bits before we do the data.
102 data.clear();
103 let _ = data.push(0x01); // ctrl=Breathing light
104 let _ = data.push(*speed);
105 let _ = data.push(colour as u8); // colour=Red, Blue, Purple
106 let _ = data.push(0x00); // times=Infinite
107
108 // Clear buffers
109 vec_buf.clear();
110
111 // START
112 let _ = vec_buf.extend_from_slice(&START.to_be_bytes()[..]);
113
114 // ADDRESS
115 let _ = vec_buf.extend_from_slice(&ADDRESS.to_be_bytes()[..]);
116
117 // PID
118 let _ = vec_buf.extend_from_slice(&[0x01]);
119
120 // LENGTH
121 let len: u16 = (1 + data.len() + 2).try_into().unwrap();
122 let _ = vec_buf.extend_from_slice(&len.to_be_bytes()[..]);
123
124 // COMMAND
125 let _ = vec_buf.push(0x35); // Command: AuraLedConfig
126
127 // DATA
128 let _ = vec_buf.extend_from_slice(&data);
129
130 // SUM
131 let chk = compute_checksum(vec_buf.clone());
132 let _ = vec_buf.extend_from_slice(&chk.to_be_bytes()[..]);
133
134 // =====
135
136 // Send command buffer.
137 let data_write: [u8; 16] = vec_buf.clone().into_array().unwrap();
138 debug!(" write='{:?}'", data_write[..]);
139 match tx.write(&data_write).await {
140 Ok(..) => info!("Write successful."),
141 Err(e) => error!("Write error: {:?}", e),
142 }
143
144 // =====
145
146 // Read command buffer.
147 let mut read_buf: [u8; 1] = [0; 1]; // Can only read one byte at a time!
148 let mut data_read: Vec<u8, 32> = heapless::Vec::new(); // Save buffer.
149
150 info!("Attempting read.");
151 loop {
152 // Some commands, like `Img2Tz()` needs longer, but we hard-code this to 200ms
153 // for this command.
154 match with_timeout(Duration::from_millis(200), rx.read(&mut read_buf)).await {
155 Ok(..) => {
156 // Extract and save read byte.
157 debug!(" r='{=u8:#04x}H' ({:03}D)", read_buf[0], read_buf[0]);
158 let _ = data_read.push(read_buf[0]).unwrap();
159 }
160 Err(..) => break, // TimeoutError -> Ignore.
161 }
162 }
163 info!("Read successful");
164 debug!(" read='{:?}'", data_read[..]);
165
166 Timer::after_secs(3).await;
167 info!("Changing speed.");
168 }
169
170 info!("Changing colour.");
171 }
172 }
173}
diff --git a/examples/rp23/src/bin/uart_unidir.rs b/examples/rp23/src/bin/uart_unidir.rs
new file mode 100644
index 000000000..38210a8d0
--- /dev/null
+++ b/examples/rp23/src/bin/uart_unidir.rs
@@ -0,0 +1,65 @@
1//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip.
2//!
3//! Test TX-only and RX-only on two different UARTs. You need to connect GPIO0 to GPIO5 for
4//! this to work
5//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used
6//! with its UART port.
7
8#![no_std]
9#![no_main]
10
11use defmt::*;
12use embassy_executor::Spawner;
13use embassy_rp::bind_interrupts;
14use embassy_rp::block::ImageDef;
15use embassy_rp::peripherals::UART1;
16use embassy_rp::uart::{Async, Config, InterruptHandler, UartRx, UartTx};
17use embassy_time::Timer;
18use {defmt_rtt as _, panic_probe as _};
19
20#[link_section = ".start_block"]
21#[used]
22pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
23
24// Program metadata for `picotool info`
25#[link_section = ".bi_entries"]
26#[used]
27pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
28 embassy_rp::binary_info_rp_cargo_bin_name!(),
29 embassy_rp::binary_info_rp_cargo_version!(),
30 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
31 embassy_rp::binary_info_rp_program_build_attribute!(),
32];
33
34bind_interrupts!(struct Irqs {
35 UART1_IRQ => InterruptHandler<UART1>;
36});
37
38#[embassy_executor::main]
39async fn main(spawner: Spawner) {
40 let p = embassy_rp::init(Default::default());
41
42 let mut uart_tx = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Config::default());
43 let uart_rx = UartRx::new(p.UART1, p.PIN_5, Irqs, p.DMA_CH1, Config::default());
44
45 unwrap!(spawner.spawn(reader(uart_rx)));
46
47 info!("Writing...");
48 loop {
49 let data = [1u8, 2, 3, 4, 5, 6, 7, 8];
50 info!("TX {:?}", data);
51 uart_tx.write(&data).await.unwrap();
52 Timer::after_secs(1).await;
53 }
54}
55
56#[embassy_executor::task]
57async fn reader(mut rx: UartRx<'static, UART1, Async>) {
58 info!("Reading...");
59 loop {
60 // read a total of 4 transmissions (32 / 8) and then print the result
61 let mut buf = [0; 32];
62 rx.read(&mut buf).await.unwrap();
63 info!("RX {:?}", buf);
64 }
65}
diff --git a/examples/rp23/src/bin/usb_webusb.rs b/examples/rp23/src/bin/usb_webusb.rs
new file mode 100644
index 000000000..f4ecde30e
--- /dev/null
+++ b/examples/rp23/src/bin/usb_webusb.rs
@@ -0,0 +1,170 @@
1//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip.
2//!
3//! This creates a WebUSB capable device that echoes data back to the host.
4//!
5//! To test this in the browser (ideally host this on localhost:8080, to test the landing page
6//! feature):
7//! ```js
8//! (async () => {
9//! const device = await navigator.usb.requestDevice({ filters: [{ vendorId: 0xf569 }] });
10//! await device.open();
11//! await device.claimInterface(1);
12//! device.transferIn(1, 64).then(data => console.log(data));
13//! await device.transferOut(1, new Uint8Array([1,2,3]));
14//! })();
15//! ```
16
17#![no_std]
18#![no_main]
19
20use defmt::info;
21use embassy_executor::Spawner;
22use embassy_futures::join::join;
23use embassy_rp::bind_interrupts;
24use embassy_rp::block::ImageDef;
25use embassy_rp::peripherals::USB;
26use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler};
27use embassy_usb::class::web_usb::{Config as WebUsbConfig, State, Url, WebUsb};
28use embassy_usb::driver::{Driver, Endpoint, EndpointIn, EndpointOut};
29use embassy_usb::msos::{self, windows_version};
30use embassy_usb::{Builder, Config};
31use {defmt_rtt as _, panic_probe as _};
32
33#[link_section = ".start_block"]
34#[used]
35pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
36
37// Program metadata for `picotool info`
38#[link_section = ".bi_entries"]
39#[used]
40pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
41 embassy_rp::binary_info_rp_cargo_bin_name!(),
42 embassy_rp::binary_info_rp_cargo_version!(),
43 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
44 embassy_rp::binary_info_rp_program_build_attribute!(),
45];
46
47bind_interrupts!(struct Irqs {
48 USBCTRL_IRQ => InterruptHandler<USB>;
49});
50
51// This is a randomly generated GUID to allow clients on Windows to find our device
52const DEVICE_INTERFACE_GUIDS: &[&str] = &["{AFB9A6FB-30BA-44BC-9232-806CFC875321}"];
53
54#[embassy_executor::main]
55async fn main(_spawner: Spawner) {
56 let p = embassy_rp::init(Default::default());
57
58 // Create the driver, from the HAL.
59 let driver = UsbDriver::new(p.USB, Irqs);
60
61 // Create embassy-usb Config
62 let mut config = Config::new(0xf569, 0x0001);
63 config.manufacturer = Some("Embassy");
64 config.product = Some("WebUSB example");
65 config.serial_number = Some("12345678");
66 config.max_power = 100;
67 config.max_packet_size_0 = 64;
68
69 // Required for windows compatibility.
70 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
71 config.device_class = 0xff;
72 config.device_sub_class = 0x00;
73 config.device_protocol = 0x00;
74
75 // Create embassy-usb DeviceBuilder using the driver and config.
76 // It needs some buffers for building the descriptors.
77 let mut config_descriptor = [0; 256];
78 let mut bos_descriptor = [0; 256];
79 let mut control_buf = [0; 64];
80 let mut msos_descriptor = [0; 256];
81
82 let webusb_config = WebUsbConfig {
83 max_packet_size: 64,
84 vendor_code: 1,
85 // If defined, shows a landing page which the device manufacturer would like the user to visit in order to control their device. Suggest the user to navigate to this URL when the device is connected.
86 landing_url: Some(Url::new("http://localhost:8080")),
87 };
88
89 let mut state = State::new();
90
91 let mut builder = Builder::new(
92 driver,
93 config,
94 &mut config_descriptor,
95 &mut bos_descriptor,
96 &mut msos_descriptor,
97 &mut control_buf,
98 );
99
100 // Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.
101 // We tell Windows that this entire device is compatible with the "WINUSB" feature,
102 // which causes it to use the built-in WinUSB driver automatically, which in turn
103 // can be used by libusb/rusb software without needing a custom driver or INF file.
104 // In principle you might want to call msos_feature() just on a specific function,
105 // if your device also has other functions that still use standard class drivers.
106 builder.msos_descriptor(windows_version::WIN8_1, 0);
107 builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
108 builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
109 "DeviceInterfaceGUIDs",
110 msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
111 ));
112
113 // Create classes on the builder (WebUSB just needs some setup, but doesn't return anything)
114 WebUsb::configure(&mut builder, &mut state, &webusb_config);
115 // Create some USB bulk endpoints for testing.
116 let mut endpoints = WebEndpoints::new(&mut builder, &webusb_config);
117
118 // Build the builder.
119 let mut usb = builder.build();
120
121 // Run the USB device.
122 let usb_fut = usb.run();
123
124 // Do some WebUSB transfers.
125 let webusb_fut = async {
126 loop {
127 endpoints.wait_connected().await;
128 info!("Connected");
129 endpoints.echo().await;
130 }
131 };
132
133 // Run everything concurrently.
134 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
135 join(usb_fut, webusb_fut).await;
136}
137
138struct WebEndpoints<'d, D: Driver<'d>> {
139 write_ep: D::EndpointIn,
140 read_ep: D::EndpointOut,
141}
142
143impl<'d, D: Driver<'d>> WebEndpoints<'d, D> {
144 fn new(builder: &mut Builder<'d, D>, config: &'d WebUsbConfig<'d>) -> Self {
145 let mut func = builder.function(0xff, 0x00, 0x00);
146 let mut iface = func.interface();
147 let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None);
148
149 let write_ep = alt.endpoint_bulk_in(config.max_packet_size);
150 let read_ep = alt.endpoint_bulk_out(config.max_packet_size);
151
152 WebEndpoints { write_ep, read_ep }
153 }
154
155 // Wait until the device's endpoints are enabled.
156 async fn wait_connected(&mut self) {
157 self.read_ep.wait_enabled().await
158 }
159
160 // Echo data back to the host.
161 async fn echo(&mut self) {
162 let mut buf = [0; 64];
163 loop {
164 let n = self.read_ep.read(&mut buf).await.unwrap();
165 let data = &buf[..n];
166 info!("Data read: {:x}", data);
167 self.write_ep.write(data).await.unwrap();
168 }
169 }
170}
diff --git a/examples/rp23/src/bin/watchdog.rs b/examples/rp23/src/bin/watchdog.rs
new file mode 100644
index 000000000..3ac457219
--- /dev/null
+++ b/examples/rp23/src/bin/watchdog.rs
@@ -0,0 +1,66 @@
1//! This example shows how to use Watchdog in the RP2040 chip.
2//!
3//! It does not work with the RP Pico W board. See wifi_blinky.rs or connect external LED and resistor.
4
5#![no_std]
6#![no_main]
7
8use defmt::info;
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::gpio;
12use embassy_rp::watchdog::*;
13use embassy_time::{Duration, Timer};
14use gpio::{Level, Output};
15use {defmt_rtt as _, panic_probe as _};
16
17#[link_section = ".start_block"]
18#[used]
19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
20
21// Program metadata for `picotool info`
22#[link_section = ".bi_entries"]
23#[used]
24pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
25 embassy_rp::binary_info_rp_cargo_bin_name!(),
26 embassy_rp::binary_info_rp_cargo_version!(),
27 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
28 embassy_rp::binary_info_rp_program_build_attribute!(),
29];
30
31#[embassy_executor::main]
32async fn main(_spawner: Spawner) {
33 let p = embassy_rp::init(Default::default());
34 info!("Hello world!");
35
36 let mut watchdog = Watchdog::new(p.WATCHDOG);
37 let mut led = Output::new(p.PIN_25, Level::Low);
38
39 // Set the LED high for 2 seconds so we know when we're about to start the watchdog
40 led.set_high();
41 Timer::after_secs(2).await;
42
43 // Set to watchdog to reset if it's not fed within 1.05 seconds, and start it
44 watchdog.start(Duration::from_millis(1_050));
45 info!("Started the watchdog timer");
46
47 // Blink once a second for 5 seconds, feed the watchdog timer once a second to avoid a reset
48 for _ in 1..=5 {
49 led.set_low();
50 Timer::after_millis(500).await;
51 led.set_high();
52 Timer::after_millis(500).await;
53 info!("Feeding watchdog");
54 watchdog.feed();
55 }
56
57 info!("Stopped feeding, device will reset in 1.05 seconds");
58 // Blink 10 times per second, not feeding the watchdog.
59 // The processor should reset in 1.05 seconds.
60 loop {
61 led.set_low();
62 Timer::after_millis(100).await;
63 led.set_high();
64 Timer::after_millis(100).await;
65 }
66}
diff --git a/examples/rp23/src/bin/zerocopy.rs b/examples/rp23/src/bin/zerocopy.rs
new file mode 100644
index 000000000..d04e1bf2a
--- /dev/null
+++ b/examples/rp23/src/bin/zerocopy.rs
@@ -0,0 +1,109 @@
1//! This example shows how to use `zerocopy_channel` from `embassy_sync` for
2//! sending large values between two tasks without copying.
3//! The example also shows how to use the RP2040 ADC with DMA.
4#![no_std]
5#![no_main]
6
7use core::sync::atomic::{AtomicU16, Ordering};
8
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_rp::adc::{self, Adc, Async, Config, InterruptHandler};
12use embassy_rp::bind_interrupts;
13use embassy_rp::block::ImageDef;
14use embassy_rp::gpio::Pull;
15use embassy_rp::peripherals::DMA_CH0;
16use embassy_sync::blocking_mutex::raw::NoopRawMutex;
17use embassy_sync::zerocopy_channel::{Channel, Receiver, Sender};
18use embassy_time::{Duration, Ticker, Timer};
19use static_cell::StaticCell;
20use {defmt_rtt as _, panic_probe as _};
21
22#[link_section = ".start_block"]
23#[used]
24pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
25
26// Program metadata for `picotool info`
27#[link_section = ".bi_entries"]
28#[used]
29pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
30 embassy_rp::binary_info_rp_cargo_bin_name!(),
31 embassy_rp::binary_info_rp_cargo_version!(),
32 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
33 embassy_rp::binary_info_rp_program_build_attribute!(),
34];
35
36type SampleBuffer = [u16; 512];
37
38bind_interrupts!(struct Irqs {
39 ADC_IRQ_FIFO => InterruptHandler;
40});
41
42const BLOCK_SIZE: usize = 512;
43const NUM_BLOCKS: usize = 2;
44static MAX: AtomicU16 = AtomicU16::new(0);
45
46struct AdcParts {
47 adc: Adc<'static, Async>,
48 pin: adc::Channel<'static>,
49 dma: DMA_CH0,
50}
51
52#[embassy_executor::main]
53async fn main(spawner: Spawner) {
54 let p = embassy_rp::init(Default::default());
55 info!("Here we go!");
56
57 let adc_parts = AdcParts {
58 adc: Adc::new(p.ADC, Irqs, Config::default()),
59 pin: adc::Channel::new_pin(p.PIN_29, Pull::None),
60 dma: p.DMA_CH0,
61 };
62
63 static BUF: StaticCell<[SampleBuffer; NUM_BLOCKS]> = StaticCell::new();
64 let buf = BUF.init([[0; BLOCK_SIZE]; NUM_BLOCKS]);
65
66 static CHANNEL: StaticCell<Channel<'_, NoopRawMutex, SampleBuffer>> = StaticCell::new();
67 let channel = CHANNEL.init(Channel::new(buf));
68 let (sender, receiver) = channel.split();
69
70 spawner.must_spawn(consumer(receiver));
71 spawner.must_spawn(producer(sender, adc_parts));
72
73 let mut ticker = Ticker::every(Duration::from_secs(1));
74 loop {
75 ticker.next().await;
76 let max = MAX.load(Ordering::Relaxed);
77 info!("latest block's max value: {:?}", max);
78 }
79}
80
81#[embassy_executor::task]
82async fn producer(mut sender: Sender<'static, NoopRawMutex, SampleBuffer>, mut adc: AdcParts) {
83 loop {
84 // Obtain a free buffer from the channel
85 let buf = sender.send().await;
86
87 // Fill it with data
88 adc.adc.read_many(&mut adc.pin, buf, 1, &mut adc.dma).await.unwrap();
89
90 // Notify the channel that the buffer is now ready to be received
91 sender.send_done();
92 }
93}
94
95#[embassy_executor::task]
96async fn consumer(mut receiver: Receiver<'static, NoopRawMutex, SampleBuffer>) {
97 loop {
98 // Receive a buffer from the channel
99 let buf = receiver.receive().await;
100
101 // Simulate using the data, while the producer is filling up the next buffer
102 Timer::after_micros(1000).await;
103 let max = buf.iter().max().unwrap();
104 MAX.store(*max, Ordering::Relaxed);
105
106 // Notify the channel that the buffer is now ready to be reused
107 receiver.receive_done();
108 }
109}