diff options
| author | Raul Alimbekov <[email protected]> | 2025-12-16 09:05:22 +0300 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-12-16 09:05:22 +0300 |
| commit | c9a04b4b732b7a3b696eb8223664c1a7942b1875 (patch) | |
| tree | 6dbe5c02e66eed8d8762f13f95afd24f8db2b38c /embassy-stm32 | |
| parent | cde24a3ef1117653ba5ed4184102b33f745782fb (diff) | |
| parent | 5ae6e060ec1c90561719aabdc29d5b6e7b8b0a82 (diff) | |
Merge branch 'main' into main
Diffstat (limited to 'embassy-stm32')
131 files changed, 11581 insertions, 5980 deletions
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 74f0ba4e0..67af79080 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md | |||
| @@ -5,9 +5,37 @@ 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 | - Add `receive_waveform` method in `InputCapture`, allowing asynchronous input capture with DMA. | 9 | - Add `receive_waveform` method in `InputCapture`, allowing asynchronous input capture with DMA. |
| 10 | |||
| 11 | - fix: stm32: GPDMA driver reset ignored during channel configuration | ||
| 12 | - fix: stm32: SPI driver SSOE and SSM manegment, add `nss_output_disable` to SPI Config | ||
| 13 | - change: stm32: use typelevel timer type to allow dma for 32 bit timers | ||
| 14 | - fix: fix incorrect handling of split interrupts in timer driver | ||
| 15 | - feat: allow granular stop for regular usart | ||
| 16 | - feat: Add continuous waveform method to SimplePWM | ||
| 17 | - change: remove waveform timer method | ||
| 18 | - change: low power: store stop mode for dma channels | ||
| 19 | - fix: Fixed ADC4 enable() for WBA | ||
| 20 | - feat: allow use of anyadcchannel for adc4 | ||
| 21 | - fix: fix incorrect logic for buffered usart transmission complete. | ||
| 22 | - feat: add poll_for methods to exti | ||
| 23 | - feat: implement stop for stm32wb. | ||
| 24 | - change: rework hsem and add HIL test for some chips. | ||
| 25 | - change: stm32/eth: ethernet no longer has a hard dependency on station management, and station management can be used independently ([#4871](https://github.com/embassy-rs/embassy/pull/4871)) | ||
| 26 | - feat: allow embassy_executor::main for low power | ||
| 27 | - feat: Add waveform methods to ComplementaryPwm | ||
| 28 | - fix: Avoid generating timer update events when updating the frequency ([#4890](https://github.com/embassy-rs/embassy/pull/4890)) | ||
| 29 | - chore: cleanup low-power add time | ||
| 30 | - fix: Allow setting SAI peripheral `frame_length` to `256` | ||
| 31 | - fix: flash erase on dual-bank STM32Gxxx | ||
| 32 | - feat: Add support for STM32N657X0 | ||
| 33 | - feat: timer: Add 32-bit timer support to SimplePwm waveform_up method following waveform pattern ([#4717](https://github.com/embassy-rs/embassy/pull/4717)) | ||
| 34 | - feat: Add support for injected ADC measurements for g4 ([#4840](https://github.com/embassy-rs/embassy/pull/4840)) | ||
| 35 | - feat: Implement into_ring_buffered for g4 ([#4840](https://github.com/embassy-rs/embassy/pull/4840)) | ||
| 36 | - feat: Add support for 13-bit address and 16-bit data SDRAM chips | ||
| 37 | - feat: stm32/hrtim add new_chx_with_config to provide pin configuration | ||
| 38 | - fix flash erase on L4 & L5 | ||
| 11 | - fix: Fixed STM32H5 builds requiring time feature | 39 | - fix: Fixed STM32H5 builds requiring time feature |
| 12 | - feat: Derive Clone, Copy for QSPI Config | 40 | - feat: Derive Clone, Copy for QSPI Config |
| 13 | - fix: stm32/i2c in master mode (blocking): subsequent transmissions failed after a NACK was received | 41 | - fix: stm32/i2c in master mode (blocking): subsequent transmissions failed after a NACK was received |
| @@ -20,16 +48,57 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 20 | - feat: Configurable gpio speed for QSPI | 48 | - feat: Configurable gpio speed for QSPI |
| 21 | - feat: derive Clone, Copy and defmt::Format for all *SPI-related configs | 49 | - feat: derive Clone, Copy and defmt::Format for all *SPI-related configs |
| 22 | - fix: handle address and data-length errors in OSPI | 50 | - fix: handle address and data-length errors in OSPI |
| 23 | - feat: Allow OSPI DMA writes larger than 64kB using chunking | 51 | - feat: Allow OSPI/HSPI/XSPI DMA writes larger than 64kB using chunking |
| 24 | - feat: More ADC enums for g0 PAC, API change for oversampling, allow separate sample times | 52 | - feat: More ADC enums for g0 PAC, API change for oversampling, allow separate sample times |
| 25 | - feat: Add USB CRS sync support for STM32C071 | 53 | - feat: Add USB CRS sync support for STM32C071 |
| 26 | - fix: RTC register definition for STM32L4P5 and L4Q5 as they use v3 register map. | 54 | - fix: RTC register definition for STM32L4P5 and L4Q5 as they use v3 register map. |
| 27 | - fix: Cut down the capabilities of the STM32L412 and L422 RTC as those are missing binary timer mode and underflow interrupt. | 55 | - fix: Cut down the capabilities of the STM32L412 and L422 RTC as those are missing binary timer mode and underflow interrupt. |
| 28 | - fix: Allow configuration of the internal pull up/down resistors on the pins for the Qei peripheral, as well as the Qei decoder mode. | 56 | - fix: Allow configuration of the internal pull up/down resistors on the pins for the Qei peripheral, as well as the Qei decoder mode. |
| 57 | - 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)) | ||
| 58 | - feat: derive Clone, Copy and defmt::Format for all SPI-related configs | ||
| 59 | - 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)) | ||
| 60 | - feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options | ||
| 61 | - change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer | ||
| 62 | - fix: Properly set the transfer size for OSPI/HSPI/XSPI transfers with word sizes other than 8 bits. | ||
| 63 | - fix: stm32/adc: Calculate the ADC prescaler in a way that it allows for the max frequency to be reached | ||
| 64 | - fix: Prevent a HardFault crash on STM32H5 devices by changing `uid()` to return `[u8; 12]` by value instead of a reference. (Fixes #2696) | ||
| 65 | - change: timer: added output compare values | ||
| 66 | - feat: timer: add ability to set master mode | ||
| 67 | - fix: sdmmc: don't wait for DBCKEND flag on sdmmc_v2 devices as it never fires (Fixes #4723) | ||
| 68 | - fix: usart: fix race condition in ringbuffered usart | ||
| 69 | - feat: Add backup_sram::init() for H5 devices to access BKPSRAM | ||
| 70 | - feat: stm32/i2c v1: Add I2C MultiMaster (Slave) support | ||
| 71 | - feat: stm32/i2c v2: Add transaction() and blocking_transaction() methods with contract-compliant operation merging | ||
| 72 | - feat: stm32/fdcan: add ability to control automatic recovery from bus off ([#4821](https://github.com/embassy-rs/embassy/pull/4821)) | ||
| 73 | - low-power: update rtc api to allow reconfig | ||
| 74 | - adc: consolidate ringbuffer | ||
| 75 | - feat: Added RTC low-power support for STM32WLEx ([#4716](https://github.com/embassy-rs/embassy/pull/4716)) | ||
| 76 | - fix: Correct STM32WBA VREFBUFTRIM values | ||
| 77 | - low_power: remove stop_with rtc and initialize in init if low-power feature enabled. | ||
| 78 | - feat: stm32/dsi support zero parameter commands in `write_cmd` ([#4847](https://github.com/embassy-rs/embassy/pull/4847)) | ||
| 79 | - feat: stm32/spi: added support for slave mode ([#4388](https://github.com/embassy-rs/embassy/pull/4388)) | ||
| 80 | - chore: Updated stm32-metapac and stm32-data dependencies | ||
| 81 | - adc: reogranize and cleanup somewhat. require sample_time to be passed on conversion | ||
| 82 | - fix: stm32/i2c v2 slave: prevent misaligned reads, error false positives, and incorrect counts of bytes read/written | ||
| 83 | - feat: add flash support for c0 family ([#4874](https://github.com/embassy-rs/embassy/pull/4874)) | ||
| 84 | - fix: fixing channel numbers on vbat and vddcore for adc on adc | ||
| 85 | - adc: adding disable to vbat | ||
| 86 | - feat: stm32/flash: add async support for h7 family | ||
| 87 | - feat: exti brought in line with other drivers' interrupt rebinding system ([#4922](https://github.com/embassy-rs/embassy/pull/4922)) | ||
| 88 | - removal: ExtiInput no longer accepts AnyPin/AnyChannel; AnyChannel removed entirely | ||
| 89 | - fix: build script ensures EXTI2_TSC is listed as the IRQ of EXTI2 even if the PAC doesn't | ||
| 90 | - feat: stm32/lcd: added implementation | ||
| 91 | - change: add error messages to can timing calculations ([#4961](https://github.com/embassy-rs/embassy/pull/4961)) | ||
| 92 | - feat: stm32/spi bidirectional mode | ||
| 93 | - fix: stm32/i2c v2: add stop flag on stop received | ||
| 94 | - stm32: Add blocking_listen for blocking I2C driver | ||
| 95 | - fix: stm32l47*/stm32l48* adc analog pin setup | ||
| 96 | - fix: keep stm32/sai: make NODIV independent of MCKDIV | ||
| 97 | - fix: Source system clock from MSIS before (de)configuring PLLs on STM32U5 | ||
| 29 | 98 | ||
| 30 | ## 0.4.0 - 2025-08-26 | 99 | ## 0.4.0 - 2025-08-26 |
| 31 | 100 | ||
| 32 | - feat: stm32/sai: make NODIV independent of MCKDIV | 101 | - feat: stm32/sai: make NODIV independent of MCKDIV |
| 33 | - fix: stm32/sai: fix WB MCKDIV | 102 | - fix: stm32/sai: fix WB MCKDIV |
| 34 | - fix: stm32/i2c: pull-down was enabled instead of pull-none when no internal pull-up was needed. | 103 | - fix: stm32/i2c: pull-down was enabled instead of pull-none when no internal pull-up was needed. |
| 35 | - feat: Improve blocking hash speed | 104 | - feat: Improve blocking hash speed |
| @@ -38,6 +107,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 38 | - chore: Updated stm32-metapac and stm32-data dependencies | 107 | - chore: Updated stm32-metapac and stm32-data dependencies |
| 39 | - feat: stm32/adc/v3: allow DMA reads to loop through enable channels | 108 | - feat: stm32/adc/v3: allow DMA reads to loop through enable channels |
| 40 | - fix: Fix XSPI not disabling alternate bytes when they were previously enabled | 109 | - fix: Fix XSPI not disabling alternate bytes when they were previously enabled |
| 110 | - feat: stm32/adc/v3: added support for Continuous DMA configuration | ||
| 41 | - fix: Fix stm32h7rs init when using external flash via XSPI | 111 | - fix: Fix stm32h7rs init when using external flash via XSPI |
| 42 | - feat: Add Adc::new_with_clock() to configure analog clock | 112 | - feat: Add Adc::new_with_clock() to configure analog clock |
| 43 | - feat: Add GPDMA linked-list + ringbuffer support ([#3923](https://github.com/embassy-rs/embassy/pull/3923)) | 113 | - 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 82bc73708..718da9a34 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"] |
| @@ -99,6 +99,7 @@ build = [ | |||
| 99 | {target = "thumbv8m.main-none-eabihf", features = ["defmt", "exti", "low-power", "stm32wba65ri", "time", "time-driver-any"]}, | 99 | {target = "thumbv8m.main-none-eabihf", features = ["defmt", "exti", "low-power", "stm32wba65ri", "time", "time-driver-any"]}, |
| 100 | {target = "thumbv8m.main-none-eabihf", features = ["defmt", "exti", "stm32u5f9zj", "time", "time-driver-any"]}, | 100 | {target = "thumbv8m.main-none-eabihf", features = ["defmt", "exti", "stm32u5f9zj", "time", "time-driver-any"]}, |
| 101 | {target = "thumbv8m.main-none-eabihf", features = ["defmt", "exti", "stm32u5g9nj", "time", "time-driver-any"]}, | 101 | {target = "thumbv8m.main-none-eabihf", features = ["defmt", "exti", "stm32u5g9nj", "time", "time-driver-any"]}, |
| 102 | {target = "thumbv8m.main-none-eabihf", features = ["defmt", "exti", "stm32n657x0", "time", "time-driver-any"]}, | ||
| 102 | {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32wb35ce", "time", "time-driver-any"]}, | 103 | {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32wb35ce", "time", "time-driver-any"]}, |
| 103 | {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "low-power", "stm32wb55rg", "time", "time-driver-any"]}, | 104 | {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "low-power", "stm32wb55rg", "time", "time-driver-any"]}, |
| 104 | {target = "thumbv6m-none-eabi", features = ["defmt", "exti", "stm32u031r8", "time", "time-driver-any"]}, | 105 | {target = "thumbv6m-none-eabi", features = ["defmt", "exti", "stm32u031r8", "time", "time-driver-any"]}, |
| @@ -138,6 +139,7 @@ flavors = [ | |||
| 138 | { regex_feature = "stm32wb.*", target = "thumbv7em-none-eabi" }, | 139 | { regex_feature = "stm32wb.*", target = "thumbv7em-none-eabi" }, |
| 139 | { regex_feature = "stm32wba.*", target = "thumbv8m.main-none-eabihf" }, | 140 | { regex_feature = "stm32wba.*", target = "thumbv8m.main-none-eabihf" }, |
| 140 | { regex_feature = "stm32wl.*", target = "thumbv7em-none-eabi" }, | 141 | { regex_feature = "stm32wl.*", target = "thumbv7em-none-eabi" }, |
| 142 | { regex_feature = "stm32n6.*", target = "thumbv8m.main-none-eabihf" }, | ||
| 141 | ] | 143 | ] |
| 142 | 144 | ||
| 143 | [package.metadata.docs.rs] | 145 | [package.metadata.docs.rs] |
| @@ -177,38 +179,42 @@ cortex-m = "0.7.6" | |||
| 177 | futures-util = { version = "0.3.30", default-features = false } | 179 | futures-util = { version = "0.3.30", default-features = false } |
| 178 | sdio-host = "0.9.0" | 180 | sdio-host = "0.9.0" |
| 179 | critical-section = "1.1" | 181 | critical-section = "1.1" |
| 180 | #stm32-metapac = { version = "18" } | ||
| 181 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b9f6b0c542d85ee695d71c35ced195e0cef51ac0" } | ||
| 182 | 182 | ||
| 183 | vcell = "0.1.3" | 183 | vcell = "0.1.3" |
| 184 | nb = "1.0.0" | 184 | nb = "1.0.0" |
| 185 | stm32-fmc = "0.3.0" | 185 | stm32-fmc = "0.4.0" |
| 186 | cfg-if = "1.0.0" | 186 | cfg-if = "1.0.0" |
| 187 | embedded-io = { version = "0.6.0" } | 187 | embedded-io = { version = "0.6.0" } |
| 188 | embedded-io-async = { version = "0.6.1" } | 188 | embedded-io-async = { version = "0.6.1" } |
| 189 | chrono = { version = "^0.4", default-features = false, optional = true } | 189 | chrono = { version = "^0.4", default-features = false, optional = true } |
| 190 | bit_field = "0.10.2" | 190 | bit_field = "0.10.2" |
| 191 | trait-set = "0.3.0" | ||
| 191 | document-features = "0.2.7" | 192 | document-features = "0.2.7" |
| 192 | 193 | ||
| 193 | static_assertions = { version = "1.1" } | 194 | static_assertions = { version = "1.1" } |
| 194 | volatile-register = { version = "0.2.1" } | 195 | volatile-register = { version = "0.2.1" } |
| 195 | bitflags = "2.4.2" | 196 | bitflags = "2.10.0" |
| 196 | 197 | ||
| 197 | block-device-driver = { version = "0.2" } | 198 | block-device-driver = { version = "0.2" } |
| 198 | aligned = "0.4.1" | 199 | aligned = "0.4.1" |
| 199 | heapless = "0.9.1" | 200 | heapless = "0.9.1" |
| 200 | 201 | ||
| 201 | [dev-dependencies] | 202 | #stm32-metapac = { version = "18" } |
| 202 | critical-section = { version = "1.1", features = ["std"] } | 203 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-658588478e426d68090a59ff8385bce5b407c2bc" } |
| 203 | proptest = "1.5.0" | ||
| 204 | proptest-state-machine = "0.3.0" | ||
| 205 | 204 | ||
| 206 | [build-dependencies] | 205 | [build-dependencies] |
| 206 | #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} | ||
| 207 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-658588478e426d68090a59ff8385bce5b407c2bc", default-features = false, features = ["metadata"] } | ||
| 208 | |||
| 207 | proc-macro2 = "1.0.36" | 209 | proc-macro2 = "1.0.36" |
| 208 | quote = "1.0.15" | 210 | quote = "1.0.15" |
| 209 | 211 | ||
| 210 | #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} | 212 | |
| 211 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b9f6b0c542d85ee695d71c35ced195e0cef51ac0", default-features = false, features = ["metadata"] } | 213 | [dev-dependencies] |
| 214 | critical-section = { version = "1.1", features = ["std"] } | ||
| 215 | proptest = "1.5.0" | ||
| 216 | proptest-state-machine = "0.3.0" | ||
| 217 | |||
| 212 | 218 | ||
| 213 | [features] | 219 | [features] |
| 214 | default = ["rt"] | 220 | default = ["rt"] |
| @@ -313,6 +319,7 @@ single-bank = [] | |||
| 313 | 319 | ||
| 314 | ## internal use only | 320 | ## internal use only |
| 315 | _split-pins-enabled = [] | 321 | _split-pins-enabled = [] |
| 322 | _allow-disable-rtc = [] | ||
| 316 | 323 | ||
| 317 | ## internal use only | 324 | ## internal use only |
| 318 | _dual-core = [] | 325 | _dual-core = [] |
| @@ -1637,6 +1644,30 @@ stm32l562qe = [ "stm32-metapac/stm32l562qe" ] | |||
| 1637 | stm32l562re = [ "stm32-metapac/stm32l562re" ] | 1644 | stm32l562re = [ "stm32-metapac/stm32l562re" ] |
| 1638 | stm32l562ve = [ "stm32-metapac/stm32l562ve" ] | 1645 | stm32l562ve = [ "stm32-metapac/stm32l562ve" ] |
| 1639 | stm32l562ze = [ "stm32-metapac/stm32l562ze" ] | 1646 | stm32l562ze = [ "stm32-metapac/stm32l562ze" ] |
| 1647 | stm32n645a0 = [ "stm32-metapac/stm32n645a0" ] | ||
| 1648 | stm32n645b0 = [ "stm32-metapac/stm32n645b0" ] | ||
| 1649 | stm32n645i0 = [ "stm32-metapac/stm32n645i0" ] | ||
| 1650 | stm32n645l0 = [ "stm32-metapac/stm32n645l0" ] | ||
| 1651 | stm32n645x0 = [ "stm32-metapac/stm32n645x0" ] | ||
| 1652 | stm32n645z0 = [ "stm32-metapac/stm32n645z0" ] | ||
| 1653 | stm32n647a0 = [ "stm32-metapac/stm32n647a0" ] | ||
| 1654 | stm32n647b0 = [ "stm32-metapac/stm32n647b0" ] | ||
| 1655 | stm32n647i0 = [ "stm32-metapac/stm32n647i0" ] | ||
| 1656 | stm32n647l0 = [ "stm32-metapac/stm32n647l0" ] | ||
| 1657 | stm32n647x0 = [ "stm32-metapac/stm32n647x0" ] | ||
| 1658 | stm32n647z0 = [ "stm32-metapac/stm32n647z0" ] | ||
| 1659 | stm32n655a0 = [ "stm32-metapac/stm32n655a0" ] | ||
| 1660 | stm32n655b0 = [ "stm32-metapac/stm32n655b0" ] | ||
| 1661 | stm32n655i0 = [ "stm32-metapac/stm32n655i0" ] | ||
| 1662 | stm32n655l0 = [ "stm32-metapac/stm32n655l0" ] | ||
| 1663 | stm32n655x0 = [ "stm32-metapac/stm32n655x0" ] | ||
| 1664 | stm32n655z0 = [ "stm32-metapac/stm32n655z0" ] | ||
| 1665 | stm32n657a0 = [ "stm32-metapac/stm32n657a0" ] | ||
| 1666 | stm32n657b0 = [ "stm32-metapac/stm32n657b0" ] | ||
| 1667 | stm32n657i0 = [ "stm32-metapac/stm32n657i0" ] | ||
| 1668 | stm32n657l0 = [ "stm32-metapac/stm32n657l0" ] | ||
| 1669 | stm32n657x0 = [ "stm32-metapac/stm32n657x0" ] | ||
| 1670 | stm32n657z0 = [ "stm32-metapac/stm32n657z0" ] | ||
| 1640 | stm32u031c6 = [ "stm32-metapac/stm32u031c6" ] | 1671 | stm32u031c6 = [ "stm32-metapac/stm32u031c6" ] |
| 1641 | stm32u031c8 = [ "stm32-metapac/stm32u031c8" ] | 1672 | stm32u031c8 = [ "stm32-metapac/stm32u031c8" ] |
| 1642 | stm32u031f4 = [ "stm32-metapac/stm32u031f4" ] | 1673 | stm32u031f4 = [ "stm32-metapac/stm32u031f4" ] |
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index b5f1261fe..a3b863340 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, Peripheral, 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,16 @@ 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 | |||
| 136 | // compile a map of peripherals | ||
| 137 | let peripheral_map: BTreeMap<&str, &Peripheral> = METADATA.peripherals.iter().map(|p| (p.name, p)).collect(); | ||
| 138 | |||
| 125 | // generate one singleton per peripheral (with many exceptions...) | 139 | // generate one singleton per peripheral (with many exceptions...) |
| 126 | for p in METADATA.peripherals { | 140 | for p in METADATA.peripherals { |
| 127 | if let Some(r) = &p.registers { | 141 | if let Some(r) = &p.registers { |
| @@ -159,6 +173,11 @@ fn main() { | |||
| 159 | } | 173 | } |
| 160 | singletons.push(p.name.to_string()); | 174 | singletons.push(p.name.to_string()); |
| 161 | } | 175 | } |
| 176 | |||
| 177 | "eth" => { | ||
| 178 | singletons.push(p.name.to_string()); | ||
| 179 | singletons.push("ETH_SMA".to_string()); | ||
| 180 | } | ||
| 162 | //"dbgmcu" => {} | 181 | //"dbgmcu" => {} |
| 163 | //"syscfg" => {} | 182 | //"syscfg" => {} |
| 164 | //"dma" => {} | 183 | //"dma" => {} |
| @@ -303,9 +322,33 @@ fn main() { | |||
| 303 | _ => panic!("unknown time_driver {:?}", time_driver), | 322 | _ => panic!("unknown time_driver {:?}", time_driver), |
| 304 | }; | 323 | }; |
| 305 | 324 | ||
| 306 | if !time_driver_singleton.is_empty() { | 325 | let time_driver_irq_decl = if !time_driver_singleton.is_empty() { |
| 307 | cfgs.enable(format!("time_driver_{}", time_driver_singleton.to_lowercase())); | 326 | cfgs.enable(format!("time_driver_{}", time_driver_singleton.to_lowercase())); |
| 308 | } | 327 | |
| 328 | let p = peripheral_map.get(time_driver_singleton).unwrap(); | ||
| 329 | let irqs: BTreeSet<_> = p | ||
| 330 | .interrupts | ||
| 331 | .iter() | ||
| 332 | .filter(|i| i.signal == "CC" || i.signal == "UP") | ||
| 333 | .map(|i| i.interrupt.to_ascii_uppercase()) | ||
| 334 | .collect(); | ||
| 335 | |||
| 336 | irqs.iter() | ||
| 337 | .map(|i| { | ||
| 338 | let irq = format_ident!("{}", i); | ||
| 339 | quote! { | ||
| 340 | #[cfg(feature = "rt")] | ||
| 341 | #[interrupt] | ||
| 342 | fn #irq() { | ||
| 343 | crate::time_driver::get_driver().on_interrupt(); | ||
| 344 | } | ||
| 345 | } | ||
| 346 | }) | ||
| 347 | .collect() | ||
| 348 | } else { | ||
| 349 | TokenStream::new() | ||
| 350 | }; | ||
| 351 | |||
| 309 | for tim in [ | 352 | for tim in [ |
| 310 | "tim1", "tim2", "tim3", "tim4", "tim5", "tim8", "tim9", "tim12", "tim15", "tim20", "tim21", "tim22", "tim23", | 353 | "tim1", "tim2", "tim3", "tim4", "tim5", "tim8", "tim9", "tim12", "tim15", "tim20", "tim21", "tim22", "tim23", |
| 311 | "tim24", | 354 | "tim24", |
| @@ -337,8 +380,13 @@ fn main() { | |||
| 337 | // ======== | 380 | // ======== |
| 338 | // Generate interrupt declarations | 381 | // Generate interrupt declarations |
| 339 | 382 | ||
| 383 | let mut exti2_tsc_shared_int_present: Option<stm32_metapac::metadata::Interrupt> = None; | ||
| 340 | let mut irqs = Vec::new(); | 384 | let mut irqs = Vec::new(); |
| 341 | for irq in METADATA.interrupts { | 385 | for irq in METADATA.interrupts { |
| 386 | // The PAC doesn't ensure this is listed as the IRQ of EXTI2, so we must do so | ||
| 387 | if irq.name == "EXTI2_TSC" { | ||
| 388 | exti2_tsc_shared_int_present = Some(irq.clone()) | ||
| 389 | } | ||
| 342 | irqs.push(format_ident!("{}", irq.name)); | 390 | irqs.push(format_ident!("{}", irq.name)); |
| 343 | } | 391 | } |
| 344 | 392 | ||
| @@ -350,103 +398,112 @@ fn main() { | |||
| 350 | ); | 398 | ); |
| 351 | }); | 399 | }); |
| 352 | 400 | ||
| 401 | g.extend(time_driver_irq_decl); | ||
| 402 | |||
| 353 | // ======== | 403 | // ======== |
| 354 | // Generate FLASH regions | 404 | // Generate FLASH regions |
| 355 | let mut flash_regions = TokenStream::new(); | 405 | cfgs.declare("flash"); |
| 356 | let flash_memory_regions: Vec<_> = memory | 406 | let mut has_flash = false; |
| 357 | .iter() | 407 | if !chip_name.starts_with("stm32n6") { |
| 358 | .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some()) | 408 | cfgs.enable("flash"); |
| 359 | .collect(); | 409 | has_flash = true; |
| 360 | for region in flash_memory_regions.iter() { | 410 | |
| 361 | let region_name = format_ident!("{}", get_flash_region_name(region.name)); | 411 | let mut flash_regions = TokenStream::new(); |
| 362 | let bank_variant = format_ident!( | 412 | let flash_memory_regions: Vec<_> = memory |
| 363 | "{}", | 413 | .iter() |
| 364 | if region.name.starts_with("BANK_1") { | 414 | .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some()) |
| 365 | "Bank1" | 415 | .collect(); |
| 366 | } else if region.name.starts_with("BANK_2") { | 416 | for region in flash_memory_regions.iter() { |
| 367 | "Bank2" | 417 | let region_name = format_ident!("{}", get_flash_region_name(region.name)); |
| 368 | } else if region.name == "OTP" { | 418 | let bank_variant = format_ident!( |
| 369 | "Otp" | 419 | "{}", |
| 370 | } else { | 420 | if region.name.starts_with("BANK_1") { |
| 371 | continue; | 421 | "Bank1" |
| 372 | } | 422 | } else if region.name.starts_with("BANK_2") { |
| 373 | ); | 423 | "Bank2" |
| 374 | let base = region.address; | 424 | } else if region.name == "OTP" { |
| 375 | let size = region.size; | 425 | "Otp" |
| 376 | let settings = region.settings.as_ref().unwrap(); | 426 | } else { |
| 377 | let erase_size = settings.erase_size; | 427 | continue; |
| 378 | let write_size = settings.write_size; | 428 | } |
| 379 | let erase_value = settings.erase_value; | 429 | ); |
| 380 | 430 | let base = region.address; | |
| 381 | flash_regions.extend(quote! { | 431 | let size = region.size; |
| 382 | pub const #region_name: crate::flash::FlashRegion = crate::flash::FlashRegion { | 432 | let settings = region.settings.as_ref().unwrap(); |
| 383 | bank: crate::flash::FlashBank::#bank_variant, | 433 | let erase_size = settings.erase_size; |
| 384 | base: #base, | 434 | let write_size = settings.write_size; |
| 385 | size: #size, | 435 | let erase_value = settings.erase_value; |
| 386 | erase_size: #erase_size, | 436 | |
| 387 | write_size: #write_size, | 437 | flash_regions.extend(quote! { |
| 388 | erase_value: #erase_value, | 438 | pub const #region_name: crate::flash::FlashRegion = crate::flash::FlashRegion { |
| 389 | _ensure_internal: (), | 439 | bank: crate::flash::FlashBank::#bank_variant, |
| 390 | }; | 440 | base: #base, |
| 391 | }); | 441 | size: #size, |
| 442 | erase_size: #erase_size, | ||
| 443 | write_size: #write_size, | ||
| 444 | erase_value: #erase_value, | ||
| 445 | _ensure_internal: (), | ||
| 446 | }; | ||
| 447 | }); | ||
| 392 | 448 | ||
| 393 | let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); | 449 | let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); |
| 394 | flash_regions.extend(quote! { | 450 | flash_regions.extend(quote! { |
| 395 | #[cfg(flash)] | 451 | #[cfg(flash)] |
| 396 | pub struct #region_type<'d, MODE = crate::flash::Async>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_internal::Peri<'d, crate::peripherals::FLASH>, pub(crate) core::marker::PhantomData<MODE>); | 452 | pub struct #region_type<'d, MODE = crate::flash::Async>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_internal::Peri<'d, crate::peripherals::FLASH>, pub(crate) core::marker::PhantomData<MODE>); |
| 397 | }); | 453 | }); |
| 398 | } | 454 | } |
| 399 | 455 | ||
| 400 | let (fields, (inits, region_names)): (Vec<TokenStream>, (Vec<TokenStream>, Vec<Ident>)) = flash_memory_regions | 456 | let (fields, (inits, region_names)): (Vec<TokenStream>, (Vec<TokenStream>, Vec<Ident>)) = flash_memory_regions |
| 401 | .iter() | 457 | .iter() |
| 402 | .map(|f| { | 458 | .map(|f| { |
| 403 | let region_name = get_flash_region_name(f.name); | 459 | let region_name = get_flash_region_name(f.name); |
| 404 | let field_name = format_ident!("{}", region_name.to_lowercase()); | 460 | let field_name = format_ident!("{}", region_name.to_lowercase()); |
| 405 | let field_type = format_ident!("{}", get_flash_region_type_name(f.name)); | 461 | let field_type = format_ident!("{}", get_flash_region_type_name(f.name)); |
| 406 | let field = quote! { | 462 | let field = quote! { |
| 407 | pub #field_name: #field_type<'d, MODE> | 463 | pub #field_name: #field_type<'d, MODE> |
| 408 | }; | 464 | }; |
| 409 | let region_name = format_ident!("{}", region_name); | 465 | let region_name = format_ident!("{}", region_name); |
| 410 | let init = quote! { | 466 | let init = quote! { |
| 411 | #field_name: #field_type(&#region_name, unsafe { p.clone_unchecked()}, core::marker::PhantomData) | 467 | #field_name: #field_type(&#region_name, unsafe { p.clone_unchecked()}, core::marker::PhantomData) |
| 412 | }; | 468 | }; |
| 413 | 469 | ||
| 414 | (field, (init, region_name)) | 470 | (field, (init, region_name)) |
| 415 | }) | 471 | }) |
| 416 | .unzip(); | 472 | .unzip(); |
| 417 | |||
| 418 | let regions_len = flash_memory_regions.len(); | ||
| 419 | flash_regions.extend(quote! { | ||
| 420 | #[cfg(flash)] | ||
| 421 | pub struct FlashLayout<'d, MODE = crate::flash::Async> { | ||
| 422 | #(#fields),*, | ||
| 423 | _mode: core::marker::PhantomData<MODE>, | ||
| 424 | } | ||
| 425 | 473 | ||
| 426 | #[cfg(flash)] | 474 | let regions_len = flash_memory_regions.len(); |
| 427 | impl<'d, MODE> FlashLayout<'d, MODE> { | 475 | flash_regions.extend(quote! { |
| 428 | pub(crate) fn new(p: embassy_hal_internal::Peri<'d, crate::peripherals::FLASH>) -> Self { | 476 | #[cfg(flash)] |
| 429 | Self { | 477 | pub struct FlashLayout<'d, MODE = crate::flash::Async> { |
| 430 | #(#inits),*, | 478 | #(#fields),*, |
| 431 | _mode: core::marker::PhantomData, | 479 | _mode: core::marker::PhantomData<MODE>, |
| 480 | } | ||
| 481 | |||
| 482 | #[cfg(flash)] | ||
| 483 | impl<'d, MODE> FlashLayout<'d, MODE> { | ||
| 484 | pub(crate) fn new(p: embassy_hal_internal::Peri<'d, crate::peripherals::FLASH>) -> Self { | ||
| 485 | Self { | ||
| 486 | #(#inits),*, | ||
| 487 | _mode: core::marker::PhantomData, | ||
| 488 | } | ||
| 432 | } | 489 | } |
| 433 | } | 490 | } |
| 434 | } | ||
| 435 | 491 | ||
| 436 | pub const FLASH_REGIONS: [&crate::flash::FlashRegion; #regions_len] = [ | 492 | pub const FLASH_REGIONS: [&crate::flash::FlashRegion; #regions_len] = [ |
| 437 | #(&#region_names),* | 493 | #(&#region_names),* |
| 438 | ]; | 494 | ]; |
| 439 | }); | 495 | }); |
| 440 | 496 | ||
| 441 | let max_erase_size = flash_memory_regions | 497 | let max_erase_size = flash_memory_regions |
| 442 | .iter() | 498 | .iter() |
| 443 | .map(|region| region.settings.as_ref().unwrap().erase_size) | 499 | .map(|region| region.settings.as_ref().unwrap().erase_size) |
| 444 | .max() | 500 | .max() |
| 445 | .unwrap(); | 501 | .unwrap(); |
| 446 | 502 | ||
| 447 | g.extend(quote! { pub const MAX_ERASE_SIZE: usize = #max_erase_size as usize; }); | 503 | g.extend(quote! { pub const MAX_ERASE_SIZE: usize = #max_erase_size as usize; }); |
| 448 | 504 | ||
| 449 | g.extend(quote! { pub mod flash_regions { #flash_regions } }); | 505 | g.extend(quote! { pub mod flash_regions { #flash_regions } }); |
| 506 | } | ||
| 450 | 507 | ||
| 451 | // ======== | 508 | // ======== |
| 452 | // Extract the rcc registers | 509 | // Extract the rcc registers |
| @@ -893,6 +950,60 @@ fn main() { | |||
| 893 | } | 950 | } |
| 894 | } | 951 | } |
| 895 | 952 | ||
| 953 | if kind == "gpio" { | ||
| 954 | for p in METADATA.peripherals { | ||
| 955 | // set all GPIOs to analog mode except for PA13 and PA14 which are SWDIO and SWDCLK | ||
| 956 | if p.registers.is_some() | ||
| 957 | && p.registers.as_ref().unwrap().kind == "gpio" | ||
| 958 | && p.registers.as_ref().unwrap().version != "v1" | ||
| 959 | { | ||
| 960 | let port = format_ident!("{}", p.name); | ||
| 961 | if p.name == "GPIOA" { | ||
| 962 | gg.extend(quote! { | ||
| 963 | // leave PA13 and PA14 as unchanged | ||
| 964 | crate::pac::#port.moder().modify(|w| { | ||
| 965 | w.set_moder(0, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 966 | w.set_moder(1, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 967 | w.set_moder(2, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 968 | w.set_moder(3, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 969 | w.set_moder(4, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 970 | w.set_moder(5, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 971 | w.set_moder(6, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 972 | w.set_moder(7, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 973 | w.set_moder(8, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 974 | w.set_moder(9, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 975 | w.set_moder(10, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 976 | w.set_moder(11, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 977 | w.set_moder(12, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 978 | w.set_moder(15, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 979 | }); | ||
| 980 | }); | ||
| 981 | } else { | ||
| 982 | gg.extend(quote! { | ||
| 983 | crate::pac::#port.moder().modify(|w| { | ||
| 984 | w.set_moder(0, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 985 | w.set_moder(1, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 986 | w.set_moder(2, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 987 | w.set_moder(3, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 988 | w.set_moder(4, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 989 | w.set_moder(5, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 990 | w.set_moder(6, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 991 | w.set_moder(7, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 992 | w.set_moder(8, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 993 | w.set_moder(9, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 994 | w.set_moder(10, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 995 | w.set_moder(11, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 996 | w.set_moder(12, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 997 | w.set_moder(13, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 998 | w.set_moder(14, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 999 | w.set_moder(15, crate::pac::gpio::vals::Moder::ANALOG); | ||
| 1000 | }); | ||
| 1001 | }); | ||
| 1002 | } | ||
| 1003 | } | ||
| 1004 | } | ||
| 1005 | } | ||
| 1006 | |||
| 896 | let fname = format_ident!("init_{}", kind); | 1007 | let fname = format_ident!("init_{}", kind); |
| 897 | g.extend(quote! { | 1008 | g.extend(quote! { |
| 898 | pub unsafe fn #fname(){ | 1009 | pub unsafe fn #fname(){ |
| @@ -1329,14 +1440,32 @@ fn main() { | |||
| 1329 | (("tsc", "G8_IO2"), quote!(crate::tsc::G8IO2Pin)), | 1440 | (("tsc", "G8_IO2"), quote!(crate::tsc::G8IO2Pin)), |
| 1330 | (("tsc", "G8_IO3"), quote!(crate::tsc::G8IO3Pin)), | 1441 | (("tsc", "G8_IO3"), quote!(crate::tsc::G8IO3Pin)), |
| 1331 | (("tsc", "G8_IO4"), quote!(crate::tsc::G8IO4Pin)), | 1442 | (("tsc", "G8_IO4"), quote!(crate::tsc::G8IO4Pin)), |
| 1443 | (("lcd", "SEG"), quote!(crate::lcd::SegPin)), | ||
| 1444 | (("lcd", "COM"), quote!(crate::lcd::ComPin)), | ||
| 1445 | (("lcd", "VLCD"), quote!(crate::lcd::VlcdPin)), | ||
| 1332 | (("dac", "OUT1"), quote!(crate::dac::DacPin<Ch1>)), | 1446 | (("dac", "OUT1"), quote!(crate::dac::DacPin<Ch1>)), |
| 1333 | (("dac", "OUT2"), quote!(crate::dac::DacPin<Ch2>)), | 1447 | (("dac", "OUT2"), quote!(crate::dac::DacPin<Ch2>)), |
| 1334 | ].into(); | 1448 | ].into(); |
| 1335 | 1449 | ||
| 1336 | for p in METADATA.peripherals { | 1450 | for p in METADATA.peripherals { |
| 1337 | if let Some(regs) = &p.registers { | 1451 | if let Some(regs) = &p.registers { |
| 1452 | let mut adc_pairs: BTreeMap<u8, (Option<Ident>, Option<Ident>)> = BTreeMap::new(); | ||
| 1453 | let mut seen_lcd_seg_pins = HashSet::new(); | ||
| 1454 | |||
| 1338 | for pin in p.pins { | 1455 | for pin in p.pins { |
| 1339 | let key = (regs.kind, pin.signal); | 1456 | let mut key = (regs.kind, pin.signal); |
| 1457 | |||
| 1458 | // LCD is special. There are so many pins! | ||
| 1459 | if regs.kind == "lcd" { | ||
| 1460 | key.1 = pin.signal.trim_end_matches(char::is_numeric); | ||
| 1461 | |||
| 1462 | if key.1 == "SEG" && !seen_lcd_seg_pins.insert(pin.pin) { | ||
| 1463 | // LCD has SEG pins multiplexed in the peripheral | ||
| 1464 | // This means we can see them twice. We need to skip those so we're not impl'ing the trait twice | ||
| 1465 | continue; | ||
| 1466 | } | ||
| 1467 | } | ||
| 1468 | |||
| 1340 | if let Some(tr) = signals.get(&key) { | 1469 | if let Some(tr) = signals.get(&key) { |
| 1341 | let mut peri = format_ident!("{}", p.name); | 1470 | let mut peri = format_ident!("{}", p.name); |
| 1342 | 1471 | ||
| @@ -1379,6 +1508,11 @@ fn main() { | |||
| 1379 | } | 1508 | } |
| 1380 | } | 1509 | } |
| 1381 | 1510 | ||
| 1511 | // MDIO and MDC are special | ||
| 1512 | if pin.signal == "MDIO" || pin.signal == "MDC" { | ||
| 1513 | peri = format_ident!("{}", "ETH_SMA"); | ||
| 1514 | } | ||
| 1515 | |||
| 1382 | // XSPI NCS pin to CSSEL mapping | 1516 | // XSPI NCS pin to CSSEL mapping |
| 1383 | if pin.signal.ends_with("NCS1") { | 1517 | if pin.signal.ends_with("NCS1") { |
| 1384 | g.extend(quote! { | 1518 | g.extend(quote! { |
| @@ -1456,25 +1590,29 @@ fn main() { | |||
| 1456 | }; | 1590 | }; |
| 1457 | 1591 | ||
| 1458 | // H7 has differential voltage measurements | 1592 | // H7 has differential voltage measurements |
| 1459 | let ch: Option<u8> = if pin.signal.starts_with("INP") { | 1593 | let ch: Option<(u8, bool)> = if pin.signal.starts_with("INP") { |
| 1460 | Some(pin.signal.strip_prefix("INP").unwrap().parse().unwrap()) | 1594 | Some((pin.signal.strip_prefix("INP").unwrap().parse().unwrap(), false)) |
| 1461 | } else if pin.signal.starts_with("INN") { | 1595 | } else if pin.signal.starts_with("INN") { |
| 1462 | // TODO handle in the future when embassy supports differential measurements | 1596 | Some((pin.signal.strip_prefix("INN").unwrap().parse().unwrap(), true)) |
| 1463 | None | ||
| 1464 | } else if pin.signal.starts_with("IN") && pin.signal.ends_with('b') { | 1597 | } else if pin.signal.starts_with("IN") && pin.signal.ends_with('b') { |
| 1465 | // we number STM32L1 ADC bank 1 as 0..=31, bank 2 as 32..=63 | 1598 | // we number STM32L1 ADC bank 1 as 0..=31, bank 2 as 32..=63 |
| 1466 | let signal = pin.signal.strip_prefix("IN").unwrap().strip_suffix('b').unwrap(); | 1599 | let signal = pin.signal.strip_prefix("IN").unwrap().strip_suffix('b').unwrap(); |
| 1467 | Some(32u8 + signal.parse::<u8>().unwrap()) | 1600 | Some((32u8 + signal.parse::<u8>().unwrap(), false)) |
| 1468 | } else if pin.signal.starts_with("IN") { | 1601 | } else if pin.signal.starts_with("IN") { |
| 1469 | Some(pin.signal.strip_prefix("IN").unwrap().parse().unwrap()) | 1602 | Some((pin.signal.strip_prefix("IN").unwrap().parse().unwrap(), false)) |
| 1470 | } else { | 1603 | } else { |
| 1471 | None | 1604 | None |
| 1472 | }; | 1605 | }; |
| 1473 | if let Some(ch) = ch { | 1606 | if let Some((ch, false)) = ch { |
| 1607 | adc_pairs.entry(ch).or_insert((None, None)).0.replace(pin_name.clone()); | ||
| 1608 | |||
| 1474 | g.extend(quote! { | 1609 | g.extend(quote! { |
| 1475 | impl_adc_pin!( #peri, #pin_name, #ch); | 1610 | impl_adc_pin!( #peri, #pin_name, #ch); |
| 1476 | }) | 1611 | }) |
| 1477 | } | 1612 | } |
| 1613 | if let Some((ch, true)) = ch { | ||
| 1614 | adc_pairs.entry(ch).or_insert((None, None)).1.replace(pin_name.clone()); | ||
| 1615 | } | ||
| 1478 | } | 1616 | } |
| 1479 | 1617 | ||
| 1480 | if regs.kind == "opamp" { | 1618 | if regs.kind == "opamp" { |
| @@ -1513,6 +1651,23 @@ fn main() { | |||
| 1513 | }) | 1651 | }) |
| 1514 | } | 1652 | } |
| 1515 | } | 1653 | } |
| 1654 | |||
| 1655 | { | ||
| 1656 | let peri = format_ident!("{}", p.name); | ||
| 1657 | |||
| 1658 | for (ch, (pin, npin)) in adc_pairs { | ||
| 1659 | let (pin_name, npin_name) = match (pin, npin) { | ||
| 1660 | (Some(pin), Some(npin)) => (pin, npin), | ||
| 1661 | _ => { | ||
| 1662 | continue; | ||
| 1663 | } | ||
| 1664 | }; | ||
| 1665 | |||
| 1666 | g.extend(quote! { | ||
| 1667 | impl_adc_pair!( #peri, #pin_name, #npin_name, #ch); | ||
| 1668 | }) | ||
| 1669 | } | ||
| 1670 | } | ||
| 1516 | } | 1671 | } |
| 1517 | } | 1672 | } |
| 1518 | 1673 | ||
| @@ -1561,13 +1716,13 @@ fn main() { | |||
| 1561 | .into(); | 1716 | .into(); |
| 1562 | 1717 | ||
| 1563 | if chip_name.starts_with("stm32u5") { | 1718 | if chip_name.starts_with("stm32u5") { |
| 1564 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma4)); | 1719 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); |
| 1565 | } else { | 1720 | } else { |
| 1566 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); | 1721 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); |
| 1567 | } | 1722 | } |
| 1568 | 1723 | ||
| 1569 | if chip_name.starts_with("stm32wba") { | 1724 | if chip_name.starts_with("stm32wba") { |
| 1570 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma4)); | 1725 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); |
| 1571 | } | 1726 | } |
| 1572 | 1727 | ||
| 1573 | if chip_name.starts_with("stm32g4") { | 1728 | if chip_name.starts_with("stm32g4") { |
| @@ -1654,70 +1809,88 @@ fn main() { | |||
| 1654 | } | 1809 | } |
| 1655 | 1810 | ||
| 1656 | // ======== | 1811 | // ======== |
| 1657 | // Generate Div/Mul impls for RCC prescalers/dividers/multipliers. | 1812 | // Generate Div/Mul impls for RCC and ADC prescalers/dividers/multipliers. |
| 1658 | for e in rcc_registers.ir.enums { | 1813 | for (kind, psc_enums) in ["rcc", "adc", "adccommon"].iter().filter_map(|kind| { |
| 1659 | fn is_rcc_name(e: &str) -> bool { | 1814 | METADATA |
| 1660 | match e { | 1815 | .peripherals |
| 1661 | "Pllp" | "Pllq" | "Pllr" | "Plldivst" | "Pllm" | "Plln" | "Prediv1" | "Prediv2" | "Hpre5" => true, | 1816 | .iter() |
| 1662 | "Timpre" | "Pllrclkpre" => false, | 1817 | .filter_map(|p| p.registers.as_ref()) |
| 1663 | e if e.ends_with("pre") || e.ends_with("pres") || e.ends_with("div") || e.ends_with("mul") => true, | 1818 | .find(|r| r.kind == *kind) |
| 1664 | _ => false, | 1819 | .map(|r| (*kind, r.ir.enums)) |
| 1820 | }) { | ||
| 1821 | for e in psc_enums.iter() { | ||
| 1822 | fn is_adc_name(e: &str) -> bool { | ||
| 1823 | match e { | ||
| 1824 | "Presc" | "Adc4Presc" | "Adcpre" => true, | ||
| 1825 | _ => false, | ||
| 1826 | } | ||
| 1665 | } | 1827 | } |
| 1666 | } | ||
| 1667 | 1828 | ||
| 1668 | fn parse_num(n: &str) -> Result<Frac, ()> { | 1829 | fn is_rcc_name(e: &str) -> bool { |
| 1669 | for prefix in ["DIV", "MUL"] { | 1830 | match e { |
| 1670 | if let Some(n) = n.strip_prefix(prefix) { | 1831 | "Pllp" | "Pllq" | "Pllr" | "Plldivst" | "Pllm" | "Plln" | "Prediv1" | "Prediv2" | "Hpre5" => true, |
| 1671 | let exponent = n.find('_').map(|e| n.len() - 1 - e).unwrap_or(0) as u32; | 1832 | "Timpre" | "Pllrclkpre" => false, |
| 1672 | let mantissa = n.replace('_', "").parse().map_err(|_| ())?; | 1833 | e if e.ends_with("pre") || e.ends_with("pres") || e.ends_with("div") || e.ends_with("mul") => true, |
| 1673 | let f = Frac { | 1834 | _ => false, |
| 1674 | num: mantissa, | ||
| 1675 | denom: 10u32.pow(exponent), | ||
| 1676 | }; | ||
| 1677 | return Ok(f.simplify()); | ||
| 1678 | } | 1835 | } |
| 1679 | } | 1836 | } |
| 1680 | Err(()) | ||
| 1681 | } | ||
| 1682 | 1837 | ||
| 1683 | if is_rcc_name(e.name) { | 1838 | fn parse_num(n: &str) -> Result<Frac, ()> { |
| 1684 | let enum_name = format_ident!("{}", e.name); | 1839 | for prefix in ["DIV", "MUL"] { |
| 1685 | let mut muls = Vec::new(); | 1840 | if let Some(n) = n.strip_prefix(prefix) { |
| 1686 | let mut divs = Vec::new(); | 1841 | let exponent = n.find('_').map(|e| n.len() - 1 - e).unwrap_or(0) as u32; |
| 1687 | for v in e.variants { | 1842 | let mantissa = n.replace('_', "").parse().map_err(|_| ())?; |
| 1688 | let Ok(val) = parse_num(v.name) else { | 1843 | let f = Frac { |
| 1689 | panic!("could not parse mul/div. enum={} variant={}", e.name, v.name) | 1844 | num: mantissa, |
| 1690 | }; | 1845 | denom: 10u32.pow(exponent), |
| 1691 | let variant_name = format_ident!("{}", v.name); | 1846 | }; |
| 1692 | let variant = quote!(crate::pac::rcc::vals::#enum_name::#variant_name); | 1847 | return Ok(f.simplify()); |
| 1693 | let num = val.num; | 1848 | } |
| 1694 | let denom = val.denom; | 1849 | } |
| 1695 | muls.push(quote!(#variant => self * #num / #denom,)); | 1850 | Err(()) |
| 1696 | divs.push(quote!(#variant => self * #denom / #num,)); | ||
| 1697 | } | 1851 | } |
| 1698 | 1852 | ||
| 1699 | g.extend(quote! { | 1853 | if (kind == "rcc" && is_rcc_name(e.name)) || ((kind == "adccommon" || kind == "adc") && is_adc_name(e.name)) |
| 1700 | impl core::ops::Div<crate::pac::rcc::vals::#enum_name> for crate::time::Hertz { | 1854 | { |
| 1701 | type Output = crate::time::Hertz; | 1855 | let kind = format_ident!("{}", kind); |
| 1702 | fn div(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output { | 1856 | let enum_name = format_ident!("{}", e.name); |
| 1703 | match rhs { | 1857 | let mut muls = Vec::new(); |
| 1704 | #(#divs)* | 1858 | let mut divs = Vec::new(); |
| 1705 | #[allow(unreachable_patterns)] | 1859 | for v in e.variants { |
| 1706 | _ => unreachable!(), | 1860 | let Ok(val) = parse_num(v.name) else { |
| 1861 | panic!("could not parse mul/div. enum={} variant={}", e.name, v.name) | ||
| 1862 | }; | ||
| 1863 | let variant_name = format_ident!("{}", v.name); | ||
| 1864 | let variant = quote!(crate::pac::#kind::vals::#enum_name::#variant_name); | ||
| 1865 | let num = val.num; | ||
| 1866 | let denom = val.denom; | ||
| 1867 | muls.push(quote!(#variant => self * #num / #denom,)); | ||
| 1868 | divs.push(quote!(#variant => self * #denom / #num,)); | ||
| 1869 | } | ||
| 1870 | |||
| 1871 | g.extend(quote! { | ||
| 1872 | impl core::ops::Div<crate::pac::#kind::vals::#enum_name> for crate::time::Hertz { | ||
| 1873 | type Output = crate::time::Hertz; | ||
| 1874 | fn div(self, rhs: crate::pac::#kind::vals::#enum_name) -> Self::Output { | ||
| 1875 | match rhs { | ||
| 1876 | #(#divs)* | ||
| 1877 | #[allow(unreachable_patterns)] | ||
| 1878 | _ => unreachable!(), | ||
| 1879 | } | ||
| 1707 | } | 1880 | } |
| 1708 | } | 1881 | } |
| 1709 | } | 1882 | impl core::ops::Mul<crate::pac::#kind::vals::#enum_name> for crate::time::Hertz { |
| 1710 | impl core::ops::Mul<crate::pac::rcc::vals::#enum_name> for crate::time::Hertz { | 1883 | type Output = crate::time::Hertz; |
| 1711 | type Output = crate::time::Hertz; | 1884 | fn mul(self, rhs: crate::pac::#kind::vals::#enum_name) -> Self::Output { |
| 1712 | fn mul(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output { | 1885 | match rhs { |
| 1713 | match rhs { | 1886 | #(#muls)* |
| 1714 | #(#muls)* | 1887 | #[allow(unreachable_patterns)] |
| 1715 | #[allow(unreachable_patterns)] | 1888 | _ => unreachable!(), |
| 1716 | _ => unreachable!(), | 1889 | } |
| 1717 | } | 1890 | } |
| 1718 | } | 1891 | } |
| 1719 | } | 1892 | }); |
| 1720 | }); | 1893 | } |
| 1721 | } | 1894 | } |
| 1722 | } | 1895 | } |
| 1723 | 1896 | ||
| @@ -1727,7 +1900,19 @@ fn main() { | |||
| 1727 | for p in METADATA.peripherals { | 1900 | for p in METADATA.peripherals { |
| 1728 | let mut pt = TokenStream::new(); | 1901 | let mut pt = TokenStream::new(); |
| 1729 | 1902 | ||
| 1903 | let mut exti2_tsc_injected = false; | ||
| 1904 | if let Some(ref irq) = exti2_tsc_shared_int_present | ||
| 1905 | && p.name == "EXTI" | ||
| 1906 | { | ||
| 1907 | exti2_tsc_injected = true; | ||
| 1908 | let iname = format_ident!("{}", irq.name); | ||
| 1909 | let sname = format_ident!("{}", "EXTI2"); | ||
| 1910 | pt.extend(quote!(pub type #sname = crate::interrupt::typelevel::#iname;)); | ||
| 1911 | } | ||
| 1730 | for irq in p.interrupts { | 1912 | for irq in p.interrupts { |
| 1913 | if exti2_tsc_injected && irq.signal == "EXTI2" { | ||
| 1914 | continue; | ||
| 1915 | } | ||
| 1731 | let iname = format_ident!("{}", irq.interrupt); | 1916 | let iname = format_ident!("{}", irq.interrupt); |
| 1732 | let sname = format_ident!("{}", irq.signal); | 1917 | let sname = format_ident!("{}", irq.signal); |
| 1733 | pt.extend(quote!(pub type #sname = crate::interrupt::typelevel::#iname;)); | 1918 | pt.extend(quote!(pub type #sname = crate::interrupt::typelevel::#iname;)); |
| @@ -1760,7 +1945,7 @@ fn main() { | |||
| 1760 | flash_regions_table.push(row); | 1945 | flash_regions_table.push(row); |
| 1761 | } | 1946 | } |
| 1762 | 1947 | ||
| 1763 | let gpio_base = METADATA.peripherals.iter().find(|p| p.name == "GPIOA").unwrap().address as u32; | 1948 | let gpio_base = peripheral_map.get("GPIOA").unwrap().address as u32; |
| 1764 | let gpio_stride = 0x400; | 1949 | let gpio_stride = 0x400; |
| 1765 | 1950 | ||
| 1766 | for pin in METADATA.pins { | 1951 | for pin in METADATA.pins { |
| @@ -1844,7 +2029,12 @@ fn main() { | |||
| 1844 | if r.kind == "dma" || r.kind == "bdma" || r.kind == "gpdma" || r.kind == "lpdma" { | 2029 | if r.kind == "dma" || r.kind == "bdma" || r.kind == "gpdma" || r.kind == "lpdma" { |
| 1845 | for irq in p.interrupts { | 2030 | for irq in p.interrupts { |
| 1846 | let ch_name = format!("{}_{}", p.name, irq.signal); | 2031 | let ch_name = format!("{}_{}", p.name, irq.signal); |
| 1847 | let ch = METADATA.dma_channels.iter().find(|c| c.name == ch_name).unwrap(); | 2032 | let ch = METADATA.dma_channels.iter().find(|c| c.name == ch_name); |
| 2033 | |||
| 2034 | if ch.is_none() { | ||
| 2035 | continue; | ||
| 2036 | } | ||
| 2037 | let ch = ch.unwrap(); | ||
| 1848 | 2038 | ||
| 1849 | // Some H7 chips have BDMA1 hardcoded for DFSDM, ie no DMAMUX. It's unsupported, skip it. | 2039 | // Some H7 chips have BDMA1 hardcoded for DFSDM, ie no DMAMUX. It's unsupported, skip it. |
| 1850 | if has_dmamux && ch.dmamux.is_none() { | 2040 | if has_dmamux && ch.dmamux.is_none() { |
| @@ -1873,6 +2063,19 @@ fn main() { | |||
| 1873 | continue; | 2063 | continue; |
| 1874 | } | 2064 | } |
| 1875 | 2065 | ||
| 2066 | let dma_peri = peripheral_map.get(ch.dma).unwrap(); | ||
| 2067 | let stop_mode = dma_peri | ||
| 2068 | .rcc | ||
| 2069 | .as_ref() | ||
| 2070 | .map(|rcc| rcc.stop_mode.clone()) | ||
| 2071 | .unwrap_or_default(); | ||
| 2072 | |||
| 2073 | let stop_mode = match stop_mode { | ||
| 2074 | StopMode::Standby => quote! { Standby }, | ||
| 2075 | StopMode::Stop2 => quote! { Stop2 }, | ||
| 2076 | StopMode::Stop1 => quote! { Stop1 }, | ||
| 2077 | }; | ||
| 2078 | |||
| 1876 | let name = format_ident!("{}", ch.name); | 2079 | let name = format_ident!("{}", ch.name); |
| 1877 | let idx = ch_idx as u8; | 2080 | let idx = ch_idx as u8; |
| 1878 | #[cfg(feature = "_dual-core")] | 2081 | #[cfg(feature = "_dual-core")] |
| @@ -1885,12 +2088,10 @@ fn main() { | |||
| 1885 | quote!(crate::pac::Interrupt::#irq_name) | 2088 | quote!(crate::pac::Interrupt::#irq_name) |
| 1886 | }; | 2089 | }; |
| 1887 | 2090 | ||
| 1888 | g.extend(quote!(dma_channel_impl!(#name, #idx);)); | 2091 | g.extend(quote!(dma_channel_impl!(#name, #idx, #stop_mode);)); |
| 1889 | 2092 | ||
| 1890 | let dma = format_ident!("{}", ch.dma); | 2093 | let dma = format_ident!("{}", ch.dma); |
| 1891 | let ch_num = ch.channel as usize; | 2094 | let ch_num = ch.channel as usize; |
| 1892 | |||
| 1893 | let dma_peri = METADATA.peripherals.iter().find(|p| p.name == ch.dma).unwrap(); | ||
| 1894 | let bi = dma_peri.registers.as_ref().unwrap(); | 2095 | let bi = dma_peri.registers.as_ref().unwrap(); |
| 1895 | 2096 | ||
| 1896 | let dma_info = match bi.kind { | 2097 | let dma_info = match bi.kind { |
| @@ -1983,33 +2184,47 @@ fn main() { | |||
| 1983 | )); | 2184 | )); |
| 1984 | 2185 | ||
| 1985 | // ======== | 2186 | // ======== |
| 2187 | // Generate backup sram constants | ||
| 2188 | if let Some(m) = memory.iter().find(|m| m.name == "BKPSRAM") { | ||
| 2189 | let bkpsram_base = m.address as usize; | ||
| 2190 | let bkpsram_size = m.size as usize; | ||
| 2191 | |||
| 2192 | g.extend(quote!( | ||
| 2193 | pub const BKPSRAM_BASE: usize = #bkpsram_base; | ||
| 2194 | pub const BKPSRAM_SIZE: usize = #bkpsram_size; | ||
| 2195 | )); | ||
| 2196 | } | ||
| 2197 | |||
| 2198 | // ======== | ||
| 1986 | // Generate flash constants | 2199 | // Generate flash constants |
| 1987 | 2200 | ||
| 1988 | let flash_regions: Vec<&MemoryRegion> = memory | 2201 | if has_flash { |
| 1989 | .iter() | 2202 | let flash_regions: Vec<&MemoryRegion> = memory |
| 1990 | .filter(|x| x.kind == MemoryRegionKind::Flash && x.name.starts_with("BANK_")) | 2203 | .iter() |
| 1991 | .collect(); | 2204 | .filter(|x| x.kind == MemoryRegionKind::Flash && x.name.starts_with("BANK_")) |
| 1992 | let first_flash = flash_regions.first().unwrap(); | 2205 | .collect(); |
| 1993 | let total_flash_size = flash_regions | 2206 | let first_flash = flash_regions.first().unwrap(); |
| 1994 | .iter() | 2207 | let total_flash_size = flash_regions |
| 1995 | .map(|x| x.size) | 2208 | .iter() |
| 1996 | .reduce(|acc, item| acc + item) | 2209 | .map(|x| x.size) |
| 1997 | .unwrap(); | 2210 | .reduce(|acc, item| acc + item) |
| 1998 | let write_sizes: HashSet<_> = flash_regions | 2211 | .unwrap(); |
| 1999 | .iter() | 2212 | let write_sizes: HashSet<_> = flash_regions |
| 2000 | .map(|r| r.settings.as_ref().unwrap().write_size) | 2213 | .iter() |
| 2001 | .collect(); | 2214 | .map(|r| r.settings.as_ref().unwrap().write_size) |
| 2002 | assert_eq!(1, write_sizes.len()); | 2215 | .collect(); |
| 2216 | assert_eq!(1, write_sizes.len()); | ||
| 2003 | 2217 | ||
| 2004 | let flash_base = first_flash.address as usize; | 2218 | let flash_base = first_flash.address as usize; |
| 2005 | let total_flash_size = total_flash_size as usize; | 2219 | let total_flash_size = total_flash_size as usize; |
| 2006 | let write_size = (*write_sizes.iter().next().unwrap()) as usize; | 2220 | let write_size = (*write_sizes.iter().next().unwrap()) as usize; |
| 2007 | 2221 | ||
| 2008 | g.extend(quote!( | 2222 | g.extend(quote!( |
| 2009 | pub const FLASH_BASE: usize = #flash_base; | 2223 | pub const FLASH_BASE: usize = #flash_base; |
| 2010 | pub const FLASH_SIZE: usize = #total_flash_size; | 2224 | pub const FLASH_SIZE: usize = #total_flash_size; |
| 2011 | pub const WRITE_SIZE: usize = #write_size; | 2225 | pub const WRITE_SIZE: usize = #write_size; |
| 2012 | )); | 2226 | )); |
| 2227 | } | ||
| 2013 | 2228 | ||
| 2014 | // ======== | 2229 | // ======== |
| 2015 | // Generate EEPROM constants | 2230 | // Generate EEPROM constants |
| @@ -2309,6 +2524,10 @@ fn mem_filter(chip: &str, region: &str) -> bool { | |||
| 2309 | return false; | 2524 | return false; |
| 2310 | } | 2525 | } |
| 2311 | 2526 | ||
| 2527 | if region.starts_with("SDRAM_") || region.starts_with("FMC_") || region.starts_with("OCTOSPI_") { | ||
| 2528 | return false; | ||
| 2529 | } | ||
| 2530 | |||
| 2312 | true | 2531 | true |
| 2313 | } | 2532 | } |
| 2314 | 2533 | ||
diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index 255dc7956..491569c34 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs | |||
| @@ -4,8 +4,8 @@ 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::blocking_delay_us; |
| 8 | use crate::dma::Transfer; | 8 | use crate::adc::{AdcRegs, ConversionMode, Instance}; |
| 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; |
| 11 | #[cfg(stm32wba)] | 11 | #[cfg(stm32wba)] |
| @@ -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 | ||
| @@ -24,56 +24,24 @@ pub const VREF_DEFAULT_MV: u32 = 3300; | |||
| 24 | /// VREF voltage used for factory calibration of VREFINTCAL register. | 24 | /// VREF voltage used for factory calibration of VREFINTCAL register. |
| 25 | pub const VREF_CALIB_MV: u32 = 3300; | 25 | pub const VREF_CALIB_MV: u32 = 3300; |
| 26 | 26 | ||
| 27 | const VREF_CHANNEL: u8 = 0; | 27 | impl super::SealedSpecialConverter<super::VrefInt> for crate::peripherals::ADC4 { |
| 28 | const VCORE_CHANNEL: u8 = 12; | 28 | const CHANNEL: u8 = 0; |
| 29 | const TEMP_CHANNEL: u8 = 13; | ||
| 30 | const VBAT_CHANNEL: u8 = 14; | ||
| 31 | const DAC_CHANNEL: u8 = 21; | ||
| 32 | |||
| 33 | // 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 | ||
| 34 | /// Internal voltage reference channel. | ||
| 35 | pub struct VrefInt; | ||
| 36 | impl<T: Instance> AdcChannel<T> for VrefInt {} | ||
| 37 | impl<T: Instance> SealedAdcChannel<T> for VrefInt { | ||
| 38 | fn channel(&self) -> u8 { | ||
| 39 | VREF_CHANNEL | ||
| 40 | } | ||
| 41 | } | 29 | } |
| 42 | 30 | ||
| 43 | /// Internal temperature channel. | 31 | impl super::SealedSpecialConverter<super::Temperature> for crate::peripherals::ADC4 { |
| 44 | pub struct Temperature; | 32 | const CHANNEL: u8 = 13; |
| 45 | impl<T: Instance> AdcChannel<T> for Temperature {} | ||
| 46 | impl<T: Instance> SealedAdcChannel<T> for Temperature { | ||
| 47 | fn channel(&self) -> u8 { | ||
| 48 | TEMP_CHANNEL | ||
| 49 | } | ||
| 50 | } | 33 | } |
| 51 | 34 | ||
| 52 | /// Internal battery voltage channel. | 35 | impl super::SealedSpecialConverter<super::Vcore> for crate::peripherals::ADC4 { |
| 53 | pub struct Vbat; | 36 | const CHANNEL: u8 = 12; |
| 54 | impl<T: Instance> AdcChannel<T> for Vbat {} | ||
| 55 | impl<T: Instance> SealedAdcChannel<T> for Vbat { | ||
| 56 | fn channel(&self) -> u8 { | ||
| 57 | VBAT_CHANNEL | ||
| 58 | } | ||
| 59 | } | 37 | } |
| 60 | 38 | ||
| 61 | /// Internal DAC channel. | 39 | impl super::SealedSpecialConverter<super::Vbat> for crate::peripherals::ADC4 { |
| 62 | pub struct Dac; | 40 | const CHANNEL: u8 = 14; |
| 63 | impl<T: Instance> AdcChannel<T> for Dac {} | ||
| 64 | impl<T: Instance> SealedAdcChannel<T> for Dac { | ||
| 65 | fn channel(&self) -> u8 { | ||
| 66 | DAC_CHANNEL | ||
| 67 | } | ||
| 68 | } | 41 | } |
| 69 | 42 | ||
| 70 | /// Internal Vcore channel. | 43 | impl super::SealedSpecialConverter<super::Dac> for crate::peripherals::ADC4 { |
| 71 | pub struct Vcore; | 44 | const CHANNEL: u8 = 21; |
| 72 | impl<T: Instance> AdcChannel<T> for Vcore {} | ||
| 73 | impl<T: Instance> SealedAdcChannel<T> for Vcore { | ||
| 74 | fn channel(&self) -> u8 { | ||
| 75 | VCORE_CHANNEL | ||
| 76 | } | ||
| 77 | } | 45 | } |
| 78 | 46 | ||
| 79 | #[derive(Copy, Clone)] | 47 | #[derive(Copy, Clone)] |
| @@ -108,123 +76,145 @@ pub const fn resolution_to_max_count(res: Resolution) -> u32 { | |||
| 108 | } | 76 | } |
| 109 | } | 77 | } |
| 110 | 78 | ||
| 111 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | 79 | fn from_ker_ck(frequency: Hertz) -> Presc { |
| 112 | // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. | 80 | let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); |
| 113 | #[allow(unused)] | 81 | match raw_prescaler { |
| 114 | enum Prescaler { | 82 | 0 => Presc::DIV1, |
| 115 | NotDivided, | 83 | 1 => Presc::DIV2, |
| 116 | DividedBy2, | 84 | 2..=3 => Presc::DIV4, |
| 117 | DividedBy4, | 85 | 4..=5 => Presc::DIV6, |
| 118 | DividedBy6, | 86 | 6..=7 => Presc::DIV8, |
| 119 | DividedBy8, | 87 | 8..=9 => Presc::DIV10, |
| 120 | DividedBy10, | 88 | 10..=11 => Presc::DIV12, |
| 121 | DividedBy12, | 89 | _ => unimplemented!(), |
| 122 | DividedBy16, | 90 | } |
| 123 | DividedBy32, | ||
| 124 | DividedBy64, | ||
| 125 | DividedBy128, | ||
| 126 | DividedBy256, | ||
| 127 | } | 91 | } |
| 128 | 92 | ||
| 129 | impl Prescaler { | 93 | impl AdcRegs for crate::pac::adc::Adc4 { |
| 130 | fn from_ker_ck(frequency: Hertz) -> Self { | 94 | fn data(&self) -> *mut u16 { |
| 131 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | 95 | crate::pac::adc::Adc4::dr(*self).as_ptr() as *mut u16 |
| 132 | match raw_prescaler { | 96 | } |
| 133 | 0 => Self::NotDivided, | 97 | |
| 134 | 1 => Self::DividedBy2, | 98 | fn enable(&self) { |
| 135 | 2..=3 => Self::DividedBy4, | 99 | if !self.cr().read().aden() || !self.isr().read().adrdy() { |
| 136 | 4..=5 => Self::DividedBy6, | 100 | self.isr().write(|w| w.set_adrdy(true)); |
| 137 | 6..=7 => Self::DividedBy8, | 101 | self.cr().modify(|w| w.set_aden(true)); |
| 138 | 8..=9 => Self::DividedBy10, | 102 | while !self.isr().read().adrdy() {} |
| 139 | 10..=11 => Self::DividedBy12, | ||
| 140 | _ => unimplemented!(), | ||
| 141 | } | 103 | } |
| 142 | } | 104 | } |
| 143 | 105 | ||
| 144 | fn divisor(&self) -> u32 { | 106 | fn start(&self) { |
| 145 | match self { | 107 | // Start conversion |
| 146 | Prescaler::NotDivided => 1, | 108 | self.cr().modify(|reg| { |
| 147 | Prescaler::DividedBy2 => 2, | 109 | reg.set_adstart(true); |
| 148 | Prescaler::DividedBy4 => 4, | 110 | }); |
| 149 | Prescaler::DividedBy6 => 6, | 111 | } |
| 150 | Prescaler::DividedBy8 => 8, | 112 | |
| 151 | Prescaler::DividedBy10 => 10, | 113 | fn stop(&self) { |
| 152 | Prescaler::DividedBy12 => 12, | 114 | let cr = self.cr().read(); |
| 153 | Prescaler::DividedBy16 => 16, | 115 | if cr.adstart() { |
| 154 | Prescaler::DividedBy32 => 32, | 116 | self.cr().modify(|w| w.set_adstp(true)); |
| 155 | Prescaler::DividedBy64 => 64, | 117 | while self.cr().read().adstart() {} |
| 156 | Prescaler::DividedBy128 => 128, | 118 | } |
| 157 | Prescaler::DividedBy256 => 256, | 119 | |
| 120 | if cr.aden() || cr.adstart() { | ||
| 121 | self.cr().modify(|w| w.set_addis(true)); | ||
| 122 | while self.cr().read().aden() {} | ||
| 158 | } | 123 | } |
| 124 | |||
| 125 | // Reset configuration. | ||
| 126 | self.cfgr1().modify(|reg| { | ||
| 127 | reg.set_dmaen(false); | ||
| 128 | }); | ||
| 159 | } | 129 | } |
| 160 | 130 | ||
| 161 | fn presc(&self) -> Presc { | 131 | fn configure_dma(&self, conversion_mode: ConversionMode) { |
| 162 | match self { | 132 | match conversion_mode { |
| 163 | Prescaler::NotDivided => Presc::DIV1, | 133 | ConversionMode::Singular => { |
| 164 | Prescaler::DividedBy2 => Presc::DIV2, | 134 | self.isr().modify(|reg| { |
| 165 | Prescaler::DividedBy4 => Presc::DIV4, | 135 | reg.set_ovr(true); |
| 166 | Prescaler::DividedBy6 => Presc::DIV6, | 136 | reg.set_eos(true); |
| 167 | Prescaler::DividedBy8 => Presc::DIV8, | 137 | reg.set_eoc(true); |
| 168 | Prescaler::DividedBy10 => Presc::DIV10, | 138 | }); |
| 169 | Prescaler::DividedBy12 => Presc::DIV12, | 139 | |
| 170 | Prescaler::DividedBy16 => Presc::DIV16, | 140 | self.cfgr1().modify(|reg| { |
| 171 | Prescaler::DividedBy32 => Presc::DIV32, | 141 | reg.set_dmaen(true); |
| 172 | Prescaler::DividedBy64 => Presc::DIV64, | 142 | reg.set_dmacfg(Dmacfg::ONE_SHOT); |
| 173 | Prescaler::DividedBy128 => Presc::DIV128, | 143 | #[cfg(stm32u5)] |
| 174 | Prescaler::DividedBy256 => Presc::DIV256, | 144 | reg.set_chselrmod(false); |
| 145 | #[cfg(stm32wba)] | ||
| 146 | reg.set_chselrmod(Chselrmod::ENABLE_INPUT) | ||
| 147 | }); | ||
| 148 | } | ||
| 149 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 150 | _ => unreachable!(), | ||
| 175 | } | 151 | } |
| 176 | } | 152 | } |
| 177 | } | ||
| 178 | 153 | ||
| 179 | pub trait SealedInstance { | 154 | fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| 180 | #[allow(unused)] | 155 | let mut prev_channel: i16 = -1; |
| 181 | fn regs() -> crate::pac::adc::Adc4; | 156 | #[cfg(stm32wba)] |
| 182 | } | 157 | self.chselr().write_value(Chselr(0_u32)); |
| 158 | #[cfg(stm32u5)] | ||
| 159 | self.chselrmod0().write_value(Chselr(0_u32)); | ||
| 160 | for (_i, ((channel, _), sample_time)) in sequence.enumerate() { | ||
| 161 | self.smpr().modify(|w| { | ||
| 162 | w.set_smp(_i, sample_time); | ||
| 163 | }); | ||
| 183 | 164 | ||
| 184 | pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral { | 165 | let channel_num = channel; |
| 185 | type Interrupt: crate::interrupt::typelevel::Interrupt; | 166 | if channel_num as i16 <= prev_channel { |
| 186 | } | 167 | return; |
| 168 | }; | ||
| 169 | prev_channel = channel_num as i16; | ||
| 187 | 170 | ||
| 188 | pub struct Adc4<'d, T: Instance> { | 171 | #[cfg(stm32wba)] |
| 189 | #[allow(unused)] | 172 | self.chselr().modify(|w| { |
| 190 | adc: crate::Peri<'d, T>, | 173 | w.set_chsel0(channel as usize, true); |
| 191 | } | 174 | }); |
| 175 | #[cfg(stm32u5)] | ||
| 176 | self.chselrmod0().modify(|w| { | ||
| 177 | w.set_chsel(channel as usize, true); | ||
| 178 | }); | ||
| 179 | } | ||
| 180 | } | ||
| 192 | 181 | ||
| 193 | #[derive(Copy, Clone, Debug)] | 182 | fn convert(&self) { |
| 194 | pub enum Adc4Error { | 183 | // Reset interrupts |
| 195 | InvalidSequence, | 184 | self.isr().modify(|reg| { |
| 196 | DMAError, | 185 | reg.set_eos(true); |
| 186 | reg.set_eoc(true); | ||
| 187 | }); | ||
| 188 | |||
| 189 | // Start conversion | ||
| 190 | self.cr().modify(|reg| { | ||
| 191 | reg.set_adstart(true); | ||
| 192 | }); | ||
| 193 | |||
| 194 | while !self.isr().read().eos() { | ||
| 195 | // spin | ||
| 196 | } | ||
| 197 | } | ||
| 197 | } | 198 | } |
| 198 | 199 | ||
| 199 | impl<'d, T: Instance> Adc4<'d, T> { | 200 | impl<'d, T: Instance<Regs = crate::pac::adc::Adc4>> super::Adc<'d, T> { |
| 200 | /// Create a new ADC driver. | 201 | /// Create a new ADC driver. |
| 201 | pub fn new(adc: Peri<'d, T>) -> Self { | 202 | pub fn new_adc4(adc: Peri<'d, T>) -> Self { |
| 202 | rcc::enable_and_reset::<T>(); | 203 | rcc::enable_and_reset::<T>(); |
| 203 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 204 | let prescaler = from_ker_ck(T::frequency()); |
| 204 | 205 | ||
| 205 | T::regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 206 | T::regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 206 | 207 | ||
| 207 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 208 | let frequency = T::frequency() / prescaler; |
| 208 | info!("ADC4 frequency set to {}", frequency); | 209 | info!("ADC4 frequency set to {}", frequency); |
| 209 | 210 | ||
| 210 | if frequency > MAX_ADC_CLK_FREQ { | 211 | 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 ); | 212 | panic!( |
| 213 | "Maximal allowed frequency for ADC4 is {} MHz and it varies with different packages, refer to ST docs for more information.", | ||
| 214 | MAX_ADC_CLK_FREQ.0 / 1_000_000 | ||
| 215 | ); | ||
| 212 | } | 216 | } |
| 213 | 217 | ||
| 214 | let mut s = Self { adc }; | ||
| 215 | |||
| 216 | s.power_up(); | ||
| 217 | |||
| 218 | s.calibrate(); | ||
| 219 | blocking_delay_us(1); | ||
| 220 | |||
| 221 | s.enable(); | ||
| 222 | s.configure(); | ||
| 223 | |||
| 224 | s | ||
| 225 | } | ||
| 226 | |||
| 227 | fn power_up(&mut self) { | ||
| 228 | T::regs().isr().modify(|w| { | 218 | T::regs().isr().modify(|w| { |
| 229 | w.set_ldordy(true); | 219 | w.set_ldordy(true); |
| 230 | }); | 220 | }); |
| @@ -236,22 +226,15 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 236 | T::regs().isr().modify(|w| { | 226 | T::regs().isr().modify(|w| { |
| 237 | w.set_ldordy(true); | 227 | w.set_ldordy(true); |
| 238 | }); | 228 | }); |
| 239 | } | ||
| 240 | 229 | ||
| 241 | fn calibrate(&mut self) { | ||
| 242 | T::regs().cr().modify(|w| w.set_adcal(true)); | 230 | T::regs().cr().modify(|w| w.set_adcal(true)); |
| 243 | while T::regs().cr().read().adcal() {} | 231 | while T::regs().cr().read().adcal() {} |
| 244 | T::regs().isr().modify(|w| w.set_eocal(true)); | 232 | T::regs().isr().modify(|w| w.set_eocal(true)); |
| 245 | } | ||
| 246 | 233 | ||
| 247 | fn enable(&mut self) { | 234 | blocking_delay_us(1); |
| 248 | T::regs().isr().write(|w| w.set_adrdy(true)); | 235 | |
| 249 | T::regs().cr().modify(|w| w.set_aden(true)); | 236 | T::regs().enable(); |
| 250 | while !T::regs().isr().read().adrdy() {} | ||
| 251 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 252 | } | ||
| 253 | 237 | ||
| 254 | fn configure(&mut self) { | ||
| 255 | // single conversion mode, software trigger | 238 | // single conversion mode, software trigger |
| 256 | T::regs().cfgr1().modify(|w| { | 239 | T::regs().cfgr1().modify(|w| { |
| 257 | #[cfg(stm32u5)] | 240 | #[cfg(stm32u5)] |
| @@ -277,73 +260,63 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 277 | w.set_smpsel(i, Smpsel::SMP1); | 260 | w.set_smpsel(i, Smpsel::SMP1); |
| 278 | } | 261 | } |
| 279 | }); | 262 | }); |
| 263 | |||
| 264 | Self { adc } | ||
| 280 | } | 265 | } |
| 281 | 266 | ||
| 282 | /// Enable reading the voltage reference internal channel. | 267 | /// Enable reading the voltage reference internal channel. |
| 283 | pub fn enable_vrefint(&self) -> VrefInt { | 268 | pub fn enable_vrefint_adc4(&self) -> super::VrefInt { |
| 284 | T::regs().ccr().modify(|w| { | 269 | T::regs().ccr().modify(|w| { |
| 285 | w.set_vrefen(true); | 270 | w.set_vrefen(true); |
| 286 | }); | 271 | }); |
| 287 | 272 | ||
| 288 | VrefInt {} | 273 | super::VrefInt {} |
| 289 | } | 274 | } |
| 290 | 275 | ||
| 291 | /// Enable reading the temperature internal channel. | 276 | /// Enable reading the temperature internal channel. |
| 292 | pub fn enable_temperature(&self) -> Temperature { | 277 | pub fn enable_temperature_adc4(&self) -> super::Temperature { |
| 293 | T::regs().ccr().modify(|w| { | 278 | T::regs().ccr().modify(|w| { |
| 294 | w.set_vsensesel(true); | 279 | w.set_vsensesel(true); |
| 295 | }); | 280 | }); |
| 296 | 281 | ||
| 297 | Temperature {} | 282 | super::Temperature {} |
| 298 | } | 283 | } |
| 299 | 284 | ||
| 300 | /// Enable reading the vbat internal channel. | 285 | /// Enable reading the vbat internal channel. |
| 301 | #[cfg(stm32u5)] | 286 | #[cfg(stm32u5)] |
| 302 | pub fn enable_vbat(&self) -> Vbat { | 287 | pub fn enable_vbat_adc4(&self) -> super::Vbat { |
| 303 | T::regs().ccr().modify(|w| { | 288 | T::regs().ccr().modify(|w| { |
| 304 | w.set_vbaten(true); | 289 | w.set_vbaten(true); |
| 305 | }); | 290 | }); |
| 306 | 291 | ||
| 307 | Vbat {} | 292 | super::Vbat {} |
| 308 | } | 293 | } |
| 309 | 294 | ||
| 310 | /// Enable reading the vbat internal channel. | 295 | /// Enable reading the vbat internal channel. |
| 311 | pub fn enable_vcore(&self) -> Vcore { | 296 | pub fn enable_vcore_adc4(&self) -> super::Vcore { |
| 312 | Vcore {} | 297 | super::Vcore {} |
| 313 | } | 298 | } |
| 314 | 299 | ||
| 315 | /// Enable reading the vbat internal channel. | 300 | /// Enable reading the vbat internal channel. |
| 316 | #[cfg(stm32u5)] | 301 | #[cfg(stm32u5)] |
| 317 | pub fn enable_dac_channel(&self, dac: DacChannel) -> Dac { | 302 | pub fn enable_dac_channel_adc4(&self, dac: DacChannel) -> super::Dac { |
| 318 | let mux; | 303 | let mux; |
| 319 | match dac { | 304 | match dac { |
| 320 | DacChannel::OUT1 => mux = false, | 305 | DacChannel::OUT1 => mux = false, |
| 321 | DacChannel::OUT2 => mux = true, | 306 | DacChannel::OUT2 => mux = true, |
| 322 | } | 307 | } |
| 323 | T::regs().or().modify(|w| w.set_chn21sel(mux)); | 308 | T::regs().or().modify(|w| w.set_chn21sel(mux)); |
| 324 | Dac {} | 309 | super::Dac {} |
| 325 | } | ||
| 326 | |||
| 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 | } | 310 | } |
| 338 | 311 | ||
| 339 | /// Set the ADC resolution. | 312 | /// Set the ADC resolution. |
| 340 | pub fn set_resolution(&mut self, resolution: Resolution) { | 313 | pub fn set_resolution_adc4(&mut self, resolution: Resolution) { |
| 341 | T::regs().cfgr1().modify(|w| w.set_res(resolution.into())); | 314 | T::regs().cfgr1().modify(|w| w.set_res(resolution.into())); |
| 342 | } | 315 | } |
| 343 | 316 | ||
| 344 | /// Set hardware averaging. | 317 | /// Set hardware averaging. |
| 345 | #[cfg(stm32u5)] | 318 | #[cfg(stm32u5)] |
| 346 | pub fn set_averaging(&mut self, averaging: Averaging) { | 319 | pub fn set_averaging_adc4(&mut self, averaging: Averaging) { |
| 347 | let (enable, samples, right_shift) = match averaging { | 320 | let (enable, samples, right_shift) = match averaging { |
| 348 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, 0), | 321 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, 0), |
| 349 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, 1), | 322 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, 1), |
| @@ -363,7 +336,7 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 363 | }) | 336 | }) |
| 364 | } | 337 | } |
| 365 | #[cfg(stm32wba)] | 338 | #[cfg(stm32wba)] |
| 366 | pub fn set_averaging(&mut self, averaging: Averaging) { | 339 | pub fn set_averaging_adc4(&mut self, averaging: Averaging) { |
| 367 | let (enable, samples, right_shift) = match averaging { | 340 | let (enable, samples, right_shift) = match averaging { |
| 368 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT0), | 341 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT0), |
| 369 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT1), | 342 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT1), |
| @@ -382,164 +355,4 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 382 | w.set_ovse(enable) | 355 | w.set_ovse(enable) |
| 383 | }) | 356 | }) |
| 384 | } | 357 | } |
| 385 | |||
| 386 | /// Read an ADC channel. | ||
| 387 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 388 | channel.setup(); | ||
| 389 | |||
| 390 | // Select channel | ||
| 391 | #[cfg(stm32wba)] | ||
| 392 | { | ||
| 393 | T::regs().chselr().write_value(Chselr(0_u32)); | ||
| 394 | T::regs().chselr().modify(|w| { | ||
| 395 | w.set_chsel0(channel.channel() as usize, true); | ||
| 396 | }); | ||
| 397 | } | ||
| 398 | #[cfg(stm32u5)] | ||
| 399 | { | ||
| 400 | T::regs().chselrmod0().write_value(Chselr(0_u32)); | ||
| 401 | T::regs().chselrmod0().modify(|w| { | ||
| 402 | w.set_chsel(channel.channel() as usize, true); | ||
| 403 | }); | ||
| 404 | } | ||
| 405 | |||
| 406 | // Reset interrupts | ||
| 407 | T::regs().isr().modify(|reg| { | ||
| 408 | reg.set_eos(true); | ||
| 409 | reg.set_eoc(true); | ||
| 410 | }); | ||
| 411 | |||
| 412 | // Start conversion | ||
| 413 | T::regs().cr().modify(|reg| { | ||
| 414 | reg.set_adstart(true); | ||
| 415 | }); | ||
| 416 | |||
| 417 | while !T::regs().isr().read().eos() { | ||
| 418 | // spin | ||
| 419 | } | ||
| 420 | |||
| 421 | T::regs().dr().read().0 as u16 | ||
| 422 | } | ||
| 423 | |||
| 424 | /// Read one or multiple ADC channels using DMA. | ||
| 425 | /// | ||
| 426 | /// `sequence` iterator and `readings` must have the same length. | ||
| 427 | /// The channels in `sequence` must be in ascending order. | ||
| 428 | /// | ||
| 429 | /// Example | ||
| 430 | /// ```rust,ignore | ||
| 431 | /// use embassy_stm32::adc::adc4; | ||
| 432 | /// use embassy_stm32::adc::AdcChannel; | ||
| 433 | /// | ||
| 434 | /// let mut adc4 = adc4::Adc4::new(p.ADC4); | ||
| 435 | /// let mut adc4_pin1 = p.PC1; | ||
| 436 | /// let mut adc4_pin2 = p.PC0; | ||
| 437 | /// let mut.into()d41 = adc4_pin1.into(); | ||
| 438 | /// let mut.into()d42 = adc4_pin2.into(); | ||
| 439 | /// let mut measurements = [0u16; 2]; | ||
| 440 | /// // not that the channels must be in ascending order | ||
| 441 | /// adc4.read( | ||
| 442 | /// &mut p.GPDMA1_CH1, | ||
| 443 | /// [ | ||
| 444 | /// &mut.into()d42, | ||
| 445 | /// &mut.into()d41, | ||
| 446 | /// ] | ||
| 447 | /// .into_iter(), | ||
| 448 | /// &mut measurements, | ||
| 449 | /// ).await.unwrap(); | ||
| 450 | /// ``` | ||
| 451 | pub async fn read( | ||
| 452 | &mut self, | ||
| 453 | rx_dma: Peri<'_, impl RxDma4<T>>, | ||
| 454 | sequence: impl ExactSizeIterator<Item = &mut AnyAdcChannel<T>>, | ||
| 455 | readings: &mut [u16], | ||
| 456 | ) -> Result<(), Adc4Error> { | ||
| 457 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 458 | assert!( | ||
| 459 | sequence.len() == readings.len(), | ||
| 460 | "Sequence length must be equal to readings length" | ||
| 461 | ); | ||
| 462 | |||
| 463 | // Ensure no conversions are ongoing | ||
| 464 | Self::cancel_conversions(); | ||
| 465 | |||
| 466 | T::regs().isr().modify(|reg| { | ||
| 467 | reg.set_ovr(true); | ||
| 468 | reg.set_eos(true); | ||
| 469 | reg.set_eoc(true); | ||
| 470 | }); | ||
| 471 | |||
| 472 | T::regs().cfgr1().modify(|reg| { | ||
| 473 | reg.set_dmaen(true); | ||
| 474 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 475 | #[cfg(stm32u5)] | ||
| 476 | reg.set_chselrmod(false); | ||
| 477 | #[cfg(stm32wba)] | ||
| 478 | reg.set_chselrmod(Chselrmod::ENABLE_INPUT) | ||
| 479 | }); | ||
| 480 | |||
| 481 | // Verify and activate sequence | ||
| 482 | let mut prev_channel: i16 = -1; | ||
| 483 | #[cfg(stm32wba)] | ||
| 484 | T::regs().chselr().write_value(Chselr(0_u32)); | ||
| 485 | #[cfg(stm32u5)] | ||
| 486 | T::regs().chselrmod0().write_value(Chselr(0_u32)); | ||
| 487 | for channel in sequence { | ||
| 488 | let channel_num = channel.channel; | ||
| 489 | if channel_num as i16 <= prev_channel { | ||
| 490 | return Err(Adc4Error::InvalidSequence); | ||
| 491 | }; | ||
| 492 | prev_channel = channel_num as i16; | ||
| 493 | |||
| 494 | #[cfg(stm32wba)] | ||
| 495 | T::regs().chselr().modify(|w| { | ||
| 496 | w.set_chsel0(channel.channel as usize, true); | ||
| 497 | }); | ||
| 498 | #[cfg(stm32u5)] | ||
| 499 | T::regs().chselrmod0().modify(|w| { | ||
| 500 | w.set_chsel(channel.channel as usize, true); | ||
| 501 | }); | ||
| 502 | } | ||
| 503 | |||
| 504 | let request = rx_dma.request(); | ||
| 505 | let transfer = unsafe { | ||
| 506 | Transfer::new_read( | ||
| 507 | rx_dma, | ||
| 508 | request, | ||
| 509 | T::regs().dr().as_ptr() as *mut u16, | ||
| 510 | readings, | ||
| 511 | Default::default(), | ||
| 512 | ) | ||
| 513 | }; | ||
| 514 | |||
| 515 | // Start conversion | ||
| 516 | T::regs().cr().modify(|reg| { | ||
| 517 | reg.set_adstart(true); | ||
| 518 | }); | ||
| 519 | |||
| 520 | transfer.await; | ||
| 521 | |||
| 522 | // Ensure conversions are finished. | ||
| 523 | Self::cancel_conversions(); | ||
| 524 | |||
| 525 | // Reset configuration. | ||
| 526 | T::regs().cfgr1().modify(|reg| { | ||
| 527 | reg.set_dmaen(false); | ||
| 528 | }); | ||
| 529 | |||
| 530 | if T::regs().isr().read().ovr() { | ||
| 531 | Err(Adc4Error::DMAError) | ||
| 532 | } else { | ||
| 533 | Ok(()) | ||
| 534 | } | ||
| 535 | } | ||
| 536 | |||
| 537 | fn cancel_conversions() { | ||
| 538 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 539 | T::regs().cr().modify(|reg| { | ||
| 540 | reg.set_adstp(true); | ||
| 541 | }); | ||
| 542 | while T::regs().cr().read().adstart() {} | ||
| 543 | } | ||
| 544 | } | ||
| 545 | } | 358 | } |
diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index f2837a8f1..2f0f326af 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs | |||
| @@ -1,14 +1,12 @@ | |||
| 1 | use pac::adc::vals::Scandir; | ||
| 2 | #[allow(unused)] | 1 | #[allow(unused)] |
| 3 | use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr}; | 2 | use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr}; |
| 4 | use pac::adccommon::vals::Presc; | 3 | use pac::adccommon::vals::Presc; |
| 4 | use stm32_metapac::adc::vals::{SampleTime, Scandir}; | ||
| 5 | 5 | ||
| 6 | use super::{ | 6 | use super::{Adc, Instance, Resolution, blocking_delay_us}; |
| 7 | blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, | 7 | use crate::adc::{AdcRegs, ConversionMode}; |
| 8 | }; | ||
| 9 | use crate::dma::Transfer; | ||
| 10 | use crate::time::Hertz; | 8 | use crate::time::Hertz; |
| 11 | use crate::{pac, rcc, Peri}; | 9 | use crate::{Peri, pac, rcc}; |
| 12 | 10 | ||
| 13 | /// Default VREF voltage used for sample conversion to millivolts. | 11 | /// Default VREF voltage used for sample conversion to millivolts. |
| 14 | pub const VREF_DEFAULT_MV: u32 = 3300; | 12 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| @@ -19,189 +17,198 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(25); | |||
| 19 | 17 | ||
| 20 | const TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US: u32 = 20; | 18 | const TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US: u32 = 20; |
| 21 | 19 | ||
| 22 | const TEMP_CHANNEL: u8 = 9; | ||
| 23 | const VREF_CHANNEL: u8 = 10; | ||
| 24 | |||
| 25 | const NUM_HW_CHANNELS: u8 = 22; | ||
| 26 | const CHSELR_SQ_SIZE: usize = 8; | 20 | const CHSELR_SQ_SIZE: usize = 8; |
| 27 | const CHSELR_SQ_MAX_CHANNEL: u8 = 14; | 21 | const CHSELR_SQ_MAX_CHANNEL: u8 = 14; |
| 28 | const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111; | 22 | const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111; |
| 29 | 23 | ||
| 30 | // NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, | 24 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 31 | // this currently cannot be modeled with stm32-data, | 25 | 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 | } | 26 | } |
| 41 | 27 | ||
| 42 | /// Internal temperature channel. | 28 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 43 | pub struct Temperature; | 29 | 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 | } | 30 | } |
| 50 | 31 | ||
| 51 | #[derive(Copy, Clone, Debug)] | 32 | fn from_ker_ck(frequency: Hertz) -> Presc { |
| 52 | pub enum Prescaler { | 33 | let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); |
| 53 | NotDivided, | 34 | match raw_prescaler { |
| 54 | DividedBy2, | 35 | 0 => Presc::DIV1, |
| 55 | DividedBy4, | 36 | 1 => Presc::DIV2, |
| 56 | DividedBy6, | 37 | 2..=3 => Presc::DIV4, |
| 57 | DividedBy8, | 38 | 4..=5 => Presc::DIV6, |
| 58 | DividedBy10, | 39 | 6..=7 => Presc::DIV8, |
| 59 | DividedBy12, | 40 | 8..=9 => Presc::DIV10, |
| 60 | DividedBy16, | 41 | 10..=11 => Presc::DIV12, |
| 61 | DividedBy32, | 42 | _ => unimplemented!(), |
| 62 | DividedBy64, | 43 | } |
| 63 | DividedBy128, | ||
| 64 | DividedBy256, | ||
| 65 | } | 44 | } |
| 66 | 45 | ||
| 67 | impl Prescaler { | 46 | impl AdcRegs for crate::pac::adc::Adc { |
| 68 | fn from_ker_ck(frequency: Hertz) -> Self { | 47 | fn data(&self) -> *mut u16 { |
| 69 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | 48 | crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16 |
| 70 | match raw_prescaler { | ||
| 71 | 0 => Self::NotDivided, | ||
| 72 | 1 => Self::DividedBy2, | ||
| 73 | 2..=3 => Self::DividedBy4, | ||
| 74 | 4..=5 => Self::DividedBy6, | ||
| 75 | 6..=7 => Self::DividedBy8, | ||
| 76 | 8..=9 => Self::DividedBy10, | ||
| 77 | 10..=11 => Self::DividedBy12, | ||
| 78 | _ => unimplemented!(), | ||
| 79 | } | ||
| 80 | } | 49 | } |
| 81 | 50 | ||
| 82 | #[allow(unused)] | 51 | fn enable(&self) { |
| 83 | fn divisor(&self) -> u32 { | 52 | self.isr().modify(|w| w.set_adrdy(true)); |
| 84 | match self { | 53 | self.cr().modify(|w| w.set_aden(true)); |
| 85 | Prescaler::NotDivided => 1, | 54 | // ADRDY is "ADC ready". Wait until it will be True. |
| 86 | Prescaler::DividedBy2 => 2, | 55 | while !self.isr().read().adrdy() {} |
| 87 | Prescaler::DividedBy4 => 4, | 56 | } |
| 88 | Prescaler::DividedBy6 => 6, | 57 | |
| 89 | Prescaler::DividedBy8 => 8, | 58 | fn start(&self) { |
| 90 | Prescaler::DividedBy10 => 10, | 59 | // Start conversion |
| 91 | Prescaler::DividedBy12 => 12, | 60 | self.cr().modify(|reg| { |
| 92 | Prescaler::DividedBy16 => 16, | 61 | reg.set_adstart(true); |
| 93 | Prescaler::DividedBy32 => 32, | 62 | }); |
| 94 | Prescaler::DividedBy64 => 64, | ||
| 95 | Prescaler::DividedBy128 => 128, | ||
| 96 | Prescaler::DividedBy256 => 256, | ||
| 97 | } | ||
| 98 | } | 63 | } |
| 99 | 64 | ||
| 100 | fn presc(&self) -> Presc { | 65 | fn stop(&self) { |
| 101 | match self { | 66 | if self.cr().read().adstart() && !self.cr().read().addis() { |
| 102 | Prescaler::NotDivided => Presc::DIV1, | 67 | self.cr().modify(|reg| { |
| 103 | Prescaler::DividedBy2 => Presc::DIV2, | 68 | reg.set_adstp(Adstp::STOP); |
| 104 | Prescaler::DividedBy4 => Presc::DIV4, | 69 | }); |
| 105 | Prescaler::DividedBy6 => Presc::DIV6, | 70 | while self.cr().read().adstart() {} |
| 106 | Prescaler::DividedBy8 => Presc::DIV8, | ||
| 107 | Prescaler::DividedBy10 => Presc::DIV10, | ||
| 108 | Prescaler::DividedBy12 => Presc::DIV12, | ||
| 109 | Prescaler::DividedBy16 => Presc::DIV16, | ||
| 110 | Prescaler::DividedBy32 => Presc::DIV32, | ||
| 111 | Prescaler::DividedBy64 => Presc::DIV64, | ||
| 112 | Prescaler::DividedBy128 => Presc::DIV128, | ||
| 113 | Prescaler::DividedBy256 => Presc::DIV256, | ||
| 114 | } | 71 | } |
| 72 | |||
| 73 | // Reset configuration. | ||
| 74 | self.cfgr1().modify(|reg| { | ||
| 75 | reg.set_cont(false); | ||
| 76 | reg.set_dmacfg(Dmacfg::from_bits(0)); | ||
| 77 | reg.set_dmaen(false); | ||
| 78 | }); | ||
| 115 | } | 79 | } |
| 116 | } | ||
| 117 | 80 | ||
| 118 | #[cfg(feature = "defmt")] | 81 | fn configure_dma(&self, conversion_mode: super::ConversionMode) { |
| 119 | impl<'a> defmt::Format for Prescaler { | 82 | match conversion_mode { |
| 120 | fn format(&self, fmt: defmt::Formatter) { | 83 | ConversionMode::Singular => { |
| 121 | match self { | 84 | // Enable overrun control, so no new DMA requests will be generated until |
| 122 | Prescaler::NotDivided => defmt::write!(fmt, "Prescaler::NotDivided"), | 85 | // previous DR values is read. |
| 123 | Prescaler::DividedBy2 => defmt::write!(fmt, "Prescaler::DividedBy2"), | 86 | self.isr().modify(|reg| { |
| 124 | Prescaler::DividedBy4 => defmt::write!(fmt, "Prescaler::DividedBy4"), | 87 | reg.set_ovr(true); |
| 125 | Prescaler::DividedBy6 => defmt::write!(fmt, "Prescaler::DividedBy6"), | 88 | }); |
| 126 | Prescaler::DividedBy8 => defmt::write!(fmt, "Prescaler::DividedBy8"), | 89 | |
| 127 | Prescaler::DividedBy10 => defmt::write!(fmt, "Prescaler::DividedBy10"), | 90 | // Set continuous mode with oneshot dma. |
| 128 | Prescaler::DividedBy12 => defmt::write!(fmt, "Prescaler::DividedBy12"), | 91 | self.cfgr1().modify(|reg| { |
| 129 | Prescaler::DividedBy16 => defmt::write!(fmt, "Prescaler::DividedBy16"), | 92 | reg.set_discen(false); |
| 130 | Prescaler::DividedBy32 => defmt::write!(fmt, "Prescaler::DividedBy32"), | 93 | reg.set_cont(true); |
| 131 | Prescaler::DividedBy64 => defmt::write!(fmt, "Prescaler::DividedBy64"), | 94 | reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT); |
| 132 | Prescaler::DividedBy128 => defmt::write!(fmt, "Prescaler::DividedBy128"), | 95 | reg.set_dmaen(true); |
| 133 | Prescaler::DividedBy256 => defmt::write!(fmt, "Prescaler::DividedBy256"), | 96 | reg.set_ovrmod(Ovrmod::PRESERVE); |
| 97 | }); | ||
| 98 | } | ||
| 134 | } | 99 | } |
| 135 | } | 100 | } |
| 136 | } | ||
| 137 | 101 | ||
| 138 | /// Number of samples used for averaging. | 102 | fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>) { |
| 139 | /// TODO: Implement hardware averaging setting. | 103 | let mut needs_hw = sequence.len() == 1 || sequence.len() > CHSELR_SQ_SIZE; |
| 140 | #[allow(unused)] | 104 | let mut is_ordered_up = true; |
| 141 | #[derive(Copy, Clone, Debug)] | 105 | let mut is_ordered_down = true; |
| 142 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 143 | pub enum Averaging { | ||
| 144 | Disabled, | ||
| 145 | Samples2, | ||
| 146 | Samples4, | ||
| 147 | Samples8, | ||
| 148 | Samples16, | ||
| 149 | Samples32, | ||
| 150 | Samples64, | ||
| 151 | Samples128, | ||
| 152 | Samples256, | ||
| 153 | Samples512, | ||
| 154 | Samples1024, | ||
| 155 | } | ||
| 156 | 106 | ||
| 157 | impl<'d, T: Instance> Adc<'d, T> { | 107 | let sequence_len = sequence.len(); |
| 158 | /// Create a new ADC driver. | 108 | let mut hw_channel_selection: u32 = 0; |
| 159 | pub fn new(adc: Peri<'d, T>, sample_time: SampleTime, resolution: Resolution) -> Self { | 109 | let mut last_channel: u8 = 0; |
| 160 | rcc::enable_and_reset::<T>(); | 110 | let mut sample_time: Self::SampleTime = SampleTime::CYCLES2_5; |
| 161 | 111 | ||
| 162 | T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); | 112 | self.chselr_sq().write(|w| { |
| 113 | for (i, ((channel, _), _sample_time)) in sequence.enumerate() { | ||
| 114 | assert!( | ||
| 115 | sample_time == _sample_time || i == 0, | ||
| 116 | "C0 only supports one sample time for the sequence." | ||
| 117 | ); | ||
| 163 | 118 | ||
| 164 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 119 | sample_time = _sample_time; |
| 165 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 120 | needs_hw = needs_hw || channel > CHSELR_SQ_MAX_CHANNEL; |
| 121 | is_ordered_up = is_ordered_up && (channel > last_channel || i == 0); | ||
| 122 | is_ordered_down = is_ordered_down && (channel < last_channel || i == 0); | ||
| 123 | hw_channel_selection += 1 << channel; | ||
| 124 | last_channel = channel; | ||
| 166 | 125 | ||
| 167 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 126 | if !needs_hw { |
| 168 | debug!("ADC frequency set to {}", frequency); | 127 | w.set_sq(i, channel); |
| 128 | } | ||
| 129 | } | ||
| 169 | 130 | ||
| 170 | if frequency > MAX_ADC_CLK_FREQ { | 131 | for i in sequence_len..CHSELR_SQ_SIZE { |
| 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 ); | 132 | w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER); |
| 133 | } | ||
| 134 | }); | ||
| 135 | |||
| 136 | if needs_hw { | ||
| 137 | assert!( | ||
| 138 | sequence_len <= CHSELR_SQ_SIZE || is_ordered_up || is_ordered_down, | ||
| 139 | "Sequencer is required because of unordered channels, but read set cannot be more than {} in size.", | ||
| 140 | CHSELR_SQ_SIZE | ||
| 141 | ); | ||
| 142 | assert!( | ||
| 143 | sequence_len > CHSELR_SQ_SIZE || is_ordered_up || is_ordered_down, | ||
| 144 | "Sequencer is required because of unordered channels, but only support HW channels smaller than {}.", | ||
| 145 | CHSELR_SQ_MAX_CHANNEL | ||
| 146 | ); | ||
| 147 | |||
| 148 | // Set required channels for multi-convert. | ||
| 149 | unsafe { (self.chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } | ||
| 172 | } | 150 | } |
| 173 | 151 | ||
| 174 | let mut s = Self { | 152 | self.smpr().modify(|w| { |
| 175 | adc, | 153 | w.smpsel(0); |
| 176 | sample_time: SampleTime::from_bits(0), | 154 | w.set_smp1(sample_time); |
| 177 | }; | 155 | }); |
| 178 | 156 | ||
| 179 | s.power_up(); | 157 | self.cfgr1().modify(|reg| { |
| 158 | reg.set_chselrmod(!needs_hw); | ||
| 159 | reg.set_align(Align::RIGHT); | ||
| 160 | reg.set_scandir(if is_ordered_up { Scandir::UP } else { Scandir::BACK }); | ||
| 161 | }); | ||
| 180 | 162 | ||
| 181 | s.set_resolution(resolution); | 163 | // Trigger and wait for the channel selection procedure to complete. |
| 164 | self.isr().modify(|w| w.set_ccrdy(false)); | ||
| 165 | while !self.isr().read().ccrdy() {} | ||
| 166 | } | ||
| 182 | 167 | ||
| 183 | s.calibrate(); | 168 | fn convert(&self) { |
| 169 | // Set single conversion mode. | ||
| 170 | self.cfgr1().modify(|w| w.set_cont(false)); | ||
| 184 | 171 | ||
| 185 | s.enable(); | 172 | // Start conversion |
| 173 | self.cr().modify(|reg| { | ||
| 174 | reg.set_adstart(true); | ||
| 175 | }); | ||
| 186 | 176 | ||
| 187 | s.configure_default(); | 177 | // Waiting for End Of Conversion (EOC). |
| 178 | while !self.isr().read().eoc() {} | ||
| 179 | } | ||
| 180 | } | ||
| 188 | 181 | ||
| 189 | s.set_sample_time_all_channels(sample_time); | 182 | impl<'d, T: Instance<Regs = crate::pac::adc::Adc>> Adc<'d, T> { |
| 183 | /// Create a new ADC driver. | ||
| 184 | pub fn new(adc: Peri<'d, T>, resolution: Resolution) -> Self { | ||
| 185 | rcc::enable_and_reset::<T>(); | ||
| 190 | 186 | ||
| 191 | s | 187 | T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); |
| 192 | } | 188 | |
| 189 | let prescaler = from_ker_ck(T::frequency()); | ||
| 190 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); | ||
| 191 | |||
| 192 | let frequency = T::frequency() / prescaler; | ||
| 193 | debug!("ADC frequency set to {}", frequency); | ||
| 194 | |||
| 195 | if frequency > MAX_ADC_CLK_FREQ { | ||
| 196 | panic!( | ||
| 197 | "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", | ||
| 198 | MAX_ADC_CLK_FREQ.0 / 1_000_000 | ||
| 199 | ); | ||
| 200 | } | ||
| 193 | 201 | ||
| 194 | fn power_up(&mut self) { | ||
| 195 | T::regs().cr().modify(|reg| { | 202 | T::regs().cr().modify(|reg| { |
| 196 | reg.set_advregen(true); | 203 | reg.set_advregen(true); |
| 197 | }); | 204 | }); |
| 198 | 205 | ||
| 199 | // "The software must wait for the ADC voltage regulator startup time." | 206 | // "The software must wait for the ADC voltage regulator startup time." |
| 200 | // See datasheet for the value. | 207 | // See datasheet for the value. |
| 201 | blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US + 1); | 208 | blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US as u64 + 1); |
| 202 | } | 209 | |
| 210 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); | ||
| 203 | 211 | ||
| 204 | fn calibrate(&mut self) { | ||
| 205 | // We have to make sure AUTOFF is OFF, but keep its value after calibration. | 212 | // We have to make sure AUTOFF is OFF, but keep its value after calibration. |
| 206 | let autoff_value = T::regs().cfgr1().read().autoff(); | 213 | let autoff_value = T::regs().cfgr1().read().autoff(); |
| 207 | T::regs().cfgr1().modify(|w| w.set_autoff(false)); | 214 | T::regs().cfgr1().modify(|w| w.set_autoff(false)); |
| @@ -215,255 +222,35 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 215 | debug!("ADC calibration value: {}.", T::regs().dr().read().data()); | 222 | debug!("ADC calibration value: {}.", T::regs().dr().read().data()); |
| 216 | 223 | ||
| 217 | T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); | 224 | T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); |
| 218 | } | ||
| 219 | 225 | ||
| 220 | fn enable(&mut self) { | 226 | T::regs().enable(); |
| 221 | T::regs().isr().modify(|w| w.set_adrdy(true)); | ||
| 222 | T::regs().cr().modify(|w| w.set_aden(true)); | ||
| 223 | // ADRDY is "ADC ready". Wait until it will be True. | ||
| 224 | while !T::regs().isr().read().adrdy() {} | ||
| 225 | } | ||
| 226 | 227 | ||
| 227 | fn configure_default(&mut self) { | ||
| 228 | // single conversion mode, software trigger | 228 | // single conversion mode, software trigger |
| 229 | T::regs().cfgr1().modify(|w| { | 229 | T::regs().cfgr1().modify(|w| { |
| 230 | w.set_cont(false); | 230 | w.set_cont(false); |
| 231 | w.set_exten(Exten::DISABLED); | 231 | w.set_exten(Exten::DISABLED); |
| 232 | w.set_align(Align::RIGHT); | 232 | w.set_align(Align::RIGHT); |
| 233 | }); | 233 | }); |
| 234 | |||
| 235 | Self { adc } | ||
| 234 | } | 236 | } |
| 235 | 237 | ||
| 236 | /// Enable reading the voltage reference internal channel. | 238 | /// Enable reading the voltage reference internal channel. |
| 237 | pub fn enable_vrefint(&self) -> VrefInt { | 239 | pub fn enable_vrefint(&self) -> super::VrefInt { |
| 238 | T::common_regs().ccr().modify(|reg| { | 240 | T::common_regs().ccr().modify(|reg| { |
| 239 | reg.set_vrefen(true); | 241 | reg.set_vrefen(true); |
| 240 | }); | 242 | }); |
| 241 | 243 | ||
| 242 | VrefInt {} | 244 | super::VrefInt {} |
| 243 | } | 245 | } |
| 244 | 246 | ||
| 245 | /// Enable reading the temperature internal channel. | 247 | /// Enable reading the temperature internal channel. |
| 246 | pub fn enable_temperature(&self) -> Temperature { | 248 | 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!"); | 249 | debug!("Ensure that sample time is set to more than temperature sensor T_start from the datasheet!"); |
| 248 | T::common_regs().ccr().modify(|reg| { | 250 | T::common_regs().ccr().modify(|reg| { |
| 249 | reg.set_tsen(true); | 251 | reg.set_tsen(true); |
| 250 | }); | 252 | }); |
| 251 | 253 | ||
| 252 | Temperature {} | 254 | super::Temperature {} |
| 253 | } | ||
| 254 | |||
| 255 | /// Set the ADC sample time. | ||
| 256 | /// Shall only be called when ADC is not converting. | ||
| 257 | 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. | ||
| 261 | T::regs().smpr().modify(|w| { | ||
| 262 | w.smpsel(0); | ||
| 263 | w.set_smp1(sample_time); | ||
| 264 | }); | ||
| 265 | } | ||
| 266 | |||
| 267 | /// Set the ADC resolution. | ||
| 268 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 269 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); | ||
| 270 | } | ||
| 271 | |||
| 272 | /// Perform a single conversion. | ||
| 273 | fn convert(&mut self) -> u16 { | ||
| 274 | // Set single conversion mode. | ||
| 275 | T::regs().cfgr1().modify(|w| w.set_cont(false)); | ||
| 276 | |||
| 277 | // Start conversion | ||
| 278 | T::regs().cr().modify(|reg| { | ||
| 279 | reg.set_adstart(true); | ||
| 280 | }); | ||
| 281 | |||
| 282 | // Waiting for End Of Conversion (EOC). | ||
| 283 | while !T::regs().isr().read().eoc() {} | ||
| 284 | |||
| 285 | T::regs().dr().read().data() as u16 | ||
| 286 | } | ||
| 287 | |||
| 288 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 289 | Self::configure_channel(channel); | ||
| 290 | T::regs().cfgr1().write(|reg| { | ||
| 291 | reg.set_chselrmod(false); | ||
| 292 | reg.set_align(Align::RIGHT); | ||
| 293 | }); | ||
| 294 | self.convert() | ||
| 295 | } | ||
| 296 | |||
| 297 | fn setup_channel_sequencer<'a>(channel_sequence: impl ExactSizeIterator<Item = &'a mut AnyAdcChannel<T>>) { | ||
| 298 | assert!( | ||
| 299 | channel_sequence.len() <= CHSELR_SQ_SIZE, | ||
| 300 | "Seqenced read set cannot be more than {} in size.", | ||
| 301 | CHSELR_SQ_SIZE | ||
| 302 | ); | ||
| 303 | let mut last_sq_set: usize = 0; | ||
| 304 | T::regs().chselr_sq().write(|w| { | ||
| 305 | for (i, channel) in channel_sequence.enumerate() { | ||
| 306 | assert!( | ||
| 307 | channel.channel() <= CHSELR_SQ_MAX_CHANNEL, | ||
| 308 | "Sequencer only support HW channels smaller than {}.", | ||
| 309 | CHSELR_SQ_MAX_CHANNEL | ||
| 310 | ); | ||
| 311 | w.set_sq(i, channel.channel()); | ||
| 312 | last_sq_set = i; | ||
| 313 | } | ||
| 314 | |||
| 315 | for i in (last_sq_set + 1)..CHSELR_SQ_SIZE { | ||
| 316 | w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER); | ||
| 317 | } | ||
| 318 | }); | ||
| 319 | |||
| 320 | Self::apply_channel_conf() | ||
| 321 | } | ||
| 322 | |||
| 323 | async fn dma_convert(&mut self, rx_dma: Peri<'_, impl RxDma<T>>, readings: &mut [u16]) { | ||
| 324 | // Enable overrun control, so no new DMA requests will be generated until | ||
| 325 | // previous DR values is read. | ||
| 326 | T::regs().isr().modify(|reg| { | ||
| 327 | reg.set_ovr(true); | ||
| 328 | }); | ||
| 329 | |||
| 330 | // Set continuous mode with oneshot dma. | ||
| 331 | T::regs().cfgr1().modify(|reg| { | ||
| 332 | reg.set_discen(false); | ||
| 333 | reg.set_cont(true); | ||
| 334 | reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT); | ||
| 335 | reg.set_dmaen(true); | ||
| 336 | reg.set_ovrmod(Ovrmod::PRESERVE); | ||
| 337 | }); | ||
| 338 | |||
| 339 | let request = rx_dma.request(); | ||
| 340 | let transfer = unsafe { | ||
| 341 | Transfer::new_read( | ||
| 342 | rx_dma, | ||
| 343 | request, | ||
| 344 | T::regs().dr().as_ptr() as *mut u16, | ||
| 345 | readings, | ||
| 346 | Default::default(), | ||
| 347 | ) | ||
| 348 | }; | ||
| 349 | |||
| 350 | // Start conversion. | ||
| 351 | T::regs().cr().modify(|reg| { | ||
| 352 | reg.set_adstart(true); | ||
| 353 | }); | ||
| 354 | |||
| 355 | // Wait for conversion sequence to finish. | ||
| 356 | transfer.await; | ||
| 357 | |||
| 358 | // Ensure conversions are finished. | ||
| 359 | Self::cancel_conversions(); | ||
| 360 | |||
| 361 | // Reset configuration. | ||
| 362 | T::regs().cfgr1().modify(|reg| { | ||
| 363 | reg.set_cont(false); | ||
| 364 | reg.set_dmacfg(Dmacfg::from_bits(0)); | ||
| 365 | reg.set_dmaen(false); | ||
| 366 | }); | ||
| 367 | } | ||
| 368 | |||
| 369 | /// Read one or multiple ADC channels using DMA in hardware order. | ||
| 370 | /// Readings will be ordered based on **hardware** ADC channel number and `scandir` setting. | ||
| 371 | /// Readings won't be in the same order as in the `set`! | ||
| 372 | /// | ||
| 373 | /// In STM32C0, channels bigger than 14 cannot be read using sequencer, so you have to use | ||
| 374 | /// either blocking reads or use the mechanism to read in HW order (CHSELRMOD=0). | ||
| 375 | /// TODO(chudsaviet): externalize generic code and merge with read(). | ||
| 376 | pub async fn read_in_hw_order( | ||
| 377 | &mut self, | ||
| 378 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 379 | hw_channel_selection: u32, | ||
| 380 | scandir: Scandir, | ||
| 381 | readings: &mut [u16], | ||
| 382 | ) { | ||
| 383 | assert!( | ||
| 384 | hw_channel_selection != 0, | ||
| 385 | "Some bits in `hw_channel_selection` shall be set." | ||
| 386 | ); | ||
| 387 | assert!( | ||
| 388 | (hw_channel_selection >> NUM_HW_CHANNELS) == 0, | ||
| 389 | "STM32C0 only have {} ADC channels. `hw_channel_selection` cannot have bits higher than this number set.", | ||
| 390 | NUM_HW_CHANNELS | ||
| 391 | ); | ||
| 392 | // To check for correct readings slice size, we shall solve Hamming weight problem, | ||
| 393 | // which is either slow or memory consuming. | ||
| 394 | // Since we have limited resources, we don't do it here. | ||
| 395 | // Not doing this have a great potential for a bug through. | ||
| 396 | |||
| 397 | // Ensure no conversions are ongoing. | ||
| 398 | Self::cancel_conversions(); | ||
| 399 | |||
| 400 | T::regs().cfgr1().modify(|reg| { | ||
| 401 | reg.set_chselrmod(false); | ||
| 402 | reg.set_scandir(scandir); | ||
| 403 | reg.set_align(Align::RIGHT); | ||
| 404 | }); | ||
| 405 | |||
| 406 | // Set required channels for multi-convert. | ||
| 407 | unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } | ||
| 408 | |||
| 409 | Self::apply_channel_conf(); | ||
| 410 | |||
| 411 | self.dma_convert(rx_dma, readings).await | ||
| 412 | } | ||
| 413 | |||
| 414 | // Read ADC channels in specified order using DMA (CHSELRMOD = 1). | ||
| 415 | // In STM32C0, only lower 14 ADC channels can be read this way. | ||
| 416 | // For other channels, use `read_in_hw_order()` or blocking read. | ||
| 417 | pub async fn read( | ||
| 418 | &mut self, | ||
| 419 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 420 | channel_sequence: impl ExactSizeIterator<Item = &mut AnyAdcChannel<T>>, | ||
| 421 | readings: &mut [u16], | ||
| 422 | ) { | ||
| 423 | assert!( | ||
| 424 | channel_sequence.len() != 0, | ||
| 425 | "Asynchronous read channel sequence cannot be empty." | ||
| 426 | ); | ||
| 427 | assert!( | ||
| 428 | channel_sequence.len() == readings.len(), | ||
| 429 | "Channel sequence length must be equal to readings length." | ||
| 430 | ); | ||
| 431 | |||
| 432 | // Ensure no conversions are ongoing. | ||
| 433 | Self::cancel_conversions(); | ||
| 434 | |||
| 435 | T::regs().cfgr1().modify(|reg| { | ||
| 436 | reg.set_chselrmod(true); | ||
| 437 | reg.set_align(Align::RIGHT); | ||
| 438 | }); | ||
| 439 | |||
| 440 | Self::setup_channel_sequencer(channel_sequence); | ||
| 441 | |||
| 442 | self.dma_convert(rx_dma, readings).await | ||
| 443 | } | ||
| 444 | |||
| 445 | fn configure_channel(channel: &mut impl AdcChannel<T>) { | ||
| 446 | channel.setup(); | ||
| 447 | // write() because we want all other bits to be set to 0. | ||
| 448 | T::regs() | ||
| 449 | .chselr() | ||
| 450 | .write(|w| w.set_chsel(channel.channel().into(), true)); | ||
| 451 | |||
| 452 | Self::apply_channel_conf(); | ||
| 453 | } | ||
| 454 | |||
| 455 | fn apply_channel_conf() { | ||
| 456 | // Trigger and wait for the channel selection procedure to complete. | ||
| 457 | T::regs().isr().modify(|w| w.set_ccrdy(false)); | ||
| 458 | while !T::regs().isr().read().ccrdy() {} | ||
| 459 | } | ||
| 460 | |||
| 461 | fn cancel_conversions() { | ||
| 462 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 463 | T::regs().cr().modify(|reg| { | ||
| 464 | reg.set_adstp(Adstp::STOP); | ||
| 465 | }); | ||
| 466 | while T::regs().cr().read().adstart() {} | ||
| 467 | } | ||
| 468 | } | 255 | } |
| 469 | } | 256 | } |
diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index 3cdc9d8fb..d6c6f480b 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs | |||
| @@ -3,11 +3,11 @@ use core::marker::PhantomData; | |||
| 3 | use core::task::Poll; | 3 | use core::task::Poll; |
| 4 | 4 | ||
| 5 | use super::blocking_delay_us; | 5 | use super::blocking_delay_us; |
| 6 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; | 6 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime, VrefInt}; |
| 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::SealedSpecialConverter<VrefInt> 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::SealedSpecialConverter<super::Temperature> 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> { |
| @@ -51,7 +43,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 51 | 43 | ||
| 52 | // 11.4: Before starting a calibration, the ADC must have been in power-on state (ADON bit = ‘1’) | 44 | // 11.4: Before starting a calibration, the ADC must have been in power-on state (ADON bit = ‘1’) |
| 53 | // for at least two ADC clock cycles. | 45 | // for at least two ADC clock cycles. |
| 54 | blocking_delay_us((1_000_000 * 2) / Self::freq().0 + 1); | 46 | blocking_delay_us((1_000_000 * 2) / Self::freq().0 as u64 + 1); |
| 55 | 47 | ||
| 56 | // Reset calibration | 48 | // Reset calibration |
| 57 | T::regs().cr2().modify(|reg| reg.set_rstcal(true)); | 49 | T::regs().cr2().modify(|reg| reg.set_rstcal(true)); |
| @@ -66,15 +58,12 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 66 | } | 58 | } |
| 67 | 59 | ||
| 68 | // One cycle after calibration | 60 | // One cycle after calibration |
| 69 | blocking_delay_us((1_000_000 * 1) / Self::freq().0 + 1); | 61 | blocking_delay_us((1_000_000 * 1) / Self::freq().0 as u64 + 1); |
| 70 | 62 | ||
| 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..29bfdac97 100644 --- a/embassy-stm32/src/adc/f3.rs +++ b/embassy-stm32/src/adc/f3.rs | |||
| @@ -3,10 +3,10 @@ use core::marker::PhantomData; | |||
| 3 | use core::task::Poll; | 3 | use core::task::Poll; |
| 4 | 4 | ||
| 5 | use super::blocking_delay_us; | 5 | use super::blocking_delay_us; |
| 6 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; | 6 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime, VrefInt}; |
| 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::SealedSpecialConverter<VrefInt> 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::SealedSpecialConverter<super::Temperature> 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> { |
| @@ -77,7 +62,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 77 | while T::regs().cr().read().adcal() {} | 62 | while T::regs().cr().read().adcal() {} |
| 78 | 63 | ||
| 79 | // Wait more than 4 clock cycles after adcal is cleared (RM0364 p. 223). | 64 | // Wait more than 4 clock cycles after adcal is cleared (RM0364 p. 223). |
| 80 | blocking_delay_us((1_000_000 * 4) / Self::freq().0 + 1); | 65 | blocking_delay_us((1_000_000 * 4) / Self::freq().0 as u64 + 1); |
| 81 | 66 | ||
| 82 | // Enable the adc | 67 | // Enable the adc |
| 83 | T::regs().cr().modify(|w| w.set_aden(true)); | 68 | T::regs().cr().modify(|w| w.set_aden(true)); |
| @@ -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..e93ed945f 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs | |||
| @@ -1,175 +1,275 @@ | |||
| 1 | #[cfg(stm32g4)] | ||
| 2 | use pac::adc::regs::Difsel as DifselReg; | ||
| 3 | #[allow(unused)] | ||
| 4 | #[cfg(stm32g4)] | ||
| 5 | pub use pac::adc::vals::{Adcaldif, Adstp, Difsel, Dmacfg, Dmaen, Exten, Rovsm, Trovs}; | ||
| 1 | #[allow(unused)] | 6 | #[allow(unused)] |
| 2 | #[cfg(stm32h7)] | 7 | #[cfg(stm32h7)] |
| 3 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; | 8 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; |
| 4 | #[allow(unused)] | 9 | pub use pac::adccommon::vals::{Dual, Presc}; |
| 5 | #[cfg(stm32g4)] | 10 | |
| 6 | use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs}; | 11 | use super::{ |
| 7 | use pac::adccommon::vals::Presc; | 12 | Adc, AnyAdcChannel, ConversionMode, Instance, RegularConversionMode, Resolution, RxDma, SampleTime, |
| 8 | use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; | 13 | blocking_delay_us, |
| 9 | 14 | }; | |
| 10 | use super::{blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime}; | 15 | use crate::adc::{AdcRegs, BasicAdcRegs, SealedAdcChannel}; |
| 11 | use crate::adc::SealedAdcChannel; | 16 | use crate::pac::adc::regs::{Smpr, Smpr2, Sqr1, Sqr2, Sqr3, Sqr4}; |
| 12 | use crate::dma::Transfer; | ||
| 13 | use crate::time::Hertz; | 17 | use crate::time::Hertz; |
| 14 | use crate::{pac, rcc, Peri}; | 18 | use crate::{Peri, pac, rcc}; |
| 19 | |||
| 20 | mod injected; | ||
| 21 | pub use injected::InjectedAdc; | ||
| 15 | 22 | ||
| 16 | /// Default VREF voltage used for sample conversion to millivolts. | 23 | /// Default VREF voltage used for sample conversion to millivolts. |
| 17 | pub const VREF_DEFAULT_MV: u32 = 3300; | 24 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| 18 | /// VREF voltage used for factory calibration of VREFINTCAL register. | 25 | /// VREF voltage used for factory calibration of VREFINTCAL register. |
| 19 | pub const VREF_CALIB_MV: u32 = 3300; | 26 | pub const VREF_CALIB_MV: u32 = 3300; |
| 20 | 27 | ||
| 28 | const NR_INJECTED_RANKS: usize = 4; | ||
| 29 | |||
| 21 | /// Max single ADC operation clock frequency | 30 | /// Max single ADC operation clock frequency |
| 22 | #[cfg(stm32g4)] | 31 | #[cfg(stm32g4)] |
| 23 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); | 32 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); |
| 24 | #[cfg(stm32h7)] | 33 | #[cfg(stm32h7)] |
| 25 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); | 34 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); |
| 26 | 35 | ||
| 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 | 36 | fn from_ker_ck(frequency: Hertz) -> Presc { |
| 28 | /// Internal voltage reference channel. | 37 | let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); |
| 29 | pub struct VrefInt; | 38 | match raw_prescaler { |
| 30 | impl<T: Instance + VrefChannel> AdcChannel<T> for VrefInt {} | 39 | 0 => Presc::DIV1, |
| 31 | impl<T: Instance + VrefChannel> super::SealedAdcChannel<T> for VrefInt { | 40 | 1 => Presc::DIV2, |
| 32 | fn channel(&self) -> u8 { | 41 | 2..=3 => Presc::DIV4, |
| 33 | T::CHANNEL | 42 | 4..=5 => Presc::DIV6, |
| 43 | 6..=7 => Presc::DIV8, | ||
| 44 | 8..=9 => Presc::DIV10, | ||
| 45 | 10..=11 => Presc::DIV12, | ||
| 46 | _ => unimplemented!(), | ||
| 34 | } | 47 | } |
| 35 | } | 48 | } |
| 36 | 49 | ||
| 37 | /// Internal temperature channel. | 50 | /// ADC configuration |
| 38 | pub struct Temperature; | 51 | #[derive(Default)] |
| 39 | impl<T: Instance + TemperatureChannel> AdcChannel<T> for Temperature {} | 52 | pub struct AdcConfig { |
| 40 | impl<T: Instance + TemperatureChannel> super::SealedAdcChannel<T> for Temperature { | 53 | pub dual_mode: Option<Dual>, |
| 41 | fn channel(&self) -> u8 { | 54 | pub resolution: Option<Resolution>, |
| 42 | T::CHANNEL | 55 | #[cfg(stm32g4)] |
| 43 | } | 56 | pub oversampling_shift: Option<u8>, |
| 57 | #[cfg(stm32g4)] | ||
| 58 | pub oversampling_ratio: Option<u8>, | ||
| 59 | #[cfg(stm32g4)] | ||
| 60 | pub oversampling_mode: Option<(Rovsm, Trovs, bool)>, | ||
| 44 | } | 61 | } |
| 45 | 62 | ||
| 46 | /// Internal battery voltage channel. | 63 | // Trigger source for ADC conversions¨ |
| 47 | pub struct Vbat; | 64 | #[derive(Copy, Clone)] |
| 48 | impl<T: Instance + VBatChannel> AdcChannel<T> for Vbat {} | 65 | pub struct ConversionTrigger { |
| 49 | impl<T: Instance + VBatChannel> super::SealedAdcChannel<T> for Vbat { | 66 | // See Table 166 and 167 in RM0440 Rev 9 for ADC1/2 External triggers |
| 50 | fn channel(&self) -> u8 { | 67 | // Note that Injected and Regular channels uses different mappings |
| 51 | T::CHANNEL | 68 | pub channel: u8, |
| 52 | } | 69 | pub edge: Exten, |
| 53 | } | 70 | } |
| 54 | 71 | ||
| 55 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | 72 | impl super::AdcRegs for crate::pac::adc::Adc { |
| 56 | // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. | 73 | fn data(&self) -> *mut u16 { |
| 57 | #[allow(unused)] | 74 | crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16 |
| 58 | enum Prescaler { | 75 | } |
| 59 | NotDivided, | 76 | |
| 60 | DividedBy2, | 77 | fn enable(&self) { |
| 61 | DividedBy4, | 78 | // Make sure bits are off |
| 62 | DividedBy6, | 79 | while self.cr().read().addis() { |
| 63 | DividedBy8, | 80 | // spin |
| 64 | DividedBy10, | 81 | } |
| 65 | DividedBy12, | 82 | |
| 66 | DividedBy16, | 83 | if !self.cr().read().aden() { |
| 67 | DividedBy32, | 84 | // Enable ADC |
| 68 | DividedBy64, | 85 | self.isr().modify(|reg| { |
| 69 | DividedBy128, | 86 | reg.set_adrdy(true); |
| 70 | DividedBy256, | 87 | }); |
| 71 | } | 88 | self.cr().modify(|reg| { |
| 89 | reg.set_aden(true); | ||
| 90 | }); | ||
| 72 | 91 | ||
| 73 | impl Prescaler { | 92 | while !self.isr().read().adrdy() { |
| 74 | fn from_ker_ck(frequency: Hertz) -> Self { | 93 | // spin |
| 75 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | 94 | } |
| 76 | match raw_prescaler { | ||
| 77 | 0 => Self::NotDivided, | ||
| 78 | 1 => Self::DividedBy2, | ||
| 79 | 2..=3 => Self::DividedBy4, | ||
| 80 | 4..=5 => Self::DividedBy6, | ||
| 81 | 6..=7 => Self::DividedBy8, | ||
| 82 | 8..=9 => Self::DividedBy10, | ||
| 83 | 10..=11 => Self::DividedBy12, | ||
| 84 | _ => unimplemented!(), | ||
| 85 | } | 95 | } |
| 86 | } | 96 | } |
| 87 | 97 | ||
| 88 | fn divisor(&self) -> u32 { | 98 | fn start(&self) { |
| 89 | match self { | 99 | self.cr().modify(|reg| { |
| 90 | Prescaler::NotDivided => 1, | 100 | reg.set_adstart(true); |
| 91 | Prescaler::DividedBy2 => 2, | 101 | }); |
| 92 | Prescaler::DividedBy4 => 4, | 102 | } |
| 93 | Prescaler::DividedBy6 => 6, | 103 | |
| 94 | Prescaler::DividedBy8 => 8, | 104 | fn stop(&self) { |
| 95 | Prescaler::DividedBy10 => 10, | 105 | if self.cr().read().adstart() && !self.cr().read().addis() { |
| 96 | Prescaler::DividedBy12 => 12, | 106 | self.cr().modify(|reg| { |
| 97 | Prescaler::DividedBy16 => 16, | 107 | reg.set_adstp(Adstp::STOP); |
| 98 | Prescaler::DividedBy32 => 32, | 108 | }); |
| 99 | Prescaler::DividedBy64 => 64, | 109 | // The software must poll ADSTART until the bit is reset before assuming the |
| 100 | Prescaler::DividedBy128 => 128, | 110 | // ADC is completely stopped |
| 101 | Prescaler::DividedBy256 => 256, | 111 | while self.cr().read().adstart() {} |
| 102 | } | 112 | } |
| 113 | |||
| 114 | // Disable dma control and continuous conversion, if enabled | ||
| 115 | self.cfgr().modify(|reg| { | ||
| 116 | reg.set_cont(false); | ||
| 117 | reg.set_dmaen(Dmaen::DISABLE); | ||
| 118 | }); | ||
| 103 | } | 119 | } |
| 104 | 120 | ||
| 105 | fn presc(&self) -> Presc { | 121 | fn convert(&self) { |
| 106 | match self { | 122 | self.isr().modify(|reg| { |
| 107 | Prescaler::NotDivided => Presc::DIV1, | 123 | reg.set_eos(true); |
| 108 | Prescaler::DividedBy2 => Presc::DIV2, | 124 | reg.set_eoc(true); |
| 109 | Prescaler::DividedBy4 => Presc::DIV4, | 125 | }); |
| 110 | Prescaler::DividedBy6 => Presc::DIV6, | 126 | |
| 111 | Prescaler::DividedBy8 => Presc::DIV8, | 127 | // Start conversion |
| 112 | Prescaler::DividedBy10 => Presc::DIV10, | 128 | self.cr().modify(|reg| { |
| 113 | Prescaler::DividedBy12 => Presc::DIV12, | 129 | reg.set_adstart(true); |
| 114 | Prescaler::DividedBy16 => Presc::DIV16, | 130 | }); |
| 115 | Prescaler::DividedBy32 => Presc::DIV32, | 131 | |
| 116 | Prescaler::DividedBy64 => Presc::DIV64, | 132 | while !self.isr().read().eos() { |
| 117 | Prescaler::DividedBy128 => Presc::DIV128, | 133 | // spin |
| 118 | Prescaler::DividedBy256 => Presc::DIV256, | ||
| 119 | } | 134 | } |
| 120 | } | 135 | } |
| 121 | } | ||
| 122 | 136 | ||
| 123 | impl<'d, T: Instance> Adc<'d, T> { | 137 | fn configure_dma(&self, conversion_mode: ConversionMode) { |
| 124 | /// Create a new ADC driver. | 138 | self.isr().modify(|reg| { |
| 125 | pub fn new(adc: Peri<'d, T>) -> Self { | 139 | reg.set_ovr(true); |
| 126 | rcc::enable_and_reset::<T>(); | 140 | }); |
| 127 | 141 | ||
| 128 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 142 | self.cfgr().modify(|reg| { |
| 143 | reg.set_discen(false); // Convert all channels for each trigger | ||
| 144 | reg.set_dmacfg(match conversion_mode { | ||
| 145 | ConversionMode::Singular => Dmacfg::ONE_SHOT, | ||
| 146 | ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, | ||
| 147 | }); | ||
| 148 | reg.set_dmaen(Dmaen::ENABLE); | ||
| 149 | }); | ||
| 129 | 150 | ||
| 130 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 151 | if let ConversionMode::Repeated(mode) = conversion_mode { |
| 152 | match mode { | ||
| 153 | RegularConversionMode::Continuous => { | ||
| 154 | self.cfgr().modify(|reg| { | ||
| 155 | reg.set_cont(true); | ||
| 156 | }); | ||
| 157 | } | ||
| 158 | RegularConversionMode::Triggered(trigger) => { | ||
| 159 | self.cfgr().modify(|r| { | ||
| 160 | r.set_cont(false); // New trigger is neede for each sample to be read | ||
| 161 | }); | ||
| 131 | 162 | ||
| 132 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 163 | self.cfgr().modify(|r| { |
| 133 | trace!("ADC frequency set to {}", frequency); | 164 | r.set_extsel(trigger.channel); |
| 165 | r.set_exten(trigger.edge); | ||
| 166 | }); | ||
| 134 | 167 | ||
| 135 | if frequency > MAX_ADC_CLK_FREQ { | 168 | // Regular conversions uses DMA so no need to generate interrupt |
| 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 ); | 169 | self.ier().modify(|r| r.set_eosie(false)); |
| 170 | } | ||
| 171 | } | ||
| 137 | } | 172 | } |
| 173 | } | ||
| 138 | 174 | ||
| 139 | let mut s = Self { | 175 | fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| 140 | adc, | 176 | self.cr().modify(|w| w.set_aden(false)); |
| 141 | sample_time: SampleTime::from_bits(0), | ||
| 142 | }; | ||
| 143 | s.power_up(); | ||
| 144 | s.configure_differential_inputs(); | ||
| 145 | 177 | ||
| 146 | s.calibrate(); | 178 | #[cfg(stm32g4)] |
| 147 | blocking_delay_us(1); | 179 | let mut difsel = DifselReg::default(); |
| 180 | let mut smpr = Smpr::default(); | ||
| 181 | let mut smpr2 = Smpr2::default(); | ||
| 182 | let mut sqr1 = Sqr1::default(); | ||
| 183 | let mut sqr2 = Sqr2::default(); | ||
| 184 | let mut sqr3 = Sqr3::default(); | ||
| 185 | let mut sqr4 = Sqr4::default(); | ||
| 186 | |||
| 187 | // Set sequence length | ||
| 188 | sqr1.set_l(sequence.len() as u8 - 1); | ||
| 189 | |||
| 190 | // Configure channels and ranks | ||
| 191 | for (_i, ((ch, is_differential), sample_time)) in sequence.enumerate() { | ||
| 192 | let sample_time = sample_time.into(); | ||
| 193 | if ch <= 9 { | ||
| 194 | smpr.set_smp(ch as _, sample_time); | ||
| 195 | } else { | ||
| 196 | smpr2.set_smp((ch - 10) as _, sample_time); | ||
| 197 | } | ||
| 198 | |||
| 199 | match _i { | ||
| 200 | 0..=3 => { | ||
| 201 | sqr1.set_sq(_i, ch); | ||
| 202 | } | ||
| 203 | 4..=8 => { | ||
| 204 | sqr2.set_sq(_i - 4, ch); | ||
| 205 | } | ||
| 206 | 9..=13 => { | ||
| 207 | sqr3.set_sq(_i - 9, ch); | ||
| 208 | } | ||
| 209 | 14..=15 => { | ||
| 210 | sqr4.set_sq(_i - 14, ch); | ||
| 211 | } | ||
| 212 | _ => unreachable!(), | ||
| 213 | } | ||
| 148 | 214 | ||
| 149 | s.enable(); | 215 | #[cfg(stm32g4)] |
| 150 | s.configure(); | 216 | { |
| 217 | if ch < 18 { | ||
| 218 | difsel.set_difsel( | ||
| 219 | ch.into(), | ||
| 220 | if is_differential { | ||
| 221 | Difsel::DIFFERENTIAL | ||
| 222 | } else { | ||
| 223 | Difsel::SINGLE_ENDED | ||
| 224 | }, | ||
| 225 | ); | ||
| 226 | } | ||
| 227 | } | ||
| 228 | } | ||
| 151 | 229 | ||
| 152 | s | 230 | self.smpr().write_value(smpr); |
| 231 | self.smpr2().write_value(smpr2); | ||
| 232 | self.sqr1().write_value(sqr1); | ||
| 233 | self.sqr2().write_value(sqr2); | ||
| 234 | self.sqr3().write_value(sqr3); | ||
| 235 | self.sqr4().write_value(sqr4); | ||
| 236 | #[cfg(stm32g4)] | ||
| 237 | self.difsel().write_value(difsel); | ||
| 153 | } | 238 | } |
| 239 | } | ||
| 240 | |||
| 241 | impl<'d, T: Instance<Regs = crate::pac::adc::Adc>> Adc<'d, T> { | ||
| 242 | /// Create a new ADC driver. | ||
| 243 | pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self { | ||
| 244 | rcc::enable_and_reset::<T>(); | ||
| 245 | |||
| 246 | let prescaler = from_ker_ck(T::frequency()); | ||
| 247 | |||
| 248 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); | ||
| 249 | |||
| 250 | let frequency = T::frequency() / prescaler; | ||
| 251 | trace!("ADC frequency set to {}", frequency); | ||
| 252 | |||
| 253 | if frequency > MAX_ADC_CLK_FREQ { | ||
| 254 | panic!( | ||
| 255 | "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", | ||
| 256 | MAX_ADC_CLK_FREQ.0 / 1_000_000 | ||
| 257 | ); | ||
| 258 | } | ||
| 154 | 259 | ||
| 155 | fn power_up(&mut self) { | ||
| 156 | T::regs().cr().modify(|reg| { | 260 | T::regs().cr().modify(|reg| { |
| 157 | reg.set_deeppwd(false); | 261 | reg.set_deeppwd(false); |
| 158 | reg.set_advregen(true); | 262 | reg.set_advregen(true); |
| 159 | }); | 263 | }); |
| 160 | 264 | ||
| 161 | blocking_delay_us(20); | 265 | blocking_delay_us(20); |
| 162 | } | ||
| 163 | 266 | ||
| 164 | fn configure_differential_inputs(&mut self) { | ||
| 165 | T::regs().difsel().modify(|w| { | 267 | T::regs().difsel().modify(|w| { |
| 166 | for n in 0..18 { | 268 | for n in 0..18 { |
| 167 | w.set_difsel(n, Difsel::SINGLE_ENDED); | 269 | w.set_difsel(n, Difsel::SINGLE_ENDED); |
| 168 | } | 270 | } |
| 169 | }); | 271 | }); |
| 170 | } | ||
| 171 | 272 | ||
| 172 | fn calibrate(&mut self) { | ||
| 173 | T::regs().cr().modify(|w| { | 273 | T::regs().cr().modify(|w| { |
| 174 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); | 274 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); |
| 175 | }); | 275 | }); |
| @@ -189,120 +289,79 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 189 | while T::regs().cr().read().adcal() {} | 289 | while T::regs().cr().read().adcal() {} |
| 190 | 290 | ||
| 191 | blocking_delay_us(20); | 291 | blocking_delay_us(20); |
| 192 | } | ||
| 193 | 292 | ||
| 194 | fn enable(&mut self) { | 293 | T::regs().enable(); |
| 195 | // Make sure bits are off | ||
| 196 | while T::regs().cr().read().addis() { | ||
| 197 | // spin | ||
| 198 | } | ||
| 199 | |||
| 200 | if !T::regs().cr().read().aden() { | ||
| 201 | // Enable ADC | ||
| 202 | T::regs().isr().modify(|reg| { | ||
| 203 | reg.set_adrdy(true); | ||
| 204 | }); | ||
| 205 | T::regs().cr().modify(|reg| { | ||
| 206 | reg.set_aden(true); | ||
| 207 | }); | ||
| 208 | |||
| 209 | while !T::regs().isr().read().adrdy() { | ||
| 210 | // spin | ||
| 211 | } | ||
| 212 | } | ||
| 213 | } | ||
| 214 | 294 | ||
| 215 | fn configure(&mut self) { | ||
| 216 | // single conversion mode, software trigger | 295 | // single conversion mode, software trigger |
| 217 | T::regs().cfgr().modify(|w| { | 296 | T::regs().cfgr().modify(|w| { |
| 218 | w.set_cont(false); | 297 | w.set_cont(false); |
| 219 | w.set_exten(Exten::DISABLED); | 298 | w.set_exten(Exten::DISABLED); |
| 220 | }); | 299 | }); |
| 300 | |||
| 301 | if let Some(dual) = config.dual_mode { | ||
| 302 | T::common_regs().ccr().modify(|reg| { | ||
| 303 | reg.set_dual(dual); | ||
| 304 | }) | ||
| 305 | } | ||
| 306 | |||
| 307 | if let Some(resolution) = config.resolution { | ||
| 308 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 309 | } | ||
| 310 | |||
| 311 | #[cfg(stm32g4)] | ||
| 312 | if let Some(shift) = config.oversampling_shift { | ||
| 313 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 314 | } | ||
| 315 | |||
| 316 | #[cfg(stm32g4)] | ||
| 317 | if let Some(ratio) = config.oversampling_ratio { | ||
| 318 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 319 | } | ||
| 320 | |||
| 321 | #[cfg(stm32g4)] | ||
| 322 | if let Some((mode, trig_mode, enable)) = config.oversampling_mode { | ||
| 323 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 324 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 325 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 326 | } | ||
| 327 | |||
| 328 | Self { adc } | ||
| 221 | } | 329 | } |
| 222 | 330 | ||
| 223 | /// Enable reading the voltage reference internal channel. | 331 | /// Enable reading the voltage reference internal channel. |
| 224 | pub fn enable_vrefint(&self) -> VrefInt | 332 | pub fn enable_vrefint(&self) -> super::VrefInt |
| 225 | where | 333 | where |
| 226 | T: VrefChannel, | 334 | T: super::SpecialConverter<super::VrefInt>, |
| 227 | { | 335 | { |
| 228 | T::common_regs().ccr().modify(|reg| { | 336 | T::common_regs().ccr().modify(|reg| { |
| 229 | reg.set_vrefen(true); | 337 | reg.set_vrefen(true); |
| 230 | }); | 338 | }); |
| 231 | 339 | ||
| 232 | VrefInt {} | 340 | super::VrefInt {} |
| 233 | } | 341 | } |
| 234 | 342 | ||
| 235 | /// Enable reading the temperature internal channel. | 343 | /// Enable reading the temperature internal channel. |
| 236 | pub fn enable_temperature(&self) -> Temperature | 344 | pub fn enable_temperature(&self) -> super::Temperature |
| 237 | where | 345 | where |
| 238 | T: TemperatureChannel, | 346 | T: super::SpecialConverter<super::Temperature>, |
| 239 | { | 347 | { |
| 240 | T::common_regs().ccr().modify(|reg| { | 348 | T::common_regs().ccr().modify(|reg| { |
| 241 | reg.set_vsenseen(true); | 349 | reg.set_vsenseen(true); |
| 242 | }); | 350 | }); |
| 243 | 351 | ||
| 244 | Temperature {} | 352 | super::Temperature {} |
| 245 | } | 353 | } |
| 246 | 354 | ||
| 247 | /// Enable reading the vbat internal channel. | 355 | /// Enable reading the vbat internal channel. |
| 248 | pub fn enable_vbat(&self) -> Vbat | 356 | pub fn enable_vbat(&self) -> super::Vbat |
| 249 | where | 357 | where |
| 250 | T: VBatChannel, | 358 | T: super::SpecialConverter<super::Vbat>, |
| 251 | { | 359 | { |
| 252 | T::common_regs().ccr().modify(|reg| { | 360 | T::common_regs().ccr().modify(|reg| { |
| 253 | reg.set_vbaten(true); | 361 | reg.set_vbaten(true); |
| 254 | }); | 362 | }); |
| 255 | 363 | ||
| 256 | Vbat {} | 364 | super::Vbat {} |
| 257 | } | ||
| 258 | |||
| 259 | /// Enable differential channel. | ||
| 260 | /// Caution: | ||
| 261 | /// : When configuring the channel “i” in differential input mode, its negative input voltage VINN[i] | ||
| 262 | /// is connected to another channel. As a consequence, this channel is no longer usable in | ||
| 263 | /// single-ended mode or in differential mode and must never be configured to be converted. | ||
| 264 | /// Some channels are shared between ADC1/ADC2/ADC3/ADC4/ADC5: this can make the | ||
| 265 | /// channel on the other ADC unusable. The only exception is when ADC master and the slave | ||
| 266 | /// operate in interleaved mode. | ||
| 267 | #[cfg(stm32g4)] | ||
| 268 | pub 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().difsel().modify(|w| { | ||
| 271 | w.set_difsel( | ||
| 272 | ch, | ||
| 273 | if enable { | ||
| 274 | Difsel::DIFFERENTIAL | ||
| 275 | } else { | ||
| 276 | Difsel::SINGLE_ENDED | ||
| 277 | }, | ||
| 278 | ); | ||
| 279 | }); | ||
| 280 | T::regs().cr().modify(|w| w.set_aden(true)); | ||
| 281 | } | ||
| 282 | |||
| 283 | #[cfg(stm32g4)] | ||
| 284 | pub fn set_differential(&mut self, channel: &mut impl AdcChannel<T>, enable: bool) { | ||
| 285 | self.set_differential_channel(channel.channel() as usize, enable); | ||
| 286 | } | ||
| 287 | |||
| 288 | /// Set oversampling shift. | ||
| 289 | #[cfg(stm32g4)] | ||
| 290 | pub fn set_oversampling_shift(&mut self, shift: u8) { | ||
| 291 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 292 | } | ||
| 293 | |||
| 294 | /// Set oversampling ratio. | ||
| 295 | #[cfg(stm32g4)] | ||
| 296 | pub fn set_oversampling_ratio(&mut self, ratio: u8) { | ||
| 297 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 298 | } | ||
| 299 | |||
| 300 | /// Enable oversampling in regular mode. | ||
| 301 | #[cfg(stm32g4)] | ||
| 302 | pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { | ||
| 303 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 304 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 305 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 306 | } | 365 | } |
| 307 | 366 | ||
| 308 | // Reads that are not implemented as INJECTED in "blocking_read" | 367 | // Reads that are not implemented as INJECTED in "blocking_read" |
| @@ -318,260 +377,215 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 318 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); | 377 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); |
| 319 | // } | 378 | // } |
| 320 | 379 | ||
| 321 | /// Set the ADC sample time. | 380 | /// Configures the ADC for injected conversions. |
| 322 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 323 | self.sample_time = sample_time; | ||
| 324 | } | ||
| 325 | |||
| 326 | /// Set the ADC resolution. | ||
| 327 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 328 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 329 | } | ||
| 330 | |||
| 331 | /// Perform a single conversion. | ||
| 332 | fn convert(&mut self) -> u16 { | ||
| 333 | T::regs().isr().modify(|reg| { | ||
| 334 | reg.set_eos(true); | ||
| 335 | reg.set_eoc(true); | ||
| 336 | }); | ||
| 337 | |||
| 338 | // Start conversion | ||
| 339 | T::regs().cr().modify(|reg| { | ||
| 340 | reg.set_adstart(true); | ||
| 341 | }); | ||
| 342 | |||
| 343 | while !T::regs().isr().read().eos() { | ||
| 344 | // spin | ||
| 345 | } | ||
| 346 | |||
| 347 | T::regs().dr().read().0 as u16 | ||
| 348 | } | ||
| 349 | |||
| 350 | /// Read an ADC pin. | ||
| 351 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 352 | channel.setup(); | ||
| 353 | |||
| 354 | self.read_channel(channel) | ||
| 355 | } | ||
| 356 | |||
| 357 | /// Read one or multiple ADC channels using DMA. | ||
| 358 | /// | 381 | /// |
| 359 | /// `sequence` iterator and `readings` must have the same length. | 382 | /// Injected conversions are separate from the regular conversion sequence and are typically |
| 383 | /// triggered by software or an external event. This method sets up a fixed-length sequence of | ||
| 384 | /// injected channels with specified sample times, the trigger source, and whether the end-of-sequence | ||
| 385 | /// interrupt should be enabled. | ||
| 360 | /// | 386 | /// |
| 361 | /// Example | 387 | /// # Parameters |
| 362 | /// ```rust,ignore | 388 | /// - `sequence`: An array of tuples containing the ADC channels and their sample times. The length |
| 363 | /// use embassy_stm32::adc::{Adc, AdcChannel} | 389 | /// `N` determines the number of injected ranks to configure (maximum 4 for STM32). |
| 390 | /// - `trigger`: The trigger source that starts the injected conversion sequence. | ||
| 391 | /// - `interrupt`: If `true`, enables the end-of-sequence (JEOS) interrupt for injected conversions. | ||
| 364 | /// | 392 | /// |
| 365 | /// let mut adc = Adc::new(p.ADC1); | 393 | /// # Returns |
| 366 | /// let mut adc_pin0 = p.PA0.into(); | 394 | /// An `InjectedAdc<T, N>` instance that represents the configured injected sequence. The returned |
| 367 | /// let mut adc_pin1 = p.PA1.into(); | 395 | /// type encodes the sequence length `N` in its type, ensuring that reads return exactly `N` samples. |
| 368 | /// let mut measurements = [0u16; 2]; | ||
| 369 | /// | 396 | /// |
| 370 | /// adc.read( | 397 | /// # Panics |
| 371 | /// p.DMA1_CH2.reborrow(), | 398 | /// This function will panic if: |
| 372 | /// [ | 399 | /// - `sequence` is empty. |
| 373 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | 400 | /// - `sequence` length exceeds the maximum number of injected ranks (`NR_INJECTED_RANKS`). |
| 374 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | 401 | /// |
| 375 | /// ] | 402 | /// # Notes |
| 376 | /// .into_iter(), | 403 | /// - Injected conversions can run independently of regular ADC conversions. |
| 377 | /// &mut measurements, | 404 | /// - The order of channels in `sequence` determines the rank order in the injected sequence. |
| 378 | /// ) | 405 | /// - Accessing samples beyond `N` will result in a panic; use the returned type |
| 379 | /// .await; | 406 | /// `InjectedAdc<T, N>` to enforce bounds at compile time. |
| 380 | /// defmt::info!("measurements: {}", measurements); | 407 | pub fn setup_injected_conversions<'a, const N: usize>( |
| 381 | /// ``` | 408 | self, |
| 382 | pub async fn read( | 409 | sequence: [(AnyAdcChannel<'a, T>, SampleTime); N], |
| 383 | &mut self, | 410 | trigger: ConversionTrigger, |
| 384 | rx_dma: Peri<'_, impl RxDma<T>>, | 411 | interrupt: bool, |
| 385 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | 412 | ) -> InjectedAdc<'a, T, N> { |
| 386 | readings: &mut [u16], | 413 | assert!(N != 0, "Read sequence cannot be empty"); |
| 387 | ) { | ||
| 388 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 389 | assert!( | ||
| 390 | sequence.len() == readings.len(), | ||
| 391 | "Sequence length must be equal to readings length" | ||
| 392 | ); | ||
| 393 | assert!( | 414 | assert!( |
| 394 | sequence.len() <= 16, | 415 | N <= NR_INJECTED_RANKS, |
| 395 | "Asynchronous read sequence cannot be more than 16 in length" | 416 | "Read sequence cannot be more than {} in length", |
| 417 | NR_INJECTED_RANKS | ||
| 396 | ); | 418 | ); |
| 397 | 419 | ||
| 398 | // Ensure no conversions are ongoing and ADC is enabled. | 420 | T::regs().enable(); |
| 399 | Self::cancel_conversions(); | ||
| 400 | self.enable(); | ||
| 401 | 421 | ||
| 402 | // Set sequence length | 422 | T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); |
| 403 | T::regs().sqr1().modify(|w| { | ||
| 404 | w.set_l(sequence.len() as u8 - 1); | ||
| 405 | }); | ||
| 406 | |||
| 407 | // Configure channels and ranks | ||
| 408 | for (_i, (channel, sample_time)) in sequence.enumerate() { | ||
| 409 | Self::configure_channel(channel, sample_time); | ||
| 410 | 423 | ||
| 411 | match _i { | 424 | for (n, (channel, sample_time)) in sequence.iter().enumerate() { |
| 412 | 0..=3 => { | 425 | let sample_time = sample_time.clone().into(); |
| 413 | T::regs().sqr1().modify(|w| { | 426 | if channel.channel() <= 9 { |
| 414 | w.set_sq(_i, channel.channel()); | 427 | T::regs() |
| 415 | }); | 428 | .smpr() |
| 416 | } | 429 | .modify(|reg| reg.set_smp(channel.channel() as _, sample_time)); |
| 417 | 4..=8 => { | 430 | } else { |
| 418 | T::regs().sqr2().modify(|w| { | 431 | T::regs() |
| 419 | w.set_sq(_i - 4, channel.channel()); | 432 | .smpr2() |
| 420 | }); | 433 | .modify(|reg| reg.set_smp((channel.channel() - 10) as _, sample_time)); |
| 421 | } | ||
| 422 | 9..=13 => { | ||
| 423 | T::regs().sqr3().modify(|w| { | ||
| 424 | w.set_sq(_i - 9, channel.channel()); | ||
| 425 | }); | ||
| 426 | } | ||
| 427 | 14..=15 => { | ||
| 428 | T::regs().sqr4().modify(|w| { | ||
| 429 | w.set_sq(_i - 14, channel.channel()); | ||
| 430 | }); | ||
| 431 | } | ||
| 432 | _ => unreachable!(), | ||
| 433 | } | 434 | } |
| 434 | } | ||
| 435 | |||
| 436 | // Set continuous mode with oneshot dma. | ||
| 437 | // Clear overrun flag before starting transfer. | ||
| 438 | T::regs().isr().modify(|reg| { | ||
| 439 | reg.set_ovr(true); | ||
| 440 | }); | ||
| 441 | 435 | ||
| 442 | T::regs().cfgr().modify(|reg| { | 436 | let idx = match n { |
| 443 | reg.set_discen(false); | 437 | 0..=3 => n, |
| 444 | reg.set_cont(true); | 438 | 4..=8 => n - 4, |
| 445 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | 439 | 9..=13 => n - 9, |
| 446 | reg.set_dmaen(Dmaen::ENABLE); | 440 | 14..=15 => n - 14, |
| 447 | }); | 441 | _ => unreachable!(), |
| 448 | 442 | }; | |
| 449 | let request = rx_dma.request(); | ||
| 450 | let transfer = unsafe { | ||
| 451 | Transfer::new_read( | ||
| 452 | rx_dma, | ||
| 453 | request, | ||
| 454 | T::regs().dr().as_ptr() as *mut u16, | ||
| 455 | readings, | ||
| 456 | Default::default(), | ||
| 457 | ) | ||
| 458 | }; | ||
| 459 | |||
| 460 | // Start conversion | ||
| 461 | T::regs().cr().modify(|reg| { | ||
| 462 | reg.set_adstart(true); | ||
| 463 | }); | ||
| 464 | 443 | ||
| 465 | // Wait for conversion sequence to finish. | 444 | T::regs().jsqr().modify(|w| w.set_jsq(idx, channel.channel())); |
| 466 | transfer.await; | 445 | } |
| 467 | 446 | ||
| 468 | // Ensure conversions are finished. | 447 | T::regs().cfgr().modify(|reg| reg.set_jdiscen(false)); |
| 469 | Self::cancel_conversions(); | ||
| 470 | 448 | ||
| 471 | // Reset configuration. | 449 | // Set external trigger for injected conversion sequence |
| 472 | T::regs().cfgr().modify(|reg| { | 450 | // Possible trigger values are seen in Table 167 in RM0440 Rev 9 |
| 473 | reg.set_cont(false); | 451 | T::regs().jsqr().modify(|r| { |
| 452 | r.set_jextsel(trigger.channel); | ||
| 453 | r.set_jexten(trigger.edge); | ||
| 474 | }); | 454 | }); |
| 475 | } | ||
| 476 | |||
| 477 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | ||
| 478 | // Configure channel | ||
| 479 | Self::set_channel_sample_time(channel.channel(), sample_time); | ||
| 480 | } | ||
| 481 | 455 | ||
| 482 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 456 | // Enable end of injected sequence interrupt |
| 483 | Self::configure_channel(channel, self.sample_time); | 457 | T::regs().ier().modify(|r| r.set_jeosie(interrupt)); |
| 484 | #[cfg(stm32h7)] | ||
| 485 | { | ||
| 486 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 487 | T::regs() | ||
| 488 | .pcsel() | ||
| 489 | .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); | ||
| 490 | } | ||
| 491 | 458 | ||
| 492 | T::regs().sqr1().write(|reg| { | 459 | Self::start_injected_conversions(); |
| 493 | reg.set_sq(0, channel.channel()); | ||
| 494 | reg.set_l(0); | ||
| 495 | }); | ||
| 496 | 460 | ||
| 497 | self.convert() | 461 | InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels |
| 498 | } | 462 | } |
| 499 | 463 | ||
| 500 | fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | 464 | /// Configures ADC for both regular conversions with a ring-buffered DMA and injected conversions. |
| 501 | let sample_time = sample_time.into(); | 465 | /// |
| 502 | if ch <= 9 { | 466 | /// # Parameters |
| 503 | T::regs().smpr().modify(|reg| reg.set_smp(ch as _, sample_time)); | 467 | /// - `dma`: The DMA peripheral to use for the ring-buffered ADC transfers. |
| 504 | } else { | 468 | /// - `dma_buf`: The buffer to store DMA-transferred samples for regular conversions. |
| 505 | T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | 469 | /// - `regular_sequence`: The sequence of channels and their sample times for regular conversions. |
| 470 | /// - `regular_conversion_mode`: The mode for regular conversions (e.g., continuous or triggered). | ||
| 471 | /// - `injected_sequence`: An array of channels and sample times for injected conversions (length `N`). | ||
| 472 | /// - `injected_trigger`: The trigger source for injected conversions. | ||
| 473 | /// - `injected_interrupt`: Whether to enable the end-of-sequence interrupt for injected conversions. | ||
| 474 | /// | ||
| 475 | /// Injected conversions are typically used with interrupts. If ADC1 and ADC2 are used in dual mode, | ||
| 476 | /// it is recommended to enable interrupts only for the ADC whose sequence takes the longest to complete. | ||
| 477 | /// | ||
| 478 | /// # Returns | ||
| 479 | /// A tuple containing: | ||
| 480 | /// 1. `RingBufferedAdc<'a, T>` — the configured ADC for regular conversions using DMA. | ||
| 481 | /// 2. `InjectedAdc<T, N>` — the configured ADC for injected conversions. | ||
| 482 | /// | ||
| 483 | /// # Safety | ||
| 484 | /// This function is `unsafe` because it clones the ADC peripheral handle unchecked. Both the | ||
| 485 | /// `RingBufferedAdc` and `InjectedAdc` take ownership of the handle and drop it independently. | ||
| 486 | /// Ensure no other code concurrently accesses the same ADC instance in a conflicting way. | ||
| 487 | pub fn into_ring_buffered_and_injected<'a, 'b, const N: usize>( | ||
| 488 | self, | ||
| 489 | dma: Peri<'a, impl RxDma<T>>, | ||
| 490 | dma_buf: &'a mut [u16], | ||
| 491 | regular_sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<'b, T>, <T::Regs as BasicAdcRegs>::SampleTime)>, | ||
| 492 | regular_conversion_mode: RegularConversionMode, | ||
| 493 | injected_sequence: [(AnyAdcChannel<'b, T>, SampleTime); N], | ||
| 494 | injected_trigger: ConversionTrigger, | ||
| 495 | injected_interrupt: bool, | ||
| 496 | ) -> (super::RingBufferedAdc<'a, T>, InjectedAdc<'b, T, N>) { | ||
| 497 | unsafe { | ||
| 498 | ( | ||
| 499 | Self { | ||
| 500 | adc: self.adc.clone_unchecked(), | ||
| 501 | } | ||
| 502 | .into_ring_buffered(dma, dma_buf, regular_sequence, regular_conversion_mode), | ||
| 503 | Self { | ||
| 504 | adc: self.adc.clone_unchecked(), | ||
| 505 | } | ||
| 506 | .setup_injected_conversions(injected_sequence, injected_trigger, injected_interrupt), | ||
| 507 | ) | ||
| 506 | } | 508 | } |
| 507 | } | 509 | } |
| 508 | 510 | ||
| 509 | fn cancel_conversions() { | 511 | /// Stop injected conversions |
| 512 | pub(super) fn stop_injected_conversions() { | ||
| 510 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | 513 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 511 | T::regs().cr().modify(|reg| { | 514 | T::regs().cr().modify(|reg| { |
| 512 | reg.set_adstp(Adstp::STOP); | 515 | reg.set_jadstp(Adstp::STOP); |
| 513 | }); | 516 | }); |
| 514 | while T::regs().cr().read().adstart() {} | 517 | // The software must poll JADSTART until the bit is reset before assuming the |
| 518 | // ADC is completely stopped | ||
| 519 | while T::regs().cr().read().jadstart() {} | ||
| 515 | } | 520 | } |
| 516 | } | 521 | } |
| 517 | } | ||
| 518 | 522 | ||
| 519 | /// Implemented for ADCs that have a Temperature channel | 523 | /// Start injected ADC conversion |
| 520 | pub trait TemperatureChannel { | 524 | pub(super) fn start_injected_conversions() { |
| 521 | const CHANNEL: u8; | 525 | T::regs().cr().modify(|reg| { |
| 522 | } | 526 | reg.set_jadstart(true); |
| 523 | /// Implemented for ADCs that have a Vref channel | 527 | }); |
| 524 | pub trait VrefChannel { | 528 | } |
| 525 | const CHANNEL: u8; | ||
| 526 | } | 529 | } |
| 527 | /// Implemented for ADCs that have a VBat channel | 530 | |
| 528 | pub trait VBatChannel { | 531 | impl<'a, T: Instance<Regs = crate::pac::adc::Adc>, const N: usize> InjectedAdc<'a, T, N> { |
| 529 | const CHANNEL: u8; | 532 | /// Read sampled data from all injected ADC injected ranks |
| 533 | /// Clear the JEOS flag to allow a new injected sequence | ||
| 534 | pub(super) fn read_injected_data() -> [u16; N] { | ||
| 535 | let mut data = [0u16; N]; | ||
| 536 | for i in 0..N { | ||
| 537 | data[i] = T::regs().jdr(i).read().jdata(); | ||
| 538 | } | ||
| 539 | |||
| 540 | // Clear JEOS by writing 1 | ||
| 541 | T::regs().isr().modify(|r| r.set_jeos(true)); | ||
| 542 | data | ||
| 543 | } | ||
| 530 | } | 544 | } |
| 531 | 545 | ||
| 532 | #[cfg(stm32g4)] | 546 | #[cfg(stm32g4)] |
| 533 | mod g4 { | 547 | mod g4 { |
| 534 | pub use super::*; | 548 | use crate::adc::{SealedSpecialConverter, Temperature, Vbat, VrefInt}; |
| 535 | 549 | ||
| 536 | impl TemperatureChannel for crate::peripherals::ADC1 { | 550 | impl SealedSpecialConverter<Temperature> for crate::peripherals::ADC1 { |
| 537 | const CHANNEL: u8 = 16; | 551 | const CHANNEL: u8 = 16; |
| 538 | } | 552 | } |
| 539 | 553 | ||
| 540 | impl VrefChannel for crate::peripherals::ADC1 { | 554 | impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC1 { |
| 541 | const CHANNEL: u8 = 18; | 555 | const CHANNEL: u8 = 18; |
| 542 | } | 556 | } |
| 543 | 557 | ||
| 544 | impl VBatChannel for crate::peripherals::ADC1 { | 558 | impl SealedSpecialConverter<Vbat> for crate::peripherals::ADC1 { |
| 545 | const CHANNEL: u8 = 17; | 559 | const CHANNEL: u8 = 17; |
| 546 | } | 560 | } |
| 547 | 561 | ||
| 548 | #[cfg(peri_adc3_common)] | 562 | #[cfg(peri_adc3_common)] |
| 549 | impl VrefChannel for crate::peripherals::ADC3 { | 563 | impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC3 { |
| 550 | const CHANNEL: u8 = 18; | 564 | const CHANNEL: u8 = 18; |
| 551 | } | 565 | } |
| 552 | 566 | ||
| 553 | #[cfg(peri_adc3_common)] | 567 | #[cfg(peri_adc3_common)] |
| 554 | impl VBatChannel for crate::peripherals::ADC3 { | 568 | impl SealedSpecialConverter<Vbat> for crate::peripherals::ADC3 { |
| 555 | const CHANNEL: u8 = 17; | 569 | const CHANNEL: u8 = 17; |
| 556 | } | 570 | } |
| 557 | 571 | ||
| 558 | #[cfg(not(stm32g4x1))] | 572 | #[cfg(not(stm32g4x1))] |
| 559 | impl VrefChannel for crate::peripherals::ADC4 { | 573 | impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC4 { |
| 560 | const CHANNEL: u8 = 18; | 574 | const CHANNEL: u8 = 18; |
| 561 | } | 575 | } |
| 562 | 576 | ||
| 563 | #[cfg(not(stm32g4x1))] | 577 | #[cfg(not(stm32g4x1))] |
| 564 | impl TemperatureChannel for crate::peripherals::ADC5 { | 578 | impl SealedSpecialConverter<Temperature> for crate::peripherals::ADC5 { |
| 565 | const CHANNEL: u8 = 4; | 579 | const CHANNEL: u8 = 4; |
| 566 | } | 580 | } |
| 567 | 581 | ||
| 568 | #[cfg(not(stm32g4x1))] | 582 | #[cfg(not(stm32g4x1))] |
| 569 | impl VrefChannel for crate::peripherals::ADC5 { | 583 | impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC5 { |
| 570 | const CHANNEL: u8 = 18; | 584 | const CHANNEL: u8 = 18; |
| 571 | } | 585 | } |
| 572 | 586 | ||
| 573 | #[cfg(not(stm32g4x1))] | 587 | #[cfg(not(stm32g4x1))] |
| 574 | impl VBatChannel for crate::peripherals::ADC5 { | 588 | impl SealedSpecialConverter<Vbat> for crate::peripherals::ADC5 { |
| 575 | const CHANNEL: u8 = 17; | 589 | const CHANNEL: u8 = 17; |
| 576 | } | 590 | } |
| 577 | } | 591 | } |
| @@ -579,13 +593,13 @@ mod g4 { | |||
| 579 | // TODO this should look at each ADC individually and impl the correct channels | 593 | // TODO this should look at each ADC individually and impl the correct channels |
| 580 | #[cfg(stm32h7)] | 594 | #[cfg(stm32h7)] |
| 581 | mod h7 { | 595 | mod h7 { |
| 582 | impl<T: Instance> TemperatureChannel for T { | 596 | impl<T: Instance> SealedSpecialConverter<Temperature> for T { |
| 583 | const CHANNEL: u8 = 18; | 597 | const CHANNEL: u8 = 18; |
| 584 | } | 598 | } |
| 585 | impl<T: Instance> VrefChannel for T { | 599 | impl<T: Instance> SealedSpecialConverter<VrefInt> for T { |
| 586 | const CHANNEL: u8 = 19; | 600 | const CHANNEL: u8 = 19; |
| 587 | } | 601 | } |
| 588 | impl<T: Instance> VBatChannel for T { | 602 | impl<T: Instance> SealedSpecialConverter<Vbat> for T { |
| 589 | // TODO this should be 14 for H7a/b/35 | 603 | // TODO this should be 14 for H7a/b/35 |
| 590 | const CHANNEL: u8 = 17; | 604 | const CHANNEL: u8 = 17; |
| 591 | } | 605 | } |
diff --git a/embassy-stm32/src/adc/injected.rs b/embassy-stm32/src/adc/injected.rs new file mode 100644 index 000000000..029722b84 --- /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::{AdcRegs, 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<'a, T: Instance<Regs = crate::pac::adc::Adc>, const N: usize> { | ||
| 14 | _channels: [(AnyAdcChannel<'a, T>, SampleTime); N], | ||
| 15 | _phantom: PhantomData<T>, | ||
| 16 | } | ||
| 17 | |||
| 18 | impl<'a, T: Instance<Regs = crate::pac::adc::Adc>, const N: usize> InjectedAdc<'a, T, N> { | ||
| 19 | pub(crate) fn new(channels: [(AnyAdcChannel<'a, 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<'a, T: Instance<Regs = crate::pac::adc::Adc>, const N: usize> Drop for InjectedAdc<'a, T, N> { | ||
| 40 | fn drop(&mut self) { | ||
| 41 | T::regs().stop(); | ||
| 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..da432f6ce 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs | |||
| @@ -17,38 +17,44 @@ | |||
| 17 | #[cfg_attr(adc_c0, path = "c0.rs")] | 17 | #[cfg_attr(adc_c0, path = "c0.rs")] |
| 18 | mod _version; | 18 | mod _version; |
| 19 | 19 | ||
| 20 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 21 | mod ringbuffered; | ||
| 22 | |||
| 20 | use core::marker::PhantomData; | 23 | use core::marker::PhantomData; |
| 21 | 24 | ||
| 22 | #[allow(unused)] | 25 | #[allow(unused)] |
| 23 | #[cfg(not(any(adc_f3v3, adc_wba)))] | 26 | #[cfg(not(any(adc_f3v3, adc_wba)))] |
| 24 | pub use _version::*; | 27 | pub use _version::*; |
| 25 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; | 28 | #[allow(unused)] |
| 29 | use embassy_hal_internal::PeripheralType; | ||
| 26 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] | 30 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] |
| 27 | use embassy_sync::waitqueue::AtomicWaker; | 31 | use embassy_sync::waitqueue::AtomicWaker; |
| 32 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 33 | pub use ringbuffered::RingBufferedAdc; | ||
| 34 | |||
| 35 | #[cfg(adc_u5)] | ||
| 36 | use crate::pac::adc::vals::Adc4SampleTime; | ||
| 37 | #[cfg(adc_wba)] | ||
| 38 | use crate::pac::adc::vals::SampleTime as Adc4SampleTime; | ||
| 28 | 39 | ||
| 29 | #[cfg(any(adc_u5, adc_wba))] | 40 | #[cfg(any(adc_u5, adc_wba))] |
| 30 | #[path = "adc4.rs"] | 41 | #[path = "adc4.rs"] |
| 31 | pub mod adc4; | 42 | pub mod adc4; |
| 32 | 43 | ||
| 44 | #[allow(unused)] | ||
| 45 | pub(self) use crate::block_for_us as blocking_delay_us; | ||
| 33 | pub use crate::pac::adc::vals; | 46 | pub use crate::pac::adc::vals; |
| 34 | #[cfg(not(any(adc_f1, adc_f3v3)))] | 47 | #[cfg(not(any(adc_f1, adc_f3v3)))] |
| 35 | pub use crate::pac::adc::vals::Res as Resolution; | 48 | pub use crate::pac::adc::vals::Res as Resolution; |
| 36 | pub use crate::pac::adc::vals::SampleTime; | 49 | pub use crate::pac::adc::vals::SampleTime; |
| 37 | use crate::peripherals; | 50 | use crate::peripherals; |
| 38 | 51 | ||
| 39 | #[cfg(not(adc_wba))] | ||
| 40 | dma_trait!(RxDma, Instance); | 52 | dma_trait!(RxDma, Instance); |
| 41 | #[cfg(adc_u5)] | ||
| 42 | dma_trait!(RxDma4, adc4::Instance); | ||
| 43 | #[cfg(adc_wba)] | ||
| 44 | dma_trait!(RxDma4, adc4::Instance); | ||
| 45 | 53 | ||
| 46 | /// Analog to Digital driver. | 54 | /// Analog to Digital driver. |
| 47 | pub struct Adc<'d, T: Instance> { | 55 | pub struct Adc<'d, T: Instance> { |
| 48 | #[allow(unused)] | 56 | #[allow(unused)] |
| 49 | adc: crate::Peri<'d, T>, | 57 | adc: crate::Peri<'d, T>, |
| 50 | #[cfg(not(any(adc_f3v3, adc_f3v2, adc_wba)))] | ||
| 51 | sample_time: SampleTime, | ||
| 52 | } | 58 | } |
| 53 | 59 | ||
| 54 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] | 60 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] |
| @@ -65,10 +71,53 @@ impl State { | |||
| 65 | } | 71 | } |
| 66 | } | 72 | } |
| 67 | 73 | ||
| 68 | trait SealedInstance { | 74 | #[cfg(any(adc_f1, adc_f3v1, adc_f3v2, adc_v1, adc_l0))] |
| 69 | #[cfg(not(adc_wba))] | 75 | trait_set::trait_set! { |
| 70 | #[allow(unused)] | 76 | pub trait DefaultInstance = Instance; |
| 77 | } | ||
| 78 | |||
| 79 | #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_g4, adc_c0))] | ||
| 80 | trait_set::trait_set! { | ||
| 81 | pub trait DefaultInstance = Instance<Regs = crate::pac::adc::Adc>; | ||
| 82 | } | ||
| 83 | |||
| 84 | #[cfg(adc_wba)] | ||
| 85 | trait_set::trait_set! { | ||
| 86 | pub trait DefaultInstance = Instance<Regs = crate::pac::adc::Adc4>; | ||
| 87 | } | ||
| 88 | |||
| 89 | pub trait BasicAdcRegs { | ||
| 90 | type SampleTime; | ||
| 91 | } | ||
| 92 | |||
| 93 | #[cfg(any( | ||
| 94 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 95 | ))] | ||
| 96 | trait AdcRegs: BasicAdcRegs { | ||
| 97 | fn enable(&self); | ||
| 98 | fn start(&self); | ||
| 99 | fn stop(&self); | ||
| 100 | fn convert(&self); | ||
| 101 | fn configure_dma(&self, conversion_mode: ConversionMode); | ||
| 102 | fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>); | ||
| 103 | fn data(&self) -> *mut u16; | ||
| 104 | } | ||
| 105 | |||
| 106 | #[allow(private_bounds)] | ||
| 107 | pub trait BasicInstance { | ||
| 108 | #[cfg(any( | ||
| 109 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 110 | ))] | ||
| 111 | type Regs: AdcRegs; | ||
| 112 | } | ||
| 113 | |||
| 114 | trait SealedInstance: BasicInstance { | ||
| 115 | #[cfg(any(adc_f1, adc_f3v1, adc_f3v2, adc_v1, adc_l0))] | ||
| 71 | fn regs() -> crate::pac::adc::Adc; | 116 | fn regs() -> crate::pac::adc::Adc; |
| 117 | #[cfg(any( | ||
| 118 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 119 | ))] | ||
| 120 | fn regs() -> Self::Regs; | ||
| 72 | #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3v3, adc_f3v2, adc_g0)))] | 121 | #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3v3, adc_f3v2, adc_g0)))] |
| 73 | #[allow(unused)] | 122 | #[allow(unused)] |
| 74 | fn common_regs() -> crate::pac::adccommon::AdcCommon; | 123 | fn common_regs() -> crate::pac::adccommon::AdcCommon; |
| @@ -77,27 +126,278 @@ trait SealedInstance { | |||
| 77 | } | 126 | } |
| 78 | 127 | ||
| 79 | pub(crate) trait SealedAdcChannel<T> { | 128 | pub(crate) trait SealedAdcChannel<T> { |
| 80 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] | 129 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_wba))] |
| 81 | fn setup(&mut self) {} | 130 | fn setup(&mut self) {} |
| 82 | 131 | ||
| 83 | #[allow(unused)] | 132 | #[allow(unused)] |
| 84 | fn channel(&self) -> u8; | 133 | fn channel(&self) -> u8; |
| 134 | |||
| 135 | #[allow(unused)] | ||
| 136 | fn is_differential(&self) -> bool { | ||
| 137 | false | ||
| 138 | } | ||
| 85 | } | 139 | } |
| 86 | 140 | ||
| 87 | /// Performs a busy-wait delay for a specified number of microseconds. | 141 | #[cfg(any(adc_c0, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] |
| 88 | #[allow(unused)] | 142 | /// Number of samples used for averaging. |
| 89 | pub(crate) fn blocking_delay_us(us: u32) { | 143 | #[derive(Copy, Clone, Debug)] |
| 90 | #[cfg(feature = "time")] | 144 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 91 | embassy_time::block_for(embassy_time::Duration::from_micros(us as u64)); | 145 | pub enum Averaging { |
| 92 | #[cfg(not(feature = "time"))] | 146 | Disabled, |
| 93 | { | 147 | Samples2, |
| 94 | let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64; | 148 | Samples4, |
| 95 | let us = us as u64; | 149 | Samples8, |
| 96 | let cycles = freq * us / 1_000_000; | 150 | Samples16, |
| 97 | cortex_m::asm::delay(cycles as u32); | 151 | Samples32, |
| 152 | Samples64, | ||
| 153 | Samples128, | ||
| 154 | Samples256, | ||
| 155 | #[cfg(any(adc_c0, adc_v4, adc_u5))] | ||
| 156 | Samples512, | ||
| 157 | #[cfg(any(adc_c0, adc_v4, adc_u5))] | ||
| 158 | Samples1024, | ||
| 159 | } | ||
| 160 | |||
| 161 | #[cfg(any( | ||
| 162 | adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0 | ||
| 163 | ))] | ||
| 164 | pub(crate) enum ConversionMode { | ||
| 165 | // Should match the cfg on "read" below | ||
| 166 | #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] | ||
| 167 | Singular, | ||
| 168 | // Should match the cfg on "into_ring_buffered" below | ||
| 169 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 170 | Repeated(RegularConversionMode), | ||
| 171 | } | ||
| 172 | |||
| 173 | // Should match the cfg on "into_ring_buffered" below | ||
| 174 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 175 | // Conversion mode for regular ADC channels | ||
| 176 | #[derive(Copy, Clone)] | ||
| 177 | pub enum RegularConversionMode { | ||
| 178 | // Samples as fast as possible | ||
| 179 | Continuous, | ||
| 180 | #[cfg(adc_g4)] | ||
| 181 | // Sample at rate determined by external trigger | ||
| 182 | Triggered(ConversionTrigger), | ||
| 183 | } | ||
| 184 | |||
| 185 | impl<'d, T: Instance> Adc<'d, T> { | ||
| 186 | #[cfg(any( | ||
| 187 | adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v3, adc_v4, adc_wba, adc_c0 | ||
| 188 | ))] | ||
| 189 | /// Read an ADC pin. | ||
| 190 | pub fn blocking_read( | ||
| 191 | &mut self, | ||
| 192 | channel: &mut impl AdcChannel<T>, | ||
| 193 | sample_time: <T::Regs as BasicAdcRegs>::SampleTime, | ||
| 194 | ) -> u16 { | ||
| 195 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_wba))] | ||
| 196 | channel.setup(); | ||
| 197 | |||
| 198 | // Ensure no conversions are ongoing | ||
| 199 | T::regs().stop(); | ||
| 200 | #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h7rs, adc_u0, adc_u5, adc_wba, adc_c0))] | ||
| 201 | T::regs().enable(); | ||
| 202 | T::regs().configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); | ||
| 203 | |||
| 204 | // On chips with differential channels, enable after configure_sequence to allow setting differential channels | ||
| 205 | // | ||
| 206 | // TODO: If hardware allows, enable after configure_sequence on all chips | ||
| 207 | #[cfg(any(adc_g4, adc_h5))] | ||
| 208 | T::regs().enable(); | ||
| 209 | T::regs().convert(); | ||
| 210 | |||
| 211 | unsafe { core::ptr::read_volatile(T::regs().data()) } | ||
| 212 | } | ||
| 213 | |||
| 214 | #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] | ||
| 215 | /// Read one or multiple ADC regular channels using DMA. | ||
| 216 | /// | ||
| 217 | /// `sequence` iterator and `readings` must have the same length. | ||
| 218 | /// | ||
| 219 | /// Example | ||
| 220 | /// ```rust,ignore | ||
| 221 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 222 | /// | ||
| 223 | /// let mut adc = Adc::new(p.ADC1); | ||
| 224 | /// let mut adc_pin0 = p.PA0.into(); | ||
| 225 | /// let mut adc_pin1 = p.PA1.into(); | ||
| 226 | /// let mut measurements = [0u16; 2]; | ||
| 227 | /// | ||
| 228 | /// adc.read( | ||
| 229 | /// p.DMA1_CH2.reborrow(), | ||
| 230 | /// [ | ||
| 231 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 232 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 233 | /// ] | ||
| 234 | /// .into_iter(), | ||
| 235 | /// &mut measurements, | ||
| 236 | /// ) | ||
| 237 | /// .await; | ||
| 238 | /// defmt::info!("measurements: {}", measurements); | ||
| 239 | /// ``` | ||
| 240 | /// | ||
| 241 | /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use | ||
| 242 | /// `into_ring_buffered`, `into_ring_buffered_and_injected` | ||
| 243 | /// | ||
| 244 | /// Note: Depending on hardware limitations, this method may require channels to be passed | ||
| 245 | /// in order or require the sequence to have the same sample time for all channnels, depending | ||
| 246 | /// on the number and properties of the channels in the sequence. This method will panic if | ||
| 247 | /// the hardware cannot deliver the requested configuration. | ||
| 248 | pub async fn read<'a, 'b: 'a>( | ||
| 249 | &mut self, | ||
| 250 | rx_dma: embassy_hal_internal::Peri<'_, impl RxDma<T>>, | ||
| 251 | sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<'b, T>, <T::Regs as BasicAdcRegs>::SampleTime)>, | ||
| 252 | readings: &mut [u16], | ||
| 253 | ) { | ||
| 254 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 255 | assert!( | ||
| 256 | sequence.len() == readings.len(), | ||
| 257 | "Sequence length must be equal to readings length" | ||
| 258 | ); | ||
| 259 | assert!( | ||
| 260 | sequence.len() <= 16, | ||
| 261 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 262 | ); | ||
| 263 | |||
| 264 | // Ensure no conversions are ongoing | ||
| 265 | T::regs().stop(); | ||
| 266 | #[cfg(any(adc_g0, adc_v3, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] | ||
| 267 | T::regs().enable(); | ||
| 268 | |||
| 269 | T::regs().configure_sequence( | ||
| 270 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 271 | ); | ||
| 272 | |||
| 273 | // On chips with differential channels, enable after configure_sequence to allow setting differential channels | ||
| 274 | // | ||
| 275 | // TODO: If hardware allows, enable after configure_sequence on all chips | ||
| 276 | #[cfg(any(adc_g4, adc_h5))] | ||
| 277 | T::regs().enable(); | ||
| 278 | T::regs().configure_dma(ConversionMode::Singular); | ||
| 279 | |||
| 280 | let request = rx_dma.request(); | ||
| 281 | let transfer = | ||
| 282 | unsafe { crate::dma::Transfer::new_read(rx_dma, request, T::regs().data(), readings, Default::default()) }; | ||
| 283 | |||
| 284 | T::regs().start(); | ||
| 285 | |||
| 286 | // Wait for conversion sequence to finish. | ||
| 287 | transfer.await; | ||
| 288 | |||
| 289 | // Ensure conversions are finished. | ||
| 290 | T::regs().stop(); | ||
| 291 | } | ||
| 292 | |||
| 293 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 294 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 295 | /// | ||
| 296 | /// Use the [`read`] method to retrieve measurements from the DMA ring buffer. The read buffer | ||
| 297 | /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended | ||
| 298 | /// to configure `dma_buf` as a double buffer so that one half can be read while the other half | ||
| 299 | /// is being filled by the DMA, preventing data loss. The trigger period of the ADC effectively | ||
| 300 | /// defines the period at which the buffer should be read. | ||
| 301 | /// | ||
| 302 | /// If continous conversion mode is selected, the provided `dma_buf` must be large enough to prevent | ||
| 303 | /// DMA buffer overruns. Its length should be a multiple of the number of ADC channels being measured. | ||
| 304 | /// For example, if 3 channels are measured and you want to store 40 samples per channel, | ||
| 305 | /// the buffer length should be `3 * 40 = 120`. | ||
| 306 | /// | ||
| 307 | /// # Parameters | ||
| 308 | /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer. | ||
| 309 | /// - `dma_buf`: The buffer where DMA stores ADC samples. | ||
| 310 | /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions. | ||
| 311 | /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered). | ||
| 312 | /// | ||
| 313 | /// # Returns | ||
| 314 | /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. | ||
| 315 | /// | ||
| 316 | /// Note: Depending on hardware limitations, this method may require channels to be passed | ||
| 317 | /// in order or require the sequence to have the same sample time for all channnels, depending | ||
| 318 | /// on the number and properties of the channels in the sequence. This method will panic if | ||
| 319 | /// the hardware cannot deliver the requested configuration. | ||
| 320 | pub fn into_ring_buffered<'a, 'b>( | ||
| 321 | self, | ||
| 322 | dma: embassy_hal_internal::Peri<'a, impl RxDma<T>>, | ||
| 323 | dma_buf: &'a mut [u16], | ||
| 324 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<'b, T>, <T::Regs as BasicAdcRegs>::SampleTime)>, | ||
| 325 | mode: RegularConversionMode, | ||
| 326 | ) -> RingBufferedAdc<'a, T> { | ||
| 327 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 328 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 329 | assert!( | ||
| 330 | sequence.len() <= 16, | ||
| 331 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 332 | ); | ||
| 333 | // Ensure no conversions are ongoing | ||
| 334 | T::regs().stop(); | ||
| 335 | #[cfg(any(adc_g0, adc_v3, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] | ||
| 336 | T::regs().enable(); | ||
| 337 | |||
| 338 | T::regs().configure_sequence( | ||
| 339 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 340 | ); | ||
| 341 | |||
| 342 | // On chips with differential channels, enable after configure_sequence to allow setting differential channels | ||
| 343 | // | ||
| 344 | // TODO: If hardware allows, enable after configure_sequence on all chips | ||
| 345 | #[cfg(any(adc_g4, adc_h5))] | ||
| 346 | T::regs().enable(); | ||
| 347 | T::regs().configure_dma(ConversionMode::Repeated(mode)); | ||
| 348 | |||
| 349 | core::mem::forget(self); | ||
| 350 | |||
| 351 | RingBufferedAdc::new(dma, dma_buf) | ||
| 352 | } | ||
| 353 | } | ||
| 354 | |||
| 355 | pub(self) trait SpecialChannel {} | ||
| 356 | |||
| 357 | /// Implemented for ADCs that have a special channel | ||
| 358 | trait SealedSpecialConverter<T: SpecialChannel + Sized> { | ||
| 359 | const CHANNEL: u8; | ||
| 360 | } | ||
| 361 | |||
| 362 | #[allow(private_bounds)] | ||
| 363 | pub trait SpecialConverter<T: SpecialChannel + Sized>: SealedSpecialConverter<T> {} | ||
| 364 | |||
| 365 | impl<C: SpecialChannel + Sized, T: SealedSpecialConverter<C>> SpecialConverter<C> for T {} | ||
| 366 | |||
| 367 | impl<C: SpecialChannel, T: Instance + SealedSpecialConverter<C>> AdcChannel<T> for C {} | ||
| 368 | impl<C: SpecialChannel, T: Instance + SealedSpecialConverter<C>> SealedAdcChannel<T> for C { | ||
| 369 | fn channel(&self) -> u8 { | ||
| 370 | T::CHANNEL | ||
| 98 | } | 371 | } |
| 99 | } | 372 | } |
| 100 | 373 | ||
| 374 | pub struct VrefInt; | ||
| 375 | impl SpecialChannel for VrefInt {} | ||
| 376 | |||
| 377 | impl VrefInt { | ||
| 378 | #[cfg(any(adc_f3v1, adc_f3v2))] | ||
| 379 | /// The value that vref would be if vdda was at 3300mv | ||
| 380 | pub fn calibrated_value(&self) -> u16 { | ||
| 381 | crate::pac::VREFINTCAL.data().read() | ||
| 382 | } | ||
| 383 | } | ||
| 384 | |||
| 385 | /// Internal temperature channel. | ||
| 386 | pub struct Temperature; | ||
| 387 | impl SpecialChannel for Temperature {} | ||
| 388 | |||
| 389 | /// Internal battery voltage channel. | ||
| 390 | pub struct Vbat; | ||
| 391 | impl SpecialChannel for Vbat {} | ||
| 392 | |||
| 393 | /// Vcore channel. | ||
| 394 | pub struct Vcore; | ||
| 395 | impl SpecialChannel for Vcore {} | ||
| 396 | |||
| 397 | /// Internal dac channel. | ||
| 398 | pub struct Dac; | ||
| 399 | impl SpecialChannel for Dac {} | ||
| 400 | |||
| 101 | /// ADC instance. | 401 | /// ADC instance. |
| 102 | #[cfg(not(any( | 402 | #[cfg(not(any( |
| 103 | adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_g4, adc_f3v1, adc_f3v2, adc_g0, adc_u0, adc_h5, adc_h7rs, | 403 | adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_g4, adc_f3v1, adc_f3v2, adc_g0, adc_u0, adc_h5, adc_h7rs, |
| @@ -121,12 +421,16 @@ pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeri | |||
| 121 | #[allow(private_bounds)] | 421 | #[allow(private_bounds)] |
| 122 | pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized { | 422 | pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized { |
| 123 | #[allow(unused_mut)] | 423 | #[allow(unused_mut)] |
| 124 | fn degrade_adc(mut self) -> AnyAdcChannel<T> { | 424 | fn degrade_adc<'a>(mut self) -> AnyAdcChannel<'a, T> |
| 125 | #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] | 425 | where |
| 426 | Self: 'a, | ||
| 427 | { | ||
| 428 | #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_wba))] | ||
| 126 | self.setup(); | 429 | self.setup(); |
| 127 | 430 | ||
| 128 | AnyAdcChannel { | 431 | AnyAdcChannel { |
| 129 | channel: self.channel(), | 432 | channel: self.channel(), |
| 433 | is_differential: self.is_differential(), | ||
| 130 | _phantom: PhantomData, | 434 | _phantom: PhantomData, |
| 131 | } | 435 | } |
| 132 | } | 436 | } |
| @@ -136,34 +440,57 @@ pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized { | |||
| 136 | /// | 440 | /// |
| 137 | /// This is useful in scenarios where you need the ADC channels to have the same type, such as | 441 | /// This is useful in scenarios where you need the ADC channels to have the same type, such as |
| 138 | /// storing them in an array. | 442 | /// storing them in an array. |
| 139 | pub struct AnyAdcChannel<T> { | 443 | pub struct AnyAdcChannel<'a, T> { |
| 140 | channel: u8, | 444 | channel: u8, |
| 141 | _phantom: PhantomData<T>, | 445 | is_differential: bool, |
| 446 | _phantom: PhantomData<&'a mut T>, | ||
| 142 | } | 447 | } |
| 143 | impl_peripheral!(AnyAdcChannel<T: Instance>); | 448 | impl<T: Instance> AdcChannel<T> for AnyAdcChannel<'_, T> {} |
| 144 | impl<T: Instance> AdcChannel<T> for AnyAdcChannel<T> {} | 449 | impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<'_, T> { |
| 145 | impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<T> { | ||
| 146 | fn channel(&self) -> u8 { | 450 | fn channel(&self) -> u8 { |
| 147 | self.channel | 451 | self.channel |
| 148 | } | 452 | } |
| 453 | |||
| 454 | fn is_differential(&self) -> bool { | ||
| 455 | self.is_differential | ||
| 456 | } | ||
| 149 | } | 457 | } |
| 150 | 458 | ||
| 151 | impl<T> AnyAdcChannel<T> { | 459 | impl<T> AnyAdcChannel<'_, T> { |
| 152 | #[allow(unused)] | 460 | #[allow(unused)] |
| 153 | pub fn get_hw_channel(&self) -> u8 { | 461 | pub fn get_hw_channel(&self) -> u8 { |
| 154 | self.channel | 462 | self.channel |
| 155 | } | 463 | } |
| 156 | } | 464 | } |
| 465 | |||
| 466 | #[cfg(not(adc_wba))] | ||
| 467 | impl BasicAdcRegs for crate::pac::adc::Adc { | ||
| 468 | type SampleTime = SampleTime; | ||
| 469 | } | ||
| 470 | |||
| 471 | #[cfg(any(adc_wba, adc_u5))] | ||
| 472 | impl BasicAdcRegs for crate::pac::adc::Adc4 { | ||
| 473 | type SampleTime = Adc4SampleTime; | ||
| 474 | } | ||
| 475 | |||
| 157 | #[cfg(adc_wba)] | 476 | #[cfg(adc_wba)] |
| 158 | foreach_adc!( | 477 | foreach_adc!( |
| 159 | (ADC4, $common_inst:ident, $clock:ident) => { | 478 | (ADC4, $common_inst:ident, $clock:ident) => { |
| 160 | impl crate::adc::adc4::SealedInstance for peripherals::ADC4 { | 479 | impl crate::adc::BasicInstance for peripherals::ADC4 { |
| 161 | fn regs() -> crate::pac::adc::Adc4 { | 480 | type Regs = crate::pac::adc::Adc4; |
| 481 | } | ||
| 482 | |||
| 483 | impl crate::adc::SealedInstance for peripherals::ADC4 { | ||
| 484 | fn regs() -> Self::Regs { | ||
| 162 | crate::pac::ADC4 | 485 | crate::pac::ADC4 |
| 163 | } | 486 | } |
| 487 | |||
| 488 | fn common_regs() -> crate::pac::adccommon::AdcCommon { | ||
| 489 | return crate::pac::$common_inst | ||
| 490 | } | ||
| 164 | } | 491 | } |
| 165 | 492 | ||
| 166 | impl crate::adc::adc4::Instance for peripherals::ADC4 { | 493 | impl crate::adc::Instance for peripherals::ADC4 { |
| 167 | type Interrupt = crate::_generated::peripheral_interrupts::ADC4::GLOBAL; | 494 | type Interrupt = crate::_generated::peripheral_interrupts::ADC4::GLOBAL; |
| 168 | } | 495 | } |
| 169 | }; | 496 | }; |
| @@ -188,20 +515,32 @@ foreach_adc!( | |||
| 188 | #[cfg(adc_u5)] | 515 | #[cfg(adc_u5)] |
| 189 | foreach_adc!( | 516 | foreach_adc!( |
| 190 | (ADC4, $common_inst:ident, $clock:ident) => { | 517 | (ADC4, $common_inst:ident, $clock:ident) => { |
| 191 | impl crate::adc::adc4::SealedInstance for peripherals::ADC4 { | 518 | impl crate::adc::BasicInstance for peripherals::ADC4 { |
| 192 | fn regs() -> crate::pac::adc::Adc4 { | 519 | type Regs = crate::pac::adc::Adc4; |
| 520 | } | ||
| 521 | |||
| 522 | impl crate::adc::SealedInstance for peripherals::ADC4 { | ||
| 523 | fn regs() -> Self::Regs { | ||
| 193 | crate::pac::ADC4 | 524 | crate::pac::ADC4 |
| 194 | } | 525 | } |
| 526 | |||
| 527 | fn common_regs() -> crate::pac::adccommon::AdcCommon { | ||
| 528 | return crate::pac::$common_inst | ||
| 529 | } | ||
| 195 | } | 530 | } |
| 196 | 531 | ||
| 197 | impl crate::adc::adc4::Instance for peripherals::ADC4 { | 532 | impl crate::adc::Instance for peripherals::ADC4 { |
| 198 | type Interrupt = crate::_generated::peripheral_interrupts::ADC4::GLOBAL; | 533 | type Interrupt = crate::_generated::peripheral_interrupts::ADC4::GLOBAL; |
| 199 | } | 534 | } |
| 200 | }; | 535 | }; |
| 201 | 536 | ||
| 202 | ($inst:ident, $common_inst:ident, $clock:ident) => { | 537 | ($inst:ident, $common_inst:ident, $clock:ident) => { |
| 538 | impl crate::adc::BasicInstance for peripherals::$inst { | ||
| 539 | type Regs = crate::pac::adc::Adc; | ||
| 540 | } | ||
| 541 | |||
| 203 | impl crate::adc::SealedInstance for peripherals::$inst { | 542 | impl crate::adc::SealedInstance for peripherals::$inst { |
| 204 | fn regs() -> crate::pac::adc::Adc { | 543 | fn regs() -> Self::Regs { |
| 205 | crate::pac::$inst | 544 | crate::pac::$inst |
| 206 | } | 545 | } |
| 207 | 546 | ||
| @@ -219,14 +558,23 @@ foreach_adc!( | |||
| 219 | #[cfg(not(any(adc_u5, adc_wba)))] | 558 | #[cfg(not(any(adc_u5, adc_wba)))] |
| 220 | foreach_adc!( | 559 | foreach_adc!( |
| 221 | ($inst:ident, $common_inst:ident, $clock:ident) => { | 560 | ($inst:ident, $common_inst:ident, $clock:ident) => { |
| 561 | impl crate::adc::BasicInstance for peripherals::$inst { | ||
| 562 | #[cfg(any( | ||
| 563 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 564 | ))] | ||
| 565 | type Regs = crate::pac::adc::Adc; | ||
| 566 | } | ||
| 567 | |||
| 222 | impl crate::adc::SealedInstance for peripherals::$inst { | 568 | impl crate::adc::SealedInstance for peripherals::$inst { |
| 223 | #[cfg(not(adc_wba))] | 569 | #[cfg(any( |
| 224 | fn regs() -> crate::pac::adc::Adc { | 570 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 |
| 571 | ))] | ||
| 572 | fn regs() -> Self::Regs { | ||
| 225 | crate::pac::$inst | 573 | crate::pac::$inst |
| 226 | } | 574 | } |
| 227 | 575 | ||
| 228 | #[cfg(adc_wba)] | 576 | #[cfg(any(adc_f1, adc_f3v1, adc_f3v2, adc_v1, adc_l0))] |
| 229 | fn regs() -> crate::pac::adc::Adc4 { | 577 | fn regs() -> crate::pac::adc::Adc { |
| 230 | crate::pac::$inst | 578 | crate::pac::$inst |
| 231 | } | 579 | } |
| 232 | 580 | ||
| @@ -252,7 +600,7 @@ macro_rules! impl_adc_pin { | |||
| 252 | ($inst:ident, $pin:ident, $ch:expr) => { | 600 | ($inst:ident, $pin:ident, $ch:expr) => { |
| 253 | impl crate::adc::AdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> {} | 601 | impl crate::adc::AdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> {} |
| 254 | impl crate::adc::SealedAdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> { | 602 | impl crate::adc::SealedAdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> { |
| 255 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] | 603 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_wba))] |
| 256 | fn setup(&mut self) { | 604 | fn setup(&mut self) { |
| 257 | <crate::peripherals::$pin as crate::gpio::SealedPin>::set_as_analog(self); | 605 | <crate::peripherals::$pin as crate::gpio::SealedPin>::set_as_analog(self); |
| 258 | } | 606 | } |
| @@ -264,6 +612,39 @@ macro_rules! impl_adc_pin { | |||
| 264 | }; | 612 | }; |
| 265 | } | 613 | } |
| 266 | 614 | ||
| 615 | #[allow(unused_macros)] | ||
| 616 | macro_rules! impl_adc_pair { | ||
| 617 | ($inst:ident, $pin:ident, $npin:ident, $ch:expr) => { | ||
| 618 | impl crate::adc::AdcChannel<peripherals::$inst> | ||
| 619 | for ( | ||
| 620 | crate::Peri<'_, crate::peripherals::$pin>, | ||
| 621 | crate::Peri<'_, crate::peripherals::$npin>, | ||
| 622 | ) | ||
| 623 | { | ||
| 624 | } | ||
| 625 | impl crate::adc::SealedAdcChannel<peripherals::$inst> | ||
| 626 | for ( | ||
| 627 | crate::Peri<'_, crate::peripherals::$pin>, | ||
| 628 | crate::Peri<'_, crate::peripherals::$npin>, | ||
| 629 | ) | ||
| 630 | { | ||
| 631 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_wba))] | ||
| 632 | fn setup(&mut self) { | ||
| 633 | <crate::peripherals::$pin as crate::gpio::SealedPin>::set_as_analog(&mut self.0); | ||
| 634 | <crate::peripherals::$npin as crate::gpio::SealedPin>::set_as_analog(&mut self.1); | ||
| 635 | } | ||
| 636 | |||
| 637 | fn channel(&self) -> u8 { | ||
| 638 | $ch | ||
| 639 | } | ||
| 640 | |||
| 641 | fn is_differential(&self) -> bool { | ||
| 642 | true | ||
| 643 | } | ||
| 644 | } | ||
| 645 | }; | ||
| 646 | } | ||
| 647 | |||
| 267 | /// Get the maximum reading value for this resolution. | 648 | /// Get the maximum reading value for this resolution. |
| 268 | /// | 649 | /// |
| 269 | /// This is `2**n - 1`. | 650 | /// This is `2**n - 1`. |
diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs new file mode 100644 index 000000000..242a1a89c --- /dev/null +++ b/embassy-stm32/src/adc/ringbuffered.rs | |||
| @@ -0,0 +1,180 @@ | |||
| 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::AdcRegs; | ||
| 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 = unsafe { ReadableRingBuffer::new(dma, request, T::regs().data(), dma_buf, opts) }; | ||
| 35 | |||
| 36 | Self { | ||
| 37 | _phantom: PhantomData, | ||
| 38 | ring_buf, | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | /// Turns on ADC if it is not already turned on and starts continuous DMA transfer. | ||
| 43 | pub fn start(&mut self) { | ||
| 44 | compiler_fence(Ordering::SeqCst); | ||
| 45 | self.ring_buf.start(); | ||
| 46 | |||
| 47 | T::regs().start(); | ||
| 48 | } | ||
| 49 | |||
| 50 | pub fn stop(&mut self) { | ||
| 51 | self.ring_buf.request_pause(); | ||
| 52 | |||
| 53 | compiler_fence(Ordering::SeqCst); | ||
| 54 | } | ||
| 55 | |||
| 56 | pub fn clear(&mut self) { | ||
| 57 | self.ring_buf.clear(); | ||
| 58 | } | ||
| 59 | |||
| 60 | /// Reads measurements from the DMA ring buffer. | ||
| 61 | /// | ||
| 62 | /// This method fills the provided `measurements` array with ADC readings from the DMA buffer. | ||
| 63 | /// The length of the `measurements` array should be exactly half of the DMA buffer length. | ||
| 64 | /// Because interrupts are only generated if half or full DMA transfer completes. | ||
| 65 | /// | ||
| 66 | /// Each call to `read` will populate the `measurements` array in the same order as the channels | ||
| 67 | /// defined with `sequence`. There will be many sequences worth of measurements in this array | ||
| 68 | /// because it only returns if at least half of the DMA buffer is filled. For example if 2 | ||
| 69 | /// channels are sampled `measurements` contain: `[sq0 sq1 sq0 sq1 sq0 sq1 ..]`. | ||
| 70 | /// | ||
| 71 | /// Note that the ADC Datarate can be very fast, it is suggested to use DMA mode inside tightly | ||
| 72 | /// running tasks. Otherwise, you'll see constant Overrun errors occurring, this means that | ||
| 73 | /// you're sampling too quickly for the task to handle, and you may need to increase the buffer size. | ||
| 74 | /// Example: | ||
| 75 | /// ```rust,ignore | ||
| 76 | /// const DMA_BUF_LEN: usize = 120; | ||
| 77 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 78 | /// | ||
| 79 | /// let mut adc = Adc::new(p.ADC1); | ||
| 80 | /// let mut adc_pin0 = p.PA0.degrade_adc(); | ||
| 81 | /// let mut adc_pin1 = p.PA1.degrade_adc(); | ||
| 82 | /// let adc_dma_buf = [0u16; DMA_BUF_LEN]; | ||
| 83 | /// | ||
| 84 | /// let mut ring_buffered_adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered( | ||
| 85 | /// p.DMA2_CH0, | ||
| 86 | /// adc_dma_buf, [ | ||
| 87 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 88 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 89 | /// ].into_iter()); | ||
| 90 | /// | ||
| 91 | /// | ||
| 92 | /// let mut measurements = [0u16; DMA_BUF_LEN / 2]; | ||
| 93 | /// loop { | ||
| 94 | /// match ring_buffered_adc.read(&mut measurements).await { | ||
| 95 | /// Ok(_) => { | ||
| 96 | /// defmt::info!("adc1: {}", measurements); | ||
| 97 | /// } | ||
| 98 | /// Err(e) => { | ||
| 99 | /// defmt::warn!("Error: {:?}", e); | ||
| 100 | /// } | ||
| 101 | /// } | ||
| 102 | /// } | ||
| 103 | /// ``` | ||
| 104 | /// | ||
| 105 | /// | ||
| 106 | /// [`teardown_adc`]: #method.teardown_adc | ||
| 107 | /// [`start_continuous_sampling`]: #method.start_continuous_sampling | ||
| 108 | pub async fn read(&mut self, measurements: &mut [u16]) -> Result<usize, OverrunError> { | ||
| 109 | assert_eq!( | ||
| 110 | self.ring_buf.capacity() / 2, | ||
| 111 | measurements.len(), | ||
| 112 | "Buffer size must be half the size of the ring buffer" | ||
| 113 | ); | ||
| 114 | |||
| 115 | if !self.ring_buf.is_running() { | ||
| 116 | self.start(); | ||
| 117 | } | ||
| 118 | |||
| 119 | // #[cfg(adc_v2)] | ||
| 120 | // { | ||
| 121 | // // Clear overrun flag if set. | ||
| 122 | // if T::regs().sr().read().ovr() { | ||
| 123 | // self.stop(); | ||
| 124 | // | ||
| 125 | // return Err(OverrunError); | ||
| 126 | // } | ||
| 127 | // } | ||
| 128 | |||
| 129 | self.ring_buf.read_exact(measurements).await.map_err(|_| OverrunError) | ||
| 130 | } | ||
| 131 | |||
| 132 | /// Read bytes that are readily available in the ring buffer. | ||
| 133 | /// If no bytes are currently available in the buffer the call waits until the some | ||
| 134 | /// bytes are available (at least one byte and at most half the buffer size) | ||
| 135 | /// | ||
| 136 | /// Background receive is started if `start_continuous_sampling()` has not been previously called. | ||
| 137 | /// | ||
| 138 | /// Receive in the background is terminated if an error is returned. | ||
| 139 | /// It must then manually be started again by calling `start_continuous_sampling()` or by re-calling `blocking_read()`. | ||
| 140 | pub fn blocking_read(&mut self, buf: &mut [u16]) -> Result<usize, OverrunError> { | ||
| 141 | if !self.ring_buf.is_running() { | ||
| 142 | self.start(); | ||
| 143 | } | ||
| 144 | |||
| 145 | // #[cfg(adc_v2)] | ||
| 146 | // { | ||
| 147 | // // Clear overrun flag if set. | ||
| 148 | // if T::regs().sr().read().ovr() { | ||
| 149 | // self.stop(); | ||
| 150 | // | ||
| 151 | // return Err(OverrunError); | ||
| 152 | // } | ||
| 153 | // } | ||
| 154 | |||
| 155 | loop { | ||
| 156 | match self.ring_buf.read(buf) { | ||
| 157 | Ok((0, _)) => {} | ||
| 158 | Ok((len, _)) => { | ||
| 159 | return Ok(len); | ||
| 160 | } | ||
| 161 | Err(_) => { | ||
| 162 | self.ring_buf.request_pause(); | ||
| 163 | |||
| 164 | return Err(OverrunError); | ||
| 165 | } | ||
| 166 | } | ||
| 167 | } | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | impl<T: Instance> Drop for RingBufferedAdc<'_, T> { | ||
| 172 | fn drop(&mut self) { | ||
| 173 | T::regs().stop(); | ||
| 174 | |||
| 175 | compiler_fence(Ordering::SeqCst); | ||
| 176 | |||
| 177 | self.ring_buf.request_pause(); | ||
| 178 | rcc::disable::<T>(); | ||
| 179 | } | ||
| 180 | } | ||
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..58c30935f 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::SealedSpecialConverter<super::Vbat> 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::SealedSpecialConverter<super::VrefInt> 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::SealedSpecialConverter<super::VrefInt> 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::SealedSpecialConverter<super::Temperature> 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..b026383d5 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs | |||
| @@ -1,23 +1,40 @@ | |||
| 1 | use super::blocking_delay_us; | 1 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 2 | use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; | 2 | |
| 3 | use crate::peripherals::ADC1; | 3 | use super::{ConversionMode, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 4 | use crate::adc::{Adc, AdcRegs, Instance, Resolution, SampleTime}; | ||
| 5 | use crate::pac::adc::vals; | ||
| 6 | pub use crate::pac::adccommon::vals::Adcpre; | ||
| 4 | use crate::time::Hertz; | 7 | use crate::time::Hertz; |
| 5 | use crate::{rcc, Peri}; | 8 | use crate::{Peri, rcc}; |
| 6 | 9 | ||
| 7 | mod ringbuffered_v2; | 10 | fn clear_interrupt_flags(r: crate::pac::adc::Adc) { |
| 8 | pub use ringbuffered_v2::{RingBufferedAdc, Sequence}; | 11 | r.sr().modify(|regs| { |
| 12 | regs.set_eoc(false); | ||
| 13 | regs.set_ovr(false); | ||
| 14 | }); | ||
| 15 | } | ||
| 9 | 16 | ||
| 10 | /// Default VREF voltage used for sample conversion to millivolts. | 17 | /// Default VREF voltage used for sample conversion to millivolts. |
| 11 | pub const VREF_DEFAULT_MV: u32 = 3300; | 18 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| 12 | /// VREF voltage used for factory calibration of VREFINTCAL register. | 19 | /// VREF voltage used for factory calibration of VREFINTCAL register. |
| 13 | pub const VREF_CALIB_MV: u32 = 3300; | 20 | pub const VREF_CALIB_MV: u32 = 3300; |
| 14 | 21 | ||
| 15 | pub struct VrefInt; | 22 | impl super::SealedSpecialConverter<super::VrefInt> for crate::peripherals::ADC1 { |
| 16 | impl AdcChannel<ADC1> for VrefInt {} | 23 | const CHANNEL: u8 = 17; |
| 17 | impl super::SealedAdcChannel<ADC1> for VrefInt { | 24 | } |
| 18 | fn channel(&self) -> u8 { | 25 | |
| 19 | 17 | 26 | #[cfg(any(stm32f2, stm32f40x, stm32f41x))] |
| 20 | } | 27 | impl super::SealedSpecialConverter<super::Temperature> for crate::peripherals::ADC1 { |
| 28 | const CHANNEL: u8 = 16; | ||
| 29 | } | ||
| 30 | |||
| 31 | #[cfg(not(any(stm32f2, stm32f40x, stm32f41x)))] | ||
| 32 | impl super::SealedSpecialConverter<super::Temperature> for crate::peripherals::ADC1 { | ||
| 33 | const CHANNEL: u8 = 18; | ||
| 34 | } | ||
| 35 | |||
| 36 | impl super::SealedSpecialConverter<super::Vbat> for crate::peripherals::ADC1 { | ||
| 37 | const CHANNEL: u8 = 18; | ||
| 21 | } | 38 | } |
| 22 | 39 | ||
| 23 | impl VrefInt { | 40 | impl VrefInt { |
| @@ -27,20 +44,6 @@ impl VrefInt { | |||
| 27 | } | 44 | } |
| 28 | } | 45 | } |
| 29 | 46 | ||
| 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 { | 47 | impl Temperature { |
| 45 | /// Time needed for temperature sensor readings to stabilize | 48 | /// Time needed for temperature sensor readings to stabilize |
| 46 | pub fn start_time_us() -> u32 { | 49 | pub fn start_time_us() -> u32 { |
| @@ -48,76 +51,174 @@ impl Temperature { | |||
| 48 | } | 51 | } |
| 49 | } | 52 | } |
| 50 | 53 | ||
| 51 | pub struct Vbat; | 54 | fn from_pclk2(freq: Hertz) -> Adcpre { |
| 52 | impl AdcChannel<ADC1> for Vbat {} | 55 | // Datasheet for F2 specifies min frequency 0.6 MHz, and max 30 MHz (with VDDA 2.4-3.6V). |
| 53 | impl super::SealedAdcChannel<ADC1> for Vbat { | 56 | #[cfg(stm32f2)] |
| 54 | fn channel(&self) -> u8 { | 57 | const MAX_FREQUENCY: Hertz = Hertz(30_000_000); |
| 55 | 18 | 58 | // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. |
| 59 | #[cfg(not(stm32f2))] | ||
| 60 | const MAX_FREQUENCY: Hertz = Hertz(36_000_000); | ||
| 61 | let raw_div = rcc::raw_prescaler(freq.0, MAX_FREQUENCY.0); | ||
| 62 | match raw_div { | ||
| 63 | 0..=1 => Adcpre::DIV2, | ||
| 64 | 2..=3 => Adcpre::DIV4, | ||
| 65 | 4..=5 => Adcpre::DIV6, | ||
| 66 | 6..=7 => Adcpre::DIV8, | ||
| 67 | _ => panic!("Selected PCLK2 frequency is too high for ADC with largest possible prescaler."), | ||
| 56 | } | 68 | } |
| 57 | } | 69 | } |
| 58 | 70 | ||
| 59 | enum Prescaler { | 71 | /// ADC configuration |
| 60 | Div2, | 72 | #[derive(Default)] |
| 61 | Div4, | 73 | pub struct AdcConfig { |
| 62 | Div6, | 74 | pub resolution: Option<Resolution>, |
| 63 | Div8, | ||
| 64 | } | 75 | } |
| 65 | 76 | ||
| 66 | impl Prescaler { | 77 | impl super::AdcRegs for crate::pac::adc::Adc { |
| 67 | fn from_pclk2(freq: Hertz) -> Self { | 78 | fn data(&self) -> *mut u16 { |
| 68 | // Datasheet for F2 specifies min frequency 0.6 MHz, and max 30 MHz (with VDDA 2.4-3.6V). | 79 | crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16 |
| 69 | #[cfg(stm32f2)] | 80 | } |
| 70 | const MAX_FREQUENCY: Hertz = Hertz(30_000_000); | 81 | |
| 71 | // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. | 82 | fn enable(&self) { |
| 72 | #[cfg(not(stm32f2))] | 83 | self.cr2().modify(|reg| { |
| 73 | const MAX_FREQUENCY: Hertz = Hertz(36_000_000); | 84 | reg.set_adon(true); |
| 74 | let raw_div = freq.0 / MAX_FREQUENCY.0; | 85 | }); |
| 75 | match raw_div { | 86 | |
| 76 | 0..=1 => Self::Div2, | 87 | blocking_delay_us(3); |
| 77 | 2..=3 => Self::Div4, | 88 | } |
| 78 | 4..=5 => Self::Div6, | 89 | |
| 79 | 6..=7 => Self::Div8, | 90 | fn start(&self) { |
| 80 | _ => panic!("Selected PCLK2 frequency is too high for ADC with largest possible prescaler."), | 91 | // Begin ADC conversions |
| 92 | self.cr2().modify(|reg| { | ||
| 93 | reg.set_swstart(true); | ||
| 94 | }); | ||
| 95 | } | ||
| 96 | |||
| 97 | fn stop(&self) { | ||
| 98 | let r = self; | ||
| 99 | |||
| 100 | // Stop ADC | ||
| 101 | r.cr2().modify(|reg| { | ||
| 102 | // Stop ADC | ||
| 103 | reg.set_swstart(false); | ||
| 104 | // Stop ADC | ||
| 105 | reg.set_adon(false); | ||
| 106 | // Stop DMA | ||
| 107 | reg.set_dma(false); | ||
| 108 | }); | ||
| 109 | |||
| 110 | r.cr1().modify(|w| { | ||
| 111 | // Disable interrupt for end of conversion | ||
| 112 | w.set_eocie(false); | ||
| 113 | // Disable interrupt for overrun | ||
| 114 | w.set_ovrie(false); | ||
| 115 | }); | ||
| 116 | |||
| 117 | clear_interrupt_flags(*r); | ||
| 118 | |||
| 119 | compiler_fence(Ordering::SeqCst); | ||
| 120 | } | ||
| 121 | |||
| 122 | fn convert(&self) { | ||
| 123 | // clear end of conversion flag | ||
| 124 | self.sr().modify(|reg| { | ||
| 125 | reg.set_eoc(false); | ||
| 126 | }); | ||
| 127 | |||
| 128 | // Start conversion | ||
| 129 | self.cr2().modify(|reg| { | ||
| 130 | reg.set_swstart(true); | ||
| 131 | }); | ||
| 132 | |||
| 133 | while self.sr().read().strt() == false { | ||
| 134 | // spin //wait for actual start | ||
| 135 | } | ||
| 136 | while self.sr().read().eoc() == false { | ||
| 137 | // spin //wait for finish | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | fn configure_dma(&self, conversion_mode: ConversionMode) { | ||
| 142 | match conversion_mode { | ||
| 143 | ConversionMode::Repeated(_) => { | ||
| 144 | let r = self; | ||
| 145 | |||
| 146 | // Clear all interrupts | ||
| 147 | r.sr().modify(|regs| { | ||
| 148 | regs.set_eoc(false); | ||
| 149 | regs.set_ovr(false); | ||
| 150 | regs.set_strt(false); | ||
| 151 | }); | ||
| 152 | |||
| 153 | r.cr1().modify(|w| { | ||
| 154 | // Enable interrupt for end of conversion | ||
| 155 | w.set_eocie(true); | ||
| 156 | // Enable interrupt for overrun | ||
| 157 | w.set_ovrie(true); | ||
| 158 | // Scanning converisons of multiple channels | ||
| 159 | w.set_scan(true); | ||
| 160 | // Continuous conversion mode | ||
| 161 | w.set_discen(false); | ||
| 162 | }); | ||
| 163 | |||
| 164 | r.cr2().modify(|w| { | ||
| 165 | // Enable DMA mode | ||
| 166 | w.set_dma(true); | ||
| 167 | // Enable continuous conversions | ||
| 168 | w.set_cont(true); | ||
| 169 | // DMA requests are issues as long as DMA=1 and data are converted. | ||
| 170 | w.set_dds(vals::Dds::CONTINUOUS); | ||
| 171 | // EOC flag is set at the end of each conversion. | ||
| 172 | w.set_eocs(vals::Eocs::EACH_CONVERSION); | ||
| 173 | }); | ||
| 174 | } | ||
| 81 | } | 175 | } |
| 82 | } | 176 | } |
| 83 | 177 | ||
| 84 | fn adcpre(&self) -> crate::pac::adccommon::vals::Adcpre { | 178 | fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| 85 | match self { | 179 | self.cr2().modify(|reg| { |
| 86 | Prescaler::Div2 => crate::pac::adccommon::vals::Adcpre::DIV2, | 180 | reg.set_adon(true); |
| 87 | Prescaler::Div4 => crate::pac::adccommon::vals::Adcpre::DIV4, | 181 | }); |
| 88 | Prescaler::Div6 => crate::pac::adccommon::vals::Adcpre::DIV6, | 182 | |
| 89 | Prescaler::Div8 => crate::pac::adccommon::vals::Adcpre::DIV8, | 183 | // Check the sequence is long enough |
| 184 | self.sqr1().modify(|r| { | ||
| 185 | r.set_l((sequence.len() - 1).try_into().unwrap()); | ||
| 186 | }); | ||
| 187 | |||
| 188 | for (i, ((ch, _), sample_time)) in sequence.enumerate() { | ||
| 189 | // Set the channel in the right sequence field. | ||
| 190 | self.sqr3().modify(|w| w.set_sq(i, ch)); | ||
| 191 | |||
| 192 | let sample_time = sample_time.into(); | ||
| 193 | if ch <= 9 { | ||
| 194 | self.smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 195 | } else { | ||
| 196 | self.smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 197 | } | ||
| 90 | } | 198 | } |
| 91 | } | 199 | } |
| 92 | } | 200 | } |
| 93 | 201 | ||
| 94 | impl<'d, T> Adc<'d, T> | 202 | impl<'d, T> Adc<'d, T> |
| 95 | where | 203 | where |
| 96 | T: Instance, | 204 | T: Instance<Regs = crate::pac::adc::Adc>, |
| 97 | { | 205 | { |
| 98 | pub fn new(adc: Peri<'d, T>) -> Self { | 206 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 99 | rcc::enable_and_reset::<T>(); | 207 | Self::new_with_config(adc, Default::default()) |
| 208 | } | ||
| 100 | 209 | ||
| 101 | let presc = Prescaler::from_pclk2(T::frequency()); | 210 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { |
| 102 | T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); | 211 | rcc::enable_and_reset::<T>(); |
| 103 | T::regs().cr2().modify(|reg| { | ||
| 104 | reg.set_adon(true); | ||
| 105 | }); | ||
| 106 | 212 | ||
| 107 | blocking_delay_us(3); | 213 | let presc = from_pclk2(T::frequency()); |
| 214 | T::common_regs().ccr().modify(|w| w.set_adcpre(presc)); | ||
| 215 | T::regs().enable(); | ||
| 108 | 216 | ||
| 109 | Self { | 217 | if let Some(resolution) = config.resolution { |
| 110 | adc, | 218 | T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); |
| 111 | sample_time: SampleTime::from_bits(0), | ||
| 112 | } | 219 | } |
| 113 | } | ||
| 114 | |||
| 115 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 116 | self.sample_time = sample_time; | ||
| 117 | } | ||
| 118 | 220 | ||
| 119 | pub fn set_resolution(&mut self, resolution: Resolution) { | 221 | Self { adc } |
| 120 | T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); | ||
| 121 | } | 222 | } |
| 122 | 223 | ||
| 123 | /// Enables internal voltage reference and returns [VrefInt], which can be used in | 224 | /// Enables internal voltage reference and returns [VrefInt], which can be used in |
| @@ -152,59 +253,11 @@ where | |||
| 152 | 253 | ||
| 153 | Vbat {} | 254 | Vbat {} |
| 154 | } | 255 | } |
| 155 | |||
| 156 | /// Perform a single conversion. | ||
| 157 | fn convert(&mut self) -> u16 { | ||
| 158 | // clear end of conversion flag | ||
| 159 | T::regs().sr().modify(|reg| { | ||
| 160 | reg.set_eoc(false); | ||
| 161 | }); | ||
| 162 | |||
| 163 | // Start conversion | ||
| 164 | T::regs().cr2().modify(|reg| { | ||
| 165 | reg.set_swstart(true); | ||
| 166 | }); | ||
| 167 | |||
| 168 | while T::regs().sr().read().strt() == false { | ||
| 169 | // spin //wait for actual start | ||
| 170 | } | ||
| 171 | while T::regs().sr().read().eoc() == false { | ||
| 172 | // spin //wait for finish | ||
| 173 | } | ||
| 174 | |||
| 175 | T::regs().dr().read().0 as u16 | ||
| 176 | } | ||
| 177 | |||
| 178 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 179 | channel.setup(); | ||
| 180 | |||
| 181 | // Configure ADC | ||
| 182 | let channel = channel.channel(); | ||
| 183 | |||
| 184 | // Select channel | ||
| 185 | T::regs().sqr3().write(|reg| reg.set_sq(0, channel)); | ||
| 186 | |||
| 187 | // Configure channel | ||
| 188 | Self::set_channel_sample_time(channel, self.sample_time); | ||
| 189 | |||
| 190 | self.convert() | ||
| 191 | } | ||
| 192 | |||
| 193 | fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | ||
| 194 | let sample_time = sample_time.into(); | ||
| 195 | if ch <= 9 { | ||
| 196 | T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 197 | } else { | ||
| 198 | T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 199 | } | ||
| 200 | } | ||
| 201 | } | 256 | } |
| 202 | 257 | ||
| 203 | impl<'d, T: Instance> Drop for Adc<'d, T> { | 258 | impl<'d, T: Instance> Drop for Adc<'d, T> { |
| 204 | fn drop(&mut self) { | 259 | fn drop(&mut self) { |
| 205 | T::regs().cr2().modify(|reg| { | 260 | T::regs().stop(); |
| 206 | reg.set_adon(false); | ||
| 207 | }); | ||
| 208 | 261 | ||
| 209 | rcc::disable::<T>(); | 262 | rcc::disable::<T>(); |
| 210 | } | 263 | } |
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 16063ce4d..9cc44aa9a 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs | |||
| @@ -1,19 +1,19 @@ | |||
| 1 | use cfg_if::cfg_if; | 1 | use cfg_if::cfg_if; |
| 2 | #[cfg(adc_g0)] | 2 | #[cfg(adc_g0)] |
| 3 | use heapless::Vec; | 3 | use heapless::Vec; |
| 4 | use pac::adc::vals::Dmacfg; | ||
| 5 | #[cfg(adc_g0)] | 4 | #[cfg(adc_g0)] |
| 6 | use pac::adc::vals::{Ckmode, Smpsel}; | 5 | use pac::adc::vals::Ckmode; |
| 6 | use pac::adc::vals::Dmacfg; | ||
| 7 | #[cfg(adc_v3)] | 7 | #[cfg(adc_v3)] |
| 8 | use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; | 8 | 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 | use super::{ | 12 | #[allow(unused_imports)] |
| 13 | blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, | 13 | use super::SealedAdcChannel; |
| 14 | }; | 14 | use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 15 | use crate::dma::Transfer; | 15 | use crate::adc::ConversionMode; |
| 16 | use crate::{pac, rcc, Peri}; | 16 | use crate::{Peri, pac, rcc}; |
| 17 | 17 | ||
| 18 | /// Default VREF voltage used for sample conversion to millivolts. | 18 | /// Default VREF voltage used for sample conversion to millivolts. |
| 19 | pub const VREF_DEFAULT_MV: u32 = 3300; | 19 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| @@ -25,70 +25,64 @@ pub const VREF_CALIB_MV: u32 = 3000; | |||
| 25 | // TODO: Use [#![feature(variant_count)]](https://github.com/rust-lang/rust/issues/73662) when stable | 25 | // TODO: Use [#![feature(variant_count)]](https://github.com/rust-lang/rust/issues/73662) when stable |
| 26 | const SAMPLE_TIMES_CAPACITY: usize = 2; | 26 | const SAMPLE_TIMES_CAPACITY: usize = 2; |
| 27 | 27 | ||
| 28 | pub struct VrefInt; | 28 | #[cfg(adc_g0)] |
| 29 | impl<T: Instance> AdcChannel<T> for VrefInt {} | 29 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 30 | impl<T: Instance> SealedAdcChannel<T> for VrefInt { | 30 | const CHANNEL: u8 = 13; |
| 31 | fn channel(&self) -> u8 { | 31 | } |
| 32 | cfg_if! { | 32 | #[cfg(any(adc_h5, adc_h7rs))] |
| 33 | if #[cfg(adc_g0)] { | 33 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 34 | let val = 13; | 34 | const CHANNEL: u8 = 17; |
| 35 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 35 | } |
| 36 | let val = 17; | 36 | #[cfg(adc_u0)] |
| 37 | } else if #[cfg(adc_u0)] { | 37 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 38 | let val = 12; | 38 | const CHANNEL: u8 = 12; |
| 39 | } else { | 39 | } |
| 40 | let val = 0; | 40 | #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] |
| 41 | } | 41 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 42 | } | 42 | const CHANNEL: u8 = 0; |
| 43 | val | ||
| 44 | } | ||
| 45 | } | 43 | } |
| 46 | 44 | ||
| 47 | pub struct Temperature; | 45 | #[cfg(adc_g0)] |
| 48 | impl<T: Instance> AdcChannel<T> for Temperature {} | 46 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 49 | impl<T: Instance> SealedAdcChannel<T> for Temperature { | 47 | const CHANNEL: u8 = 12; |
| 50 | fn channel(&self) -> u8 { | 48 | } |
| 51 | cfg_if! { | 49 | #[cfg(any(adc_h5, adc_h7rs))] |
| 52 | if #[cfg(adc_g0)] { | 50 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 53 | let val = 12; | 51 | const CHANNEL: u8 = 16; |
| 54 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 52 | } |
| 55 | let val = 16; | 53 | #[cfg(adc_u0)] |
| 56 | } else if #[cfg(adc_u0)] { | 54 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 57 | let val = 11; | 55 | const CHANNEL: u8 = 11; |
| 58 | } else { | 56 | } |
| 59 | let val = 17; | 57 | #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] |
| 60 | } | 58 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 61 | } | 59 | const CHANNEL: u8 = 17; |
| 62 | val | ||
| 63 | } | ||
| 64 | } | 60 | } |
| 65 | 61 | ||
| 66 | pub struct Vbat; | 62 | #[cfg(adc_g0)] |
| 67 | impl<T: Instance> AdcChannel<T> for Vbat {} | 63 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 68 | impl<T: Instance> SealedAdcChannel<T> for Vbat { | 64 | const CHANNEL: u8 = 14; |
| 69 | fn channel(&self) -> u8 { | 65 | } |
| 70 | cfg_if! { | 66 | #[cfg(any(adc_h5, adc_h7rs))] |
| 71 | if #[cfg(adc_g0)] { | 67 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 72 | let val = 14; | 68 | const CHANNEL: u8 = 16; |
| 73 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 69 | } |
| 74 | let val = 2; | 70 | #[cfg(adc_u0)] |
| 75 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 71 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 76 | let val = 13; | 72 | const CHANNEL: u8 = 13; |
| 77 | } else { | 73 | } |
| 78 | let val = 18; | 74 | #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] |
| 79 | } | 75 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 80 | } | 76 | const CHANNEL: u8 = 18; |
| 81 | val | ||
| 82 | } | ||
| 83 | } | 77 | } |
| 84 | 78 | ||
| 85 | cfg_if! { | 79 | cfg_if! { |
| 86 | if #[cfg(any(adc_h5, adc_h7rs))] { | 80 | if #[cfg(any(adc_h5, adc_h7rs))] { |
| 87 | pub struct VddCore; | 81 | pub struct VddCore; |
| 88 | impl<T: Instance> AdcChannel<T> for VddCore {} | 82 | impl<T: Instance> super::AdcChannel<T> for VddCore {} |
| 89 | impl<T: Instance> super::SealedAdcChannel<T> for VddCore { | 83 | impl<T: Instance> super::SealedAdcChannel<T> for VddCore { |
| 90 | fn channel(&self) -> u8 { | 84 | fn channel(&self) -> u8 { |
| 91 | 6 | 85 | 17 |
| 92 | } | 86 | } |
| 93 | } | 87 | } |
| 94 | } | 88 | } |
| @@ -97,7 +91,7 @@ cfg_if! { | |||
| 97 | cfg_if! { | 91 | cfg_if! { |
| 98 | if #[cfg(adc_u0)] { | 92 | if #[cfg(adc_u0)] { |
| 99 | pub struct DacOut; | 93 | pub struct DacOut; |
| 100 | impl<T: Instance> AdcChannel<T> for DacOut {} | 94 | impl<T: Instance> super::AdcChannel<T> for DacOut {} |
| 101 | impl<T: Instance> super::SealedAdcChannel<T> for DacOut { | 95 | impl<T: Instance> super::SealedAdcChannel<T> for DacOut { |
| 102 | fn channel(&self) -> u8 { | 96 | fn channel(&self) -> u8 { |
| 103 | 19 | 97 | 19 |
| @@ -106,21 +100,6 @@ cfg_if! { | |||
| 106 | } | 100 | } |
| 107 | } | 101 | } |
| 108 | 102 | ||
| 109 | /// Number of samples used for averaging. | ||
| 110 | #[derive(Copy, Clone, Debug)] | ||
| 111 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 112 | pub enum Averaging { | ||
| 113 | Disabled, | ||
| 114 | Samples2, | ||
| 115 | Samples4, | ||
| 116 | Samples8, | ||
| 117 | Samples16, | ||
| 118 | Samples32, | ||
| 119 | Samples64, | ||
| 120 | Samples128, | ||
| 121 | Samples256, | ||
| 122 | } | ||
| 123 | |||
| 124 | cfg_if! { if #[cfg(adc_g0)] { | 103 | cfg_if! { if #[cfg(adc_g0)] { |
| 125 | 104 | ||
| 126 | /// Synchronous PCLK prescaler | 105 | /// Synchronous PCLK prescaler |
| @@ -141,295 +120,150 @@ pub enum Clock { | |||
| 141 | 120 | ||
| 142 | }} | 121 | }} |
| 143 | 122 | ||
| 144 | impl<'d, T: Instance> Adc<'d, T> { | 123 | #[cfg(adc_u0)] |
| 145 | /// Enable the voltage regulator | 124 | type Ovss = u8; |
| 146 | fn init_regulator() { | 125 | #[cfg(adc_u0)] |
| 147 | rcc::enable_and_reset::<T>(); | 126 | type Ovsr = u8; |
| 148 | T::regs().cr().modify(|reg| { | 127 | #[cfg(adc_v3)] |
| 149 | #[cfg(not(any(adc_g0, adc_u0)))] | 128 | type Ovss = OversamplingShift; |
| 150 | reg.set_deeppwd(false); | 129 | #[cfg(adc_v3)] |
| 151 | reg.set_advregen(true); | 130 | type Ovsr = OversamplingRatio; |
| 152 | }); | 131 | |
| 153 | 132 | /// Adc configuration | |
| 154 | // If this is false then each ADC_CHSELR bit enables an input channel. | 133 | #[derive(Default)] |
| 155 | // This is the reset value, so has no effect. | 134 | pub struct AdcConfig { |
| 156 | #[cfg(any(adc_g0, adc_u0))] | 135 | #[cfg(any(adc_u0, adc_g0, adc_v3))] |
| 157 | T::regs().cfgr1().modify(|reg| { | 136 | pub oversampling_shift: Option<Ovss>, |
| 158 | reg.set_chselrmod(false); | 137 | #[cfg(any(adc_u0, adc_g0, adc_v3))] |
| 159 | }); | 138 | pub oversampling_ratio: Option<Ovsr>, |
| 160 | 139 | #[cfg(any(adc_u0, adc_g0))] | |
| 161 | blocking_delay_us(20); | 140 | pub oversampling_enable: Option<bool>, |
| 162 | } | 141 | #[cfg(adc_v3)] |
| 163 | 142 | pub oversampling_mode: Option<(Rovsm, Trovs, bool)>, | |
| 164 | /// Calibrate to remove conversion offset | ||
| 165 | fn init_calibrate() { | ||
| 166 | T::regs().cr().modify(|reg| { | ||
| 167 | reg.set_adcal(true); | ||
| 168 | }); | ||
| 169 | |||
| 170 | while T::regs().cr().read().adcal() { | ||
| 171 | // spin | ||
| 172 | } | ||
| 173 | |||
| 174 | blocking_delay_us(1); | ||
| 175 | } | ||
| 176 | |||
| 177 | /// Initialize the ADC leaving any analog clock at reset value. | ||
| 178 | /// For G0 and WL, this is the async clock without prescaler. | ||
| 179 | pub fn new(adc: Peri<'d, T>) -> Self { | ||
| 180 | Self::init_regulator(); | ||
| 181 | Self::init_calibrate(); | ||
| 182 | Self { | ||
| 183 | adc, | ||
| 184 | sample_time: SampleTime::from_bits(0), | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | #[cfg(adc_g0)] | 143 | #[cfg(adc_g0)] |
| 189 | /// Initialize ADC with explicit clock for the analog ADC | 144 | pub clock: Option<Clock>, |
| 190 | pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { | 145 | pub resolution: Option<Resolution>, |
| 191 | Self::init_regulator(); | 146 | pub averaging: Option<Averaging>, |
| 192 | 147 | } | |
| 193 | #[cfg(any(stm32wl5x))] | ||
| 194 | { | ||
| 195 | // Reset value 0 is actually _No clock selected_ in the STM32WL5x reference manual | ||
| 196 | let async_clock_available = pac::RCC.ccipr().read().adcsel() != pac::rcc::vals::Adcsel::_RESERVED_0; | ||
| 197 | match clock { | ||
| 198 | Clock::Async { div: _ } => { | ||
| 199 | assert!(async_clock_available); | ||
| 200 | } | ||
| 201 | Clock::Sync { div: _ } => { | ||
| 202 | if async_clock_available { | ||
| 203 | warn!("Not using configured ADC clock"); | ||
| 204 | } | ||
| 205 | } | ||
| 206 | } | ||
| 207 | } | ||
| 208 | match clock { | ||
| 209 | Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div)), | ||
| 210 | Clock::Sync { div } => T::regs().cfgr2().modify(|reg| { | ||
| 211 | reg.set_ckmode(match div { | ||
| 212 | CkModePclk::DIV1 => Ckmode::PCLK, | ||
| 213 | CkModePclk::DIV2 => Ckmode::PCLK_DIV2, | ||
| 214 | CkModePclk::DIV4 => Ckmode::PCLK_DIV4, | ||
| 215 | }) | ||
| 216 | }), | ||
| 217 | } | ||
| 218 | |||
| 219 | Self::init_calibrate(); | ||
| 220 | 148 | ||
| 221 | Self { | 149 | impl super::AdcRegs for crate::pac::adc::Adc { |
| 222 | adc, | 150 | fn data(&self) -> *mut u16 { |
| 223 | sample_time: SampleTime::from_bits(0), | 151 | crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16 |
| 224 | } | ||
| 225 | } | 152 | } |
| 226 | 153 | ||
| 227 | // Enable ADC only when it is not already running. | 154 | // Enable ADC only when it is not already running. |
| 228 | fn enable(&mut self) { | 155 | fn enable(&self) { |
| 229 | // Make sure bits are off | 156 | // Make sure bits are off |
| 230 | while T::regs().cr().read().addis() { | 157 | while self.cr().read().addis() { |
| 231 | // spin | 158 | // spin |
| 232 | } | 159 | } |
| 233 | 160 | ||
| 234 | if !T::regs().cr().read().aden() { | 161 | if !self.cr().read().aden() { |
| 235 | // Enable ADC | 162 | // Enable ADC |
| 236 | T::regs().isr().modify(|reg| { | 163 | self.isr().modify(|reg| { |
| 237 | reg.set_adrdy(true); | 164 | reg.set_adrdy(true); |
| 238 | }); | 165 | }); |
| 239 | T::regs().cr().modify(|reg| { | 166 | self.cr().modify(|reg| { |
| 240 | reg.set_aden(true); | 167 | reg.set_aden(true); |
| 241 | }); | 168 | }); |
| 242 | 169 | ||
| 243 | while !T::regs().isr().read().adrdy() { | 170 | while !self.isr().read().adrdy() { |
| 244 | // spin | 171 | // spin |
| 245 | } | 172 | } |
| 246 | } | 173 | } |
| 247 | } | 174 | } |
| 248 | 175 | ||
| 249 | pub fn enable_vrefint(&self) -> VrefInt { | 176 | fn start(&self) { |
| 177 | self.cr().modify(|reg| { | ||
| 178 | reg.set_adstart(true); | ||
| 179 | }); | ||
| 180 | } | ||
| 181 | |||
| 182 | fn stop(&self) { | ||
| 183 | // Ensure conversions are finished. | ||
| 184 | if self.cr().read().adstart() && !self.cr().read().addis() { | ||
| 185 | self.cr().modify(|reg| { | ||
| 186 | reg.set_adstp(true); | ||
| 187 | }); | ||
| 188 | while self.cr().read().adstart() {} | ||
| 189 | } | ||
| 190 | |||
| 191 | // Reset configuration. | ||
| 250 | #[cfg(not(any(adc_g0, adc_u0)))] | 192 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 251 | T::common_regs().ccr().modify(|reg| { | 193 | self.cfgr().modify(|reg| { |
| 252 | reg.set_vrefen(true); | 194 | reg.set_cont(false); |
| 195 | reg.set_dmaen(false); | ||
| 253 | }); | 196 | }); |
| 254 | #[cfg(any(adc_g0, adc_u0))] | 197 | #[cfg(any(adc_g0, adc_u0))] |
| 255 | T::regs().ccr().modify(|reg| { | 198 | self.cfgr1().modify(|reg| { |
| 256 | reg.set_vrefen(true); | 199 | reg.set_cont(false); |
| 200 | reg.set_dmaen(false); | ||
| 257 | }); | 201 | }); |
| 202 | } | ||
| 258 | 203 | ||
| 259 | // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us | 204 | /// Perform a single conversion. |
| 260 | // to stabilize the internal voltage reference. | 205 | fn convert(&self) { |
| 261 | blocking_delay_us(15); | 206 | // Some models are affected by an erratum: |
| 207 | // If we perform conversions slower than 1 kHz, the first read ADC value can be | ||
| 208 | // corrupted, so we discard it and measure again. | ||
| 209 | // | ||
| 210 | // STM32L471xx: Section 2.7.3 | ||
| 211 | // STM32G4: Section 2.7.3 | ||
| 212 | #[cfg(any(rcc_l4, rcc_g4))] | ||
| 213 | let len = 2; | ||
| 262 | 214 | ||
| 263 | VrefInt {} | 215 | #[cfg(not(any(rcc_l4, rcc_g4)))] |
| 264 | } | 216 | let len = 1; |
| 265 | 217 | ||
| 266 | pub fn enable_temperature(&self) -> Temperature { | 218 | for _ in 0..len { |
| 267 | cfg_if! { | 219 | self.isr().modify(|reg| { |
| 268 | if #[cfg(any(adc_g0, adc_u0))] { | 220 | reg.set_eos(true); |
| 269 | T::regs().ccr().modify(|reg| { | 221 | reg.set_eoc(true); |
| 270 | reg.set_tsen(true); | 222 | }); |
| 271 | }); | ||
| 272 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 273 | T::common_regs().ccr().modify(|reg| { | ||
| 274 | reg.set_tsen(true); | ||
| 275 | }); | ||
| 276 | } else { | ||
| 277 | T::common_regs().ccr().modify(|reg| { | ||
| 278 | reg.set_ch17sel(true); | ||
| 279 | }); | ||
| 280 | } | ||
| 281 | } | ||
| 282 | 223 | ||
| 283 | Temperature {} | 224 | // Start conversion |
| 284 | } | 225 | self.cr().modify(|reg| { |
| 226 | reg.set_adstart(true); | ||
| 227 | }); | ||
| 285 | 228 | ||
| 286 | pub fn enable_vbat(&self) -> Vbat { | 229 | while !self.isr().read().eos() { |
| 287 | cfg_if! { | 230 | // spin |
| 288 | if #[cfg(any(adc_g0, adc_u0))] { | ||
| 289 | T::regs().ccr().modify(|reg| { | ||
| 290 | reg.set_vbaten(true); | ||
| 291 | }); | ||
| 292 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 293 | T::common_regs().ccr().modify(|reg| { | ||
| 294 | reg.set_vbaten(true); | ||
| 295 | }); | ||
| 296 | } else { | ||
| 297 | T::common_regs().ccr().modify(|reg| { | ||
| 298 | reg.set_ch18sel(true); | ||
| 299 | }); | ||
| 300 | } | 231 | } |
| 301 | } | 232 | } |
| 302 | |||
| 303 | Vbat {} | ||
| 304 | } | 233 | } |
| 305 | 234 | ||
| 306 | /// Set the ADC sample time. | 235 | fn configure_dma(&self, conversion_mode: ConversionMode) { |
| 307 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | 236 | // Set continuous mode with oneshot dma. |
| 308 | self.sample_time = sample_time; | 237 | // Clear overrun flag before starting transfer. |
| 309 | } | 238 | self.isr().modify(|reg| { |
| 310 | 239 | reg.set_ovr(true); | |
| 311 | /// Get the ADC sample time. | 240 | }); |
| 312 | pub fn sample_time(&self) -> SampleTime { | ||
| 313 | self.sample_time | ||
| 314 | } | ||
| 315 | 241 | ||
| 316 | /// Set the ADC resolution. | ||
| 317 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 318 | #[cfg(not(any(adc_g0, adc_u0)))] | 242 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 319 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | 243 | let regs = self.cfgr(); |
| 320 | #[cfg(any(adc_g0, adc_u0))] | ||
| 321 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); | ||
| 322 | } | ||
| 323 | 244 | ||
| 324 | pub fn set_averaging(&mut self, averaging: Averaging) { | 245 | #[cfg(any(adc_g0, adc_u0))] |
| 325 | let (enable, samples, right_shift) = match averaging { | 246 | let regs = self.cfgr1(); |
| 326 | Averaging::Disabled => (false, 0, 0), | ||
| 327 | Averaging::Samples2 => (true, 0, 1), | ||
| 328 | Averaging::Samples4 => (true, 1, 2), | ||
| 329 | Averaging::Samples8 => (true, 2, 3), | ||
| 330 | Averaging::Samples16 => (true, 3, 4), | ||
| 331 | Averaging::Samples32 => (true, 4, 5), | ||
| 332 | Averaging::Samples64 => (true, 5, 6), | ||
| 333 | Averaging::Samples128 => (true, 6, 7), | ||
| 334 | Averaging::Samples256 => (true, 7, 8), | ||
| 335 | }; | ||
| 336 | T::regs().cfgr2().modify(|reg| { | ||
| 337 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 338 | reg.set_rovse(enable); | ||
| 339 | #[cfg(any(adc_g0, adc_u0))] | ||
| 340 | reg.set_ovse(enable); | ||
| 341 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 342 | reg.set_ovsr(samples.into()); | ||
| 343 | #[cfg(not(any(adc_h5, adc_h7rs)))] | ||
| 344 | reg.set_ovsr(samples.into()); | ||
| 345 | reg.set_ovss(right_shift.into()); | ||
| 346 | }) | ||
| 347 | } | ||
| 348 | /* | ||
| 349 | /// Convert a raw sample from the `Temperature` to deg C | ||
| 350 | pub fn to_degrees_centigrade(sample: u16) -> f32 { | ||
| 351 | (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) | ||
| 352 | * (sample as f32 - VtempCal30::get().read() as f32) | ||
| 353 | + 30.0 | ||
| 354 | } | ||
| 355 | */ | ||
| 356 | |||
| 357 | /// Perform a single conversion. | ||
| 358 | fn convert(&mut self) -> u16 { | ||
| 359 | T::regs().isr().modify(|reg| { | ||
| 360 | reg.set_eos(true); | ||
| 361 | reg.set_eoc(true); | ||
| 362 | }); | ||
| 363 | 247 | ||
| 364 | // Start conversion | 248 | regs.modify(|reg| { |
| 365 | T::regs().cr().modify(|reg| { | 249 | reg.set_discen(false); |
| 366 | reg.set_adstart(true); | 250 | reg.set_cont(true); |
| 251 | reg.set_dmacfg(match conversion_mode { | ||
| 252 | ConversionMode::Singular => Dmacfg::ONE_SHOT, | ||
| 253 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 254 | ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, | ||
| 255 | }); | ||
| 256 | reg.set_dmaen(true); | ||
| 367 | }); | 257 | }); |
| 368 | |||
| 369 | while !T::regs().isr().read().eos() { | ||
| 370 | // spin | ||
| 371 | } | ||
| 372 | |||
| 373 | T::regs().dr().read().0 as u16 | ||
| 374 | } | 258 | } |
| 375 | 259 | ||
| 376 | /// Read an ADC channel. | 260 | fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| 377 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 261 | #[cfg(adc_h5)] |
| 378 | self.read_channel(channel) | 262 | self.cr().modify(|w| w.set_aden(false)); |
| 379 | } | ||
| 380 | |||
| 381 | /// Read one or multiple ADC channels using DMA. | ||
| 382 | /// | ||
| 383 | /// `readings` must have a length that is a multiple of the length of the | ||
| 384 | /// `sequence` iterator. | ||
| 385 | /// | ||
| 386 | /// Note: The order of values in `readings` is defined by the pin ADC | ||
| 387 | /// channel number and not the pin order in `sequence`. | ||
| 388 | /// | ||
| 389 | /// Example | ||
| 390 | /// ```rust,ignore | ||
| 391 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 392 | /// | ||
| 393 | /// let mut adc = Adc::new(p.ADC1); | ||
| 394 | /// let mut adc_pin0 = p.PA0.degrade_adc(); | ||
| 395 | /// let mut adc_pin1 = p.PA1.degrade_adc(); | ||
| 396 | /// let mut measurements = [0u16; 2]; | ||
| 397 | /// | ||
| 398 | /// adc.read( | ||
| 399 | /// p.DMA1_CH2.reborrow(), | ||
| 400 | /// [ | ||
| 401 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 402 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 403 | /// ] | ||
| 404 | /// .into_iter(), | ||
| 405 | /// &mut measurements, | ||
| 406 | /// ) | ||
| 407 | /// .await; | ||
| 408 | /// defmt::info!("measurements: {}", measurements); | ||
| 409 | /// ``` | ||
| 410 | pub async fn read( | ||
| 411 | &mut self, | ||
| 412 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 413 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 414 | readings: &mut [u16], | ||
| 415 | ) { | ||
| 416 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 417 | assert!( | ||
| 418 | readings.len() % sequence.len() == 0, | ||
| 419 | "Readings length must be a multiple of sequence length" | ||
| 420 | ); | ||
| 421 | assert!( | ||
| 422 | sequence.len() <= 16, | ||
| 423 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 424 | ); | ||
| 425 | |||
| 426 | // Ensure no conversions are ongoing and ADC is enabled. | ||
| 427 | Self::cancel_conversions(); | ||
| 428 | self.enable(); | ||
| 429 | 263 | ||
| 430 | // Set sequence length | 264 | // Set sequence length |
| 431 | #[cfg(not(any(adc_g0, adc_u0)))] | 265 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 432 | T::regs().sqr1().modify(|w| { | 266 | self.sqr1().modify(|w| { |
| 433 | w.set_l(sequence.len() as u8 - 1); | 267 | w.set_l(sequence.len() as u8 - 1); |
| 434 | }); | 268 | }); |
| 435 | 269 | ||
| @@ -437,12 +271,12 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 437 | { | 271 | { |
| 438 | let mut sample_times = Vec::<SampleTime, SAMPLE_TIMES_CAPACITY>::new(); | 272 | let mut sample_times = Vec::<SampleTime, SAMPLE_TIMES_CAPACITY>::new(); |
| 439 | 273 | ||
| 440 | T::regs().chselr().write(|chselr| { | 274 | self.chselr().write(|chselr| { |
| 441 | T::regs().smpr().write(|smpr| { | 275 | self.smpr().write(|smpr| { |
| 442 | for (channel, sample_time) in sequence { | 276 | for ((channel, _), sample_time) in sequence { |
| 443 | chselr.set_chsel(channel.channel.into(), true); | 277 | chselr.set_chsel(channel.into(), true); |
| 444 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { | 278 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { |
| 445 | smpr.set_smpsel(channel.channel.into(), (i as u8).into()); | 279 | smpr.set_smpsel(channel.into(), (i as u8).into()); |
| 446 | } else { | 280 | } else { |
| 447 | smpr.set_sample_time(sample_times.len(), sample_time); | 281 | smpr.set_sample_time(sample_times.len(), sample_time); |
| 448 | if let Err(_) = sample_times.push(sample_time) { | 282 | if let Err(_) = sample_times.push(sample_time) { |
| @@ -461,225 +295,321 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 461 | #[cfg(adc_u0)] | 295 | #[cfg(adc_u0)] |
| 462 | let mut channel_mask = 0; | 296 | let mut channel_mask = 0; |
| 463 | 297 | ||
| 298 | #[cfg(adc_h5)] | ||
| 299 | let mut difsel = 0u32; | ||
| 300 | |||
| 464 | // Configure channels and ranks | 301 | // Configure channels and ranks |
| 465 | for (_i, (channel, sample_time)) in sequence.enumerate() { | 302 | for (_i, ((channel, _is_differential), sample_time)) in sequence.enumerate() { |
| 466 | Self::configure_channel(channel, sample_time); | 303 | // RM0492, RM0481, etc. |
| 304 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | ||
| 305 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 306 | if channel == 0 { | ||
| 307 | self.or().modify(|reg| reg.set_op0(true)); | ||
| 308 | } | ||
| 309 | |||
| 310 | // Configure channel | ||
| 311 | cfg_if! { | ||
| 312 | if #[cfg(adc_u0)] { | ||
| 313 | // On G0 and U6 all channels use the same sampling time. | ||
| 314 | self.smpr().modify(|reg| reg.set_smp1(sample_time.into())); | ||
| 315 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 316 | match channel { | ||
| 317 | 0..=9 => self.smpr1().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), | ||
| 318 | _ => self.smpr2().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), | ||
| 319 | } | ||
| 320 | } else { | ||
| 321 | let sample_time = sample_time.into(); | ||
| 322 | self | ||
| 323 | .smpr(channel as usize / 10) | ||
| 324 | .modify(|reg| reg.set_smp(channel as usize % 10, sample_time)); | ||
| 325 | } | ||
| 326 | } | ||
| 327 | |||
| 328 | #[cfg(stm32h7)] | ||
| 329 | { | ||
| 330 | use crate::pac::adc::vals::Pcsel; | ||
| 331 | |||
| 332 | self.cfgr2().modify(|w| w.set_lshift(0)); | ||
| 333 | self.pcsel() | ||
| 334 | .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); | ||
| 335 | } | ||
| 467 | 336 | ||
| 468 | // Each channel is sampled according to sequence | 337 | // Each channel is sampled according to sequence |
| 469 | #[cfg(not(any(adc_g0, adc_u0)))] | 338 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 470 | match _i { | 339 | match _i { |
| 471 | 0..=3 => { | 340 | 0..=3 => { |
| 472 | T::regs().sqr1().modify(|w| { | 341 | self.sqr1().modify(|w| { |
| 473 | w.set_sq(_i, channel.channel()); | 342 | w.set_sq(_i, channel); |
| 474 | }); | 343 | }); |
| 475 | } | 344 | } |
| 476 | 4..=8 => { | 345 | 4..=8 => { |
| 477 | T::regs().sqr2().modify(|w| { | 346 | self.sqr2().modify(|w| { |
| 478 | w.set_sq(_i - 4, channel.channel()); | 347 | w.set_sq(_i - 4, channel); |
| 479 | }); | 348 | }); |
| 480 | } | 349 | } |
| 481 | 9..=13 => { | 350 | 9..=13 => { |
| 482 | T::regs().sqr3().modify(|w| { | 351 | self.sqr3().modify(|w| { |
| 483 | w.set_sq(_i - 9, channel.channel()); | 352 | w.set_sq(_i - 9, channel); |
| 484 | }); | 353 | }); |
| 485 | } | 354 | } |
| 486 | 14..=15 => { | 355 | 14..=15 => { |
| 487 | T::regs().sqr4().modify(|w| { | 356 | self.sqr4().modify(|w| { |
| 488 | w.set_sq(_i - 14, channel.channel()); | 357 | w.set_sq(_i - 14, channel); |
| 489 | }); | 358 | }); |
| 490 | } | 359 | } |
| 491 | _ => unreachable!(), | 360 | _ => unreachable!(), |
| 492 | } | 361 | } |
| 493 | 362 | ||
| 363 | #[cfg(adc_h5)] | ||
| 364 | { | ||
| 365 | difsel |= (_is_differential as u32) << channel; | ||
| 366 | } | ||
| 367 | |||
| 494 | #[cfg(adc_u0)] | 368 | #[cfg(adc_u0)] |
| 495 | { | 369 | { |
| 496 | channel_mask |= 1 << channel.channel(); | 370 | channel_mask |= 1 << channel; |
| 497 | } | 371 | } |
| 498 | } | 372 | } |
| 499 | 373 | ||
| 374 | #[cfg(adc_h5)] | ||
| 375 | self.difsel().write(|w| w.set_difsel(difsel)); | ||
| 376 | |||
| 500 | // On G0 and U0 enabled channels are sampled from 0 to last channel. | 377 | // On G0 and U0 enabled channels are sampled from 0 to last channel. |
| 501 | // It is possible to add up to 8 sequences if CHSELRMOD = 1. | 378 | // It is possible to add up to 8 sequences if CHSELRMOD = 1. |
| 502 | // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. | 379 | // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. |
| 503 | #[cfg(adc_u0)] | 380 | #[cfg(adc_u0)] |
| 504 | T::regs().chselr().modify(|reg| { | 381 | self.chselr().modify(|reg| { |
| 505 | reg.set_chsel(channel_mask); | 382 | reg.set_chsel(channel_mask); |
| 506 | }); | 383 | }); |
| 507 | } | 384 | } |
| 508 | // Set continuous mode with oneshot dma. | 385 | } |
| 509 | // Clear overrun flag before starting transfer. | 386 | } |
| 510 | T::regs().isr().modify(|reg| { | ||
| 511 | reg.set_ovr(true); | ||
| 512 | }); | ||
| 513 | 387 | ||
| 514 | #[cfg(not(any(adc_g0, adc_u0)))] | 388 | impl<'d, T: Instance<Regs = crate::pac::adc::Adc>> Adc<'d, T> { |
| 515 | T::regs().cfgr().modify(|reg| { | 389 | /// Enable the voltage regulator |
| 516 | reg.set_discen(false); | 390 | fn init_regulator() { |
| 517 | reg.set_cont(true); | 391 | rcc::enable_and_reset::<T>(); |
| 518 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | 392 | T::regs().cr().modify(|reg| { |
| 519 | reg.set_dmaen(true); | 393 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 394 | reg.set_deeppwd(false); | ||
| 395 | reg.set_advregen(true); | ||
| 520 | }); | 396 | }); |
| 397 | |||
| 398 | // If this is false then each ADC_CHSELR bit enables an input channel. | ||
| 399 | // This is the reset value, so has no effect. | ||
| 521 | #[cfg(any(adc_g0, adc_u0))] | 400 | #[cfg(any(adc_g0, adc_u0))] |
| 522 | T::regs().cfgr1().modify(|reg| { | 401 | T::regs().cfgr1().modify(|reg| { |
| 523 | reg.set_discen(false); | 402 | reg.set_chselrmod(false); |
| 524 | reg.set_cont(true); | ||
| 525 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 526 | reg.set_dmaen(true); | ||
| 527 | }); | 403 | }); |
| 528 | 404 | ||
| 529 | let request = rx_dma.request(); | 405 | blocking_delay_us(20); |
| 530 | let transfer = unsafe { | 406 | } |
| 531 | Transfer::new_read( | ||
| 532 | rx_dma, | ||
| 533 | request, | ||
| 534 | T::regs().dr().as_ptr() as *mut u16, | ||
| 535 | readings, | ||
| 536 | Default::default(), | ||
| 537 | ) | ||
| 538 | }; | ||
| 539 | 407 | ||
| 540 | // Start conversion | 408 | /// Calibrate to remove conversion offset |
| 409 | fn init_calibrate() { | ||
| 541 | T::regs().cr().modify(|reg| { | 410 | T::regs().cr().modify(|reg| { |
| 542 | reg.set_adstart(true); | 411 | reg.set_adcal(true); |
| 543 | }); | 412 | }); |
| 544 | 413 | ||
| 545 | // Wait for conversion sequence to finish. | 414 | while T::regs().cr().read().adcal() { |
| 546 | transfer.await; | 415 | // spin |
| 547 | 416 | } | |
| 548 | // Ensure conversions are finished. | ||
| 549 | Self::cancel_conversions(); | ||
| 550 | 417 | ||
| 551 | // Reset configuration. | 418 | blocking_delay_us(1); |
| 552 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 553 | T::regs().cfgr().modify(|reg| { | ||
| 554 | reg.set_cont(false); | ||
| 555 | }); | ||
| 556 | #[cfg(any(adc_g0, adc_u0))] | ||
| 557 | T::regs().cfgr1().modify(|reg| { | ||
| 558 | reg.set_cont(false); | ||
| 559 | }); | ||
| 560 | } | 419 | } |
| 561 | 420 | ||
| 562 | #[cfg(not(adc_g0))] | 421 | /// Initialize the ADC leaving any analog clock at reset value. |
| 563 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | 422 | /// For G0 and WL, this is the async clock without prescaler. |
| 564 | // RM0492, RM0481, etc. | 423 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 565 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | 424 | Self::init_regulator(); |
| 566 | #[cfg(any(adc_h5, adc_h7rs))] | 425 | Self::init_calibrate(); |
| 567 | if channel.channel() == 0 { | 426 | Self { adc } |
| 568 | T::regs().or().modify(|reg| reg.set_op0(true)); | ||
| 569 | } | ||
| 570 | |||
| 571 | // Configure channel | ||
| 572 | Self::set_channel_sample_time(channel.channel(), sample_time); | ||
| 573 | } | 427 | } |
| 574 | 428 | ||
| 575 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 429 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { |
| 576 | self.enable(); | ||
| 577 | #[cfg(not(adc_g0))] | 430 | #[cfg(not(adc_g0))] |
| 578 | Self::configure_channel(channel, self.sample_time); | 431 | let s = Self::new(adc); |
| 432 | |||
| 579 | #[cfg(adc_g0)] | 433 | #[cfg(adc_g0)] |
| 580 | T::regs().smpr().write(|reg| { | 434 | let s = match config.clock { |
| 581 | reg.set_sample_time(0, self.sample_time); | 435 | Some(clock) => Self::new_with_clock(adc, clock), |
| 582 | reg.set_smpsel(channel.channel().into(), Smpsel::SMP1); | 436 | None => Self::new(adc), |
| 583 | }); | 437 | }; |
| 584 | // Select channel | 438 | |
| 585 | #[cfg(not(any(adc_g0, adc_u0)))] | 439 | #[cfg(any(adc_g0, adc_u0, adc_v3))] |
| 586 | T::regs().sqr1().write(|reg| reg.set_sq(0, channel.channel())); | 440 | if let Some(shift) = config.oversampling_shift { |
| 441 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 442 | } | ||
| 443 | |||
| 444 | #[cfg(any(adc_g0, adc_u0, adc_v3))] | ||
| 445 | if let Some(ratio) = config.oversampling_ratio { | ||
| 446 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 447 | } | ||
| 448 | |||
| 587 | #[cfg(any(adc_g0, adc_u0))] | 449 | #[cfg(any(adc_g0, adc_u0))] |
| 588 | T::regs().chselr().write(|reg| { | 450 | if let Some(enable) = config.oversampling_enable { |
| 589 | #[cfg(adc_g0)] | 451 | T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); |
| 590 | reg.set_chsel(channel.channel().into(), true); | 452 | } |
| 591 | #[cfg(adc_u0)] | ||
| 592 | reg.set_chsel(1 << channel.channel()); | ||
| 593 | }); | ||
| 594 | 453 | ||
| 595 | // Some models are affected by an erratum: | 454 | #[cfg(adc_v3)] |
| 596 | // If we perform conversions slower than 1 kHz, the first read ADC value can be | 455 | if let Some((mode, trig_mode, enable)) = config.oversampling_mode { |
| 597 | // corrupted, so we discard it and measure again. | 456 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); |
| 598 | // | 457 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); |
| 599 | // STM32L471xx: Section 2.7.3 | 458 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); |
| 600 | // STM32G4: Section 2.7.3 | 459 | } |
| 601 | #[cfg(any(rcc_l4, rcc_g4))] | ||
| 602 | let _ = self.convert(); | ||
| 603 | let val = self.convert(); | ||
| 604 | 460 | ||
| 605 | T::regs().cr().modify(|reg| reg.set_addis(true)); | 461 | if let Some(resolution) = config.resolution { |
| 462 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 463 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 464 | #[cfg(any(adc_g0, adc_u0))] | ||
| 465 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); | ||
| 466 | } | ||
| 606 | 467 | ||
| 607 | // RM0492, RM0481, etc. | 468 | if let Some(averaging) = config.averaging { |
| 608 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | 469 | let (enable, samples, right_shift) = match averaging { |
| 609 | #[cfg(any(adc_h5, adc_h7rs))] | 470 | Averaging::Disabled => (false, 0, 0), |
| 610 | if channel.channel() == 0 { | 471 | Averaging::Samples2 => (true, 0, 1), |
| 611 | T::regs().or().modify(|reg| reg.set_op0(false)); | 472 | Averaging::Samples4 => (true, 1, 2), |
| 473 | Averaging::Samples8 => (true, 2, 3), | ||
| 474 | Averaging::Samples16 => (true, 3, 4), | ||
| 475 | Averaging::Samples32 => (true, 4, 5), | ||
| 476 | Averaging::Samples64 => (true, 5, 6), | ||
| 477 | Averaging::Samples128 => (true, 6, 7), | ||
| 478 | Averaging::Samples256 => (true, 7, 8), | ||
| 479 | }; | ||
| 480 | T::regs().cfgr2().modify(|reg| { | ||
| 481 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 482 | reg.set_rovse(enable); | ||
| 483 | #[cfg(any(adc_g0, adc_u0))] | ||
| 484 | reg.set_ovse(enable); | ||
| 485 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 486 | reg.set_ovsr(samples.into()); | ||
| 487 | #[cfg(not(any(adc_h5, adc_h7rs)))] | ||
| 488 | reg.set_ovsr(samples.into()); | ||
| 489 | reg.set_ovss(right_shift.into()); | ||
| 490 | }) | ||
| 612 | } | 491 | } |
| 613 | 492 | ||
| 614 | val | 493 | s |
| 615 | } | 494 | } |
| 616 | 495 | ||
| 617 | #[cfg(adc_g0)] | 496 | #[cfg(adc_g0)] |
| 618 | pub fn set_oversampling_shift(&mut self, shift: Ovss) { | 497 | /// Initialize ADC with explicit clock for the analog ADC |
| 619 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | 498 | pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { |
| 620 | } | 499 | Self::init_regulator(); |
| 621 | #[cfg(adc_u0)] | ||
| 622 | pub fn set_oversampling_shift(&mut self, shift: u8) { | ||
| 623 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 624 | } | ||
| 625 | 500 | ||
| 626 | #[cfg(adc_g0)] | 501 | #[cfg(any(stm32wl5x))] |
| 627 | pub fn set_oversampling_ratio(&mut self, ratio: Ovsr) { | 502 | { |
| 628 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | 503 | // Reset value 0 is actually _No clock selected_ in the STM32WL5x reference manual |
| 629 | } | 504 | let async_clock_available = pac::RCC.ccipr().read().adcsel() != pac::rcc::vals::Adcsel::_RESERVED_0; |
| 630 | #[cfg(adc_u0)] | 505 | match clock { |
| 631 | pub fn set_oversampling_ratio(&mut self, ratio: u8) { | 506 | Clock::Async { div: _ } => { |
| 632 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | 507 | assert!(async_clock_available); |
| 633 | } | 508 | } |
| 509 | Clock::Sync { div: _ } => { | ||
| 510 | if async_clock_available { | ||
| 511 | warn!("Not using configured ADC clock"); | ||
| 512 | } | ||
| 513 | } | ||
| 514 | } | ||
| 515 | } | ||
| 516 | match clock { | ||
| 517 | Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div)), | ||
| 518 | Clock::Sync { div } => T::regs().cfgr2().modify(|reg| { | ||
| 519 | reg.set_ckmode(match div { | ||
| 520 | CkModePclk::DIV1 => Ckmode::PCLK, | ||
| 521 | CkModePclk::DIV2 => Ckmode::PCLK_DIV2, | ||
| 522 | CkModePclk::DIV4 => Ckmode::PCLK_DIV4, | ||
| 523 | }) | ||
| 524 | }), | ||
| 525 | } | ||
| 634 | 526 | ||
| 635 | #[cfg(any(adc_g0, adc_u0))] | 527 | Self::init_calibrate(); |
| 636 | pub fn oversampling_enable(&mut self, enable: bool) { | ||
| 637 | T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); | ||
| 638 | } | ||
| 639 | 528 | ||
| 640 | #[cfg(adc_v3)] | 529 | Self { adc } |
| 641 | pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { | ||
| 642 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 643 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 644 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 645 | } | 530 | } |
| 646 | 531 | ||
| 647 | #[cfg(adc_v3)] | 532 | pub fn enable_vrefint(&self) -> VrefInt { |
| 648 | pub fn set_oversampling_ratio(&mut self, ratio: OversamplingRatio) { | 533 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 649 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | 534 | T::common_regs().ccr().modify(|reg| { |
| 535 | reg.set_vrefen(true); | ||
| 536 | }); | ||
| 537 | #[cfg(any(adc_g0, adc_u0))] | ||
| 538 | T::regs().ccr().modify(|reg| { | ||
| 539 | reg.set_vrefen(true); | ||
| 540 | }); | ||
| 541 | |||
| 542 | // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us | ||
| 543 | // to stabilize the internal voltage reference. | ||
| 544 | blocking_delay_us(15); | ||
| 545 | |||
| 546 | VrefInt {} | ||
| 650 | } | 547 | } |
| 651 | 548 | ||
| 652 | #[cfg(adc_v3)] | 549 | pub fn enable_temperature(&self) -> Temperature { |
| 653 | pub fn set_oversampling_shift(&mut self, shift: OversamplingShift) { | 550 | cfg_if! { |
| 654 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | 551 | if #[cfg(any(adc_g0, adc_u0))] { |
| 552 | T::regs().ccr().modify(|reg| { | ||
| 553 | reg.set_tsen(true); | ||
| 554 | }); | ||
| 555 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 556 | T::common_regs().ccr().modify(|reg| { | ||
| 557 | reg.set_tsen(true); | ||
| 558 | }); | ||
| 559 | } else { | ||
| 560 | T::common_regs().ccr().modify(|reg| { | ||
| 561 | reg.set_ch17sel(true); | ||
| 562 | }); | ||
| 563 | } | ||
| 564 | } | ||
| 565 | |||
| 566 | Temperature {} | ||
| 655 | } | 567 | } |
| 656 | 568 | ||
| 657 | #[cfg(not(adc_g0))] | 569 | pub fn enable_vbat(&self) -> Vbat { |
| 658 | fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { | ||
| 659 | cfg_if! { | 570 | cfg_if! { |
| 660 | if #[cfg(adc_u0)] { | 571 | if #[cfg(any(adc_g0, adc_u0))] { |
| 661 | // On G0 and U6 all channels use the same sampling time. | 572 | T::regs().ccr().modify(|reg| { |
| 662 | T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); | 573 | reg.set_vbaten(true); |
| 574 | }); | ||
| 663 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 575 | } else if #[cfg(any(adc_h5, adc_h7rs))] { |
| 664 | match _ch { | 576 | T::common_regs().ccr().modify(|reg| { |
| 665 | 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), | 577 | reg.set_vbaten(true); |
| 666 | _ => T::regs().smpr2().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), | 578 | }); |
| 667 | } | ||
| 668 | } else { | 579 | } else { |
| 669 | let sample_time = sample_time.into(); | 580 | T::common_regs().ccr().modify(|reg| { |
| 670 | T::regs() | 581 | reg.set_ch18sel(true); |
| 671 | .smpr(_ch as usize / 10) | 582 | }); |
| 672 | .modify(|reg| reg.set_smp(_ch as usize % 10, sample_time)); | ||
| 673 | } | 583 | } |
| 674 | } | 584 | } |
| 585 | |||
| 586 | Vbat {} | ||
| 675 | } | 587 | } |
| 676 | 588 | ||
| 677 | fn cancel_conversions() { | 589 | pub fn disable_vbat(&self) { |
| 678 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | 590 | cfg_if! { |
| 679 | T::regs().cr().modify(|reg| { | 591 | if #[cfg(any(adc_g0, adc_u0))] { |
| 680 | reg.set_adstp(true); | 592 | T::regs().ccr().modify(|reg| { |
| 681 | }); | 593 | reg.set_vbaten(false); |
| 682 | while T::regs().cr().read().adstart() {} | 594 | }); |
| 595 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 596 | T::common_regs().ccr().modify(|reg| { | ||
| 597 | reg.set_vbaten(false); | ||
| 598 | }); | ||
| 599 | } else { | ||
| 600 | T::common_regs().ccr().modify(|reg| { | ||
| 601 | reg.set_ch18sel(false); | ||
| 602 | }); | ||
| 603 | } | ||
| 683 | } | 604 | } |
| 684 | } | 605 | } |
| 606 | |||
| 607 | /* | ||
| 608 | /// Convert a raw sample from the `Temperature` to deg C | ||
| 609 | pub fn to_degrees_centigrade(sample: u16) -> f32 { | ||
| 610 | (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) | ||
| 611 | * (sample as f32 - VtempCal30::get().read() as f32) | ||
| 612 | + 30.0 | ||
| 613 | } | ||
| 614 | */ | ||
| 685 | } | 615 | } |
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index b66437e6e..962816194 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs | |||
| @@ -4,12 +4,12 @@ use pac::adc::vals::{Adcaldif, Boost}; | |||
| 4 | use pac::adc::vals::{Adstp, Difsel, Dmngt, Exten, Pcsel}; | 4 | 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::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 8 | blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, | 8 | #[cfg(stm32u5)] |
| 9 | }; | 9 | use crate::adc::DefaultInstance; |
| 10 | use crate::dma::Transfer; | 10 | use crate::adc::{AdcRegs, ConversionMode}; |
| 11 | use crate::time::Hertz; | 11 | use crate::time::Hertz; |
| 12 | use crate::{pac, rcc, Peri}; | 12 | use crate::{Peri, pac, rcc}; |
| 13 | 13 | ||
| 14 | /// Default VREF voltage used for sample conversion to millivolts. | 14 | /// Default VREF voltage used for sample conversion to millivolts. |
| 15 | pub const VREF_DEFAULT_MV: u32 = 3300; | 15 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| @@ -25,153 +25,228 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); | |||
| 25 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55); | 25 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55); |
| 26 | 26 | ||
| 27 | #[cfg(stm32g4)] | 27 | #[cfg(stm32g4)] |
| 28 | const VREF_CHANNEL: u8 = 18; | 28 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 29 | const CHANNEL: u8 = 18; | ||
| 30 | } | ||
| 29 | #[cfg(stm32g4)] | 31 | #[cfg(stm32g4)] |
| 30 | const TEMP_CHANNEL: u8 = 16; | 32 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 33 | const CHANNEL: u8 = 16; | ||
| 34 | } | ||
| 31 | 35 | ||
| 32 | #[cfg(stm32h7)] | 36 | #[cfg(stm32h7)] |
| 33 | const VREF_CHANNEL: u8 = 19; | 37 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 38 | const CHANNEL: u8 = 19; | ||
| 39 | } | ||
| 34 | #[cfg(stm32h7)] | 40 | #[cfg(stm32h7)] |
| 35 | const TEMP_CHANNEL: u8 = 18; | 41 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 42 | const CHANNEL: u8 = 18; | ||
| 43 | } | ||
| 36 | 44 | ||
| 37 | // TODO this should be 14 for H7a/b/35 | 45 | // TODO this should be 14 for H7a/b/35 |
| 38 | #[cfg(not(stm32u5))] | 46 | #[cfg(not(stm32u5))] |
| 39 | const VBAT_CHANNEL: u8 = 17; | 47 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 48 | const CHANNEL: u8 = 17; | ||
| 49 | } | ||
| 40 | 50 | ||
| 41 | #[cfg(stm32u5)] | 51 | #[cfg(stm32u5)] |
| 42 | const VREF_CHANNEL: u8 = 0; | 52 | impl<T: DefaultInstance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 53 | const CHANNEL: u8 = 0; | ||
| 54 | } | ||
| 43 | #[cfg(stm32u5)] | 55 | #[cfg(stm32u5)] |
| 44 | const TEMP_CHANNEL: u8 = 19; | 56 | impl<T: DefaultInstance> super::SealedSpecialConverter<super::Temperature> for T { |
| 57 | const CHANNEL: u8 = 19; | ||
| 58 | } | ||
| 45 | #[cfg(stm32u5)] | 59 | #[cfg(stm32u5)] |
| 46 | const VBAT_CHANNEL: u8 = 18; | 60 | impl<T: DefaultInstance> super::SealedSpecialConverter<super::Vbat> for T { |
| 47 | 61 | const CHANNEL: u8 = 18; | |
| 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 | } | 62 | } |
| 57 | 63 | ||
| 58 | /// Internal temperature channel. | 64 | fn from_ker_ck(frequency: Hertz) -> Presc { |
| 59 | pub struct Temperature; | 65 | let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); |
| 60 | impl<T: Instance> AdcChannel<T> for Temperature {} | 66 | match raw_prescaler { |
| 61 | impl<T: Instance> SealedAdcChannel<T> for Temperature { | 67 | 0 => Presc::DIV1, |
| 62 | fn channel(&self) -> u8 { | 68 | 1 => Presc::DIV2, |
| 63 | TEMP_CHANNEL | 69 | 2..=3 => Presc::DIV4, |
| 70 | 4..=5 => Presc::DIV6, | ||
| 71 | 6..=7 => Presc::DIV8, | ||
| 72 | 8..=9 => Presc::DIV10, | ||
| 73 | 10..=11 => Presc::DIV12, | ||
| 74 | _ => unimplemented!(), | ||
| 64 | } | 75 | } |
| 65 | } | 76 | } |
| 66 | 77 | ||
| 67 | /// Internal battery voltage channel. | 78 | /// Adc configuration |
| 68 | pub struct Vbat; | 79 | #[derive(Default)] |
| 69 | impl<T: Instance> AdcChannel<T> for Vbat {} | 80 | pub struct AdcConfig { |
| 70 | impl<T: Instance> SealedAdcChannel<T> for Vbat { | 81 | pub resolution: Option<Resolution>, |
| 71 | fn channel(&self) -> u8 { | 82 | pub averaging: Option<Averaging>, |
| 72 | VBAT_CHANNEL | ||
| 73 | } | ||
| 74 | } | 83 | } |
| 75 | 84 | ||
| 76 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | 85 | impl AdcRegs for crate::pac::adc::Adc { |
| 77 | // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. | 86 | fn data(&self) -> *mut u16 { |
| 78 | #[allow(unused)] | 87 | crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16 |
| 79 | enum Prescaler { | 88 | } |
| 80 | NotDivided, | 89 | |
| 81 | DividedBy2, | 90 | fn enable(&self) { |
| 82 | DividedBy4, | 91 | self.isr().write(|w| w.set_adrdy(true)); |
| 83 | DividedBy6, | 92 | self.cr().modify(|w| w.set_aden(true)); |
| 84 | DividedBy8, | 93 | while !self.isr().read().adrdy() {} |
| 85 | DividedBy10, | 94 | self.isr().write(|w| w.set_adrdy(true)); |
| 86 | DividedBy12, | 95 | } |
| 87 | DividedBy16, | ||
| 88 | DividedBy32, | ||
| 89 | DividedBy64, | ||
| 90 | DividedBy128, | ||
| 91 | DividedBy256, | ||
| 92 | } | ||
| 93 | 96 | ||
| 94 | impl Prescaler { | 97 | fn start(&self) { |
| 95 | fn from_ker_ck(frequency: Hertz) -> Self { | 98 | // Start conversion |
| 96 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | 99 | self.cr().modify(|reg| { |
| 97 | match raw_prescaler { | 100 | reg.set_adstart(true); |
| 98 | 0 => Self::NotDivided, | 101 | }); |
| 99 | 1 => Self::DividedBy2, | 102 | } |
| 100 | 2..=3 => Self::DividedBy4, | 103 | |
| 101 | 4..=5 => Self::DividedBy6, | 104 | fn stop(&self) { |
| 102 | 6..=7 => Self::DividedBy8, | 105 | if self.cr().read().adstart() && !self.cr().read().addis() { |
| 103 | 8..=9 => Self::DividedBy10, | 106 | self.cr().modify(|reg| { |
| 104 | 10..=11 => Self::DividedBy12, | 107 | reg.set_adstp(Adstp::STOP); |
| 105 | _ => unimplemented!(), | 108 | }); |
| 109 | while self.cr().read().adstart() {} | ||
| 106 | } | 110 | } |
| 111 | |||
| 112 | // Reset configuration. | ||
| 113 | self.cfgr().modify(|reg| { | ||
| 114 | reg.set_cont(false); | ||
| 115 | reg.set_dmngt(Dmngt::from_bits(0)); | ||
| 116 | }); | ||
| 107 | } | 117 | } |
| 108 | 118 | ||
| 109 | fn divisor(&self) -> u32 { | 119 | fn convert(&self) { |
| 110 | match self { | 120 | self.isr().modify(|reg| { |
| 111 | Prescaler::NotDivided => 1, | 121 | reg.set_eos(true); |
| 112 | Prescaler::DividedBy2 => 2, | 122 | reg.set_eoc(true); |
| 113 | Prescaler::DividedBy4 => 4, | 123 | }); |
| 114 | Prescaler::DividedBy6 => 6, | 124 | |
| 115 | Prescaler::DividedBy8 => 8, | 125 | // Start conversion |
| 116 | Prescaler::DividedBy10 => 10, | 126 | self.cr().modify(|reg| { |
| 117 | Prescaler::DividedBy12 => 12, | 127 | reg.set_adstart(true); |
| 118 | Prescaler::DividedBy16 => 16, | 128 | }); |
| 119 | Prescaler::DividedBy32 => 32, | 129 | |
| 120 | Prescaler::DividedBy64 => 64, | 130 | while !self.isr().read().eos() { |
| 121 | Prescaler::DividedBy128 => 128, | 131 | // spin |
| 122 | Prescaler::DividedBy256 => 256, | ||
| 123 | } | 132 | } |
| 124 | } | 133 | } |
| 125 | 134 | ||
| 126 | fn presc(&self) -> Presc { | 135 | fn configure_dma(&self, conversion_mode: ConversionMode) { |
| 127 | match self { | 136 | match conversion_mode { |
| 128 | Prescaler::NotDivided => Presc::DIV1, | 137 | ConversionMode::Singular => { |
| 129 | Prescaler::DividedBy2 => Presc::DIV2, | 138 | self.isr().modify(|reg| { |
| 130 | Prescaler::DividedBy4 => Presc::DIV4, | 139 | reg.set_ovr(true); |
| 131 | Prescaler::DividedBy6 => Presc::DIV6, | 140 | }); |
| 132 | Prescaler::DividedBy8 => Presc::DIV8, | 141 | self.cfgr().modify(|reg| { |
| 133 | Prescaler::DividedBy10 => Presc::DIV10, | 142 | reg.set_cont(true); |
| 134 | Prescaler::DividedBy12 => Presc::DIV12, | 143 | reg.set_dmngt(Dmngt::DMA_ONE_SHOT); |
| 135 | Prescaler::DividedBy16 => Presc::DIV16, | 144 | }); |
| 136 | Prescaler::DividedBy32 => Presc::DIV32, | 145 | } |
| 137 | Prescaler::DividedBy64 => Presc::DIV64, | 146 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] |
| 138 | Prescaler::DividedBy128 => Presc::DIV128, | 147 | _ => unreachable!(), |
| 139 | Prescaler::DividedBy256 => Presc::DIV256, | ||
| 140 | } | 148 | } |
| 141 | } | 149 | } |
| 142 | } | ||
| 143 | 150 | ||
| 144 | /// Number of samples used for averaging. | 151 | fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| 145 | #[derive(Copy, Clone, Debug)] | 152 | // Set sequence length |
| 146 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 153 | self.sqr1().modify(|w| { |
| 147 | pub enum Averaging { | 154 | w.set_l(sequence.len() as u8 - 1); |
| 148 | Disabled, | 155 | }); |
| 149 | Samples2, | 156 | |
| 150 | Samples4, | 157 | // Configure channels and ranks |
| 151 | Samples8, | 158 | for (i, ((channel, _), sample_time)) in sequence.enumerate() { |
| 152 | Samples16, | 159 | let sample_time = sample_time.into(); |
| 153 | Samples32, | 160 | if channel <= 9 { |
| 154 | Samples64, | 161 | self.smpr(0).modify(|reg| reg.set_smp(channel as _, sample_time)); |
| 155 | Samples128, | 162 | } else { |
| 156 | Samples256, | 163 | self.smpr(1).modify(|reg| reg.set_smp((channel - 10) as _, sample_time)); |
| 157 | Samples512, | 164 | } |
| 158 | Samples1024, | 165 | |
| 166 | #[cfg(any(stm32h7, stm32u5))] | ||
| 167 | { | ||
| 168 | self.cfgr2().modify(|w| w.set_lshift(0)); | ||
| 169 | self.pcsel().modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); | ||
| 170 | } | ||
| 171 | |||
| 172 | match i { | ||
| 173 | 0..=3 => { | ||
| 174 | self.sqr1().modify(|w| { | ||
| 175 | w.set_sq(i, channel); | ||
| 176 | }); | ||
| 177 | } | ||
| 178 | 4..=8 => { | ||
| 179 | self.sqr2().modify(|w| { | ||
| 180 | w.set_sq(i - 4, channel); | ||
| 181 | }); | ||
| 182 | } | ||
| 183 | 9..=13 => { | ||
| 184 | self.sqr3().modify(|w| { | ||
| 185 | w.set_sq(i - 9, channel); | ||
| 186 | }); | ||
| 187 | } | ||
| 188 | 14..=15 => { | ||
| 189 | self.sqr4().modify(|w| { | ||
| 190 | w.set_sq(i - 14, channel); | ||
| 191 | }); | ||
| 192 | } | ||
| 193 | _ => unreachable!(), | ||
| 194 | } | ||
| 195 | } | ||
| 196 | } | ||
| 159 | } | 197 | } |
| 160 | 198 | ||
| 161 | impl<'d, T: Instance> Adc<'d, T> { | 199 | impl<'d, T: Instance<Regs = crate::pac::adc::Adc>> Adc<'d, T> { |
| 200 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { | ||
| 201 | let s = Self::new(adc); | ||
| 202 | |||
| 203 | // Set the ADC resolution. | ||
| 204 | if let Some(resolution) = config.resolution { | ||
| 205 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 206 | } | ||
| 207 | |||
| 208 | // Set hardware averaging. | ||
| 209 | if let Some(averaging) = config.averaging { | ||
| 210 | let (enable, samples, right_shift) = match averaging { | ||
| 211 | Averaging::Disabled => (false, 0, 0), | ||
| 212 | Averaging::Samples2 => (true, 1, 1), | ||
| 213 | Averaging::Samples4 => (true, 3, 2), | ||
| 214 | Averaging::Samples8 => (true, 7, 3), | ||
| 215 | Averaging::Samples16 => (true, 15, 4), | ||
| 216 | Averaging::Samples32 => (true, 31, 5), | ||
| 217 | Averaging::Samples64 => (true, 63, 6), | ||
| 218 | Averaging::Samples128 => (true, 127, 7), | ||
| 219 | Averaging::Samples256 => (true, 255, 8), | ||
| 220 | Averaging::Samples512 => (true, 511, 9), | ||
| 221 | Averaging::Samples1024 => (true, 1023, 10), | ||
| 222 | }; | ||
| 223 | |||
| 224 | T::regs().cfgr2().modify(|reg| { | ||
| 225 | reg.set_rovse(enable); | ||
| 226 | reg.set_ovsr(samples); | ||
| 227 | reg.set_ovss(right_shift); | ||
| 228 | }) | ||
| 229 | } | ||
| 230 | |||
| 231 | s | ||
| 232 | } | ||
| 233 | |||
| 162 | /// Create a new ADC driver. | 234 | /// Create a new ADC driver. |
| 163 | pub fn new(adc: Peri<'d, T>) -> Self { | 235 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 164 | rcc::enable_and_reset::<T>(); | 236 | rcc::enable_and_reset::<T>(); |
| 165 | 237 | ||
| 166 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 238 | let prescaler = from_ker_ck(T::frequency()); |
| 167 | 239 | ||
| 168 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 240 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 169 | 241 | ||
| 170 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 242 | let frequency = T::frequency() / prescaler; |
| 171 | info!("ADC frequency set to {}", frequency); | 243 | info!("ADC frequency set to {}", frequency); |
| 172 | 244 | ||
| 173 | if frequency > MAX_ADC_CLK_FREQ { | 245 | 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 ); | 246 | panic!( |
| 247 | "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", | ||
| 248 | MAX_ADC_CLK_FREQ.0 / 1_000_000 | ||
| 249 | ); | ||
| 175 | } | 250 | } |
| 176 | 251 | ||
| 177 | #[cfg(stm32h7)] | 252 | #[cfg(stm32h7)] |
| @@ -187,40 +262,20 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 187 | }; | 262 | }; |
| 188 | T::regs().cr().modify(|w| w.set_boost(boost)); | 263 | T::regs().cr().modify(|w| w.set_boost(boost)); |
| 189 | } | 264 | } |
| 190 | let mut s = Self { | ||
| 191 | adc, | ||
| 192 | sample_time: SampleTime::from_bits(0), | ||
| 193 | }; | ||
| 194 | s.power_up(); | ||
| 195 | s.configure_differential_inputs(); | ||
| 196 | |||
| 197 | s.calibrate(); | ||
| 198 | blocking_delay_us(1); | ||
| 199 | 265 | ||
| 200 | s.enable(); | ||
| 201 | s.configure(); | ||
| 202 | |||
| 203 | s | ||
| 204 | } | ||
| 205 | |||
| 206 | fn power_up(&mut self) { | ||
| 207 | T::regs().cr().modify(|reg| { | 266 | T::regs().cr().modify(|reg| { |
| 208 | reg.set_deeppwd(false); | 267 | reg.set_deeppwd(false); |
| 209 | reg.set_advregen(true); | 268 | reg.set_advregen(true); |
| 210 | }); | 269 | }); |
| 211 | 270 | ||
| 212 | blocking_delay_us(10); | 271 | blocking_delay_us(10); |
| 213 | } | ||
| 214 | 272 | ||
| 215 | fn configure_differential_inputs(&mut self) { | ||
| 216 | T::regs().difsel().modify(|w| { | 273 | T::regs().difsel().modify(|w| { |
| 217 | for n in 0..20 { | 274 | for n in 0..20 { |
| 218 | w.set_difsel(n, Difsel::SINGLE_ENDED); | 275 | w.set_difsel(n, Difsel::SINGLE_ENDED); |
| 219 | } | 276 | } |
| 220 | }); | 277 | }); |
| 221 | } | ||
| 222 | 278 | ||
| 223 | fn calibrate(&mut self) { | ||
| 224 | T::regs().cr().modify(|w| { | 279 | T::regs().cr().modify(|w| { |
| 225 | #[cfg(not(adc_u5))] | 280 | #[cfg(not(adc_u5))] |
| 226 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); | 281 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); |
| @@ -230,21 +285,18 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 230 | T::regs().cr().modify(|w| w.set_adcal(true)); | 285 | T::regs().cr().modify(|w| w.set_adcal(true)); |
| 231 | 286 | ||
| 232 | while T::regs().cr().read().adcal() {} | 287 | while T::regs().cr().read().adcal() {} |
| 233 | } | ||
| 234 | 288 | ||
| 235 | fn enable(&mut self) { | 289 | blocking_delay_us(1); |
| 236 | T::regs().isr().write(|w| w.set_adrdy(true)); | 290 | |
| 237 | T::regs().cr().modify(|w| w.set_aden(true)); | 291 | T::regs().enable(); |
| 238 | while !T::regs().isr().read().adrdy() {} | ||
| 239 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 240 | } | ||
| 241 | 292 | ||
| 242 | fn configure(&mut self) { | ||
| 243 | // single conversion mode, software trigger | 293 | // single conversion mode, software trigger |
| 244 | T::regs().cfgr().modify(|w| { | 294 | T::regs().cfgr().modify(|w| { |
| 245 | w.set_cont(false); | 295 | w.set_cont(false); |
| 246 | w.set_exten(Exten::DISABLED); | 296 | w.set_exten(Exten::DISABLED); |
| 247 | }); | 297 | }); |
| 298 | |||
| 299 | Self { adc } | ||
| 248 | } | 300 | } |
| 249 | 301 | ||
| 250 | /// Enable reading the voltage reference internal channel. | 302 | /// Enable reading the voltage reference internal channel. |
| @@ -273,228 +325,4 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 273 | 325 | ||
| 274 | Vbat {} | 326 | Vbat {} |
| 275 | } | 327 | } |
| 276 | |||
| 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. | ||
| 288 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 289 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 290 | } | ||
| 291 | |||
| 292 | /// Set hardware averaging. | ||
| 293 | pub fn set_averaging(&mut self, averaging: Averaging) { | ||
| 294 | let (enable, samples, right_shift) = match averaging { | ||
| 295 | Averaging::Disabled => (false, 0, 0), | ||
| 296 | Averaging::Samples2 => (true, 1, 1), | ||
| 297 | Averaging::Samples4 => (true, 3, 2), | ||
| 298 | Averaging::Samples8 => (true, 7, 3), | ||
| 299 | Averaging::Samples16 => (true, 15, 4), | ||
| 300 | Averaging::Samples32 => (true, 31, 5), | ||
| 301 | Averaging::Samples64 => (true, 63, 6), | ||
| 302 | Averaging::Samples128 => (true, 127, 7), | ||
| 303 | Averaging::Samples256 => (true, 255, 8), | ||
| 304 | Averaging::Samples512 => (true, 511, 9), | ||
| 305 | Averaging::Samples1024 => (true, 1023, 10), | ||
| 306 | }; | ||
| 307 | |||
| 308 | T::regs().cfgr2().modify(|reg| { | ||
| 309 | reg.set_rovse(enable); | ||
| 310 | reg.set_ovsr(samples); | ||
| 311 | reg.set_ovss(right_shift); | ||
| 312 | }) | ||
| 313 | } | ||
| 314 | |||
| 315 | /// Perform a single conversion. | ||
| 316 | fn convert(&mut self) -> u16 { | ||
| 317 | T::regs().isr().modify(|reg| { | ||
| 318 | reg.set_eos(true); | ||
| 319 | reg.set_eoc(true); | ||
| 320 | }); | ||
| 321 | |||
| 322 | // Start conversion | ||
| 323 | T::regs().cr().modify(|reg| { | ||
| 324 | reg.set_adstart(true); | ||
| 325 | }); | ||
| 326 | |||
| 327 | while !T::regs().isr().read().eos() { | ||
| 328 | // spin | ||
| 329 | } | ||
| 330 | |||
| 331 | T::regs().dr().read().0 as u16 | ||
| 332 | } | ||
| 333 | |||
| 334 | /// Read an ADC channel. | ||
| 335 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 336 | self.read_channel(channel) | ||
| 337 | } | ||
| 338 | |||
| 339 | /// Read one or multiple ADC channels using DMA. | ||
| 340 | /// | ||
| 341 | /// `sequence` iterator and `readings` must have the same length. | ||
| 342 | /// | ||
| 343 | /// Example | ||
| 344 | /// ```rust,ignore | ||
| 345 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 346 | /// | ||
| 347 | /// let mut adc = Adc::new(p.ADC1); | ||
| 348 | /// let mut adc_pin0 = p.PA0.into(); | ||
| 349 | /// let mut adc_pin2 = p.PA2.into(); | ||
| 350 | /// let mut measurements = [0u16; 2]; | ||
| 351 | /// | ||
| 352 | /// adc.read( | ||
| 353 | /// p.DMA2_CH0.reborrow(), | ||
| 354 | /// [ | ||
| 355 | /// (&mut *adc_pin0, SampleTime::CYCLES112), | ||
| 356 | /// (&mut *adc_pin2, SampleTime::CYCLES112), | ||
| 357 | /// ] | ||
| 358 | /// .into_iter(), | ||
| 359 | /// &mut measurements, | ||
| 360 | /// ) | ||
| 361 | /// .await; | ||
| 362 | /// defmt::info!("measurements: {}", measurements); | ||
| 363 | /// ``` | ||
| 364 | pub async fn read( | ||
| 365 | &mut self, | ||
| 366 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 367 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 368 | readings: &mut [u16], | ||
| 369 | ) { | ||
| 370 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 371 | assert!( | ||
| 372 | sequence.len() == readings.len(), | ||
| 373 | "Sequence length must be equal to readings length" | ||
| 374 | ); | ||
| 375 | assert!( | ||
| 376 | sequence.len() <= 16, | ||
| 377 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 378 | ); | ||
| 379 | |||
| 380 | // Ensure no conversions are ongoing | ||
| 381 | Self::cancel_conversions(); | ||
| 382 | |||
| 383 | // Set sequence length | ||
| 384 | T::regs().sqr1().modify(|w| { | ||
| 385 | w.set_l(sequence.len() as u8 - 1); | ||
| 386 | }); | ||
| 387 | |||
| 388 | // Configure channels and ranks | ||
| 389 | for (i, (channel, sample_time)) in sequence.enumerate() { | ||
| 390 | Self::configure_channel(channel, sample_time); | ||
| 391 | match i { | ||
| 392 | 0..=3 => { | ||
| 393 | T::regs().sqr1().modify(|w| { | ||
| 394 | w.set_sq(i, channel.channel()); | ||
| 395 | }); | ||
| 396 | } | ||
| 397 | 4..=8 => { | ||
| 398 | T::regs().sqr2().modify(|w| { | ||
| 399 | w.set_sq(i - 4, channel.channel()); | ||
| 400 | }); | ||
| 401 | } | ||
| 402 | 9..=13 => { | ||
| 403 | T::regs().sqr3().modify(|w| { | ||
| 404 | w.set_sq(i - 9, channel.channel()); | ||
| 405 | }); | ||
| 406 | } | ||
| 407 | 14..=15 => { | ||
| 408 | T::regs().sqr4().modify(|w| { | ||
| 409 | w.set_sq(i - 14, channel.channel()); | ||
| 410 | }); | ||
| 411 | } | ||
| 412 | _ => unreachable!(), | ||
| 413 | } | ||
| 414 | } | ||
| 415 | |||
| 416 | // Set continuous mode with oneshot dma. | ||
| 417 | // Clear overrun flag before starting transfer. | ||
| 418 | |||
| 419 | T::regs().isr().modify(|reg| { | ||
| 420 | reg.set_ovr(true); | ||
| 421 | }); | ||
| 422 | T::regs().cfgr().modify(|reg| { | ||
| 423 | reg.set_cont(true); | ||
| 424 | reg.set_dmngt(Dmngt::DMA_ONE_SHOT); | ||
| 425 | }); | ||
| 426 | |||
| 427 | let request = rx_dma.request(); | ||
| 428 | let transfer = unsafe { | ||
| 429 | Transfer::new_read( | ||
| 430 | rx_dma, | ||
| 431 | request, | ||
| 432 | T::regs().dr().as_ptr() as *mut u16, | ||
| 433 | readings, | ||
| 434 | Default::default(), | ||
| 435 | ) | ||
| 436 | }; | ||
| 437 | |||
| 438 | // Start conversion | ||
| 439 | T::regs().cr().modify(|reg| { | ||
| 440 | reg.set_adstart(true); | ||
| 441 | }); | ||
| 442 | |||
| 443 | // Wait for conversion sequence to finish. | ||
| 444 | transfer.await; | ||
| 445 | |||
| 446 | // Ensure conversions are finished. | ||
| 447 | Self::cancel_conversions(); | ||
| 448 | |||
| 449 | // Reset configuration. | ||
| 450 | T::regs().cfgr().modify(|reg| { | ||
| 451 | reg.set_cont(false); | ||
| 452 | reg.set_dmngt(Dmngt::from_bits(0)); | ||
| 453 | }); | ||
| 454 | } | ||
| 455 | |||
| 456 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | ||
| 457 | channel.setup(); | ||
| 458 | |||
| 459 | let channel = channel.channel(); | ||
| 460 | |||
| 461 | Self::set_channel_sample_time(channel, sample_time); | ||
| 462 | |||
| 463 | #[cfg(any(stm32h7, stm32u5))] | ||
| 464 | { | ||
| 465 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 466 | T::regs() | ||
| 467 | .pcsel() | ||
| 468 | .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); | ||
| 469 | } | ||
| 470 | } | ||
| 471 | |||
| 472 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 473 | Self::configure_channel(channel, self.sample_time); | ||
| 474 | |||
| 475 | T::regs().sqr1().modify(|reg| { | ||
| 476 | reg.set_sq(0, channel.channel()); | ||
| 477 | reg.set_l(0); | ||
| 478 | }); | ||
| 479 | |||
| 480 | self.convert() | ||
| 481 | } | ||
| 482 | |||
| 483 | fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | ||
| 484 | let sample_time = sample_time.into(); | ||
| 485 | if ch <= 9 { | ||
| 486 | T::regs().smpr(0).modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 487 | } else { | ||
| 488 | T::regs().smpr(1).modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 489 | } | ||
| 490 | } | ||
| 491 | |||
| 492 | fn cancel_conversions() { | ||
| 493 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 494 | T::regs().cr().modify(|reg| { | ||
| 495 | reg.set_adstp(Adstp::STOP); | ||
| 496 | }); | ||
| 497 | while T::regs().cr().read().adstart() {} | ||
| 498 | } | ||
| 499 | } | ||
| 500 | } | 328 | } |
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/enums.rs b/embassy-stm32/src/can/enums.rs index 6d91020fc..c5900cadc 100644 --- a/embassy-stm32/src/can/enums.rs +++ b/embassy-stm32/src/can/enums.rs | |||
| @@ -82,3 +82,40 @@ pub enum RefCountOp { | |||
| 82 | /// Notify sender destroyed | 82 | /// Notify sender destroyed |
| 83 | NotifySenderDestroyed, | 83 | NotifySenderDestroyed, |
| 84 | } | 84 | } |
| 85 | |||
| 86 | /// Error returned when calculating the can timing fails | ||
| 87 | #[derive(Debug)] | ||
| 88 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 89 | pub enum TimingCalcError { | ||
| 90 | /// Bitrate is lower than 1000 | ||
| 91 | BitrateTooLow { | ||
| 92 | /// The set bitrate | ||
| 93 | bitrate: u32, | ||
| 94 | }, | ||
| 95 | /// No solution possible | ||
| 96 | NoSolution { | ||
| 97 | /// The sum of BS1 and BS2 | ||
| 98 | bs1_bs2_sum: u8, | ||
| 99 | }, | ||
| 100 | /// Prescaler is not 1 < prescaler < 1024 | ||
| 101 | InvalidPrescaler { | ||
| 102 | /// The calculated prescaler value | ||
| 103 | prescaler: u32, | ||
| 104 | }, | ||
| 105 | /// BS1 or BS2 are not in the range 0 < BSx < BSx_MAX | ||
| 106 | BSNotInRange { | ||
| 107 | /// The value of BS1 | ||
| 108 | bs1: u8, | ||
| 109 | /// The value of BS2 | ||
| 110 | bs2: u8, | ||
| 111 | }, | ||
| 112 | /// Final bitrate doesn't match the requested bitrate | ||
| 113 | NoMatch { | ||
| 114 | /// The requested bitrate | ||
| 115 | requested: u32, | ||
| 116 | /// The calculated bitrate | ||
| 117 | final_calculated: u32, | ||
| 118 | }, | ||
| 119 | /// core::num::NonZeroUxx::new error | ||
| 120 | CoreNumNew, | ||
| 121 | } | ||
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..beca4c34e 100644 --- a/embassy-stm32/src/can/util.rs +++ b/embassy-stm32/src/can/util.rs | |||
| @@ -1,6 +1,8 @@ | |||
| 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 | |||
| 5 | use crate::can::enums::TimingCalcError; | ||
| 4 | 6 | ||
| 5 | /// Shared struct to represent bit timings used by calc_can_timings. | 7 | /// Shared struct to represent bit timings used by calc_can_timings. |
| 6 | #[derive(Clone, Copy, Debug)] | 8 | #[derive(Clone, Copy, Debug)] |
| @@ -17,7 +19,10 @@ pub struct NominalBitTiming { | |||
| 17 | } | 19 | } |
| 18 | 20 | ||
| 19 | /// Calculate nominal CAN bit timing based on CAN bitrate and periphial clock frequency | 21 | /// Calculate nominal CAN bit timing based on CAN bitrate and periphial clock frequency |
| 20 | pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> Option<NominalBitTiming> { | 22 | pub fn calc_can_timings( |
| 23 | periph_clock: crate::time::Hertz, | ||
| 24 | can_bitrate: u32, | ||
| 25 | ) -> Result<NominalBitTiming, TimingCalcError> { | ||
| 21 | const BS1_MAX: u8 = 16; | 26 | const BS1_MAX: u8 = 16; |
| 22 | const BS2_MAX: u8 = 8; | 27 | const BS2_MAX: u8 = 8; |
| 23 | const MAX_SAMPLE_POINT_PERMILL: u16 = 900; | 28 | const MAX_SAMPLE_POINT_PERMILL: u16 = 900; |
| @@ -25,7 +30,7 @@ pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> O | |||
| 25 | let periph_clock = periph_clock.0; | 30 | let periph_clock = periph_clock.0; |
| 26 | 31 | ||
| 27 | if can_bitrate < 1000 { | 32 | if can_bitrate < 1000 { |
| 28 | return None; | 33 | return Err(TimingCalcError::BitrateTooLow { bitrate: can_bitrate }); |
| 29 | } | 34 | } |
| 30 | 35 | ||
| 31 | // Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG | 36 | // Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG |
| @@ -53,14 +58,14 @@ pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> O | |||
| 53 | let mut bs1_bs2_sum = max_quanta_per_bit - 1; | 58 | let mut bs1_bs2_sum = max_quanta_per_bit - 1; |
| 54 | while (prescaler_bs % (1 + bs1_bs2_sum) as u32) != 0 { | 59 | while (prescaler_bs % (1 + bs1_bs2_sum) as u32) != 0 { |
| 55 | if bs1_bs2_sum <= 2 { | 60 | if bs1_bs2_sum <= 2 { |
| 56 | return None; // No solution | 61 | return Err(TimingCalcError::NoSolution { bs1_bs2_sum }); // No solution |
| 57 | } | 62 | } |
| 58 | bs1_bs2_sum -= 1; | 63 | bs1_bs2_sum -= 1; |
| 59 | } | 64 | } |
| 60 | 65 | ||
| 61 | let prescaler = prescaler_bs / (1 + bs1_bs2_sum) as u32; | 66 | let prescaler = prescaler_bs / (1 + bs1_bs2_sum) as u32; |
| 62 | if (prescaler < 1) || (prescaler > 1024) { | 67 | if (prescaler < 1) || (prescaler > 1024) { |
| 63 | return None; // No solution | 68 | return Err(TimingCalcError::InvalidPrescaler { prescaler }); // No solution |
| 64 | } | 69 | } |
| 65 | 70 | ||
| 66 | // Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum. | 71 | // Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum. |
| @@ -93,22 +98,26 @@ pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> O | |||
| 93 | 98 | ||
| 94 | // Check is BS1 and BS2 are in range | 99 | // Check is BS1 and BS2 are in range |
| 95 | if (bs1 < 1) || (bs1 > BS1_MAX) || (bs2 < 1) || (bs2 > BS2_MAX) { | 100 | if (bs1 < 1) || (bs1 > BS1_MAX) || (bs2 < 1) || (bs2 > BS2_MAX) { |
| 96 | return None; | 101 | return Err(TimingCalcError::BSNotInRange { bs1, bs2 }); |
| 97 | } | 102 | } |
| 98 | 103 | ||
| 104 | let calculated = periph_clock / (prescaler * (1 + bs1 + bs2) as u32); | ||
| 99 | // Check if final bitrate matches the requested | 105 | // Check if final bitrate matches the requested |
| 100 | if can_bitrate != (periph_clock / (prescaler * (1 + bs1 + bs2) as u32)) { | 106 | if can_bitrate != calculated { |
| 101 | return None; | 107 | return Err(TimingCalcError::NoMatch { |
| 108 | requested: can_bitrate, | ||
| 109 | final_calculated: calculated, | ||
| 110 | }); | ||
| 102 | } | 111 | } |
| 103 | 112 | ||
| 104 | // One is recommended by DS-015, CANOpen, and DeviceNet | 113 | // One is recommended by DS-015, CANOpen, and DeviceNet |
| 105 | let sync_jump_width = core::num::NonZeroU8::new(1)?; | 114 | let sync_jump_width = core::num::NonZeroU8::new(1).ok_or(TimingCalcError::CoreNumNew)?; |
| 106 | 115 | ||
| 107 | let seg1 = core::num::NonZeroU8::new(bs1)?; | 116 | let seg1 = core::num::NonZeroU8::new(bs1).ok_or(TimingCalcError::CoreNumNew)?; |
| 108 | let seg2 = core::num::NonZeroU8::new(bs2)?; | 117 | let seg2 = core::num::NonZeroU8::new(bs2).ok_or(TimingCalcError::CoreNumNew)?; |
| 109 | let nz_prescaler = core::num::NonZeroU16::new(prescaler as u16)?; | 118 | let nz_prescaler = core::num::NonZeroU16::new(prescaler as u16).ok_or(TimingCalcError::CoreNumNew)?; |
| 110 | 119 | ||
| 111 | Some(NominalBitTiming { | 120 | Ok(NominalBitTiming { |
| 112 | sync_jump_width, | 121 | sync_jump_width, |
| 113 | prescaler: nz_prescaler, | 122 | prescaler: nz_prescaler, |
| 114 | seg1, | 123 | seg1, |
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..adc084474 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; |
| @@ -10,6 +10,7 @@ use super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBu | |||
| 10 | use super::word::{Word, WordSize}; | 10 | use super::word::{Word, WordSize}; |
| 11 | use super::{AnyChannel, Channel, Dir, Request, STATE}; | 11 | use super::{AnyChannel, Channel, Dir, Request, STATE}; |
| 12 | use crate::interrupt::typelevel::Interrupt; | 12 | use crate::interrupt::typelevel::Interrupt; |
| 13 | use crate::rcc::BusyPeripheral; | ||
| 13 | use crate::{interrupt, pac}; | 14 | use crate::{interrupt, pac}; |
| 14 | 15 | ||
| 15 | pub(crate) struct ChannelInfo { | 16 | pub(crate) struct ChannelInfo { |
| @@ -602,7 +603,7 @@ impl AnyChannel { | |||
| 602 | /// DMA transfer. | 603 | /// DMA transfer. |
| 603 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 604 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 604 | pub struct Transfer<'a> { | 605 | pub struct Transfer<'a> { |
| 605 | channel: Peri<'a, AnyChannel>, | 606 | channel: BusyPeripheral<Peri<'a, AnyChannel>>, |
| 606 | } | 607 | } |
| 607 | 608 | ||
| 608 | impl<'a> Transfer<'a> { | 609 | impl<'a> Transfer<'a> { |
| @@ -713,7 +714,9 @@ impl<'a> Transfer<'a> { | |||
| 713 | _request, dir, peri_addr, mem_addr, mem_len, incr_mem, mem_size, peri_size, options, | 714 | _request, dir, peri_addr, mem_addr, mem_len, incr_mem, mem_size, peri_size, options, |
| 714 | ); | 715 | ); |
| 715 | channel.start(); | 716 | channel.start(); |
| 716 | Self { channel } | 717 | Self { |
| 718 | channel: BusyPeripheral::new(channel), | ||
| 719 | } | ||
| 717 | } | 720 | } |
| 718 | 721 | ||
| 719 | /// Request the transfer to pause, keeping the existing configuration for this channel. | 722 | /// Request the transfer to pause, keeping the existing configuration for this channel. |
| @@ -816,7 +819,7 @@ impl<'a> DmaCtrl for DmaCtrlImpl<'a> { | |||
| 816 | 819 | ||
| 817 | /// Ringbuffer for receiving data using DMA circular mode. | 820 | /// Ringbuffer for receiving data using DMA circular mode. |
| 818 | pub struct ReadableRingBuffer<'a, W: Word> { | 821 | pub struct ReadableRingBuffer<'a, W: Word> { |
| 819 | channel: Peri<'a, AnyChannel>, | 822 | channel: BusyPeripheral<Peri<'a, AnyChannel>>, |
| 820 | ringbuf: ReadableDmaRingBuffer<'a, W>, | 823 | ringbuf: ReadableDmaRingBuffer<'a, W>, |
| 821 | } | 824 | } |
| 822 | 825 | ||
| @@ -853,7 +856,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { | |||
| 853 | ); | 856 | ); |
| 854 | 857 | ||
| 855 | Self { | 858 | Self { |
| 856 | channel, | 859 | channel: BusyPeripheral::new(channel), |
| 857 | ringbuf: ReadableDmaRingBuffer::new(buffer), | 860 | ringbuf: ReadableDmaRingBuffer::new(buffer), |
| 858 | } | 861 | } |
| 859 | } | 862 | } |
| @@ -972,7 +975,7 @@ impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> { | |||
| 972 | 975 | ||
| 973 | /// Ringbuffer for writing data using DMA circular mode. | 976 | /// Ringbuffer for writing data using DMA circular mode. |
| 974 | pub struct WritableRingBuffer<'a, W: Word> { | 977 | pub struct WritableRingBuffer<'a, W: Word> { |
| 975 | channel: Peri<'a, AnyChannel>, | 978 | channel: BusyPeripheral<Peri<'a, AnyChannel>>, |
| 976 | ringbuf: WritableDmaRingBuffer<'a, W>, | 979 | ringbuf: WritableDmaRingBuffer<'a, W>, |
| 977 | } | 980 | } |
| 978 | 981 | ||
| @@ -1009,7 +1012,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { | |||
| 1009 | ); | 1012 | ); |
| 1010 | 1013 | ||
| 1011 | Self { | 1014 | Self { |
| 1012 | channel, | 1015 | channel: BusyPeripheral::new(channel), |
| 1013 | ringbuf: WritableDmaRingBuffer::new(buffer), | 1016 | ringbuf: WritableDmaRingBuffer::new(buffer), |
| 1014 | } | 1017 | } |
| 1015 | } | 1018 | } |
diff --git a/embassy-stm32/src/dma/gpdma/mod.rs b/embassy-stm32/src/dma/gpdma/mod.rs index 4a14c2a8e..afb18ec1a 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; |
| @@ -14,6 +14,7 @@ use super::{AnyChannel, Channel, Dir, Request, STATE}; | |||
| 14 | use crate::interrupt::typelevel::Interrupt; | 14 | use crate::interrupt::typelevel::Interrupt; |
| 15 | use crate::pac; | 15 | use crate::pac; |
| 16 | use crate::pac::gpdma::vals; | 16 | use crate::pac::gpdma::vals; |
| 17 | use crate::rcc::BusyPeripheral; | ||
| 17 | 18 | ||
| 18 | pub mod linked_list; | 19 | pub mod linked_list; |
| 19 | pub mod ringbuffered; | 20 | pub mod ringbuffered; |
| @@ -236,6 +237,11 @@ impl AnyChannel { | |||
| 236 | // "Preceding reads and writes cannot be moved past subsequent writes." | 237 | // "Preceding reads and writes cannot be moved past subsequent writes." |
| 237 | fence(Ordering::SeqCst); | 238 | fence(Ordering::SeqCst); |
| 238 | 239 | ||
| 240 | if ch.cr().read().en() { | ||
| 241 | ch.cr().modify(|w| w.set_susp(true)); | ||
| 242 | while !ch.sr().read().suspf() {} | ||
| 243 | } | ||
| 244 | |||
| 239 | ch.cr().write(|w| w.set_reset(true)); | 245 | ch.cr().write(|w| w.set_reset(true)); |
| 240 | ch.fcr().write(|w| { | 246 | ch.fcr().write(|w| { |
| 241 | // Clear all irqs | 247 | // Clear all irqs |
| @@ -407,7 +413,7 @@ impl AnyChannel { | |||
| 407 | /// Linked-list DMA transfer. | 413 | /// Linked-list DMA transfer. |
| 408 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 414 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 409 | pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> { | 415 | pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> { |
| 410 | channel: Peri<'a, AnyChannel>, | 416 | channel: BusyPeripheral<Peri<'a, AnyChannel>>, |
| 411 | } | 417 | } |
| 412 | 418 | ||
| 413 | impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { | 419 | impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { |
| @@ -428,7 +434,9 @@ impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { | |||
| 428 | channel.configure_linked_list(&table, options); | 434 | channel.configure_linked_list(&table, options); |
| 429 | channel.start(); | 435 | channel.start(); |
| 430 | 436 | ||
| 431 | Self { channel } | 437 | Self { |
| 438 | channel: BusyPeripheral::new(channel), | ||
| 439 | } | ||
| 432 | } | 440 | } |
| 433 | 441 | ||
| 434 | /// Request the transfer to pause, keeping the existing configuration for this channel. | 442 | /// Request the transfer to pause, keeping the existing configuration for this channel. |
| @@ -504,7 +512,7 @@ impl<'a, const ITEM_COUNT: usize> Future for LinkedListTransfer<'a, ITEM_COUNT> | |||
| 504 | /// DMA transfer. | 512 | /// DMA transfer. |
| 505 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 513 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 506 | pub struct Transfer<'a> { | 514 | pub struct Transfer<'a> { |
| 507 | channel: Peri<'a, AnyChannel>, | 515 | channel: BusyPeripheral<Peri<'a, AnyChannel>>, |
| 508 | } | 516 | } |
| 509 | 517 | ||
| 510 | impl<'a> Transfer<'a> { | 518 | impl<'a> Transfer<'a> { |
| @@ -624,7 +632,9 @@ impl<'a> Transfer<'a> { | |||
| 624 | ); | 632 | ); |
| 625 | channel.start(); | 633 | channel.start(); |
| 626 | 634 | ||
| 627 | Self { channel } | 635 | Self { |
| 636 | channel: BusyPeripheral::new(channel), | ||
| 637 | } | ||
| 628 | } | 638 | } |
| 629 | 639 | ||
| 630 | /// Request the transfer to pause, keeping the existing configuration for this channel. | 640 | /// Request the transfer to pause, keeping the existing configuration for this channel. |
diff --git a/embassy-stm32/src/dma/gpdma/ringbuffered.rs b/embassy-stm32/src/dma/gpdma/ringbuffered.rs index 9ee52193b..c150d0b95 100644 --- a/embassy-stm32/src/dma/gpdma/ringbuffered.rs +++ b/embassy-stm32/src/dma/gpdma/ringbuffered.rs | |||
| @@ -3,16 +3,17 @@ | |||
| 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; |
| 15 | use crate::dma::{Channel, Dir, Request}; | 15 | use crate::dma::{Channel, Dir, Request}; |
| 16 | use crate::rcc::BusyPeripheral; | ||
| 16 | 17 | ||
| 17 | struct DmaCtrlImpl<'a>(Peri<'a, AnyChannel>); | 18 | struct DmaCtrlImpl<'a>(Peri<'a, AnyChannel>); |
| 18 | 19 | ||
| @@ -49,7 +50,7 @@ impl<'a> DmaCtrl for DmaCtrlImpl<'a> { | |||
| 49 | 50 | ||
| 50 | /// Ringbuffer for receiving data using GPDMA linked-list mode. | 51 | /// Ringbuffer for receiving data using GPDMA linked-list mode. |
| 51 | pub struct ReadableRingBuffer<'a, W: Word> { | 52 | pub struct ReadableRingBuffer<'a, W: Word> { |
| 52 | channel: Peri<'a, AnyChannel>, | 53 | channel: BusyPeripheral<Peri<'a, AnyChannel>>, |
| 53 | ringbuf: ReadableDmaRingBuffer<'a, W>, | 54 | ringbuf: ReadableDmaRingBuffer<'a, W>, |
| 54 | table: Table<2>, | 55 | table: Table<2>, |
| 55 | options: TransferOptions, | 56 | options: TransferOptions, |
| @@ -70,7 +71,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { | |||
| 70 | let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::PeripheralToMemory); | 71 | let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::PeripheralToMemory); |
| 71 | 72 | ||
| 72 | Self { | 73 | Self { |
| 73 | channel, | 74 | channel: BusyPeripheral::new(channel), |
| 74 | ringbuf: ReadableDmaRingBuffer::new(buffer), | 75 | ringbuf: ReadableDmaRingBuffer::new(buffer), |
| 75 | table, | 76 | table, |
| 76 | options, | 77 | options, |
| @@ -189,7 +190,7 @@ impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> { | |||
| 189 | 190 | ||
| 190 | /// Ringbuffer for writing data using GPDMA linked-list mode. | 191 | /// Ringbuffer for writing data using GPDMA linked-list mode. |
| 191 | pub struct WritableRingBuffer<'a, W: Word> { | 192 | pub struct WritableRingBuffer<'a, W: Word> { |
| 192 | channel: Peri<'a, AnyChannel>, | 193 | channel: BusyPeripheral<Peri<'a, AnyChannel>>, |
| 193 | ringbuf: WritableDmaRingBuffer<'a, W>, | 194 | ringbuf: WritableDmaRingBuffer<'a, W>, |
| 194 | table: Table<2>, | 195 | table: Table<2>, |
| 195 | options: TransferOptions, | 196 | options: TransferOptions, |
| @@ -210,7 +211,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { | |||
| 210 | let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::MemoryToPeripheral); | 211 | let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::MemoryToPeripheral); |
| 211 | 212 | ||
| 212 | Self { | 213 | Self { |
| 213 | channel, | 214 | channel: BusyPeripheral::new(channel), |
| 214 | ringbuf: WritableDmaRingBuffer::new(buffer), | 215 | ringbuf: WritableDmaRingBuffer::new(buffer), |
| 215 | table, | 216 | table, |
| 216 | options, | 217 | options, |
diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 5989bfd7c..05d9c2e51 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #[cfg(any(bdma, dma))] | 4 | #[cfg(any(bdma, dma))] |
| 5 | mod dma_bdma; | 5 | mod dma_bdma; |
| 6 | |||
| 6 | #[cfg(any(bdma, dma))] | 7 | #[cfg(any(bdma, dma))] |
| 7 | pub use dma_bdma::*; | 8 | pub use dma_bdma::*; |
| 8 | 9 | ||
| @@ -24,9 +25,10 @@ pub(crate) use util::*; | |||
| 24 | pub(crate) mod ringbuffer; | 25 | pub(crate) mod ringbuffer; |
| 25 | pub mod word; | 26 | pub mod word; |
| 26 | 27 | ||
| 27 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; | 28 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; |
| 28 | 29 | ||
| 29 | use crate::interrupt; | 30 | use crate::interrupt; |
| 31 | use crate::rcc::StoppablePeripheral; | ||
| 30 | 32 | ||
| 31 | /// The direction of a DMA transfer. | 33 | /// The direction of a DMA transfer. |
| 32 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | 34 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
| @@ -45,7 +47,7 @@ pub type Request = u8; | |||
| 45 | #[cfg(not(any(dma_v2, bdma_v2, gpdma, dmamux)))] | 47 | #[cfg(not(any(dma_v2, bdma_v2, gpdma, dmamux)))] |
| 46 | pub type Request = (); | 48 | pub type Request = (); |
| 47 | 49 | ||
| 48 | pub(crate) trait SealedChannel { | 50 | pub(crate) trait SealedChannel: StoppablePeripheral { |
| 49 | fn id(&self) -> u8; | 51 | fn id(&self) -> u8; |
| 50 | } | 52 | } |
| 51 | 53 | ||
| @@ -59,15 +61,28 @@ pub(crate) trait ChannelInterrupt { | |||
| 59 | pub trait Channel: SealedChannel + PeripheralType + Into<AnyChannel> + 'static {} | 61 | pub trait Channel: SealedChannel + PeripheralType + Into<AnyChannel> + 'static {} |
| 60 | 62 | ||
| 61 | macro_rules! dma_channel_impl { | 63 | macro_rules! dma_channel_impl { |
| 62 | ($channel_peri:ident, $index:expr) => { | 64 | ($channel_peri:ident, $index:expr, $stop_mode:ident) => { |
| 65 | impl crate::rcc::StoppablePeripheral for crate::peripherals::$channel_peri { | ||
| 66 | #[cfg(feature = "low-power")] | ||
| 67 | fn stop_mode(&self) -> crate::rcc::StopMode { | ||
| 68 | crate::rcc::StopMode::$stop_mode | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 63 | impl crate::dma::SealedChannel for crate::peripherals::$channel_peri { | 72 | impl crate::dma::SealedChannel for crate::peripherals::$channel_peri { |
| 64 | fn id(&self) -> u8 { | 73 | fn id(&self) -> u8 { |
| 65 | $index | 74 | $index |
| 66 | } | 75 | } |
| 67 | } | 76 | } |
| 77 | |||
| 68 | impl crate::dma::ChannelInterrupt for crate::peripherals::$channel_peri { | 78 | impl crate::dma::ChannelInterrupt for crate::peripherals::$channel_peri { |
| 69 | unsafe fn on_irq() { | 79 | unsafe fn on_irq() { |
| 70 | crate::dma::AnyChannel { id: $index }.on_irq(); | 80 | crate::dma::AnyChannel { |
| 81 | id: $index, | ||
| 82 | #[cfg(feature = "low-power")] | ||
| 83 | stop_mode: crate::rcc::StopMode::$stop_mode, | ||
| 84 | } | ||
| 85 | .on_irq(); | ||
| 71 | } | 86 | } |
| 72 | } | 87 | } |
| 73 | 88 | ||
| @@ -77,6 +92,8 @@ macro_rules! dma_channel_impl { | |||
| 77 | fn from(val: crate::peripherals::$channel_peri) -> Self { | 92 | fn from(val: crate::peripherals::$channel_peri) -> Self { |
| 78 | Self { | 93 | Self { |
| 79 | id: crate::dma::SealedChannel::id(&val), | 94 | id: crate::dma::SealedChannel::id(&val), |
| 95 | #[cfg(feature = "low-power")] | ||
| 96 | stop_mode: crate::rcc::StoppablePeripheral::stop_mode(&val), | ||
| 80 | } | 97 | } |
| 81 | } | 98 | } |
| 82 | } | 99 | } |
| @@ -86,6 +103,8 @@ macro_rules! dma_channel_impl { | |||
| 86 | /// Type-erased DMA channel. | 103 | /// Type-erased DMA channel. |
| 87 | pub struct AnyChannel { | 104 | pub struct AnyChannel { |
| 88 | pub(crate) id: u8, | 105 | pub(crate) id: u8, |
| 106 | #[cfg(feature = "low-power")] | ||
| 107 | pub(crate) stop_mode: crate::rcc::StopMode, | ||
| 89 | } | 108 | } |
| 90 | impl_peripheral!(AnyChannel); | 109 | impl_peripheral!(AnyChannel); |
| 91 | 110 | ||
| @@ -95,6 +114,13 @@ impl AnyChannel { | |||
| 95 | } | 114 | } |
| 96 | } | 115 | } |
| 97 | 116 | ||
| 117 | impl StoppablePeripheral for AnyChannel { | ||
| 118 | #[cfg(feature = "low-power")] | ||
| 119 | fn stop_mode(&self) -> crate::rcc::StopMode { | ||
| 120 | self.stop_mode | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 98 | impl SealedChannel for AnyChannel { | 124 | impl SealedChannel for AnyChannel { |
| 99 | fn id(&self) -> u8 { | 125 | fn id(&self) -> u8 { |
| 100 | self.id | 126 | self.id |
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/dma/util.rs b/embassy-stm32/src/dma/util.rs index 3245887c1..304268963 100644 --- a/embassy-stm32/src/dma/util.rs +++ b/embassy-stm32/src/dma/util.rs | |||
| @@ -20,6 +20,16 @@ impl<'d> ChannelAndRequest<'d> { | |||
| 20 | Transfer::new_read(self.channel.reborrow(), self.request, peri_addr, buf, options) | 20 | Transfer::new_read(self.channel.reborrow(), self.request, peri_addr, buf, options) |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | #[allow(dead_code)] | ||
| 24 | pub unsafe fn read_unchecked<'a, W: Word>( | ||
| 25 | &'a self, | ||
| 26 | peri_addr: *mut W, | ||
| 27 | buf: &'a mut [W], | ||
| 28 | options: TransferOptions, | ||
| 29 | ) -> Transfer<'a> { | ||
| 30 | Transfer::new_read(self.channel.clone_unchecked(), self.request, peri_addr, buf, options) | ||
| 31 | } | ||
| 32 | |||
| 23 | pub unsafe fn read_raw<'a, MW: Word, PW: Word>( | 33 | pub unsafe fn read_raw<'a, MW: Word, PW: Word>( |
| 24 | &'a mut self, | 34 | &'a mut self, |
| 25 | peri_addr: *mut PW, | 35 | peri_addr: *mut PW, |
| @@ -29,6 +39,16 @@ impl<'d> ChannelAndRequest<'d> { | |||
| 29 | Transfer::new_read_raw(self.channel.reborrow(), self.request, peri_addr, buf, options) | 39 | Transfer::new_read_raw(self.channel.reborrow(), self.request, peri_addr, buf, options) |
| 30 | } | 40 | } |
| 31 | 41 | ||
| 42 | #[allow(dead_code)] | ||
| 43 | pub unsafe fn read_raw_unchecked<'a, MW: Word, PW: Word>( | ||
| 44 | &'a self, | ||
| 45 | peri_addr: *mut PW, | ||
| 46 | buf: *mut [MW], | ||
| 47 | options: TransferOptions, | ||
| 48 | ) -> Transfer<'a> { | ||
| 49 | Transfer::new_read_raw(self.channel.clone_unchecked(), self.request, peri_addr, buf, options) | ||
| 50 | } | ||
| 51 | |||
| 32 | pub unsafe fn write<'a, W: Word>( | 52 | pub unsafe fn write<'a, W: Word>( |
| 33 | &'a mut self, | 53 | &'a mut self, |
| 34 | buf: &'a [W], | 54 | buf: &'a [W], |
| @@ -38,6 +58,16 @@ impl<'d> ChannelAndRequest<'d> { | |||
| 38 | Transfer::new_write(self.channel.reborrow(), self.request, buf, peri_addr, options) | 58 | Transfer::new_write(self.channel.reborrow(), self.request, buf, peri_addr, options) |
| 39 | } | 59 | } |
| 40 | 60 | ||
| 61 | #[allow(dead_code)] | ||
| 62 | pub unsafe fn write_unchecked<'a, W: Word>( | ||
| 63 | &'a self, | ||
| 64 | buf: &'a [W], | ||
| 65 | peri_addr: *mut W, | ||
| 66 | options: TransferOptions, | ||
| 67 | ) -> Transfer<'a> { | ||
| 68 | Transfer::new_write(self.channel.clone_unchecked(), self.request, buf, peri_addr, options) | ||
| 69 | } | ||
| 70 | |||
| 41 | pub unsafe fn write_raw<'a, MW: Word, PW: Word>( | 71 | pub unsafe fn write_raw<'a, MW: Word, PW: Word>( |
| 42 | &'a mut self, | 72 | &'a mut self, |
| 43 | buf: *const [MW], | 73 | buf: *const [MW], |
| @@ -48,6 +78,16 @@ impl<'d> ChannelAndRequest<'d> { | |||
| 48 | } | 78 | } |
| 49 | 79 | ||
| 50 | #[allow(dead_code)] | 80 | #[allow(dead_code)] |
| 81 | pub unsafe fn write_raw_unchecked<'a, MW: Word, PW: Word>( | ||
| 82 | &'a self, | ||
| 83 | buf: *const [MW], | ||
| 84 | peri_addr: *mut PW, | ||
| 85 | options: TransferOptions, | ||
| 86 | ) -> Transfer<'a> { | ||
| 87 | Transfer::new_write_raw(self.channel.clone_unchecked(), self.request, buf, peri_addr, options) | ||
| 88 | } | ||
| 89 | |||
| 90 | #[allow(dead_code)] | ||
| 51 | pub unsafe fn write_repeated<'a, W: Word>( | 91 | pub unsafe fn write_repeated<'a, W: Word>( |
| 52 | &'a mut self, | 92 | &'a mut self, |
| 53 | repeated: &'a W, | 93 | repeated: &'a W, |
| @@ -64,4 +104,22 @@ impl<'d> ChannelAndRequest<'d> { | |||
| 64 | options, | 104 | options, |
| 65 | ) | 105 | ) |
| 66 | } | 106 | } |
| 107 | |||
| 108 | #[allow(dead_code)] | ||
| 109 | pub unsafe fn write_repeated_unchecked<'a, W: Word>( | ||
| 110 | &'a self, | ||
| 111 | repeated: &'a W, | ||
| 112 | count: usize, | ||
| 113 | peri_addr: *mut W, | ||
| 114 | options: TransferOptions, | ||
| 115 | ) -> Transfer<'a> { | ||
| 116 | Transfer::new_write_repeated( | ||
| 117 | self.channel.clone_unchecked(), | ||
| 118 | self.request, | ||
| 119 | repeated, | ||
| 120 | count, | ||
| 121 | peri_addr, | ||
| 122 | options, | ||
| 123 | ) | ||
| 124 | } | ||
| 67 | } | 125 | } |
diff --git a/embassy-stm32/src/dma/word.rs b/embassy-stm32/src/dma/word.rs index fb1bde860..5c3bb8f7f 100644 --- a/embassy-stm32/src/dma/word.rs +++ b/embassy-stm32/src/dma/word.rs | |||
| @@ -31,6 +31,10 @@ pub trait Word: SealedWord + Default + Copy + 'static { | |||
| 31 | fn size() -> WordSize; | 31 | fn size() -> WordSize; |
| 32 | /// Amount of bits of this word size. | 32 | /// Amount of bits of this word size. |
| 33 | fn bits() -> usize; | 33 | fn bits() -> usize; |
| 34 | /// Maximum value of this type. | ||
| 35 | fn max() -> usize { | ||
| 36 | (1 << Self::bits()) - 1 | ||
| 37 | } | ||
| 34 | } | 38 | } |
| 35 | 39 | ||
| 36 | macro_rules! impl_word { | 40 | macro_rules! impl_word { |
diff --git a/embassy-stm32/src/dsihost.rs b/embassy-stm32/src/dsihost.rs index deda956af..b8945820c 100644 --- a/embassy-stm32/src/dsihost.rs +++ b/embassy-stm32/src/dsihost.rs | |||
| @@ -5,17 +5,10 @@ use core::marker::PhantomData; | |||
| 5 | use embassy_hal_internal::PeripheralType; | 5 | use embassy_hal_internal::PeripheralType; |
| 6 | 6 | ||
| 7 | //use crate::gpio::{AnyPin, SealedPin}; | 7 | //use crate::gpio::{AnyPin, SealedPin}; |
| 8 | use crate::block_for_us; | ||
| 8 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 9 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
| 9 | use crate::rcc::{self, RccPeripheral}; | 10 | use crate::rcc::{self, RccPeripheral}; |
| 10 | use crate::{peripherals, Peri}; | 11 | use crate::{Peri, peripherals}; |
| 11 | |||
| 12 | /// Performs a busy-wait delay for a specified number of microseconds. | ||
| 13 | pub fn blocking_delay_ms(ms: u32) { | ||
| 14 | #[cfg(feature = "time")] | ||
| 15 | embassy_time::block_for(embassy_time::Duration::from_millis(ms as u64)); | ||
| 16 | #[cfg(not(feature = "time"))] | ||
| 17 | cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 / 1_000 * ms); | ||
| 18 | } | ||
| 19 | 12 | ||
| 20 | /// PacketTypes extracted from CubeMX | 13 | /// PacketTypes extracted from CubeMX |
| 21 | #[repr(u8)] | 14 | #[repr(u8)] |
| @@ -121,17 +114,15 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 121 | 114 | ||
| 122 | /// DCS or Generic short/long write command | 115 | /// DCS or Generic short/long write command |
| 123 | pub fn write_cmd(&mut self, channel_id: u8, address: u8, data: &[u8]) -> Result<(), Error> { | 116 | pub fn write_cmd(&mut self, channel_id: u8, address: u8, data: &[u8]) -> Result<(), Error> { |
| 124 | assert!(data.len() > 0); | 117 | match data.len() { |
| 125 | 118 | 0 => self.short_write(channel_id, PacketType::DcsShortPktWriteP0, address, 0), | |
| 126 | if data.len() == 1 { | 119 | 1 => self.short_write(channel_id, PacketType::DcsShortPktWriteP1, address, data[0]), |
| 127 | self.short_write(channel_id, PacketType::DcsShortPktWriteP1, address, data[0]) | 120 | _ => self.long_write( |
| 128 | } else { | ||
| 129 | self.long_write( | ||
| 130 | channel_id, | 121 | channel_id, |
| 131 | PacketType::DcsLongPktWrite, // FIXME: This might be a generic long packet, as well... | 122 | PacketType::DcsLongPktWrite, // FIXME: This might be a generic long packet, as well... |
| 132 | address, | 123 | address, |
| 133 | data, | 124 | data, |
| 134 | ) | 125 | ), |
| 135 | } | 126 | } |
| 136 | } | 127 | } |
| 137 | 128 | ||
| @@ -336,7 +327,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 336 | if T::regs().gpsr().read().cmdfe() { | 327 | if T::regs().gpsr().read().cmdfe() { |
| 337 | return Ok(()); | 328 | return Ok(()); |
| 338 | } | 329 | } |
| 339 | blocking_delay_ms(1); | 330 | block_for_us(1_000); |
| 340 | } | 331 | } |
| 341 | Err(Error::FifoTimeout) | 332 | Err(Error::FifoTimeout) |
| 342 | } | 333 | } |
| @@ -347,7 +338,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 347 | if !T::regs().gpsr().read().cmdff() { | 338 | if !T::regs().gpsr().read().cmdff() { |
| 348 | return Ok(()); | 339 | return Ok(()); |
| 349 | } | 340 | } |
| 350 | blocking_delay_ms(1); | 341 | block_for_us(1_000); |
| 351 | } | 342 | } |
| 352 | Err(Error::FifoTimeout) | 343 | Err(Error::FifoTimeout) |
| 353 | } | 344 | } |
| @@ -358,7 +349,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 358 | if !self.read_busy() { | 349 | if !self.read_busy() { |
| 359 | return Ok(()); | 350 | return Ok(()); |
| 360 | } | 351 | } |
| 361 | blocking_delay_ms(1); | 352 | block_for_us(1_000); |
| 362 | } | 353 | } |
| 363 | Err(Error::ReadTimeout) | 354 | Err(Error::ReadTimeout) |
| 364 | } | 355 | } |
| @@ -369,7 +360,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 369 | if !T::regs().gpsr().read().prdfe() { | 360 | if !T::regs().gpsr().read().prdfe() { |
| 370 | return Ok(()); | 361 | return Ok(()); |
| 371 | } | 362 | } |
| 372 | blocking_delay_ms(1); | 363 | block_for_us(1_000); |
| 373 | } | 364 | } |
| 374 | Err(Error::FifoTimeout) | 365 | Err(Error::FifoTimeout) |
| 375 | } | 366 | } |
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/dts/tsel.rs b/embassy-stm32/src/dts/tsel.rs index 99eab6dd8..79c697c8d 100644 --- a/embassy-stm32/src/dts/tsel.rs +++ b/embassy-stm32/src/dts/tsel.rs | |||
| @@ -49,3 +49,20 @@ pub enum TriggerSel { | |||
| 49 | /// EXTI13 | 49 | /// EXTI13 |
| 50 | Exti13 = 4, | 50 | Exti13 = 4, |
| 51 | } | 51 | } |
| 52 | |||
| 53 | /// Trigger selection for N6 | ||
| 54 | #[cfg(stm32n6)] | ||
| 55 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 56 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 57 | pub enum TriggerSel { | ||
| 58 | /// Software triggering. Performs continuous measurements. | ||
| 59 | Software = 0, | ||
| 60 | /// LPTIM4 OUT | ||
| 61 | Lptim4 = 1, | ||
| 62 | /// LPTIM2 CH1 | ||
| 63 | Lptim2 = 2, | ||
| 64 | /// LPTIM3 CH1 | ||
| 65 | Lptim3 = 3, | ||
| 66 | /// EXTI13 | ||
| 67 | Exti13 = 4, | ||
| 68 | } | ||
diff --git a/embassy-stm32/src/eth/generic_phy.rs b/embassy-stm32/src/eth/generic_phy.rs index 774beef80..0a5f41de0 100644 --- a/embassy-stm32/src/eth/generic_phy.rs +++ b/embassy-stm32/src/eth/generic_phy.rs | |||
| @@ -8,6 +8,7 @@ use embassy_time::{Duration, Timer}; | |||
| 8 | use futures_util::FutureExt; | 8 | use futures_util::FutureExt; |
| 9 | 9 | ||
| 10 | use super::{Phy, StationManagement}; | 10 | use super::{Phy, StationManagement}; |
| 11 | use crate::block_for_us as blocking_delay_us; | ||
| 11 | 12 | ||
| 12 | #[allow(dead_code)] | 13 | #[allow(dead_code)] |
| 13 | mod phy_consts { | 14 | mod phy_consts { |
| @@ -43,21 +44,23 @@ mod phy_consts { | |||
| 43 | use self::phy_consts::*; | 44 | use self::phy_consts::*; |
| 44 | 45 | ||
| 45 | /// Generic SMI Ethernet PHY implementation | 46 | /// Generic SMI Ethernet PHY implementation |
| 46 | pub struct GenericPhy { | 47 | pub struct GenericPhy<SM: StationManagement> { |
| 47 | phy_addr: u8, | 48 | phy_addr: u8, |
| 49 | sm: SM, | ||
| 48 | #[cfg(feature = "time")] | 50 | #[cfg(feature = "time")] |
| 49 | poll_interval: Duration, | 51 | poll_interval: Duration, |
| 50 | } | 52 | } |
| 51 | 53 | ||
| 52 | impl GenericPhy { | 54 | impl<SM: StationManagement> GenericPhy<SM> { |
| 53 | /// Construct the PHY. It assumes the address `phy_addr` in the SMI communication | 55 | /// Construct the PHY. It assumes the address `phy_addr` in the SMI communication |
| 54 | /// | 56 | /// |
| 55 | /// # Panics | 57 | /// # Panics |
| 56 | /// `phy_addr` must be in range `0..32` | 58 | /// `phy_addr` must be in range `0..32` |
| 57 | pub fn new(phy_addr: u8) -> Self { | 59 | pub fn new(sm: SM, phy_addr: u8) -> Self { |
| 58 | assert!(phy_addr < 32); | 60 | assert!(phy_addr < 32); |
| 59 | Self { | 61 | Self { |
| 60 | phy_addr, | 62 | phy_addr, |
| 63 | sm, | ||
| 61 | #[cfg(feature = "time")] | 64 | #[cfg(feature = "time")] |
| 62 | poll_interval: Duration::from_millis(500), | 65 | poll_interval: Duration::from_millis(500), |
| 63 | } | 66 | } |
| @@ -67,8 +70,9 @@ impl GenericPhy { | |||
| 67 | /// | 70 | /// |
| 68 | /// # Panics | 71 | /// # Panics |
| 69 | /// Initialization panics if PHY didn't respond on any address | 72 | /// Initialization panics if PHY didn't respond on any address |
| 70 | pub fn new_auto() -> Self { | 73 | pub fn new_auto(sm: SM) -> Self { |
| 71 | Self { | 74 | Self { |
| 75 | sm, | ||
| 72 | phy_addr: 0xFF, | 76 | phy_addr: 0xFF, |
| 73 | #[cfg(feature = "time")] | 77 | #[cfg(feature = "time")] |
| 74 | poll_interval: Duration::from_millis(500), | 78 | poll_interval: Duration::from_millis(500), |
| @@ -76,27 +80,14 @@ impl GenericPhy { | |||
| 76 | } | 80 | } |
| 77 | } | 81 | } |
| 78 | 82 | ||
| 79 | // TODO: Factor out to shared functionality | 83 | impl<SM: StationManagement> Phy for GenericPhy<SM> { |
| 80 | fn blocking_delay_us(us: u32) { | 84 | fn phy_reset(&mut self) { |
| 81 | #[cfg(feature = "time")] | ||
| 82 | embassy_time::block_for(Duration::from_micros(us as u64)); | ||
| 83 | #[cfg(not(feature = "time"))] | ||
| 84 | { | ||
| 85 | let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64; | ||
| 86 | let us = us as u64; | ||
| 87 | let cycles = freq * us / 1_000_000; | ||
| 88 | cortex_m::asm::delay(cycles as u32); | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | impl Phy for GenericPhy { | ||
| 93 | fn phy_reset<S: StationManagement>(&mut self, sm: &mut S) { | ||
| 94 | // Detect SMI address | 85 | // Detect SMI address |
| 95 | if self.phy_addr == 0xFF { | 86 | if self.phy_addr == 0xFF { |
| 96 | for addr in 0..32 { | 87 | for addr in 0..32 { |
| 97 | sm.smi_write(addr, PHY_REG_BCR, PHY_REG_BCR_RESET); | 88 | self.sm.smi_write(addr, PHY_REG_BCR, PHY_REG_BCR_RESET); |
| 98 | for _ in 0..10 { | 89 | for _ in 0..10 { |
| 99 | if sm.smi_read(addr, PHY_REG_BCR) & PHY_REG_BCR_RESET != PHY_REG_BCR_RESET { | 90 | if self.sm.smi_read(addr, PHY_REG_BCR) & PHY_REG_BCR_RESET != PHY_REG_BCR_RESET { |
| 100 | trace!("Found ETH PHY on address {}", addr); | 91 | trace!("Found ETH PHY on address {}", addr); |
| 101 | self.phy_addr = addr; | 92 | self.phy_addr = addr; |
| 102 | return; | 93 | return; |
| @@ -108,30 +99,30 @@ impl Phy for GenericPhy { | |||
| 108 | panic!("PHY did not respond"); | 99 | panic!("PHY did not respond"); |
| 109 | } | 100 | } |
| 110 | 101 | ||
| 111 | sm.smi_write(self.phy_addr, PHY_REG_BCR, PHY_REG_BCR_RESET); | 102 | self.sm.smi_write(self.phy_addr, PHY_REG_BCR, PHY_REG_BCR_RESET); |
| 112 | while sm.smi_read(self.phy_addr, PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {} | 103 | while self.sm.smi_read(self.phy_addr, PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {} |
| 113 | } | 104 | } |
| 114 | 105 | ||
| 115 | fn phy_init<S: StationManagement>(&mut self, sm: &mut S) { | 106 | fn phy_init(&mut self) { |
| 116 | // Clear WU CSR | 107 | // Clear WU CSR |
| 117 | self.smi_write_ext(sm, PHY_REG_WUCSR, 0); | 108 | self.smi_write_ext(PHY_REG_WUCSR, 0); |
| 118 | 109 | ||
| 119 | // Enable auto-negotiation | 110 | // Enable auto-negotiation |
| 120 | sm.smi_write( | 111 | self.sm.smi_write( |
| 121 | self.phy_addr, | 112 | self.phy_addr, |
| 122 | PHY_REG_BCR, | 113 | PHY_REG_BCR, |
| 123 | PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M, | 114 | PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M, |
| 124 | ); | 115 | ); |
| 125 | } | 116 | } |
| 126 | 117 | ||
| 127 | fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool { | 118 | fn poll_link(&mut self, cx: &mut Context) -> bool { |
| 128 | #[cfg(not(feature = "time"))] | 119 | #[cfg(not(feature = "time"))] |
| 129 | cx.waker().wake_by_ref(); | 120 | cx.waker().wake_by_ref(); |
| 130 | 121 | ||
| 131 | #[cfg(feature = "time")] | 122 | #[cfg(feature = "time")] |
| 132 | let _ = Timer::after(self.poll_interval).poll_unpin(cx); | 123 | let _ = Timer::after(self.poll_interval).poll_unpin(cx); |
| 133 | 124 | ||
| 134 | let bsr = sm.smi_read(self.phy_addr, PHY_REG_BSR); | 125 | let bsr = self.sm.smi_read(self.phy_addr, PHY_REG_BSR); |
| 135 | 126 | ||
| 136 | // No link without autonegotiate | 127 | // No link without autonegotiate |
| 137 | if bsr & PHY_REG_BSR_ANDONE == 0 { | 128 | if bsr & PHY_REG_BSR_ANDONE == 0 { |
| @@ -148,7 +139,7 @@ impl Phy for GenericPhy { | |||
| 148 | } | 139 | } |
| 149 | 140 | ||
| 150 | /// Public functions for the PHY | 141 | /// Public functions for the PHY |
| 151 | impl GenericPhy { | 142 | impl<SM: StationManagement> GenericPhy<SM> { |
| 152 | /// Set the SMI polling interval. | 143 | /// Set the SMI polling interval. |
| 153 | #[cfg(feature = "time")] | 144 | #[cfg(feature = "time")] |
| 154 | pub fn set_poll_interval(&mut self, poll_interval: Duration) { | 145 | pub fn set_poll_interval(&mut self, poll_interval: Duration) { |
| @@ -156,10 +147,15 @@ impl GenericPhy { | |||
| 156 | } | 147 | } |
| 157 | 148 | ||
| 158 | // Writes a value to an extended PHY register in MMD address space | 149 | // Writes a value to an extended PHY register in MMD address space |
| 159 | fn smi_write_ext<S: StationManagement>(&mut self, sm: &mut S, reg_addr: u16, reg_data: u16) { | 150 | fn smi_write_ext(&mut self, reg_addr: u16, reg_data: u16) { |
| 160 | sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x0003); // set address | 151 | self.sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x0003); // set address |
| 161 | sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_addr); | 152 | self.sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_addr); |
| 162 | sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x4003); // set data | 153 | self.sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x4003); // set data |
| 163 | sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_data); | 154 | self.sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_data); |
| 155 | } | ||
| 156 | |||
| 157 | /// Access the underlying station management. | ||
| 158 | pub fn station_management(&mut self) -> &mut SM { | ||
| 159 | &mut self.sm | ||
| 164 | } | 160 | } |
| 165 | } | 161 | } |
diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 10b3a0517..c8bce0e8a 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #[cfg_attr(eth_v2, path = "v2/mod.rs")] | 5 | #[cfg_attr(eth_v2, path = "v2/mod.rs")] |
| 6 | mod _version; | 6 | mod _version; |
| 7 | mod generic_phy; | 7 | mod generic_phy; |
| 8 | mod sma; | ||
| 8 | 9 | ||
| 9 | use core::mem::MaybeUninit; | 10 | use core::mem::MaybeUninit; |
| 10 | use core::task::Context; | 11 | use core::task::Context; |
| @@ -15,6 +16,7 @@ use embassy_sync::waitqueue::AtomicWaker; | |||
| 15 | 16 | ||
| 16 | pub use self::_version::{InterruptHandler, *}; | 17 | pub use self::_version::{InterruptHandler, *}; |
| 17 | pub use self::generic_phy::*; | 18 | pub use self::generic_phy::*; |
| 19 | pub use self::sma::{Sma, StationManagement}; | ||
| 18 | use crate::rcc::RccPeripheral; | 20 | use crate::rcc::RccPeripheral; |
| 19 | 21 | ||
| 20 | #[allow(unused)] | 22 | #[allow(unused)] |
| @@ -109,7 +111,7 @@ impl<'d, T: Instance, P: Phy> embassy_net_driver::Driver for Ethernet<'d, T, P> | |||
| 109 | } | 111 | } |
| 110 | 112 | ||
| 111 | fn link_state(&mut self, cx: &mut Context) -> LinkState { | 113 | fn link_state(&mut self, cx: &mut Context) -> LinkState { |
| 112 | if self.phy.poll_link(&mut self.station_management, cx) { | 114 | if self.phy.poll_link(cx) { |
| 113 | LinkState::Up | 115 | LinkState::Up |
| 114 | } else { | 116 | } else { |
| 115 | LinkState::Down | 117 | LinkState::Down |
| @@ -157,32 +159,17 @@ impl<'a, 'd> embassy_net_driver::TxToken for TxToken<'a, 'd> { | |||
| 157 | } | 159 | } |
| 158 | } | 160 | } |
| 159 | 161 | ||
| 160 | /// Station Management Interface (SMI) on an ethernet PHY | ||
| 161 | pub trait StationManagement { | ||
| 162 | /// Read a register over SMI. | ||
| 163 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16; | ||
| 164 | /// Write a register over SMI. | ||
| 165 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16); | ||
| 166 | } | ||
| 167 | |||
| 168 | /// Trait for an Ethernet PHY | 162 | /// Trait for an Ethernet PHY |
| 169 | pub trait Phy { | 163 | pub trait Phy { |
| 170 | /// Reset PHY and wait for it to come out of reset. | 164 | /// Reset PHY and wait for it to come out of reset. |
| 171 | fn phy_reset<S: StationManagement>(&mut self, sm: &mut S); | 165 | fn phy_reset(&mut self); |
| 172 | /// PHY initialisation. | 166 | /// PHY initialisation. |
| 173 | fn phy_init<S: StationManagement>(&mut self, sm: &mut S); | 167 | fn phy_init(&mut self); |
| 174 | /// Poll link to see if it is up and FD with 100Mbps | 168 | /// Poll link to see if it is up and FD with 100Mbps |
| 175 | fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool; | 169 | fn poll_link(&mut self, cx: &mut Context) -> bool; |
| 176 | } | 170 | } |
| 177 | 171 | ||
| 178 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | 172 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { |
| 179 | /// Directly expose the SMI interface used by the Ethernet driver. | ||
| 180 | /// | ||
| 181 | /// This can be used to for example configure special PHY registers for compliance testing. | ||
| 182 | pub fn station_management(&mut self) -> &mut impl StationManagement { | ||
| 183 | &mut self.station_management | ||
| 184 | } | ||
| 185 | |||
| 186 | /// Access the user-supplied `Phy`. | 173 | /// Access the user-supplied `Phy`. |
| 187 | pub fn phy(&self) -> &P { | 174 | pub fn phy(&self) -> &P { |
| 188 | &self.phy | 175 | &self.phy |
| @@ -212,8 +199,8 @@ impl Instance for crate::peripherals::ETH {} | |||
| 212 | pin_trait!(RXClkPin, Instance, @A); | 199 | pin_trait!(RXClkPin, Instance, @A); |
| 213 | pin_trait!(TXClkPin, Instance, @A); | 200 | pin_trait!(TXClkPin, Instance, @A); |
| 214 | pin_trait!(RefClkPin, Instance, @A); | 201 | pin_trait!(RefClkPin, Instance, @A); |
| 215 | pin_trait!(MDIOPin, Instance, @A); | 202 | pin_trait!(MDIOPin, sma::Instance, @A); |
| 216 | pin_trait!(MDCPin, Instance, @A); | 203 | pin_trait!(MDCPin, sma::Instance, @A); |
| 217 | pin_trait!(RXDVPin, Instance, @A); | 204 | pin_trait!(RXDVPin, Instance, @A); |
| 218 | pin_trait!(CRSPin, Instance, @A); | 205 | pin_trait!(CRSPin, Instance, @A); |
| 219 | pin_trait!(RXD0Pin, Instance, @A); | 206 | pin_trait!(RXD0Pin, Instance, @A); |
diff --git a/embassy-stm32/src/eth/sma/mod.rs b/embassy-stm32/src/eth/sma/mod.rs new file mode 100644 index 000000000..6c851911d --- /dev/null +++ b/embassy-stm32/src/eth/sma/mod.rs | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | //! Station Management Agent (also known as MDIO or SMI). | ||
| 2 | |||
| 3 | #![macro_use] | ||
| 4 | |||
| 5 | #[cfg_attr(eth_v2, path = "v2.rs")] | ||
| 6 | #[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1.rs")] | ||
| 7 | mod _version; | ||
| 8 | |||
| 9 | use embassy_hal_internal::PeripheralType; | ||
| 10 | use stm32_metapac::common::{RW, Reg}; | ||
| 11 | |||
| 12 | pub use self::_version::*; | ||
| 13 | |||
| 14 | /// Station Management Interface (SMI). | ||
| 15 | pub trait StationManagement { | ||
| 16 | /// Read a register over SMI. | ||
| 17 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16; | ||
| 18 | /// Write a register over SMI. | ||
| 19 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16); | ||
| 20 | } | ||
| 21 | |||
| 22 | trait SealedInstance { | ||
| 23 | fn regs() -> (Reg<AddressRegister, RW>, Reg<DataRegister, RW>); | ||
| 24 | } | ||
| 25 | |||
| 26 | /// MDIO instance. | ||
| 27 | #[allow(private_bounds)] | ||
| 28 | pub trait Instance: SealedInstance + PeripheralType + Send + 'static {} | ||
| 29 | |||
| 30 | impl SealedInstance for crate::peripherals::ETH_SMA { | ||
| 31 | fn regs() -> (Reg<AddressRegister, RW>, Reg<DataRegister, RW>) { | ||
| 32 | let mac = crate::pac::ETH.ethernet_mac(); | ||
| 33 | |||
| 34 | #[cfg(any(eth_v1a, eth_v1b, eth_v1c))] | ||
| 35 | return (mac.macmiiar(), mac.macmiidr()); | ||
| 36 | |||
| 37 | #[cfg(eth_v2)] | ||
| 38 | return (mac.macmdioar(), mac.macmdiodr()); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | impl Instance for crate::peripherals::ETH_SMA {} | ||
diff --git a/embassy-stm32/src/eth/sma/v1.rs b/embassy-stm32/src/eth/sma/v1.rs new file mode 100644 index 000000000..db64a6c78 --- /dev/null +++ b/embassy-stm32/src/eth/sma/v1.rs | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | use embassy_hal_internal::Peri; | ||
| 2 | pub(crate) use regs::{Macmiiar as AddressRegister, Macmiidr as DataRegister}; | ||
| 3 | use stm32_metapac::eth::regs; | ||
| 4 | use stm32_metapac::eth::vals::{Cr, MbProgress, Mw}; | ||
| 5 | |||
| 6 | use super::{Instance, StationManagement}; | ||
| 7 | use crate::eth::{MDCPin, MDIOPin}; | ||
| 8 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; | ||
| 9 | |||
| 10 | /// Station Management Agent. | ||
| 11 | /// | ||
| 12 | /// This peripheral is used for SMI reads and writes to the connected | ||
| 13 | /// ethernet PHY/device(s). | ||
| 14 | pub struct Sma<'d, T: Instance> { | ||
| 15 | _peri: Peri<'d, T>, | ||
| 16 | clock_range: Cr, | ||
| 17 | pins: [Peri<'d, AnyPin>; 2], | ||
| 18 | } | ||
| 19 | |||
| 20 | impl<'d, T: Instance> Sma<'d, T> { | ||
| 21 | /// Create a new instance of this peripheral. | ||
| 22 | pub fn new<#[cfg(afio)] A>( | ||
| 23 | peri: Peri<'d, T>, | ||
| 24 | mdio: Peri<'d, if_afio!(impl MDIOPin<T, A>)>, | ||
| 25 | mdc: Peri<'d, if_afio!(impl MDCPin<T, A>)>, | ||
| 26 | ) -> Self { | ||
| 27 | set_as_af!(mdio, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 28 | set_as_af!(mdc, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 29 | |||
| 30 | // Enable necessary clocks. | ||
| 31 | critical_section::with(|_| { | ||
| 32 | #[cfg(eth_v1a)] | ||
| 33 | let reg = crate::pac::RCC.ahbenr(); | ||
| 34 | |||
| 35 | #[cfg(any(eth_v1b, eth_v1c))] | ||
| 36 | let reg = crate::pac::RCC.ahb1enr(); | ||
| 37 | |||
| 38 | reg.modify(|w| { | ||
| 39 | w.set_ethen(true); | ||
| 40 | }) | ||
| 41 | }); | ||
| 42 | |||
| 43 | let hclk = unsafe { crate::rcc::get_freqs().hclk1.to_hertz() }; | ||
| 44 | let hclk = unwrap!(hclk, "SMA requires HCLK to be enabled, but it was not."); | ||
| 45 | let hclk_mhz = hclk.0 / 1_000_000; | ||
| 46 | |||
| 47 | // Set the MDC clock frequency in the range 1MHz - 2.5MHz | ||
| 48 | let clock_range = match hclk_mhz { | ||
| 49 | 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."), | ||
| 50 | 25..=34 => Cr::CR_20_35, // Divide by 16 | ||
| 51 | 35..=59 => Cr::CR_35_60, // Divide by 26 | ||
| 52 | 60..=99 => Cr::CR_60_100, // Divide by 42 | ||
| 53 | 100..=149 => Cr::CR_100_150, // Divide by 62 | ||
| 54 | 150..=216 => Cr::CR_150_168, // Divide by 102 | ||
| 55 | _ => { | ||
| 56 | panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") | ||
| 57 | } | ||
| 58 | }; | ||
| 59 | |||
| 60 | Self { | ||
| 61 | _peri: peri, | ||
| 62 | clock_range, | ||
| 63 | pins: [mdio.into(), mdc.into()], | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | impl<T: Instance> StationManagement for Sma<'_, T> { | ||
| 69 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { | ||
| 70 | let (macmiiar, macmiidr) = T::regs(); | ||
| 71 | |||
| 72 | macmiiar.modify(|w| { | ||
| 73 | w.set_pa(phy_addr); | ||
| 74 | w.set_mr(reg); | ||
| 75 | w.set_mw(Mw::READ); // read operation | ||
| 76 | w.set_cr(self.clock_range); | ||
| 77 | w.set_mb(MbProgress::BUSY); // indicate that operation is in progress | ||
| 78 | }); | ||
| 79 | while macmiiar.read().mb() == MbProgress::BUSY {} | ||
| 80 | macmiidr.read().md() | ||
| 81 | } | ||
| 82 | |||
| 83 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { | ||
| 84 | let (macmiiar, macmiidr) = T::regs(); | ||
| 85 | |||
| 86 | macmiidr.write(|w| w.set_md(val)); | ||
| 87 | macmiiar.modify(|w| { | ||
| 88 | w.set_pa(phy_addr); | ||
| 89 | w.set_mr(reg); | ||
| 90 | w.set_mw(Mw::WRITE); // write | ||
| 91 | w.set_cr(self.clock_range); | ||
| 92 | w.set_mb(MbProgress::BUSY); | ||
| 93 | }); | ||
| 94 | while macmiiar.read().mb() == MbProgress::BUSY {} | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | impl<T: Instance> Drop for Sma<'_, T> { | ||
| 99 | fn drop(&mut self) { | ||
| 100 | self.pins.iter_mut().for_each(|p| p.set_as_disconnected()); | ||
| 101 | } | ||
| 102 | } | ||
diff --git a/embassy-stm32/src/eth/sma/v2.rs b/embassy-stm32/src/eth/sma/v2.rs new file mode 100644 index 000000000..6bc5230b5 --- /dev/null +++ b/embassy-stm32/src/eth/sma/v2.rs | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | use embassy_hal_internal::Peri; | ||
| 2 | pub(crate) use regs::{Macmdioar as AddressRegister, Macmdiodr as DataRegister}; | ||
| 3 | use stm32_metapac::eth::regs; | ||
| 4 | |||
| 5 | use super::{Instance, StationManagement}; | ||
| 6 | use crate::eth::{MDCPin, MDIOPin}; | ||
| 7 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; | ||
| 8 | |||
| 9 | /// Station Management Agent. | ||
| 10 | /// | ||
| 11 | /// This peripheral is used for SMI reads and writes to the connected | ||
| 12 | /// ethernet PHY/device(s). | ||
| 13 | pub struct Sma<'d, T: Instance> { | ||
| 14 | _peri: Peri<'d, T>, | ||
| 15 | pins: [Peri<'d, AnyPin>; 2], | ||
| 16 | clock_range: u8, | ||
| 17 | } | ||
| 18 | |||
| 19 | impl<'d, T: Instance> Sma<'d, T> { | ||
| 20 | /// Create a new instance of this peripheral. | ||
| 21 | pub fn new(peri: Peri<'d, T>, mdio: Peri<'d, impl MDIOPin<T>>, mdc: Peri<'d, impl MDCPin<T>>) -> Self { | ||
| 22 | set_as_af!(mdio, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 23 | set_as_af!(mdc, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 24 | |||
| 25 | // Enable necessary clocks. | ||
| 26 | critical_section::with(|_| { | ||
| 27 | crate::pac::RCC.ahb1enr().modify(|w| { | ||
| 28 | w.set_ethen(true); | ||
| 29 | }) | ||
| 30 | }); | ||
| 31 | |||
| 32 | let hclk = unsafe { crate::rcc::get_freqs().hclk1.to_hertz() }; | ||
| 33 | let hclk = unwrap!(hclk, "SMA requires HCLK to be enabled, but it was not."); | ||
| 34 | let hclk_mhz = hclk.0 / 1_000_000; | ||
| 35 | |||
| 36 | // Set the MDC clock frequency in the range 1MHz - 2.5MHz | ||
| 37 | let clock_range = match hclk_mhz { | ||
| 38 | 0..=34 => 2, // Divide by 16 | ||
| 39 | 35..=59 => 3, // Divide by 26 | ||
| 40 | 60..=99 => 0, // Divide by 42 | ||
| 41 | 100..=149 => 1, // Divide by 62 | ||
| 42 | 150..=249 => 4, // Divide by 102 | ||
| 43 | 250..=310 => 5, // Divide by 124 | ||
| 44 | _ => { | ||
| 45 | panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") | ||
| 46 | } | ||
| 47 | }; | ||
| 48 | |||
| 49 | Self { | ||
| 50 | _peri: peri, | ||
| 51 | clock_range, | ||
| 52 | pins: [mdio.into(), mdc.into()], | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | impl<T: Instance> StationManagement for Sma<'_, T> { | ||
| 58 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { | ||
| 59 | let (macmdioar, macmdiodr) = T::regs(); | ||
| 60 | |||
| 61 | macmdioar.modify(|w| { | ||
| 62 | w.set_pa(phy_addr); | ||
| 63 | w.set_rda(reg); | ||
| 64 | w.set_goc(0b11); // read | ||
| 65 | w.set_cr(self.clock_range); | ||
| 66 | w.set_mb(true); | ||
| 67 | }); | ||
| 68 | |||
| 69 | while macmdioar.read().mb() {} | ||
| 70 | |||
| 71 | macmdiodr.read().md() | ||
| 72 | } | ||
| 73 | |||
| 74 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { | ||
| 75 | let (macmdioar, macmdiodr) = T::regs(); | ||
| 76 | |||
| 77 | macmdiodr.write(|w| w.set_md(val)); | ||
| 78 | macmdioar.modify(|w| { | ||
| 79 | w.set_pa(phy_addr); | ||
| 80 | w.set_rda(reg); | ||
| 81 | w.set_goc(0b01); // write | ||
| 82 | w.set_cr(self.clock_range); | ||
| 83 | w.set_mb(true); | ||
| 84 | }); | ||
| 85 | |||
| 86 | while macmdioar.read().mb() {} | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | impl<T: Instance> Drop for Sma<'_, T> { | ||
| 91 | fn drop(&mut self) { | ||
| 92 | self.pins.iter_mut().for_each(|p| p.set_as_disconnected()); | ||
| 93 | } | ||
| 94 | } | ||
diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 5be1c9739..8de26ce9d 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs | |||
| @@ -3,11 +3,10 @@ | |||
| 3 | mod rx_desc; | 3 | mod rx_desc; |
| 4 | mod tx_desc; | 4 | mod tx_desc; |
| 5 | 5 | ||
| 6 | use core::marker::PhantomData; | 6 | use core::sync::atomic::{Ordering, fence}; |
| 7 | use core::sync::atomic::{fence, Ordering}; | ||
| 8 | 7 | ||
| 9 | use embassy_hal_internal::Peri; | 8 | use embassy_hal_internal::Peri; |
| 10 | use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; | 9 | use stm32_metapac::eth::vals::{Apcs, Dm, DmaomrSr, Fes, Ftf, Ifg, Pbl, Rsf, St, Tsf}; |
| 11 | 10 | ||
| 12 | pub(crate) use self::rx_desc::{RDes, RDesRing}; | 11 | pub(crate) use self::rx_desc::{RDes, RDesRing}; |
| 13 | pub(crate) use self::tx_desc::{TDes, TDesRing}; | 12 | pub(crate) use self::tx_desc::{TDes, TDesRing}; |
| @@ -22,7 +21,6 @@ use crate::pac::AFIO; | |||
| 22 | #[cfg(any(eth_v1b, eth_v1c))] | 21 | #[cfg(any(eth_v1b, eth_v1c))] |
| 23 | use crate::pac::SYSCFG; | 22 | use crate::pac::SYSCFG; |
| 24 | use crate::pac::{ETH, RCC}; | 23 | use crate::pac::{ETH, RCC}; |
| 25 | use crate::rcc::SealedRccPeripheral; | ||
| 26 | 24 | ||
| 27 | /// Interrupt handler. | 25 | /// Interrupt handler. |
| 28 | pub struct InterruptHandler {} | 26 | pub struct InterruptHandler {} |
| @@ -53,14 +51,13 @@ pub struct Ethernet<'d, T: Instance, P: Phy> { | |||
| 53 | 51 | ||
| 54 | pins: Pins<'d>, | 52 | pins: Pins<'d>, |
| 55 | pub(crate) phy: P, | 53 | pub(crate) phy: P, |
| 56 | pub(crate) station_management: EthernetStationManagement<T>, | ||
| 57 | pub(crate) mac_addr: [u8; 6], | 54 | pub(crate) mac_addr: [u8; 6], |
| 58 | } | 55 | } |
| 59 | 56 | ||
| 60 | /// Pins of ethernet driver. | 57 | /// Pins of ethernet driver. |
| 61 | enum Pins<'d> { | 58 | enum Pins<'d> { |
| 62 | Rmii([Peri<'d, AnyPin>; 9]), | 59 | Rmii([Peri<'d, AnyPin>; 7]), |
| 63 | Mii([Peri<'d, AnyPin>; 14]), | 60 | Mii([Peri<'d, AnyPin>; 12]), |
| 64 | } | 61 | } |
| 65 | 62 | ||
| 66 | #[cfg(eth_v1a)] | 63 | #[cfg(eth_v1a)] |
| @@ -97,68 +94,105 @@ macro_rules! config_pins { | |||
| 97 | }; | 94 | }; |
| 98 | } | 95 | } |
| 99 | 96 | ||
| 100 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | 97 | impl<'d, T: Instance, SMA: sma::Instance> Ethernet<'d, T, GenericPhy<Sma<'d, SMA>>> { |
| 98 | /// Create a new RMII ethernet driver using 7 pins. | ||
| 99 | /// | ||
| 100 | /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the | ||
| 101 | /// provided [`SMA`](sma::Instance), and MDIO and MDC pins. | ||
| 102 | /// | ||
| 103 | /// See [`Ethernet::new_with_phy`] for creating an RMII ethernet | ||
| 104 | /// river with a non-standard PHY. | ||
| 105 | /// | ||
| 101 | /// safety: the returned instance is not leak-safe | 106 | /// safety: the returned instance is not leak-safe |
| 102 | pub fn new<const TX: usize, const RX: usize, #[cfg(afio)] A>( | 107 | pub fn new<const TX: usize, const RX: usize, #[cfg(afio)] A>( |
| 103 | queue: &'d mut PacketQueue<TX, RX>, | 108 | queue: &'d mut PacketQueue<TX, RX>, |
| 104 | peri: Peri<'d, T>, | 109 | peri: Peri<'d, T>, |
| 105 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | 110 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, |
| 106 | ref_clk: Peri<'d, if_afio!(impl RefClkPin<T, A>)>, | 111 | ref_clk: Peri<'d, if_afio!(impl RefClkPin<T, A>)>, |
| 107 | mdio: Peri<'d, if_afio!(impl MDIOPin<T, A>)>, | ||
| 108 | mdc: Peri<'d, if_afio!(impl MDCPin<T, A>)>, | ||
| 109 | crs: Peri<'d, if_afio!(impl CRSPin<T, A>)>, | 112 | crs: Peri<'d, if_afio!(impl CRSPin<T, A>)>, |
| 110 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, | 113 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, |
| 111 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, | 114 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, |
| 112 | tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>, | 115 | tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>, |
| 113 | tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>, | 116 | tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>, |
| 114 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, | 117 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, |
| 115 | phy: P, | ||
| 116 | mac_addr: [u8; 6], | 118 | mac_addr: [u8; 6], |
| 119 | sma: Peri<'d, SMA>, | ||
| 120 | mdio: Peri<'d, if_afio!(impl MDIOPin<SMA, A>)>, | ||
| 121 | mdc: Peri<'d, if_afio!(impl MDCPin<SMA, A>)>, | ||
| 117 | ) -> Self { | 122 | ) -> Self { |
| 118 | // Enable the necessary Clocks | 123 | let sma = Sma::new(sma, mdio, mdc); |
| 119 | #[cfg(eth_v1a)] | 124 | let phy = GenericPhy::new_auto(sma); |
| 120 | critical_section::with(|_| { | ||
| 121 | RCC.apb2enr().modify(|w| w.set_afioen(true)); | ||
| 122 | |||
| 123 | // Select RMII (Reduced Media Independent Interface) | ||
| 124 | // Must be done prior to enabling peripheral clock | ||
| 125 | AFIO.mapr().modify(|w| { | ||
| 126 | w.set_mii_rmii_sel(true); | ||
| 127 | w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); | ||
| 128 | }); | ||
| 129 | 125 | ||
| 130 | RCC.ahbenr().modify(|w| { | 126 | Self::new_with_phy( |
| 131 | w.set_ethen(true); | 127 | queue, peri, irq, ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en, mac_addr, phy, |
| 132 | w.set_ethtxen(true); | 128 | ) |
| 133 | w.set_ethrxen(true); | 129 | } |
| 134 | }); | ||
| 135 | }); | ||
| 136 | 130 | ||
| 137 | #[cfg(any(eth_v1b, eth_v1c))] | 131 | /// Create a new MII ethernet driver using 14 pins. |
| 138 | critical_section::with(|_| { | 132 | /// |
| 139 | RCC.ahb1enr().modify(|w| { | 133 | /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the |
| 140 | w.set_ethen(true); | 134 | /// provided [`SMA`](sma::Instance), and MDIO and MDC pins. |
| 141 | w.set_ethtxen(true); | 135 | /// |
| 142 | w.set_ethrxen(true); | 136 | /// See [`Ethernet::new_mii_with_phy`] for creating an RMII ethernet |
| 143 | }); | 137 | /// river with a non-standard PHY. |
| 138 | pub fn new_mii<const TX: usize, const RX: usize, #[cfg(afio)] A>( | ||
| 139 | queue: &'d mut PacketQueue<TX, RX>, | ||
| 140 | peri: Peri<'d, T>, | ||
| 141 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | ||
| 142 | rx_clk: Peri<'d, if_afio!(impl RXClkPin<T, A>)>, | ||
| 143 | tx_clk: Peri<'d, if_afio!(impl TXClkPin<T, A>)>, | ||
| 144 | rxdv: Peri<'d, if_afio!(impl RXDVPin<T, A>)>, | ||
| 145 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, | ||
| 146 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, | ||
| 147 | rx_d2: Peri<'d, if_afio!(impl RXD2Pin<T, A>)>, | ||
| 148 | rx_d3: Peri<'d, if_afio!(impl RXD3Pin<T, A>)>, | ||
| 149 | tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>, | ||
| 150 | tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>, | ||
| 151 | tx_d2: Peri<'d, if_afio!(impl TXD2Pin<T, A>)>, | ||
| 152 | tx_d3: Peri<'d, if_afio!(impl TXD3Pin<T, A>)>, | ||
| 153 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, | ||
| 154 | mac_addr: [u8; 6], | ||
| 155 | sma: Peri<'d, SMA>, | ||
| 156 | mdio: Peri<'d, if_afio!(impl MDIOPin<SMA, A>)>, | ||
| 157 | mdc: Peri<'d, if_afio!(impl MDCPin<SMA, A>)>, | ||
| 158 | ) -> Self { | ||
| 159 | let sma = Sma::new(sma, mdio, mdc); | ||
| 160 | let phy = GenericPhy::new_auto(sma); | ||
| 144 | 161 | ||
| 145 | // RMII (Reduced Media Independent Interface) | 162 | Self::new_mii_with_phy( |
| 146 | SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(true)); | 163 | queue, peri, irq, rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en, |
| 147 | }); | 164 | mac_addr, phy, |
| 165 | ) | ||
| 166 | } | ||
| 167 | } | ||
| 148 | 168 | ||
| 169 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | ||
| 170 | /// safety: the returned instance is not leak-safe | ||
| 171 | pub fn new_with_phy<const TX: usize, const RX: usize, #[cfg(afio)] A>( | ||
| 172 | queue: &'d mut PacketQueue<TX, RX>, | ||
| 173 | peri: Peri<'d, T>, | ||
| 174 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | ||
| 175 | ref_clk: Peri<'d, if_afio!(impl RefClkPin<T, A>)>, | ||
| 176 | crs: Peri<'d, if_afio!(impl CRSPin<T, A>)>, | ||
| 177 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, | ||
| 178 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, | ||
| 179 | tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>, | ||
| 180 | tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>, | ||
| 181 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, | ||
| 182 | mac_addr: [u8; 6], | ||
| 183 | phy: P, | ||
| 184 | ) -> Self { | ||
| 149 | #[cfg(eth_v1a)] | 185 | #[cfg(eth_v1a)] |
| 150 | { | 186 | { |
| 151 | config_in_pins!(ref_clk, rx_d0, rx_d1); | 187 | config_in_pins!(ref_clk, rx_d0, rx_d1); |
| 152 | config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en); | 188 | config_af_pins!(tx_d0, tx_d1, tx_en); |
| 153 | } | 189 | } |
| 154 | 190 | ||
| 155 | #[cfg(any(eth_v1b, eth_v1c))] | 191 | #[cfg(any(eth_v1b, eth_v1c))] |
| 156 | config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | 192 | config_pins!(ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); |
| 157 | 193 | ||
| 158 | let pins = Pins::Rmii([ | 194 | let pins = Pins::Rmii([ |
| 159 | ref_clk.into(), | 195 | ref_clk.into(), |
| 160 | mdio.into(), | ||
| 161 | mdc.into(), | ||
| 162 | crs.into(), | 196 | crs.into(), |
| 163 | rx_d0.into(), | 197 | rx_d0.into(), |
| 164 | rx_d1.into(), | 198 | rx_d1.into(), |
| @@ -167,7 +201,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 167 | tx_en.into(), | 201 | tx_en.into(), |
| 168 | ]); | 202 | ]); |
| 169 | 203 | ||
| 170 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) | 204 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr, true) |
| 171 | } | 205 | } |
| 172 | 206 | ||
| 173 | fn new_inner<const TX: usize, const RX: usize>( | 207 | fn new_inner<const TX: usize, const RX: usize>( |
| @@ -177,7 +211,39 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 177 | pins: Pins<'d>, | 211 | pins: Pins<'d>, |
| 178 | phy: P, | 212 | phy: P, |
| 179 | mac_addr: [u8; 6], | 213 | mac_addr: [u8; 6], |
| 214 | rmii_mii_sel: bool, | ||
| 180 | ) -> Self { | 215 | ) -> Self { |
| 216 | // Enable the necessary Clocks | ||
| 217 | #[cfg(eth_v1a)] | ||
| 218 | critical_section::with(|_| { | ||
| 219 | RCC.apb2enr().modify(|w| w.set_afioen(true)); | ||
| 220 | |||
| 221 | // Select (R)MII (Reduced Media Independent Interface) | ||
| 222 | // Must be done prior to enabling peripheral clock | ||
| 223 | AFIO.mapr().modify(|w| { | ||
| 224 | w.set_mii_rmii_sel(rmii_mii_sel); | ||
| 225 | w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); | ||
| 226 | }); | ||
| 227 | |||
| 228 | RCC.ahbenr().modify(|w| { | ||
| 229 | w.set_ethen(true); | ||
| 230 | w.set_ethtxen(true); | ||
| 231 | w.set_ethrxen(true); | ||
| 232 | }); | ||
| 233 | }); | ||
| 234 | |||
| 235 | #[cfg(any(eth_v1b, eth_v1c))] | ||
| 236 | critical_section::with(|_| { | ||
| 237 | RCC.ahb1enr().modify(|w| { | ||
| 238 | w.set_ethen(true); | ||
| 239 | w.set_ethtxen(true); | ||
| 240 | w.set_ethrxen(true); | ||
| 241 | }); | ||
| 242 | |||
| 243 | // (R)MII ((Reduced) Media Independent Interface) | ||
| 244 | SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(rmii_mii_sel)); | ||
| 245 | }); | ||
| 246 | |||
| 181 | let dma = T::regs().ethernet_dma(); | 247 | let dma = T::regs().ethernet_dma(); |
| 182 | let mac = T::regs().ethernet_mac(); | 248 | let mac = T::regs().ethernet_mac(); |
| 183 | 249 | ||
| @@ -190,7 +256,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 190 | w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping | 256 | w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping |
| 191 | w.set_fes(Fes::FES100); // fast ethernet speed | 257 | w.set_fes(Fes::FES100); // fast ethernet speed |
| 192 | w.set_dm(Dm::FULL_DUPLEX); // full duplex | 258 | w.set_dm(Dm::FULL_DUPLEX); // full duplex |
| 193 | // TODO: Carrier sense ? ECRSFD | 259 | // TODO: Carrier sense ? ECRSFD |
| 194 | }); | 260 | }); |
| 195 | 261 | ||
| 196 | // Set the mac to pass all multicast packets | 262 | // Set the mac to pass all multicast packets |
| @@ -226,30 +292,10 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 226 | 292 | ||
| 227 | // TODO MTU size setting not found for v1 ethernet, check if correct | 293 | // TODO MTU size setting not found for v1 ethernet, check if correct |
| 228 | 294 | ||
| 229 | let hclk = <T as SealedRccPeripheral>::frequency(); | ||
| 230 | let hclk_mhz = hclk.0 / 1_000_000; | ||
| 231 | |||
| 232 | // Set the MDC clock frequency in the range 1MHz - 2.5MHz | ||
| 233 | let clock_range = match hclk_mhz { | ||
| 234 | 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."), | ||
| 235 | 25..=34 => Cr::CR_20_35, // Divide by 16 | ||
| 236 | 35..=59 => Cr::CR_35_60, // Divide by 26 | ||
| 237 | 60..=99 => Cr::CR_60_100, // Divide by 42 | ||
| 238 | 100..=149 => Cr::CR_100_150, // Divide by 62 | ||
| 239 | 150..=216 => Cr::CR_150_168, // Divide by 102 | ||
| 240 | _ => { | ||
| 241 | panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") | ||
| 242 | } | ||
| 243 | }; | ||
| 244 | |||
| 245 | let mut this = Self { | 295 | let mut this = Self { |
| 246 | _peri: peri, | 296 | _peri: peri, |
| 247 | pins, | 297 | pins, |
| 248 | phy: phy, | 298 | phy: phy, |
| 249 | station_management: EthernetStationManagement { | ||
| 250 | peri: PhantomData, | ||
| 251 | clock_range: clock_range, | ||
| 252 | }, | ||
| 253 | mac_addr, | 299 | mac_addr, |
| 254 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), | 300 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), |
| 255 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), | 301 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), |
| @@ -279,8 +325,8 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 279 | w.set_tie(true); | 325 | w.set_tie(true); |
| 280 | }); | 326 | }); |
| 281 | 327 | ||
| 282 | this.phy.phy_reset(&mut this.station_management); | 328 | this.phy.phy_reset(); |
| 283 | this.phy.phy_init(&mut this.station_management); | 329 | this.phy.phy_init(); |
| 284 | 330 | ||
| 285 | interrupt::ETH.unpend(); | 331 | interrupt::ETH.unpend(); |
| 286 | unsafe { interrupt::ETH.enable() }; | 332 | unsafe { interrupt::ETH.enable() }; |
| @@ -288,15 +334,13 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 288 | this | 334 | this |
| 289 | } | 335 | } |
| 290 | 336 | ||
| 291 | /// Create a new MII ethernet driver using 14 pins. | 337 | /// Create a new MII ethernet driver using 12 pins. |
| 292 | pub fn new_mii<const TX: usize, const RX: usize, #[cfg(afio)] A>( | 338 | pub fn new_mii_with_phy<const TX: usize, const RX: usize, #[cfg(afio)] A>( |
| 293 | queue: &'d mut PacketQueue<TX, RX>, | 339 | queue: &'d mut PacketQueue<TX, RX>, |
| 294 | peri: Peri<'d, T>, | 340 | peri: Peri<'d, T>, |
| 295 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | 341 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, |
| 296 | rx_clk: Peri<'d, if_afio!(impl RXClkPin<T, A>)>, | 342 | rx_clk: Peri<'d, if_afio!(impl RXClkPin<T, A>)>, |
| 297 | tx_clk: Peri<'d, if_afio!(impl TXClkPin<T, A>)>, | 343 | tx_clk: Peri<'d, if_afio!(impl TXClkPin<T, A>)>, |
| 298 | mdio: Peri<'d, if_afio!(impl MDIOPin<T, A>)>, | ||
| 299 | mdc: Peri<'d, if_afio!(impl MDCPin<T, A>)>, | ||
| 300 | rxdv: Peri<'d, if_afio!(impl RXDVPin<T, A>)>, | 344 | rxdv: Peri<'d, if_afio!(impl RXDVPin<T, A>)>, |
| 301 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, | 345 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, |
| 302 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, | 346 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, |
| @@ -307,56 +351,23 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 307 | tx_d2: Peri<'d, if_afio!(impl TXD2Pin<T, A>)>, | 351 | tx_d2: Peri<'d, if_afio!(impl TXD2Pin<T, A>)>, |
| 308 | tx_d3: Peri<'d, if_afio!(impl TXD3Pin<T, A>)>, | 352 | tx_d3: Peri<'d, if_afio!(impl TXD3Pin<T, A>)>, |
| 309 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, | 353 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, |
| 310 | phy: P, | ||
| 311 | mac_addr: [u8; 6], | 354 | mac_addr: [u8; 6], |
| 355 | phy: P, | ||
| 312 | ) -> Self { | 356 | ) -> Self { |
| 313 | // TODO: Handle optional signals like CRS, MII_COL, RX_ER? | ||
| 314 | |||
| 315 | // Enable the necessary Clocks | ||
| 316 | #[cfg(eth_v1a)] | ||
| 317 | critical_section::with(|_| { | ||
| 318 | RCC.apb2enr().modify(|w| w.set_afioen(true)); | ||
| 319 | |||
| 320 | // Select MII (Media Independent Interface) | ||
| 321 | // Must be done prior to enabling peripheral clock | ||
| 322 | AFIO.mapr().modify(|w| { | ||
| 323 | w.set_mii_rmii_sel(false); | ||
| 324 | w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); | ||
| 325 | }); | ||
| 326 | |||
| 327 | RCC.ahbenr().modify(|w| { | ||
| 328 | w.set_ethen(true); | ||
| 329 | w.set_ethtxen(true); | ||
| 330 | w.set_ethrxen(true); | ||
| 331 | }); | ||
| 332 | }); | ||
| 333 | |||
| 334 | #[cfg(any(eth_v1b, eth_v1c))] | ||
| 335 | critical_section::with(|_| { | ||
| 336 | RCC.ahb1enr().modify(|w| { | ||
| 337 | w.set_ethen(true); | ||
| 338 | w.set_ethtxen(true); | ||
| 339 | w.set_ethrxen(true); | ||
| 340 | }); | ||
| 341 | |||
| 342 | // MII (Media Independent Interface) | ||
| 343 | SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(false)); | ||
| 344 | }); | ||
| 345 | |||
| 346 | #[cfg(eth_v1a)] | 357 | #[cfg(eth_v1a)] |
| 347 | { | 358 | { |
| 348 | config_in_pins!(rx_clk, tx_clk, rx_d0, rx_d1, rx_d2, rx_d3, rxdv); | 359 | config_in_pins!(rx_clk, tx_clk, rx_d0, rx_d1, rx_d2, rx_d3, rxdv); |
| 349 | config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); | 360 | config_af_pins!(tx_d0, tx_d1, tx_d2, tx_d3, tx_en); |
| 350 | } | 361 | } |
| 351 | 362 | ||
| 352 | #[cfg(any(eth_v1b, eth_v1c))] | 363 | #[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); | 364 | config_pins!( |
| 365 | rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en | ||
| 366 | ); | ||
| 354 | 367 | ||
| 355 | let pins = Pins::Mii([ | 368 | let pins = Pins::Mii([ |
| 356 | rx_clk.into(), | 369 | rx_clk.into(), |
| 357 | tx_clk.into(), | 370 | tx_clk.into(), |
| 358 | mdio.into(), | ||
| 359 | mdc.into(), | ||
| 360 | rxdv.into(), | 371 | rxdv.into(), |
| 361 | rx_d0.into(), | 372 | rx_d0.into(), |
| 362 | rx_d1.into(), | 373 | rx_d1.into(), |
| @@ -369,43 +380,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 369 | tx_en.into(), | 380 | tx_en.into(), |
| 370 | ]); | 381 | ]); |
| 371 | 382 | ||
| 372 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) | 383 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr, false) |
| 373 | } | ||
| 374 | } | ||
| 375 | |||
| 376 | /// Ethernet station management interface. | ||
| 377 | pub(crate) struct EthernetStationManagement<T: Instance> { | ||
| 378 | peri: PhantomData<T>, | ||
| 379 | clock_range: Cr, | ||
| 380 | } | ||
| 381 | |||
| 382 | impl<T: Instance> StationManagement for EthernetStationManagement<T> { | ||
| 383 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { | ||
| 384 | let mac = T::regs().ethernet_mac(); | ||
| 385 | |||
| 386 | mac.macmiiar().modify(|w| { | ||
| 387 | w.set_pa(phy_addr); | ||
| 388 | w.set_mr(reg); | ||
| 389 | w.set_mw(Mw::READ); // read operation | ||
| 390 | w.set_cr(self.clock_range); | ||
| 391 | w.set_mb(MbProgress::BUSY); // indicate that operation is in progress | ||
| 392 | }); | ||
| 393 | while mac.macmiiar().read().mb() == MbProgress::BUSY {} | ||
| 394 | mac.macmiidr().read().md() | ||
| 395 | } | ||
| 396 | |||
| 397 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { | ||
| 398 | let mac = T::regs().ethernet_mac(); | ||
| 399 | |||
| 400 | mac.macmiidr().write(|w| w.set_md(val)); | ||
| 401 | mac.macmiiar().modify(|w| { | ||
| 402 | w.set_pa(phy_addr); | ||
| 403 | w.set_mr(reg); | ||
| 404 | w.set_mw(Mw::WRITE); // write | ||
| 405 | w.set_cr(self.clock_range); | ||
| 406 | w.set_mb(MbProgress::BUSY); | ||
| 407 | }); | ||
| 408 | while mac.macmiiar().read().mb() == MbProgress::BUSY {} | ||
| 409 | } | 384 | } |
| 410 | } | 385 | } |
| 411 | 386 | ||
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..7f92e351c 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs | |||
| @@ -1,7 +1,6 @@ | |||
| 1 | mod descriptors; | 1 | mod descriptors; |
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | use core::sync::atomic::{Ordering, fence}; |
| 4 | use core::sync::atomic::{fence, Ordering}; | ||
| 5 | 4 | ||
| 6 | use embassy_hal_internal::Peri; | 5 | use embassy_hal_internal::Peri; |
| 7 | use stm32_metapac::syscfg::vals::EthSelPhy; | 6 | use stm32_metapac::syscfg::vals::EthSelPhy; |
| @@ -12,7 +11,6 @@ use crate::gpio::{AfType, AnyPin, OutputType, SealedPin as _, Speed}; | |||
| 12 | use crate::interrupt; | 11 | use crate::interrupt; |
| 13 | use crate::interrupt::InterruptExt; | 12 | use crate::interrupt::InterruptExt; |
| 14 | use crate::pac::ETH; | 13 | use crate::pac::ETH; |
| 15 | use crate::rcc::SealedRccPeripheral; | ||
| 16 | 14 | ||
| 17 | /// Interrupt handler. | 15 | /// Interrupt handler. |
| 18 | pub struct InterruptHandler {} | 16 | pub struct InterruptHandler {} |
| @@ -42,14 +40,13 @@ pub struct Ethernet<'d, T: Instance, P: Phy> { | |||
| 42 | pub(crate) rx: RDesRing<'d>, | 40 | pub(crate) rx: RDesRing<'d>, |
| 43 | pins: Pins<'d>, | 41 | pins: Pins<'d>, |
| 44 | pub(crate) phy: P, | 42 | pub(crate) phy: P, |
| 45 | pub(crate) station_management: EthernetStationManagement<T>, | ||
| 46 | pub(crate) mac_addr: [u8; 6], | 43 | pub(crate) mac_addr: [u8; 6], |
| 47 | } | 44 | } |
| 48 | 45 | ||
| 49 | /// Pins of ethernet driver. | 46 | /// Pins of ethernet driver. |
| 50 | enum Pins<'d> { | 47 | enum Pins<'d> { |
| 51 | Rmii([Peri<'d, AnyPin>; 9]), | 48 | Rmii([Peri<'d, AnyPin>; 7]), |
| 52 | Mii([Peri<'d, AnyPin>; 14]), | 49 | Mii([Peri<'d, AnyPin>; 12]), |
| 53 | } | 50 | } |
| 54 | 51 | ||
| 55 | macro_rules! config_pins { | 52 | macro_rules! config_pins { |
| @@ -63,41 +60,96 @@ macro_rules! config_pins { | |||
| 63 | }; | 60 | }; |
| 64 | } | 61 | } |
| 65 | 62 | ||
| 66 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | 63 | impl<'d, T: Instance, SMA: sma::Instance> Ethernet<'d, T, GenericPhy<Sma<'d, SMA>>> { |
| 67 | /// Create a new RMII ethernet driver using 9 pins. | 64 | /// Create a new RMII ethernet driver using 7 pins. |
| 65 | /// | ||
| 66 | /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the | ||
| 67 | /// provided [`SMA`](sma::Instance), and MDIO and MDC pins. | ||
| 68 | /// | ||
| 69 | /// See [`Ethernet::new_with_phy`] for creating an RMII ethernet | ||
| 70 | /// river with a non-standard PHY. | ||
| 68 | pub fn new<const TX: usize, const RX: usize>( | 71 | pub fn new<const TX: usize, const RX: usize>( |
| 69 | queue: &'d mut PacketQueue<TX, RX>, | 72 | queue: &'d mut PacketQueue<TX, RX>, |
| 70 | peri: Peri<'d, T>, | 73 | peri: Peri<'d, T>, |
| 71 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | 74 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, |
| 72 | ref_clk: Peri<'d, impl RefClkPin<T>>, | 75 | ref_clk: Peri<'d, impl RefClkPin<T>>, |
| 73 | mdio: Peri<'d, impl MDIOPin<T>>, | ||
| 74 | mdc: Peri<'d, impl MDCPin<T>>, | ||
| 75 | crs: Peri<'d, impl CRSPin<T>>, | 76 | crs: Peri<'d, impl CRSPin<T>>, |
| 76 | rx_d0: Peri<'d, impl RXD0Pin<T>>, | 77 | rx_d0: Peri<'d, impl RXD0Pin<T>>, |
| 77 | rx_d1: Peri<'d, impl RXD1Pin<T>>, | 78 | rx_d1: Peri<'d, impl RXD1Pin<T>>, |
| 78 | tx_d0: Peri<'d, impl TXD0Pin<T>>, | 79 | tx_d0: Peri<'d, impl TXD0Pin<T>>, |
| 79 | tx_d1: Peri<'d, impl TXD1Pin<T>>, | 80 | tx_d1: Peri<'d, impl TXD1Pin<T>>, |
| 80 | tx_en: Peri<'d, impl TXEnPin<T>>, | 81 | tx_en: Peri<'d, impl TXEnPin<T>>, |
| 81 | phy: P, | ||
| 82 | mac_addr: [u8; 6], | 82 | mac_addr: [u8; 6], |
| 83 | sma: Peri<'d, SMA>, | ||
| 84 | mdio: Peri<'d, impl MDIOPin<SMA>>, | ||
| 85 | mdc: Peri<'d, impl MDCPin<SMA>>, | ||
| 83 | ) -> Self { | 86 | ) -> Self { |
| 84 | // Enable the necessary clocks | 87 | let sma = Sma::new(sma, mdio, mdc); |
| 85 | critical_section::with(|_| { | 88 | let phy = GenericPhy::new_auto(sma); |
| 86 | crate::pac::RCC.ahb1enr().modify(|w| { | ||
| 87 | w.set_ethen(true); | ||
| 88 | w.set_ethtxen(true); | ||
| 89 | w.set_ethrxen(true); | ||
| 90 | }); | ||
| 91 | 89 | ||
| 92 | crate::pac::SYSCFG.pmcr().modify(|w| w.set_eth_sel_phy(EthSelPhy::RMII)); | 90 | Self::new_with_phy( |
| 93 | }); | 91 | queue, peri, irq, ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en, mac_addr, phy, |
| 92 | ) | ||
| 93 | } | ||
| 94 | |||
| 95 | /// Create a new MII ethernet driver using 14 pins. | ||
| 96 | /// | ||
| 97 | /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the | ||
| 98 | /// provided [`SMA`](sma::Instance), and MDIO and MDC pins. | ||
| 99 | /// | ||
| 100 | /// See [`Ethernet::new_mii_with_phy`] for creating an RMII ethernet | ||
| 101 | /// river with a non-standard PHY. | ||
| 102 | pub fn new_mii<const TX: usize, const RX: usize>( | ||
| 103 | queue: &'d mut PacketQueue<TX, RX>, | ||
| 104 | peri: Peri<'d, T>, | ||
| 105 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | ||
| 106 | rx_clk: Peri<'d, impl RXClkPin<T>>, | ||
| 107 | tx_clk: Peri<'d, impl TXClkPin<T>>, | ||
| 108 | rxdv: Peri<'d, impl RXDVPin<T>>, | ||
| 109 | rx_d0: Peri<'d, impl RXD0Pin<T>>, | ||
| 110 | rx_d1: Peri<'d, impl RXD1Pin<T>>, | ||
| 111 | rx_d2: Peri<'d, impl RXD2Pin<T>>, | ||
| 112 | rx_d3: Peri<'d, impl RXD3Pin<T>>, | ||
| 113 | tx_d0: Peri<'d, impl TXD0Pin<T>>, | ||
| 114 | tx_d1: Peri<'d, impl TXD1Pin<T>>, | ||
| 115 | tx_d2: Peri<'d, impl TXD2Pin<T>>, | ||
| 116 | tx_d3: Peri<'d, impl TXD3Pin<T>>, | ||
| 117 | tx_en: Peri<'d, impl TXEnPin<T>>, | ||
| 118 | mac_addr: [u8; 6], | ||
| 119 | sma: Peri<'d, SMA>, | ||
| 120 | mdio: Peri<'d, impl MDIOPin<SMA>>, | ||
| 121 | mdc: Peri<'d, impl MDCPin<SMA>>, | ||
| 122 | ) -> Self { | ||
| 123 | let sma = Sma::new(sma, mdio, mdc); | ||
| 124 | let phy = GenericPhy::new_auto(sma); | ||
| 125 | |||
| 126 | Self::new_mii_with_phy( | ||
| 127 | queue, peri, irq, rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en, | ||
| 128 | mac_addr, phy, | ||
| 129 | ) | ||
| 130 | } | ||
| 131 | } | ||
| 94 | 132 | ||
| 95 | config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | 133 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { |
| 134 | /// Create a new RMII ethernet driver using 7 pins. | ||
| 135 | pub fn new_with_phy<const TX: usize, const RX: usize>( | ||
| 136 | queue: &'d mut PacketQueue<TX, RX>, | ||
| 137 | peri: Peri<'d, T>, | ||
| 138 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | ||
| 139 | ref_clk: Peri<'d, impl RefClkPin<T>>, | ||
| 140 | crs: Peri<'d, impl CRSPin<T>>, | ||
| 141 | rx_d0: Peri<'d, impl RXD0Pin<T>>, | ||
| 142 | rx_d1: Peri<'d, impl RXD1Pin<T>>, | ||
| 143 | tx_d0: Peri<'d, impl TXD0Pin<T>>, | ||
| 144 | tx_d1: Peri<'d, impl TXD1Pin<T>>, | ||
| 145 | tx_en: Peri<'d, impl TXEnPin<T>>, | ||
| 146 | mac_addr: [u8; 6], | ||
| 147 | phy: P, | ||
| 148 | ) -> Self { | ||
| 149 | config_pins!(ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | ||
| 96 | 150 | ||
| 97 | let pins = Pins::Rmii([ | 151 | let pins = Pins::Rmii([ |
| 98 | ref_clk.into(), | 152 | ref_clk.into(), |
| 99 | mdio.into(), | ||
| 100 | mdc.into(), | ||
| 101 | crs.into(), | 153 | crs.into(), |
| 102 | rx_d0.into(), | 154 | rx_d0.into(), |
| 103 | rx_d1.into(), | 155 | rx_d1.into(), |
| @@ -106,18 +158,16 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 106 | tx_en.into(), | 158 | tx_en.into(), |
| 107 | ]); | 159 | ]); |
| 108 | 160 | ||
| 109 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) | 161 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr, EthSelPhy::RMII) |
| 110 | } | 162 | } |
| 111 | 163 | ||
| 112 | /// Create a new MII ethernet driver using 14 pins. | 164 | /// Create a new MII ethernet driver using 12 pins. |
| 113 | pub fn new_mii<const TX: usize, const RX: usize>( | 165 | pub fn new_mii_with_phy<const TX: usize, const RX: usize>( |
| 114 | queue: &'d mut PacketQueue<TX, RX>, | 166 | queue: &'d mut PacketQueue<TX, RX>, |
| 115 | peri: Peri<'d, T>, | 167 | peri: Peri<'d, T>, |
| 116 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | 168 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, |
| 117 | rx_clk: Peri<'d, impl RXClkPin<T>>, | 169 | rx_clk: Peri<'d, impl RXClkPin<T>>, |
| 118 | tx_clk: Peri<'d, impl TXClkPin<T>>, | 170 | tx_clk: Peri<'d, impl TXClkPin<T>>, |
| 119 | mdio: Peri<'d, impl MDIOPin<T>>, | ||
| 120 | mdc: Peri<'d, impl MDCPin<T>>, | ||
| 121 | rxdv: Peri<'d, impl RXDVPin<T>>, | 171 | rxdv: Peri<'d, impl RXDVPin<T>>, |
| 122 | rx_d0: Peri<'d, impl RXD0Pin<T>>, | 172 | rx_d0: Peri<'d, impl RXD0Pin<T>>, |
| 123 | rx_d1: Peri<'d, impl RXD1Pin<T>>, | 173 | rx_d1: Peri<'d, impl RXD1Pin<T>>, |
| @@ -128,29 +178,16 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 128 | tx_d2: Peri<'d, impl TXD2Pin<T>>, | 178 | tx_d2: Peri<'d, impl TXD2Pin<T>>, |
| 129 | tx_d3: Peri<'d, impl TXD3Pin<T>>, | 179 | tx_d3: Peri<'d, impl TXD3Pin<T>>, |
| 130 | tx_en: Peri<'d, impl TXEnPin<T>>, | 180 | tx_en: Peri<'d, impl TXEnPin<T>>, |
| 131 | phy: P, | ||
| 132 | mac_addr: [u8; 6], | 181 | mac_addr: [u8; 6], |
| 182 | phy: P, | ||
| 133 | ) -> Self { | 183 | ) -> Self { |
| 134 | // Enable the necessary clocks | 184 | config_pins!( |
| 135 | critical_section::with(|_| { | 185 | rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en |
| 136 | crate::pac::RCC.ahb1enr().modify(|w| { | 186 | ); |
| 137 | w.set_ethen(true); | ||
| 138 | w.set_ethtxen(true); | ||
| 139 | w.set_ethrxen(true); | ||
| 140 | }); | ||
| 141 | |||
| 142 | crate::pac::SYSCFG | ||
| 143 | .pmcr() | ||
| 144 | .modify(|w| w.set_eth_sel_phy(EthSelPhy::MII_GMII)); | ||
| 145 | }); | ||
| 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); | ||
| 148 | 187 | ||
| 149 | let pins = Pins::Mii([ | 188 | let pins = Pins::Mii([ |
| 150 | rx_clk.into(), | 189 | rx_clk.into(), |
| 151 | tx_clk.into(), | 190 | tx_clk.into(), |
| 152 | mdio.into(), | ||
| 153 | mdc.into(), | ||
| 154 | rxdv.into(), | 191 | rxdv.into(), |
| 155 | rx_d0.into(), | 192 | rx_d0.into(), |
| 156 | rx_d1.into(), | 193 | rx_d1.into(), |
| @@ -163,7 +200,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 163 | tx_en.into(), | 200 | tx_en.into(), |
| 164 | ]); | 201 | ]); |
| 165 | 202 | ||
| 166 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) | 203 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr, EthSelPhy::MII_GMII) |
| 167 | } | 204 | } |
| 168 | 205 | ||
| 169 | fn new_inner<const TX: usize, const RX: usize>( | 206 | fn new_inner<const TX: usize, const RX: usize>( |
| @@ -173,7 +210,19 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 173 | pins: Pins<'d>, | 210 | pins: Pins<'d>, |
| 174 | phy: P, | 211 | phy: P, |
| 175 | mac_addr: [u8; 6], | 212 | mac_addr: [u8; 6], |
| 213 | eth_sel_phy: EthSelPhy, | ||
| 176 | ) -> Self { | 214 | ) -> Self { |
| 215 | // Enable the necessary clocks | ||
| 216 | critical_section::with(|_| { | ||
| 217 | crate::pac::RCC.ahb1enr().modify(|w| { | ||
| 218 | w.set_ethen(true); | ||
| 219 | w.set_ethtxen(true); | ||
| 220 | w.set_ethrxen(true); | ||
| 221 | }); | ||
| 222 | |||
| 223 | crate::pac::SYSCFG.pmcr().modify(|w| w.set_eth_sel_phy(eth_sel_phy)); | ||
| 224 | }); | ||
| 225 | |||
| 177 | let dma = T::regs().ethernet_dma(); | 226 | let dma = T::regs().ethernet_dma(); |
| 178 | let mac = T::regs().ethernet_mac(); | 227 | let mac = T::regs().ethernet_mac(); |
| 179 | let mtl = T::regs().ethernet_mtl(); | 228 | let mtl = T::regs().ethernet_mtl(); |
| @@ -235,32 +284,12 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 235 | w.set_rbsz(RX_BUFFER_SIZE as u16); | 284 | w.set_rbsz(RX_BUFFER_SIZE as u16); |
| 236 | }); | 285 | }); |
| 237 | 286 | ||
| 238 | let hclk = <T as SealedRccPeripheral>::frequency(); | ||
| 239 | let hclk_mhz = hclk.0 / 1_000_000; | ||
| 240 | |||
| 241 | // Set the MDC clock frequency in the range 1MHz - 2.5MHz | ||
| 242 | let clock_range = match hclk_mhz { | ||
| 243 | 0..=34 => 2, // Divide by 16 | ||
| 244 | 35..=59 => 3, // Divide by 26 | ||
| 245 | 60..=99 => 0, // Divide by 42 | ||
| 246 | 100..=149 => 1, // Divide by 62 | ||
| 247 | 150..=249 => 4, // Divide by 102 | ||
| 248 | 250..=310 => 5, // Divide by 124 | ||
| 249 | _ => { | ||
| 250 | panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") | ||
| 251 | } | ||
| 252 | }; | ||
| 253 | |||
| 254 | let mut this = Self { | 287 | let mut this = Self { |
| 255 | _peri: peri, | 288 | _peri: peri, |
| 256 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), | 289 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), |
| 257 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), | 290 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), |
| 258 | pins, | 291 | pins, |
| 259 | phy, | 292 | phy, |
| 260 | station_management: EthernetStationManagement { | ||
| 261 | peri: PhantomData, | ||
| 262 | clock_range: clock_range, | ||
| 263 | }, | ||
| 264 | mac_addr, | 293 | mac_addr, |
| 265 | }; | 294 | }; |
| 266 | 295 | ||
| @@ -286,8 +315,8 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 286 | w.set_tie(true); | 315 | w.set_tie(true); |
| 287 | }); | 316 | }); |
| 288 | 317 | ||
| 289 | this.phy.phy_reset(&mut this.station_management); | 318 | this.phy.phy_reset(); |
| 290 | this.phy.phy_init(&mut this.station_management); | 319 | this.phy.phy_init(); |
| 291 | 320 | ||
| 292 | interrupt::ETH.unpend(); | 321 | interrupt::ETH.unpend(); |
| 293 | unsafe { interrupt::ETH.enable() }; | 322 | unsafe { interrupt::ETH.enable() }; |
| @@ -296,42 +325,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 296 | } | 325 | } |
| 297 | } | 326 | } |
| 298 | 327 | ||
| 299 | /// Ethernet SMI driver. | ||
| 300 | pub struct EthernetStationManagement<T: Instance> { | ||
| 301 | peri: PhantomData<T>, | ||
| 302 | clock_range: u8, | ||
| 303 | } | ||
| 304 | |||
| 305 | impl<T: Instance> StationManagement for EthernetStationManagement<T> { | ||
| 306 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { | ||
| 307 | let mac = T::regs().ethernet_mac(); | ||
| 308 | |||
| 309 | mac.macmdioar().modify(|w| { | ||
| 310 | w.set_pa(phy_addr); | ||
| 311 | w.set_rda(reg); | ||
| 312 | w.set_goc(0b11); // read | ||
| 313 | w.set_cr(self.clock_range); | ||
| 314 | w.set_mb(true); | ||
| 315 | }); | ||
| 316 | while mac.macmdioar().read().mb() {} | ||
| 317 | mac.macmdiodr().read().md() | ||
| 318 | } | ||
| 319 | |||
| 320 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { | ||
| 321 | let mac = T::regs().ethernet_mac(); | ||
| 322 | |||
| 323 | mac.macmdiodr().write(|w| w.set_md(val)); | ||
| 324 | mac.macmdioar().modify(|w| { | ||
| 325 | w.set_pa(phy_addr); | ||
| 326 | w.set_rda(reg); | ||
| 327 | w.set_goc(0b01); // write | ||
| 328 | w.set_cr(self.clock_range); | ||
| 329 | w.set_mb(true); | ||
| 330 | }); | ||
| 331 | while mac.macmdioar().read().mb() {} | ||
| 332 | } | ||
| 333 | } | ||
| 334 | |||
| 335 | impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> { | 328 | impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> { |
| 336 | fn drop(&mut self) { | 329 | fn drop(&mut self) { |
| 337 | let dma = T::regs().ethernet_dma(); | 330 | let dma = T::regs().ethernet_dma(); |
diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 9fce78f95..458174b5d 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs | |||
| @@ -5,13 +5,16 @@ use core::marker::PhantomData; | |||
| 5 | use core::pin::Pin; | 5 | use core::pin::Pin; |
| 6 | use core::task::{Context, Poll}; | 6 | use core::task::{Context, Poll}; |
| 7 | 7 | ||
| 8 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; | 8 | use embassy_hal_internal::PeripheralType; |
| 9 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 10 | use futures_util::FutureExt; | ||
| 10 | 11 | ||
| 11 | use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, Pull}; | 12 | use crate::gpio::{AnyPin, ExtiPin, Input, Level, Pin as GpioPin, PinNumber, Pull}; |
| 12 | use crate::pac::exti::regs::Lines; | 13 | use crate::interrupt::Interrupt as InterruptEnum; |
| 14 | use crate::interrupt::typelevel::{Binding, Handler, Interrupt as InterruptType}; | ||
| 13 | use crate::pac::EXTI; | 15 | use crate::pac::EXTI; |
| 14 | use crate::{interrupt, pac, peripherals, Peri}; | 16 | use crate::pac::exti::regs::Lines; |
| 17 | use crate::{Peri, pac}; | ||
| 15 | 18 | ||
| 16 | const EXTI_COUNT: usize = 16; | 19 | const EXTI_COUNT: usize = 16; |
| 17 | static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT]; | 20 | static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT]; |
| @@ -31,11 +34,11 @@ fn cpu_regs() -> pac::exti::Exti { | |||
| 31 | EXTI | 34 | EXTI |
| 32 | } | 35 | } |
| 33 | 36 | ||
| 34 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50)))] | 37 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50, exti_n6)))] |
| 35 | fn exticr_regs() -> pac::syscfg::Syscfg { | 38 | fn exticr_regs() -> pac::syscfg::Syscfg { |
| 36 | pac::SYSCFG | 39 | pac::SYSCFG |
| 37 | } | 40 | } |
| 38 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] | 41 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6))] |
| 39 | fn exticr_regs() -> pac::exti::Exti { | 42 | fn exticr_regs() -> pac::exti::Exti { |
| 40 | EXTI | 43 | EXTI |
| 41 | } | 44 | } |
| @@ -45,9 +48,9 @@ fn exticr_regs() -> pac::afio::Afio { | |||
| 45 | } | 48 | } |
| 46 | 49 | ||
| 47 | unsafe fn on_irq() { | 50 | unsafe fn on_irq() { |
| 48 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] | 51 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6)))] |
| 49 | let bits = EXTI.pr(0).read().0; | 52 | let bits = EXTI.pr(0).read().0; |
| 50 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] | 53 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6))] |
| 51 | let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0; | 54 | let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0; |
| 52 | 55 | ||
| 53 | // We don't handle or change any EXTI lines above 16. | 56 | // We don't handle or change any EXTI lines above 16. |
| @@ -62,16 +65,16 @@ unsafe fn on_irq() { | |||
| 62 | } | 65 | } |
| 63 | 66 | ||
| 64 | // Clear pending | 67 | // Clear pending |
| 65 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] | 68 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6)))] |
| 66 | EXTI.pr(0).write_value(Lines(bits)); | 69 | EXTI.pr(0).write_value(Lines(bits)); |
| 67 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] | 70 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6))] |
| 68 | { | 71 | { |
| 69 | EXTI.rpr(0).write_value(Lines(bits)); | 72 | EXTI.rpr(0).write_value(Lines(bits)); |
| 70 | EXTI.fpr(0).write_value(Lines(bits)); | 73 | EXTI.fpr(0).write_value(Lines(bits)); |
| 71 | } | 74 | } |
| 72 | 75 | ||
| 73 | #[cfg(feature = "low-power")] | 76 | #[cfg(feature = "low-power")] |
| 74 | crate::low_power::on_wakeup_irq(); | 77 | crate::low_power::Executor::on_wakeup_irq_or_event(); |
| 75 | } | 78 | } |
| 76 | 79 | ||
| 77 | struct BitIter(u32); | 80 | struct BitIter(u32); |
| @@ -105,10 +108,17 @@ impl<'d> Unpin for ExtiInput<'d> {} | |||
| 105 | 108 | ||
| 106 | impl<'d> ExtiInput<'d> { | 109 | impl<'d> ExtiInput<'d> { |
| 107 | /// Create an EXTI input. | 110 | /// Create an EXTI input. |
| 108 | pub fn new<T: GpioPin>(pin: Peri<'d, T>, ch: Peri<'d, T::ExtiChannel>, pull: Pull) -> Self { | 111 | /// |
| 109 | // Needed if using AnyPin+AnyChannel. | 112 | /// The Binding must bind the Channel's IRQ to [InterruptHandler]. |
| 110 | assert_eq!(pin.pin(), ch.number()); | 113 | pub fn new<T: ExtiPin + GpioPin>( |
| 111 | 114 | pin: Peri<'d, T>, | |
| 115 | _ch: Peri<'d, T::ExtiChannel>, | ||
| 116 | pull: Pull, | ||
| 117 | _irq: impl Binding< | ||
| 118 | <<T as ExtiPin>::ExtiChannel as Channel>::IRQ, | ||
| 119 | InterruptHandler<<<T as ExtiPin>::ExtiChannel as Channel>::IRQ>, | ||
| 120 | >, | ||
| 121 | ) -> Self { | ||
| 112 | Self { | 122 | Self { |
| 113 | pin: Input::new(pin, pull), | 123 | pin: Input::new(pin, pull), |
| 114 | } | 124 | } |
| @@ -133,7 +143,7 @@ impl<'d> ExtiInput<'d> { | |||
| 133 | /// | 143 | /// |
| 134 | /// This returns immediately if the pin is already high. | 144 | /// This returns immediately if the pin is already high. |
| 135 | pub async fn wait_for_high(&mut self) { | 145 | pub async fn wait_for_high(&mut self) { |
| 136 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false); | 146 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false, true); |
| 137 | if self.is_high() { | 147 | if self.is_high() { |
| 138 | return; | 148 | return; |
| 139 | } | 149 | } |
| @@ -144,7 +154,7 @@ impl<'d> ExtiInput<'d> { | |||
| 144 | /// | 154 | /// |
| 145 | /// This returns immediately if the pin is already low. | 155 | /// This returns immediately if the pin is already low. |
| 146 | pub async fn wait_for_low(&mut self) { | 156 | pub async fn wait_for_low(&mut self) { |
| 147 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true); | 157 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true, true); |
| 148 | if self.is_low() { | 158 | if self.is_low() { |
| 149 | return; | 159 | return; |
| 150 | } | 160 | } |
| @@ -155,19 +165,40 @@ impl<'d> ExtiInput<'d> { | |||
| 155 | /// | 165 | /// |
| 156 | /// If the pin is already high, it will wait for it to go low then back high. | 166 | /// If the pin is already high, it will wait for it to go low then back high. |
| 157 | pub async fn wait_for_rising_edge(&mut self) { | 167 | pub async fn wait_for_rising_edge(&mut self) { |
| 158 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false).await | 168 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false, true).await |
| 169 | } | ||
| 170 | |||
| 171 | /// Asynchronously wait until the pin sees a rising edge. | ||
| 172 | /// | ||
| 173 | /// If the pin is already high, it will wait for it to go low then back high. | ||
| 174 | pub fn poll_for_rising_edge<'a>(&mut self, cx: &mut Context<'a>) { | ||
| 175 | let _ = | ||
| 176 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false, false).poll_unpin(cx); | ||
| 159 | } | 177 | } |
| 160 | 178 | ||
| 161 | /// Asynchronously wait until the pin sees a falling edge. | 179 | /// Asynchronously wait until the pin sees a falling edge. |
| 162 | /// | 180 | /// |
| 163 | /// If the pin is already low, it will wait for it to go high then back low. | 181 | /// If the pin is already low, it will wait for it to go high then back low. |
| 164 | pub async fn wait_for_falling_edge(&mut self) { | 182 | pub async fn wait_for_falling_edge(&mut self) { |
| 165 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true).await | 183 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true, true).await |
| 184 | } | ||
| 185 | |||
| 186 | /// Asynchronously wait until the pin sees a falling edge. | ||
| 187 | /// | ||
| 188 | /// If the pin is already low, it will wait for it to go high then back low. | ||
| 189 | pub fn poll_for_falling_edge<'a>(&mut self, cx: &mut Context<'a>) { | ||
| 190 | let _ = | ||
| 191 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true, false).poll_unpin(cx); | ||
| 166 | } | 192 | } |
| 167 | 193 | ||
| 168 | /// Asynchronously wait until the pin sees any edge (either rising or falling). | 194 | /// Asynchronously wait until the pin sees any edge (either rising or falling). |
| 169 | pub async fn wait_for_any_edge(&mut self) { | 195 | pub async fn wait_for_any_edge(&mut self) { |
| 170 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true).await | 196 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true, true).await |
| 197 | } | ||
| 198 | |||
| 199 | /// Asynchronously wait until the pin sees any edge (either rising or falling). | ||
| 200 | pub fn poll_for_any_edge<'a>(&mut self, cx: &mut Context<'a>) { | ||
| 201 | let _ = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true, false).poll_unpin(cx); | ||
| 171 | } | 202 | } |
| 172 | } | 203 | } |
| 173 | 204 | ||
| @@ -226,12 +257,13 @@ impl<'d> embedded_hal_async::digital::Wait for ExtiInput<'d> { | |||
| 226 | 257 | ||
| 227 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 258 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 228 | struct ExtiInputFuture<'a> { | 259 | struct ExtiInputFuture<'a> { |
| 229 | pin: u8, | 260 | pin: PinNumber, |
| 261 | drop: bool, | ||
| 230 | phantom: PhantomData<&'a mut AnyPin>, | 262 | phantom: PhantomData<&'a mut AnyPin>, |
| 231 | } | 263 | } |
| 232 | 264 | ||
| 233 | impl<'a> ExtiInputFuture<'a> { | 265 | impl<'a> ExtiInputFuture<'a> { |
| 234 | fn new(pin: u8, port: u8, rising: bool, falling: bool) -> Self { | 266 | fn new(pin: PinNumber, port: PinNumber, rising: bool, falling: bool, drop: bool) -> Self { |
| 235 | critical_section::with(|_| { | 267 | critical_section::with(|_| { |
| 236 | let pin = pin as usize; | 268 | let pin = pin as usize; |
| 237 | exticr_regs().exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port)); | 269 | exticr_regs().exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port)); |
| @@ -239,9 +271,9 @@ impl<'a> ExtiInputFuture<'a> { | |||
| 239 | EXTI.ftsr(0).modify(|w| w.set_line(pin, falling)); | 271 | EXTI.ftsr(0).modify(|w| w.set_line(pin, falling)); |
| 240 | 272 | ||
| 241 | // clear pending bit | 273 | // clear pending bit |
| 242 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] | 274 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6)))] |
| 243 | EXTI.pr(0).write(|w| w.set_line(pin, true)); | 275 | EXTI.pr(0).write(|w| w.set_line(pin, true)); |
| 244 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] | 276 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6))] |
| 245 | { | 277 | { |
| 246 | EXTI.rpr(0).write(|w| w.set_line(pin, true)); | 278 | EXTI.rpr(0).write(|w| w.set_line(pin, true)); |
| 247 | EXTI.fpr(0).write(|w| w.set_line(pin, true)); | 279 | EXTI.fpr(0).write(|w| w.set_line(pin, true)); |
| @@ -252,6 +284,7 @@ impl<'a> ExtiInputFuture<'a> { | |||
| 252 | 284 | ||
| 253 | Self { | 285 | Self { |
| 254 | pin, | 286 | pin, |
| 287 | drop, | ||
| 255 | phantom: PhantomData, | 288 | phantom: PhantomData, |
| 256 | } | 289 | } |
| 257 | } | 290 | } |
| @@ -259,10 +292,12 @@ impl<'a> ExtiInputFuture<'a> { | |||
| 259 | 292 | ||
| 260 | impl<'a> Drop for ExtiInputFuture<'a> { | 293 | impl<'a> Drop for ExtiInputFuture<'a> { |
| 261 | fn drop(&mut self) { | 294 | fn drop(&mut self) { |
| 262 | critical_section::with(|_| { | 295 | if self.drop { |
| 263 | let pin = self.pin as _; | 296 | critical_section::with(|_| { |
| 264 | cpu_regs().imr(0).modify(|w| w.set_line(pin, false)); | 297 | let pin = self.pin as _; |
| 265 | }); | 298 | cpu_regs().imr(0).modify(|w| w.set_line(pin, false)); |
| 299 | }); | ||
| 300 | } | ||
| 266 | } | 301 | } |
| 267 | } | 302 | } |
| 268 | 303 | ||
| @@ -302,7 +337,7 @@ macro_rules! foreach_exti_irq { | |||
| 302 | (EXTI15) => { $action!(EXTI15); }; | 337 | (EXTI15) => { $action!(EXTI15); }; |
| 303 | 338 | ||
| 304 | // plus the weird ones | 339 | // plus the weird ones |
| 305 | (EXTI0_1) => { $action!( EXTI0_1 ); }; | 340 | (EXTI0_1) => { $action!(EXTI0_1); }; |
| 306 | (EXTI15_10) => { $action!(EXTI15_10); }; | 341 | (EXTI15_10) => { $action!(EXTI15_10); }; |
| 307 | (EXTI15_4) => { $action!(EXTI15_4); }; | 342 | (EXTI15_4) => { $action!(EXTI15_4); }; |
| 308 | (EXTI1_0) => { $action!(EXTI1_0); }; | 343 | (EXTI1_0) => { $action!(EXTI1_0); }; |
| @@ -315,57 +350,67 @@ macro_rules! foreach_exti_irq { | |||
| 315 | }; | 350 | }; |
| 316 | } | 351 | } |
| 317 | 352 | ||
| 318 | macro_rules! impl_irq { | 353 | ///EXTI interrupt handler. All EXTI interrupt vectors should be bound to this handler. |
| 319 | ($e:ident) => { | 354 | /// |
| 320 | #[allow(non_snake_case)] | 355 | /// It is generic over the [Interrupt](InterruptType) rather |
| 321 | #[cfg(feature = "rt")] | 356 | /// than the [Channel] because it should not be bound multiple |
| 322 | #[interrupt] | 357 | /// times to the same vector on chips which multiplex multiple EXTI interrupts into one vector. |
| 323 | unsafe fn $e() { | 358 | // |
| 324 | on_irq() | 359 | // It technically doesn't need to be generic at all, except to satisfy the generic argument |
| 325 | } | 360 | // of [Handler]. All EXTI interrupts eventually land in the same on_irq() function. |
| 326 | }; | 361 | pub struct InterruptHandler<T: crate::interrupt::typelevel::Interrupt> { |
| 362 | _phantom: PhantomData<T>, | ||
| 327 | } | 363 | } |
| 328 | 364 | ||
| 329 | foreach_exti_irq!(impl_irq); | 365 | impl<T: InterruptType> Handler<T> for InterruptHandler<T> { |
| 366 | unsafe fn on_interrupt() { | ||
| 367 | on_irq() | ||
| 368 | } | ||
| 369 | } | ||
| 330 | 370 | ||
| 331 | trait SealedChannel {} | 371 | trait SealedChannel {} |
| 332 | 372 | ||
| 333 | /// EXTI channel trait. | 373 | /// EXTI channel trait. |
| 334 | #[allow(private_bounds)] | 374 | #[allow(private_bounds)] |
| 335 | pub trait Channel: PeripheralType + SealedChannel + Sized { | 375 | pub trait Channel: PeripheralType + SealedChannel + Sized { |
| 336 | /// Get the EXTI channel number. | 376 | /// EXTI channel number. |
| 337 | fn number(&self) -> u8; | 377 | fn number(&self) -> PinNumber; |
| 378 | /// [Enum-level Interrupt](InterruptEnum), which may be the same for multiple channels. | ||
| 379 | fn irq(&self) -> InterruptEnum; | ||
| 380 | /// [Type-level Interrupt](InterruptType), which may be the same for multiple channels. | ||
| 381 | type IRQ: InterruptType; | ||
| 338 | } | 382 | } |
| 339 | 383 | ||
| 340 | /// Type-erased EXTI channel. | 384 | //Doc isn't hidden in order to surface the explanation to users, even though it's completely inoperable, not just deprecated. |
| 385 | //Entire type along with doc can probably be removed after deprecation has appeared in a release once. | ||
| 386 | /// Deprecated type-erased EXTI channel. | ||
| 341 | /// | 387 | /// |
| 342 | /// This represents ownership over any EXTI channel, known at runtime. | 388 | /// Support for AnyChannel was removed in order to support manually bindable EXTI interrupts via bind_interrupts; [ExtiInput::new()] |
| 389 | /// must know the required IRQ at compile time, and therefore cannot support type-erased channels. | ||
| 390 | #[deprecated = "type-erased EXTI channels are no longer supported, in order to support manually bindable EXTI interrupts (more info: https://github.com/embassy-rs/embassy/pull/4922)"] | ||
| 343 | pub struct AnyChannel { | 391 | pub struct AnyChannel { |
| 344 | number: u8, | 392 | #[allow(unused)] |
| 345 | } | 393 | number: PinNumber, |
| 346 | |||
| 347 | impl_peripheral!(AnyChannel); | ||
| 348 | impl SealedChannel for AnyChannel {} | ||
| 349 | impl Channel for AnyChannel { | ||
| 350 | fn number(&self) -> u8 { | ||
| 351 | self.number | ||
| 352 | } | ||
| 353 | } | 394 | } |
| 354 | 395 | ||
| 355 | macro_rules! impl_exti { | 396 | macro_rules! impl_exti { |
| 356 | ($type:ident, $number:expr) => { | 397 | ($type:ident, $number:expr) => { |
| 357 | impl SealedChannel for peripherals::$type {} | 398 | impl SealedChannel for crate::peripherals::$type {} |
| 358 | impl Channel for peripherals::$type { | 399 | impl Channel for crate::peripherals::$type { |
| 359 | fn number(&self) -> u8 { | 400 | fn number(&self) -> PinNumber { |
| 360 | $number | 401 | $number |
| 361 | } | 402 | } |
| 403 | fn irq(&self) -> InterruptEnum { | ||
| 404 | crate::_generated::peripheral_interrupts::EXTI::$type::IRQ | ||
| 405 | } | ||
| 406 | type IRQ = crate::_generated::peripheral_interrupts::EXTI::$type; | ||
| 362 | } | 407 | } |
| 363 | 408 | ||
| 364 | impl From<peripherals::$type> for AnyChannel { | 409 | //Still here to surface deprecation messages to the user - remove when removing AnyChannel |
| 365 | fn from(val: peripherals::$type) -> Self { | 410 | #[allow(deprecated)] |
| 366 | Self { | 411 | impl From<crate::peripherals::$type> for AnyChannel { |
| 367 | number: val.number() as u8, | 412 | fn from(_val: crate::peripherals::$type) -> Self { |
| 368 | } | 413 | Self { number: $number } |
| 369 | } | 414 | } |
| 370 | } | 415 | } |
| 371 | }; | 416 | }; |
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/c.rs b/embassy-stm32/src/flash/c.rs new file mode 100644 index 000000000..0ad1002b0 --- /dev/null +++ b/embassy-stm32/src/flash/c.rs | |||
| @@ -0,0 +1,131 @@ | |||
| 1 | use core::ptr::write_volatile; | ||
| 2 | use core::sync::atomic::{Ordering, fence}; | ||
| 3 | |||
| 4 | use cortex_m::interrupt; | ||
| 5 | |||
| 6 | use super::{FlashSector, WRITE_SIZE}; | ||
| 7 | use crate::flash::Error; | ||
| 8 | use crate::pac; | ||
| 9 | |||
| 10 | pub(crate) unsafe fn lock() { | ||
| 11 | pac::FLASH.cr().modify(|w| w.set_lock(true)); | ||
| 12 | } | ||
| 13 | pub(crate) unsafe fn unlock() { | ||
| 14 | // Wait, while the memory interface is busy. | ||
| 15 | wait_busy(); | ||
| 16 | |||
| 17 | // Unlock flash | ||
| 18 | if pac::FLASH.cr().read().lock() { | ||
| 19 | pac::FLASH.keyr().write_value(0x4567_0123); | ||
| 20 | pac::FLASH.keyr().write_value(0xCDEF_89AB); | ||
| 21 | } | ||
| 22 | } | ||
| 23 | |||
| 24 | pub(crate) unsafe fn enable_blocking_write() { | ||
| 25 | assert_eq!(0, WRITE_SIZE % 4); | ||
| 26 | pac::FLASH.cr().write(|w| w.set_pg(true)); | ||
| 27 | } | ||
| 28 | |||
| 29 | pub(crate) unsafe fn disable_blocking_write() { | ||
| 30 | pac::FLASH.cr().write(|w| w.set_pg(false)); | ||
| 31 | } | ||
| 32 | |||
| 33 | pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { | ||
| 34 | let mut address = start_address; | ||
| 35 | for val in buf.chunks(4) { | ||
| 36 | write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into()))); | ||
| 37 | address += val.len() as u32; | ||
| 38 | |||
| 39 | // prevents parallelism errors | ||
| 40 | fence(Ordering::SeqCst); | ||
| 41 | } | ||
| 42 | |||
| 43 | wait_ready_blocking() | ||
| 44 | } | ||
| 45 | |||
| 46 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { | ||
| 47 | let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; | ||
| 48 | |||
| 49 | #[cfg(feature = "defmt")] | ||
| 50 | defmt::trace!( | ||
| 51 | "STM32C0 Erase: addr=0x{:08x}, idx={}, erase_size={}", | ||
| 52 | sector.start, | ||
| 53 | idx, | ||
| 54 | super::BANK1_REGION.erase_size | ||
| 55 | ); | ||
| 56 | |||
| 57 | wait_busy(); | ||
| 58 | clear_all_err(); | ||
| 59 | |||
| 60 | // Explicitly unlock before erase | ||
| 61 | unlock(); | ||
| 62 | |||
| 63 | interrupt::free(|_| { | ||
| 64 | #[cfg(feature = "defmt")] | ||
| 65 | { | ||
| 66 | let cr_before = pac::FLASH.cr().read(); | ||
| 67 | defmt::trace!("FLASH_CR before: 0x{:08x}", cr_before.0); | ||
| 68 | } | ||
| 69 | |||
| 70 | pac::FLASH.cr().modify(|w| { | ||
| 71 | w.set_per(true); | ||
| 72 | w.set_pnb(idx as u8); | ||
| 73 | w.set_strt(true); | ||
| 74 | }); | ||
| 75 | |||
| 76 | #[cfg(feature = "defmt")] | ||
| 77 | { | ||
| 78 | let cr_after = pac::FLASH.cr().read(); | ||
| 79 | defmt::trace!( | ||
| 80 | "FLASH_CR after: 0x{:08x}, PER={}, PNB={}, STRT={}", | ||
| 81 | cr_after.0, | ||
| 82 | cr_after.per(), | ||
| 83 | cr_after.pnb(), | ||
| 84 | cr_after.strt() | ||
| 85 | ); | ||
| 86 | } | ||
| 87 | }); | ||
| 88 | |||
| 89 | let ret: Result<(), Error> = wait_ready_blocking(); | ||
| 90 | |||
| 91 | // Clear erase bit | ||
| 92 | pac::FLASH.cr().modify(|w| w.set_per(false)); | ||
| 93 | |||
| 94 | // Explicitly lock after erase | ||
| 95 | lock(); | ||
| 96 | |||
| 97 | // Extra wait to ensure operation completes | ||
| 98 | wait_busy(); | ||
| 99 | |||
| 100 | ret | ||
| 101 | } | ||
| 102 | |||
| 103 | pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> { | ||
| 104 | while pac::FLASH.sr().read().bsy() {} | ||
| 105 | |||
| 106 | let sr = pac::FLASH.sr().read(); | ||
| 107 | |||
| 108 | if sr.progerr() { | ||
| 109 | return Err(Error::Prog); | ||
| 110 | } | ||
| 111 | |||
| 112 | if sr.wrperr() { | ||
| 113 | return Err(Error::Protected); | ||
| 114 | } | ||
| 115 | |||
| 116 | if sr.pgaerr() { | ||
| 117 | return Err(Error::Unaligned); | ||
| 118 | } | ||
| 119 | |||
| 120 | Ok(()) | ||
| 121 | } | ||
| 122 | |||
| 123 | pub(crate) unsafe fn clear_all_err() { | ||
| 124 | // read and write back the same value. | ||
| 125 | // This clears all "write 1 to clear" bits. | ||
| 126 | pac::FLASH.sr().modify(|_| {}); | ||
| 127 | } | ||
| 128 | |||
| 129 | fn wait_busy() { | ||
| 130 | while pac::FLASH.sr().read().bsy() {} | ||
| 131 | } | ||
diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 10023e637..60d00e766 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. |
| @@ -102,7 +102,13 @@ pub(super) unsafe fn blocking_write( | |||
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | let mut address = base + offset; | 104 | let mut address = base + offset; |
| 105 | trace!("Writing {} bytes at 0x{:x}", bytes.len(), address); | 105 | trace!( |
| 106 | "Writing {} bytes at 0x{:x} (base=0x{:x}, offset=0x{:x})", | ||
| 107 | bytes.len(), | ||
| 108 | address, | ||
| 109 | base, | ||
| 110 | offset | ||
| 111 | ); | ||
| 106 | 112 | ||
| 107 | for chunk in bytes.chunks(WRITE_SIZE) { | 113 | for chunk in bytes.chunks(WRITE_SIZE) { |
| 108 | write_chunk(address, chunk)?; | 114 | write_chunk(address, chunk)?; |
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..d7ba2f571 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 | ||
| @@ -44,7 +44,6 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) | |||
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { | 46 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { |
| 47 | let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; | ||
| 48 | wait_busy(); | 47 | wait_busy(); |
| 49 | clear_all_err(); | 48 | clear_all_err(); |
| 50 | 49 | ||
| @@ -54,9 +53,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E | |||
| 54 | #[cfg(any(flash_g0x0, flash_g0x1, flash_g4c3))] | 53 | #[cfg(any(flash_g0x0, flash_g0x1, flash_g4c3))] |
| 55 | w.set_bker(sector.bank == crate::flash::FlashBank::Bank2); | 54 | w.set_bker(sector.bank == crate::flash::FlashBank::Bank2); |
| 56 | #[cfg(flash_g0x0)] | 55 | #[cfg(flash_g0x0)] |
| 57 | w.set_pnb(idx as u16); | 56 | w.set_pnb(sector.index_in_bank as u16); |
| 58 | #[cfg(not(flash_g0x0))] | 57 | #[cfg(not(flash_g0x0))] |
| 59 | w.set_pnb(idx as u8); | 58 | w.set_pnb(sector.index_in_bank as u8); |
| 60 | w.set_strt(true); | 59 | w.set_strt(true); |
| 61 | }); | 60 | }); |
| 62 | }); | 61 | }); |
| @@ -105,19 +104,27 @@ fn wait_busy() { | |||
| 105 | #[cfg(all(bank_setup_configurable, any(flash_g4c2, flash_g4c3, flash_g4c4)))] | 104 | #[cfg(all(bank_setup_configurable, any(flash_g4c2, flash_g4c3, flash_g4c4)))] |
| 106 | pub(crate) fn check_bank_setup() { | 105 | pub(crate) fn check_bank_setup() { |
| 107 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dbank() { | 106 | 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"); | 107 | panic!( |
| 108 | "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" | ||
| 109 | ); | ||
| 109 | } | 110 | } |
| 110 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dbank() { | 111 | 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"); | 112 | panic!( |
| 113 | "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" | ||
| 114 | ); | ||
| 112 | } | 115 | } |
| 113 | } | 116 | } |
| 114 | 117 | ||
| 115 | #[cfg(all(bank_setup_configurable, flash_g0x1))] | 118 | #[cfg(all(bank_setup_configurable, flash_g0x1))] |
| 116 | pub(crate) fn check_bank_setup() { | 119 | pub(crate) fn check_bank_setup() { |
| 117 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dual_bank() { | 120 | 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"); | 121 | panic!( |
| 122 | "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" | ||
| 123 | ); | ||
| 119 | } | 124 | } |
| 120 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dual_bank() { | 125 | 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"); | 126 | panic!( |
| 127 | "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" | ||
| 128 | ); | ||
| 122 | } | 129 | } |
| 123 | } | 130 | } |
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..b342f4a83 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs | |||
| @@ -1,10 +1,31 @@ | |||
| 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 embassy_sync::waitqueue::AtomicWaker; |
| 5 | use pac::flash::regs::Sr; | ||
| 6 | |||
| 7 | use super::{BANK1_REGION, FLASH_REGIONS, FlashSector, WRITE_SIZE}; | ||
| 5 | use crate::flash::Error; | 8 | use crate::flash::Error; |
| 6 | use crate::pac; | 9 | use crate::pac; |
| 7 | 10 | ||
| 11 | static WAKER: AtomicWaker = AtomicWaker::new(); | ||
| 12 | |||
| 13 | pub(crate) unsafe fn on_interrupt() { | ||
| 14 | // Clear IRQ flags | ||
| 15 | pac::FLASH.bank(0).ccr().write(|w| { | ||
| 16 | w.set_clr_eop(true); | ||
| 17 | w.set_clr_operr(true); | ||
| 18 | }); | ||
| 19 | if is_dual_bank() { | ||
| 20 | pac::FLASH.bank(1).ccr().write(|w| { | ||
| 21 | w.set_clr_eop(true); | ||
| 22 | w.set_clr_operr(true); | ||
| 23 | }); | ||
| 24 | } | ||
| 25 | |||
| 26 | WAKER.wake(); | ||
| 27 | } | ||
| 28 | |||
| 8 | const fn is_dual_bank() -> bool { | 29 | const fn is_dual_bank() -> bool { |
| 9 | FLASH_REGIONS.len() >= 2 | 30 | FLASH_REGIONS.len() >= 2 |
| 10 | } | 31 | } |
| @@ -29,12 +50,68 @@ pub(crate) unsafe fn unlock() { | |||
| 29 | } | 50 | } |
| 30 | } | 51 | } |
| 31 | 52 | ||
| 53 | pub(crate) unsafe fn enable_write() { | ||
| 54 | enable_blocking_write(); | ||
| 55 | } | ||
| 56 | |||
| 57 | pub(crate) unsafe fn disable_write() { | ||
| 58 | disable_blocking_write(); | ||
| 59 | } | ||
| 60 | |||
| 32 | pub(crate) unsafe fn enable_blocking_write() { | 61 | pub(crate) unsafe fn enable_blocking_write() { |
| 33 | assert_eq!(0, WRITE_SIZE % 4); | 62 | assert_eq!(0, WRITE_SIZE % 4); |
| 34 | } | 63 | } |
| 35 | 64 | ||
| 36 | pub(crate) unsafe fn disable_blocking_write() {} | 65 | pub(crate) unsafe fn disable_blocking_write() {} |
| 37 | 66 | ||
| 67 | pub(crate) async unsafe fn write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { | ||
| 68 | // We cannot have the write setup sequence in begin_write as it depends on the address | ||
| 69 | let bank = if start_address < BANK1_REGION.end() { | ||
| 70 | pac::FLASH.bank(0) | ||
| 71 | } else { | ||
| 72 | pac::FLASH.bank(1) | ||
| 73 | }; | ||
| 74 | bank.cr().write(|w| { | ||
| 75 | w.set_pg(true); | ||
| 76 | #[cfg(flash_h7)] | ||
| 77 | w.set_psize(2); // 32 bits at once | ||
| 78 | w.set_eopie(true); | ||
| 79 | w.set_operrie(true); | ||
| 80 | }); | ||
| 81 | cortex_m::asm::isb(); | ||
| 82 | cortex_m::asm::dsb(); | ||
| 83 | fence(Ordering::SeqCst); | ||
| 84 | |||
| 85 | let mut res = None; | ||
| 86 | let mut address = start_address; | ||
| 87 | for val in buf.chunks(4) { | ||
| 88 | write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into()))); | ||
| 89 | address += val.len() as u32; | ||
| 90 | |||
| 91 | res = Some(wait_ready(bank).await); | ||
| 92 | bank.sr().modify(|w| { | ||
| 93 | if w.eop() { | ||
| 94 | w.set_eop(true); | ||
| 95 | } | ||
| 96 | }); | ||
| 97 | if unwrap!(res).is_err() { | ||
| 98 | break; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | cortex_m::asm::isb(); | ||
| 103 | cortex_m::asm::dsb(); | ||
| 104 | fence(Ordering::SeqCst); | ||
| 105 | |||
| 106 | bank.cr().write(|w| { | ||
| 107 | w.set_pg(false); | ||
| 108 | w.set_eopie(false); | ||
| 109 | w.set_operrie(false); | ||
| 110 | }); | ||
| 111 | |||
| 112 | unwrap!(res) | ||
| 113 | } | ||
| 114 | |||
| 38 | pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { | 115 | pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { |
| 39 | // We cannot have the write setup sequence in begin_write as it depends on the address | 116 | // We cannot have the write setup sequence in begin_write as it depends on the address |
| 40 | let bank = if start_address < BANK1_REGION.end() { | 117 | let bank = if start_address < BANK1_REGION.end() { |
| @@ -77,6 +154,36 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) | |||
| 77 | unwrap!(res) | 154 | unwrap!(res) |
| 78 | } | 155 | } |
| 79 | 156 | ||
| 157 | pub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Error> { | ||
| 158 | let bank = pac::FLASH.bank(sector.bank as usize); | ||
| 159 | bank.cr().modify(|w| { | ||
| 160 | w.set_ser(true); | ||
| 161 | #[cfg(flash_h7)] | ||
| 162 | w.set_snb(sector.index_in_bank); | ||
| 163 | #[cfg(flash_h7ab)] | ||
| 164 | w.set_ssn(sector.index_in_bank); | ||
| 165 | w.set_eopie(true); | ||
| 166 | w.set_operrie(true); | ||
| 167 | }); | ||
| 168 | |||
| 169 | bank.cr().modify(|w| { | ||
| 170 | w.set_start(true); | ||
| 171 | }); | ||
| 172 | |||
| 173 | cortex_m::asm::isb(); | ||
| 174 | cortex_m::asm::dsb(); | ||
| 175 | fence(Ordering::SeqCst); | ||
| 176 | |||
| 177 | let ret: Result<(), Error> = wait_ready(bank).await; | ||
| 178 | bank.cr().modify(|w| { | ||
| 179 | w.set_ser(false); | ||
| 180 | w.set_eopie(false); | ||
| 181 | w.set_operrie(false); | ||
| 182 | }); | ||
| 183 | bank_clear_all_err(bank); | ||
| 184 | ret | ||
| 185 | } | ||
| 186 | |||
| 80 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { | 187 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { |
| 81 | let bank = pac::FLASH.bank(sector.bank as usize); | 188 | let bank = pac::FLASH.bank(sector.bank as usize); |
| 82 | bank.cr().modify(|w| { | 189 | bank.cr().modify(|w| { |
| @@ -112,46 +219,59 @@ unsafe fn bank_clear_all_err(bank: pac::flash::Bank) { | |||
| 112 | bank.sr().modify(|_| {}); | 219 | bank.sr().modify(|_| {}); |
| 113 | } | 220 | } |
| 114 | 221 | ||
| 222 | async fn wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { | ||
| 223 | use core::future::poll_fn; | ||
| 224 | use core::task::Poll; | ||
| 225 | |||
| 226 | poll_fn(|cx| { | ||
| 227 | WAKER.register(cx.waker()); | ||
| 228 | |||
| 229 | let sr = bank.sr().read(); | ||
| 230 | if !sr.bsy() && !sr.qw() { | ||
| 231 | Poll::Ready(get_result(sr)) | ||
| 232 | } else { | ||
| 233 | return Poll::Pending; | ||
| 234 | } | ||
| 235 | }) | ||
| 236 | .await | ||
| 237 | } | ||
| 238 | |||
| 115 | unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { | 239 | unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { |
| 116 | loop { | 240 | loop { |
| 117 | let sr = bank.sr().read(); | 241 | let sr = bank.sr().read(); |
| 118 | 242 | ||
| 119 | if !sr.bsy() && !sr.qw() { | 243 | if !sr.bsy() && !sr.qw() { |
| 120 | if sr.wrperr() { | 244 | return get_result(sr); |
| 121 | return Err(Error::Protected); | ||
| 122 | } | ||
| 123 | if sr.pgserr() { | ||
| 124 | error!("pgserr"); | ||
| 125 | return Err(Error::Seq); | ||
| 126 | } | ||
| 127 | if sr.incerr() { | ||
| 128 | // writing to a different address when programming 256 bit word was not finished | ||
| 129 | error!("incerr"); | ||
| 130 | return Err(Error::Seq); | ||
| 131 | } | ||
| 132 | if sr.crcrderr() { | ||
| 133 | error!("crcrderr"); | ||
| 134 | return Err(Error::Seq); | ||
| 135 | } | ||
| 136 | if sr.operr() { | ||
| 137 | return Err(Error::Prog); | ||
| 138 | } | ||
| 139 | if sr.sneccerr1() { | ||
| 140 | // single ECC error | ||
| 141 | return Err(Error::Prog); | ||
| 142 | } | ||
| 143 | if sr.dbeccerr() { | ||
| 144 | // double ECC error | ||
| 145 | return Err(Error::Prog); | ||
| 146 | } | ||
| 147 | if sr.rdperr() { | ||
| 148 | return Err(Error::Protected); | ||
| 149 | } | ||
| 150 | if sr.rdserr() { | ||
| 151 | return Err(Error::Protected); | ||
| 152 | } | ||
| 153 | |||
| 154 | return Ok(()); | ||
| 155 | } | 245 | } |
| 156 | } | 246 | } |
| 157 | } | 247 | } |
| 248 | |||
| 249 | fn get_result(sr: Sr) -> Result<(), Error> { | ||
| 250 | if sr.wrperr() { | ||
| 251 | Err(Error::Protected) | ||
| 252 | } else if sr.pgserr() { | ||
| 253 | error!("pgserr"); | ||
| 254 | Err(Error::Seq) | ||
| 255 | } else if sr.incerr() { | ||
| 256 | // writing to a different address when programming 256 bit word was not finished | ||
| 257 | error!("incerr"); | ||
| 258 | Err(Error::Seq) | ||
| 259 | } else if sr.crcrderr() { | ||
| 260 | error!("crcrderr"); | ||
| 261 | Err(Error::Seq) | ||
| 262 | } else if sr.operr() { | ||
| 263 | Err(Error::Prog) | ||
| 264 | } else if sr.sneccerr1() { | ||
| 265 | // single ECC error | ||
| 266 | Err(Error::Prog) | ||
| 267 | } else if sr.dbeccerr() { | ||
| 268 | // double ECC error | ||
| 269 | Err(Error::Prog) | ||
| 270 | } else if sr.rdperr() { | ||
| 271 | Err(Error::Protected) | ||
| 272 | } else if sr.rdserr() { | ||
| 273 | Err(Error::Protected) | ||
| 274 | } else { | ||
| 275 | Ok(()) | ||
| 276 | } | ||
| 277 | } | ||
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/mod.rs b/embassy-stm32/src/flash/mod.rs index 3e74d857a..6211a37b7 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs | |||
| @@ -1,14 +1,14 @@ | |||
| 1 | //! Flash memory (FLASH) | 1 | //! Flash memory (FLASH) |
| 2 | use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; | 2 | use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; |
| 3 | 3 | ||
| 4 | #[cfg(flash_f4)] | 4 | #[cfg(any(flash_f4, flash_h7, flash_h7ab))] |
| 5 | mod asynch; | 5 | mod asynch; |
| 6 | #[cfg(flash)] | 6 | #[cfg(flash)] |
| 7 | mod common; | 7 | mod common; |
| 8 | #[cfg(eeprom)] | 8 | #[cfg(eeprom)] |
| 9 | mod eeprom; | 9 | mod eeprom; |
| 10 | 10 | ||
| 11 | #[cfg(flash_f4)] | 11 | #[cfg(any(flash_f4, flash_h7, flash_h7ab))] |
| 12 | pub use asynch::InterruptHandler; | 12 | pub use asynch::InterruptHandler; |
| 13 | #[cfg(flash)] | 13 | #[cfg(flash)] |
| 14 | pub use common::*; | 14 | pub use common::*; |
| @@ -99,6 +99,7 @@ compile_error!("The 'eeprom' cfg is enabled for a non-L0/L1 chip family. This is | |||
| 99 | #[cfg_attr(flash_f4, path = "f4.rs")] | 99 | #[cfg_attr(flash_f4, path = "f4.rs")] |
| 100 | #[cfg_attr(flash_f7, path = "f7.rs")] | 100 | #[cfg_attr(flash_f7, path = "f7.rs")] |
| 101 | #[cfg_attr(any(flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4), path = "g.rs")] | 101 | #[cfg_attr(any(flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4), path = "g.rs")] |
| 102 | #[cfg_attr(flash_c0, path = "c.rs")] | ||
| 102 | #[cfg_attr(flash_h7, path = "h7.rs")] | 103 | #[cfg_attr(flash_h7, path = "h7.rs")] |
| 103 | #[cfg_attr(flash_h7ab, path = "h7.rs")] | 104 | #[cfg_attr(flash_h7ab, path = "h7.rs")] |
| 104 | #[cfg_attr(any(flash_u5, flash_wba), path = "u5.rs")] | 105 | #[cfg_attr(any(flash_u5, flash_wba), path = "u5.rs")] |
| @@ -108,7 +109,7 @@ compile_error!("The 'eeprom' cfg is enabled for a non-L0/L1 chip family. This is | |||
| 108 | #[cfg_attr( | 109 | #[cfg_attr( |
| 109 | not(any( | 110 | not(any( |
| 110 | flash_l0, flash_l1, flash_l4, flash_l5, flash_wl, flash_wb, flash_f0, flash_f1, flash_f2, flash_f3, flash_f4, | 111 | flash_l0, flash_l1, flash_l4, flash_l5, flash_wl, flash_wb, flash_f0, flash_f1, flash_f2, flash_f3, flash_f4, |
| 111 | flash_f7, flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4, flash_h7, flash_h7ab, flash_u5, | 112 | flash_f7, flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4, flash_c0, flash_h7, flash_h7ab, flash_u5, |
| 112 | flash_wba, flash_h50, flash_u0, flash_h5, | 113 | flash_wba, flash_h50, flash_u0, flash_h5, |
| 113 | )), | 114 | )), |
| 114 | path = "other.rs" | 115 | path = "other.rs" |
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/fmt.rs b/embassy-stm32/src/fmt.rs index b6ae24ee8..b731796f0 100644 --- a/embassy-stm32/src/fmt.rs +++ b/embassy-stm32/src/fmt.rs | |||
| @@ -207,6 +207,16 @@ macro_rules! error { | |||
| 207 | } | 207 | } |
| 208 | 208 | ||
| 209 | #[cfg(feature = "defmt")] | 209 | #[cfg(feature = "defmt")] |
| 210 | trait_set::trait_set! { | ||
| 211 | pub trait Debuggable = Debug + defmt::Format; | ||
| 212 | } | ||
| 213 | |||
| 214 | #[cfg(not(feature = "defmt"))] | ||
| 215 | trait_set::trait_set! { | ||
| 216 | pub trait Debuggable = Debug; | ||
| 217 | } | ||
| 218 | |||
| 219 | #[cfg(feature = "defmt")] | ||
| 210 | #[collapse_debuginfo(yes)] | 220 | #[collapse_debuginfo(yes)] |
| 211 | macro_rules! unwrap { | 221 | macro_rules! unwrap { |
| 212 | ($($x:tt)*) => { | 222 | ($($x:tt)*) => { |
diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index 5a8d23183..5de8bad2c 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; |
| @@ -592,7 +592,7 @@ impl AfType { | |||
| 592 | 592 | ||
| 593 | #[inline(never)] | 593 | #[inline(never)] |
| 594 | #[cfg(gpio_v1)] | 594 | #[cfg(gpio_v1)] |
| 595 | fn set_as_af(pin_port: u8, af_type: AfType) { | 595 | fn set_as_af(pin_port: PinNumber, af_type: AfType) { |
| 596 | let pin = unsafe { AnyPin::steal(pin_port) }; | 596 | let pin = unsafe { AnyPin::steal(pin_port) }; |
| 597 | let r = pin.block(); | 597 | let r = pin.block(); |
| 598 | let n = pin._pin() as usize; | 598 | let n = pin._pin() as usize; |
| @@ -649,7 +649,7 @@ impl AfType { | |||
| 649 | 649 | ||
| 650 | #[inline(never)] | 650 | #[inline(never)] |
| 651 | #[cfg(gpio_v2)] | 651 | #[cfg(gpio_v2)] |
| 652 | fn set_as_af(pin_port: u8, af_num: u8, af_type: AfType) { | 652 | fn set_as_af(pin_port: PinNumber, af_num: u8, af_type: AfType) { |
| 653 | let pin = unsafe { AnyPin::steal(pin_port) }; | 653 | let pin = unsafe { AnyPin::steal(pin_port) }; |
| 654 | let r = pin.block(); | 654 | let r = pin.block(); |
| 655 | let n = pin._pin() as usize; | 655 | let n = pin._pin() as usize; |
| @@ -663,7 +663,7 @@ fn set_as_af(pin_port: u8, af_num: u8, af_type: AfType) { | |||
| 663 | 663 | ||
| 664 | #[inline(never)] | 664 | #[inline(never)] |
| 665 | #[cfg(gpio_v2)] | 665 | #[cfg(gpio_v2)] |
| 666 | fn set_speed(pin_port: u8, speed: Speed) { | 666 | fn set_speed(pin_port: PinNumber, speed: Speed) { |
| 667 | let pin = unsafe { AnyPin::steal(pin_port) }; | 667 | let pin = unsafe { AnyPin::steal(pin_port) }; |
| 668 | let r = pin.block(); | 668 | let r = pin.block(); |
| 669 | let n = pin._pin() as usize; | 669 | let n = pin._pin() as usize; |
| @@ -672,7 +672,7 @@ fn set_speed(pin_port: u8, speed: Speed) { | |||
| 672 | } | 672 | } |
| 673 | 673 | ||
| 674 | #[inline(never)] | 674 | #[inline(never)] |
| 675 | fn set_as_analog(pin_port: u8) { | 675 | fn set_as_analog(pin_port: PinNumber) { |
| 676 | let pin = unsafe { AnyPin::steal(pin_port) }; | 676 | let pin = unsafe { AnyPin::steal(pin_port) }; |
| 677 | let r = pin.block(); | 677 | let r = pin.block(); |
| 678 | let n = pin._pin() as usize; | 678 | let n = pin._pin() as usize; |
| @@ -684,11 +684,15 @@ fn set_as_analog(pin_port: u8) { | |||
| 684 | }); | 684 | }); |
| 685 | 685 | ||
| 686 | #[cfg(gpio_v2)] | 686 | #[cfg(gpio_v2)] |
| 687 | r.moder().modify(|w| w.set_moder(n, vals::Moder::ANALOG)); | 687 | { |
| 688 | #[cfg(any(stm32l47x, stm32l48x))] | ||
| 689 | r.ascr().modify(|w| w.set_asc(n, true)); | ||
| 690 | r.moder().modify(|w| w.set_moder(n, vals::Moder::ANALOG)); | ||
| 691 | } | ||
| 688 | } | 692 | } |
| 689 | 693 | ||
| 690 | #[inline(never)] | 694 | #[inline(never)] |
| 691 | fn get_pull(pin_port: u8) -> Pull { | 695 | fn get_pull(pin_port: PinNumber) -> Pull { |
| 692 | let pin = unsafe { AnyPin::steal(pin_port) }; | 696 | let pin = unsafe { AnyPin::steal(pin_port) }; |
| 693 | let r = pin.block(); | 697 | let r = pin.block(); |
| 694 | let n = pin._pin() as usize; | 698 | let n = pin._pin() as usize; |
| @@ -727,15 +731,15 @@ pub struct AfioRemapBool<const V: bool>; | |||
| 727 | pub struct AfioRemapNotApplicable; | 731 | pub struct AfioRemapNotApplicable; |
| 728 | 732 | ||
| 729 | pub(crate) trait SealedPin { | 733 | pub(crate) trait SealedPin { |
| 730 | fn pin_port(&self) -> u8; | 734 | fn pin_port(&self) -> PinNumber; |
| 731 | 735 | ||
| 732 | #[inline] | 736 | #[inline] |
| 733 | fn _pin(&self) -> u8 { | 737 | fn _pin(&self) -> PinNumber { |
| 734 | self.pin_port() % 16 | 738 | self.pin_port() % 16 |
| 735 | } | 739 | } |
| 736 | 740 | ||
| 737 | #[inline] | 741 | #[inline] |
| 738 | fn _port(&self) -> u8 { | 742 | fn _port(&self) -> PinNumber { |
| 739 | self.pin_port() / 16 | 743 | self.pin_port() / 16 |
| 740 | } | 744 | } |
| 741 | 745 | ||
| @@ -798,31 +802,49 @@ pub(crate) trait SealedPin { | |||
| 798 | } | 802 | } |
| 799 | } | 803 | } |
| 800 | 804 | ||
| 801 | /// GPIO pin trait. | 805 | /// GPIO pin number type. |
| 806 | /// | ||
| 807 | /// Some chips have a total number of ports that exceeds 8, a larger integer | ||
| 808 | /// is needed to hold the total pin number `(ports * number)`. | ||
| 809 | #[cfg(not(stm32n6))] | ||
| 810 | pub type PinNumber = u8; | ||
| 811 | |||
| 812 | /// GPIO pin number type. | ||
| 813 | /// | ||
| 814 | /// Some chips have a total number of ports that exceeds 8, a larger integer | ||
| 815 | /// is needed to hold the total pin number `(ports * number)`. | ||
| 816 | #[cfg(stm32n6)] | ||
| 817 | pub type PinNumber = u16; | ||
| 818 | |||
| 819 | /// Pin that can be used to configure an [ExtiInput](crate::exti::ExtiInput). This trait is lost when converting to [AnyPin]. | ||
| 820 | #[cfg(feature = "exti")] | ||
| 802 | #[allow(private_bounds)] | 821 | #[allow(private_bounds)] |
| 803 | pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static { | 822 | pub trait ExtiPin: PeripheralType + SealedPin { |
| 804 | /// EXTI channel assigned to this pin. | 823 | /// EXTI channel assigned to this pin. |
| 805 | /// | 824 | /// |
| 806 | /// For example, PC4 uses EXTI4. | 825 | /// For example, PC4 uses EXTI4. |
| 807 | #[cfg(feature = "exti")] | ||
| 808 | type ExtiChannel: crate::exti::Channel; | 826 | type ExtiChannel: crate::exti::Channel; |
| 827 | } | ||
| 809 | 828 | ||
| 829 | /// GPIO pin trait. | ||
| 830 | #[allow(private_bounds)] | ||
| 831 | pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static { | ||
| 810 | /// Number of the pin within the port (0..31) | 832 | /// Number of the pin within the port (0..31) |
| 811 | #[inline] | 833 | #[inline] |
| 812 | fn pin(&self) -> u8 { | 834 | fn pin(&self) -> PinNumber { |
| 813 | self._pin() | 835 | self._pin() |
| 814 | } | 836 | } |
| 815 | 837 | ||
| 816 | /// Port of the pin | 838 | /// Port of the pin |
| 817 | #[inline] | 839 | #[inline] |
| 818 | fn port(&self) -> u8 { | 840 | fn port(&self) -> PinNumber { |
| 819 | self._port() | 841 | self._port() |
| 820 | } | 842 | } |
| 821 | } | 843 | } |
| 822 | 844 | ||
| 823 | /// Type-erased GPIO pin | 845 | /// Type-erased GPIO pin. |
| 824 | pub struct AnyPin { | 846 | pub struct AnyPin { |
| 825 | pin_port: u8, | 847 | pin_port: PinNumber, |
| 826 | } | 848 | } |
| 827 | 849 | ||
| 828 | impl AnyPin { | 850 | impl AnyPin { |
| @@ -830,12 +852,12 @@ impl AnyPin { | |||
| 830 | /// | 852 | /// |
| 831 | /// `pin_port` is `port_num * 16 + pin_num`, where `port_num` is 0 for port `A`, 1 for port `B`, etc... | 853 | /// `pin_port` is `port_num * 16 + pin_num`, where `port_num` is 0 for port `A`, 1 for port `B`, etc... |
| 832 | #[inline] | 854 | #[inline] |
| 833 | pub unsafe fn steal(pin_port: u8) -> Peri<'static, Self> { | 855 | pub unsafe fn steal(pin_port: PinNumber) -> Peri<'static, Self> { |
| 834 | Peri::new_unchecked(Self { pin_port }) | 856 | Peri::new_unchecked(Self { pin_port }) |
| 835 | } | 857 | } |
| 836 | 858 | ||
| 837 | #[inline] | 859 | #[inline] |
| 838 | fn _port(&self) -> u8 { | 860 | fn _port(&self) -> PinNumber { |
| 839 | self.pin_port / 16 | 861 | self.pin_port / 16 |
| 840 | } | 862 | } |
| 841 | 863 | ||
| @@ -848,13 +870,10 @@ impl AnyPin { | |||
| 848 | } | 870 | } |
| 849 | 871 | ||
| 850 | impl_peripheral!(AnyPin); | 872 | impl_peripheral!(AnyPin); |
| 851 | impl Pin for AnyPin { | 873 | impl Pin for AnyPin {} |
| 852 | #[cfg(feature = "exti")] | ||
| 853 | type ExtiChannel = crate::exti::AnyChannel; | ||
| 854 | } | ||
| 855 | impl SealedPin for AnyPin { | 874 | impl SealedPin for AnyPin { |
| 856 | #[inline] | 875 | #[inline] |
| 857 | fn pin_port(&self) -> u8 { | 876 | fn pin_port(&self) -> PinNumber { |
| 858 | self.pin_port | 877 | self.pin_port |
| 859 | } | 878 | } |
| 860 | } | 879 | } |
| @@ -864,12 +883,14 @@ impl SealedPin for AnyPin { | |||
| 864 | foreach_pin!( | 883 | foreach_pin!( |
| 865 | ($pin_name:ident, $port_name:ident, $port_num:expr, $pin_num:expr, $exti_ch:ident) => { | 884 | ($pin_name:ident, $port_name:ident, $port_num:expr, $pin_num:expr, $exti_ch:ident) => { |
| 866 | impl Pin for peripherals::$pin_name { | 885 | impl Pin for peripherals::$pin_name { |
| 867 | #[cfg(feature = "exti")] | 886 | } |
| 887 | #[cfg(feature = "exti")] | ||
| 888 | impl ExtiPin for peripherals::$pin_name { | ||
| 868 | type ExtiChannel = peripherals::$exti_ch; | 889 | type ExtiChannel = peripherals::$exti_ch; |
| 869 | } | 890 | } |
| 870 | impl SealedPin for peripherals::$pin_name { | 891 | impl SealedPin for peripherals::$pin_name { |
| 871 | #[inline] | 892 | #[inline] |
| 872 | fn pin_port(&self) -> u8 { | 893 | fn pin_port(&self) -> PinNumber { |
| 873 | $port_num * 16 + $pin_num | 894 | $port_num * 16 + $pin_num |
| 874 | } | 895 | } |
| 875 | } | 896 | } |
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..b5fa3c897 100644 --- a/embassy-stm32/src/hsem/mod.rs +++ b/embassy-stm32/src/hsem/mod.rs | |||
| @@ -1,14 +1,22 @@ | |||
| 1 | //! Hardware Semaphore (HSEM) | 1 | //! Hardware Semaphore (HSEM) |
| 2 | 2 | ||
| 3 | use core::future::poll_fn; | ||
| 4 | use core::marker::PhantomData; | ||
| 5 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 6 | use core::task::Poll; | ||
| 7 | |||
| 8 | #[cfg(all(stm32wb, feature = "low-power"))] | ||
| 9 | use critical_section::CriticalSection; | ||
| 3 | use embassy_hal_internal::PeripheralType; | 10 | use embassy_hal_internal::PeripheralType; |
| 11 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 4 | 12 | ||
| 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. | 13 | // 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, | 14 | // Those MCUs have a different HSEM implementation (Secure semaphore lock support, |
| 9 | // Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), | 15 | // Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), |
| 10 | // which is not yet supported by this code. | 16 | // which is not yet supported by this code. |
| 11 | use crate::Peri; | 17 | use crate::Peri; |
| 18 | use crate::rcc::{self, RccPeripheral}; | ||
| 19 | use crate::{interrupt, pac}; | ||
| 12 | 20 | ||
| 13 | /// HSEM error. | 21 | /// HSEM error. |
| 14 | #[derive(Debug)] | 22 | #[derive(Debug)] |
| @@ -41,63 +49,153 @@ pub enum CoreId { | |||
| 41 | Core1 = 0x8, | 49 | Core1 = 0x8, |
| 42 | } | 50 | } |
| 43 | 51 | ||
| 44 | /// Get the current core id | 52 | impl CoreId { |
| 45 | /// This code assume that it is only executed on a Cortex-M M0+, M4 or M7 core. | 53 | /// Get the current core id |
| 46 | #[inline(always)] | 54 | /// This code assume that it is only executed on a Cortex-M M0+, M4 or M7 core. |
| 47 | pub fn get_current_coreid() -> CoreId { | 55 | pub fn current() -> Self { |
| 48 | let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() }; | 56 | let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() }; |
| 49 | match (cpuid & 0x000000F0) >> 4 { | 57 | match (cpuid & 0x000000F0) >> 4 { |
| 50 | #[cfg(any(stm32wb, stm32wl))] | 58 | #[cfg(any(stm32wb, stm32wl))] |
| 51 | 0x0 => CoreId::Core1, | 59 | 0x0 => CoreId::Core1, |
| 52 | 60 | ||
| 53 | #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))] | 61 | #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))] |
| 54 | 0x4 => CoreId::Core0, | 62 | 0x4 => CoreId::Core0, |
| 55 | 63 | ||
| 56 | #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] | 64 | #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] |
| 57 | 0x4 => CoreId::Core1, | 65 | 0x4 => CoreId::Core1, |
| 58 | 66 | ||
| 59 | #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] | 67 | #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] |
| 60 | 0x7 => CoreId::Core0, | 68 | 0x7 => CoreId::Core0, |
| 61 | _ => panic!("Unknown Cortex-M core"), | 69 | _ => panic!("Unknown Cortex-M core"), |
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | /// Translates the core ID to an index into the interrupt registers. | ||
| 74 | pub fn to_index(&self) -> usize { | ||
| 75 | match &self { | ||
| 76 | CoreId::Core0 => 0, | ||
| 77 | #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))] | ||
| 78 | CoreId::Core1 => 1, | ||
| 79 | } | ||
| 62 | } | 80 | } |
| 63 | } | 81 | } |
| 64 | 82 | ||
| 65 | /// Translates the core ID to an index into the interrupt registers. | 83 | #[cfg(not(all(stm32wb, feature = "low-power")))] |
| 66 | #[inline(always)] | 84 | const PUB_CHANNELS: usize = 6; |
| 67 | fn core_id_to_index(core: CoreId) -> usize { | 85 | |
| 68 | match core { | 86 | #[cfg(all(stm32wb, feature = "low-power"))] |
| 69 | CoreId::Core0 => 0, | 87 | const PUB_CHANNELS: usize = 4; |
| 70 | #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))] | 88 | |
| 71 | CoreId::Core1 => 1, | 89 | /// TX interrupt handler. |
| 90 | pub struct HardwareSemaphoreInterruptHandler<T: Instance> { | ||
| 91 | _phantom: PhantomData<T>, | ||
| 92 | } | ||
| 93 | |||
| 94 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for HardwareSemaphoreInterruptHandler<T> { | ||
| 95 | unsafe fn on_interrupt() { | ||
| 96 | let core_id = CoreId::current(); | ||
| 97 | |||
| 98 | for number in 0..5 { | ||
| 99 | if T::regs().isr(core_id.to_index()).read().isf(number as usize) { | ||
| 100 | T::regs() | ||
| 101 | .icr(core_id.to_index()) | ||
| 102 | .write(|w| w.set_isc(number as usize, true)); | ||
| 103 | |||
| 104 | T::regs() | ||
| 105 | .ier(core_id.to_index()) | ||
| 106 | .modify(|w| w.set_ise(number as usize, false)); | ||
| 107 | |||
| 108 | T::state().waker_for(number).wake(); | ||
| 109 | } | ||
| 110 | } | ||
| 72 | } | 111 | } |
| 73 | } | 112 | } |
| 74 | 113 | ||
| 75 | /// HSEM driver | 114 | /// Hardware semaphore mutex |
| 76 | pub struct HardwareSemaphore<'d, T: Instance> { | 115 | pub struct HardwareSemaphoreMutex<'a, T: Instance> { |
| 77 | _peri: Peri<'d, T>, | 116 | index: u8, |
| 117 | process_id: u8, | ||
| 118 | _lifetime: PhantomData<&'a mut T>, | ||
| 78 | } | 119 | } |
| 79 | 120 | ||
| 80 | impl<'d, T: Instance> HardwareSemaphore<'d, T> { | 121 | impl<'a, T: Instance> Drop for HardwareSemaphoreMutex<'a, T> { |
| 81 | /// Creates a new HardwareSemaphore instance. | 122 | fn drop(&mut self) { |
| 82 | pub fn new(peripheral: Peri<'d, T>) -> Self { | 123 | HardwareSemaphoreChannel::<'a, T> { |
| 83 | rcc::enable_and_reset::<T>(); | 124 | index: self.index, |
| 125 | _lifetime: PhantomData, | ||
| 126 | } | ||
| 127 | .unlock(self.process_id); | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | /// Hardware semaphore channel | ||
| 132 | pub struct HardwareSemaphoreChannel<'a, T: Instance> { | ||
| 133 | index: u8, | ||
| 134 | _lifetime: PhantomData<&'a mut T>, | ||
| 135 | } | ||
| 136 | |||
| 137 | impl<'a, T: Instance> HardwareSemaphoreChannel<'a, T> { | ||
| 138 | pub(crate) const fn new(number: u8) -> Self { | ||
| 139 | core::assert!(number > 0 && number <= 6); | ||
| 140 | |||
| 141 | Self { | ||
| 142 | index: number - 1, | ||
| 143 | _lifetime: PhantomData, | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Locks the semaphore. | ||
| 148 | /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to | ||
| 149 | /// check if the lock has been successful, carried out from the HSEM_Rx register. | ||
| 150 | pub async fn lock(&mut self, process_id: u8) -> HardwareSemaphoreMutex<'a, T> { | ||
| 151 | let _scoped_block_stop = T::RCC_INFO.block_stop(); | ||
| 152 | let core_id = CoreId::current(); | ||
| 153 | |||
| 154 | poll_fn(|cx| { | ||
| 155 | T::state().waker_for(self.index).register(cx.waker()); | ||
| 156 | |||
| 157 | compiler_fence(Ordering::SeqCst); | ||
| 158 | |||
| 159 | T::regs() | ||
| 160 | .ier(core_id.to_index()) | ||
| 161 | .modify(|w| w.set_ise(self.index as usize, true)); | ||
| 84 | 162 | ||
| 85 | HardwareSemaphore { _peri: peripheral } | 163 | match self.try_lock(process_id) { |
| 164 | Some(mutex) => Poll::Ready(mutex), | ||
| 165 | None => Poll::Pending, | ||
| 166 | } | ||
| 167 | }) | ||
| 168 | .await | ||
| 169 | } | ||
| 170 | |||
| 171 | /// Try to lock the semaphor | ||
| 172 | /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to | ||
| 173 | /// check if the lock has been successful, carried out from the HSEM_Rx register. | ||
| 174 | pub fn try_lock(&mut self, process_id: u8) -> Option<HardwareSemaphoreMutex<'a, T>> { | ||
| 175 | if self.two_step_lock(process_id).is_ok() { | ||
| 176 | Some(HardwareSemaphoreMutex { | ||
| 177 | index: self.index, | ||
| 178 | process_id: process_id, | ||
| 179 | _lifetime: PhantomData, | ||
| 180 | }) | ||
| 181 | } else { | ||
| 182 | None | ||
| 183 | } | ||
| 86 | } | 184 | } |
| 87 | 185 | ||
| 88 | /// Locks the semaphore. | 186 | /// Locks the semaphore. |
| 89 | /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to | 187 | /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to |
| 90 | /// check if the lock has been successful, carried out from the HSEM_Rx register. | 188 | /// check if the lock has been successful, carried out from the HSEM_Rx register. |
| 91 | pub fn two_step_lock(&mut self, sem_id: u8, process_id: u8) -> Result<(), HsemError> { | 189 | pub fn two_step_lock(&mut self, process_id: u8) -> Result<(), HsemError> { |
| 92 | T::regs().r(sem_id as usize).write(|w| { | 190 | T::regs().r(self.index as usize).write(|w| { |
| 93 | w.set_procid(process_id); | 191 | w.set_procid(process_id); |
| 94 | w.set_coreid(get_current_coreid() as u8); | 192 | w.set_coreid(CoreId::current() as u8); |
| 95 | w.set_lock(true); | 193 | w.set_lock(true); |
| 96 | }); | 194 | }); |
| 97 | let reg = T::regs().r(sem_id as usize).read(); | 195 | let reg = T::regs().r(self.index as usize).read(); |
| 98 | match ( | 196 | match ( |
| 99 | reg.lock(), | 197 | reg.lock(), |
| 100 | reg.coreid() == get_current_coreid() as u8, | 198 | reg.coreid() == CoreId::current() as u8, |
| 101 | reg.procid() == process_id, | 199 | reg.procid() == process_id, |
| 102 | ) { | 200 | ) { |
| 103 | (true, true, true) => Ok(()), | 201 | (true, true, true) => Ok(()), |
| @@ -108,9 +206,9 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { | |||
| 108 | /// Locks the semaphore. | 206 | /// Locks the semaphore. |
| 109 | /// The 1-step procedure consists in a read to lock and check the semaphore in a single step, | 207 | /// The 1-step procedure consists in a read to lock and check the semaphore in a single step, |
| 110 | /// carried out from the HSEM_RLRx register. | 208 | /// carried out from the HSEM_RLRx register. |
| 111 | pub fn one_step_lock(&mut self, sem_id: u8) -> Result<(), HsemError> { | 209 | pub fn one_step_lock(&mut self) -> Result<(), HsemError> { |
| 112 | let reg = T::regs().rlr(sem_id as usize).read(); | 210 | let reg = T::regs().rlr(self.index as usize).read(); |
| 113 | match (reg.lock(), reg.coreid() == get_current_coreid() as u8, reg.procid()) { | 211 | match (reg.lock(), reg.coreid() == CoreId::current() as u8, reg.procid()) { |
| 114 | (false, true, 0) => Ok(()), | 212 | (false, true, 0) => Ok(()), |
| 115 | _ => Err(HsemError::LockFailed), | 213 | _ => Err(HsemError::LockFailed), |
| 116 | } | 214 | } |
| @@ -119,14 +217,60 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { | |||
| 119 | /// Unlocks the semaphore. | 217 | /// Unlocks the semaphore. |
| 120 | /// Unlocking a semaphore is a protected process, to prevent accidental clearing by a AHB bus | 218 | /// Unlocking a semaphore is a protected process, to prevent accidental clearing by a AHB bus |
| 121 | /// core ID or by a process not having the semaphore lock right. | 219 | /// core ID or by a process not having the semaphore lock right. |
| 122 | pub fn unlock(&mut self, sem_id: u8, process_id: u8) { | 220 | pub fn unlock(&mut self, process_id: u8) { |
| 123 | T::regs().r(sem_id as usize).write(|w| { | 221 | T::regs().r(self.index as usize).write(|w| { |
| 124 | w.set_procid(process_id); | 222 | w.set_procid(process_id); |
| 125 | w.set_coreid(get_current_coreid() as u8); | 223 | w.set_coreid(CoreId::current() as u8); |
| 126 | w.set_lock(false); | 224 | w.set_lock(false); |
| 127 | }); | 225 | }); |
| 128 | } | 226 | } |
| 129 | 227 | ||
| 228 | /// Return the channel number | ||
| 229 | pub const fn channel(&self) -> u8 { | ||
| 230 | self.index + 1 | ||
| 231 | } | ||
| 232 | } | ||
| 233 | |||
| 234 | /// HSEM driver | ||
| 235 | pub struct HardwareSemaphore<T: Instance> { | ||
| 236 | _type: PhantomData<T>, | ||
| 237 | } | ||
| 238 | |||
| 239 | impl<T: Instance> HardwareSemaphore<T> { | ||
| 240 | /// Creates a new HardwareSemaphore instance. | ||
| 241 | pub fn new<'d>( | ||
| 242 | _peripheral: Peri<'d, T>, | ||
| 243 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, HardwareSemaphoreInterruptHandler<T>> + 'd, | ||
| 244 | ) -> Self { | ||
| 245 | rcc::enable_and_reset_without_stop::<T>(); | ||
| 246 | |||
| 247 | HardwareSemaphore { _type: PhantomData } | ||
| 248 | } | ||
| 249 | |||
| 250 | /// Get a single channel, and keep the global struct | ||
| 251 | pub const fn channel_for<'a>(&'a mut self, number: u8) -> HardwareSemaphoreChannel<'a, T> { | ||
| 252 | #[cfg(all(stm32wb, feature = "low-power"))] | ||
| 253 | core::assert!(number != 3 && number != 4); | ||
| 254 | |||
| 255 | HardwareSemaphoreChannel::new(number) | ||
| 256 | } | ||
| 257 | |||
| 258 | /// Split the global struct into channels | ||
| 259 | /// | ||
| 260 | /// If using low-power mode, channels 3 and 4 will not be returned | ||
| 261 | pub const fn split<'a>(self) -> [HardwareSemaphoreChannel<'a, T>; PUB_CHANNELS] { | ||
| 262 | [ | ||
| 263 | HardwareSemaphoreChannel::new(1), | ||
| 264 | HardwareSemaphoreChannel::new(2), | ||
| 265 | #[cfg(not(all(stm32wb, feature = "low-power")))] | ||
| 266 | HardwareSemaphoreChannel::new(3), | ||
| 267 | #[cfg(not(all(stm32wb, feature = "low-power")))] | ||
| 268 | HardwareSemaphoreChannel::new(4), | ||
| 269 | HardwareSemaphoreChannel::new(5), | ||
| 270 | HardwareSemaphoreChannel::new(6), | ||
| 271 | ] | ||
| 272 | } | ||
| 273 | |||
| 130 | /// Unlocks all semaphores. | 274 | /// Unlocks all semaphores. |
| 131 | /// All semaphores locked by a COREID can be unlocked at once by using the HSEM_CR | 275 | /// All semaphores locked by a COREID can be unlocked at once by using the HSEM_CR |
| 132 | /// register. Write COREID and correct KEY value in HSEM_CR. All locked semaphores with a | 276 | /// register. Write COREID and correct KEY value in HSEM_CR. All locked semaphores with a |
| @@ -138,11 +282,6 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { | |||
| 138 | }); | 282 | }); |
| 139 | } | 283 | } |
| 140 | 284 | ||
| 141 | /// Checks if the semaphore is locked. | ||
| 142 | pub fn is_semaphore_locked(&self, sem_id: u8) -> bool { | ||
| 143 | T::regs().r(sem_id as usize).read().lock() | ||
| 144 | } | ||
| 145 | |||
| 146 | /// Sets the clear (unlock) key | 285 | /// Sets the clear (unlock) key |
| 147 | pub fn set_clear_key(&mut self, key: u16) { | 286 | pub fn set_clear_key(&mut self, key: u16) { |
| 148 | T::regs().keyr().modify(|w| w.set_key(key)); | 287 | T::regs().keyr().modify(|w| w.set_key(key)); |
| @@ -152,38 +291,61 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { | |||
| 152 | pub fn get_clear_key(&mut self) -> u16 { | 291 | pub fn get_clear_key(&mut self) -> u16 { |
| 153 | T::regs().keyr().read().key() | 292 | T::regs().keyr().read().key() |
| 154 | } | 293 | } |
| 294 | } | ||
| 155 | 295 | ||
| 156 | /// Sets the interrupt enable bit for the semaphore. | 296 | #[cfg(all(stm32wb, feature = "low-power"))] |
| 157 | pub fn enable_interrupt(&mut self, core_id: CoreId, sem_x: usize, enable: bool) { | 297 | pub(crate) fn init_hsem(cs: CriticalSection) { |
| 158 | T::regs() | 298 | rcc::enable_and_reset_with_cs::<crate::peripherals::HSEM>(cs); |
| 159 | .ier(core_id_to_index(core_id)) | 299 | |
| 160 | .modify(|w| w.set_ise(sem_x, enable)); | 300 | unsafe { |
| 301 | crate::rcc::REFCOUNT_STOP1 = 0; | ||
| 302 | crate::rcc::REFCOUNT_STOP2 = 0; | ||
| 161 | } | 303 | } |
| 304 | } | ||
| 162 | 305 | ||
| 163 | /// Gets the interrupt flag for the semaphore. | 306 | struct State { |
| 164 | pub fn is_interrupt_active(&mut self, core_id: CoreId, sem_x: usize) -> bool { | 307 | wakers: [AtomicWaker; 6], |
| 165 | T::regs().isr(core_id_to_index(core_id)).read().isf(sem_x) | 308 | } |
| 309 | |||
| 310 | impl State { | ||
| 311 | const fn new() -> Self { | ||
| 312 | Self { | ||
| 313 | wakers: [const { AtomicWaker::new() }; 6], | ||
| 314 | } | ||
| 166 | } | 315 | } |
| 167 | 316 | ||
| 168 | /// Clears the interrupt flag for the semaphore. | 317 | const fn waker_for(&self, index: u8) -> &AtomicWaker { |
| 169 | pub fn clear_interrupt(&mut self, core_id: CoreId, sem_x: usize) { | 318 | &self.wakers[index as usize] |
| 170 | T::regs() | ||
| 171 | .icr(core_id_to_index(core_id)) | ||
| 172 | .write(|w| w.set_isc(sem_x, false)); | ||
| 173 | } | 319 | } |
| 174 | } | 320 | } |
| 175 | 321 | ||
| 176 | trait SealedInstance { | 322 | trait SealedInstance { |
| 177 | fn regs() -> pac::hsem::Hsem; | 323 | fn regs() -> pac::hsem::Hsem; |
| 324 | fn state() -> &'static State; | ||
| 178 | } | 325 | } |
| 179 | 326 | ||
| 180 | /// HSEM instance trait. | 327 | /// HSEM instance trait. |
| 181 | #[allow(private_bounds)] | 328 | #[allow(private_bounds)] |
| 182 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {} | 329 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static { |
| 330 | /// Interrupt for this peripheral. | ||
| 331 | type Interrupt: interrupt::typelevel::Interrupt; | ||
| 332 | } | ||
| 183 | 333 | ||
| 184 | impl SealedInstance for crate::peripherals::HSEM { | 334 | impl SealedInstance for crate::peripherals::HSEM { |
| 185 | fn regs() -> crate::pac::hsem::Hsem { | 335 | fn regs() -> crate::pac::hsem::Hsem { |
| 186 | crate::pac::HSEM | 336 | crate::pac::HSEM |
| 187 | } | 337 | } |
| 338 | |||
| 339 | fn state() -> &'static State { | ||
| 340 | static STATE: State = State::new(); | ||
| 341 | &STATE | ||
| 342 | } | ||
| 188 | } | 343 | } |
| 189 | impl Instance for crate::peripherals::HSEM {} | 344 | |
| 345 | foreach_interrupt!( | ||
| 346 | ($inst:ident, hsem, $block:ident, $signal_name:ident, $irq:ident) => { | ||
| 347 | impl Instance for crate::peripherals::$inst { | ||
| 348 | type Interrupt = crate::interrupt::typelevel::$irq; | ||
| 349 | } | ||
| 350 | }; | ||
| 351 | ); | ||
diff --git a/embassy-stm32/src/hspi/mod.rs b/embassy-stm32/src/hspi/mod.rs index 95d9e5099..1d3560678 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; |
| @@ -391,7 +391,7 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { | |||
| 391 | while T::REGS.sr().read().busy() {} | 391 | while T::REGS.sr().read().busy() {} |
| 392 | 392 | ||
| 393 | T::REGS.cr().modify(|w| { | 393 | T::REGS.cr().modify(|w| { |
| 394 | w.set_fmode(0.into()); | 394 | w.set_fmode(FunctionalMode::IndirectWrite.into()); |
| 395 | }); | 395 | }); |
| 396 | 396 | ||
| 397 | // Configure alternate bytes | 397 | // Configure alternate bytes |
| @@ -498,7 +498,8 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { | |||
| 498 | w.set_dmaen(false); | 498 | w.set_dmaen(false); |
| 499 | }); | 499 | }); |
| 500 | 500 | ||
| 501 | self.configure_command(&transaction, Some(buf.len()))?; | 501 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 502 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 502 | 503 | ||
| 503 | let current_address = T::REGS.ar().read().address(); | 504 | let current_address = T::REGS.ar().read().address(); |
| 504 | let current_instruction = T::REGS.ir().read().instruction(); | 505 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -537,7 +538,8 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { | |||
| 537 | w.set_dmaen(false); | 538 | w.set_dmaen(false); |
| 538 | }); | 539 | }); |
| 539 | 540 | ||
| 540 | self.configure_command(&transaction, Some(buf.len()))?; | 541 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 542 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 541 | 543 | ||
| 542 | T::REGS | 544 | T::REGS |
| 543 | .cr() | 545 | .cr() |
| @@ -767,7 +769,8 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { | |||
| 767 | // Wait for peripheral to be free | 769 | // Wait for peripheral to be free |
| 768 | while T::REGS.sr().read().busy() {} | 770 | while T::REGS.sr().read().busy() {} |
| 769 | 771 | ||
| 770 | self.configure_command(&transaction, Some(buf.len()))?; | 772 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 773 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 771 | 774 | ||
| 772 | let current_address = T::REGS.ar().read().address(); | 775 | let current_address = T::REGS.ar().read().address(); |
| 773 | let current_instruction = T::REGS.ir().read().instruction(); | 776 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -782,16 +785,18 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { | |||
| 782 | T::REGS.ar().write(|v| v.set_address(current_address)); | 785 | T::REGS.ar().write(|v| v.set_address(current_address)); |
| 783 | } | 786 | } |
| 784 | 787 | ||
| 785 | let transfer = unsafe { | 788 | for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { |
| 786 | self.dma | 789 | let transfer = unsafe { |
| 787 | .as_mut() | 790 | self.dma |
| 788 | .unwrap() | 791 | .as_mut() |
| 789 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | 792 | .unwrap() |
| 790 | }; | 793 | .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) |
| 794 | }; | ||
| 791 | 795 | ||
| 792 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 796 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 793 | 797 | ||
| 794 | transfer.blocking_wait(); | 798 | transfer.blocking_wait(); |
| 799 | } | ||
| 795 | 800 | ||
| 796 | finish_dma(T::REGS); | 801 | finish_dma(T::REGS); |
| 797 | 802 | ||
| @@ -807,21 +812,24 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { | |||
| 807 | // Wait for peripheral to be free | 812 | // Wait for peripheral to be free |
| 808 | while T::REGS.sr().read().busy() {} | 813 | while T::REGS.sr().read().busy() {} |
| 809 | 814 | ||
| 810 | self.configure_command(&transaction, Some(buf.len()))?; | 815 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 816 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 811 | T::REGS | 817 | T::REGS |
| 812 | .cr() | 818 | .cr() |
| 813 | .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); | 819 | .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); |
| 814 | 820 | ||
| 815 | let transfer = unsafe { | 821 | for chunk in buf.chunks(0xFFFF / W::size().bytes()) { |
| 816 | self.dma | 822 | let transfer = unsafe { |
| 817 | .as_mut() | 823 | self.dma |
| 818 | .unwrap() | 824 | .as_mut() |
| 819 | .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) | 825 | .unwrap() |
| 820 | }; | 826 | .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) |
| 827 | }; | ||
| 821 | 828 | ||
| 822 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 829 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 823 | 830 | ||
| 824 | transfer.blocking_wait(); | 831 | transfer.blocking_wait(); |
| 832 | } | ||
| 825 | 833 | ||
| 826 | finish_dma(T::REGS); | 834 | finish_dma(T::REGS); |
| 827 | 835 | ||
| @@ -837,7 +845,8 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { | |||
| 837 | // Wait for peripheral to be free | 845 | // Wait for peripheral to be free |
| 838 | while T::REGS.sr().read().busy() {} | 846 | while T::REGS.sr().read().busy() {} |
| 839 | 847 | ||
| 840 | self.configure_command(&transaction, Some(buf.len()))?; | 848 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 849 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 841 | 850 | ||
| 842 | let current_address = T::REGS.ar().read().address(); | 851 | let current_address = T::REGS.ar().read().address(); |
| 843 | let current_instruction = T::REGS.ir().read().instruction(); | 852 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -852,16 +861,18 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { | |||
| 852 | T::REGS.ar().write(|v| v.set_address(current_address)); | 861 | T::REGS.ar().write(|v| v.set_address(current_address)); |
| 853 | } | 862 | } |
| 854 | 863 | ||
| 855 | let transfer = unsafe { | 864 | for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { |
| 856 | self.dma | 865 | let transfer = unsafe { |
| 857 | .as_mut() | 866 | self.dma |
| 858 | .unwrap() | 867 | .as_mut() |
| 859 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | 868 | .unwrap() |
| 860 | }; | 869 | .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) |
| 870 | }; | ||
| 861 | 871 | ||
| 862 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 872 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 863 | 873 | ||
| 864 | transfer.await; | 874 | transfer.await; |
| 875 | } | ||
| 865 | 876 | ||
| 866 | finish_dma(T::REGS); | 877 | finish_dma(T::REGS); |
| 867 | 878 | ||
| @@ -877,21 +888,25 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { | |||
| 877 | // Wait for peripheral to be free | 888 | // Wait for peripheral to be free |
| 878 | while T::REGS.sr().read().busy() {} | 889 | while T::REGS.sr().read().busy() {} |
| 879 | 890 | ||
| 880 | self.configure_command(&transaction, Some(buf.len()))?; | 891 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 892 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 881 | T::REGS | 893 | T::REGS |
| 882 | .cr() | 894 | .cr() |
| 883 | .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); | 895 | .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); |
| 884 | 896 | ||
| 885 | let transfer = unsafe { | 897 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. |
| 886 | self.dma | 898 | for chunk in buf.chunks(0xFFFF / W::size().bytes()) { |
| 887 | .as_mut() | 899 | let transfer = unsafe { |
| 888 | .unwrap() | 900 | self.dma |
| 889 | .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) | 901 | .as_mut() |
| 890 | }; | 902 | .unwrap() |
| 903 | .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) | ||
| 904 | }; | ||
| 891 | 905 | ||
| 892 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 906 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 893 | 907 | ||
| 894 | transfer.await; | 908 | transfer.await; |
| 909 | } | ||
| 895 | 910 | ||
| 896 | finish_dma(T::REGS); | 911 | finish_dma(T::REGS); |
| 897 | 912 | ||
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..0aa2d1da9 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs | |||
| @@ -129,7 +129,7 @@ impl<'d> Drop for I2CDropGuard<'d> { | |||
| 129 | x.set_as_disconnected() | 129 | x.set_as_disconnected() |
| 130 | } | 130 | } |
| 131 | 131 | ||
| 132 | self.info.rcc.disable(); | 132 | self.info.rcc.disable_without_stop(); |
| 133 | } | 133 | } |
| 134 | } | 134 | } |
| 135 | 135 | ||
| @@ -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,13 +219,14 @@ 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 |
| 225 | } | 226 | } |
| 226 | 227 | ||
| 227 | fn enable_and_init(&mut self, config: Config) { | 228 | fn enable_and_init(&mut self, config: Config) { |
| 228 | self.info.rcc.enable_and_reset(); | 229 | self.info.rcc.enable_and_reset_without_stop(); |
| 229 | self.init(config); | 230 | self.init(config); |
| 230 | } | 231 | } |
| 231 | } | 232 | } |
| @@ -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..81a6d74c1 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,30 @@ 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 | let _scoped_block_stop = self.info.rcc.block_stop(); |
| 533 | self.write_frame(address, write_buffer, FrameOptions::FirstAndLastFrame) | ||
| 506 | .await?; | 534 | .await?; |
| 507 | 535 | ||
| 508 | Ok(()) | 536 | Ok(()) |
| 509 | } | 537 | } |
| 510 | 538 | ||
| 511 | /// Read. | 539 | /// Read. |
| 512 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { | 540 | pub async fn read(&mut self, address: u8, read_buffer: &mut [u8]) -> Result<(), Error> { |
| 513 | self.read_frame(address, buffer, FrameOptions::FirstAndLastFrame) | 541 | let _scoped_block_stop = self.info.rcc.block_stop(); |
| 542 | self.read_frame(address, read_buffer, FrameOptions::FirstAndLastFrame) | ||
| 514 | .await?; | 543 | .await?; |
| 515 | 544 | ||
| 516 | Ok(()) | 545 | Ok(()) |
| 517 | } | 546 | } |
| 518 | 547 | ||
| 519 | async fn read_frame(&mut self, address: u8, buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error> { | 548 | async fn read_frame(&mut self, address: u8, read_buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error> { |
| 520 | if buffer.is_empty() { | 549 | if read_buffer.is_empty() { |
| 521 | return Err(Error::Overrun); | 550 | return Err(Error::Overrun); |
| 522 | } | 551 | } |
| 523 | 552 | ||
| 524 | // Some branches below depend on whether the buffer contains only a single byte. | 553 | // Some branches below depend on whether the buffer contains only a single byte. |
| 525 | let single_byte = buffer.len() == 1; | 554 | let single_byte = read_buffer.len() == 1; |
| 526 | 555 | ||
| 527 | self.info.regs.cr2().modify(|w| { | 556 | self.info.regs.cr2().modify(|w| { |
| 528 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for | 557 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for |
| @@ -612,7 +641,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 612 | self.info.regs.sr2().read(); | 641 | self.info.regs.sr2().read(); |
| 613 | } else { | 642 | } else { |
| 614 | // Before starting reception of single byte (but without START condition, i.e. in case | 643 | // 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. | 644 | // of merged operations), program NACK to emit at end of this byte. |
| 616 | if frame.send_nack() && single_byte { | 645 | if frame.send_nack() && single_byte { |
| 617 | self.info.regs.cr1().modify(|w| { | 646 | self.info.regs.cr1().modify(|w| { |
| 618 | w.set_ack(false); | 647 | w.set_ack(false); |
| @@ -634,7 +663,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 634 | // from this address from the memory after each RxE event. | 663 | // from this address from the memory after each RxE event. |
| 635 | let src = self.info.regs.dr().as_ptr() as *mut u8; | 664 | let src = self.info.regs.dr().as_ptr() as *mut u8; |
| 636 | 665 | ||
| 637 | self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) | 666 | self.rx_dma.as_mut().unwrap().read(src, read_buffer, Default::default()) |
| 638 | }; | 667 | }; |
| 639 | 668 | ||
| 640 | // Wait for bytes to be received, or an error to occur. | 669 | // Wait for bytes to be received, or an error to occur. |
| @@ -673,15 +702,18 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 673 | } | 702 | } |
| 674 | 703 | ||
| 675 | /// Write, restart, read. | 704 | /// Write, restart, read. |
| 676 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { | 705 | pub async fn write_read(&mut self, address: u8, write_buffer: &[u8], read_buffer: &mut [u8]) -> Result<(), Error> { |
| 706 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 677 | // Check empty read buffer before starting transaction. Otherwise, we would not generate the | 707 | // Check empty read buffer before starting transaction. Otherwise, we would not generate the |
| 678 | // stop condition below. | 708 | // stop condition below. |
| 679 | if read.is_empty() { | 709 | if read_buffer.is_empty() { |
| 680 | return Err(Error::Overrun); | 710 | return Err(Error::Overrun); |
| 681 | } | 711 | } |
| 682 | 712 | ||
| 683 | self.write_frame(address, write, FrameOptions::FirstFrame).await?; | 713 | self.write_frame(address, write_buffer, FrameOptions::FirstFrame) |
| 684 | self.read_frame(address, read, FrameOptions::FirstAndLastFrame).await | 714 | .await?; |
| 715 | self.read_frame(address, read_buffer, FrameOptions::FirstAndLastFrame) | ||
| 716 | .await | ||
| 685 | } | 717 | } |
| 686 | 718 | ||
| 687 | /// Transaction with operations. | 719 | /// Transaction with operations. |
| @@ -689,11 +721,12 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 689 | /// Consecutive operations of same type are merged. See [transaction contract] for details. | 721 | /// Consecutive operations of same type are merged. See [transaction contract] for details. |
| 690 | /// | 722 | /// |
| 691 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | 723 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction |
| 692 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | 724 | pub async fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { |
| 725 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 693 | for (op, frame) in operation_frames(operations)? { | 726 | for (op, frame) in operation_frames(operations)? { |
| 694 | match op { | 727 | match op { |
| 695 | Operation::Read(read) => self.read_frame(addr, read, frame).await?, | 728 | Operation::Read(read_buffer) => self.read_frame(address, read_buffer, frame).await?, |
| 696 | Operation::Write(write) => self.write_frame(addr, write, frame).await?, | 729 | Operation::Write(write_buffer) => self.write_frame(address, write_buffer, frame).await?, |
| 697 | } | 730 | } |
| 698 | } | 731 | } |
| 699 | 732 | ||
| @@ -729,12 +762,959 @@ impl Duty { | |||
| 729 | } | 762 | } |
| 730 | } | 763 | } |
| 731 | 764 | ||
| 765 | /// Result of attempting to send a byte in slave transmitter mode | ||
| 766 | #[derive(Debug, PartialEq)] | ||
| 767 | enum TransmitResult { | ||
| 768 | /// Byte sent and ACKed by master - continue transmission | ||
| 769 | Acknowledged, | ||
| 770 | /// Byte sent but NACKed by master - normal end of read transaction | ||
| 771 | NotAcknowledged, | ||
| 772 | /// STOP condition detected - master terminated transaction | ||
| 773 | Stopped, | ||
| 774 | /// RESTART condition detected - master starting new transaction | ||
| 775 | Restarted, | ||
| 776 | } | ||
| 777 | |||
| 778 | /// Result of attempting to receive a byte in slave receiver mode | ||
| 779 | #[derive(Debug, PartialEq)] | ||
| 780 | enum ReceiveResult { | ||
| 781 | /// Data byte successfully received | ||
| 782 | Data(u8), | ||
| 783 | /// STOP condition detected - end of write transaction | ||
| 784 | Stopped, | ||
| 785 | /// RESTART condition detected - master starting new transaction | ||
| 786 | Restarted, | ||
| 787 | } | ||
| 788 | |||
| 789 | /// Enumeration of slave transaction termination conditions | ||
| 790 | #[derive(Debug, Clone, Copy, PartialEq)] | ||
| 791 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 792 | enum SlaveTermination { | ||
| 793 | /// STOP condition received - normal end of transaction | ||
| 794 | Stop, | ||
| 795 | /// RESTART condition received - master starting new transaction | ||
| 796 | Restart, | ||
| 797 | /// NACK received - normal end of read transaction | ||
| 798 | Nack, | ||
| 799 | } | ||
| 800 | |||
| 801 | impl<'d, M: PeriMode> I2c<'d, M, Master> { | ||
| 802 | /// Configure the I2C driver for slave operations, allowing for the driver to be used as a slave and a master (multimaster) | ||
| 803 | pub fn into_slave_multimaster(mut self, slave_addr_config: SlaveAddrConfig) -> I2c<'d, M, MultiMaster> { | ||
| 804 | let mut slave = I2c { | ||
| 805 | info: self.info, | ||
| 806 | state: self.state, | ||
| 807 | kernel_clock: self.kernel_clock, | ||
| 808 | tx_dma: self.tx_dma.take(), // Use take() to move ownership | ||
| 809 | rx_dma: self.rx_dma.take(), // Use take() to move ownership | ||
| 810 | #[cfg(feature = "time")] | ||
| 811 | timeout: self.timeout, | ||
| 812 | _phantom: PhantomData, | ||
| 813 | _phantom2: PhantomData, | ||
| 814 | _drop_guard: self._drop_guard, // Move the drop guard | ||
| 815 | }; | ||
| 816 | slave.init_slave(slave_addr_config); | ||
| 817 | slave | ||
| 818 | } | ||
| 819 | } | ||
| 820 | |||
| 821 | // Address configuration methods | ||
| 822 | impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | ||
| 823 | /// Initialize slave mode with address configuration | ||
| 824 | pub(crate) fn init_slave(&mut self, config: SlaveAddrConfig) { | ||
| 825 | trace!("I2C slave: initializing with config={:?}", config); | ||
| 826 | |||
| 827 | // Disable peripheral for configuration | ||
| 828 | self.info.regs.cr1().modify(|reg| reg.set_pe(false)); | ||
| 829 | |||
| 830 | // Configure slave addresses | ||
| 831 | self.apply_address_configuration(config); | ||
| 832 | |||
| 833 | // Enable peripheral with slave settings | ||
| 834 | self.info.regs.cr1().modify(|reg| { | ||
| 835 | reg.set_pe(true); | ||
| 836 | reg.set_ack(true); // Enable acknowledgment for slave mode | ||
| 837 | reg.set_nostretch(false); // Allow clock stretching for processing time | ||
| 838 | }); | ||
| 839 | |||
| 840 | trace!("I2C slave: initialization complete"); | ||
| 841 | } | ||
| 842 | |||
| 843 | /// Apply the complete address configuration for slave mode | ||
| 844 | fn apply_address_configuration(&mut self, config: SlaveAddrConfig) { | ||
| 845 | match config.addr { | ||
| 846 | OwnAddresses::OA1(addr) => { | ||
| 847 | self.configure_primary_address(addr); | ||
| 848 | self.disable_secondary_address(); | ||
| 849 | } | ||
| 850 | OwnAddresses::OA2(oa2) => { | ||
| 851 | self.configure_default_primary_address(); | ||
| 852 | self.configure_secondary_address(oa2.addr); // v1 ignores mask | ||
| 853 | } | ||
| 854 | OwnAddresses::Both { oa1, oa2 } => { | ||
| 855 | self.configure_primary_address(oa1); | ||
| 856 | self.configure_secondary_address(oa2.addr); // v1 ignores mask | ||
| 857 | } | ||
| 858 | } | ||
| 859 | |||
| 860 | // Configure general call detection | ||
| 861 | if config.general_call { | ||
| 862 | self.info.regs.cr1().modify(|w| w.set_engc(true)); | ||
| 863 | } | ||
| 864 | } | ||
| 865 | |||
| 866 | /// Configure the primary address (OA1) register | ||
| 867 | fn configure_primary_address(&mut self, addr: Address) { | ||
| 868 | match addr { | ||
| 869 | Address::SevenBit(addr) => { | ||
| 870 | self.info.regs.oar1().write(|reg| { | ||
| 871 | let hw_addr = (addr as u16) << 1; // Address in bits [7:1] | ||
| 872 | reg.set_add(hw_addr); | ||
| 873 | reg.set_addmode(i2c::vals::Addmode::BIT7); | ||
| 874 | }); | ||
| 875 | } | ||
| 876 | Address::TenBit(addr) => { | ||
| 877 | self.info.regs.oar1().write(|reg| { | ||
| 878 | reg.set_add(addr); | ||
| 879 | reg.set_addmode(i2c::vals::Addmode::BIT10); | ||
| 880 | }); | ||
| 881 | } | ||
| 882 | } | ||
| 883 | |||
| 884 | // Set required bit 14 as per reference manual | ||
| 885 | self.info.regs.oar1().modify(|reg| reg.0 |= 1 << 14); | ||
| 886 | } | ||
| 887 | |||
| 888 | /// Configure the secondary address (OA2) register | ||
| 889 | fn configure_secondary_address(&mut self, addr: u8) { | ||
| 890 | self.info.regs.oar2().write(|reg| { | ||
| 891 | reg.set_add2(addr); | ||
| 892 | reg.set_endual(i2c::vals::Endual::DUAL); | ||
| 893 | }); | ||
| 894 | } | ||
| 895 | |||
| 896 | /// Set a default primary address when using OA2-only mode | ||
| 897 | fn configure_default_primary_address(&mut self) { | ||
| 898 | self.info.regs.oar1().write(|reg| { | ||
| 899 | reg.set_add(0); // Reserved address, safe to use | ||
| 900 | reg.set_addmode(i2c::vals::Addmode::BIT7); | ||
| 901 | }); | ||
| 902 | self.info.regs.oar1().modify(|reg| reg.0 |= 1 << 14); | ||
| 903 | } | ||
| 904 | |||
| 905 | /// Disable secondary address when not needed | ||
| 906 | fn disable_secondary_address(&mut self) { | ||
| 907 | self.info.regs.oar2().write(|reg| { | ||
| 908 | reg.set_endual(i2c::vals::Endual::SINGLE); | ||
| 909 | }); | ||
| 910 | } | ||
| 911 | } | ||
| 912 | |||
| 913 | impl<'d, M: PeriMode> I2c<'d, M, MultiMaster> { | ||
| 914 | /// Listen for incoming I2C address match and return the command type | ||
| 915 | /// | ||
| 916 | /// This method blocks until the slave address is matched by a master. | ||
| 917 | /// Returns the command type (Read/Write) and the matched address. | ||
| 918 | pub fn blocking_listen(&mut self) -> Result<SlaveCommand, Error> { | ||
| 919 | trace!("I2C slave: starting blocking listen for address match"); | ||
| 920 | let result = self.blocking_listen_with_timeout(self.timeout()); | ||
| 921 | trace!("I2C slave: blocking listen complete, result={:?}", result); | ||
| 922 | result | ||
| 923 | } | ||
| 924 | |||
| 925 | /// Respond to a master read request by transmitting data | ||
| 926 | /// | ||
| 927 | /// Sends the provided data to the master. If the master requests more bytes | ||
| 928 | /// than available, padding bytes (0x00) are sent until the master terminates | ||
| 929 | /// the transaction with NACK. | ||
| 930 | /// | ||
| 931 | /// Returns the total number of bytes transmitted (including padding). | ||
| 932 | pub fn blocking_respond_to_read(&mut self, data: &[u8]) -> Result<usize, Error> { | ||
| 933 | trace!("I2C slave: starting blocking respond_to_read, data_len={}", data.len()); | ||
| 934 | |||
| 935 | if let Some(zero_length_result) = self.detect_zero_length_read(self.timeout())? { | ||
| 936 | trace!("I2C slave: zero-length read detected"); | ||
| 937 | return Ok(zero_length_result); | ||
| 938 | } | ||
| 939 | |||
| 940 | let result = self.transmit_to_master(data, self.timeout()); | ||
| 941 | trace!("I2C slave: blocking respond_to_read complete, result={:?}", result); | ||
| 942 | result | ||
| 943 | } | ||
| 944 | |||
| 945 | /// Respond to a master write request by receiving data | ||
| 946 | /// | ||
| 947 | /// Receives data from the master into the provided buffer. If the master | ||
| 948 | /// sends more bytes than the buffer can hold, excess bytes are acknowledged | ||
| 949 | /// but discarded. | ||
| 950 | /// | ||
| 951 | /// Returns the number of bytes stored in the buffer (not total received). | ||
| 952 | pub fn blocking_respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { | ||
| 953 | trace!( | ||
| 954 | "I2C slave: starting blocking respond_to_write, buffer_len={}", | ||
| 955 | buffer.len() | ||
| 956 | ); | ||
| 957 | let result = self.receive_from_master(buffer, self.timeout()); | ||
| 958 | trace!("I2C slave: blocking respond_to_write complete, result={:?}", result); | ||
| 959 | result | ||
| 960 | } | ||
| 961 | |||
| 962 | // Private implementation methods | ||
| 963 | |||
| 964 | /// Wait for address match and determine transaction type | ||
| 965 | fn blocking_listen_with_timeout(&mut self, timeout: Timeout) -> Result<SlaveCommand, Error> { | ||
| 966 | // Ensure interrupts are disabled for blocking operation | ||
| 967 | self.disable_i2c_interrupts(); | ||
| 968 | |||
| 969 | // Wait for address match (ADDR flag) | ||
| 970 | loop { | ||
| 971 | let sr1 = Self::read_status_and_handle_errors(self.info)?; | ||
| 972 | |||
| 973 | if sr1.addr() { | ||
| 974 | // Address matched - read SR2 to get direction and clear ADDR flag | ||
| 975 | let sr2 = self.info.regs.sr2().read(); | ||
| 976 | let direction = if sr2.tra() { | ||
| 977 | SlaveCommandKind::Read | ||
| 978 | } else { | ||
| 979 | SlaveCommandKind::Write | ||
| 980 | }; | ||
| 981 | |||
| 982 | // Use the static method instead of the instance method | ||
| 983 | let matched_address = Self::decode_matched_address(sr2, self.info)?; | ||
| 984 | trace!( | ||
| 985 | "I2C slave: address matched, direction={:?}, addr={:?}", | ||
| 986 | direction, matched_address | ||
| 987 | ); | ||
| 988 | |||
| 989 | return Ok(SlaveCommand { | ||
| 990 | kind: direction, | ||
| 991 | address: matched_address, | ||
| 992 | }); | ||
| 993 | } | ||
| 994 | |||
| 995 | timeout.check()?; | ||
| 996 | } | ||
| 997 | } | ||
| 998 | |||
| 999 | /// Transmit data to master in response to read request | ||
| 1000 | fn transmit_to_master(&mut self, data: &[u8], timeout: Timeout) -> Result<usize, Error> { | ||
| 1001 | let mut bytes_transmitted = 0; | ||
| 1002 | let mut padding_count = 0; | ||
| 1003 | |||
| 1004 | loop { | ||
| 1005 | let byte_to_send = if bytes_transmitted < data.len() { | ||
| 1006 | data[bytes_transmitted] | ||
| 1007 | } else { | ||
| 1008 | padding_count += 1; | ||
| 1009 | 0x00 // Send padding bytes when data is exhausted | ||
| 1010 | }; | ||
| 1011 | |||
| 1012 | match self.transmit_byte(byte_to_send, timeout)? { | ||
| 1013 | TransmitResult::Acknowledged => { | ||
| 1014 | bytes_transmitted += 1; | ||
| 1015 | } | ||
| 1016 | TransmitResult::NotAcknowledged => { | ||
| 1017 | bytes_transmitted += 1; // Count the NACKed byte | ||
| 1018 | break; | ||
| 1019 | } | ||
| 1020 | TransmitResult::Stopped | TransmitResult::Restarted => { | ||
| 1021 | break; | ||
| 1022 | } | ||
| 1023 | } | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | if padding_count > 0 { | ||
| 1027 | trace!( | ||
| 1028 | "I2C slave: sent {} data bytes + {} padding bytes = {} total", | ||
| 1029 | data.len(), | ||
| 1030 | padding_count, | ||
| 1031 | bytes_transmitted | ||
| 1032 | ); | ||
| 1033 | } | ||
| 1034 | |||
| 1035 | Ok(bytes_transmitted) | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | /// Receive data from master during write request | ||
| 1039 | fn receive_from_master(&mut self, buffer: &mut [u8], timeout: Timeout) -> Result<usize, Error> { | ||
| 1040 | let mut bytes_stored = 0; | ||
| 1041 | |||
| 1042 | // Receive bytes that fit in buffer | ||
| 1043 | while bytes_stored < buffer.len() { | ||
| 1044 | match self.receive_byte(timeout)? { | ||
| 1045 | ReceiveResult::Data(byte) => { | ||
| 1046 | buffer[bytes_stored] = byte; | ||
| 1047 | bytes_stored += 1; | ||
| 1048 | } | ||
| 1049 | ReceiveResult::Stopped | ReceiveResult::Restarted => { | ||
| 1050 | return Ok(bytes_stored); | ||
| 1051 | } | ||
| 1052 | } | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | // Handle buffer overflow by discarding excess bytes | ||
| 1056 | if bytes_stored == buffer.len() { | ||
| 1057 | trace!("I2C slave: buffer full, discarding excess bytes"); | ||
| 1058 | self.discard_excess_bytes(timeout)?; | ||
| 1059 | } | ||
| 1060 | |||
| 1061 | Ok(bytes_stored) | ||
| 1062 | } | ||
| 1063 | |||
| 1064 | /// Detect zero-length read pattern early | ||
| 1065 | /// | ||
| 1066 | /// Zero-length reads occur when a master sends START+ADDR+R followed immediately | ||
| 1067 | /// by NACK+STOP without wanting any data. This must be detected before attempting | ||
| 1068 | /// to transmit any bytes to avoid SDA line issues. | ||
| 1069 | fn detect_zero_length_read(&mut self, _timeout: Timeout) -> Result<Option<usize>, Error> { | ||
| 1070 | // Quick check for immediate termination signals | ||
| 1071 | let sr1 = self.info.regs.sr1().read(); | ||
| 1072 | |||
| 1073 | // Check for immediate NACK (fastest zero-length pattern) | ||
| 1074 | if sr1.af() { | ||
| 1075 | self.clear_acknowledge_failure(); | ||
| 1076 | return Ok(Some(0)); | ||
| 1077 | } | ||
| 1078 | |||
| 1079 | // Check for immediate STOP (alternative zero-length pattern) | ||
| 1080 | if sr1.stopf() { | ||
| 1081 | Self::clear_stop_flag(self.info); | ||
| 1082 | return Ok(Some(0)); | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | // Give a brief window for master to send termination signals | ||
| 1086 | // This handles masters that have slight delays between address ACK and NACK | ||
| 1087 | const ZERO_LENGTH_DETECTION_CYCLES: u32 = 20; // ~5-10µs window | ||
| 1088 | |||
| 1089 | for _ in 0..ZERO_LENGTH_DETECTION_CYCLES { | ||
| 1090 | let sr1 = self.info.regs.sr1().read(); | ||
| 1091 | |||
| 1092 | // Immediate NACK indicates zero-length read | ||
| 1093 | if sr1.af() { | ||
| 1094 | self.clear_acknowledge_failure(); | ||
| 1095 | return Ok(Some(0)); | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | // Immediate STOP indicates zero-length read | ||
| 1099 | if sr1.stopf() { | ||
| 1100 | Self::clear_stop_flag(self.info); | ||
| 1101 | return Ok(Some(0)); | ||
| 1102 | } | ||
| 1103 | |||
| 1104 | // If TXE becomes ready, master is waiting for data - not zero-length | ||
| 1105 | if sr1.txe() { | ||
| 1106 | return Ok(None); // Proceed with normal transmission | ||
| 1107 | } | ||
| 1108 | |||
| 1109 | // If RESTART detected, handle as zero-length | ||
| 1110 | if sr1.addr() { | ||
| 1111 | return Ok(Some(0)); | ||
| 1112 | } | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | // No zero-length pattern detected within the window | ||
| 1116 | Ok(None) | ||
| 1117 | } | ||
| 1118 | |||
| 1119 | /// Discard excess bytes when buffer is full | ||
| 1120 | fn discard_excess_bytes(&mut self, timeout: Timeout) -> Result<(), Error> { | ||
| 1121 | let mut discarded_count = 0; | ||
| 1122 | |||
| 1123 | loop { | ||
| 1124 | match self.receive_byte(timeout)? { | ||
| 1125 | ReceiveResult::Data(_) => { | ||
| 1126 | discarded_count += 1; | ||
| 1127 | continue; | ||
| 1128 | } | ||
| 1129 | ReceiveResult::Stopped | ReceiveResult::Restarted => { | ||
| 1130 | if discarded_count > 0 { | ||
| 1131 | trace!("I2C slave: discarded {} excess bytes", discarded_count); | ||
| 1132 | } | ||
| 1133 | break; | ||
| 1134 | } | ||
| 1135 | } | ||
| 1136 | } | ||
| 1137 | Ok(()) | ||
| 1138 | } | ||
| 1139 | |||
| 1140 | /// Send a single byte and wait for master's response | ||
| 1141 | fn transmit_byte(&mut self, byte: u8, timeout: Timeout) -> Result<TransmitResult, Error> { | ||
| 1142 | // Wait for transmit buffer ready | ||
| 1143 | self.wait_for_transmit_ready(timeout)?; | ||
| 1144 | |||
| 1145 | // Send the byte | ||
| 1146 | self.info.regs.dr().write(|w| w.set_dr(byte)); | ||
| 1147 | |||
| 1148 | // Wait for transmission completion or master response | ||
| 1149 | self.wait_for_transmit_completion(timeout) | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | /// Wait until transmit buffer is ready (TXE flag set) | ||
| 1153 | fn wait_for_transmit_ready(&mut self, timeout: Timeout) -> Result<(), Error> { | ||
| 1154 | loop { | ||
| 1155 | let sr1 = Self::read_status_and_handle_errors(self.info)?; | ||
| 1156 | |||
| 1157 | // Check for early termination conditions | ||
| 1158 | if let Some(result) = Self::check_early_termination(sr1) { | ||
| 1159 | return Err(self.handle_early_termination(result)); | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | if sr1.txe() { | ||
| 1163 | return Ok(()); // Ready to transmit | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | timeout.check()?; | ||
| 1167 | } | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | /// Wait for byte transmission completion or master response | ||
| 1171 | fn wait_for_transmit_completion(&mut self, timeout: Timeout) -> Result<TransmitResult, Error> { | ||
| 1172 | loop { | ||
| 1173 | let sr1 = self.info.regs.sr1().read(); | ||
| 1174 | |||
| 1175 | // Check flags in priority order | ||
| 1176 | if sr1.af() { | ||
| 1177 | self.clear_acknowledge_failure(); | ||
| 1178 | return Ok(TransmitResult::NotAcknowledged); | ||
| 1179 | } | ||
| 1180 | |||
| 1181 | if sr1.btf() { | ||
| 1182 | return Ok(TransmitResult::Acknowledged); | ||
| 1183 | } | ||
| 1184 | |||
| 1185 | if sr1.stopf() { | ||
| 1186 | Self::clear_stop_flag(self.info); | ||
| 1187 | return Ok(TransmitResult::Stopped); | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | if sr1.addr() { | ||
| 1191 | return Ok(TransmitResult::Restarted); | ||
| 1192 | } | ||
| 1193 | |||
| 1194 | // Check for other error conditions | ||
| 1195 | self.check_for_hardware_errors(sr1)?; | ||
| 1196 | |||
| 1197 | timeout.check()?; | ||
| 1198 | } | ||
| 1199 | } | ||
| 1200 | |||
| 1201 | /// Receive a single byte or detect transaction termination | ||
| 1202 | fn receive_byte(&mut self, timeout: Timeout) -> Result<ReceiveResult, Error> { | ||
| 1203 | loop { | ||
| 1204 | let sr1 = Self::read_status_and_handle_errors(self.info)?; | ||
| 1205 | |||
| 1206 | // Check for received data first (prioritize data over control signals) | ||
| 1207 | if sr1.rxne() { | ||
| 1208 | let byte = self.info.regs.dr().read().dr(); | ||
| 1209 | return Ok(ReceiveResult::Data(byte)); | ||
| 1210 | } | ||
| 1211 | |||
| 1212 | // Check for transaction termination | ||
| 1213 | if sr1.addr() { | ||
| 1214 | return Ok(ReceiveResult::Restarted); | ||
| 1215 | } | ||
| 1216 | |||
| 1217 | if sr1.stopf() { | ||
| 1218 | Self::clear_stop_flag(self.info); | ||
| 1219 | return Ok(ReceiveResult::Stopped); | ||
| 1220 | } | ||
| 1221 | |||
| 1222 | timeout.check()?; | ||
| 1223 | } | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | /// Determine which slave address was matched based on SR2 flags | ||
| 1227 | fn decode_matched_address(sr2: i2c::regs::Sr2, info: &'static Info) -> Result<Address, Error> { | ||
| 1228 | if sr2.gencall() { | ||
| 1229 | Ok(Address::SevenBit(0x00)) // General call address | ||
| 1230 | } else if sr2.dualf() { | ||
| 1231 | // OA2 (secondary address) was matched | ||
| 1232 | let oar2 = info.regs.oar2().read(); | ||
| 1233 | if oar2.endual() != i2c::vals::Endual::DUAL { | ||
| 1234 | return Err(Error::Bus); // Hardware inconsistency | ||
| 1235 | } | ||
| 1236 | Ok(Address::SevenBit(oar2.add2())) | ||
| 1237 | } else { | ||
| 1238 | // OA1 (primary address) was matched | ||
| 1239 | let oar1 = info.regs.oar1().read(); | ||
| 1240 | match oar1.addmode() { | ||
| 1241 | i2c::vals::Addmode::BIT7 => { | ||
| 1242 | let addr = (oar1.add() >> 1) as u8; | ||
| 1243 | Ok(Address::SevenBit(addr)) | ||
| 1244 | } | ||
| 1245 | i2c::vals::Addmode::BIT10 => Ok(Address::TenBit(oar1.add())), | ||
| 1246 | } | ||
| 1247 | } | ||
| 1248 | } | ||
| 1249 | |||
| 1250 | // Helper methods for hardware interaction | ||
| 1251 | |||
| 1252 | /// Read status register and handle I2C errors (except NACK in slave mode) | ||
| 1253 | fn read_status_and_handle_errors(info: &'static Info) -> Result<i2c::regs::Sr1, Error> { | ||
| 1254 | match Self::check_and_clear_error_flags(info) { | ||
| 1255 | Ok(sr1) => Ok(sr1), | ||
| 1256 | Err(Error::Nack) => { | ||
| 1257 | // In slave mode, NACK is normal protocol behavior, not an error | ||
| 1258 | Ok(info.regs.sr1().read()) | ||
| 1259 | } | ||
| 1260 | Err(other_error) => Err(other_error), | ||
| 1261 | } | ||
| 1262 | } | ||
| 1263 | |||
| 1264 | /// Check for conditions that cause early termination of operations | ||
| 1265 | fn check_early_termination(sr1: i2c::regs::Sr1) -> Option<TransmitResult> { | ||
| 1266 | if sr1.stopf() { | ||
| 1267 | Some(TransmitResult::Stopped) | ||
| 1268 | } else if sr1.addr() { | ||
| 1269 | Some(TransmitResult::Restarted) | ||
| 1270 | } else if sr1.af() { | ||
| 1271 | Some(TransmitResult::NotAcknowledged) | ||
| 1272 | } else { | ||
| 1273 | None | ||
| 1274 | } | ||
| 1275 | } | ||
| 1276 | |||
| 1277 | /// Convert early termination to appropriate error | ||
| 1278 | fn handle_early_termination(&mut self, result: TransmitResult) -> Error { | ||
| 1279 | match result { | ||
| 1280 | TransmitResult::Stopped => { | ||
| 1281 | Self::clear_stop_flag(self.info); | ||
| 1282 | Error::Bus // Unexpected STOP during setup | ||
| 1283 | } | ||
| 1284 | TransmitResult::Restarted => { | ||
| 1285 | Error::Bus // Unexpected RESTART during setup | ||
| 1286 | } | ||
| 1287 | TransmitResult::NotAcknowledged => { | ||
| 1288 | self.clear_acknowledge_failure(); | ||
| 1289 | Error::Bus // Unexpected NACK during setup | ||
| 1290 | } | ||
| 1291 | TransmitResult::Acknowledged => { | ||
| 1292 | unreachable!() // This should never be passed to this function | ||
| 1293 | } | ||
| 1294 | } | ||
| 1295 | } | ||
| 1296 | |||
| 1297 | /// Check for hardware-level I2C errors during transmission | ||
| 1298 | fn check_for_hardware_errors(&self, sr1: i2c::regs::Sr1) -> Result<(), Error> { | ||
| 1299 | if sr1.timeout() || sr1.ovr() || sr1.arlo() || sr1.berr() { | ||
| 1300 | // Delegate to existing error handling | ||
| 1301 | Self::check_and_clear_error_flags(self.info)?; | ||
| 1302 | } | ||
| 1303 | Ok(()) | ||
| 1304 | } | ||
| 1305 | |||
| 1306 | /// Disable I2C event and error interrupts for blocking operations | ||
| 1307 | fn disable_i2c_interrupts(&mut self) { | ||
| 1308 | self.info.regs.cr2().modify(|w| { | ||
| 1309 | w.set_itevten(false); | ||
| 1310 | w.set_iterren(false); | ||
| 1311 | }); | ||
| 1312 | } | ||
| 1313 | |||
| 1314 | /// Clear the acknowledge failure flag | ||
| 1315 | fn clear_acknowledge_failure(&mut self) { | ||
| 1316 | self.info.regs.sr1().write(|reg| { | ||
| 1317 | reg.0 = !0; | ||
| 1318 | reg.set_af(false); | ||
| 1319 | }); | ||
| 1320 | } | ||
| 1321 | |||
| 1322 | /// Configure DMA settings for slave operations (shared between read/write) | ||
| 1323 | fn setup_slave_dma_base(&mut self) { | ||
| 1324 | self.info.regs.cr2().modify(|w| { | ||
| 1325 | w.set_itbufen(false); // Always disable buffer interrupts when using DMA | ||
| 1326 | w.set_dmaen(true); // Enable DMA requests | ||
| 1327 | w.set_last(false); // LAST bit not used in slave mode for v1 hardware | ||
| 1328 | }); | ||
| 1329 | } | ||
| 1330 | |||
| 1331 | /// Disable DMA and interrupts in a critical section | ||
| 1332 | fn disable_dma_and_interrupts(info: &'static Info) { | ||
| 1333 | critical_section::with(|_| { | ||
| 1334 | info.regs.cr2().modify(|w| { | ||
| 1335 | w.set_dmaen(false); | ||
| 1336 | w.set_iterren(false); | ||
| 1337 | w.set_itevten(false); | ||
| 1338 | }); | ||
| 1339 | }); | ||
| 1340 | } | ||
| 1341 | |||
| 1342 | /// Check for early termination conditions during slave operations | ||
| 1343 | /// Returns Some(result) if termination detected, None to continue | ||
| 1344 | fn check_slave_termination_conditions(sr1: i2c::regs::Sr1) -> Option<SlaveTermination> { | ||
| 1345 | if sr1.stopf() { | ||
| 1346 | Some(SlaveTermination::Stop) | ||
| 1347 | } else if sr1.addr() { | ||
| 1348 | Some(SlaveTermination::Restart) | ||
| 1349 | } else if sr1.af() { | ||
| 1350 | Some(SlaveTermination::Nack) | ||
| 1351 | } else { | ||
| 1352 | None | ||
| 1353 | } | ||
| 1354 | } | ||
| 1355 | } | ||
| 1356 | |||
| 1357 | impl<'d> I2c<'d, Async, MultiMaster> { | ||
| 1358 | /// Async listen for incoming I2C messages using interrupts | ||
| 1359 | /// | ||
| 1360 | /// Waits for a master to address this slave and returns the command type | ||
| 1361 | /// (Read/Write) and the matched address. This method will suspend until | ||
| 1362 | /// an address match occurs. | ||
| 1363 | pub async fn listen(&mut self) -> Result<SlaveCommand, Error> { | ||
| 1364 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 1365 | trace!("I2C slave: starting async listen for address match"); | ||
| 1366 | let state = self.state; | ||
| 1367 | let info = self.info; | ||
| 1368 | |||
| 1369 | Self::enable_interrupts(info); | ||
| 1370 | |||
| 1371 | let on_drop = OnDrop::new(|| { | ||
| 1372 | Self::disable_dma_and_interrupts(info); | ||
| 1373 | }); | ||
| 1374 | |||
| 1375 | let result = poll_fn(|cx| { | ||
| 1376 | state.waker.register(cx.waker()); | ||
| 1377 | |||
| 1378 | match Self::check_and_clear_error_flags(info) { | ||
| 1379 | Err(e) => { | ||
| 1380 | error!("I2C slave: error during listen: {:?}", e); | ||
| 1381 | Poll::Ready(Err(e)) | ||
| 1382 | } | ||
| 1383 | Ok(sr1) => { | ||
| 1384 | if sr1.addr() { | ||
| 1385 | let sr2 = info.regs.sr2().read(); | ||
| 1386 | let direction = if sr2.tra() { | ||
| 1387 | SlaveCommandKind::Read | ||
| 1388 | } else { | ||
| 1389 | SlaveCommandKind::Write | ||
| 1390 | }; | ||
| 1391 | |||
| 1392 | let matched_address = match Self::decode_matched_address(sr2, info) { | ||
| 1393 | Ok(addr) => { | ||
| 1394 | trace!("I2C slave: address matched, direction={:?}, addr={:?}", direction, addr); | ||
| 1395 | addr | ||
| 1396 | } | ||
| 1397 | Err(e) => { | ||
| 1398 | error!("I2C slave: failed to decode matched address: {:?}", e); | ||
| 1399 | return Poll::Ready(Err(e)); | ||
| 1400 | } | ||
| 1401 | }; | ||
| 1402 | |||
| 1403 | Poll::Ready(Ok(SlaveCommand { | ||
| 1404 | kind: direction, | ||
| 1405 | address: matched_address, | ||
| 1406 | })) | ||
| 1407 | } else { | ||
| 1408 | Self::enable_interrupts(info); | ||
| 1409 | Poll::Pending | ||
| 1410 | } | ||
| 1411 | } | ||
| 1412 | } | ||
| 1413 | }) | ||
| 1414 | .await; | ||
| 1415 | |||
| 1416 | drop(on_drop); | ||
| 1417 | trace!("I2C slave: listen complete, result={:?}", result); | ||
| 1418 | result | ||
| 1419 | } | ||
| 1420 | |||
| 1421 | /// Async respond to write command using RX DMA | ||
| 1422 | /// | ||
| 1423 | /// Receives data from the master into the provided buffer using DMA. | ||
| 1424 | /// If the master sends more bytes than the buffer can hold, excess bytes | ||
| 1425 | /// are acknowledged but discarded to prevent interrupt flooding. | ||
| 1426 | /// | ||
| 1427 | /// Returns the number of bytes stored in the buffer (not total received). | ||
| 1428 | pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { | ||
| 1429 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 1430 | trace!("I2C slave: starting respond_to_write, buffer_len={}", buffer.len()); | ||
| 1431 | |||
| 1432 | if buffer.is_empty() { | ||
| 1433 | warn!("I2C slave: respond_to_write called with empty buffer"); | ||
| 1434 | return Err(Error::Overrun); | ||
| 1435 | } | ||
| 1436 | |||
| 1437 | let state = self.state; | ||
| 1438 | let info = self.info; | ||
| 1439 | |||
| 1440 | self.setup_slave_dma_base(); | ||
| 1441 | |||
| 1442 | let on_drop = OnDrop::new(|| { | ||
| 1443 | Self::disable_dma_and_interrupts(info); | ||
| 1444 | }); | ||
| 1445 | |||
| 1446 | info.regs.sr2().read(); | ||
| 1447 | |||
| 1448 | let result = self.execute_slave_receive_transfer(buffer, state, info).await; | ||
| 1449 | |||
| 1450 | drop(on_drop); | ||
| 1451 | trace!("I2C slave: respond_to_write complete, result={:?}", result); | ||
| 1452 | result | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | /// Async respond to read command using TX DMA | ||
| 1456 | /// | ||
| 1457 | /// Transmits data to the master using DMA. If the master requests more bytes | ||
| 1458 | /// than available in the data buffer, padding bytes (0x00) are sent until | ||
| 1459 | /// the master terminates the transaction with NACK, STOP, or RESTART. | ||
| 1460 | /// | ||
| 1461 | /// Returns the total number of bytes transmitted (data + padding). | ||
| 1462 | pub async fn respond_to_read(&mut self, data: &[u8]) -> Result<usize, Error> { | ||
| 1463 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 1464 | trace!("I2C slave: starting respond_to_read, data_len={}", data.len()); | ||
| 1465 | |||
| 1466 | if data.is_empty() { | ||
| 1467 | warn!("I2C slave: respond_to_read called with empty data"); | ||
| 1468 | return Err(Error::Overrun); | ||
| 1469 | } | ||
| 1470 | |||
| 1471 | let state = self.state; | ||
| 1472 | let info = self.info; | ||
| 1473 | |||
| 1474 | self.setup_slave_dma_base(); | ||
| 1475 | |||
| 1476 | let on_drop = OnDrop::new(|| { | ||
| 1477 | Self::disable_dma_and_interrupts(info); | ||
| 1478 | }); | ||
| 1479 | |||
| 1480 | info.regs.sr2().read(); | ||
| 1481 | |||
| 1482 | let result = self.execute_slave_transmit_transfer(data, state, info).await; | ||
| 1483 | |||
| 1484 | drop(on_drop); | ||
| 1485 | trace!("I2C slave: respond_to_read complete, result={:?}", result); | ||
| 1486 | result | ||
| 1487 | } | ||
| 1488 | |||
| 1489 | // === Private Transfer Execution Methods === | ||
| 1490 | |||
| 1491 | /// Execute complete slave receive transfer with excess byte handling | ||
| 1492 | async fn execute_slave_receive_transfer( | ||
| 1493 | &mut self, | ||
| 1494 | buffer: &mut [u8], | ||
| 1495 | state: &'static State, | ||
| 1496 | info: &'static Info, | ||
| 1497 | ) -> Result<usize, Error> { | ||
| 1498 | let dma_transfer = unsafe { | ||
| 1499 | let src = info.regs.dr().as_ptr() as *mut u8; | ||
| 1500 | self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) | ||
| 1501 | }; | ||
| 1502 | |||
| 1503 | let i2c_monitor = | ||
| 1504 | Self::create_termination_monitor(state, info, &[SlaveTermination::Stop, SlaveTermination::Restart]); | ||
| 1505 | |||
| 1506 | match select(dma_transfer, i2c_monitor).await { | ||
| 1507 | Either::Second(Err(e)) => { | ||
| 1508 | error!("I2C slave: error during receive transfer: {:?}", e); | ||
| 1509 | Self::disable_dma_and_interrupts(info); | ||
| 1510 | Err(e) | ||
| 1511 | } | ||
| 1512 | Either::First(_) => { | ||
| 1513 | trace!("I2C slave: DMA receive completed, handling excess bytes"); | ||
| 1514 | Self::disable_dma_and_interrupts(info); | ||
| 1515 | self.handle_excess_bytes(state, info).await?; | ||
| 1516 | Ok(buffer.len()) | ||
| 1517 | } | ||
| 1518 | Either::Second(Ok(termination)) => { | ||
| 1519 | trace!("I2C slave: receive terminated by I2C event: {:?}", termination); | ||
| 1520 | Self::disable_dma_and_interrupts(info); | ||
| 1521 | Ok(buffer.len()) | ||
| 1522 | } | ||
| 1523 | } | ||
| 1524 | } | ||
| 1525 | |||
| 1526 | /// Execute complete slave transmit transfer with padding byte handling | ||
| 1527 | async fn execute_slave_transmit_transfer( | ||
| 1528 | &mut self, | ||
| 1529 | data: &[u8], | ||
| 1530 | state: &'static State, | ||
| 1531 | info: &'static Info, | ||
| 1532 | ) -> Result<usize, Error> { | ||
| 1533 | let dma_transfer = unsafe { | ||
| 1534 | let dst = info.regs.dr().as_ptr() as *mut u8; | ||
| 1535 | self.tx_dma.as_mut().unwrap().write(data, dst, Default::default()) | ||
| 1536 | }; | ||
| 1537 | |||
| 1538 | let i2c_monitor = Self::create_termination_monitor( | ||
| 1539 | state, | ||
| 1540 | info, | ||
| 1541 | &[ | ||
| 1542 | SlaveTermination::Stop, | ||
| 1543 | SlaveTermination::Restart, | ||
| 1544 | SlaveTermination::Nack, | ||
| 1545 | ], | ||
| 1546 | ); | ||
| 1547 | |||
| 1548 | match select(dma_transfer, i2c_monitor).await { | ||
| 1549 | Either::Second(Err(e)) => { | ||
| 1550 | error!("I2C slave: error during transmit transfer: {:?}", e); | ||
| 1551 | Self::disable_dma_and_interrupts(info); | ||
| 1552 | Err(e) | ||
| 1553 | } | ||
| 1554 | Either::First(_) => { | ||
| 1555 | trace!("I2C slave: DMA transmit completed, handling padding bytes"); | ||
| 1556 | Self::disable_dma_and_interrupts(info); | ||
| 1557 | let padding_count = self.handle_padding_bytes(state, info).await?; | ||
| 1558 | let total_bytes = data.len() + padding_count; | ||
| 1559 | trace!( | ||
| 1560 | "I2C slave: sent {} data bytes + {} padding bytes = {} total", | ||
| 1561 | data.len(), | ||
| 1562 | padding_count, | ||
| 1563 | total_bytes | ||
| 1564 | ); | ||
| 1565 | Ok(total_bytes) | ||
| 1566 | } | ||
| 1567 | Either::Second(Ok(termination)) => { | ||
| 1568 | trace!("I2C slave: transmit terminated by I2C event: {:?}", termination); | ||
| 1569 | Self::disable_dma_and_interrupts(info); | ||
| 1570 | Ok(data.len()) | ||
| 1571 | } | ||
| 1572 | } | ||
| 1573 | } | ||
| 1574 | |||
| 1575 | /// Create a future that monitors for specific slave termination conditions | ||
| 1576 | fn create_termination_monitor( | ||
| 1577 | state: &'static State, | ||
| 1578 | info: &'static Info, | ||
| 1579 | allowed_terminations: &'static [SlaveTermination], | ||
| 1580 | ) -> impl Future<Output = Result<SlaveTermination, Error>> { | ||
| 1581 | poll_fn(move |cx| { | ||
| 1582 | state.waker.register(cx.waker()); | ||
| 1583 | |||
| 1584 | match Self::check_and_clear_error_flags(info) { | ||
| 1585 | Err(Error::Nack) if allowed_terminations.contains(&SlaveTermination::Nack) => { | ||
| 1586 | Poll::Ready(Ok(SlaveTermination::Nack)) | ||
| 1587 | } | ||
| 1588 | Err(e) => Poll::Ready(Err(e)), | ||
| 1589 | Ok(sr1) => { | ||
| 1590 | if let Some(termination) = Self::check_slave_termination_conditions(sr1) { | ||
| 1591 | if allowed_terminations.contains(&termination) { | ||
| 1592 | // Handle the specific termination condition | ||
| 1593 | match termination { | ||
| 1594 | SlaveTermination::Stop => Self::clear_stop_flag(info), | ||
| 1595 | SlaveTermination::Nack => { | ||
| 1596 | info.regs.sr1().write(|reg| { | ||
| 1597 | reg.0 = !0; | ||
| 1598 | reg.set_af(false); | ||
| 1599 | }); | ||
| 1600 | } | ||
| 1601 | SlaveTermination::Restart => { | ||
| 1602 | // ADDR flag will be handled by next listen() call | ||
| 1603 | } | ||
| 1604 | } | ||
| 1605 | Poll::Ready(Ok(termination)) | ||
| 1606 | } else { | ||
| 1607 | // Unexpected termination condition | ||
| 1608 | Poll::Ready(Err(Error::Bus)) | ||
| 1609 | } | ||
| 1610 | } else { | ||
| 1611 | Self::enable_interrupts(info); | ||
| 1612 | Poll::Pending | ||
| 1613 | } | ||
| 1614 | } | ||
| 1615 | } | ||
| 1616 | }) | ||
| 1617 | } | ||
| 1618 | |||
| 1619 | /// Handle excess bytes after DMA buffer is full | ||
| 1620 | /// | ||
| 1621 | /// Reads and discards bytes until transaction termination to prevent interrupt flooding | ||
| 1622 | async fn handle_excess_bytes(&mut self, state: &'static State, info: &'static Info) -> Result<(), Error> { | ||
| 1623 | let mut discarded_count = 0; | ||
| 1624 | |||
| 1625 | poll_fn(|cx| { | ||
| 1626 | state.waker.register(cx.waker()); | ||
| 1627 | |||
| 1628 | match Self::check_and_clear_error_flags(info) { | ||
| 1629 | Err(e) => { | ||
| 1630 | error!("I2C slave: error while discarding excess bytes: {:?}", e); | ||
| 1631 | Poll::Ready(Err(e)) | ||
| 1632 | } | ||
| 1633 | Ok(sr1) => { | ||
| 1634 | if let Some(termination) = Self::check_slave_termination_conditions(sr1) { | ||
| 1635 | match termination { | ||
| 1636 | SlaveTermination::Stop => Self::clear_stop_flag(info), | ||
| 1637 | SlaveTermination::Restart => {} | ||
| 1638 | SlaveTermination::Nack => unreachable!("NACK not expected during receive"), | ||
| 1639 | } | ||
| 1640 | if discarded_count > 0 { | ||
| 1641 | trace!("I2C slave: discarded {} excess bytes", discarded_count); | ||
| 1642 | } | ||
| 1643 | return Poll::Ready(Ok(())); | ||
| 1644 | } | ||
| 1645 | |||
| 1646 | if sr1.rxne() { | ||
| 1647 | let _discarded_byte = info.regs.dr().read().dr(); | ||
| 1648 | discarded_count += 1; | ||
| 1649 | Self::enable_interrupts(info); | ||
| 1650 | return Poll::Pending; | ||
| 1651 | } | ||
| 1652 | |||
| 1653 | Self::enable_interrupts(info); | ||
| 1654 | Poll::Pending | ||
| 1655 | } | ||
| 1656 | } | ||
| 1657 | }) | ||
| 1658 | .await | ||
| 1659 | } | ||
| 1660 | |||
| 1661 | /// Handle padding bytes after DMA data is exhausted | ||
| 1662 | /// | ||
| 1663 | /// Sends 0x00 bytes until transaction termination to prevent interrupt flooding | ||
| 1664 | async fn handle_padding_bytes(&mut self, state: &'static State, info: &'static Info) -> Result<usize, Error> { | ||
| 1665 | let mut padding_count = 0; | ||
| 1666 | |||
| 1667 | poll_fn(|cx| { | ||
| 1668 | state.waker.register(cx.waker()); | ||
| 1669 | |||
| 1670 | match Self::check_and_clear_error_flags(info) { | ||
| 1671 | Err(Error::Nack) => Poll::Ready(Ok(padding_count)), | ||
| 1672 | Err(e) => { | ||
| 1673 | error!("I2C slave: error while sending padding bytes: {:?}", e); | ||
| 1674 | Poll::Ready(Err(e)) | ||
| 1675 | } | ||
| 1676 | Ok(sr1) => { | ||
| 1677 | if let Some(termination) = Self::check_slave_termination_conditions(sr1) { | ||
| 1678 | match termination { | ||
| 1679 | SlaveTermination::Stop => Self::clear_stop_flag(info), | ||
| 1680 | SlaveTermination::Restart => {} | ||
| 1681 | SlaveTermination::Nack => { | ||
| 1682 | info.regs.sr1().write(|reg| { | ||
| 1683 | reg.0 = !0; | ||
| 1684 | reg.set_af(false); | ||
| 1685 | }); | ||
| 1686 | } | ||
| 1687 | } | ||
| 1688 | return Poll::Ready(Ok(padding_count)); | ||
| 1689 | } | ||
| 1690 | |||
| 1691 | if sr1.txe() { | ||
| 1692 | info.regs.dr().write(|w| w.set_dr(0x00)); | ||
| 1693 | padding_count += 1; | ||
| 1694 | Self::enable_interrupts(info); | ||
| 1695 | return Poll::Pending; | ||
| 1696 | } | ||
| 1697 | |||
| 1698 | Self::enable_interrupts(info); | ||
| 1699 | Poll::Pending | ||
| 1700 | } | ||
| 1701 | } | ||
| 1702 | }) | ||
| 1703 | .await | ||
| 1704 | } | ||
| 1705 | } | ||
| 1706 | |||
| 1707 | /// Timing configuration for I2C v1 hardware | ||
| 1708 | /// | ||
| 1709 | /// This struct encapsulates the complex timing calculations required for STM32 I2C v1 | ||
| 1710 | /// peripherals, which use three separate registers (CR2.FREQ, CCR, TRISE) instead of | ||
| 1711 | /// the unified TIMINGR register found in v2 hardware. | ||
| 732 | struct Timings { | 1712 | struct Timings { |
| 733 | freq: u8, | 1713 | freq: u8, // APB frequency in MHz for CR2.FREQ register |
| 734 | mode: Mode, | 1714 | mode: Mode, // Standard or Fast mode selection |
| 735 | trise: u8, | 1715 | trise: u8, // Rise time compensation value |
| 736 | ccr: u16, | 1716 | ccr: u16, // Clock control register value |
| 737 | duty: Duty, | 1717 | duty: Duty, // Fast mode duty cycle selection |
| 738 | } | 1718 | } |
| 739 | 1719 | ||
| 740 | impl Timings { | 1720 | impl Timings { |
| @@ -762,11 +1742,7 @@ impl Timings { | |||
| 762 | mode = Mode::Standard; | 1742 | mode = Mode::Standard; |
| 763 | ccr = { | 1743 | ccr = { |
| 764 | let ccr = clock / (frequency * 2); | 1744 | let ccr = clock / (frequency * 2); |
| 765 | if ccr < 4 { | 1745 | if ccr < 4 { 4 } else { ccr } |
| 766 | 4 | ||
| 767 | } else { | ||
| 768 | ccr | ||
| 769 | } | ||
| 770 | }; | 1746 | }; |
| 771 | } else { | 1747 | } else { |
| 772 | const DUTYCYCLE: u8 = 0; | 1748 | const DUTYCYCLE: u8 = 0; |
| @@ -775,14 +1751,10 @@ impl Timings { | |||
| 775 | duty = Duty::Duty2_1; | 1751 | duty = Duty::Duty2_1; |
| 776 | ccr = clock / (frequency * 3); | 1752 | ccr = clock / (frequency * 3); |
| 777 | ccr = if ccr < 1 { 1 } else { ccr }; | 1753 | 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 { | 1754 | } else { |
| 781 | duty = Duty::Duty16_9; | 1755 | duty = Duty::Duty16_9; |
| 782 | ccr = clock / (frequency * 25); | 1756 | ccr = clock / (frequency * 25); |
| 783 | ccr = if ccr < 1 { 1 } else { ccr }; | 1757 | 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 | } | 1758 | } |
| 787 | } | 1759 | } |
| 788 | 1760 | ||
| @@ -792,11 +1764,6 @@ impl Timings { | |||
| 792 | ccr: ccr as u16, | 1764 | ccr: ccr as u16, |
| 793 | duty, | 1765 | duty, |
| 794 | mode, | 1766 | mode, |
| 795 | //prescale: presc_reg, | ||
| 796 | //scll, | ||
| 797 | //sclh, | ||
| 798 | //sdadel, | ||
| 799 | //scldel, | ||
| 800 | } | 1767 | } |
| 801 | } | 1768 | } |
| 802 | } | 1769 | } |
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 0bfc795ac..fe7782a7c 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_or_event(); | ||
| 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 | ||
| @@ -93,6 +98,27 @@ pub(crate) unsafe fn on_interrupt<T: Instance>() { | |||
| 93 | } | 98 | } |
| 94 | 99 | ||
| 95 | impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | 100 | impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { |
| 101 | #[inline] | ||
| 102 | fn to_reload(reload: bool) -> i2c::vals::Reload { | ||
| 103 | if reload { | ||
| 104 | i2c::vals::Reload::NOT_COMPLETED | ||
| 105 | } else { | ||
| 106 | i2c::vals::Reload::COMPLETED | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | /// Calculate total bytes in a group of operations | ||
| 111 | #[inline] | ||
| 112 | fn total_operation_bytes(operations: &[Operation<'_>]) -> usize { | ||
| 113 | operations | ||
| 114 | .iter() | ||
| 115 | .map(|op| match op { | ||
| 116 | Operation::Write(buf) => buf.len(), | ||
| 117 | Operation::Read(buf) => buf.len(), | ||
| 118 | }) | ||
| 119 | .sum() | ||
| 120 | } | ||
| 121 | |||
| 96 | pub(crate) fn init(&mut self, config: Config) { | 122 | pub(crate) fn init(&mut self, config: Config) { |
| 97 | self.info.regs.cr1().modify(|reg| { | 123 | self.info.regs.cr1().modify(|reg| { |
| 98 | reg.set_pe(false); | 124 | reg.set_pe(false); |
| @@ -142,12 +168,6 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 142 | // `buffer`. The START bit can be set even if the bus | 168 | // `buffer`. The START bit can be set even if the bus |
| 143 | // is BUSY or I2C is in slave mode. | 169 | // is BUSY or I2C is in slave mode. |
| 144 | 170 | ||
| 145 | let reload = if reload { | ||
| 146 | i2c::vals::Reload::NOT_COMPLETED | ||
| 147 | } else { | ||
| 148 | i2c::vals::Reload::COMPLETED | ||
| 149 | }; | ||
| 150 | |||
| 151 | info.regs.cr2().modify(|w| { | 171 | info.regs.cr2().modify(|w| { |
| 152 | w.set_sadd(address.addr() << 1); | 172 | w.set_sadd(address.addr() << 1); |
| 153 | w.set_add10(address.add_mode()); | 173 | w.set_add10(address.add_mode()); |
| @@ -155,7 +175,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 155 | w.set_nbytes(length as u8); | 175 | w.set_nbytes(length as u8); |
| 156 | w.set_start(true); | 176 | w.set_start(true); |
| 157 | w.set_autoend(stop.autoend()); | 177 | w.set_autoend(stop.autoend()); |
| 158 | w.set_reload(reload); | 178 | w.set_reload(Self::to_reload(reload)); |
| 159 | }); | 179 | }); |
| 160 | 180 | ||
| 161 | Ok(()) | 181 | Ok(()) |
| @@ -167,28 +187,25 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 167 | length: usize, | 187 | length: usize, |
| 168 | stop: Stop, | 188 | stop: Stop, |
| 169 | reload: bool, | 189 | reload: bool, |
| 190 | restart: bool, | ||
| 170 | timeout: Timeout, | 191 | timeout: Timeout, |
| 171 | ) -> Result<(), Error> { | 192 | ) -> Result<(), Error> { |
| 172 | assert!(length < 256); | 193 | assert!(length < 256); |
| 173 | 194 | ||
| 174 | // Wait for any previous address sequence to end | 195 | if !restart { |
| 175 | // automatically. This could be up to 50% of a bus | 196 | // Wait for any previous address sequence to end |
| 176 | // cycle (ie. up to 0.5/freq) | 197 | // automatically. This could be up to 50% of a bus |
| 177 | while info.regs.cr2().read().start() { | 198 | // cycle (ie. up to 0.5/freq) |
| 178 | timeout.check()?; | 199 | while info.regs.cr2().read().start() { |
| 179 | } | 200 | timeout.check()?; |
| 201 | } | ||
| 180 | 202 | ||
| 181 | // Wait for the bus to be free | 203 | // Wait for the bus to be free |
| 182 | while info.regs.isr().read().busy() { | 204 | while info.regs.isr().read().busy() { |
| 183 | timeout.check()?; | 205 | timeout.check()?; |
| 206 | } | ||
| 184 | } | 207 | } |
| 185 | 208 | ||
| 186 | let reload = if reload { | ||
| 187 | i2c::vals::Reload::NOT_COMPLETED | ||
| 188 | } else { | ||
| 189 | i2c::vals::Reload::COMPLETED | ||
| 190 | }; | ||
| 191 | |||
| 192 | // Set START and prepare to send `bytes`. The | 209 | // Set START and prepare to send `bytes`. The |
| 193 | // START bit can be set even if the bus is BUSY or | 210 | // START bit can be set even if the bus is BUSY or |
| 194 | // I2C is in slave mode. | 211 | // I2C is in slave mode. |
| @@ -199,28 +216,36 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 199 | w.set_nbytes(length as u8); | 216 | w.set_nbytes(length as u8); |
| 200 | w.set_start(true); | 217 | w.set_start(true); |
| 201 | w.set_autoend(stop.autoend()); | 218 | w.set_autoend(stop.autoend()); |
| 202 | w.set_reload(reload); | 219 | w.set_reload(Self::to_reload(reload)); |
| 203 | }); | 220 | }); |
| 204 | 221 | ||
| 205 | Ok(()) | 222 | Ok(()) |
| 206 | } | 223 | } |
| 207 | 224 | ||
| 208 | fn reload(info: &'static Info, length: usize, will_reload: bool, timeout: Timeout) -> Result<(), Error> { | 225 | fn reload( |
| 226 | info: &'static Info, | ||
| 227 | length: usize, | ||
| 228 | will_reload: bool, | ||
| 229 | stop: Stop, | ||
| 230 | timeout: Timeout, | ||
| 231 | ) -> Result<(), Error> { | ||
| 209 | assert!(length < 256 && length > 0); | 232 | assert!(length < 256 && length > 0); |
| 210 | 233 | ||
| 211 | while !info.regs.isr().read().tcr() { | 234 | // Wait for either TCR (Transfer Complete Reload) or TC (Transfer Complete) |
| 235 | // TCR occurs when RELOAD=1, TC occurs when RELOAD=0 | ||
| 236 | // Both indicate the peripheral is ready for the next transfer | ||
| 237 | loop { | ||
| 238 | let isr = info.regs.isr().read(); | ||
| 239 | if isr.tcr() || isr.tc() { | ||
| 240 | break; | ||
| 241 | } | ||
| 212 | timeout.check()?; | 242 | timeout.check()?; |
| 213 | } | 243 | } |
| 214 | 244 | ||
| 215 | let will_reload = if will_reload { | ||
| 216 | i2c::vals::Reload::NOT_COMPLETED | ||
| 217 | } else { | ||
| 218 | i2c::vals::Reload::COMPLETED | ||
| 219 | }; | ||
| 220 | |||
| 221 | info.regs.cr2().modify(|w| { | 245 | info.regs.cr2().modify(|w| { |
| 222 | w.set_nbytes(length as u8); | 246 | w.set_nbytes(length as u8); |
| 223 | w.set_reload(will_reload); | 247 | w.set_reload(Self::to_reload(will_reload)); |
| 248 | w.set_autoend(stop.autoend()); | ||
| 224 | }); | 249 | }); |
| 225 | 250 | ||
| 226 | Ok(()) | 251 | Ok(()) |
| @@ -364,7 +389,9 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 364 | loop { | 389 | loop { |
| 365 | let isr = self.info.regs.isr().read(); | 390 | let isr = self.info.regs.isr().read(); |
| 366 | self.error_occurred(&isr, timeout)?; | 391 | self.error_occurred(&isr, timeout)?; |
| 367 | if isr.tc() { | 392 | // Wait for either TC or TCR - both indicate transfer completion |
| 393 | // TCR occurs when RELOAD=1, TC occurs when RELOAD=0 | ||
| 394 | if isr.tc() || isr.tcr() { | ||
| 368 | return Ok(()); | 395 | return Ok(()); |
| 369 | } | 396 | } |
| 370 | timeout.check()?; | 397 | timeout.check()?; |
| @@ -391,14 +418,20 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 391 | address, | 418 | address, |
| 392 | read.len().min(255), | 419 | read.len().min(255), |
| 393 | Stop::Automatic, | 420 | Stop::Automatic, |
| 394 | last_chunk_idx != 0, | 421 | last_chunk_idx != 0, // reload |
| 395 | restart, | 422 | restart, |
| 396 | timeout, | 423 | timeout, |
| 397 | )?; | 424 | )?; |
| 398 | 425 | ||
| 399 | for (number, chunk) in read.chunks_mut(255).enumerate() { | 426 | for (number, chunk) in read.chunks_mut(255).enumerate() { |
| 400 | if number != 0 { | 427 | if number != 0 { |
| 401 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 428 | Self::reload( |
| 429 | self.info, | ||
| 430 | chunk.len(), | ||
| 431 | number != last_chunk_idx, | ||
| 432 | Stop::Automatic, | ||
| 433 | timeout, | ||
| 434 | )?; | ||
| 402 | } | 435 | } |
| 403 | 436 | ||
| 404 | for byte in chunk { | 437 | for byte in chunk { |
| @@ -436,6 +469,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 436 | write.len().min(255), | 469 | write.len().min(255), |
| 437 | Stop::Software, | 470 | Stop::Software, |
| 438 | last_chunk_idx != 0, | 471 | last_chunk_idx != 0, |
| 472 | false, // restart | ||
| 439 | timeout, | 473 | timeout, |
| 440 | ) { | 474 | ) { |
| 441 | if send_stop { | 475 | if send_stop { |
| @@ -446,7 +480,13 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 446 | 480 | ||
| 447 | for (number, chunk) in write.chunks(255).enumerate() { | 481 | for (number, chunk) in write.chunks(255).enumerate() { |
| 448 | if number != 0 { | 482 | if number != 0 { |
| 449 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 483 | Self::reload( |
| 484 | self.info, | ||
| 485 | chunk.len(), | ||
| 486 | number != last_chunk_idx, | ||
| 487 | Stop::Software, | ||
| 488 | timeout, | ||
| 489 | )?; | ||
| 450 | } | 490 | } |
| 451 | 491 | ||
| 452 | for byte in chunk { | 492 | for byte in chunk { |
| @@ -502,9 +542,215 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 502 | /// | 542 | /// |
| 503 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | 543 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction |
| 504 | pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | 544 | pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { |
| 505 | let _ = addr; | 545 | if operations.is_empty() { |
| 506 | let _ = operations; | 546 | return Err(Error::ZeroLengthTransfer); |
| 507 | todo!() | 547 | } |
| 548 | |||
| 549 | let address = addr.into(); | ||
| 550 | let timeout = self.timeout(); | ||
| 551 | |||
| 552 | // Group consecutive operations of the same type | ||
| 553 | let mut op_idx = 0; | ||
| 554 | let mut is_first_group = true; | ||
| 555 | |||
| 556 | while op_idx < operations.len() { | ||
| 557 | // Determine the type of current group and find all consecutive operations of same type | ||
| 558 | let is_read = matches!(operations[op_idx], Operation::Read(_)); | ||
| 559 | let group_start = op_idx; | ||
| 560 | |||
| 561 | // Find end of this group (consecutive operations of same type) | ||
| 562 | while op_idx < operations.len() && matches!(operations[op_idx], Operation::Read(_)) == is_read { | ||
| 563 | op_idx += 1; | ||
| 564 | } | ||
| 565 | let group_end = op_idx; | ||
| 566 | let is_last_group = op_idx >= operations.len(); | ||
| 567 | |||
| 568 | // Execute this group of operations | ||
| 569 | if is_read { | ||
| 570 | self.execute_read_group( | ||
| 571 | address, | ||
| 572 | &mut operations[group_start..group_end], | ||
| 573 | is_first_group, | ||
| 574 | is_last_group, | ||
| 575 | timeout, | ||
| 576 | )?; | ||
| 577 | } else { | ||
| 578 | self.execute_write_group( | ||
| 579 | address, | ||
| 580 | &operations[group_start..group_end], | ||
| 581 | is_first_group, | ||
| 582 | is_last_group, | ||
| 583 | timeout, | ||
| 584 | )?; | ||
| 585 | } | ||
| 586 | |||
| 587 | is_first_group = false; | ||
| 588 | } | ||
| 589 | |||
| 590 | Ok(()) | ||
| 591 | } | ||
| 592 | |||
| 593 | fn execute_write_group( | ||
| 594 | &mut self, | ||
| 595 | address: Address, | ||
| 596 | operations: &[Operation<'_>], | ||
| 597 | is_first_group: bool, | ||
| 598 | is_last_group: bool, | ||
| 599 | timeout: Timeout, | ||
| 600 | ) -> Result<(), Error> { | ||
| 601 | // Calculate total bytes across all operations in this group | ||
| 602 | let total_bytes = Self::total_operation_bytes(operations); | ||
| 603 | |||
| 604 | if total_bytes == 0 { | ||
| 605 | // Handle empty write group - just send address | ||
| 606 | if is_first_group { | ||
| 607 | Self::master_write(self.info, address, 0, Stop::Software, false, !is_first_group, timeout)?; | ||
| 608 | } | ||
| 609 | if is_last_group { | ||
| 610 | self.master_stop(); | ||
| 611 | } | ||
| 612 | return Ok(()); | ||
| 613 | } | ||
| 614 | |||
| 615 | let mut total_remaining = total_bytes; | ||
| 616 | let mut first_chunk = true; | ||
| 617 | |||
| 618 | for operation in operations { | ||
| 619 | if let Operation::Write(buffer) = operation { | ||
| 620 | for chunk in buffer.chunks(255) { | ||
| 621 | let chunk_len = chunk.len(); | ||
| 622 | total_remaining -= chunk_len; | ||
| 623 | let is_last_chunk = total_remaining == 0; | ||
| 624 | let will_reload = !is_last_chunk; | ||
| 625 | |||
| 626 | if first_chunk { | ||
| 627 | // First chunk: initiate transfer | ||
| 628 | // If not first group, use RESTART instead of START | ||
| 629 | Self::master_write( | ||
| 630 | self.info, | ||
| 631 | address, | ||
| 632 | chunk_len, | ||
| 633 | Stop::Software, | ||
| 634 | will_reload, | ||
| 635 | !is_first_group, | ||
| 636 | timeout, | ||
| 637 | )?; | ||
| 638 | first_chunk = false; | ||
| 639 | } else { | ||
| 640 | // Subsequent chunks: use reload | ||
| 641 | // Always use Software stop for writes | ||
| 642 | Self::reload(self.info, chunk_len, will_reload, Stop::Software, timeout)?; | ||
| 643 | } | ||
| 644 | |||
| 645 | // Send data bytes | ||
| 646 | for byte in chunk { | ||
| 647 | self.wait_txis(timeout)?; | ||
| 648 | self.info.regs.txdr().write(|w| w.set_txdata(*byte)); | ||
| 649 | } | ||
| 650 | } | ||
| 651 | } | ||
| 652 | } | ||
| 653 | |||
| 654 | // Wait for transfer to complete | ||
| 655 | if is_last_group { | ||
| 656 | self.wait_tc(timeout)?; | ||
| 657 | self.master_stop(); | ||
| 658 | self.wait_stop(timeout)?; | ||
| 659 | } else { | ||
| 660 | // Wait for TC before next group (enables RESTART) | ||
| 661 | self.wait_tc(timeout)?; | ||
| 662 | } | ||
| 663 | |||
| 664 | Ok(()) | ||
| 665 | } | ||
| 666 | |||
| 667 | fn execute_read_group( | ||
| 668 | &mut self, | ||
| 669 | address: Address, | ||
| 670 | operations: &mut [Operation<'_>], | ||
| 671 | is_first_group: bool, | ||
| 672 | is_last_group: bool, | ||
| 673 | timeout: Timeout, | ||
| 674 | ) -> Result<(), Error> { | ||
| 675 | // Calculate total bytes across all operations in this group | ||
| 676 | let total_bytes = Self::total_operation_bytes(operations); | ||
| 677 | |||
| 678 | if total_bytes == 0 { | ||
| 679 | // Handle empty read group | ||
| 680 | if is_first_group { | ||
| 681 | Self::master_read( | ||
| 682 | self.info, | ||
| 683 | address, | ||
| 684 | 0, | ||
| 685 | if is_last_group { Stop::Automatic } else { Stop::Software }, | ||
| 686 | false, // reload | ||
| 687 | !is_first_group, | ||
| 688 | timeout, | ||
| 689 | )?; | ||
| 690 | } | ||
| 691 | if is_last_group { | ||
| 692 | self.wait_stop(timeout)?; | ||
| 693 | } | ||
| 694 | return Ok(()); | ||
| 695 | } | ||
| 696 | |||
| 697 | let mut total_remaining = total_bytes; | ||
| 698 | let mut first_chunk = true; | ||
| 699 | |||
| 700 | for operation in operations { | ||
| 701 | if let Operation::Read(buffer) = operation { | ||
| 702 | for chunk in buffer.chunks_mut(255) { | ||
| 703 | let chunk_len = chunk.len(); | ||
| 704 | total_remaining -= chunk_len; | ||
| 705 | let is_last_chunk = total_remaining == 0; | ||
| 706 | let will_reload = !is_last_chunk; | ||
| 707 | |||
| 708 | if first_chunk { | ||
| 709 | // First chunk: initiate transfer | ||
| 710 | let stop = if is_last_group && is_last_chunk { | ||
| 711 | Stop::Automatic | ||
| 712 | } else { | ||
| 713 | Stop::Software | ||
| 714 | }; | ||
| 715 | |||
| 716 | Self::master_read( | ||
| 717 | self.info, | ||
| 718 | address, | ||
| 719 | chunk_len, | ||
| 720 | stop, | ||
| 721 | will_reload, | ||
| 722 | !is_first_group, // restart if not first group | ||
| 723 | timeout, | ||
| 724 | )?; | ||
| 725 | first_chunk = false; | ||
| 726 | } else { | ||
| 727 | // Subsequent chunks: use reload | ||
| 728 | let stop = if is_last_group && is_last_chunk { | ||
| 729 | Stop::Automatic | ||
| 730 | } else { | ||
| 731 | Stop::Software | ||
| 732 | }; | ||
| 733 | Self::reload(self.info, chunk_len, will_reload, stop, timeout)?; | ||
| 734 | } | ||
| 735 | |||
| 736 | // Receive data bytes | ||
| 737 | for byte in chunk { | ||
| 738 | self.wait_rxne(timeout)?; | ||
| 739 | *byte = self.info.regs.rxdr().read().rxdata(); | ||
| 740 | } | ||
| 741 | } | ||
| 742 | } | ||
| 743 | } | ||
| 744 | |||
| 745 | // Wait for transfer to complete | ||
| 746 | if is_last_group { | ||
| 747 | self.wait_stop(timeout)?; | ||
| 748 | } | ||
| 749 | // For non-last read groups, don't wait for TC - the peripheral may hold SCL low | ||
| 750 | // in Software AUTOEND mode until the next START is issued. Just proceed directly | ||
| 751 | // to the next group which will issue RESTART and release the clock. | ||
| 752 | |||
| 753 | Ok(()) | ||
| 508 | } | 754 | } |
| 509 | 755 | ||
| 510 | /// Blocking write multiple buffers. | 756 | /// Blocking write multiple buffers. |
| @@ -526,6 +772,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 526 | first_length.min(255), | 772 | first_length.min(255), |
| 527 | Stop::Software, | 773 | Stop::Software, |
| 528 | (first_length > 255) || (last_slice_index != 0), | 774 | (first_length > 255) || (last_slice_index != 0), |
| 775 | false, // restart | ||
| 529 | timeout, | 776 | timeout, |
| 530 | ) { | 777 | ) { |
| 531 | self.master_stop(); | 778 | self.master_stop(); |
| @@ -547,6 +794,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 547 | self.info, | 794 | self.info, |
| 548 | slice_len.min(255), | 795 | slice_len.min(255), |
| 549 | (idx != last_slice_index) || (slice_len > 255), | 796 | (idx != last_slice_index) || (slice_len > 255), |
| 797 | Stop::Software, | ||
| 550 | timeout, | 798 | timeout, |
| 551 | ) { | 799 | ) { |
| 552 | if err != Error::Nack { | 800 | if err != Error::Nack { |
| @@ -562,6 +810,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 562 | self.info, | 810 | self.info, |
| 563 | chunk.len(), | 811 | chunk.len(), |
| 564 | (number != last_chunk_idx) || (idx != last_slice_index), | 812 | (number != last_chunk_idx) || (idx != last_slice_index), |
| 813 | Stop::Software, | ||
| 565 | timeout, | 814 | timeout, |
| 566 | ) { | 815 | ) { |
| 567 | if err != Error::Nack { | 816 | if err != Error::Nack { |
| @@ -605,6 +854,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 605 | first_slice: bool, | 854 | first_slice: bool, |
| 606 | last_slice: bool, | 855 | last_slice: bool, |
| 607 | send_stop: bool, | 856 | send_stop: bool, |
| 857 | restart: bool, | ||
| 608 | timeout: Timeout, | 858 | timeout: Timeout, |
| 609 | ) -> Result<(), Error> { | 859 | ) -> Result<(), Error> { |
| 610 | let total_len = write.len(); | 860 | let total_len = write.len(); |
| @@ -671,10 +921,17 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 671 | total_len.min(255), | 921 | total_len.min(255), |
| 672 | Stop::Software, | 922 | Stop::Software, |
| 673 | (total_len > 255) || !last_slice, | 923 | (total_len > 255) || !last_slice, |
| 924 | restart, | ||
| 674 | timeout, | 925 | timeout, |
| 675 | )?; | 926 | )?; |
| 676 | } else { | 927 | } else { |
| 677 | Self::reload(self.info, total_len.min(255), (total_len > 255) || !last_slice, timeout)?; | 928 | Self::reload( |
| 929 | self.info, | ||
| 930 | total_len.min(255), | ||
| 931 | (total_len > 255) || !last_slice, | ||
| 932 | Stop::Software, | ||
| 933 | timeout, | ||
| 934 | )?; | ||
| 678 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | 935 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); |
| 679 | } | 936 | } |
| 680 | } else if !(isr.tcr() || isr.tc()) { | 937 | } else if !(isr.tcr() || isr.tc()) { |
| @@ -683,9 +940,13 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 683 | } else if remaining_len == 0 { | 940 | } else if remaining_len == 0 { |
| 684 | return Poll::Ready(Ok(())); | 941 | return Poll::Ready(Ok(())); |
| 685 | } else { | 942 | } else { |
| 686 | let last_piece = (remaining_len <= 255) && last_slice; | 943 | if let Err(e) = Self::reload( |
| 687 | 944 | self.info, | |
| 688 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) { | 945 | remaining_len.min(255), |
| 946 | (remaining_len > 255) || !last_slice, | ||
| 947 | Stop::Software, | ||
| 948 | timeout, | ||
| 949 | ) { | ||
| 689 | return Poll::Ready(Err(e)); | 950 | return Poll::Ready(Err(e)); |
| 690 | } | 951 | } |
| 691 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | 952 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); |
| @@ -697,10 +958,9 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 697 | .await?; | 958 | .await?; |
| 698 | 959 | ||
| 699 | dma_transfer.await; | 960 | dma_transfer.await; |
| 700 | if last_slice { | 961 | |
| 701 | // This should be done already | 962 | // Always wait for TC after DMA completes - needed for consecutive buffers |
| 702 | self.wait_tc(timeout)?; | 963 | self.wait_tc(timeout)?; |
| 703 | } | ||
| 704 | 964 | ||
| 705 | if last_slice & send_stop { | 965 | if last_slice & send_stop { |
| 706 | self.master_stop(); | 966 | self.master_stop(); |
| @@ -775,7 +1035,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 775 | address, | 1035 | address, |
| 776 | total_len.min(255), | 1036 | total_len.min(255), |
| 777 | Stop::Automatic, | 1037 | Stop::Automatic, |
| 778 | total_len > 255, | 1038 | total_len > 255, // reload |
| 779 | restart, | 1039 | restart, |
| 780 | timeout, | 1040 | timeout, |
| 781 | )?; | 1041 | )?; |
| @@ -783,12 +1043,10 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 783 | return Poll::Ready(Ok(())); | 1043 | return Poll::Ready(Ok(())); |
| 784 | } | 1044 | } |
| 785 | } else if isr.tcr() { | 1045 | } else if isr.tcr() { |
| 786 | // poll_fn was woken without an interrupt present | 1046 | // Transfer Complete Reload - need to set up next chunk |
| 787 | return Poll::Pending; | ||
| 788 | } else { | ||
| 789 | let last_piece = remaining_len <= 255; | 1047 | let last_piece = remaining_len <= 255; |
| 790 | 1048 | ||
| 791 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) { | 1049 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, Stop::Automatic, timeout) { |
| 792 | return Poll::Ready(Err(e)); | 1050 | return Poll::Ready(Err(e)); |
| 793 | } | 1051 | } |
| 794 | // Return here if we are on last chunk, | 1052 | // Return here if we are on last chunk, |
| @@ -797,6 +1055,9 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 797 | return Poll::Ready(Ok(())); | 1055 | return Poll::Ready(Ok(())); |
| 798 | } | 1056 | } |
| 799 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | 1057 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); |
| 1058 | } else { | ||
| 1059 | // poll_fn was woken without TCR interrupt | ||
| 1060 | return Poll::Pending; | ||
| 800 | } | 1061 | } |
| 801 | 1062 | ||
| 802 | remaining_len = remaining_len.saturating_sub(255); | 1063 | remaining_len = remaining_len.saturating_sub(255); |
| @@ -814,12 +1075,13 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 814 | 1075 | ||
| 815 | /// Write. | 1076 | /// Write. |
| 816 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { | 1077 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { |
| 1078 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 817 | let timeout = self.timeout(); | 1079 | let timeout = self.timeout(); |
| 818 | if write.is_empty() { | 1080 | if write.is_empty() { |
| 819 | self.write_internal(address.into(), write, true, timeout) | 1081 | self.write_internal(address.into(), write, true, timeout) |
| 820 | } else { | 1082 | } else { |
| 821 | timeout | 1083 | timeout |
| 822 | .with(self.write_dma_internal(address.into(), write, true, true, true, timeout)) | 1084 | .with(self.write_dma_internal(address.into(), write, true, true, true, false, timeout)) |
| 823 | .await | 1085 | .await |
| 824 | } | 1086 | } |
| 825 | } | 1087 | } |
| @@ -828,21 +1090,30 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 828 | /// | 1090 | /// |
| 829 | /// The buffers are concatenated in a single write transaction. | 1091 | /// The buffers are concatenated in a single write transaction. |
| 830 | pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { | 1092 | pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { |
| 1093 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 831 | let timeout = self.timeout(); | 1094 | let timeout = self.timeout(); |
| 832 | 1095 | ||
| 833 | if write.is_empty() { | 1096 | if write.is_empty() { |
| 834 | return Err(Error::ZeroLengthTransfer); | 1097 | return Err(Error::ZeroLengthTransfer); |
| 835 | } | 1098 | } |
| 836 | let mut iter = write.iter(); | ||
| 837 | 1099 | ||
| 1100 | let mut iter = write.iter(); | ||
| 838 | let mut first = true; | 1101 | let mut first = true; |
| 839 | let mut current = iter.next(); | 1102 | let mut current = iter.next(); |
| 1103 | |||
| 840 | while let Some(c) = current { | 1104 | while let Some(c) = current { |
| 841 | let next = iter.next(); | 1105 | let next = iter.next(); |
| 842 | let is_last = next.is_none(); | 1106 | let is_last = next.is_none(); |
| 843 | 1107 | ||
| 844 | let fut = self.write_dma_internal(address, c, first, is_last, is_last, timeout); | 1108 | let fut = self.write_dma_internal( |
| 1109 | address, c, first, // first_slice | ||
| 1110 | is_last, // last_slice | ||
| 1111 | is_last, // send_stop (only on last buffer) | ||
| 1112 | false, // restart (false for all - they're one continuous write) | ||
| 1113 | timeout, | ||
| 1114 | ); | ||
| 845 | timeout.with(fut).await?; | 1115 | timeout.with(fut).await?; |
| 1116 | |||
| 846 | first = false; | 1117 | first = false; |
| 847 | current = next; | 1118 | current = next; |
| 848 | } | 1119 | } |
| @@ -851,6 +1122,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 851 | 1122 | ||
| 852 | /// Read. | 1123 | /// Read. |
| 853 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { | 1124 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { |
| 1125 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 854 | let timeout = self.timeout(); | 1126 | let timeout = self.timeout(); |
| 855 | 1127 | ||
| 856 | if buffer.is_empty() { | 1128 | if buffer.is_empty() { |
| @@ -863,12 +1135,13 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 863 | 1135 | ||
| 864 | /// Write, restart, read. | 1136 | /// Write, restart, read. |
| 865 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { | 1137 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { |
| 1138 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 866 | let timeout = self.timeout(); | 1139 | let timeout = self.timeout(); |
| 867 | 1140 | ||
| 868 | if write.is_empty() { | 1141 | if write.is_empty() { |
| 869 | self.write_internal(address.into(), write, false, timeout)?; | 1142 | self.write_internal(address.into(), write, false, timeout)?; |
| 870 | } else { | 1143 | } else { |
| 871 | let fut = self.write_dma_internal(address.into(), write, true, true, false, timeout); | 1144 | let fut = self.write_dma_internal(address.into(), write, true, true, false, false, timeout); |
| 872 | timeout.with(fut).await?; | 1145 | timeout.with(fut).await?; |
| 873 | } | 1146 | } |
| 874 | 1147 | ||
| @@ -888,9 +1161,299 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 888 | /// | 1161 | /// |
| 889 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | 1162 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction |
| 890 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | 1163 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { |
| 891 | let _ = addr; | 1164 | let _scoped_block_stop = self.info.rcc.block_stop(); |
| 892 | let _ = operations; | 1165 | if operations.is_empty() { |
| 893 | todo!() | 1166 | return Err(Error::ZeroLengthTransfer); |
| 1167 | } | ||
| 1168 | |||
| 1169 | let address = addr.into(); | ||
| 1170 | let timeout = self.timeout(); | ||
| 1171 | |||
| 1172 | // Group consecutive operations of the same type | ||
| 1173 | let mut op_idx = 0; | ||
| 1174 | let mut is_first_group = true; | ||
| 1175 | |||
| 1176 | while op_idx < operations.len() { | ||
| 1177 | // Determine the type of current group and find all consecutive operations of same type | ||
| 1178 | let is_read = matches!(operations[op_idx], Operation::Read(_)); | ||
| 1179 | let group_start = op_idx; | ||
| 1180 | |||
| 1181 | // Find end of this group (consecutive operations of same type) | ||
| 1182 | while op_idx < operations.len() && matches!(operations[op_idx], Operation::Read(_)) == is_read { | ||
| 1183 | op_idx += 1; | ||
| 1184 | } | ||
| 1185 | let group_end = op_idx; | ||
| 1186 | let is_last_group = op_idx >= operations.len(); | ||
| 1187 | |||
| 1188 | // Execute this group of operations | ||
| 1189 | if is_read { | ||
| 1190 | self.execute_read_group_async( | ||
| 1191 | address, | ||
| 1192 | &mut operations[group_start..group_end], | ||
| 1193 | is_first_group, | ||
| 1194 | is_last_group, | ||
| 1195 | timeout, | ||
| 1196 | ) | ||
| 1197 | .await?; | ||
| 1198 | } else { | ||
| 1199 | self.execute_write_group_async( | ||
| 1200 | address, | ||
| 1201 | &operations[group_start..group_end], | ||
| 1202 | is_first_group, | ||
| 1203 | is_last_group, | ||
| 1204 | timeout, | ||
| 1205 | ) | ||
| 1206 | .await?; | ||
| 1207 | } | ||
| 1208 | |||
| 1209 | is_first_group = false; | ||
| 1210 | } | ||
| 1211 | |||
| 1212 | Ok(()) | ||
| 1213 | } | ||
| 1214 | |||
| 1215 | async fn execute_write_group_async( | ||
| 1216 | &mut self, | ||
| 1217 | address: Address, | ||
| 1218 | operations: &[Operation<'_>], | ||
| 1219 | is_first_group: bool, | ||
| 1220 | is_last_group: bool, | ||
| 1221 | timeout: Timeout, | ||
| 1222 | ) -> Result<(), Error> { | ||
| 1223 | // Calculate total bytes across all operations in this group | ||
| 1224 | let total_bytes = Self::total_operation_bytes(operations); | ||
| 1225 | |||
| 1226 | if total_bytes == 0 { | ||
| 1227 | // Handle empty write group using blocking call | ||
| 1228 | if is_first_group { | ||
| 1229 | Self::master_write(self.info, address, 0, Stop::Software, false, !is_first_group, timeout)?; | ||
| 1230 | } | ||
| 1231 | if is_last_group { | ||
| 1232 | self.master_stop(); | ||
| 1233 | } | ||
| 1234 | return Ok(()); | ||
| 1235 | } | ||
| 1236 | |||
| 1237 | // Collect all write buffers | ||
| 1238 | let mut write_buffers: heapless::Vec<&[u8], 16> = heapless::Vec::new(); | ||
| 1239 | for operation in operations { | ||
| 1240 | if let Operation::Write(buffer) = operation { | ||
| 1241 | if !buffer.is_empty() { | ||
| 1242 | let _ = write_buffers.push(buffer); | ||
| 1243 | } | ||
| 1244 | } | ||
| 1245 | } | ||
| 1246 | |||
| 1247 | if write_buffers.is_empty() { | ||
| 1248 | return Ok(()); | ||
| 1249 | } | ||
| 1250 | |||
| 1251 | // Send each buffer using DMA | ||
| 1252 | let num_buffers = write_buffers.len(); | ||
| 1253 | for (idx, buffer) in write_buffers.iter().enumerate() { | ||
| 1254 | let is_first_buffer = idx == 0; | ||
| 1255 | let is_last_buffer = idx == num_buffers - 1; | ||
| 1256 | |||
| 1257 | let fut = self.write_dma_internal( | ||
| 1258 | address, | ||
| 1259 | buffer, | ||
| 1260 | is_first_buffer, // first_slice | ||
| 1261 | is_last_buffer, // last_slice | ||
| 1262 | is_last_buffer && is_last_group, // send_stop | ||
| 1263 | is_first_buffer && !is_first_group, // restart (only for first buffer if not first group) | ||
| 1264 | timeout, | ||
| 1265 | ); | ||
| 1266 | timeout.with(fut).await?; | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | Ok(()) | ||
| 1270 | } | ||
| 1271 | |||
| 1272 | async fn execute_read_group_async( | ||
| 1273 | &mut self, | ||
| 1274 | address: Address, | ||
| 1275 | operations: &mut [Operation<'_>], | ||
| 1276 | is_first_group: bool, | ||
| 1277 | is_last_group: bool, | ||
| 1278 | timeout: Timeout, | ||
| 1279 | ) -> Result<(), Error> { | ||
| 1280 | // Calculate total bytes across all operations in this group | ||
| 1281 | let total_bytes = Self::total_operation_bytes(operations); | ||
| 1282 | |||
| 1283 | if total_bytes == 0 { | ||
| 1284 | // Handle empty read group using blocking call | ||
| 1285 | if is_first_group { | ||
| 1286 | Self::master_read( | ||
| 1287 | self.info, | ||
| 1288 | address, | ||
| 1289 | 0, | ||
| 1290 | if is_last_group { Stop::Automatic } else { Stop::Software }, | ||
| 1291 | false, // reload | ||
| 1292 | !is_first_group, | ||
| 1293 | timeout, | ||
| 1294 | )?; | ||
| 1295 | } | ||
| 1296 | if is_last_group { | ||
| 1297 | self.wait_stop(timeout)?; | ||
| 1298 | } | ||
| 1299 | return Ok(()); | ||
| 1300 | } | ||
| 1301 | |||
| 1302 | // Use DMA for read operations - need to handle multiple buffers | ||
| 1303 | let restart = !is_first_group; | ||
| 1304 | let mut total_remaining = total_bytes; | ||
| 1305 | let mut is_first_in_group = true; | ||
| 1306 | |||
| 1307 | for operation in operations { | ||
| 1308 | if let Operation::Read(buffer) = operation { | ||
| 1309 | if buffer.is_empty() { | ||
| 1310 | // Skip empty buffers | ||
| 1311 | continue; | ||
| 1312 | } | ||
| 1313 | |||
| 1314 | let buf_len = buffer.len(); | ||
| 1315 | total_remaining -= buf_len; | ||
| 1316 | let is_last_in_group = total_remaining == 0; | ||
| 1317 | |||
| 1318 | // Perform DMA read | ||
| 1319 | if is_first_in_group { | ||
| 1320 | // First buffer: use read_dma_internal which handles restart properly | ||
| 1321 | // Only use Automatic stop if this is the last buffer in the last group | ||
| 1322 | let stop_mode = if is_last_group && is_last_in_group { | ||
| 1323 | Stop::Automatic | ||
| 1324 | } else { | ||
| 1325 | Stop::Software | ||
| 1326 | }; | ||
| 1327 | |||
| 1328 | // We need a custom DMA read that respects our stop mode | ||
| 1329 | self.read_dma_group_internal(address, buffer, restart, stop_mode, timeout) | ||
| 1330 | .await?; | ||
| 1331 | is_first_in_group = false; | ||
| 1332 | } else { | ||
| 1333 | // Subsequent buffers: need to reload and continue | ||
| 1334 | let stop_mode = if is_last_group && is_last_in_group { | ||
| 1335 | Stop::Automatic | ||
| 1336 | } else { | ||
| 1337 | Stop::Software | ||
| 1338 | }; | ||
| 1339 | |||
| 1340 | self.read_dma_group_internal( | ||
| 1341 | address, buffer, false, // no restart for subsequent buffers in same group | ||
| 1342 | stop_mode, timeout, | ||
| 1343 | ) | ||
| 1344 | .await?; | ||
| 1345 | } | ||
| 1346 | } | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | // Wait for transfer to complete | ||
| 1350 | if is_last_group { | ||
| 1351 | self.wait_stop(timeout)?; | ||
| 1352 | } | ||
| 1353 | |||
| 1354 | Ok(()) | ||
| 1355 | } | ||
| 1356 | |||
| 1357 | /// Internal DMA read helper for transaction groups | ||
| 1358 | async fn read_dma_group_internal( | ||
| 1359 | &mut self, | ||
| 1360 | address: Address, | ||
| 1361 | buffer: &mut [u8], | ||
| 1362 | restart: bool, | ||
| 1363 | stop_mode: Stop, | ||
| 1364 | timeout: Timeout, | ||
| 1365 | ) -> Result<(), Error> { | ||
| 1366 | let total_len = buffer.len(); | ||
| 1367 | |||
| 1368 | let dma_transfer = unsafe { | ||
| 1369 | let regs = self.info.regs; | ||
| 1370 | regs.cr1().modify(|w| { | ||
| 1371 | w.set_rxdmaen(true); | ||
| 1372 | w.set_tcie(true); | ||
| 1373 | w.set_nackie(true); | ||
| 1374 | w.set_errie(true); | ||
| 1375 | }); | ||
| 1376 | let src = regs.rxdr().as_ptr() as *mut u8; | ||
| 1377 | |||
| 1378 | self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) | ||
| 1379 | }; | ||
| 1380 | |||
| 1381 | let mut remaining_len = total_len; | ||
| 1382 | |||
| 1383 | let on_drop = OnDrop::new(|| { | ||
| 1384 | let regs = self.info.regs; | ||
| 1385 | regs.cr1().modify(|w| { | ||
| 1386 | w.set_rxdmaen(false); | ||
| 1387 | w.set_tcie(false); | ||
| 1388 | w.set_nackie(false); | ||
| 1389 | w.set_errie(false); | ||
| 1390 | }); | ||
| 1391 | regs.icr().write(|w| { | ||
| 1392 | w.set_nackcf(true); | ||
| 1393 | w.set_berrcf(true); | ||
| 1394 | w.set_arlocf(true); | ||
| 1395 | w.set_ovrcf(true); | ||
| 1396 | }); | ||
| 1397 | }); | ||
| 1398 | |||
| 1399 | poll_fn(|cx| { | ||
| 1400 | self.state.waker.register(cx.waker()); | ||
| 1401 | |||
| 1402 | let isr = self.info.regs.isr().read(); | ||
| 1403 | |||
| 1404 | if isr.nackf() { | ||
| 1405 | return Poll::Ready(Err(Error::Nack)); | ||
| 1406 | } | ||
| 1407 | if isr.arlo() { | ||
| 1408 | return Poll::Ready(Err(Error::Arbitration)); | ||
| 1409 | } | ||
| 1410 | if isr.berr() { | ||
| 1411 | return Poll::Ready(Err(Error::Bus)); | ||
| 1412 | } | ||
| 1413 | if isr.ovr() { | ||
| 1414 | return Poll::Ready(Err(Error::Overrun)); | ||
| 1415 | } | ||
| 1416 | |||
| 1417 | if remaining_len == total_len { | ||
| 1418 | Self::master_read( | ||
| 1419 | self.info, | ||
| 1420 | address, | ||
| 1421 | total_len.min(255), | ||
| 1422 | stop_mode, | ||
| 1423 | total_len > 255, // reload | ||
| 1424 | restart, | ||
| 1425 | timeout, | ||
| 1426 | )?; | ||
| 1427 | if total_len <= 255 { | ||
| 1428 | return Poll::Ready(Ok(())); | ||
| 1429 | } | ||
| 1430 | } else if isr.tcr() { | ||
| 1431 | // Transfer Complete Reload - need to set up next chunk | ||
| 1432 | let last_piece = remaining_len <= 255; | ||
| 1433 | |||
| 1434 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, stop_mode, timeout) { | ||
| 1435 | return Poll::Ready(Err(e)); | ||
| 1436 | } | ||
| 1437 | // Return here if we are on last chunk, | ||
| 1438 | // end of transfer will be awaited with the DMA below | ||
| 1439 | if last_piece { | ||
| 1440 | return Poll::Ready(Ok(())); | ||
| 1441 | } | ||
| 1442 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | ||
| 1443 | } else { | ||
| 1444 | // poll_fn was woken without TCR interrupt | ||
| 1445 | return Poll::Pending; | ||
| 1446 | } | ||
| 1447 | |||
| 1448 | remaining_len = remaining_len.saturating_sub(255); | ||
| 1449 | Poll::Pending | ||
| 1450 | }) | ||
| 1451 | .await?; | ||
| 1452 | |||
| 1453 | dma_transfer.await; | ||
| 1454 | drop(on_drop); | ||
| 1455 | |||
| 1456 | Ok(()) | ||
| 894 | } | 1457 | } |
| 895 | } | 1458 | } |
| 896 | 1459 | ||
| @@ -1028,7 +1591,13 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1028 | if number == 0 { | 1591 | if number == 0 { |
| 1029 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); | 1592 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); |
| 1030 | } else { | 1593 | } else { |
| 1031 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 1594 | Self::reload( |
| 1595 | self.info, | ||
| 1596 | chunk.len(), | ||
| 1597 | number != last_chunk_idx, | ||
| 1598 | Stop::Software, | ||
| 1599 | timeout, | ||
| 1600 | )?; | ||
| 1032 | } | 1601 | } |
| 1033 | 1602 | ||
| 1034 | let mut index = 0; | 1603 | let mut index = 0; |
| @@ -1036,7 +1605,8 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1036 | for byte in chunk { | 1605 | for byte in chunk { |
| 1037 | // Wait until we have received something | 1606 | // Wait until we have received something |
| 1038 | match self.wait_rxne(timeout) { | 1607 | match self.wait_rxne(timeout) { |
| 1039 | Ok(ReceiveResult::StopReceived) | Ok(ReceiveResult::NewStart) => { | 1608 | Ok(ReceiveResult::StopReceived) => {} |
| 1609 | Ok(ReceiveResult::NewStart) => { | ||
| 1040 | trace!("--- Slave RX transmission end (early)"); | 1610 | trace!("--- Slave RX transmission end (early)"); |
| 1041 | return Ok(total_len - remaining_len); // Return N bytes read | 1611 | return Ok(total_len - remaining_len); // Return N bytes read |
| 1042 | } | 1612 | } |
| @@ -1077,7 +1647,13 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1077 | if number == 0 { | 1647 | if number == 0 { |
| 1078 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); | 1648 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); |
| 1079 | } else { | 1649 | } else { |
| 1080 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 1650 | Self::reload( |
| 1651 | self.info, | ||
| 1652 | chunk.len(), | ||
| 1653 | number != last_chunk_idx, | ||
| 1654 | Stop::Software, | ||
| 1655 | timeout, | ||
| 1656 | )?; | ||
| 1081 | } | 1657 | } |
| 1082 | 1658 | ||
| 1083 | let mut index = 0; | 1659 | let mut index = 0; |
| @@ -1104,42 +1680,50 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1104 | 1680 | ||
| 1105 | /// Listen for incoming I2C messages. | 1681 | /// Listen for incoming I2C messages. |
| 1106 | /// | 1682 | /// |
| 1107 | /// The listen method is an asynchronous method but it does not require DMA to be asynchronous. | 1683 | /// This method blocks until the slave address is matched by a master. |
| 1108 | pub async fn listen(&mut self) -> Result<SlaveCommand, Error> { | 1684 | pub fn blocking_listen(&mut self) -> Result<SlaveCommand, Error> { |
| 1109 | let state = self.state; | 1685 | let timeout = self.timeout(); |
| 1686 | |||
| 1110 | self.info.regs.cr1().modify(|reg| { | 1687 | self.info.regs.cr1().modify(|reg| { |
| 1111 | reg.set_addrie(true); | 1688 | reg.set_addrie(true); |
| 1112 | trace!("Enable ADDRIE"); | 1689 | trace!("Enable ADDRIE"); |
| 1113 | }); | 1690 | }); |
| 1114 | 1691 | ||
| 1115 | poll_fn(|cx| { | 1692 | loop { |
| 1116 | state.waker.register(cx.waker()); | ||
| 1117 | let isr = self.info.regs.isr().read(); | 1693 | let isr = self.info.regs.isr().read(); |
| 1118 | if !isr.addr() { | 1694 | if isr.addr() { |
| 1119 | Poll::Pending | 1695 | break; |
| 1120 | } else { | ||
| 1121 | trace!("ADDR triggered (address match)"); | ||
| 1122 | // we do not clear the address flag here as it will be cleared by the dma read/write | ||
| 1123 | // if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it | ||
| 1124 | match isr.dir() { | ||
| 1125 | i2c::vals::Dir::WRITE => { | ||
| 1126 | trace!("DIR: write"); | ||
| 1127 | Poll::Ready(Ok(SlaveCommand { | ||
| 1128 | kind: SlaveCommandKind::Write, | ||
| 1129 | address: self.determine_matched_address()?, | ||
| 1130 | })) | ||
| 1131 | } | ||
| 1132 | i2c::vals::Dir::READ => { | ||
| 1133 | trace!("DIR: read"); | ||
| 1134 | Poll::Ready(Ok(SlaveCommand { | ||
| 1135 | kind: SlaveCommandKind::Read, | ||
| 1136 | address: self.determine_matched_address()?, | ||
| 1137 | })) | ||
| 1138 | } | ||
| 1139 | } | ||
| 1140 | } | 1696 | } |
| 1141 | }) | 1697 | timeout.check()?; |
| 1142 | .await | 1698 | } |
| 1699 | |||
| 1700 | trace!("ADDR triggered (address match)"); | ||
| 1701 | |||
| 1702 | // we do not clear the address flag here as it will be cleared by the dma read/write | ||
| 1703 | // if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it | ||
| 1704 | self.slave_command() | ||
| 1705 | } | ||
| 1706 | |||
| 1707 | /// Determine the received slave command. | ||
| 1708 | fn slave_command(&self) -> Result<SlaveCommand, Error> { | ||
| 1709 | let isr = self.info.regs.isr().read(); | ||
| 1710 | |||
| 1711 | match isr.dir() { | ||
| 1712 | i2c::vals::Dir::WRITE => { | ||
| 1713 | trace!("DIR: write"); | ||
| 1714 | Ok(SlaveCommand { | ||
| 1715 | kind: SlaveCommandKind::Write, | ||
| 1716 | address: self.determine_matched_address()?, | ||
| 1717 | }) | ||
| 1718 | } | ||
| 1719 | i2c::vals::Dir::READ => { | ||
| 1720 | trace!("DIR: read"); | ||
| 1721 | Ok(SlaveCommand { | ||
| 1722 | kind: SlaveCommandKind::Read, | ||
| 1723 | address: self.determine_matched_address()?, | ||
| 1724 | }) | ||
| 1725 | } | ||
| 1726 | } | ||
| 1143 | } | 1727 | } |
| 1144 | 1728 | ||
| 1145 | /// Respond to a write command. | 1729 | /// Respond to a write command. |
| @@ -1158,16 +1742,44 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1158 | } | 1742 | } |
| 1159 | 1743 | ||
| 1160 | impl<'d> I2c<'d, Async, MultiMaster> { | 1744 | impl<'d> I2c<'d, Async, MultiMaster> { |
| 1745 | /// Listen for incoming I2C messages. | ||
| 1746 | /// | ||
| 1747 | /// The listen method is an asynchronous method but it does not require DMA to be asynchronous. | ||
| 1748 | pub async fn listen(&mut self) -> Result<SlaveCommand, Error> { | ||
| 1749 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 1750 | let state = self.state; | ||
| 1751 | self.info.regs.cr1().modify(|reg| { | ||
| 1752 | reg.set_addrie(true); | ||
| 1753 | trace!("Enable ADDRIE"); | ||
| 1754 | }); | ||
| 1755 | |||
| 1756 | poll_fn(|cx| { | ||
| 1757 | state.waker.register(cx.waker()); | ||
| 1758 | let isr = self.info.regs.isr().read(); | ||
| 1759 | if !isr.addr() { | ||
| 1760 | Poll::Pending | ||
| 1761 | } else { | ||
| 1762 | trace!("ADDR triggered (address match)"); | ||
| 1763 | // we do not clear the address flag here as it will be cleared by the dma read/write | ||
| 1764 | // if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it | ||
| 1765 | Poll::Ready(self.slave_command()) | ||
| 1766 | } | ||
| 1767 | }) | ||
| 1768 | .await | ||
| 1769 | } | ||
| 1770 | |||
| 1161 | /// Respond to a write command. | 1771 | /// Respond to a write command. |
| 1162 | /// | 1772 | /// |
| 1163 | /// Returns the total number of bytes received. | 1773 | /// Returns the total number of bytes received. |
| 1164 | pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { | 1774 | pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { |
| 1775 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 1165 | let timeout = self.timeout(); | 1776 | let timeout = self.timeout(); |
| 1166 | timeout.with(self.read_dma_internal_slave(buffer, timeout)).await | 1777 | timeout.with(self.read_dma_internal_slave(buffer, timeout)).await |
| 1167 | } | 1778 | } |
| 1168 | 1779 | ||
| 1169 | /// Respond to a read request from an I2C master. | 1780 | /// Respond to a read request from an I2C master. |
| 1170 | pub async fn respond_to_read(&mut self, write: &[u8]) -> Result<SendStatus, Error> { | 1781 | pub async fn respond_to_read(&mut self, write: &[u8]) -> Result<SendStatus, Error> { |
| 1782 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 1171 | let timeout = self.timeout(); | 1783 | let timeout = self.timeout(); |
| 1172 | timeout.with(self.write_dma_internal_slave(write, timeout)).await | 1784 | timeout.with(self.write_dma_internal_slave(write, timeout)).await |
| 1173 | } | 1785 | } |
| @@ -1181,7 +1793,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1181 | 1793 | ||
| 1182 | let regs = self.info.regs; | 1794 | let regs = self.info.regs; |
| 1183 | 1795 | ||
| 1184 | let dma_transfer = unsafe { | 1796 | let mut dma_transfer = unsafe { |
| 1185 | regs.cr1().modify(|w| { | 1797 | regs.cr1().modify(|w| { |
| 1186 | w.set_rxdmaen(true); | 1798 | w.set_rxdmaen(true); |
| 1187 | w.set_stopie(true); | 1799 | w.set_stopie(true); |
| @@ -1213,13 +1825,20 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1213 | Poll::Pending | 1825 | Poll::Pending |
| 1214 | } else if isr.tcr() { | 1826 | } else if isr.tcr() { |
| 1215 | let is_last_slice = remaining_len <= 255; | 1827 | let is_last_slice = remaining_len <= 255; |
| 1216 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { | 1828 | if let Err(e) = Self::reload( |
| 1829 | self.info, | ||
| 1830 | remaining_len.min(255), | ||
| 1831 | !is_last_slice, | ||
| 1832 | Stop::Software, | ||
| 1833 | timeout, | ||
| 1834 | ) { | ||
| 1217 | return Poll::Ready(Err(e)); | 1835 | return Poll::Ready(Err(e)); |
| 1218 | } | 1836 | } |
| 1219 | remaining_len = remaining_len.saturating_sub(255); | 1837 | remaining_len = remaining_len.saturating_sub(255); |
| 1220 | regs.cr1().modify(|w| w.set_tcie(true)); | 1838 | regs.cr1().modify(|w| w.set_tcie(true)); |
| 1221 | Poll::Pending | 1839 | Poll::Pending |
| 1222 | } else if isr.stopf() { | 1840 | } else if isr.stopf() { |
| 1841 | remaining_len = remaining_len.saturating_add(dma_transfer.get_remaining_transfers() as usize); | ||
| 1223 | regs.icr().write(|reg| reg.set_stopcf(true)); | 1842 | regs.icr().write(|reg| reg.set_stopcf(true)); |
| 1224 | let poll = Poll::Ready(Ok(total_len - remaining_len)); | 1843 | let poll = Poll::Ready(Ok(total_len - remaining_len)); |
| 1225 | poll | 1844 | poll |
| @@ -1229,6 +1848,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1229 | }) | 1848 | }) |
| 1230 | .await?; | 1849 | .await?; |
| 1231 | 1850 | ||
| 1851 | dma_transfer.request_pause(); | ||
| 1232 | dma_transfer.await; | 1852 | dma_transfer.await; |
| 1233 | 1853 | ||
| 1234 | drop(on_drop); | 1854 | drop(on_drop); |
| @@ -1258,7 +1878,8 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1258 | w.set_txdmaen(false); | 1878 | w.set_txdmaen(false); |
| 1259 | w.set_stopie(false); | 1879 | w.set_stopie(false); |
| 1260 | w.set_tcie(false); | 1880 | w.set_tcie(false); |
| 1261 | }) | 1881 | }); |
| 1882 | regs.isr().write(|w| w.set_txe(true)); | ||
| 1262 | }); | 1883 | }); |
| 1263 | 1884 | ||
| 1264 | let state = self.state; | 1885 | let state = self.state; |
| @@ -1274,13 +1895,24 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1274 | Poll::Pending | 1895 | Poll::Pending |
| 1275 | } else if isr.tcr() { | 1896 | } else if isr.tcr() { |
| 1276 | let is_last_slice = remaining_len <= 255; | 1897 | let is_last_slice = remaining_len <= 255; |
| 1277 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { | 1898 | if let Err(e) = Self::reload( |
| 1899 | self.info, | ||
| 1900 | remaining_len.min(255), | ||
| 1901 | !is_last_slice, | ||
| 1902 | Stop::Software, | ||
| 1903 | timeout, | ||
| 1904 | ) { | ||
| 1278 | return Poll::Ready(Err(e)); | 1905 | return Poll::Ready(Err(e)); |
| 1279 | } | 1906 | } |
| 1280 | remaining_len = remaining_len.saturating_sub(255); | 1907 | remaining_len = remaining_len.saturating_sub(255); |
| 1281 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | 1908 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); |
| 1282 | Poll::Pending | 1909 | Poll::Pending |
| 1283 | } else if isr.stopf() { | 1910 | } else if isr.stopf() { |
| 1911 | let mut leftover_bytes = dma_transfer.get_remaining_transfers(); | ||
| 1912 | if !self.info.regs.isr().read().txe() { | ||
| 1913 | leftover_bytes = leftover_bytes.saturating_add(1); | ||
| 1914 | } | ||
| 1915 | remaining_len = remaining_len.saturating_add(leftover_bytes as usize); | ||
| 1284 | self.info.regs.icr().write(|reg| reg.set_stopcf(true)); | 1916 | self.info.regs.icr().write(|reg| reg.set_stopcf(true)); |
| 1285 | if remaining_len > 0 { | 1917 | if remaining_len > 0 { |
| 1286 | dma_transfer.request_pause(); | 1918 | dma_transfer.request_pause(); |
| @@ -1294,6 +1926,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1294 | }) | 1926 | }) |
| 1295 | .await?; | 1927 | .await?; |
| 1296 | 1928 | ||
| 1929 | dma_transfer.request_pause(); | ||
| 1297 | dma_transfer.await; | 1930 | dma_transfer.await; |
| 1298 | 1931 | ||
| 1299 | drop(on_drop); | 1932 | 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..74ce0b29e 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs | |||
| @@ -1,13 +1,16 @@ | |||
| 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::marker::PhantomData; |
| 5 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 5 | use core::task::Poll; | 6 | use core::task::Poll; |
| 6 | 7 | ||
| 8 | use embassy_hal_internal::Peri; | ||
| 7 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 8 | 10 | ||
| 9 | use crate::interrupt::typelevel::Interrupt; | 11 | use crate::interrupt::typelevel::Interrupt; |
| 10 | use crate::peripherals::IPCC; | 12 | use crate::peripherals::IPCC; |
| 13 | use crate::rcc::SealedRccPeripheral; | ||
| 11 | use crate::{interrupt, rcc}; | 14 | use crate::{interrupt, rcc}; |
| 12 | 15 | ||
| 13 | /// Interrupt handler. | 16 | /// Interrupt handler. |
| @@ -17,25 +20,16 @@ impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_RX> for Receive | |||
| 17 | unsafe fn on_interrupt() { | 20 | unsafe fn on_interrupt() { |
| 18 | let regs = IPCC::regs(); | 21 | let regs = IPCC::regs(); |
| 19 | 22 | ||
| 20 | let channels = [ | ||
| 21 | IpccChannel::Channel1, | ||
| 22 | IpccChannel::Channel2, | ||
| 23 | IpccChannel::Channel3, | ||
| 24 | IpccChannel::Channel4, | ||
| 25 | IpccChannel::Channel5, | ||
| 26 | IpccChannel::Channel6, | ||
| 27 | ]; | ||
| 28 | |||
| 29 | // Status register gives channel occupied status. For rx, use cpu1. | 23 | // Status register gives channel occupied status. For rx, use cpu1. |
| 30 | let sr = regs.cpu(1).sr().read(); | 24 | let sr = regs.cpu(1).sr().read(); |
| 31 | regs.cpu(0).mr().modify(|w| { | 25 | regs.cpu(0).mr().modify(|w| { |
| 32 | for channel in channels { | 26 | for index in 0..5 { |
| 33 | if sr.chf(channel as usize) { | 27 | if sr.chf(index as usize) { |
| 34 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt | 28 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt |
| 35 | w.set_chom(channel as usize, true); | 29 | w.set_chom(index as usize, true); |
| 36 | 30 | ||
| 37 | // There shouldn't be a race because the channel is masked only if the interrupt has fired | 31 | // There shouldn't be a race because the channel is masked only if the interrupt has fired |
| 38 | IPCC::state().rx_waker_for(channel).wake(); | 32 | IPCC::state().rx_waker_for(index).wake(); |
| 39 | } | 33 | } |
| 40 | } | 34 | } |
| 41 | }) | 35 | }) |
| @@ -49,25 +43,16 @@ impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_TX> for Transmi | |||
| 49 | unsafe fn on_interrupt() { | 43 | unsafe fn on_interrupt() { |
| 50 | let regs = IPCC::regs(); | 44 | let regs = IPCC::regs(); |
| 51 | 45 | ||
| 52 | let channels = [ | ||
| 53 | IpccChannel::Channel1, | ||
| 54 | IpccChannel::Channel2, | ||
| 55 | IpccChannel::Channel3, | ||
| 56 | IpccChannel::Channel4, | ||
| 57 | IpccChannel::Channel5, | ||
| 58 | IpccChannel::Channel6, | ||
| 59 | ]; | ||
| 60 | |||
| 61 | // Status register gives channel occupied status. For tx, use cpu0. | 46 | // Status register gives channel occupied status. For tx, use cpu0. |
| 62 | let sr = regs.cpu(0).sr().read(); | 47 | let sr = regs.cpu(0).sr().read(); |
| 63 | regs.cpu(0).mr().modify(|w| { | 48 | regs.cpu(0).mr().modify(|w| { |
| 64 | for channel in channels { | 49 | for index in 0..5 { |
| 65 | if !sr.chf(channel as usize) { | 50 | if !sr.chf(index as usize) { |
| 66 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt | 51 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt |
| 67 | w.set_chfm(channel as usize, true); | 52 | w.set_chfm(index as usize, true); |
| 68 | 53 | ||
| 69 | // There shouldn't be a race because the channel is masked only if the interrupt has fired | 54 | // There shouldn't be a race because the channel is masked only if the interrupt has fired |
| 70 | IPCC::state().tx_waker_for(channel).wake(); | 55 | IPCC::state().tx_waker_for(index).wake(); |
| 71 | } | 56 | } |
| 72 | } | 57 | } |
| 73 | }); | 58 | }); |
| @@ -82,76 +67,57 @@ pub struct Config { | |||
| 82 | // reserved for future use | 67 | // reserved for future use |
| 83 | } | 68 | } |
| 84 | 69 | ||
| 85 | /// Channel. | 70 | /// IPCC TX Channel |
| 86 | #[allow(missing_docs)] | 71 | pub struct IpccTxChannel<'a> { |
| 87 | #[derive(Debug, Clone, Copy)] | 72 | index: u8, |
| 88 | #[repr(C)] | 73 | _lifetime: PhantomData<&'a mut usize>, |
| 89 | pub enum IpccChannel { | ||
| 90 | Channel1 = 0, | ||
| 91 | Channel2 = 1, | ||
| 92 | Channel3 = 2, | ||
| 93 | Channel4 = 3, | ||
| 94 | Channel5 = 4, | ||
| 95 | Channel6 = 5, | ||
| 96 | } | 74 | } |
| 97 | 75 | ||
| 98 | /// IPCC driver. | 76 | impl<'a> IpccTxChannel<'a> { |
| 99 | pub struct Ipcc; | 77 | pub(crate) const fn new(index: u8) -> Self { |
| 100 | 78 | core::assert!(index < 6); | |
| 101 | impl Ipcc { | ||
| 102 | /// Enable IPCC. | ||
| 103 | pub fn enable(_config: Config) { | ||
| 104 | rcc::enable_and_reset::<IPCC>(); | ||
| 105 | IPCC::set_cpu2(true); | ||
| 106 | |||
| 107 | let regs = IPCC::regs(); | ||
| 108 | 79 | ||
| 109 | regs.cpu(0).cr().modify(|w| { | 80 | Self { |
| 110 | w.set_rxoie(true); | 81 | index: index, |
| 111 | w.set_txfie(true); | 82 | _lifetime: PhantomData, |
| 112 | }); | 83 | } |
| 113 | |||
| 114 | // enable interrupts | ||
| 115 | crate::interrupt::typelevel::IPCC_C1_RX::unpend(); | ||
| 116 | crate::interrupt::typelevel::IPCC_C1_TX::unpend(); | ||
| 117 | |||
| 118 | unsafe { crate::interrupt::typelevel::IPCC_C1_RX::enable() }; | ||
| 119 | unsafe { crate::interrupt::typelevel::IPCC_C1_TX::enable() }; | ||
| 120 | } | 84 | } |
| 121 | 85 | ||
| 122 | /// Send data to an IPCC channel. The closure is called to write the data when appropriate. | 86 | /// Send data to an IPCC channel. The closure is called to write the data when appropriate. |
| 123 | pub async fn send(channel: IpccChannel, f: impl FnOnce()) { | 87 | pub async fn send(&mut self, f: impl FnOnce()) { |
| 88 | let _scoped_block_stop = IPCC::RCC_INFO.block_stop(); | ||
| 124 | let regs = IPCC::regs(); | 89 | let regs = IPCC::regs(); |
| 125 | 90 | ||
| 126 | Self::flush(channel).await; | 91 | self.flush().await; |
| 127 | 92 | ||
| 128 | f(); | 93 | f(); |
| 129 | 94 | ||
| 130 | compiler_fence(Ordering::SeqCst); | 95 | compiler_fence(Ordering::SeqCst); |
| 131 | 96 | ||
| 132 | trace!("ipcc: ch {}: send data", channel as u8); | 97 | trace!("ipcc: ch {}: send data", self.index as u8); |
| 133 | regs.cpu(0).scr().write(|w| w.set_chs(channel as usize, true)); | 98 | regs.cpu(0).scr().write(|w| w.set_chs(self.index as usize, true)); |
| 134 | } | 99 | } |
| 135 | 100 | ||
| 136 | /// Wait for the tx channel to become clear | 101 | /// Wait for the tx channel to become clear |
| 137 | pub async fn flush(channel: IpccChannel) { | 102 | pub async fn flush(&mut self) { |
| 103 | let _scoped_block_stop = IPCC::RCC_INFO.block_stop(); | ||
| 138 | let regs = IPCC::regs(); | 104 | let regs = IPCC::regs(); |
| 139 | 105 | ||
| 140 | // This is a race, but is nice for debugging | 106 | // This is a race, but is nice for debugging |
| 141 | if regs.cpu(0).sr().read().chf(channel as usize) { | 107 | if regs.cpu(0).sr().read().chf(self.index as usize) { |
| 142 | trace!("ipcc: ch {}: wait for tx free", channel as u8); | 108 | trace!("ipcc: ch {}: wait for tx free", self.index as u8); |
| 143 | } | 109 | } |
| 144 | 110 | ||
| 145 | poll_fn(|cx| { | 111 | poll_fn(|cx| { |
| 146 | IPCC::state().tx_waker_for(channel).register(cx.waker()); | 112 | IPCC::state().tx_waker_for(self.index).register(cx.waker()); |
| 147 | // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt | 113 | // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt |
| 148 | regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, false)); | 114 | regs.cpu(0).mr().modify(|w| w.set_chfm(self.index as usize, false)); |
| 149 | 115 | ||
| 150 | compiler_fence(Ordering::SeqCst); | 116 | compiler_fence(Ordering::SeqCst); |
| 151 | 117 | ||
| 152 | if !regs.cpu(0).sr().read().chf(channel as usize) { | 118 | if !regs.cpu(0).sr().read().chf(self.index as usize) { |
| 153 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt | 119 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt |
| 154 | regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true)); | 120 | regs.cpu(0).mr().modify(|w| w.set_chfm(self.index as usize, true)); |
| 155 | 121 | ||
| 156 | Poll::Ready(()) | 122 | Poll::Ready(()) |
| 157 | } else { | 123 | } else { |
| @@ -160,27 +126,45 @@ impl Ipcc { | |||
| 160 | }) | 126 | }) |
| 161 | .await; | 127 | .await; |
| 162 | } | 128 | } |
| 129 | } | ||
| 130 | |||
| 131 | /// IPCC RX Channel | ||
| 132 | pub struct IpccRxChannel<'a> { | ||
| 133 | index: u8, | ||
| 134 | _lifetime: PhantomData<&'a mut usize>, | ||
| 135 | } | ||
| 136 | |||
| 137 | impl<'a> IpccRxChannel<'a> { | ||
| 138 | pub(crate) const fn new(index: u8) -> Self { | ||
| 139 | core::assert!(index < 6); | ||
| 140 | |||
| 141 | Self { | ||
| 142 | index: index, | ||
| 143 | _lifetime: PhantomData, | ||
| 144 | } | ||
| 145 | } | ||
| 163 | 146 | ||
| 164 | /// Receive data from an IPCC channel. The closure is called to read the data when appropriate. | 147 | /// Receive data from an IPCC channel. The closure is called to read the data when appropriate. |
| 165 | pub async fn receive<R>(channel: IpccChannel, mut f: impl FnMut() -> Option<R>) -> R { | 148 | pub async fn receive<R>(&mut self, mut f: impl FnMut() -> Option<R>) -> R { |
| 149 | let _scoped_block_stop = IPCC::RCC_INFO.block_stop(); | ||
| 166 | let regs = IPCC::regs(); | 150 | let regs = IPCC::regs(); |
| 167 | 151 | ||
| 168 | loop { | 152 | loop { |
| 169 | // This is a race, but is nice for debugging | 153 | // This is a race, but is nice for debugging |
| 170 | if !regs.cpu(1).sr().read().chf(channel as usize) { | 154 | if !regs.cpu(1).sr().read().chf(self.index as usize) { |
| 171 | trace!("ipcc: ch {}: wait for rx occupied", channel as u8); | 155 | trace!("ipcc: ch {}: wait for rx occupied", self.index as u8); |
| 172 | } | 156 | } |
| 173 | 157 | ||
| 174 | poll_fn(|cx| { | 158 | poll_fn(|cx| { |
| 175 | IPCC::state().rx_waker_for(channel).register(cx.waker()); | 159 | IPCC::state().rx_waker_for(self.index).register(cx.waker()); |
| 176 | // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt | 160 | // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt |
| 177 | regs.cpu(0).mr().modify(|w| w.set_chom(channel as usize, false)); | 161 | regs.cpu(0).mr().modify(|w| w.set_chom(self.index as usize, false)); |
| 178 | 162 | ||
| 179 | compiler_fence(Ordering::SeqCst); | 163 | compiler_fence(Ordering::SeqCst); |
| 180 | 164 | ||
| 181 | if regs.cpu(1).sr().read().chf(channel as usize) { | 165 | if regs.cpu(1).sr().read().chf(self.index as usize) { |
| 182 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt | 166 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt |
| 183 | regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true)); | 167 | regs.cpu(0).mr().modify(|w| w.set_chfm(self.index as usize, true)); |
| 184 | 168 | ||
| 185 | Poll::Ready(()) | 169 | Poll::Ready(()) |
| 186 | } else { | 170 | } else { |
| @@ -189,21 +173,114 @@ impl Ipcc { | |||
| 189 | }) | 173 | }) |
| 190 | .await; | 174 | .await; |
| 191 | 175 | ||
| 192 | trace!("ipcc: ch {}: read data", channel as u8); | 176 | trace!("ipcc: ch {}: read data", self.index as u8); |
| 193 | 177 | ||
| 194 | match f() { | 178 | match f() { |
| 195 | Some(ret) => return ret, | 179 | Some(ret) => return ret, |
| 196 | None => {} | 180 | None => {} |
| 197 | } | 181 | } |
| 198 | 182 | ||
| 199 | trace!("ipcc: ch {}: clear rx", channel as u8); | 183 | trace!("ipcc: ch {}: clear rx", self.index as u8); |
| 200 | compiler_fence(Ordering::SeqCst); | 184 | compiler_fence(Ordering::SeqCst); |
| 201 | // If the channel is clear and the read function returns none, fetch more data | 185 | // If the channel is clear and the read function returns none, fetch more data |
| 202 | regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)); | 186 | regs.cpu(0).scr().write(|w| w.set_chc(self.index as usize, true)); |
| 203 | } | 187 | } |
| 204 | } | 188 | } |
| 205 | } | 189 | } |
| 206 | 190 | ||
| 191 | /// IPCC Channel | ||
| 192 | pub struct IpccChannel<'a> { | ||
| 193 | index: u8, | ||
| 194 | _lifetime: PhantomData<&'a mut usize>, | ||
| 195 | } | ||
| 196 | |||
| 197 | impl<'a> IpccChannel<'a> { | ||
| 198 | pub(crate) const fn new(number: u8) -> Self { | ||
| 199 | core::assert!(number > 0 && number <= 6); | ||
| 200 | |||
| 201 | Self { | ||
| 202 | index: number - 1, | ||
| 203 | _lifetime: PhantomData, | ||
| 204 | } | ||
| 205 | } | ||
| 206 | |||
| 207 | /// Split into a tx and rx channel | ||
| 208 | pub const fn split(self) -> (IpccTxChannel<'a>, IpccRxChannel<'a>) { | ||
| 209 | (IpccTxChannel::new(self.index), IpccRxChannel::new(self.index)) | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | /// IPCC driver. | ||
| 214 | pub struct Ipcc { | ||
| 215 | _private: (), | ||
| 216 | } | ||
| 217 | |||
| 218 | impl Ipcc { | ||
| 219 | /// Creates a new HardwareSemaphore instance. | ||
| 220 | pub fn new<'d>( | ||
| 221 | _peripheral: Peri<'d, crate::peripherals::IPCC>, | ||
| 222 | _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_RX, ReceiveInterruptHandler> | ||
| 223 | + interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_TX, TransmitInterruptHandler> | ||
| 224 | + 'd, | ||
| 225 | _config: Config, | ||
| 226 | ) -> Self { | ||
| 227 | rcc::enable_and_reset_without_stop::<IPCC>(); | ||
| 228 | IPCC::set_cpu2(true); | ||
| 229 | |||
| 230 | // Verify rfwkpsel is set | ||
| 231 | let _ = IPCC::frequency(); | ||
| 232 | |||
| 233 | let regs = IPCC::regs(); | ||
| 234 | |||
| 235 | regs.cpu(0).cr().modify(|w| { | ||
| 236 | w.set_rxoie(true); | ||
| 237 | w.set_txfie(true); | ||
| 238 | }); | ||
| 239 | |||
| 240 | // enable interrupts | ||
| 241 | crate::interrupt::typelevel::IPCC_C1_RX::unpend(); | ||
| 242 | crate::interrupt::typelevel::IPCC_C1_TX::unpend(); | ||
| 243 | |||
| 244 | unsafe { crate::interrupt::typelevel::IPCC_C1_RX::enable() }; | ||
| 245 | unsafe { crate::interrupt::typelevel::IPCC_C1_TX::enable() }; | ||
| 246 | |||
| 247 | Self { _private: () } | ||
| 248 | } | ||
| 249 | |||
| 250 | /// Split into a tx and rx channel | ||
| 251 | pub const fn split<'a>(self) -> [(IpccTxChannel<'a>, IpccRxChannel<'a>); 6] { | ||
| 252 | [ | ||
| 253 | IpccChannel::new(1).split(), | ||
| 254 | IpccChannel::new(2).split(), | ||
| 255 | IpccChannel::new(3).split(), | ||
| 256 | IpccChannel::new(4).split(), | ||
| 257 | IpccChannel::new(5).split(), | ||
| 258 | IpccChannel::new(6).split(), | ||
| 259 | ] | ||
| 260 | } | ||
| 261 | |||
| 262 | /// Receive from a channel number | ||
| 263 | pub async unsafe fn receive<R>(number: u8, f: impl FnMut() -> Option<R>) -> R { | ||
| 264 | core::assert!(number > 0 && number <= 6); | ||
| 265 | |||
| 266 | IpccRxChannel::new(number - 1).receive(f).await | ||
| 267 | } | ||
| 268 | |||
| 269 | /// Send to a channel number | ||
| 270 | pub async unsafe fn send(number: u8, f: impl FnOnce()) { | ||
| 271 | core::assert!(number > 0 && number <= 6); | ||
| 272 | |||
| 273 | IpccTxChannel::new(number - 1).send(f).await | ||
| 274 | } | ||
| 275 | |||
| 276 | /// Send to a channel number | ||
| 277 | pub async unsafe fn flush(number: u8) { | ||
| 278 | core::assert!(number > 0 && number <= 6); | ||
| 279 | |||
| 280 | IpccTxChannel::new(number - 1).flush().await | ||
| 281 | } | ||
| 282 | } | ||
| 283 | |||
| 207 | impl SealedInstance for crate::peripherals::IPCC { | 284 | impl SealedInstance for crate::peripherals::IPCC { |
| 208 | fn regs() -> crate::pac::ipcc::Ipcc { | 285 | fn regs() -> crate::pac::ipcc::Ipcc { |
| 209 | crate::pac::IPCC | 286 | crate::pac::IPCC |
| @@ -232,26 +309,12 @@ impl State { | |||
| 232 | } | 309 | } |
| 233 | } | 310 | } |
| 234 | 311 | ||
| 235 | const fn rx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker { | 312 | const fn rx_waker_for(&self, index: u8) -> &AtomicWaker { |
| 236 | match channel { | 313 | &self.rx_wakers[index as usize] |
| 237 | IpccChannel::Channel1 => &self.rx_wakers[0], | ||
| 238 | IpccChannel::Channel2 => &self.rx_wakers[1], | ||
| 239 | IpccChannel::Channel3 => &self.rx_wakers[2], | ||
| 240 | IpccChannel::Channel4 => &self.rx_wakers[3], | ||
| 241 | IpccChannel::Channel5 => &self.rx_wakers[4], | ||
| 242 | IpccChannel::Channel6 => &self.rx_wakers[5], | ||
| 243 | } | ||
| 244 | } | 314 | } |
| 245 | 315 | ||
| 246 | const fn tx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker { | 316 | const fn tx_waker_for(&self, index: u8) -> &AtomicWaker { |
| 247 | match channel { | 317 | &self.tx_wakers[index as usize] |
| 248 | IpccChannel::Channel1 => &self.tx_wakers[0], | ||
| 249 | IpccChannel::Channel2 => &self.tx_wakers[1], | ||
| 250 | IpccChannel::Channel3 => &self.tx_wakers[2], | ||
| 251 | IpccChannel::Channel4 => &self.tx_wakers[3], | ||
| 252 | IpccChannel::Channel5 => &self.tx_wakers[4], | ||
| 253 | IpccChannel::Channel6 => &self.tx_wakers[5], | ||
| 254 | } | ||
| 255 | } | 318 | } |
| 256 | } | 319 | } |
| 257 | 320 | ||
diff --git a/embassy-stm32/src/lcd.rs b/embassy-stm32/src/lcd.rs new file mode 100644 index 000000000..ea29f1398 --- /dev/null +++ b/embassy-stm32/src/lcd.rs | |||
| @@ -0,0 +1,510 @@ | |||
| 1 | //! LCD | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | |||
| 4 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 5 | |||
| 6 | use crate::gpio::{AfType, AnyPin, SealedPin}; | ||
| 7 | use crate::peripherals; | ||
| 8 | use crate::rcc::{self, RccPeripheral}; | ||
| 9 | use crate::time::Hertz; | ||
| 10 | |||
| 11 | #[cfg(any(stm32u0, stm32l073, stm32l083))] | ||
| 12 | const NUM_SEGMENTS: u8 = 52; | ||
| 13 | #[cfg(any(stm32wb, stm32l4x6, stm32l15x, stm32l162, stm32l4x3, stm32l4x6))] | ||
| 14 | const NUM_SEGMENTS: u8 = 44; | ||
| 15 | #[cfg(any(stm32l053, stm32l063, stm32l100))] | ||
| 16 | const NUM_SEGMENTS: u8 = 32; | ||
| 17 | |||
| 18 | /// LCD configuration struct | ||
| 19 | #[non_exhaustive] | ||
| 20 | #[derive(Debug, Clone, Copy)] | ||
| 21 | pub struct Config { | ||
| 22 | #[cfg(lcd_v2)] | ||
| 23 | /// Enable the voltage output buffer for higher driving capability. | ||
| 24 | /// | ||
| 25 | /// The LCD driving capability is improved as buffers prevent the LCD capacitive loads from loading the resistor | ||
| 26 | /// bridge unacceptably and interfering with its voltage generation. | ||
| 27 | pub use_voltage_output_buffer: bool, | ||
| 28 | /// Enable SEG pin remapping. SEG[31:28] multiplexed with SEG[43:40] | ||
| 29 | pub use_segment_muxing: bool, | ||
| 30 | /// Bias selector | ||
| 31 | pub bias: Bias, | ||
| 32 | /// Duty selector | ||
| 33 | pub duty: Duty, | ||
| 34 | /// Internal or external voltage source | ||
| 35 | pub voltage_source: VoltageSource, | ||
| 36 | /// The frequency used to update the LCD with. | ||
| 37 | /// Should be between ~30 and ~100. Lower is better for power consumption, but has lower visual fidelity. | ||
| 38 | pub target_fps: Hertz, | ||
| 39 | /// LCD driver selector | ||
| 40 | pub drive: Drive, | ||
| 41 | } | ||
| 42 | |||
| 43 | impl Default for Config { | ||
| 44 | fn default() -> Self { | ||
| 45 | Self { | ||
| 46 | #[cfg(lcd_v2)] | ||
| 47 | use_voltage_output_buffer: false, | ||
| 48 | use_segment_muxing: false, | ||
| 49 | bias: Default::default(), | ||
| 50 | duty: Default::default(), | ||
| 51 | voltage_source: Default::default(), | ||
| 52 | target_fps: Hertz(60), | ||
| 53 | drive: Drive::Medium, | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | /// The number of voltage levels used when driving an LCD. | ||
| 59 | /// Your LCD datasheet should tell you what to use. | ||
| 60 | #[repr(u8)] | ||
| 61 | #[derive(Debug, Default, Clone, Copy)] | ||
| 62 | pub enum Bias { | ||
| 63 | /// 1/4 bias | ||
| 64 | #[default] | ||
| 65 | Quarter = 0b00, | ||
| 66 | /// 1/2 bias | ||
| 67 | Half = 0b01, | ||
| 68 | /// 1/3 bias | ||
| 69 | Third = 0b10, | ||
| 70 | } | ||
| 71 | |||
| 72 | /// The duty used by the LCD driver. | ||
| 73 | /// | ||
| 74 | /// This is essentially how many COM pins you're using. | ||
| 75 | #[repr(u8)] | ||
| 76 | #[derive(Debug, Default, Clone, Copy)] | ||
| 77 | pub enum Duty { | ||
| 78 | #[default] | ||
| 79 | /// Use a single COM pin | ||
| 80 | Static = 0b000, | ||
| 81 | /// Use two COM pins | ||
| 82 | Half = 0b001, | ||
| 83 | /// Use three COM pins | ||
| 84 | Third = 0b010, | ||
| 85 | /// Use four COM pins | ||
| 86 | Quarter = 0b011, | ||
| 87 | /// Use eight COM pins. | ||
| 88 | /// | ||
| 89 | /// In this mode, `COM[7:4]` outputs are available on `SEG[51:48]`. | ||
| 90 | /// This allows reducing the number of available segments. | ||
| 91 | Eigth = 0b100, | ||
| 92 | } | ||
| 93 | |||
| 94 | impl Duty { | ||
| 95 | fn num_com_pins(&self) -> u8 { | ||
| 96 | match self { | ||
| 97 | Duty::Static => 1, | ||
| 98 | Duty::Half => 2, | ||
| 99 | Duty::Third => 3, | ||
| 100 | Duty::Quarter => 4, | ||
| 101 | Duty::Eigth => 8, | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Whether to use the internal or external voltage source to drive the LCD | ||
| 107 | #[repr(u8)] | ||
| 108 | #[derive(Debug, Default, Clone, Copy)] | ||
| 109 | pub enum VoltageSource { | ||
| 110 | #[default] | ||
| 111 | /// Voltage stepup converter | ||
| 112 | Internal, | ||
| 113 | /// VLCD pin | ||
| 114 | External, | ||
| 115 | } | ||
| 116 | |||
| 117 | /// Defines the pulse duration in terms of ck_ps pulses. | ||
| 118 | /// | ||
| 119 | /// A short pulse leads to lower power consumption, but displays with high internal resistance | ||
| 120 | /// may need a longer pulse to achieve satisfactory contrast. | ||
| 121 | /// Note that the pulse is never longer than one half prescaled LCD clock period. | ||
| 122 | /// | ||
| 123 | /// Displays with high internal resistance may need a longer drive time to achieve satisfactory contrast. | ||
| 124 | /// `PermanentHighDrive` is useful in this case if some additional power consumption can be tolerated. | ||
| 125 | /// | ||
| 126 | /// Basically, for power usage, you want this as low as possible while still being able to use the LCD | ||
| 127 | /// with a good enough contrast. | ||
| 128 | #[repr(u8)] | ||
| 129 | #[derive(Debug, Clone, Copy)] | ||
| 130 | pub enum Drive { | ||
| 131 | /// Zero clock pulse on duration | ||
| 132 | Lowest = 0x00, | ||
| 133 | /// One clock pulse on duration | ||
| 134 | VeryLow = 0x01, | ||
| 135 | /// Two clock pulse on duration | ||
| 136 | Low = 0x02, | ||
| 137 | /// Three clock pulse on duration | ||
| 138 | Medium = 0x03, | ||
| 139 | /// Four clock pulse on duration | ||
| 140 | MediumHigh = 0x04, | ||
| 141 | /// Five clock pulse on duration | ||
| 142 | High = 0x05, | ||
| 143 | /// Six clock pulse on duration | ||
| 144 | VeryHigh = 0x06, | ||
| 145 | /// Seven clock pulse on duration | ||
| 146 | Highest = 0x07, | ||
| 147 | /// Enables the highdrive bit of the hardware | ||
| 148 | PermanentHighDrive = 0x09, | ||
| 149 | } | ||
| 150 | |||
| 151 | /// LCD driver. | ||
| 152 | pub struct Lcd<'d, T: Instance> { | ||
| 153 | _peri: PhantomData<&'d mut T>, | ||
| 154 | duty: Duty, | ||
| 155 | ck_div: u32, | ||
| 156 | } | ||
| 157 | |||
| 158 | impl<'d, T: Instance> Lcd<'d, T> { | ||
| 159 | /// Initialize the lcd driver. | ||
| 160 | /// | ||
| 161 | /// The `pins` parameter must contain *all* segment and com pins that are connected to the LCD. | ||
| 162 | /// This is not further checked by this driver. Pins not routed to the LCD can be used for other purposes. | ||
| 163 | pub fn new<const N: usize>( | ||
| 164 | _peripheral: Peri<'d, T>, | ||
| 165 | config: Config, | ||
| 166 | vlcd_pin: Peri<'_, impl VlcdPin<T>>, | ||
| 167 | pins: [LcdPin<'d, T>; N], | ||
| 168 | ) -> Self { | ||
| 169 | rcc::enable_and_reset::<T>(); | ||
| 170 | |||
| 171 | vlcd_pin.set_as_af( | ||
| 172 | vlcd_pin.af_num(), | ||
| 173 | AfType::output(crate::gpio::OutputType::PushPull, crate::gpio::Speed::VeryHigh), | ||
| 174 | ); | ||
| 175 | |||
| 176 | assert_eq!( | ||
| 177 | pins.iter().filter(|pin| !pin.is_seg).count(), | ||
| 178 | config.duty.num_com_pins() as usize, | ||
| 179 | "The number of provided COM pins is not the same as the duty configures" | ||
| 180 | ); | ||
| 181 | |||
| 182 | // Set the pins | ||
| 183 | for pin in pins { | ||
| 184 | pin.pin.set_as_af( | ||
| 185 | pin.af_num, | ||
| 186 | AfType::output(crate::gpio::OutputType::PushPull, crate::gpio::Speed::VeryHigh), | ||
| 187 | ); | ||
| 188 | } | ||
| 189 | |||
| 190 | // Initialize the display ram to 0 | ||
| 191 | for i in 0..8 { | ||
| 192 | T::regs().ram_com(i).low().write_value(0); | ||
| 193 | T::regs().ram_com(i).high().write_value(0); | ||
| 194 | } | ||
| 195 | |||
| 196 | // Calculate the clock dividers | ||
| 197 | let Some(lcd_clk) = (unsafe { rcc::get_freqs().rtc.to_hertz() }) else { | ||
| 198 | panic!("The LCD driver needs the RTC/LCD clock to be running"); | ||
| 199 | }; | ||
| 200 | let duty_divider = match config.duty { | ||
| 201 | Duty::Static => 1, | ||
| 202 | Duty::Half => 2, | ||
| 203 | Duty::Third => 3, | ||
| 204 | Duty::Quarter => 4, | ||
| 205 | Duty::Eigth => 8, | ||
| 206 | }; | ||
| 207 | let target_clock = config.target_fps.0 * duty_divider; | ||
| 208 | let target_division = lcd_clk.0 / target_clock; | ||
| 209 | |||
| 210 | let mut ps = 0; | ||
| 211 | let mut div = 0; | ||
| 212 | let mut best_fps_match = u32::MAX; | ||
| 213 | |||
| 214 | for trial_div in 0..0xF { | ||
| 215 | let trial_ps = (target_division / (trial_div + 16)) | ||
| 216 | .next_power_of_two() | ||
| 217 | .trailing_zeros(); | ||
| 218 | let fps = lcd_clk.0 / ((1 << trial_ps) * (trial_div + 16)) / duty_divider; | ||
| 219 | |||
| 220 | if fps < config.target_fps.0 { | ||
| 221 | continue; | ||
| 222 | } | ||
| 223 | |||
| 224 | if fps < best_fps_match { | ||
| 225 | ps = trial_ps; | ||
| 226 | div = trial_div; | ||
| 227 | best_fps_match = fps; | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | let ck_div = lcd_clk.0 / ((1 << ps) * (div + 16)); | ||
| 232 | |||
| 233 | trace!( | ||
| 234 | "lcd_clk: {}, fps: {}, ps: {}, div: {}, ck_div: {}", | ||
| 235 | lcd_clk, best_fps_match, ps, div, ck_div | ||
| 236 | ); | ||
| 237 | |||
| 238 | if best_fps_match == u32::MAX || ps > 0xF { | ||
| 239 | panic!("Lcd clock error"); | ||
| 240 | } | ||
| 241 | |||
| 242 | // Set the frame control | ||
| 243 | T::regs().fcr().modify(|w| { | ||
| 244 | w.set_ps(ps as u8); | ||
| 245 | w.set_div(div as u8); | ||
| 246 | w.set_cc(0b100); // Init in the middle-ish | ||
| 247 | w.set_dead(0b000); | ||
| 248 | w.set_pon(config.drive as u8 & 0x07); | ||
| 249 | w.set_hd((config.drive as u8 & !0x07) != 0); | ||
| 250 | }); | ||
| 251 | |||
| 252 | // Wait for the frame control to synchronize | ||
| 253 | while !T::regs().sr().read().fcrsf() {} | ||
| 254 | |||
| 255 | // Set the control register values | ||
| 256 | T::regs().cr().modify(|w| { | ||
| 257 | #[cfg(lcd_v2)] | ||
| 258 | w.set_bufen(config.use_voltage_output_buffer); | ||
| 259 | w.set_mux_seg(config.use_segment_muxing); | ||
| 260 | w.set_bias(config.bias as u8); | ||
| 261 | w.set_duty(config.duty as u8); | ||
| 262 | w.set_vsel(matches!(config.voltage_source, VoltageSource::External)); | ||
| 263 | }); | ||
| 264 | |||
| 265 | // Enable the lcd | ||
| 266 | T::regs().cr().modify(|w| w.set_lcden(true)); | ||
| 267 | |||
| 268 | // Wait for the lcd to be enabled | ||
| 269 | while !T::regs().sr().read().ens() {} | ||
| 270 | |||
| 271 | // Wait for the stepup converter to be ready | ||
| 272 | while !T::regs().sr().read().rdy() {} | ||
| 273 | |||
| 274 | Self { | ||
| 275 | _peri: PhantomData, | ||
| 276 | duty: config.duty, | ||
| 277 | ck_div, | ||
| 278 | } | ||
| 279 | } | ||
| 280 | |||
| 281 | /// Change the contrast by changing the voltage being used. | ||
| 282 | /// | ||
| 283 | /// This is from low at 0 to high at 7. | ||
| 284 | pub fn set_contrast_control(&mut self, value: u8) { | ||
| 285 | assert!((0..=7).contains(&value)); | ||
| 286 | T::regs().fcr().modify(|w| w.set_cc(value)); | ||
| 287 | } | ||
| 288 | |||
| 289 | /// Change the contrast by introducing a deadtime to the signals | ||
| 290 | /// where the voltages are held at 0V. | ||
| 291 | /// | ||
| 292 | /// This is from no dead time at 0 to high dead time at 7. | ||
| 293 | pub fn set_dead_time(&mut self, value: u8) { | ||
| 294 | assert!((0..=7).contains(&value)); | ||
| 295 | T::regs() | ||
| 296 | .fcr() | ||
| 297 | .modify(|w: &mut stm32_metapac::lcd::regs::Fcr| w.set_dead(value)); | ||
| 298 | } | ||
| 299 | |||
| 300 | /// Write data into the display RAM. This overwrites the data already in it for the specified com index. | ||
| 301 | /// | ||
| 302 | /// The `com_index` value determines which part of the RAM is written to. | ||
| 303 | /// The `segments` value is a bitmap where each bit represents whether a pixel is turned on or off. | ||
| 304 | /// | ||
| 305 | /// This function waits last update request to be finished, but does not submit the buffer to the LCD with a new request. | ||
| 306 | /// Submission has to be done manually using [Self::submit_frame]. | ||
| 307 | pub fn write_com_segments(&mut self, com_index: u8, segments: u64) { | ||
| 308 | while T::regs().sr().read().udr() {} | ||
| 309 | |||
| 310 | assert!( | ||
| 311 | com_index < self.duty.num_com_pins(), | ||
| 312 | "Com index cannot be higher than number of configured com pins (through the Duty setting in the config)" | ||
| 313 | ); | ||
| 314 | |||
| 315 | assert!( | ||
| 316 | segments.leading_zeros() >= 64 - self.num_segments() as u32, | ||
| 317 | "Invalid segment pixel set", | ||
| 318 | ); | ||
| 319 | |||
| 320 | T::regs() | ||
| 321 | .ram_com(com_index as usize) | ||
| 322 | .low() | ||
| 323 | .write_value((segments & 0xFFFF_FFFF) as u32); | ||
| 324 | T::regs() | ||
| 325 | .ram_com(com_index as usize) | ||
| 326 | .high() | ||
| 327 | .write_value(((segments >> 32) & 0xFFFF_FFFF) as u32); | ||
| 328 | } | ||
| 329 | |||
| 330 | /// Read the data from the display RAM. | ||
| 331 | /// | ||
| 332 | /// The `com_index` value determines which part of the RAM is read from. | ||
| 333 | /// | ||
| 334 | /// This function waits for the last update request to be finished. | ||
| 335 | pub fn read_com_segments(&self, com_index: u8) -> u64 { | ||
| 336 | while T::regs().sr().read().udr() {} | ||
| 337 | |||
| 338 | assert!( | ||
| 339 | com_index < self.duty.num_com_pins(), | ||
| 340 | "Com index cannot be higher than number of configured com pins (through the Duty setting in the config)" | ||
| 341 | ); | ||
| 342 | |||
| 343 | let low = T::regs().ram_com(com_index as usize).low().read(); | ||
| 344 | let high = T::regs().ram_com(com_index as usize).high().read(); | ||
| 345 | |||
| 346 | ((high as u64) << 32) | low as u64 | ||
| 347 | } | ||
| 348 | |||
| 349 | /// Submit the current RAM data to the LCD. | ||
| 350 | /// | ||
| 351 | /// This function waits until the RAM is writable, but does not wait for the frame to be drawn. | ||
| 352 | pub fn submit_frame(&mut self) { | ||
| 353 | while T::regs().sr().read().udr() {} | ||
| 354 | // Clear the update done flag | ||
| 355 | T::regs().sr().write(|w| w.set_udd(true)); | ||
| 356 | // Set the update request flag | ||
| 357 | T::regs().sr().write(|w| w.set_udr(true)); | ||
| 358 | } | ||
| 359 | |||
| 360 | /// Get the number of segments that are supported on this LCD | ||
| 361 | pub fn num_segments(&self) -> u8 { | ||
| 362 | match self.duty { | ||
| 363 | Duty::Eigth => NUM_SEGMENTS - 4, // With 8 coms, 4 of the segment pins turn into com pins | ||
| 364 | _ => NUM_SEGMENTS, | ||
| 365 | } | ||
| 366 | } | ||
| 367 | |||
| 368 | /// Get the pixel mask for the current LCD setup. | ||
| 369 | /// This is a mask of all bits that are allowed to be set in the [Self::write_com_segments] function. | ||
| 370 | pub fn segment_pixel_mask(&self) -> u64 { | ||
| 371 | (1 << self.num_segments()) - 1 | ||
| 372 | } | ||
| 373 | |||
| 374 | /// Get the number of COM pins that were configured through the Drive config | ||
| 375 | pub fn num_com_pins(&self) -> u8 { | ||
| 376 | self.duty.num_com_pins() | ||
| 377 | } | ||
| 378 | |||
| 379 | /// Set the blink behavior on some pixels. | ||
| 380 | /// | ||
| 381 | /// The blink frequency is an approximation. It's divided from the clock selected by the FPS. | ||
| 382 | /// Play with the FPS value if you want the blink frequency to be more accurate. | ||
| 383 | /// | ||
| 384 | /// If a blink frequency cannot be attained, this function will panic. | ||
| 385 | pub fn set_blink(&mut self, selector: BlinkSelector, freq: BlinkFreq) { | ||
| 386 | // Freq * 100 to be able to do integer math | ||
| 387 | let scaled_blink_freq = match freq { | ||
| 388 | BlinkFreq::Hz0_25 => 25, | ||
| 389 | BlinkFreq::Hz0_5 => 50, | ||
| 390 | BlinkFreq::Hz1 => 100, | ||
| 391 | BlinkFreq::Hz2 => 200, | ||
| 392 | BlinkFreq::Hz4 => 400, | ||
| 393 | }; | ||
| 394 | |||
| 395 | let desired_divider = self.ck_div * 100 / scaled_blink_freq; | ||
| 396 | let target_divider = desired_divider.next_power_of_two(); | ||
| 397 | let power_divisions = target_divider.trailing_zeros(); | ||
| 398 | |||
| 399 | trace!( | ||
| 400 | "Setting LCD blink frequency -> desired_divider: {}, target_divider: {}", | ||
| 401 | desired_divider, target_divider | ||
| 402 | ); | ||
| 403 | |||
| 404 | assert!( | ||
| 405 | (8..=1024).contains(&target_divider), | ||
| 406 | "LCD blink frequency cannot be attained" | ||
| 407 | ); | ||
| 408 | |||
| 409 | T::regs().fcr().modify(|reg| { | ||
| 410 | reg.set_blinkf((power_divisions - 3) as u8); | ||
| 411 | reg.set_blink(selector as u8); | ||
| 412 | }) | ||
| 413 | } | ||
| 414 | } | ||
| 415 | |||
| 416 | impl<'d, T: Instance> Drop for Lcd<'d, T> { | ||
| 417 | fn drop(&mut self) { | ||
| 418 | // Disable the lcd | ||
| 419 | T::regs().cr().modify(|w| w.set_lcden(false)); | ||
| 420 | rcc::disable::<T>(); | ||
| 421 | } | ||
| 422 | } | ||
| 423 | |||
| 424 | /// Blink frequency | ||
| 425 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 426 | pub enum BlinkFreq { | ||
| 427 | /// 0.25 hz | ||
| 428 | Hz0_25, | ||
| 429 | /// 0.5 hz | ||
| 430 | Hz0_5, | ||
| 431 | /// 1 hz | ||
| 432 | Hz1, | ||
| 433 | /// 2 hz | ||
| 434 | Hz2, | ||
| 435 | /// 4 hz | ||
| 436 | Hz4, | ||
| 437 | } | ||
| 438 | |||
| 439 | /// Blink pixel selector | ||
| 440 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 441 | #[repr(u8)] | ||
| 442 | pub enum BlinkSelector { | ||
| 443 | /// No pixels blink | ||
| 444 | None = 0b00, | ||
| 445 | /// The SEG0, COM0 pixel blinks if the pixel is set | ||
| 446 | Seg0Com0 = 0b01, | ||
| 447 | /// The SEG0 pixel of all COMs blinks if the pixel is set | ||
| 448 | Seg0ComAll = 0b10, | ||
| 449 | /// All pixels blink if the pixel is set | ||
| 450 | All = 0b11, | ||
| 451 | } | ||
| 452 | |||
| 453 | /// A type-erased pin that can be configured as an LCD pin. | ||
| 454 | /// This is used for passing pins to the new function in the array. | ||
| 455 | pub struct LcdPin<'d, T: Instance> { | ||
| 456 | pin: Peri<'d, AnyPin>, | ||
| 457 | af_num: u8, | ||
| 458 | is_seg: bool, | ||
| 459 | _phantom: PhantomData<T>, | ||
| 460 | } | ||
| 461 | |||
| 462 | impl<'d, T: Instance> LcdPin<'d, T> { | ||
| 463 | /// Construct an LCD pin from any pin that supports it | ||
| 464 | pub fn new_seg(pin: Peri<'d, impl SegPin<T>>) -> Self { | ||
| 465 | let af = pin.af_num(); | ||
| 466 | |||
| 467 | Self { | ||
| 468 | pin: pin.into(), | ||
| 469 | af_num: af, | ||
| 470 | is_seg: true, | ||
| 471 | _phantom: PhantomData, | ||
| 472 | } | ||
| 473 | } | ||
| 474 | |||
| 475 | /// Construct an LCD pin from any pin that supports it | ||
| 476 | pub fn new_com(pin: Peri<'d, impl ComPin<T>>) -> Self { | ||
| 477 | let af = pin.af_num(); | ||
| 478 | |||
| 479 | Self { | ||
| 480 | pin: pin.into(), | ||
| 481 | af_num: af, | ||
| 482 | is_seg: false, | ||
| 483 | _phantom: PhantomData, | ||
| 484 | } | ||
| 485 | } | ||
| 486 | } | ||
| 487 | |||
| 488 | trait SealedInstance: crate::rcc::SealedRccPeripheral + PeripheralType { | ||
| 489 | fn regs() -> crate::pac::lcd::Lcd; | ||
| 490 | } | ||
| 491 | |||
| 492 | /// DSI instance trait. | ||
| 493 | #[allow(private_bounds)] | ||
| 494 | pub trait Instance: SealedInstance + RccPeripheral + 'static {} | ||
| 495 | |||
| 496 | pin_trait!(SegPin, Instance); | ||
| 497 | pin_trait!(ComPin, Instance); | ||
| 498 | pin_trait!(VlcdPin, Instance); | ||
| 499 | |||
| 500 | foreach_peripheral!( | ||
| 501 | (lcd, $inst:ident) => { | ||
| 502 | impl crate::lcd::SealedInstance for peripherals::$inst { | ||
| 503 | fn regs() -> crate::pac::lcd::Lcd { | ||
| 504 | crate::pac::$inst | ||
| 505 | } | ||
| 506 | } | ||
| 507 | |||
| 508 | impl crate::lcd::Instance for peripherals::$inst {} | ||
| 509 | }; | ||
| 510 | ); | ||
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 7e0f7884e..2f783bf64 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 |
| @@ -74,6 +77,7 @@ pub mod dts; | |||
| 74 | pub mod eth; | 77 | pub mod eth; |
| 75 | #[cfg(feature = "exti")] | 78 | #[cfg(feature = "exti")] |
| 76 | pub mod exti; | 79 | pub mod exti; |
| 80 | #[cfg(flash)] | ||
| 77 | pub mod flash; | 81 | pub mod flash; |
| 78 | #[cfg(fmc)] | 82 | #[cfg(fmc)] |
| 79 | pub mod fmc; | 83 | pub mod fmc; |
| @@ -91,6 +95,8 @@ pub mod i2c; | |||
| 91 | pub mod i2s; | 95 | pub mod i2s; |
| 92 | #[cfg(stm32wb)] | 96 | #[cfg(stm32wb)] |
| 93 | pub mod ipcc; | 97 | pub mod ipcc; |
| 98 | #[cfg(lcd)] | ||
| 99 | pub mod lcd; | ||
| 94 | #[cfg(feature = "low-power")] | 100 | #[cfg(feature = "low-power")] |
| 95 | pub mod low_power; | 101 | pub mod low_power; |
| 96 | #[cfg(lptim)] | 102 | #[cfg(lptim)] |
| @@ -147,7 +153,7 @@ pub use crate::_generated::interrupt; | |||
| 147 | /// Macro to bind interrupts to handlers. | 153 | /// Macro to bind interrupts to handlers. |
| 148 | /// | 154 | /// |
| 149 | /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) | 155 | /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) |
| 150 | /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to | 156 | /// and implements the right [`Binding`](crate::interrupt::typelevel::Binding)s for it. You can pass this struct to drivers to |
| 151 | /// prove at compile-time that the right interrupts have been bound. | 157 | /// prove at compile-time that the right interrupts have been bound. |
| 152 | /// | 158 | /// |
| 153 | /// Example of how to bind one interrupt: | 159 | /// Example of how to bind one interrupt: |
| @@ -174,7 +180,10 @@ pub use crate::_generated::interrupt; | |||
| 174 | /// } | 180 | /// } |
| 175 | /// ); | 181 | /// ); |
| 176 | /// ``` | 182 | /// ``` |
| 177 | 183 | /// | |
| 184 | /// Some chips collate multiple interrupt signals into a single interrupt vector. In the above example, I2C2_3 is a | ||
| 185 | /// single vector which is activated by events and errors on both peripherals I2C2 and I2C3. Check your chip's list | ||
| 186 | /// of interrupt vectors if you get an unexpected compile error trying to bind the standard name. | ||
| 178 | // developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. | 187 | // developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. |
| 179 | #[macro_export] | 188 | #[macro_export] |
| 180 | macro_rules! bind_interrupts { | 189 | macro_rules! bind_interrupts { |
| @@ -194,7 +203,7 @@ macro_rules! bind_interrupts { | |||
| 194 | 203 | ||
| 195 | $( | 204 | $( |
| 196 | #[allow(non_snake_case)] | 205 | #[allow(non_snake_case)] |
| 197 | #[no_mangle] | 206 | #[unsafe(no_mangle)] |
| 198 | $(#[cfg($cond_irq)])? | 207 | $(#[cfg($cond_irq)])? |
| 199 | $(#[doc = $doc])* | 208 | $(#[doc = $doc])* |
| 200 | unsafe extern "C" fn $irq() { | 209 | unsafe extern "C" fn $irq() { |
| @@ -222,7 +231,7 @@ macro_rules! bind_interrupts { | |||
| 222 | } | 231 | } |
| 223 | 232 | ||
| 224 | // Reexports | 233 | // Reexports |
| 225 | pub use _generated::{peripherals, Peripherals}; | 234 | pub use _generated::{Peripherals, peripherals}; |
| 226 | pub use embassy_hal_internal::{Peri, PeripheralType}; | 235 | pub use embassy_hal_internal::{Peri, PeripheralType}; |
| 227 | #[cfg(feature = "unstable-pac")] | 236 | #[cfg(feature = "unstable-pac")] |
| 228 | pub use stm32_metapac as pac; | 237 | pub use stm32_metapac as pac; |
| @@ -240,6 +249,14 @@ pub struct Config { | |||
| 240 | /// RCC config. | 249 | /// RCC config. |
| 241 | pub rcc: rcc::Config, | 250 | pub rcc: rcc::Config, |
| 242 | 251 | ||
| 252 | #[cfg(feature = "low-power")] | ||
| 253 | /// RTC config | ||
| 254 | pub rtc: rtc::RtcConfig, | ||
| 255 | |||
| 256 | #[cfg(feature = "low-power")] | ||
| 257 | /// Minimum time to stop | ||
| 258 | pub min_stop_pause: embassy_time::Duration, | ||
| 259 | |||
| 243 | /// Enable debug during sleep and stop. | 260 | /// Enable debug during sleep and stop. |
| 244 | /// | 261 | /// |
| 245 | /// May increase power consumption. Defaults to true. | 262 | /// May increase power consumption. Defaults to true. |
| @@ -293,6 +310,10 @@ impl Default for Config { | |||
| 293 | fn default() -> Self { | 310 | fn default() -> Self { |
| 294 | Self { | 311 | Self { |
| 295 | rcc: Default::default(), | 312 | rcc: Default::default(), |
| 313 | #[cfg(feature = "low-power")] | ||
| 314 | rtc: Default::default(), | ||
| 315 | #[cfg(feature = "low-power")] | ||
| 316 | min_stop_pause: embassy_time::Duration::from_millis(250), | ||
| 296 | #[cfg(dbgmcu)] | 317 | #[cfg(dbgmcu)] |
| 297 | enable_debug_during_sleep: true, | 318 | enable_debug_during_sleep: true, |
| 298 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba))] | 319 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba))] |
| @@ -495,6 +516,16 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 495 | critical_section::with(|cs| { | 516 | critical_section::with(|cs| { |
| 496 | let p = Peripherals::take_with_cs(cs); | 517 | let p = Peripherals::take_with_cs(cs); |
| 497 | 518 | ||
| 519 | #[cfg(dbgmcu_n6)] | ||
| 520 | { | ||
| 521 | crate::pac::RCC.miscensr().write(|w| w.set_dbgens(true)); | ||
| 522 | crate::pac::RCC.miscenr().read(); // volatile read | ||
| 523 | crate::pac::DBGMCU | ||
| 524 | .cr() | ||
| 525 | .modify(|w| w.set_dbgclken(stm32_metapac::dbgmcu::vals::Dbgclken::B_0X1)); | ||
| 526 | crate::pac::DBGMCU.cr().read(); | ||
| 527 | } | ||
| 528 | |||
| 498 | #[cfg(dbgmcu)] | 529 | #[cfg(dbgmcu)] |
| 499 | crate::pac::DBGMCU.cr().modify(|cr| { | 530 | crate::pac::DBGMCU.cr().modify(|cr| { |
| 500 | #[cfg(dbgmcu_h5)] | 531 | #[cfg(dbgmcu_h5)] |
| @@ -509,7 +540,7 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 509 | } | 540 | } |
| 510 | #[cfg(any( | 541 | #[cfg(any( |
| 511 | dbgmcu_f1, dbgmcu_f2, dbgmcu_f3, dbgmcu_f4, dbgmcu_f7, dbgmcu_g4, dbgmcu_f7, dbgmcu_l0, dbgmcu_l1, | 542 | dbgmcu_f1, dbgmcu_f2, dbgmcu_f3, dbgmcu_f4, dbgmcu_f7, dbgmcu_g4, dbgmcu_f7, dbgmcu_l0, dbgmcu_l1, |
| 512 | dbgmcu_l4, dbgmcu_wb, dbgmcu_wl | 543 | dbgmcu_l4, dbgmcu_wb, dbgmcu_wl, dbgmcu_n6 |
| 513 | ))] | 544 | ))] |
| 514 | { | 545 | { |
| 515 | cr.set_dbg_sleep(config.enable_debug_during_sleep); | 546 | cr.set_dbg_sleep(config.enable_debug_during_sleep); |
| @@ -530,7 +561,7 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 530 | rcc::enable_and_reset_with_cs::<peripherals::SYSCFG>(cs); | 561 | rcc::enable_and_reset_with_cs::<peripherals::SYSCFG>(cs); |
| 531 | #[cfg(not(any(stm32h5, stm32h7, stm32h7rs, stm32wb, stm32wl)))] | 562 | #[cfg(not(any(stm32h5, stm32h7, stm32h7rs, stm32wb, stm32wl)))] |
| 532 | rcc::enable_and_reset_with_cs::<peripherals::PWR>(cs); | 563 | rcc::enable_and_reset_with_cs::<peripherals::PWR>(cs); |
| 533 | #[cfg(not(any(stm32f2, stm32f4, stm32f7, stm32l0, stm32h5, stm32h7, stm32h7rs)))] | 564 | #[cfg(all(flash, not(any(stm32f2, stm32f4, stm32f7, stm32l0, stm32h5, stm32h7, stm32h7rs))))] |
| 534 | rcc::enable_and_reset_with_cs::<peripherals::FLASH>(cs); | 565 | rcc::enable_and_reset_with_cs::<peripherals::FLASH>(cs); |
| 535 | 566 | ||
| 536 | // Enable the VDDIO2 power supply on chips that have it. | 567 | // Enable the VDDIO2 power supply on chips that have it. |
| @@ -590,7 +621,7 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 590 | #[cfg(ucpd)] | 621 | #[cfg(ucpd)] |
| 591 | ucpd::init( | 622 | ucpd::init( |
| 592 | cs, | 623 | cs, |
| 593 | #[cfg(peri_ucpd1)] | 624 | #[cfg(all(peri_ucpd1, not(stm32n6)))] |
| 594 | config.enable_ucpd1_dead_battery, | 625 | config.enable_ucpd1_dead_battery, |
| 595 | #[cfg(peri_ucpd2)] | 626 | #[cfg(peri_ucpd2)] |
| 596 | config.enable_ucpd2_dead_battery, | 627 | config.enable_ucpd2_dead_battery, |
| @@ -622,8 +653,28 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 622 | exti::init(cs); | 653 | exti::init(cs); |
| 623 | 654 | ||
| 624 | rcc::init_rcc(cs, config.rcc); | 655 | rcc::init_rcc(cs, config.rcc); |
| 656 | |||
| 657 | #[cfg(feature = "low-power")] | ||
| 658 | rtc::init_rtc(cs, config.rtc, config.min_stop_pause); | ||
| 659 | |||
| 660 | #[cfg(all(stm32wb, feature = "low-power"))] | ||
| 661 | hsem::init_hsem(cs); | ||
| 625 | } | 662 | } |
| 626 | 663 | ||
| 627 | p | 664 | p |
| 628 | }) | 665 | }) |
| 629 | } | 666 | } |
| 667 | |||
| 668 | /// Performs a busy-wait delay for a specified number of microseconds. | ||
| 669 | #[allow(unused)] | ||
| 670 | pub(crate) fn block_for_us(us: u64) { | ||
| 671 | cfg_if::cfg_if! { | ||
| 672 | // this does strange things on stm32wlx in low power mode depending on exactly when it's called | ||
| 673 | // as in sometimes 15 us (1 tick) would take > 20 seconds. | ||
| 674 | if #[cfg(all(feature = "time", all(not(feature = "low-power"), not(stm32wlex))))] { | ||
| 675 | embassy_time::block_for(embassy_time::Duration::from_micros(us)); | ||
| 676 | } else { | ||
| 677 | cortex_m::asm::delay(unsafe { rcc::get_freqs().sys.to_hertz().unwrap().0 as u64 * us / 1_000_000 } as u32); | ||
| 678 | } | ||
| 679 | } | ||
| 680 | } | ||
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 342f73bc8..2388abe3c 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | //! | 14 | //! |
| 15 | //! Since entering and leaving low-power modes typically incurs a significant latency, the | 15 | //! Since entering and leaving low-power modes typically incurs a significant latency, the |
| 16 | //! low-power executor will only attempt to enter when the next timer event is at least | 16 | //! low-power executor will only attempt to enter when the next timer event is at least |
| 17 | //! [`time_driver::MIN_STOP_PAUSE`] in the future. | 17 | //! [`time_driver::min_stop_pause`] in the future. |
| 18 | //! | 18 | //! |
| 19 | //! Currently there is no macro analogous to `embassy_executor::main` for this executor; | 19 | //! Currently there is no macro analogous to `embassy_executor::main` for this executor; |
| 20 | //! consequently one must define their entrypoint manually. Moreover, you must relinquish control | 20 | //! consequently one must define their entrypoint manually. Moreover, you must relinquish control |
| @@ -22,53 +22,65 @@ | |||
| 22 | //! | 22 | //! |
| 23 | //! ```rust,no_run | 23 | //! ```rust,no_run |
| 24 | //! use embassy_executor::Spawner; | 24 | //! use embassy_executor::Spawner; |
| 25 | //! use embassy_stm32::low_power::Executor; | 25 | //! use embassy_stm32::low_power; |
| 26 | //! use embassy_stm32::rtc::{Rtc, RtcConfig}; | 26 | //! use embassy_stm32::rtc::{Rtc, RtcConfig}; |
| 27 | //! use static_cell::StaticCell; | 27 | //! use embassy_time::Duration; |
| 28 | //! | 28 | //! |
| 29 | //! #[cortex_m_rt::entry] | 29 | //! #[embassy_executor::main(executor = "low_power::Executor")] |
| 30 | //! fn main() -> ! { | ||
| 31 | //! Executor::take().run(|spawner| { | ||
| 32 | //! spawner.spawn(unwrap!(async_main(spawner))); | ||
| 33 | //! }); | ||
| 34 | //! } | ||
| 35 | //! | ||
| 36 | //! #[embassy_executor::task] | ||
| 37 | //! async fn async_main(spawner: Spawner) { | 30 | //! async fn async_main(spawner: Spawner) { |
| 38 | //! // initialize the platform... | 31 | //! // initialize the platform... |
| 39 | //! let mut config = embassy_stm32::Config::default(); | 32 | //! let mut config = embassy_stm32::Config::default(); |
| 33 | //! // the default value, but can be adjusted | ||
| 34 | //! config.min_stop_pause = Duration::from_millis(250); | ||
| 40 | //! // when enabled the power-consumption is much higher during stop, but debugging and RTT is working | 35 | //! // when enabled the power-consumption is much higher during stop, but debugging and RTT is working |
| 41 | //! config.enable_debug_during_sleep = false; | 36 | //! config.enable_debug_during_sleep = false; |
| 42 | //! let p = embassy_stm32::init(config); | 37 | //! let p = embassy_stm32::init(config); |
| 43 | //! | 38 | //! |
| 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... | 39 | //! // your application here... |
| 51 | //! } | 40 | //! } |
| 52 | //! ``` | 41 | //! ``` |
| 53 | 42 | ||
| 54 | // TODO: Usage of `static mut` here is unsound. Fix then remove this `allow`.` | ||
| 55 | #![allow(static_mut_refs)] | ||
| 56 | |||
| 57 | use core::arch::asm; | 43 | use core::arch::asm; |
| 58 | use core::marker::PhantomData; | 44 | use core::marker::PhantomData; |
| 59 | use core::sync::atomic::{compiler_fence, Ordering}; | 45 | use core::mem; |
| 46 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 60 | 47 | ||
| 61 | use cortex_m::peripheral::SCB; | 48 | use cortex_m::peripheral::SCB; |
| 49 | use critical_section::CriticalSection; | ||
| 62 | use embassy_executor::*; | 50 | use embassy_executor::*; |
| 63 | 51 | ||
| 64 | use crate::interrupt; | 52 | use crate::interrupt; |
| 65 | use crate::time_driver::{get_driver, RtcDriver}; | 53 | pub use crate::rcc::StopMode; |
| 54 | use crate::rcc::{BusyPeripheral, RCC_CONFIG, REFCOUNT_STOP1, REFCOUNT_STOP2}; | ||
| 55 | use crate::time_driver::get_driver; | ||
| 66 | 56 | ||
| 67 | const THREAD_PENDER: usize = usize::MAX; | 57 | const THREAD_PENDER: usize = usize::MAX; |
| 68 | 58 | ||
| 69 | use crate::rtc::Rtc; | 59 | static mut EXECUTOR_TAKEN: bool = false; |
| 70 | 60 | ||
| 71 | static mut EXECUTOR: Option<Executor> = None; | 61 | /// Prevent the device from going into the stop mode if held |
| 62 | pub struct DeviceBusy { | ||
| 63 | _stop_mode: BusyPeripheral<StopMode>, | ||
| 64 | } | ||
| 65 | |||
| 66 | impl DeviceBusy { | ||
| 67 | /// Create a new DeviceBusy with stop1. | ||
| 68 | pub fn new_stop1() -> Self { | ||
| 69 | Self::new(StopMode::Stop1) | ||
| 70 | } | ||
| 71 | |||
| 72 | /// Create a new DeviceBusy with stop2. | ||
| 73 | pub fn new_stop2() -> Self { | ||
| 74 | Self::new(StopMode::Stop2) | ||
| 75 | } | ||
| 76 | |||
| 77 | /// Create a new DeviceBusy. | ||
| 78 | pub fn new(stop_mode: StopMode) -> Self { | ||
| 79 | Self { | ||
| 80 | _stop_mode: BusyPeripheral::new(stop_mode), | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 72 | 84 | ||
| 73 | #[cfg(not(stm32u0))] | 85 | #[cfg(not(stm32u0))] |
| 74 | foreach_interrupt! { | 86 | foreach_interrupt! { |
| @@ -76,7 +88,7 @@ foreach_interrupt! { | |||
| 76 | #[interrupt] | 88 | #[interrupt] |
| 77 | #[allow(non_snake_case)] | 89 | #[allow(non_snake_case)] |
| 78 | unsafe fn $irq() { | 90 | unsafe fn $irq() { |
| 79 | EXECUTOR.as_mut().unwrap().on_wakeup_irq(); | 91 | Executor::on_wakeup_irq_or_event(); |
| 80 | } | 92 | } |
| 81 | }; | 93 | }; |
| 82 | } | 94 | } |
| @@ -87,55 +99,35 @@ foreach_interrupt! { | |||
| 87 | #[interrupt] | 99 | #[interrupt] |
| 88 | #[allow(non_snake_case)] | 100 | #[allow(non_snake_case)] |
| 89 | unsafe fn $irq() { | 101 | unsafe fn $irq() { |
| 90 | EXECUTOR.as_mut().unwrap().on_wakeup_irq(); | 102 | Executor::on_wakeup_irq_or_event(); |
| 91 | } | 103 | } |
| 92 | }; | 104 | }; |
| 93 | } | 105 | } |
| 94 | 106 | ||
| 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. | 107 | /// Get whether the core is ready to enter the given stop mode. |
| 106 | /// | 108 | /// |
| 107 | /// This will return false if some peripheral driver is in use that | 109 | /// This will return false if some peripheral driver is in use that |
| 108 | /// prevents entering the given stop mode. | 110 | /// prevents entering the given stop mode. |
| 109 | pub fn stop_ready(stop_mode: StopMode) -> bool { | 111 | pub fn stop_ready(stop_mode: StopMode) -> bool { |
| 110 | match unsafe { EXECUTOR.as_mut().unwrap() }.stop_mode() { | 112 | critical_section::with(|cs| match Executor::stop_mode(cs) { |
| 111 | Some(StopMode::Stop2) => true, | 113 | Some(StopMode::Standby | StopMode::Stop2) => true, |
| 112 | Some(StopMode::Stop1) => stop_mode == StopMode::Stop1, | 114 | Some(StopMode::Stop1) => stop_mode == StopMode::Stop1, |
| 113 | None => false, | 115 | None => false, |
| 114 | } | 116 | }) |
| 115 | } | ||
| 116 | |||
| 117 | /// Available Stop modes. | ||
| 118 | #[non_exhaustive] | ||
| 119 | #[derive(PartialEq)] | ||
| 120 | pub enum StopMode { | ||
| 121 | /// STOP 1 | ||
| 122 | Stop1, | ||
| 123 | /// STOP 2 | ||
| 124 | Stop2, | ||
| 125 | } | 117 | } |
| 126 | 118 | ||
| 127 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32u0))] | 119 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wb, stm32wlex, stm32u0))] |
| 128 | use stm32_metapac::pwr::vals::Lpms; | 120 | use crate::pac::pwr::vals::Lpms; |
| 129 | 121 | ||
| 130 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32u0))] | 122 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wb, stm32wlex, stm32u0))] |
| 131 | impl Into<Lpms> for StopMode { | 123 | impl Into<Lpms> for StopMode { |
| 132 | fn into(self) -> Lpms { | 124 | fn into(self) -> Lpms { |
| 133 | match self { | 125 | match self { |
| 134 | StopMode::Stop1 => Lpms::STOP1, | 126 | StopMode::Stop1 => Lpms::STOP1, |
| 135 | #[cfg(not(stm32wba))] | 127 | #[cfg(not(any(stm32wb, stm32wba)))] |
| 136 | StopMode::Stop2 => Lpms::STOP2, | 128 | StopMode::Standby | StopMode::Stop2 => Lpms::STOP2, |
| 137 | #[cfg(stm32wba)] | 129 | #[cfg(any(stm32wb, stm32wba))] |
| 138 | StopMode::Stop2 => Lpms::STOP1, // TODO: WBA has no STOP2? | 130 | StopMode::Standby | StopMode::Stop2 => Lpms::STOP1, // TODO: WBA has no STOP2? |
| 139 | } | 131 | } |
| 140 | } | 132 | } |
| 141 | } | 133 | } |
| @@ -153,55 +145,146 @@ impl Into<Lpms> for StopMode { | |||
| 153 | pub struct Executor { | 145 | pub struct Executor { |
| 154 | inner: raw::Executor, | 146 | inner: raw::Executor, |
| 155 | not_send: PhantomData<*mut ()>, | 147 | not_send: PhantomData<*mut ()>, |
| 156 | scb: SCB, | ||
| 157 | time_driver: &'static RtcDriver, | ||
| 158 | } | 148 | } |
| 159 | 149 | ||
| 160 | impl Executor { | 150 | impl Executor { |
| 161 | /// Create a new Executor. | 151 | /// Create a new Executor. |
| 162 | pub fn take() -> &'static mut Self { | 152 | pub fn new() -> Self { |
| 163 | critical_section::with(|_| unsafe { | 153 | unsafe { |
| 164 | assert!(EXECUTOR.is_none()); | 154 | if EXECUTOR_TAKEN { |
| 165 | 155 | panic!("Low power executor can only be taken once."); | |
| 166 | EXECUTOR = Some(Self { | 156 | } else { |
| 167 | inner: raw::Executor::new(THREAD_PENDER as *mut ()), | 157 | EXECUTOR_TAKEN = true; |
| 168 | not_send: PhantomData, | 158 | } |
| 169 | scb: cortex_m::Peripherals::steal().SCB, | 159 | } |
| 170 | time_driver: get_driver(), | ||
| 171 | }); | ||
| 172 | |||
| 173 | let executor = EXECUTOR.as_mut().unwrap(); | ||
| 174 | |||
| 175 | executor | ||
| 176 | }) | ||
| 177 | } | ||
| 178 | 160 | ||
| 179 | unsafe fn on_wakeup_irq(&mut self) { | 161 | Self { |
| 180 | self.time_driver.resume_time(); | 162 | inner: raw::Executor::new(THREAD_PENDER as *mut ()), |
| 181 | trace!("low power: resume"); | 163 | not_send: PhantomData, |
| 164 | } | ||
| 182 | } | 165 | } |
| 183 | 166 | ||
| 184 | pub(self) fn stop_with_rtc(&mut self, rtc: &'static Rtc) { | 167 | pub(crate) unsafe fn on_wakeup_irq_or_event() { |
| 185 | self.time_driver.set_rtc(rtc); | 168 | if !get_driver().is_stopped() { |
| 169 | return; | ||
| 170 | } | ||
| 186 | 171 | ||
| 187 | rtc.enable_wakeup_line(); | 172 | critical_section::with(|cs| { |
| 173 | #[cfg(stm32wlex)] | ||
| 174 | { | ||
| 175 | let es = crate::pac::PWR.extscr().read(); | ||
| 176 | match (es.c1stopf(), es.c1stop2f()) { | ||
| 177 | (true, false) => debug!("low power: wake from STOP1"), | ||
| 178 | (false, true) => debug!("low power: wake from STOP2"), | ||
| 179 | (true, true) => debug!("low power: wake from STOP1 and STOP2 ???"), | ||
| 180 | (false, false) => trace!("low power: stop mode not entered"), | ||
| 181 | }; | ||
| 182 | crate::pac::PWR.extscr().modify(|w| { | ||
| 183 | w.set_c1cssf(false); | ||
| 184 | }); | ||
| 185 | |||
| 186 | if es.c1stop2f() || es.c1stopf() { | ||
| 187 | // when we wake from any stop mode we need to re-initialize the rcc | ||
| 188 | crate::rcc::init(RCC_CONFIG.unwrap()); | ||
| 189 | |||
| 190 | if es.c1stop2f() { | ||
| 191 | // when we wake from STOP2, we need to re-initialize the time driver | ||
| 192 | get_driver().init_timer(cs); | ||
| 193 | // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer) | ||
| 194 | // and given that we just woke from STOP2, we can reset them | ||
| 195 | REFCOUNT_STOP2 = 0; | ||
| 196 | REFCOUNT_STOP1 = 0; | ||
| 197 | } | ||
| 198 | } | ||
| 199 | } | ||
| 200 | get_driver().resume_time(cs); | ||
| 201 | |||
| 202 | trace!("low power: resume time"); | ||
| 203 | }); | ||
| 204 | } | ||
| 188 | 205 | ||
| 189 | trace!("low power: stop with rtc configured"); | 206 | const fn get_scb() -> SCB { |
| 207 | unsafe { mem::transmute(()) } | ||
| 190 | } | 208 | } |
| 191 | 209 | ||
| 192 | fn stop_mode(&self) -> Option<StopMode> { | 210 | fn stop_mode(_cs: CriticalSection) -> Option<StopMode> { |
| 193 | if unsafe { crate::rcc::REFCOUNT_STOP2 == 0 } && unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { | 211 | // We cannot enter standby because we will lose program state. |
| 212 | if unsafe { REFCOUNT_STOP2 == 0 && REFCOUNT_STOP1 == 0 } { | ||
| 194 | Some(StopMode::Stop2) | 213 | Some(StopMode::Stop2) |
| 195 | } else if unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { | 214 | } else if unsafe { REFCOUNT_STOP1 == 0 } { |
| 196 | Some(StopMode::Stop1) | 215 | Some(StopMode::Stop1) |
| 197 | } else { | 216 | } else { |
| 217 | trace!("low power: not ready to stop (refcount_stop1: {})", unsafe { | ||
| 218 | REFCOUNT_STOP1 | ||
| 219 | }); | ||
| 198 | None | 220 | None |
| 199 | } | 221 | } |
| 200 | } | 222 | } |
| 201 | 223 | ||
| 224 | #[cfg(all(stm32wb, feature = "low-power"))] | ||
| 225 | fn configure_stop_stm32wb(&self, _cs: CriticalSection) -> Result<(), ()> { | ||
| 226 | use core::task::Poll; | ||
| 227 | |||
| 228 | use embassy_futures::poll_once; | ||
| 229 | |||
| 230 | use crate::hsem::HardwareSemaphoreChannel; | ||
| 231 | use crate::pac::rcc::vals::{Smps, Sw}; | ||
| 232 | use crate::pac::{PWR, RCC}; | ||
| 233 | |||
| 234 | trace!("low power: trying to get sem3"); | ||
| 235 | |||
| 236 | let sem3_mutex = match poll_once(HardwareSemaphoreChannel::<crate::peripherals::HSEM>::new(3).lock(0)) { | ||
| 237 | Poll::Pending => None, | ||
| 238 | Poll::Ready(mutex) => Some(mutex), | ||
| 239 | } | ||
| 240 | .ok_or(())?; | ||
| 241 | |||
| 242 | trace!("low power: got sem3"); | ||
| 243 | |||
| 244 | let sem4_mutex = HardwareSemaphoreChannel::<crate::peripherals::HSEM>::new(4).try_lock(0); | ||
| 245 | if let Some(sem4_mutex) = sem4_mutex { | ||
| 246 | trace!("low power: got sem4"); | ||
| 247 | |||
| 248 | if PWR.extscr().read().c2ds() { | ||
| 249 | drop(sem4_mutex); | ||
| 250 | } else { | ||
| 251 | return Ok(()); | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | // Sem4 not granted | ||
| 256 | // Set HSION | ||
| 257 | RCC.cr().modify(|w| { | ||
| 258 | w.set_hsion(true); | ||
| 259 | }); | ||
| 260 | |||
| 261 | // Wait for HSIRDY | ||
| 262 | while !RCC.cr().read().hsirdy() {} | ||
| 263 | |||
| 264 | // Set SW to HSI | ||
| 265 | RCC.cfgr().modify(|w| { | ||
| 266 | w.set_sw(Sw::HSI); | ||
| 267 | }); | ||
| 268 | |||
| 269 | // Wait for SWS to report HSI | ||
| 270 | while !RCC.cfgr().read().sws().eq(&Sw::HSI) {} | ||
| 271 | |||
| 272 | // Set SMPSSEL to HSI | ||
| 273 | RCC.smpscr().modify(|w| { | ||
| 274 | w.set_smpssel(Smps::HSI); | ||
| 275 | }); | ||
| 276 | |||
| 277 | drop(sem3_mutex); | ||
| 278 | |||
| 279 | Ok(()) | ||
| 280 | } | ||
| 281 | |||
| 202 | #[allow(unused_variables)] | 282 | #[allow(unused_variables)] |
| 203 | fn configure_stop(&mut self, stop_mode: StopMode) { | 283 | fn configure_stop(&self, _cs: CriticalSection, stop_mode: StopMode) -> Result<(), ()> { |
| 204 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba))] | 284 | #[cfg(all(stm32wb, feature = "low-power"))] |
| 285 | self.configure_stop_stm32wb(_cs)?; | ||
| 286 | |||
| 287 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wb, stm32wba, stm32wlex))] | ||
| 205 | crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); | 288 | crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); |
| 206 | #[cfg(stm32h5)] | 289 | #[cfg(stm32h5)] |
| 207 | crate::pac::PWR.pmcr().modify(|v| { | 290 | crate::pac::PWR.pmcr().modify(|v| { |
| @@ -209,34 +292,34 @@ impl Executor { | |||
| 209 | v.set_lpms(vals::Lpms::STOP); | 292 | v.set_lpms(vals::Lpms::STOP); |
| 210 | v.set_svos(vals::Svos::SCALE3); | 293 | v.set_svos(vals::Svos::SCALE3); |
| 211 | }); | 294 | }); |
| 295 | |||
| 296 | Ok(()) | ||
| 212 | } | 297 | } |
| 213 | 298 | ||
| 214 | fn configure_pwr(&mut self) { | 299 | fn configure_pwr(&self) { |
| 215 | self.scb.clear_sleepdeep(); | 300 | Self::get_scb().clear_sleepdeep(); |
| 301 | // Clear any previous stop flags | ||
| 302 | #[cfg(stm32wlex)] | ||
| 303 | crate::pac::PWR.extscr().modify(|w| { | ||
| 304 | w.set_c1cssf(true); | ||
| 305 | }); | ||
| 216 | 306 | ||
| 217 | compiler_fence(Ordering::SeqCst); | 307 | compiler_fence(Ordering::SeqCst); |
| 218 | 308 | ||
| 219 | let stop_mode = self.stop_mode(); | 309 | critical_section::with(|cs| { |
| 220 | 310 | let _ = unsafe { RCC_CONFIG }?; | |
| 221 | if stop_mode.is_none() { | 311 | let stop_mode = Self::stop_mode(cs)?; |
| 222 | trace!("low power: not ready to stop"); | 312 | get_driver().pause_time(cs).ok()?; |
| 223 | return; | 313 | self.configure_stop(cs, stop_mode).ok()?; |
| 224 | } | ||
| 225 | |||
| 226 | if self.time_driver.pause_time().is_err() { | ||
| 227 | trace!("low power: failed to pause time"); | ||
| 228 | return; | ||
| 229 | } | ||
| 230 | 314 | ||
| 231 | let stop_mode = stop_mode.unwrap(); | 315 | Some(stop_mode) |
| 232 | match stop_mode { | 316 | }) |
| 233 | StopMode::Stop1 => trace!("low power: stop 1"), | 317 | .map(|stop_mode| { |
| 234 | StopMode::Stop2 => trace!("low power: stop 2"), | 318 | trace!("low power: enter stop: {}", stop_mode); |
| 235 | } | ||
| 236 | self.configure_stop(stop_mode); | ||
| 237 | 319 | ||
| 238 | #[cfg(not(feature = "low-power-debug-with-sleep"))] | 320 | #[cfg(not(feature = "low-power-debug-with-sleep"))] |
| 239 | self.scb.set_sleepdeep(); | 321 | Self::get_scb().set_sleepdeep(); |
| 322 | }); | ||
| 240 | } | 323 | } |
| 241 | 324 | ||
| 242 | /// Run the executor. | 325 | /// Run the executor. |
| @@ -258,14 +341,16 @@ impl Executor { | |||
| 258 | /// | 341 | /// |
| 259 | /// This function never returns. | 342 | /// This function never returns. |
| 260 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | 343 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { |
| 261 | let executor = unsafe { EXECUTOR.as_mut().unwrap() }; | 344 | init(self.inner.spawner()); |
| 262 | init(executor.inner.spawner()); | ||
| 263 | 345 | ||
| 264 | loop { | 346 | loop { |
| 265 | unsafe { | 347 | unsafe { |
| 266 | executor.inner.poll(); | 348 | self.inner.poll(); |
| 267 | self.configure_pwr(); | 349 | self.configure_pwr(); |
| 350 | #[cfg(feature = "defmt")] | ||
| 351 | defmt::flush(); | ||
| 268 | asm!("wfe"); | 352 | asm!("wfe"); |
| 353 | Self::on_wakeup_irq_or_event(); | ||
| 269 | }; | 354 | }; |
| 270 | } | 355 | } |
| 271 | } | 356 | } |
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..4a55f5bd3 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs | |||
| @@ -3,19 +3,12 @@ | |||
| 3 | 3 | ||
| 4 | use embassy_hal_internal::PeripheralType; | 4 | use embassy_hal_internal::PeripheralType; |
| 5 | 5 | ||
| 6 | use crate::Peri; | ||
| 7 | #[cfg(opamp_v5)] | ||
| 8 | use crate::block_for_us; | ||
| 6 | use crate::pac::opamp::vals::*; | 9 | use crate::pac::opamp::vals::*; |
| 7 | #[cfg(not(any(stm32g4, stm32f3)))] | 10 | #[cfg(not(any(stm32g4, stm32f3)))] |
| 8 | use crate::rcc::RccInfo; | 11 | use crate::rcc::RccInfo; |
| 9 | use crate::Peri; | ||
| 10 | |||
| 11 | /// Performs a busy-wait delay for a specified number of microseconds. | ||
| 12 | #[cfg(opamp_v5)] | ||
| 13 | fn blocking_delay_ms(ms: u32) { | ||
| 14 | #[cfg(feature = "time")] | ||
| 15 | embassy_time::block_for(embassy_time::Duration::from_millis(ms as u64)); | ||
| 16 | #[cfg(not(feature = "time"))] | ||
| 17 | cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 / 1_000 * ms); | ||
| 18 | } | ||
| 19 | 12 | ||
| 20 | /// Gain | 13 | /// Gain |
| 21 | #[allow(missing_docs)] | 14 | #[allow(missing_docs)] |
| @@ -439,7 +432,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 439 | 432 | ||
| 440 | // The closer the trimming value is to the optimum trimming value, the longer it takes to stabilize | 433 | // The closer the trimming value is to the optimum trimming value, the longer it takes to stabilize |
| 441 | // (with a maximum stabilization time remaining below 2 ms in any case) -- RM0440 25.3.7 | 434 | // (with a maximum stabilization time remaining below 2 ms in any case) -- RM0440 25.3.7 |
| 442 | blocking_delay_ms(2); | 435 | block_for_us(2_000); |
| 443 | 436 | ||
| 444 | if !T::regs().csr().read().calout() { | 437 | if !T::regs().csr().read().calout() { |
| 445 | if mid == 0 { | 438 | if mid == 0 { |
diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index d93cecb69..2d5dbd95a 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)] |
| @@ -451,7 +451,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 451 | } | 451 | } |
| 452 | 452 | ||
| 453 | T::REGS.cr().modify(|w| { | 453 | T::REGS.cr().modify(|w| { |
| 454 | w.set_fmode(0.into()); | 454 | w.set_fmode(vals::FunctionalMode::INDIRECT_WRITE); |
| 455 | }); | 455 | }); |
| 456 | 456 | ||
| 457 | // Configure alternate bytes | 457 | // Configure alternate bytes |
| @@ -577,7 +577,8 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 577 | w.set_dmaen(false); | 577 | w.set_dmaen(false); |
| 578 | }); | 578 | }); |
| 579 | 579 | ||
| 580 | self.configure_command(&transaction, Some(buf.len()))?; | 580 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 581 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 581 | 582 | ||
| 582 | let current_address = T::REGS.ar().read().address(); | 583 | let current_address = T::REGS.ar().read().address(); |
| 583 | let current_instruction = T::REGS.ir().read().instruction(); | 584 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -616,7 +617,8 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 616 | w.set_dmaen(false); | 617 | w.set_dmaen(false); |
| 617 | }); | 618 | }); |
| 618 | 619 | ||
| 619 | self.configure_command(&transaction, Some(buf.len()))?; | 620 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 621 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 620 | 622 | ||
| 621 | T::REGS | 623 | T::REGS |
| 622 | .cr() | 624 | .cr() |
| @@ -1153,7 +1155,8 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1153 | // Wait for peripheral to be free | 1155 | // Wait for peripheral to be free |
| 1154 | while T::REGS.sr().read().busy() {} | 1156 | while T::REGS.sr().read().busy() {} |
| 1155 | 1157 | ||
| 1156 | self.configure_command(&transaction, Some(buf.len()))?; | 1158 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1159 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1157 | 1160 | ||
| 1158 | let current_address = T::REGS.ar().read().address(); | 1161 | let current_address = T::REGS.ar().read().address(); |
| 1159 | let current_instruction = T::REGS.ir().read().instruction(); | 1162 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -1168,16 +1171,18 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1168 | T::REGS.ar().write(|v| v.set_address(current_address)); | 1171 | T::REGS.ar().write(|v| v.set_address(current_address)); |
| 1169 | } | 1172 | } |
| 1170 | 1173 | ||
| 1171 | let transfer = unsafe { | 1174 | for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { |
| 1172 | self.dma | 1175 | let transfer = unsafe { |
| 1173 | .as_mut() | 1176 | self.dma |
| 1174 | .unwrap() | 1177 | .as_mut() |
| 1175 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | 1178 | .unwrap() |
| 1176 | }; | 1179 | .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) |
| 1180 | }; | ||
| 1177 | 1181 | ||
| 1178 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1182 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1179 | 1183 | ||
| 1180 | transfer.blocking_wait(); | 1184 | transfer.blocking_wait(); |
| 1185 | } | ||
| 1181 | 1186 | ||
| 1182 | finish_dma(T::REGS); | 1187 | finish_dma(T::REGS); |
| 1183 | 1188 | ||
| @@ -1193,13 +1198,14 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1193 | // Wait for peripheral to be free | 1198 | // Wait for peripheral to be free |
| 1194 | while T::REGS.sr().read().busy() {} | 1199 | while T::REGS.sr().read().busy() {} |
| 1195 | 1200 | ||
| 1196 | self.configure_command(&transaction, Some(buf.len()))?; | 1201 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1202 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1197 | T::REGS | 1203 | T::REGS |
| 1198 | .cr() | 1204 | .cr() |
| 1199 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); | 1205 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); |
| 1200 | 1206 | ||
| 1201 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. | 1207 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. |
| 1202 | for chunk in buf.chunks(0xFFFF) { | 1208 | for chunk in buf.chunks(0xFFFF / W::size().bytes()) { |
| 1203 | let transfer = unsafe { | 1209 | let transfer = unsafe { |
| 1204 | self.dma | 1210 | self.dma |
| 1205 | .as_mut() | 1211 | .as_mut() |
| @@ -1226,7 +1232,8 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1226 | // Wait for peripheral to be free | 1232 | // Wait for peripheral to be free |
| 1227 | while T::REGS.sr().read().busy() {} | 1233 | while T::REGS.sr().read().busy() {} |
| 1228 | 1234 | ||
| 1229 | self.configure_command(&transaction, Some(buf.len()))?; | 1235 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1236 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1230 | 1237 | ||
| 1231 | let current_address = T::REGS.ar().read().address(); | 1238 | let current_address = T::REGS.ar().read().address(); |
| 1232 | let current_instruction = T::REGS.ir().read().instruction(); | 1239 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -1241,16 +1248,18 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1241 | T::REGS.ar().write(|v| v.set_address(current_address)); | 1248 | T::REGS.ar().write(|v| v.set_address(current_address)); |
| 1242 | } | 1249 | } |
| 1243 | 1250 | ||
| 1244 | let transfer = unsafe { | 1251 | for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { |
| 1245 | self.dma | 1252 | let transfer = unsafe { |
| 1246 | .as_mut() | 1253 | self.dma |
| 1247 | .unwrap() | 1254 | .as_mut() |
| 1248 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | 1255 | .unwrap() |
| 1249 | }; | 1256 | .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) |
| 1257 | }; | ||
| 1250 | 1258 | ||
| 1251 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1259 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1252 | 1260 | ||
| 1253 | transfer.await; | 1261 | transfer.await; |
| 1262 | } | ||
| 1254 | 1263 | ||
| 1255 | finish_dma(T::REGS); | 1264 | finish_dma(T::REGS); |
| 1256 | 1265 | ||
| @@ -1266,13 +1275,14 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1266 | // Wait for peripheral to be free | 1275 | // Wait for peripheral to be free |
| 1267 | while T::REGS.sr().read().busy() {} | 1276 | while T::REGS.sr().read().busy() {} |
| 1268 | 1277 | ||
| 1269 | self.configure_command(&transaction, Some(buf.len()))?; | 1278 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1279 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1270 | T::REGS | 1280 | T::REGS |
| 1271 | .cr() | 1281 | .cr() |
| 1272 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); | 1282 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); |
| 1273 | 1283 | ||
| 1274 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. | 1284 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. |
| 1275 | for chunk in buf.chunks(0xFFFF) { | 1285 | for chunk in buf.chunks(0xFFFF / W::size().bytes()) { |
| 1276 | let transfer = unsafe { | 1286 | let transfer = unsafe { |
| 1277 | self.dma | 1287 | self.dma |
| 1278 | .as_mut() | 1288 | .as_mut() |
diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index b03cd9009..1f47f4845 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)] |
| @@ -111,7 +111,7 @@ impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> { | |||
| 111 | config: Config, | 111 | config: Config, |
| 112 | fsel: FlashSelection, | 112 | fsel: FlashSelection, |
| 113 | ) -> Self { | 113 | ) -> Self { |
| 114 | rcc::enable_and_reset::<T>(); | 114 | rcc::enable_and_reset_without_stop::<T>(); |
| 115 | 115 | ||
| 116 | while T::REGS.sr().read().busy() {} | 116 | while T::REGS.sr().read().busy() {} |
| 117 | 117 | ||
| @@ -403,6 +403,7 @@ impl<'d, T: Instance> Qspi<'d, T, Async> { | |||
| 403 | 403 | ||
| 404 | /// Async read data, using DMA. | 404 | /// Async read data, using DMA. |
| 405 | pub async fn read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) { | 405 | pub async fn read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) { |
| 406 | let _scoped_block_stop = T::RCC_INFO.block_stop(); | ||
| 406 | let transfer = self.start_read_transfer(transaction, buf); | 407 | let transfer = self.start_read_transfer(transaction, buf); |
| 407 | transfer.await; | 408 | transfer.await; |
| 408 | } | 409 | } |
| @@ -443,6 +444,7 @@ impl<'d, T: Instance> Qspi<'d, T, Async> { | |||
| 443 | 444 | ||
| 444 | /// Async write data, using DMA. | 445 | /// Async write data, using DMA. |
| 445 | pub async fn write_dma(&mut self, buf: &[u8], transaction: TransferConfig) { | 446 | pub async fn write_dma(&mut self, buf: &[u8], transaction: TransferConfig) { |
| 447 | let _scoped_block_stop = T::RCC_INFO.block_stop(); | ||
| 446 | let transfer = self.start_write_transfer(transaction, buf); | 448 | let transfer = self.start_write_transfer(transaction, buf); |
| 447 | transfer.await; | 449 | transfer.await; |
| 448 | } | 450 | } |
diff --git a/embassy-stm32/src/rcc/bd.rs b/embassy-stm32/src/rcc/bd.rs index 63fc195dd..219be208f 100644 --- a/embassy-stm32/src/rcc/bd.rs +++ b/embassy-stm32/src/rcc/bd.rs | |||
| @@ -1,6 +1,10 @@ | |||
| 1 | use core::sync::atomic::{compiler_fence, Ordering}; | 1 | #[cfg(not(stm32n6))] |
| 2 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 2 | 3 | ||
| 3 | use crate::pac::common::{Reg, RW}; | 4 | #[cfg(not(stm32n6))] |
| 5 | use crate::pac::common::{RW, Reg}; | ||
| 6 | #[cfg(backup_sram)] | ||
| 7 | use crate::pac::pwr::vals::Retention; | ||
| 4 | pub use crate::pac::rcc::vals::Rtcsel as RtcClockSource; | 8 | pub use crate::pac::rcc::vals::Rtcsel as RtcClockSource; |
| 5 | use crate::time::Hertz; | 9 | use crate::time::Hertz; |
| 6 | 10 | ||
| @@ -52,7 +56,7 @@ impl From<LseDrive> for crate::pac::rcc::vals::Lsedrv { | |||
| 52 | } | 56 | } |
| 53 | } | 57 | } |
| 54 | 58 | ||
| 55 | #[cfg(not(any(rtc_v2_l0, rtc_v2_l1, stm32c0)))] | 59 | #[cfg(not(any(rtc_v2_l0, rtc_v2_l1, stm32c0, stm32n6)))] |
| 56 | type Bdcr = crate::pac::rcc::regs::Bdcr; | 60 | type Bdcr = crate::pac::rcc::regs::Bdcr; |
| 57 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] | 61 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] |
| 58 | type Bdcr = crate::pac::rcc::regs::Csr; | 62 | type Bdcr = crate::pac::rcc::regs::Csr; |
| @@ -62,19 +66,22 @@ type Bdcr = crate::pac::rcc::regs::Csr1; | |||
| 62 | #[cfg(any(stm32c0))] | 66 | #[cfg(any(stm32c0))] |
| 63 | fn unlock() {} | 67 | fn unlock() {} |
| 64 | 68 | ||
| 65 | #[cfg(not(any(stm32c0)))] | 69 | #[cfg(not(any(stm32c0, stm32n6)))] |
| 66 | fn unlock() { | 70 | fn unlock() { |
| 67 | #[cfg(any(stm32f0, stm32f1, stm32f2, stm32f3, stm32l0, stm32l1))] | 71 | #[cfg(any(stm32f0, stm32f1, stm32f2, stm32f3, stm32l0, stm32l1))] |
| 68 | let cr = crate::pac::PWR.cr(); | 72 | let cr = crate::pac::PWR.cr(); |
| 69 | #[cfg(not(any(stm32f0, stm32f1, stm32f2, stm32f3, stm32l0, stm32l1, stm32u5, stm32h5, stm32wba)))] | 73 | #[cfg(not(any( |
| 74 | stm32f0, stm32f1, stm32f2, stm32f3, stm32l0, stm32l1, stm32u5, stm32h5, stm32wba, stm32n6 | ||
| 75 | )))] | ||
| 70 | let cr = crate::pac::PWR.cr1(); | 76 | let cr = crate::pac::PWR.cr1(); |
| 71 | #[cfg(any(stm32u5, stm32h5, stm32wba))] | 77 | #[cfg(any(stm32u5, stm32h5, stm32wba, stm32n6))] |
| 72 | let cr = crate::pac::PWR.dbpcr(); | 78 | let cr = crate::pac::PWR.dbpcr(); |
| 73 | 79 | ||
| 74 | cr.modify(|w| w.set_dbp(true)); | 80 | cr.modify(|w| w.set_dbp(true)); |
| 75 | while !cr.read().dbp() {} | 81 | while !cr.read().dbp() {} |
| 76 | } | 82 | } |
| 77 | 83 | ||
| 84 | #[cfg(not(stm32n6))] | ||
| 78 | fn bdcr() -> Reg<Bdcr, RW> { | 85 | fn bdcr() -> Reg<Bdcr, RW> { |
| 79 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] | 86 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] |
| 80 | return crate::pac::RCC.csr(); | 87 | return crate::pac::RCC.csr(); |
| @@ -89,6 +96,8 @@ pub struct LsConfig { | |||
| 89 | pub rtc: RtcClockSource, | 96 | pub rtc: RtcClockSource, |
| 90 | pub lsi: bool, | 97 | pub lsi: bool, |
| 91 | pub lse: Option<LseConfig>, | 98 | pub lse: Option<LseConfig>, |
| 99 | #[cfg(backup_sram)] | ||
| 100 | pub enable_backup_sram: bool, | ||
| 92 | } | 101 | } |
| 93 | 102 | ||
| 94 | impl LsConfig { | 103 | impl LsConfig { |
| @@ -113,6 +122,8 @@ impl LsConfig { | |||
| 113 | peripherals_clocked: false, | 122 | peripherals_clocked: false, |
| 114 | }), | 123 | }), |
| 115 | lsi: false, | 124 | lsi: false, |
| 125 | #[cfg(backup_sram)] | ||
| 126 | enable_backup_sram: false, | ||
| 116 | } | 127 | } |
| 117 | } | 128 | } |
| 118 | 129 | ||
| @@ -121,6 +132,8 @@ impl LsConfig { | |||
| 121 | rtc: RtcClockSource::LSI, | 132 | rtc: RtcClockSource::LSI, |
| 122 | lsi: true, | 133 | lsi: true, |
| 123 | lse: None, | 134 | lse: None, |
| 135 | #[cfg(backup_sram)] | ||
| 136 | enable_backup_sram: false, | ||
| 124 | } | 137 | } |
| 125 | } | 138 | } |
| 126 | 139 | ||
| @@ -129,6 +142,8 @@ impl LsConfig { | |||
| 129 | rtc: RtcClockSource::DISABLE, | 142 | rtc: RtcClockSource::DISABLE, |
| 130 | lsi: false, | 143 | lsi: false, |
| 131 | lse: None, | 144 | lse: None, |
| 145 | #[cfg(backup_sram)] | ||
| 146 | enable_backup_sram: false, | ||
| 132 | } | 147 | } |
| 133 | } | 148 | } |
| 134 | } | 149 | } |
| @@ -140,6 +155,7 @@ impl Default for LsConfig { | |||
| 140 | } | 155 | } |
| 141 | 156 | ||
| 142 | impl LsConfig { | 157 | impl LsConfig { |
| 158 | #[cfg(not(stm32n6))] | ||
| 143 | pub(crate) fn init(&self) -> Option<Hertz> { | 159 | pub(crate) fn init(&self) -> Option<Hertz> { |
| 144 | let rtc_clk = match self.rtc { | 160 | let rtc_clk = match self.rtc { |
| 145 | RtcClockSource::LSI => { | 161 | RtcClockSource::LSI => { |
| @@ -175,14 +191,19 @@ impl LsConfig { | |||
| 175 | if self.lsi { | 191 | if self.lsi { |
| 176 | #[cfg(any(stm32u5, stm32h5, stm32wba))] | 192 | #[cfg(any(stm32u5, stm32h5, stm32wba))] |
| 177 | let csr = crate::pac::RCC.bdcr(); | 193 | let csr = crate::pac::RCC.bdcr(); |
| 178 | #[cfg(not(any(stm32u5, stm32h5, stm32wba, stm32c0)))] | 194 | #[cfg(stm32n6)] |
| 195 | let csr = crate::pac::RCC.sr(); | ||
| 196 | #[cfg(not(any(stm32u5, stm32h5, stm32wba, stm32c0, stm32n6)))] | ||
| 179 | let csr = crate::pac::RCC.csr(); | 197 | let csr = crate::pac::RCC.csr(); |
| 180 | #[cfg(any(stm32c0))] | 198 | #[cfg(stm32c0)] |
| 181 | let csr = crate::pac::RCC.csr2(); | 199 | let csr = crate::pac::RCC.csr2(); |
| 182 | 200 | ||
| 183 | #[cfg(not(any(rcc_wb, rcc_wba)))] | 201 | #[cfg(not(any(rcc_wb, rcc_wba, rcc_n6)))] |
| 184 | csr.modify(|w| w.set_lsion(true)); | 202 | csr.modify(|w| w.set_lsion(true)); |
| 185 | 203 | ||
| 204 | #[cfg(rcc_n6)] | ||
| 205 | crate::pac::RCC.cr().modify(|w| w.set_lsion(true)); | ||
| 206 | |||
| 186 | #[cfg(any(rcc_wb, rcc_wba))] | 207 | #[cfg(any(rcc_wb, rcc_wba))] |
| 187 | csr.modify(|w| w.set_lsi1on(true)); | 208 | csr.modify(|w| w.set_lsi1on(true)); |
| 188 | 209 | ||
| @@ -193,28 +214,77 @@ impl LsConfig { | |||
| 193 | while !csr.read().lsi1rdy() {} | 214 | while !csr.read().lsi1rdy() {} |
| 194 | } | 215 | } |
| 195 | 216 | ||
| 217 | // Enable backup regulator for peristent battery backed sram | ||
| 218 | #[cfg(backup_sram)] | ||
| 219 | { | ||
| 220 | unsafe { super::BKSRAM_RETAINED = crate::pac::PWR.bdcr().read().bren() == Retention::PRESERVED }; | ||
| 221 | |||
| 222 | crate::pac::PWR.bdcr().modify(|w| { | ||
| 223 | w.set_bren(match self.enable_backup_sram { | ||
| 224 | true => Retention::PRESERVED, | ||
| 225 | false => Retention::LOST, | ||
| 226 | }); | ||
| 227 | }); | ||
| 228 | |||
| 229 | // Wait for backup regulator voltage to stabilize | ||
| 230 | while self.enable_backup_sram && !crate::pac::PWR.bdsr().read().brrdy() {} | ||
| 231 | } | ||
| 232 | |||
| 196 | // backup domain configuration (LSEON, RTCEN, RTCSEL) is kept across resets. | 233 | // backup domain configuration (LSEON, RTCEN, RTCSEL) is kept across resets. |
| 197 | // once set, changing it requires a backup domain reset. | 234 | // once set, changing it requires a backup domain reset. |
| 198 | // first check if the configuration matches what we want. | 235 | // first check if the configuration matches what we want. |
| 236 | // N6 has all the fields spread across multiple registers under RCC. | ||
| 199 | 237 | ||
| 200 | // check if it's already enabled and in the source we want. | 238 | // check if it's already enabled and in the source we want. |
| 239 | #[cfg(not(rcc_n6))] | ||
| 201 | let reg = bdcr().read(); | 240 | let reg = bdcr().read(); |
| 241 | #[cfg(rcc_n6)] | ||
| 242 | let reg = crate::pac::RCC.cr().read(); | ||
| 243 | #[cfg(rcc_n6)] | ||
| 244 | let apb4lenr = crate::pac::RCC.apb4lenr().read(); | ||
| 245 | #[cfg(rcc_n6)] | ||
| 246 | let ccipr7 = crate::pac::RCC.ccipr7().read(); | ||
| 247 | #[cfg(rcc_n6)] | ||
| 248 | let lsecfgr = crate::pac::RCC.lsecfgr().read(); | ||
| 249 | |||
| 202 | let mut ok = true; | 250 | let mut ok = true; |
| 203 | ok &= reg.rtcsel() == self.rtc; | 251 | #[cfg(not(rcc_n6))] |
| 204 | #[cfg(not(rcc_wba))] | 252 | { |
| 253 | ok &= reg.rtcsel() == self.rtc; | ||
| 254 | } | ||
| 255 | #[cfg(rcc_n6)] | ||
| 256 | { | ||
| 257 | ok &= ccipr7.rtcsel() == self.rtc; | ||
| 258 | } | ||
| 259 | #[cfg(not(any(rcc_wba, rcc_n6)))] | ||
| 205 | { | 260 | { |
| 206 | ok &= reg.rtcen() == (self.rtc != RtcClockSource::DISABLE); | 261 | ok &= reg.rtcen() == (self.rtc != RtcClockSource::DISABLE); |
| 207 | } | 262 | } |
| 263 | #[cfg(rcc_n6)] | ||
| 264 | { | ||
| 265 | ok &= apb4lenr.rtcen() == (self.rtc != RtcClockSource::DISABLE); | ||
| 266 | } | ||
| 208 | ok &= reg.lseon() == lse_en; | 267 | ok &= reg.lseon() == lse_en; |
| 209 | ok &= reg.lsebyp() == lse_byp; | 268 | #[cfg(not(rcc_n6))] |
| 269 | { | ||
| 270 | ok &= reg.lsebyp() == lse_byp; | ||
| 271 | } | ||
| 272 | #[cfg(rcc_n6)] | ||
| 273 | { | ||
| 274 | ok &= lsecfgr.lsebyp() == lse_byp; | ||
| 275 | } | ||
| 210 | #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba, rcc_u0))] | 276 | #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba, rcc_u0))] |
| 211 | if let Some(lse_sysen) = lse_sysen { | 277 | if let Some(lse_sysen) = lse_sysen { |
| 212 | ok &= reg.lsesysen() == lse_sysen; | 278 | ok &= reg.lsesysen() == lse_sysen; |
| 213 | } | 279 | } |
| 214 | #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1)))] | 280 | #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1, rcc_n6)))] |
| 215 | if let Some(lse_drv) = lse_drv { | 281 | if let Some(lse_drv) = lse_drv { |
| 216 | ok &= reg.lsedrv() == lse_drv.into(); | 282 | ok &= reg.lsedrv() == lse_drv.into(); |
| 217 | } | 283 | } |
| 284 | #[cfg(rcc_n6)] | ||
| 285 | if let Some(lse_drv) = lse_drv { | ||
| 286 | ok &= lsecfgr.lsedrv() == lse_drv.into(); | ||
| 287 | } | ||
| 218 | 288 | ||
| 219 | // if configuration is OK, we're done. | 289 | // if configuration is OK, we're done. |
| 220 | if ok { | 290 | if ok { |
| @@ -223,7 +293,7 @@ impl LsConfig { | |||
| 223 | } | 293 | } |
| 224 | 294 | ||
| 225 | // If not OK, reset backup domain and configure it. | 295 | // If not OK, reset backup domain and configure it. |
| 226 | #[cfg(not(any(rcc_l0, rcc_l0_v2, rcc_l1, stm32h5, stm32h7rs, stm32c0)))] | 296 | #[cfg(not(any(rcc_l0, rcc_l0_v2, rcc_l1, stm32h5, stm32h7rs, stm32c0, stm32n6)))] |
| 227 | { | 297 | { |
| 228 | bdcr().modify(|w| w.set_bdrst(true)); | 298 | bdcr().modify(|w| w.set_bdrst(true)); |
| 229 | bdcr().modify(|w| w.set_bdrst(false)); | 299 | bdcr().modify(|w| w.set_bdrst(false)); |
| @@ -236,7 +306,7 @@ impl LsConfig { | |||
| 236 | // STM32H503CB/EB/KB/RB device errata - 2.2.8 SRAM2 unduly erased upon a backup domain reset | 306 | // STM32H503CB/EB/KB/RB device errata - 2.2.8 SRAM2 unduly erased upon a backup domain reset |
| 237 | // STM32H562xx/563xx/573xx device errata - 2.2.14 SRAM2 is erased when the backup domain is reset | 307 | // STM32H562xx/563xx/573xx device errata - 2.2.14 SRAM2 is erased when the backup domain is reset |
| 238 | //#[cfg(any(stm32h5, stm32h7rs))] | 308 | //#[cfg(any(stm32h5, stm32h7rs))] |
| 239 | #[cfg(any(stm32h7rs))] | 309 | #[cfg(any(stm32h7rs, stm32n6))] |
| 240 | { | 310 | { |
| 241 | bdcr().modify(|w| w.set_vswrst(true)); | 311 | bdcr().modify(|w| w.set_vswrst(true)); |
| 242 | bdcr().modify(|w| w.set_vswrst(false)); | 312 | bdcr().modify(|w| w.set_vswrst(false)); |
| @@ -248,16 +318,31 @@ impl LsConfig { | |||
| 248 | } | 318 | } |
| 249 | 319 | ||
| 250 | if lse_en { | 320 | if lse_en { |
| 251 | bdcr().modify(|w| { | 321 | #[cfg(not(rcc_n6))] |
| 252 | #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1)))] | 322 | { |
| 253 | if let Some(lse_drv) = lse_drv { | 323 | bdcr().modify(|w| { |
| 254 | w.set_lsedrv(lse_drv.into()); | 324 | #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1)))] |
| 255 | } | 325 | if let Some(lse_drv) = lse_drv { |
| 256 | w.set_lsebyp(lse_byp); | 326 | w.set_lsedrv(lse_drv.into()); |
| 257 | w.set_lseon(true); | 327 | } |
| 258 | }); | 328 | w.set_lsebyp(lse_byp); |
| 329 | w.set_lseon(true); | ||
| 330 | }); | ||
| 331 | |||
| 332 | while !bdcr().read().lserdy() {} | ||
| 333 | } | ||
| 334 | #[cfg(rcc_n6)] | ||
| 335 | { | ||
| 336 | crate::pac::RCC.lsecfgr().modify(|w| { | ||
| 337 | if let Some(lse_drv) = lse_drv { | ||
| 338 | w.set_lsedrv(lse_drv.into()); | ||
| 339 | } | ||
| 340 | w.set_lsebyp(lse_byp); | ||
| 341 | }); | ||
| 342 | crate::pac::RCC.cr().modify(|w| w.set_lseon(true)); | ||
| 259 | 343 | ||
| 260 | while !bdcr().read().lserdy() {} | 344 | while !crate::pac::RCC.sr().read().lserdy() {} |
| 345 | } | ||
| 261 | 346 | ||
| 262 | #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba, rcc_u0))] | 347 | #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba, rcc_u0))] |
| 263 | if let Some(lse_sysen) = lse_sysen { | 348 | if let Some(lse_sysen) = lse_sysen { |
| @@ -272,6 +357,7 @@ impl LsConfig { | |||
| 272 | } | 357 | } |
| 273 | 358 | ||
| 274 | if self.rtc != RtcClockSource::DISABLE { | 359 | if self.rtc != RtcClockSource::DISABLE { |
| 360 | #[cfg(not(rcc_n6))] | ||
| 275 | bdcr().modify(|w| { | 361 | bdcr().modify(|w| { |
| 276 | #[cfg(any(rtc_v2_h7, rtc_v2_l4, rtc_v2_wb, rtc_v3_base, rtc_v3_u5))] | 362 | #[cfg(any(rtc_v2_h7, rtc_v2_l4, rtc_v2_wb, rtc_v3_base, rtc_v3_u5))] |
| 277 | assert!(!w.lsecsson(), "RTC is not compatible with LSE CSS, yet."); | 363 | assert!(!w.lsecsson(), "RTC is not compatible with LSE CSS, yet."); |
| @@ -280,6 +366,12 @@ impl LsConfig { | |||
| 280 | w.set_rtcen(true); | 366 | w.set_rtcen(true); |
| 281 | w.set_rtcsel(self.rtc); | 367 | w.set_rtcsel(self.rtc); |
| 282 | }); | 368 | }); |
| 369 | |||
| 370 | #[cfg(rcc_n6)] | ||
| 371 | { | ||
| 372 | crate::pac::RCC.ccipr7().modify(|w| w.set_rtcsel(self.rtc)); | ||
| 373 | crate::pac::RCC.apb4lenr().modify(|w| w.set_rtcen(true)) | ||
| 374 | } | ||
| 283 | } | 375 | } |
| 284 | 376 | ||
| 285 | trace!("BDCR configured: {:08x}", bdcr().read().0); | 377 | trace!("BDCR configured: {:08x}", bdcr().read().0); |
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..0d668103c 100644 --- a/embassy-stm32/src/rcc/l.rs +++ b/embassy-stm32/src/rcc/l.rs | |||
| @@ -135,7 +135,14 @@ pub const WPAN_DEFAULT: Config = Config { | |||
| 135 | apb1_pre: APBPrescaler::DIV1, | 135 | apb1_pre: APBPrescaler::DIV1, |
| 136 | apb2_pre: APBPrescaler::DIV1, | 136 | apb2_pre: APBPrescaler::DIV1, |
| 137 | 137 | ||
| 138 | mux: super::mux::ClockMux::default(), | 138 | mux: { |
| 139 | use crate::pac::rcc::vals::Rfwkpsel; | ||
| 140 | |||
| 141 | let mut mux = super::mux::ClockMux::default(); | ||
| 142 | |||
| 143 | mux.rfwkpsel = Rfwkpsel::LSE; | ||
| 144 | mux | ||
| 145 | }, | ||
| 139 | }; | 146 | }; |
| 140 | 147 | ||
| 141 | fn msi_enable(range: MSIRange) { | 148 | fn msi_enable(range: MSIRange) { |
| @@ -499,9 +506,9 @@ pub use pll::*; | |||
| 499 | 506 | ||
| 500 | #[cfg(any(stm32l0, stm32l1))] | 507 | #[cfg(any(stm32l0, stm32l1))] |
| 501 | mod pll { | 508 | mod pll { |
| 502 | use super::{pll_enable, PllInstance}; | 509 | 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; | 510 | use crate::pac::RCC; |
| 511 | pub use crate::pac::rcc::vals::{Plldiv as PllDiv, Pllmul as PllMul, Pllsrc as PllSource}; | ||
| 505 | use crate::time::Hertz; | 512 | use crate::time::Hertz; |
| 506 | 513 | ||
| 507 | #[derive(Clone, Copy)] | 514 | #[derive(Clone, Copy)] |
| @@ -563,11 +570,11 @@ mod pll { | |||
| 563 | 570 | ||
| 564 | #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))] | 571 | #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))] |
| 565 | mod pll { | 572 | mod pll { |
| 566 | use super::{pll_enable, PllInstance}; | 573 | use super::{PllInstance, pll_enable}; |
| 574 | use crate::pac::RCC; | ||
| 567 | pub use crate::pac::rcc::vals::{ | 575 | 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, | 576 | Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc as PllSource, |
| 569 | }; | 577 | }; |
| 570 | use crate::pac::RCC; | ||
| 571 | use crate::time::Hertz; | 578 | use crate::time::Hertz; |
| 572 | 579 | ||
| 573 | #[derive(Clone, Copy)] | 580 | #[derive(Clone, Copy)] |
diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs index 59ccc8cb5..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( |
| @@ -15,7 +16,8 @@ pub use crate::pac::rcc::vals::Mcopre as McoPrescaler; | |||
| 15 | rcc_h7ab, | 16 | rcc_h7ab, |
| 16 | rcc_h7rm0433, | 17 | rcc_h7rm0433, |
| 17 | rcc_h7, | 18 | rcc_h7, |
| 18 | rcc_h7rs | 19 | rcc_h7rs, |
| 20 | rcc_n6 | ||
| 19 | )))] | 21 | )))] |
| 20 | pub use crate::pac::rcc::vals::Mcosel as McoSource; | 22 | pub use crate::pac::rcc::vals::Mcosel as McoSource; |
| 21 | #[cfg(any( | 23 | #[cfg(any( |
| @@ -28,11 +30,11 @@ pub use crate::pac::rcc::vals::Mcosel as McoSource; | |||
| 28 | rcc_h7ab, | 30 | rcc_h7ab, |
| 29 | rcc_h7rm0433, | 31 | rcc_h7rm0433, |
| 30 | rcc_h7, | 32 | rcc_h7, |
| 31 | rcc_h7rs | 33 | rcc_h7rs, |
| 34 | rcc_n6 | ||
| 32 | ))] | 35 | ))] |
| 33 | 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}; |
| 34 | use crate::pac::RCC; | 37 | use crate::{Peri, peripherals}; |
| 35 | use crate::{peripherals, Peri}; | ||
| 36 | 38 | ||
| 37 | #[cfg(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37))] | 39 | #[cfg(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37))] |
| 38 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] | 40 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] |
| @@ -59,10 +61,12 @@ macro_rules! impl_peri { | |||
| 59 | type Source = $source; | 61 | type Source = $source; |
| 60 | 62 | ||
| 61 | unsafe fn _apply_clock_settings(source: Self::Source, _prescaler: McoPrescaler) { | 63 | unsafe fn _apply_clock_settings(source: Self::Source, _prescaler: McoPrescaler) { |
| 62 | #[cfg(not(any(stm32u5, stm32wba)))] | 64 | #[cfg(not(any(stm32u5, stm32wba, stm32n6)))] |
| 63 | let r = RCC.cfgr(); | 65 | let r = RCC.cfgr(); |
| 64 | #[cfg(any(stm32u5, stm32wba))] | 66 | #[cfg(any(stm32u5, stm32wba))] |
| 65 | let r = RCC.cfgr1(); | 67 | let r = RCC.cfgr1(); |
| 68 | #[cfg(any(stm32n6))] | ||
| 69 | let r = RCC.ccipr5(); | ||
| 66 | 70 | ||
| 67 | r.modify(|w| { | 71 | r.modify(|w| { |
| 68 | w.$set_source(source); | 72 | w.$set_source(source); |
| @@ -91,12 +95,29 @@ pub struct Mco<'d, T: McoInstance> { | |||
| 91 | 95 | ||
| 92 | impl<'d, T: McoInstance> Mco<'d, T> { | 96 | impl<'d, T: McoInstance> Mco<'d, T> { |
| 93 | /// Create a new MCO instance. | 97 | /// Create a new MCO instance. |
| 94 | 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 { |
| 95 | critical_section::with(|_| unsafe { | 99 | critical_section::with(|_| unsafe { |
| 96 | T::_apply_clock_settings(source, prescaler); | 100 | T::_apply_clock_settings(source, config.prescaler); |
| 97 | set_as_af!(pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 101 | set_as_af!(pin, AfType::output(OutputType::PushPull, config.speed)); |
| 98 | }); | 102 | }); |
| 99 | 103 | ||
| 100 | Self { phantom: PhantomData } | 104 | Self { phantom: PhantomData } |
| 101 | } | 105 | } |
| 102 | } | 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 c41f81816..d25c922d8 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #![allow(missing_docs)] // TODO | 4 | #![allow(missing_docs)] // TODO |
| 5 | 5 | ||
| 6 | use core::mem::MaybeUninit; | 6 | use core::mem::MaybeUninit; |
| 7 | use core::ops; | ||
| 7 | 8 | ||
| 8 | mod bd; | 9 | mod bd; |
| 9 | pub use bd::*; | 10 | pub use bd::*; |
| @@ -11,6 +12,7 @@ pub use bd::*; | |||
| 11 | #[cfg(any(mco, mco1, mco2))] | 12 | #[cfg(any(mco, mco1, mco2))] |
| 12 | mod mco; | 13 | mod mco; |
| 13 | use critical_section::CriticalSection; | 14 | use critical_section::CriticalSection; |
| 15 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 14 | #[cfg(any(mco, mco1, mco2))] | 16 | #[cfg(any(mco, mco1, mco2))] |
| 15 | pub use mco::*; | 17 | pub use mco::*; |
| 16 | 18 | ||
| @@ -28,12 +30,13 @@ pub use hsi48::*; | |||
| 28 | #[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl, stm32u0), path = "l.rs")] | 30 | #[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl, stm32u0), path = "l.rs")] |
| 29 | #[cfg_attr(stm32u5, path = "u5.rs")] | 31 | #[cfg_attr(stm32u5, path = "u5.rs")] |
| 30 | #[cfg_attr(stm32wba, path = "wba.rs")] | 32 | #[cfg_attr(stm32wba, path = "wba.rs")] |
| 33 | #[cfg_attr(stm32n6, path = "n6.rs")] | ||
| 31 | mod _version; | 34 | mod _version; |
| 32 | 35 | ||
| 33 | pub use _version::*; | 36 | pub use _version::*; |
| 34 | use stm32_metapac::RCC; | 37 | use stm32_metapac::RCC; |
| 35 | 38 | ||
| 36 | pub use crate::_generated::{mux, Clocks}; | 39 | pub use crate::_generated::{Clocks, mux}; |
| 37 | use crate::time::Hertz; | 40 | use crate::time::Hertz; |
| 38 | 41 | ||
| 39 | #[cfg(feature = "low-power")] | 42 | #[cfg(feature = "low-power")] |
| @@ -48,6 +51,12 @@ pub(crate) static mut REFCOUNT_STOP1: u32 = 0; | |||
| 48 | /// May be read without a critical section | 51 | /// May be read without a critical section |
| 49 | pub(crate) static mut REFCOUNT_STOP2: u32 = 0; | 52 | pub(crate) static mut REFCOUNT_STOP2: u32 = 0; |
| 50 | 53 | ||
| 54 | #[cfg(feature = "low-power")] | ||
| 55 | pub(crate) static mut RCC_CONFIG: Option<Config> = None; | ||
| 56 | |||
| 57 | #[cfg(backup_sram)] | ||
| 58 | pub(crate) static mut BKSRAM_RETAINED: bool = false; | ||
| 59 | |||
| 51 | #[cfg(not(feature = "_dual-core"))] | 60 | #[cfg(not(feature = "_dual-core"))] |
| 52 | /// Frozen clock frequencies | 61 | /// Frozen clock frequencies |
| 53 | /// | 62 | /// |
| @@ -104,6 +113,32 @@ pub fn clocks<'a>(_rcc: &'a crate::Peri<'a, crate::peripherals::RCC>) -> &'a Clo | |||
| 104 | unsafe { get_freqs() } | 113 | unsafe { get_freqs() } |
| 105 | } | 114 | } |
| 106 | 115 | ||
| 116 | #[cfg(feature = "low-power")] | ||
| 117 | fn increment_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { | ||
| 118 | match stop_mode { | ||
| 119 | StopMode::Standby => {} | ||
| 120 | StopMode::Stop2 => unsafe { | ||
| 121 | REFCOUNT_STOP2 += 1; | ||
| 122 | }, | ||
| 123 | StopMode::Stop1 => unsafe { | ||
| 124 | REFCOUNT_STOP1 += 1; | ||
| 125 | }, | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | #[cfg(feature = "low-power")] | ||
| 130 | fn decrement_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { | ||
| 131 | match stop_mode { | ||
| 132 | StopMode::Standby => {} | ||
| 133 | StopMode::Stop2 => unsafe { | ||
| 134 | REFCOUNT_STOP2 -= 1; | ||
| 135 | }, | ||
| 136 | StopMode::Stop1 => unsafe { | ||
| 137 | REFCOUNT_STOP1 -= 1; | ||
| 138 | }, | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 107 | pub(crate) trait SealedRccPeripheral { | 142 | pub(crate) trait SealedRccPeripheral { |
| 108 | fn frequency() -> Hertz; | 143 | fn frequency() -> Hertz; |
| 109 | #[allow(dead_code)] | 144 | #[allow(dead_code)] |
| @@ -134,14 +169,27 @@ pub(crate) struct RccInfo { | |||
| 134 | stop_mode: StopMode, | 169 | stop_mode: StopMode, |
| 135 | } | 170 | } |
| 136 | 171 | ||
| 172 | /// Specifies a limit for the stop mode of the peripheral or the stop mode to be entered. | ||
| 173 | /// E.g. if `StopMode::Stop1` is selected, the peripheral prevents the chip from entering Stop1 mode. | ||
| 137 | #[cfg(feature = "low-power")] | 174 | #[cfg(feature = "low-power")] |
| 138 | #[allow(dead_code)] | 175 | #[allow(dead_code)] |
| 139 | pub(crate) enum StopMode { | 176 | #[derive(Debug, Clone, Copy, PartialEq, Default, defmt::Format)] |
| 140 | Standby, | 177 | pub enum StopMode { |
| 141 | Stop2, | 178 | #[default] |
| 179 | /// Peripheral prevents chip from entering Stop1 or executor will enter Stop1 | ||
| 142 | Stop1, | 180 | Stop1, |
| 181 | /// Peripheral prevents chip from entering Stop2 or executor will enter Stop2 | ||
| 182 | Stop2, | ||
| 183 | /// Peripheral does not prevent chip from entering Stop | ||
| 184 | Standby, | ||
| 143 | } | 185 | } |
| 144 | 186 | ||
| 187 | #[cfg(feature = "low-power")] | ||
| 188 | type BusyRccPeripheral = BusyPeripheral<StopMode>; | ||
| 189 | |||
| 190 | #[cfg(not(feature = "low-power"))] | ||
| 191 | type BusyRccPeripheral = (); | ||
| 192 | |||
| 145 | impl RccInfo { | 193 | impl RccInfo { |
| 146 | /// Safety: | 194 | /// Safety: |
| 147 | /// - `reset_offset_and_bit`, if set, must correspond to valid xxxRST bit | 195 | /// - `reset_offset_and_bit`, if set, must correspond to valid xxxRST bit |
| @@ -194,17 +242,6 @@ impl RccInfo { | |||
| 194 | } | 242 | } |
| 195 | } | 243 | } |
| 196 | 244 | ||
| 197 | #[cfg(feature = "low-power")] | ||
| 198 | match self.stop_mode { | ||
| 199 | StopMode::Standby => {} | ||
| 200 | StopMode::Stop2 => unsafe { | ||
| 201 | REFCOUNT_STOP2 += 1; | ||
| 202 | }, | ||
| 203 | StopMode::Stop1 => unsafe { | ||
| 204 | REFCOUNT_STOP1 += 1; | ||
| 205 | }, | ||
| 206 | } | ||
| 207 | |||
| 208 | // set the xxxRST bit | 245 | // set the xxxRST bit |
| 209 | let reset_ptr = self.reset_ptr(); | 246 | let reset_ptr = self.reset_ptr(); |
| 210 | if let Some(reset_ptr) = reset_ptr { | 247 | if let Some(reset_ptr) = reset_ptr { |
| @@ -260,17 +297,6 @@ impl RccInfo { | |||
| 260 | } | 297 | } |
| 261 | } | 298 | } |
| 262 | 299 | ||
| 263 | #[cfg(feature = "low-power")] | ||
| 264 | match self.stop_mode { | ||
| 265 | StopMode::Standby => {} | ||
| 266 | StopMode::Stop2 => unsafe { | ||
| 267 | REFCOUNT_STOP2 -= 1; | ||
| 268 | }, | ||
| 269 | StopMode::Stop1 => unsafe { | ||
| 270 | REFCOUNT_STOP1 -= 1; | ||
| 271 | }, | ||
| 272 | } | ||
| 273 | |||
| 274 | // clear the xxxEN bit | 300 | // clear the xxxEN bit |
| 275 | let enable_ptr = self.enable_ptr(); | 301 | let enable_ptr = self.enable_ptr(); |
| 276 | unsafe { | 302 | unsafe { |
| @@ -279,14 +305,85 @@ impl RccInfo { | |||
| 279 | } | 305 | } |
| 280 | } | 306 | } |
| 281 | 307 | ||
| 308 | #[allow(dead_code)] | ||
| 309 | pub(crate) fn increment_stop_refcount_with_cs(&self, _cs: CriticalSection) { | ||
| 310 | #[cfg(feature = "low-power")] | ||
| 311 | increment_stop_refcount(_cs, self.stop_mode); | ||
| 312 | } | ||
| 313 | |||
| 314 | #[allow(dead_code)] | ||
| 315 | fn increment_minimum_stop_refcount_with_cs(&self, _cs: CriticalSection) { | ||
| 316 | #[cfg(all(stm32wlex, feature = "low-power"))] | ||
| 317 | match self.stop_mode { | ||
| 318 | StopMode::Stop1 | StopMode::Stop2 => increment_stop_refcount(_cs, StopMode::Stop2), | ||
| 319 | _ => {} | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | #[allow(dead_code)] | ||
| 324 | pub(crate) fn increment_stop_refcount(&self) { | ||
| 325 | #[cfg(feature = "low-power")] | ||
| 326 | critical_section::with(|cs| self.increment_stop_refcount_with_cs(cs)) | ||
| 327 | } | ||
| 328 | |||
| 329 | #[allow(dead_code)] | ||
| 330 | pub(crate) fn decrement_stop_refcount_with_cs(&self, _cs: CriticalSection) { | ||
| 331 | #[cfg(feature = "low-power")] | ||
| 332 | decrement_stop_refcount(_cs, self.stop_mode); | ||
| 333 | } | ||
| 334 | |||
| 335 | #[allow(dead_code)] | ||
| 336 | fn decrement_minimum_stop_refcount_with_cs(&self, _cs: CriticalSection) { | ||
| 337 | #[cfg(all(stm32wlex, feature = "low-power"))] | ||
| 338 | match self.stop_mode { | ||
| 339 | StopMode::Stop1 | StopMode::Stop2 => decrement_stop_refcount(_cs, StopMode::Stop2), | ||
| 340 | _ => {} | ||
| 341 | } | ||
| 342 | } | ||
| 343 | |||
| 344 | #[allow(dead_code)] | ||
| 345 | pub(crate) fn decrement_stop_refcount(&self) { | ||
| 346 | #[cfg(feature = "low-power")] | ||
| 347 | critical_section::with(|cs| self.decrement_stop_refcount_with_cs(cs)) | ||
| 348 | } | ||
| 349 | |||
| 282 | // TODO: should this be `unsafe`? | 350 | // TODO: should this be `unsafe`? |
| 283 | pub(crate) fn enable_and_reset(&self) { | 351 | pub(crate) fn enable_and_reset(&self) { |
| 284 | critical_section::with(|cs| self.enable_and_reset_with_cs(cs)) | 352 | critical_section::with(|cs| { |
| 353 | self.enable_and_reset_with_cs(cs); | ||
| 354 | self.increment_stop_refcount_with_cs(cs); | ||
| 355 | }) | ||
| 356 | } | ||
| 357 | |||
| 358 | #[allow(dead_code)] | ||
| 359 | pub(crate) fn enable_and_reset_without_stop(&self) { | ||
| 360 | critical_section::with(|cs| { | ||
| 361 | self.enable_and_reset_with_cs(cs); | ||
| 362 | self.increment_minimum_stop_refcount_with_cs(cs); | ||
| 363 | }) | ||
| 285 | } | 364 | } |
| 286 | 365 | ||
| 287 | // TODO: should this be `unsafe`? | 366 | // TODO: should this be `unsafe`? |
| 288 | pub(crate) fn disable(&self) { | 367 | pub(crate) fn disable(&self) { |
| 289 | critical_section::with(|cs| self.disable_with_cs(cs)) | 368 | critical_section::with(|cs| { |
| 369 | self.disable_with_cs(cs); | ||
| 370 | self.decrement_stop_refcount_with_cs(cs); | ||
| 371 | }) | ||
| 372 | } | ||
| 373 | |||
| 374 | // TODO: should this be `unsafe`? | ||
| 375 | #[allow(dead_code)] | ||
| 376 | pub(crate) fn disable_without_stop(&self) { | ||
| 377 | critical_section::with(|cs| { | ||
| 378 | self.disable_with_cs(cs); | ||
| 379 | self.decrement_minimum_stop_refcount_with_cs(cs); | ||
| 380 | }) | ||
| 381 | } | ||
| 382 | |||
| 383 | #[allow(dead_code)] | ||
| 384 | pub(crate) fn block_stop(&self) -> BusyRccPeripheral { | ||
| 385 | #[cfg(feature = "low-power")] | ||
| 386 | BusyPeripheral::new(self.stop_mode) | ||
| 290 | } | 387 | } |
| 291 | 388 | ||
| 292 | fn reset_ptr(&self) -> Option<*mut u32> { | 389 | fn reset_ptr(&self) -> Option<*mut u32> { |
| @@ -302,6 +399,60 @@ impl RccInfo { | |||
| 302 | } | 399 | } |
| 303 | } | 400 | } |
| 304 | 401 | ||
| 402 | pub(crate) trait StoppablePeripheral { | ||
| 403 | #[cfg(feature = "low-power")] | ||
| 404 | #[allow(dead_code)] | ||
| 405 | fn stop_mode(&self) -> StopMode; | ||
| 406 | } | ||
| 407 | |||
| 408 | #[cfg(feature = "low-power")] | ||
| 409 | impl StoppablePeripheral for StopMode { | ||
| 410 | fn stop_mode(&self) -> StopMode { | ||
| 411 | *self | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | impl<'a, T: StoppablePeripheral + PeripheralType> StoppablePeripheral for Peri<'a, T> { | ||
| 416 | #[cfg(feature = "low-power")] | ||
| 417 | fn stop_mode(&self) -> StopMode { | ||
| 418 | T::stop_mode(&self) | ||
| 419 | } | ||
| 420 | } | ||
| 421 | |||
| 422 | pub(crate) struct BusyPeripheral<T: StoppablePeripheral> { | ||
| 423 | peripheral: T, | ||
| 424 | } | ||
| 425 | |||
| 426 | impl<T: StoppablePeripheral> BusyPeripheral<T> { | ||
| 427 | pub fn new(peripheral: T) -> Self { | ||
| 428 | #[cfg(feature = "low-power")] | ||
| 429 | critical_section::with(|cs| increment_stop_refcount(cs, peripheral.stop_mode())); | ||
| 430 | |||
| 431 | Self { peripheral } | ||
| 432 | } | ||
| 433 | } | ||
| 434 | |||
| 435 | impl<T: StoppablePeripheral> Drop for BusyPeripheral<T> { | ||
| 436 | fn drop(&mut self) { | ||
| 437 | #[cfg(feature = "low-power")] | ||
| 438 | critical_section::with(|cs| decrement_stop_refcount(cs, self.peripheral.stop_mode())); | ||
| 439 | } | ||
| 440 | } | ||
| 441 | |||
| 442 | impl<T: StoppablePeripheral> ops::Deref for BusyPeripheral<T> { | ||
| 443 | type Target = T; | ||
| 444 | |||
| 445 | fn deref(&self) -> &Self::Target { | ||
| 446 | &self.peripheral | ||
| 447 | } | ||
| 448 | } | ||
| 449 | |||
| 450 | impl<T: StoppablePeripheral> ops::DerefMut for BusyPeripheral<T> { | ||
| 451 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 452 | &mut self.peripheral | ||
| 453 | } | ||
| 454 | } | ||
| 455 | |||
| 305 | #[allow(unused)] | 456 | #[allow(unused)] |
| 306 | mod util { | 457 | mod util { |
| 307 | use crate::time::Hertz; | 458 | use crate::time::Hertz; |
| @@ -371,6 +522,16 @@ pub fn enable_and_reset<T: RccPeripheral>() { | |||
| 371 | T::RCC_INFO.enable_and_reset(); | 522 | T::RCC_INFO.enable_and_reset(); |
| 372 | } | 523 | } |
| 373 | 524 | ||
| 525 | /// Enables and resets peripheral `T` without incrementing the stop refcount. | ||
| 526 | /// | ||
| 527 | /// # Safety | ||
| 528 | /// | ||
| 529 | /// Peripheral must not be in use. | ||
| 530 | // TODO: should this be `unsafe`? | ||
| 531 | pub fn enable_and_reset_without_stop<T: RccPeripheral>() { | ||
| 532 | T::RCC_INFO.enable_and_reset_without_stop(); | ||
| 533 | } | ||
| 534 | |||
| 374 | /// Disables peripheral `T`. | 535 | /// Disables peripheral `T`. |
| 375 | /// | 536 | /// |
| 376 | /// # Safety | 537 | /// # Safety |
| @@ -390,7 +551,7 @@ pub fn disable<T: RccPeripheral>() { | |||
| 390 | /// | 551 | /// |
| 391 | /// This should only be called after `init`. | 552 | /// This should only be called after `init`. |
| 392 | #[cfg(not(feature = "_dual-core"))] | 553 | #[cfg(not(feature = "_dual-core"))] |
| 393 | pub fn reinit<'a>(config: Config, _rcc: &'a mut crate::Peri<'a, crate::peripherals::RCC>) { | 554 | pub fn reinit(config: Config, _rcc: &'_ mut crate::Peri<'_, crate::peripherals::RCC>) { |
| 394 | critical_section::with(|cs| init_rcc(cs, config)) | 555 | critical_section::with(|cs| init_rcc(cs, config)) |
| 395 | } | 556 | } |
| 396 | 557 | ||
| @@ -404,8 +565,39 @@ pub(crate) fn init_rcc(_cs: CriticalSection, config: Config) { | |||
| 404 | 565 | ||
| 405 | #[cfg(feature = "low-power")] | 566 | #[cfg(feature = "low-power")] |
| 406 | { | 567 | { |
| 568 | RCC_CONFIG = Some(config); | ||
| 407 | REFCOUNT_STOP2 = 0; | 569 | REFCOUNT_STOP2 = 0; |
| 408 | REFCOUNT_STOP1 = 0; | 570 | REFCOUNT_STOP1 = 0; |
| 409 | } | 571 | } |
| 410 | } | 572 | } |
| 411 | } | 573 | } |
| 574 | |||
| 575 | /// Calculate intermediate prescaler number used to calculate peripheral prescalers | ||
| 576 | /// | ||
| 577 | /// This function is intended to calculate a number indicating a minimum division | ||
| 578 | /// necessary to result in a frequency lower than the provided `freq_max`. | ||
| 579 | /// | ||
| 580 | /// The returned value indicates the `val + 1` divider is necessary to result in | ||
| 581 | /// the output frequency that is below the maximum provided. | ||
| 582 | /// | ||
| 583 | /// For example: | ||
| 584 | /// 0 = divider of 1 => no division necessary as the input frequency is below max | ||
| 585 | /// 1 = divider of 2 => division by 2 necessary | ||
| 586 | /// ... | ||
| 587 | /// | ||
| 588 | /// The provided max frequency is inclusive. So if `freq_in == freq_max` the result | ||
| 589 | /// will be 0, indicating that no division is necessary. To accomplish that we subtract | ||
| 590 | /// 1 from the input frequency so that the integer rounding plays in our favor. | ||
| 591 | /// | ||
| 592 | /// For example: | ||
| 593 | /// Let the input frequency be 110 and the max frequency be 55. | ||
| 594 | /// If we naiively do `110/55 = 2` the renult will indicate that we need a divider by 3 | ||
| 595 | /// which in reality will be rounded up to 4 as usually a 3 division is not available. | ||
| 596 | /// In either case the resulting frequency will be either 36 or 27 which is lower than | ||
| 597 | /// what we would want. The result should be 1. | ||
| 598 | /// If we do the following instead `109/55 = 1` indicating that we need a divide by 2 | ||
| 599 | /// which will result in the correct 55. | ||
| 600 | #[allow(unused)] | ||
| 601 | pub(crate) fn raw_prescaler(freq_in: u32, freq_max: u32) -> u32 { | ||
| 602 | freq_in.saturating_sub(1) / freq_max | ||
| 603 | } | ||
diff --git a/embassy-stm32/src/rcc/n6.rs b/embassy-stm32/src/rcc/n6.rs new file mode 100644 index 000000000..178ec57d4 --- /dev/null +++ b/embassy-stm32/src/rcc/n6.rs | |||
| @@ -0,0 +1,1066 @@ | |||
| 1 | use stm32_metapac::rcc::vals::{ | ||
| 2 | Cpusw, Cpusws, Hseext, Hsitrim, Icint, Icsel, Msifreqsel, Plldivm, Pllmodssdis, Pllpdiv, Pllsel, Syssw, Syssws, | ||
| 3 | Timpre, | ||
| 4 | }; | ||
| 5 | pub use stm32_metapac::rcc::vals::{ | ||
| 6 | Hpre as AhbPrescaler, Hsidiv as HsiPrescaler, Hsitrim as HsiCalibration, Ppre as ApbPrescaler, | ||
| 7 | }; | ||
| 8 | |||
| 9 | use crate::pac::{PWR, RCC, SYSCFG}; | ||
| 10 | use crate::time::Hertz; | ||
| 11 | |||
| 12 | pub const HSI_FREQ: Hertz = Hertz(64_000_000); | ||
| 13 | pub const LSE_FREQ: Hertz = Hertz(32_768); | ||
| 14 | |||
| 15 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 16 | pub enum HseMode { | ||
| 17 | /// crystal/ceramic oscillator | ||
| 18 | Oscillator, | ||
| 19 | /// oscillator bypassed with external clock (analog) | ||
| 20 | Bypass, | ||
| 21 | /// oscillator bypassed with external digital clock | ||
| 22 | BypassDigital, | ||
| 23 | } | ||
| 24 | |||
| 25 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 26 | pub struct Hse { | ||
| 27 | /// HSE frequency. | ||
| 28 | pub freq: Hertz, | ||
| 29 | /// HSE oscillator mode. | ||
| 30 | pub mode: HseMode, | ||
| 31 | } | ||
| 32 | |||
| 33 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 34 | pub struct Hsi { | ||
| 35 | pub pre: HsiPrescaler, | ||
| 36 | pub trim: Hsitrim, | ||
| 37 | } | ||
| 38 | |||
| 39 | #[derive(Clone, Copy, PartialEq)] | ||
| 40 | pub enum SupplyConfig { | ||
| 41 | Smps, | ||
| 42 | External, | ||
| 43 | } | ||
| 44 | |||
| 45 | #[derive(Clone, Copy, PartialEq)] | ||
| 46 | pub enum CpuClk { | ||
| 47 | Hse, | ||
| 48 | Ic1 { source: Icsel, divider: Icint }, | ||
| 49 | Msi, | ||
| 50 | Hsi, | ||
| 51 | } | ||
| 52 | |||
| 53 | impl CpuClk { | ||
| 54 | const fn to_bits(self) -> u8 { | ||
| 55 | match self { | ||
| 56 | Self::Hsi => 0x0, | ||
| 57 | Self::Msi => 0x1, | ||
| 58 | Self::Hse => 0x2, | ||
| 59 | Self::Ic1 { .. } => 0x3, | ||
| 60 | } | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | #[derive(Clone, Copy, PartialEq)] | ||
| 65 | pub struct IcConfig { | ||
| 66 | source: Icsel, | ||
| 67 | divider: Icint, | ||
| 68 | } | ||
| 69 | |||
| 70 | #[derive(Clone, Copy, PartialEq)] | ||
| 71 | pub enum SysClk { | ||
| 72 | Hse, | ||
| 73 | Ic2 { | ||
| 74 | ic2: IcConfig, | ||
| 75 | ic6: IcConfig, | ||
| 76 | ic11: IcConfig, | ||
| 77 | }, | ||
| 78 | Msi, | ||
| 79 | Hsi, | ||
| 80 | } | ||
| 81 | |||
| 82 | impl SysClk { | ||
| 83 | const fn to_bits(self) -> u8 { | ||
| 84 | match self { | ||
| 85 | Self::Hsi => 0x0, | ||
| 86 | Self::Msi => 0x1, | ||
| 87 | Self::Hse => 0x2, | ||
| 88 | Self::Ic2 { .. } => 0x3, | ||
| 89 | } | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | #[derive(Clone, Copy, PartialEq)] | ||
| 94 | pub struct Msi { | ||
| 95 | pub freq: Msifreqsel, | ||
| 96 | pub trim: u8, | ||
| 97 | } | ||
| 98 | |||
| 99 | #[derive(Clone, Copy, PartialEq)] | ||
| 100 | pub enum Pll { | ||
| 101 | Oscillator { | ||
| 102 | source: Pllsel, | ||
| 103 | divm: Plldivm, | ||
| 104 | fractional: u32, | ||
| 105 | divn: u16, | ||
| 106 | divp1: Pllpdiv, | ||
| 107 | divp2: Pllpdiv, | ||
| 108 | }, | ||
| 109 | Bypass { | ||
| 110 | source: Pllsel, | ||
| 111 | }, | ||
| 112 | } | ||
| 113 | |||
| 114 | /// Configuration of the core clocks | ||
| 115 | #[non_exhaustive] | ||
| 116 | #[derive(Clone, Copy)] | ||
| 117 | pub struct Config { | ||
| 118 | pub hsi: Option<Hsi>, | ||
| 119 | pub hse: Option<Hse>, | ||
| 120 | pub msi: Option<Msi>, | ||
| 121 | pub lsi: bool, | ||
| 122 | pub lse: bool, | ||
| 123 | |||
| 124 | pub sys: SysClk, | ||
| 125 | pub cpu: CpuClk, | ||
| 126 | |||
| 127 | pub pll1: Option<Pll>, | ||
| 128 | pub pll2: Option<Pll>, | ||
| 129 | pub pll3: Option<Pll>, | ||
| 130 | pub pll4: Option<Pll>, | ||
| 131 | |||
| 132 | pub ahb: AhbPrescaler, | ||
| 133 | pub apb1: ApbPrescaler, | ||
| 134 | pub apb2: ApbPrescaler, | ||
| 135 | pub apb4: ApbPrescaler, | ||
| 136 | pub apb5: ApbPrescaler, | ||
| 137 | |||
| 138 | pub supply_config: SupplyConfig, | ||
| 139 | } | ||
| 140 | |||
| 141 | impl Config { | ||
| 142 | pub const fn new() -> Self { | ||
| 143 | Self { | ||
| 144 | hsi: Some(Hsi { | ||
| 145 | pre: HsiPrescaler::DIV1, | ||
| 146 | trim: HsiCalibration::from_bits(32), | ||
| 147 | }), | ||
| 148 | hse: None, | ||
| 149 | msi: None, | ||
| 150 | lsi: true, | ||
| 151 | lse: false, | ||
| 152 | sys: SysClk::Hsi, | ||
| 153 | cpu: CpuClk::Hsi, | ||
| 154 | pll1: Some(Pll::Bypass { source: Pllsel::HSI }), | ||
| 155 | pll2: Some(Pll::Bypass { source: Pllsel::HSI }), | ||
| 156 | pll3: Some(Pll::Bypass { source: Pllsel::HSI }), | ||
| 157 | pll4: Some(Pll::Bypass { source: Pllsel::HSI }), | ||
| 158 | |||
| 159 | ahb: AhbPrescaler::DIV2, | ||
| 160 | apb1: ApbPrescaler::DIV1, | ||
| 161 | apb2: ApbPrescaler::DIV1, | ||
| 162 | apb4: ApbPrescaler::DIV1, | ||
| 163 | apb5: ApbPrescaler::DIV1, | ||
| 164 | |||
| 165 | supply_config: SupplyConfig::Smps, | ||
| 166 | } | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | #[allow(dead_code)] | ||
| 171 | struct ClocksOutput { | ||
| 172 | cpuclk: Hertz, | ||
| 173 | sysclk: Hertz, | ||
| 174 | pclk_tim: Hertz, | ||
| 175 | ahb: Hertz, | ||
| 176 | apb1: Hertz, | ||
| 177 | apb2: Hertz, | ||
| 178 | apb4: Hertz, | ||
| 179 | apb5: Hertz, | ||
| 180 | } | ||
| 181 | |||
| 182 | struct ClocksInput { | ||
| 183 | hsi: Option<Hertz>, | ||
| 184 | msi: Option<Hertz>, | ||
| 185 | hse: Option<Hertz>, | ||
| 186 | } | ||
| 187 | |||
| 188 | fn init_clocks(config: Config, input: &ClocksInput) -> ClocksOutput { | ||
| 189 | // handle increasing dividers | ||
| 190 | debug!("configuring increasing pclk dividers"); | ||
| 191 | RCC.cfgr2().modify(|w| { | ||
| 192 | if config.apb1 > w.ppre1() { | ||
| 193 | debug!(" - APB1"); | ||
| 194 | w.set_ppre1(config.apb1); | ||
| 195 | } | ||
| 196 | if config.apb2 > w.ppre2() { | ||
| 197 | debug!(" - APB2"); | ||
| 198 | w.set_ppre2(config.apb2); | ||
| 199 | } | ||
| 200 | if config.apb4 > w.ppre4() { | ||
| 201 | debug!(" - APB4"); | ||
| 202 | w.set_ppre4(config.apb4); | ||
| 203 | } | ||
| 204 | if config.apb5 > w.ppre5() { | ||
| 205 | debug!(" - APB5"); | ||
| 206 | w.set_ppre5(config.apb5); | ||
| 207 | } | ||
| 208 | if config.ahb > w.hpre() { | ||
| 209 | debug!(" - AHB"); | ||
| 210 | w.set_hpre(config.ahb); | ||
| 211 | } | ||
| 212 | }); | ||
| 213 | // cpuclk | ||
| 214 | debug!("configuring cpuclk"); | ||
| 215 | match config.cpu { | ||
| 216 | CpuClk::Hse if !RCC.sr().read().hserdy() => panic!("HSE is not ready to be selected as CPU clock source"), | ||
| 217 | CpuClk::Ic1 { source, divider } => { | ||
| 218 | if !pll_sources_ready(RCC.iccfgr(0).read().icsel().to_bits(), source.to_bits()) { | ||
| 219 | panic!("ICx clock switch requires both origin and destination clock source to be active") | ||
| 220 | } | ||
| 221 | |||
| 222 | RCC.iccfgr(0).write(|w| { | ||
| 223 | w.set_icsel(source); | ||
| 224 | w.set_icint(divider); | ||
| 225 | }); | ||
| 226 | RCC.divensr().modify(|w| w.set_ic1ens(true)); | ||
| 227 | } | ||
| 228 | CpuClk::Msi if !RCC.sr().read().msirdy() => panic!("MSI is not ready to be selected as CPU clock source"), | ||
| 229 | CpuClk::Hsi if !RCC.sr().read().hsirdy() => panic!("HSI is not ready to be selected as CPU clock source"), | ||
| 230 | _ => {} | ||
| 231 | } | ||
| 232 | // set source | ||
| 233 | let cpusw = Cpusw::from_bits(config.cpu.to_bits()); | ||
| 234 | RCC.cfgr().modify(|w| w.set_cpusw(cpusw)); | ||
| 235 | // wait for changes to take effect | ||
| 236 | while RCC.cfgr().read().cpusws() != Cpusws::from_bits(config.cpu.to_bits()) {} | ||
| 237 | |||
| 238 | // sysclk | ||
| 239 | debug!("configuring sysclk"); | ||
| 240 | match config.sys { | ||
| 241 | SysClk::Hse if !RCC.sr().read().hserdy() => panic!("HSE is not ready to be selected as CPU clock source"), | ||
| 242 | SysClk::Ic2 { ic2, ic6, ic11 } => { | ||
| 243 | if !pll_sources_ready(RCC.iccfgr(1).read().icsel().to_bits(), ic2.source.to_bits()) { | ||
| 244 | panic!("IC2 clock switch requires both origin and destination clock source to be active") | ||
| 245 | } | ||
| 246 | if !pll_sources_ready(RCC.iccfgr(5).read().icsel().to_bits(), ic6.source.to_bits()) { | ||
| 247 | panic!("IC6 clock switch requires both origin and destination clock source to be active") | ||
| 248 | } | ||
| 249 | if !pll_sources_ready(RCC.iccfgr(10).read().icsel().to_bits(), ic11.source.to_bits()) { | ||
| 250 | panic!("IC11 clock switch requires both origin and destination clock source to be active") | ||
| 251 | } | ||
| 252 | |||
| 253 | RCC.iccfgr(1).write(|w| { | ||
| 254 | w.set_icsel(ic2.source); | ||
| 255 | w.set_icint(ic2.divider); | ||
| 256 | }); | ||
| 257 | RCC.iccfgr(5).write(|w| { | ||
| 258 | w.set_icsel(ic6.source); | ||
| 259 | w.set_icint(ic6.divider); | ||
| 260 | }); | ||
| 261 | RCC.iccfgr(10).write(|w| { | ||
| 262 | w.set_icsel(ic11.source); | ||
| 263 | w.set_icint(ic11.divider); | ||
| 264 | }); | ||
| 265 | RCC.divensr().modify(|w| { | ||
| 266 | w.set_ic2ens(true); | ||
| 267 | w.set_ic6ens(true); | ||
| 268 | w.set_ic11ens(true); | ||
| 269 | }); | ||
| 270 | } | ||
| 271 | SysClk::Msi if !RCC.sr().read().msirdy() => panic!("MSI is not ready to be selected as CPU clock source"), | ||
| 272 | SysClk::Hsi if !RCC.sr().read().hsirdy() => panic!("HSI is not ready to be selected as CPU clock source"), | ||
| 273 | _ => {} | ||
| 274 | } | ||
| 275 | // switch the system bus clock | ||
| 276 | let syssw = Syssw::from_bits(config.sys.to_bits()); | ||
| 277 | RCC.cfgr().modify(|w| w.set_syssw(syssw)); | ||
| 278 | // wait for changes to be applied | ||
| 279 | while RCC.cfgr().read().syssws() != Syssws::from_bits(config.sys.to_bits()) {} | ||
| 280 | |||
| 281 | // decreasing dividers | ||
| 282 | debug!("configuring decreasing pclk dividers"); | ||
| 283 | RCC.cfgr2().modify(|w| { | ||
| 284 | if config.ahb < w.hpre() { | ||
| 285 | debug!(" - AHB"); | ||
| 286 | w.set_hpre(config.ahb); | ||
| 287 | } | ||
| 288 | if config.apb1 < w.ppre1() { | ||
| 289 | debug!(" - APB1"); | ||
| 290 | w.set_ppre1(config.apb1); | ||
| 291 | } | ||
| 292 | if config.apb2 < w.ppre2() { | ||
| 293 | debug!(" - APB2"); | ||
| 294 | w.set_ppre2(config.apb2); | ||
| 295 | } | ||
| 296 | if config.apb4 < w.ppre4() { | ||
| 297 | debug!(" - APB4"); | ||
| 298 | w.set_ppre4(config.apb4); | ||
| 299 | } | ||
| 300 | if config.apb5 < w.ppre5() { | ||
| 301 | debug!(" - APB5"); | ||
| 302 | w.set_ppre5(config.apb5); | ||
| 303 | } | ||
| 304 | }); | ||
| 305 | |||
| 306 | let cpuclk = match config.cpu { | ||
| 307 | CpuClk::Hsi => unwrap!(input.hsi), | ||
| 308 | CpuClk::Msi => unwrap!(input.msi), | ||
| 309 | CpuClk::Hse => unwrap!(input.hse), | ||
| 310 | CpuClk::Ic1 { .. } => todo!(), | ||
| 311 | }; | ||
| 312 | |||
| 313 | let sysclk = match config.sys { | ||
| 314 | SysClk::Hsi => unwrap!(input.hsi), | ||
| 315 | SysClk::Msi => unwrap!(input.msi), | ||
| 316 | SysClk::Hse => unwrap!(input.hse), | ||
| 317 | SysClk::Ic2 { .. } => todo!(), | ||
| 318 | }; | ||
| 319 | |||
| 320 | let timpre: u32 = match RCC.cfgr2().read().timpre() { | ||
| 321 | Timpre::DIV1 => 1, | ||
| 322 | Timpre::DIV2 => 2, | ||
| 323 | Timpre::DIV4 => 4, | ||
| 324 | Timpre::_RESERVED_3 => 8, | ||
| 325 | }; | ||
| 326 | |||
| 327 | let hpre = periph_prescaler_to_value(config.ahb.to_bits()); | ||
| 328 | let ppre1 = periph_prescaler_to_value(config.apb1.to_bits()); | ||
| 329 | let ppre2 = periph_prescaler_to_value(config.apb2.to_bits()); | ||
| 330 | let ppre4 = periph_prescaler_to_value(config.apb4.to_bits()); | ||
| 331 | let ppre5 = periph_prescaler_to_value(config.apb5.to_bits()); | ||
| 332 | |||
| 333 | // enable all peripherals in sleep mode | ||
| 334 | enable_low_power_peripherals(); | ||
| 335 | |||
| 336 | // enable interrupts | ||
| 337 | unsafe { | ||
| 338 | core::arch::asm!("cpsie i"); | ||
| 339 | } | ||
| 340 | |||
| 341 | ClocksOutput { | ||
| 342 | sysclk, | ||
| 343 | cpuclk, | ||
| 344 | pclk_tim: sysclk / timpre, | ||
| 345 | ahb: Hertz(sysclk.0 / hpre as u32), | ||
| 346 | apb1: sysclk / hpre / ppre1, | ||
| 347 | apb2: sysclk / hpre / ppre2, | ||
| 348 | apb4: sysclk / hpre / ppre4, | ||
| 349 | apb5: sysclk / hpre / ppre5, | ||
| 350 | } | ||
| 351 | } | ||
| 352 | |||
| 353 | fn enable_low_power_peripherals() { | ||
| 354 | // AHB1-5 | ||
| 355 | RCC.ahb1lpenr().modify(|w| { | ||
| 356 | w.set_adc12lpen(true); | ||
| 357 | w.set_gpdma1lpen(true); | ||
| 358 | }); | ||
| 359 | RCC.ahb2lpenr().modify(|w| { | ||
| 360 | w.set_adf1lpen(true); | ||
| 361 | w.set_mdf1lpen(true); | ||
| 362 | w.set_ramcfglpen(true); | ||
| 363 | }); | ||
| 364 | RCC.ahb3lpenr().modify(|w| { | ||
| 365 | w.set_risaflpen(true); | ||
| 366 | w.set_iaclpen(true); | ||
| 367 | w.set_rifsclpen(true); | ||
| 368 | w.set_pkalpen(true); | ||
| 369 | w.set_saeslpen(true); | ||
| 370 | w.set_cryplpen(true); | ||
| 371 | w.set_hashlpen(true); | ||
| 372 | w.set_rnglpen(true); | ||
| 373 | }); | ||
| 374 | RCC.ahb4lpenr().modify(|w| { | ||
| 375 | w.set_crclpen(true); | ||
| 376 | w.set_pwrlpen(true); | ||
| 377 | w.set_gpioqlpen(true); | ||
| 378 | w.set_gpioplpen(true); | ||
| 379 | w.set_gpioolpen(true); | ||
| 380 | w.set_gpionlpen(true); | ||
| 381 | w.set_gpiohlpen(true); | ||
| 382 | w.set_gpioglpen(true); | ||
| 383 | w.set_gpioflpen(true); | ||
| 384 | w.set_gpioelpen(true); | ||
| 385 | w.set_gpiodlpen(true); | ||
| 386 | w.set_gpioclpen(true); | ||
| 387 | w.set_gpioblpen(true); | ||
| 388 | w.set_gpioalpen(true); | ||
| 389 | }); | ||
| 390 | RCC.ahb5lpenr().modify(|w| { | ||
| 391 | w.set_npulpen(true); | ||
| 392 | w.set_npucachelpen(true); | ||
| 393 | w.set_otg2lpen(true); | ||
| 394 | w.set_otgphy2lpen(true); | ||
| 395 | w.set_otgphy1lpen(true); | ||
| 396 | w.set_otg1lpen(true); | ||
| 397 | w.set_eth1lpen(true); | ||
| 398 | w.set_eth1rxlpen(true); | ||
| 399 | w.set_eth1txlpen(true); | ||
| 400 | w.set_eth1maclpen(true); | ||
| 401 | w.set_gpulpen(true); | ||
| 402 | w.set_gfxmmulpen(true); | ||
| 403 | w.set_mce4lpen(true); | ||
| 404 | w.set_xspi3lpen(true); | ||
| 405 | w.set_mce3lpen(true); | ||
| 406 | w.set_mce2lpen(true); | ||
| 407 | w.set_mce1lpen(true); | ||
| 408 | w.set_xspimlpen(true); | ||
| 409 | w.set_xspi2lpen(true); | ||
| 410 | w.set_sdmmc1lpen(true); | ||
| 411 | w.set_sdmmc2lpen(true); | ||
| 412 | w.set_pssilpen(true); | ||
| 413 | w.set_xspi1lpen(true); | ||
| 414 | w.set_fmclpen(true); | ||
| 415 | w.set_jpeglpen(true); | ||
| 416 | w.set_dma2dlpen(true); | ||
| 417 | w.set_hpdma1lpen(true); | ||
| 418 | }); | ||
| 419 | |||
| 420 | // APB1-5 | ||
| 421 | RCC.apb1llpenr().modify(|w| { | ||
| 422 | w.set_uart8lpen(true); | ||
| 423 | w.set_uart7lpen(true); | ||
| 424 | w.set_i3c2lpen(true); | ||
| 425 | w.set_i3c1lpen(true); | ||
| 426 | w.set_i2c3lpen(true); | ||
| 427 | w.set_i2c2lpen(true); | ||
| 428 | w.set_i2c1lpen(true); | ||
| 429 | w.set_uart5lpen(true); | ||
| 430 | w.set_uart4lpen(true); | ||
| 431 | w.set_usart3lpen(true); | ||
| 432 | w.set_usart2lpen(true); | ||
| 433 | w.set_spdifrx1lpen(true); | ||
| 434 | w.set_spi3lpen(true); | ||
| 435 | w.set_spi2lpen(true); | ||
| 436 | w.set_tim11lpen(true); | ||
| 437 | w.set_tim10lpen(true); | ||
| 438 | w.set_wwdglpen(true); | ||
| 439 | w.set_lptim1lpen(true); | ||
| 440 | w.set_tim14lpen(true); | ||
| 441 | w.set_tim13lpen(true); | ||
| 442 | w.set_tim12lpen(true); | ||
| 443 | w.set_tim7lpen(true); | ||
| 444 | w.set_tim6lpen(true); | ||
| 445 | w.set_tim5lpen(true); | ||
| 446 | w.set_tim4lpen(true); | ||
| 447 | w.set_tim3lpen(true); | ||
| 448 | w.set_tim2lpen(true); | ||
| 449 | }); | ||
| 450 | RCC.apb1hlpenr().modify(|w| { | ||
| 451 | w.set_ucpd1lpen(true); | ||
| 452 | w.set_fdcanlpen(true); | ||
| 453 | w.set_mdioslpen(true); | ||
| 454 | }); | ||
| 455 | RCC.apb2lpenr().modify(|w| { | ||
| 456 | w.set_sai2lpen(true); | ||
| 457 | w.set_sai1lpen(true); | ||
| 458 | w.set_spi5lpen(true); | ||
| 459 | w.set_tim9lpen(true); | ||
| 460 | w.set_tim17lpen(true); | ||
| 461 | w.set_tim16lpen(true); | ||
| 462 | w.set_tim15lpen(true); | ||
| 463 | w.set_tim18lpen(true); | ||
| 464 | w.set_spi4lpen(true); | ||
| 465 | w.set_spi1lpen(true); | ||
| 466 | w.set_usart10lpen(true); | ||
| 467 | w.set_uart9lpen(true); | ||
| 468 | w.set_usart6lpen(true); | ||
| 469 | w.set_usart1lpen(true); | ||
| 470 | w.set_tim8lpen(true); | ||
| 471 | w.set_tim1lpen(true); | ||
| 472 | }); | ||
| 473 | RCC.apb3lpenr().modify(|w| { | ||
| 474 | w.set_dftlpen(true); | ||
| 475 | }); | ||
| 476 | RCC.apb4llpenr().modify(|w| { | ||
| 477 | w.set_rtcapblpen(true); | ||
| 478 | w.set_rtclpen(true); | ||
| 479 | w.set_vrefbuflpen(true); | ||
| 480 | w.set_lptim5lpen(true); | ||
| 481 | w.set_lptim4lpen(true); | ||
| 482 | w.set_lptim3lpen(true); | ||
| 483 | w.set_lptim2lpen(true); | ||
| 484 | w.set_i2c4lpen(true); | ||
| 485 | w.set_spi6lpen(true); | ||
| 486 | w.set_lpuart1lpen(true); | ||
| 487 | w.set_hdplpen(true); | ||
| 488 | }); | ||
| 489 | RCC.apb4hlpenr().modify(|w| { | ||
| 490 | w.set_dtslpen(true); | ||
| 491 | w.set_bseclpen(true); | ||
| 492 | w.set_syscfglpen(true); | ||
| 493 | }); | ||
| 494 | RCC.apb5lpenr().modify(|w| { | ||
| 495 | w.set_csilpen(true); | ||
| 496 | w.set_venclpen(true); | ||
| 497 | w.set_gfxtimlpen(true); | ||
| 498 | w.set_dcmilpen(true); | ||
| 499 | w.set_ltdclpen(true); | ||
| 500 | }); | ||
| 501 | |||
| 502 | RCC.buslpenr().modify(|w| { | ||
| 503 | w.set_aclknclpen(true); | ||
| 504 | w.set_aclknlpen(true); | ||
| 505 | }); | ||
| 506 | |||
| 507 | RCC.memlpenr().modify(|w| { | ||
| 508 | w.set_bootromlpen(true); | ||
| 509 | w.set_vencramlpen(true); | ||
| 510 | w.set_npucacheramlpen(true); | ||
| 511 | w.set_flexramlpen(true); | ||
| 512 | w.set_axisram2lpen(true); | ||
| 513 | w.set_axisram1lpen(true); | ||
| 514 | w.set_bkpsramlpen(true); | ||
| 515 | w.set_ahbsram2lpen(true); | ||
| 516 | w.set_ahbsram1lpen(true); | ||
| 517 | w.set_axisram6lpen(true); | ||
| 518 | w.set_axisram5lpen(true); | ||
| 519 | w.set_axisram4lpen(true); | ||
| 520 | w.set_axisram3lpen(true); | ||
| 521 | }); | ||
| 522 | |||
| 523 | RCC.misclpenr().modify(|w| { | ||
| 524 | w.set_perlpen(true); | ||
| 525 | w.set_xspiphycomplpen(true); | ||
| 526 | w.set_dbglpen(true); | ||
| 527 | }); | ||
| 528 | } | ||
| 529 | |||
| 530 | const fn periph_prescaler_to_value(bits: u8) -> u8 { | ||
| 531 | match bits { | ||
| 532 | 0 => 1, | ||
| 533 | 1 => 2, | ||
| 534 | 2 => 4, | ||
| 535 | 3 => 8, | ||
| 536 | 4 => 16, | ||
| 537 | 5 => 32, | ||
| 538 | 6 => 64, | ||
| 539 | 7.. => 128, | ||
| 540 | } | ||
| 541 | } | ||
| 542 | |||
| 543 | fn pll_source_ready(source: u8) -> bool { | ||
| 544 | match source { | ||
| 545 | 0x0 if !RCC.sr().read().pllrdy(0) && !RCC.pllcfgr1(0).read().pllbyp() => false, | ||
| 546 | 0x1 if !RCC.sr().read().pllrdy(1) && !RCC.pllcfgr1(1).read().pllbyp() => false, | ||
| 547 | 0x2 if !RCC.sr().read().pllrdy(2) && !RCC.pllcfgr1(2).read().pllbyp() => false, | ||
| 548 | 0x3 if !RCC.sr().read().pllrdy(3) && !RCC.pllcfgr1(3).read().pllbyp() => false, | ||
| 549 | _ => true, | ||
| 550 | } | ||
| 551 | } | ||
| 552 | |||
| 553 | fn pll_sources_ready(source1: u8, source2: u8) -> bool { | ||
| 554 | pll_source_ready(source1) && pll_source_ready(source2) | ||
| 555 | } | ||
| 556 | |||
| 557 | impl Default for Config { | ||
| 558 | fn default() -> Self { | ||
| 559 | Self::new() | ||
| 560 | } | ||
| 561 | } | ||
| 562 | |||
| 563 | fn power_supply_config(supply_config: SupplyConfig) { | ||
| 564 | // power supply config | ||
| 565 | PWR.cr1().modify(|w| { | ||
| 566 | w.set_sden(match supply_config { | ||
| 567 | SupplyConfig::External => false, | ||
| 568 | SupplyConfig::Smps => true, | ||
| 569 | }); | ||
| 570 | }); | ||
| 571 | |||
| 572 | // Validate supply configuration | ||
| 573 | while !PWR.voscr().read().actvosrdy() {} | ||
| 574 | } | ||
| 575 | |||
| 576 | struct PllInput { | ||
| 577 | hsi: Option<Hertz>, | ||
| 578 | msi: Option<Hertz>, | ||
| 579 | hse: Option<Hertz>, | ||
| 580 | i2s_ckin: Option<Hertz>, | ||
| 581 | } | ||
| 582 | |||
| 583 | #[derive(Clone, Copy, Default)] | ||
| 584 | #[allow(dead_code)] | ||
| 585 | struct PllOutput { | ||
| 586 | divm: Option<Hertz>, | ||
| 587 | divn: Option<Hertz>, | ||
| 588 | divp1: Option<Hertz>, | ||
| 589 | divp2: Option<Hertz>, | ||
| 590 | output: Option<Hertz>, | ||
| 591 | } | ||
| 592 | |||
| 593 | fn init_pll(pll_config: Option<Pll>, pll_index: usize, input: &PllInput) -> PllOutput { | ||
| 594 | let cfgr1 = RCC.pllcfgr1(pll_index); | ||
| 595 | let cfgr2 = RCC.pllcfgr2(pll_index); | ||
| 596 | let cfgr3 = RCC.pllcfgr3(pll_index); | ||
| 597 | |||
| 598 | match pll_config { | ||
| 599 | Some(Pll::Oscillator { | ||
| 600 | source, | ||
| 601 | divm, | ||
| 602 | fractional, | ||
| 603 | divn, | ||
| 604 | divp1, | ||
| 605 | divp2, | ||
| 606 | }) => { | ||
| 607 | // ensure pll is disabled | ||
| 608 | RCC.ccr().write(|w| w.set_pllonc(pll_index, true)); | ||
| 609 | while RCC.sr().read().pllrdy(pll_index) {} | ||
| 610 | |||
| 611 | // ensure PLLxMODSSDIS=1 to work in fractional mode | ||
| 612 | cfgr3.modify(|w| w.set_pllmodssdis(Pllmodssdis::FRACTIONAL_DIVIDE)); | ||
| 613 | // clear bypass mode | ||
| 614 | cfgr1.modify(|w| w.set_pllbyp(false)); | ||
| 615 | // configure the pll clock source, mul and div factors | ||
| 616 | cfgr1.modify(|w| { | ||
| 617 | w.set_pllsel(source); | ||
| 618 | w.set_plldivm(divm); | ||
| 619 | w.set_plldivn(divn); | ||
| 620 | }); | ||
| 621 | |||
| 622 | let in_clk = match source { | ||
| 623 | Pllsel::HSI => unwrap!(input.hsi), | ||
| 624 | Pllsel::MSI => unwrap!(input.msi), | ||
| 625 | Pllsel::HSE => unwrap!(input.hse), | ||
| 626 | Pllsel::I2S_CKIN => unwrap!(input.i2s_ckin), | ||
| 627 | _ => panic!("reserved PLL source not allowed"), | ||
| 628 | }; | ||
| 629 | |||
| 630 | let m = divm.to_bits() as u32; | ||
| 631 | let n = divn as u32; | ||
| 632 | |||
| 633 | cfgr3.modify(|w| { | ||
| 634 | w.set_pllpdiv1(divp1); | ||
| 635 | w.set_pllpdiv2(divp2); | ||
| 636 | }); | ||
| 637 | |||
| 638 | let p1 = divp1.to_bits() as u32; | ||
| 639 | let p2 = divp2.to_bits() as u32; | ||
| 640 | |||
| 641 | // configure pll divnfrac | ||
| 642 | cfgr2.modify(|w| w.set_plldivnfrac(fractional)); | ||
| 643 | // clear pllxmoddsen | ||
| 644 | cfgr3.modify(|w| w.set_pllmoddsen(false)); | ||
| 645 | // fractional mode | ||
| 646 | if fractional != 0 { | ||
| 647 | cfgr3.modify(|w| { | ||
| 648 | w.set_pllmoddsen(true); | ||
| 649 | w.set_plldacen(true); | ||
| 650 | }) | ||
| 651 | } | ||
| 652 | // enable pll post divider output | ||
| 653 | cfgr3.modify(|w| { | ||
| 654 | w.set_pllmodssrst(true); | ||
| 655 | w.set_pllpdiven(true); | ||
| 656 | }); | ||
| 657 | // enable the pll | ||
| 658 | RCC.csr().write(|w| w.pllons(pll_index)); | ||
| 659 | // wait until ready | ||
| 660 | while RCC.sr().read().pllrdy(pll_index) {} | ||
| 661 | |||
| 662 | PllOutput { | ||
| 663 | divm: Some(Hertz(m)), | ||
| 664 | divn: Some(Hertz(n)), | ||
| 665 | divp1: Some(Hertz(p1)), | ||
| 666 | divp2: Some(Hertz(p2)), | ||
| 667 | output: Some(Hertz(in_clk.0 / m / n / p1 / p2)), | ||
| 668 | } | ||
| 669 | } | ||
| 670 | Some(Pll::Bypass { source }) => { | ||
| 671 | // check if source is ready | ||
| 672 | if !pll_source_ready(source.to_bits()) { | ||
| 673 | panic!("PLL source is not ready") | ||
| 674 | } | ||
| 675 | |||
| 676 | // ensure pll is disabled | ||
| 677 | RCC.ccr().write(|w| w.set_pllonc(pll_index, true)); | ||
| 678 | while RCC.sr().read().pllrdy(pll_index) {} | ||
| 679 | |||
| 680 | cfgr1.modify(|w| { | ||
| 681 | w.set_pllbyp(true); | ||
| 682 | w.set_pllsel(source); | ||
| 683 | }); | ||
| 684 | |||
| 685 | let in_clk = match source { | ||
| 686 | Pllsel::HSI => unwrap!(input.hsi), | ||
| 687 | Pllsel::MSI => unwrap!(input.msi), | ||
| 688 | Pllsel::HSE => unwrap!(input.hse), | ||
| 689 | Pllsel::I2S_CKIN => unwrap!(input.i2s_ckin), | ||
| 690 | _ => panic!("reserved PLL source not allowed"), | ||
| 691 | }; | ||
| 692 | |||
| 693 | PllOutput { | ||
| 694 | output: Some(in_clk), | ||
| 695 | ..Default::default() | ||
| 696 | } | ||
| 697 | } | ||
| 698 | None => { | ||
| 699 | cfgr3.modify(|w| w.set_pllpdiven(false)); | ||
| 700 | RCC.ccr().write(|w| w.set_pllonc(pll_index, true)); | ||
| 701 | // wait till disabled | ||
| 702 | while RCC.sr().read().pllrdy(pll_index) {} | ||
| 703 | |||
| 704 | // clear bypass mode | ||
| 705 | cfgr1.modify(|w| w.set_pllbyp(false)); | ||
| 706 | |||
| 707 | PllOutput::default() | ||
| 708 | } | ||
| 709 | } | ||
| 710 | } | ||
| 711 | |||
| 712 | #[allow(dead_code)] | ||
| 713 | struct OscOutput { | ||
| 714 | hsi: Option<Hertz>, | ||
| 715 | hse: Option<Hertz>, | ||
| 716 | msi: Option<Hertz>, | ||
| 717 | lsi: Option<Hertz>, | ||
| 718 | lse: Option<Hertz>, | ||
| 719 | pll1: Option<Hertz>, | ||
| 720 | pll2: Option<Hertz>, | ||
| 721 | pll3: Option<Hertz>, | ||
| 722 | pll4: Option<Hertz>, | ||
| 723 | ic1sel: Icsel, | ||
| 724 | ic2sel: Icsel, | ||
| 725 | ic6sel: Icsel, | ||
| 726 | ic11sel: Icsel, | ||
| 727 | } | ||
| 728 | |||
| 729 | fn init_osc(config: Config) -> OscOutput { | ||
| 730 | let (cpu_src, sys_src) = { | ||
| 731 | let reg = RCC.cfgr().read(); | ||
| 732 | (reg.cpusws(), reg.syssws()) | ||
| 733 | }; | ||
| 734 | let pll1_src = RCC.pllcfgr1(0).read().pllsel(); | ||
| 735 | let pll2_src = RCC.pllcfgr1(1).read().pllsel(); | ||
| 736 | let pll3_src = RCC.pllcfgr1(2).read().pllsel(); | ||
| 737 | let pll4_src = RCC.pllcfgr1(3).read().pllsel(); | ||
| 738 | let rcc_sr = RCC.sr().read(); | ||
| 739 | |||
| 740 | debug!("configuring HSE"); | ||
| 741 | |||
| 742 | // hse configuration | ||
| 743 | let hse = if let Some(hse) = config.hse { | ||
| 744 | match hse.mode { | ||
| 745 | HseMode::Oscillator => { | ||
| 746 | debug!("HSE in oscillator mode"); | ||
| 747 | } | ||
| 748 | HseMode::Bypass => { | ||
| 749 | debug!("HSE in bypass mode"); | ||
| 750 | RCC.hsecfgr().modify(|w| { | ||
| 751 | w.set_hsebyp(true); | ||
| 752 | w.set_hseext(Hseext::ANALOG); | ||
| 753 | }); | ||
| 754 | } | ||
| 755 | HseMode::BypassDigital => { | ||
| 756 | debug!("HSE in bypass digital mode"); | ||
| 757 | RCC.hsecfgr().modify(|w| { | ||
| 758 | w.set_hsebyp(true); | ||
| 759 | w.set_hseext(Hseext::DIGITAL); | ||
| 760 | }); | ||
| 761 | } | ||
| 762 | } | ||
| 763 | RCC.csr().write(|w| w.set_hseons(true)); | ||
| 764 | |||
| 765 | // wait until the hse is ready | ||
| 766 | while !RCC.sr().read().hserdy() {} | ||
| 767 | |||
| 768 | Some(hse.freq) | ||
| 769 | } else if cpu_src == Cpusws::HSE | ||
| 770 | || sys_src == Syssws::HSE | ||
| 771 | || (pll1_src == Pllsel::HSE && rcc_sr.pllrdy(0)) | ||
| 772 | || (pll2_src == Pllsel::HSE && rcc_sr.pllrdy(1)) | ||
| 773 | || (pll3_src == Pllsel::HSE && rcc_sr.pllrdy(2)) | ||
| 774 | || (pll4_src == Pllsel::HSE && rcc_sr.pllrdy(3)) | ||
| 775 | { | ||
| 776 | panic!( | ||
| 777 | "When the HSE is used as cpu/system bus clock or clock source for any PLL, it is not allowed to be disabled" | ||
| 778 | ); | ||
| 779 | } else { | ||
| 780 | debug!("HSE off"); | ||
| 781 | |||
| 782 | RCC.ccr().write(|w| w.set_hseonc(true)); | ||
| 783 | RCC.hsecfgr().modify(|w| { | ||
| 784 | w.set_hseext(Hseext::ANALOG); | ||
| 785 | w.set_hsebyp(false); | ||
| 786 | }); | ||
| 787 | |||
| 788 | // wait until the hse is disabled | ||
| 789 | while RCC.sr().read().hserdy() {} | ||
| 790 | |||
| 791 | None | ||
| 792 | }; | ||
| 793 | |||
| 794 | // hsi configuration | ||
| 795 | debug!("configuring HSI"); | ||
| 796 | let hsi = if let Some(hsi) = config.hsi { | ||
| 797 | RCC.csr().write(|w| w.set_hsions(true)); | ||
| 798 | while !RCC.sr().read().hsirdy() {} | ||
| 799 | |||
| 800 | // set divider and calibration | ||
| 801 | RCC.hsicfgr().modify(|w| { | ||
| 802 | w.set_hsidiv(hsi.pre); | ||
| 803 | w.set_hsitrim(hsi.trim); | ||
| 804 | }); | ||
| 805 | |||
| 806 | Some(HSI_FREQ / hsi.pre) | ||
| 807 | } else if cpu_src == Cpusws::HSI | ||
| 808 | || sys_src == Syssws::HSI | ||
| 809 | || (pll1_src == Pllsel::HSI && rcc_sr.pllrdy(0)) | ||
| 810 | || (pll2_src == Pllsel::HSI && rcc_sr.pllrdy(1)) | ||
| 811 | || (pll3_src == Pllsel::HSI && rcc_sr.pllrdy(2)) | ||
| 812 | || (pll4_src == Pllsel::HSI && rcc_sr.pllrdy(3)) | ||
| 813 | { | ||
| 814 | panic!( | ||
| 815 | "When the HSI is used as cpu/system bus clock or clock source for any PLL, it is not allowed to be disabled" | ||
| 816 | ); | ||
| 817 | } else { | ||
| 818 | debug!("HSI off"); | ||
| 819 | |||
| 820 | RCC.ccr().write(|w| w.set_hsionc(true)); | ||
| 821 | while RCC.sr().read().hsirdy() {} | ||
| 822 | |||
| 823 | None | ||
| 824 | }; | ||
| 825 | |||
| 826 | // msi configuration | ||
| 827 | debug!("configuring MSI"); | ||
| 828 | let msi = if let Some(msi) = config.msi { | ||
| 829 | RCC.msicfgr().modify(|w| w.set_msifreqsel(msi.freq)); | ||
| 830 | RCC.csr().write(|w| w.set_msions(true)); | ||
| 831 | while !RCC.sr().read().msirdy() {} | ||
| 832 | RCC.msicfgr().modify(|w| w.set_msitrim(msi.trim)); | ||
| 833 | |||
| 834 | Some(match msi.freq { | ||
| 835 | Msifreqsel::_4MHZ => Hertz::mhz(4), | ||
| 836 | Msifreqsel::_16MHZ => Hertz::mhz(16), | ||
| 837 | }) | ||
| 838 | } else if cpu_src == Cpusws::MSI | ||
| 839 | || sys_src == Syssws::MSI | ||
| 840 | || (pll1_src == Pllsel::MSI && rcc_sr.pllrdy(0)) | ||
| 841 | || (pll2_src == Pllsel::MSI && rcc_sr.pllrdy(1)) | ||
| 842 | || (pll3_src == Pllsel::MSI && rcc_sr.pllrdy(2)) | ||
| 843 | || (pll4_src == Pllsel::MSI && rcc_sr.pllrdy(3)) | ||
| 844 | { | ||
| 845 | panic!( | ||
| 846 | "When the MSI is used as cpu/system bus clock or clock source for any PLL, it is not allowed to be disabled" | ||
| 847 | ); | ||
| 848 | } else { | ||
| 849 | RCC.ccr().write(|w| w.set_msionc(true)); | ||
| 850 | while RCC.sr().read().msirdy() {} | ||
| 851 | |||
| 852 | None | ||
| 853 | }; | ||
| 854 | |||
| 855 | // lsi configuration | ||
| 856 | debug!("configuring LSI"); | ||
| 857 | let lsi = if config.lsi { | ||
| 858 | RCC.csr().write(|w| w.set_lsions(true)); | ||
| 859 | while !RCC.sr().read().lsirdy() {} | ||
| 860 | Some(super::LSI_FREQ) | ||
| 861 | } else { | ||
| 862 | RCC.ccr().write(|w| w.set_lsionc(true)); | ||
| 863 | while RCC.sr().read().lsirdy() {} | ||
| 864 | None | ||
| 865 | }; | ||
| 866 | |||
| 867 | // lse configuration | ||
| 868 | debug!("configuring LSE"); | ||
| 869 | let lse = if config.lse { | ||
| 870 | RCC.csr().write(|w| w.set_lseons(true)); | ||
| 871 | while !RCC.sr().read().lserdy() {} | ||
| 872 | Some(LSE_FREQ) | ||
| 873 | } else { | ||
| 874 | RCC.ccr().write(|w| w.set_lseonc(true)); | ||
| 875 | while RCC.sr().read().lserdy() {} | ||
| 876 | None | ||
| 877 | }; | ||
| 878 | |||
| 879 | let pll_input = PllInput { | ||
| 880 | hse, | ||
| 881 | msi, | ||
| 882 | hsi, | ||
| 883 | i2s_ckin: None, | ||
| 884 | }; | ||
| 885 | |||
| 886 | // pll1,2,3,4 config | ||
| 887 | let pll_configs = [config.pll1, config.pll2, config.pll3, config.pll4]; | ||
| 888 | let mut pll_outputs: [PllOutput; 4] = [PllOutput::default(); 4]; | ||
| 889 | |||
| 890 | let ic1_src = RCC.iccfgr(0).read().icsel(); | ||
| 891 | let ic2_src = RCC.iccfgr(1).read().icsel(); | ||
| 892 | let ic6_src = RCC.iccfgr(5).read().icsel(); | ||
| 893 | let ic11_src = RCC.iccfgr(10).read().icsel(); | ||
| 894 | |||
| 895 | for (n, (&pll, out)) in pll_configs.iter().zip(pll_outputs.iter_mut()).enumerate() { | ||
| 896 | debug!("configuring PLL{}", n + 1); | ||
| 897 | let pll_ready = RCC.sr().read().pllrdy(n); | ||
| 898 | |||
| 899 | if is_new_pll_config(pll, 0) { | ||
| 900 | let this_pll = Icsel::from_bits(n as u8); | ||
| 901 | |||
| 902 | if cpu_src == Cpusws::IC1 && ic1_src == this_pll { | ||
| 903 | panic!("PLL should not be disabled / reconfigured if used for IC1 (cpuclksrc)") | ||
| 904 | } | ||
| 905 | |||
| 906 | if sys_src == Syssws::IC2 && (ic2_src == this_pll || ic6_src == this_pll || ic11_src == this_pll) { | ||
| 907 | panic!("PLL should not be disabled / reconfigured if used for IC2, IC6 or IC11 (sysclksrc)") | ||
| 908 | } | ||
| 909 | |||
| 910 | *out = init_pll(pll, 0, &pll_input); | ||
| 911 | } else if pll.is_some() && !pll_ready { | ||
| 912 | RCC.csr().write(|w| w.pllons(n)); | ||
| 913 | while !RCC.sr().read().pllrdy(n) {} | ||
| 914 | } | ||
| 915 | } | ||
| 916 | |||
| 917 | OscOutput { | ||
| 918 | hsi, | ||
| 919 | hse, | ||
| 920 | msi, | ||
| 921 | lsi, | ||
| 922 | lse, | ||
| 923 | pll1: pll_outputs[0].output, | ||
| 924 | pll2: pll_outputs[1].output, | ||
| 925 | pll3: pll_outputs[2].output, | ||
| 926 | pll4: pll_outputs[3].output, | ||
| 927 | ic1sel: ic1_src, | ||
| 928 | ic2sel: ic2_src, | ||
| 929 | ic6sel: ic6_src, | ||
| 930 | ic11sel: ic11_src, | ||
| 931 | } | ||
| 932 | } | ||
| 933 | |||
| 934 | fn is_new_pll_config(pll: Option<Pll>, pll_index: usize) -> bool { | ||
| 935 | let cfgr1 = RCC.pllcfgr1(pll_index).read(); | ||
| 936 | let cfgr2 = RCC.pllcfgr2(pll_index).read(); | ||
| 937 | let cfgr3 = RCC.pllcfgr3(pll_index).read(); | ||
| 938 | |||
| 939 | let ready = RCC.sr().read().pllrdy(pll_index); | ||
| 940 | let bypass = cfgr1.pllbyp(); | ||
| 941 | |||
| 942 | match (pll, ready, bypass) { | ||
| 943 | (None, true, _) => return true, | ||
| 944 | (Some(_), false, _) => return true, | ||
| 945 | (Some(conf), true, bypass) => match (conf, bypass) { | ||
| 946 | (Pll::Bypass { .. }, false) => return true, | ||
| 947 | (Pll::Oscillator { .. }, true) => return true, | ||
| 948 | _ => {} | ||
| 949 | }, | ||
| 950 | _ => {} | ||
| 951 | } | ||
| 952 | |||
| 953 | match pll { | ||
| 954 | Some(Pll::Bypass { source }) => cfgr1.pllsel() != source, | ||
| 955 | Some(Pll::Oscillator { | ||
| 956 | source, | ||
| 957 | divm: m, | ||
| 958 | fractional, | ||
| 959 | divn: n, | ||
| 960 | divp1: p1, | ||
| 961 | divp2: p2, | ||
| 962 | }) => { | ||
| 963 | cfgr1.pllsel() != source | ||
| 964 | || cfgr1.plldivm() != m | ||
| 965 | || cfgr1.plldivn() != n | ||
| 966 | || cfgr2.plldivnfrac() != fractional | ||
| 967 | || cfgr3.pllpdiv1() != p1 | ||
| 968 | || cfgr3.pllpdiv2() != p2 | ||
| 969 | } | ||
| 970 | None => false, | ||
| 971 | } | ||
| 972 | } | ||
| 973 | |||
| 974 | pub(crate) unsafe fn init(config: Config) { | ||
| 975 | debug!("enabling SYSCFG"); | ||
| 976 | // system configuration setup | ||
| 977 | RCC.apb4hensr().write(|w| w.set_syscfgens(true)); | ||
| 978 | // delay after RCC peripheral clock enabling | ||
| 979 | RCC.apb4hensr().read(); | ||
| 980 | |||
| 981 | debug!("setting VTOR"); | ||
| 982 | |||
| 983 | let vtor = unsafe { | ||
| 984 | let p = cortex_m::Peripherals::steal(); | ||
| 985 | p.SCB.vtor.read() | ||
| 986 | }; | ||
| 987 | |||
| 988 | // set default vector table location after reset or standby | ||
| 989 | SYSCFG.initsvtorcr().write(|w| w.set_svtor_addr(vtor)); | ||
| 990 | // read back the value to ensure it is written before deactivating SYSCFG | ||
| 991 | SYSCFG.initsvtorcr().read(); | ||
| 992 | |||
| 993 | debug!("deactivating SYSCFG"); | ||
| 994 | |||
| 995 | // deactivate SYSCFG | ||
| 996 | RCC.apb4hensr().write(|w| w.set_syscfgens(false)); | ||
| 997 | |||
| 998 | debug!("enabling FPU"); | ||
| 999 | |||
| 1000 | // enable fpu | ||
| 1001 | unsafe { | ||
| 1002 | let p = cortex_m::Peripherals::steal(); | ||
| 1003 | p.SCB.cpacr.modify(|w| w | (3 << 20) | (3 << 22)); | ||
| 1004 | } | ||
| 1005 | |||
| 1006 | // TODO: ugly workaround for DMA accesses until RIF is properly implemented | ||
| 1007 | debug!("deactivating RIF"); | ||
| 1008 | const RISAF3_BASE_NS: *mut u32 = stm32_metapac::RNG.wrapping_byte_offset(0x8000) as _; // AHB3PERIPH_BASE_NS + 0x8000UL | ||
| 1009 | const RISAF3_REG0_CFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x40); | ||
| 1010 | const RISAF3_REG0_ENDR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x48); | ||
| 1011 | const RISAF3_REG0_CIDCFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x4C); | ||
| 1012 | const RISAF3_REG1_CFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x80); | ||
| 1013 | const RISAF3_REG1_ENDR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x88); | ||
| 1014 | const RISAF3_REG1_CIDCFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x8C); | ||
| 1015 | unsafe { | ||
| 1016 | *RISAF3_REG0_CIDCFGR = 0x000F000F; /* RW for everyone */ | ||
| 1017 | *RISAF3_REG0_ENDR = 0xFFFFFFFF; /* all-encompassing */ | ||
| 1018 | *RISAF3_REG0_CFGR = 0x00000101; /* enabled, secure, unprivileged for everyone */ | ||
| 1019 | *RISAF3_REG1_CIDCFGR = 0x00FF00FF; /* RW for everyone */ | ||
| 1020 | *RISAF3_REG1_ENDR = 0xFFFFFFFF; /* all-encompassing */ | ||
| 1021 | *RISAF3_REG1_CFGR = 0x00000001; /* enabled, non-secure, unprivileged*/ | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | debug!("setting power supply config"); | ||
| 1025 | |||
| 1026 | power_supply_config(config.supply_config); | ||
| 1027 | |||
| 1028 | let osc = init_osc(config); | ||
| 1029 | let clock_inputs = ClocksInput { | ||
| 1030 | hsi: osc.hsi, | ||
| 1031 | msi: osc.msi, | ||
| 1032 | hse: osc.hse, | ||
| 1033 | }; | ||
| 1034 | let clocks = init_clocks(config, &clock_inputs); | ||
| 1035 | |||
| 1036 | // TODO: sysb, sysc, sysd must have the same clock source | ||
| 1037 | |||
| 1038 | set_clocks!( | ||
| 1039 | sys: Some(clocks.sysclk), | ||
| 1040 | hsi: osc.hsi, | ||
| 1041 | hsi_div: None, | ||
| 1042 | hse: osc.hse, | ||
| 1043 | msi: osc.msi, | ||
| 1044 | hclk1: Some(clocks.ahb), | ||
| 1045 | hclk2: Some(clocks.ahb), | ||
| 1046 | hclk3: Some(clocks.ahb), | ||
| 1047 | hclk4: Some(clocks.ahb), | ||
| 1048 | hclk5: Some(clocks.ahb), | ||
| 1049 | pclk1: Some(clocks.apb1), | ||
| 1050 | pclk2: Some(clocks.apb2), | ||
| 1051 | pclk1_tim: Some(clocks.pclk_tim), | ||
| 1052 | pclk2_tim: Some(clocks.pclk_tim), | ||
| 1053 | pclk4: Some(clocks.apb4), | ||
| 1054 | pclk5: Some(clocks.apb5), | ||
| 1055 | per: None, | ||
| 1056 | rtc: None, | ||
| 1057 | i2s_ckin: None, | ||
| 1058 | ic8: None, | ||
| 1059 | ic9: None, | ||
| 1060 | ic10: None, | ||
| 1061 | ic14: None, | ||
| 1062 | ic15: None, | ||
| 1063 | ic17: None, | ||
| 1064 | ic20: None, | ||
| 1065 | ); | ||
| 1066 | } | ||
diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 06895a99a..47cc29c6f 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 | ||
| @@ -343,6 +343,16 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 343 | 343 | ||
| 344 | let hsi48 = config.hsi48.map(super::init_hsi48); | 344 | let hsi48 = config.hsi48.map(super::init_hsi48); |
| 345 | 345 | ||
| 346 | // There's a possibility that a bootloader that ran before us has configured the system clock | ||
| 347 | // source to be PLL1_R. In that case we'd get forever stuck on (de)configuring PLL1 as the chip | ||
| 348 | // prohibits disabling PLL1 when it's used as a source for system clock. Change the system | ||
| 349 | // clock source to MSIS which doesn't suffer from this conflict. The correct source per the | ||
| 350 | // provided config is then set further down. | ||
| 351 | // See https://github.com/embassy-rs/embassy/issues/5072 | ||
| 352 | let default_system_clock_source = Config::default().sys; | ||
| 353 | RCC.cfgr1().modify(|w| w.set_sw(default_system_clock_source)); | ||
| 354 | while RCC.cfgr1().read().sws() != default_system_clock_source {} | ||
| 355 | |||
| 346 | let pll_input = PllInput { hse, hsi, msi: msis }; | 356 | let pll_input = PllInput { hse, hsi, msi: msis }; |
| 347 | let pll1 = init_pll(PllInstance::Pll1, config.pll1, &pll_input, config.voltage_range); | 357 | let pll1 = init_pll(PllInstance::Pll1, config.pll1, &pll_input, config.voltage_range); |
| 348 | let pll2 = init_pll(PllInstance::Pll2, config.pll2, &pll_input, config.voltage_range); | 358 | let pll2 = init_pll(PllInstance::Pll2, config.pll2, &pll_input, config.voltage_range); |
| @@ -442,7 +452,10 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 442 | Hertz(24_000_000) => Usbrefcksel::MHZ24, | 452 | Hertz(24_000_000) => Usbrefcksel::MHZ24, |
| 443 | Hertz(26_000_000) => Usbrefcksel::MHZ26, | 453 | Hertz(26_000_000) => Usbrefcksel::MHZ26, |
| 444 | Hertz(32_000_000) => Usbrefcksel::MHZ32, | 454 | 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), | 455 | _ => panic!( |
| 456 | "cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", | ||
| 457 | clk_val | ||
| 458 | ), | ||
| 446 | }, | 459 | }, |
| 447 | None => Usbrefcksel::MHZ24, | 460 | None => Usbrefcksel::MHZ24, |
| 448 | }; | 461 | }; |
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..b8dfc7ecf 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs | |||
| @@ -9,10 +9,27 @@ 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 | ||
| 16 | /// WBA-specific health test configuration values for RNG | ||
| 17 | #[derive(Clone, Copy)] | ||
| 18 | #[allow(dead_code)] | ||
| 19 | enum Htcfg { | ||
| 20 | /// WBA-specific health test configuration (0x0000AAC7) | ||
| 21 | /// Corresponds to configuration A, B, and C thresholds as recommended in the reference manual | ||
| 22 | WbaRecommended = 0x0000_AAC7, | ||
| 23 | } | ||
| 24 | |||
| 25 | impl Htcfg { | ||
| 26 | /// Convert to the raw u32 value for register access | ||
| 27 | #[allow(dead_code)] | ||
| 28 | fn value(self) -> u32 { | ||
| 29 | self as u32 | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 16 | /// RNG error | 33 | /// RNG error |
| 17 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 34 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
| 18 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 35 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| @@ -100,7 +117,7 @@ impl<'d, T: Instance> Rng<'d, T> { | |||
| 100 | // wait for CONDRST to be set | 117 | // wait for CONDRST to be set |
| 101 | while !T::regs().cr().read().condrst() {} | 118 | while !T::regs().cr().read().condrst() {} |
| 102 | 119 | ||
| 103 | // TODO for WBA6, the HTCR reg is different | 120 | // Set health test configuration values |
| 104 | #[cfg(not(rng_wba6))] | 121 | #[cfg(not(rng_wba6))] |
| 105 | { | 122 | { |
| 106 | // magic number must be written immediately before every read or write access to HTCR | 123 | // magic number must be written immediately before every read or write access to HTCR |
| @@ -111,6 +128,12 @@ impl<'d, T: Instance> Rng<'d, T> { | |||
| 111 | .htcr() | 128 | .htcr() |
| 112 | .write(|w| w.set_htcfg(pac::rng::vals::Htcfg::RECOMMENDED)); | 129 | .write(|w| w.set_htcfg(pac::rng::vals::Htcfg::RECOMMENDED)); |
| 113 | } | 130 | } |
| 131 | #[cfg(rng_wba6)] | ||
| 132 | { | ||
| 133 | // For WBA6, set RNG_HTCR0 to the recommended value for configurations A, B, and C | ||
| 134 | // This value corresponds to the health test thresholds specified in the reference manual | ||
| 135 | T::regs().htcr(0).write(|w| w.0 = Htcfg::WbaRecommended.value()); | ||
| 136 | } | ||
| 114 | 137 | ||
| 115 | // finish conditioning | 138 | // finish conditioning |
| 116 | T::regs().cr().modify(|reg| { | 139 | T::regs().cr().modify(|reg| { |
diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs index a81ac6746..f049d6b12 100644 --- a/embassy-stm32/src/rtc/low_power.rs +++ b/embassy-stm32/src/rtc/low_power.rs | |||
| @@ -1,10 +1,11 @@ | |||
| 1 | #[cfg(feature = "time")] | 1 | #[cfg(feature = "time")] |
| 2 | use embassy_time::{Duration, TICK_HZ}; | 2 | use embassy_time::{Duration, TICK_HZ}; |
| 3 | 3 | ||
| 4 | use super::{bcd2_to_byte, DateTimeError, Rtc, RtcError}; | 4 | use super::{DateTimeError, Rtc, RtcError, bcd2_to_byte}; |
| 5 | use crate::interrupt::typelevel::Interrupt; | 5 | use crate::interrupt::typelevel::Interrupt; |
| 6 | use crate::pac::rtc::vals::Wucksel; | ||
| 6 | use crate::peripherals::RTC; | 7 | use crate::peripherals::RTC; |
| 7 | use crate::rtc::SealedInstance; | 8 | use crate::rtc::{RtcTimeProvider, SealedInstance}; |
| 8 | 9 | ||
| 9 | /// Represents an instant in time that can be substracted to compute a duration | 10 | /// Represents an instant in time that can be substracted to compute a duration |
| 10 | pub(super) struct RtcInstant { | 11 | pub(super) struct RtcInstant { |
| @@ -58,66 +59,22 @@ impl core::ops::Sub for RtcInstant { | |||
| 58 | } | 59 | } |
| 59 | } | 60 | } |
| 60 | 61 | ||
| 61 | #[repr(u8)] | 62 | fn wucksel_compute_min(val: u32) -> (Wucksel, u32) { |
| 62 | #[derive(Clone, Copy, Debug)] | 63 | *[ |
| 63 | pub(crate) enum WakeupPrescaler { | 64 | (Wucksel::DIV2, 2), |
| 64 | Div2 = 2, | 65 | (Wucksel::DIV4, 4), |
| 65 | Div4 = 4, | 66 | (Wucksel::DIV8, 8), |
| 66 | Div8 = 8, | 67 | (Wucksel::DIV16, 16), |
| 67 | Div16 = 16, | 68 | ] |
| 68 | } | 69 | .iter() |
| 69 | 70 | .find(|(_, psc)| *psc as u32 > val) | |
| 70 | #[cfg(any( | 71 | .unwrap_or(&(Wucksel::DIV16, 16)) |
| 71 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba | ||
| 72 | ))] | ||
| 73 | impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { | ||
| 74 | fn from(val: WakeupPrescaler) -> Self { | ||
| 75 | use crate::pac::rtc::vals::Wucksel; | ||
| 76 | |||
| 77 | match val { | ||
| 78 | WakeupPrescaler::Div2 => Wucksel::DIV2, | ||
| 79 | WakeupPrescaler::Div4 => Wucksel::DIV4, | ||
| 80 | WakeupPrescaler::Div8 => Wucksel::DIV8, | ||
| 81 | WakeupPrescaler::Div16 => Wucksel::DIV16, | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | #[cfg(any( | ||
| 87 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba | ||
| 88 | ))] | ||
| 89 | impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler { | ||
| 90 | fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { | ||
| 91 | use crate::pac::rtc::vals::Wucksel; | ||
| 92 | |||
| 93 | match val { | ||
| 94 | Wucksel::DIV2 => WakeupPrescaler::Div2, | ||
| 95 | Wucksel::DIV4 => WakeupPrescaler::Div4, | ||
| 96 | Wucksel::DIV8 => WakeupPrescaler::Div8, | ||
| 97 | Wucksel::DIV16 => WakeupPrescaler::Div16, | ||
| 98 | _ => unreachable!(), | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | impl WakeupPrescaler { | ||
| 104 | pub fn compute_min(val: u32) -> Self { | ||
| 105 | *[ | ||
| 106 | WakeupPrescaler::Div2, | ||
| 107 | WakeupPrescaler::Div4, | ||
| 108 | WakeupPrescaler::Div8, | ||
| 109 | WakeupPrescaler::Div16, | ||
| 110 | ] | ||
| 111 | .iter() | ||
| 112 | .find(|psc| **psc as u32 > val) | ||
| 113 | .unwrap_or(&WakeupPrescaler::Div16) | ||
| 114 | } | ||
| 115 | } | 72 | } |
| 116 | 73 | ||
| 117 | impl Rtc { | 74 | impl Rtc { |
| 118 | /// Return the current instant. | 75 | /// Return the current instant. |
| 119 | fn instant(&self) -> Result<RtcInstant, RtcError> { | 76 | fn instant(&self) -> Result<RtcInstant, RtcError> { |
| 120 | self.time_provider().read(|_, tr, ss| { | 77 | RtcTimeProvider::new().read(|_, tr, ss| { |
| 121 | let second = bcd2_to_byte((tr.st(), tr.su())); | 78 | let second = bcd2_to_byte((tr.st(), tr.su())); |
| 122 | 79 | ||
| 123 | RtcInstant::from(second, ss).map_err(RtcError::InvalidDateTime) | 80 | RtcInstant::from(second, ss).map_err(RtcError::InvalidDateTime) |
| @@ -127,7 +84,7 @@ impl Rtc { | |||
| 127 | /// start the wakeup alarm and with a duration that is as close to but less than | 84 | /// start the wakeup alarm and with a duration that is as close to but less than |
| 128 | /// the requested duration, and record the instant the wakeup alarm was started | 85 | /// the requested duration, and record the instant the wakeup alarm was started |
| 129 | pub(crate) fn start_wakeup_alarm( | 86 | pub(crate) fn start_wakeup_alarm( |
| 130 | &self, | 87 | &mut self, |
| 131 | requested_duration: embassy_time::Duration, | 88 | requested_duration: embassy_time::Duration, |
| 132 | cs: critical_section::CriticalSection, | 89 | cs: critical_section::CriticalSection, |
| 133 | ) { | 90 | ) { |
| @@ -138,7 +95,7 @@ impl Rtc { | |||
| 138 | let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64); | 95 | let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64); |
| 139 | let rtc_hz = Self::frequency().0 as u64; | 96 | let rtc_hz = Self::frequency().0 as u64; |
| 140 | let rtc_ticks = requested_duration * rtc_hz / TICK_HZ; | 97 | let rtc_ticks = requested_duration * rtc_hz / TICK_HZ; |
| 141 | let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); | 98 | let (wucksel, prescaler) = wucksel_compute_min((rtc_ticks / u16::MAX as u64) as u32); |
| 142 | 99 | ||
| 143 | // adjust the rtc ticks to the prescaler and subtract one rtc tick | 100 | // adjust the rtc ticks to the prescaler and subtract one rtc tick |
| 144 | let rtc_ticks = rtc_ticks / prescaler as u64; | 101 | let rtc_ticks = rtc_ticks / prescaler as u64; |
| @@ -159,7 +116,7 @@ impl Rtc { | |||
| 159 | while !regs.icsr().read().wutwf() {} | 116 | while !regs.icsr().read().wutwf() {} |
| 160 | } | 117 | } |
| 161 | 118 | ||
| 162 | regs.cr().modify(|w| w.set_wucksel(prescaler.into())); | 119 | regs.cr().modify(|w| w.set_wucksel(wucksel)); |
| 163 | regs.wutr().write(|w| w.set_wut(rtc_ticks)); | 120 | regs.wutr().write(|w| w.set_wut(rtc_ticks)); |
| 164 | regs.cr().modify(|w| w.set_wute(true)); | 121 | regs.cr().modify(|w| w.set_wute(true)); |
| 165 | regs.cr().modify(|w| w.set_wutie(true)); | 122 | regs.cr().modify(|w| w.set_wutie(true)); |
| @@ -179,7 +136,10 @@ impl Rtc { | |||
| 179 | 136 | ||
| 180 | /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` | 137 | /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` |
| 181 | /// was called, otherwise none | 138 | /// was called, otherwise none |
| 182 | pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> { | 139 | pub(crate) fn stop_wakeup_alarm( |
| 140 | &mut self, | ||
| 141 | cs: critical_section::CriticalSection, | ||
| 142 | ) -> Option<embassy_time::Duration> { | ||
| 183 | let instant = self.instant().unwrap(); | 143 | let instant = self.instant().unwrap(); |
| 184 | if RTC::regs().cr().read().wute() { | 144 | if RTC::regs().cr().read().wute() { |
| 185 | trace!("rtc: stop wakeup alarm at {}", instant); | 145 | trace!("rtc: stop wakeup alarm at {}", instant); |
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 92dec0960..e88bd7ab2 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs | |||
| @@ -5,15 +5,19 @@ 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 | ||
| @@ -25,8 +29,8 @@ mod _version; | |||
| 25 | #[allow(unused_imports)] | 29 | #[allow(unused_imports)] |
| 26 | pub use _version::*; | 30 | pub use _version::*; |
| 27 | 31 | ||
| 28 | use crate::peripherals::RTC; | ||
| 29 | use crate::Peri; | 32 | use crate::Peri; |
| 33 | use crate::peripherals::RTC; | ||
| 30 | 34 | ||
| 31 | /// Errors that can occur on methods on [RtcClock] | 35 | /// Errors that can occur on methods on [RtcClock] |
| 32 | #[non_exhaustive] | 36 | #[non_exhaustive] |
| @@ -44,11 +48,17 @@ pub enum RtcError { | |||
| 44 | } | 48 | } |
| 45 | 49 | ||
| 46 | /// Provides immutable access to the current time of the RTC. | 50 | /// Provides immutable access to the current time of the RTC. |
| 51 | #[derive(Clone)] | ||
| 47 | pub struct RtcTimeProvider { | 52 | pub struct RtcTimeProvider { |
| 48 | _private: (), | 53 | _private: (), |
| 49 | } | 54 | } |
| 50 | 55 | ||
| 51 | 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 | |||
| 52 | /// Return the current datetime. | 62 | /// Return the current datetime. |
| 53 | /// | 63 | /// |
| 54 | /// # Errors | 64 | /// # Errors |
| @@ -106,6 +116,50 @@ impl RtcTimeProvider { | |||
| 106 | } | 116 | } |
| 107 | } | 117 | } |
| 108 | 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 | |||
| 109 | /// RTC driver. | 163 | /// RTC driver. |
| 110 | pub struct Rtc { | 164 | pub struct Rtc { |
| 111 | #[cfg(feature = "low-power")] | 165 | #[cfg(feature = "low-power")] |
| @@ -121,13 +175,21 @@ pub struct RtcConfig { | |||
| 121 | /// | 175 | /// |
| 122 | /// A high counter frequency may impact stop power consumption | 176 | /// A high counter frequency may impact stop power consumption |
| 123 | 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, | ||
| 124 | } | 182 | } |
| 125 | 183 | ||
| 126 | impl Default for RtcConfig { | 184 | impl Default for RtcConfig { |
| 127 | /// LSI with prescalers assuming 32.768 kHz. | 185 | /// LSI with prescalers assuming 32.768 kHz. |
| 128 | /// Raw sub-seconds in 1/256. | 186 | /// Raw sub-seconds in 1/256. |
| 129 | fn default() -> Self { | 187 | fn default() -> Self { |
| 130 | RtcConfig { frequency: Hertz(256) } | 188 | RtcConfig { |
| 189 | frequency: Hertz(256), | ||
| 190 | #[cfg(feature = "_allow-disable-rtc")] | ||
| 191 | _disable_rtc: false, | ||
| 192 | } | ||
| 131 | } | 193 | } |
| 132 | } | 194 | } |
| 133 | 195 | ||
| @@ -145,8 +207,19 @@ pub enum RtcCalibrationCyclePeriod { | |||
| 145 | } | 207 | } |
| 146 | 208 | ||
| 147 | impl Rtc { | 209 | impl Rtc { |
| 210 | #[cfg(not(feature = "low-power"))] | ||
| 148 | /// Create a new RTC instance. | 211 | /// Create a new RTC instance. |
| 149 | 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 { | ||
| 150 | #[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))] | 223 | #[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))] |
| 151 | crate::rcc::enable_and_reset::<RTC>(); | 224 | crate::rcc::enable_and_reset::<RTC>(); |
| 152 | 225 | ||
| @@ -165,10 +238,13 @@ impl Rtc { | |||
| 165 | // Wait for the clock to update after initialization | 238 | // Wait for the clock to update after initialization |
| 166 | #[cfg(not(rtc_v2_f2))] | 239 | #[cfg(not(rtc_v2_f2))] |
| 167 | { | 240 | { |
| 168 | let now = this.time_provider().read(|_, _, ss| Ok(ss)).unwrap(); | 241 | let now = RtcTimeProvider::new().read(|_, _, ss| Ok(ss)).unwrap(); |
| 169 | while now == this.time_provider().read(|_, _, ss| Ok(ss)).unwrap() {} | 242 | while now == RtcTimeProvider::new().read(|_, _, ss| Ok(ss)).unwrap() {} |
| 170 | } | 243 | } |
| 171 | 244 | ||
| 245 | #[cfg(feature = "low-power")] | ||
| 246 | this.enable_wakeup_line(); | ||
| 247 | |||
| 172 | this | 248 | this |
| 173 | } | 249 | } |
| 174 | 250 | ||
| @@ -177,11 +253,6 @@ impl Rtc { | |||
| 177 | freqs.rtc.to_hertz().unwrap() | 253 | freqs.rtc.to_hertz().unwrap() |
| 178 | } | 254 | } |
| 179 | 255 | ||
| 180 | /// Acquire a [`RtcTimeProvider`] instance. | ||
| 181 | pub const fn time_provider(&self) -> RtcTimeProvider { | ||
| 182 | RtcTimeProvider { _private: () } | ||
| 183 | } | ||
| 184 | |||
| 185 | /// Set the datetime to a new value. | 256 | /// Set the datetime to a new value. |
| 186 | /// | 257 | /// |
| 187 | /// # Errors | 258 | /// # Errors |
| @@ -225,15 +296,6 @@ impl Rtc { | |||
| 225 | Ok(()) | 296 | Ok(()) |
| 226 | } | 297 | } |
| 227 | 298 | ||
| 228 | /// Return the current datetime. | ||
| 229 | /// | ||
| 230 | /// # Errors | ||
| 231 | /// | ||
| 232 | /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. | ||
| 233 | pub fn now(&self) -> Result<DateTime, RtcError> { | ||
| 234 | self.time_provider().now() | ||
| 235 | } | ||
| 236 | |||
| 237 | /// Check if daylight savings time is active. | 299 | /// Check if daylight savings time is active. |
| 238 | pub fn get_daylight_savings(&self) -> bool { | 300 | pub fn get_daylight_savings(&self) -> bool { |
| 239 | let cr = RTC::regs().cr().read(); | 301 | let cr = RTC::regs().cr().read(); |
| @@ -315,3 +377,18 @@ trait SealedInstance { | |||
| 315 | 377 | ||
| 316 | // fn apply_config(&mut self, rtc_config: RtcConfig); | 378 | // fn apply_config(&mut self, rtc_config: RtcConfig); |
| 317 | } | 379 | } |
| 380 | |||
| 381 | #[cfg(feature = "low-power")] | ||
| 382 | pub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig, min_stop_pause: embassy_time::Duration) { | ||
| 383 | use crate::time_driver::get_driver; | ||
| 384 | |||
| 385 | #[cfg(feature = "_allow-disable-rtc")] | ||
| 386 | if config._disable_rtc { | ||
| 387 | return; | ||
| 388 | } | ||
| 389 | |||
| 390 | get_driver().set_rtc(cs, Rtc::new_inner(config)); | ||
| 391 | get_driver().set_min_stop_pause(cs, min_stop_pause); | ||
| 392 | |||
| 393 | trace!("low power: stop with rtc configured"); | ||
| 394 | } | ||
diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 23f6ccb0c..8ac022536 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs | |||
| @@ -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..579c34c13 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)] |
| @@ -391,10 +391,11 @@ pub struct Config { | |||
| 391 | pub frame_sync_polarity: FrameSyncPolarity, | 391 | pub frame_sync_polarity: FrameSyncPolarity, |
| 392 | pub frame_sync_active_level_length: word::U7, | 392 | pub frame_sync_active_level_length: word::U7, |
| 393 | pub frame_sync_definition: FrameSyncDefinition, | 393 | pub frame_sync_definition: FrameSyncDefinition, |
| 394 | pub frame_length: u8, | 394 | pub frame_length: u16, |
| 395 | pub clock_strobe: ClockStrobe, | 395 | pub clock_strobe: ClockStrobe, |
| 396 | pub output_drive: OutputDrive, | 396 | pub output_drive: OutputDrive, |
| 397 | pub master_clock_divider: Option<MasterClockDivider>, | 397 | pub master_clock_divider: MasterClockDivider, |
| 398 | pub nodiv: bool, | ||
| 398 | pub is_high_impedance_on_inactive_slot: bool, | 399 | pub is_high_impedance_on_inactive_slot: bool, |
| 399 | pub fifo_threshold: FifoThreshold, | 400 | pub fifo_threshold: FifoThreshold, |
| 400 | pub companding: Companding, | 401 | pub companding: Companding, |
| @@ -423,7 +424,8 @@ impl Default for Config { | |||
| 423 | frame_sync_active_level_length: word::U7(16), | 424 | frame_sync_active_level_length: word::U7(16), |
| 424 | frame_sync_definition: FrameSyncDefinition::ChannelIdentification, | 425 | frame_sync_definition: FrameSyncDefinition::ChannelIdentification, |
| 425 | frame_length: 32, | 426 | frame_length: 32, |
| 426 | master_clock_divider: None, | 427 | master_clock_divider: MasterClockDivider::DIV1, |
| 428 | nodiv: false, | ||
| 427 | clock_strobe: ClockStrobe::Rising, | 429 | clock_strobe: ClockStrobe::Rising, |
| 428 | output_drive: OutputDrive::Immediately, | 430 | output_drive: OutputDrive::Immediately, |
| 429 | is_high_impedance_on_inactive_slot: false, | 431 | is_high_impedance_on_inactive_slot: false, |
| @@ -677,8 +679,8 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 677 | w.set_syncen(config.sync_input.syncen()); | 679 | w.set_syncen(config.sync_input.syncen()); |
| 678 | w.set_mono(config.stereo_mono.mono()); | 680 | w.set_mono(config.stereo_mono.mono()); |
| 679 | w.set_outdriv(config.output_drive.outdriv()); | 681 | w.set_outdriv(config.output_drive.outdriv()); |
| 680 | w.set_mckdiv(config.master_clock_divider.unwrap_or(MasterClockDivider::DIV1)); | 682 | w.set_mckdiv(config.master_clock_divider); |
| 681 | w.set_nodiv(config.master_clock_divider.is_none()); | 683 | w.set_nodiv(config.nodiv); |
| 682 | w.set_dmaen(true); | 684 | w.set_dmaen(true); |
| 683 | }); | 685 | }); |
| 684 | 686 | ||
| @@ -696,7 +698,7 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 696 | w.set_fspol(config.frame_sync_polarity.fspol()); | 698 | w.set_fspol(config.frame_sync_polarity.fspol()); |
| 697 | w.set_fsdef(config.frame_sync_definition.fsdef()); | 699 | w.set_fsdef(config.frame_sync_definition.fsdef()); |
| 698 | w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1); | 700 | w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1); |
| 699 | w.set_frl(config.frame_length - 1); | 701 | w.set_frl((config.frame_length - 1).try_into().unwrap()); |
| 700 | }); | 702 | }); |
| 701 | 703 | ||
| 702 | ch.slotr().modify(|w| { | 704 | ch.slotr().modify(|w| { |
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index ccbd16cbf..cfe18ef52 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs | |||
| @@ -4,16 +4,16 @@ | |||
| 4 | use core::default::Default; | 4 | use core::default::Default; |
| 5 | use core::future::poll_fn; | 5 | use core::future::poll_fn; |
| 6 | use core::marker::PhantomData; | 6 | use core::marker::PhantomData; |
| 7 | use core::ops::{Deref, DerefMut}; | 7 | use core::slice; |
| 8 | use core::task::Poll; | 8 | use core::task::Poll; |
| 9 | 9 | ||
| 10 | use embassy_hal_internal::drop::OnDrop; | 10 | use aligned::{A4, Aligned}; |
| 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::Cmd; |
| 14 | use sdio_host::emmc::{ExtCSD, EMMC}; | 14 | use sdio_host::common_cmd::{self, R1, R2, R3, Resp, ResponseLen, Rz}; |
| 15 | use sdio_host::sd::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CIC, CID, CSD, OCR, RCA, SCR, SD}; | 15 | use sdio_host::sd::{BusWidth, CardStatus}; |
| 16 | use sdio_host::{emmc_cmd, sd_cmd, Cmd}; | 16 | use sdio_host::sd_cmd::{R6, R7}; |
| 17 | 17 | ||
| 18 | #[cfg(sdmmc_v1)] | 18 | #[cfg(sdmmc_v1)] |
| 19 | use crate::dma::ChannelAndRequest; | 19 | use crate::dma::ChannelAndRequest; |
| @@ -22,37 +22,27 @@ use crate::gpio::Pull; | |||
| 22 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; | 22 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; |
| 23 | use crate::interrupt::typelevel::Interrupt; | 23 | use crate::interrupt::typelevel::Interrupt; |
| 24 | use crate::pac::sdmmc::Sdmmc as RegBlock; | 24 | use crate::pac::sdmmc::Sdmmc as RegBlock; |
| 25 | use crate::rcc::{self, RccPeripheral}; | 25 | use crate::rcc::{self, RccInfo, RccPeripheral, SealedRccPeripheral}; |
| 26 | use crate::sdmmc::sd::Addressable; | ||
| 26 | use crate::time::Hertz; | 27 | use crate::time::Hertz; |
| 27 | use crate::{interrupt, peripherals}; | 28 | use crate::{interrupt, peripherals}; |
| 28 | 29 | ||
| 30 | /// Module for SD and EMMC cards | ||
| 31 | pub mod sd; | ||
| 32 | |||
| 33 | /// Module for SDIO interface | ||
| 34 | pub mod sdio; | ||
| 35 | |||
| 29 | /// Interrupt handler. | 36 | /// Interrupt handler. |
| 30 | pub struct InterruptHandler<T: Instance> { | 37 | pub struct InterruptHandler<T: Instance> { |
| 31 | _phantom: PhantomData<T>, | 38 | _phantom: PhantomData<T>, |
| 32 | } | 39 | } |
| 33 | 40 | ||
| 34 | impl<T: Instance> InterruptHandler<T> { | ||
| 35 | fn enable_interrupts() { | ||
| 36 | let regs = T::regs(); | ||
| 37 | regs.maskr().write(|w| { | ||
| 38 | w.set_dcrcfailie(true); | ||
| 39 | w.set_dtimeoutie(true); | ||
| 40 | w.set_dataendie(true); | ||
| 41 | w.set_dbckendie(true); | ||
| 42 | |||
| 43 | #[cfg(sdmmc_v1)] | ||
| 44 | w.set_stbiterre(true); | ||
| 45 | #[cfg(sdmmc_v2)] | ||
| 46 | w.set_dabortie(true); | ||
| 47 | }); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { | 41 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { |
| 52 | unsafe fn on_interrupt() { | 42 | unsafe fn on_interrupt() { |
| 53 | T::state().wake(); | 43 | T::state().waker.wake(); |
| 54 | let status = T::regs().star().read(); | 44 | let status = T::info().regs.star().read(); |
| 55 | T::regs().maskr().modify(|w| { | 45 | T::info().regs.maskr().modify(|w| { |
| 56 | if status.dcrcfail() { | 46 | if status.dcrcfail() { |
| 57 | w.set_dcrcfailie(false) | 47 | w.set_dcrcfailie(false) |
| 58 | } | 48 | } |
| @@ -77,6 +67,57 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 77 | } | 67 | } |
| 78 | } | 68 | } |
| 79 | 69 | ||
| 70 | struct U128(pub u128); | ||
| 71 | |||
| 72 | trait TypedResp: Resp { | ||
| 73 | type Word: From<U128>; | ||
| 74 | } | ||
| 75 | |||
| 76 | impl From<U128> for () { | ||
| 77 | fn from(value: U128) -> Self { | ||
| 78 | match value.0 { | ||
| 79 | 0 => (), | ||
| 80 | _ => unreachable!(), | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | impl From<U128> for u32 { | ||
| 86 | fn from(value: U128) -> Self { | ||
| 87 | unwrap!(value.0.try_into()) | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | impl From<U128> for u128 { | ||
| 92 | fn from(value: U128) -> Self { | ||
| 93 | value.0 | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | impl TypedResp for Rz { | ||
| 98 | type Word = (); | ||
| 99 | } | ||
| 100 | |||
| 101 | impl TypedResp for R1 { | ||
| 102 | type Word = u32; | ||
| 103 | } | ||
| 104 | |||
| 105 | impl TypedResp for R2 { | ||
| 106 | type Word = u128; | ||
| 107 | } | ||
| 108 | |||
| 109 | impl TypedResp for R3 { | ||
| 110 | type Word = u32; | ||
| 111 | } | ||
| 112 | |||
| 113 | impl TypedResp for R6 { | ||
| 114 | type Word = u32; | ||
| 115 | } | ||
| 116 | |||
| 117 | impl TypedResp for R7 { | ||
| 118 | type Word = u32; | ||
| 119 | } | ||
| 120 | |||
| 80 | /// Frequency used for SD Card initialization. Must be no higher than 400 kHz. | 121 | /// Frequency used for SD Card initialization. Must be no higher than 400 kHz. |
| 81 | const SD_INIT_FREQ: Hertz = Hertz(400_000); | 122 | const SD_INIT_FREQ: Hertz = Hertz(400_000); |
| 82 | 123 | ||
| @@ -99,54 +140,36 @@ impl Default for Signalling { | |||
| 99 | } | 140 | } |
| 100 | } | 141 | } |
| 101 | 142 | ||
| 102 | /// Aligned data block for SDMMC transfers. | 143 | const fn aligned_mut(x: &mut [u32]) -> &mut Aligned<A4, [u8]> { |
| 103 | /// | 144 | let len = x.len() * 4; |
| 104 | /// This is a 512-byte array, aligned to 4 bytes to satisfy DMA requirements. | 145 | unsafe { core::mem::transmute(slice::from_raw_parts_mut(x.as_mut_ptr() as *mut u8, len)) } |
| 105 | #[repr(align(4))] | ||
| 106 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 107 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 108 | pub struct DataBlock(pub [u8; 512]); | ||
| 109 | |||
| 110 | impl Deref for DataBlock { | ||
| 111 | type Target = [u8; 512]; | ||
| 112 | |||
| 113 | fn deref(&self) -> &Self::Target { | ||
| 114 | &self.0 | ||
| 115 | } | ||
| 116 | } | 146 | } |
| 117 | 147 | ||
| 118 | impl DerefMut for DataBlock { | 148 | const fn slice8_mut(x: &mut [u32]) -> &mut [u8] { |
| 119 | fn deref_mut(&mut self) -> &mut Self::Target { | 149 | let len = x.len() * 4; |
| 120 | &mut self.0 | 150 | unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as *mut u8, len) } |
| 121 | } | ||
| 122 | } | 151 | } |
| 123 | 152 | ||
| 124 | /// Command Block buffer for SDMMC command transfers. | 153 | #[allow(unused)] |
| 125 | /// | 154 | const fn slice32_mut(x: &mut Aligned<A4, [u8]>) -> &mut [u32] { |
| 126 | /// This is a 16-word array, exposed so that DMA commpatible memory can be used if required. | 155 | let len = (size_of_val(x) + 4 - 1) / 4; |
| 127 | #[derive(Debug, Clone, PartialEq, Eq)] | 156 | unsafe { slice::from_raw_parts_mut(x as *mut Aligned<A4, [u8]> as *mut u32, len) } |
| 128 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 129 | pub struct CmdBlock(pub [u32; 16]); | ||
| 130 | |||
| 131 | impl CmdBlock { | ||
| 132 | /// Creates a new instance of CmdBlock | ||
| 133 | pub const fn new() -> Self { | ||
| 134 | Self([0u32; 16]) | ||
| 135 | } | ||
| 136 | } | 157 | } |
| 137 | 158 | ||
| 138 | impl Deref for CmdBlock { | 159 | const fn aligned_ref(x: &[u32]) -> &Aligned<A4, [u8]> { |
| 139 | type Target = [u32; 16]; | 160 | let len = x.len() * 4; |
| 161 | unsafe { core::mem::transmute(slice::from_raw_parts(x.as_ptr() as *const u8, len)) } | ||
| 162 | } | ||
| 140 | 163 | ||
| 141 | fn deref(&self) -> &Self::Target { | 164 | const fn slice8_ref(x: &[u32]) -> &[u8] { |
| 142 | &self.0 | 165 | let len = x.len() * 4; |
| 143 | } | 166 | unsafe { slice::from_raw_parts(x.as_ptr() as *const u8, len) } |
| 144 | } | 167 | } |
| 145 | 168 | ||
| 146 | impl DerefMut for CmdBlock { | 169 | #[allow(unused)] |
| 147 | fn deref_mut(&mut self) -> &mut Self::Target { | 170 | const fn slice32_ref(x: &Aligned<A4, [u8]>) -> &[u32] { |
| 148 | &mut self.0 | 171 | let len = (size_of_val(x) + 4 - 1) / 4; |
| 149 | } | 172 | unsafe { slice::from_raw_parts(x as *const Aligned<A4, [u8]> as *const u32, len) } |
| 150 | } | 173 | } |
| 151 | 174 | ||
| 152 | /// Errors | 175 | /// Errors |
| @@ -181,48 +204,17 @@ pub enum Error { | |||
| 181 | StBitErr, | 204 | StBitErr, |
| 182 | } | 205 | } |
| 183 | 206 | ||
| 184 | #[derive(Clone, Copy, Debug, Default)] | ||
| 185 | /// SD Card | ||
| 186 | pub struct Card { | ||
| 187 | /// The type of this card | ||
| 188 | pub card_type: CardCapacity, | ||
| 189 | /// Operation Conditions Register | ||
| 190 | pub ocr: OCR<SD>, | ||
| 191 | /// Relative Card Address | ||
| 192 | pub rca: u16, | ||
| 193 | /// Card ID | ||
| 194 | pub cid: CID<SD>, | ||
| 195 | /// Card Specific Data | ||
| 196 | pub csd: CSD<SD>, | ||
| 197 | /// SD CARD Configuration Register | ||
| 198 | pub scr: SCR, | ||
| 199 | /// SD Status | ||
| 200 | pub status: SDStatus, | ||
| 201 | } | ||
| 202 | |||
| 203 | #[derive(Clone, Copy, Debug, Default)] | ||
| 204 | /// eMMC storage | ||
| 205 | pub struct Emmc { | ||
| 206 | /// The capacity of this card | ||
| 207 | pub capacity: CardCapacity, | ||
| 208 | /// Operation Conditions Register | ||
| 209 | pub ocr: OCR<EMMC>, | ||
| 210 | /// Relative Card Address | ||
| 211 | pub rca: u16, | ||
| 212 | /// Card ID | ||
| 213 | pub cid: CID<EMMC>, | ||
| 214 | /// Card Specific Data | ||
| 215 | pub csd: CSD<EMMC>, | ||
| 216 | /// Extended Card Specific Data | ||
| 217 | pub ext_csd: ExtCSD, | ||
| 218 | } | ||
| 219 | |||
| 220 | #[repr(u8)] | 207 | #[repr(u8)] |
| 221 | enum PowerCtrl { | 208 | enum PowerCtrl { |
| 222 | Off = 0b00, | 209 | Off = 0b00, |
| 223 | On = 0b11, | 210 | On = 0b11, |
| 224 | } | 211 | } |
| 225 | 212 | ||
| 213 | enum DatapathMode { | ||
| 214 | Block(BlockSize), | ||
| 215 | Byte, | ||
| 216 | } | ||
| 217 | |||
| 226 | fn get_waitresp_val(rlen: ResponseLen) -> u8 { | 218 | fn get_waitresp_val(rlen: ResponseLen) -> u8 { |
| 227 | match rlen { | 219 | match rlen { |
| 228 | common_cmd::ResponseLen::Zero => 0, | 220 | common_cmd::ResponseLen::Zero => 0, |
| @@ -259,6 +251,55 @@ fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> { | |||
| 259 | Ok((false, clk_div, clk_f)) | 251 | Ok((false, clk_div, clk_f)) |
| 260 | } | 252 | } |
| 261 | 253 | ||
| 254 | fn bus_width_vals(bus_width: BusWidth) -> (u8, u32) { | ||
| 255 | match bus_width { | ||
| 256 | BusWidth::One => (0, 1u32), | ||
| 257 | BusWidth::Four => (1, 4u32), | ||
| 258 | BusWidth::Eight => (2, 8u32), | ||
| 259 | _ => panic!("Invalid Bus Width"), | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | #[repr(u8)] | ||
| 264 | enum BlockSize { | ||
| 265 | Size1 = 0b0000, | ||
| 266 | Size2 = 0b0001, | ||
| 267 | Size4 = 0b0010, | ||
| 268 | Size8 = 0b0011, | ||
| 269 | Size16 = 0b0100, | ||
| 270 | Size32 = 0b0101, | ||
| 271 | Size64 = 0b0110, | ||
| 272 | Size128 = 0b0111, | ||
| 273 | Size256 = 0b1000, | ||
| 274 | Size512 = 0b1001, | ||
| 275 | Size1024 = 0b1010, | ||
| 276 | Size2048 = 0b1011, | ||
| 277 | Size4096 = 0b1100, | ||
| 278 | Size8192 = 0b1101, | ||
| 279 | Size16384 = 0b1110, | ||
| 280 | } | ||
| 281 | |||
| 282 | const fn block_size(bytes: usize) -> BlockSize { | ||
| 283 | match bytes { | ||
| 284 | 1 => BlockSize::Size1, | ||
| 285 | 2 => BlockSize::Size2, | ||
| 286 | 4 => BlockSize::Size4, | ||
| 287 | 8 => BlockSize::Size8, | ||
| 288 | 16 => BlockSize::Size16, | ||
| 289 | 32 => BlockSize::Size32, | ||
| 290 | 64 => BlockSize::Size64, | ||
| 291 | 128 => BlockSize::Size128, | ||
| 292 | 256 => BlockSize::Size256, | ||
| 293 | 512 => BlockSize::Size512, | ||
| 294 | 1024 => BlockSize::Size1024, | ||
| 295 | 2048 => BlockSize::Size2048, | ||
| 296 | 4096 => BlockSize::Size4096, | ||
| 297 | 8192 => BlockSize::Size8192, | ||
| 298 | 16384 => BlockSize::Size16384, | ||
| 299 | _ => core::unreachable!(), | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 262 | /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to | 303 | /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to |
| 263 | /// `sdmmc_ck` in Hertz. | 304 | /// `sdmmc_ck` in Hertz. |
| 264 | /// | 305 | /// |
| @@ -286,6 +327,34 @@ struct Transfer<'a> { | |||
| 286 | _dummy: PhantomData<&'a ()>, | 327 | _dummy: PhantomData<&'a ()>, |
| 287 | } | 328 | } |
| 288 | 329 | ||
| 330 | struct WrappedTransfer<'a> { | ||
| 331 | _transfer: Transfer<'a>, | ||
| 332 | sdmmc: &'a Sdmmc<'a>, | ||
| 333 | defused: bool, | ||
| 334 | } | ||
| 335 | |||
| 336 | impl<'a> WrappedTransfer<'a> { | ||
| 337 | pub const fn new(_transfer: Transfer<'a>, sdmmc: &'a Sdmmc) -> Self { | ||
| 338 | Self { | ||
| 339 | _transfer, | ||
| 340 | sdmmc, | ||
| 341 | defused: false, | ||
| 342 | } | ||
| 343 | } | ||
| 344 | |||
| 345 | pub fn defuse(&mut self) { | ||
| 346 | self.defused = true; | ||
| 347 | } | ||
| 348 | } | ||
| 349 | |||
| 350 | impl<'a> Drop for WrappedTransfer<'a> { | ||
| 351 | fn drop(&mut self) { | ||
| 352 | if !self.defused { | ||
| 353 | self.sdmmc.on_drop(); | ||
| 354 | } | ||
| 355 | } | ||
| 356 | } | ||
| 357 | |||
| 289 | #[cfg(all(sdmmc_v1, dma))] | 358 | #[cfg(all(sdmmc_v1, dma))] |
| 290 | const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions { | 359 | const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions { |
| 291 | pburst: crate::dma::Burst::Incr4, | 360 | pburst: crate::dma::Burst::Incr4, |
| @@ -323,64 +392,11 @@ impl Default for Config { | |||
| 323 | } | 392 | } |
| 324 | } | 393 | } |
| 325 | 394 | ||
| 326 | /// Peripheral that can be operated over SDMMC | ||
| 327 | #[derive(Clone, Copy, Debug)] | ||
| 328 | pub enum SdmmcPeripheral { | ||
| 329 | /// SD Card | ||
| 330 | SdCard(Card), | ||
| 331 | /// eMMC memory | ||
| 332 | Emmc(Emmc), | ||
| 333 | } | ||
| 334 | |||
| 335 | impl SdmmcPeripheral { | ||
| 336 | /// Get this peripheral's address on the SDMMC bus | ||
| 337 | fn get_address(&self) -> u16 { | ||
| 338 | match self { | ||
| 339 | Self::SdCard(c) => c.rca, | ||
| 340 | Self::Emmc(e) => e.rca, | ||
| 341 | } | ||
| 342 | } | ||
| 343 | /// Is this a standard or high capacity peripheral? | ||
| 344 | fn get_capacity(&self) -> CardCapacity { | ||
| 345 | match self { | ||
| 346 | Self::SdCard(c) => c.card_type, | ||
| 347 | Self::Emmc(e) => e.capacity, | ||
| 348 | } | ||
| 349 | } | ||
| 350 | /// Size in bytes | ||
| 351 | fn size(&self) -> u64 { | ||
| 352 | match self { | ||
| 353 | // SDHC / SDXC / SDUC | ||
| 354 | Self::SdCard(c) => u64::from(c.csd.block_count()) * 512, | ||
| 355 | // capacity > 2GB | ||
| 356 | Self::Emmc(e) => u64::from(e.ext_csd.sector_count()) * 512, | ||
| 357 | } | ||
| 358 | } | ||
| 359 | |||
| 360 | /// Get a mutable reference to the SD Card. | ||
| 361 | /// | ||
| 362 | /// Panics if there is another peripheral instead. | ||
| 363 | fn get_sd_card(&mut self) -> &mut Card { | ||
| 364 | match *self { | ||
| 365 | Self::SdCard(ref mut c) => c, | ||
| 366 | _ => unreachable!("SD only"), | ||
| 367 | } | ||
| 368 | } | ||
| 369 | |||
| 370 | /// Get a mutable reference to the eMMC. | ||
| 371 | /// | ||
| 372 | /// Panics if there is another peripheral instead. | ||
| 373 | fn get_emmc(&mut self) -> &mut Emmc { | ||
| 374 | match *self { | ||
| 375 | Self::Emmc(ref mut e) => e, | ||
| 376 | _ => unreachable!("eMMC only"), | ||
| 377 | } | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | /// Sdmmc device | 395 | /// Sdmmc device |
| 382 | pub struct Sdmmc<'d, T: Instance> { | 396 | pub struct Sdmmc<'d> { |
| 383 | _peri: Peri<'d, T>, | 397 | info: &'static Info, |
| 398 | state: &'static State, | ||
| 399 | ker_clk: Hertz, | ||
| 384 | #[cfg(sdmmc_v1)] | 400 | #[cfg(sdmmc_v1)] |
| 385 | dma: ChannelAndRequest<'d>, | 401 | dma: ChannelAndRequest<'d>, |
| 386 | 402 | ||
| @@ -400,12 +416,6 @@ pub struct Sdmmc<'d, T: Instance> { | |||
| 400 | clock: Hertz, | 416 | clock: Hertz, |
| 401 | /// Current signalling scheme to card | 417 | /// Current signalling scheme to card |
| 402 | signalling: Signalling, | 418 | signalling: Signalling, |
| 403 | /// Card | ||
| 404 | card: Option<SdmmcPeripheral>, | ||
| 405 | |||
| 406 | /// An optional buffer to be used for commands | ||
| 407 | /// This should be used if there are special memory location requirements for dma | ||
| 408 | cmd_block: Option<&'d mut CmdBlock>, | ||
| 409 | } | 419 | } |
| 410 | 420 | ||
| 411 | const CLK_AF: AfType = AfType::output(OutputType::PushPull, Speed::VeryHigh); | 421 | const CLK_AF: AfType = AfType::output(OutputType::PushPull, Speed::VeryHigh); |
| @@ -416,9 +426,9 @@ const CMD_AF: AfType = AfType::output_pull(OutputType::PushPull, Speed::VeryHigh | |||
| 416 | const DATA_AF: AfType = CMD_AF; | 426 | const DATA_AF: AfType = CMD_AF; |
| 417 | 427 | ||
| 418 | #[cfg(sdmmc_v1)] | 428 | #[cfg(sdmmc_v1)] |
| 419 | impl<'d, T: Instance> Sdmmc<'d, T> { | 429 | impl<'d> Sdmmc<'d> { |
| 420 | /// Create a new SDMMC driver, with 1 data lane. | 430 | /// Create a new SDMMC driver, with 1 data lane. |
| 421 | pub fn new_1bit( | 431 | pub fn new_1bit<T: Instance>( |
| 422 | sdmmc: Peri<'d, T>, | 432 | sdmmc: Peri<'d, T>, |
| 423 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 433 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 424 | dma: Peri<'d, impl SdmmcDma<T>>, | 434 | dma: Peri<'d, impl SdmmcDma<T>>, |
| @@ -451,7 +461,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 451 | } | 461 | } |
| 452 | 462 | ||
| 453 | /// Create a new SDMMC driver, with 4 data lanes. | 463 | /// Create a new SDMMC driver, with 4 data lanes. |
| 454 | pub fn new_4bit( | 464 | pub fn new_4bit<T: Instance>( |
| 455 | sdmmc: Peri<'d, T>, | 465 | sdmmc: Peri<'d, T>, |
| 456 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 466 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 457 | dma: Peri<'d, impl SdmmcDma<T>>, | 467 | dma: Peri<'d, impl SdmmcDma<T>>, |
| @@ -491,9 +501,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 491 | } | 501 | } |
| 492 | 502 | ||
| 493 | #[cfg(sdmmc_v1)] | 503 | #[cfg(sdmmc_v1)] |
| 494 | impl<'d, T: Instance> Sdmmc<'d, T> { | 504 | impl<'d> Sdmmc<'d> { |
| 495 | /// Create a new SDMMC driver, with 8 data lanes. | 505 | /// Create a new SDMMC driver, with 8 data lanes. |
| 496 | pub fn new_8bit( | 506 | pub fn new_8bit<T: Instance>( |
| 497 | sdmmc: Peri<'d, T>, | 507 | sdmmc: Peri<'d, T>, |
| 498 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 508 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 499 | dma: Peri<'d, impl SdmmcDma<T>>, | 509 | dma: Peri<'d, impl SdmmcDma<T>>, |
| @@ -541,9 +551,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 541 | } | 551 | } |
| 542 | 552 | ||
| 543 | #[cfg(sdmmc_v2)] | 553 | #[cfg(sdmmc_v2)] |
| 544 | impl<'d, T: Instance> Sdmmc<'d, T> { | 554 | impl<'d> Sdmmc<'d> { |
| 545 | /// Create a new SDMMC driver, with 1 data lane. | 555 | /// Create a new SDMMC driver, with 1 data lane. |
| 546 | pub fn new_1bit( | 556 | pub fn new_1bit<T: Instance>( |
| 547 | sdmmc: Peri<'d, T>, | 557 | sdmmc: Peri<'d, T>, |
| 548 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 558 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 549 | clk: Peri<'d, impl CkPin<T>>, | 559 | clk: Peri<'d, impl CkPin<T>>, |
| @@ -574,7 +584,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 574 | } | 584 | } |
| 575 | 585 | ||
| 576 | /// Create a new SDMMC driver, with 4 data lanes. | 586 | /// Create a new SDMMC driver, with 4 data lanes. |
| 577 | pub fn new_4bit( | 587 | pub fn new_4bit<T: Instance>( |
| 578 | sdmmc: Peri<'d, T>, | 588 | sdmmc: Peri<'d, T>, |
| 579 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 589 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 580 | clk: Peri<'d, impl CkPin<T>>, | 590 | clk: Peri<'d, impl CkPin<T>>, |
| @@ -612,9 +622,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 612 | } | 622 | } |
| 613 | 623 | ||
| 614 | #[cfg(sdmmc_v2)] | 624 | #[cfg(sdmmc_v2)] |
| 615 | impl<'d, T: Instance> Sdmmc<'d, T> { | 625 | impl<'d> Sdmmc<'d> { |
| 616 | /// Create a new SDMMC driver, with 8 data lanes. | 626 | /// Create a new SDMMC driver, with 8 data lanes. |
| 617 | pub fn new_8bit( | 627 | pub fn new_8bit<T: Instance>( |
| 618 | sdmmc: Peri<'d, T>, | 628 | sdmmc: Peri<'d, T>, |
| 619 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 629 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 620 | clk: Peri<'d, impl CkPin<T>>, | 630 | clk: Peri<'d, impl CkPin<T>>, |
| @@ -659,9 +669,24 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 659 | } | 669 | } |
| 660 | } | 670 | } |
| 661 | 671 | ||
| 662 | impl<'d, T: Instance> Sdmmc<'d, T> { | 672 | impl<'d> Sdmmc<'d> { |
| 663 | fn new_inner( | 673 | fn enable_interrupts(&self) { |
| 664 | sdmmc: Peri<'d, T>, | 674 | let regs = self.info.regs; |
| 675 | regs.maskr().write(|w| { | ||
| 676 | w.set_dcrcfailie(true); | ||
| 677 | w.set_dtimeoutie(true); | ||
| 678 | w.set_dataendie(true); | ||
| 679 | w.set_dbckendie(true); | ||
| 680 | |||
| 681 | #[cfg(sdmmc_v1)] | ||
| 682 | w.set_stbiterre(true); | ||
| 683 | #[cfg(sdmmc_v2)] | ||
| 684 | w.set_dabortie(true); | ||
| 685 | }); | ||
| 686 | } | ||
| 687 | |||
| 688 | fn new_inner<T: Instance>( | ||
| 689 | _sdmmc: Peri<'d, T>, | ||
| 665 | #[cfg(sdmmc_v1)] dma: ChannelAndRequest<'d>, | 690 | #[cfg(sdmmc_v1)] dma: ChannelAndRequest<'d>, |
| 666 | clk: Peri<'d, AnyPin>, | 691 | clk: Peri<'d, AnyPin>, |
| 667 | cmd: Peri<'d, AnyPin>, | 692 | cmd: Peri<'d, AnyPin>, |
| @@ -675,13 +700,16 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 675 | d7: Option<Peri<'d, AnyPin>>, | 700 | d7: Option<Peri<'d, AnyPin>>, |
| 676 | config: Config, | 701 | config: Config, |
| 677 | ) -> Self { | 702 | ) -> Self { |
| 678 | rcc::enable_and_reset::<T>(); | 703 | rcc::enable_and_reset_without_stop::<T>(); |
| 679 | 704 | ||
| 680 | T::Interrupt::unpend(); | 705 | T::Interrupt::unpend(); |
| 681 | unsafe { T::Interrupt::enable() }; | 706 | unsafe { T::Interrupt::enable() }; |
| 682 | 707 | ||
| 683 | let regs = T::regs(); | 708 | let info = T::info(); |
| 684 | regs.clkcr().write(|w| { | 709 | let state = T::state(); |
| 710 | let ker_clk = T::frequency(); | ||
| 711 | |||
| 712 | info.regs.clkcr().write(|w| { | ||
| 685 | w.set_pwrsav(false); | 713 | w.set_pwrsav(false); |
| 686 | w.set_negedge(false); | 714 | w.set_negedge(false); |
| 687 | 715 | ||
| @@ -698,10 +726,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 698 | 726 | ||
| 699 | // Power off, writen 00: Clock to the card is stopped; | 727 | // Power off, writen 00: Clock to the card is stopped; |
| 700 | // D[7:0], CMD, and CK are driven high. | 728 | // D[7:0], CMD, and CK are driven high. |
| 701 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8)); | 729 | info.regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8)); |
| 702 | 730 | ||
| 703 | Self { | 731 | Self { |
| 704 | _peri: sdmmc, | 732 | info, |
| 733 | state, | ||
| 734 | ker_clk, | ||
| 705 | #[cfg(sdmmc_v1)] | 735 | #[cfg(sdmmc_v1)] |
| 706 | dma, | 736 | dma, |
| 707 | 737 | ||
| @@ -719,15 +749,13 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 719 | config, | 749 | config, |
| 720 | clock: SD_INIT_FREQ, | 750 | clock: SD_INIT_FREQ, |
| 721 | signalling: Default::default(), | 751 | signalling: Default::default(), |
| 722 | card: None, | ||
| 723 | cmd_block: None, | ||
| 724 | } | 752 | } |
| 725 | } | 753 | } |
| 726 | 754 | ||
| 727 | /// Data transfer is in progress | 755 | /// Data transfer is in progress |
| 728 | #[inline] | 756 | #[inline] |
| 729 | fn data_active() -> bool { | 757 | fn data_active(&self) -> bool { |
| 730 | let regs = T::regs(); | 758 | let regs = self.info.regs; |
| 731 | 759 | ||
| 732 | let status = regs.star().read(); | 760 | let status = regs.star().read(); |
| 733 | #[cfg(sdmmc_v1)] | 761 | #[cfg(sdmmc_v1)] |
| @@ -738,8 +766,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 738 | 766 | ||
| 739 | /// Coammand transfer is in progress | 767 | /// Coammand transfer is in progress |
| 740 | #[inline] | 768 | #[inline] |
| 741 | fn cmd_active() -> bool { | 769 | fn cmd_active(&self) -> bool { |
| 742 | let regs = T::regs(); | 770 | let regs = self.info.regs; |
| 743 | 771 | ||
| 744 | let status = regs.star().read(); | 772 | let status = regs.star().read(); |
| 745 | #[cfg(sdmmc_v1)] | 773 | #[cfg(sdmmc_v1)] |
| @@ -750,8 +778,16 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 750 | 778 | ||
| 751 | /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2) | 779 | /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2) |
| 752 | #[inline] | 780 | #[inline] |
| 753 | fn wait_idle() { | 781 | fn wait_idle(&self) { |
| 754 | while Self::data_active() || Self::cmd_active() {} | 782 | while self.data_active() || self.cmd_active() {} |
| 783 | } | ||
| 784 | |||
| 785 | fn bus_width(&self) -> BusWidth { | ||
| 786 | match (self.d3.is_some(), self.d7.is_some()) { | ||
| 787 | (true, true) => BusWidth::Eight, | ||
| 788 | (true, false) => BusWidth::Four, | ||
| 789 | _ => BusWidth::One, | ||
| 790 | } | ||
| 755 | } | 791 | } |
| 756 | 792 | ||
| 757 | /// # Safety | 793 | /// # Safety |
| @@ -759,23 +795,32 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 759 | /// `buffer` must be valid for the whole transfer and word aligned | 795 | /// `buffer` must be valid for the whole transfer and word aligned |
| 760 | #[allow(unused_variables)] | 796 | #[allow(unused_variables)] |
| 761 | fn prepare_datapath_read<'a>( | 797 | fn prepare_datapath_read<'a>( |
| 762 | config: &Config, | 798 | &'a self, |
| 763 | #[cfg(sdmmc_v1)] dma: &'a mut ChannelAndRequest<'d>, | 799 | buffer: &'a mut Aligned<A4, [u8]>, |
| 764 | buffer: &'a mut [u32], | 800 | mode: DatapathMode, |
| 765 | length_bytes: u32, | 801 | ) -> WrappedTransfer<'a> { |
| 766 | block_size: u8, | 802 | let regs = self.info.regs; |
| 767 | ) -> Transfer<'a> { | 803 | |
| 768 | assert!(block_size <= 14, "Block size up to 2^14 bytes"); | 804 | let (byte_mode, block_size) = match mode { |
| 769 | let regs = T::regs(); | 805 | DatapathMode::Block(block_size) => (false, block_size as u8), |
| 806 | DatapathMode::Byte => (true, 0), | ||
| 807 | }; | ||
| 770 | 808 | ||
| 771 | // Command AND Data state machines must be idle | 809 | // Command AND Data state machines must be idle |
| 772 | Self::wait_idle(); | 810 | self.wait_idle(); |
| 773 | Self::clear_interrupt_flags(); | 811 | self.clear_interrupt_flags(); |
| 774 | 812 | ||
| 775 | regs.dlenr().write(|w| w.set_datalength(length_bytes)); | 813 | regs.dlenr().write(|w| w.set_datalength(size_of_val(buffer) as u32)); |
| 776 | 814 | ||
| 815 | // SAFETY: No other functions use the dma | ||
| 777 | #[cfg(sdmmc_v1)] | 816 | #[cfg(sdmmc_v1)] |
| 778 | let transfer = unsafe { dma.read(regs.fifor().as_ptr() as *mut u32, buffer, DMA_TRANSFER_OPTIONS) }; | 817 | let transfer = unsafe { |
| 818 | self.dma.read_unchecked( | ||
| 819 | regs.fifor().as_ptr() as *mut u32, | ||
| 820 | slice32_mut(buffer), | ||
| 821 | DMA_TRANSFER_OPTIONS, | ||
| 822 | ) | ||
| 823 | }; | ||
| 779 | #[cfg(sdmmc_v2)] | 824 | #[cfg(sdmmc_v2)] |
| 780 | let transfer = { | 825 | let transfer = { |
| 781 | regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_mut_ptr() as u32)); | 826 | regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_mut_ptr() as u32)); |
| @@ -785,8 +830,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 785 | } | 830 | } |
| 786 | }; | 831 | }; |
| 787 | 832 | ||
| 833 | #[cfg(sdmmc_v2)] | ||
| 834 | let byte_mode = byte_mode as u8; | ||
| 835 | |||
| 788 | regs.dctrl().modify(|w| { | 836 | regs.dctrl().modify(|w| { |
| 789 | w.set_dblocksize(block_size); | 837 | w.set_dtmode(byte_mode); |
| 838 | w.set_dblocksize(block_size as u8); | ||
| 790 | w.set_dtdir(true); | 839 | w.set_dtdir(true); |
| 791 | #[cfg(sdmmc_v1)] | 840 | #[cfg(sdmmc_v1)] |
| 792 | { | 841 | { |
| @@ -795,26 +844,36 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 795 | } | 844 | } |
| 796 | }); | 845 | }); |
| 797 | 846 | ||
| 798 | transfer | 847 | self.enable_interrupts(); |
| 848 | |||
| 849 | WrappedTransfer::new(transfer, &self) | ||
| 799 | } | 850 | } |
| 800 | 851 | ||
| 801 | /// # Safety | 852 | /// # Safety |
| 802 | /// | 853 | /// |
| 803 | /// `buffer` must be valid for the whole transfer and word aligned | 854 | /// `buffer` must be valid for the whole transfer and word aligned |
| 804 | fn prepare_datapath_write<'a>(&'a mut self, buffer: &'a [u32], length_bytes: u32, block_size: u8) -> Transfer<'a> { | 855 | fn prepare_datapath_write<'a>(&'a self, buffer: &'a Aligned<A4, [u8]>, mode: DatapathMode) -> WrappedTransfer<'a> { |
| 805 | assert!(block_size <= 14, "Block size up to 2^14 bytes"); | 856 | let regs = self.info.regs; |
| 806 | let regs = T::regs(); | 857 | |
| 858 | let (byte_mode, block_size) = match mode { | ||
| 859 | DatapathMode::Block(block_size) => (false, block_size as u8), | ||
| 860 | DatapathMode::Byte => (true, 0), | ||
| 861 | }; | ||
| 807 | 862 | ||
| 808 | // Command AND Data state machines must be idle | 863 | // Command AND Data state machines must be idle |
| 809 | Self::wait_idle(); | 864 | self.wait_idle(); |
| 810 | Self::clear_interrupt_flags(); | 865 | self.clear_interrupt_flags(); |
| 811 | 866 | ||
| 812 | regs.dlenr().write(|w| w.set_datalength(length_bytes)); | 867 | regs.dlenr().write(|w| w.set_datalength(size_of_val(buffer) as u32)); |
| 813 | 868 | ||
| 869 | // SAFETY: No other functions use the dma | ||
| 814 | #[cfg(sdmmc_v1)] | 870 | #[cfg(sdmmc_v1)] |
| 815 | let transfer = unsafe { | 871 | let transfer = unsafe { |
| 816 | self.dma | 872 | self.dma.write_unchecked( |
| 817 | .write(buffer, regs.fifor().as_ptr() as *mut u32, DMA_TRANSFER_OPTIONS) | 873 | slice32_ref(buffer), |
| 874 | regs.fifor().as_ptr() as *mut u32, | ||
| 875 | DMA_TRANSFER_OPTIONS, | ||
| 876 | ) | ||
| 818 | }; | 877 | }; |
| 819 | #[cfg(sdmmc_v2)] | 878 | #[cfg(sdmmc_v2)] |
| 820 | let transfer = { | 879 | let transfer = { |
| @@ -825,8 +884,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 825 | } | 884 | } |
| 826 | }; | 885 | }; |
| 827 | 886 | ||
| 887 | #[cfg(sdmmc_v2)] | ||
| 888 | let byte_mode = byte_mode as u8; | ||
| 889 | |||
| 828 | regs.dctrl().modify(|w| { | 890 | regs.dctrl().modify(|w| { |
| 829 | w.set_dblocksize(block_size); | 891 | w.set_dtmode(byte_mode); |
| 892 | w.set_dblocksize(block_size as u8); | ||
| 830 | w.set_dtdir(false); | 893 | w.set_dtdir(false); |
| 831 | #[cfg(sdmmc_v1)] | 894 | #[cfg(sdmmc_v1)] |
| 832 | { | 895 | { |
| @@ -835,12 +898,14 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 835 | } | 898 | } |
| 836 | }); | 899 | }); |
| 837 | 900 | ||
| 838 | transfer | 901 | self.enable_interrupts(); |
| 902 | |||
| 903 | WrappedTransfer::new(transfer, &self) | ||
| 839 | } | 904 | } |
| 840 | 905 | ||
| 841 | /// Stops the DMA datapath | 906 | /// Stops the DMA datapath |
| 842 | fn stop_datapath() { | 907 | fn stop_datapath(&self) { |
| 843 | let regs = T::regs(); | 908 | let regs = self.info.regs; |
| 844 | 909 | ||
| 845 | #[cfg(sdmmc_v1)] | 910 | #[cfg(sdmmc_v1)] |
| 846 | regs.dctrl().modify(|w| { | 911 | regs.dctrl().modify(|w| { |
| @@ -851,49 +916,58 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 851 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); | 916 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); |
| 852 | } | 917 | } |
| 853 | 918 | ||
| 919 | fn init_idle(&mut self) -> Result<(), Error> { | ||
| 920 | let regs = self.info.regs; | ||
| 921 | |||
| 922 | self.clkcr_set_clkdiv(SD_INIT_FREQ, BusWidth::One)?; | ||
| 923 | regs.dtimer() | ||
| 924 | .write(|w| w.set_datatime(self.config.data_transfer_timeout)); | ||
| 925 | |||
| 926 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); | ||
| 927 | self.cmd(common_cmd::idle(), false) | ||
| 928 | } | ||
| 929 | |||
| 854 | /// Sets the CLKDIV field in CLKCR. Updates clock field in self | 930 | /// Sets the CLKDIV field in CLKCR. Updates clock field in self |
| 855 | fn clkcr_set_clkdiv(&mut self, freq: u32, width: BusWidth) -> Result<(), Error> { | 931 | fn clkcr_set_clkdiv(&mut self, freq: Hertz, width: BusWidth) -> Result<(), Error> { |
| 856 | let regs = T::regs(); | 932 | let regs = self.info.regs; |
| 857 | |||
| 858 | let width_u32 = match width { | ||
| 859 | BusWidth::One => 1u32, | ||
| 860 | BusWidth::Four => 4u32, | ||
| 861 | BusWidth::Eight => 8u32, | ||
| 862 | _ => panic!("Invalid Bus Width"), | ||
| 863 | }; | ||
| 864 | 933 | ||
| 865 | let ker_ck = T::frequency(); | 934 | let (widbus, width_u32) = bus_width_vals(width); |
| 866 | let (_bypass, clkdiv, new_clock) = clk_div(ker_ck, freq)?; | 935 | let (_bypass, clkdiv, new_clock) = clk_div(self.ker_clk, freq.0)?; |
| 867 | 936 | ||
| 868 | // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 | 937 | // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 |
| 869 | // Section 55.5.8 | 938 | // Section 55.5.8 |
| 870 | let sdmmc_bus_bandwidth = new_clock.0 * width_u32; | 939 | let sdmmc_bus_bandwidth = new_clock.0 * width_u32; |
| 871 | assert!(ker_ck.0 > 3 * sdmmc_bus_bandwidth / 32); | 940 | assert!(self.ker_clk.0 > 3 * sdmmc_bus_bandwidth / 32); |
| 872 | self.clock = new_clock; | 941 | self.clock = new_clock; |
| 873 | 942 | ||
| 874 | // CPSMACT and DPSMACT must be 0 to set CLKDIV | 943 | // CPSMACT and DPSMACT must be 0 to set CLKDIV or WIDBUS |
| 875 | Self::wait_idle(); | 944 | self.wait_idle(); |
| 876 | regs.clkcr().modify(|w| { | 945 | regs.clkcr().modify(|w| { |
| 877 | w.set_clkdiv(clkdiv); | 946 | w.set_clkdiv(clkdiv); |
| 878 | #[cfg(sdmmc_v1)] | 947 | #[cfg(sdmmc_v1)] |
| 879 | w.set_bypass(_bypass); | 948 | w.set_bypass(_bypass); |
| 949 | w.set_widbus(widbus); | ||
| 880 | }); | 950 | }); |
| 881 | 951 | ||
| 882 | Ok(()) | 952 | Ok(()) |
| 883 | } | 953 | } |
| 884 | 954 | ||
| 955 | fn get_cid(&self) -> Result<u128, Error> { | ||
| 956 | self.cmd(common_cmd::all_send_cid(), false) // CMD2 | ||
| 957 | } | ||
| 958 | |||
| 959 | fn get_csd(&self, address: u16) -> Result<u128, Error> { | ||
| 960 | self.cmd(common_cmd::send_csd(address), false) | ||
| 961 | } | ||
| 962 | |||
| 885 | /// Query the card status (CMD13, returns R1) | 963 | /// Query the card status (CMD13, returns R1) |
| 886 | fn read_status<Ext>(&self, card: &SdmmcPeripheral) -> Result<CardStatus<Ext>, Error> | 964 | fn read_status<A: Addressable>(&self, card: &A) -> Result<CardStatus<A::Ext>, Error> |
| 887 | where | 965 | where |
| 888 | CardStatus<Ext>: From<u32>, | 966 | CardStatus<A::Ext>: From<u32>, |
| 889 | { | 967 | { |
| 890 | let regs = T::regs(); | ||
| 891 | let rca = card.get_address(); | 968 | let rca = card.get_address(); |
| 892 | 969 | ||
| 893 | Self::cmd(common_cmd::card_status(rca, false), false)?; // CMD13 | 970 | Ok(self.cmd(common_cmd::card_status(rca, false), false)?.into()) // CMD13 |
| 894 | |||
| 895 | let r1 = regs.respr(0).read().cardstatus(); | ||
| 896 | Ok(r1.into()) | ||
| 897 | } | 971 | } |
| 898 | 972 | ||
| 899 | /// Select one card and place it into the _Tranfer State_ | 973 | /// Select one card and place it into the _Tranfer State_ |
| @@ -904,17 +978,23 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 904 | // Determine Relative Card Address (RCA) of given card | 978 | // Determine Relative Card Address (RCA) of given card |
| 905 | let rca = rca.unwrap_or(0); | 979 | let rca = rca.unwrap_or(0); |
| 906 | 980 | ||
| 907 | let r = Self::cmd(common_cmd::select_card(rca), false); | 981 | let resp = self.cmd(common_cmd::select_card(rca), false); |
| 908 | match (r, rca) { | 982 | |
| 909 | (Err(Error::Timeout), 0) => Ok(()), | 983 | if let Err(Error::Timeout) = resp |
| 910 | _ => r, | 984 | && rca == 0 |
| 985 | { | ||
| 986 | return Ok(()); | ||
| 911 | } | 987 | } |
| 988 | |||
| 989 | resp?; | ||
| 990 | |||
| 991 | Ok(()) | ||
| 912 | } | 992 | } |
| 913 | 993 | ||
| 914 | /// Clear flags in interrupt clear register | 994 | /// Clear flags in interrupt clear register |
| 915 | #[inline] | 995 | #[inline] |
| 916 | fn clear_interrupt_flags() { | 996 | fn clear_interrupt_flags(&self) { |
| 917 | let regs = T::regs(); | 997 | let regs = self.info.regs; |
| 918 | regs.icr().write(|w| { | 998 | regs.icr().write(|w| { |
| 919 | w.set_ccrcfailc(true); | 999 | w.set_ccrcfailc(true); |
| 920 | w.set_dcrcfailc(true); | 1000 | w.set_dcrcfailc(true); |
| @@ -947,12 +1027,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 947 | 1027 | ||
| 948 | /// Send command to card | 1028 | /// Send command to card |
| 949 | #[allow(unused_variables)] | 1029 | #[allow(unused_variables)] |
| 950 | fn cmd<R: Resp>(cmd: Cmd<R>, data: bool) -> Result<(), Error> { | 1030 | fn cmd<R: TypedResp>(&self, cmd: Cmd<R>, data: bool) -> Result<R::Word, Error> { |
| 951 | let regs = T::regs(); | 1031 | let regs = self.info.regs; |
| 952 | 1032 | ||
| 953 | Self::clear_interrupt_flags(); | 1033 | self.clear_interrupt_flags(); |
| 954 | // CP state machine must be idle | 1034 | // CP state machine must be idle |
| 955 | while Self::cmd_active() {} | 1035 | while self.cmd_active() {} |
| 956 | 1036 | ||
| 957 | // Command arg | 1037 | // Command arg |
| 958 | regs.argr().write(|w| w.set_cmdarg(cmd.arg)); | 1038 | regs.argr().write(|w| w.set_cmdarg(cmd.arg)); |
| @@ -994,16 +1074,29 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 994 | } else if status.ccrcfail() { | 1074 | } else if status.ccrcfail() { |
| 995 | return Err(Error::Crc); | 1075 | return Err(Error::Crc); |
| 996 | } | 1076 | } |
| 997 | Ok(()) | 1077 | |
| 1078 | Ok(match R::LENGTH { | ||
| 1079 | ResponseLen::Zero => U128(0u128), | ||
| 1080 | ResponseLen::R48 => U128(self.info.regs.respr(0).read().cardstatus() as u128), | ||
| 1081 | ResponseLen::R136 => { | ||
| 1082 | let cid0 = self.info.regs.respr(0).read().cardstatus() as u128; | ||
| 1083 | let cid1 = self.info.regs.respr(1).read().cardstatus() as u128; | ||
| 1084 | let cid2 = self.info.regs.respr(2).read().cardstatus() as u128; | ||
| 1085 | let cid3 = self.info.regs.respr(3).read().cardstatus() as u128; | ||
| 1086 | |||
| 1087 | U128((cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3)) | ||
| 1088 | } | ||
| 1089 | } | ||
| 1090 | .into()) | ||
| 998 | } | 1091 | } |
| 999 | 1092 | ||
| 1000 | fn on_drop() { | 1093 | fn on_drop(&self) { |
| 1001 | let regs = T::regs(); | 1094 | let regs = self.info.regs; |
| 1002 | if Self::data_active() { | 1095 | if self.data_active() { |
| 1003 | Self::clear_interrupt_flags(); | 1096 | self.clear_interrupt_flags(); |
| 1004 | // Send abort | 1097 | // Send abort |
| 1005 | // CP state machine must be idle | 1098 | // CP state machine must be idle |
| 1006 | while Self::cmd_active() {} | 1099 | while self.cmd_active() {} |
| 1007 | 1100 | ||
| 1008 | // Command arg | 1101 | // Command arg |
| 1009 | regs.argr().write(|w| w.set_cmdarg(0)); | 1102 | regs.argr().write(|w| w.set_cmdarg(0)); |
| @@ -1023,21 +1116,22 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1023 | }); | 1116 | }); |
| 1024 | 1117 | ||
| 1025 | // Wait for the abort | 1118 | // Wait for the abort |
| 1026 | while Self::data_active() {} | 1119 | while self.data_active() {} |
| 1027 | } | 1120 | } |
| 1028 | regs.maskr().write(|_| ()); // disable irqs | 1121 | regs.maskr().write(|_| ()); // disable irqs |
| 1029 | Self::clear_interrupt_flags(); | 1122 | self.clear_interrupt_flags(); |
| 1030 | Self::stop_datapath(); | 1123 | self.stop_datapath(); |
| 1031 | } | 1124 | } |
| 1032 | 1125 | ||
| 1033 | /// Wait for a previously started datapath transfer to complete from an interrupt. | 1126 | /// Wait for a previously started datapath transfer to complete from an interrupt. |
| 1034 | #[inline] | 1127 | #[inline] |
| 1035 | async fn complete_datapath_transfer(block: bool) -> Result<(), Error> { | 1128 | #[allow(unused)] |
| 1036 | let regs = T::regs(); | 1129 | async fn complete_datapath_transfer(&self, mut transfer: WrappedTransfer<'_>, block: bool) -> Result<(), Error> { |
| 1037 | |||
| 1038 | let res = poll_fn(|cx| { | 1130 | let res = poll_fn(|cx| { |
| 1039 | T::state().register(cx.waker()); | 1131 | // Compiler might not be sufficiently constrained here |
| 1040 | let status = regs.star().read(); | 1132 | // https://github.com/embassy-rs/embassy/issues/4723 |
| 1133 | self.state.waker.register(cx.waker()); | ||
| 1134 | let status = self.info.regs.star().read(); | ||
| 1041 | 1135 | ||
| 1042 | if status.dcrcfail() { | 1136 | if status.dcrcfail() { |
| 1043 | return Poll::Ready(Err(Error::Crc)); | 1137 | return Poll::Ready(Err(Error::Crc)); |
| @@ -1052,10 +1146,13 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1052 | if status.stbiterr() { | 1146 | if status.stbiterr() { |
| 1053 | return Poll::Ready(Err(Error::StBitErr)); | 1147 | return Poll::Ready(Err(Error::StBitErr)); |
| 1054 | } | 1148 | } |
| 1149 | #[cfg(sdmmc_v1)] | ||
| 1055 | let done = match block { | 1150 | let done = match block { |
| 1056 | true => status.dbckend(), | 1151 | true => status.dbckend(), |
| 1057 | false => status.dataend(), | 1152 | false => status.dataend(), |
| 1058 | }; | 1153 | }; |
| 1154 | #[cfg(sdmmc_v2)] | ||
| 1155 | let done = status.dataend(); | ||
| 1059 | if done { | 1156 | if done { |
| 1060 | return Poll::Ready(Ok(())); | 1157 | return Poll::Ready(Ok(())); |
| 1061 | } | 1158 | } |
| @@ -1063,698 +1160,26 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1063 | }) | 1160 | }) |
| 1064 | .await; | 1161 | .await; |
| 1065 | 1162 | ||
| 1066 | Self::clear_interrupt_flags(); | 1163 | self.clear_interrupt_flags(); |
| 1067 | 1164 | self.stop_datapath(); | |
| 1068 | res | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | /// Read a data block. | ||
| 1072 | #[inline] | ||
| 1073 | pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { | ||
| 1074 | let card_capacity = self.card()?.get_capacity(); | ||
| 1075 | |||
| 1076 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 1077 | let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; | ||
| 1078 | 1165 | ||
| 1079 | // Always read 1 block of 512 bytes | 1166 | transfer.defuse(); |
| 1080 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | 1167 | drop(transfer); |
| 1081 | let address = match card_capacity { | ||
| 1082 | CardCapacity::StandardCapacity => block_idx * 512, | ||
| 1083 | _ => block_idx, | ||
| 1084 | }; | ||
| 1085 | Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 | ||
| 1086 | 1168 | ||
| 1087 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1088 | |||
| 1089 | let transfer = Self::prepare_datapath_read( | ||
| 1090 | &self.config, | ||
| 1091 | #[cfg(sdmmc_v1)] | ||
| 1092 | &mut self.dma, | ||
| 1093 | buffer, | ||
| 1094 | 512, | ||
| 1095 | 9, | ||
| 1096 | ); | ||
| 1097 | InterruptHandler::<T>::enable_interrupts(); | ||
| 1098 | Self::cmd(common_cmd::read_single_block(address), true)?; | ||
| 1099 | |||
| 1100 | let res = Self::complete_datapath_transfer(true).await; | ||
| 1101 | |||
| 1102 | if res.is_ok() { | ||
| 1103 | on_drop.defuse(); | ||
| 1104 | Self::stop_datapath(); | ||
| 1105 | drop(transfer); | ||
| 1106 | } | ||
| 1107 | res | 1169 | res |
| 1108 | } | 1170 | } |
| 1109 | 1171 | ||
| 1110 | /// Read multiple data blocks. | ||
| 1111 | #[inline] | ||
| 1112 | pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { | ||
| 1113 | let card_capacity = self.card()?.get_capacity(); | ||
| 1114 | |||
| 1115 | // NOTE(unsafe) reinterpret buffer as &mut [u32] | ||
| 1116 | let buffer = unsafe { | ||
| 1117 | let ptr = blocks.as_mut_ptr() as *mut u32; | ||
| 1118 | let len = blocks.len() * 128; | ||
| 1119 | core::slice::from_raw_parts_mut(ptr, len) | ||
| 1120 | }; | ||
| 1121 | |||
| 1122 | // Always read 1 block of 512 bytes | ||
| 1123 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 1124 | let address = match card_capacity { | ||
| 1125 | CardCapacity::StandardCapacity => block_idx * 512, | ||
| 1126 | _ => block_idx, | ||
| 1127 | }; | ||
| 1128 | Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 | ||
| 1129 | |||
| 1130 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1131 | |||
| 1132 | let transfer = Self::prepare_datapath_read( | ||
| 1133 | &self.config, | ||
| 1134 | #[cfg(sdmmc_v1)] | ||
| 1135 | &mut self.dma, | ||
| 1136 | buffer, | ||
| 1137 | 512 * blocks.len() as u32, | ||
| 1138 | 9, | ||
| 1139 | ); | ||
| 1140 | InterruptHandler::<T>::enable_interrupts(); | ||
| 1141 | |||
| 1142 | Self::cmd(common_cmd::read_multiple_blocks(address), true)?; | ||
| 1143 | |||
| 1144 | let res = Self::complete_datapath_transfer(false).await; | ||
| 1145 | |||
| 1146 | Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12 | ||
| 1147 | Self::clear_interrupt_flags(); | ||
| 1148 | |||
| 1149 | if res.is_ok() { | ||
| 1150 | on_drop.defuse(); | ||
| 1151 | Self::stop_datapath(); | ||
| 1152 | drop(transfer); | ||
| 1153 | } | ||
| 1154 | res | ||
| 1155 | } | ||
| 1156 | |||
| 1157 | /// Write a data block. | ||
| 1158 | pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { | ||
| 1159 | let card = self.card.as_mut().ok_or(Error::NoCard)?; | ||
| 1160 | |||
| 1161 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 1162 | let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; | ||
| 1163 | |||
| 1164 | // Always read 1 block of 512 bytes | ||
| 1165 | // cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 1166 | let address = match card.get_capacity() { | ||
| 1167 | CardCapacity::StandardCapacity => block_idx * 512, | ||
| 1168 | _ => block_idx, | ||
| 1169 | }; | ||
| 1170 | Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 | ||
| 1171 | |||
| 1172 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1173 | |||
| 1174 | // sdmmc_v1 uses different cmd/dma order than v2, but only for writes | ||
| 1175 | #[cfg(sdmmc_v1)] | ||
| 1176 | Self::cmd(common_cmd::write_single_block(address), true)?; | ||
| 1177 | |||
| 1178 | let transfer = self.prepare_datapath_write(buffer, 512, 9); | ||
| 1179 | InterruptHandler::<T>::enable_interrupts(); | ||
| 1180 | |||
| 1181 | #[cfg(sdmmc_v2)] | ||
| 1182 | Self::cmd(common_cmd::write_single_block(address), true)?; | ||
| 1183 | |||
| 1184 | let res = Self::complete_datapath_transfer(true).await; | ||
| 1185 | |||
| 1186 | match res { | ||
| 1187 | Ok(_) => { | ||
| 1188 | on_drop.defuse(); | ||
| 1189 | Self::stop_datapath(); | ||
| 1190 | drop(transfer); | ||
| 1191 | |||
| 1192 | // TODO: Make this configurable | ||
| 1193 | let mut timeout: u32 = 0x00FF_FFFF; | ||
| 1194 | |||
| 1195 | let card = self.card.as_ref().unwrap(); | ||
| 1196 | while timeout > 0 { | ||
| 1197 | let ready_for_data = match card { | ||
| 1198 | SdmmcPeripheral::Emmc(_) => self.read_status::<EMMC>(card)?.ready_for_data(), | ||
| 1199 | SdmmcPeripheral::SdCard(_) => self.read_status::<SD>(card)?.ready_for_data(), | ||
| 1200 | }; | ||
| 1201 | |||
| 1202 | if ready_for_data { | ||
| 1203 | return Ok(()); | ||
| 1204 | } | ||
| 1205 | timeout -= 1; | ||
| 1206 | } | ||
| 1207 | Err(Error::SoftwareTimeout) | ||
| 1208 | } | ||
| 1209 | Err(e) => Err(e), | ||
| 1210 | } | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | /// Write multiple data blocks. | ||
| 1214 | pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error> { | ||
| 1215 | let card = self.card.as_mut().ok_or(Error::NoCard)?; | ||
| 1216 | |||
| 1217 | // NOTE(unsafe) reinterpret buffer as &[u32] | ||
| 1218 | let buffer = unsafe { | ||
| 1219 | let ptr = blocks.as_ptr() as *const u32; | ||
| 1220 | let len = blocks.len() * 128; | ||
| 1221 | core::slice::from_raw_parts(ptr, len) | ||
| 1222 | }; | ||
| 1223 | |||
| 1224 | // Always read 1 block of 512 bytes | ||
| 1225 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 1226 | let address = match card.get_capacity() { | ||
| 1227 | CardCapacity::StandardCapacity => block_idx * 512, | ||
| 1228 | _ => block_idx, | ||
| 1229 | }; | ||
| 1230 | |||
| 1231 | Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 | ||
| 1232 | |||
| 1233 | let block_count = blocks.len(); | ||
| 1234 | |||
| 1235 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1236 | |||
| 1237 | #[cfg(sdmmc_v1)] | ||
| 1238 | Self::cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 | ||
| 1239 | |||
| 1240 | // Setup write command | ||
| 1241 | let transfer = self.prepare_datapath_write(buffer, 512 * block_count as u32, 9); | ||
| 1242 | InterruptHandler::<T>::enable_interrupts(); | ||
| 1243 | |||
| 1244 | #[cfg(sdmmc_v2)] | ||
| 1245 | Self::cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 | ||
| 1246 | |||
| 1247 | let res = Self::complete_datapath_transfer(false).await; | ||
| 1248 | |||
| 1249 | Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12 | ||
| 1250 | Self::clear_interrupt_flags(); | ||
| 1251 | |||
| 1252 | match res { | ||
| 1253 | Ok(_) => { | ||
| 1254 | on_drop.defuse(); | ||
| 1255 | Self::stop_datapath(); | ||
| 1256 | drop(transfer); | ||
| 1257 | |||
| 1258 | // TODO: Make this configurable | ||
| 1259 | let mut timeout: u32 = 0x00FF_FFFF; | ||
| 1260 | |||
| 1261 | // Try to read card status (ACMD13) | ||
| 1262 | while timeout > 0 { | ||
| 1263 | match self.read_sd_status().await { | ||
| 1264 | Ok(_) => return Ok(()), | ||
| 1265 | Err(Error::Timeout) => (), // Try again | ||
| 1266 | Err(e) => return Err(e), | ||
| 1267 | } | ||
| 1268 | timeout -= 1; | ||
| 1269 | } | ||
| 1270 | Err(Error::SoftwareTimeout) | ||
| 1271 | } | ||
| 1272 | Err(e) => Err(e), | ||
| 1273 | } | ||
| 1274 | } | ||
| 1275 | |||
| 1276 | /// Get a reference to the initialized card | ||
| 1277 | /// | ||
| 1278 | /// # Errors | ||
| 1279 | /// | ||
| 1280 | /// Returns Error::NoCard if [`init_sd_card`](#method.init_sd_card) or | ||
| 1281 | /// [`init_emmc`](#method.init_emmc) has not previously succeeded | ||
| 1282 | #[inline] | ||
| 1283 | pub fn card(&self) -> Result<&SdmmcPeripheral, Error> { | ||
| 1284 | self.card.as_ref().ok_or(Error::NoCard) | ||
| 1285 | } | ||
| 1286 | |||
| 1287 | /// Get the current SDMMC bus clock | 1172 | /// Get the current SDMMC bus clock |
| 1288 | pub fn clock(&self) -> Hertz { | 1173 | pub fn clock(&self) -> Hertz { |
| 1289 | self.clock | 1174 | self.clock |
| 1290 | } | 1175 | } |
| 1291 | |||
| 1292 | /// Set a specific cmd buffer rather than using the default stack allocated one. | ||
| 1293 | /// This is required if stack RAM cannot be used with DMA and usually manifests | ||
| 1294 | /// itself as an indefinite wait on a dma transfer because the dma peripheral | ||
| 1295 | /// cannot access the memory. | ||
| 1296 | pub fn set_cmd_block(&mut self, cmd_block: &'d mut CmdBlock) { | ||
| 1297 | self.cmd_block = Some(cmd_block) | ||
| 1298 | } | ||
| 1299 | |||
| 1300 | async fn init_internal(&mut self, freq: Hertz, mut card: SdmmcPeripheral) -> Result<(), Error> { | ||
| 1301 | let regs = T::regs(); | ||
| 1302 | let ker_ck = T::frequency(); | ||
| 1303 | |||
| 1304 | let bus_width = match (self.d3.is_some(), self.d7.is_some()) { | ||
| 1305 | (true, true) => { | ||
| 1306 | if matches!(card, SdmmcPeripheral::SdCard(_)) { | ||
| 1307 | return Err(Error::BusWidth); | ||
| 1308 | } | ||
| 1309 | BusWidth::Eight | ||
| 1310 | } | ||
| 1311 | (true, false) => BusWidth::Four, | ||
| 1312 | _ => BusWidth::One, | ||
| 1313 | }; | ||
| 1314 | |||
| 1315 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 1316 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 1317 | let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); | ||
| 1318 | self.clock = init_clock; | ||
| 1319 | |||
| 1320 | // CPSMACT and DPSMACT must be 0 to set WIDBUS | ||
| 1321 | Self::wait_idle(); | ||
| 1322 | |||
| 1323 | regs.clkcr().modify(|w| { | ||
| 1324 | w.set_widbus(0); | ||
| 1325 | w.set_clkdiv(clkdiv); | ||
| 1326 | #[cfg(sdmmc_v1)] | ||
| 1327 | w.set_bypass(_bypass); | ||
| 1328 | }); | ||
| 1329 | regs.dtimer() | ||
| 1330 | .write(|w| w.set_datatime(self.config.data_transfer_timeout)); | ||
| 1331 | |||
| 1332 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); | ||
| 1333 | Self::cmd(common_cmd::idle(), false)?; | ||
| 1334 | |||
| 1335 | match card { | ||
| 1336 | SdmmcPeripheral::SdCard(ref mut card) => { | ||
| 1337 | // Check if cards supports CMD8 (with pattern) | ||
| 1338 | Self::cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; | ||
| 1339 | let cic = CIC::from(regs.respr(0).read().cardstatus()); | ||
| 1340 | |||
| 1341 | if cic.pattern() != 0xAA { | ||
| 1342 | return Err(Error::UnsupportedCardVersion); | ||
| 1343 | } | ||
| 1344 | |||
| 1345 | if cic.voltage_accepted() & 1 == 0 { | ||
| 1346 | return Err(Error::UnsupportedVoltage); | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | let ocr = loop { | ||
| 1350 | // Signal that next command is a app command | ||
| 1351 | Self::cmd(common_cmd::app_cmd(0), false)?; // CMD55 | ||
| 1352 | |||
| 1353 | // 3.2-3.3V | ||
| 1354 | let voltage_window = 1 << 5; | ||
| 1355 | // Initialize card | ||
| 1356 | match Self::cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) { | ||
| 1357 | // ACMD41 | ||
| 1358 | Ok(_) => (), | ||
| 1359 | Err(Error::Crc) => (), | ||
| 1360 | Err(err) => return Err(err), | ||
| 1361 | } | ||
| 1362 | let ocr: OCR<SD> = regs.respr(0).read().cardstatus().into(); | ||
| 1363 | if !ocr.is_busy() { | ||
| 1364 | // Power up done | ||
| 1365 | break ocr; | ||
| 1366 | } | ||
| 1367 | }; | ||
| 1368 | |||
| 1369 | if ocr.high_capacity() { | ||
| 1370 | // Card is SDHC or SDXC or SDUC | ||
| 1371 | card.card_type = CardCapacity::HighCapacity; | ||
| 1372 | } else { | ||
| 1373 | card.card_type = CardCapacity::StandardCapacity; | ||
| 1374 | } | ||
| 1375 | card.ocr = ocr; | ||
| 1376 | } | ||
| 1377 | SdmmcPeripheral::Emmc(ref mut emmc) => { | ||
| 1378 | let ocr = loop { | ||
| 1379 | let high_voltage = 0b0 << 7; | ||
| 1380 | let access_mode = 0b10 << 29; | ||
| 1381 | let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15; | ||
| 1382 | // Initialize card | ||
| 1383 | match Self::cmd(emmc_cmd::send_op_cond(op_cond), false) { | ||
| 1384 | Ok(_) => (), | ||
| 1385 | Err(Error::Crc) => (), | ||
| 1386 | Err(err) => return Err(err), | ||
| 1387 | } | ||
| 1388 | let ocr: OCR<EMMC> = regs.respr(0).read().cardstatus().into(); | ||
| 1389 | if !ocr.is_busy() { | ||
| 1390 | // Power up done | ||
| 1391 | break ocr; | ||
| 1392 | } | ||
| 1393 | }; | ||
| 1394 | |||
| 1395 | emmc.capacity = if ocr.access_mode() == 0b10 { | ||
| 1396 | // Card is SDHC or SDXC or SDUC | ||
| 1397 | CardCapacity::HighCapacity | ||
| 1398 | } else { | ||
| 1399 | CardCapacity::StandardCapacity | ||
| 1400 | }; | ||
| 1401 | emmc.ocr = ocr; | ||
| 1402 | } | ||
| 1403 | } | ||
| 1404 | |||
| 1405 | Self::cmd(common_cmd::all_send_cid(), false)?; // CMD2 | ||
| 1406 | let cid0 = regs.respr(0).read().cardstatus() as u128; | ||
| 1407 | let cid1 = regs.respr(1).read().cardstatus() as u128; | ||
| 1408 | let cid2 = regs.respr(2).read().cardstatus() as u128; | ||
| 1409 | let cid3 = regs.respr(3).read().cardstatus() as u128; | ||
| 1410 | let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); | ||
| 1411 | |||
| 1412 | match card { | ||
| 1413 | SdmmcPeripheral::SdCard(ref mut card) => { | ||
| 1414 | card.cid = cid.into(); | ||
| 1415 | |||
| 1416 | Self::cmd(sd_cmd::send_relative_address(), false)?; | ||
| 1417 | let rca = RCA::<SD>::from(regs.respr(0).read().cardstatus()); | ||
| 1418 | card.rca = rca.address(); | ||
| 1419 | } | ||
| 1420 | SdmmcPeripheral::Emmc(ref mut emmc) => { | ||
| 1421 | emmc.cid = cid.into(); | ||
| 1422 | |||
| 1423 | emmc.rca = 1u16.into(); | ||
| 1424 | Self::cmd(emmc_cmd::assign_relative_address(emmc.rca), false)?; | ||
| 1425 | } | ||
| 1426 | } | ||
| 1427 | |||
| 1428 | Self::cmd(common_cmd::send_csd(card.get_address()), false)?; | ||
| 1429 | let csd0 = regs.respr(0).read().cardstatus() as u128; | ||
| 1430 | let csd1 = regs.respr(1).read().cardstatus() as u128; | ||
| 1431 | let csd2 = regs.respr(2).read().cardstatus() as u128; | ||
| 1432 | let csd3 = regs.respr(3).read().cardstatus() as u128; | ||
| 1433 | let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3); | ||
| 1434 | |||
| 1435 | self.select_card(Some(card.get_address()))?; | ||
| 1436 | |||
| 1437 | let bus_width = match card { | ||
| 1438 | SdmmcPeripheral::SdCard(ref mut card) => { | ||
| 1439 | card.csd = csd.into(); | ||
| 1440 | |||
| 1441 | self.get_scr(card).await?; | ||
| 1442 | |||
| 1443 | if !card.scr.bus_width_four() { | ||
| 1444 | BusWidth::One | ||
| 1445 | } else { | ||
| 1446 | BusWidth::Four | ||
| 1447 | } | ||
| 1448 | } | ||
| 1449 | SdmmcPeripheral::Emmc(ref mut emmc) => { | ||
| 1450 | emmc.csd = csd.into(); | ||
| 1451 | |||
| 1452 | bus_width | ||
| 1453 | } | ||
| 1454 | }; | ||
| 1455 | |||
| 1456 | // Set bus width | ||
| 1457 | let widbus = match bus_width { | ||
| 1458 | BusWidth::Eight => 2, | ||
| 1459 | BusWidth::Four => 1, | ||
| 1460 | BusWidth::One => 0, | ||
| 1461 | _ => unreachable!(), | ||
| 1462 | }; | ||
| 1463 | |||
| 1464 | match card { | ||
| 1465 | SdmmcPeripheral::SdCard(ref mut card) => { | ||
| 1466 | let acmd_arg = match bus_width { | ||
| 1467 | BusWidth::Four if card.scr.bus_width_four() => 2, | ||
| 1468 | _ => 0, | ||
| 1469 | }; | ||
| 1470 | Self::cmd(common_cmd::app_cmd(card.rca), false)?; | ||
| 1471 | Self::cmd(sd_cmd::cmd6(acmd_arg), false)?; | ||
| 1472 | } | ||
| 1473 | SdmmcPeripheral::Emmc(_) => { | ||
| 1474 | // Write bus width to ExtCSD byte 183 | ||
| 1475 | Self::cmd( | ||
| 1476 | emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus), | ||
| 1477 | false, | ||
| 1478 | )?; | ||
| 1479 | |||
| 1480 | // Wait for ready after R1b response | ||
| 1481 | loop { | ||
| 1482 | let status = self.read_status::<EMMC>(&card)?; | ||
| 1483 | |||
| 1484 | if status.ready_for_data() { | ||
| 1485 | break; | ||
| 1486 | } | ||
| 1487 | } | ||
| 1488 | } | ||
| 1489 | } | ||
| 1490 | |||
| 1491 | // CPSMACT and DPSMACT must be 0 to set WIDBUS | ||
| 1492 | Self::wait_idle(); | ||
| 1493 | |||
| 1494 | regs.clkcr().modify(|w| w.set_widbus(widbus)); | ||
| 1495 | |||
| 1496 | // Set Clock | ||
| 1497 | if freq.0 <= 25_000_000 { | ||
| 1498 | // Final clock frequency | ||
| 1499 | self.clkcr_set_clkdiv(freq.0, bus_width)?; | ||
| 1500 | } else { | ||
| 1501 | // Switch to max clock for SDR12 | ||
| 1502 | self.clkcr_set_clkdiv(25_000_000, bus_width)?; | ||
| 1503 | } | ||
| 1504 | |||
| 1505 | self.card = Some(card); | ||
| 1506 | |||
| 1507 | match card { | ||
| 1508 | SdmmcPeripheral::SdCard(_) => { | ||
| 1509 | // Read status | ||
| 1510 | self.read_sd_status().await?; | ||
| 1511 | |||
| 1512 | if freq.0 > 25_000_000 { | ||
| 1513 | // Switch to SDR25 | ||
| 1514 | self.signalling = self.switch_signalling_mode(Signalling::SDR25).await?; | ||
| 1515 | |||
| 1516 | if self.signalling == Signalling::SDR25 { | ||
| 1517 | // Set final clock frequency | ||
| 1518 | self.clkcr_set_clkdiv(freq.0, bus_width)?; | ||
| 1519 | |||
| 1520 | if self.read_status::<SD>(self.card.as_ref().unwrap())?.state() != CurrentState::Transfer { | ||
| 1521 | return Err(Error::SignalingSwitchFailed); | ||
| 1522 | } | ||
| 1523 | } | ||
| 1524 | } | ||
| 1525 | |||
| 1526 | // Read status after signalling change | ||
| 1527 | self.read_sd_status().await?; | ||
| 1528 | } | ||
| 1529 | SdmmcPeripheral::Emmc(_) => { | ||
| 1530 | self.read_ext_csd().await?; | ||
| 1531 | } | ||
| 1532 | } | ||
| 1533 | |||
| 1534 | Ok(()) | ||
| 1535 | } | ||
| 1536 | |||
| 1537 | /// Initializes card (if present) and sets the bus at the specified frequency. | ||
| 1538 | /// | ||
| 1539 | /// SD only. | ||
| 1540 | pub async fn init_sd_card(&mut self, freq: Hertz) -> Result<(), Error> { | ||
| 1541 | self.init_internal(freq, SdmmcPeripheral::SdCard(Card::default())).await | ||
| 1542 | } | ||
| 1543 | |||
| 1544 | /// Switch mode using CMD6. | ||
| 1545 | /// | ||
| 1546 | /// Attempt to set a new signalling mode. The selected | ||
| 1547 | /// signalling mode is returned. Expects the current clock | ||
| 1548 | /// frequency to be > 12.5MHz. | ||
| 1549 | /// | ||
| 1550 | /// SD only. | ||
| 1551 | async fn switch_signalling_mode(&mut self, signalling: Signalling) -> Result<Signalling, Error> { | ||
| 1552 | let _ = self.card.as_mut().ok_or(Error::NoCard)?.get_sd_card(); | ||
| 1553 | // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not | ||
| 1554 | // necessary" | ||
| 1555 | |||
| 1556 | let set_function = 0x8000_0000 | ||
| 1557 | | match signalling { | ||
| 1558 | // See PLSS v7_10 Table 4-11 | ||
| 1559 | Signalling::DDR50 => 0xFF_FF04, | ||
| 1560 | Signalling::SDR104 => 0xFF_1F03, | ||
| 1561 | Signalling::SDR50 => 0xFF_1F02, | ||
| 1562 | Signalling::SDR25 => 0xFF_FF01, | ||
| 1563 | Signalling::SDR12 => 0xFF_FF00, | ||
| 1564 | }; | ||
| 1565 | |||
| 1566 | let status = match self.cmd_block.as_deref_mut() { | ||
| 1567 | Some(x) => x, | ||
| 1568 | None => &mut CmdBlock::new(), | ||
| 1569 | }; | ||
| 1570 | |||
| 1571 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 1572 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1573 | |||
| 1574 | let transfer = Self::prepare_datapath_read( | ||
| 1575 | &self.config, | ||
| 1576 | #[cfg(sdmmc_v1)] | ||
| 1577 | &mut self.dma, | ||
| 1578 | status.as_mut(), | ||
| 1579 | 64, | ||
| 1580 | 6, | ||
| 1581 | ); | ||
| 1582 | InterruptHandler::<T>::enable_interrupts(); | ||
| 1583 | Self::cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 | ||
| 1584 | |||
| 1585 | let res = Self::complete_datapath_transfer(true).await; | ||
| 1586 | |||
| 1587 | // Host is allowed to use the new functions at least 8 | ||
| 1588 | // clocks after the end of the switch command | ||
| 1589 | // transaction. We know the current clock period is < 80ns, | ||
| 1590 | // so a total delay of 640ns is required here | ||
| 1591 | for _ in 0..300 { | ||
| 1592 | cortex_m::asm::nop(); | ||
| 1593 | } | ||
| 1594 | |||
| 1595 | match res { | ||
| 1596 | Ok(_) => { | ||
| 1597 | on_drop.defuse(); | ||
| 1598 | Self::stop_datapath(); | ||
| 1599 | drop(transfer); | ||
| 1600 | |||
| 1601 | // Function Selection of Function Group 1 | ||
| 1602 | let selection = (u32::from_be(status[4]) >> 24) & 0xF; | ||
| 1603 | |||
| 1604 | match selection { | ||
| 1605 | 0 => Ok(Signalling::SDR12), | ||
| 1606 | 1 => Ok(Signalling::SDR25), | ||
| 1607 | 2 => Ok(Signalling::SDR50), | ||
| 1608 | 3 => Ok(Signalling::SDR104), | ||
| 1609 | 4 => Ok(Signalling::DDR50), | ||
| 1610 | _ => Err(Error::UnsupportedCardType), | ||
| 1611 | } | ||
| 1612 | } | ||
| 1613 | Err(e) => Err(e), | ||
| 1614 | } | ||
| 1615 | } | ||
| 1616 | |||
| 1617 | /// Reads the SCR register. | ||
| 1618 | /// | ||
| 1619 | /// SD only. | ||
| 1620 | async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> { | ||
| 1621 | // Read the 64-bit SCR register | ||
| 1622 | Self::cmd(common_cmd::set_block_length(8), false)?; // CMD16 | ||
| 1623 | Self::cmd(common_cmd::app_cmd(card.rca), false)?; | ||
| 1624 | |||
| 1625 | let cmd_block = match self.cmd_block.as_deref_mut() { | ||
| 1626 | Some(x) => x, | ||
| 1627 | None => &mut CmdBlock::new(), | ||
| 1628 | }; | ||
| 1629 | let scr = &mut cmd_block.0[..2]; | ||
| 1630 | |||
| 1631 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 1632 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1633 | |||
| 1634 | let transfer = Self::prepare_datapath_read( | ||
| 1635 | &self.config, | ||
| 1636 | #[cfg(sdmmc_v1)] | ||
| 1637 | &mut self.dma, | ||
| 1638 | scr, | ||
| 1639 | 8, | ||
| 1640 | 3, | ||
| 1641 | ); | ||
| 1642 | InterruptHandler::<T>::enable_interrupts(); | ||
| 1643 | Self::cmd(sd_cmd::send_scr(), true)?; | ||
| 1644 | |||
| 1645 | let res = Self::complete_datapath_transfer(true).await; | ||
| 1646 | |||
| 1647 | if res.is_ok() { | ||
| 1648 | on_drop.defuse(); | ||
| 1649 | Self::stop_datapath(); | ||
| 1650 | drop(transfer); | ||
| 1651 | |||
| 1652 | unsafe { | ||
| 1653 | let scr_bytes = &*(&scr as *const _ as *const [u8; 8]); | ||
| 1654 | card.scr = SCR(u64::from_be_bytes(*scr_bytes)); | ||
| 1655 | } | ||
| 1656 | } | ||
| 1657 | res | ||
| 1658 | } | ||
| 1659 | |||
| 1660 | /// Reads the SD Status (ACMD13) | ||
| 1661 | /// | ||
| 1662 | /// SD only. | ||
| 1663 | async fn read_sd_status(&mut self) -> Result<(), Error> { | ||
| 1664 | let card = self.card.as_mut().ok_or(Error::NoCard)?.get_sd_card(); | ||
| 1665 | let rca = card.rca; | ||
| 1666 | |||
| 1667 | let cmd_block = match self.cmd_block.as_deref_mut() { | ||
| 1668 | Some(x) => x, | ||
| 1669 | None => &mut CmdBlock::new(), | ||
| 1670 | }; | ||
| 1671 | |||
| 1672 | Self::cmd(common_cmd::set_block_length(64), false)?; // CMD16 | ||
| 1673 | Self::cmd(common_cmd::app_cmd(rca), false)?; // APP | ||
| 1674 | |||
| 1675 | let status = cmd_block; | ||
| 1676 | |||
| 1677 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 1678 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1679 | |||
| 1680 | let transfer = Self::prepare_datapath_read( | ||
| 1681 | &self.config, | ||
| 1682 | #[cfg(sdmmc_v1)] | ||
| 1683 | &mut self.dma, | ||
| 1684 | status.as_mut(), | ||
| 1685 | 64, | ||
| 1686 | 6, | ||
| 1687 | ); | ||
| 1688 | InterruptHandler::<T>::enable_interrupts(); | ||
| 1689 | Self::cmd(sd_cmd::sd_status(), true)?; | ||
| 1690 | |||
| 1691 | let res = Self::complete_datapath_transfer(true).await; | ||
| 1692 | |||
| 1693 | if res.is_ok() { | ||
| 1694 | on_drop.defuse(); | ||
| 1695 | Self::stop_datapath(); | ||
| 1696 | drop(transfer); | ||
| 1697 | |||
| 1698 | for byte in status.iter_mut() { | ||
| 1699 | *byte = u32::from_be(*byte); | ||
| 1700 | } | ||
| 1701 | card.status = status.0.into(); | ||
| 1702 | } | ||
| 1703 | res | ||
| 1704 | } | ||
| 1705 | |||
| 1706 | /// Initializes eMMC and sets the bus at the specified frequency. | ||
| 1707 | /// | ||
| 1708 | /// eMMC only. | ||
| 1709 | pub async fn init_emmc(&mut self, freq: Hertz) -> Result<(), Error> { | ||
| 1710 | self.init_internal(freq, SdmmcPeripheral::Emmc(Emmc::default())).await | ||
| 1711 | } | ||
| 1712 | |||
| 1713 | /// Gets the EXT_CSD register. | ||
| 1714 | /// | ||
| 1715 | /// eMMC only. | ||
| 1716 | async fn read_ext_csd(&mut self) -> Result<(), Error> { | ||
| 1717 | let card = self.card.as_mut().ok_or(Error::NoCard)?.get_emmc(); | ||
| 1718 | |||
| 1719 | // Note: cmd_block can't be used because ExtCSD is too long to fit. | ||
| 1720 | let mut data_block = DataBlock([0u8; 512]); | ||
| 1721 | |||
| 1722 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 1723 | let buffer = unsafe { &mut *((&mut data_block.0) as *mut [u8; 512] as *mut [u32; 128]) }; | ||
| 1724 | |||
| 1725 | Self::cmd(common_cmd::set_block_length(512), false).unwrap(); // CMD16 | ||
| 1726 | |||
| 1727 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 1728 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1729 | |||
| 1730 | let transfer = Self::prepare_datapath_read( | ||
| 1731 | &self.config, | ||
| 1732 | #[cfg(sdmmc_v1)] | ||
| 1733 | &mut self.dma, | ||
| 1734 | buffer, | ||
| 1735 | 512, | ||
| 1736 | 9, | ||
| 1737 | ); | ||
| 1738 | InterruptHandler::<T>::enable_interrupts(); | ||
| 1739 | Self::cmd(emmc_cmd::send_ext_csd(), true)?; | ||
| 1740 | |||
| 1741 | let res = Self::complete_datapath_transfer(true).await; | ||
| 1742 | |||
| 1743 | if res.is_ok() { | ||
| 1744 | on_drop.defuse(); | ||
| 1745 | Self::stop_datapath(); | ||
| 1746 | drop(transfer); | ||
| 1747 | |||
| 1748 | card.ext_csd = unsafe { core::mem::transmute::<_, [u32; 128]>(data_block.0) }.into(); | ||
| 1749 | } | ||
| 1750 | res | ||
| 1751 | } | ||
| 1752 | } | 1176 | } |
| 1753 | 1177 | ||
| 1754 | impl<'d, T: Instance> Drop for Sdmmc<'d, T> { | 1178 | impl<'d> Drop for Sdmmc<'d> { |
| 1755 | fn drop(&mut self) { | 1179 | fn drop(&mut self) { |
| 1756 | T::Interrupt::disable(); | 1180 | // T::Interrupt::disable(); |
| 1757 | Self::on_drop(); | 1181 | self.on_drop(); |
| 1182 | self.info.rcc.disable_without_stop(); | ||
| 1758 | 1183 | ||
| 1759 | critical_section::with(|_| { | 1184 | critical_section::with(|_| { |
| 1760 | self.clk.set_as_disconnected(); | 1185 | self.clk.set_as_disconnected(); |
| @@ -1787,9 +1212,28 @@ impl<'d, T: Instance> Drop for Sdmmc<'d, T> { | |||
| 1787 | 1212 | ||
| 1788 | ////////////////////////////////////////////////////// | 1213 | ////////////////////////////////////////////////////// |
| 1789 | 1214 | ||
| 1215 | type Regs = RegBlock; | ||
| 1216 | |||
| 1217 | struct Info { | ||
| 1218 | regs: Regs, | ||
| 1219 | rcc: RccInfo, | ||
| 1220 | } | ||
| 1221 | |||
| 1222 | struct State { | ||
| 1223 | waker: AtomicWaker, | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | impl State { | ||
| 1227 | const fn new() -> Self { | ||
| 1228 | Self { | ||
| 1229 | waker: AtomicWaker::new(), | ||
| 1230 | } | ||
| 1231 | } | ||
| 1232 | } | ||
| 1233 | |||
| 1790 | trait SealedInstance { | 1234 | trait SealedInstance { |
| 1791 | fn regs() -> RegBlock; | 1235 | fn info() -> &'static Info; |
| 1792 | fn state() -> &'static AtomicWaker; | 1236 | fn state() -> &'static State; |
| 1793 | } | 1237 | } |
| 1794 | 1238 | ||
| 1795 | /// SDMMC instance trait. | 1239 | /// SDMMC instance trait. |
| @@ -1816,13 +1260,17 @@ dma_trait!(SdmmcDma, Instance); | |||
| 1816 | foreach_peripheral!( | 1260 | foreach_peripheral!( |
| 1817 | (sdmmc, $inst:ident) => { | 1261 | (sdmmc, $inst:ident) => { |
| 1818 | impl SealedInstance for peripherals::$inst { | 1262 | impl SealedInstance for peripherals::$inst { |
| 1819 | fn regs() -> RegBlock { | 1263 | fn info() -> &'static Info { |
| 1820 | crate::pac::$inst | 1264 | static INFO: Info = Info { |
| 1265 | regs: unsafe { Regs::from_ptr(crate::pac::$inst.as_ptr()) }, | ||
| 1266 | rcc: crate::peripherals::$inst::RCC_INFO, | ||
| 1267 | }; | ||
| 1268 | &INFO | ||
| 1821 | } | 1269 | } |
| 1822 | 1270 | ||
| 1823 | fn state() -> &'static ::embassy_sync::waitqueue::AtomicWaker { | 1271 | fn state() -> &'static State { |
| 1824 | static WAKER: ::embassy_sync::waitqueue::AtomicWaker = ::embassy_sync::waitqueue::AtomicWaker::new(); | 1272 | static STATE: State = State::new(); |
| 1825 | &WAKER | 1273 | &STATE |
| 1826 | } | 1274 | } |
| 1827 | } | 1275 | } |
| 1828 | 1276 | ||
| @@ -1831,46 +1279,3 @@ foreach_peripheral!( | |||
| 1831 | } | 1279 | } |
| 1832 | }; | 1280 | }; |
| 1833 | ); | 1281 | ); |
| 1834 | |||
| 1835 | impl<'d, T: Instance> block_device_driver::BlockDevice<512> for Sdmmc<'d, T> { | ||
| 1836 | type Error = Error; | ||
| 1837 | type Align = aligned::A4; | ||
| 1838 | |||
| 1839 | async fn read( | ||
| 1840 | &mut self, | ||
| 1841 | block_address: u32, | ||
| 1842 | buf: &mut [aligned::Aligned<Self::Align, [u8; 512]>], | ||
| 1843 | ) -> Result<(), Self::Error> { | ||
| 1844 | // TODO: I think block_address needs to be adjusted by the partition start offset | ||
| 1845 | if buf.len() == 1 { | ||
| 1846 | let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut crate::sdmmc::DataBlock) }; | ||
| 1847 | self.read_block(block_address, block).await?; | ||
| 1848 | } else { | ||
| 1849 | let blocks: &mut [DataBlock] = | ||
| 1850 | unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut DataBlock, buf.len()) }; | ||
| 1851 | self.read_blocks(block_address, blocks).await?; | ||
| 1852 | } | ||
| 1853 | Ok(()) | ||
| 1854 | } | ||
| 1855 | |||
| 1856 | async fn write( | ||
| 1857 | &mut self, | ||
| 1858 | block_address: u32, | ||
| 1859 | buf: &[aligned::Aligned<Self::Align, [u8; 512]>], | ||
| 1860 | ) -> Result<(), Self::Error> { | ||
| 1861 | // TODO: I think block_address needs to be adjusted by the partition start offset | ||
| 1862 | if buf.len() == 1 { | ||
| 1863 | let block = unsafe { &*(&buf[0] as *const _ as *const crate::sdmmc::DataBlock) }; | ||
| 1864 | self.write_block(block_address, block).await?; | ||
| 1865 | } else { | ||
| 1866 | let blocks: &[DataBlock] = | ||
| 1867 | unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const DataBlock, buf.len()) }; | ||
| 1868 | self.write_blocks(block_address, blocks).await?; | ||
| 1869 | } | ||
| 1870 | Ok(()) | ||
| 1871 | } | ||
| 1872 | |||
| 1873 | async fn size(&mut self) -> Result<u64, Self::Error> { | ||
| 1874 | Ok(self.card()?.size()) | ||
| 1875 | } | ||
| 1876 | } | ||
diff --git a/embassy-stm32/src/sdmmc/sd.rs b/embassy-stm32/src/sdmmc/sd.rs new file mode 100644 index 000000000..20318bbfa --- /dev/null +++ b/embassy-stm32/src/sdmmc/sd.rs | |||
| @@ -0,0 +1,699 @@ | |||
| 1 | use core::default::Default; | ||
| 2 | use core::ops::{Deref, DerefMut}; | ||
| 3 | |||
| 4 | use sdio_host::emmc::{EMMC, ExtCSD}; | ||
| 5 | use sdio_host::sd::{BusWidth, CIC, CID, CSD, CardCapacity, CardStatus, CurrentState, OCR, RCA, SCR, SD, SDStatus}; | ||
| 6 | use sdio_host::{common_cmd, emmc_cmd, sd_cmd}; | ||
| 7 | |||
| 8 | use crate::sdmmc::{ | ||
| 9 | BlockSize, DatapathMode, Error, Sdmmc, Signalling, aligned_mut, aligned_ref, block_size, bus_width_vals, | ||
| 10 | slice8_mut, slice8_ref, | ||
| 11 | }; | ||
| 12 | use crate::time::{Hertz, mhz}; | ||
| 13 | |||
| 14 | /// Aligned data block for SDMMC transfers. | ||
| 15 | /// | ||
| 16 | /// This is a 512-byte array, aligned to 4 bytes to satisfy DMA requirements. | ||
| 17 | #[repr(align(4))] | ||
| 18 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 19 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 20 | pub struct DataBlock(pub [u32; 128]); | ||
| 21 | |||
| 22 | impl DataBlock { | ||
| 23 | /// Create a new DataBlock | ||
| 24 | pub const fn new() -> Self { | ||
| 25 | DataBlock([0u32; 128]) | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | impl Deref for DataBlock { | ||
| 30 | type Target = [u8; 512]; | ||
| 31 | |||
| 32 | fn deref(&self) -> &Self::Target { | ||
| 33 | unwrap!(slice8_ref(&self.0[..]).try_into()) | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | impl DerefMut for DataBlock { | ||
| 38 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 39 | unwrap!(slice8_mut(&mut self.0[..]).try_into()) | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | /// Command Block buffer for SDMMC command transfers. | ||
| 44 | /// | ||
| 45 | /// This is a 16-word array, exposed so that DMA commpatible memory can be used if required. | ||
| 46 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 47 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 48 | pub struct CmdBlock(pub [u32; 16]); | ||
| 49 | |||
| 50 | impl CmdBlock { | ||
| 51 | /// Creates a new instance of CmdBlock | ||
| 52 | pub const fn new() -> Self { | ||
| 53 | Self([0u32; 16]) | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | impl Deref for CmdBlock { | ||
| 58 | type Target = [u32; 16]; | ||
| 59 | |||
| 60 | fn deref(&self) -> &Self::Target { | ||
| 61 | &self.0 | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | impl DerefMut for CmdBlock { | ||
| 66 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 67 | &mut self.0 | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Represents either an SD or EMMC card | ||
| 72 | pub trait Addressable: Sized + Clone { | ||
| 73 | /// Associated type | ||
| 74 | type Ext; | ||
| 75 | |||
| 76 | /// Get this peripheral's address on the SDMMC bus | ||
| 77 | fn get_address(&self) -> u16; | ||
| 78 | |||
| 79 | /// Is this a standard or high capacity peripheral? | ||
| 80 | fn get_capacity(&self) -> CardCapacity; | ||
| 81 | |||
| 82 | /// Size in bytes | ||
| 83 | fn size(&self) -> u64; | ||
| 84 | } | ||
| 85 | |||
| 86 | /// Storage Device | ||
| 87 | pub struct StorageDevice<'a, 'b, T: Addressable> { | ||
| 88 | info: T, | ||
| 89 | /// Inner member | ||
| 90 | pub sdmmc: &'a mut Sdmmc<'b>, | ||
| 91 | } | ||
| 92 | |||
| 93 | /// Card Storage Device | ||
| 94 | impl<'a, 'b> StorageDevice<'a, 'b, Card> { | ||
| 95 | /// Create a new SD card | ||
| 96 | pub async fn new_sd_card(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<Self, Error> { | ||
| 97 | let mut s = Self { | ||
| 98 | info: Card::default(), | ||
| 99 | sdmmc, | ||
| 100 | }; | ||
| 101 | |||
| 102 | s.acquire(cmd_block, freq).await?; | ||
| 103 | |||
| 104 | Ok(s) | ||
| 105 | } | ||
| 106 | |||
| 107 | /// Initializes the card into a known state (or at least tries to). | ||
| 108 | async fn acquire(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> { | ||
| 109 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 110 | let regs = self.sdmmc.info.regs; | ||
| 111 | |||
| 112 | let _bus_width = match self.sdmmc.bus_width() { | ||
| 113 | BusWidth::Eight => return Err(Error::BusWidth), | ||
| 114 | bus_width => bus_width, | ||
| 115 | }; | ||
| 116 | |||
| 117 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 118 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 119 | self.sdmmc.init_idle()?; | ||
| 120 | |||
| 121 | // Check if cards supports CMD8 (with pattern) | ||
| 122 | self.sdmmc.cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; | ||
| 123 | let cic = CIC::from(regs.respr(0).read().cardstatus()); | ||
| 124 | |||
| 125 | if cic.pattern() != 0xAA { | ||
| 126 | return Err(Error::UnsupportedCardVersion); | ||
| 127 | } | ||
| 128 | |||
| 129 | if cic.voltage_accepted() & 1 == 0 { | ||
| 130 | return Err(Error::UnsupportedVoltage); | ||
| 131 | } | ||
| 132 | |||
| 133 | let ocr = loop { | ||
| 134 | // Signal that next command is a app command | ||
| 135 | self.sdmmc.cmd(common_cmd::app_cmd(0), false)?; // CMD55 | ||
| 136 | |||
| 137 | // 3.2-3.3V | ||
| 138 | let voltage_window = 1 << 5; | ||
| 139 | // Initialize card | ||
| 140 | match self | ||
| 141 | .sdmmc | ||
| 142 | .cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) | ||
| 143 | { | ||
| 144 | // ACMD41 | ||
| 145 | Ok(_) => (), | ||
| 146 | Err(Error::Crc) => (), | ||
| 147 | Err(err) => return Err(err), | ||
| 148 | } | ||
| 149 | |||
| 150 | let ocr: OCR<SD> = regs.respr(0).read().cardstatus().into(); | ||
| 151 | if !ocr.is_busy() { | ||
| 152 | // Power up done | ||
| 153 | break ocr; | ||
| 154 | } | ||
| 155 | }; | ||
| 156 | |||
| 157 | if ocr.high_capacity() { | ||
| 158 | // Card is SDHC or SDXC or SDUC | ||
| 159 | self.info.card_type = CardCapacity::HighCapacity; | ||
| 160 | } else { | ||
| 161 | self.info.card_type = CardCapacity::StandardCapacity; | ||
| 162 | } | ||
| 163 | self.info.ocr = ocr; | ||
| 164 | |||
| 165 | self.info.cid = self.sdmmc.get_cid()?.into(); | ||
| 166 | |||
| 167 | self.sdmmc.cmd(sd_cmd::send_relative_address(), false)?; | ||
| 168 | let rca = RCA::<SD>::from(regs.respr(0).read().cardstatus()); | ||
| 169 | self.info.rca = rca.address(); | ||
| 170 | |||
| 171 | self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into(); | ||
| 172 | self.sdmmc.select_card(Some(self.info.get_address()))?; | ||
| 173 | |||
| 174 | self.info.scr = self.get_scr(cmd_block).await?; | ||
| 175 | |||
| 176 | let (bus_width, acmd_arg) = if !self.info.scr.bus_width_four() { | ||
| 177 | (BusWidth::One, 0) | ||
| 178 | } else { | ||
| 179 | (BusWidth::Four, 2) | ||
| 180 | }; | ||
| 181 | |||
| 182 | self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), false)?; | ||
| 183 | self.sdmmc.cmd(sd_cmd::cmd6(acmd_arg), false)?; | ||
| 184 | |||
| 185 | self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?; | ||
| 186 | |||
| 187 | // Read status | ||
| 188 | self.info.status = self.read_sd_status(cmd_block).await?; | ||
| 189 | |||
| 190 | if freq > mhz(25) { | ||
| 191 | // Switch to SDR25 | ||
| 192 | self.sdmmc.signalling = self.switch_signalling_mode(cmd_block, Signalling::SDR25).await?; | ||
| 193 | |||
| 194 | if self.sdmmc.signalling == Signalling::SDR25 { | ||
| 195 | // Set final clock frequency | ||
| 196 | self.sdmmc.clkcr_set_clkdiv(freq, bus_width)?; | ||
| 197 | |||
| 198 | if self.sdmmc.read_status(&self.info)?.state() != CurrentState::Transfer { | ||
| 199 | return Err(Error::SignalingSwitchFailed); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | // Read status after signalling change | ||
| 204 | self.read_sd_status(cmd_block).await?; | ||
| 205 | } | ||
| 206 | |||
| 207 | Ok(()) | ||
| 208 | } | ||
| 209 | |||
| 210 | /// Switch mode using CMD6. | ||
| 211 | /// | ||
| 212 | /// Attempt to set a new signalling mode. The selected | ||
| 213 | /// signalling mode is returned. Expects the current clock | ||
| 214 | /// frequency to be > 12.5MHz. | ||
| 215 | /// | ||
| 216 | /// SD only. | ||
| 217 | async fn switch_signalling_mode( | ||
| 218 | &self, | ||
| 219 | cmd_block: &mut CmdBlock, | ||
| 220 | signalling: Signalling, | ||
| 221 | ) -> Result<Signalling, Error> { | ||
| 222 | // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not | ||
| 223 | // necessary" | ||
| 224 | |||
| 225 | let set_function = 0x8000_0000 | ||
| 226 | | match signalling { | ||
| 227 | // See PLSS v7_10 Table 4-11 | ||
| 228 | Signalling::DDR50 => 0xFF_FF04, | ||
| 229 | Signalling::SDR104 => 0xFF_1F03, | ||
| 230 | Signalling::SDR50 => 0xFF_1F02, | ||
| 231 | Signalling::SDR25 => 0xFF_FF01, | ||
| 232 | Signalling::SDR12 => 0xFF_FF00, | ||
| 233 | }; | ||
| 234 | |||
| 235 | let buffer = &mut cmd_block.0[..64 / 4]; | ||
| 236 | let mode = DatapathMode::Block(block_size(size_of_val(buffer))); | ||
| 237 | let transfer = self.sdmmc.prepare_datapath_read(aligned_mut(buffer), mode); | ||
| 238 | |||
| 239 | self.sdmmc.cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 | ||
| 240 | |||
| 241 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 242 | |||
| 243 | // Host is allowed to use the new functions at least 8 | ||
| 244 | // clocks after the end of the switch command | ||
| 245 | // transaction. We know the current clock period is < 80ns, | ||
| 246 | // so a total delay of 640ns is required here | ||
| 247 | for _ in 0..300 { | ||
| 248 | cortex_m::asm::nop(); | ||
| 249 | } | ||
| 250 | |||
| 251 | // Function Selection of Function Group 1 | ||
| 252 | let selection = (u32::from_be(cmd_block[4]) >> 24) & 0xF; | ||
| 253 | |||
| 254 | match selection { | ||
| 255 | 0 => Ok(Signalling::SDR12), | ||
| 256 | 1 => Ok(Signalling::SDR25), | ||
| 257 | 2 => Ok(Signalling::SDR50), | ||
| 258 | 3 => Ok(Signalling::SDR104), | ||
| 259 | 4 => Ok(Signalling::DDR50), | ||
| 260 | _ => Err(Error::UnsupportedCardType), | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 264 | /// Reads the SCR register. | ||
| 265 | /// | ||
| 266 | /// SD only. | ||
| 267 | async fn get_scr(&self, cmd_block: &mut CmdBlock) -> Result<SCR, Error> { | ||
| 268 | // Read the 64-bit SCR register | ||
| 269 | self.sdmmc.cmd(common_cmd::set_block_length(8), false)?; // CMD16 | ||
| 270 | self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), false)?; | ||
| 271 | |||
| 272 | let scr = &mut cmd_block.0[..2]; | ||
| 273 | |||
| 274 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 275 | |||
| 276 | let transfer = self | ||
| 277 | .sdmmc | ||
| 278 | .prepare_datapath_read(aligned_mut(scr), DatapathMode::Block(BlockSize::Size8)); | ||
| 279 | self.sdmmc.cmd(sd_cmd::send_scr(), true)?; | ||
| 280 | |||
| 281 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 282 | |||
| 283 | Ok(SCR(u64::from_be_bytes(unwrap!(slice8_mut(scr).try_into())))) | ||
| 284 | } | ||
| 285 | |||
| 286 | /// Reads the SD Status (ACMD13) | ||
| 287 | /// | ||
| 288 | /// SD only. | ||
| 289 | async fn read_sd_status(&self, cmd_block: &mut CmdBlock) -> Result<SDStatus, Error> { | ||
| 290 | let rca = self.info.rca; | ||
| 291 | |||
| 292 | self.sdmmc.cmd(common_cmd::set_block_length(64), false)?; // CMD16 | ||
| 293 | self.sdmmc.cmd(common_cmd::app_cmd(rca), false)?; // APP | ||
| 294 | |||
| 295 | let buffer = &mut cmd_block.as_mut()[..64 / 4]; | ||
| 296 | let mode = DatapathMode::Block(block_size(size_of_val(buffer))); | ||
| 297 | |||
| 298 | let transfer = self.sdmmc.prepare_datapath_read(aligned_mut(buffer), mode); | ||
| 299 | self.sdmmc.cmd(sd_cmd::sd_status(), true)?; | ||
| 300 | |||
| 301 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 302 | |||
| 303 | for byte in cmd_block.iter_mut() { | ||
| 304 | *byte = u32::from_be(*byte); | ||
| 305 | } | ||
| 306 | |||
| 307 | Ok(cmd_block.0.into()) | ||
| 308 | } | ||
| 309 | } | ||
| 310 | |||
| 311 | /// Emmc storage device | ||
| 312 | impl<'a, 'b> StorageDevice<'a, 'b, Emmc> { | ||
| 313 | /// Create a new EMMC card | ||
| 314 | pub async fn new_emmc(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<Self, Error> { | ||
| 315 | let mut s = Self { | ||
| 316 | info: Emmc::default(), | ||
| 317 | sdmmc, | ||
| 318 | }; | ||
| 319 | |||
| 320 | s.acquire(cmd_block, freq).await?; | ||
| 321 | |||
| 322 | Ok(s) | ||
| 323 | } | ||
| 324 | |||
| 325 | async fn acquire(&mut self, _cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> { | ||
| 326 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 327 | let regs = self.sdmmc.info.regs; | ||
| 328 | |||
| 329 | let bus_width = self.sdmmc.bus_width(); | ||
| 330 | |||
| 331 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 332 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 333 | self.sdmmc.init_idle()?; | ||
| 334 | |||
| 335 | let ocr = loop { | ||
| 336 | let high_voltage = 0b0 << 7; | ||
| 337 | let access_mode = 0b10 << 29; | ||
| 338 | let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15; | ||
| 339 | // Initialize card | ||
| 340 | match self.sdmmc.cmd(emmc_cmd::send_op_cond(op_cond), false) { | ||
| 341 | Ok(_) => (), | ||
| 342 | Err(Error::Crc) => (), | ||
| 343 | Err(err) => return Err(err), | ||
| 344 | } | ||
| 345 | let ocr: OCR<EMMC> = regs.respr(0).read().cardstatus().into(); | ||
| 346 | if !ocr.is_busy() { | ||
| 347 | // Power up done | ||
| 348 | break ocr; | ||
| 349 | } | ||
| 350 | }; | ||
| 351 | |||
| 352 | self.info.capacity = if ocr.access_mode() == 0b10 { | ||
| 353 | // Card is SDHC or SDXC or SDUC | ||
| 354 | CardCapacity::HighCapacity | ||
| 355 | } else { | ||
| 356 | CardCapacity::StandardCapacity | ||
| 357 | }; | ||
| 358 | self.info.ocr = ocr; | ||
| 359 | |||
| 360 | self.info.cid = self.sdmmc.get_cid()?.into(); | ||
| 361 | |||
| 362 | self.info.rca = 1u16.into(); | ||
| 363 | self.sdmmc | ||
| 364 | .cmd(emmc_cmd::assign_relative_address(self.info.rca), false)?; | ||
| 365 | |||
| 366 | self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into(); | ||
| 367 | self.sdmmc.select_card(Some(self.info.get_address()))?; | ||
| 368 | |||
| 369 | let (widbus, _) = bus_width_vals(bus_width); | ||
| 370 | |||
| 371 | // Write bus width to ExtCSD byte 183 | ||
| 372 | self.sdmmc.cmd( | ||
| 373 | emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus), | ||
| 374 | false, | ||
| 375 | )?; | ||
| 376 | |||
| 377 | // Wait for ready after R1b response | ||
| 378 | loop { | ||
| 379 | let status = self.sdmmc.read_status(&self.info)?; | ||
| 380 | |||
| 381 | if status.ready_for_data() { | ||
| 382 | break; | ||
| 383 | } | ||
| 384 | } | ||
| 385 | |||
| 386 | self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?; | ||
| 387 | self.info.ext_csd = self.read_ext_csd().await?; | ||
| 388 | |||
| 389 | Ok(()) | ||
| 390 | } | ||
| 391 | |||
| 392 | /// Gets the EXT_CSD register. | ||
| 393 | /// | ||
| 394 | /// eMMC only. | ||
| 395 | async fn read_ext_csd(&self) -> Result<ExtCSD, Error> { | ||
| 396 | // Note: cmd_block can't be used because ExtCSD is too long to fit. | ||
| 397 | let mut data_block = DataBlock::new(); | ||
| 398 | |||
| 399 | self.sdmmc | ||
| 400 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false) | ||
| 401 | .unwrap(); // CMD16 | ||
| 402 | |||
| 403 | let transfer = self.sdmmc.prepare_datapath_read( | ||
| 404 | aligned_mut(&mut data_block.0), | ||
| 405 | DatapathMode::Block(block_size(size_of::<DataBlock>())), | ||
| 406 | ); | ||
| 407 | self.sdmmc.cmd(emmc_cmd::send_ext_csd(), true)?; | ||
| 408 | |||
| 409 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 410 | |||
| 411 | Ok(data_block.0.into()) | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | /// Card or Emmc storage device | ||
| 416 | impl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> { | ||
| 417 | /// Write a block | ||
| 418 | pub fn card(&self) -> A { | ||
| 419 | self.info.clone() | ||
| 420 | } | ||
| 421 | |||
| 422 | /// Read a data block. | ||
| 423 | #[inline] | ||
| 424 | pub async fn read_block(&mut self, block_idx: u32, data_block: &mut DataBlock) -> Result<(), Error> { | ||
| 425 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 426 | let card_capacity = self.info.get_capacity(); | ||
| 427 | |||
| 428 | // Always read 1 block of 512 bytes | ||
| 429 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 430 | let address = match card_capacity { | ||
| 431 | CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32, | ||
| 432 | _ => block_idx, | ||
| 433 | }; | ||
| 434 | self.sdmmc | ||
| 435 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16 | ||
| 436 | |||
| 437 | let transfer = self.sdmmc.prepare_datapath_read( | ||
| 438 | aligned_mut(&mut data_block.0), | ||
| 439 | DatapathMode::Block(block_size(size_of::<DataBlock>())), | ||
| 440 | ); | ||
| 441 | self.sdmmc.cmd(common_cmd::read_single_block(address), true)?; | ||
| 442 | |||
| 443 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 444 | |||
| 445 | Ok(()) | ||
| 446 | } | ||
| 447 | |||
| 448 | /// Read multiple data blocks. | ||
| 449 | #[inline] | ||
| 450 | pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { | ||
| 451 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 452 | let card_capacity = self.info.get_capacity(); | ||
| 453 | |||
| 454 | // NOTE(unsafe) reinterpret buffer as &mut [u32] | ||
| 455 | let buffer = unsafe { | ||
| 456 | core::slice::from_raw_parts_mut( | ||
| 457 | blocks.as_mut_ptr() as *mut u32, | ||
| 458 | blocks.len() * size_of::<DataBlock>() / size_of::<u32>(), | ||
| 459 | ) | ||
| 460 | }; | ||
| 461 | |||
| 462 | // Always read 1 block of 512 bytes | ||
| 463 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 464 | let address = match card_capacity { | ||
| 465 | CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32, | ||
| 466 | _ => block_idx, | ||
| 467 | }; | ||
| 468 | self.sdmmc | ||
| 469 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16 | ||
| 470 | |||
| 471 | let transfer = self.sdmmc.prepare_datapath_read( | ||
| 472 | aligned_mut(buffer), | ||
| 473 | DatapathMode::Block(block_size(size_of::<DataBlock>())), | ||
| 474 | ); | ||
| 475 | self.sdmmc.cmd(common_cmd::read_multiple_blocks(address), true)?; | ||
| 476 | |||
| 477 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 478 | |||
| 479 | self.sdmmc.cmd(common_cmd::stop_transmission(), false)?; // CMD12 | ||
| 480 | self.sdmmc.clear_interrupt_flags(); | ||
| 481 | |||
| 482 | Ok(()) | ||
| 483 | } | ||
| 484 | |||
| 485 | /// Write a data block. | ||
| 486 | pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> | ||
| 487 | where | ||
| 488 | CardStatus<A::Ext>: From<u32>, | ||
| 489 | { | ||
| 490 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 491 | |||
| 492 | // Always read 1 block of 512 bytes | ||
| 493 | // cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 494 | let address = match self.info.get_capacity() { | ||
| 495 | CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32, | ||
| 496 | _ => block_idx, | ||
| 497 | }; | ||
| 498 | self.sdmmc | ||
| 499 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16 | ||
| 500 | |||
| 501 | // sdmmc_v1 uses different cmd/dma order than v2, but only for writes | ||
| 502 | #[cfg(sdmmc_v1)] | ||
| 503 | self.sdmmc.cmd(common_cmd::write_single_block(address), true)?; | ||
| 504 | |||
| 505 | let transfer = self.sdmmc.prepare_datapath_write( | ||
| 506 | aligned_ref(&buffer.0), | ||
| 507 | DatapathMode::Block(block_size(size_of::<DataBlock>())), | ||
| 508 | ); | ||
| 509 | |||
| 510 | #[cfg(sdmmc_v2)] | ||
| 511 | self.sdmmc.cmd(common_cmd::write_single_block(address), true)?; | ||
| 512 | |||
| 513 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 514 | |||
| 515 | // TODO: Make this configurable | ||
| 516 | let mut timeout: u32 = 0x00FF_FFFF; | ||
| 517 | |||
| 518 | while timeout > 0 { | ||
| 519 | let ready_for_data = self.sdmmc.read_status(&self.info)?.ready_for_data(); | ||
| 520 | if ready_for_data { | ||
| 521 | return Ok(()); | ||
| 522 | } | ||
| 523 | timeout -= 1; | ||
| 524 | } | ||
| 525 | |||
| 526 | Err(Error::SoftwareTimeout) | ||
| 527 | } | ||
| 528 | |||
| 529 | /// Write multiple data blocks. | ||
| 530 | pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error> | ||
| 531 | where | ||
| 532 | CardStatus<A::Ext>: From<u32>, | ||
| 533 | { | ||
| 534 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 535 | |||
| 536 | // NOTE(unsafe) reinterpret buffer as &[u32] | ||
| 537 | let buffer = unsafe { | ||
| 538 | core::slice::from_raw_parts( | ||
| 539 | blocks.as_ptr() as *const u32, | ||
| 540 | blocks.len() * size_of::<DataBlock>() / size_of::<u32>(), | ||
| 541 | ) | ||
| 542 | }; | ||
| 543 | // Always read 1 block of 512 bytes | ||
| 544 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 545 | let address = match self.info.get_capacity() { | ||
| 546 | CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32, | ||
| 547 | _ => block_idx, | ||
| 548 | }; | ||
| 549 | |||
| 550 | self.sdmmc | ||
| 551 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16 | ||
| 552 | |||
| 553 | #[cfg(sdmmc_v1)] | ||
| 554 | self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 | ||
| 555 | |||
| 556 | // Setup write command | ||
| 557 | let transfer = self.sdmmc.prepare_datapath_write( | ||
| 558 | aligned_ref(buffer), | ||
| 559 | DatapathMode::Block(block_size(size_of::<DataBlock>())), | ||
| 560 | ); | ||
| 561 | #[cfg(sdmmc_v2)] | ||
| 562 | self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 | ||
| 563 | |||
| 564 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 565 | |||
| 566 | self.sdmmc.cmd(common_cmd::stop_transmission(), false)?; // CMD12 | ||
| 567 | self.sdmmc.clear_interrupt_flags(); | ||
| 568 | |||
| 569 | // TODO: Make this configurable | ||
| 570 | let mut timeout: u32 = 0x00FF_FFFF; | ||
| 571 | |||
| 572 | while timeout > 0 { | ||
| 573 | let ready_for_data = self.sdmmc.read_status(&self.info)?.ready_for_data(); | ||
| 574 | |||
| 575 | if ready_for_data { | ||
| 576 | return Ok(()); | ||
| 577 | } | ||
| 578 | timeout -= 1; | ||
| 579 | } | ||
| 580 | Err(Error::SoftwareTimeout) | ||
| 581 | } | ||
| 582 | } | ||
| 583 | |||
| 584 | #[derive(Clone, Copy, Debug, Default)] | ||
| 585 | /// SD Card | ||
| 586 | pub struct Card { | ||
| 587 | /// The type of this card | ||
| 588 | pub card_type: CardCapacity, | ||
| 589 | /// Operation Conditions Register | ||
| 590 | pub ocr: OCR<SD>, | ||
| 591 | /// Relative Card Address | ||
| 592 | pub rca: u16, | ||
| 593 | /// Card ID | ||
| 594 | pub cid: CID<SD>, | ||
| 595 | /// Card Specific Data | ||
| 596 | pub csd: CSD<SD>, | ||
| 597 | /// SD CARD Configuration Register | ||
| 598 | pub scr: SCR, | ||
| 599 | /// SD Status | ||
| 600 | pub status: SDStatus, | ||
| 601 | } | ||
| 602 | |||
| 603 | impl Addressable for Card { | ||
| 604 | type Ext = SD; | ||
| 605 | |||
| 606 | /// Get this peripheral's address on the SDMMC bus | ||
| 607 | fn get_address(&self) -> u16 { | ||
| 608 | self.rca | ||
| 609 | } | ||
| 610 | |||
| 611 | /// Is this a standard or high capacity peripheral? | ||
| 612 | fn get_capacity(&self) -> CardCapacity { | ||
| 613 | self.card_type | ||
| 614 | } | ||
| 615 | |||
| 616 | /// Size in bytes | ||
| 617 | fn size(&self) -> u64 { | ||
| 618 | u64::from(self.csd.block_count()) * 512 | ||
| 619 | } | ||
| 620 | } | ||
| 621 | |||
| 622 | #[derive(Clone, Copy, Debug, Default)] | ||
| 623 | /// eMMC storage | ||
| 624 | pub struct Emmc { | ||
| 625 | /// The capacity of this card | ||
| 626 | pub capacity: CardCapacity, | ||
| 627 | /// Operation Conditions Register | ||
| 628 | pub ocr: OCR<EMMC>, | ||
| 629 | /// Relative Card Address | ||
| 630 | pub rca: u16, | ||
| 631 | /// Card ID | ||
| 632 | pub cid: CID<EMMC>, | ||
| 633 | /// Card Specific Data | ||
| 634 | pub csd: CSD<EMMC>, | ||
| 635 | /// Extended Card Specific Data | ||
| 636 | pub ext_csd: ExtCSD, | ||
| 637 | } | ||
| 638 | |||
| 639 | impl Addressable for Emmc { | ||
| 640 | type Ext = EMMC; | ||
| 641 | |||
| 642 | /// Get this peripheral's address on the SDMMC bus | ||
| 643 | fn get_address(&self) -> u16 { | ||
| 644 | self.rca | ||
| 645 | } | ||
| 646 | |||
| 647 | /// Is this a standard or high capacity peripheral? | ||
| 648 | fn get_capacity(&self) -> CardCapacity { | ||
| 649 | self.capacity | ||
| 650 | } | ||
| 651 | |||
| 652 | /// Size in bytes | ||
| 653 | fn size(&self) -> u64 { | ||
| 654 | u64::from(self.ext_csd.sector_count()) * 512 | ||
| 655 | } | ||
| 656 | } | ||
| 657 | |||
| 658 | impl<'d, 'e, A: Addressable> block_device_driver::BlockDevice<512> for StorageDevice<'d, 'e, A> { | ||
| 659 | type Error = Error; | ||
| 660 | type Align = aligned::A4; | ||
| 661 | |||
| 662 | async fn read( | ||
| 663 | &mut self, | ||
| 664 | block_address: u32, | ||
| 665 | buf: &mut [aligned::Aligned<Self::Align, [u8; 512]>], | ||
| 666 | ) -> Result<(), Self::Error> { | ||
| 667 | // TODO: I think block_address needs to be adjusted by the partition start offset | ||
| 668 | if buf.len() == 1 { | ||
| 669 | let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut DataBlock) }; | ||
| 670 | self.read_block(block_address, block).await?; | ||
| 671 | } else { | ||
| 672 | let blocks: &mut [DataBlock] = | ||
| 673 | unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut DataBlock, buf.len()) }; | ||
| 674 | self.read_blocks(block_address, blocks).await?; | ||
| 675 | } | ||
| 676 | Ok(()) | ||
| 677 | } | ||
| 678 | |||
| 679 | async fn write( | ||
| 680 | &mut self, | ||
| 681 | block_address: u32, | ||
| 682 | buf: &[aligned::Aligned<Self::Align, [u8; 512]>], | ||
| 683 | ) -> Result<(), Self::Error> { | ||
| 684 | // TODO: I think block_address needs to be adjusted by the partition start offset | ||
| 685 | if buf.len() == 1 { | ||
| 686 | let block = unsafe { &*(&buf[0] as *const _ as *const DataBlock) }; | ||
| 687 | self.write_block(block_address, block).await?; | ||
| 688 | } else { | ||
| 689 | let blocks: &[DataBlock] = | ||
| 690 | unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const DataBlock, buf.len()) }; | ||
| 691 | self.write_blocks(block_address, blocks).await?; | ||
| 692 | } | ||
| 693 | Ok(()) | ||
| 694 | } | ||
| 695 | |||
| 696 | async fn size(&mut self) -> Result<u64, Self::Error> { | ||
| 697 | Ok(self.info.size()) | ||
| 698 | } | ||
| 699 | } | ||
diff --git a/embassy-stm32/src/sdmmc/sdio.rs b/embassy-stm32/src/sdmmc/sdio.rs new file mode 100644 index 000000000..e436d68cb --- /dev/null +++ b/embassy-stm32/src/sdmmc/sdio.rs | |||
| @@ -0,0 +1,178 @@ | |||
| 1 | use core::ops::{Deref, DerefMut}; | ||
| 2 | |||
| 3 | use aligned::{A4, Aligned}; | ||
| 4 | use sdio_host::common_cmd::{R1, Rz, cmd}; | ||
| 5 | use sdio_host::sd::BusWidth; | ||
| 6 | use sdio_host::sd_cmd; | ||
| 7 | |||
| 8 | use crate::sdmmc::{DatapathMode, Error, Sdmmc, aligned_mut, aligned_ref, block_size, slice8_mut, slice8_ref}; | ||
| 9 | use crate::time::Hertz; | ||
| 10 | |||
| 11 | /// Aligned data block for SDMMC transfers. | ||
| 12 | /// | ||
| 13 | /// This is a 64-byte array, aligned to 4 bytes to satisfy DMA requirements. | ||
| 14 | #[repr(align(4))] | ||
| 15 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 16 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 17 | pub struct DataBlock(pub [u32; 16]); | ||
| 18 | |||
| 19 | impl DataBlock { | ||
| 20 | /// Create a new DataBlock | ||
| 21 | pub const fn new() -> Self { | ||
| 22 | DataBlock([0u32; 16]) | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | impl Deref for DataBlock { | ||
| 27 | type Target = [u8; 64]; | ||
| 28 | |||
| 29 | fn deref(&self) -> &Self::Target { | ||
| 30 | unwrap!(slice8_ref(&self.0[..]).try_into()) | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | impl DerefMut for DataBlock { | ||
| 35 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 36 | unwrap!(slice8_mut(&mut self.0[..]).try_into()) | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Storage Device | ||
| 41 | pub struct SerialDataInterface<'a, 'b> { | ||
| 42 | /// Inner member | ||
| 43 | sdmmc: &'a mut Sdmmc<'b>, | ||
| 44 | } | ||
| 45 | |||
| 46 | /// Card Storage Device | ||
| 47 | impl<'a, 'b> SerialDataInterface<'a, 'b> { | ||
| 48 | /// Create a new SD card | ||
| 49 | pub async fn new(sdmmc: &'a mut Sdmmc<'b>, freq: Hertz) -> Result<Self, Error> { | ||
| 50 | let mut s = Self { sdmmc }; | ||
| 51 | |||
| 52 | s.acquire(freq).await?; | ||
| 53 | |||
| 54 | Ok(s) | ||
| 55 | } | ||
| 56 | |||
| 57 | /// Initializes the card into a known state (or at least tries to). | ||
| 58 | async fn acquire(&mut self, _freq: Hertz) -> Result<(), Error> { | ||
| 59 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 60 | |||
| 61 | let _bus_width = match self.sdmmc.bus_width() { | ||
| 62 | BusWidth::Eight => return Err(Error::BusWidth), | ||
| 63 | bus_width => bus_width, | ||
| 64 | }; | ||
| 65 | |||
| 66 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 67 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 68 | self.sdmmc.init_idle()?; | ||
| 69 | |||
| 70 | self.sdmmc.cmd(cmd::<Rz>(5, 0), false)?; | ||
| 71 | |||
| 72 | // Get RCA | ||
| 73 | let rca = self.sdmmc.cmd(sd_cmd::send_relative_address(), false)?; | ||
| 74 | |||
| 75 | // Select the card with RCA | ||
| 76 | self.sdmmc.select_card(Some(rca.try_into().unwrap()))?; | ||
| 77 | |||
| 78 | Ok(()) | ||
| 79 | } | ||
| 80 | |||
| 81 | /// Set the bus to the 4-bit high-speed frequency | ||
| 82 | pub fn set_bus_to_high_speed(&mut self, frequency: Hertz) -> Result<(), Error> { | ||
| 83 | self.sdmmc.clkcr_set_clkdiv(frequency, BusWidth::Four)?; | ||
| 84 | |||
| 85 | Ok(()) | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Run cmd52 | ||
| 89 | pub async fn cmd52(&mut self, arg: u32) -> Result<u32, Error> { | ||
| 90 | self.sdmmc.cmd(cmd::<R1>(52, arg), false) | ||
| 91 | } | ||
| 92 | |||
| 93 | /// Read in block mode using cmd53 | ||
| 94 | pub async fn cmd53_block_read(&mut self, arg: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { | ||
| 95 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 96 | |||
| 97 | // NOTE(unsafe) reinterpret buffer as &mut [u32] | ||
| 98 | let buffer = unsafe { | ||
| 99 | core::slice::from_raw_parts_mut( | ||
| 100 | blocks.as_mut_ptr() as *mut u32, | ||
| 101 | blocks.len() * size_of::<DataBlock>() / size_of::<u32>(), | ||
| 102 | ) | ||
| 103 | }; | ||
| 104 | |||
| 105 | let transfer = self.sdmmc.prepare_datapath_read( | ||
| 106 | aligned_mut(buffer), | ||
| 107 | DatapathMode::Block(block_size(size_of::<DataBlock>())), | ||
| 108 | ); | ||
| 109 | self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; | ||
| 110 | |||
| 111 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 112 | self.sdmmc.clear_interrupt_flags(); | ||
| 113 | |||
| 114 | Ok(()) | ||
| 115 | } | ||
| 116 | |||
| 117 | /// Read in multibyte mode using cmd53 | ||
| 118 | pub async fn cmd53_byte_read(&mut self, arg: u32, buffer: &mut Aligned<A4, [u8]>) -> Result<(), Error> { | ||
| 119 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 120 | |||
| 121 | let transfer = self.sdmmc.prepare_datapath_read(buffer, DatapathMode::Byte); | ||
| 122 | self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; | ||
| 123 | |||
| 124 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 125 | self.sdmmc.clear_interrupt_flags(); | ||
| 126 | |||
| 127 | Ok(()) | ||
| 128 | } | ||
| 129 | |||
| 130 | /// Write in block mode using cmd53 | ||
| 131 | pub async fn cmd53_block_write(&mut self, arg: u32, blocks: &[DataBlock]) -> Result<(), Error> { | ||
| 132 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 133 | |||
| 134 | // NOTE(unsafe) reinterpret buffer as &mut [u32] | ||
| 135 | let buffer = unsafe { | ||
| 136 | core::slice::from_raw_parts_mut( | ||
| 137 | blocks.as_ptr() as *mut u32, | ||
| 138 | blocks.len() * size_of::<DataBlock>() / size_of::<u32>(), | ||
| 139 | ) | ||
| 140 | }; | ||
| 141 | |||
| 142 | #[cfg(sdmmc_v1)] | ||
| 143 | self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; | ||
| 144 | |||
| 145 | let transfer = self.sdmmc.prepare_datapath_write( | ||
| 146 | aligned_ref(buffer), | ||
| 147 | DatapathMode::Block(block_size(size_of::<DataBlock>())), | ||
| 148 | ); | ||
| 149 | |||
| 150 | #[cfg(sdmmc_v2)] | ||
| 151 | self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; | ||
| 152 | |||
| 153 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 154 | self.sdmmc.clear_interrupt_flags(); | ||
| 155 | |||
| 156 | Ok(()) | ||
| 157 | } | ||
| 158 | |||
| 159 | /// Write in multibyte mode using cmd53 | ||
| 160 | pub async fn cmd53_byte_write(&mut self, arg: u32, buffer: &[u32]) -> Result<(), Error> { | ||
| 161 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 162 | |||
| 163 | #[cfg(sdmmc_v1)] | ||
| 164 | self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; | ||
| 165 | |||
| 166 | let transfer = self | ||
| 167 | .sdmmc | ||
| 168 | .prepare_datapath_write(aligned_ref(buffer), DatapathMode::Byte); | ||
| 169 | |||
| 170 | #[cfg(sdmmc_v2)] | ||
| 171 | self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; | ||
| 172 | |||
| 173 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 174 | self.sdmmc.clear_interrupt_flags(); | ||
| 175 | |||
| 176 | Ok(()) | ||
| 177 | } | ||
| 178 | } | ||
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..13ff31a34 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)] |
| @@ -54,6 +54,26 @@ pub enum BitOrder { | |||
| 54 | MsbFirst, | 54 | MsbFirst, |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | /// SPI Direction. | ||
| 58 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 59 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 60 | pub enum Direction { | ||
| 61 | /// Transmit | ||
| 62 | Transmit, | ||
| 63 | /// Receive | ||
| 64 | Receive, | ||
| 65 | } | ||
| 66 | |||
| 67 | /// Slave Select (SS) pin polarity. | ||
| 68 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 69 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 70 | pub enum SlaveSelectPolarity { | ||
| 71 | /// SS active high | ||
| 72 | ActiveHigh, | ||
| 73 | /// SS active low | ||
| 74 | ActiveLow, | ||
| 75 | } | ||
| 76 | |||
| 57 | /// SPI configuration. | 77 | /// SPI configuration. |
| 58 | #[non_exhaustive] | 78 | #[non_exhaustive] |
| 59 | #[derive(Copy, Clone)] | 79 | #[derive(Copy, Clone)] |
| @@ -72,6 +92,13 @@ pub struct Config { | |||
| 72 | /// signal rise/fall speed (slew rate) - defaults to `Medium`. | 92 | /// signal rise/fall speed (slew rate) - defaults to `Medium`. |
| 73 | /// Increase for high SPI speeds. Change to `Low` to reduce ringing. | 93 | /// Increase for high SPI speeds. Change to `Low` to reduce ringing. |
| 74 | pub gpio_speed: Speed, | 94 | pub gpio_speed: Speed, |
| 95 | /// If True sets SSOE to zero even if SPI is in Master Mode. | ||
| 96 | /// NSS output enabled (SSM = 0, SSOE = 1): The NSS signal is driven low when the master starts the communication and is kept low until the SPI is disabled. | ||
| 97 | /// NSS output disabled (SSM = 0, SSOE = 0): For devices set as slave, the NSS pin acts as a classical NSS input: the slave is selected when NSS is low and deselected when NSS high. | ||
| 98 | pub nss_output_disable: bool, | ||
| 99 | /// Slave Select (SS) pin polarity. | ||
| 100 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 101 | pub nss_polarity: SlaveSelectPolarity, | ||
| 75 | } | 102 | } |
| 76 | 103 | ||
| 77 | impl Default for Config { | 104 | impl Default for Config { |
| @@ -82,6 +109,9 @@ impl Default for Config { | |||
| 82 | frequency: Hertz(1_000_000), | 109 | frequency: Hertz(1_000_000), |
| 83 | miso_pull: Pull::None, | 110 | miso_pull: Pull::None, |
| 84 | gpio_speed: Speed::VeryHigh, | 111 | gpio_speed: Speed::VeryHigh, |
| 112 | nss_output_disable: false, | ||
| 113 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 114 | nss_polarity: SlaveSelectPolarity::ActiveHigh, | ||
| 85 | } | 115 | } |
| 86 | } | 116 | } |
| 87 | } | 117 | } |
| @@ -108,6 +138,14 @@ impl Config { | |||
| 108 | } | 138 | } |
| 109 | } | 139 | } |
| 110 | 140 | ||
| 141 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 142 | fn raw_nss_polarity(&self) -> vals::Ssiop { | ||
| 143 | match self.nss_polarity { | ||
| 144 | SlaveSelectPolarity::ActiveHigh => vals::Ssiop::ACTIVE_HIGH, | ||
| 145 | SlaveSelectPolarity::ActiveLow => vals::Ssiop::ACTIVE_LOW, | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 111 | #[cfg(gpio_v1)] | 149 | #[cfg(gpio_v1)] |
| 112 | fn sck_af(&self) -> AfType { | 150 | fn sck_af(&self) -> AfType { |
| 113 | AfType::output(OutputType::PushPull, self.gpio_speed) | 151 | AfType::output(OutputType::PushPull, self.gpio_speed) |
| @@ -125,26 +163,69 @@ impl Config { | |||
| 125 | ) | 163 | ) |
| 126 | } | 164 | } |
| 127 | } | 165 | } |
| 166 | |||
| 167 | /// SPI communication mode | ||
| 168 | pub mod mode { | ||
| 169 | use stm32_metapac::spi::vals; | ||
| 170 | |||
| 171 | trait SealedMode {} | ||
| 172 | |||
| 173 | /// Trait for SPI communication mode operations. | ||
| 174 | #[allow(private_bounds)] | ||
| 175 | pub trait CommunicationMode: SealedMode { | ||
| 176 | /// Spi communication mode | ||
| 177 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] | ||
| 178 | const MASTER: vals::Mstr; | ||
| 179 | /// Spi communication mode | ||
| 180 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 181 | const MASTER: vals::Master; | ||
| 182 | } | ||
| 183 | |||
| 184 | /// Mode allowing for SPI master operations. | ||
| 185 | pub struct Master; | ||
| 186 | /// Mode allowing for SPI slave operations. | ||
| 187 | pub struct Slave; | ||
| 188 | |||
| 189 | impl SealedMode for Master {} | ||
| 190 | impl CommunicationMode for Master { | ||
| 191 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] | ||
| 192 | const MASTER: vals::Mstr = vals::Mstr::MASTER; | ||
| 193 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 194 | const MASTER: vals::Master = vals::Master::MASTER; | ||
| 195 | } | ||
| 196 | |||
| 197 | impl SealedMode for Slave {} | ||
| 198 | impl CommunicationMode for Slave { | ||
| 199 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] | ||
| 200 | const MASTER: vals::Mstr = vals::Mstr::SLAVE; | ||
| 201 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 202 | const MASTER: vals::Master = vals::Master::SLAVE; | ||
| 203 | } | ||
| 204 | } | ||
| 205 | use mode::{CommunicationMode, Master, Slave}; | ||
| 206 | |||
| 128 | /// SPI driver. | 207 | /// SPI driver. |
| 129 | pub struct Spi<'d, M: PeriMode> { | 208 | pub struct Spi<'d, M: PeriMode, CM: CommunicationMode> { |
| 130 | pub(crate) info: &'static Info, | 209 | pub(crate) info: &'static Info, |
| 131 | kernel_clock: Hertz, | 210 | kernel_clock: Hertz, |
| 132 | sck: Option<Peri<'d, AnyPin>>, | 211 | sck: Option<Peri<'d, AnyPin>>, |
| 133 | mosi: Option<Peri<'d, AnyPin>>, | 212 | mosi: Option<Peri<'d, AnyPin>>, |
| 134 | miso: Option<Peri<'d, AnyPin>>, | 213 | miso: Option<Peri<'d, AnyPin>>, |
| 214 | nss: Option<Peri<'d, AnyPin>>, | ||
| 135 | tx_dma: Option<ChannelAndRequest<'d>>, | 215 | tx_dma: Option<ChannelAndRequest<'d>>, |
| 136 | rx_dma: Option<ChannelAndRequest<'d>>, | 216 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 137 | _phantom: PhantomData<M>, | 217 | _phantom: PhantomData<(M, CM)>, |
| 138 | current_word_size: word_impl::Config, | 218 | current_word_size: word_impl::Config, |
| 139 | gpio_speed: Speed, | 219 | gpio_speed: Speed, |
| 140 | } | 220 | } |
| 141 | 221 | ||
| 142 | impl<'d, M: PeriMode> Spi<'d, M> { | 222 | impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { |
| 143 | fn new_inner<T: Instance>( | 223 | fn new_inner<T: Instance>( |
| 144 | _peri: Peri<'d, T>, | 224 | _peri: Peri<'d, T>, |
| 145 | sck: Option<Peri<'d, AnyPin>>, | 225 | sck: Option<Peri<'d, AnyPin>>, |
| 146 | mosi: Option<Peri<'d, AnyPin>>, | 226 | mosi: Option<Peri<'d, AnyPin>>, |
| 147 | miso: Option<Peri<'d, AnyPin>>, | 227 | miso: Option<Peri<'d, AnyPin>>, |
| 228 | nss: Option<Peri<'d, AnyPin>>, | ||
| 148 | tx_dma: Option<ChannelAndRequest<'d>>, | 229 | tx_dma: Option<ChannelAndRequest<'d>>, |
| 149 | rx_dma: Option<ChannelAndRequest<'d>>, | 230 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 150 | config: Config, | 231 | config: Config, |
| @@ -155,6 +236,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 155 | sck, | 236 | sck, |
| 156 | mosi, | 237 | mosi, |
| 157 | miso, | 238 | miso, |
| 239 | nss, | ||
| 158 | tx_dma, | 240 | tx_dma, |
| 159 | rx_dma, | 241 | rx_dma, |
| 160 | current_word_size: <u8 as SealedWord>::CONFIG, | 242 | current_word_size: <u8 as SealedWord>::CONFIG, |
| @@ -171,24 +253,46 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 171 | let cpol = config.raw_polarity(); | 253 | let cpol = config.raw_polarity(); |
| 172 | let lsbfirst = config.raw_byte_order(); | 254 | let lsbfirst = config.raw_byte_order(); |
| 173 | 255 | ||
| 174 | self.info.rcc.enable_and_reset(); | 256 | self.info.rcc.enable_and_reset_without_stop(); |
| 257 | |||
| 258 | /* | ||
| 259 | - Software NSS management (SSM = 1) | ||
| 260 | The slave select information is driven internally by the value of the SSI bit in the | ||
| 261 | SPI_CR1 register. The external NSS pin remains free for other application uses. | ||
| 262 | |||
| 263 | - Hardware NSS management (SSM = 0) | ||
| 264 | Two configurations are possible depending on the NSS output configuration (SSOE bit | ||
| 265 | in register SPI_CR1). | ||
| 266 | |||
| 267 | -- NSS output enabled (SSM = 0, SSOE = 1) | ||
| 268 | This configuration is used only when the device operates in master mode. The | ||
| 269 | NSS signal is driven low when the master starts the communication and is kept | ||
| 270 | low until the SPI is disabled. | ||
| 271 | |||
| 272 | -- NSS output disabled (SSM = 0, SSOE = 0) | ||
| 273 | This configuration allows multimaster capability for devices operating in master | ||
| 274 | mode. For devices set as slave, the NSS pin acts as a classical NSS input: the | ||
| 275 | slave is selected when NSS is low and deselected when NSS high | ||
| 276 | */ | ||
| 277 | let ssm = self.nss.is_none(); | ||
| 175 | 278 | ||
| 176 | let regs = self.info.regs; | 279 | let regs = self.info.regs; |
| 177 | #[cfg(any(spi_v1, spi_v2))] | 280 | #[cfg(any(spi_v1, spi_v2))] |
| 178 | { | 281 | { |
| 282 | let ssoe = CM::MASTER == vals::Mstr::MASTER && !config.nss_output_disable; | ||
| 179 | regs.cr2().modify(|w| { | 283 | regs.cr2().modify(|w| { |
| 180 | w.set_ssoe(false); | 284 | w.set_ssoe(ssoe); |
| 181 | }); | 285 | }); |
| 182 | regs.cr1().modify(|w| { | 286 | regs.cr1().modify(|w| { |
| 183 | w.set_cpha(cpha); | 287 | w.set_cpha(cpha); |
| 184 | w.set_cpol(cpol); | 288 | w.set_cpol(cpol); |
| 185 | 289 | ||
| 186 | w.set_mstr(vals::Mstr::MASTER); | 290 | w.set_mstr(CM::MASTER); |
| 187 | w.set_br(br); | 291 | w.set_br(br); |
| 188 | w.set_spe(true); | 292 | w.set_spe(true); |
| 189 | w.set_lsbfirst(lsbfirst); | 293 | w.set_lsbfirst(lsbfirst); |
| 190 | w.set_ssi(true); | 294 | w.set_ssi(CM::MASTER == vals::Mstr::MASTER); |
| 191 | w.set_ssm(true); | 295 | w.set_ssm(ssm); |
| 192 | w.set_crcen(false); | 296 | w.set_crcen(false); |
| 193 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); | 297 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); |
| 194 | // we're doing "fake rxonly", by actually writing one | 298 | // we're doing "fake rxonly", by actually writing one |
| @@ -200,21 +304,22 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 200 | } | 304 | } |
| 201 | #[cfg(spi_v3)] | 305 | #[cfg(spi_v3)] |
| 202 | { | 306 | { |
| 307 | let ssoe = CM::MASTER == vals::Mstr::MASTER && !config.nss_output_disable; | ||
| 203 | regs.cr2().modify(|w| { | 308 | regs.cr2().modify(|w| { |
| 204 | let (ds, frxth) = <u8 as SealedWord>::CONFIG; | 309 | let (ds, frxth) = <u8 as SealedWord>::CONFIG; |
| 205 | w.set_frxth(frxth); | 310 | w.set_frxth(frxth); |
| 206 | w.set_ds(ds); | 311 | w.set_ds(ds); |
| 207 | w.set_ssoe(false); | 312 | w.set_ssoe(ssoe); |
| 208 | }); | 313 | }); |
| 209 | regs.cr1().modify(|w| { | 314 | regs.cr1().modify(|w| { |
| 210 | w.set_cpha(cpha); | 315 | w.set_cpha(cpha); |
| 211 | w.set_cpol(cpol); | 316 | w.set_cpol(cpol); |
| 212 | 317 | ||
| 213 | w.set_mstr(vals::Mstr::MASTER); | 318 | w.set_mstr(CM::MASTER); |
| 214 | w.set_br(br); | 319 | w.set_br(br); |
| 215 | w.set_lsbfirst(lsbfirst); | 320 | w.set_lsbfirst(lsbfirst); |
| 216 | w.set_ssi(true); | 321 | w.set_ssi(CM::MASTER == vals::Mstr::MASTER); |
| 217 | w.set_ssm(true); | 322 | w.set_ssm(ssm); |
| 218 | w.set_crcen(false); | 323 | w.set_crcen(false); |
| 219 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); | 324 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); |
| 220 | w.set_spe(true); | 325 | w.set_spe(true); |
| @@ -222,21 +327,22 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 222 | } | 327 | } |
| 223 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | 328 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 224 | { | 329 | { |
| 330 | let ssoe = CM::MASTER == vals::Master::MASTER && !config.nss_output_disable; | ||
| 331 | let ssiop = config.raw_nss_polarity(); | ||
| 225 | regs.ifcr().write(|w| w.0 = 0xffff_ffff); | 332 | regs.ifcr().write(|w| w.0 = 0xffff_ffff); |
| 226 | regs.cfg2().modify(|w| { | 333 | regs.cfg2().modify(|w| { |
| 227 | //w.set_ssoe(true); | 334 | w.set_ssoe(ssoe); |
| 228 | w.set_ssoe(false); | ||
| 229 | w.set_cpha(cpha); | 335 | w.set_cpha(cpha); |
| 230 | w.set_cpol(cpol); | 336 | w.set_cpol(cpol); |
| 231 | w.set_lsbfirst(lsbfirst); | 337 | w.set_lsbfirst(lsbfirst); |
| 232 | w.set_ssm(true); | 338 | w.set_ssm(ssm); |
| 233 | w.set_master(vals::Master::MASTER); | 339 | w.set_master(CM::MASTER); |
| 234 | w.set_comm(vals::Comm::FULL_DUPLEX); | 340 | w.set_comm(vals::Comm::FULL_DUPLEX); |
| 235 | w.set_ssom(vals::Ssom::ASSERTED); | 341 | w.set_ssom(vals::Ssom::ASSERTED); |
| 236 | w.set_midi(0); | 342 | w.set_midi(0); |
| 237 | w.set_mssi(0); | 343 | w.set_mssi(0); |
| 238 | w.set_afcntr(true); | 344 | w.set_afcntr(true); |
| 239 | w.set_ssiop(vals::Ssiop::ACTIVE_HIGH); | 345 | w.set_ssiop(ssiop); |
| 240 | }); | 346 | }); |
| 241 | regs.cfg1().modify(|w| { | 347 | regs.cfg1().modify(|w| { |
| 242 | w.set_crcen(false); | 348 | w.set_crcen(false); |
| @@ -284,6 +390,8 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 284 | 390 | ||
| 285 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | 391 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 286 | { | 392 | { |
| 393 | let ssiop = config.raw_nss_polarity(); | ||
| 394 | |||
| 287 | self.info.regs.cr1().modify(|w| { | 395 | self.info.regs.cr1().modify(|w| { |
| 288 | w.set_spe(false); | 396 | w.set_spe(false); |
| 289 | }); | 397 | }); |
| @@ -292,6 +400,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 292 | w.set_cpha(cpha); | 400 | w.set_cpha(cpha); |
| 293 | w.set_cpol(cpol); | 401 | w.set_cpol(cpol); |
| 294 | w.set_lsbfirst(lsbfirst); | 402 | w.set_lsbfirst(lsbfirst); |
| 403 | w.set_ssiop(ssiop); | ||
| 295 | }); | 404 | }); |
| 296 | self.info.regs.cfg1().modify(|w| { | 405 | self.info.regs.cfg1().modify(|w| { |
| 297 | w.set_mbr(br); | 406 | w.set_mbr(br); |
| @@ -304,6 +413,20 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 304 | Ok(()) | 413 | Ok(()) |
| 305 | } | 414 | } |
| 306 | 415 | ||
| 416 | /// Set SPI direction | ||
| 417 | #[cfg(any(spi_v1, spi_v2, spi_v3))] | ||
| 418 | pub fn set_direction(&mut self, dir: Option<Direction>) { | ||
| 419 | let (bidimode, bidioe) = match dir { | ||
| 420 | Some(Direction::Transmit) => (vals::Bidimode::BIDIRECTIONAL, vals::Bidioe::TRANSMIT), | ||
| 421 | Some(Direction::Receive) => (vals::Bidimode::BIDIRECTIONAL, vals::Bidioe::RECEIVE), | ||
| 422 | None => (vals::Bidimode::UNIDIRECTIONAL, vals::Bidioe::TRANSMIT), | ||
| 423 | }; | ||
| 424 | self.info.regs.cr1().modify(|w| { | ||
| 425 | w.set_bidimode(bidimode); | ||
| 426 | w.set_bidioe(bidioe); | ||
| 427 | }); | ||
| 428 | } | ||
| 429 | |||
| 307 | /// Get current SPI configuration. | 430 | /// Get current SPI configuration. |
| 308 | pub fn get_current_config(&self) -> Config { | 431 | pub fn get_current_config(&self) -> Config { |
| 309 | #[cfg(any(spi_v1, spi_v2, spi_v3))] | 432 | #[cfg(any(spi_v1, spi_v2, spi_v3))] |
| @@ -313,6 +436,11 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 313 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | 436 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 314 | let cfg1 = self.info.regs.cfg1().read(); | 437 | let cfg1 = self.info.regs.cfg1().read(); |
| 315 | 438 | ||
| 439 | #[cfg(any(spi_v1, spi_v2, spi_v3))] | ||
| 440 | let ssoe = self.info.regs.cr2().read().ssoe(); | ||
| 441 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 442 | let ssoe = cfg.ssoe(); | ||
| 443 | |||
| 316 | let polarity = if cfg.cpol() == vals::Cpol::IDLE_LOW { | 444 | let polarity = if cfg.cpol() == vals::Cpol::IDLE_LOW { |
| 317 | Polarity::IdleLow | 445 | Polarity::IdleLow |
| 318 | } else { | 446 | } else { |
| @@ -342,12 +470,25 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 342 | 470 | ||
| 343 | let frequency = compute_frequency(self.kernel_clock, br); | 471 | let frequency = compute_frequency(self.kernel_clock, br); |
| 344 | 472 | ||
| 473 | // NSS output disabled if SSOE=0 or if SSM=1 software slave management enabled | ||
| 474 | let nss_output_disable = !ssoe || cfg.ssm(); | ||
| 475 | |||
| 476 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 477 | let nss_polarity = if cfg.ssiop() == vals::Ssiop::ACTIVE_LOW { | ||
| 478 | SlaveSelectPolarity::ActiveLow | ||
| 479 | } else { | ||
| 480 | SlaveSelectPolarity::ActiveHigh | ||
| 481 | }; | ||
| 482 | |||
| 345 | Config { | 483 | Config { |
| 346 | mode: Mode { polarity, phase }, | 484 | mode: Mode { polarity, phase }, |
| 347 | bit_order, | 485 | bit_order, |
| 348 | frequency, | 486 | frequency, |
| 349 | miso_pull, | 487 | miso_pull, |
| 350 | gpio_speed: self.gpio_speed, | 488 | gpio_speed: self.gpio_speed, |
| 489 | nss_output_disable, | ||
| 490 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 491 | nss_polarity, | ||
| 351 | } | 492 | } |
| 352 | } | 493 | } |
| 353 | 494 | ||
| @@ -469,7 +610,30 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 469 | } | 610 | } |
| 470 | } | 611 | } |
| 471 | 612 | ||
| 472 | impl<'d> Spi<'d, Blocking> { | 613 | impl<'d> Spi<'d, Blocking, Slave> { |
| 614 | /// Create a new blocking SPI slave driver. | ||
| 615 | pub fn new_blocking_slave<T: Instance, #[cfg(afio)] A>( | ||
| 616 | peri: Peri<'d, T>, | ||
| 617 | sck: Peri<'d, if_afio!(impl SckPin<T, A>)>, | ||
| 618 | mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>, | ||
| 619 | miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>, | ||
| 620 | cs: Peri<'d, if_afio!(impl CsPin<T, A>)>, | ||
| 621 | config: Config, | ||
| 622 | ) -> Self { | ||
| 623 | Self::new_inner( | ||
| 624 | peri, | ||
| 625 | new_pin!(sck, config.sck_af()), | ||
| 626 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | ||
| 627 | new_pin!(miso, AfType::input(config.miso_pull)), | ||
| 628 | new_pin!(cs, AfType::input(Pull::None)), | ||
| 629 | None, | ||
| 630 | None, | ||
| 631 | config, | ||
| 632 | ) | ||
| 633 | } | ||
| 634 | } | ||
| 635 | |||
| 636 | impl<'d> Spi<'d, Blocking, Master> { | ||
| 473 | /// Create a new blocking SPI driver. | 637 | /// Create a new blocking SPI driver. |
| 474 | pub fn new_blocking<T: Instance, #[cfg(afio)] A>( | 638 | pub fn new_blocking<T: Instance, #[cfg(afio)] A>( |
| 475 | peri: Peri<'d, T>, | 639 | peri: Peri<'d, T>, |
| @@ -485,6 +649,7 @@ impl<'d> Spi<'d, Blocking> { | |||
| 485 | new_pin!(miso, AfType::input(config.miso_pull)), | 649 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 486 | None, | 650 | None, |
| 487 | None, | 651 | None, |
| 652 | None, | ||
| 488 | config, | 653 | config, |
| 489 | ) | 654 | ) |
| 490 | } | 655 | } |
| @@ -503,6 +668,7 @@ impl<'d> Spi<'d, Blocking> { | |||
| 503 | new_pin!(miso, AfType::input(config.miso_pull)), | 668 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 504 | None, | 669 | None, |
| 505 | None, | 670 | None, |
| 671 | None, | ||
| 506 | config, | 672 | config, |
| 507 | ) | 673 | ) |
| 508 | } | 674 | } |
| @@ -521,6 +687,7 @@ impl<'d> Spi<'d, Blocking> { | |||
| 521 | None, | 687 | None, |
| 522 | None, | 688 | None, |
| 523 | None, | 689 | None, |
| 690 | None, | ||
| 524 | config, | 691 | config, |
| 525 | ) | 692 | ) |
| 526 | } | 693 | } |
| @@ -540,12 +707,38 @@ impl<'d> Spi<'d, Blocking> { | |||
| 540 | None, | 707 | None, |
| 541 | None, | 708 | None, |
| 542 | None, | 709 | None, |
| 710 | None, | ||
| 543 | config, | 711 | config, |
| 544 | ) | 712 | ) |
| 545 | } | 713 | } |
| 546 | } | 714 | } |
| 547 | 715 | ||
| 548 | impl<'d> Spi<'d, Async> { | 716 | impl<'d> Spi<'d, Async, Slave> { |
| 717 | /// Create a new SPI slave driver. | ||
| 718 | pub fn new_slave<T: Instance, #[cfg(afio)] A>( | ||
| 719 | peri: Peri<'d, T>, | ||
| 720 | sck: Peri<'d, if_afio!(impl SckPin<T, A>)>, | ||
| 721 | mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>, | ||
| 722 | miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>, | ||
| 723 | cs: Peri<'d, if_afio!(impl CsPin<T, A>)>, | ||
| 724 | tx_dma: Peri<'d, impl TxDma<T>>, | ||
| 725 | rx_dma: Peri<'d, impl RxDma<T>>, | ||
| 726 | config: Config, | ||
| 727 | ) -> Self { | ||
| 728 | Self::new_inner( | ||
| 729 | peri, | ||
| 730 | new_pin!(sck, config.sck_af()), | ||
| 731 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | ||
| 732 | new_pin!(miso, AfType::input(config.miso_pull)), | ||
| 733 | new_pin!(cs, AfType::input(Pull::None)), | ||
| 734 | new_dma!(tx_dma), | ||
| 735 | new_dma!(rx_dma), | ||
| 736 | config, | ||
| 737 | ) | ||
| 738 | } | ||
| 739 | } | ||
| 740 | |||
| 741 | impl<'d> Spi<'d, Async, Master> { | ||
| 549 | /// Create a new SPI driver. | 742 | /// Create a new SPI driver. |
| 550 | pub fn new<T: Instance, #[cfg(afio)] A>( | 743 | pub fn new<T: Instance, #[cfg(afio)] A>( |
| 551 | peri: Peri<'d, T>, | 744 | peri: Peri<'d, T>, |
| @@ -561,6 +754,7 @@ impl<'d> Spi<'d, Async> { | |||
| 561 | new_pin!(sck, config.sck_af()), | 754 | new_pin!(sck, config.sck_af()), |
| 562 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | 755 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 563 | new_pin!(miso, AfType::input(config.miso_pull)), | 756 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 757 | None, | ||
| 564 | new_dma!(tx_dma), | 758 | new_dma!(tx_dma), |
| 565 | new_dma!(rx_dma), | 759 | new_dma!(rx_dma), |
| 566 | config, | 760 | config, |
| @@ -581,6 +775,7 @@ impl<'d> Spi<'d, Async> { | |||
| 581 | new_pin!(sck, config.sck_af()), | 775 | new_pin!(sck, config.sck_af()), |
| 582 | None, | 776 | None, |
| 583 | new_pin!(miso, AfType::input(config.miso_pull)), | 777 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 778 | None, | ||
| 584 | #[cfg(any(spi_v1, spi_v2, spi_v3))] | 779 | #[cfg(any(spi_v1, spi_v2, spi_v3))] |
| 585 | new_dma!(tx_dma), | 780 | new_dma!(tx_dma), |
| 586 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | 781 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| @@ -603,12 +798,37 @@ impl<'d> Spi<'d, Async> { | |||
| 603 | new_pin!(sck, config.sck_af()), | 798 | new_pin!(sck, config.sck_af()), |
| 604 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | 799 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 605 | None, | 800 | None, |
| 801 | None, | ||
| 606 | new_dma!(tx_dma), | 802 | new_dma!(tx_dma), |
| 607 | None, | 803 | None, |
| 608 | config, | 804 | config, |
| 609 | ) | 805 | ) |
| 610 | } | 806 | } |
| 611 | 807 | ||
| 808 | /// Create a new SPI driver, in bidirectional mode, specifically in tranmit mode | ||
| 809 | #[cfg(any(spi_v1, spi_v2, spi_v3))] | ||
| 810 | pub fn new_bidi<T: Instance, #[cfg(afio)] A>( | ||
| 811 | peri: Peri<'d, T>, | ||
| 812 | sck: Peri<'d, if_afio!(impl SckPin<T, A>)>, | ||
| 813 | sdio: Peri<'d, if_afio!(impl MosiPin<T, A>)>, | ||
| 814 | tx_dma: Peri<'d, impl TxDma<T>>, | ||
| 815 | rx_dma: Peri<'d, impl RxDma<T>>, | ||
| 816 | config: Config, | ||
| 817 | ) -> Self { | ||
| 818 | let mut this = Self::new_inner( | ||
| 819 | peri, | ||
| 820 | new_pin!(sck, config.sck_af()), | ||
| 821 | new_pin!(sdio, AfType::output(OutputType::PushPull, config.gpio_speed)), | ||
| 822 | None, | ||
| 823 | None, | ||
| 824 | new_dma!(tx_dma), | ||
| 825 | new_dma!(rx_dma), | ||
| 826 | config, | ||
| 827 | ); | ||
| 828 | this.set_direction(Some(Direction::Transmit)); | ||
| 829 | this | ||
| 830 | } | ||
| 831 | |||
| 612 | /// Create a new SPI driver, in TX-only mode, without SCK pin. | 832 | /// Create a new SPI driver, in TX-only mode, without SCK pin. |
| 613 | /// | 833 | /// |
| 614 | /// This can be useful for bit-banging non-SPI protocols. | 834 | /// This can be useful for bit-banging non-SPI protocols. |
| @@ -623,6 +843,7 @@ impl<'d> Spi<'d, Async> { | |||
| 623 | None, | 843 | None, |
| 624 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | 844 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 625 | None, | 845 | None, |
| 846 | None, | ||
| 626 | new_dma!(tx_dma), | 847 | new_dma!(tx_dma), |
| 627 | None, | 848 | None, |
| 628 | config, | 849 | config, |
| @@ -646,7 +867,7 @@ impl<'d> Spi<'d, Async> { | |||
| 646 | config.bit_order = BitOrder::MsbFirst; | 867 | config.bit_order = BitOrder::MsbFirst; |
| 647 | config.frequency = freq; | 868 | config.frequency = freq; |
| 648 | 869 | ||
| 649 | Self::new_inner(peri, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config) | 870 | Self::new_inner(peri, None, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config) |
| 650 | } | 871 | } |
| 651 | 872 | ||
| 652 | #[allow(dead_code)] | 873 | #[allow(dead_code)] |
| @@ -656,11 +877,14 @@ impl<'d> Spi<'d, Async> { | |||
| 656 | rx_dma: Option<ChannelAndRequest<'d>>, | 877 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 657 | config: Config, | 878 | config: Config, |
| 658 | ) -> Self { | 879 | ) -> Self { |
| 659 | Self::new_inner(peri, None, None, None, tx_dma, rx_dma, config) | 880 | Self::new_inner(peri, None, None, None, None, tx_dma, rx_dma, config) |
| 660 | } | 881 | } |
| 882 | } | ||
| 661 | 883 | ||
| 884 | impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { | ||
| 662 | /// SPI write, using DMA. | 885 | /// SPI write, using DMA. |
| 663 | pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> { | 886 | pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> { |
| 887 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 664 | if data.is_empty() { | 888 | if data.is_empty() { |
| 665 | return Ok(()); | 889 | return Ok(()); |
| 666 | } | 890 | } |
| @@ -692,6 +916,7 @@ impl<'d> Spi<'d, Async> { | |||
| 692 | /// SPI read, using DMA. | 916 | /// SPI read, using DMA. |
| 693 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | 917 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 694 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { | 918 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { |
| 919 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 695 | if data.is_empty() { | 920 | if data.is_empty() { |
| 696 | return Ok(()); | 921 | return Ok(()); |
| 697 | } | 922 | } |
| @@ -779,6 +1004,7 @@ impl<'d> Spi<'d, Async> { | |||
| 779 | /// SPI read, using DMA. | 1004 | /// SPI read, using DMA. |
| 780 | #[cfg(any(spi_v1, spi_v2, spi_v3))] | 1005 | #[cfg(any(spi_v1, spi_v2, spi_v3))] |
| 781 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { | 1006 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { |
| 1007 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 782 | if data.is_empty() { | 1008 | if data.is_empty() { |
| 783 | return Ok(()); | 1009 | return Ok(()); |
| 784 | } | 1010 | } |
| @@ -826,6 +1052,7 @@ impl<'d> Spi<'d, Async> { | |||
| 826 | } | 1052 | } |
| 827 | 1053 | ||
| 828 | async fn transfer_inner<W: Word>(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error> { | 1054 | async fn transfer_inner<W: Word>(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error> { |
| 1055 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 829 | assert_eq!(read.len(), write.len()); | 1056 | assert_eq!(read.len(), write.len()); |
| 830 | if read.len() == 0 { | 1057 | if read.len() == 0 { |
| 831 | return Ok(()); | 1058 | return Ok(()); |
| @@ -877,6 +1104,8 @@ impl<'d> Spi<'d, Async> { | |||
| 877 | /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored. | 1104 | /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored. |
| 878 | /// If `write` is shorter it is padded with zero bytes. | 1105 | /// If `write` is shorter it is padded with zero bytes. |
| 879 | pub async fn transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { | 1106 | pub async fn transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { |
| 1107 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 1108 | |||
| 880 | self.transfer_inner(read, write).await | 1109 | self.transfer_inner(read, write).await |
| 881 | } | 1110 | } |
| 882 | 1111 | ||
| @@ -884,17 +1113,20 @@ impl<'d> Spi<'d, Async> { | |||
| 884 | /// | 1113 | /// |
| 885 | /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. | 1114 | /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. |
| 886 | pub async fn transfer_in_place<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { | 1115 | pub async fn transfer_in_place<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { |
| 1116 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 1117 | |||
| 887 | self.transfer_inner(data, data).await | 1118 | self.transfer_inner(data, data).await |
| 888 | } | 1119 | } |
| 889 | } | 1120 | } |
| 890 | 1121 | ||
| 891 | impl<'d, M: PeriMode> Drop for Spi<'d, M> { | 1122 | impl<'d, M: PeriMode, CM: CommunicationMode> Drop for Spi<'d, M, CM> { |
| 892 | fn drop(&mut self) { | 1123 | fn drop(&mut self) { |
| 893 | self.sck.as_ref().map(|x| x.set_as_disconnected()); | 1124 | self.sck.as_ref().map(|x| x.set_as_disconnected()); |
| 894 | self.mosi.as_ref().map(|x| x.set_as_disconnected()); | 1125 | self.mosi.as_ref().map(|x| x.set_as_disconnected()); |
| 895 | self.miso.as_ref().map(|x| x.set_as_disconnected()); | 1126 | self.miso.as_ref().map(|x| x.set_as_disconnected()); |
| 1127 | self.nss.as_ref().map(|x| x.set_as_disconnected()); | ||
| 896 | 1128 | ||
| 897 | self.info.rcc.disable(); | 1129 | self.info.rcc.disable_without_stop(); |
| 898 | } | 1130 | } |
| 899 | } | 1131 | } |
| 900 | 1132 | ||
| @@ -1127,7 +1359,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 | 1359 | // some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289 |
| 1128 | macro_rules! impl_blocking { | 1360 | macro_rules! impl_blocking { |
| 1129 | ($w:ident) => { | 1361 | ($w:ident) => { |
| 1130 | impl<'d, M: PeriMode> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, M> { | 1362 | impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, M, CM> { |
| 1131 | type Error = Error; | 1363 | type Error = Error; |
| 1132 | 1364 | ||
| 1133 | fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> { | 1365 | fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> { |
| @@ -1135,7 +1367,7 @@ macro_rules! impl_blocking { | |||
| 1135 | } | 1367 | } |
| 1136 | } | 1368 | } |
| 1137 | 1369 | ||
| 1138 | impl<'d, M: PeriMode> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, M> { | 1370 | impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, M, CM> { |
| 1139 | type Error = Error; | 1371 | type Error = Error; |
| 1140 | 1372 | ||
| 1141 | fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> { | 1373 | fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> { |
| @@ -1149,11 +1381,11 @@ macro_rules! impl_blocking { | |||
| 1149 | impl_blocking!(u8); | 1381 | impl_blocking!(u8); |
| 1150 | impl_blocking!(u16); | 1382 | impl_blocking!(u16); |
| 1151 | 1383 | ||
| 1152 | impl<'d, M: PeriMode> embedded_hal_1::spi::ErrorType for Spi<'d, M> { | 1384 | impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_1::spi::ErrorType for Spi<'d, M, CM> { |
| 1153 | type Error = Error; | 1385 | type Error = Error; |
| 1154 | } | 1386 | } |
| 1155 | 1387 | ||
| 1156 | impl<'d, W: Word, M: PeriMode> embedded_hal_1::spi::SpiBus<W> for Spi<'d, M> { | 1388 | 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> { | 1389 | fn flush(&mut self) -> Result<(), Self::Error> { |
| 1158 | Ok(()) | 1390 | Ok(()) |
| 1159 | } | 1391 | } |
| @@ -1186,7 +1418,7 @@ impl embedded_hal_1::spi::Error for Error { | |||
| 1186 | } | 1418 | } |
| 1187 | } | 1419 | } |
| 1188 | 1420 | ||
| 1189 | impl<'d, W: Word> embedded_hal_async::spi::SpiBus<W> for Spi<'d, Async> { | 1421 | 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> { | 1422 | async fn flush(&mut self) -> Result<(), Self::Error> { |
| 1191 | Ok(()) | 1423 | Ok(()) |
| 1192 | } | 1424 | } |
| @@ -1328,7 +1560,7 @@ foreach_peripheral!( | |||
| 1328 | }; | 1560 | }; |
| 1329 | ); | 1561 | ); |
| 1330 | 1562 | ||
| 1331 | impl<'d, M: PeriMode> SetConfig for Spi<'d, M> { | 1563 | impl<'d, M: PeriMode, CM: CommunicationMode> SetConfig for Spi<'d, M, CM> { |
| 1332 | type Config = Config; | 1564 | type Config = Config; |
| 1333 | type ConfigError = (); | 1565 | type ConfigError = (); |
| 1334 | fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { | 1566 | fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { |
diff --git a/embassy-stm32/src/time.rs b/embassy-stm32/src/time.rs index 532877f70..88a28ee3d 100644 --- a/embassy-stm32/src/time.rs +++ b/embassy-stm32/src/time.rs | |||
| @@ -4,7 +4,7 @@ use core::fmt::Display; | |||
| 4 | use core::ops::{Div, Mul}; | 4 | use core::ops::{Div, Mul}; |
| 5 | 5 | ||
| 6 | /// Hertz | 6 | /// Hertz |
| 7 | #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)] | 7 | #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug, Default)] |
| 8 | pub struct Hertz(pub u32); | 8 | pub struct Hertz(pub u32); |
| 9 | 9 | ||
| 10 | impl Display for Hertz { | 10 | impl Display for Hertz { |
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 7db74bdf6..ed5d902bd 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs | |||
| @@ -1,22 +1,24 @@ | |||
| 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; |
| 17 | use crate::peripherals; | ||
| 15 | use crate::rcc::{self, SealedRccPeripheral}; | 18 | use crate::rcc::{self, SealedRccPeripheral}; |
| 16 | #[cfg(feature = "low-power")] | 19 | #[cfg(feature = "low-power")] |
| 17 | use crate::rtc::Rtc; | 20 | use crate::rtc::Rtc; |
| 18 | use crate::timer::{CoreInstance, GeneralInstance1Channel}; | 21 | use crate::timer::{CoreInstance, GeneralInstance1Channel}; |
| 19 | use crate::{interrupt, peripherals}; | ||
| 20 | 22 | ||
| 21 | // NOTE regarding ALARM_COUNT: | 23 | // NOTE regarding ALARM_COUNT: |
| 22 | // | 24 | // |
| @@ -54,121 +56,6 @@ type T = peripherals::TIM23; | |||
| 54 | #[cfg(time_driver_tim24)] | 56 | #[cfg(time_driver_tim24)] |
| 55 | type T = peripherals::TIM24; | 57 | type T = peripherals::TIM24; |
| 56 | 58 | ||
| 57 | foreach_interrupt! { | ||
| 58 | (TIM1, timer, $block:ident, CC, $irq:ident) => { | ||
| 59 | #[cfg(time_driver_tim1)] | ||
| 60 | #[cfg(feature = "rt")] | ||
| 61 | #[interrupt] | ||
| 62 | fn $irq() { | ||
| 63 | DRIVER.on_interrupt() | ||
| 64 | } | ||
| 65 | }; | ||
| 66 | (TIM2, timer, $block:ident, CC, $irq:ident) => { | ||
| 67 | #[cfg(time_driver_tim2)] | ||
| 68 | #[cfg(feature = "rt")] | ||
| 69 | #[interrupt] | ||
| 70 | fn $irq() { | ||
| 71 | DRIVER.on_interrupt() | ||
| 72 | } | ||
| 73 | }; | ||
| 74 | (TIM3, timer, $block:ident, CC, $irq:ident) => { | ||
| 75 | #[cfg(time_driver_tim3)] | ||
| 76 | #[cfg(feature = "rt")] | ||
| 77 | #[interrupt] | ||
| 78 | fn $irq() { | ||
| 79 | DRIVER.on_interrupt() | ||
| 80 | } | ||
| 81 | }; | ||
| 82 | (TIM4, timer, $block:ident, CC, $irq:ident) => { | ||
| 83 | #[cfg(time_driver_tim4)] | ||
| 84 | #[cfg(feature = "rt")] | ||
| 85 | #[interrupt] | ||
| 86 | fn $irq() { | ||
| 87 | DRIVER.on_interrupt() | ||
| 88 | } | ||
| 89 | }; | ||
| 90 | (TIM5, timer, $block:ident, CC, $irq:ident) => { | ||
| 91 | #[cfg(time_driver_tim5)] | ||
| 92 | #[cfg(feature = "rt")] | ||
| 93 | #[interrupt] | ||
| 94 | fn $irq() { | ||
| 95 | DRIVER.on_interrupt() | ||
| 96 | } | ||
| 97 | }; | ||
| 98 | (TIM8, timer, $block:ident, CC, $irq:ident) => { | ||
| 99 | #[cfg(time_driver_tim8)] | ||
| 100 | #[cfg(feature = "rt")] | ||
| 101 | #[interrupt] | ||
| 102 | fn $irq() { | ||
| 103 | DRIVER.on_interrupt() | ||
| 104 | } | ||
| 105 | }; | ||
| 106 | (TIM9, timer, $block:ident, CC, $irq:ident) => { | ||
| 107 | #[cfg(time_driver_tim9)] | ||
| 108 | #[cfg(feature = "rt")] | ||
| 109 | #[interrupt] | ||
| 110 | fn $irq() { | ||
| 111 | DRIVER.on_interrupt() | ||
| 112 | } | ||
| 113 | }; | ||
| 114 | (TIM12, timer, $block:ident, CC, $irq:ident) => { | ||
| 115 | #[cfg(time_driver_tim12)] | ||
| 116 | #[cfg(feature = "rt")] | ||
| 117 | #[interrupt] | ||
| 118 | fn $irq() { | ||
| 119 | DRIVER.on_interrupt() | ||
| 120 | } | ||
| 121 | }; | ||
| 122 | (TIM15, timer, $block:ident, CC, $irq:ident) => { | ||
| 123 | #[cfg(time_driver_tim15)] | ||
| 124 | #[cfg(feature = "rt")] | ||
| 125 | #[interrupt] | ||
| 126 | fn $irq() { | ||
| 127 | DRIVER.on_interrupt() | ||
| 128 | } | ||
| 129 | }; | ||
| 130 | (TIM20, timer, $block:ident, CC, $irq:ident) => { | ||
| 131 | #[cfg(time_driver_tim20)] | ||
| 132 | #[cfg(feature = "rt")] | ||
| 133 | #[interrupt] | ||
| 134 | fn $irq() { | ||
| 135 | DRIVER.on_interrupt() | ||
| 136 | } | ||
| 137 | }; | ||
| 138 | (TIM21, timer, $block:ident, CC, $irq:ident) => { | ||
| 139 | #[cfg(time_driver_tim21)] | ||
| 140 | #[cfg(feature = "rt")] | ||
| 141 | #[interrupt] | ||
| 142 | fn $irq() { | ||
| 143 | DRIVER.on_interrupt() | ||
| 144 | } | ||
| 145 | }; | ||
| 146 | (TIM22, timer, $block:ident, CC, $irq:ident) => { | ||
| 147 | #[cfg(time_driver_tim22)] | ||
| 148 | #[cfg(feature = "rt")] | ||
| 149 | #[interrupt] | ||
| 150 | fn $irq() { | ||
| 151 | DRIVER.on_interrupt() | ||
| 152 | } | ||
| 153 | }; | ||
| 154 | (TIM23, timer, $block:ident, CC, $irq:ident) => { | ||
| 155 | #[cfg(time_driver_tim23)] | ||
| 156 | #[cfg(feature = "rt")] | ||
| 157 | #[interrupt] | ||
| 158 | fn $irq() { | ||
| 159 | DRIVER.on_interrupt() | ||
| 160 | } | ||
| 161 | }; | ||
| 162 | (TIM24, timer, $block:ident, CC, $irq:ident) => { | ||
| 163 | #[cfg(time_driver_tim24)] | ||
| 164 | #[cfg(feature = "rt")] | ||
| 165 | #[interrupt] | ||
| 166 | fn $irq() { | ||
| 167 | DRIVER.on_interrupt() | ||
| 168 | } | ||
| 169 | }; | ||
| 170 | } | ||
| 171 | |||
| 172 | fn regs_gp16() -> TimGp16 { | 59 | fn regs_gp16() -> TimGp16 { |
| 173 | unsafe { TimGp16::from_ptr(T::regs()) } | 60 | unsafe { TimGp16::from_ptr(T::regs()) } |
| 174 | } | 61 | } |
| @@ -194,6 +81,11 @@ fn calc_now(period: u32, counter: u16) -> u64 { | |||
| 194 | ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64) | 81 | ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64) |
| 195 | } | 82 | } |
| 196 | 83 | ||
| 84 | #[cfg(feature = "low-power")] | ||
| 85 | fn calc_period_counter(ticks: u64) -> (u32, u16) { | ||
| 86 | (2 * (ticks >> 16) as u32 + (ticks as u16 >= 0x8000) as u32, ticks as u16) | ||
| 87 | } | ||
| 88 | |||
| 197 | struct AlarmState { | 89 | struct AlarmState { |
| 198 | timestamp: Cell<u64>, | 90 | timestamp: Cell<u64>, |
| 199 | } | 91 | } |
| @@ -213,7 +105,13 @@ pub(crate) struct RtcDriver { | |||
| 213 | period: AtomicU32, | 105 | period: AtomicU32, |
| 214 | alarm: Mutex<CriticalSectionRawMutex, AlarmState>, | 106 | alarm: Mutex<CriticalSectionRawMutex, AlarmState>, |
| 215 | #[cfg(feature = "low-power")] | 107 | #[cfg(feature = "low-power")] |
| 216 | rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>, | 108 | pub(crate) rtc: Mutex<CriticalSectionRawMutex, RefCell<Option<Rtc>>>, |
| 109 | #[cfg(feature = "low-power")] | ||
| 110 | /// The minimum pause time beyond which the executor will enter a low-power state. | ||
| 111 | min_stop_pause: Mutex<CriticalSectionRawMutex, Cell<embassy_time::Duration>>, | ||
| 112 | /// Saved count for the timer (its value is lost when entering STOP2) | ||
| 113 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 114 | saved_count: AtomicU16, | ||
| 217 | queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>, | 115 | queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>, |
| 218 | } | 116 | } |
| 219 | 117 | ||
| @@ -221,12 +119,18 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { | |||
| 221 | period: AtomicU32::new(0), | 119 | period: AtomicU32::new(0), |
| 222 | alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), | 120 | alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), |
| 223 | #[cfg(feature = "low-power")] | 121 | #[cfg(feature = "low-power")] |
| 224 | rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), | 122 | rtc: Mutex::const_new(CriticalSectionRawMutex::new(), RefCell::new(None)), |
| 123 | #[cfg(feature = "low-power")] | ||
| 124 | min_stop_pause: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(embassy_time::Duration::from_millis(0))), | ||
| 125 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 126 | saved_count: AtomicU16::new(0), | ||
| 225 | queue: Mutex::new(RefCell::new(Queue::new())) | 127 | queue: Mutex::new(RefCell::new(Queue::new())) |
| 226 | }); | 128 | }); |
| 227 | 129 | ||
| 228 | impl RtcDriver { | 130 | impl RtcDriver { |
| 229 | fn init(&'static self, cs: critical_section::CriticalSection) { | 131 | /// initialize the timer, but don't start it. Used for chips like stm32wle5 |
| 132 | /// for low power where the timer config is lost in STOP2. | ||
| 133 | pub(crate) fn init_timer(&'static self, cs: critical_section::CriticalSection) { | ||
| 230 | let r = regs_gp16(); | 134 | let r = regs_gp16(); |
| 231 | 135 | ||
| 232 | rcc::enable_and_reset_with_cs::<T>(cs); | 136 | rcc::enable_and_reset_with_cs::<T>(cs); |
| @@ -259,13 +163,23 @@ impl RtcDriver { | |||
| 259 | w.set_ccie(0, true); | 163 | w.set_ccie(0, true); |
| 260 | }); | 164 | }); |
| 261 | 165 | ||
| 166 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 167 | r.cnt().write(|w| w.set_cnt(self.saved_count.load(Ordering::SeqCst))); | ||
| 168 | |||
| 262 | <T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend(); | 169 | <T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend(); |
| 263 | unsafe { <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable() }; | 170 | <T as CoreInstance>::UpdateInterrupt::unpend(); |
| 171 | unsafe { | ||
| 172 | <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable(); | ||
| 173 | <T as CoreInstance>::UpdateInterrupt::enable(); | ||
| 174 | } | ||
| 175 | } | ||
| 264 | 176 | ||
| 265 | r.cr1().modify(|w| w.set_cen(true)); | 177 | fn init(&'static self, cs: CriticalSection) { |
| 178 | self.init_timer(cs); | ||
| 179 | regs_gp16().cr1().modify(|w| w.set_cen(true)); | ||
| 266 | } | 180 | } |
| 267 | 181 | ||
| 268 | fn on_interrupt(&self) { | 182 | pub(crate) fn on_interrupt(&self) { |
| 269 | let r = regs_gp16(); | 183 | let r = regs_gp16(); |
| 270 | 184 | ||
| 271 | critical_section::with(|cs| { | 185 | critical_section::with(|cs| { |
| @@ -338,34 +252,10 @@ impl RtcDriver { | |||
| 338 | #[cfg(feature = "low-power")] | 252 | #[cfg(feature = "low-power")] |
| 339 | /// Add the given offset to the current time | 253 | /// Add the given offset to the current time |
| 340 | fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) { | 254 | fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) { |
| 341 | let offset = offset.as_ticks(); | 255 | let (period, counter) = calc_period_counter(self.now() + offset.as_ticks()); |
| 342 | let cnt = regs_gp16().cnt().read().cnt() as u32; | ||
| 343 | let period = self.period.load(Ordering::SeqCst); | ||
| 344 | |||
| 345 | // Correct the race, if it exists | ||
| 346 | let period = if period & 1 == 1 && cnt < u16::MAX as u32 / 2 { | ||
| 347 | period + 1 | ||
| 348 | } else { | ||
| 349 | period | ||
| 350 | }; | ||
| 351 | |||
| 352 | // Normalize to the full overflow | ||
| 353 | let period = (period / 2) * 2; | ||
| 354 | |||
| 355 | // Add the offset | ||
| 356 | let period = period + 2 * (offset / u16::MAX as u64) as u32; | ||
| 357 | let cnt = cnt + (offset % u16::MAX as u64) as u32; | ||
| 358 | |||
| 359 | let (cnt, period) = if cnt > u16::MAX as u32 { | ||
| 360 | (cnt - u16::MAX as u32, period + 2) | ||
| 361 | } else { | ||
| 362 | (cnt, period) | ||
| 363 | }; | ||
| 364 | |||
| 365 | let period = if cnt > u16::MAX as u32 / 2 { period + 1 } else { period }; | ||
| 366 | 256 | ||
| 367 | self.period.store(period, Ordering::SeqCst); | 257 | self.period.store(period, Ordering::SeqCst); |
| 368 | regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); | 258 | regs_gp16().cnt().write(|w| w.set_cnt(counter)); |
| 369 | 259 | ||
| 370 | // Now, recompute alarm | 260 | // Now, recompute alarm |
| 371 | let alarm = self.alarm.borrow(cs); | 261 | let alarm = self.alarm.borrow(cs); |
| @@ -379,70 +269,70 @@ impl RtcDriver { | |||
| 379 | #[cfg(feature = "low-power")] | 269 | #[cfg(feature = "low-power")] |
| 380 | /// Stop the wakeup alarm, if enabled, and add the appropriate offset | 270 | /// Stop the wakeup alarm, if enabled, and add the appropriate offset |
| 381 | fn stop_wakeup_alarm(&self, cs: CriticalSection) { | 271 | fn stop_wakeup_alarm(&self, cs: CriticalSection) { |
| 382 | if let Some(offset) = self.rtc.borrow(cs).get().unwrap().stop_wakeup_alarm(cs) { | 272 | if !regs_gp16().cr1().read().cen() |
| 273 | && let Some(offset) = self.rtc.borrow(cs).borrow_mut().as_mut().unwrap().stop_wakeup_alarm(cs) | ||
| 274 | { | ||
| 383 | self.add_time(offset, cs); | 275 | self.add_time(offset, cs); |
| 384 | } | 276 | } |
| 385 | } | 277 | } |
| 386 | 278 | ||
| 387 | /* | 279 | /* |
| 388 | Low-power public functions: all create a critical section | 280 | Low-power public functions: all require a critical section |
| 389 | */ | 281 | */ |
| 390 | #[cfg(feature = "low-power")] | 282 | #[cfg(feature = "low-power")] |
| 391 | /// Set the rtc but panic if it's already been set | 283 | 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) { | 284 | 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 | } | 285 | } |
| 399 | 286 | ||
| 400 | #[cfg(feature = "low-power")] | 287 | #[cfg(feature = "low-power")] |
| 401 | /// The minimum pause time beyond which the executor will enter a low-power state. | 288 | /// 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); | 289 | pub(crate) fn set_rtc(&self, cs: CriticalSection, mut rtc: Rtc) { |
| 290 | rtc.stop_wakeup_alarm(cs); | ||
| 291 | |||
| 292 | assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none()); | ||
| 293 | } | ||
| 403 | 294 | ||
| 404 | #[cfg(feature = "low-power")] | 295 | #[cfg(feature = "low-power")] |
| 405 | /// Pause the timer if ready; return err if not | 296 | /// Pause the timer if ready; return err if not |
| 406 | pub(crate) fn pause_time(&self) -> Result<(), ()> { | 297 | pub(crate) fn pause_time(&self, cs: CriticalSection) -> Result<(), ()> { |
| 407 | critical_section::with(|cs| { | 298 | self.stop_wakeup_alarm(cs); |
| 408 | /* | 299 | |
| 409 | If the wakeup timer is currently running, then we need to stop it and | 300 | let time_until_next_alarm = self.time_until_next_alarm(cs); |
| 410 | add the elapsed time to the current time, as this will impact the result | 301 | if time_until_next_alarm < self.min_stop_pause.borrow(cs).get() { |
| 411 | of `time_until_next_alarm`. | 302 | trace!( |
| 412 | */ | 303 | "time_until_next_alarm < self.min_stop_pause ({})", |
| 413 | self.stop_wakeup_alarm(cs); | 304 | time_until_next_alarm |
| 414 | 305 | ); | |
| 415 | let time_until_next_alarm = self.time_until_next_alarm(cs); | 306 | Err(()) |
| 416 | if time_until_next_alarm < Self::MIN_STOP_PAUSE { | 307 | } else { |
| 417 | Err(()) | 308 | self.rtc |
| 418 | } else { | 309 | .borrow(cs) |
| 419 | self.rtc | 310 | .borrow_mut() |
| 420 | .borrow(cs) | 311 | .as_mut() |
| 421 | .get() | 312 | .unwrap() |
| 422 | .unwrap() | 313 | .start_wakeup_alarm(time_until_next_alarm, cs); |
| 423 | .start_wakeup_alarm(time_until_next_alarm, cs); | 314 | |
| 424 | 315 | regs_gp16().cr1().modify(|w| w.set_cen(false)); | |
| 425 | regs_gp16().cr1().modify(|w| w.set_cen(false)); | 316 | // save the count for the timer as its lost in STOP2 for stm32wlex |
| 426 | 317 | #[cfg(stm32wlex)] | |
| 427 | Ok(()) | 318 | self.saved_count |
| 428 | } | 319 | .store(regs_gp16().cnt().read().cnt() as u16, Ordering::SeqCst); |
| 429 | }) | 320 | Ok(()) |
| 321 | } | ||
| 430 | } | 322 | } |
| 431 | 323 | ||
| 432 | #[cfg(feature = "low-power")] | 324 | #[cfg(feature = "low-power")] |
| 433 | /// Resume the timer with the given offset | 325 | /// Resume the timer with the given offset |
| 434 | pub(crate) fn resume_time(&self) { | 326 | pub(crate) fn resume_time(&self, cs: CriticalSection) { |
| 435 | if regs_gp16().cr1().read().cen() { | 327 | self.stop_wakeup_alarm(cs); |
| 436 | // Time isn't currently stopped | ||
| 437 | 328 | ||
| 438 | return; | 329 | regs_gp16().cr1().modify(|w| w.set_cen(true)); |
| 439 | } | 330 | } |
| 440 | |||
| 441 | critical_section::with(|cs| { | ||
| 442 | self.stop_wakeup_alarm(cs); | ||
| 443 | 331 | ||
| 444 | regs_gp16().cr1().modify(|w| w.set_cen(true)); | 332 | #[cfg(feature = "low-power")] |
| 445 | }) | 333 | /// Returns whether time is currently stopped |
| 334 | pub(crate) fn is_stopped(&self) -> bool { | ||
| 335 | !regs_gp16().cr1().read().cen() | ||
| 446 | } | 336 | } |
| 447 | 337 | ||
| 448 | fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { | 338 | fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { |
| @@ -513,8 +403,7 @@ impl Driver for RtcDriver { | |||
| 513 | } | 403 | } |
| 514 | } | 404 | } |
| 515 | 405 | ||
| 516 | #[cfg(feature = "low-power")] | 406 | pub(crate) const fn get_driver() -> &'static RtcDriver { |
| 517 | pub(crate) fn get_driver() -> &'static RtcDriver { | ||
| 518 | &DRIVER | 407 | &DRIVER |
| 519 | } | 408 | } |
| 520 | 409 | ||
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 484aae1d0..620d7858e 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs | |||
| @@ -2,16 +2,17 @@ | |||
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | 4 | ||
| 5 | pub use stm32_metapac::timer::vals::{Ckd, Ossi, Ossr}; | ||
| 6 | |||
| 7 | use super::low_level::{CountingMode, OutputPolarity, Timer}; | 5 | use super::low_level::{CountingMode, OutputPolarity, Timer}; |
| 8 | use super::simple_pwm::PwmPin; | 6 | use super::simple_pwm::PwmPin; |
| 9 | use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin}; | 7 | use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin}; |
| 10 | use crate::gpio::{AnyPin, OutputType}; | 8 | use crate::Peri; |
| 9 | use crate::dma::word::Word; | ||
| 10 | use crate::gpio::{AfType, AnyPin, OutputType}; | ||
| 11 | pub use crate::pac::timer::vals::{Ccds, Ckd, Mms2, Ossi, Ossr}; | ||
| 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 | use crate::timer::simple_pwm::PwmPinConfig; | ||
| 15 | 16 | ||
| 16 | /// Complementary PWM pin wrapper. | 17 | /// Complementary PWM pin wrapper. |
| 17 | /// | 18 | /// |
| @@ -27,9 +28,27 @@ impl<'d, T: AdvancedInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!( | |||
| 27 | pub fn new(pin: Peri<'d, if_afio!(impl TimerComplementaryPin<T, C, A>)>, output_type: OutputType) -> Self { | 28 | pub fn new(pin: Peri<'d, if_afio!(impl TimerComplementaryPin<T, C, A>)>, output_type: OutputType) -> Self { |
| 28 | critical_section::with(|_| { | 29 | critical_section::with(|_| { |
| 29 | pin.set_low(); | 30 | pin.set_low(); |
| 30 | set_as_af!( | 31 | set_as_af!(pin, AfType::output(output_type, crate::gpio::Speed::VeryHigh)); |
| 31 | pin, | 32 | }); |
| 32 | crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh) | 33 | ComplementaryPwmPin { |
| 34 | pin: pin.into(), | ||
| 35 | phantom: PhantomData, | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | /// Create a new PWM pin instance with config. | ||
| 40 | pub fn new_with_config( | ||
| 41 | pin: Peri<'d, if_afio!(impl TimerComplementaryPin<T, C, A>)>, | ||
| 42 | pin_config: PwmPinConfig, | ||
| 43 | ) -> Self { | ||
| 44 | critical_section::with(|_| { | ||
| 45 | pin.set_low(); | ||
| 46 | #[cfg(gpio_v1)] | ||
| 47 | set_as_af!(pin, AfType::output(pin_config.output_type, pin_config.speed)); | ||
| 48 | #[cfg(gpio_v2)] | ||
| 49 | pin.set_as_af( | ||
| 50 | pin.af_num(), | ||
| 51 | AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull), | ||
| 33 | ); | 52 | ); |
| 34 | }); | 53 | }); |
| 35 | ComplementaryPwmPin { | 54 | ComplementaryPwmPin { |
| @@ -77,8 +96,6 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 77 | 96 | ||
| 78 | this.inner.set_counting_mode(counting_mode); | 97 | this.inner.set_counting_mode(counting_mode); |
| 79 | this.set_frequency(freq); | 98 | this.set_frequency(freq); |
| 80 | this.inner.start(); | ||
| 81 | |||
| 82 | this.inner.enable_outputs(); | 99 | this.inner.enable_outputs(); |
| 83 | 100 | ||
| 84 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] | 101 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] |
| @@ -89,6 +106,10 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 89 | }); | 106 | }); |
| 90 | this.inner.set_autoreload_preload(true); | 107 | this.inner.set_autoreload_preload(true); |
| 91 | 108 | ||
| 109 | // Generate update event so pre-load registers are written to the shadow registers | ||
| 110 | this.inner.generate_update_event(); | ||
| 111 | this.inner.start(); | ||
| 112 | |||
| 92 | this | 113 | this |
| 93 | } | 114 | } |
| 94 | 115 | ||
| @@ -136,6 +157,16 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 136 | self.inner.get_moe() | 157 | self.inner.get_moe() |
| 137 | } | 158 | } |
| 138 | 159 | ||
| 160 | /// Set Master Slave Mode 2 | ||
| 161 | pub fn set_mms2(&mut self, mms2: Mms2) { | ||
| 162 | self.inner.set_mms2_selection(mms2); | ||
| 163 | } | ||
| 164 | |||
| 165 | /// Set Repetition Counter | ||
| 166 | pub fn set_repetition_counter(&mut self, val: u16) { | ||
| 167 | self.inner.set_repetition_counter(val); | ||
| 168 | } | ||
| 169 | |||
| 139 | /// Enable the given channel. | 170 | /// Enable the given channel. |
| 140 | pub fn enable(&mut self, channel: Channel) { | 171 | pub fn enable(&mut self, channel: Channel) { |
| 141 | self.inner.enable_channel(channel, true); | 172 | self.inner.enable_channel(channel, true); |
| @@ -150,8 +181,8 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 150 | 181 | ||
| 151 | /// Set PWM frequency. | 182 | /// Set PWM frequency. |
| 152 | /// | 183 | /// |
| 153 | /// Note: when you call this, the max duty value changes, so you will have to | 184 | /// Note: that the frequency will not be applied in the timer until an update event |
| 154 | /// call `set_duty` on all channels with the duty calculated based on the new max duty. | 185 | /// occurs. |
| 155 | pub fn set_frequency(&mut self, freq: Hertz) { | 186 | pub fn set_frequency(&mut self, freq: Hertz) { |
| 156 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { | 187 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { |
| 157 | 2u8 | 188 | 2u8 |
| @@ -164,20 +195,20 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 164 | /// Get max duty value. | 195 | /// Get max duty value. |
| 165 | /// | 196 | /// |
| 166 | /// This value depends on the configured frequency and the timer's clock rate from RCC. | 197 | /// This value depends on the configured frequency and the timer's clock rate from RCC. |
| 167 | pub fn get_max_duty(&self) -> u16 { | 198 | pub fn get_max_duty(&self) -> u32 { |
| 168 | if self.inner.get_counting_mode().is_center_aligned() { | 199 | if self.inner.get_counting_mode().is_center_aligned() { |
| 169 | self.inner.get_max_compare_value() as u16 | 200 | self.inner.get_max_compare_value().into() |
| 170 | } else { | 201 | } else { |
| 171 | self.inner.get_max_compare_value() as u16 + 1 | 202 | self.inner.get_max_compare_value().into() + 1 |
| 172 | } | 203 | } |
| 173 | } | 204 | } |
| 174 | 205 | ||
| 175 | /// Set the duty for a given channel. | 206 | /// Set the duty for a given channel. |
| 176 | /// | 207 | /// |
| 177 | /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. | 208 | /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. |
| 178 | pub fn set_duty(&mut self, channel: Channel, duty: u16) { | 209 | pub fn set_duty(&mut self, channel: Channel, duty: u32) { |
| 179 | assert!(duty <= self.get_max_duty()); | 210 | assert!(duty <= self.get_max_duty()); |
| 180 | self.inner.set_compare_value(channel, duty as _) | 211 | self.inner.set_compare_value(channel, unwrap!(duty.try_into())) |
| 181 | } | 212 | } |
| 182 | 213 | ||
| 183 | /// Set the output polarity for a given channel. | 214 | /// Set the output polarity for a given channel. |
| @@ -207,61 +238,88 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 207 | /// Generate a sequence of PWM waveform | 238 | /// Generate a sequence of PWM waveform |
| 208 | /// | 239 | /// |
| 209 | /// Note: | 240 | /// Note: |
| 210 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | 241 | /// The DMA channel provided does not need to correspond to the requested channel. |
| 211 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { | 242 | pub async fn waveform<C: TimerChannel, W: Word + Into<T::Word>>( |
| 212 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | 243 | &mut self, |
| 213 | let req = dma.request(); | 244 | dma: Peri<'_, impl super::Dma<T, C>>, |
| 214 | 245 | channel: Channel, | |
| 215 | let original_duty_state = self.inner.get_compare_value(channel); | 246 | duty: &[W], |
| 216 | let original_enable_state = self.inner.get_channel_enable_state(channel); | 247 | ) { |
| 217 | let original_update_dma_state = self.inner.get_update_dma_state(); | 248 | self.inner.enable_channel(channel, true); |
| 218 | 249 | self.inner.enable_channel(C::CHANNEL, true); | |
| 219 | if !original_update_dma_state { | 250 | self.inner.clamp_compare_value::<W>(channel); |
| 220 | self.inner.enable_update_dma(true); | 251 | self.inner.set_cc_dma_selection(Ccds::ON_UPDATE); |
| 221 | } | 252 | self.inner.set_cc_dma_enable_state(C::CHANNEL, true); |
| 222 | 253 | self.inner.setup_channel_update_dma(dma, channel, duty).await; | |
| 223 | if !original_enable_state { | 254 | self.inner.set_cc_dma_enable_state(C::CHANNEL, false); |
| 224 | self.inner.enable_channel(channel, true); | 255 | } |
| 225 | } | ||
| 226 | |||
| 227 | unsafe { | ||
| 228 | #[cfg(not(any(bdma, gpdma)))] | ||
| 229 | use crate::dma::{Burst, FifoThreshold}; | ||
| 230 | use crate::dma::{Transfer, TransferOptions}; | ||
| 231 | |||
| 232 | let dma_transfer_option = TransferOptions { | ||
| 233 | #[cfg(not(any(bdma, gpdma)))] | ||
| 234 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 235 | #[cfg(not(any(bdma, gpdma)))] | ||
| 236 | mburst: Burst::Incr8, | ||
| 237 | ..Default::default() | ||
| 238 | }; | ||
| 239 | |||
| 240 | Transfer::new_write( | ||
| 241 | dma, | ||
| 242 | req, | ||
| 243 | duty, | ||
| 244 | self.inner.regs_gp16().ccr(channel.index()).as_ptr() as *mut u16, | ||
| 245 | dma_transfer_option, | ||
| 246 | ) | ||
| 247 | .await | ||
| 248 | }; | ||
| 249 | |||
| 250 | // restore output compare state | ||
| 251 | if !original_enable_state { | ||
| 252 | self.inner.enable_channel(channel, false); | ||
| 253 | } | ||
| 254 | 256 | ||
| 255 | self.inner.set_compare_value(channel, original_duty_state); | 257 | /// Generate a sequence of PWM waveform |
| 258 | /// | ||
| 259 | /// Note: | ||
| 260 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | ||
| 261 | pub async fn waveform_up<W: Word + Into<T::Word>>( | ||
| 262 | &mut self, | ||
| 263 | dma: Peri<'_, impl super::UpDma<T>>, | ||
| 264 | channel: Channel, | ||
| 265 | duty: &[W], | ||
| 266 | ) { | ||
| 267 | self.inner.enable_channel(channel, true); | ||
| 268 | self.inner.clamp_compare_value::<W>(channel); | ||
| 269 | self.inner.enable_update_dma(true); | ||
| 270 | self.inner.setup_update_dma(dma, channel, duty).await; | ||
| 271 | self.inner.enable_update_dma(false); | ||
| 272 | } | ||
| 256 | 273 | ||
| 257 | // Since DMA is closed before timer update event trigger DMA is turn off, | 274 | /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. |
| 258 | // this can almost always trigger a DMA FIFO error. | 275 | /// |
| 259 | // | 276 | /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers |
| 260 | // optional TODO: | 277 | /// in sequence on each update event (UEV). The data is written via the DMAR register using the |
| 261 | // clean FEIF after disable UDE | 278 | /// DMA base address (DBA) and burst length (DBL) configured in the DCR register. |
| 262 | if !original_update_dma_state { | 279 | /// |
| 263 | self.inner.enable_update_dma(false); | 280 | /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row |
| 264 | } | 281 | /// represents a single update event and each column corresponds to a specific timer channel (starting |
| 282 | /// from `starting_channel` up to and including `ending_channel`). | ||
| 283 | /// | ||
| 284 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: | ||
| 285 | /// | ||
| 286 | /// ```rust,ignore | ||
| 287 | /// let dma_buf: [u16; 16] = [ | ||
| 288 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 | ||
| 289 | /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 | ||
| 290 | /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 | ||
| 291 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 | ||
| 292 | /// ]; | ||
| 293 | /// ``` | ||
| 294 | /// | ||
| 295 | /// Each group of `N` values (where `N` is number of channels) is transferred on one update event, | ||
| 296 | /// updating the duty cycles of all selected channels simultaneously. | ||
| 297 | /// | ||
| 298 | /// Note: | ||
| 299 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. | ||
| 300 | /// Also be aware that embassy timers use one of timers internally. It is possible to | ||
| 301 | /// switch this timer by using `time-driver-timX` feature. | ||
| 302 | /// | ||
| 303 | pub async fn waveform_up_multi_channel<W: Word + Into<T::Word>>( | ||
| 304 | &mut self, | ||
| 305 | dma: Peri<'_, impl super::UpDma<T>>, | ||
| 306 | starting_channel: Channel, | ||
| 307 | ending_channel: Channel, | ||
| 308 | duty: &[W], | ||
| 309 | ) { | ||
| 310 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] | ||
| 311 | .iter() | ||
| 312 | .filter(|ch| ch.index() >= starting_channel.index()) | ||
| 313 | .filter(|ch| ch.index() <= ending_channel.index()) | ||
| 314 | .for_each(|ch| { | ||
| 315 | self.inner.enable_channel(*ch, true); | ||
| 316 | self.inner.clamp_compare_value::<W>(*ch); | ||
| 317 | }); | ||
| 318 | self.inner.enable_update_dma(true); | ||
| 319 | self.inner | ||
| 320 | .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) | ||
| 321 | .await; | ||
| 322 | self.inner.enable_update_dma(false); | ||
| 265 | } | 323 | } |
| 266 | } | 324 | } |
| 267 | 325 | ||
| @@ -285,20 +343,20 @@ impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm< | |||
| 285 | } | 343 | } |
| 286 | 344 | ||
| 287 | fn get_duty(&self, channel: Self::Channel) -> Self::Duty { | 345 | fn get_duty(&self, channel: Self::Channel) -> Self::Duty { |
| 288 | self.inner.get_compare_value(channel) as u16 | 346 | unwrap!(self.inner.get_compare_value(channel).try_into()) |
| 289 | } | 347 | } |
| 290 | 348 | ||
| 291 | fn get_max_duty(&self) -> Self::Duty { | 349 | fn get_max_duty(&self) -> Self::Duty { |
| 292 | if self.inner.get_counting_mode().is_center_aligned() { | 350 | if self.inner.get_counting_mode().is_center_aligned() { |
| 293 | self.inner.get_max_compare_value() as u16 | 351 | unwrap!(self.inner.get_max_compare_value().try_into()) |
| 294 | } else { | 352 | } else { |
| 295 | self.inner.get_max_compare_value() as u16 + 1 | 353 | unwrap!(self.inner.get_max_compare_value().try_into()) + 1 |
| 296 | } | 354 | } |
| 297 | } | 355 | } |
| 298 | 356 | ||
| 299 | fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { | 357 | fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { |
| 300 | assert!(duty <= self.get_max_duty()); | 358 | assert!(duty <= unwrap!(self.get_max_duty().try_into())); |
| 301 | self.inner.set_compare_value(channel, duty as u32) | 359 | self.inner.set_compare_value(channel, unwrap!(duty.try_into())) |
| 302 | } | 360 | } |
| 303 | 361 | ||
| 304 | fn set_period<P>(&mut self, period: P) | 362 | fn set_period<P>(&mut self, period: P) |
| @@ -388,7 +446,7 @@ fn compute_dead_time_value(value: u16) -> (Ckd, u8) { | |||
| 388 | 446 | ||
| 389 | #[cfg(test)] | 447 | #[cfg(test)] |
| 390 | mod tests { | 448 | mod tests { |
| 391 | use super::{compute_dead_time_value, Ckd}; | 449 | use super::{Ckd, compute_dead_time_value}; |
| 392 | 450 | ||
| 393 | #[test] | 451 | #[test] |
| 394 | fn test_compute_dead_time_value() { | 452 | 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 e6739fbc1..905f2de1a 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 | /// |
| @@ -60,6 +60,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { | |||
| 60 | this.inner.set_counting_mode(counting_mode); | 60 | this.inner.set_counting_mode(counting_mode); |
| 61 | this.inner.set_tick_freq(freq); | 61 | this.inner.set_tick_freq(freq); |
| 62 | this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details | 62 | this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details |
| 63 | this.inner.generate_update_event(); | ||
| 63 | this.inner.start(); | 64 | this.inner.start(); |
| 64 | 65 | ||
| 65 | // enable NVIC interrupt | 66 | // enable NVIC interrupt |
| @@ -96,7 +97,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { | |||
| 96 | 97 | ||
| 97 | /// Get capture value for a channel. | 98 | /// Get capture value for a channel. |
| 98 | pub fn get_capture_value(&self, channel: Channel) -> u32 { | 99 | pub fn get_capture_value(&self, channel: Channel) -> u32 { |
| 99 | self.inner.get_capture_value(channel) | 100 | self.inner.get_capture_value(channel).into() |
| 100 | } | 101 | } |
| 101 | 102 | ||
| 102 | /// Get input interrupt. | 103 | /// Get input interrupt. |
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index ac039bb0d..82e936f3a 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs | |||
| @@ -10,9 +10,10 @@ 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::dma::{self, Transfer, WritableRingBuffer}; | ||
| 16 | use crate::pac::timer::vals; | 17 | use crate::pac::timer::vals; |
| 17 | use crate::rcc; | 18 | use crate::rcc; |
| 18 | use crate::time::Hertz; | 19 | use crate::time::Hertz; |
| @@ -143,20 +144,69 @@ pub enum OutputCompareMode { | |||
| 143 | /// TIMx_CNT<TIMx_CCRx else active. In downcounting, channel is active as long as | 144 | /// TIMx_CNT<TIMx_CCRx else active. In downcounting, channel is active as long as |
| 144 | /// TIMx_CNT>TIMx_CCRx else inactive. | 145 | /// TIMx_CNT>TIMx_CCRx else inactive. |
| 145 | PwmMode2, | 146 | PwmMode2, |
| 146 | // TODO: there's more modes here depending on the chip family. | 147 | |
| 148 | #[cfg(timer_v2)] | ||
| 149 | /// In up-counting mode, the channel is active until a trigger | ||
| 150 | /// event is detected (on tim_trgi signal). Then, a comparison is performed as in PWM | ||
| 151 | /// mode 1 and the channels becomes active again at the next update. In down-counting | ||
| 152 | /// mode, the channel is inactive until a trigger event is detected (on tim_trgi signal). | ||
| 153 | /// Then, a comparison is performed as in PWM mode 1 and the channels becomes | ||
| 154 | /// inactive again at the next update. | ||
| 155 | OnePulseMode1, | ||
| 156 | |||
| 157 | #[cfg(timer_v2)] | ||
| 158 | /// In up-counting mode, the channel is inactive until a | ||
| 159 | /// trigger event is detected (on tim_trgi signal). Then, a comparison is performed as in | ||
| 160 | /// PWM mode 2 and the channels becomes inactive again at the next update. In down | ||
| 161 | /// counting mode, the channel is active until a trigger event is detected (on tim_trgi | ||
| 162 | /// signal). Then, a comparison is performed as in PWM mode 1 and the channels | ||
| 163 | /// becomes active again at the next update. | ||
| 164 | OnePulseMode2, | ||
| 165 | |||
| 166 | #[cfg(timer_v2)] | ||
| 167 | /// Combined PWM mode 1 - tim_oc1ref has the same behavior as in PWM mode 1. | ||
| 168 | /// tim_oc1refc is the logical OR between tim_oc1ref and tim_oc2ref. | ||
| 169 | CombinedPwmMode1, | ||
| 170 | |||
| 171 | #[cfg(timer_v2)] | ||
| 172 | /// Combined PWM mode 2 - tim_oc1ref has the same behavior as in PWM mode 2. | ||
| 173 | /// tim_oc1refc is the logical AND between tim_oc1ref and tim_oc2ref. | ||
| 174 | CombinedPwmMode2, | ||
| 175 | |||
| 176 | #[cfg(timer_v2)] | ||
| 177 | /// tim_oc1ref has the same behavior as in PWM mode 1. tim_oc1refc outputs tim_oc1ref | ||
| 178 | /// when the counter is counting up, tim_oc2ref when it is counting down. | ||
| 179 | AsymmetricPwmMode1, | ||
| 180 | |||
| 181 | #[cfg(timer_v2)] | ||
| 182 | /// tim_oc1ref has the same behavior as in PWM mode 2. tim_oc1refc outputs tim_oc1ref | ||
| 183 | /// when the counter is counting up, tim_oc2ref when it is counting down. | ||
| 184 | AsymmetricPwmMode2, | ||
| 147 | } | 185 | } |
| 148 | 186 | ||
| 149 | impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm { | 187 | impl From<OutputCompareMode> for crate::pac::timer::vals::Ocm { |
| 150 | fn from(mode: OutputCompareMode) -> Self { | 188 | fn from(mode: OutputCompareMode) -> Self { |
| 151 | match mode { | 189 | match mode { |
| 152 | OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN, | 190 | OutputCompareMode::Frozen => crate::pac::timer::vals::Ocm::FROZEN, |
| 153 | OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVE_ON_MATCH, | 191 | OutputCompareMode::ActiveOnMatch => crate::pac::timer::vals::Ocm::ACTIVE_ON_MATCH, |
| 154 | OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVE_ON_MATCH, | 192 | OutputCompareMode::InactiveOnMatch => crate::pac::timer::vals::Ocm::INACTIVE_ON_MATCH, |
| 155 | OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE, | 193 | OutputCompareMode::Toggle => crate::pac::timer::vals::Ocm::TOGGLE, |
| 156 | OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCE_INACTIVE, | 194 | OutputCompareMode::ForceInactive => crate::pac::timer::vals::Ocm::FORCE_INACTIVE, |
| 157 | OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCE_ACTIVE, | 195 | OutputCompareMode::ForceActive => crate::pac::timer::vals::Ocm::FORCE_ACTIVE, |
| 158 | OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWM_MODE1, | 196 | OutputCompareMode::PwmMode1 => crate::pac::timer::vals::Ocm::PWM_MODE1, |
| 159 | OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWM_MODE2, | 197 | OutputCompareMode::PwmMode2 => crate::pac::timer::vals::Ocm::PWM_MODE2, |
| 198 | #[cfg(timer_v2)] | ||
| 199 | OutputCompareMode::OnePulseMode1 => crate::pac::timer::vals::Ocm::RETRIGERRABLE_OPM_MODE_1, | ||
| 200 | #[cfg(timer_v2)] | ||
| 201 | OutputCompareMode::OnePulseMode2 => crate::pac::timer::vals::Ocm::RETRIGERRABLE_OPM_MODE_2, | ||
| 202 | #[cfg(timer_v2)] | ||
| 203 | OutputCompareMode::CombinedPwmMode1 => crate::pac::timer::vals::Ocm::COMBINED_PWM_MODE_1, | ||
| 204 | #[cfg(timer_v2)] | ||
| 205 | OutputCompareMode::CombinedPwmMode2 => crate::pac::timer::vals::Ocm::COMBINED_PWM_MODE_2, | ||
| 206 | #[cfg(timer_v2)] | ||
| 207 | OutputCompareMode::AsymmetricPwmMode1 => crate::pac::timer::vals::Ocm::ASYMMETRIC_PWM_MODE_1, | ||
| 208 | #[cfg(timer_v2)] | ||
| 209 | OutputCompareMode::AsymmetricPwmMode2 => crate::pac::timer::vals::Ocm::ASYMMETRIC_PWM_MODE_2, | ||
| 160 | } | 210 | } |
| 161 | } | 211 | } |
| 162 | } | 212 | } |
| @@ -218,11 +268,27 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 218 | unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) } | 268 | unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) } |
| 219 | } | 269 | } |
| 220 | 270 | ||
| 271 | #[cfg(stm32l0)] | ||
| 272 | fn regs_gp32_unchecked(&self) -> crate::pac::timer::TimGp16 { | ||
| 273 | unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) } | ||
| 274 | } | ||
| 275 | |||
| 221 | /// Start the timer. | 276 | /// Start the timer. |
| 222 | pub fn start(&self) { | 277 | pub fn start(&self) { |
| 223 | self.regs_core().cr1().modify(|r| r.set_cen(true)); | 278 | self.regs_core().cr1().modify(|r| r.set_cen(true)); |
| 224 | } | 279 | } |
| 225 | 280 | ||
| 281 | /// Generate timer update event from software. | ||
| 282 | /// | ||
| 283 | /// Set URS to avoid generating interrupt or DMA request. This update event is only | ||
| 284 | /// used to load value from pre-load registers. If called when the timer is running, | ||
| 285 | /// it may disrupt the output waveform. | ||
| 286 | pub fn generate_update_event(&self) { | ||
| 287 | self.regs_core().cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | ||
| 288 | self.regs_core().egr().write(|r| r.set_ug(true)); | ||
| 289 | self.regs_core().cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | ||
| 290 | } | ||
| 291 | |||
| 226 | /// Stop the timer. | 292 | /// Stop the timer. |
| 227 | pub fn stop(&self) { | 293 | pub fn stop(&self) { |
| 228 | self.regs_core().cr1().modify(|r| r.set_cen(false)); | 294 | self.regs_core().cr1().modify(|r| r.set_cen(false)); |
| @@ -235,7 +301,12 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 235 | 301 | ||
| 236 | /// get the capability of the timer | 302 | /// get the capability of the timer |
| 237 | pub fn bits(&self) -> TimerBits { | 303 | pub fn bits(&self) -> TimerBits { |
| 238 | T::BITS | 304 | match T::Word::bits() { |
| 305 | 16 => TimerBits::Bits16, | ||
| 306 | #[cfg(not(stm32l0))] | ||
| 307 | 32 => TimerBits::Bits32, | ||
| 308 | _ => unreachable!(), | ||
| 309 | } | ||
| 239 | } | 310 | } |
| 240 | 311 | ||
| 241 | /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. | 312 | /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. |
| @@ -245,18 +316,10 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 245 | /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved | 316 | /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved |
| 246 | /// because it needs to count up and down. | 317 | /// because it needs to count up and down. |
| 247 | pub fn set_frequency(&self, frequency: Hertz) { | 318 | pub fn set_frequency(&self, frequency: Hertz) { |
| 248 | match T::BITS { | 319 | self.set_frequency_internal(frequency, T::Word::bits()); |
| 249 | TimerBits::Bits16 => { | ||
| 250 | self.set_frequency_internal(frequency, 16); | ||
| 251 | } | ||
| 252 | #[cfg(not(stm32l0))] | ||
| 253 | TimerBits::Bits32 => { | ||
| 254 | self.set_frequency_internal(frequency, 32); | ||
| 255 | } | ||
| 256 | } | ||
| 257 | } | 320 | } |
| 258 | 321 | ||
| 259 | pub(crate) fn set_frequency_internal(&self, frequency: Hertz, max_divide_by_bits: u8) { | 322 | pub(crate) fn set_frequency_internal(&self, frequency: Hertz, max_divide_by_bits: usize) { |
| 260 | let f = frequency.0; | 323 | let f = frequency.0; |
| 261 | assert!(f > 0); | 324 | assert!(f > 0); |
| 262 | let timer_f = T::frequency().0; | 325 | let timer_f = T::frequency().0; |
| @@ -265,33 +328,15 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 265 | let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << max_divide_by_bits)).try_into()); | 328 | let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << max_divide_by_bits)).try_into()); |
| 266 | let divide_by = pclk_ticks_per_timer_period / (u64::from(psc) + 1); | 329 | let divide_by = pclk_ticks_per_timer_period / (u64::from(psc) + 1); |
| 267 | 330 | ||
| 268 | match T::BITS { | 331 | // the timer counts `0..=arr`, we want it to count `0..divide_by` |
| 269 | TimerBits::Bits16 => { | 332 | let arr: T::Word = unwrap!(T::Word::try_from(divide_by - 1)); |
| 270 | // the timer counts `0..=arr`, we want it to count `0..divide_by` | ||
| 271 | let arr = unwrap!(u16::try_from(divide_by - 1)); | ||
| 272 | |||
| 273 | let regs = self.regs_core(); | ||
| 274 | regs.psc().write_value(psc); | ||
| 275 | regs.arr().write(|r| r.set_arr(arr)); | ||
| 276 | |||
| 277 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | ||
| 278 | regs.egr().write(|r| r.set_ug(true)); | ||
| 279 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | ||
| 280 | } | ||
| 281 | #[cfg(not(stm32l0))] | ||
| 282 | TimerBits::Bits32 => { | ||
| 283 | // the timer counts `0..=arr`, we want it to count `0..divide_by` | ||
| 284 | let arr: u32 = unwrap!(u32::try_from(divide_by - 1)); | ||
| 285 | |||
| 286 | let regs = self.regs_gp32_unchecked(); | ||
| 287 | regs.psc().write_value(psc); | ||
| 288 | regs.arr().write_value(arr); | ||
| 289 | 333 | ||
| 290 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | 334 | let regs = self.regs_gp32_unchecked(); |
| 291 | regs.egr().write(|r| r.set_ug(true)); | 335 | regs.psc().write_value(psc); |
| 292 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | 336 | #[cfg(stm32l0)] |
| 293 | } | 337 | regs.arr().write(|r| r.set_arr(unwrap!(arr.try_into()))); |
| 294 | } | 338 | #[cfg(not(stm32l0))] |
| 339 | regs.arr().write_value(arr.into()); | ||
| 295 | } | 340 | } |
| 296 | 341 | ||
| 297 | /// Set tick frequency. | 342 | /// Set tick frequency. |
| @@ -340,23 +385,14 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 340 | pub fn get_frequency(&self) -> Hertz { | 385 | pub fn get_frequency(&self) -> Hertz { |
| 341 | let timer_f = T::frequency(); | 386 | let timer_f = T::frequency(); |
| 342 | 387 | ||
| 343 | match T::BITS { | 388 | let regs = self.regs_gp32_unchecked(); |
| 344 | TimerBits::Bits16 => { | 389 | #[cfg(not(stm32l0))] |
| 345 | let regs = self.regs_core(); | 390 | let arr = regs.arr().read(); |
| 346 | let arr = regs.arr().read().arr(); | 391 | #[cfg(stm32l0)] |
| 347 | let psc = regs.psc().read(); | 392 | let arr = regs.arr().read().arr(); |
| 348 | 393 | let psc = regs.psc().read(); | |
| 349 | timer_f / arr / (psc + 1) | ||
| 350 | } | ||
| 351 | #[cfg(not(stm32l0))] | ||
| 352 | TimerBits::Bits32 => { | ||
| 353 | let regs = self.regs_gp32_unchecked(); | ||
| 354 | let arr = regs.arr().read(); | ||
| 355 | let psc = regs.psc().read(); | ||
| 356 | 394 | ||
| 357 | timer_f / arr / (psc + 1) | 395 | timer_f / arr / (psc + 1) |
| 358 | } | ||
| 359 | } | ||
| 360 | } | 396 | } |
| 361 | 397 | ||
| 362 | /// Get the clock frequency of the timer (before prescaler is applied). | 398 | /// Get the clock frequency of the timer (before prescaler is applied). |
| @@ -416,42 +452,29 @@ impl<'d, T: GeneralInstance1Channel> Timer<'d, T> { | |||
| 416 | } | 452 | } |
| 417 | 453 | ||
| 418 | /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. | 454 | /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. |
| 419 | pub fn get_max_compare_value(&self) -> u32 { | 455 | pub fn get_max_compare_value(&self) -> T::Word { |
| 420 | match T::BITS { | 456 | #[cfg(not(stm32l0))] |
| 421 | TimerBits::Bits16 => self.regs_1ch().arr().read().arr() as u32, | 457 | return unwrap!(self.regs_gp32_unchecked().arr().read().try_into()); |
| 422 | #[cfg(not(stm32l0))] | 458 | #[cfg(stm32l0)] |
| 423 | TimerBits::Bits32 => self.regs_gp32_unchecked().arr().read(), | 459 | return unwrap!(self.regs_gp32_unchecked().arr().read().arr().try_into()); |
| 424 | } | ||
| 425 | } | 460 | } |
| 426 | 461 | ||
| 427 | /// Set the max compare value. | 462 | /// Set the max compare value. |
| 428 | /// | 463 | /// |
| 429 | /// An update event is generated to load the new value. The update event is | 464 | /// An update event is generated to load the new value. The update event is |
| 430 | /// generated such that it will not cause an interrupt or DMA request. | 465 | /// generated such that it will not cause an interrupt or DMA request. |
| 431 | pub fn set_max_compare_value(&self, ticks: u32) { | 466 | pub fn set_max_compare_value(&self, ticks: T::Word) { |
| 432 | match T::BITS { | 467 | let arr = ticks; |
| 433 | TimerBits::Bits16 => { | ||
| 434 | let arr = unwrap!(u16::try_from(ticks)); | ||
| 435 | |||
| 436 | let regs = self.regs_1ch(); | ||
| 437 | regs.arr().write(|r| r.set_arr(arr)); | ||
| 438 | |||
| 439 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | ||
| 440 | regs.egr().write(|r| r.set_ug(true)); | ||
| 441 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | ||
| 442 | } | ||
| 443 | #[cfg(not(stm32l0))] | ||
| 444 | TimerBits::Bits32 => { | ||
| 445 | let arr = ticks; | ||
| 446 | 468 | ||
| 447 | let regs = self.regs_gp32_unchecked(); | 469 | let regs = self.regs_gp32_unchecked(); |
| 448 | regs.arr().write_value(arr); | 470 | #[cfg(not(stm32l0))] |
| 471 | regs.arr().write_value(arr.into()); | ||
| 472 | #[cfg(stm32l0)] | ||
| 473 | regs.arr().write(|r| r.set_arr(unwrap!(arr.try_into()))); | ||
| 449 | 474 | ||
| 450 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | 475 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); |
| 451 | regs.egr().write(|r| r.set_ug(true)); | 476 | regs.egr().write(|r| r.set_ug(true)); |
| 452 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | 477 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); |
| 453 | } | ||
| 454 | } | ||
| 455 | } | 478 | } |
| 456 | } | 479 | } |
| 457 | 480 | ||
| @@ -585,30 +608,204 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { | |||
| 585 | } | 608 | } |
| 586 | 609 | ||
| 587 | /// Set compare value for a channel. | 610 | /// Set compare value for a channel. |
| 588 | pub fn set_compare_value(&self, channel: Channel, value: u32) { | 611 | pub fn set_compare_value(&self, channel: Channel, value: T::Word) { |
| 589 | match T::BITS { | 612 | #[cfg(not(stm32l0))] |
| 590 | TimerBits::Bits16 => { | 613 | self.regs_gp32_unchecked() |
| 591 | let value = unwrap!(u16::try_from(value)); | 614 | .ccr(channel.index()) |
| 592 | self.regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value)); | 615 | .write_value(value.into()); |
| 593 | } | 616 | #[cfg(stm32l0)] |
| 594 | #[cfg(not(stm32l0))] | 617 | self.regs_gp16() |
| 595 | TimerBits::Bits32 => { | 618 | .ccr(channel.index()) |
| 596 | self.regs_gp32_unchecked().ccr(channel.index()).write_value(value); | 619 | .modify(|w| w.set_ccr(unwrap!(value.try_into()))); |
| 597 | } | ||
| 598 | } | ||
| 599 | } | 620 | } |
| 600 | 621 | ||
| 601 | /// Get compare value for a channel. | 622 | /// Get compare value for a channel. |
| 602 | pub fn get_compare_value(&self, channel: Channel) -> u32 { | 623 | pub fn get_compare_value(&self, channel: Channel) -> T::Word { |
| 603 | match T::BITS { | 624 | #[cfg(not(stm32l0))] |
| 604 | TimerBits::Bits16 => self.regs_gp16().ccr(channel.index()).read().ccr() as u32, | 625 | return unwrap!(self.regs_gp32_unchecked().ccr(channel.index()).read().try_into()); |
| 605 | #[cfg(not(stm32l0))] | 626 | #[cfg(stm32l0)] |
| 606 | TimerBits::Bits32 => self.regs_gp32_unchecked().ccr(channel.index()).read(), | 627 | return unwrap!(self.regs_gp32_unchecked().ccr(channel.index()).read().ccr().try_into()); |
| 628 | } | ||
| 629 | |||
| 630 | pub(crate) fn clamp_compare_value<W: Word>(&mut self, channel: Channel) { | ||
| 631 | self.set_compare_value( | ||
| 632 | channel, | ||
| 633 | unwrap!( | ||
| 634 | self.get_compare_value(channel) | ||
| 635 | .into() | ||
| 636 | .clamp(0, W::max() as u32) | ||
| 637 | .try_into() | ||
| 638 | ), | ||
| 639 | ); | ||
| 640 | } | ||
| 641 | |||
| 642 | /// Setup a ring buffer for the channel | ||
| 643 | pub fn setup_ring_buffer<'a, W: Word + Into<T::Word>>( | ||
| 644 | &mut self, | ||
| 645 | dma: Peri<'a, impl super::UpDma<T>>, | ||
| 646 | channel: Channel, | ||
| 647 | dma_buf: &'a mut [W], | ||
| 648 | ) -> WritableRingBuffer<'a, W> { | ||
| 649 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 650 | let req = dma.request(); | ||
| 651 | |||
| 652 | unsafe { | ||
| 653 | use crate::dma::TransferOptions; | ||
| 654 | #[cfg(not(any(bdma, gpdma)))] | ||
| 655 | use crate::dma::{Burst, FifoThreshold}; | ||
| 656 | |||
| 657 | let dma_transfer_option = TransferOptions { | ||
| 658 | #[cfg(not(any(bdma, gpdma)))] | ||
| 659 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 660 | #[cfg(not(any(bdma, gpdma)))] | ||
| 661 | mburst: Burst::Incr8, | ||
| 662 | ..Default::default() | ||
| 663 | }; | ||
| 664 | |||
| 665 | WritableRingBuffer::new( | ||
| 666 | dma, | ||
| 667 | req, | ||
| 668 | self.regs_1ch().ccr(channel.index()).as_ptr() as *mut W, | ||
| 669 | dma_buf, | ||
| 670 | dma_transfer_option, | ||
| 671 | ) | ||
| 672 | } | ||
| 673 | } | ||
| 674 | |||
| 675 | /// Generate a sequence of PWM waveform | ||
| 676 | /// | ||
| 677 | /// Note: | ||
| 678 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | ||
| 679 | pub fn setup_update_dma<'a, W: Word + Into<T::Word>>( | ||
| 680 | &mut self, | ||
| 681 | dma: Peri<'a, impl super::UpDma<T>>, | ||
| 682 | channel: Channel, | ||
| 683 | duty: &'a [W], | ||
| 684 | ) -> Transfer<'a> { | ||
| 685 | self.setup_update_dma_inner(dma.request(), dma, channel, duty) | ||
| 686 | } | ||
| 687 | |||
| 688 | /// Generate a sequence of PWM waveform | ||
| 689 | /// | ||
| 690 | /// Note: | ||
| 691 | /// The DMA channel provided does not need to correspond to the requested channel. | ||
| 692 | pub fn setup_channel_update_dma<'a, C: TimerChannel, W: Word + Into<T::Word>>( | ||
| 693 | &mut self, | ||
| 694 | dma: Peri<'a, impl super::Dma<T, C>>, | ||
| 695 | channel: Channel, | ||
| 696 | duty: &'a [W], | ||
| 697 | ) -> Transfer<'a> { | ||
| 698 | self.setup_update_dma_inner(dma.request(), dma, channel, duty) | ||
| 699 | } | ||
| 700 | |||
| 701 | fn setup_update_dma_inner<'a, W: Word + Into<T::Word>>( | ||
| 702 | &mut self, | ||
| 703 | request: dma::Request, | ||
| 704 | dma: Peri<'a, impl dma::Channel>, | ||
| 705 | channel: Channel, | ||
| 706 | duty: &'a [W], | ||
| 707 | ) -> Transfer<'a> { | ||
| 708 | unsafe { | ||
| 709 | #[cfg(not(any(bdma, gpdma)))] | ||
| 710 | use crate::dma::{Burst, FifoThreshold}; | ||
| 711 | use crate::dma::{Transfer, TransferOptions}; | ||
| 712 | |||
| 713 | let dma_transfer_option = TransferOptions { | ||
| 714 | #[cfg(not(any(bdma, gpdma)))] | ||
| 715 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 716 | #[cfg(not(any(bdma, gpdma)))] | ||
| 717 | mburst: Burst::Incr8, | ||
| 718 | ..Default::default() | ||
| 719 | }; | ||
| 720 | |||
| 721 | Transfer::new_write( | ||
| 722 | dma, | ||
| 723 | request, | ||
| 724 | duty, | ||
| 725 | self.regs_gp16().ccr(channel.index()).as_ptr() as *mut W, | ||
| 726 | dma_transfer_option, | ||
| 727 | ) | ||
| 728 | } | ||
| 729 | } | ||
| 730 | |||
| 731 | /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. | ||
| 732 | /// | ||
| 733 | /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers | ||
| 734 | /// in sequence on each update event (UEV). The data is written via the DMAR register using the | ||
| 735 | /// DMA base address (DBA) and burst length (DBL) configured in the DCR register. | ||
| 736 | /// | ||
| 737 | /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row | ||
| 738 | /// represents a single update event and each column corresponds to a specific timer channel (starting | ||
| 739 | /// from `starting_channel` up to and including `ending_channel`). | ||
| 740 | /// | ||
| 741 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: | ||
| 742 | /// | ||
| 743 | /// ```rust,ignore | ||
| 744 | /// let dma_buf: [u16; 16] = [ | ||
| 745 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 | ||
| 746 | /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 | ||
| 747 | /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 | ||
| 748 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 | ||
| 749 | /// ]; | ||
| 750 | /// ``` | ||
| 751 | /// | ||
| 752 | /// Each group of `N` values (where `N` is number of channels) is transferred on one update event, | ||
| 753 | /// updating the duty cycles of all selected channels simultaneously. | ||
| 754 | /// | ||
| 755 | /// Note: | ||
| 756 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. | ||
| 757 | /// Also be aware that embassy timers use one of timers internally. It is possible to | ||
| 758 | /// switch this timer by using `time-driver-timX` feature. | ||
| 759 | /// | ||
| 760 | pub fn setup_update_dma_burst<'a, W: Word + Into<T::Word>>( | ||
| 761 | &mut self, | ||
| 762 | dma: Peri<'a, impl super::UpDma<T>>, | ||
| 763 | starting_channel: Channel, | ||
| 764 | ending_channel: Channel, | ||
| 765 | duty: &'a [W], | ||
| 766 | ) -> Transfer<'a> { | ||
| 767 | let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32; | ||
| 768 | let start_ch_index = starting_channel.index(); | ||
| 769 | let end_ch_index = ending_channel.index(); | ||
| 770 | |||
| 771 | assert!(start_ch_index <= end_ch_index); | ||
| 772 | |||
| 773 | let ccrx_addr = self.regs_gp16().ccr(start_ch_index).as_ptr() as u32; | ||
| 774 | self.regs_gp16() | ||
| 775 | .dcr() | ||
| 776 | .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8)); | ||
| 777 | self.regs_gp16() | ||
| 778 | .dcr() | ||
| 779 | .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8)); | ||
| 780 | |||
| 781 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 782 | let req = dma.request(); | ||
| 783 | |||
| 784 | unsafe { | ||
| 785 | #[cfg(not(any(bdma, gpdma)))] | ||
| 786 | use crate::dma::{Burst, FifoThreshold}; | ||
| 787 | use crate::dma::{Transfer, TransferOptions}; | ||
| 788 | |||
| 789 | let dma_transfer_option = TransferOptions { | ||
| 790 | #[cfg(not(any(bdma, gpdma)))] | ||
| 791 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 792 | #[cfg(not(any(bdma, gpdma)))] | ||
| 793 | mburst: Burst::Incr4, | ||
| 794 | ..Default::default() | ||
| 795 | }; | ||
| 796 | |||
| 797 | Transfer::new_write( | ||
| 798 | dma, | ||
| 799 | req, | ||
| 800 | duty, | ||
| 801 | self.regs_gp16().dmar().as_ptr() as *mut W, | ||
| 802 | dma_transfer_option, | ||
| 803 | ) | ||
| 607 | } | 804 | } |
| 608 | } | 805 | } |
| 609 | 806 | ||
| 610 | /// Get capture value for a channel. | 807 | /// Get capture value for a channel. |
| 611 | pub fn get_capture_value(&self, channel: Channel) -> u32 { | 808 | pub fn get_capture_value(&self, channel: Channel) -> T::Word { |
| 612 | self.get_compare_value(channel) | 809 | self.get_compare_value(channel) |
| 613 | } | 810 | } |
| 614 | 811 | ||
| @@ -640,6 +837,11 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { | |||
| 640 | self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde)) | 837 | self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde)) |
| 641 | } | 838 | } |
| 642 | 839 | ||
| 840 | /// Set Timer Master Mode | ||
| 841 | pub fn set_master_mode(&self, mms: MasterMode) { | ||
| 842 | self.regs_gp16().cr2().modify(|w| w.set_mms(mms)); | ||
| 843 | } | ||
| 844 | |||
| 643 | /// Set Timer Slave Mode | 845 | /// Set Timer Slave Mode |
| 644 | pub fn set_slave_mode(&self, sms: SlaveMode) { | 846 | pub fn set_slave_mode(&self, sms: SlaveMode) { |
| 645 | self.regs_gp16().smcr().modify(|r| r.set_sms(sms)); | 847 | self.regs_gp16().smcr().modify(|r| r.set_sms(sms)); |
| @@ -760,6 +962,16 @@ impl<'d, T: AdvancedInstance4Channel> Timer<'d, T> { | |||
| 760 | self.regs_advanced().cr2().modify(|w| w.set_oisn(channel.index(), val)); | 962 | self.regs_advanced().cr2().modify(|w| w.set_oisn(channel.index(), val)); |
| 761 | } | 963 | } |
| 762 | 964 | ||
| 965 | /// Set master mode selection 2 | ||
| 966 | pub fn set_mms2_selection(&self, mms2: vals::Mms2) { | ||
| 967 | self.regs_advanced().cr2().modify(|w| w.set_mms2(mms2)); | ||
| 968 | } | ||
| 969 | |||
| 970 | /// Set repetition counter | ||
| 971 | pub fn set_repetition_counter(&self, val: u16) { | ||
| 972 | self.regs_advanced().rcr().modify(|w| w.set_rep(val)); | ||
| 973 | } | ||
| 974 | |||
| 763 | /// Trigger software break 1 or 2 | 975 | /// Trigger software break 1 or 2 |
| 764 | /// Setting this bit generates a break event. This bit is automatically cleared by the hardware. | 976 | /// Setting this bit generates a break event. This bit is automatically cleared by the hardware. |
| 765 | pub fn trigger_software_break(&self, n: usize) { | 977 | 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..998e3a6f4 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs | |||
| @@ -12,8 +12,11 @@ pub mod low_level; | |||
| 12 | pub mod one_pulse; | 12 | pub mod one_pulse; |
| 13 | pub mod pwm_input; | 13 | pub mod pwm_input; |
| 14 | pub mod qei; | 14 | pub mod qei; |
| 15 | pub mod ringbuffered; | ||
| 15 | pub mod simple_pwm; | 16 | pub mod simple_pwm; |
| 16 | 17 | ||
| 18 | use crate::dma::word::Word; | ||
| 19 | use crate::fmt::Debuggable; | ||
| 17 | use crate::interrupt; | 20 | use crate::interrupt; |
| 18 | use crate::rcc::RccPeripheral; | 21 | use crate::rcc::RccPeripheral; |
| 19 | 22 | ||
| @@ -162,7 +165,12 @@ pub trait CoreInstance: SealedInstance + 'static { | |||
| 162 | type UpdateInterrupt: interrupt::typelevel::Interrupt; | 165 | type UpdateInterrupt: interrupt::typelevel::Interrupt; |
| 163 | 166 | ||
| 164 | /// Amount of bits this timer has. | 167 | /// Amount of bits this timer has. |
| 165 | const BITS: TimerBits; | 168 | type Word: Word |
| 169 | + TryInto<u16, Error: Debuggable> | ||
| 170 | + From<u16> | ||
| 171 | + TryFrom<u32, Error: Debuggable> | ||
| 172 | + Into<u32> | ||
| 173 | + TryFrom<u64, Error: Debuggable>; | ||
| 166 | 174 | ||
| 167 | /// Registers for this timer. | 175 | /// Registers for this timer. |
| 168 | /// | 176 | /// |
| @@ -240,7 +248,7 @@ dma_trait!(Dma, GeneralInstance4Channel, TimerChannel); | |||
| 240 | 248 | ||
| 241 | #[allow(unused)] | 249 | #[allow(unused)] |
| 242 | macro_rules! impl_core_timer { | 250 | macro_rules! impl_core_timer { |
| 243 | ($inst:ident, $bits:expr) => { | 251 | ($inst:ident, $bits:ident) => { |
| 244 | impl SealedInstance for crate::peripherals::$inst { | 252 | impl SealedInstance for crate::peripherals::$inst { |
| 245 | fn state() -> &'static State { | 253 | fn state() -> &'static State { |
| 246 | static STATE: State = State::new(); | 254 | static STATE: State = State::new(); |
| @@ -250,8 +258,7 @@ macro_rules! impl_core_timer { | |||
| 250 | 258 | ||
| 251 | impl CoreInstance for crate::peripherals::$inst { | 259 | impl CoreInstance for crate::peripherals::$inst { |
| 252 | type UpdateInterrupt = crate::_generated::peripheral_interrupts::$inst::UP; | 260 | type UpdateInterrupt = crate::_generated::peripheral_interrupts::$inst::UP; |
| 253 | 261 | type Word = $bits; | |
| 254 | const BITS: TimerBits = $bits; | ||
| 255 | 262 | ||
| 256 | fn regs() -> *mut () { | 263 | fn regs() -> *mut () { |
| 257 | crate::pac::$inst.as_ptr() | 264 | crate::pac::$inst.as_ptr() |
| @@ -305,13 +312,13 @@ macro_rules! impl_general_4ch_blank_sealed { | |||
| 305 | 312 | ||
| 306 | foreach_interrupt! { | 313 | foreach_interrupt! { |
| 307 | ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => { | 314 | ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => { |
| 308 | impl_core_timer!($inst, TimerBits::Bits16); | 315 | impl_core_timer!($inst, u16); |
| 309 | impl BasicNoCr2Instance for crate::peripherals::$inst {} | 316 | impl BasicNoCr2Instance for crate::peripherals::$inst {} |
| 310 | impl BasicInstance for crate::peripherals::$inst {} | 317 | impl BasicInstance for crate::peripherals::$inst {} |
| 311 | }; | 318 | }; |
| 312 | 319 | ||
| 313 | ($inst:ident, timer, TIM_1CH, UP, $irq:ident) => { | 320 | ($inst:ident, timer, TIM_1CH, UP, $irq:ident) => { |
| 314 | impl_core_timer!($inst, TimerBits::Bits16); | 321 | impl_core_timer!($inst, u16); |
| 315 | impl BasicNoCr2Instance for crate::peripherals::$inst {} | 322 | impl BasicNoCr2Instance for crate::peripherals::$inst {} |
| 316 | impl BasicInstance for crate::peripherals::$inst {} | 323 | impl BasicInstance for crate::peripherals::$inst {} |
| 317 | impl_general_1ch!($inst); | 324 | impl_general_1ch!($inst); |
| @@ -321,7 +328,7 @@ foreach_interrupt! { | |||
| 321 | }; | 328 | }; |
| 322 | 329 | ||
| 323 | ($inst:ident, timer, TIM_2CH, UP, $irq:ident) => { | 330 | ($inst:ident, timer, TIM_2CH, UP, $irq:ident) => { |
| 324 | impl_core_timer!($inst, TimerBits::Bits16); | 331 | impl_core_timer!($inst, u16); |
| 325 | impl BasicNoCr2Instance for crate::peripherals::$inst {} | 332 | impl BasicNoCr2Instance for crate::peripherals::$inst {} |
| 326 | impl BasicInstance for crate::peripherals::$inst {} | 333 | impl BasicInstance for crate::peripherals::$inst {} |
| 327 | impl_general_1ch!($inst); | 334 | impl_general_1ch!($inst); |
| @@ -331,7 +338,7 @@ foreach_interrupt! { | |||
| 331 | }; | 338 | }; |
| 332 | 339 | ||
| 333 | ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { | 340 | ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { |
| 334 | impl_core_timer!($inst, TimerBits::Bits16); | 341 | impl_core_timer!($inst, u16); |
| 335 | impl BasicNoCr2Instance for crate::peripherals::$inst {} | 342 | impl BasicNoCr2Instance for crate::peripherals::$inst {} |
| 336 | impl BasicInstance for crate::peripherals::$inst {} | 343 | impl BasicInstance for crate::peripherals::$inst {} |
| 337 | impl_general_1ch!($inst); | 344 | impl_general_1ch!($inst); |
| @@ -341,7 +348,7 @@ foreach_interrupt! { | |||
| 341 | }; | 348 | }; |
| 342 | 349 | ||
| 343 | ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { | 350 | ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { |
| 344 | impl_core_timer!($inst, TimerBits::Bits32); | 351 | impl_core_timer!($inst, u32); |
| 345 | impl BasicNoCr2Instance for crate::peripherals::$inst {} | 352 | impl BasicNoCr2Instance for crate::peripherals::$inst {} |
| 346 | impl BasicInstance for crate::peripherals::$inst {} | 353 | impl BasicInstance for crate::peripherals::$inst {} |
| 347 | impl_general_1ch!($inst); | 354 | impl_general_1ch!($inst); |
| @@ -352,7 +359,7 @@ foreach_interrupt! { | |||
| 352 | }; | 359 | }; |
| 353 | 360 | ||
| 354 | ($inst:ident, timer, TIM_1CH_CMP, UP, $irq:ident) => { | 361 | ($inst:ident, timer, TIM_1CH_CMP, UP, $irq:ident) => { |
| 355 | impl_core_timer!($inst, TimerBits::Bits16); | 362 | impl_core_timer!($inst, u16); |
| 356 | impl BasicNoCr2Instance for crate::peripherals::$inst {} | 363 | impl BasicNoCr2Instance for crate::peripherals::$inst {} |
| 357 | impl BasicInstance for crate::peripherals::$inst {} | 364 | impl BasicInstance for crate::peripherals::$inst {} |
| 358 | impl_general_1ch!($inst); | 365 | impl_general_1ch!($inst); |
| @@ -365,7 +372,7 @@ foreach_interrupt! { | |||
| 365 | }; | 372 | }; |
| 366 | 373 | ||
| 367 | ($inst:ident, timer, TIM_2CH_CMP, UP, $irq:ident) => { | 374 | ($inst:ident, timer, TIM_2CH_CMP, UP, $irq:ident) => { |
| 368 | impl_core_timer!($inst, TimerBits::Bits16); | 375 | impl_core_timer!($inst, u16); |
| 369 | impl BasicNoCr2Instance for crate::peripherals::$inst {} | 376 | impl BasicNoCr2Instance for crate::peripherals::$inst {} |
| 370 | impl BasicInstance for crate::peripherals::$inst {} | 377 | impl BasicInstance for crate::peripherals::$inst {} |
| 371 | impl_general_1ch!($inst); | 378 | impl_general_1ch!($inst); |
| @@ -378,7 +385,7 @@ foreach_interrupt! { | |||
| 378 | }; | 385 | }; |
| 379 | 386 | ||
| 380 | ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { | 387 | ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { |
| 381 | impl_core_timer!($inst, TimerBits::Bits16); | 388 | impl_core_timer!($inst, u16); |
| 382 | impl BasicNoCr2Instance for crate::peripherals::$inst {} | 389 | impl BasicNoCr2Instance for crate::peripherals::$inst {} |
| 383 | impl BasicInstance for crate::peripherals::$inst {} | 390 | impl BasicInstance for crate::peripherals::$inst {} |
| 384 | impl_general_1ch!($inst); | 391 | impl_general_1ch!($inst); |
| @@ -399,7 +406,7 @@ pub struct UpdateInterruptHandler<T: CoreInstance> { | |||
| 399 | impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> { | 406 | impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> { |
| 400 | unsafe fn on_interrupt() { | 407 | unsafe fn on_interrupt() { |
| 401 | #[cfg(feature = "low-power")] | 408 | #[cfg(feature = "low-power")] |
| 402 | crate::low_power::on_wakeup_irq(); | 409 | crate::low_power::Executor::on_wakeup_irq_or_event(); |
| 403 | 410 | ||
| 404 | let regs = crate::pac::timer::TimCore::from_ptr(T::regs()); | 411 | let regs = crate::pac::timer::TimCore::from_ptr(T::regs()); |
| 405 | 412 | ||
| @@ -429,7 +436,7 @@ impl<T: GeneralInstance1Channel> interrupt::typelevel::Handler<T::CaptureCompare | |||
| 429 | { | 436 | { |
| 430 | unsafe fn on_interrupt() { | 437 | unsafe fn on_interrupt() { |
| 431 | #[cfg(feature = "low-power")] | 438 | #[cfg(feature = "low-power")] |
| 432 | crate::low_power::on_wakeup_irq(); | 439 | crate::low_power::Executor::on_wakeup_irq_or_event(); |
| 433 | 440 | ||
| 434 | let regs = crate::pac::timer::TimGp16::from_ptr(T::regs()); | 441 | let regs = crate::pac::timer::TimGp16::from_ptr(T::regs()); |
| 435 | 442 | ||
diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs index a75b41bd7..989e1d630 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 {} |
| @@ -199,7 +199,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { | |||
| 199 | fn new_inner(&mut self, freq: Hertz, pulse_end: u32, counting_mode: CountingMode) { | 199 | fn new_inner(&mut self, freq: Hertz, pulse_end: u32, counting_mode: CountingMode) { |
| 200 | self.inner.set_counting_mode(counting_mode); | 200 | self.inner.set_counting_mode(counting_mode); |
| 201 | self.inner.set_tick_freq(freq); | 201 | self.inner.set_tick_freq(freq); |
| 202 | self.inner.set_max_compare_value(pulse_end); | 202 | self.inner.set_max_compare_value(unwrap!(pulse_end.try_into())); |
| 203 | self.inner.regs_core().cr1().modify(|r| r.set_opm(true)); | 203 | self.inner.regs_core().cr1().modify(|r| r.set_opm(true)); |
| 204 | // Required for advanced timers, see GeneralInstance4Channel for details | 204 | // Required for advanced timers, see GeneralInstance4Channel for details |
| 205 | self.inner.enable_outputs(); | 205 | self.inner.enable_outputs(); |
| @@ -211,14 +211,14 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { | |||
| 211 | 211 | ||
| 212 | /// Get the end of the pulse in ticks from the trigger. | 212 | /// Get the end of the pulse in ticks from the trigger. |
| 213 | pub fn pulse_end(&self) -> u32 { | 213 | pub fn pulse_end(&self) -> u32 { |
| 214 | let max = self.inner.get_max_compare_value(); | 214 | let max: u32 = self.inner.get_max_compare_value().into(); |
| 215 | assert!(max < u32::MAX); | 215 | assert!(max < u32::MAX); |
| 216 | max + 1 | 216 | max + 1 |
| 217 | } | 217 | } |
| 218 | 218 | ||
| 219 | /// Set the end of the pulse in ticks from the trigger. | 219 | /// Set the end of the pulse in ticks from the trigger. |
| 220 | pub fn set_pulse_end(&mut self, ticks: u32) { | 220 | pub fn set_pulse_end(&mut self, ticks: u32) { |
| 221 | self.inner.set_max_compare_value(ticks) | 221 | self.inner.set_max_compare_value(unwrap!(ticks.try_into())) |
| 222 | } | 222 | } |
| 223 | 223 | ||
| 224 | /// Reset the timer on each trigger | 224 | /// Reset the timer on each trigger |
| @@ -327,7 +327,7 @@ pub struct OnePulseChannel<'d, T: GeneralInstance4Channel> { | |||
| 327 | impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> { | 327 | impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> { |
| 328 | /// Get the end of the pulse in ticks from the trigger. | 328 | /// Get the end of the pulse in ticks from the trigger. |
| 329 | pub fn pulse_end(&self) -> u32 { | 329 | pub fn pulse_end(&self) -> u32 { |
| 330 | let max = self.inner.get_max_compare_value(); | 330 | let max: u32 = self.inner.get_max_compare_value().into(); |
| 331 | assert!(max < u32::MAX); | 331 | assert!(max < u32::MAX); |
| 332 | max + 1 | 332 | max + 1 |
| 333 | } | 333 | } |
| @@ -339,13 +339,13 @@ impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> { | |||
| 339 | 339 | ||
| 340 | /// Get the start of the pulse in ticks from the trigger. | 340 | /// Get the start of the pulse in ticks from the trigger. |
| 341 | pub fn pulse_delay(&mut self) -> u32 { | 341 | pub fn pulse_delay(&mut self) -> u32 { |
| 342 | self.inner.get_compare_value(self.channel) | 342 | self.inner.get_compare_value(self.channel).into() |
| 343 | } | 343 | } |
| 344 | 344 | ||
| 345 | /// Set the start of the pulse in ticks from the trigger. | 345 | /// Set the start of the pulse in ticks from the trigger. |
| 346 | pub fn set_pulse_delay(&mut self, delay: u32) { | 346 | pub fn set_pulse_delay(&mut self, delay: u32) { |
| 347 | assert!(delay <= self.pulse_end()); | 347 | assert!(delay <= self.pulse_end()); |
| 348 | self.inner.set_compare_value(self.channel, delay); | 348 | self.inner.set_compare_value(self.channel, unwrap!(delay.try_into())); |
| 349 | } | 349 | } |
| 350 | 350 | ||
| 351 | /// Set the pulse width in ticks. | 351 | /// Set the pulse width in ticks. |
diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index 159b5a177..f2f00927d 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 | /// |
| @@ -47,6 +47,7 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { | |||
| 47 | inner.set_counting_mode(CountingMode::EdgeAlignedUp); | 47 | inner.set_counting_mode(CountingMode::EdgeAlignedUp); |
| 48 | inner.set_tick_freq(freq); | 48 | inner.set_tick_freq(freq); |
| 49 | inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details | 49 | inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details |
| 50 | inner.generate_update_event(); | ||
| 50 | inner.start(); | 51 | inner.start(); |
| 51 | 52 | ||
| 52 | // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6 | 53 | // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6 |
| @@ -90,16 +91,18 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { | |||
| 90 | 91 | ||
| 91 | /// Get the period tick count | 92 | /// Get the period tick count |
| 92 | pub fn get_period_ticks(&self) -> u32 { | 93 | pub fn get_period_ticks(&self) -> u32 { |
| 93 | self.inner.get_capture_value(self.channel) | 94 | self.inner.get_capture_value(self.channel).into() |
| 94 | } | 95 | } |
| 95 | 96 | ||
| 96 | /// Get the pulse width tick count | 97 | /// Get the pulse width tick count |
| 97 | pub fn get_width_ticks(&self) -> u32 { | 98 | pub fn get_width_ticks(&self) -> u32 { |
| 98 | self.inner.get_capture_value(match self.channel { | 99 | self.inner |
| 99 | Channel::Ch1 => Channel::Ch2, | 100 | .get_capture_value(match self.channel { |
| 100 | Channel::Ch2 => Channel::Ch1, | 101 | Channel::Ch1 => Channel::Ch2, |
| 101 | _ => panic!("Invalid channel for PWM input"), | 102 | Channel::Ch2 => Channel::Ch1, |
| 102 | }) | 103 | _ => panic!("Invalid channel for PWM input"), |
| 104 | }) | ||
| 105 | .into() | ||
| 103 | } | 106 | } |
| 104 | 107 | ||
| 105 | /// Get the duty cycle in 100% | 108 | /// Get the duty cycle in 100% |
diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index bb152731c..a547a2a19 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs | |||
| @@ -5,9 +5,9 @@ use stm32_metapac::timer::vals::{self, Sms}; | |||
| 5 | use super::low_level::Timer; | 5 | use super::low_level::Timer; |
| 6 | pub use super::{Ch1, Ch2}; | 6 | pub use super::{Ch1, Ch2}; |
| 7 | use super::{GeneralInstance4Channel, TimerPin}; | 7 | use super::{GeneralInstance4Channel, TimerPin}; |
| 8 | use crate::Peri; | ||
| 8 | use crate::gpio::{AfType, AnyPin, Pull}; | 9 | use crate::gpio::{AfType, AnyPin, Pull}; |
| 9 | use crate::timer::TimerChannel; | 10 | use crate::timer::TimerChannel; |
| 10 | use crate::Peri; | ||
| 11 | 11 | ||
| 12 | /// Qei driver config. | 12 | /// Qei driver config. |
| 13 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 13 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
diff --git a/embassy-stm32/src/timer/ringbuffered.rs b/embassy-stm32/src/timer/ringbuffered.rs new file mode 100644 index 000000000..fbb6b19ea --- /dev/null +++ b/embassy-stm32/src/timer/ringbuffered.rs | |||
| @@ -0,0 +1,158 @@ | |||
| 1 | //! RingBuffered PWM driver. | ||
| 2 | |||
| 3 | use core::mem::ManuallyDrop; | ||
| 4 | use core::task::Waker; | ||
| 5 | |||
| 6 | use super::low_level::Timer; | ||
| 7 | use super::{Channel, GeneralInstance4Channel}; | ||
| 8 | use crate::dma::WritableRingBuffer; | ||
| 9 | use crate::dma::ringbuffer::Error; | ||
| 10 | use crate::dma::word::Word; | ||
| 11 | |||
| 12 | /// A PWM channel that uses a DMA ring buffer for continuous waveform generation. | ||
| 13 | /// | ||
| 14 | /// This allows you to continuously update PWM duty cycles via DMA without blocking the CPU. | ||
| 15 | /// The ring buffer enables smooth, uninterrupted waveform generation by automatically cycling | ||
| 16 | /// through duty cycle values stored in memory. | ||
| 17 | /// | ||
| 18 | /// You can write new duty cycle values to the ring buffer while it's running, enabling | ||
| 19 | /// dynamic waveform generation for applications like motor control, LED dimming, or audio output. | ||
| 20 | /// | ||
| 21 | /// # Example | ||
| 22 | /// ```ignore | ||
| 23 | /// let mut channel = pwm.ch1().into_ring_buffered_channel(dma_ch, &mut buffer); | ||
| 24 | /// channel.start(); // Start DMA transfer | ||
| 25 | /// channel.write(&[100, 200, 300]).ok(); // Update duty cycles | ||
| 26 | /// ``` | ||
| 27 | pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel, W: Word + Into<T::Word>> { | ||
| 28 | timer: ManuallyDrop<Timer<'d, T>>, | ||
| 29 | ring_buf: WritableRingBuffer<'d, W>, | ||
| 30 | channel: Channel, | ||
| 31 | } | ||
| 32 | |||
| 33 | impl<'d, T: GeneralInstance4Channel, W: Word + Into<T::Word>> RingBufferedPwmChannel<'d, T, W> { | ||
| 34 | pub(crate) fn new( | ||
| 35 | timer: ManuallyDrop<Timer<'d, T>>, | ||
| 36 | channel: Channel, | ||
| 37 | ring_buf: WritableRingBuffer<'d, W>, | ||
| 38 | ) -> Self { | ||
| 39 | Self { | ||
| 40 | timer, | ||
| 41 | ring_buf, | ||
| 42 | channel, | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | /// Start the ring buffer operation. | ||
| 47 | /// | ||
| 48 | /// You must call this after creating it for it to work. | ||
| 49 | pub fn start(&mut self) { | ||
| 50 | self.ring_buf.start() | ||
| 51 | } | ||
| 52 | |||
| 53 | /// Clear all data in the ring buffer. | ||
| 54 | pub fn clear(&mut self) { | ||
| 55 | self.ring_buf.clear() | ||
| 56 | } | ||
| 57 | |||
| 58 | /// Write elements directly to the raw buffer. This can be used to fill the buffer before starting the DMA transfer. | ||
| 59 | pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> { | ||
| 60 | self.ring_buf.write_immediate(buf) | ||
| 61 | } | ||
| 62 | |||
| 63 | /// Write elements from the ring buffer | ||
| 64 | /// Return a tuple of the length written and the length remaining in the buffer | ||
| 65 | pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), Error> { | ||
| 66 | self.ring_buf.write(buf) | ||
| 67 | } | ||
| 68 | |||
| 69 | /// Write an exact number of elements to the ringbuffer. | ||
| 70 | pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, Error> { | ||
| 71 | self.ring_buf.write_exact(buffer).await | ||
| 72 | } | ||
| 73 | |||
| 74 | /// Wait for any ring buffer write error. | ||
| 75 | pub async fn wait_write_error(&mut self) -> Result<usize, Error> { | ||
| 76 | self.ring_buf.wait_write_error().await | ||
| 77 | } | ||
| 78 | |||
| 79 | /// The current length of the ringbuffer | ||
| 80 | pub fn len(&mut self) -> Result<usize, Error> { | ||
| 81 | self.ring_buf.len() | ||
| 82 | } | ||
| 83 | |||
| 84 | /// The capacity of the ringbuffer | ||
| 85 | pub const fn capacity(&self) -> usize { | ||
| 86 | self.ring_buf.capacity() | ||
| 87 | } | ||
| 88 | |||
| 89 | /// Set a waker to be woken when at least one byte is send. | ||
| 90 | pub fn set_waker(&mut self, waker: &Waker) { | ||
| 91 | self.ring_buf.set_waker(waker) | ||
| 92 | } | ||
| 93 | |||
| 94 | /// Request the DMA to reset. The configuration for this channel will not be preserved. | ||
| 95 | /// | ||
| 96 | /// This doesn't immediately stop the transfer, you have to wait until is_running returns false. | ||
| 97 | pub fn request_reset(&mut self) { | ||
| 98 | self.ring_buf.request_reset() | ||
| 99 | } | ||
| 100 | |||
| 101 | /// Request the transfer to pause, keeping the existing configuration for this channel. | ||
| 102 | /// To restart the transfer, call [`start`](Self::start) again. | ||
| 103 | /// | ||
| 104 | /// This doesn't immediately stop the transfer, you have to wait until is_running returns false. | ||
| 105 | pub fn request_pause(&mut self) { | ||
| 106 | self.ring_buf.request_pause() | ||
| 107 | } | ||
| 108 | |||
| 109 | /// Return whether DMA is still running. | ||
| 110 | /// | ||
| 111 | /// If this returns false, it can be because either the transfer finished, or it was requested to stop early with request_stop. | ||
| 112 | pub fn is_running(&mut self) -> bool { | ||
| 113 | self.ring_buf.is_running() | ||
| 114 | } | ||
| 115 | |||
| 116 | /// Stop the DMA transfer and await until the buffer is empty. | ||
| 117 | /// | ||
| 118 | /// This disables the DMA transfer's circular mode so that the transfer stops when all available data has been written. | ||
| 119 | /// | ||
| 120 | /// This is designed to be used with streaming output data such as the I2S/SAI or DAC. | ||
| 121 | pub async fn stop(&mut self) { | ||
| 122 | self.ring_buf.stop().await | ||
| 123 | } | ||
| 124 | |||
| 125 | /// Enable the given channel. | ||
| 126 | pub fn enable(&mut self) { | ||
| 127 | self.timer.enable_channel(self.channel, true); | ||
| 128 | } | ||
| 129 | |||
| 130 | /// Disable the given channel. | ||
| 131 | pub fn disable(&mut self) { | ||
| 132 | self.timer.enable_channel(self.channel, false); | ||
| 133 | } | ||
| 134 | |||
| 135 | /// Check whether given channel is enabled | ||
| 136 | pub fn is_enabled(&self) -> bool { | ||
| 137 | self.timer.get_channel_enable_state(self.channel) | ||
| 138 | } | ||
| 139 | |||
| 140 | /// Get max duty value. | ||
| 141 | /// | ||
| 142 | /// This value depends on the configured frequency and the timer's clock rate from RCC. | ||
| 143 | pub fn max_duty_cycle(&self) -> u16 { | ||
| 144 | let max: u32 = self.timer.get_max_compare_value().into(); | ||
| 145 | assert!(max < u16::MAX as u32); | ||
| 146 | max as u16 + 1 | ||
| 147 | } | ||
| 148 | |||
| 149 | /// Set the output polarity for a given channel. | ||
| 150 | pub fn set_polarity(&mut self, polarity: super::low_level::OutputPolarity) { | ||
| 151 | self.timer.set_output_polarity(self.channel, polarity); | ||
| 152 | } | ||
| 153 | |||
| 154 | /// Set the output compare mode for a given channel. | ||
| 155 | pub fn set_output_compare_mode(&mut self, mode: super::low_level::OutputCompareMode) { | ||
| 156 | self.timer.set_output_compare_mode(self.channel, mode); | ||
| 157 | } | ||
| 158 | } | ||
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index e6165e42b..4ffa58778 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs | |||
| @@ -4,12 +4,15 @@ use core::marker::PhantomData; | |||
| 4 | use core::mem::ManuallyDrop; | 4 | 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::ringbuffered::RingBufferedPwmChannel; |
| 8 | use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; | ||
| 9 | use crate::Peri; | ||
| 10 | use crate::dma::word::Word; | ||
| 8 | #[cfg(gpio_v2)] | 11 | #[cfg(gpio_v2)] |
| 9 | use crate::gpio::Pull; | 12 | use crate::gpio::Pull; |
| 10 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 13 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
| 14 | use crate::pac::timer::vals::Ccds; | ||
| 11 | use crate::time::Hertz; | 15 | use crate::time::Hertz; |
| 12 | use crate::Peri; | ||
| 13 | 16 | ||
| 14 | /// PWM pin wrapper. | 17 | /// PWM pin wrapper. |
| 15 | /// | 18 | /// |
| @@ -94,21 +97,24 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { | |||
| 94 | self.timer.get_channel_enable_state(self.channel) | 97 | self.timer.get_channel_enable_state(self.channel) |
| 95 | } | 98 | } |
| 96 | 99 | ||
| 100 | /// Get the frequency of the PWM channel. | ||
| 101 | pub fn get_frequency(&self) -> Hertz { | ||
| 102 | self.timer.get_frequency() | ||
| 103 | } | ||
| 104 | |||
| 97 | /// Get max duty value. | 105 | /// Get max duty value. |
| 98 | /// | 106 | /// |
| 99 | /// This value depends on the configured frequency and the timer's clock rate from RCC. | 107 | /// This value depends on the configured frequency and the timer's clock rate from RCC. |
| 100 | pub fn max_duty_cycle(&self) -> u16 { | 108 | pub fn max_duty_cycle(&self) -> u32 { |
| 101 | let max = self.timer.get_max_compare_value(); | 109 | self.timer.get_max_compare_value().into() + 1 |
| 102 | assert!(max < u16::MAX as u32); | ||
| 103 | max as u16 + 1 | ||
| 104 | } | 110 | } |
| 105 | 111 | ||
| 106 | /// Set the duty for a given channel. | 112 | /// Set the duty for a given channel. |
| 107 | /// | 113 | /// |
| 108 | /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included. | 114 | /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included. |
| 109 | pub fn set_duty_cycle(&mut self, duty: u16) { | 115 | pub fn set_duty_cycle(&mut self, duty: u32) { |
| 110 | assert!(duty <= (*self).max_duty_cycle()); | 116 | assert!(duty <= (*self).max_duty_cycle()); |
| 111 | self.timer.set_compare_value(self.channel, duty.into()) | 117 | self.timer.set_compare_value(self.channel, unwrap!(duty.try_into())) |
| 112 | } | 118 | } |
| 113 | 119 | ||
| 114 | /// Set the duty cycle to 0%, or always inactive. | 120 | /// Set the duty cycle to 0%, or always inactive. |
| @@ -125,21 +131,21 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { | |||
| 125 | /// | 131 | /// |
| 126 | /// The caller is responsible for ensuring that `num` is less than or equal to `denom`, | 132 | /// The caller is responsible for ensuring that `num` is less than or equal to `denom`, |
| 127 | /// and that `denom` is not zero. | 133 | /// and that `denom` is not zero. |
| 128 | pub fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) { | 134 | pub fn set_duty_cycle_fraction(&mut self, num: u32, denom: u32) { |
| 129 | assert!(denom != 0); | 135 | assert!(denom != 0); |
| 130 | assert!(num <= denom); | 136 | assert!(num <= denom); |
| 131 | let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom); | 137 | let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom); |
| 132 | 138 | ||
| 133 | // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16) | 139 | // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16) |
| 134 | #[allow(clippy::cast_possible_truncation)] | 140 | #[allow(clippy::cast_possible_truncation)] |
| 135 | self.set_duty_cycle(duty as u16); | 141 | self.set_duty_cycle(unwrap!(duty.try_into())); |
| 136 | } | 142 | } |
| 137 | 143 | ||
| 138 | /// Set the duty cycle to `percent / 100` | 144 | /// Set the duty cycle to `percent / 100` |
| 139 | /// | 145 | /// |
| 140 | /// The caller is responsible for ensuring that `percent` is less than or equal to 100. | 146 | /// The caller is responsible for ensuring that `percent` is less than or equal to 100. |
| 141 | pub fn set_duty_cycle_percent(&mut self, percent: u8) { | 147 | pub fn set_duty_cycle_percent(&mut self, percent: u8) { |
| 142 | self.set_duty_cycle_fraction(u16::from(percent), 100) | 148 | self.set_duty_cycle_fraction(percent as u32, 100) |
| 143 | } | 149 | } |
| 144 | 150 | ||
| 145 | /// Get the duty for a given channel. | 151 | /// Get the duty for a given channel. |
| @@ -158,6 +164,34 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { | |||
| 158 | pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) { | 164 | pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) { |
| 159 | self.timer.set_output_compare_mode(self.channel, mode); | 165 | self.timer.set_output_compare_mode(self.channel, mode); |
| 160 | } | 166 | } |
| 167 | |||
| 168 | /// Convert this PWM channel into a ring-buffered PWM channel. | ||
| 169 | /// | ||
| 170 | /// This allows continuous PWM waveform generation using a DMA ring buffer. | ||
| 171 | /// The ring buffer enables dynamic updates to the PWM duty cycle without blocking. | ||
| 172 | /// | ||
| 173 | /// # Arguments | ||
| 174 | /// * `tx_dma` - The DMA channel to use for transferring duty cycle values | ||
| 175 | /// * `dma_buf` - The buffer to use as a ring buffer (must be non-empty and <= 65535 elements) | ||
| 176 | /// | ||
| 177 | /// # Panics | ||
| 178 | /// Panics if `dma_buf` is empty or longer than 65535 elements. | ||
| 179 | pub fn into_ring_buffered_channel<W: Word + Into<T::Word>>( | ||
| 180 | mut self, | ||
| 181 | tx_dma: Peri<'d, impl super::UpDma<T>>, | ||
| 182 | dma_buf: &'d mut [W], | ||
| 183 | ) -> RingBufferedPwmChannel<'d, T, W> { | ||
| 184 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 185 | |||
| 186 | self.timer.clamp_compare_value::<W>(self.channel); | ||
| 187 | self.timer.enable_update_dma(true); | ||
| 188 | |||
| 189 | RingBufferedPwmChannel::new( | ||
| 190 | unsafe { self.timer.clone_unchecked() }, | ||
| 191 | self.channel, | ||
| 192 | self.timer.setup_ring_buffer(tx_dma, self.channel, dma_buf), | ||
| 193 | ) | ||
| 194 | } | ||
| 161 | } | 195 | } |
| 162 | 196 | ||
| 163 | /// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. | 197 | /// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. |
| @@ -198,7 +232,6 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 198 | this.inner.set_counting_mode(counting_mode); | 232 | this.inner.set_counting_mode(counting_mode); |
| 199 | this.set_frequency(freq); | 233 | this.set_frequency(freq); |
| 200 | this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details | 234 | this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details |
| 201 | this.inner.start(); | ||
| 202 | 235 | ||
| 203 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] | 236 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] |
| 204 | .iter() | 237 | .iter() |
| @@ -207,6 +240,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 207 | 240 | ||
| 208 | this.inner.set_output_compare_preload(channel, true); | 241 | this.inner.set_output_compare_preload(channel, true); |
| 209 | }); | 242 | }); |
| 243 | this.inner.set_autoreload_preload(true); | ||
| 244 | |||
| 245 | // Generate update event so pre-load registers are written to the shadow registers | ||
| 246 | this.inner.generate_update_event(); | ||
| 247 | this.inner.start(); | ||
| 210 | 248 | ||
| 211 | this | 249 | this |
| 212 | } | 250 | } |
| @@ -285,8 +323,8 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 285 | 323 | ||
| 286 | /// Set PWM frequency. | 324 | /// Set PWM frequency. |
| 287 | /// | 325 | /// |
| 288 | /// Note: when you call this, the max duty value changes, so you will have to | 326 | /// Note: that the frequency will not be applied in the timer until an update event |
| 289 | /// call `set_duty` on all channels with the duty calculated based on the new max duty. | 327 | /// occurs. |
| 290 | pub fn set_frequency(&mut self, freq: Hertz) { | 328 | pub fn set_frequency(&mut self, freq: Hertz) { |
| 291 | // TODO: prevent ARR = u16::MAX? | 329 | // TODO: prevent ARR = u16::MAX? |
| 292 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { | 330 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { |
| @@ -297,73 +335,54 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 297 | self.inner.set_frequency_internal(freq * multiplier, 16); | 335 | self.inner.set_frequency_internal(freq * multiplier, 16); |
| 298 | } | 336 | } |
| 299 | 337 | ||
| 338 | /// Get the PWM driver frequency. | ||
| 339 | pub fn get_frequency(&self) -> Hertz { | ||
| 340 | self.inner.get_frequency() | ||
| 341 | } | ||
| 342 | |||
| 300 | /// Get max duty value. | 343 | /// Get max duty value. |
| 301 | /// | 344 | /// |
| 302 | /// This value depends on the configured frequency and the timer's clock rate from RCC. | 345 | /// This value depends on the configured frequency and the timer's clock rate from RCC. |
| 303 | pub fn max_duty_cycle(&self) -> u16 { | 346 | pub fn max_duty_cycle(&self) -> u32 { |
| 304 | let max = self.inner.get_max_compare_value(); | 347 | self.inner.get_max_compare_value().into() + 1 |
| 305 | assert!(max < u16::MAX as u32); | ||
| 306 | max as u16 + 1 | ||
| 307 | } | 348 | } |
| 308 | 349 | ||
| 309 | /// Generate a sequence of PWM waveform | 350 | /// Generate a sequence of PWM waveform |
| 310 | /// | 351 | /// |
| 311 | /// Note: | 352 | /// Note: |
| 312 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | 353 | /// The DMA channel provided does not need to correspond to the requested channel. |
| 313 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { | 354 | pub async fn waveform<C: TimerChannel, W: Word + Into<T::Word>>( |
| 314 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | 355 | &mut self, |
| 315 | let req = dma.request(); | 356 | dma: Peri<'_, impl super::Dma<T, C>>, |
| 316 | 357 | channel: Channel, | |
| 317 | let original_duty_state = self.channel(channel).current_duty_cycle(); | 358 | duty: &[W], |
| 318 | let original_enable_state = self.channel(channel).is_enabled(); | 359 | ) { |
| 319 | let original_update_dma_state = self.inner.get_update_dma_state(); | 360 | self.inner.enable_channel(channel, true); |
| 320 | 361 | self.inner.enable_channel(C::CHANNEL, true); | |
| 321 | if !original_update_dma_state { | 362 | self.inner.clamp_compare_value::<W>(channel); |
| 322 | self.inner.enable_update_dma(true); | 363 | self.inner.set_cc_dma_selection(Ccds::ON_UPDATE); |
| 323 | } | 364 | self.inner.set_cc_dma_enable_state(C::CHANNEL, true); |
| 324 | 365 | self.inner.setup_channel_update_dma(dma, channel, duty).await; | |
| 325 | if !original_enable_state { | 366 | self.inner.set_cc_dma_enable_state(C::CHANNEL, false); |
| 326 | self.channel(channel).enable(); | 367 | } |
| 327 | } | ||
| 328 | |||
| 329 | unsafe { | ||
| 330 | #[cfg(not(any(bdma, gpdma)))] | ||
| 331 | use crate::dma::{Burst, FifoThreshold}; | ||
| 332 | use crate::dma::{Transfer, TransferOptions}; | ||
| 333 | |||
| 334 | let dma_transfer_option = TransferOptions { | ||
| 335 | #[cfg(not(any(bdma, gpdma)))] | ||
| 336 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 337 | #[cfg(not(any(bdma, gpdma)))] | ||
| 338 | mburst: Burst::Incr8, | ||
| 339 | ..Default::default() | ||
| 340 | }; | ||
| 341 | |||
| 342 | Transfer::new_write( | ||
| 343 | dma, | ||
| 344 | req, | ||
| 345 | duty, | ||
| 346 | self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, | ||
| 347 | dma_transfer_option, | ||
| 348 | ) | ||
| 349 | .await | ||
| 350 | }; | ||
| 351 | |||
| 352 | // restore output compare state | ||
| 353 | if !original_enable_state { | ||
| 354 | self.channel(channel).disable(); | ||
| 355 | } | ||
| 356 | |||
| 357 | self.channel(channel).set_duty_cycle(original_duty_state); | ||
| 358 | 368 | ||
| 359 | // Since DMA is closed before timer update event trigger DMA is turn off, | 369 | /// Generate a sequence of PWM waveform |
| 360 | // this can almost always trigger a DMA FIFO error. | 370 | /// |
| 361 | // | 371 | /// Note: |
| 362 | // optional TODO: | 372 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. |
| 363 | // clean FEIF after disable UDE | 373 | /// Also be aware that embassy timers use one of timers internally. It is possible to |
| 364 | if !original_update_dma_state { | 374 | /// switch this timer by using `time-driver-timX` feature. |
| 365 | self.inner.enable_update_dma(false); | 375 | pub async fn waveform_up<W: Word + Into<T::Word>>( |
| 366 | } | 376 | &mut self, |
| 377 | dma: Peri<'_, impl super::UpDma<T>>, | ||
| 378 | channel: Channel, | ||
| 379 | duty: &[W], | ||
| 380 | ) { | ||
| 381 | self.inner.enable_channel(channel, true); | ||
| 382 | self.inner.clamp_compare_value::<W>(channel); | ||
| 383 | self.inner.enable_update_dma(true); | ||
| 384 | self.inner.setup_update_dma(dma, channel, duty).await; | ||
| 385 | self.inner.enable_update_dma(false); | ||
| 367 | } | 386 | } |
| 368 | 387 | ||
| 369 | /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. | 388 | /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. |
| @@ -378,167 +397,43 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 378 | /// | 397 | /// |
| 379 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: | 398 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: |
| 380 | /// | 399 | /// |
| 400 | /// ```rust,ignore | ||
| 381 | /// let dma_buf: [u16; 16] = [ | 401 | /// let dma_buf: [u16; 16] = [ |
| 382 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 | 402 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 |
| 383 | /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 | 403 | /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 |
| 384 | /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 | 404 | /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 |
| 385 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 | 405 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 |
| 386 | /// ]; | 406 | /// ]; |
| 407 | /// ``` | ||
| 387 | /// | 408 | /// |
| 388 | /// Each group of N values (where N = number of channels) is transferred on one update event, | 409 | /// Each group of `N` values (where `N` is number of channels) is transferred on one update event, |
| 389 | /// updating the duty cycles of all selected channels simultaneously. | 410 | /// updating the duty cycles of all selected channels simultaneously. |
| 390 | /// | 411 | /// |
| 391 | /// Note: | 412 | /// Note: |
| 392 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | 413 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. |
| 393 | pub async fn waveform_up_multi_channel( | 414 | /// Also be aware that embassy timers use one of timers internally. It is possible to |
| 415 | /// switch this timer by using `time-driver-timX` feature. | ||
| 416 | /// | ||
| 417 | pub async fn waveform_up_multi_channel<W: Word + Into<T::Word>>( | ||
| 394 | &mut self, | 418 | &mut self, |
| 395 | dma: Peri<'_, impl super::UpDma<T>>, | 419 | dma: Peri<'_, impl super::UpDma<T>>, |
| 396 | starting_channel: Channel, | 420 | starting_channel: Channel, |
| 397 | ending_channel: Channel, | 421 | ending_channel: Channel, |
| 398 | duty: &[u16], | 422 | duty: &[W], |
| 399 | ) { | 423 | ) { |
| 400 | let cr1_addr = self.inner.regs_gp16().cr1().as_ptr() as u32; | 424 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] |
| 401 | let start_ch_index = starting_channel.index(); | 425 | .iter() |
| 402 | let end_ch_index = ending_channel.index(); | 426 | .filter(|ch| ch.index() >= starting_channel.index()) |
| 403 | 427 | .filter(|ch| ch.index() <= ending_channel.index()) | |
| 404 | assert!(start_ch_index <= end_ch_index); | 428 | .for_each(|ch| { |
| 405 | 429 | self.inner.enable_channel(*ch, true); | |
| 406 | let ccrx_addr = self.inner.regs_gp16().ccr(start_ch_index).as_ptr() as u32; | 430 | self.inner.clamp_compare_value::<W>(*ch); |
| 407 | self.inner | 431 | }); |
| 408 | .regs_gp16() | 432 | self.inner.enable_update_dma(true); |
| 409 | .dcr() | ||
| 410 | .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8)); | ||
| 411 | self.inner | 433 | self.inner |
| 412 | .regs_gp16() | 434 | .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) |
| 413 | .dcr() | 435 | .await; |
| 414 | .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8)); | 436 | self.inner.enable_update_dma(false); |
| 415 | |||
| 416 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 417 | let req = dma.request(); | ||
| 418 | |||
| 419 | let original_update_dma_state = self.inner.get_update_dma_state(); | ||
| 420 | if !original_update_dma_state { | ||
| 421 | self.inner.enable_update_dma(true); | ||
| 422 | } | ||
| 423 | |||
| 424 | unsafe { | ||
| 425 | #[cfg(not(any(bdma, gpdma)))] | ||
| 426 | use crate::dma::{Burst, FifoThreshold}; | ||
| 427 | use crate::dma::{Transfer, TransferOptions}; | ||
| 428 | |||
| 429 | let dma_transfer_option = TransferOptions { | ||
| 430 | #[cfg(not(any(bdma, gpdma)))] | ||
| 431 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 432 | #[cfg(not(any(bdma, gpdma)))] | ||
| 433 | mburst: Burst::Incr4, | ||
| 434 | ..Default::default() | ||
| 435 | }; | ||
| 436 | |||
| 437 | Transfer::new_write( | ||
| 438 | dma, | ||
| 439 | req, | ||
| 440 | duty, | ||
| 441 | self.inner.regs_gp16().dmar().as_ptr() as *mut u16, | ||
| 442 | dma_transfer_option, | ||
| 443 | ) | ||
| 444 | .await | ||
| 445 | }; | ||
| 446 | |||
| 447 | if !original_update_dma_state { | ||
| 448 | self.inner.enable_update_dma(false); | ||
| 449 | } | ||
| 450 | } | ||
| 451 | } | ||
| 452 | |||
| 453 | impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | ||
| 454 | /// Generate a sequence of PWM waveform | ||
| 455 | pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) { | ||
| 456 | use crate::pac::timer::vals::Ccds; | ||
| 457 | |||
| 458 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 459 | let req = dma.request(); | ||
| 460 | |||
| 461 | let cc_channel = C::CHANNEL; | ||
| 462 | |||
| 463 | let original_duty_state = self.channel(cc_channel).current_duty_cycle(); | ||
| 464 | let original_enable_state = self.channel(cc_channel).is_enabled(); | ||
| 465 | let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE; | ||
| 466 | let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); | ||
| 467 | |||
| 468 | // redirect CC DMA request onto Update Event | ||
| 469 | if !original_cc_dma_on_update { | ||
| 470 | self.inner.set_cc_dma_selection(Ccds::ON_UPDATE) | ||
| 471 | } | ||
| 472 | |||
| 473 | if !original_cc_dma_enabled { | ||
| 474 | self.inner.set_cc_dma_enable_state(cc_channel, true); | ||
| 475 | } | ||
| 476 | |||
| 477 | if !original_enable_state { | ||
| 478 | self.channel(cc_channel).enable(); | ||
| 479 | } | ||
| 480 | |||
| 481 | unsafe { | ||
| 482 | #[cfg(not(any(bdma, gpdma)))] | ||
| 483 | use crate::dma::{Burst, FifoThreshold}; | ||
| 484 | use crate::dma::{Transfer, TransferOptions}; | ||
| 485 | |||
| 486 | let dma_transfer_option = TransferOptions { | ||
| 487 | #[cfg(not(any(bdma, gpdma)))] | ||
| 488 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 489 | #[cfg(not(any(bdma, gpdma)))] | ||
| 490 | mburst: Burst::Incr8, | ||
| 491 | ..Default::default() | ||
| 492 | }; | ||
| 493 | |||
| 494 | match self.inner.bits() { | ||
| 495 | TimerBits::Bits16 => { | ||
| 496 | Transfer::new_write( | ||
| 497 | dma, | ||
| 498 | req, | ||
| 499 | duty, | ||
| 500 | self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16, | ||
| 501 | dma_transfer_option, | ||
| 502 | ) | ||
| 503 | .await | ||
| 504 | } | ||
| 505 | #[cfg(not(any(stm32l0)))] | ||
| 506 | TimerBits::Bits32 => { | ||
| 507 | #[cfg(not(any(bdma, gpdma)))] | ||
| 508 | panic!("unsupported timer bits"); | ||
| 509 | |||
| 510 | #[cfg(any(bdma, gpdma))] | ||
| 511 | Transfer::new_write( | ||
| 512 | dma, | ||
| 513 | req, | ||
| 514 | duty, | ||
| 515 | self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32, | ||
| 516 | dma_transfer_option, | ||
| 517 | ) | ||
| 518 | .await | ||
| 519 | } | ||
| 520 | }; | ||
| 521 | }; | ||
| 522 | |||
| 523 | // restore output compare state | ||
| 524 | if !original_enable_state { | ||
| 525 | self.channel(cc_channel).disable(); | ||
| 526 | } | ||
| 527 | |||
| 528 | self.channel(cc_channel).set_duty_cycle(original_duty_state); | ||
| 529 | |||
| 530 | // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, | ||
| 531 | // this can almost always trigger a DMA FIFO error. | ||
| 532 | // | ||
| 533 | // optional TODO: | ||
| 534 | // clean FEIF after disable UDE | ||
| 535 | if !original_cc_dma_enabled { | ||
| 536 | self.inner.set_cc_dma_enable_state(cc_channel, false); | ||
| 537 | } | ||
| 538 | |||
| 539 | if !original_cc_dma_on_update { | ||
| 540 | self.inner.set_cc_dma_selection(Ccds::ON_COMPARE) | ||
| 541 | } | ||
| 542 | } | 437 | } |
| 543 | } | 438 | } |
| 544 | 439 | ||
| @@ -548,11 +443,11 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePw | |||
| 548 | 443 | ||
| 549 | impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> { | 444 | impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> { |
| 550 | fn max_duty_cycle(&self) -> u16 { | 445 | fn max_duty_cycle(&self) -> u16 { |
| 551 | self.max_duty_cycle() | 446 | unwrap!(self.max_duty_cycle().try_into()) |
| 552 | } | 447 | } |
| 553 | 448 | ||
| 554 | fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { | 449 | fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { |
| 555 | self.set_duty_cycle(duty); | 450 | self.set_duty_cycle(duty.into()); |
| 556 | Ok(()) | 451 | Ok(()) |
| 557 | } | 452 | } |
| 558 | 453 | ||
| @@ -567,7 +462,7 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for Simpl | |||
| 567 | } | 462 | } |
| 568 | 463 | ||
| 569 | fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { | 464 | fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { |
| 570 | self.set_duty_cycle_fraction(num, denom); | 465 | self.set_duty_cycle_fraction(num.into(), denom.into()); |
| 571 | Ok(()) | 466 | Ok(()) |
| 572 | } | 467 | } |
| 573 | 468 | ||
| @@ -595,16 +490,16 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> { | |||
| 595 | } | 490 | } |
| 596 | 491 | ||
| 597 | fn get_duty(&self, channel: Self::Channel) -> Self::Duty { | 492 | fn get_duty(&self, channel: Self::Channel) -> Self::Duty { |
| 598 | self.inner.get_compare_value(channel) | 493 | self.inner.get_compare_value(channel).into() |
| 599 | } | 494 | } |
| 600 | 495 | ||
| 601 | fn get_max_duty(&self) -> Self::Duty { | 496 | fn get_max_duty(&self) -> Self::Duty { |
| 602 | self.inner.get_max_compare_value() + 1 | 497 | self.inner.get_max_compare_value().into() + 1 |
| 603 | } | 498 | } |
| 604 | 499 | ||
| 605 | fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { | 500 | fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { |
| 606 | assert!(duty <= self.max_duty_cycle() as u32); | 501 | assert!(duty <= self.max_duty_cycle() as u32); |
| 607 | self.inner.set_compare_value(channel, duty) | 502 | self.inner.set_compare_value(channel, unwrap!(duty.try_into())) |
| 608 | } | 503 | } |
| 609 | 504 | ||
| 610 | fn set_period<P>(&mut self, period: P) | 505 | fn set_period<P>(&mut self, period: P) |
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..ae86d28f0 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,11 +28,11 @@ 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, |
| 35 | #[cfg(peri_ucpd1)] ucpd1_db_enable: bool, | 35 | #[cfg(all(peri_ucpd1, not(stm32n6)))] ucpd1_db_enable: bool, |
| 36 | #[cfg(peri_ucpd2)] ucpd2_db_enable: bool, | 36 | #[cfg(peri_ucpd2)] ucpd2_db_enable: bool, |
| 37 | ) { | 37 | ) { |
| 38 | #[cfg(stm32g0x1)] | 38 | #[cfg(stm32g0x1)] |
| @@ -349,6 +349,7 @@ impl<'d, T: Instance> CcPhy<'d, T> { | |||
| 349 | critical_section::with(|cs| { | 349 | critical_section::with(|cs| { |
| 350 | init( | 350 | init( |
| 351 | cs, | 351 | cs, |
| 352 | #[cfg(not(stm32n6))] | ||
| 352 | false, | 353 | false, |
| 353 | #[cfg(peri_ucpd2)] | 354 | #[cfg(peri_ucpd2)] |
| 354 | false, | 355 | false, |
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..26d2b8991 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 | ||
| @@ -80,7 +87,7 @@ unsafe fn on_interrupt(r: Regs, state: &'static State) { | |||
| 80 | // With `usart_v4` hardware FIFO is enabled and Transmission complete (TC) | 87 | // With `usart_v4` hardware FIFO is enabled and Transmission complete (TC) |
| 81 | // indicates that all bytes are pushed out from the FIFO. | 88 | // indicates that all bytes are pushed out from the FIFO. |
| 82 | // For other usart variants it shows that last byte from the buffer was just sent. | 89 | // For other usart variants it shows that last byte from the buffer was just sent. |
| 83 | if sr_val.tc() { | 90 | if sr_val.tc() && r.cr1().read().tcie() { |
| 84 | // For others it is cleared above with `clear_interrupt_flags`. | 91 | // For others it is cleared above with `clear_interrupt_flags`. |
| 85 | #[cfg(any(usart_v1, usart_v2))] | 92 | #[cfg(any(usart_v1, usart_v2))] |
| 86 | sr(r).modify(|w| w.set_tc(false)); | 93 | sr(r).modify(|w| w.set_tc(false)); |
| @@ -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..d2c361c61 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 | } |
| @@ -457,6 +491,8 @@ impl<'d> UartTx<'d, Async> { | |||
| 457 | 491 | ||
| 458 | /// Initiate an asynchronous UART write | 492 | /// Initiate an asynchronous UART write |
| 459 | pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { | 493 | pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 494 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 495 | |||
| 460 | let r = self.info.regs; | 496 | let r = self.info.regs; |
| 461 | 497 | ||
| 462 | half_duplex_set_rx_tx_before_write(&r, self.duplex == Duplex::Half(HalfDuplexReadback::Readback)); | 498 | half_duplex_set_rx_tx_before_write(&r, self.duplex == Duplex::Half(HalfDuplexReadback::Readback)); |
| @@ -474,6 +510,8 @@ impl<'d> UartTx<'d, Async> { | |||
| 474 | 510 | ||
| 475 | /// Wait until transmission complete | 511 | /// Wait until transmission complete |
| 476 | pub async fn flush(&mut self) -> Result<(), Error> { | 512 | pub async fn flush(&mut self) -> Result<(), Error> { |
| 513 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 514 | |||
| 477 | flush(&self.info, &self.state).await | 515 | flush(&self.info, &self.state).await |
| 478 | } | 516 | } |
| 479 | } | 517 | } |
| @@ -535,7 +573,7 @@ impl<'d, M: Mode> UartTx<'d, M> { | |||
| 535 | let state = self.state; | 573 | let state = self.state; |
| 536 | state.tx_rx_refcount.store(1, Ordering::Relaxed); | 574 | state.tx_rx_refcount.store(1, Ordering::Relaxed); |
| 537 | 575 | ||
| 538 | info.rcc.enable_and_reset(); | 576 | info.rcc.enable_and_reset_without_stop(); |
| 539 | 577 | ||
| 540 | info.regs.cr3().modify(|w| { | 578 | info.regs.cr3().modify(|w| { |
| 541 | w.set_ctse(self.cts.is_some()); | 579 | w.set_ctse(self.cts.is_some()); |
| @@ -692,6 +730,8 @@ impl<'d> UartRx<'d, Async> { | |||
| 692 | 730 | ||
| 693 | /// Initiate an asynchronous UART read | 731 | /// Initiate an asynchronous UART read |
| 694 | pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { | 732 | pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { |
| 733 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 734 | |||
| 695 | self.inner_read(buffer, false).await?; | 735 | self.inner_read(buffer, false).await?; |
| 696 | 736 | ||
| 697 | Ok(()) | 737 | Ok(()) |
| @@ -699,6 +739,8 @@ impl<'d> UartRx<'d, Async> { | |||
| 699 | 739 | ||
| 700 | /// Initiate an asynchronous read with idle line detection enabled | 740 | /// Initiate an asynchronous read with idle line detection enabled |
| 701 | pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { | 741 | pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { |
| 742 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 743 | |||
| 702 | self.inner_read(buffer, true).await | 744 | self.inner_read(buffer, true).await |
| 703 | } | 745 | } |
| 704 | 746 | ||
| @@ -966,8 +1008,11 @@ impl<'d, M: Mode> UartRx<'d, M> { | |||
| 966 | let info = self.info; | 1008 | let info = self.info; |
| 967 | let state = self.state; | 1009 | let state = self.state; |
| 968 | state.tx_rx_refcount.store(1, Ordering::Relaxed); | 1010 | state.tx_rx_refcount.store(1, Ordering::Relaxed); |
| 1011 | state | ||
| 1012 | .eager_reads | ||
| 1013 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 969 | 1014 | ||
| 970 | info.rcc.enable_and_reset(); | 1015 | info.rcc.enable_and_reset_without_stop(); |
| 971 | 1016 | ||
| 972 | info.regs.cr3().write(|w| { | 1017 | info.regs.cr3().write(|w| { |
| 973 | w.set_rtse(self.rts.is_some()); | 1018 | w.set_rtse(self.rts.is_some()); |
| @@ -982,6 +1027,9 @@ impl<'d, M: Mode> UartRx<'d, M> { | |||
| 982 | 1027 | ||
| 983 | /// Reconfigure the driver | 1028 | /// Reconfigure the driver |
| 984 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | 1029 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { |
| 1030 | self.state | ||
| 1031 | .eager_reads | ||
| 1032 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 985 | reconfigure(self.info, self.kernel_clock, config) | 1033 | reconfigure(self.info, self.kernel_clock, config) |
| 986 | } | 1034 | } |
| 987 | 1035 | ||
| @@ -1103,7 +1151,7 @@ fn drop_tx_rx(info: &Info, state: &State) { | |||
| 1103 | refcount == 1 | 1151 | refcount == 1 |
| 1104 | }); | 1152 | }); |
| 1105 | if is_last_drop { | 1153 | if is_last_drop { |
| 1106 | info.rcc.disable(); | 1154 | info.rcc.disable_without_stop(); |
| 1107 | } | 1155 | } |
| 1108 | } | 1156 | } |
| 1109 | 1157 | ||
| @@ -1462,8 +1510,11 @@ impl<'d, M: Mode> Uart<'d, M> { | |||
| 1462 | let info = self.rx.info; | 1510 | let info = self.rx.info; |
| 1463 | let state = self.rx.state; | 1511 | let state = self.rx.state; |
| 1464 | state.tx_rx_refcount.store(2, Ordering::Relaxed); | 1512 | state.tx_rx_refcount.store(2, Ordering::Relaxed); |
| 1513 | state | ||
| 1514 | .eager_reads | ||
| 1515 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 1465 | 1516 | ||
| 1466 | info.rcc.enable_and_reset(); | 1517 | info.rcc.enable_and_reset_without_stop(); |
| 1467 | 1518 | ||
| 1468 | info.regs.cr3().write(|w| { | 1519 | info.regs.cr3().write(|w| { |
| 1469 | w.set_rtse(self.rx.rts.is_some()); | 1520 | w.set_rtse(self.rx.rts.is_some()); |
| @@ -1690,6 +1741,16 @@ fn configure( | |||
| 1690 | return Err(ConfigError::RxOrTxNotEnabled); | 1741 | return Err(ConfigError::RxOrTxNotEnabled); |
| 1691 | } | 1742 | } |
| 1692 | 1743 | ||
| 1744 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 1745 | let dem = r.cr3().read().dem(); | ||
| 1746 | |||
| 1747 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 1748 | if config.de_assertion_time > 31 { | ||
| 1749 | return Err(ConfigError::DeAssertionTimeTooHigh); | ||
| 1750 | } else if config.de_deassertion_time > 31 { | ||
| 1751 | return Err(ConfigError::DeDeassertionTimeTooHigh); | ||
| 1752 | } | ||
| 1753 | |||
| 1693 | // UART must be disabled during configuration. | 1754 | // UART must be disabled during configuration. |
| 1694 | r.cr1().modify(|w| { | 1755 | r.cr1().modify(|w| { |
| 1695 | w.set_ue(false); | 1756 | w.set_ue(false); |
| @@ -1738,6 +1799,20 @@ fn configure( | |||
| 1738 | w.set_re(enable_rx); | 1799 | w.set_re(enable_rx); |
| 1739 | } | 1800 | } |
| 1740 | 1801 | ||
| 1802 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 1803 | if dem { | ||
| 1804 | w.set_deat(if over8 { | ||
| 1805 | config.de_assertion_time / 2 | ||
| 1806 | } else { | ||
| 1807 | config.de_assertion_time | ||
| 1808 | }); | ||
| 1809 | w.set_dedt(if over8 { | ||
| 1810 | config.de_assertion_time / 2 | ||
| 1811 | } else { | ||
| 1812 | config.de_assertion_time | ||
| 1813 | }); | ||
| 1814 | } | ||
| 1815 | |||
| 1741 | // configure word size and parity, since the parity bit is inserted into the MSB position, | 1816 | // configure word size and parity, since the parity bit is inserted into the MSB position, |
| 1742 | // it increases the effective word size | 1817 | // it increases the effective word size |
| 1743 | match (config.parity, config.data_bits) { | 1818 | match (config.parity, config.data_bits) { |
| @@ -2022,6 +2097,7 @@ struct State { | |||
| 2022 | rx_waker: AtomicWaker, | 2097 | rx_waker: AtomicWaker, |
| 2023 | tx_waker: AtomicWaker, | 2098 | tx_waker: AtomicWaker, |
| 2024 | tx_rx_refcount: AtomicU8, | 2099 | tx_rx_refcount: AtomicU8, |
| 2100 | eager_reads: AtomicUsize, | ||
| 2025 | } | 2101 | } |
| 2026 | 2102 | ||
| 2027 | impl State { | 2103 | impl State { |
| @@ -2030,6 +2106,7 @@ impl State { | |||
| 2030 | rx_waker: AtomicWaker::new(), | 2106 | rx_waker: AtomicWaker::new(), |
| 2031 | tx_waker: AtomicWaker::new(), | 2107 | tx_waker: AtomicWaker::new(), |
| 2032 | tx_rx_refcount: AtomicU8::new(0), | 2108 | tx_rx_refcount: AtomicU8::new(0), |
| 2109 | eager_reads: AtomicUsize::new(0), | ||
| 2033 | } | 2110 | } |
| 2034 | } | 2111 | } |
| 2035 | } | 2112 | } |
diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index 5f4e87834..cc5224b69 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, |
| @@ -116,6 +117,8 @@ impl<'d> UartRx<'d, Async> { | |||
| 116 | let rx = unsafe { self.rx.as_ref().map(|x| x.clone_unchecked()) }; | 117 | let rx = unsafe { self.rx.as_ref().map(|x| x.clone_unchecked()) }; |
| 117 | let rts = unsafe { self.rts.as_ref().map(|x| x.clone_unchecked()) }; | 118 | let rts = unsafe { self.rts.as_ref().map(|x| x.clone_unchecked()) }; |
| 118 | 119 | ||
| 120 | info.rcc.increment_stop_refcount(); | ||
| 121 | |||
| 119 | // Don't disable the clock | 122 | // Don't disable the clock |
| 120 | mem::forget(self); | 123 | mem::forget(self); |
| 121 | 124 | ||
| @@ -133,6 +136,9 @@ impl<'d> UartRx<'d, Async> { | |||
| 133 | impl<'d> RingBufferedUartRx<'d> { | 136 | impl<'d> RingBufferedUartRx<'d> { |
| 134 | /// Reconfigure the driver | 137 | /// Reconfigure the driver |
| 135 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | 138 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { |
| 139 | self.state | ||
| 140 | .eager_reads | ||
| 141 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 136 | reconfigure(self.info, self.kernel_clock, config) | 142 | reconfigure(self.info, self.kernel_clock, config) |
| 137 | } | 143 | } |
| 138 | 144 | ||
| @@ -148,8 +154,8 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 148 | let r = self.info.regs; | 154 | let r = self.info.regs; |
| 149 | // clear all interrupts and DMA Rx Request | 155 | // clear all interrupts and DMA Rx Request |
| 150 | r.cr1().modify(|w| { | 156 | r.cr1().modify(|w| { |
| 151 | // disable RXNE interrupt | 157 | // use RXNE only when returning reads early |
| 152 | w.set_rxneie(false); | 158 | w.set_rxneie(self.state.eager_reads.load(Ordering::Relaxed) > 0); |
| 153 | // enable parity interrupt if not ParityNone | 159 | // enable parity interrupt if not ParityNone |
| 154 | w.set_peie(w.pce()); | 160 | w.set_peie(w.pce()); |
| 155 | // enable idle line interrupt | 161 | // enable idle line interrupt |
| @@ -248,39 +254,67 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 248 | async fn wait_for_data_or_idle(&mut self) -> Result<(), Error> { | 254 | async fn wait_for_data_or_idle(&mut self) -> Result<(), Error> { |
| 249 | compiler_fence(Ordering::SeqCst); | 255 | compiler_fence(Ordering::SeqCst); |
| 250 | 256 | ||
| 251 | // Future which completes when idle line is detected | 257 | loop { |
| 252 | let s = self.state; | 258 | // Future which completes when idle line is detected |
| 253 | let uart = poll_fn(|cx| { | 259 | let s = self.state; |
| 254 | s.rx_waker.register(cx.waker()); | 260 | let mut uart_init = false; |
| 255 | 261 | let uart = poll_fn(|cx| { | |
| 256 | compiler_fence(Ordering::SeqCst); | 262 | s.rx_waker.register(cx.waker()); |
| 257 | 263 | ||
| 258 | if check_idle_and_errors(self.info.regs)? { | 264 | compiler_fence(Ordering::SeqCst); |
| 259 | // Idle line is detected | 265 | |
| 260 | Poll::Ready(Ok(())) | 266 | // We may have been woken by IDLE or, if eager_reads is set, by RXNE. |
| 261 | } else { | 267 | // However, DMA will clear RXNE, so we can't check directly, and because |
| 262 | Poll::Pending | 268 | // the other future borrows `ring_buf`, we can't check `len()` here either. |
| 263 | } | 269 | // Instead, return from this future and we'll check the length afterwards. |
| 264 | }); | 270 | let eager = s.eager_reads.load(Ordering::Relaxed) > 0; |
| 271 | |||
| 272 | let idle = check_idle_and_errors(self.info.regs)?; | ||
| 273 | if idle || (eager && uart_init) { | ||
| 274 | // Idle line is detected, or eager reads is set and some data is available. | ||
| 275 | Poll::Ready(Ok(idle)) | ||
| 276 | } else { | ||
| 277 | uart_init = true; | ||
| 278 | Poll::Pending | ||
| 279 | } | ||
| 280 | }); | ||
| 265 | 281 | ||
| 266 | let mut dma_init = false; | 282 | let mut dma_init = false; |
| 267 | // Future which completes when the DMA controller indicates it | 283 | // Future which completes when the DMA controller indicates it |
| 268 | // has written to the ring buffer's middle byte, or last byte | 284 | // has written to the ring buffer's middle byte, or last byte |
| 269 | let dma = poll_fn(|cx| { | 285 | let dma = poll_fn(|cx| { |
| 270 | self.ring_buf.set_waker(cx.waker()); | 286 | self.ring_buf.set_waker(cx.waker()); |
| 271 | 287 | ||
| 272 | let status = match dma_init { | 288 | let status = match dma_init { |
| 273 | false => Poll::Pending, | 289 | false => Poll::Pending, |
| 274 | true => Poll::Ready(()), | 290 | true => Poll::Ready(()), |
| 275 | }; | 291 | }; |
| 276 | 292 | ||
| 277 | dma_init = true; | 293 | dma_init = true; |
| 278 | status | 294 | status |
| 279 | }); | 295 | }); |
| 280 | 296 | ||
| 281 | match select(uart, dma).await { | 297 | match select(uart, dma).await { |
| 282 | Either::Left((result, _)) => result, | 298 | // UART woke with line idle |
| 283 | Either::Right(((), _)) => Ok(()), | 299 | Either::Left((Ok(true), _)) => { |
| 300 | return Ok(()); | ||
| 301 | } | ||
| 302 | // UART woke without idle or error: word received | ||
| 303 | Either::Left((Ok(false), _)) => { | ||
| 304 | let eager = self.state.eager_reads.load(Ordering::Relaxed); | ||
| 305 | if eager > 0 && self.ring_buf.len().unwrap_or(0) >= eager { | ||
| 306 | return Ok(()); | ||
| 307 | } else { | ||
| 308 | continue; | ||
| 309 | } | ||
| 310 | } | ||
| 311 | // UART woke with error | ||
| 312 | Either::Left((Err(e), _)) => { | ||
| 313 | return Err(e); | ||
| 314 | } | ||
| 315 | // DMA woke | ||
| 316 | Either::Right(((), _)) => return Ok(()), | ||
| 317 | } | ||
| 284 | } | 318 | } |
| 285 | } | 319 | } |
| 286 | 320 | ||
| @@ -292,6 +326,7 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 292 | 326 | ||
| 293 | impl Drop for RingBufferedUartRx<'_> { | 327 | impl Drop for RingBufferedUartRx<'_> { |
| 294 | fn drop(&mut self) { | 328 | fn drop(&mut self) { |
| 329 | self.info.rcc.decrement_stop_refcount(); | ||
| 295 | self.stop_uart(); | 330 | self.stop_uart(); |
| 296 | self.rx.as_ref().map(|x| x.set_as_disconnected()); | 331 | self.rx.as_ref().map(|x| x.set_as_disconnected()); |
| 297 | self.rts.as_ref().map(|x| x.set_as_disconnected()); | 332 | self.rts.as_ref().map(|x| x.set_as_disconnected()); |
| @@ -308,26 +343,16 @@ impl Drop for RingBufferedUartRx<'_> { | |||
| 308 | /// For usart_v1 and usart_v2, all status flags must be handled together anyway because all flags | 343 | /// 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. | 344 | /// are cleared by a single read to the RDR register. |
| 310 | fn check_idle_and_errors(r: Regs) -> Result<bool, Error> { | 345 | 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 | 346 | // SAFETY: read only and we only use Rx related flags |
| 312 | let sr = critical_section::with(|_| { | 347 | let sr = sr(r).read(); |
| 313 | // SAFETY: read only and we only use Rx related flags | 348 | |
| 314 | let sr = sr(r).read(); | 349 | #[cfg(not(any(usart_v3, usart_v4)))] |
| 315 | 350 | unsafe { | |
| 316 | #[cfg(any(usart_v3, usart_v4))] | 351 | // This read also clears the error and idle interrupt flags on v1 (TODO and v2?) |
| 317 | r.icr().write(|w| { | 352 | rdr(r).read_volatile() |
| 318 | w.set_idle(true); | 353 | }; |
| 319 | w.set_pe(true); | 354 | clear_interrupt_flags(r, sr); |
| 320 | w.set_fe(true); | 355 | |
| 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() { | 356 | if sr.pe() { |
| 332 | Err(Error::Parity) | 357 | Err(Error::Parity) |
| 333 | } else if sr.fe() { | 358 | } 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..466e1a9b4 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)] |
| @@ -420,9 +420,9 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { | |||
| 420 | return Err(XspiError::InvalidCommand); | 420 | return Err(XspiError::InvalidCommand); |
| 421 | } | 421 | } |
| 422 | 422 | ||
| 423 | T::REGS.cr().modify(|w| { | 423 | T::REGS |
| 424 | w.set_fmode(0.into()); | 424 | .cr() |
| 425 | }); | 425 | .modify(|w| w.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); |
| 426 | 426 | ||
| 427 | // Configure alternate bytes | 427 | // Configure alternate bytes |
| 428 | if let Some(ab) = command.alternate_bytes { | 428 | if let Some(ab) = command.alternate_bytes { |
| @@ -538,8 +538,8 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { | |||
| 538 | w.set_dmaen(false); | 538 | w.set_dmaen(false); |
| 539 | }); | 539 | }); |
| 540 | 540 | ||
| 541 | // self.configure_command(&transaction, Some(buf.len()))?; | 541 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 542 | self.configure_command(&transaction, Some(buf.len())).unwrap(); | 542 | self.configure_command(&transaction, Some(transfer_size_bytes))?; |
| 543 | 543 | ||
| 544 | let current_address = T::REGS.ar().read().address(); | 544 | let current_address = T::REGS.ar().read().address(); |
| 545 | let current_instruction = T::REGS.ir().read().instruction(); | 545 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -578,7 +578,8 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { | |||
| 578 | w.set_dmaen(false); | 578 | w.set_dmaen(false); |
| 579 | }); | 579 | }); |
| 580 | 580 | ||
| 581 | self.configure_command(&transaction, Some(buf.len()))?; | 581 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 582 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 582 | 583 | ||
| 583 | T::REGS | 584 | T::REGS |
| 584 | .cr() | 585 | .cr() |
| @@ -1145,7 +1146,8 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { | |||
| 1145 | // Wait for peripheral to be free | 1146 | // Wait for peripheral to be free |
| 1146 | while T::REGS.sr().read().busy() {} | 1147 | while T::REGS.sr().read().busy() {} |
| 1147 | 1148 | ||
| 1148 | self.configure_command(&transaction, Some(buf.len()))?; | 1149 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1150 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1149 | 1151 | ||
| 1150 | let current_address = T::REGS.ar().read().address(); | 1152 | let current_address = T::REGS.ar().read().address(); |
| 1151 | let current_instruction = T::REGS.ir().read().instruction(); | 1153 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -1160,16 +1162,18 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { | |||
| 1160 | T::REGS.ar().write(|v| v.set_address(current_address)); | 1162 | T::REGS.ar().write(|v| v.set_address(current_address)); |
| 1161 | } | 1163 | } |
| 1162 | 1164 | ||
| 1163 | let transfer = unsafe { | 1165 | for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { |
| 1164 | self.dma | 1166 | let transfer = unsafe { |
| 1165 | .as_mut() | 1167 | self.dma |
| 1166 | .unwrap() | 1168 | .as_mut() |
| 1167 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | 1169 | .unwrap() |
| 1168 | }; | 1170 | .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) |
| 1171 | }; | ||
| 1169 | 1172 | ||
| 1170 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1173 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1171 | 1174 | ||
| 1172 | transfer.blocking_wait(); | 1175 | transfer.blocking_wait(); |
| 1176 | } | ||
| 1173 | 1177 | ||
| 1174 | finish_dma(T::REGS); | 1178 | finish_dma(T::REGS); |
| 1175 | 1179 | ||
| @@ -1185,21 +1189,24 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { | |||
| 1185 | // Wait for peripheral to be free | 1189 | // Wait for peripheral to be free |
| 1186 | while T::REGS.sr().read().busy() {} | 1190 | while T::REGS.sr().read().busy() {} |
| 1187 | 1191 | ||
| 1188 | self.configure_command(&transaction, Some(buf.len()))?; | 1192 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1193 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1189 | T::REGS | 1194 | T::REGS |
| 1190 | .cr() | 1195 | .cr() |
| 1191 | .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); | 1196 | .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); |
| 1192 | 1197 | ||
| 1193 | let transfer = unsafe { | 1198 | for chunk in buf.chunks(0xFFFF / W::size().bytes()) { |
| 1194 | self.dma | 1199 | let transfer = unsafe { |
| 1195 | .as_mut() | 1200 | self.dma |
| 1196 | .unwrap() | 1201 | .as_mut() |
| 1197 | .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) | 1202 | .unwrap() |
| 1198 | }; | 1203 | .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) |
| 1204 | }; | ||
| 1199 | 1205 | ||
| 1200 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1206 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1201 | 1207 | ||
| 1202 | transfer.blocking_wait(); | 1208 | transfer.blocking_wait(); |
| 1209 | } | ||
| 1203 | 1210 | ||
| 1204 | finish_dma(T::REGS); | 1211 | finish_dma(T::REGS); |
| 1205 | 1212 | ||
| @@ -1215,7 +1222,8 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { | |||
| 1215 | // Wait for peripheral to be free | 1222 | // Wait for peripheral to be free |
| 1216 | while T::REGS.sr().read().busy() {} | 1223 | while T::REGS.sr().read().busy() {} |
| 1217 | 1224 | ||
| 1218 | self.configure_command(&transaction, Some(buf.len()))?; | 1225 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1226 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1219 | 1227 | ||
| 1220 | let current_address = T::REGS.ar().read().address(); | 1228 | let current_address = T::REGS.ar().read().address(); |
| 1221 | let current_instruction = T::REGS.ir().read().instruction(); | 1229 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -1230,16 +1238,18 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { | |||
| 1230 | T::REGS.ar().write(|v| v.set_address(current_address)); | 1238 | T::REGS.ar().write(|v| v.set_address(current_address)); |
| 1231 | } | 1239 | } |
| 1232 | 1240 | ||
| 1233 | let transfer = unsafe { | 1241 | for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { |
| 1234 | self.dma | 1242 | let transfer = unsafe { |
| 1235 | .as_mut() | 1243 | self.dma |
| 1236 | .unwrap() | 1244 | .as_mut() |
| 1237 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | 1245 | .unwrap() |
| 1238 | }; | 1246 | .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) |
| 1247 | }; | ||
| 1239 | 1248 | ||
| 1240 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1249 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1241 | 1250 | ||
| 1242 | transfer.await; | 1251 | transfer.await; |
| 1252 | } | ||
| 1243 | 1253 | ||
| 1244 | finish_dma(T::REGS); | 1254 | finish_dma(T::REGS); |
| 1245 | 1255 | ||
| @@ -1255,21 +1265,25 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { | |||
| 1255 | // Wait for peripheral to be free | 1265 | // Wait for peripheral to be free |
| 1256 | while T::REGS.sr().read().busy() {} | 1266 | while T::REGS.sr().read().busy() {} |
| 1257 | 1267 | ||
| 1258 | self.configure_command(&transaction, Some(buf.len()))?; | 1268 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1269 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1259 | T::REGS | 1270 | T::REGS |
| 1260 | .cr() | 1271 | .cr() |
| 1261 | .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); | 1272 | .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); |
| 1262 | 1273 | ||
| 1263 | let transfer = unsafe { | 1274 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. |
| 1264 | self.dma | 1275 | for chunk in buf.chunks(0xFFFF / W::size().bytes()) { |
| 1265 | .as_mut() | 1276 | let transfer = unsafe { |
| 1266 | .unwrap() | 1277 | self.dma |
| 1267 | .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) | 1278 | .as_mut() |
| 1268 | }; | 1279 | .unwrap() |
| 1280 | .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) | ||
| 1281 | }; | ||
| 1269 | 1282 | ||
| 1270 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1283 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1271 | 1284 | ||
| 1272 | transfer.await; | 1285 | transfer.await; |
| 1286 | } | ||
| 1273 | 1287 | ||
| 1274 | finish_dma(T::REGS); | 1288 | finish_dma(T::REGS); |
| 1275 | 1289 | ||
