aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCaleb Jamison <[email protected]>2024-08-07 23:20:26 -0400
committerCaleb Jamison <[email protected]>2024-08-08 21:35:21 -0400
commitb185e02a42ad751ec6c31ffa6a1b87503f15489d (patch)
tree0f0c66747267d24d95b5957b22db7e5c525cb00e
parent891c5ee10584cd990dad529e3506fe1328e4e69d (diff)
Initial rp235x support
Examples have been run, but there is not yet a test suite.
-rw-r--r--embassy-rp/Cargo.toml20
-rw-r--r--embassy-rp/build.rs18
-rw-r--r--embassy-rp/link-rp.x.in2
-rw-r--r--embassy-rp/src/adc.rs27
-rw-r--r--embassy-rp/src/binary_info/consts.rs31
-rw-r--r--embassy-rp/src/binary_info/macros.rs171
-rw-r--r--embassy-rp/src/binary_info/mod.rs174
-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.rs84
-rw-r--r--embassy-rp/src/dma.rs32
-rw-r--r--embassy-rp/src/flash.rs25
-rw-r--r--embassy-rp/src/float/mod.rs1
-rw-r--r--embassy-rp/src/gpio.rs76
-rw-r--r--embassy-rp/src/i2c.rs36
-rw-r--r--embassy-rp/src/lib.rs218
-rw-r--r--embassy-rp/src/multicore.rs36
-rw-r--r--embassy-rp/src/pio/mod.rs95
-rw-r--r--embassy-rp/src/pwm.rs45
-rw-r--r--embassy-rp/src/reset.rs2
-rw-r--r--embassy-rp/src/rtc/mod.rs1
-rw-r--r--embassy-rp/src/spi.rs98
-rw-r--r--embassy-rp/src/time_driver.rs73
-rw-r--r--embassy-rp/src/uart/mod.rs122
-rw-r--r--embassy-rp/src/usb.rs6
-rw-r--r--embassy-rp/src/watchdog.rs1
-rw-r--r--examples/rp/Cargo.toml4
-rw-r--r--examples/rp23/.cargo/config.toml9
-rw-r--r--examples/rp23/Cargo.toml80
-rw-r--r--examples/rp23/assets/ferris.rawbin0 -> 11008 bytes
-rw-r--r--examples/rp23/build.rs35
-rw-r--r--examples/rp23/memory.x74
-rw-r--r--examples/rp23/src/bin/adc.rs64
-rw-r--r--examples/rp23/src/bin/adc_dma.rs70
-rw-r--r--examples/rp23/src/bin/assign_resources.rs95
-rw-r--r--examples/rp23/src/bin/blinky.rs44
-rw-r--r--examples/rp23/src/bin/blinky_two_channels.rs66
-rw-r--r--examples/rp23/src/bin/blinky_two_tasks.rs65
-rw-r--r--examples/rp23/src/bin/button.rs44
-rw-r--r--examples/rp23/src/bin/debounce.rs96
-rw-r--r--examples/rp23/src/bin/flash.rs150
-rw-r--r--examples/rp23/src/bin/gpio_async.rs56
-rw-r--r--examples/rp23/src/bin/gpout.rs53
-rw-r--r--examples/rp23/src/bin/i2c_async.rs126
-rw-r--r--examples/rp23/src/bin/i2c_async_embassy.rs101
-rw-r--r--examples/rp23/src/bin/i2c_blocking.rs90
-rw-r--r--examples/rp23/src/bin/i2c_slave.rs132
-rw-r--r--examples/rp23/src/bin/interrupt.rs110
-rw-r--r--examples/rp23/src/bin/multicore.rs82
-rw-r--r--examples/rp23/src/bin/multiprio.rs161
-rw-r--r--examples/rp23/src/bin/pio_async.rs146
-rw-r--r--examples/rp23/src/bin/pio_dma.rs99
-rw-r--r--examples/rp23/src/bin/pio_hd44780.rs256
-rw-r--r--examples/rp23/src/bin/pio_i2s.rs141
-rw-r--r--examples/rp23/src/bin/pio_pwm.rs134
-rw-r--r--examples/rp23/src/bin/pio_rotary_encoder.rs96
-rw-r--r--examples/rp23/src/bin/pio_servo.rs224
-rw-r--r--examples/rp23/src/bin/pio_stepper.rs184
-rw-r--r--examples/rp23/src/bin/pio_ws2812.rs177
-rw-r--r--examples/rp23/src/bin/pwm.rs45
-rw-r--r--examples/rp23/src/bin/pwm_input.rs42
-rw-r--r--examples/rp23/src/bin/rosc.rs47
-rw-r--r--examples/rp23/src/bin/shared_bus.rs131
-rw-r--r--examples/rp23/src/bin/sharing.rs166
-rw-r--r--examples/rp23/src/bin/spi.rs62
-rw-r--r--examples/rp23/src/bin/spi_async.rs47
-rw-r--r--examples/rp23/src/bin/spi_display.rs328
-rw-r--r--examples/rp23/src/bin/spi_sdmmc.rs99
-rw-r--r--examples/rp23/src/bin/uart.rs40
-rw-r--r--examples/rp23/src/bin/uart_buffered_split.rs74
-rw-r--r--examples/rp23/src/bin/uart_r503.rs174
-rw-r--r--examples/rp23/src/bin/uart_unidir.rs66
-rw-r--r--examples/rp23/src/bin/usb_webusb.rs171
-rw-r--r--examples/rp23/src/bin/watchdog.rs67
-rw-r--r--examples/rp23/src/bin/zerocopy.rs110
-rw-r--r--tests/rp/Cargo.toml2
76 files changed, 7507 insertions, 90 deletions
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml
index b7f5a2bcc..4e36c885b 100644
--- a/embassy-rp/Cargo.toml
+++ b/embassy-rp/Cargo.toml
@@ -15,6 +15,7 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-rp/sr
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" },
18 { name = "rp235x", target = "thumbv8m.main-none-eabi" },
18] 19]
19 20
20[package.metadata.docs.rs] 21[package.metadata.docs.rs]
@@ -23,7 +24,9 @@ features = ["defmt", "unstable-pac", "time-driver"]
23[features] 24[features]
24default = [ "rt" ] 25default = [ "rt" ]
25## Enable the rt feature of [`rp-pac`](https://docs.rs/rp-pac). This brings in the [`cortex-m-rt`](https://docs.rs/cortex-m-rt) crate, which adds startup code and minimal runtime initialization. 26## Enable the rt feature of [`rp-pac`](https://docs.rs/rp-pac). This brings in the [`cortex-m-rt`](https://docs.rs/cortex-m-rt) crate, which adds startup code and minimal runtime initialization.
26rt = [ "rp-pac/rt" ] 27rt = []
28rt-2040 = [ "rt", "rp-pac/rt" ]
29rt-235x = [ "rt", "rp23-pac/rt" ]
27 30
28## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers. 31## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers.
29defmt = ["dep:defmt", "embassy-usb-driver/defmt", "embassy-hal-internal/defmt"] 32defmt = ["dep:defmt", "embassy-usb-driver/defmt", "embassy-hal-internal/defmt"]
@@ -88,6 +91,17 @@ boot2-w25x10cl = []
88## ``` 91## ```
89boot2-none = [] 92boot2-none = []
90 93
94rp2040 = ["dep:rp-pac"]
95rp235x = ["dep:rp23-pac"]
96rp235xa = ["rp235x"]
97rp235xb = ["rp235x"]
98
99# Add a binary-info header block containing picotool-compatible metadata.
100#
101# Takes up a little flash space, but picotool can then report the name of your
102# program and other details.
103binary-info = [ "rt-235x" ]
104
91[dependencies] 105[dependencies]
92embassy-sync = { version = "0.6.0", path = "../embassy-sync" } 106embassy-sync = { version = "0.6.0", path = "../embassy-sync" }
93embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } 107embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true }
@@ -112,7 +126,8 @@ embedded-storage-async = { version = "0.4.1" }
112rand_core = "0.6.4" 126rand_core = "0.6.4"
113fixed = "1.23.1" 127fixed = "1.23.1"
114 128
115rp-pac = { version = "6" } 129rp-pac = { path = "/home/cbjamo/235x/rp-pac", feature = ["rt"], optional = true }
130rp23-pac = { path = "/home/cbjamo/235x/rp23-pac/", feature = ["rt"], optional = true }
116 131
117embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 132embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
118embedded-hal-1 = { package = "embedded-hal", version = "1.0" } 133embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
@@ -123,6 +138,7 @@ pio-proc = {version= "0.2" }
123pio = {version= "0.2.1" } 138pio = {version= "0.2.1" }
124rp2040-boot2 = "0.3" 139rp2040-boot2 = "0.3"
125document-features = "0.2.7" 140document-features = "0.2.7"
141sha2-const-stable = "0.1"
126 142
127[dev-dependencies] 143[dev-dependencies]
128embassy-executor = { version = "0.6.0", path = "../embassy-executor", features = ["arch-std", "executor-thread"] } 144embassy-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..12d08d06b 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
@@ -229,7 +230,10 @@ impl<'d> Adc<'d, Async> {
229 div: u16, 230 div: u16,
230 dma: impl Peripheral<P = impl dma::Channel>, 231 dma: impl Peripheral<P = impl dma::Channel>,
231 ) -> Result<(), Error> { 232 ) -> Result<(), Error> {
233 #[cfg(feature = "rp2040")]
232 let mut rrobin = 0_u8; 234 let mut rrobin = 0_u8;
235 #[cfg(feature = "rp235x")]
236 let mut rrobin = 0_u16;
233 for c in channels { 237 for c in channels {
234 rrobin |= 1 << c; 238 rrobin |= 1 << c;
235 } 239 }
@@ -278,7 +282,7 @@ impl<'d> Adc<'d, Async> {
278 } 282 }
279 let auto_reset = ResetDmaConfig; 283 let auto_reset = ResetDmaConfig;
280 284
281 let dma = unsafe { dma::read(dma, r.fifo().as_ptr() as *const W, buf as *mut [W], 36) }; 285 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 286 // 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 287 // because there's no interrupt to signal them, and inspecting every element
284 // of the fifo is too costly to do here. 288 // of the fifo is too costly to do here.
@@ -423,10 +427,31 @@ macro_rules! impl_pin {
423 }; 427 };
424} 428}
425 429
430#[cfg(any(feature = "rp235xa", feature = "rp2040"))]
426impl_pin!(PIN_26, 0); 431impl_pin!(PIN_26, 0);
432#[cfg(any(feature = "rp235xa", feature = "rp2040"))]
427impl_pin!(PIN_27, 1); 433impl_pin!(PIN_27, 1);
434#[cfg(any(feature = "rp235xa", feature = "rp2040"))]
428impl_pin!(PIN_28, 2); 435impl_pin!(PIN_28, 2);
436#[cfg(any(feature = "rp235xa", feature = "rp2040"))]
429impl_pin!(PIN_29, 3); 437impl_pin!(PIN_29, 3);
430 438
439#[cfg(feature = "rp235xb")]
440impl_pin!(PIN_40, 0);
441#[cfg(feature = "rp235xb")]
442impl_pin!(PIN_41, 1);
443#[cfg(feature = "rp235xb")]
444impl_pin!(PIN_42, 2);
445#[cfg(feature = "rp235xb")]
446impl_pin!(PIN_43, 3);
447#[cfg(feature = "rp235xb")]
448impl_pin!(PIN_44, 4);
449#[cfg(feature = "rp235xb")]
450impl_pin!(PIN_45, 5);
451#[cfg(feature = "rp235xb")]
452impl_pin!(PIN_46, 6);
453#[cfg(feature = "rp235xb")]
454impl_pin!(PIN_47, 7);
455
431impl SealedAdcChannel for peripherals::ADC_TEMP_SENSOR {} 456impl SealedAdcChannel for peripherals::ADC_TEMP_SENSOR {}
432impl AdcChannel for peripherals::ADC_TEMP_SENSOR {} 457impl 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..ef98c8399
--- /dev/null
+++ b/embassy-rp/src/binary_info/macros.rs
@@ -0,0 +1,171 @@
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 =
14 unsafe { core::ffi::CStr::from_bytes_with_nul_unchecked(value.as_bytes()) };
15 value_cstr
16 })
17 };
18}
19
20/// Generate a static item containing the given string, and return its
21/// [`EntryAddr`](super::EntryAddr).
22///
23/// You must pass a numeric tag, a numeric ID, and `&CStr` (which is always
24/// null-terminated).
25#[macro_export]
26macro_rules! binary_info_str {
27 ($tag:expr, $id:expr, $str:expr) => {{
28 static ENTRY: $crate::binary_info::StringEntry =
29 $crate::binary_info::StringEntry::new($tag, $id, $str);
30 ENTRY.addr()
31 }};
32}
33
34/// Generate a static item containing the given string, and return its
35/// [`EntryAddr`](super::EntryAddr).
36///
37/// You must pass a numeric tag, a numeric ID, and `&CStr` (which is always
38/// null-terminated).
39#[macro_export]
40macro_rules! binary_info_int {
41 ($tag:expr, $id:expr, $int:expr) => {{
42 static ENTRY: $crate::binary_info::IntegerEntry =
43 $crate::binary_info::IntegerEntry::new($tag, $id, $int);
44 ENTRY.addr()
45 }};
46}
47
48/// Generate a static item containing the program name, and return its
49/// [`EntryAddr`](super::EntryAddr).
50#[macro_export]
51macro_rules! binary_info_rp_program_name {
52 ($name:expr) => {
53 $crate::binary_info_str!(
54 $crate::binary_info::consts::TAG_RASPBERRY_PI,
55 $crate::binary_info::consts::ID_RP_PROGRAM_NAME,
56 $name
57 )
58 };
59}
60
61/// Generate a static item containing the `CARGO_BIN_NAME` as the program name,
62/// and return its [`EntryAddr`](super::EntryAddr).
63#[macro_export]
64macro_rules! binary_info_rp_cargo_bin_name {
65 () => {
66 $crate::binary_info_env!(
67 $crate::binary_info::consts::TAG_RASPBERRY_PI,
68 $crate::binary_info::consts::ID_RP_PROGRAM_NAME,
69 "CARGO_BIN_NAME"
70 )
71 };
72}
73
74/// Generate a static item containing the program version, and return its
75/// [`EntryAddr`](super::EntryAddr).
76#[macro_export]
77macro_rules! binary_info_rp_program_version {
78 ($version:expr) => {{
79 $crate::binary_info_str!(
80 $crate::binary_info::consts::TAG_RASPBERRY_PI,
81 $crate::binary_info::consts::ID_RP_PROGRAM_VERSION,
82 $version
83 )
84 }};
85}
86
87/// Generate a static item containing the `CARGO_PKG_VERSION` as the program
88/// version, and return its [`EntryAddr`](super::EntryAddr).
89#[macro_export]
90macro_rules! binary_info_rp_cargo_version {
91 () => {
92 $crate::binary_info_env!(
93 $crate::binary_info::consts::TAG_RASPBERRY_PI,
94 $crate::binary_info::consts::ID_RP_PROGRAM_VERSION_STRING,
95 "CARGO_PKG_VERSION"
96 )
97 };
98}
99
100/// Generate a static item containing the program URL, and return its
101/// [`EntryAddr`](super::EntryAddr).
102#[macro_export]
103macro_rules! binary_info_rp_program_url {
104 ($url:expr) => {
105 $crate::binary_info_str!(
106 $crate::binary_info::consts::TAG_RASPBERRY_PI,
107 $crate::binary_info::consts::ID_RP_PROGRAM_URL,
108 $url
109 )
110 };
111}
112
113/// Generate a static item containing the `CARGO_PKG_HOMEPAGE` as the program URL,
114/// and return its [`EntryAddr`](super::EntryAddr).
115#[macro_export]
116macro_rules! binary_info_rp_cargo_homepage_url {
117 () => {
118 $crate::binary_info_env!(
119 $crate::binary_info::consts::TAG_RASPBERRY_PI,
120 $crate::binary_info::consts::ID_RP_PROGRAM_URL,
121 "CARGO_PKG_HOMEPAGE"
122 )
123 };
124}
125
126/// Generate a static item containing the program description, and return its
127/// [`EntryAddr`](super::EntryAddr).
128#[macro_export]
129macro_rules! binary_info_rp_program_description {
130 ($description:expr) => {
131 $crate::binary_info_str!(
132 $crate::binary_info::consts::TAG_RASPBERRY_PI,
133 $crate::binary_info::consts::ID_RP_PROGRAM_DESCRIPTION,
134 $description
135 )
136 };
137}
138
139/// Generate a static item containing whether this is a debug or a release
140/// build, and return its [`EntryAddr`](super::EntryAddr).
141#[macro_export]
142macro_rules! binary_info_rp_program_build_attribute {
143 () => {
144 $crate::binary_info_str!(
145 $crate::binary_info::consts::TAG_RASPBERRY_PI,
146 $crate::binary_info::consts::ID_RP_PROGRAM_BUILD_ATTRIBUTE,
147 {
148 if cfg!(debug_assertions) {
149 c"debug"
150 } else {
151 c"release"
152 }
153 }
154 )
155 };
156}
157
158/// Generate a static item containing the specific board this program runs on,
159/// and return its [`EntryAddr`](super::EntryAddr).
160#[macro_export]
161macro_rules! binary_info_rp_pico_board {
162 ($board:expr) => {
163 $crate::binary_info_str!(
164 $crate::binary_info::consts::TAG_RASPBERRY_PI,
165 $crate::binary_info::consts::ID_RP_PICO_BOARD,
166 $board
167 )
168 };
169}
170
171// 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..ce3829a7c
--- /dev/null
+++ b/embassy-rp/src/binary_info/mod.rs
@@ -0,0 +1,174 @@
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//! # use rp235x_hal as hal;
8//! #[link_section = ".bi_entries"]
9//! #[used]
10//! pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 3] = [
11//! hal::binary_info_rp_program_name!(c"Program Name Here"),
12//! hal::binary_info_rp_cargo_version!(),
13//! hal::binary_info_int!(hal::binary_info::make_tag(b"JP"), 0x0000_0001, 0x12345678),
14//! ];
15//! ```
16
17pub mod consts;
18
19mod types;
20pub use types::*;
21
22#[macro_use]
23mod macros;
24
25extern "C" {
26 /// The linker script sets this symbol to have the address of the first
27 /// entry in the `.bi_entries` section.
28 static __bi_entries_start: EntryAddr;
29 /// The linker script sets this symbol to have the address just past the
30 /// last entry in the `.bi_entries` section.
31 static __bi_entries_end: EntryAddr;
32 /// The linker script sets this symbol to have the address of the first
33 /// entry in the `.data` section.
34 static __sdata: u32;
35 /// The linker script sets this symbol to have the address just past the
36 /// first entry in the `.data` section.
37 static __edata: u32;
38 /// The linker script sets this symbol to have the address of the
39 /// initialisation data for the first entry in the `.data` section (i.e. a
40 /// flash address, not a RAM address).
41 static __sidata: u32;
42}
43
44/// Picotool can find this block in our ELF file and report interesting
45/// metadata.
46///
47/// The data here tells picotool the start and end flash addresses of our
48/// metadata.
49#[cfg(feature = "binary-info")]
50#[link_section = ".start_block"]
51#[used]
52pub static PICOTOOL_HEADER: Header = unsafe {
53 Header::new(
54 core::ptr::addr_of!(__bi_entries_start),
55 core::ptr::addr_of!(__bi_entries_end),
56 &MAPPING_TABLE,
57 )
58};
59
60/// This tells picotool how to convert RAM addresses back into Flash addresses
61#[cfg(feature = "binary-info")]
62pub static MAPPING_TABLE: [MappingTableEntry; 2] = [
63 // This is the entry for .data
64 MappingTableEntry {
65 source_addr_start: unsafe { core::ptr::addr_of!(__sidata) },
66 dest_addr_start: unsafe { core::ptr::addr_of!(__sdata) },
67 dest_addr_end: unsafe { core::ptr::addr_of!(__edata) },
68 },
69 // This is the terminating marker
70 MappingTableEntry::null(),
71];
72
73/// Create a 'Binary Info' entry containing the program name
74///
75/// This is well-known to picotool, and will be displayed if you run `picotool info`.
76///
77/// * Tag: [`consts::TAG_RASPBERRY_PI`]
78/// * ID: [`consts::ID_RP_PROGRAM_NAME`]
79pub const fn rp_program_name(name: &'static core::ffi::CStr) -> StringEntry {
80 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_NAME, name)
81}
82
83/// Create a 'Binary Info' entry containing the program version.
84///
85/// * Tag: [`consts::TAG_RASPBERRY_PI`]
86/// * Id: [`consts::ID_RP_PROGRAM_VERSION_STRING`]
87pub const fn rp_program_version(name: &'static core::ffi::CStr) -> StringEntry {
88 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_VERSION_STRING, name)
89}
90
91/// Create a 'Binary Info' entry with a URL
92///
93/// * Tag: [`consts::TAG_RASPBERRY_PI`]
94/// * Id: [`consts::ID_RP_PROGRAM_URL`]
95pub const fn rp_program_url(url: &'static core::ffi::CStr) -> StringEntry {
96 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_URL, url)
97}
98
99/// Create a 'Binary Info' with the program build date
100///
101/// * Tag: [`consts::TAG_RASPBERRY_PI`]
102/// * Id: [`consts::ID_RP_PROGRAM_BUILD_DATE_STRING`]
103pub const fn rp_program_build_date_string(value: &'static core::ffi::CStr) -> StringEntry {
104 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_BUILD_DATE_STRING, value)
105}
106
107/// Create a 'Binary Info' with the size of the binary
108///
109/// * Tag: [`consts::TAG_RASPBERRY_PI`]
110/// * Id: [`consts::ID_RP_BINARY_END`]
111pub const fn rp_binary_end(value: u32) -> IntegerEntry {
112 IntegerEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_BINARY_END, value)
113}
114
115/// Create a 'Binary Info' with a description of the program
116///
117/// * Tag: [`consts::TAG_RASPBERRY_PI`]
118/// * Id: [`consts::ID_RP_PROGRAM_DESCRIPTION`]
119pub const fn rp_program_description(value: &'static core::ffi::CStr) -> StringEntry {
120 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_DESCRIPTION, value)
121}
122
123/// Create a 'Binary Info' with some feature of the program
124///
125/// * Tag: [`consts::TAG_RASPBERRY_PI`]
126/// * Id: [`consts::ID_RP_PROGRAM_FEATURE`]
127pub const fn rp_program_feature(value: &'static core::ffi::CStr) -> StringEntry {
128 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_FEATURE, value)
129}
130
131/// Create a 'Binary Info' with some whether this was a Debug or Release build
132///
133/// * Tag: [`consts::TAG_RASPBERRY_PI`]
134/// * Id: [`consts::ID_RP_PROGRAM_BUILD_ATTRIBUTE`]
135pub const fn rp_program_build_attribute(value: &'static core::ffi::CStr) -> StringEntry {
136 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_BUILD_ATTRIBUTE, value)
137}
138
139/// Create a 'Binary Info' with the Pico SDK version used
140///
141/// * Tag: [`consts::TAG_RASPBERRY_PI`]
142/// * Id: [`consts::ID_RP_SDK_VERSION`]
143pub const fn rp_sdk_version(value: &'static core::ffi::CStr) -> StringEntry {
144 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_SDK_VERSION, value)
145}
146
147/// Create a 'Binary Info' with which board this program targets
148///
149/// * Tag: [`consts::TAG_RASPBERRY_PI`]
150/// * Id: [`consts::ID_RP_PICO_BOARD`]
151pub const fn rp_pico_board(value: &'static core::ffi::CStr) -> StringEntry {
152 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PICO_BOARD, value)
153}
154
155/// Create a 'Binary Info' with which `boot2` image this program uses
156///
157/// * Tag: [`consts::TAG_RASPBERRY_PI`]
158/// * Id: [`consts::ID_RP_BOOT2_NAME`]
159pub const fn rp_boot2_name(value: &'static core::ffi::CStr) -> StringEntry {
160 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_BOOT2_NAME, value)
161}
162
163/// Create a tag from two ASCII letters.
164///
165/// ```
166/// # use rp235x_hal as hal;
167/// let tag = hal::binary_info::make_tag(b"RP");
168/// assert_eq!(tag, 0x5052);
169/// ```
170pub const fn make_tag(c: &[u8; 2]) -> u16 {
171 u16::from_le_bytes(*c)
172}
173
174// 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..9f387b70f 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}
@@ -855,8 +902,8 @@ pub enum GpoutSrc {
855 Usb = ClkGpoutCtrlAuxsrc::CLK_USB as _, 902 Usb = ClkGpoutCtrlAuxsrc::CLK_USB as _,
856 /// ADC. 903 /// ADC.
857 Adc = ClkGpoutCtrlAuxsrc::CLK_ADC as _, 904 Adc = ClkGpoutCtrlAuxsrc::CLK_ADC as _,
858 /// RTC. 905 // RTC.
859 Rtc = ClkGpoutCtrlAuxsrc::CLK_RTC as _, 906 //Rtc = ClkGpoutCtrlAuxsrc::CLK_RTC as _,
860 /// REF. 907 /// REF.
861 Ref = ClkGpoutCtrlAuxsrc::CLK_REF as _, 908 Ref = ClkGpoutCtrlAuxsrc::CLK_REF as _,
862} 909}
@@ -877,6 +924,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> {
877 } 924 }
878 925
879 /// Set clock divider. 926 /// Set clock divider.
927 #[cfg(feature = "rp2040")]
880 pub fn set_div(&self, int: u32, frac: u8) { 928 pub fn set_div(&self, int: u32, frac: u8) {
881 let c = pac::CLOCKS; 929 let c = pac::CLOCKS;
882 c.clk_gpout_div(self.gpout.number()).write(|w| { 930 c.clk_gpout_div(self.gpout.number()).write(|w| {
@@ -885,6 +933,16 @@ impl<'d, T: GpoutPin> Gpout<'d, T> {
885 }); 933 });
886 } 934 }
887 935
936 /// Set clock divider.
937 #[cfg(feature = "rp235x")]
938 pub fn set_div(&self, int: u16, frac: u16) {
939 let c = pac::CLOCKS;
940 c.clk_gpout_div(self.gpout.number()).write(|w| {
941 w.set_int(int);
942 w.set_frac(frac);
943 });
944 }
945
888 /// Set clock source. 946 /// Set clock source.
889 pub fn set_src(&self, src: GpoutSrc) { 947 pub fn set_src(&self, src: GpoutSrc) {
890 let c = pac::CLOCKS; 948 let c = pac::CLOCKS;
@@ -924,13 +982,13 @@ impl<'d, T: GpoutPin> Gpout<'d, T> {
924 ClkGpoutCtrlAuxsrc::CLK_SYS => clk_sys_freq(), 982 ClkGpoutCtrlAuxsrc::CLK_SYS => clk_sys_freq(),
925 ClkGpoutCtrlAuxsrc::CLK_USB => clk_usb_freq(), 983 ClkGpoutCtrlAuxsrc::CLK_USB => clk_usb_freq(),
926 ClkGpoutCtrlAuxsrc::CLK_ADC => clk_adc_freq(), 984 ClkGpoutCtrlAuxsrc::CLK_ADC => clk_adc_freq(),
927 ClkGpoutCtrlAuxsrc::CLK_RTC => clk_rtc_freq() as _, 985 //ClkGpoutCtrlAuxsrc::CLK_RTC => clk_rtc_freq() as _,
928 ClkGpoutCtrlAuxsrc::CLK_REF => clk_ref_freq(), 986 ClkGpoutCtrlAuxsrc::CLK_REF => clk_ref_freq(),
929 _ => unreachable!(), 987 _ => unreachable!(),
930 }; 988 };
931 989
932 let div = c.clk_gpout_div(self.gpout.number()).read(); 990 let div = c.clk_gpout_div(self.gpout.number()).read();
933 let int = if div.int() == 0 { 65536 } else { div.int() } as u64; 991 let int = if div.int() == 0 { 0xFFFF } else { div.int() } as u64;
934 let frac = div.frac() as u64; 992 let frac = div.frac() as u64;
935 993
936 ((base as u64 * 256) / (int * 256 + frac)) as u32 994 ((base as u64 * 256) / (int * 256 + frac)) as u32
@@ -987,7 +1045,7 @@ impl rand_core::RngCore for RoscRng {
987/// and can only be exited through resets, dormant-wake GPIO interrupts, 1045/// and can only be exited through resets, dormant-wake GPIO interrupts,
988/// and RTC interrupts. If RTC is clocked from an internal clock source 1046/// and RTC interrupts. If RTC is clocked from an internal clock source
989/// it will be stopped and not function as a wakeup source. 1047/// it will be stopped and not function as a wakeup source.
990#[cfg(target_arch = "arm")] 1048#[cfg(all(target_arch = "arm", feature = "rp2040"))]
991pub fn dormant_sleep() { 1049pub fn dormant_sleep() {
992 struct Set<T: Copy, F: Fn()>(Reg<T, RW>, T, F); 1050 struct Set<T: Copy, F: Fn()>(Reg<T, RW>, T, F);
993 1051
@@ -1107,7 +1165,7 @@ pub fn dormant_sleep() {
1107 coma = in (reg) 0x636f6d61, 1165 coma = in (reg) 0x636f6d61,
1108 ); 1166 );
1109 } else { 1167 } else {
1110 pac::ROSC.dormant().write_value(0x636f6d61); 1168 pac::ROSC.dormant().write_value(rp_pac::rosc::regs::Dormant(0x636f6d61));
1111 } 1169 }
1112 } 1170 }
1113} 1171}
diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs
index 8c04b43a1..df10da0b6 100644
--- a/embassy-rp/src/dma.rs
+++ b/embassy-rp/src/dma.rs
@@ -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);
@@ -297,3 +303,11 @@ channel!(DMA_CH8, 8);
297channel!(DMA_CH9, 9); 303channel!(DMA_CH9, 9);
298channel!(DMA_CH10, 10); 304channel!(DMA_CH10, 10);
299channel!(DMA_CH11, 11); 305channel!(DMA_CH11, 11);
306#[cfg(feature = "rp235x")]
307channel!(DMA_CH12, 12);
308#[cfg(feature = "rp235x")]
309channel!(DMA_CH13, 13);
310#[cfg(feature = "rp235x")]
311channel!(DMA_CH14, 14);
312#[cfg(feature = "rp235x")]
313channel!(DMA_CH15, 15);
diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs
index 9e4542b2f..cc84bb12d 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,11 @@ 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
662 #[repr(C)] 675 #[repr(C)]
663 struct FlashCommand { 676 struct FlashCommand {
664 cmd_addr: *const u8, 677 cmd_addr: *const u8,
@@ -758,6 +771,7 @@ mod ram_helpers {
758 /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) 771 /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT)
759 #[inline(never)] 772 #[inline(never)]
760 #[link_section = ".data.ram_func"] 773 #[link_section = ".data.ram_func"]
774 #[cfg(feature = "rp2040")]
761 unsafe fn read_flash_inner(cmd: FlashCommand, ptrs: *const FlashFunctionPointers) { 775 unsafe fn read_flash_inner(cmd: FlashCommand, ptrs: *const FlashFunctionPointers) {
762 #[cfg(target_arch = "arm")] 776 #[cfg(target_arch = "arm")]
763 core::arch::asm!( 777 core::arch::asm!(
@@ -874,6 +888,11 @@ mod ram_helpers {
874 clobber_abi("C"), 888 clobber_abi("C"),
875 ); 889 );
876 } 890 }
891
892 #[inline(never)]
893 #[link_section = ".data.ram_func"]
894 #[cfg(feature = "rp235x")]
895 unsafe fn read_flash_inner(_cmd: FlashCommand, _ptrs: *const FlashFunctionPointers) {}
877} 896}
878 897
879/// Make sure to uphold the contract points with rp2040-flash. 898/// Make sure to uphold the contract points with rp2040-flash.
@@ -887,7 +906,7 @@ pub(crate) unsafe fn in_ram(operation: impl FnOnce()) -> Result<(), Error> {
887 } 906 }
888 907
889 // Make sure CORE1 is paused during the entire duration of the RAM function 908 // Make sure CORE1 is paused during the entire duration of the RAM function
890 crate::multicore::pause_core1(); 909 //crate::multicore::pause_core1();
891 910
892 critical_section::with(|_| { 911 critical_section::with(|_| {
893 // Wait for all DMA channels in flash to finish before ram operation 912 // Wait for all DMA channels in flash to finish before ram operation
@@ -904,7 +923,7 @@ pub(crate) unsafe fn in_ram(operation: impl FnOnce()) -> Result<(), Error> {
904 }); 923 });
905 924
906 // Resume CORE1 execution 925 // Resume CORE1 execution
907 crate::multicore::resume_core1(); 926 //crate::multicore::resume_core1();
908 Ok(()) 927 Ok(())
909} 928}
910 929
diff --git a/embassy-rp/src/float/mod.rs b/embassy-rp/src/float/mod.rs
index 3ad6f1c50..1df8c0e08 100644
--- a/embassy-rp/src/float/mod.rs
+++ b/embassy-rp/src/float/mod.rs
@@ -1,3 +1,4 @@
1#![cfg(feature = "rp2040")]
1// Credit: taken from `rp-hal` (also licensed Apache+MIT) 2// Credit: taken from `rp-hal` (also licensed Apache+MIT)
2// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/mod.rs 3// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/mod.rs
3 4
diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs
index ea87fd9da..8d6a8f2bd 100644
--- a/embassy-rp/src/gpio.rs
+++ b/embassy-rp/src/gpio.rs
@@ -178,6 +178,13 @@ impl<'d> Input<'d> {
178 pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake<'_> { 178 pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake<'_> {
179 self.pin.dormant_wake(cfg) 179 self.pin.dormant_wake(cfg)
180 } 180 }
181
182 /// Set the pin's pad isolation
183 #[cfg(feature = "rp235x")]
184 #[inline]
185 pub fn set_pad_isolation(&mut self, isolate: bool) {
186 self.pin.set_pad_isolation(isolate)
187 }
181} 188}
182 189
183/// Interrupt trigger levels. 190/// Interrupt trigger levels.
@@ -413,6 +420,13 @@ impl<'d> Output<'d> {
413 pub fn toggle(&mut self) { 420 pub fn toggle(&mut self) {
414 self.pin.toggle() 421 self.pin.toggle()
415 } 422 }
423
424 /// Set the pin's pad isolation
425 #[cfg(feature = "rp235x")]
426 #[inline]
427 pub fn set_pad_isolation(&mut self, isolate: bool) {
428 self.pin.set_pad_isolation(isolate)
429 }
416} 430}
417 431
418/// GPIO output open-drain. 432/// GPIO output open-drain.
@@ -539,6 +553,13 @@ impl<'d> OutputOpenDrain<'d> {
539 pub async fn wait_for_any_edge(&mut self) { 553 pub async fn wait_for_any_edge(&mut self) {
540 self.pin.wait_for_any_edge().await; 554 self.pin.wait_for_any_edge().await;
541 } 555 }
556
557 /// Set the pin's pad isolation
558 #[cfg(feature = "rp235x")]
559 #[inline]
560 pub fn set_pad_isolation(&mut self, isolate: bool) {
561 self.pin.set_pad_isolation(isolate)
562 }
542} 563}
543 564
544/// GPIO flexible pin. 565/// GPIO flexible pin.
@@ -564,7 +585,15 @@ impl<'d> Flex<'d> {
564 }); 585 });
565 586
566 pin.gpio().ctrl().write(|w| { 587 pin.gpio().ctrl().write(|w| {
588 #[cfg(feature = "rp2040")]
567 w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0 as _); 589 w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0 as _);
590 #[cfg(feature = "rp235x")]
591 w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIOB_PROC_0 as _);
592 });
593
594 #[cfg(feature = "rp235x")]
595 pin.pad_ctrl().modify(|w| {
596 w.set_iso(false);
568 }); 597 });
569 598
570 Self { pin: pin.map_into() } 599 Self { pin: pin.map_into() }
@@ -760,6 +789,15 @@ impl<'d> Flex<'d> {
760 cfg, 789 cfg,
761 } 790 }
762 } 791 }
792
793 /// Set the pin's pad isolation
794 #[cfg(feature = "rp235x")]
795 #[inline]
796 pub fn set_pad_isolation(&mut self, isolate: bool) {
797 self.pin.pad_ctrl().modify(|w| {
798 w.set_iso(isolate);
799 });
800 }
763} 801}
764 802
765impl<'d> Drop for Flex<'d> { 803impl<'d> Drop for Flex<'d> {
@@ -956,6 +994,44 @@ impl_pin!(PIN_27, Bank::Bank0, 27);
956impl_pin!(PIN_28, Bank::Bank0, 28); 994impl_pin!(PIN_28, Bank::Bank0, 28);
957impl_pin!(PIN_29, Bank::Bank0, 29); 995impl_pin!(PIN_29, Bank::Bank0, 29);
958 996
997#[cfg(feature = "rp235xb")]
998impl_pin!(PIN_30, Bank::Bank0, 30);
999#[cfg(feature = "rp235xb")]
1000impl_pin!(PIN_31, Bank::Bank0, 31);
1001#[cfg(feature = "rp235xb")]
1002impl_pin!(PIN_32, Bank::Bank0, 32);
1003#[cfg(feature = "rp235xb")]
1004impl_pin!(PIN_33, Bank::Bank0, 33);
1005#[cfg(feature = "rp235xb")]
1006impl_pin!(PIN_34, Bank::Bank0, 34);
1007#[cfg(feature = "rp235xb")]
1008impl_pin!(PIN_35, Bank::Bank0, 35);
1009#[cfg(feature = "rp235xb")]
1010impl_pin!(PIN_36, Bank::Bank0, 36);
1011#[cfg(feature = "rp235xb")]
1012impl_pin!(PIN_37, Bank::Bank0, 37);
1013#[cfg(feature = "rp235xb")]
1014impl_pin!(PIN_38, Bank::Bank0, 38);
1015#[cfg(feature = "rp235xb")]
1016impl_pin!(PIN_39, Bank::Bank0, 39);
1017#[cfg(feature = "rp235xb")]
1018impl_pin!(PIN_40, Bank::Bank0, 40);
1019#[cfg(feature = "rp235xb")]
1020impl_pin!(PIN_41, Bank::Bank0, 41);
1021#[cfg(feature = "rp235xb")]
1022impl_pin!(PIN_42, Bank::Bank0, 42);
1023#[cfg(feature = "rp235xb")]
1024impl_pin!(PIN_43, Bank::Bank0, 43);
1025#[cfg(feature = "rp235xb")]
1026impl_pin!(PIN_44, Bank::Bank0, 44);
1027#[cfg(feature = "rp235xb")]
1028impl_pin!(PIN_45, Bank::Bank0, 45);
1029#[cfg(feature = "rp235xb")]
1030impl_pin!(PIN_46, Bank::Bank0, 46);
1031#[cfg(feature = "rp235xb")]
1032impl_pin!(PIN_47, Bank::Bank0, 47);
1033
1034// TODO rp235x bank1 as gpio support
959#[cfg(feature = "qspi-as-gpio")] 1035#[cfg(feature = "qspi-as-gpio")]
960impl_pin!(PIN_QSPI_SCLK, Bank::Qspi, 0); 1036impl_pin!(PIN_QSPI_SCLK, Bank::Qspi, 0);
961#[cfg(feature = "qspi-as-gpio")] 1037#[cfg(feature = "qspi-as-gpio")]
diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs
index ac2b1bc5a..d95b17ff1 100644
--- a/embassy-rp/src/i2c.rs
+++ b/embassy-rp/src/i2c.rs
@@ -884,3 +884,39 @@ impl_pin!(PIN_26, I2C1, SdaPin);
884impl_pin!(PIN_27, I2C1, SclPin); 884impl_pin!(PIN_27, I2C1, SclPin);
885impl_pin!(PIN_28, I2C0, SdaPin); 885impl_pin!(PIN_28, I2C0, SdaPin);
886impl_pin!(PIN_29, I2C0, SclPin); 886impl_pin!(PIN_29, I2C0, SclPin);
887#[cfg(feature = "rp235xb")]
888impl_pin!(PIN_30, I2C1, SdaPin);
889#[cfg(feature = "rp235xb")]
890impl_pin!(PIN_31, I2C1, SclPin);
891#[cfg(feature = "rp235xb")]
892impl_pin!(PIN_32, I2C0, SdaPin);
893#[cfg(feature = "rp235xb")]
894impl_pin!(PIN_33, I2C0, SclPin);
895#[cfg(feature = "rp235xb")]
896impl_pin!(PIN_34, I2C1, SdaPin);
897#[cfg(feature = "rp235xb")]
898impl_pin!(PIN_35, I2C1, SclPin);
899#[cfg(feature = "rp235xb")]
900impl_pin!(PIN_36, I2C0, SdaPin);
901#[cfg(feature = "rp235xb")]
902impl_pin!(PIN_37, I2C0, SclPin);
903#[cfg(feature = "rp235xb")]
904impl_pin!(PIN_38, I2C1, SdaPin);
905#[cfg(feature = "rp235xb")]
906impl_pin!(PIN_39, I2C1, SclPin);
907#[cfg(feature = "rp235xb")]
908impl_pin!(PIN_40, I2C0, SdaPin);
909#[cfg(feature = "rp235xb")]
910impl_pin!(PIN_41, I2C0, SclPin);
911#[cfg(feature = "rp235xb")]
912impl_pin!(PIN_42, I2C1, SdaPin);
913#[cfg(feature = "rp235xb")]
914impl_pin!(PIN_43, I2C1, SclPin);
915#[cfg(feature = "rp235xb")]
916impl_pin!(PIN_44, I2C0, SdaPin);
917#[cfg(feature = "rp235xb")]
918impl_pin!(PIN_45, I2C0, SclPin);
919#[cfg(feature = "rp235xb")]
920impl_pin!(PIN_46, I2C1, SdaPin);
921#[cfg(feature = "rp235xb")]
922impl_pin!(PIN_47, I2C1, SclPin);
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index 471e7f8b1..c5b2498b4 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -15,6 +15,11 @@ 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;
@@ -41,14 +46,20 @@ pub(crate) mod relocate;
41 46
42// Reexports 47// Reexports
43pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; 48pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
44#[cfg(feature = "unstable-pac")] 49#[cfg(all(feature = "unstable-pac", feature = "rp235x"))]
50pub use rp23_pac as pac;
51#[cfg(all(feature = "unstable-pac", feature = "rp2040"))]
45pub use rp_pac as pac; 52pub use rp_pac as pac;
46#[cfg(not(feature = "unstable-pac"))] 53
54#[cfg(all(not(feature = "unstable-pac"), feature = "rp235x"))]
55pub(crate) use rp23_pac as pac;
56#[cfg(all(not(feature = "unstable-pac"), feature = "rp2040"))]
47pub(crate) use rp_pac as pac; 57pub(crate) use rp_pac as pac;
48 58
49#[cfg(feature = "rt")] 59#[cfg(feature = "rt")]
50pub use crate::pac::NVIC_PRIO_BITS; 60pub use crate::pac::NVIC_PRIO_BITS;
51 61
62#[cfg(feature = "rp2040")]
52embassy_hal_internal::interrupt_mod!( 63embassy_hal_internal::interrupt_mod!(
53 TIMER_IRQ_0, 64 TIMER_IRQ_0,
54 TIMER_IRQ_1, 65 TIMER_IRQ_1,
@@ -84,6 +95,54 @@ embassy_hal_internal::interrupt_mod!(
84 SWI_IRQ_5, 95 SWI_IRQ_5,
85); 96);
86 97
98#[cfg(feature = "rp235x")]
99embassy_hal_internal::interrupt_mod!(
100 TIMER0_IRQ_0,
101 TIMER0_IRQ_1,
102 TIMER0_IRQ_2,
103 TIMER0_IRQ_3,
104 TIMER1_IRQ_0,
105 TIMER1_IRQ_1,
106 TIMER1_IRQ_2,
107 TIMER1_IRQ_3,
108 PWM_IRQ_WRAP_0,
109 PWM_IRQ_WRAP_1,
110 DMA_IRQ_0,
111 DMA_IRQ_1,
112 USBCTRL_IRQ,
113 PIO0_IRQ_0,
114 PIO0_IRQ_1,
115 PIO1_IRQ_0,
116 PIO1_IRQ_1,
117 PIO2_IRQ_0,
118 PIO2_IRQ_1,
119 IO_IRQ_BANK0,
120 IO_IRQ_BANK0_NS,
121 IO_IRQ_QSPI,
122 IO_IRQ_QSPI_NS,
123 SIO_IRQ_FIFO,
124 SIO_IRQ_BELL,
125 SIO_IRQ_FIFO_NS,
126 SIO_IRQ_BELL_NS,
127 CLOCKS_IRQ,
128 SPI0_IRQ,
129 SPI1_IRQ,
130 UART0_IRQ,
131 UART1_IRQ,
132 ADC_IRQ_FIFO,
133 I2C0_IRQ,
134 I2C1_IRQ,
135 TRNG_IRQ,
136 PLL_SYS_IRQ,
137 PLL_USB_IRQ,
138 SWI_IRQ_0,
139 SWI_IRQ_1,
140 SWI_IRQ_2,
141 SWI_IRQ_3,
142 SWI_IRQ_4,
143 SWI_IRQ_5,
144);
145
87/// Macro to bind interrupts to handlers. 146/// Macro to bind interrupts to handlers.
88/// 147///
89/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) 148/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
@@ -123,6 +182,95 @@ macro_rules! bind_interrupts {
123 }; 182 };
124} 183}
125 184
185#[cfg(feature = "rp2040")]
186embassy_hal_internal::peripherals! {
187 PIN_0,
188 PIN_1,
189 PIN_2,
190 PIN_3,
191 PIN_4,
192 PIN_5,
193 PIN_6,
194 PIN_7,
195 PIN_8,
196 PIN_9,
197 PIN_10,
198 PIN_11,
199 PIN_12,
200 PIN_13,
201 PIN_14,
202 PIN_15,
203 PIN_16,
204 PIN_17,
205 PIN_18,
206 PIN_19,
207 PIN_20,
208 PIN_21,
209 PIN_22,
210 PIN_23,
211 PIN_24,
212 PIN_25,
213 PIN_26,
214 PIN_27,
215 PIN_28,
216 PIN_29,
217 PIN_QSPI_SCLK,
218 PIN_QSPI_SS,
219 PIN_QSPI_SD0,
220 PIN_QSPI_SD1,
221 PIN_QSPI_SD2,
222 PIN_QSPI_SD3,
223
224 UART0,
225 UART1,
226
227 SPI0,
228 SPI1,
229
230 I2C0,
231 I2C1,
232
233 DMA_CH0,
234 DMA_CH1,
235 DMA_CH2,
236 DMA_CH3,
237 DMA_CH4,
238 DMA_CH5,
239 DMA_CH6,
240 DMA_CH7,
241 DMA_CH8,
242 DMA_CH9,
243 DMA_CH10,
244 DMA_CH11,
245
246 PWM_SLICE0,
247 PWM_SLICE1,
248 PWM_SLICE2,
249 PWM_SLICE3,
250 PWM_SLICE4,
251 PWM_SLICE5,
252 PWM_SLICE6,
253 PWM_SLICE7,
254
255 USB,
256
257 RTC,
258
259 FLASH,
260
261 ADC,
262 ADC_TEMP_SENSOR,
263
264 CORE1,
265
266 PIO0,
267 PIO1,
268
269 WATCHDOG,
270 BOOTSEL,
271}
272
273#[cfg(feature = "rp235x")]
126embassy_hal_internal::peripherals! { 274embassy_hal_internal::peripherals! {
127 PIN_0, 275 PIN_0,
128 PIN_1, 276 PIN_1,
@@ -154,6 +302,42 @@ embassy_hal_internal::peripherals! {
154 PIN_27, 302 PIN_27,
155 PIN_28, 303 PIN_28,
156 PIN_29, 304 PIN_29,
305 #[cfg(feature = "rp235xb")]
306 PIN_30,
307 #[cfg(feature = "rp235xb")]
308 PIN_31,
309 #[cfg(feature = "rp235xb")]
310 PIN_32,
311 #[cfg(feature = "rp235xb")]
312 PIN_33,
313 #[cfg(feature = "rp235xb")]
314 PIN_34,
315 #[cfg(feature = "rp235xb")]
316 PIN_35,
317 #[cfg(feature = "rp235xb")]
318 PIN_36,
319 #[cfg(feature = "rp235xb")]
320 PIN_37,
321 #[cfg(feature = "rp235xb")]
322 PIN_38,
323 #[cfg(feature = "rp235xb")]
324 PIN_39,
325 #[cfg(feature = "rp235xb")]
326 PIN_40,
327 #[cfg(feature = "rp235xb")]
328 PIN_41,
329 #[cfg(feature = "rp235xb")]
330 PIN_42,
331 #[cfg(feature = "rp235xb")]
332 PIN_43,
333 #[cfg(feature = "rp235xb")]
334 PIN_44,
335 #[cfg(feature = "rp235xb")]
336 PIN_45,
337 #[cfg(feature = "rp235xb")]
338 PIN_46,
339 #[cfg(feature = "rp235xb")]
340 PIN_47,
157 PIN_QSPI_SCLK, 341 PIN_QSPI_SCLK,
158 PIN_QSPI_SS, 342 PIN_QSPI_SS,
159 PIN_QSPI_SD0, 343 PIN_QSPI_SD0,
@@ -182,6 +366,10 @@ embassy_hal_internal::peripherals! {
182 DMA_CH9, 366 DMA_CH9,
183 DMA_CH10, 367 DMA_CH10,
184 DMA_CH11, 368 DMA_CH11,
369 DMA_CH12,
370 DMA_CH13,
371 DMA_CH14,
372 DMA_CH15,
185 373
186 PWM_SLICE0, 374 PWM_SLICE0,
187 PWM_SLICE1, 375 PWM_SLICE1,
@@ -191,6 +379,10 @@ embassy_hal_internal::peripherals! {
191 PWM_SLICE5, 379 PWM_SLICE5,
192 PWM_SLICE6, 380 PWM_SLICE6,
193 PWM_SLICE7, 381 PWM_SLICE7,
382 PWM_SLICE8,
383 PWM_SLICE9,
384 PWM_SLICE10,
385 PWM_SLICE11,
194 386
195 USB, 387 USB,
196 388
@@ -205,6 +397,7 @@ embassy_hal_internal::peripherals! {
205 397
206 PIO0, 398 PIO0,
207 PIO1, 399 PIO1,
400 PIO2,
208 401
209 WATCHDOG, 402 WATCHDOG,
210 BOOTSEL, 403 BOOTSEL,
@@ -279,6 +472,7 @@ pub fn install_core0_stack_guard() -> Result<(), ()> {
279 unsafe { install_stack_guard(core::ptr::addr_of_mut!(_stack_end)) } 472 unsafe { install_stack_guard(core::ptr::addr_of_mut!(_stack_end)) }
280} 473}
281 474
475#[cfg(feature = "rp2040")]
282#[inline(always)] 476#[inline(always)]
283fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> { 477fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> {
284 let core = unsafe { cortex_m::Peripherals::steal() }; 478 let core = unsafe { cortex_m::Peripherals::steal() };
@@ -306,6 +500,24 @@ fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> {
306 Ok(()) 500 Ok(())
307} 501}
308 502
503#[cfg(feature = "rp235x")]
504#[inline(always)]
505fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> {
506 let core = unsafe { cortex_m::Peripherals::steal() };
507
508 // Fail if MPU is already configured
509 if core.MPU.ctrl.read() != 0 {
510 return Err(());
511 }
512
513 unsafe {
514 core.MPU.ctrl.write(5); // enable mpu with background default map
515 core.MPU.rbar.write(stack_bottom as u32 & !0xff); // set address
516 core.MPU.rlar.write(1); // enable region
517 }
518 Ok(())
519}
520
309/// HAL configuration for RP. 521/// HAL configuration for RP.
310pub mod config { 522pub mod config {
311 use crate::clocks::ClockConfig; 523 use crate::clocks::ClockConfig;
@@ -354,7 +566,7 @@ pub fn init(config: config::Config) -> Peripherals {
354 peripherals 566 peripherals
355} 567}
356 568
357#[cfg(feature = "rt")] 569#[cfg(all(feature = "rt", feature = "rp2040"))]
358#[cortex_m_rt::pre_init] 570#[cortex_m_rt::pre_init]
359unsafe fn pre_init() { 571unsafe fn pre_init() {
360 // SIO does not get reset when core0 is reset with either `scb::sys_reset()` or with SWD. 572 // 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..d1ce688ce 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..89313086b 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..3f96a3f05 100644
--- a/embassy-rp/src/pwm.rs
+++ b/embassy-rp/src/pwm.rs
@@ -363,6 +363,15 @@ slice!(PWM_SLICE5, 5);
363slice!(PWM_SLICE6, 6); 363slice!(PWM_SLICE6, 6);
364slice!(PWM_SLICE7, 7); 364slice!(PWM_SLICE7, 7);
365 365
366#[cfg(feature = "rp235x")]
367slice!(PWM_SLICE8, 8);
368#[cfg(feature = "rp235x")]
369slice!(PWM_SLICE9, 9);
370#[cfg(feature = "rp235x")]
371slice!(PWM_SLICE10, 10);
372#[cfg(feature = "rp235x")]
373slice!(PWM_SLICE11, 11);
374
366/// PWM Channel A. 375/// PWM Channel A.
367pub trait ChannelAPin<T: Slice>: GpioPin {} 376pub trait ChannelAPin<T: Slice>: GpioPin {}
368/// PWM Channel B. 377/// PWM Channel B.
@@ -404,3 +413,39 @@ impl_pin!(PIN_26, PWM_SLICE5, ChannelAPin);
404impl_pin!(PIN_27, PWM_SLICE5, ChannelBPin); 413impl_pin!(PIN_27, PWM_SLICE5, ChannelBPin);
405impl_pin!(PIN_28, PWM_SLICE6, ChannelAPin); 414impl_pin!(PIN_28, PWM_SLICE6, ChannelAPin);
406impl_pin!(PIN_29, PWM_SLICE6, ChannelBPin); 415impl_pin!(PIN_29, PWM_SLICE6, ChannelBPin);
416#[cfg(feature = "rp235xb")]
417impl_pin!(PIN_30, PWM_SLICE7, ChannelAPin);
418#[cfg(feature = "rp235xb")]
419impl_pin!(PIN_31, PWM_SLICE7, ChannelBPin);
420#[cfg(feature = "rp235xb")]
421impl_pin!(PIN_32, PWM_SLICE8, ChannelAPin);
422#[cfg(feature = "rp235xb")]
423impl_pin!(PIN_33, PWM_SLICE8, ChannelBPin);
424#[cfg(feature = "rp235xb")]
425impl_pin!(PIN_34, PWM_SLICE9, ChannelAPin);
426#[cfg(feature = "rp235xb")]
427impl_pin!(PIN_35, PWM_SLICE9, ChannelBPin);
428#[cfg(feature = "rp235xb")]
429impl_pin!(PIN_36, PWM_SLICE10, ChannelAPin);
430#[cfg(feature = "rp235xb")]
431impl_pin!(PIN_37, PWM_SLICE10, ChannelBPin);
432#[cfg(feature = "rp235xb")]
433impl_pin!(PIN_38, PWM_SLICE11, ChannelAPin);
434#[cfg(feature = "rp235xb")]
435impl_pin!(PIN_39, PWM_SLICE11, ChannelBPin);
436#[cfg(feature = "rp235xb")]
437impl_pin!(PIN_40, PWM_SLICE8, ChannelAPin);
438#[cfg(feature = "rp235xb")]
439impl_pin!(PIN_41, PWM_SLICE8, ChannelBPin);
440#[cfg(feature = "rp235xb")]
441impl_pin!(PIN_42, PWM_SLICE9, ChannelAPin);
442#[cfg(feature = "rp235xb")]
443impl_pin!(PIN_43, PWM_SLICE9, ChannelBPin);
444#[cfg(feature = "rp235xb")]
445impl_pin!(PIN_44, PWM_SLICE10, ChannelAPin);
446#[cfg(feature = "rp235xb")]
447impl_pin!(PIN_45, PWM_SLICE10, ChannelBPin);
448#[cfg(feature = "rp235xb")]
449impl_pin!(PIN_46, PWM_SLICE11, ChannelAPin);
450#[cfg(feature = "rp235xb")]
451impl_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/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs
index 2ce7ac645..4ba5bad4b 100644
--- a/embassy-rp/src/rtc/mod.rs
+++ b/embassy-rp/src/rtc/mod.rs
@@ -1,3 +1,4 @@
1#![cfg(feature = "rp2040")]
1//! RTC driver. 2//! RTC driver.
2mod filter; 3mod filter;
3 4
diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs
index 1617c144c..bc852ff7b 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..6f532fa8e 100644
--- a/embassy-rp/src/time_driver.rs
+++ b/embassy-rp/src/time_driver.rs
@@ -10,6 +10,11 @@ use embassy_time_driver::{AlarmHandle, Driver};
10use crate::interrupt::InterruptExt; 10use crate::interrupt::InterruptExt;
11use crate::{interrupt, pac}; 11use crate::{interrupt, pac};
12 12
13#[cfg(feature = "rp2040")]
14use pac::TIMER;
15#[cfg(feature = "rp235x")]
16use pac::TIMER0 as TIMER;
17
13struct AlarmState { 18struct AlarmState {
14 timestamp: Cell<u64>, 19 timestamp: Cell<u64>,
15 callback: Cell<Option<(fn(*mut ()), *mut ())>>, 20 callback: Cell<Option<(fn(*mut ()), *mut ())>>,
@@ -35,9 +40,9 @@ embassy_time_driver::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{
35impl Driver for TimerDriver { 40impl Driver for TimerDriver {
36 fn now(&self) -> u64 { 41 fn now(&self) -> u64 {
37 loop { 42 loop {
38 let hi = pac::TIMER.timerawh().read(); 43 let hi = TIMER.timerawh().read();
39 let lo = pac::TIMER.timerawl().read(); 44 let lo = TIMER.timerawl().read();
40 let hi2 = pac::TIMER.timerawh().read(); 45 let hi2 = TIMER.timerawh().read();
41 if hi == hi2 { 46 if hi == hi2 {
42 return (hi as u64) << 32 | (lo as u64); 47 return (hi as u64) << 32 | (lo as u64);
43 } 48 }
@@ -77,13 +82,13 @@ impl Driver for TimerDriver {
77 // Note that we're not checking the high bits at all. This means the irq may fire early 82 // 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 83 // 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. 84 // it is checked if the alarm time has passed.
80 pac::TIMER.alarm(n).write_value(timestamp as u32); 85 TIMER.alarm(n).write_value(timestamp as u32);
81 86
82 let now = self.now(); 87 let now = self.now();
83 if timestamp <= now { 88 if timestamp <= now {
84 // If alarm timestamp has passed the alarm will not fire. 89 // If alarm timestamp has passed the alarm will not fire.
85 // Disarm the alarm and return `false` to indicate that. 90 // Disarm the alarm and return `false` to indicate that.
86 pac::TIMER.armed().write(|w| w.set_armed(1 << n)); 91 TIMER.armed().write(|w| w.set_armed(1 << n));
87 92
88 alarm.timestamp.set(u64::MAX); 93 alarm.timestamp.set(u64::MAX);
89 94
@@ -105,17 +110,17 @@ impl TimerDriver {
105 } else { 110 } else {
106 // Not elapsed, arm it again. 111 // Not elapsed, arm it again.
107 // This can happen if it was set more than 2^32 us in the future. 112 // 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); 113 TIMER.alarm(n).write_value(timestamp as u32);
109 } 114 }
110 }); 115 });
111 116
112 // clear the irq 117 // clear the irq
113 pac::TIMER.intr().write(|w| w.set_alarm(n, true)); 118 TIMER.intr().write(|w| w.set_alarm(n, true));
114 } 119 }
115 120
116 fn trigger_alarm(&self, n: usize, cs: CriticalSection) { 121 fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
117 // disarm 122 // disarm
118 pac::TIMER.armed().write(|w| w.set_armed(1 << n)); 123 TIMER.armed().write(|w| w.set_armed(1 << n));
119 124
120 let alarm = &self.alarms.borrow(cs)[n]; 125 let alarm = &self.alarms.borrow(cs)[n];
121 alarm.timestamp.set(u64::MAX); 126 alarm.timestamp.set(u64::MAX);
@@ -138,38 +143,72 @@ pub unsafe fn init() {
138 }); 143 });
139 144
140 // enable all irqs 145 // enable all irqs
141 pac::TIMER.inte().write(|w| { 146 TIMER.inte().write(|w| {
142 w.set_alarm(0, true); 147 w.set_alarm(0, true);
143 w.set_alarm(1, true); 148 w.set_alarm(1, true);
144 w.set_alarm(2, true); 149 w.set_alarm(2, true);
145 w.set_alarm(3, true); 150 w.set_alarm(3, true);
146 }); 151 });
147 interrupt::TIMER_IRQ_0.enable(); 152 #[cfg(feature = "rp2040")]
148 interrupt::TIMER_IRQ_1.enable(); 153 {
149 interrupt::TIMER_IRQ_2.enable(); 154 interrupt::TIMER_IRQ_0.enable();
150 interrupt::TIMER_IRQ_3.enable(); 155 interrupt::TIMER_IRQ_1.enable();
156 interrupt::TIMER_IRQ_2.enable();
157 interrupt::TIMER_IRQ_3.enable();
158 }
159 #[cfg(feature = "rp235x")]
160 {
161 interrupt::TIMER0_IRQ_0.enable();
162 interrupt::TIMER0_IRQ_1.enable();
163 interrupt::TIMER0_IRQ_2.enable();
164 interrupt::TIMER0_IRQ_3.enable();
165 }
151} 166}
152 167
153#[cfg(feature = "rt")] 168#[cfg(all(feature = "rt", feature = "rp2040"))]
154#[interrupt] 169#[interrupt]
155fn TIMER_IRQ_0() { 170fn TIMER_IRQ_0() {
156 DRIVER.check_alarm(0) 171 DRIVER.check_alarm(0)
157} 172}
158 173
159#[cfg(feature = "rt")] 174#[cfg(all(feature = "rt", feature = "rp2040"))]
160#[interrupt] 175#[interrupt]
161fn TIMER_IRQ_1() { 176fn TIMER_IRQ_1() {
162 DRIVER.check_alarm(1) 177 DRIVER.check_alarm(1)
163} 178}
164 179
165#[cfg(feature = "rt")] 180#[cfg(all(feature = "rt", feature = "rp2040"))]
166#[interrupt] 181#[interrupt]
167fn TIMER_IRQ_2() { 182fn TIMER_IRQ_2() {
168 DRIVER.check_alarm(2) 183 DRIVER.check_alarm(2)
169} 184}
170 185
171#[cfg(feature = "rt")] 186#[cfg(all(feature = "rt", feature = "rp2040"))]
172#[interrupt] 187#[interrupt]
173fn TIMER_IRQ_3() { 188fn TIMER_IRQ_3() {
174 DRIVER.check_alarm(3) 189 DRIVER.check_alarm(3)
175} 190}
191
192#[cfg(all(feature = "rt", feature = "rp235x"))]
193#[interrupt]
194fn TIMER0_IRQ_0() {
195 DRIVER.check_alarm(0)
196}
197
198#[cfg(all(feature = "rt", feature = "rp235x"))]
199#[interrupt]
200fn TIMER0_IRQ_1() {
201 DRIVER.check_alarm(1)
202}
203
204#[cfg(all(feature = "rt", feature = "rp235x"))]
205#[interrupt]
206fn TIMER0_IRQ_2() {
207 DRIVER.check_alarm(2)
208}
209
210#[cfg(all(feature = "rt", feature = "rp235x"))]
211#[interrupt]
212fn TIMER0_IRQ_3() {
213 DRIVER.check_alarm(3)
214}
diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs
index d50f5b4d5..058cfcbee 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,8 +835,16 @@ 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 {
@@ -841,8 +854,16 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
841 pin.pad_ctrl().write(|w| w.set_ie(true)); 854 pin.pad_ctrl().write(|w| w.set_ie(true));
842 } 855 }
843 if let Some(pin) = &rx { 856 if let Some(pin) = &rx {
857 let funcsel = {
858 let pin_number = ((pin.gpio().as_ptr() as u32) & 0x1FF) / 8;
859 if ((pin_number - 1) % 4) == 0 {
860 2
861 } else {
862 11
863 }
864 };
844 pin.gpio().ctrl().write(|w| { 865 pin.gpio().ctrl().write(|w| {
845 w.set_funcsel(2); 866 w.set_funcsel(funcsel);
846 w.set_inover(if config.invert_rx { 867 w.set_inover(if config.invert_rx {
847 Inover::INVERT 868 Inover::INVERT
848 } else { 869 } else {
@@ -904,7 +925,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
904 }); 925 });
905 } 926 }
906 927
907 fn lcr_modify<R>(f: impl FnOnce(&mut rp_pac::uart::regs::UartlcrH) -> R) -> R { 928 fn lcr_modify<R>(f: impl FnOnce(&mut crate::pac::uart::regs::UartlcrH) -> R) -> R {
908 let r = T::regs(); 929 let r = T::regs();
909 930
910 // Notes from PL011 reference manual: 931 // Notes from PL011 reference manual:
@@ -1332,3 +1353,92 @@ impl_pin!(PIN_26, UART1, CtsPin);
1332impl_pin!(PIN_27, UART1, RtsPin); 1353impl_pin!(PIN_27, UART1, RtsPin);
1333impl_pin!(PIN_28, UART0, TxPin); 1354impl_pin!(PIN_28, UART0, TxPin);
1334impl_pin!(PIN_29, UART0, RxPin); 1355impl_pin!(PIN_29, UART0, RxPin);
1356
1357// Additional functions added by all 2350s
1358#[cfg(feature = "rp235x")]
1359impl_pin!(PIN_2, UART0, TxPin);
1360#[cfg(feature = "rp235x")]
1361impl_pin!(PIN_3, UART0, RxPin);
1362#[cfg(feature = "rp235x")]
1363impl_pin!(PIN_6, UART1, TxPin);
1364#[cfg(feature = "rp235x")]
1365impl_pin!(PIN_7, UART1, RxPin);
1366#[cfg(feature = "rp235x")]
1367impl_pin!(PIN_10, UART1, TxPin);
1368#[cfg(feature = "rp235x")]
1369impl_pin!(PIN_11, UART1, RxPin);
1370#[cfg(feature = "rp235x")]
1371impl_pin!(PIN_14, UART0, TxPin);
1372#[cfg(feature = "rp235x")]
1373impl_pin!(PIN_15, UART0, RxPin);
1374#[cfg(feature = "rp235x")]
1375impl_pin!(PIN_18, UART0, TxPin);
1376#[cfg(feature = "rp235x")]
1377impl_pin!(PIN_19, UART0, RxPin);
1378#[cfg(feature = "rp235x")]
1379impl_pin!(PIN_22, UART1, TxPin);
1380#[cfg(feature = "rp235x")]
1381impl_pin!(PIN_23, UART1, RxPin);
1382#[cfg(feature = "rp235x")]
1383impl_pin!(PIN_26, UART1, TxPin);
1384#[cfg(feature = "rp235x")]
1385impl_pin!(PIN_27, UART1, RxPin);
1386
1387// Additional pins added by larger 2350 packages.
1388#[cfg(feature = "rp235xb")]
1389impl_pin!(PIN_30, UART0, CtsPin);
1390#[cfg(feature = "rp235xb")]
1391impl_pin!(PIN_31, UART0, RtsPin);
1392#[cfg(feature = "rp235xb")]
1393impl_pin!(PIN_32, UART0, TxPin);
1394#[cfg(feature = "rp235xb")]
1395impl_pin!(PIN_33, UART0, RxPin);
1396#[cfg(feature = "rp235xb")]
1397impl_pin!(PIN_34, UART0, CtsPin);
1398#[cfg(feature = "rp235xb")]
1399impl_pin!(PIN_35, UART0, RtsPin);
1400#[cfg(feature = "rp235xb")]
1401impl_pin!(PIN_36, UART1, TxPin);
1402#[cfg(feature = "rp235xb")]
1403impl_pin!(PIN_37, UART1, RxPin);
1404#[cfg(feature = "rp235xb")]
1405impl_pin!(PIN_38, UART1, CtsPin);
1406#[cfg(feature = "rp235xb")]
1407impl_pin!(PIN_39, UART1, RtsPin);
1408#[cfg(feature = "rp235xb")]
1409impl_pin!(PIN_40, UART1, TxPin);
1410#[cfg(feature = "rp235xb")]
1411impl_pin!(PIN_41, UART1, RxPin);
1412#[cfg(feature = "rp235xb")]
1413impl_pin!(PIN_42, UART1, CtsPin);
1414#[cfg(feature = "rp235xb")]
1415impl_pin!(PIN_43, UART1, RtsPin);
1416#[cfg(feature = "rp235xb")]
1417impl_pin!(PIN_44, UART0, TxPin);
1418#[cfg(feature = "rp235xb")]
1419impl_pin!(PIN_45, UART0, RxPin);
1420#[cfg(feature = "rp235xb")]
1421impl_pin!(PIN_46, UART0, CtsPin);
1422#[cfg(feature = "rp235xb")]
1423impl_pin!(PIN_47, UART0, RtsPin);
1424
1425#[cfg(feature = "rp235xb")]
1426impl_pin!(PIN_30, UART0, TxPin);
1427#[cfg(feature = "rp235xb")]
1428impl_pin!(PIN_31, UART0, RxPin);
1429#[cfg(feature = "rp235xb")]
1430impl_pin!(PIN_34, UART0, TxPin);
1431#[cfg(feature = "rp235xb")]
1432impl_pin!(PIN_35, UART0, RxPin);
1433#[cfg(feature = "rp235xb")]
1434impl_pin!(PIN_38, UART1, TxPin);
1435#[cfg(feature = "rp235xb")]
1436impl_pin!(PIN_39, UART1, RxPin);
1437#[cfg(feature = "rp235xb")]
1438impl_pin!(PIN_42, UART1, TxPin);
1439#[cfg(feature = "rp235xb")]
1440impl_pin!(PIN_43, UART1, RxPin);
1441#[cfg(feature = "rp235xb")]
1442impl_pin!(PIN_46, UART0, TxPin);
1443#[cfg(feature = "rp235xb")]
1444impl_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/rp/Cargo.toml b/examples/rp/Cargo.toml
index 2884ca85a..500425315 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -10,7 +10,7 @@ embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal",
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
13embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl"] } 13embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] }
14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } 15embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] }
16embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } 16embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
@@ -51,7 +51,7 @@ embedded-hal-async = "1.0"
51embedded-hal-bus = { version = "0.1", features = ["async"] } 51embedded-hal-bus = { version = "0.1", features = ["async"] }
52embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } 52embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
53embedded-storage = { version = "0.3" } 53embedded-storage = { version = "0.3" }
54static_cell = "2" 54static_cell = "2.1"
55portable-atomic = { version = "1.5", features = ["critical-section"] } 55portable-atomic = { version = "1.5", features = ["critical-section"] }
56log = "0.4" 56log = "0.4"
57pio-proc = "0.2" 57pio-proc = "0.2"
diff --git a/examples/rp23/.cargo/config.toml b/examples/rp23/.cargo/config.toml
new file mode 100644
index 000000000..f77e004dc
--- /dev/null
+++ b/examples/rp23/.cargo/config.toml
@@ -0,0 +1,9 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2#runner = "probe-rs run --chip RP2040"
3runner = "elf2uf2-rs -d"
4
5[build]
6target = "thumbv8m.main-none-eabihf"
7
8[env]
9DEFMT_LOG = "debug"
diff --git a/examples/rp23/Cargo.toml b/examples/rp23/Cargo.toml
new file mode 100644
index 000000000..89947ae46
--- /dev/null
+++ b/examples/rp23/Cargo.toml
@@ -0,0 +1,80 @@
1[package]
2edition = "2021"
3name = "embassy-rp2350-examples"
4version = "0.1.0"
5license = "MIT OR Apache-2.0"
6
7
8[dependencies]
9embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
13embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info", "rt-235x", "boot2-none"] }
14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] }
16embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
17embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
18embassy-usb-logger = { version = "0.2.0", path = "../../embassy-usb-logger" }
19cyw43 = { version = "0.2.0", path = "../../cyw43", features = ["defmt", "firmware-logs", "bluetooth"] }
20cyw43-pio = { version = "0.2.0", path = "../../cyw43-pio", features = ["defmt"] }
21
22defmt = "0.3"
23defmt-rtt = "0.4"
24fixed = "1.23.1"
25fixed-macro = "1.2"
26
27# for web request example
28reqwless = { version = "0.12.0", features = ["defmt",]}
29serde = { version = "1.0.203", default-features = false, features = ["derive"] }
30serde-json-core = "0.5.1"
31
32# for assign resources example
33assign-resources = { git = "https://github.com/adamgreig/assign-resources", rev = "94ad10e2729afdf0fd5a77cd12e68409a982f58a" }
34
35#cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
36cortex-m = { version = "0.7.6", features = ["inline-asm"] }
37cortex-m-rt = "0.7.0"
38critical-section = "1.1"
39panic-probe = { version = "0.3", features = ["print-defmt"] }
40display-interface-spi = "0.4.1"
41embedded-graphics = "0.7.1"
42st7789 = "0.6.1"
43display-interface = "0.4.1"
44byte-slice-cast = { version = "1.2.0", default-features = false }
45smart-leds = "0.3.0"
46heapless = "0.8"
47usbd-hid = "0.8.1"
48
49embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
50embedded-hal-async = "1.0"
51embedded-hal-bus = { version = "0.1", features = ["async"] }
52embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
53embedded-storage = { version = "0.3" }
54static_cell = "2.1"
55portable-atomic = { version = "1.5", features = ["critical-section"] }
56log = "0.4"
57pio-proc = "0.2"
58pio = "0.2.1"
59rand = { version = "0.8.5", default-features = false }
60embedded-sdmmc = "0.7.0"
61
62bt-hci = { version = "0.1.0", default-features = false, features = ["defmt"] }
63trouble-host = { version = "0.1.0", features = ["defmt", "gatt"] }
64
65[profile.release]
66debug = 2
67
68[profile.dev]
69lto = true
70opt-level = "z"
71
72[patch.crates-io]
73trouble-host = { git = "https://github.com/embassy-rs/trouble.git", rev = "4b8c0f499b34e46ca23a56e2d1640ede371722cf" }
74bt-hci = { git = "https://github.com/alexmoon/bt-hci.git", rev = "b9cd5954f6bd89b535cad9c418e9fdf12812d7c3" }
75embassy-executor = { path = "../../embassy-executor" }
76embassy-sync = { path = "../../embassy-sync" }
77embassy-futures = { path = "../../embassy-futures" }
78embassy-time = { path = "../../embassy-time" }
79embassy-time-driver = { path = "../../embassy-time-driver" }
80embassy-embedded-hal = { path = "../../embassy-embedded-hal" }
diff --git a/examples/rp23/assets/ferris.raw b/examples/rp23/assets/ferris.raw
new file mode 100644
index 000000000..9733889c5
--- /dev/null
+++ b/examples/rp23/assets/ferris.raw
Binary files differ
diff --git a/examples/rp23/build.rs b/examples/rp23/build.rs
new file mode 100644
index 000000000..30691aa97
--- /dev/null
+++ b/examples/rp23/build.rs
@@ -0,0 +1,35 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
35}
diff --git a/examples/rp23/memory.x b/examples/rp23/memory.x
new file mode 100644
index 000000000..777492062
--- /dev/null
+++ b/examples/rp23/memory.x
@@ -0,0 +1,74 @@
1MEMORY {
2 /*
3 * The RP2350 has either external or internal flash.
4 *
5 * 2 MiB is a safe default here, although a Pico 2 has 4 MiB.
6 */
7 FLASH : ORIGIN = 0x10000000, LENGTH = 2048K
8 /*
9 * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping.
10 * This is usually good for performance, as it distributes load on
11 * those banks evenly.
12 */
13 RAM : ORIGIN = 0x20000000, LENGTH = 512K
14 /*
15 * RAM banks 8 and 9 use a direct mapping. They can be used to have
16 * memory areas dedicated for some specific job, improving predictability
17 * of access times.
18 * Example: Separate stacks for core0 and core1.
19 */
20 SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K
21 SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K
22}
23
24SECTIONS {
25 /* ### Boot ROM info
26 *
27 * Goes after .vector_table, to keep it in the first 4K of flash
28 * where the Boot ROM (and picotool) can find it
29 */
30 .start_block : ALIGN(4)
31 {
32 __start_block_addr = .;
33 KEEP(*(.start_block));
34 } > FLASH
35
36} INSERT AFTER .vector_table;
37
38/* move .text to start /after/ the boot info */
39_stext = ADDR(.start_block) + SIZEOF(.start_block);
40
41SECTIONS {
42 /* ### Picotool 'Binary Info' Entries
43 *
44 * Picotool looks through this block (as we have pointers to it in our
45 * header) to find interesting information.
46 */
47 .bi_entries : ALIGN(4)
48 {
49 /* We put this in the header */
50 __bi_entries_start = .;
51 /* Here are the entries */
52 KEEP(*(.bi_entries));
53 /* Keep this block a nice round size */
54 . = ALIGN(4);
55 /* We put this in the header */
56 __bi_entries_end = .;
57 } > FLASH
58} INSERT AFTER .text;
59
60SECTIONS {
61 /* ### Boot ROM extra info
62 *
63 * Goes after everything in our program, so it can contain a signature.
64 */
65 .end_block : ALIGN(4)
66 {
67 __end_block_addr = .;
68 KEEP(*(.end_block));
69 } > FLASH
70
71} INSERT AFTER .uninit;
72
73PROVIDE(start_to_end = __end_block_addr - __start_block_addr);
74PROVIDE(end_to_start = __start_block_addr - __end_block_addr);
diff --git a/examples/rp23/src/bin/adc.rs b/examples/rp23/src/bin/adc.rs
new file mode 100644
index 000000000..a1a94ca47
--- /dev/null
+++ b/examples/rp23/src/bin/adc.rs
@@ -0,0 +1,64 @@
1//! This example test the ADC (Analog to Digital Conversion) of the RS2040 pin 26, 27 and 28.
2//! It also reads the temperature sensor in the chip.
3
4#![no_std]
5#![no_main]
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler};
10use embassy_rp::bind_interrupts;
11use embassy_rp::gpio::Pull;
12use embassy_time::Timer;
13use {defmt_rtt as _, panic_probe as _};
14use embassy_rp::block::ImageDef;
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30
31bind_interrupts!(struct Irqs {
32 ADC_IRQ_FIFO => InterruptHandler;
33});
34
35#[embassy_executor::main]
36async fn main(_spawner: Spawner) {
37 let p = embassy_rp::init(Default::default());
38 let mut adc = Adc::new(p.ADC, Irqs, Config::default());
39
40 let mut p26 = Channel::new_pin(p.PIN_26, Pull::None);
41 let mut p27 = Channel::new_pin(p.PIN_27, Pull::None);
42 let mut p28 = Channel::new_pin(p.PIN_28, Pull::None);
43 let mut ts = Channel::new_temp_sensor(p.ADC_TEMP_SENSOR);
44
45 loop {
46 let level = adc.read(&mut p26).await.unwrap();
47 info!("Pin 26 ADC: {}", level);
48 let level = adc.read(&mut p27).await.unwrap();
49 info!("Pin 27 ADC: {}", level);
50 let level = adc.read(&mut p28).await.unwrap();
51 info!("Pin 28 ADC: {}", level);
52 let temp = adc.read(&mut ts).await.unwrap();
53 info!("Temp: {} degrees", convert_to_celsius(temp));
54 Timer::after_secs(1).await;
55 }
56}
57
58fn convert_to_celsius(raw_temp: u16) -> f32 {
59 // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet
60 let temp = 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721;
61 let sign = if temp < 0.0 { -1.0 } else { 1.0 };
62 let rounded_temp_x10: i16 = ((temp * 10.0) + 0.5 * sign) as i16;
63 (rounded_temp_x10 as f32) / 10.0
64}
diff --git a/examples/rp23/src/bin/adc_dma.rs b/examples/rp23/src/bin/adc_dma.rs
new file mode 100644
index 000000000..887c8500a
--- /dev/null
+++ b/examples/rp23/src/bin/adc_dma.rs
@@ -0,0 +1,70 @@
1//! This example shows how to use the RP2040 ADC with DMA, both single- and multichannel reads.
2//! For multichannel, the samples are interleaved in the buffer:
3//! `[ch1, ch2, ch3, ch4, ch1, ch2, ch3, ch4, ...]`
4#![no_std]
5#![no_main]
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler};
10use embassy_rp::bind_interrupts;
11use embassy_rp::gpio::Pull;
12use embassy_time::{Duration, Ticker};
13use {defmt_rtt as _, panic_probe as _};
14use embassy_rp::block::ImageDef;
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30
31bind_interrupts!(struct Irqs {
32 ADC_IRQ_FIFO => InterruptHandler;
33});
34
35#[embassy_executor::main]
36async fn main(_spawner: Spawner) {
37 let p = embassy_rp::init(Default::default());
38 info!("Here we go!");
39
40 let mut adc = Adc::new(p.ADC, Irqs, Config::default());
41 let mut dma = p.DMA_CH0;
42 let mut pin = Channel::new_pin(p.PIN_26, Pull::Up);
43 let mut pins = [
44 Channel::new_pin(p.PIN_27, Pull::Down),
45 Channel::new_pin(p.PIN_28, Pull::None),
46 Channel::new_pin(p.PIN_29, Pull::Up),
47 Channel::new_temp_sensor(p.ADC_TEMP_SENSOR),
48 ];
49
50 const BLOCK_SIZE: usize = 100;
51 const NUM_CHANNELS: usize = 4;
52 let mut ticker = Ticker::every(Duration::from_secs(1));
53 loop {
54 // Read 100 samples from a single channel
55 let mut buf = [0_u16; BLOCK_SIZE];
56 let div = 479; // 100kHz sample rate (48Mhz / 100kHz - 1)
57 adc.read_many(&mut pin, &mut buf, div, &mut dma).await.unwrap();
58 info!("single: {:?} ...etc", buf[..8]);
59
60 // Read 100 samples from 4 channels interleaved
61 let mut buf = [0_u16; { BLOCK_SIZE * NUM_CHANNELS }];
62 let div = 119; // 100kHz sample rate (48Mhz / 100kHz * 4ch - 1)
63 adc.read_many_multichannel(&mut pins, &mut buf, div, &mut dma)
64 .await
65 .unwrap();
66 info!("multi: {:?} ...etc", buf[..NUM_CHANNELS * 2]);
67
68 ticker.next().await;
69 }
70}
diff --git a/examples/rp23/src/bin/assign_resources.rs b/examples/rp23/src/bin/assign_resources.rs
new file mode 100644
index 000000000..8aea2f35d
--- /dev/null
+++ b/examples/rp23/src/bin/assign_resources.rs
@@ -0,0 +1,95 @@
1//! This example demonstrates how to assign resources to multiple tasks by splitting up the peripherals.
2//! It is not about sharing the same resources between tasks, see sharing.rs for that or head to https://embassy.dev/book/#_sharing_peripherals_between_tasks)
3//! Of course splitting up resources and sharing resources can be combined, yet this example is only about splitting up resources.
4//!
5//! There are basically two ways we demonstrate here:
6//! 1) Assigning resources to a task by passing parts of the peripherals
7//! 2) Assigning resources to a task by passing a struct with the split up peripherals, using the assign-resources macro
8//!
9//! using four LEDs on Pins 10, 11, 20 and 21
10
11#![no_std]
12#![no_main]
13
14use assign_resources::assign_resources;
15use defmt::*;
16use embassy_executor::Spawner;
17use embassy_rp::gpio::{Level, Output};
18use embassy_rp::peripherals::{self, PIN_20, PIN_21};
19use embassy_time::Timer;
20use {defmt_rtt as _, panic_probe as _};
21use embassy_rp::block::ImageDef;
22
23#[link_section = ".start_block"]
24#[used]
25pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
26
27// Program metadata for `picotool info`
28#[link_section = ".bi_entries"]
29#[used]
30pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
31 embassy_rp::binary_info_rp_cargo_bin_name!(),
32 embassy_rp::binary_info_rp_cargo_version!(),
33 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
34 embassy_rp::binary_info_rp_program_build_attribute!(),
35];
36
37
38#[embassy_executor::main]
39async fn main(spawner: Spawner) {
40 // initialize the peripherals
41 let p = embassy_rp::init(Default::default());
42
43 // 1) Assigning a resource to a task by passing parts of the peripherals.
44 spawner
45 .spawn(double_blinky_manually_assigned(spawner, p.PIN_20, p.PIN_21))
46 .unwrap();
47
48 // 2) Using the assign-resources macro to assign resources to a task.
49 // we perform the split, see further below for the definition of the resources struct
50 let r = split_resources!(p);
51 // and then we can use them
52 spawner.spawn(double_blinky_macro_assigned(spawner, r.leds)).unwrap();
53}
54
55// 1) Assigning a resource to a task by passing parts of the peripherals.
56#[embassy_executor::task]
57async fn double_blinky_manually_assigned(_spawner: Spawner, pin_20: PIN_20, pin_21: PIN_21) {
58 let mut led_20 = Output::new(pin_20, Level::Low);
59 let mut led_21 = Output::new(pin_21, Level::High);
60
61 loop {
62 info!("toggling leds");
63 led_20.toggle();
64 led_21.toggle();
65 Timer::after_secs(1).await;
66 }
67}
68
69// 2) Using the assign-resources macro to assign resources to a task.
70// first we define the resources we want to assign to the task using the assign_resources! macro
71// basically this will split up the peripherals struct into smaller structs, that we define here
72// naming is up to you, make sure your future self understands what you did here
73assign_resources! {
74 leds: Leds{
75 led_10: PIN_10,
76 led_11: PIN_11,
77 }
78 // add more resources to more structs if needed, for example defining one struct for each task
79}
80// this could be done in another file and imported here, but for the sake of simplicity we do it here
81// see https://github.com/adamgreig/assign-resources for more information
82
83// 2) Using the split resources in a task
84#[embassy_executor::task]
85async fn double_blinky_macro_assigned(_spawner: Spawner, r: Leds) {
86 let mut led_10 = Output::new(r.led_10, Level::Low);
87 let mut led_11 = Output::new(r.led_11, Level::High);
88
89 loop {
90 info!("toggling leds");
91 led_10.toggle();
92 led_11.toggle();
93 Timer::after_secs(1).await;
94 }
95}
diff --git a/examples/rp23/src/bin/blinky.rs b/examples/rp23/src/bin/blinky.rs
new file mode 100644
index 000000000..1e3a52085
--- /dev/null
+++ b/examples/rp23/src/bin/blinky.rs
@@ -0,0 +1,44 @@
1//! This example test the RP Pico on board LED.
2//!
3//! It does not work with the RP Pico W board. See wifi_blinky.rs.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::gpio;
11use embassy_time::Timer;
12use gpio::{Level, Output};
13use {defmt_rtt as _, panic_probe as _};
14use embassy_rp::block::ImageDef;
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30#[embassy_executor::main]
31async fn main(_spawner: Spawner) {
32 let p = embassy_rp::init(Default::default());
33 let mut led = Output::new(p.PIN_2, Level::Low);
34
35 loop {
36 info!("led on!");
37 led.set_high();
38 Timer::after_millis(250).await;
39
40 info!("led off!");
41 led.set_low();
42 Timer::after_millis(250).await;
43 }
44}
diff --git a/examples/rp23/src/bin/blinky_two_channels.rs b/examples/rp23/src/bin/blinky_two_channels.rs
new file mode 100644
index 000000000..aae0283cf
--- /dev/null
+++ b/examples/rp23/src/bin/blinky_two_channels.rs
@@ -0,0 +1,66 @@
1#![no_std]
2#![no_main]
3/// This example demonstrates how to access a given pin from more than one embassy task
4/// The on-board LED is toggled by two tasks with slightly different periods, leading to the
5/// apparent duty cycle of the LED increasing, then decreasing, linearly. The phenomenon is similar
6/// to interference and the 'beats' you can hear if you play two frequencies close to one another
7/// [Link explaining it](https://www.physicsclassroom.com/class/sound/Lesson-3/Interference-and-Beats)
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::gpio;
11use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
12use embassy_sync::channel::{Channel, Sender};
13use embassy_time::{Duration, Ticker};
14use gpio::{AnyPin, Level, Output};
15use {defmt_rtt as _, panic_probe as _};
16use embassy_rp::block::ImageDef;
17
18#[link_section = ".start_block"]
19#[used]
20pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
21
22// Program metadata for `picotool info`
23#[link_section = ".bi_entries"]
24#[used]
25pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
26 embassy_rp::binary_info_rp_cargo_bin_name!(),
27 embassy_rp::binary_info_rp_cargo_version!(),
28 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
29 embassy_rp::binary_info_rp_program_build_attribute!(),
30];
31
32
33enum LedState {
34 Toggle,
35}
36static CHANNEL: Channel<ThreadModeRawMutex, LedState, 64> = Channel::new();
37
38#[embassy_executor::main]
39async fn main(spawner: Spawner) {
40 let p = embassy_rp::init(Default::default());
41 let mut led = Output::new(AnyPin::from(p.PIN_25), Level::High);
42
43 let dt = 100 * 1_000_000;
44 let k = 1.003;
45
46 unwrap!(spawner.spawn(toggle_led(CHANNEL.sender(), Duration::from_nanos(dt))));
47 unwrap!(spawner.spawn(toggle_led(
48 CHANNEL.sender(),
49 Duration::from_nanos((dt as f64 * k) as u64)
50 )));
51
52 loop {
53 match CHANNEL.receive().await {
54 LedState::Toggle => led.toggle(),
55 }
56 }
57}
58
59#[embassy_executor::task(pool_size = 2)]
60async fn toggle_led(control: Sender<'static, ThreadModeRawMutex, LedState, 64>, delay: Duration) {
61 let mut ticker = Ticker::every(delay);
62 loop {
63 control.send(LedState::Toggle).await;
64 ticker.next().await;
65 }
66}
diff --git a/examples/rp23/src/bin/blinky_two_tasks.rs b/examples/rp23/src/bin/blinky_two_tasks.rs
new file mode 100644
index 000000000..aeabb238f
--- /dev/null
+++ b/examples/rp23/src/bin/blinky_two_tasks.rs
@@ -0,0 +1,65 @@
1#![no_std]
2#![no_main]
3/// This example demonstrates how to access a given pin from more than one embassy task
4/// The on-board LED is toggled by two tasks with slightly different periods, leading to the
5/// apparent duty cycle of the LED increasing, then decreasing, linearly. The phenomenon is similar
6/// to interference and the 'beats' you can hear if you play two frequencies close to one another
7/// [Link explaining it](https://www.physicsclassroom.com/class/sound/Lesson-3/Interference-and-Beats)
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::gpio;
11use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
12use embassy_sync::mutex::Mutex;
13use embassy_time::{Duration, Ticker};
14use gpio::{AnyPin, Level, Output};
15use {defmt_rtt as _, panic_probe as _};
16use embassy_rp::block::ImageDef;
17
18#[link_section = ".start_block"]
19#[used]
20pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
21
22// Program metadata for `picotool info`
23#[link_section = ".bi_entries"]
24#[used]
25pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
26 embassy_rp::binary_info_rp_cargo_bin_name!(),
27 embassy_rp::binary_info_rp_cargo_version!(),
28 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
29 embassy_rp::binary_info_rp_program_build_attribute!(),
30];
31
32
33type LedType = Mutex<ThreadModeRawMutex, Option<Output<'static>>>;
34static LED: LedType = Mutex::new(None);
35
36#[embassy_executor::main]
37async fn main(spawner: Spawner) {
38 let p = embassy_rp::init(Default::default());
39 // set the content of the global LED reference to the real LED pin
40 let led = Output::new(AnyPin::from(p.PIN_25), Level::High);
41 // inner scope is so that once the mutex is written to, the MutexGuard is dropped, thus the
42 // Mutex is released
43 {
44 *(LED.lock().await) = Some(led);
45 }
46 let dt = 100 * 1_000_000;
47 let k = 1.003;
48
49 unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos(dt))));
50 unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos((dt as f64 * k) as u64))));
51}
52
53#[embassy_executor::task(pool_size = 2)]
54async fn toggle_led(led: &'static LedType, delay: Duration) {
55 let mut ticker = Ticker::every(delay);
56 loop {
57 {
58 let mut led_unlocked = led.lock().await;
59 if let Some(pin_ref) = led_unlocked.as_mut() {
60 pin_ref.toggle();
61 }
62 }
63 ticker.next().await;
64 }
65}
diff --git a/examples/rp23/src/bin/button.rs b/examples/rp23/src/bin/button.rs
new file mode 100644
index 000000000..2a78a19db
--- /dev/null
+++ b/examples/rp23/src/bin/button.rs
@@ -0,0 +1,44 @@
1//! This example uses the RP Pico on board LED to test input pin 28. This is not the button on the board.
2//!
3//! It does not work with the RP Pico W board. Use wifi_blinky.rs and add input pin.
4
5#![no_std]
6#![no_main]
7
8use embassy_executor::Spawner;
9use embassy_rp::gpio::{Input, Level, Output, Pull};
10use {defmt_rtt as _, panic_probe as _};
11use embassy_rp::block::ImageDef;
12
13#[link_section = ".start_block"]
14#[used]
15pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
16
17// Program metadata for `picotool info`
18#[link_section = ".bi_entries"]
19#[used]
20pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
21 embassy_rp::binary_info_rp_cargo_bin_name!(),
22 embassy_rp::binary_info_rp_cargo_version!(),
23 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
24 embassy_rp::binary_info_rp_program_build_attribute!(),
25];
26
27
28#[embassy_executor::main]
29async fn main(_spawner: Spawner) {
30 let p = embassy_rp::init(Default::default());
31 let mut led = Output::new(p.PIN_25, Level::Low);
32
33 // Use PIN_28, Pin34 on J0 for RP Pico, as a input.
34 // You need to add your own button.
35 let button = Input::new(p.PIN_28, Pull::Up);
36
37 loop {
38 if button.is_high() {
39 led.set_high();
40 } else {
41 led.set_low();
42 }
43 }
44}
diff --git a/examples/rp23/src/bin/debounce.rs b/examples/rp23/src/bin/debounce.rs
new file mode 100644
index 000000000..0e3b5cb54
--- /dev/null
+++ b/examples/rp23/src/bin/debounce.rs
@@ -0,0 +1,96 @@
1//! This example shows the ease of debouncing a button with async rust.
2//! Hook up a button or switch between pin 9 and ground.
3
4#![no_std]
5#![no_main]
6
7use defmt::info;
8use embassy_executor::Spawner;
9use embassy_rp::gpio::{Input, Level, Pull};
10use embassy_time::{with_deadline, Duration, Instant, Timer};
11use {defmt_rtt as _, panic_probe as _};
12use embassy_rp::block::ImageDef;
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28
29pub struct Debouncer<'a> {
30 input: Input<'a>,
31 debounce: Duration,
32}
33
34impl<'a> Debouncer<'a> {
35 pub fn new(input: Input<'a>, debounce: Duration) -> Self {
36 Self { input, debounce }
37 }
38
39 pub async fn debounce(&mut self) -> Level {
40 loop {
41 let l1 = self.input.get_level();
42
43 self.input.wait_for_any_edge().await;
44
45 Timer::after(self.debounce).await;
46
47 let l2 = self.input.get_level();
48 if l1 != l2 {
49 break l2;
50 }
51 }
52 }
53}
54
55#[embassy_executor::main]
56async fn main(_spawner: Spawner) {
57 let p = embassy_rp::init(Default::default());
58 let mut btn = Debouncer::new(Input::new(p.PIN_9, Pull::Up), Duration::from_millis(20));
59
60 info!("Debounce Demo");
61
62 loop {
63 // button pressed
64 btn.debounce().await;
65 let start = Instant::now();
66 info!("Button Press");
67
68 match with_deadline(start + Duration::from_secs(1), btn.debounce()).await {
69 // Button Released < 1s
70 Ok(_) => {
71 info!("Button pressed for: {}ms", start.elapsed().as_millis());
72 continue;
73 }
74 // button held for > 1s
75 Err(_) => {
76 info!("Button Held");
77 }
78 }
79
80 match with_deadline(start + Duration::from_secs(5), btn.debounce()).await {
81 // Button released <5s
82 Ok(_) => {
83 info!("Button pressed for: {}ms", start.elapsed().as_millis());
84 continue;
85 }
86 // button held for > >5s
87 Err(_) => {
88 info!("Button Long Held");
89 }
90 }
91
92 // wait for button release before handling another press
93 btn.debounce().await;
94 info!("Button pressed for: {}ms", start.elapsed().as_millis());
95 }
96}
diff --git a/examples/rp23/src/bin/flash.rs b/examples/rp23/src/bin/flash.rs
new file mode 100644
index 000000000..42620ca93
--- /dev/null
+++ b/examples/rp23/src/bin/flash.rs
@@ -0,0 +1,150 @@
1//! This example test the flash connected to the RP2040 chip.
2
3#![no_std]
4#![no_main]
5
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_rp::flash::{Async, ERASE_SIZE, FLASH_BASE};
9use embassy_rp::peripherals::FLASH;
10use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _};
12use embassy_rp::block::ImageDef;
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28
29const ADDR_OFFSET: u32 = 0x100000;
30const FLASH_SIZE: usize = 2 * 1024 * 1024;
31
32#[embassy_executor::main]
33async fn main(_spawner: Spawner) {
34 let p = embassy_rp::init(Default::default());
35 info!("Hello World!");
36
37 // add some delay to give an attached debug probe time to parse the
38 // defmt RTT header. Reading that header might touch flash memory, which
39 // interferes with flash write operations.
40 // https://github.com/knurling-rs/defmt/pull/683
41 Timer::after_millis(10).await;
42
43 let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0);
44
45 // Get JEDEC id
46 let jedec = flash.blocking_jedec_id().unwrap();
47 info!("jedec id: 0x{:x}", jedec);
48
49 // Get unique id
50 let mut uid = [0; 8];
51 flash.blocking_unique_id(&mut uid).unwrap();
52 info!("unique id: {:?}", uid);
53
54 erase_write_sector(&mut flash, 0x00);
55
56 multiwrite_bytes(&mut flash, ERASE_SIZE as u32);
57
58 background_read(&mut flash, (ERASE_SIZE * 2) as u32).await;
59
60 loop {}
61}
62
63fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
64 info!(">>>> [multiwrite_bytes]");
65 let mut read_buf = [0u8; ERASE_SIZE];
66 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
67
68 info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
69 info!("Contents start with {=[u8]}", read_buf[0..4]);
70
71 defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
72
73 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
74 info!("Contents after erase starts with {=[u8]}", read_buf[0..4]);
75 if read_buf.iter().any(|x| *x != 0xFF) {
76 defmt::panic!("unexpected");
77 }
78
79 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &[0x01]));
80 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 1, &[0x02]));
81 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 2, &[0x03]));
82 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 3, &[0x04]));
83
84 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
85 info!("Contents after write starts with {=[u8]}", read_buf[0..4]);
86 if &read_buf[0..4] != &[0x01, 0x02, 0x03, 0x04] {
87 defmt::panic!("unexpected");
88 }
89}
90
91fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
92 info!(">>>> [erase_write_sector]");
93 let mut buf = [0u8; ERASE_SIZE];
94 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));
95
96 info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
97 info!("Contents start with {=[u8]}", buf[0..4]);
98
99 defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
100
101 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));
102 info!("Contents after erase starts with {=[u8]}", buf[0..4]);
103 if buf.iter().any(|x| *x != 0xFF) {
104 defmt::panic!("unexpected");
105 }
106
107 for b in buf.iter_mut() {
108 *b = 0xDA;
109 }
110
111 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &buf));
112
113 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));
114 info!("Contents after write starts with {=[u8]}", buf[0..4]);
115 if buf.iter().any(|x| *x != 0xDA) {
116 defmt::panic!("unexpected");
117 }
118}
119
120async fn background_read(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
121 info!(">>>> [background_read]");
122
123 let mut buf = [0u32; 8];
124 defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
125
126 info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
127 info!("Contents start with {=u32:x}", buf[0]);
128
129 defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
130
131 defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
132 info!("Contents after erase starts with {=u32:x}", buf[0]);
133 if buf.iter().any(|x| *x != 0xFFFFFFFF) {
134 defmt::panic!("unexpected");
135 }
136
137 for b in buf.iter_mut() {
138 *b = 0xDABA1234;
139 }
140
141 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, unsafe {
142 core::slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len() * 4)
143 }));
144
145 defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
146 info!("Contents after write starts with {=u32:x}", buf[0]);
147 if buf.iter().any(|x| *x != 0xDABA1234) {
148 defmt::panic!("unexpected");
149 }
150}
diff --git a/examples/rp23/src/bin/gpio_async.rs b/examples/rp23/src/bin/gpio_async.rs
new file mode 100644
index 000000000..360932d62
--- /dev/null
+++ b/examples/rp23/src/bin/gpio_async.rs
@@ -0,0 +1,56 @@
1//! This example shows how async gpio can be used with a RP2040.
2//!
3//! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::gpio;
11use embassy_time::Timer;
12use gpio::{Input, Level, Output, Pull};
13use {defmt_rtt as _, panic_probe as _};
14use embassy_rp::block::ImageDef;
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30
31/// It requires an external signal to be manually triggered on PIN 16. For
32/// example, this could be accomplished using an external power source with a
33/// button so that it is possible to toggle the signal from low to high.
34///
35/// This example will begin with turning on the LED on the board and wait for a
36/// high signal on PIN 16. Once the high event/signal occurs the program will
37/// continue and turn off the LED, and then wait for 2 seconds before completing
38/// the loop and starting over again.
39#[embassy_executor::main]
40async fn main(_spawner: Spawner) {
41 let p = embassy_rp::init(Default::default());
42 let mut led = Output::new(p.PIN_25, Level::Low);
43 let mut async_input = Input::new(p.PIN_16, Pull::None);
44
45 loop {
46 info!("wait_for_high. Turn on LED");
47 led.set_high();
48
49 async_input.wait_for_high().await;
50
51 info!("done wait_for_high. Turn off LED");
52 led.set_low();
53
54 Timer::after_secs(2).await;
55 }
56}
diff --git a/examples/rp23/src/bin/gpout.rs b/examples/rp23/src/bin/gpout.rs
new file mode 100644
index 000000000..8d1e4d05f
--- /dev/null
+++ b/examples/rp23/src/bin/gpout.rs
@@ -0,0 +1,53 @@
1//! This example shows how GPOUT (General purpose clock outputs) can toggle a output pin.
2//!
3//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::clocks;
11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _};
13use embassy_rp::block::ImageDef;
14
15#[link_section = ".start_block"]
16#[used]
17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
18
19// Program metadata for `picotool info`
20#[link_section = ".bi_entries"]
21#[used]
22pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
23 embassy_rp::binary_info_rp_cargo_bin_name!(),
24 embassy_rp::binary_info_rp_cargo_version!(),
25 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
26 embassy_rp::binary_info_rp_program_build_attribute!(),
27];
28
29
30#[embassy_executor::main]
31async fn main(_spawner: Spawner) {
32 let p = embassy_rp::init(Default::default());
33
34 let gpout3 = clocks::Gpout::new(p.PIN_25);
35 gpout3.set_div(1000, 0);
36 gpout3.enable();
37
38 loop {
39 gpout3.set_src(clocks::GpoutSrc::Sys);
40 info!(
41 "Pin 25 is now outputing CLK_SYS/1000, should be toggling at {}",
42 gpout3.get_freq()
43 );
44 Timer::after_secs(2).await;
45
46 gpout3.set_src(clocks::GpoutSrc::Ref);
47 info!(
48 "Pin 25 is now outputing CLK_REF/1000, should be toggling at {}",
49 gpout3.get_freq()
50 );
51 Timer::after_secs(2).await;
52 }
53}
diff --git a/examples/rp23/src/bin/i2c_async.rs b/examples/rp23/src/bin/i2c_async.rs
new file mode 100644
index 000000000..64f103849
--- /dev/null
+++ b/examples/rp23/src/bin/i2c_async.rs
@@ -0,0 +1,126 @@
1//! This example shows how to communicate asynchronous using i2c with external chips.
2//!
3//! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip.
4//! (https://www.microchip.com/en-us/product/mcp23017)
5
6#![no_std]
7#![no_main]
8
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_rp::bind_interrupts;
12use embassy_rp::i2c::{self, Config, InterruptHandler};
13use embassy_rp::peripherals::I2C1;
14use embassy_time::Timer;
15use embedded_hal_async::i2c::I2c;
16use {defmt_rtt as _, panic_probe as _};
17use embassy_rp::block::ImageDef;
18
19#[link_section = ".start_block"]
20#[used]
21pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
22
23// Program metadata for `picotool info`
24#[link_section = ".bi_entries"]
25#[used]
26pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
27 embassy_rp::binary_info_rp_cargo_bin_name!(),
28 embassy_rp::binary_info_rp_cargo_version!(),
29 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
30 embassy_rp::binary_info_rp_program_build_attribute!(),
31];
32
33
34bind_interrupts!(struct Irqs {
35 I2C1_IRQ => InterruptHandler<I2C1>;
36});
37
38#[allow(dead_code)]
39mod mcp23017 {
40 pub const ADDR: u8 = 0x20; // default addr
41
42 macro_rules! mcpregs {
43 ($($name:ident : $val:expr),* $(,)?) => {
44 $(
45 pub const $name: u8 = $val;
46 )*
47
48 pub fn regname(reg: u8) -> &'static str {
49 match reg {
50 $(
51 $val => stringify!($name),
52 )*
53 _ => panic!("bad reg"),
54 }
55 }
56 }
57 }
58
59 // These are correct for IOCON.BANK=0
60 mcpregs! {
61 IODIRA: 0x00,
62 IPOLA: 0x02,
63 GPINTENA: 0x04,
64 DEFVALA: 0x06,
65 INTCONA: 0x08,
66 IOCONA: 0x0A,
67 GPPUA: 0x0C,
68 INTFA: 0x0E,
69 INTCAPA: 0x10,
70 GPIOA: 0x12,
71 OLATA: 0x14,
72 IODIRB: 0x01,
73 IPOLB: 0x03,
74 GPINTENB: 0x05,
75 DEFVALB: 0x07,
76 INTCONB: 0x09,
77 IOCONB: 0x0B,
78 GPPUB: 0x0D,
79 INTFB: 0x0F,
80 INTCAPB: 0x11,
81 GPIOB: 0x13,
82 OLATB: 0x15,
83 }
84}
85
86#[embassy_executor::main]
87async fn main(_spawner: Spawner) {
88 let p = embassy_rp::init(Default::default());
89
90 let sda = p.PIN_14;
91 let scl = p.PIN_15;
92
93 info!("set up i2c ");
94 let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, Config::default());
95
96 use mcp23017::*;
97
98 info!("init mcp23017 config for IxpandO");
99 // init - a outputs, b inputs
100 i2c.write(ADDR, &[IODIRA, 0x00]).await.unwrap();
101 i2c.write(ADDR, &[IODIRB, 0xff]).await.unwrap();
102 i2c.write(ADDR, &[GPPUB, 0xff]).await.unwrap(); // pullups
103
104 let mut val = 1;
105 loop {
106 let mut portb = [0];
107
108 i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).await.unwrap();
109 info!("portb = {:02x}", portb[0]);
110 i2c.write(mcp23017::ADDR, &[GPIOA, val | portb[0]]).await.unwrap();
111 val = val.rotate_left(1);
112
113 // get a register dump
114 info!("getting register dump");
115 let mut regs = [0; 22];
116 i2c.write_read(ADDR, &[0], &mut regs).await.unwrap();
117 // always get the regdump but only display it if portb'0 is set
118 if portb[0] & 1 != 0 {
119 for (idx, reg) in regs.into_iter().enumerate() {
120 info!("{} => {:02x}", regname(idx as u8), reg);
121 }
122 }
123
124 Timer::after_millis(100).await;
125 }
126}
diff --git a/examples/rp23/src/bin/i2c_async_embassy.rs b/examples/rp23/src/bin/i2c_async_embassy.rs
new file mode 100644
index 000000000..4b14ec6f6
--- /dev/null
+++ b/examples/rp23/src/bin/i2c_async_embassy.rs
@@ -0,0 +1,101 @@
1//! This example shows how to communicate asynchronous using i2c with external chip.
2//!
3//! It's using embassy's functions directly instead of traits from embedded_hal_async::i2c::I2c.
4//! While most of i2c devices are addressed using 7 bits, an extension allows 10 bits too.
5
6#![no_std]
7#![no_main]
8
9use defmt::*;
10use embassy_rp::i2c::InterruptHandler;
11use {defmt_rtt as _, panic_probe as _};
12use embassy_rp::block::ImageDef;
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28
29// Our anonymous hypotetical temperature sensor could be:
30// a 12-bit sensor, with 100ms startup time, range of -40*C - 125*C, and precision 0.25*C
31// It requires no configuration or calibration, works with all i2c bus speeds,
32// never stretches clock or does anything complicated. Replies with one u16.
33// It requires only one write to take it out of suspend mode, and stays on.
34// Often result would be just on 12 bits, but here we'll simplify it to 16.
35
36enum UncomplicatedSensorId {
37 A(UncomplicatedSensorU8),
38 B(UncomplicatedSensorU16),
39}
40enum UncomplicatedSensorU8 {
41 First = 0x48,
42}
43enum UncomplicatedSensorU16 {
44 Other = 0x0049,
45}
46
47impl Into<u16> for UncomplicatedSensorU16 {
48 fn into(self) -> u16 {
49 self as u16
50 }
51}
52impl Into<u16> for UncomplicatedSensorU8 {
53 fn into(self) -> u16 {
54 0x48
55 }
56}
57impl From<UncomplicatedSensorId> for u16 {
58 fn from(t: UncomplicatedSensorId) -> Self {
59 match t {
60 UncomplicatedSensorId::A(x) => x.into(),
61 UncomplicatedSensorId::B(x) => x.into(),
62 }
63 }
64}
65
66embassy_rp::bind_interrupts!(struct Irqs {
67 I2C1_IRQ => InterruptHandler<embassy_rp::peripherals::I2C1>;
68});
69
70#[embassy_executor::main]
71async fn main(_task_spawner: embassy_executor::Spawner) {
72 let p = embassy_rp::init(Default::default());
73 let sda = p.PIN_14;
74 let scl = p.PIN_15;
75 let config = embassy_rp::i2c::Config::default();
76 let mut bus = embassy_rp::i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, config);
77
78 const WAKEYWAKEY: u16 = 0xBABE;
79 let mut result: [u8; 2] = [0, 0];
80 // wait for sensors to initialize
81 embassy_time::Timer::after(embassy_time::Duration::from_millis(100)).await;
82
83 let _res_1 = bus
84 .write_async(UncomplicatedSensorU8::First, WAKEYWAKEY.to_be_bytes())
85 .await;
86 let _res_2 = bus
87 .write_async(UncomplicatedSensorU16::Other, WAKEYWAKEY.to_be_bytes())
88 .await;
89
90 loop {
91 let s1 = UncomplicatedSensorId::A(UncomplicatedSensorU8::First);
92 let s2 = UncomplicatedSensorId::B(UncomplicatedSensorU16::Other);
93 let sensors = [s1, s2];
94 for sensor in sensors {
95 if bus.read_async(sensor, &mut result).await.is_ok() {
96 info!("Result {}", u16::from_be_bytes(result.into()));
97 }
98 }
99 embassy_time::Timer::after(embassy_time::Duration::from_millis(200)).await;
100 }
101}
diff --git a/examples/rp23/src/bin/i2c_blocking.rs b/examples/rp23/src/bin/i2c_blocking.rs
new file mode 100644
index 000000000..d2cccf09b
--- /dev/null
+++ b/examples/rp23/src/bin/i2c_blocking.rs
@@ -0,0 +1,90 @@
1//! This example shows how to communicate using i2c with external chips.
2//!
3//! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip.
4//! (https://www.microchip.com/en-us/product/mcp23017)
5
6#![no_std]
7#![no_main]
8
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_rp::i2c::{self, Config};
12use embassy_time::Timer;
13use embedded_hal_1::i2c::I2c;
14use {defmt_rtt as _, panic_probe as _};
15use embassy_rp::block::ImageDef;
16
17#[link_section = ".start_block"]
18#[used]
19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
20
21// Program metadata for `picotool info`
22#[link_section = ".bi_entries"]
23#[used]
24pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
25 embassy_rp::binary_info_rp_cargo_bin_name!(),
26 embassy_rp::binary_info_rp_cargo_version!(),
27 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
28 embassy_rp::binary_info_rp_program_build_attribute!(),
29];
30
31
32#[allow(dead_code)]
33mod mcp23017 {
34 pub const ADDR: u8 = 0x20; // default addr
35
36 pub const IODIRA: u8 = 0x00;
37 pub const IPOLA: u8 = 0x02;
38 pub const GPINTENA: u8 = 0x04;
39 pub const DEFVALA: u8 = 0x06;
40 pub const INTCONA: u8 = 0x08;
41 pub const IOCONA: u8 = 0x0A;
42 pub const GPPUA: u8 = 0x0C;
43 pub const INTFA: u8 = 0x0E;
44 pub const INTCAPA: u8 = 0x10;
45 pub const GPIOA: u8 = 0x12;
46 pub const OLATA: u8 = 0x14;
47 pub const IODIRB: u8 = 0x01;
48 pub const IPOLB: u8 = 0x03;
49 pub const GPINTENB: u8 = 0x05;
50 pub const DEFVALB: u8 = 0x07;
51 pub const INTCONB: u8 = 0x09;
52 pub const IOCONB: u8 = 0x0B;
53 pub const GPPUB: u8 = 0x0D;
54 pub const INTFB: u8 = 0x0F;
55 pub const INTCAPB: u8 = 0x11;
56 pub const GPIOB: u8 = 0x13;
57 pub const OLATB: u8 = 0x15;
58}
59
60#[embassy_executor::main]
61async fn main(_spawner: Spawner) {
62 let p = embassy_rp::init(Default::default());
63
64 let sda = p.PIN_14;
65 let scl = p.PIN_15;
66
67 info!("set up i2c ");
68 let mut i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default());
69
70 use mcp23017::*;
71
72 info!("init mcp23017 config for IxpandO");
73 // init - a outputs, b inputs
74 i2c.write(ADDR, &[IODIRA, 0x00]).unwrap();
75 i2c.write(ADDR, &[IODIRB, 0xff]).unwrap();
76 i2c.write(ADDR, &[GPPUB, 0xff]).unwrap(); // pullups
77
78 let mut val = 0xaa;
79 loop {
80 let mut portb = [0];
81
82 i2c.write(mcp23017::ADDR, &[GPIOA, val]).unwrap();
83 i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).unwrap();
84
85 info!("portb = {:02x}", portb[0]);
86 val = !val;
87
88 Timer::after_secs(1).await;
89 }
90}
diff --git a/examples/rp23/src/bin/i2c_slave.rs b/examples/rp23/src/bin/i2c_slave.rs
new file mode 100644
index 000000000..4bf407bcc
--- /dev/null
+++ b/examples/rp23/src/bin/i2c_slave.rs
@@ -0,0 +1,132 @@
1//! This example shows how to use the 2040 as an i2c slave.
2#![no_std]
3#![no_main]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_rp::peripherals::{I2C0, I2C1};
8use embassy_rp::{bind_interrupts, i2c, i2c_slave};
9use embassy_time::Timer;
10use embedded_hal_async::i2c::I2c;
11use {defmt_rtt as _, panic_probe as _};
12use embassy_rp::{block::ImageDef};
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28bind_interrupts!(struct Irqs {
29 I2C0_IRQ => i2c::InterruptHandler<I2C0>;
30 I2C1_IRQ => i2c::InterruptHandler<I2C1>;
31});
32
33const DEV_ADDR: u8 = 0x42;
34
35#[embassy_executor::task]
36async fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! {
37 info!("Device start");
38
39 let mut state = 0;
40
41 loop {
42 let mut buf = [0u8; 128];
43 match dev.listen(&mut buf).await {
44 Ok(i2c_slave::Command::GeneralCall(len)) => info!("Device received general call write: {}", buf[..len]),
45 Ok(i2c_slave::Command::Read) => loop {
46 match dev.respond_to_read(&[state]).await {
47 Ok(x) => match x {
48 i2c_slave::ReadStatus::Done => break,
49 i2c_slave::ReadStatus::NeedMoreBytes => (),
50 i2c_slave::ReadStatus::LeftoverBytes(x) => {
51 info!("tried to write {} extra bytes", x);
52 break;
53 }
54 },
55 Err(e) => error!("error while responding {}", e),
56 }
57 },
58 Ok(i2c_slave::Command::Write(len)) => info!("Device received write: {}", buf[..len]),
59 Ok(i2c_slave::Command::WriteRead(len)) => {
60 info!("device received write read: {:x}", buf[..len]);
61 match buf[0] {
62 // Set the state
63 0xC2 => {
64 state = buf[1];
65 match dev.respond_and_fill(&[state], 0x00).await {
66 Ok(read_status) => info!("response read status {}", read_status),
67 Err(e) => error!("error while responding {}", e),
68 }
69 }
70 // Reset State
71 0xC8 => {
72 state = 0;
73 match dev.respond_and_fill(&[state], 0x00).await {
74 Ok(read_status) => info!("response read status {}", read_status),
75 Err(e) => error!("error while responding {}", e),
76 }
77 }
78 x => error!("Invalid Write Read {:x}", x),
79 }
80 }
81 Err(e) => error!("{}", e),
82 }
83 }
84}
85
86#[embassy_executor::task]
87async fn controller_task(mut con: i2c::I2c<'static, I2C0, i2c::Async>) {
88 info!("Controller start");
89
90 loop {
91 let mut resp_buff = [0u8; 2];
92 for i in 0..10 {
93 match con.write_read(DEV_ADDR, &[0xC2, i], &mut resp_buff).await {
94 Ok(_) => info!("write_read response: {}", resp_buff),
95 Err(e) => error!("Error writing {}", e),
96 }
97
98 Timer::after_millis(100).await;
99 }
100 match con.read(DEV_ADDR, &mut resp_buff).await {
101 Ok(_) => info!("read response: {}", resp_buff),
102 Err(e) => error!("Error writing {}", e),
103 }
104 match con.write_read(DEV_ADDR, &[0xC8], &mut resp_buff).await {
105 Ok(_) => info!("write_read response: {}", resp_buff),
106 Err(e) => error!("Error writing {}", e),
107 }
108 Timer::after_millis(100).await;
109 }
110}
111
112#[embassy_executor::main]
113async fn main(spawner: Spawner) {
114 let p = embassy_rp::init(Default::default());
115 info!("Hello World!");
116
117 let d_sda = p.PIN_3;
118 let d_scl = p.PIN_2;
119 let mut config = i2c_slave::Config::default();
120 config.addr = DEV_ADDR as u16;
121 let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config);
122
123 unwrap!(spawner.spawn(device_task(device)));
124
125 let c_sda = p.PIN_1;
126 let c_scl = p.PIN_0;
127 let mut config = i2c::Config::default();
128 config.frequency = 1_000_000;
129 let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config);
130
131 unwrap!(spawner.spawn(controller_task(controller)));
132}
diff --git a/examples/rp23/src/bin/interrupt.rs b/examples/rp23/src/bin/interrupt.rs
new file mode 100644
index 000000000..f46117f95
--- /dev/null
+++ b/examples/rp23/src/bin/interrupt.rs
@@ -0,0 +1,110 @@
1//! This example shows how you can use raw interrupt handlers alongside embassy.
2//! The example also showcases some of the options available for sharing resources/data.
3//!
4//! In the example, an ADC reading is triggered every time the PWM wraps around.
5//! The sample data is sent down a channel, to be processed inside a low priority task.
6//! The processed data is then used to adjust the PWM duty cycle, once every second.
7
8#![no_std]
9#![no_main]
10
11use core::cell::{Cell, RefCell};
12
13use defmt::*;
14use embassy_executor::Spawner;
15use embassy_rp::adc::{self, Adc, Blocking};
16use embassy_rp::gpio::Pull;
17use embassy_rp::interrupt;
18use embassy_rp::pwm::{Config, Pwm};
19use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
20use embassy_sync::blocking_mutex::Mutex;
21use embassy_sync::channel::Channel;
22use embassy_time::{Duration, Ticker};
23use portable_atomic::{AtomicU32, Ordering};
24use static_cell::StaticCell;
25use {defmt_rtt as _, panic_probe as _};
26use embassy_rp::block::ImageDef;
27
28#[link_section = ".start_block"]
29#[used]
30pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
31
32// Program metadata for `picotool info`
33#[link_section = ".bi_entries"]
34#[used]
35pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
36 embassy_rp::binary_info_rp_cargo_bin_name!(),
37 embassy_rp::binary_info_rp_cargo_version!(),
38 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
39 embassy_rp::binary_info_rp_program_build_attribute!(),
40];
41
42
43static COUNTER: AtomicU32 = AtomicU32::new(0);
44static PWM: Mutex<CriticalSectionRawMutex, RefCell<Option<Pwm>>> = Mutex::new(RefCell::new(None));
45static ADC: Mutex<CriticalSectionRawMutex, RefCell<Option<(Adc<Blocking>, adc::Channel)>>> =
46 Mutex::new(RefCell::new(None));
47static ADC_VALUES: Channel<CriticalSectionRawMutex, u16, 2048> = Channel::new();
48
49#[embassy_executor::main]
50async fn main(spawner: Spawner) {
51 embassy_rp::pac::SIO.spinlock(31).write_value(1);
52 let p = embassy_rp::init(Default::default());
53
54 let adc = Adc::new_blocking(p.ADC, Default::default());
55 let p26 = adc::Channel::new_pin(p.PIN_26, Pull::None);
56 ADC.lock(|a| a.borrow_mut().replace((adc, p26)));
57
58 let pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, Default::default());
59 PWM.lock(|p| p.borrow_mut().replace(pwm));
60
61 // Enable the interrupt for pwm slice 4
62 embassy_rp::pac::PWM.irq0_inte().modify(|w| w.set_ch4(true));
63 unsafe {
64 cortex_m::peripheral::NVIC::unmask(interrupt::PWM_IRQ_WRAP_0);
65 }
66
67 // Tasks require their resources to have 'static lifetime
68 // No Mutex needed when sharing within the same executor/prio level
69 static AVG: StaticCell<Cell<u32>> = StaticCell::new();
70 let avg = AVG.init(Default::default());
71 spawner.must_spawn(processing(avg));
72
73 let mut ticker = Ticker::every(Duration::from_secs(1));
74 loop {
75 ticker.next().await;
76 let freq = COUNTER.swap(0, Ordering::Relaxed);
77 info!("pwm freq: {:?} Hz", freq);
78 info!("adc average: {:?}", avg.get());
79
80 // Update the pwm duty cycle, based on the averaged adc reading
81 let mut config = Config::default();
82 config.compare_b = ((avg.get() as f32 / 4095.0) * config.top as f32) as _;
83 PWM.lock(|p| p.borrow_mut().as_mut().unwrap().set_config(&config));
84 }
85}
86
87#[embassy_executor::task]
88async fn processing(avg: &'static Cell<u32>) {
89 let mut buffer: heapless::HistoryBuffer<u16, 100> = Default::default();
90 loop {
91 let val = ADC_VALUES.receive().await;
92 buffer.write(val);
93 let sum: u32 = buffer.iter().map(|x| *x as u32).sum();
94 avg.set(sum / buffer.len() as u32);
95 }
96}
97
98#[interrupt]
99fn PWM_IRQ_WRAP_0() {
100 critical_section::with(|cs| {
101 let mut adc = ADC.borrow(cs).borrow_mut();
102 let (adc, p26) = adc.as_mut().unwrap();
103 let val = adc.blocking_read(p26).unwrap();
104 ADC_VALUES.try_send(val).ok();
105
106 // Clear the interrupt, so we don't immediately re-enter this irq handler
107 PWM.borrow(cs).borrow_mut().as_mut().unwrap().clear_wrapped();
108 });
109 COUNTER.fetch_add(1, Ordering::Relaxed);
110}
diff --git a/examples/rp23/src/bin/multicore.rs b/examples/rp23/src/bin/multicore.rs
new file mode 100644
index 000000000..0b20ecaae
--- /dev/null
+++ b/examples/rp23/src/bin/multicore.rs
@@ -0,0 +1,82 @@
1//! This example shows how to send messages between the two cores in the RP2040 chip.
2//!
3//! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Executor;
10use embassy_rp::gpio::{Level, Output};
11use embassy_rp::multicore::{spawn_core1, Stack};
12use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
13use embassy_sync::channel::Channel;
14use embassy_time::Timer;
15use static_cell::StaticCell;
16use {defmt_rtt as _, panic_probe as _};
17use embassy_rp::block::ImageDef;
18
19#[link_section = ".start_block"]
20#[used]
21pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
22
23// Program metadata for `picotool info`
24#[link_section = ".bi_entries"]
25#[used]
26pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
27 embassy_rp::binary_info_rp_cargo_bin_name!(),
28 embassy_rp::binary_info_rp_cargo_version!(),
29 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
30 embassy_rp::binary_info_rp_program_build_attribute!(),
31];
32
33
34static mut CORE1_STACK: Stack<4096> = Stack::new();
35static EXECUTOR0: StaticCell<Executor> = StaticCell::new();
36static EXECUTOR1: StaticCell<Executor> = StaticCell::new();
37static CHANNEL: Channel<CriticalSectionRawMutex, LedState, 1> = Channel::new();
38
39enum LedState {
40 On,
41 Off,
42}
43
44#[cortex_m_rt::entry]
45fn main() -> ! {
46 let p = embassy_rp::init(Default::default());
47 let led = Output::new(p.PIN_25, Level::Low);
48
49 spawn_core1(
50 p.CORE1,
51 unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) },
52 move || {
53 let executor1 = EXECUTOR1.init(Executor::new());
54 executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led))));
55 },
56 );
57
58 let executor0 = EXECUTOR0.init(Executor::new());
59 executor0.run(|spawner| unwrap!(spawner.spawn(core0_task())));
60}
61
62#[embassy_executor::task]
63async fn core0_task() {
64 info!("Hello from core 0");
65 loop {
66 CHANNEL.send(LedState::On).await;
67 Timer::after_millis(100).await;
68 CHANNEL.send(LedState::Off).await;
69 Timer::after_millis(400).await;
70 }
71}
72
73#[embassy_executor::task]
74async fn core1_task(mut led: Output<'static>) {
75 info!("Hello from core 1");
76 loop {
77 match CHANNEL.receive().await {
78 LedState::On => led.set_high(),
79 LedState::Off => led.set_low(),
80 }
81 }
82}
diff --git a/examples/rp23/src/bin/multiprio.rs b/examples/rp23/src/bin/multiprio.rs
new file mode 100644
index 000000000..52c801973
--- /dev/null
+++ b/examples/rp23/src/bin/multiprio.rs
@@ -0,0 +1,161 @@
1//! This example showcases how to create multiple Executor instances to run tasks at
2//! different priority levels.
3//!
4//! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling
5//! there's work in the queue, and `wfe` for waiting for work.
6//!
7//! Medium and high priority executors run in two interrupts with different priorities.
8//! Signaling work is done by pending the interrupt. No "waiting" needs to be done explicitly, since
9//! when there's work the interrupt will trigger and run the executor.
10//!
11//! Sample output below. Note that high priority ticks can interrupt everything else, and
12//! medium priority computations can interrupt low priority computations, making them to appear
13//! to take significantly longer time.
14//!
15//! ```not_rust
16//! [med] Starting long computation
17//! [med] done in 992 ms
18//! [high] tick!
19//! [low] Starting long computation
20//! [med] Starting long computation
21//! [high] tick!
22//! [high] tick!
23//! [med] done in 993 ms
24//! [med] Starting long computation
25//! [high] tick!
26//! [high] tick!
27//! [med] done in 993 ms
28//! [low] done in 3972 ms
29//! [med] Starting long computation
30//! [high] tick!
31//! [high] tick!
32//! [med] done in 993 ms
33//! ```
34//!
35//! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor.
36//! You will get an output like the following. Note that no computation is ever interrupted.
37//!
38//! ```not_rust
39//! [high] tick!
40//! [med] Starting long computation
41//! [med] done in 496 ms
42//! [low] Starting long computation
43//! [low] done in 992 ms
44//! [med] Starting long computation
45//! [med] done in 496 ms
46//! [high] tick!
47//! [low] Starting long computation
48//! [low] done in 992 ms
49//! [high] tick!
50//! [med] Starting long computation
51//! [med] done in 496 ms
52//! [high] tick!
53//! ```
54//!
55
56#![no_std]
57#![no_main]
58
59use cortex_m_rt::entry;
60use defmt::{info, unwrap};
61use embassy_executor::{Executor, InterruptExecutor};
62use embassy_rp::interrupt;
63use embassy_rp::interrupt::{InterruptExt, Priority};
64use embassy_time::{Instant, Timer, TICK_HZ};
65use static_cell::StaticCell;
66use {defmt_rtt as _, panic_probe as _};
67use embassy_rp::block::ImageDef;
68
69#[link_section = ".start_block"]
70#[used]
71pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
72
73// Program metadata for `picotool info`
74#[link_section = ".bi_entries"]
75#[used]
76pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
77 embassy_rp::binary_info_rp_cargo_bin_name!(),
78 embassy_rp::binary_info_rp_cargo_version!(),
79 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
80 embassy_rp::binary_info_rp_program_build_attribute!(),
81];
82
83
84#[embassy_executor::task]
85async fn run_high() {
86 loop {
87 info!(" [high] tick!");
88 Timer::after_ticks(673740).await;
89 }
90}
91
92#[embassy_executor::task]
93async fn run_med() {
94 loop {
95 let start = Instant::now();
96 info!(" [med] Starting long computation");
97
98 // Spin-wait to simulate a long CPU computation
99 embassy_time::block_for(embassy_time::Duration::from_secs(1)); // ~1 second
100
101 let end = Instant::now();
102 let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ;
103 info!(" [med] done in {} ms", ms);
104
105 Timer::after_ticks(53421).await;
106 }
107}
108
109#[embassy_executor::task]
110async fn run_low() {
111 loop {
112 let start = Instant::now();
113 info!("[low] Starting long computation");
114
115 // Spin-wait to simulate a long CPU computation
116 embassy_time::block_for(embassy_time::Duration::from_secs(2)); // ~2 seconds
117
118 let end = Instant::now();
119 let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ;
120 info!("[low] done in {} ms", ms);
121
122 Timer::after_ticks(82983).await;
123 }
124}
125
126static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new();
127static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new();
128static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();
129
130#[interrupt]
131unsafe fn SWI_IRQ_1() {
132 EXECUTOR_HIGH.on_interrupt()
133}
134
135#[interrupt]
136unsafe fn SWI_IRQ_0() {
137 EXECUTOR_MED.on_interrupt()
138}
139
140#[entry]
141fn main() -> ! {
142 info!("Hello World!");
143
144 let _p = embassy_rp::init(Default::default());
145
146 // High-priority executor: SWI_IRQ_1, priority level 2
147 interrupt::SWI_IRQ_1.set_priority(Priority::P2);
148 let spawner = EXECUTOR_HIGH.start(interrupt::SWI_IRQ_1);
149 unwrap!(spawner.spawn(run_high()));
150
151 // Medium-priority executor: SWI_IRQ_0, priority level 3
152 interrupt::SWI_IRQ_0.set_priority(Priority::P3);
153 let spawner = EXECUTOR_MED.start(interrupt::SWI_IRQ_0);
154 unwrap!(spawner.spawn(run_med()));
155
156 // Low priority executor: runs in thread mode, using WFE/SEV
157 let executor = EXECUTOR_LOW.init(Executor::new());
158 executor.run(|spawner| {
159 unwrap!(spawner.spawn(run_low()));
160 });
161}
diff --git a/examples/rp23/src/bin/pio_async.rs b/examples/rp23/src/bin/pio_async.rs
new file mode 100644
index 000000000..f3a48bbce
--- /dev/null
+++ b/examples/rp23/src/bin/pio_async.rs
@@ -0,0 +1,146 @@
1//! This example shows powerful PIO module in the RP2040 chip.
2
3#![no_std]
4#![no_main]
5use defmt::info;
6use embassy_executor::Spawner;
7use embassy_rp::bind_interrupts;
8use embassy_rp::peripherals::PIO0;
9use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine};
10use fixed::traits::ToFixed;
11use fixed_macro::types::U56F8;
12use {defmt_rtt as _, panic_probe as _};
13use embassy_rp::block::ImageDef;
14
15#[link_section = ".start_block"]
16#[used]
17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
18
19// Program metadata for `picotool info`
20#[link_section = ".bi_entries"]
21#[used]
22pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
23 embassy_rp::binary_info_rp_cargo_bin_name!(),
24 embassy_rp::binary_info_rp_cargo_version!(),
25 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
26 embassy_rp::binary_info_rp_program_build_attribute!(),
27];
28
29
30bind_interrupts!(struct Irqs {
31 PIO0_IRQ_0 => InterruptHandler<PIO0>;
32});
33
34fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) {
35 // Setup sm0
36
37 // Send data serially to pin
38 let prg = pio_proc::pio_asm!(
39 ".origin 16",
40 "set pindirs, 1",
41 ".wrap_target",
42 "out pins,1 [19]",
43 ".wrap",
44 );
45
46 let mut cfg = Config::default();
47 cfg.use_program(&pio.load_program(&prg.program), &[]);
48 let out_pin = pio.make_pio_pin(pin);
49 cfg.set_out_pins(&[&out_pin]);
50 cfg.set_set_pins(&[&out_pin]);
51 cfg.clock_divider = (U56F8!(125_000_000) / 20 / 200).to_fixed();
52 cfg.shift_out.auto_fill = true;
53 sm.set_config(&cfg);
54}
55
56#[embassy_executor::task]
57async fn pio_task_sm0(mut sm: StateMachine<'static, PIO0, 0>) {
58 sm.set_enable(true);
59
60 let mut v = 0x0f0caffa;
61 loop {
62 sm.tx().wait_push(v).await;
63 v ^= 0xffff;
64 info!("Pushed {:032b} to FIFO", v);
65 }
66}
67
68fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 1>) {
69 // Setupm sm1
70
71 // Read 0b10101 repeatedly until ISR is full
72 let prg = pio_proc::pio_asm!(
73 //
74 ".origin 8",
75 "set x, 0x15",
76 ".wrap_target",
77 "in x, 5 [31]",
78 ".wrap",
79 );
80
81 let mut cfg = Config::default();
82 cfg.use_program(&pio.load_program(&prg.program), &[]);
83 cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
84 cfg.shift_in.auto_fill = true;
85 cfg.shift_in.direction = ShiftDirection::Right;
86 sm.set_config(&cfg);
87}
88
89#[embassy_executor::task]
90async fn pio_task_sm1(mut sm: StateMachine<'static, PIO0, 1>) {
91 sm.set_enable(true);
92 loop {
93 let rx = sm.rx().wait_pull().await;
94 info!("Pulled {:032b} from FIFO", rx);
95 }
96}
97
98fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 2>) {
99 // Setup sm2
100
101 // Repeatedly trigger IRQ 3
102 let prg = pio_proc::pio_asm!(
103 ".origin 0",
104 ".wrap_target",
105 "set x,10",
106 "delay:",
107 "jmp x-- delay [15]",
108 "irq 3 [15]",
109 ".wrap",
110 );
111 let mut cfg = Config::default();
112 cfg.use_program(&pio.load_program(&prg.program), &[]);
113 cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
114 sm.set_config(&cfg);
115}
116
117#[embassy_executor::task]
118async fn pio_task_sm2(mut irq: Irq<'static, PIO0, 3>, mut sm: StateMachine<'static, PIO0, 2>) {
119 sm.set_enable(true);
120 loop {
121 irq.wait().await;
122 info!("IRQ trigged");
123 }
124}
125
126#[embassy_executor::main]
127async fn main(spawner: Spawner) {
128 let p = embassy_rp::init(Default::default());
129 let pio = p.PIO0;
130
131 let Pio {
132 mut common,
133 irq3,
134 mut sm0,
135 mut sm1,
136 mut sm2,
137 ..
138 } = Pio::new(pio, Irqs);
139
140 setup_pio_task_sm0(&mut common, &mut sm0, p.PIN_0);
141 setup_pio_task_sm1(&mut common, &mut sm1);
142 setup_pio_task_sm2(&mut common, &mut sm2);
143 spawner.spawn(pio_task_sm0(sm0)).unwrap();
144 spawner.spawn(pio_task_sm1(sm1)).unwrap();
145 spawner.spawn(pio_task_sm2(irq3, sm2)).unwrap();
146}
diff --git a/examples/rp23/src/bin/pio_dma.rs b/examples/rp23/src/bin/pio_dma.rs
new file mode 100644
index 000000000..d5a831d09
--- /dev/null
+++ b/examples/rp23/src/bin/pio_dma.rs
@@ -0,0 +1,99 @@
1//! This example shows powerful PIO module in the RP2040 chip.
2
3#![no_std]
4#![no_main]
5use defmt::info;
6use embassy_executor::Spawner;
7use embassy_futures::join::join;
8use embassy_rp::peripherals::PIO0;
9use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection};
10use embassy_rp::{bind_interrupts, Peripheral};
11use fixed::traits::ToFixed;
12use fixed_macro::types::U56F8;
13use {defmt_rtt as _, panic_probe as _};
14use embassy_rp::block::ImageDef;
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30
31bind_interrupts!(struct Irqs {
32 PIO0_IRQ_0 => InterruptHandler<PIO0>;
33});
34
35fn swap_nibbles(v: u32) -> u32 {
36 let v = (v & 0x0f0f_0f0f) << 4 | (v & 0xf0f0_f0f0) >> 4;
37 let v = (v & 0x00ff_00ff) << 8 | (v & 0xff00_ff00) >> 8;
38 (v & 0x0000_ffff) << 16 | (v & 0xffff_0000) >> 16
39}
40
41#[embassy_executor::main]
42async fn main(_spawner: Spawner) {
43 let p = embassy_rp::init(Default::default());
44 let pio = p.PIO0;
45 let Pio {
46 mut common,
47 sm0: mut sm,
48 ..
49 } = Pio::new(pio, Irqs);
50
51 let prg = pio_proc::pio_asm!(
52 ".origin 0",
53 "set pindirs,1",
54 ".wrap_target",
55 "set y,7",
56 "loop:",
57 "out x,4",
58 "in x,4",
59 "jmp y--, loop",
60 ".wrap",
61 );
62
63 let mut cfg = Config::default();
64 cfg.use_program(&common.load_program(&prg.program), &[]);
65 cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed();
66 cfg.shift_in = ShiftConfig {
67 auto_fill: true,
68 threshold: 32,
69 direction: ShiftDirection::Left,
70 };
71 cfg.shift_out = ShiftConfig {
72 auto_fill: true,
73 threshold: 32,
74 direction: ShiftDirection::Right,
75 };
76
77 sm.set_config(&cfg);
78 sm.set_enable(true);
79
80 let mut dma_out_ref = p.DMA_CH0.into_ref();
81 let mut dma_in_ref = p.DMA_CH1.into_ref();
82 let mut dout = [0x12345678u32; 29];
83 for i in 1..dout.len() {
84 dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7;
85 }
86 let mut din = [0u32; 29];
87 loop {
88 let (rx, tx) = sm.rx_tx();
89 join(
90 tx.dma_push(dma_out_ref.reborrow(), &dout),
91 rx.dma_pull(dma_in_ref.reborrow(), &mut din),
92 )
93 .await;
94 for i in 0..din.len() {
95 assert_eq!(din[i], swap_nibbles(dout[i]));
96 }
97 info!("Swapped {} words", dout.len());
98 }
99}
diff --git a/examples/rp23/src/bin/pio_hd44780.rs b/examples/rp23/src/bin/pio_hd44780.rs
new file mode 100644
index 000000000..f601bbc66
--- /dev/null
+++ b/examples/rp23/src/bin/pio_hd44780.rs
@@ -0,0 +1,256 @@
1//! This example shows powerful PIO module in the RP2040 chip to communicate with a HD44780 display.
2//! See (https://www.sparkfun.com/datasheets/LCD/HD44780.pdf)
3
4#![no_std]
5#![no_main]
6
7use core::fmt::Write;
8
9use embassy_executor::Spawner;
10use embassy_rp::dma::{AnyChannel, Channel};
11use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{
13 Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
14};
15use embassy_rp::pwm::{self, Pwm};
16use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef};
17use embassy_time::{Instant, Timer};
18use {defmt_rtt as _, panic_probe as _};
19use embassy_rp::block::ImageDef;
20
21#[link_section = ".start_block"]
22#[used]
23pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
24
25// Program metadata for `picotool info`
26#[link_section = ".bi_entries"]
27#[used]
28pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
29 embassy_rp::binary_info_rp_cargo_bin_name!(),
30 embassy_rp::binary_info_rp_cargo_version!(),
31 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
32 embassy_rp::binary_info_rp_program_build_attribute!(),
33];
34
35
36bind_interrupts!(pub struct Irqs {
37 PIO0_IRQ_0 => InterruptHandler<PIO0>;
38});
39
40#[embassy_executor::main]
41async fn main(_spawner: Spawner) {
42 // this test assumes a 2x16 HD44780 display attached as follow:
43 // rs = PIN0
44 // rw = PIN1
45 // e = PIN2
46 // db4 = PIN3
47 // db5 = PIN4
48 // db6 = PIN5
49 // db7 = PIN6
50 // additionally a pwm signal for a bias voltage charge pump is provided on pin 15,
51 // allowing direct connection of the display to the RP2040 without level shifters.
52 let p = embassy_rp::init(Default::default());
53
54 let _pwm = Pwm::new_output_b(p.PWM_SLICE7, p.PIN_15, {
55 let mut c = pwm::Config::default();
56 c.divider = 125.into();
57 c.top = 100;
58 c.compare_b = 50;
59 c
60 });
61
62 let mut hd = HD44780::new(
63 p.PIO0, Irqs, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6,
64 )
65 .await;
66
67 loop {
68 struct Buf<const N: usize>([u8; N], usize);
69 impl<const N: usize> Write for Buf<N> {
70 fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
71 for b in s.as_bytes() {
72 if self.1 >= N {
73 return Err(core::fmt::Error);
74 }
75 self.0[self.1] = *b;
76 self.1 += 1;
77 }
78 Ok(())
79 }
80 }
81 let mut buf = Buf([0; 16], 0);
82 write!(buf, "up {}s", Instant::now().as_micros() as f32 / 1e6).unwrap();
83 hd.add_line(&buf.0[0..buf.1]).await;
84 Timer::after_secs(1).await;
85 }
86}
87
88pub struct HD44780<'l> {
89 dma: PeripheralRef<'l, AnyChannel>,
90 sm: StateMachine<'l, PIO0, 0>,
91
92 buf: [u8; 40],
93}
94
95impl<'l> HD44780<'l> {
96 pub async fn new(
97 pio: impl Peripheral<P = PIO0> + 'l,
98 irq: Irqs,
99 dma: impl Peripheral<P = impl Channel> + 'l,
100 rs: impl PioPin,
101 rw: impl PioPin,
102 e: impl PioPin,
103 db4: impl PioPin,
104 db5: impl PioPin,
105 db6: impl PioPin,
106 db7: impl PioPin,
107 ) -> HD44780<'l> {
108 into_ref!(dma);
109
110 let Pio {
111 mut common,
112 mut irq0,
113 mut sm0,
114 ..
115 } = Pio::new(pio, irq);
116
117 // takes command words (<wait:24> <command:4> <0:4>)
118 let prg = pio_proc::pio_asm!(
119 r#"
120 .side_set 1 opt
121 .origin 20
122
123 loop:
124 out x, 24
125 delay:
126 jmp x--, delay
127 out pins, 4 side 1
128 out null, 4 side 0
129 jmp !osre, loop
130 irq 0
131 "#,
132 );
133
134 let rs = common.make_pio_pin(rs);
135 let rw = common.make_pio_pin(rw);
136 let e = common.make_pio_pin(e);
137 let db4 = common.make_pio_pin(db4);
138 let db5 = common.make_pio_pin(db5);
139 let db6 = common.make_pio_pin(db6);
140 let db7 = common.make_pio_pin(db7);
141
142 sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]);
143
144 let mut cfg = Config::default();
145 cfg.use_program(&common.load_program(&prg.program), &[&e]);
146 cfg.clock_divider = 125u8.into();
147 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
148 cfg.shift_out = ShiftConfig {
149 auto_fill: true,
150 direction: ShiftDirection::Left,
151 threshold: 32,
152 };
153 cfg.fifo_join = FifoJoin::TxOnly;
154 sm0.set_config(&cfg);
155
156 sm0.set_enable(true);
157 // init to 8 bit thrice
158 sm0.tx().push((50000 << 8) | 0x30);
159 sm0.tx().push((5000 << 8) | 0x30);
160 sm0.tx().push((200 << 8) | 0x30);
161 // init 4 bit
162 sm0.tx().push((200 << 8) | 0x20);
163 // set font and lines
164 sm0.tx().push((50 << 8) | 0x20);
165 sm0.tx().push(0b1100_0000);
166
167 irq0.wait().await;
168 sm0.set_enable(false);
169
170 // takes command sequences (<rs:1> <count:7>, data...)
171 // many side sets are only there to free up a delay bit!
172 let prg = pio_proc::pio_asm!(
173 r#"
174 .origin 27
175 .side_set 1
176
177 .wrap_target
178 pull side 0
179 out x 1 side 0 ; !rs
180 out y 7 side 0 ; #data - 1
181
182 ; rs/rw to e: >= 60ns
183 ; e high time: >= 500ns
184 ; e low time: >= 500ns
185 ; read data valid after e falling: ~5ns
186 ; write data hold after e falling: ~10ns
187
188 loop:
189 pull side 0
190 jmp !x data side 0
191 command:
192 set pins 0b00 side 0
193 jmp shift side 0
194 data:
195 set pins 0b01 side 0
196 shift:
197 out pins 4 side 1 [9]
198 nop side 0 [9]
199 out pins 4 side 1 [9]
200 mov osr null side 0 [7]
201 out pindirs 4 side 0
202 set pins 0b10 side 0
203 busy:
204 nop side 1 [9]
205 jmp pin more side 0 [9]
206 mov osr ~osr side 1 [9]
207 nop side 0 [4]
208 out pindirs 4 side 0
209 jmp y-- loop side 0
210 .wrap
211 more:
212 nop side 1 [9]
213 jmp busy side 0 [9]
214 "#
215 );
216
217 let mut cfg = Config::default();
218 cfg.use_program(&common.load_program(&prg.program), &[&e]);
219 cfg.clock_divider = 8u8.into(); // ~64ns/insn
220 cfg.set_jmp_pin(&db7);
221 cfg.set_set_pins(&[&rs, &rw]);
222 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
223 cfg.shift_out.direction = ShiftDirection::Left;
224 cfg.fifo_join = FifoJoin::TxOnly;
225 sm0.set_config(&cfg);
226
227 sm0.set_enable(true);
228
229 // display on and cursor on and blinking, reset display
230 sm0.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await;
231
232 Self {
233 dma: dma.map_into(),
234 sm: sm0,
235 buf: [0x20; 40],
236 }
237 }
238
239 pub async fn add_line(&mut self, s: &[u8]) {
240 // move cursor to 0:0, prepare 16 characters
241 self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]);
242 // move line 2 up
243 self.buf.copy_within(22..38, 3);
244 // move cursor to 1:0, prepare 16 characters
245 self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]);
246 // file line 2 with spaces
247 self.buf[22..38].fill(0x20);
248 // copy input line
249 let len = s.len().min(16);
250 self.buf[22..22 + len].copy_from_slice(&s[0..len]);
251 // set cursor to 1:15
252 self.buf[38..].copy_from_slice(&[0x80, 0xcf]);
253
254 self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await;
255 }
256}
diff --git a/examples/rp23/src/bin/pio_i2s.rs b/examples/rp23/src/bin/pio_i2s.rs
new file mode 100644
index 000000000..b12b050e6
--- /dev/null
+++ b/examples/rp23/src/bin/pio_i2s.rs
@@ -0,0 +1,141 @@
1//! This example shows generating audio and sending it to a connected i2s DAC using the PIO
2//! module of the RP2040.
3//!
4//! Connect the i2s DAC as follows:
5//! bclk : GPIO 18
6//! lrc : GPIO 19
7//! din : GPIO 20
8//! Then hold down the boot select button to trigger a rising triangle waveform.
9
10#![no_std]
11#![no_main]
12
13use core::mem;
14
15use embassy_executor::Spawner;
16use embassy_rp::peripherals::PIO0;
17use embassy_rp::pio::{Config, FifoJoin, InterruptHandler, Pio, ShiftConfig, ShiftDirection};
18use embassy_rp::{bind_interrupts, Peripheral};
19use fixed::traits::ToFixed;
20use static_cell::StaticCell;
21use {defmt_rtt as _, panic_probe as _};
22use embassy_rp::block::ImageDef;
23
24#[link_section = ".start_block"]
25#[used]
26pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
27
28// Program metadata for `picotool info`
29#[link_section = ".bi_entries"]
30#[used]
31pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
32 embassy_rp::binary_info_rp_cargo_bin_name!(),
33 embassy_rp::binary_info_rp_cargo_version!(),
34 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
35 embassy_rp::binary_info_rp_program_build_attribute!(),
36];
37
38
39bind_interrupts!(struct Irqs {
40 PIO0_IRQ_0 => InterruptHandler<PIO0>;
41});
42
43const SAMPLE_RATE: u32 = 48_000;
44
45#[embassy_executor::main]
46async fn main(_spawner: Spawner) {
47 let p = embassy_rp::init(Default::default());
48
49 // Setup pio state machine for i2s output
50 let mut pio = Pio::new(p.PIO0, Irqs);
51
52 #[rustfmt::skip]
53 let pio_program = pio_proc::pio_asm!(
54 ".side_set 2",
55 " set x, 14 side 0b01", // side 0bWB - W = Word Clock, B = Bit Clock
56 "left_data:",
57 " out pins, 1 side 0b00",
58 " jmp x-- left_data side 0b01",
59 " out pins 1 side 0b10",
60 " set x, 14 side 0b11",
61 "right_data:",
62 " out pins 1 side 0b10",
63 " jmp x-- right_data side 0b11",
64 " out pins 1 side 0b00",
65 );
66
67 let bit_clock_pin = p.PIN_18;
68 let left_right_clock_pin = p.PIN_19;
69 let data_pin = p.PIN_20;
70
71 let data_pin = pio.common.make_pio_pin(data_pin);
72 let bit_clock_pin = pio.common.make_pio_pin(bit_clock_pin);
73 let left_right_clock_pin = pio.common.make_pio_pin(left_right_clock_pin);
74
75 let cfg = {
76 let mut cfg = Config::default();
77 cfg.use_program(
78 &pio.common.load_program(&pio_program.program),
79 &[&bit_clock_pin, &left_right_clock_pin],
80 );
81 cfg.set_out_pins(&[&data_pin]);
82 const BIT_DEPTH: u32 = 16;
83 const CHANNELS: u32 = 2;
84 let clock_frequency = SAMPLE_RATE * BIT_DEPTH * CHANNELS;
85 cfg.clock_divider = (125_000_000. / clock_frequency as f64 / 2.).to_fixed();
86 cfg.shift_out = ShiftConfig {
87 threshold: 32,
88 direction: ShiftDirection::Left,
89 auto_fill: true,
90 };
91 // join fifos to have twice the time to start the next dma transfer
92 cfg.fifo_join = FifoJoin::TxOnly;
93 cfg
94 };
95 pio.sm0.set_config(&cfg);
96 pio.sm0.set_pin_dirs(
97 embassy_rp::pio::Direction::Out,
98 &[&data_pin, &left_right_clock_pin, &bit_clock_pin],
99 );
100
101 // create two audio buffers (back and front) which will take turns being
102 // filled with new audio data and being sent to the pio fifo using dma
103 const BUFFER_SIZE: usize = 960;
104 static DMA_BUFFER: StaticCell<[u32; BUFFER_SIZE * 2]> = StaticCell::new();
105 let dma_buffer = DMA_BUFFER.init_with(|| [0u32; BUFFER_SIZE * 2]);
106 let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE);
107
108 // start pio state machine
109 pio.sm0.set_enable(true);
110 let tx = pio.sm0.tx();
111 let mut dma_ref = p.DMA_CH0.into_ref();
112
113 let mut fade_value: i32 = 0;
114 let mut phase: i32 = 0;
115
116 loop {
117 // trigger transfer of front buffer data to the pio fifo
118 // but don't await the returned future, yet
119 let dma_future = tx.dma_push(dma_ref.reborrow(), front_buffer);
120
121 // fade in audio
122 let fade_target = i32::MAX;
123
124 // fill back buffer with fresh audio samples before awaiting the dma future
125 for s in back_buffer.iter_mut() {
126 // exponential approach of fade_value => fade_target
127 fade_value += (fade_target - fade_value) >> 14;
128 // generate triangle wave with amplitude and frequency based on fade value
129 phase = (phase + (fade_value >> 22)) & 0xffff;
130 let triangle_sample = (phase as i16 as i32).abs() - 16384;
131 let sample = (triangle_sample * (fade_value >> 15)) >> 16;
132 // duplicate mono sample into lower and upper half of dma word
133 *s = (sample as u16 as u32) * 0x10001;
134 }
135
136 // now await the dma future. once the dma finishes, the next buffer needs to be queued
137 // within DMA_DEPTH / SAMPLE_RATE = 8 / 48000 seconds = 166us
138 dma_future.await;
139 mem::swap(&mut back_buffer, &mut front_buffer);
140 }
141}
diff --git a/examples/rp23/src/bin/pio_pwm.rs b/examples/rp23/src/bin/pio_pwm.rs
new file mode 100644
index 000000000..c8e834eae
--- /dev/null
+++ b/examples/rp23/src/bin/pio_pwm.rs
@@ -0,0 +1,134 @@
1//! This example shows how to create a pwm using the PIO module in the RP2040 chip.
2
3#![no_std]
4#![no_main]
5use core::time::Duration;
6
7use embassy_executor::Spawner;
8use embassy_rp::gpio::Level;
9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine};
11use embassy_rp::{bind_interrupts, clocks};
12use embassy_time::Timer;
13use pio::InstructionOperands;
14use {defmt_rtt as _, panic_probe as _};
15use embassy_rp::block::ImageDef;
16
17#[link_section = ".start_block"]
18#[used]
19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
20
21// Program metadata for `picotool info`
22#[link_section = ".bi_entries"]
23#[used]
24pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
25 embassy_rp::binary_info_rp_cargo_bin_name!(),
26 embassy_rp::binary_info_rp_cargo_version!(),
27 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
28 embassy_rp::binary_info_rp_program_build_attribute!(),
29];
30
31
32const REFRESH_INTERVAL: u64 = 20000;
33
34bind_interrupts!(struct Irqs {
35 PIO0_IRQ_0 => InterruptHandler<PIO0>;
36});
37
38pub fn to_pio_cycles(duration: Duration) -> u32 {
39 (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
40}
41
42pub struct PwmPio<'d, T: Instance, const SM: usize> {
43 sm: StateMachine<'d, T, SM>,
44}
45
46impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
47 pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
48 let prg = pio_proc::pio_asm!(
49 ".side_set 1 opt"
50 "pull noblock side 0"
51 "mov x, osr"
52 "mov y, isr"
53 "countloop:"
54 "jmp x!=y noset"
55 "jmp skip side 1"
56 "noset:"
57 "nop"
58 "skip:"
59 "jmp y-- countloop"
60 );
61
62 pio.load_program(&prg.program);
63 let pin = pio.make_pio_pin(pin);
64 sm.set_pins(Level::High, &[&pin]);
65 sm.set_pin_dirs(Direction::Out, &[&pin]);
66
67 let mut cfg = Config::default();
68 cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
69
70 sm.set_config(&cfg);
71
72 Self { sm }
73 }
74
75 pub fn start(&mut self) {
76 self.sm.set_enable(true);
77 }
78
79 pub fn stop(&mut self) {
80 self.sm.set_enable(false);
81 }
82
83 pub fn set_period(&mut self, duration: Duration) {
84 let is_enabled = self.sm.is_enabled();
85 while !self.sm.tx().empty() {} // Make sure that the queue is empty
86 self.sm.set_enable(false);
87 self.sm.tx().push(to_pio_cycles(duration));
88 unsafe {
89 self.sm.exec_instr(
90 InstructionOperands::PULL {
91 if_empty: false,
92 block: false,
93 }
94 .encode(),
95 );
96 self.sm.exec_instr(
97 InstructionOperands::OUT {
98 destination: ::pio::OutDestination::ISR,
99 bit_count: 32,
100 }
101 .encode(),
102 );
103 };
104 if is_enabled {
105 self.sm.set_enable(true) // Enable if previously enabled
106 }
107 }
108
109 pub fn set_level(&mut self, level: u32) {
110 self.sm.tx().push(level);
111 }
112
113 pub fn write(&mut self, duration: Duration) {
114 self.set_level(to_pio_cycles(duration));
115 }
116}
117
118#[embassy_executor::main]
119async fn main(_spawner: Spawner) {
120 let p = embassy_rp::init(Default::default());
121 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
122
123 // Note that PIN_25 is the led pin on the Pico
124 let mut pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_25);
125 pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL));
126 pwm_pio.start();
127
128 let mut duration = 0;
129 loop {
130 duration = (duration + 1) % 1000;
131 pwm_pio.write(Duration::from_micros(duration));
132 Timer::after_millis(1).await;
133 }
134}
diff --git a/examples/rp23/src/bin/pio_rotary_encoder.rs b/examples/rp23/src/bin/pio_rotary_encoder.rs
new file mode 100644
index 000000000..971b55c9e
--- /dev/null
+++ b/examples/rp23/src/bin/pio_rotary_encoder.rs
@@ -0,0 +1,96 @@
1//! This example shows how to use the PIO module in the RP2040 to read a quadrature rotary encoder.
2
3#![no_std]
4#![no_main]
5
6use defmt::info;
7use embassy_executor::Spawner;
8use embassy_rp::gpio::Pull;
9use embassy_rp::peripherals::PIO0;
10use embassy_rp::{bind_interrupts, pio};
11use fixed::traits::ToFixed;
12use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine};
13use {defmt_rtt as _, panic_probe as _};
14use embassy_rp::block::ImageDef;
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30
31bind_interrupts!(struct Irqs {
32 PIO0_IRQ_0 => InterruptHandler<PIO0>;
33});
34
35pub struct PioEncoder<'d, T: Instance, const SM: usize> {
36 sm: StateMachine<'d, T, SM>,
37}
38
39impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> {
40 pub fn new(
41 pio: &mut Common<'d, T>,
42 mut sm: StateMachine<'d, T, SM>,
43 pin_a: impl PioPin,
44 pin_b: impl PioPin,
45 ) -> Self {
46 let mut pin_a = pio.make_pio_pin(pin_a);
47 let mut pin_b = pio.make_pio_pin(pin_b);
48 pin_a.set_pull(Pull::Up);
49 pin_b.set_pull(Pull::Up);
50 sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]);
51
52 let prg = pio_proc::pio_asm!("wait 1 pin 1", "wait 0 pin 1", "in pins, 2", "push",);
53
54 let mut cfg = Config::default();
55 cfg.set_in_pins(&[&pin_a, &pin_b]);
56 cfg.fifo_join = FifoJoin::RxOnly;
57 cfg.shift_in.direction = ShiftDirection::Left;
58 cfg.clock_divider = 10_000.to_fixed();
59 cfg.use_program(&pio.load_program(&prg.program), &[]);
60 sm.set_config(&cfg);
61 sm.set_enable(true);
62 Self { sm }
63 }
64
65 pub async fn read(&mut self) -> Direction {
66 loop {
67 match self.sm.rx().wait_pull().await {
68 0 => return Direction::CounterClockwise,
69 1 => return Direction::Clockwise,
70 _ => {}
71 }
72 }
73 }
74}
75
76pub enum Direction {
77 Clockwise,
78 CounterClockwise,
79}
80
81#[embassy_executor::main]
82async fn main(_spawner: Spawner) {
83 let p = embassy_rp::init(Default::default());
84 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
85
86 let mut encoder = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5);
87
88 let mut count = 0;
89 loop {
90 info!("Count: {}", count);
91 count += match encoder.read().await {
92 Direction::Clockwise => 1,
93 Direction::CounterClockwise => -1,
94 };
95 }
96}
diff --git a/examples/rp23/src/bin/pio_servo.rs b/examples/rp23/src/bin/pio_servo.rs
new file mode 100644
index 000000000..67e52019a
--- /dev/null
+++ b/examples/rp23/src/bin/pio_servo.rs
@@ -0,0 +1,224 @@
1//! This example shows how to create a pwm using the PIO module in the RP2040 chip.
2
3#![no_std]
4#![no_main]
5use core::time::Duration;
6
7use embassy_executor::Spawner;
8use embassy_rp::gpio::Level;
9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine};
11use embassy_rp::{bind_interrupts, clocks};
12use embassy_time::Timer;
13use pio::InstructionOperands;
14use {defmt_rtt as _, panic_probe as _};
15use embassy_rp::block::ImageDef;
16
17#[link_section = ".start_block"]
18#[used]
19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
20
21// Program metadata for `picotool info`
22#[link_section = ".bi_entries"]
23#[used]
24pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
25 embassy_rp::binary_info_rp_cargo_bin_name!(),
26 embassy_rp::binary_info_rp_cargo_version!(),
27 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
28 embassy_rp::binary_info_rp_program_build_attribute!(),
29];
30
31
32const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo
33const DEFAULT_MAX_PULSE_WIDTH: u64 = 2000; // uncalibrated default, the longest duty cycle sent to a servo
34const DEFAULT_MAX_DEGREE_ROTATION: u64 = 160; // 160 degrees is typical
35const REFRESH_INTERVAL: u64 = 20000; // The period of each cycle
36
37bind_interrupts!(struct Irqs {
38 PIO0_IRQ_0 => InterruptHandler<PIO0>;
39});
40
41pub fn to_pio_cycles(duration: Duration) -> u32 {
42 (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
43}
44
45pub struct PwmPio<'d, T: Instance, const SM: usize> {
46 sm: StateMachine<'d, T, SM>,
47}
48
49impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
50 pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
51 let prg = pio_proc::pio_asm!(
52 ".side_set 1 opt"
53 "pull noblock side 0"
54 "mov x, osr"
55 "mov y, isr"
56 "countloop:"
57 "jmp x!=y noset"
58 "jmp skip side 1"
59 "noset:"
60 "nop"
61 "skip:"
62 "jmp y-- countloop"
63 );
64
65 pio.load_program(&prg.program);
66 let pin = pio.make_pio_pin(pin);
67 sm.set_pins(Level::High, &[&pin]);
68 sm.set_pin_dirs(Direction::Out, &[&pin]);
69
70 let mut cfg = Config::default();
71 cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
72
73 sm.set_config(&cfg);
74
75 Self { sm }
76 }
77
78 pub fn start(&mut self) {
79 self.sm.set_enable(true);
80 }
81
82 pub fn stop(&mut self) {
83 self.sm.set_enable(false);
84 }
85
86 pub fn set_period(&mut self, duration: Duration) {
87 let is_enabled = self.sm.is_enabled();
88 while !self.sm.tx().empty() {} // Make sure that the queue is empty
89 self.sm.set_enable(false);
90 self.sm.tx().push(to_pio_cycles(duration));
91 unsafe {
92 self.sm.exec_instr(
93 InstructionOperands::PULL {
94 if_empty: false,
95 block: false,
96 }
97 .encode(),
98 );
99 self.sm.exec_instr(
100 InstructionOperands::OUT {
101 destination: ::pio::OutDestination::ISR,
102 bit_count: 32,
103 }
104 .encode(),
105 );
106 };
107 if is_enabled {
108 self.sm.set_enable(true) // Enable if previously enabled
109 }
110 }
111
112 pub fn set_level(&mut self, level: u32) {
113 self.sm.tx().push(level);
114 }
115
116 pub fn write(&mut self, duration: Duration) {
117 self.set_level(to_pio_cycles(duration));
118 }
119}
120
121pub struct ServoBuilder<'d, T: Instance, const SM: usize> {
122 pwm: PwmPio<'d, T, SM>,
123 period: Duration,
124 min_pulse_width: Duration,
125 max_pulse_width: Duration,
126 max_degree_rotation: u64,
127}
128
129impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> {
130 pub fn new(pwm: PwmPio<'d, T, SM>) -> Self {
131 Self {
132 pwm,
133 period: Duration::from_micros(REFRESH_INTERVAL),
134 min_pulse_width: Duration::from_micros(DEFAULT_MIN_PULSE_WIDTH),
135 max_pulse_width: Duration::from_micros(DEFAULT_MAX_PULSE_WIDTH),
136 max_degree_rotation: DEFAULT_MAX_DEGREE_ROTATION,
137 }
138 }
139
140 pub fn set_period(mut self, duration: Duration) -> Self {
141 self.period = duration;
142 self
143 }
144
145 pub fn set_min_pulse_width(mut self, duration: Duration) -> Self {
146 self.min_pulse_width = duration;
147 self
148 }
149
150 pub fn set_max_pulse_width(mut self, duration: Duration) -> Self {
151 self.max_pulse_width = duration;
152 self
153 }
154
155 pub fn set_max_degree_rotation(mut self, degree: u64) -> Self {
156 self.max_degree_rotation = degree;
157 self
158 }
159
160 pub fn build(mut self) -> Servo<'d, T, SM> {
161 self.pwm.set_period(self.period);
162 Servo {
163 pwm: self.pwm,
164 min_pulse_width: self.min_pulse_width,
165 max_pulse_width: self.max_pulse_width,
166 max_degree_rotation: self.max_degree_rotation,
167 }
168 }
169}
170
171pub struct Servo<'d, T: Instance, const SM: usize> {
172 pwm: PwmPio<'d, T, SM>,
173 min_pulse_width: Duration,
174 max_pulse_width: Duration,
175 max_degree_rotation: u64,
176}
177
178impl<'d, T: Instance, const SM: usize> Servo<'d, T, SM> {
179 pub fn start(&mut self) {
180 self.pwm.start();
181 }
182
183 pub fn stop(&mut self) {
184 self.pwm.stop();
185 }
186
187 pub fn write_time(&mut self, duration: Duration) {
188 self.pwm.write(duration);
189 }
190
191 pub fn rotate(&mut self, degree: u64) {
192 let degree_per_nano_second = (self.max_pulse_width.as_nanos() as u64 - self.min_pulse_width.as_nanos() as u64)
193 / self.max_degree_rotation;
194 let mut duration =
195 Duration::from_nanos(degree * degree_per_nano_second + self.min_pulse_width.as_nanos() as u64);
196 if self.max_pulse_width < duration {
197 duration = self.max_pulse_width;
198 }
199
200 self.pwm.write(duration);
201 }
202}
203
204#[embassy_executor::main]
205async fn main(_spawner: Spawner) {
206 let p = embassy_rp::init(Default::default());
207 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
208
209 let pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_1);
210 let mut servo = ServoBuilder::new(pwm_pio)
211 .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo
212 .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment.
213 .set_max_pulse_width(Duration::from_micros(2600)) // Along with this value.
214 .build();
215
216 servo.start();
217
218 let mut degree = 0;
219 loop {
220 degree = (degree + 1) % 120;
221 servo.rotate(degree);
222 Timer::after_millis(50).await;
223 }
224}
diff --git a/examples/rp23/src/bin/pio_stepper.rs b/examples/rp23/src/bin/pio_stepper.rs
new file mode 100644
index 000000000..9cbf0bd92
--- /dev/null
+++ b/examples/rp23/src/bin/pio_stepper.rs
@@ -0,0 +1,184 @@
1//! This example shows how to use the PIO module in the RP2040 to implement a stepper motor driver
2//! for a 5-wire stepper such as the 28BYJ-48. You can halt an ongoing rotation by dropping the future.
3
4#![no_std]
5#![no_main]
6use core::mem::{self, MaybeUninit};
7
8use defmt::info;
9use embassy_executor::Spawner;
10use embassy_rp::bind_interrupts;
11use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Irq, Pio, PioPin, StateMachine};
13use embassy_time::{with_timeout, Duration, Timer};
14use fixed::traits::ToFixed;
15use fixed::types::extra::U8;
16use fixed::FixedU32;
17use {defmt_rtt as _, panic_probe as _};
18use embassy_rp::block::ImageDef;
19
20#[link_section = ".start_block"]
21#[used]
22pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
23
24// Program metadata for `picotool info`
25#[link_section = ".bi_entries"]
26#[used]
27pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
28 embassy_rp::binary_info_rp_cargo_bin_name!(),
29 embassy_rp::binary_info_rp_cargo_version!(),
30 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
31 embassy_rp::binary_info_rp_program_build_attribute!(),
32];
33
34
35bind_interrupts!(struct Irqs {
36 PIO0_IRQ_0 => InterruptHandler<PIO0>;
37});
38
39pub struct PioStepper<'d, T: Instance, const SM: usize> {
40 irq: Irq<'d, T, SM>,
41 sm: StateMachine<'d, T, SM>,
42}
43
44impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> {
45 pub fn new(
46 pio: &mut Common<'d, T>,
47 mut sm: StateMachine<'d, T, SM>,
48 irq: Irq<'d, T, SM>,
49 pin0: impl PioPin,
50 pin1: impl PioPin,
51 pin2: impl PioPin,
52 pin3: impl PioPin,
53 ) -> Self {
54 let prg = pio_proc::pio_asm!(
55 "pull block",
56 "mov x, osr",
57 "pull block",
58 "mov y, osr",
59 "jmp !x end",
60 "loop:",
61 "jmp !osre step",
62 "mov osr, y",
63 "step:",
64 "out pins, 4 [31]"
65 "jmp x-- loop",
66 "end:",
67 "irq 0 rel"
68 );
69 let pin0 = pio.make_pio_pin(pin0);
70 let pin1 = pio.make_pio_pin(pin1);
71 let pin2 = pio.make_pio_pin(pin2);
72 let pin3 = pio.make_pio_pin(pin3);
73 sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]);
74 let mut cfg = Config::default();
75 cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]);
76 cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed();
77 cfg.use_program(&pio.load_program(&prg.program), &[]);
78 sm.set_config(&cfg);
79 sm.set_enable(true);
80 Self { irq, sm }
81 }
82
83 // Set pulse frequency
84 pub fn set_frequency(&mut self, freq: u32) {
85 let clock_divider: FixedU32<U8> = (125_000_000 / (freq * 136)).to_fixed();
86 assert!(clock_divider <= 65536, "clkdiv must be <= 65536");
87 assert!(clock_divider >= 1, "clkdiv must be >= 1");
88 self.sm.set_clock_divider(clock_divider);
89 self.sm.clkdiv_restart();
90 }
91
92 // Full step, one phase
93 pub async fn step(&mut self, steps: i32) {
94 if steps > 0 {
95 self.run(steps, 0b1000_0100_0010_0001_1000_0100_0010_0001).await
96 } else {
97 self.run(-steps, 0b0001_0010_0100_1000_0001_0010_0100_1000).await
98 }
99 }
100
101 // Full step, two phase
102 pub async fn step2(&mut self, steps: i32) {
103 if steps > 0 {
104 self.run(steps, 0b1001_1100_0110_0011_1001_1100_0110_0011).await
105 } else {
106 self.run(-steps, 0b0011_0110_1100_1001_0011_0110_1100_1001).await
107 }
108 }
109
110 // Half step
111 pub async fn step_half(&mut self, steps: i32) {
112 if steps > 0 {
113 self.run(steps, 0b1001_1000_1100_0100_0110_0010_0011_0001).await
114 } else {
115 self.run(-steps, 0b0001_0011_0010_0110_0100_1100_1000_1001).await
116 }
117 }
118
119 async fn run(&mut self, steps: i32, pattern: u32) {
120 self.sm.tx().wait_push(steps as u32).await;
121 self.sm.tx().wait_push(pattern).await;
122 let drop = OnDrop::new(|| {
123 self.sm.clear_fifos();
124 unsafe {
125 self.sm.exec_instr(
126 pio::InstructionOperands::JMP {
127 address: 0,
128 condition: pio::JmpCondition::Always,
129 }
130 .encode(),
131 );
132 }
133 });
134 self.irq.wait().await;
135 drop.defuse();
136 }
137}
138
139struct OnDrop<F: FnOnce()> {
140 f: MaybeUninit<F>,
141}
142
143impl<F: FnOnce()> OnDrop<F> {
144 pub fn new(f: F) -> Self {
145 Self { f: MaybeUninit::new(f) }
146 }
147
148 pub fn defuse(self) {
149 mem::forget(self)
150 }
151}
152
153impl<F: FnOnce()> Drop for OnDrop<F> {
154 fn drop(&mut self) {
155 unsafe { self.f.as_ptr().read()() }
156 }
157}
158
159#[embassy_executor::main]
160async fn main(_spawner: Spawner) {
161 let p = embassy_rp::init(Default::default());
162 let Pio {
163 mut common, irq0, sm0, ..
164 } = Pio::new(p.PIO0, Irqs);
165
166 let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7);
167 stepper.set_frequency(120);
168 loop {
169 info!("CW full steps");
170 stepper.step(1000).await;
171
172 info!("CCW full steps, drop after 1 sec");
173 if let Err(_) = with_timeout(Duration::from_secs(1), stepper.step(i32::MIN)).await {
174 info!("Time's up!");
175 Timer::after(Duration::from_secs(1)).await;
176 }
177
178 info!("CW half steps");
179 stepper.step_half(1000).await;
180
181 info!("CCW half steps");
182 stepper.step_half(-1000).await;
183 }
184}
diff --git a/examples/rp23/src/bin/pio_ws2812.rs b/examples/rp23/src/bin/pio_ws2812.rs
new file mode 100644
index 000000000..2e62a3d97
--- /dev/null
+++ b/examples/rp23/src/bin/pio_ws2812.rs
@@ -0,0 +1,177 @@
1//! This example shows powerful PIO module in the RP2040 chip to communicate with WS2812 LED modules.
2//! See (https://www.sparkfun.com/categories/tags/ws2812)
3
4#![no_std]
5#![no_main]
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_rp::dma::{AnyChannel, Channel};
10use embassy_rp::peripherals::PIO0;
11use embassy_rp::pio::{
12 Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
13};
14use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef};
15use embassy_time::{Duration, Ticker, Timer};
16use fixed::types::U24F8;
17use fixed_macro::fixed;
18use smart_leds::RGB8;
19use {defmt_rtt as _, panic_probe as _};
20use embassy_rp::block::ImageDef;
21
22#[link_section = ".start_block"]
23#[used]
24pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
25
26// Program metadata for `picotool info`
27#[link_section = ".bi_entries"]
28#[used]
29pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
30 embassy_rp::binary_info_rp_cargo_bin_name!(),
31 embassy_rp::binary_info_rp_cargo_version!(),
32 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
33 embassy_rp::binary_info_rp_program_build_attribute!(),
34];
35
36
37bind_interrupts!(struct Irqs {
38 PIO0_IRQ_0 => InterruptHandler<PIO0>;
39});
40
41pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> {
42 dma: PeripheralRef<'d, AnyChannel>,
43 sm: StateMachine<'d, P, S>,
44}
45
46impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> {
47 pub fn new(
48 pio: &mut Common<'d, P>,
49 mut sm: StateMachine<'d, P, S>,
50 dma: impl Peripheral<P = impl Channel> + 'd,
51 pin: impl PioPin,
52 ) -> Self {
53 into_ref!(dma);
54
55 // Setup sm0
56
57 // prepare the PIO program
58 let side_set = pio::SideSet::new(false, 1, false);
59 let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set);
60
61 const T1: u8 = 2; // start bit
62 const T2: u8 = 5; // data bit
63 const T3: u8 = 3; // stop bit
64 const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32;
65
66 let mut wrap_target = a.label();
67 let mut wrap_source = a.label();
68 let mut do_zero = a.label();
69 a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0);
70 a.bind(&mut wrap_target);
71 // Do stop bit
72 a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0);
73 // Do start bit
74 a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1);
75 // Do data bit = 1
76 a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1);
77 a.bind(&mut do_zero);
78 // Do data bit = 0
79 a.nop_with_delay_and_side_set(T2 - 1, 0);
80 a.bind(&mut wrap_source);
81
82 let prg = a.assemble_with_wrap(wrap_source, wrap_target);
83 let mut cfg = Config::default();
84
85 // Pin config
86 let out_pin = pio.make_pio_pin(pin);
87 cfg.set_out_pins(&[&out_pin]);
88 cfg.set_set_pins(&[&out_pin]);
89
90 cfg.use_program(&pio.load_program(&prg), &[&out_pin]);
91
92 // Clock config, measured in kHz to avoid overflows
93 // TODO CLOCK_FREQ should come from embassy_rp
94 let clock_freq = U24F8::from_num(clocks::clk_sys_freq() / 1000);
95 let ws2812_freq = fixed!(800: U24F8);
96 let bit_freq = ws2812_freq * CYCLES_PER_BIT;
97 cfg.clock_divider = clock_freq / bit_freq;
98
99 // FIFO config
100 cfg.fifo_join = FifoJoin::TxOnly;
101 cfg.shift_out = ShiftConfig {
102 auto_fill: true,
103 threshold: 24,
104 direction: ShiftDirection::Left,
105 };
106
107 sm.set_config(&cfg);
108 sm.set_enable(true);
109
110 Self {
111 dma: dma.map_into(),
112 sm,
113 }
114 }
115
116 pub async fn write(&mut self, colors: &[RGB8; N]) {
117 // Precompute the word bytes from the colors
118 let mut words = [0u32; N];
119 for i in 0..N {
120 let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8);
121 words[i] = word;
122 }
123
124 // DMA transfer
125 self.sm.tx().dma_push(self.dma.reborrow(), &words).await;
126
127 Timer::after_micros(55).await;
128 }
129}
130
131/// Input a value 0 to 255 to get a color value
132/// The colours are a transition r - g - b - back to r.
133fn wheel(mut wheel_pos: u8) -> RGB8 {
134 wheel_pos = 255 - wheel_pos;
135 if wheel_pos < 85 {
136 return (255 - wheel_pos * 3, 0, wheel_pos * 3).into();
137 }
138 if wheel_pos < 170 {
139 wheel_pos -= 85;
140 return (0, wheel_pos * 3, 255 - wheel_pos * 3).into();
141 }
142 wheel_pos -= 170;
143 (wheel_pos * 3, 255 - wheel_pos * 3, 0).into()
144}
145
146#[embassy_executor::main]
147async fn main(_spawner: Spawner) {
148 info!("Start");
149 let p = embassy_rp::init(Default::default());
150
151 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
152
153 // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit
154 // feather boards for the 2040 both have one built in.
155 const NUM_LEDS: usize = 1;
156 let mut data = [RGB8::default(); NUM_LEDS];
157
158 // Common neopixel pins:
159 // Thing plus: 8
160 // Adafruit Feather: 16; Adafruit Feather+RFM95: 4
161 let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16);
162
163 // Loop forever making RGB values and pushing them out to the WS2812.
164 let mut ticker = Ticker::every(Duration::from_millis(10));
165 loop {
166 for j in 0..(256 * 5) {
167 debug!("New Colors:");
168 for i in 0..NUM_LEDS {
169 data[i] = wheel((((i * 256) as u16 / NUM_LEDS as u16 + j as u16) & 255) as u8);
170 debug!("R: {} G: {} B: {}", data[i].r, data[i].g, data[i].b);
171 }
172 ws2812.write(&data).await;
173
174 ticker.next().await;
175 }
176 }
177}
diff --git a/examples/rp23/src/bin/pwm.rs b/examples/rp23/src/bin/pwm.rs
new file mode 100644
index 000000000..ab38a03bb
--- /dev/null
+++ b/examples/rp23/src/bin/pwm.rs
@@ -0,0 +1,45 @@
1//! This example shows how to use PWM (Pulse Width Modulation) in the RP2040 chip.
2//!
3//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::pwm::{Config, Pwm};
11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _};
13use embassy_rp::block::ImageDef;
14
15#[link_section = ".start_block"]
16#[used]
17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
18
19// Program metadata for `picotool info`
20#[link_section = ".bi_entries"]
21#[used]
22pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
23 embassy_rp::binary_info_rp_cargo_bin_name!(),
24 embassy_rp::binary_info_rp_cargo_version!(),
25 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
26 embassy_rp::binary_info_rp_program_build_attribute!(),
27];
28
29
30#[embassy_executor::main]
31async fn main(_spawner: Spawner) {
32 let p = embassy_rp::init(Default::default());
33
34 let mut c: Config = Default::default();
35 c.top = 0x8000;
36 c.compare_b = 8;
37 let mut pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, c.clone());
38
39 loop {
40 info!("current LED duty cycle: {}/32768", c.compare_b);
41 Timer::after_secs(1).await;
42 c.compare_b = c.compare_b.rotate_left(4);
43 pwm.set_config(&c);
44 }
45}
diff --git a/examples/rp23/src/bin/pwm_input.rs b/examples/rp23/src/bin/pwm_input.rs
new file mode 100644
index 000000000..fcb561cfd
--- /dev/null
+++ b/examples/rp23/src/bin/pwm_input.rs
@@ -0,0 +1,42 @@
1//! This example shows how to use the PWM module to measure the frequency of an input signal.
2
3#![no_std]
4#![no_main]
5
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_rp::gpio::Pull;
9use embassy_rp::pwm::{Config, InputMode, Pwm};
10use embassy_time::{Duration, Ticker};
11use {defmt_rtt as _, panic_probe as _};
12use embassy_rp::block::ImageDef;
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28
29#[embassy_executor::main]
30async fn main(_spawner: Spawner) {
31 let p = embassy_rp::init(Default::default());
32
33 let cfg: Config = Default::default();
34 let pwm = Pwm::new_input(p.PWM_SLICE2, p.PIN_5, Pull::None, InputMode::RisingEdge, cfg);
35
36 let mut ticker = Ticker::every(Duration::from_secs(1));
37 loop {
38 info!("Input frequency: {} Hz", pwm.counter());
39 pwm.set_counter(0);
40 ticker.next().await;
41 }
42}
diff --git a/examples/rp23/src/bin/rosc.rs b/examples/rp23/src/bin/rosc.rs
new file mode 100644
index 000000000..051b4710f
--- /dev/null
+++ b/examples/rp23/src/bin/rosc.rs
@@ -0,0 +1,47 @@
1//! This example test the RP Pico on board LED.
2//!
3//! It does not work with the RP Pico W board. See wifi_blinky.rs.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::{clocks, gpio};
11use embassy_time::Timer;
12use gpio::{Level, Output};
13use {defmt_rtt as _, panic_probe as _};
14use embassy_rp::block::ImageDef;
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30
31#[embassy_executor::main]
32async fn main(_spawner: Spawner) {
33 let mut config = embassy_rp::config::Config::default();
34 config.clocks = clocks::ClockConfig::rosc();
35 let p = embassy_rp::init(config);
36 let mut led = Output::new(p.PIN_25, Level::Low);
37
38 loop {
39 info!("led on!");
40 led.set_high();
41 Timer::after_secs(1).await;
42
43 info!("led off!");
44 led.set_low();
45 Timer::after_secs(1).await;
46 }
47}
diff --git a/examples/rp23/src/bin/shared_bus.rs b/examples/rp23/src/bin/shared_bus.rs
new file mode 100644
index 000000000..e3213cd91
--- /dev/null
+++ b/examples/rp23/src/bin/shared_bus.rs
@@ -0,0 +1,131 @@
1//! This example shows how to share (async) I2C and SPI buses between multiple devices.
2
3#![no_std]
4#![no_main]
5
6use defmt::*;
7use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice;
8use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
9use embassy_executor::Spawner;
10use embassy_rp::bind_interrupts;
11use embassy_rp::gpio::{AnyPin, Level, Output};
12use embassy_rp::i2c::{self, I2c, InterruptHandler};
13use embassy_rp::peripherals::{I2C1, SPI1};
14use embassy_rp::spi::{self, Spi};
15use embassy_sync::blocking_mutex::raw::NoopRawMutex;
16use embassy_sync::mutex::Mutex;
17use embassy_time::Timer;
18use static_cell::StaticCell;
19use {defmt_rtt as _, panic_probe as _};
20use embassy_rp::block::ImageDef;
21
22#[link_section = ".start_block"]
23#[used]
24pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
25
26// Program metadata for `picotool info`
27#[link_section = ".bi_entries"]
28#[used]
29pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
30 embassy_rp::binary_info_rp_cargo_bin_name!(),
31 embassy_rp::binary_info_rp_cargo_version!(),
32 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
33 embassy_rp::binary_info_rp_program_build_attribute!(),
34];
35
36
37type Spi1Bus = Mutex<NoopRawMutex, Spi<'static, SPI1, spi::Async>>;
38type I2c1Bus = Mutex<NoopRawMutex, I2c<'static, I2C1, i2c::Async>>;
39
40bind_interrupts!(struct Irqs {
41 I2C1_IRQ => InterruptHandler<I2C1>;
42});
43
44#[embassy_executor::main]
45async fn main(spawner: Spawner) {
46 let p = embassy_rp::init(Default::default());
47 info!("Here we go!");
48
49 // Shared I2C bus
50 let i2c = I2c::new_async(p.I2C1, p.PIN_15, p.PIN_14, Irqs, i2c::Config::default());
51 static I2C_BUS: StaticCell<I2c1Bus> = StaticCell::new();
52 let i2c_bus = I2C_BUS.init(Mutex::new(i2c));
53
54 spawner.must_spawn(i2c_task_a(i2c_bus));
55 spawner.must_spawn(i2c_task_b(i2c_bus));
56
57 // Shared SPI bus
58 let spi_cfg = spi::Config::default();
59 let spi = Spi::new(p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, p.DMA_CH0, p.DMA_CH1, spi_cfg);
60 static SPI_BUS: StaticCell<Spi1Bus> = StaticCell::new();
61 let spi_bus = SPI_BUS.init(Mutex::new(spi));
62
63 // Chip select pins for the SPI devices
64 let cs_a = Output::new(AnyPin::from(p.PIN_0), Level::High);
65 let cs_b = Output::new(AnyPin::from(p.PIN_1), Level::High);
66
67 spawner.must_spawn(spi_task_a(spi_bus, cs_a));
68 spawner.must_spawn(spi_task_b(spi_bus, cs_b));
69}
70
71#[embassy_executor::task]
72async fn i2c_task_a(i2c_bus: &'static I2c1Bus) {
73 let i2c_dev = I2cDevice::new(i2c_bus);
74 let _sensor = DummyI2cDeviceDriver::new(i2c_dev, 0xC0);
75 loop {
76 info!("i2c task A");
77 Timer::after_secs(1).await;
78 }
79}
80
81#[embassy_executor::task]
82async fn i2c_task_b(i2c_bus: &'static I2c1Bus) {
83 let i2c_dev = I2cDevice::new(i2c_bus);
84 let _sensor = DummyI2cDeviceDriver::new(i2c_dev, 0xDE);
85 loop {
86 info!("i2c task B");
87 Timer::after_secs(1).await;
88 }
89}
90
91#[embassy_executor::task]
92async fn spi_task_a(spi_bus: &'static Spi1Bus, cs: Output<'static>) {
93 let spi_dev = SpiDevice::new(spi_bus, cs);
94 let _sensor = DummySpiDeviceDriver::new(spi_dev);
95 loop {
96 info!("spi task A");
97 Timer::after_secs(1).await;
98 }
99}
100
101#[embassy_executor::task]
102async fn spi_task_b(spi_bus: &'static Spi1Bus, cs: Output<'static>) {
103 let spi_dev = SpiDevice::new(spi_bus, cs);
104 let _sensor = DummySpiDeviceDriver::new(spi_dev);
105 loop {
106 info!("spi task B");
107 Timer::after_secs(1).await;
108 }
109}
110
111// Dummy I2C device driver, using `embedded-hal-async`
112struct DummyI2cDeviceDriver<I2C: embedded_hal_async::i2c::I2c> {
113 _i2c: I2C,
114}
115
116impl<I2C: embedded_hal_async::i2c::I2c> DummyI2cDeviceDriver<I2C> {
117 fn new(i2c_dev: I2C, _address: u8) -> Self {
118 Self { _i2c: i2c_dev }
119 }
120}
121
122// Dummy SPI device driver, using `embedded-hal-async`
123struct DummySpiDeviceDriver<SPI: embedded_hal_async::spi::SpiDevice> {
124 _spi: SPI,
125}
126
127impl<SPI: embedded_hal_async::spi::SpiDevice> DummySpiDeviceDriver<SPI> {
128 fn new(spi_dev: SPI) -> Self {
129 Self { _spi: spi_dev }
130 }
131}
diff --git a/examples/rp23/src/bin/sharing.rs b/examples/rp23/src/bin/sharing.rs
new file mode 100644
index 000000000..10f064947
--- /dev/null
+++ b/examples/rp23/src/bin/sharing.rs
@@ -0,0 +1,166 @@
1//! This example shows some common strategies for sharing resources between tasks.
2//!
3//! We demonstrate five different ways of sharing, covering different use cases:
4//! - Atomics: This method is used for simple values, such as bool and u8..u32
5//! - Blocking Mutex: This is used for sharing non-async things, using Cell/RefCell for interior mutability.
6//! - Async Mutex: This is used for sharing async resources, where you need to hold the lock across await points.
7//! The async Mutex has interior mutability built-in, so no RefCell is needed.
8//! - Cell: For sharing Copy types between tasks running on the same executor.
9//! - RefCell: When you want &mut access to a value shared between tasks running on the same executor.
10//!
11//! More information: https://embassy.dev/book/#_sharing_peripherals_between_tasks
12
13#![no_std]
14#![no_main]
15
16use core::cell::{Cell, RefCell};
17use core::sync::atomic::{AtomicU32, Ordering};
18
19use cortex_m_rt::entry;
20use defmt::info;
21use embassy_executor::{Executor, InterruptExecutor};
22use embassy_rp::clocks::RoscRng;
23use embassy_rp::interrupt::{Priority, InterruptExt};
24use embassy_rp::peripherals::UART0;
25use embassy_rp::uart::{self, InterruptHandler, UartTx};
26use embassy_rp::{bind_interrupts, interrupt};
27use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
28use embassy_sync::{blocking_mutex, mutex};
29use embassy_time::{Duration, Ticker};
30use rand::RngCore;
31use static_cell::{ConstStaticCell, StaticCell};
32use {defmt_rtt as _, panic_probe as _};
33use embassy_rp::block::ImageDef;
34
35#[link_section = ".start_block"]
36#[used]
37pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
38
39// Program metadata for `picotool info`
40#[link_section = ".bi_entries"]
41#[used]
42pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
43 embassy_rp::binary_info_rp_cargo_bin_name!(),
44 embassy_rp::binary_info_rp_cargo_version!(),
45 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
46 embassy_rp::binary_info_rp_program_build_attribute!(),
47];
48
49
50type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, UART0, uart::Async>>;
51
52struct MyType {
53 inner: u32,
54}
55
56static EXECUTOR_HI: InterruptExecutor = InterruptExecutor::new();
57static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();
58
59// Use Atomics for simple values
60static ATOMIC: AtomicU32 = AtomicU32::new(0);
61
62// Use blocking Mutex with Cell/RefCell for sharing non-async things
63static MUTEX_BLOCKING: blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<MyType>> =
64 blocking_mutex::Mutex::new(RefCell::new(MyType { inner: 0 }));
65
66bind_interrupts!(struct Irqs {
67 UART0_IRQ => InterruptHandler<UART0>;
68});
69
70#[interrupt]
71unsafe fn SWI_IRQ_0() {
72 EXECUTOR_HI.on_interrupt()
73}
74
75#[entry]
76fn main() -> ! {
77 let p = embassy_rp::init(Default::default());
78 info!("Here we go!");
79
80 let uart = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, uart::Config::default());
81 // Use the async Mutex for sharing async things (built-in interior mutability)
82 static UART: StaticCell<UartAsyncMutex> = StaticCell::new();
83 let uart = UART.init(mutex::Mutex::new(uart));
84
85 // High-priority executor: runs in interrupt mode
86 interrupt::SWI_IRQ_0.set_priority(Priority::P3);
87 let spawner = EXECUTOR_HI.start(interrupt::SWI_IRQ_0);
88 spawner.must_spawn(task_a(uart));
89
90 // Low priority executor: runs in thread mode
91 let executor = EXECUTOR_LOW.init(Executor::new());
92 executor.run(|spawner| {
93 // No Mutex needed when sharing between tasks running on the same executor
94
95 // Use Cell for Copy-types
96 static CELL: ConstStaticCell<Cell<[u8; 4]>> = ConstStaticCell::new(Cell::new([0; 4]));
97 let cell = CELL.take();
98
99 // Use RefCell for &mut access
100 static REF_CELL: ConstStaticCell<RefCell<MyType>> = ConstStaticCell::new(RefCell::new(MyType { inner: 0 }));
101 let ref_cell = REF_CELL.take();
102
103 spawner.must_spawn(task_b(uart, cell, ref_cell));
104 spawner.must_spawn(task_c(cell, ref_cell));
105 });
106}
107
108#[embassy_executor::task]
109async fn task_a(uart: &'static UartAsyncMutex) {
110 let mut ticker = Ticker::every(Duration::from_secs(1));
111 loop {
112 let random = RoscRng.next_u32();
113
114 {
115 let mut uart = uart.lock().await;
116 uart.write(b"task a").await.unwrap();
117 // The uart lock is released when it goes out of scope
118 }
119
120 ATOMIC.store(random, Ordering::Relaxed);
121
122 MUTEX_BLOCKING.lock(|x| x.borrow_mut().inner = random);
123
124 ticker.next().await;
125 }
126}
127
128#[embassy_executor::task]
129async fn task_b(uart: &'static UartAsyncMutex, cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {
130 let mut ticker = Ticker::every(Duration::from_secs(1));
131 loop {
132 let random = RoscRng.next_u32();
133
134 uart.lock().await.write(b"task b").await.unwrap();
135
136 cell.set(random.to_be_bytes());
137
138 ref_cell.borrow_mut().inner = random;
139
140 ticker.next().await;
141 }
142}
143
144#[embassy_executor::task]
145async fn task_c(cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {
146 let mut ticker = Ticker::every(Duration::from_secs(1));
147 loop {
148 info!("=======================");
149
150 let atomic_val = ATOMIC.load(Ordering::Relaxed);
151 info!("atomic: {}", atomic_val);
152
153 MUTEX_BLOCKING.lock(|x| {
154 let val = x.borrow().inner;
155 info!("blocking mutex: {}", val);
156 });
157
158 let cell_val = cell.get();
159 info!("cell: {:?}", cell_val);
160
161 let ref_cell_val = ref_cell.borrow().inner;
162 info!("ref_cell: {:?}", ref_cell_val);
163
164 ticker.next().await;
165 }
166}
diff --git a/examples/rp23/src/bin/spi.rs b/examples/rp23/src/bin/spi.rs
new file mode 100644
index 000000000..bcf356188
--- /dev/null
+++ b/examples/rp23/src/bin/spi.rs
@@ -0,0 +1,62 @@
1//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.
2//!
3//! Example for resistive touch sensor in Waveshare Pico-ResTouch
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::spi::Spi;
11use embassy_rp::{gpio, spi};
12use gpio::{Level, Output};
13use {defmt_rtt as _, panic_probe as _};
14use embassy_rp::block::ImageDef;
15
16#[link_section = ".start_block"]
17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info_rp_cargo_bin_name!(),
25 embassy_rp::binary_info_rp_cargo_version!(),
26 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info_rp_program_build_attribute!(),
28];
29
30
31#[embassy_executor::main]
32async fn main(_spawner: Spawner) {
33 let p = embassy_rp::init(Default::default());
34 info!("Hello World!");
35
36 // Example for resistive touch sensor in Waveshare Pico-ResTouch
37
38 let miso = p.PIN_12;
39 let mosi = p.PIN_11;
40 let clk = p.PIN_10;
41 let touch_cs = p.PIN_16;
42
43 // create SPI
44 let mut config = spi::Config::default();
45 config.frequency = 2_000_000;
46 let mut spi = Spi::new_blocking(p.SPI1, clk, mosi, miso, config);
47
48 // Configure CS
49 let mut cs = Output::new(touch_cs, Level::Low);
50
51 loop {
52 cs.set_low();
53 let mut buf = [0x90, 0x00, 0x00, 0xd0, 0x00, 0x00];
54 spi.blocking_transfer_in_place(&mut buf).unwrap();
55 cs.set_high();
56
57 let x = (buf[1] as u32) << 5 | (buf[2] as u32) >> 3;
58 let y = (buf[4] as u32) << 5 | (buf[5] as u32) >> 3;
59
60 info!("touch: {=u32} {=u32}", x, y);
61 }
62}
diff --git a/examples/rp23/src/bin/spi_async.rs b/examples/rp23/src/bin/spi_async.rs
new file mode 100644
index 000000000..7a43995d2
--- /dev/null
+++ b/examples/rp23/src/bin/spi_async.rs
@@ -0,0 +1,47 @@
1//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.
2//! No specific hardware is specified in this example. If you connect pin 11 and 12 you should get the same data back.
3
4#![no_std]
5#![no_main]
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_rp::spi::{Config, Spi};
10use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _};
12use embassy_rp::block::ImageDef;
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28
29#[embassy_executor::main]
30async fn main(_spawner: Spawner) {
31 let p = embassy_rp::init(Default::default());
32 info!("Hello World!");
33
34 let miso = p.PIN_12;
35 let mosi = p.PIN_11;
36 let clk = p.PIN_10;
37
38 let mut spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default());
39
40 loop {
41 let tx_buf = [1_u8, 2, 3, 4, 5, 6];
42 let mut rx_buf = [0_u8; 6];
43 spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();
44 info!("{:?}", rx_buf);
45 Timer::after_secs(1).await;
46 }
47}
diff --git a/examples/rp23/src/bin/spi_display.rs b/examples/rp23/src/bin/spi_display.rs
new file mode 100644
index 000000000..25368bb94
--- /dev/null
+++ b/examples/rp23/src/bin/spi_display.rs
@@ -0,0 +1,328 @@
1//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.
2//!
3//! Example written for a display using the ST7789 chip. Possibly the Waveshare Pico-ResTouch
4//! (https://www.waveshare.com/wiki/Pico-ResTouch-LCD-2.8)
5
6#![no_std]
7#![no_main]
8
9use core::cell::RefCell;
10
11use defmt::*;
12use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig;
13use embassy_executor::Spawner;
14use embassy_rp::gpio::{Level, Output};
15use embassy_rp::spi;
16use embassy_rp::spi::{Blocking, Spi};
17use embassy_sync::blocking_mutex::raw::NoopRawMutex;
18use embassy_sync::blocking_mutex::Mutex;
19use embassy_time::Delay;
20use embedded_graphics::image::{Image, ImageRawLE};
21use embedded_graphics::mono_font::ascii::FONT_10X20;
22use embedded_graphics::mono_font::MonoTextStyle;
23use embedded_graphics::pixelcolor::Rgb565;
24use embedded_graphics::prelude::*;
25use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle};
26use embedded_graphics::text::Text;
27use st7789::{Orientation, ST7789};
28use {defmt_rtt as _, panic_probe as _};
29use embassy_rp::block::ImageDef;
30
31#[link_section = ".start_block"]
32#[used]
33pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
34
35// Program metadata for `picotool info`
36#[link_section = ".bi_entries"]
37#[used]
38pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
39 embassy_rp::binary_info_rp_cargo_bin_name!(),
40 embassy_rp::binary_info_rp_cargo_version!(),
41 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
42 embassy_rp::binary_info_rp_program_build_attribute!(),
43];
44
45
46use crate::my_display_interface::SPIDeviceInterface;
47use crate::touch::Touch;
48
49const DISPLAY_FREQ: u32 = 64_000_000;
50const TOUCH_FREQ: u32 = 200_000;
51
52#[embassy_executor::main]
53async fn main(_spawner: Spawner) {
54 let p = embassy_rp::init(Default::default());
55 info!("Hello World!");
56
57 let bl = p.PIN_13;
58 let rst = p.PIN_15;
59 let display_cs = p.PIN_9;
60 let dcx = p.PIN_8;
61 let miso = p.PIN_12;
62 let mosi = p.PIN_11;
63 let clk = p.PIN_10;
64 let touch_cs = p.PIN_16;
65 //let touch_irq = p.PIN_17;
66
67 // create SPI
68 let mut display_config = spi::Config::default();
69 display_config.frequency = DISPLAY_FREQ;
70 display_config.phase = spi::Phase::CaptureOnSecondTransition;
71 display_config.polarity = spi::Polarity::IdleHigh;
72 let mut touch_config = spi::Config::default();
73 touch_config.frequency = TOUCH_FREQ;
74 touch_config.phase = spi::Phase::CaptureOnSecondTransition;
75 touch_config.polarity = spi::Polarity::IdleHigh;
76
77 let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone());
78 let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi));
79
80 let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config);
81 let touch_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(touch_cs, Level::High), touch_config);
82
83 let mut touch = Touch::new(touch_spi);
84
85 let dcx = Output::new(dcx, Level::Low);
86 let rst = Output::new(rst, Level::Low);
87 // dcx: 0 = command, 1 = data
88
89 // Enable LCD backlight
90 let _bl = Output::new(bl, Level::High);
91
92 // display interface abstraction from SPI and DC
93 let di = SPIDeviceInterface::new(display_spi, dcx);
94
95 // create driver
96 let mut display = ST7789::new(di, rst, 240, 320);
97
98 // initialize
99 display.init(&mut Delay).unwrap();
100
101 // set default orientation
102 display.set_orientation(Orientation::Landscape).unwrap();
103
104 display.clear(Rgb565::BLACK).unwrap();
105
106 let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86);
107 let ferris = Image::new(&raw_image_data, Point::new(34, 68));
108
109 // Display the image
110 ferris.draw(&mut display).unwrap();
111
112 let style = MonoTextStyle::new(&FONT_10X20, Rgb565::GREEN);
113 Text::new(
114 "Hello embedded_graphics \n + embassy + RP2040!",
115 Point::new(20, 200),
116 style,
117 )
118 .draw(&mut display)
119 .unwrap();
120
121 loop {
122 if let Some((x, y)) = touch.read() {
123 let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::BLUE).build();
124
125 Rectangle::new(Point::new(x - 1, y - 1), Size::new(3, 3))
126 .into_styled(style)
127 .draw(&mut display)
128 .unwrap();
129 }
130 }
131}
132
133/// Driver for the XPT2046 resistive touchscreen sensor
134mod touch {
135 use embedded_hal_1::spi::{Operation, SpiDevice};
136
137 struct Calibration {
138 x1: i32,
139 x2: i32,
140 y1: i32,
141 y2: i32,
142 sx: i32,
143 sy: i32,
144 }
145
146 const CALIBRATION: Calibration = Calibration {
147 x1: 3880,
148 x2: 340,
149 y1: 262,
150 y2: 3850,
151 sx: 320,
152 sy: 240,
153 };
154
155 pub struct Touch<SPI: SpiDevice> {
156 spi: SPI,
157 }
158
159 impl<SPI> Touch<SPI>
160 where
161 SPI: SpiDevice,
162 {
163 pub fn new(spi: SPI) -> Self {
164 Self { spi }
165 }
166
167 pub fn read(&mut self) -> Option<(i32, i32)> {
168 let mut x = [0; 2];
169 let mut y = [0; 2];
170 self.spi
171 .transaction(&mut [
172 Operation::Write(&[0x90]),
173 Operation::Read(&mut x),
174 Operation::Write(&[0xd0]),
175 Operation::Read(&mut y),
176 ])
177 .unwrap();
178
179 let x = (u16::from_be_bytes(x) >> 3) as i32;
180 let y = (u16::from_be_bytes(y) >> 3) as i32;
181
182 let cal = &CALIBRATION;
183
184 let x = ((x - cal.x1) * cal.sx / (cal.x2 - cal.x1)).clamp(0, cal.sx);
185 let y = ((y - cal.y1) * cal.sy / (cal.y2 - cal.y1)).clamp(0, cal.sy);
186 if x == 0 && y == 0 {
187 None
188 } else {
189 Some((x, y))
190 }
191 }
192 }
193}
194
195mod my_display_interface {
196 use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand};
197 use embedded_hal_1::digital::OutputPin;
198 use embedded_hal_1::spi::SpiDevice;
199
200 /// SPI display interface.
201 ///
202 /// This combines the SPI peripheral and a data/command pin
203 pub struct SPIDeviceInterface<SPI, DC> {
204 spi: SPI,
205 dc: DC,
206 }
207
208 impl<SPI, DC> SPIDeviceInterface<SPI, DC>
209 where
210 SPI: SpiDevice,
211 DC: OutputPin,
212 {
213 /// Create new SPI interface for communciation with a display driver
214 pub fn new(spi: SPI, dc: DC) -> Self {
215 Self { spi, dc }
216 }
217 }
218
219 impl<SPI, DC> WriteOnlyDataCommand for SPIDeviceInterface<SPI, DC>
220 where
221 SPI: SpiDevice,
222 DC: OutputPin,
223 {
224 fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> {
225 // 1 = data, 0 = command
226 self.dc.set_low().map_err(|_| DisplayError::DCError)?;
227
228 send_u8(&mut self.spi, cmds).map_err(|_| DisplayError::BusWriteError)?;
229 Ok(())
230 }
231
232 fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> {
233 // 1 = data, 0 = command
234 self.dc.set_high().map_err(|_| DisplayError::DCError)?;
235
236 send_u8(&mut self.spi, buf).map_err(|_| DisplayError::BusWriteError)?;
237 Ok(())
238 }
239 }
240
241 fn send_u8<T: SpiDevice>(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> {
242 match words {
243 DataFormat::U8(slice) => spi.write(slice),
244 DataFormat::U16(slice) => {
245 use byte_slice_cast::*;
246 spi.write(slice.as_byte_slice())
247 }
248 DataFormat::U16LE(slice) => {
249 use byte_slice_cast::*;
250 for v in slice.as_mut() {
251 *v = v.to_le();
252 }
253 spi.write(slice.as_byte_slice())
254 }
255 DataFormat::U16BE(slice) => {
256 use byte_slice_cast::*;
257 for v in slice.as_mut() {
258 *v = v.to_be();
259 }
260 spi.write(slice.as_byte_slice())
261 }
262 DataFormat::U8Iter(iter) => {
263 let mut buf = [0; 32];
264 let mut i = 0;
265
266 for v in iter.into_iter() {
267 buf[i] = v;
268 i += 1;
269
270 if i == buf.len() {
271 spi.write(&buf)?;
272 i = 0;
273 }
274 }
275
276 if i > 0 {
277 spi.write(&buf[..i])?;
278 }
279
280 Ok(())
281 }
282 DataFormat::U16LEIter(iter) => {
283 use byte_slice_cast::*;
284 let mut buf = [0; 32];
285 let mut i = 0;
286
287 for v in iter.map(u16::to_le) {
288 buf[i] = v;
289 i += 1;
290
291 if i == buf.len() {
292 spi.write(&buf.as_byte_slice())?;
293 i = 0;
294 }
295 }
296
297 if i > 0 {
298 spi.write(&buf[..i].as_byte_slice())?;
299 }
300
301 Ok(())
302 }
303 DataFormat::U16BEIter(iter) => {
304 use byte_slice_cast::*;
305 let mut buf = [0; 64];
306 let mut i = 0;
307 let len = buf.len();
308
309 for v in iter.map(u16::to_be) {
310 buf[i] = v;
311 i += 1;
312
313 if i == len {
314 spi.write(&buf.as_byte_slice())?;
315 i = 0;
316 }
317 }
318
319 if i > 0 {
320 spi.write(&buf[..i].as_byte_slice())?;
321 }
322
323 Ok(())
324 }
325 _ => unimplemented!(),
326 }
327 }
328}
diff --git a/examples/rp23/src/bin/spi_sdmmc.rs b/examples/rp23/src/bin/spi_sdmmc.rs
new file mode 100644
index 000000000..992215b0d
--- /dev/null
+++ b/examples/rp23/src/bin/spi_sdmmc.rs
@@ -0,0 +1,99 @@
1//! This example shows how to use `embedded-sdmmc` with the RP2040 chip, over SPI.
2//!
3//! The example will attempt to read a file `MY_FILE.TXT` from the root directory
4//! of the SD card and print its contents.
5
6#![no_std]
7#![no_main]
8
9use defmt::*;
10use embassy_embedded_hal::SetConfig;
11use embassy_executor::Spawner;
12use embassy_rp::spi::Spi;
13use embassy_rp::{gpio, spi};
14use embedded_hal_bus::spi::ExclusiveDevice;
15use embedded_sdmmc::sdcard::{DummyCsPin, SdCard};
16use gpio::{Level, Output};
17use {defmt_rtt as _, panic_probe as _};
18use embassy_rp::block::ImageDef;
19
20#[link_section = ".start_block"]
21#[used]
22pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
23
24// Program metadata for `picotool info`
25#[link_section = ".bi_entries"]
26#[used]
27pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
28 embassy_rp::binary_info_rp_cargo_bin_name!(),
29 embassy_rp::binary_info_rp_cargo_version!(),
30 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
31 embassy_rp::binary_info_rp_program_build_attribute!(),
32];
33
34
35struct DummyTimesource();
36
37impl embedded_sdmmc::TimeSource for DummyTimesource {
38 fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {
39 embedded_sdmmc::Timestamp {
40 year_since_1970: 0,
41 zero_indexed_month: 0,
42 zero_indexed_day: 0,
43 hours: 0,
44 minutes: 0,
45 seconds: 0,
46 }
47 }
48}
49
50#[embassy_executor::main]
51async fn main(_spawner: Spawner) {
52 embassy_rp::pac::SIO.spinlock(31).write_value(1);
53 let p = embassy_rp::init(Default::default());
54
55 // SPI clock needs to be running at <= 400kHz during initialization
56 let mut config = spi::Config::default();
57 config.frequency = 400_000;
58 let spi = Spi::new_blocking(p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, config);
59 // Use a dummy cs pin here, for embedded-hal SpiDevice compatibility reasons
60 let spi_dev = ExclusiveDevice::new_no_delay(spi, DummyCsPin);
61 // Real cs pin
62 let cs = Output::new(p.PIN_16, Level::High);
63
64 let sdcard = SdCard::new(spi_dev, cs, embassy_time::Delay);
65 info!("Card size is {} bytes", sdcard.num_bytes().unwrap());
66
67 // Now that the card is initialized, the SPI clock can go faster
68 let mut config = spi::Config::default();
69 config.frequency = 16_000_000;
70 sdcard.spi(|dev| dev.bus_mut().set_config(&config)).ok();
71
72 // Now let's look for volumes (also known as partitions) on our block device.
73 // To do this we need a Volume Manager. It will take ownership of the block device.
74 let mut volume_mgr = embedded_sdmmc::VolumeManager::new(sdcard, DummyTimesource());
75
76 // Try and access Volume 0 (i.e. the first partition).
77 // The volume object holds information about the filesystem on that volume.
78 let mut volume0 = volume_mgr.open_volume(embedded_sdmmc::VolumeIdx(0)).unwrap();
79 info!("Volume 0: {:?}", defmt::Debug2Format(&volume0));
80
81 // Open the root directory (mutably borrows from the volume).
82 let mut root_dir = volume0.open_root_dir().unwrap();
83
84 // Open a file called "MY_FILE.TXT" in the root directory
85 // This mutably borrows the directory.
86 let mut my_file = root_dir
87 .open_file_in_dir("MY_FILE.TXT", embedded_sdmmc::Mode::ReadOnly)
88 .unwrap();
89
90 // Print the contents of the file
91 while !my_file.is_eof() {
92 let mut buf = [0u8; 32];
93 if let Ok(n) = my_file.read(&mut buf) {
94 info!("{:a}", buf[..n]);
95 }
96 }
97
98 loop {}
99}
diff --git a/examples/rp23/src/bin/uart.rs b/examples/rp23/src/bin/uart.rs
new file mode 100644
index 000000000..7b82fa350
--- /dev/null
+++ b/examples/rp23/src/bin/uart.rs
@@ -0,0 +1,40 @@
1//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip.
2//!
3//! No specific hardware is specified in this example. Only output on pin 0 is tested.
4//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used
5//! with its UART port.
6
7#![no_std]
8#![no_main]
9
10use embassy_executor::Spawner;
11use embassy_rp::uart;
12use {defmt_rtt as _, panic_probe as _};
13use embassy_rp::block::ImageDef;
14
15#[link_section = ".start_block"]
16#[used]
17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
18
19// Program metadata for `picotool info`
20#[link_section = ".bi_entries"]
21#[used]
22pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
23 embassy_rp::binary_info_rp_cargo_bin_name!(),
24 embassy_rp::binary_info_rp_cargo_version!(),
25 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
26 embassy_rp::binary_info_rp_program_build_attribute!(),
27];
28
29#[embassy_executor::main]
30async fn main(_spawner: Spawner) {
31 let p = embassy_rp::init(Default::default());
32 let config = uart::Config::default();
33 let mut uart = uart::Uart::new_blocking(p.UART1, p.PIN_4, p.PIN_5, config);
34 uart.blocking_write("Hello World!\r\n".as_bytes()).unwrap();
35
36 loop {
37 uart.blocking_write("hello there!\r\n".as_bytes()).unwrap();
38 cortex_m::asm::delay(1_000_000);
39 }
40}
diff --git a/examples/rp23/src/bin/uart_buffered_split.rs b/examples/rp23/src/bin/uart_buffered_split.rs
new file mode 100644
index 000000000..f7acdade9
--- /dev/null
+++ b/examples/rp23/src/bin/uart_buffered_split.rs
@@ -0,0 +1,74 @@
1//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip.
2//!
3//! No specific hardware is specified in this example. If you connect pin 0 and 1 you should get the same data back.
4//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used
5//! with its UART port.
6
7#![no_std]
8#![no_main]
9
10use defmt::*;
11use embassy_executor::Spawner;
12use embassy_rp::bind_interrupts;
13use embassy_rp::peripherals::UART0;
14use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config};
15use embassy_time::Timer;
16use embedded_io_async::{Read, Write};
17use static_cell::StaticCell;
18use {defmt_rtt as _, panic_probe as _};
19use embassy_rp::block::ImageDef;
20
21#[link_section = ".start_block"]
22#[used]
23pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
24
25// Program metadata for `picotool info`
26#[link_section = ".bi_entries"]
27#[used]
28pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
29 embassy_rp::binary_info_rp_cargo_bin_name!(),
30 embassy_rp::binary_info_rp_cargo_version!(),
31 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
32 embassy_rp::binary_info_rp_program_build_attribute!(),
33];
34
35
36bind_interrupts!(struct Irqs {
37 UART0_IRQ => BufferedInterruptHandler<UART0>;
38});
39
40#[embassy_executor::main]
41async fn main(spawner: Spawner) {
42 let p = embassy_rp::init(Default::default());
43 let (tx_pin, rx_pin, uart) = (p.PIN_0, p.PIN_1, p.UART0);
44
45 static TX_BUF: StaticCell<[u8; 16]> = StaticCell::new();
46 let tx_buf = &mut TX_BUF.init([0; 16])[..];
47 static RX_BUF: StaticCell<[u8; 16]> = StaticCell::new();
48 let rx_buf = &mut RX_BUF.init([0; 16])[..];
49 let uart = BufferedUart::new(uart, Irqs, tx_pin, rx_pin, tx_buf, rx_buf, Config::default());
50 let (mut tx, rx) = uart.split();
51
52 unwrap!(spawner.spawn(reader(rx)));
53
54 info!("Writing...");
55 loop {
56 let data = [
57 1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
58 29, 30, 31,
59 ];
60 info!("TX {:?}", data);
61 tx.write_all(&data).await.unwrap();
62 Timer::after_secs(1).await;
63 }
64}
65
66#[embassy_executor::task]
67async fn reader(mut rx: BufferedUartRx<'static, UART0>) {
68 info!("Reading...");
69 loop {
70 let mut buf = [0; 31];
71 rx.read_exact(&mut buf).await.unwrap();
72 info!("RX {:?}", buf);
73 }
74}
diff --git a/examples/rp23/src/bin/uart_r503.rs b/examples/rp23/src/bin/uart_r503.rs
new file mode 100644
index 000000000..69f6dbbff
--- /dev/null
+++ b/examples/rp23/src/bin/uart_r503.rs
@@ -0,0 +1,174 @@
1#![no_std]
2#![no_main]
3
4use defmt::{debug, error, info};
5use embassy_executor::Spawner;
6use embassy_rp::bind_interrupts;
7use embassy_rp::peripherals::UART0;
8use embassy_rp::uart::{Config, DataBits, InterruptHandler as UARTInterruptHandler, Parity, StopBits, Uart};
9use embassy_time::{with_timeout, Duration, Timer};
10use heapless::Vec;
11use {defmt_rtt as _, panic_probe as _};
12use embassy_rp::block::ImageDef;
13
14#[link_section = ".start_block"]
15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info_rp_cargo_bin_name!(),
23 embassy_rp::binary_info_rp_cargo_version!(),
24 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info_rp_program_build_attribute!(),
26];
27
28
29bind_interrupts!(pub struct Irqs {
30 UART0_IRQ => UARTInterruptHandler<UART0>;
31});
32
33const START: u16 = 0xEF01;
34const ADDRESS: u32 = 0xFFFFFFFF;
35
36// ================================================================================
37
38// Data package format
39// Name Length Description
40// ==========================================================================================================
41// Start 2 bytes Fixed value of 0xEF01; High byte transferred first.
42// Address 4 bytes Default value is 0xFFFFFFFF, which can be modified by command.
43// High byte transferred first and at wrong adder value, module
44// will reject to transfer.
45// PID 1 byte 01H Command packet;
46// 02H Data packet; Data packet shall not appear alone in executing
47// processs, must follow command packet or acknowledge packet.
48// 07H Acknowledge packet;
49// 08H End of Data packet.
50// LENGTH 2 bytes Refers to the length of package content (command packets and data packets)
51// plus the length of Checksum (2 bytes). Unit is byte. Max length is 256 bytes.
52// And high byte is transferred first.
53// DATA - It can be commands, data, command’s parameters, acknowledge result, etc.
54// (fingerprint character value, template are all deemed as data);
55// SUM 2 bytes The arithmetic sum of package identifier, package length and all package
56// contens. Overflowing bits are omitted. high byte is transferred first.
57
58// ================================================================================
59
60// Checksum is calculated on 'length (2 bytes) + data (??)'.
61fn compute_checksum(buf: Vec<u8, 32>) -> u16 {
62 let mut checksum = 0u16;
63
64 let check_end = buf.len();
65 let checked_bytes = &buf[6..check_end];
66 for byte in checked_bytes {
67 checksum += (*byte) as u16;
68 }
69 return checksum;
70}
71
72#[embassy_executor::main]
73async fn main(_spawner: Spawner) {
74 info!("Start");
75
76 let p = embassy_rp::init(Default::default());
77
78 // Initialize the fingerprint scanner.
79 let mut config = Config::default();
80 config.baudrate = 57600;
81 config.stop_bits = StopBits::STOP1;
82 config.data_bits = DataBits::DataBits8;
83 config.parity = Parity::ParityNone;
84
85 let (uart, tx_pin, tx_dma, rx_pin, rx_dma) = (p.UART0, p.PIN_16, p.DMA_CH0, p.PIN_17, p.DMA_CH1);
86 let uart = Uart::new(uart, tx_pin, rx_pin, Irqs, tx_dma, rx_dma, config);
87 let (mut tx, mut rx) = uart.split();
88
89 let mut vec_buf: Vec<u8, 32> = heapless::Vec::new();
90 let mut data: Vec<u8, 32> = heapless::Vec::new();
91
92 let mut speeds: Vec<u8, 3> = heapless::Vec::new();
93 let _ = speeds.push(0xC8); // Slow
94 let _ = speeds.push(0x20); // Medium
95 let _ = speeds.push(0x02); // Fast
96
97 // Cycle through the three colours Red, Blue and Purple forever.
98 loop {
99 for colour in 1..=3 {
100 for speed in &speeds {
101 // Set the data first, because the length is dependent on that.
102 // However, we write the length bits before we do the data.
103 data.clear();
104 let _ = data.push(0x01); // ctrl=Breathing light
105 let _ = data.push(*speed);
106 let _ = data.push(colour as u8); // colour=Red, Blue, Purple
107 let _ = data.push(0x00); // times=Infinite
108
109 // Clear buffers
110 vec_buf.clear();
111
112 // START
113 let _ = vec_buf.extend_from_slice(&START.to_be_bytes()[..]);
114
115 // ADDRESS
116 let _ = vec_buf.extend_from_slice(&ADDRESS.to_be_bytes()[..]);
117
118 // PID
119 let _ = vec_buf.extend_from_slice(&[0x01]);
120
121 // LENGTH
122 let len: u16 = (1 + data.len() + 2).try_into().unwrap();
123 let _ = vec_buf.extend_from_slice(&len.to_be_bytes()[..]);
124
125 // COMMAND
126 let _ = vec_buf.push(0x35); // Command: AuraLedConfig
127
128 // DATA
129 let _ = vec_buf.extend_from_slice(&data);
130
131 // SUM
132 let chk = compute_checksum(vec_buf.clone());
133 let _ = vec_buf.extend_from_slice(&chk.to_be_bytes()[..]);
134
135 // =====
136
137 // Send command buffer.
138 let data_write: [u8; 16] = vec_buf.clone().into_array().unwrap();
139 debug!(" write='{:?}'", data_write[..]);
140 match tx.write(&data_write).await {
141 Ok(..) => info!("Write successful."),
142 Err(e) => error!("Write error: {:?}", e),
143 }
144
145 // =====
146
147 // Read command buffer.
148 let mut read_buf: [u8; 1] = [0; 1]; // Can only read one byte at a time!
149 let mut data_read: Vec<u8, 32> = heapless::Vec::new(); // Save buffer.
150
151 info!("Attempting read.");
152 loop {
153 // Some commands, like `Img2Tz()` needs longer, but we hard-code this to 200ms
154 // for this command.
155 match with_timeout(Duration::from_millis(200), rx.read(&mut read_buf)).await {
156 Ok(..) => {
157 // Extract and save read byte.
158 debug!(" r='{=u8:#04x}H' ({:03}D)", read_buf[0], read_buf[0]);
159 let _ = data_read.push(read_buf[0]).unwrap();
160 }
161 Err(..) => break, // TimeoutError -> Ignore.
162 }
163 }
164 info!("Read successful");
165 debug!(" read='{:?}'", data_read[..]);
166
167 Timer::after_secs(3).await;
168 info!("Changing speed.");
169 }
170
171 info!("Changing colour.");
172 }
173 }
174}
diff --git a/examples/rp23/src/bin/uart_unidir.rs b/examples/rp23/src/bin/uart_unidir.rs
new file mode 100644
index 000000000..4d3163285
--- /dev/null
+++ b/examples/rp23/src/bin/uart_unidir.rs
@@ -0,0 +1,66 @@
1//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip.
2//!
3//! Test TX-only and RX-only on two different UARTs. You need to connect GPIO0 to GPIO5 for
4//! this to work
5//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used
6//! with its UART port.
7
8#![no_std]
9#![no_main]
10
11use defmt::*;
12use embassy_executor::Spawner;
13use embassy_rp::bind_interrupts;
14use embassy_rp::peripherals::UART1;
15use embassy_rp::uart::{Async, Config, InterruptHandler, UartRx, UartTx};
16use embassy_time::Timer;
17use {defmt_rtt as _, panic_probe as _};
18use embassy_rp::block::ImageDef;
19
20#[link_section = ".start_block"]
21#[used]
22pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
23
24// Program metadata for `picotool info`
25#[link_section = ".bi_entries"]
26#[used]
27pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
28 embassy_rp::binary_info_rp_cargo_bin_name!(),
29 embassy_rp::binary_info_rp_cargo_version!(),
30 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
31 embassy_rp::binary_info_rp_program_build_attribute!(),
32];
33
34
35bind_interrupts!(struct Irqs {
36 UART1_IRQ => InterruptHandler<UART1>;
37});
38
39#[embassy_executor::main]
40async fn main(spawner: Spawner) {
41 let p = embassy_rp::init(Default::default());
42
43 let mut uart_tx = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Config::default());
44 let uart_rx = UartRx::new(p.UART1, p.PIN_5, Irqs, p.DMA_CH1, Config::default());
45
46 unwrap!(spawner.spawn(reader(uart_rx)));
47
48 info!("Writing...");
49 loop {
50 let data = [1u8, 2, 3, 4, 5, 6, 7, 8];
51 info!("TX {:?}", data);
52 uart_tx.write(&data).await.unwrap();
53 Timer::after_secs(1).await;
54 }
55}
56
57#[embassy_executor::task]
58async fn reader(mut rx: UartRx<'static, UART1, Async>) {
59 info!("Reading...");
60 loop {
61 // read a total of 4 transmissions (32 / 8) and then print the result
62 let mut buf = [0; 32];
63 rx.read(&mut buf).await.unwrap();
64 info!("RX {:?}", buf);
65 }
66}
diff --git a/examples/rp23/src/bin/usb_webusb.rs b/examples/rp23/src/bin/usb_webusb.rs
new file mode 100644
index 000000000..c9cab45c1
--- /dev/null
+++ b/examples/rp23/src/bin/usb_webusb.rs
@@ -0,0 +1,171 @@
1//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip.
2//!
3//! This creates a WebUSB capable device that echoes data back to the host.
4//!
5//! To test this in the browser (ideally host this on localhost:8080, to test the landing page
6//! feature):
7//! ```js
8//! (async () => {
9//! const device = await navigator.usb.requestDevice({ filters: [{ vendorId: 0xf569 }] });
10//! await device.open();
11//! await device.claimInterface(1);
12//! device.transferIn(1, 64).then(data => console.log(data));
13//! await device.transferOut(1, new Uint8Array([1,2,3]));
14//! })();
15//! ```
16
17#![no_std]
18#![no_main]
19
20use defmt::info;
21use embassy_executor::Spawner;
22use embassy_futures::join::join;
23use embassy_rp::bind_interrupts;
24use embassy_rp::peripherals::USB;
25use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler};
26use embassy_usb::class::web_usb::{Config as WebUsbConfig, State, Url, WebUsb};
27use embassy_usb::driver::{Driver, Endpoint, EndpointIn, EndpointOut};
28use embassy_usb::msos::{self, windows_version};
29use embassy_usb::{Builder, Config};
30use {defmt_rtt as _, panic_probe as _};
31use embassy_rp::block::ImageDef;
32
33#[link_section = ".start_block"]
34#[used]
35pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
36
37// Program metadata for `picotool info`
38#[link_section = ".bi_entries"]
39#[used]
40pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
41 embassy_rp::binary_info_rp_cargo_bin_name!(),
42 embassy_rp::binary_info_rp_cargo_version!(),
43 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
44 embassy_rp::binary_info_rp_program_build_attribute!(),
45];
46
47
48bind_interrupts!(struct Irqs {
49 USBCTRL_IRQ => InterruptHandler<USB>;
50});
51
52// This is a randomly generated GUID to allow clients on Windows to find our device
53const DEVICE_INTERFACE_GUIDS: &[&str] = &["{AFB9A6FB-30BA-44BC-9232-806CFC875321}"];
54
55#[embassy_executor::main]
56async fn main(_spawner: Spawner) {
57 let p = embassy_rp::init(Default::default());
58
59 // Create the driver, from the HAL.
60 let driver = UsbDriver::new(p.USB, Irqs);
61
62 // Create embassy-usb Config
63 let mut config = Config::new(0xf569, 0x0001);
64 config.manufacturer = Some("Embassy");
65 config.product = Some("WebUSB example");
66 config.serial_number = Some("12345678");
67 config.max_power = 100;
68 config.max_packet_size_0 = 64;
69
70 // Required for windows compatibility.
71 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
72 config.device_class = 0xff;
73 config.device_sub_class = 0x00;
74 config.device_protocol = 0x00;
75
76 // Create embassy-usb DeviceBuilder using the driver and config.
77 // It needs some buffers for building the descriptors.
78 let mut config_descriptor = [0; 256];
79 let mut bos_descriptor = [0; 256];
80 let mut control_buf = [0; 64];
81 let mut msos_descriptor = [0; 256];
82
83 let webusb_config = WebUsbConfig {
84 max_packet_size: 64,
85 vendor_code: 1,
86 // If defined, shows a landing page which the device manufacturer would like the user to visit in order to control their device. Suggest the user to navigate to this URL when the device is connected.
87 landing_url: Some(Url::new("http://localhost:8080")),
88 };
89
90 let mut state = State::new();
91
92 let mut builder = Builder::new(
93 driver,
94 config,
95 &mut config_descriptor,
96 &mut bos_descriptor,
97 &mut msos_descriptor,
98 &mut control_buf,
99 );
100
101 // Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.
102 // We tell Windows that this entire device is compatible with the "WINUSB" feature,
103 // which causes it to use the built-in WinUSB driver automatically, which in turn
104 // can be used by libusb/rusb software without needing a custom driver or INF file.
105 // In principle you might want to call msos_feature() just on a specific function,
106 // if your device also has other functions that still use standard class drivers.
107 builder.msos_descriptor(windows_version::WIN8_1, 0);
108 builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
109 builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
110 "DeviceInterfaceGUIDs",
111 msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
112 ));
113
114 // Create classes on the builder (WebUSB just needs some setup, but doesn't return anything)
115 WebUsb::configure(&mut builder, &mut state, &webusb_config);
116 // Create some USB bulk endpoints for testing.
117 let mut endpoints = WebEndpoints::new(&mut builder, &webusb_config);
118
119 // Build the builder.
120 let mut usb = builder.build();
121
122 // Run the USB device.
123 let usb_fut = usb.run();
124
125 // Do some WebUSB transfers.
126 let webusb_fut = async {
127 loop {
128 endpoints.wait_connected().await;
129 info!("Connected");
130 endpoints.echo().await;
131 }
132 };
133
134 // Run everything concurrently.
135 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
136 join(usb_fut, webusb_fut).await;
137}
138
139struct WebEndpoints<'d, D: Driver<'d>> {
140 write_ep: D::EndpointIn,
141 read_ep: D::EndpointOut,
142}
143
144impl<'d, D: Driver<'d>> WebEndpoints<'d, D> {
145 fn new(builder: &mut Builder<'d, D>, config: &'d WebUsbConfig<'d>) -> Self {
146 let mut func = builder.function(0xff, 0x00, 0x00);
147 let mut iface = func.interface();
148 let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None);
149
150 let write_ep = alt.endpoint_bulk_in(config.max_packet_size);
151 let read_ep = alt.endpoint_bulk_out(config.max_packet_size);
152
153 WebEndpoints { write_ep, read_ep }
154 }
155
156 // Wait until the device's endpoints are enabled.
157 async fn wait_connected(&mut self) {
158 self.read_ep.wait_enabled().await
159 }
160
161 // Echo data back to the host.
162 async fn echo(&mut self) {
163 let mut buf = [0; 64];
164 loop {
165 let n = self.read_ep.read(&mut buf).await.unwrap();
166 let data = &buf[..n];
167 info!("Data read: {:x}", data);
168 self.write_ep.write(data).await.unwrap();
169 }
170 }
171}
diff --git a/examples/rp23/src/bin/watchdog.rs b/examples/rp23/src/bin/watchdog.rs
new file mode 100644
index 000000000..8cc723150
--- /dev/null
+++ b/examples/rp23/src/bin/watchdog.rs
@@ -0,0 +1,67 @@
1//! This example shows how to use Watchdog in the RP2040 chip.
2//!
3//! It does not work with the RP Pico W board. See wifi_blinky.rs or connect external LED and resistor.
4
5#![no_std]
6#![no_main]
7
8use defmt::info;
9use embassy_executor::Spawner;
10use embassy_rp::gpio;
11use embassy_rp::watchdog::*;
12use embassy_time::{Duration, Timer};
13use gpio::{Level, Output};
14use {defmt_rtt as _, panic_probe as _};
15use embassy_rp::block::ImageDef;
16
17#[link_section = ".start_block"]
18#[used]
19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
20
21// Program metadata for `picotool info`
22#[link_section = ".bi_entries"]
23#[used]
24pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
25 embassy_rp::binary_info_rp_cargo_bin_name!(),
26 embassy_rp::binary_info_rp_cargo_version!(),
27 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
28 embassy_rp::binary_info_rp_program_build_attribute!(),
29];
30
31
32#[embassy_executor::main]
33async fn main(_spawner: Spawner) {
34 let p = embassy_rp::init(Default::default());
35 info!("Hello world!");
36
37 let mut watchdog = Watchdog::new(p.WATCHDOG);
38 let mut led = Output::new(p.PIN_25, Level::Low);
39
40 // Set the LED high for 2 seconds so we know when we're about to start the watchdog
41 led.set_high();
42 Timer::after_secs(2).await;
43
44 // Set to watchdog to reset if it's not fed within 1.05 seconds, and start it
45 watchdog.start(Duration::from_millis(1_050));
46 info!("Started the watchdog timer");
47
48 // Blink once a second for 5 seconds, feed the watchdog timer once a second to avoid a reset
49 for _ in 1..=5 {
50 led.set_low();
51 Timer::after_millis(500).await;
52 led.set_high();
53 Timer::after_millis(500).await;
54 info!("Feeding watchdog");
55 watchdog.feed();
56 }
57
58 info!("Stopped feeding, device will reset in 1.05 seconds");
59 // Blink 10 times per second, not feeding the watchdog.
60 // The processor should reset in 1.05 seconds.
61 loop {
62 led.set_low();
63 Timer::after_millis(100).await;
64 led.set_high();
65 Timer::after_millis(100).await;
66 }
67}
diff --git a/examples/rp23/src/bin/zerocopy.rs b/examples/rp23/src/bin/zerocopy.rs
new file mode 100644
index 000000000..e379d9c00
--- /dev/null
+++ b/examples/rp23/src/bin/zerocopy.rs
@@ -0,0 +1,110 @@
1//! This example shows how to use `zerocopy_channel` from `embassy_sync` for
2//! sending large values between two tasks without copying.
3//! The example also shows how to use the RP2040 ADC with DMA.
4#![no_std]
5#![no_main]
6
7use core::sync::atomic::{AtomicU16, Ordering};
8
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_rp::adc::{self, Adc, Async, Config, InterruptHandler};
12use embassy_rp::bind_interrupts;
13use embassy_rp::gpio::Pull;
14use embassy_rp::peripherals::DMA_CH0;
15use embassy_sync::blocking_mutex::raw::NoopRawMutex;
16use embassy_sync::zerocopy_channel::{Channel, Receiver, Sender};
17use embassy_time::{Duration, Ticker, Timer};
18use static_cell::StaticCell;
19use {defmt_rtt as _, panic_probe as _};
20use embassy_rp::block::ImageDef;
21
22#[link_section = ".start_block"]
23#[used]
24pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
25
26// Program metadata for `picotool info`
27#[link_section = ".bi_entries"]
28#[used]
29pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
30 embassy_rp::binary_info_rp_cargo_bin_name!(),
31 embassy_rp::binary_info_rp_cargo_version!(),
32 embassy_rp::binary_info_rp_program_description!(c"Blinky"),
33 embassy_rp::binary_info_rp_program_build_attribute!(),
34];
35
36
37type SampleBuffer = [u16; 512];
38
39bind_interrupts!(struct Irqs {
40 ADC_IRQ_FIFO => InterruptHandler;
41});
42
43const BLOCK_SIZE: usize = 512;
44const NUM_BLOCKS: usize = 2;
45static MAX: AtomicU16 = AtomicU16::new(0);
46
47struct AdcParts {
48 adc: Adc<'static, Async>,
49 pin: adc::Channel<'static>,
50 dma: DMA_CH0,
51}
52
53#[embassy_executor::main]
54async fn main(spawner: Spawner) {
55 let p = embassy_rp::init(Default::default());
56 info!("Here we go!");
57
58 let adc_parts = AdcParts {
59 adc: Adc::new(p.ADC, Irqs, Config::default()),
60 pin: adc::Channel::new_pin(p.PIN_29, Pull::None),
61 dma: p.DMA_CH0,
62 };
63
64 static BUF: StaticCell<[SampleBuffer; NUM_BLOCKS]> = StaticCell::new();
65 let buf = BUF.init([[0; BLOCK_SIZE]; NUM_BLOCKS]);
66
67 static CHANNEL: StaticCell<Channel<'_, NoopRawMutex, SampleBuffer>> = StaticCell::new();
68 let channel = CHANNEL.init(Channel::new(buf));
69 let (sender, receiver) = channel.split();
70
71 spawner.must_spawn(consumer(receiver));
72 spawner.must_spawn(producer(sender, adc_parts));
73
74 let mut ticker = Ticker::every(Duration::from_secs(1));
75 loop {
76 ticker.next().await;
77 let max = MAX.load(Ordering::Relaxed);
78 info!("latest block's max value: {:?}", max);
79 }
80}
81
82#[embassy_executor::task]
83async fn producer(mut sender: Sender<'static, NoopRawMutex, SampleBuffer>, mut adc: AdcParts) {
84 loop {
85 // Obtain a free buffer from the channel
86 let buf = sender.send().await;
87
88 // Fill it with data
89 adc.adc.read_many(&mut adc.pin, buf, 1, &mut adc.dma).await.unwrap();
90
91 // Notify the channel that the buffer is now ready to be received
92 sender.send_done();
93 }
94}
95
96#[embassy_executor::task]
97async fn consumer(mut receiver: Receiver<'static, NoopRawMutex, SampleBuffer>) {
98 loop {
99 // Receive a buffer from the channel
100 let buf = receiver.receive().await;
101
102 // Simulate using the data, while the producer is filling up the next buffer
103 Timer::after_micros(1000).await;
104 let max = buf.iter().max().unwrap();
105 MAX.store(*max, Ordering::Relaxed);
106
107 // Notify the channel that the buffer is now ready to be reused
108 receiver.receive_done();
109 }
110}
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"] }