diff options
| author | Felipe Balbi <[email protected]> | 2025-12-11 09:48:32 -0800 |
|---|---|---|
| committer | Felipe Balbi <[email protected]> | 2025-12-11 09:48:32 -0800 |
| commit | 86de5d04b91f38ab4322eaa8ff5b0f371824b57a (patch) | |
| tree | f758f462bd3cc7f670fdb7613282d1beaedefb95 | |
| parent | af02ef481498441289a6decfe3baf7f3878f7871 (diff) | |
| parent | 3588023e3e04b18cf98a2a0d10756e1f236ca351 (diff) | |
Merge remote-tracking branch 'upstream/main' into mcxa/trng
49 files changed, 2695 insertions, 1548 deletions
diff --git a/.github/ci/book.sh b/.github/ci/book.sh index 6c300bf09..a39f0dac7 100755 --- a/.github/ci/book.sh +++ b/.github/ci/book.sh | |||
| @@ -9,7 +9,7 @@ set -euxo pipefail | |||
| 9 | make -C docs | 9 | make -C docs |
| 10 | 10 | ||
| 11 | export KUBECONFIG=/ci/secrets/kubeconfig.yml | 11 | export KUBECONFIG=/ci/secrets/kubeconfig.yml |
| 12 | POD=$(kubectl -n embassy get po -l app=website -o jsonpath={.items[0].metadata.name}) | 12 | POD=$(kubectl get po -l app=website -o jsonpath={.items[0].metadata.name}) |
| 13 | 13 | ||
| 14 | mkdir -p build | 14 | mkdir -p build |
| 15 | mv docs/book build/book | 15 | mv docs/book build/book |
diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 5a0032d9d..85eed9f80 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh | |||
| @@ -17,6 +17,6 @@ cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked | |||
| 17 | cargo embassy-devtool doc -o webroot | 17 | cargo embassy-devtool doc -o webroot |
| 18 | 18 | ||
| 19 | export KUBECONFIG=/ci/secrets/kubeconfig.yml | 19 | export KUBECONFIG=/ci/secrets/kubeconfig.yml |
| 20 | POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) | 20 | POD=$(kubectl get po -l app=docserver -o jsonpath={.items[0].metadata.name}) |
| 21 | kubectl cp webroot/crates $POD:/data | 21 | kubectl cp webroot/crates $POD:/data |
| 22 | kubectl cp webroot/static $POD:/data | 22 | kubectl cp webroot/static $POD:/data |
| @@ -67,6 +67,8 @@ rm out/tests/rpi-pico/pwm | |||
| 67 | rm out/tests/rpi-pico/cyw43-perf | 67 | rm out/tests/rpi-pico/cyw43-perf |
| 68 | rm out/tests/rpi-pico/uart_buffered | 68 | rm out/tests/rpi-pico/uart_buffered |
| 69 | 69 | ||
| 70 | rm out/tests/stm32h563zi/usart_dma | ||
| 71 | |||
| 70 | # tests are implemented but the HIL test farm doesn't actually have these boards, yet | 72 | # tests are implemented but the HIL test farm doesn't actually have these boards, yet |
| 71 | rm -rf out/tests/stm32c071rb | 73 | rm -rf out/tests/stm32c071rb |
| 72 | rm -rf out/tests/stm32f100rd | 74 | rm -rf out/tests/stm32f100rd |
diff --git a/cyw43/src/consts.rs b/cyw43/src/consts.rs index c3f0dbfd8..e561d4794 100644 --- a/cyw43/src/consts.rs +++ b/cyw43/src/consts.rs | |||
| @@ -177,9 +177,11 @@ pub(crate) enum Security { | |||
| 177 | } | 177 | } |
| 178 | 178 | ||
| 179 | #[allow(non_camel_case_types)] | 179 | #[allow(non_camel_case_types)] |
| 180 | #[derive(Copy, Clone)] | 180 | #[derive(Copy, Clone, PartialEq, num_enum::FromPrimitive)] |
| 181 | #[repr(u8)] | 181 | #[repr(u8)] |
| 182 | pub enum EStatus { | 182 | pub enum EStatus { |
| 183 | #[num_enum(default)] | ||
| 184 | Unknown = 0xFF, | ||
| 183 | /// operation was successful | 185 | /// operation was successful |
| 184 | SUCCESS = 0, | 186 | SUCCESS = 0, |
| 185 | /// operation failed | 187 | /// operation failed |
diff --git a/cyw43/src/control.rs b/cyw43/src/control.rs index 07fa1955e..240c0e728 100644 --- a/cyw43/src/control.rs +++ b/cyw43/src/control.rs | |||
| @@ -12,11 +12,16 @@ use crate::ioctl::{IoctlState, IoctlType}; | |||
| 12 | use crate::structs::*; | 12 | use crate::structs::*; |
| 13 | use crate::{PowerManagementMode, countries, events}; | 13 | use crate::{PowerManagementMode, countries, events}; |
| 14 | 14 | ||
| 15 | /// Control errors. | 15 | /// Join errors. |
| 16 | #[derive(Debug)] | 16 | #[derive(Debug)] |
| 17 | pub struct Error { | 17 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 18 | /// Status code. | 18 | pub enum JoinError { |
| 19 | pub status: u32, | 19 | /// Network not found. |
| 20 | NetworkNotFound, | ||
| 21 | /// Failure to join network. Contains the status code from the SET_SSID event. | ||
| 22 | JoinFailure(u8), | ||
| 23 | /// Authentication failure for a secure network. | ||
| 24 | AuthenticationFailure, | ||
| 20 | } | 25 | } |
| 21 | 26 | ||
| 22 | /// Multicast errors. | 27 | /// Multicast errors. |
| @@ -296,7 +301,7 @@ impl<'a> Control<'a> { | |||
| 296 | } | 301 | } |
| 297 | 302 | ||
| 298 | /// Join a network with the provided SSID using the specified options. | 303 | /// Join a network with the provided SSID using the specified options. |
| 299 | pub async fn join(&mut self, ssid: &str, options: JoinOptions<'_>) -> Result<(), Error> { | 304 | pub async fn join(&mut self, ssid: &str, options: JoinOptions<'_>) -> Result<(), JoinError> { |
| 300 | self.set_iovar_u32("ampdu_ba_wsize", 8).await; | 305 | self.set_iovar_u32("ampdu_ba_wsize", 8).await; |
| 301 | 306 | ||
| 302 | if options.auth == JoinAuth::Open { | 307 | if options.auth == JoinAuth::Open { |
| @@ -367,40 +372,55 @@ impl<'a> Control<'a> { | |||
| 367 | }; | 372 | }; |
| 368 | i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); | 373 | i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); |
| 369 | 374 | ||
| 370 | self.wait_for_join(i).await | 375 | let secure_network = options.auth != JoinAuth::Open; |
| 376 | self.wait_for_join(i, secure_network).await | ||
| 371 | } | 377 | } |
| 372 | 378 | ||
| 373 | async fn wait_for_join(&mut self, i: SsidInfo) -> Result<(), Error> { | 379 | async fn wait_for_join(&mut self, i: SsidInfo, secure_network: bool) -> Result<(), JoinError> { |
| 374 | self.events.mask.enable(&[Event::SET_SSID, Event::AUTH]); | 380 | self.events.mask.enable(&[Event::SET_SSID, Event::AUTH, Event::PSK_SUP]); |
| 375 | let mut subscriber = self.events.queue.subscriber().unwrap(); | 381 | let mut subscriber = self.events.queue.subscriber().unwrap(); |
| 376 | // the actual join operation starts here | 382 | // the actual join operation starts here |
| 377 | // we make sure to enable events before so we don't miss any | 383 | // we make sure to enable events before so we don't miss any |
| 378 | 384 | ||
| 379 | self.ioctl(IoctlType::Set, Ioctl::SetSsid, 0, &mut i.to_bytes()).await; | 385 | self.ioctl(IoctlType::Set, Ioctl::SetSsid, 0, &mut i.to_bytes()).await; |
| 380 | 386 | ||
| 381 | // to complete the join, we wait for a SET_SSID event | 387 | // To complete the join on an open network, we wait for a SET_SSID event with status SUCCESS |
| 382 | // we also save the AUTH status for the user, it may be interesting | 388 | // For secured networks, we wait for a PSK_SUP event with status 6 "UNSOLICITED" |
| 383 | let mut auth_status = 0; | 389 | let result = loop { |
| 384 | let status = loop { | ||
| 385 | let msg = subscriber.next_message_pure().await; | 390 | let msg = subscriber.next_message_pure().await; |
| 386 | if msg.header.event_type == Event::AUTH && msg.header.status != EStatus::SUCCESS { | 391 | |
| 387 | auth_status = msg.header.status; | 392 | let status = EStatus::from(msg.header.status as u8); |
| 388 | } else if msg.header.event_type == Event::SET_SSID { | 393 | match (msg.header.event_type, status, secure_network) { |
| 389 | // join operation ends with SET_SSID event | 394 | // Join operation ends with SET_SSID event for open networks |
| 390 | break msg.header.status; | 395 | (Event::SET_SSID, EStatus::SUCCESS, false) => break Ok(()), |
| 391 | } | 396 | (Event::SET_SSID, EStatus::NO_NETWORKS, _) => break Err(JoinError::NetworkNotFound), |
| 397 | (Event::SET_SSID, status, _) if status != EStatus::SUCCESS => { | ||
| 398 | break Err(JoinError::JoinFailure(status as u8)); | ||
| 399 | } | ||
| 400 | // Ignore PSK_SUP "ABORT" which is sometimes sent before successful join | ||
| 401 | (Event::PSK_SUP, EStatus::ABORT, true) => {} | ||
| 402 | // Event PSK_SUP with status 6 "UNSOLICITED" indicates success for secure networks | ||
| 403 | (Event::PSK_SUP, EStatus::UNSOLICITED, true) => break Ok(()), | ||
| 404 | // Events indicating authentication failure, possibly due to incorrect password | ||
| 405 | (Event::PSK_SUP, _, true) | (Event::AUTH, EStatus::FAIL, true) => { | ||
| 406 | break Err(JoinError::AuthenticationFailure); | ||
| 407 | } | ||
| 408 | _ => {} | ||
| 409 | }; | ||
| 392 | }; | 410 | }; |
| 393 | 411 | ||
| 394 | self.events.mask.disable_all(); | 412 | self.events.mask.disable_all(); |
| 395 | if status == EStatus::SUCCESS { | 413 | match result { |
| 396 | // successful join | 414 | Ok(()) => { |
| 397 | self.state_ch.set_link_state(LinkState::Up); | 415 | self.state_ch.set_link_state(LinkState::Up); |
| 398 | debug!("JOINED"); | 416 | debug!("JOINED"); |
| 399 | Ok(()) | 417 | } |
| 400 | } else { | 418 | Err(JoinError::JoinFailure(status)) => debug!("JOIN failed: status={}", status), |
| 401 | warn!("JOIN failed with status={} auth={}", status, auth_status); | 419 | Err(JoinError::NetworkNotFound) => debug!("JOIN failed: network not found"), |
| 402 | Err(Error { status }) | 420 | Err(JoinError::AuthenticationFailure) => debug!("JOIN failed: authentication failure"), |
| 403 | } | 421 | }; |
| 422 | |||
| 423 | result | ||
| 404 | } | 424 | } |
| 405 | 425 | ||
| 406 | /// Set GPIO pin on WiFi chip. | 426 | /// Set GPIO pin on WiFi chip. |
diff --git a/cyw43/src/lib.rs b/cyw43/src/lib.rs index 82c636346..73d2830ca 100644 --- a/cyw43/src/lib.rs +++ b/cyw43/src/lib.rs | |||
| @@ -31,7 +31,7 @@ use ioctl::IoctlState; | |||
| 31 | use crate::bus::Bus; | 31 | use crate::bus::Bus; |
| 32 | pub use crate::bus::SpiBusCyw43; | 32 | pub use crate::bus::SpiBusCyw43; |
| 33 | pub use crate::control::{ | 33 | pub use crate::control::{ |
| 34 | AddMulticastAddressError, Control, Error as ControlError, JoinAuth, JoinOptions, ScanOptions, ScanType, Scanner, | 34 | AddMulticastAddressError, Control, JoinAuth, JoinError, JoinOptions, ScanOptions, ScanType, Scanner, |
| 35 | }; | 35 | }; |
| 36 | pub use crate::runner::Runner; | 36 | pub use crate::runner::Runner; |
| 37 | pub use crate::structs::BssInfo; | 37 | pub use crate::structs::BssInfo; |
diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index 7475299ba..f88bb6b37 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs | |||
| @@ -1,42 +1,23 @@ | |||
| 1 | //! ADC driver | 1 | //! ADC driver |
| 2 | use core::sync::atomic::{AtomicBool, Ordering}; | 2 | use core::future::Future; |
| 3 | use core::marker::PhantomData; | ||
| 3 | 4 | ||
| 4 | use embassy_hal_internal::{Peri, PeripheralType}; | 5 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 6 | use maitake_sync::WaitCell; | ||
| 7 | use paste::paste; | ||
| 5 | 8 | ||
| 6 | use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4}; | 9 | use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4}; |
| 7 | use crate::clocks::{Gate, PoweredClock, enable_and_reset}; | 10 | use crate::clocks::{ClockError, Gate, PoweredClock, enable_and_reset}; |
| 11 | use crate::gpio::{GpioPin, SealedPin}; | ||
| 12 | use crate::interrupt::typelevel::{Handler, Interrupt}; | ||
| 8 | use crate::pac; | 13 | use crate::pac; |
| 9 | use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres}; | 14 | use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres}; |
| 10 | use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts}; | 15 | use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts}; |
| 11 | use crate::pac::adc1::cmdl1::{Adch, Ctype, Mode}; | 16 | use crate::pac::adc1::cmdl1::Mode; |
| 12 | use crate::pac::adc1::ctrl::CalAvgs; | 17 | use crate::pac::adc1::ctrl::CalAvgs; |
| 13 | use crate::pac::adc1::tctrl::{Tcmd, Tpri}; | 18 | use crate::pac::adc1::tctrl::{Tcmd, Tpri}; |
| 14 | 19 | ||
| 15 | type Regs = pac::adc1::RegisterBlock; | 20 | /// Trigger priority policy for ADC conversions. |
| 16 | |||
| 17 | static INTERRUPT_TRIGGERED: AtomicBool = AtomicBool::new(false); | ||
| 18 | // Token-based instance pattern like embassy-imxrt | ||
| 19 | pub trait Instance: Gate<MrccPeriphConfig = AdcConfig> + PeripheralType { | ||
| 20 | fn ptr() -> *const Regs; | ||
| 21 | } | ||
| 22 | |||
| 23 | /// Token for ADC1 | ||
| 24 | pub type Adc1 = crate::peripherals::ADC1; | ||
| 25 | impl Instance for crate::peripherals::ADC1 { | ||
| 26 | #[inline(always)] | ||
| 27 | fn ptr() -> *const Regs { | ||
| 28 | pac::Adc1::ptr() | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | // Also implement Instance for the Peri wrapper type | ||
| 33 | // impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::ADC1> { | ||
| 34 | // #[inline(always)] | ||
| 35 | // fn ptr() -> *const Regs { | ||
| 36 | // pac::Adc1::ptr() | ||
| 37 | // } | ||
| 38 | // } | ||
| 39 | |||
| 40 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 21 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 41 | #[repr(u8)] | 22 | #[repr(u8)] |
| 42 | pub enum TriggerPriorityPolicy { | 23 | pub enum TriggerPriorityPolicy { |
| @@ -52,20 +33,36 @@ pub enum TriggerPriorityPolicy { | |||
| 52 | TriggerPriorityExceptionDisabled = 16, | 33 | TriggerPriorityExceptionDisabled = 16, |
| 53 | } | 34 | } |
| 54 | 35 | ||
| 36 | /// Configuration for the LPADC peripheral. | ||
| 55 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 37 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 56 | pub struct LpadcConfig { | 38 | pub struct LpadcConfig { |
| 39 | /// Control system transition to Stop and Wait power modes while ADC is converting. | ||
| 40 | /// When enabled in Doze mode, immediate entries to Wait or Stop are allowed. | ||
| 41 | /// When disabled, the ADC will wait for the current averaging iteration/FIFO storage to complete before acknowledging stop or wait mode entry. | ||
| 57 | pub enable_in_doze_mode: bool, | 42 | pub enable_in_doze_mode: bool, |
| 43 | /// Auto-Calibration Averages. | ||
| 58 | pub conversion_average_mode: CalAvgs, | 44 | pub conversion_average_mode: CalAvgs, |
| 45 | /// ADC analog circuits are pre-enabled and ready to execute conversions without startup delays(at the cost of higher DC current consumption). | ||
| 59 | pub enable_analog_preliminary: bool, | 46 | pub enable_analog_preliminary: bool, |
| 47 | /// Power-up delay value (in ADC clock cycles) | ||
| 60 | pub power_up_delay: u8, | 48 | pub power_up_delay: u8, |
| 49 | /// Reference voltage source selection | ||
| 61 | pub reference_voltage_source: Refsel, | 50 | pub reference_voltage_source: Refsel, |
| 51 | /// Power configuration selection. | ||
| 62 | pub power_level_mode: Pwrsel, | 52 | pub power_level_mode: Pwrsel, |
| 53 | /// Trigger priority policy for handling multiple triggers | ||
| 63 | pub trigger_priority_policy: TriggerPriorityPolicy, | 54 | pub trigger_priority_policy: TriggerPriorityPolicy, |
| 55 | /// Enables the ADC pausing function. When enabled, a programmable delay is inserted during command execution sequencing between LOOP iterations, | ||
| 56 | /// between commands in a sequence, and between conversions when command is executing in "Compare Until True" configuration. | ||
| 64 | pub enable_conv_pause: bool, | 57 | pub enable_conv_pause: bool, |
| 58 | /// Controls the duration of pausing during command execution sequencing. The pause delay is a count of (convPauseDelay*4) ADCK cycles. | ||
| 59 | /// Only available when ADC pausing function is enabled. The available value range is in 9-bit. | ||
| 65 | pub conv_pause_delay: u16, | 60 | pub conv_pause_delay: u16, |
| 66 | pub fifo_watermark: u8, | 61 | /// Power configuration (normal/deep sleep behavior) |
| 67 | pub power: PoweredClock, | 62 | pub power: PoweredClock, |
| 63 | /// ADC clock source selection | ||
| 68 | pub source: AdcClockSel, | 64 | pub source: AdcClockSel, |
| 65 | /// Clock divider for ADC clock | ||
| 69 | pub div: Div4, | 66 | pub div: Div4, |
| 70 | } | 67 | } |
| 71 | 68 | ||
| @@ -81,7 +78,6 @@ impl Default for LpadcConfig { | |||
| 81 | trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, | 78 | trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, |
| 82 | enable_conv_pause: false, | 79 | enable_conv_pause: false, |
| 83 | conv_pause_delay: 0, | 80 | conv_pause_delay: 0, |
| 84 | fifo_watermark: 0, | ||
| 85 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | 81 | power: PoweredClock::NormalEnabledDeepSleepDisabled, |
| 86 | source: AdcClockSel::FroLfDiv, | 82 | source: AdcClockSel::FroLfDiv, |
| 87 | div: Div4::no_div(), | 83 | div: Div4::no_div(), |
| @@ -89,10 +85,11 @@ impl Default for LpadcConfig { | |||
| 89 | } | 85 | } |
| 90 | } | 86 | } |
| 91 | 87 | ||
| 88 | /// Configuration for a conversion command. | ||
| 89 | /// | ||
| 90 | /// Defines the parameters for a single ADC conversion operation. | ||
| 92 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 91 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 93 | pub struct ConvCommandConfig { | 92 | pub struct ConvCommandConfig { |
| 94 | pub sample_channel_mode: Ctype, | ||
| 95 | pub channel_number: Adch, | ||
| 96 | pub chained_next_command_number: Next, | 93 | pub chained_next_command_number: Next, |
| 97 | pub enable_auto_channel_increment: bool, | 94 | pub enable_auto_channel_increment: bool, |
| 98 | pub loop_count: u8, | 95 | pub loop_count: u8, |
| @@ -105,6 +102,26 @@ pub struct ConvCommandConfig { | |||
| 105 | pub enable_wait_trigger: bool, | 102 | pub enable_wait_trigger: bool, |
| 106 | } | 103 | } |
| 107 | 104 | ||
| 105 | impl Default for ConvCommandConfig { | ||
| 106 | fn default() -> Self { | ||
| 107 | ConvCommandConfig { | ||
| 108 | chained_next_command_number: Next::NoNextCmdTerminateOnFinish, | ||
| 109 | enable_auto_channel_increment: false, | ||
| 110 | loop_count: 0, | ||
| 111 | hardware_average_mode: Avgs::NoAverage, | ||
| 112 | sample_time_mode: Sts::Sample3p5, | ||
| 113 | hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult, | ||
| 114 | hardware_compare_value_high: 0, | ||
| 115 | hardware_compare_value_low: 0, | ||
| 116 | conversion_resolution_mode: Mode::Data12Bits, | ||
| 117 | enable_wait_trigger: false, | ||
| 118 | } | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | /// Configuration for a conversion trigger. | ||
| 123 | /// | ||
| 124 | /// Defines how a trigger initiates ADC conversions. | ||
| 108 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 125 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 109 | pub struct ConvTriggerConfig { | 126 | pub struct ConvTriggerConfig { |
| 110 | pub target_command_id: Tcmd, | 127 | pub target_command_id: Tcmd, |
| @@ -113,32 +130,298 @@ pub struct ConvTriggerConfig { | |||
| 113 | pub enable_hardware_trigger: bool, | 130 | pub enable_hardware_trigger: bool, |
| 114 | } | 131 | } |
| 115 | 132 | ||
| 133 | impl Default for ConvTriggerConfig { | ||
| 134 | fn default() -> Self { | ||
| 135 | ConvTriggerConfig { | ||
| 136 | target_command_id: Tcmd::NotValid, | ||
| 137 | delay_power: 0, | ||
| 138 | priority: Tpri::HighestPriority, | ||
| 139 | enable_hardware_trigger: false, | ||
| 140 | } | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | /// Shorthand for `Result<T>`. | ||
| 145 | pub type Result<T> = core::result::Result<T, Error>; | ||
| 146 | |||
| 147 | /// ADC Error types | ||
| 148 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 149 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 150 | pub enum Error { | ||
| 151 | /// FIFO is empty, no conversion result available | ||
| 152 | FifoEmpty, | ||
| 153 | /// Invalid configuration | ||
| 154 | InvalidConfig, | ||
| 155 | /// Clock configuration error. | ||
| 156 | ClockSetup(ClockError), | ||
| 157 | } | ||
| 158 | |||
| 159 | /// Result of an ADC conversion. | ||
| 160 | /// | ||
| 161 | /// Contains the conversion value and metadata about the conversion. | ||
| 116 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 162 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 117 | pub struct ConvResult { | 163 | pub struct ConvResult { |
| 118 | pub command_id_source: u32, | 164 | pub command_id_source: u8, |
| 119 | pub loop_count_index: u32, | 165 | pub loop_count_index: u8, |
| 120 | pub trigger_id_source: u32, | 166 | pub trigger_id_source: u8, |
| 121 | pub conv_value: u16, | 167 | pub conv_value: u16, |
| 122 | } | 168 | } |
| 123 | 169 | ||
| 124 | pub struct Adc<'a, I: Instance> { | 170 | /// ADC interrupt handler. |
| 125 | _inst: core::marker::PhantomData<&'a mut I>, | 171 | pub struct InterruptHandler<I: Instance> { |
| 172 | _phantom: PhantomData<I>, | ||
| 173 | } | ||
| 174 | |||
| 175 | /// ADC driver instance. | ||
| 176 | pub struct Adc<'a, M: ModeAdc> { | ||
| 177 | _inst: PhantomData<&'a mut ()>, | ||
| 178 | mode: M, | ||
| 179 | |||
| 180 | // The channel index of the pin used to create our ADC instance | ||
| 181 | channel_idx: u8, | ||
| 182 | |||
| 183 | // The register block of the ADC instance | ||
| 184 | info: &'static pac::adc0::RegisterBlock, | ||
| 185 | } | ||
| 186 | |||
| 187 | impl<'a> Adc<'a, Blocking> { | ||
| 188 | /// Create a new blocking instance of the ADC driver. | ||
| 189 | /// # Arguments | ||
| 190 | /// * `_inst` - ADC peripheral instance | ||
| 191 | /// * `pin` - GPIO pin to use for ADC | ||
| 192 | /// * `config` - ADC configuration | ||
| 193 | pub fn new_blocking<I: Instance>( | ||
| 194 | _inst: Peri<'a, I>, | ||
| 195 | pin: Peri<'a, impl AdcPin<I>>, | ||
| 196 | config: LpadcConfig, | ||
| 197 | ) -> Result<Self> { | ||
| 198 | Self::new_inner(_inst, pin, config, Blocking) | ||
| 199 | } | ||
| 200 | |||
| 201 | /// Enable ADC interrupts. | ||
| 202 | /// | ||
| 203 | /// Enables the interrupt sources specified in the bitmask. | ||
| 204 | /// | ||
| 205 | /// # Arguments | ||
| 206 | /// * `mask` - Bitmask of interrupt sources to enable | ||
| 207 | pub fn enable_interrupt(&mut self, mask: u32) { | ||
| 208 | self.info.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) }); | ||
| 209 | } | ||
| 210 | |||
| 211 | /// Disable ADC interrupts. | ||
| 212 | /// | ||
| 213 | /// Disables the interrupt sources specified in the bitmask. | ||
| 214 | /// | ||
| 215 | /// # Arguments | ||
| 216 | /// * `mask` - Bitmask of interrupt sources to disable | ||
| 217 | pub fn disable_interrupt(&mut self, mask: u32) { | ||
| 218 | self.info.ie().modify(|r, w| unsafe { w.bits(r.bits() & !mask) }); | ||
| 219 | } | ||
| 220 | |||
| 221 | pub fn set_fifo_watermark(&mut self, watermark: u8) -> Result<()> { | ||
| 222 | if watermark > 0b111 { | ||
| 223 | return Err(Error::InvalidConfig); | ||
| 224 | } | ||
| 225 | self.info.fctrl0().modify(|_r, w| unsafe { w.fwmark().bits(watermark) }); | ||
| 226 | Ok(()) | ||
| 227 | } | ||
| 228 | |||
| 229 | /// Trigger ADC conversion(s) via software. | ||
| 230 | /// | ||
| 231 | /// Initiates conversion(s) for the trigger(s) specified in the bitmask. | ||
| 232 | /// Each bit in the mask corresponds to a trigger ID (bit 0 = trigger 0, etc.). | ||
| 233 | /// | ||
| 234 | /// # Arguments | ||
| 235 | /// * `trigger_id_mask` - Bitmask of trigger IDs to activate (bit N = trigger N) | ||
| 236 | /// | ||
| 237 | /// # Returns | ||
| 238 | /// * `Ok(())` if the triger mask was valid | ||
| 239 | /// * `Err(Error::InvalidConfig)` if the mask was greater than `0b1111` | ||
| 240 | pub fn do_software_trigger(&self, trigger_id_mask: u8) -> Result<()> { | ||
| 241 | if trigger_id_mask > 0b1111 { | ||
| 242 | return Err(Error::InvalidConfig); | ||
| 243 | } | ||
| 244 | self.info.swtrig().write(|w| unsafe { w.bits(trigger_id_mask as u32) }); | ||
| 245 | Ok(()) | ||
| 246 | } | ||
| 247 | |||
| 248 | /// Set conversion command configuration. | ||
| 249 | /// | ||
| 250 | /// Configures a conversion command slot with the specified parameters. | ||
| 251 | /// Commands define how conversions are performed (channel, resolution, etc.). | ||
| 252 | /// | ||
| 253 | /// # Arguments | ||
| 254 | /// * `index` - Command index (Must be in range 1..=7) | ||
| 255 | /// * `config` - Command configuration | ||
| 256 | /// | ||
| 257 | /// # Returns | ||
| 258 | /// * `Ok(())` if the command was configured successfully | ||
| 259 | /// * `Err(Error::InvalidConfig)` if the index is out of range | ||
| 260 | pub fn set_conv_command_config(&self, index: usize, config: &ConvCommandConfig) -> Result<()> { | ||
| 261 | self.set_conv_command_config_inner(index, config) | ||
| 262 | } | ||
| 263 | |||
| 264 | /// Set conversion trigger configuration. | ||
| 265 | /// | ||
| 266 | /// Configures a trigger to initiate conversions. Triggers can be | ||
| 267 | /// activated by software or hardware signals. | ||
| 268 | /// | ||
| 269 | /// # Arguments | ||
| 270 | /// * `trigger_id` - Trigger index (0..=3) | ||
| 271 | /// * `config` - Trigger configuration | ||
| 272 | pub fn set_conv_trigger_config(&self, trigger_id: usize, config: &ConvTriggerConfig) -> Result<()> { | ||
| 273 | self.set_conv_trigger_config_inner(trigger_id, config) | ||
| 274 | } | ||
| 275 | |||
| 276 | /// Reset the FIFO buffer. | ||
| 277 | /// | ||
| 278 | /// Clears all pending conversion results from the FIFO. | ||
| 279 | pub fn do_reset_fifo(&self) { | ||
| 280 | self.info.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); | ||
| 281 | } | ||
| 282 | |||
| 283 | /// Get conversion result from FIFO. | ||
| 284 | /// | ||
| 285 | /// Reads and returns the next conversion result from the FIFO. | ||
| 286 | /// Returns `None` if the FIFO is empty. | ||
| 287 | /// | ||
| 288 | /// # Returns | ||
| 289 | /// - `Some(ConvResult)` if a result is available | ||
| 290 | /// - `Err(Error::FifoEmpty)` if the FIFO is empty | ||
| 291 | pub fn get_conv_result(&self) -> Result<ConvResult> { | ||
| 292 | self.get_conv_result_inner() | ||
| 293 | } | ||
| 126 | } | 294 | } |
| 127 | 295 | ||
| 128 | impl<'a, I: Instance> Adc<'a, I> { | 296 | impl<'a> Adc<'a, Async> { |
| 129 | /// initialize ADC | 297 | /// Initialize ADC with interrupt support. |
| 130 | pub fn new(_inst: Peri<'a, I>, config: LpadcConfig) -> Self { | 298 | /// |
| 131 | let adc = unsafe { &*I::ptr() }; | 299 | /// # Arguments |
| 300 | /// * `_inst` - ADC peripheral instance | ||
| 301 | /// * `pin` - GPIO pin to use for ADC | ||
| 302 | /// * `_irq` - Interrupt binding for this ADC instance | ||
| 303 | /// * `config` - ADC configuration | ||
| 304 | pub fn new_async<I: Instance>( | ||
| 305 | _inst: Peri<'a, I>, | ||
| 306 | pin: Peri<'a, impl AdcPin<I>>, | ||
| 307 | _irq: impl crate::interrupt::typelevel::Binding<I::Interrupt, InterruptHandler<I>> + 'a, | ||
| 308 | config: LpadcConfig, | ||
| 309 | ) -> Result<Self> { | ||
| 310 | let adc = Self::new_inner(_inst, pin, config, Async { waiter: I::wait_cell() })?; | ||
| 311 | |||
| 312 | I::Interrupt::unpend(); | ||
| 313 | unsafe { I::Interrupt::enable() }; | ||
| 314 | |||
| 315 | let cfg = ConvCommandConfig { | ||
| 316 | chained_next_command_number: Next::NoNextCmdTerminateOnFinish, | ||
| 317 | enable_auto_channel_increment: false, | ||
| 318 | loop_count: 0, | ||
| 319 | hardware_average_mode: Avgs::NoAverage, | ||
| 320 | sample_time_mode: Sts::Sample3p5, | ||
| 321 | hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult, | ||
| 322 | hardware_compare_value_high: 0, | ||
| 323 | hardware_compare_value_low: 0, | ||
| 324 | conversion_resolution_mode: Mode::Data16Bits, | ||
| 325 | enable_wait_trigger: false, | ||
| 326 | }; | ||
| 327 | |||
| 328 | // We always use command 1, so this cannot fail | ||
| 329 | _ = adc.set_conv_command_config_inner(1, &cfg); | ||
| 330 | |||
| 331 | let cfg = ConvTriggerConfig { | ||
| 332 | target_command_id: Tcmd::ExecuteCmd1, | ||
| 333 | delay_power: 0, | ||
| 334 | priority: Tpri::HighestPriority, | ||
| 335 | enable_hardware_trigger: false, | ||
| 336 | }; | ||
| 337 | |||
| 338 | // We always use trigger 0, so this cannot fail | ||
| 339 | _ = adc.set_conv_trigger_config_inner(0, &cfg); | ||
| 340 | |||
| 341 | // We always set the watermark to 0 (trigger when 1 is available) | ||
| 342 | I::ptr().fctrl0().modify(|_r, w| unsafe { w.fwmark().bits(0) }); | ||
| 343 | |||
| 344 | Ok(adc) | ||
| 345 | } | ||
| 346 | |||
| 347 | /// Set the number of averages | ||
| 348 | pub fn set_averages(&mut self, avgs: Avgs) { | ||
| 349 | // TODO: we should probably return a result or wait for idle? | ||
| 350 | // "A write to a CMD buffer while that CMD buffer is controlling the ADC operation may cause unpredictable behavior." | ||
| 351 | self.info.cmdh1().modify(|_r, w| w.avgs().variant(avgs)); | ||
| 352 | } | ||
| 132 | 353 | ||
| 133 | let _clock_freq = unsafe { | 354 | /// Set the sample time |
| 355 | pub fn set_sample_time(&mut self, st: Sts) { | ||
| 356 | // TODO: we should probably return a result or wait for idle? | ||
| 357 | // "A write to a CMD buffer while that CMD buffer is controlling the ADC operation may cause unpredictable behavior." | ||
| 358 | self.info.cmdh1().modify(|_r, w| w.sts().variant(st)); | ||
| 359 | } | ||
| 360 | |||
| 361 | pub fn set_resolution(&mut self, mode: Mode) { | ||
| 362 | // TODO: we should probably return a result or wait for idle? | ||
| 363 | // "A write to a CMD buffer while that CMD buffer is controlling the ADC operation may cause unpredictable behavior." | ||
| 364 | self.info.cmdl1().modify(|_r, w| w.mode().variant(mode)); | ||
| 365 | } | ||
| 366 | |||
| 367 | fn wait_idle(&mut self) -> impl Future<Output = core::result::Result<(), maitake_sync::Closed>> + use<'_> { | ||
| 368 | self.mode | ||
| 369 | .waiter | ||
| 370 | .wait_for(|| self.info.ie().read().fwmie0().bit_is_clear()) | ||
| 371 | } | ||
| 372 | |||
| 373 | /// Read ADC value asynchronously. | ||
| 374 | /// | ||
| 375 | /// Performs a single ADC conversion and returns the result when the ADC interrupt is triggered. | ||
| 376 | /// | ||
| 377 | /// The function: | ||
| 378 | /// 1. Enables the FIFO watermark interrupt | ||
| 379 | /// 2. Triggers a software conversion on trigger 0 | ||
| 380 | /// 3. Waits for the conversion to complete | ||
| 381 | /// 4. Returns the conversion result | ||
| 382 | /// | ||
| 383 | /// # Returns | ||
| 384 | /// 16-bit ADC conversion value | ||
| 385 | pub async fn read(&mut self) -> Result<u16> { | ||
| 386 | // If we cancelled a previous read, we might still be busy, wait | ||
| 387 | // until the interrupt is cleared (done by the interrupt) | ||
| 388 | _ = self.wait_idle().await; | ||
| 389 | |||
| 390 | // Clear the fifo | ||
| 391 | self.info.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); | ||
| 392 | |||
| 393 | // Trigger a new conversion | ||
| 394 | self.info.ie().modify(|_r, w| w.fwmie0().set_bit()); | ||
| 395 | self.info.swtrig().write(|w| w.swt0().set_bit()); | ||
| 396 | |||
| 397 | // Wait for completion | ||
| 398 | _ = self.wait_idle().await; | ||
| 399 | |||
| 400 | self.get_conv_result_inner().map(|r| r.conv_value) | ||
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | impl<'a, M: ModeAdc> Adc<'a, M> { | ||
| 405 | /// Internal initialization function shared by `new_async` and `new_blocking`. | ||
| 406 | fn new_inner<I: Instance, P: AdcPin<I>>( | ||
| 407 | _inst: Peri<'a, I>, | ||
| 408 | pin: Peri<'a, P>, | ||
| 409 | config: LpadcConfig, | ||
| 410 | mode: M, | ||
| 411 | ) -> Result<Self> { | ||
| 412 | let adc = I::ptr(); | ||
| 413 | |||
| 414 | _ = unsafe { | ||
| 134 | enable_and_reset::<I>(&AdcConfig { | 415 | enable_and_reset::<I>(&AdcConfig { |
| 135 | power: config.power, | 416 | power: config.power, |
| 136 | source: config.source, | 417 | source: config.source, |
| 137 | div: config.div, | 418 | div: config.div, |
| 138 | }) | 419 | }) |
| 139 | .expect("Adc Init should not fail") | 420 | .map_err(Error::ClockSetup)? |
| 140 | }; | 421 | }; |
| 141 | 422 | ||
| 423 | pin.mux(); | ||
| 424 | |||
| 142 | /* Reset the module. */ | 425 | /* Reset the module. */ |
| 143 | adc.ctrl().modify(|_, w| w.rst().held_in_reset()); | 426 | adc.ctrl().modify(|_, w| w.rst().held_in_reset()); |
| 144 | adc.ctrl().modify(|_, w| w.rst().released_from_reset()); | 427 | adc.ctrl().modify(|_, w| w.rst().released_from_reset()); |
| @@ -149,22 +432,14 @@ impl<'a, I: Instance> Adc<'a, I> { | |||
| 149 | adc.ctrl().modify(|_, w| w.adcen().disabled()); | 432 | adc.ctrl().modify(|_, w| w.adcen().disabled()); |
| 150 | 433 | ||
| 151 | /* Configure the module generally. */ | 434 | /* Configure the module generally. */ |
| 152 | if config.enable_in_doze_mode { | 435 | adc.ctrl().modify(|_, w| w.dozen().bit(config.enable_in_doze_mode)); |
| 153 | adc.ctrl().modify(|_, w| w.dozen().enabled()); | ||
| 154 | } else { | ||
| 155 | adc.ctrl().modify(|_, w| w.dozen().disabled()); | ||
| 156 | } | ||
| 157 | 436 | ||
| 158 | /* Set calibration average mode. */ | 437 | /* Set calibration average mode. */ |
| 159 | adc.ctrl() | 438 | adc.ctrl() |
| 160 | .modify(|_, w| w.cal_avgs().variant(config.conversion_average_mode)); | 439 | .modify(|_, w| w.cal_avgs().variant(config.conversion_average_mode)); |
| 161 | 440 | ||
| 162 | adc.cfg().write(|w| unsafe { | 441 | adc.cfg().write(|w| unsafe { |
| 163 | let w = if config.enable_analog_preliminary { | 442 | w.pwren().bit(config.enable_analog_preliminary); |
| 164 | w.pwren().pre_enabled() | ||
| 165 | } else { | ||
| 166 | w | ||
| 167 | }; | ||
| 168 | 443 | ||
| 169 | w.pudly() | 444 | w.pudly() |
| 170 | .bits(config.power_up_delay) | 445 | .bits(config.power_up_delay) |
| @@ -214,32 +489,38 @@ impl<'a, I: Instance> Adc<'a, I> { | |||
| 214 | adc.pause().write(|w| unsafe { w.bits(0) }); | 489 | adc.pause().write(|w| unsafe { w.bits(0) }); |
| 215 | } | 490 | } |
| 216 | 491 | ||
| 217 | adc.fctrl0() | 492 | adc.fctrl0().write(|w| unsafe { w.fwmark().bits(0) }); |
| 218 | .write(|w| unsafe { w.fwmark().bits(config.fifo_watermark) }); | ||
| 219 | 493 | ||
| 220 | // Enable ADC | 494 | // Enable ADC |
| 221 | adc.ctrl().modify(|_, w| w.adcen().enabled()); | 495 | adc.ctrl().modify(|_, w| w.adcen().enabled()); |
| 222 | 496 | ||
| 223 | Self { | 497 | Ok(Self { |
| 224 | _inst: core::marker::PhantomData, | 498 | _inst: PhantomData, |
| 225 | } | 499 | mode, |
| 226 | } | 500 | channel_idx: P::CHANNEL, |
| 227 | 501 | info: adc, | |
| 228 | pub fn deinit(&self) { | 502 | }) |
| 229 | let adc = unsafe { &*I::ptr() }; | ||
| 230 | adc.ctrl().modify(|_, w| w.adcen().disabled()); | ||
| 231 | } | 503 | } |
| 232 | 504 | ||
| 505 | /// Perform offset calibration. | ||
| 506 | /// Waits for calibration to complete before returning. | ||
| 233 | pub fn do_offset_calibration(&self) { | 507 | pub fn do_offset_calibration(&self) { |
| 234 | let adc = unsafe { &*I::ptr() }; | ||
| 235 | // Enable calibration mode | 508 | // Enable calibration mode |
| 236 | adc.ctrl() | 509 | self.info |
| 510 | .ctrl() | ||
| 237 | .modify(|_, w| w.calofs().offset_calibration_request_pending()); | 511 | .modify(|_, w| w.calofs().offset_calibration_request_pending()); |
| 238 | 512 | ||
| 239 | // Wait for calibration to complete (polling status register) | 513 | // Wait for calibration to complete (polling status register) |
| 240 | while adc.stat().read().cal_rdy().is_not_set() {} | 514 | while self.info.stat().read().cal_rdy().is_not_set() {} |
| 241 | } | 515 | } |
| 242 | 516 | ||
| 517 | /// Calculate gain conversion result from gain adjustment factor. | ||
| 518 | /// | ||
| 519 | /// # Arguments | ||
| 520 | /// * `gain_adjustment` - Gain adjustment factor | ||
| 521 | /// | ||
| 522 | /// # Returns | ||
| 523 | /// Gain calibration register value | ||
| 243 | pub fn get_gain_conv_result(&self, mut gain_adjustment: f32) -> u32 { | 524 | pub fn get_gain_conv_result(&self, mut gain_adjustment: f32) -> u32 { |
| 244 | let mut gcra_array = [0u32; 17]; | 525 | let mut gcra_array = [0u32; 17]; |
| 245 | let mut gcalr: u32 = 0; | 526 | let mut gcalr: u32 = 0; |
| @@ -258,102 +539,80 @@ impl<'a, I: Instance> Adc<'a, I> { | |||
| 258 | gcalr | 539 | gcalr |
| 259 | } | 540 | } |
| 260 | 541 | ||
| 542 | /// Perform automatic gain calibration. | ||
| 261 | pub fn do_auto_calibration(&self) { | 543 | pub fn do_auto_calibration(&self) { |
| 262 | let adc = unsafe { &*I::ptr() }; | 544 | self.info |
| 263 | adc.ctrl().modify(|_, w| w.cal_req().calibration_request_pending()); | 545 | .ctrl() |
| 546 | .modify(|_, w| w.cal_req().calibration_request_pending()); | ||
| 264 | 547 | ||
| 265 | while adc.gcc0().read().rdy().is_gain_cal_not_valid() {} | 548 | while self.info.gcc0().read().rdy().is_gain_cal_not_valid() {} |
| 266 | 549 | ||
| 267 | let mut gcca = adc.gcc0().read().gain_cal().bits() as u32; | 550 | let mut gcca = self.info.gcc0().read().gain_cal().bits() as u32; |
| 268 | if gcca & ((0xFFFF + 1) >> 1) != 0 { | 551 | if gcca & 0x8000 != 0 { |
| 269 | gcca |= !0xFFFF; | 552 | gcca |= !0xFFFF; |
| 270 | } | 553 | } |
| 271 | 554 | ||
| 272 | let gcra = 131072.0 / (131072.0 - gcca as f32); | 555 | let gcra = 131072.0 / (131072.0 - gcca as f32); |
| 273 | 556 | ||
| 274 | // Write to GCR0 | 557 | // Write to GCR0 |
| 275 | adc.gcr0().write(|w| unsafe { w.bits(self.get_gain_conv_result(gcra)) }); | 558 | self.info |
| 559 | .gcr0() | ||
| 560 | .write(|w| unsafe { w.bits(self.get_gain_conv_result(gcra)) }); | ||
| 276 | 561 | ||
| 277 | adc.gcr0().modify(|_, w| w.rdy().set_bit()); | 562 | self.info.gcr0().modify(|_, w| w.rdy().set_bit()); |
| 278 | 563 | ||
| 279 | // Wait for calibration to complete (polling status register) | 564 | // Wait for calibration to complete (polling status register) |
| 280 | while adc.stat().read().cal_rdy().is_not_set() {} | 565 | while self.info.stat().read().cal_rdy().is_not_set() {} |
| 281 | } | 566 | } |
| 282 | 567 | ||
| 283 | pub fn do_software_trigger(&self, trigger_id_mask: u32) { | 568 | fn set_conv_command_config_inner(&self, index: usize, config: &ConvCommandConfig) -> Result<()> { |
| 284 | let adc = unsafe { &*I::ptr() }; | 569 | let (cmdl, cmdh) = match index { |
| 285 | adc.swtrig().write(|w| unsafe { w.bits(trigger_id_mask) }); | 570 | 1 => (self.info.cmdl1(), self.info.cmdh1()), |
| 286 | } | 571 | 2 => (self.info.cmdl2(), self.info.cmdh2()), |
| 572 | 3 => (self.info.cmdl3(), self.info.cmdh3()), | ||
| 573 | 4 => (self.info.cmdl4(), self.info.cmdh4()), | ||
| 574 | 5 => (self.info.cmdl5(), self.info.cmdh5()), | ||
| 575 | 6 => (self.info.cmdl6(), self.info.cmdh6()), | ||
| 576 | 7 => (self.info.cmdl7(), self.info.cmdh7()), | ||
| 577 | _ => return Err(Error::InvalidConfig), | ||
| 578 | }; | ||
| 287 | 579 | ||
| 288 | pub fn get_default_conv_command_config(&self) -> ConvCommandConfig { | 580 | cmdl.write(|w| { |
| 289 | ConvCommandConfig { | 581 | unsafe { |
| 290 | sample_channel_mode: Ctype::SingleEndedASideChannel, | 582 | w.adch().bits(self.channel_idx); |
| 291 | channel_number: Adch::SelectCh0, | 583 | } |
| 292 | chained_next_command_number: Next::NoNextCmdTerminateOnFinish, | 584 | w.mode().variant(config.conversion_resolution_mode) |
| 293 | enable_auto_channel_increment: false, | 585 | }); |
| 294 | loop_count: 0, | ||
| 295 | hardware_average_mode: Avgs::NoAverage, | ||
| 296 | sample_time_mode: Sts::Sample3p5, | ||
| 297 | hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult, | ||
| 298 | hardware_compare_value_high: 0, | ||
| 299 | hardware_compare_value_low: 0, | ||
| 300 | conversion_resolution_mode: Mode::Data12Bits, | ||
| 301 | enable_wait_trigger: false, | ||
| 302 | } | ||
| 303 | } | ||
| 304 | 586 | ||
| 305 | //TBD Need to add cmdlx and cmdhx with x {2..7} | 587 | cmdh.write(|w| { |
| 306 | pub fn set_conv_command_config(&self, index: u32, config: &ConvCommandConfig) { | 588 | w.next().variant(config.chained_next_command_number); |
| 307 | let adc = unsafe { &*I::ptr() }; | 589 | unsafe { |
| 308 | 590 | w.loop_().bits(config.loop_count); | |
| 309 | match index { | ||
| 310 | 1 => { | ||
| 311 | adc.cmdl1().write(|w| { | ||
| 312 | w.adch() | ||
| 313 | .variant(config.channel_number) | ||
| 314 | .mode() | ||
| 315 | .variant(config.conversion_resolution_mode) | ||
| 316 | }); | ||
| 317 | adc.cmdh1().write(|w| unsafe { | ||
| 318 | w.next() | ||
| 319 | .variant(config.chained_next_command_number) | ||
| 320 | .loop_() | ||
| 321 | .bits(config.loop_count) | ||
| 322 | .avgs() | ||
| 323 | .variant(config.hardware_average_mode) | ||
| 324 | .sts() | ||
| 325 | .variant(config.sample_time_mode) | ||
| 326 | .cmpen() | ||
| 327 | .variant(config.hardware_compare_mode); | ||
| 328 | if config.enable_wait_trigger { | ||
| 329 | w.wait_trig().enabled(); | ||
| 330 | } | ||
| 331 | if config.enable_auto_channel_increment { | ||
| 332 | w.lwi().enabled(); | ||
| 333 | } | ||
| 334 | w | ||
| 335 | }); | ||
| 336 | } | 591 | } |
| 337 | _ => panic!("Invalid command index: must be between 1 and 7"), | 592 | w.avgs().variant(config.hardware_average_mode); |
| 338 | } | 593 | w.sts().variant(config.sample_time_mode); |
| 594 | w.cmpen().variant(config.hardware_compare_mode); | ||
| 595 | w.wait_trig().bit(config.enable_wait_trigger); | ||
| 596 | w.lwi().bit(config.enable_auto_channel_increment); | ||
| 597 | w | ||
| 598 | }); | ||
| 599 | |||
| 600 | Ok(()) | ||
| 339 | } | 601 | } |
| 340 | 602 | ||
| 341 | pub fn get_default_conv_trigger_config(&self) -> ConvTriggerConfig { | 603 | fn set_conv_trigger_config_inner(&self, trigger_id: usize, config: &ConvTriggerConfig) -> Result<()> { |
| 342 | ConvTriggerConfig { | 604 | // 0..4 are valid |
| 343 | target_command_id: Tcmd::NotValid, | 605 | if trigger_id >= 4 { |
| 344 | delay_power: 0, | 606 | return Err(Error::InvalidConfig); |
| 345 | priority: Tpri::HighestPriority, | ||
| 346 | enable_hardware_trigger: false, | ||
| 347 | } | 607 | } |
| 348 | } | ||
| 349 | 608 | ||
| 350 | pub fn set_conv_trigger_config(&self, trigger_id: usize, config: &ConvTriggerConfig) { | 609 | let tctrl = &self.info.tctrl(trigger_id); |
| 351 | let adc = unsafe { &*I::ptr() }; | ||
| 352 | let tctrl = &adc.tctrl(trigger_id); | ||
| 353 | 610 | ||
| 354 | tctrl.write(|w| unsafe { | 611 | tctrl.write(|w| { |
| 355 | let w = w.tcmd().variant(config.target_command_id); | 612 | w.tcmd().variant(config.target_command_id); |
| 356 | let w = w.tdly().bits(config.delay_power); | 613 | unsafe { |
| 614 | w.tdly().bits(config.delay_power); | ||
| 615 | } | ||
| 357 | w.tpri().variant(config.priority); | 616 | w.tpri().variant(config.priority); |
| 358 | if config.enable_hardware_trigger { | 617 | if config.enable_hardware_trigger { |
| 359 | w.hten().enabled() | 618 | w.hten().enabled() |
| @@ -361,49 +620,230 @@ impl<'a, I: Instance> Adc<'a, I> { | |||
| 361 | w | 620 | w |
| 362 | } | 621 | } |
| 363 | }); | 622 | }); |
| 364 | } | ||
| 365 | 623 | ||
| 366 | pub fn do_reset_fifo(&self) { | 624 | Ok(()) |
| 367 | let adc = unsafe { &*I::ptr() }; | ||
| 368 | adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); | ||
| 369 | } | 625 | } |
| 370 | 626 | ||
| 371 | pub fn enable_interrupt(&self, mask: u32) { | 627 | /// Get conversion result from FIFO. |
| 372 | let adc = unsafe { &*I::ptr() }; | 628 | /// |
| 373 | adc.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) }); | 629 | /// Reads and returns the next conversion result from the FIFO. |
| 374 | INTERRUPT_TRIGGERED.store(false, Ordering::SeqCst); | 630 | /// Returns `None` if the FIFO is empty. |
| 375 | } | 631 | /// |
| 632 | /// # Returns | ||
| 633 | /// - `Some(ConvResult)` if a result is available | ||
| 634 | /// - `Err(Error::FifoEmpty)` if the FIFO is empty | ||
| 635 | fn get_conv_result_inner(&self) -> Result<ConvResult> { | ||
| 636 | let fifo = self.info.resfifo0().read(); | ||
| 637 | if !fifo.valid().is_valid() { | ||
| 638 | return Err(Error::FifoEmpty); | ||
| 639 | } | ||
| 376 | 640 | ||
| 377 | pub fn is_interrupt_triggered(&self) -> bool { | 641 | Ok(ConvResult { |
| 378 | INTERRUPT_TRIGGERED.load(Ordering::Relaxed) | 642 | command_id_source: fifo.cmdsrc().bits(), |
| 643 | loop_count_index: fifo.loopcnt().bits(), | ||
| 644 | trigger_id_source: fifo.tsrc().bits(), | ||
| 645 | conv_value: fifo.d().bits(), | ||
| 646 | }) | ||
| 379 | } | 647 | } |
| 380 | } | 648 | } |
| 381 | 649 | ||
| 382 | pub fn get_conv_result() -> Option<ConvResult> { | 650 | impl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> { |
| 383 | let adc = unsafe { &*pac::Adc1::ptr() }; | 651 | unsafe fn on_interrupt() { |
| 384 | let fifo = adc.resfifo0().read().bits(); | 652 | T::ptr().ie().modify(|_r, w| w.fwmie0().clear_bit()); |
| 385 | const VALID_MASK: u32 = 1 << 31; | 653 | T::wait_cell().wake(); |
| 386 | if fifo & VALID_MASK == 0 { | ||
| 387 | return None; | ||
| 388 | } | 654 | } |
| 655 | } | ||
| 389 | 656 | ||
| 390 | Some(ConvResult { | 657 | mod sealed { |
| 391 | command_id_source: (fifo >> 24) & 0x0F, | 658 | /// Seal a trait |
| 392 | loop_count_index: (fifo >> 20) & 0x0F, | 659 | pub trait Sealed {} |
| 393 | trigger_id_source: (fifo >> 16) & 0x0F, | ||
| 394 | conv_value: (fifo & 0xFFFF) as u16, | ||
| 395 | }) | ||
| 396 | } | 660 | } |
| 397 | 661 | ||
| 398 | pub fn on_interrupt() { | 662 | impl<I: GpioPin> sealed::Sealed for I {} |
| 399 | if get_conv_result().is_some() { | 663 | |
| 400 | INTERRUPT_TRIGGERED.store(true, Ordering::SeqCst); | 664 | trait SealedInstance { |
| 401 | } | 665 | fn ptr() -> &'static pac::adc0::RegisterBlock; |
| 666 | fn wait_cell() -> &'static WaitCell; | ||
| 402 | } | 667 | } |
| 403 | 668 | ||
| 404 | pub struct AdcHandler; | 669 | /// ADC Instance |
| 405 | impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::ADC1> for AdcHandler { | 670 | #[allow(private_bounds)] |
| 406 | unsafe fn on_interrupt() { | 671 | pub trait Instance: SealedInstance + PeripheralType + Gate<MrccPeriphConfig = AdcConfig> { |
| 407 | on_interrupt(); | 672 | /// Interrupt for this ADC instance. |
| 408 | } | 673 | type Interrupt: Interrupt; |
| 409 | } | 674 | } |
| 675 | |||
| 676 | macro_rules! impl_instance { | ||
| 677 | ($($n:expr),*) => { | ||
| 678 | $( | ||
| 679 | paste!{ | ||
| 680 | impl SealedInstance for crate::peripherals::[<ADC $n>] { | ||
| 681 | fn ptr() -> &'static pac::adc0::RegisterBlock { | ||
| 682 | unsafe { &*pac::[<Adc $n>]::ptr() } | ||
| 683 | } | ||
| 684 | |||
| 685 | fn wait_cell() -> &'static WaitCell { | ||
| 686 | static WAIT_CELL: WaitCell = WaitCell::new(); | ||
| 687 | &WAIT_CELL | ||
| 688 | } | ||
| 689 | |||
| 690 | } | ||
| 691 | |||
| 692 | impl Instance for crate::peripherals::[<ADC $n>] { | ||
| 693 | type Interrupt = crate::interrupt::typelevel::[<ADC $n>]; | ||
| 694 | } | ||
| 695 | } | ||
| 696 | )* | ||
| 697 | }; | ||
| 698 | } | ||
| 699 | |||
| 700 | impl_instance!(0, 1, 2, 3); | ||
| 701 | |||
| 702 | pub trait AdcPin<Instance>: GpioPin + sealed::Sealed + PeripheralType { | ||
| 703 | const CHANNEL: u8; | ||
| 704 | |||
| 705 | /// Set the given pin to the correct muxing state | ||
| 706 | fn mux(&self); | ||
| 707 | } | ||
| 708 | |||
| 709 | /// Driver mode. | ||
| 710 | #[allow(private_bounds)] | ||
| 711 | pub trait ModeAdc: sealed::Sealed {} | ||
| 712 | |||
| 713 | /// Blocking mode. | ||
| 714 | pub struct Blocking; | ||
| 715 | impl sealed::Sealed for Blocking {} | ||
| 716 | impl ModeAdc for Blocking {} | ||
| 717 | |||
| 718 | /// Async mode. | ||
| 719 | pub struct Async { | ||
| 720 | waiter: &'static WaitCell, | ||
| 721 | } | ||
| 722 | impl sealed::Sealed for Async {} | ||
| 723 | impl ModeAdc for Async {} | ||
| 724 | |||
| 725 | macro_rules! impl_pin { | ||
| 726 | ($pin:ident, $peri:ident, $func:ident, $channel:literal) => { | ||
| 727 | impl AdcPin<crate::peripherals::$peri> for crate::peripherals::$pin { | ||
| 728 | const CHANNEL: u8 = $channel; | ||
| 729 | |||
| 730 | fn mux(&self) { | ||
| 731 | self.set_pull(crate::gpio::Pull::Disabled); | ||
| 732 | self.set_slew_rate(crate::gpio::SlewRate::Fast.into()); | ||
| 733 | self.set_drive_strength(crate::gpio::DriveStrength::Normal.into()); | ||
| 734 | self.set_function(crate::pac::port0::pcr0::Mux::$func); | ||
| 735 | } | ||
| 736 | } | ||
| 737 | }; | ||
| 738 | } | ||
| 739 | |||
| 740 | impl_pin!(P2_0, ADC0, Mux0, 0); | ||
| 741 | impl_pin!(P2_4, ADC0, Mux0, 1); | ||
| 742 | impl_pin!(P2_15, ADC0, Mux0, 2); | ||
| 743 | impl_pin!(P2_3, ADC0, Mux0, 3); | ||
| 744 | impl_pin!(P2_2, ADC0, Mux0, 4); | ||
| 745 | impl_pin!(P2_12, ADC0, Mux0, 5); | ||
| 746 | impl_pin!(P2_16, ADC0, Mux0, 6); | ||
| 747 | impl_pin!(P2_7, ADC0, Mux0, 7); | ||
| 748 | impl_pin!(P0_18, ADC0, Mux0, 8); | ||
| 749 | impl_pin!(P0_19, ADC0, Mux0, 9); | ||
| 750 | impl_pin!(P0_20, ADC0, Mux0, 10); | ||
| 751 | impl_pin!(P0_21, ADC0, Mux0, 11); | ||
| 752 | impl_pin!(P0_22, ADC0, Mux0, 12); | ||
| 753 | impl_pin!(P0_23, ADC0, Mux0, 13); | ||
| 754 | impl_pin!(P0_3, ADC0, Mux0, 14); | ||
| 755 | impl_pin!(P0_6, ADC0, Mux0, 15); | ||
| 756 | impl_pin!(P1_0, ADC0, Mux0, 16); | ||
| 757 | impl_pin!(P1_1, ADC0, Mux0, 17); | ||
| 758 | impl_pin!(P1_2, ADC0, Mux0, 18); | ||
| 759 | impl_pin!(P1_3, ADC0, Mux0, 19); | ||
| 760 | impl_pin!(P1_4, ADC0, Mux0, 20); | ||
| 761 | impl_pin!(P1_5, ADC0, Mux0, 21); | ||
| 762 | impl_pin!(P1_6, ADC0, Mux0, 22); | ||
| 763 | impl_pin!(P1_7, ADC0, Mux0, 23); | ||
| 764 | |||
| 765 | // ??? | ||
| 766 | // impl_pin!(P1_10, ADC0, Mux0, 255); | ||
| 767 | |||
| 768 | impl_pin!(P2_1, ADC1, Mux0, 0); | ||
| 769 | impl_pin!(P2_5, ADC1, Mux0, 1); | ||
| 770 | impl_pin!(P2_19, ADC1, Mux0, 2); | ||
| 771 | impl_pin!(P2_6, ADC1, Mux0, 3); | ||
| 772 | impl_pin!(P2_3, ADC1, Mux0, 4); | ||
| 773 | impl_pin!(P2_13, ADC1, Mux0, 5); | ||
| 774 | impl_pin!(P2_17, ADC1, Mux0, 6); | ||
| 775 | impl_pin!(P2_7, ADC1, Mux0, 7); | ||
| 776 | impl_pin!(P1_10, ADC1, Mux0, 8); | ||
| 777 | impl_pin!(P1_11, ADC1, Mux0, 9); | ||
| 778 | impl_pin!(P1_12, ADC1, Mux0, 10); | ||
| 779 | impl_pin!(P1_13, ADC1, Mux0, 11); | ||
| 780 | impl_pin!(P1_14, ADC1, Mux0, 12); | ||
| 781 | impl_pin!(P1_15, ADC1, Mux0, 13); | ||
| 782 | // ??? | ||
| 783 | // impl_pin!(P1_16, ADC1, Mux0, 255); | ||
| 784 | // impl_pin!(P1_17, ADC1, Mux0, 255); | ||
| 785 | // impl_pin!(P1_18, ADC1, Mux0, 255); | ||
| 786 | // impl_pin!(P1_19, ADC1, Mux0, 255); | ||
| 787 | // ??? | ||
| 788 | impl_pin!(P3_31, ADC1, Mux0, 20); | ||
| 789 | impl_pin!(P3_30, ADC1, Mux0, 21); | ||
| 790 | impl_pin!(P3_29, ADC1, Mux0, 22); | ||
| 791 | |||
| 792 | impl_pin!(P2_4, ADC2, Mux0, 0); | ||
| 793 | impl_pin!(P2_10, ADC2, Mux0, 1); | ||
| 794 | impl_pin!(P4_4, ADC2, Mux0, 2); | ||
| 795 | // impl_pin!(P2_24, ADC2, Mux0, 255); ??? | ||
| 796 | impl_pin!(P2_16, ADC2, Mux0, 4); | ||
| 797 | impl_pin!(P2_12, ADC2, Mux0, 5); | ||
| 798 | impl_pin!(P2_20, ADC2, Mux0, 6); | ||
| 799 | impl_pin!(P2_7, ADC2, Mux0, 7); | ||
| 800 | impl_pin!(P0_2, ADC2, Mux0, 8); | ||
| 801 | // ??? | ||
| 802 | // impl_pin!(P0_4, ADC2, Mux0, 255); | ||
| 803 | // impl_pin!(P0_5, ADC2, Mux0, 255); | ||
| 804 | // impl_pin!(P0_6, ADC2, Mux0, 255); | ||
| 805 | // impl_pin!(P0_7, ADC2, Mux0, 255); | ||
| 806 | // impl_pin!(P0_12, ADC2, Mux0, 255); | ||
| 807 | // impl_pin!(P0_13, ADC2, Mux0, 255); | ||
| 808 | // ??? | ||
| 809 | impl_pin!(P0_14, ADC2, Mux0, 14); | ||
| 810 | impl_pin!(P0_15, ADC2, Mux0, 15); | ||
| 811 | // ??? | ||
| 812 | // impl_pin!(P4_0, ADC2, Mux0, 255); | ||
| 813 | // impl_pin!(P4_1, ADC2, Mux0, 255); | ||
| 814 | // ??? | ||
| 815 | impl_pin!(P4_2, ADC2, Mux0, 18); | ||
| 816 | impl_pin!(P4_3, ADC2, Mux0, 19); | ||
| 817 | //impl_pin!(P4_4, ADC2, Mux0, 20); // Conflit with ADC2_A3 and ADC2_A20 using the same pin | ||
| 818 | impl_pin!(P4_5, ADC2, Mux0, 21); | ||
| 819 | impl_pin!(P4_6, ADC2, Mux0, 22); | ||
| 820 | impl_pin!(P4_7, ADC2, Mux0, 23); | ||
| 821 | |||
| 822 | impl_pin!(P2_5, ADC3, Mux0, 0); | ||
| 823 | impl_pin!(P2_11, ADC3, Mux0, 1); | ||
| 824 | impl_pin!(P2_23, ADC3, Mux0, 2); | ||
| 825 | // impl_pin!(P2_25, ADC3, Mux0, 255); // ??? | ||
| 826 | impl_pin!(P2_17, ADC3, Mux0, 4); | ||
| 827 | impl_pin!(P2_13, ADC3, Mux0, 5); | ||
| 828 | impl_pin!(P2_21, ADC3, Mux0, 6); | ||
| 829 | impl_pin!(P2_7, ADC3, Mux0, 7); | ||
| 830 | // ??? | ||
| 831 | // impl_pin!(P3_2, ADC3, Mux0, 255); | ||
| 832 | // impl_pin!(P3_3, ADC3, Mux0, 255); | ||
| 833 | // impl_pin!(P3_4, ADC3, Mux0, 255); | ||
| 834 | // impl_pin!(P3_5, ADC3, Mux0, 255); | ||
| 835 | // ??? | ||
| 836 | impl_pin!(P3_6, ADC3, Mux0, 12); | ||
| 837 | impl_pin!(P3_7, ADC3, Mux0, 13); | ||
| 838 | impl_pin!(P3_12, ADC3, Mux0, 14); | ||
| 839 | impl_pin!(P3_13, ADC3, Mux0, 15); | ||
| 840 | impl_pin!(P3_14, ADC3, Mux0, 16); | ||
| 841 | impl_pin!(P3_15, ADC3, Mux0, 17); | ||
| 842 | impl_pin!(P3_20, ADC3, Mux0, 18); | ||
| 843 | impl_pin!(P3_21, ADC3, Mux0, 19); | ||
| 844 | impl_pin!(P3_22, ADC3, Mux0, 20); | ||
| 845 | // ??? | ||
| 846 | // impl_pin!(P3_23, ADC3, Mux0, 255); | ||
| 847 | // impl_pin!(P3_24, ADC3, Mux0, 255); | ||
| 848 | // impl_pin!(P3_25, ADC3, Mux0, 255); | ||
| 849 | // ??? | ||
diff --git a/embassy-mcxa/src/clocks/mod.rs b/embassy-mcxa/src/clocks/mod.rs index 9288f5dc1..b96d9612a 100644 --- a/embassy-mcxa/src/clocks/mod.rs +++ b/embassy-mcxa/src/clocks/mod.rs | |||
| @@ -945,7 +945,10 @@ pub(crate) mod gate { | |||
| 945 | impl_cc_gate!(LPUART3, mrcc_glb_cc0, mrcc_glb_rst0, lpuart3, LpuartConfig); | 945 | impl_cc_gate!(LPUART3, mrcc_glb_cc0, mrcc_glb_rst0, lpuart3, LpuartConfig); |
| 946 | impl_cc_gate!(LPUART4, mrcc_glb_cc0, mrcc_glb_rst0, lpuart4, LpuartConfig); | 946 | impl_cc_gate!(LPUART4, mrcc_glb_cc0, mrcc_glb_rst0, lpuart4, LpuartConfig); |
| 947 | impl_cc_gate!(LPUART5, mrcc_glb_cc1, mrcc_glb_rst1, lpuart5, LpuartConfig); | 947 | impl_cc_gate!(LPUART5, mrcc_glb_cc1, mrcc_glb_rst1, lpuart5, LpuartConfig); |
| 948 | impl_cc_gate!(ADC0, mrcc_glb_cc1, mrcc_glb_rst1, adc0, AdcConfig); | ||
| 948 | impl_cc_gate!(ADC1, mrcc_glb_cc1, mrcc_glb_rst1, adc1, AdcConfig); | 949 | impl_cc_gate!(ADC1, mrcc_glb_cc1, mrcc_glb_rst1, adc1, AdcConfig); |
| 950 | impl_cc_gate!(ADC2, mrcc_glb_cc1, mrcc_glb_rst1, adc2, AdcConfig); | ||
| 951 | impl_cc_gate!(ADC3, mrcc_glb_cc1, mrcc_glb_rst1, adc3, AdcConfig); | ||
| 949 | 952 | ||
| 950 | // DMA0 peripheral - uses NoConfig since it has no selectable clock source | 953 | // DMA0 peripheral - uses NoConfig since it has no selectable clock source |
| 951 | impl_cc_gate!(DMA0, mrcc_glb_cc0, mrcc_glb_rst0, dma0, NoConfig); | 954 | impl_cc_gate!(DMA0, mrcc_glb_cc0, mrcc_glb_rst0, dma0, NoConfig); |
diff --git a/embassy-mcxa/src/clocks/periph_helpers.rs b/embassy-mcxa/src/clocks/periph_helpers.rs index fed5e558e..f2f51c60c 100644 --- a/embassy-mcxa/src/clocks/periph_helpers.rs +++ b/embassy-mcxa/src/clocks/periph_helpers.rs | |||
| @@ -427,6 +427,7 @@ impl SPConfHelper for OsTimerConfig { | |||
| 427 | 427 | ||
| 428 | /// Selectable clocks for the ADC peripheral | 428 | /// Selectable clocks for the ADC peripheral |
| 429 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | 429 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
| 430 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 430 | pub enum AdcClockSel { | 431 | pub enum AdcClockSel { |
| 431 | /// Divided `fro_lf`/`clk_12m`/FRO12M source | 432 | /// Divided `fro_lf`/`clk_12m`/FRO12M source |
| 432 | FroLfDiv, | 433 | FroLfDiv, |
diff --git a/embassy-mcxa/src/i2c/controller.rs b/embassy-mcxa/src/i2c/controller.rs index c27d508b0..62789f85f 100644 --- a/embassy-mcxa/src/i2c/controller.rs +++ b/embassy-mcxa/src/i2c/controller.rs | |||
| @@ -8,9 +8,9 @@ use embassy_hal_internal::drop::OnDrop; | |||
| 8 | use mcxa_pac::lpi2c0::mtdr::Cmd; | 8 | use mcxa_pac::lpi2c0::mtdr::Cmd; |
| 9 | 9 | ||
| 10 | use super::{Async, Blocking, Error, Instance, InterruptHandler, Mode, Result, SclPin, SdaPin}; | 10 | use super::{Async, Blocking, Error, Instance, InterruptHandler, Mode, Result, SclPin, SdaPin}; |
| 11 | use crate::AnyPin; | ||
| 12 | use crate::clocks::periph_helpers::{Div4, Lpi2cClockSel, Lpi2cConfig}; | 11 | use crate::clocks::periph_helpers::{Div4, Lpi2cClockSel, Lpi2cConfig}; |
| 13 | use crate::clocks::{PoweredClock, enable_and_reset}; | 12 | use crate::clocks::{PoweredClock, enable_and_reset}; |
| 13 | use crate::gpio::AnyPin; | ||
| 14 | use crate::interrupt::typelevel::Interrupt; | 14 | use crate::interrupt::typelevel::Interrupt; |
| 15 | 15 | ||
| 16 | /// Bus speed (nominal SCL, no clock stretching) | 16 | /// Bus speed (nominal SCL, no clock stretching) |
diff --git a/embassy-mcxa/src/interrupt.rs b/embassy-mcxa/src/interrupt.rs index be2704454..b662f7ee0 100644 --- a/embassy-mcxa/src/interrupt.rs +++ b/embassy-mcxa/src/interrupt.rs | |||
| @@ -9,7 +9,10 @@ | |||
| 9 | mod generated { | 9 | mod generated { |
| 10 | #[rustfmt::skip] | 10 | #[rustfmt::skip] |
| 11 | embassy_hal_internal::interrupt_mod!( | 11 | embassy_hal_internal::interrupt_mod!( |
| 12 | ADC0, | ||
| 12 | ADC1, | 13 | ADC1, |
| 14 | ADC2, | ||
| 15 | ADC3, | ||
| 13 | DMA_CH0, | 16 | DMA_CH0, |
| 14 | DMA_CH1, | 17 | DMA_CH1, |
| 15 | DMA_CH2, | 18 | DMA_CH2, |
| @@ -281,44 +284,6 @@ impl InterruptExt for Rtc { | |||
| 281 | } | 284 | } |
| 282 | } | 285 | } |
| 283 | 286 | ||
| 284 | pub struct Adc; | ||
| 285 | pub const ADC1: Adc = Adc; | ||
| 286 | |||
| 287 | impl InterruptExt for Adc { | ||
| 288 | /// Clear any pending ADC1 in NVIC. | ||
| 289 | #[inline] | ||
| 290 | fn unpend(&self) { | ||
| 291 | cortex_m::peripheral::NVIC::unpend(Interrupt::ADC1); | ||
| 292 | } | ||
| 293 | |||
| 294 | /// Set NVIC priority for ADC1. | ||
| 295 | #[inline] | ||
| 296 | fn set_priority(&self, priority: Priority) { | ||
| 297 | unsafe { | ||
| 298 | let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; | ||
| 299 | nvic.set_priority(Interrupt::ADC1, u8::from(priority)); | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | /// Enable ADC1 in NVIC. | ||
| 304 | #[inline] | ||
| 305 | unsafe fn enable(&self) { | ||
| 306 | cortex_m::peripheral::NVIC::unmask(Interrupt::ADC1); | ||
| 307 | } | ||
| 308 | |||
| 309 | /// Disable ADC1 in NVIC. | ||
| 310 | #[inline] | ||
| 311 | unsafe fn disable(&self) { | ||
| 312 | cortex_m::peripheral::NVIC::mask(Interrupt::ADC1); | ||
| 313 | } | ||
| 314 | |||
| 315 | /// Check if ADC1 is pending in NVIC. | ||
| 316 | #[inline] | ||
| 317 | fn is_pending(&self) -> bool { | ||
| 318 | cortex_m::peripheral::NVIC::is_pending(Interrupt::ADC1) | ||
| 319 | } | ||
| 320 | } | ||
| 321 | |||
| 322 | pub struct Gpio0; | 287 | pub struct Gpio0; |
| 323 | pub const GPIO0: Gpio0 = Gpio0; | 288 | pub const GPIO0: Gpio0 = Gpio0; |
| 324 | 289 | ||
diff --git a/embassy-mcxa/src/lib.rs b/embassy-mcxa/src/lib.rs index 12c2708de..6383353db 100644 --- a/embassy-mcxa/src/lib.rs +++ b/embassy-mcxa/src/lib.rs | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | pub mod clocks; // still provide clock helpers | 8 | pub mod clocks; // still provide clock helpers |
| 9 | pub mod dma; | 9 | pub mod dma; |
| 10 | pub mod gpio; | 10 | pub mod gpio; |
| 11 | pub mod pins; // pin mux helpers | ||
| 12 | 11 | ||
| 13 | pub mod adc; | 12 | pub mod adc; |
| 14 | pub mod clkout; | 13 | pub mod clkout; |
| @@ -18,15 +17,19 @@ pub mod i2c; | |||
| 18 | pub mod interrupt; | 17 | pub mod interrupt; |
| 19 | pub mod lpuart; | 18 | pub mod lpuart; |
| 20 | pub mod ostimer; | 19 | pub mod ostimer; |
| 20 | pub mod reset_reason; | ||
| 21 | pub mod rtc; | 21 | pub mod rtc; |
| 22 | pub mod trng; | 22 | pub mod trng; |
| 23 | 23 | ||
| 24 | use crate::interrupt::InterruptExt; | ||
| 24 | pub use crate::pac::NVIC_PRIO_BITS; | 25 | pub use crate::pac::NVIC_PRIO_BITS; |
| 25 | 26 | ||
| 26 | #[rustfmt::skip] | 27 | #[rustfmt::skip] |
| 27 | embassy_hal_internal::peripherals!( | 28 | embassy_hal_internal::peripherals!( |
| 28 | ADC0, | 29 | ADC0, |
| 29 | ADC1, | 30 | ADC1, |
| 31 | ADC2, | ||
| 32 | ADC3, | ||
| 30 | 33 | ||
| 31 | AOI0, | 34 | AOI0, |
| 32 | AOI1, | 35 | AOI1, |
| @@ -337,9 +340,6 @@ embassy_hal_internal::peripherals!( | |||
| 337 | // Use cortex-m-rt's #[interrupt] attribute directly; PAC does not re-export it. | 340 | // Use cortex-m-rt's #[interrupt] attribute directly; PAC does not re-export it. |
| 338 | 341 | ||
| 339 | // Re-export interrupt traits and types | 342 | // Re-export interrupt traits and types |
| 340 | pub use adc::Adc1 as Adc1Token; | ||
| 341 | pub use gpio::{AnyPin, Flex, Gpio as GpioToken, Input, Level, Output}; | ||
| 342 | pub use interrupt::InterruptExt; | ||
| 343 | #[cfg(feature = "unstable-pac")] | 343 | #[cfg(feature = "unstable-pac")] |
| 344 | pub use mcxa_pac as pac; | 344 | pub use mcxa_pac as pac; |
| 345 | #[cfg(not(feature = "unstable-pac"))] | 345 | #[cfg(not(feature = "unstable-pac"))] |
| @@ -355,8 +355,6 @@ pub fn init(cfg: crate::config::Config) -> Peripherals { | |||
| 355 | // Apply user-configured priority early; enabling is left to examples/apps | 355 | // Apply user-configured priority early; enabling is left to examples/apps |
| 356 | crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority); | 356 | crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority); |
| 357 | // Apply user-configured priority early; enabling is left to examples/apps | 357 | // Apply user-configured priority early; enabling is left to examples/apps |
| 358 | crate::interrupt::ADC1.set_priority(cfg.adc_interrupt_priority); | ||
| 359 | // Apply user-configured priority early; enabling is left to examples/apps | ||
| 360 | crate::interrupt::GPIO0.set_priority(cfg.gpio_interrupt_priority); | 358 | crate::interrupt::GPIO0.set_priority(cfg.gpio_interrupt_priority); |
| 361 | // Apply user-configured priority early; enabling is left to examples/apps | 359 | // Apply user-configured priority early; enabling is left to examples/apps |
| 362 | crate::interrupt::GPIO1.set_priority(cfg.gpio_interrupt_priority); | 360 | crate::interrupt::GPIO1.set_priority(cfg.gpio_interrupt_priority); |
diff --git a/embassy-mcxa/src/lpuart/mod.rs b/embassy-mcxa/src/lpuart/mod.rs index e59ce8140..bce3986b5 100644 --- a/embassy-mcxa/src/lpuart/mod.rs +++ b/embassy-mcxa/src/lpuart/mod.rs | |||
| @@ -6,12 +6,12 @@ use paste::paste; | |||
| 6 | 6 | ||
| 7 | use crate::clocks::periph_helpers::{Div4, LpuartClockSel, LpuartConfig}; | 7 | use crate::clocks::periph_helpers::{Div4, LpuartClockSel, LpuartConfig}; |
| 8 | use crate::clocks::{ClockError, Gate, PoweredClock, enable_and_reset}; | 8 | use crate::clocks::{ClockError, Gate, PoweredClock, enable_and_reset}; |
| 9 | use crate::gpio::SealedPin; | 9 | use crate::gpio::{AnyPin, SealedPin}; |
| 10 | use crate::pac::lpuart0::baud::Sbns as StopBits; | 10 | use crate::pac::lpuart0::baud::Sbns as StopBits; |
| 11 | use crate::pac::lpuart0::ctrl::{Idlecfg as IdleConfig, Ilt as IdleType, M as DataBits, Pt as Parity}; | 11 | use crate::pac::lpuart0::ctrl::{Idlecfg as IdleConfig, Ilt as IdleType, M as DataBits, Pt as Parity}; |
| 12 | use crate::pac::lpuart0::modir::{Txctsc as TxCtsConfig, Txctssrc as TxCtsSource}; | 12 | use crate::pac::lpuart0::modir::{Txctsc as TxCtsConfig, Txctssrc as TxCtsSource}; |
| 13 | use crate::pac::lpuart0::stat::Msbf as MsbFirst; | 13 | use crate::pac::lpuart0::stat::Msbf as MsbFirst; |
| 14 | use crate::{AnyPin, interrupt, pac}; | 14 | use crate::{interrupt, pac}; |
| 15 | 15 | ||
| 16 | pub mod buffered; | 16 | pub mod buffered; |
| 17 | 17 | ||
diff --git a/embassy-mcxa/src/pins.rs b/embassy-mcxa/src/pins.rs deleted file mode 100644 index 9adbe64c8..000000000 --- a/embassy-mcxa/src/pins.rs +++ /dev/null | |||
| @@ -1,33 +0,0 @@ | |||
| 1 | //! Pin configuration helpers (separate from peripheral drivers). | ||
| 2 | use crate::pac; | ||
| 3 | |||
| 4 | /// Configure pins for ADC usage. | ||
| 5 | /// | ||
| 6 | /// # Safety | ||
| 7 | /// | ||
| 8 | /// Must be called after PORT clocks are enabled. | ||
| 9 | pub unsafe fn configure_adc_pins() { | ||
| 10 | // P1_10 = ADC1_A8 | ||
| 11 | let port1 = &*pac::Port1::ptr(); | ||
| 12 | port1.pcr10().write(|w| { | ||
| 13 | w.ps() | ||
| 14 | .ps0() | ||
| 15 | .pe() | ||
| 16 | .pe0() | ||
| 17 | .sre() | ||
| 18 | .sre0() | ||
| 19 | .ode() | ||
| 20 | .ode0() | ||
| 21 | .dse() | ||
| 22 | .dse0() | ||
| 23 | .mux() | ||
| 24 | .mux0() | ||
| 25 | .ibe() | ||
| 26 | .ibe0() | ||
| 27 | .inv() | ||
| 28 | .inv0() | ||
| 29 | .lk() | ||
| 30 | .lk0() | ||
| 31 | }); | ||
| 32 | core::arch::asm!("dsb sy; isb sy"); | ||
| 33 | } | ||
diff --git a/embassy-mcxa/src/reset_reason.rs b/embassy-mcxa/src/reset_reason.rs new file mode 100644 index 000000000..f9a9ce096 --- /dev/null +++ b/embassy-mcxa/src/reset_reason.rs | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | //! Reset reason | ||
| 2 | //! | ||
| 3 | //! MCXA families keep the most recent reset reason in the SRS | ||
| 4 | //! register of the CMC block. This lets users understand why the MCU | ||
| 5 | //! has reset and take appropriate corrective actions if required. | ||
| 6 | |||
| 7 | /// Reads the most recent reset reason from the Core Mode Controller | ||
| 8 | /// (CMC). | ||
| 9 | pub fn reset_reason() -> ResetReason { | ||
| 10 | let regs = unsafe { &*crate::pac::Cmc::steal() }; | ||
| 11 | |||
| 12 | let srs = regs.srs().read(); | ||
| 13 | |||
| 14 | if srs.wakeup().is_enabled() { | ||
| 15 | ResetReason::WakeUp | ||
| 16 | } else if srs.por().bit_is_set() { | ||
| 17 | ResetReason::Por | ||
| 18 | } else if srs.vd().bit_is_set() { | ||
| 19 | ResetReason::VoltageDetect | ||
| 20 | } else if srs.warm().bit_is_set() { | ||
| 21 | ResetReason::Warm | ||
| 22 | } else if srs.fatal().bit_is_set() { | ||
| 23 | ResetReason::Fatal | ||
| 24 | } else if srs.pin().bit_is_set() { | ||
| 25 | ResetReason::Pin | ||
| 26 | } else if srs.dap().bit_is_set() { | ||
| 27 | ResetReason::Dap | ||
| 28 | } else if srs.rstack().bit_is_set() { | ||
| 29 | ResetReason::ResetAckTimeout | ||
| 30 | } else if srs.lpack().bit_is_set() { | ||
| 31 | ResetReason::LowPowerAckTimeout | ||
| 32 | } else if srs.scg().bit_is_set() { | ||
| 33 | ResetReason::SystemClockGeneration | ||
| 34 | } else if srs.wwdt0().bit_is_set() { | ||
| 35 | ResetReason::Wwdt0 | ||
| 36 | } else if srs.sw().bit_is_set() { | ||
| 37 | ResetReason::Software | ||
| 38 | } else if srs.lockup().bit_is_set() { | ||
| 39 | ResetReason::Lockup | ||
| 40 | } else if srs.cdog0().bit_is_set() { | ||
| 41 | ResetReason::Cdog0 | ||
| 42 | } else if srs.cdog1().bit_is_set() { | ||
| 43 | ResetReason::Cdog1 | ||
| 44 | } else if srs.jtag().bit_is_set() { | ||
| 45 | ResetReason::Jtag | ||
| 46 | } else { | ||
| 47 | ResetReason::Tamper | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | /// Indicates the type and source of the most recent reset. | ||
| 52 | #[derive(Clone, Copy, Debug)] | ||
| 53 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 54 | #[non_exhaustive] | ||
| 55 | pub enum ResetReason { | ||
| 56 | /// Tamper reset. | ||
| 57 | Tamper, | ||
| 58 | |||
| 59 | /// JTAG System Reset request. | ||
| 60 | Jtag, | ||
| 61 | |||
| 62 | /// Code Watchdog 0 reset. | ||
| 63 | Cdog0, | ||
| 64 | |||
| 65 | /// Code Watchdog 1 reset. | ||
| 66 | Cdog1, | ||
| 67 | |||
| 68 | /// Lockup reset. | ||
| 69 | Lockup, | ||
| 70 | |||
| 71 | /// Software reset. | ||
| 72 | Software, | ||
| 73 | |||
| 74 | /// Windowed Watchdog 0 reset. | ||
| 75 | Wwdt0, | ||
| 76 | |||
| 77 | /// System clock generation reset. | ||
| 78 | SystemClockGeneration, | ||
| 79 | |||
| 80 | /// Low Power Acknowledge Timeout reset. | ||
| 81 | LowPowerAckTimeout, | ||
| 82 | |||
| 83 | /// Reset Timeout. | ||
| 84 | ResetAckTimeout, | ||
| 85 | |||
| 86 | /// Debug Access Port reset. | ||
| 87 | Dap, | ||
| 88 | |||
| 89 | /// External assertion of RESET_b pin. | ||
| 90 | Pin, | ||
| 91 | |||
| 92 | /// Fatal reset. | ||
| 93 | Fatal, | ||
| 94 | |||
| 95 | /// Warm reset. | ||
| 96 | Warm, | ||
| 97 | |||
| 98 | /// Voltage detect reset. | ||
| 99 | VoltageDetect, | ||
| 100 | |||
| 101 | /// Power-on reset. | ||
| 102 | Por, | ||
| 103 | |||
| 104 | /// Wake-up reset. | ||
| 105 | WakeUp, | ||
| 106 | } | ||
diff --git a/embassy-mcxa/src/rtc.rs b/embassy-mcxa/src/rtc.rs index f975d9c9f..c5474d34a 100644 --- a/embassy-mcxa/src/rtc.rs +++ b/embassy-mcxa/src/rtc.rs | |||
| @@ -9,8 +9,6 @@ use crate::interrupt::typelevel::{Handler, Interrupt}; | |||
| 9 | use crate::pac; | 9 | use crate::pac; |
| 10 | use crate::pac::rtc0::cr::Um; | 10 | use crate::pac::rtc0::cr::Um; |
| 11 | 11 | ||
| 12 | type Regs = pac::rtc0::RegisterBlock; | ||
| 13 | |||
| 14 | /// Global wait cell for alarm notifications | 12 | /// Global wait cell for alarm notifications |
| 15 | static WAKER: WaitCell = WaitCell::new(); | 13 | static WAKER: WaitCell = WaitCell::new(); |
| 16 | 14 | ||
| @@ -22,7 +20,7 @@ pub struct InterruptHandler<I: Instance> { | |||
| 22 | /// Trait for RTC peripheral instances | 20 | /// Trait for RTC peripheral instances |
| 23 | pub trait Instance: PeripheralType { | 21 | pub trait Instance: PeripheralType { |
| 24 | type Interrupt: Interrupt; | 22 | type Interrupt: Interrupt; |
| 25 | fn ptr() -> *const Regs; | 23 | fn ptr() -> &'static pac::rtc0::RegisterBlock; |
| 26 | } | 24 | } |
| 27 | 25 | ||
| 28 | /// Token for RTC0 | 26 | /// Token for RTC0 |
| @@ -30,8 +28,8 @@ pub type Rtc0 = crate::peripherals::RTC0; | |||
| 30 | impl Instance for crate::peripherals::RTC0 { | 28 | impl Instance for crate::peripherals::RTC0 { |
| 31 | type Interrupt = crate::interrupt::typelevel::RTC; | 29 | type Interrupt = crate::interrupt::typelevel::RTC; |
| 32 | #[inline(always)] | 30 | #[inline(always)] |
| 33 | fn ptr() -> *const Regs { | 31 | fn ptr() -> &'static pac::rtc0::RegisterBlock { |
| 34 | pac::Rtc0::ptr() | 32 | unsafe { &*pac::Rtc0::ptr() } |
| 35 | } | 33 | } |
| 36 | } | 34 | } |
| 37 | 35 | ||
| @@ -204,18 +202,19 @@ pub fn get_default_config() -> RtcConfig { | |||
| 204 | } | 202 | } |
| 205 | } | 203 | } |
| 206 | /// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy) | 204 | /// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy) |
| 207 | pub struct Rtc<'a, I: Instance> { | 205 | pub struct Rtc<'a> { |
| 208 | _inst: core::marker::PhantomData<&'a mut I>, | 206 | _inst: core::marker::PhantomData<&'a mut ()>, |
| 207 | info: &'static pac::rtc0::RegisterBlock, | ||
| 209 | } | 208 | } |
| 210 | 209 | ||
| 211 | impl<'a, I: Instance> Rtc<'a, I> { | 210 | impl<'a> Rtc<'a> { |
| 212 | /// Create a new instance of the real time clock. | 211 | /// Create a new instance of the real time clock. |
| 213 | pub fn new( | 212 | pub fn new<I: Instance>( |
| 214 | _inst: Peri<'a, I>, | 213 | _inst: Peri<'a, I>, |
| 215 | _irq: impl crate::interrupt::typelevel::Binding<I::Interrupt, InterruptHandler<I>> + 'a, | 214 | _irq: impl crate::interrupt::typelevel::Binding<I::Interrupt, InterruptHandler<I>> + 'a, |
| 216 | config: RtcConfig, | 215 | config: RtcConfig, |
| 217 | ) -> Self { | 216 | ) -> Self { |
| 218 | let rtc = unsafe { &*I::ptr() }; | 217 | let info = I::ptr(); |
| 219 | 218 | ||
| 220 | // The RTC is NOT gated by the MRCC, but we DO need to make sure the 16k clock | 219 | // The RTC is NOT gated by the MRCC, but we DO need to make sure the 16k clock |
| 221 | // on the vsys domain is active | 220 | // on the vsys domain is active |
| @@ -227,13 +226,13 @@ impl<'a, I: Instance> Rtc<'a, I> { | |||
| 227 | } | 226 | } |
| 228 | 227 | ||
| 229 | // RTC reset | 228 | // RTC reset |
| 230 | rtc.cr().modify(|_, w| w.swr().set_bit()); | 229 | info.cr().modify(|_, w| w.swr().set_bit()); |
| 231 | rtc.cr().modify(|_, w| w.swr().clear_bit()); | 230 | info.cr().modify(|_, w| w.swr().clear_bit()); |
| 232 | rtc.tsr().write(|w| unsafe { w.bits(1) }); | 231 | info.tsr().write(|w| unsafe { w.bits(1) }); |
| 233 | 232 | ||
| 234 | rtc.cr().modify(|_, w| w.um().variant(config.update_mode)); | 233 | info.cr().modify(|_, w| w.um().variant(config.update_mode)); |
| 235 | 234 | ||
| 236 | rtc.tcr().modify(|_, w| unsafe { | 235 | info.tcr().modify(|_, w| unsafe { |
| 237 | w.cir() | 236 | w.cir() |
| 238 | .bits(config.compensation_interval) | 237 | .bits(config.compensation_interval) |
| 239 | .tcr() | 238 | .tcr() |
| @@ -246,6 +245,7 @@ impl<'a, I: Instance> Rtc<'a, I> { | |||
| 246 | 245 | ||
| 247 | Self { | 246 | Self { |
| 248 | _inst: core::marker::PhantomData, | 247 | _inst: core::marker::PhantomData, |
| 248 | info, | ||
| 249 | } | 249 | } |
| 250 | } | 250 | } |
| 251 | 251 | ||
| @@ -259,9 +259,8 @@ impl<'a, I: Instance> Rtc<'a, I> { | |||
| 259 | /// | 259 | /// |
| 260 | /// The datetime is converted to Unix timestamp and written to the time seconds register. | 260 | /// The datetime is converted to Unix timestamp and written to the time seconds register. |
| 261 | pub fn set_datetime(&self, datetime: RtcDateTime) { | 261 | pub fn set_datetime(&self, datetime: RtcDateTime) { |
| 262 | let rtc = unsafe { &*I::ptr() }; | ||
| 263 | let seconds = convert_datetime_to_seconds(&datetime); | 262 | let seconds = convert_datetime_to_seconds(&datetime); |
| 264 | rtc.tsr().write(|w| unsafe { w.bits(seconds) }); | 263 | self.info.tsr().write(|w| unsafe { w.bits(seconds) }); |
| 265 | } | 264 | } |
| 266 | 265 | ||
| 267 | /// Get the current date and time | 266 | /// Get the current date and time |
| @@ -274,8 +273,7 @@ impl<'a, I: Instance> Rtc<'a, I> { | |||
| 274 | /// | 273 | /// |
| 275 | /// Reads the current Unix timestamp from the time seconds register and converts it. | 274 | /// Reads the current Unix timestamp from the time seconds register and converts it. |
| 276 | pub fn get_datetime(&self) -> RtcDateTime { | 275 | pub fn get_datetime(&self) -> RtcDateTime { |
| 277 | let rtc = unsafe { &*I::ptr() }; | 276 | let seconds = self.info.tsr().read().bits(); |
| 278 | let seconds = rtc.tsr().read().bits(); | ||
| 279 | convert_seconds_to_datetime(seconds) | 277 | convert_seconds_to_datetime(seconds) |
| 280 | } | 278 | } |
| 281 | 279 | ||
| @@ -295,19 +293,18 @@ impl<'a, I: Instance> Rtc<'a, I> { | |||
| 295 | /// - Uses timeouts to prevent infinite loops | 293 | /// - Uses timeouts to prevent infinite loops |
| 296 | /// - Enables the alarm interrupt after setting | 294 | /// - Enables the alarm interrupt after setting |
| 297 | pub fn set_alarm(&self, alarm: RtcDateTime) { | 295 | pub fn set_alarm(&self, alarm: RtcDateTime) { |
| 298 | let rtc = unsafe { &*I::ptr() }; | ||
| 299 | let seconds = convert_datetime_to_seconds(&alarm); | 296 | let seconds = convert_datetime_to_seconds(&alarm); |
| 300 | 297 | ||
| 301 | rtc.tar().write(|w| unsafe { w.bits(0) }); | 298 | self.info.tar().write(|w| unsafe { w.bits(0) }); |
| 302 | let mut timeout = 10000; | 299 | let mut timeout = 10000; |
| 303 | while rtc.tar().read().bits() != 0 && timeout > 0 { | 300 | while self.info.tar().read().bits() != 0 && timeout > 0 { |
| 304 | timeout -= 1; | 301 | timeout -= 1; |
| 305 | } | 302 | } |
| 306 | 303 | ||
| 307 | rtc.tar().write(|w| unsafe { w.bits(seconds) }); | 304 | self.info.tar().write(|w| unsafe { w.bits(seconds) }); |
| 308 | 305 | ||
| 309 | let mut timeout = 10000; | 306 | let mut timeout = 10000; |
| 310 | while rtc.tar().read().bits() != seconds && timeout > 0 { | 307 | while self.info.tar().read().bits() != seconds && timeout > 0 { |
| 311 | timeout -= 1; | 308 | timeout -= 1; |
| 312 | } | 309 | } |
| 313 | 310 | ||
| @@ -324,8 +321,7 @@ impl<'a, I: Instance> Rtc<'a, I> { | |||
| 324 | /// | 321 | /// |
| 325 | /// Reads the alarm timestamp from the time alarm register and converts it. | 322 | /// Reads the alarm timestamp from the time alarm register and converts it. |
| 326 | pub fn get_alarm(&self) -> RtcDateTime { | 323 | pub fn get_alarm(&self) -> RtcDateTime { |
| 327 | let rtc = unsafe { &*I::ptr() }; | 324 | let alarm_seconds = self.info.tar().read().bits(); |
| 328 | let alarm_seconds = rtc.tar().read().bits(); | ||
| 329 | convert_seconds_to_datetime(alarm_seconds) | 325 | convert_seconds_to_datetime(alarm_seconds) |
| 330 | } | 326 | } |
| 331 | 327 | ||
| @@ -335,8 +331,7 @@ impl<'a, I: Instance> Rtc<'a, I> { | |||
| 335 | /// | 331 | /// |
| 336 | /// Sets the Time Counter Enable (TCE) bit in the status register. | 332 | /// Sets the Time Counter Enable (TCE) bit in the status register. |
| 337 | pub fn start(&self) { | 333 | pub fn start(&self) { |
| 338 | let rtc = unsafe { &*I::ptr() }; | 334 | self.info.sr().modify(|_, w| w.tce().set_bit()); |
| 339 | rtc.sr().modify(|_, w| w.tce().set_bit()); | ||
| 340 | } | 335 | } |
| 341 | 336 | ||
| 342 | /// Stop the RTC time counter | 337 | /// Stop the RTC time counter |
| @@ -345,8 +340,7 @@ impl<'a, I: Instance> Rtc<'a, I> { | |||
| 345 | /// | 340 | /// |
| 346 | /// Clears the Time Counter Enable (TCE) bit in the status register. | 341 | /// Clears the Time Counter Enable (TCE) bit in the status register. |
| 347 | pub fn stop(&self) { | 342 | pub fn stop(&self) { |
| 348 | let rtc = unsafe { &*I::ptr() }; | 343 | self.info.sr().modify(|_, w| w.tce().clear_bit()); |
| 349 | rtc.sr().modify(|_, w| w.tce().clear_bit()); | ||
| 350 | } | 344 | } |
| 351 | 345 | ||
| 352 | /// Enable specific RTC interrupts | 346 | /// Enable specific RTC interrupts |
| @@ -364,19 +358,17 @@ impl<'a, I: Instance> Rtc<'a, I> { | |||
| 364 | /// - Alarm Interrupt | 358 | /// - Alarm Interrupt |
| 365 | /// - Seconds Interrupt | 359 | /// - Seconds Interrupt |
| 366 | pub fn set_interrupt(&self, mask: u32) { | 360 | pub fn set_interrupt(&self, mask: u32) { |
| 367 | let rtc = unsafe { &*I::ptr() }; | ||
| 368 | |||
| 369 | if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 { | 361 | if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 { |
| 370 | rtc.ier().modify(|_, w| w.tiie().tiie_1()); | 362 | self.info.ier().modify(|_, w| w.tiie().tiie_1()); |
| 371 | } | 363 | } |
| 372 | if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 { | 364 | if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 { |
| 373 | rtc.ier().modify(|_, w| w.toie().toie_1()); | 365 | self.info.ier().modify(|_, w| w.toie().toie_1()); |
| 374 | } | 366 | } |
| 375 | if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 { | 367 | if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 { |
| 376 | rtc.ier().modify(|_, w| w.taie().taie_1()); | 368 | self.info.ier().modify(|_, w| w.taie().taie_1()); |
| 377 | } | 369 | } |
| 378 | if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 { | 370 | if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 { |
| 379 | rtc.ier().modify(|_, w| w.tsie().tsie_1()); | 371 | self.info.ier().modify(|_, w| w.tsie().tsie_1()); |
| 380 | } | 372 | } |
| 381 | } | 373 | } |
| 382 | 374 | ||
| @@ -390,19 +382,17 @@ impl<'a, I: Instance> Rtc<'a, I> { | |||
| 390 | /// | 382 | /// |
| 391 | /// This function disables the specified interrupt types. | 383 | /// This function disables the specified interrupt types. |
| 392 | pub fn disable_interrupt(&self, mask: u32) { | 384 | pub fn disable_interrupt(&self, mask: u32) { |
| 393 | let rtc = unsafe { &*I::ptr() }; | ||
| 394 | |||
| 395 | if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 { | 385 | if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 { |
| 396 | rtc.ier().modify(|_, w| w.tiie().tiie_0()); | 386 | self.info.ier().modify(|_, w| w.tiie().tiie_0()); |
| 397 | } | 387 | } |
| 398 | if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 { | 388 | if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 { |
| 399 | rtc.ier().modify(|_, w| w.toie().toie_0()); | 389 | self.info.ier().modify(|_, w| w.toie().toie_0()); |
| 400 | } | 390 | } |
| 401 | if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 { | 391 | if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 { |
| 402 | rtc.ier().modify(|_, w| w.taie().taie_0()); | 392 | self.info.ier().modify(|_, w| w.taie().taie_0()); |
| 403 | } | 393 | } |
| 404 | if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 { | 394 | if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 { |
| 405 | rtc.ier().modify(|_, w| w.tsie().tsie_0()); | 395 | self.info.ier().modify(|_, w| w.tsie().tsie_0()); |
| 406 | } | 396 | } |
| 407 | } | 397 | } |
| 408 | 398 | ||
| @@ -412,8 +402,7 @@ impl<'a, I: Instance> Rtc<'a, I> { | |||
| 412 | /// | 402 | /// |
| 413 | /// This function clears the Time Alarm Interrupt Enable bit. | 403 | /// This function clears the Time Alarm Interrupt Enable bit. |
| 414 | pub fn clear_alarm_flag(&self) { | 404 | pub fn clear_alarm_flag(&self) { |
| 415 | let rtc = unsafe { &*I::ptr() }; | 405 | self.info.ier().modify(|_, w| w.taie().clear_bit()); |
| 416 | rtc.ier().modify(|_, w| w.taie().clear_bit()); | ||
| 417 | } | 406 | } |
| 418 | 407 | ||
| 419 | /// Wait for an RTC alarm to trigger. | 408 | /// Wait for an RTC alarm to trigger. |
| @@ -421,6 +410,7 @@ impl<'a, I: Instance> Rtc<'a, I> { | |||
| 421 | /// # Arguments | 410 | /// # Arguments |
| 422 | /// | 411 | /// |
| 423 | /// * `alarm` - The date and time when the alarm should trigger | 412 | /// * `alarm` - The date and time when the alarm should trigger |
| 413 | /// | ||
| 424 | /// This function will wait until the RTC alarm is triggered. | 414 | /// This function will wait until the RTC alarm is triggered. |
| 425 | /// If no alarm is scheduled, it will wait indefinitely until one is scheduled and triggered. | 415 | /// If no alarm is scheduled, it will wait indefinitely until one is scheduled and triggered. |
| 426 | pub async fn wait_for_alarm(&mut self, alarm: RtcDateTime) { | 416 | pub async fn wait_for_alarm(&mut self, alarm: RtcDateTime) { |
diff --git a/embassy-nrf/src/cracen.rs b/embassy-nrf/src/cracen.rs index 47ef1cd87..6381701c0 100644 --- a/embassy-nrf/src/cracen.rs +++ b/embassy-nrf/src/cracen.rs | |||
| @@ -18,10 +18,7 @@ pub struct Cracen<'d, M: Mode> { | |||
| 18 | impl<'d> Cracen<'d, Blocking> { | 18 | impl<'d> Cracen<'d, Blocking> { |
| 19 | /// Create a new CRACEN driver. | 19 | /// Create a new CRACEN driver. |
| 20 | pub fn new_blocking(_peri: Peri<'d, peripherals::CRACEN>) -> Self { | 20 | pub fn new_blocking(_peri: Peri<'d, peripherals::CRACEN>) -> Self { |
| 21 | let me = Self { _peri, _p: PhantomData }; | 21 | Self { _peri, _p: PhantomData } |
| 22 | |||
| 23 | me.stop(); | ||
| 24 | me | ||
| 25 | } | 22 | } |
| 26 | } | 23 | } |
| 27 | 24 | ||
| @@ -48,7 +45,14 @@ impl<'d, M: Mode> Cracen<'d, M> { | |||
| 48 | while r.rngcontrol().status().read().state() == pac::cracencore::vals::State::STARTUP {} | 45 | while r.rngcontrol().status().read().state() == pac::cracencore::vals::State::STARTUP {} |
| 49 | } | 46 | } |
| 50 | 47 | ||
| 51 | fn stop(&self) { | 48 | fn stop_rng(&self) { |
| 49 | let r = Self::core(); | ||
| 50 | r.rngcontrol().control().write(|w| { | ||
| 51 | w.set_enable(false); | ||
| 52 | }); | ||
| 53 | |||
| 54 | while r.rngcontrol().status().read().state() != pac::cracencore::vals::State::RESET {} | ||
| 55 | |||
| 52 | let r = Self::regs(); | 56 | let r = Self::regs(); |
| 53 | r.enable().write(|w| { | 57 | r.enable().write(|w| { |
| 54 | w.set_cryptomaster(false); | 58 | w.set_cryptomaster(false); |
| @@ -69,7 +73,7 @@ impl<'d, M: Mode> Cracen<'d, M> { | |||
| 69 | chunk[..to_copy].copy_from_slice(&word[..to_copy]); | 73 | chunk[..to_copy].copy_from_slice(&word[..to_copy]); |
| 70 | } | 74 | } |
| 71 | 75 | ||
| 72 | self.stop(); | 76 | self.stop_rng(); |
| 73 | } | 77 | } |
| 74 | 78 | ||
| 75 | /// Generate a random u32 | 79 | /// Generate a random u32 |
| @@ -90,19 +94,7 @@ impl<'d, M: Mode> Cracen<'d, M> { | |||
| 90 | 94 | ||
| 91 | impl<'d, M: Mode> Drop for Cracen<'d, M> { | 95 | impl<'d, M: Mode> Drop for Cracen<'d, M> { |
| 92 | fn drop(&mut self) { | 96 | fn drop(&mut self) { |
| 93 | let r = Self::core(); | 97 | // nothing to do here, since we stop+disable rng for each operation. |
| 94 | r.rngcontrol().control().write(|w| { | ||
| 95 | w.set_enable(false); | ||
| 96 | }); | ||
| 97 | |||
| 98 | while r.rngcontrol().status().read().state() != pac::cracencore::vals::State::RESET {} | ||
| 99 | |||
| 100 | let r = Self::regs(); | ||
| 101 | r.enable().write(|w| { | ||
| 102 | w.set_cryptomaster(false); | ||
| 103 | w.set_rng(false); | ||
| 104 | w.set_pkeikg(false); | ||
| 105 | }); | ||
| 106 | } | 98 | } |
| 107 | } | 99 | } |
| 108 | 100 | ||
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 449152485..38f22b1c3 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md | |||
| @@ -90,6 +90,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 90 | - change: add error messages to can timing calculations ([#4961](https://github.com/embassy-rs/embassy/pull/4961)) | 90 | - change: add error messages to can timing calculations ([#4961](https://github.com/embassy-rs/embassy/pull/4961)) |
| 91 | - feat: stm32/spi bidirectional mode | 91 | - feat: stm32/spi bidirectional mode |
| 92 | - fix: stm32/i2c v2: add stop flag on stop received | 92 | - fix: stm32/i2c v2: add stop flag on stop received |
| 93 | - stm32: Add blocking_listen for blocking I2C driver | ||
| 93 | - fix: stm32l47*/stm32l48* adc analog pin setup | 94 | - fix: stm32l47*/stm32l48* adc analog pin setup |
| 94 | - fix: keep stm32/sai: make NODIV independent of MCKDIV | 95 | - fix: keep stm32/sai: make NODIV independent of MCKDIV |
| 95 | 96 | ||
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index e10409112..7989fc5d7 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -200,11 +200,11 @@ aligned = "0.4.1" | |||
| 200 | heapless = "0.9.1" | 200 | heapless = "0.9.1" |
| 201 | 201 | ||
| 202 | #stm32-metapac = { version = "18" } | 202 | #stm32-metapac = { version = "18" } |
| 203 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f61ed017ef12ec84ff04c49e3147694bda3b29cb" } | 203 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-497fb3042b49b765d8974aac87b8ab4fa3566d74" } |
| 204 | 204 | ||
| 205 | [build-dependencies] | 205 | [build-dependencies] |
| 206 | #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} | 206 | #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} |
| 207 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f61ed017ef12ec84ff04c49e3147694bda3b29cb", default-features = false, features = ["metadata"] } | 207 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-497fb3042b49b765d8974aac87b8ab4fa3566d74", default-features = false, features = ["metadata"] } |
| 208 | 208 | ||
| 209 | proc-macro2 = "1.0.36" | 209 | proc-macro2 = "1.0.36" |
| 210 | quote = "1.0.15" | 210 | quote = "1.0.15" |
diff --git a/embassy-stm32/src/dma/gpdma/mod.rs b/embassy-stm32/src/dma/gpdma/mod.rs index 51c107cb4..afb18ec1a 100644 --- a/embassy-stm32/src/dma/gpdma/mod.rs +++ b/embassy-stm32/src/dma/gpdma/mod.rs | |||
| @@ -137,7 +137,6 @@ pub(crate) unsafe fn init(cs: critical_section::CriticalSection, irq_priority: c | |||
| 137 | 137 | ||
| 138 | impl AnyChannel { | 138 | impl AnyChannel { |
| 139 | /// Safety: Must be called with a matching set of parameters for a valid dma channel | 139 | /// Safety: Must be called with a matching set of parameters for a valid dma channel |
| 140 | #[cfg(not(stm32n6))] | ||
| 141 | pub(crate) unsafe fn on_irq(&self) { | 140 | pub(crate) unsafe fn on_irq(&self) { |
| 142 | let info = self.info(); | 141 | let info = self.info(); |
| 143 | #[cfg(feature = "_dual-core")] | 142 | #[cfg(feature = "_dual-core")] |
diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 90feab167..05d9c2e51 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs | |||
| @@ -48,11 +48,9 @@ pub type Request = u8; | |||
| 48 | pub type Request = (); | 48 | pub type Request = (); |
| 49 | 49 | ||
| 50 | pub(crate) trait SealedChannel: StoppablePeripheral { | 50 | pub(crate) trait SealedChannel: StoppablePeripheral { |
| 51 | #[cfg(not(stm32n6))] | ||
| 52 | fn id(&self) -> u8; | 51 | fn id(&self) -> u8; |
| 53 | } | 52 | } |
| 54 | 53 | ||
| 55 | #[cfg(not(stm32n6))] | ||
| 56 | pub(crate) trait ChannelInterrupt { | 54 | pub(crate) trait ChannelInterrupt { |
| 57 | #[cfg_attr(not(feature = "rt"), allow(unused))] | 55 | #[cfg_attr(not(feature = "rt"), allow(unused))] |
| 58 | unsafe fn on_irq(); | 56 | unsafe fn on_irq(); |
| @@ -62,7 +60,6 @@ pub(crate) trait ChannelInterrupt { | |||
| 62 | #[allow(private_bounds)] | 60 | #[allow(private_bounds)] |
| 63 | pub trait Channel: SealedChannel + PeripheralType + Into<AnyChannel> + 'static {} | 61 | pub trait Channel: SealedChannel + PeripheralType + Into<AnyChannel> + 'static {} |
| 64 | 62 | ||
| 65 | #[cfg(not(stm32n6))] | ||
| 66 | macro_rules! dma_channel_impl { | 63 | macro_rules! dma_channel_impl { |
| 67 | ($channel_peri:ident, $index:expr, $stop_mode:ident) => { | 64 | ($channel_peri:ident, $index:expr, $stop_mode:ident) => { |
| 68 | impl crate::rcc::StoppablePeripheral for crate::peripherals::$channel_peri { | 65 | impl crate::rcc::StoppablePeripheral for crate::peripherals::$channel_peri { |
| @@ -125,7 +122,6 @@ impl StoppablePeripheral for AnyChannel { | |||
| 125 | } | 122 | } |
| 126 | 123 | ||
| 127 | impl SealedChannel for AnyChannel { | 124 | impl SealedChannel for AnyChannel { |
| 128 | #[cfg(not(stm32n6))] | ||
| 129 | fn id(&self) -> u8 { | 125 | fn id(&self) -> u8 { |
| 130 | self.id | 126 | self.id |
| 131 | } | 127 | } |
diff --git a/embassy-stm32/src/dma/util.rs b/embassy-stm32/src/dma/util.rs index 3245887c1..304268963 100644 --- a/embassy-stm32/src/dma/util.rs +++ b/embassy-stm32/src/dma/util.rs | |||
| @@ -20,6 +20,16 @@ impl<'d> ChannelAndRequest<'d> { | |||
| 20 | Transfer::new_read(self.channel.reborrow(), self.request, peri_addr, buf, options) | 20 | Transfer::new_read(self.channel.reborrow(), self.request, peri_addr, buf, options) |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | #[allow(dead_code)] | ||
| 24 | pub unsafe fn read_unchecked<'a, W: Word>( | ||
| 25 | &'a self, | ||
| 26 | peri_addr: *mut W, | ||
| 27 | buf: &'a mut [W], | ||
| 28 | options: TransferOptions, | ||
| 29 | ) -> Transfer<'a> { | ||
| 30 | Transfer::new_read(self.channel.clone_unchecked(), self.request, peri_addr, buf, options) | ||
| 31 | } | ||
| 32 | |||
| 23 | pub unsafe fn read_raw<'a, MW: Word, PW: Word>( | 33 | pub unsafe fn read_raw<'a, MW: Word, PW: Word>( |
| 24 | &'a mut self, | 34 | &'a mut self, |
| 25 | peri_addr: *mut PW, | 35 | peri_addr: *mut PW, |
| @@ -29,6 +39,16 @@ impl<'d> ChannelAndRequest<'d> { | |||
| 29 | Transfer::new_read_raw(self.channel.reborrow(), self.request, peri_addr, buf, options) | 39 | Transfer::new_read_raw(self.channel.reborrow(), self.request, peri_addr, buf, options) |
| 30 | } | 40 | } |
| 31 | 41 | ||
| 42 | #[allow(dead_code)] | ||
| 43 | pub unsafe fn read_raw_unchecked<'a, MW: Word, PW: Word>( | ||
| 44 | &'a self, | ||
| 45 | peri_addr: *mut PW, | ||
| 46 | buf: *mut [MW], | ||
| 47 | options: TransferOptions, | ||
| 48 | ) -> Transfer<'a> { | ||
| 49 | Transfer::new_read_raw(self.channel.clone_unchecked(), self.request, peri_addr, buf, options) | ||
| 50 | } | ||
| 51 | |||
| 32 | pub unsafe fn write<'a, W: Word>( | 52 | pub unsafe fn write<'a, W: Word>( |
| 33 | &'a mut self, | 53 | &'a mut self, |
| 34 | buf: &'a [W], | 54 | buf: &'a [W], |
| @@ -38,6 +58,16 @@ impl<'d> ChannelAndRequest<'d> { | |||
| 38 | Transfer::new_write(self.channel.reborrow(), self.request, buf, peri_addr, options) | 58 | Transfer::new_write(self.channel.reborrow(), self.request, buf, peri_addr, options) |
| 39 | } | 59 | } |
| 40 | 60 | ||
| 61 | #[allow(dead_code)] | ||
| 62 | pub unsafe fn write_unchecked<'a, W: Word>( | ||
| 63 | &'a self, | ||
| 64 | buf: &'a [W], | ||
| 65 | peri_addr: *mut W, | ||
| 66 | options: TransferOptions, | ||
| 67 | ) -> Transfer<'a> { | ||
| 68 | Transfer::new_write(self.channel.clone_unchecked(), self.request, buf, peri_addr, options) | ||
| 69 | } | ||
| 70 | |||
| 41 | pub unsafe fn write_raw<'a, MW: Word, PW: Word>( | 71 | pub unsafe fn write_raw<'a, MW: Word, PW: Word>( |
| 42 | &'a mut self, | 72 | &'a mut self, |
| 43 | buf: *const [MW], | 73 | buf: *const [MW], |
| @@ -48,6 +78,16 @@ impl<'d> ChannelAndRequest<'d> { | |||
| 48 | } | 78 | } |
| 49 | 79 | ||
| 50 | #[allow(dead_code)] | 80 | #[allow(dead_code)] |
| 81 | pub unsafe fn write_raw_unchecked<'a, MW: Word, PW: Word>( | ||
| 82 | &'a self, | ||
| 83 | buf: *const [MW], | ||
| 84 | peri_addr: *mut PW, | ||
| 85 | options: TransferOptions, | ||
| 86 | ) -> Transfer<'a> { | ||
| 87 | Transfer::new_write_raw(self.channel.clone_unchecked(), self.request, buf, peri_addr, options) | ||
| 88 | } | ||
| 89 | |||
| 90 | #[allow(dead_code)] | ||
| 51 | pub unsafe fn write_repeated<'a, W: Word>( | 91 | pub unsafe fn write_repeated<'a, W: Word>( |
| 52 | &'a mut self, | 92 | &'a mut self, |
| 53 | repeated: &'a W, | 93 | repeated: &'a W, |
| @@ -64,4 +104,22 @@ impl<'d> ChannelAndRequest<'d> { | |||
| 64 | options, | 104 | options, |
| 65 | ) | 105 | ) |
| 66 | } | 106 | } |
| 107 | |||
| 108 | #[allow(dead_code)] | ||
| 109 | pub unsafe fn write_repeated_unchecked<'a, W: Word>( | ||
| 110 | &'a self, | ||
| 111 | repeated: &'a W, | ||
| 112 | count: usize, | ||
| 113 | peri_addr: *mut W, | ||
| 114 | options: TransferOptions, | ||
| 115 | ) -> Transfer<'a> { | ||
| 116 | Transfer::new_write_repeated( | ||
| 117 | self.channel.clone_unchecked(), | ||
| 118 | self.request, | ||
| 119 | repeated, | ||
| 120 | count, | ||
| 121 | peri_addr, | ||
| 122 | options, | ||
| 123 | ) | ||
| 124 | } | ||
| 67 | } | 125 | } |
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 32ce83d40..fe7782a7c 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs | |||
| @@ -1680,43 +1680,50 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1680 | 1680 | ||
| 1681 | /// Listen for incoming I2C messages. | 1681 | /// Listen for incoming I2C messages. |
| 1682 | /// | 1682 | /// |
| 1683 | /// The listen method is an asynchronous method but it does not require DMA to be asynchronous. | 1683 | /// This method blocks until the slave address is matched by a master. |
| 1684 | pub async fn listen(&mut self) -> Result<SlaveCommand, Error> { | 1684 | pub fn blocking_listen(&mut self) -> Result<SlaveCommand, Error> { |
| 1685 | let _scoped_block_stop = self.info.rcc.block_stop(); | 1685 | let timeout = self.timeout(); |
| 1686 | let state = self.state; | 1686 | |
| 1687 | self.info.regs.cr1().modify(|reg| { | 1687 | self.info.regs.cr1().modify(|reg| { |
| 1688 | reg.set_addrie(true); | 1688 | reg.set_addrie(true); |
| 1689 | trace!("Enable ADDRIE"); | 1689 | trace!("Enable ADDRIE"); |
| 1690 | }); | 1690 | }); |
| 1691 | 1691 | ||
| 1692 | poll_fn(|cx| { | 1692 | loop { |
| 1693 | state.waker.register(cx.waker()); | ||
| 1694 | let isr = self.info.regs.isr().read(); | 1693 | let isr = self.info.regs.isr().read(); |
| 1695 | if !isr.addr() { | 1694 | if isr.addr() { |
| 1696 | Poll::Pending | 1695 | break; |
| 1697 | } else { | ||
| 1698 | trace!("ADDR triggered (address match)"); | ||
| 1699 | // we do not clear the address flag here as it will be cleared by the dma read/write | ||
| 1700 | // if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it | ||
| 1701 | match isr.dir() { | ||
| 1702 | i2c::vals::Dir::WRITE => { | ||
| 1703 | trace!("DIR: write"); | ||
| 1704 | Poll::Ready(Ok(SlaveCommand { | ||
| 1705 | kind: SlaveCommandKind::Write, | ||
| 1706 | address: self.determine_matched_address()?, | ||
| 1707 | })) | ||
| 1708 | } | ||
| 1709 | i2c::vals::Dir::READ => { | ||
| 1710 | trace!("DIR: read"); | ||
| 1711 | Poll::Ready(Ok(SlaveCommand { | ||
| 1712 | kind: SlaveCommandKind::Read, | ||
| 1713 | address: self.determine_matched_address()?, | ||
| 1714 | })) | ||
| 1715 | } | ||
| 1716 | } | ||
| 1717 | } | 1696 | } |
| 1718 | }) | 1697 | timeout.check()?; |
| 1719 | .await | 1698 | } |
| 1699 | |||
| 1700 | trace!("ADDR triggered (address match)"); | ||
| 1701 | |||
| 1702 | // we do not clear the address flag here as it will be cleared by the dma read/write | ||
| 1703 | // if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it | ||
| 1704 | self.slave_command() | ||
| 1705 | } | ||
| 1706 | |||
| 1707 | /// Determine the received slave command. | ||
| 1708 | fn slave_command(&self) -> Result<SlaveCommand, Error> { | ||
| 1709 | let isr = self.info.regs.isr().read(); | ||
| 1710 | |||
| 1711 | match isr.dir() { | ||
| 1712 | i2c::vals::Dir::WRITE => { | ||
| 1713 | trace!("DIR: write"); | ||
| 1714 | Ok(SlaveCommand { | ||
| 1715 | kind: SlaveCommandKind::Write, | ||
| 1716 | address: self.determine_matched_address()?, | ||
| 1717 | }) | ||
| 1718 | } | ||
| 1719 | i2c::vals::Dir::READ => { | ||
| 1720 | trace!("DIR: read"); | ||
| 1721 | Ok(SlaveCommand { | ||
| 1722 | kind: SlaveCommandKind::Read, | ||
| 1723 | address: self.determine_matched_address()?, | ||
| 1724 | }) | ||
| 1725 | } | ||
| 1726 | } | ||
| 1720 | } | 1727 | } |
| 1721 | 1728 | ||
| 1722 | /// Respond to a write command. | 1729 | /// Respond to a write command. |
| @@ -1735,6 +1742,32 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1735 | } | 1742 | } |
| 1736 | 1743 | ||
| 1737 | impl<'d> I2c<'d, Async, MultiMaster> { | 1744 | impl<'d> I2c<'d, Async, MultiMaster> { |
| 1745 | /// Listen for incoming I2C messages. | ||
| 1746 | /// | ||
| 1747 | /// The listen method is an asynchronous method but it does not require DMA to be asynchronous. | ||
| 1748 | pub async fn listen(&mut self) -> Result<SlaveCommand, Error> { | ||
| 1749 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 1750 | let state = self.state; | ||
| 1751 | self.info.regs.cr1().modify(|reg| { | ||
| 1752 | reg.set_addrie(true); | ||
| 1753 | trace!("Enable ADDRIE"); | ||
| 1754 | }); | ||
| 1755 | |||
| 1756 | poll_fn(|cx| { | ||
| 1757 | state.waker.register(cx.waker()); | ||
| 1758 | let isr = self.info.regs.isr().read(); | ||
| 1759 | if !isr.addr() { | ||
| 1760 | Poll::Pending | ||
| 1761 | } else { | ||
| 1762 | trace!("ADDR triggered (address match)"); | ||
| 1763 | // we do not clear the address flag here as it will be cleared by the dma read/write | ||
| 1764 | // if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it | ||
| 1765 | Poll::Ready(self.slave_command()) | ||
| 1766 | } | ||
| 1767 | }) | ||
| 1768 | .await | ||
| 1769 | } | ||
| 1770 | |||
| 1738 | /// Respond to a write command. | 1771 | /// Respond to a write command. |
| 1739 | /// | 1772 | /// |
| 1740 | /// Returns the total number of bytes received. | 1773 | /// Returns the total number of bytes received. |
diff --git a/embassy-stm32/src/rcc/n6.rs b/embassy-stm32/src/rcc/n6.rs index 866851bbd..178ec57d4 100644 --- a/embassy-stm32/src/rcc/n6.rs +++ b/embassy-stm32/src/rcc/n6.rs | |||
| @@ -1003,6 +1003,24 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 1003 | p.SCB.cpacr.modify(|w| w | (3 << 20) | (3 << 22)); | 1003 | p.SCB.cpacr.modify(|w| w | (3 << 20) | (3 << 22)); |
| 1004 | } | 1004 | } |
| 1005 | 1005 | ||
| 1006 | // TODO: ugly workaround for DMA accesses until RIF is properly implemented | ||
| 1007 | debug!("deactivating RIF"); | ||
| 1008 | const RISAF3_BASE_NS: *mut u32 = stm32_metapac::RNG.wrapping_byte_offset(0x8000) as _; // AHB3PERIPH_BASE_NS + 0x8000UL | ||
| 1009 | const RISAF3_REG0_CFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x40); | ||
| 1010 | const RISAF3_REG0_ENDR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x48); | ||
| 1011 | const RISAF3_REG0_CIDCFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x4C); | ||
| 1012 | const RISAF3_REG1_CFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x80); | ||
| 1013 | const RISAF3_REG1_ENDR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x88); | ||
| 1014 | const RISAF3_REG1_CIDCFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x8C); | ||
| 1015 | unsafe { | ||
| 1016 | *RISAF3_REG0_CIDCFGR = 0x000F000F; /* RW for everyone */ | ||
| 1017 | *RISAF3_REG0_ENDR = 0xFFFFFFFF; /* all-encompassing */ | ||
| 1018 | *RISAF3_REG0_CFGR = 0x00000101; /* enabled, secure, unprivileged for everyone */ | ||
| 1019 | *RISAF3_REG1_CIDCFGR = 0x00FF00FF; /* RW for everyone */ | ||
| 1020 | *RISAF3_REG1_ENDR = 0xFFFFFFFF; /* all-encompassing */ | ||
| 1021 | *RISAF3_REG1_CFGR = 0x00000001; /* enabled, non-secure, unprivileged*/ | ||
| 1022 | } | ||
| 1023 | |||
| 1006 | debug!("setting power supply config"); | 1024 | debug!("setting power supply config"); |
| 1007 | 1025 | ||
| 1008 | power_supply_config(config.supply_config); | 1026 | power_supply_config(config.supply_config); |
| @@ -1039,7 +1057,9 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 1039 | i2s_ckin: None, | 1057 | i2s_ckin: None, |
| 1040 | ic8: None, | 1058 | ic8: None, |
| 1041 | ic9: None, | 1059 | ic9: None, |
| 1060 | ic10: None, | ||
| 1042 | ic14: None, | 1061 | ic14: None, |
| 1062 | ic15: None, | ||
| 1043 | ic17: None, | 1063 | ic17: None, |
| 1044 | ic20: None, | 1064 | ic20: None, |
| 1045 | ); | 1065 | ); |
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 37ef7099f..12086cd3a 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs | |||
| @@ -4,16 +4,15 @@ | |||
| 4 | use core::default::Default; | 4 | use core::default::Default; |
| 5 | use core::future::poll_fn; | 5 | use core::future::poll_fn; |
| 6 | use core::marker::PhantomData; | 6 | use core::marker::PhantomData; |
| 7 | use core::ops::{Deref, DerefMut}; | 7 | use core::slice; |
| 8 | use core::task::Poll; | 8 | use core::task::Poll; |
| 9 | 9 | ||
| 10 | use embassy_hal_internal::drop::OnDrop; | ||
| 11 | use embassy_hal_internal::{Peri, PeripheralType}; | 10 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 12 | use embassy_sync::waitqueue::AtomicWaker; | 11 | use embassy_sync::waitqueue::AtomicWaker; |
| 13 | use sdio_host::common_cmd::{self, Resp, ResponseLen}; | 12 | use sdio_host::Cmd; |
| 14 | use sdio_host::emmc::{EMMC, ExtCSD}; | 13 | use sdio_host::common_cmd::{self, R1, R2, R3, Resp, ResponseLen, Rz}; |
| 15 | use sdio_host::sd::{BusWidth, CIC, CID, CSD, CardCapacity, CardStatus, CurrentState, OCR, RCA, SCR, SD, SDStatus}; | 14 | use sdio_host::sd::{BusWidth, CardStatus}; |
| 16 | use sdio_host::{Cmd, emmc_cmd, sd_cmd}; | 15 | use sdio_host::sd_cmd::{R6, R7}; |
| 17 | 16 | ||
| 18 | #[cfg(sdmmc_v1)] | 17 | #[cfg(sdmmc_v1)] |
| 19 | use crate::dma::ChannelAndRequest; | 18 | use crate::dma::ChannelAndRequest; |
| @@ -22,37 +21,27 @@ use crate::gpio::Pull; | |||
| 22 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; | 21 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; |
| 23 | use crate::interrupt::typelevel::Interrupt; | 22 | use crate::interrupt::typelevel::Interrupt; |
| 24 | use crate::pac::sdmmc::Sdmmc as RegBlock; | 23 | use crate::pac::sdmmc::Sdmmc as RegBlock; |
| 25 | use crate::rcc::{self, RccPeripheral}; | 24 | use crate::rcc::{self, RccInfo, RccPeripheral, SealedRccPeripheral}; |
| 25 | use crate::sdmmc::sd::Addressable; | ||
| 26 | use crate::time::Hertz; | 26 | use crate::time::Hertz; |
| 27 | use crate::{interrupt, peripherals}; | 27 | use crate::{interrupt, peripherals}; |
| 28 | 28 | ||
| 29 | /// Module for SD and EMMC cards | ||
| 30 | pub mod sd; | ||
| 31 | |||
| 32 | /// Module for SDIO interface | ||
| 33 | pub mod sdio; | ||
| 34 | |||
| 29 | /// Interrupt handler. | 35 | /// Interrupt handler. |
| 30 | pub struct InterruptHandler<T: Instance> { | 36 | pub struct InterruptHandler<T: Instance> { |
| 31 | _phantom: PhantomData<T>, | 37 | _phantom: PhantomData<T>, |
| 32 | } | 38 | } |
| 33 | 39 | ||
| 34 | impl<T: Instance> InterruptHandler<T> { | ||
| 35 | fn enable_interrupts() { | ||
| 36 | let regs = T::regs(); | ||
| 37 | regs.maskr().write(|w| { | ||
| 38 | w.set_dcrcfailie(true); | ||
| 39 | w.set_dtimeoutie(true); | ||
| 40 | w.set_dataendie(true); | ||
| 41 | w.set_dbckendie(true); | ||
| 42 | |||
| 43 | #[cfg(sdmmc_v1)] | ||
| 44 | w.set_stbiterre(true); | ||
| 45 | #[cfg(sdmmc_v2)] | ||
| 46 | w.set_dabortie(true); | ||
| 47 | }); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { | 40 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { |
| 52 | unsafe fn on_interrupt() { | 41 | unsafe fn on_interrupt() { |
| 53 | T::state().wake(); | 42 | T::state().waker.wake(); |
| 54 | let status = T::regs().star().read(); | 43 | let status = T::info().regs.star().read(); |
| 55 | T::regs().maskr().modify(|w| { | 44 | T::info().regs.maskr().modify(|w| { |
| 56 | if status.dcrcfail() { | 45 | if status.dcrcfail() { |
| 57 | w.set_dcrcfailie(false) | 46 | w.set_dcrcfailie(false) |
| 58 | } | 47 | } |
| @@ -77,6 +66,57 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 77 | } | 66 | } |
| 78 | } | 67 | } |
| 79 | 68 | ||
| 69 | struct U128(pub u128); | ||
| 70 | |||
| 71 | trait TypedResp: Resp { | ||
| 72 | type Word: From<U128>; | ||
| 73 | } | ||
| 74 | |||
| 75 | impl From<U128> for () { | ||
| 76 | fn from(value: U128) -> Self { | ||
| 77 | match value.0 { | ||
| 78 | 0 => (), | ||
| 79 | _ => unreachable!(), | ||
| 80 | } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | impl From<U128> for u32 { | ||
| 85 | fn from(value: U128) -> Self { | ||
| 86 | unwrap!(value.0.try_into()) | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | impl From<U128> for u128 { | ||
| 91 | fn from(value: U128) -> Self { | ||
| 92 | value.0 | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | impl TypedResp for Rz { | ||
| 97 | type Word = (); | ||
| 98 | } | ||
| 99 | |||
| 100 | impl TypedResp for R1 { | ||
| 101 | type Word = u32; | ||
| 102 | } | ||
| 103 | |||
| 104 | impl TypedResp for R2 { | ||
| 105 | type Word = u128; | ||
| 106 | } | ||
| 107 | |||
| 108 | impl TypedResp for R3 { | ||
| 109 | type Word = u32; | ||
| 110 | } | ||
| 111 | |||
| 112 | impl TypedResp for R6 { | ||
| 113 | type Word = u32; | ||
| 114 | } | ||
| 115 | |||
| 116 | impl TypedResp for R7 { | ||
| 117 | type Word = u32; | ||
| 118 | } | ||
| 119 | |||
| 80 | /// Frequency used for SD Card initialization. Must be no higher than 400 kHz. | 120 | /// Frequency used for SD Card initialization. Must be no higher than 400 kHz. |
| 81 | const SD_INIT_FREQ: Hertz = Hertz(400_000); | 121 | const SD_INIT_FREQ: Hertz = Hertz(400_000); |
| 82 | 122 | ||
| @@ -99,54 +139,14 @@ impl Default for Signalling { | |||
| 99 | } | 139 | } |
| 100 | } | 140 | } |
| 101 | 141 | ||
| 102 | /// Aligned data block for SDMMC transfers. | 142 | const fn slice8_mut(x: &mut [u32]) -> &mut [u8] { |
| 103 | /// | 143 | let len = x.len() * 4; |
| 104 | /// This is a 512-byte array, aligned to 4 bytes to satisfy DMA requirements. | 144 | unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } |
| 105 | #[repr(align(4))] | ||
| 106 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 107 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 108 | pub struct DataBlock(pub [u8; 512]); | ||
| 109 | |||
| 110 | impl Deref for DataBlock { | ||
| 111 | type Target = [u8; 512]; | ||
| 112 | |||
| 113 | fn deref(&self) -> &Self::Target { | ||
| 114 | &self.0 | ||
| 115 | } | ||
| 116 | } | 145 | } |
| 117 | 146 | ||
| 118 | impl DerefMut for DataBlock { | 147 | const fn slice8_ref(x: &[u32]) -> &[u8] { |
| 119 | fn deref_mut(&mut self) -> &mut Self::Target { | 148 | let len = x.len() * 4; |
| 120 | &mut self.0 | 149 | unsafe { slice::from_raw_parts(x.as_ptr() as _, len) } |
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | /// Command Block buffer for SDMMC command transfers. | ||
| 125 | /// | ||
| 126 | /// This is a 16-word array, exposed so that DMA commpatible memory can be used if required. | ||
| 127 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 128 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 129 | pub struct CmdBlock(pub [u32; 16]); | ||
| 130 | |||
| 131 | impl CmdBlock { | ||
| 132 | /// Creates a new instance of CmdBlock | ||
| 133 | pub const fn new() -> Self { | ||
| 134 | Self([0u32; 16]) | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | impl Deref for CmdBlock { | ||
| 139 | type Target = [u32; 16]; | ||
| 140 | |||
| 141 | fn deref(&self) -> &Self::Target { | ||
| 142 | &self.0 | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | impl DerefMut for CmdBlock { | ||
| 147 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 148 | &mut self.0 | ||
| 149 | } | ||
| 150 | } | 150 | } |
| 151 | 151 | ||
| 152 | /// Errors | 152 | /// Errors |
| @@ -181,42 +181,6 @@ pub enum Error { | |||
| 181 | StBitErr, | 181 | StBitErr, |
| 182 | } | 182 | } |
| 183 | 183 | ||
| 184 | #[derive(Clone, Copy, Debug, Default)] | ||
| 185 | /// SD Card | ||
| 186 | pub struct Card { | ||
| 187 | /// The type of this card | ||
| 188 | pub card_type: CardCapacity, | ||
| 189 | /// Operation Conditions Register | ||
| 190 | pub ocr: OCR<SD>, | ||
| 191 | /// Relative Card Address | ||
| 192 | pub rca: u16, | ||
| 193 | /// Card ID | ||
| 194 | pub cid: CID<SD>, | ||
| 195 | /// Card Specific Data | ||
| 196 | pub csd: CSD<SD>, | ||
| 197 | /// SD CARD Configuration Register | ||
| 198 | pub scr: SCR, | ||
| 199 | /// SD Status | ||
| 200 | pub status: SDStatus, | ||
| 201 | } | ||
| 202 | |||
| 203 | #[derive(Clone, Copy, Debug, Default)] | ||
| 204 | /// eMMC storage | ||
| 205 | pub struct Emmc { | ||
| 206 | /// The capacity of this card | ||
| 207 | pub capacity: CardCapacity, | ||
| 208 | /// Operation Conditions Register | ||
| 209 | pub ocr: OCR<EMMC>, | ||
| 210 | /// Relative Card Address | ||
| 211 | pub rca: u16, | ||
| 212 | /// Card ID | ||
| 213 | pub cid: CID<EMMC>, | ||
| 214 | /// Card Specific Data | ||
| 215 | pub csd: CSD<EMMC>, | ||
| 216 | /// Extended Card Specific Data | ||
| 217 | pub ext_csd: ExtCSD, | ||
| 218 | } | ||
| 219 | |||
| 220 | #[repr(u8)] | 184 | #[repr(u8)] |
| 221 | enum PowerCtrl { | 185 | enum PowerCtrl { |
| 222 | Off = 0b00, | 186 | Off = 0b00, |
| @@ -259,6 +223,55 @@ fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> { | |||
| 259 | Ok((false, clk_div, clk_f)) | 223 | Ok((false, clk_div, clk_f)) |
| 260 | } | 224 | } |
| 261 | 225 | ||
| 226 | fn bus_width_vals(bus_width: BusWidth) -> (u8, u32) { | ||
| 227 | match bus_width { | ||
| 228 | BusWidth::One => (0, 1u32), | ||
| 229 | BusWidth::Four => (1, 4u32), | ||
| 230 | BusWidth::Eight => (2, 8u32), | ||
| 231 | _ => panic!("Invalid Bus Width"), | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | #[repr(u8)] | ||
| 236 | enum BlockSize { | ||
| 237 | Size1 = 0b0000, | ||
| 238 | Size2 = 0b0001, | ||
| 239 | Size4 = 0b0010, | ||
| 240 | Size8 = 0b0011, | ||
| 241 | Size16 = 0b0100, | ||
| 242 | Size32 = 0b0101, | ||
| 243 | Size64 = 0b0110, | ||
| 244 | Size128 = 0b0111, | ||
| 245 | Size256 = 0b1000, | ||
| 246 | Size512 = 0b1001, | ||
| 247 | Size1024 = 0b1010, | ||
| 248 | Size2048 = 0b1011, | ||
| 249 | Size4096 = 0b1100, | ||
| 250 | Size8192 = 0b1101, | ||
| 251 | Size16384 = 0b1110, | ||
| 252 | } | ||
| 253 | |||
| 254 | const fn block_size(bytes: usize) -> BlockSize { | ||
| 255 | match bytes { | ||
| 256 | 1 => BlockSize::Size1, | ||
| 257 | 2 => BlockSize::Size2, | ||
| 258 | 4 => BlockSize::Size4, | ||
| 259 | 8 => BlockSize::Size8, | ||
| 260 | 16 => BlockSize::Size16, | ||
| 261 | 32 => BlockSize::Size32, | ||
| 262 | 64 => BlockSize::Size64, | ||
| 263 | 128 => BlockSize::Size128, | ||
| 264 | 256 => BlockSize::Size256, | ||
| 265 | 512 => BlockSize::Size512, | ||
| 266 | 1024 => BlockSize::Size1024, | ||
| 267 | 2048 => BlockSize::Size2048, | ||
| 268 | 4096 => BlockSize::Size4096, | ||
| 269 | 8192 => BlockSize::Size8192, | ||
| 270 | 16384 => BlockSize::Size16384, | ||
| 271 | _ => core::unreachable!(), | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 262 | /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to | 275 | /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to |
| 263 | /// `sdmmc_ck` in Hertz. | 276 | /// `sdmmc_ck` in Hertz. |
| 264 | /// | 277 | /// |
| @@ -286,6 +299,34 @@ struct Transfer<'a> { | |||
| 286 | _dummy: PhantomData<&'a ()>, | 299 | _dummy: PhantomData<&'a ()>, |
| 287 | } | 300 | } |
| 288 | 301 | ||
| 302 | struct WrappedTransfer<'a> { | ||
| 303 | _transfer: Transfer<'a>, | ||
| 304 | sdmmc: &'a Sdmmc<'a>, | ||
| 305 | defused: bool, | ||
| 306 | } | ||
| 307 | |||
| 308 | impl<'a> WrappedTransfer<'a> { | ||
| 309 | pub const fn new(_transfer: Transfer<'a>, sdmmc: &'a Sdmmc) -> Self { | ||
| 310 | Self { | ||
| 311 | _transfer, | ||
| 312 | sdmmc, | ||
| 313 | defused: false, | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 317 | pub fn defuse(&mut self) { | ||
| 318 | self.defused = true; | ||
| 319 | } | ||
| 320 | } | ||
| 321 | |||
| 322 | impl<'a> Drop for WrappedTransfer<'a> { | ||
| 323 | fn drop(&mut self) { | ||
| 324 | if !self.defused { | ||
| 325 | self.sdmmc.on_drop(); | ||
| 326 | } | ||
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 289 | #[cfg(all(sdmmc_v1, dma))] | 330 | #[cfg(all(sdmmc_v1, dma))] |
| 290 | const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions { | 331 | const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions { |
| 291 | pburst: crate::dma::Burst::Incr4, | 332 | pburst: crate::dma::Burst::Incr4, |
| @@ -323,64 +364,11 @@ impl Default for Config { | |||
| 323 | } | 364 | } |
| 324 | } | 365 | } |
| 325 | 366 | ||
| 326 | /// Peripheral that can be operated over SDMMC | ||
| 327 | #[derive(Clone, Copy, Debug)] | ||
| 328 | pub enum SdmmcPeripheral { | ||
| 329 | /// SD Card | ||
| 330 | SdCard(Card), | ||
| 331 | /// eMMC memory | ||
| 332 | Emmc(Emmc), | ||
| 333 | } | ||
| 334 | |||
| 335 | impl SdmmcPeripheral { | ||
| 336 | /// Get this peripheral's address on the SDMMC bus | ||
| 337 | fn get_address(&self) -> u16 { | ||
| 338 | match self { | ||
| 339 | Self::SdCard(c) => c.rca, | ||
| 340 | Self::Emmc(e) => e.rca, | ||
| 341 | } | ||
| 342 | } | ||
| 343 | /// Is this a standard or high capacity peripheral? | ||
| 344 | fn get_capacity(&self) -> CardCapacity { | ||
| 345 | match self { | ||
| 346 | Self::SdCard(c) => c.card_type, | ||
| 347 | Self::Emmc(e) => e.capacity, | ||
| 348 | } | ||
| 349 | } | ||
| 350 | /// Size in bytes | ||
| 351 | fn size(&self) -> u64 { | ||
| 352 | match self { | ||
| 353 | // SDHC / SDXC / SDUC | ||
| 354 | Self::SdCard(c) => u64::from(c.csd.block_count()) * 512, | ||
| 355 | // capacity > 2GB | ||
| 356 | Self::Emmc(e) => u64::from(e.ext_csd.sector_count()) * 512, | ||
| 357 | } | ||
| 358 | } | ||
| 359 | |||
| 360 | /// Get a mutable reference to the SD Card. | ||
| 361 | /// | ||
| 362 | /// Panics if there is another peripheral instead. | ||
| 363 | fn get_sd_card(&mut self) -> &mut Card { | ||
| 364 | match *self { | ||
| 365 | Self::SdCard(ref mut c) => c, | ||
| 366 | _ => unreachable!("SD only"), | ||
| 367 | } | ||
| 368 | } | ||
| 369 | |||
| 370 | /// Get a mutable reference to the eMMC. | ||
| 371 | /// | ||
| 372 | /// Panics if there is another peripheral instead. | ||
| 373 | fn get_emmc(&mut self) -> &mut Emmc { | ||
| 374 | match *self { | ||
| 375 | Self::Emmc(ref mut e) => e, | ||
| 376 | _ => unreachable!("eMMC only"), | ||
| 377 | } | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | /// Sdmmc device | 367 | /// Sdmmc device |
| 382 | pub struct Sdmmc<'d, T: Instance> { | 368 | pub struct Sdmmc<'d> { |
| 383 | _peri: Peri<'d, T>, | 369 | info: &'static Info, |
| 370 | state: &'static State, | ||
| 371 | ker_clk: Hertz, | ||
| 384 | #[cfg(sdmmc_v1)] | 372 | #[cfg(sdmmc_v1)] |
| 385 | dma: ChannelAndRequest<'d>, | 373 | dma: ChannelAndRequest<'d>, |
| 386 | 374 | ||
| @@ -400,12 +388,6 @@ pub struct Sdmmc<'d, T: Instance> { | |||
| 400 | clock: Hertz, | 388 | clock: Hertz, |
| 401 | /// Current signalling scheme to card | 389 | /// Current signalling scheme to card |
| 402 | signalling: Signalling, | 390 | signalling: Signalling, |
| 403 | /// Card | ||
| 404 | card: Option<SdmmcPeripheral>, | ||
| 405 | |||
| 406 | /// An optional buffer to be used for commands | ||
| 407 | /// This should be used if there are special memory location requirements for dma | ||
| 408 | cmd_block: Option<&'d mut CmdBlock>, | ||
| 409 | } | 391 | } |
| 410 | 392 | ||
| 411 | const CLK_AF: AfType = AfType::output(OutputType::PushPull, Speed::VeryHigh); | 393 | const CLK_AF: AfType = AfType::output(OutputType::PushPull, Speed::VeryHigh); |
| @@ -416,9 +398,9 @@ const CMD_AF: AfType = AfType::output_pull(OutputType::PushPull, Speed::VeryHigh | |||
| 416 | const DATA_AF: AfType = CMD_AF; | 398 | const DATA_AF: AfType = CMD_AF; |
| 417 | 399 | ||
| 418 | #[cfg(sdmmc_v1)] | 400 | #[cfg(sdmmc_v1)] |
| 419 | impl<'d, T: Instance> Sdmmc<'d, T> { | 401 | impl<'d> Sdmmc<'d> { |
| 420 | /// Create a new SDMMC driver, with 1 data lane. | 402 | /// Create a new SDMMC driver, with 1 data lane. |
| 421 | pub fn new_1bit( | 403 | pub fn new_1bit<T: Instance>( |
| 422 | sdmmc: Peri<'d, T>, | 404 | sdmmc: Peri<'d, T>, |
| 423 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 405 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 424 | dma: Peri<'d, impl SdmmcDma<T>>, | 406 | dma: Peri<'d, impl SdmmcDma<T>>, |
| @@ -451,7 +433,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 451 | } | 433 | } |
| 452 | 434 | ||
| 453 | /// Create a new SDMMC driver, with 4 data lanes. | 435 | /// Create a new SDMMC driver, with 4 data lanes. |
| 454 | pub fn new_4bit( | 436 | pub fn new_4bit<T: Instance>( |
| 455 | sdmmc: Peri<'d, T>, | 437 | sdmmc: Peri<'d, T>, |
| 456 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 438 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 457 | dma: Peri<'d, impl SdmmcDma<T>>, | 439 | dma: Peri<'d, impl SdmmcDma<T>>, |
| @@ -491,9 +473,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 491 | } | 473 | } |
| 492 | 474 | ||
| 493 | #[cfg(sdmmc_v1)] | 475 | #[cfg(sdmmc_v1)] |
| 494 | impl<'d, T: Instance> Sdmmc<'d, T> { | 476 | impl<'d> Sdmmc<'d> { |
| 495 | /// Create a new SDMMC driver, with 8 data lanes. | 477 | /// Create a new SDMMC driver, with 8 data lanes. |
| 496 | pub fn new_8bit( | 478 | pub fn new_8bit<T: Instance>( |
| 497 | sdmmc: Peri<'d, T>, | 479 | sdmmc: Peri<'d, T>, |
| 498 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 480 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 499 | dma: Peri<'d, impl SdmmcDma<T>>, | 481 | dma: Peri<'d, impl SdmmcDma<T>>, |
| @@ -541,9 +523,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 541 | } | 523 | } |
| 542 | 524 | ||
| 543 | #[cfg(sdmmc_v2)] | 525 | #[cfg(sdmmc_v2)] |
| 544 | impl<'d, T: Instance> Sdmmc<'d, T> { | 526 | impl<'d> Sdmmc<'d> { |
| 545 | /// Create a new SDMMC driver, with 1 data lane. | 527 | /// Create a new SDMMC driver, with 1 data lane. |
| 546 | pub fn new_1bit( | 528 | pub fn new_1bit<T: Instance>( |
| 547 | sdmmc: Peri<'d, T>, | 529 | sdmmc: Peri<'d, T>, |
| 548 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 530 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 549 | clk: Peri<'d, impl CkPin<T>>, | 531 | clk: Peri<'d, impl CkPin<T>>, |
| @@ -574,7 +556,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 574 | } | 556 | } |
| 575 | 557 | ||
| 576 | /// Create a new SDMMC driver, with 4 data lanes. | 558 | /// Create a new SDMMC driver, with 4 data lanes. |
| 577 | pub fn new_4bit( | 559 | pub fn new_4bit<T: Instance>( |
| 578 | sdmmc: Peri<'d, T>, | 560 | sdmmc: Peri<'d, T>, |
| 579 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 561 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 580 | clk: Peri<'d, impl CkPin<T>>, | 562 | clk: Peri<'d, impl CkPin<T>>, |
| @@ -612,9 +594,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 612 | } | 594 | } |
| 613 | 595 | ||
| 614 | #[cfg(sdmmc_v2)] | 596 | #[cfg(sdmmc_v2)] |
| 615 | impl<'d, T: Instance> Sdmmc<'d, T> { | 597 | impl<'d> Sdmmc<'d> { |
| 616 | /// Create a new SDMMC driver, with 8 data lanes. | 598 | /// Create a new SDMMC driver, with 8 data lanes. |
| 617 | pub fn new_8bit( | 599 | pub fn new_8bit<T: Instance>( |
| 618 | sdmmc: Peri<'d, T>, | 600 | sdmmc: Peri<'d, T>, |
| 619 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 601 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 620 | clk: Peri<'d, impl CkPin<T>>, | 602 | clk: Peri<'d, impl CkPin<T>>, |
| @@ -659,9 +641,24 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 659 | } | 641 | } |
| 660 | } | 642 | } |
| 661 | 643 | ||
| 662 | impl<'d, T: Instance> Sdmmc<'d, T> { | 644 | impl<'d> Sdmmc<'d> { |
| 663 | fn new_inner( | 645 | fn enable_interrupts(&self) { |
| 664 | sdmmc: Peri<'d, T>, | 646 | let regs = self.info.regs; |
| 647 | regs.maskr().write(|w| { | ||
| 648 | w.set_dcrcfailie(true); | ||
| 649 | w.set_dtimeoutie(true); | ||
| 650 | w.set_dataendie(true); | ||
| 651 | w.set_dbckendie(true); | ||
| 652 | |||
| 653 | #[cfg(sdmmc_v1)] | ||
| 654 | w.set_stbiterre(true); | ||
| 655 | #[cfg(sdmmc_v2)] | ||
| 656 | w.set_dabortie(true); | ||
| 657 | }); | ||
| 658 | } | ||
| 659 | |||
| 660 | fn new_inner<T: Instance>( | ||
| 661 | _sdmmc: Peri<'d, T>, | ||
| 665 | #[cfg(sdmmc_v1)] dma: ChannelAndRequest<'d>, | 662 | #[cfg(sdmmc_v1)] dma: ChannelAndRequest<'d>, |
| 666 | clk: Peri<'d, AnyPin>, | 663 | clk: Peri<'d, AnyPin>, |
| 667 | cmd: Peri<'d, AnyPin>, | 664 | cmd: Peri<'d, AnyPin>, |
| @@ -680,8 +677,11 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 680 | T::Interrupt::unpend(); | 677 | T::Interrupt::unpend(); |
| 681 | unsafe { T::Interrupt::enable() }; | 678 | unsafe { T::Interrupt::enable() }; |
| 682 | 679 | ||
| 683 | let regs = T::regs(); | 680 | let info = T::info(); |
| 684 | regs.clkcr().write(|w| { | 681 | let state = T::state(); |
| 682 | let ker_clk = T::frequency(); | ||
| 683 | |||
| 684 | info.regs.clkcr().write(|w| { | ||
| 685 | w.set_pwrsav(false); | 685 | w.set_pwrsav(false); |
| 686 | w.set_negedge(false); | 686 | w.set_negedge(false); |
| 687 | 687 | ||
| @@ -698,10 +698,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 698 | 698 | ||
| 699 | // Power off, writen 00: Clock to the card is stopped; | 699 | // Power off, writen 00: Clock to the card is stopped; |
| 700 | // D[7:0], CMD, and CK are driven high. | 700 | // D[7:0], CMD, and CK are driven high. |
| 701 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8)); | 701 | info.regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8)); |
| 702 | 702 | ||
| 703 | Self { | 703 | Self { |
| 704 | _peri: sdmmc, | 704 | info, |
| 705 | state, | ||
| 706 | ker_clk, | ||
| 705 | #[cfg(sdmmc_v1)] | 707 | #[cfg(sdmmc_v1)] |
| 706 | dma, | 708 | dma, |
| 707 | 709 | ||
| @@ -719,15 +721,13 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 719 | config, | 721 | config, |
| 720 | clock: SD_INIT_FREQ, | 722 | clock: SD_INIT_FREQ, |
| 721 | signalling: Default::default(), | 723 | signalling: Default::default(), |
| 722 | card: None, | ||
| 723 | cmd_block: None, | ||
| 724 | } | 724 | } |
| 725 | } | 725 | } |
| 726 | 726 | ||
| 727 | /// Data transfer is in progress | 727 | /// Data transfer is in progress |
| 728 | #[inline] | 728 | #[inline] |
| 729 | fn data_active() -> bool { | 729 | fn data_active(&self) -> bool { |
| 730 | let regs = T::regs(); | 730 | let regs = self.info.regs; |
| 731 | 731 | ||
| 732 | let status = regs.star().read(); | 732 | let status = regs.star().read(); |
| 733 | #[cfg(sdmmc_v1)] | 733 | #[cfg(sdmmc_v1)] |
| @@ -738,8 +738,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 738 | 738 | ||
| 739 | /// Coammand transfer is in progress | 739 | /// Coammand transfer is in progress |
| 740 | #[inline] | 740 | #[inline] |
| 741 | fn cmd_active() -> bool { | 741 | fn cmd_active(&self) -> bool { |
| 742 | let regs = T::regs(); | 742 | let regs = self.info.regs; |
| 743 | 743 | ||
| 744 | let status = regs.star().read(); | 744 | let status = regs.star().read(); |
| 745 | #[cfg(sdmmc_v1)] | 745 | #[cfg(sdmmc_v1)] |
| @@ -750,8 +750,16 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 750 | 750 | ||
| 751 | /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2) | 751 | /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2) |
| 752 | #[inline] | 752 | #[inline] |
| 753 | fn wait_idle() { | 753 | fn wait_idle(&self) { |
| 754 | while Self::data_active() || Self::cmd_active() {} | 754 | while self.data_active() || self.cmd_active() {} |
| 755 | } | ||
| 756 | |||
| 757 | fn bus_width(&self) -> BusWidth { | ||
| 758 | match (self.d3.is_some(), self.d7.is_some()) { | ||
| 759 | (true, true) => BusWidth::Eight, | ||
| 760 | (true, false) => BusWidth::Four, | ||
| 761 | _ => BusWidth::One, | ||
| 762 | } | ||
| 755 | } | 763 | } |
| 756 | 764 | ||
| 757 | /// # Safety | 765 | /// # Safety |
| @@ -759,23 +767,25 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 759 | /// `buffer` must be valid for the whole transfer and word aligned | 767 | /// `buffer` must be valid for the whole transfer and word aligned |
| 760 | #[allow(unused_variables)] | 768 | #[allow(unused_variables)] |
| 761 | fn prepare_datapath_read<'a>( | 769 | fn prepare_datapath_read<'a>( |
| 762 | config: &Config, | 770 | &'a self, |
| 763 | #[cfg(sdmmc_v1)] dma: &'a mut ChannelAndRequest<'d>, | ||
| 764 | buffer: &'a mut [u32], | 771 | buffer: &'a mut [u32], |
| 765 | length_bytes: u32, | 772 | block_size: BlockSize, |
| 766 | block_size: u8, | 773 | byte_mode: bool, |
| 767 | ) -> Transfer<'a> { | 774 | ) -> WrappedTransfer<'a> { |
| 768 | assert!(block_size <= 14, "Block size up to 2^14 bytes"); | 775 | let regs = self.info.regs; |
| 769 | let regs = T::regs(); | ||
| 770 | 776 | ||
| 771 | // Command AND Data state machines must be idle | 777 | // Command AND Data state machines must be idle |
| 772 | Self::wait_idle(); | 778 | self.wait_idle(); |
| 773 | Self::clear_interrupt_flags(); | 779 | self.clear_interrupt_flags(); |
| 774 | 780 | ||
| 775 | regs.dlenr().write(|w| w.set_datalength(length_bytes)); | 781 | regs.dlenr().write(|w| w.set_datalength(size_of_val(buffer) as u32)); |
| 776 | 782 | ||
| 783 | // SAFETY: No other functions use the dma | ||
| 777 | #[cfg(sdmmc_v1)] | 784 | #[cfg(sdmmc_v1)] |
| 778 | let transfer = unsafe { dma.read(regs.fifor().as_ptr() as *mut u32, buffer, DMA_TRANSFER_OPTIONS) }; | 785 | let transfer = unsafe { |
| 786 | self.dma | ||
| 787 | .read_unchecked(regs.fifor().as_ptr() as *mut u32, buffer, DMA_TRANSFER_OPTIONS) | ||
| 788 | }; | ||
| 779 | #[cfg(sdmmc_v2)] | 789 | #[cfg(sdmmc_v2)] |
| 780 | let transfer = { | 790 | let transfer = { |
| 781 | regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_mut_ptr() as u32)); | 791 | regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_mut_ptr() as u32)); |
| @@ -785,8 +795,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 785 | } | 795 | } |
| 786 | }; | 796 | }; |
| 787 | 797 | ||
| 798 | #[cfg(sdmmc_v2)] | ||
| 799 | let byte_mode = byte_mode as u8; | ||
| 800 | |||
| 788 | regs.dctrl().modify(|w| { | 801 | regs.dctrl().modify(|w| { |
| 789 | w.set_dblocksize(block_size); | 802 | w.set_dtmode(byte_mode); |
| 803 | w.set_dblocksize(block_size as u8); | ||
| 790 | w.set_dtdir(true); | 804 | w.set_dtdir(true); |
| 791 | #[cfg(sdmmc_v1)] | 805 | #[cfg(sdmmc_v1)] |
| 792 | { | 806 | { |
| @@ -795,26 +809,33 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 795 | } | 809 | } |
| 796 | }); | 810 | }); |
| 797 | 811 | ||
| 798 | transfer | 812 | self.enable_interrupts(); |
| 813 | |||
| 814 | WrappedTransfer::new(transfer, &self) | ||
| 799 | } | 815 | } |
| 800 | 816 | ||
| 801 | /// # Safety | 817 | /// # Safety |
| 802 | /// | 818 | /// |
| 803 | /// `buffer` must be valid for the whole transfer and word aligned | 819 | /// `buffer` must be valid for the whole transfer and word aligned |
| 804 | fn prepare_datapath_write<'a>(&'a mut self, buffer: &'a [u32], length_bytes: u32, block_size: u8) -> Transfer<'a> { | 820 | fn prepare_datapath_write<'a>( |
| 805 | assert!(block_size <= 14, "Block size up to 2^14 bytes"); | 821 | &'a self, |
| 806 | let regs = T::regs(); | 822 | buffer: &'a [u32], |
| 823 | block_size: BlockSize, | ||
| 824 | byte_mode: bool, | ||
| 825 | ) -> WrappedTransfer<'a> { | ||
| 826 | let regs = self.info.regs; | ||
| 807 | 827 | ||
| 808 | // Command AND Data state machines must be idle | 828 | // Command AND Data state machines must be idle |
| 809 | Self::wait_idle(); | 829 | self.wait_idle(); |
| 810 | Self::clear_interrupt_flags(); | 830 | self.clear_interrupt_flags(); |
| 811 | 831 | ||
| 812 | regs.dlenr().write(|w| w.set_datalength(length_bytes)); | 832 | regs.dlenr().write(|w| w.set_datalength(size_of_val(buffer) as u32)); |
| 813 | 833 | ||
| 834 | // SAFETY: No other functions use the dma | ||
| 814 | #[cfg(sdmmc_v1)] | 835 | #[cfg(sdmmc_v1)] |
| 815 | let transfer = unsafe { | 836 | let transfer = unsafe { |
| 816 | self.dma | 837 | self.dma |
| 817 | .write(buffer, regs.fifor().as_ptr() as *mut u32, DMA_TRANSFER_OPTIONS) | 838 | .write_unchecked(buffer, regs.fifor().as_ptr() as *mut u32, DMA_TRANSFER_OPTIONS) |
| 818 | }; | 839 | }; |
| 819 | #[cfg(sdmmc_v2)] | 840 | #[cfg(sdmmc_v2)] |
| 820 | let transfer = { | 841 | let transfer = { |
| @@ -825,8 +846,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 825 | } | 846 | } |
| 826 | }; | 847 | }; |
| 827 | 848 | ||
| 849 | #[cfg(sdmmc_v2)] | ||
| 850 | let byte_mode = byte_mode as u8; | ||
| 851 | |||
| 828 | regs.dctrl().modify(|w| { | 852 | regs.dctrl().modify(|w| { |
| 829 | w.set_dblocksize(block_size); | 853 | w.set_dtmode(byte_mode); |
| 854 | w.set_dblocksize(block_size as u8); | ||
| 830 | w.set_dtdir(false); | 855 | w.set_dtdir(false); |
| 831 | #[cfg(sdmmc_v1)] | 856 | #[cfg(sdmmc_v1)] |
| 832 | { | 857 | { |
| @@ -835,12 +860,14 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 835 | } | 860 | } |
| 836 | }); | 861 | }); |
| 837 | 862 | ||
| 838 | transfer | 863 | self.enable_interrupts(); |
| 864 | |||
| 865 | WrappedTransfer::new(transfer, &self) | ||
| 839 | } | 866 | } |
| 840 | 867 | ||
| 841 | /// Stops the DMA datapath | 868 | /// Stops the DMA datapath |
| 842 | fn stop_datapath() { | 869 | fn stop_datapath(&self) { |
| 843 | let regs = T::regs(); | 870 | let regs = self.info.regs; |
| 844 | 871 | ||
| 845 | #[cfg(sdmmc_v1)] | 872 | #[cfg(sdmmc_v1)] |
| 846 | regs.dctrl().modify(|w| { | 873 | regs.dctrl().modify(|w| { |
| @@ -851,49 +878,58 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 851 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); | 878 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); |
| 852 | } | 879 | } |
| 853 | 880 | ||
| 881 | fn init_idle(&mut self) -> Result<(), Error> { | ||
| 882 | let regs = self.info.regs; | ||
| 883 | |||
| 884 | self.clkcr_set_clkdiv(SD_INIT_FREQ, BusWidth::One)?; | ||
| 885 | regs.dtimer() | ||
| 886 | .write(|w| w.set_datatime(self.config.data_transfer_timeout)); | ||
| 887 | |||
| 888 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); | ||
| 889 | self.cmd(common_cmd::idle(), false) | ||
| 890 | } | ||
| 891 | |||
| 854 | /// Sets the CLKDIV field in CLKCR. Updates clock field in self | 892 | /// Sets the CLKDIV field in CLKCR. Updates clock field in self |
| 855 | fn clkcr_set_clkdiv(&mut self, freq: u32, width: BusWidth) -> Result<(), Error> { | 893 | fn clkcr_set_clkdiv(&mut self, freq: Hertz, width: BusWidth) -> Result<(), Error> { |
| 856 | let regs = T::regs(); | 894 | let regs = self.info.regs; |
| 857 | |||
| 858 | let width_u32 = match width { | ||
| 859 | BusWidth::One => 1u32, | ||
| 860 | BusWidth::Four => 4u32, | ||
| 861 | BusWidth::Eight => 8u32, | ||
| 862 | _ => panic!("Invalid Bus Width"), | ||
| 863 | }; | ||
| 864 | 895 | ||
| 865 | let ker_ck = T::frequency(); | 896 | let (widbus, width_u32) = bus_width_vals(width); |
| 866 | let (_bypass, clkdiv, new_clock) = clk_div(ker_ck, freq)?; | 897 | let (_bypass, clkdiv, new_clock) = clk_div(self.ker_clk, freq.0)?; |
| 867 | 898 | ||
| 868 | // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 | 899 | // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 |
| 869 | // Section 55.5.8 | 900 | // Section 55.5.8 |
| 870 | let sdmmc_bus_bandwidth = new_clock.0 * width_u32; | 901 | let sdmmc_bus_bandwidth = new_clock.0 * width_u32; |
| 871 | assert!(ker_ck.0 > 3 * sdmmc_bus_bandwidth / 32); | 902 | assert!(self.ker_clk.0 > 3 * sdmmc_bus_bandwidth / 32); |
| 872 | self.clock = new_clock; | 903 | self.clock = new_clock; |
| 873 | 904 | ||
| 874 | // CPSMACT and DPSMACT must be 0 to set CLKDIV | 905 | // CPSMACT and DPSMACT must be 0 to set CLKDIV or WIDBUS |
| 875 | Self::wait_idle(); | 906 | self.wait_idle(); |
| 876 | regs.clkcr().modify(|w| { | 907 | regs.clkcr().modify(|w| { |
| 877 | w.set_clkdiv(clkdiv); | 908 | w.set_clkdiv(clkdiv); |
| 878 | #[cfg(sdmmc_v1)] | 909 | #[cfg(sdmmc_v1)] |
| 879 | w.set_bypass(_bypass); | 910 | w.set_bypass(_bypass); |
| 911 | w.set_widbus(widbus); | ||
| 880 | }); | 912 | }); |
| 881 | 913 | ||
| 882 | Ok(()) | 914 | Ok(()) |
| 883 | } | 915 | } |
| 884 | 916 | ||
| 917 | fn get_cid(&self) -> Result<u128, Error> { | ||
| 918 | self.cmd(common_cmd::all_send_cid(), false) // CMD2 | ||
| 919 | } | ||
| 920 | |||
| 921 | fn get_csd(&self, address: u16) -> Result<u128, Error> { | ||
| 922 | self.cmd(common_cmd::send_csd(address), false) | ||
| 923 | } | ||
| 924 | |||
| 885 | /// Query the card status (CMD13, returns R1) | 925 | /// Query the card status (CMD13, returns R1) |
| 886 | fn read_status<Ext>(&self, card: &SdmmcPeripheral) -> Result<CardStatus<Ext>, Error> | 926 | fn read_status<A: Addressable>(&self, card: &A) -> Result<CardStatus<A::Ext>, Error> |
| 887 | where | 927 | where |
| 888 | CardStatus<Ext>: From<u32>, | 928 | CardStatus<A::Ext>: From<u32>, |
| 889 | { | 929 | { |
| 890 | let regs = T::regs(); | ||
| 891 | let rca = card.get_address(); | 930 | let rca = card.get_address(); |
| 892 | 931 | ||
| 893 | Self::cmd(common_cmd::card_status(rca, false), false)?; // CMD13 | 932 | Ok(self.cmd(common_cmd::card_status(rca, false), false)?.into()) // CMD13 |
| 894 | |||
| 895 | let r1 = regs.respr(0).read().cardstatus(); | ||
| 896 | Ok(r1.into()) | ||
| 897 | } | 933 | } |
| 898 | 934 | ||
| 899 | /// Select one card and place it into the _Tranfer State_ | 935 | /// Select one card and place it into the _Tranfer State_ |
| @@ -904,17 +940,23 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 904 | // Determine Relative Card Address (RCA) of given card | 940 | // Determine Relative Card Address (RCA) of given card |
| 905 | let rca = rca.unwrap_or(0); | 941 | let rca = rca.unwrap_or(0); |
| 906 | 942 | ||
| 907 | let r = Self::cmd(common_cmd::select_card(rca), false); | 943 | let resp = self.cmd(common_cmd::select_card(rca), false); |
| 908 | match (r, rca) { | 944 | |
| 909 | (Err(Error::Timeout), 0) => Ok(()), | 945 | if let Err(Error::Timeout) = resp |
| 910 | _ => r, | 946 | && rca == 0 |
| 947 | { | ||
| 948 | return Ok(()); | ||
| 911 | } | 949 | } |
| 950 | |||
| 951 | resp?; | ||
| 952 | |||
| 953 | Ok(()) | ||
| 912 | } | 954 | } |
| 913 | 955 | ||
| 914 | /// Clear flags in interrupt clear register | 956 | /// Clear flags in interrupt clear register |
| 915 | #[inline] | 957 | #[inline] |
| 916 | fn clear_interrupt_flags() { | 958 | fn clear_interrupt_flags(&self) { |
| 917 | let regs = T::regs(); | 959 | let regs = self.info.regs; |
| 918 | regs.icr().write(|w| { | 960 | regs.icr().write(|w| { |
| 919 | w.set_ccrcfailc(true); | 961 | w.set_ccrcfailc(true); |
| 920 | w.set_dcrcfailc(true); | 962 | w.set_dcrcfailc(true); |
| @@ -947,12 +989,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 947 | 989 | ||
| 948 | /// Send command to card | 990 | /// Send command to card |
| 949 | #[allow(unused_variables)] | 991 | #[allow(unused_variables)] |
| 950 | fn cmd<R: Resp>(cmd: Cmd<R>, data: bool) -> Result<(), Error> { | 992 | fn cmd<R: TypedResp>(&self, cmd: Cmd<R>, data: bool) -> Result<R::Word, Error> { |
| 951 | let regs = T::regs(); | 993 | let regs = self.info.regs; |
| 952 | 994 | ||
| 953 | Self::clear_interrupt_flags(); | 995 | self.clear_interrupt_flags(); |
| 954 | // CP state machine must be idle | 996 | // CP state machine must be idle |
| 955 | while Self::cmd_active() {} | 997 | while self.cmd_active() {} |
| 956 | 998 | ||
| 957 | // Command arg | 999 | // Command arg |
| 958 | regs.argr().write(|w| w.set_cmdarg(cmd.arg)); | 1000 | regs.argr().write(|w| w.set_cmdarg(cmd.arg)); |
| @@ -994,16 +1036,29 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 994 | } else if status.ccrcfail() { | 1036 | } else if status.ccrcfail() { |
| 995 | return Err(Error::Crc); | 1037 | return Err(Error::Crc); |
| 996 | } | 1038 | } |
| 997 | Ok(()) | 1039 | |
| 1040 | Ok(match R::LENGTH { | ||
| 1041 | ResponseLen::Zero => U128(0u128), | ||
| 1042 | ResponseLen::R48 => U128(self.info.regs.respr(0).read().cardstatus() as u128), | ||
| 1043 | ResponseLen::R136 => { | ||
| 1044 | let cid0 = self.info.regs.respr(0).read().cardstatus() as u128; | ||
| 1045 | let cid1 = self.info.regs.respr(1).read().cardstatus() as u128; | ||
| 1046 | let cid2 = self.info.regs.respr(2).read().cardstatus() as u128; | ||
| 1047 | let cid3 = self.info.regs.respr(3).read().cardstatus() as u128; | ||
| 1048 | |||
| 1049 | U128((cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3)) | ||
| 1050 | } | ||
| 1051 | } | ||
| 1052 | .into()) | ||
| 998 | } | 1053 | } |
| 999 | 1054 | ||
| 1000 | fn on_drop() { | 1055 | fn on_drop(&self) { |
| 1001 | let regs = T::regs(); | 1056 | let regs = self.info.regs; |
| 1002 | if Self::data_active() { | 1057 | if self.data_active() { |
| 1003 | Self::clear_interrupt_flags(); | 1058 | self.clear_interrupt_flags(); |
| 1004 | // Send abort | 1059 | // Send abort |
| 1005 | // CP state machine must be idle | 1060 | // CP state machine must be idle |
| 1006 | while Self::cmd_active() {} | 1061 | while self.cmd_active() {} |
| 1007 | 1062 | ||
| 1008 | // Command arg | 1063 | // Command arg |
| 1009 | regs.argr().write(|w| w.set_cmdarg(0)); | 1064 | regs.argr().write(|w| w.set_cmdarg(0)); |
| @@ -1023,22 +1078,22 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1023 | }); | 1078 | }); |
| 1024 | 1079 | ||
| 1025 | // Wait for the abort | 1080 | // Wait for the abort |
| 1026 | while Self::data_active() {} | 1081 | while self.data_active() {} |
| 1027 | } | 1082 | } |
| 1028 | regs.maskr().write(|_| ()); // disable irqs | 1083 | regs.maskr().write(|_| ()); // disable irqs |
| 1029 | Self::clear_interrupt_flags(); | 1084 | self.clear_interrupt_flags(); |
| 1030 | Self::stop_datapath(); | 1085 | self.stop_datapath(); |
| 1031 | } | 1086 | } |
| 1032 | 1087 | ||
| 1033 | /// Wait for a previously started datapath transfer to complete from an interrupt. | 1088 | /// Wait for a previously started datapath transfer to complete from an interrupt. |
| 1034 | #[inline] | 1089 | #[inline] |
| 1035 | #[allow(unused)] | 1090 | #[allow(unused)] |
| 1036 | async fn complete_datapath_transfer(block: bool) -> Result<(), Error> { | 1091 | async fn complete_datapath_transfer(&self, mut transfer: WrappedTransfer<'_>, block: bool) -> Result<(), Error> { |
| 1037 | let res = poll_fn(|cx| { | 1092 | let res = poll_fn(|cx| { |
| 1038 | // Compiler might not be sufficiently constrained here | 1093 | // Compiler might not be sufficiently constrained here |
| 1039 | // https://github.com/embassy-rs/embassy/issues/4723 | 1094 | // https://github.com/embassy-rs/embassy/issues/4723 |
| 1040 | T::state().register(cx.waker()); | 1095 | self.state.waker.register(cx.waker()); |
| 1041 | let status = T::regs().star().read(); | 1096 | let status = self.info.regs.star().read(); |
| 1042 | 1097 | ||
| 1043 | if status.dcrcfail() { | 1098 | if status.dcrcfail() { |
| 1044 | return Poll::Ready(Err(Error::Crc)); | 1099 | return Poll::Ready(Err(Error::Crc)); |
| @@ -1067,706 +1122,25 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1067 | }) | 1122 | }) |
| 1068 | .await; | 1123 | .await; |
| 1069 | 1124 | ||
| 1070 | Self::clear_interrupt_flags(); | 1125 | self.clear_interrupt_flags(); |
| 1071 | 1126 | self.stop_datapath(); | |
| 1072 | res | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | /// Read a data block. | ||
| 1076 | #[inline] | ||
| 1077 | pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { | ||
| 1078 | let _scoped_block_stop = T::RCC_INFO.block_stop(); | ||
| 1079 | let card_capacity = self.card()?.get_capacity(); | ||
| 1080 | |||
| 1081 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 1082 | let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; | ||
| 1083 | |||
| 1084 | // Always read 1 block of 512 bytes | ||
| 1085 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 1086 | let address = match card_capacity { | ||
| 1087 | CardCapacity::StandardCapacity => block_idx * 512, | ||
| 1088 | _ => block_idx, | ||
| 1089 | }; | ||
| 1090 | Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 | ||
| 1091 | 1127 | ||
| 1092 | let on_drop = OnDrop::new(|| Self::on_drop()); | 1128 | transfer.defuse(); |
| 1129 | drop(transfer); | ||
| 1093 | 1130 | ||
| 1094 | let transfer = Self::prepare_datapath_read( | ||
| 1095 | &self.config, | ||
| 1096 | #[cfg(sdmmc_v1)] | ||
| 1097 | &mut self.dma, | ||
| 1098 | buffer, | ||
| 1099 | 512, | ||
| 1100 | 9, | ||
| 1101 | ); | ||
| 1102 | InterruptHandler::<T>::enable_interrupts(); | ||
| 1103 | Self::cmd(common_cmd::read_single_block(address), true)?; | ||
| 1104 | |||
| 1105 | let res = Self::complete_datapath_transfer(true).await; | ||
| 1106 | |||
| 1107 | if res.is_ok() { | ||
| 1108 | on_drop.defuse(); | ||
| 1109 | Self::stop_datapath(); | ||
| 1110 | drop(transfer); | ||
| 1111 | } | ||
| 1112 | res | 1131 | res |
| 1113 | } | 1132 | } |
| 1114 | 1133 | ||
| 1115 | /// Read multiple data blocks. | ||
| 1116 | #[inline] | ||
| 1117 | pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { | ||
| 1118 | let _scoped_block_stop = T::RCC_INFO.block_stop(); | ||
| 1119 | let card_capacity = self.card()?.get_capacity(); | ||
| 1120 | |||
| 1121 | // NOTE(unsafe) reinterpret buffer as &mut [u32] | ||
| 1122 | let buffer = unsafe { | ||
| 1123 | let ptr = blocks.as_mut_ptr() as *mut u32; | ||
| 1124 | let len = blocks.len() * 128; | ||
| 1125 | core::slice::from_raw_parts_mut(ptr, len) | ||
| 1126 | }; | ||
| 1127 | |||
| 1128 | // Always read 1 block of 512 bytes | ||
| 1129 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 1130 | let address = match card_capacity { | ||
| 1131 | CardCapacity::StandardCapacity => block_idx * 512, | ||
| 1132 | _ => block_idx, | ||
| 1133 | }; | ||
| 1134 | Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 | ||
| 1135 | |||
| 1136 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1137 | |||
| 1138 | let transfer = Self::prepare_datapath_read( | ||
| 1139 | &self.config, | ||
| 1140 | #[cfg(sdmmc_v1)] | ||
| 1141 | &mut self.dma, | ||
| 1142 | buffer, | ||
| 1143 | 512 * blocks.len() as u32, | ||
| 1144 | 9, | ||
| 1145 | ); | ||
| 1146 | InterruptHandler::<T>::enable_interrupts(); | ||
| 1147 | |||
| 1148 | Self::cmd(common_cmd::read_multiple_blocks(address), true)?; | ||
| 1149 | |||
| 1150 | let res = Self::complete_datapath_transfer(false).await; | ||
| 1151 | |||
| 1152 | Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12 | ||
| 1153 | Self::clear_interrupt_flags(); | ||
| 1154 | |||
| 1155 | if res.is_ok() { | ||
| 1156 | on_drop.defuse(); | ||
| 1157 | Self::stop_datapath(); | ||
| 1158 | drop(transfer); | ||
| 1159 | } | ||
| 1160 | res | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | /// Write a data block. | ||
| 1164 | pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { | ||
| 1165 | let _scoped_block_stop = T::RCC_INFO.block_stop(); | ||
| 1166 | let card = self.card.as_mut().ok_or(Error::NoCard)?; | ||
| 1167 | |||
| 1168 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 1169 | let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; | ||
| 1170 | |||
| 1171 | // Always read 1 block of 512 bytes | ||
| 1172 | // cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 1173 | let address = match card.get_capacity() { | ||
| 1174 | CardCapacity::StandardCapacity => block_idx * 512, | ||
| 1175 | _ => block_idx, | ||
| 1176 | }; | ||
| 1177 | Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 | ||
| 1178 | |||
| 1179 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1180 | |||
| 1181 | // sdmmc_v1 uses different cmd/dma order than v2, but only for writes | ||
| 1182 | #[cfg(sdmmc_v1)] | ||
| 1183 | Self::cmd(common_cmd::write_single_block(address), true)?; | ||
| 1184 | |||
| 1185 | let transfer = self.prepare_datapath_write(buffer, 512, 9); | ||
| 1186 | InterruptHandler::<T>::enable_interrupts(); | ||
| 1187 | |||
| 1188 | #[cfg(sdmmc_v2)] | ||
| 1189 | Self::cmd(common_cmd::write_single_block(address), true)?; | ||
| 1190 | |||
| 1191 | let res = Self::complete_datapath_transfer(true).await; | ||
| 1192 | |||
| 1193 | match res { | ||
| 1194 | Ok(_) => { | ||
| 1195 | on_drop.defuse(); | ||
| 1196 | Self::stop_datapath(); | ||
| 1197 | drop(transfer); | ||
| 1198 | |||
| 1199 | // TODO: Make this configurable | ||
| 1200 | let mut timeout: u32 = 0x00FF_FFFF; | ||
| 1201 | |||
| 1202 | let card = self.card.as_ref().unwrap(); | ||
| 1203 | while timeout > 0 { | ||
| 1204 | let ready_for_data = match card { | ||
| 1205 | SdmmcPeripheral::Emmc(_) => self.read_status::<EMMC>(card)?.ready_for_data(), | ||
| 1206 | SdmmcPeripheral::SdCard(_) => self.read_status::<SD>(card)?.ready_for_data(), | ||
| 1207 | }; | ||
| 1208 | |||
| 1209 | if ready_for_data { | ||
| 1210 | return Ok(()); | ||
| 1211 | } | ||
| 1212 | timeout -= 1; | ||
| 1213 | } | ||
| 1214 | Err(Error::SoftwareTimeout) | ||
| 1215 | } | ||
| 1216 | Err(e) => Err(e), | ||
| 1217 | } | ||
| 1218 | } | ||
| 1219 | |||
| 1220 | /// Write multiple data blocks. | ||
| 1221 | pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error> { | ||
| 1222 | let _scoped_block_stop = T::RCC_INFO.block_stop(); | ||
| 1223 | let card = self.card.as_mut().ok_or(Error::NoCard)?; | ||
| 1224 | |||
| 1225 | // NOTE(unsafe) reinterpret buffer as &[u32] | ||
| 1226 | let buffer = unsafe { | ||
| 1227 | let ptr = blocks.as_ptr() as *const u32; | ||
| 1228 | let len = blocks.len() * 128; | ||
| 1229 | core::slice::from_raw_parts(ptr, len) | ||
| 1230 | }; | ||
| 1231 | |||
| 1232 | // Always read 1 block of 512 bytes | ||
| 1233 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 1234 | let address = match card.get_capacity() { | ||
| 1235 | CardCapacity::StandardCapacity => block_idx * 512, | ||
| 1236 | _ => block_idx, | ||
| 1237 | }; | ||
| 1238 | |||
| 1239 | Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 | ||
| 1240 | |||
| 1241 | let block_count = blocks.len(); | ||
| 1242 | |||
| 1243 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1244 | |||
| 1245 | #[cfg(sdmmc_v1)] | ||
| 1246 | Self::cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 | ||
| 1247 | |||
| 1248 | // Setup write command | ||
| 1249 | let transfer = self.prepare_datapath_write(buffer, 512 * block_count as u32, 9); | ||
| 1250 | InterruptHandler::<T>::enable_interrupts(); | ||
| 1251 | |||
| 1252 | #[cfg(sdmmc_v2)] | ||
| 1253 | Self::cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 | ||
| 1254 | |||
| 1255 | let res = Self::complete_datapath_transfer(false).await; | ||
| 1256 | |||
| 1257 | Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12 | ||
| 1258 | Self::clear_interrupt_flags(); | ||
| 1259 | |||
| 1260 | match res { | ||
| 1261 | Ok(_) => { | ||
| 1262 | on_drop.defuse(); | ||
| 1263 | Self::stop_datapath(); | ||
| 1264 | drop(transfer); | ||
| 1265 | |||
| 1266 | // TODO: Make this configurable | ||
| 1267 | let mut timeout: u32 = 0x00FF_FFFF; | ||
| 1268 | |||
| 1269 | // Try to read card status (ACMD13) | ||
| 1270 | while timeout > 0 { | ||
| 1271 | match self.read_sd_status().await { | ||
| 1272 | Ok(_) => return Ok(()), | ||
| 1273 | Err(Error::Timeout) => (), // Try again | ||
| 1274 | Err(e) => return Err(e), | ||
| 1275 | } | ||
| 1276 | timeout -= 1; | ||
| 1277 | } | ||
| 1278 | Err(Error::SoftwareTimeout) | ||
| 1279 | } | ||
| 1280 | Err(e) => Err(e), | ||
| 1281 | } | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | /// Get a reference to the initialized card | ||
| 1285 | /// | ||
| 1286 | /// # Errors | ||
| 1287 | /// | ||
| 1288 | /// Returns Error::NoCard if [`init_sd_card`](#method.init_sd_card) or | ||
| 1289 | /// [`init_emmc`](#method.init_emmc) has not previously succeeded | ||
| 1290 | #[inline] | ||
| 1291 | pub fn card(&self) -> Result<&SdmmcPeripheral, Error> { | ||
| 1292 | self.card.as_ref().ok_or(Error::NoCard) | ||
| 1293 | } | ||
| 1294 | |||
| 1295 | /// Get the current SDMMC bus clock | 1134 | /// Get the current SDMMC bus clock |
| 1296 | pub fn clock(&self) -> Hertz { | 1135 | pub fn clock(&self) -> Hertz { |
| 1297 | self.clock | 1136 | self.clock |
| 1298 | } | 1137 | } |
| 1299 | |||
| 1300 | /// Set a specific cmd buffer rather than using the default stack allocated one. | ||
| 1301 | /// This is required if stack RAM cannot be used with DMA and usually manifests | ||
| 1302 | /// itself as an indefinite wait on a dma transfer because the dma peripheral | ||
| 1303 | /// cannot access the memory. | ||
| 1304 | pub fn set_cmd_block(&mut self, cmd_block: &'d mut CmdBlock) { | ||
| 1305 | self.cmd_block = Some(cmd_block) | ||
| 1306 | } | ||
| 1307 | |||
| 1308 | async fn init_internal(&mut self, freq: Hertz, mut card: SdmmcPeripheral) -> Result<(), Error> { | ||
| 1309 | let regs = T::regs(); | ||
| 1310 | let ker_ck = T::frequency(); | ||
| 1311 | |||
| 1312 | let bus_width = match (self.d3.is_some(), self.d7.is_some()) { | ||
| 1313 | (true, true) => { | ||
| 1314 | if matches!(card, SdmmcPeripheral::SdCard(_)) { | ||
| 1315 | return Err(Error::BusWidth); | ||
| 1316 | } | ||
| 1317 | BusWidth::Eight | ||
| 1318 | } | ||
| 1319 | (true, false) => BusWidth::Four, | ||
| 1320 | _ => BusWidth::One, | ||
| 1321 | }; | ||
| 1322 | |||
| 1323 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 1324 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 1325 | let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); | ||
| 1326 | self.clock = init_clock; | ||
| 1327 | |||
| 1328 | // CPSMACT and DPSMACT must be 0 to set WIDBUS | ||
| 1329 | Self::wait_idle(); | ||
| 1330 | |||
| 1331 | regs.clkcr().modify(|w| { | ||
| 1332 | w.set_widbus(0); | ||
| 1333 | w.set_clkdiv(clkdiv); | ||
| 1334 | #[cfg(sdmmc_v1)] | ||
| 1335 | w.set_bypass(_bypass); | ||
| 1336 | }); | ||
| 1337 | regs.dtimer() | ||
| 1338 | .write(|w| w.set_datatime(self.config.data_transfer_timeout)); | ||
| 1339 | |||
| 1340 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); | ||
| 1341 | Self::cmd(common_cmd::idle(), false)?; | ||
| 1342 | |||
| 1343 | match card { | ||
| 1344 | SdmmcPeripheral::SdCard(ref mut card) => { | ||
| 1345 | // Check if cards supports CMD8 (with pattern) | ||
| 1346 | Self::cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; | ||
| 1347 | let cic = CIC::from(regs.respr(0).read().cardstatus()); | ||
| 1348 | |||
| 1349 | if cic.pattern() != 0xAA { | ||
| 1350 | return Err(Error::UnsupportedCardVersion); | ||
| 1351 | } | ||
| 1352 | |||
| 1353 | if cic.voltage_accepted() & 1 == 0 { | ||
| 1354 | return Err(Error::UnsupportedVoltage); | ||
| 1355 | } | ||
| 1356 | |||
| 1357 | let ocr = loop { | ||
| 1358 | // Signal that next command is a app command | ||
| 1359 | Self::cmd(common_cmd::app_cmd(0), false)?; // CMD55 | ||
| 1360 | |||
| 1361 | // 3.2-3.3V | ||
| 1362 | let voltage_window = 1 << 5; | ||
| 1363 | // Initialize card | ||
| 1364 | match Self::cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) { | ||
| 1365 | // ACMD41 | ||
| 1366 | Ok(_) => (), | ||
| 1367 | Err(Error::Crc) => (), | ||
| 1368 | Err(err) => return Err(err), | ||
| 1369 | } | ||
| 1370 | let ocr: OCR<SD> = regs.respr(0).read().cardstatus().into(); | ||
| 1371 | if !ocr.is_busy() { | ||
| 1372 | // Power up done | ||
| 1373 | break ocr; | ||
| 1374 | } | ||
| 1375 | }; | ||
| 1376 | |||
| 1377 | if ocr.high_capacity() { | ||
| 1378 | // Card is SDHC or SDXC or SDUC | ||
| 1379 | card.card_type = CardCapacity::HighCapacity; | ||
| 1380 | } else { | ||
| 1381 | card.card_type = CardCapacity::StandardCapacity; | ||
| 1382 | } | ||
| 1383 | card.ocr = ocr; | ||
| 1384 | } | ||
| 1385 | SdmmcPeripheral::Emmc(ref mut emmc) => { | ||
| 1386 | let ocr = loop { | ||
| 1387 | let high_voltage = 0b0 << 7; | ||
| 1388 | let access_mode = 0b10 << 29; | ||
| 1389 | let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15; | ||
| 1390 | // Initialize card | ||
| 1391 | match Self::cmd(emmc_cmd::send_op_cond(op_cond), false) { | ||
| 1392 | Ok(_) => (), | ||
| 1393 | Err(Error::Crc) => (), | ||
| 1394 | Err(err) => return Err(err), | ||
| 1395 | } | ||
| 1396 | let ocr: OCR<EMMC> = regs.respr(0).read().cardstatus().into(); | ||
| 1397 | if !ocr.is_busy() { | ||
| 1398 | // Power up done | ||
| 1399 | break ocr; | ||
| 1400 | } | ||
| 1401 | }; | ||
| 1402 | |||
| 1403 | emmc.capacity = if ocr.access_mode() == 0b10 { | ||
| 1404 | // Card is SDHC or SDXC or SDUC | ||
| 1405 | CardCapacity::HighCapacity | ||
| 1406 | } else { | ||
| 1407 | CardCapacity::StandardCapacity | ||
| 1408 | }; | ||
| 1409 | emmc.ocr = ocr; | ||
| 1410 | } | ||
| 1411 | } | ||
| 1412 | |||
| 1413 | Self::cmd(common_cmd::all_send_cid(), false)?; // CMD2 | ||
| 1414 | let cid0 = regs.respr(0).read().cardstatus() as u128; | ||
| 1415 | let cid1 = regs.respr(1).read().cardstatus() as u128; | ||
| 1416 | let cid2 = regs.respr(2).read().cardstatus() as u128; | ||
| 1417 | let cid3 = regs.respr(3).read().cardstatus() as u128; | ||
| 1418 | let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); | ||
| 1419 | |||
| 1420 | match card { | ||
| 1421 | SdmmcPeripheral::SdCard(ref mut card) => { | ||
| 1422 | card.cid = cid.into(); | ||
| 1423 | |||
| 1424 | Self::cmd(sd_cmd::send_relative_address(), false)?; | ||
| 1425 | let rca = RCA::<SD>::from(regs.respr(0).read().cardstatus()); | ||
| 1426 | card.rca = rca.address(); | ||
| 1427 | } | ||
| 1428 | SdmmcPeripheral::Emmc(ref mut emmc) => { | ||
| 1429 | emmc.cid = cid.into(); | ||
| 1430 | |||
| 1431 | emmc.rca = 1u16.into(); | ||
| 1432 | Self::cmd(emmc_cmd::assign_relative_address(emmc.rca), false)?; | ||
| 1433 | } | ||
| 1434 | } | ||
| 1435 | |||
| 1436 | Self::cmd(common_cmd::send_csd(card.get_address()), false)?; | ||
| 1437 | let csd0 = regs.respr(0).read().cardstatus() as u128; | ||
| 1438 | let csd1 = regs.respr(1).read().cardstatus() as u128; | ||
| 1439 | let csd2 = regs.respr(2).read().cardstatus() as u128; | ||
| 1440 | let csd3 = regs.respr(3).read().cardstatus() as u128; | ||
| 1441 | let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3); | ||
| 1442 | |||
| 1443 | self.select_card(Some(card.get_address()))?; | ||
| 1444 | |||
| 1445 | let bus_width = match card { | ||
| 1446 | SdmmcPeripheral::SdCard(ref mut card) => { | ||
| 1447 | card.csd = csd.into(); | ||
| 1448 | |||
| 1449 | self.get_scr(card).await?; | ||
| 1450 | |||
| 1451 | if !card.scr.bus_width_four() { | ||
| 1452 | BusWidth::One | ||
| 1453 | } else { | ||
| 1454 | BusWidth::Four | ||
| 1455 | } | ||
| 1456 | } | ||
| 1457 | SdmmcPeripheral::Emmc(ref mut emmc) => { | ||
| 1458 | emmc.csd = csd.into(); | ||
| 1459 | |||
| 1460 | bus_width | ||
| 1461 | } | ||
| 1462 | }; | ||
| 1463 | |||
| 1464 | // Set bus width | ||
| 1465 | let widbus = match bus_width { | ||
| 1466 | BusWidth::Eight => 2, | ||
| 1467 | BusWidth::Four => 1, | ||
| 1468 | BusWidth::One => 0, | ||
| 1469 | _ => unreachable!(), | ||
| 1470 | }; | ||
| 1471 | |||
| 1472 | match card { | ||
| 1473 | SdmmcPeripheral::SdCard(ref mut card) => { | ||
| 1474 | let acmd_arg = match bus_width { | ||
| 1475 | BusWidth::Four if card.scr.bus_width_four() => 2, | ||
| 1476 | _ => 0, | ||
| 1477 | }; | ||
| 1478 | Self::cmd(common_cmd::app_cmd(card.rca), false)?; | ||
| 1479 | Self::cmd(sd_cmd::cmd6(acmd_arg), false)?; | ||
| 1480 | } | ||
| 1481 | SdmmcPeripheral::Emmc(_) => { | ||
| 1482 | // Write bus width to ExtCSD byte 183 | ||
| 1483 | Self::cmd( | ||
| 1484 | emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus), | ||
| 1485 | false, | ||
| 1486 | )?; | ||
| 1487 | |||
| 1488 | // Wait for ready after R1b response | ||
| 1489 | loop { | ||
| 1490 | let status = self.read_status::<EMMC>(&card)?; | ||
| 1491 | |||
| 1492 | if status.ready_for_data() { | ||
| 1493 | break; | ||
| 1494 | } | ||
| 1495 | } | ||
| 1496 | } | ||
| 1497 | } | ||
| 1498 | |||
| 1499 | // CPSMACT and DPSMACT must be 0 to set WIDBUS | ||
| 1500 | Self::wait_idle(); | ||
| 1501 | |||
| 1502 | regs.clkcr().modify(|w| w.set_widbus(widbus)); | ||
| 1503 | |||
| 1504 | // Set Clock | ||
| 1505 | if freq.0 <= 25_000_000 { | ||
| 1506 | // Final clock frequency | ||
| 1507 | self.clkcr_set_clkdiv(freq.0, bus_width)?; | ||
| 1508 | } else { | ||
| 1509 | // Switch to max clock for SDR12 | ||
| 1510 | self.clkcr_set_clkdiv(25_000_000, bus_width)?; | ||
| 1511 | } | ||
| 1512 | |||
| 1513 | self.card = Some(card); | ||
| 1514 | |||
| 1515 | match card { | ||
| 1516 | SdmmcPeripheral::SdCard(_) => { | ||
| 1517 | // Read status | ||
| 1518 | self.read_sd_status().await?; | ||
| 1519 | |||
| 1520 | if freq.0 > 25_000_000 { | ||
| 1521 | // Switch to SDR25 | ||
| 1522 | self.signalling = self.switch_signalling_mode(Signalling::SDR25).await?; | ||
| 1523 | |||
| 1524 | if self.signalling == Signalling::SDR25 { | ||
| 1525 | // Set final clock frequency | ||
| 1526 | self.clkcr_set_clkdiv(freq.0, bus_width)?; | ||
| 1527 | |||
| 1528 | if self.read_status::<SD>(self.card.as_ref().unwrap())?.state() != CurrentState::Transfer { | ||
| 1529 | return Err(Error::SignalingSwitchFailed); | ||
| 1530 | } | ||
| 1531 | } | ||
| 1532 | } | ||
| 1533 | |||
| 1534 | // Read status after signalling change | ||
| 1535 | self.read_sd_status().await?; | ||
| 1536 | } | ||
| 1537 | SdmmcPeripheral::Emmc(_) => { | ||
| 1538 | self.read_ext_csd().await?; | ||
| 1539 | } | ||
| 1540 | } | ||
| 1541 | |||
| 1542 | Ok(()) | ||
| 1543 | } | ||
| 1544 | |||
| 1545 | /// Initializes card (if present) and sets the bus at the specified frequency. | ||
| 1546 | /// | ||
| 1547 | /// SD only. | ||
| 1548 | pub async fn init_sd_card(&mut self, freq: Hertz) -> Result<(), Error> { | ||
| 1549 | let _scoped_block_stop = T::RCC_INFO.block_stop(); | ||
| 1550 | |||
| 1551 | self.init_internal(freq, SdmmcPeripheral::SdCard(Card::default())).await | ||
| 1552 | } | ||
| 1553 | |||
| 1554 | /// Switch mode using CMD6. | ||
| 1555 | /// | ||
| 1556 | /// Attempt to set a new signalling mode. The selected | ||
| 1557 | /// signalling mode is returned. Expects the current clock | ||
| 1558 | /// frequency to be > 12.5MHz. | ||
| 1559 | /// | ||
| 1560 | /// SD only. | ||
| 1561 | async fn switch_signalling_mode(&mut self, signalling: Signalling) -> Result<Signalling, Error> { | ||
| 1562 | let _ = self.card.as_mut().ok_or(Error::NoCard)?.get_sd_card(); | ||
| 1563 | // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not | ||
| 1564 | // necessary" | ||
| 1565 | |||
| 1566 | let set_function = 0x8000_0000 | ||
| 1567 | | match signalling { | ||
| 1568 | // See PLSS v7_10 Table 4-11 | ||
| 1569 | Signalling::DDR50 => 0xFF_FF04, | ||
| 1570 | Signalling::SDR104 => 0xFF_1F03, | ||
| 1571 | Signalling::SDR50 => 0xFF_1F02, | ||
| 1572 | Signalling::SDR25 => 0xFF_FF01, | ||
| 1573 | Signalling::SDR12 => 0xFF_FF00, | ||
| 1574 | }; | ||
| 1575 | |||
| 1576 | let status = match self.cmd_block.as_deref_mut() { | ||
| 1577 | Some(x) => x, | ||
| 1578 | None => &mut CmdBlock::new(), | ||
| 1579 | }; | ||
| 1580 | |||
| 1581 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 1582 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1583 | |||
| 1584 | let transfer = Self::prepare_datapath_read( | ||
| 1585 | &self.config, | ||
| 1586 | #[cfg(sdmmc_v1)] | ||
| 1587 | &mut self.dma, | ||
| 1588 | status.as_mut(), | ||
| 1589 | 64, | ||
| 1590 | 6, | ||
| 1591 | ); | ||
| 1592 | InterruptHandler::<T>::enable_interrupts(); | ||
| 1593 | Self::cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 | ||
| 1594 | |||
| 1595 | let res = Self::complete_datapath_transfer(true).await; | ||
| 1596 | |||
| 1597 | // Host is allowed to use the new functions at least 8 | ||
| 1598 | // clocks after the end of the switch command | ||
| 1599 | // transaction. We know the current clock period is < 80ns, | ||
| 1600 | // so a total delay of 640ns is required here | ||
| 1601 | for _ in 0..300 { | ||
| 1602 | cortex_m::asm::nop(); | ||
| 1603 | } | ||
| 1604 | |||
| 1605 | match res { | ||
| 1606 | Ok(_) => { | ||
| 1607 | on_drop.defuse(); | ||
| 1608 | Self::stop_datapath(); | ||
| 1609 | drop(transfer); | ||
| 1610 | |||
| 1611 | // Function Selection of Function Group 1 | ||
| 1612 | let selection = (u32::from_be(status[4]) >> 24) & 0xF; | ||
| 1613 | |||
| 1614 | match selection { | ||
| 1615 | 0 => Ok(Signalling::SDR12), | ||
| 1616 | 1 => Ok(Signalling::SDR25), | ||
| 1617 | 2 => Ok(Signalling::SDR50), | ||
| 1618 | 3 => Ok(Signalling::SDR104), | ||
| 1619 | 4 => Ok(Signalling::DDR50), | ||
| 1620 | _ => Err(Error::UnsupportedCardType), | ||
| 1621 | } | ||
| 1622 | } | ||
| 1623 | Err(e) => Err(e), | ||
| 1624 | } | ||
| 1625 | } | ||
| 1626 | |||
| 1627 | /// Reads the SCR register. | ||
| 1628 | /// | ||
| 1629 | /// SD only. | ||
| 1630 | async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> { | ||
| 1631 | // Read the 64-bit SCR register | ||
| 1632 | Self::cmd(common_cmd::set_block_length(8), false)?; // CMD16 | ||
| 1633 | Self::cmd(common_cmd::app_cmd(card.rca), false)?; | ||
| 1634 | |||
| 1635 | let cmd_block = match self.cmd_block.as_deref_mut() { | ||
| 1636 | Some(x) => x, | ||
| 1637 | None => &mut CmdBlock::new(), | ||
| 1638 | }; | ||
| 1639 | let scr = &mut cmd_block.0[..2]; | ||
| 1640 | |||
| 1641 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 1642 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1643 | |||
| 1644 | let transfer = Self::prepare_datapath_read( | ||
| 1645 | &self.config, | ||
| 1646 | #[cfg(sdmmc_v1)] | ||
| 1647 | &mut self.dma, | ||
| 1648 | scr, | ||
| 1649 | 8, | ||
| 1650 | 3, | ||
| 1651 | ); | ||
| 1652 | InterruptHandler::<T>::enable_interrupts(); | ||
| 1653 | Self::cmd(sd_cmd::send_scr(), true)?; | ||
| 1654 | |||
| 1655 | let res = Self::complete_datapath_transfer(true).await; | ||
| 1656 | |||
| 1657 | if res.is_ok() { | ||
| 1658 | on_drop.defuse(); | ||
| 1659 | Self::stop_datapath(); | ||
| 1660 | drop(transfer); | ||
| 1661 | |||
| 1662 | unsafe { | ||
| 1663 | let scr_bytes = &*(&scr as *const _ as *const [u8; 8]); | ||
| 1664 | card.scr = SCR(u64::from_be_bytes(*scr_bytes)); | ||
| 1665 | } | ||
| 1666 | } | ||
| 1667 | res | ||
| 1668 | } | ||
| 1669 | |||
| 1670 | /// Reads the SD Status (ACMD13) | ||
| 1671 | /// | ||
| 1672 | /// SD only. | ||
| 1673 | async fn read_sd_status(&mut self) -> Result<(), Error> { | ||
| 1674 | let card = self.card.as_mut().ok_or(Error::NoCard)?.get_sd_card(); | ||
| 1675 | let rca = card.rca; | ||
| 1676 | |||
| 1677 | let cmd_block = match self.cmd_block.as_deref_mut() { | ||
| 1678 | Some(x) => x, | ||
| 1679 | None => &mut CmdBlock::new(), | ||
| 1680 | }; | ||
| 1681 | |||
| 1682 | Self::cmd(common_cmd::set_block_length(64), false)?; // CMD16 | ||
| 1683 | Self::cmd(common_cmd::app_cmd(rca), false)?; // APP | ||
| 1684 | |||
| 1685 | let status = cmd_block; | ||
| 1686 | |||
| 1687 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 1688 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1689 | |||
| 1690 | let transfer = Self::prepare_datapath_read( | ||
| 1691 | &self.config, | ||
| 1692 | #[cfg(sdmmc_v1)] | ||
| 1693 | &mut self.dma, | ||
| 1694 | status.as_mut(), | ||
| 1695 | 64, | ||
| 1696 | 6, | ||
| 1697 | ); | ||
| 1698 | InterruptHandler::<T>::enable_interrupts(); | ||
| 1699 | Self::cmd(sd_cmd::sd_status(), true)?; | ||
| 1700 | |||
| 1701 | let res = Self::complete_datapath_transfer(true).await; | ||
| 1702 | |||
| 1703 | if res.is_ok() { | ||
| 1704 | on_drop.defuse(); | ||
| 1705 | Self::stop_datapath(); | ||
| 1706 | drop(transfer); | ||
| 1707 | |||
| 1708 | for byte in status.iter_mut() { | ||
| 1709 | *byte = u32::from_be(*byte); | ||
| 1710 | } | ||
| 1711 | card.status = status.0.into(); | ||
| 1712 | } | ||
| 1713 | res | ||
| 1714 | } | ||
| 1715 | |||
| 1716 | /// Initializes eMMC and sets the bus at the specified frequency. | ||
| 1717 | /// | ||
| 1718 | /// eMMC only. | ||
| 1719 | pub async fn init_emmc(&mut self, freq: Hertz) -> Result<(), Error> { | ||
| 1720 | let _scoped_block_stop = T::RCC_INFO.block_stop(); | ||
| 1721 | |||
| 1722 | self.init_internal(freq, SdmmcPeripheral::Emmc(Emmc::default())).await | ||
| 1723 | } | ||
| 1724 | |||
| 1725 | /// Gets the EXT_CSD register. | ||
| 1726 | /// | ||
| 1727 | /// eMMC only. | ||
| 1728 | async fn read_ext_csd(&mut self) -> Result<(), Error> { | ||
| 1729 | let card = self.card.as_mut().ok_or(Error::NoCard)?.get_emmc(); | ||
| 1730 | |||
| 1731 | // Note: cmd_block can't be used because ExtCSD is too long to fit. | ||
| 1732 | let mut data_block = DataBlock([0u8; 512]); | ||
| 1733 | |||
| 1734 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 1735 | let buffer = unsafe { &mut *((&mut data_block.0) as *mut [u8; 512] as *mut [u32; 128]) }; | ||
| 1736 | |||
| 1737 | Self::cmd(common_cmd::set_block_length(512), false).unwrap(); // CMD16 | ||
| 1738 | |||
| 1739 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 1740 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1741 | |||
| 1742 | let transfer = Self::prepare_datapath_read( | ||
| 1743 | &self.config, | ||
| 1744 | #[cfg(sdmmc_v1)] | ||
| 1745 | &mut self.dma, | ||
| 1746 | buffer, | ||
| 1747 | 512, | ||
| 1748 | 9, | ||
| 1749 | ); | ||
| 1750 | InterruptHandler::<T>::enable_interrupts(); | ||
| 1751 | Self::cmd(emmc_cmd::send_ext_csd(), true)?; | ||
| 1752 | |||
| 1753 | let res = Self::complete_datapath_transfer(true).await; | ||
| 1754 | |||
| 1755 | if res.is_ok() { | ||
| 1756 | on_drop.defuse(); | ||
| 1757 | Self::stop_datapath(); | ||
| 1758 | drop(transfer); | ||
| 1759 | |||
| 1760 | card.ext_csd = unsafe { core::mem::transmute::<_, [u32; 128]>(data_block.0) }.into(); | ||
| 1761 | } | ||
| 1762 | res | ||
| 1763 | } | ||
| 1764 | } | 1138 | } |
| 1765 | 1139 | ||
| 1766 | impl<'d, T: Instance> Drop for Sdmmc<'d, T> { | 1140 | impl<'d> Drop for Sdmmc<'d> { |
| 1767 | fn drop(&mut self) { | 1141 | fn drop(&mut self) { |
| 1768 | T::Interrupt::disable(); | 1142 | // T::Interrupt::disable(); |
| 1769 | Self::on_drop(); | 1143 | self.on_drop(); |
| 1770 | 1144 | ||
| 1771 | critical_section::with(|_| { | 1145 | critical_section::with(|_| { |
| 1772 | self.clk.set_as_disconnected(); | 1146 | self.clk.set_as_disconnected(); |
| @@ -1799,9 +1173,28 @@ impl<'d, T: Instance> Drop for Sdmmc<'d, T> { | |||
| 1799 | 1173 | ||
| 1800 | ////////////////////////////////////////////////////// | 1174 | ////////////////////////////////////////////////////// |
| 1801 | 1175 | ||
| 1176 | type Regs = RegBlock; | ||
| 1177 | |||
| 1178 | struct Info { | ||
| 1179 | regs: Regs, | ||
| 1180 | rcc: RccInfo, | ||
| 1181 | } | ||
| 1182 | |||
| 1183 | struct State { | ||
| 1184 | waker: AtomicWaker, | ||
| 1185 | } | ||
| 1186 | |||
| 1187 | impl State { | ||
| 1188 | const fn new() -> Self { | ||
| 1189 | Self { | ||
| 1190 | waker: AtomicWaker::new(), | ||
| 1191 | } | ||
| 1192 | } | ||
| 1193 | } | ||
| 1194 | |||
| 1802 | trait SealedInstance { | 1195 | trait SealedInstance { |
| 1803 | fn regs() -> RegBlock; | 1196 | fn info() -> &'static Info; |
| 1804 | fn state() -> &'static AtomicWaker; | 1197 | fn state() -> &'static State; |
| 1805 | } | 1198 | } |
| 1806 | 1199 | ||
| 1807 | /// SDMMC instance trait. | 1200 | /// SDMMC instance trait. |
| @@ -1828,13 +1221,17 @@ dma_trait!(SdmmcDma, Instance); | |||
| 1828 | foreach_peripheral!( | 1221 | foreach_peripheral!( |
| 1829 | (sdmmc, $inst:ident) => { | 1222 | (sdmmc, $inst:ident) => { |
| 1830 | impl SealedInstance for peripherals::$inst { | 1223 | impl SealedInstance for peripherals::$inst { |
| 1831 | fn regs() -> RegBlock { | 1224 | fn info() -> &'static Info { |
| 1832 | crate::pac::$inst | 1225 | static INFO: Info = Info { |
| 1226 | regs: unsafe { Regs::from_ptr(crate::pac::$inst.as_ptr()) }, | ||
| 1227 | rcc: crate::peripherals::$inst::RCC_INFO, | ||
| 1228 | }; | ||
| 1229 | &INFO | ||
| 1833 | } | 1230 | } |
| 1834 | 1231 | ||
| 1835 | fn state() -> &'static ::embassy_sync::waitqueue::AtomicWaker { | 1232 | fn state() -> &'static State { |
| 1836 | static WAKER: ::embassy_sync::waitqueue::AtomicWaker = ::embassy_sync::waitqueue::AtomicWaker::new(); | 1233 | static STATE: State = State::new(); |
| 1837 | &WAKER | 1234 | &STATE |
| 1838 | } | 1235 | } |
| 1839 | } | 1236 | } |
| 1840 | 1237 | ||
| @@ -1843,48 +1240,3 @@ foreach_peripheral!( | |||
| 1843 | } | 1240 | } |
| 1844 | }; | 1241 | }; |
| 1845 | ); | 1242 | ); |
| 1846 | |||
| 1847 | impl<'d, T: Instance> block_device_driver::BlockDevice<512> for Sdmmc<'d, T> { | ||
| 1848 | type Error = Error; | ||
| 1849 | type Align = aligned::A4; | ||
| 1850 | |||
| 1851 | async fn read( | ||
| 1852 | &mut self, | ||
| 1853 | block_address: u32, | ||
| 1854 | buf: &mut [aligned::Aligned<Self::Align, [u8; 512]>], | ||
| 1855 | ) -> Result<(), Self::Error> { | ||
| 1856 | let _scoped_block_stop = T::RCC_INFO.block_stop(); | ||
| 1857 | // TODO: I think block_address needs to be adjusted by the partition start offset | ||
| 1858 | if buf.len() == 1 { | ||
| 1859 | let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut crate::sdmmc::DataBlock) }; | ||
| 1860 | self.read_block(block_address, block).await?; | ||
| 1861 | } else { | ||
| 1862 | let blocks: &mut [DataBlock] = | ||
| 1863 | unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut DataBlock, buf.len()) }; | ||
| 1864 | self.read_blocks(block_address, blocks).await?; | ||
| 1865 | } | ||
| 1866 | Ok(()) | ||
| 1867 | } | ||
| 1868 | |||
| 1869 | async fn write( | ||
| 1870 | &mut self, | ||
| 1871 | block_address: u32, | ||
| 1872 | buf: &[aligned::Aligned<Self::Align, [u8; 512]>], | ||
| 1873 | ) -> Result<(), Self::Error> { | ||
| 1874 | let _scoped_block_stop = T::RCC_INFO.block_stop(); | ||
| 1875 | // TODO: I think block_address needs to be adjusted by the partition start offset | ||
| 1876 | if buf.len() == 1 { | ||
| 1877 | let block = unsafe { &*(&buf[0] as *const _ as *const crate::sdmmc::DataBlock) }; | ||
| 1878 | self.write_block(block_address, block).await?; | ||
| 1879 | } else { | ||
| 1880 | let blocks: &[DataBlock] = | ||
| 1881 | unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const DataBlock, buf.len()) }; | ||
| 1882 | self.write_blocks(block_address, blocks).await?; | ||
| 1883 | } | ||
| 1884 | Ok(()) | ||
| 1885 | } | ||
| 1886 | |||
| 1887 | async fn size(&mut self) -> Result<u64, Self::Error> { | ||
| 1888 | Ok(self.card()?.size()) | ||
| 1889 | } | ||
| 1890 | } | ||
diff --git a/embassy-stm32/src/sdmmc/sd.rs b/embassy-stm32/src/sdmmc/sd.rs new file mode 100644 index 000000000..6190226b8 --- /dev/null +++ b/embassy-stm32/src/sdmmc/sd.rs | |||
| @@ -0,0 +1,693 @@ | |||
| 1 | use core::default::Default; | ||
| 2 | use core::ops::{Deref, DerefMut}; | ||
| 3 | |||
| 4 | use sdio_host::emmc::{EMMC, ExtCSD}; | ||
| 5 | use sdio_host::sd::{BusWidth, CIC, CID, CSD, CardCapacity, CardStatus, CurrentState, OCR, RCA, SCR, SD, SDStatus}; | ||
| 6 | use sdio_host::{common_cmd, emmc_cmd, sd_cmd}; | ||
| 7 | |||
| 8 | use crate::sdmmc::{BlockSize, Error, Sdmmc, Signalling, block_size, bus_width_vals, slice8_mut, slice8_ref}; | ||
| 9 | use crate::time::{Hertz, mhz}; | ||
| 10 | |||
| 11 | /// Aligned data block for SDMMC transfers. | ||
| 12 | /// | ||
| 13 | /// This is a 512-byte array, aligned to 4 bytes to satisfy DMA requirements. | ||
| 14 | #[repr(align(4))] | ||
| 15 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 16 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 17 | pub struct DataBlock(pub [u32; 128]); | ||
| 18 | |||
| 19 | impl DataBlock { | ||
| 20 | /// Create a new DataBlock | ||
| 21 | pub const fn new() -> Self { | ||
| 22 | DataBlock([0u32; 128]) | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | impl Deref for DataBlock { | ||
| 27 | type Target = [u8; 512]; | ||
| 28 | |||
| 29 | fn deref(&self) -> &Self::Target { | ||
| 30 | unwrap!(slice8_ref(&self.0[..]).try_into()) | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | impl DerefMut for DataBlock { | ||
| 35 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 36 | unwrap!(slice8_mut(&mut self.0[..]).try_into()) | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Command Block buffer for SDMMC command transfers. | ||
| 41 | /// | ||
| 42 | /// This is a 16-word array, exposed so that DMA commpatible memory can be used if required. | ||
| 43 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 44 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 45 | pub struct CmdBlock(pub [u32; 16]); | ||
| 46 | |||
| 47 | impl CmdBlock { | ||
| 48 | /// Creates a new instance of CmdBlock | ||
| 49 | pub const fn new() -> Self { | ||
| 50 | Self([0u32; 16]) | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | impl Deref for CmdBlock { | ||
| 55 | type Target = [u32; 16]; | ||
| 56 | |||
| 57 | fn deref(&self) -> &Self::Target { | ||
| 58 | &self.0 | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | impl DerefMut for CmdBlock { | ||
| 63 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 64 | &mut self.0 | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Represents either an SD or EMMC card | ||
| 69 | pub trait Addressable: Sized + Clone { | ||
| 70 | /// Associated type | ||
| 71 | type Ext; | ||
| 72 | |||
| 73 | /// Get this peripheral's address on the SDMMC bus | ||
| 74 | fn get_address(&self) -> u16; | ||
| 75 | |||
| 76 | /// Is this a standard or high capacity peripheral? | ||
| 77 | fn get_capacity(&self) -> CardCapacity; | ||
| 78 | |||
| 79 | /// Size in bytes | ||
| 80 | fn size(&self) -> u64; | ||
| 81 | } | ||
| 82 | |||
| 83 | /// Storage Device | ||
| 84 | pub struct StorageDevice<'a, 'b, T: Addressable> { | ||
| 85 | info: T, | ||
| 86 | /// Inner member | ||
| 87 | pub sdmmc: &'a mut Sdmmc<'b>, | ||
| 88 | } | ||
| 89 | |||
| 90 | /// Card Storage Device | ||
| 91 | impl<'a, 'b> StorageDevice<'a, 'b, Card> { | ||
| 92 | /// Create a new SD card | ||
| 93 | pub async fn new_sd_card(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<Self, Error> { | ||
| 94 | let mut s = Self { | ||
| 95 | info: Card::default(), | ||
| 96 | sdmmc, | ||
| 97 | }; | ||
| 98 | |||
| 99 | s.acquire(cmd_block, freq).await?; | ||
| 100 | |||
| 101 | Ok(s) | ||
| 102 | } | ||
| 103 | |||
| 104 | /// Initializes the card into a known state (or at least tries to). | ||
| 105 | async fn acquire(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> { | ||
| 106 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 107 | let regs = self.sdmmc.info.regs; | ||
| 108 | |||
| 109 | let _bus_width = match self.sdmmc.bus_width() { | ||
| 110 | BusWidth::Eight => return Err(Error::BusWidth), | ||
| 111 | bus_width => bus_width, | ||
| 112 | }; | ||
| 113 | |||
| 114 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 115 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 116 | self.sdmmc.init_idle()?; | ||
| 117 | |||
| 118 | // Check if cards supports CMD8 (with pattern) | ||
| 119 | self.sdmmc.cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; | ||
| 120 | let cic = CIC::from(regs.respr(0).read().cardstatus()); | ||
| 121 | |||
| 122 | if cic.pattern() != 0xAA { | ||
| 123 | return Err(Error::UnsupportedCardVersion); | ||
| 124 | } | ||
| 125 | |||
| 126 | if cic.voltage_accepted() & 1 == 0 { | ||
| 127 | return Err(Error::UnsupportedVoltage); | ||
| 128 | } | ||
| 129 | |||
| 130 | let ocr = loop { | ||
| 131 | // Signal that next command is a app command | ||
| 132 | self.sdmmc.cmd(common_cmd::app_cmd(0), false)?; // CMD55 | ||
| 133 | |||
| 134 | // 3.2-3.3V | ||
| 135 | let voltage_window = 1 << 5; | ||
| 136 | // Initialize card | ||
| 137 | match self | ||
| 138 | .sdmmc | ||
| 139 | .cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) | ||
| 140 | { | ||
| 141 | // ACMD41 | ||
| 142 | Ok(_) => (), | ||
| 143 | Err(Error::Crc) => (), | ||
| 144 | Err(err) => return Err(err), | ||
| 145 | } | ||
| 146 | |||
| 147 | let ocr: OCR<SD> = regs.respr(0).read().cardstatus().into(); | ||
| 148 | if !ocr.is_busy() { | ||
| 149 | // Power up done | ||
| 150 | break ocr; | ||
| 151 | } | ||
| 152 | }; | ||
| 153 | |||
| 154 | if ocr.high_capacity() { | ||
| 155 | // Card is SDHC or SDXC or SDUC | ||
| 156 | self.info.card_type = CardCapacity::HighCapacity; | ||
| 157 | } else { | ||
| 158 | self.info.card_type = CardCapacity::StandardCapacity; | ||
| 159 | } | ||
| 160 | self.info.ocr = ocr; | ||
| 161 | |||
| 162 | self.info.cid = self.sdmmc.get_cid()?.into(); | ||
| 163 | |||
| 164 | self.sdmmc.cmd(sd_cmd::send_relative_address(), false)?; | ||
| 165 | let rca = RCA::<SD>::from(regs.respr(0).read().cardstatus()); | ||
| 166 | self.info.rca = rca.address(); | ||
| 167 | |||
| 168 | self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into(); | ||
| 169 | self.sdmmc.select_card(Some(self.info.get_address()))?; | ||
| 170 | |||
| 171 | self.info.scr = self.get_scr(cmd_block).await?; | ||
| 172 | |||
| 173 | let (bus_width, acmd_arg) = if !self.info.scr.bus_width_four() { | ||
| 174 | (BusWidth::One, 0) | ||
| 175 | } else { | ||
| 176 | (BusWidth::Four, 2) | ||
| 177 | }; | ||
| 178 | |||
| 179 | self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), false)?; | ||
| 180 | self.sdmmc.cmd(sd_cmd::cmd6(acmd_arg), false)?; | ||
| 181 | |||
| 182 | self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?; | ||
| 183 | |||
| 184 | // Read status | ||
| 185 | self.info.status = self.read_sd_status(cmd_block).await?; | ||
| 186 | |||
| 187 | if freq > mhz(25) { | ||
| 188 | // Switch to SDR25 | ||
| 189 | self.sdmmc.signalling = self.switch_signalling_mode(cmd_block, Signalling::SDR25).await?; | ||
| 190 | |||
| 191 | if self.sdmmc.signalling == Signalling::SDR25 { | ||
| 192 | // Set final clock frequency | ||
| 193 | self.sdmmc.clkcr_set_clkdiv(freq, bus_width)?; | ||
| 194 | |||
| 195 | if self.sdmmc.read_status(&self.info)?.state() != CurrentState::Transfer { | ||
| 196 | return Err(Error::SignalingSwitchFailed); | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | // Read status after signalling change | ||
| 201 | self.read_sd_status(cmd_block).await?; | ||
| 202 | } | ||
| 203 | |||
| 204 | Ok(()) | ||
| 205 | } | ||
| 206 | |||
| 207 | /// Switch mode using CMD6. | ||
| 208 | /// | ||
| 209 | /// Attempt to set a new signalling mode. The selected | ||
| 210 | /// signalling mode is returned. Expects the current clock | ||
| 211 | /// frequency to be > 12.5MHz. | ||
| 212 | /// | ||
| 213 | /// SD only. | ||
| 214 | async fn switch_signalling_mode( | ||
| 215 | &self, | ||
| 216 | cmd_block: &mut CmdBlock, | ||
| 217 | signalling: Signalling, | ||
| 218 | ) -> Result<Signalling, Error> { | ||
| 219 | // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not | ||
| 220 | // necessary" | ||
| 221 | |||
| 222 | let set_function = 0x8000_0000 | ||
| 223 | | match signalling { | ||
| 224 | // See PLSS v7_10 Table 4-11 | ||
| 225 | Signalling::DDR50 => 0xFF_FF04, | ||
| 226 | Signalling::SDR104 => 0xFF_1F03, | ||
| 227 | Signalling::SDR50 => 0xFF_1F02, | ||
| 228 | Signalling::SDR25 => 0xFF_FF01, | ||
| 229 | Signalling::SDR12 => 0xFF_FF00, | ||
| 230 | }; | ||
| 231 | |||
| 232 | let buffer = &mut cmd_block.0[..64 / 4]; | ||
| 233 | |||
| 234 | let transfer = self | ||
| 235 | .sdmmc | ||
| 236 | .prepare_datapath_read(buffer, block_size(size_of_val(buffer)), false); | ||
| 237 | |||
| 238 | self.sdmmc.cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 | ||
| 239 | |||
| 240 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 241 | |||
| 242 | // Host is allowed to use the new functions at least 8 | ||
| 243 | // clocks after the end of the switch command | ||
| 244 | // transaction. We know the current clock period is < 80ns, | ||
| 245 | // so a total delay of 640ns is required here | ||
| 246 | for _ in 0..300 { | ||
| 247 | cortex_m::asm::nop(); | ||
| 248 | } | ||
| 249 | |||
| 250 | // Function Selection of Function Group 1 | ||
| 251 | let selection = (u32::from_be(cmd_block[4]) >> 24) & 0xF; | ||
| 252 | |||
| 253 | match selection { | ||
| 254 | 0 => Ok(Signalling::SDR12), | ||
| 255 | 1 => Ok(Signalling::SDR25), | ||
| 256 | 2 => Ok(Signalling::SDR50), | ||
| 257 | 3 => Ok(Signalling::SDR104), | ||
| 258 | 4 => Ok(Signalling::DDR50), | ||
| 259 | _ => Err(Error::UnsupportedCardType), | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | /// Reads the SCR register. | ||
| 264 | /// | ||
| 265 | /// SD only. | ||
| 266 | async fn get_scr(&self, cmd_block: &mut CmdBlock) -> Result<SCR, Error> { | ||
| 267 | // Read the 64-bit SCR register | ||
| 268 | self.sdmmc.cmd(common_cmd::set_block_length(8), false)?; // CMD16 | ||
| 269 | self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), false)?; | ||
| 270 | |||
| 271 | let scr = &mut cmd_block.0[..2]; | ||
| 272 | |||
| 273 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 274 | |||
| 275 | let transfer = self.sdmmc.prepare_datapath_read(scr, BlockSize::Size8, false); | ||
| 276 | self.sdmmc.cmd(sd_cmd::send_scr(), true)?; | ||
| 277 | |||
| 278 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 279 | |||
| 280 | Ok(SCR(u64::from_be_bytes(unwrap!(slice8_mut(scr).try_into())))) | ||
| 281 | } | ||
| 282 | |||
| 283 | /// Reads the SD Status (ACMD13) | ||
| 284 | /// | ||
| 285 | /// SD only. | ||
| 286 | async fn read_sd_status(&self, cmd_block: &mut CmdBlock) -> Result<SDStatus, Error> { | ||
| 287 | let rca = self.info.rca; | ||
| 288 | |||
| 289 | self.sdmmc.cmd(common_cmd::set_block_length(64), false)?; // CMD16 | ||
| 290 | self.sdmmc.cmd(common_cmd::app_cmd(rca), false)?; // APP | ||
| 291 | |||
| 292 | let buffer = &mut cmd_block.as_mut()[..64 / 4]; | ||
| 293 | |||
| 294 | let transfer = self | ||
| 295 | .sdmmc | ||
| 296 | .prepare_datapath_read(buffer, block_size(size_of_val(buffer)), false); | ||
| 297 | self.sdmmc.cmd(sd_cmd::sd_status(), true)?; | ||
| 298 | |||
| 299 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 300 | |||
| 301 | for byte in cmd_block.iter_mut() { | ||
| 302 | *byte = u32::from_be(*byte); | ||
| 303 | } | ||
| 304 | |||
| 305 | Ok(cmd_block.0.into()) | ||
| 306 | } | ||
| 307 | } | ||
| 308 | |||
| 309 | /// Emmc storage device | ||
| 310 | impl<'a, 'b> StorageDevice<'a, 'b, Emmc> { | ||
| 311 | /// Create a new EMMC card | ||
| 312 | pub async fn new_emmc(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<Self, Error> { | ||
| 313 | let mut s = Self { | ||
| 314 | info: Emmc::default(), | ||
| 315 | sdmmc, | ||
| 316 | }; | ||
| 317 | |||
| 318 | s.acquire(cmd_block, freq).await?; | ||
| 319 | |||
| 320 | Ok(s) | ||
| 321 | } | ||
| 322 | |||
| 323 | async fn acquire(&mut self, _cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> { | ||
| 324 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 325 | let regs = self.sdmmc.info.regs; | ||
| 326 | |||
| 327 | let bus_width = self.sdmmc.bus_width(); | ||
| 328 | |||
| 329 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 330 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 331 | self.sdmmc.init_idle()?; | ||
| 332 | |||
| 333 | let ocr = loop { | ||
| 334 | let high_voltage = 0b0 << 7; | ||
| 335 | let access_mode = 0b10 << 29; | ||
| 336 | let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15; | ||
| 337 | // Initialize card | ||
| 338 | match self.sdmmc.cmd(emmc_cmd::send_op_cond(op_cond), false) { | ||
| 339 | Ok(_) => (), | ||
| 340 | Err(Error::Crc) => (), | ||
| 341 | Err(err) => return Err(err), | ||
| 342 | } | ||
| 343 | let ocr: OCR<EMMC> = regs.respr(0).read().cardstatus().into(); | ||
| 344 | if !ocr.is_busy() { | ||
| 345 | // Power up done | ||
| 346 | break ocr; | ||
| 347 | } | ||
| 348 | }; | ||
| 349 | |||
| 350 | self.info.capacity = if ocr.access_mode() == 0b10 { | ||
| 351 | // Card is SDHC or SDXC or SDUC | ||
| 352 | CardCapacity::HighCapacity | ||
| 353 | } else { | ||
| 354 | CardCapacity::StandardCapacity | ||
| 355 | }; | ||
| 356 | self.info.ocr = ocr; | ||
| 357 | |||
| 358 | self.info.cid = self.sdmmc.get_cid()?.into(); | ||
| 359 | |||
| 360 | self.info.rca = 1u16.into(); | ||
| 361 | self.sdmmc | ||
| 362 | .cmd(emmc_cmd::assign_relative_address(self.info.rca), false)?; | ||
| 363 | |||
| 364 | self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into(); | ||
| 365 | self.sdmmc.select_card(Some(self.info.get_address()))?; | ||
| 366 | |||
| 367 | let (widbus, _) = bus_width_vals(bus_width); | ||
| 368 | |||
| 369 | // Write bus width to ExtCSD byte 183 | ||
| 370 | self.sdmmc.cmd( | ||
| 371 | emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus), | ||
| 372 | false, | ||
| 373 | )?; | ||
| 374 | |||
| 375 | // Wait for ready after R1b response | ||
| 376 | loop { | ||
| 377 | let status = self.sdmmc.read_status(&self.info)?; | ||
| 378 | |||
| 379 | if status.ready_for_data() { | ||
| 380 | break; | ||
| 381 | } | ||
| 382 | } | ||
| 383 | |||
| 384 | self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?; | ||
| 385 | self.info.ext_csd = self.read_ext_csd().await?; | ||
| 386 | |||
| 387 | Ok(()) | ||
| 388 | } | ||
| 389 | |||
| 390 | /// Gets the EXT_CSD register. | ||
| 391 | /// | ||
| 392 | /// eMMC only. | ||
| 393 | async fn read_ext_csd(&self) -> Result<ExtCSD, Error> { | ||
| 394 | // Note: cmd_block can't be used because ExtCSD is too long to fit. | ||
| 395 | let mut data_block = DataBlock::new(); | ||
| 396 | |||
| 397 | self.sdmmc | ||
| 398 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false) | ||
| 399 | .unwrap(); // CMD16 | ||
| 400 | |||
| 401 | let transfer = self | ||
| 402 | .sdmmc | ||
| 403 | .prepare_datapath_read(&mut data_block.0, block_size(size_of::<DataBlock>()), false); | ||
| 404 | self.sdmmc.cmd(emmc_cmd::send_ext_csd(), true)?; | ||
| 405 | |||
| 406 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 407 | |||
| 408 | Ok(data_block.0.into()) | ||
| 409 | } | ||
| 410 | } | ||
| 411 | |||
| 412 | /// Card or Emmc storage device | ||
| 413 | impl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> { | ||
| 414 | /// Write a block | ||
| 415 | pub fn card(&self) -> A { | ||
| 416 | self.info.clone() | ||
| 417 | } | ||
| 418 | |||
| 419 | /// Read a data block. | ||
| 420 | #[inline] | ||
| 421 | pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { | ||
| 422 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 423 | let card_capacity = self.info.get_capacity(); | ||
| 424 | |||
| 425 | // Always read 1 block of 512 bytes | ||
| 426 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 427 | let address = match card_capacity { | ||
| 428 | CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32, | ||
| 429 | _ => block_idx, | ||
| 430 | }; | ||
| 431 | self.sdmmc | ||
| 432 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16 | ||
| 433 | |||
| 434 | let transfer = self | ||
| 435 | .sdmmc | ||
| 436 | .prepare_datapath_read(&mut buffer.0, block_size(size_of::<DataBlock>()), false); | ||
| 437 | self.sdmmc.cmd(common_cmd::read_single_block(address), true)?; | ||
| 438 | |||
| 439 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 440 | |||
| 441 | Ok(()) | ||
| 442 | } | ||
| 443 | |||
| 444 | /// Read multiple data blocks. | ||
| 445 | #[inline] | ||
| 446 | pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { | ||
| 447 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 448 | let card_capacity = self.info.get_capacity(); | ||
| 449 | |||
| 450 | // NOTE(unsafe) reinterpret buffer as &mut [u32] | ||
| 451 | let buffer = unsafe { | ||
| 452 | core::slice::from_raw_parts_mut( | ||
| 453 | blocks.as_mut_ptr() as *mut u32, | ||
| 454 | blocks.len() * size_of::<DataBlock>() / size_of::<u32>(), | ||
| 455 | ) | ||
| 456 | }; | ||
| 457 | |||
| 458 | // Always read 1 block of 512 bytes | ||
| 459 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 460 | let address = match card_capacity { | ||
| 461 | CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32, | ||
| 462 | _ => block_idx, | ||
| 463 | }; | ||
| 464 | self.sdmmc | ||
| 465 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16 | ||
| 466 | |||
| 467 | let transfer = self | ||
| 468 | .sdmmc | ||
| 469 | .prepare_datapath_read(buffer, block_size(size_of::<DataBlock>()), false); | ||
| 470 | self.sdmmc.cmd(common_cmd::read_multiple_blocks(address), true)?; | ||
| 471 | |||
| 472 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 473 | |||
| 474 | self.sdmmc.cmd(common_cmd::stop_transmission(), false)?; // CMD12 | ||
| 475 | self.sdmmc.clear_interrupt_flags(); | ||
| 476 | |||
| 477 | Ok(()) | ||
| 478 | } | ||
| 479 | |||
| 480 | /// Write a data block. | ||
| 481 | pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> | ||
| 482 | where | ||
| 483 | CardStatus<A::Ext>: From<u32>, | ||
| 484 | { | ||
| 485 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 486 | |||
| 487 | // Always read 1 block of 512 bytes | ||
| 488 | // cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 489 | let address = match self.info.get_capacity() { | ||
| 490 | CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32, | ||
| 491 | _ => block_idx, | ||
| 492 | }; | ||
| 493 | self.sdmmc | ||
| 494 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16 | ||
| 495 | |||
| 496 | // sdmmc_v1 uses different cmd/dma order than v2, but only for writes | ||
| 497 | #[cfg(sdmmc_v1)] | ||
| 498 | self.sdmmc.cmd(common_cmd::write_single_block(address), true)?; | ||
| 499 | |||
| 500 | let transfer = self | ||
| 501 | .sdmmc | ||
| 502 | .prepare_datapath_write(&buffer.0, block_size(size_of::<DataBlock>()), false); | ||
| 503 | |||
| 504 | #[cfg(sdmmc_v2)] | ||
| 505 | self.sdmmc.cmd(common_cmd::write_single_block(address), true)?; | ||
| 506 | |||
| 507 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 508 | |||
| 509 | // TODO: Make this configurable | ||
| 510 | let mut timeout: u32 = 0x00FF_FFFF; | ||
| 511 | |||
| 512 | while timeout > 0 { | ||
| 513 | let ready_for_data = self.sdmmc.read_status(&self.info)?.ready_for_data(); | ||
| 514 | if ready_for_data { | ||
| 515 | return Ok(()); | ||
| 516 | } | ||
| 517 | timeout -= 1; | ||
| 518 | } | ||
| 519 | |||
| 520 | Err(Error::SoftwareTimeout) | ||
| 521 | } | ||
| 522 | |||
| 523 | /// Write multiple data blocks. | ||
| 524 | pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error> | ||
| 525 | where | ||
| 526 | CardStatus<A::Ext>: From<u32>, | ||
| 527 | { | ||
| 528 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 529 | |||
| 530 | // NOTE(unsafe) reinterpret buffer as &[u32] | ||
| 531 | let buffer = unsafe { | ||
| 532 | core::slice::from_raw_parts( | ||
| 533 | blocks.as_ptr() as *const u32, | ||
| 534 | blocks.len() * size_of::<DataBlock>() / size_of::<u32>(), | ||
| 535 | ) | ||
| 536 | }; | ||
| 537 | // Always read 1 block of 512 bytes | ||
| 538 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 539 | let address = match self.info.get_capacity() { | ||
| 540 | CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32, | ||
| 541 | _ => block_idx, | ||
| 542 | }; | ||
| 543 | |||
| 544 | self.sdmmc | ||
| 545 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16 | ||
| 546 | |||
| 547 | #[cfg(sdmmc_v1)] | ||
| 548 | self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 | ||
| 549 | |||
| 550 | // Setup write command | ||
| 551 | let transfer = self | ||
| 552 | .sdmmc | ||
| 553 | .prepare_datapath_write(buffer, block_size(size_of::<DataBlock>()), false); | ||
| 554 | |||
| 555 | #[cfg(sdmmc_v2)] | ||
| 556 | self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 | ||
| 557 | |||
| 558 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 559 | |||
| 560 | self.sdmmc.cmd(common_cmd::stop_transmission(), false)?; // CMD12 | ||
| 561 | self.sdmmc.clear_interrupt_flags(); | ||
| 562 | |||
| 563 | // TODO: Make this configurable | ||
| 564 | let mut timeout: u32 = 0x00FF_FFFF; | ||
| 565 | |||
| 566 | while timeout > 0 { | ||
| 567 | let ready_for_data = self.sdmmc.read_status(&self.info)?.ready_for_data(); | ||
| 568 | |||
| 569 | if ready_for_data { | ||
| 570 | return Ok(()); | ||
| 571 | } | ||
| 572 | timeout -= 1; | ||
| 573 | } | ||
| 574 | Err(Error::SoftwareTimeout) | ||
| 575 | } | ||
| 576 | } | ||
| 577 | |||
| 578 | #[derive(Clone, Copy, Debug, Default)] | ||
| 579 | /// SD Card | ||
| 580 | pub struct Card { | ||
| 581 | /// The type of this card | ||
| 582 | pub card_type: CardCapacity, | ||
| 583 | /// Operation Conditions Register | ||
| 584 | pub ocr: OCR<SD>, | ||
| 585 | /// Relative Card Address | ||
| 586 | pub rca: u16, | ||
| 587 | /// Card ID | ||
| 588 | pub cid: CID<SD>, | ||
| 589 | /// Card Specific Data | ||
| 590 | pub csd: CSD<SD>, | ||
| 591 | /// SD CARD Configuration Register | ||
| 592 | pub scr: SCR, | ||
| 593 | /// SD Status | ||
| 594 | pub status: SDStatus, | ||
| 595 | } | ||
| 596 | |||
| 597 | impl Addressable for Card { | ||
| 598 | type Ext = SD; | ||
| 599 | |||
| 600 | /// Get this peripheral's address on the SDMMC bus | ||
| 601 | fn get_address(&self) -> u16 { | ||
| 602 | self.rca | ||
| 603 | } | ||
| 604 | |||
| 605 | /// Is this a standard or high capacity peripheral? | ||
| 606 | fn get_capacity(&self) -> CardCapacity { | ||
| 607 | self.card_type | ||
| 608 | } | ||
| 609 | |||
| 610 | /// Size in bytes | ||
| 611 | fn size(&self) -> u64 { | ||
| 612 | u64::from(self.csd.block_count()) * 512 | ||
| 613 | } | ||
| 614 | } | ||
| 615 | |||
| 616 | #[derive(Clone, Copy, Debug, Default)] | ||
| 617 | /// eMMC storage | ||
| 618 | pub struct Emmc { | ||
| 619 | /// The capacity of this card | ||
| 620 | pub capacity: CardCapacity, | ||
| 621 | /// Operation Conditions Register | ||
| 622 | pub ocr: OCR<EMMC>, | ||
| 623 | /// Relative Card Address | ||
| 624 | pub rca: u16, | ||
| 625 | /// Card ID | ||
| 626 | pub cid: CID<EMMC>, | ||
| 627 | /// Card Specific Data | ||
| 628 | pub csd: CSD<EMMC>, | ||
| 629 | /// Extended Card Specific Data | ||
| 630 | pub ext_csd: ExtCSD, | ||
| 631 | } | ||
| 632 | |||
| 633 | impl Addressable for Emmc { | ||
| 634 | type Ext = EMMC; | ||
| 635 | |||
| 636 | /// Get this peripheral's address on the SDMMC bus | ||
| 637 | fn get_address(&self) -> u16 { | ||
| 638 | self.rca | ||
| 639 | } | ||
| 640 | |||
| 641 | /// Is this a standard or high capacity peripheral? | ||
| 642 | fn get_capacity(&self) -> CardCapacity { | ||
| 643 | self.capacity | ||
| 644 | } | ||
| 645 | |||
| 646 | /// Size in bytes | ||
| 647 | fn size(&self) -> u64 { | ||
| 648 | u64::from(self.ext_csd.sector_count()) * 512 | ||
| 649 | } | ||
| 650 | } | ||
| 651 | |||
| 652 | impl<'d, 'e, A: Addressable> block_device_driver::BlockDevice<512> for StorageDevice<'d, 'e, A> { | ||
| 653 | type Error = Error; | ||
| 654 | type Align = aligned::A4; | ||
| 655 | |||
| 656 | async fn read( | ||
| 657 | &mut self, | ||
| 658 | block_address: u32, | ||
| 659 | buf: &mut [aligned::Aligned<Self::Align, [u8; 512]>], | ||
| 660 | ) -> Result<(), Self::Error> { | ||
| 661 | // TODO: I think block_address needs to be adjusted by the partition start offset | ||
| 662 | if buf.len() == 1 { | ||
| 663 | let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut DataBlock) }; | ||
| 664 | self.read_block(block_address, block).await?; | ||
| 665 | } else { | ||
| 666 | let blocks: &mut [DataBlock] = | ||
| 667 | unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut DataBlock, buf.len()) }; | ||
| 668 | self.read_blocks(block_address, blocks).await?; | ||
| 669 | } | ||
| 670 | Ok(()) | ||
| 671 | } | ||
| 672 | |||
| 673 | async fn write( | ||
| 674 | &mut self, | ||
| 675 | block_address: u32, | ||
| 676 | buf: &[aligned::Aligned<Self::Align, [u8; 512]>], | ||
| 677 | ) -> Result<(), Self::Error> { | ||
| 678 | // TODO: I think block_address needs to be adjusted by the partition start offset | ||
| 679 | if buf.len() == 1 { | ||
| 680 | let block = unsafe { &*(&buf[0] as *const _ as *const DataBlock) }; | ||
| 681 | self.write_block(block_address, block).await?; | ||
| 682 | } else { | ||
| 683 | let blocks: &[DataBlock] = | ||
| 684 | unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const DataBlock, buf.len()) }; | ||
| 685 | self.write_blocks(block_address, blocks).await?; | ||
| 686 | } | ||
| 687 | Ok(()) | ||
| 688 | } | ||
| 689 | |||
| 690 | async fn size(&mut self) -> Result<u64, Self::Error> { | ||
| 691 | Ok(self.info.size()) | ||
| 692 | } | ||
| 693 | } | ||
diff --git a/embassy-stm32/src/sdmmc/sdio.rs b/embassy-stm32/src/sdmmc/sdio.rs new file mode 100644 index 000000000..1412b21fc --- /dev/null +++ b/embassy-stm32/src/sdmmc/sdio.rs | |||
| @@ -0,0 +1,177 @@ | |||
| 1 | use core::ops::{Deref, DerefMut}; | ||
| 2 | |||
| 3 | use sdio_host::common_cmd::{R1, Rz, cmd}; | ||
| 4 | use sdio_host::sd::BusWidth; | ||
| 5 | use sdio_host::sd_cmd; | ||
| 6 | |||
| 7 | use crate::sdmmc::{Error, Sdmmc, block_size, slice8_mut, slice8_ref}; | ||
| 8 | use crate::time::Hertz; | ||
| 9 | |||
| 10 | /// Aligned data block for SDMMC transfers. | ||
| 11 | /// | ||
| 12 | /// This is a 64-byte array, aligned to 4 bytes to satisfy DMA requirements. | ||
| 13 | #[repr(align(4))] | ||
| 14 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 15 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 16 | pub struct DataBlock(pub [u32; 16]); | ||
| 17 | |||
| 18 | impl DataBlock { | ||
| 19 | /// Create a new DataBlock | ||
| 20 | pub const fn new() -> Self { | ||
| 21 | DataBlock([0u32; 16]) | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | impl Deref for DataBlock { | ||
| 26 | type Target = [u8; 64]; | ||
| 27 | |||
| 28 | fn deref(&self) -> &Self::Target { | ||
| 29 | unwrap!(slice8_ref(&self.0[..]).try_into()) | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | impl DerefMut for DataBlock { | ||
| 34 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 35 | unwrap!(slice8_mut(&mut self.0[..]).try_into()) | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | /// Storage Device | ||
| 40 | pub struct SerialDataInterface<'a, 'b> { | ||
| 41 | /// Inner member | ||
| 42 | pub sdmmc: &'a mut Sdmmc<'b>, | ||
| 43 | } | ||
| 44 | |||
| 45 | /// Card Storage Device | ||
| 46 | impl<'a, 'b> SerialDataInterface<'a, 'b> { | ||
| 47 | /// Create a new SD card | ||
| 48 | pub async fn new(sdmmc: &'a mut Sdmmc<'b>, freq: Hertz) -> Result<Self, Error> { | ||
| 49 | let mut s = Self { sdmmc }; | ||
| 50 | |||
| 51 | s.acquire(freq).await?; | ||
| 52 | |||
| 53 | Ok(s) | ||
| 54 | } | ||
| 55 | |||
| 56 | /// Initializes the card into a known state (or at least tries to). | ||
| 57 | async fn acquire(&mut self, _freq: Hertz) -> Result<(), Error> { | ||
| 58 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 59 | |||
| 60 | let _bus_width = match self.sdmmc.bus_width() { | ||
| 61 | BusWidth::Eight => return Err(Error::BusWidth), | ||
| 62 | bus_width => bus_width, | ||
| 63 | }; | ||
| 64 | |||
| 65 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 66 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 67 | self.sdmmc.init_idle()?; | ||
| 68 | |||
| 69 | self.sdmmc.cmd(cmd::<Rz>(5, 0), false)?; | ||
| 70 | |||
| 71 | // Get RCA | ||
| 72 | let rca = self.sdmmc.cmd(sd_cmd::send_relative_address(), false)?; | ||
| 73 | |||
| 74 | // Select the card with RCA | ||
| 75 | self.sdmmc.select_card(Some(rca.try_into().unwrap()))?; | ||
| 76 | |||
| 77 | Ok(()) | ||
| 78 | } | ||
| 79 | |||
| 80 | /// Set the bus to the 4-bit high-speed frequency | ||
| 81 | pub fn set_bus_to_high_speed(&mut self, frequency: Hertz) -> Result<(), Error> { | ||
| 82 | self.sdmmc.clkcr_set_clkdiv(frequency, BusWidth::Four)?; | ||
| 83 | |||
| 84 | Ok(()) | ||
| 85 | } | ||
| 86 | |||
| 87 | /// Run cmd52 | ||
| 88 | pub async fn cmd52(&mut self, arg: u32) -> Result<u32, Error> { | ||
| 89 | self.sdmmc.cmd(cmd::<R1>(52, arg), false) | ||
| 90 | } | ||
| 91 | |||
| 92 | /// Read in block mode using cmd53 | ||
| 93 | pub async fn cmd53_block_read(&mut self, arg: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { | ||
| 94 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 95 | |||
| 96 | // NOTE(unsafe) reinterpret buffer as &mut [u32] | ||
| 97 | let buffer = unsafe { | ||
| 98 | core::slice::from_raw_parts_mut( | ||
| 99 | blocks.as_mut_ptr() as *mut u32, | ||
| 100 | blocks.len() * size_of::<DataBlock>() / size_of::<u32>(), | ||
| 101 | ) | ||
| 102 | }; | ||
| 103 | |||
| 104 | let transfer = self | ||
| 105 | .sdmmc | ||
| 106 | .prepare_datapath_read(buffer, block_size(size_of::<DataBlock>()), false); | ||
| 107 | self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; | ||
| 108 | |||
| 109 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 110 | self.sdmmc.clear_interrupt_flags(); | ||
| 111 | |||
| 112 | Ok(()) | ||
| 113 | } | ||
| 114 | |||
| 115 | /// Read in multibyte mode using cmd53 | ||
| 116 | pub async fn cmd53_byte_read(&mut self, arg: u32, buffer: &mut [u32]) -> Result<(), Error> { | ||
| 117 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 118 | |||
| 119 | let transfer = self | ||
| 120 | .sdmmc | ||
| 121 | .prepare_datapath_read(buffer, block_size(size_of::<DataBlock>()), true); | ||
| 122 | self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; | ||
| 123 | |||
| 124 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 125 | self.sdmmc.clear_interrupt_flags(); | ||
| 126 | |||
| 127 | Ok(()) | ||
| 128 | } | ||
| 129 | |||
| 130 | /// Write in block mode using cmd53 | ||
| 131 | pub async fn cmd53_block_write(&mut self, arg: u32, blocks: &[DataBlock]) -> Result<(), Error> { | ||
| 132 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 133 | |||
| 134 | // NOTE(unsafe) reinterpret buffer as &mut [u32] | ||
| 135 | let buffer = unsafe { | ||
| 136 | core::slice::from_raw_parts_mut( | ||
| 137 | blocks.as_ptr() as *mut u32, | ||
| 138 | blocks.len() * size_of::<DataBlock>() / size_of::<u32>(), | ||
| 139 | ) | ||
| 140 | }; | ||
| 141 | |||
| 142 | #[cfg(sdmmc_v1)] | ||
| 143 | self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; | ||
| 144 | |||
| 145 | let transfer = self | ||
| 146 | .sdmmc | ||
| 147 | .prepare_datapath_read(buffer, block_size(size_of::<DataBlock>()), false); | ||
| 148 | |||
| 149 | #[cfg(sdmmc_v2)] | ||
| 150 | self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; | ||
| 151 | |||
| 152 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 153 | self.sdmmc.clear_interrupt_flags(); | ||
| 154 | |||
| 155 | Ok(()) | ||
| 156 | } | ||
| 157 | |||
| 158 | /// Write in multibyte mode using cmd53 | ||
| 159 | pub async fn cmd53_byte_write(&mut self, arg: u32, buffer: &[u32]) -> Result<(), Error> { | ||
| 160 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 161 | |||
| 162 | #[cfg(sdmmc_v1)] | ||
| 163 | self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; | ||
| 164 | |||
| 165 | let transfer = self | ||
| 166 | .sdmmc | ||
| 167 | .prepare_datapath_write(buffer, block_size(size_of::<DataBlock>()), true); | ||
| 168 | |||
| 169 | #[cfg(sdmmc_v2)] | ||
| 170 | self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; | ||
| 171 | |||
| 172 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 173 | self.sdmmc.clear_interrupt_flags(); | ||
| 174 | |||
| 175 | Ok(()) | ||
| 176 | } | ||
| 177 | } | ||
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index c90e0cef4..af51b79b4 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs | |||
| @@ -64,6 +64,16 @@ pub enum Direction { | |||
| 64 | Receive, | 64 | Receive, |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | /// Slave Select (SS) pin polarity. | ||
| 68 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 69 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 70 | pub enum SlaveSelectPolarity { | ||
| 71 | /// SS active high | ||
| 72 | ActiveHigh, | ||
| 73 | /// SS active low | ||
| 74 | ActiveLow, | ||
| 75 | } | ||
| 76 | |||
| 67 | /// SPI configuration. | 77 | /// SPI configuration. |
| 68 | #[non_exhaustive] | 78 | #[non_exhaustive] |
| 69 | #[derive(Copy, Clone)] | 79 | #[derive(Copy, Clone)] |
| @@ -86,6 +96,9 @@ pub struct Config { | |||
| 86 | /// NSS output enabled (SSM = 0, SSOE = 1): The NSS signal is driven low when the master starts the communication and is kept low until the SPI is disabled. | 96 | /// NSS output enabled (SSM = 0, SSOE = 1): The NSS signal is driven low when the master starts the communication and is kept low until the SPI is disabled. |
| 87 | /// NSS output disabled (SSM = 0, SSOE = 0): For devices set as slave, the NSS pin acts as a classical NSS input: the slave is selected when NSS is low and deselected when NSS high. | 97 | /// NSS output disabled (SSM = 0, SSOE = 0): For devices set as slave, the NSS pin acts as a classical NSS input: the slave is selected when NSS is low and deselected when NSS high. |
| 88 | pub nss_output_disable: bool, | 98 | pub nss_output_disable: bool, |
| 99 | /// Slave Select (SS) pin polarity. | ||
| 100 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 101 | pub nss_polarity: SlaveSelectPolarity, | ||
| 89 | } | 102 | } |
| 90 | 103 | ||
| 91 | impl Default for Config { | 104 | impl Default for Config { |
| @@ -97,6 +110,8 @@ impl Default for Config { | |||
| 97 | miso_pull: Pull::None, | 110 | miso_pull: Pull::None, |
| 98 | gpio_speed: Speed::VeryHigh, | 111 | gpio_speed: Speed::VeryHigh, |
| 99 | nss_output_disable: false, | 112 | nss_output_disable: false, |
| 113 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 114 | nss_polarity: SlaveSelectPolarity::ActiveHigh, | ||
| 100 | } | 115 | } |
| 101 | } | 116 | } |
| 102 | } | 117 | } |
| @@ -123,6 +138,14 @@ impl Config { | |||
| 123 | } | 138 | } |
| 124 | } | 139 | } |
| 125 | 140 | ||
| 141 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 142 | fn raw_nss_polarity(&self) -> vals::Ssiop { | ||
| 143 | match self.nss_polarity { | ||
| 144 | SlaveSelectPolarity::ActiveHigh => vals::Ssiop::ACTIVE_HIGH, | ||
| 145 | SlaveSelectPolarity::ActiveLow => vals::Ssiop::ACTIVE_LOW, | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 126 | #[cfg(gpio_v1)] | 149 | #[cfg(gpio_v1)] |
| 127 | fn sck_af(&self) -> AfType { | 150 | fn sck_af(&self) -> AfType { |
| 128 | AfType::output(OutputType::PushPull, self.gpio_speed) | 151 | AfType::output(OutputType::PushPull, self.gpio_speed) |
| @@ -305,6 +328,7 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { | |||
| 305 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | 328 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 306 | { | 329 | { |
| 307 | let ssoe = CM::MASTER == vals::Master::MASTER && !config.nss_output_disable; | 330 | let ssoe = CM::MASTER == vals::Master::MASTER && !config.nss_output_disable; |
| 331 | let ssiop = config.raw_nss_polarity(); | ||
| 308 | regs.ifcr().write(|w| w.0 = 0xffff_ffff); | 332 | regs.ifcr().write(|w| w.0 = 0xffff_ffff); |
| 309 | regs.cfg2().modify(|w| { | 333 | regs.cfg2().modify(|w| { |
| 310 | w.set_ssoe(ssoe); | 334 | w.set_ssoe(ssoe); |
| @@ -318,7 +342,7 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { | |||
| 318 | w.set_midi(0); | 342 | w.set_midi(0); |
| 319 | w.set_mssi(0); | 343 | w.set_mssi(0); |
| 320 | w.set_afcntr(true); | 344 | w.set_afcntr(true); |
| 321 | w.set_ssiop(vals::Ssiop::ACTIVE_HIGH); | 345 | w.set_ssiop(ssiop); |
| 322 | }); | 346 | }); |
| 323 | regs.cfg1().modify(|w| { | 347 | regs.cfg1().modify(|w| { |
| 324 | w.set_crcen(false); | 348 | w.set_crcen(false); |
| @@ -366,6 +390,8 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { | |||
| 366 | 390 | ||
| 367 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | 391 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 368 | { | 392 | { |
| 393 | let ssiop = config.raw_nss_polarity(); | ||
| 394 | |||
| 369 | self.info.regs.cr1().modify(|w| { | 395 | self.info.regs.cr1().modify(|w| { |
| 370 | w.set_spe(false); | 396 | w.set_spe(false); |
| 371 | }); | 397 | }); |
| @@ -374,6 +400,7 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { | |||
| 374 | w.set_cpha(cpha); | 400 | w.set_cpha(cpha); |
| 375 | w.set_cpol(cpol); | 401 | w.set_cpol(cpol); |
| 376 | w.set_lsbfirst(lsbfirst); | 402 | w.set_lsbfirst(lsbfirst); |
| 403 | w.set_ssiop(ssiop); | ||
| 377 | }); | 404 | }); |
| 378 | self.info.regs.cfg1().modify(|w| { | 405 | self.info.regs.cfg1().modify(|w| { |
| 379 | w.set_mbr(br); | 406 | w.set_mbr(br); |
| @@ -446,6 +473,13 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { | |||
| 446 | // NSS output disabled if SSOE=0 or if SSM=1 software slave management enabled | 473 | // NSS output disabled if SSOE=0 or if SSM=1 software slave management enabled |
| 447 | let nss_output_disable = !ssoe || cfg.ssm(); | 474 | let nss_output_disable = !ssoe || cfg.ssm(); |
| 448 | 475 | ||
| 476 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 477 | let nss_polarity = if cfg.ssiop() == vals::Ssiop::ACTIVE_LOW { | ||
| 478 | SlaveSelectPolarity::ActiveLow | ||
| 479 | } else { | ||
| 480 | SlaveSelectPolarity::ActiveHigh | ||
| 481 | }; | ||
| 482 | |||
| 449 | Config { | 483 | Config { |
| 450 | mode: Mode { polarity, phase }, | 484 | mode: Mode { polarity, phase }, |
| 451 | bit_order, | 485 | bit_order, |
| @@ -453,6 +487,8 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { | |||
| 453 | miso_pull, | 487 | miso_pull, |
| 454 | gpio_speed: self.gpio_speed, | 488 | gpio_speed: self.gpio_speed, |
| 455 | nss_output_disable, | 489 | nss_output_disable, |
| 490 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 491 | nss_polarity, | ||
| 456 | } | 492 | } |
| 457 | } | 493 | } |
| 458 | 494 | ||
| @@ -769,7 +805,7 @@ impl<'d> Spi<'d, Async, Master> { | |||
| 769 | ) | 805 | ) |
| 770 | } | 806 | } |
| 771 | 807 | ||
| 772 | /// Create a new SPI driver, in bidirectional mode, specifically in tranmit mode | 808 | /// Create a new SPI driver, in bidirectional mode, specifically in tranmit mode |
| 773 | #[cfg(any(spi_v1, spi_v2, spi_v3))] | 809 | #[cfg(any(spi_v1, spi_v2, spi_v3))] |
| 774 | pub fn new_bidi<T: Instance, #[cfg(afio)] A>( | 810 | pub fn new_bidi<T: Instance, #[cfg(afio)] A>( |
| 775 | peri: Peri<'d, T>, | 811 | peri: Peri<'d, T>, |
diff --git a/embassy-stm32/src/time.rs b/embassy-stm32/src/time.rs index 532877f70..88a28ee3d 100644 --- a/embassy-stm32/src/time.rs +++ b/embassy-stm32/src/time.rs | |||
| @@ -4,7 +4,7 @@ use core::fmt::Display; | |||
| 4 | use core::ops::{Div, Mul}; | 4 | use core::ops::{Div, Mul}; |
| 5 | 5 | ||
| 6 | /// Hertz | 6 | /// Hertz |
| 7 | #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)] | 7 | #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug, Default)] |
| 8 | pub struct Hertz(pub u32); | 8 | pub struct Hertz(pub u32); |
| 9 | 9 | ||
| 10 | impl Display for Hertz { | 10 | impl Display for Hertz { |
diff --git a/examples/mcxa/src/bin/adc_interrupt.rs b/examples/mcxa/src/bin/adc_interrupt.rs index c88b1fe8d..d2cda631c 100644 --- a/examples/mcxa/src/bin/adc_interrupt.rs +++ b/examples/mcxa/src/bin/adc_interrupt.rs | |||
| @@ -2,34 +2,31 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use embassy_executor::Spawner; | 4 | use embassy_executor::Spawner; |
| 5 | use embassy_mcxa_examples::init_adc_pins; | 5 | use embassy_time::{Duration, Ticker}; |
| 6 | use hal::adc::{LpadcConfig, TriggerPriorityPolicy}; | 6 | use hal::adc::{Adc, InterruptHandler, LpadcConfig, TriggerPriorityPolicy}; |
| 7 | use hal::bind_interrupts; | ||
| 7 | use hal::clocks::PoweredClock; | 8 | use hal::clocks::PoweredClock; |
| 9 | use hal::clocks::config::Div8; | ||
| 8 | use hal::clocks::periph_helpers::{AdcClockSel, Div4}; | 10 | use hal::clocks::periph_helpers::{AdcClockSel, Div4}; |
| 11 | use hal::config::Config; | ||
| 9 | use hal::pac::adc1::cfg::{Pwrsel, Refsel}; | 12 | use hal::pac::adc1::cfg::{Pwrsel, Refsel}; |
| 10 | use hal::pac::adc1::cmdl1::{Adch, Mode}; | 13 | use hal::pac::adc1::cmdl1::Mode; |
| 11 | use hal::pac::adc1::ctrl::CalAvgs; | 14 | use hal::pac::adc1::ctrl::CalAvgs; |
| 12 | use hal::pac::adc1::tctrl::Tcmd; | 15 | use hal::peripherals::ADC1; |
| 13 | use hal::{InterruptExt, bind_interrupts}; | ||
| 14 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 16 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| 15 | 17 | ||
| 16 | bind_interrupts!(struct Irqs { | 18 | bind_interrupts!(struct Irqs { |
| 17 | ADC1 => hal::adc::AdcHandler; | 19 | ADC1 => InterruptHandler<ADC1>; |
| 18 | }); | 20 | }); |
| 19 | 21 | ||
| 20 | #[used] | ||
| 21 | #[no_mangle] | ||
| 22 | static KEEP_ADC: unsafe extern "C" fn() = ADC1; | ||
| 23 | |||
| 24 | #[embassy_executor::main] | 22 | #[embassy_executor::main] |
| 25 | async fn main(_spawner: Spawner) { | 23 | async fn main(_spawner: Spawner) { |
| 26 | let p = hal::init(hal::config::Config::default()); | 24 | let mut config = Config::default(); |
| 25 | config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); | ||
| 27 | 26 | ||
| 28 | defmt::info!("ADC interrupt Example"); | 27 | let p = hal::init(config); |
| 29 | 28 | ||
| 30 | unsafe { | 29 | defmt::info!("ADC interrupt Example"); |
| 31 | init_adc_pins(); | ||
| 32 | } | ||
| 33 | 30 | ||
| 34 | let adc_config = LpadcConfig { | 31 | let adc_config = LpadcConfig { |
| 35 | enable_in_doze_mode: true, | 32 | enable_in_doze_mode: true, |
| @@ -41,44 +38,28 @@ async fn main(_spawner: Spawner) { | |||
| 41 | trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, | 38 | trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, |
| 42 | enable_conv_pause: false, | 39 | enable_conv_pause: false, |
| 43 | conv_pause_delay: 0, | 40 | conv_pause_delay: 0, |
| 44 | fifo_watermark: 0, | ||
| 45 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | 41 | power: PoweredClock::NormalEnabledDeepSleepDisabled, |
| 46 | source: AdcClockSel::FroLfDiv, | 42 | source: AdcClockSel::FroLfDiv, |
| 47 | div: Div4::no_div(), | 43 | div: Div4::no_div(), |
| 48 | }; | 44 | }; |
| 49 | let adc = hal::adc::Adc::<hal::adc::Adc1>::new(p.ADC1, adc_config); | 45 | let mut adc = Adc::new_async(p.ADC1, p.P1_10, Irqs, adc_config).unwrap(); |
| 50 | 46 | ||
| 51 | adc.do_offset_calibration(); | 47 | adc.do_offset_calibration(); |
| 52 | adc.do_auto_calibration(); | 48 | adc.do_auto_calibration(); |
| 53 | 49 | adc.set_resolution(Mode::Data16Bits); | |
| 54 | let mut conv_command_config = adc.get_default_conv_command_config(); | ||
| 55 | conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; | ||
| 56 | conv_command_config.conversion_resolution_mode = Mode::Data16Bits; | ||
| 57 | adc.set_conv_command_config(1, &conv_command_config); | ||
| 58 | |||
| 59 | let mut conv_trigger_config = adc.get_default_conv_trigger_config(); | ||
| 60 | conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; | ||
| 61 | conv_trigger_config.enable_hardware_trigger = false; | ||
| 62 | adc.set_conv_trigger_config(0, &conv_trigger_config); | ||
| 63 | 50 | ||
| 64 | defmt::info!("ADC configuration done..."); | 51 | defmt::info!("ADC configuration done..."); |
| 65 | 52 | let mut ticker = Ticker::every(Duration::from_millis(100)); | |
| 66 | adc.enable_interrupt(0x1); | ||
| 67 | |||
| 68 | unsafe { | ||
| 69 | hal::interrupt::ADC1.enable(); | ||
| 70 | } | ||
| 71 | |||
| 72 | unsafe { | ||
| 73 | cortex_m::interrupt::enable(); | ||
| 74 | } | ||
| 75 | 53 | ||
| 76 | loop { | 54 | loop { |
| 77 | adc.do_software_trigger(1); | 55 | ticker.next().await; |
| 78 | while !adc.is_interrupt_triggered() { | 56 | match adc.read().await { |
| 79 | // Wait until the interrupt is triggered | 57 | Ok(value) => { |
| 58 | defmt::info!("ADC value: {}", value); | ||
| 59 | } | ||
| 60 | Err(e) => { | ||
| 61 | defmt::error!("ADC read error: {:?}", e); | ||
| 62 | } | ||
| 80 | } | 63 | } |
| 81 | defmt::info!("*** ADC interrupt TRIGGERED! ***"); | ||
| 82 | //TBD need to print the value | ||
| 83 | } | 64 | } |
| 84 | } | 65 | } |
diff --git a/examples/mcxa/src/bin/adc_polling.rs b/examples/mcxa/src/bin/adc_polling.rs index 07c50f224..5c4d5524c 100644 --- a/examples/mcxa/src/bin/adc_polling.rs +++ b/examples/mcxa/src/bin/adc_polling.rs | |||
| @@ -2,12 +2,15 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use embassy_executor::Spawner; | 4 | use embassy_executor::Spawner; |
| 5 | use embassy_mcxa_examples::init_adc_pins; | 5 | use embassy_mcxa::adc::{ConvCommandConfig, ConvTriggerConfig}; |
| 6 | use hal::adc::{ConvResult, LpadcConfig, TriggerPriorityPolicy}; | 6 | use embassy_time::{Duration, Ticker}; |
| 7 | use hal::adc::{Adc, LpadcConfig, TriggerPriorityPolicy}; | ||
| 7 | use hal::clocks::PoweredClock; | 8 | use hal::clocks::PoweredClock; |
| 9 | use hal::clocks::config::Div8; | ||
| 8 | use hal::clocks::periph_helpers::{AdcClockSel, Div4}; | 10 | use hal::clocks::periph_helpers::{AdcClockSel, Div4}; |
| 11 | use hal::config::Config; | ||
| 9 | use hal::pac::adc1::cfg::{Pwrsel, Refsel}; | 12 | use hal::pac::adc1::cfg::{Pwrsel, Refsel}; |
| 10 | use hal::pac::adc1::cmdl1::{Adch, Mode}; | 13 | use hal::pac::adc1::cmdl1::Mode; |
| 11 | use hal::pac::adc1::ctrl::CalAvgs; | 14 | use hal::pac::adc1::ctrl::CalAvgs; |
| 12 | use hal::pac::adc1::tctrl::Tcmd; | 15 | use hal::pac::adc1::tctrl::Tcmd; |
| 13 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 16 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| @@ -16,11 +19,10 @@ const G_LPADC_RESULT_SHIFT: u32 = 0; | |||
| 16 | 19 | ||
| 17 | #[embassy_executor::main] | 20 | #[embassy_executor::main] |
| 18 | async fn main(_spawner: Spawner) { | 21 | async fn main(_spawner: Spawner) { |
| 19 | let p = hal::init(hal::config::Config::default()); | 22 | let mut config = Config::default(); |
| 23 | config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); | ||
| 20 | 24 | ||
| 21 | unsafe { | 25 | let p = hal::init(config); |
| 22 | init_adc_pins(); | ||
| 23 | } | ||
| 24 | 26 | ||
| 25 | defmt::info!("=== ADC polling Example ==="); | 27 | defmt::info!("=== ADC polling Example ==="); |
| 26 | 28 | ||
| @@ -34,35 +36,43 @@ async fn main(_spawner: Spawner) { | |||
| 34 | trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, | 36 | trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, |
| 35 | enable_conv_pause: false, | 37 | enable_conv_pause: false, |
| 36 | conv_pause_delay: 0, | 38 | conv_pause_delay: 0, |
| 37 | fifo_watermark: 0, | ||
| 38 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | 39 | power: PoweredClock::NormalEnabledDeepSleepDisabled, |
| 39 | source: AdcClockSel::FroLfDiv, | 40 | source: AdcClockSel::FroLfDiv, |
| 40 | div: Div4::no_div(), | 41 | div: Div4::no_div(), |
| 41 | }; | 42 | }; |
| 42 | let adc = hal::adc::Adc::<hal::adc::Adc1>::new(p.ADC1, adc_config); | 43 | let adc = Adc::new_blocking(p.ADC1, p.P1_10, adc_config).unwrap(); |
| 43 | 44 | ||
| 44 | adc.do_offset_calibration(); | 45 | adc.do_offset_calibration(); |
| 45 | adc.do_auto_calibration(); | 46 | adc.do_auto_calibration(); |
| 46 | 47 | ||
| 47 | let mut conv_command_config = adc.get_default_conv_command_config(); | 48 | let conv_command_config = ConvCommandConfig { |
| 48 | conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; | 49 | conversion_resolution_mode: Mode::Data16Bits, |
| 49 | conv_command_config.conversion_resolution_mode = Mode::Data16Bits; | 50 | ..ConvCommandConfig::default() |
| 50 | adc.set_conv_command_config(1, &conv_command_config); | 51 | }; |
| 52 | adc.set_conv_command_config(1, &conv_command_config).unwrap(); | ||
| 51 | 53 | ||
| 52 | let mut conv_trigger_config = adc.get_default_conv_trigger_config(); | 54 | let conv_trigger_config = ConvTriggerConfig { |
| 53 | conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; | 55 | target_command_id: Tcmd::ExecuteCmd1, |
| 54 | conv_trigger_config.enable_hardware_trigger = false; | 56 | enable_hardware_trigger: false, |
| 55 | adc.set_conv_trigger_config(0, &conv_trigger_config); | 57 | ..Default::default() |
| 58 | }; | ||
| 59 | adc.set_conv_trigger_config(0, &conv_trigger_config).unwrap(); | ||
| 56 | 60 | ||
| 57 | defmt::info!("=== ADC configuration done... ==="); | 61 | defmt::info!("=== ADC configuration done... ==="); |
| 62 | let mut tick = Ticker::every(Duration::from_millis(100)); | ||
| 58 | 63 | ||
| 59 | loop { | 64 | loop { |
| 60 | adc.do_software_trigger(1); | 65 | tick.next().await; |
| 61 | let mut result: Option<ConvResult> = None; | 66 | adc.do_software_trigger(1).unwrap(); |
| 62 | while result.is_none() { | 67 | let result = loop { |
| 63 | result = hal::adc::get_conv_result(); | 68 | match adc.get_conv_result() { |
| 64 | } | 69 | Ok(res) => break res, |
| 65 | let value = result.unwrap().conv_value >> G_LPADC_RESULT_SHIFT; | 70 | Err(_) => { |
| 66 | defmt::info!("value: {=u16}", value); | 71 | // Conversion not ready, continue polling |
| 72 | } | ||
| 73 | } | ||
| 74 | }; | ||
| 75 | let value = result.conv_value >> G_LPADC_RESULT_SHIFT; | ||
| 76 | defmt::info!("ADC value: {=u16}", value); | ||
| 67 | } | 77 | } |
| 68 | } | 78 | } |
diff --git a/examples/mcxa/src/bin/clkout.rs b/examples/mcxa/src/bin/clkout.rs index bfd963540..1e52912d3 100644 --- a/examples/mcxa/src/bin/clkout.rs +++ b/examples/mcxa/src/bin/clkout.rs | |||
| @@ -4,8 +4,7 @@ | |||
| 4 | use embassy_executor::Spawner; | 4 | use embassy_executor::Spawner; |
| 5 | use embassy_mcxa::clkout::{ClockOut, ClockOutSel, Config, Div4}; | 5 | use embassy_mcxa::clkout::{ClockOut, ClockOutSel, Config, Div4}; |
| 6 | use embassy_mcxa::clocks::PoweredClock; | 6 | use embassy_mcxa::clocks::PoweredClock; |
| 7 | use embassy_mcxa::gpio::{DriveStrength, SlewRate}; | 7 | use embassy_mcxa::gpio::{DriveStrength, Level, Output, SlewRate}; |
| 8 | use embassy_mcxa::{Level, Output}; | ||
| 9 | use embassy_time::Timer; | 8 | use embassy_time::Timer; |
| 10 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 9 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| 11 | 10 | ||
diff --git a/examples/mcxa/src/bin/i2c-scan-blocking.rs b/examples/mcxa/src/bin/i2c-scan-blocking.rs index 0197f9b1d..bd706d712 100644 --- a/examples/mcxa/src/bin/i2c-scan-blocking.rs +++ b/examples/mcxa/src/bin/i2c-scan-blocking.rs | |||
| @@ -2,8 +2,7 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use embassy_executor::Spawner; | 4 | use embassy_executor::Spawner; |
| 5 | use embassy_mcxa::Input; | 5 | use embassy_mcxa::gpio::{Input, Pull}; |
| 6 | use embassy_mcxa::gpio::Pull; | ||
| 7 | use embassy_time::Timer; | 6 | use embassy_time::Timer; |
| 8 | use hal::clocks::config::Div8; | 7 | use hal::clocks::config::Div8; |
| 9 | use hal::config::Config; | 8 | use hal::config::Config; |
diff --git a/examples/mcxa/src/bin/reset-reason.rs b/examples/mcxa/src/bin/reset-reason.rs new file mode 100644 index 000000000..c244fbe04 --- /dev/null +++ b/examples/mcxa/src/bin/reset-reason.rs | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use hal::config::Config; | ||
| 6 | use hal::reset_reason::reset_reason; | ||
| 7 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 8 | |||
| 9 | #[embassy_executor::main] | ||
| 10 | async fn main(_spawner: Spawner) { | ||
| 11 | let config = Config::default(); | ||
| 12 | let _p = hal::init(config); | ||
| 13 | |||
| 14 | defmt::info!("Reset Reason: '{}'", reset_reason()); | ||
| 15 | } | ||
diff --git a/examples/mcxa/src/lib.rs b/examples/mcxa/src/lib.rs deleted file mode 100644 index 2573a6adc..000000000 --- a/examples/mcxa/src/lib.rs +++ /dev/null | |||
| @@ -1,16 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![allow(clippy::missing_safety_doc)] | ||
| 3 | |||
| 4 | //! Shared board-specific helpers for the FRDM-MCXA276 examples. | ||
| 5 | //! These live with the examples so the HAL stays generic. | ||
| 6 | |||
| 7 | use hal::{clocks, pins}; | ||
| 8 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 9 | |||
| 10 | /// Initialize clocks and pin muxing for ADC. | ||
| 11 | pub unsafe fn init_adc_pins() { | ||
| 12 | // NOTE: Lpuart has been updated to properly enable + reset its own clocks. | ||
| 13 | // GPIO has not. | ||
| 14 | _ = clocks::enable_and_reset::<hal::peripherals::PORT1>(&clocks::periph_helpers::NoConfig); | ||
| 15 | pins::configure_adc_pins(); | ||
| 16 | } | ||
diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs index e39de4902..ef8d08b76 100644 --- a/examples/rp/src/bin/wifi_tcp_server.rs +++ b/examples/rp/src/bin/wifi_tcp_server.rs | |||
| @@ -101,7 +101,7 @@ async fn main(spawner: Spawner) { | |||
| 101 | .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes())) | 101 | .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes())) |
| 102 | .await | 102 | .await |
| 103 | { | 103 | { |
| 104 | info!("join failed with status={}", err.status); | 104 | info!("join failed: {:?}", err); |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | info!("waiting for link..."); | 107 | info!("waiting for link..."); |
diff --git a/examples/rp/src/bin/wifi_webrequest.rs b/examples/rp/src/bin/wifi_webrequest.rs index ce85f4b9a..069afc794 100644 --- a/examples/rp/src/bin/wifi_webrequest.rs +++ b/examples/rp/src/bin/wifi_webrequest.rs | |||
| @@ -106,7 +106,7 @@ async fn main(spawner: Spawner) { | |||
| 106 | .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes())) | 106 | .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes())) |
| 107 | .await | 107 | .await |
| 108 | { | 108 | { |
| 109 | info!("join failed with status={}", err.status); | 109 | info!("join failed: {:?}", err); |
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | info!("waiting for link..."); | 112 | info!("waiting for link..."); |
diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index fe0f887bf..098fd6986 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs | |||
| @@ -3,7 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; | 6 | use embassy_stm32::sdmmc::Sdmmc; |
| 7 | use embassy_stm32::sdmmc::sd::{CmdBlock, DataBlock, StorageDevice}; | ||
| 7 | use embassy_stm32::time::{Hertz, mhz}; | 8 | use embassy_stm32::time::{Hertz, mhz}; |
| 8 | use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; | 9 | use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -57,31 +58,24 @@ async fn main(_spawner: Spawner) { | |||
| 57 | // Should print 400kHz for initialization | 58 | // Should print 400kHz for initialization |
| 58 | info!("Configured clock: {}", sdmmc.clock().0); | 59 | info!("Configured clock: {}", sdmmc.clock().0); |
| 59 | 60 | ||
| 60 | let mut err = None; | 61 | let mut cmd_block = CmdBlock::new(); |
| 61 | loop { | 62 | |
| 62 | match sdmmc.init_sd_card(mhz(24)).await { | 63 | let mut storage = StorageDevice::new_sd_card(&mut sdmmc, &mut cmd_block, mhz(24)) |
| 63 | Ok(_) => break, | 64 | .await |
| 64 | Err(e) => { | 65 | .unwrap(); |
| 65 | if err != Some(e) { | ||
| 66 | info!("waiting for card error, retrying: {:?}", e); | ||
| 67 | err = Some(e); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | } | ||
| 71 | } | ||
| 72 | 66 | ||
| 73 | let card = unwrap!(sdmmc.card()); | 67 | let card = storage.card(); |
| 74 | 68 | ||
| 75 | info!("Card: {:#?}", Debug2Format(card)); | 69 | info!("Card: {:#?}", Debug2Format(&card)); |
| 76 | info!("Clock: {}", sdmmc.clock()); | 70 | info!("Clock: {}", storage.sdmmc.clock()); |
| 77 | 71 | ||
| 78 | // Arbitrary block index | 72 | // Arbitrary block index |
| 79 | let block_idx = 16; | 73 | let block_idx = 16; |
| 80 | 74 | ||
| 81 | // SDMMC uses `DataBlock` instead of `&[u8]` to ensure 4 byte alignment required by the hardware. | 75 | // SDMMC uses `DataBlock` instead of `&[u8]` to ensure 4 byte alignment required by the hardware. |
| 82 | let mut block = DataBlock([0u8; 512]); | 76 | let mut block = DataBlock::new(); |
| 83 | 77 | ||
| 84 | sdmmc.read_block(block_idx, &mut block).await.unwrap(); | 78 | storage.read_block(block_idx, &mut block).await.unwrap(); |
| 85 | info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); | 79 | info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); |
| 86 | 80 | ||
| 87 | if !ALLOW_WRITES { | 81 | if !ALLOW_WRITES { |
| @@ -91,17 +85,17 @@ async fn main(_spawner: Spawner) { | |||
| 91 | 85 | ||
| 92 | info!("Filling block with 0x55"); | 86 | info!("Filling block with 0x55"); |
| 93 | block.fill(0x55); | 87 | block.fill(0x55); |
| 94 | sdmmc.write_block(block_idx, &block).await.unwrap(); | 88 | storage.write_block(block_idx, &block).await.unwrap(); |
| 95 | info!("Write done"); | 89 | info!("Write done"); |
| 96 | 90 | ||
| 97 | sdmmc.read_block(block_idx, &mut block).await.unwrap(); | 91 | storage.read_block(block_idx, &mut block).await.unwrap(); |
| 98 | info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); | 92 | info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); |
| 99 | 93 | ||
| 100 | info!("Filling block with 0xAA"); | 94 | info!("Filling block with 0xAA"); |
| 101 | block.fill(0xAA); | 95 | block.fill(0xAA); |
| 102 | sdmmc.write_block(block_idx, &block).await.unwrap(); | 96 | storage.write_block(block_idx, &block).await.unwrap(); |
| 103 | info!("Write done"); | 97 | info!("Write done"); |
| 104 | 98 | ||
| 105 | sdmmc.read_block(block_idx, &mut block).await.unwrap(); | 99 | storage.read_block(block_idx, &mut block).await.unwrap(); |
| 106 | info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); | 100 | info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); |
| 107 | } | 101 | } |
diff --git a/examples/stm32f7/src/bin/sdmmc.rs b/examples/stm32f7/src/bin/sdmmc.rs index 8809b5d0c..e5d261d89 100644 --- a/examples/stm32f7/src/bin/sdmmc.rs +++ b/examples/stm32f7/src/bin/sdmmc.rs | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::sdmmc::Sdmmc; | 6 | use embassy_stm32::sdmmc::Sdmmc; |
| 7 | use embassy_stm32::sdmmc::sd::{CmdBlock, StorageDevice}; | ||
| 7 | use embassy_stm32::time::{Hertz, mhz}; | 8 | use embassy_stm32::time::{Hertz, mhz}; |
| 8 | use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; | 9 | use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -54,9 +55,13 @@ async fn main(_spawner: Spawner) { | |||
| 54 | // Should print 400kHz for initialization | 55 | // Should print 400kHz for initialization |
| 55 | info!("Configured clock: {}", sdmmc.clock().0); | 56 | info!("Configured clock: {}", sdmmc.clock().0); |
| 56 | 57 | ||
| 57 | unwrap!(sdmmc.init_sd_card(mhz(25)).await); | 58 | let mut cmd_block = CmdBlock::new(); |
| 58 | 59 | ||
| 59 | let card = unwrap!(sdmmc.card()); | 60 | let storage = StorageDevice::new_sd_card(&mut sdmmc, &mut cmd_block, mhz(25)) |
| 61 | .await | ||
| 62 | .unwrap(); | ||
| 60 | 63 | ||
| 61 | info!("Card: {:#?}", Debug2Format(card)); | 64 | let card = storage.card(); |
| 65 | |||
| 66 | info!("Card: {:#?}", Debug2Format(&card)); | ||
| 62 | } | 67 | } |
diff --git a/examples/stm32h7/src/bin/sdmmc.rs b/examples/stm32h7/src/bin/sdmmc.rs index 4977fec79..f2e5bedeb 100644 --- a/examples/stm32h7/src/bin/sdmmc.rs +++ b/examples/stm32h7/src/bin/sdmmc.rs | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::sdmmc::Sdmmc; | 6 | use embassy_stm32::sdmmc::Sdmmc; |
| 7 | use embassy_stm32::sdmmc::sd::{CmdBlock, StorageDevice}; | ||
| 7 | use embassy_stm32::time::mhz; | 8 | use embassy_stm32::time::mhz; |
| 8 | use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; | 9 | use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -13,7 +14,7 @@ bind_interrupts!(struct Irqs { | |||
| 13 | }); | 14 | }); |
| 14 | 15 | ||
| 15 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 16 | async fn main(_spawner: Spawner) -> ! { | 17 | async fn main(_spawner: Spawner) { |
| 17 | let mut config = Config::default(); | 18 | let mut config = Config::default(); |
| 18 | { | 19 | { |
| 19 | use embassy_stm32::rcc::*; | 20 | use embassy_stm32::rcc::*; |
| @@ -53,11 +54,13 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 53 | // Should print 400kHz for initialization | 54 | // Should print 400kHz for initialization |
| 54 | info!("Configured clock: {}", sdmmc.clock().0); | 55 | info!("Configured clock: {}", sdmmc.clock().0); |
| 55 | 56 | ||
| 56 | unwrap!(sdmmc.init_sd_card(mhz(25)).await); | 57 | let mut cmd_block = CmdBlock::new(); |
| 57 | 58 | ||
| 58 | let card = unwrap!(sdmmc.card()); | 59 | let storage = StorageDevice::new_sd_card(&mut sdmmc, &mut cmd_block, mhz(25)) |
| 60 | .await | ||
| 61 | .unwrap(); | ||
| 59 | 62 | ||
| 60 | info!("Card: {:#?}", Debug2Format(card)); | 63 | let card = storage.card(); |
| 61 | 64 | ||
| 62 | loop {} | 65 | info!("Card: {:#?}", Debug2Format(&card)); |
| 63 | } | 66 | } |
diff --git a/examples/stm32n6/Cargo.toml b/examples/stm32n6/Cargo.toml index 5ed28eed1..5ad5b97ce 100644 --- a/examples/stm32n6/Cargo.toml +++ b/examples/stm32n6/Cargo.toml | |||
| @@ -32,6 +32,8 @@ micromath = "2.0.0" | |||
| 32 | stm32-fmc = "0.3.0" | 32 | stm32-fmc = "0.3.0" |
| 33 | embedded-storage = "0.3.1" | 33 | embedded-storage = "0.3.1" |
| 34 | static_cell = "2" | 34 | static_cell = "2" |
| 35 | hmac = "0.12.1" | ||
| 36 | sha2 = { version = "0.10.9", default-features = false } | ||
| 35 | 37 | ||
| 36 | 38 | ||
| 37 | # cargo build/run | 39 | # cargo build/run |
diff --git a/examples/stm32n6/src/bin/crc.rs b/examples/stm32n6/src/bin/crc.rs new file mode 100644 index 000000000..d1b545d5b --- /dev/null +++ b/examples/stm32n6/src/bin/crc.rs | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::crc::{Config, Crc, InputReverseConfig, PolySize}; | ||
| 7 | use {defmt_rtt as _, panic_probe as _}; | ||
| 8 | |||
| 9 | #[embassy_executor::main] | ||
| 10 | async fn main(_spawner: Spawner) { | ||
| 11 | let p = embassy_stm32::init(Default::default()); | ||
| 12 | info!("Hello World!"); | ||
| 13 | |||
| 14 | // Setup for: https://crccalc.com/?crc=Life, it never dieWomen are my favorite guy&method=crc32&datatype=ascii&outtype=0 | ||
| 15 | let mut crc = Crc::new( | ||
| 16 | p.CRC, | ||
| 17 | unwrap!(Config::new( | ||
| 18 | InputReverseConfig::Byte, | ||
| 19 | true, | ||
| 20 | PolySize::Width32, | ||
| 21 | 0xFFFFFFFF, | ||
| 22 | 0x04C11DB7 | ||
| 23 | )), | ||
| 24 | ); | ||
| 25 | |||
| 26 | let output = crc.feed_bytes(b"Life, it never die\nWomen are my favorite guy") ^ 0xFFFFFFFF; | ||
| 27 | |||
| 28 | defmt::assert_eq!(output, 0x33F0E26B); | ||
| 29 | |||
| 30 | cortex_m::asm::bkpt(); | ||
| 31 | } | ||
diff --git a/examples/stm32n6/src/bin/hash.rs b/examples/stm32n6/src/bin/hash.rs new file mode 100644 index 000000000..9f248318f --- /dev/null +++ b/examples/stm32n6/src/bin/hash.rs | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::info; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::hash::*; | ||
| 7 | use embassy_stm32::{Config, bind_interrupts, hash, peripherals}; | ||
| 8 | use embassy_time::Instant; | ||
| 9 | use hmac::{Hmac, Mac}; | ||
| 10 | use sha2::{Digest, Sha256}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | type HmacSha256 = Hmac<Sha256>; | ||
| 14 | |||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | HASH => hash::InterruptHandler<peripherals::HASH>; | ||
| 17 | }); | ||
| 18 | |||
| 19 | #[embassy_executor::main] | ||
| 20 | async fn main(_spawner: Spawner) -> ! { | ||
| 21 | let config = Config::default(); | ||
| 22 | let p = embassy_stm32::init(config); | ||
| 23 | |||
| 24 | let test_1: &[u8] = b"as;dfhaslfhas;oifvnasd;nifvnhasd;nifvhndlkfghsd;nvfnahssdfgsdafgsasdfasdfasdfasdfasdfghjklmnbvcalskdjghalskdjgfbaslkdjfgbalskdjgbalskdjbdfhsdfhsfghsfghfgh"; | ||
| 25 | let test_2: &[u8] = b"fdhalksdjfhlasdjkfhalskdjfhgal;skdjfgalskdhfjgalskdjfglafgadfgdfgdafgaadsfgfgdfgadrgsyfthxfgjfhklhjkfgukhulkvhlvhukgfhfsrghzdhxyfufynufyuszeradrtydyytserr"; | ||
| 26 | |||
| 27 | let mut hw_hasher = Hash::new_blocking(p.HASH, Irqs); | ||
| 28 | |||
| 29 | let hw_start_time = Instant::now(); | ||
| 30 | |||
| 31 | // Compute a digest in hardware. | ||
| 32 | let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, None); | ||
| 33 | hw_hasher.update_blocking(&mut context, test_1); | ||
| 34 | hw_hasher.update_blocking(&mut context, test_2); | ||
| 35 | let mut hw_digest: [u8; 32] = [0; 32]; | ||
| 36 | hw_hasher.finish_blocking(context, &mut hw_digest); | ||
| 37 | |||
| 38 | let hw_end_time = Instant::now(); | ||
| 39 | let hw_execution_time = hw_end_time - hw_start_time; | ||
| 40 | |||
| 41 | let sw_start_time = Instant::now(); | ||
| 42 | |||
| 43 | // Compute a digest in software. | ||
| 44 | let mut sw_hasher = Sha256::new(); | ||
| 45 | sw_hasher.update(test_1); | ||
| 46 | sw_hasher.update(test_2); | ||
| 47 | let sw_digest = sw_hasher.finalize(); | ||
| 48 | |||
| 49 | let sw_end_time = Instant::now(); | ||
| 50 | let sw_execution_time = sw_end_time - sw_start_time; | ||
| 51 | |||
| 52 | info!("Hardware Digest: {:?}", hw_digest); | ||
| 53 | info!("Software Digest: {:?}", sw_digest[..]); | ||
| 54 | info!("Hardware Execution Time: {:?}", hw_execution_time); | ||
| 55 | info!("Software Execution Time: {:?}", sw_execution_time); | ||
| 56 | assert_eq!(hw_digest, sw_digest[..]); | ||
| 57 | |||
| 58 | let hmac_key: [u8; 64] = [0x55; 64]; | ||
| 59 | |||
| 60 | // Compute HMAC in hardware. | ||
| 61 | let mut sha256hmac_context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, Some(&hmac_key)); | ||
| 62 | hw_hasher.update_blocking(&mut sha256hmac_context, test_1); | ||
| 63 | hw_hasher.update_blocking(&mut sha256hmac_context, test_2); | ||
| 64 | let mut hw_hmac: [u8; 32] = [0; 32]; | ||
| 65 | hw_hasher.finish_blocking(sha256hmac_context, &mut hw_hmac); | ||
| 66 | |||
| 67 | // Compute HMAC in software. | ||
| 68 | let mut sw_mac = HmacSha256::new_from_slice(&hmac_key).unwrap(); | ||
| 69 | sw_mac.update(test_1); | ||
| 70 | sw_mac.update(test_2); | ||
| 71 | let sw_hmac = sw_mac.finalize().into_bytes(); | ||
| 72 | |||
| 73 | info!("Hardware HMAC: {:?}", hw_hmac); | ||
| 74 | info!("Software HMAC: {:?}", sw_hmac[..]); | ||
| 75 | assert_eq!(hw_hmac, sw_hmac[..]); | ||
| 76 | |||
| 77 | loop {} | ||
| 78 | } | ||
diff --git a/examples/stm32wba/src/bin/rtc.rs b/examples/stm32wba/src/bin/rtc.rs new file mode 100644 index 000000000..cef8501e0 --- /dev/null +++ b/examples/stm32wba/src/bin/rtc.rs | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::Config; | ||
| 7 | use embassy_stm32::rcc::*; | ||
| 8 | use embassy_stm32::rtc::{DateTime, DayOfWeek, Rtc, RtcConfig}; | ||
| 9 | use embassy_time::Timer; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | pub fn pll_init(config: &mut Config) { | ||
| 13 | config.rcc.pll1 = Some(embassy_stm32::rcc::Pll { | ||
| 14 | source: PllSource::HSI, | ||
| 15 | prediv: PllPreDiv::DIV1, // PLLM = 1 → HSI / 1 = 16 MHz | ||
| 16 | mul: PllMul::MUL30, // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO | ||
| 17 | divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk) | ||
| 18 | // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED) | ||
| 19 | divq: None, | ||
| 20 | divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG) | ||
| 21 | frac: Some(0), // Fractional part (enabled) | ||
| 22 | }); | ||
| 23 | |||
| 24 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 25 | config.rcc.apb1_pre = APBPrescaler::DIV1; | ||
| 26 | config.rcc.apb2_pre = APBPrescaler::DIV1; | ||
| 27 | config.rcc.apb7_pre = APBPrescaler::DIV1; | ||
| 28 | config.rcc.ahb5_pre = AHB5Prescaler::DIV4; | ||
| 29 | |||
| 30 | // voltage scale for max performance | ||
| 31 | config.rcc.voltage_scale = VoltageScale::RANGE1; | ||
| 32 | // route PLL1_P into the USB‐OTG‐HS block | ||
| 33 | config.rcc.sys = Sysclk::PLL1_R; | ||
| 34 | } | ||
| 35 | |||
| 36 | #[embassy_executor::main] | ||
| 37 | async fn main(_spawner: Spawner) { | ||
| 38 | let mut config = Config::default(); | ||
| 39 | |||
| 40 | pll_init(&mut config); | ||
| 41 | |||
| 42 | let p = embassy_stm32::init(config); | ||
| 43 | |||
| 44 | let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); | ||
| 45 | |||
| 46 | // Setting datetime | ||
| 47 | let initial_datetime = DateTime::from(1970, 1, 1, DayOfWeek::Thursday, 0, 00, 00, 0).unwrap(); | ||
| 48 | match rtc.0.set_datetime(initial_datetime) { | ||
| 49 | Ok(()) => info!("RTC set successfully."), | ||
| 50 | Err(e) => error!("Failed to set RTC date/time: {:?}", e), | ||
| 51 | } | ||
| 52 | |||
| 53 | // Reading datetime every 1s | ||
| 54 | loop { | ||
| 55 | match rtc.1.now() { | ||
| 56 | Ok(result) => info!("{}", result), | ||
| 57 | Err(e) => error!("Failed to set RTC date/time: {:?}", e), | ||
| 58 | } | ||
| 59 | |||
| 60 | Timer::after_millis(1000).await; | ||
| 61 | } | ||
| 62 | } | ||
diff --git a/examples/stm32wba6/src/bin/rtc.rs b/examples/stm32wba6/src/bin/rtc.rs new file mode 100644 index 000000000..cef8501e0 --- /dev/null +++ b/examples/stm32wba6/src/bin/rtc.rs | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::Config; | ||
| 7 | use embassy_stm32::rcc::*; | ||
| 8 | use embassy_stm32::rtc::{DateTime, DayOfWeek, Rtc, RtcConfig}; | ||
| 9 | use embassy_time::Timer; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | pub fn pll_init(config: &mut Config) { | ||
| 13 | config.rcc.pll1 = Some(embassy_stm32::rcc::Pll { | ||
| 14 | source: PllSource::HSI, | ||
| 15 | prediv: PllPreDiv::DIV1, // PLLM = 1 → HSI / 1 = 16 MHz | ||
| 16 | mul: PllMul::MUL30, // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO | ||
| 17 | divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk) | ||
| 18 | // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED) | ||
| 19 | divq: None, | ||
| 20 | divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG) | ||
| 21 | frac: Some(0), // Fractional part (enabled) | ||
| 22 | }); | ||
| 23 | |||
| 24 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 25 | config.rcc.apb1_pre = APBPrescaler::DIV1; | ||
| 26 | config.rcc.apb2_pre = APBPrescaler::DIV1; | ||
| 27 | config.rcc.apb7_pre = APBPrescaler::DIV1; | ||
| 28 | config.rcc.ahb5_pre = AHB5Prescaler::DIV4; | ||
| 29 | |||
| 30 | // voltage scale for max performance | ||
| 31 | config.rcc.voltage_scale = VoltageScale::RANGE1; | ||
| 32 | // route PLL1_P into the USB‐OTG‐HS block | ||
| 33 | config.rcc.sys = Sysclk::PLL1_R; | ||
| 34 | } | ||
| 35 | |||
| 36 | #[embassy_executor::main] | ||
| 37 | async fn main(_spawner: Spawner) { | ||
| 38 | let mut config = Config::default(); | ||
| 39 | |||
| 40 | pll_init(&mut config); | ||
| 41 | |||
| 42 | let p = embassy_stm32::init(config); | ||
| 43 | |||
| 44 | let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); | ||
| 45 | |||
| 46 | // Setting datetime | ||
| 47 | let initial_datetime = DateTime::from(1970, 1, 1, DayOfWeek::Thursday, 0, 00, 00, 0).unwrap(); | ||
| 48 | match rtc.0.set_datetime(initial_datetime) { | ||
| 49 | Ok(()) => info!("RTC set successfully."), | ||
| 50 | Err(e) => error!("Failed to set RTC date/time: {:?}", e), | ||
| 51 | } | ||
| 52 | |||
| 53 | // Reading datetime every 1s | ||
| 54 | loop { | ||
| 55 | match rtc.1.now() { | ||
| 56 | Ok(result) => info!("{}", result), | ||
| 57 | Err(e) => error!("Failed to set RTC date/time: {:?}", e), | ||
| 58 | } | ||
| 59 | |||
| 60 | Timer::after_millis(1000).await; | ||
| 61 | } | ||
| 62 | } | ||
diff --git a/fmtall.sh b/fmtall.sh new file mode 100755 index 000000000..9cadcdbe9 --- /dev/null +++ b/fmtall.sh | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | |||
| 3 | set -euo pipefail | ||
| 4 | |||
| 5 | # We need the nightly toolchain for this | ||
| 6 | mv rust-toolchain-nightly.toml rust-toolchain.toml | ||
| 7 | |||
| 8 | # Similar to the CI workflow, but don't just CHECK, actualy DO the formatting | ||
| 9 | find . -name '*.rs' -not -path '*target*' | xargs rustfmt --skip-children --unstable-features --edition 2024 | ||
| 10 | |||
| 11 | # Put the toolchains back, copy back to nightly and do a clean checkout of rust-toolchain | ||
| 12 | mv rust-toolchain.toml rust-toolchain-nightly.toml | ||
| 13 | git checkout -- rust-toolchain.toml | ||
diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs index 9487f5e1a..1ef9cf066 100644 --- a/tests/rp/src/bin/cyw43-perf.rs +++ b/tests/rp/src/bin/cyw43-perf.rs | |||
| @@ -98,7 +98,7 @@ async fn main(spawner: Spawner) { | |||
| 98 | { | 98 | { |
| 99 | Ok(_) => break, | 99 | Ok(_) => break, |
| 100 | Err(err) => { | 100 | Err(err) => { |
| 101 | panic!("join failed with status={}", err.status); | 101 | panic!("join failed: {:?}", err); |
| 102 | } | 102 | } |
| 103 | } | 103 | } |
| 104 | } | 104 | } |
diff --git a/tests/stm32/src/bin/sdmmc.rs b/tests/stm32/src/bin/sdmmc.rs index 9f9c526e1..07422c42e 100644 --- a/tests/stm32/src/bin/sdmmc.rs +++ b/tests/stm32/src/bin/sdmmc.rs | |||
| @@ -7,7 +7,8 @@ mod common; | |||
| 7 | use common::*; | 7 | use common::*; |
| 8 | use defmt::assert_eq; | 8 | use defmt::assert_eq; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; | 10 | use embassy_stm32::sdmmc::Sdmmc; |
| 11 | use embassy_stm32::sdmmc::sd::{CmdBlock, DataBlock, StorageDevice}; | ||
| 11 | use embassy_stm32::time::mhz; | 12 | use embassy_stm32::time::mhz; |
| 12 | use embassy_stm32::{bind_interrupts, peripherals, sdmmc}; | 13 | use embassy_stm32::{bind_interrupts, peripherals, sdmmc}; |
| 13 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -28,16 +29,16 @@ async fn main(_spawner: Spawner) { | |||
| 28 | // Arbitrary block index | 29 | // Arbitrary block index |
| 29 | let block_idx = 16; | 30 | let block_idx = 16; |
| 30 | 31 | ||
| 31 | let mut pattern1 = DataBlock([0u8; 512]); | 32 | let mut pattern1 = DataBlock::new(); |
| 32 | let mut pattern2 = DataBlock([0u8; 512]); | 33 | let mut pattern2 = DataBlock::new(); |
| 33 | for i in 0..512 { | 34 | for i in 0..512 { |
| 34 | pattern1[i] = i as u8; | 35 | pattern1[i] = i as u8; |
| 35 | pattern2[i] = !i as u8; | 36 | pattern2[i] = !i as u8; |
| 36 | } | 37 | } |
| 37 | let patterns = [pattern1.clone(), pattern2.clone()]; | 38 | let patterns = [pattern1.clone(), pattern2.clone()]; |
| 38 | 39 | ||
| 39 | let mut block = DataBlock([0u8; 512]); | 40 | let mut block = DataBlock::new(); |
| 40 | let mut blocks = [DataBlock([0u8; 512]), DataBlock([0u8; 512])]; | 41 | let mut blocks = [DataBlock::new(), DataBlock::new()]; |
| 41 | 42 | ||
| 42 | // ======== Try 4bit. ============== | 43 | // ======== Try 4bit. ============== |
| 43 | info!("initializing in 4-bit mode..."); | 44 | info!("initializing in 4-bit mode..."); |
| @@ -54,43 +55,80 @@ async fn main(_spawner: Spawner) { | |||
| 54 | Default::default(), | 55 | Default::default(), |
| 55 | ); | 56 | ); |
| 56 | 57 | ||
| 57 | let mut err = None; | 58 | let mut cmd_block = CmdBlock::new(); |
| 58 | loop { | ||
| 59 | match s.init_sd_card(mhz(24)).await { | ||
| 60 | Ok(_) => break, | ||
| 61 | Err(e) => { | ||
| 62 | if err != Some(e) { | ||
| 63 | info!("waiting for card: {:?}", e); | ||
| 64 | err = Some(e); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | } | ||
| 68 | } | ||
| 69 | 59 | ||
| 70 | let card = unwrap!(s.card()); | 60 | let mut storage = loop { |
| 71 | 61 | if let Ok(storage) = StorageDevice::new_sd_card(&mut s, &mut cmd_block, mhz(24)).await { | |
| 72 | info!("Card: {:#?}", Debug2Format(card)); | 62 | break storage; |
| 73 | info!("Clock: {}", s.clock()); | 63 | } |
| 64 | }; | ||
| 65 | |||
| 66 | let card = storage.card(); | ||
| 67 | |||
| 68 | info!("Card: {:#?}", Debug2Format(&card)); | ||
| 69 | info!("Clock: {}", storage.sdmmc.clock()); | ||
| 70 | |||
| 71 | // card_type: HighCapacity, | ||
| 72 | // ocr: OCR: Operation Conditions Register { | ||
| 73 | // Voltage Window (mV): (2700, 3600), | ||
| 74 | // S18A (UHS-I only): true, | ||
| 75 | // Over 2TB flag (SDUC only): false, | ||
| 76 | // UHS-II Card: false, | ||
| 77 | // Card Capacity Status (CSS): \"SDHC/SDXC/SDUC\", | ||
| 78 | // Busy: false }, | ||
| 79 | // rca: 43690, | ||
| 80 | // cid: CID: Card Identification { Manufacturer ID: 3, | ||
| 81 | // OEM ID: \"SD\", | ||
| 82 | // Product Name: \"SL08G\", | ||
| 83 | // Product Revision: 128, | ||
| 84 | // Product Serial Number: 701445767, | ||
| 85 | // Manufacturing Date: (9, | ||
| 86 | // 2015) }, | ||
| 87 | // csd: CSD: Card Specific Data { Transfer Rate: 50, | ||
| 88 | // Block Count: 15523840, | ||
| 89 | // Card Size (bytes): 7948206080, | ||
| 90 | // Read I (@min VDD): 100 mA, | ||
| 91 | // Write I (@min VDD): 10 mA, | ||
| 92 | // Read I (@max VDD): 5 mA, | ||
| 93 | // Write I (@max VDD): 45 mA, | ||
| 94 | // Erase Size (Blocks): 1 }, | ||
| 95 | // scr: SCR: SD CARD Configuration Register { Version: Unknown, | ||
| 96 | // 1-bit width: false, | ||
| 97 | // 4-bit width: true }, | ||
| 98 | // status: SD Status { Bus Width: One, | ||
| 99 | // Secured Mode: false, | ||
| 100 | // SD Memory Card Type: 0, | ||
| 101 | // Protected Area Size (B): 0, | ||
| 102 | // Speed Class: 0, | ||
| 103 | // Video Speed Class: 0, | ||
| 104 | // Application Performance Class: 0, | ||
| 105 | // Move Performance (MB/s): 0, | ||
| 106 | // AU Size: 0, | ||
| 107 | // Erase Size (units of AU): 0, | ||
| 108 | // Erase Timeout (s): 0, | ||
| 109 | // Discard Support: false } } | ||
| 110 | |||
| 111 | defmt::assert!(card.scr.bus_width_four()); | ||
| 74 | 112 | ||
| 75 | info!("writing pattern1..."); | 113 | info!("writing pattern1..."); |
| 76 | s.write_block(block_idx, &pattern1).await.unwrap(); | 114 | storage.write_block(block_idx, &pattern1).await.unwrap(); |
| 77 | 115 | ||
| 78 | info!("reading..."); | 116 | info!("reading..."); |
| 79 | s.read_block(block_idx, &mut block).await.unwrap(); | 117 | storage.read_block(block_idx, &mut block).await.unwrap(); |
| 80 | assert_eq!(block, pattern1); | 118 | assert_eq!(block, pattern1); |
| 81 | 119 | ||
| 82 | info!("writing pattern2..."); | 120 | info!("writing pattern2..."); |
| 83 | s.write_block(block_idx, &pattern2).await.unwrap(); | 121 | storage.write_block(block_idx, &pattern2).await.unwrap(); |
| 84 | 122 | ||
| 85 | info!("reading..."); | 123 | info!("reading..."); |
| 86 | s.read_block(block_idx, &mut block).await.unwrap(); | 124 | storage.read_block(block_idx, &mut block).await.unwrap(); |
| 87 | assert_eq!(block, pattern2); | 125 | assert_eq!(block, pattern2); |
| 88 | 126 | ||
| 89 | info!("writing blocks [pattern1, pattern2]..."); | 127 | info!("writing blocks [pattern1, pattern2]..."); |
| 90 | s.write_blocks(block_idx, &patterns).await.unwrap(); | 128 | storage.write_blocks(block_idx, &patterns).await.unwrap(); |
| 91 | 129 | ||
| 92 | info!("reading blocks..."); | 130 | info!("reading blocks..."); |
| 93 | s.read_blocks(block_idx, &mut blocks).await.unwrap(); | 131 | storage.read_blocks(block_idx, &mut blocks).await.unwrap(); |
| 94 | assert_eq!(&blocks, &patterns); | 132 | assert_eq!(&blocks, &patterns); |
| 95 | 133 | ||
| 96 | drop(s); | 134 | drop(s); |
diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index 0b98d3eeb..ef7efe96a 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs | |||
| @@ -6,6 +6,7 @@ mod common; | |||
| 6 | use common::*; | 6 | use common::*; |
| 7 | use defmt::{assert, assert_eq, unreachable}; | 7 | use defmt::{assert, assert_eq, unreachable}; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_stm32::mode::Blocking; | ||
| 9 | use embassy_stm32::usart::{Config, ConfigError, Error, Uart}; | 10 | use embassy_stm32::usart::{Config, ConfigError, Error, Uart}; |
| 10 | use embassy_time::{Duration, Instant, block_for}; | 11 | use embassy_time::{Duration, Instant, block_for}; |
| 11 | 12 | ||
| @@ -24,22 +25,41 @@ async fn main(_spawner: Spawner) { | |||
| 24 | let config = Config::default(); | 25 | let config = Config::default(); |
| 25 | let mut usart = Uart::new_blocking(usart.reborrow(), rx.reborrow(), tx.reborrow(), config).unwrap(); | 26 | let mut usart = Uart::new_blocking(usart.reborrow(), rx.reborrow(), tx.reborrow(), config).unwrap(); |
| 26 | 27 | ||
| 27 | // We can't send too many bytes, they have to fit in the FIFO. | 28 | let test_usart = async |usart: &mut Uart<'_, Blocking>| -> Result<(), Error> { |
| 28 | // This is because we aren't sending+receiving at the same time. | 29 | // We can't send too many bytes, they have to fit in the FIFO. |
| 30 | // This is because we aren't sending+receiving at the same time. | ||
| 29 | 31 | ||
| 30 | let data = [0xC0, 0xDE]; | 32 | let data = [0xC0, 0xDE]; |
| 31 | usart.blocking_write(&data).unwrap(); | 33 | usart.blocking_write(&data)?; |
| 32 | 34 | ||
| 33 | let mut buf = [0; 2]; | 35 | let mut buf = [0; 2]; |
| 34 | usart.blocking_read(&mut buf).unwrap(); | 36 | usart.blocking_read(&mut buf)?; |
| 35 | assert_eq!(buf, data); | 37 | assert_eq!(buf, data); |
| 36 | 38 | ||
| 37 | // Test flush doesn't hang. | 39 | // Test flush doesn't hang. |
| 38 | usart.blocking_write(&data).unwrap(); | 40 | usart.blocking_write(&data)?; |
| 39 | usart.blocking_flush().unwrap(); | 41 | usart.blocking_flush()?; |
| 40 | 42 | ||
| 41 | // Test flush doesn't hang if there's nothing to flush | 43 | // Test flush doesn't hang if there's nothing to flush |
| 42 | usart.blocking_flush().unwrap(); | 44 | usart.blocking_flush()?; |
| 45 | |||
| 46 | Ok(()) | ||
| 47 | }; | ||
| 48 | |||
| 49 | let mut is_ok = false; | ||
| 50 | for _ in 0..3 { | ||
| 51 | match test_usart(&mut usart).await { | ||
| 52 | Ok(()) => is_ok = true, | ||
| 53 | Err(Error::Noise) => is_ok = false, | ||
| 54 | Err(e) => defmt::panic!("{}", e), | ||
| 55 | } | ||
| 56 | |||
| 57 | if is_ok { | ||
| 58 | break; | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | assert!(is_ok); | ||
| 43 | } | 63 | } |
| 44 | 64 | ||
| 45 | // Test error handling with with an overflow error | 65 | // Test error handling with with an overflow error |
