aboutsummaryrefslogtreecommitdiff
path: root/embassy-rp
diff options
context:
space:
mode:
author1-rafael-1 <[email protected]>2025-09-15 20:07:18 +0200
committer1-rafael-1 <[email protected]>2025-09-15 20:07:18 +0200
commit6bb3d2c0720fa082f27d3cdb70f516058497ec87 (patch)
tree5a1e255cff999b00800f203b91a759c720c973e5 /embassy-rp
parenteb685574601d98c44faed9a3534d056199b46e20 (diff)
parent92a6fd2946f2cbb15359290f68aa360953da2ff7 (diff)
Merge branch 'main' into rp2040-rtc-alarm
Diffstat (limited to 'embassy-rp')
-rw-r--r--embassy-rp/CHANGELOG.md48
-rw-r--r--embassy-rp/Cargo.toml37
-rw-r--r--embassy-rp/src/adc.rs2
-rw-r--r--embassy-rp/src/clocks.rs297
-rw-r--r--embassy-rp/src/flash.rs6
-rw-r--r--embassy-rp/src/gpio.rs55
-rw-r--r--embassy-rp/src/i2c.rs27
-rw-r--r--embassy-rp/src/i2c_slave.rs16
-rw-r--r--embassy-rp/src/lib.rs30
-rw-r--r--embassy-rp/src/multicore.rs24
-rw-r--r--embassy-rp/src/pio/mod.rs90
-rw-r--r--embassy-rp/src/pio_programs/i2s.rs114
-rw-r--r--embassy-rp/src/pio_programs/mod.rs1
-rw-r--r--embassy-rp/src/pio_programs/onewire.rs45
-rw-r--r--embassy-rp/src/pio_programs/spi.rs474
-rw-r--r--embassy-rp/src/pio_programs/ws2812.rs70
-rw-r--r--embassy-rp/src/psram.rs682
-rw-r--r--embassy-rp/src/pwm.rs8
-rw-r--r--embassy-rp/src/qmi_cs1.rs57
-rw-r--r--embassy-rp/src/relocate.rs2
-rw-r--r--embassy-rp/src/rom_data/mod.rs36
-rw-r--r--embassy-rp/src/trng.rs25
-rw-r--r--embassy-rp/src/uart/mod.rs34
-rw-r--r--embassy-rp/src/usb.rs30
24 files changed, 2060 insertions, 150 deletions
diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md
index 7ac0a47cb..841c9f068 100644
--- a/embassy-rp/CHANGELOG.md
+++ b/embassy-rp/CHANGELOG.md
@@ -5,7 +5,53 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8## Unreleased 8<!-- next-header -->
9## Unreleased - ReleaseDate
10
11- Add PIO SPI
12- Add PIO I2S input
13- Add PIO onewire parasite power strong pullup
14
15## 0.8.0 - 2025-08-26
16
17## 0.7.1 - 2025-08-26
18- add `i2c` internal pullup options ([#4564](https://github.com/embassy-rs/embassy/pull/4564))
19
20## 0.7.0 - 2025-08-04
21
22- changed: update to latest embassy-time-queue-utils
23
24## 0.6.0 - 2025-07-16
25
26- update to latest embassy-usb-driver
27
28## 0.5.0 - 2025-07-15
29
30- Fix wrong `funcsel` on RP2350 gpout/gpin ([#3975](https://github.com/embassy-rs/embassy/pull/3975))
31- Fix potential race condition in `ADC::wait_for_ready` ([#4012](https://github.com/embassy-rs/embassy/pull/4012))
32- `flash`: rename `BOOTROM_BASE` to `BOOTRAM_BASE` ([#4014](https://github.com/embassy-rs/embassy/pull/4014))
33- Remove `Peripheral` trait & rename `PeripheralRef` to `Peri` ([#3999](https://github.com/embassy-rs/embassy/pull/3999))
34- Fix watchdog count on RP235x ([#4021](https://github.com/embassy-rs/embassy/pull/4021))
35- I2C: ensure that wakers are registered before checking status of `wait_on` helpers ([#4043](https://github.com/embassy-rs/embassy/pull/4043))
36- Modify `Uarte` and `BufferedUarte` initialization to take pins before interrupts ([#3983](https://github.com/embassy-rs/embassy/pull/3983))
37- `uart`: increase RX FIFO watermark from 1/8 to 7/8 ([#4055](https://github.com/embassy-rs/embassy/pull/4055))
38- Add `spinlock_mutex` ([#4017](https://github.com/embassy-rs/embassy/pull/4017))
39- Enable input mode for PWM pins on RP235x and disable it on drop ([#4093](https://github.com/embassy-rs/embassy/pull/4093))
40- Add `impl rand_core::CryptoRng for Trng` ([#4096](https://github.com/embassy-rs/embassy/pull/4096))
41- `pwm`: enable pull-down resistors for pins in `Drop` implementation ([#4115](https://github.com/embassy-rs/embassy/pull/4115))
42- Rewrite PIO onewire implementation ([#4128](https://github.com/embassy-rs/embassy/pull/4128))
43- Implement RP2040 overclocking ([#4150](https://github.com/embassy-rs/embassy/pull/4150))
44- Implement RP235x overclocking ([#4187](https://github.com/embassy-rs/embassy/pull/4187))
45- `trng`: improve error handling ([#4139](https://github.com/embassy-rs/embassy/pull/4139))
46- Remove `<T: Instance>` from `Uart` and `BufferedUart` ([#4155](https://github.com/embassy-rs/embassy/pull/4155))
47- Make bit-depth of I2S PIO program configurable ([#4193](https://github.com/embassy-rs/embassy/pull/4193))
48- Add the possibility to document `bind_interrupts` `struct`s ([#4206](https://github.com/embassy-rs/embassy/pull/4206))
49- Add missing `Debug` and `defmt::Format` `derive`s for ADC & `AnyPin` ([#4205](https://github.com/embassy-rs/embassy/pull/4205))
50- Add `rand-core` v0.9 support ([#4217](https://github.com/embassy-rs/embassy/pull/4217))
51- Update `embassy-sync` to v0.7.0 ([#4234](https://github.com/embassy-rs/embassy/pull/4234))
52- Add compatibility with ws2812 leds that have 4 addressable lights ([#4236](https://github.com/embassy-rs/embassy/pull/4236))
53- Implement input/output inversion ([#4237](https://github.com/embassy-rs/embassy/pull/4237))
54- Add `multicore::current_core` API ([#4362](https://github.com/embassy-rs/embassy/pull/4362))
9 55
10## 0.4.0 - 2025-03-09 56## 0.4.0 - 2025-03-09
11 57
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml
index 8fb8a50fd..101914a36 100644
--- a/embassy-rp/Cargo.toml
+++ b/embassy-rp/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "embassy-rp" 2name = "embassy-rp"
3version = "0.4.0" 3version = "0.8.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6description = "Embassy Hardware Abstraction Layer (HAL) for the Raspberry Pi RP2040 or RP235x microcontroller" 6description = "Embassy Hardware Abstraction Layer (HAL) for the Raspberry Pi RP2040 or RP235x microcontroller"
@@ -9,6 +9,17 @@ categories = ["embedded", "hardware-support", "no-std", "asynchronous"]
9repository = "https://github.com/embassy-rs/embassy" 9repository = "https://github.com/embassy-rs/embassy"
10documentation = "https://docs.embassy.dev/embassy-rp" 10documentation = "https://docs.embassy.dev/embassy-rp"
11 11
12[package.metadata.embassy]
13build = [
14 {target = "thumbv6m-none-eabi", features = ["defmt", "rp2040", "time-driver"]},
15 {target = "thumbv6m-none-eabi", features = ["log", "rp2040", "time-driver"]},
16 {target = "thumbv6m-none-eabi", features = ["intrinsics", "rp2040", "time-driver"]},
17 {target = "thumbv6m-none-eabi", features = ["qspi-as-gpio", "rp2040", "time-driver"]},
18 {target = "thumbv8m.main-none-eabihf", features = ["defmt", "rp235xa", "time-driver"]},
19 {target = "thumbv8m.main-none-eabihf", features = ["log", "rp235xa", "time-driver"]},
20 {target = "thumbv8m.main-none-eabihf", features = ["binary-info", "rp235xa", "time-driver"]},
21]
22
12[package.metadata.embassy_docs] 23[package.metadata.embassy_docs]
13src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/embassy-rp/src/" 24src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/embassy-rp/src/"
14src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-rp/src/" 25src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-rp/src/"
@@ -136,16 +147,16 @@ _test = []
136binary-info = ["rt", "dep:rp-binary-info", "rp-binary-info?/binary-info"] 147binary-info = ["rt", "dep:rp-binary-info", "rp-binary-info?/binary-info"]
137 148
138[dependencies] 149[dependencies]
139embassy-sync = { version = "0.6.2", path = "../embassy-sync" } 150embassy-sync = { version = "0.7.2", path = "../embassy-sync" }
140embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true } 151embassy-time-driver = { version = "0.2.1", path = "../embassy-time-driver", optional = true }
141embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } 152embassy-time-queue-utils = { version = "0.3.0", path = "../embassy-time-queue-utils", optional = true }
142embassy-time = { version = "0.4.0", path = "../embassy-time" } 153embassy-time = { version = "0.5.0", path = "../embassy-time" }
143embassy-futures = { version = "0.1.0", path = "../embassy-futures" } 154embassy-futures = { version = "0.1.2", path = "../embassy-futures" }
144embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } 155embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] }
145embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal" } 156embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" }
146embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } 157embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" }
147atomic-polyfill = "1.0.1" 158atomic-polyfill = "1.0.1"
148defmt = { version = "0.3", optional = true } 159defmt = { version = "1.0.1", optional = true }
149log = { version = "0.4.14", optional = true } 160log = { version = "0.4.14", optional = true }
150nb = "1.1.0" 161nb = "1.1.0"
151cfg-if = "1.0.0" 162cfg-if = "1.0.0"
@@ -157,7 +168,6 @@ embedded-io = { version = "0.6.1" }
157embedded-io-async = { version = "0.6.1" } 168embedded-io-async = { version = "0.6.1" }
158embedded-storage = { version = "0.3" } 169embedded-storage = { version = "0.3" }
159embedded-storage-async = { version = "0.4.1" } 170embedded-storage-async = { version = "0.4.1" }
160rand_core = "0.6.4"
161fixed = "1.28.0" 171fixed = "1.28.0"
162 172
163rp-pac = { version = "7.0.0" } 173rp-pac = { version = "7.0.0" }
@@ -167,6 +177,9 @@ embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
167embedded-hal-async = { version = "1.0" } 177embedded-hal-async = { version = "1.0" }
168embedded-hal-nb = { version = "1.0" } 178embedded-hal-nb = { version = "1.0" }
169 179
180rand-core-06 = { package = "rand_core", version = "0.6" }
181rand-core-09 = { package = "rand_core", version = "0.9" }
182
170pio = { version = "0.3" } 183pio = { version = "0.3" }
171rp2040-boot2 = "0.3" 184rp2040-boot2 = "0.3"
172document-features = "0.2.10" 185document-features = "0.2.10"
@@ -175,5 +188,5 @@ rp-binary-info = { version = "0.1.0", optional = true }
175smart-leds = "0.4.0" 188smart-leds = "0.4.0"
176 189
177[dev-dependencies] 190[dev-dependencies]
178embassy-executor = { version = "0.7.0", path = "../embassy-executor", features = ["arch-std", "executor-thread"] } 191embassy-executor = { version = "0.9.0", path = "../embassy-executor", features = ["arch-std", "executor-thread"] }
179static_cell = { version = "2" } 192static_cell = { version = "2" }
diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs
index ec0c8c46c..2db8e63d7 100644
--- a/embassy-rp/src/adc.rs
+++ b/embassy-rp/src/adc.rs
@@ -21,6 +21,8 @@ static WAKER: AtomicWaker = AtomicWaker::new();
21#[derive(Default)] 21#[derive(Default)]
22pub struct Config {} 22pub struct Config {}
23 23
24#[derive(Debug)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
24enum Source<'p> { 26enum Source<'p> {
25 Pin(Peri<'p, AnyPin>), 27 Pin(Peri<'p, AnyPin>),
26 TempSensor(Peri<'p, ADC_TEMP_SENSOR>), 28 TempSensor(Peri<'p, ADC_TEMP_SENSOR>),
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs
index 6694aab66..2eddc0bcc 100644
--- a/embassy-rp/src/clocks.rs
+++ b/embassy-rp/src/clocks.rs
@@ -38,7 +38,7 @@
38//! 38//!
39//! ## Examples 39//! ## Examples
40//! 40//!
41//! ### Standard 125MHz configuration 41//! ### Standard 125MHz (rp2040) or 150Mhz (rp235x) configuration
42//! ```rust,ignore 42//! ```rust,ignore
43//! let config = ClockConfig::crystal(12_000_000); 43//! let config = ClockConfig::crystal(12_000_000);
44//! ``` 44//! ```
@@ -63,7 +63,6 @@
63//! // Set other parameters as needed... 63//! // Set other parameters as needed...
64//! ``` 64//! ```
65 65
66#[cfg(feature = "rp2040")]
67use core::arch::asm; 66use core::arch::asm;
68use core::marker::PhantomData; 67use core::marker::PhantomData;
69#[cfg(feature = "rp2040")] 68#[cfg(feature = "rp2040")]
@@ -73,7 +72,6 @@ use core::sync::atomic::{AtomicU32, Ordering};
73use pac::clocks::vals::*; 72use pac::clocks::vals::*;
74 73
75use crate::gpio::{AnyPin, SealedPin}; 74use crate::gpio::{AnyPin, SealedPin};
76#[cfg(feature = "rp2040")]
77use crate::pac::common::{Reg, RW}; 75use crate::pac::common::{Reg, RW};
78use crate::{pac, reset, Peri}; 76use crate::{pac, reset, Peri};
79 77
@@ -82,6 +80,18 @@ use crate::{pac, reset, Peri};
82// be very useful until we have runtime clock reconfiguration. once this 80// be very useful until we have runtime clock reconfiguration. once this
83// happens we can resurrect the commented-out gpin bits. 81// happens we can resurrect the commented-out gpin bits.
84 82
83/// Clock error types.
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85#[cfg_attr(feature = "defmt", derive(defmt::Format))]
86pub enum ClockError {
87 /// PLL failed to lock within the timeout period.
88 PllLockTimedOut,
89 /// Could not find valid PLL parameters for system clock.
90 InvalidPllParameters,
91 /// Reading the core voltage failed due to an unexpected value in the register.
92 UnexpectedCoreVoltageRead,
93}
94
85struct Clocks { 95struct Clocks {
86 xosc: AtomicU32, 96 xosc: AtomicU32,
87 sys: AtomicU32, 97 sys: AtomicU32,
@@ -136,15 +146,16 @@ pub enum PeriClkSrc {
136 // Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 as _ , 146 // Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 as _ ,
137} 147}
138 148
139/// Core voltage regulator settings for RP2040. 149/// Core voltage regulator settings.
140/// 150///
141/// The RP2040 voltage regulator can be configured for different output voltages. 151/// The voltage regulator can be configured for different output voltages.
142/// Higher voltages allow for higher clock frequencies but increase power consumption and heat. 152/// Higher voltages allow for higher clock frequencies but increase power consumption and heat.
143#[cfg(feature = "rp2040")] 153#[cfg(feature = "rp2040")]
144#[derive(Clone, Copy, Debug, PartialEq, Eq)]
145#[repr(u8)] 154#[repr(u8)]
155#[derive(Clone, Copy, Debug, PartialEq, Eq)]
156#[cfg_attr(feature = "defmt", derive(defmt::Format))]
146pub enum CoreVoltage { 157pub enum CoreVoltage {
147 /// 0.80V - Suitable for lower frequencies 158 /// 0.80V
148 V0_80 = 0b0000, 159 V0_80 = 0b0000,
149 /// 0.85V 160 /// 0.85V
150 V0_85 = 0b0110, 161 V0_85 = 0b0110,
@@ -168,11 +179,58 @@ pub enum CoreVoltage {
168 V1_30 = 0b1111, 179 V1_30 = 0b1111,
169} 180}
170 181
171#[cfg(feature = "rp2040")] 182/// Core voltage regulator settings.
183///
184/// The voltage regulator can be configured for different output voltages.
185/// Higher voltages allow for higher clock frequencies but increase power consumption and heat.
186///
187/// **Note**: The maximum voltage is 1.30V, unless unlocked by setting unless the voltage limit
188/// is disabled using the disable_voltage_limit field in the vreg_ctrl register. For lack of practical use at this
189/// point in time, this is not implemented here. So the maximum voltage in this enum is 1.30V for now.
190#[cfg(feature = "_rp235x")]
191#[repr(u8)]
192#[derive(Clone, Copy, Debug, PartialEq, Eq)]
193#[cfg_attr(feature = "defmt", derive(defmt::Format))]
194pub enum CoreVoltage {
195 /// 0.55V
196 V0_55 = 0b00000,
197 /// 0.60V
198 V0_60 = 0b00001,
199 /// 0.65V
200 V0_65 = 0b00010,
201 /// 0.70V
202 V0_70 = 0b00011,
203 /// 0.75V
204 V0_75 = 0b00100,
205 /// 0.80V
206 V0_80 = 0b00101,
207 /// 0.85V
208 V0_85 = 0b00110,
209 /// 0.90V
210 V0_90 = 0b00111,
211 /// 0.95V
212 V0_95 = 0b01000,
213 /// 1.00V
214 V1_00 = 0b01001,
215 /// 1.05V
216 V1_05 = 0b01010,
217 /// 1.10V - Default voltage level
218 V1_10 = 0b01011,
219 /// 1.15V
220 V1_15 = 0b01100,
221 /// 1.20V
222 V1_20 = 0b01101,
223 /// 1.25V
224 V1_25 = 0b01110,
225 /// 1.30V
226 V1_30 = 0b01111,
227}
228
172impl CoreVoltage { 229impl CoreVoltage {
173 /// Get the recommended Brown-Out Detection (BOD) setting for this voltage. 230 /// Get the recommended Brown-Out Detection (BOD) setting for this voltage.
174 /// Sets the BOD threshold to approximately 80% of the core voltage. 231 /// Sets the BOD threshold to approximately 80% of the core voltage.
175 fn recommended_bod(self) -> u8 { 232 fn recommended_bod(self) -> u8 {
233 #[cfg(feature = "rp2040")]
176 match self { 234 match self {
177 CoreVoltage::V0_80 => 0b0100, // 0.645V (~81% of 0.80V) 235 CoreVoltage::V0_80 => 0b0100, // 0.645V (~81% of 0.80V)
178 CoreVoltage::V0_85 => 0b0101, // 0.688V (~81% of 0.85V) 236 CoreVoltage::V0_85 => 0b0101, // 0.688V (~81% of 0.85V)
@@ -180,12 +238,32 @@ impl CoreVoltage {
180 CoreVoltage::V0_95 => 0b0111, // 0.774V (~81% of 0.95V) 238 CoreVoltage::V0_95 => 0b0111, // 0.774V (~81% of 0.95V)
181 CoreVoltage::V1_00 => 0b1000, // 0.817V (~82% of 1.00V) 239 CoreVoltage::V1_00 => 0b1000, // 0.817V (~82% of 1.00V)
182 CoreVoltage::V1_05 => 0b1000, // 0.817V (~78% of 1.05V) 240 CoreVoltage::V1_05 => 0b1000, // 0.817V (~78% of 1.05V)
183 CoreVoltage::V1_10 => 0b1001, // 0.860V (~78% of 1.10V) 241 CoreVoltage::V1_10 => 0b1001, // 0.860V (~78% of 1.10V), the default
184 CoreVoltage::V1_15 => 0b1010, // 0.903V (~79% of 1.15V) 242 CoreVoltage::V1_15 => 0b1010, // 0.903V (~79% of 1.15V)
185 CoreVoltage::V1_20 => 0b1011, // 0.946V (~79% of 1.20V) 243 CoreVoltage::V1_20 => 0b1011, // 0.946V (~79% of 1.20V)
186 CoreVoltage::V1_25 => 0b1100, // 0.989V (~79% of 1.25V) 244 CoreVoltage::V1_25 => 0b1100, // 0.989V (~79% of 1.25V)
187 CoreVoltage::V1_30 => 0b1101, // 1.032V (~79% of 1.30V) 245 CoreVoltage::V1_30 => 0b1101, // 1.032V (~79% of 1.30V)
188 } 246 }
247 #[cfg(feature = "_rp235x")]
248 match self {
249 CoreVoltage::V0_55 => 0b00001, // 0.516V (~94% of 0.55V)
250 CoreVoltage::V0_60 => 0b00010, // 0.559V (~93% of 0.60V)
251 CoreVoltage::V0_65 => 0b00011, // 0.602V (~93% of 0.65V)
252 CoreVoltage::V0_70 => 0b00011, // 0.602V (~86% of 0.70V)
253 CoreVoltage::V0_75 => 0b00100, // 0.645V (~86% of 0.75V)
254 CoreVoltage::V0_80 => 0b00101, // 0.688V (~86% of 0.80V)
255 CoreVoltage::V0_85 => 0b00110, // 0.731V (~86% of 0.85V)
256 CoreVoltage::V0_90 => 0b00110, // 0.731V (~81% of 0.90V)
257 CoreVoltage::V0_95 => 0b00111, // 0.774V (~81% of 0.95V)
258 CoreVoltage::V1_00 => 0b01000, // 0.817V (~82% of 1.00V)
259 CoreVoltage::V1_05 => 0b01000, // 0.817V (~78% of 1.05V)
260 CoreVoltage::V1_10 => 0b01001, // 0.860V (~78% of 1.10V), the default
261 CoreVoltage::V1_15 => 0b01001, // 0.860V (~75% of 1.15V)
262 CoreVoltage::V1_20 => 0b01010, // 0.903V (~75% of 1.20V)
263 CoreVoltage::V1_25 => 0b01010, // 0.903V (~72% of 1.25V)
264 CoreVoltage::V1_30 => 0b01011, // 0.946V (~73% of 1.30V)
265 // all others: 0.946V (see CoreVoltage: we do not support setting Voltages higher than 1.30V at this point)
266 }
189 } 267 }
190} 268}
191 269
@@ -209,12 +287,10 @@ pub struct ClockConfig {
209 /// RTC clock configuration. 287 /// RTC clock configuration.
210 #[cfg(feature = "rp2040")] 288 #[cfg(feature = "rp2040")]
211 pub rtc_clk: Option<RtcClkConfig>, 289 pub rtc_clk: Option<RtcClkConfig>,
212 /// Core voltage scaling (RP2040 only). Defaults to 1.10V. 290 /// Core voltage scaling. Defaults to 1.10V.
213 #[cfg(feature = "rp2040")]
214 pub core_voltage: CoreVoltage, 291 pub core_voltage: CoreVoltage,
215 /// Voltage stabilization delay in microseconds. 292 /// Voltage stabilization delay in microseconds.
216 /// If not set, defaults will be used based on voltage level. 293 /// If not set, defaults will be used based on voltage level.
217 #[cfg(feature = "rp2040")]
218 pub voltage_stabilization_delay_us: Option<u32>, 294 pub voltage_stabilization_delay_us: Option<u32>,
219 // See above re gpin handling being commented out 295 // See above re gpin handling being commented out
220 // gpin0: Option<(u32, Gpin<'static, AnyPin>)>, 296 // gpin0: Option<(u32, Gpin<'static, AnyPin>)>,
@@ -250,9 +326,7 @@ impl Default for ClockConfig {
250 adc_clk: None, 326 adc_clk: None,
251 #[cfg(feature = "rp2040")] 327 #[cfg(feature = "rp2040")]
252 rtc_clk: None, 328 rtc_clk: None,
253 #[cfg(feature = "rp2040")]
254 core_voltage: CoreVoltage::V1_10, 329 core_voltage: CoreVoltage::V1_10,
255 #[cfg(feature = "rp2040")]
256 voltage_stabilization_delay_us: None, 330 voltage_stabilization_delay_us: None,
257 // See above re gpin handling being commented out 331 // See above re gpin handling being commented out
258 // gpin0: None, 332 // gpin0: None,
@@ -323,9 +397,7 @@ impl ClockConfig {
323 div_frac: 0, 397 div_frac: 0,
324 phase: 0, 398 phase: 0,
325 }), 399 }),
326 #[cfg(feature = "rp2040")]
327 core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V) 400 core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V)
328 #[cfg(feature = "rp2040")]
329 voltage_stabilization_delay_us: None, 401 voltage_stabilization_delay_us: None,
330 // See above re gpin handling being commented out 402 // See above re gpin handling being commented out
331 // gpin0: None, 403 // gpin0: None,
@@ -368,9 +440,7 @@ impl ClockConfig {
368 div_frac: 171, 440 div_frac: 171,
369 phase: 0, 441 phase: 0,
370 }), 442 }),
371 #[cfg(feature = "rp2040")]
372 core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V) 443 core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V)
373 #[cfg(feature = "rp2040")]
374 voltage_stabilization_delay_us: None, 444 voltage_stabilization_delay_us: None,
375 // See above re gpin handling being commented out 445 // See above re gpin handling being commented out
376 // gpin0: None, 446 // gpin0: None,
@@ -391,29 +461,42 @@ impl ClockConfig {
391 /// # Returns 461 /// # Returns
392 /// 462 ///
393 /// A ClockConfig configured to achieve the requested system frequency using the 463 /// A ClockConfig configured to achieve the requested system frequency using the
394 /// the usual 12Mhz crystal, or panic if no valid parameters can be found. 464 /// the usual 12Mhz crystal, or an error if no valid parameters can be found.
395 /// 465 ///
396 /// # Note on core voltage: 466 /// # Note on core voltage:
467 ///
468 /// **For RP2040**:
397 /// To date the only officially documented core voltages (see Datasheet section 2.15.3.1. Instances) are: 469 /// To date the only officially documented core voltages (see Datasheet section 2.15.3.1. Instances) are:
398 /// - Up to 133MHz: V1_10 (default) 470 /// - Up to 133MHz: V1_10 (default)
399 /// - Above 133MHz: V1_15, but in the context of the datasheet covering reaching up to 200Mhz 471 /// - Above 133MHz: V1_15, but in the context of the datasheet covering reaching up to 200Mhz
400 /// That way all other frequencies below 133MHz or above 200MHz are not explicitly documented and not covered here. 472 /// That way all other frequencies below 133MHz or above 200MHz are not explicitly documented and not covered here.
401 /// In case You want to go below 133MHz or above 200MHz and want a different voltage, You will have to set that manually and with caution. 473 /// In case You want to go below 133MHz or above 200MHz and want a different voltage, You will have to set that manually and with caution.
402 #[cfg(feature = "rp2040")] 474 ///
403 pub fn system_freq(hz: u32) -> Self { 475 /// **For RP235x**:
476 /// At this point in time there is no official manufacturer endorsement for running the chip on other core voltages and/or other clock speeds than the defaults.
477 /// Using this function is experimental and may not work as expected or even damage the chip.
478 ///
479 /// # Returns
480 ///
481 /// A Result containing either the configured ClockConfig or a ClockError.
482 pub fn system_freq(hz: u32) -> Result<Self, ClockError> {
404 // Start with the standard configuration from crystal() 483 // Start with the standard configuration from crystal()
405 const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000; 484 const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000;
406 let mut config = Self::crystal(DEFAULT_CRYSTAL_HZ); 485 let mut config = Self::crystal(DEFAULT_CRYSTAL_HZ);
407 486
408 // No need to modify anything if target frequency is already 125MHz 487 // No need to modify anything if target frequency is already 125MHz
409 // (which is what crystal() configures by default) 488 // (which is what crystal() configures by default)
489 #[cfg(feature = "rp2040")]
410 if hz == 125_000_000 { 490 if hz == 125_000_000 {
411 return config; 491 return Ok(config);
492 }
493 #[cfg(feature = "_rp235x")]
494 if hz == 150_000_000 {
495 return Ok(config);
412 } 496 }
413 497
414 // Find optimal PLL parameters for the requested frequency 498 // Find optimal PLL parameters for the requested frequency
415 let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, hz) 499 let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, hz).ok_or(ClockError::InvalidPllParameters)?;
416 .unwrap_or_else(|| panic!("Could not find valid PLL parameters for system clock"));
417 500
418 // Replace the sys_pll configuration with our custom parameters 501 // Replace the sys_pll configuration with our custom parameters
419 if let Some(xosc) = &mut config.xosc { 502 if let Some(xosc) = &mut config.xosc {
@@ -429,8 +512,16 @@ impl ClockConfig {
429 _ => CoreVoltage::V1_10, // Use default voltage (V1_10) 512 _ => CoreVoltage::V1_10, // Use default voltage (V1_10)
430 }; 513 };
431 } 514 }
515 #[cfg(feature = "_rp235x")]
516 {
517 config.core_voltage = match hz {
518 // There is no official support for running the chip on other core voltages and/or other clock speeds than the defaults.
519 // So for now we have not way of knowing what the voltage should be. Change this if the manufacturer provides more information.
520 _ => CoreVoltage::V1_10, // Use default voltage (V1_10)
521 };
522 }
432 523
433 config 524 Ok(config)
434 } 525 }
435 526
436 /// Configure with manual PLL settings for full control over system clock 527 /// Configure with manual PLL settings for full control over system clock
@@ -525,6 +616,7 @@ impl ClockConfig {
525#[repr(u16)] 616#[repr(u16)]
526#[non_exhaustive] 617#[non_exhaustive]
527#[derive(Clone, Copy, Debug, PartialEq, Eq)] 618#[derive(Clone, Copy, Debug, PartialEq, Eq)]
619#[cfg_attr(feature = "defmt", derive(defmt::Format))]
528pub enum RoscRange { 620pub enum RoscRange {
529 /// Low range. 621 /// Low range.
530 Low = pac::rosc::vals::FreqRange::LOW.0, 622 Low = pac::rosc::vals::FreqRange::LOW.0,
@@ -631,6 +723,7 @@ pub struct RefClkConfig {
631/// Reference clock source. 723/// Reference clock source.
632#[non_exhaustive] 724#[non_exhaustive]
633#[derive(Clone, Copy, Debug, PartialEq, Eq)] 725#[derive(Clone, Copy, Debug, PartialEq, Eq)]
726#[cfg_attr(feature = "defmt", derive(defmt::Format))]
634pub enum RefClkSrc { 727pub enum RefClkSrc {
635 /// XOSC. 728 /// XOSC.
636 Xosc, 729 Xosc,
@@ -646,6 +739,7 @@ pub enum RefClkSrc {
646/// SYS clock source. 739/// SYS clock source.
647#[non_exhaustive] 740#[non_exhaustive]
648#[derive(Clone, Copy, Debug, PartialEq, Eq)] 741#[derive(Clone, Copy, Debug, PartialEq, Eq)]
742#[cfg_attr(feature = "defmt", derive(defmt::Format))]
649pub enum SysClkSrc { 743pub enum SysClkSrc {
650 /// REF. 744 /// REF.
651 Ref, 745 Ref,
@@ -684,6 +778,7 @@ pub struct SysClkConfig {
684#[repr(u8)] 778#[repr(u8)]
685#[non_exhaustive] 779#[non_exhaustive]
686#[derive(Clone, Copy, Debug, PartialEq, Eq)] 780#[derive(Clone, Copy, Debug, PartialEq, Eq)]
781#[cfg_attr(feature = "defmt", derive(defmt::Format))]
687pub enum UsbClkSrc { 782pub enum UsbClkSrc {
688 /// PLL USB. 783 /// PLL USB.
689 PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB as _, 784 PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB as _,
@@ -712,6 +807,7 @@ pub struct UsbClkConfig {
712#[repr(u8)] 807#[repr(u8)]
713#[non_exhaustive] 808#[non_exhaustive]
714#[derive(Clone, Copy, Debug, PartialEq, Eq)] 809#[derive(Clone, Copy, Debug, PartialEq, Eq)]
810#[cfg_attr(feature = "defmt", derive(defmt::Format))]
715pub enum AdcClkSrc { 811pub enum AdcClkSrc {
716 /// PLL USB. 812 /// PLL USB.
717 PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB as _, 813 PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB as _,
@@ -740,6 +836,7 @@ pub struct AdcClkConfig {
740#[repr(u8)] 836#[repr(u8)]
741#[non_exhaustive] 837#[non_exhaustive]
742#[derive(Clone, Copy, Debug, PartialEq, Eq)] 838#[derive(Clone, Copy, Debug, PartialEq, Eq)]
839#[cfg_attr(feature = "defmt", derive(defmt::Format))]
743#[cfg(feature = "rp2040")] 840#[cfg(feature = "rp2040")]
744pub enum RtcClkSrc { 841pub enum RtcClkSrc {
745 /// PLL USB. 842 /// PLL USB.
@@ -791,7 +888,6 @@ pub struct RtcClkConfig {
791/// // Find parameters for 133MHz system clock from 12MHz crystal 888/// // Find parameters for 133MHz system clock from 12MHz crystal
792/// let pll_params = find_pll_params(12_000_000, 133_000_000).unwrap(); 889/// let pll_params = find_pll_params(12_000_000, 133_000_000).unwrap();
793/// ``` 890/// ```
794#[cfg(feature = "rp2040")]
795fn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> { 891fn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> {
796 // Fixed reference divider for system PLL 892 // Fixed reference divider for system PLL
797 const PLL_SYS_REFDIV: u8 = 1; 893 const PLL_SYS_REFDIV: u8 = 1;
@@ -925,18 +1021,31 @@ pub(crate) unsafe fn init(config: ClockConfig) {
925 }; 1021 };
926 CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed); 1022 CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed);
927 1023
928 // Set Core Voltage (RP2040 only), if we have config for it and we're not using the default 1024 // Set Core Voltage, if we have config for it and we're not using the default
929 #[cfg(feature = "rp2040")]
930 { 1025 {
931 let voltage = config.core_voltage; 1026 let voltage = config.core_voltage;
1027
1028 #[cfg(feature = "rp2040")]
932 let vreg = pac::VREG_AND_CHIP_RESET; 1029 let vreg = pac::VREG_AND_CHIP_RESET;
1030 #[cfg(feature = "_rp235x")]
1031 let vreg = pac::POWMAN;
1032
933 let current_vsel = vreg.vreg().read().vsel(); 1033 let current_vsel = vreg.vreg().read().vsel();
934 let target_vsel = voltage as u8; 1034 let target_vsel = voltage as u8;
935 1035
936 // If the target voltage is different from the current one, we need to change it 1036 // If the target voltage is different from the current one, we need to change it
937 if target_vsel != current_vsel { 1037 if target_vsel != current_vsel {
938 // Use modify() to preserve the HIZ and EN bits - otherwise we will disable the regulator when changing voltage 1038 // Set the voltage regulator to the target voltage
1039 #[cfg(feature = "rp2040")]
939 vreg.vreg().modify(|w| w.set_vsel(target_vsel)); 1040 vreg.vreg().modify(|w| w.set_vsel(target_vsel));
1041 #[cfg(feature = "_rp235x")]
1042 // For rp235x changes to the voltage regulator are protected by a password, see datasheet section 6.4 Power Management (POWMAN) Registers
1043 // The password is "5AFE" (0x5AFE), it must be set in the top 16 bits of the register
1044 vreg.vreg().modify(|w| {
1045 w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); // Set the password
1046 w.set_vsel(target_vsel);
1047 *w
1048 });
940 1049
941 // Wait for the voltage to stabilize. Use the provided delay or default based on voltage 1050 // Wait for the voltage to stabilize. Use the provided delay or default based on voltage
942 let settling_time_us = config.voltage_stabilization_delay_us.unwrap_or_else(|| { 1051 let settling_time_us = config.voltage_stabilization_delay_us.unwrap_or_else(|| {
@@ -955,10 +1064,17 @@ pub(crate) unsafe fn init(config: ClockConfig) {
955 } 1064 }
956 1065
957 // Only now set the BOD level. At this point the voltage is considered stable. 1066 // Only now set the BOD level. At this point the voltage is considered stable.
1067 #[cfg(feature = "rp2040")]
958 vreg.bod().write(|w| { 1068 vreg.bod().write(|w| {
959 w.set_vsel(voltage.recommended_bod()); 1069 w.set_vsel(voltage.recommended_bod());
960 w.set_en(true); // Enable brownout detection 1070 w.set_en(true); // Enable brownout detection
961 }); 1071 });
1072 #[cfg(feature = "_rp235x")]
1073 vreg.bod().write(|w| {
1074 w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); // Set the password
1075 w.set_vsel(voltage.recommended_bod());
1076 w.set_en(true); // Enable brownout detection
1077 });
962 } 1078 }
963 } 1079 }
964 1080
@@ -970,14 +1086,14 @@ pub(crate) unsafe fn init(config: ClockConfig) {
970 let pll_sys_freq = match config.sys_pll { 1086 let pll_sys_freq = match config.sys_pll {
971 Some(sys_pll_config) => match configure_pll(pac::PLL_SYS, config.hz, sys_pll_config) { 1087 Some(sys_pll_config) => match configure_pll(pac::PLL_SYS, config.hz, sys_pll_config) {
972 Ok(freq) => freq, 1088 Ok(freq) => freq,
973 Err(e) => panic!("Failed to configure PLL_SYS: {}", e), 1089 Err(e) => panic!("Failed to configure PLL_SYS: {:?}", e),
974 }, 1090 },
975 None => 0, 1091 None => 0,
976 }; 1092 };
977 let pll_usb_freq = match config.usb_pll { 1093 let pll_usb_freq = match config.usb_pll {
978 Some(usb_pll_config) => match configure_pll(pac::PLL_USB, config.hz, usb_pll_config) { 1094 Some(usb_pll_config) => match configure_pll(pac::PLL_USB, config.hz, usb_pll_config) {
979 Ok(freq) => freq, 1095 Ok(freq) => freq,
980 Err(e) => panic!("Failed to configure PLL_USB: {}", e), 1096 Err(e) => panic!("Failed to configure PLL_USB: {:?}", e),
981 }, 1097 },
982 None => 0, 1098 None => 0,
983 }; 1099 };
@@ -1283,6 +1399,58 @@ pub fn clk_rtc_freq() -> u16 {
1283 CLOCKS.rtc.load(Ordering::Relaxed) 1399 CLOCKS.rtc.load(Ordering::Relaxed)
1284} 1400}
1285 1401
1402/// The core voltage of the chip.
1403///
1404/// Returns the current core voltage or an error if the voltage register
1405/// contains an unknown value.
1406pub fn core_voltage() -> Result<CoreVoltage, ClockError> {
1407 #[cfg(feature = "rp2040")]
1408 {
1409 let vreg = pac::VREG_AND_CHIP_RESET;
1410 let vsel = vreg.vreg().read().vsel();
1411 match vsel {
1412 0b0000 => Ok(CoreVoltage::V0_80),
1413 0b0110 => Ok(CoreVoltage::V0_85),
1414 0b0111 => Ok(CoreVoltage::V0_90),
1415 0b1000 => Ok(CoreVoltage::V0_95),
1416 0b1001 => Ok(CoreVoltage::V1_00),
1417 0b1010 => Ok(CoreVoltage::V1_05),
1418 0b1011 => Ok(CoreVoltage::V1_10),
1419 0b1100 => Ok(CoreVoltage::V1_15),
1420 0b1101 => Ok(CoreVoltage::V1_20),
1421 0b1110 => Ok(CoreVoltage::V1_25),
1422 0b1111 => Ok(CoreVoltage::V1_30),
1423 _ => Err(ClockError::UnexpectedCoreVoltageRead),
1424 }
1425 }
1426
1427 #[cfg(feature = "_rp235x")]
1428 {
1429 let vreg = pac::POWMAN;
1430 let vsel = vreg.vreg().read().vsel();
1431 match vsel {
1432 0b00000 => Ok(CoreVoltage::V0_55),
1433 0b00001 => Ok(CoreVoltage::V0_60),
1434 0b00010 => Ok(CoreVoltage::V0_65),
1435 0b00011 => Ok(CoreVoltage::V0_70),
1436 0b00100 => Ok(CoreVoltage::V0_75),
1437 0b00101 => Ok(CoreVoltage::V0_80),
1438 0b00110 => Ok(CoreVoltage::V0_85),
1439 0b00111 => Ok(CoreVoltage::V0_90),
1440 0b01000 => Ok(CoreVoltage::V0_95),
1441 0b01001 => Ok(CoreVoltage::V1_00),
1442 0b01010 => Ok(CoreVoltage::V1_05),
1443 0b01011 => Ok(CoreVoltage::V1_10),
1444 0b01100 => Ok(CoreVoltage::V1_15),
1445 0b01101 => Ok(CoreVoltage::V1_20),
1446 0b01110 => Ok(CoreVoltage::V1_25),
1447 0b01111 => Ok(CoreVoltage::V1_30),
1448 _ => Err(ClockError::UnexpectedCoreVoltageRead),
1449 // see CoreVoltage: we do not support setting Voltages higher than 1.30V at this point
1450 }
1451 }
1452}
1453
1286fn start_xosc(crystal_hz: u32, delay_multiplier: u32) { 1454fn start_xosc(crystal_hz: u32, delay_multiplier: u32) {
1287 let startup_delay = (((crystal_hz / 1000) * delay_multiplier) + 128) / 256; 1455 let startup_delay = (((crystal_hz / 1000) * delay_multiplier) + 128) / 256;
1288 pac::XOSC.startup().write(|w| w.set_delay(startup_delay as u16)); 1456 pac::XOSC.startup().write(|w| w.set_delay(startup_delay as u16));
@@ -1295,7 +1463,7 @@ fn start_xosc(crystal_hz: u32, delay_multiplier: u32) {
1295 1463
1296/// PLL (Phase-Locked Loop) configuration 1464/// PLL (Phase-Locked Loop) configuration
1297#[inline(always)] 1465#[inline(always)]
1298fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result<u32, &'static str> { 1466fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result<u32, ClockError> {
1299 // Calculate reference frequency 1467 // Calculate reference frequency
1300 let ref_freq = input_freq / config.refdiv as u32; 1468 let ref_freq = input_freq / config.refdiv as u32;
1301 1469
@@ -1366,7 +1534,7 @@ fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result
1366 timeout -= 1; 1534 timeout -= 1;
1367 if timeout == 0 { 1535 if timeout == 0 {
1368 // PLL failed to lock, return 0 to indicate failure 1536 // PLL failed to lock, return 0 to indicate failure
1369 return Err("PLL failed to lock"); 1537 return Err(ClockError::PllLockTimedOut);
1370 } 1538 }
1371 } 1539 }
1372 1540
@@ -1606,7 +1774,8 @@ impl<'d, T: GpoutPin> Drop for Gpout<'d, T> {
1606pub struct RoscRng; 1774pub struct RoscRng;
1607 1775
1608impl RoscRng { 1776impl RoscRng {
1609 fn next_u8() -> u8 { 1777 /// Get a random u8
1778 pub fn next_u8() -> u8 {
1610 let random_reg = pac::ROSC.randombit(); 1779 let random_reg = pac::ROSC.randombit();
1611 let mut acc = 0; 1780 let mut acc = 0;
1612 for _ in 0..u8::BITS { 1781 for _ in 0..u8::BITS {
@@ -1615,31 +1784,65 @@ impl RoscRng {
1615 } 1784 }
1616 acc 1785 acc
1617 } 1786 }
1787
1788 /// Get a random u32
1789 pub fn next_u32(&mut self) -> u32 {
1790 rand_core_09::impls::next_u32_via_fill(self)
1791 }
1792
1793 /// Get a random u64
1794 pub fn next_u64(&mut self) -> u64 {
1795 rand_core_09::impls::next_u64_via_fill(self)
1796 }
1797
1798 /// Fill a slice with random bytes
1799 pub fn fill_bytes(&mut self, dest: &mut [u8]) {
1800 dest.fill_with(Self::next_u8)
1801 }
1618} 1802}
1619 1803
1620impl rand_core::RngCore for RoscRng { 1804impl rand_core_06::RngCore for RoscRng {
1621 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { 1805 fn next_u32(&mut self) -> u32 {
1622 Ok(self.fill_bytes(dest)) 1806 self.next_u32()
1807 }
1808
1809 fn next_u64(&mut self) -> u64 {
1810 self.next_u64()
1811 }
1812
1813 fn fill_bytes(&mut self, dest: &mut [u8]) {
1814 self.fill_bytes(dest);
1815 }
1816
1817 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core_06::Error> {
1818 self.fill_bytes(dest);
1819 Ok(())
1623 } 1820 }
1821}
1624 1822
1823impl rand_core_06::CryptoRng for RoscRng {}
1824
1825impl rand_core_09::RngCore for RoscRng {
1625 fn next_u32(&mut self) -> u32 { 1826 fn next_u32(&mut self) -> u32 {
1626 rand_core::impls::next_u32_via_fill(self) 1827 self.next_u32()
1627 } 1828 }
1628 1829
1629 fn next_u64(&mut self) -> u64 { 1830 fn next_u64(&mut self) -> u64 {
1630 rand_core::impls::next_u64_via_fill(self) 1831 self.next_u64()
1631 } 1832 }
1632 1833
1633 fn fill_bytes(&mut self, dest: &mut [u8]) { 1834 fn fill_bytes(&mut self, dest: &mut [u8]) {
1634 dest.fill_with(Self::next_u8) 1835 self.fill_bytes(dest);
1635 } 1836 }
1636} 1837}
1637 1838
1839impl rand_core_09::CryptoRng for RoscRng {}
1840
1638/// Enter the `DORMANT` sleep state. This will stop *all* internal clocks 1841/// Enter the `DORMANT` sleep state. This will stop *all* internal clocks
1639/// and can only be exited through resets, dormant-wake GPIO interrupts, 1842/// and can only be exited through resets, dormant-wake GPIO interrupts,
1640/// and RTC interrupts. If RTC is clocked from an internal clock source 1843/// and RTC interrupts. If RTC is clocked from an internal clock source
1641/// it will be stopped and not function as a wakeup source. 1844/// it will be stopped and not function as a wakeup source.
1642#[cfg(all(target_arch = "arm", feature = "rp2040"))] 1845#[cfg(all(target_arch = "arm"))]
1643pub fn dormant_sleep() { 1846pub fn dormant_sleep() {
1644 struct Set<T: Copy, F: Fn()>(Reg<T, RW>, T, F); 1847 struct Set<T: Copy, F: Fn()>(Reg<T, RW>, T, F);
1645 1848
@@ -1937,21 +2140,21 @@ mod tests {
1937 { 2140 {
1938 // Test automatic voltage scaling based on frequency 2141 // Test automatic voltage scaling based on frequency
1939 // Under 133 MHz should use default voltage (V1_10) 2142 // Under 133 MHz should use default voltage (V1_10)
1940 let config = ClockConfig::system_freq(125_000_000); 2143 let config = ClockConfig::system_freq(125_000_000).unwrap();
1941 assert_eq!(config.core_voltage, CoreVoltage::V1_10); 2144 assert_eq!(config.core_voltage, CoreVoltage::V1_10);
1942 2145
1943 // 133-200 MHz should use V1_15 2146 // 133-200 MHz should use V1_15
1944 let config = ClockConfig::system_freq(150_000_000); 2147 let config = ClockConfig::system_freq(150_000_000).unwrap();
1945 assert_eq!(config.core_voltage, CoreVoltage::V1_15); 2148 assert_eq!(config.core_voltage, CoreVoltage::V1_15);
1946 let config = ClockConfig::system_freq(200_000_000); 2149 let config = ClockConfig::system_freq(200_000_000).unwrap();
1947 assert_eq!(config.core_voltage, CoreVoltage::V1_15); 2150 assert_eq!(config.core_voltage, CoreVoltage::V1_15);
1948 2151
1949 // Above 200 MHz should use V1_25 2152 // Above 200 MHz should use V1_15
1950 let config = ClockConfig::system_freq(250_000_000); 2153 let config = ClockConfig::system_freq(250_000_000).unwrap();
1951 assert_eq!(config.core_voltage, CoreVoltage::V1_15); 2154 assert_eq!(config.core_voltage, CoreVoltage::V1_15);
1952 2155
1953 // Below 125 MHz should use V1_10 2156 // Below 125 MHz should use V1_10
1954 let config = ClockConfig::system_freq(100_000_000); 2157 let config = ClockConfig::system_freq(100_000_000).unwrap();
1955 assert_eq!(config.core_voltage, CoreVoltage::V1_10); 2158 assert_eq!(config.core_voltage, CoreVoltage::V1_10);
1956 } 2159 }
1957 } 2160 }
diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs
index ef1cd9212..8c809090e 100644
--- a/embassy-rp/src/flash.rs
+++ b/embassy-rp/src/flash.rs
@@ -482,7 +482,11 @@ mod ram_helpers {
482 /// # Safety 482 /// # Safety
483 /// 483 ///
484 /// `boot2` must contain a valid 2nd stage boot loader which can be called to re-initialize XIP mode 484 /// `boot2` must contain a valid 2nd stage boot loader which can be called to re-initialize XIP mode
485 unsafe fn flash_function_pointers_with_boot2(erase: bool, write: bool, boot2: &[u32; 64]) -> FlashFunctionPointers { 485 unsafe fn flash_function_pointers_with_boot2(
486 erase: bool,
487 write: bool,
488 boot2: &[u32; 64],
489 ) -> FlashFunctionPointers<'_> {
486 let boot2_fn_ptr = (boot2 as *const u32 as *const u8).offset(1); 490 let boot2_fn_ptr = (boot2 as *const u32 as *const u8).offset(1);
487 let boot2_fn: unsafe extern "C" fn() -> () = core::mem::transmute(boot2_fn_ptr); 491 let boot2_fn: unsafe extern "C" fn() -> () = core::mem::transmute(boot2_fn_ptr);
488 FlashFunctionPointers { 492 FlashFunctionPointers {
diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs
index af0837f6a..f79bf8948 100644
--- a/embassy-rp/src/gpio.rs
+++ b/embassy-rp/src/gpio.rs
@@ -26,6 +26,7 @@ static QSPI_WAKERS: [AtomicWaker; QSPI_PIN_COUNT] = [const { AtomicWaker::new()
26 26
27/// Represents a digital input or output level. 27/// Represents a digital input or output level.
28#[derive(Debug, Eq, PartialEq, Clone, Copy)] 28#[derive(Debug, Eq, PartialEq, Clone, Copy)]
29#[cfg_attr(feature = "defmt", derive(defmt::Format))]
29pub enum Level { 30pub enum Level {
30 /// Logical low. 31 /// Logical low.
31 Low, 32 Low,
@@ -53,6 +54,7 @@ impl From<Level> for bool {
53 54
54/// Represents a pull setting for an input. 55/// Represents a pull setting for an input.
55#[derive(Debug, Clone, Copy, Eq, PartialEq)] 56#[derive(Debug, Clone, Copy, Eq, PartialEq)]
57#[cfg_attr(feature = "defmt", derive(defmt::Format))]
56pub enum Pull { 58pub enum Pull {
57 /// No pull. 59 /// No pull.
58 None, 60 None,
@@ -64,6 +66,7 @@ pub enum Pull {
64 66
65/// Drive strength of an output 67/// Drive strength of an output
66#[derive(Debug, Eq, PartialEq)] 68#[derive(Debug, Eq, PartialEq)]
69#[cfg_attr(feature = "defmt", derive(defmt::Format))]
67pub enum Drive { 70pub enum Drive {
68 /// 2 mA drive. 71 /// 2 mA drive.
69 _2mA, 72 _2mA,
@@ -76,6 +79,7 @@ pub enum Drive {
76} 79}
77/// Slew rate of an output 80/// Slew rate of an output
78#[derive(Debug, Eq, PartialEq)] 81#[derive(Debug, Eq, PartialEq)]
82#[cfg_attr(feature = "defmt", derive(defmt::Format))]
79pub enum SlewRate { 83pub enum SlewRate {
80 /// Fast slew rate. 84 /// Fast slew rate.
81 Fast, 85 Fast,
@@ -85,6 +89,7 @@ pub enum SlewRate {
85 89
86/// A GPIO bank with up to 32 pins. 90/// A GPIO bank with up to 32 pins.
87#[derive(Debug, Eq, PartialEq)] 91#[derive(Debug, Eq, PartialEq)]
92#[cfg_attr(feature = "defmt", derive(defmt::Format))]
88pub enum Bank { 93pub enum Bank {
89 /// Bank 0. 94 /// Bank 0.
90 Bank0 = 0, 95 Bank0 = 0,
@@ -108,6 +113,8 @@ pub struct DormantWakeConfig {
108} 113}
109 114
110/// GPIO input driver. 115/// GPIO input driver.
116#[derive(Debug)]
117#[cfg_attr(feature = "defmt", derive(defmt::Format))]
111pub struct Input<'d> { 118pub struct Input<'d> {
112 pin: Flex<'d>, 119 pin: Flex<'d>,
113} 120}
@@ -146,6 +153,12 @@ impl<'d> Input<'d> {
146 self.pin.get_level() 153 self.pin.get_level()
147 } 154 }
148 155
156 /// Configure the input logic inversion of this pin.
157 #[inline]
158 pub fn set_inversion(&mut self, invert: bool) {
159 self.pin.set_input_inversion(invert)
160 }
161
149 /// Wait until the pin is high. If it is already high, return immediately. 162 /// Wait until the pin is high. If it is already high, return immediately.
150 #[inline] 163 #[inline]
151 pub async fn wait_for_high(&mut self) { 164 pub async fn wait_for_high(&mut self) {
@@ -352,6 +365,8 @@ impl<'d> Future for InputFuture<'d> {
352} 365}
353 366
354/// GPIO output driver. 367/// GPIO output driver.
368#[derive(Debug)]
369#[cfg_attr(feature = "defmt", derive(defmt::Format))]
355pub struct Output<'d> { 370pub struct Output<'d> {
356 pin: Flex<'d>, 371 pin: Flex<'d>,
357} 372}
@@ -382,6 +397,12 @@ impl<'d> Output<'d> {
382 self.pin.set_slew_rate(slew_rate) 397 self.pin.set_slew_rate(slew_rate)
383 } 398 }
384 399
400 /// Configure the output logic inversion of this pin.
401 #[inline]
402 pub fn set_inversion(&mut self, invert: bool) {
403 self.pin.set_output_inversion(invert)
404 }
405
385 /// Set the output as high. 406 /// Set the output as high.
386 #[inline] 407 #[inline]
387 pub fn set_high(&mut self) { 408 pub fn set_high(&mut self) {
@@ -433,6 +454,8 @@ impl<'d> Output<'d> {
433} 454}
434 455
435/// GPIO output open-drain. 456/// GPIO output open-drain.
457#[derive(Debug)]
458#[cfg_attr(feature = "defmt", derive(defmt::Format))]
436pub struct OutputOpenDrain<'d> { 459pub struct OutputOpenDrain<'d> {
437 pin: Flex<'d>, 460 pin: Flex<'d>,
438} 461}
@@ -580,6 +603,8 @@ impl<'d> OutputOpenDrain<'d> {
580/// This pin can be either an input or output pin. The output level register bit will remain 603/// This pin can be either an input or output pin. The output level register bit will remain
581/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output 604/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output
582/// mode. 605/// mode.
606#[derive(Debug)]
607#[cfg_attr(feature = "defmt", derive(defmt::Format))]
583pub struct Flex<'d> { 608pub struct Flex<'d> {
584 pin: Peri<'d, AnyPin>, 609 pin: Peri<'d, AnyPin>,
585} 610}
@@ -685,6 +710,30 @@ impl<'d> Flex<'d> {
685 self.pin.sio_oe().value_xor().write_value(self.bit()) 710 self.pin.sio_oe().value_xor().write_value(self.bit())
686 } 711 }
687 712
713 /// Configure the input logic inversion of this pin.
714 #[inline]
715 pub fn set_input_inversion(&mut self, invert: bool) {
716 self.pin.gpio().ctrl().modify(|w| {
717 w.set_inover(if invert {
718 pac::io::vals::Inover::INVERT
719 } else {
720 pac::io::vals::Inover::NORMAL
721 })
722 });
723 }
724
725 /// Configure the output logic inversion of this pin.
726 #[inline]
727 pub fn set_output_inversion(&mut self, invert: bool) {
728 self.pin.gpio().ctrl().modify(|w| {
729 w.set_outover(if invert {
730 pac::io::vals::Outover::INVERT
731 } else {
732 pac::io::vals::Outover::NORMAL
733 })
734 });
735 }
736
688 /// Get whether the pin input level is high. 737 /// Get whether the pin input level is high.
689 #[inline] 738 #[inline]
690 pub fn is_high(&self) -> bool { 739 pub fn is_high(&self) -> bool {
@@ -815,6 +864,8 @@ impl<'d> Drop for Flex<'d> {
815 self.pin.pad_ctrl().write(|_| {}); 864 self.pin.pad_ctrl().write(|_| {});
816 self.pin.gpio().ctrl().write(|w| { 865 self.pin.gpio().ctrl().write(|w| {
817 w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _); 866 w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _);
867 w.set_inover(pac::io::vals::Inover::NORMAL);
868 w.set_outover(pac::io::vals::Outover::NORMAL);
818 }); 869 });
819 self.pin.io().int_dormant_wake().inte(idx / 8).write_clear(|w| { 870 self.pin.io().int_dormant_wake().inte(idx / 8).write_clear(|w| {
820 w.set_edge_high(idx % 8, true); 871 w.set_edge_high(idx % 8, true);
@@ -826,6 +877,8 @@ impl<'d> Drop for Flex<'d> {
826} 877}
827 878
828/// Dormant wake driver. 879/// Dormant wake driver.
880#[derive(Debug)]
881#[cfg_attr(feature = "defmt", derive(defmt::Format))]
829pub struct DormantWake<'w> { 882pub struct DormantWake<'w> {
830 pin: Peri<'w, AnyPin>, 883 pin: Peri<'w, AnyPin>,
831 cfg: DormantWakeConfig, 884 cfg: DormantWakeConfig,
@@ -932,6 +985,8 @@ pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static {
932} 985}
933 986
934/// Type-erased GPIO pin 987/// Type-erased GPIO pin
988#[derive(Debug)]
989#[cfg_attr(feature = "defmt", derive(defmt::Format))]
935pub struct AnyPin { 990pub struct AnyPin {
936 pin_bank: u8, 991 pin_bank: u8,
937} 992}
diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs
index a983b7bc3..ffbef63be 100644
--- a/embassy-rp/src/i2c.rs
+++ b/embassy-rp/src/i2c.rs
@@ -64,18 +64,31 @@ pub enum ConfigError {
64pub struct Config { 64pub struct Config {
65 /// Frequency. 65 /// Frequency.
66 pub frequency: u32, 66 pub frequency: u32,
67 /// Enable internal pullup on SDA.
68 ///
69 /// Using external pullup resistors is recommended for I2C. If you do
70 /// have external pullups you should not enable this.
71 pub sda_pullup: bool,
72 /// Enable internal pullup on SCL.
73 ///
74 /// Using external pullup resistors is recommended for I2C. If you do
75 /// have external pullups you should not enable this.
76 pub scl_pullup: bool,
67} 77}
68
69impl Default for Config { 78impl Default for Config {
70 fn default() -> Self { 79 fn default() -> Self {
71 Self { frequency: 100_000 } 80 Self {
81 frequency: 100_000,
82 sda_pullup: true,
83 scl_pullup: true,
84 }
72 } 85 }
73} 86}
74
75/// Size of I2C FIFO. 87/// Size of I2C FIFO.
76pub const FIFO_SIZE: u8 = 16; 88pub const FIFO_SIZE: u8 = 16;
77 89
78/// I2C driver. 90/// I2C driver.
91#[derive(Debug)]
79pub struct I2c<'d, T: Instance, M: Mode> { 92pub struct I2c<'d, T: Instance, M: Mode> {
80 phantom: PhantomData<(&'d mut T, M)>, 93 phantom: PhantomData<(&'d mut T, M)>,
81} 94}
@@ -358,7 +371,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
358 } 371 }
359} 372}
360 373
361pub(crate) fn set_up_i2c_pin<P, T>(pin: &P) 374pub(crate) fn set_up_i2c_pin<P, T>(pin: &P, pullup: bool)
362where 375where
363 P: core::ops::Deref<Target = T>, 376 P: core::ops::Deref<Target = T>,
364 T: crate::gpio::Pin, 377 T: crate::gpio::Pin,
@@ -371,7 +384,7 @@ where
371 w.set_slewfast(false); 384 w.set_slewfast(false);
372 w.set_ie(true); 385 w.set_ie(true);
373 w.set_od(false); 386 w.set_od(false);
374 w.set_pue(true); 387 w.set_pue(pullup);
375 w.set_pde(false); 388 w.set_pde(false);
376 }); 389 });
377} 390}
@@ -383,8 +396,8 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
383 crate::reset::unreset_wait(reset); 396 crate::reset::unreset_wait(reset);
384 397
385 // Configure SCL & SDA pins 398 // Configure SCL & SDA pins
386 set_up_i2c_pin(&scl); 399 set_up_i2c_pin(&scl, config.scl_pullup);
387 set_up_i2c_pin(&sda); 400 set_up_i2c_pin(&sda, config.sda_pullup);
388 401
389 let mut me = Self { phantom: PhantomData }; 402 let mut me = Self { phantom: PhantomData };
390 403
diff --git a/embassy-rp/src/i2c_slave.rs b/embassy-rp/src/i2c_slave.rs
index 7bc14511d..c263047ad 100644
--- a/embassy-rp/src/i2c_slave.rs
+++ b/embassy-rp/src/i2c_slave.rs
@@ -65,6 +65,16 @@ pub struct Config {
65 pub addr: u16, 65 pub addr: u16,
66 /// Control if the peripheral should ack to and report general calls. 66 /// Control if the peripheral should ack to and report general calls.
67 pub general_call: bool, 67 pub general_call: bool,
68 /// Enable internal pullup on SDA.
69 ///
70 /// Using external pullup resistors is recommended for I2C. If you do
71 /// have external pullups you should not enable this.
72 pub sda_pullup: bool,
73 /// Enable internal pullup on SCL.
74 ///
75 /// Using external pullup resistors is recommended for I2C. If you do
76 /// have external pullups you should not enable this.
77 pub scl_pullup: bool,
68} 78}
69 79
70impl Default for Config { 80impl Default for Config {
@@ -72,6 +82,8 @@ impl Default for Config {
72 Self { 82 Self {
73 addr: 0x55, 83 addr: 0x55,
74 general_call: true, 84 general_call: true,
85 sda_pullup: true,
86 scl_pullup: true,
75 } 87 }
76 } 88 }
77} 89}
@@ -95,8 +107,8 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
95 assert!(config.addr != 0); 107 assert!(config.addr != 0);
96 108
97 // Configure SCL & SDA pins 109 // Configure SCL & SDA pins
98 set_up_i2c_pin(&scl); 110 set_up_i2c_pin(&scl, config.scl_pullup);
99 set_up_i2c_pin(&sda); 111 set_up_i2c_pin(&sda, config.sda_pullup);
100 112
101 let mut ret = Self { 113 let mut ret = Self {
102 phantom: PhantomData, 114 phantom: PhantomData,
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index f549446bc..6fb680b34 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -35,7 +35,11 @@ pub mod multicore;
35#[cfg(feature = "_rp235x")] 35#[cfg(feature = "_rp235x")]
36pub mod otp; 36pub mod otp;
37pub mod pio_programs; 37pub mod pio_programs;
38#[cfg(feature = "_rp235x")]
39pub mod psram;
38pub mod pwm; 40pub mod pwm;
41#[cfg(feature = "_rp235x")]
42pub mod qmi_cs1;
39mod reset; 43mod reset;
40pub mod rom_data; 44pub mod rom_data;
41#[cfg(feature = "rp2040")] 45#[cfg(feature = "rp2040")]
@@ -160,15 +164,18 @@ embassy_hal_internal::interrupt_mod!(
160/// ```rust,ignore 164/// ```rust,ignore
161/// use embassy_rp::{bind_interrupts, usb, peripherals}; 165/// use embassy_rp::{bind_interrupts, usb, peripherals};
162/// 166///
163/// bind_interrupts!(struct Irqs { 167/// bind_interrupts!(
164/// USBCTRL_IRQ => usb::InterruptHandler<peripherals::USB>; 168/// /// Binds the USB Interrupts.
165/// }); 169/// struct Irqs {
170/// USBCTRL_IRQ => usb::InterruptHandler<peripherals::USB>;
171/// }
172/// );
166/// ``` 173/// ```
167/// 174///
168// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. 175// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.
169#[macro_export] 176#[macro_export]
170macro_rules! bind_interrupts { 177macro_rules! bind_interrupts {
171 ($vis:vis struct $name:ident { 178 ($(#[$attr:meta])* $vis:vis struct $name:ident {
172 $( 179 $(
173 $(#[cfg($cond_irq:meta)])? 180 $(#[cfg($cond_irq:meta)])?
174 $irq:ident => $( 181 $irq:ident => $(
@@ -178,6 +185,7 @@ macro_rules! bind_interrupts {
178 )* 185 )*
179 }) => { 186 }) => {
180 #[derive(Copy, Clone)] 187 #[derive(Copy, Clone)]
188 $(#[$attr])*
181 $vis struct $name; 189 $vis struct $name;
182 190
183 $( 191 $(
@@ -185,11 +193,13 @@ macro_rules! bind_interrupts {
185 #[no_mangle] 193 #[no_mangle]
186 $(#[cfg($cond_irq)])? 194 $(#[cfg($cond_irq)])?
187 unsafe extern "C" fn $irq() { 195 unsafe extern "C" fn $irq() {
188 $( 196 unsafe {
189 $(#[cfg($cond_handler)])? 197 $(
190 <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); 198 $(#[cfg($cond_handler)])?
199 <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
191 200
192 )* 201 )*
202 }
193 } 203 }
194 204
195 $(#[cfg($cond_irq)])? 205 $(#[cfg($cond_irq)])?
@@ -375,6 +385,8 @@ embassy_hal_internal::peripherals! {
375 SPI0, 385 SPI0,
376 SPI1, 386 SPI1,
377 387
388 QMI_CS1,
389
378 I2C0, 390 I2C0,
379 I2C1, 391 I2C1,
380 392
@@ -563,7 +575,7 @@ unsafe fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> {
563 unsafe { 575 unsafe {
564 core.MPU.ctrl.write(5); // enable mpu with background default map 576 core.MPU.ctrl.write(5); // enable mpu with background default map
565 core.MPU.rbar.write(stack_bottom as u32 & !0xff); // set address 577 core.MPU.rbar.write(stack_bottom as u32 & !0xff); // set address
566 core.MPU.rlar.write(1); // enable region 578 core.MPU.rlar.write(((stack_bottom as usize + 255) as u32) | 1);
567 } 579 }
568 Ok(()) 580 Ok(())
569} 581}
diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs
index d10b6837c..adedc98ad 100644
--- a/embassy-rp/src/multicore.rs
+++ b/embassy-rp/src/multicore.rs
@@ -38,11 +38,11 @@
38//! 38//!
39//! embassy_rp::multicore::spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { 39//! embassy_rp::multicore::spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || {
40//! let executor1 = EXECUTOR1.init(Executor::new()); 40//! let executor1 = EXECUTOR1.init(Executor::new());
41//! executor1.run(|spawner| spawner.spawn(core1_task()).unwrap()); 41//! executor1.run(|spawner| spawner.spawn(core1_task().unwrap()));
42//! }); 42//! });
43//! 43//!
44//! let executor0 = EXECUTOR0.init(Executor::new()); 44//! let executor0 = EXECUTOR0.init(Executor::new());
45//! executor0.run(|spawner| spawner.spawn(core0_task()).unwrap()) 45//! executor0.run(|spawner| spawner.spawn(core0_task().unwrap()))
46//! } 46//! }
47//! ``` 47//! ```
48 48
@@ -57,6 +57,26 @@ const PAUSE_TOKEN: u32 = 0xDEADBEEF;
57const RESUME_TOKEN: u32 = !0xDEADBEEF; 57const RESUME_TOKEN: u32 = !0xDEADBEEF;
58static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false); 58static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false);
59 59
60/// Represents a partiticular CPU core (SIO_CPUID)
61#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
62#[cfg_attr(feature = "defmt", derive(defmt::Format))]
63#[repr(u8)]
64pub enum CoreId {
65 /// Core 0
66 Core0 = 0x0,
67 /// Core 1
68 Core1 = 0x1,
69}
70
71/// Gets which core we are currently executing from
72pub fn current_core() -> CoreId {
73 if pac::SIO.cpuid().read() == 0 {
74 CoreId::Core0
75 } else {
76 CoreId::Core1
77 }
78}
79
60#[inline(always)] 80#[inline(always)]
61unsafe fn core1_setup(stack_bottom: *mut usize) { 81unsafe fn core1_setup(stack_bottom: *mut usize) {
62 if install_stack_guard(stack_bottom).is_err() { 82 if install_stack_guard(stack_bottom).is_err() {
diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs
index ec698d99c..5f554dfe3 100644
--- a/embassy-rp/src/pio/mod.rs
+++ b/embassy-rp/src/pio/mod.rs
@@ -12,7 +12,7 @@ use fixed::types::extra::U8;
12use fixed::FixedU32; 12use fixed::FixedU32;
13use pio::{Program, SideSet, Wrap}; 13use pio::{Program, SideSet, Wrap};
14 14
15use crate::dma::{Channel, Transfer, Word}; 15use crate::dma::{self, Channel, Transfer, Word};
16use crate::gpio::{self, AnyPin, Drive, Level, Pull, SealedPin, SlewRate}; 16use crate::gpio::{self, AnyPin, Drive, Level, Pull, SealedPin, SlewRate};
17use crate::interrupt::typelevel::{Binding, Handler, Interrupt}; 17use crate::interrupt::typelevel::{Binding, Handler, Interrupt};
18use crate::relocate::RelocatedProgram; 18use crate::relocate::RelocatedProgram;
@@ -98,6 +98,9 @@ pub enum StatusSource {
98 TxFifoLevel = 0, 98 TxFifoLevel = 0,
99 /// All-ones if RX FIFO level < N, otherwise all-zeroes. 99 /// All-ones if RX FIFO level < N, otherwise all-zeroes.
100 RxFifoLevel = 1, 100 RxFifoLevel = 1,
101 /// All-ones if the indexed IRQ flag is raised, otherwise all-zeroes
102 #[cfg(feature = "_rp235x")]
103 Irq = 2,
101} 104}
102 105
103const RXNEMPTY_MASK: u32 = 1 << 0; 106const RXNEMPTY_MASK: u32 = 1 << 0;
@@ -278,6 +281,18 @@ impl<'l, PIO: Instance> Pin<'l, PIO> {
278 }); 281 });
279 } 282 }
280 283
284 /// Configure the output logic inversion of this pin.
285 #[inline]
286 pub fn set_output_inversion(&mut self, invert: bool) {
287 self.pin.gpio().ctrl().modify(|w| {
288 w.set_outover(if invert {
289 pac::io::vals::Outover::INVERT
290 } else {
291 pac::io::vals::Outover::NORMAL
292 })
293 });
294 }
295
281 /// Set the pin's input sync bypass. 296 /// Set the pin's input sync bypass.
282 pub fn set_input_sync_bypass(&mut self, bypass: bool) { 297 pub fn set_input_sync_bypass(&mut self, bypass: bool) {
283 let mask = 1 << self.pin(); 298 let mask = 1 << self.pin();
@@ -357,6 +372,10 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> {
357 FifoInFuture::new(self) 372 FifoInFuture::new(self)
358 } 373 }
359 374
375 fn dreq() -> crate::pac::dma::vals::TreqSel {
376 crate::pac::dma::vals::TreqSel::from(PIO::PIO_NO * 8 + SM as u8 + 4)
377 }
378
360 /// Prepare DMA transfer from RX FIFO. 379 /// Prepare DMA transfer from RX FIFO.
361 pub fn dma_pull<'a, C: Channel, W: Word>( 380 pub fn dma_pull<'a, C: Channel, W: Word>(
362 &'a mut self, 381 &'a mut self,
@@ -364,7 +383,6 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> {
364 data: &'a mut [W], 383 data: &'a mut [W],
365 bswap: bool, 384 bswap: bool,
366 ) -> Transfer<'a, C> { 385 ) -> Transfer<'a, C> {
367 let pio_no = PIO::PIO_NO;
368 let p = ch.regs(); 386 let p = ch.regs();
369 p.write_addr().write_value(data.as_ptr() as u32); 387 p.write_addr().write_value(data.as_ptr() as u32);
370 p.read_addr().write_value(PIO::PIO.rxf(SM).as_ptr() as u32); 388 p.read_addr().write_value(PIO::PIO.rxf(SM).as_ptr() as u32);
@@ -374,8 +392,7 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> {
374 p.trans_count().write(|w| w.set_count(data.len() as u32)); 392 p.trans_count().write(|w| w.set_count(data.len() as u32));
375 compiler_fence(Ordering::SeqCst); 393 compiler_fence(Ordering::SeqCst);
376 p.ctrl_trig().write(|w| { 394 p.ctrl_trig().write(|w| {
377 // Set RX DREQ for this statemachine 395 w.set_treq_sel(Self::dreq());
378 w.set_treq_sel(crate::pac::dma::vals::TreqSel::from(pio_no * 8 + SM as u8 + 4));
379 w.set_data_size(W::size()); 396 w.set_data_size(W::size());
380 w.set_chain_to(ch.number()); 397 w.set_chain_to(ch.number());
381 w.set_incr_read(false); 398 w.set_incr_read(false);
@@ -386,6 +403,36 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> {
386 compiler_fence(Ordering::SeqCst); 403 compiler_fence(Ordering::SeqCst);
387 Transfer::new(ch) 404 Transfer::new(ch)
388 } 405 }
406
407 /// Prepare a repeated DMA transfer from RX FIFO.
408 pub fn dma_pull_repeated<'a, C: Channel, W: Word>(&'a mut self, ch: Peri<'a, C>, len: usize) -> Transfer<'a, C> {
409 // This is the read version of dma::write_repeated. This allows us to
410 // discard reads from the RX FIFO through DMA.
411
412 // static mut so it gets allocated in RAM
413 static mut DUMMY: u32 = 0;
414
415 let p = ch.regs();
416 p.write_addr().write_value(core::ptr::addr_of_mut!(DUMMY) as u32);
417 p.read_addr().write_value(PIO::PIO.rxf(SM).as_ptr() as u32);
418
419 #[cfg(feature = "rp2040")]
420 p.trans_count().write(|w| *w = len as u32);
421 #[cfg(feature = "_rp235x")]
422 p.trans_count().write(|w| w.set_count(len as u32));
423
424 compiler_fence(Ordering::SeqCst);
425 p.ctrl_trig().write(|w| {
426 w.set_treq_sel(Self::dreq());
427 w.set_data_size(W::size());
428 w.set_chain_to(ch.number());
429 w.set_incr_read(false);
430 w.set_incr_write(false);
431 w.set_en(true);
432 });
433 compiler_fence(Ordering::SeqCst);
434 Transfer::new(ch)
435 }
389} 436}
390 437
391/// Type representing a state machine TX FIFO. 438/// Type representing a state machine TX FIFO.
@@ -409,7 +456,7 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> {
409 (PIO::PIO.flevel().read().0 >> (SM * 8)) as u8 & 0x0f 456 (PIO::PIO.flevel().read().0 >> (SM * 8)) as u8 & 0x0f
410 } 457 }
411 458
412 /// Check state machine has stalled on empty TX FIFO. 459 /// Check if state machine has stalled on empty TX FIFO.
413 pub fn stalled(&self) -> bool { 460 pub fn stalled(&self) -> bool {
414 let fdebug = PIO::PIO.fdebug(); 461 let fdebug = PIO::PIO.fdebug();
415 let ret = fdebug.read().txstall() & (1 << SM) != 0; 462 let ret = fdebug.read().txstall() & (1 << SM) != 0;
@@ -448,6 +495,10 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> {
448 FifoOutFuture::new(self, value) 495 FifoOutFuture::new(self, value)
449 } 496 }
450 497
498 fn dreq() -> crate::pac::dma::vals::TreqSel {
499 crate::pac::dma::vals::TreqSel::from(PIO::PIO_NO * 8 + SM as u8)
500 }
501
451 /// Prepare a DMA transfer to TX FIFO. 502 /// Prepare a DMA transfer to TX FIFO.
452 pub fn dma_push<'a, C: Channel, W: Word>( 503 pub fn dma_push<'a, C: Channel, W: Word>(
453 &'a mut self, 504 &'a mut self,
@@ -455,7 +506,6 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> {
455 data: &'a [W], 506 data: &'a [W],
456 bswap: bool, 507 bswap: bool,
457 ) -> Transfer<'a, C> { 508 ) -> Transfer<'a, C> {
458 let pio_no = PIO::PIO_NO;
459 let p = ch.regs(); 509 let p = ch.regs();
460 p.read_addr().write_value(data.as_ptr() as u32); 510 p.read_addr().write_value(data.as_ptr() as u32);
461 p.write_addr().write_value(PIO::PIO.txf(SM).as_ptr() as u32); 511 p.write_addr().write_value(PIO::PIO.txf(SM).as_ptr() as u32);
@@ -465,8 +515,7 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> {
465 p.trans_count().write(|w| w.set_count(data.len() as u32)); 515 p.trans_count().write(|w| w.set_count(data.len() as u32));
466 compiler_fence(Ordering::SeqCst); 516 compiler_fence(Ordering::SeqCst);
467 p.ctrl_trig().write(|w| { 517 p.ctrl_trig().write(|w| {
468 // Set TX DREQ for this statemachine 518 w.set_treq_sel(Self::dreq());
469 w.set_treq_sel(crate::pac::dma::vals::TreqSel::from(pio_no * 8 + SM as u8));
470 w.set_data_size(W::size()); 519 w.set_data_size(W::size());
471 w.set_chain_to(ch.number()); 520 w.set_chain_to(ch.number());
472 w.set_incr_read(true); 521 w.set_incr_read(true);
@@ -477,6 +526,11 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> {
477 compiler_fence(Ordering::SeqCst); 526 compiler_fence(Ordering::SeqCst);
478 Transfer::new(ch) 527 Transfer::new(ch)
479 } 528 }
529
530 /// Prepare a repeated DMA transfer to TX FIFO.
531 pub fn dma_push_repeated<'a, C: Channel, W: Word>(&'a mut self, ch: Peri<'a, C>, len: usize) -> Transfer<'a, C> {
532 unsafe { dma::write_repeated(ch, PIO::PIO.txf(SM).as_ptr(), len, Self::dreq()) }
533 }
480} 534}
481 535
482/// A type representing a single PIO state machine. 536/// A type representing a single PIO state machine.
@@ -736,6 +790,7 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
736 w.set_status_sel(match config.status_sel { 790 w.set_status_sel(match config.status_sel {
737 StatusSource::TxFifoLevel => pac::pio::vals::ExecctrlStatusSel::TXLEVEL, 791 StatusSource::TxFifoLevel => pac::pio::vals::ExecctrlStatusSel::TXLEVEL,
738 StatusSource::RxFifoLevel => pac::pio::vals::ExecctrlStatusSel::RXLEVEL, 792 StatusSource::RxFifoLevel => pac::pio::vals::ExecctrlStatusSel::RXLEVEL,
793 StatusSource::Irq => pac::pio::vals::ExecctrlStatusSel::IRQ,
739 }); 794 });
740 #[cfg(feature = "rp2040")] 795 #[cfg(feature = "rp2040")]
741 w.set_status_sel(match config.status_sel { 796 w.set_status_sel(match config.status_sel {
@@ -922,13 +977,27 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
922 self.set_enable(enabled); 977 self.set_enable(enabled);
923 } 978 }
924 979
980 #[cfg(feature = "rp2040")]
981 fn pin_base() -> u8 {
982 0
983 }
984
985 #[cfg(feature = "_rp235x")]
986 fn pin_base() -> u8 {
987 if PIO::PIO.gpiobase().read().gpiobase() {
988 16
989 } else {
990 0
991 }
992 }
993
925 /// Sets pin directions. This pauses the current state machine to run `SET` commands 994 /// Sets pin directions. This pauses the current state machine to run `SET` commands
926 /// and temporarily unsets the `OUT_STICKY` bit. 995 /// and temporarily unsets the `OUT_STICKY` bit.
927 pub fn set_pin_dirs(&mut self, dir: Direction, pins: &[&Pin<'d, PIO>]) { 996 pub fn set_pin_dirs(&mut self, dir: Direction, pins: &[&Pin<'d, PIO>]) {
928 self.with_paused(|sm| { 997 self.with_paused(|sm| {
929 for pin in pins { 998 for pin in pins {
930 Self::this_sm().pinctrl().write(|w| { 999 Self::this_sm().pinctrl().write(|w| {
931 w.set_set_base(pin.pin()); 1000 w.set_set_base(pin.pin() - Self::pin_base());
932 w.set_set_count(1); 1001 w.set_set_count(1);
933 }); 1002 });
934 // SET PINDIRS, (dir) 1003 // SET PINDIRS, (dir)
@@ -943,7 +1012,7 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
943 self.with_paused(|sm| { 1012 self.with_paused(|sm| {
944 for pin in pins { 1013 for pin in pins {
945 Self::this_sm().pinctrl().write(|w| { 1014 Self::this_sm().pinctrl().write(|w| {
946 w.set_set_base(pin.pin()); 1015 w.set_set_base(pin.pin() - Self::pin_base());
947 w.set_set_count(1); 1016 w.set_set_count(1);
948 }); 1017 });
949 // SET PINS, (dir) 1018 // SET PINS, (dir)
@@ -1306,6 +1375,7 @@ impl<'d, PIO: Instance> Pio<'d, PIO> {
1306 PIO::state().users.store(5, Ordering::Release); 1375 PIO::state().users.store(5, Ordering::Release);
1307 PIO::state().used_pins.store(0, Ordering::Release); 1376 PIO::state().used_pins.store(0, Ordering::Release);
1308 PIO::Interrupt::unpend(); 1377 PIO::Interrupt::unpend();
1378
1309 unsafe { PIO::Interrupt::enable() }; 1379 unsafe { PIO::Interrupt::enable() };
1310 Self { 1380 Self {
1311 common: Common { 1381 common: Common {
diff --git a/embassy-rp/src/pio_programs/i2s.rs b/embassy-rp/src/pio_programs/i2s.rs
index b967f0160..2382a3f9f 100644
--- a/embassy-rp/src/pio_programs/i2s.rs
+++ b/embassy-rp/src/pio_programs/i2s.rs
@@ -1,14 +1,106 @@
1//! Pio backed I2s output 1//! Pio backed I2s output and output drivers
2 2
3use fixed::traits::ToFixed; 3use fixed::traits::ToFixed;
4 4
5use crate::dma::{AnyChannel, Channel, Transfer}; 5use crate::dma::{AnyChannel, Channel, Transfer};
6use crate::gpio::Pull;
6use crate::pio::{ 7use crate::pio::{
7 Common, Config, Direction, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine, 8 Common, Config, Direction, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine,
8}; 9};
9use crate::Peri; 10use crate::Peri;
10 11
12/// This struct represents an i2s receiver & controller driver program
13pub struct PioI2sInProgram<'d, PIO: Instance> {
14 prg: LoadedProgram<'d, PIO>,
15}
16
17impl<'d, PIO: Instance> PioI2sInProgram<'d, PIO> {
18 /// Load the input program into the given pio
19 pub fn new(common: &mut Common<'d, PIO>) -> Self {
20 let prg = pio::pio_asm! {
21 ".side_set 2",
22 " set x, 14 side 0b01",
23 "left_data:",
24 " in pins, 1 side 0b00", // read one left-channel bit from SD
25 " jmp x-- left_data side 0b01",
26 " in pins, 1 side 0b10", // ws changes 1 clock before MSB
27 " set x, 14 side 0b11",
28 "right_data:",
29 " in pins, 1 side 0b10",
30 " jmp x-- right_data side 0b11",
31 " in pins, 1 side 0b00" // ws changes 1 clock before ms
32 };
33 let prg = common.load_program(&prg.program);
34 Self { prg }
35 }
36}
37
38/// Pio backed I2s input driver
39pub struct PioI2sIn<'d, P: Instance, const S: usize> {
40 dma: Peri<'d, AnyChannel>,
41 sm: StateMachine<'d, P, S>,
42}
43
44impl<'d, P: Instance, const S: usize> PioI2sIn<'d, P, S> {
45 /// Configure a state machine to act as both the controller (provider of SCK and WS) and receiver (of SD) for an I2S signal
46 pub fn new(
47 common: &mut Common<'d, P>,
48 mut sm: StateMachine<'d, P, S>,
49 dma: Peri<'d, impl Channel>,
50 // Whether or not to use the MCU's internal pull-down resistor, as the
51 // Pico 2 is known to have problems with the inbuilt pulldowns, many
52 // opt to just use an external pull down resistor to meet requirements of common
53 // i2s microphones such as the INMP441
54 data_pulldown: bool,
55 data_pin: Peri<'d, impl PioPin>,
56 bit_clock_pin: Peri<'d, impl PioPin>,
57 lr_clock_pin: Peri<'d, impl PioPin>,
58 sample_rate: u32,
59 bit_depth: u32,
60 channels: u32,
61 program: &PioI2sInProgram<'d, P>,
62 ) -> Self {
63 let mut data_pin = common.make_pio_pin(data_pin);
64 if data_pulldown {
65 data_pin.set_pull(Pull::Down);
66 }
67 let bit_clock_pin = common.make_pio_pin(bit_clock_pin);
68 let left_right_clock_pin = common.make_pio_pin(lr_clock_pin);
69
70 let cfg = {
71 let mut cfg = Config::default();
72 cfg.use_program(&program.prg, &[&bit_clock_pin, &left_right_clock_pin]);
73 cfg.set_in_pins(&[&data_pin]);
74 let clock_frequency = sample_rate * bit_depth * channels;
75 cfg.clock_divider = (crate::clocks::clk_sys_freq() as f64 / clock_frequency as f64 / 2.).to_fixed();
76 cfg.shift_in = ShiftConfig {
77 threshold: 32,
78 direction: ShiftDirection::Left,
79 auto_fill: true,
80 };
81 // join fifos to have twice the time to start the next dma transfer
82 cfg.fifo_join = FifoJoin::RxOnly; // both control signals are sent via side-setting
83 cfg
84 };
85 sm.set_config(&cfg);
86 sm.set_pin_dirs(Direction::In, &[&data_pin]);
87 sm.set_pin_dirs(Direction::Out, &[&left_right_clock_pin, &bit_clock_pin]);
88 sm.set_enable(true);
89
90 Self { dma: dma.into(), sm }
91 }
92
93 /// Return an in-prograss dma transfer future. Awaiting it will guarentee a complete transfer.
94 pub fn read<'b>(&'b mut self, buff: &'b mut [u32]) -> Transfer<'b, AnyChannel> {
95 self.sm.rx().dma_pull(self.dma.reborrow(), buff, false)
96 }
97}
98
11/// This struct represents an i2s output driver program 99/// This struct represents an i2s output driver program
100///
101/// The sample bit-depth is set through scratch register `Y`.
102/// `Y` has to be set to sample bit-depth - 2.
103/// (14 = 16bit, 22 = 24bit, 30 = 32bit)
12pub struct PioI2sOutProgram<'d, PIO: Instance> { 104pub struct PioI2sOutProgram<'d, PIO: Instance> {
13 prg: LoadedProgram<'d, PIO>, 105 prg: LoadedProgram<'d, PIO>,
14} 106}
@@ -17,17 +109,17 @@ impl<'d, PIO: Instance> PioI2sOutProgram<'d, PIO> {
17 /// Load the program into the given pio 109 /// Load the program into the given pio
18 pub fn new(common: &mut Common<'d, PIO>) -> Self { 110 pub fn new(common: &mut Common<'d, PIO>) -> Self {
19 let prg = pio::pio_asm!( 111 let prg = pio::pio_asm!(
20 ".side_set 2", 112 ".side_set 2", // side 0bWB - W = Word Clock, B = Bit Clock
21 " set x, 14 side 0b01", // side 0bWB - W = Word Clock, B = Bit Clock 113 " mov x, y side 0b01", // y stores sample depth - 2 (14 = 16bit, 22 = 24bit, 30 = 32bit)
22 "left_data:", 114 "left_data:",
23 " out pins, 1 side 0b00", 115 " out pins, 1 side 0b00",
24 " jmp x-- left_data side 0b01", 116 " jmp x-- left_data side 0b01",
25 " out pins 1 side 0b10", 117 " out pins, 1 side 0b10",
26 " set x, 14 side 0b11", 118 " mov x, y side 0b11",
27 "right_data:", 119 "right_data:",
28 " out pins 1 side 0b10", 120 " out pins, 1 side 0b10",
29 " jmp x-- right_data side 0b11", 121 " jmp x-- right_data side 0b11",
30 " out pins 1 side 0b00", 122 " out pins, 1 side 0b00",
31 ); 123 );
32 124
33 let prg = common.load_program(&prg.program); 125 let prg = common.load_program(&prg.program);
@@ -53,7 +145,6 @@ impl<'d, P: Instance, const S: usize> PioI2sOut<'d, P, S> {
53 lr_clock_pin: Peri<'d, impl PioPin>, 145 lr_clock_pin: Peri<'d, impl PioPin>,
54 sample_rate: u32, 146 sample_rate: u32,
55 bit_depth: u32, 147 bit_depth: u32,
56 channels: u32,
57 program: &PioI2sOutProgram<'d, P>, 148 program: &PioI2sOutProgram<'d, P>,
58 ) -> Self { 149 ) -> Self {
59 let data_pin = common.make_pio_pin(data_pin); 150 let data_pin = common.make_pio_pin(data_pin);
@@ -64,7 +155,7 @@ impl<'d, P: Instance, const S: usize> PioI2sOut<'d, P, S> {
64 let mut cfg = Config::default(); 155 let mut cfg = Config::default();
65 cfg.use_program(&program.prg, &[&bit_clock_pin, &left_right_clock_pin]); 156 cfg.use_program(&program.prg, &[&bit_clock_pin, &left_right_clock_pin]);
66 cfg.set_out_pins(&[&data_pin]); 157 cfg.set_out_pins(&[&data_pin]);
67 let clock_frequency = sample_rate * bit_depth * channels; 158 let clock_frequency = sample_rate * bit_depth * 2;
68 cfg.clock_divider = (crate::clocks::clk_sys_freq() as f64 / clock_frequency as f64 / 2.).to_fixed(); 159 cfg.clock_divider = (crate::clocks::clk_sys_freq() as f64 / clock_frequency as f64 / 2.).to_fixed();
69 cfg.shift_out = ShiftConfig { 160 cfg.shift_out = ShiftConfig {
70 threshold: 32, 161 threshold: 32,
@@ -78,6 +169,11 @@ impl<'d, P: Instance, const S: usize> PioI2sOut<'d, P, S> {
78 sm.set_config(&cfg); 169 sm.set_config(&cfg);
79 sm.set_pin_dirs(Direction::Out, &[&data_pin, &left_right_clock_pin, &bit_clock_pin]); 170 sm.set_pin_dirs(Direction::Out, &[&data_pin, &left_right_clock_pin, &bit_clock_pin]);
80 171
172 // Set the `y` register up to configure the sample depth
173 // The SM counts down to 0 and uses one clock cycle to set up the counter,
174 // which results in bit_depth - 2 as register value.
175 unsafe { sm.set_y(bit_depth - 2) };
176
81 sm.set_enable(true); 177 sm.set_enable(true);
82 178
83 Self { dma: dma.into(), sm } 179 Self { dma: dma.into(), sm }
diff --git a/embassy-rp/src/pio_programs/mod.rs b/embassy-rp/src/pio_programs/mod.rs
index 8eac328b3..d05ba3884 100644
--- a/embassy-rp/src/pio_programs/mod.rs
+++ b/embassy-rp/src/pio_programs/mod.rs
@@ -6,6 +6,7 @@ pub mod i2s;
6pub mod onewire; 6pub mod onewire;
7pub mod pwm; 7pub mod pwm;
8pub mod rotary_encoder; 8pub mod rotary_encoder;
9pub mod spi;
9pub mod stepper; 10pub mod stepper;
10pub mod uart; 11pub mod uart;
11pub mod ws2812; 12pub mod ws2812;
diff --git a/embassy-rp/src/pio_programs/onewire.rs b/embassy-rp/src/pio_programs/onewire.rs
index 287ddab41..980d0fe5f 100644
--- a/embassy-rp/src/pio_programs/onewire.rs
+++ b/embassy-rp/src/pio_programs/onewire.rs
@@ -52,7 +52,8 @@ impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> {
52 52
53 ; The low pulse was already done, we only need to delay and poll the bit in case we are reading 53 ; The low pulse was already done, we only need to delay and poll the bit in case we are reading
54 write_1: 54 write_1:
55 nop side 0 [( 6 / CLK) - 1] ; Delay before sampling the input pin 55 jmp y--, continue_1 side 0 [( 6 / CLK) - 1] ; Delay before sampling input. Always decrement y
56 continue_1:
56 in pins, 1 side 0 [(48 / CLK) - 1] ; This writes the state of the pin into the ISR 57 in pins, 1 side 0 [(48 / CLK) - 1] ; This writes the state of the pin into the ISR
57 ; Fallthrough 58 ; Fallthrough
58 59
@@ -61,9 +62,24 @@ impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> {
61 .wrap_target 62 .wrap_target
62 out x, 1 side 0 [(12 / CLK) - 1] ; Stalls if no data available in TX FIFO and OSR 63 out x, 1 side 0 [(12 / CLK) - 1] ; Stalls if no data available in TX FIFO and OSR
63 jmp x--, write_1 side 1 [( 6 / CLK) - 1] ; Do the always low part of a bit, jump to write_1 if we want to write a 1 bit 64 jmp x--, write_1 side 1 [( 6 / CLK) - 1] ; Do the always low part of a bit, jump to write_1 if we want to write a 1 bit
64 in null, 1 side 1 [(54 / CLK) - 1] ; Do the remainder of the low part of a 0 bit 65 jmp y--, continue_0 side 1 [(48 / CLK) - 1] ; Do the remainder of the low part of a 0 bit
65 ; This writes 0 into the ISR so that the shift count stays in sync 66 jmp pullup side 1 [( 6 / CLK) - 1] ; Remain low while jumping
67 continue_0:
68 in null, 1 side 1 [( 6 / CLK) - 1] ; This writes 0 into the ISR so that the shift count stays in sync
66 .wrap 69 .wrap
70
71 ; Assume that strong pullup commands always have MSB (the last bit) = 0,
72 ; since the rising edge can be used to start the operation.
73 ; That's the case for DS18B20 (44h and 48h).
74 pullup:
75 set pins, 1 side 1[( 6 / CLK) - 1] ; Drive pin high output immediately.
76 ; Strong pullup must be within 10us of rise.
77 in null, 1 side 1[( 6 / CLK) - 1] ; Keep ISR in sync. Must occur after the y--.
78 out null, 8 side 1[( 6 / CLK) - 1] ; Wait for write_bytes_pullup() delay to complete.
79 ; The delay is hundreds of ms, so done externally.
80 set pins, 0 side 0[( 6 / CLK) - 1] ; Back to open drain, pin low when driven
81 in null, 8 side 1[( 6 / CLK) - 1] ; Inform write_bytes_pullup() it's ready
82 jmp next_bit side 0[( 6 / CLK) - 1] ; Continue
67 "# 83 "#
68 ); 84 );
69 85
@@ -98,6 +114,7 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> {
98 let mut cfg = Config::default(); 114 let mut cfg = Config::default();
99 cfg.use_program(&program.prg, &[&pin]); 115 cfg.use_program(&program.prg, &[&pin]);
100 cfg.set_in_pins(&[&pin]); 116 cfg.set_in_pins(&[&pin]);
117 cfg.set_set_pins(&[&pin]);
101 118
102 let shift_cfg = ShiftConfig { 119 let shift_cfg = ShiftConfig {
103 auto_fill: true, 120 auto_fill: true,
@@ -146,6 +163,19 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> {
146 163
147 /// Write bytes to the onewire bus 164 /// Write bytes to the onewire bus
148 pub async fn write_bytes(&mut self, data: &[u8]) { 165 pub async fn write_bytes(&mut self, data: &[u8]) {
166 unsafe { self.sm.set_y(u32::MAX as u32) };
167 let (rx, tx) = self.sm.rx_tx();
168 for b in data {
169 tx.wait_push(*b as u32).await;
170
171 // Empty the buffer that is being filled with every write
172 let _ = rx.wait_pull().await;
173 }
174 }
175
176 /// Write bytes to the onewire bus, then apply a strong pullup
177 pub async fn write_bytes_pullup(&mut self, data: &[u8], pullup_time: embassy_time::Duration) {
178 unsafe { self.sm.set_y(data.len() as u32 * 8 - 1) };
149 let (rx, tx) = self.sm.rx_tx(); 179 let (rx, tx) = self.sm.rx_tx();
150 for b in data { 180 for b in data {
151 tx.wait_push(*b as u32).await; 181 tx.wait_push(*b as u32).await;
@@ -153,10 +183,19 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> {
153 // Empty the buffer that is being filled with every write 183 // Empty the buffer that is being filled with every write
154 let _ = rx.wait_pull().await; 184 let _ = rx.wait_pull().await;
155 } 185 }
186
187 // Perform the delay, usually hundreds of ms.
188 embassy_time::Timer::after(pullup_time).await;
189
190 // Signal that delay has completed
191 tx.wait_push(0 as u32).await;
192 // Wait until it's back at 0 low, open drain
193 let _ = rx.wait_pull().await;
156 } 194 }
157 195
158 /// Read bytes from the onewire bus 196 /// Read bytes from the onewire bus
159 pub async fn read_bytes(&mut self, data: &mut [u8]) { 197 pub async fn read_bytes(&mut self, data: &mut [u8]) {
198 unsafe { self.sm.set_y(u32::MAX as u32) };
160 let (rx, tx) = self.sm.rx_tx(); 199 let (rx, tx) = self.sm.rx_tx();
161 for b in data { 200 for b in data {
162 // Write all 1's so that we can read what the device responds 201 // Write all 1's so that we can read what the device responds
diff --git a/embassy-rp/src/pio_programs/spi.rs b/embassy-rp/src/pio_programs/spi.rs
new file mode 100644
index 000000000..b10fc6628
--- /dev/null
+++ b/embassy-rp/src/pio_programs/spi.rs
@@ -0,0 +1,474 @@
1//! PIO backed SPi drivers
2
3use core::marker::PhantomData;
4
5use embassy_futures::join::join;
6use embassy_hal_internal::Peri;
7use embedded_hal_02::spi::{Phase, Polarity};
8use fixed::traits::ToFixed;
9use fixed::types::extra::U8;
10
11use crate::clocks::clk_sys_freq;
12use crate::dma::{AnyChannel, Channel};
13use crate::gpio::Level;
14use crate::pio::{Common, Direction, Instance, LoadedProgram, Pin, PioPin, ShiftDirection, StateMachine};
15use crate::spi::{Async, Blocking, Config, Mode};
16
17/// This struct represents an SPI program loaded into pio instruction memory.
18struct PioSpiProgram<'d, PIO: Instance> {
19 prg: LoadedProgram<'d, PIO>,
20 phase: Phase,
21}
22
23impl<'d, PIO: Instance> PioSpiProgram<'d, PIO> {
24 /// Load the spi program into the given pio
25 pub fn new(common: &mut Common<'d, PIO>, phase: Phase) -> Self {
26 // These PIO programs are taken straight from the datasheet (3.6.1 in
27 // RP2040 datasheet, 11.6.1 in RP2350 datasheet)
28
29 // Pin assignments:
30 // - SCK is side-set pin 0
31 // - MOSI is OUT pin 0
32 // - MISO is IN pin 0
33 //
34 // Auto-push and auto-pull must be enabled, and the serial frame size is set by
35 // configuring the push/pull threshold. Shift left/right is fine, but you must
36 // justify the data yourself. This is done most conveniently for frame sizes of
37 // 8 or 16 bits by using the narrow store replication and narrow load byte
38 // picking behavior of RP2040's IO fabric.
39
40 let prg = match phase {
41 Phase::CaptureOnFirstTransition => {
42 let prg = pio::pio_asm!(
43 r#"
44 .side_set 1
45
46 ; Clock phase = 0: data is captured on the leading edge of each SCK pulse, and
47 ; transitions on the trailing edge, or some time before the first leading edge.
48
49 out pins, 1 side 0 [1] ; Stall here on empty (sideset proceeds even if
50 in pins, 1 side 1 [1] ; instruction stalls, so we stall with SCK low)
51 "#
52 );
53
54 common.load_program(&prg.program)
55 }
56 Phase::CaptureOnSecondTransition => {
57 let prg = pio::pio_asm!(
58 r#"
59 .side_set 1
60
61 ; Clock phase = 1: data transitions on the leading edge of each SCK pulse, and
62 ; is captured on the trailing edge.
63
64 out x, 1 side 0 ; Stall here on empty (keep SCK de-asserted)
65 mov pins, x side 1 [1] ; Output data, assert SCK (mov pins uses OUT mapping)
66 in pins, 1 side 0 ; Input data, de-assert SCK
67 "#
68 );
69
70 common.load_program(&prg.program)
71 }
72 };
73
74 Self { prg, phase }
75 }
76}
77
78/// PIO SPI errors.
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80#[cfg_attr(feature = "defmt", derive(defmt::Format))]
81#[non_exhaustive]
82pub enum Error {
83 // No errors for now
84}
85
86/// PIO based Spi driver.
87/// Unlike other PIO programs, the PIO SPI driver owns and holds a reference to
88/// the PIO memory it uses. This is so that it can be reconfigured at runtime if
89/// desired.
90pub struct Spi<'d, PIO: Instance, const SM: usize, M: Mode> {
91 sm: StateMachine<'d, PIO, SM>,
92 cfg: crate::pio::Config<'d, PIO>,
93 program: Option<PioSpiProgram<'d, PIO>>,
94 clk_pin: Pin<'d, PIO>,
95 tx_dma: Option<Peri<'d, AnyChannel>>,
96 rx_dma: Option<Peri<'d, AnyChannel>>,
97 phantom: PhantomData<M>,
98}
99
100impl<'d, PIO: Instance, const SM: usize, M: Mode> Spi<'d, PIO, SM, M> {
101 #[allow(clippy::too_many_arguments)]
102 fn new_inner(
103 pio: &mut Common<'d, PIO>,
104 mut sm: StateMachine<'d, PIO, SM>,
105 clk_pin: Peri<'d, impl PioPin>,
106 mosi_pin: Peri<'d, impl PioPin>,
107 miso_pin: Peri<'d, impl PioPin>,
108 tx_dma: Option<Peri<'d, AnyChannel>>,
109 rx_dma: Option<Peri<'d, AnyChannel>>,
110 config: Config,
111 ) -> Self {
112 let program = PioSpiProgram::new(pio, config.phase);
113
114 let mut clk_pin = pio.make_pio_pin(clk_pin);
115 let mosi_pin = pio.make_pio_pin(mosi_pin);
116 let miso_pin = pio.make_pio_pin(miso_pin);
117
118 if let Polarity::IdleHigh = config.polarity {
119 clk_pin.set_output_inversion(true);
120 } else {
121 clk_pin.set_output_inversion(false);
122 }
123
124 let mut cfg = crate::pio::Config::default();
125
126 cfg.use_program(&program.prg, &[&clk_pin]);
127 cfg.set_out_pins(&[&mosi_pin]);
128 cfg.set_in_pins(&[&miso_pin]);
129
130 cfg.shift_in.auto_fill = true;
131 cfg.shift_in.direction = ShiftDirection::Left;
132 cfg.shift_in.threshold = 8;
133
134 cfg.shift_out.auto_fill = true;
135 cfg.shift_out.direction = ShiftDirection::Left;
136 cfg.shift_out.threshold = 8;
137
138 cfg.clock_divider = calculate_clock_divider(config.frequency);
139
140 sm.set_config(&cfg);
141
142 sm.set_pins(Level::Low, &[&clk_pin, &mosi_pin]);
143 sm.set_pin_dirs(Direction::Out, &[&clk_pin, &mosi_pin]);
144 sm.set_pin_dirs(Direction::In, &[&miso_pin]);
145
146 sm.set_enable(true);
147
148 Self {
149 sm,
150 program: Some(program),
151 cfg,
152 clk_pin,
153 tx_dma,
154 rx_dma,
155 phantom: PhantomData,
156 }
157 }
158
159 fn blocking_read_u8(&mut self) -> Result<u8, Error> {
160 while self.sm.rx().empty() {}
161 let value = self.sm.rx().pull() as u8;
162
163 Ok(value)
164 }
165
166 fn blocking_write_u8(&mut self, v: u8) -> Result<(), Error> {
167 let value = u32::from_be_bytes([v, 0, 0, 0]);
168
169 while !self.sm.tx().try_push(value) {}
170
171 // need to clear here for flush to work correctly
172 self.sm.tx().stalled();
173
174 Ok(())
175 }
176
177 /// Read data from SPI blocking execution until done.
178 pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<(), Error> {
179 for v in data {
180 self.blocking_write_u8(0)?;
181 *v = self.blocking_read_u8()?;
182 }
183 self.flush()?;
184 Ok(())
185 }
186
187 /// Write data to SPI blocking execution until done.
188 pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> {
189 for v in data {
190 self.blocking_write_u8(*v)?;
191 let _ = self.blocking_read_u8()?;
192 }
193 self.flush()?;
194 Ok(())
195 }
196
197 /// Transfer data to SPI blocking execution until done.
198 pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
199 let len = read.len().max(write.len());
200 for i in 0..len {
201 let wb = write.get(i).copied().unwrap_or(0);
202 self.blocking_write_u8(wb)?;
203
204 let rb = self.blocking_read_u8()?;
205 if let Some(r) = read.get_mut(i) {
206 *r = rb;
207 }
208 }
209 self.flush()?;
210 Ok(())
211 }
212
213 /// Transfer data in place to SPI blocking execution until done.
214 pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> {
215 for v in data {
216 self.blocking_write_u8(*v)?;
217 *v = self.blocking_read_u8()?;
218 }
219 self.flush()?;
220 Ok(())
221 }
222
223 /// Block execution until SPI is done.
224 pub fn flush(&mut self) -> Result<(), Error> {
225 // Wait for all words in the FIFO to have been pulled by the SM
226 while !self.sm.tx().empty() {}
227
228 // Wait for last value to be written out to the wire
229 while !self.sm.tx().stalled() {}
230
231 Ok(())
232 }
233
234 /// Set SPI frequency.
235 pub fn set_frequency(&mut self, freq: u32) {
236 self.sm.set_enable(false);
237
238 let divider = calculate_clock_divider(freq);
239
240 // save into the config for later but dont use sm.set_config() since
241 // that operation is relatively more expensive than just setting the
242 // clock divider
243 self.cfg.clock_divider = divider;
244 self.sm.set_clock_divider(divider);
245
246 self.sm.set_enable(true);
247 }
248
249 /// Set SPI config.
250 ///
251 /// This operation will panic if the PIO program needs to be reloaded and
252 /// there is insufficient room. This is unlikely since the programs for each
253 /// phase only differ in size by a single instruction.
254 pub fn set_config(&mut self, pio: &mut Common<'d, PIO>, config: &Config) {
255 self.sm.set_enable(false);
256
257 self.cfg.clock_divider = calculate_clock_divider(config.frequency);
258
259 if let Polarity::IdleHigh = config.polarity {
260 self.clk_pin.set_output_inversion(true);
261 } else {
262 self.clk_pin.set_output_inversion(false);
263 }
264
265 if self.program.as_ref().unwrap().phase != config.phase {
266 let old_program = self.program.take().unwrap();
267
268 // SAFETY: the state machine is disabled while this happens
269 unsafe { pio.free_instr(old_program.prg.used_memory) };
270
271 let new_program = PioSpiProgram::new(pio, config.phase);
272
273 self.cfg.use_program(&new_program.prg, &[&self.clk_pin]);
274 self.program = Some(new_program);
275 }
276
277 self.sm.set_config(&self.cfg);
278 self.sm.restart();
279
280 self.sm.set_enable(true);
281 }
282}
283
284fn calculate_clock_divider(frequency_hz: u32) -> fixed::FixedU32<U8> {
285 // we multiply by 4 since each clock period is equal to 4 instructions
286
287 let sys_freq = clk_sys_freq().to_fixed::<fixed::FixedU64<U8>>();
288 let target_freq = (frequency_hz * 4).to_fixed::<fixed::FixedU64<U8>>();
289 (sys_freq / target_freq).to_fixed()
290}
291
292impl<'d, PIO: Instance, const SM: usize> Spi<'d, PIO, SM, Blocking> {
293 /// Create an SPI driver in blocking mode.
294 pub fn new_blocking(
295 pio: &mut Common<'d, PIO>,
296 sm: StateMachine<'d, PIO, SM>,
297 clk: Peri<'d, impl PioPin>,
298 mosi: Peri<'d, impl PioPin>,
299 miso: Peri<'d, impl PioPin>,
300 config: Config,
301 ) -> Self {
302 Self::new_inner(pio, sm, clk, mosi, miso, None, None, config)
303 }
304}
305
306impl<'d, PIO: Instance, const SM: usize> Spi<'d, PIO, SM, Async> {
307 /// Create an SPI driver in async mode supporting DMA operations.
308 #[allow(clippy::too_many_arguments)]
309 pub fn new(
310 pio: &mut Common<'d, PIO>,
311 sm: StateMachine<'d, PIO, SM>,
312 clk: Peri<'d, impl PioPin>,
313 mosi: Peri<'d, impl PioPin>,
314 miso: Peri<'d, impl PioPin>,
315 tx_dma: Peri<'d, impl Channel>,
316 rx_dma: Peri<'d, impl Channel>,
317 config: Config,
318 ) -> Self {
319 Self::new_inner(
320 pio,
321 sm,
322 clk,
323 mosi,
324 miso,
325 Some(tx_dma.into()),
326 Some(rx_dma.into()),
327 config,
328 )
329 }
330
331 /// Read data from SPI using DMA.
332 pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
333 let (rx, tx) = self.sm.rx_tx();
334
335 let len = buffer.len();
336
337 let rx_ch = self.rx_dma.as_mut().unwrap().reborrow();
338 let rx_transfer = rx.dma_pull(rx_ch, buffer, false);
339
340 let tx_ch = self.tx_dma.as_mut().unwrap().reborrow();
341 let tx_transfer = tx.dma_push_repeated::<_, u8>(tx_ch, len);
342
343 join(tx_transfer, rx_transfer).await;
344
345 Ok(())
346 }
347
348 /// Write data to SPI using DMA.
349 pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
350 let (rx, tx) = self.sm.rx_tx();
351
352 let rx_ch = self.rx_dma.as_mut().unwrap().reborrow();
353 let rx_transfer = rx.dma_pull_repeated::<_, u8>(rx_ch, buffer.len());
354
355 let tx_ch = self.tx_dma.as_mut().unwrap().reborrow();
356 let tx_transfer = tx.dma_push(tx_ch, buffer, false);
357
358 join(tx_transfer, rx_transfer).await;
359
360 Ok(())
361 }
362
363 /// Transfer data to SPI using DMA.
364 pub async fn transfer(&mut self, rx_buffer: &mut [u8], tx_buffer: &[u8]) -> Result<(), Error> {
365 self.transfer_inner(rx_buffer, tx_buffer).await
366 }
367
368 /// Transfer data in place to SPI using DMA.
369 pub async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> {
370 self.transfer_inner(words, words).await
371 }
372
373 async fn transfer_inner(&mut self, rx_buffer: *mut [u8], tx_buffer: *const [u8]) -> Result<(), Error> {
374 let (rx, tx) = self.sm.rx_tx();
375
376 let mut rx_ch = self.rx_dma.as_mut().unwrap().reborrow();
377 let rx_transfer = async {
378 rx.dma_pull(rx_ch.reborrow(), unsafe { &mut *rx_buffer }, false).await;
379
380 if tx_buffer.len() > rx_buffer.len() {
381 let read_bytes_len = tx_buffer.len() - rx_buffer.len();
382
383 rx.dma_pull_repeated::<_, u8>(rx_ch, read_bytes_len).await;
384 }
385 };
386
387 let mut tx_ch = self.tx_dma.as_mut().unwrap().reborrow();
388 let tx_transfer = async {
389 tx.dma_push(tx_ch.reborrow(), unsafe { &*tx_buffer }, false).await;
390
391 if rx_buffer.len() > tx_buffer.len() {
392 let write_bytes_len = rx_buffer.len() - tx_buffer.len();
393
394 tx.dma_push_repeated::<_, u8>(tx_ch, write_bytes_len).await;
395 }
396 };
397
398 join(tx_transfer, rx_transfer).await;
399
400 Ok(())
401 }
402}
403
404// ====================
405
406impl<'d, PIO: Instance, const SM: usize, M: Mode> embedded_hal_02::blocking::spi::Transfer<u8> for Spi<'d, PIO, SM, M> {
407 type Error = Error;
408 fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
409 self.blocking_transfer_in_place(words)?;
410 Ok(words)
411 }
412}
413
414impl<'d, PIO: Instance, const SM: usize, M: Mode> embedded_hal_02::blocking::spi::Write<u8> for Spi<'d, PIO, SM, M> {
415 type Error = Error;
416
417 fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
418 self.blocking_write(words)
419 }
420}
421
422impl embedded_hal_1::spi::Error for Error {
423 fn kind(&self) -> embedded_hal_1::spi::ErrorKind {
424 match *self {}
425 }
426}
427
428impl<'d, PIO: Instance, const SM: usize, M: Mode> embedded_hal_1::spi::ErrorType for Spi<'d, PIO, SM, M> {
429 type Error = Error;
430}
431
432impl<'d, PIO: Instance, const SM: usize, M: Mode> embedded_hal_1::spi::SpiBus<u8> for Spi<'d, PIO, SM, M> {
433 fn flush(&mut self) -> Result<(), Self::Error> {
434 Ok(())
435 }
436
437 fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
438 self.blocking_transfer(words, &[])
439 }
440
441 fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
442 self.blocking_write(words)
443 }
444
445 fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
446 self.blocking_transfer(read, write)
447 }
448
449 fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
450 self.blocking_transfer_in_place(words)
451 }
452}
453
454impl<'d, PIO: Instance, const SM: usize> embedded_hal_async::spi::SpiBus<u8> for Spi<'d, PIO, SM, Async> {
455 async fn flush(&mut self) -> Result<(), Self::Error> {
456 Ok(())
457 }
458
459 async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
460 self.write(words).await
461 }
462
463 async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
464 self.read(words).await
465 }
466
467 async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
468 self.transfer(read, write).await
469 }
470
471 async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
472 self.transfer_in_place(words).await
473 }
474}
diff --git a/embassy-rp/src/pio_programs/ws2812.rs b/embassy-rp/src/pio_programs/ws2812.rs
index 578937e11..37dd1c4e0 100644
--- a/embassy-rp/src/pio_programs/ws2812.rs
+++ b/embassy-rp/src/pio_programs/ws2812.rs
@@ -2,7 +2,7 @@
2 2
3use embassy_time::Timer; 3use embassy_time::Timer;
4use fixed::types::U24F8; 4use fixed::types::U24F8;
5use smart_leds::RGB8; 5use smart_leds::{RGB8, RGBW};
6 6
7use crate::clocks::clk_sys_freq; 7use crate::clocks::clk_sys_freq;
8use crate::dma::{AnyChannel, Channel}; 8use crate::dma::{AnyChannel, Channel};
@@ -50,7 +50,7 @@ impl<'a, PIO: Instance> PioWs2812Program<'a, PIO> {
50 } 50 }
51} 51}
52 52
53/// Pio backed ws2812 driver 53/// Pio backed RGB ws2812 driver
54/// Const N is the number of ws2812 leds attached to this pin 54/// Const N is the number of ws2812 leds attached to this pin
55pub struct PioWs2812<'d, P: Instance, const S: usize, const N: usize> { 55pub struct PioWs2812<'d, P: Instance, const S: usize, const N: usize> {
56 dma: Peri<'d, AnyChannel>, 56 dma: Peri<'d, AnyChannel>,
@@ -111,3 +111,69 @@ impl<'d, P: Instance, const S: usize, const N: usize> PioWs2812<'d, P, S, N> {
111 Timer::after_micros(55).await; 111 Timer::after_micros(55).await;
112 } 112 }
113} 113}
114
115/// Pio backed RGBW ws2812 driver
116/// This version is intended for ws2812 leds with 4 addressable lights
117/// Const N is the number of ws2812 leds attached to this pin
118pub struct RgbwPioWs2812<'d, P: Instance, const S: usize, const N: usize> {
119 dma: Peri<'d, AnyChannel>,
120 sm: StateMachine<'d, P, S>,
121}
122
123impl<'d, P: Instance, const S: usize, const N: usize> RgbwPioWs2812<'d, P, S, N> {
124 /// Configure a pio state machine to use the loaded ws2812 program.
125 pub fn new(
126 pio: &mut Common<'d, P>,
127 mut sm: StateMachine<'d, P, S>,
128 dma: Peri<'d, impl Channel>,
129 pin: Peri<'d, impl PioPin>,
130 program: &PioWs2812Program<'d, P>,
131 ) -> Self {
132 // Setup sm0
133 let mut cfg = Config::default();
134
135 // Pin config
136 let out_pin = pio.make_pio_pin(pin);
137 cfg.set_out_pins(&[&out_pin]);
138 cfg.set_set_pins(&[&out_pin]);
139
140 cfg.use_program(&program.prg, &[&out_pin]);
141
142 // Clock config, measured in kHz to avoid overflows
143 let clock_freq = U24F8::from_num(clk_sys_freq() / 1000);
144 let ws2812_freq = U24F8::from_num(800);
145 let bit_freq = ws2812_freq * CYCLES_PER_BIT;
146 cfg.clock_divider = clock_freq / bit_freq;
147
148 // FIFO config
149 cfg.fifo_join = FifoJoin::TxOnly;
150 cfg.shift_out = ShiftConfig {
151 auto_fill: true,
152 threshold: 32,
153 direction: ShiftDirection::Left,
154 };
155
156 sm.set_config(&cfg);
157 sm.set_enable(true);
158
159 Self { dma: dma.into(), sm }
160 }
161
162 /// Write a buffer of [smart_leds::RGBW] to the ws2812 string
163 pub async fn write(&mut self, colors: &[RGBW<u8>; N]) {
164 // Precompute the word bytes from the colors
165 let mut words = [0u32; N];
166 for i in 0..N {
167 let word = (u32::from(colors[i].g) << 24)
168 | (u32::from(colors[i].r) << 16)
169 | (u32::from(colors[i].b) << 8)
170 | u32::from(colors[i].a.0);
171 words[i] = word;
172 }
173
174 // DMA transfer
175 self.sm.tx().dma_push(self.dma.reborrow(), &words, false).await;
176
177 Timer::after_micros(55).await;
178 }
179}
diff --git a/embassy-rp/src/psram.rs b/embassy-rp/src/psram.rs
new file mode 100644
index 000000000..ae43dd5aa
--- /dev/null
+++ b/embassy-rp/src/psram.rs
@@ -0,0 +1,682 @@
1//! PSRAM driver for APS6404L and compatible devices
2//!
3//! This driver provides support for PSRAM (Pseudo-Static RAM) devices connected via QMI CS1.
4//! It handles device verification, initialization, and memory-mapped access configuration.
5//!
6//! This driver is only available on RP235x chips as it requires the QMI CS1 peripheral.
7
8// Credit: Initially based on https://github.com/Altaflux/gb-rp2350 (also licensed Apache 2.0 + MIT).
9// Copyright (c) Altaflux
10
11#![cfg(feature = "_rp235x")]
12
13use critical_section::{acquire, release, CriticalSection, RestoreState};
14
15use crate::pac;
16use crate::qmi_cs1::QmiCs1;
17
18/// PSRAM errors.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[cfg_attr(feature = "defmt", derive(defmt::Format))]
21#[non_exhaustive]
22pub enum Error {
23 /// PSRAM device is not detected or not supported
24 DeviceNotFound,
25 /// Invalid configuration
26 InvalidConfig,
27 /// Detected PSRAM size does not match the expected size
28 SizeMismatch,
29}
30
31/// PSRAM device verification type.
32#[derive(Clone, Copy)]
33pub enum VerificationType {
34 /// Skip device verification
35 None,
36 /// Verify as APS6404L device
37 Aps6404l,
38}
39
40/// Memory configuration.
41#[derive(Clone)]
42pub struct Config {
43 /// System clock frequency in Hz
44 pub clock_hz: u32,
45 /// Maximum memory operating frequency in Hz
46 pub max_mem_freq: u32,
47 /// Maximum CS assert time in microseconds (must be <= 8 us)
48 pub max_select_us: u32,
49 /// Minimum CS deassert time in nanoseconds (must be >= 18 ns)
50 pub min_deselect_ns: u32,
51 /// Cooldown period between operations (in SCLK cycles)
52 pub cooldown: u8,
53 /// Page break size for memory operations
54 pub page_break: PageBreak,
55 /// Clock divisor for direct mode operations during initialization
56 pub init_clkdiv: u8,
57 /// Enter Quad Mode command
58 pub enter_quad_cmd: Option<u8>,
59 /// Quad Read command (fast read with 4-bit data)
60 pub quad_read_cmd: u8,
61 /// Quad Write command (page program with 4-bit data)
62 pub quad_write_cmd: Option<u8>,
63 /// Number of dummy cycles for quad read operations
64 pub dummy_cycles: u8,
65 /// Read format configuration
66 pub read_format: FormatConfig,
67 /// Write format configuration
68 pub write_format: Option<FormatConfig>,
69 /// Expected memory size in bytes
70 pub mem_size: usize,
71 /// Device verification type
72 pub verification_type: VerificationType,
73 /// Whether the memory is writable via XIP (e.g., PSRAM vs. read-only flash)
74 pub xip_writable: bool,
75}
76
77/// Page break configuration for memory window operations.
78#[derive(Clone, Copy)]
79pub enum PageBreak {
80 /// No page breaks
81 None,
82 /// Break at 256-byte boundaries
83 _256,
84 /// Break at 1024-byte boundaries
85 _1024,
86 /// Break at 4096-byte boundaries
87 _4096,
88}
89
90/// Format configuration for read/write operations.
91#[derive(Clone)]
92pub struct FormatConfig {
93 /// Width of command prefix phase
94 pub prefix_width: Width,
95 /// Width of address phase
96 pub addr_width: Width,
97 /// Width of command suffix phase
98 pub suffix_width: Width,
99 /// Width of dummy/turnaround phase
100 pub dummy_width: Width,
101 /// Width of data phase
102 pub data_width: Width,
103 /// Length of prefix (None or 8 bits)
104 pub prefix_len: bool,
105 /// Length of suffix (None or 8 bits)
106 pub suffix_len: bool,
107}
108
109/// Interface width for different phases of SPI transfer.
110#[derive(Clone, Copy)]
111pub enum Width {
112 /// Single-bit (standard SPI)
113 Single,
114 /// Dual-bit (2 data lines)
115 Dual,
116 /// Quad-bit (4 data lines)
117 Quad,
118}
119
120impl Default for Config {
121 fn default() -> Self {
122 Self::aps6404l()
123 }
124}
125
126impl Config {
127 /// Create configuration for APS6404L PSRAM.
128 pub fn aps6404l() -> Self {
129 Self {
130 clock_hz: 125_000_000, // Default to 125MHz
131 max_mem_freq: 133_000_000, // APS6404L max frequency
132 max_select_us: 8, // 8 microseconds max CS assert
133 min_deselect_ns: 18, // 18 nanoseconds min CS deassert
134 cooldown: 1, // 1 SCLK cycle cooldown
135 page_break: PageBreak::_1024, // 1024-byte page boundaries
136 init_clkdiv: 10, // Medium clock for initialization
137 enter_quad_cmd: Some(0x35), // Enter Quad Mode
138 quad_read_cmd: 0xEB, // Fast Quad Read
139 quad_write_cmd: Some(0x38), // Quad Page Program
140 dummy_cycles: 24, // 24 dummy cycles for quad read
141 read_format: FormatConfig {
142 prefix_width: Width::Quad,
143 addr_width: Width::Quad,
144 suffix_width: Width::Quad,
145 dummy_width: Width::Quad,
146 data_width: Width::Quad,
147 prefix_len: true, // 8-bit prefix
148 suffix_len: false, // No suffix
149 },
150 write_format: Some(FormatConfig {
151 prefix_width: Width::Quad,
152 addr_width: Width::Quad,
153 suffix_width: Width::Quad,
154 dummy_width: Width::Quad,
155 data_width: Width::Quad,
156 prefix_len: true, // 8-bit prefix
157 suffix_len: false, // No suffix
158 }),
159 mem_size: 8 * 1024 * 1024, // 8MB for APS6404L
160 verification_type: VerificationType::Aps6404l,
161 xip_writable: true, // PSRAM is writable
162 }
163 }
164
165 /// Create a custom memory configuration.
166 pub fn custom(
167 clock_hz: u32,
168 max_mem_freq: u32,
169 max_select_us: u32,
170 min_deselect_ns: u32,
171 cooldown: u8,
172 page_break: PageBreak,
173 init_clkdiv: u8,
174 enter_quad_cmd: Option<u8>,
175 quad_read_cmd: u8,
176 quad_write_cmd: Option<u8>,
177 dummy_cycles: u8,
178 read_format: FormatConfig,
179 write_format: Option<FormatConfig>,
180 mem_size: usize,
181 verification_type: VerificationType,
182 xip_writable: bool,
183 ) -> Self {
184 Self {
185 clock_hz,
186 max_mem_freq,
187 max_select_us,
188 min_deselect_ns,
189 cooldown,
190 page_break,
191 init_clkdiv,
192 enter_quad_cmd,
193 quad_read_cmd,
194 quad_write_cmd,
195 dummy_cycles,
196 read_format,
197 write_format,
198 mem_size,
199 verification_type,
200 xip_writable,
201 }
202 }
203}
204
205/// PSRAM driver.
206pub struct Psram<'d> {
207 #[allow(dead_code)]
208 qmi_cs1: QmiCs1<'d>,
209 size: usize,
210}
211
212impl<'d> Psram<'d> {
213 /// Create a new PSRAM driver instance.
214 ///
215 /// This will detect the PSRAM device and configure it for memory-mapped access.
216 pub fn new(qmi_cs1: QmiCs1<'d>, config: Config) -> Result<Self, Error> {
217 let qmi = pac::QMI;
218 let xip = pac::XIP_CTRL;
219
220 // Verify PSRAM device if requested
221 match config.verification_type {
222 VerificationType::Aps6404l => {
223 Self::verify_aps6404l(&qmi, config.mem_size)?;
224 debug!("APS6404L PSRAM verified, size: {:#x}", config.mem_size);
225 }
226 VerificationType::None => {
227 debug!("Skipping PSRAM verification, assuming size: {:#x}", config.mem_size);
228 }
229 }
230
231 // Initialize PSRAM with proper timing
232 Self::init_psram(&qmi, &xip, &config)?;
233
234 Ok(Self {
235 qmi_cs1,
236 size: config.mem_size,
237 })
238 }
239
240 /// Get the detected PSRAM size in bytes.
241 pub fn size(&self) -> usize {
242 self.size
243 }
244
245 /// Get the base address for memory-mapped access.
246 ///
247 /// After initialization, PSRAM can be accessed directly through memory mapping.
248 /// The base address for CS1 is typically 0x11000000.
249 pub fn base_address(&self) -> *mut u8 {
250 0x1100_0000 as *mut u8
251 }
252
253 /// Verify APS6404L PSRAM device matches expected configuration.
254 #[link_section = ".data.ram_func"]
255 #[inline(never)]
256 fn verify_aps6404l(qmi: &pac::qmi::Qmi, expected_size: usize) -> Result<(), Error> {
257 // APS6404L-specific constants
258 const EXPECTED_KGD: u8 = 0x5D;
259 crate::multicore::pause_core1();
260 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
261
262 {
263 // Helper for making sure `release` is called even if `f` panics.
264 struct Guard {
265 state: RestoreState,
266 }
267
268 impl Drop for Guard {
269 #[inline(always)]
270 fn drop(&mut self) {
271 unsafe { release(self.state) }
272 }
273 }
274
275 let state = unsafe { acquire() };
276 let _guard = Guard { state };
277
278 let _cs = unsafe { CriticalSection::new() };
279
280 let (kgd, eid) = unsafe { Self::read_aps6404l_kgd_eid(qmi) };
281
282 let mut detected_size: u32 = 0;
283 if kgd == EXPECTED_KGD as u32 {
284 detected_size = 1024 * 1024;
285 let size_id = eid >> 5;
286 if eid == 0x26 || size_id == 2 {
287 // APS6404L-3SQR-SN or 8MB variants
288 detected_size *= 8;
289 } else if size_id == 0 {
290 detected_size *= 2;
291 } else if size_id == 1 {
292 detected_size *= 4;
293 }
294 }
295
296 // Verify the detected size matches the expected size
297 if detected_size as usize != expected_size {
298 return Err(Error::SizeMismatch);
299 }
300
301 Ok(())
302 }?;
303
304 crate::multicore::resume_core1();
305
306 Ok(())
307 }
308
309 #[link_section = ".data.ram_func"]
310 #[inline(never)]
311 unsafe fn read_aps6404l_kgd_eid(qmi: &pac::qmi::Qmi) -> (u32, u32) {
312 const RESET_ENABLE_CMD: u8 = 0xf5;
313 const READ_ID_CMD: u8 = 0x9f;
314
315 #[allow(unused_assignments)]
316 let mut kgd: u32 = 0;
317 #[allow(unused_assignments)]
318 let mut eid: u32 = 0;
319
320 let qmi_base = qmi.as_ptr() as usize;
321
322 #[cfg(target_arch = "arm")]
323 core::arch::asm!(
324 // Configure DIRECT_CSR: shift clkdiv (30) to bits 29:22 and set EN (bit 0)
325 "movs {temp}, #30",
326 "lsls {temp}, {temp}, #22",
327 "orr {temp}, {temp}, #1", // Set EN bit
328 "str {temp}, [{qmi_base}]",
329
330 // Poll for BUSY to clear before first operation
331 "1:",
332 "ldr {temp}, [{qmi_base}]",
333 "lsls {temp}, {temp}, #30", // Shift BUSY bit to sign position
334 "bmi 1b", // Branch if negative (BUSY = 1)
335
336 // Assert CS1N (bit 3)
337 "ldr {temp}, [{qmi_base}]",
338 "orr {temp}, {temp}, #8", // Set ASSERT_CS1N bit (bit 3)
339 "str {temp}, [{qmi_base}]",
340
341 // Transmit RESET_ENABLE_CMD as quad
342 // DIRECT_TX: OE=1 (bit 19), IWIDTH=2 (bits 17:16), DATA=RESET_ENABLE_CMD
343 "movs {temp}, {reset_enable_cmd}",
344 "orr {temp}, {temp}, #0x80000", // Set OE (bit 19)
345 "orr {temp}, {temp}, #0x20000", // Set IWIDTH=2 (quad, bits 17:16)
346 "str {temp}, [{qmi_base}, #4]", // Store to DIRECT_TX
347
348 // Wait for BUSY to clear
349 "2:",
350 "ldr {temp}, [{qmi_base}]",
351 "lsls {temp}, {temp}, #30",
352 "bmi 2b",
353
354 // Read and discard RX data
355 "ldr {temp}, [{qmi_base}, #8]",
356
357 // Deassert CS1N
358 "ldr {temp}, [{qmi_base}]",
359 "bic {temp}, {temp}, #8", // Clear ASSERT_CS1N bit
360 "str {temp}, [{qmi_base}]",
361
362 // Assert CS1N again
363 "ldr {temp}, [{qmi_base}]",
364 "orr {temp}, {temp}, #8", // Set ASSERT_CS1N bit
365 "str {temp}, [{qmi_base}]",
366
367 // Read ID loop (7 iterations)
368 "movs {counter}, #0", // Initialize counter
369
370 "3:", // Loop start
371 "cmp {counter}, #0",
372 "bne 4f", // If not first iteration, send 0xFF
373
374 // First iteration: send READ_ID_CMD
375 "movs {temp}, {read_id_cmd}",
376 "b 5f",
377 "4:", // Other iterations: send 0xFF
378 "movs {temp}, #0xFF",
379 "5:",
380 "str {temp}, [{qmi_base}, #4]", // Store to DIRECT_TX
381
382 // Wait for TXEMPTY
383 "6:",
384 "ldr {temp}, [{qmi_base}]",
385 "lsls {temp}, {temp}, #20", // Shift TXEMPTY (bit 11) to bit 31
386 "bpl 6b", // Branch if positive (TXEMPTY = 0)
387
388 // Wait for BUSY to clear
389 "7:",
390 "ldr {temp}, [{qmi_base}]",
391 "lsls {temp}, {temp}, #30", // Shift BUSY bit to sign position
392 "bmi 7b", // Branch if negative (BUSY = 1)
393
394 // Read RX data
395 "ldr {temp}, [{qmi_base}, #8]",
396 "uxth {temp}, {temp}", // Extract lower 16 bits
397
398 // Store KGD or EID based on iteration
399 "cmp {counter}, #5",
400 "bne 8f",
401 "mov {kgd}, {temp}", // Store KGD
402 "b 9f",
403 "8:",
404 "cmp {counter}, #6",
405 "bne 9f",
406 "mov {eid}, {temp}", // Store EID
407
408 "9:",
409 "adds {counter}, #1",
410 "cmp {counter}, #7",
411 "blt 3b", // Continue loop if counter < 7
412
413 // Disable direct mode: clear EN and ASSERT_CS1N
414 "movs {temp}, #0",
415 "str {temp}, [{qmi_base}]",
416
417 // Memory barriers
418 "dmb",
419 "dsb",
420 "isb",
421 qmi_base = in(reg) qmi_base,
422 temp = out(reg) _,
423 counter = out(reg) _,
424 kgd = out(reg) kgd,
425 eid = out(reg) eid,
426 reset_enable_cmd = const RESET_ENABLE_CMD as u32,
427 read_id_cmd = const READ_ID_CMD as u32,
428 options(nostack),
429 );
430
431 #[cfg(target_arch = "riscv32")]
432 unimplemented!("APS6404L PSRAM verification not implemented for RISC-V");
433
434 (kgd, eid)
435 }
436
437 /// Initialize PSRAM with proper timing.
438 #[link_section = ".data.ram_func"]
439 #[inline(never)]
440 fn init_psram(qmi: &pac::qmi::Qmi, xip_ctrl: &pac::xip_ctrl::XipCtrl, config: &Config) -> Result<(), Error> {
441 // Set PSRAM timing for APS6404
442 //
443 // Using an rxdelay equal to the divisor isn't enough when running the APS6404 close to 133 MHz.
444 // So: don't allow running at divisor 1 above 100 MHz (because delay of 2 would be too late),
445 // and add an extra 1 to the rxdelay if the divided clock is > 100 MHz (i.e., sys clock > 200 MHz).
446 let clock_hz = config.clock_hz;
447 let max_psram_freq = config.max_mem_freq;
448
449 let mut divisor: u32 = (clock_hz + max_psram_freq - 1) / max_psram_freq;
450 if divisor == 1 && clock_hz > 100_000_000 {
451 divisor = 2;
452 }
453 let mut rxdelay: u32 = divisor;
454 if clock_hz / divisor > 100_000_000 {
455 rxdelay += 1;
456 }
457
458 // - Max select must be <= 8 us. The value is given in multiples of 64 system clocks.
459 // - Min deselect must be >= 18ns. The value is given in system clock cycles - ceil(divisor / 2).
460 let clock_period_fs: u64 = 1_000_000_000_000_000_u64 / u64::from(clock_hz);
461 let max_select: u8 = ((config.max_select_us as u64 * 1_000_000) / clock_period_fs) as u8;
462 let min_deselect: u32 = ((config.min_deselect_ns as u64 * 1_000_000 + (clock_period_fs - 1)) / clock_period_fs
463 - u64::from(divisor + 1) / 2) as u32;
464
465 crate::multicore::pause_core1();
466 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
467
468 if let Some(enter_quad_cmd) = config.enter_quad_cmd {
469 // Helper for making sure `release` is called even if `f` panics.
470 struct Guard {
471 state: RestoreState,
472 }
473
474 impl Drop for Guard {
475 #[inline(always)]
476 fn drop(&mut self) {
477 unsafe { release(self.state) }
478 }
479 }
480
481 let state = unsafe { acquire() };
482 let _guard = Guard { state };
483
484 let _cs = unsafe { CriticalSection::new() };
485
486 unsafe { Self::direct_csr_send_init_command(config, enter_quad_cmd) };
487
488 qmi.mem(1).timing().write(|w| {
489 w.set_cooldown(config.cooldown);
490 w.set_pagebreak(match config.page_break {
491 PageBreak::None => pac::qmi::vals::Pagebreak::NONE,
492 PageBreak::_256 => pac::qmi::vals::Pagebreak::_256,
493 PageBreak::_1024 => pac::qmi::vals::Pagebreak::_1024,
494 PageBreak::_4096 => pac::qmi::vals::Pagebreak::_4096,
495 });
496 w.set_max_select(max_select);
497 w.set_min_deselect(min_deselect as u8);
498 w.set_rxdelay(rxdelay as u8);
499 w.set_clkdiv(divisor as u8);
500 });
501
502 // Set PSRAM commands and formats
503 qmi.mem(1).rfmt().write(|w| {
504 let width_to_pac = |w: Width| match w {
505 Width::Single => pac::qmi::vals::PrefixWidth::S,
506 Width::Dual => pac::qmi::vals::PrefixWidth::D,
507 Width::Quad => pac::qmi::vals::PrefixWidth::Q,
508 };
509
510 w.set_prefix_width(width_to_pac(config.read_format.prefix_width));
511 w.set_addr_width(match config.read_format.addr_width {
512 Width::Single => pac::qmi::vals::AddrWidth::S,
513 Width::Dual => pac::qmi::vals::AddrWidth::D,
514 Width::Quad => pac::qmi::vals::AddrWidth::Q,
515 });
516 w.set_suffix_width(match config.read_format.suffix_width {
517 Width::Single => pac::qmi::vals::SuffixWidth::S,
518 Width::Dual => pac::qmi::vals::SuffixWidth::D,
519 Width::Quad => pac::qmi::vals::SuffixWidth::Q,
520 });
521 w.set_dummy_width(match config.read_format.dummy_width {
522 Width::Single => pac::qmi::vals::DummyWidth::S,
523 Width::Dual => pac::qmi::vals::DummyWidth::D,
524 Width::Quad => pac::qmi::vals::DummyWidth::Q,
525 });
526 w.set_data_width(match config.read_format.data_width {
527 Width::Single => pac::qmi::vals::DataWidth::S,
528 Width::Dual => pac::qmi::vals::DataWidth::D,
529 Width::Quad => pac::qmi::vals::DataWidth::Q,
530 });
531 w.set_prefix_len(if config.read_format.prefix_len {
532 pac::qmi::vals::PrefixLen::_8
533 } else {
534 pac::qmi::vals::PrefixLen::NONE
535 });
536 w.set_suffix_len(if config.read_format.suffix_len {
537 pac::qmi::vals::SuffixLen::_8
538 } else {
539 pac::qmi::vals::SuffixLen::NONE
540 });
541 w.set_dummy_len(match config.dummy_cycles {
542 0 => pac::qmi::vals::DummyLen::NONE,
543 4 => pac::qmi::vals::DummyLen::_4,
544 8 => pac::qmi::vals::DummyLen::_8,
545 12 => pac::qmi::vals::DummyLen::_12,
546 16 => pac::qmi::vals::DummyLen::_16,
547 20 => pac::qmi::vals::DummyLen::_20,
548 24 => pac::qmi::vals::DummyLen::_24,
549 28 => pac::qmi::vals::DummyLen::_28,
550 _ => pac::qmi::vals::DummyLen::_24, // Default to 24
551 });
552 });
553
554 qmi.mem(1).rcmd().write(|w| w.set_prefix(config.quad_read_cmd));
555
556 if let Some(ref write_format) = config.write_format {
557 qmi.mem(1).wfmt().write(|w| {
558 w.set_prefix_width(match write_format.prefix_width {
559 Width::Single => pac::qmi::vals::PrefixWidth::S,
560 Width::Dual => pac::qmi::vals::PrefixWidth::D,
561 Width::Quad => pac::qmi::vals::PrefixWidth::Q,
562 });
563 w.set_addr_width(match write_format.addr_width {
564 Width::Single => pac::qmi::vals::AddrWidth::S,
565 Width::Dual => pac::qmi::vals::AddrWidth::D,
566 Width::Quad => pac::qmi::vals::AddrWidth::Q,
567 });
568 w.set_suffix_width(match write_format.suffix_width {
569 Width::Single => pac::qmi::vals::SuffixWidth::S,
570 Width::Dual => pac::qmi::vals::SuffixWidth::D,
571 Width::Quad => pac::qmi::vals::SuffixWidth::Q,
572 });
573 w.set_dummy_width(match write_format.dummy_width {
574 Width::Single => pac::qmi::vals::DummyWidth::S,
575 Width::Dual => pac::qmi::vals::DummyWidth::D,
576 Width::Quad => pac::qmi::vals::DummyWidth::Q,
577 });
578 w.set_data_width(match write_format.data_width {
579 Width::Single => pac::qmi::vals::DataWidth::S,
580 Width::Dual => pac::qmi::vals::DataWidth::D,
581 Width::Quad => pac::qmi::vals::DataWidth::Q,
582 });
583 w.set_prefix_len(if write_format.prefix_len {
584 pac::qmi::vals::PrefixLen::_8
585 } else {
586 pac::qmi::vals::PrefixLen::NONE
587 });
588 w.set_suffix_len(if write_format.suffix_len {
589 pac::qmi::vals::SuffixLen::_8
590 } else {
591 pac::qmi::vals::SuffixLen::NONE
592 });
593 });
594 }
595
596 if let Some(quad_write_cmd) = config.quad_write_cmd {
597 qmi.mem(1).wcmd().write(|w| w.set_prefix(quad_write_cmd));
598 }
599
600 if config.xip_writable {
601 // Enable XIP writable mode for PSRAM
602 xip_ctrl.ctrl().modify(|w| w.set_writable_m1(true));
603 } else {
604 // Disable XIP writable mode
605 xip_ctrl.ctrl().modify(|w| w.set_writable_m1(false));
606 }
607 }
608 crate::multicore::resume_core1();
609
610 Ok(())
611 }
612
613 #[link_section = ".data.ram_func"]
614 #[inline(never)]
615 unsafe fn direct_csr_send_init_command(config: &Config, init_cmd: u8) {
616 #[cfg(target_arch = "arm")]
617 core::arch::asm!(
618 // Full memory barrier
619 "dmb",
620 "dsb",
621 "isb",
622
623 // Configure QMI Direct CSR register
624 // Load base address of QMI (0x400D0000)
625 "movw {base}, #0x0000",
626 "movt {base}, #0x400D",
627
628 // Load init_clkdiv and shift to bits 29:22
629 "lsl {temp}, {clkdiv}, #22",
630
631 // OR with EN (bit 0) and AUTO_CS1N (bit 7)
632 "orr {temp}, {temp}, #0x81",
633
634 // Store to DIRECT_CSR register
635 "str {temp}, [{base}, #0]",
636
637 // Memory barrier
638 "dmb",
639
640 // First busy wait loop
641 "1:",
642 "ldr {temp}, [{base}, #0]", // Load DIRECT_CSR
643 "tst {temp}, #0x2", // Test BUSY bit (bit 1)
644 "bne 1b", // Branch if busy
645
646 // Write to Direct TX register
647 "mov {temp}, {enter_quad_cmd}",
648
649 // OR with NOPUSH (bit 20)
650 "orr {temp}, {temp}, #0x100000",
651
652 // Store to DIRECT_TX register (offset 0x4)
653 "str {temp}, [{base}, #4]",
654
655 // Memory barrier
656 "dmb",
657
658 // Second busy wait loop
659 "2:",
660 "ldr {temp}, [{base}, #0]", // Load DIRECT_CSR
661 "tst {temp}, #0x2", // Test BUSY bit (bit 1)
662 "bne 2b", // Branch if busy
663
664 // Disable Direct CSR
665 "mov {temp}, #0",
666 "str {temp}, [{base}, #0]", // Clear DIRECT_CSR register
667
668 // Full memory barrier to ensure no prefetching
669 "dmb",
670 "dsb",
671 "isb",
672 base = out(reg) _,
673 temp = out(reg) _,
674 clkdiv = in(reg) config.init_clkdiv as u32,
675 enter_quad_cmd = in(reg) u32::from(init_cmd),
676 options(nostack),
677 );
678
679 #[cfg(target_arch = "riscv32")]
680 unimplemented!("Direct CSR command sending is not implemented for RISC-V yet");
681 }
682}
diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs
index 6dfb90fef..1e1ccc4c6 100644
--- a/embassy-rp/src/pwm.rs
+++ b/embassy-rp/src/pwm.rs
@@ -464,6 +464,10 @@ impl<'d> Drop for Pwm<'d> {
464 pac::PWM.ch(self.slice).csr().write_clear(|w| w.set_en(false)); 464 pac::PWM.ch(self.slice).csr().write_clear(|w| w.set_en(false));
465 if let Some(pin) = &self.pin_a { 465 if let Some(pin) = &self.pin_a {
466 pin.gpio().ctrl().write(|w| w.set_funcsel(31)); 466 pin.gpio().ctrl().write(|w| w.set_funcsel(31));
467 // Enable pin PULL-DOWN
468 pin.pad_ctrl().modify(|w| {
469 w.set_pde(true);
470 });
467 } 471 }
468 if let Some(pin) = &self.pin_b { 472 if let Some(pin) = &self.pin_b {
469 pin.gpio().ctrl().write(|w| w.set_funcsel(31)); 473 pin.gpio().ctrl().write(|w| w.set_funcsel(31));
@@ -472,6 +476,10 @@ impl<'d> Drop for Pwm<'d> {
472 pin.pad_ctrl().modify(|w| { 476 pin.pad_ctrl().modify(|w| {
473 w.set_ie(false); 477 w.set_ie(false);
474 }); 478 });
479 // Enable pin PULL-DOWN
480 pin.pad_ctrl().modify(|w| {
481 w.set_pde(true);
482 });
475 } 483 }
476 } 484 }
477} 485}
diff --git a/embassy-rp/src/qmi_cs1.rs b/embassy-rp/src/qmi_cs1.rs
new file mode 100644
index 000000000..b8ae41e35
--- /dev/null
+++ b/embassy-rp/src/qmi_cs1.rs
@@ -0,0 +1,57 @@
1//! QMI CS1 peripheral for RP235x
2//!
3//! This module provides access to the QMI CS1 functionality for use with external memory devices
4//! such as PSRAM. The QMI (Quad SPI) controller supports CS1 as a second chip select signal.
5//!
6//! This peripheral is only available on RP235x chips.
7
8#![cfg(feature = "_rp235x")]
9
10use embassy_hal_internal::{Peri, PeripheralType};
11
12use crate::gpio::Pin as GpioPin;
13use crate::{pac, peripherals};
14
15/// QMI CS1 driver.
16pub struct QmiCs1<'d> {
17 _inner: Peri<'d, peripherals::QMI_CS1>,
18}
19
20impl<'d> QmiCs1<'d> {
21 /// Create a new QMI CS1 instance.
22 pub fn new(qmi_cs1: Peri<'d, peripherals::QMI_CS1>, cs1: Peri<'d, impl QmiCs1Pin>) -> Self {
23 // Configure CS1 pin for QMI function (funcsel = 9)
24 cs1.gpio().ctrl().write(|w| w.set_funcsel(9));
25
26 // Configure pad settings for high-speed operation
27 cs1.pad_ctrl().write(|w| {
28 #[cfg(feature = "_rp235x")]
29 w.set_iso(false);
30 w.set_ie(true);
31 w.set_drive(pac::pads::vals::Drive::_12M_A);
32 w.set_slewfast(true);
33 });
34
35 Self { _inner: qmi_cs1 }
36 }
37}
38
39trait SealedInstance {}
40
41/// QMI CS1 instance trait.
42#[allow(private_bounds)]
43pub trait Instance: SealedInstance + PeripheralType {}
44
45impl SealedInstance for peripherals::QMI_CS1 {}
46
47impl Instance for peripherals::QMI_CS1 {}
48
49/// CS1 pin trait for QMI.
50pub trait QmiCs1Pin: GpioPin {}
51
52// Implement pin traits for CS1-capable GPIO pins
53impl QmiCs1Pin for peripherals::PIN_0 {}
54impl QmiCs1Pin for peripherals::PIN_8 {}
55impl QmiCs1Pin for peripherals::PIN_19 {}
56#[cfg(feature = "rp235xb")]
57impl QmiCs1Pin for peripherals::PIN_47 {}
diff --git a/embassy-rp/src/relocate.rs b/embassy-rp/src/relocate.rs
index 34487819f..6ff40ddd7 100644
--- a/embassy-rp/src/relocate.rs
+++ b/embassy-rp/src/relocate.rs
@@ -39,7 +39,7 @@ pub struct RelocatedProgram<'a, const PROGRAM_SIZE: usize> {
39} 39}
40 40
41impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> { 41impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> {
42 pub fn new_with_origin(program: &Program<PROGRAM_SIZE>, origin: u8) -> RelocatedProgram<PROGRAM_SIZE> { 42 pub fn new_with_origin(program: &Program<PROGRAM_SIZE>, origin: u8) -> RelocatedProgram<'_, PROGRAM_SIZE> {
43 RelocatedProgram { program, origin } 43 RelocatedProgram { program, origin }
44 } 44 }
45 45
diff --git a/embassy-rp/src/rom_data/mod.rs b/embassy-rp/src/rom_data/mod.rs
index e5fcf8e3c..a4aba5737 100644
--- a/embassy-rp/src/rom_data/mod.rs
+++ b/embassy-rp/src/rom_data/mod.rs
@@ -1,29 +1,29 @@
1#![cfg_attr( 1#![cfg_attr(
2 feature = "rp2040", 2 feature = "rp2040",
3 doc = r" 3 doc = r"
4//! Functions and data from the RPI Bootrom. 4Functions and data from the RPI Bootrom.
5//! 5
6//! From the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), Section 2.8.2.1: 6From the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), Section 2.8.2.1:
7//! 7
8//! > The Bootrom contains a number of public functions that provide useful 8> The Bootrom contains a number of public functions that provide useful
9//! > RP2040 functionality that might be needed in the absence of any other code 9> RP2040 functionality that might be needed in the absence of any other code
10//! > on the device, as well as highly optimized versions of certain key 10> on the device, as well as highly optimized versions of certain key
11//! > functionality that would otherwise have to take up space in most user 11> functionality that would otherwise have to take up space in most user
12//! > binaries. 12> binaries.
13" 13"
14)] 14)]
15#![cfg_attr( 15#![cfg_attr(
16 feature = "_rp235x", 16 feature = "_rp235x",
17 doc = r" 17 doc = r"
18//! Functions and data from the RPI Bootrom. 18Functions and data from the RPI Bootrom.
19//! 19
20//! From [Section 5.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the 20From [Section 5.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the
21//! RP2350 datasheet: 21RP2350 datasheet:
22//! 22
23//! > Whilst some ROM space is dedicated to the implementation of the boot 23> Whilst some ROM space is dedicated to the implementation of the boot
24//! > sequence and USB/UART boot interfaces, the bootrom also contains public 24> sequence and USB/UART boot interfaces, the bootrom also contains public
25//! > functions that provide useful RP2350 functionality that may be useful for 25> functions that provide useful RP2350 functionality that may be useful for
26//! > any code or runtime running on the device 26> any code or runtime running on the device
27" 27"
28)] 28)]
29 29
diff --git a/embassy-rp/src/trng.rs b/embassy-rp/src/trng.rs
index a8a0172be..a3f23c1f2 100644
--- a/embassy-rp/src/trng.rs
+++ b/embassy-rp/src/trng.rs
@@ -7,7 +7,6 @@ use core::task::Poll;
7 7
8use embassy_hal_internal::{Peri, PeripheralType}; 8use embassy_hal_internal::{Peri, PeripheralType};
9use embassy_sync::waitqueue::AtomicWaker; 9use embassy_sync::waitqueue::AtomicWaker;
10use rand_core::Error;
11 10
12use crate::interrupt::typelevel::{Binding, Interrupt}; 11use crate::interrupt::typelevel::{Binding, Interrupt};
13use crate::peripherals::TRNG; 12use crate::peripherals::TRNG;
@@ -369,7 +368,7 @@ impl<'d, T: Instance> Trng<'d, T> {
369 } 368 }
370} 369}
371 370
372impl<'d, T: Instance> rand_core::RngCore for Trng<'d, T> { 371impl<'d, T: Instance> rand_core_06::RngCore for Trng<'d, T> {
373 fn next_u32(&mut self) -> u32 { 372 fn next_u32(&mut self) -> u32 {
374 self.blocking_next_u32() 373 self.blocking_next_u32()
375 } 374 }
@@ -379,16 +378,32 @@ impl<'d, T: Instance> rand_core::RngCore for Trng<'d, T> {
379 } 378 }
380 379
381 fn fill_bytes(&mut self, dest: &mut [u8]) { 380 fn fill_bytes(&mut self, dest: &mut [u8]) {
382 self.blocking_fill_bytes(dest) 381 self.blocking_fill_bytes(dest);
383 } 382 }
384 383
385 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { 384 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core_06::Error> {
386 self.blocking_fill_bytes(dest); 385 self.blocking_fill_bytes(dest);
387 Ok(()) 386 Ok(())
388 } 387 }
389} 388}
390 389
391impl<'d, T: Instance> rand_core::CryptoRng for Trng<'d, T> {} 390impl<'d, T: Instance> rand_core_06::CryptoRng for Trng<'d, T> {}
391
392impl<'d, T: Instance> rand_core_09::RngCore for Trng<'d, T> {
393 fn next_u32(&mut self) -> u32 {
394 self.blocking_next_u32()
395 }
396
397 fn next_u64(&mut self) -> u64 {
398 self.blocking_next_u64()
399 }
400
401 fn fill_bytes(&mut self, dest: &mut [u8]) {
402 self.blocking_fill_bytes(dest);
403 }
404}
405
406impl<'d, T: Instance> rand_core_09::CryptoRng for Trng<'d, T> {}
392 407
393/// TRNG interrupt handler. 408/// TRNG interrupt handler.
394pub struct InterruptHandler<T: Instance> { 409pub struct InterruptHandler<T: Instance> {
diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs
index c3a15fda5..6f4e2ee07 100644
--- a/embassy-rp/src/uart/mod.rs
+++ b/embassy-rp/src/uart/mod.rs
@@ -495,52 +495,58 @@ impl<'d> UartRx<'d, Async> {
495 unreachable!("unrecognized rx error"); 495 unreachable!("unrecognized rx error");
496 } 496 }
497 497
498 /// Read from the UART, waiting for a line break. 498 /// Read from the UART, waiting for a break.
499 /// 499 ///
500 /// We read until one of the following occurs: 500 /// We read until one of the following occurs:
501 /// 501 ///
502 /// * We read `buffer.len()` bytes without a line break 502 /// * We read `buffer.len()` bytes without a break
503 /// * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))` 503 /// * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))`
504 /// * We read `n` bytes then a line break occurs 504 /// * We read `n` bytes then a break occurs
505 /// * returns `Ok(n)` 505 /// * returns `Ok(n)`
506 /// * We encounter some error OTHER than a line break 506 /// * We encounter some error OTHER than a break
507 /// * returns `Err(ReadToBreakError::Other(error))` 507 /// * returns `Err(ReadToBreakError::Other(error))`
508 /// 508 ///
509 /// **NOTE**: you MUST provide a buffer one byte larger than your largest expected 509 /// **NOTE**: you MUST provide a buffer one byte larger than your largest expected
510 /// message to reliably detect the framing on one single call to `read_to_break()`. 510 /// message to reliably detect the framing on one single call to `read_to_break()`.
511 /// 511 ///
512 /// * If you expect a message of 20 bytes + line break, and provide a 20-byte buffer: 512 /// * If you expect a message of 20 bytes + break, and provide a 20-byte buffer:
513 /// * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))` 513 /// * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))`
514 /// * The next call to `read_to_break()` will immediately return `Ok(0)`, from the "stale" line break 514 /// * The next call to `read_to_break()` will immediately return `Ok(0)`, from the "stale" break
515 /// * If you expect a message of 20 bytes + line break, and provide a 21-byte buffer: 515 /// * If you expect a message of 20 bytes + break, and provide a 21-byte buffer:
516 /// * The first call to `read_to_break()` will return `Ok(20)`. 516 /// * The first call to `read_to_break()` will return `Ok(20)`.
517 /// * The next call to `read_to_break()` will work as expected 517 /// * The next call to `read_to_break()` will work as expected
518 ///
519 /// **NOTE**: In the UART context, a break refers to a break condition (the line being held low for
520 /// for longer than a single character), not an ASCII line break.
518 pub async fn read_to_break(&mut self, buffer: &mut [u8]) -> Result<usize, ReadToBreakError> { 521 pub async fn read_to_break(&mut self, buffer: &mut [u8]) -> Result<usize, ReadToBreakError> {
519 self.read_to_break_with_count(buffer, 0).await 522 self.read_to_break_with_count(buffer, 0).await
520 } 523 }
521 524
522 /// Read from the UART, waiting for a line break as soon as at least `min_count` bytes have been read. 525 /// Read from the UART, waiting for a break as soon as at least `min_count` bytes have been read.
523 /// 526 ///
524 /// We read until one of the following occurs: 527 /// We read until one of the following occurs:
525 /// 528 ///
526 /// * We read `buffer.len()` bytes without a line break 529 /// * We read `buffer.len()` bytes without a break
527 /// * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))` 530 /// * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))`
528 /// * We read `n > min_count` bytes then a line break occurs 531 /// * We read `n > min_count` bytes then a break occurs
529 /// * returns `Ok(n)` 532 /// * returns `Ok(n)`
530 /// * We encounter some error OTHER than a line break 533 /// * We encounter some error OTHER than a break
531 /// * returns `Err(ReadToBreakError::Other(error))` 534 /// * returns `Err(ReadToBreakError::Other(error))`
532 /// 535 ///
533 /// If a line break occurs before `min_count` bytes have been read, the break will be ignored and the read will continue 536 /// If a break occurs before `min_count` bytes have been read, the break will be ignored and the read will continue
534 /// 537 ///
535 /// **NOTE**: you MUST provide a buffer one byte larger than your largest expected 538 /// **NOTE**: you MUST provide a buffer one byte larger than your largest expected
536 /// message to reliably detect the framing on one single call to `read_to_break()`. 539 /// message to reliably detect the framing on one single call to `read_to_break()`.
537 /// 540 ///
538 /// * If you expect a message of 20 bytes + line break, and provide a 20-byte buffer: 541 /// * If you expect a message of 20 bytes + break, and provide a 20-byte buffer:
539 /// * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))` 542 /// * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))`
540 /// * The next call to `read_to_break()` will immediately return `Ok(0)`, from the "stale" line break 543 /// * The next call to `read_to_break()` will immediately return `Ok(0)`, from the "stale" line break
541 /// * If you expect a message of 20 bytes + line break, and provide a 21-byte buffer: 544 /// * If you expect a message of 20 bytes + break, and provide a 21-byte buffer:
542 /// * The first call to `read_to_break()` will return `Ok(20)`. 545 /// * The first call to `read_to_break()` will return `Ok(20)`.
543 /// * The next call to `read_to_break()` will work as expected 546 /// * The next call to `read_to_break()` will work as expected
547 ///
548 /// **NOTE**: In the UART context, a break refers to a break condition (the line being held low for
549 /// for longer than a single character), not an ASCII line break.
544 pub async fn read_to_break_with_count( 550 pub async fn read_to_break_with_count(
545 &mut self, 551 &mut self,
546 buffer: &mut [u8], 552 buffer: &mut [u8],
diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs
index 96541ade6..671ecbd32 100644
--- a/embassy-rp/src/usb.rs
+++ b/embassy-rp/src/usb.rs
@@ -153,6 +153,7 @@ impl<'d, T: Instance> Driver<'d, T> {
153 fn alloc_endpoint<D: Dir>( 153 fn alloc_endpoint<D: Dir>(
154 &mut self, 154 &mut self,
155 ep_type: EndpointType, 155 ep_type: EndpointType,
156 ep_addr: Option<EndpointAddress>,
156 max_packet_size: u16, 157 max_packet_size: u16,
157 interval_ms: u8, 158 interval_ms: u8,
158 ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> { 159 ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> {
@@ -169,12 +170,25 @@ impl<'d, T: Instance> Driver<'d, T> {
169 Direction::In => &mut self.ep_in, 170 Direction::In => &mut self.ep_in,
170 }; 171 };
171 172
172 let index = alloc.iter_mut().enumerate().find(|(i, ep)| { 173 let index = if let Some(addr) = ep_addr {
173 if *i == 0 { 174 // Use the specified endpoint address
174 return false; // reserved for control pipe 175 let requested_index = addr.index();
176 if requested_index == 0 || requested_index >= EP_COUNT {
177 return Err(EndpointAllocError);
175 } 178 }
176 !ep.used 179 if alloc[requested_index].used {
177 }); 180 return Err(EndpointAllocError);
181 }
182 Some((requested_index, &mut alloc[requested_index]))
183 } else {
184 // Find any available endpoint
185 alloc.iter_mut().enumerate().find(|(i, ep)| {
186 if *i == 0 {
187 return false; // reserved for control pipe
188 }
189 !ep.used
190 })
191 };
178 192
179 let (index, ep) = index.ok_or(EndpointAllocError)?; 193 let (index, ep) = index.ok_or(EndpointAllocError)?;
180 assert!(!ep.used); 194 assert!(!ep.used);
@@ -299,19 +313,21 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
299 fn alloc_endpoint_in( 313 fn alloc_endpoint_in(
300 &mut self, 314 &mut self,
301 ep_type: EndpointType, 315 ep_type: EndpointType,
316 ep_addr: Option<EndpointAddress>,
302 max_packet_size: u16, 317 max_packet_size: u16,
303 interval_ms: u8, 318 interval_ms: u8,
304 ) -> Result<Self::EndpointIn, driver::EndpointAllocError> { 319 ) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
305 self.alloc_endpoint(ep_type, max_packet_size, interval_ms) 320 self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms)
306 } 321 }
307 322
308 fn alloc_endpoint_out( 323 fn alloc_endpoint_out(
309 &mut self, 324 &mut self,
310 ep_type: EndpointType, 325 ep_type: EndpointType,
326 ep_addr: Option<EndpointAddress>,
311 max_packet_size: u16, 327 max_packet_size: u16,
312 interval_ms: u8, 328 interval_ms: u8,
313 ) -> Result<Self::EndpointOut, driver::EndpointAllocError> { 329 ) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
314 self.alloc_endpoint(ep_type, max_packet_size, interval_ms) 330 self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms)
315 } 331 }
316 332
317 fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { 333 fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {