aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornerwalt <[email protected]>2024-08-12 06:23:57 -0600
committernerwalt <[email protected]>2024-08-12 06:23:57 -0600
commit2387fa89b2197bb03aefae4998205fe69a19f1b2 (patch)
treedab52c78b211a81435595af10303e62d5fb7fbc8
parentb7dc4c7b6f057d4e22b179563ca8565eedbff18a (diff)
parent66a5a33da9b8686265521bcd4a50bbe1a56cbe93 (diff)
Merge branch 'main' into fix-nrf9120-features
-rwxr-xr-x.github/ci/test.sh3
-rwxr-xr-xci.sh17
-rw-r--r--embassy-rp/Cargo.toml24
-rw-r--r--embassy-rp/build.rs18
-rw-r--r--embassy-rp/link-rp.x.in2
-rw-r--r--embassy-rp/src/adc.rs29
-rw-r--r--embassy-rp/src/binary_info/consts.rs31
-rw-r--r--embassy-rp/src/binary_info/macros.rs168
-rw-r--r--embassy-rp/src/binary_info/mod.rs172
-rw-r--r--embassy-rp/src/binary_info/types.rs192
-rw-r--r--embassy-rp/src/block.rs1076
-rw-r--r--embassy-rp/src/clocks.rs81
-rw-r--r--embassy-rp/src/dma.rs41
-rw-r--r--embassy-rp/src/flash.rs27
-rw-r--r--embassy-rp/src/gpio.rs78
-rw-r--r--embassy-rp/src/i2c.rs38
-rw-r--r--embassy-rp/src/lib.rs219
-rw-r--r--embassy-rp/src/multicore.rs36
-rw-r--r--embassy-rp/src/pio/mod.rs95
-rw-r--r--embassy-rp/src/pwm.rs47
-rw-r--r--embassy-rp/src/reset.rs2
-rw-r--r--embassy-rp/src/spi.rs98
-rw-r--r--embassy-rp/src/time_driver.rs72
-rw-r--r--embassy-rp/src/uart/mod.rs146
-rw-r--r--embassy-rp/src/usb.rs6
-rw-r--r--embassy-rp/src/watchdog.rs1
-rw-r--r--examples/boot/application/rp/Cargo.toml2
-rw-r--r--examples/boot/bootloader/rp/Cargo.toml2
-rw-r--r--examples/rp/Cargo.toml4
-rw-r--r--examples/rp23/.cargo/config.toml9
-rw-r--r--examples/rp23/Cargo.toml80
-rw-r--r--examples/rp23/assets/ferris.rawbin0 -> 11008 bytes
-rw-r--r--examples/rp23/build.rs35
-rw-r--r--examples/rp23/memory.x74
-rw-r--r--examples/rp23/src/bin/adc.rs63
-rw-r--r--examples/rp23/src/bin/adc_dma.rs69
-rw-r--r--examples/rp23/src/bin/assign_resources.rs94
-rw-r--r--examples/rp23/src/bin/blinky.rs44
-rw-r--r--examples/rp23/src/bin/blinky_two_channels.rs65
-rw-r--r--examples/rp23/src/bin/blinky_two_tasks.rs64
-rw-r--r--examples/rp23/src/bin/button.rs43
-rw-r--r--examples/rp23/src/bin/debounce.rs95
-rw-r--r--examples/rp23/src/bin/flash.rs149
-rw-r--r--examples/rp23/src/bin/gpio_async.rs55
-rw-r--r--examples/rp23/src/bin/gpout.rs52
-rw-r--r--examples/rp23/src/bin/i2c_async.rs125
-rw-r--r--examples/rp23/src/bin/i2c_async_embassy.rs100
-rw-r--r--examples/rp23/src/bin/i2c_blocking.rs89
-rw-r--r--examples/rp23/src/bin/i2c_slave.rs132
-rw-r--r--examples/rp23/src/bin/interrupt.rs109
-rw-r--r--examples/rp23/src/bin/multicore.rs81
-rw-r--r--examples/rp23/src/bin/multiprio.rs160
-rw-r--r--examples/rp23/src/bin/pio_async.rs145
-rw-r--r--examples/rp23/src/bin/pio_dma.rs98
-rw-r--r--examples/rp23/src/bin/pio_hd44780.rs255
-rw-r--r--examples/rp23/src/bin/pio_i2s.rs140
-rw-r--r--examples/rp23/src/bin/pio_pwm.rs133
-rw-r--r--examples/rp23/src/bin/pio_rotary_encoder.rs95
-rw-r--r--examples/rp23/src/bin/pio_servo.rs223
-rw-r--r--examples/rp23/src/bin/pio_stepper.rs183
-rw-r--r--examples/rp23/src/bin/pio_ws2812.rs176
-rw-r--r--examples/rp23/src/bin/pwm.rs44
-rw-r--r--examples/rp23/src/bin/pwm_input.rs41
-rw-r--r--examples/rp23/src/bin/rosc.rs46
-rw-r--r--examples/rp23/src/bin/shared_bus.rs130
-rw-r--r--examples/rp23/src/bin/sharing.rs165
-rw-r--r--examples/rp23/src/bin/spi.rs61
-rw-r--r--examples/rp23/src/bin/spi_async.rs46
-rw-r--r--examples/rp23/src/bin/spi_display.rs327
-rw-r--r--examples/rp23/src/bin/spi_sdmmc.rs98
-rw-r--r--examples/rp23/src/bin/uart.rs40
-rw-r--r--examples/rp23/src/bin/uart_buffered_split.rs73
-rw-r--r--examples/rp23/src/bin/uart_r503.rs173
-rw-r--r--examples/rp23/src/bin/uart_unidir.rs65
-rw-r--r--examples/rp23/src/bin/usb_webusb.rs170
-rw-r--r--examples/rp23/src/bin/watchdog.rs66
-rw-r--r--examples/rp23/src/bin/zerocopy.rs109
-rw-r--r--tests/rp/Cargo.toml2
78 files changed, 7516 insertions, 102 deletions
diff --git a/.github/ci/test.sh b/.github/ci/test.sh
index 41da644fc..da0021684 100755
--- a/.github/ci/test.sh
+++ b/.github/ci/test.sh
@@ -21,7 +21,8 @@ cargo test --manifest-path ./embassy-boot/Cargo.toml --features ed25519-salty
21 21
22cargo test --manifest-path ./embassy-nrf/Cargo.toml --no-default-features --features nrf52840,time-driver-rtc1,gpiote 22cargo test --manifest-path ./embassy-nrf/Cargo.toml --no-default-features --features nrf52840,time-driver-rtc1,gpiote
23 23
24cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --features time-driver 24cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --features time-driver,rp2040,_test
25cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --features time-driver,rp235xa,_test
25 26
26cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f429vg,exti,time-driver-any,exti 27cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f429vg,exti,time-driver-any,exti
27cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f732ze,exti,time-driver-any,exti 28cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f732ze,exti,time-driver-any,exti
diff --git a/ci.sh b/ci.sh
index 0cd0c54bc..82108536b 100755
--- a/ci.sh
+++ b/ci.sh
@@ -82,10 +82,12 @@ cargo batch \
82 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,defmt,time,time-driver-rtc1 \ 82 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,defmt,time,time-driver-rtc1 \
83 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,defmt,time \ 83 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,defmt,time \
84 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,time \ 84 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,time \
85 --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,defmt \ 85 --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,defmt,rp2040 \
86 --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,log \ 86 --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,log,rp2040 \
87 --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,intrinsics \ 87 --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,intrinsics,rp2040 \
88 --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,qspi-as-gpio \ 88 --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,qspi-as-gpio,rp2040 \
89 --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv8m.main-none-eabihf --features time-driver,defmt,rp235xa \
90 --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv8m.main-none-eabihf --features time-driver,log,rp235xa \
89 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,time \ 91 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,time \
90 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any,time \ 92 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any,time \
91 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time \ 93 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time \
@@ -175,12 +177,12 @@ cargo batch \
175 --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt,firmware-logs' \ 177 --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt,firmware-logs' \
176 --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log,firmware-logs,bluetooth' \ 178 --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log,firmware-logs,bluetooth' \
177 --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt,firmware-logs,bluetooth' \ 179 --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt,firmware-logs,bluetooth' \
178 --- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features '' \ 180 --- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features 'embassy-rp/rp2040' \
179 --- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features 'overclock' \ 181 --- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features 'embassy-rp/rp2040,overclock' \
180 --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ 182 --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
181 --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ 183 --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
182 --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9120-ns \ 184 --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9120-ns \
183 --- build --release --manifest-path embassy-boot-rp/Cargo.toml --target thumbv6m-none-eabi \ 185 --- build --release --manifest-path embassy-boot-rp/Cargo.toml --target thumbv6m-none-eabi --features embassy-rp/rp2040 \
184 --- build --release --manifest-path embassy-boot-stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32l496zg \ 186 --- build --release --manifest-path embassy-boot-stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32l496zg \
185 --- build --release --manifest-path docs/examples/basic/Cargo.toml --target thumbv7em-none-eabi \ 187 --- build --release --manifest-path docs/examples/basic/Cargo.toml --target thumbv7em-none-eabi \
186 --- build --release --manifest-path docs/examples/layer-by-layer/blinky-pac/Cargo.toml --target thumbv7em-none-eabi \ 188 --- build --release --manifest-path docs/examples/layer-by-layer/blinky-pac/Cargo.toml --target thumbv7em-none-eabi \
@@ -195,6 +197,7 @@ cargo batch \
195 --- build --release --manifest-path examples/nrf9151/ns/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf9151/ns \ 197 --- build --release --manifest-path examples/nrf9151/ns/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf9151/ns \
196 --- build --release --manifest-path examples/nrf51/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/nrf51 \ 198 --- build --release --manifest-path examples/nrf51/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/nrf51 \
197 --- build --release --manifest-path examples/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/rp \ 199 --- build --release --manifest-path examples/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/rp \
200 --- build --release --manifest-path examples/rp23/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/rp23 \
198 --- build --release --manifest-path examples/stm32f0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32f0 \ 201 --- build --release --manifest-path examples/stm32f0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32f0 \
199 --- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f1 \ 202 --- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f1 \
200 --- build --release --manifest-path examples/stm32f2/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f2 \ 203 --- build --release --manifest-path examples/stm32f2/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f2 \
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml
index b7f5a2bcc..453f1aa0f 100644
--- a/embassy-rp/Cargo.toml
+++ b/embassy-rp/Cargo.toml
@@ -14,7 +14,9 @@ src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/emba
14src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-rp/src/" 14src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-rp/src/"
15features = ["defmt", "unstable-pac", "time-driver"] 15features = ["defmt", "unstable-pac", "time-driver"]
16flavors = [ 16flavors = [
17 { name = "rp2040", target = "thumbv6m-none-eabi" }, 17 { name = "rp2040", target = "thumbv6m-none-eabi", features = ["rp2040"] },
18 { name = "rp235xa", target = "thumbv8m.main-none-eabi", features = ["rp235xa"] },
19 { name = "rp235xb", target = "thumbv8m.main-none-eabi", features = ["rp235xb"] },
18] 20]
19 21
20[package.metadata.docs.rs] 22[package.metadata.docs.rs]
@@ -88,6 +90,23 @@ boot2-w25x10cl = []
88## ``` 90## ```
89boot2-none = [] 91boot2-none = []
90 92
93## Configure the hal for use with the rp2040
94rp2040 = ["rp-pac/rp2040"]
95_rp235x = ["rp-pac/rp235x"]
96## Configure the hal for use with the rp235xA
97rp235xa = ["_rp235x"]
98## Configure the hal for use with the rp235xB
99rp235xb = ["_rp235x"]
100
101# hack around cortex-m peripherals being wrong when running tests.
102_test = []
103
104## Add a binary-info header block containing picotool-compatible metadata.
105##
106## Takes up a little flash space, but picotool can then report the name of your
107## program and other details.
108binary-info = [ "rt" ]
109
91[dependencies] 110[dependencies]
92embassy-sync = { version = "0.6.0", path = "../embassy-sync" } 111embassy-sync = { version = "0.6.0", path = "../embassy-sync" }
93embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } 112embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true }
@@ -112,7 +131,7 @@ embedded-storage-async = { version = "0.4.1" }
112rand_core = "0.6.4" 131rand_core = "0.6.4"
113fixed = "1.23.1" 132fixed = "1.23.1"
114 133
115rp-pac = { version = "6" } 134rp-pac = { git = "https://github.com/embassy-rs/rp-pac.git", rev = "a7f42d25517f7124ad3b4ed492dec8b0f50a0e6c", feature = ["rt"] }
116 135
117embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 136embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
118embedded-hal-1 = { package = "embedded-hal", version = "1.0" } 137embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
@@ -123,6 +142,7 @@ pio-proc = {version= "0.2" }
123pio = {version= "0.2.1" } 142pio = {version= "0.2.1" }
124rp2040-boot2 = "0.3" 143rp2040-boot2 = "0.3"
125document-features = "0.2.7" 144document-features = "0.2.7"
145sha2-const-stable = "0.1"
126 146
127[dev-dependencies] 147[dev-dependencies]
128embassy-executor = { version = "0.6.0", path = "../embassy-executor", features = ["arch-std", "executor-thread"] } 148embassy-executor = { version = "0.6.0", path = "../embassy-executor", features = ["arch-std", "executor-thread"] }
diff --git a/embassy-rp/build.rs b/embassy-rp/build.rs
index f41ccd220..3216a3826 100644
--- a/embassy-rp/build.rs
+++ b/embassy-rp/build.rs
@@ -4,14 +4,16 @@ use std::io::Write;
4use std::path::PathBuf; 4use std::path::PathBuf;
5 5
6fn main() { 6fn main() {
7 // Put the linker script somewhere the linker can find it 7 if env::var("CARGO_FEATURE_RP2040").is_ok() {
8 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); 8 // Put the linker script somewhere the linker can find it
9 let link_x = include_bytes!("link-rp.x.in"); 9 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
10 let mut f = File::create(out.join("link-rp.x")).unwrap(); 10 let link_x = include_bytes!("link-rp.x.in");
11 f.write_all(link_x).unwrap(); 11 let mut f = File::create(out.join("link-rp.x")).unwrap();
12 f.write_all(link_x).unwrap();
12 13
13 println!("cargo:rustc-link-search={}", out.display()); 14 println!("cargo:rustc-link-search={}", out.display());
14 15
15 println!("cargo:rerun-if-changed=build.rs"); 16 println!("cargo:rerun-if-changed=build.rs");
16 println!("cargo:rerun-if-changed=link-rp.x.in"); 17 println!("cargo:rerun-if-changed=link-rp.x.in");
18 }
17} 19}
diff --git a/embassy-rp/link-rp.x.in b/embassy-rp/link-rp.x.in
index af463f963..1839dda68 100644
--- a/embassy-rp/link-rp.x.in
+++ b/embassy-rp/link-rp.x.in
@@ -5,4 +5,4 @@ SECTIONS {
5 { 5 {
6 KEEP(*(.boot2)); 6 KEEP(*(.boot2));
7 } > BOOT2 7 } > BOOT2
8} \ No newline at end of file 8}
diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs
index eb1cc9a66..9582e43c8 100644
--- a/embassy-rp/src/adc.rs
+++ b/embassy-rp/src/adc.rs
@@ -11,6 +11,7 @@ use embassy_sync::waitqueue::AtomicWaker;
11use crate::gpio::{self, AnyPin, Pull, SealedPin as GpioPin}; 11use crate::gpio::{self, AnyPin, Pull, SealedPin as GpioPin};
12use crate::interrupt::typelevel::Binding; 12use crate::interrupt::typelevel::Binding;
13use crate::interrupt::InterruptExt; 13use crate::interrupt::InterruptExt;
14use crate::pac::dma::vals::TreqSel;
14use crate::peripherals::{ADC, ADC_TEMP_SENSOR}; 15use crate::peripherals::{ADC, ADC_TEMP_SENSOR};
15use crate::{dma, interrupt, pac, peripherals, Peripheral, RegExt}; 16use crate::{dma, interrupt, pac, peripherals, Peripheral, RegExt};
16 17
@@ -34,6 +35,8 @@ impl<'p> Channel<'p> {
34 pub fn new_pin(pin: impl Peripheral<P = impl AdcPin + 'p> + 'p, pull: Pull) -> Self { 35 pub fn new_pin(pin: impl Peripheral<P = impl AdcPin + 'p> + 'p, pull: Pull) -> Self {
35 into_ref!(pin); 36 into_ref!(pin);
36 pin.pad_ctrl().modify(|w| { 37 pin.pad_ctrl().modify(|w| {
38 #[cfg(feature = "_rp235x")]
39 w.set_iso(false);
37 // manual says: 40 // manual says:
38 // 41 //
39 // > When using an ADC input shared with a GPIO pin, the pin’s 42 // > When using an ADC input shared with a GPIO pin, the pin’s
@@ -229,7 +232,10 @@ impl<'d> Adc<'d, Async> {
229 div: u16, 232 div: u16,
230 dma: impl Peripheral<P = impl dma::Channel>, 233 dma: impl Peripheral<P = impl dma::Channel>,
231 ) -> Result<(), Error> { 234 ) -> Result<(), Error> {
235 #[cfg(feature = "rp2040")]
232 let mut rrobin = 0_u8; 236 let mut rrobin = 0_u8;
237 #[cfg(feature = "_rp235x")]
238 let mut rrobin = 0_u16;
233 for c in channels { 239 for c in channels {
234 rrobin |= 1 << c; 240 rrobin |= 1 << c;
235 } 241 }
@@ -278,7 +284,7 @@ impl<'d> Adc<'d, Async> {
278 } 284 }
279 let auto_reset = ResetDmaConfig; 285 let auto_reset = ResetDmaConfig;
280 286
281 let dma = unsafe { dma::read(dma, r.fifo().as_ptr() as *const W, buf as *mut [W], 36) }; 287 let dma = unsafe { dma::read(dma, r.fifo().as_ptr() as *const W, buf as *mut [W], TreqSel::ADC) };
282 // start conversions and wait for dma to finish. we can't report errors early 288 // start conversions and wait for dma to finish. we can't report errors early
283 // because there's no interrupt to signal them, and inspecting every element 289 // because there's no interrupt to signal them, and inspecting every element
284 // of the fifo is too costly to do here. 290 // of the fifo is too costly to do here.
@@ -423,10 +429,31 @@ macro_rules! impl_pin {
423 }; 429 };
424} 430}
425 431
432#[cfg(any(feature = "rp235xa", feature = "rp2040"))]
426impl_pin!(PIN_26, 0); 433impl_pin!(PIN_26, 0);
434#[cfg(any(feature = "rp235xa", feature = "rp2040"))]
427impl_pin!(PIN_27, 1); 435impl_pin!(PIN_27, 1);
436#[cfg(any(feature = "rp235xa", feature = "rp2040"))]
428impl_pin!(PIN_28, 2); 437impl_pin!(PIN_28, 2);
438#[cfg(any(feature = "rp235xa", feature = "rp2040"))]
429impl_pin!(PIN_29, 3); 439impl_pin!(PIN_29, 3);
430 440
441#[cfg(feature = "rp235xb")]
442impl_pin!(PIN_40, 0);
443#[cfg(feature = "rp235xb")]
444impl_pin!(PIN_41, 1);
445#[cfg(feature = "rp235xb")]
446impl_pin!(PIN_42, 2);
447#[cfg(feature = "rp235xb")]
448impl_pin!(PIN_43, 3);
449#[cfg(feature = "rp235xb")]
450impl_pin!(PIN_44, 4);
451#[cfg(feature = "rp235xb")]
452impl_pin!(PIN_45, 5);
453#[cfg(feature = "rp235xb")]
454impl_pin!(PIN_46, 6);
455#[cfg(feature = "rp235xb")]
456impl_pin!(PIN_47, 7);
457
431impl SealedAdcChannel for peripherals::ADC_TEMP_SENSOR {} 458impl SealedAdcChannel for peripherals::ADC_TEMP_SENSOR {}
432impl AdcChannel for peripherals::ADC_TEMP_SENSOR {} 459impl AdcChannel for peripherals::ADC_TEMP_SENSOR {}
diff --git a/embassy-rp/src/binary_info/consts.rs b/embassy-rp/src/binary_info/consts.rs
new file mode 100644
index 000000000..c8270c081
--- /dev/null
+++ b/embassy-rp/src/binary_info/consts.rs
@@ -0,0 +1,31 @@
1//! Constants for binary info
2
3/// All Raspberry Pi specified IDs have this tag.
4///
5/// You can create your own for custom fields.
6pub const TAG_RASPBERRY_PI: u16 = super::make_tag(b"RP");
7
8/// Used to note the program name - use with StringEntry
9pub const ID_RP_PROGRAM_NAME: u32 = 0x02031c86;
10/// Used to note the program version - use with StringEntry
11pub const ID_RP_PROGRAM_VERSION_STRING: u32 = 0x11a9bc3a;
12/// Used to note the program build date - use with StringEntry
13pub const ID_RP_PROGRAM_BUILD_DATE_STRING: u32 = 0x9da22254;
14/// Used to note the size of the binary - use with IntegerEntry
15pub const ID_RP_BINARY_END: u32 = 0x68f465de;
16/// Used to note a URL for the program - use with StringEntry
17pub const ID_RP_PROGRAM_URL: u32 = 0x1856239a;
18/// Used to note a description of the program - use with StringEntry
19pub const ID_RP_PROGRAM_DESCRIPTION: u32 = 0xb6a07c19;
20/// Used to note some feature of the program - use with StringEntry
21pub const ID_RP_PROGRAM_FEATURE: u32 = 0xa1f4b453;
22/// Used to note some whether this was a Debug or Release build - use with StringEntry
23pub const ID_RP_PROGRAM_BUILD_ATTRIBUTE: u32 = 0x4275f0d3;
24/// Used to note the Pico SDK version used - use with StringEntry
25pub const ID_RP_SDK_VERSION: u32 = 0x5360b3ab;
26/// Used to note which board this program targets - use with StringEntry
27pub const ID_RP_PICO_BOARD: u32 = 0xb63cffbb;
28/// Used to note which `boot2` image this program uses - use with StringEntry
29pub const ID_RP_BOOT2_NAME: u32 = 0x7f8882e1;
30
31// End of file
diff --git a/embassy-rp/src/binary_info/macros.rs b/embassy-rp/src/binary_info/macros.rs
new file mode 100644
index 000000000..0d6ba5eb5
--- /dev/null
+++ b/embassy-rp/src/binary_info/macros.rs
@@ -0,0 +1,168 @@
1//! Handy macros for making Binary Info entries
2
3/// Generate a static item containing the given environment variable,
4/// and return its [`EntryAddr`](super::EntryAddr).
5#[macro_export]
6macro_rules! binary_info_env {
7 ($tag:expr, $id:expr, $env_var_name:expr) => {
8 $crate::binary_info_str!($tag, $id, {
9 let value = concat!(env!($env_var_name), "\0");
10 // # Safety
11 //
12 // We used `concat!` to null-terminate on the line above.
13 let value_cstr = unsafe { core::ffi::CStr::from_bytes_with_nul_unchecked(value.as_bytes()) };
14 value_cstr
15 })
16 };
17}
18
19/// Generate a static item containing the given string, and return its
20/// [`EntryAddr`](super::EntryAddr).
21///
22/// You must pass a numeric tag, a numeric ID, and `&CStr` (which is always
23/// null-terminated).
24#[macro_export]
25macro_rules! binary_info_str {
26 ($tag:expr, $id:expr, $str:expr) => {{
27 static ENTRY: $crate::binary_info::StringEntry = $crate::binary_info::StringEntry::new($tag, $id, $str);
28 ENTRY.addr()
29 }};
30}
31
32/// Generate a static item containing the given string, and return its
33/// [`EntryAddr`](super::EntryAddr).
34///
35/// You must pass a numeric tag, a numeric ID, and `&CStr` (which is always
36/// null-terminated).
37#[macro_export]
38macro_rules! binary_info_int {
39 ($tag:expr, $id:expr, $int:expr) => {{
40 static ENTRY: $crate::binary_info::IntegerEntry = $crate::binary_info::IntegerEntry::new($tag, $id, $int);
41 ENTRY.addr()
42 }};
43}
44
45/// Generate a static item containing the program name, and return its
46/// [`EntryAddr`](super::EntryAddr).
47#[macro_export]
48macro_rules! binary_info_rp_program_name {
49 ($name:expr) => {
50 $crate::binary_info_str!(
51 $crate::binary_info::consts::TAG_RASPBERRY_PI,
52 $crate::binary_info::consts::ID_RP_PROGRAM_NAME,
53 $name
54 )
55 };
56}
57
58/// Generate a static item containing the `CARGO_BIN_NAME` as the program name,
59/// and return its [`EntryAddr`](super::EntryAddr).
60#[macro_export]
61macro_rules! binary_info_rp_cargo_bin_name {
62 () => {
63 $crate::binary_info_env!(
64 $crate::binary_info::consts::TAG_RASPBERRY_PI,
65 $crate::binary_info::consts::ID_RP_PROGRAM_NAME,
66 "CARGO_BIN_NAME"
67 )
68 };
69}
70
71/// Generate a static item containing the program version, and return its
72/// [`EntryAddr`](super::EntryAddr).
73#[macro_export]
74macro_rules! binary_info_rp_program_version {
75 ($version:expr) => {{
76 $crate::binary_info_str!(
77 $crate::binary_info::consts::TAG_RASPBERRY_PI,
78 $crate::binary_info::consts::ID_RP_PROGRAM_VERSION,
79 $version
80 )
81 }};
82}
83
84/// Generate a static item containing the `CARGO_PKG_VERSION` as the program
85/// version, and return its [`EntryAddr`](super::EntryAddr).
86#[macro_export]
87macro_rules! binary_info_rp_cargo_version {
88 () => {
89 $crate::binary_info_env!(
90 $crate::binary_info::consts::TAG_RASPBERRY_PI,
91 $crate::binary_info::consts::ID_RP_PROGRAM_VERSION_STRING,
92 "CARGO_PKG_VERSION"
93 )
94 };
95}
96
97/// Generate a static item containing the program URL, and return its
98/// [`EntryAddr`](super::EntryAddr).
99#[macro_export]
100macro_rules! binary_info_rp_program_url {
101 ($url:expr) => {
102 $crate::binary_info_str!(
103 $crate::binary_info::consts::TAG_RASPBERRY_PI,
104 $crate::binary_info::consts::ID_RP_PROGRAM_URL,
105 $url
106 )
107 };
108}
109
110/// Generate a static item containing the `CARGO_PKG_HOMEPAGE` as the program URL,
111/// and return its [`EntryAddr`](super::EntryAddr).
112#[macro_export]
113macro_rules! binary_info_rp_cargo_homepage_url {
114 () => {
115 $crate::binary_info_env!(
116 $crate::binary_info::consts::TAG_RASPBERRY_PI,
117 $crate::binary_info::consts::ID_RP_PROGRAM_URL,
118 "CARGO_PKG_HOMEPAGE"
119 )
120 };
121}
122
123/// Generate a static item containing the program description, and return its
124/// [`EntryAddr`](super::EntryAddr).
125#[macro_export]
126macro_rules! binary_info_rp_program_description {
127 ($description:expr) => {
128 $crate::binary_info_str!(
129 $crate::binary_info::consts::TAG_RASPBERRY_PI,
130 $crate::binary_info::consts::ID_RP_PROGRAM_DESCRIPTION,
131 $description
132 )
133 };
134}
135
136/// Generate a static item containing whether this is a debug or a release
137/// build, and return its [`EntryAddr`](super::EntryAddr).
138#[macro_export]
139macro_rules! binary_info_rp_program_build_attribute {
140 () => {
141 $crate::binary_info_str!(
142 $crate::binary_info::consts::TAG_RASPBERRY_PI,
143 $crate::binary_info::consts::ID_RP_PROGRAM_BUILD_ATTRIBUTE,
144 {
145 if cfg!(debug_assertions) {
146 c"debug"
147 } else {
148 c"release"
149 }
150 }
151 )
152 };
153}
154
155/// Generate a static item containing the specific board this program runs on,
156/// and return its [`EntryAddr`](super::EntryAddr).
157#[macro_export]
158macro_rules! binary_info_rp_pico_board {
159 ($board:expr) => {
160 $crate::binary_info_str!(
161 $crate::binary_info::consts::TAG_RASPBERRY_PI,
162 $crate::binary_info::consts::ID_RP_PICO_BOARD,
163 $board
164 )
165 };
166}
167
168// End of file
diff --git a/embassy-rp/src/binary_info/mod.rs b/embassy-rp/src/binary_info/mod.rs
new file mode 100644
index 000000000..213565cdf
--- /dev/null
+++ b/embassy-rp/src/binary_info/mod.rs
@@ -0,0 +1,172 @@
1//! Code and types for creating Picotool compatible "Binary Info" metadata
2//!
3//! Add something like this to your program, and compile with the "binary-info"
4//! and "rt" features:
5//!
6//! ```
7//! #[link_section = ".bi_entries"]
8//! #[used]
9//! pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 3] = [
10//! embassy_rp::binary_info_rp_program_name!(c"Program Name Here"),
11//! embassy_rp::binary_info_rp_cargo_version!(),
12//! embassy_rp::binary_info_int!(embassy_rp::binary_info::make_tag(b"JP"), 0x0000_0001, 0x12345678),
13//! ];
14//! ```
15
16pub mod consts;
17
18mod types;
19pub use types::*;
20
21#[macro_use]
22mod macros;
23
24extern "C" {
25 /// The linker script sets this symbol to have the address of the first
26 /// entry in the `.bi_entries` section.
27 static __bi_entries_start: EntryAddr;
28 /// The linker script sets this symbol to have the address just past the
29 /// last entry in the `.bi_entries` section.
30 static __bi_entries_end: EntryAddr;
31 /// The linker script sets this symbol to have the address of the first
32 /// entry in the `.data` section.
33 static __sdata: u32;
34 /// The linker script sets this symbol to have the address just past the
35 /// first entry in the `.data` section.
36 static __edata: u32;
37 /// The linker script sets this symbol to have the address of the
38 /// initialisation data for the first entry in the `.data` section (i.e. a
39 /// flash address, not a RAM address).
40 static __sidata: u32;
41}
42
43/// Picotool can find this block in our ELF file and report interesting
44/// metadata.
45///
46/// The data here tells picotool the start and end flash addresses of our
47/// metadata.
48#[cfg(feature = "binary-info")]
49#[link_section = ".start_block"]
50#[used]
51pub static PICOTOOL_HEADER: Header = unsafe {
52 Header::new(
53 core::ptr::addr_of!(__bi_entries_start),
54 core::ptr::addr_of!(__bi_entries_end),
55 &MAPPING_TABLE,
56 )
57};
58
59/// This tells picotool how to convert RAM addresses back into Flash addresses
60#[cfg(feature = "binary-info")]
61pub static MAPPING_TABLE: [MappingTableEntry; 2] = [
62 // This is the entry for .data
63 MappingTableEntry {
64 source_addr_start: unsafe { core::ptr::addr_of!(__sidata) },
65 dest_addr_start: unsafe { core::ptr::addr_of!(__sdata) },
66 dest_addr_end: unsafe { core::ptr::addr_of!(__edata) },
67 },
68 // This is the terminating marker
69 MappingTableEntry::null(),
70];
71
72/// Create a 'Binary Info' entry containing the program name
73///
74/// This is well-known to picotool, and will be displayed if you run `picotool info`.
75///
76/// * Tag: [`consts::TAG_RASPBERRY_PI`]
77/// * ID: [`consts::ID_RP_PROGRAM_NAME`]
78pub const fn rp_program_name(name: &'static core::ffi::CStr) -> StringEntry {
79 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_NAME, name)
80}
81
82/// Create a 'Binary Info' entry containing the program version.
83///
84/// * Tag: [`consts::TAG_RASPBERRY_PI`]
85/// * Id: [`consts::ID_RP_PROGRAM_VERSION_STRING`]
86pub const fn rp_program_version(name: &'static core::ffi::CStr) -> StringEntry {
87 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_VERSION_STRING, name)
88}
89
90/// Create a 'Binary Info' entry with a URL
91///
92/// * Tag: [`consts::TAG_RASPBERRY_PI`]
93/// * Id: [`consts::ID_RP_PROGRAM_URL`]
94pub const fn rp_program_url(url: &'static core::ffi::CStr) -> StringEntry {
95 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_URL, url)
96}
97
98/// Create a 'Binary Info' with the program build date
99///
100/// * Tag: [`consts::TAG_RASPBERRY_PI`]
101/// * Id: [`consts::ID_RP_PROGRAM_BUILD_DATE_STRING`]
102pub const fn rp_program_build_date_string(value: &'static core::ffi::CStr) -> StringEntry {
103 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_BUILD_DATE_STRING, value)
104}
105
106/// Create a 'Binary Info' with the size of the binary
107///
108/// * Tag: [`consts::TAG_RASPBERRY_PI`]
109/// * Id: [`consts::ID_RP_BINARY_END`]
110pub const fn rp_binary_end(value: u32) -> IntegerEntry {
111 IntegerEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_BINARY_END, value)
112}
113
114/// Create a 'Binary Info' with a description of the program
115///
116/// * Tag: [`consts::TAG_RASPBERRY_PI`]
117/// * Id: [`consts::ID_RP_PROGRAM_DESCRIPTION`]
118pub const fn rp_program_description(value: &'static core::ffi::CStr) -> StringEntry {
119 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_DESCRIPTION, value)
120}
121
122/// Create a 'Binary Info' with some feature of the program
123///
124/// * Tag: [`consts::TAG_RASPBERRY_PI`]
125/// * Id: [`consts::ID_RP_PROGRAM_FEATURE`]
126pub const fn rp_program_feature(value: &'static core::ffi::CStr) -> StringEntry {
127 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_FEATURE, value)
128}
129
130/// Create a 'Binary Info' with some whether this was a Debug or Release build
131///
132/// * Tag: [`consts::TAG_RASPBERRY_PI`]
133/// * Id: [`consts::ID_RP_PROGRAM_BUILD_ATTRIBUTE`]
134pub const fn rp_program_build_attribute(value: &'static core::ffi::CStr) -> StringEntry {
135 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_BUILD_ATTRIBUTE, value)
136}
137
138/// Create a 'Binary Info' with the Pico SDK version used
139///
140/// * Tag: [`consts::TAG_RASPBERRY_PI`]
141/// * Id: [`consts::ID_RP_SDK_VERSION`]
142pub const fn rp_sdk_version(value: &'static core::ffi::CStr) -> StringEntry {
143 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_SDK_VERSION, value)
144}
145
146/// Create a 'Binary Info' with which board this program targets
147///
148/// * Tag: [`consts::TAG_RASPBERRY_PI`]
149/// * Id: [`consts::ID_RP_PICO_BOARD`]
150pub const fn rp_pico_board(value: &'static core::ffi::CStr) -> StringEntry {
151 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PICO_BOARD, value)
152}
153
154/// Create a 'Binary Info' with which `boot2` image this program uses
155///
156/// * Tag: [`consts::TAG_RASPBERRY_PI`]
157/// * Id: [`consts::ID_RP_BOOT2_NAME`]
158pub const fn rp_boot2_name(value: &'static core::ffi::CStr) -> StringEntry {
159 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_BOOT2_NAME, value)
160}
161
162/// Create a tag from two ASCII letters.
163///
164/// ```
165/// let tag = embassy_rp::binary_info::make_tag(b"RP");
166/// assert_eq!(tag, 0x5052);
167/// ```
168pub const fn make_tag(c: &[u8; 2]) -> u16 {
169 u16::from_le_bytes(*c)
170}
171
172// End of file
diff --git a/embassy-rp/src/binary_info/types.rs b/embassy-rp/src/binary_info/types.rs
new file mode 100644
index 000000000..d2b192e32
--- /dev/null
+++ b/embassy-rp/src/binary_info/types.rs
@@ -0,0 +1,192 @@
1//! Types for the Binary Info system
2
3/// This is the 'Binary Info' header block that `picotool` looks for in your UF2
4/// file/ELF file/Pico in Bootloader Mode to give you useful metadata about your
5/// program.
6///
7/// It should be placed in the first 4096 bytes of flash, so use your `memory.x`
8/// to insert a section between `.text` and `.vector_table` and put a static
9/// value of this type in that section.
10#[repr(C)]
11pub struct Header {
12 /// Must be equal to Picotool::MARKER_START
13 marker_start: u32,
14 /// The first in our table of pointers to Entries
15 entries_start: *const EntryAddr,
16 /// The last in our table of pointers to Entries
17 entries_end: *const EntryAddr,
18 /// The first entry in a null-terminated RAM/Flash mapping table
19 mapping_table: *const MappingTableEntry,
20 /// Must be equal to Picotool::MARKER_END
21 marker_end: u32,
22}
23
24impl Header {
25 /// This is the `BINARY_INFO_MARKER_START` magic value from `picotool`
26 const MARKER_START: u32 = 0x7188ebf2;
27 /// This is the `BINARY_INFO_MARKER_END` magic value from `picotool`
28 const MARKER_END: u32 = 0xe71aa390;
29
30 /// Create a new `picotool` compatible header.
31 ///
32 /// * `entries_start` - the first [`EntryAddr`] in the table
33 /// * `entries_end` - the last [`EntryAddr`] in the table
34 /// * `mapping_table` - the RAM/Flash address mapping table
35 pub const fn new(
36 entries_start: *const EntryAddr,
37 entries_end: *const EntryAddr,
38 mapping_table: &'static [MappingTableEntry],
39 ) -> Self {
40 let mapping_table = mapping_table.as_ptr();
41 Self {
42 marker_start: Self::MARKER_START,
43 entries_start,
44 entries_end,
45 mapping_table,
46 marker_end: Self::MARKER_END,
47 }
48 }
49}
50
51// We need this as rustc complains that is is unsafe to share `*const u32`
52// pointers between threads. We only allow these to be created with static
53// data, so this is OK.
54unsafe impl Sync for Header {}
55
56/// This is a reference to an entry. It's like a `&dyn` ref to some type `T:
57/// Entry`, except that the run-time type information is encoded into the
58/// Entry itself in very specific way.
59#[repr(transparent)]
60pub struct EntryAddr(*const u32);
61
62// We need this as rustc complains that is is unsafe to share `*const u32`
63// pointers between threads. We only allow these to be created with static
64// data, so this is OK.
65unsafe impl Sync for EntryAddr {}
66
67/// Allows us to tell picotool where values are in the UF2 given their run-time
68/// address.
69///
70/// The most obvious example is RAM variables, which must be found in the
71/// `.data` section of the UF2.
72#[repr(C)]
73pub struct MappingTableEntry {
74 /// The start address in RAM (or wherever the address picotool finds will
75 /// point)
76 pub source_addr_start: *const u32,
77 /// The start address in flash (or whever the data actually lives in the
78 /// ELF)
79 pub dest_addr_start: *const u32,
80 /// The end address in flash
81 pub dest_addr_end: *const u32,
82}
83
84impl MappingTableEntry {
85 /// Generate a null entry to mark the end of the list
86 pub const fn null() -> MappingTableEntry {
87 MappingTableEntry {
88 source_addr_start: core::ptr::null(),
89 dest_addr_start: core::ptr::null(),
90 dest_addr_end: core::ptr::null(),
91 }
92 }
93}
94
95// We need this as rustc complains that is is unsafe to share `*const u32`
96// pointers between threads. We only allow these to be created with static
97// data, so this is OK.
98unsafe impl Sync for MappingTableEntry {}
99
100/// This is the set of data types that `picotool` supports.
101#[repr(u16)]
102pub enum DataType {
103 /// Raw data
104 Raw = 1,
105 /// Data with a size
106 SizedData = 2,
107 /// A list of binary data
108 BinaryInfoListZeroTerminated = 3,
109 /// A BSON encoded blob
110 Bson = 4,
111 /// An Integer with an ID
112 IdAndInt = 5,
113 /// A string with an Id
114 IdAndString = 6,
115 /// A block device
116 BlockDevice = 7,
117 /// GPIO pins, with their function
118 PinsWithFunction = 8,
119 /// GPIO pins, with their name
120 PinsWithName = 9,
121 /// GPIO pins, with multiple names?
122 PinsWithNames = 10,
123}
124
125/// All Entries start with this common header
126#[repr(C)]
127struct EntryCommon {
128 data_type: DataType,
129 tag: u16,
130}
131
132/// An entry which contains both an ID (e.g. `ID_RP_PROGRAM_NAME`) and a pointer
133/// to a null-terminated string.
134#[repr(C)]
135pub struct StringEntry {
136 header: EntryCommon,
137 id: u32,
138 value: *const core::ffi::c_char,
139}
140
141impl StringEntry {
142 /// Create a new `StringEntry`
143 pub const fn new(tag: u16, id: u32, value: &'static core::ffi::CStr) -> StringEntry {
144 StringEntry {
145 header: EntryCommon {
146 data_type: DataType::IdAndString,
147 tag,
148 },
149 id,
150 value: value.as_ptr(),
151 }
152 }
153
154 /// Get this entry's address
155 pub const fn addr(&self) -> EntryAddr {
156 EntryAddr(self as *const Self as *const u32)
157 }
158}
159
160// We need this as rustc complains that is is unsafe to share `*const
161// core::ffi::c_char` pointers between threads. We only allow these to be
162// created with static string slices, so it's OK.
163unsafe impl Sync for StringEntry {}
164
165/// An entry which contains both an ID (e.g. `ID_RP_BINARY_END`) and an integer.
166#[repr(C)]
167pub struct IntegerEntry {
168 header: EntryCommon,
169 id: u32,
170 value: u32,
171}
172
173impl IntegerEntry {
174 /// Create a new `StringEntry`
175 pub const fn new(tag: u16, id: u32, value: u32) -> IntegerEntry {
176 IntegerEntry {
177 header: EntryCommon {
178 data_type: DataType::IdAndInt,
179 tag,
180 },
181 id,
182 value,
183 }
184 }
185
186 /// Get this entry's address
187 pub const fn addr(&self) -> EntryAddr {
188 EntryAddr(self as *const Self as *const u32)
189 }
190}
191
192// End of file
diff --git a/embassy-rp/src/block.rs b/embassy-rp/src/block.rs
new file mode 100644
index 000000000..d270bbf1c
--- /dev/null
+++ b/embassy-rp/src/block.rs
@@ -0,0 +1,1076 @@
1//! Support for the RP235x Boot ROM's "Block" structures
2//!
3//! Blocks contain pointers, to form Block Loops.
4//!
5//! The `IMAGE_DEF` Block (here the `ImageDef` type) tells the ROM how to boot a
6//! firmware image. The `PARTITION_TABLE` Block (here the `PartitionTable` type)
7//! tells the ROM how to divide the flash space up into partitions.
8
9// These all have a 1 byte size
10
11/// An item ID for encoding a Vector Table address
12pub const ITEM_1BS_VECTOR_TABLE: u8 = 0x03;
13
14/// An item ID for encoding a Rolling Window Delta
15pub const ITEM_1BS_ROLLING_WINDOW_DELTA: u8 = 0x05;
16
17/// An item ID for encoding a Signature
18pub const ITEM_1BS_SIGNATURE: u8 = 0x09;
19
20/// An item ID for encoding a Salt
21pub const ITEM_1BS_SALT: u8 = 0x0c;
22
23/// An item ID for encoding an Image Type
24pub const ITEM_1BS_IMAGE_TYPE: u8 = 0x42;
25
26/// An item ID for encoding the image's Entry Point
27pub const ITEM_1BS_ENTRY_POINT: u8 = 0x44;
28
29/// An item ID for encoding the definition of a Hash
30pub const ITEM_2BS_HASH_DEF: u8 = 0x47;
31
32/// An item ID for encoding a Version
33pub const ITEM_1BS_VERSION: u8 = 0x48;
34
35/// An item ID for encoding a Hash
36pub const ITEM_1BS_HASH_VALUE: u8 = 0x4b;
37
38// These all have a 2-byte size
39
40/// An item ID for encoding a Load Map
41pub const ITEM_2BS_LOAD_MAP: u8 = 0x06;
42
43/// An item ID for encoding a Partition Table
44pub const ITEM_2BS_PARTITION_TABLE: u8 = 0x0a;
45
46/// An item ID for encoding a placeholder entry that is ignored
47///
48/// Allows a Block to not be empty.
49pub const ITEM_2BS_IGNORED: u8 = 0xfe;
50
51/// An item ID for encoding the special last item in a Block
52///
53/// It records how long the Block is.
54pub const ITEM_2BS_LAST: u8 = 0xff;
55
56// Options for ITEM_1BS_IMAGE_TYPE
57
58/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark an image as invalid
59pub const IMAGE_TYPE_INVALID: u16 = 0x0000;
60
61/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark an image as an executable
62pub const IMAGE_TYPE_EXE: u16 = 0x0001;
63
64/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark an image as data
65pub const IMAGE_TYPE_DATA: u16 = 0x0002;
66
67/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU security mode as unspecified
68pub const IMAGE_TYPE_EXE_TYPE_SECURITY_UNSPECIFIED: u16 = 0x0000;
69
70/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU security mode as Non Secure
71pub const IMAGE_TYPE_EXE_TYPE_SECURITY_NS: u16 = 0x0010;
72
73/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU security mode as Non Secure
74pub const IMAGE_TYPE_EXE_TYPE_SECURITY_S: u16 = 0x0020;
75
76/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU type as Arm
77pub const IMAGE_TYPE_EXE_CPU_ARM: u16 = 0x0000;
78
79/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU type as RISC-V
80pub const IMAGE_TYPE_EXE_CPU_RISCV: u16 = 0x0100;
81
82/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU as an RP2040
83pub const IMAGE_TYPE_EXE_CHIP_RP2040: u16 = 0x0000;
84
85/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU as an RP2350
86pub const IMAGE_TYPE_EXE_CHIP_RP2350: u16 = 0x1000;
87
88/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the image as Try Before You Buy.
89///
90/// This means the image must be marked as 'Bought' with the ROM before the
91/// watchdog times out the trial period, otherwise it is erased and the previous
92/// image will be booted.
93pub const IMAGE_TYPE_TBYB: u16 = 0x8000;
94
95/// This is the magic Block Start value.
96///
97/// The Pico-SDK calls it `PICOBIN_BLOCK_MARKER_START`
98const BLOCK_MARKER_START: u32 = 0xffffded3;
99
100/// This is the magic Block END value.
101///
102/// The Pico-SDK calls it `PICOBIN_BLOCK_MARKER_END`
103const BLOCK_MARKER_END: u32 = 0xab123579;
104
105/// An Image Definition has one item in it - an [`ITEM_1BS_IMAGE_TYPE`]
106pub type ImageDef = Block<1>;
107
108/// A Block as understood by the Boot ROM.
109///
110/// This could be an Image Definition, or a Partition Table, or maybe some other
111/// kind of block.
112///
113/// It contains within the special start and end markers the Boot ROM is looking
114/// for.
115#[derive(Debug)]
116#[repr(C)]
117pub struct Block<const N: usize> {
118 marker_start: u32,
119 items: [u32; N],
120 length: u32,
121 offset: *const u32,
122 marker_end: u32,
123}
124
125unsafe impl<const N: usize> Sync for Block<N> {}
126
127impl<const N: usize> Block<N> {
128 /// Construct a new Binary Block, with the given items.
129 ///
130 /// The length, and the Start and End markers are added automatically. The
131 /// Block Loop pointer initially points to itself.
132 pub const fn new(items: [u32; N]) -> Block<N> {
133 Block {
134 marker_start: BLOCK_MARKER_START,
135 items,
136 length: item_last(N as u16),
137 // offset from this block to next block in loop. By default
138 // we form a Block Loop with a single Block in it.
139 offset: core::ptr::null(),
140 marker_end: BLOCK_MARKER_END,
141 }
142 }
143
144 /// Change the Block Loop offset value.
145 ///
146 /// This method isn't that useful because you can't evaluate the difference
147 /// between two pointers in a const context as the addresses aren't assigned
148 /// until long after the const evaluator has run.
149 ///
150 /// If you think you need this method, you might want to set a unique random
151 /// value here and swap it for the real offset as a post-processing step.
152 pub const fn with_offset(self, offset: *const u32) -> Block<N> {
153 Block { offset, ..self }
154 }
155}
156
157impl Block<0> {
158 /// Construct an empty block.
159 pub const fn empty() -> Block<0> {
160 Block::new([])
161 }
162
163 /// Make the block one word larger
164 pub const fn extend(self, word: u32) -> Block<1> {
165 Block::new([word])
166 }
167}
168
169impl Block<1> {
170 /// Make the block one word larger
171 pub const fn extend(self, word: u32) -> Block<2> {
172 Block::new([self.items[0], word])
173 }
174}
175
176impl Block<2> {
177 /// Make the block one word larger
178 pub const fn extend(self, word: u32) -> Block<3> {
179 Block::new([self.items[0], self.items[1], word])
180 }
181}
182
183impl ImageDef {
184 /// Construct a new IMAGE_DEF Block, for an EXE with the given security and
185 /// architecture.
186 pub const fn arch_exe(security: Security, architecture: Architecture) -> Self {
187 Self::new([item_image_type_exe(security, architecture)])
188 }
189
190 /// Construct a new IMAGE_DEF Block, for an EXE with the given security.
191 ///
192 /// The target architecture is taken from the current build target (i.e. Arm
193 /// or RISC-V).
194 pub const fn exe(security: Security) -> Self {
195 if cfg!(all(target_arch = "riscv32", target_os = "none")) {
196 Self::arch_exe(security, Architecture::Riscv)
197 } else {
198 Self::arch_exe(security, Architecture::Arm)
199 }
200 }
201
202 /// Construct a new IMAGE_DEF Block, for a Non-Secure EXE.
203 ///
204 /// The target architecture is taken from the current build target (i.e. Arm
205 /// or RISC-V).
206 pub const fn non_secure_exe() -> Self {
207 Self::exe(Security::NonSecure)
208 }
209
210 /// Construct a new IMAGE_DEF Block, for a Secure EXE.
211 ///
212 /// The target architecture is taken from the current build target (i.e. Arm
213 /// or RISC-V).
214 pub const fn secure_exe() -> Self {
215 Self::exe(Security::Secure)
216 }
217}
218
219/// We make our partition table this fixed size.
220pub const PARTITION_TABLE_MAX_ITEMS: usize = 128;
221
222/// Describes a unpartitioned space
223#[derive(Debug, Clone, PartialEq, Eq, Default)]
224pub struct UnpartitionedSpace {
225 permissions_and_location: u32,
226 permissions_and_flags: u32,
227}
228
229impl UnpartitionedSpace {
230 /// Create a new unpartitioned space.
231 ///
232 /// It defaults to no permissions.
233 pub const fn new() -> Self {
234 Self {
235 permissions_and_location: 0,
236 permissions_and_flags: 0,
237 }
238 }
239
240 /// Create a new unpartition space from run-time values.
241 ///
242 /// Get these from the ROM function `get_partition_table_info` with an argument of `PT_INFO`.
243 pub const fn from_raw(permissions_and_location: u32, permissions_and_flags: u32) -> Self {
244 Self {
245 permissions_and_location,
246 permissions_and_flags,
247 }
248 }
249
250 /// Add a permission
251 pub const fn with_permission(self, permission: Permission) -> Self {
252 Self {
253 permissions_and_flags: self.permissions_and_flags | permission as u32,
254 permissions_and_location: self.permissions_and_location | permission as u32,
255 }
256 }
257
258 /// Set a flag
259 pub const fn with_flag(self, flag: UnpartitionedFlag) -> Self {
260 Self {
261 permissions_and_flags: self.permissions_and_flags | flag as u32,
262 ..self
263 }
264 }
265
266 /// Get the partition start and end
267 ///
268 /// The offsets are in 4 KiB sectors, inclusive.
269 pub fn get_first_last_sectors(&self) -> (u16, u16) {
270 (
271 (self.permissions_and_location & 0x0000_1FFF) as u16,
272 ((self.permissions_and_location >> 13) & 0x0000_1FFF) as u16,
273 )
274 }
275
276 /// Get the partition start and end
277 ///
278 /// The offsets are in bytes, inclusive.
279 pub fn get_first_last_bytes(&self) -> (u32, u32) {
280 let (first, last) = self.get_first_last_sectors();
281 (u32::from(first) * 4096, (u32::from(last) * 4096) + 4095)
282 }
283
284 /// Check if it has a permission
285 pub fn has_permission(&self, permission: Permission) -> bool {
286 let mask = permission as u32;
287 (self.permissions_and_flags & mask) != 0
288 }
289
290 /// Check if the partition has a flag set
291 pub fn has_flag(&self, flag: UnpartitionedFlag) -> bool {
292 let mask = flag as u32;
293 (self.permissions_and_flags & mask) != 0
294 }
295}
296
297impl core::fmt::Display for UnpartitionedSpace {
298 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
299 let (first, last) = self.get_first_last_bytes();
300 write!(
301 f,
302 "{:#010x}..{:#010x} S:{}{} NS:{}{} B:{}{}",
303 first,
304 last,
305 if self.has_permission(Permission::SecureRead) {
306 'R'
307 } else {
308 '_'
309 },
310 if self.has_permission(Permission::SecureWrite) {
311 'W'
312 } else {
313 '_'
314 },
315 if self.has_permission(Permission::NonSecureRead) {
316 'R'
317 } else {
318 '_'
319 },
320 if self.has_permission(Permission::NonSecureWrite) {
321 'W'
322 } else {
323 '_'
324 },
325 if self.has_permission(Permission::BootRead) {
326 'R'
327 } else {
328 '_'
329 },
330 if self.has_permission(Permission::BootWrite) {
331 'W'
332 } else {
333 '_'
334 }
335 )
336 }
337}
338
339/// Describes a Partition
340#[derive(Debug, Clone, PartialEq, Eq)]
341pub struct Partition {
342 permissions_and_location: u32,
343 permissions_and_flags: u32,
344 id: Option<u64>,
345 extra_families: [u32; 4],
346 extra_families_len: usize,
347 name: [u8; 128],
348}
349
350impl Partition {
351 const FLAGS_HAS_ID: u32 = 0b1;
352 const FLAGS_LINK_TYPE_A_PARTITION: u32 = 0b01 << 1;
353 const FLAGS_LINK_TYPE_OWNER: u32 = 0b10 << 1;
354 const FLAGS_LINK_MASK: u32 = 0b111111 << 1;
355 const FLAGS_HAS_NAME: u32 = 0b1 << 12;
356 const FLAGS_HAS_EXTRA_FAMILIES_SHIFT: u8 = 7;
357 const FLAGS_HAS_EXTRA_FAMILIES_MASK: u32 = 0b11 << Self::FLAGS_HAS_EXTRA_FAMILIES_SHIFT;
358
359 /// Create a new partition, with the given start and end sectors.
360 ///
361 /// It defaults to no permissions.
362 pub const fn new(first_sector: u16, last_sector: u16) -> Self {
363 // 0x2000 sectors of 4 KiB is 32 MiB, which is the total XIP area
364 core::assert!(first_sector < 0x2000);
365 core::assert!(last_sector < 0x2000);
366 core::assert!(first_sector <= last_sector);
367 Self {
368 permissions_and_location: (last_sector as u32) << 13 | first_sector as u32,
369 permissions_and_flags: 0,
370 id: None,
371 extra_families: [0; 4],
372 extra_families_len: 0,
373 name: [0; 128],
374 }
375 }
376
377 /// Create a new partition from run-time values.
378 ///
379 /// Get these from the ROM function `get_partition_table_info` with an argument of `PARTITION_LOCATION_AND_FLAGS`.
380 pub const fn from_raw(permissions_and_location: u32, permissions_and_flags: u32) -> Self {
381 Self {
382 permissions_and_location,
383 permissions_and_flags,
384 id: None,
385 extra_families: [0; 4],
386 extra_families_len: 0,
387 name: [0; 128],
388 }
389 }
390
391 /// Add a permission
392 pub const fn with_permission(self, permission: Permission) -> Self {
393 Self {
394 permissions_and_location: self.permissions_and_location | permission as u32,
395 permissions_and_flags: self.permissions_and_flags | permission as u32,
396 ..self
397 }
398 }
399
400 /// Set the name of the partition
401 pub const fn with_name(self, name: &str) -> Self {
402 let mut new_name = [0u8; 128];
403 let name = name.as_bytes();
404 let mut idx = 0;
405 new_name[0] = name.len() as u8;
406 while idx < name.len() {
407 new_name[idx + 1] = name[idx];
408 idx += 1;
409 }
410 Self {
411 name: new_name,
412 permissions_and_flags: self.permissions_and_flags | Self::FLAGS_HAS_NAME,
413 ..self
414 }
415 }
416
417 /// Set the extra families for the partition.
418 ///
419 /// You can supply up to four.
420 pub const fn with_extra_families(self, extra_families: &[u32]) -> Self {
421 core::assert!(extra_families.len() <= 4);
422 let mut new_extra_families = [0u32; 4];
423 let mut idx = 0;
424 while idx < extra_families.len() {
425 new_extra_families[idx] = extra_families[idx];
426 idx += 1;
427 }
428 Self {
429 extra_families: new_extra_families,
430 extra_families_len: extra_families.len(),
431 permissions_and_flags: (self.permissions_and_flags & !Self::FLAGS_HAS_EXTRA_FAMILIES_MASK)
432 | (extra_families.len() as u32) << Self::FLAGS_HAS_EXTRA_FAMILIES_SHIFT,
433 ..self
434 }
435 }
436
437 /// Set the ID
438 pub const fn with_id(self, id: u64) -> Self {
439 Self {
440 id: Some(id),
441 permissions_and_flags: self.permissions_and_flags | Self::FLAGS_HAS_ID,
442 ..self
443 }
444 }
445
446 /// Add a link
447 pub const fn with_link(self, link: Link) -> Self {
448 let mut new_flags = self.permissions_and_flags & !Self::FLAGS_LINK_MASK;
449 match link {
450 Link::Nothing => {}
451 Link::ToA { partition_idx } => {
452 core::assert!(partition_idx < 16);
453 new_flags |= Self::FLAGS_LINK_TYPE_A_PARTITION;
454 new_flags |= (partition_idx as u32) << 3;
455 }
456 Link::ToOwner { partition_idx } => {
457 core::assert!(partition_idx < 16);
458 new_flags |= Self::FLAGS_LINK_TYPE_OWNER;
459 new_flags |= (partition_idx as u32) << 3;
460 }
461 }
462 Self {
463 permissions_and_flags: new_flags,
464 ..self
465 }
466 }
467
468 /// Set a flag
469 pub const fn with_flag(self, flag: PartitionFlag) -> Self {
470 Self {
471 permissions_and_flags: self.permissions_and_flags | flag as u32,
472 ..self
473 }
474 }
475
476 /// Get the partition start and end
477 ///
478 /// The offsets are in 4 KiB sectors, inclusive.
479 pub fn get_first_last_sectors(&self) -> (u16, u16) {
480 (
481 (self.permissions_and_location & 0x0000_1FFF) as u16,
482 ((self.permissions_and_location >> 13) & 0x0000_1FFF) as u16,
483 )
484 }
485
486 /// Get the partition start and end
487 ///
488 /// The offsets are in bytes, inclusive.
489 pub fn get_first_last_bytes(&self) -> (u32, u32) {
490 let (first, last) = self.get_first_last_sectors();
491 (u32::from(first) * 4096, (u32::from(last) * 4096) + 4095)
492 }
493
494 /// Check if it has a permission
495 pub fn has_permission(&self, permission: Permission) -> bool {
496 let mask = permission as u32;
497 (self.permissions_and_flags & mask) != 0
498 }
499
500 /// Get which extra families are allowed in this partition
501 pub fn get_extra_families(&self) -> &[u32] {
502 &self.extra_families[0..self.extra_families_len]
503 }
504
505 /// Get the name of the partition
506 ///
507 /// Returns `None` if there's no name, or the name is not valid UTF-8.
508 pub fn get_name(&self) -> Option<&str> {
509 let len = self.name[0] as usize;
510 if len == 0 {
511 None
512 } else {
513 core::str::from_utf8(&self.name[1..=len]).ok()
514 }
515 }
516
517 /// Get the ID
518 pub fn get_id(&self) -> Option<u64> {
519 self.id
520 }
521
522 /// Check if this partition is linked
523 pub fn get_link(&self) -> Link {
524 if (self.permissions_and_flags & Self::FLAGS_LINK_TYPE_A_PARTITION) != 0 {
525 let partition_idx = ((self.permissions_and_flags >> 3) & 0x0F) as u8;
526 Link::ToA { partition_idx }
527 } else if (self.permissions_and_flags & Self::FLAGS_LINK_TYPE_OWNER) != 0 {
528 let partition_idx = ((self.permissions_and_flags >> 3) & 0x0F) as u8;
529 Link::ToOwner { partition_idx }
530 } else {
531 Link::Nothing
532 }
533 }
534
535 /// Check if the partition has a flag set
536 pub fn has_flag(&self, flag: PartitionFlag) -> bool {
537 let mask = flag as u32;
538 (self.permissions_and_flags & mask) != 0
539 }
540}
541
542impl core::fmt::Display for Partition {
543 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
544 let (first, last) = self.get_first_last_bytes();
545 write!(
546 f,
547 "{:#010x}..{:#010x} S:{}{} NS:{}{} B:{}{}",
548 first,
549 last,
550 if self.has_permission(Permission::SecureRead) {
551 'R'
552 } else {
553 '_'
554 },
555 if self.has_permission(Permission::SecureWrite) {
556 'W'
557 } else {
558 '_'
559 },
560 if self.has_permission(Permission::NonSecureRead) {
561 'R'
562 } else {
563 '_'
564 },
565 if self.has_permission(Permission::NonSecureWrite) {
566 'W'
567 } else {
568 '_'
569 },
570 if self.has_permission(Permission::BootRead) {
571 'R'
572 } else {
573 '_'
574 },
575 if self.has_permission(Permission::BootWrite) {
576 'W'
577 } else {
578 '_'
579 }
580 )
581 }
582}
583
584/// Describes a partition table.
585///
586/// Don't store this as a static - make sure you convert it to a block.
587#[derive(Clone)]
588pub struct PartitionTableBlock {
589 /// This must look like a block, including the 1 word header and the 3 word footer.
590 contents: [u32; PARTITION_TABLE_MAX_ITEMS],
591 /// This value doesn't include the 1 word header or the 3 word footer
592 num_items: usize,
593}
594
595impl PartitionTableBlock {
596 /// Create an empty Block, big enough for a partition table.
597 ///
598 /// At a minimum you need to call [`Self::add_partition_item`].
599 pub const fn new() -> PartitionTableBlock {
600 let mut contents = [0; PARTITION_TABLE_MAX_ITEMS];
601 contents[0] = BLOCK_MARKER_START;
602 contents[1] = item_last(0);
603 contents[2] = 0;
604 contents[3] = BLOCK_MARKER_END;
605 PartitionTableBlock { contents, num_items: 0 }
606 }
607
608 /// Add a partition to the partition table
609 pub const fn add_partition_item(self, unpartitioned: UnpartitionedSpace, partitions: &[Partition]) -> Self {
610 let mut new_table = PartitionTableBlock::new();
611 let mut idx = 0;
612 // copy over old table, with the header but not the footer
613 while idx < self.num_items + 1 {
614 new_table.contents[idx] = self.contents[idx];
615 idx += 1;
616 }
617
618 // 1. add item header space (we fill this in later)
619 let header_idx = idx;
620 new_table.contents[idx] = 0;
621 idx += 1;
622
623 // 2. unpartitioned space flags
624 //
625 // (the location of unpartition space is not recorded here - it is
626 // inferred because the unpartitioned space is where the partitions are
627 // not)
628 new_table.contents[idx] = unpartitioned.permissions_and_flags;
629 idx += 1;
630
631 // 3. partition info
632
633 let mut partition_no = 0;
634 while partition_no < partitions.len() {
635 // a. permissions_and_location (4K units)
636 new_table.contents[idx] = partitions[partition_no].permissions_and_location;
637 idx += 1;
638
639 // b. permissions_and_flags
640 new_table.contents[idx] = partitions[partition_no].permissions_and_flags;
641 idx += 1;
642
643 // c. ID
644 if let Some(id) = partitions[partition_no].id {
645 new_table.contents[idx] = id as u32;
646 new_table.contents[idx + 1] = (id >> 32) as u32;
647 idx += 2;
648 }
649
650 // d. Extra Families
651 let mut extra_families_idx = 0;
652 while extra_families_idx < partitions[partition_no].extra_families_len {
653 new_table.contents[idx] = partitions[partition_no].extra_families[extra_families_idx];
654 idx += 1;
655 extra_families_idx += 1;
656 }
657
658 // e. Name
659 let mut name_idx = 0;
660 while name_idx < partitions[partition_no].name[0] as usize {
661 let name_chunk = [
662 partitions[partition_no].name[name_idx],
663 partitions[partition_no].name[name_idx + 1],
664 partitions[partition_no].name[name_idx + 2],
665 partitions[partition_no].name[name_idx + 3],
666 ];
667 new_table.contents[idx] = u32::from_le_bytes(name_chunk);
668 name_idx += 4;
669 idx += 1;
670 }
671
672 partition_no += 1;
673 }
674
675 let len = idx - header_idx;
676 new_table.contents[header_idx] = item_generic_2bs(partitions.len() as u8, len as u16, ITEM_2BS_PARTITION_TABLE);
677
678 // 7. New Footer
679 new_table.contents[idx] = item_last(idx as u16 - 1);
680 new_table.contents[idx + 1] = 0;
681 new_table.contents[idx + 2] = BLOCK_MARKER_END;
682
683 // ignore the header
684 new_table.num_items = idx - 1;
685 new_table
686 }
687
688 /// Add a version number to the partition table
689 pub const fn with_version(self, major: u16, minor: u16) -> Self {
690 let mut new_table = PartitionTableBlock::new();
691 let mut idx = 0;
692 // copy over old table, with the header but not the footer
693 while idx < self.num_items + 1 {
694 new_table.contents[idx] = self.contents[idx];
695 idx += 1;
696 }
697
698 // 1. add item
699 new_table.contents[idx] = item_generic_2bs(0, 2, ITEM_1BS_VERSION);
700 idx += 1;
701 new_table.contents[idx] = (major as u32) << 16 | minor as u32;
702 idx += 1;
703
704 // 2. New Footer
705 new_table.contents[idx] = item_last(idx as u16 - 1);
706 new_table.contents[idx + 1] = 0;
707 new_table.contents[idx + 2] = BLOCK_MARKER_END;
708
709 // ignore the header
710 new_table.num_items = idx - 1;
711 new_table
712 }
713
714 /// Add a a SHA256 hash of the Block
715 ///
716 /// Adds a `HASH_DEF` covering all the previous items in the Block, and a
717 /// `HASH_VALUE` with a SHA-256 hash of them.
718 pub const fn with_sha256(self) -> Self {
719 let mut new_table = PartitionTableBlock::new();
720 let mut idx = 0;
721 // copy over old table, with the header but not the footer
722 while idx < self.num_items + 1 {
723 new_table.contents[idx] = self.contents[idx];
724 idx += 1;
725 }
726
727 // 1. HASH_DEF says what is hashed
728 new_table.contents[idx] = item_generic_2bs(1, 2, ITEM_2BS_HASH_DEF);
729 idx += 1;
730 // we're hashing all the previous contents - including this line.
731 new_table.contents[idx] = (idx + 1) as u32;
732 idx += 1;
733
734 // calculate hash over prior contents
735 let input = unsafe { core::slice::from_raw_parts(new_table.contents.as_ptr() as *const u8, idx * 4) };
736 let hash: [u8; 32] = sha2_const_stable::Sha256::new().update(input).finalize();
737
738 // 2. HASH_VALUE contains the hash
739 new_table.contents[idx] = item_generic_2bs(0, 9, ITEM_1BS_HASH_VALUE);
740 idx += 1;
741
742 let mut hash_idx = 0;
743 while hash_idx < hash.len() {
744 new_table.contents[idx] = u32::from_le_bytes([
745 hash[hash_idx],
746 hash[hash_idx + 1],
747 hash[hash_idx + 2],
748 hash[hash_idx + 3],
749 ]);
750 idx += 1;
751 hash_idx += 4;
752 }
753
754 // 3. New Footer
755 new_table.contents[idx] = item_last(idx as u16 - 1);
756 new_table.contents[idx + 1] = 0;
757 new_table.contents[idx + 2] = BLOCK_MARKER_END;
758
759 // ignore the header
760 new_table.num_items = idx - 1;
761 new_table
762 }
763}
764
765impl Default for PartitionTableBlock {
766 fn default() -> Self {
767 Self::new()
768 }
769}
770
771/// Flags that a Partition can have
772#[derive(Debug, Copy, Clone, PartialEq, Eq)]
773#[repr(u32)]
774#[allow(missing_docs)]
775pub enum PartitionFlag {
776 NotBootableArm = 1 << 9,
777 NotBootableRiscv = 1 << 10,
778 Uf2DownloadAbNonBootableOwnerAffinity = 1 << 11,
779 Uf2DownloadNoReboot = 1 << 13,
780 AcceptsDefaultFamilyRp2040 = 1 << 14,
781 AcceptsDefaultFamilyData = 1 << 16,
782 AcceptsDefaultFamilyRp2350ArmS = 1 << 17,
783 AcceptsDefaultFamilyRp2350Riscv = 1 << 18,
784 AcceptsDefaultFamilyRp2350ArmNs = 1 << 19,
785}
786
787/// Flags that a Partition can have
788#[derive(Debug, Copy, Clone, PartialEq, Eq)]
789#[repr(u32)]
790#[allow(missing_docs)]
791pub enum UnpartitionedFlag {
792 Uf2DownloadNoReboot = 1 << 13,
793 AcceptsDefaultFamilyRp2040 = 1 << 14,
794 AcceptsDefaultFamilyAbsolute = 1 << 15,
795 AcceptsDefaultFamilyData = 1 << 16,
796 AcceptsDefaultFamilyRp2350ArmS = 1 << 17,
797 AcceptsDefaultFamilyRp2350Riscv = 1 << 18,
798 AcceptsDefaultFamilyRp2350ArmNs = 1 << 19,
799}
800
801/// Kinds of linked partition
802#[derive(Debug, Copy, Clone, PartialEq, Eq)]
803pub enum Link {
804 /// Not linked to anything
805 Nothing,
806 /// This is a B partition - link to our A partition.
807 ToA {
808 /// The index of our matching A partition.
809 partition_idx: u8,
810 },
811 /// Link to the partition that owns this one.
812 ToOwner {
813 /// The idx of our owner
814 partition_idx: u8,
815 },
816}
817
818/// Permissions that a Partition can have
819#[derive(Debug, Copy, Clone, PartialEq, Eq)]
820#[repr(u32)]
821pub enum Permission {
822 /// Can be read in Secure Mode
823 ///
824 /// Corresponds to `PERMISSION_S_R_BITS` in the Pico SDK
825 SecureRead = 1 << 26,
826 /// Can be written in Secure Mode
827 ///
828 /// Corresponds to `PERMISSION_S_W_BITS` in the Pico SDK
829 SecureWrite = 1 << 27,
830 /// Can be read in Non-Secure Mode
831 ///
832 /// Corresponds to `PERMISSION_NS_R_BITS` in the Pico SDK
833 NonSecureRead = 1 << 28,
834 /// Can be written in Non-Secure Mode
835 ///
836 /// Corresponds to `PERMISSION_NS_W_BITS` in the Pico SDK
837 NonSecureWrite = 1 << 29,
838 /// Can be read in Non-Secure Bootloader mode
839 ///
840 /// Corresponds to `PERMISSION_NSBOOT_R_BITS` in the Pico SDK
841 BootRead = 1 << 30,
842 /// Can be written in Non-Secure Bootloader mode
843 ///
844 /// Corresponds to `PERMISSION_NSBOOT_W_BITS` in the Pico SDK
845 BootWrite = 1 << 31,
846}
847
848impl Permission {
849 /// Is this permission bit set this in this bitmask?
850 pub const fn is_in(self, mask: u32) -> bool {
851 (mask & (self as u32)) != 0
852 }
853}
854
855/// The supported RP2350 CPU architectures
856#[derive(Debug, Copy, Clone, PartialEq, Eq)]
857pub enum Architecture {
858 /// Core is in Arm Cortex-M33 mode
859 Arm,
860 /// Core is in RISC-V / Hazard3 mode
861 Riscv,
862}
863
864/// The kinds of Secure Boot we support
865#[derive(Debug, Copy, Clone)]
866pub enum Security {
867 /// Security mode not given
868 Unspecified,
869 /// Start in Non-Secure mode
870 NonSecure,
871 /// Start in Secure mode
872 Secure,
873}
874
875/// Make an item containing a tag, 1 byte length and two extra bytes.
876///
877/// The `command` arg should contain `1BS`
878pub const fn item_generic_1bs(value: u16, length: u8, command: u8) -> u32 {
879 ((value as u32) << 16) | ((length as u32) << 8) | (command as u32)
880}
881
882/// Make an item containing a tag, 2 byte length and one extra byte.
883///
884/// The `command` arg should contain `2BS`
885pub const fn item_generic_2bs(value: u8, length: u16, command: u8) -> u32 {
886 ((value as u32) << 24) | ((length as u32) << 8) | (command as u32)
887}
888
889/// Create Image Type item, of type IGNORED.
890pub const fn item_ignored() -> u32 {
891 item_generic_2bs(0, 1, ITEM_2BS_IGNORED)
892}
893
894/// Create Image Type item, of type INVALID.
895pub const fn item_image_type_invalid() -> u32 {
896 let value = IMAGE_TYPE_INVALID;
897 item_generic_1bs(value, 1, ITEM_1BS_IMAGE_TYPE)
898}
899
900/// Create Image Type item, of type DATA.
901pub const fn item_image_type_data() -> u32 {
902 let value = IMAGE_TYPE_DATA;
903 item_generic_1bs(value, 1, ITEM_1BS_IMAGE_TYPE)
904}
905
906/// Create Image Type item, of type EXE.
907pub const fn item_image_type_exe(security: Security, arch: Architecture) -> u32 {
908 let mut value = IMAGE_TYPE_EXE | IMAGE_TYPE_EXE_CHIP_RP2350;
909
910 match arch {
911 Architecture::Arm => {
912 value |= IMAGE_TYPE_EXE_CPU_ARM;
913 }
914 Architecture::Riscv => {
915 value |= IMAGE_TYPE_EXE_CPU_RISCV;
916 }
917 }
918
919 match security {
920 Security::Unspecified => value |= IMAGE_TYPE_EXE_TYPE_SECURITY_UNSPECIFIED,
921 Security::NonSecure => value |= IMAGE_TYPE_EXE_TYPE_SECURITY_NS,
922 Security::Secure => value |= IMAGE_TYPE_EXE_TYPE_SECURITY_S,
923 }
924
925 item_generic_1bs(value, 1, ITEM_1BS_IMAGE_TYPE)
926}
927
928/// Create a Block Last item.
929pub const fn item_last(length: u16) -> u32 {
930 item_generic_2bs(0, length, ITEM_2BS_LAST)
931}
932
933/// Create a Vector Table item.
934///
935/// This is only allowed on Arm systems.
936pub const fn item_vector_table(table_ptr: u32) -> [u32; 2] {
937 [item_generic_1bs(0, 2, ITEM_1BS_VECTOR_TABLE), table_ptr]
938}
939
940/// Create an Entry Point item.
941pub const fn item_entry_point(entry_point: u32, initial_sp: u32) -> [u32; 3] {
942 [item_generic_1bs(0, 3, ITEM_1BS_ENTRY_POINT), entry_point, initial_sp]
943}
944
945/// Create an Rolling Window item.
946///
947/// The delta is the number of bytes into the image that 0x10000000 should
948/// be mapped.
949pub const fn item_rolling_window(delta: u32) -> [u32; 2] {
950 [item_generic_1bs(0, 3, ITEM_1BS_ROLLING_WINDOW_DELTA), delta]
951}
952
953#[cfg(test)]
954mod test {
955 use super::*;
956
957 /// I used this JSON, with `picotool partition create`:
958 ///
959 /// ```json
960 /// {
961 /// "version": [1, 0],
962 /// "unpartitioned": {
963 /// "families": ["absolute"],
964 /// "permissions": {
965 /// "secure": "rw",
966 /// "nonsecure": "rw",
967 /// "bootloader": "rw"
968 /// }
969 /// },
970 /// "partitions": [
971 /// {
972 /// "name": "A",
973 /// "id": 0,
974 /// "size": "2044K",
975 /// "families": ["rp2350-arm-s", "rp2350-riscv"],
976 /// "permissions": {
977 /// "secure": "rw",
978 /// "nonsecure": "rw",
979 /// "bootloader": "rw"
980 /// }
981 /// },
982 /// {
983 /// "name": "B",
984 /// "id": 1,
985 /// "size": "2044K",
986 /// "families": ["rp2350-arm-s", "rp2350-riscv"],
987 /// "permissions": {
988 /// "secure": "rw",
989 /// "nonsecure": "rw",
990 /// "bootloader": "rw"
991 /// },
992 /// "link": ["a", 0]
993 /// }
994 /// ]
995 /// }
996 /// ```
997 #[test]
998 fn make_hashed_partition_table() {
999 let table = PartitionTableBlock::new()
1000 .add_partition_item(
1001 UnpartitionedSpace::new()
1002 .with_permission(Permission::SecureRead)
1003 .with_permission(Permission::SecureWrite)
1004 .with_permission(Permission::NonSecureRead)
1005 .with_permission(Permission::NonSecureWrite)
1006 .with_permission(Permission::BootRead)
1007 .with_permission(Permission::BootWrite)
1008 .with_flag(UnpartitionedFlag::AcceptsDefaultFamilyAbsolute),
1009 &[
1010 Partition::new(2, 512)
1011 .with_id(0)
1012 .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350ArmS)
1013 .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350Riscv)
1014 .with_permission(Permission::SecureRead)
1015 .with_permission(Permission::SecureWrite)
1016 .with_permission(Permission::NonSecureRead)
1017 .with_permission(Permission::NonSecureWrite)
1018 .with_permission(Permission::BootRead)
1019 .with_permission(Permission::BootWrite)
1020 .with_name("A"),
1021 Partition::new(513, 1023)
1022 .with_id(1)
1023 .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350ArmS)
1024 .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350Riscv)
1025 .with_link(Link::ToA { partition_idx: 0 })
1026 .with_permission(Permission::SecureRead)
1027 .with_permission(Permission::SecureWrite)
1028 .with_permission(Permission::NonSecureRead)
1029 .with_permission(Permission::NonSecureWrite)
1030 .with_permission(Permission::BootRead)
1031 .with_permission(Permission::BootWrite)
1032 .with_name("B"),
1033 ],
1034 )
1035 .with_version(1, 0)
1036 .with_sha256();
1037 let expected = &[
1038 0xffffded3, // start
1039 0x02000c0a, // Item = PARTITION_TABLE
1040 0xfc008000, // Unpartitioned Space - permissions_and_flags
1041 0xfc400002, // Partition 0 - permissions_and_location (512 * 4096, 2 * 4096)
1042 0xfc061001, // permissions_and_flags HAS_ID | HAS_NAME | ARM-S | RISC-V
1043 0x00000000, // ID
1044 0x00000000, // ID
1045 0x00004101, // Name ("A")
1046 0xfc7fe201, // Partition 1 - permissions_and_location (1023 * 4096, 513 * 4096)
1047 0xfc061003, // permissions_and_flags LINKA(0) | HAS_ID | HAS_NAME | ARM-S | RISC-V
1048 0x00000001, // ID
1049 0x00000000, // ID
1050 0x00004201, // Name ("B")
1051 0x00000248, // Item = Version
1052 0x00010000, // 0, 1
1053 0x01000247, // HASH_DEF with 2 words, and SHA256 hash
1054 0x00000011, // 17 words hashed
1055 0x0000094b, // HASH_VALUE with 9 words
1056 0x1945cdad, // Hash word 0
1057 0x6b5f9773, // Hash word 1
1058 0xe2bf39bd, // Hash word 2
1059 0xb243e599, // Hash word 3
1060 0xab2f0e9a, // Hash word 4
1061 0x4d5d6d0b, // Hash word 5
1062 0xf973050f, // Hash word 6
1063 0x5ab6dadb, // Hash word 7
1064 0x000019ff, // Last Item
1065 0x00000000, // Block Loop Next Offset
1066 0xab123579, // End
1067 ];
1068 core::assert_eq!(
1069 &table.contents[..29],
1070 expected,
1071 "{:#010x?}\n != \n{:#010x?}",
1072 &table.contents[0..29],
1073 expected,
1074 );
1075 }
1076}
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs
index d0c6c19bd..5f7ba10e2 100644
--- a/embassy-rp/src/clocks.rs
+++ b/embassy-rp/src/clocks.rs
@@ -1,12 +1,17 @@
1//! Clock configuration for the RP2040 1//! Clock configuration for the RP2040
2
3#[cfg(feature = "rp2040")]
2use core::arch::asm; 4use core::arch::asm;
3use core::marker::PhantomData; 5use core::marker::PhantomData;
4use core::sync::atomic::{AtomicU16, AtomicU32, Ordering}; 6#[cfg(feature = "rp2040")]
7use core::sync::atomic::AtomicU16;
8use core::sync::atomic::{AtomicU32, Ordering};
5 9
6use embassy_hal_internal::{into_ref, PeripheralRef}; 10use embassy_hal_internal::{into_ref, PeripheralRef};
7use pac::clocks::vals::*; 11use pac::clocks::vals::*;
8 12
9use crate::gpio::{AnyPin, SealedPin}; 13use crate::gpio::{AnyPin, SealedPin};
14#[cfg(feature = "rp2040")]
10use crate::pac::common::{Reg, RW}; 15use crate::pac::common::{Reg, RW};
11use crate::{pac, reset, Peripheral}; 16use crate::{pac, reset, Peripheral};
12 17
@@ -26,6 +31,7 @@ struct Clocks {
26 // gpin1: AtomicU32, 31 // gpin1: AtomicU32,
27 rosc: AtomicU32, 32 rosc: AtomicU32,
28 peri: AtomicU32, 33 peri: AtomicU32,
34 #[cfg(feature = "rp2040")]
29 rtc: AtomicU16, 35 rtc: AtomicU16,
30} 36}
31 37
@@ -41,6 +47,7 @@ static CLOCKS: Clocks = Clocks {
41 // gpin1: AtomicU32::new(0), 47 // gpin1: AtomicU32::new(0),
42 rosc: AtomicU32::new(0), 48 rosc: AtomicU32::new(0),
43 peri: AtomicU32::new(0), 49 peri: AtomicU32::new(0),
50 #[cfg(feature = "rp2040")]
44 rtc: AtomicU16::new(0), 51 rtc: AtomicU16::new(0),
45}; 52};
46 53
@@ -81,6 +88,7 @@ pub struct ClockConfig {
81 /// ADC clock configuration. 88 /// ADC clock configuration.
82 pub adc_clk: Option<AdcClkConfig>, 89 pub adc_clk: Option<AdcClkConfig>,
83 /// RTC clock configuration. 90 /// RTC clock configuration.
91 #[cfg(feature = "rp2040")]
84 pub rtc_clk: Option<RtcClkConfig>, 92 pub rtc_clk: Option<RtcClkConfig>,
85 // gpin0: Option<(u32, Gpin<'static, AnyPin>)>, 93 // gpin0: Option<(u32, Gpin<'static, AnyPin>)>,
86 // gpin1: Option<(u32, Gpin<'static, AnyPin>)>, 94 // gpin1: Option<(u32, Gpin<'static, AnyPin>)>,
@@ -135,6 +143,7 @@ impl ClockConfig {
135 phase: 0, 143 phase: 0,
136 }), 144 }),
137 // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz 145 // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz
146 #[cfg(feature = "rp2040")]
138 rtc_clk: Some(RtcClkConfig { 147 rtc_clk: Some(RtcClkConfig {
139 src: RtcClkSrc::PllUsb, 148 src: RtcClkSrc::PllUsb,
140 div_int: 1024, 149 div_int: 1024,
@@ -174,6 +183,7 @@ impl ClockConfig {
174 phase: 0, 183 phase: 0,
175 }), 184 }),
176 // CLK RTC = ROSC (140MHz) / 2986.667969 ≅ 46875Hz 185 // CLK RTC = ROSC (140MHz) / 2986.667969 ≅ 46875Hz
186 #[cfg(feature = "rp2040")]
177 rtc_clk: Some(RtcClkConfig { 187 rtc_clk: Some(RtcClkConfig {
178 src: RtcClkSrc::Rosc, 188 src: RtcClkSrc::Rosc,
179 div_int: 2986, 189 div_int: 2986,
@@ -295,9 +305,17 @@ pub struct SysClkConfig {
295 /// SYS clock source. 305 /// SYS clock source.
296 pub src: SysClkSrc, 306 pub src: SysClkSrc,
297 /// SYS clock divider. 307 /// SYS clock divider.
308 #[cfg(feature = "rp2040")]
298 pub div_int: u32, 309 pub div_int: u32,
299 /// SYS clock fraction. 310 /// SYS clock fraction.
311 #[cfg(feature = "rp2040")]
300 pub div_frac: u8, 312 pub div_frac: u8,
313 /// SYS clock divider.
314 #[cfg(feature = "_rp235x")]
315 pub div_int: u16,
316 /// SYS clock fraction.
317 #[cfg(feature = "_rp235x")]
318 pub div_frac: u16,
301} 319}
302 320
303/// USB clock source. 321/// USB clock source.
@@ -358,6 +376,7 @@ pub struct AdcClkConfig {
358#[repr(u8)] 376#[repr(u8)]
359#[non_exhaustive] 377#[non_exhaustive]
360#[derive(Clone, Copy, Debug, PartialEq, Eq)] 378#[derive(Clone, Copy, Debug, PartialEq, Eq)]
379#[cfg(feature = "rp2040")]
361pub enum RtcClkSrc { 380pub enum RtcClkSrc {
362 /// PLL USB. 381 /// PLL USB.
363 PllUsb = ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB as _, 382 PllUsb = ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB as _,
@@ -372,6 +391,7 @@ pub enum RtcClkSrc {
372} 391}
373 392
374/// RTC clock config. 393/// RTC clock config.
394#[cfg(feature = "rp2040")]
375pub struct RtcClkConfig { 395pub struct RtcClkConfig {
376 /// RTC clock source. 396 /// RTC clock source.
377 pub src: RtcClkSrc, 397 pub src: RtcClkSrc,
@@ -396,10 +416,9 @@ pub(crate) unsafe fn init(config: ClockConfig) {
396 peris.set_pads_qspi(false); 416 peris.set_pads_qspi(false);
397 peris.set_pll_sys(false); 417 peris.set_pll_sys(false);
398 peris.set_pll_usb(false); 418 peris.set_pll_usb(false);
399 // TODO investigate if usb should be unreset here
400 peris.set_usbctrl(false); 419 peris.set_usbctrl(false);
401 peris.set_syscfg(false); 420 peris.set_syscfg(false);
402 peris.set_rtc(false); 421 //peris.set_rtc(false);
403 reset::reset(peris); 422 reset::reset(peris);
404 423
405 // Disable resus that may be enabled from previous software 424 // Disable resus that may be enabled from previous software
@@ -409,9 +428,15 @@ pub(crate) unsafe fn init(config: ClockConfig) {
409 428
410 // Before we touch PLLs, switch sys and ref cleanly away from their aux sources. 429 // Before we touch PLLs, switch sys and ref cleanly away from their aux sources.
411 c.clk_sys_ctrl().modify(|w| w.set_src(ClkSysCtrlSrc::CLK_REF)); 430 c.clk_sys_ctrl().modify(|w| w.set_src(ClkSysCtrlSrc::CLK_REF));
431 #[cfg(feature = "rp2040")]
412 while c.clk_sys_selected().read() != 1 {} 432 while c.clk_sys_selected().read() != 1 {}
433 #[cfg(feature = "_rp235x")]
434 while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1) {}
413 c.clk_ref_ctrl().modify(|w| w.set_src(ClkRefCtrlSrc::ROSC_CLKSRC_PH)); 435 c.clk_ref_ctrl().modify(|w| w.set_src(ClkRefCtrlSrc::ROSC_CLKSRC_PH));
436 #[cfg(feature = "rp2040")]
414 while c.clk_ref_selected().read() != 1 {} 437 while c.clk_ref_selected().read() != 1 {}
438 #[cfg(feature = "_rp235x")]
439 while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1) {}
415 440
416 // Reset the PLLs 441 // Reset the PLLs
417 let mut peris = reset::Peripherals(0); 442 let mut peris = reset::Peripherals(0);
@@ -479,11 +504,16 @@ pub(crate) unsafe fn init(config: ClockConfig) {
479 w.set_src(ref_src); 504 w.set_src(ref_src);
480 w.set_auxsrc(ref_aux); 505 w.set_auxsrc(ref_aux);
481 }); 506 });
482 while c.clk_ref_selected().read() != 1 << ref_src as u32 {} 507 #[cfg(feature = "rp2040")]
508 while c.clk_ref_selected().read() != (1 << ref_src as u32) {}
509 #[cfg(feature = "_rp235x")]
510 while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1 << ref_src as u32) {}
483 c.clk_ref_div().write(|w| { 511 c.clk_ref_div().write(|w| {
484 w.set_int(config.ref_clk.div); 512 w.set_int(config.ref_clk.div);
485 }); 513 });
486 514
515 // Configure tick generation on the 2040. On the 2350 the timers are driven from the sysclk.
516 #[cfg(feature = "rp2040")]
487 pac::WATCHDOG.tick().write(|w| { 517 pac::WATCHDOG.tick().write(|w| {
488 w.set_cycles((clk_ref_freq / 1_000_000) as u16); 518 w.set_cycles((clk_ref_freq / 1_000_000) as u16);
489 w.set_enable(true); 519 w.set_enable(true);
@@ -500,7 +530,6 @@ pub(crate) unsafe fn init(config: ClockConfig) {
500 // SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0, gpin0_freq), 530 // SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0, gpin0_freq),
501 // SysClkSrc::Gpin1 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN1, gpin1_freq), 531 // SysClkSrc::Gpin1 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN1, gpin1_freq),
502 }; 532 };
503 assert!(config.sys_clk.div_int <= 0x1000000);
504 let div = config.sys_clk.div_int as u64 * 256 + config.sys_clk.div_frac as u64; 533 let div = config.sys_clk.div_int as u64 * 256 + config.sys_clk.div_frac as u64;
505 (src, aux, ((freq as u64 * 256) / div) as u32) 534 (src, aux, ((freq as u64 * 256) / div) as u32)
506 }; 535 };
@@ -508,13 +537,21 @@ pub(crate) unsafe fn init(config: ClockConfig) {
508 CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed); 537 CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed);
509 if sys_src != ClkSysCtrlSrc::CLK_REF { 538 if sys_src != ClkSysCtrlSrc::CLK_REF {
510 c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF)); 539 c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF));
511 while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF as u32 {} 540 #[cfg(feature = "rp2040")]
541 while c.clk_sys_selected().read() != (1 << ClkSysCtrlSrc::CLK_REF as u32) {}
542 #[cfg(feature = "_rp235x")]
543 while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1 << ClkSysCtrlSrc::CLK_REF as u32) {}
512 } 544 }
513 c.clk_sys_ctrl().write(|w| { 545 c.clk_sys_ctrl().write(|w| {
514 w.set_auxsrc(sys_aux); 546 w.set_auxsrc(sys_aux);
515 w.set_src(sys_src); 547 w.set_src(sys_src);
516 }); 548 });
517 while c.clk_sys_selected().read() != 1 << sys_src as u32 {} 549
550 #[cfg(feature = "rp2040")]
551 while c.clk_sys_selected().read() != (1 << sys_src as u32) {}
552 #[cfg(feature = "_rp235x")]
553 while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1 << sys_src as u32) {}
554
518 c.clk_sys_div().write(|w| { 555 c.clk_sys_div().write(|w| {
519 w.set_int(config.sys_clk.div_int); 556 w.set_int(config.sys_clk.div_int);
520 w.set_frac(config.sys_clk.div_frac); 557 w.set_frac(config.sys_clk.div_frac);
@@ -592,6 +629,8 @@ pub(crate) unsafe fn init(config: ClockConfig) {
592 CLOCKS.adc.store(0, Ordering::Relaxed); 629 CLOCKS.adc.store(0, Ordering::Relaxed);
593 } 630 }
594 631
632 // rp2040 specific clocks
633 #[cfg(feature = "rp2040")]
595 if let Some(conf) = config.rtc_clk { 634 if let Some(conf) = config.rtc_clk {
596 c.clk_rtc_div().write(|w| { 635 c.clk_rtc_div().write(|w| {
597 w.set_int(conf.div_int); 636 w.set_int(conf.div_int);
@@ -621,6 +660,13 @@ pub(crate) unsafe fn init(config: ClockConfig) {
621 CLOCKS.rtc.store(0, Ordering::Relaxed); 660 CLOCKS.rtc.store(0, Ordering::Relaxed);
622 } 661 }
623 662
663 // rp235x specific clocks
664 #[cfg(feature = "_rp235x")]
665 {
666 // TODO hstx clock
667 peris.set_hstx(false);
668 }
669
624 // Peripheral clocks should now all be running 670 // Peripheral clocks should now all be running
625 reset::unreset_wait(peris); 671 reset::unreset_wait(peris);
626} 672}
@@ -709,6 +755,7 @@ pub fn clk_adc_freq() -> u32 {
709} 755}
710 756
711/// RTC clock frequency. 757/// RTC clock frequency.
758#[cfg(feature = "rp2040")]
712pub fn clk_rtc_freq() -> u16 { 759pub fn clk_rtc_freq() -> u16 {
713 CLOCKS.rtc.load(Ordering::Relaxed) 760 CLOCKS.rtc.load(Ordering::Relaxed)
714} 761}
@@ -856,6 +903,7 @@ pub enum GpoutSrc {
856 /// ADC. 903 /// ADC.
857 Adc = ClkGpoutCtrlAuxsrc::CLK_ADC as _, 904 Adc = ClkGpoutCtrlAuxsrc::CLK_ADC as _,
858 /// RTC. 905 /// RTC.
906 #[cfg(feature = "rp2040")]
859 Rtc = ClkGpoutCtrlAuxsrc::CLK_RTC as _, 907 Rtc = ClkGpoutCtrlAuxsrc::CLK_RTC as _,
860 /// REF. 908 /// REF.
861 Ref = ClkGpoutCtrlAuxsrc::CLK_REF as _, 909 Ref = ClkGpoutCtrlAuxsrc::CLK_REF as _,
@@ -877,6 +925,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> {
877 } 925 }
878 926
879 /// Set clock divider. 927 /// Set clock divider.
928 #[cfg(feature = "rp2040")]
880 pub fn set_div(&self, int: u32, frac: u8) { 929 pub fn set_div(&self, int: u32, frac: u8) {
881 let c = pac::CLOCKS; 930 let c = pac::CLOCKS;
882 c.clk_gpout_div(self.gpout.number()).write(|w| { 931 c.clk_gpout_div(self.gpout.number()).write(|w| {
@@ -885,6 +934,16 @@ impl<'d, T: GpoutPin> Gpout<'d, T> {
885 }); 934 });
886 } 935 }
887 936
937 /// Set clock divider.
938 #[cfg(feature = "_rp235x")]
939 pub fn set_div(&self, int: u16, frac: u16) {
940 let c = pac::CLOCKS;
941 c.clk_gpout_div(self.gpout.number()).write(|w| {
942 w.set_int(int);
943 w.set_frac(frac);
944 });
945 }
946
888 /// Set clock source. 947 /// Set clock source.
889 pub fn set_src(&self, src: GpoutSrc) { 948 pub fn set_src(&self, src: GpoutSrc) {
890 let c = pac::CLOCKS; 949 let c = pac::CLOCKS;
@@ -924,13 +983,13 @@ impl<'d, T: GpoutPin> Gpout<'d, T> {
924 ClkGpoutCtrlAuxsrc::CLK_SYS => clk_sys_freq(), 983 ClkGpoutCtrlAuxsrc::CLK_SYS => clk_sys_freq(),
925 ClkGpoutCtrlAuxsrc::CLK_USB => clk_usb_freq(), 984 ClkGpoutCtrlAuxsrc::CLK_USB => clk_usb_freq(),
926 ClkGpoutCtrlAuxsrc::CLK_ADC => clk_adc_freq(), 985 ClkGpoutCtrlAuxsrc::CLK_ADC => clk_adc_freq(),
927 ClkGpoutCtrlAuxsrc::CLK_RTC => clk_rtc_freq() as _, 986 //ClkGpoutCtrlAuxsrc::CLK_RTC => clk_rtc_freq() as _,
928 ClkGpoutCtrlAuxsrc::CLK_REF => clk_ref_freq(), 987 ClkGpoutCtrlAuxsrc::CLK_REF => clk_ref_freq(),
929 _ => unreachable!(), 988 _ => unreachable!(),
930 }; 989 };
931 990
932 let div = c.clk_gpout_div(self.gpout.number()).read(); 991 let div = c.clk_gpout_div(self.gpout.number()).read();
933 let int = if div.int() == 0 { 65536 } else { div.int() } as u64; 992 let int = if div.int() == 0 { 0xFFFF } else { div.int() } as u64;
934 let frac = div.frac() as u64; 993 let frac = div.frac() as u64;
935 994
936 ((base as u64 * 256) / (int * 256 + frac)) as u32 995 ((base as u64 * 256) / (int * 256 + frac)) as u32
@@ -987,7 +1046,7 @@ impl rand_core::RngCore for RoscRng {
987/// and can only be exited through resets, dormant-wake GPIO interrupts, 1046/// and can only be exited through resets, dormant-wake GPIO interrupts,
988/// and RTC interrupts. If RTC is clocked from an internal clock source 1047/// and RTC interrupts. If RTC is clocked from an internal clock source
989/// it will be stopped and not function as a wakeup source. 1048/// it will be stopped and not function as a wakeup source.
990#[cfg(target_arch = "arm")] 1049#[cfg(all(target_arch = "arm", feature = "rp2040"))]
991pub fn dormant_sleep() { 1050pub fn dormant_sleep() {
992 struct Set<T: Copy, F: Fn()>(Reg<T, RW>, T, F); 1051 struct Set<T: Copy, F: Fn()>(Reg<T, RW>, T, F);
993 1052
@@ -1107,7 +1166,7 @@ pub fn dormant_sleep() {
1107 coma = in (reg) 0x636f6d61, 1166 coma = in (reg) 0x636f6d61,
1108 ); 1167 );
1109 } else { 1168 } else {
1110 pac::ROSC.dormant().write_value(0x636f6d61); 1169 pac::ROSC.dormant().write_value(rp_pac::rosc::regs::Dormant(0x636f6d61));
1111 } 1170 }
1112 } 1171 }
1113} 1172}
diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs
index 8c04b43a1..34abe3e2d 100644
--- a/embassy-rp/src/dma.rs
+++ b/embassy-rp/src/dma.rs
@@ -15,7 +15,7 @@ use crate::{interrupt, pac, peripherals};
15#[cfg(feature = "rt")] 15#[cfg(feature = "rt")]
16#[interrupt] 16#[interrupt]
17fn DMA_IRQ_0() { 17fn DMA_IRQ_0() {
18 let ints0 = pac::DMA.ints0().read().ints0(); 18 let ints0 = pac::DMA.ints(0).read();
19 for channel in 0..CHANNEL_COUNT { 19 for channel in 0..CHANNEL_COUNT {
20 let ctrl_trig = pac::DMA.ch(channel).ctrl_trig().read(); 20 let ctrl_trig = pac::DMA.ch(channel).ctrl_trig().read();
21 if ctrl_trig.ahb_error() { 21 if ctrl_trig.ahb_error() {
@@ -26,14 +26,14 @@ fn DMA_IRQ_0() {
26 CHANNEL_WAKERS[channel].wake(); 26 CHANNEL_WAKERS[channel].wake();
27 } 27 }
28 } 28 }
29 pac::DMA.ints0().write(|w| w.set_ints0(ints0)); 29 pac::DMA.ints(0).write_value(ints0);
30} 30}
31 31
32pub(crate) unsafe fn init() { 32pub(crate) unsafe fn init() {
33 interrupt::DMA_IRQ_0.disable(); 33 interrupt::DMA_IRQ_0.disable();
34 interrupt::DMA_IRQ_0.set_priority(interrupt::Priority::P3); 34 interrupt::DMA_IRQ_0.set_priority(interrupt::Priority::P3);
35 35
36 pac::DMA.inte0().write(|w| w.set_inte0(0xFFFF)); 36 pac::DMA.inte(0).write_value(0xFFFF);
37 37
38 interrupt::DMA_IRQ_0.enable(); 38 interrupt::DMA_IRQ_0.enable();
39} 39}
@@ -45,7 +45,7 @@ pub unsafe fn read<'a, C: Channel, W: Word>(
45 ch: impl Peripheral<P = C> + 'a, 45 ch: impl Peripheral<P = C> + 'a,
46 from: *const W, 46 from: *const W,
47 to: *mut [W], 47 to: *mut [W],
48 dreq: u8, 48 dreq: vals::TreqSel,
49) -> Transfer<'a, C> { 49) -> Transfer<'a, C> {
50 copy_inner( 50 copy_inner(
51 ch, 51 ch,
@@ -66,7 +66,7 @@ pub unsafe fn write<'a, C: Channel, W: Word>(
66 ch: impl Peripheral<P = C> + 'a, 66 ch: impl Peripheral<P = C> + 'a,
67 from: *const [W], 67 from: *const [W],
68 to: *mut W, 68 to: *mut W,
69 dreq: u8, 69 dreq: vals::TreqSel,
70) -> Transfer<'a, C> { 70) -> Transfer<'a, C> {
71 copy_inner( 71 copy_inner(
72 ch, 72 ch,
@@ -90,7 +90,7 @@ pub unsafe fn write_repeated<'a, C: Channel, W: Word>(
90 ch: impl Peripheral<P = C> + 'a, 90 ch: impl Peripheral<P = C> + 'a,
91 to: *mut W, 91 to: *mut W,
92 len: usize, 92 len: usize,
93 dreq: u8, 93 dreq: vals::TreqSel,
94) -> Transfer<'a, C> { 94) -> Transfer<'a, C> {
95 copy_inner( 95 copy_inner(
96 ch, 96 ch,
@@ -123,7 +123,7 @@ pub unsafe fn copy<'a, C: Channel, W: Word>(
123 W::size(), 123 W::size(),
124 true, 124 true,
125 true, 125 true,
126 vals::TreqSel::PERMANENT.0, 126 vals::TreqSel::PERMANENT,
127 ) 127 )
128} 128}
129 129
@@ -135,7 +135,7 @@ fn copy_inner<'a, C: Channel>(
135 data_size: DataSize, 135 data_size: DataSize,
136 incr_read: bool, 136 incr_read: bool,
137 incr_write: bool, 137 incr_write: bool,
138 dreq: u8, 138 dreq: vals::TreqSel,
139) -> Transfer<'a, C> { 139) -> Transfer<'a, C> {
140 into_ref!(ch); 140 into_ref!(ch);
141 141
@@ -143,14 +143,20 @@ fn copy_inner<'a, C: Channel>(
143 143
144 p.read_addr().write_value(from as u32); 144 p.read_addr().write_value(from as u32);
145 p.write_addr().write_value(to as u32); 145 p.write_addr().write_value(to as u32);
146 p.trans_count().write_value(len as u32); 146 #[cfg(feature = "rp2040")]
147 p.trans_count().write(|w| {
148 *w = len as u32;
149 });
150 #[cfg(feature = "_rp235x")]
151 p.trans_count().write(|w| {
152 w.set_mode(0.into());
153 w.set_count(len as u32);
154 });
147 155
148 compiler_fence(Ordering::SeqCst); 156 compiler_fence(Ordering::SeqCst);
149 157
150 p.ctrl_trig().write(|w| { 158 p.ctrl_trig().write(|w| {
151 // TODO: Add all DREQ options to pac vals::TreqSel, and use 159 w.set_treq_sel(dreq);
152 // `set_treq:sel`
153 w.0 = ((dreq as u32) & 0x3f) << 15usize;
154 w.set_data_size(data_size); 160 w.set_data_size(data_size);
155 w.set_incr_read(incr_read); 161 w.set_incr_read(incr_read);
156 w.set_incr_write(incr_write); 162 w.set_incr_write(incr_write);
@@ -202,7 +208,10 @@ impl<'a, C: Channel> Future for Transfer<'a, C> {
202 } 208 }
203} 209}
204 210
211#[cfg(feature = "rp2040")]
205pub(crate) const CHANNEL_COUNT: usize = 12; 212pub(crate) const CHANNEL_COUNT: usize = 12;
213#[cfg(feature = "_rp235x")]
214pub(crate) const CHANNEL_COUNT: usize = 16;
206const NEW_AW: AtomicWaker = AtomicWaker::new(); 215const NEW_AW: AtomicWaker = AtomicWaker::new();
207static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT]; 216static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT];
208 217
@@ -297,3 +306,11 @@ channel!(DMA_CH8, 8);
297channel!(DMA_CH9, 9); 306channel!(DMA_CH9, 9);
298channel!(DMA_CH10, 10); 307channel!(DMA_CH10, 10);
299channel!(DMA_CH11, 11); 308channel!(DMA_CH11, 11);
309#[cfg(feature = "_rp235x")]
310channel!(DMA_CH12, 12);
311#[cfg(feature = "_rp235x")]
312channel!(DMA_CH13, 13);
313#[cfg(feature = "_rp235x")]
314channel!(DMA_CH14, 14);
315#[cfg(feature = "_rp235x")]
316channel!(DMA_CH15, 15);
diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs
index 9e4542b2f..9991c3ac0 100644
--- a/embassy-rp/src/flash.rs
+++ b/embassy-rp/src/flash.rs
@@ -302,7 +302,14 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Async, FLASH_SIZE> {
302 // pac::XIP_CTRL.stream_fifo().as_ptr()) to avoid DMA stalling on 302 // pac::XIP_CTRL.stream_fifo().as_ptr()) to avoid DMA stalling on
303 // general XIP access. 303 // general XIP access.
304 const XIP_AUX_BASE: *const u32 = 0x50400000 as *const _; 304 const XIP_AUX_BASE: *const u32 = 0x50400000 as *const _;
305 let transfer = unsafe { crate::dma::read(self.dma.as_mut().unwrap(), XIP_AUX_BASE, data, 37) }; 305 let transfer = unsafe {
306 crate::dma::read(
307 self.dma.as_mut().unwrap(),
308 XIP_AUX_BASE,
309 data,
310 pac::dma::vals::TreqSel::XIP_STREAM,
311 )
312 };
306 313
307 Ok(BackgroundRead { 314 Ok(BackgroundRead {
308 flash: PhantomData, 315 flash: PhantomData,
@@ -597,6 +604,7 @@ mod ram_helpers {
597 /// addr must be aligned to 4096 604 /// addr must be aligned to 4096
598 #[inline(never)] 605 #[inline(never)]
599 #[link_section = ".data.ram_func"] 606 #[link_section = ".data.ram_func"]
607 #[cfg(feature = "rp2040")]
600 unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) { 608 unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) {
601 /* 609 /*
602 Should be equivalent to: 610 Should be equivalent to:
@@ -659,6 +667,13 @@ mod ram_helpers {
659 ); 667 );
660 } 668 }
661 669
670 #[inline(never)]
671 #[link_section = ".data.ram_func"]
672 #[cfg(feature = "_rp235x")]
673 unsafe fn write_flash_inner(_addr: u32, _len: u32, _data: Option<&[u8]>, _ptrs: *const FlashFunctionPointers) {
674 todo!();
675 }
676
662 #[repr(C)] 677 #[repr(C)]
663 struct FlashCommand { 678 struct FlashCommand {
664 cmd_addr: *const u8, 679 cmd_addr: *const u8,
@@ -758,6 +773,7 @@ mod ram_helpers {
758 /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) 773 /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT)
759 #[inline(never)] 774 #[inline(never)]
760 #[link_section = ".data.ram_func"] 775 #[link_section = ".data.ram_func"]
776 #[cfg(feature = "rp2040")]
761 unsafe fn read_flash_inner(cmd: FlashCommand, ptrs: *const FlashFunctionPointers) { 777 unsafe fn read_flash_inner(cmd: FlashCommand, ptrs: *const FlashFunctionPointers) {
762 #[cfg(target_arch = "arm")] 778 #[cfg(target_arch = "arm")]
763 core::arch::asm!( 779 core::arch::asm!(
@@ -874,6 +890,13 @@ mod ram_helpers {
874 clobber_abi("C"), 890 clobber_abi("C"),
875 ); 891 );
876 } 892 }
893
894 #[inline(never)]
895 #[link_section = ".data.ram_func"]
896 #[cfg(feature = "_rp235x")]
897 unsafe fn read_flash_inner(_cmd: FlashCommand, _ptrs: *const FlashFunctionPointers) {
898 todo!();
899 }
877} 900}
878 901
879/// Make sure to uphold the contract points with rp2040-flash. 902/// Make sure to uphold the contract points with rp2040-flash.
@@ -904,7 +927,7 @@ pub(crate) unsafe fn in_ram(operation: impl FnOnce()) -> Result<(), Error> {
904 }); 927 });
905 928
906 // Resume CORE1 execution 929 // Resume CORE1 execution
907 crate::multicore::resume_core1(); 930 //crate::multicore::resume_core1();
908 Ok(()) 931 Ok(())
909} 932}
910 933
diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs
index ea87fd9da..d0bb7e574 100644
--- a/embassy-rp/src/gpio.rs
+++ b/embassy-rp/src/gpio.rs
@@ -14,7 +14,12 @@ use crate::pac::SIO;
14use crate::{interrupt, pac, peripherals, Peripheral, RegExt}; 14use crate::{interrupt, pac, peripherals, Peripheral, RegExt};
15 15
16const NEW_AW: AtomicWaker = AtomicWaker::new(); 16const NEW_AW: AtomicWaker = AtomicWaker::new();
17
18#[cfg(any(feature = "rp2040", feature = "rp235xa"))]
17const BANK0_PIN_COUNT: usize = 30; 19const BANK0_PIN_COUNT: usize = 30;
20#[cfg(feature = "rp235xb")]
21const BANK0_PIN_COUNT: usize = 48;
22
18static BANK0_WAKERS: [AtomicWaker; BANK0_PIN_COUNT] = [NEW_AW; BANK0_PIN_COUNT]; 23static BANK0_WAKERS: [AtomicWaker; BANK0_PIN_COUNT] = [NEW_AW; BANK0_PIN_COUNT];
19#[cfg(feature = "qspi-as-gpio")] 24#[cfg(feature = "qspi-as-gpio")]
20const QSPI_PIN_COUNT: usize = 6; 25const QSPI_PIN_COUNT: usize = 6;
@@ -178,6 +183,13 @@ impl<'d> Input<'d> {
178 pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake<'_> { 183 pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake<'_> {
179 self.pin.dormant_wake(cfg) 184 self.pin.dormant_wake(cfg)
180 } 185 }
186
187 /// Set the pin's pad isolation
188 #[cfg(feature = "_rp235x")]
189 #[inline]
190 pub fn set_pad_isolation(&mut self, isolate: bool) {
191 self.pin.set_pad_isolation(isolate)
192 }
181} 193}
182 194
183/// Interrupt trigger levels. 195/// Interrupt trigger levels.
@@ -413,6 +425,13 @@ impl<'d> Output<'d> {
413 pub fn toggle(&mut self) { 425 pub fn toggle(&mut self) {
414 self.pin.toggle() 426 self.pin.toggle()
415 } 427 }
428
429 /// Set the pin's pad isolation
430 #[cfg(feature = "_rp235x")]
431 #[inline]
432 pub fn set_pad_isolation(&mut self, isolate: bool) {
433 self.pin.set_pad_isolation(isolate)
434 }
416} 435}
417 436
418/// GPIO output open-drain. 437/// GPIO output open-drain.
@@ -539,6 +558,13 @@ impl<'d> OutputOpenDrain<'d> {
539 pub async fn wait_for_any_edge(&mut self) { 558 pub async fn wait_for_any_edge(&mut self) {
540 self.pin.wait_for_any_edge().await; 559 self.pin.wait_for_any_edge().await;
541 } 560 }
561
562 /// Set the pin's pad isolation
563 #[cfg(feature = "_rp235x")]
564 #[inline]
565 pub fn set_pad_isolation(&mut self, isolate: bool) {
566 self.pin.set_pad_isolation(isolate)
567 }
542} 568}
543 569
544/// GPIO flexible pin. 570/// GPIO flexible pin.
@@ -560,11 +586,16 @@ impl<'d> Flex<'d> {
560 into_ref!(pin); 586 into_ref!(pin);
561 587
562 pin.pad_ctrl().write(|w| { 588 pin.pad_ctrl().write(|w| {
589 #[cfg(feature = "_rp235x")]
590 w.set_iso(false);
563 w.set_ie(true); 591 w.set_ie(true);
564 }); 592 });
565 593
566 pin.gpio().ctrl().write(|w| { 594 pin.gpio().ctrl().write(|w| {
595 #[cfg(feature = "rp2040")]
567 w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0 as _); 596 w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0 as _);
597 #[cfg(feature = "_rp235x")]
598 w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIOB_PROC_0 as _);
568 }); 599 });
569 600
570 Self { pin: pin.map_into() } 601 Self { pin: pin.map_into() }
@@ -760,6 +791,15 @@ impl<'d> Flex<'d> {
760 cfg, 791 cfg,
761 } 792 }
762 } 793 }
794
795 /// Set the pin's pad isolation
796 #[cfg(feature = "_rp235x")]
797 #[inline]
798 pub fn set_pad_isolation(&mut self, isolate: bool) {
799 self.pin.pad_ctrl().modify(|w| {
800 w.set_iso(isolate);
801 });
802 }
763} 803}
764 804
765impl<'d> Drop for Flex<'d> { 805impl<'d> Drop for Flex<'d> {
@@ -956,6 +996,44 @@ impl_pin!(PIN_27, Bank::Bank0, 27);
956impl_pin!(PIN_28, Bank::Bank0, 28); 996impl_pin!(PIN_28, Bank::Bank0, 28);
957impl_pin!(PIN_29, Bank::Bank0, 29); 997impl_pin!(PIN_29, Bank::Bank0, 29);
958 998
999#[cfg(feature = "rp235xb")]
1000impl_pin!(PIN_30, Bank::Bank0, 30);
1001#[cfg(feature = "rp235xb")]
1002impl_pin!(PIN_31, Bank::Bank0, 31);
1003#[cfg(feature = "rp235xb")]
1004impl_pin!(PIN_32, Bank::Bank0, 32);
1005#[cfg(feature = "rp235xb")]
1006impl_pin!(PIN_33, Bank::Bank0, 33);
1007#[cfg(feature = "rp235xb")]
1008impl_pin!(PIN_34, Bank::Bank0, 34);
1009#[cfg(feature = "rp235xb")]
1010impl_pin!(PIN_35, Bank::Bank0, 35);
1011#[cfg(feature = "rp235xb")]
1012impl_pin!(PIN_36, Bank::Bank0, 36);
1013#[cfg(feature = "rp235xb")]
1014impl_pin!(PIN_37, Bank::Bank0, 37);
1015#[cfg(feature = "rp235xb")]
1016impl_pin!(PIN_38, Bank::Bank0, 38);
1017#[cfg(feature = "rp235xb")]
1018impl_pin!(PIN_39, Bank::Bank0, 39);
1019#[cfg(feature = "rp235xb")]
1020impl_pin!(PIN_40, Bank::Bank0, 40);
1021#[cfg(feature = "rp235xb")]
1022impl_pin!(PIN_41, Bank::Bank0, 41);
1023#[cfg(feature = "rp235xb")]
1024impl_pin!(PIN_42, Bank::Bank0, 42);
1025#[cfg(feature = "rp235xb")]
1026impl_pin!(PIN_43, Bank::Bank0, 43);
1027#[cfg(feature = "rp235xb")]
1028impl_pin!(PIN_44, Bank::Bank0, 44);
1029#[cfg(feature = "rp235xb")]
1030impl_pin!(PIN_45, Bank::Bank0, 45);
1031#[cfg(feature = "rp235xb")]
1032impl_pin!(PIN_46, Bank::Bank0, 46);
1033#[cfg(feature = "rp235xb")]
1034impl_pin!(PIN_47, Bank::Bank0, 47);
1035
1036// TODO rp235x bank1 as gpio support
959#[cfg(feature = "qspi-as-gpio")] 1037#[cfg(feature = "qspi-as-gpio")]
960impl_pin!(PIN_QSPI_SCLK, Bank::Qspi, 0); 1038impl_pin!(PIN_QSPI_SCLK, Bank::Qspi, 0);
961#[cfg(feature = "qspi-as-gpio")] 1039#[cfg(feature = "qspi-as-gpio")]
diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs
index ac2b1bc5a..32778215f 100644
--- a/embassy-rp/src/i2c.rs
+++ b/embassy-rp/src/i2c.rs
@@ -363,6 +363,8 @@ where
363{ 363{
364 pin.gpio().ctrl().write(|w| w.set_funcsel(3)); 364 pin.gpio().ctrl().write(|w| w.set_funcsel(3));
365 pin.pad_ctrl().write(|w| { 365 pin.pad_ctrl().write(|w| {
366 #[cfg(feature = "_rp235x")]
367 w.set_iso(false);
366 w.set_schmitt(true); 368 w.set_schmitt(true);
367 w.set_slewfast(false); 369 w.set_slewfast(false);
368 w.set_ie(true); 370 w.set_ie(true);
@@ -884,3 +886,39 @@ impl_pin!(PIN_26, I2C1, SdaPin);
884impl_pin!(PIN_27, I2C1, SclPin); 886impl_pin!(PIN_27, I2C1, SclPin);
885impl_pin!(PIN_28, I2C0, SdaPin); 887impl_pin!(PIN_28, I2C0, SdaPin);
886impl_pin!(PIN_29, I2C0, SclPin); 888impl_pin!(PIN_29, I2C0, SclPin);
889#[cfg(feature = "rp235xb")]
890impl_pin!(PIN_30, I2C1, SdaPin);
891#[cfg(feature = "rp235xb")]
892impl_pin!(PIN_31, I2C1, SclPin);
893#[cfg(feature = "rp235xb")]
894impl_pin!(PIN_32, I2C0, SdaPin);
895#[cfg(feature = "rp235xb")]
896impl_pin!(PIN_33, I2C0, SclPin);
897#[cfg(feature = "rp235xb")]
898impl_pin!(PIN_34, I2C1, SdaPin);
899#[cfg(feature = "rp235xb")]
900impl_pin!(PIN_35, I2C1, SclPin);
901#[cfg(feature = "rp235xb")]
902impl_pin!(PIN_36, I2C0, SdaPin);
903#[cfg(feature = "rp235xb")]
904impl_pin!(PIN_37, I2C0, SclPin);
905#[cfg(feature = "rp235xb")]
906impl_pin!(PIN_38, I2C1, SdaPin);
907#[cfg(feature = "rp235xb")]
908impl_pin!(PIN_39, I2C1, SclPin);
909#[cfg(feature = "rp235xb")]
910impl_pin!(PIN_40, I2C0, SdaPin);
911#[cfg(feature = "rp235xb")]
912impl_pin!(PIN_41, I2C0, SclPin);
913#[cfg(feature = "rp235xb")]
914impl_pin!(PIN_42, I2C1, SdaPin);
915#[cfg(feature = "rp235xb")]
916impl_pin!(PIN_43, I2C1, SclPin);
917#[cfg(feature = "rp235xb")]
918impl_pin!(PIN_44, I2C0, SdaPin);
919#[cfg(feature = "rp235xb")]
920impl_pin!(PIN_45, I2C0, SclPin);
921#[cfg(feature = "rp235xb")]
922impl_pin!(PIN_46, I2C1, SdaPin);
923#[cfg(feature = "rp235xb")]
924impl_pin!(PIN_47, I2C1, SclPin);
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index 471e7f8b1..1fc397107 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -15,10 +15,16 @@ mod critical_section_impl;
15mod intrinsics; 15mod intrinsics;
16 16
17pub mod adc; 17pub mod adc;
18#[cfg(feature = "_rp235x")]
19pub mod binary_info;
20#[cfg(feature = "_rp235x")]
21pub mod block;
22#[cfg(feature = "rp2040")]
18pub mod bootsel; 23pub mod bootsel;
19pub mod clocks; 24pub mod clocks;
20pub mod dma; 25pub mod dma;
21pub mod flash; 26pub mod flash;
27#[cfg(feature = "rp2040")]
22mod float; 28mod float;
23pub mod gpio; 29pub mod gpio;
24pub mod i2c; 30pub mod i2c;
@@ -27,6 +33,7 @@ pub mod multicore;
27pub mod pwm; 33pub mod pwm;
28mod reset; 34mod reset;
29pub mod rom_data; 35pub mod rom_data;
36#[cfg(feature = "rp2040")]
30pub mod rtc; 37pub mod rtc;
31pub mod spi; 38pub mod spi;
32#[cfg(feature = "time-driver")] 39#[cfg(feature = "time-driver")]
@@ -49,6 +56,7 @@ pub(crate) use rp_pac as pac;
49#[cfg(feature = "rt")] 56#[cfg(feature = "rt")]
50pub use crate::pac::NVIC_PRIO_BITS; 57pub use crate::pac::NVIC_PRIO_BITS;
51 58
59#[cfg(feature = "rp2040")]
52embassy_hal_internal::interrupt_mod!( 60embassy_hal_internal::interrupt_mod!(
53 TIMER_IRQ_0, 61 TIMER_IRQ_0,
54 TIMER_IRQ_1, 62 TIMER_IRQ_1,
@@ -84,6 +92,54 @@ embassy_hal_internal::interrupt_mod!(
84 SWI_IRQ_5, 92 SWI_IRQ_5,
85); 93);
86 94
95#[cfg(feature = "_rp235x")]
96embassy_hal_internal::interrupt_mod!(
97 TIMER0_IRQ_0,
98 TIMER0_IRQ_1,
99 TIMER0_IRQ_2,
100 TIMER0_IRQ_3,
101 TIMER1_IRQ_0,
102 TIMER1_IRQ_1,
103 TIMER1_IRQ_2,
104 TIMER1_IRQ_3,
105 PWM_IRQ_WRAP_0,
106 PWM_IRQ_WRAP_1,
107 DMA_IRQ_0,
108 DMA_IRQ_1,
109 USBCTRL_IRQ,
110 PIO0_IRQ_0,
111 PIO0_IRQ_1,
112 PIO1_IRQ_0,
113 PIO1_IRQ_1,
114 PIO2_IRQ_0,
115 PIO2_IRQ_1,
116 IO_IRQ_BANK0,
117 IO_IRQ_BANK0_NS,
118 IO_IRQ_QSPI,
119 IO_IRQ_QSPI_NS,
120 SIO_IRQ_FIFO,
121 SIO_IRQ_BELL,
122 SIO_IRQ_FIFO_NS,
123 SIO_IRQ_BELL_NS,
124 CLOCKS_IRQ,
125 SPI0_IRQ,
126 SPI1_IRQ,
127 UART0_IRQ,
128 UART1_IRQ,
129 ADC_IRQ_FIFO,
130 I2C0_IRQ,
131 I2C1_IRQ,
132 TRNG_IRQ,
133 PLL_SYS_IRQ,
134 PLL_USB_IRQ,
135 SWI_IRQ_0,
136 SWI_IRQ_1,
137 SWI_IRQ_2,
138 SWI_IRQ_3,
139 SWI_IRQ_4,
140 SWI_IRQ_5,
141);
142
87/// Macro to bind interrupts to handlers. 143/// Macro to bind interrupts to handlers.
88/// 144///
89/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) 145/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
@@ -123,6 +179,7 @@ macro_rules! bind_interrupts {
123 }; 179 };
124} 180}
125 181
182#[cfg(feature = "rp2040")]
126embassy_hal_internal::peripherals! { 183embassy_hal_internal::peripherals! {
127 PIN_0, 184 PIN_0,
128 PIN_1, 185 PIN_1,
@@ -210,6 +267,139 @@ embassy_hal_internal::peripherals! {
210 BOOTSEL, 267 BOOTSEL,
211} 268}
212 269
270#[cfg(feature = "_rp235x")]
271embassy_hal_internal::peripherals! {
272 PIN_0,
273 PIN_1,
274 PIN_2,
275 PIN_3,
276 PIN_4,
277 PIN_5,
278 PIN_6,
279 PIN_7,
280 PIN_8,
281 PIN_9,
282 PIN_10,
283 PIN_11,
284 PIN_12,
285 PIN_13,
286 PIN_14,
287 PIN_15,
288 PIN_16,
289 PIN_17,
290 PIN_18,
291 PIN_19,
292 PIN_20,
293 PIN_21,
294 PIN_22,
295 PIN_23,
296 PIN_24,
297 PIN_25,
298 PIN_26,
299 PIN_27,
300 PIN_28,
301 PIN_29,
302 #[cfg(feature = "rp235xb")]
303 PIN_30,
304 #[cfg(feature = "rp235xb")]
305 PIN_31,
306 #[cfg(feature = "rp235xb")]
307 PIN_32,
308 #[cfg(feature = "rp235xb")]
309 PIN_33,
310 #[cfg(feature = "rp235xb")]
311 PIN_34,
312 #[cfg(feature = "rp235xb")]
313 PIN_35,
314 #[cfg(feature = "rp235xb")]
315 PIN_36,
316 #[cfg(feature = "rp235xb")]
317 PIN_37,
318 #[cfg(feature = "rp235xb")]
319 PIN_38,
320 #[cfg(feature = "rp235xb")]
321 PIN_39,
322 #[cfg(feature = "rp235xb")]
323 PIN_40,
324 #[cfg(feature = "rp235xb")]
325 PIN_41,
326 #[cfg(feature = "rp235xb")]
327 PIN_42,
328 #[cfg(feature = "rp235xb")]
329 PIN_43,
330 #[cfg(feature = "rp235xb")]
331 PIN_44,
332 #[cfg(feature = "rp235xb")]
333 PIN_45,
334 #[cfg(feature = "rp235xb")]
335 PIN_46,
336 #[cfg(feature = "rp235xb")]
337 PIN_47,
338 PIN_QSPI_SCLK,
339 PIN_QSPI_SS,
340 PIN_QSPI_SD0,
341 PIN_QSPI_SD1,
342 PIN_QSPI_SD2,
343 PIN_QSPI_SD3,
344
345 UART0,
346 UART1,
347
348 SPI0,
349 SPI1,
350
351 I2C0,
352 I2C1,
353
354 DMA_CH0,
355 DMA_CH1,
356 DMA_CH2,
357 DMA_CH3,
358 DMA_CH4,
359 DMA_CH5,
360 DMA_CH6,
361 DMA_CH7,
362 DMA_CH8,
363 DMA_CH9,
364 DMA_CH10,
365 DMA_CH11,
366 DMA_CH12,
367 DMA_CH13,
368 DMA_CH14,
369 DMA_CH15,
370
371 PWM_SLICE0,
372 PWM_SLICE1,
373 PWM_SLICE2,
374 PWM_SLICE3,
375 PWM_SLICE4,
376 PWM_SLICE5,
377 PWM_SLICE6,
378 PWM_SLICE7,
379 PWM_SLICE8,
380 PWM_SLICE9,
381 PWM_SLICE10,
382 PWM_SLICE11,
383
384 USB,
385
386 RTC,
387
388 FLASH,
389
390 ADC,
391 ADC_TEMP_SENSOR,
392
393 CORE1,
394
395 PIO0,
396 PIO1,
397 PIO2,
398
399 WATCHDOG,
400 BOOTSEL,
401}
402
213#[cfg(not(feature = "boot2-none"))] 403#[cfg(not(feature = "boot2-none"))]
214macro_rules! select_bootloader { 404macro_rules! select_bootloader {
215 ( $( $feature:literal => $loader:ident, )+ default => $default:ident ) => { 405 ( $( $feature:literal => $loader:ident, )+ default => $default:ident ) => {
@@ -279,6 +469,7 @@ pub fn install_core0_stack_guard() -> Result<(), ()> {
279 unsafe { install_stack_guard(core::ptr::addr_of_mut!(_stack_end)) } 469 unsafe { install_stack_guard(core::ptr::addr_of_mut!(_stack_end)) }
280} 470}
281 471
472#[cfg(all(feature = "rp2040", not(feature = "_test")))]
282#[inline(always)] 473#[inline(always)]
283fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> { 474fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> {
284 let core = unsafe { cortex_m::Peripherals::steal() }; 475 let core = unsafe { cortex_m::Peripherals::steal() };
@@ -306,6 +497,32 @@ fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> {
306 Ok(()) 497 Ok(())
307} 498}
308 499
500#[cfg(all(feature = "_rp235x", not(feature = "_test")))]
501#[inline(always)]
502fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> {
503 let core = unsafe { cortex_m::Peripherals::steal() };
504
505 // Fail if MPU is already configured
506 if core.MPU.ctrl.read() != 0 {
507 return Err(());
508 }
509
510 unsafe {
511 core.MPU.ctrl.write(5); // enable mpu with background default map
512 core.MPU.rbar.write(stack_bottom as u32 & !0xff); // set address
513 core.MPU.rlar.write(1); // enable region
514 }
515 Ok(())
516}
517
518// This is to hack around cortex_m defaulting to ARMv7 when building tests,
519// so the compile fails when we try to use ARMv8 peripherals.
520#[cfg(feature = "_test")]
521#[inline(always)]
522fn install_stack_guard(_stack_bottom: *mut usize) -> Result<(), ()> {
523 Ok(())
524}
525
309/// HAL configuration for RP. 526/// HAL configuration for RP.
310pub mod config { 527pub mod config {
311 use crate::clocks::ClockConfig; 528 use crate::clocks::ClockConfig;
@@ -354,7 +571,7 @@ pub fn init(config: config::Config) -> Peripherals {
354 peripherals 571 peripherals
355} 572}
356 573
357#[cfg(feature = "rt")] 574#[cfg(all(feature = "rt", feature = "rp2040"))]
358#[cortex_m_rt::pre_init] 575#[cortex_m_rt::pre_init]
359unsafe fn pre_init() { 576unsafe fn pre_init() {
360 // SIO does not get reset when core0 is reset with either `scb::sys_reset()` or with SWD. 577 // SIO does not get reset when core0 is reset with either `scb::sys_reset()` or with SWD.
diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs
index d9d65694a..9f7d77bf5 100644
--- a/embassy-rp/src/multicore.rs
+++ b/embassy-rp/src/multicore.rs
@@ -84,7 +84,7 @@ impl<const SIZE: usize> Stack<SIZE> {
84 } 84 }
85} 85}
86 86
87#[cfg(feature = "rt")] 87#[cfg(all(feature = "rt", feature = "rp2040"))]
88#[interrupt] 88#[interrupt]
89#[link_section = ".data.ram_func"] 89#[link_section = ".data.ram_func"]
90unsafe fn SIO_IRQ_PROC1() { 90unsafe fn SIO_IRQ_PROC1() {
@@ -109,6 +109,31 @@ unsafe fn SIO_IRQ_PROC1() {
109 } 109 }
110} 110}
111 111
112#[cfg(all(feature = "rt", feature = "_rp235x"))]
113#[interrupt]
114#[link_section = ".data.ram_func"]
115unsafe fn SIO_IRQ_FIFO() {
116 let sio = pac::SIO;
117 // Clear IRQ
118 sio.fifo().st().write(|w| w.set_wof(false));
119
120 while sio.fifo().st().read().vld() {
121 // Pause CORE1 execution and disable interrupts
122 if fifo_read_wfe() == PAUSE_TOKEN {
123 cortex_m::interrupt::disable();
124 // Signal to CORE0 that execution is paused
125 fifo_write(PAUSE_TOKEN);
126 // Wait for `resume` signal from CORE0
127 while fifo_read_wfe() != RESUME_TOKEN {
128 cortex_m::asm::nop();
129 }
130 cortex_m::interrupt::enable();
131 // Signal to CORE0 that execution is resumed
132 fifo_write(RESUME_TOKEN);
133 }
134 }
135}
136
112/// Spawn a function on this core 137/// Spawn a function on this core
113pub fn spawn_core1<F, const SIZE: usize>(_core1: CORE1, stack: &'static mut Stack<SIZE>, entry: F) 138pub fn spawn_core1<F, const SIZE: usize>(_core1: CORE1, stack: &'static mut Stack<SIZE>, entry: F)
114where 139where
@@ -135,7 +160,14 @@ where
135 160
136 IS_CORE1_INIT.store(true, Ordering::Release); 161 IS_CORE1_INIT.store(true, Ordering::Release);
137 // Enable fifo interrupt on CORE1 for `pause` functionality. 162 // Enable fifo interrupt on CORE1 for `pause` functionality.
138 unsafe { interrupt::SIO_IRQ_PROC1.enable() }; 163 #[cfg(feature = "rp2040")]
164 unsafe {
165 interrupt::SIO_IRQ_PROC1.enable()
166 };
167 #[cfg(feature = "_rp235x")]
168 unsafe {
169 interrupt::SIO_IRQ_FIFO.enable()
170 };
139 171
140 entry() 172 entry()
141 } 173 }
diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs
index 1f7adbda3..68b1d6849 100644
--- a/embassy-rp/src/pio/mod.rs
+++ b/embassy-rp/src/pio/mod.rs
@@ -10,14 +10,11 @@ use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
10use embassy_sync::waitqueue::AtomicWaker; 10use embassy_sync::waitqueue::AtomicWaker;
11use fixed::types::extra::U8; 11use fixed::types::extra::U8;
12use fixed::FixedU32; 12use fixed::FixedU32;
13use pac::io::vals::Gpio0ctrlFuncsel;
14use pac::pio::vals::SmExecctrlStatusSel;
15use pio::{Program, SideSet, Wrap}; 13use pio::{Program, SideSet, Wrap};
16 14
17use crate::dma::{Channel, Transfer, Word}; 15use crate::dma::{Channel, Transfer, Word};
18use crate::gpio::{self, AnyPin, Drive, Level, Pull, SealedPin, SlewRate}; 16use crate::gpio::{self, AnyPin, Drive, Level, Pull, SealedPin, SlewRate};
19use crate::interrupt::typelevel::{Binding, Handler, Interrupt}; 17use crate::interrupt::typelevel::{Binding, Handler, Interrupt};
20use crate::pac::dma::vals::TreqSel;
21use crate::relocate::RelocatedProgram; 18use crate::relocate::RelocatedProgram;
22use crate::{pac, peripherals, RegExt}; 19use crate::{pac, peripherals, RegExt};
23 20
@@ -355,11 +352,14 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> {
355 let p = ch.regs(); 352 let p = ch.regs();
356 p.write_addr().write_value(data.as_ptr() as u32); 353 p.write_addr().write_value(data.as_ptr() as u32);
357 p.read_addr().write_value(PIO::PIO.rxf(SM).as_ptr() as u32); 354 p.read_addr().write_value(PIO::PIO.rxf(SM).as_ptr() as u32);
358 p.trans_count().write_value(data.len() as u32); 355 #[cfg(feature = "rp2040")]
356 p.trans_count().write(|w| *w = data.len() as u32);
357 #[cfg(feature = "_rp235x")]
358 p.trans_count().write(|w| w.set_count(data.len() as u32));
359 compiler_fence(Ordering::SeqCst); 359 compiler_fence(Ordering::SeqCst);
360 p.ctrl_trig().write(|w| { 360 p.ctrl_trig().write(|w| {
361 // Set RX DREQ for this statemachine 361 // Set RX DREQ for this statemachine
362 w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8 + 4)); 362 w.set_treq_sel(crate::pac::dma::vals::TreqSel::from(pio_no * 8 + SM as u8 + 4));
363 w.set_data_size(W::size()); 363 w.set_data_size(W::size());
364 w.set_chain_to(ch.number()); 364 w.set_chain_to(ch.number());
365 w.set_incr_read(false); 365 w.set_incr_read(false);
@@ -437,11 +437,14 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> {
437 let p = ch.regs(); 437 let p = ch.regs();
438 p.read_addr().write_value(data.as_ptr() as u32); 438 p.read_addr().write_value(data.as_ptr() as u32);
439 p.write_addr().write_value(PIO::PIO.txf(SM).as_ptr() as u32); 439 p.write_addr().write_value(PIO::PIO.txf(SM).as_ptr() as u32);
440 p.trans_count().write_value(data.len() as u32); 440 #[cfg(feature = "rp2040")]
441 p.trans_count().write(|w| *w = data.len() as u32);
442 #[cfg(feature = "_rp235x")]
443 p.trans_count().write(|w| w.set_count(data.len() as u32));
441 compiler_fence(Ordering::SeqCst); 444 compiler_fence(Ordering::SeqCst);
442 p.ctrl_trig().write(|w| { 445 p.ctrl_trig().write(|w| {
443 // Set TX DREQ for this statemachine 446 // Set TX DREQ for this statemachine
444 w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8)); 447 w.set_treq_sel(crate::pac::dma::vals::TreqSel::from(pio_no * 8 + SM as u8));
445 w.set_data_size(W::size()); 448 w.set_data_size(W::size());
446 w.set_chain_to(ch.number()); 449 w.set_chain_to(ch.number());
447 w.set_incr_read(true); 450 w.set_incr_read(true);
@@ -523,6 +526,39 @@ pub struct PinConfig {
523 pub out_base: u8, 526 pub out_base: u8,
524} 527}
525 528
529/// Comparison level or IRQ index for the MOV x, STATUS instruction.
530#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
531#[cfg_attr(feature = "defmt", derive(defmt::Format))]
532#[cfg(feature = "_rp235x")]
533pub enum StatusN {
534 /// IRQ flag in this PIO block
535 This(u8),
536 /// IRQ flag in the next lower PIO block
537 Lower(u8),
538 /// IRQ flag in the next higher PIO block
539 Higher(u8),
540}
541
542#[cfg(feature = "_rp235x")]
543impl Default for StatusN {
544 fn default() -> Self {
545 Self::This(0)
546 }
547}
548
549#[cfg(feature = "_rp235x")]
550impl Into<crate::pac::pio::vals::ExecctrlStatusN> for StatusN {
551 fn into(self) -> crate::pac::pio::vals::ExecctrlStatusN {
552 let x = match self {
553 StatusN::This(n) => n,
554 StatusN::Lower(n) => n + 0x08,
555 StatusN::Higher(n) => n + 0x10,
556 };
557
558 crate::pac::pio::vals::ExecctrlStatusN(x)
559 }
560}
561
526/// PIO config. 562/// PIO config.
527#[derive(Clone, Copy, Debug)] 563#[derive(Clone, Copy, Debug)]
528pub struct Config<'d, PIO: Instance> { 564pub struct Config<'d, PIO: Instance> {
@@ -537,7 +573,12 @@ pub struct Config<'d, PIO: Instance> {
537 /// Which source to use for checking status. 573 /// Which source to use for checking status.
538 pub status_sel: StatusSource, 574 pub status_sel: StatusSource,
539 /// Status comparison level. 575 /// Status comparison level.
576 #[cfg(feature = "rp2040")]
540 pub status_n: u8, 577 pub status_n: u8,
578 // This cfg probably shouldn't be required, but the SVD for the 2040 doesn't have the enum
579 #[cfg(feature = "_rp235x")]
580 /// Status comparison level.
581 pub status_n: StatusN,
541 exec: ExecConfig, 582 exec: ExecConfig,
542 origin: Option<u8>, 583 origin: Option<u8>,
543 /// Configure FIFO allocation. 584 /// Configure FIFO allocation.
@@ -653,7 +694,7 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
653 assert!(config.clock_divider <= 65536, "clkdiv must be <= 65536"); 694 assert!(config.clock_divider <= 65536, "clkdiv must be <= 65536");
654 assert!(config.clock_divider >= 1, "clkdiv must be >= 1"); 695 assert!(config.clock_divider >= 1, "clkdiv must be >= 1");
655 assert!(config.out_en_sel < 32, "out_en_sel must be < 32"); 696 assert!(config.out_en_sel < 32, "out_en_sel must be < 32");
656 assert!(config.status_n < 32, "status_n must be < 32"); 697 //assert!(config.status_n < 32, "status_n must be < 32");
657 // sm expects 0 for 32, truncation makes that happen 698 // sm expects 0 for 32, truncation makes that happen
658 assert!(config.shift_in.threshold <= 32, "shift_in.threshold must be <= 32"); 699 assert!(config.shift_in.threshold <= 32, "shift_in.threshold must be <= 32");
659 assert!(config.shift_out.threshold <= 32, "shift_out.threshold must be <= 32"); 700 assert!(config.shift_out.threshold <= 32, "shift_out.threshold must be <= 32");
@@ -668,11 +709,17 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
668 w.set_out_sticky(config.out_sticky); 709 w.set_out_sticky(config.out_sticky);
669 w.set_wrap_top(config.exec.wrap_top); 710 w.set_wrap_top(config.exec.wrap_top);
670 w.set_wrap_bottom(config.exec.wrap_bottom); 711 w.set_wrap_bottom(config.exec.wrap_bottom);
712 #[cfg(feature = "_rp235x")]
671 w.set_status_sel(match config.status_sel { 713 w.set_status_sel(match config.status_sel {
672 StatusSource::TxFifoLevel => SmExecctrlStatusSel::TXLEVEL, 714 StatusSource::TxFifoLevel => pac::pio::vals::ExecctrlStatusSel::TXLEVEL,
673 StatusSource::RxFifoLevel => SmExecctrlStatusSel::RXLEVEL, 715 StatusSource::RxFifoLevel => pac::pio::vals::ExecctrlStatusSel::RXLEVEL,
674 }); 716 });
675 w.set_status_n(config.status_n); 717 #[cfg(feature = "rp2040")]
718 w.set_status_sel(match config.status_sel {
719 StatusSource::TxFifoLevel => pac::pio::vals::SmExecctrlStatusSel::TXLEVEL,
720 StatusSource::RxFifoLevel => pac::pio::vals::SmExecctrlStatusSel::RXLEVEL,
721 });
722 w.set_status_n(config.status_n.into());
676 }); 723 });
677 sm.shiftctrl().write(|w| { 724 sm.shiftctrl().write(|w| {
678 w.set_fjoin_rx(config.fifo_join == FifoJoin::RxOnly); 725 w.set_fjoin_rx(config.fifo_join == FifoJoin::RxOnly);
@@ -1147,7 +1194,7 @@ fn on_pio_drop<PIO: Instance>() {
1147 let state = PIO::state(); 1194 let state = PIO::state();
1148 if state.users.fetch_sub(1, Ordering::AcqRel) == 1 { 1195 if state.users.fetch_sub(1, Ordering::AcqRel) == 1 {
1149 let used_pins = state.used_pins.load(Ordering::Relaxed); 1196 let used_pins = state.used_pins.load(Ordering::Relaxed);
1150 let null = Gpio0ctrlFuncsel::NULL as _; 1197 let null = pac::io::vals::Gpio0ctrlFuncsel::NULL as _;
1151 // we only have 30 pins. don't test the other two since gpio() asserts. 1198 // we only have 30 pins. don't test the other two since gpio() asserts.
1152 for i in 0..30 { 1199 for i in 0..30 {
1153 if used_pins & (1 << i) != 0 { 1200 if used_pins & (1 << i) != 0 {
@@ -1203,6 +1250,8 @@ macro_rules! impl_pio {
1203 1250
1204impl_pio!(PIO0, 0, PIO0, PIO0_0, PIO0_IRQ_0); 1251impl_pio!(PIO0, 0, PIO0, PIO0_0, PIO0_IRQ_0);
1205impl_pio!(PIO1, 1, PIO1, PIO1_0, PIO1_IRQ_0); 1252impl_pio!(PIO1, 1, PIO1, PIO1_0, PIO1_IRQ_0);
1253#[cfg(feature = "_rp235x")]
1254impl_pio!(PIO2, 2, PIO2, PIO2_0, PIO2_IRQ_0);
1206 1255
1207/// PIO pin. 1256/// PIO pin.
1208pub trait PioPin: gpio::Pin {} 1257pub trait PioPin: gpio::Pin {}
@@ -1247,3 +1296,25 @@ impl_pio_pin! {
1247 PIN_28, 1296 PIN_28,
1248 PIN_29, 1297 PIN_29,
1249} 1298}
1299
1300#[cfg(feature = "rp235xb")]
1301impl_pio_pin! {
1302 PIN_30,
1303 PIN_31,
1304 PIN_32,
1305 PIN_33,
1306 PIN_34,
1307 PIN_35,
1308 PIN_36,
1309 PIN_37,
1310 PIN_38,
1311 PIN_39,
1312 PIN_40,
1313 PIN_41,
1314 PIN_42,
1315 PIN_43,
1316 PIN_44,
1317 PIN_45,
1318 PIN_46,
1319 PIN_47,
1320}
diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs
index c35e76587..7da3dccb0 100644
--- a/embassy-rp/src/pwm.rs
+++ b/embassy-rp/src/pwm.rs
@@ -110,6 +110,8 @@ impl<'d> Pwm<'d> {
110 if let Some(pin) = &b { 110 if let Some(pin) = &b {
111 pin.gpio().ctrl().write(|w| w.set_funcsel(4)); 111 pin.gpio().ctrl().write(|w| w.set_funcsel(4));
112 pin.pad_ctrl().modify(|w| { 112 pin.pad_ctrl().modify(|w| {
113 #[cfg(feature = "_rp235x")]
114 w.set_iso(false);
113 w.set_pue(b_pull == Pull::Up); 115 w.set_pue(b_pull == Pull::Up);
114 w.set_pde(b_pull == Pull::Down); 116 w.set_pde(b_pull == Pull::Down);
115 }); 117 });
@@ -363,6 +365,15 @@ slice!(PWM_SLICE5, 5);
363slice!(PWM_SLICE6, 6); 365slice!(PWM_SLICE6, 6);
364slice!(PWM_SLICE7, 7); 366slice!(PWM_SLICE7, 7);
365 367
368#[cfg(feature = "_rp235x")]
369slice!(PWM_SLICE8, 8);
370#[cfg(feature = "_rp235x")]
371slice!(PWM_SLICE9, 9);
372#[cfg(feature = "_rp235x")]
373slice!(PWM_SLICE10, 10);
374#[cfg(feature = "_rp235x")]
375slice!(PWM_SLICE11, 11);
376
366/// PWM Channel A. 377/// PWM Channel A.
367pub trait ChannelAPin<T: Slice>: GpioPin {} 378pub trait ChannelAPin<T: Slice>: GpioPin {}
368/// PWM Channel B. 379/// PWM Channel B.
@@ -404,3 +415,39 @@ impl_pin!(PIN_26, PWM_SLICE5, ChannelAPin);
404impl_pin!(PIN_27, PWM_SLICE5, ChannelBPin); 415impl_pin!(PIN_27, PWM_SLICE5, ChannelBPin);
405impl_pin!(PIN_28, PWM_SLICE6, ChannelAPin); 416impl_pin!(PIN_28, PWM_SLICE6, ChannelAPin);
406impl_pin!(PIN_29, PWM_SLICE6, ChannelBPin); 417impl_pin!(PIN_29, PWM_SLICE6, ChannelBPin);
418#[cfg(feature = "rp235xb")]
419impl_pin!(PIN_30, PWM_SLICE7, ChannelAPin);
420#[cfg(feature = "rp235xb")]
421impl_pin!(PIN_31, PWM_SLICE7, ChannelBPin);
422#[cfg(feature = "rp235xb")]
423impl_pin!(PIN_32, PWM_SLICE8, ChannelAPin);
424#[cfg(feature = "rp235xb")]
425impl_pin!(PIN_33, PWM_SLICE8, ChannelBPin);
426#[cfg(feature = "rp235xb")]
427impl_pin!(PIN_34, PWM_SLICE9, ChannelAPin);
428#[cfg(feature = "rp235xb")]
429impl_pin!(PIN_35, PWM_SLICE9, ChannelBPin);
430#[cfg(feature = "rp235xb")]
431impl_pin!(PIN_36, PWM_SLICE10, ChannelAPin);
432#[cfg(feature = "rp235xb")]
433impl_pin!(PIN_37, PWM_SLICE10, ChannelBPin);
434#[cfg(feature = "rp235xb")]
435impl_pin!(PIN_38, PWM_SLICE11, ChannelAPin);
436#[cfg(feature = "rp235xb")]
437impl_pin!(PIN_39, PWM_SLICE11, ChannelBPin);
438#[cfg(feature = "rp235xb")]
439impl_pin!(PIN_40, PWM_SLICE8, ChannelAPin);
440#[cfg(feature = "rp235xb")]
441impl_pin!(PIN_41, PWM_SLICE8, ChannelBPin);
442#[cfg(feature = "rp235xb")]
443impl_pin!(PIN_42, PWM_SLICE9, ChannelAPin);
444#[cfg(feature = "rp235xb")]
445impl_pin!(PIN_43, PWM_SLICE9, ChannelBPin);
446#[cfg(feature = "rp235xb")]
447impl_pin!(PIN_44, PWM_SLICE10, ChannelAPin);
448#[cfg(feature = "rp235xb")]
449impl_pin!(PIN_45, PWM_SLICE10, ChannelBPin);
450#[cfg(feature = "rp235xb")]
451impl_pin!(PIN_46, PWM_SLICE11, ChannelAPin);
452#[cfg(feature = "rp235xb")]
453impl_pin!(PIN_47, PWM_SLICE11, ChannelBPin);
diff --git a/embassy-rp/src/reset.rs b/embassy-rp/src/reset.rs
index 70512fa14..4b9e42483 100644
--- a/embassy-rp/src/reset.rs
+++ b/embassy-rp/src/reset.rs
@@ -2,7 +2,7 @@ pub use pac::resets::regs::Peripherals;
2 2
3use crate::pac; 3use crate::pac;
4 4
5pub const ALL_PERIPHERALS: Peripherals = Peripherals(0x01ffffff); 5pub const ALL_PERIPHERALS: Peripherals = Peripherals(0x01ff_ffff);
6 6
7pub(crate) fn reset(peris: Peripherals) { 7pub(crate) fn reset(peris: Peripherals) {
8 pac::RESETS.reset().write_value(peris); 8 pac::RESETS.reset().write_value(peris);
diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs
index 1617c144c..b89df74a2 100644
--- a/embassy-rp/src/spi.rs
+++ b/embassy-rp/src/spi.rs
@@ -106,15 +106,55 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> {
106 106
107 if let Some(pin) = &clk { 107 if let Some(pin) = &clk {
108 pin.gpio().ctrl().write(|w| w.set_funcsel(1)); 108 pin.gpio().ctrl().write(|w| w.set_funcsel(1));
109 pin.pad_ctrl().write(|w| {
110 #[cfg(feature = "_rp235x")]
111 w.set_iso(false);
112 w.set_schmitt(true);
113 w.set_slewfast(false);
114 w.set_ie(true);
115 w.set_od(false);
116 w.set_pue(false);
117 w.set_pde(false);
118 });
109 } 119 }
110 if let Some(pin) = &mosi { 120 if let Some(pin) = &mosi {
111 pin.gpio().ctrl().write(|w| w.set_funcsel(1)); 121 pin.gpio().ctrl().write(|w| w.set_funcsel(1));
122 pin.pad_ctrl().write(|w| {
123 #[cfg(feature = "_rp235x")]
124 w.set_iso(false);
125 w.set_schmitt(true);
126 w.set_slewfast(false);
127 w.set_ie(true);
128 w.set_od(false);
129 w.set_pue(false);
130 w.set_pde(false);
131 });
112 } 132 }
113 if let Some(pin) = &miso { 133 if let Some(pin) = &miso {
114 pin.gpio().ctrl().write(|w| w.set_funcsel(1)); 134 pin.gpio().ctrl().write(|w| w.set_funcsel(1));
135 pin.pad_ctrl().write(|w| {
136 #[cfg(feature = "_rp235x")]
137 w.set_iso(false);
138 w.set_schmitt(true);
139 w.set_slewfast(false);
140 w.set_ie(true);
141 w.set_od(false);
142 w.set_pue(false);
143 w.set_pde(false);
144 });
115 } 145 }
116 if let Some(pin) = &cs { 146 if let Some(pin) = &cs {
117 pin.gpio().ctrl().write(|w| w.set_funcsel(1)); 147 pin.gpio().ctrl().write(|w| w.set_funcsel(1));
148 pin.pad_ctrl().write(|w| {
149 #[cfg(feature = "_rp235x")]
150 w.set_iso(false);
151 w.set_schmitt(true);
152 w.set_slewfast(false);
153 w.set_ie(true);
154 w.set_od(false);
155 w.set_pue(false);
156 w.set_pde(false);
157 });
118 } 158 }
119 Self { 159 Self {
120 inner, 160 inner,
@@ -442,8 +482,8 @@ impl<'d, T: Instance> Spi<'d, T, Async> {
442trait SealedMode {} 482trait SealedMode {}
443 483
444trait SealedInstance { 484trait SealedInstance {
445 const TX_DREQ: u8; 485 const TX_DREQ: pac::dma::vals::TreqSel;
446 const RX_DREQ: u8; 486 const RX_DREQ: pac::dma::vals::TreqSel;
447 487
448 fn regs(&self) -> pac::spi::Spi; 488 fn regs(&self) -> pac::spi::Spi;
449} 489}
@@ -459,8 +499,8 @@ pub trait Instance: SealedInstance {}
459macro_rules! impl_instance { 499macro_rules! impl_instance {
460 ($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { 500 ($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => {
461 impl SealedInstance for peripherals::$type { 501 impl SealedInstance for peripherals::$type {
462 const TX_DREQ: u8 = $tx_dreq; 502 const TX_DREQ: pac::dma::vals::TreqSel = $tx_dreq;
463 const RX_DREQ: u8 = $rx_dreq; 503 const RX_DREQ: pac::dma::vals::TreqSel = $rx_dreq;
464 504
465 fn regs(&self) -> pac::spi::Spi { 505 fn regs(&self) -> pac::spi::Spi {
466 pac::$type 506 pac::$type
@@ -470,8 +510,18 @@ macro_rules! impl_instance {
470 }; 510 };
471} 511}
472 512
473impl_instance!(SPI0, Spi0, 16, 17); 513impl_instance!(
474impl_instance!(SPI1, Spi1, 18, 19); 514 SPI0,
515 Spi0,
516 pac::dma::vals::TreqSel::SPI0_TX,
517 pac::dma::vals::TreqSel::SPI0_RX
518);
519impl_instance!(
520 SPI1,
521 Spi1,
522 pac::dma::vals::TreqSel::SPI1_TX,
523 pac::dma::vals::TreqSel::SPI1_RX
524);
475 525
476/// CLK pin. 526/// CLK pin.
477pub trait ClkPin<T: Instance>: GpioPin {} 527pub trait ClkPin<T: Instance>: GpioPin {}
@@ -518,6 +568,42 @@ impl_pin!(PIN_26, SPI1, ClkPin);
518impl_pin!(PIN_27, SPI1, MosiPin); 568impl_pin!(PIN_27, SPI1, MosiPin);
519impl_pin!(PIN_28, SPI1, MisoPin); 569impl_pin!(PIN_28, SPI1, MisoPin);
520impl_pin!(PIN_29, SPI1, CsPin); 570impl_pin!(PIN_29, SPI1, CsPin);
571#[cfg(feature = "rp235xb")]
572impl_pin!(PIN_30, SPI1, ClkPin);
573#[cfg(feature = "rp235xb")]
574impl_pin!(PIN_31, SPI1, MosiPin);
575#[cfg(feature = "rp235xb")]
576impl_pin!(PIN_32, SPI0, MisoPin);
577#[cfg(feature = "rp235xb")]
578impl_pin!(PIN_33, SPI0, CsPin);
579#[cfg(feature = "rp235xb")]
580impl_pin!(PIN_34, SPI0, ClkPin);
581#[cfg(feature = "rp235xb")]
582impl_pin!(PIN_35, SPI0, MosiPin);
583#[cfg(feature = "rp235xb")]
584impl_pin!(PIN_36, SPI0, MisoPin);
585#[cfg(feature = "rp235xb")]
586impl_pin!(PIN_37, SPI0, CsPin);
587#[cfg(feature = "rp235xb")]
588impl_pin!(PIN_38, SPI0, ClkPin);
589#[cfg(feature = "rp235xb")]
590impl_pin!(PIN_39, SPI0, MosiPin);
591#[cfg(feature = "rp235xb")]
592impl_pin!(PIN_40, SPI1, MisoPin);
593#[cfg(feature = "rp235xb")]
594impl_pin!(PIN_41, SPI1, CsPin);
595#[cfg(feature = "rp235xb")]
596impl_pin!(PIN_42, SPI1, ClkPin);
597#[cfg(feature = "rp235xb")]
598impl_pin!(PIN_43, SPI1, MosiPin);
599#[cfg(feature = "rp235xb")]
600impl_pin!(PIN_44, SPI1, MisoPin);
601#[cfg(feature = "rp235xb")]
602impl_pin!(PIN_45, SPI1, CsPin);
603#[cfg(feature = "rp235xb")]
604impl_pin!(PIN_46, SPI1, ClkPin);
605#[cfg(feature = "rp235xb")]
606impl_pin!(PIN_47, SPI1, MosiPin);
521 607
522macro_rules! impl_mode { 608macro_rules! impl_mode {
523 ($name:ident) => { 609 ($name:ident) => {
diff --git a/embassy-rp/src/time_driver.rs b/embassy-rp/src/time_driver.rs
index bab1044cb..e5b407a29 100644
--- a/embassy-rp/src/time_driver.rs
+++ b/embassy-rp/src/time_driver.rs
@@ -6,6 +6,10 @@ use critical_section::CriticalSection;
6use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 6use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
7use embassy_sync::blocking_mutex::Mutex; 7use embassy_sync::blocking_mutex::Mutex;
8use embassy_time_driver::{AlarmHandle, Driver}; 8use embassy_time_driver::{AlarmHandle, Driver};
9#[cfg(feature = "rp2040")]
10use pac::TIMER;
11#[cfg(feature = "_rp235x")]
12use pac::TIMER0 as TIMER;
9 13
10use crate::interrupt::InterruptExt; 14use crate::interrupt::InterruptExt;
11use crate::{interrupt, pac}; 15use crate::{interrupt, pac};
@@ -35,9 +39,9 @@ embassy_time_driver::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{
35impl Driver for TimerDriver { 39impl Driver for TimerDriver {
36 fn now(&self) -> u64 { 40 fn now(&self) -> u64 {
37 loop { 41 loop {
38 let hi = pac::TIMER.timerawh().read(); 42 let hi = TIMER.timerawh().read();
39 let lo = pac::TIMER.timerawl().read(); 43 let lo = TIMER.timerawl().read();
40 let hi2 = pac::TIMER.timerawh().read(); 44 let hi2 = TIMER.timerawh().read();
41 if hi == hi2 { 45 if hi == hi2 {
42 return (hi as u64) << 32 | (lo as u64); 46 return (hi as u64) << 32 | (lo as u64);
43 } 47 }
@@ -77,13 +81,13 @@ impl Driver for TimerDriver {
77 // Note that we're not checking the high bits at all. This means the irq may fire early 81 // Note that we're not checking the high bits at all. This means the irq may fire early
78 // if the alarm is more than 72 minutes (2^32 us) in the future. This is OK, since on irq fire 82 // if the alarm is more than 72 minutes (2^32 us) in the future. This is OK, since on irq fire
79 // it is checked if the alarm time has passed. 83 // it is checked if the alarm time has passed.
80 pac::TIMER.alarm(n).write_value(timestamp as u32); 84 TIMER.alarm(n).write_value(timestamp as u32);
81 85
82 let now = self.now(); 86 let now = self.now();
83 if timestamp <= now { 87 if timestamp <= now {
84 // If alarm timestamp has passed the alarm will not fire. 88 // If alarm timestamp has passed the alarm will not fire.
85 // Disarm the alarm and return `false` to indicate that. 89 // Disarm the alarm and return `false` to indicate that.
86 pac::TIMER.armed().write(|w| w.set_armed(1 << n)); 90 TIMER.armed().write(|w| w.set_armed(1 << n));
87 91
88 alarm.timestamp.set(u64::MAX); 92 alarm.timestamp.set(u64::MAX);
89 93
@@ -105,17 +109,17 @@ impl TimerDriver {
105 } else { 109 } else {
106 // Not elapsed, arm it again. 110 // Not elapsed, arm it again.
107 // This can happen if it was set more than 2^32 us in the future. 111 // This can happen if it was set more than 2^32 us in the future.
108 pac::TIMER.alarm(n).write_value(timestamp as u32); 112 TIMER.alarm(n).write_value(timestamp as u32);
109 } 113 }
110 }); 114 });
111 115
112 // clear the irq 116 // clear the irq
113 pac::TIMER.intr().write(|w| w.set_alarm(n, true)); 117 TIMER.intr().write(|w| w.set_alarm(n, true));
114 } 118 }
115 119
116 fn trigger_alarm(&self, n: usize, cs: CriticalSection) { 120 fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
117 // disarm 121 // disarm
118 pac::TIMER.armed().write(|w| w.set_armed(1 << n)); 122 TIMER.armed().write(|w| w.set_armed(1 << n));
119 123
120 let alarm = &self.alarms.borrow(cs)[n]; 124 let alarm = &self.alarms.borrow(cs)[n];
121 alarm.timestamp.set(u64::MAX); 125 alarm.timestamp.set(u64::MAX);
@@ -138,38 +142,72 @@ pub unsafe fn init() {
138 }); 142 });
139 143
140 // enable all irqs 144 // enable all irqs
141 pac::TIMER.inte().write(|w| { 145 TIMER.inte().write(|w| {
142 w.set_alarm(0, true); 146 w.set_alarm(0, true);
143 w.set_alarm(1, true); 147 w.set_alarm(1, true);
144 w.set_alarm(2, true); 148 w.set_alarm(2, true);
145 w.set_alarm(3, true); 149 w.set_alarm(3, true);
146 }); 150 });
147 interrupt::TIMER_IRQ_0.enable(); 151 #[cfg(feature = "rp2040")]
148 interrupt::TIMER_IRQ_1.enable(); 152 {
149 interrupt::TIMER_IRQ_2.enable(); 153 interrupt::TIMER_IRQ_0.enable();
150 interrupt::TIMER_IRQ_3.enable(); 154 interrupt::TIMER_IRQ_1.enable();
155 interrupt::TIMER_IRQ_2.enable();
156 interrupt::TIMER_IRQ_3.enable();
157 }
158 #[cfg(feature = "_rp235x")]
159 {
160 interrupt::TIMER0_IRQ_0.enable();
161 interrupt::TIMER0_IRQ_1.enable();
162 interrupt::TIMER0_IRQ_2.enable();
163 interrupt::TIMER0_IRQ_3.enable();
164 }
151} 165}
152 166
153#[cfg(feature = "rt")] 167#[cfg(all(feature = "rt", feature = "rp2040"))]
154#[interrupt] 168#[interrupt]
155fn TIMER_IRQ_0() { 169fn TIMER_IRQ_0() {
156 DRIVER.check_alarm(0) 170 DRIVER.check_alarm(0)
157} 171}
158 172
159#[cfg(feature = "rt")] 173#[cfg(all(feature = "rt", feature = "rp2040"))]
160#[interrupt] 174#[interrupt]
161fn TIMER_IRQ_1() { 175fn TIMER_IRQ_1() {
162 DRIVER.check_alarm(1) 176 DRIVER.check_alarm(1)
163} 177}
164 178
165#[cfg(feature = "rt")] 179#[cfg(all(feature = "rt", feature = "rp2040"))]
166#[interrupt] 180#[interrupt]
167fn TIMER_IRQ_2() { 181fn TIMER_IRQ_2() {
168 DRIVER.check_alarm(2) 182 DRIVER.check_alarm(2)
169} 183}
170 184
171#[cfg(feature = "rt")] 185#[cfg(all(feature = "rt", feature = "rp2040"))]
172#[interrupt] 186#[interrupt]
173fn TIMER_IRQ_3() { 187fn TIMER_IRQ_3() {
174 DRIVER.check_alarm(3) 188 DRIVER.check_alarm(3)
175} 189}
190
191#[cfg(all(feature = "rt", feature = "_rp235x"))]
192#[interrupt]
193fn TIMER0_IRQ_0() {
194 DRIVER.check_alarm(0)
195}
196
197#[cfg(all(feature = "rt", feature = "_rp235x"))]
198#[interrupt]
199fn TIMER0_IRQ_1() {
200 DRIVER.check_alarm(1)
201}
202
203#[cfg(all(feature = "rt", feature = "_rp235x"))]
204#[interrupt]
205fn TIMER0_IRQ_2() {
206 DRIVER.check_alarm(2)
207}
208
209#[cfg(all(feature = "rt", feature = "_rp235x"))]
210#[interrupt]
211fn TIMER0_IRQ_3() {
212 DRIVER.check_alarm(3)
213}
diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs
index d50f5b4d5..aba4b792a 100644
--- a/embassy-rp/src/uart/mod.rs
+++ b/embassy-rp/src/uart/mod.rs
@@ -247,7 +247,7 @@ impl<'d, T: Instance> UartTx<'d, T, Async> {
247 }); 247 });
248 // If we don't assign future to a variable, the data register pointer 248 // If we don't assign future to a variable, the data register pointer
249 // is held across an await and makes the future non-Send. 249 // is held across an await and makes the future non-Send.
250 crate::dma::write(ch, buffer, T::regs().uartdr().as_ptr() as *mut _, T::TX_DREQ) 250 crate::dma::write(ch, buffer, T::regs().uartdr().as_ptr() as *mut _, T::TX_DREQ.into())
251 }; 251 };
252 transfer.await; 252 transfer.await;
253 Ok(()) 253 Ok(())
@@ -422,7 +422,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
422 let transfer = unsafe { 422 let transfer = unsafe {
423 // If we don't assign future to a variable, the data register pointer 423 // If we don't assign future to a variable, the data register pointer
424 // is held across an await and makes the future non-Send. 424 // is held across an await and makes the future non-Send.
425 crate::dma::read(ch, T::regs().uartdr().as_ptr() as *const _, buffer, T::RX_DREQ) 425 crate::dma::read(ch, T::regs().uartdr().as_ptr() as *const _, buffer, T::RX_DREQ.into())
426 }; 426 };
427 427
428 // wait for either the transfer to complete or an error to happen. 428 // wait for either the transfer to complete or an error to happen.
@@ -571,7 +571,12 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
571 let transfer = unsafe { 571 let transfer = unsafe {
572 // If we don't assign future to a variable, the data register pointer 572 // If we don't assign future to a variable, the data register pointer
573 // is held across an await and makes the future non-Send. 573 // is held across an await and makes the future non-Send.
574 crate::dma::read(&mut ch, T::regs().uartdr().as_ptr() as *const _, sbuffer, T::RX_DREQ) 574 crate::dma::read(
575 &mut ch,
576 T::regs().uartdr().as_ptr() as *const _,
577 sbuffer,
578 T::RX_DREQ.into(),
579 )
575 }; 580 };
576 581
577 // wait for either the transfer to complete or an error to happen. 582 // wait for either the transfer to complete or an error to happen.
@@ -830,26 +835,50 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
830 ) { 835 ) {
831 let r = T::regs(); 836 let r = T::regs();
832 if let Some(pin) = &tx { 837 if let Some(pin) = &tx {
838 let funcsel = {
839 let pin_number = ((pin.gpio().as_ptr() as u32) & 0x1FF) / 8;
840 if (pin_number % 4) == 0 {
841 2
842 } else {
843 11
844 }
845 };
833 pin.gpio().ctrl().write(|w| { 846 pin.gpio().ctrl().write(|w| {
834 w.set_funcsel(2); 847 w.set_funcsel(funcsel);
835 w.set_outover(if config.invert_tx { 848 w.set_outover(if config.invert_tx {
836 Outover::INVERT 849 Outover::INVERT
837 } else { 850 } else {
838 Outover::NORMAL 851 Outover::NORMAL
839 }); 852 });
840 }); 853 });
841 pin.pad_ctrl().write(|w| w.set_ie(true)); 854 pin.pad_ctrl().write(|w| {
855 #[cfg(feature = "_rp235x")]
856 w.set_iso(false);
857 w.set_ie(true);
858 });
842 } 859 }
843 if let Some(pin) = &rx { 860 if let Some(pin) = &rx {
861 let funcsel = {
862 let pin_number = ((pin.gpio().as_ptr() as u32) & 0x1FF) / 8;
863 if ((pin_number - 1) % 4) == 0 {
864 2
865 } else {
866 11
867 }
868 };
844 pin.gpio().ctrl().write(|w| { 869 pin.gpio().ctrl().write(|w| {
845 w.set_funcsel(2); 870 w.set_funcsel(funcsel);
846 w.set_inover(if config.invert_rx { 871 w.set_inover(if config.invert_rx {
847 Inover::INVERT 872 Inover::INVERT
848 } else { 873 } else {
849 Inover::NORMAL 874 Inover::NORMAL
850 }); 875 });
851 }); 876 });
852 pin.pad_ctrl().write(|w| w.set_ie(true)); 877 pin.pad_ctrl().write(|w| {
878 #[cfg(feature = "_rp235x")]
879 w.set_iso(false);
880 w.set_ie(true);
881 });
853 } 882 }
854 if let Some(pin) = &cts { 883 if let Some(pin) = &cts {
855 pin.gpio().ctrl().write(|w| { 884 pin.gpio().ctrl().write(|w| {
@@ -860,7 +889,11 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
860 Inover::NORMAL 889 Inover::NORMAL
861 }); 890 });
862 }); 891 });
863 pin.pad_ctrl().write(|w| w.set_ie(true)); 892 pin.pad_ctrl().write(|w| {
893 #[cfg(feature = "_rp235x")]
894 w.set_iso(false);
895 w.set_ie(true);
896 });
864 } 897 }
865 if let Some(pin) = &rts { 898 if let Some(pin) = &rts {
866 pin.gpio().ctrl().write(|w| { 899 pin.gpio().ctrl().write(|w| {
@@ -871,7 +904,11 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
871 Outover::NORMAL 904 Outover::NORMAL
872 }); 905 });
873 }); 906 });
874 pin.pad_ctrl().write(|w| w.set_ie(true)); 907 pin.pad_ctrl().write(|w| {
908 #[cfg(feature = "_rp235x")]
909 w.set_iso(false);
910 w.set_ie(true);
911 });
875 } 912 }
876 913
877 Self::set_baudrate_inner(config.baudrate); 914 Self::set_baudrate_inner(config.baudrate);
@@ -904,7 +941,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
904 }); 941 });
905 } 942 }
906 943
907 fn lcr_modify<R>(f: impl FnOnce(&mut rp_pac::uart::regs::UartlcrH) -> R) -> R { 944 fn lcr_modify<R>(f: impl FnOnce(&mut crate::pac::uart::regs::UartlcrH) -> R) -> R {
908 let r = T::regs(); 945 let r = T::regs();
909 946
910 // Notes from PL011 reference manual: 947 // Notes from PL011 reference manual:
@@ -1332,3 +1369,92 @@ impl_pin!(PIN_26, UART1, CtsPin);
1332impl_pin!(PIN_27, UART1, RtsPin); 1369impl_pin!(PIN_27, UART1, RtsPin);
1333impl_pin!(PIN_28, UART0, TxPin); 1370impl_pin!(PIN_28, UART0, TxPin);
1334impl_pin!(PIN_29, UART0, RxPin); 1371impl_pin!(PIN_29, UART0, RxPin);
1372
1373// Additional functions added by all 2350s
1374#[cfg(feature = "_rp235x")]
1375impl_pin!(PIN_2, UART0, TxPin);
1376#[cfg(feature = "_rp235x")]
1377impl_pin!(PIN_3, UART0, RxPin);
1378#[cfg(feature = "_rp235x")]
1379impl_pin!(PIN_6, UART1, TxPin);
1380#[cfg(feature = "_rp235x")]
1381impl_pin!(PIN_7, UART1, RxPin);
1382#[cfg(feature = "_rp235x")]
1383impl_pin!(PIN_10, UART1, TxPin);
1384#[cfg(feature = "_rp235x")]
1385impl_pin!(PIN_11, UART1, RxPin);
1386#[cfg(feature = "_rp235x")]
1387impl_pin!(PIN_14, UART0, TxPin);
1388#[cfg(feature = "_rp235x")]
1389impl_pin!(PIN_15, UART0, RxPin);
1390#[cfg(feature = "_rp235x")]
1391impl_pin!(PIN_18, UART0, TxPin);
1392#[cfg(feature = "_rp235x")]
1393impl_pin!(PIN_19, UART0, RxPin);
1394#[cfg(feature = "_rp235x")]
1395impl_pin!(PIN_22, UART1, TxPin);
1396#[cfg(feature = "_rp235x")]
1397impl_pin!(PIN_23, UART1, RxPin);
1398#[cfg(feature = "_rp235x")]
1399impl_pin!(PIN_26, UART1, TxPin);
1400#[cfg(feature = "_rp235x")]
1401impl_pin!(PIN_27, UART1, RxPin);
1402
1403// Additional pins added by larger 2350 packages.
1404#[cfg(feature = "rp235xb")]
1405impl_pin!(PIN_30, UART0, CtsPin);
1406#[cfg(feature = "rp235xb")]
1407impl_pin!(PIN_31, UART0, RtsPin);
1408#[cfg(feature = "rp235xb")]
1409impl_pin!(PIN_32, UART0, TxPin);
1410#[cfg(feature = "rp235xb")]
1411impl_pin!(PIN_33, UART0, RxPin);
1412#[cfg(feature = "rp235xb")]
1413impl_pin!(PIN_34, UART0, CtsPin);
1414#[cfg(feature = "rp235xb")]
1415impl_pin!(PIN_35, UART0, RtsPin);
1416#[cfg(feature = "rp235xb")]
1417impl_pin!(PIN_36, UART1, TxPin);
1418#[cfg(feature = "rp235xb")]
1419impl_pin!(PIN_37, UART1, RxPin);
1420#[cfg(feature = "rp235xb")]
1421impl_pin!(PIN_38, UART1, CtsPin);
1422#[cfg(feature = "rp235xb")]
1423impl_pin!(PIN_39, UART1, RtsPin);
1424#[cfg(feature = "rp235xb")]
1425impl_pin!(PIN_40, UART1, TxPin);
1426#[cfg(feature = "rp235xb")]
1427impl_pin!(PIN_41, UART1, RxPin);
1428#[cfg(feature = "rp235xb")]
1429impl_pin!(PIN_42, UART1, CtsPin);
1430#[cfg(feature = "rp235xb")]
1431impl_pin!(PIN_43, UART1, RtsPin);
1432#[cfg(feature = "rp235xb")]
1433impl_pin!(PIN_44, UART0, TxPin);
1434#[cfg(feature = "rp235xb")]
1435impl_pin!(PIN_45, UART0, RxPin);
1436#[cfg(feature = "rp235xb")]
1437impl_pin!(PIN_46, UART0, CtsPin);
1438#[cfg(feature = "rp235xb")]
1439impl_pin!(PIN_47, UART0, RtsPin);
1440
1441#[cfg(feature = "rp235xb")]
1442impl_pin!(PIN_30, UART0, TxPin);
1443#[cfg(feature = "rp235xb")]
1444impl_pin!(PIN_31, UART0, RxPin);
1445#[cfg(feature = "rp235xb")]
1446impl_pin!(PIN_34, UART0, TxPin);
1447#[cfg(feature = "rp235xb")]
1448impl_pin!(PIN_35, UART0, RxPin);
1449#[cfg(feature = "rp235xb")]
1450impl_pin!(PIN_38, UART1, TxPin);
1451#[cfg(feature = "rp235xb")]
1452impl_pin!(PIN_39, UART1, RxPin);
1453#[cfg(feature = "rp235xb")]
1454impl_pin!(PIN_42, UART1, TxPin);
1455#[cfg(feature = "rp235xb")]
1456impl_pin!(PIN_43, UART1, RxPin);
1457#[cfg(feature = "rp235xb")]
1458impl_pin!(PIN_46, UART0, TxPin);
1459#[cfg(feature = "rp235xb")]
1460impl_pin!(PIN_47, UART0, RxPin);
diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs
index 512271ae4..20ef881f9 100644
--- a/embassy-rp/src/usb.rs
+++ b/embassy-rp/src/usb.rs
@@ -28,10 +28,10 @@ pub trait Instance: SealedInstance + 'static {
28 28
29impl crate::usb::SealedInstance for peripherals::USB { 29impl crate::usb::SealedInstance for peripherals::USB {
30 fn regs() -> pac::usb::Usb { 30 fn regs() -> pac::usb::Usb {
31 pac::USBCTRL_REGS 31 pac::USB
32 } 32 }
33 fn dpram() -> crate::pac::usb_dpram::UsbDpram { 33 fn dpram() -> crate::pac::usb_dpram::UsbDpram {
34 pac::USBCTRL_DPRAM 34 pac::USB_DPRAM
35 } 35 }
36} 36}
37 37
@@ -41,7 +41,7 @@ impl crate::usb::Instance for peripherals::USB {
41 41
42const EP_COUNT: usize = 16; 42const EP_COUNT: usize = 16;
43const EP_MEMORY_SIZE: usize = 4096; 43const EP_MEMORY_SIZE: usize = 4096;
44const EP_MEMORY: *mut u8 = pac::USBCTRL_DPRAM.as_ptr() as *mut u8; 44const EP_MEMORY: *mut u8 = pac::USB_DPRAM.as_ptr() as *mut u8;
45 45
46const NEW_AW: AtomicWaker = AtomicWaker::new(); 46const NEW_AW: AtomicWaker = AtomicWaker::new();
47static BUS_WAKER: AtomicWaker = NEW_AW; 47static BUS_WAKER: AtomicWaker = NEW_AW;
diff --git a/embassy-rp/src/watchdog.rs b/embassy-rp/src/watchdog.rs
index 229a306fe..edd48e0e0 100644
--- a/embassy-rp/src/watchdog.rs
+++ b/embassy-rp/src/watchdog.rs
@@ -34,6 +34,7 @@ impl Watchdog {
34 /// 34 ///
35 /// * `cycles` - Total number of tick cycles before the next tick is generated. 35 /// * `cycles` - Total number of tick cycles before the next tick is generated.
36 /// It is expected to be the frequency in MHz of clk_ref. 36 /// It is expected to be the frequency in MHz of clk_ref.
37 #[cfg(feature = "rp2040")]
37 pub fn enable_tick_generation(&mut self, cycles: u8) { 38 pub fn enable_tick_generation(&mut self, cycles: u8) {
38 let watchdog = pac::WATCHDOG; 39 let watchdog = pac::WATCHDOG;
39 watchdog.tick().write(|w| { 40 watchdog.tick().write(|w| {
diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml
index c109c0732..8bb8afdfe 100644
--- a/examples/boot/application/rp/Cargo.toml
+++ b/examples/boot/application/rp/Cargo.toml
@@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
8embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } 9embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] } 10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] }
11embassy-rp = { version = "0.2.0", path = "../../../../embassy-rp", features = ["time-driver", ] } 11embassy-rp = { version = "0.2.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] }
12embassy-boot-rp = { version = "0.3.0", path = "../../../../embassy-boot-rp", features = [] } 12embassy-boot-rp = { version = "0.3.0", path = "../../../../embassy-boot-rp", features = [] }
13embassy-embedded-hal = { version = "0.2.0", path = "../../../../embassy-embedded-hal" } 13embassy-embedded-hal = { version = "0.2.0", path = "../../../../embassy-embedded-hal" }
14 14
diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml
index c15c980ca..9df396e5e 100644
--- a/examples/boot/bootloader/rp/Cargo.toml
+++ b/examples/boot/bootloader/rp/Cargo.toml
@@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
9defmt = { version = "0.3", optional = true } 9defmt = { version = "0.3", optional = true }
10defmt-rtt = { version = "0.4", optional = true } 10defmt-rtt = { version = "0.4", optional = true }
11 11
12embassy-rp = { path = "../../../../embassy-rp", features = [] } 12embassy-rp = { path = "../../../../embassy-rp", features = ["rp2040"] }
13embassy-boot-rp = { path = "../../../../embassy-boot-rp" } 13embassy-boot-rp = { path = "../../../../embassy-boot-rp" }
14embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } 14embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" }
15embassy-time = { path = "../../../../embassy-time", features = [] } 15embassy-time = { path = "../../../../embassy-time", features = [] }
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index 031f68253..83d5792b6 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -10,7 +10,7 @@ embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal",
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
13embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl"] } 13embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] }
14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } 15embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] }
16embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } 16embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
@@ -51,7 +51,7 @@ embedded-hal-async = "1.0"
51embedded-hal-bus = { version = "0.1", features = ["async"] } 51embedded-hal-bus = { version = "0.1", features = ["async"] }
52embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } 52embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
53embedded-storage = { version = "0.3" } 53embedded-storage = { version = "0.3" }
54static_cell = "2" 54static_cell = "2.1"
55portable-atomic = { version = "1.5", features = ["critical-section"] } 55portable-atomic = { version = "1.5", features = ["critical-section"] }
56log = "0.4" 56log = "0.4"
57pio-proc = "0.2" 57pio-proc = "0.2"
diff --git a/examples/rp23/.cargo/config.toml b/examples/rp23/.cargo/config.toml
new file mode 100644
index 000000000..f77e004dc
--- /dev/null
+++ b/examples/rp23/.cargo/config.toml
@@ -0,0 +1,9 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2#runner = "probe-rs run --chip RP2040"
3runner = "elf2uf2-rs -d"
4
5[build]
6target = "thumbv8m.main-none-eabihf"
7
8[env]
9DEFMT_LOG = "debug"
diff --git a/examples/rp23/Cargo.toml b/examples/rp23/Cargo.toml
new file mode 100644
index 000000000..8f8d6ff10
--- /dev/null
+++ b/examples/rp23/Cargo.toml
@@ -0,0 +1,80 @@
1[package]
2edition = "2021"
3name = "embassy-rp2350-examples"
4version = "0.1.0"
5license = "MIT OR Apache-2.0"
6
7
8[dependencies]
9embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
13embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] }
14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] }
16embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
17embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
18embassy-usb-logger = { version = "0.2.0", path = "../../embassy-usb-logger" }
19cyw43 = { version = "0.2.0", path = "../../cyw43", features = ["defmt", "firmware-logs", "bluetooth"] }
20cyw43-pio = { version = "0.2.0", path = "../../cyw43-pio", features = ["defmt"] }
21
22defmt = "0.3"
23defmt-rtt = "0.4"
24fixed = "1.23.1"
25fixed-macro = "1.2"
26
27# for web request example
28reqwless = { version = "0.12.0", features = ["defmt",]}
29serde = { version = "1.0.203", default-features = false, features = ["derive"] }
30serde-json-core = "0.5.1"
31
32# for assign resources example
33assign-resources = { git = "https://github.com/adamgreig/assign-resources", rev = "94ad10e2729afdf0fd5a77cd12e68409a982f58a" }
34
35#cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
36cortex-m = { version = "0.7.6", features = ["inline-asm"] }
37cortex-m-rt = "0.7.0"
38critical-section = "1.1"
39panic-probe = { version = "0.3", features = ["print-defmt"] }
40display-interface-spi = "0.4.1"
41embedded-graphics = "0.7.1"
42st7789 = "0.6.1"
43display-interface = "0.4.1"
44byte-slice-cast = { version = "1.2.0", default-features = false }
45smart-leds = "0.3.0"
46heapless = "0.8"
47usbd-hid = "0.8.1"
48
49embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
50embedded-hal-async = "1.0"
51embedded-hal-bus = { version = "0.1", features = ["async"] }
52embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
53embedded-storage = { version = "0.3" }
54static_cell = "2.1"
55portable-atomic = { version = "1.5", features = ["critical-section"] }
56log = "0.4"
57pio-proc = "0.2"
58pio = "0.2.1"
59rand = { version = "0.8.5", default-features = false }
60embedded-sdmmc = "0.7.0"
61
62bt-hci = { version = "0.1.0", default-features = false, features = ["defmt"] }
63trouble-host = { version = "0.1.0", features = ["defmt", "gatt"] }
64
65[profile.release]
66debug = 2
67
68[profile.dev]
69lto = true
70opt-level = "z"
71
72[patch.crates-io]
73trouble-host = { git = "https://github.com/embassy-rs/trouble.git", rev = "4b8c0f499b34e46ca23a56e2d1640ede371722cf" }
74bt-hci = { git = "https://github.com/alexmoon/bt-hci.git", rev = "b9cd5954f6bd89b535cad9c418e9fdf12812d7c3" }
75embassy-executor = { path = "../../embassy-executor" }
76embassy-sync = { path = "../../embassy-sync" }
77embassy-futures = { path = "../../embassy-futures" }
78embassy-time = { path = "../../embassy-time" }
79embassy-time-driver = { path = "../../embassy-time-driver" }
80embassy-embedded-hal = { path = "../../embassy-embedded-hal" }
diff --git a/examples/rp23/assets/ferris.raw b/examples/rp23/assets/ferris.raw
new file mode 100644
index 000000000..9733889c5
--- /dev/null
+++ b/examples/rp23/assets/ferris.raw
Binary files differ
diff --git a/examples/rp23/build.rs b/examples/rp23/build.rs
new file mode 100644
index 000000000..30691aa97
--- /dev/null
+++ b/examples/rp23/build.rs
@@ -0,0 +1,35 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
35}
diff --git a/examples/rp23/memory.x b/examples/rp23/memory.x
new file mode 100644
index 000000000..777492062
--- /dev/null
+++ b/examples/rp23/memory.x
@@ -0,0 +1,74 @@
1MEMORY {
2 /*
3 * The RP2350 has either external or internal flash.
4 *
5 * 2 MiB is a safe default here, although a Pico 2 has 4 MiB.
6 */
7 FLASH : ORIGIN = 0x10000000, LENGTH = 2048K
8 /*
9 * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping.
10 * This is usually good for performance, as it distributes load on
11 * those banks evenly.
12 */
13 RAM : ORIGIN = 0x20000000, LENGTH = 512K
14 /*
15 * RAM banks 8 and 9 use a direct mapping. They can be used to have
16 * memory areas dedicated for some specific job, improving predictability
17 * of access times.
18 * Example: Separate stacks for core0 and core1.
19 */
20 SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K
21 SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K
22}
23
24SECTIONS {
25 /* ### Boot ROM info
26 *
27 * Goes after .vector_table, to keep it in the first 4K of flash
28 * where the Boot ROM (and picotool) can find it
29 */
30 .start_block : ALIGN(4)
31 {
32 __start_block_addr = .;
33 KEEP(*(.start_block));
34 } > FLASH
35
36} INSERT AFTER .vector_table;
37
38/* move .text to start /after/ the boot info */
39_stext = ADDR(.start_block) + SIZEOF(.start_block);
40
41SECTIONS {
42 /* ### Picotool 'Binary Info' Entries
43 *
44 * Picotool looks through this block (as we have pointers to it in our
45 * header) to find interesting information.
46 */
47 .bi_entries : ALIGN(4)
48 {
49 /* We put this in the header */
50 __bi_entries_start = .;
51 /* Here are the entries */
52 KEEP(*(.bi_entries));
53 /* Keep this block a nice round size */
54 . = ALIGN(4);
55 /* We put this in the header */
56 __bi_entries_end = .;
57 } > FLASH
58} INSERT AFTER .text;
59
60SECTIONS {
61 /* ### Boot ROM extra info
62 *
63 * Goes after everything in our program, so it can contain a signature.
64 */
65 .end_block : ALIGN(4)
66 {
67 __end_block_addr = .;
68 KEEP(*(.end_block));
69 } > FLASH
70
71} INSERT AFTER .uninit;
72
73PROVIDE(start_to_end = __end_block_addr - __start_block_addr);
74PROVIDE(end_to_start = __start_block_addr - __end_block_addr);
diff --git a/examples/rp23/src/bin/adc.rs b/examples/rp23/src/bin/adc.rs
new file mode 100644
index 000000000..19872607e
--- /dev/null
+++ b/examples/rp23/src/bin/adc.rs
@@ -0,0 +1,63 @@
1//! This example test the ADC (Analog to Digital Conversion) of the RS2040 pin 26, 27 and 28.
2//! It also reads the temperature sensor in the chip.
3
4#![no_std]
5#![no_main]
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler};
10use embassy_rp::bind_interrupts;
11use embassy_rp::block::ImageDef;
12use embassy_rp::gpio::Pull;
13use embassy_time::Timer;
14use {defmt_rtt as _, panic_probe as _};
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30bind_interrupts!(struct Irqs {
31 ADC_IRQ_FIFO => InterruptHandler;
32});
33
34#[embassy_executor::main]
35async fn main(_spawner: Spawner) {
36 let p = embassy_rp::init(Default::default());
37 let mut adc = Adc::new(p.ADC, Irqs, Config::default());
38
39 let mut p26 = Channel::new_pin(p.PIN_26, Pull::None);
40 let mut p27 = Channel::new_pin(p.PIN_27, Pull::None);
41 let mut p28 = Channel::new_pin(p.PIN_28, Pull::None);
42 let mut ts = Channel::new_temp_sensor(p.ADC_TEMP_SENSOR);
43
44 loop {
45 let level = adc.read(&mut p26).await.unwrap();
46 info!("Pin 26 ADC: {}", level);
47 let level = adc.read(&mut p27).await.unwrap();
48 info!("Pin 27 ADC: {}", level);
49 let level = adc.read(&mut p28).await.unwrap();
50 info!("Pin 28 ADC: {}", level);
51 let temp = adc.read(&mut ts).await.unwrap();
52 info!("Temp: {} degrees", convert_to_celsius(temp));
53 Timer::after_secs(1).await;
54 }
55}
56
57fn convert_to_celsius(raw_temp: u16) -> f32 {
58 // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet
59 let temp = 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721;
60 let sign = if temp < 0.0 { -1.0 } else { 1.0 };
61 let rounded_temp_x10: i16 = ((temp * 10.0) + 0.5 * sign) as i16;
62 (rounded_temp_x10 as f32) / 10.0
63}
diff --git a/examples/rp23/src/bin/adc_dma.rs b/examples/rp23/src/bin/adc_dma.rs
new file mode 100644
index 000000000..d538ddaa2
--- /dev/null
+++ b/examples/rp23/src/bin/adc_dma.rs
@@ -0,0 +1,69 @@
1//! This example shows how to use the RP2040 ADC with DMA, both single- and multichannel reads.
2//! For multichannel, the samples are interleaved in the buffer:
3//! `[ch1, ch2, ch3, ch4, ch1, ch2, ch3, ch4, ...]`
4#![no_std]
5#![no_main]
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler};
10use embassy_rp::bind_interrupts;
11use embassy_rp::block::ImageDef;
12use embassy_rp::gpio::Pull;
13use embassy_time::{Duration, Ticker};
14use {defmt_rtt as _, panic_probe as _};
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30bind_interrupts!(struct Irqs {
31 ADC_IRQ_FIFO => InterruptHandler;
32});
33
34#[embassy_executor::main]
35async fn main(_spawner: Spawner) {
36 let p = embassy_rp::init(Default::default());
37 info!("Here we go!");
38
39 let mut adc = Adc::new(p.ADC, Irqs, Config::default());
40 let mut dma = p.DMA_CH0;
41 let mut pin = Channel::new_pin(p.PIN_26, Pull::Up);
42 let mut pins = [
43 Channel::new_pin(p.PIN_27, Pull::Down),
44 Channel::new_pin(p.PIN_28, Pull::None),
45 Channel::new_pin(p.PIN_29, Pull::Up),
46 Channel::new_temp_sensor(p.ADC_TEMP_SENSOR),
47 ];
48
49 const BLOCK_SIZE: usize = 100;
50 const NUM_CHANNELS: usize = 4;
51 let mut ticker = Ticker::every(Duration::from_secs(1));
52 loop {
53 // Read 100 samples from a single channel
54 let mut buf = [0_u16; BLOCK_SIZE];
55 let div = 479; // 100kHz sample rate (48Mhz / 100kHz - 1)
56 adc.read_many(&mut pin, &mut buf, div, &mut dma).await.unwrap();
57 info!("single: {:?} ...etc", buf[..8]);
58
59 // Read 100 samples from 4 channels interleaved
60 let mut buf = [0_u16; { BLOCK_SIZE * NUM_CHANNELS }];
61 let div = 119; // 100kHz sample rate (48Mhz / 100kHz * 4ch - 1)
62 adc.read_many_multichannel(&mut pins, &mut buf, div, &mut dma)
63 .await
64 .unwrap();
65 info!("multi: {:?} ...etc", buf[..NUM_CHANNELS * 2]);
66
67 ticker.next().await;
68 }
69}
diff --git a/examples/rp23/src/bin/assign_resources.rs b/examples/rp23/src/bin/assign_resources.rs
new file mode 100644
index 000000000..923c13514
--- /dev/null
+++ b/examples/rp23/src/bin/assign_resources.rs
@@ -0,0 +1,94 @@
1//! This example demonstrates how to assign resources to multiple tasks by splitting up the peripherals.
2//! It is not about sharing the same resources between tasks, see sharing.rs for that or head to https://embassy.dev/book/#_sharing_peripherals_between_tasks)
3//! Of course splitting up resources and sharing resources can be combined, yet this example is only about splitting up resources.
4//!
5//! There are basically two ways we demonstrate here:
6//! 1) Assigning resources to a task by passing parts of the peripherals
7//! 2) Assigning resources to a task by passing a struct with the split up peripherals, using the assign-resources macro
8//!
9//! using four LEDs on Pins 10, 11, 20 and 21
10
11#![no_std]
12#![no_main]
13
14use assign_resources::assign_resources;
15use defmt::*;
16use embassy_executor::Spawner;
17use embassy_rp::block::ImageDef;
18use embassy_rp::gpio::{Level, Output};
19use embassy_rp::peripherals::{self, PIN_20, PIN_21};
20use embassy_time::Timer;
21use {defmt_rtt as _, panic_probe as _};
22
23#[link_section = ".start_block"]
24#[used]
25pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
26
27// Program metadata for `picotool info`
28#[link_section = ".bi_entries"]
29#[used]
30pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
31 embassy_rp::binary_info_rp_cargo_bin_name!(),
32 embassy_rp::binary_info_rp_cargo_version!(),
33 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
34 embassy_rp::binary_info_rp_program_build_attribute!(),
35];
36
37#[embassy_executor::main]
38async fn main(spawner: Spawner) {
39 // initialize the peripherals
40 let p = embassy_rp::init(Default::default());
41
42 // 1) Assigning a resource to a task by passing parts of the peripherals.
43 spawner
44 .spawn(double_blinky_manually_assigned(spawner, p.PIN_20, p.PIN_21))
45 .unwrap();
46
47 // 2) Using the assign-resources macro to assign resources to a task.
48 // we perform the split, see further below for the definition of the resources struct
49 let r = split_resources!(p);
50 // and then we can use them
51 spawner.spawn(double_blinky_macro_assigned(spawner, r.leds)).unwrap();
52}
53
54// 1) Assigning a resource to a task by passing parts of the peripherals.
55#[embassy_executor::task]
56async fn double_blinky_manually_assigned(_spawner: Spawner, pin_20: PIN_20, pin_21: PIN_21) {
57 let mut led_20 = Output::new(pin_20, Level::Low);
58 let mut led_21 = Output::new(pin_21, Level::High);
59
60 loop {
61 info!("toggling leds");
62 led_20.toggle();
63 led_21.toggle();
64 Timer::after_secs(1).await;
65 }
66}
67
68// 2) Using the assign-resources macro to assign resources to a task.
69// first we define the resources we want to assign to the task using the assign_resources! macro
70// basically this will split up the peripherals struct into smaller structs, that we define here
71// naming is up to you, make sure your future self understands what you did here
72assign_resources! {
73 leds: Leds{
74 led_10: PIN_10,
75 led_11: PIN_11,
76 }
77 // add more resources to more structs if needed, for example defining one struct for each task
78}
79// this could be done in another file and imported here, but for the sake of simplicity we do it here
80// see https://github.com/adamgreig/assign-resources for more information
81
82// 2) Using the split resources in a task
83#[embassy_executor::task]
84async fn double_blinky_macro_assigned(_spawner: Spawner, r: Leds) {
85 let mut led_10 = Output::new(r.led_10, Level::Low);
86 let mut led_11 = Output::new(r.led_11, Level::High);
87
88 loop {
89 info!("toggling leds");
90 led_10.toggle();
91 led_11.toggle();
92 Timer::after_secs(1).await;
93 }
94}
diff --git a/examples/rp23/src/bin/blinky.rs b/examples/rp23/src/bin/blinky.rs
new file mode 100644
index 000000000..02bdf9b3d
--- /dev/null
+++ b/examples/rp23/src/bin/blinky.rs
@@ -0,0 +1,44 @@
1//! This example test the RP Pico on board LED.
2//!
3//! It does not work with the RP Pico W board. See wifi_blinky.rs.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::gpio;
12use embassy_time::Timer;
13use gpio::{Level, Output};
14use {defmt_rtt as _, panic_probe as _};
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30#[embassy_executor::main]
31async fn main(_spawner: Spawner) {
32 let p = embassy_rp::init(Default::default());
33 let mut led = Output::new(p.PIN_2, Level::Low);
34
35 loop {
36 info!("led on!");
37 led.set_high();
38 Timer::after_millis(250).await;
39
40 info!("led off!");
41 led.set_low();
42 Timer::after_millis(250).await;
43 }
44}
diff --git a/examples/rp23/src/bin/blinky_two_channels.rs b/examples/rp23/src/bin/blinky_two_channels.rs
new file mode 100644
index 000000000..4d7dc89fa
--- /dev/null
+++ b/examples/rp23/src/bin/blinky_two_channels.rs
@@ -0,0 +1,65 @@
1#![no_std]
2#![no_main]
3/// This example demonstrates how to access a given pin from more than one embassy task
4/// The on-board LED is toggled by two tasks with slightly different periods, leading to the
5/// apparent duty cycle of the LED increasing, then decreasing, linearly. The phenomenon is similar
6/// to interference and the 'beats' you can hear if you play two frequencies close to one another
7/// [Link explaining it](https://www.physicsclassroom.com/class/sound/Lesson-3/Interference-and-Beats)
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::gpio;
12use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
13use embassy_sync::channel::{Channel, Sender};
14use embassy_time::{Duration, Ticker};
15use gpio::{AnyPin, Level, Output};
16use {defmt_rtt as _, panic_probe as _};
17
18#[link_section = ".start_block"]
19#[used]
20pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
21
22// Program metadata for `picotool info`
23#[link_section = ".bi_entries"]
24#[used]
25pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
26 embassy_rp::binary_info_rp_cargo_bin_name!(),
27 embassy_rp::binary_info_rp_cargo_version!(),
28 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
29 embassy_rp::binary_info_rp_program_build_attribute!(),
30];
31
32enum LedState {
33 Toggle,
34}
35static CHANNEL: Channel<ThreadModeRawMutex, LedState, 64> = Channel::new();
36
37#[embassy_executor::main]
38async fn main(spawner: Spawner) {
39 let p = embassy_rp::init(Default::default());
40 let mut led = Output::new(AnyPin::from(p.PIN_25), Level::High);
41
42 let dt = 100 * 1_000_000;
43 let k = 1.003;
44
45 unwrap!(spawner.spawn(toggle_led(CHANNEL.sender(), Duration::from_nanos(dt))));
46 unwrap!(spawner.spawn(toggle_led(
47 CHANNEL.sender(),
48 Duration::from_nanos((dt as f64 * k) as u64)
49 )));
50
51 loop {
52 match CHANNEL.receive().await {
53 LedState::Toggle => led.toggle(),
54 }
55 }
56}
57
58#[embassy_executor::task(pool_size = 2)]
59async fn toggle_led(control: Sender<'static, ThreadModeRawMutex, LedState, 64>, delay: Duration) {
60 let mut ticker = Ticker::every(delay);
61 loop {
62 control.send(LedState::Toggle).await;
63 ticker.next().await;
64 }
65}
diff --git a/examples/rp23/src/bin/blinky_two_tasks.rs b/examples/rp23/src/bin/blinky_two_tasks.rs
new file mode 100644
index 000000000..24b960242
--- /dev/null
+++ b/examples/rp23/src/bin/blinky_two_tasks.rs
@@ -0,0 +1,64 @@
1#![no_std]
2#![no_main]
3/// This example demonstrates how to access a given pin from more than one embassy task
4/// The on-board LED is toggled by two tasks with slightly different periods, leading to the
5/// apparent duty cycle of the LED increasing, then decreasing, linearly. The phenomenon is similar
6/// to interference and the 'beats' you can hear if you play two frequencies close to one another
7/// [Link explaining it](https://www.physicsclassroom.com/class/sound/Lesson-3/Interference-and-Beats)
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::gpio;
12use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
13use embassy_sync::mutex::Mutex;
14use embassy_time::{Duration, Ticker};
15use gpio::{AnyPin, Level, Output};
16use {defmt_rtt as _, panic_probe as _};
17
18#[link_section = ".start_block"]
19#[used]
20pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
21
22// Program metadata for `picotool info`
23#[link_section = ".bi_entries"]
24#[used]
25pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
26 embassy_rp::binary_info_rp_cargo_bin_name!(),
27 embassy_rp::binary_info_rp_cargo_version!(),
28 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
29 embassy_rp::binary_info_rp_program_build_attribute!(),
30];
31
32type LedType = Mutex<ThreadModeRawMutex, Option<Output<'static>>>;
33static LED: LedType = Mutex::new(None);
34
35#[embassy_executor::main]
36async fn main(spawner: Spawner) {
37 let p = embassy_rp::init(Default::default());
38 // set the content of the global LED reference to the real LED pin
39 let led = Output::new(AnyPin::from(p.PIN_25), Level::High);
40 // inner scope is so that once the mutex is written to, the MutexGuard is dropped, thus the
41 // Mutex is released
42 {
43 *(LED.lock().await) = Some(led);
44 }
45 let dt = 100 * 1_000_000;
46 let k = 1.003;
47
48 unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos(dt))));
49 unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos((dt as f64 * k) as u64))));
50}
51
52#[embassy_executor::task(pool_size = 2)]
53async fn toggle_led(led: &'static LedType, delay: Duration) {
54 let mut ticker = Ticker::every(delay);
55 loop {
56 {
57 let mut led_unlocked = led.lock().await;
58 if let Some(pin_ref) = led_unlocked.as_mut() {
59 pin_ref.toggle();
60 }
61 }
62 ticker.next().await;
63 }
64}
diff --git a/examples/rp23/src/bin/button.rs b/examples/rp23/src/bin/button.rs
new file mode 100644
index 000000000..0a0559397
--- /dev/null
+++ b/examples/rp23/src/bin/button.rs
@@ -0,0 +1,43 @@
1//! This example uses the RP Pico on board LED to test input pin 28. This is not the button on the board.
2//!
3//! It does not work with the RP Pico W board. Use wifi_blinky.rs and add input pin.
4
5#![no_std]
6#![no_main]
7
8use embassy_executor::Spawner;
9use embassy_rp::block::ImageDef;
10use embassy_rp::gpio::{Input, Level, Output, Pull};
11use {defmt_rtt as _, panic_probe as _};
12
13#[link_section = ".start_block"]
14#[used]
15pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
16
17// Program metadata for `picotool info`
18#[link_section = ".bi_entries"]
19#[used]
20pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
21 embassy_rp::binary_info_rp_cargo_bin_name!(),
22 embassy_rp::binary_info_rp_cargo_version!(),
23 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
24 embassy_rp::binary_info_rp_program_build_attribute!(),
25];
26
27#[embassy_executor::main]
28async fn main(_spawner: Spawner) {
29 let p = embassy_rp::init(Default::default());
30 let mut led = Output::new(p.PIN_25, Level::Low);
31
32 // Use PIN_28, Pin34 on J0 for RP Pico, as a input.
33 // You need to add your own button.
34 let button = Input::new(p.PIN_28, Pull::Up);
35
36 loop {
37 if button.is_high() {
38 led.set_high();
39 } else {
40 led.set_low();
41 }
42 }
43}
diff --git a/examples/rp23/src/bin/debounce.rs b/examples/rp23/src/bin/debounce.rs
new file mode 100644
index 000000000..e82e71f61
--- /dev/null
+++ b/examples/rp23/src/bin/debounce.rs
@@ -0,0 +1,95 @@
1//! This example shows the ease of debouncing a button with async rust.
2//! Hook up a button or switch between pin 9 and ground.
3
4#![no_std]
5#![no_main]
6
7use defmt::info;
8use embassy_executor::Spawner;
9use embassy_rp::block::ImageDef;
10use embassy_rp::gpio::{Input, Level, Pull};
11use embassy_time::{with_deadline, Duration, Instant, Timer};
12use {defmt_rtt as _, panic_probe as _};
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28pub struct Debouncer<'a> {
29 input: Input<'a>,
30 debounce: Duration,
31}
32
33impl<'a> Debouncer<'a> {
34 pub fn new(input: Input<'a>, debounce: Duration) -> Self {
35 Self { input, debounce }
36 }
37
38 pub async fn debounce(&mut self) -> Level {
39 loop {
40 let l1 = self.input.get_level();
41
42 self.input.wait_for_any_edge().await;
43
44 Timer::after(self.debounce).await;
45
46 let l2 = self.input.get_level();
47 if l1 != l2 {
48 break l2;
49 }
50 }
51 }
52}
53
54#[embassy_executor::main]
55async fn main(_spawner: Spawner) {
56 let p = embassy_rp::init(Default::default());
57 let mut btn = Debouncer::new(Input::new(p.PIN_9, Pull::Up), Duration::from_millis(20));
58
59 info!("Debounce Demo");
60
61 loop {
62 // button pressed
63 btn.debounce().await;
64 let start = Instant::now();
65 info!("Button Press");
66
67 match with_deadline(start + Duration::from_secs(1), btn.debounce()).await {
68 // Button Released < 1s
69 Ok(_) => {
70 info!("Button pressed for: {}ms", start.elapsed().as_millis());
71 continue;
72 }
73 // button held for > 1s
74 Err(_) => {
75 info!("Button Held");
76 }
77 }
78
79 match with_deadline(start + Duration::from_secs(5), btn.debounce()).await {
80 // Button released <5s
81 Ok(_) => {
82 info!("Button pressed for: {}ms", start.elapsed().as_millis());
83 continue;
84 }
85 // button held for > >5s
86 Err(_) => {
87 info!("Button Long Held");
88 }
89 }
90
91 // wait for button release before handling another press
92 btn.debounce().await;
93 info!("Button pressed for: {}ms", start.elapsed().as_millis());
94 }
95}
diff --git a/examples/rp23/src/bin/flash.rs b/examples/rp23/src/bin/flash.rs
new file mode 100644
index 000000000..2917dda0b
--- /dev/null
+++ b/examples/rp23/src/bin/flash.rs
@@ -0,0 +1,149 @@
1//! This example test the flash connected to the RP2040 chip.
2
3#![no_std]
4#![no_main]
5
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_rp::block::ImageDef;
9use embassy_rp::flash::{Async, ERASE_SIZE, FLASH_BASE};
10use embassy_rp::peripherals::FLASH;
11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _};
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28const ADDR_OFFSET: u32 = 0x100000;
29const FLASH_SIZE: usize = 2 * 1024 * 1024;
30
31#[embassy_executor::main]
32async fn main(_spawner: Spawner) {
33 let p = embassy_rp::init(Default::default());
34 info!("Hello World!");
35
36 // add some delay to give an attached debug probe time to parse the
37 // defmt RTT header. Reading that header might touch flash memory, which
38 // interferes with flash write operations.
39 // https://github.com/knurling-rs/defmt/pull/683
40 Timer::after_millis(10).await;
41
42 let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0);
43
44 // Get JEDEC id
45 let jedec = flash.blocking_jedec_id().unwrap();
46 info!("jedec id: 0x{:x}", jedec);
47
48 // Get unique id
49 let mut uid = [0; 8];
50 flash.blocking_unique_id(&mut uid).unwrap();
51 info!("unique id: {:?}", uid);
52
53 erase_write_sector(&mut flash, 0x00);
54
55 multiwrite_bytes(&mut flash, ERASE_SIZE as u32);
56
57 background_read(&mut flash, (ERASE_SIZE * 2) as u32).await;
58
59 loop {}
60}
61
62fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
63 info!(">>>> [multiwrite_bytes]");
64 let mut read_buf = [0u8; ERASE_SIZE];
65 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
66
67 info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
68 info!("Contents start with {=[u8]}", read_buf[0..4]);
69
70 defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
71
72 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
73 info!("Contents after erase starts with {=[u8]}", read_buf[0..4]);
74 if read_buf.iter().any(|x| *x != 0xFF) {
75 defmt::panic!("unexpected");
76 }
77
78 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &[0x01]));
79 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 1, &[0x02]));
80 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 2, &[0x03]));
81 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 3, &[0x04]));
82
83 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
84 info!("Contents after write starts with {=[u8]}", read_buf[0..4]);
85 if &read_buf[0..4] != &[0x01, 0x02, 0x03, 0x04] {
86 defmt::panic!("unexpected");
87 }
88}
89
90fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
91 info!(">>>> [erase_write_sector]");
92 let mut buf = [0u8; ERASE_SIZE];
93 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));
94
95 info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
96 info!("Contents start with {=[u8]}", buf[0..4]);
97
98 defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
99
100 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));
101 info!("Contents after erase starts with {=[u8]}", buf[0..4]);
102 if buf.iter().any(|x| *x != 0xFF) {
103 defmt::panic!("unexpected");
104 }
105
106 for b in buf.iter_mut() {
107 *b = 0xDA;
108 }
109
110 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &buf));
111
112 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));
113 info!("Contents after write starts with {=[u8]}", buf[0..4]);
114 if buf.iter().any(|x| *x != 0xDA) {
115 defmt::panic!("unexpected");
116 }
117}
118
119async fn background_read(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
120 info!(">>>> [background_read]");
121
122 let mut buf = [0u32; 8];
123 defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
124
125 info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
126 info!("Contents start with {=u32:x}", buf[0]);
127
128 defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
129
130 defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
131 info!("Contents after erase starts with {=u32:x}", buf[0]);
132 if buf.iter().any(|x| *x != 0xFFFFFFFF) {
133 defmt::panic!("unexpected");
134 }
135
136 for b in buf.iter_mut() {
137 *b = 0xDABA1234;
138 }
139
140 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, unsafe {
141 core::slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len() * 4)
142 }));
143
144 defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
145 info!("Contents after write starts with {=u32:x}", buf[0]);
146 if buf.iter().any(|x| *x != 0xDABA1234) {
147 defmt::panic!("unexpected");
148 }
149}
diff --git a/examples/rp23/src/bin/gpio_async.rs b/examples/rp23/src/bin/gpio_async.rs
new file mode 100644
index 000000000..1618f7c8b
--- /dev/null
+++ b/examples/rp23/src/bin/gpio_async.rs
@@ -0,0 +1,55 @@
1//! This example shows how async gpio can be used with a RP2040.
2//!
3//! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::gpio;
12use embassy_time::Timer;
13use gpio::{Input, Level, Output, Pull};
14use {defmt_rtt as _, panic_probe as _};
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30/// It requires an external signal to be manually triggered on PIN 16. For
31/// example, this could be accomplished using an external power source with a
32/// button so that it is possible to toggle the signal from low to high.
33///
34/// This example will begin with turning on the LED on the board and wait for a
35/// high signal on PIN 16. Once the high event/signal occurs the program will
36/// continue and turn off the LED, and then wait for 2 seconds before completing
37/// the loop and starting over again.
38#[embassy_executor::main]
39async fn main(_spawner: Spawner) {
40 let p = embassy_rp::init(Default::default());
41 let mut led = Output::new(p.PIN_25, Level::Low);
42 let mut async_input = Input::new(p.PIN_16, Pull::None);
43
44 loop {
45 info!("wait_for_high. Turn on LED");
46 led.set_high();
47
48 async_input.wait_for_high().await;
49
50 info!("done wait_for_high. Turn off LED");
51 led.set_low();
52
53 Timer::after_secs(2).await;
54 }
55}
diff --git a/examples/rp23/src/bin/gpout.rs b/examples/rp23/src/bin/gpout.rs
new file mode 100644
index 000000000..b15963f02
--- /dev/null
+++ b/examples/rp23/src/bin/gpout.rs
@@ -0,0 +1,52 @@
1//! This example shows how GPOUT (General purpose clock outputs) can toggle a output pin.
2//!
3//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::clocks;
12use embassy_time::Timer;
13use {defmt_rtt as _, panic_probe as _};
14
15#[link_section = ".start_block"]
16#[used]
17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
18
19// Program metadata for `picotool info`
20#[link_section = ".bi_entries"]
21#[used]
22pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
23 embassy_rp::binary_info_rp_cargo_bin_name!(),
24 embassy_rp::binary_info_rp_cargo_version!(),
25 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
26 embassy_rp::binary_info_rp_program_build_attribute!(),
27];
28
29#[embassy_executor::main]
30async fn main(_spawner: Spawner) {
31 let p = embassy_rp::init(Default::default());
32
33 let gpout3 = clocks::Gpout::new(p.PIN_25);
34 gpout3.set_div(1000, 0);
35 gpout3.enable();
36
37 loop {
38 gpout3.set_src(clocks::GpoutSrc::Sys);
39 info!(
40 "Pin 25 is now outputing CLK_SYS/1000, should be toggling at {}",
41 gpout3.get_freq()
42 );
43 Timer::after_secs(2).await;
44
45 gpout3.set_src(clocks::GpoutSrc::Ref);
46 info!(
47 "Pin 25 is now outputing CLK_REF/1000, should be toggling at {}",
48 gpout3.get_freq()
49 );
50 Timer::after_secs(2).await;
51 }
52}
diff --git a/examples/rp23/src/bin/i2c_async.rs b/examples/rp23/src/bin/i2c_async.rs
new file mode 100644
index 000000000..2528fe1d2
--- /dev/null
+++ b/examples/rp23/src/bin/i2c_async.rs
@@ -0,0 +1,125 @@
1//! This example shows how to communicate asynchronous using i2c with external chips.
2//!
3//! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip.
4//! (https://www.microchip.com/en-us/product/mcp23017)
5
6#![no_std]
7#![no_main]
8
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_rp::bind_interrupts;
12use embassy_rp::block::ImageDef;
13use embassy_rp::i2c::{self, Config, InterruptHandler};
14use embassy_rp::peripherals::I2C1;
15use embassy_time::Timer;
16use embedded_hal_async::i2c::I2c;
17use {defmt_rtt as _, panic_probe as _};
18
19#[link_section = ".start_block"]
20#[used]
21pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
22
23// Program metadata for `picotool info`
24#[link_section = ".bi_entries"]
25#[used]
26pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
27 embassy_rp::binary_info_rp_cargo_bin_name!(),
28 embassy_rp::binary_info_rp_cargo_version!(),
29 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
30 embassy_rp::binary_info_rp_program_build_attribute!(),
31];
32
33bind_interrupts!(struct Irqs {
34 I2C1_IRQ => InterruptHandler<I2C1>;
35});
36
37#[allow(dead_code)]
38mod mcp23017 {
39 pub const ADDR: u8 = 0x20; // default addr
40
41 macro_rules! mcpregs {
42 ($($name:ident : $val:expr),* $(,)?) => {
43 $(
44 pub const $name: u8 = $val;
45 )*
46
47 pub fn regname(reg: u8) -> &'static str {
48 match reg {
49 $(
50 $val => stringify!($name),
51 )*
52 _ => panic!("bad reg"),
53 }
54 }
55 }
56 }
57
58 // These are correct for IOCON.BANK=0
59 mcpregs! {
60 IODIRA: 0x00,
61 IPOLA: 0x02,
62 GPINTENA: 0x04,
63 DEFVALA: 0x06,
64 INTCONA: 0x08,
65 IOCONA: 0x0A,
66 GPPUA: 0x0C,
67 INTFA: 0x0E,
68 INTCAPA: 0x10,
69 GPIOA: 0x12,
70 OLATA: 0x14,
71 IODIRB: 0x01,
72 IPOLB: 0x03,
73 GPINTENB: 0x05,
74 DEFVALB: 0x07,
75 INTCONB: 0x09,
76 IOCONB: 0x0B,
77 GPPUB: 0x0D,
78 INTFB: 0x0F,
79 INTCAPB: 0x11,
80 GPIOB: 0x13,
81 OLATB: 0x15,
82 }
83}
84
85#[embassy_executor::main]
86async fn main(_spawner: Spawner) {
87 let p = embassy_rp::init(Default::default());
88
89 let sda = p.PIN_14;
90 let scl = p.PIN_15;
91
92 info!("set up i2c ");
93 let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, Config::default());
94
95 use mcp23017::*;
96
97 info!("init mcp23017 config for IxpandO");
98 // init - a outputs, b inputs
99 i2c.write(ADDR, &[IODIRA, 0x00]).await.unwrap();
100 i2c.write(ADDR, &[IODIRB, 0xff]).await.unwrap();
101 i2c.write(ADDR, &[GPPUB, 0xff]).await.unwrap(); // pullups
102
103 let mut val = 1;
104 loop {
105 let mut portb = [0];
106
107 i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).await.unwrap();
108 info!("portb = {:02x}", portb[0]);
109 i2c.write(mcp23017::ADDR, &[GPIOA, val | portb[0]]).await.unwrap();
110 val = val.rotate_left(1);
111
112 // get a register dump
113 info!("getting register dump");
114 let mut regs = [0; 22];
115 i2c.write_read(ADDR, &[0], &mut regs).await.unwrap();
116 // always get the regdump but only display it if portb'0 is set
117 if portb[0] & 1 != 0 {
118 for (idx, reg) in regs.into_iter().enumerate() {
119 info!("{} => {:02x}", regname(idx as u8), reg);
120 }
121 }
122
123 Timer::after_millis(100).await;
124 }
125}
diff --git a/examples/rp23/src/bin/i2c_async_embassy.rs b/examples/rp23/src/bin/i2c_async_embassy.rs
new file mode 100644
index 000000000..461b1d171
--- /dev/null
+++ b/examples/rp23/src/bin/i2c_async_embassy.rs
@@ -0,0 +1,100 @@
1//! This example shows how to communicate asynchronous using i2c with external chip.
2//!
3//! It's using embassy's functions directly instead of traits from embedded_hal_async::i2c::I2c.
4//! While most of i2c devices are addressed using 7 bits, an extension allows 10 bits too.
5
6#![no_std]
7#![no_main]
8
9use defmt::*;
10use embassy_rp::block::ImageDef;
11use embassy_rp::i2c::InterruptHandler;
12use {defmt_rtt as _, panic_probe as _};
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28// Our anonymous hypotetical temperature sensor could be:
29// a 12-bit sensor, with 100ms startup time, range of -40*C - 125*C, and precision 0.25*C
30// It requires no configuration or calibration, works with all i2c bus speeds,
31// never stretches clock or does anything complicated. Replies with one u16.
32// It requires only one write to take it out of suspend mode, and stays on.
33// Often result would be just on 12 bits, but here we'll simplify it to 16.
34
35enum UncomplicatedSensorId {
36 A(UncomplicatedSensorU8),
37 B(UncomplicatedSensorU16),
38}
39enum UncomplicatedSensorU8 {
40 First = 0x48,
41}
42enum UncomplicatedSensorU16 {
43 Other = 0x0049,
44}
45
46impl Into<u16> for UncomplicatedSensorU16 {
47 fn into(self) -> u16 {
48 self as u16
49 }
50}
51impl Into<u16> for UncomplicatedSensorU8 {
52 fn into(self) -> u16 {
53 0x48
54 }
55}
56impl From<UncomplicatedSensorId> for u16 {
57 fn from(t: UncomplicatedSensorId) -> Self {
58 match t {
59 UncomplicatedSensorId::A(x) => x.into(),
60 UncomplicatedSensorId::B(x) => x.into(),
61 }
62 }
63}
64
65embassy_rp::bind_interrupts!(struct Irqs {
66 I2C1_IRQ => InterruptHandler<embassy_rp::peripherals::I2C1>;
67});
68
69#[embassy_executor::main]
70async fn main(_task_spawner: embassy_executor::Spawner) {
71 let p = embassy_rp::init(Default::default());
72 let sda = p.PIN_14;
73 let scl = p.PIN_15;
74 let config = embassy_rp::i2c::Config::default();
75 let mut bus = embassy_rp::i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, config);
76
77 const WAKEYWAKEY: u16 = 0xBABE;
78 let mut result: [u8; 2] = [0, 0];
79 // wait for sensors to initialize
80 embassy_time::Timer::after(embassy_time::Duration::from_millis(100)).await;
81
82 let _res_1 = bus
83 .write_async(UncomplicatedSensorU8::First, WAKEYWAKEY.to_be_bytes())
84 .await;
85 let _res_2 = bus
86 .write_async(UncomplicatedSensorU16::Other, WAKEYWAKEY.to_be_bytes())
87 .await;
88
89 loop {
90 let s1 = UncomplicatedSensorId::A(UncomplicatedSensorU8::First);
91 let s2 = UncomplicatedSensorId::B(UncomplicatedSensorU16::Other);
92 let sensors = [s1, s2];
93 for sensor in sensors {
94 if bus.read_async(sensor, &mut result).await.is_ok() {
95 info!("Result {}", u16::from_be_bytes(result.into()));
96 }
97 }
98 embassy_time::Timer::after(embassy_time::Duration::from_millis(200)).await;
99 }
100}
diff --git a/examples/rp23/src/bin/i2c_blocking.rs b/examples/rp23/src/bin/i2c_blocking.rs
new file mode 100644
index 000000000..6d36d1890
--- /dev/null
+++ b/examples/rp23/src/bin/i2c_blocking.rs
@@ -0,0 +1,89 @@
1//! This example shows how to communicate using i2c with external chips.
2//!
3//! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip.
4//! (https://www.microchip.com/en-us/product/mcp23017)
5
6#![no_std]
7#![no_main]
8
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_rp::block::ImageDef;
12use embassy_rp::i2c::{self, Config};
13use embassy_time::Timer;
14use embedded_hal_1::i2c::I2c;
15use {defmt_rtt as _, panic_probe as _};
16
17#[link_section = ".start_block"]
18#[used]
19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
20
21// Program metadata for `picotool info`
22#[link_section = ".bi_entries"]
23#[used]
24pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
25 embassy_rp::binary_info_rp_cargo_bin_name!(),
26 embassy_rp::binary_info_rp_cargo_version!(),
27 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
28 embassy_rp::binary_info_rp_program_build_attribute!(),
29];
30
31#[allow(dead_code)]
32mod mcp23017 {
33 pub const ADDR: u8 = 0x20; // default addr
34
35 pub const IODIRA: u8 = 0x00;
36 pub const IPOLA: u8 = 0x02;
37 pub const GPINTENA: u8 = 0x04;
38 pub const DEFVALA: u8 = 0x06;
39 pub const INTCONA: u8 = 0x08;
40 pub const IOCONA: u8 = 0x0A;
41 pub const GPPUA: u8 = 0x0C;
42 pub const INTFA: u8 = 0x0E;
43 pub const INTCAPA: u8 = 0x10;
44 pub const GPIOA: u8 = 0x12;
45 pub const OLATA: u8 = 0x14;
46 pub const IODIRB: u8 = 0x01;
47 pub const IPOLB: u8 = 0x03;
48 pub const GPINTENB: u8 = 0x05;
49 pub const DEFVALB: u8 = 0x07;
50 pub const INTCONB: u8 = 0x09;
51 pub const IOCONB: u8 = 0x0B;
52 pub const GPPUB: u8 = 0x0D;
53 pub const INTFB: u8 = 0x0F;
54 pub const INTCAPB: u8 = 0x11;
55 pub const GPIOB: u8 = 0x13;
56 pub const OLATB: u8 = 0x15;
57}
58
59#[embassy_executor::main]
60async fn main(_spawner: Spawner) {
61 let p = embassy_rp::init(Default::default());
62
63 let sda = p.PIN_14;
64 let scl = p.PIN_15;
65
66 info!("set up i2c ");
67 let mut i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default());
68
69 use mcp23017::*;
70
71 info!("init mcp23017 config for IxpandO");
72 // init - a outputs, b inputs
73 i2c.write(ADDR, &[IODIRA, 0x00]).unwrap();
74 i2c.write(ADDR, &[IODIRB, 0xff]).unwrap();
75 i2c.write(ADDR, &[GPPUB, 0xff]).unwrap(); // pullups
76
77 let mut val = 0xaa;
78 loop {
79 let mut portb = [0];
80
81 i2c.write(mcp23017::ADDR, &[GPIOA, val]).unwrap();
82 i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).unwrap();
83
84 info!("portb = {:02x}", portb[0]);
85 val = !val;
86
87 Timer::after_secs(1).await;
88 }
89}
diff --git a/examples/rp23/src/bin/i2c_slave.rs b/examples/rp23/src/bin/i2c_slave.rs
new file mode 100644
index 000000000..1f3408cf3
--- /dev/null
+++ b/examples/rp23/src/bin/i2c_slave.rs
@@ -0,0 +1,132 @@
1//! This example shows how to use the 2040 as an i2c slave.
2#![no_std]
3#![no_main]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_rp::block::ImageDef;
8use embassy_rp::peripherals::{I2C0, I2C1};
9use embassy_rp::{bind_interrupts, i2c, i2c_slave};
10use embassy_time::Timer;
11use embedded_hal_async::i2c::I2c;
12use {defmt_rtt as _, panic_probe as _};
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28bind_interrupts!(struct Irqs {
29 I2C0_IRQ => i2c::InterruptHandler<I2C0>;
30 I2C1_IRQ => i2c::InterruptHandler<I2C1>;
31});
32
33const DEV_ADDR: u8 = 0x42;
34
35#[embassy_executor::task]
36async fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! {
37 info!("Device start");
38
39 let mut state = 0;
40
41 loop {
42 let mut buf = [0u8; 128];
43 match dev.listen(&mut buf).await {
44 Ok(i2c_slave::Command::GeneralCall(len)) => info!("Device received general call write: {}", buf[..len]),
45 Ok(i2c_slave::Command::Read) => loop {
46 match dev.respond_to_read(&[state]).await {
47 Ok(x) => match x {
48 i2c_slave::ReadStatus::Done => break,
49 i2c_slave::ReadStatus::NeedMoreBytes => (),
50 i2c_slave::ReadStatus::LeftoverBytes(x) => {
51 info!("tried to write {} extra bytes", x);
52 break;
53 }
54 },
55 Err(e) => error!("error while responding {}", e),
56 }
57 },
58 Ok(i2c_slave::Command::Write(len)) => info!("Device received write: {}", buf[..len]),
59 Ok(i2c_slave::Command::WriteRead(len)) => {
60 info!("device received write read: {:x}", buf[..len]);
61 match buf[0] {
62 // Set the state
63 0xC2 => {
64 state = buf[1];
65 match dev.respond_and_fill(&[state], 0x00).await {
66 Ok(read_status) => info!("response read status {}", read_status),
67 Err(e) => error!("error while responding {}", e),
68 }
69 }
70 // Reset State
71 0xC8 => {
72 state = 0;
73 match dev.respond_and_fill(&[state], 0x00).await {
74 Ok(read_status) => info!("response read status {}", read_status),
75 Err(e) => error!("error while responding {}", e),
76 }
77 }
78 x => error!("Invalid Write Read {:x}", x),
79 }
80 }
81 Err(e) => error!("{}", e),
82 }
83 }
84}
85
86#[embassy_executor::task]
87async fn controller_task(mut con: i2c::I2c<'static, I2C0, i2c::Async>) {
88 info!("Controller start");
89
90 loop {
91 let mut resp_buff = [0u8; 2];
92 for i in 0..10 {
93 match con.write_read(DEV_ADDR, &[0xC2, i], &mut resp_buff).await {
94 Ok(_) => info!("write_read response: {}", resp_buff),
95 Err(e) => error!("Error writing {}", e),
96 }
97
98 Timer::after_millis(100).await;
99 }
100 match con.read(DEV_ADDR, &mut resp_buff).await {
101 Ok(_) => info!("read response: {}", resp_buff),
102 Err(e) => error!("Error writing {}", e),
103 }
104 match con.write_read(DEV_ADDR, &[0xC8], &mut resp_buff).await {
105 Ok(_) => info!("write_read response: {}", resp_buff),
106 Err(e) => error!("Error writing {}", e),
107 }
108 Timer::after_millis(100).await;
109 }
110}
111
112#[embassy_executor::main]
113async fn main(spawner: Spawner) {
114 let p = embassy_rp::init(Default::default());
115 info!("Hello World!");
116
117 let d_sda = p.PIN_3;
118 let d_scl = p.PIN_2;
119 let mut config = i2c_slave::Config::default();
120 config.addr = DEV_ADDR as u16;
121 let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config);
122
123 unwrap!(spawner.spawn(device_task(device)));
124
125 let c_sda = p.PIN_1;
126 let c_scl = p.PIN_0;
127 let mut config = i2c::Config::default();
128 config.frequency = 1_000_000;
129 let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config);
130
131 unwrap!(spawner.spawn(controller_task(controller)));
132}
diff --git a/examples/rp23/src/bin/interrupt.rs b/examples/rp23/src/bin/interrupt.rs
new file mode 100644
index 000000000..6184b1bd7
--- /dev/null
+++ b/examples/rp23/src/bin/interrupt.rs
@@ -0,0 +1,109 @@
1//! This example shows how you can use raw interrupt handlers alongside embassy.
2//! The example also showcases some of the options available for sharing resources/data.
3//!
4//! In the example, an ADC reading is triggered every time the PWM wraps around.
5//! The sample data is sent down a channel, to be processed inside a low priority task.
6//! The processed data is then used to adjust the PWM duty cycle, once every second.
7
8#![no_std]
9#![no_main]
10
11use core::cell::{Cell, RefCell};
12
13use defmt::*;
14use embassy_executor::Spawner;
15use embassy_rp::adc::{self, Adc, Blocking};
16use embassy_rp::block::ImageDef;
17use embassy_rp::gpio::Pull;
18use embassy_rp::interrupt;
19use embassy_rp::pwm::{Config, Pwm};
20use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
21use embassy_sync::blocking_mutex::Mutex;
22use embassy_sync::channel::Channel;
23use embassy_time::{Duration, Ticker};
24use portable_atomic::{AtomicU32, Ordering};
25use static_cell::StaticCell;
26use {defmt_rtt as _, panic_probe as _};
27
28#[link_section = ".start_block"]
29#[used]
30pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
31
32// Program metadata for `picotool info`
33#[link_section = ".bi_entries"]
34#[used]
35pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
36 embassy_rp::binary_info_rp_cargo_bin_name!(),
37 embassy_rp::binary_info_rp_cargo_version!(),
38 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
39 embassy_rp::binary_info_rp_program_build_attribute!(),
40];
41
42static COUNTER: AtomicU32 = AtomicU32::new(0);
43static PWM: Mutex<CriticalSectionRawMutex, RefCell<Option<Pwm>>> = Mutex::new(RefCell::new(None));
44static ADC: Mutex<CriticalSectionRawMutex, RefCell<Option<(Adc<Blocking>, adc::Channel)>>> =
45 Mutex::new(RefCell::new(None));
46static ADC_VALUES: Channel<CriticalSectionRawMutex, u16, 2048> = Channel::new();
47
48#[embassy_executor::main]
49async fn main(spawner: Spawner) {
50 embassy_rp::pac::SIO.spinlock(31).write_value(1);
51 let p = embassy_rp::init(Default::default());
52
53 let adc = Adc::new_blocking(p.ADC, Default::default());
54 let p26 = adc::Channel::new_pin(p.PIN_26, Pull::None);
55 ADC.lock(|a| a.borrow_mut().replace((adc, p26)));
56
57 let pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, Default::default());
58 PWM.lock(|p| p.borrow_mut().replace(pwm));
59
60 // Enable the interrupt for pwm slice 4
61 embassy_rp::pac::PWM.irq0_inte().modify(|w| w.set_ch4(true));
62 unsafe {
63 cortex_m::peripheral::NVIC::unmask(interrupt::PWM_IRQ_WRAP_0);
64 }
65
66 // Tasks require their resources to have 'static lifetime
67 // No Mutex needed when sharing within the same executor/prio level
68 static AVG: StaticCell<Cell<u32>> = StaticCell::new();
69 let avg = AVG.init(Default::default());
70 spawner.must_spawn(processing(avg));
71
72 let mut ticker = Ticker::every(Duration::from_secs(1));
73 loop {
74 ticker.next().await;
75 let freq = COUNTER.swap(0, Ordering::Relaxed);
76 info!("pwm freq: {:?} Hz", freq);
77 info!("adc average: {:?}", avg.get());
78
79 // Update the pwm duty cycle, based on the averaged adc reading
80 let mut config = Config::default();
81 config.compare_b = ((avg.get() as f32 / 4095.0) * config.top as f32) as _;
82 PWM.lock(|p| p.borrow_mut().as_mut().unwrap().set_config(&config));
83 }
84}
85
86#[embassy_executor::task]
87async fn processing(avg: &'static Cell<u32>) {
88 let mut buffer: heapless::HistoryBuffer<u16, 100> = Default::default();
89 loop {
90 let val = ADC_VALUES.receive().await;
91 buffer.write(val);
92 let sum: u32 = buffer.iter().map(|x| *x as u32).sum();
93 avg.set(sum / buffer.len() as u32);
94 }
95}
96
97#[interrupt]
98fn PWM_IRQ_WRAP_0() {
99 critical_section::with(|cs| {
100 let mut adc = ADC.borrow(cs).borrow_mut();
101 let (adc, p26) = adc.as_mut().unwrap();
102 let val = adc.blocking_read(p26).unwrap();
103 ADC_VALUES.try_send(val).ok();
104
105 // Clear the interrupt, so we don't immediately re-enter this irq handler
106 PWM.borrow(cs).borrow_mut().as_mut().unwrap().clear_wrapped();
107 });
108 COUNTER.fetch_add(1, Ordering::Relaxed);
109}
diff --git a/examples/rp23/src/bin/multicore.rs b/examples/rp23/src/bin/multicore.rs
new file mode 100644
index 000000000..8649143e1
--- /dev/null
+++ b/examples/rp23/src/bin/multicore.rs
@@ -0,0 +1,81 @@
1//! This example shows how to send messages between the two cores in the RP2040 chip.
2//!
3//! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Executor;
10use embassy_rp::block::ImageDef;
11use embassy_rp::gpio::{Level, Output};
12use embassy_rp::multicore::{spawn_core1, Stack};
13use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
14use embassy_sync::channel::Channel;
15use embassy_time::Timer;
16use static_cell::StaticCell;
17use {defmt_rtt as _, panic_probe as _};
18
19#[link_section = ".start_block"]
20#[used]
21pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
22
23// Program metadata for `picotool info`
24#[link_section = ".bi_entries"]
25#[used]
26pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
27 embassy_rp::binary_info_rp_cargo_bin_name!(),
28 embassy_rp::binary_info_rp_cargo_version!(),
29 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
30 embassy_rp::binary_info_rp_program_build_attribute!(),
31];
32
33static mut CORE1_STACK: Stack<4096> = Stack::new();
34static EXECUTOR0: StaticCell<Executor> = StaticCell::new();
35static EXECUTOR1: StaticCell<Executor> = StaticCell::new();
36static CHANNEL: Channel<CriticalSectionRawMutex, LedState, 1> = Channel::new();
37
38enum LedState {
39 On,
40 Off,
41}
42
43#[cortex_m_rt::entry]
44fn main() -> ! {
45 let p = embassy_rp::init(Default::default());
46 let led = Output::new(p.PIN_25, Level::Low);
47
48 spawn_core1(
49 p.CORE1,
50 unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) },
51 move || {
52 let executor1 = EXECUTOR1.init(Executor::new());
53 executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led))));
54 },
55 );
56
57 let executor0 = EXECUTOR0.init(Executor::new());
58 executor0.run(|spawner| unwrap!(spawner.spawn(core0_task())));
59}
60
61#[embassy_executor::task]
62async fn core0_task() {
63 info!("Hello from core 0");
64 loop {
65 CHANNEL.send(LedState::On).await;
66 Timer::after_millis(100).await;
67 CHANNEL.send(LedState::Off).await;
68 Timer::after_millis(400).await;
69 }
70}
71
72#[embassy_executor::task]
73async fn core1_task(mut led: Output<'static>) {
74 info!("Hello from core 1");
75 loop {
76 match CHANNEL.receive().await {
77 LedState::On => led.set_high(),
78 LedState::Off => led.set_low(),
79 }
80 }
81}
diff --git a/examples/rp23/src/bin/multiprio.rs b/examples/rp23/src/bin/multiprio.rs
new file mode 100644
index 000000000..7590fb431
--- /dev/null
+++ b/examples/rp23/src/bin/multiprio.rs
@@ -0,0 +1,160 @@
1//! This example showcases how to create multiple Executor instances to run tasks at
2//! different priority levels.
3//!
4//! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling
5//! there's work in the queue, and `wfe` for waiting for work.
6//!
7//! Medium and high priority executors run in two interrupts with different priorities.
8//! Signaling work is done by pending the interrupt. No "waiting" needs to be done explicitly, since
9//! when there's work the interrupt will trigger and run the executor.
10//!
11//! Sample output below. Note that high priority ticks can interrupt everything else, and
12//! medium priority computations can interrupt low priority computations, making them to appear
13//! to take significantly longer time.
14//!
15//! ```not_rust
16//! [med] Starting long computation
17//! [med] done in 992 ms
18//! [high] tick!
19//! [low] Starting long computation
20//! [med] Starting long computation
21//! [high] tick!
22//! [high] tick!
23//! [med] done in 993 ms
24//! [med] Starting long computation
25//! [high] tick!
26//! [high] tick!
27//! [med] done in 993 ms
28//! [low] done in 3972 ms
29//! [med] Starting long computation
30//! [high] tick!
31//! [high] tick!
32//! [med] done in 993 ms
33//! ```
34//!
35//! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor.
36//! You will get an output like the following. Note that no computation is ever interrupted.
37//!
38//! ```not_rust
39//! [high] tick!
40//! [med] Starting long computation
41//! [med] done in 496 ms
42//! [low] Starting long computation
43//! [low] done in 992 ms
44//! [med] Starting long computation
45//! [med] done in 496 ms
46//! [high] tick!
47//! [low] Starting long computation
48//! [low] done in 992 ms
49//! [high] tick!
50//! [med] Starting long computation
51//! [med] done in 496 ms
52//! [high] tick!
53//! ```
54//!
55
56#![no_std]
57#![no_main]
58
59use cortex_m_rt::entry;
60use defmt::{info, unwrap};
61use embassy_executor::{Executor, InterruptExecutor};
62use embassy_rp::block::ImageDef;
63use embassy_rp::interrupt;
64use embassy_rp::interrupt::{InterruptExt, Priority};
65use embassy_time::{Instant, Timer, TICK_HZ};
66use static_cell::StaticCell;
67use {defmt_rtt as _, panic_probe as _};
68
69#[link_section = ".start_block"]
70#[used]
71pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
72
73// Program metadata for `picotool info`
74#[link_section = ".bi_entries"]
75#[used]
76pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
77 embassy_rp::binary_info_rp_cargo_bin_name!(),
78 embassy_rp::binary_info_rp_cargo_version!(),
79 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
80 embassy_rp::binary_info_rp_program_build_attribute!(),
81];
82
83#[embassy_executor::task]
84async fn run_high() {
85 loop {
86 info!(" [high] tick!");
87 Timer::after_ticks(673740).await;
88 }
89}
90
91#[embassy_executor::task]
92async fn run_med() {
93 loop {
94 let start = Instant::now();
95 info!(" [med] Starting long computation");
96
97 // Spin-wait to simulate a long CPU computation
98 embassy_time::block_for(embassy_time::Duration::from_secs(1)); // ~1 second
99
100 let end = Instant::now();
101 let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ;
102 info!(" [med] done in {} ms", ms);
103
104 Timer::after_ticks(53421).await;
105 }
106}
107
108#[embassy_executor::task]
109async fn run_low() {
110 loop {
111 let start = Instant::now();
112 info!("[low] Starting long computation");
113
114 // Spin-wait to simulate a long CPU computation
115 embassy_time::block_for(embassy_time::Duration::from_secs(2)); // ~2 seconds
116
117 let end = Instant::now();
118 let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ;
119 info!("[low] done in {} ms", ms);
120
121 Timer::after_ticks(82983).await;
122 }
123}
124
125static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new();
126static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new();
127static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();
128
129#[interrupt]
130unsafe fn SWI_IRQ_1() {
131 EXECUTOR_HIGH.on_interrupt()
132}
133
134#[interrupt]
135unsafe fn SWI_IRQ_0() {
136 EXECUTOR_MED.on_interrupt()
137}
138
139#[entry]
140fn main() -> ! {
141 info!("Hello World!");
142
143 let _p = embassy_rp::init(Default::default());
144
145 // High-priority executor: SWI_IRQ_1, priority level 2
146 interrupt::SWI_IRQ_1.set_priority(Priority::P2);
147 let spawner = EXECUTOR_HIGH.start(interrupt::SWI_IRQ_1);
148 unwrap!(spawner.spawn(run_high()));
149
150 // Medium-priority executor: SWI_IRQ_0, priority level 3
151 interrupt::SWI_IRQ_0.set_priority(Priority::P3);
152 let spawner = EXECUTOR_MED.start(interrupt::SWI_IRQ_0);
153 unwrap!(spawner.spawn(run_med()));
154
155 // Low priority executor: runs in thread mode, using WFE/SEV
156 let executor = EXECUTOR_LOW.init(Executor::new());
157 executor.run(|spawner| {
158 unwrap!(spawner.spawn(run_low()));
159 });
160}
diff --git a/examples/rp23/src/bin/pio_async.rs b/examples/rp23/src/bin/pio_async.rs
new file mode 100644
index 000000000..005708bc2
--- /dev/null
+++ b/examples/rp23/src/bin/pio_async.rs
@@ -0,0 +1,145 @@
1//! This example shows powerful PIO module in the RP2040 chip.
2
3#![no_std]
4#![no_main]
5use defmt::info;
6use embassy_executor::Spawner;
7use embassy_rp::bind_interrupts;
8use embassy_rp::block::ImageDef;
9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine};
11use fixed::traits::ToFixed;
12use fixed_macro::types::U56F8;
13use {defmt_rtt as _, panic_probe as _};
14
15#[link_section = ".start_block"]
16#[used]
17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
18
19// Program metadata for `picotool info`
20#[link_section = ".bi_entries"]
21#[used]
22pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
23 embassy_rp::binary_info_rp_cargo_bin_name!(),
24 embassy_rp::binary_info_rp_cargo_version!(),
25 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
26 embassy_rp::binary_info_rp_program_build_attribute!(),
27];
28
29bind_interrupts!(struct Irqs {
30 PIO0_IRQ_0 => InterruptHandler<PIO0>;
31});
32
33fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) {
34 // Setup sm0
35
36 // Send data serially to pin
37 let prg = pio_proc::pio_asm!(
38 ".origin 16",
39 "set pindirs, 1",
40 ".wrap_target",
41 "out pins,1 [19]",
42 ".wrap",
43 );
44
45 let mut cfg = Config::default();
46 cfg.use_program(&pio.load_program(&prg.program), &[]);
47 let out_pin = pio.make_pio_pin(pin);
48 cfg.set_out_pins(&[&out_pin]);
49 cfg.set_set_pins(&[&out_pin]);
50 cfg.clock_divider = (U56F8!(125_000_000) / 20 / 200).to_fixed();
51 cfg.shift_out.auto_fill = true;
52 sm.set_config(&cfg);
53}
54
55#[embassy_executor::task]
56async fn pio_task_sm0(mut sm: StateMachine<'static, PIO0, 0>) {
57 sm.set_enable(true);
58
59 let mut v = 0x0f0caffa;
60 loop {
61 sm.tx().wait_push(v).await;
62 v ^= 0xffff;
63 info!("Pushed {:032b} to FIFO", v);
64 }
65}
66
67fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 1>) {
68 // Setupm sm1
69
70 // Read 0b10101 repeatedly until ISR is full
71 let prg = pio_proc::pio_asm!(
72 //
73 ".origin 8",
74 "set x, 0x15",
75 ".wrap_target",
76 "in x, 5 [31]",
77 ".wrap",
78 );
79
80 let mut cfg = Config::default();
81 cfg.use_program(&pio.load_program(&prg.program), &[]);
82 cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
83 cfg.shift_in.auto_fill = true;
84 cfg.shift_in.direction = ShiftDirection::Right;
85 sm.set_config(&cfg);
86}
87
88#[embassy_executor::task]
89async fn pio_task_sm1(mut sm: StateMachine<'static, PIO0, 1>) {
90 sm.set_enable(true);
91 loop {
92 let rx = sm.rx().wait_pull().await;
93 info!("Pulled {:032b} from FIFO", rx);
94 }
95}
96
97fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 2>) {
98 // Setup sm2
99
100 // Repeatedly trigger IRQ 3
101 let prg = pio_proc::pio_asm!(
102 ".origin 0",
103 ".wrap_target",
104 "set x,10",
105 "delay:",
106 "jmp x-- delay [15]",
107 "irq 3 [15]",
108 ".wrap",
109 );
110 let mut cfg = Config::default();
111 cfg.use_program(&pio.load_program(&prg.program), &[]);
112 cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
113 sm.set_config(&cfg);
114}
115
116#[embassy_executor::task]
117async fn pio_task_sm2(mut irq: Irq<'static, PIO0, 3>, mut sm: StateMachine<'static, PIO0, 2>) {
118 sm.set_enable(true);
119 loop {
120 irq.wait().await;
121 info!("IRQ trigged");
122 }
123}
124
125#[embassy_executor::main]
126async fn main(spawner: Spawner) {
127 let p = embassy_rp::init(Default::default());
128 let pio = p.PIO0;
129
130 let Pio {
131 mut common,
132 irq3,
133 mut sm0,
134 mut sm1,
135 mut sm2,
136 ..
137 } = Pio::new(pio, Irqs);
138
139 setup_pio_task_sm0(&mut common, &mut sm0, p.PIN_0);
140 setup_pio_task_sm1(&mut common, &mut sm1);
141 setup_pio_task_sm2(&mut common, &mut sm2);
142 spawner.spawn(pio_task_sm0(sm0)).unwrap();
143 spawner.spawn(pio_task_sm1(sm1)).unwrap();
144 spawner.spawn(pio_task_sm2(irq3, sm2)).unwrap();
145}
diff --git a/examples/rp23/src/bin/pio_dma.rs b/examples/rp23/src/bin/pio_dma.rs
new file mode 100644
index 000000000..48fd9123f
--- /dev/null
+++ b/examples/rp23/src/bin/pio_dma.rs
@@ -0,0 +1,98 @@
1//! This example shows powerful PIO module in the RP2040 chip.
2
3#![no_std]
4#![no_main]
5use defmt::info;
6use embassy_executor::Spawner;
7use embassy_futures::join::join;
8use embassy_rp::block::ImageDef;
9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection};
11use embassy_rp::{bind_interrupts, Peripheral};
12use fixed::traits::ToFixed;
13use fixed_macro::types::U56F8;
14use {defmt_rtt as _, panic_probe as _};
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30bind_interrupts!(struct Irqs {
31 PIO0_IRQ_0 => InterruptHandler<PIO0>;
32});
33
34fn swap_nibbles(v: u32) -> u32 {
35 let v = (v & 0x0f0f_0f0f) << 4 | (v & 0xf0f0_f0f0) >> 4;
36 let v = (v & 0x00ff_00ff) << 8 | (v & 0xff00_ff00) >> 8;
37 (v & 0x0000_ffff) << 16 | (v & 0xffff_0000) >> 16
38}
39
40#[embassy_executor::main]
41async fn main(_spawner: Spawner) {
42 let p = embassy_rp::init(Default::default());
43 let pio = p.PIO0;
44 let Pio {
45 mut common,
46 sm0: mut sm,
47 ..
48 } = Pio::new(pio, Irqs);
49
50 let prg = pio_proc::pio_asm!(
51 ".origin 0",
52 "set pindirs,1",
53 ".wrap_target",
54 "set y,7",
55 "loop:",
56 "out x,4",
57 "in x,4",
58 "jmp y--, loop",
59 ".wrap",
60 );
61
62 let mut cfg = Config::default();
63 cfg.use_program(&common.load_program(&prg.program), &[]);
64 cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed();
65 cfg.shift_in = ShiftConfig {
66 auto_fill: true,
67 threshold: 32,
68 direction: ShiftDirection::Left,
69 };
70 cfg.shift_out = ShiftConfig {
71 auto_fill: true,
72 threshold: 32,
73 direction: ShiftDirection::Right,
74 };
75
76 sm.set_config(&cfg);
77 sm.set_enable(true);
78
79 let mut dma_out_ref = p.DMA_CH0.into_ref();
80 let mut dma_in_ref = p.DMA_CH1.into_ref();
81 let mut dout = [0x12345678u32; 29];
82 for i in 1..dout.len() {
83 dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7;
84 }
85 let mut din = [0u32; 29];
86 loop {
87 let (rx, tx) = sm.rx_tx();
88 join(
89 tx.dma_push(dma_out_ref.reborrow(), &dout),
90 rx.dma_pull(dma_in_ref.reborrow(), &mut din),
91 )
92 .await;
93 for i in 0..din.len() {
94 assert_eq!(din[i], swap_nibbles(dout[i]));
95 }
96 info!("Swapped {} words", dout.len());
97 }
98}
diff --git a/examples/rp23/src/bin/pio_hd44780.rs b/examples/rp23/src/bin/pio_hd44780.rs
new file mode 100644
index 000000000..fc658267d
--- /dev/null
+++ b/examples/rp23/src/bin/pio_hd44780.rs
@@ -0,0 +1,255 @@
1//! This example shows powerful PIO module in the RP2040 chip to communicate with a HD44780 display.
2//! See (https://www.sparkfun.com/datasheets/LCD/HD44780.pdf)
3
4#![no_std]
5#![no_main]
6
7use core::fmt::Write;
8
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::dma::{AnyChannel, Channel};
12use embassy_rp::peripherals::PIO0;
13use embassy_rp::pio::{
14 Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
15};
16use embassy_rp::pwm::{self, Pwm};
17use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef};
18use embassy_time::{Instant, Timer};
19use {defmt_rtt as _, panic_probe as _};
20
21#[link_section = ".start_block"]
22#[used]
23pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
24
25// Program metadata for `picotool info`
26#[link_section = ".bi_entries"]
27#[used]
28pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
29 embassy_rp::binary_info_rp_cargo_bin_name!(),
30 embassy_rp::binary_info_rp_cargo_version!(),
31 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
32 embassy_rp::binary_info_rp_program_build_attribute!(),
33];
34
35bind_interrupts!(pub struct Irqs {
36 PIO0_IRQ_0 => InterruptHandler<PIO0>;
37});
38
39#[embassy_executor::main]
40async fn main(_spawner: Spawner) {
41 // this test assumes a 2x16 HD44780 display attached as follow:
42 // rs = PIN0
43 // rw = PIN1
44 // e = PIN2
45 // db4 = PIN3
46 // db5 = PIN4
47 // db6 = PIN5
48 // db7 = PIN6
49 // additionally a pwm signal for a bias voltage charge pump is provided on pin 15,
50 // allowing direct connection of the display to the RP2040 without level shifters.
51 let p = embassy_rp::init(Default::default());
52
53 let _pwm = Pwm::new_output_b(p.PWM_SLICE7, p.PIN_15, {
54 let mut c = pwm::Config::default();
55 c.divider = 125.into();
56 c.top = 100;
57 c.compare_b = 50;
58 c
59 });
60
61 let mut hd = HD44780::new(
62 p.PIO0, Irqs, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6,
63 )
64 .await;
65
66 loop {
67 struct Buf<const N: usize>([u8; N], usize);
68 impl<const N: usize> Write for Buf<N> {
69 fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
70 for b in s.as_bytes() {
71 if self.1 >= N {
72 return Err(core::fmt::Error);
73 }
74 self.0[self.1] = *b;
75 self.1 += 1;
76 }
77 Ok(())
78 }
79 }
80 let mut buf = Buf([0; 16], 0);
81 write!(buf, "up {}s", Instant::now().as_micros() as f32 / 1e6).unwrap();
82 hd.add_line(&buf.0[0..buf.1]).await;
83 Timer::after_secs(1).await;
84 }
85}
86
87pub struct HD44780<'l> {
88 dma: PeripheralRef<'l, AnyChannel>,
89 sm: StateMachine<'l, PIO0, 0>,
90
91 buf: [u8; 40],
92}
93
94impl<'l> HD44780<'l> {
95 pub async fn new(
96 pio: impl Peripheral<P = PIO0> + 'l,
97 irq: Irqs,
98 dma: impl Peripheral<P = impl Channel> + 'l,
99 rs: impl PioPin,
100 rw: impl PioPin,
101 e: impl PioPin,
102 db4: impl PioPin,
103 db5: impl PioPin,
104 db6: impl PioPin,
105 db7: impl PioPin,
106 ) -> HD44780<'l> {
107 into_ref!(dma);
108
109 let Pio {
110 mut common,
111 mut irq0,
112 mut sm0,
113 ..
114 } = Pio::new(pio, irq);
115
116 // takes command words (<wait:24> <command:4> <0:4>)
117 let prg = pio_proc::pio_asm!(
118 r#"
119 .side_set 1 opt
120 .origin 20
121
122 loop:
123 out x, 24
124 delay:
125 jmp x--, delay
126 out pins, 4 side 1
127 out null, 4 side 0
128 jmp !osre, loop
129 irq 0
130 "#,
131 );
132
133 let rs = common.make_pio_pin(rs);
134 let rw = common.make_pio_pin(rw);
135 let e = common.make_pio_pin(e);
136 let db4 = common.make_pio_pin(db4);
137 let db5 = common.make_pio_pin(db5);
138 let db6 = common.make_pio_pin(db6);
139 let db7 = common.make_pio_pin(db7);
140
141 sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]);
142
143 let mut cfg = Config::default();
144 cfg.use_program(&common.load_program(&prg.program), &[&e]);
145 cfg.clock_divider = 125u8.into();
146 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
147 cfg.shift_out = ShiftConfig {
148 auto_fill: true,
149 direction: ShiftDirection::Left,
150 threshold: 32,
151 };
152 cfg.fifo_join = FifoJoin::TxOnly;
153 sm0.set_config(&cfg);
154
155 sm0.set_enable(true);
156 // init to 8 bit thrice
157 sm0.tx().push((50000 << 8) | 0x30);
158 sm0.tx().push((5000 << 8) | 0x30);
159 sm0.tx().push((200 << 8) | 0x30);
160 // init 4 bit
161 sm0.tx().push((200 << 8) | 0x20);
162 // set font and lines
163 sm0.tx().push((50 << 8) | 0x20);
164 sm0.tx().push(0b1100_0000);
165
166 irq0.wait().await;
167 sm0.set_enable(false);
168
169 // takes command sequences (<rs:1> <count:7>, data...)
170 // many side sets are only there to free up a delay bit!
171 let prg = pio_proc::pio_asm!(
172 r#"
173 .origin 27
174 .side_set 1
175
176 .wrap_target
177 pull side 0
178 out x 1 side 0 ; !rs
179 out y 7 side 0 ; #data - 1
180
181 ; rs/rw to e: >= 60ns
182 ; e high time: >= 500ns
183 ; e low time: >= 500ns
184 ; read data valid after e falling: ~5ns
185 ; write data hold after e falling: ~10ns
186
187 loop:
188 pull side 0
189 jmp !x data side 0
190 command:
191 set pins 0b00 side 0
192 jmp shift side 0
193 data:
194 set pins 0b01 side 0
195 shift:
196 out pins 4 side 1 [9]
197 nop side 0 [9]
198 out pins 4 side 1 [9]
199 mov osr null side 0 [7]
200 out pindirs 4 side 0
201 set pins 0b10 side 0
202 busy:
203 nop side 1 [9]
204 jmp pin more side 0 [9]
205 mov osr ~osr side 1 [9]
206 nop side 0 [4]
207 out pindirs 4 side 0
208 jmp y-- loop side 0
209 .wrap
210 more:
211 nop side 1 [9]
212 jmp busy side 0 [9]
213 "#
214 );
215
216 let mut cfg = Config::default();
217 cfg.use_program(&common.load_program(&prg.program), &[&e]);
218 cfg.clock_divider = 8u8.into(); // ~64ns/insn
219 cfg.set_jmp_pin(&db7);
220 cfg.set_set_pins(&[&rs, &rw]);
221 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
222 cfg.shift_out.direction = ShiftDirection::Left;
223 cfg.fifo_join = FifoJoin::TxOnly;
224 sm0.set_config(&cfg);
225
226 sm0.set_enable(true);
227
228 // display on and cursor on and blinking, reset display
229 sm0.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await;
230
231 Self {
232 dma: dma.map_into(),
233 sm: sm0,
234 buf: [0x20; 40],
235 }
236 }
237
238 pub async fn add_line(&mut self, s: &[u8]) {
239 // move cursor to 0:0, prepare 16 characters
240 self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]);
241 // move line 2 up
242 self.buf.copy_within(22..38, 3);
243 // move cursor to 1:0, prepare 16 characters
244 self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]);
245 // file line 2 with spaces
246 self.buf[22..38].fill(0x20);
247 // copy input line
248 let len = s.len().min(16);
249 self.buf[22..22 + len].copy_from_slice(&s[0..len]);
250 // set cursor to 1:15
251 self.buf[38..].copy_from_slice(&[0x80, 0xcf]);
252
253 self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await;
254 }
255}
diff --git a/examples/rp23/src/bin/pio_i2s.rs b/examples/rp23/src/bin/pio_i2s.rs
new file mode 100644
index 000000000..5a3bde759
--- /dev/null
+++ b/examples/rp23/src/bin/pio_i2s.rs
@@ -0,0 +1,140 @@
1//! This example shows generating audio and sending it to a connected i2s DAC using the PIO
2//! module of the RP2040.
3//!
4//! Connect the i2s DAC as follows:
5//! bclk : GPIO 18
6//! lrc : GPIO 19
7//! din : GPIO 20
8//! Then hold down the boot select button to trigger a rising triangle waveform.
9
10#![no_std]
11#![no_main]
12
13use core::mem;
14
15use embassy_executor::Spawner;
16use embassy_rp::block::ImageDef;
17use embassy_rp::peripherals::PIO0;
18use embassy_rp::pio::{Config, FifoJoin, InterruptHandler, Pio, ShiftConfig, ShiftDirection};
19use embassy_rp::{bind_interrupts, Peripheral};
20use fixed::traits::ToFixed;
21use static_cell::StaticCell;
22use {defmt_rtt as _, panic_probe as _};
23
24#[link_section = ".start_block"]
25#[used]
26pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
27
28// Program metadata for `picotool info`
29#[link_section = ".bi_entries"]
30#[used]
31pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
32 embassy_rp::binary_info_rp_cargo_bin_name!(),
33 embassy_rp::binary_info_rp_cargo_version!(),
34 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
35 embassy_rp::binary_info_rp_program_build_attribute!(),
36];
37
38bind_interrupts!(struct Irqs {
39 PIO0_IRQ_0 => InterruptHandler<PIO0>;
40});
41
42const SAMPLE_RATE: u32 = 48_000;
43
44#[embassy_executor::main]
45async fn main(_spawner: Spawner) {
46 let p = embassy_rp::init(Default::default());
47
48 // Setup pio state machine for i2s output
49 let mut pio = Pio::new(p.PIO0, Irqs);
50
51 #[rustfmt::skip]
52 let pio_program = pio_proc::pio_asm!(
53 ".side_set 2",
54 " set x, 14 side 0b01", // side 0bWB - W = Word Clock, B = Bit Clock
55 "left_data:",
56 " out pins, 1 side 0b00",
57 " jmp x-- left_data side 0b01",
58 " out pins 1 side 0b10",
59 " set x, 14 side 0b11",
60 "right_data:",
61 " out pins 1 side 0b10",
62 " jmp x-- right_data side 0b11",
63 " out pins 1 side 0b00",
64 );
65
66 let bit_clock_pin = p.PIN_18;
67 let left_right_clock_pin = p.PIN_19;
68 let data_pin = p.PIN_20;
69
70 let data_pin = pio.common.make_pio_pin(data_pin);
71 let bit_clock_pin = pio.common.make_pio_pin(bit_clock_pin);
72 let left_right_clock_pin = pio.common.make_pio_pin(left_right_clock_pin);
73
74 let cfg = {
75 let mut cfg = Config::default();
76 cfg.use_program(
77 &pio.common.load_program(&pio_program.program),
78 &[&bit_clock_pin, &left_right_clock_pin],
79 );
80 cfg.set_out_pins(&[&data_pin]);
81 const BIT_DEPTH: u32 = 16;
82 const CHANNELS: u32 = 2;
83 let clock_frequency = SAMPLE_RATE * BIT_DEPTH * CHANNELS;
84 cfg.clock_divider = (125_000_000. / clock_frequency as f64 / 2.).to_fixed();
85 cfg.shift_out = ShiftConfig {
86 threshold: 32,
87 direction: ShiftDirection::Left,
88 auto_fill: true,
89 };
90 // join fifos to have twice the time to start the next dma transfer
91 cfg.fifo_join = FifoJoin::TxOnly;
92 cfg
93 };
94 pio.sm0.set_config(&cfg);
95 pio.sm0.set_pin_dirs(
96 embassy_rp::pio::Direction::Out,
97 &[&data_pin, &left_right_clock_pin, &bit_clock_pin],
98 );
99
100 // create two audio buffers (back and front) which will take turns being
101 // filled with new audio data and being sent to the pio fifo using dma
102 const BUFFER_SIZE: usize = 960;
103 static DMA_BUFFER: StaticCell<[u32; BUFFER_SIZE * 2]> = StaticCell::new();
104 let dma_buffer = DMA_BUFFER.init_with(|| [0u32; BUFFER_SIZE * 2]);
105 let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE);
106
107 // start pio state machine
108 pio.sm0.set_enable(true);
109 let tx = pio.sm0.tx();
110 let mut dma_ref = p.DMA_CH0.into_ref();
111
112 let mut fade_value: i32 = 0;
113 let mut phase: i32 = 0;
114
115 loop {
116 // trigger transfer of front buffer data to the pio fifo
117 // but don't await the returned future, yet
118 let dma_future = tx.dma_push(dma_ref.reborrow(), front_buffer);
119
120 // fade in audio
121 let fade_target = i32::MAX;
122
123 // fill back buffer with fresh audio samples before awaiting the dma future
124 for s in back_buffer.iter_mut() {
125 // exponential approach of fade_value => fade_target
126 fade_value += (fade_target - fade_value) >> 14;
127 // generate triangle wave with amplitude and frequency based on fade value
128 phase = (phase + (fade_value >> 22)) & 0xffff;
129 let triangle_sample = (phase as i16 as i32).abs() - 16384;
130 let sample = (triangle_sample * (fade_value >> 15)) >> 16;
131 // duplicate mono sample into lower and upper half of dma word
132 *s = (sample as u16 as u32) * 0x10001;
133 }
134
135 // now await the dma future. once the dma finishes, the next buffer needs to be queued
136 // within DMA_DEPTH / SAMPLE_RATE = 8 / 48000 seconds = 166us
137 dma_future.await;
138 mem::swap(&mut back_buffer, &mut front_buffer);
139 }
140}
diff --git a/examples/rp23/src/bin/pio_pwm.rs b/examples/rp23/src/bin/pio_pwm.rs
new file mode 100644
index 000000000..7c5eefc45
--- /dev/null
+++ b/examples/rp23/src/bin/pio_pwm.rs
@@ -0,0 +1,133 @@
1//! This example shows how to create a pwm using the PIO module in the RP2040 chip.
2
3#![no_std]
4#![no_main]
5use core::time::Duration;
6
7use embassy_executor::Spawner;
8use embassy_rp::block::ImageDef;
9use embassy_rp::gpio::Level;
10use embassy_rp::peripherals::PIO0;
11use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine};
12use embassy_rp::{bind_interrupts, clocks};
13use embassy_time::Timer;
14use pio::InstructionOperands;
15use {defmt_rtt as _, panic_probe as _};
16
17#[link_section = ".start_block"]
18#[used]
19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
20
21// Program metadata for `picotool info`
22#[link_section = ".bi_entries"]
23#[used]
24pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
25 embassy_rp::binary_info_rp_cargo_bin_name!(),
26 embassy_rp::binary_info_rp_cargo_version!(),
27 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
28 embassy_rp::binary_info_rp_program_build_attribute!(),
29];
30
31const REFRESH_INTERVAL: u64 = 20000;
32
33bind_interrupts!(struct Irqs {
34 PIO0_IRQ_0 => InterruptHandler<PIO0>;
35});
36
37pub fn to_pio_cycles(duration: Duration) -> u32 {
38 (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
39}
40
41pub struct PwmPio<'d, T: Instance, const SM: usize> {
42 sm: StateMachine<'d, T, SM>,
43}
44
45impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
46 pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
47 let prg = pio_proc::pio_asm!(
48 ".side_set 1 opt"
49 "pull noblock side 0"
50 "mov x, osr"
51 "mov y, isr"
52 "countloop:"
53 "jmp x!=y noset"
54 "jmp skip side 1"
55 "noset:"
56 "nop"
57 "skip:"
58 "jmp y-- countloop"
59 );
60
61 pio.load_program(&prg.program);
62 let pin = pio.make_pio_pin(pin);
63 sm.set_pins(Level::High, &[&pin]);
64 sm.set_pin_dirs(Direction::Out, &[&pin]);
65
66 let mut cfg = Config::default();
67 cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
68
69 sm.set_config(&cfg);
70
71 Self { sm }
72 }
73
74 pub fn start(&mut self) {
75 self.sm.set_enable(true);
76 }
77
78 pub fn stop(&mut self) {
79 self.sm.set_enable(false);
80 }
81
82 pub fn set_period(&mut self, duration: Duration) {
83 let is_enabled = self.sm.is_enabled();
84 while !self.sm.tx().empty() {} // Make sure that the queue is empty
85 self.sm.set_enable(false);
86 self.sm.tx().push(to_pio_cycles(duration));
87 unsafe {
88 self.sm.exec_instr(
89 InstructionOperands::PULL {
90 if_empty: false,
91 block: false,
92 }
93 .encode(),
94 );
95 self.sm.exec_instr(
96 InstructionOperands::OUT {
97 destination: ::pio::OutDestination::ISR,
98 bit_count: 32,
99 }
100 .encode(),
101 );
102 };
103 if is_enabled {
104 self.sm.set_enable(true) // Enable if previously enabled
105 }
106 }
107
108 pub fn set_level(&mut self, level: u32) {
109 self.sm.tx().push(level);
110 }
111
112 pub fn write(&mut self, duration: Duration) {
113 self.set_level(to_pio_cycles(duration));
114 }
115}
116
117#[embassy_executor::main]
118async fn main(_spawner: Spawner) {
119 let p = embassy_rp::init(Default::default());
120 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
121
122 // Note that PIN_25 is the led pin on the Pico
123 let mut pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_25);
124 pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL));
125 pwm_pio.start();
126
127 let mut duration = 0;
128 loop {
129 duration = (duration + 1) % 1000;
130 pwm_pio.write(Duration::from_micros(duration));
131 Timer::after_millis(1).await;
132 }
133}
diff --git a/examples/rp23/src/bin/pio_rotary_encoder.rs b/examples/rp23/src/bin/pio_rotary_encoder.rs
new file mode 100644
index 000000000..287992a83
--- /dev/null
+++ b/examples/rp23/src/bin/pio_rotary_encoder.rs
@@ -0,0 +1,95 @@
1//! This example shows how to use the PIO module in the RP2040 to read a quadrature rotary encoder.
2
3#![no_std]
4#![no_main]
5
6use defmt::info;
7use embassy_executor::Spawner;
8use embassy_rp::block::ImageDef;
9use embassy_rp::gpio::Pull;
10use embassy_rp::peripherals::PIO0;
11use embassy_rp::{bind_interrupts, pio};
12use fixed::traits::ToFixed;
13use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine};
14use {defmt_rtt as _, panic_probe as _};
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30bind_interrupts!(struct Irqs {
31 PIO0_IRQ_0 => InterruptHandler<PIO0>;
32});
33
34pub struct PioEncoder<'d, T: Instance, const SM: usize> {
35 sm: StateMachine<'d, T, SM>,
36}
37
38impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> {
39 pub fn new(
40 pio: &mut Common<'d, T>,
41 mut sm: StateMachine<'d, T, SM>,
42 pin_a: impl PioPin,
43 pin_b: impl PioPin,
44 ) -> Self {
45 let mut pin_a = pio.make_pio_pin(pin_a);
46 let mut pin_b = pio.make_pio_pin(pin_b);
47 pin_a.set_pull(Pull::Up);
48 pin_b.set_pull(Pull::Up);
49 sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]);
50
51 let prg = pio_proc::pio_asm!("wait 1 pin 1", "wait 0 pin 1", "in pins, 2", "push",);
52
53 let mut cfg = Config::default();
54 cfg.set_in_pins(&[&pin_a, &pin_b]);
55 cfg.fifo_join = FifoJoin::RxOnly;
56 cfg.shift_in.direction = ShiftDirection::Left;
57 cfg.clock_divider = 10_000.to_fixed();
58 cfg.use_program(&pio.load_program(&prg.program), &[]);
59 sm.set_config(&cfg);
60 sm.set_enable(true);
61 Self { sm }
62 }
63
64 pub async fn read(&mut self) -> Direction {
65 loop {
66 match self.sm.rx().wait_pull().await {
67 0 => return Direction::CounterClockwise,
68 1 => return Direction::Clockwise,
69 _ => {}
70 }
71 }
72 }
73}
74
75pub enum Direction {
76 Clockwise,
77 CounterClockwise,
78}
79
80#[embassy_executor::main]
81async fn main(_spawner: Spawner) {
82 let p = embassy_rp::init(Default::default());
83 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
84
85 let mut encoder = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5);
86
87 let mut count = 0;
88 loop {
89 info!("Count: {}", count);
90 count += match encoder.read().await {
91 Direction::Clockwise => 1,
92 Direction::CounterClockwise => -1,
93 };
94 }
95}
diff --git a/examples/rp23/src/bin/pio_servo.rs b/examples/rp23/src/bin/pio_servo.rs
new file mode 100644
index 000000000..1dec86927
--- /dev/null
+++ b/examples/rp23/src/bin/pio_servo.rs
@@ -0,0 +1,223 @@
1//! This example shows how to create a pwm using the PIO module in the RP2040 chip.
2
3#![no_std]
4#![no_main]
5use core::time::Duration;
6
7use embassy_executor::Spawner;
8use embassy_rp::block::ImageDef;
9use embassy_rp::gpio::Level;
10use embassy_rp::peripherals::PIO0;
11use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine};
12use embassy_rp::{bind_interrupts, clocks};
13use embassy_time::Timer;
14use pio::InstructionOperands;
15use {defmt_rtt as _, panic_probe as _};
16
17#[link_section = ".start_block"]
18#[used]
19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
20
21// Program metadata for `picotool info`
22#[link_section = ".bi_entries"]
23#[used]
24pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
25 embassy_rp::binary_info_rp_cargo_bin_name!(),
26 embassy_rp::binary_info_rp_cargo_version!(),
27 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
28 embassy_rp::binary_info_rp_program_build_attribute!(),
29];
30
31const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo
32const DEFAULT_MAX_PULSE_WIDTH: u64 = 2000; // uncalibrated default, the longest duty cycle sent to a servo
33const DEFAULT_MAX_DEGREE_ROTATION: u64 = 160; // 160 degrees is typical
34const REFRESH_INTERVAL: u64 = 20000; // The period of each cycle
35
36bind_interrupts!(struct Irqs {
37 PIO0_IRQ_0 => InterruptHandler<PIO0>;
38});
39
40pub fn to_pio_cycles(duration: Duration) -> u32 {
41 (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
42}
43
44pub struct PwmPio<'d, T: Instance, const SM: usize> {
45 sm: StateMachine<'d, T, SM>,
46}
47
48impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
49 pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
50 let prg = pio_proc::pio_asm!(
51 ".side_set 1 opt"
52 "pull noblock side 0"
53 "mov x, osr"
54 "mov y, isr"
55 "countloop:"
56 "jmp x!=y noset"
57 "jmp skip side 1"
58 "noset:"
59 "nop"
60 "skip:"
61 "jmp y-- countloop"
62 );
63
64 pio.load_program(&prg.program);
65 let pin = pio.make_pio_pin(pin);
66 sm.set_pins(Level::High, &[&pin]);
67 sm.set_pin_dirs(Direction::Out, &[&pin]);
68
69 let mut cfg = Config::default();
70 cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
71
72 sm.set_config(&cfg);
73
74 Self { sm }
75 }
76
77 pub fn start(&mut self) {
78 self.sm.set_enable(true);
79 }
80
81 pub fn stop(&mut self) {
82 self.sm.set_enable(false);
83 }
84
85 pub fn set_period(&mut self, duration: Duration) {
86 let is_enabled = self.sm.is_enabled();
87 while !self.sm.tx().empty() {} // Make sure that the queue is empty
88 self.sm.set_enable(false);
89 self.sm.tx().push(to_pio_cycles(duration));
90 unsafe {
91 self.sm.exec_instr(
92 InstructionOperands::PULL {
93 if_empty: false,
94 block: false,
95 }
96 .encode(),
97 );
98 self.sm.exec_instr(
99 InstructionOperands::OUT {
100 destination: ::pio::OutDestination::ISR,
101 bit_count: 32,
102 }
103 .encode(),
104 );
105 };
106 if is_enabled {
107 self.sm.set_enable(true) // Enable if previously enabled
108 }
109 }
110
111 pub fn set_level(&mut self, level: u32) {
112 self.sm.tx().push(level);
113 }
114
115 pub fn write(&mut self, duration: Duration) {
116 self.set_level(to_pio_cycles(duration));
117 }
118}
119
120pub struct ServoBuilder<'d, T: Instance, const SM: usize> {
121 pwm: PwmPio<'d, T, SM>,
122 period: Duration,
123 min_pulse_width: Duration,
124 max_pulse_width: Duration,
125 max_degree_rotation: u64,
126}
127
128impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> {
129 pub fn new(pwm: PwmPio<'d, T, SM>) -> Self {
130 Self {
131 pwm,
132 period: Duration::from_micros(REFRESH_INTERVAL),
133 min_pulse_width: Duration::from_micros(DEFAULT_MIN_PULSE_WIDTH),
134 max_pulse_width: Duration::from_micros(DEFAULT_MAX_PULSE_WIDTH),
135 max_degree_rotation: DEFAULT_MAX_DEGREE_ROTATION,
136 }
137 }
138
139 pub fn set_period(mut self, duration: Duration) -> Self {
140 self.period = duration;
141 self
142 }
143
144 pub fn set_min_pulse_width(mut self, duration: Duration) -> Self {
145 self.min_pulse_width = duration;
146 self
147 }
148
149 pub fn set_max_pulse_width(mut self, duration: Duration) -> Self {
150 self.max_pulse_width = duration;
151 self
152 }
153
154 pub fn set_max_degree_rotation(mut self, degree: u64) -> Self {
155 self.max_degree_rotation = degree;
156 self
157 }
158
159 pub fn build(mut self) -> Servo<'d, T, SM> {
160 self.pwm.set_period(self.period);
161 Servo {
162 pwm: self.pwm,
163 min_pulse_width: self.min_pulse_width,
164 max_pulse_width: self.max_pulse_width,
165 max_degree_rotation: self.max_degree_rotation,
166 }
167 }
168}
169
170pub struct Servo<'d, T: Instance, const SM: usize> {
171 pwm: PwmPio<'d, T, SM>,
172 min_pulse_width: Duration,
173 max_pulse_width: Duration,
174 max_degree_rotation: u64,
175}
176
177impl<'d, T: Instance, const SM: usize> Servo<'d, T, SM> {
178 pub fn start(&mut self) {
179 self.pwm.start();
180 }
181
182 pub fn stop(&mut self) {
183 self.pwm.stop();
184 }
185
186 pub fn write_time(&mut self, duration: Duration) {
187 self.pwm.write(duration);
188 }
189
190 pub fn rotate(&mut self, degree: u64) {
191 let degree_per_nano_second = (self.max_pulse_width.as_nanos() as u64 - self.min_pulse_width.as_nanos() as u64)
192 / self.max_degree_rotation;
193 let mut duration =
194 Duration::from_nanos(degree * degree_per_nano_second + self.min_pulse_width.as_nanos() as u64);
195 if self.max_pulse_width < duration {
196 duration = self.max_pulse_width;
197 }
198
199 self.pwm.write(duration);
200 }
201}
202
203#[embassy_executor::main]
204async fn main(_spawner: Spawner) {
205 let p = embassy_rp::init(Default::default());
206 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
207
208 let pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_1);
209 let mut servo = ServoBuilder::new(pwm_pio)
210 .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo
211 .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment.
212 .set_max_pulse_width(Duration::from_micros(2600)) // Along with this value.
213 .build();
214
215 servo.start();
216
217 let mut degree = 0;
218 loop {
219 degree = (degree + 1) % 120;
220 servo.rotate(degree);
221 Timer::after_millis(50).await;
222 }
223}
diff --git a/examples/rp23/src/bin/pio_stepper.rs b/examples/rp23/src/bin/pio_stepper.rs
new file mode 100644
index 000000000..8b52dc37a
--- /dev/null
+++ b/examples/rp23/src/bin/pio_stepper.rs
@@ -0,0 +1,183 @@
1//! This example shows how to use the PIO module in the RP2040 to implement a stepper motor driver
2//! for a 5-wire stepper such as the 28BYJ-48. You can halt an ongoing rotation by dropping the future.
3
4#![no_std]
5#![no_main]
6use core::mem::{self, MaybeUninit};
7
8use defmt::info;
9use embassy_executor::Spawner;
10use embassy_rp::bind_interrupts;
11use embassy_rp::block::ImageDef;
12use embassy_rp::peripherals::PIO0;
13use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Irq, Pio, PioPin, StateMachine};
14use embassy_time::{with_timeout, Duration, Timer};
15use fixed::traits::ToFixed;
16use fixed::types::extra::U8;
17use fixed::FixedU32;
18use {defmt_rtt as _, panic_probe as _};
19
20#[link_section = ".start_block"]
21#[used]
22pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
23
24// Program metadata for `picotool info`
25#[link_section = ".bi_entries"]
26#[used]
27pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
28 embassy_rp::binary_info_rp_cargo_bin_name!(),
29 embassy_rp::binary_info_rp_cargo_version!(),
30 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
31 embassy_rp::binary_info_rp_program_build_attribute!(),
32];
33
34bind_interrupts!(struct Irqs {
35 PIO0_IRQ_0 => InterruptHandler<PIO0>;
36});
37
38pub struct PioStepper<'d, T: Instance, const SM: usize> {
39 irq: Irq<'d, T, SM>,
40 sm: StateMachine<'d, T, SM>,
41}
42
43impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> {
44 pub fn new(
45 pio: &mut Common<'d, T>,
46 mut sm: StateMachine<'d, T, SM>,
47 irq: Irq<'d, T, SM>,
48 pin0: impl PioPin,
49 pin1: impl PioPin,
50 pin2: impl PioPin,
51 pin3: impl PioPin,
52 ) -> Self {
53 let prg = pio_proc::pio_asm!(
54 "pull block",
55 "mov x, osr",
56 "pull block",
57 "mov y, osr",
58 "jmp !x end",
59 "loop:",
60 "jmp !osre step",
61 "mov osr, y",
62 "step:",
63 "out pins, 4 [31]"
64 "jmp x-- loop",
65 "end:",
66 "irq 0 rel"
67 );
68 let pin0 = pio.make_pio_pin(pin0);
69 let pin1 = pio.make_pio_pin(pin1);
70 let pin2 = pio.make_pio_pin(pin2);
71 let pin3 = pio.make_pio_pin(pin3);
72 sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]);
73 let mut cfg = Config::default();
74 cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]);
75 cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed();
76 cfg.use_program(&pio.load_program(&prg.program), &[]);
77 sm.set_config(&cfg);
78 sm.set_enable(true);
79 Self { irq, sm }
80 }
81
82 // Set pulse frequency
83 pub fn set_frequency(&mut self, freq: u32) {
84 let clock_divider: FixedU32<U8> = (125_000_000 / (freq * 136)).to_fixed();
85 assert!(clock_divider <= 65536, "clkdiv must be <= 65536");
86 assert!(clock_divider >= 1, "clkdiv must be >= 1");
87 self.sm.set_clock_divider(clock_divider);
88 self.sm.clkdiv_restart();
89 }
90
91 // Full step, one phase
92 pub async fn step(&mut self, steps: i32) {
93 if steps > 0 {
94 self.run(steps, 0b1000_0100_0010_0001_1000_0100_0010_0001).await
95 } else {
96 self.run(-steps, 0b0001_0010_0100_1000_0001_0010_0100_1000).await
97 }
98 }
99
100 // Full step, two phase
101 pub async fn step2(&mut self, steps: i32) {
102 if steps > 0 {
103 self.run(steps, 0b1001_1100_0110_0011_1001_1100_0110_0011).await
104 } else {
105 self.run(-steps, 0b0011_0110_1100_1001_0011_0110_1100_1001).await
106 }
107 }
108
109 // Half step
110 pub async fn step_half(&mut self, steps: i32) {
111 if steps > 0 {
112 self.run(steps, 0b1001_1000_1100_0100_0110_0010_0011_0001).await
113 } else {
114 self.run(-steps, 0b0001_0011_0010_0110_0100_1100_1000_1001).await
115 }
116 }
117
118 async fn run(&mut self, steps: i32, pattern: u32) {
119 self.sm.tx().wait_push(steps as u32).await;
120 self.sm.tx().wait_push(pattern).await;
121 let drop = OnDrop::new(|| {
122 self.sm.clear_fifos();
123 unsafe {
124 self.sm.exec_instr(
125 pio::InstructionOperands::JMP {
126 address: 0,
127 condition: pio::JmpCondition::Always,
128 }
129 .encode(),
130 );
131 }
132 });
133 self.irq.wait().await;
134 drop.defuse();
135 }
136}
137
138struct OnDrop<F: FnOnce()> {
139 f: MaybeUninit<F>,
140}
141
142impl<F: FnOnce()> OnDrop<F> {
143 pub fn new(f: F) -> Self {
144 Self { f: MaybeUninit::new(f) }
145 }
146
147 pub fn defuse(self) {
148 mem::forget(self)
149 }
150}
151
152impl<F: FnOnce()> Drop for OnDrop<F> {
153 fn drop(&mut self) {
154 unsafe { self.f.as_ptr().read()() }
155 }
156}
157
158#[embassy_executor::main]
159async fn main(_spawner: Spawner) {
160 let p = embassy_rp::init(Default::default());
161 let Pio {
162 mut common, irq0, sm0, ..
163 } = Pio::new(p.PIO0, Irqs);
164
165 let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7);
166 stepper.set_frequency(120);
167 loop {
168 info!("CW full steps");
169 stepper.step(1000).await;
170
171 info!("CCW full steps, drop after 1 sec");
172 if let Err(_) = with_timeout(Duration::from_secs(1), stepper.step(i32::MIN)).await {
173 info!("Time's up!");
174 Timer::after(Duration::from_secs(1)).await;
175 }
176
177 info!("CW half steps");
178 stepper.step_half(1000).await;
179
180 info!("CCW half steps");
181 stepper.step_half(-1000).await;
182 }
183}
diff --git a/examples/rp23/src/bin/pio_ws2812.rs b/examples/rp23/src/bin/pio_ws2812.rs
new file mode 100644
index 000000000..99d9cf7e6
--- /dev/null
+++ b/examples/rp23/src/bin/pio_ws2812.rs
@@ -0,0 +1,176 @@
1//! This example shows powerful PIO module in the RP2040 chip to communicate with WS2812 LED modules.
2//! See (https://www.sparkfun.com/categories/tags/ws2812)
3
4#![no_std]
5#![no_main]
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_rp::block::ImageDef;
10use embassy_rp::dma::{AnyChannel, Channel};
11use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{
13 Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
14};
15use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef};
16use embassy_time::{Duration, Ticker, Timer};
17use fixed::types::U24F8;
18use fixed_macro::fixed;
19use smart_leds::RGB8;
20use {defmt_rtt as _, panic_probe as _};
21
22#[link_section = ".start_block"]
23#[used]
24pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
25
26// Program metadata for `picotool info`
27#[link_section = ".bi_entries"]
28#[used]
29pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
30 embassy_rp::binary_info_rp_cargo_bin_name!(),
31 embassy_rp::binary_info_rp_cargo_version!(),
32 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
33 embassy_rp::binary_info_rp_program_build_attribute!(),
34];
35
36bind_interrupts!(struct Irqs {
37 PIO0_IRQ_0 => InterruptHandler<PIO0>;
38});
39
40pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> {
41 dma: PeripheralRef<'d, AnyChannel>,
42 sm: StateMachine<'d, P, S>,
43}
44
45impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> {
46 pub fn new(
47 pio: &mut Common<'d, P>,
48 mut sm: StateMachine<'d, P, S>,
49 dma: impl Peripheral<P = impl Channel> + 'd,
50 pin: impl PioPin,
51 ) -> Self {
52 into_ref!(dma);
53
54 // Setup sm0
55
56 // prepare the PIO program
57 let side_set = pio::SideSet::new(false, 1, false);
58 let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set);
59
60 const T1: u8 = 2; // start bit
61 const T2: u8 = 5; // data bit
62 const T3: u8 = 3; // stop bit
63 const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32;
64
65 let mut wrap_target = a.label();
66 let mut wrap_source = a.label();
67 let mut do_zero = a.label();
68 a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0);
69 a.bind(&mut wrap_target);
70 // Do stop bit
71 a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0);
72 // Do start bit
73 a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1);
74 // Do data bit = 1
75 a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1);
76 a.bind(&mut do_zero);
77 // Do data bit = 0
78 a.nop_with_delay_and_side_set(T2 - 1, 0);
79 a.bind(&mut wrap_source);
80
81 let prg = a.assemble_with_wrap(wrap_source, wrap_target);
82 let mut cfg = Config::default();
83
84 // Pin config
85 let out_pin = pio.make_pio_pin(pin);
86 cfg.set_out_pins(&[&out_pin]);
87 cfg.set_set_pins(&[&out_pin]);
88
89 cfg.use_program(&pio.load_program(&prg), &[&out_pin]);
90
91 // Clock config, measured in kHz to avoid overflows
92 // TODO CLOCK_FREQ should come from embassy_rp
93 let clock_freq = U24F8::from_num(clocks::clk_sys_freq() / 1000);
94 let ws2812_freq = fixed!(800: U24F8);
95 let bit_freq = ws2812_freq * CYCLES_PER_BIT;
96 cfg.clock_divider = clock_freq / bit_freq;
97
98 // FIFO config
99 cfg.fifo_join = FifoJoin::TxOnly;
100 cfg.shift_out = ShiftConfig {
101 auto_fill: true,
102 threshold: 24,
103 direction: ShiftDirection::Left,
104 };
105
106 sm.set_config(&cfg);
107 sm.set_enable(true);
108
109 Self {
110 dma: dma.map_into(),
111 sm,
112 }
113 }
114
115 pub async fn write(&mut self, colors: &[RGB8; N]) {
116 // Precompute the word bytes from the colors
117 let mut words = [0u32; N];
118 for i in 0..N {
119 let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8);
120 words[i] = word;
121 }
122
123 // DMA transfer
124 self.sm.tx().dma_push(self.dma.reborrow(), &words).await;
125
126 Timer::after_micros(55).await;
127 }
128}
129
130/// Input a value 0 to 255 to get a color value
131/// The colours are a transition r - g - b - back to r.
132fn wheel(mut wheel_pos: u8) -> RGB8 {
133 wheel_pos = 255 - wheel_pos;
134 if wheel_pos < 85 {
135 return (255 - wheel_pos * 3, 0, wheel_pos * 3).into();
136 }
137 if wheel_pos < 170 {
138 wheel_pos -= 85;
139 return (0, wheel_pos * 3, 255 - wheel_pos * 3).into();
140 }
141 wheel_pos -= 170;
142 (wheel_pos * 3, 255 - wheel_pos * 3, 0).into()
143}
144
145#[embassy_executor::main]
146async fn main(_spawner: Spawner) {
147 info!("Start");
148 let p = embassy_rp::init(Default::default());
149
150 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
151
152 // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit
153 // feather boards for the 2040 both have one built in.
154 const NUM_LEDS: usize = 1;
155 let mut data = [RGB8::default(); NUM_LEDS];
156
157 // Common neopixel pins:
158 // Thing plus: 8
159 // Adafruit Feather: 16; Adafruit Feather+RFM95: 4
160 let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16);
161
162 // Loop forever making RGB values and pushing them out to the WS2812.
163 let mut ticker = Ticker::every(Duration::from_millis(10));
164 loop {
165 for j in 0..(256 * 5) {
166 debug!("New Colors:");
167 for i in 0..NUM_LEDS {
168 data[i] = wheel((((i * 256) as u16 / NUM_LEDS as u16 + j as u16) & 255) as u8);
169 debug!("R: {} G: {} B: {}", data[i].r, data[i].g, data[i].b);
170 }
171 ws2812.write(&data).await;
172
173 ticker.next().await;
174 }
175 }
176}
diff --git a/examples/rp23/src/bin/pwm.rs b/examples/rp23/src/bin/pwm.rs
new file mode 100644
index 000000000..4cd3b3eb4
--- /dev/null
+++ b/examples/rp23/src/bin/pwm.rs
@@ -0,0 +1,44 @@
1//! This example shows how to use PWM (Pulse Width Modulation) in the RP2040 chip.
2//!
3//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::pwm::{Config, Pwm};
12use embassy_time::Timer;
13use {defmt_rtt as _, panic_probe as _};
14
15#[link_section = ".start_block"]
16#[used]
17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
18
19// Program metadata for `picotool info`
20#[link_section = ".bi_entries"]
21#[used]
22pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
23 embassy_rp::binary_info_rp_cargo_bin_name!(),
24 embassy_rp::binary_info_rp_cargo_version!(),
25 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
26 embassy_rp::binary_info_rp_program_build_attribute!(),
27];
28
29#[embassy_executor::main]
30async fn main(_spawner: Spawner) {
31 let p = embassy_rp::init(Default::default());
32
33 let mut c: Config = Default::default();
34 c.top = 0x8000;
35 c.compare_b = 8;
36 let mut pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, c.clone());
37
38 loop {
39 info!("current LED duty cycle: {}/32768", c.compare_b);
40 Timer::after_secs(1).await;
41 c.compare_b = c.compare_b.rotate_left(4);
42 pwm.set_config(&c);
43 }
44}
diff --git a/examples/rp23/src/bin/pwm_input.rs b/examples/rp23/src/bin/pwm_input.rs
new file mode 100644
index 000000000..b75d04963
--- /dev/null
+++ b/examples/rp23/src/bin/pwm_input.rs
@@ -0,0 +1,41 @@
1//! This example shows how to use the PWM module to measure the frequency of an input signal.
2
3#![no_std]
4#![no_main]
5
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_rp::block::ImageDef;
9use embassy_rp::gpio::Pull;
10use embassy_rp::pwm::{Config, InputMode, Pwm};
11use embassy_time::{Duration, Ticker};
12use {defmt_rtt as _, panic_probe as _};
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28#[embassy_executor::main]
29async fn main(_spawner: Spawner) {
30 let p = embassy_rp::init(Default::default());
31
32 let cfg: Config = Default::default();
33 let pwm = Pwm::new_input(p.PWM_SLICE2, p.PIN_5, Pull::None, InputMode::RisingEdge, cfg);
34
35 let mut ticker = Ticker::every(Duration::from_secs(1));
36 loop {
37 info!("Input frequency: {} Hz", pwm.counter());
38 pwm.set_counter(0);
39 ticker.next().await;
40 }
41}
diff --git a/examples/rp23/src/bin/rosc.rs b/examples/rp23/src/bin/rosc.rs
new file mode 100644
index 000000000..28c778f51
--- /dev/null
+++ b/examples/rp23/src/bin/rosc.rs
@@ -0,0 +1,46 @@
1//! This example test the RP Pico on board LED.
2//!
3//! It does not work with the RP Pico W board. See wifi_blinky.rs.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::{clocks, gpio};
12use embassy_time::Timer;
13use gpio::{Level, Output};
14use {defmt_rtt as _, panic_probe as _};
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30#[embassy_executor::main]
31async fn main(_spawner: Spawner) {
32 let mut config = embassy_rp::config::Config::default();
33 config.clocks = clocks::ClockConfig::rosc();
34 let p = embassy_rp::init(config);
35 let mut led = Output::new(p.PIN_25, Level::Low);
36
37 loop {
38 info!("led on!");
39 led.set_high();
40 Timer::after_secs(1).await;
41
42 info!("led off!");
43 led.set_low();
44 Timer::after_secs(1).await;
45 }
46}
diff --git a/examples/rp23/src/bin/shared_bus.rs b/examples/rp23/src/bin/shared_bus.rs
new file mode 100644
index 000000000..00e65f80d
--- /dev/null
+++ b/examples/rp23/src/bin/shared_bus.rs
@@ -0,0 +1,130 @@
1//! This example shows how to share (async) I2C and SPI buses between multiple devices.
2
3#![no_std]
4#![no_main]
5
6use defmt::*;
7use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice;
8use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
9use embassy_executor::Spawner;
10use embassy_rp::bind_interrupts;
11use embassy_rp::block::ImageDef;
12use embassy_rp::gpio::{AnyPin, Level, Output};
13use embassy_rp::i2c::{self, I2c, InterruptHandler};
14use embassy_rp::peripherals::{I2C1, SPI1};
15use embassy_rp::spi::{self, Spi};
16use embassy_sync::blocking_mutex::raw::NoopRawMutex;
17use embassy_sync::mutex::Mutex;
18use embassy_time::Timer;
19use static_cell::StaticCell;
20use {defmt_rtt as _, panic_probe as _};
21
22#[link_section = ".start_block"]
23#[used]
24pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
25
26// Program metadata for `picotool info`
27#[link_section = ".bi_entries"]
28#[used]
29pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
30 embassy_rp::binary_info_rp_cargo_bin_name!(),
31 embassy_rp::binary_info_rp_cargo_version!(),
32 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
33 embassy_rp::binary_info_rp_program_build_attribute!(),
34];
35
36type Spi1Bus = Mutex<NoopRawMutex, Spi<'static, SPI1, spi::Async>>;
37type I2c1Bus = Mutex<NoopRawMutex, I2c<'static, I2C1, i2c::Async>>;
38
39bind_interrupts!(struct Irqs {
40 I2C1_IRQ => InterruptHandler<I2C1>;
41});
42
43#[embassy_executor::main]
44async fn main(spawner: Spawner) {
45 let p = embassy_rp::init(Default::default());
46 info!("Here we go!");
47
48 // Shared I2C bus
49 let i2c = I2c::new_async(p.I2C1, p.PIN_15, p.PIN_14, Irqs, i2c::Config::default());
50 static I2C_BUS: StaticCell<I2c1Bus> = StaticCell::new();
51 let i2c_bus = I2C_BUS.init(Mutex::new(i2c));
52
53 spawner.must_spawn(i2c_task_a(i2c_bus));
54 spawner.must_spawn(i2c_task_b(i2c_bus));
55
56 // Shared SPI bus
57 let spi_cfg = spi::Config::default();
58 let spi = Spi::new(p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, p.DMA_CH0, p.DMA_CH1, spi_cfg);
59 static SPI_BUS: StaticCell<Spi1Bus> = StaticCell::new();
60 let spi_bus = SPI_BUS.init(Mutex::new(spi));
61
62 // Chip select pins for the SPI devices
63 let cs_a = Output::new(AnyPin::from(p.PIN_0), Level::High);
64 let cs_b = Output::new(AnyPin::from(p.PIN_1), Level::High);
65
66 spawner.must_spawn(spi_task_a(spi_bus, cs_a));
67 spawner.must_spawn(spi_task_b(spi_bus, cs_b));
68}
69
70#[embassy_executor::task]
71async fn i2c_task_a(i2c_bus: &'static I2c1Bus) {
72 let i2c_dev = I2cDevice::new(i2c_bus);
73 let _sensor = DummyI2cDeviceDriver::new(i2c_dev, 0xC0);
74 loop {
75 info!("i2c task A");
76 Timer::after_secs(1).await;
77 }
78}
79
80#[embassy_executor::task]
81async fn i2c_task_b(i2c_bus: &'static I2c1Bus) {
82 let i2c_dev = I2cDevice::new(i2c_bus);
83 let _sensor = DummyI2cDeviceDriver::new(i2c_dev, 0xDE);
84 loop {
85 info!("i2c task B");
86 Timer::after_secs(1).await;
87 }
88}
89
90#[embassy_executor::task]
91async fn spi_task_a(spi_bus: &'static Spi1Bus, cs: Output<'static>) {
92 let spi_dev = SpiDevice::new(spi_bus, cs);
93 let _sensor = DummySpiDeviceDriver::new(spi_dev);
94 loop {
95 info!("spi task A");
96 Timer::after_secs(1).await;
97 }
98}
99
100#[embassy_executor::task]
101async fn spi_task_b(spi_bus: &'static Spi1Bus, cs: Output<'static>) {
102 let spi_dev = SpiDevice::new(spi_bus, cs);
103 let _sensor = DummySpiDeviceDriver::new(spi_dev);
104 loop {
105 info!("spi task B");
106 Timer::after_secs(1).await;
107 }
108}
109
110// Dummy I2C device driver, using `embedded-hal-async`
111struct DummyI2cDeviceDriver<I2C: embedded_hal_async::i2c::I2c> {
112 _i2c: I2C,
113}
114
115impl<I2C: embedded_hal_async::i2c::I2c> DummyI2cDeviceDriver<I2C> {
116 fn new(i2c_dev: I2C, _address: u8) -> Self {
117 Self { _i2c: i2c_dev }
118 }
119}
120
121// Dummy SPI device driver, using `embedded-hal-async`
122struct DummySpiDeviceDriver<SPI: embedded_hal_async::spi::SpiDevice> {
123 _spi: SPI,
124}
125
126impl<SPI: embedded_hal_async::spi::SpiDevice> DummySpiDeviceDriver<SPI> {
127 fn new(spi_dev: SPI) -> Self {
128 Self { _spi: spi_dev }
129 }
130}
diff --git a/examples/rp23/src/bin/sharing.rs b/examples/rp23/src/bin/sharing.rs
new file mode 100644
index 000000000..b5ef08147
--- /dev/null
+++ b/examples/rp23/src/bin/sharing.rs
@@ -0,0 +1,165 @@
1//! This example shows some common strategies for sharing resources between tasks.
2//!
3//! We demonstrate five different ways of sharing, covering different use cases:
4//! - Atomics: This method is used for simple values, such as bool and u8..u32
5//! - Blocking Mutex: This is used for sharing non-async things, using Cell/RefCell for interior mutability.
6//! - Async Mutex: This is used for sharing async resources, where you need to hold the lock across await points.
7//! The async Mutex has interior mutability built-in, so no RefCell is needed.
8//! - Cell: For sharing Copy types between tasks running on the same executor.
9//! - RefCell: When you want &mut access to a value shared between tasks running on the same executor.
10//!
11//! More information: https://embassy.dev/book/#_sharing_peripherals_between_tasks
12
13#![no_std]
14#![no_main]
15
16use core::cell::{Cell, RefCell};
17use core::sync::atomic::{AtomicU32, Ordering};
18
19use cortex_m_rt::entry;
20use defmt::info;
21use embassy_executor::{Executor, InterruptExecutor};
22use embassy_rp::block::ImageDef;
23use embassy_rp::clocks::RoscRng;
24use embassy_rp::interrupt::{InterruptExt, Priority};
25use embassy_rp::peripherals::UART0;
26use embassy_rp::uart::{self, InterruptHandler, UartTx};
27use embassy_rp::{bind_interrupts, interrupt};
28use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
29use embassy_sync::{blocking_mutex, mutex};
30use embassy_time::{Duration, Ticker};
31use rand::RngCore;
32use static_cell::{ConstStaticCell, StaticCell};
33use {defmt_rtt as _, panic_probe as _};
34
35#[link_section = ".start_block"]
36#[used]
37pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
38
39// Program metadata for `picotool info`
40#[link_section = ".bi_entries"]
41#[used]
42pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
43 embassy_rp::binary_info_rp_cargo_bin_name!(),
44 embassy_rp::binary_info_rp_cargo_version!(),
45 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
46 embassy_rp::binary_info_rp_program_build_attribute!(),
47];
48
49type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, UART0, uart::Async>>;
50
51struct MyType {
52 inner: u32,
53}
54
55static EXECUTOR_HI: InterruptExecutor = InterruptExecutor::new();
56static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();
57
58// Use Atomics for simple values
59static ATOMIC: AtomicU32 = AtomicU32::new(0);
60
61// Use blocking Mutex with Cell/RefCell for sharing non-async things
62static MUTEX_BLOCKING: blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<MyType>> =
63 blocking_mutex::Mutex::new(RefCell::new(MyType { inner: 0 }));
64
65bind_interrupts!(struct Irqs {
66 UART0_IRQ => InterruptHandler<UART0>;
67});
68
69#[interrupt]
70unsafe fn SWI_IRQ_0() {
71 EXECUTOR_HI.on_interrupt()
72}
73
74#[entry]
75fn main() -> ! {
76 let p = embassy_rp::init(Default::default());
77 info!("Here we go!");
78
79 let uart = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, uart::Config::default());
80 // Use the async Mutex for sharing async things (built-in interior mutability)
81 static UART: StaticCell<UartAsyncMutex> = StaticCell::new();
82 let uart = UART.init(mutex::Mutex::new(uart));
83
84 // High-priority executor: runs in interrupt mode
85 interrupt::SWI_IRQ_0.set_priority(Priority::P3);
86 let spawner = EXECUTOR_HI.start(interrupt::SWI_IRQ_0);
87 spawner.must_spawn(task_a(uart));
88
89 // Low priority executor: runs in thread mode
90 let executor = EXECUTOR_LOW.init(Executor::new());
91 executor.run(|spawner| {
92 // No Mutex needed when sharing between tasks running on the same executor
93
94 // Use Cell for Copy-types
95 static CELL: ConstStaticCell<Cell<[u8; 4]>> = ConstStaticCell::new(Cell::new([0; 4]));
96 let cell = CELL.take();
97
98 // Use RefCell for &mut access
99 static REF_CELL: ConstStaticCell<RefCell<MyType>> = ConstStaticCell::new(RefCell::new(MyType { inner: 0 }));
100 let ref_cell = REF_CELL.take();
101
102 spawner.must_spawn(task_b(uart, cell, ref_cell));
103 spawner.must_spawn(task_c(cell, ref_cell));
104 });
105}
106
107#[embassy_executor::task]
108async fn task_a(uart: &'static UartAsyncMutex) {
109 let mut ticker = Ticker::every(Duration::from_secs(1));
110 loop {
111 let random = RoscRng.next_u32();
112
113 {
114 let mut uart = uart.lock().await;
115 uart.write(b"task a").await.unwrap();
116 // The uart lock is released when it goes out of scope
117 }
118
119 ATOMIC.store(random, Ordering::Relaxed);
120
121 MUTEX_BLOCKING.lock(|x| x.borrow_mut().inner = random);
122
123 ticker.next().await;
124 }
125}
126
127#[embassy_executor::task]
128async fn task_b(uart: &'static UartAsyncMutex, cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {
129 let mut ticker = Ticker::every(Duration::from_secs(1));
130 loop {
131 let random = RoscRng.next_u32();
132
133 uart.lock().await.write(b"task b").await.unwrap();
134
135 cell.set(random.to_be_bytes());
136
137 ref_cell.borrow_mut().inner = random;
138
139 ticker.next().await;
140 }
141}
142
143#[embassy_executor::task]
144async fn task_c(cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {
145 let mut ticker = Ticker::every(Duration::from_secs(1));
146 loop {
147 info!("=======================");
148
149 let atomic_val = ATOMIC.load(Ordering::Relaxed);
150 info!("atomic: {}", atomic_val);
151
152 MUTEX_BLOCKING.lock(|x| {
153 let val = x.borrow().inner;
154 info!("blocking mutex: {}", val);
155 });
156
157 let cell_val = cell.get();
158 info!("cell: {:?}", cell_val);
159
160 let ref_cell_val = ref_cell.borrow().inner;
161 info!("ref_cell: {:?}", ref_cell_val);
162
163 ticker.next().await;
164 }
165}
diff --git a/examples/rp23/src/bin/spi.rs b/examples/rp23/src/bin/spi.rs
new file mode 100644
index 000000000..98aa7622c
--- /dev/null
+++ b/examples/rp23/src/bin/spi.rs
@@ -0,0 +1,61 @@
1//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.
2//!
3//! Example for resistive touch sensor in Waveshare Pico-ResTouch
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::spi::Spi;
12use embassy_rp::{gpio, spi};
13use gpio::{Level, Output};
14use {defmt_rtt as _, panic_probe as _};
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30#[embassy_executor::main]
31async fn main(_spawner: Spawner) {
32 let p = embassy_rp::init(Default::default());
33 info!("Hello World!");
34
35 // Example for resistive touch sensor in Waveshare Pico-ResTouch
36
37 let miso = p.PIN_12;
38 let mosi = p.PIN_11;
39 let clk = p.PIN_10;
40 let touch_cs = p.PIN_16;
41
42 // create SPI
43 let mut config = spi::Config::default();
44 config.frequency = 2_000_000;
45 let mut spi = Spi::new_blocking(p.SPI1, clk, mosi, miso, config);
46
47 // Configure CS
48 let mut cs = Output::new(touch_cs, Level::Low);
49
50 loop {
51 cs.set_low();
52 let mut buf = [0x90, 0x00, 0x00, 0xd0, 0x00, 0x00];
53 spi.blocking_transfer_in_place(&mut buf).unwrap();
54 cs.set_high();
55
56 let x = (buf[1] as u32) << 5 | (buf[2] as u32) >> 3;
57 let y = (buf[4] as u32) << 5 | (buf[5] as u32) >> 3;
58
59 info!("touch: {=u32} {=u32}", x, y);
60 }
61}
diff --git a/examples/rp23/src/bin/spi_async.rs b/examples/rp23/src/bin/spi_async.rs
new file mode 100644
index 000000000..71eaa5c05
--- /dev/null
+++ b/examples/rp23/src/bin/spi_async.rs
@@ -0,0 +1,46 @@
1//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.
2//! No specific hardware is specified in this example. If you connect pin 11 and 12 you should get the same data back.
3
4#![no_std]
5#![no_main]
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_rp::block::ImageDef;
10use embassy_rp::spi::{Config, Spi};
11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _};
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28#[embassy_executor::main]
29async fn main(_spawner: Spawner) {
30 let p = embassy_rp::init(Default::default());
31 info!("Hello World!");
32
33 let miso = p.PIN_12;
34 let mosi = p.PIN_11;
35 let clk = p.PIN_10;
36
37 let mut spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default());
38
39 loop {
40 let tx_buf = [1_u8, 2, 3, 4, 5, 6];
41 let mut rx_buf = [0_u8; 6];
42 spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();
43 info!("{:?}", rx_buf);
44 Timer::after_secs(1).await;
45 }
46}
diff --git a/examples/rp23/src/bin/spi_display.rs b/examples/rp23/src/bin/spi_display.rs
new file mode 100644
index 000000000..2441b1186
--- /dev/null
+++ b/examples/rp23/src/bin/spi_display.rs
@@ -0,0 +1,327 @@
1//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.
2//!
3//! Example written for a display using the ST7789 chip. Possibly the Waveshare Pico-ResTouch
4//! (https://www.waveshare.com/wiki/Pico-ResTouch-LCD-2.8)
5
6#![no_std]
7#![no_main]
8
9use core::cell::RefCell;
10
11use defmt::*;
12use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig;
13use embassy_executor::Spawner;
14use embassy_rp::block::ImageDef;
15use embassy_rp::gpio::{Level, Output};
16use embassy_rp::spi;
17use embassy_rp::spi::{Blocking, Spi};
18use embassy_sync::blocking_mutex::raw::NoopRawMutex;
19use embassy_sync::blocking_mutex::Mutex;
20use embassy_time::Delay;
21use embedded_graphics::image::{Image, ImageRawLE};
22use embedded_graphics::mono_font::ascii::FONT_10X20;
23use embedded_graphics::mono_font::MonoTextStyle;
24use embedded_graphics::pixelcolor::Rgb565;
25use embedded_graphics::prelude::*;
26use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle};
27use embedded_graphics::text::Text;
28use st7789::{Orientation, ST7789};
29use {defmt_rtt as _, panic_probe as _};
30
31#[link_section = ".start_block"]
32#[used]
33pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
34
35// Program metadata for `picotool info`
36#[link_section = ".bi_entries"]
37#[used]
38pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
39 embassy_rp::binary_info_rp_cargo_bin_name!(),
40 embassy_rp::binary_info_rp_cargo_version!(),
41 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
42 embassy_rp::binary_info_rp_program_build_attribute!(),
43];
44
45use crate::my_display_interface::SPIDeviceInterface;
46use crate::touch::Touch;
47
48const DISPLAY_FREQ: u32 = 64_000_000;
49const TOUCH_FREQ: u32 = 200_000;
50
51#[embassy_executor::main]
52async fn main(_spawner: Spawner) {
53 let p = embassy_rp::init(Default::default());
54 info!("Hello World!");
55
56 let bl = p.PIN_13;
57 let rst = p.PIN_15;
58 let display_cs = p.PIN_9;
59 let dcx = p.PIN_8;
60 let miso = p.PIN_12;
61 let mosi = p.PIN_11;
62 let clk = p.PIN_10;
63 let touch_cs = p.PIN_16;
64 //let touch_irq = p.PIN_17;
65
66 // create SPI
67 let mut display_config = spi::Config::default();
68 display_config.frequency = DISPLAY_FREQ;
69 display_config.phase = spi::Phase::CaptureOnSecondTransition;
70 display_config.polarity = spi::Polarity::IdleHigh;
71 let mut touch_config = spi::Config::default();
72 touch_config.frequency = TOUCH_FREQ;
73 touch_config.phase = spi::Phase::CaptureOnSecondTransition;
74 touch_config.polarity = spi::Polarity::IdleHigh;
75
76 let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone());
77 let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi));
78
79 let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config);
80 let touch_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(touch_cs, Level::High), touch_config);
81
82 let mut touch = Touch::new(touch_spi);
83
84 let dcx = Output::new(dcx, Level::Low);
85 let rst = Output::new(rst, Level::Low);
86 // dcx: 0 = command, 1 = data
87
88 // Enable LCD backlight
89 let _bl = Output::new(bl, Level::High);
90
91 // display interface abstraction from SPI and DC
92 let di = SPIDeviceInterface::new(display_spi, dcx);
93
94 // create driver
95 let mut display = ST7789::new(di, rst, 240, 320);
96
97 // initialize
98 display.init(&mut Delay).unwrap();
99
100 // set default orientation
101 display.set_orientation(Orientation::Landscape).unwrap();
102
103 display.clear(Rgb565::BLACK).unwrap();
104
105 let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86);
106 let ferris = Image::new(&raw_image_data, Point::new(34, 68));
107
108 // Display the image
109 ferris.draw(&mut display).unwrap();
110
111 let style = MonoTextStyle::new(&FONT_10X20, Rgb565::GREEN);
112 Text::new(
113 "Hello embedded_graphics \n + embassy + RP2040!",
114 Point::new(20, 200),
115 style,
116 )
117 .draw(&mut display)
118 .unwrap();
119
120 loop {
121 if let Some((x, y)) = touch.read() {
122 let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::BLUE).build();
123
124 Rectangle::new(Point::new(x - 1, y - 1), Size::new(3, 3))
125 .into_styled(style)
126 .draw(&mut display)
127 .unwrap();
128 }
129 }
130}
131
132/// Driver for the XPT2046 resistive touchscreen sensor
133mod touch {
134 use embedded_hal_1::spi::{Operation, SpiDevice};
135
136 struct Calibration {
137 x1: i32,
138 x2: i32,
139 y1: i32,
140 y2: i32,
141 sx: i32,
142 sy: i32,
143 }
144
145 const CALIBRATION: Calibration = Calibration {
146 x1: 3880,
147 x2: 340,
148 y1: 262,
149 y2: 3850,
150 sx: 320,
151 sy: 240,
152 };
153
154 pub struct Touch<SPI: SpiDevice> {
155 spi: SPI,
156 }
157
158 impl<SPI> Touch<SPI>
159 where
160 SPI: SpiDevice,
161 {
162 pub fn new(spi: SPI) -> Self {
163 Self { spi }
164 }
165
166 pub fn read(&mut self) -> Option<(i32, i32)> {
167 let mut x = [0; 2];
168 let mut y = [0; 2];
169 self.spi
170 .transaction(&mut [
171 Operation::Write(&[0x90]),
172 Operation::Read(&mut x),
173 Operation::Write(&[0xd0]),
174 Operation::Read(&mut y),
175 ])
176 .unwrap();
177
178 let x = (u16::from_be_bytes(x) >> 3) as i32;
179 let y = (u16::from_be_bytes(y) >> 3) as i32;
180
181 let cal = &CALIBRATION;
182
183 let x = ((x - cal.x1) * cal.sx / (cal.x2 - cal.x1)).clamp(0, cal.sx);
184 let y = ((y - cal.y1) * cal.sy / (cal.y2 - cal.y1)).clamp(0, cal.sy);
185 if x == 0 && y == 0 {
186 None
187 } else {
188 Some((x, y))
189 }
190 }
191 }
192}
193
194mod my_display_interface {
195 use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand};
196 use embedded_hal_1::digital::OutputPin;
197 use embedded_hal_1::spi::SpiDevice;
198
199 /// SPI display interface.
200 ///
201 /// This combines the SPI peripheral and a data/command pin
202 pub struct SPIDeviceInterface<SPI, DC> {
203 spi: SPI,
204 dc: DC,
205 }
206
207 impl<SPI, DC> SPIDeviceInterface<SPI, DC>
208 where
209 SPI: SpiDevice,
210 DC: OutputPin,
211 {
212 /// Create new SPI interface for communciation with a display driver
213 pub fn new(spi: SPI, dc: DC) -> Self {
214 Self { spi, dc }
215 }
216 }
217
218 impl<SPI, DC> WriteOnlyDataCommand for SPIDeviceInterface<SPI, DC>
219 where
220 SPI: SpiDevice,
221 DC: OutputPin,
222 {
223 fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> {
224 // 1 = data, 0 = command
225 self.dc.set_low().map_err(|_| DisplayError::DCError)?;
226
227 send_u8(&mut self.spi, cmds).map_err(|_| DisplayError::BusWriteError)?;
228 Ok(())
229 }
230
231 fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> {
232 // 1 = data, 0 = command
233 self.dc.set_high().map_err(|_| DisplayError::DCError)?;
234
235 send_u8(&mut self.spi, buf).map_err(|_| DisplayError::BusWriteError)?;
236 Ok(())
237 }
238 }
239
240 fn send_u8<T: SpiDevice>(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> {
241 match words {
242 DataFormat::U8(slice) => spi.write(slice),
243 DataFormat::U16(slice) => {
244 use byte_slice_cast::*;
245 spi.write(slice.as_byte_slice())
246 }
247 DataFormat::U16LE(slice) => {
248 use byte_slice_cast::*;
249 for v in slice.as_mut() {
250 *v = v.to_le();
251 }
252 spi.write(slice.as_byte_slice())
253 }
254 DataFormat::U16BE(slice) => {
255 use byte_slice_cast::*;
256 for v in slice.as_mut() {
257 *v = v.to_be();
258 }
259 spi.write(slice.as_byte_slice())
260 }
261 DataFormat::U8Iter(iter) => {
262 let mut buf = [0; 32];
263 let mut i = 0;
264
265 for v in iter.into_iter() {
266 buf[i] = v;
267 i += 1;
268
269 if i == buf.len() {
270 spi.write(&buf)?;
271 i = 0;
272 }
273 }
274
275 if i > 0 {
276 spi.write(&buf[..i])?;
277 }
278
279 Ok(())
280 }
281 DataFormat::U16LEIter(iter) => {
282 use byte_slice_cast::*;
283 let mut buf = [0; 32];
284 let mut i = 0;
285
286 for v in iter.map(u16::to_le) {
287 buf[i] = v;
288 i += 1;
289
290 if i == buf.len() {
291 spi.write(&buf.as_byte_slice())?;
292 i = 0;
293 }
294 }
295
296 if i > 0 {
297 spi.write(&buf[..i].as_byte_slice())?;
298 }
299
300 Ok(())
301 }
302 DataFormat::U16BEIter(iter) => {
303 use byte_slice_cast::*;
304 let mut buf = [0; 64];
305 let mut i = 0;
306 let len = buf.len();
307
308 for v in iter.map(u16::to_be) {
309 buf[i] = v;
310 i += 1;
311
312 if i == len {
313 spi.write(&buf.as_byte_slice())?;
314 i = 0;
315 }
316 }
317
318 if i > 0 {
319 spi.write(&buf[..i].as_byte_slice())?;
320 }
321
322 Ok(())
323 }
324 _ => unimplemented!(),
325 }
326 }
327}
diff --git a/examples/rp23/src/bin/spi_sdmmc.rs b/examples/rp23/src/bin/spi_sdmmc.rs
new file mode 100644
index 000000000..d7af77a30
--- /dev/null
+++ b/examples/rp23/src/bin/spi_sdmmc.rs
@@ -0,0 +1,98 @@
1//! This example shows how to use `embedded-sdmmc` with the RP2040 chip, over SPI.
2//!
3//! The example will attempt to read a file `MY_FILE.TXT` from the root directory
4//! of the SD card and print its contents.
5
6#![no_std]
7#![no_main]
8
9use defmt::*;
10use embassy_embedded_hal::SetConfig;
11use embassy_executor::Spawner;
12use embassy_rp::block::ImageDef;
13use embassy_rp::spi::Spi;
14use embassy_rp::{gpio, spi};
15use embedded_hal_bus::spi::ExclusiveDevice;
16use embedded_sdmmc::sdcard::{DummyCsPin, SdCard};
17use gpio::{Level, Output};
18use {defmt_rtt as _, panic_probe as _};
19
20#[link_section = ".start_block"]
21#[used]
22pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
23
24// Program metadata for `picotool info`
25#[link_section = ".bi_entries"]
26#[used]
27pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
28 embassy_rp::binary_info_rp_cargo_bin_name!(),
29 embassy_rp::binary_info_rp_cargo_version!(),
30 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
31 embassy_rp::binary_info_rp_program_build_attribute!(),
32];
33
34struct DummyTimesource();
35
36impl embedded_sdmmc::TimeSource for DummyTimesource {
37 fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {
38 embedded_sdmmc::Timestamp {
39 year_since_1970: 0,
40 zero_indexed_month: 0,
41 zero_indexed_day: 0,
42 hours: 0,
43 minutes: 0,
44 seconds: 0,
45 }
46 }
47}
48
49#[embassy_executor::main]
50async fn main(_spawner: Spawner) {
51 embassy_rp::pac::SIO.spinlock(31).write_value(1);
52 let p = embassy_rp::init(Default::default());
53
54 // SPI clock needs to be running at <= 400kHz during initialization
55 let mut config = spi::Config::default();
56 config.frequency = 400_000;
57 let spi = Spi::new_blocking(p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, config);
58 // Use a dummy cs pin here, for embedded-hal SpiDevice compatibility reasons
59 let spi_dev = ExclusiveDevice::new_no_delay(spi, DummyCsPin);
60 // Real cs pin
61 let cs = Output::new(p.PIN_16, Level::High);
62
63 let sdcard = SdCard::new(spi_dev, cs, embassy_time::Delay);
64 info!("Card size is {} bytes", sdcard.num_bytes().unwrap());
65
66 // Now that the card is initialized, the SPI clock can go faster
67 let mut config = spi::Config::default();
68 config.frequency = 16_000_000;
69 sdcard.spi(|dev| dev.bus_mut().set_config(&config)).ok();
70
71 // Now let's look for volumes (also known as partitions) on our block device.
72 // To do this we need a Volume Manager. It will take ownership of the block device.
73 let mut volume_mgr = embedded_sdmmc::VolumeManager::new(sdcard, DummyTimesource());
74
75 // Try and access Volume 0 (i.e. the first partition).
76 // The volume object holds information about the filesystem on that volume.
77 let mut volume0 = volume_mgr.open_volume(embedded_sdmmc::VolumeIdx(0)).unwrap();
78 info!("Volume 0: {:?}", defmt::Debug2Format(&volume0));
79
80 // Open the root directory (mutably borrows from the volume).
81 let mut root_dir = volume0.open_root_dir().unwrap();
82
83 // Open a file called "MY_FILE.TXT" in the root directory
84 // This mutably borrows the directory.
85 let mut my_file = root_dir
86 .open_file_in_dir("MY_FILE.TXT", embedded_sdmmc::Mode::ReadOnly)
87 .unwrap();
88
89 // Print the contents of the file
90 while !my_file.is_eof() {
91 let mut buf = [0u8; 32];
92 if let Ok(n) = my_file.read(&mut buf) {
93 info!("{:a}", buf[..n]);
94 }
95 }
96
97 loop {}
98}
diff --git a/examples/rp23/src/bin/uart.rs b/examples/rp23/src/bin/uart.rs
new file mode 100644
index 000000000..ae00f36dc
--- /dev/null
+++ b/examples/rp23/src/bin/uart.rs
@@ -0,0 +1,40 @@
1//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip.
2//!
3//! No specific hardware is specified in this example. Only output on pin 0 is tested.
4//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used
5//! with its UART port.
6
7#![no_std]
8#![no_main]
9
10use embassy_executor::Spawner;
11use embassy_rp::block::ImageDef;
12use embassy_rp::uart;
13use {defmt_rtt as _, panic_probe as _};
14
15#[link_section = ".start_block"]
16#[used]
17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
18
19// Program metadata for `picotool info`
20#[link_section = ".bi_entries"]
21#[used]
22pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
23 embassy_rp::binary_info_rp_cargo_bin_name!(),
24 embassy_rp::binary_info_rp_cargo_version!(),
25 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
26 embassy_rp::binary_info_rp_program_build_attribute!(),
27];
28
29#[embassy_executor::main]
30async fn main(_spawner: Spawner) {
31 let p = embassy_rp::init(Default::default());
32 let config = uart::Config::default();
33 let mut uart = uart::Uart::new_blocking(p.UART1, p.PIN_4, p.PIN_5, config);
34 uart.blocking_write("Hello World!\r\n".as_bytes()).unwrap();
35
36 loop {
37 uart.blocking_write("hello there!\r\n".as_bytes()).unwrap();
38 cortex_m::asm::delay(1_000_000);
39 }
40}
diff --git a/examples/rp23/src/bin/uart_buffered_split.rs b/examples/rp23/src/bin/uart_buffered_split.rs
new file mode 100644
index 000000000..2b14520d5
--- /dev/null
+++ b/examples/rp23/src/bin/uart_buffered_split.rs
@@ -0,0 +1,73 @@
1//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip.
2//!
3//! No specific hardware is specified in this example. If you connect pin 0 and 1 you should get the same data back.
4//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used
5//! with its UART port.
6
7#![no_std]
8#![no_main]
9
10use defmt::*;
11use embassy_executor::Spawner;
12use embassy_rp::bind_interrupts;
13use embassy_rp::block::ImageDef;
14use embassy_rp::peripherals::UART0;
15use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config};
16use embassy_time::Timer;
17use embedded_io_async::{Read, Write};
18use static_cell::StaticCell;
19use {defmt_rtt as _, panic_probe as _};
20
21#[link_section = ".start_block"]
22#[used]
23pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
24
25// Program metadata for `picotool info`
26#[link_section = ".bi_entries"]
27#[used]
28pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
29 embassy_rp::binary_info_rp_cargo_bin_name!(),
30 embassy_rp::binary_info_rp_cargo_version!(),
31 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
32 embassy_rp::binary_info_rp_program_build_attribute!(),
33];
34
35bind_interrupts!(struct Irqs {
36 UART0_IRQ => BufferedInterruptHandler<UART0>;
37});
38
39#[embassy_executor::main]
40async fn main(spawner: Spawner) {
41 let p = embassy_rp::init(Default::default());
42 let (tx_pin, rx_pin, uart) = (p.PIN_0, p.PIN_1, p.UART0);
43
44 static TX_BUF: StaticCell<[u8; 16]> = StaticCell::new();
45 let tx_buf = &mut TX_BUF.init([0; 16])[..];
46 static RX_BUF: StaticCell<[u8; 16]> = StaticCell::new();
47 let rx_buf = &mut RX_BUF.init([0; 16])[..];
48 let uart = BufferedUart::new(uart, Irqs, tx_pin, rx_pin, tx_buf, rx_buf, Config::default());
49 let (mut tx, rx) = uart.split();
50
51 unwrap!(spawner.spawn(reader(rx)));
52
53 info!("Writing...");
54 loop {
55 let data = [
56 1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
57 29, 30, 31,
58 ];
59 info!("TX {:?}", data);
60 tx.write_all(&data).await.unwrap();
61 Timer::after_secs(1).await;
62 }
63}
64
65#[embassy_executor::task]
66async fn reader(mut rx: BufferedUartRx<'static, UART0>) {
67 info!("Reading...");
68 loop {
69 let mut buf = [0; 31];
70 rx.read_exact(&mut buf).await.unwrap();
71 info!("RX {:?}", buf);
72 }
73}
diff --git a/examples/rp23/src/bin/uart_r503.rs b/examples/rp23/src/bin/uart_r503.rs
new file mode 100644
index 000000000..39a17d305
--- /dev/null
+++ b/examples/rp23/src/bin/uart_r503.rs
@@ -0,0 +1,173 @@
1#![no_std]
2#![no_main]
3
4use defmt::{debug, error, info};
5use embassy_executor::Spawner;
6use embassy_rp::bind_interrupts;
7use embassy_rp::block::ImageDef;
8use embassy_rp::peripherals::UART0;
9use embassy_rp::uart::{Config, DataBits, InterruptHandler as UARTInterruptHandler, Parity, StopBits, Uart};
10use embassy_time::{with_timeout, Duration, Timer};
11use heapless::Vec;
12use {defmt_rtt as _, panic_probe as _};
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28bind_interrupts!(pub struct Irqs {
29 UART0_IRQ => UARTInterruptHandler<UART0>;
30});
31
32const START: u16 = 0xEF01;
33const ADDRESS: u32 = 0xFFFFFFFF;
34
35// ================================================================================
36
37// Data package format
38// Name Length Description
39// ==========================================================================================================
40// Start 2 bytes Fixed value of 0xEF01; High byte transferred first.
41// Address 4 bytes Default value is 0xFFFFFFFF, which can be modified by command.
42// High byte transferred first and at wrong adder value, module
43// will reject to transfer.
44// PID 1 byte 01H Command packet;
45// 02H Data packet; Data packet shall not appear alone in executing
46// processs, must follow command packet or acknowledge packet.
47// 07H Acknowledge packet;
48// 08H End of Data packet.
49// LENGTH 2 bytes Refers to the length of package content (command packets and data packets)
50// plus the length of Checksum (2 bytes). Unit is byte. Max length is 256 bytes.
51// And high byte is transferred first.
52// DATA - It can be commands, data, command’s parameters, acknowledge result, etc.
53// (fingerprint character value, template are all deemed as data);
54// SUM 2 bytes The arithmetic sum of package identifier, package length and all package
55// contens. Overflowing bits are omitted. high byte is transferred first.
56
57// ================================================================================
58
59// Checksum is calculated on 'length (2 bytes) + data (??)'.
60fn compute_checksum(buf: Vec<u8, 32>) -> u16 {
61 let mut checksum = 0u16;
62
63 let check_end = buf.len();
64 let checked_bytes = &buf[6..check_end];
65 for byte in checked_bytes {
66 checksum += (*byte) as u16;
67 }
68 return checksum;
69}
70
71#[embassy_executor::main]
72async fn main(_spawner: Spawner) {
73 info!("Start");
74
75 let p = embassy_rp::init(Default::default());
76
77 // Initialize the fingerprint scanner.
78 let mut config = Config::default();
79 config.baudrate = 57600;
80 config.stop_bits = StopBits::STOP1;
81 config.data_bits = DataBits::DataBits8;
82 config.parity = Parity::ParityNone;
83
84 let (uart, tx_pin, tx_dma, rx_pin, rx_dma) = (p.UART0, p.PIN_16, p.DMA_CH0, p.PIN_17, p.DMA_CH1);
85 let uart = Uart::new(uart, tx_pin, rx_pin, Irqs, tx_dma, rx_dma, config);
86 let (mut tx, mut rx) = uart.split();
87
88 let mut vec_buf: Vec<u8, 32> = heapless::Vec::new();
89 let mut data: Vec<u8, 32> = heapless::Vec::new();
90
91 let mut speeds: Vec<u8, 3> = heapless::Vec::new();
92 let _ = speeds.push(0xC8); // Slow
93 let _ = speeds.push(0x20); // Medium
94 let _ = speeds.push(0x02); // Fast
95
96 // Cycle through the three colours Red, Blue and Purple forever.
97 loop {
98 for colour in 1..=3 {
99 for speed in &speeds {
100 // Set the data first, because the length is dependent on that.
101 // However, we write the length bits before we do the data.
102 data.clear();
103 let _ = data.push(0x01); // ctrl=Breathing light
104 let _ = data.push(*speed);
105 let _ = data.push(colour as u8); // colour=Red, Blue, Purple
106 let _ = data.push(0x00); // times=Infinite
107
108 // Clear buffers
109 vec_buf.clear();
110
111 // START
112 let _ = vec_buf.extend_from_slice(&START.to_be_bytes()[..]);
113
114 // ADDRESS
115 let _ = vec_buf.extend_from_slice(&ADDRESS.to_be_bytes()[..]);
116
117 // PID
118 let _ = vec_buf.extend_from_slice(&[0x01]);
119
120 // LENGTH
121 let len: u16 = (1 + data.len() + 2).try_into().unwrap();
122 let _ = vec_buf.extend_from_slice(&len.to_be_bytes()[..]);
123
124 // COMMAND
125 let _ = vec_buf.push(0x35); // Command: AuraLedConfig
126
127 // DATA
128 let _ = vec_buf.extend_from_slice(&data);
129
130 // SUM
131 let chk = compute_checksum(vec_buf.clone());
132 let _ = vec_buf.extend_from_slice(&chk.to_be_bytes()[..]);
133
134 // =====
135
136 // Send command buffer.
137 let data_write: [u8; 16] = vec_buf.clone().into_array().unwrap();
138 debug!(" write='{:?}'", data_write[..]);
139 match tx.write(&data_write).await {
140 Ok(..) => info!("Write successful."),
141 Err(e) => error!("Write error: {:?}", e),
142 }
143
144 // =====
145
146 // Read command buffer.
147 let mut read_buf: [u8; 1] = [0; 1]; // Can only read one byte at a time!
148 let mut data_read: Vec<u8, 32> = heapless::Vec::new(); // Save buffer.
149
150 info!("Attempting read.");
151 loop {
152 // Some commands, like `Img2Tz()` needs longer, but we hard-code this to 200ms
153 // for this command.
154 match with_timeout(Duration::from_millis(200), rx.read(&mut read_buf)).await {
155 Ok(..) => {
156 // Extract and save read byte.
157 debug!(" r='{=u8:#04x}H' ({:03}D)", read_buf[0], read_buf[0]);
158 let _ = data_read.push(read_buf[0]).unwrap();
159 }
160 Err(..) => break, // TimeoutError -> Ignore.
161 }
162 }
163 info!("Read successful");
164 debug!(" read='{:?}'", data_read[..]);
165
166 Timer::after_secs(3).await;
167 info!("Changing speed.");
168 }
169
170 info!("Changing colour.");
171 }
172 }
173}
diff --git a/examples/rp23/src/bin/uart_unidir.rs b/examples/rp23/src/bin/uart_unidir.rs
new file mode 100644
index 000000000..38210a8d0
--- /dev/null
+++ b/examples/rp23/src/bin/uart_unidir.rs
@@ -0,0 +1,65 @@
1//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip.
2//!
3//! Test TX-only and RX-only on two different UARTs. You need to connect GPIO0 to GPIO5 for
4//! this to work
5//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used
6//! with its UART port.
7
8#![no_std]
9#![no_main]
10
11use defmt::*;
12use embassy_executor::Spawner;
13use embassy_rp::bind_interrupts;
14use embassy_rp::block::ImageDef;
15use embassy_rp::peripherals::UART1;
16use embassy_rp::uart::{Async, Config, InterruptHandler, UartRx, UartTx};
17use embassy_time::Timer;
18use {defmt_rtt as _, panic_probe as _};
19
20#[link_section = ".start_block"]
21#[used]
22pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
23
24// Program metadata for `picotool info`
25#[link_section = ".bi_entries"]
26#[used]
27pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
28 embassy_rp::binary_info_rp_cargo_bin_name!(),
29 embassy_rp::binary_info_rp_cargo_version!(),
30 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
31 embassy_rp::binary_info_rp_program_build_attribute!(),
32];
33
34bind_interrupts!(struct Irqs {
35 UART1_IRQ => InterruptHandler<UART1>;
36});
37
38#[embassy_executor::main]
39async fn main(spawner: Spawner) {
40 let p = embassy_rp::init(Default::default());
41
42 let mut uart_tx = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Config::default());
43 let uart_rx = UartRx::new(p.UART1, p.PIN_5, Irqs, p.DMA_CH1, Config::default());
44
45 unwrap!(spawner.spawn(reader(uart_rx)));
46
47 info!("Writing...");
48 loop {
49 let data = [1u8, 2, 3, 4, 5, 6, 7, 8];
50 info!("TX {:?}", data);
51 uart_tx.write(&data).await.unwrap();
52 Timer::after_secs(1).await;
53 }
54}
55
56#[embassy_executor::task]
57async fn reader(mut rx: UartRx<'static, UART1, Async>) {
58 info!("Reading...");
59 loop {
60 // read a total of 4 transmissions (32 / 8) and then print the result
61 let mut buf = [0; 32];
62 rx.read(&mut buf).await.unwrap();
63 info!("RX {:?}", buf);
64 }
65}
diff --git a/examples/rp23/src/bin/usb_webusb.rs b/examples/rp23/src/bin/usb_webusb.rs
new file mode 100644
index 000000000..f4ecde30e
--- /dev/null
+++ b/examples/rp23/src/bin/usb_webusb.rs
@@ -0,0 +1,170 @@
1//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip.
2//!
3//! This creates a WebUSB capable device that echoes data back to the host.
4//!
5//! To test this in the browser (ideally host this on localhost:8080, to test the landing page
6//! feature):
7//! ```js
8//! (async () => {
9//! const device = await navigator.usb.requestDevice({ filters: [{ vendorId: 0xf569 }] });
10//! await device.open();
11//! await device.claimInterface(1);
12//! device.transferIn(1, 64).then(data => console.log(data));
13//! await device.transferOut(1, new Uint8Array([1,2,3]));
14//! })();
15//! ```
16
17#![no_std]
18#![no_main]
19
20use defmt::info;
21use embassy_executor::Spawner;
22use embassy_futures::join::join;
23use embassy_rp::bind_interrupts;
24use embassy_rp::block::ImageDef;
25use embassy_rp::peripherals::USB;
26use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler};
27use embassy_usb::class::web_usb::{Config as WebUsbConfig, State, Url, WebUsb};
28use embassy_usb::driver::{Driver, Endpoint, EndpointIn, EndpointOut};
29use embassy_usb::msos::{self, windows_version};
30use embassy_usb::{Builder, Config};
31use {defmt_rtt as _, panic_probe as _};
32
33#[link_section = ".start_block"]
34#[used]
35pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
36
37// Program metadata for `picotool info`
38#[link_section = ".bi_entries"]
39#[used]
40pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
41 embassy_rp::binary_info_rp_cargo_bin_name!(),
42 embassy_rp::binary_info_rp_cargo_version!(),
43 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
44 embassy_rp::binary_info_rp_program_build_attribute!(),
45];
46
47bind_interrupts!(struct Irqs {
48 USBCTRL_IRQ => InterruptHandler<USB>;
49});
50
51// This is a randomly generated GUID to allow clients on Windows to find our device
52const DEVICE_INTERFACE_GUIDS: &[&str] = &["{AFB9A6FB-30BA-44BC-9232-806CFC875321}"];
53
54#[embassy_executor::main]
55async fn main(_spawner: Spawner) {
56 let p = embassy_rp::init(Default::default());
57
58 // Create the driver, from the HAL.
59 let driver = UsbDriver::new(p.USB, Irqs);
60
61 // Create embassy-usb Config
62 let mut config = Config::new(0xf569, 0x0001);
63 config.manufacturer = Some("Embassy");
64 config.product = Some("WebUSB example");
65 config.serial_number = Some("12345678");
66 config.max_power = 100;
67 config.max_packet_size_0 = 64;
68
69 // Required for windows compatibility.
70 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
71 config.device_class = 0xff;
72 config.device_sub_class = 0x00;
73 config.device_protocol = 0x00;
74
75 // Create embassy-usb DeviceBuilder using the driver and config.
76 // It needs some buffers for building the descriptors.
77 let mut config_descriptor = [0; 256];
78 let mut bos_descriptor = [0; 256];
79 let mut control_buf = [0; 64];
80 let mut msos_descriptor = [0; 256];
81
82 let webusb_config = WebUsbConfig {
83 max_packet_size: 64,
84 vendor_code: 1,
85 // If defined, shows a landing page which the device manufacturer would like the user to visit in order to control their device. Suggest the user to navigate to this URL when the device is connected.
86 landing_url: Some(Url::new("http://localhost:8080")),
87 };
88
89 let mut state = State::new();
90
91 let mut builder = Builder::new(
92 driver,
93 config,
94 &mut config_descriptor,
95 &mut bos_descriptor,
96 &mut msos_descriptor,
97 &mut control_buf,
98 );
99
100 // Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.
101 // We tell Windows that this entire device is compatible with the "WINUSB" feature,
102 // which causes it to use the built-in WinUSB driver automatically, which in turn
103 // can be used by libusb/rusb software without needing a custom driver or INF file.
104 // In principle you might want to call msos_feature() just on a specific function,
105 // if your device also has other functions that still use standard class drivers.
106 builder.msos_descriptor(windows_version::WIN8_1, 0);
107 builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
108 builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
109 "DeviceInterfaceGUIDs",
110 msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
111 ));
112
113 // Create classes on the builder (WebUSB just needs some setup, but doesn't return anything)
114 WebUsb::configure(&mut builder, &mut state, &webusb_config);
115 // Create some USB bulk endpoints for testing.
116 let mut endpoints = WebEndpoints::new(&mut builder, &webusb_config);
117
118 // Build the builder.
119 let mut usb = builder.build();
120
121 // Run the USB device.
122 let usb_fut = usb.run();
123
124 // Do some WebUSB transfers.
125 let webusb_fut = async {
126 loop {
127 endpoints.wait_connected().await;
128 info!("Connected");
129 endpoints.echo().await;
130 }
131 };
132
133 // Run everything concurrently.
134 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
135 join(usb_fut, webusb_fut).await;
136}
137
138struct WebEndpoints<'d, D: Driver<'d>> {
139 write_ep: D::EndpointIn,
140 read_ep: D::EndpointOut,
141}
142
143impl<'d, D: Driver<'d>> WebEndpoints<'d, D> {
144 fn new(builder: &mut Builder<'d, D>, config: &'d WebUsbConfig<'d>) -> Self {
145 let mut func = builder.function(0xff, 0x00, 0x00);
146 let mut iface = func.interface();
147 let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None);
148
149 let write_ep = alt.endpoint_bulk_in(config.max_packet_size);
150 let read_ep = alt.endpoint_bulk_out(config.max_packet_size);
151
152 WebEndpoints { write_ep, read_ep }
153 }
154
155 // Wait until the device's endpoints are enabled.
156 async fn wait_connected(&mut self) {
157 self.read_ep.wait_enabled().await
158 }
159
160 // Echo data back to the host.
161 async fn echo(&mut self) {
162 let mut buf = [0; 64];
163 loop {
164 let n = self.read_ep.read(&mut buf).await.unwrap();
165 let data = &buf[..n];
166 info!("Data read: {:x}", data);
167 self.write_ep.write(data).await.unwrap();
168 }
169 }
170}
diff --git a/examples/rp23/src/bin/watchdog.rs b/examples/rp23/src/bin/watchdog.rs
new file mode 100644
index 000000000..3ac457219
--- /dev/null
+++ b/examples/rp23/src/bin/watchdog.rs
@@ -0,0 +1,66 @@
1//! This example shows how to use Watchdog in the RP2040 chip.
2//!
3//! It does not work with the RP Pico W board. See wifi_blinky.rs or connect external LED and resistor.
4
5#![no_std]
6#![no_main]
7
8use defmt::info;
9use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef;
11use embassy_rp::gpio;
12use embassy_rp::watchdog::*;
13use embassy_time::{Duration, Timer};
14use gpio::{Level, Output};
15use {defmt_rtt as _, panic_probe as _};
16
17#[link_section = ".start_block"]
18#[used]
19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
20
21// Program metadata for `picotool info`
22#[link_section = ".bi_entries"]
23#[used]
24pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
25 embassy_rp::binary_info_rp_cargo_bin_name!(),
26 embassy_rp::binary_info_rp_cargo_version!(),
27 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
28 embassy_rp::binary_info_rp_program_build_attribute!(),
29];
30
31#[embassy_executor::main]
32async fn main(_spawner: Spawner) {
33 let p = embassy_rp::init(Default::default());
34 info!("Hello world!");
35
36 let mut watchdog = Watchdog::new(p.WATCHDOG);
37 let mut led = Output::new(p.PIN_25, Level::Low);
38
39 // Set the LED high for 2 seconds so we know when we're about to start the watchdog
40 led.set_high();
41 Timer::after_secs(2).await;
42
43 // Set to watchdog to reset if it's not fed within 1.05 seconds, and start it
44 watchdog.start(Duration::from_millis(1_050));
45 info!("Started the watchdog timer");
46
47 // Blink once a second for 5 seconds, feed the watchdog timer once a second to avoid a reset
48 for _ in 1..=5 {
49 led.set_low();
50 Timer::after_millis(500).await;
51 led.set_high();
52 Timer::after_millis(500).await;
53 info!("Feeding watchdog");
54 watchdog.feed();
55 }
56
57 info!("Stopped feeding, device will reset in 1.05 seconds");
58 // Blink 10 times per second, not feeding the watchdog.
59 // The processor should reset in 1.05 seconds.
60 loop {
61 led.set_low();
62 Timer::after_millis(100).await;
63 led.set_high();
64 Timer::after_millis(100).await;
65 }
66}
diff --git a/examples/rp23/src/bin/zerocopy.rs b/examples/rp23/src/bin/zerocopy.rs
new file mode 100644
index 000000000..d04e1bf2a
--- /dev/null
+++ b/examples/rp23/src/bin/zerocopy.rs
@@ -0,0 +1,109 @@
1//! This example shows how to use `zerocopy_channel` from `embassy_sync` for
2//! sending large values between two tasks without copying.
3//! The example also shows how to use the RP2040 ADC with DMA.
4#![no_std]
5#![no_main]
6
7use core::sync::atomic::{AtomicU16, Ordering};
8
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_rp::adc::{self, Adc, Async, Config, InterruptHandler};
12use embassy_rp::bind_interrupts;
13use embassy_rp::block::ImageDef;
14use embassy_rp::gpio::Pull;
15use embassy_rp::peripherals::DMA_CH0;
16use embassy_sync::blocking_mutex::raw::NoopRawMutex;
17use embassy_sync::zerocopy_channel::{Channel, Receiver, Sender};
18use embassy_time::{Duration, Ticker, Timer};
19use static_cell::StaticCell;
20use {defmt_rtt as _, panic_probe as _};
21
22#[link_section = ".start_block"]
23#[used]
24pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
25
26// Program metadata for `picotool info`
27#[link_section = ".bi_entries"]
28#[used]
29pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
30 embassy_rp::binary_info_rp_cargo_bin_name!(),
31 embassy_rp::binary_info_rp_cargo_version!(),
32 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
33 embassy_rp::binary_info_rp_program_build_attribute!(),
34];
35
36type SampleBuffer = [u16; 512];
37
38bind_interrupts!(struct Irqs {
39 ADC_IRQ_FIFO => InterruptHandler;
40});
41
42const BLOCK_SIZE: usize = 512;
43const NUM_BLOCKS: usize = 2;
44static MAX: AtomicU16 = AtomicU16::new(0);
45
46struct AdcParts {
47 adc: Adc<'static, Async>,
48 pin: adc::Channel<'static>,
49 dma: DMA_CH0,
50}
51
52#[embassy_executor::main]
53async fn main(spawner: Spawner) {
54 let p = embassy_rp::init(Default::default());
55 info!("Here we go!");
56
57 let adc_parts = AdcParts {
58 adc: Adc::new(p.ADC, Irqs, Config::default()),
59 pin: adc::Channel::new_pin(p.PIN_29, Pull::None),
60 dma: p.DMA_CH0,
61 };
62
63 static BUF: StaticCell<[SampleBuffer; NUM_BLOCKS]> = StaticCell::new();
64 let buf = BUF.init([[0; BLOCK_SIZE]; NUM_BLOCKS]);
65
66 static CHANNEL: StaticCell<Channel<'_, NoopRawMutex, SampleBuffer>> = StaticCell::new();
67 let channel = CHANNEL.init(Channel::new(buf));
68 let (sender, receiver) = channel.split();
69
70 spawner.must_spawn(consumer(receiver));
71 spawner.must_spawn(producer(sender, adc_parts));
72
73 let mut ticker = Ticker::every(Duration::from_secs(1));
74 loop {
75 ticker.next().await;
76 let max = MAX.load(Ordering::Relaxed);
77 info!("latest block's max value: {:?}", max);
78 }
79}
80
81#[embassy_executor::task]
82async fn producer(mut sender: Sender<'static, NoopRawMutex, SampleBuffer>, mut adc: AdcParts) {
83 loop {
84 // Obtain a free buffer from the channel
85 let buf = sender.send().await;
86
87 // Fill it with data
88 adc.adc.read_many(&mut adc.pin, buf, 1, &mut adc.dma).await.unwrap();
89
90 // Notify the channel that the buffer is now ready to be received
91 sender.send_done();
92 }
93}
94
95#[embassy_executor::task]
96async fn consumer(mut receiver: Receiver<'static, NoopRawMutex, SampleBuffer>) {
97 loop {
98 // Receive a buffer from the channel
99 let buf = receiver.receive().await;
100
101 // Simulate using the data, while the producer is filling up the next buffer
102 Timer::after_micros(1000).await;
103 let max = buf.iter().max().unwrap();
104 MAX.store(*max, Ordering::Relaxed);
105
106 // Notify the channel that the buffer is now ready to be reused
107 receiver.receive_done();
108 }
109}
diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml
index def270558..12f1ec3ce 100644
--- a/tests/rp/Cargo.toml
+++ b/tests/rp/Cargo.toml
@@ -10,7 +10,7 @@ teleprobe-meta = "1.1"
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-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", ] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", ] }
13embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } 13embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram", "rp2040"] }
14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
15embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] } 15embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] }
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"] }