aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorCaleb Jamison <[email protected]>2024-08-07 23:20:26 -0400
committerCaleb Jamison <[email protected]>2024-08-08 21:35:21 -0400
commitb185e02a42ad751ec6c31ffa6a1b87503f15489d (patch)
tree0f0c66747267d24d95b5957b22db7e5c525cb00e /examples
parent891c5ee10584cd990dad529e3506fe1328e4e69d (diff)
Initial rp235x support
Examples have been run, but there is not yet a test suite.
Diffstat (limited to 'examples')
-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.rs64
-rw-r--r--examples/rp23/src/bin/adc_dma.rs70
-rw-r--r--examples/rp23/src/bin/assign_resources.rs95
-rw-r--r--examples/rp23/src/bin/blinky.rs44
-rw-r--r--examples/rp23/src/bin/blinky_two_channels.rs66
-rw-r--r--examples/rp23/src/bin/blinky_two_tasks.rs65
-rw-r--r--examples/rp23/src/bin/button.rs44
-rw-r--r--examples/rp23/src/bin/debounce.rs96
-rw-r--r--examples/rp23/src/bin/flash.rs150
-rw-r--r--examples/rp23/src/bin/gpio_async.rs56
-rw-r--r--examples/rp23/src/bin/gpout.rs53
-rw-r--r--examples/rp23/src/bin/i2c_async.rs126
-rw-r--r--examples/rp23/src/bin/i2c_async_embassy.rs101
-rw-r--r--examples/rp23/src/bin/i2c_blocking.rs90
-rw-r--r--examples/rp23/src/bin/i2c_slave.rs132
-rw-r--r--examples/rp23/src/bin/interrupt.rs110
-rw-r--r--examples/rp23/src/bin/multicore.rs82
-rw-r--r--examples/rp23/src/bin/multiprio.rs161
-rw-r--r--examples/rp23/src/bin/pio_async.rs146
-rw-r--r--examples/rp23/src/bin/pio_dma.rs99
-rw-r--r--examples/rp23/src/bin/pio_hd44780.rs256
-rw-r--r--examples/rp23/src/bin/pio_i2s.rs141
-rw-r--r--examples/rp23/src/bin/pio_pwm.rs134
-rw-r--r--examples/rp23/src/bin/pio_rotary_encoder.rs96
-rw-r--r--examples/rp23/src/bin/pio_servo.rs224
-rw-r--r--examples/rp23/src/bin/pio_stepper.rs184
-rw-r--r--examples/rp23/src/bin/pio_ws2812.rs177
-rw-r--r--examples/rp23/src/bin/pwm.rs45
-rw-r--r--examples/rp23/src/bin/pwm_input.rs42
-rw-r--r--examples/rp23/src/bin/rosc.rs47
-rw-r--r--examples/rp23/src/bin/shared_bus.rs131
-rw-r--r--examples/rp23/src/bin/sharing.rs166
-rw-r--r--examples/rp23/src/bin/spi.rs62
-rw-r--r--examples/rp23/src/bin/spi_async.rs47
-rw-r--r--examples/rp23/src/bin/spi_display.rs328
-rw-r--r--examples/rp23/src/bin/spi_sdmmc.rs99
-rw-r--r--examples/rp23/src/bin/uart.rs40
-rw-r--r--examples/rp23/src/bin/uart_buffered_split.rs74
-rw-r--r--examples/rp23/src/bin/uart_r503.rs174
-rw-r--r--examples/rp23/src/bin/uart_unidir.rs66
-rw-r--r--examples/rp23/src/bin/usb_webusb.rs171
-rw-r--r--examples/rp23/src/bin/watchdog.rs67
-rw-r--r--examples/rp23/src/bin/zerocopy.rs110
49 files changed, 4931 insertions, 2 deletions
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index 2884ca85a..500425315 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..89947ae46
--- /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", "rt-235x", "boot2-none"] }
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..a1a94ca47
--- /dev/null
+++ b/examples/rp23/src/bin/adc.rs
@@ -0,0 +1,64 @@
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::gpio::Pull;
12use embassy_time::Timer;
13use {defmt_rtt as _, panic_probe as _};
14use embassy_rp::block::ImageDef;
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
31bind_interrupts!(struct Irqs {
32 ADC_IRQ_FIFO => InterruptHandler;
33});
34
35#[embassy_executor::main]
36async fn main(_spawner: Spawner) {
37 let p = embassy_rp::init(Default::default());
38 let mut adc = Adc::new(p.ADC, Irqs, Config::default());
39
40 let mut p26 = Channel::new_pin(p.PIN_26, Pull::None);
41 let mut p27 = Channel::new_pin(p.PIN_27, Pull::None);
42 let mut p28 = Channel::new_pin(p.PIN_28, Pull::None);
43 let mut ts = Channel::new_temp_sensor(p.ADC_TEMP_SENSOR);
44
45 loop {
46 let level = adc.read(&mut p26).await.unwrap();
47 info!("Pin 26 ADC: {}", level);
48 let level = adc.read(&mut p27).await.unwrap();
49 info!("Pin 27 ADC: {}", level);
50 let level = adc.read(&mut p28).await.unwrap();
51 info!("Pin 28 ADC: {}", level);
52 let temp = adc.read(&mut ts).await.unwrap();
53 info!("Temp: {} degrees", convert_to_celsius(temp));
54 Timer::after_secs(1).await;
55 }
56}
57
58fn convert_to_celsius(raw_temp: u16) -> f32 {
59 // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet
60 let temp = 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721;
61 let sign = if temp < 0.0 { -1.0 } else { 1.0 };
62 let rounded_temp_x10: i16 = ((temp * 10.0) + 0.5 * sign) as i16;
63 (rounded_temp_x10 as f32) / 10.0
64}
diff --git a/examples/rp23/src/bin/adc_dma.rs b/examples/rp23/src/bin/adc_dma.rs
new file mode 100644
index 000000000..887c8500a
--- /dev/null
+++ b/examples/rp23/src/bin/adc_dma.rs
@@ -0,0 +1,70 @@
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::gpio::Pull;
12use embassy_time::{Duration, Ticker};
13use {defmt_rtt as _, panic_probe as _};
14use embassy_rp::block::ImageDef;
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
31bind_interrupts!(struct Irqs {
32 ADC_IRQ_FIFO => InterruptHandler;
33});
34
35#[embassy_executor::main]
36async fn main(_spawner: Spawner) {
37 let p = embassy_rp::init(Default::default());
38 info!("Here we go!");
39
40 let mut adc = Adc::new(p.ADC, Irqs, Config::default());
41 let mut dma = p.DMA_CH0;
42 let mut pin = Channel::new_pin(p.PIN_26, Pull::Up);
43 let mut pins = [
44 Channel::new_pin(p.PIN_27, Pull::Down),
45 Channel::new_pin(p.PIN_28, Pull::None),
46 Channel::new_pin(p.PIN_29, Pull::Up),
47 Channel::new_temp_sensor(p.ADC_TEMP_SENSOR),
48 ];
49
50 const BLOCK_SIZE: usize = 100;
51 const NUM_CHANNELS: usize = 4;
52 let mut ticker = Ticker::every(Duration::from_secs(1));
53 loop {
54 // Read 100 samples from a single channel
55 let mut buf = [0_u16; BLOCK_SIZE];
56 let div = 479; // 100kHz sample rate (48Mhz / 100kHz - 1)
57 adc.read_many(&mut pin, &mut buf, div, &mut dma).await.unwrap();
58 info!("single: {:?} ...etc", buf[..8]);
59
60 // Read 100 samples from 4 channels interleaved
61 let mut buf = [0_u16; { BLOCK_SIZE * NUM_CHANNELS }];
62 let div = 119; // 100kHz sample rate (48Mhz / 100kHz * 4ch - 1)
63 adc.read_many_multichannel(&mut pins, &mut buf, div, &mut dma)
64 .await
65 .unwrap();
66 info!("multi: {:?} ...etc", buf[..NUM_CHANNELS * 2]);
67
68 ticker.next().await;
69 }
70}
diff --git a/examples/rp23/src/bin/assign_resources.rs b/examples/rp23/src/bin/assign_resources.rs
new file mode 100644
index 000000000..8aea2f35d
--- /dev/null
+++ b/examples/rp23/src/bin/assign_resources.rs
@@ -0,0 +1,95 @@
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::gpio::{Level, Output};
18use embassy_rp::peripherals::{self, PIN_20, PIN_21};
19use embassy_time::Timer;
20use {defmt_rtt as _, panic_probe as _};
21use embassy_rp::block::ImageDef;
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
38#[embassy_executor::main]
39async fn main(spawner: Spawner) {
40 // initialize the peripherals
41 let p = embassy_rp::init(Default::default());
42
43 // 1) Assigning a resource to a task by passing parts of the peripherals.
44 spawner
45 .spawn(double_blinky_manually_assigned(spawner, p.PIN_20, p.PIN_21))
46 .unwrap();
47
48 // 2) Using the assign-resources macro to assign resources to a task.
49 // we perform the split, see further below for the definition of the resources struct
50 let r = split_resources!(p);
51 // and then we can use them
52 spawner.spawn(double_blinky_macro_assigned(spawner, r.leds)).unwrap();
53}
54
55// 1) Assigning a resource to a task by passing parts of the peripherals.
56#[embassy_executor::task]
57async fn double_blinky_manually_assigned(_spawner: Spawner, pin_20: PIN_20, pin_21: PIN_21) {
58 let mut led_20 = Output::new(pin_20, Level::Low);
59 let mut led_21 = Output::new(pin_21, Level::High);
60
61 loop {
62 info!("toggling leds");
63 led_20.toggle();
64 led_21.toggle();
65 Timer::after_secs(1).await;
66 }
67}
68
69// 2) Using the assign-resources macro to assign resources to a task.
70// first we define the resources we want to assign to the task using the assign_resources! macro
71// basically this will split up the peripherals struct into smaller structs, that we define here
72// naming is up to you, make sure your future self understands what you did here
73assign_resources! {
74 leds: Leds{
75 led_10: PIN_10,
76 led_11: PIN_11,
77 }
78 // add more resources to more structs if needed, for example defining one struct for each task
79}
80// this could be done in another file and imported here, but for the sake of simplicity we do it here
81// see https://github.com/adamgreig/assign-resources for more information
82
83// 2) Using the split resources in a task
84#[embassy_executor::task]
85async fn double_blinky_macro_assigned(_spawner: Spawner, r: Leds) {
86 let mut led_10 = Output::new(r.led_10, Level::Low);
87 let mut led_11 = Output::new(r.led_11, Level::High);
88
89 loop {
90 info!("toggling leds");
91 led_10.toggle();
92 led_11.toggle();
93 Timer::after_secs(1).await;
94 }
95}
diff --git a/examples/rp23/src/bin/blinky.rs b/examples/rp23/src/bin/blinky.rs
new file mode 100644
index 000000000..1e3a52085
--- /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::gpio;
11use embassy_time::Timer;
12use gpio::{Level, Output};
13use {defmt_rtt as _, panic_probe as _};
14use embassy_rp::block::ImageDef;
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..aae0283cf
--- /dev/null
+++ b/examples/rp23/src/bin/blinky_two_channels.rs
@@ -0,0 +1,66 @@
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::gpio;
11use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
12use embassy_sync::channel::{Channel, Sender};
13use embassy_time::{Duration, Ticker};
14use gpio::{AnyPin, Level, Output};
15use {defmt_rtt as _, panic_probe as _};
16use embassy_rp::block::ImageDef;
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
32
33enum LedState {
34 Toggle,
35}
36static CHANNEL: Channel<ThreadModeRawMutex, LedState, 64> = Channel::new();
37
38#[embassy_executor::main]
39async fn main(spawner: Spawner) {
40 let p = embassy_rp::init(Default::default());
41 let mut led = Output::new(AnyPin::from(p.PIN_25), Level::High);
42
43 let dt = 100 * 1_000_000;
44 let k = 1.003;
45
46 unwrap!(spawner.spawn(toggle_led(CHANNEL.sender(), Duration::from_nanos(dt))));
47 unwrap!(spawner.spawn(toggle_led(
48 CHANNEL.sender(),
49 Duration::from_nanos((dt as f64 * k) as u64)
50 )));
51
52 loop {
53 match CHANNEL.receive().await {
54 LedState::Toggle => led.toggle(),
55 }
56 }
57}
58
59#[embassy_executor::task(pool_size = 2)]
60async fn toggle_led(control: Sender<'static, ThreadModeRawMutex, LedState, 64>, delay: Duration) {
61 let mut ticker = Ticker::every(delay);
62 loop {
63 control.send(LedState::Toggle).await;
64 ticker.next().await;
65 }
66}
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..aeabb238f
--- /dev/null
+++ b/examples/rp23/src/bin/blinky_two_tasks.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::gpio;
11use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
12use embassy_sync::mutex::Mutex;
13use embassy_time::{Duration, Ticker};
14use gpio::{AnyPin, Level, Output};
15use {defmt_rtt as _, panic_probe as _};
16use embassy_rp::block::ImageDef;
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
32
33type LedType = Mutex<ThreadModeRawMutex, Option<Output<'static>>>;
34static LED: LedType = Mutex::new(None);
35
36#[embassy_executor::main]
37async fn main(spawner: Spawner) {
38 let p = embassy_rp::init(Default::default());
39 // set the content of the global LED reference to the real LED pin
40 let led = Output::new(AnyPin::from(p.PIN_25), Level::High);
41 // inner scope is so that once the mutex is written to, the MutexGuard is dropped, thus the
42 // Mutex is released
43 {
44 *(LED.lock().await) = Some(led);
45 }
46 let dt = 100 * 1_000_000;
47 let k = 1.003;
48
49 unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos(dt))));
50 unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos((dt as f64 * k) as u64))));
51}
52
53#[embassy_executor::task(pool_size = 2)]
54async fn toggle_led(led: &'static LedType, delay: Duration) {
55 let mut ticker = Ticker::every(delay);
56 loop {
57 {
58 let mut led_unlocked = led.lock().await;
59 if let Some(pin_ref) = led_unlocked.as_mut() {
60 pin_ref.toggle();
61 }
62 }
63 ticker.next().await;
64 }
65}
diff --git a/examples/rp23/src/bin/button.rs b/examples/rp23/src/bin/button.rs
new file mode 100644
index 000000000..2a78a19db
--- /dev/null
+++ b/examples/rp23/src/bin/button.rs
@@ -0,0 +1,44 @@
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::gpio::{Input, Level, Output, Pull};
10use {defmt_rtt as _, panic_probe as _};
11use embassy_rp::block::ImageDef;
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
28#[embassy_executor::main]
29async fn main(_spawner: Spawner) {
30 let p = embassy_rp::init(Default::default());
31 let mut led = Output::new(p.PIN_25, Level::Low);
32
33 // Use PIN_28, Pin34 on J0 for RP Pico, as a input.
34 // You need to add your own button.
35 let button = Input::new(p.PIN_28, Pull::Up);
36
37 loop {
38 if button.is_high() {
39 led.set_high();
40 } else {
41 led.set_low();
42 }
43 }
44}
diff --git a/examples/rp23/src/bin/debounce.rs b/examples/rp23/src/bin/debounce.rs
new file mode 100644
index 000000000..0e3b5cb54
--- /dev/null
+++ b/examples/rp23/src/bin/debounce.rs
@@ -0,0 +1,96 @@
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::gpio::{Input, Level, Pull};
10use embassy_time::{with_deadline, Duration, Instant, Timer};
11use {defmt_rtt as _, panic_probe as _};
12use embassy_rp::block::ImageDef;
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
29pub struct Debouncer<'a> {
30 input: Input<'a>,
31 debounce: Duration,
32}
33
34impl<'a> Debouncer<'a> {
35 pub fn new(input: Input<'a>, debounce: Duration) -> Self {
36 Self { input, debounce }
37 }
38
39 pub async fn debounce(&mut self) -> Level {
40 loop {
41 let l1 = self.input.get_level();
42
43 self.input.wait_for_any_edge().await;
44
45 Timer::after(self.debounce).await;
46
47 let l2 = self.input.get_level();
48 if l1 != l2 {
49 break l2;
50 }
51 }
52 }
53}
54
55#[embassy_executor::main]
56async fn main(_spawner: Spawner) {
57 let p = embassy_rp::init(Default::default());
58 let mut btn = Debouncer::new(Input::new(p.PIN_9, Pull::Up), Duration::from_millis(20));
59
60 info!("Debounce Demo");
61
62 loop {
63 // button pressed
64 btn.debounce().await;
65 let start = Instant::now();
66 info!("Button Press");
67
68 match with_deadline(start + Duration::from_secs(1), btn.debounce()).await {
69 // Button Released < 1s
70 Ok(_) => {
71 info!("Button pressed for: {}ms", start.elapsed().as_millis());
72 continue;
73 }
74 // button held for > 1s
75 Err(_) => {
76 info!("Button Held");
77 }
78 }
79
80 match with_deadline(start + Duration::from_secs(5), btn.debounce()).await {
81 // Button released <5s
82 Ok(_) => {
83 info!("Button pressed for: {}ms", start.elapsed().as_millis());
84 continue;
85 }
86 // button held for > >5s
87 Err(_) => {
88 info!("Button Long Held");
89 }
90 }
91
92 // wait for button release before handling another press
93 btn.debounce().await;
94 info!("Button pressed for: {}ms", start.elapsed().as_millis());
95 }
96}
diff --git a/examples/rp23/src/bin/flash.rs b/examples/rp23/src/bin/flash.rs
new file mode 100644
index 000000000..42620ca93
--- /dev/null
+++ b/examples/rp23/src/bin/flash.rs
@@ -0,0 +1,150 @@
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::flash::{Async, ERASE_SIZE, FLASH_BASE};
9use embassy_rp::peripherals::FLASH;
10use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _};
12use embassy_rp::block::ImageDef;
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
29const ADDR_OFFSET: u32 = 0x100000;
30const FLASH_SIZE: usize = 2 * 1024 * 1024;
31
32#[embassy_executor::main]
33async fn main(_spawner: Spawner) {
34 let p = embassy_rp::init(Default::default());
35 info!("Hello World!");
36
37 // add some delay to give an attached debug probe time to parse the
38 // defmt RTT header. Reading that header might touch flash memory, which
39 // interferes with flash write operations.
40 // https://github.com/knurling-rs/defmt/pull/683
41 Timer::after_millis(10).await;
42
43 let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0);
44
45 // Get JEDEC id
46 let jedec = flash.blocking_jedec_id().unwrap();
47 info!("jedec id: 0x{:x}", jedec);
48
49 // Get unique id
50 let mut uid = [0; 8];
51 flash.blocking_unique_id(&mut uid).unwrap();
52 info!("unique id: {:?}", uid);
53
54 erase_write_sector(&mut flash, 0x00);
55
56 multiwrite_bytes(&mut flash, ERASE_SIZE as u32);
57
58 background_read(&mut flash, (ERASE_SIZE * 2) as u32).await;
59
60 loop {}
61}
62
63fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
64 info!(">>>> [multiwrite_bytes]");
65 let mut read_buf = [0u8; ERASE_SIZE];
66 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
67
68 info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
69 info!("Contents start with {=[u8]}", read_buf[0..4]);
70
71 defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
72
73 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
74 info!("Contents after erase starts with {=[u8]}", read_buf[0..4]);
75 if read_buf.iter().any(|x| *x != 0xFF) {
76 defmt::panic!("unexpected");
77 }
78
79 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &[0x01]));
80 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 1, &[0x02]));
81 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 2, &[0x03]));
82 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 3, &[0x04]));
83
84 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
85 info!("Contents after write starts with {=[u8]}", read_buf[0..4]);
86 if &read_buf[0..4] != &[0x01, 0x02, 0x03, 0x04] {
87 defmt::panic!("unexpected");
88 }
89}
90
91fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
92 info!(">>>> [erase_write_sector]");
93 let mut buf = [0u8; ERASE_SIZE];
94 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));
95
96 info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
97 info!("Contents start with {=[u8]}", buf[0..4]);
98
99 defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
100
101 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));
102 info!("Contents after erase starts with {=[u8]}", buf[0..4]);
103 if buf.iter().any(|x| *x != 0xFF) {
104 defmt::panic!("unexpected");
105 }
106
107 for b in buf.iter_mut() {
108 *b = 0xDA;
109 }
110
111 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &buf));
112
113 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));
114 info!("Contents after write starts with {=[u8]}", buf[0..4]);
115 if buf.iter().any(|x| *x != 0xDA) {
116 defmt::panic!("unexpected");
117 }
118}
119
120async fn background_read(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
121 info!(">>>> [background_read]");
122
123 let mut buf = [0u32; 8];
124 defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
125
126 info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
127 info!("Contents start with {=u32:x}", buf[0]);
128
129 defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
130
131 defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
132 info!("Contents after erase starts with {=u32:x}", buf[0]);
133 if buf.iter().any(|x| *x != 0xFFFFFFFF) {
134 defmt::panic!("unexpected");
135 }
136
137 for b in buf.iter_mut() {
138 *b = 0xDABA1234;
139 }
140
141 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, unsafe {
142 core::slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len() * 4)
143 }));
144
145 defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
146 info!("Contents after write starts with {=u32:x}", buf[0]);
147 if buf.iter().any(|x| *x != 0xDABA1234) {
148 defmt::panic!("unexpected");
149 }
150}
diff --git a/examples/rp23/src/bin/gpio_async.rs b/examples/rp23/src/bin/gpio_async.rs
new file mode 100644
index 000000000..360932d62
--- /dev/null
+++ b/examples/rp23/src/bin/gpio_async.rs
@@ -0,0 +1,56 @@
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::gpio;
11use embassy_time::Timer;
12use gpio::{Input, Level, Output, Pull};
13use {defmt_rtt as _, panic_probe as _};
14use embassy_rp::block::ImageDef;
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
31/// It requires an external signal to be manually triggered on PIN 16. For
32/// example, this could be accomplished using an external power source with a
33/// button so that it is possible to toggle the signal from low to high.
34///
35/// This example will begin with turning on the LED on the board and wait for a
36/// high signal on PIN 16. Once the high event/signal occurs the program will
37/// continue and turn off the LED, and then wait for 2 seconds before completing
38/// the loop and starting over again.
39#[embassy_executor::main]
40async fn main(_spawner: Spawner) {
41 let p = embassy_rp::init(Default::default());
42 let mut led = Output::new(p.PIN_25, Level::Low);
43 let mut async_input = Input::new(p.PIN_16, Pull::None);
44
45 loop {
46 info!("wait_for_high. Turn on LED");
47 led.set_high();
48
49 async_input.wait_for_high().await;
50
51 info!("done wait_for_high. Turn off LED");
52 led.set_low();
53
54 Timer::after_secs(2).await;
55 }
56}
diff --git a/examples/rp23/src/bin/gpout.rs b/examples/rp23/src/bin/gpout.rs
new file mode 100644
index 000000000..8d1e4d05f
--- /dev/null
+++ b/examples/rp23/src/bin/gpout.rs
@@ -0,0 +1,53 @@
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::clocks;
11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _};
13use embassy_rp::block::ImageDef;
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
30#[embassy_executor::main]
31async fn main(_spawner: Spawner) {
32 let p = embassy_rp::init(Default::default());
33
34 let gpout3 = clocks::Gpout::new(p.PIN_25);
35 gpout3.set_div(1000, 0);
36 gpout3.enable();
37
38 loop {
39 gpout3.set_src(clocks::GpoutSrc::Sys);
40 info!(
41 "Pin 25 is now outputing CLK_SYS/1000, should be toggling at {}",
42 gpout3.get_freq()
43 );
44 Timer::after_secs(2).await;
45
46 gpout3.set_src(clocks::GpoutSrc::Ref);
47 info!(
48 "Pin 25 is now outputing CLK_REF/1000, should be toggling at {}",
49 gpout3.get_freq()
50 );
51 Timer::after_secs(2).await;
52 }
53}
diff --git a/examples/rp23/src/bin/i2c_async.rs b/examples/rp23/src/bin/i2c_async.rs
new file mode 100644
index 000000000..64f103849
--- /dev/null
+++ b/examples/rp23/src/bin/i2c_async.rs
@@ -0,0 +1,126 @@
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::i2c::{self, Config, InterruptHandler};
13use embassy_rp::peripherals::I2C1;
14use embassy_time::Timer;
15use embedded_hal_async::i2c::I2c;
16use {defmt_rtt as _, panic_probe as _};
17use embassy_rp::block::ImageDef;
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
33
34bind_interrupts!(struct Irqs {
35 I2C1_IRQ => InterruptHandler<I2C1>;
36});
37
38#[allow(dead_code)]
39mod mcp23017 {
40 pub const ADDR: u8 = 0x20; // default addr
41
42 macro_rules! mcpregs {
43 ($($name:ident : $val:expr),* $(,)?) => {
44 $(
45 pub const $name: u8 = $val;
46 )*
47
48 pub fn regname(reg: u8) -> &'static str {
49 match reg {
50 $(
51 $val => stringify!($name),
52 )*
53 _ => panic!("bad reg"),
54 }
55 }
56 }
57 }
58
59 // These are correct for IOCON.BANK=0
60 mcpregs! {
61 IODIRA: 0x00,
62 IPOLA: 0x02,
63 GPINTENA: 0x04,
64 DEFVALA: 0x06,
65 INTCONA: 0x08,
66 IOCONA: 0x0A,
67 GPPUA: 0x0C,
68 INTFA: 0x0E,
69 INTCAPA: 0x10,
70 GPIOA: 0x12,
71 OLATA: 0x14,
72 IODIRB: 0x01,
73 IPOLB: 0x03,
74 GPINTENB: 0x05,
75 DEFVALB: 0x07,
76 INTCONB: 0x09,
77 IOCONB: 0x0B,
78 GPPUB: 0x0D,
79 INTFB: 0x0F,
80 INTCAPB: 0x11,
81 GPIOB: 0x13,
82 OLATB: 0x15,
83 }
84}
85
86#[embassy_executor::main]
87async fn main(_spawner: Spawner) {
88 let p = embassy_rp::init(Default::default());
89
90 let sda = p.PIN_14;
91 let scl = p.PIN_15;
92
93 info!("set up i2c ");
94 let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, Config::default());
95
96 use mcp23017::*;
97
98 info!("init mcp23017 config for IxpandO");
99 // init - a outputs, b inputs
100 i2c.write(ADDR, &[IODIRA, 0x00]).await.unwrap();
101 i2c.write(ADDR, &[IODIRB, 0xff]).await.unwrap();
102 i2c.write(ADDR, &[GPPUB, 0xff]).await.unwrap(); // pullups
103
104 let mut val = 1;
105 loop {
106 let mut portb = [0];
107
108 i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).await.unwrap();
109 info!("portb = {:02x}", portb[0]);
110 i2c.write(mcp23017::ADDR, &[GPIOA, val | portb[0]]).await.unwrap();
111 val = val.rotate_left(1);
112
113 // get a register dump
114 info!("getting register dump");
115 let mut regs = [0; 22];
116 i2c.write_read(ADDR, &[0], &mut regs).await.unwrap();
117 // always get the regdump but only display it if portb'0 is set
118 if portb[0] & 1 != 0 {
119 for (idx, reg) in regs.into_iter().enumerate() {
120 info!("{} => {:02x}", regname(idx as u8), reg);
121 }
122 }
123
124 Timer::after_millis(100).await;
125 }
126}
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..4b14ec6f6
--- /dev/null
+++ b/examples/rp23/src/bin/i2c_async_embassy.rs
@@ -0,0 +1,101 @@
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::i2c::InterruptHandler;
11use {defmt_rtt as _, panic_probe as _};
12use embassy_rp::block::ImageDef;
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
29// Our anonymous hypotetical temperature sensor could be:
30// a 12-bit sensor, with 100ms startup time, range of -40*C - 125*C, and precision 0.25*C
31// It requires no configuration or calibration, works with all i2c bus speeds,
32// never stretches clock or does anything complicated. Replies with one u16.
33// It requires only one write to take it out of suspend mode, and stays on.
34// Often result would be just on 12 bits, but here we'll simplify it to 16.
35
36enum UncomplicatedSensorId {
37 A(UncomplicatedSensorU8),
38 B(UncomplicatedSensorU16),
39}
40enum UncomplicatedSensorU8 {
41 First = 0x48,
42}
43enum UncomplicatedSensorU16 {
44 Other = 0x0049,
45}
46
47impl Into<u16> for UncomplicatedSensorU16 {
48 fn into(self) -> u16 {
49 self as u16
50 }
51}
52impl Into<u16> for UncomplicatedSensorU8 {
53 fn into(self) -> u16 {
54 0x48
55 }
56}
57impl From<UncomplicatedSensorId> for u16 {
58 fn from(t: UncomplicatedSensorId) -> Self {
59 match t {
60 UncomplicatedSensorId::A(x) => x.into(),
61 UncomplicatedSensorId::B(x) => x.into(),
62 }
63 }
64}
65
66embassy_rp::bind_interrupts!(struct Irqs {
67 I2C1_IRQ => InterruptHandler<embassy_rp::peripherals::I2C1>;
68});
69
70#[embassy_executor::main]
71async fn main(_task_spawner: embassy_executor::Spawner) {
72 let p = embassy_rp::init(Default::default());
73 let sda = p.PIN_14;
74 let scl = p.PIN_15;
75 let config = embassy_rp::i2c::Config::default();
76 let mut bus = embassy_rp::i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, config);
77
78 const WAKEYWAKEY: u16 = 0xBABE;
79 let mut result: [u8; 2] = [0, 0];
80 // wait for sensors to initialize
81 embassy_time::Timer::after(embassy_time::Duration::from_millis(100)).await;
82
83 let _res_1 = bus
84 .write_async(UncomplicatedSensorU8::First, WAKEYWAKEY.to_be_bytes())
85 .await;
86 let _res_2 = bus
87 .write_async(UncomplicatedSensorU16::Other, WAKEYWAKEY.to_be_bytes())
88 .await;
89
90 loop {
91 let s1 = UncomplicatedSensorId::A(UncomplicatedSensorU8::First);
92 let s2 = UncomplicatedSensorId::B(UncomplicatedSensorU16::Other);
93 let sensors = [s1, s2];
94 for sensor in sensors {
95 if bus.read_async(sensor, &mut result).await.is_ok() {
96 info!("Result {}", u16::from_be_bytes(result.into()));
97 }
98 }
99 embassy_time::Timer::after(embassy_time::Duration::from_millis(200)).await;
100 }
101}
diff --git a/examples/rp23/src/bin/i2c_blocking.rs b/examples/rp23/src/bin/i2c_blocking.rs
new file mode 100644
index 000000000..d2cccf09b
--- /dev/null
+++ b/examples/rp23/src/bin/i2c_blocking.rs
@@ -0,0 +1,90 @@
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::i2c::{self, Config};
12use embassy_time::Timer;
13use embedded_hal_1::i2c::I2c;
14use {defmt_rtt as _, panic_probe as _};
15use embassy_rp::block::ImageDef;
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
32#[allow(dead_code)]
33mod mcp23017 {
34 pub const ADDR: u8 = 0x20; // default addr
35
36 pub const IODIRA: u8 = 0x00;
37 pub const IPOLA: u8 = 0x02;
38 pub const GPINTENA: u8 = 0x04;
39 pub const DEFVALA: u8 = 0x06;
40 pub const INTCONA: u8 = 0x08;
41 pub const IOCONA: u8 = 0x0A;
42 pub const GPPUA: u8 = 0x0C;
43 pub const INTFA: u8 = 0x0E;
44 pub const INTCAPA: u8 = 0x10;
45 pub const GPIOA: u8 = 0x12;
46 pub const OLATA: u8 = 0x14;
47 pub const IODIRB: u8 = 0x01;
48 pub const IPOLB: u8 = 0x03;
49 pub const GPINTENB: u8 = 0x05;
50 pub const DEFVALB: u8 = 0x07;
51 pub const INTCONB: u8 = 0x09;
52 pub const IOCONB: u8 = 0x0B;
53 pub const GPPUB: u8 = 0x0D;
54 pub const INTFB: u8 = 0x0F;
55 pub const INTCAPB: u8 = 0x11;
56 pub const GPIOB: u8 = 0x13;
57 pub const OLATB: u8 = 0x15;
58}
59
60#[embassy_executor::main]
61async fn main(_spawner: Spawner) {
62 let p = embassy_rp::init(Default::default());
63
64 let sda = p.PIN_14;
65 let scl = p.PIN_15;
66
67 info!("set up i2c ");
68 let mut i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default());
69
70 use mcp23017::*;
71
72 info!("init mcp23017 config for IxpandO");
73 // init - a outputs, b inputs
74 i2c.write(ADDR, &[IODIRA, 0x00]).unwrap();
75 i2c.write(ADDR, &[IODIRB, 0xff]).unwrap();
76 i2c.write(ADDR, &[GPPUB, 0xff]).unwrap(); // pullups
77
78 let mut val = 0xaa;
79 loop {
80 let mut portb = [0];
81
82 i2c.write(mcp23017::ADDR, &[GPIOA, val]).unwrap();
83 i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).unwrap();
84
85 info!("portb = {:02x}", portb[0]);
86 val = !val;
87
88 Timer::after_secs(1).await;
89 }
90}
diff --git a/examples/rp23/src/bin/i2c_slave.rs b/examples/rp23/src/bin/i2c_slave.rs
new file mode 100644
index 000000000..4bf407bcc
--- /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::peripherals::{I2C0, I2C1};
8use embassy_rp::{bind_interrupts, i2c, i2c_slave};
9use embassy_time::Timer;
10use embedded_hal_async::i2c::I2c;
11use {defmt_rtt as _, panic_probe as _};
12use embassy_rp::{block::ImageDef};
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..f46117f95
--- /dev/null
+++ b/examples/rp23/src/bin/interrupt.rs
@@ -0,0 +1,110 @@
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::gpio::Pull;
17use embassy_rp::interrupt;
18use embassy_rp::pwm::{Config, Pwm};
19use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
20use embassy_sync::blocking_mutex::Mutex;
21use embassy_sync::channel::Channel;
22use embassy_time::{Duration, Ticker};
23use portable_atomic::{AtomicU32, Ordering};
24use static_cell::StaticCell;
25use {defmt_rtt as _, panic_probe as _};
26use embassy_rp::block::ImageDef;
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
42
43static COUNTER: AtomicU32 = AtomicU32::new(0);
44static PWM: Mutex<CriticalSectionRawMutex, RefCell<Option<Pwm>>> = Mutex::new(RefCell::new(None));
45static ADC: Mutex<CriticalSectionRawMutex, RefCell<Option<(Adc<Blocking>, adc::Channel)>>> =
46 Mutex::new(RefCell::new(None));
47static ADC_VALUES: Channel<CriticalSectionRawMutex, u16, 2048> = Channel::new();
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 let adc = Adc::new_blocking(p.ADC, Default::default());
55 let p26 = adc::Channel::new_pin(p.PIN_26, Pull::None);
56 ADC.lock(|a| a.borrow_mut().replace((adc, p26)));
57
58 let pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, Default::default());
59 PWM.lock(|p| p.borrow_mut().replace(pwm));
60
61 // Enable the interrupt for pwm slice 4
62 embassy_rp::pac::PWM.irq0_inte().modify(|w| w.set_ch4(true));
63 unsafe {
64 cortex_m::peripheral::NVIC::unmask(interrupt::PWM_IRQ_WRAP_0);
65 }
66
67 // Tasks require their resources to have 'static lifetime
68 // No Mutex needed when sharing within the same executor/prio level
69 static AVG: StaticCell<Cell<u32>> = StaticCell::new();
70 let avg = AVG.init(Default::default());
71 spawner.must_spawn(processing(avg));
72
73 let mut ticker = Ticker::every(Duration::from_secs(1));
74 loop {
75 ticker.next().await;
76 let freq = COUNTER.swap(0, Ordering::Relaxed);
77 info!("pwm freq: {:?} Hz", freq);
78 info!("adc average: {:?}", avg.get());
79
80 // Update the pwm duty cycle, based on the averaged adc reading
81 let mut config = Config::default();
82 config.compare_b = ((avg.get() as f32 / 4095.0) * config.top as f32) as _;
83 PWM.lock(|p| p.borrow_mut().as_mut().unwrap().set_config(&config));
84 }
85}
86
87#[embassy_executor::task]
88async fn processing(avg: &'static Cell<u32>) {
89 let mut buffer: heapless::HistoryBuffer<u16, 100> = Default::default();
90 loop {
91 let val = ADC_VALUES.receive().await;
92 buffer.write(val);
93 let sum: u32 = buffer.iter().map(|x| *x as u32).sum();
94 avg.set(sum / buffer.len() as u32);
95 }
96}
97
98#[interrupt]
99fn PWM_IRQ_WRAP_0() {
100 critical_section::with(|cs| {
101 let mut adc = ADC.borrow(cs).borrow_mut();
102 let (adc, p26) = adc.as_mut().unwrap();
103 let val = adc.blocking_read(p26).unwrap();
104 ADC_VALUES.try_send(val).ok();
105
106 // Clear the interrupt, so we don't immediately re-enter this irq handler
107 PWM.borrow(cs).borrow_mut().as_mut().unwrap().clear_wrapped();
108 });
109 COUNTER.fetch_add(1, Ordering::Relaxed);
110}
diff --git a/examples/rp23/src/bin/multicore.rs b/examples/rp23/src/bin/multicore.rs
new file mode 100644
index 000000000..0b20ecaae
--- /dev/null
+++ b/examples/rp23/src/bin/multicore.rs
@@ -0,0 +1,82 @@
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::gpio::{Level, Output};
11use embassy_rp::multicore::{spawn_core1, Stack};
12use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
13use embassy_sync::channel::Channel;
14use embassy_time::Timer;
15use static_cell::StaticCell;
16use {defmt_rtt as _, panic_probe as _};
17use embassy_rp::block::ImageDef;
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
33
34static mut CORE1_STACK: Stack<4096> = Stack::new();
35static EXECUTOR0: StaticCell<Executor> = StaticCell::new();
36static EXECUTOR1: StaticCell<Executor> = StaticCell::new();
37static CHANNEL: Channel<CriticalSectionRawMutex, LedState, 1> = Channel::new();
38
39enum LedState {
40 On,
41 Off,
42}
43
44#[cortex_m_rt::entry]
45fn main() -> ! {
46 let p = embassy_rp::init(Default::default());
47 let led = Output::new(p.PIN_25, Level::Low);
48
49 spawn_core1(
50 p.CORE1,
51 unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) },
52 move || {
53 let executor1 = EXECUTOR1.init(Executor::new());
54 executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led))));
55 },
56 );
57
58 let executor0 = EXECUTOR0.init(Executor::new());
59 executor0.run(|spawner| unwrap!(spawner.spawn(core0_task())));
60}
61
62#[embassy_executor::task]
63async fn core0_task() {
64 info!("Hello from core 0");
65 loop {
66 CHANNEL.send(LedState::On).await;
67 Timer::after_millis(100).await;
68 CHANNEL.send(LedState::Off).await;
69 Timer::after_millis(400).await;
70 }
71}
72
73#[embassy_executor::task]
74async fn core1_task(mut led: Output<'static>) {
75 info!("Hello from core 1");
76 loop {
77 match CHANNEL.receive().await {
78 LedState::On => led.set_high(),
79 LedState::Off => led.set_low(),
80 }
81 }
82}
diff --git a/examples/rp23/src/bin/multiprio.rs b/examples/rp23/src/bin/multiprio.rs
new file mode 100644
index 000000000..52c801973
--- /dev/null
+++ b/examples/rp23/src/bin/multiprio.rs
@@ -0,0 +1,161 @@
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::interrupt;
63use embassy_rp::interrupt::{InterruptExt, Priority};
64use embassy_time::{Instant, Timer, TICK_HZ};
65use static_cell::StaticCell;
66use {defmt_rtt as _, panic_probe as _};
67use embassy_rp::block::ImageDef;
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
84#[embassy_executor::task]
85async fn run_high() {
86 loop {
87 info!(" [high] tick!");
88 Timer::after_ticks(673740).await;
89 }
90}
91
92#[embassy_executor::task]
93async fn run_med() {
94 loop {
95 let start = Instant::now();
96 info!(" [med] Starting long computation");
97
98 // Spin-wait to simulate a long CPU computation
99 embassy_time::block_for(embassy_time::Duration::from_secs(1)); // ~1 second
100
101 let end = Instant::now();
102 let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ;
103 info!(" [med] done in {} ms", ms);
104
105 Timer::after_ticks(53421).await;
106 }
107}
108
109#[embassy_executor::task]
110async fn run_low() {
111 loop {
112 let start = Instant::now();
113 info!("[low] Starting long computation");
114
115 // Spin-wait to simulate a long CPU computation
116 embassy_time::block_for(embassy_time::Duration::from_secs(2)); // ~2 seconds
117
118 let end = Instant::now();
119 let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ;
120 info!("[low] done in {} ms", ms);
121
122 Timer::after_ticks(82983).await;
123 }
124}
125
126static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new();
127static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new();
128static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();
129
130#[interrupt]
131unsafe fn SWI_IRQ_1() {
132 EXECUTOR_HIGH.on_interrupt()
133}
134
135#[interrupt]
136unsafe fn SWI_IRQ_0() {
137 EXECUTOR_MED.on_interrupt()
138}
139
140#[entry]
141fn main() -> ! {
142 info!("Hello World!");
143
144 let _p = embassy_rp::init(Default::default());
145
146 // High-priority executor: SWI_IRQ_1, priority level 2
147 interrupt::SWI_IRQ_1.set_priority(Priority::P2);
148 let spawner = EXECUTOR_HIGH.start(interrupt::SWI_IRQ_1);
149 unwrap!(spawner.spawn(run_high()));
150
151 // Medium-priority executor: SWI_IRQ_0, priority level 3
152 interrupt::SWI_IRQ_0.set_priority(Priority::P3);
153 let spawner = EXECUTOR_MED.start(interrupt::SWI_IRQ_0);
154 unwrap!(spawner.spawn(run_med()));
155
156 // Low priority executor: runs in thread mode, using WFE/SEV
157 let executor = EXECUTOR_LOW.init(Executor::new());
158 executor.run(|spawner| {
159 unwrap!(spawner.spawn(run_low()));
160 });
161}
diff --git a/examples/rp23/src/bin/pio_async.rs b/examples/rp23/src/bin/pio_async.rs
new file mode 100644
index 000000000..f3a48bbce
--- /dev/null
+++ b/examples/rp23/src/bin/pio_async.rs
@@ -0,0 +1,146 @@
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::peripherals::PIO0;
9use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine};
10use fixed::traits::ToFixed;
11use fixed_macro::types::U56F8;
12use {defmt_rtt as _, panic_probe as _};
13use embassy_rp::block::ImageDef;
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
30bind_interrupts!(struct Irqs {
31 PIO0_IRQ_0 => InterruptHandler<PIO0>;
32});
33
34fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) {
35 // Setup sm0
36
37 // Send data serially to pin
38 let prg = pio_proc::pio_asm!(
39 ".origin 16",
40 "set pindirs, 1",
41 ".wrap_target",
42 "out pins,1 [19]",
43 ".wrap",
44 );
45
46 let mut cfg = Config::default();
47 cfg.use_program(&pio.load_program(&prg.program), &[]);
48 let out_pin = pio.make_pio_pin(pin);
49 cfg.set_out_pins(&[&out_pin]);
50 cfg.set_set_pins(&[&out_pin]);
51 cfg.clock_divider = (U56F8!(125_000_000) / 20 / 200).to_fixed();
52 cfg.shift_out.auto_fill = true;
53 sm.set_config(&cfg);
54}
55
56#[embassy_executor::task]
57async fn pio_task_sm0(mut sm: StateMachine<'static, PIO0, 0>) {
58 sm.set_enable(true);
59
60 let mut v = 0x0f0caffa;
61 loop {
62 sm.tx().wait_push(v).await;
63 v ^= 0xffff;
64 info!("Pushed {:032b} to FIFO", v);
65 }
66}
67
68fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 1>) {
69 // Setupm sm1
70
71 // Read 0b10101 repeatedly until ISR is full
72 let prg = pio_proc::pio_asm!(
73 //
74 ".origin 8",
75 "set x, 0x15",
76 ".wrap_target",
77 "in x, 5 [31]",
78 ".wrap",
79 );
80
81 let mut cfg = Config::default();
82 cfg.use_program(&pio.load_program(&prg.program), &[]);
83 cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
84 cfg.shift_in.auto_fill = true;
85 cfg.shift_in.direction = ShiftDirection::Right;
86 sm.set_config(&cfg);
87}
88
89#[embassy_executor::task]
90async fn pio_task_sm1(mut sm: StateMachine<'static, PIO0, 1>) {
91 sm.set_enable(true);
92 loop {
93 let rx = sm.rx().wait_pull().await;
94 info!("Pulled {:032b} from FIFO", rx);
95 }
96}
97
98fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 2>) {
99 // Setup sm2
100
101 // Repeatedly trigger IRQ 3
102 let prg = pio_proc::pio_asm!(
103 ".origin 0",
104 ".wrap_target",
105 "set x,10",
106 "delay:",
107 "jmp x-- delay [15]",
108 "irq 3 [15]",
109 ".wrap",
110 );
111 let mut cfg = Config::default();
112 cfg.use_program(&pio.load_program(&prg.program), &[]);
113 cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
114 sm.set_config(&cfg);
115}
116
117#[embassy_executor::task]
118async fn pio_task_sm2(mut irq: Irq<'static, PIO0, 3>, mut sm: StateMachine<'static, PIO0, 2>) {
119 sm.set_enable(true);
120 loop {
121 irq.wait().await;
122 info!("IRQ trigged");
123 }
124}
125
126#[embassy_executor::main]
127async fn main(spawner: Spawner) {
128 let p = embassy_rp::init(Default::default());
129 let pio = p.PIO0;
130
131 let Pio {
132 mut common,
133 irq3,
134 mut sm0,
135 mut sm1,
136 mut sm2,
137 ..
138 } = Pio::new(pio, Irqs);
139
140 setup_pio_task_sm0(&mut common, &mut sm0, p.PIN_0);
141 setup_pio_task_sm1(&mut common, &mut sm1);
142 setup_pio_task_sm2(&mut common, &mut sm2);
143 spawner.spawn(pio_task_sm0(sm0)).unwrap();
144 spawner.spawn(pio_task_sm1(sm1)).unwrap();
145 spawner.spawn(pio_task_sm2(irq3, sm2)).unwrap();
146}
diff --git a/examples/rp23/src/bin/pio_dma.rs b/examples/rp23/src/bin/pio_dma.rs
new file mode 100644
index 000000000..d5a831d09
--- /dev/null
+++ b/examples/rp23/src/bin/pio_dma.rs
@@ -0,0 +1,99 @@
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::peripherals::PIO0;
9use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection};
10use embassy_rp::{bind_interrupts, Peripheral};
11use fixed::traits::ToFixed;
12use fixed_macro::types::U56F8;
13use {defmt_rtt as _, panic_probe as _};
14use embassy_rp::block::ImageDef;
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
31bind_interrupts!(struct Irqs {
32 PIO0_IRQ_0 => InterruptHandler<PIO0>;
33});
34
35fn swap_nibbles(v: u32) -> u32 {
36 let v = (v & 0x0f0f_0f0f) << 4 | (v & 0xf0f0_f0f0) >> 4;
37 let v = (v & 0x00ff_00ff) << 8 | (v & 0xff00_ff00) >> 8;
38 (v & 0x0000_ffff) << 16 | (v & 0xffff_0000) >> 16
39}
40
41#[embassy_executor::main]
42async fn main(_spawner: Spawner) {
43 let p = embassy_rp::init(Default::default());
44 let pio = p.PIO0;
45 let Pio {
46 mut common,
47 sm0: mut sm,
48 ..
49 } = Pio::new(pio, Irqs);
50
51 let prg = pio_proc::pio_asm!(
52 ".origin 0",
53 "set pindirs,1",
54 ".wrap_target",
55 "set y,7",
56 "loop:",
57 "out x,4",
58 "in x,4",
59 "jmp y--, loop",
60 ".wrap",
61 );
62
63 let mut cfg = Config::default();
64 cfg.use_program(&common.load_program(&prg.program), &[]);
65 cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed();
66 cfg.shift_in = ShiftConfig {
67 auto_fill: true,
68 threshold: 32,
69 direction: ShiftDirection::Left,
70 };
71 cfg.shift_out = ShiftConfig {
72 auto_fill: true,
73 threshold: 32,
74 direction: ShiftDirection::Right,
75 };
76
77 sm.set_config(&cfg);
78 sm.set_enable(true);
79
80 let mut dma_out_ref = p.DMA_CH0.into_ref();
81 let mut dma_in_ref = p.DMA_CH1.into_ref();
82 let mut dout = [0x12345678u32; 29];
83 for i in 1..dout.len() {
84 dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7;
85 }
86 let mut din = [0u32; 29];
87 loop {
88 let (rx, tx) = sm.rx_tx();
89 join(
90 tx.dma_push(dma_out_ref.reborrow(), &dout),
91 rx.dma_pull(dma_in_ref.reborrow(), &mut din),
92 )
93 .await;
94 for i in 0..din.len() {
95 assert_eq!(din[i], swap_nibbles(dout[i]));
96 }
97 info!("Swapped {} words", dout.len());
98 }
99}
diff --git a/examples/rp23/src/bin/pio_hd44780.rs b/examples/rp23/src/bin/pio_hd44780.rs
new file mode 100644
index 000000000..f601bbc66
--- /dev/null
+++ b/examples/rp23/src/bin/pio_hd44780.rs
@@ -0,0 +1,256 @@
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::dma::{AnyChannel, Channel};
11use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{
13 Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
14};
15use embassy_rp::pwm::{self, Pwm};
16use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef};
17use embassy_time::{Instant, Timer};
18use {defmt_rtt as _, panic_probe as _};
19use embassy_rp::block::ImageDef;
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
35
36bind_interrupts!(pub struct Irqs {
37 PIO0_IRQ_0 => InterruptHandler<PIO0>;
38});
39
40#[embassy_executor::main]
41async fn main(_spawner: Spawner) {
42 // this test assumes a 2x16 HD44780 display attached as follow:
43 // rs = PIN0
44 // rw = PIN1
45 // e = PIN2
46 // db4 = PIN3
47 // db5 = PIN4
48 // db6 = PIN5
49 // db7 = PIN6
50 // additionally a pwm signal for a bias voltage charge pump is provided on pin 15,
51 // allowing direct connection of the display to the RP2040 without level shifters.
52 let p = embassy_rp::init(Default::default());
53
54 let _pwm = Pwm::new_output_b(p.PWM_SLICE7, p.PIN_15, {
55 let mut c = pwm::Config::default();
56 c.divider = 125.into();
57 c.top = 100;
58 c.compare_b = 50;
59 c
60 });
61
62 let mut hd = HD44780::new(
63 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,
64 )
65 .await;
66
67 loop {
68 struct Buf<const N: usize>([u8; N], usize);
69 impl<const N: usize> Write for Buf<N> {
70 fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
71 for b in s.as_bytes() {
72 if self.1 >= N {
73 return Err(core::fmt::Error);
74 }
75 self.0[self.1] = *b;
76 self.1 += 1;
77 }
78 Ok(())
79 }
80 }
81 let mut buf = Buf([0; 16], 0);
82 write!(buf, "up {}s", Instant::now().as_micros() as f32 / 1e6).unwrap();
83 hd.add_line(&buf.0[0..buf.1]).await;
84 Timer::after_secs(1).await;
85 }
86}
87
88pub struct HD44780<'l> {
89 dma: PeripheralRef<'l, AnyChannel>,
90 sm: StateMachine<'l, PIO0, 0>,
91
92 buf: [u8; 40],
93}
94
95impl<'l> HD44780<'l> {
96 pub async fn new(
97 pio: impl Peripheral<P = PIO0> + 'l,
98 irq: Irqs,
99 dma: impl Peripheral<P = impl Channel> + 'l,
100 rs: impl PioPin,
101 rw: impl PioPin,
102 e: impl PioPin,
103 db4: impl PioPin,
104 db5: impl PioPin,
105 db6: impl PioPin,
106 db7: impl PioPin,
107 ) -> HD44780<'l> {
108 into_ref!(dma);
109
110 let Pio {
111 mut common,
112 mut irq0,
113 mut sm0,
114 ..
115 } = Pio::new(pio, irq);
116
117 // takes command words (<wait:24> <command:4> <0:4>)
118 let prg = pio_proc::pio_asm!(
119 r#"
120 .side_set 1 opt
121 .origin 20
122
123 loop:
124 out x, 24
125 delay:
126 jmp x--, delay
127 out pins, 4 side 1
128 out null, 4 side 0
129 jmp !osre, loop
130 irq 0
131 "#,
132 );
133
134 let rs = common.make_pio_pin(rs);
135 let rw = common.make_pio_pin(rw);
136 let e = common.make_pio_pin(e);
137 let db4 = common.make_pio_pin(db4);
138 let db5 = common.make_pio_pin(db5);
139 let db6 = common.make_pio_pin(db6);
140 let db7 = common.make_pio_pin(db7);
141
142 sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]);
143
144 let mut cfg = Config::default();
145 cfg.use_program(&common.load_program(&prg.program), &[&e]);
146 cfg.clock_divider = 125u8.into();
147 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
148 cfg.shift_out = ShiftConfig {
149 auto_fill: true,
150 direction: ShiftDirection::Left,
151 threshold: 32,
152 };
153 cfg.fifo_join = FifoJoin::TxOnly;
154 sm0.set_config(&cfg);
155
156 sm0.set_enable(true);
157 // init to 8 bit thrice
158 sm0.tx().push((50000 << 8) | 0x30);
159 sm0.tx().push((5000 << 8) | 0x30);
160 sm0.tx().push((200 << 8) | 0x30);
161 // init 4 bit
162 sm0.tx().push((200 << 8) | 0x20);
163 // set font and lines
164 sm0.tx().push((50 << 8) | 0x20);
165 sm0.tx().push(0b1100_0000);
166
167 irq0.wait().await;
168 sm0.set_enable(false);
169
170 // takes command sequences (<rs:1> <count:7>, data...)
171 // many side sets are only there to free up a delay bit!
172 let prg = pio_proc::pio_asm!(
173 r#"
174 .origin 27
175 .side_set 1
176
177 .wrap_target
178 pull side 0
179 out x 1 side 0 ; !rs
180 out y 7 side 0 ; #data - 1
181
182 ; rs/rw to e: >= 60ns
183 ; e high time: >= 500ns
184 ; e low time: >= 500ns
185 ; read data valid after e falling: ~5ns
186 ; write data hold after e falling: ~10ns
187
188 loop:
189 pull side 0
190 jmp !x data side 0
191 command:
192 set pins 0b00 side 0
193 jmp shift side 0
194 data:
195 set pins 0b01 side 0
196 shift:
197 out pins 4 side 1 [9]
198 nop side 0 [9]
199 out pins 4 side 1 [9]
200 mov osr null side 0 [7]
201 out pindirs 4 side 0
202 set pins 0b10 side 0
203 busy:
204 nop side 1 [9]
205 jmp pin more side 0 [9]
206 mov osr ~osr side 1 [9]
207 nop side 0 [4]
208 out pindirs 4 side 0
209 jmp y-- loop side 0
210 .wrap
211 more:
212 nop side 1 [9]
213 jmp busy side 0 [9]
214 "#
215 );
216
217 let mut cfg = Config::default();
218 cfg.use_program(&common.load_program(&prg.program), &[&e]);
219 cfg.clock_divider = 8u8.into(); // ~64ns/insn
220 cfg.set_jmp_pin(&db7);
221 cfg.set_set_pins(&[&rs, &rw]);
222 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
223 cfg.shift_out.direction = ShiftDirection::Left;
224 cfg.fifo_join = FifoJoin::TxOnly;
225 sm0.set_config(&cfg);
226
227 sm0.set_enable(true);
228
229 // display on and cursor on and blinking, reset display
230 sm0.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await;
231
232 Self {
233 dma: dma.map_into(),
234 sm: sm0,
235 buf: [0x20; 40],
236 }
237 }
238
239 pub async fn add_line(&mut self, s: &[u8]) {
240 // move cursor to 0:0, prepare 16 characters
241 self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]);
242 // move line 2 up
243 self.buf.copy_within(22..38, 3);
244 // move cursor to 1:0, prepare 16 characters
245 self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]);
246 // file line 2 with spaces
247 self.buf[22..38].fill(0x20);
248 // copy input line
249 let len = s.len().min(16);
250 self.buf[22..22 + len].copy_from_slice(&s[0..len]);
251 // set cursor to 1:15
252 self.buf[38..].copy_from_slice(&[0x80, 0xcf]);
253
254 self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await;
255 }
256}
diff --git a/examples/rp23/src/bin/pio_i2s.rs b/examples/rp23/src/bin/pio_i2s.rs
new file mode 100644
index 000000000..b12b050e6
--- /dev/null
+++ b/examples/rp23/src/bin/pio_i2s.rs
@@ -0,0 +1,141 @@
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::peripherals::PIO0;
17use embassy_rp::pio::{Config, FifoJoin, InterruptHandler, Pio, ShiftConfig, ShiftDirection};
18use embassy_rp::{bind_interrupts, Peripheral};
19use fixed::traits::ToFixed;
20use static_cell::StaticCell;
21use {defmt_rtt as _, panic_probe as _};
22use embassy_rp::block::ImageDef;
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
38
39bind_interrupts!(struct Irqs {
40 PIO0_IRQ_0 => InterruptHandler<PIO0>;
41});
42
43const SAMPLE_RATE: u32 = 48_000;
44
45#[embassy_executor::main]
46async fn main(_spawner: Spawner) {
47 let p = embassy_rp::init(Default::default());
48
49 // Setup pio state machine for i2s output
50 let mut pio = Pio::new(p.PIO0, Irqs);
51
52 #[rustfmt::skip]
53 let pio_program = pio_proc::pio_asm!(
54 ".side_set 2",
55 " set x, 14 side 0b01", // side 0bWB - W = Word Clock, B = Bit Clock
56 "left_data:",
57 " out pins, 1 side 0b00",
58 " jmp x-- left_data side 0b01",
59 " out pins 1 side 0b10",
60 " set x, 14 side 0b11",
61 "right_data:",
62 " out pins 1 side 0b10",
63 " jmp x-- right_data side 0b11",
64 " out pins 1 side 0b00",
65 );
66
67 let bit_clock_pin = p.PIN_18;
68 let left_right_clock_pin = p.PIN_19;
69 let data_pin = p.PIN_20;
70
71 let data_pin = pio.common.make_pio_pin(data_pin);
72 let bit_clock_pin = pio.common.make_pio_pin(bit_clock_pin);
73 let left_right_clock_pin = pio.common.make_pio_pin(left_right_clock_pin);
74
75 let cfg = {
76 let mut cfg = Config::default();
77 cfg.use_program(
78 &pio.common.load_program(&pio_program.program),
79 &[&bit_clock_pin, &left_right_clock_pin],
80 );
81 cfg.set_out_pins(&[&data_pin]);
82 const BIT_DEPTH: u32 = 16;
83 const CHANNELS: u32 = 2;
84 let clock_frequency = SAMPLE_RATE * BIT_DEPTH * CHANNELS;
85 cfg.clock_divider = (125_000_000. / clock_frequency as f64 / 2.).to_fixed();
86 cfg.shift_out = ShiftConfig {
87 threshold: 32,
88 direction: ShiftDirection::Left,
89 auto_fill: true,
90 };
91 // join fifos to have twice the time to start the next dma transfer
92 cfg.fifo_join = FifoJoin::TxOnly;
93 cfg
94 };
95 pio.sm0.set_config(&cfg);
96 pio.sm0.set_pin_dirs(
97 embassy_rp::pio::Direction::Out,
98 &[&data_pin, &left_right_clock_pin, &bit_clock_pin],
99 );
100
101 // create two audio buffers (back and front) which will take turns being
102 // filled with new audio data and being sent to the pio fifo using dma
103 const BUFFER_SIZE: usize = 960;
104 static DMA_BUFFER: StaticCell<[u32; BUFFER_SIZE * 2]> = StaticCell::new();
105 let dma_buffer = DMA_BUFFER.init_with(|| [0u32; BUFFER_SIZE * 2]);
106 let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE);
107
108 // start pio state machine
109 pio.sm0.set_enable(true);
110 let tx = pio.sm0.tx();
111 let mut dma_ref = p.DMA_CH0.into_ref();
112
113 let mut fade_value: i32 = 0;
114 let mut phase: i32 = 0;
115
116 loop {
117 // trigger transfer of front buffer data to the pio fifo
118 // but don't await the returned future, yet
119 let dma_future = tx.dma_push(dma_ref.reborrow(), front_buffer);
120
121 // fade in audio
122 let fade_target = i32::MAX;
123
124 // fill back buffer with fresh audio samples before awaiting the dma future
125 for s in back_buffer.iter_mut() {
126 // exponential approach of fade_value => fade_target
127 fade_value += (fade_target - fade_value) >> 14;
128 // generate triangle wave with amplitude and frequency based on fade value
129 phase = (phase + (fade_value >> 22)) & 0xffff;
130 let triangle_sample = (phase as i16 as i32).abs() - 16384;
131 let sample = (triangle_sample * (fade_value >> 15)) >> 16;
132 // duplicate mono sample into lower and upper half of dma word
133 *s = (sample as u16 as u32) * 0x10001;
134 }
135
136 // now await the dma future. once the dma finishes, the next buffer needs to be queued
137 // within DMA_DEPTH / SAMPLE_RATE = 8 / 48000 seconds = 166us
138 dma_future.await;
139 mem::swap(&mut back_buffer, &mut front_buffer);
140 }
141}
diff --git a/examples/rp23/src/bin/pio_pwm.rs b/examples/rp23/src/bin/pio_pwm.rs
new file mode 100644
index 000000000..c8e834eae
--- /dev/null
+++ b/examples/rp23/src/bin/pio_pwm.rs
@@ -0,0 +1,134 @@
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::gpio::Level;
9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine};
11use embassy_rp::{bind_interrupts, clocks};
12use embassy_time::Timer;
13use pio::InstructionOperands;
14use {defmt_rtt as _, panic_probe as _};
15use embassy_rp::block::ImageDef;
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
32const REFRESH_INTERVAL: u64 = 20000;
33
34bind_interrupts!(struct Irqs {
35 PIO0_IRQ_0 => InterruptHandler<PIO0>;
36});
37
38pub fn to_pio_cycles(duration: Duration) -> u32 {
39 (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
40}
41
42pub struct PwmPio<'d, T: Instance, const SM: usize> {
43 sm: StateMachine<'d, T, SM>,
44}
45
46impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
47 pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
48 let prg = pio_proc::pio_asm!(
49 ".side_set 1 opt"
50 "pull noblock side 0"
51 "mov x, osr"
52 "mov y, isr"
53 "countloop:"
54 "jmp x!=y noset"
55 "jmp skip side 1"
56 "noset:"
57 "nop"
58 "skip:"
59 "jmp y-- countloop"
60 );
61
62 pio.load_program(&prg.program);
63 let pin = pio.make_pio_pin(pin);
64 sm.set_pins(Level::High, &[&pin]);
65 sm.set_pin_dirs(Direction::Out, &[&pin]);
66
67 let mut cfg = Config::default();
68 cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
69
70 sm.set_config(&cfg);
71
72 Self { sm }
73 }
74
75 pub fn start(&mut self) {
76 self.sm.set_enable(true);
77 }
78
79 pub fn stop(&mut self) {
80 self.sm.set_enable(false);
81 }
82
83 pub fn set_period(&mut self, duration: Duration) {
84 let is_enabled = self.sm.is_enabled();
85 while !self.sm.tx().empty() {} // Make sure that the queue is empty
86 self.sm.set_enable(false);
87 self.sm.tx().push(to_pio_cycles(duration));
88 unsafe {
89 self.sm.exec_instr(
90 InstructionOperands::PULL {
91 if_empty: false,
92 block: false,
93 }
94 .encode(),
95 );
96 self.sm.exec_instr(
97 InstructionOperands::OUT {
98 destination: ::pio::OutDestination::ISR,
99 bit_count: 32,
100 }
101 .encode(),
102 );
103 };
104 if is_enabled {
105 self.sm.set_enable(true) // Enable if previously enabled
106 }
107 }
108
109 pub fn set_level(&mut self, level: u32) {
110 self.sm.tx().push(level);
111 }
112
113 pub fn write(&mut self, duration: Duration) {
114 self.set_level(to_pio_cycles(duration));
115 }
116}
117
118#[embassy_executor::main]
119async fn main(_spawner: Spawner) {
120 let p = embassy_rp::init(Default::default());
121 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
122
123 // Note that PIN_25 is the led pin on the Pico
124 let mut pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_25);
125 pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL));
126 pwm_pio.start();
127
128 let mut duration = 0;
129 loop {
130 duration = (duration + 1) % 1000;
131 pwm_pio.write(Duration::from_micros(duration));
132 Timer::after_millis(1).await;
133 }
134}
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..971b55c9e
--- /dev/null
+++ b/examples/rp23/src/bin/pio_rotary_encoder.rs
@@ -0,0 +1,96 @@
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::gpio::Pull;
9use embassy_rp::peripherals::PIO0;
10use embassy_rp::{bind_interrupts, pio};
11use fixed::traits::ToFixed;
12use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine};
13use {defmt_rtt as _, panic_probe as _};
14use embassy_rp::block::ImageDef;
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
31bind_interrupts!(struct Irqs {
32 PIO0_IRQ_0 => InterruptHandler<PIO0>;
33});
34
35pub struct PioEncoder<'d, T: Instance, const SM: usize> {
36 sm: StateMachine<'d, T, SM>,
37}
38
39impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> {
40 pub fn new(
41 pio: &mut Common<'d, T>,
42 mut sm: StateMachine<'d, T, SM>,
43 pin_a: impl PioPin,
44 pin_b: impl PioPin,
45 ) -> Self {
46 let mut pin_a = pio.make_pio_pin(pin_a);
47 let mut pin_b = pio.make_pio_pin(pin_b);
48 pin_a.set_pull(Pull::Up);
49 pin_b.set_pull(Pull::Up);
50 sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]);
51
52 let prg = pio_proc::pio_asm!("wait 1 pin 1", "wait 0 pin 1", "in pins, 2", "push",);
53
54 let mut cfg = Config::default();
55 cfg.set_in_pins(&[&pin_a, &pin_b]);
56 cfg.fifo_join = FifoJoin::RxOnly;
57 cfg.shift_in.direction = ShiftDirection::Left;
58 cfg.clock_divider = 10_000.to_fixed();
59 cfg.use_program(&pio.load_program(&prg.program), &[]);
60 sm.set_config(&cfg);
61 sm.set_enable(true);
62 Self { sm }
63 }
64
65 pub async fn read(&mut self) -> Direction {
66 loop {
67 match self.sm.rx().wait_pull().await {
68 0 => return Direction::CounterClockwise,
69 1 => return Direction::Clockwise,
70 _ => {}
71 }
72 }
73 }
74}
75
76pub enum Direction {
77 Clockwise,
78 CounterClockwise,
79}
80
81#[embassy_executor::main]
82async fn main(_spawner: Spawner) {
83 let p = embassy_rp::init(Default::default());
84 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
85
86 let mut encoder = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5);
87
88 let mut count = 0;
89 loop {
90 info!("Count: {}", count);
91 count += match encoder.read().await {
92 Direction::Clockwise => 1,
93 Direction::CounterClockwise => -1,
94 };
95 }
96}
diff --git a/examples/rp23/src/bin/pio_servo.rs b/examples/rp23/src/bin/pio_servo.rs
new file mode 100644
index 000000000..67e52019a
--- /dev/null
+++ b/examples/rp23/src/bin/pio_servo.rs
@@ -0,0 +1,224 @@
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::gpio::Level;
9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine};
11use embassy_rp::{bind_interrupts, clocks};
12use embassy_time::Timer;
13use pio::InstructionOperands;
14use {defmt_rtt as _, panic_probe as _};
15use embassy_rp::block::ImageDef;
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
32const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo
33const DEFAULT_MAX_PULSE_WIDTH: u64 = 2000; // uncalibrated default, the longest duty cycle sent to a servo
34const DEFAULT_MAX_DEGREE_ROTATION: u64 = 160; // 160 degrees is typical
35const REFRESH_INTERVAL: u64 = 20000; // The period of each cycle
36
37bind_interrupts!(struct Irqs {
38 PIO0_IRQ_0 => InterruptHandler<PIO0>;
39});
40
41pub fn to_pio_cycles(duration: Duration) -> u32 {
42 (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
43}
44
45pub struct PwmPio<'d, T: Instance, const SM: usize> {
46 sm: StateMachine<'d, T, SM>,
47}
48
49impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
50 pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
51 let prg = pio_proc::pio_asm!(
52 ".side_set 1 opt"
53 "pull noblock side 0"
54 "mov x, osr"
55 "mov y, isr"
56 "countloop:"
57 "jmp x!=y noset"
58 "jmp skip side 1"
59 "noset:"
60 "nop"
61 "skip:"
62 "jmp y-- countloop"
63 );
64
65 pio.load_program(&prg.program);
66 let pin = pio.make_pio_pin(pin);
67 sm.set_pins(Level::High, &[&pin]);
68 sm.set_pin_dirs(Direction::Out, &[&pin]);
69
70 let mut cfg = Config::default();
71 cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
72
73 sm.set_config(&cfg);
74
75 Self { sm }
76 }
77
78 pub fn start(&mut self) {
79 self.sm.set_enable(true);
80 }
81
82 pub fn stop(&mut self) {
83 self.sm.set_enable(false);
84 }
85
86 pub fn set_period(&mut self, duration: Duration) {
87 let is_enabled = self.sm.is_enabled();
88 while !self.sm.tx().empty() {} // Make sure that the queue is empty
89 self.sm.set_enable(false);
90 self.sm.tx().push(to_pio_cycles(duration));
91 unsafe {
92 self.sm.exec_instr(
93 InstructionOperands::PULL {
94 if_empty: false,
95 block: false,
96 }
97 .encode(),
98 );
99 self.sm.exec_instr(
100 InstructionOperands::OUT {
101 destination: ::pio::OutDestination::ISR,
102 bit_count: 32,
103 }
104 .encode(),
105 );
106 };
107 if is_enabled {
108 self.sm.set_enable(true) // Enable if previously enabled
109 }
110 }
111
112 pub fn set_level(&mut self, level: u32) {
113 self.sm.tx().push(level);
114 }
115
116 pub fn write(&mut self, duration: Duration) {
117 self.set_level(to_pio_cycles(duration));
118 }
119}
120
121pub struct ServoBuilder<'d, T: Instance, const SM: usize> {
122 pwm: PwmPio<'d, T, SM>,
123 period: Duration,
124 min_pulse_width: Duration,
125 max_pulse_width: Duration,
126 max_degree_rotation: u64,
127}
128
129impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> {
130 pub fn new(pwm: PwmPio<'d, T, SM>) -> Self {
131 Self {
132 pwm,
133 period: Duration::from_micros(REFRESH_INTERVAL),
134 min_pulse_width: Duration::from_micros(DEFAULT_MIN_PULSE_WIDTH),
135 max_pulse_width: Duration::from_micros(DEFAULT_MAX_PULSE_WIDTH),
136 max_degree_rotation: DEFAULT_MAX_DEGREE_ROTATION,
137 }
138 }
139
140 pub fn set_period(mut self, duration: Duration) -> Self {
141 self.period = duration;
142 self
143 }
144
145 pub fn set_min_pulse_width(mut self, duration: Duration) -> Self {
146 self.min_pulse_width = duration;
147 self
148 }
149
150 pub fn set_max_pulse_width(mut self, duration: Duration) -> Self {
151 self.max_pulse_width = duration;
152 self
153 }
154
155 pub fn set_max_degree_rotation(mut self, degree: u64) -> Self {
156 self.max_degree_rotation = degree;
157 self
158 }
159
160 pub fn build(mut self) -> Servo<'d, T, SM> {
161 self.pwm.set_period(self.period);
162 Servo {
163 pwm: self.pwm,
164 min_pulse_width: self.min_pulse_width,
165 max_pulse_width: self.max_pulse_width,
166 max_degree_rotation: self.max_degree_rotation,
167 }
168 }
169}
170
171pub struct Servo<'d, T: Instance, const SM: usize> {
172 pwm: PwmPio<'d, T, SM>,
173 min_pulse_width: Duration,
174 max_pulse_width: Duration,
175 max_degree_rotation: u64,
176}
177
178impl<'d, T: Instance, const SM: usize> Servo<'d, T, SM> {
179 pub fn start(&mut self) {
180 self.pwm.start();
181 }
182
183 pub fn stop(&mut self) {
184 self.pwm.stop();
185 }
186
187 pub fn write_time(&mut self, duration: Duration) {
188 self.pwm.write(duration);
189 }
190
191 pub fn rotate(&mut self, degree: u64) {
192 let degree_per_nano_second = (self.max_pulse_width.as_nanos() as u64 - self.min_pulse_width.as_nanos() as u64)
193 / self.max_degree_rotation;
194 let mut duration =
195 Duration::from_nanos(degree * degree_per_nano_second + self.min_pulse_width.as_nanos() as u64);
196 if self.max_pulse_width < duration {
197 duration = self.max_pulse_width;
198 }
199
200 self.pwm.write(duration);
201 }
202}
203
204#[embassy_executor::main]
205async fn main(_spawner: Spawner) {
206 let p = embassy_rp::init(Default::default());
207 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
208
209 let pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_1);
210 let mut servo = ServoBuilder::new(pwm_pio)
211 .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo
212 .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment.
213 .set_max_pulse_width(Duration::from_micros(2600)) // Along with this value.
214 .build();
215
216 servo.start();
217
218 let mut degree = 0;
219 loop {
220 degree = (degree + 1) % 120;
221 servo.rotate(degree);
222 Timer::after_millis(50).await;
223 }
224}
diff --git a/examples/rp23/src/bin/pio_stepper.rs b/examples/rp23/src/bin/pio_stepper.rs
new file mode 100644
index 000000000..9cbf0bd92
--- /dev/null
+++ b/examples/rp23/src/bin/pio_stepper.rs
@@ -0,0 +1,184 @@
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::peripherals::PIO0;
12use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Irq, Pio, PioPin, StateMachine};
13use embassy_time::{with_timeout, Duration, Timer};
14use fixed::traits::ToFixed;
15use fixed::types::extra::U8;
16use fixed::FixedU32;
17use {defmt_rtt as _, panic_probe as _};
18use embassy_rp::block::ImageDef;
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
34
35bind_interrupts!(struct Irqs {
36 PIO0_IRQ_0 => InterruptHandler<PIO0>;
37});
38
39pub struct PioStepper<'d, T: Instance, const SM: usize> {
40 irq: Irq<'d, T, SM>,
41 sm: StateMachine<'d, T, SM>,
42}
43
44impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> {
45 pub fn new(
46 pio: &mut Common<'d, T>,
47 mut sm: StateMachine<'d, T, SM>,
48 irq: Irq<'d, T, SM>,
49 pin0: impl PioPin,
50 pin1: impl PioPin,
51 pin2: impl PioPin,
52 pin3: impl PioPin,
53 ) -> Self {
54 let prg = pio_proc::pio_asm!(
55 "pull block",
56 "mov x, osr",
57 "pull block",
58 "mov y, osr",
59 "jmp !x end",
60 "loop:",
61 "jmp !osre step",
62 "mov osr, y",
63 "step:",
64 "out pins, 4 [31]"
65 "jmp x-- loop",
66 "end:",
67 "irq 0 rel"
68 );
69 let pin0 = pio.make_pio_pin(pin0);
70 let pin1 = pio.make_pio_pin(pin1);
71 let pin2 = pio.make_pio_pin(pin2);
72 let pin3 = pio.make_pio_pin(pin3);
73 sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]);
74 let mut cfg = Config::default();
75 cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]);
76 cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed();
77 cfg.use_program(&pio.load_program(&prg.program), &[]);
78 sm.set_config(&cfg);
79 sm.set_enable(true);
80 Self { irq, sm }
81 }
82
83 // Set pulse frequency
84 pub fn set_frequency(&mut self, freq: u32) {
85 let clock_divider: FixedU32<U8> = (125_000_000 / (freq * 136)).to_fixed();
86 assert!(clock_divider <= 65536, "clkdiv must be <= 65536");
87 assert!(clock_divider >= 1, "clkdiv must be >= 1");
88 self.sm.set_clock_divider(clock_divider);
89 self.sm.clkdiv_restart();
90 }
91
92 // Full step, one phase
93 pub async fn step(&mut self, steps: i32) {
94 if steps > 0 {
95 self.run(steps, 0b1000_0100_0010_0001_1000_0100_0010_0001).await
96 } else {
97 self.run(-steps, 0b0001_0010_0100_1000_0001_0010_0100_1000).await
98 }
99 }
100
101 // Full step, two phase
102 pub async fn step2(&mut self, steps: i32) {
103 if steps > 0 {
104 self.run(steps, 0b1001_1100_0110_0011_1001_1100_0110_0011).await
105 } else {
106 self.run(-steps, 0b0011_0110_1100_1001_0011_0110_1100_1001).await
107 }
108 }
109
110 // Half step
111 pub async fn step_half(&mut self, steps: i32) {
112 if steps > 0 {
113 self.run(steps, 0b1001_1000_1100_0100_0110_0010_0011_0001).await
114 } else {
115 self.run(-steps, 0b0001_0011_0010_0110_0100_1100_1000_1001).await
116 }
117 }
118
119 async fn run(&mut self, steps: i32, pattern: u32) {
120 self.sm.tx().wait_push(steps as u32).await;
121 self.sm.tx().wait_push(pattern).await;
122 let drop = OnDrop::new(|| {
123 self.sm.clear_fifos();
124 unsafe {
125 self.sm.exec_instr(
126 pio::InstructionOperands::JMP {
127 address: 0,
128 condition: pio::JmpCondition::Always,
129 }
130 .encode(),
131 );
132 }
133 });
134 self.irq.wait().await;
135 drop.defuse();
136 }
137}
138
139struct OnDrop<F: FnOnce()> {
140 f: MaybeUninit<F>,
141}
142
143impl<F: FnOnce()> OnDrop<F> {
144 pub fn new(f: F) -> Self {
145 Self { f: MaybeUninit::new(f) }
146 }
147
148 pub fn defuse(self) {
149 mem::forget(self)
150 }
151}
152
153impl<F: FnOnce()> Drop for OnDrop<F> {
154 fn drop(&mut self) {
155 unsafe { self.f.as_ptr().read()() }
156 }
157}
158
159#[embassy_executor::main]
160async fn main(_spawner: Spawner) {
161 let p = embassy_rp::init(Default::default());
162 let Pio {
163 mut common, irq0, sm0, ..
164 } = Pio::new(p.PIO0, Irqs);
165
166 let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7);
167 stepper.set_frequency(120);
168 loop {
169 info!("CW full steps");
170 stepper.step(1000).await;
171
172 info!("CCW full steps, drop after 1 sec");
173 if let Err(_) = with_timeout(Duration::from_secs(1), stepper.step(i32::MIN)).await {
174 info!("Time's up!");
175 Timer::after(Duration::from_secs(1)).await;
176 }
177
178 info!("CW half steps");
179 stepper.step_half(1000).await;
180
181 info!("CCW half steps");
182 stepper.step_half(-1000).await;
183 }
184}
diff --git a/examples/rp23/src/bin/pio_ws2812.rs b/examples/rp23/src/bin/pio_ws2812.rs
new file mode 100644
index 000000000..2e62a3d97
--- /dev/null
+++ b/examples/rp23/src/bin/pio_ws2812.rs
@@ -0,0 +1,177 @@
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::dma::{AnyChannel, Channel};
10use embassy_rp::peripherals::PIO0;
11use embassy_rp::pio::{
12 Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
13};
14use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef};
15use embassy_time::{Duration, Ticker, Timer};
16use fixed::types::U24F8;
17use fixed_macro::fixed;
18use smart_leds::RGB8;
19use {defmt_rtt as _, panic_probe as _};
20use embassy_rp::block::ImageDef;
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
36
37bind_interrupts!(struct Irqs {
38 PIO0_IRQ_0 => InterruptHandler<PIO0>;
39});
40
41pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> {
42 dma: PeripheralRef<'d, AnyChannel>,
43 sm: StateMachine<'d, P, S>,
44}
45
46impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> {
47 pub fn new(
48 pio: &mut Common<'d, P>,
49 mut sm: StateMachine<'d, P, S>,
50 dma: impl Peripheral<P = impl Channel> + 'd,
51 pin: impl PioPin,
52 ) -> Self {
53 into_ref!(dma);
54
55 // Setup sm0
56
57 // prepare the PIO program
58 let side_set = pio::SideSet::new(false, 1, false);
59 let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set);
60
61 const T1: u8 = 2; // start bit
62 const T2: u8 = 5; // data bit
63 const T3: u8 = 3; // stop bit
64 const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32;
65
66 let mut wrap_target = a.label();
67 let mut wrap_source = a.label();
68 let mut do_zero = a.label();
69 a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0);
70 a.bind(&mut wrap_target);
71 // Do stop bit
72 a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0);
73 // Do start bit
74 a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1);
75 // Do data bit = 1
76 a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1);
77 a.bind(&mut do_zero);
78 // Do data bit = 0
79 a.nop_with_delay_and_side_set(T2 - 1, 0);
80 a.bind(&mut wrap_source);
81
82 let prg = a.assemble_with_wrap(wrap_source, wrap_target);
83 let mut cfg = Config::default();
84
85 // Pin config
86 let out_pin = pio.make_pio_pin(pin);
87 cfg.set_out_pins(&[&out_pin]);
88 cfg.set_set_pins(&[&out_pin]);
89
90 cfg.use_program(&pio.load_program(&prg), &[&out_pin]);
91
92 // Clock config, measured in kHz to avoid overflows
93 // TODO CLOCK_FREQ should come from embassy_rp
94 let clock_freq = U24F8::from_num(clocks::clk_sys_freq() / 1000);
95 let ws2812_freq = fixed!(800: U24F8);
96 let bit_freq = ws2812_freq * CYCLES_PER_BIT;
97 cfg.clock_divider = clock_freq / bit_freq;
98
99 // FIFO config
100 cfg.fifo_join = FifoJoin::TxOnly;
101 cfg.shift_out = ShiftConfig {
102 auto_fill: true,
103 threshold: 24,
104 direction: ShiftDirection::Left,
105 };
106
107 sm.set_config(&cfg);
108 sm.set_enable(true);
109
110 Self {
111 dma: dma.map_into(),
112 sm,
113 }
114 }
115
116 pub async fn write(&mut self, colors: &[RGB8; N]) {
117 // Precompute the word bytes from the colors
118 let mut words = [0u32; N];
119 for i in 0..N {
120 let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8);
121 words[i] = word;
122 }
123
124 // DMA transfer
125 self.sm.tx().dma_push(self.dma.reborrow(), &words).await;
126
127 Timer::after_micros(55).await;
128 }
129}
130
131/// Input a value 0 to 255 to get a color value
132/// The colours are a transition r - g - b - back to r.
133fn wheel(mut wheel_pos: u8) -> RGB8 {
134 wheel_pos = 255 - wheel_pos;
135 if wheel_pos < 85 {
136 return (255 - wheel_pos * 3, 0, wheel_pos * 3).into();
137 }
138 if wheel_pos < 170 {
139 wheel_pos -= 85;
140 return (0, wheel_pos * 3, 255 - wheel_pos * 3).into();
141 }
142 wheel_pos -= 170;
143 (wheel_pos * 3, 255 - wheel_pos * 3, 0).into()
144}
145
146#[embassy_executor::main]
147async fn main(_spawner: Spawner) {
148 info!("Start");
149 let p = embassy_rp::init(Default::default());
150
151 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
152
153 // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit
154 // feather boards for the 2040 both have one built in.
155 const NUM_LEDS: usize = 1;
156 let mut data = [RGB8::default(); NUM_LEDS];
157
158 // Common neopixel pins:
159 // Thing plus: 8
160 // Adafruit Feather: 16; Adafruit Feather+RFM95: 4
161 let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16);
162
163 // Loop forever making RGB values and pushing them out to the WS2812.
164 let mut ticker = Ticker::every(Duration::from_millis(10));
165 loop {
166 for j in 0..(256 * 5) {
167 debug!("New Colors:");
168 for i in 0..NUM_LEDS {
169 data[i] = wheel((((i * 256) as u16 / NUM_LEDS as u16 + j as u16) & 255) as u8);
170 debug!("R: {} G: {} B: {}", data[i].r, data[i].g, data[i].b);
171 }
172 ws2812.write(&data).await;
173
174 ticker.next().await;
175 }
176 }
177}
diff --git a/examples/rp23/src/bin/pwm.rs b/examples/rp23/src/bin/pwm.rs
new file mode 100644
index 000000000..ab38a03bb
--- /dev/null
+++ b/examples/rp23/src/bin/pwm.rs
@@ -0,0 +1,45 @@
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::pwm::{Config, Pwm};
11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _};
13use embassy_rp::block::ImageDef;
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
30#[embassy_executor::main]
31async fn main(_spawner: Spawner) {
32 let p = embassy_rp::init(Default::default());
33
34 let mut c: Config = Default::default();
35 c.top = 0x8000;
36 c.compare_b = 8;
37 let mut pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, c.clone());
38
39 loop {
40 info!("current LED duty cycle: {}/32768", c.compare_b);
41 Timer::after_secs(1).await;
42 c.compare_b = c.compare_b.rotate_left(4);
43 pwm.set_config(&c);
44 }
45}
diff --git a/examples/rp23/src/bin/pwm_input.rs b/examples/rp23/src/bin/pwm_input.rs
new file mode 100644
index 000000000..fcb561cfd
--- /dev/null
+++ b/examples/rp23/src/bin/pwm_input.rs
@@ -0,0 +1,42 @@
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::gpio::Pull;
9use embassy_rp::pwm::{Config, InputMode, Pwm};
10use embassy_time::{Duration, Ticker};
11use {defmt_rtt as _, panic_probe as _};
12use embassy_rp::block::ImageDef;
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
29#[embassy_executor::main]
30async fn main(_spawner: Spawner) {
31 let p = embassy_rp::init(Default::default());
32
33 let cfg: Config = Default::default();
34 let pwm = Pwm::new_input(p.PWM_SLICE2, p.PIN_5, Pull::None, InputMode::RisingEdge, cfg);
35
36 let mut ticker = Ticker::every(Duration::from_secs(1));
37 loop {
38 info!("Input frequency: {} Hz", pwm.counter());
39 pwm.set_counter(0);
40 ticker.next().await;
41 }
42}
diff --git a/examples/rp23/src/bin/rosc.rs b/examples/rp23/src/bin/rosc.rs
new file mode 100644
index 000000000..051b4710f
--- /dev/null
+++ b/examples/rp23/src/bin/rosc.rs
@@ -0,0 +1,47 @@
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::{clocks, gpio};
11use embassy_time::Timer;
12use gpio::{Level, Output};
13use {defmt_rtt as _, panic_probe as _};
14use embassy_rp::block::ImageDef;
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
31#[embassy_executor::main]
32async fn main(_spawner: Spawner) {
33 let mut config = embassy_rp::config::Config::default();
34 config.clocks = clocks::ClockConfig::rosc();
35 let p = embassy_rp::init(config);
36 let mut led = Output::new(p.PIN_25, Level::Low);
37
38 loop {
39 info!("led on!");
40 led.set_high();
41 Timer::after_secs(1).await;
42
43 info!("led off!");
44 led.set_low();
45 Timer::after_secs(1).await;
46 }
47}
diff --git a/examples/rp23/src/bin/shared_bus.rs b/examples/rp23/src/bin/shared_bus.rs
new file mode 100644
index 000000000..e3213cd91
--- /dev/null
+++ b/examples/rp23/src/bin/shared_bus.rs
@@ -0,0 +1,131 @@
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::gpio::{AnyPin, Level, Output};
12use embassy_rp::i2c::{self, I2c, InterruptHandler};
13use embassy_rp::peripherals::{I2C1, SPI1};
14use embassy_rp::spi::{self, Spi};
15use embassy_sync::blocking_mutex::raw::NoopRawMutex;
16use embassy_sync::mutex::Mutex;
17use embassy_time::Timer;
18use static_cell::StaticCell;
19use {defmt_rtt as _, panic_probe as _};
20use embassy_rp::block::ImageDef;
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
36
37type Spi1Bus = Mutex<NoopRawMutex, Spi<'static, SPI1, spi::Async>>;
38type I2c1Bus = Mutex<NoopRawMutex, I2c<'static, I2C1, i2c::Async>>;
39
40bind_interrupts!(struct Irqs {
41 I2C1_IRQ => InterruptHandler<I2C1>;
42});
43
44#[embassy_executor::main]
45async fn main(spawner: Spawner) {
46 let p = embassy_rp::init(Default::default());
47 info!("Here we go!");
48
49 // Shared I2C bus
50 let i2c = I2c::new_async(p.I2C1, p.PIN_15, p.PIN_14, Irqs, i2c::Config::default());
51 static I2C_BUS: StaticCell<I2c1Bus> = StaticCell::new();
52 let i2c_bus = I2C_BUS.init(Mutex::new(i2c));
53
54 spawner.must_spawn(i2c_task_a(i2c_bus));
55 spawner.must_spawn(i2c_task_b(i2c_bus));
56
57 // Shared SPI bus
58 let spi_cfg = spi::Config::default();
59 let spi = Spi::new(p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, p.DMA_CH0, p.DMA_CH1, spi_cfg);
60 static SPI_BUS: StaticCell<Spi1Bus> = StaticCell::new();
61 let spi_bus = SPI_BUS.init(Mutex::new(spi));
62
63 // Chip select pins for the SPI devices
64 let cs_a = Output::new(AnyPin::from(p.PIN_0), Level::High);
65 let cs_b = Output::new(AnyPin::from(p.PIN_1), Level::High);
66
67 spawner.must_spawn(spi_task_a(spi_bus, cs_a));
68 spawner.must_spawn(spi_task_b(spi_bus, cs_b));
69}
70
71#[embassy_executor::task]
72async fn i2c_task_a(i2c_bus: &'static I2c1Bus) {
73 let i2c_dev = I2cDevice::new(i2c_bus);
74 let _sensor = DummyI2cDeviceDriver::new(i2c_dev, 0xC0);
75 loop {
76 info!("i2c task A");
77 Timer::after_secs(1).await;
78 }
79}
80
81#[embassy_executor::task]
82async fn i2c_task_b(i2c_bus: &'static I2c1Bus) {
83 let i2c_dev = I2cDevice::new(i2c_bus);
84 let _sensor = DummyI2cDeviceDriver::new(i2c_dev, 0xDE);
85 loop {
86 info!("i2c task B");
87 Timer::after_secs(1).await;
88 }
89}
90
91#[embassy_executor::task]
92async fn spi_task_a(spi_bus: &'static Spi1Bus, cs: Output<'static>) {
93 let spi_dev = SpiDevice::new(spi_bus, cs);
94 let _sensor = DummySpiDeviceDriver::new(spi_dev);
95 loop {
96 info!("spi task A");
97 Timer::after_secs(1).await;
98 }
99}
100
101#[embassy_executor::task]
102async fn spi_task_b(spi_bus: &'static Spi1Bus, cs: Output<'static>) {
103 let spi_dev = SpiDevice::new(spi_bus, cs);
104 let _sensor = DummySpiDeviceDriver::new(spi_dev);
105 loop {
106 info!("spi task B");
107 Timer::after_secs(1).await;
108 }
109}
110
111// Dummy I2C device driver, using `embedded-hal-async`
112struct DummyI2cDeviceDriver<I2C: embedded_hal_async::i2c::I2c> {
113 _i2c: I2C,
114}
115
116impl<I2C: embedded_hal_async::i2c::I2c> DummyI2cDeviceDriver<I2C> {
117 fn new(i2c_dev: I2C, _address: u8) -> Self {
118 Self { _i2c: i2c_dev }
119 }
120}
121
122// Dummy SPI device driver, using `embedded-hal-async`
123struct DummySpiDeviceDriver<SPI: embedded_hal_async::spi::SpiDevice> {
124 _spi: SPI,
125}
126
127impl<SPI: embedded_hal_async::spi::SpiDevice> DummySpiDeviceDriver<SPI> {
128 fn new(spi_dev: SPI) -> Self {
129 Self { _spi: spi_dev }
130 }
131}
diff --git a/examples/rp23/src/bin/sharing.rs b/examples/rp23/src/bin/sharing.rs
new file mode 100644
index 000000000..10f064947
--- /dev/null
+++ b/examples/rp23/src/bin/sharing.rs
@@ -0,0 +1,166 @@
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::clocks::RoscRng;
23use embassy_rp::interrupt::{Priority, InterruptExt};
24use embassy_rp::peripherals::UART0;
25use embassy_rp::uart::{self, InterruptHandler, UartTx};
26use embassy_rp::{bind_interrupts, interrupt};
27use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
28use embassy_sync::{blocking_mutex, mutex};
29use embassy_time::{Duration, Ticker};
30use rand::RngCore;
31use static_cell::{ConstStaticCell, StaticCell};
32use {defmt_rtt as _, panic_probe as _};
33use embassy_rp::block::ImageDef;
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
49
50type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, UART0, uart::Async>>;
51
52struct MyType {
53 inner: u32,
54}
55
56static EXECUTOR_HI: InterruptExecutor = InterruptExecutor::new();
57static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();
58
59// Use Atomics for simple values
60static ATOMIC: AtomicU32 = AtomicU32::new(0);
61
62// Use blocking Mutex with Cell/RefCell for sharing non-async things
63static MUTEX_BLOCKING: blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<MyType>> =
64 blocking_mutex::Mutex::new(RefCell::new(MyType { inner: 0 }));
65
66bind_interrupts!(struct Irqs {
67 UART0_IRQ => InterruptHandler<UART0>;
68});
69
70#[interrupt]
71unsafe fn SWI_IRQ_0() {
72 EXECUTOR_HI.on_interrupt()
73}
74
75#[entry]
76fn main() -> ! {
77 let p = embassy_rp::init(Default::default());
78 info!("Here we go!");
79
80 let uart = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, uart::Config::default());
81 // Use the async Mutex for sharing async things (built-in interior mutability)
82 static UART: StaticCell<UartAsyncMutex> = StaticCell::new();
83 let uart = UART.init(mutex::Mutex::new(uart));
84
85 // High-priority executor: runs in interrupt mode
86 interrupt::SWI_IRQ_0.set_priority(Priority::P3);
87 let spawner = EXECUTOR_HI.start(interrupt::SWI_IRQ_0);
88 spawner.must_spawn(task_a(uart));
89
90 // Low priority executor: runs in thread mode
91 let executor = EXECUTOR_LOW.init(Executor::new());
92 executor.run(|spawner| {
93 // No Mutex needed when sharing between tasks running on the same executor
94
95 // Use Cell for Copy-types
96 static CELL: ConstStaticCell<Cell<[u8; 4]>> = ConstStaticCell::new(Cell::new([0; 4]));
97 let cell = CELL.take();
98
99 // Use RefCell for &mut access
100 static REF_CELL: ConstStaticCell<RefCell<MyType>> = ConstStaticCell::new(RefCell::new(MyType { inner: 0 }));
101 let ref_cell = REF_CELL.take();
102
103 spawner.must_spawn(task_b(uart, cell, ref_cell));
104 spawner.must_spawn(task_c(cell, ref_cell));
105 });
106}
107
108#[embassy_executor::task]
109async fn task_a(uart: &'static UartAsyncMutex) {
110 let mut ticker = Ticker::every(Duration::from_secs(1));
111 loop {
112 let random = RoscRng.next_u32();
113
114 {
115 let mut uart = uart.lock().await;
116 uart.write(b"task a").await.unwrap();
117 // The uart lock is released when it goes out of scope
118 }
119
120 ATOMIC.store(random, Ordering::Relaxed);
121
122 MUTEX_BLOCKING.lock(|x| x.borrow_mut().inner = random);
123
124 ticker.next().await;
125 }
126}
127
128#[embassy_executor::task]
129async fn task_b(uart: &'static UartAsyncMutex, cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {
130 let mut ticker = Ticker::every(Duration::from_secs(1));
131 loop {
132 let random = RoscRng.next_u32();
133
134 uart.lock().await.write(b"task b").await.unwrap();
135
136 cell.set(random.to_be_bytes());
137
138 ref_cell.borrow_mut().inner = random;
139
140 ticker.next().await;
141 }
142}
143
144#[embassy_executor::task]
145async fn task_c(cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {
146 let mut ticker = Ticker::every(Duration::from_secs(1));
147 loop {
148 info!("=======================");
149
150 let atomic_val = ATOMIC.load(Ordering::Relaxed);
151 info!("atomic: {}", atomic_val);
152
153 MUTEX_BLOCKING.lock(|x| {
154 let val = x.borrow().inner;
155 info!("blocking mutex: {}", val);
156 });
157
158 let cell_val = cell.get();
159 info!("cell: {:?}", cell_val);
160
161 let ref_cell_val = ref_cell.borrow().inner;
162 info!("ref_cell: {:?}", ref_cell_val);
163
164 ticker.next().await;
165 }
166}
diff --git a/examples/rp23/src/bin/spi.rs b/examples/rp23/src/bin/spi.rs
new file mode 100644
index 000000000..bcf356188
--- /dev/null
+++ b/examples/rp23/src/bin/spi.rs
@@ -0,0 +1,62 @@
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::spi::Spi;
11use embassy_rp::{gpio, spi};
12use gpio::{Level, Output};
13use {defmt_rtt as _, panic_probe as _};
14use embassy_rp::block::ImageDef;
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
31#[embassy_executor::main]
32async fn main(_spawner: Spawner) {
33 let p = embassy_rp::init(Default::default());
34 info!("Hello World!");
35
36 // Example for resistive touch sensor in Waveshare Pico-ResTouch
37
38 let miso = p.PIN_12;
39 let mosi = p.PIN_11;
40 let clk = p.PIN_10;
41 let touch_cs = p.PIN_16;
42
43 // create SPI
44 let mut config = spi::Config::default();
45 config.frequency = 2_000_000;
46 let mut spi = Spi::new_blocking(p.SPI1, clk, mosi, miso, config);
47
48 // Configure CS
49 let mut cs = Output::new(touch_cs, Level::Low);
50
51 loop {
52 cs.set_low();
53 let mut buf = [0x90, 0x00, 0x00, 0xd0, 0x00, 0x00];
54 spi.blocking_transfer_in_place(&mut buf).unwrap();
55 cs.set_high();
56
57 let x = (buf[1] as u32) << 5 | (buf[2] as u32) >> 3;
58 let y = (buf[4] as u32) << 5 | (buf[5] as u32) >> 3;
59
60 info!("touch: {=u32} {=u32}", x, y);
61 }
62}
diff --git a/examples/rp23/src/bin/spi_async.rs b/examples/rp23/src/bin/spi_async.rs
new file mode 100644
index 000000000..7a43995d2
--- /dev/null
+++ b/examples/rp23/src/bin/spi_async.rs
@@ -0,0 +1,47 @@
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::spi::{Config, Spi};
10use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _};
12use embassy_rp::block::ImageDef;
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
29#[embassy_executor::main]
30async fn main(_spawner: Spawner) {
31 let p = embassy_rp::init(Default::default());
32 info!("Hello World!");
33
34 let miso = p.PIN_12;
35 let mosi = p.PIN_11;
36 let clk = p.PIN_10;
37
38 let mut spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default());
39
40 loop {
41 let tx_buf = [1_u8, 2, 3, 4, 5, 6];
42 let mut rx_buf = [0_u8; 6];
43 spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();
44 info!("{:?}", rx_buf);
45 Timer::after_secs(1).await;
46 }
47}
diff --git a/examples/rp23/src/bin/spi_display.rs b/examples/rp23/src/bin/spi_display.rs
new file mode 100644
index 000000000..25368bb94
--- /dev/null
+++ b/examples/rp23/src/bin/spi_display.rs
@@ -0,0 +1,328 @@
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::gpio::{Level, Output};
15use embassy_rp::spi;
16use embassy_rp::spi::{Blocking, Spi};
17use embassy_sync::blocking_mutex::raw::NoopRawMutex;
18use embassy_sync::blocking_mutex::Mutex;
19use embassy_time::Delay;
20use embedded_graphics::image::{Image, ImageRawLE};
21use embedded_graphics::mono_font::ascii::FONT_10X20;
22use embedded_graphics::mono_font::MonoTextStyle;
23use embedded_graphics::pixelcolor::Rgb565;
24use embedded_graphics::prelude::*;
25use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle};
26use embedded_graphics::text::Text;
27use st7789::{Orientation, ST7789};
28use {defmt_rtt as _, panic_probe as _};
29use embassy_rp::block::ImageDef;
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
45
46use crate::my_display_interface::SPIDeviceInterface;
47use crate::touch::Touch;
48
49const DISPLAY_FREQ: u32 = 64_000_000;
50const TOUCH_FREQ: u32 = 200_000;
51
52#[embassy_executor::main]
53async fn main(_spawner: Spawner) {
54 let p = embassy_rp::init(Default::default());
55 info!("Hello World!");
56
57 let bl = p.PIN_13;
58 let rst = p.PIN_15;
59 let display_cs = p.PIN_9;
60 let dcx = p.PIN_8;
61 let miso = p.PIN_12;
62 let mosi = p.PIN_11;
63 let clk = p.PIN_10;
64 let touch_cs = p.PIN_16;
65 //let touch_irq = p.PIN_17;
66
67 // create SPI
68 let mut display_config = spi::Config::default();
69 display_config.frequency = DISPLAY_FREQ;
70 display_config.phase = spi::Phase::CaptureOnSecondTransition;
71 display_config.polarity = spi::Polarity::IdleHigh;
72 let mut touch_config = spi::Config::default();
73 touch_config.frequency = TOUCH_FREQ;
74 touch_config.phase = spi::Phase::CaptureOnSecondTransition;
75 touch_config.polarity = spi::Polarity::IdleHigh;
76
77 let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone());
78 let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi));
79
80 let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config);
81 let touch_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(touch_cs, Level::High), touch_config);
82
83 let mut touch = Touch::new(touch_spi);
84
85 let dcx = Output::new(dcx, Level::Low);
86 let rst = Output::new(rst, Level::Low);
87 // dcx: 0 = command, 1 = data
88
89 // Enable LCD backlight
90 let _bl = Output::new(bl, Level::High);
91
92 // display interface abstraction from SPI and DC
93 let di = SPIDeviceInterface::new(display_spi, dcx);
94
95 // create driver
96 let mut display = ST7789::new(di, rst, 240, 320);
97
98 // initialize
99 display.init(&mut Delay).unwrap();
100
101 // set default orientation
102 display.set_orientation(Orientation::Landscape).unwrap();
103
104 display.clear(Rgb565::BLACK).unwrap();
105
106 let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86);
107 let ferris = Image::new(&raw_image_data, Point::new(34, 68));
108
109 // Display the image
110 ferris.draw(&mut display).unwrap();
111
112 let style = MonoTextStyle::new(&FONT_10X20, Rgb565::GREEN);
113 Text::new(
114 "Hello embedded_graphics \n + embassy + RP2040!",
115 Point::new(20, 200),
116 style,
117 )
118 .draw(&mut display)
119 .unwrap();
120
121 loop {
122 if let Some((x, y)) = touch.read() {
123 let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::BLUE).build();
124
125 Rectangle::new(Point::new(x - 1, y - 1), Size::new(3, 3))
126 .into_styled(style)
127 .draw(&mut display)
128 .unwrap();
129 }
130 }
131}
132
133/// Driver for the XPT2046 resistive touchscreen sensor
134mod touch {
135 use embedded_hal_1::spi::{Operation, SpiDevice};
136
137 struct Calibration {
138 x1: i32,
139 x2: i32,
140 y1: i32,
141 y2: i32,
142 sx: i32,
143 sy: i32,
144 }
145
146 const CALIBRATION: Calibration = Calibration {
147 x1: 3880,
148 x2: 340,
149 y1: 262,
150 y2: 3850,
151 sx: 320,
152 sy: 240,
153 };
154
155 pub struct Touch<SPI: SpiDevice> {
156 spi: SPI,
157 }
158
159 impl<SPI> Touch<SPI>
160 where
161 SPI: SpiDevice,
162 {
163 pub fn new(spi: SPI) -> Self {
164 Self { spi }
165 }
166
167 pub fn read(&mut self) -> Option<(i32, i32)> {
168 let mut x = [0; 2];
169 let mut y = [0; 2];
170 self.spi
171 .transaction(&mut [
172 Operation::Write(&[0x90]),
173 Operation::Read(&mut x),
174 Operation::Write(&[0xd0]),
175 Operation::Read(&mut y),
176 ])
177 .unwrap();
178
179 let x = (u16::from_be_bytes(x) >> 3) as i32;
180 let y = (u16::from_be_bytes(y) >> 3) as i32;
181
182 let cal = &CALIBRATION;
183
184 let x = ((x - cal.x1) * cal.sx / (cal.x2 - cal.x1)).clamp(0, cal.sx);
185 let y = ((y - cal.y1) * cal.sy / (cal.y2 - cal.y1)).clamp(0, cal.sy);
186 if x == 0 && y == 0 {
187 None
188 } else {
189 Some((x, y))
190 }
191 }
192 }
193}
194
195mod my_display_interface {
196 use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand};
197 use embedded_hal_1::digital::OutputPin;
198 use embedded_hal_1::spi::SpiDevice;
199
200 /// SPI display interface.
201 ///
202 /// This combines the SPI peripheral and a data/command pin
203 pub struct SPIDeviceInterface<SPI, DC> {
204 spi: SPI,
205 dc: DC,
206 }
207
208 impl<SPI, DC> SPIDeviceInterface<SPI, DC>
209 where
210 SPI: SpiDevice,
211 DC: OutputPin,
212 {
213 /// Create new SPI interface for communciation with a display driver
214 pub fn new(spi: SPI, dc: DC) -> Self {
215 Self { spi, dc }
216 }
217 }
218
219 impl<SPI, DC> WriteOnlyDataCommand for SPIDeviceInterface<SPI, DC>
220 where
221 SPI: SpiDevice,
222 DC: OutputPin,
223 {
224 fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> {
225 // 1 = data, 0 = command
226 self.dc.set_low().map_err(|_| DisplayError::DCError)?;
227
228 send_u8(&mut self.spi, cmds).map_err(|_| DisplayError::BusWriteError)?;
229 Ok(())
230 }
231
232 fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> {
233 // 1 = data, 0 = command
234 self.dc.set_high().map_err(|_| DisplayError::DCError)?;
235
236 send_u8(&mut self.spi, buf).map_err(|_| DisplayError::BusWriteError)?;
237 Ok(())
238 }
239 }
240
241 fn send_u8<T: SpiDevice>(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> {
242 match words {
243 DataFormat::U8(slice) => spi.write(slice),
244 DataFormat::U16(slice) => {
245 use byte_slice_cast::*;
246 spi.write(slice.as_byte_slice())
247 }
248 DataFormat::U16LE(slice) => {
249 use byte_slice_cast::*;
250 for v in slice.as_mut() {
251 *v = v.to_le();
252 }
253 spi.write(slice.as_byte_slice())
254 }
255 DataFormat::U16BE(slice) => {
256 use byte_slice_cast::*;
257 for v in slice.as_mut() {
258 *v = v.to_be();
259 }
260 spi.write(slice.as_byte_slice())
261 }
262 DataFormat::U8Iter(iter) => {
263 let mut buf = [0; 32];
264 let mut i = 0;
265
266 for v in iter.into_iter() {
267 buf[i] = v;
268 i += 1;
269
270 if i == buf.len() {
271 spi.write(&buf)?;
272 i = 0;
273 }
274 }
275
276 if i > 0 {
277 spi.write(&buf[..i])?;
278 }
279
280 Ok(())
281 }
282 DataFormat::U16LEIter(iter) => {
283 use byte_slice_cast::*;
284 let mut buf = [0; 32];
285 let mut i = 0;
286
287 for v in iter.map(u16::to_le) {
288 buf[i] = v;
289 i += 1;
290
291 if i == buf.len() {
292 spi.write(&buf.as_byte_slice())?;
293 i = 0;
294 }
295 }
296
297 if i > 0 {
298 spi.write(&buf[..i].as_byte_slice())?;
299 }
300
301 Ok(())
302 }
303 DataFormat::U16BEIter(iter) => {
304 use byte_slice_cast::*;
305 let mut buf = [0; 64];
306 let mut i = 0;
307 let len = buf.len();
308
309 for v in iter.map(u16::to_be) {
310 buf[i] = v;
311 i += 1;
312
313 if i == len {
314 spi.write(&buf.as_byte_slice())?;
315 i = 0;
316 }
317 }
318
319 if i > 0 {
320 spi.write(&buf[..i].as_byte_slice())?;
321 }
322
323 Ok(())
324 }
325 _ => unimplemented!(),
326 }
327 }
328}
diff --git a/examples/rp23/src/bin/spi_sdmmc.rs b/examples/rp23/src/bin/spi_sdmmc.rs
new file mode 100644
index 000000000..992215b0d
--- /dev/null
+++ b/examples/rp23/src/bin/spi_sdmmc.rs
@@ -0,0 +1,99 @@
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::spi::Spi;
13use embassy_rp::{gpio, spi};
14use embedded_hal_bus::spi::ExclusiveDevice;
15use embedded_sdmmc::sdcard::{DummyCsPin, SdCard};
16use gpio::{Level, Output};
17use {defmt_rtt as _, panic_probe as _};
18use embassy_rp::block::ImageDef;
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
34
35struct DummyTimesource();
36
37impl embedded_sdmmc::TimeSource for DummyTimesource {
38 fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {
39 embedded_sdmmc::Timestamp {
40 year_since_1970: 0,
41 zero_indexed_month: 0,
42 zero_indexed_day: 0,
43 hours: 0,
44 minutes: 0,
45 seconds: 0,
46 }
47 }
48}
49
50#[embassy_executor::main]
51async fn main(_spawner: Spawner) {
52 embassy_rp::pac::SIO.spinlock(31).write_value(1);
53 let p = embassy_rp::init(Default::default());
54
55 // SPI clock needs to be running at <= 400kHz during initialization
56 let mut config = spi::Config::default();
57 config.frequency = 400_000;
58 let spi = Spi::new_blocking(p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, config);
59 // Use a dummy cs pin here, for embedded-hal SpiDevice compatibility reasons
60 let spi_dev = ExclusiveDevice::new_no_delay(spi, DummyCsPin);
61 // Real cs pin
62 let cs = Output::new(p.PIN_16, Level::High);
63
64 let sdcard = SdCard::new(spi_dev, cs, embassy_time::Delay);
65 info!("Card size is {} bytes", sdcard.num_bytes().unwrap());
66
67 // Now that the card is initialized, the SPI clock can go faster
68 let mut config = spi::Config::default();
69 config.frequency = 16_000_000;
70 sdcard.spi(|dev| dev.bus_mut().set_config(&config)).ok();
71
72 // Now let's look for volumes (also known as partitions) on our block device.
73 // To do this we need a Volume Manager. It will take ownership of the block device.
74 let mut volume_mgr = embedded_sdmmc::VolumeManager::new(sdcard, DummyTimesource());
75
76 // Try and access Volume 0 (i.e. the first partition).
77 // The volume object holds information about the filesystem on that volume.
78 let mut volume0 = volume_mgr.open_volume(embedded_sdmmc::VolumeIdx(0)).unwrap();
79 info!("Volume 0: {:?}", defmt::Debug2Format(&volume0));
80
81 // Open the root directory (mutably borrows from the volume).
82 let mut root_dir = volume0.open_root_dir().unwrap();
83
84 // Open a file called "MY_FILE.TXT" in the root directory
85 // This mutably borrows the directory.
86 let mut my_file = root_dir
87 .open_file_in_dir("MY_FILE.TXT", embedded_sdmmc::Mode::ReadOnly)
88 .unwrap();
89
90 // Print the contents of the file
91 while !my_file.is_eof() {
92 let mut buf = [0u8; 32];
93 if let Ok(n) = my_file.read(&mut buf) {
94 info!("{:a}", buf[..n]);
95 }
96 }
97
98 loop {}
99}
diff --git a/examples/rp23/src/bin/uart.rs b/examples/rp23/src/bin/uart.rs
new file mode 100644
index 000000000..7b82fa350
--- /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::uart;
12use {defmt_rtt as _, panic_probe as _};
13use embassy_rp::block::ImageDef;
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..f7acdade9
--- /dev/null
+++ b/examples/rp23/src/bin/uart_buffered_split.rs
@@ -0,0 +1,74 @@
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::peripherals::UART0;
14use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config};
15use embassy_time::Timer;
16use embedded_io_async::{Read, Write};
17use static_cell::StaticCell;
18use {defmt_rtt as _, panic_probe as _};
19use embassy_rp::block::ImageDef;
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
35
36bind_interrupts!(struct Irqs {
37 UART0_IRQ => BufferedInterruptHandler<UART0>;
38});
39
40#[embassy_executor::main]
41async fn main(spawner: Spawner) {
42 let p = embassy_rp::init(Default::default());
43 let (tx_pin, rx_pin, uart) = (p.PIN_0, p.PIN_1, p.UART0);
44
45 static TX_BUF: StaticCell<[u8; 16]> = StaticCell::new();
46 let tx_buf = &mut TX_BUF.init([0; 16])[..];
47 static RX_BUF: StaticCell<[u8; 16]> = StaticCell::new();
48 let rx_buf = &mut RX_BUF.init([0; 16])[..];
49 let uart = BufferedUart::new(uart, Irqs, tx_pin, rx_pin, tx_buf, rx_buf, Config::default());
50 let (mut tx, rx) = uart.split();
51
52 unwrap!(spawner.spawn(reader(rx)));
53
54 info!("Writing...");
55 loop {
56 let data = [
57 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,
58 29, 30, 31,
59 ];
60 info!("TX {:?}", data);
61 tx.write_all(&data).await.unwrap();
62 Timer::after_secs(1).await;
63 }
64}
65
66#[embassy_executor::task]
67async fn reader(mut rx: BufferedUartRx<'static, UART0>) {
68 info!("Reading...");
69 loop {
70 let mut buf = [0; 31];
71 rx.read_exact(&mut buf).await.unwrap();
72 info!("RX {:?}", buf);
73 }
74}
diff --git a/examples/rp23/src/bin/uart_r503.rs b/examples/rp23/src/bin/uart_r503.rs
new file mode 100644
index 000000000..69f6dbbff
--- /dev/null
+++ b/examples/rp23/src/bin/uart_r503.rs
@@ -0,0 +1,174 @@
1#![no_std]
2#![no_main]
3
4use defmt::{debug, error, info};
5use embassy_executor::Spawner;
6use embassy_rp::bind_interrupts;
7use embassy_rp::peripherals::UART0;
8use embassy_rp::uart::{Config, DataBits, InterruptHandler as UARTInterruptHandler, Parity, StopBits, Uart};
9use embassy_time::{with_timeout, Duration, Timer};
10use heapless::Vec;
11use {defmt_rtt as _, panic_probe as _};
12use embassy_rp::block::ImageDef;
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
29bind_interrupts!(pub struct Irqs {
30 UART0_IRQ => UARTInterruptHandler<UART0>;
31});
32
33const START: u16 = 0xEF01;
34const ADDRESS: u32 = 0xFFFFFFFF;
35
36// ================================================================================
37
38// Data package format
39// Name Length Description
40// ==========================================================================================================
41// Start 2 bytes Fixed value of 0xEF01; High byte transferred first.
42// Address 4 bytes Default value is 0xFFFFFFFF, which can be modified by command.
43// High byte transferred first and at wrong adder value, module
44// will reject to transfer.
45// PID 1 byte 01H Command packet;
46// 02H Data packet; Data packet shall not appear alone in executing
47// processs, must follow command packet or acknowledge packet.
48// 07H Acknowledge packet;
49// 08H End of Data packet.
50// LENGTH 2 bytes Refers to the length of package content (command packets and data packets)
51// plus the length of Checksum (2 bytes). Unit is byte. Max length is 256 bytes.
52// And high byte is transferred first.
53// DATA - It can be commands, data, command’s parameters, acknowledge result, etc.
54// (fingerprint character value, template are all deemed as data);
55// SUM 2 bytes The arithmetic sum of package identifier, package length and all package
56// contens. Overflowing bits are omitted. high byte is transferred first.
57
58// ================================================================================
59
60// Checksum is calculated on 'length (2 bytes) + data (??)'.
61fn compute_checksum(buf: Vec<u8, 32>) -> u16 {
62 let mut checksum = 0u16;
63
64 let check_end = buf.len();
65 let checked_bytes = &buf[6..check_end];
66 for byte in checked_bytes {
67 checksum += (*byte) as u16;
68 }
69 return checksum;
70}
71
72#[embassy_executor::main]
73async fn main(_spawner: Spawner) {
74 info!("Start");
75
76 let p = embassy_rp::init(Default::default());
77
78 // Initialize the fingerprint scanner.
79 let mut config = Config::default();
80 config.baudrate = 57600;
81 config.stop_bits = StopBits::STOP1;
82 config.data_bits = DataBits::DataBits8;
83 config.parity = Parity::ParityNone;
84
85 let (uart, tx_pin, tx_dma, rx_pin, rx_dma) = (p.UART0, p.PIN_16, p.DMA_CH0, p.PIN_17, p.DMA_CH1);
86 let uart = Uart::new(uart, tx_pin, rx_pin, Irqs, tx_dma, rx_dma, config);
87 let (mut tx, mut rx) = uart.split();
88
89 let mut vec_buf: Vec<u8, 32> = heapless::Vec::new();
90 let mut data: Vec<u8, 32> = heapless::Vec::new();
91
92 let mut speeds: Vec<u8, 3> = heapless::Vec::new();
93 let _ = speeds.push(0xC8); // Slow
94 let _ = speeds.push(0x20); // Medium
95 let _ = speeds.push(0x02); // Fast
96
97 // Cycle through the three colours Red, Blue and Purple forever.
98 loop {
99 for colour in 1..=3 {
100 for speed in &speeds {
101 // Set the data first, because the length is dependent on that.
102 // However, we write the length bits before we do the data.
103 data.clear();
104 let _ = data.push(0x01); // ctrl=Breathing light
105 let _ = data.push(*speed);
106 let _ = data.push(colour as u8); // colour=Red, Blue, Purple
107 let _ = data.push(0x00); // times=Infinite
108
109 // Clear buffers
110 vec_buf.clear();
111
112 // START
113 let _ = vec_buf.extend_from_slice(&START.to_be_bytes()[..]);
114
115 // ADDRESS
116 let _ = vec_buf.extend_from_slice(&ADDRESS.to_be_bytes()[..]);
117
118 // PID
119 let _ = vec_buf.extend_from_slice(&[0x01]);
120
121 // LENGTH
122 let len: u16 = (1 + data.len() + 2).try_into().unwrap();
123 let _ = vec_buf.extend_from_slice(&len.to_be_bytes()[..]);
124
125 // COMMAND
126 let _ = vec_buf.push(0x35); // Command: AuraLedConfig
127
128 // DATA
129 let _ = vec_buf.extend_from_slice(&data);
130
131 // SUM
132 let chk = compute_checksum(vec_buf.clone());
133 let _ = vec_buf.extend_from_slice(&chk.to_be_bytes()[..]);
134
135 // =====
136
137 // Send command buffer.
138 let data_write: [u8; 16] = vec_buf.clone().into_array().unwrap();
139 debug!(" write='{:?}'", data_write[..]);
140 match tx.write(&data_write).await {
141 Ok(..) => info!("Write successful."),
142 Err(e) => error!("Write error: {:?}", e),
143 }
144
145 // =====
146
147 // Read command buffer.
148 let mut read_buf: [u8; 1] = [0; 1]; // Can only read one byte at a time!
149 let mut data_read: Vec<u8, 32> = heapless::Vec::new(); // Save buffer.
150
151 info!("Attempting read.");
152 loop {
153 // Some commands, like `Img2Tz()` needs longer, but we hard-code this to 200ms
154 // for this command.
155 match with_timeout(Duration::from_millis(200), rx.read(&mut read_buf)).await {
156 Ok(..) => {
157 // Extract and save read byte.
158 debug!(" r='{=u8:#04x}H' ({:03}D)", read_buf[0], read_buf[0]);
159 let _ = data_read.push(read_buf[0]).unwrap();
160 }
161 Err(..) => break, // TimeoutError -> Ignore.
162 }
163 }
164 info!("Read successful");
165 debug!(" read='{:?}'", data_read[..]);
166
167 Timer::after_secs(3).await;
168 info!("Changing speed.");
169 }
170
171 info!("Changing colour.");
172 }
173 }
174}
diff --git a/examples/rp23/src/bin/uart_unidir.rs b/examples/rp23/src/bin/uart_unidir.rs
new file mode 100644
index 000000000..4d3163285
--- /dev/null
+++ b/examples/rp23/src/bin/uart_unidir.rs
@@ -0,0 +1,66 @@
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::peripherals::UART1;
15use embassy_rp::uart::{Async, Config, InterruptHandler, UartRx, UartTx};
16use embassy_time::Timer;
17use {defmt_rtt as _, panic_probe as _};
18use embassy_rp::block::ImageDef;
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
34
35bind_interrupts!(struct Irqs {
36 UART1_IRQ => InterruptHandler<UART1>;
37});
38
39#[embassy_executor::main]
40async fn main(spawner: Spawner) {
41 let p = embassy_rp::init(Default::default());
42
43 let mut uart_tx = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Config::default());
44 let uart_rx = UartRx::new(p.UART1, p.PIN_5, Irqs, p.DMA_CH1, Config::default());
45
46 unwrap!(spawner.spawn(reader(uart_rx)));
47
48 info!("Writing...");
49 loop {
50 let data = [1u8, 2, 3, 4, 5, 6, 7, 8];
51 info!("TX {:?}", data);
52 uart_tx.write(&data).await.unwrap();
53 Timer::after_secs(1).await;
54 }
55}
56
57#[embassy_executor::task]
58async fn reader(mut rx: UartRx<'static, UART1, Async>) {
59 info!("Reading...");
60 loop {
61 // read a total of 4 transmissions (32 / 8) and then print the result
62 let mut buf = [0; 32];
63 rx.read(&mut buf).await.unwrap();
64 info!("RX {:?}", buf);
65 }
66}
diff --git a/examples/rp23/src/bin/usb_webusb.rs b/examples/rp23/src/bin/usb_webusb.rs
new file mode 100644
index 000000000..c9cab45c1
--- /dev/null
+++ b/examples/rp23/src/bin/usb_webusb.rs
@@ -0,0 +1,171 @@
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::peripherals::USB;
25use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler};
26use embassy_usb::class::web_usb::{Config as WebUsbConfig, State, Url, WebUsb};
27use embassy_usb::driver::{Driver, Endpoint, EndpointIn, EndpointOut};
28use embassy_usb::msos::{self, windows_version};
29use embassy_usb::{Builder, Config};
30use {defmt_rtt as _, panic_probe as _};
31use embassy_rp::block::ImageDef;
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
47
48bind_interrupts!(struct Irqs {
49 USBCTRL_IRQ => InterruptHandler<USB>;
50});
51
52// This is a randomly generated GUID to allow clients on Windows to find our device
53const DEVICE_INTERFACE_GUIDS: &[&str] = &["{AFB9A6FB-30BA-44BC-9232-806CFC875321}"];
54
55#[embassy_executor::main]
56async fn main(_spawner: Spawner) {
57 let p = embassy_rp::init(Default::default());
58
59 // Create the driver, from the HAL.
60 let driver = UsbDriver::new(p.USB, Irqs);
61
62 // Create embassy-usb Config
63 let mut config = Config::new(0xf569, 0x0001);
64 config.manufacturer = Some("Embassy");
65 config.product = Some("WebUSB example");
66 config.serial_number = Some("12345678");
67 config.max_power = 100;
68 config.max_packet_size_0 = 64;
69
70 // Required for windows compatibility.
71 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
72 config.device_class = 0xff;
73 config.device_sub_class = 0x00;
74 config.device_protocol = 0x00;
75
76 // Create embassy-usb DeviceBuilder using the driver and config.
77 // It needs some buffers for building the descriptors.
78 let mut config_descriptor = [0; 256];
79 let mut bos_descriptor = [0; 256];
80 let mut control_buf = [0; 64];
81 let mut msos_descriptor = [0; 256];
82
83 let webusb_config = WebUsbConfig {
84 max_packet_size: 64,
85 vendor_code: 1,
86 // 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.
87 landing_url: Some(Url::new("http://localhost:8080")),
88 };
89
90 let mut state = State::new();
91
92 let mut builder = Builder::new(
93 driver,
94 config,
95 &mut config_descriptor,
96 &mut bos_descriptor,
97 &mut msos_descriptor,
98 &mut control_buf,
99 );
100
101 // Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.
102 // We tell Windows that this entire device is compatible with the "WINUSB" feature,
103 // which causes it to use the built-in WinUSB driver automatically, which in turn
104 // can be used by libusb/rusb software without needing a custom driver or INF file.
105 // In principle you might want to call msos_feature() just on a specific function,
106 // if your device also has other functions that still use standard class drivers.
107 builder.msos_descriptor(windows_version::WIN8_1, 0);
108 builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
109 builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
110 "DeviceInterfaceGUIDs",
111 msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
112 ));
113
114 // Create classes on the builder (WebUSB just needs some setup, but doesn't return anything)
115 WebUsb::configure(&mut builder, &mut state, &webusb_config);
116 // Create some USB bulk endpoints for testing.
117 let mut endpoints = WebEndpoints::new(&mut builder, &webusb_config);
118
119 // Build the builder.
120 let mut usb = builder.build();
121
122 // Run the USB device.
123 let usb_fut = usb.run();
124
125 // Do some WebUSB transfers.
126 let webusb_fut = async {
127 loop {
128 endpoints.wait_connected().await;
129 info!("Connected");
130 endpoints.echo().await;
131 }
132 };
133
134 // Run everything concurrently.
135 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
136 join(usb_fut, webusb_fut).await;
137}
138
139struct WebEndpoints<'d, D: Driver<'d>> {
140 write_ep: D::EndpointIn,
141 read_ep: D::EndpointOut,
142}
143
144impl<'d, D: Driver<'d>> WebEndpoints<'d, D> {
145 fn new(builder: &mut Builder<'d, D>, config: &'d WebUsbConfig<'d>) -> Self {
146 let mut func = builder.function(0xff, 0x00, 0x00);
147 let mut iface = func.interface();
148 let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None);
149
150 let write_ep = alt.endpoint_bulk_in(config.max_packet_size);
151 let read_ep = alt.endpoint_bulk_out(config.max_packet_size);
152
153 WebEndpoints { write_ep, read_ep }
154 }
155
156 // Wait until the device's endpoints are enabled.
157 async fn wait_connected(&mut self) {
158 self.read_ep.wait_enabled().await
159 }
160
161 // Echo data back to the host.
162 async fn echo(&mut self) {
163 let mut buf = [0; 64];
164 loop {
165 let n = self.read_ep.read(&mut buf).await.unwrap();
166 let data = &buf[..n];
167 info!("Data read: {:x}", data);
168 self.write_ep.write(data).await.unwrap();
169 }
170 }
171}
diff --git a/examples/rp23/src/bin/watchdog.rs b/examples/rp23/src/bin/watchdog.rs
new file mode 100644
index 000000000..8cc723150
--- /dev/null
+++ b/examples/rp23/src/bin/watchdog.rs
@@ -0,0 +1,67 @@
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::gpio;
11use embassy_rp::watchdog::*;
12use embassy_time::{Duration, Timer};
13use gpio::{Level, Output};
14use {defmt_rtt as _, panic_probe as _};
15use embassy_rp::block::ImageDef;
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
32#[embassy_executor::main]
33async fn main(_spawner: Spawner) {
34 let p = embassy_rp::init(Default::default());
35 info!("Hello world!");
36
37 let mut watchdog = Watchdog::new(p.WATCHDOG);
38 let mut led = Output::new(p.PIN_25, Level::Low);
39
40 // Set the LED high for 2 seconds so we know when we're about to start the watchdog
41 led.set_high();
42 Timer::after_secs(2).await;
43
44 // Set to watchdog to reset if it's not fed within 1.05 seconds, and start it
45 watchdog.start(Duration::from_millis(1_050));
46 info!("Started the watchdog timer");
47
48 // Blink once a second for 5 seconds, feed the watchdog timer once a second to avoid a reset
49 for _ in 1..=5 {
50 led.set_low();
51 Timer::after_millis(500).await;
52 led.set_high();
53 Timer::after_millis(500).await;
54 info!("Feeding watchdog");
55 watchdog.feed();
56 }
57
58 info!("Stopped feeding, device will reset in 1.05 seconds");
59 // Blink 10 times per second, not feeding the watchdog.
60 // The processor should reset in 1.05 seconds.
61 loop {
62 led.set_low();
63 Timer::after_millis(100).await;
64 led.set_high();
65 Timer::after_millis(100).await;
66 }
67}
diff --git a/examples/rp23/src/bin/zerocopy.rs b/examples/rp23/src/bin/zerocopy.rs
new file mode 100644
index 000000000..e379d9c00
--- /dev/null
+++ b/examples/rp23/src/bin/zerocopy.rs
@@ -0,0 +1,110 @@
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::gpio::Pull;
14use embassy_rp::peripherals::DMA_CH0;
15use embassy_sync::blocking_mutex::raw::NoopRawMutex;
16use embassy_sync::zerocopy_channel::{Channel, Receiver, Sender};
17use embassy_time::{Duration, Ticker, Timer};
18use static_cell::StaticCell;
19use {defmt_rtt as _, panic_probe as _};
20use embassy_rp::block::ImageDef;
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
36
37type SampleBuffer = [u16; 512];
38
39bind_interrupts!(struct Irqs {
40 ADC_IRQ_FIFO => InterruptHandler;
41});
42
43const BLOCK_SIZE: usize = 512;
44const NUM_BLOCKS: usize = 2;
45static MAX: AtomicU16 = AtomicU16::new(0);
46
47struct AdcParts {
48 adc: Adc<'static, Async>,
49 pin: adc::Channel<'static>,
50 dma: DMA_CH0,
51}
52
53#[embassy_executor::main]
54async fn main(spawner: Spawner) {
55 let p = embassy_rp::init(Default::default());
56 info!("Here we go!");
57
58 let adc_parts = AdcParts {
59 adc: Adc::new(p.ADC, Irqs, Config::default()),
60 pin: adc::Channel::new_pin(p.PIN_29, Pull::None),
61 dma: p.DMA_CH0,
62 };
63
64 static BUF: StaticCell<[SampleBuffer; NUM_BLOCKS]> = StaticCell::new();
65 let buf = BUF.init([[0; BLOCK_SIZE]; NUM_BLOCKS]);
66
67 static CHANNEL: StaticCell<Channel<'_, NoopRawMutex, SampleBuffer>> = StaticCell::new();
68 let channel = CHANNEL.init(Channel::new(buf));
69 let (sender, receiver) = channel.split();
70
71 spawner.must_spawn(consumer(receiver));
72 spawner.must_spawn(producer(sender, adc_parts));
73
74 let mut ticker = Ticker::every(Duration::from_secs(1));
75 loop {
76 ticker.next().await;
77 let max = MAX.load(Ordering::Relaxed);
78 info!("latest block's max value: {:?}", max);
79 }
80}
81
82#[embassy_executor::task]
83async fn producer(mut sender: Sender<'static, NoopRawMutex, SampleBuffer>, mut adc: AdcParts) {
84 loop {
85 // Obtain a free buffer from the channel
86 let buf = sender.send().await;
87
88 // Fill it with data
89 adc.adc.read_many(&mut adc.pin, buf, 1, &mut adc.dma).await.unwrap();
90
91 // Notify the channel that the buffer is now ready to be received
92 sender.send_done();
93 }
94}
95
96#[embassy_executor::task]
97async fn consumer(mut receiver: Receiver<'static, NoopRawMutex, SampleBuffer>) {
98 loop {
99 // Receive a buffer from the channel
100 let buf = receiver.receive().await;
101
102 // Simulate using the data, while the producer is filling up the next buffer
103 Timer::after_micros(1000).await;
104 let max = buf.iter().max().unwrap();
105 MAX.store(*max, Ordering::Relaxed);
106
107 // Notify the channel that the buffer is now ready to be reused
108 receiver.receive_done();
109 }
110}