diff options
| author | James Munns <[email protected]> | 2025-12-05 12:03:00 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-12-05 12:03:00 +0000 |
| commit | 4053e5233fab875c4607cd2fed7a7cf5659ce3e1 (patch) | |
| tree | 9fd7d45b6c3f65c7eb62604ba3be625de3b4d3a5 /embassy-mcxa | |
| parent | 5572b884a166b4012a250a12537c4cdd63b377c3 (diff) | |
| parent | 2b607b8aae8cab98e41159f657ecd2c3bf790592 (diff) | |
Merge pull request #4989 from jamesmunns/james/upstream-mcxa
Upstream `embassy-mcxa`
Diffstat (limited to 'embassy-mcxa')
| -rw-r--r-- | embassy-mcxa/.gitignore | 19 | ||||
| -rw-r--r-- | embassy-mcxa/Cargo.toml | 61 | ||||
| -rw-r--r-- | embassy-mcxa/README.md | 5 | ||||
| -rw-r--r-- | embassy-mcxa/src/adc.rs | 409 | ||||
| -rw-r--r-- | embassy-mcxa/src/clkout.rs | 169 | ||||
| -rw-r--r-- | embassy-mcxa/src/clocks/config.rs | 204 | ||||
| -rw-r--r-- | embassy-mcxa/src/clocks/mod.rs | 943 | ||||
| -rw-r--r-- | embassy-mcxa/src/clocks/periph_helpers.rs | 498 | ||||
| -rw-r--r-- | embassy-mcxa/src/config.rs | 27 | ||||
| -rw-r--r-- | embassy-mcxa/src/gpio.rs | 1062 | ||||
| -rw-r--r-- | embassy-mcxa/src/i2c/controller.rs | 742 | ||||
| -rw-r--r-- | embassy-mcxa/src/i2c/mod.rs | 194 | ||||
| -rw-r--r-- | embassy-mcxa/src/interrupt.rs | 563 | ||||
| -rw-r--r-- | embassy-mcxa/src/lib.rs | 471 | ||||
| -rw-r--r-- | embassy-mcxa/src/lpuart/buffered.rs | 780 | ||||
| -rw-r--r-- | embassy-mcxa/src/lpuart/mod.rs | 1292 | ||||
| -rw-r--r-- | embassy-mcxa/src/ostimer.rs | 745 | ||||
| -rw-r--r-- | embassy-mcxa/src/pins.rs | 28 | ||||
| -rw-r--r-- | embassy-mcxa/src/rtc.rs | 453 |
19 files changed, 8665 insertions, 0 deletions
diff --git a/embassy-mcxa/.gitignore b/embassy-mcxa/.gitignore new file mode 100644 index 000000000..d128a49cb --- /dev/null +++ b/embassy-mcxa/.gitignore | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | # Rust | ||
| 2 | /target/ | ||
| 3 | |||
| 4 | # IDE | ||
| 5 | .vscode/ | ||
| 6 | .idea/ | ||
| 7 | |||
| 8 | # OS | ||
| 9 | .DS_Store | ||
| 10 | Thumbs.db | ||
| 11 | |||
| 12 | # Embedded | ||
| 13 | *.bin | ||
| 14 | *.hex | ||
| 15 | *.elf | ||
| 16 | *.map | ||
| 17 | |||
| 18 | # Debug | ||
| 19 | *.log | ||
diff --git a/embassy-mcxa/Cargo.toml b/embassy-mcxa/Cargo.toml new file mode 100644 index 000000000..65328c751 --- /dev/null +++ b/embassy-mcxa/Cargo.toml | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | [package] | ||
| 2 | name = "embassy-mcxa" | ||
| 3 | version = "0.1.0" | ||
| 4 | edition = "2021" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | description = "Embassy Hardware Abstraction Layer (HAL) for NXP MCXA series of MCUs" | ||
| 7 | repository = "https://github.com/embassy-rs/embassy" | ||
| 8 | keywords = ["embedded", "hal", "nxp", "mcxa", "embassy"] | ||
| 9 | categories = ["embedded", "hardware-support", "no-std"] | ||
| 10 | documentation = "https://docs.embassy.dev/embassy-mcxa" | ||
| 11 | |||
| 12 | [package.metadata.embassy] | ||
| 13 | build = [ | ||
| 14 | {target = "thumbv8m.main-none-eabihf", features = ["defmt", "time", "unstable-pac"]}, | ||
| 15 | {target = "thumbv8m.main-none-eabihf", features = ["defmt", "time", "unstable-pac"]}, | ||
| 16 | ] | ||
| 17 | |||
| 18 | [dependencies] | ||
| 19 | cortex-m = { version = "0.7", features = ["critical-section-single-core"] } | ||
| 20 | # If you would like "device" to be an optional feature, please open an issue. | ||
| 21 | cortex-m-rt = { version = "0.7", features = ["device"] } | ||
| 22 | critical-section = "1.2.0" | ||
| 23 | defmt = { version = "1.0", optional = true } | ||
| 24 | embassy-embedded-hal = "0.5.0" | ||
| 25 | embassy-hal-internal = { version = "0.3.0", features = ["cortex-m", "prio-bits-3"] } | ||
| 26 | embassy-sync = "0.7.2" | ||
| 27 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | ||
| 28 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | ||
| 29 | embedded-hal-async = { version = "1.0" } | ||
| 30 | embedded-hal-nb = { version = "1.0" } | ||
| 31 | embedded-io = "0.6" | ||
| 32 | embedded-io-async = { version = "0.6.1" } | ||
| 33 | heapless = "0.8" | ||
| 34 | mcxa-pac = { git = "https://github.com/OpenDevicePartnership/mcxa-pac", features = ["rt", "critical-section"], version = "0.1.0", rev = "e7dfed8740b449b6ac646bab8ac6776a3c099267" } | ||
| 35 | nb = "1.1.0" | ||
| 36 | paste = "1.0.15" | ||
| 37 | maitake-sync = { version = "0.2.2", default-features = false, features = ["critical-section", "no-cache-pad"] } | ||
| 38 | |||
| 39 | # `time` dependencies | ||
| 40 | embassy-time = { version = "0.5.0", optional = true } | ||
| 41 | embassy-time-driver = { version = "0.2.1", optional = true } | ||
| 42 | |||
| 43 | [features] | ||
| 44 | default = ["rt"] | ||
| 45 | |||
| 46 | # Base defmt feature enables core + panic handler | ||
| 47 | # Use with one logger feature: defmt-rtt (preferred) or defmt-uart (fallback) | ||
| 48 | defmt = ["dep:defmt", "mcxa-pac/defmt"] | ||
| 49 | |||
| 50 | unstable-pac = [] | ||
| 51 | |||
| 52 | # dummy feature to silence embassy-hal-internal lint | ||
| 53 | # | ||
| 54 | # This feature makes no change to embassy-mcxa's operation. | ||
| 55 | rt = [] | ||
| 56 | |||
| 57 | # Embassy time | ||
| 58 | time = [ | ||
| 59 | "dep:embassy-time", | ||
| 60 | "dep:embassy-time-driver", | ||
| 61 | ] | ||
diff --git a/embassy-mcxa/README.md b/embassy-mcxa/README.md new file mode 100644 index 000000000..079eeb487 --- /dev/null +++ b/embassy-mcxa/README.md | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | # Embassy MCXA256 HAL | ||
| 2 | |||
| 3 | A Hardware Abstraction Layer (HAL) for the NXP MCXA256 microcontroller | ||
| 4 | using the Embassy async framework. This HAL provides safe, idiomatic | ||
| 5 | Rust interfaces for GPIO, UART, and OSTIMER peripherals. | ||
diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs new file mode 100644 index 000000000..7475299ba --- /dev/null +++ b/embassy-mcxa/src/adc.rs | |||
| @@ -0,0 +1,409 @@ | |||
| 1 | //! ADC driver | ||
| 2 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 3 | |||
| 4 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 5 | |||
| 6 | use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4}; | ||
| 7 | use crate::clocks::{Gate, PoweredClock, enable_and_reset}; | ||
| 8 | use crate::pac; | ||
| 9 | use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres}; | ||
| 10 | use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts}; | ||
| 11 | use crate::pac::adc1::cmdl1::{Adch, Ctype, Mode}; | ||
| 12 | use crate::pac::adc1::ctrl::CalAvgs; | ||
| 13 | use crate::pac::adc1::tctrl::{Tcmd, Tpri}; | ||
| 14 | |||
| 15 | type Regs = pac::adc1::RegisterBlock; | ||
| 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)] | ||
| 41 | #[repr(u8)] | ||
| 42 | pub enum TriggerPriorityPolicy { | ||
| 43 | ConvPreemptImmediatelyNotAutoResumed = 0, | ||
| 44 | ConvPreemptSoftlyNotAutoResumed = 1, | ||
| 45 | ConvPreemptImmediatelyAutoRestarted = 4, | ||
| 46 | ConvPreemptSoftlyAutoRestarted = 5, | ||
| 47 | ConvPreemptImmediatelyAutoResumed = 12, | ||
| 48 | ConvPreemptSoftlyAutoResumed = 13, | ||
| 49 | ConvPreemptSubsequentlyNotAutoResumed = 2, | ||
| 50 | ConvPreemptSubsequentlyAutoRestarted = 6, | ||
| 51 | ConvPreemptSubsequentlyAutoResumed = 14, | ||
| 52 | TriggerPriorityExceptionDisabled = 16, | ||
| 53 | } | ||
| 54 | |||
| 55 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 56 | pub struct LpadcConfig { | ||
| 57 | pub enable_in_doze_mode: bool, | ||
| 58 | pub conversion_average_mode: CalAvgs, | ||
| 59 | pub enable_analog_preliminary: bool, | ||
| 60 | pub power_up_delay: u8, | ||
| 61 | pub reference_voltage_source: Refsel, | ||
| 62 | pub power_level_mode: Pwrsel, | ||
| 63 | pub trigger_priority_policy: TriggerPriorityPolicy, | ||
| 64 | pub enable_conv_pause: bool, | ||
| 65 | pub conv_pause_delay: u16, | ||
| 66 | pub fifo_watermark: u8, | ||
| 67 | pub power: PoweredClock, | ||
| 68 | pub source: AdcClockSel, | ||
| 69 | pub div: Div4, | ||
| 70 | } | ||
| 71 | |||
| 72 | impl Default for LpadcConfig { | ||
| 73 | fn default() -> Self { | ||
| 74 | LpadcConfig { | ||
| 75 | enable_in_doze_mode: true, | ||
| 76 | conversion_average_mode: CalAvgs::NoAverage, | ||
| 77 | enable_analog_preliminary: false, | ||
| 78 | power_up_delay: 0x80, | ||
| 79 | reference_voltage_source: Refsel::Option1, | ||
| 80 | power_level_mode: Pwrsel::Lowest, | ||
| 81 | trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, | ||
| 82 | enable_conv_pause: false, | ||
| 83 | conv_pause_delay: 0, | ||
| 84 | fifo_watermark: 0, | ||
| 85 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 86 | source: AdcClockSel::FroLfDiv, | ||
| 87 | div: Div4::no_div(), | ||
| 88 | } | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 93 | pub struct ConvCommandConfig { | ||
| 94 | pub sample_channel_mode: Ctype, | ||
| 95 | pub channel_number: Adch, | ||
| 96 | pub chained_next_command_number: Next, | ||
| 97 | pub enable_auto_channel_increment: bool, | ||
| 98 | pub loop_count: u8, | ||
| 99 | pub hardware_average_mode: Avgs, | ||
| 100 | pub sample_time_mode: Sts, | ||
| 101 | pub hardware_compare_mode: Cmpen, | ||
| 102 | pub hardware_compare_value_high: u32, | ||
| 103 | pub hardware_compare_value_low: u32, | ||
| 104 | pub conversion_resolution_mode: Mode, | ||
| 105 | pub enable_wait_trigger: bool, | ||
| 106 | } | ||
| 107 | |||
| 108 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 109 | pub struct ConvTriggerConfig { | ||
| 110 | pub target_command_id: Tcmd, | ||
| 111 | pub delay_power: u8, | ||
| 112 | pub priority: Tpri, | ||
| 113 | pub enable_hardware_trigger: bool, | ||
| 114 | } | ||
| 115 | |||
| 116 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 117 | pub struct ConvResult { | ||
| 118 | pub command_id_source: u32, | ||
| 119 | pub loop_count_index: u32, | ||
| 120 | pub trigger_id_source: u32, | ||
| 121 | pub conv_value: u16, | ||
| 122 | } | ||
| 123 | |||
| 124 | pub struct Adc<'a, I: Instance> { | ||
| 125 | _inst: core::marker::PhantomData<&'a mut I>, | ||
| 126 | } | ||
| 127 | |||
| 128 | impl<'a, I: Instance> Adc<'a, I> { | ||
| 129 | /// initialize ADC | ||
| 130 | pub fn new(_inst: Peri<'a, I>, config: LpadcConfig) -> Self { | ||
| 131 | let adc = unsafe { &*I::ptr() }; | ||
| 132 | |||
| 133 | let _clock_freq = unsafe { | ||
| 134 | enable_and_reset::<I>(&AdcConfig { | ||
| 135 | power: config.power, | ||
| 136 | source: config.source, | ||
| 137 | div: config.div, | ||
| 138 | }) | ||
| 139 | .expect("Adc Init should not fail") | ||
| 140 | }; | ||
| 141 | |||
| 142 | /* Reset the module. */ | ||
| 143 | adc.ctrl().modify(|_, w| w.rst().held_in_reset()); | ||
| 144 | adc.ctrl().modify(|_, w| w.rst().released_from_reset()); | ||
| 145 | |||
| 146 | adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); | ||
| 147 | |||
| 148 | /* Disable the module before setting configuration. */ | ||
| 149 | adc.ctrl().modify(|_, w| w.adcen().disabled()); | ||
| 150 | |||
| 151 | /* Configure the module generally. */ | ||
| 152 | if 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 | |||
| 158 | /* Set calibration average mode. */ | ||
| 159 | adc.ctrl() | ||
| 160 | .modify(|_, w| w.cal_avgs().variant(config.conversion_average_mode)); | ||
| 161 | |||
| 162 | adc.cfg().write(|w| unsafe { | ||
| 163 | let w = if config.enable_analog_preliminary { | ||
| 164 | w.pwren().pre_enabled() | ||
| 165 | } else { | ||
| 166 | w | ||
| 167 | }; | ||
| 168 | |||
| 169 | w.pudly() | ||
| 170 | .bits(config.power_up_delay) | ||
| 171 | .refsel() | ||
| 172 | .variant(config.reference_voltage_source) | ||
| 173 | .pwrsel() | ||
| 174 | .variant(config.power_level_mode) | ||
| 175 | .tprictrl() | ||
| 176 | .variant(match config.trigger_priority_policy { | ||
| 177 | TriggerPriorityPolicy::ConvPreemptSoftlyNotAutoResumed | ||
| 178 | | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted | ||
| 179 | | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed => Tprictrl::FinishCurrentOnPriority, | ||
| 180 | TriggerPriorityPolicy::ConvPreemptSubsequentlyNotAutoResumed | ||
| 181 | | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted | ||
| 182 | | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tprictrl::FinishSequenceOnPriority, | ||
| 183 | _ => Tprictrl::AbortCurrentOnPriority, | ||
| 184 | }) | ||
| 185 | .tres() | ||
| 186 | .variant(match config.trigger_priority_policy { | ||
| 187 | TriggerPriorityPolicy::ConvPreemptImmediatelyAutoRestarted | ||
| 188 | | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted | ||
| 189 | | TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed | ||
| 190 | | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed | ||
| 191 | | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted | ||
| 192 | | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tres::Enabled, | ||
| 193 | _ => Tres::Disabled, | ||
| 194 | }) | ||
| 195 | .tcmdres() | ||
| 196 | .variant(match config.trigger_priority_policy { | ||
| 197 | TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed | ||
| 198 | | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed | ||
| 199 | | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed | ||
| 200 | | TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => Tcmdres::Enabled, | ||
| 201 | _ => Tcmdres::Disabled, | ||
| 202 | }) | ||
| 203 | .hpt_exdi() | ||
| 204 | .variant(match config.trigger_priority_policy { | ||
| 205 | TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => HptExdi::Disabled, | ||
| 206 | _ => HptExdi::Enabled, | ||
| 207 | }) | ||
| 208 | }); | ||
| 209 | |||
| 210 | if config.enable_conv_pause { | ||
| 211 | adc.pause() | ||
| 212 | .modify(|_, w| unsafe { w.pauseen().enabled().pausedly().bits(config.conv_pause_delay) }); | ||
| 213 | } else { | ||
| 214 | adc.pause().write(|w| unsafe { w.bits(0) }); | ||
| 215 | } | ||
| 216 | |||
| 217 | adc.fctrl0() | ||
| 218 | .write(|w| unsafe { w.fwmark().bits(config.fifo_watermark) }); | ||
| 219 | |||
| 220 | // Enable ADC | ||
| 221 | adc.ctrl().modify(|_, w| w.adcen().enabled()); | ||
| 222 | |||
| 223 | Self { | ||
| 224 | _inst: core::marker::PhantomData, | ||
| 225 | } | ||
| 226 | } | ||
| 227 | |||
| 228 | pub fn deinit(&self) { | ||
| 229 | let adc = unsafe { &*I::ptr() }; | ||
| 230 | adc.ctrl().modify(|_, w| w.adcen().disabled()); | ||
| 231 | } | ||
| 232 | |||
| 233 | pub fn do_offset_calibration(&self) { | ||
| 234 | let adc = unsafe { &*I::ptr() }; | ||
| 235 | // Enable calibration mode | ||
| 236 | adc.ctrl() | ||
| 237 | .modify(|_, w| w.calofs().offset_calibration_request_pending()); | ||
| 238 | |||
| 239 | // Wait for calibration to complete (polling status register) | ||
| 240 | while adc.stat().read().cal_rdy().is_not_set() {} | ||
| 241 | } | ||
| 242 | |||
| 243 | pub fn get_gain_conv_result(&self, mut gain_adjustment: f32) -> u32 { | ||
| 244 | let mut gcra_array = [0u32; 17]; | ||
| 245 | let mut gcalr: u32 = 0; | ||
| 246 | |||
| 247 | for i in (1..=17).rev() { | ||
| 248 | let shift = 16 - (i - 1); | ||
| 249 | let step = 1.0 / (1u32 << shift) as f32; | ||
| 250 | let tmp = (gain_adjustment / step) as u32; | ||
| 251 | gcra_array[i - 1] = tmp; | ||
| 252 | gain_adjustment -= tmp as f32 * step; | ||
| 253 | } | ||
| 254 | |||
| 255 | for i in (1..=17).rev() { | ||
| 256 | gcalr += gcra_array[i - 1] << (i - 1); | ||
| 257 | } | ||
| 258 | gcalr | ||
| 259 | } | ||
| 260 | |||
| 261 | pub fn do_auto_calibration(&self) { | ||
| 262 | let adc = unsafe { &*I::ptr() }; | ||
| 263 | adc.ctrl().modify(|_, w| w.cal_req().calibration_request_pending()); | ||
| 264 | |||
| 265 | while adc.gcc0().read().rdy().is_gain_cal_not_valid() {} | ||
| 266 | |||
| 267 | let mut gcca = adc.gcc0().read().gain_cal().bits() as u32; | ||
| 268 | if gcca & ((0xFFFF + 1) >> 1) != 0 { | ||
| 269 | gcca |= !0xFFFF; | ||
| 270 | } | ||
| 271 | |||
| 272 | let gcra = 131072.0 / (131072.0 - gcca as f32); | ||
| 273 | |||
| 274 | // Write to GCR0 | ||
| 275 | adc.gcr0().write(|w| unsafe { w.bits(self.get_gain_conv_result(gcra)) }); | ||
| 276 | |||
| 277 | adc.gcr0().modify(|_, w| w.rdy().set_bit()); | ||
| 278 | |||
| 279 | // Wait for calibration to complete (polling status register) | ||
| 280 | while adc.stat().read().cal_rdy().is_not_set() {} | ||
| 281 | } | ||
| 282 | |||
| 283 | pub fn do_software_trigger(&self, trigger_id_mask: u32) { | ||
| 284 | let adc = unsafe { &*I::ptr() }; | ||
| 285 | adc.swtrig().write(|w| unsafe { w.bits(trigger_id_mask) }); | ||
| 286 | } | ||
| 287 | |||
| 288 | pub fn get_default_conv_command_config(&self) -> ConvCommandConfig { | ||
| 289 | ConvCommandConfig { | ||
| 290 | sample_channel_mode: Ctype::SingleEndedASideChannel, | ||
| 291 | channel_number: Adch::SelectCh0, | ||
| 292 | chained_next_command_number: Next::NoNextCmdTerminateOnFinish, | ||
| 293 | enable_auto_channel_increment: false, | ||
| 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 | |||
| 305 | //TBD Need to add cmdlx and cmdhx with x {2..7} | ||
| 306 | pub fn set_conv_command_config(&self, index: u32, config: &ConvCommandConfig) { | ||
| 307 | let adc = unsafe { &*I::ptr() }; | ||
| 308 | |||
| 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 | } | ||
| 337 | _ => panic!("Invalid command index: must be between 1 and 7"), | ||
| 338 | } | ||
| 339 | } | ||
| 340 | |||
| 341 | pub fn get_default_conv_trigger_config(&self) -> ConvTriggerConfig { | ||
| 342 | ConvTriggerConfig { | ||
| 343 | target_command_id: Tcmd::NotValid, | ||
| 344 | delay_power: 0, | ||
| 345 | priority: Tpri::HighestPriority, | ||
| 346 | enable_hardware_trigger: false, | ||
| 347 | } | ||
| 348 | } | ||
| 349 | |||
| 350 | pub fn set_conv_trigger_config(&self, trigger_id: usize, config: &ConvTriggerConfig) { | ||
| 351 | let adc = unsafe { &*I::ptr() }; | ||
| 352 | let tctrl = &adc.tctrl(trigger_id); | ||
| 353 | |||
| 354 | tctrl.write(|w| unsafe { | ||
| 355 | let w = w.tcmd().variant(config.target_command_id); | ||
| 356 | let w = w.tdly().bits(config.delay_power); | ||
| 357 | w.tpri().variant(config.priority); | ||
| 358 | if config.enable_hardware_trigger { | ||
| 359 | w.hten().enabled() | ||
| 360 | } else { | ||
| 361 | w | ||
| 362 | } | ||
| 363 | }); | ||
| 364 | } | ||
| 365 | |||
| 366 | pub fn do_reset_fifo(&self) { | ||
| 367 | let adc = unsafe { &*I::ptr() }; | ||
| 368 | adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); | ||
| 369 | } | ||
| 370 | |||
| 371 | pub fn enable_interrupt(&self, mask: u32) { | ||
| 372 | let adc = unsafe { &*I::ptr() }; | ||
| 373 | adc.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) }); | ||
| 374 | INTERRUPT_TRIGGERED.store(false, Ordering::SeqCst); | ||
| 375 | } | ||
| 376 | |||
| 377 | pub fn is_interrupt_triggered(&self) -> bool { | ||
| 378 | INTERRUPT_TRIGGERED.load(Ordering::Relaxed) | ||
| 379 | } | ||
| 380 | } | ||
| 381 | |||
| 382 | pub fn get_conv_result() -> Option<ConvResult> { | ||
| 383 | let adc = unsafe { &*pac::Adc1::ptr() }; | ||
| 384 | let fifo = adc.resfifo0().read().bits(); | ||
| 385 | const VALID_MASK: u32 = 1 << 31; | ||
| 386 | if fifo & VALID_MASK == 0 { | ||
| 387 | return None; | ||
| 388 | } | ||
| 389 | |||
| 390 | Some(ConvResult { | ||
| 391 | command_id_source: (fifo >> 24) & 0x0F, | ||
| 392 | loop_count_index: (fifo >> 20) & 0x0F, | ||
| 393 | trigger_id_source: (fifo >> 16) & 0x0F, | ||
| 394 | conv_value: (fifo & 0xFFFF) as u16, | ||
| 395 | }) | ||
| 396 | } | ||
| 397 | |||
| 398 | pub fn on_interrupt() { | ||
| 399 | if get_conv_result().is_some() { | ||
| 400 | INTERRUPT_TRIGGERED.store(true, Ordering::SeqCst); | ||
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | pub struct AdcHandler; | ||
| 405 | impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::ADC1> for AdcHandler { | ||
| 406 | unsafe fn on_interrupt() { | ||
| 407 | on_interrupt(); | ||
| 408 | } | ||
| 409 | } | ||
diff --git a/embassy-mcxa/src/clkout.rs b/embassy-mcxa/src/clkout.rs new file mode 100644 index 000000000..5b21f24b0 --- /dev/null +++ b/embassy-mcxa/src/clkout.rs | |||
| @@ -0,0 +1,169 @@ | |||
| 1 | //! CLKOUT pseudo-peripheral | ||
| 2 | //! | ||
| 3 | //! CLKOUT is a part of the clock generation subsystem, and can be used | ||
| 4 | //! either to generate arbitrary waveforms, or to debug the state of | ||
| 5 | //! internal oscillators. | ||
| 6 | |||
| 7 | use core::marker::PhantomData; | ||
| 8 | |||
| 9 | use embassy_hal_internal::Peri; | ||
| 10 | |||
| 11 | pub use crate::clocks::periph_helpers::Div4; | ||
| 12 | use crate::clocks::{ClockError, PoweredClock, with_clocks}; | ||
| 13 | use crate::pac::mrcc0::mrcc_clkout_clksel::Mux; | ||
| 14 | use crate::peripherals::CLKOUT; | ||
| 15 | |||
| 16 | /// A peripheral representing the CLKOUT pseudo-peripheral | ||
| 17 | pub struct ClockOut<'a> { | ||
| 18 | _p: PhantomData<&'a mut CLKOUT>, | ||
| 19 | freq: u32, | ||
| 20 | } | ||
| 21 | |||
| 22 | /// Selected clock source to output | ||
| 23 | pub enum ClockOutSel { | ||
| 24 | /// 12MHz Internal Oscillator | ||
| 25 | Fro12M, | ||
| 26 | /// FRO180M Internal Oscillator, via divisor | ||
| 27 | FroHfDiv, | ||
| 28 | /// External Oscillator | ||
| 29 | ClkIn, | ||
| 30 | /// 16KHz oscillator | ||
| 31 | Clk16K, | ||
| 32 | /// Output of PLL1 | ||
| 33 | Pll1Clk, | ||
| 34 | /// Main System CPU clock, divided by 6 | ||
| 35 | SlowClk, | ||
| 36 | } | ||
| 37 | |||
| 38 | /// Configuration for the ClockOut | ||
| 39 | pub struct Config { | ||
| 40 | /// Selected Source Clock | ||
| 41 | pub sel: ClockOutSel, | ||
| 42 | /// Selected division level | ||
| 43 | pub div: Div4, | ||
| 44 | /// Selected power level | ||
| 45 | pub level: PoweredClock, | ||
| 46 | } | ||
| 47 | |||
| 48 | impl<'a> ClockOut<'a> { | ||
| 49 | /// Create a new ClockOut pin. On success, the clock signal will begin immediately | ||
| 50 | /// on the given pin. | ||
| 51 | pub fn new( | ||
| 52 | _peri: Peri<'a, CLKOUT>, | ||
| 53 | pin: Peri<'a, impl sealed::ClockOutPin>, | ||
| 54 | cfg: Config, | ||
| 55 | ) -> Result<Self, ClockError> { | ||
| 56 | // There's no MRCC enable bit, so we check the validity of the clocks here | ||
| 57 | // | ||
| 58 | // TODO: Should we check that the frequency is suitably low? | ||
| 59 | let (freq, mux) = check_sel(cfg.sel, cfg.level)?; | ||
| 60 | |||
| 61 | // All good! Apply requested config, starting with the pin. | ||
| 62 | pin.mux(); | ||
| 63 | |||
| 64 | setup_clkout(mux, cfg.div); | ||
| 65 | |||
| 66 | Ok(Self { | ||
| 67 | _p: PhantomData, | ||
| 68 | freq: freq / cfg.div.into_divisor(), | ||
| 69 | }) | ||
| 70 | } | ||
| 71 | |||
| 72 | /// Frequency of the clkout pin | ||
| 73 | #[inline] | ||
| 74 | pub fn frequency(&self) -> u32 { | ||
| 75 | self.freq | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | impl Drop for ClockOut<'_> { | ||
| 80 | fn drop(&mut self) { | ||
| 81 | disable_clkout(); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | /// Check whether the given clock selection is valid | ||
| 86 | fn check_sel(sel: ClockOutSel, level: PoweredClock) -> Result<(u32, Mux), ClockError> { | ||
| 87 | let res = with_clocks(|c| { | ||
| 88 | Ok(match sel { | ||
| 89 | ClockOutSel::Fro12M => (c.ensure_fro_hf_active(&level)?, Mux::Clkroot12m), | ||
| 90 | ClockOutSel::FroHfDiv => (c.ensure_fro_hf_div_active(&level)?, Mux::ClkrootFircDiv), | ||
| 91 | ClockOutSel::ClkIn => (c.ensure_clk_in_active(&level)?, Mux::ClkrootSosc), | ||
| 92 | ClockOutSel::Clk16K => (c.ensure_clk_16k_vdd_core_active(&level)?, Mux::Clkroot16k), | ||
| 93 | ClockOutSel::Pll1Clk => (c.ensure_pll1_clk_active(&level)?, Mux::ClkrootSpll), | ||
| 94 | ClockOutSel::SlowClk => (c.ensure_slow_clk_active(&level)?, Mux::ClkrootSlow), | ||
| 95 | }) | ||
| 96 | }); | ||
| 97 | let Some(res) = res else { | ||
| 98 | return Err(ClockError::NeverInitialized); | ||
| 99 | }; | ||
| 100 | res | ||
| 101 | } | ||
| 102 | |||
| 103 | /// Set up the clkout pin using the given mux and div settings | ||
| 104 | fn setup_clkout(mux: Mux, div: Div4) { | ||
| 105 | let mrcc = unsafe { crate::pac::Mrcc0::steal() }; | ||
| 106 | |||
| 107 | mrcc.mrcc_clkout_clksel().write(|w| w.mux().variant(mux)); | ||
| 108 | |||
| 109 | // Set up clkdiv | ||
| 110 | mrcc.mrcc_clkout_clkdiv().write(|w| { | ||
| 111 | w.halt().set_bit(); | ||
| 112 | w.reset().set_bit(); | ||
| 113 | unsafe { w.div().bits(div.into_bits()) }; | ||
| 114 | w | ||
| 115 | }); | ||
| 116 | mrcc.mrcc_clkout_clkdiv().write(|w| { | ||
| 117 | w.halt().clear_bit(); | ||
| 118 | w.reset().clear_bit(); | ||
| 119 | unsafe { w.div().bits(div.into_bits()) }; | ||
| 120 | w | ||
| 121 | }); | ||
| 122 | |||
| 123 | while mrcc.mrcc_clkout_clkdiv().read().unstab().bit_is_set() {} | ||
| 124 | } | ||
| 125 | |||
| 126 | /// Stop the clkout | ||
| 127 | fn disable_clkout() { | ||
| 128 | // Stop the output by selecting the "none" clock | ||
| 129 | // | ||
| 130 | // TODO: restore the pin to hi-z or something? | ||
| 131 | let mrcc = unsafe { crate::pac::Mrcc0::steal() }; | ||
| 132 | mrcc.mrcc_clkout_clkdiv().write(|w| { | ||
| 133 | w.reset().set_bit(); | ||
| 134 | w.halt().set_bit(); | ||
| 135 | unsafe { | ||
| 136 | w.div().bits(0); | ||
| 137 | } | ||
| 138 | w | ||
| 139 | }); | ||
| 140 | mrcc.mrcc_clkout_clksel().write(|w| unsafe { w.bits(0b111) }); | ||
| 141 | } | ||
| 142 | |||
| 143 | mod sealed { | ||
| 144 | use embassy_hal_internal::PeripheralType; | ||
| 145 | |||
| 146 | use crate::gpio::{Pull, SealedPin}; | ||
| 147 | |||
| 148 | /// Sealed marker trait for clockout pins | ||
| 149 | pub trait ClockOutPin: PeripheralType { | ||
| 150 | /// Set the given pin to the correct muxing state | ||
| 151 | fn mux(&self); | ||
| 152 | } | ||
| 153 | |||
| 154 | macro_rules! impl_pin { | ||
| 155 | ($pin:ident, $func:ident) => { | ||
| 156 | impl ClockOutPin for crate::peripherals::$pin { | ||
| 157 | fn mux(&self) { | ||
| 158 | self.set_function(crate::pac::port0::pcr0::Mux::$func); | ||
| 159 | self.set_pull(Pull::Disabled); | ||
| 160 | } | ||
| 161 | } | ||
| 162 | }; | ||
| 163 | } | ||
| 164 | |||
| 165 | impl_pin!(P0_6, Mux12); | ||
| 166 | impl_pin!(P3_6, Mux1); | ||
| 167 | impl_pin!(P3_8, Mux12); | ||
| 168 | impl_pin!(P4_2, Mux1); | ||
| 169 | } | ||
diff --git a/embassy-mcxa/src/clocks/config.rs b/embassy-mcxa/src/clocks/config.rs new file mode 100644 index 000000000..0563b8917 --- /dev/null +++ b/embassy-mcxa/src/clocks/config.rs | |||
| @@ -0,0 +1,204 @@ | |||
| 1 | //! Clock Configuration | ||
| 2 | //! | ||
| 3 | //! This module holds configuration types used for the system clocks. For | ||
| 4 | //! configuration of individual peripherals, see [`super::periph_helpers`]. | ||
| 5 | |||
| 6 | use super::PoweredClock; | ||
| 7 | |||
| 8 | /// This type represents a divider in the range 1..=256. | ||
| 9 | /// | ||
| 10 | /// At a hardware level, this is an 8-bit register from 0..=255, | ||
| 11 | /// which adds one. | ||
| 12 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 13 | pub struct Div8(pub(super) u8); | ||
| 14 | |||
| 15 | impl Div8 { | ||
| 16 | /// Store a "raw" divisor value that will divide the source by | ||
| 17 | /// `(n + 1)`, e.g. `Div8::from_raw(0)` will divide the source | ||
| 18 | /// by 1, and `Div8::from_raw(255)` will divide the source by | ||
| 19 | /// 256. | ||
| 20 | pub const fn from_raw(n: u8) -> Self { | ||
| 21 | Self(n) | ||
| 22 | } | ||
| 23 | |||
| 24 | /// Divide by one, or no division | ||
| 25 | pub const fn no_div() -> Self { | ||
| 26 | Self(0) | ||
| 27 | } | ||
| 28 | |||
| 29 | /// Store a specific divisor value that will divide the source | ||
| 30 | /// by `n`. e.g. `Div8::from_divisor(1)` will divide the source | ||
| 31 | /// by 1, and `Div8::from_divisor(256)` will divide the source | ||
| 32 | /// by 256. | ||
| 33 | /// | ||
| 34 | /// Will return `None` if `n` is not in the range `1..=256`. | ||
| 35 | /// Consider [`Self::from_raw`] for an infallible version. | ||
| 36 | pub const fn from_divisor(n: u16) -> Option<Self> { | ||
| 37 | let Some(n) = n.checked_sub(1) else { | ||
| 38 | return None; | ||
| 39 | }; | ||
| 40 | if n > (u8::MAX as u16) { | ||
| 41 | return None; | ||
| 42 | } | ||
| 43 | Some(Self(n as u8)) | ||
| 44 | } | ||
| 45 | |||
| 46 | /// Convert into "raw" bits form | ||
| 47 | #[inline(always)] | ||
| 48 | pub const fn into_bits(self) -> u8 { | ||
| 49 | self.0 | ||
| 50 | } | ||
| 51 | |||
| 52 | /// Convert into "divisor" form, as a u32 for convenient frequency math | ||
| 53 | #[inline(always)] | ||
| 54 | pub const fn into_divisor(self) -> u32 { | ||
| 55 | self.0 as u32 + 1 | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | /// ```text | ||
| 60 | /// ┌─────────────────────────────────────────────────────────┐ | ||
| 61 | /// │ │ | ||
| 62 | /// │ ┌───────────┐ clk_out ┌─────────┐ │ | ||
| 63 | /// XTAL ──────┼──▷│ System │───────────▷│ │ clk_in │ | ||
| 64 | /// │ │ OSC │ clkout_byp │ MUX │──────────────────┼──────▷ | ||
| 65 | /// EXTAL ──────┼──▷│ │───────────▷│ │ │ | ||
| 66 | /// │ └───────────┘ └─────────┘ │ | ||
| 67 | /// │ │ | ||
| 68 | /// │ ┌───────────┐ fro_hf_root ┌────┐ fro_hf │ | ||
| 69 | /// │ │ FRO180 ├───────┬─────▷│ CG │─────────────────────┼──────▷ | ||
| 70 | /// │ │ │ │ ├────┤ clk_45m │ | ||
| 71 | /// │ │ │ └─────▷│ CG │─────────────────────┼──────▷ | ||
| 72 | /// │ └───────────┘ └────┘ │ | ||
| 73 | /// │ ┌───────────┐ fro_12m_root ┌────┐ fro_12m │ | ||
| 74 | /// │ │ FRO12M │────────┬─────▷│ CG │────────────────────┼──────▷ | ||
| 75 | /// │ │ │ │ ├────┤ clk_1m │ | ||
| 76 | /// │ │ │ └─────▷│1/12│────────────────────┼──────▷ | ||
| 77 | /// │ └───────────┘ └────┘ │ | ||
| 78 | /// │ │ | ||
| 79 | /// │ ┌──────────┐ │ | ||
| 80 | /// │ │000 │ │ | ||
| 81 | /// │ clk_in │ │ │ | ||
| 82 | /// │ ───────────────▷│001 │ │ | ||
| 83 | /// │ fro_12m │ │ │ | ||
| 84 | /// │ ───────────────▷│010 │ │ | ||
| 85 | /// │ fro_hf_root │ │ │ | ||
| 86 | /// │ ───────────────▷│011 │ main_clk │ | ||
| 87 | /// │ │ │───────────────────────────┼──────▷ | ||
| 88 | /// clk_16k ──────┼─────────────────▷│100 │ │ | ||
| 89 | /// │ none │ │ │ | ||
| 90 | /// │ ───────────────▷│101 │ │ | ||
| 91 | /// │ pll1_clk │ │ │ | ||
| 92 | /// │ ───────────────▷│110 │ │ | ||
| 93 | /// │ none │ │ │ | ||
| 94 | /// │ ───────────────▷│111 │ │ | ||
| 95 | /// │ └──────────┘ │ | ||
| 96 | /// │ ▲ │ | ||
| 97 | /// │ │ │ | ||
| 98 | /// │ SCG SCS │ | ||
| 99 | /// │ SCG-Lite │ | ||
| 100 | /// └─────────────────────────────────────────────────────────┘ | ||
| 101 | /// | ||
| 102 | /// | ||
| 103 | /// clk_in ┌─────┐ | ||
| 104 | /// ───────────────▷│00 │ | ||
| 105 | /// clk_45m │ │ | ||
| 106 | /// ───────────────▷│01 │ ┌───────────┐ pll1_clk | ||
| 107 | /// none │ │─────▷│ SPLL │───────────────▷ | ||
| 108 | /// ───────────────▷│10 │ └───────────┘ | ||
| 109 | /// fro_12m │ │ | ||
| 110 | /// ───────────────▷│11 │ | ||
| 111 | /// └─────┘ | ||
| 112 | /// ``` | ||
| 113 | #[non_exhaustive] | ||
| 114 | pub struct ClocksConfig { | ||
| 115 | /// FIRC, FRO180, 45/60/90/180M clock source | ||
| 116 | pub firc: Option<FircConfig>, | ||
| 117 | /// SIRC, FRO12M, clk_12m clock source | ||
| 118 | // NOTE: I don't think we *can* disable the SIRC? | ||
| 119 | pub sirc: SircConfig, | ||
| 120 | /// FRO16K clock source | ||
| 121 | pub fro16k: Option<Fro16KConfig>, | ||
| 122 | } | ||
| 123 | |||
| 124 | // FIRC/FRO180M | ||
| 125 | |||
| 126 | /// ```text | ||
| 127 | /// ┌───────────┐ fro_hf_root ┌────┐ fro_hf | ||
| 128 | /// │ FRO180M ├───────┬─────▷│GATE│──────────▷ | ||
| 129 | /// │ │ │ ├────┤ clk_45m | ||
| 130 | /// │ │ └─────▷│GATE│──────────▷ | ||
| 131 | /// └───────────┘ └────┘ | ||
| 132 | /// ``` | ||
| 133 | #[non_exhaustive] | ||
| 134 | pub struct FircConfig { | ||
| 135 | /// Selected clock frequency | ||
| 136 | pub frequency: FircFreqSel, | ||
| 137 | /// Selected power state of the clock | ||
| 138 | pub power: PoweredClock, | ||
| 139 | /// Is the "fro_hf" gated clock enabled? | ||
| 140 | pub fro_hf_enabled: bool, | ||
| 141 | /// Is the "clk_45m" gated clock enabled? | ||
| 142 | pub clk_45m_enabled: bool, | ||
| 143 | /// Is the "fro_hf_div" clock enabled? Requires `fro_hf`! | ||
| 144 | pub fro_hf_div: Option<Div8>, | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Selected FIRC frequency | ||
| 148 | pub enum FircFreqSel { | ||
| 149 | /// 45MHz Output | ||
| 150 | Mhz45, | ||
| 151 | /// 60MHz Output | ||
| 152 | Mhz60, | ||
| 153 | /// 90MHz Output | ||
| 154 | Mhz90, | ||
| 155 | /// 180MHz Output | ||
| 156 | Mhz180, | ||
| 157 | } | ||
| 158 | |||
| 159 | // SIRC/FRO12M | ||
| 160 | |||
| 161 | /// ```text | ||
| 162 | /// ┌───────────┐ fro_12m_root ┌────┐ fro_12m | ||
| 163 | /// │ FRO12M │────────┬─────▷│ CG │──────────▷ | ||
| 164 | /// │ │ │ ├────┤ clk_1m | ||
| 165 | /// │ │ └─────▷│1/12│──────────▷ | ||
| 166 | /// └───────────┘ └────┘ | ||
| 167 | /// ``` | ||
| 168 | #[non_exhaustive] | ||
| 169 | pub struct SircConfig { | ||
| 170 | pub power: PoweredClock, | ||
| 171 | // peripheral output, aka sirc_12mhz | ||
| 172 | pub fro_12m_enabled: bool, | ||
| 173 | /// Is the "fro_lf_div" clock enabled? Requires `fro_12m`! | ||
| 174 | pub fro_lf_div: Option<Div8>, | ||
| 175 | } | ||
| 176 | |||
| 177 | #[non_exhaustive] | ||
| 178 | pub struct Fro16KConfig { | ||
| 179 | pub vsys_domain_active: bool, | ||
| 180 | pub vdd_core_domain_active: bool, | ||
| 181 | } | ||
| 182 | |||
| 183 | impl Default for ClocksConfig { | ||
| 184 | fn default() -> Self { | ||
| 185 | Self { | ||
| 186 | firc: Some(FircConfig { | ||
| 187 | frequency: FircFreqSel::Mhz45, | ||
| 188 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 189 | fro_hf_enabled: true, | ||
| 190 | clk_45m_enabled: true, | ||
| 191 | fro_hf_div: None, | ||
| 192 | }), | ||
| 193 | sirc: SircConfig { | ||
| 194 | power: PoweredClock::AlwaysEnabled, | ||
| 195 | fro_12m_enabled: true, | ||
| 196 | fro_lf_div: None, | ||
| 197 | }, | ||
| 198 | fro16k: Some(Fro16KConfig { | ||
| 199 | vsys_domain_active: true, | ||
| 200 | vdd_core_domain_active: true, | ||
| 201 | }), | ||
| 202 | } | ||
| 203 | } | ||
| 204 | } | ||
diff --git a/embassy-mcxa/src/clocks/mod.rs b/embassy-mcxa/src/clocks/mod.rs new file mode 100644 index 000000000..9c9e6ef3d --- /dev/null +++ b/embassy-mcxa/src/clocks/mod.rs | |||
| @@ -0,0 +1,943 @@ | |||
| 1 | //! # Clock Module | ||
| 2 | //! | ||
| 3 | //! For the MCX-A, we separate clock and peripheral control into two main stages: | ||
| 4 | //! | ||
| 5 | //! 1. At startup, e.g. when `embassy_mcxa::init()` is called, we configure the | ||
| 6 | //! core system clocks, including external and internal oscillators. This | ||
| 7 | //! configuration is then largely static for the duration of the program. | ||
| 8 | //! 2. When HAL drivers are created, e.g. `Lpuart::new()` is called, the driver | ||
| 9 | //! is responsible for two main things: | ||
| 10 | //! * Ensuring that any required "upstream" core system clocks necessary for | ||
| 11 | //! clocking the peripheral is active and configured to a reasonable value | ||
| 12 | //! * Enabling the clock gates for that peripheral, and resetting the peripheral | ||
| 13 | //! | ||
| 14 | //! From a user perspective, only step 1 is visible. Step 2 is automatically handled | ||
| 15 | //! by HAL drivers, using interfaces defined in this module. | ||
| 16 | //! | ||
| 17 | //! It is also possible to *view* the state of the clock configuration after [`init()`] | ||
| 18 | //! has been called, using the [`with_clocks()`] function, which provides a view of the | ||
| 19 | //! [`Clocks`] structure. | ||
| 20 | //! | ||
| 21 | //! ## For HAL driver implementors | ||
| 22 | //! | ||
| 23 | //! The majority of peripherals in the MCXA chip are fed from either a "hard-coded" or | ||
| 24 | //! configurable clock source, e.g. selecting the FROM12M or `clk_1m` as a source. This | ||
| 25 | //! selection, as well as often any pre-scaler division from that source clock, is made | ||
| 26 | //! through MRCC registers. | ||
| 27 | //! | ||
| 28 | //! Any peripheral that is controlled through the MRCC register can automatically implement | ||
| 29 | //! the necessary APIs using the `impl_cc_gate!` macro in this module. You will also need | ||
| 30 | //! to define the configuration surface and steps necessary to fully configure that peripheral | ||
| 31 | //! from a clocks perspective by: | ||
| 32 | //! | ||
| 33 | //! 1. Defining a configuration type in the [`periph_helpers`] module that contains any selects | ||
| 34 | //! or divisions available to the HAL driver | ||
| 35 | //! 2. Implementing the [`periph_helpers::SPConfHelper`] trait, which should check that the | ||
| 36 | //! necessary input clocks are reasonable | ||
| 37 | |||
| 38 | use core::cell::RefCell; | ||
| 39 | |||
| 40 | use config::{ClocksConfig, FircConfig, FircFreqSel, Fro16KConfig, SircConfig}; | ||
| 41 | use mcxa_pac::scg0::firccsr::{FircFclkPeriphEn, FircSclkPeriphEn, Fircsten}; | ||
| 42 | use mcxa_pac::scg0::sirccsr::{SircClkPeriphEn, Sircsten}; | ||
| 43 | use periph_helpers::SPConfHelper; | ||
| 44 | |||
| 45 | use crate::pac; | ||
| 46 | pub mod config; | ||
| 47 | pub mod periph_helpers; | ||
| 48 | |||
| 49 | // | ||
| 50 | // Statics/Consts | ||
| 51 | // | ||
| 52 | |||
| 53 | /// The state of system core clocks. | ||
| 54 | /// | ||
| 55 | /// Initialized by [`init()`], and then unchanged for the remainder of the program. | ||
| 56 | static CLOCKS: critical_section::Mutex<RefCell<Option<Clocks>>> = critical_section::Mutex::new(RefCell::new(None)); | ||
| 57 | |||
| 58 | // | ||
| 59 | // Free functions | ||
| 60 | // | ||
| 61 | |||
| 62 | /// Initialize the core system clocks with the given [`ClocksConfig`]. | ||
| 63 | /// | ||
| 64 | /// This function should be called EXACTLY once at start-up, usually via a | ||
| 65 | /// call to [`embassy_mcxa::init()`](crate::init()). Subsequent calls will | ||
| 66 | /// return an error. | ||
| 67 | pub fn init(settings: ClocksConfig) -> Result<(), ClockError> { | ||
| 68 | critical_section::with(|cs| { | ||
| 69 | if CLOCKS.borrow_ref(cs).is_some() { | ||
| 70 | Err(ClockError::AlreadyInitialized) | ||
| 71 | } else { | ||
| 72 | Ok(()) | ||
| 73 | } | ||
| 74 | })?; | ||
| 75 | |||
| 76 | let mut clocks = Clocks::default(); | ||
| 77 | let mut operator = ClockOperator { | ||
| 78 | clocks: &mut clocks, | ||
| 79 | config: &settings, | ||
| 80 | |||
| 81 | _mrcc0: unsafe { pac::Mrcc0::steal() }, | ||
| 82 | scg0: unsafe { pac::Scg0::steal() }, | ||
| 83 | syscon: unsafe { pac::Syscon::steal() }, | ||
| 84 | vbat0: unsafe { pac::Vbat0::steal() }, | ||
| 85 | }; | ||
| 86 | |||
| 87 | operator.configure_firc_clocks()?; | ||
| 88 | operator.configure_sirc_clocks()?; | ||
| 89 | operator.configure_fro16k_clocks()?; | ||
| 90 | |||
| 91 | // For now, just use FIRC as the main/cpu clock, which should already be | ||
| 92 | // the case on reset | ||
| 93 | assert!(operator.scg0.rccr().read().scs().is_firc()); | ||
| 94 | let input = operator.clocks.fro_hf_root.clone().unwrap(); | ||
| 95 | operator.clocks.main_clk = Some(input.clone()); | ||
| 96 | // We can also assume cpu/system clk == fro_hf because div is /1. | ||
| 97 | assert_eq!(operator.syscon.ahbclkdiv().read().div().bits(), 0); | ||
| 98 | operator.clocks.cpu_system_clk = Some(input); | ||
| 99 | |||
| 100 | critical_section::with(|cs| { | ||
| 101 | let mut clks = CLOCKS.borrow_ref_mut(cs); | ||
| 102 | assert!(clks.is_none(), "Clock setup race!"); | ||
| 103 | *clks = Some(clocks); | ||
| 104 | }); | ||
| 105 | |||
| 106 | Ok(()) | ||
| 107 | } | ||
| 108 | |||
| 109 | /// Obtain the full clocks structure, calling the given closure in a critical section. | ||
| 110 | /// | ||
| 111 | /// The given closure will be called with read-only access to the state of the system | ||
| 112 | /// clocks. This can be used to query and return the state of a given clock. | ||
| 113 | /// | ||
| 114 | /// As the caller's closure will be called in a critical section, care must be taken | ||
| 115 | /// not to block or cause any other undue delays while accessing. | ||
| 116 | /// | ||
| 117 | /// Calls to this function will not succeed until after a successful call to `init()`, | ||
| 118 | /// and will always return None. | ||
| 119 | pub fn with_clocks<R: 'static, F: FnOnce(&Clocks) -> R>(f: F) -> Option<R> { | ||
| 120 | critical_section::with(|cs| { | ||
| 121 | let c = CLOCKS.borrow_ref(cs); | ||
| 122 | let c = c.as_ref()?; | ||
| 123 | Some(f(c)) | ||
| 124 | }) | ||
| 125 | } | ||
| 126 | |||
| 127 | // | ||
| 128 | // Structs/Enums | ||
| 129 | // | ||
| 130 | |||
| 131 | /// The `Clocks` structure contains the initialized state of the core system clocks | ||
| 132 | /// | ||
| 133 | /// These values are configured by providing [`config::ClocksConfig`] to the [`init()`] function | ||
| 134 | /// at boot time. | ||
| 135 | #[derive(Default, Debug, Clone)] | ||
| 136 | #[non_exhaustive] | ||
| 137 | pub struct Clocks { | ||
| 138 | /// The `clk_in` is a clock provided by an external oscillator | ||
| 139 | pub clk_in: Option<Clock>, | ||
| 140 | |||
| 141 | // FRO180M stuff | ||
| 142 | // | ||
| 143 | /// `fro_hf_root` is the direct output of the `FRO180M` internal oscillator | ||
| 144 | /// | ||
| 145 | /// It is used to feed downstream clocks, such as `fro_hf`, `clk_45m`, | ||
| 146 | /// and `fro_hf_div`. | ||
| 147 | pub fro_hf_root: Option<Clock>, | ||
| 148 | |||
| 149 | /// `fro_hf` is the same frequency as `fro_hf_root`, but behind a gate. | ||
| 150 | pub fro_hf: Option<Clock>, | ||
| 151 | |||
| 152 | /// `clk_45` is a 45MHz clock, sourced from `fro_hf`. | ||
| 153 | pub clk_45m: Option<Clock>, | ||
| 154 | |||
| 155 | /// `fro_hf_div` is a configurable frequency clock, sourced from `fro_hf`. | ||
| 156 | pub fro_hf_div: Option<Clock>, | ||
| 157 | |||
| 158 | // | ||
| 159 | // End FRO180M | ||
| 160 | |||
| 161 | // FRO12M stuff | ||
| 162 | // | ||
| 163 | /// `fro_12m_root` is the direct output of the `FRO12M` internal oscillator | ||
| 164 | /// | ||
| 165 | /// It is used to feed downstream clocks, such as `fro_12m`, `clk_1m`, | ||
| 166 | /// `and `fro_lf_div`. | ||
| 167 | pub fro_12m_root: Option<Clock>, | ||
| 168 | |||
| 169 | /// `fro_12m` is the same frequency as `fro_12m_root`, but behind a gate. | ||
| 170 | pub fro_12m: Option<Clock>, | ||
| 171 | |||
| 172 | /// `clk_1m` is a 1MHz clock, sourced from `fro_12m` | ||
| 173 | pub clk_1m: Option<Clock>, | ||
| 174 | |||
| 175 | /// `fro_lf_div` is a configurable frequency clock, sourced from `fro_12m` | ||
| 176 | pub fro_lf_div: Option<Clock>, | ||
| 177 | // | ||
| 178 | // End FRO12M stuff | ||
| 179 | /// `clk_16k_vsys` is one of two outputs of the `FRO16K` internal oscillator. | ||
| 180 | /// | ||
| 181 | /// Also referred to as `clk_16k[0]` in the datasheet, it feeds peripherals in | ||
| 182 | /// the system domain, such as the CMP and RTC. | ||
| 183 | pub clk_16k_vsys: Option<Clock>, | ||
| 184 | |||
| 185 | /// `clk_16k_vdd_core` is one of two outputs of the `FRO16K` internal oscillator. | ||
| 186 | /// | ||
| 187 | /// Also referred to as `clk_16k[1]` in the datasheet, it feeds peripherals in | ||
| 188 | /// the VDD Core domain, such as the OSTimer or LPUarts. | ||
| 189 | pub clk_16k_vdd_core: Option<Clock>, | ||
| 190 | |||
| 191 | /// `main_clk` is the main clock used by the CPU, AHB, APB, IPS bus, and some | ||
| 192 | /// peripherals. | ||
| 193 | pub main_clk: Option<Clock>, | ||
| 194 | |||
| 195 | /// `CPU_CLK` or `SYSTEM_CLK` is the output of `main_clk`, run through the `AHBCLKDIV` | ||
| 196 | pub cpu_system_clk: Option<Clock>, | ||
| 197 | |||
| 198 | /// `pll1_clk` is the output of the main system PLL, `pll1`. | ||
| 199 | pub pll1_clk: Option<Clock>, | ||
| 200 | } | ||
| 201 | |||
| 202 | /// `ClockError` is the main error returned when configuring or checking clock state | ||
| 203 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 204 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 205 | #[non_exhaustive] | ||
| 206 | pub enum ClockError { | ||
| 207 | /// The system clocks were never initialized by calling [`init()`] | ||
| 208 | NeverInitialized, | ||
| 209 | /// The [`init()`] function was called more than once | ||
| 210 | AlreadyInitialized, | ||
| 211 | /// The requested configuration was not possible to fulfill, as the system clocks | ||
| 212 | /// were not configured in a compatible way | ||
| 213 | BadConfig { clock: &'static str, reason: &'static str }, | ||
| 214 | /// The requested configuration was not possible to fulfill, as the required system | ||
| 215 | /// clocks have not yet been implemented. | ||
| 216 | NotImplemented { clock: &'static str }, | ||
| 217 | /// The requested peripheral could not be configured, as the steps necessary to | ||
| 218 | /// enable it have not yet been implemented. | ||
| 219 | UnimplementedConfig, | ||
| 220 | } | ||
| 221 | |||
| 222 | /// Information regarding a system clock | ||
| 223 | #[derive(Debug, Clone)] | ||
| 224 | pub struct Clock { | ||
| 225 | /// The frequency, in Hz, of the given clock | ||
| 226 | pub frequency: u32, | ||
| 227 | /// The power state of the clock, e.g. whether it is active in deep sleep mode | ||
| 228 | /// or not. | ||
| 229 | pub power: PoweredClock, | ||
| 230 | } | ||
| 231 | |||
| 232 | /// The power state of a given clock. | ||
| 233 | /// | ||
| 234 | /// On the MCX-A, when Deep-Sleep is entered, any clock not configured for Deep Sleep | ||
| 235 | /// mode will be stopped. This means that any downstream usage, e.g. by peripherals, | ||
| 236 | /// will also stop. | ||
| 237 | /// | ||
| 238 | /// In the future, we will provide an API for entering Deep Sleep, and if there are | ||
| 239 | /// any peripherals that are NOT using an `AlwaysEnabled` clock active, entry into | ||
| 240 | /// Deep Sleep will be prevented, in order to avoid misbehaving peripherals. | ||
| 241 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 242 | pub enum PoweredClock { | ||
| 243 | /// The given clock will NOT continue running in Deep Sleep mode | ||
| 244 | NormalEnabledDeepSleepDisabled, | ||
| 245 | /// The given clock WILL continue running in Deep Sleep mode | ||
| 246 | AlwaysEnabled, | ||
| 247 | } | ||
| 248 | |||
| 249 | /// The ClockOperator is a private helper type that contains the methods used | ||
| 250 | /// during system clock initialization. | ||
| 251 | /// | ||
| 252 | /// # SAFETY | ||
| 253 | /// | ||
| 254 | /// Concurrent access to clock-relevant peripheral registers, such as `MRCC`, `SCG`, | ||
| 255 | /// `SYSCON`, and `VBAT` should not be allowed for the duration of the [`init()`] function. | ||
| 256 | struct ClockOperator<'a> { | ||
| 257 | /// A mutable reference to the current state of system clocks | ||
| 258 | clocks: &'a mut Clocks, | ||
| 259 | /// A reference to the requested configuration provided by the caller of [`init()`] | ||
| 260 | config: &'a ClocksConfig, | ||
| 261 | |||
| 262 | // We hold on to stolen peripherals | ||
| 263 | _mrcc0: pac::Mrcc0, | ||
| 264 | scg0: pac::Scg0, | ||
| 265 | syscon: pac::Syscon, | ||
| 266 | vbat0: pac::Vbat0, | ||
| 267 | } | ||
| 268 | |||
| 269 | /// Trait describing an AHB clock gate that can be toggled through MRCC. | ||
| 270 | pub trait Gate { | ||
| 271 | type MrccPeriphConfig: SPConfHelper; | ||
| 272 | |||
| 273 | /// Enable the clock gate. | ||
| 274 | /// | ||
| 275 | /// # SAFETY | ||
| 276 | /// | ||
| 277 | /// The current peripheral must be disabled prior to calling this method | ||
| 278 | unsafe fn enable_clock(); | ||
| 279 | |||
| 280 | /// Disable the clock gate. | ||
| 281 | /// | ||
| 282 | /// # SAFETY | ||
| 283 | /// | ||
| 284 | /// There must be no active user of this peripheral when calling this method | ||
| 285 | unsafe fn disable_clock(); | ||
| 286 | |||
| 287 | /// Drive the peripheral into reset. | ||
| 288 | /// | ||
| 289 | /// # SAFETY | ||
| 290 | /// | ||
| 291 | /// There must be no active user of this peripheral when calling this method | ||
| 292 | unsafe fn assert_reset(); | ||
| 293 | |||
| 294 | /// Drive the peripheral out of reset. | ||
| 295 | /// | ||
| 296 | /// # SAFETY | ||
| 297 | /// | ||
| 298 | /// There must be no active user of this peripheral when calling this method | ||
| 299 | unsafe fn release_reset(); | ||
| 300 | |||
| 301 | /// Return whether the clock gate for this peripheral is currently enabled. | ||
| 302 | fn is_clock_enabled() -> bool; | ||
| 303 | |||
| 304 | /// Return whether the peripheral is currently held in reset. | ||
| 305 | fn is_reset_released() -> bool; | ||
| 306 | } | ||
| 307 | |||
| 308 | /// This is the primary helper method HAL drivers are expected to call when creating | ||
| 309 | /// an instance of the peripheral. | ||
| 310 | /// | ||
| 311 | /// This method: | ||
| 312 | /// | ||
| 313 | /// 1. Enables the MRCC clock gate for this peripheral | ||
| 314 | /// 2. Calls the `G::MrccPeriphConfig::post_enable_config()` method, returning an error | ||
| 315 | /// and re-disabling the peripheral if this fails. | ||
| 316 | /// 3. Pulses the MRCC reset line, to reset the peripheral to the default state | ||
| 317 | /// 4. Returns the frequency, in Hz that is fed into the peripheral, taking into account | ||
| 318 | /// the selected upstream clock, as well as any division specified by `cfg`. | ||
| 319 | /// | ||
| 320 | /// NOTE: if a clock is disabled, sourced from an "ambient" clock source, this method | ||
| 321 | /// may return `Ok(0)`. In the future, this might be updated to return the correct | ||
| 322 | /// "ambient" clock, e.g. the AHB/APB frequency. | ||
| 323 | /// | ||
| 324 | /// # SAFETY | ||
| 325 | /// | ||
| 326 | /// This peripheral must not yet be in use prior to calling `enable_and_reset`. | ||
| 327 | #[inline] | ||
| 328 | pub unsafe fn enable_and_reset<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> { | ||
| 329 | let freq = enable::<G>(cfg).inspect_err(|_| disable::<G>())?; | ||
| 330 | pulse_reset::<G>(); | ||
| 331 | Ok(freq) | ||
| 332 | } | ||
| 333 | |||
| 334 | /// Enable the clock gate for the given peripheral. | ||
| 335 | /// | ||
| 336 | /// Prefer [`enable_and_reset`] unless you are specifically avoiding a pulse of the reset, or need | ||
| 337 | /// to control the duration of the pulse more directly. | ||
| 338 | /// | ||
| 339 | /// # SAFETY | ||
| 340 | /// | ||
| 341 | /// This peripheral must not yet be in use prior to calling `enable`. | ||
| 342 | #[inline] | ||
| 343 | pub unsafe fn enable<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> { | ||
| 344 | G::enable_clock(); | ||
| 345 | while !G::is_clock_enabled() {} | ||
| 346 | core::arch::asm!("dsb sy; isb sy", options(nomem, nostack, preserves_flags)); | ||
| 347 | |||
| 348 | let freq = critical_section::with(|cs| { | ||
| 349 | let clocks = CLOCKS.borrow_ref(cs); | ||
| 350 | let clocks = clocks.as_ref().ok_or(ClockError::NeverInitialized)?; | ||
| 351 | cfg.post_enable_config(clocks) | ||
| 352 | }); | ||
| 353 | |||
| 354 | freq.inspect_err(|_e| { | ||
| 355 | G::disable_clock(); | ||
| 356 | }) | ||
| 357 | } | ||
| 358 | |||
| 359 | /// Disable the clock gate for the given peripheral. | ||
| 360 | /// | ||
| 361 | /// # SAFETY | ||
| 362 | /// | ||
| 363 | /// This peripheral must no longer be in use prior to calling `enable`. | ||
| 364 | #[allow(dead_code)] | ||
| 365 | #[inline] | ||
| 366 | pub unsafe fn disable<G: Gate>() { | ||
| 367 | G::disable_clock(); | ||
| 368 | } | ||
| 369 | |||
| 370 | /// Check whether a gate is currently enabled. | ||
| 371 | #[allow(dead_code)] | ||
| 372 | #[inline] | ||
| 373 | pub fn is_clock_enabled<G: Gate>() -> bool { | ||
| 374 | G::is_clock_enabled() | ||
| 375 | } | ||
| 376 | |||
| 377 | /// Release a reset line for the given peripheral set. | ||
| 378 | /// | ||
| 379 | /// Prefer [`enable_and_reset`]. | ||
| 380 | /// | ||
| 381 | /// # SAFETY | ||
| 382 | /// | ||
| 383 | /// This peripheral must not yet be in use prior to calling `release_reset`. | ||
| 384 | #[inline] | ||
| 385 | pub unsafe fn release_reset<G: Gate>() { | ||
| 386 | G::release_reset(); | ||
| 387 | } | ||
| 388 | |||
| 389 | /// Assert a reset line for the given peripheral set. | ||
| 390 | /// | ||
| 391 | /// Prefer [`enable_and_reset`]. | ||
| 392 | /// | ||
| 393 | /// # SAFETY | ||
| 394 | /// | ||
| 395 | /// This peripheral must not yet be in use prior to calling `assert_reset`. | ||
| 396 | #[inline] | ||
| 397 | pub unsafe fn assert_reset<G: Gate>() { | ||
| 398 | G::assert_reset(); | ||
| 399 | } | ||
| 400 | |||
| 401 | /// Check whether the peripheral is held in reset. | ||
| 402 | #[inline] | ||
| 403 | pub unsafe fn is_reset_released<G: Gate>() -> bool { | ||
| 404 | G::is_reset_released() | ||
| 405 | } | ||
| 406 | |||
| 407 | /// Pulse a reset line (assert then release) with a short delay. | ||
| 408 | /// | ||
| 409 | /// Prefer [`enable_and_reset`]. | ||
| 410 | /// | ||
| 411 | /// # SAFETY | ||
| 412 | /// | ||
| 413 | /// This peripheral must not yet be in use prior to calling `release_reset`. | ||
| 414 | #[inline] | ||
| 415 | pub unsafe fn pulse_reset<G: Gate>() { | ||
| 416 | G::assert_reset(); | ||
| 417 | cortex_m::asm::nop(); | ||
| 418 | cortex_m::asm::nop(); | ||
| 419 | G::release_reset(); | ||
| 420 | } | ||
| 421 | |||
| 422 | // | ||
| 423 | // `impl`s for structs/enums | ||
| 424 | // | ||
| 425 | |||
| 426 | /// The [`Clocks`] type's methods generally take the form of "ensure X clock is active". | ||
| 427 | /// | ||
| 428 | /// These methods are intended to be used by HAL peripheral implementors to ensure that their | ||
| 429 | /// selected clocks are active at a suitable level at time of construction. These methods | ||
| 430 | /// return the frequency of the requested clock, in Hertz, or a [`ClockError`]. | ||
| 431 | impl Clocks { | ||
| 432 | /// Ensure the `fro_lf_div` clock is active and valid at the given power state. | ||
| 433 | pub fn ensure_fro_lf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 434 | let Some(clk) = self.fro_lf_div.as_ref() else { | ||
| 435 | return Err(ClockError::BadConfig { | ||
| 436 | clock: "fro_lf_div", | ||
| 437 | reason: "required but not active", | ||
| 438 | }); | ||
| 439 | }; | ||
| 440 | if !clk.power.meets_requirement_of(at_level) { | ||
| 441 | return Err(ClockError::BadConfig { | ||
| 442 | clock: "fro_lf_div", | ||
| 443 | reason: "not low power active", | ||
| 444 | }); | ||
| 445 | } | ||
| 446 | Ok(clk.frequency) | ||
| 447 | } | ||
| 448 | |||
| 449 | /// Ensure the `fro_hf` clock is active and valid at the given power state. | ||
| 450 | pub fn ensure_fro_hf_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 451 | let Some(clk) = self.fro_hf.as_ref() else { | ||
| 452 | return Err(ClockError::BadConfig { | ||
| 453 | clock: "fro_hf", | ||
| 454 | reason: "required but not active", | ||
| 455 | }); | ||
| 456 | }; | ||
| 457 | if !clk.power.meets_requirement_of(at_level) { | ||
| 458 | return Err(ClockError::BadConfig { | ||
| 459 | clock: "fro_hf", | ||
| 460 | reason: "not low power active", | ||
| 461 | }); | ||
| 462 | } | ||
| 463 | Ok(clk.frequency) | ||
| 464 | } | ||
| 465 | |||
| 466 | /// Ensure the `fro_hf_div` clock is active and valid at the given power state. | ||
| 467 | pub fn ensure_fro_hf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 468 | let Some(clk) = self.fro_hf_div.as_ref() else { | ||
| 469 | return Err(ClockError::BadConfig { | ||
| 470 | clock: "fro_hf_div", | ||
| 471 | reason: "required but not active", | ||
| 472 | }); | ||
| 473 | }; | ||
| 474 | if !clk.power.meets_requirement_of(at_level) { | ||
| 475 | return Err(ClockError::BadConfig { | ||
| 476 | clock: "fro_hf_div", | ||
| 477 | reason: "not low power active", | ||
| 478 | }); | ||
| 479 | } | ||
| 480 | Ok(clk.frequency) | ||
| 481 | } | ||
| 482 | |||
| 483 | /// Ensure the `clk_in` clock is active and valid at the given power state. | ||
| 484 | pub fn ensure_clk_in_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 485 | Err(ClockError::NotImplemented { clock: "clk_in" }) | ||
| 486 | } | ||
| 487 | |||
| 488 | /// Ensure the `clk_16k_vsys` clock is active and valid at the given power state. | ||
| 489 | pub fn ensure_clk_16k_vsys_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 490 | // NOTE: clk_16k is always active in low power mode | ||
| 491 | Ok(self | ||
| 492 | .clk_16k_vsys | ||
| 493 | .as_ref() | ||
| 494 | .ok_or(ClockError::BadConfig { | ||
| 495 | clock: "clk_16k_vsys", | ||
| 496 | reason: "required but not active", | ||
| 497 | })? | ||
| 498 | .frequency) | ||
| 499 | } | ||
| 500 | |||
| 501 | /// Ensure the `clk_16k_vdd_core` clock is active and valid at the given power state. | ||
| 502 | pub fn ensure_clk_16k_vdd_core_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 503 | // NOTE: clk_16k is always active in low power mode | ||
| 504 | Ok(self | ||
| 505 | .clk_16k_vdd_core | ||
| 506 | .as_ref() | ||
| 507 | .ok_or(ClockError::BadConfig { | ||
| 508 | clock: "clk_16k_vdd_core", | ||
| 509 | reason: "required but not active", | ||
| 510 | })? | ||
| 511 | .frequency) | ||
| 512 | } | ||
| 513 | |||
| 514 | /// Ensure the `clk_1m` clock is active and valid at the given power state. | ||
| 515 | pub fn ensure_clk_1m_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 516 | let Some(clk) = self.clk_1m.as_ref() else { | ||
| 517 | return Err(ClockError::BadConfig { | ||
| 518 | clock: "clk_1m", | ||
| 519 | reason: "required but not active", | ||
| 520 | }); | ||
| 521 | }; | ||
| 522 | if !clk.power.meets_requirement_of(at_level) { | ||
| 523 | return Err(ClockError::BadConfig { | ||
| 524 | clock: "clk_1m", | ||
| 525 | reason: "not low power active", | ||
| 526 | }); | ||
| 527 | } | ||
| 528 | Ok(clk.frequency) | ||
| 529 | } | ||
| 530 | |||
| 531 | /// Ensure the `pll1_clk` clock is active and valid at the given power state. | ||
| 532 | pub fn ensure_pll1_clk_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 533 | Err(ClockError::NotImplemented { clock: "pll1_clk" }) | ||
| 534 | } | ||
| 535 | |||
| 536 | /// Ensure the `pll1_clk_div` clock is active and valid at the given power state. | ||
| 537 | pub fn ensure_pll1_clk_div_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 538 | Err(ClockError::NotImplemented { clock: "pll1_clk_div" }) | ||
| 539 | } | ||
| 540 | |||
| 541 | /// Ensure the `CPU_CLK` or `SYSTEM_CLK` is active | ||
| 542 | pub fn ensure_cpu_system_clk_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 543 | let Some(clk) = self.cpu_system_clk.as_ref() else { | ||
| 544 | return Err(ClockError::BadConfig { | ||
| 545 | clock: "cpu_system_clk", | ||
| 546 | reason: "required but not active", | ||
| 547 | }); | ||
| 548 | }; | ||
| 549 | // Can the main_clk ever be active in deep sleep? I think it is gated? | ||
| 550 | match at_level { | ||
| 551 | PoweredClock::NormalEnabledDeepSleepDisabled => {} | ||
| 552 | PoweredClock::AlwaysEnabled => { | ||
| 553 | return Err(ClockError::BadConfig { | ||
| 554 | clock: "main_clk", | ||
| 555 | reason: "not low power active", | ||
| 556 | }); | ||
| 557 | } | ||
| 558 | } | ||
| 559 | |||
| 560 | Ok(clk.frequency) | ||
| 561 | } | ||
| 562 | |||
| 563 | pub fn ensure_slow_clk_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 564 | let freq = self.ensure_cpu_system_clk_active(at_level)?; | ||
| 565 | |||
| 566 | Ok(freq / 6) | ||
| 567 | } | ||
| 568 | } | ||
| 569 | |||
| 570 | impl PoweredClock { | ||
| 571 | /// Does THIS clock meet the power requirements of the OTHER clock? | ||
| 572 | pub fn meets_requirement_of(&self, other: &Self) -> bool { | ||
| 573 | match (self, other) { | ||
| 574 | (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::AlwaysEnabled) => false, | ||
| 575 | (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true, | ||
| 576 | (PoweredClock::AlwaysEnabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true, | ||
| 577 | (PoweredClock::AlwaysEnabled, PoweredClock::AlwaysEnabled) => true, | ||
| 578 | } | ||
| 579 | } | ||
| 580 | } | ||
| 581 | |||
| 582 | impl ClockOperator<'_> { | ||
| 583 | /// Configure the FIRC/FRO180M clock family | ||
| 584 | /// | ||
| 585 | /// NOTE: Currently we require this to be a fairly hardcoded value, as this clock is used | ||
| 586 | /// as the main clock used for the CPU, AHB, APB, etc. | ||
| 587 | fn configure_firc_clocks(&mut self) -> Result<(), ClockError> { | ||
| 588 | const HARDCODED_ERR: Result<(), ClockError> = Err(ClockError::BadConfig { | ||
| 589 | clock: "firc", | ||
| 590 | reason: "For now, FIRC must be enabled and in default state!", | ||
| 591 | }); | ||
| 592 | |||
| 593 | // Did the user give us a FIRC config? | ||
| 594 | let Some(firc) = self.config.firc.as_ref() else { | ||
| 595 | return HARDCODED_ERR; | ||
| 596 | }; | ||
| 597 | // Is the FIRC set to 45MHz (should be reset default) | ||
| 598 | if !matches!(firc.frequency, FircFreqSel::Mhz45) { | ||
| 599 | return HARDCODED_ERR; | ||
| 600 | } | ||
| 601 | let base_freq = 45_000_000; | ||
| 602 | |||
| 603 | // Now, check if the FIRC as expected for our hardcoded value | ||
| 604 | let mut firc_ok = true; | ||
| 605 | |||
| 606 | // Is the hardware currently set to the default 45MHz? | ||
| 607 | // | ||
| 608 | // NOTE: the SVD currently has the wrong(?) values for these: | ||
| 609 | // 45 -> 48 | ||
| 610 | // 60 -> 64 | ||
| 611 | // 90 -> 96 | ||
| 612 | // 180 -> 192 | ||
| 613 | // Probably correct-ish, but for a different trim value? | ||
| 614 | firc_ok &= self.scg0.firccfg().read().freq_sel().is_firc_48mhz_192s(); | ||
| 615 | |||
| 616 | // Check some values in the CSR | ||
| 617 | let csr = self.scg0.firccsr().read(); | ||
| 618 | // Is it enabled? | ||
| 619 | firc_ok &= csr.fircen().is_enabled(); | ||
| 620 | // Is it accurate? | ||
| 621 | firc_ok &= csr.fircacc().is_enabled_and_valid(); | ||
| 622 | // Is there no error? | ||
| 623 | firc_ok &= csr.fircerr().is_error_not_detected(); | ||
| 624 | // Is the FIRC the system clock? | ||
| 625 | firc_ok &= csr.fircsel().is_firc(); | ||
| 626 | // Is it valid? | ||
| 627 | firc_ok &= csr.fircvld().is_enabled_and_valid(); | ||
| 628 | |||
| 629 | // Are we happy with the current (hardcoded) state? | ||
| 630 | if !firc_ok { | ||
| 631 | return HARDCODED_ERR; | ||
| 632 | } | ||
| 633 | |||
| 634 | // Note that the fro_hf_root is active | ||
| 635 | self.clocks.fro_hf_root = Some(Clock { | ||
| 636 | frequency: base_freq, | ||
| 637 | power: firc.power, | ||
| 638 | }); | ||
| 639 | |||
| 640 | // Okay! Now we're past that, let's enable all the downstream clocks. | ||
| 641 | let FircConfig { | ||
| 642 | frequency: _, | ||
| 643 | power, | ||
| 644 | fro_hf_enabled, | ||
| 645 | clk_45m_enabled, | ||
| 646 | fro_hf_div, | ||
| 647 | } = firc; | ||
| 648 | |||
| 649 | // When is the FRO enabled? | ||
| 650 | let pow_set = match power { | ||
| 651 | PoweredClock::NormalEnabledDeepSleepDisabled => Fircsten::DisabledInStopModes, | ||
| 652 | PoweredClock::AlwaysEnabled => Fircsten::EnabledInStopModes, | ||
| 653 | }; | ||
| 654 | |||
| 655 | // Do we enable the `fro_hf` output? | ||
| 656 | let fro_hf_set = if *fro_hf_enabled { | ||
| 657 | self.clocks.fro_hf = Some(Clock { | ||
| 658 | frequency: base_freq, | ||
| 659 | power: *power, | ||
| 660 | }); | ||
| 661 | FircFclkPeriphEn::Enabled | ||
| 662 | } else { | ||
| 663 | FircFclkPeriphEn::Disabled | ||
| 664 | }; | ||
| 665 | |||
| 666 | // Do we enable the `clk_45m` output? | ||
| 667 | let clk_45m_set = if *clk_45m_enabled { | ||
| 668 | self.clocks.clk_45m = Some(Clock { | ||
| 669 | frequency: 45_000_000, | ||
| 670 | power: *power, | ||
| 671 | }); | ||
| 672 | FircSclkPeriphEn::Enabled | ||
| 673 | } else { | ||
| 674 | FircSclkPeriphEn::Disabled | ||
| 675 | }; | ||
| 676 | |||
| 677 | self.scg0.firccsr().modify(|_r, w| { | ||
| 678 | w.fircsten().variant(pow_set); | ||
| 679 | w.firc_fclk_periph_en().variant(fro_hf_set); | ||
| 680 | w.firc_sclk_periph_en().variant(clk_45m_set); | ||
| 681 | w | ||
| 682 | }); | ||
| 683 | |||
| 684 | // Do we enable the `fro_hf_div` output? | ||
| 685 | if let Some(d) = fro_hf_div.as_ref() { | ||
| 686 | // We need `fro_hf` to be enabled | ||
| 687 | if !*fro_hf_enabled { | ||
| 688 | return Err(ClockError::BadConfig { | ||
| 689 | clock: "fro_hf_div", | ||
| 690 | reason: "fro_hf not enabled", | ||
| 691 | }); | ||
| 692 | } | ||
| 693 | |||
| 694 | // Halt and reset the div; then set our desired div. | ||
| 695 | self.syscon.frohfdiv().write(|w| { | ||
| 696 | w.halt().halt(); | ||
| 697 | w.reset().asserted(); | ||
| 698 | unsafe { w.div().bits(d.into_bits()) }; | ||
| 699 | w | ||
| 700 | }); | ||
| 701 | // Then unhalt it, and reset it | ||
| 702 | self.syscon.frohfdiv().write(|w| { | ||
| 703 | w.halt().run(); | ||
| 704 | w.reset().released(); | ||
| 705 | w | ||
| 706 | }); | ||
| 707 | |||
| 708 | // Wait for clock to stabilize | ||
| 709 | while self.syscon.frohfdiv().read().unstab().is_ongoing() {} | ||
| 710 | |||
| 711 | // Store off the clock info | ||
| 712 | self.clocks.fro_hf_div = Some(Clock { | ||
| 713 | frequency: base_freq / d.into_divisor(), | ||
| 714 | power: *power, | ||
| 715 | }); | ||
| 716 | } | ||
| 717 | |||
| 718 | Ok(()) | ||
| 719 | } | ||
| 720 | |||
| 721 | /// Configure the SIRC/FRO12M clock family | ||
| 722 | fn configure_sirc_clocks(&mut self) -> Result<(), ClockError> { | ||
| 723 | let SircConfig { | ||
| 724 | power, | ||
| 725 | fro_12m_enabled, | ||
| 726 | fro_lf_div, | ||
| 727 | } = &self.config.sirc; | ||
| 728 | let base_freq = 12_000_000; | ||
| 729 | |||
| 730 | // Allow writes | ||
| 731 | self.scg0.sirccsr().modify(|_r, w| w.lk().write_enabled()); | ||
| 732 | self.clocks.fro_12m_root = Some(Clock { | ||
| 733 | frequency: base_freq, | ||
| 734 | power: *power, | ||
| 735 | }); | ||
| 736 | |||
| 737 | let deep = match power { | ||
| 738 | PoweredClock::NormalEnabledDeepSleepDisabled => Sircsten::Disabled, | ||
| 739 | PoweredClock::AlwaysEnabled => Sircsten::Enabled, | ||
| 740 | }; | ||
| 741 | let pclk = if *fro_12m_enabled { | ||
| 742 | self.clocks.fro_12m = Some(Clock { | ||
| 743 | frequency: base_freq, | ||
| 744 | power: *power, | ||
| 745 | }); | ||
| 746 | self.clocks.clk_1m = Some(Clock { | ||
| 747 | frequency: base_freq / 12, | ||
| 748 | power: *power, | ||
| 749 | }); | ||
| 750 | SircClkPeriphEn::Enabled | ||
| 751 | } else { | ||
| 752 | SircClkPeriphEn::Disabled | ||
| 753 | }; | ||
| 754 | |||
| 755 | // Set sleep/peripheral usage | ||
| 756 | self.scg0.sirccsr().modify(|_r, w| { | ||
| 757 | w.sircsten().variant(deep); | ||
| 758 | w.sirc_clk_periph_en().variant(pclk); | ||
| 759 | w | ||
| 760 | }); | ||
| 761 | |||
| 762 | while self.scg0.sirccsr().read().sircvld().is_disabled_or_not_valid() {} | ||
| 763 | if self.scg0.sirccsr().read().sircerr().is_error_detected() { | ||
| 764 | return Err(ClockError::BadConfig { | ||
| 765 | clock: "sirc", | ||
| 766 | reason: "error set", | ||
| 767 | }); | ||
| 768 | } | ||
| 769 | |||
| 770 | // reset lock | ||
| 771 | self.scg0.sirccsr().modify(|_r, w| w.lk().write_disabled()); | ||
| 772 | |||
| 773 | // Do we enable the `fro_lf_div` output? | ||
| 774 | if let Some(d) = fro_lf_div.as_ref() { | ||
| 775 | // We need `fro_lf` to be enabled | ||
| 776 | if !*fro_12m_enabled { | ||
| 777 | return Err(ClockError::BadConfig { | ||
| 778 | clock: "fro_lf_div", | ||
| 779 | reason: "fro_12m not enabled", | ||
| 780 | }); | ||
| 781 | } | ||
| 782 | |||
| 783 | // Halt and reset the div; then set our desired div. | ||
| 784 | self.syscon.frolfdiv().write(|w| { | ||
| 785 | w.halt().halt(); | ||
| 786 | w.reset().asserted(); | ||
| 787 | unsafe { w.div().bits(d.into_bits()) }; | ||
| 788 | w | ||
| 789 | }); | ||
| 790 | // Then unhalt it, and reset it | ||
| 791 | self.syscon.frolfdiv().modify(|_r, w| { | ||
| 792 | w.halt().run(); | ||
| 793 | w.reset().released(); | ||
| 794 | w | ||
| 795 | }); | ||
| 796 | |||
| 797 | // Wait for clock to stabilize | ||
| 798 | while self.syscon.frolfdiv().read().unstab().is_ongoing() {} | ||
| 799 | |||
| 800 | // Store off the clock info | ||
| 801 | self.clocks.fro_lf_div = Some(Clock { | ||
| 802 | frequency: base_freq / d.into_divisor(), | ||
| 803 | power: *power, | ||
| 804 | }); | ||
| 805 | } | ||
| 806 | |||
| 807 | Ok(()) | ||
| 808 | } | ||
| 809 | |||
| 810 | /// Configure the FRO16K/clk_16k clock family | ||
| 811 | fn configure_fro16k_clocks(&mut self) -> Result<(), ClockError> { | ||
| 812 | let Some(fro16k) = self.config.fro16k.as_ref() else { | ||
| 813 | return Ok(()); | ||
| 814 | }; | ||
| 815 | // Enable FRO16K oscillator | ||
| 816 | self.vbat0.froctla().modify(|_, w| w.fro_en().set_bit()); | ||
| 817 | |||
| 818 | // Lock the control register | ||
| 819 | self.vbat0.frolcka().modify(|_, w| w.lock().set_bit()); | ||
| 820 | |||
| 821 | let Fro16KConfig { | ||
| 822 | vsys_domain_active, | ||
| 823 | vdd_core_domain_active, | ||
| 824 | } = fro16k; | ||
| 825 | |||
| 826 | // Enable clock outputs to both VSYS and VDD_CORE domains | ||
| 827 | // Bit 0: clk_16k0 to VSYS domain | ||
| 828 | // Bit 1: clk_16k1 to VDD_CORE domain | ||
| 829 | // | ||
| 830 | // TODO: Define sub-fields for this register with a PAC patch? | ||
| 831 | let mut bits = 0; | ||
| 832 | if *vsys_domain_active { | ||
| 833 | bits |= 0b01; | ||
| 834 | self.clocks.clk_16k_vsys = Some(Clock { | ||
| 835 | frequency: 16_384, | ||
| 836 | power: PoweredClock::AlwaysEnabled, | ||
| 837 | }); | ||
| 838 | } | ||
| 839 | if *vdd_core_domain_active { | ||
| 840 | bits |= 0b10; | ||
| 841 | self.clocks.clk_16k_vdd_core = Some(Clock { | ||
| 842 | frequency: 16_384, | ||
| 843 | power: PoweredClock::AlwaysEnabled, | ||
| 844 | }); | ||
| 845 | } | ||
| 846 | self.vbat0.froclke().modify(|_r, w| unsafe { w.clke().bits(bits) }); | ||
| 847 | |||
| 848 | Ok(()) | ||
| 849 | } | ||
| 850 | } | ||
| 851 | |||
| 852 | // | ||
| 853 | // Macros/macro impls | ||
| 854 | // | ||
| 855 | |||
| 856 | /// This macro is used to implement the [`Gate`] trait for a given peripheral | ||
| 857 | /// that is controlled by the MRCC peripheral. | ||
| 858 | macro_rules! impl_cc_gate { | ||
| 859 | ($name:ident, $clk_reg:ident, $rst_reg:ident, $field:ident, $config:ty) => { | ||
| 860 | impl Gate for crate::peripherals::$name { | ||
| 861 | type MrccPeriphConfig = $config; | ||
| 862 | |||
| 863 | #[inline] | ||
| 864 | unsafe fn enable_clock() { | ||
| 865 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 866 | mrcc.$clk_reg().modify(|_, w| w.$field().enabled()); | ||
| 867 | } | ||
| 868 | |||
| 869 | #[inline] | ||
| 870 | unsafe fn disable_clock() { | ||
| 871 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 872 | mrcc.$clk_reg().modify(|_r, w| w.$field().disabled()); | ||
| 873 | } | ||
| 874 | |||
| 875 | #[inline] | ||
| 876 | fn is_clock_enabled() -> bool { | ||
| 877 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 878 | mrcc.$clk_reg().read().$field().is_enabled() | ||
| 879 | } | ||
| 880 | |||
| 881 | #[inline] | ||
| 882 | unsafe fn release_reset() { | ||
| 883 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 884 | mrcc.$rst_reg().modify(|_, w| w.$field().enabled()); | ||
| 885 | } | ||
| 886 | |||
| 887 | #[inline] | ||
| 888 | unsafe fn assert_reset() { | ||
| 889 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 890 | mrcc.$rst_reg().modify(|_, w| w.$field().disabled()); | ||
| 891 | } | ||
| 892 | |||
| 893 | #[inline] | ||
| 894 | fn is_reset_released() -> bool { | ||
| 895 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 896 | mrcc.$rst_reg().read().$field().is_enabled() | ||
| 897 | } | ||
| 898 | } | ||
| 899 | }; | ||
| 900 | } | ||
| 901 | |||
| 902 | /// This module contains implementations of MRCC APIs, specifically of the [`Gate`] trait, | ||
| 903 | /// for various low level peripherals. | ||
| 904 | pub(crate) mod gate { | ||
| 905 | #[cfg(not(feature = "time"))] | ||
| 906 | use super::periph_helpers::OsTimerConfig; | ||
| 907 | use super::periph_helpers::{AdcConfig, Lpi2cConfig, LpuartConfig, NoConfig}; | ||
| 908 | use super::*; | ||
| 909 | |||
| 910 | // These peripherals have no additional upstream clocks or configuration required | ||
| 911 | // other than enabling through the MRCC gate. Currently, these peripherals will | ||
| 912 | // ALWAYS return `Ok(0)` when calling [`enable_and_reset()`] and/or | ||
| 913 | // [`SPConfHelper::post_enable_config()`]. | ||
| 914 | impl_cc_gate!(PORT0, mrcc_glb_cc1, mrcc_glb_rst1, port0, NoConfig); | ||
| 915 | impl_cc_gate!(PORT1, mrcc_glb_cc1, mrcc_glb_rst1, port1, NoConfig); | ||
| 916 | impl_cc_gate!(PORT2, mrcc_glb_cc1, mrcc_glb_rst1, port2, NoConfig); | ||
| 917 | impl_cc_gate!(PORT3, mrcc_glb_cc1, mrcc_glb_rst1, port3, NoConfig); | ||
| 918 | impl_cc_gate!(PORT4, mrcc_glb_cc1, mrcc_glb_rst1, port4, NoConfig); | ||
| 919 | |||
| 920 | impl_cc_gate!(GPIO0, mrcc_glb_cc2, mrcc_glb_rst2, gpio0, NoConfig); | ||
| 921 | impl_cc_gate!(GPIO1, mrcc_glb_cc2, mrcc_glb_rst2, gpio1, NoConfig); | ||
| 922 | impl_cc_gate!(GPIO2, mrcc_glb_cc2, mrcc_glb_rst2, gpio2, NoConfig); | ||
| 923 | impl_cc_gate!(GPIO3, mrcc_glb_cc2, mrcc_glb_rst2, gpio3, NoConfig); | ||
| 924 | impl_cc_gate!(GPIO4, mrcc_glb_cc2, mrcc_glb_rst2, gpio4, NoConfig); | ||
| 925 | |||
| 926 | // These peripherals DO have meaningful configuration, and could fail if the system | ||
| 927 | // clocks do not match their needs. | ||
| 928 | #[cfg(not(feature = "time"))] | ||
| 929 | impl_cc_gate!(OSTIMER0, mrcc_glb_cc1, mrcc_glb_rst1, ostimer0, OsTimerConfig); | ||
| 930 | |||
| 931 | impl_cc_gate!(LPI2C0, mrcc_glb_cc0, mrcc_glb_rst0, lpi2c0, Lpi2cConfig); | ||
| 932 | impl_cc_gate!(LPI2C1, mrcc_glb_cc0, mrcc_glb_rst0, lpi2c1, Lpi2cConfig); | ||
| 933 | impl_cc_gate!(LPI2C2, mrcc_glb_cc1, mrcc_glb_rst1, lpi2c2, Lpi2cConfig); | ||
| 934 | impl_cc_gate!(LPI2C3, mrcc_glb_cc1, mrcc_glb_rst1, lpi2c3, Lpi2cConfig); | ||
| 935 | |||
| 936 | impl_cc_gate!(LPUART0, mrcc_glb_cc0, mrcc_glb_rst0, lpuart0, LpuartConfig); | ||
| 937 | impl_cc_gate!(LPUART1, mrcc_glb_cc0, mrcc_glb_rst0, lpuart1, LpuartConfig); | ||
| 938 | impl_cc_gate!(LPUART2, mrcc_glb_cc0, mrcc_glb_rst0, lpuart2, LpuartConfig); | ||
| 939 | impl_cc_gate!(LPUART3, mrcc_glb_cc0, mrcc_glb_rst0, lpuart3, LpuartConfig); | ||
| 940 | impl_cc_gate!(LPUART4, mrcc_glb_cc0, mrcc_glb_rst0, lpuart4, LpuartConfig); | ||
| 941 | impl_cc_gate!(LPUART5, mrcc_glb_cc1, mrcc_glb_rst1, lpuart5, LpuartConfig); | ||
| 942 | impl_cc_gate!(ADC1, mrcc_glb_cc1, mrcc_glb_rst1, adc1, AdcConfig); | ||
| 943 | } | ||
diff --git a/embassy-mcxa/src/clocks/periph_helpers.rs b/embassy-mcxa/src/clocks/periph_helpers.rs new file mode 100644 index 000000000..fed5e558e --- /dev/null +++ b/embassy-mcxa/src/clocks/periph_helpers.rs | |||
| @@ -0,0 +1,498 @@ | |||
| 1 | //! Peripheral Helpers | ||
| 2 | //! | ||
| 3 | //! The purpose of this module is to define the per-peripheral special handling | ||
| 4 | //! required from a clocking perspective. Different peripherals have different | ||
| 5 | //! selectable source clocks, and some peripherals have additional pre-dividers | ||
| 6 | //! that can be used. | ||
| 7 | //! | ||
| 8 | //! See the docs of [`SPConfHelper`] for more details. | ||
| 9 | |||
| 10 | use super::{ClockError, Clocks, PoweredClock}; | ||
| 11 | use crate::pac; | ||
| 12 | |||
| 13 | /// Sealed Peripheral Configuration Helper | ||
| 14 | /// | ||
| 15 | /// NOTE: the name "sealed" doesn't *totally* make sense because its not sealed yet in the | ||
| 16 | /// embassy-mcxa project, but it derives from embassy-imxrt where it is. We should | ||
| 17 | /// fix the name, or actually do the sealing of peripherals. | ||
| 18 | /// | ||
| 19 | /// This trait serves to act as a per-peripheral customization for clocking behavior. | ||
| 20 | /// | ||
| 21 | /// This trait should be implemented on a configuration type for a given peripheral, and | ||
| 22 | /// provide the methods that will be called by the higher level operations like | ||
| 23 | /// `embassy_mcxa::clocks::enable_and_reset()`. | ||
| 24 | pub trait SPConfHelper { | ||
| 25 | /// This method is called AFTER a given MRCC peripheral has been enabled (e.g. un-gated), | ||
| 26 | /// but BEFORE the peripheral reset line is reset. | ||
| 27 | /// | ||
| 28 | /// This function should check that any relevant upstream clocks are enabled, are in a | ||
| 29 | /// reasonable power state, and that the requested configuration can be made. If any of | ||
| 30 | /// these checks fail, an `Err(ClockError)` should be returned, likely `ClockError::BadConfig`. | ||
| 31 | /// | ||
| 32 | /// This function SHOULD NOT make any changes to the system clock configuration, even | ||
| 33 | /// unsafely, as this should remain static for the duration of the program. | ||
| 34 | /// | ||
| 35 | /// This function WILL be called in a critical section, care should be taken not to delay | ||
| 36 | /// for an unreasonable amount of time. | ||
| 37 | /// | ||
| 38 | /// On success, this function MUST return an `Ok(freq)`, where `freq` is the frequency | ||
| 39 | /// fed into the peripheral, taking into account the selected source clock, as well as | ||
| 40 | /// any pre-divisors. | ||
| 41 | fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError>; | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Copy and paste macro that: | ||
| 45 | /// | ||
| 46 | /// * Sets the clocksel mux to `$selvar` | ||
| 47 | /// * Resets and halts the div, and applies the calculated div4 bits | ||
| 48 | /// * Releases reset + halt | ||
| 49 | /// * Waits for the div to stabilize | ||
| 50 | /// * Returns `Ok($freq / $conf.div.into_divisor())` | ||
| 51 | /// | ||
| 52 | /// Assumes: | ||
| 53 | /// | ||
| 54 | /// * self is a configuration struct that has a field called `div`, which | ||
| 55 | /// is a `Div4` | ||
| 56 | /// | ||
| 57 | /// usage: | ||
| 58 | /// | ||
| 59 | /// ```rust | ||
| 60 | /// apply_div4!(self, clksel, clkdiv, variant, freq) | ||
| 61 | /// ``` | ||
| 62 | /// | ||
| 63 | /// In the future if we make all the clksel+clkdiv pairs into commonly derivedFrom | ||
| 64 | /// registers, or if we put some kind of simple trait around those regs, we could | ||
| 65 | /// do this with something other than a macro, but for now, this is harm-reduction | ||
| 66 | /// to avoid incorrect copy + paste | ||
| 67 | macro_rules! apply_div4 { | ||
| 68 | ($conf:ident, $selreg:ident, $divreg:ident, $selvar:ident, $freq:ident) => {{ | ||
| 69 | // set clksel | ||
| 70 | $selreg.modify(|_r, w| w.mux().variant($selvar)); | ||
| 71 | |||
| 72 | // Set up clkdiv | ||
| 73 | $divreg.modify(|_r, w| { | ||
| 74 | unsafe { w.div().bits($conf.div.into_bits()) } | ||
| 75 | .halt() | ||
| 76 | .asserted() | ||
| 77 | .reset() | ||
| 78 | .asserted() | ||
| 79 | }); | ||
| 80 | $divreg.modify(|_r, w| w.halt().deasserted().reset().deasserted()); | ||
| 81 | |||
| 82 | while $divreg.read().unstab().is_unstable() {} | ||
| 83 | |||
| 84 | Ok($freq / $conf.div.into_divisor()) | ||
| 85 | }}; | ||
| 86 | } | ||
| 87 | |||
| 88 | // config types | ||
| 89 | |||
| 90 | /// This type represents a divider in the range 1..=16. | ||
| 91 | /// | ||
| 92 | /// At a hardware level, this is an 8-bit register from 0..=15, | ||
| 93 | /// which adds one. | ||
| 94 | /// | ||
| 95 | /// While the *clock* domain seems to use 8-bit dividers, the *peripheral* domain | ||
| 96 | /// seems to use 4 bit dividers! | ||
| 97 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 98 | pub struct Div4(pub(super) u8); | ||
| 99 | |||
| 100 | impl Div4 { | ||
| 101 | /// Divide by one, or no division | ||
| 102 | pub const fn no_div() -> Self { | ||
| 103 | Self(0) | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Store a "raw" divisor value that will divide the source by | ||
| 107 | /// `(n + 1)`, e.g. `Div4::from_raw(0)` will divide the source | ||
| 108 | /// by 1, and `Div4::from_raw(15)` will divide the source by | ||
| 109 | /// 16. | ||
| 110 | pub const fn from_raw(n: u8) -> Option<Self> { | ||
| 111 | if n > 0b1111 { None } else { Some(Self(n)) } | ||
| 112 | } | ||
| 113 | |||
| 114 | /// Store a specific divisor value that will divide the source | ||
| 115 | /// by `n`. e.g. `Div4::from_divisor(1)` will divide the source | ||
| 116 | /// by 1, and `Div4::from_divisor(16)` will divide the source | ||
| 117 | /// by 16. | ||
| 118 | /// | ||
| 119 | /// Will return `None` if `n` is not in the range `1..=16`. | ||
| 120 | /// Consider [`Self::from_raw`] for an infallible version. | ||
| 121 | pub const fn from_divisor(n: u8) -> Option<Self> { | ||
| 122 | let Some(n) = n.checked_sub(1) else { | ||
| 123 | return None; | ||
| 124 | }; | ||
| 125 | if n > 0b1111 { | ||
| 126 | return None; | ||
| 127 | } | ||
| 128 | Some(Self(n)) | ||
| 129 | } | ||
| 130 | |||
| 131 | /// Convert into "raw" bits form | ||
| 132 | #[inline(always)] | ||
| 133 | pub const fn into_bits(self) -> u8 { | ||
| 134 | self.0 | ||
| 135 | } | ||
| 136 | |||
| 137 | /// Convert into "divisor" form, as a u32 for convenient frequency math | ||
| 138 | #[inline(always)] | ||
| 139 | pub const fn into_divisor(self) -> u32 { | ||
| 140 | self.0 as u32 + 1 | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | /// A basic type that always returns an error when `post_enable_config` is called. | ||
| 145 | /// | ||
| 146 | /// Should only be used as a placeholder. | ||
| 147 | pub struct UnimplementedConfig; | ||
| 148 | |||
| 149 | impl SPConfHelper for UnimplementedConfig { | ||
| 150 | fn post_enable_config(&self, _clocks: &Clocks) -> Result<u32, ClockError> { | ||
| 151 | Err(ClockError::UnimplementedConfig) | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | /// A basic type that always returns `Ok(0)` when `post_enable_config` is called. | ||
| 156 | /// | ||
| 157 | /// This should only be used for peripherals that are "ambiently" clocked, like `PORTn` | ||
| 158 | /// peripherals, which have no selectable/configurable source clock. | ||
| 159 | pub struct NoConfig; | ||
| 160 | impl SPConfHelper for NoConfig { | ||
| 161 | fn post_enable_config(&self, _clocks: &Clocks) -> Result<u32, ClockError> { | ||
| 162 | Ok(0) | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | // | ||
| 167 | // LPI2c | ||
| 168 | // | ||
| 169 | |||
| 170 | /// Selectable clocks for `Lpi2c` peripherals | ||
| 171 | #[derive(Debug, Clone, Copy)] | ||
| 172 | pub enum Lpi2cClockSel { | ||
| 173 | /// FRO12M/FRO_LF/SIRC clock source, passed through divider | ||
| 174 | /// "fro_lf_div" | ||
| 175 | FroLfDiv, | ||
| 176 | /// FRO180M/FRO_HF/FIRC clock source, passed through divider | ||
| 177 | /// "fro_hf_div" | ||
| 178 | FroHfDiv, | ||
| 179 | /// SOSC/XTAL/EXTAL clock source | ||
| 180 | ClkIn, | ||
| 181 | /// clk_1m/FRO_LF divided by 12 | ||
| 182 | Clk1M, | ||
| 183 | /// Output of PLL1, passed through clock divider, | ||
| 184 | /// "pll1_clk_div", maybe "pll1_lf_div"? | ||
| 185 | Pll1ClkDiv, | ||
| 186 | /// Disabled | ||
| 187 | None, | ||
| 188 | } | ||
| 189 | |||
| 190 | /// Which instance of the `Lpi2c` is this? | ||
| 191 | /// | ||
| 192 | /// Should not be directly selectable by end-users. | ||
| 193 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 194 | pub enum Lpi2cInstance { | ||
| 195 | /// Instance 0 | ||
| 196 | Lpi2c0, | ||
| 197 | /// Instance 1 | ||
| 198 | Lpi2c1, | ||
| 199 | /// Instance 2 | ||
| 200 | Lpi2c2, | ||
| 201 | /// Instance 3 | ||
| 202 | Lpi2c3, | ||
| 203 | } | ||
| 204 | |||
| 205 | /// Top level configuration for `Lpi2c` instances. | ||
| 206 | pub struct Lpi2cConfig { | ||
| 207 | /// Power state required for this peripheral | ||
| 208 | pub power: PoweredClock, | ||
| 209 | /// Clock source | ||
| 210 | pub source: Lpi2cClockSel, | ||
| 211 | /// Clock divisor | ||
| 212 | pub div: Div4, | ||
| 213 | /// Which instance is this? | ||
| 214 | // NOTE: should not be user settable | ||
| 215 | pub(crate) instance: Lpi2cInstance, | ||
| 216 | } | ||
| 217 | |||
| 218 | impl SPConfHelper for Lpi2cConfig { | ||
| 219 | fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> { | ||
| 220 | // check that source is suitable | ||
| 221 | let mrcc0 = unsafe { pac::Mrcc0::steal() }; | ||
| 222 | use mcxa_pac::mrcc0::mrcc_lpi2c0_clksel::Mux; | ||
| 223 | |||
| 224 | let (clkdiv, clksel) = match self.instance { | ||
| 225 | Lpi2cInstance::Lpi2c0 => (mrcc0.mrcc_lpi2c0_clkdiv(), mrcc0.mrcc_lpi2c0_clksel()), | ||
| 226 | Lpi2cInstance::Lpi2c1 => (mrcc0.mrcc_lpi2c1_clkdiv(), mrcc0.mrcc_lpi2c1_clksel()), | ||
| 227 | Lpi2cInstance::Lpi2c2 => (mrcc0.mrcc_lpi2c2_clkdiv(), mrcc0.mrcc_lpi2c2_clksel()), | ||
| 228 | Lpi2cInstance::Lpi2c3 => (mrcc0.mrcc_lpi2c3_clkdiv(), mrcc0.mrcc_lpi2c3_clksel()), | ||
| 229 | }; | ||
| 230 | |||
| 231 | let (freq, variant) = match self.source { | ||
| 232 | Lpi2cClockSel::FroLfDiv => { | ||
| 233 | let freq = clocks.ensure_fro_lf_div_active(&self.power)?; | ||
| 234 | (freq, Mux::ClkrootFunc0) | ||
| 235 | } | ||
| 236 | Lpi2cClockSel::FroHfDiv => { | ||
| 237 | let freq = clocks.ensure_fro_hf_div_active(&self.power)?; | ||
| 238 | (freq, Mux::ClkrootFunc2) | ||
| 239 | } | ||
| 240 | Lpi2cClockSel::ClkIn => { | ||
| 241 | let freq = clocks.ensure_clk_in_active(&self.power)?; | ||
| 242 | (freq, Mux::ClkrootFunc3) | ||
| 243 | } | ||
| 244 | Lpi2cClockSel::Clk1M => { | ||
| 245 | let freq = clocks.ensure_clk_1m_active(&self.power)?; | ||
| 246 | (freq, Mux::ClkrootFunc5) | ||
| 247 | } | ||
| 248 | Lpi2cClockSel::Pll1ClkDiv => { | ||
| 249 | let freq = clocks.ensure_pll1_clk_div_active(&self.power)?; | ||
| 250 | (freq, Mux::ClkrootFunc6) | ||
| 251 | } | ||
| 252 | Lpi2cClockSel::None => unsafe { | ||
| 253 | // no ClkrootFunc7, just write manually for now | ||
| 254 | clksel.write(|w| w.bits(0b111)); | ||
| 255 | clkdiv.modify(|_r, w| w.reset().asserted().halt().asserted()); | ||
| 256 | return Ok(0); | ||
| 257 | }, | ||
| 258 | }; | ||
| 259 | |||
| 260 | apply_div4!(self, clksel, clkdiv, variant, freq) | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 264 | // | ||
| 265 | // LPUart | ||
| 266 | // | ||
| 267 | |||
| 268 | /// Selectable clocks for Lpuart peripherals | ||
| 269 | #[derive(Debug, Clone, Copy)] | ||
| 270 | pub enum LpuartClockSel { | ||
| 271 | /// FRO12M/FRO_LF/SIRC clock source, passed through divider | ||
| 272 | /// "fro_lf_div" | ||
| 273 | FroLfDiv, | ||
| 274 | /// FRO180M/FRO_HF/FIRC clock source, passed through divider | ||
| 275 | /// "fro_hf_div" | ||
| 276 | FroHfDiv, | ||
| 277 | /// SOSC/XTAL/EXTAL clock source | ||
| 278 | ClkIn, | ||
| 279 | /// FRO16K/clk_16k source | ||
| 280 | Clk16K, | ||
| 281 | /// clk_1m/FRO_LF divided by 12 | ||
| 282 | Clk1M, | ||
| 283 | /// Output of PLL1, passed through clock divider, | ||
| 284 | /// "pll1_clk_div", maybe "pll1_lf_div"? | ||
| 285 | Pll1ClkDiv, | ||
| 286 | /// Disabled | ||
| 287 | None, | ||
| 288 | } | ||
| 289 | |||
| 290 | /// Which instance of the Lpuart is this? | ||
| 291 | /// | ||
| 292 | /// Should not be directly selectable by end-users. | ||
| 293 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 294 | pub enum LpuartInstance { | ||
| 295 | /// Instance 0 | ||
| 296 | Lpuart0, | ||
| 297 | /// Instance 1 | ||
| 298 | Lpuart1, | ||
| 299 | /// Instance 2 | ||
| 300 | Lpuart2, | ||
| 301 | /// Instance 3 | ||
| 302 | Lpuart3, | ||
| 303 | /// Instance 4 | ||
| 304 | Lpuart4, | ||
| 305 | /// Instance 5 | ||
| 306 | Lpuart5, | ||
| 307 | } | ||
| 308 | |||
| 309 | /// Top level configuration for `Lpuart` instances. | ||
| 310 | pub struct LpuartConfig { | ||
| 311 | /// Power state required for this peripheral | ||
| 312 | pub power: PoweredClock, | ||
| 313 | /// Clock source | ||
| 314 | pub source: LpuartClockSel, | ||
| 315 | /// Clock divisor | ||
| 316 | pub div: Div4, | ||
| 317 | /// Which instance is this? | ||
| 318 | // NOTE: should not be user settable | ||
| 319 | pub(crate) instance: LpuartInstance, | ||
| 320 | } | ||
| 321 | |||
| 322 | impl SPConfHelper for LpuartConfig { | ||
| 323 | fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> { | ||
| 324 | // check that source is suitable | ||
| 325 | let mrcc0 = unsafe { pac::Mrcc0::steal() }; | ||
| 326 | use mcxa_pac::mrcc0::mrcc_lpuart0_clksel::Mux; | ||
| 327 | |||
| 328 | let (clkdiv, clksel) = match self.instance { | ||
| 329 | LpuartInstance::Lpuart0 => (mrcc0.mrcc_lpuart0_clkdiv(), mrcc0.mrcc_lpuart0_clksel()), | ||
| 330 | LpuartInstance::Lpuart1 => (mrcc0.mrcc_lpuart1_clkdiv(), mrcc0.mrcc_lpuart1_clksel()), | ||
| 331 | LpuartInstance::Lpuart2 => (mrcc0.mrcc_lpuart2_clkdiv(), mrcc0.mrcc_lpuart2_clksel()), | ||
| 332 | LpuartInstance::Lpuart3 => (mrcc0.mrcc_lpuart3_clkdiv(), mrcc0.mrcc_lpuart3_clksel()), | ||
| 333 | LpuartInstance::Lpuart4 => (mrcc0.mrcc_lpuart4_clkdiv(), mrcc0.mrcc_lpuart4_clksel()), | ||
| 334 | LpuartInstance::Lpuart5 => (mrcc0.mrcc_lpuart5_clkdiv(), mrcc0.mrcc_lpuart5_clksel()), | ||
| 335 | }; | ||
| 336 | |||
| 337 | let (freq, variant) = match self.source { | ||
| 338 | LpuartClockSel::FroLfDiv => { | ||
| 339 | let freq = clocks.ensure_fro_lf_div_active(&self.power)?; | ||
| 340 | (freq, Mux::ClkrootFunc0) | ||
| 341 | } | ||
| 342 | LpuartClockSel::FroHfDiv => { | ||
| 343 | let freq = clocks.ensure_fro_hf_div_active(&self.power)?; | ||
| 344 | (freq, Mux::ClkrootFunc2) | ||
| 345 | } | ||
| 346 | LpuartClockSel::ClkIn => { | ||
| 347 | let freq = clocks.ensure_clk_in_active(&self.power)?; | ||
| 348 | (freq, Mux::ClkrootFunc3) | ||
| 349 | } | ||
| 350 | LpuartClockSel::Clk16K => { | ||
| 351 | let freq = clocks.ensure_clk_16k_vdd_core_active(&self.power)?; | ||
| 352 | (freq, Mux::ClkrootFunc4) | ||
| 353 | } | ||
| 354 | LpuartClockSel::Clk1M => { | ||
| 355 | let freq = clocks.ensure_clk_1m_active(&self.power)?; | ||
| 356 | (freq, Mux::ClkrootFunc5) | ||
| 357 | } | ||
| 358 | LpuartClockSel::Pll1ClkDiv => { | ||
| 359 | let freq = clocks.ensure_pll1_clk_div_active(&self.power)?; | ||
| 360 | (freq, Mux::ClkrootFunc6) | ||
| 361 | } | ||
| 362 | LpuartClockSel::None => unsafe { | ||
| 363 | // no ClkrootFunc7, just write manually for now | ||
| 364 | clksel.write(|w| w.bits(0b111)); | ||
| 365 | clkdiv.modify(|_r, w| { | ||
| 366 | w.reset().asserted(); | ||
| 367 | w.halt().asserted(); | ||
| 368 | w | ||
| 369 | }); | ||
| 370 | return Ok(0); | ||
| 371 | }, | ||
| 372 | }; | ||
| 373 | |||
| 374 | // set clksel | ||
| 375 | apply_div4!(self, clksel, clkdiv, variant, freq) | ||
| 376 | } | ||
| 377 | } | ||
| 378 | |||
| 379 | // | ||
| 380 | // OSTimer | ||
| 381 | // | ||
| 382 | |||
| 383 | /// Selectable clocks for the OSTimer peripheral | ||
| 384 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 385 | pub enum OstimerClockSel { | ||
| 386 | /// 16k clock, sourced from FRO16K (Vdd Core) | ||
| 387 | Clk16kVddCore, | ||
| 388 | /// 1 MHz Clock sourced from FRO12M | ||
| 389 | Clk1M, | ||
| 390 | /// Disabled | ||
| 391 | None, | ||
| 392 | } | ||
| 393 | |||
| 394 | /// Top level configuration for the `OSTimer` peripheral | ||
| 395 | pub struct OsTimerConfig { | ||
| 396 | /// Power state required for this peripheral | ||
| 397 | pub power: PoweredClock, | ||
| 398 | /// Selected clock source for this peripheral | ||
| 399 | pub source: OstimerClockSel, | ||
| 400 | } | ||
| 401 | |||
| 402 | impl SPConfHelper for OsTimerConfig { | ||
| 403 | fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> { | ||
| 404 | let mrcc0 = unsafe { pac::Mrcc0::steal() }; | ||
| 405 | Ok(match self.source { | ||
| 406 | OstimerClockSel::Clk16kVddCore => { | ||
| 407 | let freq = clocks.ensure_clk_16k_vdd_core_active(&self.power)?; | ||
| 408 | mrcc0.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_16k()); | ||
| 409 | freq | ||
| 410 | } | ||
| 411 | OstimerClockSel::Clk1M => { | ||
| 412 | let freq = clocks.ensure_clk_1m_active(&self.power)?; | ||
| 413 | mrcc0.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_1m()); | ||
| 414 | freq | ||
| 415 | } | ||
| 416 | OstimerClockSel::None => { | ||
| 417 | mrcc0.mrcc_ostimer0_clksel().write(|w| unsafe { w.mux().bits(0b11) }); | ||
| 418 | 0 | ||
| 419 | } | ||
| 420 | }) | ||
| 421 | } | ||
| 422 | } | ||
| 423 | |||
| 424 | // | ||
| 425 | // Adc | ||
| 426 | // | ||
| 427 | |||
| 428 | /// Selectable clocks for the ADC peripheral | ||
| 429 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 430 | pub enum AdcClockSel { | ||
| 431 | /// Divided `fro_lf`/`clk_12m`/FRO12M source | ||
| 432 | FroLfDiv, | ||
| 433 | /// Gated `fro_hf`/`FRO180M` source | ||
| 434 | FroHf, | ||
| 435 | /// External Clock Source | ||
| 436 | ClkIn, | ||
| 437 | /// 1MHz clock sourced by a divided `fro_lf`/`clk_12m` | ||
| 438 | Clk1M, | ||
| 439 | /// Internal PLL output, with configurable divisor | ||
| 440 | Pll1ClkDiv, | ||
| 441 | /// No clock/disabled | ||
| 442 | None, | ||
| 443 | } | ||
| 444 | |||
| 445 | /// Top level configuration for the ADC peripheral | ||
| 446 | pub struct AdcConfig { | ||
| 447 | /// Power state required for this peripheral | ||
| 448 | pub power: PoweredClock, | ||
| 449 | /// Selected clock-source for this peripheral | ||
| 450 | pub source: AdcClockSel, | ||
| 451 | /// Pre-divisor, applied to the upstream clock output | ||
| 452 | pub div: Div4, | ||
| 453 | } | ||
| 454 | |||
| 455 | impl SPConfHelper for AdcConfig { | ||
| 456 | fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> { | ||
| 457 | use mcxa_pac::mrcc0::mrcc_adc_clksel::Mux; | ||
| 458 | let mrcc0 = unsafe { pac::Mrcc0::steal() }; | ||
| 459 | let (freq, variant) = match self.source { | ||
| 460 | AdcClockSel::FroLfDiv => { | ||
| 461 | let freq = clocks.ensure_fro_lf_div_active(&self.power)?; | ||
| 462 | (freq, Mux::ClkrootFunc0) | ||
| 463 | } | ||
| 464 | AdcClockSel::FroHf => { | ||
| 465 | let freq = clocks.ensure_fro_hf_active(&self.power)?; | ||
| 466 | (freq, Mux::ClkrootFunc1) | ||
| 467 | } | ||
| 468 | AdcClockSel::ClkIn => { | ||
| 469 | let freq = clocks.ensure_clk_in_active(&self.power)?; | ||
| 470 | (freq, Mux::ClkrootFunc3) | ||
| 471 | } | ||
| 472 | AdcClockSel::Clk1M => { | ||
| 473 | let freq = clocks.ensure_clk_1m_active(&self.power)?; | ||
| 474 | (freq, Mux::ClkrootFunc5) | ||
| 475 | } | ||
| 476 | AdcClockSel::Pll1ClkDiv => { | ||
| 477 | let freq = clocks.ensure_pll1_clk_div_active(&self.power)?; | ||
| 478 | (freq, Mux::ClkrootFunc6) | ||
| 479 | } | ||
| 480 | AdcClockSel::None => { | ||
| 481 | mrcc0.mrcc_adc_clksel().write(|w| unsafe { | ||
| 482 | // no ClkrootFunc7, just write manually for now | ||
| 483 | w.mux().bits(0b111) | ||
| 484 | }); | ||
| 485 | mrcc0.mrcc_adc_clkdiv().modify(|_r, w| { | ||
| 486 | w.reset().asserted(); | ||
| 487 | w.halt().asserted(); | ||
| 488 | w | ||
| 489 | }); | ||
| 490 | return Ok(0); | ||
| 491 | } | ||
| 492 | }; | ||
| 493 | let clksel = mrcc0.mrcc_adc_clksel(); | ||
| 494 | let clkdiv = mrcc0.mrcc_adc_clkdiv(); | ||
| 495 | |||
| 496 | apply_div4!(self, clksel, clkdiv, variant, freq) | ||
| 497 | } | ||
| 498 | } | ||
diff --git a/embassy-mcxa/src/config.rs b/embassy-mcxa/src/config.rs new file mode 100644 index 000000000..8d52a1d44 --- /dev/null +++ b/embassy-mcxa/src/config.rs | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // HAL configuration (minimal), mirroring embassy-imxrt style | ||
| 2 | |||
| 3 | use crate::clocks::config::ClocksConfig; | ||
| 4 | use crate::interrupt::Priority; | ||
| 5 | |||
| 6 | #[non_exhaustive] | ||
| 7 | pub struct Config { | ||
| 8 | #[cfg(feature = "time")] | ||
| 9 | pub time_interrupt_priority: Priority, | ||
| 10 | pub rtc_interrupt_priority: Priority, | ||
| 11 | pub adc_interrupt_priority: Priority, | ||
| 12 | pub gpio_interrupt_priority: Priority, | ||
| 13 | pub clock_cfg: ClocksConfig, | ||
| 14 | } | ||
| 15 | |||
| 16 | impl Default for Config { | ||
| 17 | fn default() -> Self { | ||
| 18 | Self { | ||
| 19 | #[cfg(feature = "time")] | ||
| 20 | time_interrupt_priority: Priority::from(0), | ||
| 21 | rtc_interrupt_priority: Priority::from(0), | ||
| 22 | adc_interrupt_priority: Priority::from(0), | ||
| 23 | gpio_interrupt_priority: Priority::from(0), | ||
| 24 | clock_cfg: ClocksConfig::default(), | ||
| 25 | } | ||
| 26 | } | ||
| 27 | } | ||
diff --git a/embassy-mcxa/src/gpio.rs b/embassy-mcxa/src/gpio.rs new file mode 100644 index 000000000..65f8df985 --- /dev/null +++ b/embassy-mcxa/src/gpio.rs | |||
| @@ -0,0 +1,1062 @@ | |||
| 1 | //! GPIO driver built around a type-erased `Flex` pin, similar to other Embassy HALs. | ||
| 2 | //! The exported `Output`/`Input` drivers own a `Flex` so they no longer depend on the | ||
| 3 | //! concrete pin type. | ||
| 4 | |||
| 5 | use core::convert::Infallible; | ||
| 6 | use core::future::Future; | ||
| 7 | use core::marker::PhantomData; | ||
| 8 | use core::pin::pin; | ||
| 9 | |||
| 10 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 11 | use maitake_sync::WaitMap; | ||
| 12 | use paste::paste; | ||
| 13 | |||
| 14 | use crate::pac::interrupt; | ||
| 15 | use crate::pac::port0::pcr0::{Dse, Inv, Mux, Pe, Ps, Sre}; | ||
| 16 | |||
| 17 | struct BitIter(u32); | ||
| 18 | |||
| 19 | impl Iterator for BitIter { | ||
| 20 | type Item = usize; | ||
| 21 | |||
| 22 | fn next(&mut self) -> Option<Self::Item> { | ||
| 23 | match self.0.trailing_zeros() { | ||
| 24 | 32 => None, | ||
| 25 | b => { | ||
| 26 | self.0 &= !(1 << b); | ||
| 27 | Some(b as usize) | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | const PORT_COUNT: usize = 5; | ||
| 34 | |||
| 35 | static PORT_WAIT_MAPS: [WaitMap<usize, ()>; PORT_COUNT] = [ | ||
| 36 | WaitMap::new(), | ||
| 37 | WaitMap::new(), | ||
| 38 | WaitMap::new(), | ||
| 39 | WaitMap::new(), | ||
| 40 | WaitMap::new(), | ||
| 41 | ]; | ||
| 42 | |||
| 43 | fn irq_handler(port_index: usize, gpio_base: *const crate::pac::gpio0::RegisterBlock) { | ||
| 44 | let gpio = unsafe { &*gpio_base }; | ||
| 45 | let isfr = gpio.isfr0().read().bits(); | ||
| 46 | |||
| 47 | for pin in BitIter(isfr) { | ||
| 48 | // Clear all pending interrupts | ||
| 49 | gpio.isfr0().write(|w| unsafe { w.bits(1 << pin) }); | ||
| 50 | gpio.icr(pin).modify(|_, w| w.irqc().irqc0()); // Disable interrupt | ||
| 51 | |||
| 52 | // Wake the corresponding port waker | ||
| 53 | if let Some(w) = PORT_WAIT_MAPS.get(port_index) { | ||
| 54 | w.wake(&pin, ()); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | #[interrupt] | ||
| 60 | fn GPIO0() { | ||
| 61 | irq_handler(0, crate::pac::Gpio0::ptr()); | ||
| 62 | } | ||
| 63 | |||
| 64 | #[interrupt] | ||
| 65 | fn GPIO1() { | ||
| 66 | irq_handler(1, crate::pac::Gpio1::ptr()); | ||
| 67 | } | ||
| 68 | |||
| 69 | #[interrupt] | ||
| 70 | fn GPIO2() { | ||
| 71 | irq_handler(2, crate::pac::Gpio2::ptr()); | ||
| 72 | } | ||
| 73 | |||
| 74 | #[interrupt] | ||
| 75 | fn GPIO3() { | ||
| 76 | irq_handler(3, crate::pac::Gpio3::ptr()); | ||
| 77 | } | ||
| 78 | |||
| 79 | #[interrupt] | ||
| 80 | fn GPIO4() { | ||
| 81 | irq_handler(4, crate::pac::Gpio4::ptr()); | ||
| 82 | } | ||
| 83 | |||
| 84 | pub(crate) unsafe fn init() { | ||
| 85 | use embassy_hal_internal::interrupt::InterruptExt; | ||
| 86 | |||
| 87 | crate::pac::interrupt::GPIO0.enable(); | ||
| 88 | crate::pac::interrupt::GPIO1.enable(); | ||
| 89 | crate::pac::interrupt::GPIO2.enable(); | ||
| 90 | crate::pac::interrupt::GPIO3.enable(); | ||
| 91 | crate::pac::interrupt::GPIO4.enable(); | ||
| 92 | |||
| 93 | cortex_m::interrupt::enable(); | ||
| 94 | } | ||
| 95 | |||
| 96 | /// Logical level for GPIO pins. | ||
| 97 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 98 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 99 | pub enum Level { | ||
| 100 | Low, | ||
| 101 | High, | ||
| 102 | } | ||
| 103 | |||
| 104 | impl From<bool> for Level { | ||
| 105 | fn from(val: bool) -> Self { | ||
| 106 | match val { | ||
| 107 | true => Self::High, | ||
| 108 | false => Self::Low, | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 114 | pub enum Pull { | ||
| 115 | Disabled, | ||
| 116 | Up, | ||
| 117 | Down, | ||
| 118 | } | ||
| 119 | |||
| 120 | impl From<Pull> for (Pe, Ps) { | ||
| 121 | fn from(pull: Pull) -> Self { | ||
| 122 | match pull { | ||
| 123 | Pull::Disabled => (Pe::Pe0, Ps::Ps0), | ||
| 124 | Pull::Up => (Pe::Pe1, Ps::Ps1), | ||
| 125 | Pull::Down => (Pe::Pe1, Ps::Ps0), | ||
| 126 | } | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 131 | pub enum SlewRate { | ||
| 132 | Fast, | ||
| 133 | Slow, | ||
| 134 | } | ||
| 135 | |||
| 136 | impl From<SlewRate> for Sre { | ||
| 137 | fn from(slew_rate: SlewRate) -> Self { | ||
| 138 | match slew_rate { | ||
| 139 | SlewRate::Fast => Sre::Sre0, | ||
| 140 | SlewRate::Slow => Sre::Sre1, | ||
| 141 | } | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 146 | pub enum DriveStrength { | ||
| 147 | Normal, | ||
| 148 | Double, | ||
| 149 | } | ||
| 150 | |||
| 151 | impl From<DriveStrength> for Dse { | ||
| 152 | fn from(strength: DriveStrength) -> Self { | ||
| 153 | match strength { | ||
| 154 | DriveStrength::Normal => Dse::Dse0, | ||
| 155 | DriveStrength::Double => Dse::Dse1, | ||
| 156 | } | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 161 | pub enum Inverter { | ||
| 162 | Disabled, | ||
| 163 | Enabled, | ||
| 164 | } | ||
| 165 | |||
| 166 | impl From<Inverter> for Inv { | ||
| 167 | fn from(strength: Inverter) -> Self { | ||
| 168 | match strength { | ||
| 169 | Inverter::Disabled => Inv::Inv0, | ||
| 170 | Inverter::Enabled => Inv::Inv1, | ||
| 171 | } | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | pub type Gpio = crate::peripherals::GPIO0; | ||
| 176 | |||
| 177 | /// Type-erased representation of a GPIO pin. | ||
| 178 | pub struct AnyPin { | ||
| 179 | port: usize, | ||
| 180 | pin: usize, | ||
| 181 | gpio: &'static crate::pac::gpio0::RegisterBlock, | ||
| 182 | port_reg: &'static crate::pac::port0::RegisterBlock, | ||
| 183 | pcr_reg: &'static crate::pac::port0::Pcr0, | ||
| 184 | } | ||
| 185 | |||
| 186 | impl AnyPin { | ||
| 187 | /// Create an `AnyPin` from raw components. | ||
| 188 | fn new( | ||
| 189 | port: usize, | ||
| 190 | pin: usize, | ||
| 191 | gpio: &'static crate::pac::gpio0::RegisterBlock, | ||
| 192 | port_reg: &'static crate::pac::port0::RegisterBlock, | ||
| 193 | pcr_reg: &'static crate::pac::port0::Pcr0, | ||
| 194 | ) -> Self { | ||
| 195 | Self { | ||
| 196 | port, | ||
| 197 | pin, | ||
| 198 | gpio, | ||
| 199 | port_reg, | ||
| 200 | pcr_reg, | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | #[inline(always)] | ||
| 205 | fn mask(&self) -> u32 { | ||
| 206 | 1 << self.pin | ||
| 207 | } | ||
| 208 | |||
| 209 | #[inline(always)] | ||
| 210 | fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock { | ||
| 211 | self.gpio | ||
| 212 | } | ||
| 213 | |||
| 214 | #[inline(always)] | ||
| 215 | pub fn port_index(&self) -> usize { | ||
| 216 | self.port | ||
| 217 | } | ||
| 218 | |||
| 219 | #[inline(always)] | ||
| 220 | pub fn pin_index(&self) -> usize { | ||
| 221 | self.pin | ||
| 222 | } | ||
| 223 | |||
| 224 | #[inline(always)] | ||
| 225 | fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock { | ||
| 226 | self.port_reg | ||
| 227 | } | ||
| 228 | |||
| 229 | #[inline(always)] | ||
| 230 | fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0 { | ||
| 231 | self.pcr_reg | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | embassy_hal_internal::impl_peripheral!(AnyPin); | ||
| 236 | |||
| 237 | pub(crate) trait SealedPin { | ||
| 238 | fn pin_port(&self) -> usize; | ||
| 239 | |||
| 240 | fn port(&self) -> usize { | ||
| 241 | self.pin_port() / 32 | ||
| 242 | } | ||
| 243 | |||
| 244 | fn pin(&self) -> usize { | ||
| 245 | self.pin_port() % 32 | ||
| 246 | } | ||
| 247 | |||
| 248 | fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock; | ||
| 249 | |||
| 250 | fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock; | ||
| 251 | |||
| 252 | fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0; | ||
| 253 | |||
| 254 | fn set_function(&self, function: Mux); | ||
| 255 | |||
| 256 | fn set_pull(&self, pull: Pull); | ||
| 257 | |||
| 258 | fn set_drive_strength(&self, strength: Dse); | ||
| 259 | |||
| 260 | fn set_slew_rate(&self, slew_rate: Sre); | ||
| 261 | |||
| 262 | fn set_enable_input_buffer(&self); | ||
| 263 | } | ||
| 264 | |||
| 265 | /// GPIO pin trait. | ||
| 266 | #[allow(private_bounds)] | ||
| 267 | pub trait GpioPin: SealedPin + Sized + PeripheralType + Into<AnyPin> + 'static { | ||
| 268 | /// Type-erase the pin. | ||
| 269 | fn degrade(self) -> AnyPin { | ||
| 270 | // SAFETY: This is only called within the GpioPin trait, which is only | ||
| 271 | // implemented within this module on valid pin peripherals and thus | ||
| 272 | // has been verified to be correct. | ||
| 273 | AnyPin::new(self.port(), self.pin(), self.gpio(), self.port_reg(), self.pcr_reg()) | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | impl SealedPin for AnyPin { | ||
| 278 | fn pin_port(&self) -> usize { | ||
| 279 | self.port * 32 + self.pin | ||
| 280 | } | ||
| 281 | |||
| 282 | fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock { | ||
| 283 | self.gpio() | ||
| 284 | } | ||
| 285 | |||
| 286 | fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock { | ||
| 287 | self.port_reg() | ||
| 288 | } | ||
| 289 | |||
| 290 | fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0 { | ||
| 291 | self.pcr_reg() | ||
| 292 | } | ||
| 293 | |||
| 294 | fn set_function(&self, function: Mux) { | ||
| 295 | self.pcr_reg().modify(|_, w| w.mux().variant(function)); | ||
| 296 | } | ||
| 297 | |||
| 298 | fn set_pull(&self, pull: Pull) { | ||
| 299 | let (pull_enable, pull_select) = pull.into(); | ||
| 300 | self.pcr_reg().modify(|_, w| { | ||
| 301 | w.pe().variant(pull_enable); | ||
| 302 | w.ps().variant(pull_select) | ||
| 303 | }); | ||
| 304 | } | ||
| 305 | |||
| 306 | fn set_drive_strength(&self, strength: Dse) { | ||
| 307 | self.pcr_reg().modify(|_, w| w.dse().variant(strength)); | ||
| 308 | } | ||
| 309 | |||
| 310 | fn set_slew_rate(&self, slew_rate: Sre) { | ||
| 311 | self.pcr_reg().modify(|_, w| w.sre().variant(slew_rate)); | ||
| 312 | } | ||
| 313 | |||
| 314 | fn set_enable_input_buffer(&self) { | ||
| 315 | self.pcr_reg().modify(|_, w| w.ibe().ibe1()); | ||
| 316 | } | ||
| 317 | } | ||
| 318 | |||
| 319 | impl GpioPin for AnyPin {} | ||
| 320 | |||
| 321 | macro_rules! impl_pin { | ||
| 322 | ($peri:ident, $port:expr, $pin:expr, $block:ident) => { | ||
| 323 | paste! { | ||
| 324 | impl SealedPin for crate::peripherals::$peri { | ||
| 325 | fn pin_port(&self) -> usize { | ||
| 326 | $port * 32 + $pin | ||
| 327 | } | ||
| 328 | |||
| 329 | fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock { | ||
| 330 | unsafe { &*crate::pac::$block::ptr() } | ||
| 331 | } | ||
| 332 | |||
| 333 | fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock { | ||
| 334 | unsafe { &*crate::pac::[<Port $port>]::ptr() } | ||
| 335 | } | ||
| 336 | |||
| 337 | fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0 { | ||
| 338 | self.port_reg().[<pcr $pin>]() | ||
| 339 | } | ||
| 340 | |||
| 341 | fn set_function(&self, function: Mux) { | ||
| 342 | unsafe { | ||
| 343 | let port_reg = &*crate::pac::[<Port $port>]::ptr(); | ||
| 344 | port_reg.[<pcr $pin>]().modify(|_, w| { | ||
| 345 | w.mux().variant(function) | ||
| 346 | }); | ||
| 347 | } | ||
| 348 | } | ||
| 349 | |||
| 350 | fn set_pull(&self, pull: Pull) { | ||
| 351 | let port_reg = unsafe {&*crate::pac::[<Port $port>]::ptr()}; | ||
| 352 | let (pull_enable, pull_select) = pull.into(); | ||
| 353 | port_reg.[<pcr $pin>]().modify(|_, w| { | ||
| 354 | w.pe().variant(pull_enable); | ||
| 355 | w.ps().variant(pull_select) | ||
| 356 | }); | ||
| 357 | } | ||
| 358 | |||
| 359 | fn set_drive_strength(&self, strength: Dse) { | ||
| 360 | let port_reg = unsafe {&*crate::pac::[<Port $port>]::ptr()}; | ||
| 361 | port_reg.[<pcr $pin>]().modify(|_, w| w.dse().variant(strength)); | ||
| 362 | } | ||
| 363 | |||
| 364 | fn set_slew_rate(&self, slew_rate: Sre) { | ||
| 365 | let port_reg = unsafe {&*crate::pac::[<Port $port>]::ptr()}; | ||
| 366 | port_reg.[<pcr $pin>]().modify(|_, w| w.sre().variant(slew_rate)); | ||
| 367 | } | ||
| 368 | |||
| 369 | fn set_enable_input_buffer(&self) { | ||
| 370 | let port_reg = unsafe {&*crate::pac::[<Port $port>]::ptr()}; | ||
| 371 | port_reg.[<pcr $pin>]().modify(|_, w| w.ibe().ibe1()); | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | impl GpioPin for crate::peripherals::$peri {} | ||
| 376 | |||
| 377 | impl From<crate::peripherals::$peri> for AnyPin { | ||
| 378 | fn from(value: crate::peripherals::$peri) -> Self { | ||
| 379 | value.degrade() | ||
| 380 | } | ||
| 381 | } | ||
| 382 | |||
| 383 | impl crate::peripherals::$peri { | ||
| 384 | /// Convenience helper to obtain a type-erased handle to this pin. | ||
| 385 | pub fn degrade(&self) -> AnyPin { | ||
| 386 | AnyPin::new(self.port(), self.pin(), self.gpio(), self.port_reg(), self.pcr_reg()) | ||
| 387 | } | ||
| 388 | } | ||
| 389 | } | ||
| 390 | }; | ||
| 391 | } | ||
| 392 | |||
| 393 | impl_pin!(P0_0, 0, 0, Gpio0); | ||
| 394 | impl_pin!(P0_1, 0, 1, Gpio0); | ||
| 395 | impl_pin!(P0_2, 0, 2, Gpio0); | ||
| 396 | impl_pin!(P0_3, 0, 3, Gpio0); | ||
| 397 | impl_pin!(P0_4, 0, 4, Gpio0); | ||
| 398 | impl_pin!(P0_5, 0, 5, Gpio0); | ||
| 399 | impl_pin!(P0_6, 0, 6, Gpio0); | ||
| 400 | impl_pin!(P0_7, 0, 7, Gpio0); | ||
| 401 | impl_pin!(P0_8, 0, 8, Gpio0); | ||
| 402 | impl_pin!(P0_9, 0, 9, Gpio0); | ||
| 403 | impl_pin!(P0_10, 0, 10, Gpio0); | ||
| 404 | impl_pin!(P0_11, 0, 11, Gpio0); | ||
| 405 | impl_pin!(P0_12, 0, 12, Gpio0); | ||
| 406 | impl_pin!(P0_13, 0, 13, Gpio0); | ||
| 407 | impl_pin!(P0_14, 0, 14, Gpio0); | ||
| 408 | impl_pin!(P0_15, 0, 15, Gpio0); | ||
| 409 | impl_pin!(P0_16, 0, 16, Gpio0); | ||
| 410 | impl_pin!(P0_17, 0, 17, Gpio0); | ||
| 411 | impl_pin!(P0_18, 0, 18, Gpio0); | ||
| 412 | impl_pin!(P0_19, 0, 19, Gpio0); | ||
| 413 | impl_pin!(P0_20, 0, 20, Gpio0); | ||
| 414 | impl_pin!(P0_21, 0, 21, Gpio0); | ||
| 415 | impl_pin!(P0_22, 0, 22, Gpio0); | ||
| 416 | impl_pin!(P0_23, 0, 23, Gpio0); | ||
| 417 | impl_pin!(P0_24, 0, 24, Gpio0); | ||
| 418 | impl_pin!(P0_25, 0, 25, Gpio0); | ||
| 419 | impl_pin!(P0_26, 0, 26, Gpio0); | ||
| 420 | impl_pin!(P0_27, 0, 27, Gpio0); | ||
| 421 | impl_pin!(P0_28, 0, 28, Gpio0); | ||
| 422 | impl_pin!(P0_29, 0, 29, Gpio0); | ||
| 423 | impl_pin!(P0_30, 0, 30, Gpio0); | ||
| 424 | impl_pin!(P0_31, 0, 31, Gpio0); | ||
| 425 | |||
| 426 | impl_pin!(P1_0, 1, 0, Gpio1); | ||
| 427 | impl_pin!(P1_1, 1, 1, Gpio1); | ||
| 428 | impl_pin!(P1_2, 1, 2, Gpio1); | ||
| 429 | impl_pin!(P1_3, 1, 3, Gpio1); | ||
| 430 | impl_pin!(P1_4, 1, 4, Gpio1); | ||
| 431 | impl_pin!(P1_5, 1, 5, Gpio1); | ||
| 432 | impl_pin!(P1_6, 1, 6, Gpio1); | ||
| 433 | impl_pin!(P1_7, 1, 7, Gpio1); | ||
| 434 | impl_pin!(P1_8, 1, 8, Gpio1); | ||
| 435 | impl_pin!(P1_9, 1, 9, Gpio1); | ||
| 436 | impl_pin!(P1_10, 1, 10, Gpio1); | ||
| 437 | impl_pin!(P1_11, 1, 11, Gpio1); | ||
| 438 | impl_pin!(P1_12, 1, 12, Gpio1); | ||
| 439 | impl_pin!(P1_13, 1, 13, Gpio1); | ||
| 440 | impl_pin!(P1_14, 1, 14, Gpio1); | ||
| 441 | impl_pin!(P1_15, 1, 15, Gpio1); | ||
| 442 | impl_pin!(P1_16, 1, 16, Gpio1); | ||
| 443 | impl_pin!(P1_17, 1, 17, Gpio1); | ||
| 444 | impl_pin!(P1_18, 1, 18, Gpio1); | ||
| 445 | impl_pin!(P1_19, 1, 19, Gpio1); | ||
| 446 | impl_pin!(P1_20, 1, 20, Gpio1); | ||
| 447 | impl_pin!(P1_21, 1, 21, Gpio1); | ||
| 448 | impl_pin!(P1_22, 1, 22, Gpio1); | ||
| 449 | impl_pin!(P1_23, 1, 23, Gpio1); | ||
| 450 | impl_pin!(P1_24, 1, 24, Gpio1); | ||
| 451 | impl_pin!(P1_25, 1, 25, Gpio1); | ||
| 452 | impl_pin!(P1_26, 1, 26, Gpio1); | ||
| 453 | impl_pin!(P1_27, 1, 27, Gpio1); | ||
| 454 | impl_pin!(P1_28, 1, 28, Gpio1); | ||
| 455 | impl_pin!(P1_29, 1, 29, Gpio1); | ||
| 456 | impl_pin!(P1_30, 1, 30, Gpio1); | ||
| 457 | impl_pin!(P1_31, 1, 31, Gpio1); | ||
| 458 | |||
| 459 | impl_pin!(P2_0, 2, 0, Gpio2); | ||
| 460 | impl_pin!(P2_1, 2, 1, Gpio2); | ||
| 461 | impl_pin!(P2_2, 2, 2, Gpio2); | ||
| 462 | impl_pin!(P2_3, 2, 3, Gpio2); | ||
| 463 | impl_pin!(P2_4, 2, 4, Gpio2); | ||
| 464 | impl_pin!(P2_5, 2, 5, Gpio2); | ||
| 465 | impl_pin!(P2_6, 2, 6, Gpio2); | ||
| 466 | impl_pin!(P2_7, 2, 7, Gpio2); | ||
| 467 | impl_pin!(P2_8, 2, 8, Gpio2); | ||
| 468 | impl_pin!(P2_9, 2, 9, Gpio2); | ||
| 469 | impl_pin!(P2_10, 2, 10, Gpio2); | ||
| 470 | impl_pin!(P2_11, 2, 11, Gpio2); | ||
| 471 | impl_pin!(P2_12, 2, 12, Gpio2); | ||
| 472 | impl_pin!(P2_13, 2, 13, Gpio2); | ||
| 473 | impl_pin!(P2_14, 2, 14, Gpio2); | ||
| 474 | impl_pin!(P2_15, 2, 15, Gpio2); | ||
| 475 | impl_pin!(P2_16, 2, 16, Gpio2); | ||
| 476 | impl_pin!(P2_17, 2, 17, Gpio2); | ||
| 477 | impl_pin!(P2_18, 2, 18, Gpio2); | ||
| 478 | impl_pin!(P2_19, 2, 19, Gpio2); | ||
| 479 | impl_pin!(P2_20, 2, 20, Gpio2); | ||
| 480 | impl_pin!(P2_21, 2, 21, Gpio2); | ||
| 481 | impl_pin!(P2_22, 2, 22, Gpio2); | ||
| 482 | impl_pin!(P2_23, 2, 23, Gpio2); | ||
| 483 | impl_pin!(P2_24, 2, 24, Gpio2); | ||
| 484 | impl_pin!(P2_25, 2, 25, Gpio2); | ||
| 485 | impl_pin!(P2_26, 2, 26, Gpio2); | ||
| 486 | impl_pin!(P2_27, 2, 27, Gpio2); | ||
| 487 | impl_pin!(P2_28, 2, 28, Gpio2); | ||
| 488 | impl_pin!(P2_29, 2, 29, Gpio2); | ||
| 489 | impl_pin!(P2_30, 2, 30, Gpio2); | ||
| 490 | impl_pin!(P2_31, 2, 31, Gpio2); | ||
| 491 | |||
| 492 | impl_pin!(P3_0, 3, 0, Gpio3); | ||
| 493 | impl_pin!(P3_1, 3, 1, Gpio3); | ||
| 494 | impl_pin!(P3_2, 3, 2, Gpio3); | ||
| 495 | impl_pin!(P3_3, 3, 3, Gpio3); | ||
| 496 | impl_pin!(P3_4, 3, 4, Gpio3); | ||
| 497 | impl_pin!(P3_5, 3, 5, Gpio3); | ||
| 498 | impl_pin!(P3_6, 3, 6, Gpio3); | ||
| 499 | impl_pin!(P3_7, 3, 7, Gpio3); | ||
| 500 | impl_pin!(P3_8, 3, 8, Gpio3); | ||
| 501 | impl_pin!(P3_9, 3, 9, Gpio3); | ||
| 502 | impl_pin!(P3_10, 3, 10, Gpio3); | ||
| 503 | impl_pin!(P3_11, 3, 11, Gpio3); | ||
| 504 | impl_pin!(P3_12, 3, 12, Gpio3); | ||
| 505 | impl_pin!(P3_13, 3, 13, Gpio3); | ||
| 506 | impl_pin!(P3_14, 3, 14, Gpio3); | ||
| 507 | impl_pin!(P3_15, 3, 15, Gpio3); | ||
| 508 | impl_pin!(P3_16, 3, 16, Gpio3); | ||
| 509 | impl_pin!(P3_17, 3, 17, Gpio3); | ||
| 510 | impl_pin!(P3_18, 3, 18, Gpio3); | ||
| 511 | impl_pin!(P3_19, 3, 19, Gpio3); | ||
| 512 | impl_pin!(P3_20, 3, 20, Gpio3); | ||
| 513 | impl_pin!(P3_21, 3, 21, Gpio3); | ||
| 514 | impl_pin!(P3_22, 3, 22, Gpio3); | ||
| 515 | impl_pin!(P3_23, 3, 23, Gpio3); | ||
| 516 | impl_pin!(P3_24, 3, 24, Gpio3); | ||
| 517 | impl_pin!(P3_25, 3, 25, Gpio3); | ||
| 518 | impl_pin!(P3_26, 3, 26, Gpio3); | ||
| 519 | impl_pin!(P3_27, 3, 27, Gpio3); | ||
| 520 | impl_pin!(P3_28, 3, 28, Gpio3); | ||
| 521 | impl_pin!(P3_29, 3, 29, Gpio3); | ||
| 522 | impl_pin!(P3_30, 3, 30, Gpio3); | ||
| 523 | impl_pin!(P3_31, 3, 31, Gpio3); | ||
| 524 | |||
| 525 | impl_pin!(P4_0, 4, 0, Gpio4); | ||
| 526 | impl_pin!(P4_1, 4, 1, Gpio4); | ||
| 527 | impl_pin!(P4_2, 4, 2, Gpio4); | ||
| 528 | impl_pin!(P4_3, 4, 3, Gpio4); | ||
| 529 | impl_pin!(P4_4, 4, 4, Gpio4); | ||
| 530 | impl_pin!(P4_5, 4, 5, Gpio4); | ||
| 531 | impl_pin!(P4_6, 4, 6, Gpio4); | ||
| 532 | impl_pin!(P4_7, 4, 7, Gpio4); | ||
| 533 | impl_pin!(P4_8, 4, 8, Gpio4); | ||
| 534 | impl_pin!(P4_9, 4, 9, Gpio4); | ||
| 535 | impl_pin!(P4_10, 4, 10, Gpio4); | ||
| 536 | impl_pin!(P4_11, 4, 11, Gpio4); | ||
| 537 | impl_pin!(P4_12, 4, 12, Gpio4); | ||
| 538 | impl_pin!(P4_13, 4, 13, Gpio4); | ||
| 539 | impl_pin!(P4_14, 4, 14, Gpio4); | ||
| 540 | impl_pin!(P4_15, 4, 15, Gpio4); | ||
| 541 | impl_pin!(P4_16, 4, 16, Gpio4); | ||
| 542 | impl_pin!(P4_17, 4, 17, Gpio4); | ||
| 543 | impl_pin!(P4_18, 4, 18, Gpio4); | ||
| 544 | impl_pin!(P4_19, 4, 19, Gpio4); | ||
| 545 | impl_pin!(P4_20, 4, 20, Gpio4); | ||
| 546 | impl_pin!(P4_21, 4, 21, Gpio4); | ||
| 547 | impl_pin!(P4_22, 4, 22, Gpio4); | ||
| 548 | impl_pin!(P4_23, 4, 23, Gpio4); | ||
| 549 | impl_pin!(P4_24, 4, 24, Gpio4); | ||
| 550 | impl_pin!(P4_25, 4, 25, Gpio4); | ||
| 551 | impl_pin!(P4_26, 4, 26, Gpio4); | ||
| 552 | impl_pin!(P4_27, 4, 27, Gpio4); | ||
| 553 | impl_pin!(P4_28, 4, 28, Gpio4); | ||
| 554 | impl_pin!(P4_29, 4, 29, Gpio4); | ||
| 555 | impl_pin!(P4_30, 4, 30, Gpio4); | ||
| 556 | impl_pin!(P4_31, 4, 31, Gpio4); | ||
| 557 | |||
| 558 | /// A flexible pin that can be configured as input or output. | ||
| 559 | pub struct Flex<'d> { | ||
| 560 | pin: Peri<'d, AnyPin>, | ||
| 561 | _marker: PhantomData<&'d mut ()>, | ||
| 562 | } | ||
| 563 | |||
| 564 | impl<'d> Flex<'d> { | ||
| 565 | /// Wrap the pin in a `Flex`. | ||
| 566 | /// | ||
| 567 | /// The pin remains unmodified. The initial output level is unspecified, but | ||
| 568 | /// can be changed before the pin is put into output mode. | ||
| 569 | pub fn new(pin: Peri<'d, impl GpioPin>) -> Self { | ||
| 570 | pin.set_function(Mux::Mux0); | ||
| 571 | Self { | ||
| 572 | pin: pin.into(), | ||
| 573 | _marker: PhantomData, | ||
| 574 | } | ||
| 575 | } | ||
| 576 | |||
| 577 | #[inline] | ||
| 578 | fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock { | ||
| 579 | self.pin.gpio() | ||
| 580 | } | ||
| 581 | |||
| 582 | #[inline] | ||
| 583 | fn mask(&self) -> u32 { | ||
| 584 | self.pin.mask() | ||
| 585 | } | ||
| 586 | |||
| 587 | /// Put the pin into input mode. | ||
| 588 | pub fn set_as_input(&mut self) { | ||
| 589 | let mask = self.mask(); | ||
| 590 | let gpio = self.gpio(); | ||
| 591 | |||
| 592 | self.set_enable_input_buffer(); | ||
| 593 | |||
| 594 | gpio.pddr().modify(|r, w| unsafe { w.bits(r.bits() & !mask) }); | ||
| 595 | } | ||
| 596 | |||
| 597 | /// Put the pin into output mode. | ||
| 598 | pub fn set_as_output(&mut self) { | ||
| 599 | let mask = self.mask(); | ||
| 600 | let gpio = self.gpio(); | ||
| 601 | |||
| 602 | self.set_pull(Pull::Disabled); | ||
| 603 | |||
| 604 | gpio.pddr().modify(|r, w| unsafe { w.bits(r.bits() | mask) }); | ||
| 605 | } | ||
| 606 | |||
| 607 | /// Set output level to High. | ||
| 608 | #[inline] | ||
| 609 | pub fn set_high(&mut self) { | ||
| 610 | self.gpio().psor().write(|w| unsafe { w.bits(self.mask()) }); | ||
| 611 | } | ||
| 612 | |||
| 613 | /// Set output level to Low. | ||
| 614 | #[inline] | ||
| 615 | pub fn set_low(&mut self) { | ||
| 616 | self.gpio().pcor().write(|w| unsafe { w.bits(self.mask()) }); | ||
| 617 | } | ||
| 618 | |||
| 619 | /// Set output level to the given `Level`. | ||
| 620 | #[inline] | ||
| 621 | pub fn set_level(&mut self, level: Level) { | ||
| 622 | match level { | ||
| 623 | Level::High => self.set_high(), | ||
| 624 | Level::Low => self.set_low(), | ||
| 625 | } | ||
| 626 | } | ||
| 627 | |||
| 628 | /// Toggle output level. | ||
| 629 | #[inline] | ||
| 630 | pub fn toggle(&mut self) { | ||
| 631 | self.gpio().ptor().write(|w| unsafe { w.bits(self.mask()) }); | ||
| 632 | } | ||
| 633 | |||
| 634 | /// Get whether the pin input level is high. | ||
| 635 | #[inline] | ||
| 636 | pub fn is_high(&self) -> bool { | ||
| 637 | (self.gpio().pdir().read().bits() & self.mask()) != 0 | ||
| 638 | } | ||
| 639 | |||
| 640 | /// Get whether the pin input level is low. | ||
| 641 | #[inline] | ||
| 642 | pub fn is_low(&self) -> bool { | ||
| 643 | !self.is_high() | ||
| 644 | } | ||
| 645 | |||
| 646 | /// Is the output pin set as high? | ||
| 647 | #[inline] | ||
| 648 | pub fn is_set_high(&self) -> bool { | ||
| 649 | self.is_high() | ||
| 650 | } | ||
| 651 | |||
| 652 | /// Is the output pin set as low? | ||
| 653 | #[inline] | ||
| 654 | pub fn is_set_low(&self) -> bool { | ||
| 655 | !self.is_set_high() | ||
| 656 | } | ||
| 657 | |||
| 658 | /// Configure the pin pull up/down level. | ||
| 659 | pub fn set_pull(&mut self, pull_select: Pull) { | ||
| 660 | self.pin.set_pull(pull_select); | ||
| 661 | } | ||
| 662 | |||
| 663 | /// Configure the pin drive strength. | ||
| 664 | pub fn set_drive_strength(&mut self, strength: DriveStrength) { | ||
| 665 | self.pin.set_drive_strength(strength.into()); | ||
| 666 | } | ||
| 667 | |||
| 668 | /// Configure the pin slew rate. | ||
| 669 | pub fn set_slew_rate(&mut self, slew_rate: SlewRate) { | ||
| 670 | self.pin.set_slew_rate(slew_rate.into()); | ||
| 671 | } | ||
| 672 | |||
| 673 | /// Enable input buffer for the pin. | ||
| 674 | pub fn set_enable_input_buffer(&mut self) { | ||
| 675 | self.pin.set_enable_input_buffer(); | ||
| 676 | } | ||
| 677 | |||
| 678 | /// Get pin level. | ||
| 679 | pub fn get_level(&self) -> Level { | ||
| 680 | self.is_high().into() | ||
| 681 | } | ||
| 682 | } | ||
| 683 | |||
| 684 | /// Async methods | ||
| 685 | impl<'d> Flex<'d> { | ||
| 686 | /// Helper function that waits for a given interrupt trigger | ||
| 687 | async fn wait_for_inner(&mut self, level: crate::pac::gpio0::icr::Irqc) { | ||
| 688 | // First, ensure that we have a waker that is ready for this port+pin | ||
| 689 | let w = PORT_WAIT_MAPS[self.pin.port].wait(self.pin.pin); | ||
| 690 | let mut w = pin!(w); | ||
| 691 | // Wait for the subscription to occur, which requires polling at least once | ||
| 692 | // | ||
| 693 | // This function returns a result, but can only be an Err if: | ||
| 694 | // | ||
| 695 | // * We call `.close()` on a WaitMap, which we never do | ||
| 696 | // * We have a duplicate key, which can't happen because `wait_for_*` methods | ||
| 697 | // take an &mut ref of their unique port+pin combo | ||
| 698 | // | ||
| 699 | // So we wait for it to complete, but ignore the result. | ||
| 700 | _ = w.as_mut().subscribe().await; | ||
| 701 | |||
| 702 | // Now that our waker is in the map, we can enable the appropriate interrupt | ||
| 703 | // | ||
| 704 | // Clear any existing pending interrupt on this pin | ||
| 705 | self.pin | ||
| 706 | .gpio() | ||
| 707 | .isfr0() | ||
| 708 | .write(|w| unsafe { w.bits(1 << self.pin.pin()) }); | ||
| 709 | self.pin.gpio().icr(self.pin.pin()).write(|w| w.isf().isf1()); | ||
| 710 | |||
| 711 | // Pin interrupt configuration | ||
| 712 | self.pin | ||
| 713 | .gpio() | ||
| 714 | .icr(self.pin.pin()) | ||
| 715 | .modify(|_, w| w.irqc().variant(level)); | ||
| 716 | |||
| 717 | // Finally, we can await the matching call to `.wake()` from the interrupt. | ||
| 718 | // | ||
| 719 | // Again, technically, this could return a result, but for the same reasons | ||
| 720 | // as above, this can't be an error in our case, so just wait for it to complete | ||
| 721 | _ = w.await; | ||
| 722 | } | ||
| 723 | |||
| 724 | /// Wait until the pin is high. If it is already high, return immediately. | ||
| 725 | #[inline] | ||
| 726 | pub fn wait_for_high(&mut self) -> impl Future<Output = ()> + use<'_, 'd> { | ||
| 727 | self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc12) | ||
| 728 | } | ||
| 729 | |||
| 730 | /// Wait until the pin is low. If it is already low, return immediately. | ||
| 731 | #[inline] | ||
| 732 | pub fn wait_for_low(&mut self) -> impl Future<Output = ()> + use<'_, 'd> { | ||
| 733 | self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc8) | ||
| 734 | } | ||
| 735 | |||
| 736 | /// Wait for the pin to undergo a transition from low to high. | ||
| 737 | #[inline] | ||
| 738 | pub fn wait_for_rising_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> { | ||
| 739 | self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc9) | ||
| 740 | } | ||
| 741 | |||
| 742 | /// Wait for the pin to undergo a transition from high to low. | ||
| 743 | #[inline] | ||
| 744 | pub fn wait_for_falling_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> { | ||
| 745 | self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc10) | ||
| 746 | } | ||
| 747 | |||
| 748 | /// Wait for the pin to undergo any transition, i.e low to high OR high to low. | ||
| 749 | #[inline] | ||
| 750 | pub fn wait_for_any_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> { | ||
| 751 | self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc11) | ||
| 752 | } | ||
| 753 | } | ||
| 754 | |||
| 755 | /// GPIO output driver that owns a `Flex` pin. | ||
| 756 | pub struct Output<'d> { | ||
| 757 | flex: Flex<'d>, | ||
| 758 | } | ||
| 759 | |||
| 760 | impl<'d> Output<'d> { | ||
| 761 | /// Create a GPIO output driver for a [GpioPin] with the provided [Level]. | ||
| 762 | pub fn new(pin: Peri<'d, impl GpioPin>, initial: Level, strength: DriveStrength, slew_rate: SlewRate) -> Self { | ||
| 763 | let mut flex = Flex::new(pin); | ||
| 764 | flex.set_level(initial); | ||
| 765 | flex.set_as_output(); | ||
| 766 | flex.set_drive_strength(strength); | ||
| 767 | flex.set_slew_rate(slew_rate); | ||
| 768 | Self { flex } | ||
| 769 | } | ||
| 770 | |||
| 771 | /// Set the output as high. | ||
| 772 | #[inline] | ||
| 773 | pub fn set_high(&mut self) { | ||
| 774 | self.flex.set_high(); | ||
| 775 | } | ||
| 776 | |||
| 777 | /// Set the output as low. | ||
| 778 | #[inline] | ||
| 779 | pub fn set_low(&mut self) { | ||
| 780 | self.flex.set_low(); | ||
| 781 | } | ||
| 782 | |||
| 783 | /// Set the output level. | ||
| 784 | #[inline] | ||
| 785 | pub fn set_level(&mut self, level: Level) { | ||
| 786 | self.flex.set_level(level); | ||
| 787 | } | ||
| 788 | |||
| 789 | /// Toggle the output level. | ||
| 790 | #[inline] | ||
| 791 | pub fn toggle(&mut self) { | ||
| 792 | self.flex.toggle(); | ||
| 793 | } | ||
| 794 | |||
| 795 | /// Is the output pin set as high? | ||
| 796 | #[inline] | ||
| 797 | pub fn is_set_high(&self) -> bool { | ||
| 798 | self.flex.is_high() | ||
| 799 | } | ||
| 800 | |||
| 801 | /// Is the output pin set as low? | ||
| 802 | #[inline] | ||
| 803 | pub fn is_set_low(&self) -> bool { | ||
| 804 | !self.is_set_high() | ||
| 805 | } | ||
| 806 | |||
| 807 | /// Expose the inner `Flex` if callers need to reconfigure the pin. | ||
| 808 | #[inline] | ||
| 809 | pub fn into_flex(self) -> Flex<'d> { | ||
| 810 | self.flex | ||
| 811 | } | ||
| 812 | } | ||
| 813 | |||
| 814 | /// GPIO input driver that owns a `Flex` pin. | ||
| 815 | pub struct Input<'d> { | ||
| 816 | flex: Flex<'d>, | ||
| 817 | } | ||
| 818 | |||
| 819 | impl<'d> Input<'d> { | ||
| 820 | /// Create a GPIO input driver for a [GpioPin]. | ||
| 821 | /// | ||
| 822 | pub fn new(pin: Peri<'d, impl GpioPin>, pull_select: Pull) -> Self { | ||
| 823 | let mut flex = Flex::new(pin); | ||
| 824 | flex.set_as_input(); | ||
| 825 | flex.set_pull(pull_select); | ||
| 826 | Self { flex } | ||
| 827 | } | ||
| 828 | |||
| 829 | /// Get whether the pin input level is high. | ||
| 830 | #[inline] | ||
| 831 | pub fn is_high(&self) -> bool { | ||
| 832 | self.flex.is_high() | ||
| 833 | } | ||
| 834 | |||
| 835 | /// Get whether the pin input level is low. | ||
| 836 | #[inline] | ||
| 837 | pub fn is_low(&self) -> bool { | ||
| 838 | self.flex.is_low() | ||
| 839 | } | ||
| 840 | |||
| 841 | /// Expose the inner `Flex` if callers need to reconfigure the pin. | ||
| 842 | /// | ||
| 843 | /// Since Drive Strength and Slew Rate are not set when creating the Input | ||
| 844 | /// pin, they need to be set when converting | ||
| 845 | #[inline] | ||
| 846 | pub fn into_flex(mut self, strength: DriveStrength, slew_rate: SlewRate) -> Flex<'d> { | ||
| 847 | self.flex.set_drive_strength(strength); | ||
| 848 | self.flex.set_slew_rate(slew_rate); | ||
| 849 | self.flex | ||
| 850 | } | ||
| 851 | |||
| 852 | /// Get the pin level. | ||
| 853 | pub fn get_level(&self) -> Level { | ||
| 854 | self.flex.get_level() | ||
| 855 | } | ||
| 856 | } | ||
| 857 | |||
| 858 | /// Async methods | ||
| 859 | impl<'d> Input<'d> { | ||
| 860 | /// Wait until the pin is high. If it is already high, return immediately. | ||
| 861 | #[inline] | ||
| 862 | pub fn wait_for_high(&mut self) -> impl Future<Output = ()> + use<'_, 'd> { | ||
| 863 | self.flex.wait_for_high() | ||
| 864 | } | ||
| 865 | |||
| 866 | /// Wait until the pin is low. If it is already low, return immediately. | ||
| 867 | #[inline] | ||
| 868 | pub fn wait_for_low(&mut self) -> impl Future<Output = ()> + use<'_, 'd> { | ||
| 869 | self.flex.wait_for_low() | ||
| 870 | } | ||
| 871 | |||
| 872 | /// Wait for the pin to undergo a transition from low to high. | ||
| 873 | #[inline] | ||
| 874 | pub fn wait_for_rising_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> { | ||
| 875 | self.flex.wait_for_rising_edge() | ||
| 876 | } | ||
| 877 | |||
| 878 | /// Wait for the pin to undergo a transition from high to low. | ||
| 879 | #[inline] | ||
| 880 | pub fn wait_for_falling_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> { | ||
| 881 | self.flex.wait_for_falling_edge() | ||
| 882 | } | ||
| 883 | |||
| 884 | /// Wait for the pin to undergo any transition, i.e low to high OR high to low. | ||
| 885 | #[inline] | ||
| 886 | pub fn wait_for_any_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> { | ||
| 887 | self.flex.wait_for_any_edge() | ||
| 888 | } | ||
| 889 | } | ||
| 890 | |||
| 891 | impl embedded_hal_async::digital::Wait for Input<'_> { | ||
| 892 | async fn wait_for_high(&mut self) -> Result<(), Self::Error> { | ||
| 893 | self.wait_for_high().await; | ||
| 894 | Ok(()) | ||
| 895 | } | ||
| 896 | |||
| 897 | async fn wait_for_low(&mut self) -> Result<(), Self::Error> { | ||
| 898 | self.wait_for_low().await; | ||
| 899 | Ok(()) | ||
| 900 | } | ||
| 901 | |||
| 902 | async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { | ||
| 903 | self.wait_for_rising_edge().await; | ||
| 904 | Ok(()) | ||
| 905 | } | ||
| 906 | |||
| 907 | async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { | ||
| 908 | self.wait_for_falling_edge().await; | ||
| 909 | Ok(()) | ||
| 910 | } | ||
| 911 | |||
| 912 | async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { | ||
| 913 | self.wait_for_any_edge().await; | ||
| 914 | Ok(()) | ||
| 915 | } | ||
| 916 | } | ||
| 917 | |||
| 918 | impl embedded_hal_async::digital::Wait for Flex<'_> { | ||
| 919 | async fn wait_for_high(&mut self) -> Result<(), Self::Error> { | ||
| 920 | self.wait_for_high().await; | ||
| 921 | Ok(()) | ||
| 922 | } | ||
| 923 | |||
| 924 | async fn wait_for_low(&mut self) -> Result<(), Self::Error> { | ||
| 925 | self.wait_for_low().await; | ||
| 926 | Ok(()) | ||
| 927 | } | ||
| 928 | |||
| 929 | async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { | ||
| 930 | self.wait_for_rising_edge().await; | ||
| 931 | Ok(()) | ||
| 932 | } | ||
| 933 | |||
| 934 | async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { | ||
| 935 | self.wait_for_falling_edge().await; | ||
| 936 | Ok(()) | ||
| 937 | } | ||
| 938 | |||
| 939 | async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { | ||
| 940 | self.wait_for_any_edge().await; | ||
| 941 | Ok(()) | ||
| 942 | } | ||
| 943 | } | ||
| 944 | |||
| 945 | // Both embedded_hal 0.2 and 1.0 must be supported by embassy HALs. | ||
| 946 | impl embedded_hal_02::digital::v2::InputPin for Flex<'_> { | ||
| 947 | // GPIO operations on this block cannot fail, therefor we set the error type | ||
| 948 | // to Infallible to guarantee that we can only produce Ok variants. | ||
| 949 | type Error = Infallible; | ||
| 950 | |||
| 951 | #[inline] | ||
| 952 | fn is_high(&self) -> Result<bool, Self::Error> { | ||
| 953 | Ok(self.is_high()) | ||
| 954 | } | ||
| 955 | |||
| 956 | #[inline] | ||
| 957 | fn is_low(&self) -> Result<bool, Self::Error> { | ||
| 958 | Ok(self.is_low()) | ||
| 959 | } | ||
| 960 | } | ||
| 961 | |||
| 962 | impl embedded_hal_02::digital::v2::InputPin for Input<'_> { | ||
| 963 | type Error = Infallible; | ||
| 964 | |||
| 965 | #[inline] | ||
| 966 | fn is_high(&self) -> Result<bool, Self::Error> { | ||
| 967 | Ok(self.is_high()) | ||
| 968 | } | ||
| 969 | |||
| 970 | #[inline] | ||
| 971 | fn is_low(&self) -> Result<bool, Self::Error> { | ||
| 972 | Ok(self.is_low()) | ||
| 973 | } | ||
| 974 | } | ||
| 975 | |||
| 976 | impl embedded_hal_02::digital::v2::OutputPin for Flex<'_> { | ||
| 977 | type Error = Infallible; | ||
| 978 | |||
| 979 | #[inline] | ||
| 980 | fn set_high(&mut self) -> Result<(), Self::Error> { | ||
| 981 | self.set_high(); | ||
| 982 | Ok(()) | ||
| 983 | } | ||
| 984 | |||
| 985 | #[inline] | ||
| 986 | fn set_low(&mut self) -> Result<(), Self::Error> { | ||
| 987 | self.set_low(); | ||
| 988 | Ok(()) | ||
| 989 | } | ||
| 990 | } | ||
| 991 | |||
| 992 | impl embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'_> { | ||
| 993 | #[inline] | ||
| 994 | fn is_set_high(&self) -> Result<bool, Self::Error> { | ||
| 995 | Ok(self.is_set_high()) | ||
| 996 | } | ||
| 997 | |||
| 998 | #[inline] | ||
| 999 | fn is_set_low(&self) -> Result<bool, Self::Error> { | ||
| 1000 | Ok(self.is_set_low()) | ||
| 1001 | } | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | impl embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'_> { | ||
| 1005 | type Error = Infallible; | ||
| 1006 | |||
| 1007 | #[inline] | ||
| 1008 | fn toggle(&mut self) -> Result<(), Self::Error> { | ||
| 1009 | self.toggle(); | ||
| 1010 | Ok(()) | ||
| 1011 | } | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | impl embedded_hal_1::digital::ErrorType for Flex<'_> { | ||
| 1015 | type Error = Infallible; | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | impl embedded_hal_1::digital::ErrorType for Input<'_> { | ||
| 1019 | type Error = Infallible; | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | impl embedded_hal_1::digital::ErrorType for Output<'_> { | ||
| 1023 | type Error = Infallible; | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | impl embedded_hal_1::digital::InputPin for Input<'_> { | ||
| 1027 | #[inline] | ||
| 1028 | fn is_high(&mut self) -> Result<bool, Self::Error> { | ||
| 1029 | Ok((*self).is_high()) | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | #[inline] | ||
| 1033 | fn is_low(&mut self) -> Result<bool, Self::Error> { | ||
| 1034 | Ok((*self).is_low()) | ||
| 1035 | } | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | impl embedded_hal_1::digital::OutputPin for Flex<'_> { | ||
| 1039 | #[inline] | ||
| 1040 | fn set_high(&mut self) -> Result<(), Self::Error> { | ||
| 1041 | self.set_high(); | ||
| 1042 | Ok(()) | ||
| 1043 | } | ||
| 1044 | |||
| 1045 | #[inline] | ||
| 1046 | fn set_low(&mut self) -> Result<(), Self::Error> { | ||
| 1047 | self.set_low(); | ||
| 1048 | Ok(()) | ||
| 1049 | } | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | impl embedded_hal_1::digital::StatefulOutputPin for Flex<'_> { | ||
| 1053 | #[inline] | ||
| 1054 | fn is_set_high(&mut self) -> Result<bool, Self::Error> { | ||
| 1055 | Ok((*self).is_set_high()) | ||
| 1056 | } | ||
| 1057 | |||
| 1058 | #[inline] | ||
| 1059 | fn is_set_low(&mut self) -> Result<bool, Self::Error> { | ||
| 1060 | Ok((*self).is_set_low()) | ||
| 1061 | } | ||
| 1062 | } | ||
diff --git a/embassy-mcxa/src/i2c/controller.rs b/embassy-mcxa/src/i2c/controller.rs new file mode 100644 index 000000000..c27d508b0 --- /dev/null +++ b/embassy-mcxa/src/i2c/controller.rs | |||
| @@ -0,0 +1,742 @@ | |||
| 1 | //! LPI2C controller driver | ||
| 2 | |||
| 3 | use core::future::Future; | ||
| 4 | use core::marker::PhantomData; | ||
| 5 | |||
| 6 | use embassy_hal_internal::Peri; | ||
| 7 | use embassy_hal_internal::drop::OnDrop; | ||
| 8 | use mcxa_pac::lpi2c0::mtdr::Cmd; | ||
| 9 | |||
| 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}; | ||
| 13 | use crate::clocks::{PoweredClock, enable_and_reset}; | ||
| 14 | use crate::interrupt::typelevel::Interrupt; | ||
| 15 | |||
| 16 | /// Bus speed (nominal SCL, no clock stretching) | ||
| 17 | #[derive(Clone, Copy, Default, PartialEq)] | ||
| 18 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 19 | pub enum Speed { | ||
| 20 | #[default] | ||
| 21 | /// 100 kbit/sec | ||
| 22 | Standard, | ||
| 23 | /// 400 kbit/sec | ||
| 24 | Fast, | ||
| 25 | /// 1 Mbit/sec | ||
| 26 | FastPlus, | ||
| 27 | /// 3.4 Mbit/sec | ||
| 28 | UltraFast, | ||
| 29 | } | ||
| 30 | |||
| 31 | impl From<Speed> for (u8, u8, u8, u8) { | ||
| 32 | fn from(value: Speed) -> (u8, u8, u8, u8) { | ||
| 33 | match value { | ||
| 34 | Speed::Standard => (0x3d, 0x37, 0x3b, 0x1d), | ||
| 35 | Speed::Fast => (0x0e, 0x0c, 0x0d, 0x06), | ||
| 36 | Speed::FastPlus => (0x04, 0x03, 0x03, 0x02), | ||
| 37 | |||
| 38 | // UltraFast is "special". Leaving it unimplemented until | ||
| 39 | // the driver and the clock API is further stabilized. | ||
| 40 | Speed::UltraFast => todo!(), | ||
| 41 | } | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 46 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 47 | enum SendStop { | ||
| 48 | No, | ||
| 49 | Yes, | ||
| 50 | } | ||
| 51 | |||
| 52 | /// I2C controller configuration | ||
| 53 | #[derive(Clone, Copy, Default)] | ||
| 54 | #[non_exhaustive] | ||
| 55 | pub struct Config { | ||
| 56 | /// Bus speed | ||
| 57 | pub speed: Speed, | ||
| 58 | } | ||
| 59 | |||
| 60 | /// I2C Controller Driver. | ||
| 61 | pub struct I2c<'d, T: Instance, M: Mode> { | ||
| 62 | _peri: Peri<'d, T>, | ||
| 63 | _scl: Peri<'d, AnyPin>, | ||
| 64 | _sda: Peri<'d, AnyPin>, | ||
| 65 | _phantom: PhantomData<M>, | ||
| 66 | is_hs: bool, | ||
| 67 | } | ||
| 68 | |||
| 69 | impl<'d, T: Instance> I2c<'d, T, Blocking> { | ||
| 70 | /// Create a new blocking instance of the I2C Controller bus driver. | ||
| 71 | pub fn new_blocking( | ||
| 72 | peri: Peri<'d, T>, | ||
| 73 | scl: Peri<'d, impl SclPin<T>>, | ||
| 74 | sda: Peri<'d, impl SdaPin<T>>, | ||
| 75 | config: Config, | ||
| 76 | ) -> Result<Self> { | ||
| 77 | Self::new_inner(peri, scl, sda, config) | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { | ||
| 82 | fn new_inner( | ||
| 83 | _peri: Peri<'d, T>, | ||
| 84 | scl: Peri<'d, impl SclPin<T>>, | ||
| 85 | sda: Peri<'d, impl SdaPin<T>>, | ||
| 86 | config: Config, | ||
| 87 | ) -> Result<Self> { | ||
| 88 | let (power, source, div) = Self::clock_config(config.speed); | ||
| 89 | |||
| 90 | // Enable clocks | ||
| 91 | let conf = Lpi2cConfig { | ||
| 92 | power, | ||
| 93 | source, | ||
| 94 | div, | ||
| 95 | instance: T::CLOCK_INSTANCE, | ||
| 96 | }; | ||
| 97 | |||
| 98 | _ = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? }; | ||
| 99 | |||
| 100 | scl.mux(); | ||
| 101 | sda.mux(); | ||
| 102 | |||
| 103 | let _scl = scl.into(); | ||
| 104 | let _sda = sda.into(); | ||
| 105 | |||
| 106 | Self::set_config(&config)?; | ||
| 107 | |||
| 108 | Ok(Self { | ||
| 109 | _peri, | ||
| 110 | _scl, | ||
| 111 | _sda, | ||
| 112 | _phantom: PhantomData, | ||
| 113 | is_hs: config.speed == Speed::UltraFast, | ||
| 114 | }) | ||
| 115 | } | ||
| 116 | |||
| 117 | fn set_config(config: &Config) -> Result<()> { | ||
| 118 | // Disable the controller. | ||
| 119 | critical_section::with(|_| T::regs().mcr().modify(|_, w| w.men().disabled())); | ||
| 120 | |||
| 121 | // Soft-reset the controller, read and write FIFOs. | ||
| 122 | Self::reset_fifos(); | ||
| 123 | critical_section::with(|_| { | ||
| 124 | T::regs().mcr().modify(|_, w| w.rst().reset()); | ||
| 125 | // According to Reference Manual section 40.7.1.4, "There | ||
| 126 | // is no minimum delay required before clearing the | ||
| 127 | // software reset", therefore we clear it immediately. | ||
| 128 | T::regs().mcr().modify(|_, w| w.rst().not_reset()); | ||
| 129 | |||
| 130 | T::regs().mcr().modify(|_, w| w.dozen().clear_bit().dbgen().clear_bit()); | ||
| 131 | }); | ||
| 132 | |||
| 133 | let (clklo, clkhi, sethold, datavd) = config.speed.into(); | ||
| 134 | |||
| 135 | critical_section::with(|_| { | ||
| 136 | T::regs().mccr0().modify(|_, w| unsafe { | ||
| 137 | w.clklo() | ||
| 138 | .bits(clklo) | ||
| 139 | .clkhi() | ||
| 140 | .bits(clkhi) | ||
| 141 | .sethold() | ||
| 142 | .bits(sethold) | ||
| 143 | .datavd() | ||
| 144 | .bits(datavd) | ||
| 145 | }) | ||
| 146 | }); | ||
| 147 | |||
| 148 | // Enable the controller. | ||
| 149 | critical_section::with(|_| T::regs().mcr().modify(|_, w| w.men().enabled())); | ||
| 150 | |||
| 151 | // Clear all flags | ||
| 152 | T::regs().msr().write(|w| { | ||
| 153 | w.epf() | ||
| 154 | .clear_bit_by_one() | ||
| 155 | .sdf() | ||
| 156 | .clear_bit_by_one() | ||
| 157 | .ndf() | ||
| 158 | .clear_bit_by_one() | ||
| 159 | .alf() | ||
| 160 | .clear_bit_by_one() | ||
| 161 | .fef() | ||
| 162 | .clear_bit_by_one() | ||
| 163 | .pltf() | ||
| 164 | .clear_bit_by_one() | ||
| 165 | .dmf() | ||
| 166 | .clear_bit_by_one() | ||
| 167 | .stf() | ||
| 168 | .clear_bit_by_one() | ||
| 169 | }); | ||
| 170 | |||
| 171 | Ok(()) | ||
| 172 | } | ||
| 173 | |||
| 174 | // REVISIT: turn this into a function of the speed parameter | ||
| 175 | fn clock_config(speed: Speed) -> (PoweredClock, Lpi2cClockSel, Div4) { | ||
| 176 | match speed { | ||
| 177 | Speed::Standard | Speed::Fast | Speed::FastPlus => ( | ||
| 178 | PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 179 | Lpi2cClockSel::FroLfDiv, | ||
| 180 | const { Div4::no_div() }, | ||
| 181 | ), | ||
| 182 | Speed::UltraFast => ( | ||
| 183 | PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 184 | Lpi2cClockSel::FroHfDiv, | ||
| 185 | const { Div4::no_div() }, | ||
| 186 | ), | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | /// Resets both TX and RX FIFOs dropping their contents. | ||
| 191 | fn reset_fifos() { | ||
| 192 | critical_section::with(|_| { | ||
| 193 | T::regs().mcr().modify(|_, w| w.rtf().reset().rrf().reset()); | ||
| 194 | }); | ||
| 195 | } | ||
| 196 | |||
| 197 | /// Checks whether the TX FIFO is full | ||
| 198 | fn is_tx_fifo_full() -> bool { | ||
| 199 | let txfifo_size = 1 << T::regs().param().read().mtxfifo().bits(); | ||
| 200 | T::regs().mfsr().read().txcount().bits() == txfifo_size | ||
| 201 | } | ||
| 202 | |||
| 203 | /// Checks whether the TX FIFO is empty | ||
| 204 | fn is_tx_fifo_empty() -> bool { | ||
| 205 | T::regs().mfsr().read().txcount() == 0 | ||
| 206 | } | ||
| 207 | |||
| 208 | /// Checks whether the RX FIFO is empty. | ||
| 209 | fn is_rx_fifo_empty() -> bool { | ||
| 210 | T::regs().mfsr().read().rxcount() == 0 | ||
| 211 | } | ||
| 212 | |||
| 213 | /// Reads and parses the controller status producing an | ||
| 214 | /// appropriate `Result<(), Error>` variant. | ||
| 215 | fn status() -> Result<()> { | ||
| 216 | let msr = T::regs().msr().read(); | ||
| 217 | T::regs().msr().write(|w| { | ||
| 218 | w.epf() | ||
| 219 | .clear_bit_by_one() | ||
| 220 | .sdf() | ||
| 221 | .clear_bit_by_one() | ||
| 222 | .ndf() | ||
| 223 | .clear_bit_by_one() | ||
| 224 | .alf() | ||
| 225 | .clear_bit_by_one() | ||
| 226 | .fef() | ||
| 227 | .clear_bit_by_one() | ||
| 228 | .fef() | ||
| 229 | .clear_bit_by_one() | ||
| 230 | .pltf() | ||
| 231 | .clear_bit_by_one() | ||
| 232 | .dmf() | ||
| 233 | .clear_bit_by_one() | ||
| 234 | .stf() | ||
| 235 | .clear_bit_by_one() | ||
| 236 | }); | ||
| 237 | |||
| 238 | if msr.ndf().bit_is_set() { | ||
| 239 | Err(Error::AddressNack) | ||
| 240 | } else if msr.alf().bit_is_set() { | ||
| 241 | Err(Error::ArbitrationLoss) | ||
| 242 | } else if msr.fef().bit_is_set() { | ||
| 243 | Err(Error::FifoError) | ||
| 244 | } else { | ||
| 245 | Ok(()) | ||
| 246 | } | ||
| 247 | } | ||
| 248 | |||
| 249 | /// Inserts the given command into the outgoing FIFO. | ||
| 250 | /// | ||
| 251 | /// Caller must ensure there is space in the FIFO for the new | ||
| 252 | /// command. | ||
| 253 | fn send_cmd(cmd: Cmd, data: u8) { | ||
| 254 | #[cfg(feature = "defmt")] | ||
| 255 | defmt::trace!( | ||
| 256 | "Sending cmd '{}' ({}) with data '{:08x}' MSR: {:08x}", | ||
| 257 | cmd, | ||
| 258 | cmd as u8, | ||
| 259 | data, | ||
| 260 | T::regs().msr().read().bits() | ||
| 261 | ); | ||
| 262 | |||
| 263 | T::regs() | ||
| 264 | .mtdr() | ||
| 265 | .write(|w| unsafe { w.data().bits(data) }.cmd().variant(cmd)); | ||
| 266 | } | ||
| 267 | |||
| 268 | /// Prepares an appropriate Start condition on bus by issuing a | ||
| 269 | /// `Start` command together with the device address and R/w bit. | ||
| 270 | /// | ||
| 271 | /// Blocks waiting for space in the FIFO to become available, then | ||
| 272 | /// sends the command and blocks waiting for the FIFO to become | ||
| 273 | /// empty ensuring the command was sent. | ||
| 274 | fn start(&mut self, address: u8, read: bool) -> Result<()> { | ||
| 275 | if address >= 0x80 { | ||
| 276 | return Err(Error::AddressOutOfRange(address)); | ||
| 277 | } | ||
| 278 | |||
| 279 | // Wait until we have space in the TxFIFO | ||
| 280 | while Self::is_tx_fifo_full() {} | ||
| 281 | |||
| 282 | let addr_rw = address << 1 | if read { 1 } else { 0 }; | ||
| 283 | Self::send_cmd(if self.is_hs { Cmd::StartHs } else { Cmd::Start }, addr_rw); | ||
| 284 | |||
| 285 | // Wait for TxFIFO to be drained | ||
| 286 | while !Self::is_tx_fifo_empty() {} | ||
| 287 | |||
| 288 | // Check controller status | ||
| 289 | Self::status() | ||
| 290 | } | ||
| 291 | |||
| 292 | /// Prepares a Stop condition on the bus. | ||
| 293 | /// | ||
| 294 | /// Analogous to `start`, this blocks waiting for space in the | ||
| 295 | /// FIFO to become available, then sends the command and blocks | ||
| 296 | /// waiting for the FIFO to become empty ensuring the command was | ||
| 297 | /// sent. | ||
| 298 | fn stop() -> Result<()> { | ||
| 299 | // Wait until we have space in the TxFIFO | ||
| 300 | while Self::is_tx_fifo_full() {} | ||
| 301 | |||
| 302 | Self::send_cmd(Cmd::Stop, 0); | ||
| 303 | |||
| 304 | // Wait for TxFIFO to be drained | ||
| 305 | while !Self::is_tx_fifo_empty() {} | ||
| 306 | |||
| 307 | Self::status() | ||
| 308 | } | ||
| 309 | |||
| 310 | fn blocking_read_internal(&mut self, address: u8, read: &mut [u8], send_stop: SendStop) -> Result<()> { | ||
| 311 | if read.is_empty() { | ||
| 312 | return Err(Error::InvalidReadBufferLength); | ||
| 313 | } | ||
| 314 | |||
| 315 | for chunk in read.chunks_mut(256) { | ||
| 316 | self.start(address, true)?; | ||
| 317 | |||
| 318 | // Wait until we have space in the TxFIFO | ||
| 319 | while Self::is_tx_fifo_full() {} | ||
| 320 | |||
| 321 | Self::send_cmd(Cmd::Receive, (chunk.len() - 1) as u8); | ||
| 322 | |||
| 323 | for byte in chunk.iter_mut() { | ||
| 324 | // Wait until there's data in the RxFIFO | ||
| 325 | while Self::is_rx_fifo_empty() {} | ||
| 326 | |||
| 327 | *byte = T::regs().mrdr().read().data().bits(); | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | if send_stop == SendStop::Yes { | ||
| 332 | Self::stop()?; | ||
| 333 | } | ||
| 334 | |||
| 335 | Ok(()) | ||
| 336 | } | ||
| 337 | |||
| 338 | fn blocking_write_internal(&mut self, address: u8, write: &[u8], send_stop: SendStop) -> Result<()> { | ||
| 339 | self.start(address, false)?; | ||
| 340 | |||
| 341 | // Usually, embassy HALs error out with an empty write, | ||
| 342 | // however empty writes are useful for writing I2C scanning | ||
| 343 | // logic through write probing. That is, we send a start with | ||
| 344 | // R/w bit cleared, but instead of writing any data, just send | ||
| 345 | // the stop onto the bus. This has the effect of checking if | ||
| 346 | // the resulting address got an ACK but causing no | ||
| 347 | // side-effects to the device on the other end. | ||
| 348 | // | ||
| 349 | // Because of this, we are not going to error out in case of | ||
| 350 | // empty writes. | ||
| 351 | #[cfg(feature = "defmt")] | ||
| 352 | if write.is_empty() { | ||
| 353 | defmt::trace!("Empty write, write probing?"); | ||
| 354 | } | ||
| 355 | |||
| 356 | for byte in write { | ||
| 357 | // Wait until we have space in the TxFIFO | ||
| 358 | while Self::is_tx_fifo_full() {} | ||
| 359 | |||
| 360 | Self::send_cmd(Cmd::Transmit, *byte); | ||
| 361 | } | ||
| 362 | |||
| 363 | if send_stop == SendStop::Yes { | ||
| 364 | Self::stop()?; | ||
| 365 | } | ||
| 366 | |||
| 367 | Ok(()) | ||
| 368 | } | ||
| 369 | |||
| 370 | // Public API: Blocking | ||
| 371 | |||
| 372 | /// Read from address into buffer blocking caller until done. | ||
| 373 | pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<()> { | ||
| 374 | self.blocking_read_internal(address, read, SendStop::Yes) | ||
| 375 | } | ||
| 376 | |||
| 377 | /// Write to address from buffer blocking caller until done. | ||
| 378 | pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<()> { | ||
| 379 | self.blocking_write_internal(address, write, SendStop::Yes) | ||
| 380 | } | ||
| 381 | |||
| 382 | /// Write to address from bytes and read from address into buffer blocking caller until done. | ||
| 383 | pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<()> { | ||
| 384 | self.blocking_write_internal(address, write, SendStop::No)?; | ||
| 385 | self.blocking_read_internal(address, read, SendStop::Yes) | ||
| 386 | } | ||
| 387 | } | ||
| 388 | |||
| 389 | impl<'d, T: Instance> I2c<'d, T, Async> { | ||
| 390 | /// Create a new async instance of the I2C Controller bus driver. | ||
| 391 | pub fn new_async( | ||
| 392 | peri: Peri<'d, T>, | ||
| 393 | scl: Peri<'d, impl SclPin<T>>, | ||
| 394 | sda: Peri<'d, impl SdaPin<T>>, | ||
| 395 | _irq: impl crate::interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 396 | config: Config, | ||
| 397 | ) -> Result<Self> { | ||
| 398 | T::Interrupt::unpend(); | ||
| 399 | |||
| 400 | // Safety: `_irq` ensures an Interrupt Handler exists. | ||
| 401 | unsafe { T::Interrupt::enable() }; | ||
| 402 | |||
| 403 | Self::new_inner(peri, scl, sda, config) | ||
| 404 | } | ||
| 405 | |||
| 406 | fn remediation() { | ||
| 407 | #[cfg(feature = "defmt")] | ||
| 408 | defmt::trace!("Future dropped, issuing stop",); | ||
| 409 | |||
| 410 | // if the FIFO is not empty, drop its contents. | ||
| 411 | if !Self::is_tx_fifo_empty() { | ||
| 412 | Self::reset_fifos(); | ||
| 413 | } | ||
| 414 | |||
| 415 | // send a stop command | ||
| 416 | let _ = Self::stop(); | ||
| 417 | } | ||
| 418 | |||
| 419 | fn enable_rx_ints(&mut self) { | ||
| 420 | T::regs().mier().write(|w| { | ||
| 421 | w.rdie() | ||
| 422 | .enabled() | ||
| 423 | .ndie() | ||
| 424 | .enabled() | ||
| 425 | .alie() | ||
| 426 | .enabled() | ||
| 427 | .feie() | ||
| 428 | .enabled() | ||
| 429 | .pltie() | ||
| 430 | .enabled() | ||
| 431 | }); | ||
| 432 | } | ||
| 433 | |||
| 434 | fn enable_tx_ints(&mut self) { | ||
| 435 | T::regs().mier().write(|w| { | ||
| 436 | w.tdie() | ||
| 437 | .enabled() | ||
| 438 | .ndie() | ||
| 439 | .enabled() | ||
| 440 | .alie() | ||
| 441 | .enabled() | ||
| 442 | .feie() | ||
| 443 | .enabled() | ||
| 444 | .pltie() | ||
| 445 | .enabled() | ||
| 446 | }); | ||
| 447 | } | ||
| 448 | |||
| 449 | async fn async_start(&mut self, address: u8, read: bool) -> Result<()> { | ||
| 450 | if address >= 0x80 { | ||
| 451 | return Err(Error::AddressOutOfRange(address)); | ||
| 452 | } | ||
| 453 | |||
| 454 | // send the start command | ||
| 455 | let addr_rw = address << 1 | if read { 1 } else { 0 }; | ||
| 456 | Self::send_cmd(if self.is_hs { Cmd::StartHs } else { Cmd::Start }, addr_rw); | ||
| 457 | |||
| 458 | T::wait_cell() | ||
| 459 | .wait_for(|| { | ||
| 460 | // enable interrupts | ||
| 461 | self.enable_tx_ints(); | ||
| 462 | // if the command FIFO is empty, we're done sending start | ||
| 463 | Self::is_tx_fifo_empty() | ||
| 464 | }) | ||
| 465 | .await | ||
| 466 | .map_err(|_| Error::Other)?; | ||
| 467 | |||
| 468 | Self::status() | ||
| 469 | } | ||
| 470 | |||
| 471 | async fn async_stop(&mut self) -> Result<()> { | ||
| 472 | // send the stop command | ||
| 473 | Self::send_cmd(Cmd::Stop, 0); | ||
| 474 | |||
| 475 | T::wait_cell() | ||
| 476 | .wait_for(|| { | ||
| 477 | // enable interrupts | ||
| 478 | self.enable_tx_ints(); | ||
| 479 | // if the command FIFO is empty, we're done sending stop | ||
| 480 | Self::is_tx_fifo_empty() | ||
| 481 | }) | ||
| 482 | .await | ||
| 483 | .map_err(|_| Error::Other)?; | ||
| 484 | |||
| 485 | Self::status() | ||
| 486 | } | ||
| 487 | |||
| 488 | async fn async_read_internal(&mut self, address: u8, read: &mut [u8], send_stop: SendStop) -> Result<()> { | ||
| 489 | if read.is_empty() { | ||
| 490 | return Err(Error::InvalidReadBufferLength); | ||
| 491 | } | ||
| 492 | |||
| 493 | // perform corrective action if the future is dropped | ||
| 494 | let on_drop = OnDrop::new(|| Self::remediation()); | ||
| 495 | |||
| 496 | for chunk in read.chunks_mut(256) { | ||
| 497 | self.async_start(address, true).await?; | ||
| 498 | |||
| 499 | // send receive command | ||
| 500 | Self::send_cmd(Cmd::Receive, (chunk.len() - 1) as u8); | ||
| 501 | |||
| 502 | T::wait_cell() | ||
| 503 | .wait_for(|| { | ||
| 504 | // enable interrupts | ||
| 505 | self.enable_tx_ints(); | ||
| 506 | // if the command FIFO is empty, we're done sending start | ||
| 507 | Self::is_tx_fifo_empty() | ||
| 508 | }) | ||
| 509 | .await | ||
| 510 | .map_err(|_| Error::Other)?; | ||
| 511 | |||
| 512 | for byte in chunk.iter_mut() { | ||
| 513 | T::wait_cell() | ||
| 514 | .wait_for(|| { | ||
| 515 | // enable interrupts | ||
| 516 | self.enable_rx_ints(); | ||
| 517 | // if the rx FIFO is not empty, we need to read a byte | ||
| 518 | !Self::is_rx_fifo_empty() | ||
| 519 | }) | ||
| 520 | .await | ||
| 521 | .map_err(|_| Error::ReadFail)?; | ||
| 522 | |||
| 523 | *byte = T::regs().mrdr().read().data().bits(); | ||
| 524 | } | ||
| 525 | } | ||
| 526 | |||
| 527 | if send_stop == SendStop::Yes { | ||
| 528 | self.async_stop().await?; | ||
| 529 | } | ||
| 530 | |||
| 531 | // defuse it if the future is not dropped | ||
| 532 | on_drop.defuse(); | ||
| 533 | |||
| 534 | Ok(()) | ||
| 535 | } | ||
| 536 | |||
| 537 | async fn async_write_internal(&mut self, address: u8, write: &[u8], send_stop: SendStop) -> Result<()> { | ||
| 538 | self.async_start(address, false).await?; | ||
| 539 | |||
| 540 | // perform corrective action if the future is dropped | ||
| 541 | let on_drop = OnDrop::new(|| Self::remediation()); | ||
| 542 | |||
| 543 | // Usually, embassy HALs error out with an empty write, | ||
| 544 | // however empty writes are useful for writing I2C scanning | ||
| 545 | // logic through write probing. That is, we send a start with | ||
| 546 | // R/w bit cleared, but instead of writing any data, just send | ||
| 547 | // the stop onto the bus. This has the effect of checking if | ||
| 548 | // the resulting address got an ACK but causing no | ||
| 549 | // side-effects to the device on the other end. | ||
| 550 | // | ||
| 551 | // Because of this, we are not going to error out in case of | ||
| 552 | // empty writes. | ||
| 553 | #[cfg(feature = "defmt")] | ||
| 554 | if write.is_empty() { | ||
| 555 | defmt::trace!("Empty write, write probing?"); | ||
| 556 | } | ||
| 557 | |||
| 558 | for byte in write { | ||
| 559 | T::wait_cell() | ||
| 560 | .wait_for(|| { | ||
| 561 | // enable interrupts | ||
| 562 | self.enable_tx_ints(); | ||
| 563 | // initiate transmit | ||
| 564 | Self::send_cmd(Cmd::Transmit, *byte); | ||
| 565 | // if the tx FIFO is empty, we're done transmiting | ||
| 566 | Self::is_tx_fifo_empty() | ||
| 567 | }) | ||
| 568 | .await | ||
| 569 | .map_err(|_| Error::WriteFail)?; | ||
| 570 | } | ||
| 571 | |||
| 572 | if send_stop == SendStop::Yes { | ||
| 573 | self.async_stop().await?; | ||
| 574 | } | ||
| 575 | |||
| 576 | // defuse it if the future is not dropped | ||
| 577 | on_drop.defuse(); | ||
| 578 | |||
| 579 | Ok(()) | ||
| 580 | } | ||
| 581 | |||
| 582 | // Public API: Async | ||
| 583 | |||
| 584 | /// Read from address into buffer asynchronously. | ||
| 585 | pub fn async_read<'a>( | ||
| 586 | &mut self, | ||
| 587 | address: u8, | ||
| 588 | read: &'a mut [u8], | ||
| 589 | ) -> impl Future<Output = Result<()>> + use<'_, 'a, 'd, T> { | ||
| 590 | self.async_read_internal(address, read, SendStop::Yes) | ||
| 591 | } | ||
| 592 | |||
| 593 | /// Write to address from buffer asynchronously. | ||
| 594 | pub fn async_write<'a>( | ||
| 595 | &mut self, | ||
| 596 | address: u8, | ||
| 597 | write: &'a [u8], | ||
| 598 | ) -> impl Future<Output = Result<()>> + use<'_, 'a, 'd, T> { | ||
| 599 | self.async_write_internal(address, write, SendStop::Yes) | ||
| 600 | } | ||
| 601 | |||
| 602 | /// Write to address from bytes and read from address into buffer asynchronously. | ||
| 603 | pub async fn async_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<()> { | ||
| 604 | self.async_write_internal(address, write, SendStop::No).await?; | ||
| 605 | self.async_read_internal(address, read, SendStop::Yes).await | ||
| 606 | } | ||
| 607 | } | ||
| 608 | |||
| 609 | impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> { | ||
| 610 | type Error = Error; | ||
| 611 | |||
| 612 | fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<()> { | ||
| 613 | self.blocking_read(address, buffer) | ||
| 614 | } | ||
| 615 | } | ||
| 616 | |||
| 617 | impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> { | ||
| 618 | type Error = Error; | ||
| 619 | |||
| 620 | fn write(&mut self, address: u8, bytes: &[u8]) -> Result<()> { | ||
| 621 | self.blocking_write(address, bytes) | ||
| 622 | } | ||
| 623 | } | ||
| 624 | |||
| 625 | impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> { | ||
| 626 | type Error = Error; | ||
| 627 | |||
| 628 | fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<()> { | ||
| 629 | self.blocking_write_read(address, bytes, buffer) | ||
| 630 | } | ||
| 631 | } | ||
| 632 | |||
| 633 | impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Transactional for I2c<'d, T, M> { | ||
| 634 | type Error = Error; | ||
| 635 | |||
| 636 | fn exec(&mut self, address: u8, operations: &mut [embedded_hal_02::blocking::i2c::Operation<'_>]) -> Result<()> { | ||
| 637 | if let Some((last, rest)) = operations.split_last_mut() { | ||
| 638 | for op in rest { | ||
| 639 | match op { | ||
| 640 | embedded_hal_02::blocking::i2c::Operation::Read(buf) => { | ||
| 641 | self.blocking_read_internal(address, buf, SendStop::No)? | ||
| 642 | } | ||
| 643 | embedded_hal_02::blocking::i2c::Operation::Write(buf) => { | ||
| 644 | self.blocking_write_internal(address, buf, SendStop::No)? | ||
| 645 | } | ||
| 646 | } | ||
| 647 | } | ||
| 648 | |||
| 649 | match last { | ||
| 650 | embedded_hal_02::blocking::i2c::Operation::Read(buf) => { | ||
| 651 | self.blocking_read_internal(address, buf, SendStop::Yes) | ||
| 652 | } | ||
| 653 | embedded_hal_02::blocking::i2c::Operation::Write(buf) => { | ||
| 654 | self.blocking_write_internal(address, buf, SendStop::Yes) | ||
| 655 | } | ||
| 656 | } | ||
| 657 | } else { | ||
| 658 | Ok(()) | ||
| 659 | } | ||
| 660 | } | ||
| 661 | } | ||
| 662 | |||
| 663 | impl embedded_hal_1::i2c::Error for Error { | ||
| 664 | fn kind(&self) -> embedded_hal_1::i2c::ErrorKind { | ||
| 665 | match *self { | ||
| 666 | Self::ArbitrationLoss => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss, | ||
| 667 | Self::AddressNack => { | ||
| 668 | embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address) | ||
| 669 | } | ||
| 670 | _ => embedded_hal_1::i2c::ErrorKind::Other, | ||
| 671 | } | ||
| 672 | } | ||
| 673 | } | ||
| 674 | |||
| 675 | impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> { | ||
| 676 | type Error = Error; | ||
| 677 | } | ||
| 678 | |||
| 679 | impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> { | ||
| 680 | fn transaction(&mut self, address: u8, operations: &mut [embedded_hal_1::i2c::Operation<'_>]) -> Result<()> { | ||
| 681 | if let Some((last, rest)) = operations.split_last_mut() { | ||
| 682 | for op in rest { | ||
| 683 | match op { | ||
| 684 | embedded_hal_1::i2c::Operation::Read(buf) => { | ||
| 685 | self.blocking_read_internal(address, buf, SendStop::No)? | ||
| 686 | } | ||
| 687 | embedded_hal_1::i2c::Operation::Write(buf) => { | ||
| 688 | self.blocking_write_internal(address, buf, SendStop::No)? | ||
| 689 | } | ||
| 690 | } | ||
| 691 | } | ||
| 692 | |||
| 693 | match last { | ||
| 694 | embedded_hal_1::i2c::Operation::Read(buf) => self.blocking_read_internal(address, buf, SendStop::Yes), | ||
| 695 | embedded_hal_1::i2c::Operation::Write(buf) => self.blocking_write_internal(address, buf, SendStop::Yes), | ||
| 696 | } | ||
| 697 | } else { | ||
| 698 | Ok(()) | ||
| 699 | } | ||
| 700 | } | ||
| 701 | } | ||
| 702 | |||
| 703 | impl<'d, T: Instance> embedded_hal_async::i2c::I2c for I2c<'d, T, Async> { | ||
| 704 | async fn transaction( | ||
| 705 | &mut self, | ||
| 706 | address: u8, | ||
| 707 | operations: &mut [embedded_hal_async::i2c::Operation<'_>], | ||
| 708 | ) -> Result<()> { | ||
| 709 | if let Some((last, rest)) = operations.split_last_mut() { | ||
| 710 | for op in rest { | ||
| 711 | match op { | ||
| 712 | embedded_hal_async::i2c::Operation::Read(buf) => { | ||
| 713 | self.async_read_internal(address, buf, SendStop::No).await? | ||
| 714 | } | ||
| 715 | embedded_hal_async::i2c::Operation::Write(buf) => { | ||
| 716 | self.async_write_internal(address, buf, SendStop::No).await? | ||
| 717 | } | ||
| 718 | } | ||
| 719 | } | ||
| 720 | |||
| 721 | match last { | ||
| 722 | embedded_hal_async::i2c::Operation::Read(buf) => { | ||
| 723 | self.async_read_internal(address, buf, SendStop::Yes).await | ||
| 724 | } | ||
| 725 | embedded_hal_async::i2c::Operation::Write(buf) => { | ||
| 726 | self.async_write_internal(address, buf, SendStop::Yes).await | ||
| 727 | } | ||
| 728 | } | ||
| 729 | } else { | ||
| 730 | Ok(()) | ||
| 731 | } | ||
| 732 | } | ||
| 733 | } | ||
| 734 | |||
| 735 | impl<'d, T: Instance, M: Mode> embassy_embedded_hal::SetConfig for I2c<'d, T, M> { | ||
| 736 | type Config = Config; | ||
| 737 | type ConfigError = Error; | ||
| 738 | |||
| 739 | fn set_config(&mut self, config: &Self::Config) -> Result<()> { | ||
| 740 | Self::set_config(config) | ||
| 741 | } | ||
| 742 | } | ||
diff --git a/embassy-mcxa/src/i2c/mod.rs b/embassy-mcxa/src/i2c/mod.rs new file mode 100644 index 000000000..9a014224a --- /dev/null +++ b/embassy-mcxa/src/i2c/mod.rs | |||
| @@ -0,0 +1,194 @@ | |||
| 1 | //! I2C Support | ||
| 2 | |||
| 3 | use core::marker::PhantomData; | ||
| 4 | |||
| 5 | use embassy_hal_internal::PeripheralType; | ||
| 6 | use maitake_sync::WaitCell; | ||
| 7 | use paste::paste; | ||
| 8 | |||
| 9 | use crate::clocks::periph_helpers::Lpi2cConfig; | ||
| 10 | use crate::clocks::{ClockError, Gate}; | ||
| 11 | use crate::gpio::{GpioPin, SealedPin}; | ||
| 12 | use crate::{interrupt, pac}; | ||
| 13 | |||
| 14 | /// Shorthand for `Result<T>`. | ||
| 15 | pub type Result<T> = core::result::Result<T, Error>; | ||
| 16 | |||
| 17 | pub mod controller; | ||
| 18 | |||
| 19 | /// Error information type | ||
| 20 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
| 21 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 22 | pub enum Error { | ||
| 23 | /// Clock configuration error. | ||
| 24 | ClockSetup(ClockError), | ||
| 25 | /// FIFO Error | ||
| 26 | FifoError, | ||
| 27 | /// Reading for I2C failed. | ||
| 28 | ReadFail, | ||
| 29 | /// Writing to I2C failed. | ||
| 30 | WriteFail, | ||
| 31 | /// I2C address NAK condition. | ||
| 32 | AddressNack, | ||
| 33 | /// Bus level arbitration loss. | ||
| 34 | ArbitrationLoss, | ||
| 35 | /// Address out of range. | ||
| 36 | AddressOutOfRange(u8), | ||
| 37 | /// Invalid write buffer length. | ||
| 38 | InvalidWriteBufferLength, | ||
| 39 | /// Invalid read buffer length. | ||
| 40 | InvalidReadBufferLength, | ||
| 41 | /// Other internal errors or unexpected state. | ||
| 42 | Other, | ||
| 43 | } | ||
| 44 | |||
| 45 | /// I2C interrupt handler. | ||
| 46 | pub struct InterruptHandler<T: Instance> { | ||
| 47 | _phantom: PhantomData<T>, | ||
| 48 | } | ||
| 49 | |||
| 50 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { | ||
| 51 | unsafe fn on_interrupt() { | ||
| 52 | if T::regs().mier().read().bits() != 0 { | ||
| 53 | T::regs().mier().write(|w| { | ||
| 54 | w.tdie() | ||
| 55 | .disabled() | ||
| 56 | .rdie() | ||
| 57 | .disabled() | ||
| 58 | .epie() | ||
| 59 | .disabled() | ||
| 60 | .sdie() | ||
| 61 | .disabled() | ||
| 62 | .ndie() | ||
| 63 | .disabled() | ||
| 64 | .alie() | ||
| 65 | .disabled() | ||
| 66 | .feie() | ||
| 67 | .disabled() | ||
| 68 | .pltie() | ||
| 69 | .disabled() | ||
| 70 | .dmie() | ||
| 71 | .disabled() | ||
| 72 | .stie() | ||
| 73 | .disabled() | ||
| 74 | }); | ||
| 75 | |||
| 76 | T::wait_cell().wake(); | ||
| 77 | } | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | mod sealed { | ||
| 82 | /// Seal a trait | ||
| 83 | pub trait Sealed {} | ||
| 84 | } | ||
| 85 | |||
| 86 | impl<T: GpioPin> sealed::Sealed for T {} | ||
| 87 | |||
| 88 | trait SealedInstance { | ||
| 89 | fn regs() -> &'static pac::lpi2c0::RegisterBlock; | ||
| 90 | fn wait_cell() -> &'static WaitCell; | ||
| 91 | } | ||
| 92 | |||
| 93 | /// I2C Instance | ||
| 94 | #[allow(private_bounds)] | ||
| 95 | pub trait Instance: SealedInstance + PeripheralType + 'static + Send + Gate<MrccPeriphConfig = Lpi2cConfig> { | ||
| 96 | /// Interrupt for this I2C instance. | ||
| 97 | type Interrupt: interrupt::typelevel::Interrupt; | ||
| 98 | /// Clock instance | ||
| 99 | const CLOCK_INSTANCE: crate::clocks::periph_helpers::Lpi2cInstance; | ||
| 100 | } | ||
| 101 | |||
| 102 | macro_rules! impl_instance { | ||
| 103 | ($($n:expr),*) => { | ||
| 104 | $( | ||
| 105 | paste!{ | ||
| 106 | impl SealedInstance for crate::peripherals::[<LPI2C $n>] { | ||
| 107 | fn regs() -> &'static pac::lpi2c0::RegisterBlock { | ||
| 108 | unsafe { &*pac::[<Lpi2c $n>]::ptr() } | ||
| 109 | } | ||
| 110 | |||
| 111 | fn wait_cell() -> &'static WaitCell { | ||
| 112 | static WAIT_CELL: WaitCell = WaitCell::new(); | ||
| 113 | &WAIT_CELL | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | impl Instance for crate::peripherals::[<LPI2C $n>] { | ||
| 118 | type Interrupt = crate::interrupt::typelevel::[<LPI2C $n>]; | ||
| 119 | const CLOCK_INSTANCE: crate::clocks::periph_helpers::Lpi2cInstance | ||
| 120 | = crate::clocks::periph_helpers::Lpi2cInstance::[<Lpi2c $n>]; | ||
| 121 | } | ||
| 122 | } | ||
| 123 | )* | ||
| 124 | }; | ||
| 125 | } | ||
| 126 | |||
| 127 | impl_instance!(0, 1, 2, 3); | ||
| 128 | |||
| 129 | /// SCL pin trait. | ||
| 130 | pub trait SclPin<Instance>: GpioPin + sealed::Sealed + PeripheralType { | ||
| 131 | fn mux(&self); | ||
| 132 | } | ||
| 133 | |||
| 134 | /// SDA pin trait. | ||
| 135 | pub trait SdaPin<Instance>: GpioPin + sealed::Sealed + PeripheralType { | ||
| 136 | fn mux(&self); | ||
| 137 | } | ||
| 138 | |||
| 139 | /// Driver mode. | ||
| 140 | #[allow(private_bounds)] | ||
| 141 | pub trait Mode: sealed::Sealed {} | ||
| 142 | |||
| 143 | /// Blocking mode. | ||
| 144 | pub struct Blocking; | ||
| 145 | impl sealed::Sealed for Blocking {} | ||
| 146 | impl Mode for Blocking {} | ||
| 147 | |||
| 148 | /// Async mode. | ||
| 149 | pub struct Async; | ||
| 150 | impl sealed::Sealed for Async {} | ||
| 151 | impl Mode for Async {} | ||
| 152 | |||
| 153 | macro_rules! impl_pin { | ||
| 154 | ($pin:ident, $peri:ident, $fn:ident, $trait:ident) => { | ||
| 155 | impl $trait<crate::peripherals::$peri> for crate::peripherals::$pin { | ||
| 156 | fn mux(&self) { | ||
| 157 | self.set_pull(crate::gpio::Pull::Disabled); | ||
| 158 | self.set_slew_rate(crate::gpio::SlewRate::Fast.into()); | ||
| 159 | self.set_drive_strength(crate::gpio::DriveStrength::Double.into()); | ||
| 160 | self.set_function(crate::pac::port0::pcr0::Mux::$fn); | ||
| 161 | self.set_enable_input_buffer(); | ||
| 162 | } | ||
| 163 | } | ||
| 164 | }; | ||
| 165 | } | ||
| 166 | |||
| 167 | impl_pin!(P0_16, LPI2C0, Mux2, SdaPin); | ||
| 168 | impl_pin!(P0_17, LPI2C0, Mux2, SclPin); | ||
| 169 | impl_pin!(P0_18, LPI2C0, Mux2, SclPin); | ||
| 170 | impl_pin!(P0_19, LPI2C0, Mux2, SdaPin); | ||
| 171 | impl_pin!(P1_0, LPI2C1, Mux3, SdaPin); | ||
| 172 | impl_pin!(P1_1, LPI2C1, Mux3, SclPin); | ||
| 173 | impl_pin!(P1_2, LPI2C1, Mux3, SdaPin); | ||
| 174 | impl_pin!(P1_3, LPI2C1, Mux3, SclPin); | ||
| 175 | impl_pin!(P1_8, LPI2C2, Mux3, SdaPin); | ||
| 176 | impl_pin!(P1_9, LPI2C2, Mux3, SclPin); | ||
| 177 | impl_pin!(P1_10, LPI2C2, Mux3, SdaPin); | ||
| 178 | impl_pin!(P1_11, LPI2C2, Mux3, SclPin); | ||
| 179 | impl_pin!(P1_12, LPI2C1, Mux2, SdaPin); | ||
| 180 | impl_pin!(P1_13, LPI2C1, Mux2, SclPin); | ||
| 181 | impl_pin!(P1_14, LPI2C1, Mux2, SclPin); | ||
| 182 | impl_pin!(P1_15, LPI2C1, Mux2, SdaPin); | ||
| 183 | impl_pin!(P1_30, LPI2C0, Mux3, SdaPin); | ||
| 184 | impl_pin!(P1_31, LPI2C0, Mux3, SclPin); | ||
| 185 | impl_pin!(P3_27, LPI2C3, Mux2, SclPin); | ||
| 186 | impl_pin!(P3_28, LPI2C3, Mux2, SdaPin); | ||
| 187 | // impl_pin!(P3_29, LPI2C3, Mux2, HreqPin); What is this HREQ pin? | ||
| 188 | impl_pin!(P3_30, LPI2C3, Mux2, SclPin); | ||
| 189 | impl_pin!(P3_31, LPI2C3, Mux2, SdaPin); | ||
| 190 | impl_pin!(P4_2, LPI2C2, Mux2, SdaPin); | ||
| 191 | impl_pin!(P4_3, LPI2C0, Mux2, SclPin); | ||
| 192 | impl_pin!(P4_4, LPI2C2, Mux2, SdaPin); | ||
| 193 | impl_pin!(P4_5, LPI2C0, Mux2, SclPin); | ||
| 194 | // impl_pin!(P4_6, LPI2C0, Mux2, HreqPin); What is this HREQ pin? | ||
diff --git a/embassy-mcxa/src/interrupt.rs b/embassy-mcxa/src/interrupt.rs new file mode 100644 index 000000000..c1f7e55a0 --- /dev/null +++ b/embassy-mcxa/src/interrupt.rs | |||
| @@ -0,0 +1,563 @@ | |||
| 1 | //! Minimal interrupt helpers mirroring embassy-imxrt style for OS_EVENT and LPUART2. | ||
| 2 | //! Type-level interrupt traits and bindings are provided by the | ||
| 3 | //! `embassy_hal_internal::interrupt_mod!` macro via the generated module below. | ||
| 4 | |||
| 5 | // TODO(AJM): As of 2025-11-13, we need to do a pass to ensure safety docs | ||
| 6 | // are complete prior to release. | ||
| 7 | #![allow(clippy::missing_safety_doc)] | ||
| 8 | |||
| 9 | mod generated { | ||
| 10 | #[rustfmt::skip] | ||
| 11 | embassy_hal_internal::interrupt_mod!( | ||
| 12 | ADC1, | ||
| 13 | GPIO0, | ||
| 14 | GPIO1, | ||
| 15 | GPIO2, | ||
| 16 | GPIO3, | ||
| 17 | GPIO4, | ||
| 18 | LPI2C0, | ||
| 19 | LPI2C1, | ||
| 20 | LPI2C2, | ||
| 21 | LPI2C3, | ||
| 22 | LPUART0, | ||
| 23 | LPUART1, | ||
| 24 | LPUART2, | ||
| 25 | LPUART3, | ||
| 26 | LPUART4, | ||
| 27 | LPUART5, | ||
| 28 | OS_EVENT, | ||
| 29 | RTC, | ||
| 30 | ); | ||
| 31 | } | ||
| 32 | |||
| 33 | use core::sync::atomic::{AtomicU16, AtomicU32, Ordering}; | ||
| 34 | |||
| 35 | pub use generated::interrupt::{Priority, typelevel}; | ||
| 36 | |||
| 37 | use crate::pac::Interrupt; | ||
| 38 | |||
| 39 | /// Trait for configuring and controlling interrupts. | ||
| 40 | /// | ||
| 41 | /// This trait provides a consistent interface for interrupt management across | ||
| 42 | /// different interrupt sources, similar to embassy-imxrt's InterruptExt. | ||
| 43 | pub trait InterruptExt { | ||
| 44 | /// Clear any pending interrupt in NVIC. | ||
| 45 | fn unpend(&self); | ||
| 46 | |||
| 47 | /// Set NVIC priority for this interrupt. | ||
| 48 | fn set_priority(&self, priority: Priority); | ||
| 49 | |||
| 50 | /// Enable this interrupt in NVIC. | ||
| 51 | /// | ||
| 52 | /// # Safety | ||
| 53 | /// This function is unsafe because it can enable interrupts that may not be | ||
| 54 | /// properly configured, potentially leading to undefined behavior. | ||
| 55 | unsafe fn enable(&self); | ||
| 56 | |||
| 57 | /// Disable this interrupt in NVIC. | ||
| 58 | /// | ||
| 59 | /// # Safety | ||
| 60 | /// This function is unsafe because disabling interrupts may leave the system | ||
| 61 | /// in an inconsistent state if the interrupt was expected to fire. | ||
| 62 | unsafe fn disable(&self); | ||
| 63 | |||
| 64 | /// Check if the interrupt is pending in NVIC. | ||
| 65 | fn is_pending(&self) -> bool; | ||
| 66 | } | ||
| 67 | |||
| 68 | #[derive(Clone, Copy, Debug, Default)] | ||
| 69 | pub struct DefaultHandlerSnapshot { | ||
| 70 | pub vector: u16, | ||
| 71 | pub count: u32, | ||
| 72 | pub cfsr: u32, | ||
| 73 | pub hfsr: u32, | ||
| 74 | pub stacked_pc: u32, | ||
| 75 | pub stacked_lr: u32, | ||
| 76 | pub stacked_sp: u32, | ||
| 77 | } | ||
| 78 | |||
| 79 | static LAST_DEFAULT_VECTOR: AtomicU16 = AtomicU16::new(0); | ||
| 80 | static LAST_DEFAULT_COUNT: AtomicU32 = AtomicU32::new(0); | ||
| 81 | static LAST_DEFAULT_CFSR: AtomicU32 = AtomicU32::new(0); | ||
| 82 | static LAST_DEFAULT_HFSR: AtomicU32 = AtomicU32::new(0); | ||
| 83 | static LAST_DEFAULT_PC: AtomicU32 = AtomicU32::new(0); | ||
| 84 | static LAST_DEFAULT_LR: AtomicU32 = AtomicU32::new(0); | ||
| 85 | static LAST_DEFAULT_SP: AtomicU32 = AtomicU32::new(0); | ||
| 86 | |||
| 87 | #[inline] | ||
| 88 | pub fn default_handler_snapshot() -> DefaultHandlerSnapshot { | ||
| 89 | DefaultHandlerSnapshot { | ||
| 90 | vector: LAST_DEFAULT_VECTOR.load(Ordering::Relaxed), | ||
| 91 | count: LAST_DEFAULT_COUNT.load(Ordering::Relaxed), | ||
| 92 | cfsr: LAST_DEFAULT_CFSR.load(Ordering::Relaxed), | ||
| 93 | hfsr: LAST_DEFAULT_HFSR.load(Ordering::Relaxed), | ||
| 94 | stacked_pc: LAST_DEFAULT_PC.load(Ordering::Relaxed), | ||
| 95 | stacked_lr: LAST_DEFAULT_LR.load(Ordering::Relaxed), | ||
| 96 | stacked_sp: LAST_DEFAULT_SP.load(Ordering::Relaxed), | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | #[inline] | ||
| 101 | pub fn clear_default_handler_snapshot() { | ||
| 102 | LAST_DEFAULT_VECTOR.store(0, Ordering::Relaxed); | ||
| 103 | LAST_DEFAULT_COUNT.store(0, Ordering::Relaxed); | ||
| 104 | LAST_DEFAULT_CFSR.store(0, Ordering::Relaxed); | ||
| 105 | LAST_DEFAULT_HFSR.store(0, Ordering::Relaxed); | ||
| 106 | LAST_DEFAULT_PC.store(0, Ordering::Relaxed); | ||
| 107 | LAST_DEFAULT_LR.store(0, Ordering::Relaxed); | ||
| 108 | LAST_DEFAULT_SP.store(0, Ordering::Relaxed); | ||
| 109 | } | ||
| 110 | |||
| 111 | /// OS_EVENT interrupt helper with methods similar to embassy-imxrt's InterruptExt. | ||
| 112 | pub struct OsEvent; | ||
| 113 | pub const OS_EVENT: OsEvent = OsEvent; | ||
| 114 | |||
| 115 | impl InterruptExt for OsEvent { | ||
| 116 | /// Clear any pending OS_EVENT in NVIC. | ||
| 117 | #[inline] | ||
| 118 | fn unpend(&self) { | ||
| 119 | cortex_m::peripheral::NVIC::unpend(Interrupt::OS_EVENT); | ||
| 120 | } | ||
| 121 | |||
| 122 | /// Set NVIC priority for OS_EVENT. | ||
| 123 | #[inline] | ||
| 124 | fn set_priority(&self, priority: Priority) { | ||
| 125 | unsafe { | ||
| 126 | let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; | ||
| 127 | nvic.set_priority(Interrupt::OS_EVENT, u8::from(priority)); | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | /// Enable OS_EVENT in NVIC. | ||
| 132 | #[inline] | ||
| 133 | unsafe fn enable(&self) { | ||
| 134 | cortex_m::peripheral::NVIC::unmask(Interrupt::OS_EVENT); | ||
| 135 | } | ||
| 136 | |||
| 137 | /// Disable OS_EVENT in NVIC. | ||
| 138 | #[inline] | ||
| 139 | unsafe fn disable(&self) { | ||
| 140 | cortex_m::peripheral::NVIC::mask(Interrupt::OS_EVENT); | ||
| 141 | } | ||
| 142 | |||
| 143 | /// Check if OS_EVENT is pending in NVIC. | ||
| 144 | #[inline] | ||
| 145 | fn is_pending(&self) -> bool { | ||
| 146 | cortex_m::peripheral::NVIC::is_pending(Interrupt::OS_EVENT) | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | impl OsEvent { | ||
| 151 | /// Configure OS_EVENT interrupt for timer operation. | ||
| 152 | /// This sets up the NVIC priority, enables the interrupt, and ensures global interrupts are enabled. | ||
| 153 | /// Also performs a software event to wake any pending WFE. | ||
| 154 | pub fn configure_for_timer(&self, priority: Priority) { | ||
| 155 | // Configure NVIC | ||
| 156 | self.unpend(); | ||
| 157 | self.set_priority(priority); | ||
| 158 | unsafe { | ||
| 159 | self.enable(); | ||
| 160 | } | ||
| 161 | |||
| 162 | // Ensure global interrupts are enabled in no-reset scenarios (e.g., cargo run) | ||
| 163 | // Debuggers typically perform a reset which leaves PRIMASK=0; cargo run may not. | ||
| 164 | unsafe { | ||
| 165 | cortex_m::interrupt::enable(); | ||
| 166 | } | ||
| 167 | |||
| 168 | // Wake any executor WFE that might be sleeping when we armed the first deadline | ||
| 169 | cortex_m::asm::sev(); | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | /// LPUART2 interrupt helper with methods similar to embassy-imxrt's InterruptExt. | ||
| 174 | pub struct Lpuart2; | ||
| 175 | pub const LPUART2: Lpuart2 = Lpuart2; | ||
| 176 | |||
| 177 | impl InterruptExt for Lpuart2 { | ||
| 178 | /// Clear any pending LPUART2 in NVIC. | ||
| 179 | #[inline] | ||
| 180 | fn unpend(&self) { | ||
| 181 | cortex_m::peripheral::NVIC::unpend(Interrupt::LPUART2); | ||
| 182 | } | ||
| 183 | |||
| 184 | /// Set NVIC priority for LPUART2. | ||
| 185 | #[inline] | ||
| 186 | fn set_priority(&self, priority: Priority) { | ||
| 187 | unsafe { | ||
| 188 | let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; | ||
| 189 | nvic.set_priority(Interrupt::LPUART2, u8::from(priority)); | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | /// Enable LPUART2 in NVIC. | ||
| 194 | #[inline] | ||
| 195 | unsafe fn enable(&self) { | ||
| 196 | cortex_m::peripheral::NVIC::unmask(Interrupt::LPUART2); | ||
| 197 | } | ||
| 198 | |||
| 199 | /// Disable LPUART2 in NVIC. | ||
| 200 | #[inline] | ||
| 201 | unsafe fn disable(&self) { | ||
| 202 | cortex_m::peripheral::NVIC::mask(Interrupt::LPUART2); | ||
| 203 | } | ||
| 204 | |||
| 205 | /// Check if LPUART2 is pending in NVIC. | ||
| 206 | #[inline] | ||
| 207 | fn is_pending(&self) -> bool { | ||
| 208 | cortex_m::peripheral::NVIC::is_pending(Interrupt::LPUART2) | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | impl Lpuart2 { | ||
| 213 | /// Configure LPUART2 interrupt for UART operation. | ||
| 214 | /// This sets up the NVIC priority, enables the interrupt, and ensures global interrupts are enabled. | ||
| 215 | pub fn configure_for_uart(&self, priority: Priority) { | ||
| 216 | // Configure NVIC | ||
| 217 | self.unpend(); | ||
| 218 | self.set_priority(priority); | ||
| 219 | unsafe { | ||
| 220 | self.enable(); | ||
| 221 | } | ||
| 222 | |||
| 223 | // Ensure global interrupts are enabled in no-reset scenarios (e.g., cargo run) | ||
| 224 | // Debuggers typically perform a reset which leaves PRIMASK=0; cargo run may not. | ||
| 225 | unsafe { | ||
| 226 | cortex_m::interrupt::enable(); | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | /// Install LPUART2 handler into the RAM vector table. | ||
| 231 | /// Safety: See `install_irq_handler`. | ||
| 232 | pub unsafe fn install_handler(&self, handler: unsafe extern "C" fn()) { | ||
| 233 | install_irq_handler(Interrupt::LPUART2, handler); | ||
| 234 | } | ||
| 235 | } | ||
| 236 | |||
| 237 | pub struct Rtc; | ||
| 238 | pub const RTC: Rtc = Rtc; | ||
| 239 | |||
| 240 | impl InterruptExt for Rtc { | ||
| 241 | /// Clear any pending RTC in NVIC. | ||
| 242 | #[inline] | ||
| 243 | fn unpend(&self) { | ||
| 244 | cortex_m::peripheral::NVIC::unpend(Interrupt::RTC); | ||
| 245 | } | ||
| 246 | |||
| 247 | /// Set NVIC priority for RTC. | ||
| 248 | #[inline] | ||
| 249 | fn set_priority(&self, priority: Priority) { | ||
| 250 | unsafe { | ||
| 251 | let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; | ||
| 252 | nvic.set_priority(Interrupt::RTC, u8::from(priority)); | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | /// Enable RTC in NVIC. | ||
| 257 | #[inline] | ||
| 258 | unsafe fn enable(&self) { | ||
| 259 | cortex_m::peripheral::NVIC::unmask(Interrupt::RTC); | ||
| 260 | } | ||
| 261 | |||
| 262 | /// Disable RTC in NVIC. | ||
| 263 | #[inline] | ||
| 264 | unsafe fn disable(&self) { | ||
| 265 | cortex_m::peripheral::NVIC::mask(Interrupt::RTC); | ||
| 266 | } | ||
| 267 | |||
| 268 | /// Check if RTC is pending in NVIC. | ||
| 269 | #[inline] | ||
| 270 | fn is_pending(&self) -> bool { | ||
| 271 | cortex_m::peripheral::NVIC::is_pending(Interrupt::RTC) | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 275 | pub struct Adc; | ||
| 276 | pub const ADC1: Adc = Adc; | ||
| 277 | |||
| 278 | impl InterruptExt for Adc { | ||
| 279 | /// Clear any pending ADC1 in NVIC. | ||
| 280 | #[inline] | ||
| 281 | fn unpend(&self) { | ||
| 282 | cortex_m::peripheral::NVIC::unpend(Interrupt::ADC1); | ||
| 283 | } | ||
| 284 | |||
| 285 | /// Set NVIC priority for ADC1. | ||
| 286 | #[inline] | ||
| 287 | fn set_priority(&self, priority: Priority) { | ||
| 288 | unsafe { | ||
| 289 | let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; | ||
| 290 | nvic.set_priority(Interrupt::ADC1, u8::from(priority)); | ||
| 291 | } | ||
| 292 | } | ||
| 293 | |||
| 294 | /// Enable ADC1 in NVIC. | ||
| 295 | #[inline] | ||
| 296 | unsafe fn enable(&self) { | ||
| 297 | cortex_m::peripheral::NVIC::unmask(Interrupt::ADC1); | ||
| 298 | } | ||
| 299 | |||
| 300 | /// Disable ADC1 in NVIC. | ||
| 301 | #[inline] | ||
| 302 | unsafe fn disable(&self) { | ||
| 303 | cortex_m::peripheral::NVIC::mask(Interrupt::ADC1); | ||
| 304 | } | ||
| 305 | |||
| 306 | /// Check if ADC1 is pending in NVIC. | ||
| 307 | #[inline] | ||
| 308 | fn is_pending(&self) -> bool { | ||
| 309 | cortex_m::peripheral::NVIC::is_pending(Interrupt::ADC1) | ||
| 310 | } | ||
| 311 | } | ||
| 312 | |||
| 313 | pub struct Gpio0; | ||
| 314 | pub const GPIO0: Gpio0 = Gpio0; | ||
| 315 | |||
| 316 | impl InterruptExt for Gpio0 { | ||
| 317 | /// Clear any pending GPIO0 in NVIC. | ||
| 318 | #[inline] | ||
| 319 | fn unpend(&self) { | ||
| 320 | cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO0); | ||
| 321 | } | ||
| 322 | |||
| 323 | /// Set NVIC priority for GPIO0. | ||
| 324 | #[inline] | ||
| 325 | fn set_priority(&self, priority: Priority) { | ||
| 326 | unsafe { | ||
| 327 | let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; | ||
| 328 | nvic.set_priority(Interrupt::GPIO0, u8::from(priority)); | ||
| 329 | } | ||
| 330 | } | ||
| 331 | |||
| 332 | /// Enable GPIO0 in NVIC. | ||
| 333 | #[inline] | ||
| 334 | unsafe fn enable(&self) { | ||
| 335 | cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO0); | ||
| 336 | } | ||
| 337 | |||
| 338 | /// Disable GPIO0 in NVIC. | ||
| 339 | #[inline] | ||
| 340 | unsafe fn disable(&self) { | ||
| 341 | cortex_m::peripheral::NVIC::mask(Interrupt::GPIO0); | ||
| 342 | } | ||
| 343 | |||
| 344 | /// Check if GPIO0 is pending in NVIC. | ||
| 345 | #[inline] | ||
| 346 | fn is_pending(&self) -> bool { | ||
| 347 | cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO0) | ||
| 348 | } | ||
| 349 | } | ||
| 350 | |||
| 351 | pub struct Gpio1; | ||
| 352 | pub const GPIO1: Gpio1 = Gpio1; | ||
| 353 | |||
| 354 | impl InterruptExt for Gpio1 { | ||
| 355 | /// Clear any pending GPIO1 in NVIC. | ||
| 356 | #[inline] | ||
| 357 | fn unpend(&self) { | ||
| 358 | cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO1); | ||
| 359 | } | ||
| 360 | |||
| 361 | /// Set NVIC priority for GPIO1. | ||
| 362 | #[inline] | ||
| 363 | fn set_priority(&self, priority: Priority) { | ||
| 364 | unsafe { | ||
| 365 | let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; | ||
| 366 | nvic.set_priority(Interrupt::GPIO1, u8::from(priority)); | ||
| 367 | } | ||
| 368 | } | ||
| 369 | |||
| 370 | /// Enable GPIO1 in NVIC. | ||
| 371 | #[inline] | ||
| 372 | unsafe fn enable(&self) { | ||
| 373 | cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO1); | ||
| 374 | } | ||
| 375 | |||
| 376 | /// Disable GPIO1 in NVIC. | ||
| 377 | #[inline] | ||
| 378 | unsafe fn disable(&self) { | ||
| 379 | cortex_m::peripheral::NVIC::mask(Interrupt::GPIO1); | ||
| 380 | } | ||
| 381 | |||
| 382 | /// Check if GPIO1 is pending in NVIC. | ||
| 383 | #[inline] | ||
| 384 | fn is_pending(&self) -> bool { | ||
| 385 | cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO1) | ||
| 386 | } | ||
| 387 | } | ||
| 388 | |||
| 389 | pub struct Gpio2; | ||
| 390 | pub const GPIO2: Gpio2 = Gpio2; | ||
| 391 | |||
| 392 | impl InterruptExt for Gpio2 { | ||
| 393 | /// Clear any pending GPIO2 in NVIC. | ||
| 394 | #[inline] | ||
| 395 | fn unpend(&self) { | ||
| 396 | cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO2); | ||
| 397 | } | ||
| 398 | |||
| 399 | /// Set NVIC priority for GPIO2. | ||
| 400 | #[inline] | ||
| 401 | fn set_priority(&self, priority: Priority) { | ||
| 402 | unsafe { | ||
| 403 | let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; | ||
| 404 | nvic.set_priority(Interrupt::GPIO2, u8::from(priority)); | ||
| 405 | } | ||
| 406 | } | ||
| 407 | |||
| 408 | /// Enable GPIO2 in NVIC. | ||
| 409 | #[inline] | ||
| 410 | unsafe fn enable(&self) { | ||
| 411 | cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO2); | ||
| 412 | } | ||
| 413 | |||
| 414 | /// Disable GPIO2 in NVIC. | ||
| 415 | #[inline] | ||
| 416 | unsafe fn disable(&self) { | ||
| 417 | cortex_m::peripheral::NVIC::mask(Interrupt::GPIO2); | ||
| 418 | } | ||
| 419 | |||
| 420 | /// Check if GPIO2 is pending in NVIC. | ||
| 421 | #[inline] | ||
| 422 | fn is_pending(&self) -> bool { | ||
| 423 | cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO2) | ||
| 424 | } | ||
| 425 | } | ||
| 426 | |||
| 427 | pub struct Gpio3; | ||
| 428 | pub const GPIO3: Gpio3 = Gpio3; | ||
| 429 | |||
| 430 | impl InterruptExt for Gpio3 { | ||
| 431 | /// Clear any pending GPIO3 in NVIC. | ||
| 432 | #[inline] | ||
| 433 | fn unpend(&self) { | ||
| 434 | cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO3); | ||
| 435 | } | ||
| 436 | |||
| 437 | /// Set NVIC priority for GPIO3. | ||
| 438 | #[inline] | ||
| 439 | fn set_priority(&self, priority: Priority) { | ||
| 440 | unsafe { | ||
| 441 | let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; | ||
| 442 | nvic.set_priority(Interrupt::GPIO3, u8::from(priority)); | ||
| 443 | } | ||
| 444 | } | ||
| 445 | |||
| 446 | /// Enable GPIO3 in NVIC. | ||
| 447 | #[inline] | ||
| 448 | unsafe fn enable(&self) { | ||
| 449 | cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO3); | ||
| 450 | } | ||
| 451 | |||
| 452 | /// Disable GPIO3 in NVIC. | ||
| 453 | #[inline] | ||
| 454 | unsafe fn disable(&self) { | ||
| 455 | cortex_m::peripheral::NVIC::mask(Interrupt::GPIO3); | ||
| 456 | } | ||
| 457 | |||
| 458 | /// Check if GPIO3 is pending in NVIC. | ||
| 459 | #[inline] | ||
| 460 | fn is_pending(&self) -> bool { | ||
| 461 | cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO3) | ||
| 462 | } | ||
| 463 | } | ||
| 464 | |||
| 465 | pub struct Gpio4; | ||
| 466 | pub const GPIO4: Gpio4 = Gpio4; | ||
| 467 | |||
| 468 | impl InterruptExt for Gpio4 { | ||
| 469 | /// Clear any pending GPIO4 in NVIC. | ||
| 470 | #[inline] | ||
| 471 | fn unpend(&self) { | ||
| 472 | cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO4); | ||
| 473 | } | ||
| 474 | |||
| 475 | /// Set NVIC priority for GPIO4. | ||
| 476 | #[inline] | ||
| 477 | fn set_priority(&self, priority: Priority) { | ||
| 478 | unsafe { | ||
| 479 | let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; | ||
| 480 | nvic.set_priority(Interrupt::GPIO4, u8::from(priority)); | ||
| 481 | } | ||
| 482 | } | ||
| 483 | |||
| 484 | /// Enable GPIO4 in NVIC. | ||
| 485 | #[inline] | ||
| 486 | unsafe fn enable(&self) { | ||
| 487 | cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO4); | ||
| 488 | } | ||
| 489 | |||
| 490 | /// Disable GPIO4 in NVIC. | ||
| 491 | #[inline] | ||
| 492 | unsafe fn disable(&self) { | ||
| 493 | cortex_m::peripheral::NVIC::mask(Interrupt::GPIO4); | ||
| 494 | } | ||
| 495 | |||
| 496 | /// Check if GPIO4 is pending in NVIC. | ||
| 497 | #[inline] | ||
| 498 | fn is_pending(&self) -> bool { | ||
| 499 | cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO4) | ||
| 500 | } | ||
| 501 | } | ||
| 502 | |||
| 503 | /// Set VTOR (Vector Table Offset) to a RAM-based vector table. | ||
| 504 | /// Pass a pointer to the first word in the RAM table (stack pointer slot 0). | ||
| 505 | /// Safety: Caller must ensure the RAM table is valid and aligned as required by the core. | ||
| 506 | pub unsafe fn vtor_set_ram_vector_base(base: *const u32) { | ||
| 507 | core::ptr::write_volatile(0xE000_ED08 as *mut u32, base as u32); | ||
| 508 | } | ||
| 509 | |||
| 510 | /// Install an interrupt handler into the current VTOR-based vector table. | ||
| 511 | /// This writes the function pointer at index 16 + irq number. | ||
| 512 | /// Safety: Caller must ensure VTOR points at a writable RAM table and that `handler` | ||
| 513 | /// has the correct ABI and lifetime. | ||
| 514 | pub unsafe fn install_irq_handler(irq: Interrupt, handler: unsafe extern "C" fn()) { | ||
| 515 | let vtor_base = core::ptr::read_volatile(0xE000_ED08 as *const u32) as *mut u32; | ||
| 516 | let idx = 16 + (irq as isize as usize); | ||
| 517 | core::ptr::write_volatile(vtor_base.add(idx), handler as usize as u32); | ||
| 518 | } | ||
| 519 | |||
| 520 | impl OsEvent { | ||
| 521 | /// Convenience to install the OS_EVENT handler into the RAM vector table. | ||
| 522 | /// Safety: See `install_irq_handler`. | ||
| 523 | pub unsafe fn install_handler(&self, handler: extern "C" fn()) { | ||
| 524 | install_irq_handler(Interrupt::OS_EVENT, handler); | ||
| 525 | } | ||
| 526 | } | ||
| 527 | |||
| 528 | /// Install OS_EVENT handler by raw address. Useful to avoid fn pointer type mismatches. | ||
| 529 | /// Safety: Caller must ensure the address is a valid `extern "C" fn()` handler. | ||
| 530 | pub unsafe fn os_event_install_handler_raw(handler_addr: usize) { | ||
| 531 | let vtor_base = core::ptr::read_volatile(0xE000_ED08 as *const u32) as *mut u32; | ||
| 532 | let idx = 16 + (Interrupt::OS_EVENT as isize as usize); | ||
| 533 | core::ptr::write_volatile(vtor_base.add(idx), handler_addr as u32); | ||
| 534 | } | ||
| 535 | |||
| 536 | /// Provide a conservative default IRQ handler that avoids wedging the system. | ||
| 537 | /// It clears all NVIC pending bits and returns, so spurious or reserved IRQs | ||
| 538 | /// don’t trap the core in an infinite WFI loop during bring-up. | ||
| 539 | #[no_mangle] | ||
| 540 | pub unsafe extern "C" fn DefaultHandler() { | ||
| 541 | let active = core::ptr::read_volatile(0xE000_ED04 as *const u32) & 0x1FF; | ||
| 542 | let cfsr = core::ptr::read_volatile(0xE000_ED28 as *const u32); | ||
| 543 | let hfsr = core::ptr::read_volatile(0xE000_ED2C as *const u32); | ||
| 544 | |||
| 545 | let sp = cortex_m::register::msp::read(); | ||
| 546 | let stacked = sp as *const u32; | ||
| 547 | // Stacked registers follow ARMv8-M procedure call standard order | ||
| 548 | let stacked_pc = unsafe { stacked.add(6).read() }; | ||
| 549 | let stacked_lr = unsafe { stacked.add(5).read() }; | ||
| 550 | |||
| 551 | LAST_DEFAULT_VECTOR.store(active as u16, Ordering::Relaxed); | ||
| 552 | LAST_DEFAULT_CFSR.store(cfsr, Ordering::Relaxed); | ||
| 553 | LAST_DEFAULT_HFSR.store(hfsr, Ordering::Relaxed); | ||
| 554 | LAST_DEFAULT_COUNT.fetch_add(1, Ordering::Relaxed); | ||
| 555 | LAST_DEFAULT_PC.store(stacked_pc, Ordering::Relaxed); | ||
| 556 | LAST_DEFAULT_LR.store(stacked_lr, Ordering::Relaxed); | ||
| 557 | LAST_DEFAULT_SP.store(sp, Ordering::Relaxed); | ||
| 558 | |||
| 559 | // Do nothing here: on some MCUs/TrustZone setups, writing NVIC from a spurious | ||
| 560 | // handler can fault if targeting the Secure bank. Just return. | ||
| 561 | cortex_m::asm::dsb(); | ||
| 562 | cortex_m::asm::isb(); | ||
| 563 | } | ||
diff --git a/embassy-mcxa/src/lib.rs b/embassy-mcxa/src/lib.rs new file mode 100644 index 000000000..c6d8adc8f --- /dev/null +++ b/embassy-mcxa/src/lib.rs | |||
| @@ -0,0 +1,471 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![allow(async_fn_in_trait)] | ||
| 3 | #![doc = include_str!("../README.md")] | ||
| 4 | |||
| 5 | // //! ## Feature flags | ||
| 6 | // #![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)] | ||
| 7 | |||
| 8 | pub mod clocks; // still provide clock helpers | ||
| 9 | pub mod gpio; | ||
| 10 | pub mod pins; // pin mux helpers | ||
| 11 | |||
| 12 | pub mod adc; | ||
| 13 | pub mod clkout; | ||
| 14 | pub mod config; | ||
| 15 | pub mod i2c; | ||
| 16 | pub mod interrupt; | ||
| 17 | pub mod lpuart; | ||
| 18 | pub mod ostimer; | ||
| 19 | pub mod rtc; | ||
| 20 | |||
| 21 | pub use crate::pac::NVIC_PRIO_BITS; | ||
| 22 | |||
| 23 | #[rustfmt::skip] | ||
| 24 | embassy_hal_internal::peripherals!( | ||
| 25 | ADC0, | ||
| 26 | ADC1, | ||
| 27 | |||
| 28 | AOI0, | ||
| 29 | AOI1, | ||
| 30 | |||
| 31 | CAN0, | ||
| 32 | CAN1, | ||
| 33 | |||
| 34 | CDOG0, | ||
| 35 | CDOG1, | ||
| 36 | |||
| 37 | // CLKOUT is not specifically a peripheral (it's part of SYSCON), | ||
| 38 | // but we still want it to be a singleton. | ||
| 39 | CLKOUT, | ||
| 40 | |||
| 41 | CMC, | ||
| 42 | CMP0, | ||
| 43 | CMP1, | ||
| 44 | CRC0, | ||
| 45 | |||
| 46 | CTIMER0, | ||
| 47 | CTIMER1, | ||
| 48 | CTIMER2, | ||
| 49 | CTIMER3, | ||
| 50 | CTIMER4, | ||
| 51 | |||
| 52 | DBGMAILBOX, | ||
| 53 | DMA0, | ||
| 54 | EDMA0_TCD0, | ||
| 55 | EIM0, | ||
| 56 | EQDC0, | ||
| 57 | EQDC1, | ||
| 58 | ERM0, | ||
| 59 | FLEXIO0, | ||
| 60 | FLEXPWM0, | ||
| 61 | FLEXPWM1, | ||
| 62 | FMC0, | ||
| 63 | FMU0, | ||
| 64 | FREQME0, | ||
| 65 | GLIKEY0, | ||
| 66 | |||
| 67 | GPIO0, | ||
| 68 | GPIO1, | ||
| 69 | GPIO2, | ||
| 70 | GPIO3, | ||
| 71 | GPIO4, | ||
| 72 | |||
| 73 | I3C0, | ||
| 74 | INPUTMUX0, | ||
| 75 | |||
| 76 | LPI2C0, | ||
| 77 | LPI2C1, | ||
| 78 | LPI2C2, | ||
| 79 | LPI2C3, | ||
| 80 | |||
| 81 | LPSPI0, | ||
| 82 | LPSPI1, | ||
| 83 | |||
| 84 | LPTMR0, | ||
| 85 | |||
| 86 | LPUART0, | ||
| 87 | LPUART1, | ||
| 88 | LPUART2, | ||
| 89 | LPUART3, | ||
| 90 | LPUART4, | ||
| 91 | LPUART5, | ||
| 92 | |||
| 93 | MAU0, | ||
| 94 | MBC0, | ||
| 95 | MRCC0, | ||
| 96 | OPAMP0, | ||
| 97 | |||
| 98 | #[cfg(not(feature = "time"))] | ||
| 99 | OSTIMER0, | ||
| 100 | |||
| 101 | P0_0, | ||
| 102 | P0_1, | ||
| 103 | P0_2, | ||
| 104 | P0_3, | ||
| 105 | P0_4, | ||
| 106 | P0_5, | ||
| 107 | P0_6, | ||
| 108 | P0_7, | ||
| 109 | P0_8, | ||
| 110 | P0_9, | ||
| 111 | P0_10, | ||
| 112 | P0_11, | ||
| 113 | P0_12, | ||
| 114 | P0_13, | ||
| 115 | P0_14, | ||
| 116 | P0_15, | ||
| 117 | P0_16, | ||
| 118 | P0_17, | ||
| 119 | P0_18, | ||
| 120 | P0_19, | ||
| 121 | P0_20, | ||
| 122 | P0_21, | ||
| 123 | P0_22, | ||
| 124 | P0_23, | ||
| 125 | P0_24, | ||
| 126 | P0_25, | ||
| 127 | P0_26, | ||
| 128 | P0_27, | ||
| 129 | P0_28, | ||
| 130 | P0_29, | ||
| 131 | P0_30, | ||
| 132 | P0_31, | ||
| 133 | |||
| 134 | P1_0, | ||
| 135 | P1_1, | ||
| 136 | P1_2, | ||
| 137 | P1_3, | ||
| 138 | P1_4, | ||
| 139 | P1_5, | ||
| 140 | P1_6, | ||
| 141 | P1_7, | ||
| 142 | P1_8, | ||
| 143 | P1_9, | ||
| 144 | P1_10, | ||
| 145 | P1_11, | ||
| 146 | P1_12, | ||
| 147 | P1_13, | ||
| 148 | P1_14, | ||
| 149 | P1_15, | ||
| 150 | P1_16, | ||
| 151 | P1_17, | ||
| 152 | P1_18, | ||
| 153 | P1_19, | ||
| 154 | P1_20, | ||
| 155 | P1_21, | ||
| 156 | P1_22, | ||
| 157 | P1_23, | ||
| 158 | P1_24, | ||
| 159 | P1_25, | ||
| 160 | P1_26, | ||
| 161 | P1_27, | ||
| 162 | P1_28, | ||
| 163 | P1_29, | ||
| 164 | P1_30, | ||
| 165 | P1_31, | ||
| 166 | |||
| 167 | P2_0, | ||
| 168 | P2_1, | ||
| 169 | P2_2, | ||
| 170 | P2_3, | ||
| 171 | P2_4, | ||
| 172 | P2_5, | ||
| 173 | P2_6, | ||
| 174 | P2_7, | ||
| 175 | P2_8, | ||
| 176 | P2_9, | ||
| 177 | P2_10, | ||
| 178 | P2_11, | ||
| 179 | P2_12, | ||
| 180 | P2_13, | ||
| 181 | P2_14, | ||
| 182 | P2_15, | ||
| 183 | P2_16, | ||
| 184 | P2_17, | ||
| 185 | P2_18, | ||
| 186 | P2_19, | ||
| 187 | P2_20, | ||
| 188 | P2_21, | ||
| 189 | P2_22, | ||
| 190 | P2_23, | ||
| 191 | P2_24, | ||
| 192 | P2_25, | ||
| 193 | P2_26, | ||
| 194 | P2_27, | ||
| 195 | P2_28, | ||
| 196 | P2_29, | ||
| 197 | P2_30, | ||
| 198 | P2_31, | ||
| 199 | |||
| 200 | P3_0, | ||
| 201 | P3_1, | ||
| 202 | P3_2, | ||
| 203 | P3_3, | ||
| 204 | P3_4, | ||
| 205 | P3_5, | ||
| 206 | P3_6, | ||
| 207 | P3_7, | ||
| 208 | P3_8, | ||
| 209 | P3_9, | ||
| 210 | P3_10, | ||
| 211 | P3_11, | ||
| 212 | P3_12, | ||
| 213 | P3_13, | ||
| 214 | P3_14, | ||
| 215 | P3_15, | ||
| 216 | P3_16, | ||
| 217 | P3_17, | ||
| 218 | P3_18, | ||
| 219 | P3_19, | ||
| 220 | P3_20, | ||
| 221 | P3_21, | ||
| 222 | P3_22, | ||
| 223 | P3_23, | ||
| 224 | P3_24, | ||
| 225 | P3_25, | ||
| 226 | P3_26, | ||
| 227 | P3_27, | ||
| 228 | P3_28, | ||
| 229 | P3_29, | ||
| 230 | P3_30, | ||
| 231 | P3_31, | ||
| 232 | |||
| 233 | P4_0, | ||
| 234 | P4_1, | ||
| 235 | P4_2, | ||
| 236 | P4_3, | ||
| 237 | P4_4, | ||
| 238 | P4_5, | ||
| 239 | P4_6, | ||
| 240 | P4_7, | ||
| 241 | P4_8, | ||
| 242 | P4_9, | ||
| 243 | P4_10, | ||
| 244 | P4_11, | ||
| 245 | P4_12, | ||
| 246 | P4_13, | ||
| 247 | P4_14, | ||
| 248 | P4_15, | ||
| 249 | P4_16, | ||
| 250 | P4_17, | ||
| 251 | P4_18, | ||
| 252 | P4_19, | ||
| 253 | P4_20, | ||
| 254 | P4_21, | ||
| 255 | P4_22, | ||
| 256 | P4_23, | ||
| 257 | P4_24, | ||
| 258 | P4_25, | ||
| 259 | P4_26, | ||
| 260 | P4_27, | ||
| 261 | P4_28, | ||
| 262 | P4_29, | ||
| 263 | P4_30, | ||
| 264 | P4_31, | ||
| 265 | |||
| 266 | P5_0, | ||
| 267 | P5_1, | ||
| 268 | P5_2, | ||
| 269 | P5_3, | ||
| 270 | P5_4, | ||
| 271 | P5_5, | ||
| 272 | P5_6, | ||
| 273 | P5_7, | ||
| 274 | P5_8, | ||
| 275 | P5_9, | ||
| 276 | P5_10, | ||
| 277 | P5_11, | ||
| 278 | P5_12, | ||
| 279 | P5_13, | ||
| 280 | P5_14, | ||
| 281 | P5_15, | ||
| 282 | P5_16, | ||
| 283 | P5_17, | ||
| 284 | P5_18, | ||
| 285 | P5_19, | ||
| 286 | P5_20, | ||
| 287 | P5_21, | ||
| 288 | P5_22, | ||
| 289 | P5_23, | ||
| 290 | P5_24, | ||
| 291 | P5_25, | ||
| 292 | P5_26, | ||
| 293 | P5_27, | ||
| 294 | P5_28, | ||
| 295 | P5_29, | ||
| 296 | P5_30, | ||
| 297 | P5_31, | ||
| 298 | |||
| 299 | PKC0, | ||
| 300 | |||
| 301 | PORT0, | ||
| 302 | PORT1, | ||
| 303 | PORT2, | ||
| 304 | PORT3, | ||
| 305 | PORT4, | ||
| 306 | |||
| 307 | RTC0, | ||
| 308 | SAU, | ||
| 309 | SCG0, | ||
| 310 | SCN_SCB, | ||
| 311 | SGI0, | ||
| 312 | SMARTDMA0, | ||
| 313 | SPC0, | ||
| 314 | SYSCON, | ||
| 315 | TDET0, | ||
| 316 | TRNG0, | ||
| 317 | UDF0, | ||
| 318 | USB0, | ||
| 319 | UTICK0, | ||
| 320 | VBAT0, | ||
| 321 | WAKETIMER0, | ||
| 322 | WUU0, | ||
| 323 | WWDT0, | ||
| 324 | ); | ||
| 325 | |||
| 326 | // Use cortex-m-rt's #[interrupt] attribute directly; PAC does not re-export it. | ||
| 327 | |||
| 328 | // Re-export interrupt traits and types | ||
| 329 | pub use adc::Adc1 as Adc1Token; | ||
| 330 | pub use gpio::{AnyPin, Flex, Gpio as GpioToken, Input, Level, Output}; | ||
| 331 | pub use interrupt::InterruptExt; | ||
| 332 | #[cfg(feature = "unstable-pac")] | ||
| 333 | pub use mcxa_pac as pac; | ||
| 334 | #[cfg(not(feature = "unstable-pac"))] | ||
| 335 | pub(crate) use mcxa_pac as pac; | ||
| 336 | |||
| 337 | /// Initialize HAL with configuration (mirrors embassy-imxrt style). Minimal: just take peripherals. | ||
| 338 | /// Also applies configurable NVIC priority for the OSTIMER OS_EVENT interrupt (no enabling). | ||
| 339 | pub fn init(cfg: crate::config::Config) -> Peripherals { | ||
| 340 | let peripherals = Peripherals::take(); | ||
| 341 | // Apply user-configured priority early; enabling is left to examples/apps | ||
| 342 | #[cfg(feature = "time")] | ||
| 343 | crate::interrupt::OS_EVENT.set_priority(cfg.time_interrupt_priority); | ||
| 344 | // Apply user-configured priority early; enabling is left to examples/apps | ||
| 345 | crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority); | ||
| 346 | // Apply user-configured priority early; enabling is left to examples/apps | ||
| 347 | crate::interrupt::ADC1.set_priority(cfg.adc_interrupt_priority); | ||
| 348 | // Apply user-configured priority early; enabling is left to examples/apps | ||
| 349 | crate::interrupt::GPIO0.set_priority(cfg.gpio_interrupt_priority); | ||
| 350 | // Apply user-configured priority early; enabling is left to examples/apps | ||
| 351 | crate::interrupt::GPIO1.set_priority(cfg.gpio_interrupt_priority); | ||
| 352 | // Apply user-configured priority early; enabling is left to examples/apps | ||
| 353 | crate::interrupt::GPIO2.set_priority(cfg.gpio_interrupt_priority); | ||
| 354 | // Apply user-configured priority early; enabling is left to examples/apps | ||
| 355 | crate::interrupt::GPIO3.set_priority(cfg.gpio_interrupt_priority); | ||
| 356 | // Apply user-configured priority early; enabling is left to examples/apps | ||
| 357 | crate::interrupt::GPIO4.set_priority(cfg.gpio_interrupt_priority); | ||
| 358 | |||
| 359 | // Configure clocks | ||
| 360 | crate::clocks::init(cfg.clock_cfg).unwrap(); | ||
| 361 | |||
| 362 | unsafe { | ||
| 363 | crate::gpio::init(); | ||
| 364 | } | ||
| 365 | |||
| 366 | // Initialize embassy-time global driver backed by OSTIMER0 | ||
| 367 | #[cfg(feature = "time")] | ||
| 368 | crate::ostimer::time_driver::init(crate::config::Config::default().time_interrupt_priority, 1_000_000); | ||
| 369 | |||
| 370 | // Enable GPIO clocks | ||
| 371 | unsafe { | ||
| 372 | _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT0>(&crate::clocks::periph_helpers::NoConfig); | ||
| 373 | _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO0>(&crate::clocks::periph_helpers::NoConfig); | ||
| 374 | |||
| 375 | _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT1>(&crate::clocks::periph_helpers::NoConfig); | ||
| 376 | _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO1>(&crate::clocks::periph_helpers::NoConfig); | ||
| 377 | |||
| 378 | _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT2>(&crate::clocks::periph_helpers::NoConfig); | ||
| 379 | _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO2>(&crate::clocks::periph_helpers::NoConfig); | ||
| 380 | |||
| 381 | _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT3>(&crate::clocks::periph_helpers::NoConfig); | ||
| 382 | _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO3>(&crate::clocks::periph_helpers::NoConfig); | ||
| 383 | |||
| 384 | _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT4>(&crate::clocks::periph_helpers::NoConfig); | ||
| 385 | _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO4>(&crate::clocks::periph_helpers::NoConfig); | ||
| 386 | } | ||
| 387 | |||
| 388 | peripherals | ||
| 389 | } | ||
| 390 | |||
| 391 | // /// Optional hook called by cortex-m-rt before RAM init. | ||
| 392 | // /// We proactively mask and clear all NVIC IRQs to avoid wedges from stale state | ||
| 393 | // /// left by soft resets/debug sessions. | ||
| 394 | // /// | ||
| 395 | // /// NOTE: Manual VTOR setup is required for RAM execution. The cortex-m-rt 'set-vtor' | ||
| 396 | // /// feature is incompatible with our setup because it expects __vector_table to be | ||
| 397 | // /// defined differently than how our RAM-based linker script arranges it. | ||
| 398 | // #[no_mangle] | ||
| 399 | // pub unsafe extern "C" fn __pre_init() { | ||
| 400 | // // Set the VTOR to point to the interrupt vector table in RAM | ||
| 401 | // // This is required since code runs from RAM on this MCU | ||
| 402 | // crate::interrupt::vtor_set_ram_vector_base(0x2000_0000 as *const u32); | ||
| 403 | |||
| 404 | // // Mask and clear pending for all NVIC lines (0..127) to avoid stale state across runs. | ||
| 405 | // let nvic = &*cortex_m::peripheral::NVIC::PTR; | ||
| 406 | // for i in 0..4 { | ||
| 407 | // // 4 words x 32 = 128 IRQs | ||
| 408 | // nvic.icer[i].write(0xFFFF_FFFF); | ||
| 409 | // nvic.icpr[i].write(0xFFFF_FFFF); | ||
| 410 | // } | ||
| 411 | // // Do NOT touch peripheral registers here: clocks may be off and accesses can fault. | ||
| 412 | // crate::interrupt::clear_default_handler_snapshot(); | ||
| 413 | // } | ||
| 414 | |||
| 415 | /// Internal helper to dispatch a type-level interrupt handler. | ||
| 416 | #[inline(always)] | ||
| 417 | #[doc(hidden)] | ||
| 418 | pub unsafe fn __handle_interrupt<T, H>() | ||
| 419 | where | ||
| 420 | T: crate::interrupt::typelevel::Interrupt, | ||
| 421 | H: crate::interrupt::typelevel::Handler<T>, | ||
| 422 | { | ||
| 423 | H::on_interrupt(); | ||
| 424 | } | ||
| 425 | |||
| 426 | /// Macro to bind interrupts to handlers, similar to embassy-imxrt. | ||
| 427 | /// | ||
| 428 | /// Example: | ||
| 429 | /// - Bind OS_EVENT to the OSTIMER time-driver handler | ||
| 430 | /// bind_interrupts!(struct Irqs { OS_EVENT => crate::ostimer::time_driver::OsEventHandler; }); | ||
| 431 | #[macro_export] | ||
| 432 | macro_rules! bind_interrupts { | ||
| 433 | ($(#[$attr:meta])* $vis:vis struct $name:ident { | ||
| 434 | $( | ||
| 435 | $(#[cfg($cond_irq:meta)])? | ||
| 436 | $irq:ident => $( | ||
| 437 | $(#[cfg($cond_handler:meta)])? | ||
| 438 | $handler:ty | ||
| 439 | ),*; | ||
| 440 | )* | ||
| 441 | }) => { | ||
| 442 | #[derive(Copy, Clone)] | ||
| 443 | $(#[$attr])* | ||
| 444 | $vis struct $name; | ||
| 445 | |||
| 446 | $( | ||
| 447 | #[allow(non_snake_case)] | ||
| 448 | #[no_mangle] | ||
| 449 | $(#[cfg($cond_irq)])? | ||
| 450 | unsafe extern "C" fn $irq() { | ||
| 451 | unsafe { | ||
| 452 | $( | ||
| 453 | $(#[cfg($cond_handler)])? | ||
| 454 | <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); | ||
| 455 | )* | ||
| 456 | } | ||
| 457 | } | ||
| 458 | |||
| 459 | $(#[cfg($cond_irq)])? | ||
| 460 | $crate::bind_interrupts!(@inner | ||
| 461 | $( | ||
| 462 | $(#[cfg($cond_handler)])? | ||
| 463 | unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} | ||
| 464 | )* | ||
| 465 | ); | ||
| 466 | )* | ||
| 467 | }; | ||
| 468 | (@inner $($t:tt)*) => { | ||
| 469 | $($t)* | ||
| 470 | } | ||
| 471 | } | ||
diff --git a/embassy-mcxa/src/lpuart/buffered.rs b/embassy-mcxa/src/lpuart/buffered.rs new file mode 100644 index 000000000..34fdbb76c --- /dev/null +++ b/embassy-mcxa/src/lpuart/buffered.rs | |||
| @@ -0,0 +1,780 @@ | |||
| 1 | use core::future::poll_fn; | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 4 | use core::task::Poll; | ||
| 5 | |||
| 6 | use embassy_hal_internal::Peri; | ||
| 7 | use embassy_hal_internal::atomic_ring_buffer::RingBuffer; | ||
| 8 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 9 | |||
| 10 | use super::*; | ||
| 11 | use crate::interrupt; | ||
| 12 | |||
| 13 | // ============================================================================ | ||
| 14 | // STATIC STATE MANAGEMENT | ||
| 15 | // ============================================================================ | ||
| 16 | |||
| 17 | /// State for buffered LPUART operations | ||
| 18 | pub struct State { | ||
| 19 | tx_waker: AtomicWaker, | ||
| 20 | tx_buf: RingBuffer, | ||
| 21 | tx_done: AtomicBool, | ||
| 22 | rx_waker: AtomicWaker, | ||
| 23 | rx_buf: RingBuffer, | ||
| 24 | initialized: AtomicBool, | ||
| 25 | } | ||
| 26 | |||
| 27 | impl Default for State { | ||
| 28 | fn default() -> Self { | ||
| 29 | Self::new() | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | impl State { | ||
| 34 | /// Create a new state instance | ||
| 35 | pub const fn new() -> Self { | ||
| 36 | Self { | ||
| 37 | tx_waker: AtomicWaker::new(), | ||
| 38 | tx_buf: RingBuffer::new(), | ||
| 39 | tx_done: AtomicBool::new(true), | ||
| 40 | rx_waker: AtomicWaker::new(), | ||
| 41 | rx_buf: RingBuffer::new(), | ||
| 42 | initialized: AtomicBool::new(false), | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | // ============================================================================ | ||
| 47 | // BUFFERED DRIVER STRUCTURES | ||
| 48 | // ============================================================================ | ||
| 49 | |||
| 50 | /// Buffered LPUART driver | ||
| 51 | pub struct BufferedLpuart<'a> { | ||
| 52 | tx: BufferedLpuartTx<'a>, | ||
| 53 | rx: BufferedLpuartRx<'a>, | ||
| 54 | } | ||
| 55 | |||
| 56 | /// Buffered LPUART TX driver | ||
| 57 | pub struct BufferedLpuartTx<'a> { | ||
| 58 | info: Info, | ||
| 59 | state: &'static State, | ||
| 60 | _tx_pin: Peri<'a, AnyPin>, | ||
| 61 | _cts_pin: Option<Peri<'a, AnyPin>>, | ||
| 62 | } | ||
| 63 | |||
| 64 | /// Buffered LPUART RX driver | ||
| 65 | pub struct BufferedLpuartRx<'a> { | ||
| 66 | info: Info, | ||
| 67 | state: &'static State, | ||
| 68 | _rx_pin: Peri<'a, AnyPin>, | ||
| 69 | _rts_pin: Option<Peri<'a, AnyPin>>, | ||
| 70 | } | ||
| 71 | |||
| 72 | // ============================================================================ | ||
| 73 | // BUFFERED LPUART IMPLEMENTATION | ||
| 74 | // ============================================================================ | ||
| 75 | |||
| 76 | impl<'a> BufferedLpuart<'a> { | ||
| 77 | /// Common initialization logic | ||
| 78 | fn init_common<T: Instance>( | ||
| 79 | _inner: &Peri<'a, T>, | ||
| 80 | tx_buffer: Option<&'a mut [u8]>, | ||
| 81 | rx_buffer: Option<&'a mut [u8]>, | ||
| 82 | config: &Config, | ||
| 83 | enable_tx: bool, | ||
| 84 | enable_rx: bool, | ||
| 85 | enable_rts: bool, | ||
| 86 | enable_cts: bool, | ||
| 87 | ) -> Result<&'static State> { | ||
| 88 | let state = T::buffered_state(); | ||
| 89 | |||
| 90 | if state.initialized.load(Ordering::Relaxed) { | ||
| 91 | return Err(Error::InvalidArgument); | ||
| 92 | } | ||
| 93 | |||
| 94 | // Initialize buffers | ||
| 95 | if let Some(tx_buffer) = tx_buffer { | ||
| 96 | if tx_buffer.is_empty() { | ||
| 97 | return Err(Error::InvalidArgument); | ||
| 98 | } | ||
| 99 | unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), tx_buffer.len()) }; | ||
| 100 | } | ||
| 101 | |||
| 102 | if let Some(rx_buffer) = rx_buffer { | ||
| 103 | if rx_buffer.is_empty() { | ||
| 104 | return Err(Error::InvalidArgument); | ||
| 105 | } | ||
| 106 | unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), rx_buffer.len()) }; | ||
| 107 | } | ||
| 108 | |||
| 109 | state.initialized.store(true, Ordering::Relaxed); | ||
| 110 | |||
| 111 | // Enable clocks and initialize hardware | ||
| 112 | let conf = LpuartConfig { | ||
| 113 | power: config.power, | ||
| 114 | source: config.source, | ||
| 115 | div: config.div, | ||
| 116 | instance: T::CLOCK_INSTANCE, | ||
| 117 | }; | ||
| 118 | let clock_freq = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? }; | ||
| 119 | |||
| 120 | Self::init_hardware( | ||
| 121 | T::info().regs, | ||
| 122 | *config, | ||
| 123 | clock_freq, | ||
| 124 | enable_tx, | ||
| 125 | enable_rx, | ||
| 126 | enable_rts, | ||
| 127 | enable_cts, | ||
| 128 | )?; | ||
| 129 | |||
| 130 | Ok(state) | ||
| 131 | } | ||
| 132 | |||
| 133 | /// Helper for full-duplex initialization | ||
| 134 | fn new_inner<T: Instance>( | ||
| 135 | inner: Peri<'a, T>, | ||
| 136 | tx_pin: Peri<'a, AnyPin>, | ||
| 137 | rx_pin: Peri<'a, AnyPin>, | ||
| 138 | rts_pin: Option<Peri<'a, AnyPin>>, | ||
| 139 | cts_pin: Option<Peri<'a, AnyPin>>, | ||
| 140 | tx_buffer: &'a mut [u8], | ||
| 141 | rx_buffer: &'a mut [u8], | ||
| 142 | config: Config, | ||
| 143 | ) -> Result<(BufferedLpuartTx<'a>, BufferedLpuartRx<'a>)> { | ||
| 144 | let state = Self::init_common::<T>( | ||
| 145 | &inner, | ||
| 146 | Some(tx_buffer), | ||
| 147 | Some(rx_buffer), | ||
| 148 | &config, | ||
| 149 | true, | ||
| 150 | true, | ||
| 151 | rts_pin.is_some(), | ||
| 152 | cts_pin.is_some(), | ||
| 153 | )?; | ||
| 154 | |||
| 155 | let tx = BufferedLpuartTx { | ||
| 156 | info: T::info(), | ||
| 157 | state, | ||
| 158 | _tx_pin: tx_pin, | ||
| 159 | _cts_pin: cts_pin, | ||
| 160 | }; | ||
| 161 | |||
| 162 | let rx = BufferedLpuartRx { | ||
| 163 | info: T::info(), | ||
| 164 | state, | ||
| 165 | _rx_pin: rx_pin, | ||
| 166 | _rts_pin: rts_pin, | ||
| 167 | }; | ||
| 168 | |||
| 169 | Ok((tx, rx)) | ||
| 170 | } | ||
| 171 | |||
| 172 | /// Common hardware initialization logic | ||
| 173 | fn init_hardware( | ||
| 174 | regs: &'static mcxa_pac::lpuart0::RegisterBlock, | ||
| 175 | config: Config, | ||
| 176 | clock_freq: u32, | ||
| 177 | enable_tx: bool, | ||
| 178 | enable_rx: bool, | ||
| 179 | enable_rts: bool, | ||
| 180 | enable_cts: bool, | ||
| 181 | ) -> Result<()> { | ||
| 182 | // Perform standard initialization | ||
| 183 | perform_software_reset(regs); | ||
| 184 | disable_transceiver(regs); | ||
| 185 | configure_baudrate(regs, config.baudrate_bps, clock_freq)?; | ||
| 186 | configure_frame_format(regs, &config); | ||
| 187 | configure_control_settings(regs, &config); | ||
| 188 | configure_fifo(regs, &config); | ||
| 189 | clear_all_status_flags(regs); | ||
| 190 | configure_flow_control(regs, enable_rts, enable_cts, &config); | ||
| 191 | configure_bit_order(regs, config.msb_first); | ||
| 192 | |||
| 193 | // Enable interrupts for buffered operation | ||
| 194 | cortex_m::interrupt::free(|_| { | ||
| 195 | regs.ctrl().modify(|_, w| { | ||
| 196 | w.rie() | ||
| 197 | .enabled() // RX interrupt | ||
| 198 | .orie() | ||
| 199 | .enabled() // Overrun interrupt | ||
| 200 | .peie() | ||
| 201 | .enabled() // Parity error interrupt | ||
| 202 | .feie() | ||
| 203 | .enabled() // Framing error interrupt | ||
| 204 | .neie() | ||
| 205 | .enabled() // Noise error interrupt | ||
| 206 | }); | ||
| 207 | }); | ||
| 208 | |||
| 209 | // Enable the transceiver | ||
| 210 | enable_transceiver(regs, enable_rx, enable_tx); | ||
| 211 | |||
| 212 | Ok(()) | ||
| 213 | } | ||
| 214 | |||
| 215 | /// Create a new full duplex buffered LPUART | ||
| 216 | pub fn new<T: Instance>( | ||
| 217 | inner: Peri<'a, T>, | ||
| 218 | tx_pin: Peri<'a, impl TxPin<T>>, | ||
| 219 | rx_pin: Peri<'a, impl RxPin<T>>, | ||
| 220 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a, | ||
| 221 | tx_buffer: &'a mut [u8], | ||
| 222 | rx_buffer: &'a mut [u8], | ||
| 223 | config: Config, | ||
| 224 | ) -> Result<Self> { | ||
| 225 | tx_pin.as_tx(); | ||
| 226 | rx_pin.as_rx(); | ||
| 227 | |||
| 228 | let (tx, rx) = Self::new_inner::<T>( | ||
| 229 | inner, | ||
| 230 | tx_pin.into(), | ||
| 231 | rx_pin.into(), | ||
| 232 | None, | ||
| 233 | None, | ||
| 234 | tx_buffer, | ||
| 235 | rx_buffer, | ||
| 236 | config, | ||
| 237 | )?; | ||
| 238 | |||
| 239 | Ok(Self { tx, rx }) | ||
| 240 | } | ||
| 241 | |||
| 242 | /// Create a new buffered LPUART instance with RTS/CTS flow control | ||
| 243 | pub fn new_with_rtscts<T: Instance>( | ||
| 244 | inner: Peri<'a, T>, | ||
| 245 | tx_pin: Peri<'a, impl TxPin<T>>, | ||
| 246 | rx_pin: Peri<'a, impl RxPin<T>>, | ||
| 247 | rts_pin: Peri<'a, impl RtsPin<T>>, | ||
| 248 | cts_pin: Peri<'a, impl CtsPin<T>>, | ||
| 249 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a, | ||
| 250 | tx_buffer: &'a mut [u8], | ||
| 251 | rx_buffer: &'a mut [u8], | ||
| 252 | config: Config, | ||
| 253 | ) -> Result<Self> { | ||
| 254 | tx_pin.as_tx(); | ||
| 255 | rx_pin.as_rx(); | ||
| 256 | rts_pin.as_rts(); | ||
| 257 | cts_pin.as_cts(); | ||
| 258 | |||
| 259 | let (tx, rx) = Self::new_inner::<T>( | ||
| 260 | inner, | ||
| 261 | tx_pin.into(), | ||
| 262 | rx_pin.into(), | ||
| 263 | Some(rts_pin.into()), | ||
| 264 | Some(cts_pin.into()), | ||
| 265 | tx_buffer, | ||
| 266 | rx_buffer, | ||
| 267 | config, | ||
| 268 | )?; | ||
| 269 | |||
| 270 | Ok(Self { tx, rx }) | ||
| 271 | } | ||
| 272 | |||
| 273 | /// Create a new buffered LPUART with only RTS flow control (RX flow control) | ||
| 274 | pub fn new_with_rts<T: Instance>( | ||
| 275 | inner: Peri<'a, T>, | ||
| 276 | tx_pin: Peri<'a, impl TxPin<T>>, | ||
| 277 | rx_pin: Peri<'a, impl RxPin<T>>, | ||
| 278 | rts_pin: Peri<'a, impl RtsPin<T>>, | ||
| 279 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a, | ||
| 280 | tx_buffer: &'a mut [u8], | ||
| 281 | rx_buffer: &'a mut [u8], | ||
| 282 | config: Config, | ||
| 283 | ) -> Result<Self> { | ||
| 284 | tx_pin.as_tx(); | ||
| 285 | rx_pin.as_rx(); | ||
| 286 | rts_pin.as_rts(); | ||
| 287 | |||
| 288 | let (tx, rx) = Self::new_inner::<T>( | ||
| 289 | inner, | ||
| 290 | tx_pin.into(), | ||
| 291 | rx_pin.into(), | ||
| 292 | Some(rts_pin.into()), | ||
| 293 | None, | ||
| 294 | tx_buffer, | ||
| 295 | rx_buffer, | ||
| 296 | config, | ||
| 297 | )?; | ||
| 298 | |||
| 299 | Ok(Self { tx, rx }) | ||
| 300 | } | ||
| 301 | |||
| 302 | /// Create a new buffered LPUART with only CTS flow control (TX flow control) | ||
| 303 | pub fn new_with_cts<T: Instance>( | ||
| 304 | inner: Peri<'a, T>, | ||
| 305 | tx_pin: Peri<'a, impl TxPin<T>>, | ||
| 306 | rx_pin: Peri<'a, impl RxPin<T>>, | ||
| 307 | cts_pin: Peri<'a, impl CtsPin<T>>, | ||
| 308 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a, | ||
| 309 | tx_buffer: &'a mut [u8], | ||
| 310 | rx_buffer: &'a mut [u8], | ||
| 311 | config: Config, | ||
| 312 | ) -> Result<Self> { | ||
| 313 | tx_pin.as_tx(); | ||
| 314 | rx_pin.as_rx(); | ||
| 315 | cts_pin.as_cts(); | ||
| 316 | |||
| 317 | let (tx, rx) = Self::new_inner::<T>( | ||
| 318 | inner, | ||
| 319 | tx_pin.into(), | ||
| 320 | rx_pin.into(), | ||
| 321 | None, | ||
| 322 | Some(cts_pin.into()), | ||
| 323 | tx_buffer, | ||
| 324 | rx_buffer, | ||
| 325 | config, | ||
| 326 | )?; | ||
| 327 | |||
| 328 | Ok(Self { tx, rx }) | ||
| 329 | } | ||
| 330 | |||
| 331 | /// Split the buffered LPUART into separate TX and RX parts | ||
| 332 | pub fn split(self) -> (BufferedLpuartTx<'a>, BufferedLpuartRx<'a>) { | ||
| 333 | (self.tx, self.rx) | ||
| 334 | } | ||
| 335 | |||
| 336 | /// Get mutable references to TX and RX parts | ||
| 337 | pub fn split_ref(&mut self) -> (&mut BufferedLpuartTx<'a>, &mut BufferedLpuartRx<'a>) { | ||
| 338 | (&mut self.tx, &mut self.rx) | ||
| 339 | } | ||
| 340 | } | ||
| 341 | |||
| 342 | // ============================================================================ | ||
| 343 | // BUFFERED TX IMPLEMENTATION | ||
| 344 | // ============================================================================ | ||
| 345 | |||
| 346 | impl<'a> BufferedLpuartTx<'a> { | ||
| 347 | /// Helper for TX-only initialization | ||
| 348 | fn new_inner<T: Instance>( | ||
| 349 | inner: Peri<'a, T>, | ||
| 350 | tx_pin: Peri<'a, AnyPin>, | ||
| 351 | cts_pin: Option<Peri<'a, AnyPin>>, | ||
| 352 | tx_buffer: &'a mut [u8], | ||
| 353 | config: Config, | ||
| 354 | ) -> Result<BufferedLpuartTx<'a>> { | ||
| 355 | let state = BufferedLpuart::init_common::<T>( | ||
| 356 | &inner, | ||
| 357 | Some(tx_buffer), | ||
| 358 | None, | ||
| 359 | &config, | ||
| 360 | true, | ||
| 361 | false, | ||
| 362 | false, | ||
| 363 | cts_pin.is_some(), | ||
| 364 | )?; | ||
| 365 | |||
| 366 | Ok(BufferedLpuartTx { | ||
| 367 | info: T::info(), | ||
| 368 | state, | ||
| 369 | _tx_pin: tx_pin, | ||
| 370 | _cts_pin: cts_pin, | ||
| 371 | }) | ||
| 372 | } | ||
| 373 | |||
| 374 | pub fn new<T: Instance>( | ||
| 375 | inner: Peri<'a, T>, | ||
| 376 | tx_pin: Peri<'a, impl TxPin<T>>, | ||
| 377 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a, | ||
| 378 | tx_buffer: &'a mut [u8], | ||
| 379 | config: Config, | ||
| 380 | ) -> Result<Self> { | ||
| 381 | tx_pin.as_tx(); | ||
| 382 | |||
| 383 | Self::new_inner::<T>(inner, tx_pin.into(), None, tx_buffer, config) | ||
| 384 | } | ||
| 385 | |||
| 386 | /// Create a new TX-only buffered LPUART with CTS flow control | ||
| 387 | pub fn new_with_cts<T: Instance>( | ||
| 388 | inner: Peri<'a, T>, | ||
| 389 | tx_pin: Peri<'a, impl TxPin<T>>, | ||
| 390 | cts_pin: Peri<'a, impl CtsPin<T>>, | ||
| 391 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a, | ||
| 392 | tx_buffer: &'a mut [u8], | ||
| 393 | config: Config, | ||
| 394 | ) -> Result<Self> { | ||
| 395 | tx_pin.as_tx(); | ||
| 396 | cts_pin.as_cts(); | ||
| 397 | |||
| 398 | Self::new_inner::<T>(inner, tx_pin.into(), Some(cts_pin.into()), tx_buffer, config) | ||
| 399 | } | ||
| 400 | } | ||
| 401 | |||
| 402 | impl<'a> BufferedLpuartTx<'a> { | ||
| 403 | /// Write data asynchronously | ||
| 404 | pub async fn write(&mut self, buf: &[u8]) -> Result<usize> { | ||
| 405 | let mut written = 0; | ||
| 406 | |||
| 407 | for &byte in buf { | ||
| 408 | // Wait for space in the buffer | ||
| 409 | poll_fn(|cx| { | ||
| 410 | self.state.tx_waker.register(cx.waker()); | ||
| 411 | |||
| 412 | let mut writer = unsafe { self.state.tx_buf.writer() }; | ||
| 413 | if writer.push_one(byte) { | ||
| 414 | // Enable TX interrupt to start transmission | ||
| 415 | cortex_m::interrupt::free(|_| { | ||
| 416 | self.info.regs.ctrl().modify(|_, w| w.tie().enabled()); | ||
| 417 | }); | ||
| 418 | Poll::Ready(Ok(())) | ||
| 419 | } else { | ||
| 420 | Poll::Pending | ||
| 421 | } | ||
| 422 | }) | ||
| 423 | .await?; | ||
| 424 | |||
| 425 | written += 1; | ||
| 426 | } | ||
| 427 | |||
| 428 | Ok(written) | ||
| 429 | } | ||
| 430 | |||
| 431 | /// Flush the TX buffer and wait for transmission to complete | ||
| 432 | pub async fn flush(&mut self) -> Result<()> { | ||
| 433 | // Wait for TX buffer to empty and transmission to complete | ||
| 434 | poll_fn(|cx| { | ||
| 435 | self.state.tx_waker.register(cx.waker()); | ||
| 436 | |||
| 437 | let tx_empty = self.state.tx_buf.is_empty(); | ||
| 438 | let fifo_empty = self.info.regs.water().read().txcount().bits() == 0; | ||
| 439 | let tc_complete = self.info.regs.stat().read().tc().is_complete(); | ||
| 440 | |||
| 441 | if tx_empty && fifo_empty && tc_complete { | ||
| 442 | Poll::Ready(Ok(())) | ||
| 443 | } else { | ||
| 444 | // Enable appropriate interrupt | ||
| 445 | cortex_m::interrupt::free(|_| { | ||
| 446 | if !tx_empty { | ||
| 447 | self.info.regs.ctrl().modify(|_, w| w.tie().enabled()); | ||
| 448 | } else { | ||
| 449 | self.info.regs.ctrl().modify(|_, w| w.tcie().enabled()); | ||
| 450 | } | ||
| 451 | }); | ||
| 452 | Poll::Pending | ||
| 453 | } | ||
| 454 | }) | ||
| 455 | .await | ||
| 456 | } | ||
| 457 | |||
| 458 | /// Try to write without blocking | ||
| 459 | pub fn try_write(&mut self, buf: &[u8]) -> Result<usize> { | ||
| 460 | let mut writer = unsafe { self.state.tx_buf.writer() }; | ||
| 461 | let mut written = 0; | ||
| 462 | |||
| 463 | for &byte in buf { | ||
| 464 | if writer.push_one(byte) { | ||
| 465 | written += 1; | ||
| 466 | } else { | ||
| 467 | break; | ||
| 468 | } | ||
| 469 | } | ||
| 470 | |||
| 471 | if written > 0 { | ||
| 472 | // Enable TX interrupt to start transmission | ||
| 473 | cortex_m::interrupt::free(|_| { | ||
| 474 | self.info.regs.ctrl().modify(|_, w| w.tie().enabled()); | ||
| 475 | }); | ||
| 476 | } | ||
| 477 | |||
| 478 | Ok(written) | ||
| 479 | } | ||
| 480 | } | ||
| 481 | |||
| 482 | // ============================================================================ | ||
| 483 | // BUFFERED RX IMPLEMENTATION | ||
| 484 | // ============================================================================ | ||
| 485 | |||
| 486 | impl<'a> BufferedLpuartRx<'a> { | ||
| 487 | /// Helper for RX-only initialization | ||
| 488 | fn new_inner<T: Instance>( | ||
| 489 | inner: Peri<'a, T>, | ||
| 490 | rx_pin: Peri<'a, AnyPin>, | ||
| 491 | rts_pin: Option<Peri<'a, AnyPin>>, | ||
| 492 | rx_buffer: &'a mut [u8], | ||
| 493 | config: Config, | ||
| 494 | ) -> Result<BufferedLpuartRx<'a>> { | ||
| 495 | let state = BufferedLpuart::init_common::<T>( | ||
| 496 | &inner, | ||
| 497 | None, | ||
| 498 | Some(rx_buffer), | ||
| 499 | &config, | ||
| 500 | false, | ||
| 501 | true, | ||
| 502 | rts_pin.is_some(), | ||
| 503 | false, | ||
| 504 | )?; | ||
| 505 | |||
| 506 | Ok(BufferedLpuartRx { | ||
| 507 | info: T::info(), | ||
| 508 | state, | ||
| 509 | _rx_pin: rx_pin, | ||
| 510 | _rts_pin: rts_pin, | ||
| 511 | }) | ||
| 512 | } | ||
| 513 | |||
| 514 | /// Create a new RX-only buffered LPUART | ||
| 515 | pub fn new<T: Instance>( | ||
| 516 | inner: Peri<'a, T>, | ||
| 517 | rx_pin: Peri<'a, impl RxPin<T>>, | ||
| 518 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a, | ||
| 519 | rx_buffer: &'a mut [u8], | ||
| 520 | config: Config, | ||
| 521 | ) -> Result<Self> { | ||
| 522 | rx_pin.as_rx(); | ||
| 523 | |||
| 524 | Self::new_inner::<T>(inner, rx_pin.into(), None, rx_buffer, config) | ||
| 525 | } | ||
| 526 | |||
| 527 | /// Create a new RX-only buffered LPUART with RTS flow control | ||
| 528 | pub fn new_with_rts<T: Instance>( | ||
| 529 | inner: Peri<'a, T>, | ||
| 530 | rx_pin: Peri<'a, impl RxPin<T>>, | ||
| 531 | rts_pin: Peri<'a, impl RtsPin<T>>, | ||
| 532 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a, | ||
| 533 | rx_buffer: &'a mut [u8], | ||
| 534 | config: Config, | ||
| 535 | ) -> Result<Self> { | ||
| 536 | rx_pin.as_rx(); | ||
| 537 | rts_pin.as_rts(); | ||
| 538 | |||
| 539 | Self::new_inner::<T>(inner, rx_pin.into(), Some(rts_pin.into()), rx_buffer, config) | ||
| 540 | } | ||
| 541 | } | ||
| 542 | |||
| 543 | impl<'a> BufferedLpuartRx<'a> { | ||
| 544 | /// Read data asynchronously | ||
| 545 | pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize> { | ||
| 546 | if buf.is_empty() { | ||
| 547 | return Ok(0); | ||
| 548 | } | ||
| 549 | |||
| 550 | let mut read = 0; | ||
| 551 | |||
| 552 | // Try to read available data | ||
| 553 | poll_fn(|cx| { | ||
| 554 | self.state.rx_waker.register(cx.waker()); | ||
| 555 | |||
| 556 | // Disable RX interrupt while reading from buffer | ||
| 557 | cortex_m::interrupt::free(|_| { | ||
| 558 | self.info.regs.ctrl().modify(|_, w| w.rie().disabled()); | ||
| 559 | }); | ||
| 560 | |||
| 561 | let mut reader = unsafe { self.state.rx_buf.reader() }; | ||
| 562 | let available = reader.pop(|data| { | ||
| 563 | let to_copy = core::cmp::min(data.len(), buf.len() - read); | ||
| 564 | if to_copy > 0 { | ||
| 565 | buf[read..read + to_copy].copy_from_slice(&data[..to_copy]); | ||
| 566 | read += to_copy; | ||
| 567 | } | ||
| 568 | to_copy | ||
| 569 | }); | ||
| 570 | |||
| 571 | // Re-enable RX interrupt | ||
| 572 | cortex_m::interrupt::free(|_| { | ||
| 573 | self.info.regs.ctrl().modify(|_, w| w.rie().enabled()); | ||
| 574 | }); | ||
| 575 | |||
| 576 | if read > 0 { | ||
| 577 | Poll::Ready(Ok(read)) | ||
| 578 | } else if available == 0 { | ||
| 579 | Poll::Pending | ||
| 580 | } else { | ||
| 581 | Poll::Ready(Ok(0)) | ||
| 582 | } | ||
| 583 | }) | ||
| 584 | .await | ||
| 585 | } | ||
| 586 | |||
| 587 | /// Try to read without blocking | ||
| 588 | pub fn try_read(&mut self, buf: &mut [u8]) -> Result<usize> { | ||
| 589 | if buf.is_empty() { | ||
| 590 | return Ok(0); | ||
| 591 | } | ||
| 592 | |||
| 593 | // Disable RX interrupt while reading from buffer | ||
| 594 | cortex_m::interrupt::free(|_| { | ||
| 595 | self.info.regs.ctrl().modify(|_, w| w.rie().disabled()); | ||
| 596 | }); | ||
| 597 | |||
| 598 | let mut reader = unsafe { self.state.rx_buf.reader() }; | ||
| 599 | let read = reader.pop(|data| { | ||
| 600 | let to_copy = core::cmp::min(data.len(), buf.len()); | ||
| 601 | if to_copy > 0 { | ||
| 602 | buf[..to_copy].copy_from_slice(&data[..to_copy]); | ||
| 603 | } | ||
| 604 | to_copy | ||
| 605 | }); | ||
| 606 | |||
| 607 | // Re-enable RX interrupt | ||
| 608 | cortex_m::interrupt::free(|_| { | ||
| 609 | self.info.regs.ctrl().modify(|_, w| w.rie().enabled()); | ||
| 610 | }); | ||
| 611 | |||
| 612 | Ok(read) | ||
| 613 | } | ||
| 614 | } | ||
| 615 | |||
| 616 | // ============================================================================ | ||
| 617 | // INTERRUPT HANDLER | ||
| 618 | // ============================================================================ | ||
| 619 | |||
| 620 | /// Buffered UART interrupt handler | ||
| 621 | pub struct BufferedInterruptHandler<T: Instance> { | ||
| 622 | _phantom: PhantomData<T>, | ||
| 623 | } | ||
| 624 | |||
| 625 | impl<T: Instance> crate::interrupt::typelevel::Handler<T::Interrupt> for BufferedInterruptHandler<T> { | ||
| 626 | unsafe fn on_interrupt() { | ||
| 627 | let regs = T::info().regs; | ||
| 628 | let state = T::buffered_state(); | ||
| 629 | |||
| 630 | // Check if this instance is initialized | ||
| 631 | if !state.initialized.load(Ordering::Relaxed) { | ||
| 632 | return; | ||
| 633 | } | ||
| 634 | |||
| 635 | let ctrl = regs.ctrl().read(); | ||
| 636 | let stat = regs.stat().read(); | ||
| 637 | let has_fifo = regs.param().read().rxfifo().bits() > 0; | ||
| 638 | |||
| 639 | // Handle overrun error | ||
| 640 | if stat.or().is_overrun() { | ||
| 641 | regs.stat().write(|w| w.or().clear_bit_by_one()); | ||
| 642 | state.rx_waker.wake(); | ||
| 643 | return; | ||
| 644 | } | ||
| 645 | |||
| 646 | // Clear other error flags | ||
| 647 | if stat.pf().is_parity() { | ||
| 648 | regs.stat().write(|w| w.pf().clear_bit_by_one()); | ||
| 649 | } | ||
| 650 | if stat.fe().is_error() { | ||
| 651 | regs.stat().write(|w| w.fe().clear_bit_by_one()); | ||
| 652 | } | ||
| 653 | if stat.nf().is_noise() { | ||
| 654 | regs.stat().write(|w| w.nf().clear_bit_by_one()); | ||
| 655 | } | ||
| 656 | |||
| 657 | // Handle RX data | ||
| 658 | if ctrl.rie().is_enabled() && (has_data(regs) || stat.idle().is_idle()) { | ||
| 659 | let mut pushed_any = false; | ||
| 660 | let mut writer = state.rx_buf.writer(); | ||
| 661 | |||
| 662 | if has_fifo { | ||
| 663 | // Read from FIFO | ||
| 664 | while regs.water().read().rxcount().bits() > 0 { | ||
| 665 | let byte = (regs.data().read().bits() & 0xFF) as u8; | ||
| 666 | if writer.push_one(byte) { | ||
| 667 | pushed_any = true; | ||
| 668 | } else { | ||
| 669 | // Buffer full, stop reading | ||
| 670 | break; | ||
| 671 | } | ||
| 672 | } | ||
| 673 | } else { | ||
| 674 | // Read single byte | ||
| 675 | if regs.stat().read().rdrf().is_rxdata() { | ||
| 676 | let byte = (regs.data().read().bits() & 0xFF) as u8; | ||
| 677 | if writer.push_one(byte) { | ||
| 678 | pushed_any = true; | ||
| 679 | } | ||
| 680 | } | ||
| 681 | } | ||
| 682 | |||
| 683 | if pushed_any { | ||
| 684 | state.rx_waker.wake(); | ||
| 685 | } | ||
| 686 | |||
| 687 | // Clear idle flag if set | ||
| 688 | if stat.idle().is_idle() { | ||
| 689 | regs.stat().write(|w| w.idle().clear_bit_by_one()); | ||
| 690 | } | ||
| 691 | } | ||
| 692 | |||
| 693 | // Handle TX data | ||
| 694 | if ctrl.tie().is_enabled() { | ||
| 695 | let mut sent_any = false; | ||
| 696 | let mut reader = state.tx_buf.reader(); | ||
| 697 | |||
| 698 | // Send data while TX buffer is ready and we have data | ||
| 699 | while regs.stat().read().tdre().is_no_txdata() { | ||
| 700 | if let Some(byte) = reader.pop_one() { | ||
| 701 | regs.data().write(|w| w.bits(u32::from(byte))); | ||
| 702 | sent_any = true; | ||
| 703 | } else { | ||
| 704 | // No more data to send | ||
| 705 | break; | ||
| 706 | } | ||
| 707 | } | ||
| 708 | |||
| 709 | if sent_any { | ||
| 710 | state.tx_waker.wake(); | ||
| 711 | } | ||
| 712 | |||
| 713 | // If buffer is empty, switch to TC interrupt or disable | ||
| 714 | if state.tx_buf.is_empty() { | ||
| 715 | cortex_m::interrupt::free(|_| { | ||
| 716 | regs.ctrl().modify(|_, w| w.tie().disabled().tcie().enabled()); | ||
| 717 | }); | ||
| 718 | } | ||
| 719 | } | ||
| 720 | |||
| 721 | // Handle transmission complete | ||
| 722 | if ctrl.tcie().is_enabled() && regs.stat().read().tc().is_complete() { | ||
| 723 | state.tx_done.store(true, Ordering::Release); | ||
| 724 | state.tx_waker.wake(); | ||
| 725 | |||
| 726 | // Disable TC interrupt | ||
| 727 | cortex_m::interrupt::free(|_| { | ||
| 728 | regs.ctrl().modify(|_, w| w.tcie().disabled()); | ||
| 729 | }); | ||
| 730 | } | ||
| 731 | } | ||
| 732 | } | ||
| 733 | |||
| 734 | // ============================================================================ | ||
| 735 | // EMBEDDED-IO ASYNC TRAIT IMPLEMENTATIONS | ||
| 736 | // ============================================================================ | ||
| 737 | |||
| 738 | impl embedded_io_async::ErrorType for BufferedLpuartTx<'_> { | ||
| 739 | type Error = Error; | ||
| 740 | } | ||
| 741 | |||
| 742 | impl embedded_io_async::ErrorType for BufferedLpuartRx<'_> { | ||
| 743 | type Error = Error; | ||
| 744 | } | ||
| 745 | |||
| 746 | impl embedded_io_async::ErrorType for BufferedLpuart<'_> { | ||
| 747 | type Error = Error; | ||
| 748 | } | ||
| 749 | |||
| 750 | impl embedded_io_async::Write for BufferedLpuartTx<'_> { | ||
| 751 | async fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> { | ||
| 752 | self.write(buf).await | ||
| 753 | } | ||
| 754 | |||
| 755 | async fn flush(&mut self) -> core::result::Result<(), Self::Error> { | ||
| 756 | self.flush().await | ||
| 757 | } | ||
| 758 | } | ||
| 759 | |||
| 760 | impl embedded_io_async::Read for BufferedLpuartRx<'_> { | ||
| 761 | async fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> { | ||
| 762 | self.read(buf).await | ||
| 763 | } | ||
| 764 | } | ||
| 765 | |||
| 766 | impl embedded_io_async::Write for BufferedLpuart<'_> { | ||
| 767 | async fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> { | ||
| 768 | self.tx.write(buf).await | ||
| 769 | } | ||
| 770 | |||
| 771 | async fn flush(&mut self) -> core::result::Result<(), Self::Error> { | ||
| 772 | self.tx.flush().await | ||
| 773 | } | ||
| 774 | } | ||
| 775 | |||
| 776 | impl embedded_io_async::Read for BufferedLpuart<'_> { | ||
| 777 | async fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> { | ||
| 778 | self.rx.read(buf).await | ||
| 779 | } | ||
| 780 | } | ||
diff --git a/embassy-mcxa/src/lpuart/mod.rs b/embassy-mcxa/src/lpuart/mod.rs new file mode 100644 index 000000000..b8a2d5172 --- /dev/null +++ b/embassy-mcxa/src/lpuart/mod.rs | |||
| @@ -0,0 +1,1292 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | |||
| 3 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 4 | use paste::paste; | ||
| 5 | |||
| 6 | use crate::clocks::periph_helpers::{Div4, LpuartClockSel, LpuartConfig}; | ||
| 7 | use crate::clocks::{ClockError, Gate, PoweredClock, enable_and_reset}; | ||
| 8 | use crate::gpio::SealedPin; | ||
| 9 | use crate::pac::lpuart0::baud::Sbns as StopBits; | ||
| 10 | use crate::pac::lpuart0::ctrl::{Idlecfg as IdleConfig, Ilt as IdleType, M as DataBits, Pt as Parity}; | ||
| 11 | use crate::pac::lpuart0::modir::{Txctsc as TxCtsConfig, Txctssrc as TxCtsSource}; | ||
| 12 | use crate::pac::lpuart0::stat::Msbf as MsbFirst; | ||
| 13 | use crate::{AnyPin, interrupt, pac}; | ||
| 14 | |||
| 15 | pub mod buffered; | ||
| 16 | |||
| 17 | // ============================================================================ | ||
| 18 | // STUB IMPLEMENTATION | ||
| 19 | // ============================================================================ | ||
| 20 | |||
| 21 | // Stub implementation for LIB (Peripherals), GPIO, DMA and CLOCK until stable API | ||
| 22 | // Pin and Clock initialization is currently done at the examples level. | ||
| 23 | |||
| 24 | // --- START DMA --- | ||
| 25 | mod dma { | ||
| 26 | pub struct Channel<'d> { | ||
| 27 | pub(super) _lifetime: core::marker::PhantomData<&'d ()>, | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | use dma::Channel; | ||
| 32 | |||
| 33 | // --- END DMA --- | ||
| 34 | |||
| 35 | // ============================================================================ | ||
| 36 | // MISC | ||
| 37 | // ============================================================================ | ||
| 38 | |||
| 39 | mod sealed { | ||
| 40 | /// Simply seal a trait to prevent external implementations | ||
| 41 | pub trait Sealed {} | ||
| 42 | } | ||
| 43 | |||
| 44 | // ============================================================================ | ||
| 45 | // INSTANCE TRAIT | ||
| 46 | // ============================================================================ | ||
| 47 | |||
| 48 | pub type Regs = &'static crate::pac::lpuart0::RegisterBlock; | ||
| 49 | |||
| 50 | pub trait SealedInstance { | ||
| 51 | fn info() -> Info; | ||
| 52 | fn index() -> usize; | ||
| 53 | fn buffered_state() -> &'static buffered::State; | ||
| 54 | } | ||
| 55 | |||
| 56 | pub struct Info { | ||
| 57 | pub regs: Regs, | ||
| 58 | } | ||
| 59 | |||
| 60 | /// Trait for LPUART peripheral instances | ||
| 61 | #[allow(private_bounds)] | ||
| 62 | pub trait Instance: SealedInstance + PeripheralType + 'static + Send + Gate<MrccPeriphConfig = LpuartConfig> { | ||
| 63 | const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance; | ||
| 64 | type Interrupt: interrupt::typelevel::Interrupt; | ||
| 65 | } | ||
| 66 | |||
| 67 | macro_rules! impl_instance { | ||
| 68 | ($($n:expr),*) => { | ||
| 69 | $( | ||
| 70 | paste!{ | ||
| 71 | impl SealedInstance for crate::peripherals::[<LPUART $n>] { | ||
| 72 | fn info() -> Info { | ||
| 73 | Info { | ||
| 74 | regs: unsafe { &*pac::[<Lpuart $n>]::ptr() }, | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | #[inline] | ||
| 79 | fn index() -> usize { | ||
| 80 | $n | ||
| 81 | } | ||
| 82 | |||
| 83 | fn buffered_state() -> &'static buffered::State { | ||
| 84 | static BUFFERED_STATE: buffered::State = buffered::State::new(); | ||
| 85 | &BUFFERED_STATE | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | impl Instance for crate::peripherals::[<LPUART $n>] { | ||
| 90 | const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance | ||
| 91 | = crate::clocks::periph_helpers::LpuartInstance::[<Lpuart $n>]; | ||
| 92 | type Interrupt = crate::interrupt::typelevel::[<LPUART $n>]; | ||
| 93 | } | ||
| 94 | } | ||
| 95 | )* | ||
| 96 | }; | ||
| 97 | } | ||
| 98 | |||
| 99 | impl_instance!(0, 1, 2, 3, 4, 5); | ||
| 100 | |||
| 101 | // ============================================================================ | ||
| 102 | // INSTANCE HELPER FUNCTIONS | ||
| 103 | // ============================================================================ | ||
| 104 | |||
| 105 | /// Perform software reset on the LPUART peripheral | ||
| 106 | pub fn perform_software_reset(regs: Regs) { | ||
| 107 | // Software reset - set and clear RST bit (Global register) | ||
| 108 | regs.global().write(|w| w.rst().reset()); | ||
| 109 | regs.global().write(|w| w.rst().no_effect()); | ||
| 110 | } | ||
| 111 | |||
| 112 | /// Disable both transmitter and receiver | ||
| 113 | pub fn disable_transceiver(regs: Regs) { | ||
| 114 | regs.ctrl().modify(|_, w| w.te().disabled().re().disabled()); | ||
| 115 | } | ||
| 116 | |||
| 117 | /// Calculate and configure baudrate settings | ||
| 118 | pub fn configure_baudrate(regs: Regs, baudrate_bps: u32, clock_freq: u32) -> Result<()> { | ||
| 119 | let (osr, sbr) = calculate_baudrate(baudrate_bps, clock_freq)?; | ||
| 120 | |||
| 121 | // Configure BAUD register | ||
| 122 | regs.baud().modify(|_, w| unsafe { | ||
| 123 | // Clear and set OSR | ||
| 124 | w.osr().bits(osr - 1); | ||
| 125 | // Clear and set SBR | ||
| 126 | w.sbr().bits(sbr); | ||
| 127 | // Set BOTHEDGE if OSR is between 4 and 7 | ||
| 128 | if osr > 3 && osr < 8 { | ||
| 129 | w.bothedge().enabled() | ||
| 130 | } else { | ||
| 131 | w.bothedge().disabled() | ||
| 132 | } | ||
| 133 | }); | ||
| 134 | |||
| 135 | Ok(()) | ||
| 136 | } | ||
| 137 | |||
| 138 | /// Configure frame format (stop bits, data bits) | ||
| 139 | pub fn configure_frame_format(regs: Regs, config: &Config) { | ||
| 140 | // Configure stop bits | ||
| 141 | regs.baud().modify(|_, w| w.sbns().variant(config.stop_bits_count)); | ||
| 142 | |||
| 143 | // Clear M10 for now (10-bit mode) | ||
| 144 | regs.baud().modify(|_, w| w.m10().disabled()); | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Configure control settings (parity, data bits, idle config, pin swap) | ||
| 148 | pub fn configure_control_settings(regs: Regs, config: &Config) { | ||
| 149 | regs.ctrl().modify(|_, w| { | ||
| 150 | // Parity configuration | ||
| 151 | let mut w = if let Some(parity) = config.parity_mode { | ||
| 152 | w.pe().enabled().pt().variant(parity) | ||
| 153 | } else { | ||
| 154 | w.pe().disabled() | ||
| 155 | }; | ||
| 156 | |||
| 157 | // Data bits configuration | ||
| 158 | w = match config.data_bits_count { | ||
| 159 | DataBits::Data8 => { | ||
| 160 | if config.parity_mode.is_some() { | ||
| 161 | w.m().data9() // 8 data + 1 parity = 9 bits | ||
| 162 | } else { | ||
| 163 | w.m().data8() // 8 data bits only | ||
| 164 | } | ||
| 165 | } | ||
| 166 | DataBits::Data9 => w.m().data9(), | ||
| 167 | }; | ||
| 168 | |||
| 169 | // Idle configuration | ||
| 170 | w = w.idlecfg().variant(config.rx_idle_config); | ||
| 171 | w = w.ilt().variant(config.rx_idle_type); | ||
| 172 | |||
| 173 | // Swap TXD/RXD if configured | ||
| 174 | if config.swap_txd_rxd { | ||
| 175 | w.swap().swap() | ||
| 176 | } else { | ||
| 177 | w.swap().standard() | ||
| 178 | } | ||
| 179 | }); | ||
| 180 | } | ||
| 181 | |||
| 182 | /// Configure FIFO settings and watermarks | ||
| 183 | pub fn configure_fifo(regs: Regs, config: &Config) { | ||
| 184 | // Configure WATER register for FIFO watermarks | ||
| 185 | regs.water().write(|w| unsafe { | ||
| 186 | w.rxwater() | ||
| 187 | .bits(config.rx_fifo_watermark) | ||
| 188 | .txwater() | ||
| 189 | .bits(config.tx_fifo_watermark) | ||
| 190 | }); | ||
| 191 | |||
| 192 | // Enable TX/RX FIFOs | ||
| 193 | regs.fifo().modify(|_, w| w.txfe().enabled().rxfe().enabled()); | ||
| 194 | |||
| 195 | // Flush FIFOs | ||
| 196 | regs.fifo() | ||
| 197 | .modify(|_, w| w.txflush().txfifo_rst().rxflush().rxfifo_rst()); | ||
| 198 | } | ||
| 199 | |||
| 200 | /// Clear all status flags | ||
| 201 | pub fn clear_all_status_flags(regs: Regs) { | ||
| 202 | regs.stat().reset(); | ||
| 203 | } | ||
| 204 | |||
| 205 | /// Configure hardware flow control if enabled | ||
| 206 | pub fn configure_flow_control(regs: Regs, enable_tx_cts: bool, enable_rx_rts: bool, config: &Config) { | ||
| 207 | if enable_rx_rts || enable_tx_cts { | ||
| 208 | regs.modir().modify(|_, w| { | ||
| 209 | let mut w = w; | ||
| 210 | |||
| 211 | // Configure TX CTS | ||
| 212 | w = w.txctsc().variant(config.tx_cts_config); | ||
| 213 | w = w.txctssrc().variant(config.tx_cts_source); | ||
| 214 | |||
| 215 | if enable_rx_rts { | ||
| 216 | w = w.rxrtse().enabled(); | ||
| 217 | } else { | ||
| 218 | w = w.rxrtse().disabled(); | ||
| 219 | } | ||
| 220 | |||
| 221 | if enable_tx_cts { | ||
| 222 | w = w.txctse().enabled(); | ||
| 223 | } else { | ||
| 224 | w = w.txctse().disabled(); | ||
| 225 | } | ||
| 226 | |||
| 227 | w | ||
| 228 | }); | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | /// Configure bit order (MSB first or LSB first) | ||
| 233 | pub fn configure_bit_order(regs: Regs, msb_first: MsbFirst) { | ||
| 234 | regs.stat().modify(|_, w| w.msbf().variant(msb_first)); | ||
| 235 | } | ||
| 236 | |||
| 237 | /// Enable transmitter and/or receiver based on configuration | ||
| 238 | pub fn enable_transceiver(regs: Regs, enable_tx: bool, enable_rx: bool) { | ||
| 239 | regs.ctrl().modify(|_, w| { | ||
| 240 | let mut w = w; | ||
| 241 | if enable_tx { | ||
| 242 | w = w.te().enabled(); | ||
| 243 | } | ||
| 244 | if enable_rx { | ||
| 245 | w = w.re().enabled(); | ||
| 246 | } | ||
| 247 | w | ||
| 248 | }); | ||
| 249 | } | ||
| 250 | |||
| 251 | pub fn calculate_baudrate(baudrate: u32, src_clock_hz: u32) -> Result<(u8, u16)> { | ||
| 252 | let mut baud_diff = baudrate; | ||
| 253 | let mut osr = 0u8; | ||
| 254 | let mut sbr = 0u16; | ||
| 255 | |||
| 256 | // Try OSR values from 4 to 32 | ||
| 257 | for osr_temp in 4u8..=32u8 { | ||
| 258 | // Calculate SBR: (srcClock_Hz * 2 / (baudRate * osr) + 1) / 2 | ||
| 259 | let sbr_calc = ((src_clock_hz * 2) / (baudrate * osr_temp as u32)).div_ceil(2); | ||
| 260 | |||
| 261 | let sbr_temp = if sbr_calc == 0 { | ||
| 262 | 1 | ||
| 263 | } else if sbr_calc > 0x1FFF { | ||
| 264 | 0x1FFF | ||
| 265 | } else { | ||
| 266 | sbr_calc as u16 | ||
| 267 | }; | ||
| 268 | |||
| 269 | // Calculate actual baud rate | ||
| 270 | let calculated_baud = src_clock_hz / (osr_temp as u32 * sbr_temp as u32); | ||
| 271 | |||
| 272 | let temp_diff = calculated_baud.abs_diff(baudrate); | ||
| 273 | |||
| 274 | if temp_diff <= baud_diff { | ||
| 275 | baud_diff = temp_diff; | ||
| 276 | osr = osr_temp; | ||
| 277 | sbr = sbr_temp; | ||
| 278 | } | ||
| 279 | } | ||
| 280 | |||
| 281 | // Check if baud rate difference is within 3% | ||
| 282 | if baud_diff > (baudrate / 100) * 3 { | ||
| 283 | return Err(Error::UnsupportedBaudrate); | ||
| 284 | } | ||
| 285 | |||
| 286 | Ok((osr, sbr)) | ||
| 287 | } | ||
| 288 | |||
| 289 | /// Wait for all transmit operations to complete | ||
| 290 | pub fn wait_for_tx_complete(regs: Regs) { | ||
| 291 | // Wait for TX FIFO to empty | ||
| 292 | while regs.water().read().txcount().bits() != 0 { | ||
| 293 | // Wait for TX FIFO to drain | ||
| 294 | } | ||
| 295 | |||
| 296 | // Wait for last character to shift out (TC = Transmission Complete) | ||
| 297 | while regs.stat().read().tc().is_active() { | ||
| 298 | // Wait for transmission to complete | ||
| 299 | } | ||
| 300 | } | ||
| 301 | |||
| 302 | pub fn check_and_clear_rx_errors(regs: Regs) -> Result<()> { | ||
| 303 | let stat = regs.stat().read(); | ||
| 304 | let mut status = Ok(()); | ||
| 305 | |||
| 306 | // Check for overrun first - other error flags are prevented when OR is set | ||
| 307 | if stat.or().is_overrun() { | ||
| 308 | regs.stat().write(|w| w.or().clear_bit_by_one()); | ||
| 309 | |||
| 310 | return Err(Error::Overrun); | ||
| 311 | } | ||
| 312 | |||
| 313 | if stat.pf().is_parity() { | ||
| 314 | regs.stat().write(|w| w.pf().clear_bit_by_one()); | ||
| 315 | status = Err(Error::Parity); | ||
| 316 | } | ||
| 317 | |||
| 318 | if stat.fe().is_error() { | ||
| 319 | regs.stat().write(|w| w.fe().clear_bit_by_one()); | ||
| 320 | status = Err(Error::Framing); | ||
| 321 | } | ||
| 322 | |||
| 323 | if stat.nf().is_noise() { | ||
| 324 | regs.stat().write(|w| w.nf().clear_bit_by_one()); | ||
| 325 | status = Err(Error::Noise); | ||
| 326 | } | ||
| 327 | |||
| 328 | status | ||
| 329 | } | ||
| 330 | |||
| 331 | pub fn has_data(regs: Regs) -> bool { | ||
| 332 | if regs.param().read().rxfifo().bits() > 0 { | ||
| 333 | // FIFO is available - check RXCOUNT in WATER register | ||
| 334 | regs.water().read().rxcount().bits() > 0 | ||
| 335 | } else { | ||
| 336 | // No FIFO - check RDRF flag in STAT register | ||
| 337 | regs.stat().read().rdrf().is_rxdata() | ||
| 338 | } | ||
| 339 | } | ||
| 340 | |||
| 341 | // ============================================================================ | ||
| 342 | // PIN TRAITS FOR LPUART FUNCTIONALITY | ||
| 343 | // ============================================================================ | ||
| 344 | |||
| 345 | impl<T: SealedPin> sealed::Sealed for T {} | ||
| 346 | |||
| 347 | /// io configuration trait for Lpuart Tx configuration | ||
| 348 | pub trait TxPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType { | ||
| 349 | /// convert the pin to appropriate function for Lpuart Tx usage | ||
| 350 | fn as_tx(&self); | ||
| 351 | } | ||
| 352 | |||
| 353 | /// io configuration trait for Lpuart Rx configuration | ||
| 354 | pub trait RxPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType { | ||
| 355 | /// convert the pin to appropriate function for Lpuart Rx usage | ||
| 356 | fn as_rx(&self); | ||
| 357 | } | ||
| 358 | |||
| 359 | /// io configuration trait for Lpuart Cts | ||
| 360 | pub trait CtsPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType { | ||
| 361 | /// convert the pin to appropriate function for Lpuart Cts usage | ||
| 362 | fn as_cts(&self); | ||
| 363 | } | ||
| 364 | |||
| 365 | /// io configuration trait for Lpuart Rts | ||
| 366 | pub trait RtsPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType { | ||
| 367 | /// convert the pin to appropriate function for Lpuart Rts usage | ||
| 368 | fn as_rts(&self); | ||
| 369 | } | ||
| 370 | |||
| 371 | macro_rules! impl_tx_pin { | ||
| 372 | ($inst:ident, $pin:ident, $alt:ident) => { | ||
| 373 | impl TxPin<crate::peripherals::$inst> for crate::peripherals::$pin { | ||
| 374 | fn as_tx(&self) { | ||
| 375 | // TODO: Check these are right | ||
| 376 | self.set_pull(crate::gpio::Pull::Up); | ||
| 377 | self.set_slew_rate(crate::gpio::SlewRate::Fast.into()); | ||
| 378 | self.set_drive_strength(crate::gpio::DriveStrength::Double.into()); | ||
| 379 | self.set_function(crate::pac::port0::pcr0::Mux::$alt); | ||
| 380 | self.set_enable_input_buffer(); | ||
| 381 | } | ||
| 382 | } | ||
| 383 | }; | ||
| 384 | } | ||
| 385 | |||
| 386 | macro_rules! impl_rx_pin { | ||
| 387 | ($inst:ident, $pin:ident, $alt:ident) => { | ||
| 388 | impl RxPin<crate::peripherals::$inst> for crate::peripherals::$pin { | ||
| 389 | fn as_rx(&self) { | ||
| 390 | // TODO: Check these are right | ||
| 391 | self.set_pull(crate::gpio::Pull::Up); | ||
| 392 | self.set_slew_rate(crate::gpio::SlewRate::Fast.into()); | ||
| 393 | self.set_drive_strength(crate::gpio::DriveStrength::Double.into()); | ||
| 394 | self.set_function(crate::pac::port0::pcr0::Mux::$alt); | ||
| 395 | self.set_enable_input_buffer(); | ||
| 396 | } | ||
| 397 | } | ||
| 398 | }; | ||
| 399 | } | ||
| 400 | |||
| 401 | // TODO: Macro and impls for CTS/RTS pins | ||
| 402 | macro_rules! impl_cts_pin { | ||
| 403 | ($inst:ident, $pin:ident, $alt:ident) => { | ||
| 404 | impl CtsPin<crate::peripherals::$inst> for crate::peripherals::$pin { | ||
| 405 | fn as_cts(&self) { | ||
| 406 | todo!() | ||
| 407 | } | ||
| 408 | } | ||
| 409 | }; | ||
| 410 | } | ||
| 411 | |||
| 412 | macro_rules! impl_rts_pin { | ||
| 413 | ($inst:ident, $pin:ident, $alt:ident) => { | ||
| 414 | impl RtsPin<crate::peripherals::$inst> for crate::peripherals::$pin { | ||
| 415 | fn as_rts(&self) { | ||
| 416 | todo!() | ||
| 417 | } | ||
| 418 | } | ||
| 419 | }; | ||
| 420 | } | ||
| 421 | |||
| 422 | // LPUART 0 | ||
| 423 | impl_tx_pin!(LPUART0, P0_3, Mux2); | ||
| 424 | impl_tx_pin!(LPUART0, P0_21, Mux3); | ||
| 425 | impl_tx_pin!(LPUART0, P2_1, Mux2); | ||
| 426 | |||
| 427 | impl_rx_pin!(LPUART0, P0_2, Mux2); | ||
| 428 | impl_rx_pin!(LPUART0, P0_20, Mux3); | ||
| 429 | impl_rx_pin!(LPUART0, P2_0, Mux2); | ||
| 430 | |||
| 431 | impl_cts_pin!(LPUART0, P0_1, Mux2); | ||
| 432 | impl_cts_pin!(LPUART0, P0_23, Mux3); | ||
| 433 | impl_cts_pin!(LPUART0, P2_3, Mux2); | ||
| 434 | |||
| 435 | impl_rts_pin!(LPUART0, P0_0, Mux2); | ||
| 436 | impl_rts_pin!(LPUART0, P0_22, Mux3); | ||
| 437 | impl_rts_pin!(LPUART0, P2_2, Mux2); | ||
| 438 | |||
| 439 | // LPUART 1 | ||
| 440 | impl_tx_pin!(LPUART1, P1_9, Mux2); | ||
| 441 | impl_tx_pin!(LPUART1, P2_13, Mux3); | ||
| 442 | impl_tx_pin!(LPUART1, P3_9, Mux3); | ||
| 443 | impl_tx_pin!(LPUART1, P3_21, Mux3); | ||
| 444 | |||
| 445 | impl_rx_pin!(LPUART1, P1_8, Mux2); | ||
| 446 | impl_rx_pin!(LPUART1, P2_12, Mux3); | ||
| 447 | impl_rx_pin!(LPUART1, P3_8, Mux3); | ||
| 448 | impl_rx_pin!(LPUART1, P3_20, Mux3); | ||
| 449 | |||
| 450 | impl_cts_pin!(LPUART1, P1_11, Mux2); | ||
| 451 | impl_cts_pin!(LPUART1, P2_17, Mux3); | ||
| 452 | impl_cts_pin!(LPUART1, P3_11, Mux3); | ||
| 453 | impl_cts_pin!(LPUART1, P3_23, Mux3); | ||
| 454 | |||
| 455 | impl_rts_pin!(LPUART1, P1_10, Mux2); | ||
| 456 | impl_rts_pin!(LPUART1, P2_15, Mux3); | ||
| 457 | impl_rts_pin!(LPUART1, P2_16, Mux3); | ||
| 458 | impl_rts_pin!(LPUART1, P3_10, Mux3); | ||
| 459 | |||
| 460 | // LPUART 2 | ||
| 461 | impl_tx_pin!(LPUART2, P1_5, Mux3); | ||
| 462 | impl_tx_pin!(LPUART2, P1_13, Mux3); | ||
| 463 | impl_tx_pin!(LPUART2, P2_2, Mux3); | ||
| 464 | impl_tx_pin!(LPUART2, P2_10, Mux3); | ||
| 465 | impl_tx_pin!(LPUART2, P3_15, Mux2); | ||
| 466 | |||
| 467 | impl_rx_pin!(LPUART2, P1_4, Mux3); | ||
| 468 | impl_rx_pin!(LPUART2, P1_12, Mux3); | ||
| 469 | impl_rx_pin!(LPUART2, P2_3, Mux3); | ||
| 470 | impl_rx_pin!(LPUART2, P2_11, Mux3); | ||
| 471 | impl_rx_pin!(LPUART2, P3_14, Mux2); | ||
| 472 | |||
| 473 | impl_cts_pin!(LPUART2, P1_7, Mux3); | ||
| 474 | impl_cts_pin!(LPUART2, P1_15, Mux3); | ||
| 475 | impl_cts_pin!(LPUART2, P2_4, Mux3); | ||
| 476 | impl_cts_pin!(LPUART2, P3_13, Mux2); | ||
| 477 | |||
| 478 | impl_rts_pin!(LPUART2, P1_6, Mux3); | ||
| 479 | impl_rts_pin!(LPUART2, P1_14, Mux3); | ||
| 480 | impl_rts_pin!(LPUART2, P2_5, Mux3); | ||
| 481 | impl_rts_pin!(LPUART2, P3_12, Mux2); | ||
| 482 | |||
| 483 | // LPUART 3 | ||
| 484 | impl_tx_pin!(LPUART3, P3_1, Mux3); | ||
| 485 | impl_tx_pin!(LPUART3, P3_12, Mux3); | ||
| 486 | impl_tx_pin!(LPUART3, P4_5, Mux3); | ||
| 487 | |||
| 488 | impl_rx_pin!(LPUART3, P3_0, Mux3); | ||
| 489 | impl_rx_pin!(LPUART3, P3_13, Mux3); | ||
| 490 | impl_rx_pin!(LPUART3, P4_2, Mux3); | ||
| 491 | |||
| 492 | impl_cts_pin!(LPUART3, P3_7, Mux3); | ||
| 493 | impl_cts_pin!(LPUART3, P3_14, Mux3); | ||
| 494 | impl_cts_pin!(LPUART3, P4_6, Mux3); | ||
| 495 | |||
| 496 | impl_rts_pin!(LPUART3, P3_6, Mux3); | ||
| 497 | impl_rts_pin!(LPUART3, P3_15, Mux3); | ||
| 498 | impl_rts_pin!(LPUART3, P4_7, Mux3); | ||
| 499 | |||
| 500 | // LPUART 4 | ||
| 501 | impl_tx_pin!(LPUART4, P2_7, Mux3); | ||
| 502 | impl_tx_pin!(LPUART4, P3_19, Mux2); | ||
| 503 | impl_tx_pin!(LPUART4, P3_27, Mux3); | ||
| 504 | impl_tx_pin!(LPUART4, P4_3, Mux3); | ||
| 505 | |||
| 506 | impl_rx_pin!(LPUART4, P2_6, Mux3); | ||
| 507 | impl_rx_pin!(LPUART4, P3_18, Mux2); | ||
| 508 | impl_rx_pin!(LPUART4, P3_28, Mux3); | ||
| 509 | impl_rx_pin!(LPUART4, P4_4, Mux3); | ||
| 510 | |||
| 511 | impl_cts_pin!(LPUART4, P2_0, Mux3); | ||
| 512 | impl_cts_pin!(LPUART4, P3_17, Mux2); | ||
| 513 | impl_cts_pin!(LPUART4, P3_31, Mux3); | ||
| 514 | |||
| 515 | impl_rts_pin!(LPUART4, P2_1, Mux3); | ||
| 516 | impl_rts_pin!(LPUART4, P3_16, Mux2); | ||
| 517 | impl_rts_pin!(LPUART4, P3_30, Mux3); | ||
| 518 | |||
| 519 | // LPUART 5 | ||
| 520 | impl_tx_pin!(LPUART5, P1_10, Mux8); | ||
| 521 | impl_tx_pin!(LPUART5, P1_17, Mux8); | ||
| 522 | |||
| 523 | impl_rx_pin!(LPUART5, P1_11, Mux8); | ||
| 524 | impl_rx_pin!(LPUART5, P1_16, Mux8); | ||
| 525 | |||
| 526 | impl_cts_pin!(LPUART5, P1_12, Mux8); | ||
| 527 | impl_cts_pin!(LPUART5, P1_19, Mux8); | ||
| 528 | |||
| 529 | impl_rts_pin!(LPUART5, P1_13, Mux8); | ||
| 530 | impl_rts_pin!(LPUART5, P1_18, Mux8); | ||
| 531 | |||
| 532 | // ============================================================================ | ||
| 533 | // ERROR TYPES AND RESULTS | ||
| 534 | // ============================================================================ | ||
| 535 | |||
| 536 | /// LPUART error types | ||
| 537 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 538 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 539 | pub enum Error { | ||
| 540 | /// Read error | ||
| 541 | Read, | ||
| 542 | /// Buffer overflow | ||
| 543 | Overrun, | ||
| 544 | /// Noise error | ||
| 545 | Noise, | ||
| 546 | /// Framing error | ||
| 547 | Framing, | ||
| 548 | /// Parity error | ||
| 549 | Parity, | ||
| 550 | /// Failure | ||
| 551 | Fail, | ||
| 552 | /// Invalid argument | ||
| 553 | InvalidArgument, | ||
| 554 | /// Lpuart baud rate cannot be supported with the given clock | ||
| 555 | UnsupportedBaudrate, | ||
| 556 | /// RX FIFO Empty | ||
| 557 | RxFifoEmpty, | ||
| 558 | /// TX FIFO Full | ||
| 559 | TxFifoFull, | ||
| 560 | /// TX Busy | ||
| 561 | TxBusy, | ||
| 562 | /// Clock Error | ||
| 563 | ClockSetup(ClockError), | ||
| 564 | } | ||
| 565 | |||
| 566 | /// A specialized Result type for LPUART operations | ||
| 567 | pub type Result<T> = core::result::Result<T, Error>; | ||
| 568 | |||
| 569 | // ============================================================================ | ||
| 570 | // CONFIGURATION STRUCTURES | ||
| 571 | // ============================================================================ | ||
| 572 | |||
| 573 | /// Lpuart config | ||
| 574 | #[derive(Debug, Clone, Copy)] | ||
| 575 | pub struct Config { | ||
| 576 | /// Power state required for this peripheral | ||
| 577 | pub power: PoweredClock, | ||
| 578 | /// Clock source | ||
| 579 | pub source: LpuartClockSel, | ||
| 580 | /// Clock divisor | ||
| 581 | pub div: Div4, | ||
| 582 | /// Baud rate in bits per second | ||
| 583 | pub baudrate_bps: u32, | ||
| 584 | /// Parity configuration | ||
| 585 | pub parity_mode: Option<Parity>, | ||
| 586 | /// Number of data bits | ||
| 587 | pub data_bits_count: DataBits, | ||
| 588 | /// MSB First or LSB First configuration | ||
| 589 | pub msb_first: MsbFirst, | ||
| 590 | /// Number of stop bits | ||
| 591 | pub stop_bits_count: StopBits, | ||
| 592 | /// TX FIFO watermark | ||
| 593 | pub tx_fifo_watermark: u8, | ||
| 594 | /// RX FIFO watermark | ||
| 595 | pub rx_fifo_watermark: u8, | ||
| 596 | /// TX CTS source | ||
| 597 | pub tx_cts_source: TxCtsSource, | ||
| 598 | /// TX CTS configure | ||
| 599 | pub tx_cts_config: TxCtsConfig, | ||
| 600 | /// RX IDLE type | ||
| 601 | pub rx_idle_type: IdleType, | ||
| 602 | /// RX IDLE configuration | ||
| 603 | pub rx_idle_config: IdleConfig, | ||
| 604 | /// Swap TXD and RXD pins | ||
| 605 | pub swap_txd_rxd: bool, | ||
| 606 | } | ||
| 607 | |||
| 608 | impl Default for Config { | ||
| 609 | fn default() -> Self { | ||
| 610 | Self { | ||
| 611 | baudrate_bps: 115_200u32, | ||
| 612 | parity_mode: None, | ||
| 613 | data_bits_count: DataBits::Data8, | ||
| 614 | msb_first: MsbFirst::LsbFirst, | ||
| 615 | stop_bits_count: StopBits::One, | ||
| 616 | tx_fifo_watermark: 0, | ||
| 617 | rx_fifo_watermark: 1, | ||
| 618 | tx_cts_source: TxCtsSource::Cts, | ||
| 619 | tx_cts_config: TxCtsConfig::Start, | ||
| 620 | rx_idle_type: IdleType::FromStart, | ||
| 621 | rx_idle_config: IdleConfig::Idle1, | ||
| 622 | swap_txd_rxd: false, | ||
| 623 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 624 | source: LpuartClockSel::FroLfDiv, | ||
| 625 | div: Div4::no_div(), | ||
| 626 | } | ||
| 627 | } | ||
| 628 | } | ||
| 629 | |||
| 630 | /// LPUART status flags | ||
| 631 | #[derive(Debug, Clone, Copy)] | ||
| 632 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 633 | pub struct Status { | ||
| 634 | /// Transmit data register empty | ||
| 635 | pub tx_empty: bool, | ||
| 636 | /// Transmission complete | ||
| 637 | pub tx_complete: bool, | ||
| 638 | /// Receive data register full | ||
| 639 | pub rx_full: bool, | ||
| 640 | /// Idle line detected | ||
| 641 | pub idle: bool, | ||
| 642 | /// Receiver overrun | ||
| 643 | pub overrun: bool, | ||
| 644 | /// Noise error | ||
| 645 | pub noise: bool, | ||
| 646 | /// Framing error | ||
| 647 | pub framing: bool, | ||
| 648 | /// Parity error | ||
| 649 | pub parity: bool, | ||
| 650 | } | ||
| 651 | |||
| 652 | // ============================================================================ | ||
| 653 | // MODE TRAITS (BLOCKING/ASYNC) | ||
| 654 | // ============================================================================ | ||
| 655 | |||
| 656 | /// Driver move trait. | ||
| 657 | #[allow(private_bounds)] | ||
| 658 | pub trait Mode: sealed::Sealed {} | ||
| 659 | |||
| 660 | /// Blocking mode. | ||
| 661 | pub struct Blocking; | ||
| 662 | impl sealed::Sealed for Blocking {} | ||
| 663 | impl Mode for Blocking {} | ||
| 664 | |||
| 665 | /// Async mode. | ||
| 666 | pub struct Async; | ||
| 667 | impl sealed::Sealed for Async {} | ||
| 668 | impl Mode for Async {} | ||
| 669 | |||
| 670 | // ============================================================================ | ||
| 671 | // CORE DRIVER STRUCTURES | ||
| 672 | // ============================================================================ | ||
| 673 | |||
| 674 | /// Lpuart driver. | ||
| 675 | pub struct Lpuart<'a, M: Mode> { | ||
| 676 | info: Info, | ||
| 677 | tx: LpuartTx<'a, M>, | ||
| 678 | rx: LpuartRx<'a, M>, | ||
| 679 | } | ||
| 680 | |||
| 681 | /// Lpuart TX driver. | ||
| 682 | pub struct LpuartTx<'a, M: Mode> { | ||
| 683 | info: Info, | ||
| 684 | _tx_pin: Peri<'a, AnyPin>, | ||
| 685 | _cts_pin: Option<Peri<'a, AnyPin>>, | ||
| 686 | _tx_dma: Option<Channel<'a>>, | ||
| 687 | mode: PhantomData<(&'a (), M)>, | ||
| 688 | } | ||
| 689 | |||
| 690 | /// Lpuart Rx driver. | ||
| 691 | pub struct LpuartRx<'a, M: Mode> { | ||
| 692 | info: Info, | ||
| 693 | _rx_pin: Peri<'a, AnyPin>, | ||
| 694 | _rts_pin: Option<Peri<'a, AnyPin>>, | ||
| 695 | _rx_dma: Option<Channel<'a>>, | ||
| 696 | mode: PhantomData<(&'a (), M)>, | ||
| 697 | } | ||
| 698 | |||
| 699 | // ============================================================================ | ||
| 700 | // LPUART CORE IMPLEMENTATION | ||
| 701 | // ============================================================================ | ||
| 702 | |||
| 703 | impl<'a, M: Mode> Lpuart<'a, M> { | ||
| 704 | fn init<T: Instance>( | ||
| 705 | enable_tx: bool, | ||
| 706 | enable_rx: bool, | ||
| 707 | enable_tx_cts: bool, | ||
| 708 | enable_rx_rts: bool, | ||
| 709 | config: Config, | ||
| 710 | ) -> Result<()> { | ||
| 711 | let regs = T::info().regs; | ||
| 712 | |||
| 713 | // Enable clocks | ||
| 714 | let conf = LpuartConfig { | ||
| 715 | power: config.power, | ||
| 716 | source: config.source, | ||
| 717 | div: config.div, | ||
| 718 | instance: T::CLOCK_INSTANCE, | ||
| 719 | }; | ||
| 720 | let clock_freq = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? }; | ||
| 721 | |||
| 722 | // Perform initialization sequence | ||
| 723 | perform_software_reset(regs); | ||
| 724 | disable_transceiver(regs); | ||
| 725 | configure_baudrate(regs, config.baudrate_bps, clock_freq)?; | ||
| 726 | configure_frame_format(regs, &config); | ||
| 727 | configure_control_settings(regs, &config); | ||
| 728 | configure_fifo(regs, &config); | ||
| 729 | clear_all_status_flags(regs); | ||
| 730 | configure_flow_control(regs, enable_tx_cts, enable_rx_rts, &config); | ||
| 731 | configure_bit_order(regs, config.msb_first); | ||
| 732 | enable_transceiver(regs, enable_rx, enable_tx); | ||
| 733 | |||
| 734 | Ok(()) | ||
| 735 | } | ||
| 736 | |||
| 737 | /// Deinitialize the LPUART peripheral | ||
| 738 | pub fn deinit(&self) -> Result<()> { | ||
| 739 | let regs = self.info.regs; | ||
| 740 | |||
| 741 | // Wait for TX operations to complete | ||
| 742 | wait_for_tx_complete(regs); | ||
| 743 | |||
| 744 | // Clear all status flags | ||
| 745 | clear_all_status_flags(regs); | ||
| 746 | |||
| 747 | // Disable the module - clear all CTRL register bits | ||
| 748 | regs.ctrl().reset(); | ||
| 749 | |||
| 750 | Ok(()) | ||
| 751 | } | ||
| 752 | |||
| 753 | /// Split the Lpuart into a transmitter and receiver | ||
| 754 | pub fn split(self) -> (LpuartTx<'a, M>, LpuartRx<'a, M>) { | ||
| 755 | (self.tx, self.rx) | ||
| 756 | } | ||
| 757 | |||
| 758 | /// Split the Lpuart into a transmitter and receiver by mutable reference | ||
| 759 | pub fn split_ref(&mut self) -> (&mut LpuartTx<'a, M>, &mut LpuartRx<'a, M>) { | ||
| 760 | (&mut self.tx, &mut self.rx) | ||
| 761 | } | ||
| 762 | } | ||
| 763 | |||
| 764 | // ============================================================================ | ||
| 765 | // BLOCKING MODE IMPLEMENTATIONS | ||
| 766 | // ============================================================================ | ||
| 767 | |||
| 768 | impl<'a> Lpuart<'a, Blocking> { | ||
| 769 | /// Create a new blocking LPUART instance with RX/TX pins. | ||
| 770 | pub fn new_blocking<T: Instance>( | ||
| 771 | _inner: Peri<'a, T>, | ||
| 772 | tx_pin: Peri<'a, impl TxPin<T>>, | ||
| 773 | rx_pin: Peri<'a, impl RxPin<T>>, | ||
| 774 | config: Config, | ||
| 775 | ) -> Result<Self> { | ||
| 776 | // Configure the pins for LPUART usage | ||
| 777 | tx_pin.as_tx(); | ||
| 778 | rx_pin.as_rx(); | ||
| 779 | |||
| 780 | // Initialize the peripheral | ||
| 781 | Self::init::<T>(true, true, false, false, config)?; | ||
| 782 | |||
| 783 | Ok(Self { | ||
| 784 | info: T::info(), | ||
| 785 | tx: LpuartTx::new_inner(T::info(), tx_pin.into(), None, None), | ||
| 786 | rx: LpuartRx::new_inner(T::info(), rx_pin.into(), None, None), | ||
| 787 | }) | ||
| 788 | } | ||
| 789 | |||
| 790 | /// Create a new blocking LPUART instance with RX, TX and RTS/CTS flow control pins | ||
| 791 | pub fn new_blocking_with_rtscts<T: Instance>( | ||
| 792 | _inner: Peri<'a, T>, | ||
| 793 | tx_pin: Peri<'a, impl TxPin<T>>, | ||
| 794 | rx_pin: Peri<'a, impl RxPin<T>>, | ||
| 795 | cts_pin: Peri<'a, impl CtsPin<T>>, | ||
| 796 | rts_pin: Peri<'a, impl RtsPin<T>>, | ||
| 797 | config: Config, | ||
| 798 | ) -> Result<Self> { | ||
| 799 | // Configure the pins for LPUART usage | ||
| 800 | rx_pin.as_rx(); | ||
| 801 | tx_pin.as_tx(); | ||
| 802 | rts_pin.as_rts(); | ||
| 803 | cts_pin.as_cts(); | ||
| 804 | |||
| 805 | // Initialize the peripheral with flow control | ||
| 806 | Self::init::<T>(true, true, true, true, config)?; | ||
| 807 | |||
| 808 | Ok(Self { | ||
| 809 | info: T::info(), | ||
| 810 | rx: LpuartRx::new_inner(T::info(), rx_pin.into(), Some(rts_pin.into()), None), | ||
| 811 | tx: LpuartTx::new_inner(T::info(), tx_pin.into(), Some(cts_pin.into()), None), | ||
| 812 | }) | ||
| 813 | } | ||
| 814 | } | ||
| 815 | |||
| 816 | // ---------------------------------------------------------------------------- | ||
| 817 | // Blocking TX Implementation | ||
| 818 | // ---------------------------------------------------------------------------- | ||
| 819 | |||
| 820 | impl<'a, M: Mode> LpuartTx<'a, M> { | ||
| 821 | fn new_inner( | ||
| 822 | info: Info, | ||
| 823 | tx_pin: Peri<'a, AnyPin>, | ||
| 824 | cts_pin: Option<Peri<'a, AnyPin>>, | ||
| 825 | tx_dma: Option<Channel<'a>>, | ||
| 826 | ) -> Self { | ||
| 827 | Self { | ||
| 828 | info, | ||
| 829 | _tx_pin: tx_pin, | ||
| 830 | _cts_pin: cts_pin, | ||
| 831 | _tx_dma: tx_dma, | ||
| 832 | mode: PhantomData, | ||
| 833 | } | ||
| 834 | } | ||
| 835 | } | ||
| 836 | |||
| 837 | impl<'a> LpuartTx<'a, Blocking> { | ||
| 838 | /// Create a new blocking LPUART transmitter instance | ||
| 839 | pub fn new_blocking<T: Instance>( | ||
| 840 | _inner: Peri<'a, T>, | ||
| 841 | tx_pin: Peri<'a, impl TxPin<T>>, | ||
| 842 | config: Config, | ||
| 843 | ) -> Result<Self> { | ||
| 844 | // Configure the pins for LPUART usage | ||
| 845 | tx_pin.as_tx(); | ||
| 846 | |||
| 847 | // Initialize the peripheral | ||
| 848 | Lpuart::<Blocking>::init::<T>(true, false, false, false, config)?; | ||
| 849 | |||
| 850 | Ok(Self::new_inner(T::info(), tx_pin.into(), None, None)) | ||
| 851 | } | ||
| 852 | |||
| 853 | /// Create a new blocking LPUART transmitter instance with CTS flow control | ||
| 854 | pub fn new_blocking_with_cts<T: Instance>( | ||
| 855 | _inner: Peri<'a, T>, | ||
| 856 | tx_pin: Peri<'a, impl TxPin<T>>, | ||
| 857 | cts_pin: Peri<'a, impl CtsPin<T>>, | ||
| 858 | config: Config, | ||
| 859 | ) -> Result<Self> { | ||
| 860 | tx_pin.as_tx(); | ||
| 861 | cts_pin.as_cts(); | ||
| 862 | |||
| 863 | Lpuart::<Blocking>::init::<T>(true, false, true, false, config)?; | ||
| 864 | |||
| 865 | Ok(Self::new_inner(T::info(), tx_pin.into(), Some(cts_pin.into()), None)) | ||
| 866 | } | ||
| 867 | |||
| 868 | fn write_byte_internal(&mut self, byte: u8) -> Result<()> { | ||
| 869 | self.info.regs.data().modify(|_, w| unsafe { w.bits(u32::from(byte)) }); | ||
| 870 | |||
| 871 | Ok(()) | ||
| 872 | } | ||
| 873 | |||
| 874 | fn blocking_write_byte(&mut self, byte: u8) -> Result<()> { | ||
| 875 | while self.info.regs.stat().read().tdre().is_txdata() {} | ||
| 876 | self.write_byte_internal(byte) | ||
| 877 | } | ||
| 878 | |||
| 879 | fn write_byte(&mut self, byte: u8) -> Result<()> { | ||
| 880 | if self.info.regs.stat().read().tdre().is_txdata() { | ||
| 881 | Err(Error::TxFifoFull) | ||
| 882 | } else { | ||
| 883 | self.write_byte_internal(byte) | ||
| 884 | } | ||
| 885 | } | ||
| 886 | |||
| 887 | /// Write data to LPUART TX blocking execution until all data is sent. | ||
| 888 | pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> { | ||
| 889 | for x in buf { | ||
| 890 | self.blocking_write_byte(*x)?; | ||
| 891 | } | ||
| 892 | |||
| 893 | Ok(()) | ||
| 894 | } | ||
| 895 | |||
| 896 | pub fn write_str_blocking(&mut self, buf: &str) { | ||
| 897 | let _ = self.blocking_write(buf.as_bytes()); | ||
| 898 | } | ||
| 899 | |||
| 900 | /// Write data to LPUART TX without blocking. | ||
| 901 | pub fn write(&mut self, buf: &[u8]) -> Result<()> { | ||
| 902 | for x in buf { | ||
| 903 | self.write_byte(*x)?; | ||
| 904 | } | ||
| 905 | |||
| 906 | Ok(()) | ||
| 907 | } | ||
| 908 | |||
| 909 | /// Flush LPUART TX blocking execution until all data has been transmitted. | ||
| 910 | pub fn blocking_flush(&mut self) -> Result<()> { | ||
| 911 | while self.info.regs.water().read().txcount().bits() != 0 { | ||
| 912 | // Wait for TX FIFO to drain | ||
| 913 | } | ||
| 914 | |||
| 915 | // Wait for last character to shift out | ||
| 916 | while self.info.regs.stat().read().tc().is_active() { | ||
| 917 | // Wait for transmission to complete | ||
| 918 | } | ||
| 919 | |||
| 920 | Ok(()) | ||
| 921 | } | ||
| 922 | |||
| 923 | /// Flush LPUART TX. | ||
| 924 | pub fn flush(&mut self) -> Result<()> { | ||
| 925 | // Check if TX FIFO is empty | ||
| 926 | if self.info.regs.water().read().txcount().bits() != 0 { | ||
| 927 | return Err(Error::TxBusy); | ||
| 928 | } | ||
| 929 | |||
| 930 | // Check if transmission is complete | ||
| 931 | if self.info.regs.stat().read().tc().is_active() { | ||
| 932 | return Err(Error::TxBusy); | ||
| 933 | } | ||
| 934 | |||
| 935 | Ok(()) | ||
| 936 | } | ||
| 937 | } | ||
| 938 | |||
| 939 | // ---------------------------------------------------------------------------- | ||
| 940 | // Blocking RX Implementation | ||
| 941 | // ---------------------------------------------------------------------------- | ||
| 942 | |||
| 943 | impl<'a, M: Mode> LpuartRx<'a, M> { | ||
| 944 | fn new_inner( | ||
| 945 | info: Info, | ||
| 946 | rx_pin: Peri<'a, AnyPin>, | ||
| 947 | rts_pin: Option<Peri<'a, AnyPin>>, | ||
| 948 | rx_dma: Option<Channel<'a>>, | ||
| 949 | ) -> Self { | ||
| 950 | Self { | ||
| 951 | info, | ||
| 952 | _rx_pin: rx_pin, | ||
| 953 | _rts_pin: rts_pin, | ||
| 954 | _rx_dma: rx_dma, | ||
| 955 | mode: PhantomData, | ||
| 956 | } | ||
| 957 | } | ||
| 958 | } | ||
| 959 | |||
| 960 | impl<'a> LpuartRx<'a, Blocking> { | ||
| 961 | /// Create a new blocking LPUART Receiver instance | ||
| 962 | pub fn new_blocking<T: Instance>( | ||
| 963 | _inner: Peri<'a, T>, | ||
| 964 | rx_pin: Peri<'a, impl RxPin<T>>, | ||
| 965 | config: Config, | ||
| 966 | ) -> Result<Self> { | ||
| 967 | rx_pin.as_rx(); | ||
| 968 | |||
| 969 | Lpuart::<Blocking>::init::<T>(false, true, false, false, config)?; | ||
| 970 | |||
| 971 | Ok(Self::new_inner(T::info(), rx_pin.into(), None, None)) | ||
| 972 | } | ||
| 973 | |||
| 974 | /// Create a new blocking LPUART Receiver instance with RTS flow control | ||
| 975 | pub fn new_blocking_with_rts<T: Instance>( | ||
| 976 | _inner: Peri<'a, T>, | ||
| 977 | rx_pin: Peri<'a, impl RxPin<T>>, | ||
| 978 | rts_pin: Peri<'a, impl RtsPin<T>>, | ||
| 979 | config: Config, | ||
| 980 | ) -> Result<Self> { | ||
| 981 | rx_pin.as_rx(); | ||
| 982 | rts_pin.as_rts(); | ||
| 983 | |||
| 984 | Lpuart::<Blocking>::init::<T>(false, true, false, true, config)?; | ||
| 985 | |||
| 986 | Ok(Self::new_inner(T::info(), rx_pin.into(), Some(rts_pin.into()), None)) | ||
| 987 | } | ||
| 988 | |||
| 989 | fn read_byte_internal(&mut self) -> Result<u8> { | ||
| 990 | let data = self.info.regs.data().read(); | ||
| 991 | |||
| 992 | Ok((data.bits() & 0xFF) as u8) | ||
| 993 | } | ||
| 994 | |||
| 995 | fn read_byte(&mut self) -> Result<u8> { | ||
| 996 | check_and_clear_rx_errors(self.info.regs)?; | ||
| 997 | |||
| 998 | if !has_data(self.info.regs) { | ||
| 999 | return Err(Error::RxFifoEmpty); | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | self.read_byte_internal() | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | fn blocking_read_byte(&mut self) -> Result<u8> { | ||
| 1006 | loop { | ||
| 1007 | if has_data(self.info.regs) { | ||
| 1008 | return self.read_byte_internal(); | ||
| 1009 | } | ||
| 1010 | |||
| 1011 | check_and_clear_rx_errors(self.info.regs)?; | ||
| 1012 | } | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | /// Read data from LPUART RX without blocking. | ||
| 1016 | pub fn read(&mut self, buf: &mut [u8]) -> Result<()> { | ||
| 1017 | for byte in buf.iter_mut() { | ||
| 1018 | *byte = self.read_byte()?; | ||
| 1019 | } | ||
| 1020 | Ok(()) | ||
| 1021 | } | ||
| 1022 | |||
| 1023 | /// Read data from LPUART RX blocking execution until the buffer is filled. | ||
| 1024 | pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> { | ||
| 1025 | for byte in buf.iter_mut() { | ||
| 1026 | *byte = self.blocking_read_byte()?; | ||
| 1027 | } | ||
| 1028 | Ok(()) | ||
| 1029 | } | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | impl<'a> Lpuart<'a, Blocking> { | ||
| 1033 | /// Read data from LPUART RX blocking execution until the buffer is filled | ||
| 1034 | pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> { | ||
| 1035 | self.rx.blocking_read(buf) | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | /// Read data from LPUART RX without blocking | ||
| 1039 | pub fn read(&mut self, buf: &mut [u8]) -> Result<()> { | ||
| 1040 | self.rx.read(buf) | ||
| 1041 | } | ||
| 1042 | |||
| 1043 | /// Write data to LPUART TX blocking execution until all data is sent | ||
| 1044 | pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> { | ||
| 1045 | self.tx.blocking_write(buf) | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | pub fn write_byte(&mut self, byte: u8) -> Result<()> { | ||
| 1049 | self.tx.write_byte(byte) | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | pub fn read_byte_blocking(&mut self) -> u8 { | ||
| 1053 | loop { | ||
| 1054 | if let Ok(b) = self.rx.read_byte() { | ||
| 1055 | return b; | ||
| 1056 | } | ||
| 1057 | } | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | pub fn write_str_blocking(&mut self, buf: &str) { | ||
| 1061 | self.tx.write_str_blocking(buf); | ||
| 1062 | } | ||
| 1063 | |||
| 1064 | /// Write data to LPUART TX without blocking | ||
| 1065 | pub fn write(&mut self, buf: &[u8]) -> Result<()> { | ||
| 1066 | self.tx.write(buf) | ||
| 1067 | } | ||
| 1068 | |||
| 1069 | /// Flush LPUART TX blocking execution until all data has been transmitted | ||
| 1070 | pub fn blocking_flush(&mut self) -> Result<()> { | ||
| 1071 | self.tx.blocking_flush() | ||
| 1072 | } | ||
| 1073 | |||
| 1074 | /// Flush LPUART TX without blocking | ||
| 1075 | pub fn flush(&mut self) -> Result<()> { | ||
| 1076 | self.tx.flush() | ||
| 1077 | } | ||
| 1078 | } | ||
| 1079 | |||
| 1080 | // ============================================================================ | ||
| 1081 | // ASYNC MODE IMPLEMENTATIONS | ||
| 1082 | // ============================================================================ | ||
| 1083 | |||
| 1084 | // TODO: Implement async mode for LPUART | ||
| 1085 | |||
| 1086 | // ============================================================================ | ||
| 1087 | // EMBEDDED-HAL 0.2 TRAIT IMPLEMENTATIONS | ||
| 1088 | // ============================================================================ | ||
| 1089 | |||
| 1090 | impl embedded_hal_02::serial::Read<u8> for LpuartRx<'_, Blocking> { | ||
| 1091 | type Error = Error; | ||
| 1092 | |||
| 1093 | fn read(&mut self) -> core::result::Result<u8, nb::Error<Self::Error>> { | ||
| 1094 | let mut buf = [0; 1]; | ||
| 1095 | match self.read(&mut buf) { | ||
| 1096 | Ok(_) => Ok(buf[0]), | ||
| 1097 | Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock), | ||
| 1098 | Err(e) => Err(nb::Error::Other(e)), | ||
| 1099 | } | ||
| 1100 | } | ||
| 1101 | } | ||
| 1102 | |||
| 1103 | impl embedded_hal_02::serial::Write<u8> for LpuartTx<'_, Blocking> { | ||
| 1104 | type Error = Error; | ||
| 1105 | |||
| 1106 | fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error<Self::Error>> { | ||
| 1107 | match self.write(&[word]) { | ||
| 1108 | Ok(_) => Ok(()), | ||
| 1109 | Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock), | ||
| 1110 | Err(e) => Err(nb::Error::Other(e)), | ||
| 1111 | } | ||
| 1112 | } | ||
| 1113 | |||
| 1114 | fn flush(&mut self) -> core::result::Result<(), nb::Error<Self::Error>> { | ||
| 1115 | match self.flush() { | ||
| 1116 | Ok(_) => Ok(()), | ||
| 1117 | Err(Error::TxBusy) => Err(nb::Error::WouldBlock), | ||
| 1118 | Err(e) => Err(nb::Error::Other(e)), | ||
| 1119 | } | ||
| 1120 | } | ||
| 1121 | } | ||
| 1122 | |||
| 1123 | impl embedded_hal_02::blocking::serial::Write<u8> for LpuartTx<'_, Blocking> { | ||
| 1124 | type Error = Error; | ||
| 1125 | |||
| 1126 | fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> { | ||
| 1127 | self.blocking_write(buffer) | ||
| 1128 | } | ||
| 1129 | |||
| 1130 | fn bflush(&mut self) -> core::result::Result<(), Self::Error> { | ||
| 1131 | self.blocking_flush() | ||
| 1132 | } | ||
| 1133 | } | ||
| 1134 | |||
| 1135 | impl embedded_hal_02::serial::Read<u8> for Lpuart<'_, Blocking> { | ||
| 1136 | type Error = Error; | ||
| 1137 | |||
| 1138 | fn read(&mut self) -> core::result::Result<u8, nb::Error<Self::Error>> { | ||
| 1139 | embedded_hal_02::serial::Read::read(&mut self.rx) | ||
| 1140 | } | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | impl embedded_hal_02::serial::Write<u8> for Lpuart<'_, Blocking> { | ||
| 1144 | type Error = Error; | ||
| 1145 | |||
| 1146 | fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error<Self::Error>> { | ||
| 1147 | embedded_hal_02::serial::Write::write(&mut self.tx, word) | ||
| 1148 | } | ||
| 1149 | |||
| 1150 | fn flush(&mut self) -> core::result::Result<(), nb::Error<Self::Error>> { | ||
| 1151 | embedded_hal_02::serial::Write::flush(&mut self.tx) | ||
| 1152 | } | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | impl embedded_hal_02::blocking::serial::Write<u8> for Lpuart<'_, Blocking> { | ||
| 1156 | type Error = Error; | ||
| 1157 | |||
| 1158 | fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> { | ||
| 1159 | self.blocking_write(buffer) | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | fn bflush(&mut self) -> core::result::Result<(), Self::Error> { | ||
| 1163 | self.blocking_flush() | ||
| 1164 | } | ||
| 1165 | } | ||
| 1166 | |||
| 1167 | // ============================================================================ | ||
| 1168 | // EMBEDDED-HAL-NB TRAIT IMPLEMENTATIONS | ||
| 1169 | // ============================================================================ | ||
| 1170 | |||
| 1171 | impl embedded_hal_nb::serial::Error for Error { | ||
| 1172 | fn kind(&self) -> embedded_hal_nb::serial::ErrorKind { | ||
| 1173 | match *self { | ||
| 1174 | Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat, | ||
| 1175 | Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun, | ||
| 1176 | Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity, | ||
| 1177 | Self::Noise => embedded_hal_nb::serial::ErrorKind::Noise, | ||
| 1178 | _ => embedded_hal_nb::serial::ErrorKind::Other, | ||
| 1179 | } | ||
| 1180 | } | ||
| 1181 | } | ||
| 1182 | |||
| 1183 | impl embedded_hal_nb::serial::ErrorType for LpuartRx<'_, Blocking> { | ||
| 1184 | type Error = Error; | ||
| 1185 | } | ||
| 1186 | |||
| 1187 | impl embedded_hal_nb::serial::ErrorType for LpuartTx<'_, Blocking> { | ||
| 1188 | type Error = Error; | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | impl embedded_hal_nb::serial::ErrorType for Lpuart<'_, Blocking> { | ||
| 1192 | type Error = Error; | ||
| 1193 | } | ||
| 1194 | |||
| 1195 | impl embedded_hal_nb::serial::Read for LpuartRx<'_, Blocking> { | ||
| 1196 | fn read(&mut self) -> nb::Result<u8, Self::Error> { | ||
| 1197 | let mut buf = [0; 1]; | ||
| 1198 | match self.read(&mut buf) { | ||
| 1199 | Ok(_) => Ok(buf[0]), | ||
| 1200 | Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock), | ||
| 1201 | Err(e) => Err(nb::Error::Other(e)), | ||
| 1202 | } | ||
| 1203 | } | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | impl embedded_hal_nb::serial::Write for LpuartTx<'_, Blocking> { | ||
| 1207 | fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { | ||
| 1208 | match self.write(&[word]) { | ||
| 1209 | Ok(_) => Ok(()), | ||
| 1210 | Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock), | ||
| 1211 | Err(e) => Err(nb::Error::Other(e)), | ||
| 1212 | } | ||
| 1213 | } | ||
| 1214 | |||
| 1215 | fn flush(&mut self) -> nb::Result<(), Self::Error> { | ||
| 1216 | match self.flush() { | ||
| 1217 | Ok(_) => Ok(()), | ||
| 1218 | Err(Error::TxBusy) => Err(nb::Error::WouldBlock), | ||
| 1219 | Err(e) => Err(nb::Error::Other(e)), | ||
| 1220 | } | ||
| 1221 | } | ||
| 1222 | } | ||
| 1223 | |||
| 1224 | impl embedded_hal_nb::serial::Read for Lpuart<'_, Blocking> { | ||
| 1225 | fn read(&mut self) -> nb::Result<u8, Self::Error> { | ||
| 1226 | embedded_hal_nb::serial::Read::read(&mut self.rx) | ||
| 1227 | } | ||
| 1228 | } | ||
| 1229 | |||
| 1230 | impl embedded_hal_nb::serial::Write for Lpuart<'_, Blocking> { | ||
| 1231 | fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { | ||
| 1232 | embedded_hal_nb::serial::Write::write(&mut self.tx, char) | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | fn flush(&mut self) -> nb::Result<(), Self::Error> { | ||
| 1236 | embedded_hal_nb::serial::Write::flush(&mut self.tx) | ||
| 1237 | } | ||
| 1238 | } | ||
| 1239 | |||
| 1240 | // ============================================================================ | ||
| 1241 | // EMBEDDED-IO TRAIT IMPLEMENTATIONS | ||
| 1242 | // ============================================================================ | ||
| 1243 | |||
| 1244 | impl embedded_io::Error for Error { | ||
| 1245 | fn kind(&self) -> embedded_io::ErrorKind { | ||
| 1246 | embedded_io::ErrorKind::Other | ||
| 1247 | } | ||
| 1248 | } | ||
| 1249 | |||
| 1250 | impl embedded_io::ErrorType for LpuartRx<'_, Blocking> { | ||
| 1251 | type Error = Error; | ||
| 1252 | } | ||
| 1253 | |||
| 1254 | impl embedded_io::ErrorType for LpuartTx<'_, Blocking> { | ||
| 1255 | type Error = Error; | ||
| 1256 | } | ||
| 1257 | |||
| 1258 | impl embedded_io::ErrorType for Lpuart<'_, Blocking> { | ||
| 1259 | type Error = Error; | ||
| 1260 | } | ||
| 1261 | |||
| 1262 | impl embedded_io::Read for LpuartRx<'_, Blocking> { | ||
| 1263 | fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> { | ||
| 1264 | self.blocking_read(buf).map(|_| buf.len()) | ||
| 1265 | } | ||
| 1266 | } | ||
| 1267 | |||
| 1268 | impl embedded_io::Write for LpuartTx<'_, Blocking> { | ||
| 1269 | fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> { | ||
| 1270 | self.blocking_write(buf).map(|_| buf.len()) | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | fn flush(&mut self) -> core::result::Result<(), Self::Error> { | ||
| 1274 | self.blocking_flush() | ||
| 1275 | } | ||
| 1276 | } | ||
| 1277 | |||
| 1278 | impl embedded_io::Read for Lpuart<'_, Blocking> { | ||
| 1279 | fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> { | ||
| 1280 | embedded_io::Read::read(&mut self.rx, buf) | ||
| 1281 | } | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | impl embedded_io::Write for Lpuart<'_, Blocking> { | ||
| 1285 | fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> { | ||
| 1286 | embedded_io::Write::write(&mut self.tx, buf) | ||
| 1287 | } | ||
| 1288 | |||
| 1289 | fn flush(&mut self) -> core::result::Result<(), Self::Error> { | ||
| 1290 | embedded_io::Write::flush(&mut self.tx) | ||
| 1291 | } | ||
| 1292 | } | ||
diff --git a/embassy-mcxa/src/ostimer.rs b/embassy-mcxa/src/ostimer.rs new file mode 100644 index 000000000..9e66e82d8 --- /dev/null +++ b/embassy-mcxa/src/ostimer.rs | |||
| @@ -0,0 +1,745 @@ | |||
| 1 | //! # OSTIMER Driver with Robustness Features | ||
| 2 | //! | ||
| 3 | //! This module provides an async timer driver for the NXP MCXA276 OSTIMER peripheral | ||
| 4 | //! with protection against race conditions and timer rollover issues. | ||
| 5 | //! | ||
| 6 | //! ## Features | ||
| 7 | //! | ||
| 8 | //! - Async timing with embassy-time integration | ||
| 9 | //! - Gray code counter handling (42-bit counter) | ||
| 10 | //! - Interrupt-driven wakeups | ||
| 11 | //! - Configurable interrupt priority | ||
| 12 | //! - **Race condition protection**: Critical sections and atomic operations | ||
| 13 | //! - **Timer rollover handling**: Bounds checking and rollover prevention | ||
| 14 | //! | ||
| 15 | //! ## Clock Frequency Configuration | ||
| 16 | //! | ||
| 17 | //! The OSTIMER frequency depends on your system's clock configuration. You must provide | ||
| 18 | //! the actual frequency when calling `time_driver::init()`. | ||
| 19 | //! | ||
| 20 | //! ## Race Condition Protection | ||
| 21 | //! - Critical sections in interrupt handlers prevent concurrent access | ||
| 22 | //! - Atomic register operations with memory barriers | ||
| 23 | //! - Proper interrupt flag clearing and validation | ||
| 24 | //! | ||
| 25 | //! ## Timer Rollover Handling | ||
| 26 | //! - Bounds checking prevents scheduling beyond timer capacity | ||
| 27 | //! - Immediate wake for timestamps that would cause rollover issues | ||
| 28 | #![allow(dead_code)] | ||
| 29 | |||
| 30 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 31 | |||
| 32 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 33 | |||
| 34 | use crate::clocks::periph_helpers::{OsTimerConfig, OstimerClockSel}; | ||
| 35 | use crate::clocks::{Gate, PoweredClock, assert_reset, enable_and_reset, is_reset_released, release_reset}; | ||
| 36 | use crate::interrupt::InterruptExt; | ||
| 37 | use crate::pac; | ||
| 38 | |||
| 39 | // PAC defines the shared RegisterBlock under `ostimer0`. | ||
| 40 | type Regs = pac::ostimer0::RegisterBlock; | ||
| 41 | |||
| 42 | // OSTIMER EVTIMER register layout constants | ||
| 43 | /// Total width of the EVTIMER counter in bits (42 bits total) | ||
| 44 | const EVTIMER_TOTAL_BITS: u32 = 42; | ||
| 45 | /// Width of the low part of EVTIMER (bits 31:0) | ||
| 46 | const EVTIMER_LO_BITS: u32 = 32; | ||
| 47 | /// Width of the high part of EVTIMER (bits 41:32) | ||
| 48 | const EVTIMER_HI_BITS: u32 = 10; | ||
| 49 | /// Bit position where high part starts in the combined 64-bit value | ||
| 50 | const EVTIMER_HI_SHIFT: u32 = 32; | ||
| 51 | |||
| 52 | /// Bit mask for the high part of EVTIMER | ||
| 53 | const EVTIMER_HI_MASK: u16 = (1 << EVTIMER_HI_BITS) - 1; | ||
| 54 | |||
| 55 | /// Maximum value for MATCH_L register (32-bit) | ||
| 56 | const MATCH_L_MAX: u32 = u32::MAX; | ||
| 57 | /// Maximum value for MATCH_H register (10-bit) | ||
| 58 | const MATCH_H_MAX: u16 = EVTIMER_HI_MASK; | ||
| 59 | |||
| 60 | /// Bit mask for extracting the low 32 bits from a 64-bit value | ||
| 61 | const LOW_32_BIT_MASK: u64 = u32::MAX as u64; | ||
| 62 | |||
| 63 | /// Gray code conversion bit shifts (most significant to least) | ||
| 64 | const GRAY_CONVERSION_SHIFTS: [u32; 6] = [32, 16, 8, 4, 2, 1]; | ||
| 65 | |||
| 66 | /// Maximum timer value before rollover (2^42 - 1 ticks) | ||
| 67 | /// Actual rollover time depends on the configured clock frequency | ||
| 68 | const TIMER_MAX_VALUE: u64 = (1u64 << EVTIMER_TOTAL_BITS) - 1; | ||
| 69 | |||
| 70 | /// Threshold for detecting timer rollover in comparisons (1 second at 1MHz) | ||
| 71 | const TIMER_ROLLOVER_THRESHOLD: u64 = 1_000_000; | ||
| 72 | |||
| 73 | /// Common default interrupt priority for OSTIMER | ||
| 74 | const DEFAULT_INTERRUPT_PRIORITY: u8 = 3; | ||
| 75 | |||
| 76 | // Global alarm state for interrupt handling | ||
| 77 | static ALARM_ACTIVE: AtomicBool = AtomicBool::new(false); | ||
| 78 | static mut ALARM_CALLBACK: Option<fn()> = None; | ||
| 79 | static mut ALARM_FLAG: Option<*const AtomicBool> = None; | ||
| 80 | static mut ALARM_TARGET_TIME: u64 = 0; | ||
| 81 | |||
| 82 | /// Number of tight spin iterations between elapsed time checks while waiting for MATCH writes to return to the idle (0) state. | ||
| 83 | const MATCH_WRITE_READY_SPINS: usize = 512; | ||
| 84 | /// Maximum time (in OSTIMER ticks) to wait for MATCH registers to become writable (~5 ms at 1 MHz). | ||
| 85 | const MATCH_WRITE_READY_TIMEOUT_TICKS: u64 = 5_000; | ||
| 86 | /// Short stabilization delay executed after toggling the MRCC reset line to let the OSTIMER bus interface settle. | ||
| 87 | const RESET_STABILIZE_SPINS: usize = 512; | ||
| 88 | |||
| 89 | pub(super) fn wait_for_match_write_ready(r: &Regs) -> bool { | ||
| 90 | let start = now_ticks_read(); | ||
| 91 | let mut spin_budget = 0usize; | ||
| 92 | |||
| 93 | loop { | ||
| 94 | if r.osevent_ctrl().read().match_wr_rdy().bit_is_clear() { | ||
| 95 | return true; | ||
| 96 | } | ||
| 97 | |||
| 98 | cortex_m::asm::nop(); | ||
| 99 | spin_budget += 1; | ||
| 100 | |||
| 101 | if spin_budget >= MATCH_WRITE_READY_SPINS { | ||
| 102 | spin_budget = 0; | ||
| 103 | |||
| 104 | let elapsed = now_ticks_read().wrapping_sub(start); | ||
| 105 | if elapsed >= MATCH_WRITE_READY_TIMEOUT_TICKS { | ||
| 106 | return false; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | pub(super) fn wait_for_match_write_complete(r: &Regs) -> bool { | ||
| 113 | let start = now_ticks_read(); | ||
| 114 | let mut spin_budget = 0usize; | ||
| 115 | |||
| 116 | loop { | ||
| 117 | if r.osevent_ctrl().read().match_wr_rdy().bit_is_clear() { | ||
| 118 | return true; | ||
| 119 | } | ||
| 120 | |||
| 121 | cortex_m::asm::nop(); | ||
| 122 | spin_budget += 1; | ||
| 123 | |||
| 124 | if spin_budget >= MATCH_WRITE_READY_SPINS { | ||
| 125 | spin_budget = 0; | ||
| 126 | |||
| 127 | let elapsed = now_ticks_read().wrapping_sub(start); | ||
| 128 | if elapsed >= MATCH_WRITE_READY_TIMEOUT_TICKS { | ||
| 129 | return false; | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | fn prime_match_registers(r: &Regs) { | ||
| 136 | // Disable the interrupt, clear any pending flag, then wait until the MATCH registers are writable. | ||
| 137 | r.osevent_ctrl() | ||
| 138 | .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit()); | ||
| 139 | |||
| 140 | if wait_for_match_write_ready(r) { | ||
| 141 | r.match_l().write(|w| unsafe { w.match_value().bits(MATCH_L_MAX) }); | ||
| 142 | r.match_h().write(|w| unsafe { w.match_value().bits(MATCH_H_MAX) }); | ||
| 143 | let _ = wait_for_match_write_complete(r); | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Single-shot alarm functionality for OSTIMER | ||
| 148 | pub struct Alarm<'d> { | ||
| 149 | /// Whether the alarm is currently active | ||
| 150 | active: AtomicBool, | ||
| 151 | /// Callback to execute when alarm expires (optional) | ||
| 152 | callback: Option<fn()>, | ||
| 153 | /// Flag that gets set when alarm expires (optional) | ||
| 154 | flag: Option<&'d AtomicBool>, | ||
| 155 | _phantom: core::marker::PhantomData<&'d mut ()>, | ||
| 156 | } | ||
| 157 | |||
| 158 | impl<'d> Default for Alarm<'d> { | ||
| 159 | fn default() -> Self { | ||
| 160 | Self::new() | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | impl<'d> Alarm<'d> { | ||
| 165 | /// Create a new alarm instance | ||
| 166 | pub fn new() -> Self { | ||
| 167 | Self { | ||
| 168 | active: AtomicBool::new(false), | ||
| 169 | callback: None, | ||
| 170 | flag: None, | ||
| 171 | _phantom: core::marker::PhantomData, | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | /// Set a callback that will be executed when the alarm expires | ||
| 176 | /// Note: Due to interrupt handler constraints, callbacks must be static function pointers | ||
| 177 | pub fn with_callback(mut self, callback: fn()) -> Self { | ||
| 178 | self.callback = Some(callback); | ||
| 179 | self | ||
| 180 | } | ||
| 181 | |||
| 182 | /// Set a flag that will be set to true when the alarm expires | ||
| 183 | pub fn with_flag(mut self, flag: &'d AtomicBool) -> Self { | ||
| 184 | self.flag = Some(flag); | ||
| 185 | self | ||
| 186 | } | ||
| 187 | |||
| 188 | /// Check if the alarm is currently active | ||
| 189 | pub fn is_active(&self) -> bool { | ||
| 190 | self.active.load(Ordering::Acquire) | ||
| 191 | } | ||
| 192 | |||
| 193 | /// Cancel the alarm if it's active | ||
| 194 | pub fn cancel(&self) { | ||
| 195 | self.active.store(false, Ordering::Release); | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | /// Configuration for Ostimer::new() | ||
| 200 | #[derive(Copy, Clone)] | ||
| 201 | pub struct Config { | ||
| 202 | /// Initialize MATCH registers to their max values and mask/clear the interrupt flag. | ||
| 203 | pub init_match_max: bool, | ||
| 204 | pub power: PoweredClock, | ||
| 205 | pub source: OstimerClockSel, | ||
| 206 | } | ||
| 207 | |||
| 208 | impl Default for Config { | ||
| 209 | fn default() -> Self { | ||
| 210 | Self { | ||
| 211 | init_match_max: true, | ||
| 212 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 213 | source: OstimerClockSel::Clk1M, | ||
| 214 | } | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | /// OSTIMER peripheral instance | ||
| 219 | pub struct Ostimer<'d, I: Instance> { | ||
| 220 | _inst: core::marker::PhantomData<I>, | ||
| 221 | clock_frequency_hz: u64, | ||
| 222 | _phantom: core::marker::PhantomData<&'d mut ()>, | ||
| 223 | } | ||
| 224 | |||
| 225 | impl<'d, I: Instance> Ostimer<'d, I> { | ||
| 226 | /// Construct OSTIMER handle. | ||
| 227 | /// Requires clocks for the instance to be enabled by the board before calling. | ||
| 228 | /// Does not enable NVIC or INTENA; use time_driver::init() for async operation. | ||
| 229 | pub fn new(_inst: Peri<'d, I>, cfg: Config) -> Self { | ||
| 230 | let clock_freq = unsafe { | ||
| 231 | enable_and_reset::<I>(&OsTimerConfig { | ||
| 232 | power: cfg.power, | ||
| 233 | source: cfg.source, | ||
| 234 | }) | ||
| 235 | .expect("Enabling OsTimer clock should not fail") | ||
| 236 | }; | ||
| 237 | |||
| 238 | assert!(clock_freq > 0, "OSTIMER frequency must be greater than 0"); | ||
| 239 | |||
| 240 | if cfg.init_match_max { | ||
| 241 | let r: &Regs = unsafe { &*I::ptr() }; | ||
| 242 | // Mask INTENA, clear pending flag, and set MATCH to max so no spurious IRQ fires. | ||
| 243 | prime_match_registers(r); | ||
| 244 | } | ||
| 245 | |||
| 246 | Self { | ||
| 247 | _inst: core::marker::PhantomData, | ||
| 248 | clock_frequency_hz: clock_freq as u64, | ||
| 249 | _phantom: core::marker::PhantomData, | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | /// Get the configured clock frequency in Hz | ||
| 254 | pub fn clock_frequency_hz(&self) -> u64 { | ||
| 255 | self.clock_frequency_hz | ||
| 256 | } | ||
| 257 | |||
| 258 | /// Read the current timer counter value in timer ticks | ||
| 259 | /// | ||
| 260 | /// # Returns | ||
| 261 | /// Current timer counter value as a 64-bit unsigned integer | ||
| 262 | pub fn now(&self) -> u64 { | ||
| 263 | now_ticks_read() | ||
| 264 | } | ||
| 265 | |||
| 266 | /// Reset the timer counter to zero | ||
| 267 | /// | ||
| 268 | /// This performs a hardware reset of the OSTIMER peripheral, which will reset | ||
| 269 | /// the counter to zero and clear any pending interrupts. Note that this will | ||
| 270 | /// affect all timer operations including embassy-time. | ||
| 271 | /// | ||
| 272 | /// # Safety | ||
| 273 | /// This operation will reset the entire OSTIMER peripheral. Any active alarms | ||
| 274 | /// or time_driver operations will be disrupted. Use with caution. | ||
| 275 | pub fn reset(&self, _peripherals: &crate::pac::Peripherals) { | ||
| 276 | critical_section::with(|_| { | ||
| 277 | let r: &Regs = unsafe { &*I::ptr() }; | ||
| 278 | |||
| 279 | // Mask the peripheral interrupt flag before we toggle the reset line so that | ||
| 280 | // no new NVIC activity races with the reset sequence. | ||
| 281 | r.osevent_ctrl() | ||
| 282 | .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit()); | ||
| 283 | |||
| 284 | unsafe { | ||
| 285 | assert_reset::<I>(); | ||
| 286 | |||
| 287 | for _ in 0..RESET_STABILIZE_SPINS { | ||
| 288 | cortex_m::asm::nop(); | ||
| 289 | } | ||
| 290 | |||
| 291 | release_reset::<I>(); | ||
| 292 | |||
| 293 | while !is_reset_released::<I>() { | ||
| 294 | cortex_m::asm::nop(); | ||
| 295 | } | ||
| 296 | } | ||
| 297 | |||
| 298 | for _ in 0..RESET_STABILIZE_SPINS { | ||
| 299 | cortex_m::asm::nop(); | ||
| 300 | } | ||
| 301 | |||
| 302 | // Clear alarm bookkeeping before re-arming MATCH registers. | ||
| 303 | ALARM_ACTIVE.store(false, Ordering::Release); | ||
| 304 | unsafe { | ||
| 305 | ALARM_TARGET_TIME = 0; | ||
| 306 | ALARM_CALLBACK = None; | ||
| 307 | ALARM_FLAG = None; | ||
| 308 | } | ||
| 309 | |||
| 310 | prime_match_registers(r); | ||
| 311 | }); | ||
| 312 | |||
| 313 | // Ensure no stale OS_EVENT request remains pending after the reset sequence. | ||
| 314 | crate::interrupt::OS_EVENT.unpend(); | ||
| 315 | } | ||
| 316 | |||
| 317 | /// Schedule a single-shot alarm to expire after the specified delay in microseconds | ||
| 318 | /// | ||
| 319 | /// # Parameters | ||
| 320 | /// * `alarm` - The alarm instance to schedule | ||
| 321 | /// * `delay_us` - Delay in microseconds from now | ||
| 322 | /// | ||
| 323 | /// # Returns | ||
| 324 | /// `true` if the alarm was scheduled successfully, `false` if it would exceed timer capacity | ||
| 325 | pub fn schedule_alarm_delay(&self, alarm: &Alarm, delay_us: u64) -> bool { | ||
| 326 | let delay_ticks = (delay_us * self.clock_frequency_hz) / 1_000_000; | ||
| 327 | let target_time = now_ticks_read() + delay_ticks; | ||
| 328 | self.schedule_alarm_at(alarm, target_time) | ||
| 329 | } | ||
| 330 | |||
| 331 | /// Schedule a single-shot alarm to expire at the specified absolute time in timer ticks | ||
| 332 | /// | ||
| 333 | /// # Parameters | ||
| 334 | /// * `alarm` - The alarm instance to schedule | ||
| 335 | /// * `target_ticks` - Absolute time in timer ticks when the alarm should expire | ||
| 336 | /// | ||
| 337 | /// # Returns | ||
| 338 | /// `true` if the alarm was scheduled successfully, `false` if it would exceed timer capacity | ||
| 339 | pub fn schedule_alarm_at(&self, alarm: &Alarm, target_ticks: u64) -> bool { | ||
| 340 | let now = now_ticks_read(); | ||
| 341 | |||
| 342 | // Check if target time is in the past | ||
| 343 | if target_ticks <= now { | ||
| 344 | // Execute callback immediately if alarm was supposed to be active | ||
| 345 | if alarm.active.load(Ordering::Acquire) { | ||
| 346 | alarm.active.store(false, Ordering::Release); | ||
| 347 | if let Some(callback) = alarm.callback { | ||
| 348 | callback(); | ||
| 349 | } | ||
| 350 | if let Some(flag) = &alarm.flag { | ||
| 351 | flag.store(true, Ordering::Release); | ||
| 352 | } | ||
| 353 | } | ||
| 354 | return true; | ||
| 355 | } | ||
| 356 | |||
| 357 | // Check for timer rollover | ||
| 358 | let max_future = now + TIMER_MAX_VALUE; | ||
| 359 | if target_ticks > max_future { | ||
| 360 | return false; // Would exceed timer capacity | ||
| 361 | } | ||
| 362 | |||
| 363 | // Program the timer | ||
| 364 | let r: &Regs = unsafe { &*I::ptr() }; | ||
| 365 | |||
| 366 | critical_section::with(|_| { | ||
| 367 | // Disable interrupt and clear flag | ||
| 368 | r.osevent_ctrl() | ||
| 369 | .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit()); | ||
| 370 | |||
| 371 | if !wait_for_match_write_ready(r) { | ||
| 372 | prime_match_registers(r); | ||
| 373 | |||
| 374 | if !wait_for_match_write_ready(r) { | ||
| 375 | alarm.active.store(false, Ordering::Release); | ||
| 376 | ALARM_ACTIVE.store(false, Ordering::Release); | ||
| 377 | unsafe { | ||
| 378 | ALARM_TARGET_TIME = 0; | ||
| 379 | ALARM_CALLBACK = None; | ||
| 380 | ALARM_FLAG = None; | ||
| 381 | } | ||
| 382 | return false; | ||
| 383 | } | ||
| 384 | } | ||
| 385 | |||
| 386 | // Mark alarm as active now that we know the MATCH registers are writable | ||
| 387 | alarm.active.store(true, Ordering::Release); | ||
| 388 | |||
| 389 | // Set global alarm state for interrupt handler | ||
| 390 | ALARM_ACTIVE.store(true, Ordering::Release); | ||
| 391 | unsafe { | ||
| 392 | ALARM_TARGET_TIME = target_ticks; | ||
| 393 | ALARM_CALLBACK = alarm.callback; | ||
| 394 | ALARM_FLAG = alarm.flag.map(|f| f as *const AtomicBool); | ||
| 395 | } | ||
| 396 | |||
| 397 | // Program MATCH registers (Gray-coded) | ||
| 398 | let gray = bin_to_gray(target_ticks); | ||
| 399 | let l = (gray & LOW_32_BIT_MASK) as u32; | ||
| 400 | let h = (((gray >> EVTIMER_HI_SHIFT) as u16) & EVTIMER_HI_MASK) as u16; | ||
| 401 | |||
| 402 | r.match_l().write(|w| unsafe { w.match_value().bits(l) }); | ||
| 403 | r.match_h().write(|w| unsafe { w.match_value().bits(h) }); | ||
| 404 | |||
| 405 | if !wait_for_match_write_complete(r) { | ||
| 406 | alarm.active.store(false, Ordering::Release); | ||
| 407 | ALARM_ACTIVE.store(false, Ordering::Release); | ||
| 408 | unsafe { | ||
| 409 | ALARM_TARGET_TIME = 0; | ||
| 410 | ALARM_CALLBACK = None; | ||
| 411 | ALARM_FLAG = None; | ||
| 412 | } | ||
| 413 | return false; | ||
| 414 | } | ||
| 415 | |||
| 416 | let now_after_program = now_ticks_read(); | ||
| 417 | let intrflag_set = r.osevent_ctrl().read().ostimer_intrflag().bit_is_set(); | ||
| 418 | if now_after_program >= target_ticks && !intrflag_set { | ||
| 419 | alarm.active.store(false, Ordering::Release); | ||
| 420 | ALARM_ACTIVE.store(false, Ordering::Release); | ||
| 421 | unsafe { | ||
| 422 | ALARM_TARGET_TIME = 0; | ||
| 423 | ALARM_CALLBACK = None; | ||
| 424 | ALARM_FLAG = None; | ||
| 425 | } | ||
| 426 | return false; | ||
| 427 | } | ||
| 428 | |||
| 429 | // Enable interrupt | ||
| 430 | r.osevent_ctrl().write(|w| w.ostimer_intena().set_bit()); | ||
| 431 | |||
| 432 | true | ||
| 433 | }) | ||
| 434 | } | ||
| 435 | |||
| 436 | /// Cancel any active alarm | ||
| 437 | pub fn cancel_alarm(&self, alarm: &Alarm) { | ||
| 438 | critical_section::with(|_| { | ||
| 439 | alarm.cancel(); | ||
| 440 | |||
| 441 | // Clear global alarm state | ||
| 442 | ALARM_ACTIVE.store(false, Ordering::Release); | ||
| 443 | unsafe { ALARM_TARGET_TIME = 0 }; | ||
| 444 | |||
| 445 | // Reset MATCH registers to maximum values to prevent spurious interrupts | ||
| 446 | let r: &Regs = unsafe { &*I::ptr() }; | ||
| 447 | prime_match_registers(r); | ||
| 448 | }); | ||
| 449 | } | ||
| 450 | |||
| 451 | /// Check if an alarm has expired (call this from your interrupt handler) | ||
| 452 | /// Returns true if the alarm was active and has now expired | ||
| 453 | pub fn check_alarm_expired(&self, alarm: &Alarm) -> bool { | ||
| 454 | if alarm.active.load(Ordering::Acquire) { | ||
| 455 | alarm.active.store(false, Ordering::Release); | ||
| 456 | |||
| 457 | // Execute callback | ||
| 458 | if let Some(callback) = alarm.callback { | ||
| 459 | callback(); | ||
| 460 | } | ||
| 461 | |||
| 462 | // Set flag | ||
| 463 | if let Some(flag) = &alarm.flag { | ||
| 464 | flag.store(true, Ordering::Release); | ||
| 465 | } | ||
| 466 | |||
| 467 | true | ||
| 468 | } else { | ||
| 469 | false | ||
| 470 | } | ||
| 471 | } | ||
| 472 | } | ||
| 473 | |||
| 474 | /// Read current EVTIMER (Gray-coded) and convert to binary ticks. | ||
| 475 | #[inline(always)] | ||
| 476 | fn now_ticks_read() -> u64 { | ||
| 477 | let r: &Regs = unsafe { &*pac::Ostimer0::ptr() }; | ||
| 478 | |||
| 479 | // Read high then low to minimize incoherent snapshots | ||
| 480 | let hi = (r.evtimerh().read().evtimer_count_value().bits() as u64) & (EVTIMER_HI_MASK as u64); | ||
| 481 | let lo = r.evtimerl().read().evtimer_count_value().bits() as u64; | ||
| 482 | // Combine and convert from Gray code to binary | ||
| 483 | let gray = lo | (hi << EVTIMER_HI_SHIFT); | ||
| 484 | gray_to_bin(gray) | ||
| 485 | } | ||
| 486 | |||
| 487 | // Instance trait like other drivers, providing a PAC pointer for this OSTIMER instance | ||
| 488 | pub trait Instance: Gate<MrccPeriphConfig = OsTimerConfig> + PeripheralType { | ||
| 489 | fn ptr() -> *const Regs; | ||
| 490 | } | ||
| 491 | |||
| 492 | #[cfg(not(feature = "time"))] | ||
| 493 | impl Instance for crate::peripherals::OSTIMER0 { | ||
| 494 | #[inline(always)] | ||
| 495 | fn ptr() -> *const Regs { | ||
| 496 | pac::Ostimer0::ptr() | ||
| 497 | } | ||
| 498 | } | ||
| 499 | |||
| 500 | #[inline(always)] | ||
| 501 | fn bin_to_gray(x: u64) -> u64 { | ||
| 502 | x ^ (x >> 1) | ||
| 503 | } | ||
| 504 | |||
| 505 | #[inline(always)] | ||
| 506 | fn gray_to_bin(gray: u64) -> u64 { | ||
| 507 | // More efficient iterative conversion using predefined shifts | ||
| 508 | let mut bin = gray; | ||
| 509 | for &shift in &GRAY_CONVERSION_SHIFTS { | ||
| 510 | bin ^= bin >> shift; | ||
| 511 | } | ||
| 512 | bin | ||
| 513 | } | ||
| 514 | |||
| 515 | #[cfg(feature = "time")] | ||
| 516 | pub mod time_driver { | ||
| 517 | use core::sync::atomic::Ordering; | ||
| 518 | use core::task::Waker; | ||
| 519 | |||
| 520 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 521 | use embassy_time_driver as etd; | ||
| 522 | |||
| 523 | use super::{ | ||
| 524 | ALARM_ACTIVE, ALARM_CALLBACK, ALARM_FLAG, ALARM_TARGET_TIME, EVTIMER_HI_MASK, EVTIMER_HI_SHIFT, | ||
| 525 | LOW_32_BIT_MASK, Regs, bin_to_gray, now_ticks_read, | ||
| 526 | }; | ||
| 527 | use crate::clocks::periph_helpers::{OsTimerConfig, OstimerClockSel}; | ||
| 528 | use crate::clocks::{PoweredClock, enable_and_reset}; | ||
| 529 | use crate::pac; | ||
| 530 | |||
| 531 | #[allow(non_camel_case_types)] | ||
| 532 | pub(crate) struct _OSTIMER0_TIME_DRIVER { | ||
| 533 | _x: (), | ||
| 534 | } | ||
| 535 | |||
| 536 | // #[cfg(feature = "time")] | ||
| 537 | // impl_cc_gate!(_OSTIMER0_TIME_DRIVER, mrcc_glb_cc1, mrcc_glb_rst1, ostimer0, OsTimerConfig); | ||
| 538 | |||
| 539 | impl crate::clocks::Gate for _OSTIMER0_TIME_DRIVER { | ||
| 540 | type MrccPeriphConfig = crate::clocks::periph_helpers::OsTimerConfig; | ||
| 541 | |||
| 542 | #[inline] | ||
| 543 | unsafe fn enable_clock() { | ||
| 544 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 545 | mrcc.mrcc_glb_cc1().modify(|_, w| w.ostimer0().enabled()); | ||
| 546 | } | ||
| 547 | |||
| 548 | #[inline] | ||
| 549 | unsafe fn disable_clock() { | ||
| 550 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 551 | mrcc.mrcc_glb_cc1().modify(|_r, w| w.ostimer0().disabled()); | ||
| 552 | } | ||
| 553 | |||
| 554 | #[inline] | ||
| 555 | fn is_clock_enabled() -> bool { | ||
| 556 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 557 | mrcc.mrcc_glb_cc1().read().ostimer0().is_enabled() | ||
| 558 | } | ||
| 559 | |||
| 560 | #[inline] | ||
| 561 | unsafe fn release_reset() { | ||
| 562 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 563 | mrcc.mrcc_glb_rst1().modify(|_, w| w.ostimer0().enabled()); | ||
| 564 | } | ||
| 565 | |||
| 566 | #[inline] | ||
| 567 | unsafe fn assert_reset() { | ||
| 568 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 569 | mrcc.mrcc_glb_rst1().modify(|_, w| w.ostimer0().disabled()); | ||
| 570 | } | ||
| 571 | |||
| 572 | #[inline] | ||
| 573 | fn is_reset_released() -> bool { | ||
| 574 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 575 | mrcc.mrcc_glb_rst1().read().ostimer0().is_enabled() | ||
| 576 | } | ||
| 577 | } | ||
| 578 | |||
| 579 | pub struct Driver; | ||
| 580 | static TIMER_WAKER: AtomicWaker = AtomicWaker::new(); | ||
| 581 | |||
| 582 | impl etd::Driver for Driver { | ||
| 583 | fn now(&self) -> u64 { | ||
| 584 | // Use the hardware counter (frequency configured in init) | ||
| 585 | super::now_ticks_read() | ||
| 586 | } | ||
| 587 | |||
| 588 | fn schedule_wake(&self, timestamp: u64, waker: &Waker) { | ||
| 589 | let now = self.now(); | ||
| 590 | |||
| 591 | // If timestamp is in the past or very close to now, wake immediately | ||
| 592 | if timestamp <= now { | ||
| 593 | waker.wake_by_ref(); | ||
| 594 | return; | ||
| 595 | } | ||
| 596 | |||
| 597 | // Prevent scheduling too far in the future (beyond timer rollover) | ||
| 598 | // This prevents wraparound issues | ||
| 599 | let max_future = now + super::TIMER_MAX_VALUE; | ||
| 600 | if timestamp > max_future { | ||
| 601 | // For very long timeouts, wake immediately to avoid rollover issues | ||
| 602 | waker.wake_by_ref(); | ||
| 603 | return; | ||
| 604 | } | ||
| 605 | |||
| 606 | // Register the waker first so any immediate wake below is observed by the executor. | ||
| 607 | TIMER_WAKER.register(waker); | ||
| 608 | |||
| 609 | let r: &Regs = unsafe { &*pac::Ostimer0::ptr() }; | ||
| 610 | |||
| 611 | critical_section::with(|_| { | ||
| 612 | // Mask INTENA and clear flag | ||
| 613 | r.osevent_ctrl() | ||
| 614 | .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit()); | ||
| 615 | |||
| 616 | // Read back to ensure W1C took effect on hardware | ||
| 617 | let _ = r.osevent_ctrl().read().ostimer_intrflag().bit(); | ||
| 618 | |||
| 619 | if !super::wait_for_match_write_ready(r) { | ||
| 620 | super::prime_match_registers(r); | ||
| 621 | |||
| 622 | if !super::wait_for_match_write_ready(r) { | ||
| 623 | // If we can't safely program MATCH, wake immediately and leave INTENA masked. | ||
| 624 | waker.wake_by_ref(); | ||
| 625 | return; | ||
| 626 | } | ||
| 627 | } | ||
| 628 | |||
| 629 | // Program MATCH (Gray-coded). Write low then high, then fence. | ||
| 630 | let gray = bin_to_gray(timestamp); | ||
| 631 | let l = (gray & LOW_32_BIT_MASK) as u32; | ||
| 632 | |||
| 633 | let h = (((gray >> EVTIMER_HI_SHIFT) as u16) & EVTIMER_HI_MASK) as u16; | ||
| 634 | |||
| 635 | r.match_l().write(|w| unsafe { w.match_value().bits(l) }); | ||
| 636 | r.match_h().write(|w| unsafe { w.match_value().bits(h) }); | ||
| 637 | |||
| 638 | if !super::wait_for_match_write_complete(r) { | ||
| 639 | waker.wake_by_ref(); | ||
| 640 | return; | ||
| 641 | } | ||
| 642 | |||
| 643 | let now_after_program = super::now_ticks_read(); | ||
| 644 | let intrflag_set = r.osevent_ctrl().read().ostimer_intrflag().bit_is_set(); | ||
| 645 | if now_after_program >= timestamp && !intrflag_set { | ||
| 646 | waker.wake_by_ref(); | ||
| 647 | return; | ||
| 648 | } | ||
| 649 | |||
| 650 | // Enable peripheral interrupt | ||
| 651 | r.osevent_ctrl().write(|w| w.ostimer_intena().set_bit()); | ||
| 652 | }); | ||
| 653 | } | ||
| 654 | } | ||
| 655 | |||
| 656 | /// Install the global embassy-time driver and configure NVIC priority for OS_EVENT. | ||
| 657 | /// | ||
| 658 | /// # Parameters | ||
| 659 | /// * `priority` - Interrupt priority for the OSTIMER interrupt | ||
| 660 | /// * `frequency_hz` - Actual OSTIMER clock frequency in Hz (stored for future use) | ||
| 661 | /// | ||
| 662 | /// Note: The frequency parameter is currently accepted for API compatibility. | ||
| 663 | /// The embassy_time_driver macro handles driver registration automatically. | ||
| 664 | pub fn init(priority: crate::interrupt::Priority, frequency_hz: u64) { | ||
| 665 | let _clock_freq = unsafe { | ||
| 666 | enable_and_reset::<_OSTIMER0_TIME_DRIVER>(&OsTimerConfig { | ||
| 667 | power: PoweredClock::AlwaysEnabled, | ||
| 668 | source: OstimerClockSel::Clk1M, | ||
| 669 | }) | ||
| 670 | .expect("Enabling OsTimer clock should not fail") | ||
| 671 | }; | ||
| 672 | |||
| 673 | // Mask/clear at peripheral and set default MATCH | ||
| 674 | let r: &Regs = unsafe { &*pac::Ostimer0::ptr() }; | ||
| 675 | super::prime_match_registers(r); | ||
| 676 | |||
| 677 | // Configure NVIC for timer operation | ||
| 678 | crate::interrupt::OS_EVENT.configure_for_timer(priority); | ||
| 679 | |||
| 680 | // Note: The embassy_time_driver macro automatically registers the driver | ||
| 681 | // The frequency parameter is accepted for future compatibility | ||
| 682 | let _ = frequency_hz; // Suppress unused parameter warning | ||
| 683 | } | ||
| 684 | |||
| 685 | // Export the global time driver expected by embassy-time | ||
| 686 | embassy_time_driver::time_driver_impl!(static DRIVER: Driver = Driver); | ||
| 687 | |||
| 688 | /// To be called from the OS_EVENT IRQ. | ||
| 689 | pub fn on_interrupt() { | ||
| 690 | let r: &Regs = unsafe { &*pac::Ostimer0::ptr() }; | ||
| 691 | |||
| 692 | // Critical section to prevent races with schedule_wake | ||
| 693 | critical_section::with(|_| { | ||
| 694 | // Check if interrupt is actually pending and handle it atomically | ||
| 695 | if r.osevent_ctrl().read().ostimer_intrflag().bit_is_set() { | ||
| 696 | // Clear flag and disable interrupt atomically | ||
| 697 | r.osevent_ctrl().write(|w| { | ||
| 698 | w.ostimer_intrflag() | ||
| 699 | .clear_bit_by_one() // Write-1-to-clear using safe helper | ||
| 700 | .ostimer_intena() | ||
| 701 | .clear_bit() | ||
| 702 | }); | ||
| 703 | |||
| 704 | // Wake the waiting task | ||
| 705 | TIMER_WAKER.wake(); | ||
| 706 | |||
| 707 | // Handle alarm callback if active and this interrupt is for the alarm | ||
| 708 | if ALARM_ACTIVE.load(Ordering::SeqCst) { | ||
| 709 | let current_time = now_ticks_read(); | ||
| 710 | let target_time = unsafe { ALARM_TARGET_TIME }; | ||
| 711 | |||
| 712 | // Check if current time is close to alarm target time (within 1000 ticks for timing variations) | ||
| 713 | if current_time >= target_time && current_time <= target_time + 1000 { | ||
| 714 | ALARM_ACTIVE.store(false, Ordering::SeqCst); | ||
| 715 | unsafe { ALARM_TARGET_TIME = 0 }; | ||
| 716 | |||
| 717 | // Execute callback if set | ||
| 718 | unsafe { | ||
| 719 | if let Some(callback) = ALARM_CALLBACK { | ||
| 720 | callback(); | ||
| 721 | } | ||
| 722 | } | ||
| 723 | |||
| 724 | // Set flag if provided | ||
| 725 | unsafe { | ||
| 726 | if let Some(flag) = ALARM_FLAG { | ||
| 727 | (*flag).store(true, Ordering::SeqCst); | ||
| 728 | } | ||
| 729 | } | ||
| 730 | } | ||
| 731 | } | ||
| 732 | } | ||
| 733 | }); | ||
| 734 | } | ||
| 735 | } | ||
| 736 | |||
| 737 | #[cfg(feature = "time")] | ||
| 738 | use crate::pac::interrupt; | ||
| 739 | |||
| 740 | #[cfg(feature = "time")] | ||
| 741 | #[allow(non_snake_case)] | ||
| 742 | #[interrupt] | ||
| 743 | fn OS_EVENT() { | ||
| 744 | time_driver::on_interrupt() | ||
| 745 | } | ||
diff --git a/embassy-mcxa/src/pins.rs b/embassy-mcxa/src/pins.rs new file mode 100644 index 000000000..fdf1b0a86 --- /dev/null +++ b/embassy-mcxa/src/pins.rs | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | //! Pin configuration helpers (separate from peripheral drivers). | ||
| 2 | use crate::pac; | ||
| 3 | |||
| 4 | pub unsafe fn configure_adc_pins() { | ||
| 5 | // P1_10 = ADC1_A8 | ||
| 6 | let port1 = &*pac::Port1::ptr(); | ||
| 7 | port1.pcr10().write(|w| { | ||
| 8 | w.ps() | ||
| 9 | .ps0() | ||
| 10 | .pe() | ||
| 11 | .pe0() | ||
| 12 | .sre() | ||
| 13 | .sre0() | ||
| 14 | .ode() | ||
| 15 | .ode0() | ||
| 16 | .dse() | ||
| 17 | .dse0() | ||
| 18 | .mux() | ||
| 19 | .mux0() | ||
| 20 | .ibe() | ||
| 21 | .ibe0() | ||
| 22 | .inv() | ||
| 23 | .inv0() | ||
| 24 | .lk() | ||
| 25 | .lk0() | ||
| 26 | }); | ||
| 27 | core::arch::asm!("dsb sy; isb sy"); | ||
| 28 | } | ||
diff --git a/embassy-mcxa/src/rtc.rs b/embassy-mcxa/src/rtc.rs new file mode 100644 index 000000000..f975d9c9f --- /dev/null +++ b/embassy-mcxa/src/rtc.rs | |||
| @@ -0,0 +1,453 @@ | |||
| 1 | //! RTC DateTime driver. | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | |||
| 4 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 5 | use maitake_sync::WaitCell; | ||
| 6 | |||
| 7 | use crate::clocks::with_clocks; | ||
| 8 | use crate::interrupt::typelevel::{Handler, Interrupt}; | ||
| 9 | use crate::pac; | ||
| 10 | use crate::pac::rtc0::cr::Um; | ||
| 11 | |||
| 12 | type Regs = pac::rtc0::RegisterBlock; | ||
| 13 | |||
| 14 | /// Global wait cell for alarm notifications | ||
| 15 | static WAKER: WaitCell = WaitCell::new(); | ||
| 16 | |||
| 17 | /// RTC interrupt handler. | ||
| 18 | pub struct InterruptHandler<I: Instance> { | ||
| 19 | _phantom: PhantomData<I>, | ||
| 20 | } | ||
| 21 | |||
| 22 | /// Trait for RTC peripheral instances | ||
| 23 | pub trait Instance: PeripheralType { | ||
| 24 | type Interrupt: Interrupt; | ||
| 25 | fn ptr() -> *const Regs; | ||
| 26 | } | ||
| 27 | |||
| 28 | /// Token for RTC0 | ||
| 29 | pub type Rtc0 = crate::peripherals::RTC0; | ||
| 30 | impl Instance for crate::peripherals::RTC0 { | ||
| 31 | type Interrupt = crate::interrupt::typelevel::RTC; | ||
| 32 | #[inline(always)] | ||
| 33 | fn ptr() -> *const Regs { | ||
| 34 | pac::Rtc0::ptr() | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | /// Number of days in a standard year | ||
| 39 | const DAYS_IN_A_YEAR: u32 = 365; | ||
| 40 | /// Number of seconds in a day | ||
| 41 | const SECONDS_IN_A_DAY: u32 = 86400; | ||
| 42 | /// Number of seconds in an hour | ||
| 43 | const SECONDS_IN_A_HOUR: u32 = 3600; | ||
| 44 | /// Number of seconds in a minute | ||
| 45 | const SECONDS_IN_A_MINUTE: u32 = 60; | ||
| 46 | /// Unix epoch start year | ||
| 47 | const YEAR_RANGE_START: u16 = 1970; | ||
| 48 | |||
| 49 | /// Date and time structure for RTC operations | ||
| 50 | #[derive(Debug, Clone, Copy)] | ||
| 51 | pub struct RtcDateTime { | ||
| 52 | pub year: u16, | ||
| 53 | pub month: u8, | ||
| 54 | pub day: u8, | ||
| 55 | pub hour: u8, | ||
| 56 | pub minute: u8, | ||
| 57 | pub second: u8, | ||
| 58 | } | ||
| 59 | #[derive(Copy, Clone)] | ||
| 60 | pub struct RtcConfig { | ||
| 61 | #[allow(dead_code)] | ||
| 62 | wakeup_select: bool, | ||
| 63 | update_mode: Um, | ||
| 64 | #[allow(dead_code)] | ||
| 65 | supervisor_access: bool, | ||
| 66 | compensation_interval: u8, | ||
| 67 | compensation_time: u8, | ||
| 68 | } | ||
| 69 | |||
| 70 | /// RTC interrupt enable flags | ||
| 71 | #[derive(Copy, Clone)] | ||
| 72 | pub struct RtcInterruptEnable; | ||
| 73 | impl RtcInterruptEnable { | ||
| 74 | pub const RTC_TIME_INVALID_INTERRUPT_ENABLE: u32 = 1 << 0; | ||
| 75 | pub const RTC_TIME_OVERFLOW_INTERRUPT_ENABLE: u32 = 1 << 1; | ||
| 76 | pub const RTC_ALARM_INTERRUPT_ENABLE: u32 = 1 << 2; | ||
| 77 | pub const RTC_SECONDS_INTERRUPT_ENABLE: u32 = 1 << 4; | ||
| 78 | } | ||
| 79 | |||
| 80 | /// Converts a DateTime structure to Unix timestamp (seconds since 1970-01-01) | ||
| 81 | /// | ||
| 82 | /// # Arguments | ||
| 83 | /// | ||
| 84 | /// * `datetime` - The date and time to convert | ||
| 85 | /// | ||
| 86 | /// # Returns | ||
| 87 | /// | ||
| 88 | /// Unix timestamp as u32 | ||
| 89 | /// | ||
| 90 | /// # Note | ||
| 91 | /// | ||
| 92 | /// This function handles leap years correctly. | ||
| 93 | pub fn convert_datetime_to_seconds(datetime: &RtcDateTime) -> u32 { | ||
| 94 | let month_days: [u16; 13] = [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; | ||
| 95 | |||
| 96 | let mut seconds = (datetime.year as u32 - 1970) * DAYS_IN_A_YEAR; | ||
| 97 | seconds += (datetime.year as u32 / 4) - (1970 / 4); | ||
| 98 | seconds += month_days[datetime.month as usize] as u32; | ||
| 99 | seconds += datetime.day as u32 - 1; | ||
| 100 | |||
| 101 | if (datetime.year & 3 == 0) && (datetime.month <= 2) { | ||
| 102 | seconds -= 1; | ||
| 103 | } | ||
| 104 | |||
| 105 | seconds = seconds * SECONDS_IN_A_DAY | ||
| 106 | + (datetime.hour as u32 * SECONDS_IN_A_HOUR) | ||
| 107 | + (datetime.minute as u32 * SECONDS_IN_A_MINUTE) | ||
| 108 | + datetime.second as u32; | ||
| 109 | |||
| 110 | seconds | ||
| 111 | } | ||
| 112 | |||
| 113 | /// Converts Unix timestamp to DateTime structure | ||
| 114 | /// | ||
| 115 | /// # Arguments | ||
| 116 | /// | ||
| 117 | /// * `seconds` - Unix timestamp (seconds since 1970-01-01) | ||
| 118 | /// | ||
| 119 | /// # Returns | ||
| 120 | /// | ||
| 121 | /// RtcDateTime structure with the converted date and time | ||
| 122 | /// | ||
| 123 | /// # Note | ||
| 124 | /// | ||
| 125 | /// This function handles leap years correctly. | ||
| 126 | pub fn convert_seconds_to_datetime(seconds: u32) -> RtcDateTime { | ||
| 127 | let mut seconds_remaining = seconds; | ||
| 128 | let mut days = seconds_remaining / SECONDS_IN_A_DAY + 1; | ||
| 129 | seconds_remaining %= SECONDS_IN_A_DAY; | ||
| 130 | |||
| 131 | let hour = (seconds_remaining / SECONDS_IN_A_HOUR) as u8; | ||
| 132 | seconds_remaining %= SECONDS_IN_A_HOUR; | ||
| 133 | let minute = (seconds_remaining / SECONDS_IN_A_MINUTE) as u8; | ||
| 134 | let second = (seconds_remaining % SECONDS_IN_A_MINUTE) as u8; | ||
| 135 | |||
| 136 | let mut year = YEAR_RANGE_START; | ||
| 137 | let mut days_in_year = DAYS_IN_A_YEAR; | ||
| 138 | |||
| 139 | while days > days_in_year { | ||
| 140 | days -= days_in_year; | ||
| 141 | year += 1; | ||
| 142 | |||
| 143 | days_in_year = if year.is_multiple_of(4) { | ||
| 144 | DAYS_IN_A_YEAR + 1 | ||
| 145 | } else { | ||
| 146 | DAYS_IN_A_YEAR | ||
| 147 | }; | ||
| 148 | } | ||
| 149 | |||
| 150 | let days_per_month = [ | ||
| 151 | 31, | ||
| 152 | if year.is_multiple_of(4) { 29 } else { 28 }, | ||
| 153 | 31, | ||
| 154 | 30, | ||
| 155 | 31, | ||
| 156 | 30, | ||
| 157 | 31, | ||
| 158 | 31, | ||
| 159 | 30, | ||
| 160 | 31, | ||
| 161 | 30, | ||
| 162 | 31, | ||
| 163 | ]; | ||
| 164 | |||
| 165 | let mut month = 1; | ||
| 166 | for (m, month_days) in days_per_month.iter().enumerate() { | ||
| 167 | let m = m + 1; | ||
| 168 | if days <= *month_days as u32 { | ||
| 169 | month = m; | ||
| 170 | break; | ||
| 171 | } else { | ||
| 172 | days -= *month_days as u32; | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | let day = days as u8; | ||
| 177 | |||
| 178 | RtcDateTime { | ||
| 179 | year, | ||
| 180 | month: month as u8, | ||
| 181 | day, | ||
| 182 | hour, | ||
| 183 | minute, | ||
| 184 | second, | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | /// Returns default RTC configuration | ||
| 189 | /// | ||
| 190 | /// # Returns | ||
| 191 | /// | ||
| 192 | /// RtcConfig with sensible default values: | ||
| 193 | /// - No wakeup selection | ||
| 194 | /// - Update mode 0 (immediate updates) | ||
| 195 | /// - No supervisor access restriction | ||
| 196 | /// - No compensation | ||
| 197 | pub fn get_default_config() -> RtcConfig { | ||
| 198 | RtcConfig { | ||
| 199 | wakeup_select: false, | ||
| 200 | update_mode: Um::Um0, | ||
| 201 | supervisor_access: false, | ||
| 202 | compensation_interval: 0, | ||
| 203 | compensation_time: 0, | ||
| 204 | } | ||
| 205 | } | ||
| 206 | /// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy) | ||
| 207 | pub struct Rtc<'a, I: Instance> { | ||
| 208 | _inst: core::marker::PhantomData<&'a mut I>, | ||
| 209 | } | ||
| 210 | |||
| 211 | impl<'a, I: Instance> Rtc<'a, I> { | ||
| 212 | /// Create a new instance of the real time clock. | ||
| 213 | pub fn new( | ||
| 214 | _inst: Peri<'a, I>, | ||
| 215 | _irq: impl crate::interrupt::typelevel::Binding<I::Interrupt, InterruptHandler<I>> + 'a, | ||
| 216 | config: RtcConfig, | ||
| 217 | ) -> Self { | ||
| 218 | let rtc = unsafe { &*I::ptr() }; | ||
| 219 | |||
| 220 | // 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 | ||
| 222 | let clocks = with_clocks(|c| c.clk_16k_vsys.clone()); | ||
| 223 | match clocks { | ||
| 224 | None => panic!("Clocks have not been initialized"), | ||
| 225 | Some(None) => panic!("Clocks initialized, but clk_16k_vsys not active"), | ||
| 226 | Some(Some(_)) => {} | ||
| 227 | } | ||
| 228 | |||
| 229 | // RTC reset | ||
| 230 | rtc.cr().modify(|_, w| w.swr().set_bit()); | ||
| 231 | rtc.cr().modify(|_, w| w.swr().clear_bit()); | ||
| 232 | rtc.tsr().write(|w| unsafe { w.bits(1) }); | ||
| 233 | |||
| 234 | rtc.cr().modify(|_, w| w.um().variant(config.update_mode)); | ||
| 235 | |||
| 236 | rtc.tcr().modify(|_, w| unsafe { | ||
| 237 | w.cir() | ||
| 238 | .bits(config.compensation_interval) | ||
| 239 | .tcr() | ||
| 240 | .bits(config.compensation_time) | ||
| 241 | }); | ||
| 242 | |||
| 243 | // Enable RTC interrupt | ||
| 244 | I::Interrupt::unpend(); | ||
| 245 | unsafe { I::Interrupt::enable() }; | ||
| 246 | |||
| 247 | Self { | ||
| 248 | _inst: core::marker::PhantomData, | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | /// Set the current date and time | ||
| 253 | /// | ||
| 254 | /// # Arguments | ||
| 255 | /// | ||
| 256 | /// * `datetime` - The date and time to set | ||
| 257 | /// | ||
| 258 | /// # Note | ||
| 259 | /// | ||
| 260 | /// The datetime is converted to Unix timestamp and written to the time seconds register. | ||
| 261 | pub fn set_datetime(&self, datetime: RtcDateTime) { | ||
| 262 | let rtc = unsafe { &*I::ptr() }; | ||
| 263 | let seconds = convert_datetime_to_seconds(&datetime); | ||
| 264 | rtc.tsr().write(|w| unsafe { w.bits(seconds) }); | ||
| 265 | } | ||
| 266 | |||
| 267 | /// Get the current date and time | ||
| 268 | /// | ||
| 269 | /// # Returns | ||
| 270 | /// | ||
| 271 | /// Current date and time as RtcDateTime | ||
| 272 | /// | ||
| 273 | /// # Note | ||
| 274 | /// | ||
| 275 | /// Reads the current Unix timestamp from the time seconds register and converts it. | ||
| 276 | pub fn get_datetime(&self) -> RtcDateTime { | ||
| 277 | let rtc = unsafe { &*I::ptr() }; | ||
| 278 | let seconds = rtc.tsr().read().bits(); | ||
| 279 | convert_seconds_to_datetime(seconds) | ||
| 280 | } | ||
| 281 | |||
| 282 | /// Set the alarm date and time | ||
| 283 | /// | ||
| 284 | /// # Arguments | ||
| 285 | /// | ||
| 286 | /// * `alarm` - The date and time when the alarm should trigger | ||
| 287 | /// | ||
| 288 | /// # Note | ||
| 289 | /// | ||
| 290 | /// This function: | ||
| 291 | /// - Clears any existing alarm by writing 0 to the alarm register | ||
| 292 | /// - Waits for the clear operation to complete | ||
| 293 | /// - Sets the new alarm time | ||
| 294 | /// - Waits for the write operation to complete | ||
| 295 | /// - Uses timeouts to prevent infinite loops | ||
| 296 | /// - Enables the alarm interrupt after setting | ||
| 297 | pub fn set_alarm(&self, alarm: RtcDateTime) { | ||
| 298 | let rtc = unsafe { &*I::ptr() }; | ||
| 299 | let seconds = convert_datetime_to_seconds(&alarm); | ||
| 300 | |||
| 301 | rtc.tar().write(|w| unsafe { w.bits(0) }); | ||
| 302 | let mut timeout = 10000; | ||
| 303 | while rtc.tar().read().bits() != 0 && timeout > 0 { | ||
| 304 | timeout -= 1; | ||
| 305 | } | ||
| 306 | |||
| 307 | rtc.tar().write(|w| unsafe { w.bits(seconds) }); | ||
| 308 | |||
| 309 | let mut timeout = 10000; | ||
| 310 | while rtc.tar().read().bits() != seconds && timeout > 0 { | ||
| 311 | timeout -= 1; | ||
| 312 | } | ||
| 313 | |||
| 314 | self.set_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE); | ||
| 315 | } | ||
| 316 | |||
| 317 | /// Get the current alarm date and time | ||
| 318 | /// | ||
| 319 | /// # Returns | ||
| 320 | /// | ||
| 321 | /// Alarm date and time as RtcDateTime | ||
| 322 | /// | ||
| 323 | /// # Note | ||
| 324 | /// | ||
| 325 | /// Reads the alarm timestamp from the time alarm register and converts it. | ||
| 326 | pub fn get_alarm(&self) -> RtcDateTime { | ||
| 327 | let rtc = unsafe { &*I::ptr() }; | ||
| 328 | let alarm_seconds = rtc.tar().read().bits(); | ||
| 329 | convert_seconds_to_datetime(alarm_seconds) | ||
| 330 | } | ||
| 331 | |||
| 332 | /// Start the RTC time counter | ||
| 333 | /// | ||
| 334 | /// # Note | ||
| 335 | /// | ||
| 336 | /// Sets the Time Counter Enable (TCE) bit in the status register. | ||
| 337 | pub fn start(&self) { | ||
| 338 | let rtc = unsafe { &*I::ptr() }; | ||
| 339 | rtc.sr().modify(|_, w| w.tce().set_bit()); | ||
| 340 | } | ||
| 341 | |||
| 342 | /// Stop the RTC time counter | ||
| 343 | /// | ||
| 344 | /// # Note | ||
| 345 | /// | ||
| 346 | /// Clears the Time Counter Enable (TCE) bit in the status register. | ||
| 347 | pub fn stop(&self) { | ||
| 348 | let rtc = unsafe { &*I::ptr() }; | ||
| 349 | rtc.sr().modify(|_, w| w.tce().clear_bit()); | ||
| 350 | } | ||
| 351 | |||
| 352 | /// Enable specific RTC interrupts | ||
| 353 | /// | ||
| 354 | /// # Arguments | ||
| 355 | /// | ||
| 356 | /// * `mask` - Bitmask of interrupts to enable (use RtcInterruptEnable constants) | ||
| 357 | /// | ||
| 358 | /// # Note | ||
| 359 | /// | ||
| 360 | /// This function enables the specified interrupt types and resets the alarm occurred flag. | ||
| 361 | /// Available interrupts: | ||
| 362 | /// - Time Invalid Interrupt | ||
| 363 | /// - Time Overflow Interrupt | ||
| 364 | /// - Alarm Interrupt | ||
| 365 | /// - Seconds Interrupt | ||
| 366 | pub fn set_interrupt(&self, mask: u32) { | ||
| 367 | let rtc = unsafe { &*I::ptr() }; | ||
| 368 | |||
| 369 | if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 { | ||
| 370 | rtc.ier().modify(|_, w| w.tiie().tiie_1()); | ||
| 371 | } | ||
| 372 | if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 { | ||
| 373 | rtc.ier().modify(|_, w| w.toie().toie_1()); | ||
| 374 | } | ||
| 375 | if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 { | ||
| 376 | rtc.ier().modify(|_, w| w.taie().taie_1()); | ||
| 377 | } | ||
| 378 | if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 { | ||
| 379 | rtc.ier().modify(|_, w| w.tsie().tsie_1()); | ||
| 380 | } | ||
| 381 | } | ||
| 382 | |||
| 383 | /// Disable specific RTC interrupts | ||
| 384 | /// | ||
| 385 | /// # Arguments | ||
| 386 | /// | ||
| 387 | /// * `mask` - Bitmask of interrupts to disable (use RtcInterruptEnable constants) | ||
| 388 | /// | ||
| 389 | /// # Note | ||
| 390 | /// | ||
| 391 | /// This function disables the specified interrupt types. | ||
| 392 | pub fn disable_interrupt(&self, mask: u32) { | ||
| 393 | let rtc = unsafe { &*I::ptr() }; | ||
| 394 | |||
| 395 | if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 { | ||
| 396 | rtc.ier().modify(|_, w| w.tiie().tiie_0()); | ||
| 397 | } | ||
| 398 | if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 { | ||
| 399 | rtc.ier().modify(|_, w| w.toie().toie_0()); | ||
| 400 | } | ||
| 401 | if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 { | ||
| 402 | rtc.ier().modify(|_, w| w.taie().taie_0()); | ||
| 403 | } | ||
| 404 | if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 { | ||
| 405 | rtc.ier().modify(|_, w| w.tsie().tsie_0()); | ||
| 406 | } | ||
| 407 | } | ||
| 408 | |||
| 409 | /// Clear the alarm interrupt flag | ||
| 410 | /// | ||
| 411 | /// # Note | ||
| 412 | /// | ||
| 413 | /// This function clears the Time Alarm Interrupt Enable bit. | ||
| 414 | pub fn clear_alarm_flag(&self) { | ||
| 415 | let rtc = unsafe { &*I::ptr() }; | ||
| 416 | rtc.ier().modify(|_, w| w.taie().clear_bit()); | ||
| 417 | } | ||
| 418 | |||
| 419 | /// Wait for an RTC alarm to trigger. | ||
| 420 | /// | ||
| 421 | /// # Arguments | ||
| 422 | /// | ||
| 423 | /// * `alarm` - The date and time when the alarm should trigger | ||
| 424 | /// 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. | ||
| 426 | pub async fn wait_for_alarm(&mut self, alarm: RtcDateTime) { | ||
| 427 | let wait = WAKER.subscribe().await; | ||
| 428 | |||
| 429 | self.set_alarm(alarm); | ||
| 430 | self.start(); | ||
| 431 | |||
| 432 | // REVISIT: propagate error? | ||
| 433 | let _ = wait.await; | ||
| 434 | |||
| 435 | // Clear the interrupt and disable the alarm after waking up | ||
| 436 | self.disable_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE); | ||
| 437 | } | ||
| 438 | } | ||
| 439 | |||
| 440 | /// RTC interrupt handler | ||
| 441 | /// | ||
| 442 | /// This struct implements the interrupt handler for RTC events. | ||
| 443 | impl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> { | ||
| 444 | unsafe fn on_interrupt() { | ||
| 445 | let rtc = &*pac::Rtc0::ptr(); | ||
| 446 | // Check if this is actually a time alarm interrupt | ||
| 447 | let sr = rtc.sr().read(); | ||
| 448 | if sr.taf().bit_is_set() { | ||
| 449 | rtc.ier().modify(|_, w| w.taie().clear_bit()); | ||
| 450 | WAKER.wake(); | ||
| 451 | } | ||
| 452 | } | ||
| 453 | } | ||
