diff options
| author | everdrone <[email protected]> | 2025-11-11 15:48:56 +0100 |
|---|---|---|
| committer | everdrone <[email protected]> | 2025-11-11 15:48:56 +0100 |
| commit | cede7216861a82b0db55f5a88afb3acf2ace6c4b (patch) | |
| tree | d92fb578897c77f51317318c5b180931b7b25c63 /embassy-stm32 | |
| parent | cf55b39f9a54cf3ed01f52c0565a36a444174235 (diff) | |
| parent | 3d1f09597335d3681699ba09a77da4b39ed984fd (diff) | |
Merge branch main into n6
Diffstat (limited to 'embassy-stm32')
114 files changed, 3752 insertions, 1553 deletions
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 190e68d6d..33c7b5da5 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md | |||
| @@ -5,9 +5,14 @@ All notable changes to this project will be documented in this file. | |||
| 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), |
| 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). |
| 7 | 7 | ||
| 8 | <!-- next-header --> | ||
| 9 | ## Unreleased - ReleaseDate | 8 | ## Unreleased - ReleaseDate |
| 10 | 9 | ||
| 10 | - feat: timer: Add 32-bit timer support to SimplePwm waveform_up method following waveform pattern ([#4717](https://github.com/embassy-rs/embassy/pull/4717)) | ||
| 11 | - feat: Add support for injected ADC measurements for g4 ([#4840](https://github.com/embassy-rs/embassy/pull/4840)) | ||
| 12 | - feat: Implement into_ring_buffered for g4 ([#4840](https://github.com/embassy-rs/embassy/pull/4840)) | ||
| 13 | - feat: Add support for 13-bit address and 16-bit data SDRAM chips | ||
| 14 | - feat: stm32/hrtim add new_chx_with_config to provide pin configuration | ||
| 15 | - fix flash erase on L4 & L5 | ||
| 11 | - fix: Fixed STM32H5 builds requiring time feature | 16 | - fix: Fixed STM32H5 builds requiring time feature |
| 12 | - feat: Derive Clone, Copy for QSPI Config | 17 | - feat: Derive Clone, Copy for QSPI Config |
| 13 | - fix: stm32/i2c in master mode (blocking): subsequent transmissions failed after a NACK was received | 18 | - fix: stm32/i2c in master mode (blocking): subsequent transmissions failed after a NACK was received |
| @@ -22,10 +27,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 22 | - fix: handle address and data-length errors in OSPI | 27 | - fix: handle address and data-length errors in OSPI |
| 23 | - feat: Allow OSPI DMA writes larger than 64kB using chunking | 28 | - feat: Allow OSPI DMA writes larger than 64kB using chunking |
| 24 | - feat: More ADC enums for g0 PAC, API change for oversampling, allow separate sample times | 29 | - feat: More ADC enums for g0 PAC, API change for oversampling, allow separate sample times |
| 30 | - feat: Add USB CRS sync support for STM32C071 | ||
| 31 | - fix: RTC register definition for STM32L4P5 and L4Q5 as they use v3 register map. | ||
| 32 | - fix: Cut down the capabilities of the STM32L412 and L422 RTC as those are missing binary timer mode and underflow interrupt. | ||
| 33 | - fix: Allow configuration of the internal pull up/down resistors on the pins for the Qei peripheral, as well as the Qei decoder mode. | ||
| 34 | - feat: stm32/rcc/mco: Added support for IO driver strength when using Master Clock Out IO. This changes signature on Mco::new taking a McoConfig struct ([#4679](https://github.com/embassy-rs/embassy/pull/4679)) | ||
| 35 | - feat: derive Clone, Copy and defmt::Format for all SPI-related configs | ||
| 36 | - feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668)) | ||
| 37 | - feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options | ||
| 38 | - change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer | ||
| 39 | - fix: Prevent a HardFault crash on STM32H5 devices by changing `uid()` to return `[u8; 12]` by value instead of a reference. (Fixes #2696) | ||
| 40 | - change: timer: added output compare values | ||
| 41 | - feat: timer: add ability to set master mode | ||
| 42 | - fix: sdmmc: don't wait for DBCKEND flag on sdmmc_v2 devices as it never fires (Fixes #4723) | ||
| 43 | - fix: usart: fix race condition in ringbuffered usart | ||
| 44 | - feat: Add backup_sram::init() for H5 devices to access BKPSRAM | ||
| 45 | - feat: Add I2C MultiMaster (Slave) support for I2C v1 | ||
| 46 | - feat: stm32/fdcan: add ability to control automatic recovery from bus off ([#4821](https://github.com/embassy-rs/embassy/pull/4821)) | ||
| 47 | - low-power: update rtc api to allow reconfig | ||
| 48 | - adc: consolidate ringbuffer | ||
| 49 | - feat: Added RTC low-power support for STM32WLEx ([#4716](https://github.com/embassy-rs/embassy/pull/4716)) | ||
| 50 | - fix: Correct STM32WBA VREFBUFTRIM values | ||
| 51 | - low_power: remove stop_with rtc and initialize in init if low-power feature enabled. | ||
| 52 | - feat: stm32/dsi support zero parameter commands in `write_cmd` ([#4847](https://github.com/embassy-rs/embassy/pull/4847)) | ||
| 53 | - feat: stm32/spi: added support for slave mode ([#4388](https://github.com/embassy-rs/embassy/pull/4388)) | ||
| 54 | - chore: Updated stm32-metapac and stm32-data dependencies | ||
| 55 | - adc: reogranize and cleanup somewhat. require sample_time to be passed on conversion | ||
| 56 | - fix: stm32/i2c v2 slave: prevent misaligned reads, error false positives, and incorrect counts of bytes read/written | ||
| 25 | 57 | ||
| 26 | ## 0.4.0 - 2025-08-26 | 58 | ## 0.4.0 - 2025-08-26 |
| 27 | 59 | ||
| 28 | - feat: stm32/sai: make NODIV independent of MCKDIV | 60 | - feat: stm32/sai: make NODIV independent of MCKDIV |
| 29 | - fix: stm32/sai: fix WB MCKDIV | 61 | - fix: stm32/sai: fix WB MCKDIV |
| 30 | - fix: stm32/i2c: pull-down was enabled instead of pull-none when no internal pull-up was needed. | 62 | - fix: stm32/i2c: pull-down was enabled instead of pull-none when no internal pull-up was needed. |
| 31 | - feat: Improve blocking hash speed | 63 | - feat: Improve blocking hash speed |
| @@ -34,6 +66,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 34 | - chore: Updated stm32-metapac and stm32-data dependencies | 66 | - chore: Updated stm32-metapac and stm32-data dependencies |
| 35 | - feat: stm32/adc/v3: allow DMA reads to loop through enable channels | 67 | - feat: stm32/adc/v3: allow DMA reads to loop through enable channels |
| 36 | - fix: Fix XSPI not disabling alternate bytes when they were previously enabled | 68 | - fix: Fix XSPI not disabling alternate bytes when they were previously enabled |
| 69 | - feat: stm32/adc/v3: added support for Continuous DMA configuration | ||
| 37 | - fix: Fix stm32h7rs init when using external flash via XSPI | 70 | - fix: Fix stm32h7rs init when using external flash via XSPI |
| 38 | - feat: Add Adc::new_with_clock() to configure analog clock | 71 | - feat: Add Adc::new_with_clock() to configure analog clock |
| 39 | - feat: Add GPDMA linked-list + ringbuffer support ([#3923](https://github.com/embassy-rs/embassy/pull/3923)) | 72 | - feat: Add GPDMA linked-list + ringbuffer support ([#3923](https://github.com/embassy-rs/embassy/pull/3923)) |
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index fabfa8342..0a1854dab 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | name = "embassy-stm32" | 2 | name = "embassy-stm32" |
| 3 | version = "0.4.0" | 3 | version = "0.4.0" |
| 4 | edition = "2021" | 4 | edition = "2024" |
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | description = "Embassy Hardware Abstraction Layer (HAL) for ST STM32 series microcontrollers" | 6 | description = "Embassy Hardware Abstraction Layer (HAL) for ST STM32 series microcontrollers" |
| 7 | keywords = ["embedded", "async", "stm32", "hal", "embedded-hal"] | 7 | keywords = ["embedded", "async", "stm32", "hal", "embedded-hal"] |
| @@ -66,6 +66,10 @@ build = [ | |||
| 66 | {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32l431cb", "time", "time-driver-any"]}, | 66 | {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32l431cb", "time", "time-driver-any"]}, |
| 67 | {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32l476vg", "time", "time-driver-any"]}, | 67 | {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32l476vg", "time", "time-driver-any"]}, |
| 68 | {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32l422cb", "time", "time-driver-any"]}, | 68 | {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32l422cb", "time", "time-driver-any"]}, |
| 69 | {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32l4p5ae", "time", "time-driver-any", "single-bank"]}, | ||
| 70 | {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32l4q5zg", "time", "time-driver-any", "single-bank"]}, | ||
| 71 | {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32l4r9vi", "time", "time-driver-any", "dual-bank"]}, | ||
| 72 | {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32l4s7vi", "time", "time-driver-any", "dual-bank"]}, | ||
| 69 | {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32wb15cc", "time", "time-driver-any"]}, | 73 | {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32wb15cc", "time", "time-driver-any"]}, |
| 70 | {target = "thumbv6m-none-eabi", features = ["defmt", "exti", "stm32l072cz", "time", "time-driver-any"]}, | 74 | {target = "thumbv6m-none-eabi", features = ["defmt", "exti", "stm32l072cz", "time", "time-driver-any"]}, |
| 71 | {target = "thumbv6m-none-eabi", features = ["defmt", "exti", "stm32l041f6", "time", "time-driver-any"]}, | 75 | {target = "thumbv6m-none-eabi", features = ["defmt", "exti", "stm32l041f6", "time", "time-driver-any"]}, |
| @@ -174,11 +178,10 @@ cortex-m = "0.7.6" | |||
| 174 | futures-util = { version = "0.3.30", default-features = false } | 178 | futures-util = { version = "0.3.30", default-features = false } |
| 175 | sdio-host = "0.9.0" | 179 | sdio-host = "0.9.0" |
| 176 | critical-section = "1.1" | 180 | critical-section = "1.1" |
| 177 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-3cf72eac610259fd78ef16f1c63be69a144d75f7" } | ||
| 178 | 181 | ||
| 179 | vcell = "0.1.3" | 182 | vcell = "0.1.3" |
| 180 | nb = "1.0.0" | 183 | nb = "1.0.0" |
| 181 | stm32-fmc = "0.3.0" | 184 | stm32-fmc = "0.4.0" |
| 182 | cfg-if = "1.0.0" | 185 | cfg-if = "1.0.0" |
| 183 | embedded-io = { version = "0.6.0" } | 186 | embedded-io = { version = "0.6.0" } |
| 184 | embedded-io-async = { version = "0.6.1" } | 187 | embedded-io-async = { version = "0.6.1" } |
| @@ -194,16 +197,22 @@ block-device-driver = { version = "0.2" } | |||
| 194 | aligned = "0.4.1" | 197 | aligned = "0.4.1" |
| 195 | heapless = "0.9.1" | 198 | heapless = "0.9.1" |
| 196 | 199 | ||
| 197 | [dev-dependencies] | 200 | #stm32-metapac = { version = "18" } |
| 198 | critical-section = { version = "1.1", features = ["std"] } | 201 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-22374e3344a2c9150b9b3d4da45c03f398fdc54e" } |
| 199 | proptest = "1.5.0" | ||
| 200 | proptest-state-machine = "0.3.0" | ||
| 201 | 202 | ||
| 202 | [build-dependencies] | 203 | [build-dependencies] |
| 204 | #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} | ||
| 205 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-22374e3344a2c9150b9b3d4da45c03f398fdc54e", default-features = false, features = ["metadata"] } | ||
| 206 | |||
| 203 | proc-macro2 = "1.0.36" | 207 | proc-macro2 = "1.0.36" |
| 204 | quote = "1.0.15" | 208 | quote = "1.0.15" |
| 205 | 209 | ||
| 206 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-3cf72eac610259fd78ef16f1c63be69a144d75f7", default-features = false, features = ["metadata"] } | 210 | |
| 211 | [dev-dependencies] | ||
| 212 | critical-section = { version = "1.1", features = ["std"] } | ||
| 213 | proptest = "1.5.0" | ||
| 214 | proptest-state-machine = "0.3.0" | ||
| 215 | |||
| 207 | 216 | ||
| 208 | [features] | 217 | [features] |
| 209 | default = ["rt"] | 218 | default = ["rt"] |
| @@ -224,6 +233,10 @@ defmt = [ | |||
| 224 | "embassy-usb-synopsys-otg/defmt", | 233 | "embassy-usb-synopsys-otg/defmt", |
| 225 | "stm32-metapac/defmt" | 234 | "stm32-metapac/defmt" |
| 226 | ] | 235 | ] |
| 236 | ## Use log for logging | ||
| 237 | log = ["dep:log"] | ||
| 238 | ## Enable chrono support | ||
| 239 | chrono = ["dep:chrono"] | ||
| 227 | 240 | ||
| 228 | exti = [] | 241 | exti = [] |
| 229 | low-power = [ "dep:embassy-executor", "embassy-executor?/arch-cortex-m", "time" ] | 242 | low-power = [ "dep:embassy-executor", "embassy-executor?/arch-cortex-m", "time" ] |
| @@ -304,6 +317,7 @@ single-bank = [] | |||
| 304 | 317 | ||
| 305 | ## internal use only | 318 | ## internal use only |
| 306 | _split-pins-enabled = [] | 319 | _split-pins-enabled = [] |
| 320 | _allow-disable-rtc = [] | ||
| 307 | 321 | ||
| 308 | ## internal use only | 322 | ## internal use only |
| 309 | _dual-core = [] | 323 | _dual-core = [] |
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 861f3fcee..09a05ce68 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -9,8 +9,8 @@ use proc_macro2::{Ident, TokenStream}; | |||
| 9 | use quote::{format_ident, quote}; | 9 | use quote::{format_ident, quote}; |
| 10 | use stm32_metapac::metadata::ir::BitOffset; | 10 | use stm32_metapac::metadata::ir::BitOffset; |
| 11 | use stm32_metapac::metadata::{ | 11 | use stm32_metapac::metadata::{ |
| 12 | MemoryRegion, MemoryRegionKind, PeripheralRccKernelClock, PeripheralRccRegister, PeripheralRegisters, StopMode, | 12 | ALL_CHIPS, ALL_PERIPHERAL_VERSIONS, METADATA, MemoryRegion, MemoryRegionKind, PeripheralRccKernelClock, |
| 13 | ALL_CHIPS, ALL_PERIPHERAL_VERSIONS, METADATA, | 13 | PeripheralRccRegister, PeripheralRegisters, StopMode, |
| 14 | }; | 14 | }; |
| 15 | 15 | ||
| 16 | #[path = "./build_common.rs"] | 16 | #[path = "./build_common.rs"] |
| @@ -105,13 +105,17 @@ fn main() { | |||
| 105 | } | 105 | } |
| 106 | (false, false) => { | 106 | (false, false) => { |
| 107 | if METADATA.memory.len() != 1 { | 107 | if METADATA.memory.len() != 1 { |
| 108 | panic!("Chip supports single and dual bank configuration. No Cargo feature to select one is enabled. Use the 'single-bank' or 'dual-bank' feature to make your selection") | 108 | panic!( |
| 109 | "Chip supports single and dual bank configuration. No Cargo feature to select one is enabled. Use the 'single-bank' or 'dual-bank' feature to make your selection" | ||
| 110 | ) | ||
| 109 | } | 111 | } |
| 110 | METADATA.memory[0] | 112 | METADATA.memory[0] |
| 111 | } | 113 | } |
| 112 | } | 114 | } |
| 113 | }; | 115 | }; |
| 114 | 116 | ||
| 117 | let has_bkpsram = memory.iter().any(|m| m.name == "BKPSRAM"); | ||
| 118 | |||
| 115 | // ======== | 119 | // ======== |
| 116 | // Generate singletons | 120 | // Generate singletons |
| 117 | 121 | ||
| @@ -122,6 +126,13 @@ fn main() { | |||
| 122 | singletons.push(p.name.to_string()); | 126 | singletons.push(p.name.to_string()); |
| 123 | } | 127 | } |
| 124 | 128 | ||
| 129 | cfgs.declare("backup_sram"); | ||
| 130 | |||
| 131 | if has_bkpsram { | ||
| 132 | singletons.push("BKPSRAM".to_string()); | ||
| 133 | cfgs.enable("backup_sram") | ||
| 134 | } | ||
| 135 | |||
| 125 | // generate one singleton per peripheral (with many exceptions...) | 136 | // generate one singleton per peripheral (with many exceptions...) |
| 126 | for p in METADATA.peripherals { | 137 | for p in METADATA.peripherals { |
| 127 | if let Some(r) = &p.registers { | 138 | if let Some(r) = &p.registers { |
| @@ -1995,6 +2006,18 @@ fn main() { | |||
| 1995 | )); | 2006 | )); |
| 1996 | 2007 | ||
| 1997 | // ======== | 2008 | // ======== |
| 2009 | // Generate backup sram constants | ||
| 2010 | if let Some(m) = memory.iter().find(|m| m.name == "BKPSRAM") { | ||
| 2011 | let bkpsram_base = m.address as usize; | ||
| 2012 | let bkpsram_size = m.size as usize; | ||
| 2013 | |||
| 2014 | g.extend(quote!( | ||
| 2015 | pub const BKPSRAM_BASE: usize = #bkpsram_base; | ||
| 2016 | pub const BKPSRAM_SIZE: usize = #bkpsram_size; | ||
| 2017 | )); | ||
| 2018 | } | ||
| 2019 | |||
| 2020 | // ======== | ||
| 1998 | // Generate flash constants | 2021 | // Generate flash constants |
| 1999 | 2022 | ||
| 2000 | if has_flash { | 2023 | if has_flash { |
| @@ -2323,6 +2346,10 @@ fn mem_filter(chip: &str, region: &str) -> bool { | |||
| 2323 | return false; | 2346 | return false; |
| 2324 | } | 2347 | } |
| 2325 | 2348 | ||
| 2349 | if region.starts_with("SDRAM_") || region.starts_with("FMC_") || region.starts_with("OCTOSPI_") { | ||
| 2350 | return false; | ||
| 2351 | } | ||
| 2352 | |||
| 2326 | true | 2353 | true |
| 2327 | } | 2354 | } |
| 2328 | 2355 | ||
diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index 255dc7956..befa8ed4a 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs | |||
| @@ -4,7 +4,7 @@ use pac::adc::vals::{Adc4Dmacfg as Dmacfg, Adc4Exten as Exten, Adc4OversamplingR | |||
| 4 | #[cfg(stm32wba)] | 4 | #[cfg(stm32wba)] |
| 5 | use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel}; | 5 | use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel}; |
| 6 | 6 | ||
| 7 | use super::{blocking_delay_us, AdcChannel, AnyAdcChannel, RxDma4, SealedAdcChannel}; | 7 | use super::{AdcChannel, AnyAdcChannel, RxDma4, SealedAdcChannel, blocking_delay_us}; |
| 8 | use crate::dma::Transfer; | 8 | use crate::dma::Transfer; |
| 9 | #[cfg(stm32u5)] | 9 | #[cfg(stm32u5)] |
| 10 | pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr; | 10 | pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr; |
| @@ -15,7 +15,7 @@ pub use crate::pac::adc::vals::{Adc4Presc as Presc, Adc4Res as Resolution, Adc4S | |||
| 15 | #[cfg(stm32wba)] | 15 | #[cfg(stm32wba)] |
| 16 | pub use crate::pac::adc::vals::{Presc, Res as Resolution, SampleTime}; | 16 | pub use crate::pac::adc::vals::{Presc, Res as Resolution, SampleTime}; |
| 17 | use crate::time::Hertz; | 17 | use crate::time::Hertz; |
| 18 | use crate::{pac, rcc, Peri}; | 18 | use crate::{Peri, pac, rcc}; |
| 19 | 19 | ||
| 20 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55); | 20 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55); |
| 21 | 21 | ||
| @@ -208,7 +208,10 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 208 | info!("ADC4 frequency set to {}", frequency); | 208 | info!("ADC4 frequency set to {}", frequency); |
| 209 | 209 | ||
| 210 | if frequency > MAX_ADC_CLK_FREQ { | 210 | if frequency > MAX_ADC_CLK_FREQ { |
| 211 | panic!("Maximal allowed frequency for ADC4 is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); | 211 | panic!( |
| 212 | "Maximal allowed frequency for ADC4 is {} MHz and it varies with different packages, refer to ST docs for more information.", | ||
| 213 | MAX_ADC_CLK_FREQ.0 / 1_000_000 | ||
| 214 | ); | ||
| 212 | } | 215 | } |
| 213 | 216 | ||
| 214 | let mut s = Self { adc }; | 217 | let mut s = Self { adc }; |
| @@ -324,18 +327,6 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 324 | Dac {} | 327 | Dac {} |
| 325 | } | 328 | } |
| 326 | 329 | ||
| 327 | /// Set the ADC sample time. | ||
| 328 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 329 | T::regs().smpr().modify(|w| { | ||
| 330 | w.set_smp(0, sample_time); | ||
| 331 | }); | ||
| 332 | } | ||
| 333 | |||
| 334 | /// Get the ADC sample time. | ||
| 335 | pub fn sample_time(&self) -> SampleTime { | ||
| 336 | T::regs().smpr().read().smp(0) | ||
| 337 | } | ||
| 338 | |||
| 339 | /// Set the ADC resolution. | 330 | /// Set the ADC resolution. |
| 340 | pub fn set_resolution(&mut self, resolution: Resolution) { | 331 | pub fn set_resolution(&mut self, resolution: Resolution) { |
| 341 | T::regs().cfgr1().modify(|w| w.set_res(resolution.into())); | 332 | T::regs().cfgr1().modify(|w| w.set_res(resolution.into())); |
| @@ -384,7 +375,11 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 384 | } | 375 | } |
| 385 | 376 | ||
| 386 | /// Read an ADC channel. | 377 | /// Read an ADC channel. |
| 387 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 378 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { |
| 379 | T::regs().smpr().modify(|w| { | ||
| 380 | w.set_smp(0, sample_time); | ||
| 381 | }); | ||
| 382 | |||
| 388 | channel.setup(); | 383 | channel.setup(); |
| 389 | 384 | ||
| 390 | // Select channel | 385 | // Select channel |
diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index f2837a8f1..1869993a5 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs | |||
| @@ -4,11 +4,11 @@ use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr}; | |||
| 4 | use pac::adccommon::vals::Presc; | 4 | use pac::adccommon::vals::Presc; |
| 5 | 5 | ||
| 6 | use super::{ | 6 | use super::{ |
| 7 | blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, | 7 | Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, blocking_delay_us, |
| 8 | }; | 8 | }; |
| 9 | use crate::dma::Transfer; | 9 | use crate::dma::Transfer; |
| 10 | use crate::time::Hertz; | 10 | use crate::time::Hertz; |
| 11 | use crate::{pac, rcc, Peri}; | 11 | use crate::{Peri, pac, rcc}; |
| 12 | 12 | ||
| 13 | /// Default VREF voltage used for sample conversion to millivolts. | 13 | /// Default VREF voltage used for sample conversion to millivolts. |
| 14 | pub const VREF_DEFAULT_MV: u32 = 3300; | 14 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| @@ -19,33 +19,17 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(25); | |||
| 19 | 19 | ||
| 20 | const TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US: u32 = 20; | 20 | const TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US: u32 = 20; |
| 21 | 21 | ||
| 22 | const TEMP_CHANNEL: u8 = 9; | ||
| 23 | const VREF_CHANNEL: u8 = 10; | ||
| 24 | |||
| 25 | const NUM_HW_CHANNELS: u8 = 22; | 22 | const NUM_HW_CHANNELS: u8 = 22; |
| 26 | const CHSELR_SQ_SIZE: usize = 8; | 23 | const CHSELR_SQ_SIZE: usize = 8; |
| 27 | const CHSELR_SQ_MAX_CHANNEL: u8 = 14; | 24 | const CHSELR_SQ_MAX_CHANNEL: u8 = 14; |
| 28 | const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111; | 25 | const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111; |
| 29 | 26 | ||
| 30 | // NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, | 27 | impl<T: Instance> super::VrefConverter for T { |
| 31 | // this currently cannot be modeled with stm32-data, | 28 | const CHANNEL: u8 = 10; |
| 32 | // so these are available from the software on all ADCs. | ||
| 33 | /// Internal voltage reference channel. | ||
| 34 | pub struct VrefInt; | ||
| 35 | impl<T: Instance> AdcChannel<T> for VrefInt {} | ||
| 36 | impl<T: Instance> SealedAdcChannel<T> for VrefInt { | ||
| 37 | fn channel(&self) -> u8 { | ||
| 38 | VREF_CHANNEL | ||
| 39 | } | ||
| 40 | } | 29 | } |
| 41 | 30 | ||
| 42 | /// Internal temperature channel. | 31 | impl<T: Instance> super::TemperatureConverter for T { |
| 43 | pub struct Temperature; | 32 | const CHANNEL: u8 = 9; |
| 44 | impl<T: Instance> AdcChannel<T> for Temperature {} | ||
| 45 | impl<T: Instance> SealedAdcChannel<T> for Temperature { | ||
| 46 | fn channel(&self) -> u8 { | ||
| 47 | TEMP_CHANNEL | ||
| 48 | } | ||
| 49 | } | 33 | } |
| 50 | 34 | ||
| 51 | #[derive(Copy, Clone, Debug)] | 35 | #[derive(Copy, Clone, Debug)] |
| @@ -156,7 +140,7 @@ pub enum Averaging { | |||
| 156 | 140 | ||
| 157 | impl<'d, T: Instance> Adc<'d, T> { | 141 | impl<'d, T: Instance> Adc<'d, T> { |
| 158 | /// Create a new ADC driver. | 142 | /// Create a new ADC driver. |
| 159 | pub fn new(adc: Peri<'d, T>, sample_time: SampleTime, resolution: Resolution) -> Self { | 143 | pub fn new(adc: Peri<'d, T>, resolution: Resolution) -> Self { |
| 160 | rcc::enable_and_reset::<T>(); | 144 | rcc::enable_and_reset::<T>(); |
| 161 | 145 | ||
| 162 | T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); | 146 | T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); |
| @@ -168,13 +152,13 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 168 | debug!("ADC frequency set to {}", frequency); | 152 | debug!("ADC frequency set to {}", frequency); |
| 169 | 153 | ||
| 170 | if frequency > MAX_ADC_CLK_FREQ { | 154 | if frequency > MAX_ADC_CLK_FREQ { |
| 171 | panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); | 155 | panic!( |
| 156 | "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", | ||
| 157 | MAX_ADC_CLK_FREQ.0 / 1_000_000 | ||
| 158 | ); | ||
| 172 | } | 159 | } |
| 173 | 160 | ||
| 174 | let mut s = Self { | 161 | let mut s = Self { adc }; |
| 175 | adc, | ||
| 176 | sample_time: SampleTime::from_bits(0), | ||
| 177 | }; | ||
| 178 | 162 | ||
| 179 | s.power_up(); | 163 | s.power_up(); |
| 180 | 164 | ||
| @@ -186,8 +170,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 186 | 170 | ||
| 187 | s.configure_default(); | 171 | s.configure_default(); |
| 188 | 172 | ||
| 189 | s.set_sample_time_all_channels(sample_time); | ||
| 190 | |||
| 191 | s | 173 | s |
| 192 | } | 174 | } |
| 193 | 175 | ||
| @@ -234,29 +216,27 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 234 | } | 216 | } |
| 235 | 217 | ||
| 236 | /// Enable reading the voltage reference internal channel. | 218 | /// Enable reading the voltage reference internal channel. |
| 237 | pub fn enable_vrefint(&self) -> VrefInt { | 219 | pub fn enable_vrefint(&self) -> super::VrefInt { |
| 238 | T::common_regs().ccr().modify(|reg| { | 220 | T::common_regs().ccr().modify(|reg| { |
| 239 | reg.set_vrefen(true); | 221 | reg.set_vrefen(true); |
| 240 | }); | 222 | }); |
| 241 | 223 | ||
| 242 | VrefInt {} | 224 | super::VrefInt {} |
| 243 | } | 225 | } |
| 244 | 226 | ||
| 245 | /// Enable reading the temperature internal channel. | 227 | /// Enable reading the temperature internal channel. |
| 246 | pub fn enable_temperature(&self) -> Temperature { | 228 | pub fn enable_temperature(&self) -> super::Temperature { |
| 247 | debug!("Ensure that sample time is set to more than temperature sensor T_start from the datasheet!"); | 229 | debug!("Ensure that sample time is set to more than temperature sensor T_start from the datasheet!"); |
| 248 | T::common_regs().ccr().modify(|reg| { | 230 | T::common_regs().ccr().modify(|reg| { |
| 249 | reg.set_tsen(true); | 231 | reg.set_tsen(true); |
| 250 | }); | 232 | }); |
| 251 | 233 | ||
| 252 | Temperature {} | 234 | super::Temperature {} |
| 253 | } | 235 | } |
| 254 | 236 | ||
| 255 | /// Set the ADC sample time. | 237 | /// Set the ADC sample time. |
| 256 | /// Shall only be called when ADC is not converting. | 238 | /// Shall only be called when ADC is not converting. |
| 257 | pub fn set_sample_time_all_channels(&mut self, sample_time: SampleTime) { | 239 | pub fn set_sample_time_all_channels(&mut self, sample_time: SampleTime) { |
| 258 | self.sample_time = sample_time; | ||
| 259 | |||
| 260 | // Set all channels to use SMP1 field as source. | 240 | // Set all channels to use SMP1 field as source. |
| 261 | T::regs().smpr().modify(|w| { | 241 | T::regs().smpr().modify(|w| { |
| 262 | w.smpsel(0); | 242 | w.smpsel(0); |
| @@ -285,7 +265,9 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 285 | T::regs().dr().read().data() as u16 | 265 | T::regs().dr().read().data() as u16 |
| 286 | } | 266 | } |
| 287 | 267 | ||
| 288 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 268 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { |
| 269 | self.set_sample_time_all_channels(sample_time); | ||
| 270 | |||
| 289 | Self::configure_channel(channel); | 271 | Self::configure_channel(channel); |
| 290 | T::regs().cfgr1().write(|reg| { | 272 | T::regs().cfgr1().write(|reg| { |
| 291 | reg.set_chselrmod(false); | 273 | reg.set_chselrmod(false); |
diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index 3cdc9d8fb..835cc8c63 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs | |||
| @@ -7,7 +7,7 @@ use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; | |||
| 7 | use crate::interrupt::typelevel::Interrupt; | 7 | use crate::interrupt::typelevel::Interrupt; |
| 8 | use crate::interrupt::{self}; | 8 | use crate::interrupt::{self}; |
| 9 | use crate::time::Hertz; | 9 | use crate::time::Hertz; |
| 10 | use crate::{rcc, Peri}; | 10 | use crate::{Peri, rcc}; |
| 11 | 11 | ||
| 12 | pub const VDDA_CALIB_MV: u32 = 3300; | 12 | pub const VDDA_CALIB_MV: u32 = 3300; |
| 13 | pub const ADC_MAX: u32 = (1 << 12) - 1; | 13 | pub const ADC_MAX: u32 = (1 << 12) - 1; |
| @@ -28,20 +28,12 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 28 | } | 28 | } |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | pub struct Vref; | 31 | impl<T: Instance> super::VrefConverter for T { |
| 32 | impl<T: Instance> AdcChannel<T> for Vref {} | 32 | const CHANNEL: u8 = 17; |
| 33 | impl<T: Instance> super::SealedAdcChannel<T> for Vref { | ||
| 34 | fn channel(&self) -> u8 { | ||
| 35 | 17 | ||
| 36 | } | ||
| 37 | } | 33 | } |
| 38 | 34 | ||
| 39 | pub struct Temperature; | 35 | impl<T: Instance> super::TemperatureConverter for T { |
| 40 | impl<T: Instance> AdcChannel<T> for Temperature {} | 36 | const CHANNEL: u8 = 16; |
| 41 | impl<T: Instance> super::SealedAdcChannel<T> for Temperature { | ||
| 42 | fn channel(&self) -> u8 { | ||
| 43 | 16 | ||
| 44 | } | ||
| 45 | } | 37 | } |
| 46 | 38 | ||
| 47 | impl<'d, T: Instance> Adc<'d, T> { | 39 | impl<'d, T: Instance> Adc<'d, T> { |
| @@ -71,10 +63,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 71 | T::Interrupt::unpend(); | 63 | T::Interrupt::unpend(); |
| 72 | unsafe { T::Interrupt::enable() }; | 64 | unsafe { T::Interrupt::enable() }; |
| 73 | 65 | ||
| 74 | Self { | 66 | Self { adc } |
| 75 | adc, | ||
| 76 | sample_time: SampleTime::from_bits(0), | ||
| 77 | } | ||
| 78 | } | 67 | } |
| 79 | 68 | ||
| 80 | fn freq() -> Hertz { | 69 | fn freq() -> Hertz { |
| @@ -94,22 +83,18 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 94 | } | 83 | } |
| 95 | } | 84 | } |
| 96 | 85 | ||
| 97 | pub fn enable_vref(&self) -> Vref { | 86 | pub fn enable_vref(&self) -> super::VrefInt { |
| 98 | T::regs().cr2().modify(|reg| { | 87 | T::regs().cr2().modify(|reg| { |
| 99 | reg.set_tsvrefe(true); | 88 | reg.set_tsvrefe(true); |
| 100 | }); | 89 | }); |
| 101 | Vref {} | 90 | super::VrefInt {} |
| 102 | } | 91 | } |
| 103 | 92 | ||
| 104 | pub fn enable_temperature(&self) -> Temperature { | 93 | pub fn enable_temperature(&self) -> super::Temperature { |
| 105 | T::regs().cr2().modify(|reg| { | 94 | T::regs().cr2().modify(|reg| { |
| 106 | reg.set_tsvrefe(true); | 95 | reg.set_tsvrefe(true); |
| 107 | }); | 96 | }); |
| 108 | Temperature {} | 97 | super::Temperature {} |
| 109 | } | ||
| 110 | |||
| 111 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 112 | self.sample_time = sample_time; | ||
| 113 | } | 98 | } |
| 114 | 99 | ||
| 115 | /// Perform a single conversion. | 100 | /// Perform a single conversion. |
| @@ -134,8 +119,8 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 134 | T::regs().dr().read().0 as u16 | 119 | T::regs().dr().read().0 as u16 |
| 135 | } | 120 | } |
| 136 | 121 | ||
| 137 | pub async fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 122 | pub async fn read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { |
| 138 | Self::set_channel_sample_time(channel.channel(), self.sample_time); | 123 | Self::set_channel_sample_time(channel.channel(), sample_time); |
| 139 | T::regs().cr1().modify(|reg| { | 124 | T::regs().cr1().modify(|reg| { |
| 140 | reg.set_scan(false); | 125 | reg.set_scan(false); |
| 141 | reg.set_discen(false); | 126 | reg.set_discen(false); |
diff --git a/embassy-stm32/src/adc/f3.rs b/embassy-stm32/src/adc/f3.rs index 3aeb6f2c7..f6a4e1209 100644 --- a/embassy-stm32/src/adc/f3.rs +++ b/embassy-stm32/src/adc/f3.rs | |||
| @@ -6,7 +6,7 @@ use super::blocking_delay_us; | |||
| 6 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; | 6 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; |
| 7 | use crate::interrupt::typelevel::Interrupt; | 7 | use crate::interrupt::typelevel::Interrupt; |
| 8 | use crate::time::Hertz; | 8 | use crate::time::Hertz; |
| 9 | use crate::{interrupt, rcc, Peri}; | 9 | use crate::{Peri, interrupt, rcc}; |
| 10 | 10 | ||
| 11 | pub const VDDA_CALIB_MV: u32 = 3300; | 11 | pub const VDDA_CALIB_MV: u32 = 3300; |
| 12 | pub const ADC_MAX: u32 = (1 << 12) - 1; | 12 | pub const ADC_MAX: u32 = (1 << 12) - 1; |
| @@ -29,27 +29,12 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 29 | } | 29 | } |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | pub struct Vref; | 32 | impl<T: Instance> super::VrefConverter for T { |
| 33 | impl<T: Instance> AdcChannel<T> for Vref {} | 33 | const CHANNEL: u8 = 18; |
| 34 | impl<T: Instance> super::SealedAdcChannel<T> for Vref { | ||
| 35 | fn channel(&self) -> u8 { | ||
| 36 | 18 | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | impl Vref { | ||
| 41 | /// The value that vref would be if vdda was at 3300mv | ||
| 42 | pub fn value(&self) -> u16 { | ||
| 43 | crate::pac::VREFINTCAL.data().read() | ||
| 44 | } | ||
| 45 | } | 34 | } |
| 46 | 35 | ||
| 47 | pub struct Temperature; | 36 | impl<T: Instance> super::TemperatureConverter for T { |
| 48 | impl<T: Instance> AdcChannel<T> for Temperature {} | 37 | const CHANNEL: u8 = 16; |
| 49 | impl<T: Instance> super::SealedAdcChannel<T> for Temperature { | ||
| 50 | fn channel(&self) -> u8 { | ||
| 51 | 16 | ||
| 52 | } | ||
| 53 | } | 38 | } |
| 54 | 39 | ||
| 55 | impl<'d, T: Instance> Adc<'d, T> { | 40 | impl<'d, T: Instance> Adc<'d, T> { |
| @@ -90,10 +75,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 90 | T::Interrupt::enable(); | 75 | T::Interrupt::enable(); |
| 91 | } | 76 | } |
| 92 | 77 | ||
| 93 | Self { | 78 | Self { adc } |
| 94 | adc, | ||
| 95 | sample_time: SampleTime::from_bits(0), | ||
| 96 | } | ||
| 97 | } | 79 | } |
| 98 | 80 | ||
| 99 | fn freq() -> Hertz { | 81 | fn freq() -> Hertz { |
| @@ -112,20 +94,16 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 112 | } | 94 | } |
| 113 | } | 95 | } |
| 114 | 96 | ||
| 115 | pub fn enable_vref(&self) -> Vref { | 97 | pub fn enable_vref(&self) -> super::VrefInt { |
| 116 | T::common_regs().ccr().modify(|w| w.set_vrefen(true)); | 98 | T::common_regs().ccr().modify(|w| w.set_vrefen(true)); |
| 117 | 99 | ||
| 118 | Vref {} | 100 | super::VrefInt {} |
| 119 | } | 101 | } |
| 120 | 102 | ||
| 121 | pub fn enable_temperature(&self) -> Temperature { | 103 | pub fn enable_temperature(&self) -> super::Temperature { |
| 122 | T::common_regs().ccr().modify(|w| w.set_tsen(true)); | 104 | T::common_regs().ccr().modify(|w| w.set_tsen(true)); |
| 123 | 105 | ||
| 124 | Temperature {} | 106 | super::Temperature {} |
| 125 | } | ||
| 126 | |||
| 127 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 128 | self.sample_time = sample_time; | ||
| 129 | } | 107 | } |
| 130 | 108 | ||
| 131 | /// Perform a single conversion. | 109 | /// Perform a single conversion. |
| @@ -150,8 +128,8 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 150 | T::regs().dr().read().rdata() | 128 | T::regs().dr().read().rdata() |
| 151 | } | 129 | } |
| 152 | 130 | ||
| 153 | pub async fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 131 | pub async fn read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { |
| 154 | Self::set_channel_sample_time(channel.channel(), self.sample_time); | 132 | Self::set_channel_sample_time(channel.channel(), sample_time); |
| 155 | 133 | ||
| 156 | // Configure the channel to sample | 134 | // Configure the channel to sample |
| 157 | T::regs().sqr1().write(|w| w.set_sq(0, channel.channel())); | 135 | T::regs().sqr1().write(|w| w.set_sq(0, channel.channel())); |
diff --git a/embassy-stm32/src/adc/f3_v1_1.rs b/embassy-stm32/src/adc/f3_v1_1.rs index 84613078c..919ac3cc0 100644 --- a/embassy-stm32/src/adc/f3_v1_1.rs +++ b/embassy-stm32/src/adc/f3_v1_1.rs | |||
| @@ -9,7 +9,7 @@ use super::Resolution; | |||
| 9 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; | 9 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; |
| 10 | use crate::interrupt::typelevel::Interrupt; | 10 | use crate::interrupt::typelevel::Interrupt; |
| 11 | use crate::time::Hertz; | 11 | use crate::time::Hertz; |
| 12 | use crate::{interrupt, rcc, Peri}; | 12 | use crate::{Peri, interrupt, rcc}; |
| 13 | 13 | ||
| 14 | const ADC_FREQ: Hertz = crate::rcc::HSI_FREQ; | 14 | const ADC_FREQ: Hertz = crate::rcc::HSI_FREQ; |
| 15 | 15 | ||
| @@ -79,7 +79,7 @@ impl<T: Instance> Vref<T> { | |||
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | pub async fn calibrate(&mut self, adc: &mut Adc<'_, T>) -> Calibration { | 81 | pub async fn calibrate(&mut self, adc: &mut Adc<'_, T>) -> Calibration { |
| 82 | let vref_val = adc.read(self).await; | 82 | let vref_val = adc.read(self, SampleTime::from(0)).await; |
| 83 | Calibration { | 83 | Calibration { |
| 84 | vref_cal: self.calibrated_value(), | 84 | vref_cal: self.calibrated_value(), |
| 85 | vref_val, | 85 | vref_val, |
| @@ -270,7 +270,8 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 270 | } | 270 | } |
| 271 | } | 271 | } |
| 272 | 272 | ||
| 273 | pub async fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 273 | pub async fn read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { |
| 274 | self.set_sample_time(channel, sample_time).await; | ||
| 274 | self.set_sample_sequence(&[channel.channel()]).await; | 275 | self.set_sample_sequence(&[channel.channel()]).await; |
| 275 | self.convert().await | 276 | self.convert().await |
| 276 | } | 277 | } |
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 43498966f..5d9c6ff74 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs | |||
| @@ -1,57 +1,40 @@ | |||
| 1 | use core::mem; | ||
| 2 | |||
| 1 | #[allow(unused)] | 3 | #[allow(unused)] |
| 2 | #[cfg(stm32h7)] | 4 | #[cfg(stm32h7)] |
| 3 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; | 5 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; |
| 4 | #[allow(unused)] | 6 | #[allow(unused)] |
| 5 | #[cfg(stm32g4)] | 7 | #[cfg(stm32g4)] |
| 6 | use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs}; | 8 | pub use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs}; |
| 7 | use pac::adccommon::vals::Presc; | 9 | pub use pac::adccommon::vals::Presc; |
| 8 | use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; | 10 | pub use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; |
| 11 | pub use stm32_metapac::adccommon::vals::Dual; | ||
| 9 | 12 | ||
| 10 | use super::{blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime}; | 13 | use super::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, blocking_delay_us}; |
| 11 | use crate::adc::SealedAdcChannel; | 14 | use crate::adc::SealedAdcChannel; |
| 12 | use crate::dma::Transfer; | 15 | use crate::dma::Transfer; |
| 13 | use crate::time::Hertz; | 16 | use crate::time::Hertz; |
| 14 | use crate::{pac, rcc, Peri}; | 17 | use crate::{Peri, pac, rcc}; |
| 18 | |||
| 19 | mod ringbuffered; | ||
| 20 | pub use ringbuffered::RingBufferedAdc; | ||
| 21 | |||
| 22 | mod injected; | ||
| 23 | pub use injected::InjectedAdc; | ||
| 15 | 24 | ||
| 16 | /// Default VREF voltage used for sample conversion to millivolts. | 25 | /// Default VREF voltage used for sample conversion to millivolts. |
| 17 | pub const VREF_DEFAULT_MV: u32 = 3300; | 26 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| 18 | /// VREF voltage used for factory calibration of VREFINTCAL register. | 27 | /// VREF voltage used for factory calibration of VREFINTCAL register. |
| 19 | pub const VREF_CALIB_MV: u32 = 3300; | 28 | pub const VREF_CALIB_MV: u32 = 3300; |
| 20 | 29 | ||
| 30 | const NR_INJECTED_RANKS: usize = 4; | ||
| 31 | |||
| 21 | /// Max single ADC operation clock frequency | 32 | /// Max single ADC operation clock frequency |
| 22 | #[cfg(stm32g4)] | 33 | #[cfg(stm32g4)] |
| 23 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); | 34 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); |
| 24 | #[cfg(stm32h7)] | 35 | #[cfg(stm32h7)] |
| 25 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); | 36 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); |
| 26 | 37 | ||
| 27 | // NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs | ||
| 28 | /// Internal voltage reference channel. | ||
| 29 | pub struct VrefInt; | ||
| 30 | impl<T: Instance + VrefChannel> AdcChannel<T> for VrefInt {} | ||
| 31 | impl<T: Instance + VrefChannel> super::SealedAdcChannel<T> for VrefInt { | ||
| 32 | fn channel(&self) -> u8 { | ||
| 33 | T::CHANNEL | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | /// Internal temperature channel. | ||
| 38 | pub struct Temperature; | ||
| 39 | impl<T: Instance + TemperatureChannel> AdcChannel<T> for Temperature {} | ||
| 40 | impl<T: Instance + TemperatureChannel> super::SealedAdcChannel<T> for Temperature { | ||
| 41 | fn channel(&self) -> u8 { | ||
| 42 | T::CHANNEL | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | /// Internal battery voltage channel. | ||
| 47 | pub struct Vbat; | ||
| 48 | impl<T: Instance + VBatChannel> AdcChannel<T> for Vbat {} | ||
| 49 | impl<T: Instance + VBatChannel> super::SealedAdcChannel<T> for Vbat { | ||
| 50 | fn channel(&self) -> u8 { | ||
| 51 | T::CHANNEL | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | 38 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, |
| 56 | // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. | 39 | // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. |
| 57 | #[allow(unused)] | 40 | #[allow(unused)] |
| @@ -120,6 +103,24 @@ impl Prescaler { | |||
| 120 | } | 103 | } |
| 121 | } | 104 | } |
| 122 | 105 | ||
| 106 | // Trigger source for ADC conversions¨ | ||
| 107 | #[derive(Copy, Clone)] | ||
| 108 | pub struct ConversionTrigger { | ||
| 109 | // See Table 166 and 167 in RM0440 Rev 9 for ADC1/2 External triggers | ||
| 110 | // Note that Injected and Regular channels uses different mappings | ||
| 111 | pub channel: u8, | ||
| 112 | pub edge: Exten, | ||
| 113 | } | ||
| 114 | |||
| 115 | // Conversion mode for regular ADC channels | ||
| 116 | #[derive(Copy, Clone)] | ||
| 117 | pub enum RegularConversionMode { | ||
| 118 | // Samples as fast as possible | ||
| 119 | Continuous, | ||
| 120 | // Sample at rate determined by external trigger | ||
| 121 | Triggered(ConversionTrigger), | ||
| 122 | } | ||
| 123 | |||
| 123 | impl<'d, T: Instance> Adc<'d, T> { | 124 | impl<'d, T: Instance> Adc<'d, T> { |
| 124 | /// Create a new ADC driver. | 125 | /// Create a new ADC driver. |
| 125 | pub fn new(adc: Peri<'d, T>) -> Self { | 126 | pub fn new(adc: Peri<'d, T>) -> Self { |
| @@ -133,20 +134,20 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 133 | trace!("ADC frequency set to {}", frequency); | 134 | trace!("ADC frequency set to {}", frequency); |
| 134 | 135 | ||
| 135 | if frequency > MAX_ADC_CLK_FREQ { | 136 | if frequency > MAX_ADC_CLK_FREQ { |
| 136 | panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); | 137 | panic!( |
| 138 | "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", | ||
| 139 | MAX_ADC_CLK_FREQ.0 / 1_000_000 | ||
| 140 | ); | ||
| 137 | } | 141 | } |
| 138 | 142 | ||
| 139 | let mut s = Self { | 143 | let mut s = Self { adc }; |
| 140 | adc, | ||
| 141 | sample_time: SampleTime::from_bits(0), | ||
| 142 | }; | ||
| 143 | s.power_up(); | 144 | s.power_up(); |
| 144 | s.configure_differential_inputs(); | 145 | s.configure_differential_inputs(); |
| 145 | 146 | ||
| 146 | s.calibrate(); | 147 | s.calibrate(); |
| 147 | blocking_delay_us(1); | 148 | blocking_delay_us(1); |
| 148 | 149 | ||
| 149 | s.enable(); | 150 | Self::enable(); |
| 150 | s.configure(); | 151 | s.configure(); |
| 151 | 152 | ||
| 152 | s | 153 | s |
| @@ -191,7 +192,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 191 | blocking_delay_us(20); | 192 | blocking_delay_us(20); |
| 192 | } | 193 | } |
| 193 | 194 | ||
| 194 | fn enable(&mut self) { | 195 | fn enable() { |
| 195 | // Make sure bits are off | 196 | // Make sure bits are off |
| 196 | while T::regs().cr().read().addis() { | 197 | while T::regs().cr().read().addis() { |
| 197 | // spin | 198 | // spin |
| @@ -221,39 +222,39 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 221 | } | 222 | } |
| 222 | 223 | ||
| 223 | /// Enable reading the voltage reference internal channel. | 224 | /// Enable reading the voltage reference internal channel. |
| 224 | pub fn enable_vrefint(&self) -> VrefInt | 225 | pub fn enable_vrefint(&self) -> super::VrefInt |
| 225 | where | 226 | where |
| 226 | T: VrefChannel, | 227 | T: super::VrefConverter, |
| 227 | { | 228 | { |
| 228 | T::common_regs().ccr().modify(|reg| { | 229 | T::common_regs().ccr().modify(|reg| { |
| 229 | reg.set_vrefen(true); | 230 | reg.set_vrefen(true); |
| 230 | }); | 231 | }); |
| 231 | 232 | ||
| 232 | VrefInt {} | 233 | super::VrefInt {} |
| 233 | } | 234 | } |
| 234 | 235 | ||
| 235 | /// Enable reading the temperature internal channel. | 236 | /// Enable reading the temperature internal channel. |
| 236 | pub fn enable_temperature(&self) -> Temperature | 237 | pub fn enable_temperature(&self) -> super::Temperature |
| 237 | where | 238 | where |
| 238 | T: TemperatureChannel, | 239 | T: super::TemperatureConverter, |
| 239 | { | 240 | { |
| 240 | T::common_regs().ccr().modify(|reg| { | 241 | T::common_regs().ccr().modify(|reg| { |
| 241 | reg.set_vsenseen(true); | 242 | reg.set_vsenseen(true); |
| 242 | }); | 243 | }); |
| 243 | 244 | ||
| 244 | Temperature {} | 245 | super::Temperature {} |
| 245 | } | 246 | } |
| 246 | 247 | ||
| 247 | /// Enable reading the vbat internal channel. | 248 | /// Enable reading the vbat internal channel. |
| 248 | pub fn enable_vbat(&self) -> Vbat | 249 | pub fn enable_vbat(&self) -> super::Vbat |
| 249 | where | 250 | where |
| 250 | T: VBatChannel, | 251 | T: super::VBatConverter, |
| 251 | { | 252 | { |
| 252 | T::common_regs().ccr().modify(|reg| { | 253 | T::common_regs().ccr().modify(|reg| { |
| 253 | reg.set_vbaten(true); | 254 | reg.set_vbaten(true); |
| 254 | }); | 255 | }); |
| 255 | 256 | ||
| 256 | Vbat {} | 257 | super::Vbat {} |
| 257 | } | 258 | } |
| 258 | 259 | ||
| 259 | /// Enable differential channel. | 260 | /// Enable differential channel. |
| @@ -265,7 +266,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 265 | /// channel on the other ADC unusable. The only exception is when ADC master and the slave | 266 | /// channel on the other ADC unusable. The only exception is when ADC master and the slave |
| 266 | /// operate in interleaved mode. | 267 | /// operate in interleaved mode. |
| 267 | #[cfg(stm32g4)] | 268 | #[cfg(stm32g4)] |
| 268 | pub fn set_differential_channel(&mut self, ch: usize, enable: bool) { | 269 | fn set_differential_channel(&mut self, ch: usize, enable: bool) { |
| 269 | T::regs().cr().modify(|w| w.set_aden(false)); // disable adc | 270 | T::regs().cr().modify(|w| w.set_aden(false)); // disable adc |
| 270 | T::regs().difsel().modify(|w| { | 271 | T::regs().difsel().modify(|w| { |
| 271 | w.set_difsel( | 272 | w.set_difsel( |
| @@ -318,11 +319,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 318 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); | 319 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); |
| 319 | // } | 320 | // } |
| 320 | 321 | ||
| 321 | /// Set the ADC sample time. | ||
| 322 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 323 | self.sample_time = sample_time; | ||
| 324 | } | ||
| 325 | |||
| 326 | /// Set the ADC resolution. | 322 | /// Set the ADC resolution. |
| 327 | pub fn set_resolution(&mut self, resolution: Resolution) { | 323 | pub fn set_resolution(&mut self, resolution: Resolution) { |
| 328 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | 324 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); |
| @@ -348,13 +344,35 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 348 | } | 344 | } |
| 349 | 345 | ||
| 350 | /// Read an ADC pin. | 346 | /// Read an ADC pin. |
| 351 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 347 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { |
| 352 | channel.setup(); | 348 | channel.setup(); |
| 353 | 349 | ||
| 354 | self.read_channel(channel) | 350 | self.read_channel(channel, sample_time) |
| 355 | } | 351 | } |
| 356 | 352 | ||
| 357 | /// Read one or multiple ADC channels using DMA. | 353 | /// Start regular adc conversion |
| 354 | pub(super) fn start() { | ||
| 355 | T::regs().cr().modify(|reg| { | ||
| 356 | reg.set_adstart(true); | ||
| 357 | }); | ||
| 358 | } | ||
| 359 | |||
| 360 | /// Stop regular conversions | ||
| 361 | pub(super) fn stop() { | ||
| 362 | Self::stop_regular_conversions(); | ||
| 363 | } | ||
| 364 | |||
| 365 | /// Teardown method for stopping regular ADC conversions | ||
| 366 | pub(super) fn teardown_dma() { | ||
| 367 | Self::stop_regular_conversions(); | ||
| 368 | |||
| 369 | // Disable dma control | ||
| 370 | T::regs().cfgr().modify(|reg| { | ||
| 371 | reg.set_dmaen(Dmaen::DISABLE); | ||
| 372 | }); | ||
| 373 | } | ||
| 374 | |||
| 375 | /// Read one or multiple ADC regular channels using DMA. | ||
| 358 | /// | 376 | /// |
| 359 | /// `sequence` iterator and `readings` must have the same length. | 377 | /// `sequence` iterator and `readings` must have the same length. |
| 360 | /// | 378 | /// |
| @@ -379,6 +397,9 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 379 | /// .await; | 397 | /// .await; |
| 380 | /// defmt::info!("measurements: {}", measurements); | 398 | /// defmt::info!("measurements: {}", measurements); |
| 381 | /// ``` | 399 | /// ``` |
| 400 | /// | ||
| 401 | /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use | ||
| 402 | /// `into_ring_buffered`, `into_ring_buffered_and_injected` | ||
| 382 | pub async fn read( | 403 | pub async fn read( |
| 383 | &mut self, | 404 | &mut self, |
| 384 | rx_dma: Peri<'_, impl RxDma<T>>, | 405 | rx_dma: Peri<'_, impl RxDma<T>>, |
| @@ -396,91 +417,345 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 396 | ); | 417 | ); |
| 397 | 418 | ||
| 398 | // Ensure no conversions are ongoing and ADC is enabled. | 419 | // Ensure no conversions are ongoing and ADC is enabled. |
| 399 | Self::cancel_conversions(); | 420 | Self::stop_regular_conversions(); |
| 400 | self.enable(); | 421 | Self::enable(); |
| 422 | |||
| 423 | Self::configure_sequence(sequence.map(|(channel, sample_time)| { | ||
| 424 | channel.setup(); | ||
| 425 | |||
| 426 | (channel.channel, sample_time) | ||
| 427 | })); | ||
| 428 | |||
| 429 | // Set continuous mode with oneshot dma. | ||
| 430 | // Clear overrun flag before starting transfer. | ||
| 431 | T::regs().isr().modify(|reg| { | ||
| 432 | reg.set_ovr(true); | ||
| 433 | }); | ||
| 401 | 434 | ||
| 435 | T::regs().cfgr().modify(|reg| { | ||
| 436 | reg.set_discen(false); | ||
| 437 | reg.set_cont(true); | ||
| 438 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 439 | reg.set_dmaen(Dmaen::ENABLE); | ||
| 440 | }); | ||
| 441 | |||
| 442 | let request = rx_dma.request(); | ||
| 443 | let transfer = unsafe { | ||
| 444 | Transfer::new_read( | ||
| 445 | rx_dma, | ||
| 446 | request, | ||
| 447 | T::regs().dr().as_ptr() as *mut u16, | ||
| 448 | readings, | ||
| 449 | Default::default(), | ||
| 450 | ) | ||
| 451 | }; | ||
| 452 | |||
| 453 | // Start conversion | ||
| 454 | T::regs().cr().modify(|reg| { | ||
| 455 | reg.set_adstart(true); | ||
| 456 | }); | ||
| 457 | |||
| 458 | // Wait for conversion sequence to finish. | ||
| 459 | transfer.await; | ||
| 460 | |||
| 461 | // Ensure conversions are finished. | ||
| 462 | Self::stop_regular_conversions(); | ||
| 463 | |||
| 464 | // Reset configuration. | ||
| 465 | T::regs().cfgr().modify(|reg| { | ||
| 466 | reg.set_cont(false); | ||
| 467 | }); | ||
| 468 | } | ||
| 469 | |||
| 470 | pub(super) fn configure_sequence(sequence: impl ExactSizeIterator<Item = (u8, SampleTime)>) { | ||
| 402 | // Set sequence length | 471 | // Set sequence length |
| 403 | T::regs().sqr1().modify(|w| { | 472 | T::regs().sqr1().modify(|w| { |
| 404 | w.set_l(sequence.len() as u8 - 1); | 473 | w.set_l(sequence.len() as u8 - 1); |
| 405 | }); | 474 | }); |
| 406 | 475 | ||
| 407 | // Configure channels and ranks | 476 | // Configure channels and ranks |
| 408 | for (_i, (channel, sample_time)) in sequence.enumerate() { | 477 | for (_i, (ch, sample_time)) in sequence.enumerate() { |
| 409 | Self::configure_channel(channel, sample_time); | 478 | let sample_time = sample_time.into(); |
| 479 | if ch <= 9 { | ||
| 480 | T::regs().smpr().modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 481 | } else { | ||
| 482 | T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 483 | } | ||
| 410 | 484 | ||
| 411 | match _i { | 485 | match _i { |
| 412 | 0..=3 => { | 486 | 0..=3 => { |
| 413 | T::regs().sqr1().modify(|w| { | 487 | T::regs().sqr1().modify(|w| { |
| 414 | w.set_sq(_i, channel.channel()); | 488 | w.set_sq(_i, ch); |
| 415 | }); | 489 | }); |
| 416 | } | 490 | } |
| 417 | 4..=8 => { | 491 | 4..=8 => { |
| 418 | T::regs().sqr2().modify(|w| { | 492 | T::regs().sqr2().modify(|w| { |
| 419 | w.set_sq(_i - 4, channel.channel()); | 493 | w.set_sq(_i - 4, ch); |
| 420 | }); | 494 | }); |
| 421 | } | 495 | } |
| 422 | 9..=13 => { | 496 | 9..=13 => { |
| 423 | T::regs().sqr3().modify(|w| { | 497 | T::regs().sqr3().modify(|w| { |
| 424 | w.set_sq(_i - 9, channel.channel()); | 498 | w.set_sq(_i - 9, ch); |
| 425 | }); | 499 | }); |
| 426 | } | 500 | } |
| 427 | 14..=15 => { | 501 | 14..=15 => { |
| 428 | T::regs().sqr4().modify(|w| { | 502 | T::regs().sqr4().modify(|w| { |
| 429 | w.set_sq(_i - 14, channel.channel()); | 503 | w.set_sq(_i - 14, ch); |
| 430 | }); | 504 | }); |
| 431 | } | 505 | } |
| 432 | _ => unreachable!(), | 506 | _ => unreachable!(), |
| 433 | } | 507 | } |
| 434 | } | 508 | } |
| 509 | } | ||
| 510 | |||
| 511 | /// Set external trigger for regular conversion sequence | ||
| 512 | fn set_regular_conversion_trigger(&mut self, trigger: ConversionTrigger) { | ||
| 513 | T::regs().cfgr().modify(|r| { | ||
| 514 | r.set_extsel(trigger.channel); | ||
| 515 | r.set_exten(trigger.edge); | ||
| 516 | }); | ||
| 517 | // Regular conversions uses DMA so no need to generate interrupt | ||
| 518 | T::regs().ier().modify(|r| r.set_eosie(false)); | ||
| 519 | } | ||
| 520 | |||
| 521 | // Dual ADC mode selection | ||
| 522 | pub fn configure_dual_mode(&mut self, val: Dual) { | ||
| 523 | T::common_regs().ccr().modify(|reg| { | ||
| 524 | reg.set_dual(val); | ||
| 525 | }) | ||
| 526 | } | ||
| 527 | |||
| 528 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 529 | /// | ||
| 530 | /// Use the [`read`] method to retrieve measurements from the DMA ring buffer. The read buffer | ||
| 531 | /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended | ||
| 532 | /// to configure `dma_buf` as a double buffer so that one half can be read while the other half | ||
| 533 | /// is being filled by the DMA, preventing data loss. The trigger period of the ADC effectively | ||
| 534 | /// defines the period at which the buffer should be read. | ||
| 535 | /// | ||
| 536 | /// If continous conversion mode is selected, the provided `dma_buf` must be large enough to prevent | ||
| 537 | /// DMA buffer overruns. Its length should be a multiple of the number of ADC channels being measured. | ||
| 538 | /// For example, if 3 channels are measured and you want to store 40 samples per channel, | ||
| 539 | /// the buffer length should be `3 * 40 = 120`. | ||
| 540 | /// | ||
| 541 | /// # Parameters | ||
| 542 | /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer. | ||
| 543 | /// - `dma_buf`: The buffer where DMA stores ADC samples. | ||
| 544 | /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions. | ||
| 545 | /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered). | ||
| 546 | /// | ||
| 547 | /// # Returns | ||
| 548 | /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. | ||
| 549 | pub fn into_ring_buffered<'a>( | ||
| 550 | mut self, | ||
| 551 | dma: Peri<'a, impl RxDma<T>>, | ||
| 552 | dma_buf: &'a mut [u16], | ||
| 553 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 554 | mode: RegularConversionMode, | ||
| 555 | ) -> RingBufferedAdc<'a, T> { | ||
| 556 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 557 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 558 | assert!( | ||
| 559 | sequence.len() <= 16, | ||
| 560 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 561 | ); | ||
| 562 | // reset conversions and enable the adc | ||
| 563 | Self::stop_regular_conversions(); | ||
| 564 | Self::enable(); | ||
| 565 | |||
| 566 | //adc side setup | ||
| 567 | |||
| 568 | Self::configure_sequence(sequence.map(|(mut channel, sample_time)| { | ||
| 569 | channel.setup(); | ||
| 570 | |||
| 571 | (channel.channel, sample_time) | ||
| 572 | })); | ||
| 435 | 573 | ||
| 436 | // Set continuous mode with oneshot dma. | ||
| 437 | // Clear overrun flag before starting transfer. | 574 | // Clear overrun flag before starting transfer. |
| 438 | T::regs().isr().modify(|reg| { | 575 | T::regs().isr().modify(|reg| { |
| 439 | reg.set_ovr(true); | 576 | reg.set_ovr(true); |
| 440 | }); | 577 | }); |
| 441 | 578 | ||
| 442 | T::regs().cfgr().modify(|reg| { | 579 | T::regs().cfgr().modify(|reg| { |
| 443 | reg.set_discen(false); | 580 | reg.set_discen(false); // Convert all channels for each trigger |
| 444 | reg.set_cont(true); | 581 | reg.set_dmacfg(Dmacfg::CIRCULAR); |
| 445 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 446 | reg.set_dmaen(Dmaen::ENABLE); | 582 | reg.set_dmaen(Dmaen::ENABLE); |
| 447 | }); | 583 | }); |
| 448 | 584 | ||
| 449 | let request = rx_dma.request(); | 585 | match mode { |
| 450 | let transfer = unsafe { | 586 | RegularConversionMode::Continuous => { |
| 451 | Transfer::new_read( | 587 | T::regs().cfgr().modify(|reg| { |
| 452 | rx_dma, | 588 | reg.set_cont(true); |
| 453 | request, | 589 | }); |
| 454 | T::regs().dr().as_ptr() as *mut u16, | 590 | } |
| 455 | readings, | 591 | RegularConversionMode::Triggered(trigger) => { |
| 456 | Default::default(), | 592 | T::regs().cfgr().modify(|r| { |
| 593 | r.set_cont(false); // New trigger is neede for each sample to be read | ||
| 594 | }); | ||
| 595 | self.set_regular_conversion_trigger(trigger); | ||
| 596 | } | ||
| 597 | } | ||
| 598 | |||
| 599 | mem::forget(self); | ||
| 600 | |||
| 601 | RingBufferedAdc::new(dma, dma_buf) | ||
| 602 | } | ||
| 603 | |||
| 604 | /// Configures the ADC for injected conversions. | ||
| 605 | /// | ||
| 606 | /// Injected conversions are separate from the regular conversion sequence and are typically | ||
| 607 | /// triggered by software or an external event. This method sets up a fixed-length sequence of | ||
| 608 | /// injected channels with specified sample times, the trigger source, and whether the end-of-sequence | ||
| 609 | /// interrupt should be enabled. | ||
| 610 | /// | ||
| 611 | /// # Parameters | ||
| 612 | /// - `sequence`: An array of tuples containing the ADC channels and their sample times. The length | ||
| 613 | /// `N` determines the number of injected ranks to configure (maximum 4 for STM32). | ||
| 614 | /// - `trigger`: The trigger source that starts the injected conversion sequence. | ||
| 615 | /// - `interrupt`: If `true`, enables the end-of-sequence (JEOS) interrupt for injected conversions. | ||
| 616 | /// | ||
| 617 | /// # Returns | ||
| 618 | /// An `InjectedAdc<T, N>` instance that represents the configured injected sequence. The returned | ||
| 619 | /// type encodes the sequence length `N` in its type, ensuring that reads return exactly `N` samples. | ||
| 620 | /// | ||
| 621 | /// # Panics | ||
| 622 | /// This function will panic if: | ||
| 623 | /// - `sequence` is empty. | ||
| 624 | /// - `sequence` length exceeds the maximum number of injected ranks (`NR_INJECTED_RANKS`). | ||
| 625 | /// | ||
| 626 | /// # Notes | ||
| 627 | /// - Injected conversions can run independently of regular ADC conversions. | ||
| 628 | /// - The order of channels in `sequence` determines the rank order in the injected sequence. | ||
| 629 | /// - Accessing samples beyond `N` will result in a panic; use the returned type | ||
| 630 | /// `InjectedAdc<T, N>` to enforce bounds at compile time. | ||
| 631 | pub fn setup_injected_conversions<'a, const N: usize>( | ||
| 632 | mut self, | ||
| 633 | sequence: [(AnyAdcChannel<T>, SampleTime); N], | ||
| 634 | trigger: ConversionTrigger, | ||
| 635 | interrupt: bool, | ||
| 636 | ) -> InjectedAdc<T, N> { | ||
| 637 | assert!(N != 0, "Read sequence cannot be empty"); | ||
| 638 | assert!( | ||
| 639 | N <= NR_INJECTED_RANKS, | ||
| 640 | "Read sequence cannot be more than {} in length", | ||
| 641 | NR_INJECTED_RANKS | ||
| 642 | ); | ||
| 643 | |||
| 644 | Self::stop_regular_conversions(); | ||
| 645 | Self::enable(); | ||
| 646 | |||
| 647 | T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); | ||
| 648 | |||
| 649 | for (n, (mut channel, sample_time)) in sequence.into_iter().enumerate() { | ||
| 650 | Self::configure_channel(&mut channel, sample_time); | ||
| 651 | |||
| 652 | let idx = match n { | ||
| 653 | 0..=3 => n, | ||
| 654 | 4..=8 => n - 4, | ||
| 655 | 9..=13 => n - 9, | ||
| 656 | 14..=15 => n - 14, | ||
| 657 | _ => unreachable!(), | ||
| 658 | }; | ||
| 659 | |||
| 660 | T::regs().jsqr().modify(|w| w.set_jsq(idx, channel.channel())); | ||
| 661 | } | ||
| 662 | |||
| 663 | T::regs().cfgr().modify(|reg| reg.set_jdiscen(false)); | ||
| 664 | |||
| 665 | self.set_injected_conversion_trigger(trigger); | ||
| 666 | self.enable_injected_eos_interrupt(interrupt); | ||
| 667 | Self::start_injected_conversions(); | ||
| 668 | |||
| 669 | InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels | ||
| 670 | } | ||
| 671 | |||
| 672 | /// Configures ADC for both regular conversions with a ring-buffered DMA and injected conversions. | ||
| 673 | /// | ||
| 674 | /// # Parameters | ||
| 675 | /// - `dma`: The DMA peripheral to use for the ring-buffered ADC transfers. | ||
| 676 | /// - `dma_buf`: The buffer to store DMA-transferred samples for regular conversions. | ||
| 677 | /// - `regular_sequence`: The sequence of channels and their sample times for regular conversions. | ||
| 678 | /// - `regular_conversion_mode`: The mode for regular conversions (e.g., continuous or triggered). | ||
| 679 | /// - `injected_sequence`: An array of channels and sample times for injected conversions (length `N`). | ||
| 680 | /// - `injected_trigger`: The trigger source for injected conversions. | ||
| 681 | /// - `injected_interrupt`: Whether to enable the end-of-sequence interrupt for injected conversions. | ||
| 682 | /// | ||
| 683 | /// Injected conversions are typically used with interrupts. If ADC1 and ADC2 are used in dual mode, | ||
| 684 | /// it is recommended to enable interrupts only for the ADC whose sequence takes the longest to complete. | ||
| 685 | /// | ||
| 686 | /// # Returns | ||
| 687 | /// A tuple containing: | ||
| 688 | /// 1. `RingBufferedAdc<'a, T>` — the configured ADC for regular conversions using DMA. | ||
| 689 | /// 2. `InjectedAdc<T, N>` — the configured ADC for injected conversions. | ||
| 690 | /// | ||
| 691 | /// # Safety | ||
| 692 | /// This function is `unsafe` because it clones the ADC peripheral handle unchecked. Both the | ||
| 693 | /// `RingBufferedAdc` and `InjectedAdc` take ownership of the handle and drop it independently. | ||
| 694 | /// Ensure no other code concurrently accesses the same ADC instance in a conflicting way. | ||
| 695 | pub fn into_ring_buffered_and_injected<'a, const N: usize>( | ||
| 696 | self, | ||
| 697 | dma: Peri<'a, impl RxDma<T>>, | ||
| 698 | dma_buf: &'a mut [u16], | ||
| 699 | regular_sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 700 | regular_conversion_mode: RegularConversionMode, | ||
| 701 | injected_sequence: [(AnyAdcChannel<T>, SampleTime); N], | ||
| 702 | injected_trigger: ConversionTrigger, | ||
| 703 | injected_interrupt: bool, | ||
| 704 | ) -> (RingBufferedAdc<'a, T>, InjectedAdc<T, N>) { | ||
| 705 | unsafe { | ||
| 706 | ( | ||
| 707 | Self { | ||
| 708 | adc: self.adc.clone_unchecked(), | ||
| 709 | } | ||
| 710 | .into_ring_buffered(dma, dma_buf, regular_sequence, regular_conversion_mode), | ||
| 711 | Self { | ||
| 712 | adc: self.adc.clone_unchecked(), | ||
| 713 | } | ||
| 714 | .setup_injected_conversions(injected_sequence, injected_trigger, injected_interrupt), | ||
| 457 | ) | 715 | ) |
| 458 | }; | 716 | } |
| 717 | } | ||
| 459 | 718 | ||
| 460 | // Start conversion | 719 | /// Stop injected conversions |
| 720 | pub(super) fn stop_injected_conversions() { | ||
| 721 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 722 | T::regs().cr().modify(|reg| { | ||
| 723 | reg.set_jadstp(Adstp::STOP); | ||
| 724 | }); | ||
| 725 | // The software must poll JADSTART until the bit is reset before assuming the | ||
| 726 | // ADC is completely stopped | ||
| 727 | while T::regs().cr().read().jadstart() {} | ||
| 728 | } | ||
| 729 | } | ||
| 730 | |||
| 731 | /// Start injected ADC conversion | ||
| 732 | pub(super) fn start_injected_conversions() { | ||
| 461 | T::regs().cr().modify(|reg| { | 733 | T::regs().cr().modify(|reg| { |
| 462 | reg.set_adstart(true); | 734 | reg.set_jadstart(true); |
| 463 | }); | 735 | }); |
| 736 | } | ||
| 464 | 737 | ||
| 465 | // Wait for conversion sequence to finish. | 738 | /// Set external trigger for injected conversion sequence |
| 466 | transfer.await; | 739 | /// Possible trigger values are seen in Table 167 in RM0440 Rev 9 |
| 467 | 740 | fn set_injected_conversion_trigger(&mut self, trigger: ConversionTrigger) { | |
| 468 | // Ensure conversions are finished. | 741 | T::regs().jsqr().modify(|r| { |
| 469 | Self::cancel_conversions(); | 742 | r.set_jextsel(trigger.channel); |
| 470 | 743 | r.set_jexten(trigger.edge); | |
| 471 | // Reset configuration. | ||
| 472 | T::regs().cfgr().modify(|reg| { | ||
| 473 | reg.set_cont(false); | ||
| 474 | }); | 744 | }); |
| 475 | } | 745 | } |
| 476 | 746 | ||
| 747 | /// Enable end of injected sequence interrupt | ||
| 748 | fn enable_injected_eos_interrupt(&mut self, enable: bool) { | ||
| 749 | T::regs().ier().modify(|r| r.set_jeosie(enable)); | ||
| 750 | } | ||
| 751 | |||
| 477 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | 752 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { |
| 478 | // Configure channel | 753 | // Configure channel |
| 479 | Self::set_channel_sample_time(channel.channel(), sample_time); | 754 | Self::set_channel_sample_time(channel.channel(), sample_time); |
| 480 | } | 755 | } |
| 481 | 756 | ||
| 482 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 757 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { |
| 483 | Self::configure_channel(channel, self.sample_time); | 758 | Self::configure_channel(channel, sample_time); |
| 484 | #[cfg(stm32h7)] | 759 | #[cfg(stm32h7)] |
| 485 | { | 760 | { |
| 486 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | 761 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); |
| @@ -506,72 +781,77 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 506 | } | 781 | } |
| 507 | } | 782 | } |
| 508 | 783 | ||
| 509 | fn cancel_conversions() { | 784 | // Stop regular conversions |
| 785 | fn stop_regular_conversions() { | ||
| 510 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | 786 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 511 | T::regs().cr().modify(|reg| { | 787 | T::regs().cr().modify(|reg| { |
| 512 | reg.set_adstp(Adstp::STOP); | 788 | reg.set_adstp(Adstp::STOP); |
| 513 | }); | 789 | }); |
| 790 | // The software must poll ADSTART until the bit is reset before assuming the | ||
| 791 | // ADC is completely stopped | ||
| 514 | while T::regs().cr().read().adstart() {} | 792 | while T::regs().cr().read().adstart() {} |
| 515 | } | 793 | } |
| 516 | } | 794 | } |
| 517 | } | 795 | } |
| 518 | 796 | ||
| 519 | /// Implemented for ADCs that have a Temperature channel | 797 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { |
| 520 | pub trait TemperatureChannel { | 798 | /// Read sampled data from all injected ADC injected ranks |
| 521 | const CHANNEL: u8; | 799 | /// Clear the JEOS flag to allow a new injected sequence |
| 522 | } | 800 | pub(super) fn read_injected_data() -> [u16; N] { |
| 523 | /// Implemented for ADCs that have a Vref channel | 801 | let mut data = [0u16; N]; |
| 524 | pub trait VrefChannel { | 802 | for i in 0..N { |
| 525 | const CHANNEL: u8; | 803 | data[i] = T::regs().jdr(i).read().jdata(); |
| 526 | } | 804 | } |
| 527 | /// Implemented for ADCs that have a VBat channel | 805 | |
| 528 | pub trait VBatChannel { | 806 | // Clear JEOS by writing 1 |
| 529 | const CHANNEL: u8; | 807 | T::regs().isr().modify(|r| r.set_jeos(true)); |
| 808 | data | ||
| 809 | } | ||
| 530 | } | 810 | } |
| 531 | 811 | ||
| 532 | #[cfg(stm32g4)] | 812 | #[cfg(stm32g4)] |
| 533 | mod g4 { | 813 | mod g4 { |
| 534 | pub use super::*; | 814 | use crate::adc::{TemperatureConverter, VBatConverter, VrefConverter}; |
| 535 | 815 | ||
| 536 | impl TemperatureChannel for crate::peripherals::ADC1 { | 816 | impl TemperatureConverter for crate::peripherals::ADC1 { |
| 537 | const CHANNEL: u8 = 16; | 817 | const CHANNEL: u8 = 16; |
| 538 | } | 818 | } |
| 539 | 819 | ||
| 540 | impl VrefChannel for crate::peripherals::ADC1 { | 820 | impl VrefConverter for crate::peripherals::ADC1 { |
| 541 | const CHANNEL: u8 = 18; | 821 | const CHANNEL: u8 = 18; |
| 542 | } | 822 | } |
| 543 | 823 | ||
| 544 | impl VBatChannel for crate::peripherals::ADC1 { | 824 | impl VBatConverter for crate::peripherals::ADC1 { |
| 545 | const CHANNEL: u8 = 17; | 825 | const CHANNEL: u8 = 17; |
| 546 | } | 826 | } |
| 547 | 827 | ||
| 548 | #[cfg(peri_adc3_common)] | 828 | #[cfg(peri_adc3_common)] |
| 549 | impl VrefChannel for crate::peripherals::ADC3 { | 829 | impl VrefConverter for crate::peripherals::ADC3 { |
| 550 | const CHANNEL: u8 = 18; | 830 | const CHANNEL: u8 = 18; |
| 551 | } | 831 | } |
| 552 | 832 | ||
| 553 | #[cfg(peri_adc3_common)] | 833 | #[cfg(peri_adc3_common)] |
| 554 | impl VBatChannel for crate::peripherals::ADC3 { | 834 | impl VBatConverter for crate::peripherals::ADC3 { |
| 555 | const CHANNEL: u8 = 17; | 835 | const CHANNEL: u8 = 17; |
| 556 | } | 836 | } |
| 557 | 837 | ||
| 558 | #[cfg(not(stm32g4x1))] | 838 | #[cfg(not(stm32g4x1))] |
| 559 | impl VrefChannel for crate::peripherals::ADC4 { | 839 | impl VrefConverter for crate::peripherals::ADC4 { |
| 560 | const CHANNEL: u8 = 18; | 840 | const CHANNEL: u8 = 18; |
| 561 | } | 841 | } |
| 562 | 842 | ||
| 563 | #[cfg(not(stm32g4x1))] | 843 | #[cfg(not(stm32g4x1))] |
| 564 | impl TemperatureChannel for crate::peripherals::ADC5 { | 844 | impl TemperatureConverter for crate::peripherals::ADC5 { |
| 565 | const CHANNEL: u8 = 4; | 845 | const CHANNEL: u8 = 4; |
| 566 | } | 846 | } |
| 567 | 847 | ||
| 568 | #[cfg(not(stm32g4x1))] | 848 | #[cfg(not(stm32g4x1))] |
| 569 | impl VrefChannel for crate::peripherals::ADC5 { | 849 | impl VrefConverter for crate::peripherals::ADC5 { |
| 570 | const CHANNEL: u8 = 18; | 850 | const CHANNEL: u8 = 18; |
| 571 | } | 851 | } |
| 572 | 852 | ||
| 573 | #[cfg(not(stm32g4x1))] | 853 | #[cfg(not(stm32g4x1))] |
| 574 | impl VBatChannel for crate::peripherals::ADC5 { | 854 | impl VBatConverter for crate::peripherals::ADC5 { |
| 575 | const CHANNEL: u8 = 17; | 855 | const CHANNEL: u8 = 17; |
| 576 | } | 856 | } |
| 577 | } | 857 | } |
| @@ -579,13 +859,13 @@ mod g4 { | |||
| 579 | // TODO this should look at each ADC individually and impl the correct channels | 859 | // TODO this should look at each ADC individually and impl the correct channels |
| 580 | #[cfg(stm32h7)] | 860 | #[cfg(stm32h7)] |
| 581 | mod h7 { | 861 | mod h7 { |
| 582 | impl<T: Instance> TemperatureChannel for T { | 862 | impl<T: Instance> TemperatureConverter for T { |
| 583 | const CHANNEL: u8 = 18; | 863 | const CHANNEL: u8 = 18; |
| 584 | } | 864 | } |
| 585 | impl<T: Instance> VrefChannel for T { | 865 | impl<T: Instance> VrefConverter for T { |
| 586 | const CHANNEL: u8 = 19; | 866 | const CHANNEL: u8 = 19; |
| 587 | } | 867 | } |
| 588 | impl<T: Instance> VBatChannel for T { | 868 | impl<T: Instance> VBatConverter for T { |
| 589 | // TODO this should be 14 for H7a/b/35 | 869 | // TODO this should be 14 for H7a/b/35 |
| 590 | const CHANNEL: u8 = 17; | 870 | const CHANNEL: u8 = 17; |
| 591 | } | 871 | } |
diff --git a/embassy-stm32/src/adc/injected.rs b/embassy-stm32/src/adc/injected.rs new file mode 100644 index 000000000..f9f1bba2a --- /dev/null +++ b/embassy-stm32/src/adc/injected.rs | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 3 | |||
| 4 | #[allow(unused_imports)] | ||
| 5 | use embassy_hal_internal::Peri; | ||
| 6 | |||
| 7 | use super::{AnyAdcChannel, SampleTime}; | ||
| 8 | use crate::adc::Adc; | ||
| 9 | #[allow(unused_imports)] | ||
| 10 | use crate::adc::Instance; | ||
| 11 | |||
| 12 | /// Injected ADC sequence with owned channels. | ||
| 13 | pub struct InjectedAdc<T: Instance, const N: usize> { | ||
| 14 | _channels: [(AnyAdcChannel<T>, SampleTime); N], | ||
| 15 | _phantom: PhantomData<T>, | ||
| 16 | } | ||
| 17 | |||
| 18 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { | ||
| 19 | pub(crate) fn new(channels: [(AnyAdcChannel<T>, SampleTime); N]) -> Self { | ||
| 20 | Self { | ||
| 21 | _channels: channels, | ||
| 22 | _phantom: PhantomData, | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | pub fn stop_injected_conversions(&mut self) { | ||
| 27 | Adc::<T>::stop_injected_conversions() | ||
| 28 | } | ||
| 29 | |||
| 30 | pub fn start_injected_conversions(&mut self) { | ||
| 31 | Adc::<T>::start_injected_conversions() | ||
| 32 | } | ||
| 33 | |||
| 34 | pub fn read_injected_samples(&mut self) -> [u16; N] { | ||
| 35 | InjectedAdc::<T, N>::read_injected_data() | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | impl<T: Instance, const N: usize> Drop for InjectedAdc<T, N> { | ||
| 40 | fn drop(&mut self) { | ||
| 41 | Adc::<T>::teardown_dma(); | ||
| 42 | compiler_fence(Ordering::SeqCst); | ||
| 43 | } | ||
| 44 | } | ||
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index ea986f4cf..3bf893a35 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs | |||
| @@ -22,7 +22,7 @@ use core::marker::PhantomData; | |||
| 22 | #[allow(unused)] | 22 | #[allow(unused)] |
| 23 | #[cfg(not(any(adc_f3v3, adc_wba)))] | 23 | #[cfg(not(any(adc_f3v3, adc_wba)))] |
| 24 | pub use _version::*; | 24 | pub use _version::*; |
| 25 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; | 25 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; |
| 26 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] | 26 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] |
| 27 | use embassy_sync::waitqueue::AtomicWaker; | 27 | use embassy_sync::waitqueue::AtomicWaker; |
| 28 | 28 | ||
| @@ -47,8 +47,6 @@ dma_trait!(RxDma4, adc4::Instance); | |||
| 47 | pub struct Adc<'d, T: Instance> { | 47 | pub struct Adc<'d, T: Instance> { |
| 48 | #[allow(unused)] | 48 | #[allow(unused)] |
| 49 | adc: crate::Peri<'d, T>, | 49 | adc: crate::Peri<'d, T>, |
| 50 | #[cfg(not(any(adc_f3v3, adc_f3v2, adc_wba)))] | ||
| 51 | sample_time: SampleTime, | ||
| 52 | } | 50 | } |
| 53 | 51 | ||
| 54 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] | 52 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] |
| @@ -87,14 +85,67 @@ pub(crate) trait SealedAdcChannel<T> { | |||
| 87 | /// Performs a busy-wait delay for a specified number of microseconds. | 85 | /// Performs a busy-wait delay for a specified number of microseconds. |
| 88 | #[allow(unused)] | 86 | #[allow(unused)] |
| 89 | pub(crate) fn blocking_delay_us(us: u32) { | 87 | pub(crate) fn blocking_delay_us(us: u32) { |
| 90 | #[cfg(feature = "time")] | 88 | cfg_if::cfg_if! { |
| 91 | embassy_time::block_for(embassy_time::Duration::from_micros(us as u64)); | 89 | // this does strange things on stm32wlx in low power mode depending on exactly when it's called |
| 92 | #[cfg(not(feature = "time"))] | 90 | // as in sometimes 15 us (1 tick) would take > 20 seconds. |
| 93 | { | 91 | if #[cfg(all(feature = "time", all(not(feature = "low-power"), not(stm32wlex))))] { |
| 94 | let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64; | 92 | let duration = embassy_time::Duration::from_micros(us as u64); |
| 95 | let us = us as u64; | 93 | embassy_time::block_for(duration); |
| 96 | let cycles = freq * us / 1_000_000; | 94 | } else { |
| 97 | cortex_m::asm::delay(cycles as u32); | 95 | let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64; |
| 96 | let us = us as u64; | ||
| 97 | let cycles = freq * us / 1_000_000; | ||
| 98 | cortex_m::asm::delay(cycles as u32); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | /// Implemented for ADCs that have a Temperature channel | ||
| 104 | pub trait TemperatureConverter { | ||
| 105 | const CHANNEL: u8; | ||
| 106 | } | ||
| 107 | /// Implemented for ADCs that have a Vref channel | ||
| 108 | pub trait VrefConverter { | ||
| 109 | const CHANNEL: u8; | ||
| 110 | } | ||
| 111 | /// Implemented for ADCs that have a VBat channel | ||
| 112 | pub trait VBatConverter { | ||
| 113 | const CHANNEL: u8; | ||
| 114 | } | ||
| 115 | |||
| 116 | // NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs | ||
| 117 | /// Internal voltage reference channel. | ||
| 118 | pub struct VrefInt; | ||
| 119 | impl<T: Instance + VrefConverter> AdcChannel<T> for VrefInt {} | ||
| 120 | impl<T: Instance + VrefConverter> SealedAdcChannel<T> for VrefInt { | ||
| 121 | fn channel(&self) -> u8 { | ||
| 122 | T::CHANNEL | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | impl VrefInt { | ||
| 127 | #[cfg(any(adc_f3v1, adc_f3v2))] | ||
| 128 | /// The value that vref would be if vdda was at 3300mv | ||
| 129 | pub fn calibrated_value(&self) -> u16 { | ||
| 130 | crate::pac::VREFINTCAL.data().read() | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | /// Internal temperature channel. | ||
| 135 | pub struct Temperature; | ||
| 136 | impl<T: Instance + TemperatureConverter> AdcChannel<T> for Temperature {} | ||
| 137 | impl<T: Instance + TemperatureConverter> SealedAdcChannel<T> for Temperature { | ||
| 138 | fn channel(&self) -> u8 { | ||
| 139 | T::CHANNEL | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | /// Internal battery voltage channel. | ||
| 144 | pub struct Vbat; | ||
| 145 | impl<T: Instance + VBatConverter> AdcChannel<T> for Vbat {} | ||
| 146 | impl<T: Instance + VBatConverter> SealedAdcChannel<T> for Vbat { | ||
| 147 | fn channel(&self) -> u8 { | ||
| 148 | T::CHANNEL | ||
| 98 | } | 149 | } |
| 99 | } | 150 | } |
| 100 | 151 | ||
diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs new file mode 100644 index 000000000..024c6acdc --- /dev/null +++ b/embassy-stm32/src/adc/ringbuffered.rs | |||
| @@ -0,0 +1,182 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 3 | |||
| 4 | #[allow(unused_imports)] | ||
| 5 | use embassy_hal_internal::Peri; | ||
| 6 | |||
| 7 | use crate::adc::Adc; | ||
| 8 | #[allow(unused_imports)] | ||
| 9 | use crate::adc::{Instance, RxDma}; | ||
| 10 | #[allow(unused_imports)] | ||
| 11 | use crate::dma::{ReadableRingBuffer, TransferOptions}; | ||
| 12 | use crate::rcc; | ||
| 13 | |||
| 14 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 15 | pub struct OverrunError; | ||
| 16 | |||
| 17 | pub struct RingBufferedAdc<'d, T: Instance> { | ||
| 18 | _phantom: PhantomData<T>, | ||
| 19 | ring_buf: ReadableRingBuffer<'d, u16>, | ||
| 20 | } | ||
| 21 | |||
| 22 | impl<'d, T: Instance> RingBufferedAdc<'d, T> { | ||
| 23 | pub(crate) fn new(dma: Peri<'d, impl RxDma<T>>, dma_buf: &'d mut [u16]) -> Self { | ||
| 24 | //dma side setup | ||
| 25 | let opts = TransferOptions { | ||
| 26 | half_transfer_ir: true, | ||
| 27 | circular: true, | ||
| 28 | ..Default::default() | ||
| 29 | }; | ||
| 30 | |||
| 31 | // Safety: we forget the struct before this function returns. | ||
| 32 | let request = dma.request(); | ||
| 33 | |||
| 34 | let ring_buf = | ||
| 35 | unsafe { ReadableRingBuffer::new(dma, request, T::regs().dr().as_ptr() as *mut u16, dma_buf, opts) }; | ||
| 36 | |||
| 37 | Self { | ||
| 38 | _phantom: PhantomData, | ||
| 39 | ring_buf, | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | /// Turns on ADC if it is not already turned on and starts continuous DMA transfer. | ||
| 44 | pub fn start(&mut self) { | ||
| 45 | compiler_fence(Ordering::SeqCst); | ||
| 46 | self.ring_buf.start(); | ||
| 47 | |||
| 48 | Adc::<T>::start(); | ||
| 49 | } | ||
| 50 | |||
| 51 | pub fn stop(&mut self) { | ||
| 52 | Adc::<T>::stop(); | ||
| 53 | |||
| 54 | self.ring_buf.request_pause(); | ||
| 55 | |||
| 56 | compiler_fence(Ordering::SeqCst); | ||
| 57 | } | ||
| 58 | |||
| 59 | pub fn clear(&mut self) { | ||
| 60 | self.ring_buf.clear(); | ||
| 61 | } | ||
| 62 | |||
| 63 | /// Reads measurements from the DMA ring buffer. | ||
| 64 | /// | ||
| 65 | /// This method fills the provided `measurements` array with ADC readings from the DMA buffer. | ||
| 66 | /// The length of the `measurements` array should be exactly half of the DMA buffer length. | ||
| 67 | /// Because interrupts are only generated if half or full DMA transfer completes. | ||
| 68 | /// | ||
| 69 | /// Each call to `read` will populate the `measurements` array in the same order as the channels | ||
| 70 | /// defined with `sequence`. There will be many sequences worth of measurements in this array | ||
| 71 | /// because it only returns if at least half of the DMA buffer is filled. For example if 2 | ||
| 72 | /// channels are sampled `measurements` contain: `[sq0 sq1 sq0 sq1 sq0 sq1 ..]`. | ||
| 73 | /// | ||
| 74 | /// Note that the ADC Datarate can be very fast, it is suggested to use DMA mode inside tightly | ||
| 75 | /// running tasks. Otherwise, you'll see constant Overrun errors occurring, this means that | ||
| 76 | /// you're sampling too quickly for the task to handle, and you may need to increase the buffer size. | ||
| 77 | /// Example: | ||
| 78 | /// ```rust,ignore | ||
| 79 | /// const DMA_BUF_LEN: usize = 120; | ||
| 80 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 81 | /// | ||
| 82 | /// let mut adc = Adc::new(p.ADC1); | ||
| 83 | /// let mut adc_pin0 = p.PA0.degrade_adc(); | ||
| 84 | /// let mut adc_pin1 = p.PA1.degrade_adc(); | ||
| 85 | /// let adc_dma_buf = [0u16; DMA_BUF_LEN]; | ||
| 86 | /// | ||
| 87 | /// let mut ring_buffered_adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered( | ||
| 88 | /// p.DMA2_CH0, | ||
| 89 | /// adc_dma_buf, [ | ||
| 90 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 91 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 92 | /// ].into_iter()); | ||
| 93 | /// | ||
| 94 | /// | ||
| 95 | /// let mut measurements = [0u16; DMA_BUF_LEN / 2]; | ||
| 96 | /// loop { | ||
| 97 | /// match ring_buffered_adc.read(&mut measurements).await { | ||
| 98 | /// Ok(_) => { | ||
| 99 | /// defmt::info!("adc1: {}", measurements); | ||
| 100 | /// } | ||
| 101 | /// Err(e) => { | ||
| 102 | /// defmt::warn!("Error: {:?}", e); | ||
| 103 | /// } | ||
| 104 | /// } | ||
| 105 | /// } | ||
| 106 | /// ``` | ||
| 107 | /// | ||
| 108 | /// | ||
| 109 | /// [`teardown_adc`]: #method.teardown_adc | ||
| 110 | /// [`start_continuous_sampling`]: #method.start_continuous_sampling | ||
| 111 | pub async fn read(&mut self, measurements: &mut [u16]) -> Result<usize, OverrunError> { | ||
| 112 | assert_eq!( | ||
| 113 | self.ring_buf.capacity() / 2, | ||
| 114 | measurements.len(), | ||
| 115 | "Buffer size must be half the size of the ring buffer" | ||
| 116 | ); | ||
| 117 | |||
| 118 | if !self.ring_buf.is_running() { | ||
| 119 | self.start(); | ||
| 120 | } | ||
| 121 | |||
| 122 | #[cfg(adc_v2)] | ||
| 123 | { | ||
| 124 | // Clear overrun flag if set. | ||
| 125 | if T::regs().sr().read().ovr() { | ||
| 126 | self.stop(); | ||
| 127 | |||
| 128 | return Err(OverrunError); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | self.ring_buf.read_exact(measurements).await.map_err(|_| OverrunError) | ||
| 133 | } | ||
| 134 | |||
| 135 | /// Read bytes that are readily available in the ring buffer. | ||
| 136 | /// If no bytes are currently available in the buffer the call waits until the some | ||
| 137 | /// bytes are available (at least one byte and at most half the buffer size) | ||
| 138 | /// | ||
| 139 | /// Background receive is started if `start_continuous_sampling()` has not been previously called. | ||
| 140 | /// | ||
| 141 | /// Receive in the background is terminated if an error is returned. | ||
| 142 | /// It must then manually be started again by calling `start_continuous_sampling()` or by re-calling `blocking_read()`. | ||
| 143 | pub fn blocking_read(&mut self, buf: &mut [u16]) -> Result<usize, OverrunError> { | ||
| 144 | if !self.ring_buf.is_running() { | ||
| 145 | self.start(); | ||
| 146 | } | ||
| 147 | |||
| 148 | #[cfg(adc_v2)] | ||
| 149 | { | ||
| 150 | // Clear overrun flag if set. | ||
| 151 | if T::regs().sr().read().ovr() { | ||
| 152 | self.stop(); | ||
| 153 | |||
| 154 | return Err(OverrunError); | ||
| 155 | } | ||
| 156 | } | ||
| 157 | loop { | ||
| 158 | match self.ring_buf.read(buf) { | ||
| 159 | Ok((0, _)) => {} | ||
| 160 | Ok((len, _)) => { | ||
| 161 | return Ok(len); | ||
| 162 | } | ||
| 163 | Err(_) => { | ||
| 164 | self.stop(); | ||
| 165 | |||
| 166 | return Err(OverrunError); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | } | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | impl<T: Instance> Drop for RingBufferedAdc<'_, T> { | ||
| 174 | fn drop(&mut self) { | ||
| 175 | Adc::<T>::teardown_dma(); | ||
| 176 | |||
| 177 | compiler_fence(Ordering::SeqCst); | ||
| 178 | |||
| 179 | self.ring_buf.request_pause(); | ||
| 180 | rcc::disable::<T>(); | ||
| 181 | } | ||
| 182 | } | ||
diff --git a/embassy-stm32/src/adc/ringbuffered_v2.rs b/embassy-stm32/src/adc/ringbuffered_v2.rs deleted file mode 100644 index 6f69e8486..000000000 --- a/embassy-stm32/src/adc/ringbuffered_v2.rs +++ /dev/null | |||
| @@ -1,432 +0,0 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | use core::mem; | ||
| 3 | use core::sync::atomic::{compiler_fence, Ordering}; | ||
| 4 | |||
| 5 | use stm32_metapac::adc::vals::SampleTime; | ||
| 6 | |||
| 7 | use crate::adc::{Adc, AdcChannel, Instance, RxDma}; | ||
| 8 | use crate::dma::{Priority, ReadableRingBuffer, TransferOptions}; | ||
| 9 | use crate::pac::adc::vals; | ||
| 10 | use crate::{rcc, Peri}; | ||
| 11 | |||
| 12 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 13 | pub struct OverrunError; | ||
| 14 | |||
| 15 | fn clear_interrupt_flags(r: crate::pac::adc::Adc) { | ||
| 16 | r.sr().modify(|regs| { | ||
| 17 | regs.set_eoc(false); | ||
| 18 | regs.set_ovr(false); | ||
| 19 | }); | ||
| 20 | } | ||
| 21 | |||
| 22 | #[derive(PartialOrd, PartialEq, Debug, Clone, Copy)] | ||
| 23 | pub enum Sequence { | ||
| 24 | One, | ||
| 25 | Two, | ||
| 26 | Three, | ||
| 27 | Four, | ||
| 28 | Five, | ||
| 29 | Six, | ||
| 30 | Seven, | ||
| 31 | Eight, | ||
| 32 | Nine, | ||
| 33 | Ten, | ||
| 34 | Eleven, | ||
| 35 | Twelve, | ||
| 36 | Thirteen, | ||
| 37 | Fourteen, | ||
| 38 | Fifteen, | ||
| 39 | Sixteen, | ||
| 40 | } | ||
| 41 | |||
| 42 | impl From<Sequence> for u8 { | ||
| 43 | fn from(s: Sequence) -> u8 { | ||
| 44 | match s { | ||
| 45 | Sequence::One => 0, | ||
| 46 | Sequence::Two => 1, | ||
| 47 | Sequence::Three => 2, | ||
| 48 | Sequence::Four => 3, | ||
| 49 | Sequence::Five => 4, | ||
| 50 | Sequence::Six => 5, | ||
| 51 | Sequence::Seven => 6, | ||
| 52 | Sequence::Eight => 7, | ||
| 53 | Sequence::Nine => 8, | ||
| 54 | Sequence::Ten => 9, | ||
| 55 | Sequence::Eleven => 10, | ||
| 56 | Sequence::Twelve => 11, | ||
| 57 | Sequence::Thirteen => 12, | ||
| 58 | Sequence::Fourteen => 13, | ||
| 59 | Sequence::Fifteen => 14, | ||
| 60 | Sequence::Sixteen => 15, | ||
| 61 | } | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | impl From<u8> for Sequence { | ||
| 66 | fn from(val: u8) -> Self { | ||
| 67 | match val { | ||
| 68 | 0 => Sequence::One, | ||
| 69 | 1 => Sequence::Two, | ||
| 70 | 2 => Sequence::Three, | ||
| 71 | 3 => Sequence::Four, | ||
| 72 | 4 => Sequence::Five, | ||
| 73 | 5 => Sequence::Six, | ||
| 74 | 6 => Sequence::Seven, | ||
| 75 | 7 => Sequence::Eight, | ||
| 76 | 8 => Sequence::Nine, | ||
| 77 | 9 => Sequence::Ten, | ||
| 78 | 10 => Sequence::Eleven, | ||
| 79 | 11 => Sequence::Twelve, | ||
| 80 | 12 => Sequence::Thirteen, | ||
| 81 | 13 => Sequence::Fourteen, | ||
| 82 | 14 => Sequence::Fifteen, | ||
| 83 | 15 => Sequence::Sixteen, | ||
| 84 | _ => panic!("Invalid sequence number"), | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | pub struct RingBufferedAdc<'d, T: Instance> { | ||
| 90 | _phantom: PhantomData<T>, | ||
| 91 | ring_buf: ReadableRingBuffer<'d, u16>, | ||
| 92 | } | ||
| 93 | |||
| 94 | impl<'d, T: Instance> Adc<'d, T> { | ||
| 95 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 96 | /// | ||
| 97 | /// The `dma_buf` should be large enough to prevent DMA buffer overrun. | ||
| 98 | /// The length of the `dma_buf` should be a multiple of the ADC channel count. | ||
| 99 | /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. | ||
| 100 | /// | ||
| 101 | /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length. | ||
| 102 | /// It is critical to call `read` frequently to prevent DMA buffer overrun. | ||
| 103 | /// | ||
| 104 | /// [`read`]: #method.read | ||
| 105 | pub fn into_ring_buffered(self, dma: Peri<'d, impl RxDma<T>>, dma_buf: &'d mut [u16]) -> RingBufferedAdc<'d, T> { | ||
| 106 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 107 | |||
| 108 | let opts: crate::dma::TransferOptions = TransferOptions { | ||
| 109 | half_transfer_ir: true, | ||
| 110 | priority: Priority::VeryHigh, | ||
| 111 | ..Default::default() | ||
| 112 | }; | ||
| 113 | |||
| 114 | // Safety: we forget the struct before this function returns. | ||
| 115 | let rx_src = T::regs().dr().as_ptr() as *mut u16; | ||
| 116 | let request = dma.request(); | ||
| 117 | |||
| 118 | let ring_buf = unsafe { ReadableRingBuffer::new(dma, request, rx_src, dma_buf, opts) }; | ||
| 119 | |||
| 120 | // Don't disable the clock | ||
| 121 | mem::forget(self); | ||
| 122 | |||
| 123 | RingBufferedAdc { | ||
| 124 | _phantom: PhantomData, | ||
| 125 | ring_buf, | ||
| 126 | } | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | impl<'d, T: Instance> RingBufferedAdc<'d, T> { | ||
| 131 | fn is_on() -> bool { | ||
| 132 | T::regs().cr2().read().adon() | ||
| 133 | } | ||
| 134 | |||
| 135 | fn stop_adc() { | ||
| 136 | T::regs().cr2().modify(|reg| { | ||
| 137 | reg.set_adon(false); | ||
| 138 | }); | ||
| 139 | } | ||
| 140 | |||
| 141 | fn start_adc() { | ||
| 142 | T::regs().cr2().modify(|reg| { | ||
| 143 | reg.set_adon(true); | ||
| 144 | }); | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Sets the channel sample time | ||
| 148 | /// | ||
| 149 | /// ## SAFETY: | ||
| 150 | /// - ADON == 0 i.e ADC must not be enabled when this is called. | ||
| 151 | unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | ||
| 152 | if ch <= 9 { | ||
| 153 | T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 154 | } else { | ||
| 155 | T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | fn set_channels_sample_time(&mut self, ch: &[u8], sample_time: SampleTime) { | ||
| 160 | let ch_iter = ch.iter(); | ||
| 161 | for idx in ch_iter { | ||
| 162 | unsafe { | ||
| 163 | Self::set_channel_sample_time(*idx, sample_time); | ||
| 164 | } | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | pub fn set_sample_sequence( | ||
| 169 | &mut self, | ||
| 170 | sequence: Sequence, | ||
| 171 | channel: &mut impl AdcChannel<T>, | ||
| 172 | sample_time: SampleTime, | ||
| 173 | ) { | ||
| 174 | let was_on = Self::is_on(); | ||
| 175 | if !was_on { | ||
| 176 | Self::start_adc(); | ||
| 177 | } | ||
| 178 | |||
| 179 | // Check the sequence is long enough | ||
| 180 | T::regs().sqr1().modify(|r| { | ||
| 181 | let prev: Sequence = r.l().into(); | ||
| 182 | if prev < sequence { | ||
| 183 | let new_l: Sequence = sequence; | ||
| 184 | trace!("Setting sequence length from {:?} to {:?}", prev as u8, new_l as u8); | ||
| 185 | r.set_l(sequence.into()) | ||
| 186 | } else { | ||
| 187 | r.set_l(prev.into()) | ||
| 188 | } | ||
| 189 | }); | ||
| 190 | |||
| 191 | // Set this GPIO as an analog input. | ||
| 192 | channel.setup(); | ||
| 193 | |||
| 194 | // Set the channel in the right sequence field. | ||
| 195 | match sequence { | ||
| 196 | Sequence::One => T::regs().sqr3().modify(|w| w.set_sq(0, channel.channel())), | ||
| 197 | Sequence::Two => T::regs().sqr3().modify(|w| w.set_sq(1, channel.channel())), | ||
| 198 | Sequence::Three => T::regs().sqr3().modify(|w| w.set_sq(2, channel.channel())), | ||
| 199 | Sequence::Four => T::regs().sqr3().modify(|w| w.set_sq(3, channel.channel())), | ||
| 200 | Sequence::Five => T::regs().sqr3().modify(|w| w.set_sq(4, channel.channel())), | ||
| 201 | Sequence::Six => T::regs().sqr3().modify(|w| w.set_sq(5, channel.channel())), | ||
| 202 | Sequence::Seven => T::regs().sqr2().modify(|w| w.set_sq(0, channel.channel())), | ||
| 203 | Sequence::Eight => T::regs().sqr2().modify(|w| w.set_sq(1, channel.channel())), | ||
| 204 | Sequence::Nine => T::regs().sqr2().modify(|w| w.set_sq(2, channel.channel())), | ||
| 205 | Sequence::Ten => T::regs().sqr2().modify(|w| w.set_sq(3, channel.channel())), | ||
| 206 | Sequence::Eleven => T::regs().sqr2().modify(|w| w.set_sq(4, channel.channel())), | ||
| 207 | Sequence::Twelve => T::regs().sqr2().modify(|w| w.set_sq(5, channel.channel())), | ||
| 208 | Sequence::Thirteen => T::regs().sqr1().modify(|w| w.set_sq(0, channel.channel())), | ||
| 209 | Sequence::Fourteen => T::regs().sqr1().modify(|w| w.set_sq(1, channel.channel())), | ||
| 210 | Sequence::Fifteen => T::regs().sqr1().modify(|w| w.set_sq(2, channel.channel())), | ||
| 211 | Sequence::Sixteen => T::regs().sqr1().modify(|w| w.set_sq(3, channel.channel())), | ||
| 212 | }; | ||
| 213 | |||
| 214 | if !was_on { | ||
| 215 | Self::stop_adc(); | ||
| 216 | } | ||
| 217 | |||
| 218 | self.set_channels_sample_time(&[channel.channel()], sample_time); | ||
| 219 | |||
| 220 | Self::start_adc(); | ||
| 221 | } | ||
| 222 | |||
| 223 | /// Turns on ADC if it is not already turned on and starts continuous DMA transfer. | ||
| 224 | pub fn start(&mut self) -> Result<(), OverrunError> { | ||
| 225 | self.setup_adc(); | ||
| 226 | self.ring_buf.clear(); | ||
| 227 | |||
| 228 | Ok(()) | ||
| 229 | } | ||
| 230 | |||
| 231 | fn stop(&mut self, err: OverrunError) -> Result<usize, OverrunError> { | ||
| 232 | self.teardown_adc(); | ||
| 233 | Err(err) | ||
| 234 | } | ||
| 235 | |||
| 236 | /// Stops DMA transfer. | ||
| 237 | /// It does not turn off ADC. | ||
| 238 | /// Calling `start` restarts continuous DMA transfer. | ||
| 239 | /// | ||
| 240 | /// [`start`]: #method.start | ||
| 241 | pub fn teardown_adc(&mut self) { | ||
| 242 | // Stop the DMA transfer | ||
| 243 | self.ring_buf.request_pause(); | ||
| 244 | |||
| 245 | let r = T::regs(); | ||
| 246 | |||
| 247 | // Stop ADC | ||
| 248 | r.cr2().modify(|reg| { | ||
| 249 | // Stop ADC | ||
| 250 | reg.set_swstart(false); | ||
| 251 | // Stop DMA | ||
| 252 | reg.set_dma(false); | ||
| 253 | }); | ||
| 254 | |||
| 255 | r.cr1().modify(|w| { | ||
| 256 | // Disable interrupt for end of conversion | ||
| 257 | w.set_eocie(false); | ||
| 258 | // Disable interrupt for overrun | ||
| 259 | w.set_ovrie(false); | ||
| 260 | }); | ||
| 261 | |||
| 262 | clear_interrupt_flags(r); | ||
| 263 | |||
| 264 | compiler_fence(Ordering::SeqCst); | ||
| 265 | } | ||
| 266 | |||
| 267 | fn setup_adc(&mut self) { | ||
| 268 | compiler_fence(Ordering::SeqCst); | ||
| 269 | |||
| 270 | self.ring_buf.start(); | ||
| 271 | |||
| 272 | let r = T::regs(); | ||
| 273 | |||
| 274 | // Enable ADC | ||
| 275 | let was_on = Self::is_on(); | ||
| 276 | if !was_on { | ||
| 277 | r.cr2().modify(|reg| { | ||
| 278 | reg.set_adon(false); | ||
| 279 | reg.set_swstart(false); | ||
| 280 | }); | ||
| 281 | } | ||
| 282 | |||
| 283 | // Clear all interrupts | ||
| 284 | r.sr().modify(|regs| { | ||
| 285 | regs.set_eoc(false); | ||
| 286 | regs.set_ovr(false); | ||
| 287 | regs.set_strt(false); | ||
| 288 | }); | ||
| 289 | |||
| 290 | r.cr1().modify(|w| { | ||
| 291 | // Enable interrupt for end of conversion | ||
| 292 | w.set_eocie(true); | ||
| 293 | // Enable interrupt for overrun | ||
| 294 | w.set_ovrie(true); | ||
| 295 | // Scanning converisons of multiple channels | ||
| 296 | w.set_scan(true); | ||
| 297 | // Continuous conversion mode | ||
| 298 | w.set_discen(false); | ||
| 299 | }); | ||
| 300 | |||
| 301 | r.cr2().modify(|w| { | ||
| 302 | // Enable DMA mode | ||
| 303 | w.set_dma(true); | ||
| 304 | // Enable continuous conversions | ||
| 305 | w.set_cont(true); | ||
| 306 | // DMA requests are issues as long as DMA=1 and data are converted. | ||
| 307 | w.set_dds(vals::Dds::CONTINUOUS); | ||
| 308 | // EOC flag is set at the end of each conversion. | ||
| 309 | w.set_eocs(vals::Eocs::EACH_CONVERSION); | ||
| 310 | }); | ||
| 311 | |||
| 312 | // Begin ADC conversions | ||
| 313 | T::regs().cr2().modify(|reg| { | ||
| 314 | reg.set_adon(true); | ||
| 315 | reg.set_swstart(true); | ||
| 316 | }); | ||
| 317 | |||
| 318 | super::blocking_delay_us(3); | ||
| 319 | } | ||
| 320 | |||
| 321 | /// Read bytes that are readily available in the ring buffer. | ||
| 322 | /// If no bytes are currently available in the buffer the call waits until the some | ||
| 323 | /// bytes are available (at least one byte and at most half the buffer size) | ||
| 324 | /// | ||
| 325 | /// Background receive is started if `start()` has not been previously called. | ||
| 326 | /// | ||
| 327 | /// Receive in the background is terminated if an error is returned. | ||
| 328 | /// It must then manually be started again by calling `start()` or by re-calling `read()`. | ||
| 329 | pub fn blocking_read<const N: usize>(&mut self, buf: &mut [u16; N]) -> Result<usize, OverrunError> { | ||
| 330 | let r = T::regs(); | ||
| 331 | |||
| 332 | // Start background receive if it was not already started | ||
| 333 | if !r.cr2().read().dma() { | ||
| 334 | self.start()?; | ||
| 335 | } | ||
| 336 | |||
| 337 | // Clear overrun flag if set. | ||
| 338 | if r.sr().read().ovr() { | ||
| 339 | return self.stop(OverrunError); | ||
| 340 | } | ||
| 341 | |||
| 342 | loop { | ||
| 343 | match self.ring_buf.read(buf) { | ||
| 344 | Ok((0, _)) => {} | ||
| 345 | Ok((len, _)) => { | ||
| 346 | return Ok(len); | ||
| 347 | } | ||
| 348 | Err(_) => { | ||
| 349 | return self.stop(OverrunError); | ||
| 350 | } | ||
| 351 | } | ||
| 352 | } | ||
| 353 | } | ||
| 354 | |||
| 355 | /// Reads measurements from the DMA ring buffer. | ||
| 356 | /// | ||
| 357 | /// This method fills the provided `measurements` array with ADC readings from the DMA buffer. | ||
| 358 | /// The length of the `measurements` array should be exactly half of the DMA buffer length. Because interrupts are only generated if half or full DMA transfer completes. | ||
| 359 | /// | ||
| 360 | /// Each call to `read` will populate the `measurements` array in the same order as the channels defined with `set_sample_sequence`. | ||
| 361 | /// There will be many sequences worth of measurements in this array because it only returns if at least half of the DMA buffer is filled. | ||
| 362 | /// For example if 3 channels are sampled `measurements` contain: `[sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3..]`. | ||
| 363 | /// | ||
| 364 | /// If an error is returned, it indicates a DMA overrun, and the process must be restarted by calling `start` or `read` again. | ||
| 365 | /// | ||
| 366 | /// By default, the ADC fills the DMA buffer as quickly as possible. To control the sample rate, call `teardown_adc` after each readout, and then start the DMA again at the desired interval. | ||
| 367 | /// Note that even if using `teardown_adc` to control the sample rate, with each call to `read`, measurements equivalent to half the size of the DMA buffer are still collected. | ||
| 368 | /// | ||
| 369 | /// Example: | ||
| 370 | /// ```rust,ignore | ||
| 371 | /// const DMA_BUF_LEN: usize = 120; | ||
| 372 | /// let adc_dma_buf = [0u16; DMA_BUF_LEN]; | ||
| 373 | /// let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(p.DMA2_CH0, adc_dma_buf); | ||
| 374 | /// | ||
| 375 | /// adc.set_sample_sequence(Sequence::One, &mut p.PA0, SampleTime::CYCLES112); | ||
| 376 | /// adc.set_sample_sequence(Sequence::Two, &mut p.PA1, SampleTime::CYCLES112); | ||
| 377 | /// adc.set_sample_sequence(Sequence::Three, &mut p.PA2, SampleTime::CYCLES112); | ||
| 378 | /// | ||
| 379 | /// let mut measurements = [0u16; DMA_BUF_LEN / 2]; | ||
| 380 | /// loop { | ||
| 381 | /// match adc.read(&mut measurements).await { | ||
| 382 | /// Ok(_) => { | ||
| 383 | /// defmt::info!("adc1: {}", measurements); | ||
| 384 | /// // Only needed to manually control sample rate. | ||
| 385 | /// adc.teardown_adc(); | ||
| 386 | /// } | ||
| 387 | /// Err(e) => { | ||
| 388 | /// defmt::warn!("Error: {:?}", e); | ||
| 389 | /// // DMA overrun, next call to `read` restarts ADC. | ||
| 390 | /// } | ||
| 391 | /// } | ||
| 392 | /// | ||
| 393 | /// // Manually control sample rate. | ||
| 394 | /// Timer::after_millis(100).await; | ||
| 395 | /// } | ||
| 396 | /// ``` | ||
| 397 | /// | ||
| 398 | /// | ||
| 399 | /// [`set_sample_sequence`]: #method.set_sample_sequence | ||
| 400 | /// [`teardown_adc`]: #method.teardown_adc | ||
| 401 | /// [`start`]: #method.start | ||
| 402 | pub async fn read<const N: usize>(&mut self, measurements: &mut [u16; N]) -> Result<usize, OverrunError> { | ||
| 403 | assert_eq!( | ||
| 404 | self.ring_buf.capacity() / 2, | ||
| 405 | N, | ||
| 406 | "Buffer size must be half the size of the ring buffer" | ||
| 407 | ); | ||
| 408 | |||
| 409 | let r = T::regs(); | ||
| 410 | |||
| 411 | // Start background receive if it was not already started | ||
| 412 | if !r.cr2().read().dma() { | ||
| 413 | self.start()?; | ||
| 414 | } | ||
| 415 | |||
| 416 | // Clear overrun flag if set. | ||
| 417 | if r.sr().read().ovr() { | ||
| 418 | return self.stop(OverrunError); | ||
| 419 | } | ||
| 420 | match self.ring_buf.read_exact(measurements).await { | ||
| 421 | Ok(len) => Ok(len), | ||
| 422 | Err(_) => self.stop(OverrunError), | ||
| 423 | } | ||
| 424 | } | ||
| 425 | } | ||
| 426 | |||
| 427 | impl<T: Instance> Drop for RingBufferedAdc<'_, T> { | ||
| 428 | fn drop(&mut self) { | ||
| 429 | self.teardown_adc(); | ||
| 430 | rcc::disable::<T>(); | ||
| 431 | } | ||
| 432 | } | ||
diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index d09374876..97557ee8a 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs | |||
| @@ -5,11 +5,12 @@ use core::task::Poll; | |||
| 5 | #[cfg(adc_l0)] | 5 | #[cfg(adc_l0)] |
| 6 | use stm32_metapac::adc::vals::Ckmode; | 6 | use stm32_metapac::adc::vals::Ckmode; |
| 7 | 7 | ||
| 8 | use super::blocking_delay_us; | 8 | #[cfg(not(adc_l0))] |
| 9 | use super::Vbat; | ||
| 10 | use super::{Temperature, VrefInt, blocking_delay_us}; | ||
| 9 | use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; | 11 | use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; |
| 10 | use crate::interrupt::typelevel::Interrupt; | 12 | use crate::interrupt::typelevel::Interrupt; |
| 11 | use crate::peripherals::ADC1; | 13 | use crate::{Peri, interrupt, rcc}; |
| 12 | use crate::{interrupt, rcc, Peri}; | ||
| 13 | 14 | ||
| 14 | mod watchdog_v1; | 15 | mod watchdog_v1; |
| 15 | pub use watchdog_v1::WatchdogChannels; | 16 | pub use watchdog_v1::WatchdogChannels; |
| @@ -42,36 +43,23 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 42 | } | 43 | } |
| 43 | 44 | ||
| 44 | #[cfg(not(adc_l0))] | 45 | #[cfg(not(adc_l0))] |
| 45 | pub struct Vbat; | 46 | impl super::VBatConverter for crate::peripherals::ADC1 { |
| 46 | 47 | const CHANNEL: u8 = 18; | |
| 47 | #[cfg(not(adc_l0))] | 48 | } |
| 48 | impl AdcChannel<ADC1> for Vbat {} | ||
| 49 | 49 | ||
| 50 | #[cfg(not(adc_l0))] | 50 | #[cfg(not(adc_l0))] |
| 51 | impl super::SealedAdcChannel<ADC1> for Vbat { | 51 | impl super::VrefConverter for crate::peripherals::ADC1 { |
| 52 | fn channel(&self) -> u8 { | 52 | const CHANNEL: u8 = 17; |
| 53 | 18 | ||
| 54 | } | ||
| 55 | } | 53 | } |
| 56 | 54 | ||
| 57 | pub struct Vref; | 55 | #[cfg(adc_l0)] |
| 58 | impl AdcChannel<ADC1> for Vref {} | 56 | impl super::VrefConverter for crate::peripherals::ADC1 { |
| 59 | impl super::SealedAdcChannel<ADC1> for Vref { | 57 | const CHANNEL: u8 = 18; |
| 60 | fn channel(&self) -> u8 { | ||
| 61 | 17 | ||
| 62 | } | ||
| 63 | } | 58 | } |
| 64 | 59 | ||
| 65 | pub struct Temperature; | 60 | #[cfg(not(adc_l0))] |
| 66 | impl AdcChannel<ADC1> for Temperature {} | 61 | impl super::TemperatureConverter for crate::peripherals::ADC1 { |
| 67 | impl super::SealedAdcChannel<ADC1> for Temperature { | 62 | const CHANNEL: u8 = 16; |
| 68 | fn channel(&self) -> u8 { | ||
| 69 | if cfg!(adc_l0) { | ||
| 70 | 18 | ||
| 71 | } else { | ||
| 72 | 16 | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | 63 | } |
| 76 | 64 | ||
| 77 | impl<'d, T: Instance> Adc<'d, T> { | 65 | impl<'d, T: Instance> Adc<'d, T> { |
| @@ -118,10 +106,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 118 | T::Interrupt::enable(); | 106 | T::Interrupt::enable(); |
| 119 | } | 107 | } |
| 120 | 108 | ||
| 121 | Self { | 109 | Self { adc } |
| 122 | adc, | ||
| 123 | sample_time: SampleTime::from_bits(0), | ||
| 124 | } | ||
| 125 | } | 110 | } |
| 126 | 111 | ||
| 127 | #[cfg(not(adc_l0))] | 112 | #[cfg(not(adc_l0))] |
| @@ -134,12 +119,12 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 134 | Vbat | 119 | Vbat |
| 135 | } | 120 | } |
| 136 | 121 | ||
| 137 | pub fn enable_vref(&self) -> Vref { | 122 | pub fn enable_vref(&self) -> VrefInt { |
| 138 | // Table 28. Embedded internal reference voltage | 123 | // Table 28. Embedded internal reference voltage |
| 139 | // tstart = 10μs | 124 | // tstart = 10μs |
| 140 | T::regs().ccr().modify(|reg| reg.set_vrefen(true)); | 125 | T::regs().ccr().modify(|reg| reg.set_vrefen(true)); |
| 141 | blocking_delay_us(10); | 126 | blocking_delay_us(10); |
| 142 | Vref | 127 | VrefInt |
| 143 | } | 128 | } |
| 144 | 129 | ||
| 145 | pub fn enable_temperature(&self) -> Temperature { | 130 | pub fn enable_temperature(&self) -> Temperature { |
| @@ -153,10 +138,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 153 | Temperature | 138 | Temperature |
| 154 | } | 139 | } |
| 155 | 140 | ||
| 156 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 157 | self.sample_time = sample_time; | ||
| 158 | } | ||
| 159 | |||
| 160 | pub fn set_resolution(&mut self, resolution: Resolution) { | 141 | pub fn set_resolution(&mut self, resolution: Resolution) { |
| 161 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); | 142 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); |
| 162 | } | 143 | } |
| @@ -167,12 +148,13 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 167 | T::regs().cfgr2().modify(|reg| reg.set_ckmode(ckmode)); | 148 | T::regs().cfgr2().modify(|reg| reg.set_ckmode(ckmode)); |
| 168 | } | 149 | } |
| 169 | 150 | ||
| 170 | pub async fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 151 | pub async fn read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { |
| 171 | let ch_num = channel.channel(); | 152 | let ch_num = channel.channel(); |
| 172 | channel.setup(); | 153 | channel.setup(); |
| 173 | 154 | ||
| 174 | // A.7.5 Single conversion sequence code example - Software trigger | 155 | // A.7.5 Single conversion sequence code example - Software trigger |
| 175 | T::regs().chselr().write(|reg| reg.set_chsel_x(ch_num as usize, true)); | 156 | T::regs().chselr().write(|reg| reg.set_chsel_x(ch_num as usize, true)); |
| 157 | T::regs().smpr().modify(|reg| reg.set_smp(sample_time.into())); | ||
| 176 | 158 | ||
| 177 | self.convert().await | 159 | self.convert().await |
| 178 | } | 160 | } |
| @@ -183,7 +165,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 183 | reg.set_eosmp(true); | 165 | reg.set_eosmp(true); |
| 184 | }); | 166 | }); |
| 185 | 167 | ||
| 186 | T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.into())); | ||
| 187 | T::regs().ier().modify(|w| w.set_eocie(true)); | 168 | T::regs().ier().modify(|w| w.set_eocie(true)); |
| 188 | T::regs().cr().modify(|reg| reg.set_adstart(true)); | 169 | T::regs().cr().modify(|reg| reg.set_adstart(true)); |
| 189 | 170 | ||
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index e94a25b24..88a8b96ed 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs | |||
| @@ -1,23 +1,43 @@ | |||
| 1 | use super::blocking_delay_us; | 1 | use core::mem; |
| 2 | use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; | 2 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 3 | use crate::peripherals::ADC1; | 3 | |
| 4 | use super::{Temperature, Vbat, VrefInt, blocking_delay_us}; | ||
| 5 | use crate::adc::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel}; | ||
| 6 | use crate::pac::adc::vals; | ||
| 4 | use crate::time::Hertz; | 7 | use crate::time::Hertz; |
| 5 | use crate::{rcc, Peri}; | 8 | use crate::{Peri, rcc}; |
| 9 | |||
| 10 | mod ringbuffered; | ||
| 11 | pub use ringbuffered::RingBufferedAdc; | ||
| 6 | 12 | ||
| 7 | mod ringbuffered_v2; | 13 | fn clear_interrupt_flags(r: crate::pac::adc::Adc) { |
| 8 | pub use ringbuffered_v2::{RingBufferedAdc, Sequence}; | 14 | r.sr().modify(|regs| { |
| 15 | regs.set_eoc(false); | ||
| 16 | regs.set_ovr(false); | ||
| 17 | }); | ||
| 18 | } | ||
| 9 | 19 | ||
| 10 | /// Default VREF voltage used for sample conversion to millivolts. | 20 | /// Default VREF voltage used for sample conversion to millivolts. |
| 11 | pub const VREF_DEFAULT_MV: u32 = 3300; | 21 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| 12 | /// VREF voltage used for factory calibration of VREFINTCAL register. | 22 | /// VREF voltage used for factory calibration of VREFINTCAL register. |
| 13 | pub const VREF_CALIB_MV: u32 = 3300; | 23 | pub const VREF_CALIB_MV: u32 = 3300; |
| 14 | 24 | ||
| 15 | pub struct VrefInt; | 25 | impl super::VrefConverter for crate::peripherals::ADC1 { |
| 16 | impl AdcChannel<ADC1> for VrefInt {} | 26 | const CHANNEL: u8 = 17; |
| 17 | impl super::SealedAdcChannel<ADC1> for VrefInt { | 27 | } |
| 18 | fn channel(&self) -> u8 { | 28 | |
| 19 | 17 | 29 | #[cfg(any(stm32f2, stm32f40x, stm32f41x))] |
| 20 | } | 30 | impl super::TemperatureConverter for crate::peripherals::ADC1 { |
| 31 | const CHANNEL: u8 = 16; | ||
| 32 | } | ||
| 33 | |||
| 34 | #[cfg(not(any(stm32f2, stm32f40x, stm32f41x)))] | ||
| 35 | impl super::TemperatureConverter for crate::peripherals::ADC1 { | ||
| 36 | const CHANNEL: u8 = 18; | ||
| 37 | } | ||
| 38 | |||
| 39 | impl super::VBatConverter for crate::peripherals::ADC1 { | ||
| 40 | const CHANNEL: u8 = 18; | ||
| 21 | } | 41 | } |
| 22 | 42 | ||
| 23 | impl VrefInt { | 43 | impl VrefInt { |
| @@ -27,20 +47,6 @@ impl VrefInt { | |||
| 27 | } | 47 | } |
| 28 | } | 48 | } |
| 29 | 49 | ||
| 30 | pub struct Temperature; | ||
| 31 | impl AdcChannel<ADC1> for Temperature {} | ||
| 32 | impl super::SealedAdcChannel<ADC1> for Temperature { | ||
| 33 | fn channel(&self) -> u8 { | ||
| 34 | cfg_if::cfg_if! { | ||
| 35 | if #[cfg(any(stm32f2, stm32f40x, stm32f41x))] { | ||
| 36 | 16 | ||
| 37 | } else { | ||
| 38 | 18 | ||
| 39 | } | ||
| 40 | } | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | impl Temperature { | 50 | impl Temperature { |
| 45 | /// Time needed for temperature sensor readings to stabilize | 51 | /// Time needed for temperature sensor readings to stabilize |
| 46 | pub fn start_time_us() -> u32 { | 52 | pub fn start_time_us() -> u32 { |
| @@ -48,14 +54,6 @@ impl Temperature { | |||
| 48 | } | 54 | } |
| 49 | } | 55 | } |
| 50 | 56 | ||
| 51 | pub struct Vbat; | ||
| 52 | impl AdcChannel<ADC1> for Vbat {} | ||
| 53 | impl super::SealedAdcChannel<ADC1> for Vbat { | ||
| 54 | fn channel(&self) -> u8 { | ||
| 55 | 18 | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | enum Prescaler { | 57 | enum Prescaler { |
| 60 | Div2, | 58 | Div2, |
| 61 | Div4, | 59 | Div4, |
| @@ -106,18 +104,50 @@ where | |||
| 106 | 104 | ||
| 107 | blocking_delay_us(3); | 105 | blocking_delay_us(3); |
| 108 | 106 | ||
| 109 | Self { | 107 | Self { adc } |
| 110 | adc, | ||
| 111 | sample_time: SampleTime::from_bits(0), | ||
| 112 | } | ||
| 113 | } | 108 | } |
| 114 | 109 | ||
| 115 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | 110 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. |
| 116 | self.sample_time = sample_time; | 111 | /// |
| 112 | /// The `dma_buf` should be large enough to prevent DMA buffer overrun. | ||
| 113 | /// The length of the `dma_buf` should be a multiple of the ADC channel count. | ||
| 114 | /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. | ||
| 115 | /// | ||
| 116 | /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length. | ||
| 117 | /// It is critical to call `read` frequently to prevent DMA buffer overrun. | ||
| 118 | /// | ||
| 119 | /// [`read`]: #method.read | ||
| 120 | pub fn into_ring_buffered<'a>( | ||
| 121 | self, | ||
| 122 | dma: Peri<'d, impl RxDma<T>>, | ||
| 123 | dma_buf: &'d mut [u16], | ||
| 124 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 125 | ) -> RingBufferedAdc<'d, T> { | ||
| 126 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 127 | |||
| 128 | Self::configure_sequence(sequence.map(|(mut channel, sample_time)| { | ||
| 129 | channel.setup(); | ||
| 130 | |||
| 131 | (channel.channel, sample_time) | ||
| 132 | })); | ||
| 133 | compiler_fence(Ordering::SeqCst); | ||
| 134 | |||
| 135 | Self::setup_dma(); | ||
| 136 | |||
| 137 | // Don't disable the clock | ||
| 138 | mem::forget(self); | ||
| 139 | |||
| 140 | RingBufferedAdc::new(dma, dma_buf) | ||
| 117 | } | 141 | } |
| 118 | 142 | ||
| 119 | pub fn set_resolution(&mut self, resolution: Resolution) { | 143 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { |
| 120 | T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); | 144 | channel.setup(); |
| 145 | |||
| 146 | // Configure ADC | ||
| 147 | let channel = channel.channel(); | ||
| 148 | |||
| 149 | Self::configure_sequence([(channel, sample_time)].into_iter()); | ||
| 150 | Self::blocking_convert() | ||
| 121 | } | 151 | } |
| 122 | 152 | ||
| 123 | /// Enables internal voltage reference and returns [VrefInt], which can be used in | 153 | /// Enables internal voltage reference and returns [VrefInt], which can be used in |
| @@ -153,8 +183,27 @@ where | |||
| 153 | Vbat {} | 183 | Vbat {} |
| 154 | } | 184 | } |
| 155 | 185 | ||
| 156 | /// Perform a single conversion. | 186 | pub(super) fn start() { |
| 157 | fn convert(&mut self) -> u16 { | 187 | // Begin ADC conversions |
| 188 | T::regs().cr2().modify(|reg| { | ||
| 189 | reg.set_adon(true); | ||
| 190 | reg.set_swstart(true); | ||
| 191 | }); | ||
| 192 | } | ||
| 193 | |||
| 194 | pub(super) fn stop() { | ||
| 195 | // Stop ADC | ||
| 196 | T::regs().cr2().modify(|reg| { | ||
| 197 | // Stop ADC | ||
| 198 | reg.set_swstart(false); | ||
| 199 | }); | ||
| 200 | } | ||
| 201 | |||
| 202 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 203 | T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); | ||
| 204 | } | ||
| 205 | |||
| 206 | pub(super) fn blocking_convert() -> u16 { | ||
| 158 | // clear end of conversion flag | 207 | // clear end of conversion flag |
| 159 | T::regs().sr().modify(|reg| { | 208 | T::regs().sr().modify(|reg| { |
| 160 | reg.set_eoc(false); | 209 | reg.set_eoc(false); |
| @@ -175,28 +224,85 @@ where | |||
| 175 | T::regs().dr().read().0 as u16 | 224 | T::regs().dr().read().0 as u16 |
| 176 | } | 225 | } |
| 177 | 226 | ||
| 178 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 227 | pub(super) fn configure_sequence(sequence: impl ExactSizeIterator<Item = (u8, SampleTime)>) { |
| 179 | channel.setup(); | 228 | T::regs().cr2().modify(|reg| { |
| 229 | reg.set_adon(true); | ||
| 230 | }); | ||
| 180 | 231 | ||
| 181 | // Configure ADC | 232 | // Check the sequence is long enough |
| 182 | let channel = channel.channel(); | 233 | T::regs().sqr1().modify(|r| { |
| 234 | r.set_l((sequence.len() - 1).try_into().unwrap()); | ||
| 235 | }); | ||
| 183 | 236 | ||
| 184 | // Select channel | 237 | for (i, (ch, sample_time)) in sequence.enumerate() { |
| 185 | T::regs().sqr3().write(|reg| reg.set_sq(0, channel)); | 238 | // Set the channel in the right sequence field. |
| 239 | T::regs().sqr3().modify(|w| w.set_sq(i, ch)); | ||
| 186 | 240 | ||
| 187 | // Configure channel | 241 | let sample_time = sample_time.into(); |
| 188 | Self::set_channel_sample_time(channel, self.sample_time); | 242 | if ch <= 9 { |
| 243 | T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 244 | } else { | ||
| 245 | T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 246 | } | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | pub(super) fn setup_dma() { | ||
| 251 | let r = T::regs(); | ||
| 252 | |||
| 253 | // Clear all interrupts | ||
| 254 | r.sr().modify(|regs| { | ||
| 255 | regs.set_eoc(false); | ||
| 256 | regs.set_ovr(false); | ||
| 257 | regs.set_strt(false); | ||
| 258 | }); | ||
| 259 | |||
| 260 | r.cr1().modify(|w| { | ||
| 261 | // Enable interrupt for end of conversion | ||
| 262 | w.set_eocie(true); | ||
| 263 | // Enable interrupt for overrun | ||
| 264 | w.set_ovrie(true); | ||
| 265 | // Scanning converisons of multiple channels | ||
| 266 | w.set_scan(true); | ||
| 267 | // Continuous conversion mode | ||
| 268 | w.set_discen(false); | ||
| 269 | }); | ||
| 189 | 270 | ||
| 190 | self.convert() | 271 | r.cr2().modify(|w| { |
| 272 | // Enable DMA mode | ||
| 273 | w.set_dma(true); | ||
| 274 | // Enable continuous conversions | ||
| 275 | w.set_cont(true); | ||
| 276 | // DMA requests are issues as long as DMA=1 and data are converted. | ||
| 277 | w.set_dds(vals::Dds::CONTINUOUS); | ||
| 278 | // EOC flag is set at the end of each conversion. | ||
| 279 | w.set_eocs(vals::Eocs::EACH_CONVERSION); | ||
| 280 | }); | ||
| 191 | } | 281 | } |
| 192 | 282 | ||
| 193 | fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | 283 | pub(super) fn teardown_dma() { |
| 194 | let sample_time = sample_time.into(); | 284 | let r = T::regs(); |
| 195 | if ch <= 9 { | 285 | |
| 196 | T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); | 286 | // Stop ADC |
| 197 | } else { | 287 | r.cr2().modify(|reg| { |
| 198 | T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | 288 | // Stop ADC |
| 199 | } | 289 | reg.set_swstart(false); |
| 290 | // Stop ADC | ||
| 291 | reg.set_adon(false); | ||
| 292 | // Stop DMA | ||
| 293 | reg.set_dma(false); | ||
| 294 | }); | ||
| 295 | |||
| 296 | r.cr1().modify(|w| { | ||
| 297 | // Disable interrupt for end of conversion | ||
| 298 | w.set_eocie(false); | ||
| 299 | // Disable interrupt for overrun | ||
| 300 | w.set_ovrie(false); | ||
| 301 | }); | ||
| 302 | |||
| 303 | clear_interrupt_flags(r); | ||
| 304 | |||
| 305 | compiler_fence(Ordering::SeqCst); | ||
| 200 | } | 306 | } |
| 201 | } | 307 | } |
| 202 | 308 | ||
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 16063ce4d..e816907d1 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs | |||
| @@ -9,11 +9,21 @@ use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; | |||
| 9 | #[cfg(adc_g0)] | 9 | #[cfg(adc_g0)] |
| 10 | pub use pac::adc::vals::{Ovsr, Ovss, Presc}; | 10 | pub use pac::adc::vals::{Ovsr, Ovss, Presc}; |
| 11 | 11 | ||
| 12 | #[allow(unused_imports)] | ||
| 13 | use super::SealedAdcChannel; | ||
| 12 | use super::{ | 14 | use super::{ |
| 13 | blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, | 15 | Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, Temperature, Vbat, VrefInt, |
| 16 | blocking_delay_us, | ||
| 14 | }; | 17 | }; |
| 18 | |||
| 19 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 20 | mod ringbuffered; | ||
| 21 | |||
| 22 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 23 | use ringbuffered::RingBufferedAdc; | ||
| 24 | |||
| 15 | use crate::dma::Transfer; | 25 | use crate::dma::Transfer; |
| 16 | use crate::{pac, rcc, Peri}; | 26 | use crate::{Peri, pac, rcc}; |
| 17 | 27 | ||
| 18 | /// Default VREF voltage used for sample conversion to millivolts. | 28 | /// Default VREF voltage used for sample conversion to millivolts. |
| 19 | pub const VREF_DEFAULT_MV: u32 = 3300; | 29 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| @@ -25,61 +35,55 @@ pub const VREF_CALIB_MV: u32 = 3000; | |||
| 25 | // TODO: Use [#![feature(variant_count)]](https://github.com/rust-lang/rust/issues/73662) when stable | 35 | // TODO: Use [#![feature(variant_count)]](https://github.com/rust-lang/rust/issues/73662) when stable |
| 26 | const SAMPLE_TIMES_CAPACITY: usize = 2; | 36 | const SAMPLE_TIMES_CAPACITY: usize = 2; |
| 27 | 37 | ||
| 28 | pub struct VrefInt; | 38 | #[cfg(adc_g0)] |
| 29 | impl<T: Instance> AdcChannel<T> for VrefInt {} | 39 | impl<T: Instance> super::VrefConverter for T { |
| 30 | impl<T: Instance> SealedAdcChannel<T> for VrefInt { | 40 | const CHANNEL: u8 = 13; |
| 31 | fn channel(&self) -> u8 { | 41 | } |
| 32 | cfg_if! { | 42 | #[cfg(any(adc_h5, adc_h7rs))] |
| 33 | if #[cfg(adc_g0)] { | 43 | impl<T: Instance> super::VrefConverter for T { |
| 34 | let val = 13; | 44 | const CHANNEL: u8 = 17; |
| 35 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 45 | } |
| 36 | let val = 17; | 46 | #[cfg(adc_u0)] |
| 37 | } else if #[cfg(adc_u0)] { | 47 | impl<T: Instance> super::VrefConverter for T { |
| 38 | let val = 12; | 48 | const CHANNEL: u8 = 12; |
| 39 | } else { | 49 | } |
| 40 | let val = 0; | 50 | #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] |
| 41 | } | 51 | impl<T: Instance> super::VrefConverter for T { |
| 42 | } | 52 | const CHANNEL: u8 = 0; |
| 43 | val | ||
| 44 | } | ||
| 45 | } | 53 | } |
| 46 | 54 | ||
| 47 | pub struct Temperature; | 55 | #[cfg(adc_g0)] |
| 48 | impl<T: Instance> AdcChannel<T> for Temperature {} | 56 | impl<T: Instance> super::TemperatureConverter for T { |
| 49 | impl<T: Instance> SealedAdcChannel<T> for Temperature { | 57 | const CHANNEL: u8 = 12; |
| 50 | fn channel(&self) -> u8 { | 58 | } |
| 51 | cfg_if! { | 59 | #[cfg(any(adc_h5, adc_h7rs))] |
| 52 | if #[cfg(adc_g0)] { | 60 | impl<T: Instance> super::TemperatureConverter for T { |
| 53 | let val = 12; | 61 | const CHANNEL: u8 = 16; |
| 54 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 62 | } |
| 55 | let val = 16; | 63 | #[cfg(adc_u0)] |
| 56 | } else if #[cfg(adc_u0)] { | 64 | impl<T: Instance> super::TemperatureConverter for T { |
| 57 | let val = 11; | 65 | const CHANNEL: u8 = 11; |
| 58 | } else { | 66 | } |
| 59 | let val = 17; | 67 | #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] |
| 60 | } | 68 | impl<T: Instance> super::TemperatureConverter for T { |
| 61 | } | 69 | const CHANNEL: u8 = 17; |
| 62 | val | ||
| 63 | } | ||
| 64 | } | 70 | } |
| 65 | 71 | ||
| 66 | pub struct Vbat; | 72 | #[cfg(adc_g0)] |
| 67 | impl<T: Instance> AdcChannel<T> for Vbat {} | 73 | impl<T: Instance> super::VBatConverter for T { |
| 68 | impl<T: Instance> SealedAdcChannel<T> for Vbat { | 74 | const CHANNEL: u8 = 14; |
| 69 | fn channel(&self) -> u8 { | 75 | } |
| 70 | cfg_if! { | 76 | #[cfg(any(adc_h5, adc_h7rs))] |
| 71 | if #[cfg(adc_g0)] { | 77 | impl<T: Instance> super::VBatConverter for T { |
| 72 | let val = 14; | 78 | const CHANNEL: u8 = 2; |
| 73 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 79 | } |
| 74 | let val = 2; | 80 | #[cfg(adc_u0)] |
| 75 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 81 | impl<T: Instance> super::VBatConverter for T { |
| 76 | let val = 13; | 82 | const CHANNEL: u8 = 13; |
| 77 | } else { | 83 | } |
| 78 | let val = 18; | 84 | #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] |
| 79 | } | 85 | impl<T: Instance> super::VBatConverter for T { |
| 80 | } | 86 | const CHANNEL: u8 = 18; |
| 81 | val | ||
| 82 | } | ||
| 83 | } | 87 | } |
| 84 | 88 | ||
| 85 | cfg_if! { | 89 | cfg_if! { |
| @@ -174,15 +178,44 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 174 | blocking_delay_us(1); | 178 | blocking_delay_us(1); |
| 175 | } | 179 | } |
| 176 | 180 | ||
| 181 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 182 | pub(super) fn start() { | ||
| 183 | // Start adc conversion | ||
| 184 | T::regs().cr().modify(|reg| { | ||
| 185 | reg.set_adstart(true); | ||
| 186 | }); | ||
| 187 | } | ||
| 188 | |||
| 189 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 190 | pub(super) fn stop() { | ||
| 191 | // Stop adc conversion | ||
| 192 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 193 | T::regs().cr().modify(|reg| { | ||
| 194 | reg.set_adstp(true); | ||
| 195 | }); | ||
| 196 | while T::regs().cr().read().adstart() {} | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 201 | pub(super) fn teardown_dma() { | ||
| 202 | //disable dma control | ||
| 203 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 204 | T::regs().cfgr().modify(|reg| { | ||
| 205 | reg.set_dmaen(false); | ||
| 206 | }); | ||
| 207 | #[cfg(any(adc_g0, adc_u0))] | ||
| 208 | T::regs().cfgr1().modify(|reg| { | ||
| 209 | reg.set_dmaen(false); | ||
| 210 | }); | ||
| 211 | } | ||
| 212 | |||
| 177 | /// Initialize the ADC leaving any analog clock at reset value. | 213 | /// Initialize the ADC leaving any analog clock at reset value. |
| 178 | /// For G0 and WL, this is the async clock without prescaler. | 214 | /// For G0 and WL, this is the async clock without prescaler. |
| 179 | pub fn new(adc: Peri<'d, T>) -> Self { | 215 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 180 | Self::init_regulator(); | 216 | Self::init_regulator(); |
| 181 | Self::init_calibrate(); | 217 | Self::init_calibrate(); |
| 182 | Self { | 218 | Self { adc } |
| 183 | adc, | ||
| 184 | sample_time: SampleTime::from_bits(0), | ||
| 185 | } | ||
| 186 | } | 219 | } |
| 187 | 220 | ||
| 188 | #[cfg(adc_g0)] | 221 | #[cfg(adc_g0)] |
| @@ -218,10 +251,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 218 | 251 | ||
| 219 | Self::init_calibrate(); | 252 | Self::init_calibrate(); |
| 220 | 253 | ||
| 221 | Self { | 254 | Self { adc } |
| 222 | adc, | ||
| 223 | sample_time: SampleTime::from_bits(0), | ||
| 224 | } | ||
| 225 | } | 255 | } |
| 226 | 256 | ||
| 227 | // Enable ADC only when it is not already running. | 257 | // Enable ADC only when it is not already running. |
| @@ -303,16 +333,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 303 | Vbat {} | 333 | Vbat {} |
| 304 | } | 334 | } |
| 305 | 335 | ||
| 306 | /// Set the ADC sample time. | ||
| 307 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 308 | self.sample_time = sample_time; | ||
| 309 | } | ||
| 310 | |||
| 311 | /// Get the ADC sample time. | ||
| 312 | pub fn sample_time(&self) -> SampleTime { | ||
| 313 | self.sample_time | ||
| 314 | } | ||
| 315 | |||
| 316 | /// Set the ADC resolution. | 336 | /// Set the ADC resolution. |
| 317 | pub fn set_resolution(&mut self, resolution: Resolution) { | 337 | pub fn set_resolution(&mut self, resolution: Resolution) { |
| 318 | #[cfg(not(any(adc_g0, adc_u0)))] | 338 | #[cfg(not(any(adc_g0, adc_u0)))] |
| @@ -374,8 +394,8 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 374 | } | 394 | } |
| 375 | 395 | ||
| 376 | /// Read an ADC channel. | 396 | /// Read an ADC channel. |
| 377 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 397 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { |
| 378 | self.read_channel(channel) | 398 | self.read_channel(channel, sample_time) |
| 379 | } | 399 | } |
| 380 | 400 | ||
| 381 | /// Read one or multiple ADC channels using DMA. | 401 | /// Read one or multiple ADC channels using DMA. |
| @@ -423,6 +443,9 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 423 | "Asynchronous read sequence cannot be more than 16 in length" | 443 | "Asynchronous read sequence cannot be more than 16 in length" |
| 424 | ); | 444 | ); |
| 425 | 445 | ||
| 446 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 447 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 448 | |||
| 426 | // Ensure no conversions are ongoing and ADC is enabled. | 449 | // Ensure no conversions are ongoing and ADC is enabled. |
| 427 | Self::cancel_conversions(); | 450 | Self::cancel_conversions(); |
| 428 | self.enable(); | 451 | self.enable(); |
| @@ -559,6 +582,137 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 559 | }); | 582 | }); |
| 560 | } | 583 | } |
| 561 | 584 | ||
| 585 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 586 | /// | ||
| 587 | /// The `dma_buf` should be large enough to prevent DMA buffer overrun. | ||
| 588 | /// The length of the `dma_buf` should be a multiple of the ADC channel count. | ||
| 589 | /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. | ||
| 590 | /// | ||
| 591 | /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length. | ||
| 592 | /// It is critical to call `read` frequently to prevent DMA buffer overrun. | ||
| 593 | /// | ||
| 594 | /// [`read`]: #method.read | ||
| 595 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 596 | pub fn into_ring_buffered<'a>( | ||
| 597 | &mut self, | ||
| 598 | dma: Peri<'a, impl RxDma<T>>, | ||
| 599 | dma_buf: &'a mut [u16], | ||
| 600 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 601 | ) -> RingBufferedAdc<'a, T> { | ||
| 602 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 603 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 604 | assert!( | ||
| 605 | sequence.len() <= 16, | ||
| 606 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 607 | ); | ||
| 608 | // reset conversions and enable the adc | ||
| 609 | Self::cancel_conversions(); | ||
| 610 | self.enable(); | ||
| 611 | |||
| 612 | //adc side setup | ||
| 613 | |||
| 614 | // Set sequence length | ||
| 615 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 616 | T::regs().sqr1().modify(|w| { | ||
| 617 | w.set_l(sequence.len() as u8 - 1); | ||
| 618 | }); | ||
| 619 | |||
| 620 | #[cfg(adc_g0)] | ||
| 621 | { | ||
| 622 | let mut sample_times = Vec::<SampleTime, SAMPLE_TIMES_CAPACITY>::new(); | ||
| 623 | |||
| 624 | T::regs().chselr().write(|chselr| { | ||
| 625 | T::regs().smpr().write(|smpr| { | ||
| 626 | for (channel, sample_time) in sequence { | ||
| 627 | chselr.set_chsel(channel.channel.into(), true); | ||
| 628 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { | ||
| 629 | smpr.set_smpsel(channel.channel.into(), (i as u8).into()); | ||
| 630 | } else { | ||
| 631 | smpr.set_sample_time(sample_times.len(), sample_time); | ||
| 632 | if let Err(_) = sample_times.push(sample_time) { | ||
| 633 | panic!( | ||
| 634 | "Implementation is limited to {} unique sample times among all channels.", | ||
| 635 | SAMPLE_TIMES_CAPACITY | ||
| 636 | ); | ||
| 637 | } | ||
| 638 | } | ||
| 639 | } | ||
| 640 | }) | ||
| 641 | }); | ||
| 642 | } | ||
| 643 | #[cfg(not(adc_g0))] | ||
| 644 | { | ||
| 645 | #[cfg(adc_u0)] | ||
| 646 | let mut channel_mask = 0; | ||
| 647 | |||
| 648 | // Configure channels and ranks | ||
| 649 | for (_i, (mut channel, sample_time)) in sequence.enumerate() { | ||
| 650 | Self::configure_channel(&mut channel, sample_time); | ||
| 651 | |||
| 652 | // Each channel is sampled according to sequence | ||
| 653 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 654 | match _i { | ||
| 655 | 0..=3 => { | ||
| 656 | T::regs().sqr1().modify(|w| { | ||
| 657 | w.set_sq(_i, channel.channel()); | ||
| 658 | }); | ||
| 659 | } | ||
| 660 | 4..=8 => { | ||
| 661 | T::regs().sqr2().modify(|w| { | ||
| 662 | w.set_sq(_i - 4, channel.channel()); | ||
| 663 | }); | ||
| 664 | } | ||
| 665 | 9..=13 => { | ||
| 666 | T::regs().sqr3().modify(|w| { | ||
| 667 | w.set_sq(_i - 9, channel.channel()); | ||
| 668 | }); | ||
| 669 | } | ||
| 670 | 14..=15 => { | ||
| 671 | T::regs().sqr4().modify(|w| { | ||
| 672 | w.set_sq(_i - 14, channel.channel()); | ||
| 673 | }); | ||
| 674 | } | ||
| 675 | _ => unreachable!(), | ||
| 676 | } | ||
| 677 | |||
| 678 | #[cfg(adc_u0)] | ||
| 679 | { | ||
| 680 | channel_mask |= 1 << channel.channel(); | ||
| 681 | } | ||
| 682 | } | ||
| 683 | |||
| 684 | // On G0 and U0 enabled channels are sampled from 0 to last channel. | ||
| 685 | // It is possible to add up to 8 sequences if CHSELRMOD = 1. | ||
| 686 | // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. | ||
| 687 | #[cfg(adc_u0)] | ||
| 688 | T::regs().chselr().modify(|reg| { | ||
| 689 | reg.set_chsel(channel_mask); | ||
| 690 | }); | ||
| 691 | } | ||
| 692 | // Set continuous mode with Circular dma. | ||
| 693 | // Clear overrun flag before starting transfer. | ||
| 694 | T::regs().isr().modify(|reg| { | ||
| 695 | reg.set_ovr(true); | ||
| 696 | }); | ||
| 697 | |||
| 698 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 699 | T::regs().cfgr().modify(|reg| { | ||
| 700 | reg.set_discen(false); | ||
| 701 | reg.set_cont(true); | ||
| 702 | reg.set_dmacfg(Dmacfg::CIRCULAR); | ||
| 703 | reg.set_dmaen(true); | ||
| 704 | }); | ||
| 705 | #[cfg(any(adc_g0, adc_u0))] | ||
| 706 | T::regs().cfgr1().modify(|reg| { | ||
| 707 | reg.set_discen(false); | ||
| 708 | reg.set_cont(true); | ||
| 709 | reg.set_dmacfg(Dmacfg::CIRCULAR); | ||
| 710 | reg.set_dmaen(true); | ||
| 711 | }); | ||
| 712 | |||
| 713 | RingBufferedAdc::new(dma, dma_buf) | ||
| 714 | } | ||
| 715 | |||
| 562 | #[cfg(not(adc_g0))] | 716 | #[cfg(not(adc_g0))] |
| 563 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | 717 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { |
| 564 | // RM0492, RM0481, etc. | 718 | // RM0492, RM0481, etc. |
| @@ -572,13 +726,13 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 572 | Self::set_channel_sample_time(channel.channel(), sample_time); | 726 | Self::set_channel_sample_time(channel.channel(), sample_time); |
| 573 | } | 727 | } |
| 574 | 728 | ||
| 575 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 729 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { |
| 576 | self.enable(); | 730 | self.enable(); |
| 577 | #[cfg(not(adc_g0))] | 731 | #[cfg(not(adc_g0))] |
| 578 | Self::configure_channel(channel, self.sample_time); | 732 | Self::configure_channel(channel, sample_time); |
| 579 | #[cfg(adc_g0)] | 733 | #[cfg(adc_g0)] |
| 580 | T::regs().smpr().write(|reg| { | 734 | T::regs().smpr().write(|reg| { |
| 581 | reg.set_sample_time(0, self.sample_time); | 735 | reg.set_sample_time(0, sample_time); |
| 582 | reg.set_smpsel(channel.channel().into(), Smpsel::SMP1); | 736 | reg.set_smpsel(channel.channel().into(), Smpsel::SMP1); |
| 583 | }); | 737 | }); |
| 584 | // Select channel | 738 | // Select channel |
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index b66437e6e..2f7baf3bf 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs | |||
| @@ -5,11 +5,12 @@ use pac::adc::vals::{Adstp, Difsel, Dmngt, Exten, Pcsel}; | |||
| 5 | use pac::adccommon::vals::Presc; | 5 | use pac::adccommon::vals::Presc; |
| 6 | 6 | ||
| 7 | use super::{ | 7 | use super::{ |
| 8 | blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, | 8 | Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, Temperature, Vbat, |
| 9 | VrefInt, blocking_delay_us, | ||
| 9 | }; | 10 | }; |
| 10 | use crate::dma::Transfer; | 11 | use crate::dma::Transfer; |
| 11 | use crate::time::Hertz; | 12 | use crate::time::Hertz; |
| 12 | use crate::{pac, rcc, Peri}; | 13 | use crate::{Peri, pac, rcc}; |
| 13 | 14 | ||
| 14 | /// Default VREF voltage used for sample conversion to millivolts. | 15 | /// Default VREF voltage used for sample conversion to millivolts. |
| 15 | pub const VREF_DEFAULT_MV: u32 = 3300; | 16 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| @@ -25,52 +26,40 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); | |||
| 25 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55); | 26 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55); |
| 26 | 27 | ||
| 27 | #[cfg(stm32g4)] | 28 | #[cfg(stm32g4)] |
| 28 | const VREF_CHANNEL: u8 = 18; | 29 | impl<T: Instance> super::VrefConverter for T { |
| 30 | const CHANNEL: u8 = 18; | ||
| 31 | } | ||
| 29 | #[cfg(stm32g4)] | 32 | #[cfg(stm32g4)] |
| 30 | const TEMP_CHANNEL: u8 = 16; | 33 | impl<T: Instance> super::TemperatureConverter for T { |
| 34 | const CHANNEL: u8 = 16; | ||
| 35 | } | ||
| 31 | 36 | ||
| 32 | #[cfg(stm32h7)] | 37 | #[cfg(stm32h7)] |
| 33 | const VREF_CHANNEL: u8 = 19; | 38 | impl<T: Instance> super::VrefConverter for T { |
| 39 | const CHANNEL: u8 = 19; | ||
| 40 | } | ||
| 34 | #[cfg(stm32h7)] | 41 | #[cfg(stm32h7)] |
| 35 | const TEMP_CHANNEL: u8 = 18; | 42 | impl<T: Instance> super::TemperatureConverter for T { |
| 43 | const CHANNEL: u8 = 18; | ||
| 44 | } | ||
| 36 | 45 | ||
| 37 | // TODO this should be 14 for H7a/b/35 | 46 | // TODO this should be 14 for H7a/b/35 |
| 38 | #[cfg(not(stm32u5))] | 47 | #[cfg(not(stm32u5))] |
| 39 | const VBAT_CHANNEL: u8 = 17; | 48 | impl<T: Instance> super::VBatConverter for T { |
| 49 | const CHANNEL: u8 = 17; | ||
| 50 | } | ||
| 40 | 51 | ||
| 41 | #[cfg(stm32u5)] | 52 | #[cfg(stm32u5)] |
| 42 | const VREF_CHANNEL: u8 = 0; | 53 | impl<T: Instance> super::VrefConverter for T { |
| 43 | #[cfg(stm32u5)] | 54 | const CHANNEL: u8 = 0; |
| 44 | const TEMP_CHANNEL: u8 = 19; | ||
| 45 | #[cfg(stm32u5)] | ||
| 46 | const VBAT_CHANNEL: u8 = 18; | ||
| 47 | |||
| 48 | // NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs | ||
| 49 | /// Internal voltage reference channel. | ||
| 50 | pub struct VrefInt; | ||
| 51 | impl<T: Instance> AdcChannel<T> for VrefInt {} | ||
| 52 | impl<T: Instance> SealedAdcChannel<T> for VrefInt { | ||
| 53 | fn channel(&self) -> u8 { | ||
| 54 | VREF_CHANNEL | ||
| 55 | } | ||
| 56 | } | 55 | } |
| 57 | 56 | #[cfg(stm32u5)] | |
| 58 | /// Internal temperature channel. | 57 | impl<T: Instance> super::TemperatureConverter for T { |
| 59 | pub struct Temperature; | 58 | const CHANNEL: u8 = 19; |
| 60 | impl<T: Instance> AdcChannel<T> for Temperature {} | ||
| 61 | impl<T: Instance> SealedAdcChannel<T> for Temperature { | ||
| 62 | fn channel(&self) -> u8 { | ||
| 63 | TEMP_CHANNEL | ||
| 64 | } | ||
| 65 | } | 59 | } |
| 66 | 60 | #[cfg(stm32u5)] | |
| 67 | /// Internal battery voltage channel. | 61 | impl<T: Instance> super::VBatConverter for T { |
| 68 | pub struct Vbat; | 62 | const CHANNEL: u8 = 18; |
| 69 | impl<T: Instance> AdcChannel<T> for Vbat {} | ||
| 70 | impl<T: Instance> SealedAdcChannel<T> for Vbat { | ||
| 71 | fn channel(&self) -> u8 { | ||
| 72 | VBAT_CHANNEL | ||
| 73 | } | ||
| 74 | } | 63 | } |
| 75 | 64 | ||
| 76 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | 65 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, |
| @@ -171,7 +160,10 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 171 | info!("ADC frequency set to {}", frequency); | 160 | info!("ADC frequency set to {}", frequency); |
| 172 | 161 | ||
| 173 | if frequency > MAX_ADC_CLK_FREQ { | 162 | if frequency > MAX_ADC_CLK_FREQ { |
| 174 | panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); | 163 | panic!( |
| 164 | "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", | ||
| 165 | MAX_ADC_CLK_FREQ.0 / 1_000_000 | ||
| 166 | ); | ||
| 175 | } | 167 | } |
| 176 | 168 | ||
| 177 | #[cfg(stm32h7)] | 169 | #[cfg(stm32h7)] |
| @@ -187,10 +179,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 187 | }; | 179 | }; |
| 188 | T::regs().cr().modify(|w| w.set_boost(boost)); | 180 | T::regs().cr().modify(|w| w.set_boost(boost)); |
| 189 | } | 181 | } |
| 190 | let mut s = Self { | 182 | let mut s = Self { adc }; |
| 191 | adc, | ||
| 192 | sample_time: SampleTime::from_bits(0), | ||
| 193 | }; | ||
| 194 | s.power_up(); | 183 | s.power_up(); |
| 195 | s.configure_differential_inputs(); | 184 | s.configure_differential_inputs(); |
| 196 | 185 | ||
| @@ -274,16 +263,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 274 | Vbat {} | 263 | Vbat {} |
| 275 | } | 264 | } |
| 276 | 265 | ||
| 277 | /// Set the ADC sample time. | ||
| 278 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 279 | self.sample_time = sample_time; | ||
| 280 | } | ||
| 281 | |||
| 282 | /// Get the ADC sample time. | ||
| 283 | pub fn sample_time(&self) -> SampleTime { | ||
| 284 | self.sample_time | ||
| 285 | } | ||
| 286 | |||
| 287 | /// Set the ADC resolution. | 266 | /// Set the ADC resolution. |
| 288 | pub fn set_resolution(&mut self, resolution: Resolution) { | 267 | pub fn set_resolution(&mut self, resolution: Resolution) { |
| 289 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | 268 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); |
| @@ -332,8 +311,8 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 332 | } | 311 | } |
| 333 | 312 | ||
| 334 | /// Read an ADC channel. | 313 | /// Read an ADC channel. |
| 335 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 314 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { |
| 336 | self.read_channel(channel) | 315 | self.read_channel(channel, sample_time) |
| 337 | } | 316 | } |
| 338 | 317 | ||
| 339 | /// Read one or multiple ADC channels using DMA. | 318 | /// Read one or multiple ADC channels using DMA. |
| @@ -469,8 +448,8 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 469 | } | 448 | } |
| 470 | } | 449 | } |
| 471 | 450 | ||
| 472 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 451 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { |
| 473 | Self::configure_channel(channel, self.sample_time); | 452 | Self::configure_channel(channel, sample_time); |
| 474 | 453 | ||
| 475 | T::regs().sqr1().modify(|reg| { | 454 | T::regs().sqr1().modify(|reg| { |
| 476 | reg.set_sq(0, channel.channel()); | 455 | reg.set_sq(0, channel.channel()); |
diff --git a/embassy-stm32/src/adc/watchdog_v1.rs b/embassy-stm32/src/adc/watchdog_v1.rs index bbe8e1971..b12e0d333 100644 --- a/embassy-stm32/src/adc/watchdog_v1.rs +++ b/embassy-stm32/src/adc/watchdog_v1.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | use core::future::poll_fn; | 1 | use core::future::poll_fn; |
| 2 | use core::task::Poll; | 2 | use core::task::Poll; |
| 3 | 3 | ||
| 4 | use stm32_metapac::adc::vals::{Align, Awdsgl, Res}; | 4 | use stm32_metapac::adc::vals::{Align, Awdsgl, Res, SampleTime}; |
| 5 | 5 | ||
| 6 | use crate::adc::{Adc, AdcChannel, Instance}; | 6 | use crate::adc::{Adc, AdcChannel, Instance}; |
| 7 | 7 | ||
| @@ -67,7 +67,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 67 | /// let v_high = adc.monitor_watchdog().await; | 67 | /// let v_high = adc.monitor_watchdog().await; |
| 68 | /// info!("ADC sample is high {}", v_high); | 68 | /// info!("ADC sample is high {}", v_high); |
| 69 | /// ``` | 69 | /// ``` |
| 70 | pub async fn monitor_watchdog(&mut self) -> u16 { | 70 | pub async fn monitor_watchdog(&mut self, sample_time: SampleTime) -> u16 { |
| 71 | assert!( | 71 | assert!( |
| 72 | match T::regs().cfgr1().read().awdsgl() { | 72 | match T::regs().cfgr1().read().awdsgl() { |
| 73 | Awdsgl::SINGLE_CHANNEL => T::regs().cfgr1().read().awdch() != 0, | 73 | Awdsgl::SINGLE_CHANNEL => T::regs().cfgr1().read().awdch() != 0, |
| @@ -76,7 +76,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 76 | "`set_channel` should be called before `monitor`", | 76 | "`set_channel` should be called before `monitor`", |
| 77 | ); | 77 | ); |
| 78 | assert!(T::regs().chselr().read().0 != 0); | 78 | assert!(T::regs().chselr().read().0 != 0); |
| 79 | T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.into())); | 79 | T::regs().smpr().modify(|reg| reg.set_smp(sample_time.into())); |
| 80 | Self::start_awd(); | 80 | Self::start_awd(); |
| 81 | 81 | ||
| 82 | let sample = poll_fn(|cx| { | 82 | let sample = poll_fn(|cx| { |
diff --git a/embassy-stm32/src/backup_sram.rs b/embassy-stm32/src/backup_sram.rs new file mode 100644 index 000000000..31b373c6c --- /dev/null +++ b/embassy-stm32/src/backup_sram.rs | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | //! Battary backed SRAM | ||
| 2 | |||
| 3 | use core::slice; | ||
| 4 | |||
| 5 | use embassy_hal_internal::Peri; | ||
| 6 | |||
| 7 | use crate::_generated::{BKPSRAM_BASE, BKPSRAM_SIZE}; | ||
| 8 | use crate::peripherals::BKPSRAM; | ||
| 9 | |||
| 10 | /// Struct used to initilize backup sram | ||
| 11 | pub struct BackupMemory {} | ||
| 12 | |||
| 13 | impl BackupMemory { | ||
| 14 | /// Setup battery backed sram | ||
| 15 | /// | ||
| 16 | /// Returns slice to sram and whether the sram was retained | ||
| 17 | pub fn new(_backup_sram: Peri<'static, BKPSRAM>) -> (&'static mut [u8], bool) { | ||
| 18 | // Assert bksram has been enabled in rcc | ||
| 19 | assert!(crate::pac::PWR.bdcr().read().bren() == crate::pac::pwr::vals::Retention::PRESERVED); | ||
| 20 | |||
| 21 | unsafe { | ||
| 22 | ( | ||
| 23 | slice::from_raw_parts_mut(BKPSRAM_BASE as *mut u8, BKPSRAM_SIZE), | ||
| 24 | critical_section::with(|_| crate::rcc::BKSRAM_RETAINED), | ||
| 25 | ) | ||
| 26 | } | ||
| 27 | } | ||
| 28 | } | ||
diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index 8eb188560..507350c42 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs | |||
| @@ -5,8 +5,8 @@ use core::future::poll_fn; | |||
| 5 | use core::marker::PhantomData; | 5 | use core::marker::PhantomData; |
| 6 | use core::task::Poll; | 6 | use core::task::Poll; |
| 7 | 7 | ||
| 8 | use embassy_hal_internal::interrupt::InterruptExt; | ||
| 9 | use embassy_hal_internal::PeripheralType; | 8 | use embassy_hal_internal::PeripheralType; |
| 9 | use embassy_hal_internal::interrupt::InterruptExt; | ||
| 10 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 10 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 11 | use embassy_sync::channel::Channel; | 11 | use embassy_sync::channel::Channel; |
| 12 | use embassy_sync::waitqueue::AtomicWaker; | 12 | use embassy_sync::waitqueue::AtomicWaker; |
| @@ -22,7 +22,7 @@ use crate::can::enums::{BusError, RefCountOp, TryReadError}; | |||
| 22 | use crate::gpio::{AfType, OutputType, Pull, Speed}; | 22 | use crate::gpio::{AfType, OutputType, Pull, Speed}; |
| 23 | use crate::interrupt::typelevel::Interrupt; | 23 | use crate::interrupt::typelevel::Interrupt; |
| 24 | use crate::rcc::{self, RccPeripheral}; | 24 | use crate::rcc::{self, RccPeripheral}; |
| 25 | use crate::{interrupt, peripherals, Peri}; | 25 | use crate::{Peri, interrupt, peripherals}; |
| 26 | 26 | ||
| 27 | /// Interrupt handler. | 27 | /// Interrupt handler. |
| 28 | pub struct TxInterruptHandler<T: Instance> { | 28 | pub struct TxInterruptHandler<T: Instance> { |
| @@ -186,10 +186,10 @@ impl<'d> Can<'d> { | |||
| 186 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, | 186 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 187 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, | 187 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 188 | _irqs: impl interrupt::typelevel::Binding<T::TXInterrupt, TxInterruptHandler<T>> | 188 | _irqs: impl interrupt::typelevel::Binding<T::TXInterrupt, TxInterruptHandler<T>> |
| 189 | + interrupt::typelevel::Binding<T::RX0Interrupt, Rx0InterruptHandler<T>> | 189 | + interrupt::typelevel::Binding<T::RX0Interrupt, Rx0InterruptHandler<T>> |
| 190 | + interrupt::typelevel::Binding<T::RX1Interrupt, Rx1InterruptHandler<T>> | 190 | + interrupt::typelevel::Binding<T::RX1Interrupt, Rx1InterruptHandler<T>> |
| 191 | + interrupt::typelevel::Binding<T::SCEInterrupt, SceInterruptHandler<T>> | 191 | + interrupt::typelevel::Binding<T::SCEInterrupt, SceInterruptHandler<T>> |
| 192 | + 'd, | 192 | + 'd, |
| 193 | ) -> Self { | 193 | ) -> Self { |
| 194 | let info = T::info(); | 194 | let info = T::info(); |
| 195 | let regs = &T::info().regs; | 195 | let regs = &T::info().regs; |
diff --git a/embassy-stm32/src/can/fd/config.rs b/embassy-stm32/src/can/fd/config.rs index c6a66b469..4fe634ce4 100644 --- a/embassy-stm32/src/can/fd/config.rs +++ b/embassy-stm32/src/can/fd/config.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | //! Configuration for FDCAN Module | 1 | //! Configuration for FDCAN Module |
| 2 | // Note: This file is copied and modified from fdcan crate by Richard Meadows | 2 | // Note: This file is copied and modified from fdcan crate by Richard Meadows |
| 3 | 3 | ||
| 4 | use core::num::{NonZeroU16, NonZeroU8}; | 4 | use core::num::{NonZeroU8, NonZeroU16}; |
| 5 | 5 | ||
| 6 | /// Configures the bit timings. | 6 | /// Configures the bit timings. |
| 7 | /// | 7 | /// |
| @@ -360,6 +360,8 @@ pub struct FdCanConfig { | |||
| 360 | pub global_filter: GlobalFilter, | 360 | pub global_filter: GlobalFilter, |
| 361 | /// TX buffer mode (FIFO or priority queue) | 361 | /// TX buffer mode (FIFO or priority queue) |
| 362 | pub tx_buffer_mode: TxBufferMode, | 362 | pub tx_buffer_mode: TxBufferMode, |
| 363 | /// Automatic recovery from bus off state | ||
| 364 | pub automatic_bus_off_recovery: bool, | ||
| 363 | } | 365 | } |
| 364 | 366 | ||
| 365 | impl FdCanConfig { | 367 | impl FdCanConfig { |
| @@ -456,6 +458,16 @@ impl FdCanConfig { | |||
| 456 | self.tx_buffer_mode = txbm; | 458 | self.tx_buffer_mode = txbm; |
| 457 | self | 459 | self |
| 458 | } | 460 | } |
| 461 | |||
| 462 | /// Enables or disables automatic recovery from bus off state | ||
| 463 | /// | ||
| 464 | /// Automatic recovery is performed by clearing the INIT bit in the CCCR register if | ||
| 465 | /// the BO bit is active in the IR register in the IT0 interrupt. | ||
| 466 | #[inline] | ||
| 467 | pub const fn set_automatic_bus_off_recovery(mut self, enabled: bool) -> Self { | ||
| 468 | self.automatic_bus_off_recovery = enabled; | ||
| 469 | self | ||
| 470 | } | ||
| 459 | } | 471 | } |
| 460 | 472 | ||
| 461 | impl Default for FdCanConfig { | 473 | impl Default for FdCanConfig { |
| @@ -474,6 +486,7 @@ impl Default for FdCanConfig { | |||
| 474 | timestamp_source: TimestampSource::None, | 486 | timestamp_source: TimestampSource::None, |
| 475 | global_filter: GlobalFilter::default(), | 487 | global_filter: GlobalFilter::default(), |
| 476 | tx_buffer_mode: TxBufferMode::Priority, | 488 | tx_buffer_mode: TxBufferMode::Priority, |
| 489 | automatic_bus_off_recovery: true, | ||
| 477 | } | 490 | } |
| 478 | } | 491 | } |
| 479 | } | 492 | } |
diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index d8f71e03e..9883aff57 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs | |||
| @@ -3,8 +3,8 @@ use core::future::poll_fn; | |||
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | use core::task::Poll; | 4 | use core::task::Poll; |
| 5 | 5 | ||
| 6 | use embassy_hal_internal::interrupt::InterruptExt; | ||
| 7 | use embassy_hal_internal::PeripheralType; | 6 | use embassy_hal_internal::PeripheralType; |
| 7 | use embassy_hal_internal::interrupt::InterruptExt; | ||
| 8 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 8 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 9 | use embassy_sync::channel::Channel; | 9 | use embassy_sync::channel::Channel; |
| 10 | use embassy_sync::waitqueue::AtomicWaker; | 10 | use embassy_sync::waitqueue::AtomicWaker; |
| @@ -13,7 +13,7 @@ use crate::can::fd::peripheral::Registers; | |||
| 13 | use crate::gpio::{AfType, OutputType, Pull, SealedPin as _, Speed}; | 13 | use crate::gpio::{AfType, OutputType, Pull, SealedPin as _, Speed}; |
| 14 | use crate::interrupt::typelevel::Interrupt; | 14 | use crate::interrupt::typelevel::Interrupt; |
| 15 | use crate::rcc::{self, RccPeripheral}; | 15 | use crate::rcc::{self, RccPeripheral}; |
| 16 | use crate::{interrupt, peripherals, Peri}; | 16 | use crate::{Peri, interrupt, peripherals}; |
| 17 | 17 | ||
| 18 | pub(crate) mod fd; | 18 | pub(crate) mod fd; |
| 19 | 19 | ||
| @@ -53,7 +53,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0Interrup | |||
| 53 | regs.ir().write(|w| w.set_tefn(true)); | 53 | regs.ir().write(|w| w.set_tefn(true)); |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | T::info().state.lock(|s| { | 56 | let recover_from_bo = T::info().state.lock(|s| { |
| 57 | let state = s.borrow_mut(); | 57 | let state = s.borrow_mut(); |
| 58 | match &state.tx_mode { | 58 | match &state.tx_mode { |
| 59 | TxMode::NonBuffered(waker) => waker.wake(), | 59 | TxMode::NonBuffered(waker) => waker.wake(), |
| @@ -85,11 +85,15 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0Interrup | |||
| 85 | if ir.rfn(1) { | 85 | if ir.rfn(1) { |
| 86 | state.rx_mode.on_interrupt::<T>(1, state.ns_per_timer_tick); | 86 | state.rx_mode.on_interrupt::<T>(1, state.ns_per_timer_tick); |
| 87 | } | 87 | } |
| 88 | |||
| 89 | state.automatic_bus_off_recovery | ||
| 88 | }); | 90 | }); |
| 89 | 91 | ||
| 90 | if ir.bo() { | 92 | if ir.bo() { |
| 91 | regs.ir().write(|w| w.set_bo(true)); | 93 | regs.ir().write(|w| w.set_bo(true)); |
| 92 | if regs.psr().read().bo() { | 94 | if let Some(true) = recover_from_bo |
| 95 | && regs.psr().read().bo() | ||
| 96 | { | ||
| 93 | // Initiate bus-off recovery sequence by resetting CCCR.INIT | 97 | // Initiate bus-off recovery sequence by resetting CCCR.INIT |
| 94 | regs.cccr().modify(|w| w.set_init(false)); | 98 | regs.cccr().modify(|w| w.set_init(false)); |
| 95 | } | 99 | } |
| @@ -182,8 +186,8 @@ impl<'d> CanConfigurator<'d> { | |||
| 182 | rx: Peri<'d, impl RxPin<T>>, | 186 | rx: Peri<'d, impl RxPin<T>>, |
| 183 | tx: Peri<'d, impl TxPin<T>>, | 187 | tx: Peri<'d, impl TxPin<T>>, |
| 184 | _irqs: impl interrupt::typelevel::Binding<T::IT0Interrupt, IT0InterruptHandler<T>> | 188 | _irqs: impl interrupt::typelevel::Binding<T::IT0Interrupt, IT0InterruptHandler<T>> |
| 185 | + interrupt::typelevel::Binding<T::IT1Interrupt, IT1InterruptHandler<T>> | 189 | + interrupt::typelevel::Binding<T::IT1Interrupt, IT1InterruptHandler<T>> |
| 186 | + 'd, | 190 | + 'd, |
| 187 | ) -> CanConfigurator<'d> { | 191 | ) -> CanConfigurator<'d> { |
| 188 | set_as_af!(rx, AfType::input(Pull::None)); | 192 | set_as_af!(rx, AfType::input(Pull::None)); |
| 189 | set_as_af!(tx, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 193 | set_as_af!(tx, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| @@ -263,7 +267,9 @@ impl<'d> CanConfigurator<'d> { | |||
| 263 | pub fn start(self, mode: OperatingMode) -> Can<'d> { | 267 | pub fn start(self, mode: OperatingMode) -> Can<'d> { |
| 264 | let ns_per_timer_tick = calc_ns_per_timer_tick(&self.info, self.periph_clock, self.config.frame_transmit); | 268 | let ns_per_timer_tick = calc_ns_per_timer_tick(&self.info, self.periph_clock, self.config.frame_transmit); |
| 265 | self.info.state.lock(|s| { | 269 | self.info.state.lock(|s| { |
| 266 | s.borrow_mut().ns_per_timer_tick = ns_per_timer_tick; | 270 | let mut state = s.borrow_mut(); |
| 271 | state.ns_per_timer_tick = ns_per_timer_tick; | ||
| 272 | state.automatic_bus_off_recovery = Some(self.config.automatic_bus_off_recovery); | ||
| 267 | }); | 273 | }); |
| 268 | self.info.regs.into_mode(self.config, mode); | 274 | self.info.regs.into_mode(self.config, mode); |
| 269 | Can { | 275 | Can { |
| @@ -459,7 +465,7 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, | |||
| 459 | pub async fn write(&mut self, frame: Frame) { | 465 | pub async fn write(&mut self, frame: Frame) { |
| 460 | self.tx_buf.send(frame).await; | 466 | self.tx_buf.send(frame).await; |
| 461 | self.info.interrupt0.pend(); // Wake for Tx | 467 | self.info.interrupt0.pend(); // Wake for Tx |
| 462 | //T::IT0Interrupt::pend(); // Wake for Tx | 468 | //T::IT0Interrupt::pend(); // Wake for Tx |
| 463 | } | 469 | } |
| 464 | 470 | ||
| 465 | /// Async read frame from RX buffer. | 471 | /// Async read frame from RX buffer. |
| @@ -548,7 +554,7 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<' | |||
| 548 | pub async fn write(&mut self, frame: FdFrame) { | 554 | pub async fn write(&mut self, frame: FdFrame) { |
| 549 | self.tx_buf.send(frame).await; | 555 | self.tx_buf.send(frame).await; |
| 550 | self.info.interrupt0.pend(); // Wake for Tx | 556 | self.info.interrupt0.pend(); // Wake for Tx |
| 551 | //T::IT0Interrupt::pend(); // Wake for Tx | 557 | //T::IT0Interrupt::pend(); // Wake for Tx |
| 552 | } | 558 | } |
| 553 | 559 | ||
| 554 | /// Async read frame from RX buffer. | 560 | /// Async read frame from RX buffer. |
| @@ -861,7 +867,7 @@ struct State { | |||
| 861 | sender_instance_count: usize, | 867 | sender_instance_count: usize, |
| 862 | tx_pin_port: Option<u8>, | 868 | tx_pin_port: Option<u8>, |
| 863 | rx_pin_port: Option<u8>, | 869 | rx_pin_port: Option<u8>, |
| 864 | 870 | automatic_bus_off_recovery: Option<bool>, // controlled by CanConfigurator::start() | |
| 865 | pub err_waker: AtomicWaker, | 871 | pub err_waker: AtomicWaker, |
| 866 | } | 872 | } |
| 867 | 873 | ||
| @@ -876,6 +882,7 @@ impl State { | |||
| 876 | sender_instance_count: 0, | 882 | sender_instance_count: 0, |
| 877 | tx_pin_port: None, | 883 | tx_pin_port: None, |
| 878 | rx_pin_port: None, | 884 | rx_pin_port: None, |
| 885 | automatic_bus_off_recovery: None, | ||
| 879 | } | 886 | } |
| 880 | } | 887 | } |
| 881 | } | 888 | } |
diff --git a/embassy-stm32/src/can/util.rs b/embassy-stm32/src/can/util.rs index fcdbbad62..6d7f0c16a 100644 --- a/embassy-stm32/src/can/util.rs +++ b/embassy-stm32/src/can/util.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | //! Utility functions shared between CAN controller types. | 1 | //! Utility functions shared between CAN controller types. |
| 2 | 2 | ||
| 3 | use core::num::{NonZeroU16, NonZeroU8}; | 3 | use core::num::{NonZeroU8, NonZeroU16}; |
| 4 | 4 | ||
| 5 | /// Shared struct to represent bit timings used by calc_can_timings. | 5 | /// Shared struct to represent bit timings used by calc_can_timings. |
| 6 | #[derive(Clone, Copy, Debug)] | 6 | #[derive(Clone, Copy, Debug)] |
diff --git a/embassy-stm32/src/crc/v1.rs b/embassy-stm32/src/crc/v1.rs index 13e5263de..836228599 100644 --- a/embassy-stm32/src/crc/v1.rs +++ b/embassy-stm32/src/crc/v1.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | use crate::pac::CRC as PAC_CRC; | 1 | use crate::pac::CRC as PAC_CRC; |
| 2 | use crate::peripherals::CRC; | 2 | use crate::peripherals::CRC; |
| 3 | use crate::{rcc, Peri}; | 3 | use crate::{Peri, rcc}; |
| 4 | 4 | ||
| 5 | /// CRC driver. | 5 | /// CRC driver. |
| 6 | pub struct Crc<'d> { | 6 | pub struct Crc<'d> { |
diff --git a/embassy-stm32/src/crc/v2v3.rs b/embassy-stm32/src/crc/v2v3.rs index d834d0971..a566a2e04 100644 --- a/embassy-stm32/src/crc/v2v3.rs +++ b/embassy-stm32/src/crc/v2v3.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | use crate::pac::crc::vals; | ||
| 2 | use crate::pac::CRC as PAC_CRC; | 1 | use crate::pac::CRC as PAC_CRC; |
| 2 | use crate::pac::crc::vals; | ||
| 3 | use crate::peripherals::CRC; | 3 | use crate::peripherals::CRC; |
| 4 | use crate::{rcc, Peri}; | 4 | use crate::{Peri, rcc}; |
| 5 | 5 | ||
| 6 | /// CRC driver. | 6 | /// CRC driver. |
| 7 | pub struct Crc<'d> { | 7 | pub struct Crc<'d> { |
diff --git a/embassy-stm32/src/cryp/mod.rs b/embassy-stm32/src/cryp/mod.rs index 0173b2b5d..4f1115fb7 100644 --- a/embassy-stm32/src/cryp/mod.rs +++ b/embassy-stm32/src/cryp/mod.rs | |||
| @@ -1236,7 +1236,10 @@ impl<'d, T: Instance, M: Mode> Cryp<'d, T, M> { | |||
| 1236 | } | 1236 | } |
| 1237 | if C::REQUIRES_PADDING { | 1237 | if C::REQUIRES_PADDING { |
| 1238 | if last_block_remainder != 0 { | 1238 | if last_block_remainder != 0 { |
| 1239 | panic!("Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.", C::BLOCK_SIZE); | 1239 | panic!( |
| 1240 | "Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.", | ||
| 1241 | C::BLOCK_SIZE | ||
| 1242 | ); | ||
| 1240 | } | 1243 | } |
| 1241 | } | 1244 | } |
| 1242 | if last_block { | 1245 | if last_block { |
| @@ -1703,7 +1706,10 @@ impl<'d, T: Instance> Cryp<'d, T, Async> { | |||
| 1703 | } | 1706 | } |
| 1704 | if C::REQUIRES_PADDING { | 1707 | if C::REQUIRES_PADDING { |
| 1705 | if last_block_remainder != 0 { | 1708 | if last_block_remainder != 0 { |
| 1706 | panic!("Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.", C::BLOCK_SIZE); | 1709 | panic!( |
| 1710 | "Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.", | ||
| 1711 | C::BLOCK_SIZE | ||
| 1712 | ); | ||
| 1707 | } | 1713 | } |
| 1708 | } | 1714 | } |
| 1709 | if last_block { | 1715 | if last_block { |
diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 08e001337..d74d4a4be 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs | |||
| @@ -8,7 +8,7 @@ use crate::mode::{Async, Blocking, Mode as PeriMode}; | |||
| 8 | #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] | 8 | #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] |
| 9 | use crate::pac::dac; | 9 | use crate::pac::dac; |
| 10 | use crate::rcc::{self, RccPeripheral}; | 10 | use crate::rcc::{self, RccPeripheral}; |
| 11 | use crate::{peripherals, Peri}; | 11 | use crate::{Peri, peripherals}; |
| 12 | 12 | ||
| 13 | mod tsel; | 13 | mod tsel; |
| 14 | use embassy_hal_internal::PeripheralType; | 14 | use embassy_hal_internal::PeripheralType; |
diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index bd03f1e00..dcae9f298 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs | |||
| @@ -9,7 +9,7 @@ use embassy_sync::waitqueue::AtomicWaker; | |||
| 9 | use crate::dma::Transfer; | 9 | use crate::dma::Transfer; |
| 10 | use crate::gpio::{AfType, Pull}; | 10 | use crate::gpio::{AfType, Pull}; |
| 11 | use crate::interrupt::typelevel::Interrupt; | 11 | use crate::interrupt::typelevel::Interrupt; |
| 12 | use crate::{interrupt, rcc, Peri}; | 12 | use crate::{Peri, interrupt, rcc}; |
| 13 | 13 | ||
| 14 | /// Interrupt handler. | 14 | /// Interrupt handler. |
| 15 | pub struct InterruptHandler<T: Instance> { | 15 | pub struct InterruptHandler<T: Instance> { |
diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index 73ecab070..90dbf4f09 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | use core::future::{poll_fn, Future}; | 1 | use core::future::{Future, poll_fn}; |
| 2 | use core::pin::Pin; | 2 | use core::pin::Pin; |
| 3 | use core::sync::atomic::{fence, AtomicUsize, Ordering}; | 3 | use core::sync::atomic::{AtomicUsize, Ordering, fence}; |
| 4 | use core::task::{Context, Poll, Waker}; | 4 | use core::task::{Context, Poll, Waker}; |
| 5 | 5 | ||
| 6 | use embassy_hal_internal::Peri; | 6 | use embassy_hal_internal::Peri; |
diff --git a/embassy-stm32/src/dma/gpdma/mod.rs b/embassy-stm32/src/dma/gpdma/mod.rs index 4a14c2a8e..3e117c331 100644 --- a/embassy-stm32/src/dma/gpdma/mod.rs +++ b/embassy-stm32/src/dma/gpdma/mod.rs | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | use core::future::Future; | 3 | use core::future::Future; |
| 4 | use core::pin::Pin; | 4 | use core::pin::Pin; |
| 5 | use core::sync::atomic::{fence, AtomicUsize, Ordering}; | 5 | use core::sync::atomic::{AtomicUsize, Ordering, fence}; |
| 6 | use core::task::{Context, Poll}; | 6 | use core::task::{Context, Poll}; |
| 7 | 7 | ||
| 8 | use embassy_hal_internal::Peri; | 8 | use embassy_hal_internal::Peri; |
diff --git a/embassy-stm32/src/dma/gpdma/ringbuffered.rs b/embassy-stm32/src/dma/gpdma/ringbuffered.rs index 9ee52193b..94c597e0d 100644 --- a/embassy-stm32/src/dma/gpdma/ringbuffered.rs +++ b/embassy-stm32/src/dma/gpdma/ringbuffered.rs | |||
| @@ -3,12 +3,12 @@ | |||
| 3 | //! FIXME: Add request_pause functionality? | 3 | //! FIXME: Add request_pause functionality? |
| 4 | //! FIXME: Stop the DMA, if a user does not queue new transfers (chain of linked-list items ends automatically). | 4 | //! FIXME: Stop the DMA, if a user does not queue new transfers (chain of linked-list items ends automatically). |
| 5 | use core::future::poll_fn; | 5 | use core::future::poll_fn; |
| 6 | use core::sync::atomic::{fence, Ordering}; | 6 | use core::sync::atomic::{Ordering, fence}; |
| 7 | use core::task::Waker; | 7 | use core::task::Waker; |
| 8 | 8 | ||
| 9 | use embassy_hal_internal::Peri; | 9 | use embassy_hal_internal::Peri; |
| 10 | 10 | ||
| 11 | use super::{AnyChannel, TransferOptions, STATE}; | 11 | use super::{AnyChannel, STATE, TransferOptions}; |
| 12 | use crate::dma::gpdma::linked_list::{RunMode, Table}; | 12 | use crate::dma::gpdma::linked_list::{RunMode, Table}; |
| 13 | use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; | 13 | use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; |
| 14 | use crate::dma::word::Word; | 14 | use crate::dma::word::Word; |
diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 5989bfd7c..297fa3674 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs | |||
| @@ -24,7 +24,7 @@ pub(crate) use util::*; | |||
| 24 | pub(crate) mod ringbuffer; | 24 | pub(crate) mod ringbuffer; |
| 25 | pub mod word; | 25 | pub mod word; |
| 26 | 26 | ||
| 27 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; | 27 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; |
| 28 | 28 | ||
| 29 | use crate::interrupt; | 29 | use crate::interrupt; |
| 30 | 30 | ||
diff --git a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs index 661fb1728..eff5b4058 100644 --- a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs | |||
| @@ -2,7 +2,7 @@ use std::task::Waker; | |||
| 2 | 2 | ||
| 3 | use proptest::prop_oneof; | 3 | use proptest::prop_oneof; |
| 4 | use proptest::strategy::{self, BoxedStrategy, Strategy as _}; | 4 | use proptest::strategy::{self, BoxedStrategy, Strategy as _}; |
| 5 | use proptest_state_machine::{prop_state_machine, ReferenceStateMachine, StateMachineTest}; | 5 | use proptest_state_machine::{ReferenceStateMachine, StateMachineTest, prop_state_machine}; |
| 6 | 6 | ||
| 7 | use super::*; | 7 | use super::*; |
| 8 | 8 | ||
diff --git a/embassy-stm32/src/dsihost.rs b/embassy-stm32/src/dsihost.rs index deda956af..59a2cbcdb 100644 --- a/embassy-stm32/src/dsihost.rs +++ b/embassy-stm32/src/dsihost.rs | |||
| @@ -7,7 +7,7 @@ use embassy_hal_internal::PeripheralType; | |||
| 7 | //use crate::gpio::{AnyPin, SealedPin}; | 7 | //use crate::gpio::{AnyPin, SealedPin}; |
| 8 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 8 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
| 9 | use crate::rcc::{self, RccPeripheral}; | 9 | use crate::rcc::{self, RccPeripheral}; |
| 10 | use crate::{peripherals, Peri}; | 10 | use crate::{Peri, peripherals}; |
| 11 | 11 | ||
| 12 | /// Performs a busy-wait delay for a specified number of microseconds. | 12 | /// Performs a busy-wait delay for a specified number of microseconds. |
| 13 | pub fn blocking_delay_ms(ms: u32) { | 13 | pub fn blocking_delay_ms(ms: u32) { |
| @@ -121,17 +121,15 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 121 | 121 | ||
| 122 | /// DCS or Generic short/long write command | 122 | /// DCS or Generic short/long write command |
| 123 | pub fn write_cmd(&mut self, channel_id: u8, address: u8, data: &[u8]) -> Result<(), Error> { | 123 | pub fn write_cmd(&mut self, channel_id: u8, address: u8, data: &[u8]) -> Result<(), Error> { |
| 124 | assert!(data.len() > 0); | 124 | match data.len() { |
| 125 | 125 | 0 => self.short_write(channel_id, PacketType::DcsShortPktWriteP0, address, 0), | |
| 126 | if data.len() == 1 { | 126 | 1 => self.short_write(channel_id, PacketType::DcsShortPktWriteP1, address, data[0]), |
| 127 | self.short_write(channel_id, PacketType::DcsShortPktWriteP1, address, data[0]) | 127 | _ => self.long_write( |
| 128 | } else { | ||
| 129 | self.long_write( | ||
| 130 | channel_id, | 128 | channel_id, |
| 131 | PacketType::DcsLongPktWrite, // FIXME: This might be a generic long packet, as well... | 129 | PacketType::DcsLongPktWrite, // FIXME: This might be a generic long packet, as well... |
| 132 | address, | 130 | address, |
| 133 | data, | 131 | data, |
| 134 | ) | 132 | ), |
| 135 | } | 133 | } |
| 136 | } | 134 | } |
| 137 | 135 | ||
diff --git a/embassy-stm32/src/dts/mod.rs b/embassy-stm32/src/dts/mod.rs index 1f39c8db5..a75ae0560 100644 --- a/embassy-stm32/src/dts/mod.rs +++ b/embassy-stm32/src/dts/mod.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | //! Digital Temperature Sensor (DTS) | 1 | //! Digital Temperature Sensor (DTS) |
| 2 | 2 | ||
| 3 | use core::future::poll_fn; | 3 | use core::future::poll_fn; |
| 4 | use core::sync::atomic::{compiler_fence, Ordering}; | 4 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 5 | use core::task::Poll; | 5 | use core::task::Poll; |
| 6 | 6 | ||
| 7 | use embassy_hal_internal::Peri; | 7 | use embassy_hal_internal::Peri; |
diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 5be1c9739..a77eb8719 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs | |||
| @@ -4,7 +4,7 @@ mod rx_desc; | |||
| 4 | mod tx_desc; | 4 | mod tx_desc; |
| 5 | 5 | ||
| 6 | use core::marker::PhantomData; | 6 | use core::marker::PhantomData; |
| 7 | use core::sync::atomic::{fence, Ordering}; | 7 | use core::sync::atomic::{Ordering, fence}; |
| 8 | 8 | ||
| 9 | use embassy_hal_internal::Peri; | 9 | use embassy_hal_internal::Peri; |
| 10 | use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; | 10 | use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; |
| @@ -190,7 +190,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 190 | w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping | 190 | w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping |
| 191 | w.set_fes(Fes::FES100); // fast ethernet speed | 191 | w.set_fes(Fes::FES100); // fast ethernet speed |
| 192 | w.set_dm(Dm::FULL_DUPLEX); // full duplex | 192 | w.set_dm(Dm::FULL_DUPLEX); // full duplex |
| 193 | // TODO: Carrier sense ? ECRSFD | 193 | // TODO: Carrier sense ? ECRSFD |
| 194 | }); | 194 | }); |
| 195 | 195 | ||
| 196 | // Set the mac to pass all multicast packets | 196 | // Set the mac to pass all multicast packets |
| @@ -350,7 +350,9 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 350 | } | 350 | } |
| 351 | 351 | ||
| 352 | #[cfg(any(eth_v1b, eth_v1c))] | 352 | #[cfg(any(eth_v1b, eth_v1c))] |
| 353 | config_pins!(rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); | 353 | config_pins!( |
| 354 | rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en | ||
| 355 | ); | ||
| 354 | 356 | ||
| 355 | let pins = Pins::Mii([ | 357 | let pins = Pins::Mii([ |
| 356 | rx_clk.into(), | 358 | rx_clk.into(), |
diff --git a/embassy-stm32/src/eth/v1/rx_desc.rs b/embassy-stm32/src/eth/v1/rx_desc.rs index 2a46c1895..6ade1f29c 100644 --- a/embassy-stm32/src/eth/v1/rx_desc.rs +++ b/embassy-stm32/src/eth/v1/rx_desc.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | use core::sync::atomic::{compiler_fence, fence, Ordering}; | 1 | use core::sync::atomic::{Ordering, compiler_fence, fence}; |
| 2 | 2 | ||
| 3 | use stm32_metapac::eth::vals::{Rpd, Rps}; | 3 | use stm32_metapac::eth::vals::{Rpd, Rps}; |
| 4 | use vcell::VolatileCell; | 4 | use vcell::VolatileCell; |
diff --git a/embassy-stm32/src/eth/v1/tx_desc.rs b/embassy-stm32/src/eth/v1/tx_desc.rs index 1317d20f4..ba99b66cb 100644 --- a/embassy-stm32/src/eth/v1/tx_desc.rs +++ b/embassy-stm32/src/eth/v1/tx_desc.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | use core::sync::atomic::{compiler_fence, fence, Ordering}; | 1 | use core::sync::atomic::{Ordering, compiler_fence, fence}; |
| 2 | 2 | ||
| 3 | use vcell::VolatileCell; | 3 | use vcell::VolatileCell; |
| 4 | 4 | ||
diff --git a/embassy-stm32/src/eth/v2/descriptors.rs b/embassy-stm32/src/eth/v2/descriptors.rs index 645bfdb14..e335ed8f3 100644 --- a/embassy-stm32/src/eth/v2/descriptors.rs +++ b/embassy-stm32/src/eth/v2/descriptors.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | use core::sync::atomic::{fence, Ordering}; | 1 | use core::sync::atomic::{Ordering, fence}; |
| 2 | 2 | ||
| 3 | use vcell::VolatileCell; | 3 | use vcell::VolatileCell; |
| 4 | 4 | ||
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index cf7a9901b..39a6e8b0f 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | mod descriptors; | 1 | mod descriptors; |
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | use core::sync::atomic::{fence, Ordering}; | 4 | use core::sync::atomic::{Ordering, fence}; |
| 5 | 5 | ||
| 6 | use embassy_hal_internal::Peri; | 6 | use embassy_hal_internal::Peri; |
| 7 | use stm32_metapac::syscfg::vals::EthSelPhy; | 7 | use stm32_metapac::syscfg::vals::EthSelPhy; |
| @@ -144,7 +144,9 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 144 | .modify(|w| w.set_eth_sel_phy(EthSelPhy::MII_GMII)); | 144 | .modify(|w| w.set_eth_sel_phy(EthSelPhy::MII_GMII)); |
| 145 | }); | 145 | }); |
| 146 | 146 | ||
| 147 | config_pins!(rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); | 147 | config_pins!( |
| 148 | rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en | ||
| 149 | ); | ||
| 148 | 150 | ||
| 149 | let pins = Pins::Mii([ | 151 | let pins = Pins::Mii([ |
| 150 | rx_clk.into(), | 152 | rx_clk.into(), |
diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index bc4ecd1cc..83e0ecf88 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs | |||
| @@ -71,7 +71,7 @@ unsafe fn on_irq() { | |||
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | #[cfg(feature = "low-power")] | 73 | #[cfg(feature = "low-power")] |
| 74 | crate::low_power::on_wakeup_irq(); | 74 | crate::low_power::Executor::on_wakeup_irq(); |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | struct BitIter(u32); | 77 | struct BitIter(u32); |
diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index 006dcddeb..a131217b7 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs | |||
| @@ -1,17 +1,17 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | use core::marker::PhantomData; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use embassy_hal_internal::drop::OnDrop; | 4 | use embassy_hal_internal::drop::OnDrop; |
| 5 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 5 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 6 | use embassy_sync::mutex::Mutex; | 6 | use embassy_sync::mutex::Mutex; |
| 7 | 7 | ||
| 8 | use super::{ | 8 | use super::{ |
| 9 | blocking_read, ensure_sector_aligned, family, get_flash_regions, get_sector, Async, Error, Flash, FlashLayout, | 9 | Async, Error, FLASH_BASE, FLASH_SIZE, Flash, FlashLayout, WRITE_SIZE, blocking_read, ensure_sector_aligned, family, |
| 10 | FLASH_BASE, FLASH_SIZE, WRITE_SIZE, | 10 | get_flash_regions, get_sector, |
| 11 | }; | 11 | }; |
| 12 | use crate::interrupt::InterruptExt; | 12 | use crate::interrupt::InterruptExt; |
| 13 | use crate::peripherals::FLASH; | 13 | use crate::peripherals::FLASH; |
| 14 | use crate::{interrupt, Peri}; | 14 | use crate::{Peri, interrupt}; |
| 15 | 15 | ||
| 16 | pub(super) static REGION_ACCESS: Mutex<CriticalSectionRawMutex, ()> = Mutex::new(()); | 16 | pub(super) static REGION_ACCESS: Mutex<CriticalSectionRawMutex, ()> = Mutex::new(()); |
| 17 | 17 | ||
diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 10023e637..b595938a6 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs | |||
| @@ -1,14 +1,14 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | use core::marker::PhantomData; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use embassy_hal_internal::drop::OnDrop; | 4 | use embassy_hal_internal::drop::OnDrop; |
| 5 | 5 | ||
| 6 | use super::{ | 6 | use super::{ |
| 7 | family, get_flash_regions, Async, Blocking, Error, FlashBank, FlashLayout, FlashRegion, FlashSector, FLASH_SIZE, | 7 | Async, Blocking, Error, FLASH_SIZE, FlashBank, FlashLayout, FlashRegion, FlashSector, MAX_ERASE_SIZE, READ_SIZE, |
| 8 | MAX_ERASE_SIZE, READ_SIZE, WRITE_SIZE, | 8 | WRITE_SIZE, family, get_flash_regions, |
| 9 | }; | 9 | }; |
| 10 | use crate::Peri; | ||
| 11 | use crate::_generated::FLASH_BASE; | 10 | use crate::_generated::FLASH_BASE; |
| 11 | use crate::Peri; | ||
| 12 | use crate::peripherals::FLASH; | 12 | use crate::peripherals::FLASH; |
| 13 | 13 | ||
| 14 | /// Internal flash memory driver. | 14 | /// Internal flash memory driver. |
diff --git a/embassy-stm32/src/flash/eeprom.rs b/embassy-stm32/src/flash/eeprom.rs index cc3529eb9..39c497e3f 100644 --- a/embassy-stm32/src/flash/eeprom.rs +++ b/embassy-stm32/src/flash/eeprom.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | use embassy_hal_internal::drop::OnDrop; | 1 | use embassy_hal_internal::drop::OnDrop; |
| 2 | 2 | ||
| 3 | use super::{family, Blocking, Error, Flash, EEPROM_BASE, EEPROM_SIZE}; | 3 | use super::{Blocking, EEPROM_BASE, EEPROM_SIZE, Error, Flash, family}; |
| 4 | 4 | ||
| 5 | #[cfg(eeprom)] | 5 | #[cfg(eeprom)] |
| 6 | impl<'d> Flash<'d, Blocking> { | 6 | impl<'d> Flash<'d, Blocking> { |
diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index 3f9dbe945..5c01fce9c 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use super::{FlashSector, WRITE_SIZE}; | 4 | use super::{FlashSector, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 5 | use crate::flash::Error; |
diff --git a/embassy-stm32/src/flash/f1f3.rs b/embassy-stm32/src/flash/f1f3.rs index bf9ad2893..9e469ffbc 100644 --- a/embassy-stm32/src/flash/f1f3.rs +++ b/embassy-stm32/src/flash/f1f3.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use super::{FlashSector, WRITE_SIZE}; | 4 | use super::{FlashSector, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 5 | use crate::flash::Error; |
diff --git a/embassy-stm32/src/flash/f2.rs b/embassy-stm32/src/flash/f2.rs index 67e380619..b48ab3b76 100644 --- a/embassy-stm32/src/flash/f2.rs +++ b/embassy-stm32/src/flash/f2.rs | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, AtomicBool, Ordering}; | 2 | use core::sync::atomic::{AtomicBool, Ordering, fence}; |
| 3 | 3 | ||
| 4 | use pac::flash::regs::Sr; | 4 | use pac::flash::regs::Sr; |
| 5 | 5 | ||
| 6 | use super::{get_flash_regions, FlashBank, FlashSector, WRITE_SIZE}; | 6 | use super::{FlashBank, FlashSector, WRITE_SIZE, get_flash_regions}; |
| 7 | use crate::flash::Error; | 7 | use crate::flash::Error; |
| 8 | use crate::pac; | 8 | use crate::pac; |
| 9 | 9 | ||
diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 62e0492b5..9c5051492 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs | |||
| @@ -1,10 +1,10 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, AtomicBool, Ordering}; | 2 | use core::sync::atomic::{AtomicBool, Ordering, fence}; |
| 3 | 3 | ||
| 4 | use embassy_sync::waitqueue::AtomicWaker; | 4 | use embassy_sync::waitqueue::AtomicWaker; |
| 5 | use pac::flash::regs::Sr; | 5 | use pac::flash::regs::Sr; |
| 6 | 6 | ||
| 7 | use super::{get_flash_regions, FlashBank, FlashSector, WRITE_SIZE}; | 7 | use super::{FlashBank, FlashSector, WRITE_SIZE, get_flash_regions}; |
| 8 | use crate::_generated::FLASH_SIZE; | 8 | use crate::_generated::FLASH_SIZE; |
| 9 | use crate::flash::Error; | 9 | use crate::flash::Error; |
| 10 | use crate::pac; | 10 | use crate::pac; |
| @@ -246,7 +246,9 @@ pub(crate) fn assert_not_corrupted_read(end_address: u32) { | |||
| 246 | feature = "stm32f439zi", | 246 | feature = "stm32f439zi", |
| 247 | ))] | 247 | ))] |
| 248 | if second_bank_read && pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() { | 248 | if second_bank_read && pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() { |
| 249 | panic!("Read corruption for stm32f42xxI and stm32f43xxI when PA12 is in use for chips below revision 3, see errata 2.2.11"); | 249 | panic!( |
| 250 | "Read corruption for stm32f42xxI and stm32f43xxI when PA12 is in use for chips below revision 3, see errata 2.2.11" | ||
| 251 | ); | ||
| 250 | } | 252 | } |
| 251 | 253 | ||
| 252 | #[cfg(any( | 254 | #[cfg(any( |
| @@ -270,14 +272,16 @@ pub(crate) fn assert_not_corrupted_read(end_address: u32) { | |||
| 270 | feature = "stm32f439zg", | 272 | feature = "stm32f439zg", |
| 271 | ))] | 273 | ))] |
| 272 | if second_bank_read && pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() { | 274 | if second_bank_read && pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() { |
| 273 | panic!("Read corruption for stm32f42xxG and stm32f43xxG in dual bank mode when PA12 is in use for chips below revision 3, see errata 2.2.11"); | 275 | panic!( |
| 276 | "Read corruption for stm32f42xxG and stm32f43xxG in dual bank mode when PA12 is in use for chips below revision 3, see errata 2.2.11" | ||
| 277 | ); | ||
| 274 | } | 278 | } |
| 275 | } | 279 | } |
| 276 | 280 | ||
| 277 | #[allow(unused)] | 281 | #[allow(unused)] |
| 278 | fn pa12_is_output_pull_low() -> bool { | 282 | fn pa12_is_output_pull_low() -> bool { |
| 279 | use pac::gpio::vals; | ||
| 280 | use pac::GPIOA; | 283 | use pac::GPIOA; |
| 284 | use pac::gpio::vals; | ||
| 281 | const PIN: usize = 12; | 285 | const PIN: usize = 12; |
| 282 | GPIOA.moder().read().moder(PIN) == vals::Moder::OUTPUT | 286 | GPIOA.moder().read().moder(PIN) == vals::Moder::OUTPUT |
| 283 | && GPIOA.pupdr().read().pupdr(PIN) == vals::Pupdr::PULL_DOWN | 287 | && GPIOA.pupdr().read().pupdr(PIN) == vals::Pupdr::PULL_DOWN |
| @@ -287,7 +291,7 @@ fn pa12_is_output_pull_low() -> bool { | |||
| 287 | #[cfg(test)] | 291 | #[cfg(test)] |
| 288 | mod tests { | 292 | mod tests { |
| 289 | use super::*; | 293 | use super::*; |
| 290 | use crate::flash::{get_sector, FlashBank}; | 294 | use crate::flash::{FlashBank, get_sector}; |
| 291 | 295 | ||
| 292 | #[test] | 296 | #[test] |
| 293 | #[cfg(stm32f429)] | 297 | #[cfg(stm32f429)] |
| @@ -370,9 +374,13 @@ mod tests { | |||
| 370 | #[cfg(all(bank_setup_configurable))] | 374 | #[cfg(all(bank_setup_configurable))] |
| 371 | pub(crate) fn check_bank_setup() { | 375 | pub(crate) fn check_bank_setup() { |
| 372 | if cfg!(feature = "single-bank") && pac::FLASH.optcr().read().db1m() { | 376 | if cfg!(feature = "single-bank") && pac::FLASH.optcr().read().db1m() { |
| 373 | panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use dual-bank config"); | 377 | panic!( |
| 378 | "Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use dual-bank config" | ||
| 379 | ); | ||
| 374 | } | 380 | } |
| 375 | if cfg!(feature = "dual-bank") && !pac::FLASH.optcr().read().db1m() { | 381 | if cfg!(feature = "dual-bank") && !pac::FLASH.optcr().read().db1m() { |
| 376 | panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use single-bank config"); | 382 | panic!( |
| 383 | "Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use single-bank config" | ||
| 384 | ); | ||
| 377 | } | 385 | } |
| 378 | } | 386 | } |
diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 0547c747a..09389c417 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use super::{FlashSector, WRITE_SIZE}; | 4 | use super::{FlashSector, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 5 | use crate::flash::Error; |
| @@ -99,7 +99,7 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { | |||
| 99 | #[cfg(test)] | 99 | #[cfg(test)] |
| 100 | mod tests { | 100 | mod tests { |
| 101 | use super::*; | 101 | use super::*; |
| 102 | use crate::flash::{get_sector, FlashBank}; | 102 | use crate::flash::{FlashBank, get_sector}; |
| 103 | 103 | ||
| 104 | #[test] | 104 | #[test] |
| 105 | #[cfg(stm32f732)] | 105 | #[cfg(stm32f732)] |
| @@ -218,9 +218,13 @@ mod tests { | |||
| 218 | #[cfg(all(bank_setup_configurable))] | 218 | #[cfg(all(bank_setup_configurable))] |
| 219 | pub(crate) fn check_bank_setup() { | 219 | pub(crate) fn check_bank_setup() { |
| 220 | if cfg!(feature = "single-bank") && !pac::FLASH.optcr().read().n_dbank() { | 220 | if cfg!(feature = "single-bank") && !pac::FLASH.optcr().read().n_dbank() { |
| 221 | panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use dual-bank config"); | 221 | panic!( |
| 222 | "Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use dual-bank config" | ||
| 223 | ); | ||
| 222 | } | 224 | } |
| 223 | if cfg!(feature = "dual-bank") && pac::FLASH.optcr().read().n_dbank() { | 225 | if cfg!(feature = "dual-bank") && pac::FLASH.optcr().read().n_dbank() { |
| 224 | panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use single-bank config"); | 226 | panic!( |
| 227 | "Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use single-bank config" | ||
| 228 | ); | ||
| 225 | } | 229 | } |
| 226 | } | 230 | } |
diff --git a/embassy-stm32/src/flash/g.rs b/embassy-stm32/src/flash/g.rs index bc1fd360c..d026541a4 100644 --- a/embassy-stm32/src/flash/g.rs +++ b/embassy-stm32/src/flash/g.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use cortex_m::interrupt; | 4 | use cortex_m::interrupt; |
| 5 | 5 | ||
| @@ -105,19 +105,27 @@ fn wait_busy() { | |||
| 105 | #[cfg(all(bank_setup_configurable, any(flash_g4c2, flash_g4c3, flash_g4c4)))] | 105 | #[cfg(all(bank_setup_configurable, any(flash_g4c2, flash_g4c3, flash_g4c4)))] |
| 106 | pub(crate) fn check_bank_setup() { | 106 | pub(crate) fn check_bank_setup() { |
| 107 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dbank() { | 107 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dbank() { |
| 108 | panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config"); | 108 | panic!( |
| 109 | "Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config" | ||
| 110 | ); | ||
| 109 | } | 111 | } |
| 110 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dbank() { | 112 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dbank() { |
| 111 | panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config"); | 113 | panic!( |
| 114 | "Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config" | ||
| 115 | ); | ||
| 112 | } | 116 | } |
| 113 | } | 117 | } |
| 114 | 118 | ||
| 115 | #[cfg(all(bank_setup_configurable, flash_g0x1))] | 119 | #[cfg(all(bank_setup_configurable, flash_g0x1))] |
| 116 | pub(crate) fn check_bank_setup() { | 120 | pub(crate) fn check_bank_setup() { |
| 117 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dual_bank() { | 121 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dual_bank() { |
| 118 | panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use dual-bank config"); | 122 | panic!( |
| 123 | "Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use dual-bank config" | ||
| 124 | ); | ||
| 119 | } | 125 | } |
| 120 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dual_bank() { | 126 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dual_bank() { |
| 121 | panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use single-bank config"); | 127 | panic!( |
| 128 | "Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use single-bank config" | ||
| 129 | ); | ||
| 122 | } | 130 | } |
| 123 | } | 131 | } |
diff --git a/embassy-stm32/src/flash/h5.rs b/embassy-stm32/src/flash/h5.rs index fd9bfcc75..88f247879 100644 --- a/embassy-stm32/src/flash/h5.rs +++ b/embassy-stm32/src/flash/h5.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use super::{FlashSector, WRITE_SIZE}; | 4 | use super::{FlashSector, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 5 | use crate::flash::Error; |
diff --git a/embassy-stm32/src/flash/h50.rs b/embassy-stm32/src/flash/h50.rs index f8e210556..91d5da4d6 100644 --- a/embassy-stm32/src/flash/h50.rs +++ b/embassy-stm32/src/flash/h50.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /// STM32H50 series flash impl. See RM0492 | 1 | /// STM32H50 series flash impl. See RM0492 |
| 2 | use core::{ | 2 | use core::{ |
| 3 | ptr::write_volatile, | 3 | ptr::write_volatile, |
| 4 | sync::atomic::{fence, Ordering}, | 4 | sync::atomic::{Ordering, fence}, |
| 5 | }; | 5 | }; |
| 6 | 6 | ||
| 7 | use cortex_m::interrupt; | 7 | use cortex_m::interrupt; |
diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index f1d84101c..8a43cce3f 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use super::{FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE}; | 4 | use super::{BANK1_REGION, FLASH_REGIONS, FlashSector, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 5 | use crate::flash::Error; |
| 6 | use crate::pac; | 6 | use crate::pac; |
| 7 | 7 | ||
diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index 1b82704ec..b3281f2d5 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use super::{FlashSector, WRITE_SIZE}; | 4 | use super::{FlashSector, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 5 | use crate::flash::Error; |
| @@ -96,14 +96,20 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E | |||
| 96 | #[cfg(any(flash_wl, flash_wb, flash_l4, flash_l5))] | 96 | #[cfg(any(flash_wl, flash_wb, flash_l4, flash_l5))] |
| 97 | { | 97 | { |
| 98 | let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; | 98 | let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; |
| 99 | #[cfg(any(flash_l4, flash_l5))] | ||
| 100 | let pgn = super::BANK1_REGION.size as u32 / super::BANK1_REGION.erase_size as u32; | ||
| 99 | 101 | ||
| 100 | #[cfg(flash_l4)] | 102 | #[cfg(flash_l4)] |
| 101 | let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; | 103 | let (idx, bank) = if idx > (pgn - 1) { |
| 104 | (idx - pgn, true) | ||
| 105 | } else { | ||
| 106 | (idx, false) | ||
| 107 | }; | ||
| 102 | 108 | ||
| 103 | #[cfg(flash_l5)] | 109 | #[cfg(flash_l5)] |
| 104 | let (idx, bank) = if pac::FLASH.optr().read().dbank() { | 110 | let (idx, bank) = if pac::FLASH.optr().read().dbank() { |
| 105 | if idx > 255 { | 111 | if idx > (pgn - 1) { |
| 106 | (idx - 256, Some(true)) | 112 | (idx - pgn, Some(true)) |
| 107 | } else { | 113 | } else { |
| 108 | (idx, Some(false)) | 114 | (idx, Some(false)) |
| 109 | } | 115 | } |
| @@ -234,19 +240,27 @@ pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> { | |||
| 234 | #[cfg(all(bank_setup_configurable, flash_l5))] | 240 | #[cfg(all(bank_setup_configurable, flash_l5))] |
| 235 | pub(crate) fn check_bank_setup() { | 241 | pub(crate) fn check_bank_setup() { |
| 236 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dbank() { | 242 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dbank() { |
| 237 | panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config"); | 243 | panic!( |
| 244 | "Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config" | ||
| 245 | ); | ||
| 238 | } | 246 | } |
| 239 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dbank() { | 247 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dbank() { |
| 240 | panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config"); | 248 | panic!( |
| 249 | "Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config" | ||
| 250 | ); | ||
| 241 | } | 251 | } |
| 242 | } | 252 | } |
| 243 | 253 | ||
| 244 | #[cfg(all(bank_setup_configurable, flash_l4))] | 254 | #[cfg(all(bank_setup_configurable, flash_l4))] |
| 245 | pub(crate) fn check_bank_setup() { | 255 | pub(crate) fn check_bank_setup() { |
| 246 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dualbank() { | 256 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dualbank() { |
| 247 | panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use dual-bank config"); | 257 | panic!( |
| 258 | "Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use dual-bank config" | ||
| 259 | ); | ||
| 248 | } | 260 | } |
| 249 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dualbank() { | 261 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dualbank() { |
| 250 | panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use single-bank config"); | 262 | panic!( |
| 263 | "Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use single-bank config" | ||
| 264 | ); | ||
| 251 | } | 265 | } |
| 252 | } | 266 | } |
diff --git a/embassy-stm32/src/flash/u0.rs b/embassy-stm32/src/flash/u0.rs index 68d847eca..a64f6c492 100644 --- a/embassy-stm32/src/flash/u0.rs +++ b/embassy-stm32/src/flash/u0.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use cortex_m::interrupt; | 4 | use cortex_m::interrupt; |
| 5 | 5 | ||
diff --git a/embassy-stm32/src/flash/u5.rs b/embassy-stm32/src/flash/u5.rs index 6c3d4b422..5f1f562c0 100644 --- a/embassy-stm32/src/flash/u5.rs +++ b/embassy-stm32/src/flash/u5.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use super::{FlashBank, FlashSector, WRITE_SIZE}; | 4 | use super::{FlashBank, FlashSector, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 5 | use crate::flash::Error; |
diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs index ff18a8bee..a7c6c90bb 100644 --- a/embassy-stm32/src/fmc.rs +++ b/embassy-stm32/src/fmc.rs | |||
| @@ -4,7 +4,7 @@ use core::marker::PhantomData; | |||
| 4 | use embassy_hal_internal::PeripheralType; | 4 | use embassy_hal_internal::PeripheralType; |
| 5 | 5 | ||
| 6 | use crate::gpio::{AfType, OutputType, Pull, Speed}; | 6 | use crate::gpio::{AfType, OutputType, Pull, Speed}; |
| 7 | use crate::{rcc, Peri}; | 7 | use crate::{Peri, rcc}; |
| 8 | 8 | ||
| 9 | /// FMC driver | 9 | /// FMC driver |
| 10 | pub struct Fmc<'d, T: Instance> { | 10 | pub struct Fmc<'d, T: Instance> { |
| @@ -236,6 +236,42 @@ impl<'d, T: Instance> Fmc<'d, T> { | |||
| 236 | (sdcke: SDCKE1Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE1Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin) | 236 | (sdcke: SDCKE1Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE1Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin) |
| 237 | ] | 237 | ] |
| 238 | )); | 238 | )); |
| 239 | |||
| 240 | fmc_sdram_constructor!(sdram_a13bits_d16bits_4banks_bank1: ( | ||
| 241 | bank: stm32_fmc::SdramTargetBank::Bank1, | ||
| 242 | addr: [ | ||
| 243 | (a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin), (a12: A12Pin) | ||
| 244 | ], | ||
| 245 | ba: [(ba0: BA0Pin), (ba1: BA1Pin)], | ||
| 246 | d: [ | ||
| 247 | (d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin), | ||
| 248 | (d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin) | ||
| 249 | ], | ||
| 250 | nbl: [ | ||
| 251 | (nbl0: NBL0Pin), (nbl1: NBL1Pin) | ||
| 252 | ], | ||
| 253 | ctrl: [ | ||
| 254 | (sdcke: SDCKE0Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE0Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin) | ||
| 255 | ] | ||
| 256 | )); | ||
| 257 | |||
| 258 | fmc_sdram_constructor!(sdram_a13bits_d16bits_4banks_bank2: ( | ||
| 259 | bank: stm32_fmc::SdramTargetBank::Bank2, | ||
| 260 | addr: [ | ||
| 261 | (a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin), (a12: A12Pin) | ||
| 262 | ], | ||
| 263 | ba: [(ba0: BA0Pin), (ba1: BA1Pin)], | ||
| 264 | d: [ | ||
| 265 | (d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin), | ||
| 266 | (d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin) | ||
| 267 | ], | ||
| 268 | nbl: [ | ||
| 269 | (nbl0: NBL0Pin), (nbl1: NBL1Pin) | ||
| 270 | ], | ||
| 271 | ctrl: [ | ||
| 272 | (sdcke: SDCKE1Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE1Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin) | ||
| 273 | ] | ||
| 274 | )); | ||
| 239 | } | 275 | } |
| 240 | 276 | ||
| 241 | trait SealedInstance: crate::rcc::RccPeripheral { | 277 | trait SealedInstance: crate::rcc::RccPeripheral { |
diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index 5645f71cb..1813a887b 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | use core::convert::Infallible; | 4 | use core::convert::Infallible; |
| 5 | 5 | ||
| 6 | use critical_section::CriticalSection; | 6 | use critical_section::CriticalSection; |
| 7 | use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType}; | 7 | use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral}; |
| 8 | 8 | ||
| 9 | use crate::pac::gpio::{self, vals}; | 9 | use crate::pac::gpio::{self, vals}; |
| 10 | use crate::peripherals; | 10 | use crate::peripherals; |
diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index 90c06c0d8..ba573267c 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs | |||
| @@ -19,7 +19,7 @@ use crate::interrupt::typelevel::Interrupt; | |||
| 19 | use crate::mode::Async; | 19 | use crate::mode::Async; |
| 20 | use crate::mode::{Blocking, Mode}; | 20 | use crate::mode::{Blocking, Mode}; |
| 21 | use crate::peripherals::HASH; | 21 | use crate::peripherals::HASH; |
| 22 | use crate::{interrupt, pac, peripherals, rcc, Peri}; | 22 | use crate::{Peri, interrupt, pac, peripherals, rcc}; |
| 23 | 23 | ||
| 24 | #[cfg(hash_v1)] | 24 | #[cfg(hash_v1)] |
| 25 | const NUM_CONTEXT_REGS: usize = 51; | 25 | const NUM_CONTEXT_REGS: usize = 51; |
| @@ -514,11 +514,7 @@ impl<'d, T: Instance> Hash<'d, T, Async> { | |||
| 514 | T::regs().imr().modify(|reg| reg.set_dcie(true)); | 514 | T::regs().imr().modify(|reg| reg.set_dcie(true)); |
| 515 | // Check for completion. | 515 | // Check for completion. |
| 516 | let bits = T::regs().sr().read(); | 516 | let bits = T::regs().sr().read(); |
| 517 | if bits.dcis() { | 517 | if bits.dcis() { Poll::Ready(()) } else { Poll::Pending } |
| 518 | Poll::Ready(()) | ||
| 519 | } else { | ||
| 520 | Poll::Pending | ||
| 521 | } | ||
| 522 | }) | 518 | }) |
| 523 | .await; | 519 | .await; |
| 524 | 520 | ||
diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index 6fece5eb2..6c6807479 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs | |||
| @@ -10,6 +10,7 @@ pub use traits::Instance; | |||
| 10 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 10 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
| 11 | use crate::rcc; | 11 | use crate::rcc; |
| 12 | use crate::time::Hertz; | 12 | use crate::time::Hertz; |
| 13 | pub use crate::timer::simple_pwm::PwmPinConfig; | ||
| 13 | 14 | ||
| 14 | /// HRTIM burst controller instance. | 15 | /// HRTIM burst controller instance. |
| 15 | pub struct BurstController<T: Instance> { | 16 | pub struct BurstController<T: Instance> { |
| @@ -73,7 +74,7 @@ pub struct ComplementaryPwmPin<'d, T, C> { | |||
| 73 | } | 74 | } |
| 74 | 75 | ||
| 75 | macro_rules! advanced_channel_impl { | 76 | macro_rules! advanced_channel_impl { |
| 76 | ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { | 77 | ($new_chx:ident, $new_chx_with_config:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { |
| 77 | impl<'d, T: Instance> PwmPin<'d, T, $channel<T>> { | 78 | impl<'d, T: Instance> PwmPin<'d, T, $channel<T>> { |
| 78 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] | 79 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] |
| 79 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self { | 80 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self { |
| @@ -86,6 +87,21 @@ macro_rules! advanced_channel_impl { | |||
| 86 | phantom: PhantomData, | 87 | phantom: PhantomData, |
| 87 | } | 88 | } |
| 88 | } | 89 | } |
| 90 | |||
| 91 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance with a specific configuration.")] | ||
| 92 | pub fn $new_chx_with_config( | ||
| 93 | pin: Peri<'d, impl $pin_trait<T>>, | ||
| 94 | pin_config: PwmPinConfig, | ||
| 95 | ) -> Self { | ||
| 96 | critical_section::with(|_| { | ||
| 97 | pin.set_low(); | ||
| 98 | set_as_af!(pin, AfType::output(pin_config.output_type, pin_config.speed)); | ||
| 99 | }); | ||
| 100 | PwmPin { | ||
| 101 | _pin: pin.into(), | ||
| 102 | phantom: PhantomData, | ||
| 103 | } | ||
| 104 | } | ||
| 89 | } | 105 | } |
| 90 | 106 | ||
| 91 | impl<'d, T: Instance> ComplementaryPwmPin<'d, T, $channel<T>> { | 107 | impl<'d, T: Instance> ComplementaryPwmPin<'d, T, $channel<T>> { |
| @@ -100,6 +116,21 @@ macro_rules! advanced_channel_impl { | |||
| 100 | phantom: PhantomData, | 116 | phantom: PhantomData, |
| 101 | } | 117 | } |
| 102 | } | 118 | } |
| 119 | |||
| 120 | #[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance with a specific configuration.")] | ||
| 121 | pub fn $new_chx_with_config( | ||
| 122 | pin: Peri<'d, impl $complementary_pin_trait<T>>, | ||
| 123 | pin_config: PwmPinConfig, | ||
| 124 | ) -> Self { | ||
| 125 | critical_section::with(|_| { | ||
| 126 | pin.set_low(); | ||
| 127 | set_as_af!(pin, AfType::output(pin_config.output_type, pin_config.speed)); | ||
| 128 | }); | ||
| 129 | ComplementaryPwmPin { | ||
| 130 | _pin: pin.into(), | ||
| 131 | phantom: PhantomData, | ||
| 132 | } | ||
| 133 | } | ||
| 103 | } | 134 | } |
| 104 | 135 | ||
| 105 | impl<T: Instance> SealedAdvancedChannel<T> for $channel<T> { | 136 | impl<T: Instance> SealedAdvancedChannel<T> for $channel<T> { |
| @@ -111,13 +142,55 @@ macro_rules! advanced_channel_impl { | |||
| 111 | }; | 142 | }; |
| 112 | } | 143 | } |
| 113 | 144 | ||
| 114 | advanced_channel_impl!(new_cha, ChA, 0, ChannelAPin, ChannelAComplementaryPin); | 145 | advanced_channel_impl!( |
| 115 | advanced_channel_impl!(new_chb, ChB, 1, ChannelBPin, ChannelBComplementaryPin); | 146 | new_cha, |
| 116 | advanced_channel_impl!(new_chc, ChC, 2, ChannelCPin, ChannelCComplementaryPin); | 147 | new_cha_with_config, |
| 117 | advanced_channel_impl!(new_chd, ChD, 3, ChannelDPin, ChannelDComplementaryPin); | 148 | ChA, |
| 118 | advanced_channel_impl!(new_che, ChE, 4, ChannelEPin, ChannelEComplementaryPin); | 149 | 0, |
| 150 | ChannelAPin, | ||
| 151 | ChannelAComplementaryPin | ||
| 152 | ); | ||
| 153 | advanced_channel_impl!( | ||
| 154 | new_chb, | ||
| 155 | new_chb_with_config, | ||
| 156 | ChB, | ||
| 157 | 1, | ||
| 158 | ChannelBPin, | ||
| 159 | ChannelBComplementaryPin | ||
| 160 | ); | ||
| 161 | advanced_channel_impl!( | ||
| 162 | new_chc, | ||
| 163 | new_chc_with_config, | ||
| 164 | ChC, | ||
| 165 | 2, | ||
| 166 | ChannelCPin, | ||
| 167 | ChannelCComplementaryPin | ||
| 168 | ); | ||
| 169 | advanced_channel_impl!( | ||
| 170 | new_chd, | ||
| 171 | new_chd_with_config, | ||
| 172 | ChD, | ||
| 173 | 3, | ||
| 174 | ChannelDPin, | ||
| 175 | ChannelDComplementaryPin | ||
| 176 | ); | ||
| 177 | advanced_channel_impl!( | ||
| 178 | new_che, | ||
| 179 | new_che_with_config, | ||
| 180 | ChE, | ||
| 181 | 4, | ||
| 182 | ChannelEPin, | ||
| 183 | ChannelEComplementaryPin | ||
| 184 | ); | ||
| 119 | #[cfg(hrtim_v2)] | 185 | #[cfg(hrtim_v2)] |
| 120 | advanced_channel_impl!(new_chf, ChF, 5, ChannelFPin, ChannelFComplementaryPin); | 186 | advanced_channel_impl!( |
| 187 | new_chf, | ||
| 188 | new_chf_with_config, | ||
| 189 | ChF, | ||
| 190 | 5, | ||
| 191 | ChannelFPin, | ||
| 192 | ChannelFComplementaryPin | ||
| 193 | ); | ||
| 121 | 194 | ||
| 122 | /// Struct used to divide a high resolution timer into multiple channels | 195 | /// Struct used to divide a high resolution timer into multiple channels |
| 123 | pub struct AdvancedPwm<'d, T: Instance> { | 196 | pub struct AdvancedPwm<'d, T: Instance> { |
diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs index 573a1851d..4d3a5d68d 100644 --- a/embassy-stm32/src/hsem/mod.rs +++ b/embassy-stm32/src/hsem/mod.rs | |||
| @@ -2,13 +2,13 @@ | |||
| 2 | 2 | ||
| 3 | use embassy_hal_internal::PeripheralType; | 3 | use embassy_hal_internal::PeripheralType; |
| 4 | 4 | ||
| 5 | use crate::pac; | ||
| 6 | use crate::rcc::{self, RccPeripheral}; | ||
| 7 | // TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. | 5 | // TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. |
| 8 | // Those MCUs have a different HSEM implementation (Secure semaphore lock support, | 6 | // Those MCUs have a different HSEM implementation (Secure semaphore lock support, |
| 9 | // Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), | 7 | // Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), |
| 10 | // which is not yet supported by this code. | 8 | // which is not yet supported by this code. |
| 11 | use crate::Peri; | 9 | use crate::Peri; |
| 10 | use crate::pac; | ||
| 11 | use crate::rcc::{self, RccPeripheral}; | ||
| 12 | 12 | ||
| 13 | /// HSEM error. | 13 | /// HSEM error. |
| 14 | #[derive(Debug)] | 14 | #[derive(Debug)] |
diff --git a/embassy-stm32/src/hspi/mod.rs b/embassy-stm32/src/hspi/mod.rs index 95d9e5099..69baa708e 100644 --- a/embassy-stm32/src/hspi/mod.rs +++ b/embassy-stm32/src/hspi/mod.rs | |||
| @@ -16,7 +16,7 @@ use embassy_embedded_hal::{GetConfig, SetConfig}; | |||
| 16 | use embassy_hal_internal::{Peri, PeripheralType}; | 16 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 17 | pub use enums::*; | 17 | pub use enums::*; |
| 18 | 18 | ||
| 19 | use crate::dma::{word, ChannelAndRequest}; | 19 | use crate::dma::{ChannelAndRequest, word}; |
| 20 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | 20 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
| 21 | use crate::mode::{Async, Blocking, Mode as PeriMode}; | 21 | use crate::mode::{Async, Blocking, Mode as PeriMode}; |
| 22 | use crate::pac::hspi::Hspi as Regs; | 22 | use crate::pac::hspi::Hspi as Regs; |
diff --git a/embassy-stm32/src/i2c/config.rs b/embassy-stm32/src/i2c/config.rs index 4e3b736c7..74fac14b2 100644 --- a/embassy-stm32/src/i2c/config.rs +++ b/embassy-stm32/src/i2c/config.rs | |||
| @@ -4,7 +4,7 @@ use crate::gpio::{AfType, OutputType, Speed}; | |||
| 4 | use crate::time::Hertz; | 4 | use crate::time::Hertz; |
| 5 | 5 | ||
| 6 | #[repr(u8)] | 6 | #[repr(u8)] |
| 7 | #[derive(Copy, Clone)] | 7 | #[derive(Debug, Copy, Clone)] |
| 8 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 8 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 9 | /// Bits of the I2C OA2 register to mask out. | 9 | /// Bits of the I2C OA2 register to mask out. |
| 10 | pub enum AddrMask { | 10 | pub enum AddrMask { |
| @@ -60,7 +60,7 @@ impl Address { | |||
| 60 | } | 60 | } |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | #[derive(Copy, Clone)] | 63 | #[derive(Debug, Copy, Clone)] |
| 64 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 64 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 65 | /// The second Own Address register. | 65 | /// The second Own Address register. |
| 66 | pub struct OA2 { | 66 | pub struct OA2 { |
| @@ -70,7 +70,7 @@ pub struct OA2 { | |||
| 70 | pub mask: AddrMask, | 70 | pub mask: AddrMask, |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | #[derive(Copy, Clone)] | 73 | #[derive(Debug, Copy, Clone)] |
| 74 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 74 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 75 | /// The Own Address(es) of the I2C peripheral. | 75 | /// The Own Address(es) of the I2C peripheral. |
| 76 | pub enum OwnAddresses { | 76 | pub enum OwnAddresses { |
| @@ -88,7 +88,7 @@ pub enum OwnAddresses { | |||
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | /// Slave Configuration | 90 | /// Slave Configuration |
| 91 | #[derive(Copy, Clone)] | 91 | #[derive(Debug, Copy, Clone)] |
| 92 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 92 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 93 | pub struct SlaveAddrConfig { | 93 | pub struct SlaveAddrConfig { |
| 94 | /// Target Address(es) | 94 | /// Target Address(es) |
diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 249bac41c..ee60c3f44 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs | |||
| @@ -154,8 +154,8 @@ impl<'d> I2c<'d, Async, Master> { | |||
| 154 | scl: Peri<'d, if_afio!(impl SclPin<T, A>)>, | 154 | scl: Peri<'d, if_afio!(impl SclPin<T, A>)>, |
| 155 | sda: Peri<'d, if_afio!(impl SdaPin<T, A>)>, | 155 | sda: Peri<'d, if_afio!(impl SdaPin<T, A>)>, |
| 156 | _irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>> | 156 | _irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>> |
| 157 | + interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>> | 157 | + interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>> |
| 158 | + 'd, | 158 | + 'd, |
| 159 | tx_dma: Peri<'d, impl TxDma<T>>, | 159 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 160 | rx_dma: Peri<'d, impl RxDma<T>>, | 160 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 161 | config: Config, | 161 | config: Config, |
| @@ -219,6 +219,7 @@ impl<'d, M: Mode> I2c<'d, M, Master> { | |||
| 219 | sda, | 219 | sda, |
| 220 | }, | 220 | }, |
| 221 | }; | 221 | }; |
| 222 | |||
| 222 | this.enable_and_init(config); | 223 | this.enable_and_init(config); |
| 223 | 224 | ||
| 224 | this | 225 | this |
| @@ -437,15 +438,15 @@ impl<'d, IM: MasterMode> embedded_hal_async::i2c::I2c for I2c<'d, Async, IM> { | |||
| 437 | 438 | ||
| 438 | /// Frame type in I2C transaction. | 439 | /// Frame type in I2C transaction. |
| 439 | /// | 440 | /// |
| 440 | /// This tells each method what kind of framing to use, to generate a (repeated) start condition (ST | 441 | /// This tells each method what kind of frame to use, to generate a (repeated) start condition (ST |
| 441 | /// or SR), and/or a stop condition (SP). For read operations, this also controls whether to send an | 442 | /// or SR), and/or a stop condition (SP). For read operations, this also controls whether to send an |
| 442 | /// ACK or NACK after the last byte received. | 443 | /// ACK or NACK after the last byte received. |
| 443 | /// | 444 | /// |
| 444 | /// For write operations, the following options are identical because they differ only in the (N)ACK | 445 | /// For write operations, the following options are identical because they differ only in the (N)ACK |
| 445 | /// treatment relevant for read operations: | 446 | /// treatment relevant for read operations: |
| 446 | /// | 447 | /// |
| 447 | /// - `FirstFrame` and `FirstAndNextFrame` | 448 | /// - `FirstFrame` and `FirstAndNextFrame` behave identically for writes |
| 448 | /// - `NextFrame` and `LastFrameNoStop` | 449 | /// - `NextFrame` and `LastFrameNoStop` behave identically for writes |
| 449 | /// | 450 | /// |
| 450 | /// Abbreviations used below: | 451 | /// Abbreviations used below: |
| 451 | /// | 452 | /// |
| @@ -474,7 +475,7 @@ enum FrameOptions { | |||
| 474 | 475 | ||
| 475 | #[allow(dead_code)] | 476 | #[allow(dead_code)] |
| 476 | impl FrameOptions { | 477 | impl FrameOptions { |
| 477 | /// Sends start or repeated start condition before transfer. | 478 | /// Returns true if a start or repeated start condition should be generated before this operation. |
| 478 | fn send_start(self) -> bool { | 479 | fn send_start(self) -> bool { |
| 479 | match self { | 480 | match self { |
| 480 | Self::FirstAndLastFrame | Self::FirstFrame | Self::FirstAndNextFrame => true, | 481 | Self::FirstAndLastFrame | Self::FirstFrame | Self::FirstAndNextFrame => true, |
| @@ -482,7 +483,7 @@ impl FrameOptions { | |||
| 482 | } | 483 | } |
| 483 | } | 484 | } |
| 484 | 485 | ||
| 485 | /// Sends stop condition after transfer. | 486 | /// Returns true if a stop condition should be generated after this operation. |
| 486 | fn send_stop(self) -> bool { | 487 | fn send_stop(self) -> bool { |
| 487 | match self { | 488 | match self { |
| 488 | Self::FirstAndLastFrame | Self::LastFrame => true, | 489 | Self::FirstAndLastFrame | Self::LastFrame => true, |
| @@ -490,7 +491,10 @@ impl FrameOptions { | |||
| 490 | } | 491 | } |
| 491 | } | 492 | } |
| 492 | 493 | ||
| 493 | /// Sends NACK after last byte received, indicating end of read operation. | 494 | /// Returns true if NACK should be sent after the last byte received in a read operation. |
| 495 | /// | ||
| 496 | /// This signals the end of a read sequence and releases the bus for the master's | ||
| 497 | /// next transmission (or stop condition). | ||
| 494 | fn send_nack(self) -> bool { | 498 | fn send_nack(self) -> bool { |
| 495 | match self { | 499 | match self { |
| 496 | Self::FirstAndLastFrame | Self::FirstFrame | Self::LastFrame | Self::LastFrameNoStop => true, | 500 | Self::FirstAndLastFrame | Self::FirstFrame | Self::LastFrame | Self::LastFrameNoStop => true, |
| @@ -499,24 +503,44 @@ impl FrameOptions { | |||
| 499 | } | 503 | } |
| 500 | } | 504 | } |
| 501 | 505 | ||
| 502 | /// Iterates over operations in transaction. | 506 | /// Analyzes I2C transaction operations and assigns appropriate frame to each. |
| 507 | /// | ||
| 508 | /// This function processes a sequence of I2C operations and determines the correct | ||
| 509 | /// frame configuration for each operation to ensure proper I2C protocol compliance. | ||
| 510 | /// It handles the complex logic of: | ||
| 511 | /// | ||
| 512 | /// - Generating start conditions for the first operation of each type (read/write) | ||
| 513 | /// - Generating stop conditions for the final operation in the entire transaction | ||
| 514 | /// - Managing ACK/NACK behavior for read operations, including merging consecutive reads | ||
| 515 | /// - Ensuring proper bus handoff between different operation types | ||
| 516 | /// | ||
| 517 | /// **Transaction Contract Compliance:** | ||
| 518 | /// The frame assignments ensure compliance with the embedded-hal I2C transaction contract, | ||
| 519 | /// where consecutive operations of the same type are logically merged while maintaining | ||
| 520 | /// proper protocol boundaries. | ||
| 503 | /// | 521 | /// |
| 504 | /// Returns necessary frame options for each operation to uphold the [transaction contract] and have | 522 | /// **Error Handling:** |
| 505 | /// the right start/stop/(N)ACK conditions on the wire. | 523 | /// Returns an error if any read operation has an empty buffer, as this would create |
| 524 | /// an invalid I2C transaction that could halt mid-execution. | ||
| 525 | /// | ||
| 526 | /// # Arguments | ||
| 527 | /// * `operations` - Mutable slice of I2C operations from embedded-hal | ||
| 528 | /// | ||
| 529 | /// # Returns | ||
| 530 | /// An iterator over (operation, frame) pairs, or an error if the transaction is invalid | ||
| 506 | /// | 531 | /// |
| 507 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | ||
| 508 | #[allow(dead_code)] | 532 | #[allow(dead_code)] |
| 509 | fn operation_frames<'a, 'b: 'a>( | 533 | fn operation_frames<'a, 'b: 'a>( |
| 510 | operations: &'a mut [embedded_hal_1::i2c::Operation<'b>], | 534 | operations: &'a mut [embedded_hal_1::i2c::Operation<'b>], |
| 511 | ) -> Result<impl IntoIterator<Item = (&'a mut embedded_hal_1::i2c::Operation<'b>, FrameOptions)>, Error> { | 535 | ) -> Result<impl IntoIterator<Item = (&'a mut embedded_hal_1::i2c::Operation<'b>, FrameOptions)>, Error> { |
| 512 | use embedded_hal_1::i2c::Operation::{Read, Write}; | 536 | use embedded_hal_1::i2c::Operation::{Read, Write}; |
| 513 | 537 | ||
| 514 | // Check empty read buffer before starting transaction. Otherwise, we would risk halting with an | 538 | // Validate that no read operations have empty buffers before starting the transaction. |
| 515 | // error in the middle of the transaction. | 539 | // Empty read operations would risk halting with an error mid-transaction. |
| 516 | // | 540 | // |
| 517 | // In principle, we could allow empty read frames within consecutive read operations, as long as | 541 | // Note: We could theoretically allow empty read operations within consecutive read |
| 518 | // at least one byte remains in the final (merged) read operation, but that makes the logic more | 542 | // sequences as long as the final merged read has at least one byte, but this would |
| 519 | // complicated and error-prone. | 543 | // complicate the logic significantly and create error-prone edge cases. |
| 520 | if operations.iter().any(|op| match op { | 544 | if operations.iter().any(|op| match op { |
| 521 | Read(read) => read.is_empty(), | 545 | Read(read) => read.is_empty(), |
| 522 | Write(_) => false, | 546 | Write(_) => false, |
| @@ -525,46 +549,52 @@ fn operation_frames<'a, 'b: 'a>( | |||
| 525 | } | 549 | } |
| 526 | 550 | ||
| 527 | let mut operations = operations.iter_mut().peekable(); | 551 | let mut operations = operations.iter_mut().peekable(); |
| 528 | 552 | let mut next_first_operation = true; | |
| 529 | let mut next_first_frame = true; | ||
| 530 | 553 | ||
| 531 | Ok(iter::from_fn(move || { | 554 | Ok(iter::from_fn(move || { |
| 532 | let op = operations.next()?; | 555 | let current_op = operations.next()?; |
| 533 | 556 | ||
| 534 | // Is `op` first frame of its type? | 557 | // Determine if this is the first operation of its type (read or write) |
| 535 | let first_frame = next_first_frame; | 558 | let is_first_of_type = next_first_operation; |
| 536 | let next_op = operations.peek(); | 559 | let next_op = operations.peek(); |
| 537 | 560 | ||
| 538 | // Get appropriate frame options as combination of the following properties: | 561 | // Compute the appropriate frame based on three key properties: |
| 539 | // | 562 | // |
| 540 | // - For each first operation of its type, generate a (repeated) start condition. | 563 | // 1. **Start Condition**: Generate (repeated) start for first operation of each type |
| 541 | // - For the last operation overall in the entire transaction, generate a stop condition. | 564 | // 2. **Stop Condition**: Generate stop for the final operation in the entire transaction |
| 542 | // - For read operations, check the next operation: if it is also a read operation, we merge | 565 | // 3. **ACK/NACK for Reads**: For read operations, send ACK if more reads follow in the |
| 543 | // these and send ACK for all bytes in the current operation; send NACK only for the final | 566 | // sequence, or NACK for the final read in a sequence (before write or transaction end) |
| 544 | // read operation's last byte (before write or end of entire transaction) to indicate last | ||
| 545 | // byte read and release the bus for transmission of the bus master's next byte (or stop). | ||
| 546 | // | 567 | // |
| 547 | // We check the third property unconditionally, i.e. even for write opeartions. This is okay | 568 | // The third property is checked for all operations since the resulting frame |
| 548 | // because the resulting frame options are identical for write operations. | 569 | // configurations are identical for write operations regardless of ACK/NACK treatment. |
| 549 | let frame = match (first_frame, next_op) { | 570 | let frame = match (is_first_of_type, next_op) { |
| 571 | // First operation of type, and it's also the final operation overall | ||
| 550 | (true, None) => FrameOptions::FirstAndLastFrame, | 572 | (true, None) => FrameOptions::FirstAndLastFrame, |
| 573 | // First operation of type, next operation is also a read (continue read sequence) | ||
| 551 | (true, Some(Read(_))) => FrameOptions::FirstAndNextFrame, | 574 | (true, Some(Read(_))) => FrameOptions::FirstAndNextFrame, |
| 575 | // First operation of type, next operation is write (end current sequence) | ||
| 552 | (true, Some(Write(_))) => FrameOptions::FirstFrame, | 576 | (true, Some(Write(_))) => FrameOptions::FirstFrame, |
| 553 | // | 577 | |
| 578 | // Continuation operation, and it's the final operation overall | ||
| 554 | (false, None) => FrameOptions::LastFrame, | 579 | (false, None) => FrameOptions::LastFrame, |
| 580 | // Continuation operation, next operation is also a read (continue read sequence) | ||
| 555 | (false, Some(Read(_))) => FrameOptions::NextFrame, | 581 | (false, Some(Read(_))) => FrameOptions::NextFrame, |
| 582 | // Continuation operation, next operation is write (end current sequence, no stop) | ||
| 556 | (false, Some(Write(_))) => FrameOptions::LastFrameNoStop, | 583 | (false, Some(Write(_))) => FrameOptions::LastFrameNoStop, |
| 557 | }; | 584 | }; |
| 558 | 585 | ||
| 559 | // Pre-calculate if `next_op` is the first operation of its type. We do this here and not at | 586 | // Pre-calculate whether the next operation will be the first of its type. |
| 560 | // the beginning of the loop because we hand out `op` as iterator value and cannot access it | 587 | // This is done here because we consume `current_op` as the iterator value |
| 561 | // anymore in the next iteration. | 588 | // and cannot access it in the next iteration. |
| 562 | next_first_frame = match (&op, next_op) { | 589 | next_first_operation = match (¤t_op, next_op) { |
| 590 | // No next operation | ||
| 563 | (_, None) => false, | 591 | (_, None) => false, |
| 592 | // Operation type changes: next will be first of its type | ||
| 564 | (Read(_), Some(Write(_))) | (Write(_), Some(Read(_))) => true, | 593 | (Read(_), Some(Write(_))) | (Write(_), Some(Read(_))) => true, |
| 594 | // Operation type continues: next will not be first of its type | ||
| 565 | (Read(_), Some(Read(_))) | (Write(_), Some(Write(_))) => false, | 595 | (Read(_), Some(Read(_))) | (Write(_), Some(Write(_))) => false, |
| 566 | }; | 596 | }; |
| 567 | 597 | ||
| 568 | Some((op, frame)) | 598 | Some((current_op, frame)) |
| 569 | })) | 599 | })) |
| 570 | } | 600 | } |
diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 081eb1191..128a58db7 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs | |||
| @@ -8,7 +8,7 @@ use core::future::poll_fn; | |||
| 8 | use core::task::Poll; | 8 | use core::task::Poll; |
| 9 | 9 | ||
| 10 | use embassy_embedded_hal::SetConfig; | 10 | use embassy_embedded_hal::SetConfig; |
| 11 | use embassy_futures::select::{select, Either}; | 11 | use embassy_futures::select::{Either, select}; |
| 12 | use embassy_hal_internal::drop::OnDrop; | 12 | use embassy_hal_internal::drop::OnDrop; |
| 13 | use embedded_hal_1::i2c::Operation; | 13 | use embedded_hal_1::i2c::Operation; |
| 14 | use mode::Master; | 14 | use mode::Master; |
| @@ -30,6 +30,7 @@ use crate::pac::i2c; | |||
| 30 | // hit a case like this! | 30 | // hit a case like this! |
| 31 | pub unsafe fn on_interrupt<T: Instance>() { | 31 | pub unsafe fn on_interrupt<T: Instance>() { |
| 32 | let regs = T::info().regs; | 32 | let regs = T::info().regs; |
| 33 | trace!("I2C interrupt triggered"); | ||
| 33 | // i2c v2 only woke the task on transfer complete interrupts. v1 uses interrupts for a bunch of | 34 | // i2c v2 only woke the task on transfer complete interrupts. v1 uses interrupts for a bunch of |
| 34 | // other stuff, so we wake the task on every interrupt. | 35 | // other stuff, so we wake the task on every interrupt. |
| 35 | T::state().waker.wake(); | 36 | T::state().waker.wake(); |
| @@ -92,6 +93,7 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 92 | self.info.regs.cr1().modify(|reg| { | 93 | self.info.regs.cr1().modify(|reg| { |
| 93 | reg.set_pe(true); | 94 | reg.set_pe(true); |
| 94 | }); | 95 | }); |
| 96 | trace!("i2c v1 init complete"); | ||
| 95 | } | 97 | } |
| 96 | 98 | ||
| 97 | fn check_and_clear_error_flags(info: &'static Info) -> Result<i2c::regs::Sr1, Error> { | 99 | fn check_and_clear_error_flags(info: &'static Info) -> Result<i2c::regs::Sr1, Error> { |
| @@ -151,7 +153,13 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 151 | Ok(sr1) | 153 | Ok(sr1) |
| 152 | } | 154 | } |
| 153 | 155 | ||
| 154 | fn write_bytes(&mut self, addr: u8, bytes: &[u8], timeout: Timeout, frame: FrameOptions) -> Result<(), Error> { | 156 | fn write_bytes( |
| 157 | &mut self, | ||
| 158 | address: u8, | ||
| 159 | write_buffer: &[u8], | ||
| 160 | timeout: Timeout, | ||
| 161 | frame: FrameOptions, | ||
| 162 | ) -> Result<(), Error> { | ||
| 155 | if frame.send_start() { | 163 | if frame.send_start() { |
| 156 | // Send a START condition | 164 | // Send a START condition |
| 157 | 165 | ||
| @@ -170,7 +178,7 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 170 | } | 178 | } |
| 171 | 179 | ||
| 172 | // Set up current address we're trying to talk to | 180 | // Set up current address we're trying to talk to |
| 173 | self.info.regs.dr().write(|reg| reg.set_dr(addr << 1)); | 181 | self.info.regs.dr().write(|reg| reg.set_dr(address << 1)); |
| 174 | 182 | ||
| 175 | // Wait until address was sent | 183 | // Wait until address was sent |
| 176 | // Wait for the address to be acknowledged | 184 | // Wait for the address to be acknowledged |
| @@ -184,7 +192,7 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 184 | } | 192 | } |
| 185 | 193 | ||
| 186 | // Send bytes | 194 | // Send bytes |
| 187 | for c in bytes { | 195 | for c in write_buffer { |
| 188 | self.send_byte(*c, timeout)?; | 196 | self.send_byte(*c, timeout)?; |
| 189 | } | 197 | } |
| 190 | 198 | ||
| @@ -236,12 +244,12 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 236 | 244 | ||
| 237 | fn blocking_read_timeout( | 245 | fn blocking_read_timeout( |
| 238 | &mut self, | 246 | &mut self, |
| 239 | addr: u8, | 247 | address: u8, |
| 240 | buffer: &mut [u8], | 248 | read_buffer: &mut [u8], |
| 241 | timeout: Timeout, | 249 | timeout: Timeout, |
| 242 | frame: FrameOptions, | 250 | frame: FrameOptions, |
| 243 | ) -> Result<(), Error> { | 251 | ) -> Result<(), Error> { |
| 244 | let Some((last, buffer)) = buffer.split_last_mut() else { | 252 | let Some((last_byte, read_buffer)) = read_buffer.split_last_mut() else { |
| 245 | return Err(Error::Overrun); | 253 | return Err(Error::Overrun); |
| 246 | }; | 254 | }; |
| 247 | 255 | ||
| @@ -263,7 +271,7 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 263 | } | 271 | } |
| 264 | 272 | ||
| 265 | // Set up current address we're trying to talk to | 273 | // Set up current address we're trying to talk to |
| 266 | self.info.regs.dr().write(|reg| reg.set_dr((addr << 1) + 1)); | 274 | self.info.regs.dr().write(|reg| reg.set_dr((address << 1) + 1)); |
| 267 | 275 | ||
| 268 | // Wait until address was sent | 276 | // Wait until address was sent |
| 269 | // Wait for the address to be acknowledged | 277 | // Wait for the address to be acknowledged |
| @@ -276,7 +284,7 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 276 | } | 284 | } |
| 277 | 285 | ||
| 278 | // Receive bytes into buffer | 286 | // Receive bytes into buffer |
| 279 | for c in buffer { | 287 | for c in read_buffer { |
| 280 | *c = self.recv_byte(timeout)?; | 288 | *c = self.recv_byte(timeout)?; |
| 281 | } | 289 | } |
| 282 | 290 | ||
| @@ -291,37 +299,42 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 291 | }); | 299 | }); |
| 292 | 300 | ||
| 293 | // Receive last byte | 301 | // Receive last byte |
| 294 | *last = self.recv_byte(timeout)?; | 302 | *last_byte = self.recv_byte(timeout)?; |
| 295 | 303 | ||
| 296 | // Fallthrough is success | 304 | // Fallthrough is success |
| 297 | Ok(()) | 305 | Ok(()) |
| 298 | } | 306 | } |
| 299 | 307 | ||
| 300 | /// Blocking read. | 308 | /// Blocking read. |
| 301 | pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> { | 309 | pub fn blocking_read(&mut self, address: u8, read_buffer: &mut [u8]) -> Result<(), Error> { |
| 302 | self.blocking_read_timeout(addr, read, self.timeout(), FrameOptions::FirstAndLastFrame) | 310 | self.blocking_read_timeout(address, read_buffer, self.timeout(), FrameOptions::FirstAndLastFrame) |
| 303 | } | 311 | } |
| 304 | 312 | ||
| 305 | /// Blocking write. | 313 | /// Blocking write. |
| 306 | pub fn blocking_write(&mut self, addr: u8, write: &[u8]) -> Result<(), Error> { | 314 | pub fn blocking_write(&mut self, address: u8, write_buffer: &[u8]) -> Result<(), Error> { |
| 307 | self.write_bytes(addr, write, self.timeout(), FrameOptions::FirstAndLastFrame)?; | 315 | self.write_bytes(address, write_buffer, self.timeout(), FrameOptions::FirstAndLastFrame)?; |
| 308 | 316 | ||
| 309 | // Fallthrough is success | 317 | // Fallthrough is success |
| 310 | Ok(()) | 318 | Ok(()) |
| 311 | } | 319 | } |
| 312 | 320 | ||
| 313 | /// Blocking write, restart, read. | 321 | /// Blocking write, restart, read. |
| 314 | pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { | 322 | pub fn blocking_write_read( |
| 323 | &mut self, | ||
| 324 | address: u8, | ||
| 325 | write_buffer: &[u8], | ||
| 326 | read_buffer: &mut [u8], | ||
| 327 | ) -> Result<(), Error> { | ||
| 315 | // Check empty read buffer before starting transaction. Otherwise, we would not generate the | 328 | // Check empty read buffer before starting transaction. Otherwise, we would not generate the |
| 316 | // stop condition below. | 329 | // stop condition below. |
| 317 | if read.is_empty() { | 330 | if read_buffer.is_empty() { |
| 318 | return Err(Error::Overrun); | 331 | return Err(Error::Overrun); |
| 319 | } | 332 | } |
| 320 | 333 | ||
| 321 | let timeout = self.timeout(); | 334 | let timeout = self.timeout(); |
| 322 | 335 | ||
| 323 | self.write_bytes(addr, write, timeout, FrameOptions::FirstFrame)?; | 336 | self.write_bytes(address, write_buffer, timeout, FrameOptions::FirstFrame)?; |
| 324 | self.blocking_read_timeout(addr, read, timeout, FrameOptions::FirstAndLastFrame)?; | 337 | self.blocking_read_timeout(address, read_buffer, timeout, FrameOptions::FirstAndLastFrame)?; |
| 325 | 338 | ||
| 326 | Ok(()) | 339 | Ok(()) |
| 327 | } | 340 | } |
| @@ -331,32 +344,43 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 331 | /// Consecutive operations of same type are merged. See [transaction contract] for details. | 344 | /// Consecutive operations of same type are merged. See [transaction contract] for details. |
| 332 | /// | 345 | /// |
| 333 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | 346 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction |
| 334 | pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | 347 | pub fn blocking_transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { |
| 335 | let timeout = self.timeout(); | 348 | let timeout = self.timeout(); |
| 336 | 349 | ||
| 337 | for (op, frame) in operation_frames(operations)? { | 350 | for (op, frame) in operation_frames(operations)? { |
| 338 | match op { | 351 | match op { |
| 339 | Operation::Read(read) => self.blocking_read_timeout(addr, read, timeout, frame)?, | 352 | Operation::Read(read_buffer) => self.blocking_read_timeout(address, read_buffer, timeout, frame)?, |
| 340 | Operation::Write(write) => self.write_bytes(addr, write, timeout, frame)?, | 353 | Operation::Write(write_buffer) => self.write_bytes(address, write_buffer, timeout, frame)?, |
| 341 | } | 354 | } |
| 342 | } | 355 | } |
| 343 | 356 | ||
| 344 | Ok(()) | 357 | Ok(()) |
| 345 | } | 358 | } |
| 346 | 359 | ||
| 347 | // Async | 360 | /// Can be used by both blocking and async implementations |
| 348 | |||
| 349 | #[inline] // pretty sure this should always be inlined | 361 | #[inline] // pretty sure this should always be inlined |
| 350 | fn enable_interrupts(info: &'static Info) -> () { | 362 | fn enable_interrupts(info: &'static Info) { |
| 351 | info.regs.cr2().modify(|w| { | 363 | // The interrupt handler disables interrupts globally, so we need to re-enable them |
| 352 | w.set_iterren(true); | 364 | // This must be done in a critical section to avoid races |
| 353 | w.set_itevten(true); | 365 | critical_section::with(|_| { |
| 366 | info.regs.cr2().modify(|w| { | ||
| 367 | w.set_iterren(true); | ||
| 368 | w.set_itevten(true); | ||
| 369 | }); | ||
| 354 | }); | 370 | }); |
| 355 | } | 371 | } |
| 372 | |||
| 373 | /// Can be used by both blocking and async implementations | ||
| 374 | fn clear_stop_flag(info: &'static Info) { | ||
| 375 | trace!("I2C slave: clearing STOPF flag (v1 sequence)"); | ||
| 376 | // v1 requires: READ SR1 then WRITE CR1 to clear STOPF | ||
| 377 | let _ = info.regs.sr1().read(); | ||
| 378 | info.regs.cr1().modify(|_| {}); // Dummy write to clear STOPF | ||
| 379 | } | ||
| 356 | } | 380 | } |
| 357 | 381 | ||
| 358 | impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | 382 | impl<'d, IM: MasterMode> I2c<'d, Async, IM> { |
| 359 | async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> { | 383 | async fn write_frame(&mut self, address: u8, write_buffer: &[u8], frame: FrameOptions) -> Result<(), Error> { |
| 360 | self.info.regs.cr2().modify(|w| { | 384 | self.info.regs.cr2().modify(|w| { |
| 361 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for | 385 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for |
| 362 | // reception. | 386 | // reception. |
| @@ -439,7 +463,10 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 439 | // this address from the memory after each TxE event. | 463 | // this address from the memory after each TxE event. |
| 440 | let dst = self.info.regs.dr().as_ptr() as *mut u8; | 464 | let dst = self.info.regs.dr().as_ptr() as *mut u8; |
| 441 | 465 | ||
| 442 | self.tx_dma.as_mut().unwrap().write(write, dst, Default::default()) | 466 | self.tx_dma |
| 467 | .as_mut() | ||
| 468 | .unwrap() | ||
| 469 | .write(write_buffer, dst, Default::default()) | ||
| 443 | }; | 470 | }; |
| 444 | 471 | ||
| 445 | // Wait for bytes to be sent, or an error to occur. | 472 | // Wait for bytes to be sent, or an error to occur. |
| @@ -501,28 +528,28 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 501 | } | 528 | } |
| 502 | 529 | ||
| 503 | /// Write. | 530 | /// Write. |
| 504 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { | 531 | pub async fn write(&mut self, address: u8, write_buffer: &[u8]) -> Result<(), Error> { |
| 505 | self.write_frame(address, write, FrameOptions::FirstAndLastFrame) | 532 | self.write_frame(address, write_buffer, FrameOptions::FirstAndLastFrame) |
| 506 | .await?; | 533 | .await?; |
| 507 | 534 | ||
| 508 | Ok(()) | 535 | Ok(()) |
| 509 | } | 536 | } |
| 510 | 537 | ||
| 511 | /// Read. | 538 | /// Read. |
| 512 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { | 539 | pub async fn read(&mut self, address: u8, read_buffer: &mut [u8]) -> Result<(), Error> { |
| 513 | self.read_frame(address, buffer, FrameOptions::FirstAndLastFrame) | 540 | self.read_frame(address, read_buffer, FrameOptions::FirstAndLastFrame) |
| 514 | .await?; | 541 | .await?; |
| 515 | 542 | ||
| 516 | Ok(()) | 543 | Ok(()) |
| 517 | } | 544 | } |
| 518 | 545 | ||
| 519 | async fn read_frame(&mut self, address: u8, buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error> { | 546 | async fn read_frame(&mut self, address: u8, read_buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error> { |
| 520 | if buffer.is_empty() { | 547 | if read_buffer.is_empty() { |
| 521 | return Err(Error::Overrun); | 548 | return Err(Error::Overrun); |
| 522 | } | 549 | } |
| 523 | 550 | ||
| 524 | // Some branches below depend on whether the buffer contains only a single byte. | 551 | // Some branches below depend on whether the buffer contains only a single byte. |
| 525 | let single_byte = buffer.len() == 1; | 552 | let single_byte = read_buffer.len() == 1; |
| 526 | 553 | ||
| 527 | self.info.regs.cr2().modify(|w| { | 554 | self.info.regs.cr2().modify(|w| { |
| 528 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for | 555 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for |
| @@ -612,7 +639,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 612 | self.info.regs.sr2().read(); | 639 | self.info.regs.sr2().read(); |
| 613 | } else { | 640 | } else { |
| 614 | // Before starting reception of single byte (but without START condition, i.e. in case | 641 | // Before starting reception of single byte (but without START condition, i.e. in case |
| 615 | // of continued frame), program NACK to emit at end of this byte. | 642 | // of merged operations), program NACK to emit at end of this byte. |
| 616 | if frame.send_nack() && single_byte { | 643 | if frame.send_nack() && single_byte { |
| 617 | self.info.regs.cr1().modify(|w| { | 644 | self.info.regs.cr1().modify(|w| { |
| 618 | w.set_ack(false); | 645 | w.set_ack(false); |
| @@ -634,7 +661,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 634 | // from this address from the memory after each RxE event. | 661 | // from this address from the memory after each RxE event. |
| 635 | let src = self.info.regs.dr().as_ptr() as *mut u8; | 662 | let src = self.info.regs.dr().as_ptr() as *mut u8; |
| 636 | 663 | ||
| 637 | self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) | 664 | self.rx_dma.as_mut().unwrap().read(src, read_buffer, Default::default()) |
| 638 | }; | 665 | }; |
| 639 | 666 | ||
| 640 | // Wait for bytes to be received, or an error to occur. | 667 | // Wait for bytes to be received, or an error to occur. |
| @@ -673,15 +700,17 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 673 | } | 700 | } |
| 674 | 701 | ||
| 675 | /// Write, restart, read. | 702 | /// Write, restart, read. |
| 676 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { | 703 | pub async fn write_read(&mut self, address: u8, write_buffer: &[u8], read_buffer: &mut [u8]) -> Result<(), Error> { |
| 677 | // Check empty read buffer before starting transaction. Otherwise, we would not generate the | 704 | // Check empty read buffer before starting transaction. Otherwise, we would not generate the |
| 678 | // stop condition below. | 705 | // stop condition below. |
| 679 | if read.is_empty() { | 706 | if read_buffer.is_empty() { |
| 680 | return Err(Error::Overrun); | 707 | return Err(Error::Overrun); |
| 681 | } | 708 | } |
| 682 | 709 | ||
| 683 | self.write_frame(address, write, FrameOptions::FirstFrame).await?; | 710 | self.write_frame(address, write_buffer, FrameOptions::FirstFrame) |
| 684 | self.read_frame(address, read, FrameOptions::FirstAndLastFrame).await | 711 | .await?; |
| 712 | self.read_frame(address, read_buffer, FrameOptions::FirstAndLastFrame) | ||
| 713 | .await | ||
| 685 | } | 714 | } |
| 686 | 715 | ||
| 687 | /// Transaction with operations. | 716 | /// Transaction with operations. |
| @@ -689,11 +718,11 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 689 | /// Consecutive operations of same type are merged. See [transaction contract] for details. | 718 | /// Consecutive operations of same type are merged. See [transaction contract] for details. |
| 690 | /// | 719 | /// |
| 691 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | 720 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction |
| 692 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | 721 | pub async fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { |
| 693 | for (op, frame) in operation_frames(operations)? { | 722 | for (op, frame) in operation_frames(operations)? { |
| 694 | match op { | 723 | match op { |
| 695 | Operation::Read(read) => self.read_frame(addr, read, frame).await?, | 724 | Operation::Read(read_buffer) => self.read_frame(address, read_buffer, frame).await?, |
| 696 | Operation::Write(write) => self.write_frame(addr, write, frame).await?, | 725 | Operation::Write(write_buffer) => self.write_frame(address, write_buffer, frame).await?, |
| 697 | } | 726 | } |
| 698 | } | 727 | } |
| 699 | 728 | ||
| @@ -729,12 +758,956 @@ impl Duty { | |||
| 729 | } | 758 | } |
| 730 | } | 759 | } |
| 731 | 760 | ||
| 761 | /// Result of attempting to send a byte in slave transmitter mode | ||
| 762 | #[derive(Debug, PartialEq)] | ||
| 763 | enum TransmitResult { | ||
| 764 | /// Byte sent and ACKed by master - continue transmission | ||
| 765 | Acknowledged, | ||
| 766 | /// Byte sent but NACKed by master - normal end of read transaction | ||
| 767 | NotAcknowledged, | ||
| 768 | /// STOP condition detected - master terminated transaction | ||
| 769 | Stopped, | ||
| 770 | /// RESTART condition detected - master starting new transaction | ||
| 771 | Restarted, | ||
| 772 | } | ||
| 773 | |||
| 774 | /// Result of attempting to receive a byte in slave receiver mode | ||
| 775 | #[derive(Debug, PartialEq)] | ||
| 776 | enum ReceiveResult { | ||
| 777 | /// Data byte successfully received | ||
| 778 | Data(u8), | ||
| 779 | /// STOP condition detected - end of write transaction | ||
| 780 | Stopped, | ||
| 781 | /// RESTART condition detected - master starting new transaction | ||
| 782 | Restarted, | ||
| 783 | } | ||
| 784 | |||
| 785 | /// Enumeration of slave transaction termination conditions | ||
| 786 | #[derive(Debug, Clone, Copy, PartialEq)] | ||
| 787 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 788 | enum SlaveTermination { | ||
| 789 | /// STOP condition received - normal end of transaction | ||
| 790 | Stop, | ||
| 791 | /// RESTART condition received - master starting new transaction | ||
| 792 | Restart, | ||
| 793 | /// NACK received - normal end of read transaction | ||
| 794 | Nack, | ||
| 795 | } | ||
| 796 | |||
| 797 | impl<'d, M: PeriMode> I2c<'d, M, Master> { | ||
| 798 | /// Configure the I2C driver for slave operations, allowing for the driver to be used as a slave and a master (multimaster) | ||
| 799 | pub fn into_slave_multimaster(mut self, slave_addr_config: SlaveAddrConfig) -> I2c<'d, M, MultiMaster> { | ||
| 800 | let mut slave = I2c { | ||
| 801 | info: self.info, | ||
| 802 | state: self.state, | ||
| 803 | kernel_clock: self.kernel_clock, | ||
| 804 | tx_dma: self.tx_dma.take(), // Use take() to move ownership | ||
| 805 | rx_dma: self.rx_dma.take(), // Use take() to move ownership | ||
| 806 | #[cfg(feature = "time")] | ||
| 807 | timeout: self.timeout, | ||
| 808 | _phantom: PhantomData, | ||
| 809 | _phantom2: PhantomData, | ||
| 810 | _drop_guard: self._drop_guard, // Move the drop guard | ||
| 811 | }; | ||
| 812 | slave.init_slave(slave_addr_config); | ||
| 813 | slave | ||
| 814 | } | ||
| 815 | } | ||
| 816 | |||
| 817 | // Address configuration methods | ||
| 818 | impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | ||
| 819 | /// Initialize slave mode with address configuration | ||
| 820 | pub(crate) fn init_slave(&mut self, config: SlaveAddrConfig) { | ||
| 821 | trace!("I2C slave: initializing with config={:?}", config); | ||
| 822 | |||
| 823 | // Disable peripheral for configuration | ||
| 824 | self.info.regs.cr1().modify(|reg| reg.set_pe(false)); | ||
| 825 | |||
| 826 | // Configure slave addresses | ||
| 827 | self.apply_address_configuration(config); | ||
| 828 | |||
| 829 | // Enable peripheral with slave settings | ||
| 830 | self.info.regs.cr1().modify(|reg| { | ||
| 831 | reg.set_pe(true); | ||
| 832 | reg.set_ack(true); // Enable acknowledgment for slave mode | ||
| 833 | reg.set_nostretch(false); // Allow clock stretching for processing time | ||
| 834 | }); | ||
| 835 | |||
| 836 | trace!("I2C slave: initialization complete"); | ||
| 837 | } | ||
| 838 | |||
| 839 | /// Apply the complete address configuration for slave mode | ||
| 840 | fn apply_address_configuration(&mut self, config: SlaveAddrConfig) { | ||
| 841 | match config.addr { | ||
| 842 | OwnAddresses::OA1(addr) => { | ||
| 843 | self.configure_primary_address(addr); | ||
| 844 | self.disable_secondary_address(); | ||
| 845 | } | ||
| 846 | OwnAddresses::OA2(oa2) => { | ||
| 847 | self.configure_default_primary_address(); | ||
| 848 | self.configure_secondary_address(oa2.addr); // v1 ignores mask | ||
| 849 | } | ||
| 850 | OwnAddresses::Both { oa1, oa2 } => { | ||
| 851 | self.configure_primary_address(oa1); | ||
| 852 | self.configure_secondary_address(oa2.addr); // v1 ignores mask | ||
| 853 | } | ||
| 854 | } | ||
| 855 | |||
| 856 | // Configure general call detection | ||
| 857 | if config.general_call { | ||
| 858 | self.info.regs.cr1().modify(|w| w.set_engc(true)); | ||
| 859 | } | ||
| 860 | } | ||
| 861 | |||
| 862 | /// Configure the primary address (OA1) register | ||
| 863 | fn configure_primary_address(&mut self, addr: Address) { | ||
| 864 | match addr { | ||
| 865 | Address::SevenBit(addr) => { | ||
| 866 | self.info.regs.oar1().write(|reg| { | ||
| 867 | let hw_addr = (addr as u16) << 1; // Address in bits [7:1] | ||
| 868 | reg.set_add(hw_addr); | ||
| 869 | reg.set_addmode(i2c::vals::Addmode::BIT7); | ||
| 870 | }); | ||
| 871 | } | ||
| 872 | Address::TenBit(addr) => { | ||
| 873 | self.info.regs.oar1().write(|reg| { | ||
| 874 | reg.set_add(addr); | ||
| 875 | reg.set_addmode(i2c::vals::Addmode::BIT10); | ||
| 876 | }); | ||
| 877 | } | ||
| 878 | } | ||
| 879 | |||
| 880 | // Set required bit 14 as per reference manual | ||
| 881 | self.info.regs.oar1().modify(|reg| reg.0 |= 1 << 14); | ||
| 882 | } | ||
| 883 | |||
| 884 | /// Configure the secondary address (OA2) register | ||
| 885 | fn configure_secondary_address(&mut self, addr: u8) { | ||
| 886 | self.info.regs.oar2().write(|reg| { | ||
| 887 | reg.set_add2(addr); | ||
| 888 | reg.set_endual(i2c::vals::Endual::DUAL); | ||
| 889 | }); | ||
| 890 | } | ||
| 891 | |||
| 892 | /// Set a default primary address when using OA2-only mode | ||
| 893 | fn configure_default_primary_address(&mut self) { | ||
| 894 | self.info.regs.oar1().write(|reg| { | ||
| 895 | reg.set_add(0); // Reserved address, safe to use | ||
| 896 | reg.set_addmode(i2c::vals::Addmode::BIT7); | ||
| 897 | }); | ||
| 898 | self.info.regs.oar1().modify(|reg| reg.0 |= 1 << 14); | ||
| 899 | } | ||
| 900 | |||
| 901 | /// Disable secondary address when not needed | ||
| 902 | fn disable_secondary_address(&mut self) { | ||
| 903 | self.info.regs.oar2().write(|reg| { | ||
| 904 | reg.set_endual(i2c::vals::Endual::SINGLE); | ||
| 905 | }); | ||
| 906 | } | ||
| 907 | } | ||
| 908 | |||
| 909 | impl<'d, M: PeriMode> I2c<'d, M, MultiMaster> { | ||
| 910 | /// Listen for incoming I2C address match and return the command type | ||
| 911 | /// | ||
| 912 | /// This method blocks until the slave address is matched by a master. | ||
| 913 | /// Returns the command type (Read/Write) and the matched address. | ||
| 914 | pub fn blocking_listen(&mut self) -> Result<SlaveCommand, Error> { | ||
| 915 | trace!("I2C slave: starting blocking listen for address match"); | ||
| 916 | let result = self.blocking_listen_with_timeout(self.timeout()); | ||
| 917 | trace!("I2C slave: blocking listen complete, result={:?}", result); | ||
| 918 | result | ||
| 919 | } | ||
| 920 | |||
| 921 | /// Respond to a master read request by transmitting data | ||
| 922 | /// | ||
| 923 | /// Sends the provided data to the master. If the master requests more bytes | ||
| 924 | /// than available, padding bytes (0x00) are sent until the master terminates | ||
| 925 | /// the transaction with NACK. | ||
| 926 | /// | ||
| 927 | /// Returns the total number of bytes transmitted (including padding). | ||
| 928 | pub fn blocking_respond_to_read(&mut self, data: &[u8]) -> Result<usize, Error> { | ||
| 929 | trace!("I2C slave: starting blocking respond_to_read, data_len={}", data.len()); | ||
| 930 | |||
| 931 | if let Some(zero_length_result) = self.detect_zero_length_read(self.timeout())? { | ||
| 932 | trace!("I2C slave: zero-length read detected"); | ||
| 933 | return Ok(zero_length_result); | ||
| 934 | } | ||
| 935 | |||
| 936 | let result = self.transmit_to_master(data, self.timeout()); | ||
| 937 | trace!("I2C slave: blocking respond_to_read complete, result={:?}", result); | ||
| 938 | result | ||
| 939 | } | ||
| 940 | |||
| 941 | /// Respond to a master write request by receiving data | ||
| 942 | /// | ||
| 943 | /// Receives data from the master into the provided buffer. If the master | ||
| 944 | /// sends more bytes than the buffer can hold, excess bytes are acknowledged | ||
| 945 | /// but discarded. | ||
| 946 | /// | ||
| 947 | /// Returns the number of bytes stored in the buffer (not total received). | ||
| 948 | pub fn blocking_respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { | ||
| 949 | trace!( | ||
| 950 | "I2C slave: starting blocking respond_to_write, buffer_len={}", | ||
| 951 | buffer.len() | ||
| 952 | ); | ||
| 953 | let result = self.receive_from_master(buffer, self.timeout()); | ||
| 954 | trace!("I2C slave: blocking respond_to_write complete, result={:?}", result); | ||
| 955 | result | ||
| 956 | } | ||
| 957 | |||
| 958 | // Private implementation methods | ||
| 959 | |||
| 960 | /// Wait for address match and determine transaction type | ||
| 961 | fn blocking_listen_with_timeout(&mut self, timeout: Timeout) -> Result<SlaveCommand, Error> { | ||
| 962 | // Ensure interrupts are disabled for blocking operation | ||
| 963 | self.disable_i2c_interrupts(); | ||
| 964 | |||
| 965 | // Wait for address match (ADDR flag) | ||
| 966 | loop { | ||
| 967 | let sr1 = Self::read_status_and_handle_errors(self.info)?; | ||
| 968 | |||
| 969 | if sr1.addr() { | ||
| 970 | // Address matched - read SR2 to get direction and clear ADDR flag | ||
| 971 | let sr2 = self.info.regs.sr2().read(); | ||
| 972 | let direction = if sr2.tra() { | ||
| 973 | SlaveCommandKind::Read | ||
| 974 | } else { | ||
| 975 | SlaveCommandKind::Write | ||
| 976 | }; | ||
| 977 | |||
| 978 | // Use the static method instead of the instance method | ||
| 979 | let matched_address = Self::decode_matched_address(sr2, self.info)?; | ||
| 980 | trace!( | ||
| 981 | "I2C slave: address matched, direction={:?}, addr={:?}", | ||
| 982 | direction, matched_address | ||
| 983 | ); | ||
| 984 | |||
| 985 | return Ok(SlaveCommand { | ||
| 986 | kind: direction, | ||
| 987 | address: matched_address, | ||
| 988 | }); | ||
| 989 | } | ||
| 990 | |||
| 991 | timeout.check()?; | ||
| 992 | } | ||
| 993 | } | ||
| 994 | |||
| 995 | /// Transmit data to master in response to read request | ||
| 996 | fn transmit_to_master(&mut self, data: &[u8], timeout: Timeout) -> Result<usize, Error> { | ||
| 997 | let mut bytes_transmitted = 0; | ||
| 998 | let mut padding_count = 0; | ||
| 999 | |||
| 1000 | loop { | ||
| 1001 | let byte_to_send = if bytes_transmitted < data.len() { | ||
| 1002 | data[bytes_transmitted] | ||
| 1003 | } else { | ||
| 1004 | padding_count += 1; | ||
| 1005 | 0x00 // Send padding bytes when data is exhausted | ||
| 1006 | }; | ||
| 1007 | |||
| 1008 | match self.transmit_byte(byte_to_send, timeout)? { | ||
| 1009 | TransmitResult::Acknowledged => { | ||
| 1010 | bytes_transmitted += 1; | ||
| 1011 | } | ||
| 1012 | TransmitResult::NotAcknowledged => { | ||
| 1013 | bytes_transmitted += 1; // Count the NACKed byte | ||
| 1014 | break; | ||
| 1015 | } | ||
| 1016 | TransmitResult::Stopped | TransmitResult::Restarted => { | ||
| 1017 | break; | ||
| 1018 | } | ||
| 1019 | } | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | if padding_count > 0 { | ||
| 1023 | trace!( | ||
| 1024 | "I2C slave: sent {} data bytes + {} padding bytes = {} total", | ||
| 1025 | data.len(), | ||
| 1026 | padding_count, | ||
| 1027 | bytes_transmitted | ||
| 1028 | ); | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | Ok(bytes_transmitted) | ||
| 1032 | } | ||
| 1033 | |||
| 1034 | /// Receive data from master during write request | ||
| 1035 | fn receive_from_master(&mut self, buffer: &mut [u8], timeout: Timeout) -> Result<usize, Error> { | ||
| 1036 | let mut bytes_stored = 0; | ||
| 1037 | |||
| 1038 | // Receive bytes that fit in buffer | ||
| 1039 | while bytes_stored < buffer.len() { | ||
| 1040 | match self.receive_byte(timeout)? { | ||
| 1041 | ReceiveResult::Data(byte) => { | ||
| 1042 | buffer[bytes_stored] = byte; | ||
| 1043 | bytes_stored += 1; | ||
| 1044 | } | ||
| 1045 | ReceiveResult::Stopped | ReceiveResult::Restarted => { | ||
| 1046 | return Ok(bytes_stored); | ||
| 1047 | } | ||
| 1048 | } | ||
| 1049 | } | ||
| 1050 | |||
| 1051 | // Handle buffer overflow by discarding excess bytes | ||
| 1052 | if bytes_stored == buffer.len() { | ||
| 1053 | trace!("I2C slave: buffer full, discarding excess bytes"); | ||
| 1054 | self.discard_excess_bytes(timeout)?; | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | Ok(bytes_stored) | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | /// Detect zero-length read pattern early | ||
| 1061 | /// | ||
| 1062 | /// Zero-length reads occur when a master sends START+ADDR+R followed immediately | ||
| 1063 | /// by NACK+STOP without wanting any data. This must be detected before attempting | ||
| 1064 | /// to transmit any bytes to avoid SDA line issues. | ||
| 1065 | fn detect_zero_length_read(&mut self, _timeout: Timeout) -> Result<Option<usize>, Error> { | ||
| 1066 | // Quick check for immediate termination signals | ||
| 1067 | let sr1 = self.info.regs.sr1().read(); | ||
| 1068 | |||
| 1069 | // Check for immediate NACK (fastest zero-length pattern) | ||
| 1070 | if sr1.af() { | ||
| 1071 | self.clear_acknowledge_failure(); | ||
| 1072 | return Ok(Some(0)); | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | // Check for immediate STOP (alternative zero-length pattern) | ||
| 1076 | if sr1.stopf() { | ||
| 1077 | Self::clear_stop_flag(self.info); | ||
| 1078 | return Ok(Some(0)); | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | // Give a brief window for master to send termination signals | ||
| 1082 | // This handles masters that have slight delays between address ACK and NACK | ||
| 1083 | const ZERO_LENGTH_DETECTION_CYCLES: u32 = 20; // ~5-10µs window | ||
| 1084 | |||
| 1085 | for _ in 0..ZERO_LENGTH_DETECTION_CYCLES { | ||
| 1086 | let sr1 = self.info.regs.sr1().read(); | ||
| 1087 | |||
| 1088 | // Immediate NACK indicates zero-length read | ||
| 1089 | if sr1.af() { | ||
| 1090 | self.clear_acknowledge_failure(); | ||
| 1091 | return Ok(Some(0)); | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | // Immediate STOP indicates zero-length read | ||
| 1095 | if sr1.stopf() { | ||
| 1096 | Self::clear_stop_flag(self.info); | ||
| 1097 | return Ok(Some(0)); | ||
| 1098 | } | ||
| 1099 | |||
| 1100 | // If TXE becomes ready, master is waiting for data - not zero-length | ||
| 1101 | if sr1.txe() { | ||
| 1102 | return Ok(None); // Proceed with normal transmission | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | // If RESTART detected, handle as zero-length | ||
| 1106 | if sr1.addr() { | ||
| 1107 | return Ok(Some(0)); | ||
| 1108 | } | ||
| 1109 | } | ||
| 1110 | |||
| 1111 | // No zero-length pattern detected within the window | ||
| 1112 | Ok(None) | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | /// Discard excess bytes when buffer is full | ||
| 1116 | fn discard_excess_bytes(&mut self, timeout: Timeout) -> Result<(), Error> { | ||
| 1117 | let mut discarded_count = 0; | ||
| 1118 | |||
| 1119 | loop { | ||
| 1120 | match self.receive_byte(timeout)? { | ||
| 1121 | ReceiveResult::Data(_) => { | ||
| 1122 | discarded_count += 1; | ||
| 1123 | continue; | ||
| 1124 | } | ||
| 1125 | ReceiveResult::Stopped | ReceiveResult::Restarted => { | ||
| 1126 | if discarded_count > 0 { | ||
| 1127 | trace!("I2C slave: discarded {} excess bytes", discarded_count); | ||
| 1128 | } | ||
| 1129 | break; | ||
| 1130 | } | ||
| 1131 | } | ||
| 1132 | } | ||
| 1133 | Ok(()) | ||
| 1134 | } | ||
| 1135 | |||
| 1136 | /// Send a single byte and wait for master's response | ||
| 1137 | fn transmit_byte(&mut self, byte: u8, timeout: Timeout) -> Result<TransmitResult, Error> { | ||
| 1138 | // Wait for transmit buffer ready | ||
| 1139 | self.wait_for_transmit_ready(timeout)?; | ||
| 1140 | |||
| 1141 | // Send the byte | ||
| 1142 | self.info.regs.dr().write(|w| w.set_dr(byte)); | ||
| 1143 | |||
| 1144 | // Wait for transmission completion or master response | ||
| 1145 | self.wait_for_transmit_completion(timeout) | ||
| 1146 | } | ||
| 1147 | |||
| 1148 | /// Wait until transmit buffer is ready (TXE flag set) | ||
| 1149 | fn wait_for_transmit_ready(&mut self, timeout: Timeout) -> Result<(), Error> { | ||
| 1150 | loop { | ||
| 1151 | let sr1 = Self::read_status_and_handle_errors(self.info)?; | ||
| 1152 | |||
| 1153 | // Check for early termination conditions | ||
| 1154 | if let Some(result) = Self::check_early_termination(sr1) { | ||
| 1155 | return Err(self.handle_early_termination(result)); | ||
| 1156 | } | ||
| 1157 | |||
| 1158 | if sr1.txe() { | ||
| 1159 | return Ok(()); // Ready to transmit | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | timeout.check()?; | ||
| 1163 | } | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | /// Wait for byte transmission completion or master response | ||
| 1167 | fn wait_for_transmit_completion(&mut self, timeout: Timeout) -> Result<TransmitResult, Error> { | ||
| 1168 | loop { | ||
| 1169 | let sr1 = self.info.regs.sr1().read(); | ||
| 1170 | |||
| 1171 | // Check flags in priority order | ||
| 1172 | if sr1.af() { | ||
| 1173 | self.clear_acknowledge_failure(); | ||
| 1174 | return Ok(TransmitResult::NotAcknowledged); | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | if sr1.btf() { | ||
| 1178 | return Ok(TransmitResult::Acknowledged); | ||
| 1179 | } | ||
| 1180 | |||
| 1181 | if sr1.stopf() { | ||
| 1182 | Self::clear_stop_flag(self.info); | ||
| 1183 | return Ok(TransmitResult::Stopped); | ||
| 1184 | } | ||
| 1185 | |||
| 1186 | if sr1.addr() { | ||
| 1187 | return Ok(TransmitResult::Restarted); | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | // Check for other error conditions | ||
| 1191 | self.check_for_hardware_errors(sr1)?; | ||
| 1192 | |||
| 1193 | timeout.check()?; | ||
| 1194 | } | ||
| 1195 | } | ||
| 1196 | |||
| 1197 | /// Receive a single byte or detect transaction termination | ||
| 1198 | fn receive_byte(&mut self, timeout: Timeout) -> Result<ReceiveResult, Error> { | ||
| 1199 | loop { | ||
| 1200 | let sr1 = Self::read_status_and_handle_errors(self.info)?; | ||
| 1201 | |||
| 1202 | // Check for received data first (prioritize data over control signals) | ||
| 1203 | if sr1.rxne() { | ||
| 1204 | let byte = self.info.regs.dr().read().dr(); | ||
| 1205 | return Ok(ReceiveResult::Data(byte)); | ||
| 1206 | } | ||
| 1207 | |||
| 1208 | // Check for transaction termination | ||
| 1209 | if sr1.addr() { | ||
| 1210 | return Ok(ReceiveResult::Restarted); | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | if sr1.stopf() { | ||
| 1214 | Self::clear_stop_flag(self.info); | ||
| 1215 | return Ok(ReceiveResult::Stopped); | ||
| 1216 | } | ||
| 1217 | |||
| 1218 | timeout.check()?; | ||
| 1219 | } | ||
| 1220 | } | ||
| 1221 | |||
| 1222 | /// Determine which slave address was matched based on SR2 flags | ||
| 1223 | fn decode_matched_address(sr2: i2c::regs::Sr2, info: &'static Info) -> Result<Address, Error> { | ||
| 1224 | if sr2.gencall() { | ||
| 1225 | Ok(Address::SevenBit(0x00)) // General call address | ||
| 1226 | } else if sr2.dualf() { | ||
| 1227 | // OA2 (secondary address) was matched | ||
| 1228 | let oar2 = info.regs.oar2().read(); | ||
| 1229 | if oar2.endual() != i2c::vals::Endual::DUAL { | ||
| 1230 | return Err(Error::Bus); // Hardware inconsistency | ||
| 1231 | } | ||
| 1232 | Ok(Address::SevenBit(oar2.add2())) | ||
| 1233 | } else { | ||
| 1234 | // OA1 (primary address) was matched | ||
| 1235 | let oar1 = info.regs.oar1().read(); | ||
| 1236 | match oar1.addmode() { | ||
| 1237 | i2c::vals::Addmode::BIT7 => { | ||
| 1238 | let addr = (oar1.add() >> 1) as u8; | ||
| 1239 | Ok(Address::SevenBit(addr)) | ||
| 1240 | } | ||
| 1241 | i2c::vals::Addmode::BIT10 => Ok(Address::TenBit(oar1.add())), | ||
| 1242 | } | ||
| 1243 | } | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | // Helper methods for hardware interaction | ||
| 1247 | |||
| 1248 | /// Read status register and handle I2C errors (except NACK in slave mode) | ||
| 1249 | fn read_status_and_handle_errors(info: &'static Info) -> Result<i2c::regs::Sr1, Error> { | ||
| 1250 | match Self::check_and_clear_error_flags(info) { | ||
| 1251 | Ok(sr1) => Ok(sr1), | ||
| 1252 | Err(Error::Nack) => { | ||
| 1253 | // In slave mode, NACK is normal protocol behavior, not an error | ||
| 1254 | Ok(info.regs.sr1().read()) | ||
| 1255 | } | ||
| 1256 | Err(other_error) => Err(other_error), | ||
| 1257 | } | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | /// Check for conditions that cause early termination of operations | ||
| 1261 | fn check_early_termination(sr1: i2c::regs::Sr1) -> Option<TransmitResult> { | ||
| 1262 | if sr1.stopf() { | ||
| 1263 | Some(TransmitResult::Stopped) | ||
| 1264 | } else if sr1.addr() { | ||
| 1265 | Some(TransmitResult::Restarted) | ||
| 1266 | } else if sr1.af() { | ||
| 1267 | Some(TransmitResult::NotAcknowledged) | ||
| 1268 | } else { | ||
| 1269 | None | ||
| 1270 | } | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | /// Convert early termination to appropriate error | ||
| 1274 | fn handle_early_termination(&mut self, result: TransmitResult) -> Error { | ||
| 1275 | match result { | ||
| 1276 | TransmitResult::Stopped => { | ||
| 1277 | Self::clear_stop_flag(self.info); | ||
| 1278 | Error::Bus // Unexpected STOP during setup | ||
| 1279 | } | ||
| 1280 | TransmitResult::Restarted => { | ||
| 1281 | Error::Bus // Unexpected RESTART during setup | ||
| 1282 | } | ||
| 1283 | TransmitResult::NotAcknowledged => { | ||
| 1284 | self.clear_acknowledge_failure(); | ||
| 1285 | Error::Bus // Unexpected NACK during setup | ||
| 1286 | } | ||
| 1287 | TransmitResult::Acknowledged => { | ||
| 1288 | unreachable!() // This should never be passed to this function | ||
| 1289 | } | ||
| 1290 | } | ||
| 1291 | } | ||
| 1292 | |||
| 1293 | /// Check for hardware-level I2C errors during transmission | ||
| 1294 | fn check_for_hardware_errors(&self, sr1: i2c::regs::Sr1) -> Result<(), Error> { | ||
| 1295 | if sr1.timeout() || sr1.ovr() || sr1.arlo() || sr1.berr() { | ||
| 1296 | // Delegate to existing error handling | ||
| 1297 | Self::check_and_clear_error_flags(self.info)?; | ||
| 1298 | } | ||
| 1299 | Ok(()) | ||
| 1300 | } | ||
| 1301 | |||
| 1302 | /// Disable I2C event and error interrupts for blocking operations | ||
| 1303 | fn disable_i2c_interrupts(&mut self) { | ||
| 1304 | self.info.regs.cr2().modify(|w| { | ||
| 1305 | w.set_itevten(false); | ||
| 1306 | w.set_iterren(false); | ||
| 1307 | }); | ||
| 1308 | } | ||
| 1309 | |||
| 1310 | /// Clear the acknowledge failure flag | ||
| 1311 | fn clear_acknowledge_failure(&mut self) { | ||
| 1312 | self.info.regs.sr1().write(|reg| { | ||
| 1313 | reg.0 = !0; | ||
| 1314 | reg.set_af(false); | ||
| 1315 | }); | ||
| 1316 | } | ||
| 1317 | |||
| 1318 | /// Configure DMA settings for slave operations (shared between read/write) | ||
| 1319 | fn setup_slave_dma_base(&mut self) { | ||
| 1320 | self.info.regs.cr2().modify(|w| { | ||
| 1321 | w.set_itbufen(false); // Always disable buffer interrupts when using DMA | ||
| 1322 | w.set_dmaen(true); // Enable DMA requests | ||
| 1323 | w.set_last(false); // LAST bit not used in slave mode for v1 hardware | ||
| 1324 | }); | ||
| 1325 | } | ||
| 1326 | |||
| 1327 | /// Disable DMA and interrupts in a critical section | ||
| 1328 | fn disable_dma_and_interrupts(info: &'static Info) { | ||
| 1329 | critical_section::with(|_| { | ||
| 1330 | info.regs.cr2().modify(|w| { | ||
| 1331 | w.set_dmaen(false); | ||
| 1332 | w.set_iterren(false); | ||
| 1333 | w.set_itevten(false); | ||
| 1334 | }); | ||
| 1335 | }); | ||
| 1336 | } | ||
| 1337 | |||
| 1338 | /// Check for early termination conditions during slave operations | ||
| 1339 | /// Returns Some(result) if termination detected, None to continue | ||
| 1340 | fn check_slave_termination_conditions(sr1: i2c::regs::Sr1) -> Option<SlaveTermination> { | ||
| 1341 | if sr1.stopf() { | ||
| 1342 | Some(SlaveTermination::Stop) | ||
| 1343 | } else if sr1.addr() { | ||
| 1344 | Some(SlaveTermination::Restart) | ||
| 1345 | } else if sr1.af() { | ||
| 1346 | Some(SlaveTermination::Nack) | ||
| 1347 | } else { | ||
| 1348 | None | ||
| 1349 | } | ||
| 1350 | } | ||
| 1351 | } | ||
| 1352 | |||
| 1353 | impl<'d> I2c<'d, Async, MultiMaster> { | ||
| 1354 | /// Async listen for incoming I2C messages using interrupts | ||
| 1355 | /// | ||
| 1356 | /// Waits for a master to address this slave and returns the command type | ||
| 1357 | /// (Read/Write) and the matched address. This method will suspend until | ||
| 1358 | /// an address match occurs. | ||
| 1359 | pub async fn listen(&mut self) -> Result<SlaveCommand, Error> { | ||
| 1360 | trace!("I2C slave: starting async listen for address match"); | ||
| 1361 | let state = self.state; | ||
| 1362 | let info = self.info; | ||
| 1363 | |||
| 1364 | Self::enable_interrupts(info); | ||
| 1365 | |||
| 1366 | let on_drop = OnDrop::new(|| { | ||
| 1367 | Self::disable_dma_and_interrupts(info); | ||
| 1368 | }); | ||
| 1369 | |||
| 1370 | let result = poll_fn(|cx| { | ||
| 1371 | state.waker.register(cx.waker()); | ||
| 1372 | |||
| 1373 | match Self::check_and_clear_error_flags(info) { | ||
| 1374 | Err(e) => { | ||
| 1375 | error!("I2C slave: error during listen: {:?}", e); | ||
| 1376 | Poll::Ready(Err(e)) | ||
| 1377 | } | ||
| 1378 | Ok(sr1) => { | ||
| 1379 | if sr1.addr() { | ||
| 1380 | let sr2 = info.regs.sr2().read(); | ||
| 1381 | let direction = if sr2.tra() { | ||
| 1382 | SlaveCommandKind::Read | ||
| 1383 | } else { | ||
| 1384 | SlaveCommandKind::Write | ||
| 1385 | }; | ||
| 1386 | |||
| 1387 | let matched_address = match Self::decode_matched_address(sr2, info) { | ||
| 1388 | Ok(addr) => { | ||
| 1389 | trace!("I2C slave: address matched, direction={:?}, addr={:?}", direction, addr); | ||
| 1390 | addr | ||
| 1391 | } | ||
| 1392 | Err(e) => { | ||
| 1393 | error!("I2C slave: failed to decode matched address: {:?}", e); | ||
| 1394 | return Poll::Ready(Err(e)); | ||
| 1395 | } | ||
| 1396 | }; | ||
| 1397 | |||
| 1398 | Poll::Ready(Ok(SlaveCommand { | ||
| 1399 | kind: direction, | ||
| 1400 | address: matched_address, | ||
| 1401 | })) | ||
| 1402 | } else { | ||
| 1403 | Self::enable_interrupts(info); | ||
| 1404 | Poll::Pending | ||
| 1405 | } | ||
| 1406 | } | ||
| 1407 | } | ||
| 1408 | }) | ||
| 1409 | .await; | ||
| 1410 | |||
| 1411 | drop(on_drop); | ||
| 1412 | trace!("I2C slave: listen complete, result={:?}", result); | ||
| 1413 | result | ||
| 1414 | } | ||
| 1415 | |||
| 1416 | /// Async respond to write command using RX DMA | ||
| 1417 | /// | ||
| 1418 | /// Receives data from the master into the provided buffer using DMA. | ||
| 1419 | /// If the master sends more bytes than the buffer can hold, excess bytes | ||
| 1420 | /// are acknowledged but discarded to prevent interrupt flooding. | ||
| 1421 | /// | ||
| 1422 | /// Returns the number of bytes stored in the buffer (not total received). | ||
| 1423 | pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { | ||
| 1424 | trace!("I2C slave: starting respond_to_write, buffer_len={}", buffer.len()); | ||
| 1425 | |||
| 1426 | if buffer.is_empty() { | ||
| 1427 | warn!("I2C slave: respond_to_write called with empty buffer"); | ||
| 1428 | return Err(Error::Overrun); | ||
| 1429 | } | ||
| 1430 | |||
| 1431 | let state = self.state; | ||
| 1432 | let info = self.info; | ||
| 1433 | |||
| 1434 | self.setup_slave_dma_base(); | ||
| 1435 | |||
| 1436 | let on_drop = OnDrop::new(|| { | ||
| 1437 | Self::disable_dma_and_interrupts(info); | ||
| 1438 | }); | ||
| 1439 | |||
| 1440 | info.regs.sr2().read(); | ||
| 1441 | |||
| 1442 | let result = self.execute_slave_receive_transfer(buffer, state, info).await; | ||
| 1443 | |||
| 1444 | drop(on_drop); | ||
| 1445 | trace!("I2C slave: respond_to_write complete, result={:?}", result); | ||
| 1446 | result | ||
| 1447 | } | ||
| 1448 | |||
| 1449 | /// Async respond to read command using TX DMA | ||
| 1450 | /// | ||
| 1451 | /// Transmits data to the master using DMA. If the master requests more bytes | ||
| 1452 | /// than available in the data buffer, padding bytes (0x00) are sent until | ||
| 1453 | /// the master terminates the transaction with NACK, STOP, or RESTART. | ||
| 1454 | /// | ||
| 1455 | /// Returns the total number of bytes transmitted (data + padding). | ||
| 1456 | pub async fn respond_to_read(&mut self, data: &[u8]) -> Result<usize, Error> { | ||
| 1457 | trace!("I2C slave: starting respond_to_read, data_len={}", data.len()); | ||
| 1458 | |||
| 1459 | if data.is_empty() { | ||
| 1460 | warn!("I2C slave: respond_to_read called with empty data"); | ||
| 1461 | return Err(Error::Overrun); | ||
| 1462 | } | ||
| 1463 | |||
| 1464 | let state = self.state; | ||
| 1465 | let info = self.info; | ||
| 1466 | |||
| 1467 | self.setup_slave_dma_base(); | ||
| 1468 | |||
| 1469 | let on_drop = OnDrop::new(|| { | ||
| 1470 | Self::disable_dma_and_interrupts(info); | ||
| 1471 | }); | ||
| 1472 | |||
| 1473 | info.regs.sr2().read(); | ||
| 1474 | |||
| 1475 | let result = self.execute_slave_transmit_transfer(data, state, info).await; | ||
| 1476 | |||
| 1477 | drop(on_drop); | ||
| 1478 | trace!("I2C slave: respond_to_read complete, result={:?}", result); | ||
| 1479 | result | ||
| 1480 | } | ||
| 1481 | |||
| 1482 | // === Private Transfer Execution Methods === | ||
| 1483 | |||
| 1484 | /// Execute complete slave receive transfer with excess byte handling | ||
| 1485 | async fn execute_slave_receive_transfer( | ||
| 1486 | &mut self, | ||
| 1487 | buffer: &mut [u8], | ||
| 1488 | state: &'static State, | ||
| 1489 | info: &'static Info, | ||
| 1490 | ) -> Result<usize, Error> { | ||
| 1491 | let dma_transfer = unsafe { | ||
| 1492 | let src = info.regs.dr().as_ptr() as *mut u8; | ||
| 1493 | self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) | ||
| 1494 | }; | ||
| 1495 | |||
| 1496 | let i2c_monitor = | ||
| 1497 | Self::create_termination_monitor(state, info, &[SlaveTermination::Stop, SlaveTermination::Restart]); | ||
| 1498 | |||
| 1499 | match select(dma_transfer, i2c_monitor).await { | ||
| 1500 | Either::Second(Err(e)) => { | ||
| 1501 | error!("I2C slave: error during receive transfer: {:?}", e); | ||
| 1502 | Self::disable_dma_and_interrupts(info); | ||
| 1503 | Err(e) | ||
| 1504 | } | ||
| 1505 | Either::First(_) => { | ||
| 1506 | trace!("I2C slave: DMA receive completed, handling excess bytes"); | ||
| 1507 | Self::disable_dma_and_interrupts(info); | ||
| 1508 | self.handle_excess_bytes(state, info).await?; | ||
| 1509 | Ok(buffer.len()) | ||
| 1510 | } | ||
| 1511 | Either::Second(Ok(termination)) => { | ||
| 1512 | trace!("I2C slave: receive terminated by I2C event: {:?}", termination); | ||
| 1513 | Self::disable_dma_and_interrupts(info); | ||
| 1514 | Ok(buffer.len()) | ||
| 1515 | } | ||
| 1516 | } | ||
| 1517 | } | ||
| 1518 | |||
| 1519 | /// Execute complete slave transmit transfer with padding byte handling | ||
| 1520 | async fn execute_slave_transmit_transfer( | ||
| 1521 | &mut self, | ||
| 1522 | data: &[u8], | ||
| 1523 | state: &'static State, | ||
| 1524 | info: &'static Info, | ||
| 1525 | ) -> Result<usize, Error> { | ||
| 1526 | let dma_transfer = unsafe { | ||
| 1527 | let dst = info.regs.dr().as_ptr() as *mut u8; | ||
| 1528 | self.tx_dma.as_mut().unwrap().write(data, dst, Default::default()) | ||
| 1529 | }; | ||
| 1530 | |||
| 1531 | let i2c_monitor = Self::create_termination_monitor( | ||
| 1532 | state, | ||
| 1533 | info, | ||
| 1534 | &[ | ||
| 1535 | SlaveTermination::Stop, | ||
| 1536 | SlaveTermination::Restart, | ||
| 1537 | SlaveTermination::Nack, | ||
| 1538 | ], | ||
| 1539 | ); | ||
| 1540 | |||
| 1541 | match select(dma_transfer, i2c_monitor).await { | ||
| 1542 | Either::Second(Err(e)) => { | ||
| 1543 | error!("I2C slave: error during transmit transfer: {:?}", e); | ||
| 1544 | Self::disable_dma_and_interrupts(info); | ||
| 1545 | Err(e) | ||
| 1546 | } | ||
| 1547 | Either::First(_) => { | ||
| 1548 | trace!("I2C slave: DMA transmit completed, handling padding bytes"); | ||
| 1549 | Self::disable_dma_and_interrupts(info); | ||
| 1550 | let padding_count = self.handle_padding_bytes(state, info).await?; | ||
| 1551 | let total_bytes = data.len() + padding_count; | ||
| 1552 | trace!( | ||
| 1553 | "I2C slave: sent {} data bytes + {} padding bytes = {} total", | ||
| 1554 | data.len(), | ||
| 1555 | padding_count, | ||
| 1556 | total_bytes | ||
| 1557 | ); | ||
| 1558 | Ok(total_bytes) | ||
| 1559 | } | ||
| 1560 | Either::Second(Ok(termination)) => { | ||
| 1561 | trace!("I2C slave: transmit terminated by I2C event: {:?}", termination); | ||
| 1562 | Self::disable_dma_and_interrupts(info); | ||
| 1563 | Ok(data.len()) | ||
| 1564 | } | ||
| 1565 | } | ||
| 1566 | } | ||
| 1567 | |||
| 1568 | /// Create a future that monitors for specific slave termination conditions | ||
| 1569 | fn create_termination_monitor( | ||
| 1570 | state: &'static State, | ||
| 1571 | info: &'static Info, | ||
| 1572 | allowed_terminations: &'static [SlaveTermination], | ||
| 1573 | ) -> impl Future<Output = Result<SlaveTermination, Error>> { | ||
| 1574 | poll_fn(move |cx| { | ||
| 1575 | state.waker.register(cx.waker()); | ||
| 1576 | |||
| 1577 | match Self::check_and_clear_error_flags(info) { | ||
| 1578 | Err(Error::Nack) if allowed_terminations.contains(&SlaveTermination::Nack) => { | ||
| 1579 | Poll::Ready(Ok(SlaveTermination::Nack)) | ||
| 1580 | } | ||
| 1581 | Err(e) => Poll::Ready(Err(e)), | ||
| 1582 | Ok(sr1) => { | ||
| 1583 | if let Some(termination) = Self::check_slave_termination_conditions(sr1) { | ||
| 1584 | if allowed_terminations.contains(&termination) { | ||
| 1585 | // Handle the specific termination condition | ||
| 1586 | match termination { | ||
| 1587 | SlaveTermination::Stop => Self::clear_stop_flag(info), | ||
| 1588 | SlaveTermination::Nack => { | ||
| 1589 | info.regs.sr1().write(|reg| { | ||
| 1590 | reg.0 = !0; | ||
| 1591 | reg.set_af(false); | ||
| 1592 | }); | ||
| 1593 | } | ||
| 1594 | SlaveTermination::Restart => { | ||
| 1595 | // ADDR flag will be handled by next listen() call | ||
| 1596 | } | ||
| 1597 | } | ||
| 1598 | Poll::Ready(Ok(termination)) | ||
| 1599 | } else { | ||
| 1600 | // Unexpected termination condition | ||
| 1601 | Poll::Ready(Err(Error::Bus)) | ||
| 1602 | } | ||
| 1603 | } else { | ||
| 1604 | Self::enable_interrupts(info); | ||
| 1605 | Poll::Pending | ||
| 1606 | } | ||
| 1607 | } | ||
| 1608 | } | ||
| 1609 | }) | ||
| 1610 | } | ||
| 1611 | |||
| 1612 | /// Handle excess bytes after DMA buffer is full | ||
| 1613 | /// | ||
| 1614 | /// Reads and discards bytes until transaction termination to prevent interrupt flooding | ||
| 1615 | async fn handle_excess_bytes(&mut self, state: &'static State, info: &'static Info) -> Result<(), Error> { | ||
| 1616 | let mut discarded_count = 0; | ||
| 1617 | |||
| 1618 | poll_fn(|cx| { | ||
| 1619 | state.waker.register(cx.waker()); | ||
| 1620 | |||
| 1621 | match Self::check_and_clear_error_flags(info) { | ||
| 1622 | Err(e) => { | ||
| 1623 | error!("I2C slave: error while discarding excess bytes: {:?}", e); | ||
| 1624 | Poll::Ready(Err(e)) | ||
| 1625 | } | ||
| 1626 | Ok(sr1) => { | ||
| 1627 | if let Some(termination) = Self::check_slave_termination_conditions(sr1) { | ||
| 1628 | match termination { | ||
| 1629 | SlaveTermination::Stop => Self::clear_stop_flag(info), | ||
| 1630 | SlaveTermination::Restart => {} | ||
| 1631 | SlaveTermination::Nack => unreachable!("NACK not expected during receive"), | ||
| 1632 | } | ||
| 1633 | if discarded_count > 0 { | ||
| 1634 | trace!("I2C slave: discarded {} excess bytes", discarded_count); | ||
| 1635 | } | ||
| 1636 | return Poll::Ready(Ok(())); | ||
| 1637 | } | ||
| 1638 | |||
| 1639 | if sr1.rxne() { | ||
| 1640 | let _discarded_byte = info.regs.dr().read().dr(); | ||
| 1641 | discarded_count += 1; | ||
| 1642 | Self::enable_interrupts(info); | ||
| 1643 | return Poll::Pending; | ||
| 1644 | } | ||
| 1645 | |||
| 1646 | Self::enable_interrupts(info); | ||
| 1647 | Poll::Pending | ||
| 1648 | } | ||
| 1649 | } | ||
| 1650 | }) | ||
| 1651 | .await | ||
| 1652 | } | ||
| 1653 | |||
| 1654 | /// Handle padding bytes after DMA data is exhausted | ||
| 1655 | /// | ||
| 1656 | /// Sends 0x00 bytes until transaction termination to prevent interrupt flooding | ||
| 1657 | async fn handle_padding_bytes(&mut self, state: &'static State, info: &'static Info) -> Result<usize, Error> { | ||
| 1658 | let mut padding_count = 0; | ||
| 1659 | |||
| 1660 | poll_fn(|cx| { | ||
| 1661 | state.waker.register(cx.waker()); | ||
| 1662 | |||
| 1663 | match Self::check_and_clear_error_flags(info) { | ||
| 1664 | Err(Error::Nack) => Poll::Ready(Ok(padding_count)), | ||
| 1665 | Err(e) => { | ||
| 1666 | error!("I2C slave: error while sending padding bytes: {:?}", e); | ||
| 1667 | Poll::Ready(Err(e)) | ||
| 1668 | } | ||
| 1669 | Ok(sr1) => { | ||
| 1670 | if let Some(termination) = Self::check_slave_termination_conditions(sr1) { | ||
| 1671 | match termination { | ||
| 1672 | SlaveTermination::Stop => Self::clear_stop_flag(info), | ||
| 1673 | SlaveTermination::Restart => {} | ||
| 1674 | SlaveTermination::Nack => { | ||
| 1675 | info.regs.sr1().write(|reg| { | ||
| 1676 | reg.0 = !0; | ||
| 1677 | reg.set_af(false); | ||
| 1678 | }); | ||
| 1679 | } | ||
| 1680 | } | ||
| 1681 | return Poll::Ready(Ok(padding_count)); | ||
| 1682 | } | ||
| 1683 | |||
| 1684 | if sr1.txe() { | ||
| 1685 | info.regs.dr().write(|w| w.set_dr(0x00)); | ||
| 1686 | padding_count += 1; | ||
| 1687 | Self::enable_interrupts(info); | ||
| 1688 | return Poll::Pending; | ||
| 1689 | } | ||
| 1690 | |||
| 1691 | Self::enable_interrupts(info); | ||
| 1692 | Poll::Pending | ||
| 1693 | } | ||
| 1694 | } | ||
| 1695 | }) | ||
| 1696 | .await | ||
| 1697 | } | ||
| 1698 | } | ||
| 1699 | |||
| 1700 | /// Timing configuration for I2C v1 hardware | ||
| 1701 | /// | ||
| 1702 | /// This struct encapsulates the complex timing calculations required for STM32 I2C v1 | ||
| 1703 | /// peripherals, which use three separate registers (CR2.FREQ, CCR, TRISE) instead of | ||
| 1704 | /// the unified TIMINGR register found in v2 hardware. | ||
| 732 | struct Timings { | 1705 | struct Timings { |
| 733 | freq: u8, | 1706 | freq: u8, // APB frequency in MHz for CR2.FREQ register |
| 734 | mode: Mode, | 1707 | mode: Mode, // Standard or Fast mode selection |
| 735 | trise: u8, | 1708 | trise: u8, // Rise time compensation value |
| 736 | ccr: u16, | 1709 | ccr: u16, // Clock control register value |
| 737 | duty: Duty, | 1710 | duty: Duty, // Fast mode duty cycle selection |
| 738 | } | 1711 | } |
| 739 | 1712 | ||
| 740 | impl Timings { | 1713 | impl Timings { |
| @@ -762,11 +1735,7 @@ impl Timings { | |||
| 762 | mode = Mode::Standard; | 1735 | mode = Mode::Standard; |
| 763 | ccr = { | 1736 | ccr = { |
| 764 | let ccr = clock / (frequency * 2); | 1737 | let ccr = clock / (frequency * 2); |
| 765 | if ccr < 4 { | 1738 | if ccr < 4 { 4 } else { ccr } |
| 766 | 4 | ||
| 767 | } else { | ||
| 768 | ccr | ||
| 769 | } | ||
| 770 | }; | 1739 | }; |
| 771 | } else { | 1740 | } else { |
| 772 | const DUTYCYCLE: u8 = 0; | 1741 | const DUTYCYCLE: u8 = 0; |
| @@ -775,14 +1744,10 @@ impl Timings { | |||
| 775 | duty = Duty::Duty2_1; | 1744 | duty = Duty::Duty2_1; |
| 776 | ccr = clock / (frequency * 3); | 1745 | ccr = clock / (frequency * 3); |
| 777 | ccr = if ccr < 1 { 1 } else { ccr }; | 1746 | ccr = if ccr < 1 { 1 } else { ccr }; |
| 778 | |||
| 779 | // Set clock to fast mode with appropriate parameters for selected speed (2:1 duty cycle) | ||
| 780 | } else { | 1747 | } else { |
| 781 | duty = Duty::Duty16_9; | 1748 | duty = Duty::Duty16_9; |
| 782 | ccr = clock / (frequency * 25); | 1749 | ccr = clock / (frequency * 25); |
| 783 | ccr = if ccr < 1 { 1 } else { ccr }; | 1750 | ccr = if ccr < 1 { 1 } else { ccr }; |
| 784 | |||
| 785 | // Set clock to fast mode with appropriate parameters for selected speed (16:9 duty cycle) | ||
| 786 | } | 1751 | } |
| 787 | } | 1752 | } |
| 788 | 1753 | ||
| @@ -792,11 +1757,6 @@ impl Timings { | |||
| 792 | ccr: ccr as u16, | 1757 | ccr: ccr as u16, |
| 793 | duty, | 1758 | duty, |
| 794 | mode, | 1759 | mode, |
| 795 | //prescale: presc_reg, | ||
| 796 | //scll, | ||
| 797 | //sclh, | ||
| 798 | //sdadel, | ||
| 799 | //scldel, | ||
| 800 | } | 1760 | } |
| 801 | } | 1761 | } |
| 802 | } | 1762 | } |
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 0bfc795ac..4527e55b9 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs | |||
| @@ -2,7 +2,7 @@ use core::cmp; | |||
| 2 | use core::future::poll_fn; | 2 | use core::future::poll_fn; |
| 3 | use core::task::Poll; | 3 | use core::task::Poll; |
| 4 | 4 | ||
| 5 | use config::{Address, OwnAddresses, OA2}; | 5 | use config::{Address, OA2, OwnAddresses}; |
| 6 | use embassy_embedded_hal::SetConfig; | 6 | use embassy_embedded_hal::SetConfig; |
| 7 | use embassy_hal_internal::drop::OnDrop; | 7 | use embassy_hal_internal::drop::OnDrop; |
| 8 | use embedded_hal_1::i2c::Operation; | 8 | use embedded_hal_1::i2c::Operation; |
| @@ -70,6 +70,11 @@ fn debug_print_interrupts(isr: stm32_metapac::i2c::regs::Isr) { | |||
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | pub(crate) unsafe fn on_interrupt<T: Instance>() { | 72 | pub(crate) unsafe fn on_interrupt<T: Instance>() { |
| 73 | // restore the clocks to their last configured state as | ||
| 74 | // much is lost in STOP modes | ||
| 75 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 76 | crate::low_power::Executor::on_wakeup_irq(); | ||
| 77 | |||
| 73 | let regs = T::info().regs; | 78 | let regs = T::info().regs; |
| 74 | let isr = regs.isr().read(); | 79 | let isr = regs.isr().read(); |
| 75 | 80 | ||
| @@ -814,6 +819,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 814 | 819 | ||
| 815 | /// Write. | 820 | /// Write. |
| 816 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { | 821 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { |
| 822 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 823 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 817 | let timeout = self.timeout(); | 824 | let timeout = self.timeout(); |
| 818 | if write.is_empty() { | 825 | if write.is_empty() { |
| 819 | self.write_internal(address.into(), write, true, timeout) | 826 | self.write_internal(address.into(), write, true, timeout) |
| @@ -828,6 +835,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 828 | /// | 835 | /// |
| 829 | /// The buffers are concatenated in a single write transaction. | 836 | /// The buffers are concatenated in a single write transaction. |
| 830 | pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { | 837 | pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { |
| 838 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 839 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 831 | let timeout = self.timeout(); | 840 | let timeout = self.timeout(); |
| 832 | 841 | ||
| 833 | if write.is_empty() { | 842 | if write.is_empty() { |
| @@ -851,6 +860,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 851 | 860 | ||
| 852 | /// Read. | 861 | /// Read. |
| 853 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { | 862 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { |
| 863 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 864 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 854 | let timeout = self.timeout(); | 865 | let timeout = self.timeout(); |
| 855 | 866 | ||
| 856 | if buffer.is_empty() { | 867 | if buffer.is_empty() { |
| @@ -863,6 +874,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 863 | 874 | ||
| 864 | /// Write, restart, read. | 875 | /// Write, restart, read. |
| 865 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { | 876 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { |
| 877 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 878 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 866 | let timeout = self.timeout(); | 879 | let timeout = self.timeout(); |
| 867 | 880 | ||
| 868 | if write.is_empty() { | 881 | if write.is_empty() { |
| @@ -888,6 +901,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 888 | /// | 901 | /// |
| 889 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | 902 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction |
| 890 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | 903 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { |
| 904 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 905 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 891 | let _ = addr; | 906 | let _ = addr; |
| 892 | let _ = operations; | 907 | let _ = operations; |
| 893 | todo!() | 908 | todo!() |
| @@ -1181,7 +1196,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1181 | 1196 | ||
| 1182 | let regs = self.info.regs; | 1197 | let regs = self.info.regs; |
| 1183 | 1198 | ||
| 1184 | let dma_transfer = unsafe { | 1199 | let mut dma_transfer = unsafe { |
| 1185 | regs.cr1().modify(|w| { | 1200 | regs.cr1().modify(|w| { |
| 1186 | w.set_rxdmaen(true); | 1201 | w.set_rxdmaen(true); |
| 1187 | w.set_stopie(true); | 1202 | w.set_stopie(true); |
| @@ -1220,6 +1235,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1220 | regs.cr1().modify(|w| w.set_tcie(true)); | 1235 | regs.cr1().modify(|w| w.set_tcie(true)); |
| 1221 | Poll::Pending | 1236 | Poll::Pending |
| 1222 | } else if isr.stopf() { | 1237 | } else if isr.stopf() { |
| 1238 | remaining_len = remaining_len.saturating_add(dma_transfer.get_remaining_transfers() as usize); | ||
| 1223 | regs.icr().write(|reg| reg.set_stopcf(true)); | 1239 | regs.icr().write(|reg| reg.set_stopcf(true)); |
| 1224 | let poll = Poll::Ready(Ok(total_len - remaining_len)); | 1240 | let poll = Poll::Ready(Ok(total_len - remaining_len)); |
| 1225 | poll | 1241 | poll |
| @@ -1229,6 +1245,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1229 | }) | 1245 | }) |
| 1230 | .await?; | 1246 | .await?; |
| 1231 | 1247 | ||
| 1248 | dma_transfer.request_pause(); | ||
| 1232 | dma_transfer.await; | 1249 | dma_transfer.await; |
| 1233 | 1250 | ||
| 1234 | drop(on_drop); | 1251 | drop(on_drop); |
| @@ -1258,7 +1275,8 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1258 | w.set_txdmaen(false); | 1275 | w.set_txdmaen(false); |
| 1259 | w.set_stopie(false); | 1276 | w.set_stopie(false); |
| 1260 | w.set_tcie(false); | 1277 | w.set_tcie(false); |
| 1261 | }) | 1278 | }); |
| 1279 | regs.isr().write(|w| w.set_txe(true)); | ||
| 1262 | }); | 1280 | }); |
| 1263 | 1281 | ||
| 1264 | let state = self.state; | 1282 | let state = self.state; |
| @@ -1281,6 +1299,11 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1281 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | 1299 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); |
| 1282 | Poll::Pending | 1300 | Poll::Pending |
| 1283 | } else if isr.stopf() { | 1301 | } else if isr.stopf() { |
| 1302 | let mut leftover_bytes = dma_transfer.get_remaining_transfers(); | ||
| 1303 | if !self.info.regs.isr().read().txe() { | ||
| 1304 | leftover_bytes = leftover_bytes.saturating_add(1); | ||
| 1305 | } | ||
| 1306 | remaining_len = remaining_len.saturating_add(leftover_bytes as usize); | ||
| 1284 | self.info.regs.icr().write(|reg| reg.set_stopcf(true)); | 1307 | self.info.regs.icr().write(|reg| reg.set_stopcf(true)); |
| 1285 | if remaining_len > 0 { | 1308 | if remaining_len > 0 { |
| 1286 | dma_transfer.request_pause(); | 1309 | dma_transfer.request_pause(); |
| @@ -1294,6 +1317,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1294 | }) | 1317 | }) |
| 1295 | .await?; | 1318 | .await?; |
| 1296 | 1319 | ||
| 1320 | dma_transfer.request_pause(); | ||
| 1297 | dma_transfer.await; | 1321 | dma_transfer.await; |
| 1298 | 1322 | ||
| 1299 | drop(on_drop); | 1323 | drop(on_drop); |
diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index b6d3daf54..df077a3ae 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs | |||
| @@ -3,12 +3,13 @@ | |||
| 3 | use embassy_futures::join::join; | 3 | use embassy_futures::join::join; |
| 4 | use stm32_metapac::spi::vals; | 4 | use stm32_metapac::spi::vals; |
| 5 | 5 | ||
| 6 | use crate::dma::{ringbuffer, ChannelAndRequest, ReadableRingBuffer, TransferOptions, WritableRingBuffer}; | 6 | use crate::Peri; |
| 7 | use crate::dma::{ChannelAndRequest, ReadableRingBuffer, TransferOptions, WritableRingBuffer, ringbuffer}; | ||
| 7 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; | 8 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; |
| 8 | use crate::mode::Async; | 9 | use crate::mode::Async; |
| 10 | use crate::spi::mode::Master; | ||
| 9 | use crate::spi::{Config as SpiConfig, RegsExt as _, *}; | 11 | use crate::spi::{Config as SpiConfig, RegsExt as _, *}; |
| 10 | use crate::time::Hertz; | 12 | use crate::time::Hertz; |
| 11 | use crate::Peri; | ||
| 12 | 13 | ||
| 13 | /// I2S mode | 14 | /// I2S mode |
| 14 | #[derive(Copy, Clone)] | 15 | #[derive(Copy, Clone)] |
| @@ -225,7 +226,7 @@ impl<'s, 'd, W: Word> Reader<'s, 'd, W> { | |||
| 225 | pub struct I2S<'d, W: Word> { | 226 | pub struct I2S<'d, W: Word> { |
| 226 | #[allow(dead_code)] | 227 | #[allow(dead_code)] |
| 227 | mode: Mode, | 228 | mode: Mode, |
| 228 | spi: Spi<'d, Async>, | 229 | spi: Spi<'d, Async, Master>, |
| 229 | txsd: Option<Peri<'d, AnyPin>>, | 230 | txsd: Option<Peri<'d, AnyPin>>, |
| 230 | rxsd: Option<Peri<'d, AnyPin>>, | 231 | rxsd: Option<Peri<'d, AnyPin>>, |
| 231 | ws: Option<Peri<'d, AnyPin>>, | 232 | ws: Option<Peri<'d, AnyPin>>, |
diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 670d8332c..e1d8b1c2a 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | //! Inter-Process Communication Controller (IPCC) | 1 | //! Inter-Process Communication Controller (IPCC) |
| 2 | 2 | ||
| 3 | use core::future::poll_fn; | 3 | use core::future::poll_fn; |
| 4 | use core::sync::atomic::{compiler_fence, Ordering}; | 4 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 5 | use core::task::Poll; | 5 | use core::task::Poll; |
| 6 | 6 | ||
| 7 | use embassy_sync::waitqueue::AtomicWaker; | 7 | use embassy_sync::waitqueue::AtomicWaker; |
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 4ea38b414..5b338a28b 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | #![cfg_attr(not(test), no_std)] | 1 | #![cfg_attr(not(test), no_std)] |
| 2 | #![allow(async_fn_in_trait)] | 2 | #![allow(async_fn_in_trait)] |
| 3 | #![allow(unsafe_op_in_unsafe_fn)] | ||
| 3 | #![cfg_attr( | 4 | #![cfg_attr( |
| 4 | docsrs, | 5 | docsrs, |
| 5 | doc = "<div style='padding:30px;background:#810;color:#fff;text-align:center;'><p>You might want to <a href='https://docs.embassy.dev/embassy-stm32'>browse the `embassy-stm32` documentation on the Embassy website</a> instead.</p><p>The documentation here on `docs.rs` is built for a single chip only (stm32h7, stm32h7rs55 in particular), while on the Embassy website you can pick your exact chip from the top menu. Available peripherals and their APIs change depending on the chip.</p></div>\n\n" | 6 | doc = "<div style='padding:30px;background:#810;color:#fff;text-align:center;'><p>You might want to <a href='https://docs.embassy.dev/embassy-stm32'>browse the `embassy-stm32` documentation on the Embassy website</a> instead.</p><p>The documentation here on `docs.rs` is built for a single chip only (stm32h7, stm32h7rs55 in particular), while on the Embassy website you can pick your exact chip from the top menu. Available peripherals and their APIs change depending on the chip.</p></div>\n\n" |
| @@ -53,6 +54,8 @@ pub mod timer; | |||
| 53 | 54 | ||
| 54 | #[cfg(adc)] | 55 | #[cfg(adc)] |
| 55 | pub mod adc; | 56 | pub mod adc; |
| 57 | #[cfg(backup_sram)] | ||
| 58 | pub mod backup_sram; | ||
| 56 | #[cfg(can)] | 59 | #[cfg(can)] |
| 57 | pub mod can; | 60 | pub mod can; |
| 58 | // FIXME: Cordic driver cause stm32u5a5zj crash | 61 | // FIXME: Cordic driver cause stm32u5a5zj crash |
| @@ -194,7 +197,7 @@ macro_rules! bind_interrupts { | |||
| 194 | 197 | ||
| 195 | $( | 198 | $( |
| 196 | #[allow(non_snake_case)] | 199 | #[allow(non_snake_case)] |
| 197 | #[no_mangle] | 200 | #[unsafe(no_mangle)] |
| 198 | $(#[cfg($cond_irq)])? | 201 | $(#[cfg($cond_irq)])? |
| 199 | $(#[doc = $doc])* | 202 | $(#[doc = $doc])* |
| 200 | unsafe extern "C" fn $irq() { | 203 | unsafe extern "C" fn $irq() { |
| @@ -222,7 +225,7 @@ macro_rules! bind_interrupts { | |||
| 222 | } | 225 | } |
| 223 | 226 | ||
| 224 | // Reexports | 227 | // Reexports |
| 225 | pub use _generated::{peripherals, Peripherals}; | 228 | pub use _generated::{Peripherals, peripherals}; |
| 226 | pub use embassy_hal_internal::{Peri, PeripheralType}; | 229 | pub use embassy_hal_internal::{Peri, PeripheralType}; |
| 227 | #[cfg(feature = "unstable-pac")] | 230 | #[cfg(feature = "unstable-pac")] |
| 228 | pub use stm32_metapac as pac; | 231 | pub use stm32_metapac as pac; |
| @@ -240,6 +243,14 @@ pub struct Config { | |||
| 240 | /// RCC config. | 243 | /// RCC config. |
| 241 | pub rcc: rcc::Config, | 244 | pub rcc: rcc::Config, |
| 242 | 245 | ||
| 246 | #[cfg(feature = "low-power")] | ||
| 247 | /// RTC config | ||
| 248 | pub rtc: rtc::RtcConfig, | ||
| 249 | |||
| 250 | #[cfg(feature = "low-power")] | ||
| 251 | /// Minimum time to stop | ||
| 252 | pub min_stop_pause: embassy_time::Duration, | ||
| 253 | |||
| 243 | /// Enable debug during sleep and stop. | 254 | /// Enable debug during sleep and stop. |
| 244 | /// | 255 | /// |
| 245 | /// May increase power consumption. Defaults to true. | 256 | /// May increase power consumption. Defaults to true. |
| @@ -293,6 +304,10 @@ impl Default for Config { | |||
| 293 | fn default() -> Self { | 304 | fn default() -> Self { |
| 294 | Self { | 305 | Self { |
| 295 | rcc: Default::default(), | 306 | rcc: Default::default(), |
| 307 | #[cfg(feature = "low-power")] | ||
| 308 | rtc: Default::default(), | ||
| 309 | #[cfg(feature = "low-power")] | ||
| 310 | min_stop_pause: embassy_time::Duration::from_millis(250), | ||
| 296 | #[cfg(dbgmcu)] | 311 | #[cfg(dbgmcu)] |
| 297 | enable_debug_during_sleep: true, | 312 | enable_debug_during_sleep: true, |
| 298 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba))] | 313 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba))] |
| @@ -632,6 +647,12 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 632 | exti::init(cs); | 647 | exti::init(cs); |
| 633 | 648 | ||
| 634 | rcc::init_rcc(cs, config.rcc); | 649 | rcc::init_rcc(cs, config.rcc); |
| 650 | |||
| 651 | #[cfg(feature = "low-power")] | ||
| 652 | crate::rtc::init_rtc(cs, config.rtc); | ||
| 653 | |||
| 654 | #[cfg(feature = "low-power")] | ||
| 655 | crate::time_driver::get_driver().set_min_stop_pause(cs, config.min_stop_pause); | ||
| 635 | } | 656 | } |
| 636 | 657 | ||
| 637 | p | 658 | p |
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 342f73bc8..696dfe83f 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs | |||
| @@ -41,12 +41,6 @@ | |||
| 41 | //! config.enable_debug_during_sleep = false; | 41 | //! config.enable_debug_during_sleep = false; |
| 42 | //! let p = embassy_stm32::init(config); | 42 | //! let p = embassy_stm32::init(config); |
| 43 | //! | 43 | //! |
| 44 | //! // give the RTC to the executor... | ||
| 45 | //! let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); | ||
| 46 | //! static RTC: StaticCell<Rtc> = StaticCell::new(); | ||
| 47 | //! let rtc = RTC.init(rtc); | ||
| 48 | //! embassy_stm32::low_power::stop_with_rtc(rtc); | ||
| 49 | //! | ||
| 50 | //! // your application here... | 44 | //! // your application here... |
| 51 | //! } | 45 | //! } |
| 52 | //! ``` | 46 | //! ``` |
| @@ -56,27 +50,72 @@ | |||
| 56 | 50 | ||
| 57 | use core::arch::asm; | 51 | use core::arch::asm; |
| 58 | use core::marker::PhantomData; | 52 | use core::marker::PhantomData; |
| 59 | use core::sync::atomic::{compiler_fence, Ordering}; | 53 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 60 | 54 | ||
| 61 | use cortex_m::peripheral::SCB; | 55 | use cortex_m::peripheral::SCB; |
| 56 | use critical_section::CriticalSection; | ||
| 62 | use embassy_executor::*; | 57 | use embassy_executor::*; |
| 63 | 58 | ||
| 64 | use crate::interrupt; | 59 | use crate::interrupt; |
| 65 | use crate::time_driver::{get_driver, RtcDriver}; | 60 | use crate::time_driver::get_driver; |
| 66 | 61 | ||
| 67 | const THREAD_PENDER: usize = usize::MAX; | 62 | const THREAD_PENDER: usize = usize::MAX; |
| 68 | 63 | ||
| 69 | use crate::rtc::Rtc; | ||
| 70 | |||
| 71 | static mut EXECUTOR: Option<Executor> = None; | 64 | static mut EXECUTOR: Option<Executor> = None; |
| 72 | 65 | ||
| 66 | /// Prevent the device from going into the stop mode if held | ||
| 67 | pub struct DeviceBusy(StopMode); | ||
| 68 | |||
| 69 | impl DeviceBusy { | ||
| 70 | /// Create a new DeviceBusy with stop1. | ||
| 71 | pub fn new_stop1() -> Self { | ||
| 72 | Self::new(StopMode::Stop1) | ||
| 73 | } | ||
| 74 | |||
| 75 | /// Create a new DeviceBusy with stop2. | ||
| 76 | pub fn new_stop2() -> Self { | ||
| 77 | Self::new(StopMode::Stop2) | ||
| 78 | } | ||
| 79 | |||
| 80 | /// Create a new DeviceBusy. | ||
| 81 | pub fn new(stop_mode: StopMode) -> Self { | ||
| 82 | critical_section::with(|_| unsafe { | ||
| 83 | match stop_mode { | ||
| 84 | StopMode::Stop1 => { | ||
| 85 | crate::rcc::REFCOUNT_STOP1 += 1; | ||
| 86 | } | ||
| 87 | StopMode::Stop2 => { | ||
| 88 | crate::rcc::REFCOUNT_STOP2 += 1; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | }); | ||
| 92 | |||
| 93 | Self(stop_mode) | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | impl Drop for DeviceBusy { | ||
| 98 | fn drop(&mut self) { | ||
| 99 | critical_section::with(|_| unsafe { | ||
| 100 | match self.0 { | ||
| 101 | StopMode::Stop1 => { | ||
| 102 | crate::rcc::REFCOUNT_STOP1 -= 1; | ||
| 103 | } | ||
| 104 | StopMode::Stop2 => { | ||
| 105 | crate::rcc::REFCOUNT_STOP2 -= 1; | ||
| 106 | } | ||
| 107 | } | ||
| 108 | }); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 73 | #[cfg(not(stm32u0))] | 112 | #[cfg(not(stm32u0))] |
| 74 | foreach_interrupt! { | 113 | foreach_interrupt! { |
| 75 | (RTC, rtc, $block:ident, WKUP, $irq:ident) => { | 114 | (RTC, rtc, $block:ident, WKUP, $irq:ident) => { |
| 76 | #[interrupt] | 115 | #[interrupt] |
| 77 | #[allow(non_snake_case)] | 116 | #[allow(non_snake_case)] |
| 78 | unsafe fn $irq() { | 117 | unsafe fn $irq() { |
| 79 | EXECUTOR.as_mut().unwrap().on_wakeup_irq(); | 118 | Executor::on_wakeup_irq(); |
| 80 | } | 119 | } |
| 81 | }; | 120 | }; |
| 82 | } | 121 | } |
| @@ -87,31 +126,21 @@ foreach_interrupt! { | |||
| 87 | #[interrupt] | 126 | #[interrupt] |
| 88 | #[allow(non_snake_case)] | 127 | #[allow(non_snake_case)] |
| 89 | unsafe fn $irq() { | 128 | unsafe fn $irq() { |
| 90 | EXECUTOR.as_mut().unwrap().on_wakeup_irq(); | 129 | Executor::on_wakeup_irq(); |
| 91 | } | 130 | } |
| 92 | }; | 131 | }; |
| 93 | } | 132 | } |
| 94 | 133 | ||
| 95 | #[allow(dead_code)] | ||
| 96 | pub(crate) unsafe fn on_wakeup_irq() { | ||
| 97 | EXECUTOR.as_mut().unwrap().on_wakeup_irq(); | ||
| 98 | } | ||
| 99 | |||
| 100 | /// Configure STOP mode with RTC. | ||
| 101 | pub fn stop_with_rtc(rtc: &'static Rtc) { | ||
| 102 | unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc) | ||
| 103 | } | ||
| 104 | |||
| 105 | /// Get whether the core is ready to enter the given stop mode. | 134 | /// Get whether the core is ready to enter the given stop mode. |
| 106 | /// | 135 | /// |
| 107 | /// This will return false if some peripheral driver is in use that | 136 | /// This will return false if some peripheral driver is in use that |
| 108 | /// prevents entering the given stop mode. | 137 | /// prevents entering the given stop mode. |
| 109 | pub fn stop_ready(stop_mode: StopMode) -> bool { | 138 | pub fn stop_ready(stop_mode: StopMode) -> bool { |
| 110 | match unsafe { EXECUTOR.as_mut().unwrap() }.stop_mode() { | 139 | critical_section::with(|cs| match Executor::stop_mode(cs) { |
| 111 | Some(StopMode::Stop2) => true, | 140 | Some(StopMode::Stop2) => true, |
| 112 | Some(StopMode::Stop1) => stop_mode == StopMode::Stop1, | 141 | Some(StopMode::Stop1) => stop_mode == StopMode::Stop1, |
| 113 | None => false, | 142 | None => false, |
| 114 | } | 143 | }) |
| 115 | } | 144 | } |
| 116 | 145 | ||
| 117 | /// Available Stop modes. | 146 | /// Available Stop modes. |
| @@ -124,10 +153,10 @@ pub enum StopMode { | |||
| 124 | Stop2, | 153 | Stop2, |
| 125 | } | 154 | } |
| 126 | 155 | ||
| 127 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32u0))] | 156 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))] |
| 128 | use stm32_metapac::pwr::vals::Lpms; | 157 | use stm32_metapac::pwr::vals::Lpms; |
| 129 | 158 | ||
| 130 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32u0))] | 159 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))] |
| 131 | impl Into<Lpms> for StopMode { | 160 | impl Into<Lpms> for StopMode { |
| 132 | fn into(self) -> Lpms { | 161 | fn into(self) -> Lpms { |
| 133 | match self { | 162 | match self { |
| @@ -154,7 +183,6 @@ pub struct Executor { | |||
| 154 | inner: raw::Executor, | 183 | inner: raw::Executor, |
| 155 | not_send: PhantomData<*mut ()>, | 184 | not_send: PhantomData<*mut ()>, |
| 156 | scb: SCB, | 185 | scb: SCB, |
| 157 | time_driver: &'static RtcDriver, | ||
| 158 | } | 186 | } |
| 159 | 187 | ||
| 160 | impl Executor { | 188 | impl Executor { |
| @@ -167,7 +195,6 @@ impl Executor { | |||
| 167 | inner: raw::Executor::new(THREAD_PENDER as *mut ()), | 195 | inner: raw::Executor::new(THREAD_PENDER as *mut ()), |
| 168 | not_send: PhantomData, | 196 | not_send: PhantomData, |
| 169 | scb: cortex_m::Peripherals::steal().SCB, | 197 | scb: cortex_m::Peripherals::steal().SCB, |
| 170 | time_driver: get_driver(), | ||
| 171 | }); | 198 | }); |
| 172 | 199 | ||
| 173 | let executor = EXECUTOR.as_mut().unwrap(); | 200 | let executor = EXECUTOR.as_mut().unwrap(); |
| @@ -176,21 +203,31 @@ impl Executor { | |||
| 176 | }) | 203 | }) |
| 177 | } | 204 | } |
| 178 | 205 | ||
| 179 | unsafe fn on_wakeup_irq(&mut self) { | 206 | pub(crate) unsafe fn on_wakeup_irq() { |
| 180 | self.time_driver.resume_time(); | 207 | critical_section::with(|cs| { |
| 181 | trace!("low power: resume"); | 208 | #[cfg(stm32wlex)] |
| 182 | } | 209 | { |
| 183 | 210 | let extscr = crate::pac::PWR.extscr().read(); | |
| 184 | pub(self) fn stop_with_rtc(&mut self, rtc: &'static Rtc) { | 211 | if extscr.c1stop2f() || extscr.c1stopf() { |
| 185 | self.time_driver.set_rtc(rtc); | 212 | // when we wake from any stop mode we need to re-initialize the rcc |
| 186 | 213 | crate::rcc::apply_resume_config(); | |
| 187 | rtc.enable_wakeup_line(); | 214 | if extscr.c1stop2f() { |
| 188 | 215 | // when we wake from STOP2, we need to re-initialize the time driver | |
| 189 | trace!("low power: stop with rtc configured"); | 216 | crate::time_driver::init_timer(cs); |
| 217 | // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer) | ||
| 218 | // and given that we just woke from STOP2, we can reset them | ||
| 219 | crate::rcc::REFCOUNT_STOP2 = 0; | ||
| 220 | crate::rcc::REFCOUNT_STOP1 = 0; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | } | ||
| 224 | get_driver().resume_time(cs); | ||
| 225 | trace!("low power: resume"); | ||
| 226 | }); | ||
| 190 | } | 227 | } |
| 191 | 228 | ||
| 192 | fn stop_mode(&self) -> Option<StopMode> { | 229 | fn stop_mode(_cs: CriticalSection) -> Option<StopMode> { |
| 193 | if unsafe { crate::rcc::REFCOUNT_STOP2 == 0 } && unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { | 230 | if unsafe { crate::rcc::REFCOUNT_STOP2 == 0 && crate::rcc::REFCOUNT_STOP1 == 0 } { |
| 194 | Some(StopMode::Stop2) | 231 | Some(StopMode::Stop2) |
| 195 | } else if unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { | 232 | } else if unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { |
| 196 | Some(StopMode::Stop1) | 233 | Some(StopMode::Stop1) |
| @@ -201,7 +238,7 @@ impl Executor { | |||
| 201 | 238 | ||
| 202 | #[allow(unused_variables)] | 239 | #[allow(unused_variables)] |
| 203 | fn configure_stop(&mut self, stop_mode: StopMode) { | 240 | fn configure_stop(&mut self, stop_mode: StopMode) { |
| 204 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba))] | 241 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba, stm32wlex))] |
| 205 | crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); | 242 | crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); |
| 206 | #[cfg(stm32h5)] | 243 | #[cfg(stm32h5)] |
| 207 | crate::pac::PWR.pmcr().modify(|v| { | 244 | crate::pac::PWR.pmcr().modify(|v| { |
| @@ -213,17 +250,22 @@ impl Executor { | |||
| 213 | 250 | ||
| 214 | fn configure_pwr(&mut self) { | 251 | fn configure_pwr(&mut self) { |
| 215 | self.scb.clear_sleepdeep(); | 252 | self.scb.clear_sleepdeep(); |
| 253 | // Clear any previous stop flags | ||
| 254 | #[cfg(stm32wlex)] | ||
| 255 | crate::pac::PWR.extscr().modify(|w| { | ||
| 256 | w.set_c1cssf(true); | ||
| 257 | }); | ||
| 216 | 258 | ||
| 217 | compiler_fence(Ordering::SeqCst); | 259 | compiler_fence(Ordering::SeqCst); |
| 218 | 260 | ||
| 219 | let stop_mode = self.stop_mode(); | 261 | let stop_mode = critical_section::with(|cs| Self::stop_mode(cs)); |
| 220 | 262 | ||
| 221 | if stop_mode.is_none() { | 263 | if stop_mode.is_none() { |
| 222 | trace!("low power: not ready to stop"); | 264 | trace!("low power: not ready to stop"); |
| 223 | return; | 265 | return; |
| 224 | } | 266 | } |
| 225 | 267 | ||
| 226 | if self.time_driver.pause_time().is_err() { | 268 | if get_driver().pause_time().is_err() { |
| 227 | trace!("low power: failed to pause time"); | 269 | trace!("low power: failed to pause time"); |
| 228 | return; | 270 | return; |
| 229 | } | 271 | } |
| @@ -266,6 +308,19 @@ impl Executor { | |||
| 266 | executor.inner.poll(); | 308 | executor.inner.poll(); |
| 267 | self.configure_pwr(); | 309 | self.configure_pwr(); |
| 268 | asm!("wfe"); | 310 | asm!("wfe"); |
| 311 | #[cfg(stm32wlex)] | ||
| 312 | { | ||
| 313 | let es = crate::pac::PWR.extscr().read(); | ||
| 314 | match (es.c1stopf(), es.c1stop2f()) { | ||
| 315 | (true, false) => debug!("low power: wake from STOP1"), | ||
| 316 | (false, true) => debug!("low power: wake from STOP2"), | ||
| 317 | (true, true) => debug!("low power: wake from STOP1 and STOP2 ???"), | ||
| 318 | (false, false) => trace!("low power: stop mode not entered"), | ||
| 319 | }; | ||
| 320 | crate::pac::PWR.extscr().modify(|w| { | ||
| 321 | w.set_c1cssf(false); | ||
| 322 | }); | ||
| 323 | } | ||
| 269 | }; | 324 | }; |
| 270 | } | 325 | } |
| 271 | } | 326 | } |
diff --git a/embassy-stm32/src/lptim/pwm.rs b/embassy-stm32/src/lptim/pwm.rs index 96af9f4d9..a69db3caf 100644 --- a/embassy-stm32/src/lptim/pwm.rs +++ b/embassy-stm32/src/lptim/pwm.rs | |||
| @@ -4,12 +4,12 @@ use core::marker::PhantomData; | |||
| 4 | 4 | ||
| 5 | use embassy_hal_internal::Peri; | 5 | use embassy_hal_internal::Peri; |
| 6 | 6 | ||
| 7 | use super::timer::Timer; | ||
| 8 | #[cfg(not(any(lptim_v2a, lptim_v2b)))] | 7 | #[cfg(not(any(lptim_v2a, lptim_v2b)))] |
| 9 | use super::OutputPin; | 8 | use super::OutputPin; |
| 10 | #[cfg(any(lptim_v2a, lptim_v2b))] | 9 | use super::timer::Timer; |
| 11 | use super::{channel::Channel, timer::ChannelDirection, Channel1Pin, Channel2Pin}; | ||
| 12 | use super::{BasicInstance, Instance}; | 10 | use super::{BasicInstance, Instance}; |
| 11 | #[cfg(any(lptim_v2a, lptim_v2b))] | ||
| 12 | use super::{Channel1Pin, Channel2Pin, channel::Channel, timer::ChannelDirection}; | ||
| 13 | #[cfg(gpio_v2)] | 13 | #[cfg(gpio_v2)] |
| 14 | use crate::gpio::Pull; | 14 | use crate::gpio::Pull; |
| 15 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 15 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
diff --git a/embassy-stm32/src/ltdc.rs b/embassy-stm32/src/ltdc.rs index 0f6ef569c..de2db9872 100644 --- a/embassy-stm32/src/ltdc.rs +++ b/embassy-stm32/src/ltdc.rs | |||
| @@ -14,7 +14,7 @@ use stm32_metapac::ltdc::vals::{Bf1, Bf2, Cfuif, Clif, Crrif, Cterrif, Pf, Vbr}; | |||
| 14 | use crate::gpio::{AfType, OutputType, Speed}; | 14 | use crate::gpio::{AfType, OutputType, Speed}; |
| 15 | use crate::interrupt::typelevel::Interrupt; | 15 | use crate::interrupt::typelevel::Interrupt; |
| 16 | use crate::interrupt::{self}; | 16 | use crate::interrupt::{self}; |
| 17 | use crate::{peripherals, rcc, Peri}; | 17 | use crate::{Peri, peripherals, rcc}; |
| 18 | 18 | ||
| 19 | static LTDC_WAKER: AtomicWaker = AtomicWaker::new(); | 19 | static LTDC_WAKER: AtomicWaker = AtomicWaker::new(); |
| 20 | 20 | ||
diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index e36719ef3..ac8d5de21 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs | |||
| @@ -3,10 +3,10 @@ | |||
| 3 | 3 | ||
| 4 | use embassy_hal_internal::PeripheralType; | 4 | use embassy_hal_internal::PeripheralType; |
| 5 | 5 | ||
| 6 | use crate::Peri; | ||
| 6 | use crate::pac::opamp::vals::*; | 7 | use crate::pac::opamp::vals::*; |
| 7 | #[cfg(not(any(stm32g4, stm32f3)))] | 8 | #[cfg(not(any(stm32g4, stm32f3)))] |
| 8 | use crate::rcc::RccInfo; | 9 | use crate::rcc::RccInfo; |
| 9 | use crate::Peri; | ||
| 10 | 10 | ||
| 11 | /// Performs a busy-wait delay for a specified number of microseconds. | 11 | /// Performs a busy-wait delay for a specified number of microseconds. |
| 12 | #[cfg(opamp_v5)] | 12 | #[cfg(opamp_v5)] |
diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index d93cecb69..592a8594a 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs | |||
| @@ -12,14 +12,14 @@ use embassy_hal_internal::PeripheralType; | |||
| 12 | pub use enums::*; | 12 | pub use enums::*; |
| 13 | use stm32_metapac::octospi::vals::{PhaseMode, SizeInBits}; | 13 | use stm32_metapac::octospi::vals::{PhaseMode, SizeInBits}; |
| 14 | 14 | ||
| 15 | use crate::dma::{word, ChannelAndRequest}; | 15 | use crate::dma::{ChannelAndRequest, word}; |
| 16 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | 16 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
| 17 | use crate::mode::{Async, Blocking, Mode as PeriMode}; | 17 | use crate::mode::{Async, Blocking, Mode as PeriMode}; |
| 18 | use crate::pac::octospi::{vals, Octospi as Regs}; | 18 | use crate::pac::octospi::{Octospi as Regs, vals}; |
| 19 | #[cfg(octospim_v1)] | 19 | #[cfg(octospim_v1)] |
| 20 | use crate::pac::octospim::Octospim; | 20 | use crate::pac::octospim::Octospim; |
| 21 | use crate::rcc::{self, RccPeripheral}; | 21 | use crate::rcc::{self, RccPeripheral}; |
| 22 | use crate::{peripherals, Peri}; | 22 | use crate::{Peri, peripherals}; |
| 23 | 23 | ||
| 24 | /// OPSI driver config. | 24 | /// OPSI driver config. |
| 25 | #[derive(Clone, Copy)] | 25 | #[derive(Clone, Copy)] |
diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index b03cd9009..bb4f4f1d0 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs | |||
| @@ -14,7 +14,7 @@ use crate::gpio::{AfType, AnyPin, OutputType, Pull, Speed}; | |||
| 14 | use crate::mode::{Async, Blocking, Mode as PeriMode}; | 14 | use crate::mode::{Async, Blocking, Mode as PeriMode}; |
| 15 | use crate::pac::quadspi::Quadspi as Regs; | 15 | use crate::pac::quadspi::Quadspi as Regs; |
| 16 | use crate::rcc::{self, RccPeripheral}; | 16 | use crate::rcc::{self, RccPeripheral}; |
| 17 | use crate::{peripherals, Peri}; | 17 | use crate::{Peri, peripherals}; |
| 18 | 18 | ||
| 19 | /// QSPI transfer configuration. | 19 | /// QSPI transfer configuration. |
| 20 | #[derive(Clone, Copy)] | 20 | #[derive(Clone, Copy)] |
diff --git a/embassy-stm32/src/rcc/bd.rs b/embassy-stm32/src/rcc/bd.rs index e5f52c3c7..9cad03227 100644 --- a/embassy-stm32/src/rcc/bd.rs +++ b/embassy-stm32/src/rcc/bd.rs | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | use core::sync::atomic::{compiler_fence, Ordering}; | 1 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 2 | 2 | ||
| 3 | use crate::pac::common::{Reg, RW}; | 3 | use crate::pac::common::{RW, Reg}; |
| 4 | #[cfg(backup_sram)] | ||
| 5 | use crate::pac::pwr::vals::Retention; | ||
| 4 | pub use crate::pac::rcc::vals::Rtcsel as RtcClockSource; | 6 | pub use crate::pac::rcc::vals::Rtcsel as RtcClockSource; |
| 5 | use crate::time::Hertz; | 7 | use crate::time::Hertz; |
| 6 | 8 | ||
| @@ -52,9 +54,9 @@ impl From<LseDrive> for crate::pac::rcc::vals::Lsedrv { | |||
| 52 | } | 54 | } |
| 53 | } | 55 | } |
| 54 | 56 | ||
| 55 | #[cfg(not(any(rtc_v2l0, rtc_v2l1, stm32c0)))] | 57 | #[cfg(not(any(rtc_v2_l0, rtc_v2_l1, stm32c0)))] |
| 56 | type Bdcr = crate::pac::rcc::regs::Bdcr; | 58 | type Bdcr = crate::pac::rcc::regs::Bdcr; |
| 57 | #[cfg(any(rtc_v2l0, rtc_v2l1))] | 59 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] |
| 58 | type Bdcr = crate::pac::rcc::regs::Csr; | 60 | type Bdcr = crate::pac::rcc::regs::Csr; |
| 59 | #[cfg(any(stm32c0))] | 61 | #[cfg(any(stm32c0))] |
| 60 | type Bdcr = crate::pac::rcc::regs::Csr1; | 62 | type Bdcr = crate::pac::rcc::regs::Csr1; |
| @@ -78,9 +80,9 @@ fn unlock() { | |||
| 78 | } | 80 | } |
| 79 | 81 | ||
| 80 | fn bdcr() -> Reg<Bdcr, RW> { | 82 | fn bdcr() -> Reg<Bdcr, RW> { |
| 81 | #[cfg(any(rtc_v2l0, rtc_v2l1))] | 83 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] |
| 82 | return crate::pac::RCC.csr(); | 84 | return crate::pac::RCC.csr(); |
| 83 | #[cfg(not(any(rtc_v2l0, rtc_v2l1, stm32c0)))] | 85 | #[cfg(not(any(rtc_v2_l0, rtc_v2_l1, stm32c0)))] |
| 84 | return crate::pac::RCC.bdcr(); | 86 | return crate::pac::RCC.bdcr(); |
| 85 | #[cfg(any(stm32c0))] | 87 | #[cfg(any(stm32c0))] |
| 86 | return crate::pac::RCC.csr1(); | 88 | return crate::pac::RCC.csr1(); |
| @@ -91,6 +93,8 @@ pub struct LsConfig { | |||
| 91 | pub rtc: RtcClockSource, | 93 | pub rtc: RtcClockSource, |
| 92 | pub lsi: bool, | 94 | pub lsi: bool, |
| 93 | pub lse: Option<LseConfig>, | 95 | pub lse: Option<LseConfig>, |
| 96 | #[cfg(backup_sram)] | ||
| 97 | pub enable_backup_sram: bool, | ||
| 94 | } | 98 | } |
| 95 | 99 | ||
| 96 | impl LsConfig { | 100 | impl LsConfig { |
| @@ -115,6 +119,8 @@ impl LsConfig { | |||
| 115 | peripherals_clocked: false, | 119 | peripherals_clocked: false, |
| 116 | }), | 120 | }), |
| 117 | lsi: false, | 121 | lsi: false, |
| 122 | #[cfg(backup_sram)] | ||
| 123 | enable_backup_sram: false, | ||
| 118 | } | 124 | } |
| 119 | } | 125 | } |
| 120 | 126 | ||
| @@ -123,6 +129,8 @@ impl LsConfig { | |||
| 123 | rtc: RtcClockSource::LSI, | 129 | rtc: RtcClockSource::LSI, |
| 124 | lsi: true, | 130 | lsi: true, |
| 125 | lse: None, | 131 | lse: None, |
| 132 | #[cfg(backup_sram)] | ||
| 133 | enable_backup_sram: false, | ||
| 126 | } | 134 | } |
| 127 | } | 135 | } |
| 128 | 136 | ||
| @@ -131,6 +139,8 @@ impl LsConfig { | |||
| 131 | rtc: RtcClockSource::DISABLE, | 139 | rtc: RtcClockSource::DISABLE, |
| 132 | lsi: false, | 140 | lsi: false, |
| 133 | lse: None, | 141 | lse: None, |
| 142 | #[cfg(backup_sram)] | ||
| 143 | enable_backup_sram: false, | ||
| 134 | } | 144 | } |
| 135 | } | 145 | } |
| 136 | } | 146 | } |
| @@ -200,6 +210,22 @@ impl LsConfig { | |||
| 200 | while !csr.read().lsi1rdy() {} | 210 | while !csr.read().lsi1rdy() {} |
| 201 | } | 211 | } |
| 202 | 212 | ||
| 213 | // Enable backup regulator for peristent battery backed sram | ||
| 214 | #[cfg(backup_sram)] | ||
| 215 | { | ||
| 216 | unsafe { super::BKSRAM_RETAINED = crate::pac::PWR.bdcr().read().bren() == Retention::PRESERVED }; | ||
| 217 | |||
| 218 | crate::pac::PWR.bdcr().modify(|w| { | ||
| 219 | w.set_bren(match self.enable_backup_sram { | ||
| 220 | true => Retention::PRESERVED, | ||
| 221 | false => Retention::LOST, | ||
| 222 | }); | ||
| 223 | }); | ||
| 224 | |||
| 225 | // Wait for backup regulator voltage to stabilize | ||
| 226 | while self.enable_backup_sram && !crate::pac::PWR.bdsr().read().brrdy() {} | ||
| 227 | } | ||
| 228 | |||
| 203 | // backup domain configuration (LSEON, RTCEN, RTCSEL) is kept across resets. | 229 | // backup domain configuration (LSEON, RTCEN, RTCSEL) is kept across resets. |
| 204 | // once set, changing it requires a backup domain reset. | 230 | // once set, changing it requires a backup domain reset. |
| 205 | // first check if the configuration matches what we want. | 231 | // first check if the configuration matches what we want. |
| @@ -329,7 +355,7 @@ impl LsConfig { | |||
| 329 | if self.rtc != RtcClockSource::DISABLE { | 355 | if self.rtc != RtcClockSource::DISABLE { |
| 330 | #[cfg(not(rcc_n6))] | 356 | #[cfg(not(rcc_n6))] |
| 331 | bdcr().modify(|w| { | 357 | bdcr().modify(|w| { |
| 332 | #[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))] | 358 | #[cfg(any(rtc_v2_h7, rtc_v2_l4, rtc_v2_wb, rtc_v3_base, rtc_v3_u5))] |
| 333 | assert!(!w.lsecsson(), "RTC is not compatible with LSE CSS, yet."); | 359 | assert!(!w.lsecsson(), "RTC is not compatible with LSE CSS, yet."); |
| 334 | 360 | ||
| 335 | #[cfg(not(rcc_wba))] | 361 | #[cfg(not(rcc_wba))] |
diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index c2295bab6..99f22273d 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs | |||
| @@ -49,6 +49,10 @@ pub struct Config { | |||
| 49 | /// System Clock Configuration | 49 | /// System Clock Configuration |
| 50 | pub sys: Sysclk, | 50 | pub sys: Sysclk, |
| 51 | 51 | ||
| 52 | /// HSI48 Configuration | ||
| 53 | #[cfg(crs)] | ||
| 54 | pub hsi48: Option<super::Hsi48Config>, | ||
| 55 | |||
| 52 | pub ahb_pre: AHBPrescaler, | 56 | pub ahb_pre: AHBPrescaler, |
| 53 | pub apb1_pre: APBPrescaler, | 57 | pub apb1_pre: APBPrescaler, |
| 54 | 58 | ||
| @@ -68,6 +72,8 @@ impl Config { | |||
| 68 | }), | 72 | }), |
| 69 | hse: None, | 73 | hse: None, |
| 70 | sys: Sysclk::HSISYS, | 74 | sys: Sysclk::HSISYS, |
| 75 | #[cfg(crs)] | ||
| 76 | hsi48: Some(crate::rcc::Hsi48Config::new()), | ||
| 71 | ahb_pre: AHBPrescaler::DIV1, | 77 | ahb_pre: AHBPrescaler::DIV1, |
| 72 | apb1_pre: APBPrescaler::DIV1, | 78 | apb1_pre: APBPrescaler::DIV1, |
| 73 | ls: crate::rcc::LsConfig::new(), | 79 | ls: crate::rcc::LsConfig::new(), |
| @@ -127,6 +133,10 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 127 | } | 133 | } |
| 128 | }; | 134 | }; |
| 129 | 135 | ||
| 136 | // Configure HSI48 if required | ||
| 137 | #[cfg(crs)] | ||
| 138 | let hsi48 = config.hsi48.map(super::init_hsi48); | ||
| 139 | |||
| 130 | let rtc = config.ls.init(); | 140 | let rtc = config.ls.init(); |
| 131 | 141 | ||
| 132 | let sys = match config.sys { | 142 | let sys = match config.sys { |
| @@ -185,13 +195,13 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 185 | hsi: hsi, | 195 | hsi: hsi, |
| 186 | hsiker: hsiker, | 196 | hsiker: hsiker, |
| 187 | hse: hse, | 197 | hse: hse, |
| 198 | #[cfg(crs)] | ||
| 199 | hsi48: hsi48, | ||
| 188 | rtc: rtc, | 200 | rtc: rtc, |
| 189 | 201 | ||
| 190 | // TODO | 202 | // TODO |
| 191 | lsi: None, | 203 | lsi: None, |
| 192 | lse: None, | 204 | lse: None, |
| 193 | #[cfg(crs)] | ||
| 194 | hsi48: None, | ||
| 195 | ); | 205 | ); |
| 196 | 206 | ||
| 197 | RCC.ccipr() | 207 | RCC.ccipr() |
diff --git a/embassy-stm32/src/rcc/f247.rs b/embassy-stm32/src/rcc/f247.rs index 8f2e8db5f..d941054cd 100644 --- a/embassy-stm32/src/rcc/f247.rs +++ b/embassy-stm32/src/rcc/f247.rs | |||
| @@ -1,13 +1,13 @@ | |||
| 1 | use stm32_metapac::flash::vals::Latency; | 1 | use stm32_metapac::flash::vals::Latency; |
| 2 | 2 | ||
| 3 | #[cfg(any(stm32f4, stm32f7))] | ||
| 4 | use crate::pac::PWR; | ||
| 3 | #[cfg(any(stm32f413, stm32f423, stm32f412))] | 5 | #[cfg(any(stm32f413, stm32f423, stm32f412))] |
| 4 | pub use crate::pac::rcc::vals::Plli2ssrc as Plli2sSource; | 6 | pub use crate::pac::rcc::vals::Plli2ssrc as Plli2sSource; |
| 5 | pub use crate::pac::rcc::vals::{ | 7 | pub use crate::pac::rcc::vals::{ |
| 6 | Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, | 8 | Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, |
| 7 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, | 9 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, |
| 8 | }; | 10 | }; |
| 9 | #[cfg(any(stm32f4, stm32f7))] | ||
| 10 | use crate::pac::PWR; | ||
| 11 | use crate::pac::{FLASH, RCC}; | 11 | use crate::pac::{FLASH, RCC}; |
| 12 | use crate::time::Hertz; | 12 | use crate::time::Hertz; |
| 13 | 13 | ||
diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 331bab7a0..485edd390 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs | |||
| @@ -597,7 +597,10 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 597 | Hertz(24_000_000) => Usbrefcksel::MHZ24, | 597 | Hertz(24_000_000) => Usbrefcksel::MHZ24, |
| 598 | Hertz(26_000_000) => Usbrefcksel::MHZ26, | 598 | Hertz(26_000_000) => Usbrefcksel::MHZ26, |
| 599 | Hertz(32_000_000) => Usbrefcksel::MHZ32, | 599 | Hertz(32_000_000) => Usbrefcksel::MHZ32, |
| 600 | _ => panic!("cannot select USBPHYC reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val), | 600 | _ => panic!( |
| 601 | "cannot select USBPHYC reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", | ||
| 602 | clk_val | ||
| 603 | ), | ||
| 601 | }, | 604 | }, |
| 602 | None => Usbrefcksel::MHZ24, | 605 | None => Usbrefcksel::MHZ24, |
| 603 | }; | 606 | }; |
diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs index 81b89046e..584957c6d 100644 --- a/embassy-stm32/src/rcc/l.rs +++ b/embassy-stm32/src/rcc/l.rs | |||
| @@ -1,3 +1,6 @@ | |||
| 1 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 2 | use core::mem::MaybeUninit; | ||
| 3 | |||
| 1 | #[cfg(any(stm32l0, stm32l1))] | 4 | #[cfg(any(stm32l0, stm32l1))] |
| 2 | pub use crate::pac::pwr::vals::Vos as VoltageScale; | 5 | pub use crate::pac::pwr::vals::Vos as VoltageScale; |
| 3 | use crate::pac::rcc::regs::Cfgr; | 6 | use crate::pac::rcc::regs::Cfgr; |
| @@ -11,6 +14,42 @@ use crate::time::Hertz; | |||
| 11 | /// HSI speed | 14 | /// HSI speed |
| 12 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); | 15 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); |
| 13 | 16 | ||
| 17 | /// Saved RCC Config | ||
| 18 | /// | ||
| 19 | /// Used when exiting STOP2 to re-enable clocks to their last configured state | ||
| 20 | /// for chips that need it. | ||
| 21 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 22 | static mut RESUME_RCC_CONFIG: MaybeUninit<Config> = MaybeUninit::uninit(); | ||
| 23 | |||
| 24 | /// Set the rcc config to be restored when exiting STOP2 | ||
| 25 | /// | ||
| 26 | /// Safety: Sets a mutable global. | ||
| 27 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 28 | pub(crate) unsafe fn set_resume_config(config: Config) { | ||
| 29 | trace!("rcc set_resume_config()"); | ||
| 30 | RESUME_RCC_CONFIG = MaybeUninit::new(config); | ||
| 31 | } | ||
| 32 | |||
| 33 | /// Get the rcc config to be restored when exiting STOP2 | ||
| 34 | /// | ||
| 35 | /// Safety: Reads a mutable global. | ||
| 36 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 37 | pub(crate) unsafe fn get_resume_config() -> Config { | ||
| 38 | *(*core::ptr::addr_of_mut!(RESUME_RCC_CONFIG)).assume_init_ref() | ||
| 39 | } | ||
| 40 | |||
| 41 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 42 | /// Safety: should only be called from low power executable just after resuming from STOP2 | ||
| 43 | pub(crate) unsafe fn apply_resume_config() { | ||
| 44 | trace!("rcc apply_resume_config()"); | ||
| 45 | |||
| 46 | while RCC.cfgr().read().sws() != Sysclk::MSI {} | ||
| 47 | |||
| 48 | let config = get_resume_config(); | ||
| 49 | |||
| 50 | init(config); | ||
| 51 | } | ||
| 52 | |||
| 14 | #[derive(Clone, Copy, Eq, PartialEq)] | 53 | #[derive(Clone, Copy, Eq, PartialEq)] |
| 15 | pub enum HseMode { | 54 | pub enum HseMode { |
| 16 | /// crystal/ceramic oscillator (HSEBYP=0) | 55 | /// crystal/ceramic oscillator (HSEBYP=0) |
| @@ -154,6 +193,10 @@ fn msi_enable(range: MSIRange) { | |||
| 154 | } | 193 | } |
| 155 | 194 | ||
| 156 | pub(crate) unsafe fn init(config: Config) { | 195 | pub(crate) unsafe fn init(config: Config) { |
| 196 | // save the rcc config because if we enter stop 2 we need to re-apply it on wakeup | ||
| 197 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 198 | set_resume_config(config); | ||
| 199 | |||
| 157 | // Switch to MSI to prevent problems with PLL configuration. | 200 | // Switch to MSI to prevent problems with PLL configuration. |
| 158 | if !RCC.cr().read().msion() { | 201 | if !RCC.cr().read().msion() { |
| 159 | // Turn on MSI and configure it to 4MHz. | 202 | // Turn on MSI and configure it to 4MHz. |
| @@ -499,9 +542,9 @@ pub use pll::*; | |||
| 499 | 542 | ||
| 500 | #[cfg(any(stm32l0, stm32l1))] | 543 | #[cfg(any(stm32l0, stm32l1))] |
| 501 | mod pll { | 544 | mod pll { |
| 502 | use super::{pll_enable, PllInstance}; | 545 | use super::{PllInstance, pll_enable}; |
| 503 | pub use crate::pac::rcc::vals::{Plldiv as PllDiv, Pllmul as PllMul, Pllsrc as PllSource}; | ||
| 504 | use crate::pac::RCC; | 546 | use crate::pac::RCC; |
| 547 | pub use crate::pac::rcc::vals::{Plldiv as PllDiv, Pllmul as PllMul, Pllsrc as PllSource}; | ||
| 505 | use crate::time::Hertz; | 548 | use crate::time::Hertz; |
| 506 | 549 | ||
| 507 | #[derive(Clone, Copy)] | 550 | #[derive(Clone, Copy)] |
| @@ -563,11 +606,11 @@ mod pll { | |||
| 563 | 606 | ||
| 564 | #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))] | 607 | #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))] |
| 565 | mod pll { | 608 | mod pll { |
| 566 | use super::{pll_enable, PllInstance}; | 609 | use super::{PllInstance, pll_enable}; |
| 610 | use crate::pac::RCC; | ||
| 567 | pub use crate::pac::rcc::vals::{ | 611 | pub use crate::pac::rcc::vals::{ |
| 568 | Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc as PllSource, | 612 | Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc as PllSource, |
| 569 | }; | 613 | }; |
| 570 | use crate::pac::RCC; | ||
| 571 | use crate::time::Hertz; | 614 | use crate::time::Hertz; |
| 572 | 615 | ||
| 573 | #[derive(Clone, Copy)] | 616 | #[derive(Clone, Copy)] |
diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs index a8f8cfcff..0624fdf26 100644 --- a/embassy-stm32/src/rcc/mco.rs +++ b/embassy-stm32/src/rcc/mco.rs | |||
| @@ -3,6 +3,7 @@ use core::marker::PhantomData; | |||
| 3 | use embassy_hal_internal::PeripheralType; | 3 | use embassy_hal_internal::PeripheralType; |
| 4 | 4 | ||
| 5 | use crate::gpio::{AfType, OutputType, Speed}; | 5 | use crate::gpio::{AfType, OutputType, Speed}; |
| 6 | use crate::pac::RCC; | ||
| 6 | #[cfg(not(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37)))] | 7 | #[cfg(not(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37)))] |
| 7 | pub use crate::pac::rcc::vals::Mcopre as McoPrescaler; | 8 | pub use crate::pac::rcc::vals::Mcopre as McoPrescaler; |
| 8 | #[cfg(not(any( | 9 | #[cfg(not(any( |
| @@ -33,8 +34,7 @@ pub use crate::pac::rcc::vals::Mcosel as McoSource; | |||
| 33 | rcc_n6 | 34 | rcc_n6 |
| 34 | ))] | 35 | ))] |
| 35 | pub use crate::pac::rcc::vals::{Mco1sel as Mco1Source, Mco2sel as Mco2Source}; | 36 | pub use crate::pac::rcc::vals::{Mco1sel as Mco1Source, Mco2sel as Mco2Source}; |
| 36 | use crate::pac::RCC; | 37 | use crate::{Peri, peripherals}; |
| 37 | use crate::{peripherals, Peri}; | ||
| 38 | 38 | ||
| 39 | #[cfg(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37))] | 39 | #[cfg(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37))] |
| 40 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] | 40 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] |
| @@ -95,12 +95,29 @@ pub struct Mco<'d, T: McoInstance> { | |||
| 95 | 95 | ||
| 96 | impl<'d, T: McoInstance> Mco<'d, T> { | 96 | impl<'d, T: McoInstance> Mco<'d, T> { |
| 97 | /// Create a new MCO instance. | 97 | /// Create a new MCO instance. |
| 98 | pub fn new(_peri: Peri<'d, T>, pin: Peri<'d, impl McoPin<T>>, source: T::Source, prescaler: McoPrescaler) -> Self { | 98 | pub fn new(_peri: Peri<'d, T>, pin: Peri<'d, impl McoPin<T>>, source: T::Source, config: McoConfig) -> Self { |
| 99 | critical_section::with(|_| unsafe { | 99 | critical_section::with(|_| unsafe { |
| 100 | T::_apply_clock_settings(source, prescaler); | 100 | T::_apply_clock_settings(source, config.prescaler); |
| 101 | set_as_af!(pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 101 | set_as_af!(pin, AfType::output(OutputType::PushPull, config.speed)); |
| 102 | }); | 102 | }); |
| 103 | 103 | ||
| 104 | Self { phantom: PhantomData } | 104 | Self { phantom: PhantomData } |
| 105 | } | 105 | } |
| 106 | } | 106 | } |
| 107 | |||
| 108 | #[non_exhaustive] | ||
| 109 | pub struct McoConfig { | ||
| 110 | /// Master Clock Out prescaler | ||
| 111 | pub prescaler: McoPrescaler, | ||
| 112 | /// IO Drive Strength | ||
| 113 | pub speed: Speed, | ||
| 114 | } | ||
| 115 | |||
| 116 | impl Default for McoConfig { | ||
| 117 | fn default() -> Self { | ||
| 118 | Self { | ||
| 119 | prescaler: McoPrescaler::DIV1, | ||
| 120 | speed: Speed::VeryHigh, | ||
| 121 | } | ||
| 122 | } | ||
| 123 | } | ||
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 1cd80c17f..592890777 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -34,7 +34,7 @@ mod _version; | |||
| 34 | pub use _version::*; | 34 | pub use _version::*; |
| 35 | use stm32_metapac::RCC; | 35 | use stm32_metapac::RCC; |
| 36 | 36 | ||
| 37 | pub use crate::_generated::{mux, Clocks}; | 37 | pub use crate::_generated::{Clocks, mux}; |
| 38 | use crate::time::Hertz; | 38 | use crate::time::Hertz; |
| 39 | 39 | ||
| 40 | #[cfg(feature = "low-power")] | 40 | #[cfg(feature = "low-power")] |
| @@ -49,6 +49,9 @@ pub(crate) static mut REFCOUNT_STOP1: u32 = 0; | |||
| 49 | /// May be read without a critical section | 49 | /// May be read without a critical section |
| 50 | pub(crate) static mut REFCOUNT_STOP2: u32 = 0; | 50 | pub(crate) static mut REFCOUNT_STOP2: u32 = 0; |
| 51 | 51 | ||
| 52 | #[cfg(backup_sram)] | ||
| 53 | pub(crate) static mut BKSRAM_RETAINED: bool = false; | ||
| 54 | |||
| 52 | #[cfg(not(feature = "_dual-core"))] | 55 | #[cfg(not(feature = "_dual-core"))] |
| 53 | /// Frozen clock frequencies | 56 | /// Frozen clock frequencies |
| 54 | /// | 57 | /// |
| @@ -391,7 +394,7 @@ pub fn disable<T: RccPeripheral>() { | |||
| 391 | /// | 394 | /// |
| 392 | /// This should only be called after `init`. | 395 | /// This should only be called after `init`. |
| 393 | #[cfg(not(feature = "_dual-core"))] | 396 | #[cfg(not(feature = "_dual-core"))] |
| 394 | pub fn reinit<'a>(config: Config, _rcc: &'a mut crate::Peri<'a, crate::peripherals::RCC>) { | 397 | pub fn reinit(config: Config, _rcc: &'_ mut crate::Peri<'_, crate::peripherals::RCC>) { |
| 395 | critical_section::with(|cs| init_rcc(cs, config)) | 398 | critical_section::with(|cs| init_rcc(cs, config)) |
| 396 | } | 399 | } |
| 397 | 400 | ||
diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 06895a99a..7b0dcb63f 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs | |||
| @@ -6,9 +6,9 @@ pub use crate::pac::rcc::vals::{ | |||
| 6 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, | 6 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, |
| 7 | }; | 7 | }; |
| 8 | use crate::pac::rcc::vals::{Hseext, Msipllfast, Msipllsel, Msirgsel, Pllmboost, Pllrge}; | 8 | use crate::pac::rcc::vals::{Hseext, Msipllfast, Msipllsel, Msirgsel, Pllmboost, Pllrge}; |
| 9 | #[cfg(all(peri_usb_otg_hs))] | ||
| 10 | pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; | ||
| 11 | use crate::pac::{FLASH, PWR, RCC}; | 9 | use crate::pac::{FLASH, PWR, RCC}; |
| 10 | #[cfg(all(peri_usb_otg_hs))] | ||
| 11 | pub use crate::pac::{SYSCFG, syscfg::vals::Usbrefcksel}; | ||
| 12 | use crate::rcc::LSI_FREQ; | 12 | use crate::rcc::LSI_FREQ; |
| 13 | use crate::time::Hertz; | 13 | use crate::time::Hertz; |
| 14 | 14 | ||
| @@ -442,7 +442,10 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 442 | Hertz(24_000_000) => Usbrefcksel::MHZ24, | 442 | Hertz(24_000_000) => Usbrefcksel::MHZ24, |
| 443 | Hertz(26_000_000) => Usbrefcksel::MHZ26, | 443 | Hertz(26_000_000) => Usbrefcksel::MHZ26, |
| 444 | Hertz(32_000_000) => Usbrefcksel::MHZ32, | 444 | Hertz(32_000_000) => Usbrefcksel::MHZ32, |
| 445 | _ => panic!("cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val), | 445 | _ => panic!( |
| 446 | "cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", | ||
| 447 | clk_val | ||
| 448 | ), | ||
| 446 | }, | 449 | }, |
| 447 | None => Usbrefcksel::MHZ24, | 450 | None => Usbrefcksel::MHZ24, |
| 448 | }; | 451 | }; |
diff --git a/embassy-stm32/src/rcc/wba.rs b/embassy-stm32/src/rcc/wba.rs index 481437939..2528996d5 100644 --- a/embassy-stm32/src/rcc/wba.rs +++ b/embassy-stm32/src/rcc/wba.rs | |||
| @@ -7,9 +7,9 @@ pub use crate::pac::rcc::vals::{ | |||
| 7 | Hdiv5, Hpre as AHBPrescaler, Hpre5 as AHB5Prescaler, Hsepre as HsePrescaler, Plldiv as PllDiv, Pllm as PllPreDiv, | 7 | Hdiv5, Hpre as AHBPrescaler, Hpre5 as AHB5Prescaler, Hsepre as HsePrescaler, Plldiv as PllDiv, Pllm as PllPreDiv, |
| 8 | Plln as PllMul, Pllsrc as PllSource, Ppre as APBPrescaler, Sai1sel, Sw as Sysclk, | 8 | Plln as PllMul, Pllsrc as PllSource, Ppre as APBPrescaler, Sai1sel, Sw as Sysclk, |
| 9 | }; | 9 | }; |
| 10 | #[cfg(all(peri_usb_otg_hs))] | ||
| 11 | pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; | ||
| 12 | use crate::pac::{FLASH, RCC}; | 10 | use crate::pac::{FLASH, RCC}; |
| 11 | #[cfg(all(peri_usb_otg_hs))] | ||
| 12 | pub use crate::pac::{SYSCFG, syscfg::vals::Usbrefcksel}; | ||
| 13 | use crate::rcc::LSI_FREQ; | 13 | use crate::rcc::LSI_FREQ; |
| 14 | use crate::time::Hertz; | 14 | use crate::time::Hertz; |
| 15 | 15 | ||
| @@ -245,7 +245,10 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 245 | Hertz(24_000_000) => Usbrefcksel::MHZ24, | 245 | Hertz(24_000_000) => Usbrefcksel::MHZ24, |
| 246 | Hertz(26_000_000) => Usbrefcksel::MHZ26, | 246 | Hertz(26_000_000) => Usbrefcksel::MHZ26, |
| 247 | Hertz(32_000_000) => Usbrefcksel::MHZ32, | 247 | Hertz(32_000_000) => Usbrefcksel::MHZ32, |
| 248 | _ => panic!("cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val), | 248 | _ => panic!( |
| 249 | "cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", | ||
| 250 | clk_val | ||
| 251 | ), | ||
| 249 | }, | 252 | }, |
| 250 | None => Usbrefcksel::MHZ24, | 253 | None => Usbrefcksel::MHZ24, |
| 251 | }; | 254 | }; |
diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 63654639e..dada9bda1 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs | |||
| @@ -9,7 +9,7 @@ use embassy_hal_internal::PeripheralType; | |||
| 9 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 10 | 10 | ||
| 11 | use crate::interrupt::typelevel::Interrupt; | 11 | use crate::interrupt::typelevel::Interrupt; |
| 12 | use crate::{interrupt, pac, peripherals, rcc, Peri}; | 12 | use crate::{Peri, interrupt, pac, peripherals, rcc}; |
| 13 | 13 | ||
| 14 | static RNG_WAKER: AtomicWaker = AtomicWaker::new(); | 14 | static RNG_WAKER: AtomicWaker = AtomicWaker::new(); |
| 15 | 15 | ||
diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs index 78ccd3e6c..e5bf30927 100644 --- a/embassy-stm32/src/rtc/low_power.rs +++ b/embassy-stm32/src/rtc/low_power.rs | |||
| @@ -1,6 +1,10 @@ | |||
| 1 | use super::{bcd2_to_byte, DateTimeError, Rtc, RtcError}; | 1 | #[cfg(feature = "time")] |
| 2 | use embassy_time::{Duration, TICK_HZ}; | ||
| 3 | |||
| 4 | use super::{DateTimeError, Rtc, RtcError, bcd2_to_byte}; | ||
| 5 | use crate::interrupt::typelevel::Interrupt; | ||
| 2 | use crate::peripherals::RTC; | 6 | use crate::peripherals::RTC; |
| 3 | use crate::rtc::SealedInstance; | 7 | use crate::rtc::{RtcTimeProvider, SealedInstance}; |
| 4 | 8 | ||
| 5 | /// Represents an instant in time that can be substracted to compute a duration | 9 | /// Represents an instant in time that can be substracted to compute a duration |
| 6 | pub(super) struct RtcInstant { | 10 | pub(super) struct RtcInstant { |
| @@ -11,7 +15,7 @@ pub(super) struct RtcInstant { | |||
| 11 | } | 15 | } |
| 12 | 16 | ||
| 13 | impl RtcInstant { | 17 | impl RtcInstant { |
| 14 | #[cfg(not(rtc_v2f2))] | 18 | #[cfg(not(rtc_v2_f2))] |
| 15 | const fn from(second: u8, subsecond: u16) -> Result<Self, DateTimeError> { | 19 | const fn from(second: u8, subsecond: u16) -> Result<Self, DateTimeError> { |
| 16 | if second > 59 { | 20 | if second > 59 { |
| 17 | Err(DateTimeError::InvalidSecond) | 21 | Err(DateTimeError::InvalidSecond) |
| @@ -38,8 +42,6 @@ impl core::ops::Sub for RtcInstant { | |||
| 38 | type Output = embassy_time::Duration; | 42 | type Output = embassy_time::Duration; |
| 39 | 43 | ||
| 40 | fn sub(self, rhs: Self) -> Self::Output { | 44 | fn sub(self, rhs: Self) -> Self::Output { |
| 41 | use embassy_time::{Duration, TICK_HZ}; | ||
| 42 | |||
| 43 | let second = if self.second < rhs.second { | 45 | let second = if self.second < rhs.second { |
| 44 | self.second + 60 | 46 | self.second + 60 |
| 45 | } else { | 47 | } else { |
| @@ -66,7 +68,7 @@ pub(crate) enum WakeupPrescaler { | |||
| 66 | } | 68 | } |
| 67 | 69 | ||
| 68 | #[cfg(any( | 70 | #[cfg(any( |
| 69 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba | 71 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba, stm32wlex |
| 70 | ))] | 72 | ))] |
| 71 | impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { | 73 | impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { |
| 72 | fn from(val: WakeupPrescaler) -> Self { | 74 | fn from(val: WakeupPrescaler) -> Self { |
| @@ -82,7 +84,7 @@ impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { | |||
| 82 | } | 84 | } |
| 83 | 85 | ||
| 84 | #[cfg(any( | 86 | #[cfg(any( |
| 85 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba | 87 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba, stm32wlex |
| 86 | ))] | 88 | ))] |
| 87 | impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler { | 89 | impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler { |
| 88 | fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { | 90 | fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { |
| @@ -115,7 +117,7 @@ impl WakeupPrescaler { | |||
| 115 | impl Rtc { | 117 | impl Rtc { |
| 116 | /// Return the current instant. | 118 | /// Return the current instant. |
| 117 | fn instant(&self) -> Result<RtcInstant, RtcError> { | 119 | fn instant(&self) -> Result<RtcInstant, RtcError> { |
| 118 | self.time_provider().read(|_, tr, ss| { | 120 | RtcTimeProvider::new().read(|_, tr, ss| { |
| 119 | let second = bcd2_to_byte((tr.st(), tr.su())); | 121 | let second = bcd2_to_byte((tr.st(), tr.su())); |
| 120 | 122 | ||
| 121 | RtcInstant::from(second, ss).map_err(RtcError::InvalidDateTime) | 123 | RtcInstant::from(second, ss).map_err(RtcError::InvalidDateTime) |
| @@ -125,15 +127,10 @@ impl Rtc { | |||
| 125 | /// start the wakeup alarm and with a duration that is as close to but less than | 127 | /// start the wakeup alarm and with a duration that is as close to but less than |
| 126 | /// the requested duration, and record the instant the wakeup alarm was started | 128 | /// the requested duration, and record the instant the wakeup alarm was started |
| 127 | pub(crate) fn start_wakeup_alarm( | 129 | pub(crate) fn start_wakeup_alarm( |
| 128 | &self, | 130 | &mut self, |
| 129 | requested_duration: embassy_time::Duration, | 131 | requested_duration: embassy_time::Duration, |
| 130 | cs: critical_section::CriticalSection, | 132 | cs: critical_section::CriticalSection, |
| 131 | ) { | 133 | ) { |
| 132 | use embassy_time::{Duration, TICK_HZ}; | ||
| 133 | |||
| 134 | #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] | ||
| 135 | use crate::pac::rtc::vals::Calrf; | ||
| 136 | |||
| 137 | // Panic if the rcc mod knows we're not using low-power rtc | 134 | // Panic if the rcc mod knows we're not using low-power rtc |
| 138 | #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] | 135 | #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] |
| 139 | unsafe { crate::rcc::get_freqs() }.rtc.to_hertz().unwrap(); | 136 | unsafe { crate::rcc::get_freqs() }.rtc.to_hertz().unwrap(); |
| @@ -150,17 +147,15 @@ impl Rtc { | |||
| 150 | self.write(false, |regs| { | 147 | self.write(false, |regs| { |
| 151 | regs.cr().modify(|w| w.set_wute(false)); | 148 | regs.cr().modify(|w| w.set_wute(false)); |
| 152 | 149 | ||
| 153 | #[cfg(any( | 150 | #[cfg(rtc_v2)] |
| 154 | rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb | ||
| 155 | ))] | ||
| 156 | { | 151 | { |
| 157 | regs.isr().modify(|w| w.set_wutf(false)); | 152 | regs.isr().modify(|w| w.set_wutf(false)); |
| 158 | while !regs.isr().read().wutwf() {} | 153 | while !regs.isr().read().wutwf() {} |
| 159 | } | 154 | } |
| 160 | 155 | ||
| 161 | #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] | 156 | #[cfg(rtc_v3)] |
| 162 | { | 157 | { |
| 163 | regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR)); | 158 | regs.scr().write(|w| w.set_cwutf(crate::pac::rtc::vals::Calrf::CLEAR)); |
| 164 | while !regs.icsr().read().wutwf() {} | 159 | while !regs.icsr().read().wutwf() {} |
| 165 | } | 160 | } |
| 166 | 161 | ||
| @@ -184,11 +179,10 @@ impl Rtc { | |||
| 184 | 179 | ||
| 185 | /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` | 180 | /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` |
| 186 | /// was called, otherwise none | 181 | /// was called, otherwise none |
| 187 | pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> { | 182 | pub(crate) fn stop_wakeup_alarm( |
| 188 | use crate::interrupt::typelevel::Interrupt; | 183 | &mut self, |
| 189 | #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] | 184 | cs: critical_section::CriticalSection, |
| 190 | use crate::pac::rtc::vals::Calrf; | 185 | ) -> Option<embassy_time::Duration> { |
| 191 | |||
| 192 | let instant = self.instant().unwrap(); | 186 | let instant = self.instant().unwrap(); |
| 193 | if RTC::regs().cr().read().wute() { | 187 | if RTC::regs().cr().read().wute() { |
| 194 | trace!("rtc: stop wakeup alarm at {}", instant); | 188 | trace!("rtc: stop wakeup alarm at {}", instant); |
| @@ -197,13 +191,10 @@ impl Rtc { | |||
| 197 | regs.cr().modify(|w| w.set_wutie(false)); | 191 | regs.cr().modify(|w| w.set_wutie(false)); |
| 198 | regs.cr().modify(|w| w.set_wute(false)); | 192 | regs.cr().modify(|w| w.set_wute(false)); |
| 199 | 193 | ||
| 200 | #[cfg(any( | 194 | #[cfg(rtc_v2)] |
| 201 | rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb | ||
| 202 | ))] | ||
| 203 | regs.isr().modify(|w| w.set_wutf(false)); | 195 | regs.isr().modify(|w| w.set_wutf(false)); |
| 204 | 196 | #[cfg(rtc_v3)] | |
| 205 | #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] | 197 | regs.scr().write(|w| w.set_cwutf(crate::pac::rtc::vals::Calrf::CLEAR)); |
| 206 | regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR)); | ||
| 207 | 198 | ||
| 208 | // Check RM for EXTI and/or NVIC section, "Event event input mapping" or "EXTI interrupt/event mapping" or something similar, | 199 | // Check RM for EXTI and/or NVIC section, "Event event input mapping" or "EXTI interrupt/event mapping" or something similar, |
| 209 | // there is a table for every "Event input" / "EXTI Line". | 200 | // there is a table for every "Event input" / "EXTI Line". |
| @@ -222,8 +213,6 @@ impl Rtc { | |||
| 222 | } | 213 | } |
| 223 | 214 | ||
| 224 | pub(crate) fn enable_wakeup_line(&self) { | 215 | pub(crate) fn enable_wakeup_line(&self) { |
| 225 | use crate::interrupt::typelevel::Interrupt; | ||
| 226 | |||
| 227 | <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend(); | 216 | <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend(); |
| 228 | unsafe { <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::enable() }; | 217 | unsafe { <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::enable() }; |
| 229 | 218 | ||
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 449f3008a..116b3c7ed 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs | |||
| @@ -5,33 +5,32 @@ mod datetime; | |||
| 5 | mod low_power; | 5 | mod low_power; |
| 6 | 6 | ||
| 7 | #[cfg(feature = "low-power")] | 7 | #[cfg(feature = "low-power")] |
| 8 | use core::cell::Cell; | 8 | use core::cell::{Cell, RefCell, RefMut}; |
| 9 | #[cfg(feature = "low-power")] | ||
| 10 | use core::ops; | ||
| 9 | 11 | ||
| 10 | #[cfg(feature = "low-power")] | 12 | #[cfg(feature = "low-power")] |
| 11 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 13 | use critical_section::CriticalSection; |
| 12 | #[cfg(feature = "low-power")] | 14 | #[cfg(feature = "low-power")] |
| 13 | use embassy_sync::blocking_mutex::Mutex; | 15 | use embassy_sync::blocking_mutex::Mutex; |
| 16 | #[cfg(feature = "low-power")] | ||
| 17 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 14 | 18 | ||
| 15 | use self::datetime::{day_of_week_from_u8, day_of_week_to_u8}; | ||
| 16 | pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; | 19 | pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; |
| 20 | use self::datetime::{day_of_week_from_u8, day_of_week_to_u8}; | ||
| 17 | use crate::pac::rtc::regs::{Dr, Tr}; | 21 | use crate::pac::rtc::regs::{Dr, Tr}; |
| 18 | use crate::time::Hertz; | 22 | use crate::time::Hertz; |
| 19 | 23 | ||
| 20 | /// refer to AN4759 to compare features of RTC2 and RTC3 | 24 | /// refer to AN4759 to compare features of RTC2 and RTC3 |
| 21 | #[cfg_attr(any(rtc_v1), path = "v1.rs")] | 25 | #[cfg_attr(rtc_v1, path = "v1.rs")] |
| 22 | #[cfg_attr( | 26 | #[cfg_attr(rtc_v2, path = "v2.rs")] |
| 23 | any( | 27 | #[cfg_attr(rtc_v3, path = "v3.rs")] |
| 24 | rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb | ||
| 25 | ), | ||
| 26 | path = "v2.rs" | ||
| 27 | )] | ||
| 28 | #[cfg_attr(any(rtc_v3, rtc_v3u5, rtc_v3l5, rtc_v3h7rs, rtc_v3c0), path = "v3.rs")] | ||
| 29 | mod _version; | 28 | mod _version; |
| 30 | #[allow(unused_imports)] | 29 | #[allow(unused_imports)] |
| 31 | pub use _version::*; | 30 | pub use _version::*; |
| 32 | 31 | ||
| 33 | use crate::peripherals::RTC; | ||
| 34 | use crate::Peri; | 32 | use crate::Peri; |
| 33 | use crate::peripherals::RTC; | ||
| 35 | 34 | ||
| 36 | /// Errors that can occur on methods on [RtcClock] | 35 | /// Errors that can occur on methods on [RtcClock] |
| 37 | #[non_exhaustive] | 36 | #[non_exhaustive] |
| @@ -49,11 +48,17 @@ pub enum RtcError { | |||
| 49 | } | 48 | } |
| 50 | 49 | ||
| 51 | /// Provides immutable access to the current time of the RTC. | 50 | /// Provides immutable access to the current time of the RTC. |
| 51 | #[derive(Clone)] | ||
| 52 | pub struct RtcTimeProvider { | 52 | pub struct RtcTimeProvider { |
| 53 | _private: (), | 53 | _private: (), |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | impl RtcTimeProvider { | 56 | impl RtcTimeProvider { |
| 57 | /// Create a new RTC time provider instance. | ||
| 58 | pub(self) const fn new() -> Self { | ||
| 59 | Self { _private: () } | ||
| 60 | } | ||
| 61 | |||
| 57 | /// Return the current datetime. | 62 | /// Return the current datetime. |
| 58 | /// | 63 | /// |
| 59 | /// # Errors | 64 | /// # Errors |
| @@ -72,12 +77,12 @@ impl RtcTimeProvider { | |||
| 72 | 77 | ||
| 73 | // Calculate second fraction and multiply to microseconds | 78 | // Calculate second fraction and multiply to microseconds |
| 74 | // Formula from RM0410 | 79 | // Formula from RM0410 |
| 75 | #[cfg(not(rtc_v2f2))] | 80 | #[cfg(not(rtc_v2_f2))] |
| 76 | let us = { | 81 | let us = { |
| 77 | let prediv = RTC::regs().prer().read().prediv_s() as f32; | 82 | let prediv = RTC::regs().prer().read().prediv_s() as f32; |
| 78 | (((prediv - _ss as f32) / (prediv + 1.0)) * 1e6).min(999_999.0) as u32 | 83 | (((prediv - _ss as f32) / (prediv + 1.0)) * 1e6).min(999_999.0) as u32 |
| 79 | }; | 84 | }; |
| 80 | #[cfg(rtc_v2f2)] | 85 | #[cfg(rtc_v2_f2)] |
| 81 | let us = 0; | 86 | let us = 0; |
| 82 | 87 | ||
| 83 | DateTime::from(year, month, day, weekday, hour, minute, second, us).map_err(RtcError::InvalidDateTime) | 88 | DateTime::from(year, month, day, weekday, hour, minute, second, us).map_err(RtcError::InvalidDateTime) |
| @@ -87,9 +92,9 @@ impl RtcTimeProvider { | |||
| 87 | fn read<R>(&self, mut f: impl FnMut(Dr, Tr, u16) -> Result<R, RtcError>) -> Result<R, RtcError> { | 92 | fn read<R>(&self, mut f: impl FnMut(Dr, Tr, u16) -> Result<R, RtcError>) -> Result<R, RtcError> { |
| 88 | let r = RTC::regs(); | 93 | let r = RTC::regs(); |
| 89 | 94 | ||
| 90 | #[cfg(not(rtc_v2f2))] | 95 | #[cfg(not(rtc_v2_f2))] |
| 91 | let read_ss = || r.ssr().read().ss(); | 96 | let read_ss = || r.ssr().read().ss(); |
| 92 | #[cfg(rtc_v2f2)] | 97 | #[cfg(rtc_v2_f2)] |
| 93 | let read_ss = || 0; | 98 | let read_ss = || 0; |
| 94 | 99 | ||
| 95 | let mut ss = read_ss(); | 100 | let mut ss = read_ss(); |
| @@ -111,6 +116,50 @@ impl RtcTimeProvider { | |||
| 111 | } | 116 | } |
| 112 | } | 117 | } |
| 113 | 118 | ||
| 119 | #[cfg(feature = "low-power")] | ||
| 120 | /// Contains an RTC driver. | ||
| 121 | pub struct RtcContainer { | ||
| 122 | pub(self) mutex: &'static Mutex<CriticalSectionRawMutex, RefCell<Option<Rtc>>>, | ||
| 123 | } | ||
| 124 | |||
| 125 | #[cfg(feature = "low-power")] | ||
| 126 | impl RtcContainer { | ||
| 127 | pub(self) const fn new() -> Self { | ||
| 128 | Self { | ||
| 129 | mutex: &crate::time_driver::get_driver().rtc, | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | /// Acquire an RTC borrow. | ||
| 134 | pub fn borrow_mut<'a>(&self, cs: CriticalSection<'a>) -> RtcBorrow<'a> { | ||
| 135 | RtcBorrow { | ||
| 136 | ref_mut: self.mutex.borrow(cs).borrow_mut(), | ||
| 137 | } | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | #[cfg(feature = "low-power")] | ||
| 142 | /// Contains an RTC borrow. | ||
| 143 | pub struct RtcBorrow<'a> { | ||
| 144 | pub(self) ref_mut: RefMut<'a, Option<Rtc>>, | ||
| 145 | } | ||
| 146 | |||
| 147 | #[cfg(feature = "low-power")] | ||
| 148 | impl<'a> ops::Deref for RtcBorrow<'a> { | ||
| 149 | type Target = Rtc; | ||
| 150 | |||
| 151 | fn deref(&self) -> &Self::Target { | ||
| 152 | self.ref_mut.as_ref().unwrap() | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | #[cfg(feature = "low-power")] | ||
| 157 | impl<'a> ops::DerefMut for RtcBorrow<'a> { | ||
| 158 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 159 | self.ref_mut.as_mut().unwrap() | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 114 | /// RTC driver. | 163 | /// RTC driver. |
| 115 | pub struct Rtc { | 164 | pub struct Rtc { |
| 116 | #[cfg(feature = "low-power")] | 165 | #[cfg(feature = "low-power")] |
| @@ -126,13 +175,21 @@ pub struct RtcConfig { | |||
| 126 | /// | 175 | /// |
| 127 | /// A high counter frequency may impact stop power consumption | 176 | /// A high counter frequency may impact stop power consumption |
| 128 | pub frequency: Hertz, | 177 | pub frequency: Hertz, |
| 178 | |||
| 179 | #[cfg(feature = "_allow-disable-rtc")] | ||
| 180 | /// Allow disabling the rtc, even when stop is configured | ||
| 181 | pub _disable_rtc: bool, | ||
| 129 | } | 182 | } |
| 130 | 183 | ||
| 131 | impl Default for RtcConfig { | 184 | impl Default for RtcConfig { |
| 132 | /// LSI with prescalers assuming 32.768 kHz. | 185 | /// LSI with prescalers assuming 32.768 kHz. |
| 133 | /// Raw sub-seconds in 1/256. | 186 | /// Raw sub-seconds in 1/256. |
| 134 | fn default() -> Self { | 187 | fn default() -> Self { |
| 135 | RtcConfig { frequency: Hertz(256) } | 188 | RtcConfig { |
| 189 | frequency: Hertz(256), | ||
| 190 | #[cfg(feature = "_allow-disable-rtc")] | ||
| 191 | _disable_rtc: false, | ||
| 192 | } | ||
| 136 | } | 193 | } |
| 137 | } | 194 | } |
| 138 | 195 | ||
| @@ -150,8 +207,19 @@ pub enum RtcCalibrationCyclePeriod { | |||
| 150 | } | 207 | } |
| 151 | 208 | ||
| 152 | impl Rtc { | 209 | impl Rtc { |
| 210 | #[cfg(not(feature = "low-power"))] | ||
| 153 | /// Create a new RTC instance. | 211 | /// Create a new RTC instance. |
| 154 | pub fn new(_rtc: Peri<'static, RTC>, rtc_config: RtcConfig) -> Self { | 212 | pub fn new(_rtc: Peri<'static, RTC>, rtc_config: RtcConfig) -> (Self, RtcTimeProvider) { |
| 213 | (Self::new_inner(rtc_config), RtcTimeProvider::new()) | ||
| 214 | } | ||
| 215 | |||
| 216 | #[cfg(feature = "low-power")] | ||
| 217 | /// Create a new RTC instance. | ||
| 218 | pub fn new(_rtc: Peri<'static, RTC>) -> (RtcContainer, RtcTimeProvider) { | ||
| 219 | (RtcContainer::new(), RtcTimeProvider::new()) | ||
| 220 | } | ||
| 221 | |||
| 222 | pub(self) fn new_inner(rtc_config: RtcConfig) -> Self { | ||
| 155 | #[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))] | 223 | #[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))] |
| 156 | crate::rcc::enable_and_reset::<RTC>(); | 224 | crate::rcc::enable_and_reset::<RTC>(); |
| 157 | 225 | ||
| @@ -168,12 +236,15 @@ impl Rtc { | |||
| 168 | this.configure(async_psc, sync_psc); | 236 | this.configure(async_psc, sync_psc); |
| 169 | 237 | ||
| 170 | // Wait for the clock to update after initialization | 238 | // Wait for the clock to update after initialization |
| 171 | #[cfg(not(rtc_v2f2))] | 239 | #[cfg(not(rtc_v2_f2))] |
| 172 | { | 240 | { |
| 173 | let now = this.time_provider().read(|_, _, ss| Ok(ss)).unwrap(); | 241 | let now = RtcTimeProvider::new().read(|_, _, ss| Ok(ss)).unwrap(); |
| 174 | while now == this.time_provider().read(|_, _, ss| Ok(ss)).unwrap() {} | 242 | while now == RtcTimeProvider::new().read(|_, _, ss| Ok(ss)).unwrap() {} |
| 175 | } | 243 | } |
| 176 | 244 | ||
| 245 | #[cfg(feature = "low-power")] | ||
| 246 | this.enable_wakeup_line(); | ||
| 247 | |||
| 177 | this | 248 | this |
| 178 | } | 249 | } |
| 179 | 250 | ||
| @@ -182,11 +253,6 @@ impl Rtc { | |||
| 182 | freqs.rtc.to_hertz().unwrap() | 253 | freqs.rtc.to_hertz().unwrap() |
| 183 | } | 254 | } |
| 184 | 255 | ||
| 185 | /// Acquire a [`RtcTimeProvider`] instance. | ||
| 186 | pub const fn time_provider(&self) -> RtcTimeProvider { | ||
| 187 | RtcTimeProvider { _private: () } | ||
| 188 | } | ||
| 189 | |||
| 190 | /// Set the datetime to a new value. | 256 | /// Set the datetime to a new value. |
| 191 | /// | 257 | /// |
| 192 | /// # Errors | 258 | /// # Errors |
| @@ -230,15 +296,6 @@ impl Rtc { | |||
| 230 | Ok(()) | 296 | Ok(()) |
| 231 | } | 297 | } |
| 232 | 298 | ||
| 233 | /// Return the current datetime. | ||
| 234 | /// | ||
| 235 | /// # Errors | ||
| 236 | /// | ||
| 237 | /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. | ||
| 238 | pub fn now(&self) -> Result<DateTime, RtcError> { | ||
| 239 | self.time_provider().now() | ||
| 240 | } | ||
| 241 | |||
| 242 | /// Check if daylight savings time is active. | 299 | /// Check if daylight savings time is active. |
| 243 | pub fn get_daylight_savings(&self) -> bool { | 300 | pub fn get_daylight_savings(&self) -> bool { |
| 244 | let cr = RTC::regs().cr().read(); | 301 | let cr = RTC::regs().cr().read(); |
| @@ -320,3 +377,15 @@ trait SealedInstance { | |||
| 320 | 377 | ||
| 321 | // fn apply_config(&mut self, rtc_config: RtcConfig); | 378 | // fn apply_config(&mut self, rtc_config: RtcConfig); |
| 322 | } | 379 | } |
| 380 | |||
| 381 | #[cfg(feature = "low-power")] | ||
| 382 | pub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig) { | ||
| 383 | #[cfg(feature = "_allow-disable-rtc")] | ||
| 384 | if config._disable_rtc { | ||
| 385 | return; | ||
| 386 | } | ||
| 387 | |||
| 388 | crate::time_driver::get_driver().set_rtc(cs, Rtc::new_inner(config)); | ||
| 389 | |||
| 390 | trace!("low power: stop with rtc configured"); | ||
| 391 | } | ||
diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 28380a3c0..8ac022536 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs | |||
| @@ -11,11 +11,11 @@ impl super::Rtc { | |||
| 11 | pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) { | 11 | pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) { |
| 12 | self.write(true, |rtc| { | 12 | self.write(true, |rtc| { |
| 13 | rtc.cr().modify(|w| { | 13 | rtc.cr().modify(|w| { |
| 14 | #[cfg(not(rtc_v2f2))] | 14 | #[cfg(not(rtc_v2_f2))] |
| 15 | w.set_bypshad(true); | 15 | w.set_bypshad(true); |
| 16 | #[cfg(rtc_v2f2)] | 16 | #[cfg(rtc_v2_f2)] |
| 17 | w.set_fmt(false); | 17 | w.set_fmt(false); |
| 18 | #[cfg(not(rtc_v2f2))] | 18 | #[cfg(not(rtc_v2_f2))] |
| 19 | w.set_fmt(stm32_metapac::rtc::vals::Fmt::TWENTY_FOUR_HOUR); | 19 | w.set_fmt(stm32_metapac::rtc::vals::Fmt::TWENTY_FOUR_HOUR); |
| 20 | w.set_osel(Osel::DISABLED); | 20 | w.set_osel(Osel::DISABLED); |
| 21 | w.set_pol(Pol::HIGH); | 21 | w.set_pol(Pol::HIGH); |
| @@ -36,7 +36,7 @@ impl super::Rtc { | |||
| 36 | /// | 36 | /// |
| 37 | /// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler` | 37 | /// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler` |
| 38 | /// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12). | 38 | /// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12). |
| 39 | #[cfg(not(rtc_v2f2))] | 39 | #[cfg(not(rtc_v2_f2))] |
| 40 | pub fn calibrate(&mut self, mut clock_drift: f32, period: super::RtcCalibrationCyclePeriod) { | 40 | pub fn calibrate(&mut self, mut clock_drift: f32, period: super::RtcCalibrationCyclePeriod) { |
| 41 | const RTC_CALR_MIN_PPM: f32 = -487.1; | 41 | const RTC_CALR_MIN_PPM: f32 = -487.1; |
| 42 | const RTC_CALR_MAX_PPM: f32 = 488.5; | 42 | const RTC_CALR_MAX_PPM: f32 = 488.5; |
| @@ -93,7 +93,7 @@ impl super::Rtc { | |||
| 93 | }) | 93 | }) |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | pub(super) fn write<F, R>(&self, init_mode: bool, f: F) -> R | 96 | pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R |
| 97 | where | 97 | where |
| 98 | F: FnOnce(crate::pac::rtc::Rtc) -> R, | 98 | F: FnOnce(crate::pac::rtc::Rtc) -> R, |
| 99 | { | 99 | { |
diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index d0b52049e..f7ebea73e 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Key, Osel, Pol, TampalrmType}; | 1 | use stm32_metapac::rtc::vals::{Calp, Calw8, Calw16, Fmt, Key, Osel, Pol, TampalrmType}; |
| 2 | 2 | ||
| 3 | use super::RtcCalibrationCyclePeriod; | 3 | use super::RtcCalibrationCyclePeriod; |
| 4 | use crate::pac::rtc::Rtc; | 4 | use crate::pac::rtc::Rtc; |
| @@ -95,7 +95,7 @@ impl super::Rtc { | |||
| 95 | }) | 95 | }) |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | pub(super) fn write<F, R>(&self, init_mode: bool, f: F) -> R | 98 | pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R |
| 99 | where | 99 | where |
| 100 | F: FnOnce(crate::pac::rtc::Rtc) -> R, | 100 | F: FnOnce(crate::pac::rtc::Rtc) -> R, |
| 101 | { | 101 | { |
| @@ -131,7 +131,7 @@ impl SealedInstance for crate::peripherals::RTC { | |||
| 131 | 131 | ||
| 132 | #[cfg(feature = "low-power")] | 132 | #[cfg(feature = "low-power")] |
| 133 | cfg_if::cfg_if!( | 133 | cfg_if::cfg_if!( |
| 134 | if #[cfg(stm32g4)] { | 134 | if #[cfg(any(stm32g4, stm32wlex))] { |
| 135 | const EXTI_WAKEUP_LINE: usize = 20; | 135 | const EXTI_WAKEUP_LINE: usize = 20; |
| 136 | } else if #[cfg(stm32g0)] { | 136 | } else if #[cfg(stm32g0)] { |
| 137 | const EXTI_WAKEUP_LINE: usize = 19; | 137 | const EXTI_WAKEUP_LINE: usize = 19; |
| @@ -142,7 +142,7 @@ impl SealedInstance for crate::peripherals::RTC { | |||
| 142 | 142 | ||
| 143 | #[cfg(feature = "low-power")] | 143 | #[cfg(feature = "low-power")] |
| 144 | cfg_if::cfg_if!( | 144 | cfg_if::cfg_if!( |
| 145 | if #[cfg(stm32g4)] { | 145 | if #[cfg(any(stm32g4, stm32wlex))] { |
| 146 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; | 146 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; |
| 147 | } else if #[cfg(any(stm32g0, stm32u0))] { | 147 | } else if #[cfg(any(stm32g0, stm32u0))] { |
| 148 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP; | 148 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP; |
diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index fb8b23b79..726d1729a 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs | |||
| @@ -6,12 +6,12 @@ use core::marker::PhantomData; | |||
| 6 | use embassy_hal_internal::PeripheralType; | 6 | use embassy_hal_internal::PeripheralType; |
| 7 | 7 | ||
| 8 | pub use crate::dma::word; | 8 | pub use crate::dma::word; |
| 9 | use crate::dma::{ringbuffer, Channel, ReadableRingBuffer, Request, TransferOptions, WritableRingBuffer}; | 9 | use crate::dma::{Channel, ReadableRingBuffer, Request, TransferOptions, WritableRingBuffer, ringbuffer}; |
| 10 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | 10 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
| 11 | pub use crate::pac::sai::vals::Mckdiv as MasterClockDivider; | 11 | pub use crate::pac::sai::vals::Mckdiv as MasterClockDivider; |
| 12 | use crate::pac::sai::{vals, Sai as Regs}; | 12 | use crate::pac::sai::{Sai as Regs, vals}; |
| 13 | use crate::rcc::{self, RccPeripheral}; | 13 | use crate::rcc::{self, RccPeripheral}; |
| 14 | use crate::{peripherals, Peri}; | 14 | use crate::{Peri, peripherals}; |
| 15 | 15 | ||
| 16 | /// SAI error | 16 | /// SAI error |
| 17 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 17 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index ccbd16cbf..e05131040 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs | |||
| @@ -11,9 +11,9 @@ use embassy_hal_internal::drop::OnDrop; | |||
| 11 | use embassy_hal_internal::{Peri, PeripheralType}; | 11 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 12 | use embassy_sync::waitqueue::AtomicWaker; | 12 | use embassy_sync::waitqueue::AtomicWaker; |
| 13 | use sdio_host::common_cmd::{self, Resp, ResponseLen}; | 13 | use sdio_host::common_cmd::{self, Resp, ResponseLen}; |
| 14 | use sdio_host::emmc::{ExtCSD, EMMC}; | 14 | use sdio_host::emmc::{EMMC, ExtCSD}; |
| 15 | use sdio_host::sd::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CIC, CID, CSD, OCR, RCA, SCR, SD}; | 15 | use sdio_host::sd::{BusWidth, CIC, CID, CSD, CardCapacity, CardStatus, CurrentState, OCR, RCA, SCR, SD, SDStatus}; |
| 16 | use sdio_host::{emmc_cmd, sd_cmd, Cmd}; | 16 | use sdio_host::{Cmd, emmc_cmd, sd_cmd}; |
| 17 | 17 | ||
| 18 | #[cfg(sdmmc_v1)] | 18 | #[cfg(sdmmc_v1)] |
| 19 | use crate::dma::ChannelAndRequest; | 19 | use crate::dma::ChannelAndRequest; |
| @@ -1032,12 +1032,13 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1032 | 1032 | ||
| 1033 | /// Wait for a previously started datapath transfer to complete from an interrupt. | 1033 | /// Wait for a previously started datapath transfer to complete from an interrupt. |
| 1034 | #[inline] | 1034 | #[inline] |
| 1035 | #[allow(unused)] | ||
| 1035 | async fn complete_datapath_transfer(block: bool) -> Result<(), Error> { | 1036 | async fn complete_datapath_transfer(block: bool) -> Result<(), Error> { |
| 1036 | let regs = T::regs(); | ||
| 1037 | |||
| 1038 | let res = poll_fn(|cx| { | 1037 | let res = poll_fn(|cx| { |
| 1038 | // Compiler might not be sufficiently constrained here | ||
| 1039 | // https://github.com/embassy-rs/embassy/issues/4723 | ||
| 1039 | T::state().register(cx.waker()); | 1040 | T::state().register(cx.waker()); |
| 1040 | let status = regs.star().read(); | 1041 | let status = T::regs().star().read(); |
| 1041 | 1042 | ||
| 1042 | if status.dcrcfail() { | 1043 | if status.dcrcfail() { |
| 1043 | return Poll::Ready(Err(Error::Crc)); | 1044 | return Poll::Ready(Err(Error::Crc)); |
| @@ -1052,10 +1053,13 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1052 | if status.stbiterr() { | 1053 | if status.stbiterr() { |
| 1053 | return Poll::Ready(Err(Error::StBitErr)); | 1054 | return Poll::Ready(Err(Error::StBitErr)); |
| 1054 | } | 1055 | } |
| 1056 | #[cfg(sdmmc_v1)] | ||
| 1055 | let done = match block { | 1057 | let done = match block { |
| 1056 | true => status.dbckend(), | 1058 | true => status.dbckend(), |
| 1057 | false => status.dataend(), | 1059 | false => status.dataend(), |
| 1058 | }; | 1060 | }; |
| 1061 | #[cfg(sdmmc_v2)] | ||
| 1062 | let done = status.dataend(); | ||
| 1059 | if done { | 1063 | if done { |
| 1060 | return Poll::Ready(Ok(())); | 1064 | return Poll::Ready(Ok(())); |
| 1061 | } | 1065 | } |
diff --git a/embassy-stm32/src/spdifrx/mod.rs b/embassy-stm32/src/spdifrx/mod.rs index b0a32d5d1..6f2d24560 100644 --- a/embassy-stm32/src/spdifrx/mod.rs +++ b/embassy-stm32/src/spdifrx/mod.rs | |||
| @@ -13,7 +13,7 @@ use crate::gpio::{AfType, AnyPin, Pull, SealedPin as _}; | |||
| 13 | use crate::interrupt::typelevel::Interrupt; | 13 | use crate::interrupt::typelevel::Interrupt; |
| 14 | use crate::pac::spdifrx::Spdifrx as Regs; | 14 | use crate::pac::spdifrx::Spdifrx as Regs; |
| 15 | use crate::rcc::{RccInfo, SealedRccPeripheral}; | 15 | use crate::rcc::{RccInfo, SealedRccPeripheral}; |
| 16 | use crate::{interrupt, peripherals, Peri}; | 16 | use crate::{Peri, interrupt, peripherals}; |
| 17 | 17 | ||
| 18 | /// Possible S/PDIF preamble types. | 18 | /// Possible S/PDIF preamble types. |
| 19 | #[allow(dead_code)] | 19 | #[allow(dead_code)] |
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index c5373a54d..abb80ed26 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs | |||
| @@ -6,15 +6,15 @@ use core::ptr; | |||
| 6 | 6 | ||
| 7 | use embassy_embedded_hal::SetConfig; | 7 | use embassy_embedded_hal::SetConfig; |
| 8 | use embassy_futures::join::join; | 8 | use embassy_futures::join::join; |
| 9 | pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; | 9 | pub use embedded_hal_02::spi::{MODE_0, MODE_1, MODE_2, MODE_3, Mode, Phase, Polarity}; |
| 10 | 10 | ||
| 11 | use crate::dma::{word, ChannelAndRequest}; | 11 | use crate::Peri; |
| 12 | use crate::dma::{ChannelAndRequest, word}; | ||
| 12 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | 13 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
| 13 | use crate::mode::{Async, Blocking, Mode as PeriMode}; | 14 | use crate::mode::{Async, Blocking, Mode as PeriMode}; |
| 14 | use crate::pac::spi::{regs, vals, Spi as Regs}; | 15 | use crate::pac::spi::{Spi as Regs, regs, vals}; |
| 15 | use crate::rcc::{RccInfo, SealedRccPeripheral}; | 16 | use crate::rcc::{RccInfo, SealedRccPeripheral}; |
| 16 | use crate::time::Hertz; | 17 | use crate::time::Hertz; |
| 17 | use crate::Peri; | ||
| 18 | 18 | ||
| 19 | /// SPI error. | 19 | /// SPI error. |
| 20 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 20 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
| @@ -125,26 +125,69 @@ impl Config { | |||
| 125 | ) | 125 | ) |
| 126 | } | 126 | } |
| 127 | } | 127 | } |
| 128 | |||
| 129 | /// SPI communication mode | ||
| 130 | pub mod mode { | ||
| 131 | use stm32_metapac::spi::vals; | ||
| 132 | |||
| 133 | trait SealedMode {} | ||
| 134 | |||
| 135 | /// Trait for SPI communication mode operations. | ||
| 136 | #[allow(private_bounds)] | ||
| 137 | pub trait CommunicationMode: SealedMode { | ||
| 138 | /// Spi communication mode | ||
| 139 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] | ||
| 140 | const MASTER: vals::Mstr; | ||
| 141 | /// Spi communication mode | ||
| 142 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 143 | const MASTER: vals::Master; | ||
| 144 | } | ||
| 145 | |||
| 146 | /// Mode allowing for SPI master operations. | ||
| 147 | pub struct Master; | ||
| 148 | /// Mode allowing for SPI slave operations. | ||
| 149 | pub struct Slave; | ||
| 150 | |||
| 151 | impl SealedMode for Master {} | ||
| 152 | impl CommunicationMode for Master { | ||
| 153 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] | ||
| 154 | const MASTER: vals::Mstr = vals::Mstr::MASTER; | ||
| 155 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 156 | const MASTER: vals::Master = vals::Master::MASTER; | ||
| 157 | } | ||
| 158 | |||
| 159 | impl SealedMode for Slave {} | ||
| 160 | impl CommunicationMode for Slave { | ||
| 161 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] | ||
| 162 | const MASTER: vals::Mstr = vals::Mstr::SLAVE; | ||
| 163 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 164 | const MASTER: vals::Master = vals::Master::SLAVE; | ||
| 165 | } | ||
| 166 | } | ||
| 167 | use mode::{CommunicationMode, Master, Slave}; | ||
| 168 | |||
| 128 | /// SPI driver. | 169 | /// SPI driver. |
| 129 | pub struct Spi<'d, M: PeriMode> { | 170 | pub struct Spi<'d, M: PeriMode, CM: CommunicationMode> { |
| 130 | pub(crate) info: &'static Info, | 171 | pub(crate) info: &'static Info, |
| 131 | kernel_clock: Hertz, | 172 | kernel_clock: Hertz, |
| 132 | sck: Option<Peri<'d, AnyPin>>, | 173 | sck: Option<Peri<'d, AnyPin>>, |
| 133 | mosi: Option<Peri<'d, AnyPin>>, | 174 | mosi: Option<Peri<'d, AnyPin>>, |
| 134 | miso: Option<Peri<'d, AnyPin>>, | 175 | miso: Option<Peri<'d, AnyPin>>, |
| 176 | nss: Option<Peri<'d, AnyPin>>, | ||
| 135 | tx_dma: Option<ChannelAndRequest<'d>>, | 177 | tx_dma: Option<ChannelAndRequest<'d>>, |
| 136 | rx_dma: Option<ChannelAndRequest<'d>>, | 178 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 137 | _phantom: PhantomData<M>, | 179 | _phantom: PhantomData<(M, CM)>, |
| 138 | current_word_size: word_impl::Config, | 180 | current_word_size: word_impl::Config, |
| 139 | gpio_speed: Speed, | 181 | gpio_speed: Speed, |
| 140 | } | 182 | } |
| 141 | 183 | ||
| 142 | impl<'d, M: PeriMode> Spi<'d, M> { | 184 | impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { |
| 143 | fn new_inner<T: Instance>( | 185 | fn new_inner<T: Instance>( |
| 144 | _peri: Peri<'d, T>, | 186 | _peri: Peri<'d, T>, |
| 145 | sck: Option<Peri<'d, AnyPin>>, | 187 | sck: Option<Peri<'d, AnyPin>>, |
| 146 | mosi: Option<Peri<'d, AnyPin>>, | 188 | mosi: Option<Peri<'d, AnyPin>>, |
| 147 | miso: Option<Peri<'d, AnyPin>>, | 189 | miso: Option<Peri<'d, AnyPin>>, |
| 190 | nss: Option<Peri<'d, AnyPin>>, | ||
| 148 | tx_dma: Option<ChannelAndRequest<'d>>, | 191 | tx_dma: Option<ChannelAndRequest<'d>>, |
| 149 | rx_dma: Option<ChannelAndRequest<'d>>, | 192 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 150 | config: Config, | 193 | config: Config, |
| @@ -155,6 +198,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 155 | sck, | 198 | sck, |
| 156 | mosi, | 199 | mosi, |
| 157 | miso, | 200 | miso, |
| 201 | nss, | ||
| 158 | tx_dma, | 202 | tx_dma, |
| 159 | rx_dma, | 203 | rx_dma, |
| 160 | current_word_size: <u8 as SealedWord>::CONFIG, | 204 | current_word_size: <u8 as SealedWord>::CONFIG, |
| @@ -183,12 +227,12 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 183 | w.set_cpha(cpha); | 227 | w.set_cpha(cpha); |
| 184 | w.set_cpol(cpol); | 228 | w.set_cpol(cpol); |
| 185 | 229 | ||
| 186 | w.set_mstr(vals::Mstr::MASTER); | 230 | w.set_mstr(CM::MASTER); |
| 187 | w.set_br(br); | 231 | w.set_br(br); |
| 188 | w.set_spe(true); | 232 | w.set_spe(true); |
| 189 | w.set_lsbfirst(lsbfirst); | 233 | w.set_lsbfirst(lsbfirst); |
| 190 | w.set_ssi(true); | 234 | w.set_ssi(CM::MASTER == vals::Mstr::MASTER); |
| 191 | w.set_ssm(true); | 235 | w.set_ssm(CM::MASTER == vals::Mstr::MASTER); |
| 192 | w.set_crcen(false); | 236 | w.set_crcen(false); |
| 193 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); | 237 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); |
| 194 | // we're doing "fake rxonly", by actually writing one | 238 | // we're doing "fake rxonly", by actually writing one |
| @@ -210,11 +254,11 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 210 | w.set_cpha(cpha); | 254 | w.set_cpha(cpha); |
| 211 | w.set_cpol(cpol); | 255 | w.set_cpol(cpol); |
| 212 | 256 | ||
| 213 | w.set_mstr(vals::Mstr::MASTER); | 257 | w.set_mstr(CM::MASTER); |
| 214 | w.set_br(br); | 258 | w.set_br(br); |
| 215 | w.set_lsbfirst(lsbfirst); | 259 | w.set_lsbfirst(lsbfirst); |
| 216 | w.set_ssi(true); | 260 | w.set_ssi(CM::MASTER == vals::Mstr::MASTER); |
| 217 | w.set_ssm(true); | 261 | w.set_ssm(CM::MASTER == vals::Mstr::MASTER); |
| 218 | w.set_crcen(false); | 262 | w.set_crcen(false); |
| 219 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); | 263 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); |
| 220 | w.set_spe(true); | 264 | w.set_spe(true); |
| @@ -229,8 +273,8 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 229 | w.set_cpha(cpha); | 273 | w.set_cpha(cpha); |
| 230 | w.set_cpol(cpol); | 274 | w.set_cpol(cpol); |
| 231 | w.set_lsbfirst(lsbfirst); | 275 | w.set_lsbfirst(lsbfirst); |
| 232 | w.set_ssm(true); | 276 | w.set_ssm(CM::MASTER == vals::Master::MASTER); |
| 233 | w.set_master(vals::Master::MASTER); | 277 | w.set_master(CM::MASTER); |
| 234 | w.set_comm(vals::Comm::FULL_DUPLEX); | 278 | w.set_comm(vals::Comm::FULL_DUPLEX); |
| 235 | w.set_ssom(vals::Ssom::ASSERTED); | 279 | w.set_ssom(vals::Ssom::ASSERTED); |
| 236 | w.set_midi(0); | 280 | w.set_midi(0); |
| @@ -469,7 +513,30 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 469 | } | 513 | } |
| 470 | } | 514 | } |
| 471 | 515 | ||
| 472 | impl<'d> Spi<'d, Blocking> { | 516 | impl<'d> Spi<'d, Blocking, Slave> { |
| 517 | /// Create a new blocking SPI slave driver. | ||
| 518 | pub fn new_blocking_slave<T: Instance, #[cfg(afio)] A>( | ||
| 519 | peri: Peri<'d, T>, | ||
| 520 | sck: Peri<'d, if_afio!(impl SckPin<T, A>)>, | ||
| 521 | mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>, | ||
| 522 | miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>, | ||
| 523 | cs: Peri<'d, if_afio!(impl CsPin<T, A>)>, | ||
| 524 | config: Config, | ||
| 525 | ) -> Self { | ||
| 526 | Self::new_inner( | ||
| 527 | peri, | ||
| 528 | new_pin!(sck, config.sck_af()), | ||
| 529 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | ||
| 530 | new_pin!(miso, AfType::input(config.miso_pull)), | ||
| 531 | new_pin!(cs, AfType::input(Pull::None)), | ||
| 532 | None, | ||
| 533 | None, | ||
| 534 | config, | ||
| 535 | ) | ||
| 536 | } | ||
| 537 | } | ||
| 538 | |||
| 539 | impl<'d> Spi<'d, Blocking, Master> { | ||
| 473 | /// Create a new blocking SPI driver. | 540 | /// Create a new blocking SPI driver. |
| 474 | pub fn new_blocking<T: Instance, #[cfg(afio)] A>( | 541 | pub fn new_blocking<T: Instance, #[cfg(afio)] A>( |
| 475 | peri: Peri<'d, T>, | 542 | peri: Peri<'d, T>, |
| @@ -485,6 +552,7 @@ impl<'d> Spi<'d, Blocking> { | |||
| 485 | new_pin!(miso, AfType::input(config.miso_pull)), | 552 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 486 | None, | 553 | None, |
| 487 | None, | 554 | None, |
| 555 | None, | ||
| 488 | config, | 556 | config, |
| 489 | ) | 557 | ) |
| 490 | } | 558 | } |
| @@ -503,6 +571,7 @@ impl<'d> Spi<'d, Blocking> { | |||
| 503 | new_pin!(miso, AfType::input(config.miso_pull)), | 571 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 504 | None, | 572 | None, |
| 505 | None, | 573 | None, |
| 574 | None, | ||
| 506 | config, | 575 | config, |
| 507 | ) | 576 | ) |
| 508 | } | 577 | } |
| @@ -521,6 +590,7 @@ impl<'d> Spi<'d, Blocking> { | |||
| 521 | None, | 590 | None, |
| 522 | None, | 591 | None, |
| 523 | None, | 592 | None, |
| 593 | None, | ||
| 524 | config, | 594 | config, |
| 525 | ) | 595 | ) |
| 526 | } | 596 | } |
| @@ -540,12 +610,38 @@ impl<'d> Spi<'d, Blocking> { | |||
| 540 | None, | 610 | None, |
| 541 | None, | 611 | None, |
| 542 | None, | 612 | None, |
| 613 | None, | ||
| 543 | config, | 614 | config, |
| 544 | ) | 615 | ) |
| 545 | } | 616 | } |
| 546 | } | 617 | } |
| 547 | 618 | ||
| 548 | impl<'d> Spi<'d, Async> { | 619 | impl<'d> Spi<'d, Async, Slave> { |
| 620 | /// Create a new SPI slave driver. | ||
| 621 | pub fn new_slave<T: Instance, #[cfg(afio)] A>( | ||
| 622 | peri: Peri<'d, T>, | ||
| 623 | sck: Peri<'d, if_afio!(impl SckPin<T, A>)>, | ||
| 624 | mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>, | ||
| 625 | miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>, | ||
| 626 | cs: Peri<'d, if_afio!(impl CsPin<T, A>)>, | ||
| 627 | tx_dma: Peri<'d, impl TxDma<T>>, | ||
| 628 | rx_dma: Peri<'d, impl RxDma<T>>, | ||
| 629 | config: Config, | ||
| 630 | ) -> Self { | ||
| 631 | Self::new_inner( | ||
| 632 | peri, | ||
| 633 | new_pin!(sck, config.sck_af()), | ||
| 634 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | ||
| 635 | new_pin!(miso, AfType::input(config.miso_pull)), | ||
| 636 | new_pin!(cs, AfType::input(Pull::None)), | ||
| 637 | new_dma!(tx_dma), | ||
| 638 | new_dma!(rx_dma), | ||
| 639 | config, | ||
| 640 | ) | ||
| 641 | } | ||
| 642 | } | ||
| 643 | |||
| 644 | impl<'d> Spi<'d, Async, Master> { | ||
| 549 | /// Create a new SPI driver. | 645 | /// Create a new SPI driver. |
| 550 | pub fn new<T: Instance, #[cfg(afio)] A>( | 646 | pub fn new<T: Instance, #[cfg(afio)] A>( |
| 551 | peri: Peri<'d, T>, | 647 | peri: Peri<'d, T>, |
| @@ -561,6 +657,7 @@ impl<'d> Spi<'d, Async> { | |||
| 561 | new_pin!(sck, config.sck_af()), | 657 | new_pin!(sck, config.sck_af()), |
| 562 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | 658 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 563 | new_pin!(miso, AfType::input(config.miso_pull)), | 659 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 660 | None, | ||
| 564 | new_dma!(tx_dma), | 661 | new_dma!(tx_dma), |
| 565 | new_dma!(rx_dma), | 662 | new_dma!(rx_dma), |
| 566 | config, | 663 | config, |
| @@ -581,6 +678,7 @@ impl<'d> Spi<'d, Async> { | |||
| 581 | new_pin!(sck, config.sck_af()), | 678 | new_pin!(sck, config.sck_af()), |
| 582 | None, | 679 | None, |
| 583 | new_pin!(miso, AfType::input(config.miso_pull)), | 680 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 681 | None, | ||
| 584 | #[cfg(any(spi_v1, spi_v2, spi_v3))] | 682 | #[cfg(any(spi_v1, spi_v2, spi_v3))] |
| 585 | new_dma!(tx_dma), | 683 | new_dma!(tx_dma), |
| 586 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | 684 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| @@ -603,6 +701,7 @@ impl<'d> Spi<'d, Async> { | |||
| 603 | new_pin!(sck, config.sck_af()), | 701 | new_pin!(sck, config.sck_af()), |
| 604 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | 702 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 605 | None, | 703 | None, |
| 704 | None, | ||
| 606 | new_dma!(tx_dma), | 705 | new_dma!(tx_dma), |
| 607 | None, | 706 | None, |
| 608 | config, | 707 | config, |
| @@ -623,6 +722,7 @@ impl<'d> Spi<'d, Async> { | |||
| 623 | None, | 722 | None, |
| 624 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | 723 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 625 | None, | 724 | None, |
| 725 | None, | ||
| 626 | new_dma!(tx_dma), | 726 | new_dma!(tx_dma), |
| 627 | None, | 727 | None, |
| 628 | config, | 728 | config, |
| @@ -646,7 +746,7 @@ impl<'d> Spi<'d, Async> { | |||
| 646 | config.bit_order = BitOrder::MsbFirst; | 746 | config.bit_order = BitOrder::MsbFirst; |
| 647 | config.frequency = freq; | 747 | config.frequency = freq; |
| 648 | 748 | ||
| 649 | Self::new_inner(peri, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config) | 749 | Self::new_inner(peri, None, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config) |
| 650 | } | 750 | } |
| 651 | 751 | ||
| 652 | #[allow(dead_code)] | 752 | #[allow(dead_code)] |
| @@ -656,9 +756,11 @@ impl<'d> Spi<'d, Async> { | |||
| 656 | rx_dma: Option<ChannelAndRequest<'d>>, | 756 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 657 | config: Config, | 757 | config: Config, |
| 658 | ) -> Self { | 758 | ) -> Self { |
| 659 | Self::new_inner(peri, None, None, None, tx_dma, rx_dma, config) | 759 | Self::new_inner(peri, None, None, None, None, tx_dma, rx_dma, config) |
| 660 | } | 760 | } |
| 761 | } | ||
| 661 | 762 | ||
| 763 | impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { | ||
| 662 | /// SPI write, using DMA. | 764 | /// SPI write, using DMA. |
| 663 | pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> { | 765 | pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> { |
| 664 | if data.is_empty() { | 766 | if data.is_empty() { |
| @@ -888,11 +990,12 @@ impl<'d> Spi<'d, Async> { | |||
| 888 | } | 990 | } |
| 889 | } | 991 | } |
| 890 | 992 | ||
| 891 | impl<'d, M: PeriMode> Drop for Spi<'d, M> { | 993 | impl<'d, M: PeriMode, CM: CommunicationMode> Drop for Spi<'d, M, CM> { |
| 892 | fn drop(&mut self) { | 994 | fn drop(&mut self) { |
| 893 | self.sck.as_ref().map(|x| x.set_as_disconnected()); | 995 | self.sck.as_ref().map(|x| x.set_as_disconnected()); |
| 894 | self.mosi.as_ref().map(|x| x.set_as_disconnected()); | 996 | self.mosi.as_ref().map(|x| x.set_as_disconnected()); |
| 895 | self.miso.as_ref().map(|x| x.set_as_disconnected()); | 997 | self.miso.as_ref().map(|x| x.set_as_disconnected()); |
| 998 | self.nss.as_ref().map(|x| x.set_as_disconnected()); | ||
| 896 | 999 | ||
| 897 | self.info.rcc.disable(); | 1000 | self.info.rcc.disable(); |
| 898 | } | 1001 | } |
| @@ -1127,7 +1230,7 @@ fn write_word<W: Word>(regs: Regs, tx_word: W) -> Result<(), Error> { | |||
| 1127 | // some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289 | 1230 | // some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289 |
| 1128 | macro_rules! impl_blocking { | 1231 | macro_rules! impl_blocking { |
| 1129 | ($w:ident) => { | 1232 | ($w:ident) => { |
| 1130 | impl<'d, M: PeriMode> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, M> { | 1233 | impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, M, CM> { |
| 1131 | type Error = Error; | 1234 | type Error = Error; |
| 1132 | 1235 | ||
| 1133 | fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> { | 1236 | fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> { |
| @@ -1135,7 +1238,7 @@ macro_rules! impl_blocking { | |||
| 1135 | } | 1238 | } |
| 1136 | } | 1239 | } |
| 1137 | 1240 | ||
| 1138 | impl<'d, M: PeriMode> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, M> { | 1241 | impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, M, CM> { |
| 1139 | type Error = Error; | 1242 | type Error = Error; |
| 1140 | 1243 | ||
| 1141 | fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> { | 1244 | fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> { |
| @@ -1149,11 +1252,11 @@ macro_rules! impl_blocking { | |||
| 1149 | impl_blocking!(u8); | 1252 | impl_blocking!(u8); |
| 1150 | impl_blocking!(u16); | 1253 | impl_blocking!(u16); |
| 1151 | 1254 | ||
| 1152 | impl<'d, M: PeriMode> embedded_hal_1::spi::ErrorType for Spi<'d, M> { | 1255 | impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_1::spi::ErrorType for Spi<'d, M, CM> { |
| 1153 | type Error = Error; | 1256 | type Error = Error; |
| 1154 | } | 1257 | } |
| 1155 | 1258 | ||
| 1156 | impl<'d, W: Word, M: PeriMode> embedded_hal_1::spi::SpiBus<W> for Spi<'d, M> { | 1259 | impl<'d, W: Word, M: PeriMode, CM: CommunicationMode> embedded_hal_1::spi::SpiBus<W> for Spi<'d, M, CM> { |
| 1157 | fn flush(&mut self) -> Result<(), Self::Error> { | 1260 | fn flush(&mut self) -> Result<(), Self::Error> { |
| 1158 | Ok(()) | 1261 | Ok(()) |
| 1159 | } | 1262 | } |
| @@ -1186,7 +1289,7 @@ impl embedded_hal_1::spi::Error for Error { | |||
| 1186 | } | 1289 | } |
| 1187 | } | 1290 | } |
| 1188 | 1291 | ||
| 1189 | impl<'d, W: Word> embedded_hal_async::spi::SpiBus<W> for Spi<'d, Async> { | 1292 | impl<'d, W: Word, CM: CommunicationMode> embedded_hal_async::spi::SpiBus<W> for Spi<'d, Async, CM> { |
| 1190 | async fn flush(&mut self) -> Result<(), Self::Error> { | 1293 | async fn flush(&mut self) -> Result<(), Self::Error> { |
| 1191 | Ok(()) | 1294 | Ok(()) |
| 1192 | } | 1295 | } |
| @@ -1328,7 +1431,7 @@ foreach_peripheral!( | |||
| 1328 | }; | 1431 | }; |
| 1329 | ); | 1432 | ); |
| 1330 | 1433 | ||
| 1331 | impl<'d, M: PeriMode> SetConfig for Spi<'d, M> { | 1434 | impl<'d, M: PeriMode, CM: CommunicationMode> SetConfig for Spi<'d, M, CM> { |
| 1332 | type Config = Config; | 1435 | type Config = Config; |
| 1333 | type ConfigError = (); | 1436 | type ConfigError = (); |
| 1334 | fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { | 1437 | fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { |
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 7db74bdf6..7db51d72e 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs | |||
| @@ -1,14 +1,16 @@ | |||
| 1 | #![allow(non_snake_case)] | 1 | #![allow(non_snake_case)] |
| 2 | 2 | ||
| 3 | use core::cell::{Cell, RefCell}; | 3 | use core::cell::{Cell, RefCell}; |
| 4 | use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; | 4 | #[cfg(all(feature = "low-power", stm32wlex))] |
| 5 | use core::sync::atomic::AtomicU16; | ||
| 6 | use core::sync::atomic::{AtomicU32, Ordering, compiler_fence}; | ||
| 5 | 7 | ||
| 6 | use critical_section::CriticalSection; | 8 | use critical_section::CriticalSection; |
| 7 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 8 | use embassy_sync::blocking_mutex::Mutex; | 9 | use embassy_sync::blocking_mutex::Mutex; |
| 10 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 9 | use embassy_time_driver::{Driver, TICK_HZ}; | 11 | use embassy_time_driver::{Driver, TICK_HZ}; |
| 10 | use embassy_time_queue_utils::Queue; | 12 | use embassy_time_queue_utils::Queue; |
| 11 | use stm32_metapac::timer::{regs, TimGp16}; | 13 | use stm32_metapac::timer::{TimGp16, regs}; |
| 12 | 14 | ||
| 13 | use crate::interrupt::typelevel::Interrupt; | 15 | use crate::interrupt::typelevel::Interrupt; |
| 14 | use crate::pac::timer::vals; | 16 | use crate::pac::timer::vals; |
| @@ -213,7 +215,13 @@ pub(crate) struct RtcDriver { | |||
| 213 | period: AtomicU32, | 215 | period: AtomicU32, |
| 214 | alarm: Mutex<CriticalSectionRawMutex, AlarmState>, | 216 | alarm: Mutex<CriticalSectionRawMutex, AlarmState>, |
| 215 | #[cfg(feature = "low-power")] | 217 | #[cfg(feature = "low-power")] |
| 216 | rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>, | 218 | pub(crate) rtc: Mutex<CriticalSectionRawMutex, RefCell<Option<Rtc>>>, |
| 219 | #[cfg(feature = "low-power")] | ||
| 220 | /// The minimum pause time beyond which the executor will enter a low-power state. | ||
| 221 | min_stop_pause: Mutex<CriticalSectionRawMutex, Cell<embassy_time::Duration>>, | ||
| 222 | /// Saved count for the timer (its value is lost when entering STOP2) | ||
| 223 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 224 | saved_count: AtomicU16, | ||
| 217 | queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>, | 225 | queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>, |
| 218 | } | 226 | } |
| 219 | 227 | ||
| @@ -221,12 +229,18 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { | |||
| 221 | period: AtomicU32::new(0), | 229 | period: AtomicU32::new(0), |
| 222 | alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), | 230 | alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), |
| 223 | #[cfg(feature = "low-power")] | 231 | #[cfg(feature = "low-power")] |
| 224 | rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), | 232 | rtc: Mutex::const_new(CriticalSectionRawMutex::new(), RefCell::new(None)), |
| 233 | #[cfg(feature = "low-power")] | ||
| 234 | min_stop_pause: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(embassy_time::Duration::from_millis(0))), | ||
| 235 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 236 | saved_count: AtomicU16::new(0), | ||
| 225 | queue: Mutex::new(RefCell::new(Queue::new())) | 237 | queue: Mutex::new(RefCell::new(Queue::new())) |
| 226 | }); | 238 | }); |
| 227 | 239 | ||
| 228 | impl RtcDriver { | 240 | impl RtcDriver { |
| 229 | fn init(&'static self, cs: critical_section::CriticalSection) { | 241 | /// initialize the timer, but don't start it. Used for chips like stm32wle5 |
| 242 | /// for low power where the timer config is lost in STOP2. | ||
| 243 | fn init_timer(&'static self, cs: critical_section::CriticalSection) { | ||
| 230 | let r = regs_gp16(); | 244 | let r = regs_gp16(); |
| 231 | 245 | ||
| 232 | rcc::enable_and_reset_with_cs::<T>(cs); | 246 | rcc::enable_and_reset_with_cs::<T>(cs); |
| @@ -259,10 +273,16 @@ impl RtcDriver { | |||
| 259 | w.set_ccie(0, true); | 273 | w.set_ccie(0, true); |
| 260 | }); | 274 | }); |
| 261 | 275 | ||
| 276 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 277 | r.cnt().write(|w| w.set_cnt(self.saved_count.load(Ordering::SeqCst))); | ||
| 278 | |||
| 262 | <T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend(); | 279 | <T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend(); |
| 263 | unsafe { <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable() }; | 280 | unsafe { <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable() }; |
| 281 | } | ||
| 264 | 282 | ||
| 265 | r.cr1().modify(|w| w.set_cen(true)); | 283 | fn init(&'static self, cs: CriticalSection) { |
| 284 | self.init_timer(cs); | ||
| 285 | regs_gp16().cr1().modify(|w| w.set_cen(true)); | ||
| 266 | } | 286 | } |
| 267 | 287 | ||
| 268 | fn on_interrupt(&self) { | 288 | fn on_interrupt(&self) { |
| @@ -379,27 +399,26 @@ impl RtcDriver { | |||
| 379 | #[cfg(feature = "low-power")] | 399 | #[cfg(feature = "low-power")] |
| 380 | /// Stop the wakeup alarm, if enabled, and add the appropriate offset | 400 | /// Stop the wakeup alarm, if enabled, and add the appropriate offset |
| 381 | fn stop_wakeup_alarm(&self, cs: CriticalSection) { | 401 | fn stop_wakeup_alarm(&self, cs: CriticalSection) { |
| 382 | if let Some(offset) = self.rtc.borrow(cs).get().unwrap().stop_wakeup_alarm(cs) { | 402 | if let Some(offset) = self.rtc.borrow(cs).borrow_mut().as_mut().unwrap().stop_wakeup_alarm(cs) { |
| 383 | self.add_time(offset, cs); | 403 | self.add_time(offset, cs); |
| 384 | } | 404 | } |
| 385 | } | 405 | } |
| 386 | 406 | ||
| 387 | /* | 407 | /* |
| 388 | Low-power public functions: all create a critical section | 408 | Low-power public functions: all create or require a critical section |
| 389 | */ | 409 | */ |
| 390 | #[cfg(feature = "low-power")] | 410 | #[cfg(feature = "low-power")] |
| 391 | /// Set the rtc but panic if it's already been set | 411 | pub(crate) fn set_min_stop_pause(&self, cs: CriticalSection, min_stop_pause: embassy_time::Duration) { |
| 392 | pub(crate) fn set_rtc(&self, rtc: &'static Rtc) { | 412 | self.min_stop_pause.borrow(cs).replace(min_stop_pause); |
| 393 | critical_section::with(|cs| { | ||
| 394 | rtc.stop_wakeup_alarm(cs); | ||
| 395 | |||
| 396 | assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none()) | ||
| 397 | }); | ||
| 398 | } | 413 | } |
| 399 | 414 | ||
| 400 | #[cfg(feature = "low-power")] | 415 | #[cfg(feature = "low-power")] |
| 401 | /// The minimum pause time beyond which the executor will enter a low-power state. | 416 | /// Set the rtc but panic if it's already been set |
| 402 | pub(crate) const MIN_STOP_PAUSE: embassy_time::Duration = embassy_time::Duration::from_millis(250); | 417 | pub(crate) fn set_rtc(&self, cs: CriticalSection, mut rtc: Rtc) { |
| 418 | rtc.stop_wakeup_alarm(cs); | ||
| 419 | |||
| 420 | assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none()); | ||
| 421 | } | ||
| 403 | 422 | ||
| 404 | #[cfg(feature = "low-power")] | 423 | #[cfg(feature = "low-power")] |
| 405 | /// Pause the timer if ready; return err if not | 424 | /// Pause the timer if ready; return err if not |
| @@ -413,17 +432,25 @@ impl RtcDriver { | |||
| 413 | self.stop_wakeup_alarm(cs); | 432 | self.stop_wakeup_alarm(cs); |
| 414 | 433 | ||
| 415 | let time_until_next_alarm = self.time_until_next_alarm(cs); | 434 | let time_until_next_alarm = self.time_until_next_alarm(cs); |
| 416 | if time_until_next_alarm < Self::MIN_STOP_PAUSE { | 435 | if time_until_next_alarm < self.min_stop_pause.borrow(cs).get() { |
| 436 | trace!( | ||
| 437 | "time_until_next_alarm < self.min_stop_pause ({})", | ||
| 438 | time_until_next_alarm | ||
| 439 | ); | ||
| 417 | Err(()) | 440 | Err(()) |
| 418 | } else { | 441 | } else { |
| 419 | self.rtc | 442 | self.rtc |
| 420 | .borrow(cs) | 443 | .borrow(cs) |
| 421 | .get() | 444 | .borrow_mut() |
| 445 | .as_mut() | ||
| 422 | .unwrap() | 446 | .unwrap() |
| 423 | .start_wakeup_alarm(time_until_next_alarm, cs); | 447 | .start_wakeup_alarm(time_until_next_alarm, cs); |
| 424 | 448 | ||
| 425 | regs_gp16().cr1().modify(|w| w.set_cen(false)); | 449 | regs_gp16().cr1().modify(|w| w.set_cen(false)); |
| 426 | 450 | // save the count for the timer as its lost in STOP2 for stm32wlex | |
| 451 | #[cfg(stm32wlex)] | ||
| 452 | self.saved_count | ||
| 453 | .store(regs_gp16().cnt().read().cnt() as u16, Ordering::SeqCst); | ||
| 427 | Ok(()) | 454 | Ok(()) |
| 428 | } | 455 | } |
| 429 | }) | 456 | }) |
| @@ -431,18 +458,16 @@ impl RtcDriver { | |||
| 431 | 458 | ||
| 432 | #[cfg(feature = "low-power")] | 459 | #[cfg(feature = "low-power")] |
| 433 | /// Resume the timer with the given offset | 460 | /// Resume the timer with the given offset |
| 434 | pub(crate) fn resume_time(&self) { | 461 | pub(crate) fn resume_time(&self, cs: CriticalSection) { |
| 435 | if regs_gp16().cr1().read().cen() { | 462 | if regs_gp16().cr1().read().cen() { |
| 436 | // Time isn't currently stopped | 463 | // Time isn't currently stopped |
| 437 | 464 | ||
| 438 | return; | 465 | return; |
| 439 | } | 466 | } |
| 440 | 467 | ||
| 441 | critical_section::with(|cs| { | 468 | self.stop_wakeup_alarm(cs); |
| 442 | self.stop_wakeup_alarm(cs); | ||
| 443 | 469 | ||
| 444 | regs_gp16().cr1().modify(|w| w.set_cen(true)); | 470 | regs_gp16().cr1().modify(|w| w.set_cen(true)); |
| 445 | }) | ||
| 446 | } | 471 | } |
| 447 | 472 | ||
| 448 | fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { | 473 | fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { |
| @@ -514,10 +539,15 @@ impl Driver for RtcDriver { | |||
| 514 | } | 539 | } |
| 515 | 540 | ||
| 516 | #[cfg(feature = "low-power")] | 541 | #[cfg(feature = "low-power")] |
| 517 | pub(crate) fn get_driver() -> &'static RtcDriver { | 542 | pub(crate) const fn get_driver() -> &'static RtcDriver { |
| 518 | &DRIVER | 543 | &DRIVER |
| 519 | } | 544 | } |
| 520 | 545 | ||
| 521 | pub(crate) fn init(cs: CriticalSection) { | 546 | pub(crate) fn init(cs: CriticalSection) { |
| 522 | DRIVER.init(cs) | 547 | DRIVER.init(cs) |
| 523 | } | 548 | } |
| 549 | |||
| 550 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 551 | pub(crate) fn init_timer(cs: CriticalSection) { | ||
| 552 | DRIVER.init_timer(cs) | ||
| 553 | } | ||
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 484aae1d0..9a56a41fb 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs | |||
| @@ -2,16 +2,16 @@ | |||
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | 4 | ||
| 5 | pub use stm32_metapac::timer::vals::{Ckd, Ossi, Ossr}; | 5 | pub use stm32_metapac::timer::vals::{Ckd, Mms2, Ossi, Ossr}; |
| 6 | 6 | ||
| 7 | use super::low_level::{CountingMode, OutputPolarity, Timer}; | 7 | use super::low_level::{CountingMode, OutputPolarity, Timer}; |
| 8 | use super::simple_pwm::PwmPin; | 8 | use super::simple_pwm::PwmPin; |
| 9 | use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin}; | 9 | use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin}; |
| 10 | use crate::Peri; | ||
| 10 | use crate::gpio::{AnyPin, OutputType}; | 11 | use crate::gpio::{AnyPin, OutputType}; |
| 11 | use crate::time::Hertz; | 12 | use crate::time::Hertz; |
| 12 | use crate::timer::low_level::OutputCompareMode; | ||
| 13 | use crate::timer::TimerChannel; | 13 | use crate::timer::TimerChannel; |
| 14 | use crate::Peri; | 14 | use crate::timer::low_level::OutputCompareMode; |
| 15 | 15 | ||
| 16 | /// Complementary PWM pin wrapper. | 16 | /// Complementary PWM pin wrapper. |
| 17 | /// | 17 | /// |
| @@ -136,6 +136,16 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 136 | self.inner.get_moe() | 136 | self.inner.get_moe() |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | /// Set Master Slave Mode 2 | ||
| 140 | pub fn set_mms2(&mut self, mms2: Mms2) { | ||
| 141 | self.inner.set_mms2_selection(mms2); | ||
| 142 | } | ||
| 143 | |||
| 144 | /// Set Repetition Counter | ||
| 145 | pub fn set_repetition_counter(&mut self, val: u16) { | ||
| 146 | self.inner.set_repetition_counter(val); | ||
| 147 | } | ||
| 148 | |||
| 139 | /// Enable the given channel. | 149 | /// Enable the given channel. |
| 140 | pub fn enable(&mut self, channel: Channel) { | 150 | pub fn enable(&mut self, channel: Channel) { |
| 141 | self.inner.enable_channel(channel, true); | 151 | self.inner.enable_channel(channel, true); |
| @@ -388,7 +398,7 @@ fn compute_dead_time_value(value: u16) -> (Ckd, u8) { | |||
| 388 | 398 | ||
| 389 | #[cfg(test)] | 399 | #[cfg(test)] |
| 390 | mod tests { | 400 | mod tests { |
| 391 | use super::{compute_dead_time_value, Ckd}; | 401 | use super::{Ckd, compute_dead_time_value}; |
| 392 | 402 | ||
| 393 | #[test] | 403 | #[test] |
| 394 | fn test_compute_dead_time_value() { | 404 | fn test_compute_dead_time_value() { |
diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index 7a25e6c21..2a4ec2db0 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs | |||
| @@ -8,11 +8,11 @@ use core::task::{Context, Poll}; | |||
| 8 | use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer}; | 8 | use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer}; |
| 9 | use super::{CaptureCompareInterruptHandler, Channel, GeneralInstance4Channel, TimerPin}; | 9 | use super::{CaptureCompareInterruptHandler, Channel, GeneralInstance4Channel, TimerPin}; |
| 10 | pub use super::{Ch1, Ch2, Ch3, Ch4}; | 10 | pub use super::{Ch1, Ch2, Ch3, Ch4}; |
| 11 | use crate::Peri; | ||
| 11 | use crate::gpio::{AfType, AnyPin, Pull}; | 12 | use crate::gpio::{AfType, AnyPin, Pull}; |
| 12 | use crate::interrupt::typelevel::{Binding, Interrupt}; | 13 | use crate::interrupt::typelevel::{Binding, Interrupt}; |
| 13 | use crate::time::Hertz; | 14 | use crate::time::Hertz; |
| 14 | use crate::timer::TimerChannel; | 15 | use crate::timer::TimerChannel; |
| 15 | use crate::Peri; | ||
| 16 | 16 | ||
| 17 | /// Capture pin wrapper. | 17 | /// Capture pin wrapper. |
| 18 | /// | 18 | /// |
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index ac039bb0d..0122fe4f7 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs | |||
| @@ -10,7 +10,7 @@ use core::mem::ManuallyDrop; | |||
| 10 | 10 | ||
| 11 | use embassy_hal_internal::Peri; | 11 | use embassy_hal_internal::Peri; |
| 12 | // Re-export useful enums | 12 | // Re-export useful enums |
| 13 | pub use stm32_metapac::timer::vals::{FilterValue, Sms as SlaveMode, Ts as TriggerSource}; | 13 | pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource}; |
| 14 | 14 | ||
| 15 | use super::*; | 15 | use super::*; |
| 16 | use crate::pac::timer::vals; | 16 | use crate::pac::timer::vals; |
| @@ -143,20 +143,69 @@ pub enum OutputCompareMode { | |||
| 143 | /// TIMx_CNT<TIMx_CCRx else active. In downcounting, channel is active as long as | 143 | /// TIMx_CNT<TIMx_CCRx else active. In downcounting, channel is active as long as |
| 144 | /// TIMx_CNT>TIMx_CCRx else inactive. | 144 | /// TIMx_CNT>TIMx_CCRx else inactive. |
| 145 | PwmMode2, | 145 | PwmMode2, |
| 146 | // TODO: there's more modes here depending on the chip family. | 146 | |
| 147 | #[cfg(timer_v2)] | ||
| 148 | /// In up-counting mode, the channel is active until a trigger | ||
| 149 | /// event is detected (on tim_trgi signal). Then, a comparison is performed as in PWM | ||
| 150 | /// mode 1 and the channels becomes active again at the next update. In down-counting | ||
| 151 | /// mode, the channel is inactive until a trigger event is detected (on tim_trgi signal). | ||
| 152 | /// Then, a comparison is performed as in PWM mode 1 and the channels becomes | ||
| 153 | /// inactive again at the next update. | ||
| 154 | OnePulseMode1, | ||
| 155 | |||
| 156 | #[cfg(timer_v2)] | ||
| 157 | /// In up-counting mode, the channel is inactive until a | ||
| 158 | /// trigger event is detected (on tim_trgi signal). Then, a comparison is performed as in | ||
| 159 | /// PWM mode 2 and the channels becomes inactive again at the next update. In down | ||
| 160 | /// counting mode, the channel is active until a trigger event is detected (on tim_trgi | ||
| 161 | /// signal). Then, a comparison is performed as in PWM mode 1 and the channels | ||
| 162 | /// becomes active again at the next update. | ||
| 163 | OnePulseMode2, | ||
| 164 | |||
| 165 | #[cfg(timer_v2)] | ||
| 166 | /// Combined PWM mode 1 - tim_oc1ref has the same behavior as in PWM mode 1. | ||
| 167 | /// tim_oc1refc is the logical OR between tim_oc1ref and tim_oc2ref. | ||
| 168 | CombinedPwmMode1, | ||
| 169 | |||
| 170 | #[cfg(timer_v2)] | ||
| 171 | /// Combined PWM mode 2 - tim_oc1ref has the same behavior as in PWM mode 2. | ||
| 172 | /// tim_oc1refc is the logical AND between tim_oc1ref and tim_oc2ref. | ||
| 173 | CombinedPwmMode2, | ||
| 174 | |||
| 175 | #[cfg(timer_v2)] | ||
| 176 | /// tim_oc1ref has the same behavior as in PWM mode 1. tim_oc1refc outputs tim_oc1ref | ||
| 177 | /// when the counter is counting up, tim_oc2ref when it is counting down. | ||
| 178 | AsymmetricPwmMode1, | ||
| 179 | |||
| 180 | #[cfg(timer_v2)] | ||
| 181 | /// tim_oc1ref has the same behavior as in PWM mode 2. tim_oc1refc outputs tim_oc1ref | ||
| 182 | /// when the counter is counting up, tim_oc2ref when it is counting down. | ||
| 183 | AsymmetricPwmMode2, | ||
| 147 | } | 184 | } |
| 148 | 185 | ||
| 149 | impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm { | 186 | impl From<OutputCompareMode> for crate::pac::timer::vals::Ocm { |
| 150 | fn from(mode: OutputCompareMode) -> Self { | 187 | fn from(mode: OutputCompareMode) -> Self { |
| 151 | match mode { | 188 | match mode { |
| 152 | OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN, | 189 | OutputCompareMode::Frozen => crate::pac::timer::vals::Ocm::FROZEN, |
| 153 | OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVE_ON_MATCH, | 190 | OutputCompareMode::ActiveOnMatch => crate::pac::timer::vals::Ocm::ACTIVE_ON_MATCH, |
| 154 | OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVE_ON_MATCH, | 191 | OutputCompareMode::InactiveOnMatch => crate::pac::timer::vals::Ocm::INACTIVE_ON_MATCH, |
| 155 | OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE, | 192 | OutputCompareMode::Toggle => crate::pac::timer::vals::Ocm::TOGGLE, |
| 156 | OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCE_INACTIVE, | 193 | OutputCompareMode::ForceInactive => crate::pac::timer::vals::Ocm::FORCE_INACTIVE, |
| 157 | OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCE_ACTIVE, | 194 | OutputCompareMode::ForceActive => crate::pac::timer::vals::Ocm::FORCE_ACTIVE, |
| 158 | OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWM_MODE1, | 195 | OutputCompareMode::PwmMode1 => crate::pac::timer::vals::Ocm::PWM_MODE1, |
| 159 | OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWM_MODE2, | 196 | OutputCompareMode::PwmMode2 => crate::pac::timer::vals::Ocm::PWM_MODE2, |
| 197 | #[cfg(timer_v2)] | ||
| 198 | OutputCompareMode::OnePulseMode1 => crate::pac::timer::vals::Ocm::RETRIGERRABLE_OPM_MODE_1, | ||
| 199 | #[cfg(timer_v2)] | ||
| 200 | OutputCompareMode::OnePulseMode2 => crate::pac::timer::vals::Ocm::RETRIGERRABLE_OPM_MODE_2, | ||
| 201 | #[cfg(timer_v2)] | ||
| 202 | OutputCompareMode::CombinedPwmMode1 => crate::pac::timer::vals::Ocm::COMBINED_PWM_MODE_1, | ||
| 203 | #[cfg(timer_v2)] | ||
| 204 | OutputCompareMode::CombinedPwmMode2 => crate::pac::timer::vals::Ocm::COMBINED_PWM_MODE_2, | ||
| 205 | #[cfg(timer_v2)] | ||
| 206 | OutputCompareMode::AsymmetricPwmMode1 => crate::pac::timer::vals::Ocm::ASYMMETRIC_PWM_MODE_1, | ||
| 207 | #[cfg(timer_v2)] | ||
| 208 | OutputCompareMode::AsymmetricPwmMode2 => crate::pac::timer::vals::Ocm::ASYMMETRIC_PWM_MODE_2, | ||
| 160 | } | 209 | } |
| 161 | } | 210 | } |
| 162 | } | 211 | } |
| @@ -640,6 +689,11 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { | |||
| 640 | self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde)) | 689 | self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde)) |
| 641 | } | 690 | } |
| 642 | 691 | ||
| 692 | /// Set Timer Master Mode | ||
| 693 | pub fn set_master_mode(&self, mms: MasterMode) { | ||
| 694 | self.regs_gp16().cr2().modify(|w| w.set_mms(mms)); | ||
| 695 | } | ||
| 696 | |||
| 643 | /// Set Timer Slave Mode | 697 | /// Set Timer Slave Mode |
| 644 | pub fn set_slave_mode(&self, sms: SlaveMode) { | 698 | pub fn set_slave_mode(&self, sms: SlaveMode) { |
| 645 | self.regs_gp16().smcr().modify(|r| r.set_sms(sms)); | 699 | self.regs_gp16().smcr().modify(|r| r.set_sms(sms)); |
| @@ -760,6 +814,16 @@ impl<'d, T: AdvancedInstance4Channel> Timer<'d, T> { | |||
| 760 | self.regs_advanced().cr2().modify(|w| w.set_oisn(channel.index(), val)); | 814 | self.regs_advanced().cr2().modify(|w| w.set_oisn(channel.index(), val)); |
| 761 | } | 815 | } |
| 762 | 816 | ||
| 817 | /// Set master mode selection 2 | ||
| 818 | pub fn set_mms2_selection(&self, mms2: vals::Mms2) { | ||
| 819 | self.regs_advanced().cr2().modify(|w| w.set_mms2(mms2)); | ||
| 820 | } | ||
| 821 | |||
| 822 | /// Set repetition counter | ||
| 823 | pub fn set_repetition_counter(&self, val: u16) { | ||
| 824 | self.regs_advanced().rcr().modify(|w| w.set_rep(val)); | ||
| 825 | } | ||
| 826 | |||
| 763 | /// Trigger software break 1 or 2 | 827 | /// Trigger software break 1 or 2 |
| 764 | /// Setting this bit generates a break event. This bit is automatically cleared by the hardware. | 828 | /// Setting this bit generates a break event. This bit is automatically cleared by the hardware. |
| 765 | pub fn trigger_software_break(&self, n: usize) { | 829 | pub fn trigger_software_break(&self, n: usize) { |
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index b09bc7166..804d1ef37 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs | |||
| @@ -399,7 +399,7 @@ pub struct UpdateInterruptHandler<T: CoreInstance> { | |||
| 399 | impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> { | 399 | impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> { |
| 400 | unsafe fn on_interrupt() { | 400 | unsafe fn on_interrupt() { |
| 401 | #[cfg(feature = "low-power")] | 401 | #[cfg(feature = "low-power")] |
| 402 | crate::low_power::on_wakeup_irq(); | 402 | crate::low_power::Executor::on_wakeup_irq(); |
| 403 | 403 | ||
| 404 | let regs = crate::pac::timer::TimCore::from_ptr(T::regs()); | 404 | let regs = crate::pac::timer::TimCore::from_ptr(T::regs()); |
| 405 | 405 | ||
| @@ -429,7 +429,7 @@ impl<T: GeneralInstance1Channel> interrupt::typelevel::Handler<T::CaptureCompare | |||
| 429 | { | 429 | { |
| 430 | unsafe fn on_interrupt() { | 430 | unsafe fn on_interrupt() { |
| 431 | #[cfg(feature = "low-power")] | 431 | #[cfg(feature = "low-power")] |
| 432 | crate::low_power::on_wakeup_irq(); | 432 | crate::low_power::Executor::on_wakeup_irq(); |
| 433 | 433 | ||
| 434 | let regs = crate::pac::timer::TimGp16::from_ptr(T::regs()); | 434 | let regs = crate::pac::timer::TimGp16::from_ptr(T::regs()); |
| 435 | 435 | ||
diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs index a75b41bd7..fe8681356 100644 --- a/embassy-stm32/src/timer/one_pulse.rs +++ b/embassy-stm32/src/timer/one_pulse.rs | |||
| @@ -11,12 +11,12 @@ use super::low_level::{ | |||
| 11 | }; | 11 | }; |
| 12 | use super::{CaptureCompareInterruptHandler, Channel, ExternalTriggerPin, GeneralInstance4Channel, TimerPin}; | 12 | use super::{CaptureCompareInterruptHandler, Channel, ExternalTriggerPin, GeneralInstance4Channel, TimerPin}; |
| 13 | pub use super::{Ch1, Ch2}; | 13 | pub use super::{Ch1, Ch2}; |
| 14 | use crate::Peri; | ||
| 14 | use crate::gpio::{AfType, AnyPin, Pull}; | 15 | use crate::gpio::{AfType, AnyPin, Pull}; |
| 15 | use crate::interrupt::typelevel::{Binding, Interrupt}; | 16 | use crate::interrupt::typelevel::{Binding, Interrupt}; |
| 16 | use crate::pac::timer::vals::Etp; | 17 | use crate::pac::timer::vals::Etp; |
| 17 | use crate::time::Hertz; | 18 | use crate::time::Hertz; |
| 18 | use crate::timer::TimerChannel; | 19 | use crate::timer::TimerChannel; |
| 19 | use crate::Peri; | ||
| 20 | 20 | ||
| 21 | /// External input marker type. | 21 | /// External input marker type. |
| 22 | pub enum Ext {} | 22 | pub enum Ext {} |
diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index 159b5a177..da8a79b09 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs | |||
| @@ -2,9 +2,9 @@ | |||
| 2 | 2 | ||
| 3 | use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource}; | 3 | use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource}; |
| 4 | use super::{Ch1, Ch2, Channel, GeneralInstance4Channel, TimerPin}; | 4 | use super::{Ch1, Ch2, Channel, GeneralInstance4Channel, TimerPin}; |
| 5 | use crate::Peri; | ||
| 5 | use crate::gpio::{AfType, Pull}; | 6 | use crate::gpio::{AfType, Pull}; |
| 6 | use crate::time::Hertz; | 7 | use crate::time::Hertz; |
| 7 | use crate::Peri; | ||
| 8 | 8 | ||
| 9 | /// PWM Input driver. | 9 | /// PWM Input driver. |
| 10 | /// | 10 | /// |
diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index 82b5968b0..a547a2a19 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs | |||
| @@ -1,45 +1,67 @@ | |||
| 1 | //! Quadrature decoder using a timer. | 1 | //! Quadrature decoder using a timer. |
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | use stm32_metapac::timer::vals::{self, Sms}; |
| 4 | |||
| 5 | use stm32_metapac::timer::vals; | ||
| 6 | 4 | ||
| 7 | use super::low_level::Timer; | 5 | use super::low_level::Timer; |
| 8 | pub use super::{Ch1, Ch2}; | 6 | pub use super::{Ch1, Ch2}; |
| 9 | use super::{GeneralInstance4Channel, TimerPin}; | 7 | use super::{GeneralInstance4Channel, TimerPin}; |
| 8 | use crate::Peri; | ||
| 10 | use crate::gpio::{AfType, AnyPin, Pull}; | 9 | use crate::gpio::{AfType, AnyPin, Pull}; |
| 11 | use crate::timer::TimerChannel; | 10 | use crate::timer::TimerChannel; |
| 12 | use crate::Peri; | ||
| 13 | 11 | ||
| 14 | /// Counting direction | 12 | /// Qei driver config. |
| 15 | pub enum Direction { | 13 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 16 | /// Counting up. | 14 | #[derive(Clone, Copy)] |
| 17 | Upcounting, | 15 | pub struct Config { |
| 18 | /// Counting down. | 16 | /// Configures the internal pull up/down resistor for Qei's channel 1 pin. |
| 19 | Downcounting, | 17 | pub ch1_pull: Pull, |
| 18 | /// Configures the internal pull up/down resistor for Qei's channel 2 pin. | ||
| 19 | pub ch2_pull: Pull, | ||
| 20 | /// Specifies the encoder mode to use for the Qei peripheral. | ||
| 21 | pub mode: QeiMode, | ||
| 20 | } | 22 | } |
| 21 | 23 | ||
| 22 | /// Wrapper for using a pin with QEI. | 24 | impl Default for Config { |
| 23 | pub struct QeiPin<'d, T, Channel, #[cfg(afio)] A> { | 25 | /// Arbitrary defaults to preserve backwards compatibility |
| 24 | #[allow(unused)] | 26 | fn default() -> Self { |
| 25 | pin: Peri<'d, AnyPin>, | 27 | Self { |
| 26 | phantom: PhantomData<if_afio!((T, Channel, A))>, | 28 | ch1_pull: Pull::None, |
| 29 | ch2_pull: Pull::None, | ||
| 30 | mode: QeiMode::Mode3, | ||
| 31 | } | ||
| 32 | } | ||
| 27 | } | 33 | } |
| 28 | 34 | ||
| 29 | impl<'d, T: GeneralInstance4Channel, C: QeiChannel, #[cfg(afio)] A> if_afio!(QeiPin<'d, T, C, A>) { | 35 | /// See STMicro AN4013 for §2.3 for more information |
| 30 | /// Create a new QEI pin instance. | 36 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 31 | pub fn new(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>) -> Self { | 37 | #[derive(Clone, Copy)] |
| 32 | critical_section::with(|_| { | 38 | pub enum QeiMode { |
| 33 | pin.set_low(); | 39 | /// Direct alias for [`Sms::ENCODER_MODE_1`] |
| 34 | set_as_af!(pin, AfType::input(Pull::None)); | 40 | Mode1, |
| 35 | }); | 41 | /// Direct alias for [`Sms::ENCODER_MODE_2`] |
| 36 | QeiPin { | 42 | Mode2, |
| 37 | pin: pin.into(), | 43 | /// Direct alias for [`Sms::ENCODER_MODE_3`] |
| 38 | phantom: PhantomData, | 44 | Mode3, |
| 45 | } | ||
| 46 | |||
| 47 | impl From<QeiMode> for Sms { | ||
| 48 | fn from(mode: QeiMode) -> Self { | ||
| 49 | match mode { | ||
| 50 | QeiMode::Mode1 => Sms::ENCODER_MODE_1, | ||
| 51 | QeiMode::Mode2 => Sms::ENCODER_MODE_2, | ||
| 52 | QeiMode::Mode3 => Sms::ENCODER_MODE_3, | ||
| 39 | } | 53 | } |
| 40 | } | 54 | } |
| 41 | } | 55 | } |
| 42 | 56 | ||
| 57 | /// Counting direction | ||
| 58 | pub enum Direction { | ||
| 59 | /// Counting up. | ||
| 60 | Upcounting, | ||
| 61 | /// Counting down. | ||
| 62 | Downcounting, | ||
| 63 | } | ||
| 64 | |||
| 43 | trait SealedQeiChannel: TimerChannel {} | 65 | trait SealedQeiChannel: TimerChannel {} |
| 44 | 66 | ||
| 45 | /// Marker trait for a timer channel eligible for use with QEI. | 67 | /// Marker trait for a timer channel eligible for use with QEI. |
| @@ -55,20 +77,28 @@ impl SealedQeiChannel for Ch2 {} | |||
| 55 | /// Quadrature decoder driver. | 77 | /// Quadrature decoder driver. |
| 56 | pub struct Qei<'d, T: GeneralInstance4Channel> { | 78 | pub struct Qei<'d, T: GeneralInstance4Channel> { |
| 57 | inner: Timer<'d, T>, | 79 | inner: Timer<'d, T>, |
| 80 | _ch1: Peri<'d, AnyPin>, | ||
| 81 | _ch2: Peri<'d, AnyPin>, | ||
| 58 | } | 82 | } |
| 59 | 83 | ||
| 60 | impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { | 84 | impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { |
| 61 | /// Create a new quadrature decoder driver. | 85 | /// Create a new quadrature decoder driver, with a given [`Config`]. |
| 62 | #[allow(unused)] | 86 | #[allow(unused)] |
| 63 | pub fn new<#[cfg(afio)] A>( | 87 | pub fn new<CH1: QeiChannel, CH2: QeiChannel, #[cfg(afio)] A>( |
| 64 | tim: Peri<'d, T>, | 88 | tim: Peri<'d, T>, |
| 65 | ch1: if_afio!(QeiPin<'d, T, Ch1, A>), | 89 | ch1: Peri<'d, if_afio!(impl TimerPin<T, CH1, A>)>, |
| 66 | ch2: if_afio!(QeiPin<'d, T, Ch2, A>), | 90 | ch2: Peri<'d, if_afio!(impl TimerPin<T, CH2, A>)>, |
| 91 | config: Config, | ||
| 67 | ) -> Self { | 92 | ) -> Self { |
| 68 | Self::new_inner(tim) | 93 | // Configure the pins to be used for the QEI peripheral. |
| 69 | } | 94 | critical_section::with(|_| { |
| 95 | ch1.set_low(); | ||
| 96 | set_as_af!(ch1, AfType::input(config.ch1_pull)); | ||
| 97 | |||
| 98 | ch2.set_low(); | ||
| 99 | set_as_af!(ch2, AfType::input(config.ch2_pull)); | ||
| 100 | }); | ||
| 70 | 101 | ||
| 71 | fn new_inner(tim: Peri<'d, T>) -> Self { | ||
| 72 | let inner = Timer::new(tim); | 102 | let inner = Timer::new(tim); |
| 73 | let r = inner.regs_gp16(); | 103 | let r = inner.regs_gp16(); |
| 74 | 104 | ||
| @@ -88,13 +118,17 @@ impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { | |||
| 88 | }); | 118 | }); |
| 89 | 119 | ||
| 90 | r.smcr().modify(|w| { | 120 | r.smcr().modify(|w| { |
| 91 | w.set_sms(vals::Sms::ENCODER_MODE_3); | 121 | w.set_sms(config.mode.into()); |
| 92 | }); | 122 | }); |
| 93 | 123 | ||
| 94 | r.arr().modify(|w| w.set_arr(u16::MAX)); | 124 | r.arr().modify(|w| w.set_arr(u16::MAX)); |
| 95 | r.cr1().modify(|w| w.set_cen(true)); | 125 | r.cr1().modify(|w| w.set_cen(true)); |
| 96 | 126 | ||
| 97 | Self { inner } | 127 | Self { |
| 128 | inner, | ||
| 129 | _ch1: ch1.into(), | ||
| 130 | _ch2: ch2.into(), | ||
| 131 | } | ||
| 98 | } | 132 | } |
| 99 | 133 | ||
| 100 | /// Get direction. | 134 | /// Get direction. |
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index e6165e42b..36303aeb4 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs | |||
| @@ -5,11 +5,11 @@ use core::mem::ManuallyDrop; | |||
| 5 | 5 | ||
| 6 | use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; | 6 | use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; |
| 7 | use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin}; | 7 | use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin}; |
| 8 | use crate::Peri; | ||
| 8 | #[cfg(gpio_v2)] | 9 | #[cfg(gpio_v2)] |
| 9 | use crate::gpio::Pull; | 10 | use crate::gpio::Pull; |
| 10 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 11 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
| 11 | use crate::time::Hertz; | 12 | use crate::time::Hertz; |
| 12 | use crate::Peri; | ||
| 13 | 13 | ||
| 14 | /// PWM pin wrapper. | 14 | /// PWM pin wrapper. |
| 15 | /// | 15 | /// |
| @@ -339,14 +339,33 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 339 | ..Default::default() | 339 | ..Default::default() |
| 340 | }; | 340 | }; |
| 341 | 341 | ||
| 342 | Transfer::new_write( | 342 | match self.inner.bits() { |
| 343 | dma, | 343 | TimerBits::Bits16 => { |
| 344 | req, | 344 | Transfer::new_write( |
| 345 | duty, | 345 | dma, |
| 346 | self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, | 346 | req, |
| 347 | dma_transfer_option, | 347 | duty, |
| 348 | ) | 348 | self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, |
| 349 | .await | 349 | dma_transfer_option, |
| 350 | ) | ||
| 351 | .await | ||
| 352 | } | ||
| 353 | #[cfg(not(any(stm32l0)))] | ||
| 354 | TimerBits::Bits32 => { | ||
| 355 | #[cfg(not(any(bdma, gpdma)))] | ||
| 356 | panic!("unsupported timer bits"); | ||
| 357 | |||
| 358 | #[cfg(any(bdma, gpdma))] | ||
| 359 | Transfer::new_write( | ||
| 360 | dma, | ||
| 361 | req, | ||
| 362 | duty, | ||
| 363 | self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32, | ||
| 364 | dma_transfer_option, | ||
| 365 | ) | ||
| 366 | .await | ||
| 367 | } | ||
| 368 | }; | ||
| 350 | }; | 369 | }; |
| 351 | 370 | ||
| 352 | // restore output compare state | 371 | // restore output compare state |
diff --git a/embassy-stm32/src/tsc/acquisition_banks.rs b/embassy-stm32/src/tsc/acquisition_banks.rs index 7d6442b48..097cf7942 100644 --- a/embassy-stm32/src/tsc/acquisition_banks.rs +++ b/embassy-stm32/src/tsc/acquisition_banks.rs | |||
| @@ -1,11 +1,11 @@ | |||
| 1 | use super::TSC_NUM_GROUPS; | ||
| 1 | use super::io_pin::*; | 2 | use super::io_pin::*; |
| 2 | #[cfg(any(tsc_v2, tsc_v3))] | 3 | #[cfg(any(tsc_v2, tsc_v3))] |
| 3 | use super::pin_groups::G7; | 4 | use super::pin_groups::G7; |
| 4 | #[cfg(tsc_v3)] | 5 | #[cfg(tsc_v3)] |
| 5 | use super::pin_groups::G8; | 6 | use super::pin_groups::G8; |
| 6 | use super::pin_groups::{pin_roles, G1, G2, G3, G4, G5, G6}; | 7 | use super::pin_groups::{G1, G2, G3, G4, G5, G6, pin_roles}; |
| 7 | use super::types::{Group, GroupStatus}; | 8 | use super::types::{Group, GroupStatus}; |
| 8 | use super::TSC_NUM_GROUPS; | ||
| 9 | 9 | ||
| 10 | /// Represents a collection of TSC (Touch Sensing Controller) pins for an acquisition bank. | 10 | /// Represents a collection of TSC (Touch Sensing Controller) pins for an acquisition bank. |
| 11 | /// | 11 | /// |
diff --git a/embassy-stm32/src/tsc/pin_groups.rs b/embassy-stm32/src/tsc/pin_groups.rs index 84421f7ff..9347e6bc0 100644 --- a/embassy-stm32/src/tsc/pin_groups.rs +++ b/embassy-stm32/src/tsc/pin_groups.rs | |||
| @@ -1,11 +1,11 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | use core::marker::PhantomData; |
| 2 | use core::ops::BitOr; | 2 | use core::ops::BitOr; |
| 3 | 3 | ||
| 4 | use super::Instance; | ||
| 4 | use super::errors::GroupError; | 5 | use super::errors::GroupError; |
| 5 | use super::io_pin::*; | 6 | use super::io_pin::*; |
| 6 | use super::Instance; | ||
| 7 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | ||
| 8 | use crate::Peri; | 7 | use crate::Peri; |
| 8 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | ||
| 9 | 9 | ||
| 10 | /// Pin type definition to control IO parameters | 10 | /// Pin type definition to control IO parameters |
| 11 | #[derive(PartialEq, Clone, Copy)] | 11 | #[derive(PartialEq, Clone, Copy)] |
diff --git a/embassy-stm32/src/ucpd.rs b/embassy-stm32/src/ucpd.rs index 18aff4fbd..8f259a917 100644 --- a/embassy-stm32/src/ucpd.rs +++ b/embassy-stm32/src/ucpd.rs | |||
| @@ -19,8 +19,8 @@ use core::marker::PhantomData; | |||
| 19 | use core::sync::atomic::{AtomicBool, Ordering}; | 19 | use core::sync::atomic::{AtomicBool, Ordering}; |
| 20 | use core::task::Poll; | 20 | use core::task::Poll; |
| 21 | 21 | ||
| 22 | use embassy_hal_internal::drop::OnDrop; | ||
| 23 | use embassy_hal_internal::PeripheralType; | 22 | use embassy_hal_internal::PeripheralType; |
| 23 | use embassy_hal_internal::drop::OnDrop; | ||
| 24 | use embassy_sync::waitqueue::AtomicWaker; | 24 | use embassy_sync::waitqueue::AtomicWaker; |
| 25 | 25 | ||
| 26 | use crate::dma::{ChannelAndRequest, TransferOptions}; | 26 | use crate::dma::{ChannelAndRequest, TransferOptions}; |
| @@ -28,7 +28,7 @@ use crate::interrupt::typelevel::Interrupt; | |||
| 28 | use crate::pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk, Txmode}; | 28 | use crate::pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk, Txmode}; |
| 29 | pub use crate::pac::ucpd::vals::{Phyccsel as CcSel, Rxordset, TypecVstateCc as CcVState}; | 29 | pub use crate::pac::ucpd::vals::{Phyccsel as CcSel, Rxordset, TypecVstateCc as CcVState}; |
| 30 | use crate::rcc::{self, RccPeripheral}; | 30 | use crate::rcc::{self, RccPeripheral}; |
| 31 | use crate::{interrupt, Peri}; | 31 | use crate::{Peri, interrupt}; |
| 32 | 32 | ||
| 33 | pub(crate) fn init( | 33 | pub(crate) fn init( |
| 34 | _cs: critical_section::CriticalSection, | 34 | _cs: critical_section::CriticalSection, |
diff --git a/embassy-stm32/src/uid.rs b/embassy-stm32/src/uid.rs index 5e38532bd..2d3e2b972 100644 --- a/embassy-stm32/src/uid.rs +++ b/embassy-stm32/src/uid.rs | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | //! Unique ID (UID) | 1 | //! Unique ID (UID) |
| 2 | 2 | ||
| 3 | /// Get this device's unique 96-bit ID. | 3 | /// Get this device's unique 96-bit ID. |
| 4 | pub fn uid() -> &'static [u8; 12] { | 4 | pub fn uid() -> [u8; 12] { |
| 5 | unsafe { &*crate::pac::UID.uid(0).as_ptr().cast::<[u8; 12]>() } | 5 | unsafe { *crate::pac::UID.uid(0).as_ptr().cast::<[u8; 12]>() } |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | /// Get this device's unique 96-bit ID, encoded into a string of 24 hexadecimal ASCII digits. | 8 | /// Get this device's unique 96-bit ID, encoded into a string of 24 hexadecimal ASCII digits. |
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index c734eed49..69c3a740f 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs | |||
| @@ -1,20 +1,20 @@ | |||
| 1 | use core::future::poll_fn; | 1 | use core::future::poll_fn; |
| 2 | use core::marker::PhantomData; | 2 | use core::marker::PhantomData; |
| 3 | use core::slice; | 3 | use core::slice; |
| 4 | use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; | 4 | use core::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering}; |
| 5 | use core::task::Poll; | 5 | use core::task::Poll; |
| 6 | 6 | ||
| 7 | use embassy_embedded_hal::SetConfig; | 7 | use embassy_embedded_hal::SetConfig; |
| 8 | use embassy_hal_internal::atomic_ring_buffer::RingBuffer; | ||
| 9 | use embassy_hal_internal::Peri; | 8 | use embassy_hal_internal::Peri; |
| 9 | use embassy_hal_internal::atomic_ring_buffer::RingBuffer; | ||
| 10 | use embassy_sync::waitqueue::AtomicWaker; | 10 | use embassy_sync::waitqueue::AtomicWaker; |
| 11 | 11 | ||
| 12 | #[cfg(not(any(usart_v1, usart_v2)))] | 12 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 13 | use super::DePin; | 13 | use super::DePin; |
| 14 | use super::{ | 14 | use super::{ |
| 15 | Config, ConfigError, CtsPin, Duplex, Error, HalfDuplexReadback, Info, Instance, Regs, RtsPin, RxPin, TxPin, | ||
| 15 | clear_interrupt_flags, configure, half_duplex_set_rx_tx_before_write, rdr, reconfigure, send_break, set_baudrate, | 16 | clear_interrupt_flags, configure, half_duplex_set_rx_tx_before_write, rdr, reconfigure, send_break, set_baudrate, |
| 16 | sr, tdr, Config, ConfigError, CtsPin, Duplex, Error, HalfDuplexReadback, Info, Instance, Regs, RtsPin, RxPin, | 17 | sr, tdr, |
| 17 | TxPin, | ||
| 18 | }; | 18 | }; |
| 19 | use crate::gpio::{AfType, AnyPin, Pull, SealedPin as _}; | 19 | use crate::gpio::{AfType, AnyPin, Pull, SealedPin as _}; |
| 20 | use crate::interrupt::{self, InterruptExt}; | 20 | use crate::interrupt::{self, InterruptExt}; |
| @@ -68,8 +68,15 @@ unsafe fn on_interrupt(r: Regs, state: &'static State) { | |||
| 68 | // FIXME: Should we disable any further RX interrupts when the buffer becomes full. | 68 | // FIXME: Should we disable any further RX interrupts when the buffer becomes full. |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | if !state.rx_buf.is_empty() { | 71 | let eager = state.eager_reads.load(Ordering::Relaxed); |
| 72 | state.rx_waker.wake(); | 72 | if eager > 0 { |
| 73 | if state.rx_buf.available() >= eager { | ||
| 74 | state.rx_waker.wake(); | ||
| 75 | } | ||
| 76 | } else { | ||
| 77 | if state.rx_buf.is_half_full() { | ||
| 78 | state.rx_waker.wake(); | ||
| 79 | } | ||
| 73 | } | 80 | } |
| 74 | } | 81 | } |
| 75 | 82 | ||
| @@ -132,6 +139,7 @@ pub(super) struct State { | |||
| 132 | tx_done: AtomicBool, | 139 | tx_done: AtomicBool, |
| 133 | tx_rx_refcount: AtomicU8, | 140 | tx_rx_refcount: AtomicU8, |
| 134 | half_duplex_readback: AtomicBool, | 141 | half_duplex_readback: AtomicBool, |
| 142 | eager_reads: AtomicUsize, | ||
| 135 | } | 143 | } |
| 136 | 144 | ||
| 137 | impl State { | 145 | impl State { |
| @@ -144,6 +152,7 @@ impl State { | |||
| 144 | tx_done: AtomicBool::new(true), | 152 | tx_done: AtomicBool::new(true), |
| 145 | tx_rx_refcount: AtomicU8::new(0), | 153 | tx_rx_refcount: AtomicU8::new(0), |
| 146 | half_duplex_readback: AtomicBool::new(false), | 154 | half_duplex_readback: AtomicBool::new(false), |
| 155 | eager_reads: AtomicUsize::new(0), | ||
| 147 | } | 156 | } |
| 148 | } | 157 | } |
| 149 | } | 158 | } |
| @@ -419,6 +428,9 @@ impl<'d> BufferedUart<'d> { | |||
| 419 | let state = T::buffered_state(); | 428 | let state = T::buffered_state(); |
| 420 | let kernel_clock = T::frequency(); | 429 | let kernel_clock = T::frequency(); |
| 421 | 430 | ||
| 431 | state | ||
| 432 | .eager_reads | ||
| 433 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 422 | state.half_duplex_readback.store( | 434 | state.half_duplex_readback.store( |
| 423 | config.duplex == Duplex::Half(HalfDuplexReadback::Readback), | 435 | config.duplex == Duplex::Half(HalfDuplexReadback::Readback), |
| 424 | Ordering::Relaxed, | 436 | Ordering::Relaxed, |
| @@ -456,6 +468,9 @@ impl<'d> BufferedUart<'d> { | |||
| 456 | let info = self.rx.info; | 468 | let info = self.rx.info; |
| 457 | let state = self.rx.state; | 469 | let state = self.rx.state; |
| 458 | state.tx_rx_refcount.store(2, Ordering::Relaxed); | 470 | state.tx_rx_refcount.store(2, Ordering::Relaxed); |
| 471 | state | ||
| 472 | .eager_reads | ||
| 473 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 459 | 474 | ||
| 460 | info.rcc.enable_and_reset(); | 475 | info.rcc.enable_and_reset(); |
| 461 | 476 | ||
| @@ -527,6 +542,11 @@ impl<'d> BufferedUart<'d> { | |||
| 527 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | 542 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { |
| 528 | reconfigure(self.rx.info, self.rx.kernel_clock, config)?; | 543 | reconfigure(self.rx.info, self.rx.kernel_clock, config)?; |
| 529 | 544 | ||
| 545 | self.rx | ||
| 546 | .state | ||
| 547 | .eager_reads | ||
| 548 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 549 | |||
| 530 | self.rx.info.regs.cr1().modify(|w| { | 550 | self.rx.info.regs.cr1().modify(|w| { |
| 531 | w.set_rxneie(true); | 551 | w.set_rxneie(true); |
| 532 | w.set_idleie(true); | 552 | w.set_idleie(true); |
| @@ -553,24 +573,30 @@ impl<'d> BufferedUartRx<'d> { | |||
| 553 | poll_fn(move |cx| { | 573 | poll_fn(move |cx| { |
| 554 | let state = self.state; | 574 | let state = self.state; |
| 555 | let mut rx_reader = unsafe { state.rx_buf.reader() }; | 575 | let mut rx_reader = unsafe { state.rx_buf.reader() }; |
| 556 | let data = rx_reader.pop_slice(); | 576 | let mut buf_len = 0; |
| 577 | let mut data = rx_reader.pop_slice(); | ||
| 557 | 578 | ||
| 558 | if !data.is_empty() { | 579 | while !data.is_empty() && buf_len < buf.len() { |
| 559 | let len = data.len().min(buf.len()); | 580 | let data_len = data.len().min(buf.len() - buf_len); |
| 560 | buf[..len].copy_from_slice(&data[..len]); | 581 | buf[buf_len..buf_len + data_len].copy_from_slice(&data[..data_len]); |
| 582 | buf_len += data_len; | ||
| 561 | 583 | ||
| 562 | let do_pend = state.rx_buf.is_full(); | 584 | let do_pend = state.rx_buf.is_full(); |
| 563 | rx_reader.pop_done(len); | 585 | rx_reader.pop_done(data_len); |
| 564 | 586 | ||
| 565 | if do_pend { | 587 | if do_pend { |
| 566 | self.info.interrupt.pend(); | 588 | self.info.interrupt.pend(); |
| 567 | } | 589 | } |
| 568 | 590 | ||
| 569 | return Poll::Ready(Ok(len)); | 591 | data = rx_reader.pop_slice(); |
| 570 | } | 592 | } |
| 571 | 593 | ||
| 572 | state.rx_waker.register(cx.waker()); | 594 | if buf_len != 0 { |
| 573 | Poll::Pending | 595 | Poll::Ready(Ok(buf_len)) |
| 596 | } else { | ||
| 597 | state.rx_waker.register(cx.waker()); | ||
| 598 | Poll::Pending | ||
| 599 | } | ||
| 574 | }) | 600 | }) |
| 575 | .await | 601 | .await |
| 576 | } | 602 | } |
| @@ -579,21 +605,24 @@ impl<'d> BufferedUartRx<'d> { | |||
| 579 | loop { | 605 | loop { |
| 580 | let state = self.state; | 606 | let state = self.state; |
| 581 | let mut rx_reader = unsafe { state.rx_buf.reader() }; | 607 | let mut rx_reader = unsafe { state.rx_buf.reader() }; |
| 582 | let data = rx_reader.pop_slice(); | 608 | let mut buf_len = 0; |
| 609 | let mut data = rx_reader.pop_slice(); | ||
| 583 | 610 | ||
| 584 | if !data.is_empty() { | 611 | while !data.is_empty() && buf_len < buf.len() { |
| 585 | let len = data.len().min(buf.len()); | 612 | let data_len = data.len().min(buf.len() - buf_len); |
| 586 | buf[..len].copy_from_slice(&data[..len]); | 613 | buf[buf_len..buf_len + data_len].copy_from_slice(&data[..data_len]); |
| 614 | buf_len += data_len; | ||
| 587 | 615 | ||
| 588 | let do_pend = state.rx_buf.is_full(); | 616 | let do_pend = state.rx_buf.is_full(); |
| 589 | rx_reader.pop_done(len); | 617 | rx_reader.pop_done(data_len); |
| 590 | 618 | ||
| 591 | if do_pend { | 619 | if do_pend { |
| 592 | self.info.interrupt.pend(); | 620 | self.info.interrupt.pend(); |
| 593 | } | 621 | } |
| 594 | 622 | ||
| 595 | return Ok(len); | 623 | data = rx_reader.pop_slice(); |
| 596 | } | 624 | } |
| 625 | return Ok(buf_len); | ||
| 597 | } | 626 | } |
| 598 | } | 627 | } |
| 599 | 628 | ||
| @@ -633,6 +662,10 @@ impl<'d> BufferedUartRx<'d> { | |||
| 633 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | 662 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { |
| 634 | reconfigure(self.info, self.kernel_clock, config)?; | 663 | reconfigure(self.info, self.kernel_clock, config)?; |
| 635 | 664 | ||
| 665 | self.state | ||
| 666 | .eager_reads | ||
| 667 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 668 | |||
| 636 | self.info.regs.cr1().modify(|w| { | 669 | self.info.regs.cr1().modify(|w| { |
| 637 | w.set_rxneie(true); | 670 | w.set_rxneie(true); |
| 638 | w.set_idleie(true); | 671 | w.set_idleie(true); |
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index ff211e0c9..0e7da634d 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs | |||
| @@ -4,15 +4,16 @@ | |||
| 4 | 4 | ||
| 5 | use core::future::poll_fn; | 5 | use core::future::poll_fn; |
| 6 | use core::marker::PhantomData; | 6 | use core::marker::PhantomData; |
| 7 | use core::sync::atomic::{compiler_fence, AtomicU8, Ordering}; | 7 | use core::sync::atomic::{AtomicU8, AtomicUsize, Ordering, compiler_fence}; |
| 8 | use core::task::Poll; | 8 | use core::task::Poll; |
| 9 | 9 | ||
| 10 | use embassy_embedded_hal::SetConfig; | 10 | use embassy_embedded_hal::SetConfig; |
| 11 | use embassy_hal_internal::drop::OnDrop; | ||
| 12 | use embassy_hal_internal::PeripheralType; | 11 | use embassy_hal_internal::PeripheralType; |
| 12 | use embassy_hal_internal::drop::OnDrop; | ||
| 13 | use embassy_sync::waitqueue::AtomicWaker; | 13 | use embassy_sync::waitqueue::AtomicWaker; |
| 14 | use futures_util::future::{select, Either}; | 14 | use futures_util::future::{Either, select}; |
| 15 | 15 | ||
| 16 | use crate::Peri; | ||
| 16 | use crate::dma::ChannelAndRequest; | 17 | use crate::dma::ChannelAndRequest; |
| 17 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | 18 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
| 18 | use crate::interrupt::typelevel::Interrupt as _; | 19 | use crate::interrupt::typelevel::Interrupt as _; |
| @@ -25,7 +26,6 @@ use crate::pac::usart::Usart as Regs; | |||
| 25 | use crate::pac::usart::{regs, vals}; | 26 | use crate::pac::usart::{regs, vals}; |
| 26 | use crate::rcc::{RccInfo, SealedRccPeripheral}; | 27 | use crate::rcc::{RccInfo, SealedRccPeripheral}; |
| 27 | use crate::time::Hertz; | 28 | use crate::time::Hertz; |
| 28 | use crate::Peri; | ||
| 29 | 29 | ||
| 30 | /// Interrupt handler. | 30 | /// Interrupt handler. |
| 31 | pub struct InterruptHandler<T: Instance> { | 31 | pub struct InterruptHandler<T: Instance> { |
| @@ -185,6 +185,12 @@ pub enum ConfigError { | |||
| 185 | RxOrTxNotEnabled, | 185 | RxOrTxNotEnabled, |
| 186 | /// Data bits and parity combination not supported | 186 | /// Data bits and parity combination not supported |
| 187 | DataParityNotSupported, | 187 | DataParityNotSupported, |
| 188 | /// DE assertion time too high | ||
| 189 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 190 | DeAssertionTimeTooHigh, | ||
| 191 | /// DE deassertion time too high | ||
| 192 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 193 | DeDeassertionTimeTooHigh, | ||
| 188 | } | 194 | } |
| 189 | 195 | ||
| 190 | #[non_exhaustive] | 196 | #[non_exhaustive] |
| @@ -206,6 +212,21 @@ pub struct Config { | |||
| 206 | /// If false: the error is ignored and cleared | 212 | /// If false: the error is ignored and cleared |
| 207 | pub detect_previous_overrun: bool, | 213 | pub detect_previous_overrun: bool, |
| 208 | 214 | ||
| 215 | /// If `None` (the default) then read-like calls on `BufferedUartRx` and `RingBufferedUartRx` | ||
| 216 | /// typically only wake/return after line idle or after the buffer is at least half full | ||
| 217 | /// (for `BufferedUartRx`) or the DMA buffer is written at the half or full positions | ||
| 218 | /// (for `RingBufferedUartRx`), though it may also wake/return earlier in some circumstances. | ||
| 219 | /// | ||
| 220 | /// If `Some(n)` then such reads are also woken/return as soon as at least `n` words are | ||
| 221 | /// available in the buffer, in addition to waking/returning when the conditions described | ||
| 222 | /// above are met. `Some(0)` is treated as `None`. Setting this for `RingBufferedUartRx` | ||
| 223 | /// will trigger an interrupt for every received word to check the buffer level, which may | ||
| 224 | /// impact performance at high data rates. | ||
| 225 | /// | ||
| 226 | /// Has no effect on plain `Uart` or `UartRx` reads, which are specified to either | ||
| 227 | /// return a single word, a full buffer, or after line idle. | ||
| 228 | pub eager_reads: Option<usize>, | ||
| 229 | |||
| 209 | /// Set this to true if the line is considered noise free. | 230 | /// Set this to true if the line is considered noise free. |
| 210 | /// This will increase the receiver’s tolerance to clock deviations, | 231 | /// This will increase the receiver’s tolerance to clock deviations, |
| 211 | /// but will effectively disable noise detection. | 232 | /// but will effectively disable noise detection. |
| @@ -239,6 +260,14 @@ pub struct Config { | |||
| 239 | /// Set the pin configuration for the DE pin. | 260 | /// Set the pin configuration for the DE pin. |
| 240 | pub de_config: OutputConfig, | 261 | pub de_config: OutputConfig, |
| 241 | 262 | ||
| 263 | /// Set DE assertion time before the first start bit, 0-31 16ths of a bit period. | ||
| 264 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 265 | pub de_assertion_time: u8, | ||
| 266 | |||
| 267 | /// Set DE deassertion time after the last stop bit, 0-31 16ths of a bit period. | ||
| 268 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 269 | pub de_deassertion_time: u8, | ||
| 270 | |||
| 242 | // private: set by new_half_duplex, not by the user. | 271 | // private: set by new_half_duplex, not by the user. |
| 243 | duplex: Duplex, | 272 | duplex: Duplex, |
| 244 | } | 273 | } |
| @@ -270,6 +299,7 @@ impl Default for Config { | |||
| 270 | parity: Parity::ParityNone, | 299 | parity: Parity::ParityNone, |
| 271 | // historical behavior | 300 | // historical behavior |
| 272 | detect_previous_overrun: false, | 301 | detect_previous_overrun: false, |
| 302 | eager_reads: None, | ||
| 273 | #[cfg(not(usart_v1))] | 303 | #[cfg(not(usart_v1))] |
| 274 | assume_noise_free: false, | 304 | assume_noise_free: false, |
| 275 | #[cfg(any(usart_v3, usart_v4))] | 305 | #[cfg(any(usart_v3, usart_v4))] |
| @@ -283,6 +313,10 @@ impl Default for Config { | |||
| 283 | tx_config: OutputConfig::PushPull, | 313 | tx_config: OutputConfig::PushPull, |
| 284 | rts_config: OutputConfig::PushPull, | 314 | rts_config: OutputConfig::PushPull, |
| 285 | de_config: OutputConfig::PushPull, | 315 | de_config: OutputConfig::PushPull, |
| 316 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 317 | de_assertion_time: 0, | ||
| 318 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 319 | de_deassertion_time: 0, | ||
| 286 | duplex: Duplex::Full, | 320 | duplex: Duplex::Full, |
| 287 | } | 321 | } |
| 288 | } | 322 | } |
| @@ -966,6 +1000,9 @@ impl<'d, M: Mode> UartRx<'d, M> { | |||
| 966 | let info = self.info; | 1000 | let info = self.info; |
| 967 | let state = self.state; | 1001 | let state = self.state; |
| 968 | state.tx_rx_refcount.store(1, Ordering::Relaxed); | 1002 | state.tx_rx_refcount.store(1, Ordering::Relaxed); |
| 1003 | state | ||
| 1004 | .eager_reads | ||
| 1005 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 969 | 1006 | ||
| 970 | info.rcc.enable_and_reset(); | 1007 | info.rcc.enable_and_reset(); |
| 971 | 1008 | ||
| @@ -982,6 +1019,9 @@ impl<'d, M: Mode> UartRx<'d, M> { | |||
| 982 | 1019 | ||
| 983 | /// Reconfigure the driver | 1020 | /// Reconfigure the driver |
| 984 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | 1021 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { |
| 1022 | self.state | ||
| 1023 | .eager_reads | ||
| 1024 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 985 | reconfigure(self.info, self.kernel_clock, config) | 1025 | reconfigure(self.info, self.kernel_clock, config) |
| 986 | } | 1026 | } |
| 987 | 1027 | ||
| @@ -1462,6 +1502,9 @@ impl<'d, M: Mode> Uart<'d, M> { | |||
| 1462 | let info = self.rx.info; | 1502 | let info = self.rx.info; |
| 1463 | let state = self.rx.state; | 1503 | let state = self.rx.state; |
| 1464 | state.tx_rx_refcount.store(2, Ordering::Relaxed); | 1504 | state.tx_rx_refcount.store(2, Ordering::Relaxed); |
| 1505 | state | ||
| 1506 | .eager_reads | ||
| 1507 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 1465 | 1508 | ||
| 1466 | info.rcc.enable_and_reset(); | 1509 | info.rcc.enable_and_reset(); |
| 1467 | 1510 | ||
| @@ -1690,6 +1733,16 @@ fn configure( | |||
| 1690 | return Err(ConfigError::RxOrTxNotEnabled); | 1733 | return Err(ConfigError::RxOrTxNotEnabled); |
| 1691 | } | 1734 | } |
| 1692 | 1735 | ||
| 1736 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 1737 | let dem = r.cr3().read().dem(); | ||
| 1738 | |||
| 1739 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 1740 | if config.de_assertion_time > 31 { | ||
| 1741 | return Err(ConfigError::DeAssertionTimeTooHigh); | ||
| 1742 | } else if config.de_deassertion_time > 31 { | ||
| 1743 | return Err(ConfigError::DeDeassertionTimeTooHigh); | ||
| 1744 | } | ||
| 1745 | |||
| 1693 | // UART must be disabled during configuration. | 1746 | // UART must be disabled during configuration. |
| 1694 | r.cr1().modify(|w| { | 1747 | r.cr1().modify(|w| { |
| 1695 | w.set_ue(false); | 1748 | w.set_ue(false); |
| @@ -1738,6 +1791,20 @@ fn configure( | |||
| 1738 | w.set_re(enable_rx); | 1791 | w.set_re(enable_rx); |
| 1739 | } | 1792 | } |
| 1740 | 1793 | ||
| 1794 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 1795 | if dem { | ||
| 1796 | w.set_deat(if over8 { | ||
| 1797 | config.de_assertion_time / 2 | ||
| 1798 | } else { | ||
| 1799 | config.de_assertion_time | ||
| 1800 | }); | ||
| 1801 | w.set_dedt(if over8 { | ||
| 1802 | config.de_assertion_time / 2 | ||
| 1803 | } else { | ||
| 1804 | config.de_assertion_time | ||
| 1805 | }); | ||
| 1806 | } | ||
| 1807 | |||
| 1741 | // configure word size and parity, since the parity bit is inserted into the MSB position, | 1808 | // configure word size and parity, since the parity bit is inserted into the MSB position, |
| 1742 | // it increases the effective word size | 1809 | // it increases the effective word size |
| 1743 | match (config.parity, config.data_bits) { | 1810 | match (config.parity, config.data_bits) { |
| @@ -2022,6 +2089,7 @@ struct State { | |||
| 2022 | rx_waker: AtomicWaker, | 2089 | rx_waker: AtomicWaker, |
| 2023 | tx_waker: AtomicWaker, | 2090 | tx_waker: AtomicWaker, |
| 2024 | tx_rx_refcount: AtomicU8, | 2091 | tx_rx_refcount: AtomicU8, |
| 2092 | eager_reads: AtomicUsize, | ||
| 2025 | } | 2093 | } |
| 2026 | 2094 | ||
| 2027 | impl State { | 2095 | impl State { |
| @@ -2030,6 +2098,7 @@ impl State { | |||
| 2030 | rx_waker: AtomicWaker::new(), | 2098 | rx_waker: AtomicWaker::new(), |
| 2031 | tx_waker: AtomicWaker::new(), | 2099 | tx_waker: AtomicWaker::new(), |
| 2032 | tx_rx_refcount: AtomicU8::new(0), | 2100 | tx_rx_refcount: AtomicU8::new(0), |
| 2101 | eager_reads: AtomicUsize::new(0), | ||
| 2033 | } | 2102 | } |
| 2034 | } | 2103 | } |
| 2035 | } | 2104 | } |
diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index 5f4e87834..bac570d27 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs | |||
| @@ -1,19 +1,21 @@ | |||
| 1 | use core::future::poll_fn; | 1 | use core::future::poll_fn; |
| 2 | use core::mem; | 2 | use core::mem; |
| 3 | use core::sync::atomic::{compiler_fence, Ordering}; | 3 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 4 | use core::task::Poll; | 4 | use core::task::Poll; |
| 5 | 5 | ||
| 6 | use embassy_embedded_hal::SetConfig; | 6 | use embassy_embedded_hal::SetConfig; |
| 7 | use embedded_io_async::ReadReady; | 7 | use embedded_io_async::ReadReady; |
| 8 | use futures_util::future::{select, Either}; | 8 | use futures_util::future::{Either, select}; |
| 9 | 9 | ||
| 10 | use super::{rdr, reconfigure, set_baudrate, sr, Config, ConfigError, Error, Info, State, UartRx}; | 10 | use super::{ |
| 11 | Config, ConfigError, Error, Info, State, UartRx, clear_interrupt_flags, rdr, reconfigure, set_baudrate, sr, | ||
| 12 | }; | ||
| 13 | use crate::Peri; | ||
| 11 | use crate::dma::ReadableRingBuffer; | 14 | use crate::dma::ReadableRingBuffer; |
| 12 | use crate::gpio::{AnyPin, SealedPin as _}; | 15 | use crate::gpio::{AnyPin, SealedPin as _}; |
| 13 | use crate::mode::Async; | 16 | use crate::mode::Async; |
| 14 | use crate::time::Hertz; | 17 | use crate::time::Hertz; |
| 15 | use crate::usart::Regs; | 18 | use crate::usart::Regs; |
| 16 | use crate::Peri; | ||
| 17 | 19 | ||
| 18 | /// Rx-only Ring-buffered UART Driver | 20 | /// Rx-only Ring-buffered UART Driver |
| 19 | /// | 21 | /// |
| @@ -26,9 +28,9 @@ use crate::Peri; | |||
| 26 | /// contain enough bytes to fill the buffer passed by the caller of | 28 | /// contain enough bytes to fill the buffer passed by the caller of |
| 27 | /// the function, or is empty. | 29 | /// the function, or is empty. |
| 28 | /// | 30 | /// |
| 29 | /// Waiting for bytes operates in one of two modes, depending on | 31 | /// Waiting for bytes operates in one of three modes, depending on |
| 30 | /// the behavior of the sender and the size of the buffer passed | 32 | /// the behavior of the sender, the size of the buffer passed |
| 31 | /// to the function: | 33 | /// to the function, and the configuration: |
| 32 | /// | 34 | /// |
| 33 | /// - If the sender sends intermittently, the 'idle line' | 35 | /// - If the sender sends intermittently, the 'idle line' |
| 34 | /// condition will be detected when the sender stops, and any | 36 | /// condition will be detected when the sender stops, and any |
| @@ -47,7 +49,11 @@ use crate::Peri; | |||
| 47 | /// interrupt when those specific buffer addresses have been | 49 | /// interrupt when those specific buffer addresses have been |
| 48 | /// written. | 50 | /// written. |
| 49 | /// | 51 | /// |
| 50 | /// In both cases this will result in variable latency due to the | 52 | /// - If `eager_reads` is enabled in `config`, the UART interrupt |
| 53 | /// is enabled on all data reception and the call will only wait | ||
| 54 | /// for at least one byte to be available before returning. | ||
| 55 | /// | ||
| 56 | /// In the first two cases this will result in variable latency due to the | ||
| 51 | /// buffering effect. For example, if the baudrate is 2400 bps, and | 57 | /// buffering effect. For example, if the baudrate is 2400 bps, and |
| 52 | /// the configuration is 8 data bits, no parity bit, and one stop bit, | 58 | /// the configuration is 8 data bits, no parity bit, and one stop bit, |
| 53 | /// then a byte will be received every ~4.16ms. If the ring buffer is | 59 | /// then a byte will be received every ~4.16ms. If the ring buffer is |
| @@ -68,15 +74,10 @@ use crate::Peri; | |||
| 68 | /// sending, but would be falsely triggered in the worst-case | 74 | /// sending, but would be falsely triggered in the worst-case |
| 69 | /// buffer delay scenario. | 75 | /// buffer delay scenario. |
| 70 | /// | 76 | /// |
| 71 | /// Note: This latency is caused by the limited capabilities of the | 77 | /// Note: Enabling `eager_reads` with `RingBufferedUartRx` will enable |
| 72 | /// STM32 DMA controller; since it cannot generate an interrupt when | 78 | /// an UART RXNE interrupt, which will cause an interrupt to occur on |
| 73 | /// it stores a byte into an empty ring buffer, or in any other | 79 | /// every received data byte. The data is still copied using DMA, but |
| 74 | /// configurable conditions, it is not possible to take notice of the | 80 | /// there is nevertheless additional processing overhead for each byte. |
| 75 | /// contents of the ring buffer more quickly without introducing | ||
| 76 | /// polling. As a result the latency can be reduced by calling the | ||
| 77 | /// read functions repeatedly with smaller buffers to receive the | ||
| 78 | /// available bytes, as each call to a read function will explicitly | ||
| 79 | /// check the ring buffer for available bytes. | ||
| 80 | pub struct RingBufferedUartRx<'d> { | 81 | pub struct RingBufferedUartRx<'d> { |
| 81 | info: &'static Info, | 82 | info: &'static Info, |
| 82 | state: &'static State, | 83 | state: &'static State, |
| @@ -133,6 +134,9 @@ impl<'d> UartRx<'d, Async> { | |||
| 133 | impl<'d> RingBufferedUartRx<'d> { | 134 | impl<'d> RingBufferedUartRx<'d> { |
| 134 | /// Reconfigure the driver | 135 | /// Reconfigure the driver |
| 135 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | 136 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { |
| 137 | self.state | ||
| 138 | .eager_reads | ||
| 139 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 136 | reconfigure(self.info, self.kernel_clock, config) | 140 | reconfigure(self.info, self.kernel_clock, config) |
| 137 | } | 141 | } |
| 138 | 142 | ||
| @@ -148,8 +152,8 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 148 | let r = self.info.regs; | 152 | let r = self.info.regs; |
| 149 | // clear all interrupts and DMA Rx Request | 153 | // clear all interrupts and DMA Rx Request |
| 150 | r.cr1().modify(|w| { | 154 | r.cr1().modify(|w| { |
| 151 | // disable RXNE interrupt | 155 | // use RXNE only when returning reads early |
| 152 | w.set_rxneie(false); | 156 | w.set_rxneie(self.state.eager_reads.load(Ordering::Relaxed) > 0); |
| 153 | // enable parity interrupt if not ParityNone | 157 | // enable parity interrupt if not ParityNone |
| 154 | w.set_peie(w.pce()); | 158 | w.set_peie(w.pce()); |
| 155 | // enable idle line interrupt | 159 | // enable idle line interrupt |
| @@ -248,39 +252,67 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 248 | async fn wait_for_data_or_idle(&mut self) -> Result<(), Error> { | 252 | async fn wait_for_data_or_idle(&mut self) -> Result<(), Error> { |
| 249 | compiler_fence(Ordering::SeqCst); | 253 | compiler_fence(Ordering::SeqCst); |
| 250 | 254 | ||
| 251 | // Future which completes when idle line is detected | 255 | loop { |
| 252 | let s = self.state; | 256 | // Future which completes when idle line is detected |
| 253 | let uart = poll_fn(|cx| { | 257 | let s = self.state; |
| 254 | s.rx_waker.register(cx.waker()); | 258 | let mut uart_init = false; |
| 255 | 259 | let uart = poll_fn(|cx| { | |
| 256 | compiler_fence(Ordering::SeqCst); | 260 | s.rx_waker.register(cx.waker()); |
| 257 | 261 | ||
| 258 | if check_idle_and_errors(self.info.regs)? { | 262 | compiler_fence(Ordering::SeqCst); |
| 259 | // Idle line is detected | 263 | |
| 260 | Poll::Ready(Ok(())) | 264 | // We may have been woken by IDLE or, if eager_reads is set, by RXNE. |
| 261 | } else { | 265 | // However, DMA will clear RXNE, so we can't check directly, and because |
| 262 | Poll::Pending | 266 | // the other future borrows `ring_buf`, we can't check `len()` here either. |
| 263 | } | 267 | // Instead, return from this future and we'll check the length afterwards. |
| 264 | }); | 268 | let eager = s.eager_reads.load(Ordering::Relaxed) > 0; |
| 269 | |||
| 270 | let idle = check_idle_and_errors(self.info.regs)?; | ||
| 271 | if idle || (eager && uart_init) { | ||
| 272 | // Idle line is detected, or eager reads is set and some data is available. | ||
| 273 | Poll::Ready(Ok(idle)) | ||
| 274 | } else { | ||
| 275 | uart_init = true; | ||
| 276 | Poll::Pending | ||
| 277 | } | ||
| 278 | }); | ||
| 265 | 279 | ||
| 266 | let mut dma_init = false; | 280 | let mut dma_init = false; |
| 267 | // Future which completes when the DMA controller indicates it | 281 | // Future which completes when the DMA controller indicates it |
| 268 | // has written to the ring buffer's middle byte, or last byte | 282 | // has written to the ring buffer's middle byte, or last byte |
| 269 | let dma = poll_fn(|cx| { | 283 | let dma = poll_fn(|cx| { |
| 270 | self.ring_buf.set_waker(cx.waker()); | 284 | self.ring_buf.set_waker(cx.waker()); |
| 271 | 285 | ||
| 272 | let status = match dma_init { | 286 | let status = match dma_init { |
| 273 | false => Poll::Pending, | 287 | false => Poll::Pending, |
| 274 | true => Poll::Ready(()), | 288 | true => Poll::Ready(()), |
| 275 | }; | 289 | }; |
| 276 | 290 | ||
| 277 | dma_init = true; | 291 | dma_init = true; |
| 278 | status | 292 | status |
| 279 | }); | 293 | }); |
| 280 | 294 | ||
| 281 | match select(uart, dma).await { | 295 | match select(uart, dma).await { |
| 282 | Either::Left((result, _)) => result, | 296 | // UART woke with line idle |
| 283 | Either::Right(((), _)) => Ok(()), | 297 | Either::Left((Ok(true), _)) => { |
| 298 | return Ok(()); | ||
| 299 | } | ||
| 300 | // UART woke without idle or error: word received | ||
| 301 | Either::Left((Ok(false), _)) => { | ||
| 302 | let eager = self.state.eager_reads.load(Ordering::Relaxed); | ||
| 303 | if eager > 0 && self.ring_buf.len().unwrap_or(0) >= eager { | ||
| 304 | return Ok(()); | ||
| 305 | } else { | ||
| 306 | continue; | ||
| 307 | } | ||
| 308 | } | ||
| 309 | // UART woke with error | ||
| 310 | Either::Left((Err(e), _)) => { | ||
| 311 | return Err(e); | ||
| 312 | } | ||
| 313 | // DMA woke | ||
| 314 | Either::Right(((), _)) => return Ok(()), | ||
| 315 | } | ||
| 284 | } | 316 | } |
| 285 | } | 317 | } |
| 286 | 318 | ||
| @@ -308,26 +340,16 @@ impl Drop for RingBufferedUartRx<'_> { | |||
| 308 | /// For usart_v1 and usart_v2, all status flags must be handled together anyway because all flags | 340 | /// For usart_v1 and usart_v2, all status flags must be handled together anyway because all flags |
| 309 | /// are cleared by a single read to the RDR register. | 341 | /// are cleared by a single read to the RDR register. |
| 310 | fn check_idle_and_errors(r: Regs) -> Result<bool, Error> { | 342 | fn check_idle_and_errors(r: Regs) -> Result<bool, Error> { |
| 311 | // Critical section is required so that the flags aren't set after read and before clear | 343 | // SAFETY: read only and we only use Rx related flags |
| 312 | let sr = critical_section::with(|_| { | 344 | let sr = sr(r).read(); |
| 313 | // SAFETY: read only and we only use Rx related flags | 345 | |
| 314 | let sr = sr(r).read(); | 346 | #[cfg(not(any(usart_v3, usart_v4)))] |
| 315 | 347 | unsafe { | |
| 316 | #[cfg(any(usart_v3, usart_v4))] | 348 | // This read also clears the error and idle interrupt flags on v1 (TODO and v2?) |
| 317 | r.icr().write(|w| { | 349 | rdr(r).read_volatile() |
| 318 | w.set_idle(true); | 350 | }; |
| 319 | w.set_pe(true); | 351 | clear_interrupt_flags(r, sr); |
| 320 | w.set_fe(true); | 352 | |
| 321 | w.set_ne(true); | ||
| 322 | w.set_ore(true); | ||
| 323 | }); | ||
| 324 | #[cfg(not(any(usart_v3, usart_v4)))] | ||
| 325 | unsafe { | ||
| 326 | // This read also clears the error and idle interrupt flags on v1 (TODO and v2?) | ||
| 327 | rdr(r).read_volatile() | ||
| 328 | }; | ||
| 329 | sr | ||
| 330 | }); | ||
| 331 | if sr.pe() { | 353 | if sr.pe() { |
| 332 | Err(Error::Parity) | 354 | Err(Error::Parity) |
| 333 | } else if sr.fe() { | 355 | } else if sr.fe() { |
diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index 5ce81b131..f6b1a81db 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs | |||
| @@ -2,18 +2,18 @@ use core::marker::PhantomData; | |||
| 2 | 2 | ||
| 3 | use embassy_hal_internal::PeripheralType; | 3 | use embassy_hal_internal::PeripheralType; |
| 4 | use embassy_usb_driver::{EndpointAddress, EndpointAllocError, EndpointType, Event, Unsupported}; | 4 | use embassy_usb_driver::{EndpointAddress, EndpointAllocError, EndpointType, Event, Unsupported}; |
| 5 | use embassy_usb_synopsys_otg::otg_v1::vals::Dspd; | ||
| 6 | use embassy_usb_synopsys_otg::otg_v1::Otg; | ||
| 7 | pub use embassy_usb_synopsys_otg::Config; | 5 | pub use embassy_usb_synopsys_otg::Config; |
| 6 | use embassy_usb_synopsys_otg::otg_v1::Otg; | ||
| 7 | use embassy_usb_synopsys_otg::otg_v1::vals::Dspd; | ||
| 8 | use embassy_usb_synopsys_otg::{ | 8 | use embassy_usb_synopsys_otg::{ |
| 9 | on_interrupt as on_interrupt_impl, Bus as OtgBus, ControlPipe, Driver as OtgDriver, Endpoint, In, OtgInstance, Out, | 9 | Bus as OtgBus, ControlPipe, Driver as OtgDriver, Endpoint, In, OtgInstance, Out, PhyType, State, |
| 10 | PhyType, State, | 10 | on_interrupt as on_interrupt_impl, |
| 11 | }; | 11 | }; |
| 12 | 12 | ||
| 13 | use crate::gpio::{AfType, OutputType, Speed}; | 13 | use crate::gpio::{AfType, OutputType, Speed}; |
| 14 | use crate::interrupt::typelevel::Interrupt; | 14 | use crate::interrupt::typelevel::Interrupt; |
| 15 | use crate::rcc::{self, RccPeripheral}; | 15 | use crate::rcc::{self, RccPeripheral}; |
| 16 | use crate::{interrupt, Peri}; | 16 | use crate::{Peri, interrupt}; |
| 17 | 17 | ||
| 18 | const MAX_EP_COUNT: usize = 9; | 18 | const MAX_EP_COUNT: usize = 9; |
| 19 | 19 | ||
diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 9e08d99b3..d405e4802 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs | |||
| @@ -12,11 +12,11 @@ use embassy_usb_driver::{ | |||
| 12 | Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, | 12 | Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, |
| 13 | }; | 13 | }; |
| 14 | 14 | ||
| 15 | use crate::pac::USBRAM; | ||
| 15 | use crate::pac::usb::regs; | 16 | use crate::pac::usb::regs; |
| 16 | use crate::pac::usb::vals::{EpType, Stat}; | 17 | use crate::pac::usb::vals::{EpType, Stat}; |
| 17 | use crate::pac::USBRAM; | ||
| 18 | use crate::rcc::RccPeripheral; | 18 | use crate::rcc::RccPeripheral; |
| 19 | use crate::{interrupt, Peri}; | 19 | use crate::{Peri, interrupt}; |
| 20 | 20 | ||
| 21 | /// Interrupt handler. | 21 | /// Interrupt handler. |
| 22 | pub struct InterruptHandler<T: Instance> { | 22 | pub struct InterruptHandler<T: Instance> { |
diff --git a/embassy-stm32/src/vrefbuf/mod.rs b/embassy-stm32/src/vrefbuf/mod.rs index ccbd748d5..43dd9c800 100644 --- a/embassy-stm32/src/vrefbuf/mod.rs +++ b/embassy-stm32/src/vrefbuf/mod.rs | |||
| @@ -14,10 +14,10 @@ pub struct VoltageReferenceBuffer<'d, T: Instance> { | |||
| 14 | #[cfg(rcc_wba)] | 14 | #[cfg(rcc_wba)] |
| 15 | fn get_refbuf_trim(voltage_scale: Vrs) -> usize { | 15 | fn get_refbuf_trim(voltage_scale: Vrs) -> usize { |
| 16 | match voltage_scale { | 16 | match voltage_scale { |
| 17 | Vrs::VREF0 => 0x0BFA_07A8usize, | 17 | Vrs::VREF0 => 0x0BFA_07ABusize, |
| 18 | Vrs::VREF1 => 0x0BFA_07A9usize, | 18 | Vrs::VREF1 => 0x0BFA_07AAusize, |
| 19 | Vrs::VREF2 => 0x0BFA_07AAusize, | 19 | Vrs::VREF2 => 0x0BFA_07A9usize, |
| 20 | Vrs::VREF3 => 0x0BFA_07ABusize, | 20 | Vrs::VREF3 => 0x0BFA_07A8usize, |
| 21 | _ => panic!("Incorrect Vrs setting!"), | 21 | _ => panic!("Incorrect Vrs setting!"), |
| 22 | } | 22 | } |
| 23 | } | 23 | } |
| @@ -62,8 +62,7 @@ impl<'d, T: Instance> VoltageReferenceBuffer<'d, T> { | |||
| 62 | } | 62 | } |
| 63 | trace!( | 63 | trace!( |
| 64 | "Vrefbuf configured with voltage scale {} and impedance mode {}", | 64 | "Vrefbuf configured with voltage scale {} and impedance mode {}", |
| 65 | voltage_scale as u8, | 65 | voltage_scale as u8, impedance_mode as u8, |
| 66 | impedance_mode as u8, | ||
| 67 | ); | 66 | ); |
| 68 | VoltageReferenceBuffer { vrefbuf: PhantomData } | 67 | VoltageReferenceBuffer { vrefbuf: PhantomData } |
| 69 | } | 68 | } |
diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs index fb5c3d930..1164739ff 100644 --- a/embassy-stm32/src/wdg/mod.rs +++ b/embassy-stm32/src/wdg/mod.rs | |||
| @@ -4,8 +4,8 @@ use core::marker::PhantomData; | |||
| 4 | use embassy_hal_internal::PeripheralType; | 4 | use embassy_hal_internal::PeripheralType; |
| 5 | use stm32_metapac::iwdg::vals::{Key, Pr}; | 5 | use stm32_metapac::iwdg::vals::{Key, Pr}; |
| 6 | 6 | ||
| 7 | use crate::rcc::LSI_FREQ; | ||
| 8 | use crate::Peri; | 7 | use crate::Peri; |
| 8 | use crate::rcc::LSI_FREQ; | ||
| 9 | 9 | ||
| 10 | /// Independent watchdog (IWDG) driver. | 10 | /// Independent watchdog (IWDG) driver. |
| 11 | pub struct IndependentWatchdog<'d, T: Instance> { | 11 | pub struct IndependentWatchdog<'d, T: Instance> { |
diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs index 901569f64..a80a2692b 100644 --- a/embassy-stm32/src/xspi/mod.rs +++ b/embassy-stm32/src/xspi/mod.rs | |||
| @@ -11,15 +11,15 @@ use embassy_embedded_hal::{GetConfig, SetConfig}; | |||
| 11 | use embassy_hal_internal::PeripheralType; | 11 | use embassy_hal_internal::PeripheralType; |
| 12 | pub use enums::*; | 12 | pub use enums::*; |
| 13 | 13 | ||
| 14 | use crate::dma::{word, ChannelAndRequest}; | 14 | use crate::dma::{ChannelAndRequest, word}; |
| 15 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | 15 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
| 16 | use crate::mode::{Async, Blocking, Mode as PeriMode}; | 16 | use crate::mode::{Async, Blocking, Mode as PeriMode}; |
| 17 | use crate::pac::xspi::vals::*; | ||
| 18 | use crate::pac::xspi::Xspi as Regs; | 17 | use crate::pac::xspi::Xspi as Regs; |
| 18 | use crate::pac::xspi::vals::*; | ||
| 19 | #[cfg(xspim_v1)] | 19 | #[cfg(xspim_v1)] |
| 20 | use crate::pac::xspim::Xspim; | 20 | use crate::pac::xspim::Xspim; |
| 21 | use crate::rcc::{self, RccPeripheral}; | 21 | use crate::rcc::{self, RccPeripheral}; |
| 22 | use crate::{peripherals, Peri}; | 22 | use crate::{Peri, peripherals}; |
| 23 | 23 | ||
| 24 | /// XPSI driver config. | 24 | /// XPSI driver config. |
| 25 | #[derive(Clone, Copy)] | 25 | #[derive(Clone, Copy)] |
