diff options
| author | 1-rafael-1 <[email protected]> | 2025-09-15 20:07:18 +0200 |
|---|---|---|
| committer | 1-rafael-1 <[email protected]> | 2025-09-15 20:07:18 +0200 |
| commit | 6bb3d2c0720fa082f27d3cdb70f516058497ec87 (patch) | |
| tree | 5a1e255cff999b00800f203b91a759c720c973e5 /embassy-rp/src | |
| parent | eb685574601d98c44faed9a3534d056199b46e20 (diff) | |
| parent | 92a6fd2946f2cbb15359290f68aa360953da2ff7 (diff) | |
Merge branch 'main' into rp2040-rtc-alarm
Diffstat (limited to 'embassy-rp/src')
| -rw-r--r-- | embassy-rp/src/adc.rs | 2 | ||||
| -rw-r--r-- | embassy-rp/src/clocks.rs | 297 | ||||
| -rw-r--r-- | embassy-rp/src/flash.rs | 6 | ||||
| -rw-r--r-- | embassy-rp/src/gpio.rs | 55 | ||||
| -rw-r--r-- | embassy-rp/src/i2c.rs | 27 | ||||
| -rw-r--r-- | embassy-rp/src/i2c_slave.rs | 16 | ||||
| -rw-r--r-- | embassy-rp/src/lib.rs | 30 | ||||
| -rw-r--r-- | embassy-rp/src/multicore.rs | 24 | ||||
| -rw-r--r-- | embassy-rp/src/pio/mod.rs | 90 | ||||
| -rw-r--r-- | embassy-rp/src/pio_programs/i2s.rs | 114 | ||||
| -rw-r--r-- | embassy-rp/src/pio_programs/mod.rs | 1 | ||||
| -rw-r--r-- | embassy-rp/src/pio_programs/onewire.rs | 45 | ||||
| -rw-r--r-- | embassy-rp/src/pio_programs/spi.rs | 474 | ||||
| -rw-r--r-- | embassy-rp/src/pio_programs/ws2812.rs | 70 | ||||
| -rw-r--r-- | embassy-rp/src/psram.rs | 682 | ||||
| -rw-r--r-- | embassy-rp/src/pwm.rs | 8 | ||||
| -rw-r--r-- | embassy-rp/src/qmi_cs1.rs | 57 | ||||
| -rw-r--r-- | embassy-rp/src/relocate.rs | 2 | ||||
| -rw-r--r-- | embassy-rp/src/rom_data/mod.rs | 36 | ||||
| -rw-r--r-- | embassy-rp/src/trng.rs | 25 | ||||
| -rw-r--r-- | embassy-rp/src/uart/mod.rs | 34 | ||||
| -rw-r--r-- | embassy-rp/src/usb.rs | 30 |
22 files changed, 1988 insertions, 137 deletions
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)] |
| 22 | pub struct Config {} | 22 | pub struct Config {} |
| 23 | 23 | ||
| 24 | #[derive(Debug)] | ||
| 25 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 24 | enum Source<'p> { | 26 | enum 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")] | ||
| 67 | use core::arch::asm; | 66 | use core::arch::asm; |
| 68 | use core::marker::PhantomData; | 67 | use core::marker::PhantomData; |
| 69 | #[cfg(feature = "rp2040")] | 68 | #[cfg(feature = "rp2040")] |
| @@ -73,7 +72,6 @@ use core::sync::atomic::{AtomicU32, Ordering}; | |||
| 73 | use pac::clocks::vals::*; | 72 | use pac::clocks::vals::*; |
| 74 | 73 | ||
| 75 | use crate::gpio::{AnyPin, SealedPin}; | 74 | use crate::gpio::{AnyPin, SealedPin}; |
| 76 | #[cfg(feature = "rp2040")] | ||
| 77 | use crate::pac::common::{Reg, RW}; | 75 | use crate::pac::common::{Reg, RW}; |
| 78 | use crate::{pac, reset, Peri}; | 76 | use 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))] | ||
| 86 | pub 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 | |||
| 85 | struct Clocks { | 95 | struct 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))] | ||
| 146 | pub enum CoreVoltage { | 157 | pub 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))] | ||
| 194 | pub 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 | |||
| 172 | impl CoreVoltage { | 229 | impl 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))] | ||
| 528 | pub enum RoscRange { | 620 | pub 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))] | ||
| 634 | pub enum RefClkSrc { | 727 | pub 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))] | ||
| 649 | pub enum SysClkSrc { | 743 | pub 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))] | ||
| 687 | pub enum UsbClkSrc { | 782 | pub 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))] | ||
| 715 | pub enum AdcClkSrc { | 811 | pub 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")] |
| 744 | pub enum RtcClkSrc { | 841 | pub 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")] | ||
| 795 | fn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> { | 891 | fn 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. | ||
| 1406 | pub 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 | |||
| 1286 | fn start_xosc(crystal_hz: u32, delay_multiplier: u32) { | 1454 | fn 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)] |
| 1298 | fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result<u32, &'static str> { | 1466 | fn 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> { | |||
| 1606 | pub struct RoscRng; | 1774 | pub struct RoscRng; |
| 1607 | 1775 | ||
| 1608 | impl RoscRng { | 1776 | impl 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 | ||
| 1620 | impl rand_core::RngCore for RoscRng { | 1804 | impl 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 | ||
| 1823 | impl rand_core_06::CryptoRng for RoscRng {} | ||
| 1824 | |||
| 1825 | impl 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 | ||
| 1839 | impl 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"))] |
| 1643 | pub fn dormant_sleep() { | 1846 | pub 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))] | ||
| 29 | pub enum Level { | 30 | pub 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))] | ||
| 56 | pub enum Pull { | 58 | pub 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))] | ||
| 67 | pub enum Drive { | 70 | pub 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))] | ||
| 79 | pub enum SlewRate { | 83 | pub 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))] | ||
| 88 | pub enum Bank { | 93 | pub 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))] | ||
| 111 | pub struct Input<'d> { | 118 | pub 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))] | ||
| 355 | pub struct Output<'d> { | 370 | pub 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))] | ||
| 436 | pub struct OutputOpenDrain<'d> { | 459 | pub 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))] | ||
| 583 | pub struct Flex<'d> { | 608 | pub 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))] | ||
| 829 | pub struct DormantWake<'w> { | 882 | pub 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))] | ||
| 935 | pub struct AnyPin { | 990 | pub 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 { | |||
| 64 | pub struct Config { | 64 | pub 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 | |||
| 69 | impl Default for Config { | 78 | impl 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. |
| 76 | pub const FIFO_SIZE: u8 = 16; | 88 | pub const FIFO_SIZE: u8 = 16; |
| 77 | 89 | ||
| 78 | /// I2C driver. | 90 | /// I2C driver. |
| 91 | #[derive(Debug)] | ||
| 79 | pub struct I2c<'d, T: Instance, M: Mode> { | 92 | pub 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 | ||
| 361 | pub(crate) fn set_up_i2c_pin<P, T>(pin: &P) | 374 | pub(crate) fn set_up_i2c_pin<P, T>(pin: &P, pullup: bool) |
| 362 | where | 375 | where |
| 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 | ||
| 70 | impl Default for Config { | 80 | impl 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")] |
| 36 | pub mod otp; | 36 | pub mod otp; |
| 37 | pub mod pio_programs; | 37 | pub mod pio_programs; |
| 38 | #[cfg(feature = "_rp235x")] | ||
| 39 | pub mod psram; | ||
| 38 | pub mod pwm; | 40 | pub mod pwm; |
| 41 | #[cfg(feature = "_rp235x")] | ||
| 42 | pub mod qmi_cs1; | ||
| 39 | mod reset; | 43 | mod reset; |
| 40 | pub mod rom_data; | 44 | pub 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] |
| 170 | macro_rules! bind_interrupts { | 177 | macro_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; | |||
| 57 | const RESUME_TOKEN: u32 = !0xDEADBEEF; | 57 | const RESUME_TOKEN: u32 = !0xDEADBEEF; |
| 58 | static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false); | 58 | static 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)] | ||
| 64 | pub 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 | ||
| 72 | pub 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)] |
| 61 | unsafe fn core1_setup(stack_bottom: *mut usize) { | 81 | unsafe 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; | |||
| 12 | use fixed::FixedU32; | 12 | use fixed::FixedU32; |
| 13 | use pio::{Program, SideSet, Wrap}; | 13 | use pio::{Program, SideSet, Wrap}; |
| 14 | 14 | ||
| 15 | use crate::dma::{Channel, Transfer, Word}; | 15 | use crate::dma::{self, Channel, Transfer, Word}; |
| 16 | use crate::gpio::{self, AnyPin, Drive, Level, Pull, SealedPin, SlewRate}; | 16 | use crate::gpio::{self, AnyPin, Drive, Level, Pull, SealedPin, SlewRate}; |
| 17 | use crate::interrupt::typelevel::{Binding, Handler, Interrupt}; | 17 | use crate::interrupt::typelevel::{Binding, Handler, Interrupt}; |
| 18 | use crate::relocate::RelocatedProgram; | 18 | use 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 | ||
| 103 | const RXNEMPTY_MASK: u32 = 1 << 0; | 106 | const 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 | ||
| 3 | use fixed::traits::ToFixed; | 3 | use fixed::traits::ToFixed; |
| 4 | 4 | ||
| 5 | use crate::dma::{AnyChannel, Channel, Transfer}; | 5 | use crate::dma::{AnyChannel, Channel, Transfer}; |
| 6 | use crate::gpio::Pull; | ||
| 6 | use crate::pio::{ | 7 | use 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 | }; |
| 9 | use crate::Peri; | 10 | use crate::Peri; |
| 10 | 11 | ||
| 12 | /// This struct represents an i2s receiver & controller driver program | ||
| 13 | pub struct PioI2sInProgram<'d, PIO: Instance> { | ||
| 14 | prg: LoadedProgram<'d, PIO>, | ||
| 15 | } | ||
| 16 | |||
| 17 | impl<'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 | ||
| 39 | pub struct PioI2sIn<'d, P: Instance, const S: usize> { | ||
| 40 | dma: Peri<'d, AnyChannel>, | ||
| 41 | sm: StateMachine<'d, P, S>, | ||
| 42 | } | ||
| 43 | |||
| 44 | impl<'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) | ||
| 12 | pub struct PioI2sOutProgram<'d, PIO: Instance> { | 104 | pub 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; | |||
| 6 | pub mod onewire; | 6 | pub mod onewire; |
| 7 | pub mod pwm; | 7 | pub mod pwm; |
| 8 | pub mod rotary_encoder; | 8 | pub mod rotary_encoder; |
| 9 | pub mod spi; | ||
| 9 | pub mod stepper; | 10 | pub mod stepper; |
| 10 | pub mod uart; | 11 | pub mod uart; |
| 11 | pub mod ws2812; | 12 | pub 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 | |||
| 3 | use core::marker::PhantomData; | ||
| 4 | |||
| 5 | use embassy_futures::join::join; | ||
| 6 | use embassy_hal_internal::Peri; | ||
| 7 | use embedded_hal_02::spi::{Phase, Polarity}; | ||
| 8 | use fixed::traits::ToFixed; | ||
| 9 | use fixed::types::extra::U8; | ||
| 10 | |||
| 11 | use crate::clocks::clk_sys_freq; | ||
| 12 | use crate::dma::{AnyChannel, Channel}; | ||
| 13 | use crate::gpio::Level; | ||
| 14 | use crate::pio::{Common, Direction, Instance, LoadedProgram, Pin, PioPin, ShiftDirection, StateMachine}; | ||
| 15 | use crate::spi::{Async, Blocking, Config, Mode}; | ||
| 16 | |||
| 17 | /// This struct represents an SPI program loaded into pio instruction memory. | ||
| 18 | struct PioSpiProgram<'d, PIO: Instance> { | ||
| 19 | prg: LoadedProgram<'d, PIO>, | ||
| 20 | phase: Phase, | ||
| 21 | } | ||
| 22 | |||
| 23 | impl<'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] | ||
| 82 | pub 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. | ||
| 90 | pub 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 | |||
| 100 | impl<'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 | |||
| 284 | fn 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 | |||
| 292 | impl<'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 | |||
| 306 | impl<'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 | |||
| 406 | impl<'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 | |||
| 414 | impl<'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 | |||
| 422 | impl embedded_hal_1::spi::Error for Error { | ||
| 423 | fn kind(&self) -> embedded_hal_1::spi::ErrorKind { | ||
| 424 | match *self {} | ||
| 425 | } | ||
| 426 | } | ||
| 427 | |||
| 428 | impl<'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 | |||
| 432 | impl<'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 | |||
| 454 | impl<'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 | ||
| 3 | use embassy_time::Timer; | 3 | use embassy_time::Timer; |
| 4 | use fixed::types::U24F8; | 4 | use fixed::types::U24F8; |
| 5 | use smart_leds::RGB8; | 5 | use smart_leds::{RGB8, RGBW}; |
| 6 | 6 | ||
| 7 | use crate::clocks::clk_sys_freq; | 7 | use crate::clocks::clk_sys_freq; |
| 8 | use crate::dma::{AnyChannel, Channel}; | 8 | use 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 |
| 55 | pub struct PioWs2812<'d, P: Instance, const S: usize, const N: usize> { | 55 | pub 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 | ||
| 118 | pub struct RgbwPioWs2812<'d, P: Instance, const S: usize, const N: usize> { | ||
| 119 | dma: Peri<'d, AnyChannel>, | ||
| 120 | sm: StateMachine<'d, P, S>, | ||
| 121 | } | ||
| 122 | |||
| 123 | impl<'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 | |||
| 13 | use critical_section::{acquire, release, CriticalSection, RestoreState}; | ||
| 14 | |||
| 15 | use crate::pac; | ||
| 16 | use 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] | ||
| 22 | pub 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)] | ||
| 33 | pub enum VerificationType { | ||
| 34 | /// Skip device verification | ||
| 35 | None, | ||
| 36 | /// Verify as APS6404L device | ||
| 37 | Aps6404l, | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Memory configuration. | ||
| 41 | #[derive(Clone)] | ||
| 42 | pub 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)] | ||
| 79 | pub 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)] | ||
| 92 | pub 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)] | ||
| 111 | pub 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 | |||
| 120 | impl Default for Config { | ||
| 121 | fn default() -> Self { | ||
| 122 | Self::aps6404l() | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | impl 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. | ||
| 206 | pub struct Psram<'d> { | ||
| 207 | #[allow(dead_code)] | ||
| 208 | qmi_cs1: QmiCs1<'d>, | ||
| 209 | size: usize, | ||
| 210 | } | ||
| 211 | |||
| 212 | impl<'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 | |||
| 10 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 11 | |||
| 12 | use crate::gpio::Pin as GpioPin; | ||
| 13 | use crate::{pac, peripherals}; | ||
| 14 | |||
| 15 | /// QMI CS1 driver. | ||
| 16 | pub struct QmiCs1<'d> { | ||
| 17 | _inner: Peri<'d, peripherals::QMI_CS1>, | ||
| 18 | } | ||
| 19 | |||
| 20 | impl<'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 | |||
| 39 | trait SealedInstance {} | ||
| 40 | |||
| 41 | /// QMI CS1 instance trait. | ||
| 42 | #[allow(private_bounds)] | ||
| 43 | pub trait Instance: SealedInstance + PeripheralType {} | ||
| 44 | |||
| 45 | impl SealedInstance for peripherals::QMI_CS1 {} | ||
| 46 | |||
| 47 | impl Instance for peripherals::QMI_CS1 {} | ||
| 48 | |||
| 49 | /// CS1 pin trait for QMI. | ||
| 50 | pub trait QmiCs1Pin: GpioPin {} | ||
| 51 | |||
| 52 | // Implement pin traits for CS1-capable GPIO pins | ||
| 53 | impl QmiCs1Pin for peripherals::PIN_0 {} | ||
| 54 | impl QmiCs1Pin for peripherals::PIN_8 {} | ||
| 55 | impl QmiCs1Pin for peripherals::PIN_19 {} | ||
| 56 | #[cfg(feature = "rp235xb")] | ||
| 57 | impl 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 | ||
| 41 | impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> { | 41 | impl<'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 | #, Section 2.8.2.1: | 6 | From 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 | # of the | 20 | From [Section 5.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the |
| 21 | //! RP2350 datasheet: | 21 | RP2350 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 | ||
| 8 | use embassy_hal_internal::{Peri, PeripheralType}; | 8 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 9 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 10 | use rand_core::Error; | ||
| 11 | 10 | ||
| 12 | use crate::interrupt::typelevel::{Binding, Interrupt}; | 11 | use crate::interrupt::typelevel::{Binding, Interrupt}; |
| 13 | use crate::peripherals::TRNG; | 12 | use crate::peripherals::TRNG; |
| @@ -369,7 +368,7 @@ impl<'d, T: Instance> Trng<'d, T> { | |||
| 369 | } | 368 | } |
| 370 | } | 369 | } |
| 371 | 370 | ||
| 372 | impl<'d, T: Instance> rand_core::RngCore for Trng<'d, T> { | 371 | impl<'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 | ||
| 391 | impl<'d, T: Instance> rand_core::CryptoRng for Trng<'d, T> {} | 390 | impl<'d, T: Instance> rand_core_06::CryptoRng for Trng<'d, T> {} |
| 391 | |||
| 392 | impl<'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 | |||
| 406 | impl<'d, T: Instance> rand_core_09::CryptoRng for Trng<'d, T> {} | ||
| 392 | 407 | ||
| 393 | /// TRNG interrupt handler. | 408 | /// TRNG interrupt handler. |
| 394 | pub struct InterruptHandler<T: Instance> { | 409 | pub 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) { |
