diff options
| author | xoviat <[email protected]> | 2025-11-15 11:13:02 -0600 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-11-15 11:13:02 -0600 |
| commit | 548fe48bd89bc619845ddde6495be7a16285a38c (patch) | |
| tree | 8ca532803a36b554ede69e0ea5c8535481051009 | |
| parent | 3fb16229c7a237c29731aa05d5f29e8ea2eb015f (diff) | |
| parent | 435267941c5e585c0de714e3251f3d28426bcdca (diff) | |
Merge branch 'main' into stm32_sai_frame_length
95 files changed, 5567 insertions, 3317 deletions
diff --git a/.helix/languages.toml b/.helix/languages.toml new file mode 100644 index 000000000..d34df4b24 --- /dev/null +++ b/.helix/languages.toml | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | [language-server.rust-analyzer.config.cargo] | ||
| 2 | allTargets = false | ||
| 3 | noDefaultFeatures = true | ||
| 4 | target = "thumbv8m.main-none-eabihf" | ||
| 5 | features = ["stm32n657x0", "time-driver-any", "unstable-pac", "exti"] | ||
| 6 | |||
| 7 | [language-server.rust-analyzer.config.check] | ||
| 8 | allTargets = false | ||
| 9 | noDefaultFeatures = true | ||
| 10 | target = "thumbv8m.main-none-eabihf" | ||
| 11 | features = ["stm32n657x0", "time-driver-any", "unstable-pac", "exti"] | ||
diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index 72ecb116a..f6fe1e14f 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md | |||
| @@ -23,6 +23,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 23 | - bugfix: Do not write to UICR from non-secure code on nrf53 | 23 | - bugfix: Do not write to UICR from non-secure code on nrf53 |
| 24 | - bugfix: Add delay to uart init anomaly fix | 24 | - bugfix: Add delay to uart init anomaly fix |
| 25 | - changed: `BufferedUarte::read_ready` now uses the same definition for 'empty' so following read calls will not block when true is returned | 25 | - changed: `BufferedUarte::read_ready` now uses the same definition for 'empty' so following read calls will not block when true is returned |
| 26 | - added: add `gpiote::InputChannel::wait_for_high()` and `wait_for_low()` to wait for specific signal level | ||
| 27 | - changed: `gpiote::InputChannel::wait()` now takes a mutable reference to `self` to avoid interference from concurrent calls | ||
| 28 | - changed: `gpiote::InputChannel::wait()` now ensures events are seen as soon as the function is called, even if the future is not polled | ||
| 29 | - bugfix: use correct flash size for nRF54l | ||
| 26 | 30 | ||
| 27 | ## 0.8.0 - 2025-09-30 | 31 | ## 0.8.0 - 2025-09-30 |
| 28 | 32 | ||
diff --git a/embassy-nrf/src/chips/nrf54l15_app.rs b/embassy-nrf/src/chips/nrf54l15_app.rs index 0724f2ff6..8846717db 100644 --- a/embassy-nrf/src/chips/nrf54l15_app.rs +++ b/embassy-nrf/src/chips/nrf54l15_app.rs | |||
| @@ -204,7 +204,7 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; | |||
| 204 | 204 | ||
| 205 | // 1.5 MB NVM | 205 | // 1.5 MB NVM |
| 206 | #[allow(unused)] | 206 | #[allow(unused)] |
| 207 | pub const FLASH_SIZE: usize = 1536 * 1024; | 207 | pub const FLASH_SIZE: usize = 1524 * 1024; |
| 208 | 208 | ||
| 209 | embassy_hal_internal::peripherals! { | 209 | embassy_hal_internal::peripherals! { |
| 210 | // PPI | 210 | // PPI |
diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 91944d8cd..d4f6668f3 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs | |||
| @@ -349,16 +349,73 @@ impl<'d> InputChannel<'d> { | |||
| 349 | } | 349 | } |
| 350 | 350 | ||
| 351 | /// Asynchronously wait for an event in this channel. | 351 | /// Asynchronously wait for an event in this channel. |
| 352 | pub async fn wait(&self) { | 352 | /// |
| 353 | let g = self.ch.regs(); | 353 | /// It is possible to call this function and await the returned future later. |
| 354 | let num = self.ch.number(); | 354 | /// If an even occurs in the mean time, the future will immediately report ready. |
| 355 | let waker = self.ch.waker(); | 355 | pub fn wait(&mut self) -> impl Future<Output = ()> { |
| 356 | // NOTE: This is `-> impl Future` and not an `async fn` on purpose. | ||
| 357 | // Otherwise, events will only be detected starting at the first poll of the returned future. | ||
| 358 | Self::wait_internal(&mut self.ch) | ||
| 359 | } | ||
| 360 | |||
| 361 | /// Asynchronously wait for the pin to become high. | ||
| 362 | /// | ||
| 363 | /// The channel must be configured with [`InputChannelPolarity::LoToHi`] or [`InputChannelPolarity::Toggle`]. | ||
| 364 | /// If the channel is not configured to detect rising edges, it is unspecified when the returned future completes. | ||
| 365 | /// | ||
| 366 | /// It is possible to call this function and await the returned future later. | ||
| 367 | /// If an even occurs in the mean time, the future will immediately report ready. | ||
| 368 | pub fn wait_for_high(&mut self) -> impl Future<Output = ()> { | ||
| 369 | // NOTE: This is `-> impl Future` and not an `async fn` on purpose. | ||
| 370 | // Otherwise, events will only be detected starting at the first poll of the returned future. | ||
| 371 | |||
| 372 | // Subscribe to the event before checking the pin level. | ||
| 373 | let wait = Self::wait_internal(&mut self.ch); | ||
| 374 | let pin = &self.pin; | ||
| 375 | async move { | ||
| 376 | if pin.is_high() { | ||
| 377 | return; | ||
| 378 | } | ||
| 379 | wait.await; | ||
| 380 | } | ||
| 381 | } | ||
| 382 | |||
| 383 | /// Asynchronously wait for the pin to become low. | ||
| 384 | /// | ||
| 385 | /// The channel must be configured with [`InputChannelPolarity::HiToLo`] or [`InputChannelPolarity::Toggle`]. | ||
| 386 | /// If the channel is not configured to detect falling edges, it is unspecified when the returned future completes. | ||
| 387 | /// | ||
| 388 | /// It is possible to call this function and await the returned future later. | ||
| 389 | /// If an even occurs in the mean time, the future will immediately report ready. | ||
| 390 | pub fn wait_for_low(&mut self) -> impl Future<Output = ()> { | ||
| 391 | // NOTE: This is `-> impl Future` and not an `async fn` on purpose. | ||
| 392 | // Otherwise, events will only be detected starting at the first poll of the returned future. | ||
| 393 | |||
| 394 | // Subscribe to the event before checking the pin level. | ||
| 395 | let wait = Self::wait_internal(&mut self.ch); | ||
| 396 | let pin = &self.pin; | ||
| 397 | async move { | ||
| 398 | if pin.is_low() { | ||
| 399 | return; | ||
| 400 | } | ||
| 401 | wait.await; | ||
| 402 | } | ||
| 403 | } | ||
| 404 | |||
| 405 | /// Internal implementation for `wait()` and friends. | ||
| 406 | fn wait_internal(channel: &mut Peri<'_, AnyChannel>) -> impl Future<Output = ()> { | ||
| 407 | // NOTE: This is `-> impl Future` and not an `async fn` on purpose. | ||
| 408 | // Otherwise, events will only be detected starting at the first poll of the returned future. | ||
| 409 | |||
| 410 | let g = channel.regs(); | ||
| 411 | let num = channel.number(); | ||
| 412 | let waker = channel.waker(); | ||
| 356 | 413 | ||
| 357 | // Enable interrupt | 414 | // Enable interrupt |
| 358 | g.events_in(num).write_value(0); | 415 | g.events_in(num).write_value(0); |
| 359 | g.intenset(INTNUM).write(|w| w.0 = 1 << num); | 416 | g.intenset(INTNUM).write(|w| w.0 = 1 << num); |
| 360 | 417 | ||
| 361 | poll_fn(|cx| { | 418 | poll_fn(move |cx| { |
| 362 | CHANNEL_WAKERS[waker].register(cx.waker()); | 419 | CHANNEL_WAKERS[waker].register(cx.waker()); |
| 363 | 420 | ||
| 364 | if g.events_in(num).read() != 0 { | 421 | if g.events_in(num).read() != 0 { |
| @@ -367,7 +424,6 @@ impl<'d> InputChannel<'d> { | |||
| 367 | Poll::Pending | 424 | Poll::Pending |
| 368 | } | 425 | } |
| 369 | }) | 426 | }) |
| 370 | .await; | ||
| 371 | } | 427 | } |
| 372 | 428 | ||
| 373 | /// Get the associated input pin. | 429 | /// Get the associated input pin. |
diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md index 3b3cb5351..4b0d738a7 100644 --- a/embassy-rp/CHANGELOG.md +++ b/embassy-rp/CHANGELOG.md | |||
| @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 8 | <!-- next-header --> | 8 | <!-- next-header --> |
| 9 | ## Unreleased - ReleaseDate | 9 | ## Unreleased - ReleaseDate |
| 10 | 10 | ||
| 11 | - Add documentation for pio `get_x` about autopush. | ||
| 11 | - Fix several minor typos in documentation | 12 | - Fix several minor typos in documentation |
| 12 | - Add PIO SPI | 13 | - Add PIO SPI |
| 13 | - Add PIO I2S input | 14 | - Add PIO I2S input |
| @@ -114,3 +115,4 @@ Small release fixing a few gnarly bugs, upgrading is strongly recommended. | |||
| 114 | - rename the Channel trait to Slice and the PwmPin to PwmChannel | 115 | - rename the Channel trait to Slice and the PwmPin to PwmChannel |
| 115 | - i2c: Fix race condition that appears on fast repeated transfers. | 116 | - i2c: Fix race condition that appears on fast repeated transfers. |
| 116 | - Add a basic "read to break" function | 117 | - Add a basic "read to break" function |
| 118 | |||
diff --git a/embassy-rp/src/pio/instr.rs b/embassy-rp/src/pio/instr.rs index b15d507de..304ddb20a 100644 --- a/embassy-rp/src/pio/instr.rs +++ b/embassy-rp/src/pio/instr.rs | |||
| @@ -5,6 +5,10 @@ use crate::pio::{Instance, StateMachine}; | |||
| 5 | 5 | ||
| 6 | impl<'d, PIO: Instance, const SM: usize> StateMachine<'d, PIO, SM> { | 6 | impl<'d, PIO: Instance, const SM: usize> StateMachine<'d, PIO, SM> { |
| 7 | /// Set value of scratch register X. | 7 | /// Set value of scratch register X. |
| 8 | /// | ||
| 9 | /// SAFETY: autopull enabled else it will write undefined data. | ||
| 10 | /// Make sure to have setup the according configuration see | ||
| 11 | /// [shift_out](crate::pio::Config::shift_out) and [auto_fill](crate::pio::ShiftConfig::auto_fill). | ||
| 8 | pub unsafe fn set_x(&mut self, value: u32) { | 12 | pub unsafe fn set_x(&mut self, value: u32) { |
| 9 | const OUT: u16 = InstructionOperands::OUT { | 13 | const OUT: u16 = InstructionOperands::OUT { |
| 10 | destination: OutDestination::X, | 14 | destination: OutDestination::X, |
| @@ -16,6 +20,10 @@ impl<'d, PIO: Instance, const SM: usize> StateMachine<'d, PIO, SM> { | |||
| 16 | } | 20 | } |
| 17 | 21 | ||
| 18 | /// Get value of scratch register X. | 22 | /// Get value of scratch register X. |
| 23 | /// | ||
| 24 | /// SAFETY: autopush enabled else it will read undefined data. | ||
| 25 | /// Make sure to have setup the according configurations see | ||
| 26 | /// [shift_in](crate::pio::Config::shift_in) and [auto_fill](crate::pio::ShiftConfig::auto_fill). | ||
| 19 | pub unsafe fn get_x(&mut self) -> u32 { | 27 | pub unsafe fn get_x(&mut self) -> u32 { |
| 20 | const IN: u16 = InstructionOperands::IN { | 28 | const IN: u16 = InstructionOperands::IN { |
| 21 | source: InSource::X, | 29 | source: InSource::X, |
| @@ -27,6 +35,10 @@ impl<'d, PIO: Instance, const SM: usize> StateMachine<'d, PIO, SM> { | |||
| 27 | } | 35 | } |
| 28 | 36 | ||
| 29 | /// Set value of scratch register Y. | 37 | /// Set value of scratch register Y. |
| 38 | /// | ||
| 39 | /// SAFETY: autopull enabled else it will write undefined data. | ||
| 40 | /// Make sure to have setup the according configuration see | ||
| 41 | /// [shift_out](crate::pio::Config::shift_out) and [auto_fill](crate::pio::ShiftConfig::auto_fill). | ||
| 30 | pub unsafe fn set_y(&mut self, value: u32) { | 42 | pub unsafe fn set_y(&mut self, value: u32) { |
| 31 | const OUT: u16 = InstructionOperands::OUT { | 43 | const OUT: u16 = InstructionOperands::OUT { |
| 32 | destination: OutDestination::Y, | 44 | destination: OutDestination::Y, |
| @@ -38,6 +50,10 @@ impl<'d, PIO: Instance, const SM: usize> StateMachine<'d, PIO, SM> { | |||
| 38 | } | 50 | } |
| 39 | 51 | ||
| 40 | /// Get value of scratch register Y. | 52 | /// Get value of scratch register Y. |
| 53 | /// | ||
| 54 | /// SAFETY: autopush enabled else it will read undefined data. | ||
| 55 | /// Make sure to have setup the according configurations see | ||
| 56 | /// [shift_in](crate::pio::Config::shift_in) and [auto_fill](crate::pio::ShiftConfig::auto_fill). | ||
| 41 | pub unsafe fn get_y(&mut self) -> u32 { | 57 | pub unsafe fn get_y(&mut self) -> u32 { |
| 42 | const IN: u16 = InstructionOperands::IN { | 58 | const IN: u16 = InstructionOperands::IN { |
| 43 | source: InSource::Y, | 59 | source: InSource::Y, |
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 95d844dc8..c3b3cb3ab 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md | |||
| @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 8 | ## Unreleased - ReleaseDate | 8 | ## Unreleased - ReleaseDate |
| 9 | 9 | ||
| 10 | - fix: Allow setting SAI peripheral `frame_length` to `256` | 10 | - fix: Allow setting SAI peripheral `frame_length` to `256` |
| 11 | - fix: flash erase on dual-bank STM32Gxxx | ||
| 12 | - feat: Add support for STM32N657X0 | ||
| 13 | - feat: timer: Add 32-bit timer support to SimplePwm waveform_up method following waveform pattern ([#4717](https://github.com/embassy-rs/embassy/pull/4717)) | ||
| 11 | - feat: Add support for injected ADC measurements for g4 ([#4840](https://github.com/embassy-rs/embassy/pull/4840)) | 14 | - feat: Add support for injected ADC measurements for g4 ([#4840](https://github.com/embassy-rs/embassy/pull/4840)) |
| 12 | - feat: Implement into_ring_buffered for g4 ([#4840](https://github.com/embassy-rs/embassy/pull/4840)) | 15 | - feat: Implement into_ring_buffered for g4 ([#4840](https://github.com/embassy-rs/embassy/pull/4840)) |
| 13 | - feat: Add support for 13-bit address and 16-bit data SDRAM chips | 16 | - feat: Add support for 13-bit address and 16-bit data SDRAM chips |
| @@ -42,7 +45,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 42 | - fix: sdmmc: don't wait for DBCKEND flag on sdmmc_v2 devices as it never fires (Fixes #4723) | 45 | - fix: sdmmc: don't wait for DBCKEND flag on sdmmc_v2 devices as it never fires (Fixes #4723) |
| 43 | - fix: usart: fix race condition in ringbuffered usart | 46 | - fix: usart: fix race condition in ringbuffered usart |
| 44 | - feat: Add backup_sram::init() for H5 devices to access BKPSRAM | 47 | - feat: Add backup_sram::init() for H5 devices to access BKPSRAM |
| 45 | - feat: Add I2C MultiMaster (Slave) support for I2C v1 | 48 | - feat: stm32/i2c v1: Add I2C MultiMaster (Slave) support |
| 49 | - feat: stm32/i2c v2: Add transaction() and blocking_transaction() methods with contract-compliant operation merging | ||
| 46 | - feat: stm32/fdcan: add ability to control automatic recovery from bus off ([#4821](https://github.com/embassy-rs/embassy/pull/4821)) | 50 | - feat: stm32/fdcan: add ability to control automatic recovery from bus off ([#4821](https://github.com/embassy-rs/embassy/pull/4821)) |
| 47 | - low-power: update rtc api to allow reconfig | 51 | - low-power: update rtc api to allow reconfig |
| 48 | - adc: consolidate ringbuffer | 52 | - adc: consolidate ringbuffer |
| @@ -52,6 +56,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 52 | - feat: stm32/dsi support zero parameter commands in `write_cmd` ([#4847](https://github.com/embassy-rs/embassy/pull/4847)) | 56 | - feat: stm32/dsi support zero parameter commands in `write_cmd` ([#4847](https://github.com/embassy-rs/embassy/pull/4847)) |
| 53 | - feat: stm32/spi: added support for slave mode ([#4388](https://github.com/embassy-rs/embassy/pull/4388)) | 57 | - feat: stm32/spi: added support for slave mode ([#4388](https://github.com/embassy-rs/embassy/pull/4388)) |
| 54 | - chore: Updated stm32-metapac and stm32-data dependencies | 58 | - chore: Updated stm32-metapac and stm32-data dependencies |
| 59 | - adc: reogranize and cleanup somewhat. require sample_time to be passed on conversion | ||
| 60 | - fix: stm32/i2c v2 slave: prevent misaligned reads, error false positives, and incorrect counts of bytes read/written | ||
| 61 | - feat: add flash support for c0 family ([#4874](https://github.com/embassy-rs/embassy/pull/4874)) | ||
| 62 | - fix: fixing channel numbers on vbat and vddcore for adc on adc | ||
| 63 | - adc: adding disable to vbat | ||
| 55 | 64 | ||
| 56 | ## 0.4.0 - 2025-08-26 | 65 | ## 0.4.0 - 2025-08-26 |
| 57 | 66 | ||
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 108321d0a..2f4f2ce51 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -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] |
| @@ -186,6 +188,7 @@ embedded-io = { version = "0.6.0" } | |||
| 186 | embedded-io-async = { version = "0.6.1" } | 188 | embedded-io-async = { version = "0.6.1" } |
| 187 | chrono = { version = "^0.4", default-features = false, optional = true } | 189 | chrono = { version = "^0.4", default-features = false, optional = true } |
| 188 | bit_field = "0.10.2" | 190 | bit_field = "0.10.2" |
| 191 | trait-set = "0.3.0" | ||
| 189 | document-features = "0.2.7" | 192 | document-features = "0.2.7" |
| 190 | 193 | ||
| 191 | static_assertions = { version = "1.1" } | 194 | static_assertions = { version = "1.1" } |
| @@ -197,11 +200,11 @@ aligned = "0.4.1" | |||
| 197 | heapless = "0.9.1" | 200 | heapless = "0.9.1" |
| 198 | 201 | ||
| 199 | #stm32-metapac = { version = "18" } | 202 | #stm32-metapac = { version = "18" } |
| 200 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-22374e3344a2c9150b9b3d4da45c03f398fdc54e" } | 203 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b77c8d968f53b18d6bdcd052e354b5070ec2bbc2" } |
| 201 | 204 | ||
| 202 | [build-dependencies] | 205 | [build-dependencies] |
| 203 | #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} | 206 | #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} |
| 204 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-22374e3344a2c9150b9b3d4da45c03f398fdc54e", default-features = false, features = ["metadata"] } | 207 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b77c8d968f53b18d6bdcd052e354b5070ec2bbc2", default-features = false, features = ["metadata"] } |
| 205 | 208 | ||
| 206 | proc-macro2 = "1.0.36" | 209 | proc-macro2 = "1.0.36" |
| 207 | quote = "1.0.15" | 210 | quote = "1.0.15" |
| @@ -213,7 +216,6 @@ proptest = "1.5.0" | |||
| 213 | proptest-state-machine = "0.3.0" | 216 | proptest-state-machine = "0.3.0" |
| 214 | 217 | ||
| 215 | 218 | ||
| 216 | |||
| 217 | [features] | 219 | [features] |
| 218 | default = ["rt"] | 220 | default = ["rt"] |
| 219 | 221 | ||
| @@ -1642,6 +1644,30 @@ stm32l562qe = [ "stm32-metapac/stm32l562qe" ] | |||
| 1642 | stm32l562re = [ "stm32-metapac/stm32l562re" ] | 1644 | stm32l562re = [ "stm32-metapac/stm32l562re" ] |
| 1643 | stm32l562ve = [ "stm32-metapac/stm32l562ve" ] | 1645 | stm32l562ve = [ "stm32-metapac/stm32l562ve" ] |
| 1644 | 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" ] | ||
| 1645 | stm32u031c6 = [ "stm32-metapac/stm32u031c6" ] | 1671 | stm32u031c6 = [ "stm32-metapac/stm32u031c6" ] |
| 1646 | stm32u031c8 = [ "stm32-metapac/stm32u031c8" ] | 1672 | stm32u031c8 = [ "stm32-metapac/stm32u031c8" ] |
| 1647 | stm32u031f4 = [ "stm32-metapac/stm32u031f4" ] | 1673 | stm32u031f4 = [ "stm32-metapac/stm32u031f4" ] |
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 940e29417..1e11eb8dc 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -363,101 +363,108 @@ fn main() { | |||
| 363 | 363 | ||
| 364 | // ======== | 364 | // ======== |
| 365 | // Generate FLASH regions | 365 | // Generate FLASH regions |
| 366 | let mut flash_regions = TokenStream::new(); | 366 | cfgs.declare("flash"); |
| 367 | let flash_memory_regions: Vec<_> = memory | 367 | let mut has_flash = false; |
| 368 | .iter() | 368 | if !chip_name.starts_with("stm32n6") { |
| 369 | .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some()) | 369 | cfgs.enable("flash"); |
| 370 | .collect(); | 370 | has_flash = true; |
| 371 | for region in flash_memory_regions.iter() { | 371 | |
| 372 | let region_name = format_ident!("{}", get_flash_region_name(region.name)); | 372 | let mut flash_regions = TokenStream::new(); |
| 373 | let bank_variant = format_ident!( | 373 | let flash_memory_regions: Vec<_> = memory |
| 374 | "{}", | 374 | .iter() |
| 375 | if region.name.starts_with("BANK_1") { | 375 | .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some()) |
| 376 | "Bank1" | 376 | .collect(); |
| 377 | } else if region.name.starts_with("BANK_2") { | 377 | for region in flash_memory_regions.iter() { |
| 378 | "Bank2" | 378 | let region_name = format_ident!("{}", get_flash_region_name(region.name)); |
| 379 | } else if region.name == "OTP" { | 379 | let bank_variant = format_ident!( |
| 380 | "Otp" | 380 | "{}", |
| 381 | } else { | 381 | if region.name.starts_with("BANK_1") { |
| 382 | continue; | 382 | "Bank1" |
| 383 | } | 383 | } else if region.name.starts_with("BANK_2") { |
| 384 | ); | 384 | "Bank2" |
| 385 | let base = region.address; | 385 | } else if region.name == "OTP" { |
| 386 | let size = region.size; | 386 | "Otp" |
| 387 | let settings = region.settings.as_ref().unwrap(); | 387 | } else { |
| 388 | let erase_size = settings.erase_size; | 388 | continue; |
| 389 | let write_size = settings.write_size; | 389 | } |
| 390 | let erase_value = settings.erase_value; | 390 | ); |
| 391 | 391 | let base = region.address; | |
| 392 | flash_regions.extend(quote! { | 392 | let size = region.size; |
| 393 | pub const #region_name: crate::flash::FlashRegion = crate::flash::FlashRegion { | 393 | let settings = region.settings.as_ref().unwrap(); |
| 394 | bank: crate::flash::FlashBank::#bank_variant, | 394 | let erase_size = settings.erase_size; |
| 395 | base: #base, | 395 | let write_size = settings.write_size; |
| 396 | size: #size, | 396 | let erase_value = settings.erase_value; |
| 397 | erase_size: #erase_size, | 397 | |
| 398 | write_size: #write_size, | 398 | flash_regions.extend(quote! { |
| 399 | erase_value: #erase_value, | 399 | pub const #region_name: crate::flash::FlashRegion = crate::flash::FlashRegion { |
| 400 | _ensure_internal: (), | 400 | bank: crate::flash::FlashBank::#bank_variant, |
| 401 | }; | 401 | base: #base, |
| 402 | }); | 402 | size: #size, |
| 403 | erase_size: #erase_size, | ||
| 404 | write_size: #write_size, | ||
| 405 | erase_value: #erase_value, | ||
| 406 | _ensure_internal: (), | ||
| 407 | }; | ||
| 408 | }); | ||
| 403 | 409 | ||
| 404 | let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); | 410 | let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); |
| 405 | flash_regions.extend(quote! { | 411 | flash_regions.extend(quote! { |
| 406 | #[cfg(flash)] | 412 | #[cfg(flash)] |
| 407 | 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>); | 413 | 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>); |
| 408 | }); | 414 | }); |
| 409 | } | 415 | } |
| 410 | 416 | ||
| 411 | let (fields, (inits, region_names)): (Vec<TokenStream>, (Vec<TokenStream>, Vec<Ident>)) = flash_memory_regions | 417 | let (fields, (inits, region_names)): (Vec<TokenStream>, (Vec<TokenStream>, Vec<Ident>)) = flash_memory_regions |
| 412 | .iter() | 418 | .iter() |
| 413 | .map(|f| { | 419 | .map(|f| { |
| 414 | let region_name = get_flash_region_name(f.name); | 420 | let region_name = get_flash_region_name(f.name); |
| 415 | let field_name = format_ident!("{}", region_name.to_lowercase()); | 421 | let field_name = format_ident!("{}", region_name.to_lowercase()); |
| 416 | let field_type = format_ident!("{}", get_flash_region_type_name(f.name)); | 422 | let field_type = format_ident!("{}", get_flash_region_type_name(f.name)); |
| 417 | let field = quote! { | 423 | let field = quote! { |
| 418 | pub #field_name: #field_type<'d, MODE> | 424 | pub #field_name: #field_type<'d, MODE> |
| 419 | }; | 425 | }; |
| 420 | let region_name = format_ident!("{}", region_name); | 426 | let region_name = format_ident!("{}", region_name); |
| 421 | let init = quote! { | 427 | let init = quote! { |
| 422 | #field_name: #field_type(&#region_name, unsafe { p.clone_unchecked()}, core::marker::PhantomData) | 428 | #field_name: #field_type(&#region_name, unsafe { p.clone_unchecked()}, core::marker::PhantomData) |
| 423 | }; | 429 | }; |
| 424 | 430 | ||
| 425 | (field, (init, region_name)) | 431 | (field, (init, region_name)) |
| 426 | }) | 432 | }) |
| 427 | .unzip(); | 433 | .unzip(); |
| 428 | 434 | ||
| 429 | let regions_len = flash_memory_regions.len(); | 435 | let regions_len = flash_memory_regions.len(); |
| 430 | flash_regions.extend(quote! { | 436 | flash_regions.extend(quote! { |
| 431 | #[cfg(flash)] | 437 | #[cfg(flash)] |
| 432 | pub struct FlashLayout<'d, MODE = crate::flash::Async> { | 438 | pub struct FlashLayout<'d, MODE = crate::flash::Async> { |
| 433 | #(#fields),*, | 439 | #(#fields),*, |
| 434 | _mode: core::marker::PhantomData<MODE>, | 440 | _mode: core::marker::PhantomData<MODE>, |
| 435 | } | 441 | } |
| 436 | 442 | ||
| 437 | #[cfg(flash)] | 443 | #[cfg(flash)] |
| 438 | impl<'d, MODE> FlashLayout<'d, MODE> { | 444 | impl<'d, MODE> FlashLayout<'d, MODE> { |
| 439 | pub(crate) fn new(p: embassy_hal_internal::Peri<'d, crate::peripherals::FLASH>) -> Self { | 445 | pub(crate) fn new(p: embassy_hal_internal::Peri<'d, crate::peripherals::FLASH>) -> Self { |
| 440 | Self { | 446 | Self { |
| 441 | #(#inits),*, | 447 | #(#inits),*, |
| 442 | _mode: core::marker::PhantomData, | 448 | _mode: core::marker::PhantomData, |
| 449 | } | ||
| 443 | } | 450 | } |
| 444 | } | 451 | } |
| 445 | } | ||
| 446 | 452 | ||
| 447 | pub const FLASH_REGIONS: [&crate::flash::FlashRegion; #regions_len] = [ | 453 | pub const FLASH_REGIONS: [&crate::flash::FlashRegion; #regions_len] = [ |
| 448 | #(&#region_names),* | 454 | #(&#region_names),* |
| 449 | ]; | 455 | ]; |
| 450 | }); | 456 | }); |
| 451 | 457 | ||
| 452 | let max_erase_size = flash_memory_regions | 458 | let max_erase_size = flash_memory_regions |
| 453 | .iter() | 459 | .iter() |
| 454 | .map(|region| region.settings.as_ref().unwrap().erase_size) | 460 | .map(|region| region.settings.as_ref().unwrap().erase_size) |
| 455 | .max() | 461 | .max() |
| 456 | .unwrap(); | 462 | .unwrap(); |
| 457 | 463 | ||
| 458 | g.extend(quote! { pub const MAX_ERASE_SIZE: usize = #max_erase_size as usize; }); | 464 | g.extend(quote! { pub const MAX_ERASE_SIZE: usize = #max_erase_size as usize; }); |
| 459 | 465 | ||
| 460 | g.extend(quote! { pub mod flash_regions { #flash_regions } }); | 466 | g.extend(quote! { pub mod flash_regions { #flash_regions } }); |
| 467 | } | ||
| 461 | 468 | ||
| 462 | // ======== | 469 | // ======== |
| 463 | // Extract the rcc registers | 470 | // Extract the rcc registers |
| @@ -1346,6 +1353,8 @@ fn main() { | |||
| 1346 | 1353 | ||
| 1347 | for p in METADATA.peripherals { | 1354 | for p in METADATA.peripherals { |
| 1348 | if let Some(regs) = &p.registers { | 1355 | if let Some(regs) = &p.registers { |
| 1356 | let mut adc_pairs: BTreeMap<u8, (Option<Ident>, Option<Ident>)> = BTreeMap::new(); | ||
| 1357 | |||
| 1349 | for pin in p.pins { | 1358 | for pin in p.pins { |
| 1350 | let key = (regs.kind, pin.signal); | 1359 | let key = (regs.kind, pin.signal); |
| 1351 | if let Some(tr) = signals.get(&key) { | 1360 | if let Some(tr) = signals.get(&key) { |
| @@ -1467,25 +1476,29 @@ fn main() { | |||
| 1467 | }; | 1476 | }; |
| 1468 | 1477 | ||
| 1469 | // H7 has differential voltage measurements | 1478 | // H7 has differential voltage measurements |
| 1470 | let ch: Option<u8> = if pin.signal.starts_with("INP") { | 1479 | let ch: Option<(u8, bool)> = if pin.signal.starts_with("INP") { |
| 1471 | Some(pin.signal.strip_prefix("INP").unwrap().parse().unwrap()) | 1480 | Some((pin.signal.strip_prefix("INP").unwrap().parse().unwrap(), false)) |
| 1472 | } else if pin.signal.starts_with("INN") { | 1481 | } else if pin.signal.starts_with("INN") { |
| 1473 | // TODO handle in the future when embassy supports differential measurements | 1482 | Some((pin.signal.strip_prefix("INN").unwrap().parse().unwrap(), true)) |
| 1474 | None | ||
| 1475 | } else if pin.signal.starts_with("IN") && pin.signal.ends_with('b') { | 1483 | } else if pin.signal.starts_with("IN") && pin.signal.ends_with('b') { |
| 1476 | // we number STM32L1 ADC bank 1 as 0..=31, bank 2 as 32..=63 | 1484 | // we number STM32L1 ADC bank 1 as 0..=31, bank 2 as 32..=63 |
| 1477 | let signal = pin.signal.strip_prefix("IN").unwrap().strip_suffix('b').unwrap(); | 1485 | let signal = pin.signal.strip_prefix("IN").unwrap().strip_suffix('b').unwrap(); |
| 1478 | Some(32u8 + signal.parse::<u8>().unwrap()) | 1486 | Some((32u8 + signal.parse::<u8>().unwrap(), false)) |
| 1479 | } else if pin.signal.starts_with("IN") { | 1487 | } else if pin.signal.starts_with("IN") { |
| 1480 | Some(pin.signal.strip_prefix("IN").unwrap().parse().unwrap()) | 1488 | Some((pin.signal.strip_prefix("IN").unwrap().parse().unwrap(), false)) |
| 1481 | } else { | 1489 | } else { |
| 1482 | None | 1490 | None |
| 1483 | }; | 1491 | }; |
| 1484 | if let Some(ch) = ch { | 1492 | if let Some((ch, false)) = ch { |
| 1493 | adc_pairs.entry(ch).or_insert((None, None)).0.replace(pin_name.clone()); | ||
| 1494 | |||
| 1485 | g.extend(quote! { | 1495 | g.extend(quote! { |
| 1486 | impl_adc_pin!( #peri, #pin_name, #ch); | 1496 | impl_adc_pin!( #peri, #pin_name, #ch); |
| 1487 | }) | 1497 | }) |
| 1488 | } | 1498 | } |
| 1499 | if let Some((ch, true)) = ch { | ||
| 1500 | adc_pairs.entry(ch).or_insert((None, None)).1.replace(pin_name.clone()); | ||
| 1501 | } | ||
| 1489 | } | 1502 | } |
| 1490 | 1503 | ||
| 1491 | if regs.kind == "opamp" { | 1504 | if regs.kind == "opamp" { |
| @@ -1524,6 +1537,23 @@ fn main() { | |||
| 1524 | }) | 1537 | }) |
| 1525 | } | 1538 | } |
| 1526 | } | 1539 | } |
| 1540 | |||
| 1541 | { | ||
| 1542 | let peri = format_ident!("{}", p.name); | ||
| 1543 | |||
| 1544 | for (ch, (pin, npin)) in adc_pairs { | ||
| 1545 | let (pin_name, npin_name) = match (pin, npin) { | ||
| 1546 | (Some(pin), Some(npin)) => (pin, npin), | ||
| 1547 | _ => { | ||
| 1548 | continue; | ||
| 1549 | } | ||
| 1550 | }; | ||
| 1551 | |||
| 1552 | g.extend(quote! { | ||
| 1553 | impl_adc_pair!( #peri, #pin_name, #npin_name, #ch); | ||
| 1554 | }) | ||
| 1555 | } | ||
| 1556 | } | ||
| 1527 | } | 1557 | } |
| 1528 | } | 1558 | } |
| 1529 | 1559 | ||
| @@ -1572,13 +1602,13 @@ fn main() { | |||
| 1572 | .into(); | 1602 | .into(); |
| 1573 | 1603 | ||
| 1574 | if chip_name.starts_with("stm32u5") { | 1604 | if chip_name.starts_with("stm32u5") { |
| 1575 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma4)); | 1605 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); |
| 1576 | } else { | 1606 | } else { |
| 1577 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); | 1607 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); |
| 1578 | } | 1608 | } |
| 1579 | 1609 | ||
| 1580 | if chip_name.starts_with("stm32wba") { | 1610 | if chip_name.starts_with("stm32wba") { |
| 1581 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma4)); | 1611 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); |
| 1582 | } | 1612 | } |
| 1583 | 1613 | ||
| 1584 | if chip_name.starts_with("stm32g4") { | 1614 | if chip_name.starts_with("stm32g4") { |
| @@ -1665,70 +1695,88 @@ fn main() { | |||
| 1665 | } | 1695 | } |
| 1666 | 1696 | ||
| 1667 | // ======== | 1697 | // ======== |
| 1668 | // Generate Div/Mul impls for RCC prescalers/dividers/multipliers. | 1698 | // Generate Div/Mul impls for RCC and ADC prescalers/dividers/multipliers. |
| 1669 | for e in rcc_registers.ir.enums { | 1699 | for (kind, psc_enums) in ["rcc", "adc", "adccommon"].iter().filter_map(|kind| { |
| 1670 | fn is_rcc_name(e: &str) -> bool { | 1700 | METADATA |
| 1671 | match e { | 1701 | .peripherals |
| 1672 | "Pllp" | "Pllq" | "Pllr" | "Plldivst" | "Pllm" | "Plln" | "Prediv1" | "Prediv2" | "Hpre5" => true, | 1702 | .iter() |
| 1673 | "Timpre" | "Pllrclkpre" => false, | 1703 | .filter_map(|p| p.registers.as_ref()) |
| 1674 | e if e.ends_with("pre") || e.ends_with("pres") || e.ends_with("div") || e.ends_with("mul") => true, | 1704 | .find(|r| r.kind == *kind) |
| 1675 | _ => false, | 1705 | .map(|r| (*kind, r.ir.enums)) |
| 1706 | }) { | ||
| 1707 | for e in psc_enums.iter() { | ||
| 1708 | fn is_adc_name(e: &str) -> bool { | ||
| 1709 | match e { | ||
| 1710 | "Presc" | "Adc4Presc" | "Adcpre" => true, | ||
| 1711 | _ => false, | ||
| 1712 | } | ||
| 1676 | } | 1713 | } |
| 1677 | } | ||
| 1678 | 1714 | ||
| 1679 | fn parse_num(n: &str) -> Result<Frac, ()> { | 1715 | fn is_rcc_name(e: &str) -> bool { |
| 1680 | for prefix in ["DIV", "MUL"] { | 1716 | match e { |
| 1681 | if let Some(n) = n.strip_prefix(prefix) { | 1717 | "Pllp" | "Pllq" | "Pllr" | "Plldivst" | "Pllm" | "Plln" | "Prediv1" | "Prediv2" | "Hpre5" => true, |
| 1682 | let exponent = n.find('_').map(|e| n.len() - 1 - e).unwrap_or(0) as u32; | 1718 | "Timpre" | "Pllrclkpre" => false, |
| 1683 | let mantissa = n.replace('_', "").parse().map_err(|_| ())?; | 1719 | e if e.ends_with("pre") || e.ends_with("pres") || e.ends_with("div") || e.ends_with("mul") => true, |
| 1684 | let f = Frac { | 1720 | _ => false, |
| 1685 | num: mantissa, | ||
| 1686 | denom: 10u32.pow(exponent), | ||
| 1687 | }; | ||
| 1688 | return Ok(f.simplify()); | ||
| 1689 | } | 1721 | } |
| 1690 | } | 1722 | } |
| 1691 | Err(()) | ||
| 1692 | } | ||
| 1693 | 1723 | ||
| 1694 | if is_rcc_name(e.name) { | 1724 | fn parse_num(n: &str) -> Result<Frac, ()> { |
| 1695 | let enum_name = format_ident!("{}", e.name); | 1725 | for prefix in ["DIV", "MUL"] { |
| 1696 | let mut muls = Vec::new(); | 1726 | if let Some(n) = n.strip_prefix(prefix) { |
| 1697 | let mut divs = Vec::new(); | 1727 | let exponent = n.find('_').map(|e| n.len() - 1 - e).unwrap_or(0) as u32; |
| 1698 | for v in e.variants { | 1728 | let mantissa = n.replace('_', "").parse().map_err(|_| ())?; |
| 1699 | let Ok(val) = parse_num(v.name) else { | 1729 | let f = Frac { |
| 1700 | panic!("could not parse mul/div. enum={} variant={}", e.name, v.name) | 1730 | num: mantissa, |
| 1701 | }; | 1731 | denom: 10u32.pow(exponent), |
| 1702 | let variant_name = format_ident!("{}", v.name); | 1732 | }; |
| 1703 | let variant = quote!(crate::pac::rcc::vals::#enum_name::#variant_name); | 1733 | return Ok(f.simplify()); |
| 1704 | let num = val.num; | 1734 | } |
| 1705 | let denom = val.denom; | 1735 | } |
| 1706 | muls.push(quote!(#variant => self * #num / #denom,)); | 1736 | Err(()) |
| 1707 | divs.push(quote!(#variant => self * #denom / #num,)); | ||
| 1708 | } | 1737 | } |
| 1709 | 1738 | ||
| 1710 | g.extend(quote! { | 1739 | if (kind == "rcc" && is_rcc_name(e.name)) || ((kind == "adccommon" || kind == "adc") && is_adc_name(e.name)) |
| 1711 | impl core::ops::Div<crate::pac::rcc::vals::#enum_name> for crate::time::Hertz { | 1740 | { |
| 1712 | type Output = crate::time::Hertz; | 1741 | let kind = format_ident!("{}", kind); |
| 1713 | fn div(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output { | 1742 | let enum_name = format_ident!("{}", e.name); |
| 1714 | match rhs { | 1743 | let mut muls = Vec::new(); |
| 1715 | #(#divs)* | 1744 | let mut divs = Vec::new(); |
| 1716 | #[allow(unreachable_patterns)] | 1745 | for v in e.variants { |
| 1717 | _ => unreachable!(), | 1746 | let Ok(val) = parse_num(v.name) else { |
| 1747 | panic!("could not parse mul/div. enum={} variant={}", e.name, v.name) | ||
| 1748 | }; | ||
| 1749 | let variant_name = format_ident!("{}", v.name); | ||
| 1750 | let variant = quote!(crate::pac::#kind::vals::#enum_name::#variant_name); | ||
| 1751 | let num = val.num; | ||
| 1752 | let denom = val.denom; | ||
| 1753 | muls.push(quote!(#variant => self * #num / #denom,)); | ||
| 1754 | divs.push(quote!(#variant => self * #denom / #num,)); | ||
| 1755 | } | ||
| 1756 | |||
| 1757 | g.extend(quote! { | ||
| 1758 | impl core::ops::Div<crate::pac::#kind::vals::#enum_name> for crate::time::Hertz { | ||
| 1759 | type Output = crate::time::Hertz; | ||
| 1760 | fn div(self, rhs: crate::pac::#kind::vals::#enum_name) -> Self::Output { | ||
| 1761 | match rhs { | ||
| 1762 | #(#divs)* | ||
| 1763 | #[allow(unreachable_patterns)] | ||
| 1764 | _ => unreachable!(), | ||
| 1765 | } | ||
| 1718 | } | 1766 | } |
| 1719 | } | 1767 | } |
| 1720 | } | 1768 | impl core::ops::Mul<crate::pac::#kind::vals::#enum_name> for crate::time::Hertz { |
| 1721 | impl core::ops::Mul<crate::pac::rcc::vals::#enum_name> for crate::time::Hertz { | 1769 | type Output = crate::time::Hertz; |
| 1722 | type Output = crate::time::Hertz; | 1770 | fn mul(self, rhs: crate::pac::#kind::vals::#enum_name) -> Self::Output { |
| 1723 | fn mul(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output { | 1771 | match rhs { |
| 1724 | match rhs { | 1772 | #(#muls)* |
| 1725 | #(#muls)* | 1773 | #[allow(unreachable_patterns)] |
| 1726 | #[allow(unreachable_patterns)] | 1774 | _ => unreachable!(), |
| 1727 | _ => unreachable!(), | 1775 | } |
| 1728 | } | 1776 | } |
| 1729 | } | 1777 | } |
| 1730 | } | 1778 | }); |
| 1731 | }); | 1779 | } |
| 1732 | } | 1780 | } |
| 1733 | } | 1781 | } |
| 1734 | 1782 | ||
| @@ -1855,7 +1903,12 @@ fn main() { | |||
| 1855 | if r.kind == "dma" || r.kind == "bdma" || r.kind == "gpdma" || r.kind == "lpdma" { | 1903 | if r.kind == "dma" || r.kind == "bdma" || r.kind == "gpdma" || r.kind == "lpdma" { |
| 1856 | for irq in p.interrupts { | 1904 | for irq in p.interrupts { |
| 1857 | let ch_name = format!("{}_{}", p.name, irq.signal); | 1905 | let ch_name = format!("{}_{}", p.name, irq.signal); |
| 1858 | let ch = METADATA.dma_channels.iter().find(|c| c.name == ch_name).unwrap(); | 1906 | let ch = METADATA.dma_channels.iter().find(|c| c.name == ch_name); |
| 1907 | |||
| 1908 | if ch.is_none() { | ||
| 1909 | continue; | ||
| 1910 | } | ||
| 1911 | let ch = ch.unwrap(); | ||
| 1859 | 1912 | ||
| 1860 | // Some H7 chips have BDMA1 hardcoded for DFSDM, ie no DMAMUX. It's unsupported, skip it. | 1913 | // Some H7 chips have BDMA1 hardcoded for DFSDM, ie no DMAMUX. It's unsupported, skip it. |
| 1861 | if has_dmamux && ch.dmamux.is_none() { | 1914 | if has_dmamux && ch.dmamux.is_none() { |
| @@ -2008,31 +2061,33 @@ fn main() { | |||
| 2008 | // ======== | 2061 | // ======== |
| 2009 | // Generate flash constants | 2062 | // Generate flash constants |
| 2010 | 2063 | ||
| 2011 | let flash_regions: Vec<&MemoryRegion> = memory | 2064 | if has_flash { |
| 2012 | .iter() | 2065 | let flash_regions: Vec<&MemoryRegion> = memory |
| 2013 | .filter(|x| x.kind == MemoryRegionKind::Flash && x.name.starts_with("BANK_")) | 2066 | .iter() |
| 2014 | .collect(); | 2067 | .filter(|x| x.kind == MemoryRegionKind::Flash && x.name.starts_with("BANK_")) |
| 2015 | let first_flash = flash_regions.first().unwrap(); | 2068 | .collect(); |
| 2016 | let total_flash_size = flash_regions | 2069 | let first_flash = flash_regions.first().unwrap(); |
| 2017 | .iter() | 2070 | let total_flash_size = flash_regions |
| 2018 | .map(|x| x.size) | 2071 | .iter() |
| 2019 | .reduce(|acc, item| acc + item) | 2072 | .map(|x| x.size) |
| 2020 | .unwrap(); | 2073 | .reduce(|acc, item| acc + item) |
| 2021 | let write_sizes: HashSet<_> = flash_regions | 2074 | .unwrap(); |
| 2022 | .iter() | 2075 | let write_sizes: HashSet<_> = flash_regions |
| 2023 | .map(|r| r.settings.as_ref().unwrap().write_size) | 2076 | .iter() |
| 2024 | .collect(); | 2077 | .map(|r| r.settings.as_ref().unwrap().write_size) |
| 2025 | assert_eq!(1, write_sizes.len()); | 2078 | .collect(); |
| 2079 | assert_eq!(1, write_sizes.len()); | ||
| 2026 | 2080 | ||
| 2027 | let flash_base = first_flash.address as usize; | 2081 | let flash_base = first_flash.address as usize; |
| 2028 | let total_flash_size = total_flash_size as usize; | 2082 | let total_flash_size = total_flash_size as usize; |
| 2029 | let write_size = (*write_sizes.iter().next().unwrap()) as usize; | 2083 | let write_size = (*write_sizes.iter().next().unwrap()) as usize; |
| 2030 | 2084 | ||
| 2031 | g.extend(quote!( | 2085 | g.extend(quote!( |
| 2032 | pub const FLASH_BASE: usize = #flash_base; | 2086 | pub const FLASH_BASE: usize = #flash_base; |
| 2033 | pub const FLASH_SIZE: usize = #total_flash_size; | 2087 | pub const FLASH_SIZE: usize = #total_flash_size; |
| 2034 | pub const WRITE_SIZE: usize = #write_size; | 2088 | pub const WRITE_SIZE: usize = #write_size; |
| 2035 | )); | 2089 | )); |
| 2090 | } | ||
| 2036 | 2091 | ||
| 2037 | // ======== | 2092 | // ======== |
| 2038 | // Generate EEPROM constants | 2093 | // Generate EEPROM constants |
diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index 2608160a3..babdebfdb 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::{AdcChannel, AnyAdcChannel, RxDma4, SealedAdcChannel, blocking_delay_us}; | 7 | use super::blocking_delay_us; |
| 8 | use crate::dma::Transfer; | 8 | use crate::adc::ConversionMode; |
| 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)] |
| @@ -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<'d, T: Instance> super::SealedSpecialConverter<super::VrefInt> for Adc4<'d, T> { |
| 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<'d, T: Instance> super::SealedSpecialConverter<super::Temperature> for Adc4<'d, T> { |
| 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<'d, T: Instance> super::SealedSpecialConverter<super::Vcore> for Adc4<'d, T> { |
| 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<'d, T: Instance> super::SealedSpecialConverter<super::Vbat> for Adc4<'d, T> { |
| 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<'d, T: Instance> super::SealedSpecialConverter<super::Dac> for Adc4<'d, T> { |
| 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,71 +76,17 @@ 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 = 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, | ||
| 123 | DividedBy32, | ||
| 124 | DividedBy64, | ||
| 125 | DividedBy128, | ||
| 126 | DividedBy256, | ||
| 127 | } | ||
| 128 | |||
| 129 | impl Prescaler { | ||
| 130 | fn from_ker_ck(frequency: Hertz) -> Self { | ||
| 131 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | ||
| 132 | match raw_prescaler { | ||
| 133 | 0 => Self::NotDivided, | ||
| 134 | 1 => Self::DividedBy2, | ||
| 135 | 2..=3 => Self::DividedBy4, | ||
| 136 | 4..=5 => Self::DividedBy6, | ||
| 137 | 6..=7 => Self::DividedBy8, | ||
| 138 | 8..=9 => Self::DividedBy10, | ||
| 139 | 10..=11 => Self::DividedBy12, | ||
| 140 | _ => unimplemented!(), | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | fn divisor(&self) -> u32 { | ||
| 145 | match self { | ||
| 146 | Prescaler::NotDivided => 1, | ||
| 147 | Prescaler::DividedBy2 => 2, | ||
| 148 | Prescaler::DividedBy4 => 4, | ||
| 149 | Prescaler::DividedBy6 => 6, | ||
| 150 | Prescaler::DividedBy8 => 8, | ||
| 151 | Prescaler::DividedBy10 => 10, | ||
| 152 | Prescaler::DividedBy12 => 12, | ||
| 153 | Prescaler::DividedBy16 => 16, | ||
| 154 | Prescaler::DividedBy32 => 32, | ||
| 155 | Prescaler::DividedBy64 => 64, | ||
| 156 | Prescaler::DividedBy128 => 128, | ||
| 157 | Prescaler::DividedBy256 => 256, | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | fn presc(&self) -> Presc { | ||
| 162 | match self { | ||
| 163 | Prescaler::NotDivided => Presc::DIV1, | ||
| 164 | Prescaler::DividedBy2 => Presc::DIV2, | ||
| 165 | Prescaler::DividedBy4 => Presc::DIV4, | ||
| 166 | Prescaler::DividedBy6 => Presc::DIV6, | ||
| 167 | Prescaler::DividedBy8 => Presc::DIV8, | ||
| 168 | Prescaler::DividedBy10 => Presc::DIV10, | ||
| 169 | Prescaler::DividedBy12 => Presc::DIV12, | ||
| 170 | Prescaler::DividedBy16 => Presc::DIV16, | ||
| 171 | Prescaler::DividedBy32 => Presc::DIV32, | ||
| 172 | Prescaler::DividedBy64 => Presc::DIV64, | ||
| 173 | Prescaler::DividedBy128 => Presc::DIV128, | ||
| 174 | Prescaler::DividedBy256 => Presc::DIV256, | ||
| 175 | } | ||
| 176 | } | 90 | } |
| 177 | } | 91 | } |
| 178 | 92 | ||
| @@ -185,6 +99,122 @@ pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeri | |||
| 185 | type Interrupt: crate::interrupt::typelevel::Interrupt; | 99 | type Interrupt: crate::interrupt::typelevel::Interrupt; |
| 186 | } | 100 | } |
| 187 | 101 | ||
| 102 | foreach_adc!( | ||
| 103 | (ADC4, $common_inst:ident, $clock:ident) => { | ||
| 104 | use crate::peripherals::ADC4; | ||
| 105 | |||
| 106 | impl super::BasicAnyInstance for ADC4 { | ||
| 107 | type SampleTime = SampleTime; | ||
| 108 | } | ||
| 109 | |||
| 110 | impl super::SealedAnyInstance for ADC4 { | ||
| 111 | fn dr() -> *mut u16 { | ||
| 112 | ADC4::regs().dr().as_ptr() as *mut u16 | ||
| 113 | } | ||
| 114 | |||
| 115 | fn enable() { | ||
| 116 | ADC4::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 117 | ADC4::regs().cr().modify(|w| w.set_aden(true)); | ||
| 118 | while !ADC4::regs().isr().read().adrdy() {} | ||
| 119 | ADC4::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 120 | } | ||
| 121 | |||
| 122 | fn start() { | ||
| 123 | // Start conversion | ||
| 124 | ADC4::regs().cr().modify(|reg| { | ||
| 125 | reg.set_adstart(true); | ||
| 126 | }); | ||
| 127 | } | ||
| 128 | |||
| 129 | fn stop() { | ||
| 130 | if ADC4::regs().cr().read().adstart() && !ADC4::regs().cr().read().addis() { | ||
| 131 | ADC4::regs().cr().modify(|reg| { | ||
| 132 | reg.set_adstp(true); | ||
| 133 | }); | ||
| 134 | while ADC4::regs().cr().read().adstart() {} | ||
| 135 | } | ||
| 136 | |||
| 137 | // Reset configuration. | ||
| 138 | ADC4::regs().cfgr1().modify(|reg| { | ||
| 139 | reg.set_dmaen(false); | ||
| 140 | }); | ||
| 141 | } | ||
| 142 | |||
| 143 | fn configure_dma(conversion_mode: ConversionMode) { | ||
| 144 | match conversion_mode { | ||
| 145 | ConversionMode::Singular => { | ||
| 146 | ADC4::regs().isr().modify(|reg| { | ||
| 147 | reg.set_ovr(true); | ||
| 148 | reg.set_eos(true); | ||
| 149 | reg.set_eoc(true); | ||
| 150 | }); | ||
| 151 | |||
| 152 | ADC4::regs().cfgr1().modify(|reg| { | ||
| 153 | reg.set_dmaen(true); | ||
| 154 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 155 | #[cfg(stm32u5)] | ||
| 156 | reg.set_chselrmod(false); | ||
| 157 | #[cfg(stm32wba)] | ||
| 158 | reg.set_chselrmod(Chselrmod::ENABLE_INPUT) | ||
| 159 | }); | ||
| 160 | } | ||
| 161 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 162 | _ => unreachable!(), | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { | ||
| 167 | let mut prev_channel: i16 = -1; | ||
| 168 | #[cfg(stm32wba)] | ||
| 169 | ADC4::regs().chselr().write_value(Chselr(0_u32)); | ||
| 170 | #[cfg(stm32u5)] | ||
| 171 | ADC4::regs().chselrmod0().write_value(Chselr(0_u32)); | ||
| 172 | for (_i, ((channel, _), sample_time)) in sequence.enumerate() { | ||
| 173 | ADC4::regs().smpr().modify(|w| { | ||
| 174 | w.set_smp(_i, sample_time); | ||
| 175 | }); | ||
| 176 | |||
| 177 | let channel_num = channel; | ||
| 178 | if channel_num as i16 <= prev_channel { | ||
| 179 | return; | ||
| 180 | }; | ||
| 181 | prev_channel = channel_num as i16; | ||
| 182 | |||
| 183 | #[cfg(stm32wba)] | ||
| 184 | ADC4::regs().chselr().modify(|w| { | ||
| 185 | w.set_chsel0(channel as usize, true); | ||
| 186 | }); | ||
| 187 | #[cfg(stm32u5)] | ||
| 188 | ADC4::regs().chselrmod0().modify(|w| { | ||
| 189 | w.set_chsel(channel as usize, true); | ||
| 190 | }); | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | fn convert() -> u16 { | ||
| 195 | // Reset interrupts | ||
| 196 | ADC4::regs().isr().modify(|reg| { | ||
| 197 | reg.set_eos(true); | ||
| 198 | reg.set_eoc(true); | ||
| 199 | }); | ||
| 200 | |||
| 201 | // Start conversion | ||
| 202 | ADC4::regs().cr().modify(|reg| { | ||
| 203 | reg.set_adstart(true); | ||
| 204 | }); | ||
| 205 | |||
| 206 | while !ADC4::regs().isr().read().eos() { | ||
| 207 | // spin | ||
| 208 | } | ||
| 209 | |||
| 210 | ADC4::regs().dr().read().0 as u16 | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | impl super::AnyInstance for ADC4 {} | ||
| 215 | }; | ||
| 216 | ); | ||
| 217 | |||
| 188 | pub struct Adc4<'d, T: Instance> { | 218 | pub struct Adc4<'d, T: Instance> { |
| 189 | #[allow(unused)] | 219 | #[allow(unused)] |
| 190 | adc: crate::Peri<'d, T>, | 220 | adc: crate::Peri<'d, T>, |
| @@ -196,15 +226,15 @@ pub enum Adc4Error { | |||
| 196 | DMAError, | 226 | DMAError, |
| 197 | } | 227 | } |
| 198 | 228 | ||
| 199 | impl<'d, T: Instance> Adc4<'d, T> { | 229 | impl<'d, T: Instance + super::AnyInstance> super::Adc<'d, T> { |
| 200 | /// Create a new ADC driver. | 230 | /// Create a new ADC driver. |
| 201 | pub fn new(adc: Peri<'d, T>) -> Self { | 231 | pub fn new_adc4(adc: Peri<'d, T>) -> Self { |
| 202 | rcc::enable_and_reset::<T>(); | 232 | rcc::enable_and_reset::<T>(); |
| 203 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 233 | let prescaler = from_ker_ck(T::frequency()); |
| 204 | 234 | ||
| 205 | T::regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 235 | T::regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 206 | 236 | ||
| 207 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 237 | let frequency = T::frequency() / prescaler; |
| 208 | info!("ADC4 frequency set to {}", frequency); | 238 | info!("ADC4 frequency set to {}", frequency); |
| 209 | 239 | ||
| 210 | if frequency > MAX_ADC_CLK_FREQ { | 240 | if frequency > MAX_ADC_CLK_FREQ { |
| @@ -214,20 +244,6 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 214 | ); | 244 | ); |
| 215 | } | 245 | } |
| 216 | 246 | ||
| 217 | let mut s = Self { adc }; | ||
| 218 | |||
| 219 | s.power_up(); | ||
| 220 | |||
| 221 | s.calibrate(); | ||
| 222 | blocking_delay_us(1); | ||
| 223 | |||
| 224 | s.enable(); | ||
| 225 | s.configure(); | ||
| 226 | |||
| 227 | s | ||
| 228 | } | ||
| 229 | |||
| 230 | fn power_up(&mut self) { | ||
| 231 | T::regs().isr().modify(|w| { | 247 | T::regs().isr().modify(|w| { |
| 232 | w.set_ldordy(true); | 248 | w.set_ldordy(true); |
| 233 | }); | 249 | }); |
| @@ -239,22 +255,15 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 239 | T::regs().isr().modify(|w| { | 255 | T::regs().isr().modify(|w| { |
| 240 | w.set_ldordy(true); | 256 | w.set_ldordy(true); |
| 241 | }); | 257 | }); |
| 242 | } | ||
| 243 | 258 | ||
| 244 | fn calibrate(&mut self) { | ||
| 245 | T::regs().cr().modify(|w| w.set_adcal(true)); | 259 | T::regs().cr().modify(|w| w.set_adcal(true)); |
| 246 | while T::regs().cr().read().adcal() {} | 260 | while T::regs().cr().read().adcal() {} |
| 247 | T::regs().isr().modify(|w| w.set_eocal(true)); | 261 | T::regs().isr().modify(|w| w.set_eocal(true)); |
| 248 | } | ||
| 249 | 262 | ||
| 250 | fn enable(&mut self) { | 263 | blocking_delay_us(1); |
| 251 | T::regs().isr().write(|w| w.set_adrdy(true)); | 264 | |
| 252 | T::regs().cr().modify(|w| w.set_aden(true)); | 265 | T::enable(); |
| 253 | while !T::regs().isr().read().adrdy() {} | ||
| 254 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 255 | } | ||
| 256 | 266 | ||
| 257 | fn configure(&mut self) { | ||
| 258 | // single conversion mode, software trigger | 267 | // single conversion mode, software trigger |
| 259 | T::regs().cfgr1().modify(|w| { | 268 | T::regs().cfgr1().modify(|w| { |
| 260 | #[cfg(stm32u5)] | 269 | #[cfg(stm32u5)] |
| @@ -280,73 +289,63 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 280 | w.set_smpsel(i, Smpsel::SMP1); | 289 | w.set_smpsel(i, Smpsel::SMP1); |
| 281 | } | 290 | } |
| 282 | }); | 291 | }); |
| 292 | |||
| 293 | Self { adc } | ||
| 283 | } | 294 | } |
| 284 | 295 | ||
| 285 | /// Enable reading the voltage reference internal channel. | 296 | /// Enable reading the voltage reference internal channel. |
| 286 | pub fn enable_vrefint(&self) -> VrefInt { | 297 | pub fn enable_vrefint_adc4(&self) -> super::VrefInt { |
| 287 | T::regs().ccr().modify(|w| { | 298 | T::regs().ccr().modify(|w| { |
| 288 | w.set_vrefen(true); | 299 | w.set_vrefen(true); |
| 289 | }); | 300 | }); |
| 290 | 301 | ||
| 291 | VrefInt {} | 302 | super::VrefInt {} |
| 292 | } | 303 | } |
| 293 | 304 | ||
| 294 | /// Enable reading the temperature internal channel. | 305 | /// Enable reading the temperature internal channel. |
| 295 | pub fn enable_temperature(&self) -> Temperature { | 306 | pub fn enable_temperature_adc4(&self) -> super::Temperature { |
| 296 | T::regs().ccr().modify(|w| { | 307 | T::regs().ccr().modify(|w| { |
| 297 | w.set_vsensesel(true); | 308 | w.set_vsensesel(true); |
| 298 | }); | 309 | }); |
| 299 | 310 | ||
| 300 | Temperature {} | 311 | super::Temperature {} |
| 301 | } | 312 | } |
| 302 | 313 | ||
| 303 | /// Enable reading the vbat internal channel. | 314 | /// Enable reading the vbat internal channel. |
| 304 | #[cfg(stm32u5)] | 315 | #[cfg(stm32u5)] |
| 305 | pub fn enable_vbat(&self) -> Vbat { | 316 | pub fn enable_vbat_adc4(&self) -> super::Vbat { |
| 306 | T::regs().ccr().modify(|w| { | 317 | T::regs().ccr().modify(|w| { |
| 307 | w.set_vbaten(true); | 318 | w.set_vbaten(true); |
| 308 | }); | 319 | }); |
| 309 | 320 | ||
| 310 | Vbat {} | 321 | super::Vbat {} |
| 311 | } | 322 | } |
| 312 | 323 | ||
| 313 | /// Enable reading the vbat internal channel. | 324 | /// Enable reading the vbat internal channel. |
| 314 | pub fn enable_vcore(&self) -> Vcore { | 325 | pub fn enable_vcore_adc4(&self) -> super::Vcore { |
| 315 | Vcore {} | 326 | super::Vcore {} |
| 316 | } | 327 | } |
| 317 | 328 | ||
| 318 | /// Enable reading the vbat internal channel. | 329 | /// Enable reading the vbat internal channel. |
| 319 | #[cfg(stm32u5)] | 330 | #[cfg(stm32u5)] |
| 320 | pub fn enable_dac_channel(&self, dac: DacChannel) -> Dac { | 331 | pub fn enable_dac_channel_adc4(&self, dac: DacChannel) -> super::Dac { |
| 321 | let mux; | 332 | let mux; |
| 322 | match dac { | 333 | match dac { |
| 323 | DacChannel::OUT1 => mux = false, | 334 | DacChannel::OUT1 => mux = false, |
| 324 | DacChannel::OUT2 => mux = true, | 335 | DacChannel::OUT2 => mux = true, |
| 325 | } | 336 | } |
| 326 | T::regs().or().modify(|w| w.set_chn21sel(mux)); | 337 | T::regs().or().modify(|w| w.set_chn21sel(mux)); |
| 327 | Dac {} | 338 | super::Dac {} |
| 328 | } | ||
| 329 | |||
| 330 | /// Set the ADC sample time. | ||
| 331 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 332 | T::regs().smpr().modify(|w| { | ||
| 333 | w.set_smp(0, sample_time); | ||
| 334 | }); | ||
| 335 | } | ||
| 336 | |||
| 337 | /// Get the ADC sample time. | ||
| 338 | pub fn sample_time(&self) -> SampleTime { | ||
| 339 | T::regs().smpr().read().smp(0) | ||
| 340 | } | 339 | } |
| 341 | 340 | ||
| 342 | /// Set the ADC resolution. | 341 | /// Set the ADC resolution. |
| 343 | pub fn set_resolution(&mut self, resolution: Resolution) { | 342 | pub fn set_resolution_adc4(&mut self, resolution: Resolution) { |
| 344 | T::regs().cfgr1().modify(|w| w.set_res(resolution.into())); | 343 | T::regs().cfgr1().modify(|w| w.set_res(resolution.into())); |
| 345 | } | 344 | } |
| 346 | 345 | ||
| 347 | /// Set hardware averaging. | 346 | /// Set hardware averaging. |
| 348 | #[cfg(stm32u5)] | 347 | #[cfg(stm32u5)] |
| 349 | pub fn set_averaging(&mut self, averaging: Averaging) { | 348 | pub fn set_averaging_adc4(&mut self, averaging: Averaging) { |
| 350 | let (enable, samples, right_shift) = match averaging { | 349 | let (enable, samples, right_shift) = match averaging { |
| 351 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, 0), | 350 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, 0), |
| 352 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, 1), | 351 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, 1), |
| @@ -366,7 +365,7 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 366 | }) | 365 | }) |
| 367 | } | 366 | } |
| 368 | #[cfg(stm32wba)] | 367 | #[cfg(stm32wba)] |
| 369 | pub fn set_averaging(&mut self, averaging: Averaging) { | 368 | pub fn set_averaging_adc4(&mut self, averaging: Averaging) { |
| 370 | let (enable, samples, right_shift) = match averaging { | 369 | let (enable, samples, right_shift) = match averaging { |
| 371 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT0), | 370 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT0), |
| 372 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT1), | 371 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT1), |
| @@ -385,164 +384,4 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 385 | w.set_ovse(enable) | 384 | w.set_ovse(enable) |
| 386 | }) | 385 | }) |
| 387 | } | 386 | } |
| 388 | |||
| 389 | /// Read an ADC channel. | ||
| 390 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 391 | channel.setup(); | ||
| 392 | |||
| 393 | // Select channel | ||
| 394 | #[cfg(stm32wba)] | ||
| 395 | { | ||
| 396 | T::regs().chselr().write_value(Chselr(0_u32)); | ||
| 397 | T::regs().chselr().modify(|w| { | ||
| 398 | w.set_chsel0(channel.channel() as usize, true); | ||
| 399 | }); | ||
| 400 | } | ||
| 401 | #[cfg(stm32u5)] | ||
| 402 | { | ||
| 403 | T::regs().chselrmod0().write_value(Chselr(0_u32)); | ||
| 404 | T::regs().chselrmod0().modify(|w| { | ||
| 405 | w.set_chsel(channel.channel() as usize, true); | ||
| 406 | }); | ||
| 407 | } | ||
| 408 | |||
| 409 | // Reset interrupts | ||
| 410 | T::regs().isr().modify(|reg| { | ||
| 411 | reg.set_eos(true); | ||
| 412 | reg.set_eoc(true); | ||
| 413 | }); | ||
| 414 | |||
| 415 | // Start conversion | ||
| 416 | T::regs().cr().modify(|reg| { | ||
| 417 | reg.set_adstart(true); | ||
| 418 | }); | ||
| 419 | |||
| 420 | while !T::regs().isr().read().eos() { | ||
| 421 | // spin | ||
| 422 | } | ||
| 423 | |||
| 424 | T::regs().dr().read().0 as u16 | ||
| 425 | } | ||
| 426 | |||
| 427 | /// Read one or multiple ADC channels using DMA. | ||
| 428 | /// | ||
| 429 | /// `sequence` iterator and `readings` must have the same length. | ||
| 430 | /// The channels in `sequence` must be in ascending order. | ||
| 431 | /// | ||
| 432 | /// Example | ||
| 433 | /// ```rust,ignore | ||
| 434 | /// use embassy_stm32::adc::adc4; | ||
| 435 | /// use embassy_stm32::adc::AdcChannel; | ||
| 436 | /// | ||
| 437 | /// let mut adc4 = adc4::Adc4::new(p.ADC4); | ||
| 438 | /// let mut adc4_pin1 = p.PC1; | ||
| 439 | /// let mut adc4_pin2 = p.PC0; | ||
| 440 | /// let mut.into()d41 = adc4_pin1.into(); | ||
| 441 | /// let mut.into()d42 = adc4_pin2.into(); | ||
| 442 | /// let mut measurements = [0u16; 2]; | ||
| 443 | /// // not that the channels must be in ascending order | ||
| 444 | /// adc4.read( | ||
| 445 | /// &mut p.GPDMA1_CH1, | ||
| 446 | /// [ | ||
| 447 | /// &mut.into()d42, | ||
| 448 | /// &mut.into()d41, | ||
| 449 | /// ] | ||
| 450 | /// .into_iter(), | ||
| 451 | /// &mut measurements, | ||
| 452 | /// ).await.unwrap(); | ||
| 453 | /// ``` | ||
| 454 | pub async fn read( | ||
| 455 | &mut self, | ||
| 456 | rx_dma: Peri<'_, impl RxDma4<T>>, | ||
| 457 | sequence: impl ExactSizeIterator<Item = &mut AnyAdcChannel<T>>, | ||
| 458 | readings: &mut [u16], | ||
| 459 | ) -> Result<(), Adc4Error> { | ||
| 460 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 461 | assert!( | ||
| 462 | sequence.len() == readings.len(), | ||
| 463 | "Sequence length must be equal to readings length" | ||
| 464 | ); | ||
| 465 | |||
| 466 | // Ensure no conversions are ongoing | ||
| 467 | Self::cancel_conversions(); | ||
| 468 | |||
| 469 | T::regs().isr().modify(|reg| { | ||
| 470 | reg.set_ovr(true); | ||
| 471 | reg.set_eos(true); | ||
| 472 | reg.set_eoc(true); | ||
| 473 | }); | ||
| 474 | |||
| 475 | T::regs().cfgr1().modify(|reg| { | ||
| 476 | reg.set_dmaen(true); | ||
| 477 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 478 | #[cfg(stm32u5)] | ||
| 479 | reg.set_chselrmod(false); | ||
| 480 | #[cfg(stm32wba)] | ||
| 481 | reg.set_chselrmod(Chselrmod::ENABLE_INPUT) | ||
| 482 | }); | ||
| 483 | |||
| 484 | // Verify and activate sequence | ||
| 485 | let mut prev_channel: i16 = -1; | ||
| 486 | #[cfg(stm32wba)] | ||
| 487 | T::regs().chselr().write_value(Chselr(0_u32)); | ||
| 488 | #[cfg(stm32u5)] | ||
| 489 | T::regs().chselrmod0().write_value(Chselr(0_u32)); | ||
| 490 | for channel in sequence { | ||
| 491 | let channel_num = channel.channel; | ||
| 492 | if channel_num as i16 <= prev_channel { | ||
| 493 | return Err(Adc4Error::InvalidSequence); | ||
| 494 | }; | ||
| 495 | prev_channel = channel_num as i16; | ||
| 496 | |||
| 497 | #[cfg(stm32wba)] | ||
| 498 | T::regs().chselr().modify(|w| { | ||
| 499 | w.set_chsel0(channel.channel as usize, true); | ||
| 500 | }); | ||
| 501 | #[cfg(stm32u5)] | ||
| 502 | T::regs().chselrmod0().modify(|w| { | ||
| 503 | w.set_chsel(channel.channel as usize, true); | ||
| 504 | }); | ||
| 505 | } | ||
| 506 | |||
| 507 | let request = rx_dma.request(); | ||
| 508 | let transfer = unsafe { | ||
| 509 | Transfer::new_read( | ||
| 510 | rx_dma, | ||
| 511 | request, | ||
| 512 | T::regs().dr().as_ptr() as *mut u16, | ||
| 513 | readings, | ||
| 514 | Default::default(), | ||
| 515 | ) | ||
| 516 | }; | ||
| 517 | |||
| 518 | // Start conversion | ||
| 519 | T::regs().cr().modify(|reg| { | ||
| 520 | reg.set_adstart(true); | ||
| 521 | }); | ||
| 522 | |||
| 523 | transfer.await; | ||
| 524 | |||
| 525 | // Ensure conversions are finished. | ||
| 526 | Self::cancel_conversions(); | ||
| 527 | |||
| 528 | // Reset configuration. | ||
| 529 | T::regs().cfgr1().modify(|reg| { | ||
| 530 | reg.set_dmaen(false); | ||
| 531 | }); | ||
| 532 | |||
| 533 | if T::regs().isr().read().ovr() { | ||
| 534 | Err(Adc4Error::DMAError) | ||
| 535 | } else { | ||
| 536 | Ok(()) | ||
| 537 | } | ||
| 538 | } | ||
| 539 | |||
| 540 | fn cancel_conversions() { | ||
| 541 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 542 | T::regs().cr().modify(|reg| { | ||
| 543 | reg.set_adstp(true); | ||
| 544 | }); | ||
| 545 | while T::regs().cr().read().adstart() {} | ||
| 546 | } | ||
| 547 | } | ||
| 548 | } | 387 | } |
diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index fc28df346..d87bd1ed4 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs | |||
| @@ -1,12 +1,10 @@ | |||
| 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 | Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, blocking_delay_us, | 7 | use crate::adc::{AnyInstance, ConversionMode}; |
| 8 | }; | ||
| 9 | use crate::dma::Transfer; | ||
| 10 | use crate::time::Hertz; | 8 | use crate::time::Hertz; |
| 11 | use crate::{Peri, pac, rcc}; | 9 | use crate::{Peri, pac, rcc}; |
| 12 | 10 | ||
| @@ -19,152 +17,181 @@ 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 = 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<T: Instance> super::SealedAnyInstance for T { |
| 68 | fn from_ker_ck(frequency: Hertz) -> Self { | 47 | fn dr() -> *mut u16 { |
| 69 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | 48 | T::regs().dr().as_ptr() as *mut u16 |
| 70 | match raw_prescaler { | 49 | } |
| 71 | 0 => Self::NotDivided, | 50 | |
| 72 | 1 => Self::DividedBy2, | 51 | fn enable() { |
| 73 | 2..=3 => Self::DividedBy4, | 52 | T::regs().isr().modify(|w| w.set_adrdy(true)); |
| 74 | 4..=5 => Self::DividedBy6, | 53 | T::regs().cr().modify(|w| w.set_aden(true)); |
| 75 | 6..=7 => Self::DividedBy8, | 54 | // ADRDY is "ADC ready". Wait until it will be True. |
| 76 | 8..=9 => Self::DividedBy10, | 55 | while !T::regs().isr().read().adrdy() {} |
| 77 | 10..=11 => Self::DividedBy12, | 56 | } |
| 78 | _ => unimplemented!(), | 57 | |
| 79 | } | 58 | fn start() { |
| 59 | // Start conversion | ||
| 60 | T::regs().cr().modify(|reg| { | ||
| 61 | reg.set_adstart(true); | ||
| 62 | }); | ||
| 80 | } | 63 | } |
| 81 | 64 | ||
| 82 | #[allow(unused)] | 65 | fn stop() { |
| 83 | fn divisor(&self) -> u32 { | 66 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 84 | match self { | 67 | T::regs().cr().modify(|reg| { |
| 85 | Prescaler::NotDivided => 1, | 68 | reg.set_adstp(Adstp::STOP); |
| 86 | Prescaler::DividedBy2 => 2, | 69 | }); |
| 87 | Prescaler::DividedBy4 => 4, | 70 | while T::regs().cr().read().adstart() {} |
| 88 | Prescaler::DividedBy6 => 6, | ||
| 89 | Prescaler::DividedBy8 => 8, | ||
| 90 | Prescaler::DividedBy10 => 10, | ||
| 91 | Prescaler::DividedBy12 => 12, | ||
| 92 | Prescaler::DividedBy16 => 16, | ||
| 93 | Prescaler::DividedBy32 => 32, | ||
| 94 | Prescaler::DividedBy64 => 64, | ||
| 95 | Prescaler::DividedBy128 => 128, | ||
| 96 | Prescaler::DividedBy256 => 256, | ||
| 97 | } | 71 | } |
| 72 | |||
| 73 | // Reset configuration. | ||
| 74 | T::regs().cfgr1().modify(|reg| { | ||
| 75 | reg.set_cont(false); | ||
| 76 | reg.set_dmacfg(Dmacfg::from_bits(0)); | ||
| 77 | reg.set_dmaen(false); | ||
| 78 | }); | ||
| 98 | } | 79 | } |
| 99 | 80 | ||
| 100 | fn presc(&self) -> Presc { | 81 | fn configure_dma(conversion_mode: super::ConversionMode) { |
| 101 | match self { | 82 | match conversion_mode { |
| 102 | Prescaler::NotDivided => Presc::DIV1, | 83 | ConversionMode::Singular => { |
| 103 | Prescaler::DividedBy2 => Presc::DIV2, | 84 | // Enable overrun control, so no new DMA requests will be generated until |
| 104 | Prescaler::DividedBy4 => Presc::DIV4, | 85 | // previous DR values is read. |
| 105 | Prescaler::DividedBy6 => Presc::DIV6, | 86 | T::regs().isr().modify(|reg| { |
| 106 | Prescaler::DividedBy8 => Presc::DIV8, | 87 | reg.set_ovr(true); |
| 107 | Prescaler::DividedBy10 => Presc::DIV10, | 88 | }); |
| 108 | Prescaler::DividedBy12 => Presc::DIV12, | 89 | |
| 109 | Prescaler::DividedBy16 => Presc::DIV16, | 90 | // Set continuous mode with oneshot dma. |
| 110 | Prescaler::DividedBy32 => Presc::DIV32, | 91 | T::regs().cfgr1().modify(|reg| { |
| 111 | Prescaler::DividedBy64 => Presc::DIV64, | 92 | reg.set_discen(false); |
| 112 | Prescaler::DividedBy128 => Presc::DIV128, | 93 | reg.set_cont(true); |
| 113 | Prescaler::DividedBy256 => Presc::DIV256, | 94 | reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT); |
| 95 | reg.set_dmaen(true); | ||
| 96 | reg.set_ovrmod(Ovrmod::PRESERVE); | ||
| 97 | }); | ||
| 98 | } | ||
| 114 | } | 99 | } |
| 115 | } | 100 | } |
| 116 | } | ||
| 117 | 101 | ||
| 118 | #[cfg(feature = "defmt")] | 102 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>) { |
| 119 | impl<'a> defmt::Format for Prescaler { | 103 | let mut needs_hw = sequence.len() == 1 || sequence.len() > CHSELR_SQ_SIZE; |
| 120 | fn format(&self, fmt: defmt::Formatter) { | 104 | let mut is_ordered_up = true; |
| 121 | match self { | 105 | let mut is_ordered_down = true; |
| 122 | Prescaler::NotDivided => defmt::write!(fmt, "Prescaler::NotDivided"), | 106 | |
| 123 | Prescaler::DividedBy2 => defmt::write!(fmt, "Prescaler::DividedBy2"), | 107 | let sequence_len = sequence.len(); |
| 124 | Prescaler::DividedBy4 => defmt::write!(fmt, "Prescaler::DividedBy4"), | 108 | let mut hw_channel_selection: u32 = 0; |
| 125 | Prescaler::DividedBy6 => defmt::write!(fmt, "Prescaler::DividedBy6"), | 109 | let mut last_channel: u8 = 0; |
| 126 | Prescaler::DividedBy8 => defmt::write!(fmt, "Prescaler::DividedBy8"), | 110 | let mut sample_time: Self::SampleTime = SampleTime::CYCLES2_5; |
| 127 | Prescaler::DividedBy10 => defmt::write!(fmt, "Prescaler::DividedBy10"), | 111 | |
| 128 | Prescaler::DividedBy12 => defmt::write!(fmt, "Prescaler::DividedBy12"), | 112 | T::regs().chselr_sq().write(|w| { |
| 129 | Prescaler::DividedBy16 => defmt::write!(fmt, "Prescaler::DividedBy16"), | 113 | for (i, ((channel, _), _sample_time)) in sequence.enumerate() { |
| 130 | Prescaler::DividedBy32 => defmt::write!(fmt, "Prescaler::DividedBy32"), | 114 | assert!( |
| 131 | Prescaler::DividedBy64 => defmt::write!(fmt, "Prescaler::DividedBy64"), | 115 | sample_time == _sample_time || i == 0, |
| 132 | Prescaler::DividedBy128 => defmt::write!(fmt, "Prescaler::DividedBy128"), | 116 | "C0 only supports one sample time for the sequence." |
| 133 | Prescaler::DividedBy256 => defmt::write!(fmt, "Prescaler::DividedBy256"), | 117 | ); |
| 118 | |||
| 119 | sample_time = _sample_time; | ||
| 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; | ||
| 125 | |||
| 126 | if !needs_hw { | ||
| 127 | w.set_sq(i, channel); | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | for i in sequence_len..CHSELR_SQ_SIZE { | ||
| 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 { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } | ||
| 134 | } | 150 | } |
| 151 | |||
| 152 | T::regs().smpr().modify(|w| { | ||
| 153 | w.smpsel(0); | ||
| 154 | w.set_smp1(sample_time); | ||
| 155 | }); | ||
| 156 | |||
| 157 | T::regs().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 | }); | ||
| 162 | |||
| 163 | // Trigger and wait for the channel selection procedure to complete. | ||
| 164 | T::regs().isr().modify(|w| w.set_ccrdy(false)); | ||
| 165 | while !T::regs().isr().read().ccrdy() {} | ||
| 135 | } | 166 | } |
| 136 | } | ||
| 137 | 167 | ||
| 138 | /// Number of samples used for averaging. | 168 | fn convert() -> u16 { |
| 139 | /// TODO: Implement hardware averaging setting. | 169 | // Set single conversion mode. |
| 140 | #[allow(unused)] | 170 | T::regs().cfgr1().modify(|w| w.set_cont(false)); |
| 141 | #[derive(Copy, Clone, Debug)] | 171 | |
| 142 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 172 | // Start conversion |
| 143 | pub enum Averaging { | 173 | T::regs().cr().modify(|reg| { |
| 144 | Disabled, | 174 | reg.set_adstart(true); |
| 145 | Samples2, | 175 | }); |
| 146 | Samples4, | 176 | |
| 147 | Samples8, | 177 | // Waiting for End Of Conversion (EOC). |
| 148 | Samples16, | 178 | while !T::regs().isr().read().eoc() {} |
| 149 | Samples32, | 179 | |
| 150 | Samples64, | 180 | T::regs().dr().read().data() as u16 |
| 151 | Samples128, | 181 | } |
| 152 | Samples256, | ||
| 153 | Samples512, | ||
| 154 | Samples1024, | ||
| 155 | } | 182 | } |
| 156 | 183 | ||
| 157 | impl<'d, T: Instance> Adc<'d, T> { | 184 | impl<'d, T: AnyInstance> Adc<'d, T> { |
| 158 | /// Create a new ADC driver. | 185 | /// Create a new ADC driver. |
| 159 | pub fn new(adc: Peri<'d, T>, sample_time: SampleTime, resolution: Resolution) -> Self { | 186 | pub fn new(adc: Peri<'d, T>, resolution: Resolution) -> Self { |
| 160 | rcc::enable_and_reset::<T>(); | 187 | rcc::enable_and_reset::<T>(); |
| 161 | 188 | ||
| 162 | T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); | 189 | T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); |
| 163 | 190 | ||
| 164 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 191 | let prescaler = from_ker_ck(T::frequency()); |
| 165 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 192 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 166 | 193 | ||
| 167 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 194 | let frequency = T::frequency() / prescaler; |
| 168 | debug!("ADC frequency set to {}", frequency); | 195 | debug!("ADC frequency set to {}", frequency); |
| 169 | 196 | ||
| 170 | if frequency > MAX_ADC_CLK_FREQ { | 197 | if frequency > MAX_ADC_CLK_FREQ { |
| @@ -174,37 +201,16 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 174 | ); | 201 | ); |
| 175 | } | 202 | } |
| 176 | 203 | ||
| 177 | let mut s = Self { | ||
| 178 | adc, | ||
| 179 | sample_time: SampleTime::from_bits(0), | ||
| 180 | }; | ||
| 181 | |||
| 182 | s.power_up(); | ||
| 183 | |||
| 184 | s.set_resolution(resolution); | ||
| 185 | |||
| 186 | s.calibrate(); | ||
| 187 | |||
| 188 | s.enable(); | ||
| 189 | |||
| 190 | s.configure_default(); | ||
| 191 | |||
| 192 | s.set_sample_time_all_channels(sample_time); | ||
| 193 | |||
| 194 | s | ||
| 195 | } | ||
| 196 | |||
| 197 | fn power_up(&mut self) { | ||
| 198 | T::regs().cr().modify(|reg| { | 204 | T::regs().cr().modify(|reg| { |
| 199 | reg.set_advregen(true); | 205 | reg.set_advregen(true); |
| 200 | }); | 206 | }); |
| 201 | 207 | ||
| 202 | // "The software must wait for the ADC voltage regulator startup time." | 208 | // "The software must wait for the ADC voltage regulator startup time." |
| 203 | // See datasheet for the value. | 209 | // See datasheet for the value. |
| 204 | blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US + 1); | 210 | blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US as u64 + 1); |
| 205 | } | 211 | |
| 212 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); | ||
| 206 | 213 | ||
| 207 | fn calibrate(&mut self) { | ||
| 208 | // We have to make sure AUTOFF is OFF, but keep its value after calibration. | 214 | // We have to make sure AUTOFF is OFF, but keep its value after calibration. |
| 209 | let autoff_value = T::regs().cfgr1().read().autoff(); | 215 | let autoff_value = T::regs().cfgr1().read().autoff(); |
| 210 | T::regs().cfgr1().modify(|w| w.set_autoff(false)); | 216 | T::regs().cfgr1().modify(|w| w.set_autoff(false)); |
| @@ -218,255 +224,35 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 218 | debug!("ADC calibration value: {}.", T::regs().dr().read().data()); | 224 | debug!("ADC calibration value: {}.", T::regs().dr().read().data()); |
| 219 | 225 | ||
| 220 | T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); | 226 | T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); |
| 221 | } | ||
| 222 | 227 | ||
| 223 | fn enable(&mut self) { | 228 | T::enable(); |
| 224 | T::regs().isr().modify(|w| w.set_adrdy(true)); | ||
| 225 | T::regs().cr().modify(|w| w.set_aden(true)); | ||
| 226 | // ADRDY is "ADC ready". Wait until it will be True. | ||
| 227 | while !T::regs().isr().read().adrdy() {} | ||
| 228 | } | ||
| 229 | 229 | ||
| 230 | fn configure_default(&mut self) { | ||
| 231 | // single conversion mode, software trigger | 230 | // single conversion mode, software trigger |
| 232 | T::regs().cfgr1().modify(|w| { | 231 | T::regs().cfgr1().modify(|w| { |
| 233 | w.set_cont(false); | 232 | w.set_cont(false); |
| 234 | w.set_exten(Exten::DISABLED); | 233 | w.set_exten(Exten::DISABLED); |
| 235 | w.set_align(Align::RIGHT); | 234 | w.set_align(Align::RIGHT); |
| 236 | }); | 235 | }); |
| 236 | |||
| 237 | Self { adc } | ||
| 237 | } | 238 | } |
| 238 | 239 | ||
| 239 | /// Enable reading the voltage reference internal channel. | 240 | /// Enable reading the voltage reference internal channel. |
| 240 | pub fn enable_vrefint(&self) -> VrefInt { | 241 | pub fn enable_vrefint(&self) -> super::VrefInt { |
| 241 | T::common_regs().ccr().modify(|reg| { | 242 | T::common_regs().ccr().modify(|reg| { |
| 242 | reg.set_vrefen(true); | 243 | reg.set_vrefen(true); |
| 243 | }); | 244 | }); |
| 244 | 245 | ||
| 245 | VrefInt {} | 246 | super::VrefInt {} |
| 246 | } | 247 | } |
| 247 | 248 | ||
| 248 | /// Enable reading the temperature internal channel. | 249 | /// Enable reading the temperature internal channel. |
| 249 | pub fn enable_temperature(&self) -> Temperature { | 250 | pub fn enable_temperature(&self) -> super::Temperature { |
| 250 | debug!("Ensure that sample time is set to more than temperature sensor T_start from the datasheet!"); | 251 | debug!("Ensure that sample time is set to more than temperature sensor T_start from the datasheet!"); |
| 251 | T::common_regs().ccr().modify(|reg| { | 252 | T::common_regs().ccr().modify(|reg| { |
| 252 | reg.set_tsen(true); | 253 | reg.set_tsen(true); |
| 253 | }); | 254 | }); |
| 254 | 255 | ||
| 255 | Temperature {} | 256 | super::Temperature {} |
| 256 | } | ||
| 257 | |||
| 258 | /// Set the ADC sample time. | ||
| 259 | /// Shall only be called when ADC is not converting. | ||
| 260 | pub fn set_sample_time_all_channels(&mut self, sample_time: SampleTime) { | ||
| 261 | self.sample_time = sample_time; | ||
| 262 | |||
| 263 | // Set all channels to use SMP1 field as source. | ||
| 264 | T::regs().smpr().modify(|w| { | ||
| 265 | w.smpsel(0); | ||
| 266 | w.set_smp1(sample_time); | ||
| 267 | }); | ||
| 268 | } | ||
| 269 | |||
| 270 | /// Set the ADC resolution. | ||
| 271 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 272 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); | ||
| 273 | } | ||
| 274 | |||
| 275 | /// Perform a single conversion. | ||
| 276 | fn convert(&mut self) -> u16 { | ||
| 277 | // Set single conversion mode. | ||
| 278 | T::regs().cfgr1().modify(|w| w.set_cont(false)); | ||
| 279 | |||
| 280 | // Start conversion | ||
| 281 | T::regs().cr().modify(|reg| { | ||
| 282 | reg.set_adstart(true); | ||
| 283 | }); | ||
| 284 | |||
| 285 | // Waiting for End Of Conversion (EOC). | ||
| 286 | while !T::regs().isr().read().eoc() {} | ||
| 287 | |||
| 288 | T::regs().dr().read().data() as u16 | ||
| 289 | } | ||
| 290 | |||
| 291 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 292 | Self::configure_channel(channel); | ||
| 293 | T::regs().cfgr1().write(|reg| { | ||
| 294 | reg.set_chselrmod(false); | ||
| 295 | reg.set_align(Align::RIGHT); | ||
| 296 | }); | ||
| 297 | self.convert() | ||
| 298 | } | ||
| 299 | |||
| 300 | fn setup_channel_sequencer<'a>(channel_sequence: impl ExactSizeIterator<Item = &'a mut AnyAdcChannel<T>>) { | ||
| 301 | assert!( | ||
| 302 | channel_sequence.len() <= CHSELR_SQ_SIZE, | ||
| 303 | "Seqenced read set cannot be more than {} in size.", | ||
| 304 | CHSELR_SQ_SIZE | ||
| 305 | ); | ||
| 306 | let mut last_sq_set: usize = 0; | ||
| 307 | T::regs().chselr_sq().write(|w| { | ||
| 308 | for (i, channel) in channel_sequence.enumerate() { | ||
| 309 | assert!( | ||
| 310 | channel.channel() <= CHSELR_SQ_MAX_CHANNEL, | ||
| 311 | "Sequencer only support HW channels smaller than {}.", | ||
| 312 | CHSELR_SQ_MAX_CHANNEL | ||
| 313 | ); | ||
| 314 | w.set_sq(i, channel.channel()); | ||
| 315 | last_sq_set = i; | ||
| 316 | } | ||
| 317 | |||
| 318 | for i in (last_sq_set + 1)..CHSELR_SQ_SIZE { | ||
| 319 | w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER); | ||
| 320 | } | ||
| 321 | }); | ||
| 322 | |||
| 323 | Self::apply_channel_conf() | ||
| 324 | } | ||
| 325 | |||
| 326 | async fn dma_convert(&mut self, rx_dma: Peri<'_, impl RxDma<T>>, readings: &mut [u16]) { | ||
| 327 | // Enable overrun control, so no new DMA requests will be generated until | ||
| 328 | // previous DR values is read. | ||
| 329 | T::regs().isr().modify(|reg| { | ||
| 330 | reg.set_ovr(true); | ||
| 331 | }); | ||
| 332 | |||
| 333 | // Set continuous mode with oneshot dma. | ||
| 334 | T::regs().cfgr1().modify(|reg| { | ||
| 335 | reg.set_discen(false); | ||
| 336 | reg.set_cont(true); | ||
| 337 | reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT); | ||
| 338 | reg.set_dmaen(true); | ||
| 339 | reg.set_ovrmod(Ovrmod::PRESERVE); | ||
| 340 | }); | ||
| 341 | |||
| 342 | let request = rx_dma.request(); | ||
| 343 | let transfer = unsafe { | ||
| 344 | Transfer::new_read( | ||
| 345 | rx_dma, | ||
| 346 | request, | ||
| 347 | T::regs().dr().as_ptr() as *mut u16, | ||
| 348 | readings, | ||
| 349 | Default::default(), | ||
| 350 | ) | ||
| 351 | }; | ||
| 352 | |||
| 353 | // Start conversion. | ||
| 354 | T::regs().cr().modify(|reg| { | ||
| 355 | reg.set_adstart(true); | ||
| 356 | }); | ||
| 357 | |||
| 358 | // Wait for conversion sequence to finish. | ||
| 359 | transfer.await; | ||
| 360 | |||
| 361 | // Ensure conversions are finished. | ||
| 362 | Self::cancel_conversions(); | ||
| 363 | |||
| 364 | // Reset configuration. | ||
| 365 | T::regs().cfgr1().modify(|reg| { | ||
| 366 | reg.set_cont(false); | ||
| 367 | reg.set_dmacfg(Dmacfg::from_bits(0)); | ||
| 368 | reg.set_dmaen(false); | ||
| 369 | }); | ||
| 370 | } | ||
| 371 | |||
| 372 | /// Read one or multiple ADC channels using DMA in hardware order. | ||
| 373 | /// Readings will be ordered based on **hardware** ADC channel number and `scandir` setting. | ||
| 374 | /// Readings won't be in the same order as in the `set`! | ||
| 375 | /// | ||
| 376 | /// In STM32C0, channels bigger than 14 cannot be read using sequencer, so you have to use | ||
| 377 | /// either blocking reads or use the mechanism to read in HW order (CHSELRMOD=0). | ||
| 378 | /// TODO(chudsaviet): externalize generic code and merge with read(). | ||
| 379 | pub async fn read_in_hw_order( | ||
| 380 | &mut self, | ||
| 381 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 382 | hw_channel_selection: u32, | ||
| 383 | scandir: Scandir, | ||
| 384 | readings: &mut [u16], | ||
| 385 | ) { | ||
| 386 | assert!( | ||
| 387 | hw_channel_selection != 0, | ||
| 388 | "Some bits in `hw_channel_selection` shall be set." | ||
| 389 | ); | ||
| 390 | assert!( | ||
| 391 | (hw_channel_selection >> NUM_HW_CHANNELS) == 0, | ||
| 392 | "STM32C0 only have {} ADC channels. `hw_channel_selection` cannot have bits higher than this number set.", | ||
| 393 | NUM_HW_CHANNELS | ||
| 394 | ); | ||
| 395 | // To check for correct readings slice size, we shall solve Hamming weight problem, | ||
| 396 | // which is either slow or memory consuming. | ||
| 397 | // Since we have limited resources, we don't do it here. | ||
| 398 | // Not doing this have a great potential for a bug through. | ||
| 399 | |||
| 400 | // Ensure no conversions are ongoing. | ||
| 401 | Self::cancel_conversions(); | ||
| 402 | |||
| 403 | T::regs().cfgr1().modify(|reg| { | ||
| 404 | reg.set_chselrmod(false); | ||
| 405 | reg.set_scandir(scandir); | ||
| 406 | reg.set_align(Align::RIGHT); | ||
| 407 | }); | ||
| 408 | |||
| 409 | // Set required channels for multi-convert. | ||
| 410 | unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } | ||
| 411 | |||
| 412 | Self::apply_channel_conf(); | ||
| 413 | |||
| 414 | self.dma_convert(rx_dma, readings).await | ||
| 415 | } | ||
| 416 | |||
| 417 | // Read ADC channels in specified order using DMA (CHSELRMOD = 1). | ||
| 418 | // In STM32C0, only lower 14 ADC channels can be read this way. | ||
| 419 | // For other channels, use `read_in_hw_order()` or blocking read. | ||
| 420 | pub async fn read( | ||
| 421 | &mut self, | ||
| 422 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 423 | channel_sequence: impl ExactSizeIterator<Item = &mut AnyAdcChannel<T>>, | ||
| 424 | readings: &mut [u16], | ||
| 425 | ) { | ||
| 426 | assert!( | ||
| 427 | channel_sequence.len() != 0, | ||
| 428 | "Asynchronous read channel sequence cannot be empty." | ||
| 429 | ); | ||
| 430 | assert!( | ||
| 431 | channel_sequence.len() == readings.len(), | ||
| 432 | "Channel sequence length must be equal to readings length." | ||
| 433 | ); | ||
| 434 | |||
| 435 | // Ensure no conversions are ongoing. | ||
| 436 | Self::cancel_conversions(); | ||
| 437 | |||
| 438 | T::regs().cfgr1().modify(|reg| { | ||
| 439 | reg.set_chselrmod(true); | ||
| 440 | reg.set_align(Align::RIGHT); | ||
| 441 | }); | ||
| 442 | |||
| 443 | Self::setup_channel_sequencer(channel_sequence); | ||
| 444 | |||
| 445 | self.dma_convert(rx_dma, readings).await | ||
| 446 | } | ||
| 447 | |||
| 448 | fn configure_channel(channel: &mut impl AdcChannel<T>) { | ||
| 449 | channel.setup(); | ||
| 450 | // write() because we want all other bits to be set to 0. | ||
| 451 | T::regs() | ||
| 452 | .chselr() | ||
| 453 | .write(|w| w.set_chsel(channel.channel().into(), true)); | ||
| 454 | |||
| 455 | Self::apply_channel_conf(); | ||
| 456 | } | ||
| 457 | |||
| 458 | fn apply_channel_conf() { | ||
| 459 | // Trigger and wait for the channel selection procedure to complete. | ||
| 460 | T::regs().isr().modify(|w| w.set_ccrdy(false)); | ||
| 461 | while !T::regs().isr().read().ccrdy() {} | ||
| 462 | } | ||
| 463 | |||
| 464 | fn cancel_conversions() { | ||
| 465 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 466 | T::regs().cr().modify(|reg| { | ||
| 467 | reg.set_adstp(Adstp::STOP); | ||
| 468 | }); | ||
| 469 | while T::regs().cr().read().adstart() {} | ||
| 470 | } | ||
| 471 | } | 257 | } |
| 472 | } | 258 | } |
diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index f9c23d72b..d6c6f480b 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs | |||
| @@ -3,7 +3,7 @@ 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; |
| @@ -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 73ceb087a..29bfdac97 100644 --- a/embassy-stm32/src/adc/f3.rs +++ b/embassy-stm32/src/adc/f3.rs | |||
| @@ -3,7 +3,7 @@ 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::{Peri, interrupt, rcc}; | 9 | use crate::{Peri, interrupt, rcc}; |
| @@ -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 cd5de54f5..919ac3cc0 100644 --- a/embassy-stm32/src/adc/f3_v1_1.rs +++ b/embassy-stm32/src/adc/f3_v1_1.rs | |||
| @@ -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 3767820cf..bd8ccbf17 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use core::mem; | 1 | #[cfg(stm32g4)] |
| 2 | 2 | use pac::adc::regs::Difsel as DifselReg; | |
| 3 | #[allow(unused)] | 3 | #[allow(unused)] |
| 4 | #[cfg(stm32h7)] | 4 | #[cfg(stm32h7)] |
| 5 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; | 5 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; |
| @@ -10,15 +10,14 @@ pub use pac::adccommon::vals::Presc; | |||
| 10 | pub use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; | 10 | pub use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; |
| 11 | pub use stm32_metapac::adccommon::vals::Dual; | 11 | pub use stm32_metapac::adccommon::vals::Dual; |
| 12 | 12 | ||
| 13 | use super::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, blocking_delay_us}; | 13 | use super::{ |
| 14 | use crate::adc::SealedAdcChannel; | 14 | Adc, AnyAdcChannel, ConversionMode, Instance, RegularConversionMode, Resolution, RxDma, SampleTime, |
| 15 | use crate::dma::Transfer; | 15 | blocking_delay_us, |
| 16 | }; | ||
| 17 | use crate::adc::{AnyInstance, SealedAdcChannel}; | ||
| 16 | use crate::time::Hertz; | 18 | use crate::time::Hertz; |
| 17 | use crate::{Peri, pac, rcc}; | 19 | use crate::{Peri, pac, rcc}; |
| 18 | 20 | ||
| 19 | mod ringbuffered; | ||
| 20 | pub use ringbuffered::RingBufferedAdc; | ||
| 21 | |||
| 22 | mod injected; | 21 | mod injected; |
| 23 | pub use injected::InjectedAdc; | 22 | pub use injected::InjectedAdc; |
| 24 | 23 | ||
| @@ -35,100 +34,31 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); | |||
| 35 | #[cfg(stm32h7)] | 34 | #[cfg(stm32h7)] |
| 36 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); | 35 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); |
| 37 | 36 | ||
| 38 | // 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 | 37 | fn from_ker_ck(frequency: Hertz) -> Presc { |
| 39 | /// Internal voltage reference channel. | 38 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; |
| 40 | pub struct VrefInt; | 39 | match raw_prescaler { |
| 41 | impl<T: Instance + VrefChannel> AdcChannel<T> for VrefInt {} | 40 | 0 => Presc::DIV1, |
| 42 | impl<T: Instance + VrefChannel> super::SealedAdcChannel<T> for VrefInt { | 41 | 1 => Presc::DIV2, |
| 43 | fn channel(&self) -> u8 { | 42 | 2..=3 => Presc::DIV4, |
| 44 | T::CHANNEL | 43 | 4..=5 => Presc::DIV6, |
| 45 | } | 44 | 6..=7 => Presc::DIV8, |
| 46 | } | 45 | 8..=9 => Presc::DIV10, |
| 47 | 46 | 10..=11 => Presc::DIV12, | |
| 48 | /// Internal temperature channel. | 47 | _ => unimplemented!(), |
| 49 | pub struct Temperature; | ||
| 50 | impl<T: Instance + TemperatureChannel> AdcChannel<T> for Temperature {} | ||
| 51 | impl<T: Instance + TemperatureChannel> super::SealedAdcChannel<T> for Temperature { | ||
| 52 | fn channel(&self) -> u8 { | ||
| 53 | T::CHANNEL | ||
| 54 | } | 48 | } |
| 55 | } | 49 | } |
| 56 | 50 | ||
| 57 | /// Internal battery voltage channel. | 51 | /// ADC configuration |
| 58 | pub struct Vbat; | 52 | #[derive(Default)] |
| 59 | impl<T: Instance + VBatChannel> AdcChannel<T> for Vbat {} | 53 | pub struct AdcConfig { |
| 60 | impl<T: Instance + VBatChannel> super::SealedAdcChannel<T> for Vbat { | 54 | pub dual_mode: Option<Dual>, |
| 61 | fn channel(&self) -> u8 { | 55 | pub resolution: Option<Resolution>, |
| 62 | T::CHANNEL | 56 | #[cfg(stm32g4)] |
| 63 | } | 57 | pub oversampling_shift: Option<u8>, |
| 64 | } | 58 | #[cfg(stm32g4)] |
| 65 | 59 | pub oversampling_ratio: Option<u8>, | |
| 66 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | 60 | #[cfg(stm32g4)] |
| 67 | // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. | 61 | pub oversampling_mode: Option<(Rovsm, Trovs, bool)>, |
| 68 | #[allow(unused)] | ||
| 69 | enum Prescaler { | ||
| 70 | NotDivided, | ||
| 71 | DividedBy2, | ||
| 72 | DividedBy4, | ||
| 73 | DividedBy6, | ||
| 74 | DividedBy8, | ||
| 75 | DividedBy10, | ||
| 76 | DividedBy12, | ||
| 77 | DividedBy16, | ||
| 78 | DividedBy32, | ||
| 79 | DividedBy64, | ||
| 80 | DividedBy128, | ||
| 81 | DividedBy256, | ||
| 82 | } | ||
| 83 | |||
| 84 | impl Prescaler { | ||
| 85 | fn from_ker_ck(frequency: Hertz) -> Self { | ||
| 86 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | ||
| 87 | match raw_prescaler { | ||
| 88 | 0 => Self::NotDivided, | ||
| 89 | 1 => Self::DividedBy2, | ||
| 90 | 2..=3 => Self::DividedBy4, | ||
| 91 | 4..=5 => Self::DividedBy6, | ||
| 92 | 6..=7 => Self::DividedBy8, | ||
| 93 | 8..=9 => Self::DividedBy10, | ||
| 94 | 10..=11 => Self::DividedBy12, | ||
| 95 | _ => unimplemented!(), | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | fn divisor(&self) -> u32 { | ||
| 100 | match self { | ||
| 101 | Prescaler::NotDivided => 1, | ||
| 102 | Prescaler::DividedBy2 => 2, | ||
| 103 | Prescaler::DividedBy4 => 4, | ||
| 104 | Prescaler::DividedBy6 => 6, | ||
| 105 | Prescaler::DividedBy8 => 8, | ||
| 106 | Prescaler::DividedBy10 => 10, | ||
| 107 | Prescaler::DividedBy12 => 12, | ||
| 108 | Prescaler::DividedBy16 => 16, | ||
| 109 | Prescaler::DividedBy32 => 32, | ||
| 110 | Prescaler::DividedBy64 => 64, | ||
| 111 | Prescaler::DividedBy128 => 128, | ||
| 112 | Prescaler::DividedBy256 => 256, | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | fn presc(&self) -> Presc { | ||
| 117 | match self { | ||
| 118 | Prescaler::NotDivided => Presc::DIV1, | ||
| 119 | Prescaler::DividedBy2 => Presc::DIV2, | ||
| 120 | Prescaler::DividedBy4 => Presc::DIV4, | ||
| 121 | Prescaler::DividedBy6 => Presc::DIV6, | ||
| 122 | Prescaler::DividedBy8 => Presc::DIV8, | ||
| 123 | Prescaler::DividedBy10 => Presc::DIV10, | ||
| 124 | Prescaler::DividedBy12 => Presc::DIV12, | ||
| 125 | Prescaler::DividedBy16 => Presc::DIV16, | ||
| 126 | Prescaler::DividedBy32 => Presc::DIV32, | ||
| 127 | Prescaler::DividedBy64 => Presc::DIV64, | ||
| 128 | Prescaler::DividedBy128 => Presc::DIV128, | ||
| 129 | Prescaler::DividedBy256 => Presc::DIV256, | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | 62 | } |
| 133 | 63 | ||
| 134 | // Trigger source for ADC conversions¨ | 64 | // Trigger source for ADC conversions¨ |
| @@ -140,90 +70,12 @@ pub struct ConversionTrigger { | |||
| 140 | pub edge: Exten, | 70 | pub edge: Exten, |
| 141 | } | 71 | } |
| 142 | 72 | ||
| 143 | // Conversion mode for regular ADC channels | 73 | impl<T: Instance> super::SealedAnyInstance for T { |
| 144 | #[derive(Copy, Clone)] | 74 | fn dr() -> *mut u16 { |
| 145 | pub enum RegularConversionMode { | 75 | T::regs().dr().as_ptr() as *mut u16 |
| 146 | // Samples as fast as possible | ||
| 147 | Continuous, | ||
| 148 | // Sample at rate determined by external trigger | ||
| 149 | Triggered(ConversionTrigger), | ||
| 150 | } | ||
| 151 | |||
| 152 | impl<'d, T: Instance> Adc<'d, T> { | ||
| 153 | /// Create a new ADC driver. | ||
| 154 | pub fn new(adc: Peri<'d, T>) -> Self { | ||
| 155 | rcc::enable_and_reset::<T>(); | ||
| 156 | |||
| 157 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | ||
| 158 | |||
| 159 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | ||
| 160 | |||
| 161 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | ||
| 162 | trace!("ADC frequency set to {}", frequency); | ||
| 163 | |||
| 164 | if frequency > MAX_ADC_CLK_FREQ { | ||
| 165 | panic!( | ||
| 166 | "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", | ||
| 167 | MAX_ADC_CLK_FREQ.0 / 1_000_000 | ||
| 168 | ); | ||
| 169 | } | ||
| 170 | |||
| 171 | let mut s = Self { | ||
| 172 | adc, | ||
| 173 | sample_time: SampleTime::from_bits(0), | ||
| 174 | }; | ||
| 175 | s.power_up(); | ||
| 176 | s.configure_differential_inputs(); | ||
| 177 | |||
| 178 | s.calibrate(); | ||
| 179 | blocking_delay_us(1); | ||
| 180 | |||
| 181 | s.enable(); | ||
| 182 | s.configure(); | ||
| 183 | |||
| 184 | s | ||
| 185 | } | ||
| 186 | |||
| 187 | fn power_up(&mut self) { | ||
| 188 | T::regs().cr().modify(|reg| { | ||
| 189 | reg.set_deeppwd(false); | ||
| 190 | reg.set_advregen(true); | ||
| 191 | }); | ||
| 192 | |||
| 193 | blocking_delay_us(20); | ||
| 194 | } | ||
| 195 | |||
| 196 | fn configure_differential_inputs(&mut self) { | ||
| 197 | T::regs().difsel().modify(|w| { | ||
| 198 | for n in 0..18 { | ||
| 199 | w.set_difsel(n, Difsel::SINGLE_ENDED); | ||
| 200 | } | ||
| 201 | }); | ||
| 202 | } | 76 | } |
| 203 | 77 | ||
| 204 | fn calibrate(&mut self) { | 78 | fn enable() { |
| 205 | T::regs().cr().modify(|w| { | ||
| 206 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); | ||
| 207 | }); | ||
| 208 | |||
| 209 | T::regs().cr().modify(|w| w.set_adcal(true)); | ||
| 210 | |||
| 211 | while T::regs().cr().read().adcal() {} | ||
| 212 | |||
| 213 | blocking_delay_us(20); | ||
| 214 | |||
| 215 | T::regs().cr().modify(|w| { | ||
| 216 | w.set_adcaldif(Adcaldif::DIFFERENTIAL); | ||
| 217 | }); | ||
| 218 | |||
| 219 | T::regs().cr().modify(|w| w.set_adcal(true)); | ||
| 220 | |||
| 221 | while T::regs().cr().read().adcal() {} | ||
| 222 | |||
| 223 | blocking_delay_us(20); | ||
| 224 | } | ||
| 225 | |||
| 226 | fn enable(&mut self) { | ||
| 227 | // Make sure bits are off | 79 | // Make sure bits are off |
| 228 | while T::regs().cr().read().addis() { | 80 | while T::regs().cr().read().addis() { |
| 229 | // spin | 81 | // spin |
| @@ -244,124 +96,30 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 244 | } | 96 | } |
| 245 | } | 97 | } |
| 246 | 98 | ||
| 247 | fn configure(&mut self) { | 99 | fn start() { |
| 248 | // single conversion mode, software trigger | 100 | T::regs().cr().modify(|reg| { |
| 249 | T::regs().cfgr().modify(|w| { | 101 | reg.set_adstart(true); |
| 250 | w.set_cont(false); | ||
| 251 | w.set_exten(Exten::DISABLED); | ||
| 252 | }); | ||
| 253 | } | ||
| 254 | |||
| 255 | /// Enable reading the voltage reference internal channel. | ||
| 256 | pub fn enable_vrefint(&self) -> VrefInt | ||
| 257 | where | ||
| 258 | T: VrefChannel, | ||
| 259 | { | ||
| 260 | T::common_regs().ccr().modify(|reg| { | ||
| 261 | reg.set_vrefen(true); | ||
| 262 | }); | ||
| 263 | |||
| 264 | VrefInt {} | ||
| 265 | } | ||
| 266 | |||
| 267 | /// Enable reading the temperature internal channel. | ||
| 268 | pub fn enable_temperature(&self) -> Temperature | ||
| 269 | where | ||
| 270 | T: TemperatureChannel, | ||
| 271 | { | ||
| 272 | T::common_regs().ccr().modify(|reg| { | ||
| 273 | reg.set_vsenseen(true); | ||
| 274 | }); | 102 | }); |
| 275 | |||
| 276 | Temperature {} | ||
| 277 | } | 103 | } |
| 278 | 104 | ||
| 279 | /// Enable reading the vbat internal channel. | 105 | fn stop() { |
| 280 | pub fn enable_vbat(&self) -> Vbat | 106 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 281 | where | 107 | T::regs().cr().modify(|reg| { |
| 282 | T: VBatChannel, | 108 | reg.set_adstp(Adstp::STOP); |
| 283 | { | 109 | }); |
| 284 | T::common_regs().ccr().modify(|reg| { | 110 | // The software must poll ADSTART until the bit is reset before assuming the |
| 285 | reg.set_vbaten(true); | 111 | // ADC is completely stopped |
| 286 | }); | 112 | while T::regs().cr().read().adstart() {} |
| 287 | 113 | } | |
| 288 | Vbat {} | ||
| 289 | } | ||
| 290 | 114 | ||
| 291 | /// Enable differential channel. | 115 | // Disable dma control and continuous conversion, if enabled |
| 292 | /// Caution: | 116 | T::regs().cfgr().modify(|reg| { |
| 293 | /// : When configuring the channel “i” in differential input mode, its negative input voltage VINN[i] | 117 | reg.set_cont(false); |
| 294 | /// is connected to another channel. As a consequence, this channel is no longer usable in | 118 | reg.set_dmaen(Dmaen::DISABLE); |
| 295 | /// single-ended mode or in differential mode and must never be configured to be converted. | ||
| 296 | /// Some channels are shared between ADC1/ADC2/ADC3/ADC4/ADC5: this can make the | ||
| 297 | /// channel on the other ADC unusable. The only exception is when ADC master and the slave | ||
| 298 | /// operate in interleaved mode. | ||
| 299 | #[cfg(stm32g4)] | ||
| 300 | pub fn set_differential_channel(&mut self, ch: usize, enable: bool) { | ||
| 301 | T::regs().cr().modify(|w| w.set_aden(false)); // disable adc | ||
| 302 | T::regs().difsel().modify(|w| { | ||
| 303 | w.set_difsel( | ||
| 304 | ch, | ||
| 305 | if enable { | ||
| 306 | Difsel::DIFFERENTIAL | ||
| 307 | } else { | ||
| 308 | Difsel::SINGLE_ENDED | ||
| 309 | }, | ||
| 310 | ); | ||
| 311 | }); | 119 | }); |
| 312 | T::regs().cr().modify(|w| w.set_aden(true)); | ||
| 313 | } | ||
| 314 | |||
| 315 | #[cfg(stm32g4)] | ||
| 316 | pub fn set_differential(&mut self, channel: &mut impl AdcChannel<T>, enable: bool) { | ||
| 317 | self.set_differential_channel(channel.channel() as usize, enable); | ||
| 318 | } | ||
| 319 | |||
| 320 | /// Set oversampling shift. | ||
| 321 | #[cfg(stm32g4)] | ||
| 322 | pub fn set_oversampling_shift(&mut self, shift: u8) { | ||
| 323 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 324 | } | ||
| 325 | |||
| 326 | /// Set oversampling ratio. | ||
| 327 | #[cfg(stm32g4)] | ||
| 328 | pub fn set_oversampling_ratio(&mut self, ratio: u8) { | ||
| 329 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 330 | } | ||
| 331 | |||
| 332 | /// Enable oversampling in regular mode. | ||
| 333 | #[cfg(stm32g4)] | ||
| 334 | pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { | ||
| 335 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 336 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 337 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 338 | } | ||
| 339 | |||
| 340 | // Reads that are not implemented as INJECTED in "blocking_read" | ||
| 341 | // #[cfg(stm32g4)] | ||
| 342 | // pub fn enalble_injected_oversampling_mode(&mut self, enable: bool) { | ||
| 343 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); | ||
| 344 | // } | ||
| 345 | |||
| 346 | // #[cfg(stm32g4)] | ||
| 347 | // pub fn enable_oversampling_regular_injected_mode(&mut self, enable: bool) { | ||
| 348 | // // the regularoversampling mode is forced to resumed mode (ROVSM bit ignored), | ||
| 349 | // T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 350 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); | ||
| 351 | // } | ||
| 352 | |||
| 353 | /// Set the ADC sample time. | ||
| 354 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 355 | self.sample_time = sample_time; | ||
| 356 | } | ||
| 357 | |||
| 358 | /// Set the ADC resolution. | ||
| 359 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 360 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 361 | } | 120 | } |
| 362 | 121 | ||
| 363 | /// Perform a single conversion. | 122 | fn convert() -> u16 { |
| 364 | fn convert(&mut self) -> u16 { | ||
| 365 | T::regs().isr().modify(|reg| { | 123 | T::regs().isr().modify(|reg| { |
| 366 | reg.set_eos(true); | 124 | reg.set_eos(true); |
| 367 | reg.set_eoc(true); | 125 | reg.set_eoc(true); |
| @@ -379,277 +137,249 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 379 | T::regs().dr().read().0 as u16 | 137 | T::regs().dr().read().0 as u16 |
| 380 | } | 138 | } |
| 381 | 139 | ||
| 382 | /// Read an ADC pin. | 140 | fn configure_dma(conversion_mode: ConversionMode) { |
| 383 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 141 | T::regs().isr().modify(|reg| { |
| 384 | channel.setup(); | 142 | reg.set_ovr(true); |
| 385 | |||
| 386 | self.read_channel(channel) | ||
| 387 | } | ||
| 388 | |||
| 389 | /// Start regular adc conversion | ||
| 390 | pub(super) fn start() { | ||
| 391 | T::regs().cr().modify(|reg| { | ||
| 392 | reg.set_adstart(true); | ||
| 393 | }); | 143 | }); |
| 394 | } | ||
| 395 | |||
| 396 | /// Stop regular conversions | ||
| 397 | pub(super) fn stop() { | ||
| 398 | Self::stop_regular_conversions(); | ||
| 399 | } | ||
| 400 | |||
| 401 | /// Teardown method for stopping regular ADC conversions | ||
| 402 | pub(super) fn teardown_adc() { | ||
| 403 | Self::stop_regular_conversions(); | ||
| 404 | 144 | ||
| 405 | // Disable dma control | ||
| 406 | T::regs().cfgr().modify(|reg| { | 145 | T::regs().cfgr().modify(|reg| { |
| 407 | reg.set_dmaen(Dmaen::DISABLE); | 146 | reg.set_discen(false); // Convert all channels for each trigger |
| 147 | reg.set_dmacfg(match conversion_mode { | ||
| 148 | ConversionMode::Singular => Dmacfg::ONE_SHOT, | ||
| 149 | ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, | ||
| 150 | }); | ||
| 151 | reg.set_dmaen(Dmaen::ENABLE); | ||
| 408 | }); | 152 | }); |
| 409 | } | ||
| 410 | 153 | ||
| 411 | /// Read one or multiple ADC regular channels using DMA. | 154 | if let ConversionMode::Repeated(mode) = conversion_mode { |
| 412 | /// | 155 | match mode { |
| 413 | /// `sequence` iterator and `readings` must have the same length. | 156 | RegularConversionMode::Continuous => { |
| 414 | /// | 157 | T::regs().cfgr().modify(|reg| { |
| 415 | /// Example | 158 | reg.set_cont(true); |
| 416 | /// ```rust,ignore | 159 | }); |
| 417 | /// use embassy_stm32::adc::{Adc, AdcChannel} | 160 | } |
| 418 | /// | 161 | RegularConversionMode::Triggered(trigger) => { |
| 419 | /// let mut adc = Adc::new(p.ADC1); | 162 | T::regs().cfgr().modify(|r| { |
| 420 | /// let mut adc_pin0 = p.PA0.into(); | 163 | r.set_cont(false); // New trigger is neede for each sample to be read |
| 421 | /// let mut adc_pin1 = p.PA1.into(); | 164 | }); |
| 422 | /// let mut measurements = [0u16; 2]; | ||
| 423 | /// | ||
| 424 | /// adc.read( | ||
| 425 | /// p.DMA1_CH2.reborrow(), | ||
| 426 | /// [ | ||
| 427 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 428 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 429 | /// ] | ||
| 430 | /// .into_iter(), | ||
| 431 | /// &mut measurements, | ||
| 432 | /// ) | ||
| 433 | /// .await; | ||
| 434 | /// defmt::info!("measurements: {}", measurements); | ||
| 435 | /// ``` | ||
| 436 | /// | ||
| 437 | /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use | ||
| 438 | /// `into_ring_buffered`, `into_ring_buffered_and_injected` | ||
| 439 | pub async fn read( | ||
| 440 | &mut self, | ||
| 441 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 442 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 443 | readings: &mut [u16], | ||
| 444 | ) { | ||
| 445 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 446 | assert!( | ||
| 447 | sequence.len() == readings.len(), | ||
| 448 | "Sequence length must be equal to readings length" | ||
| 449 | ); | ||
| 450 | assert!( | ||
| 451 | sequence.len() <= 16, | ||
| 452 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 453 | ); | ||
| 454 | 165 | ||
| 455 | // Ensure no conversions are ongoing and ADC is enabled. | 166 | T::regs().cfgr().modify(|r| { |
| 456 | Self::stop_regular_conversions(); | 167 | r.set_extsel(trigger.channel); |
| 457 | self.enable(); | 168 | r.set_exten(trigger.edge); |
| 169 | }); | ||
| 170 | |||
| 171 | // Regular conversions uses DMA so no need to generate interrupt | ||
| 172 | T::regs().ier().modify(|r| r.set_eosie(false)); | ||
| 173 | } | ||
| 174 | } | ||
| 175 | } | ||
| 176 | } | ||
| 458 | 177 | ||
| 178 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { | ||
| 459 | // Set sequence length | 179 | // Set sequence length |
| 460 | T::regs().sqr1().modify(|w| { | 180 | T::regs().sqr1().modify(|w| { |
| 461 | w.set_l(sequence.len() as u8 - 1); | 181 | w.set_l(sequence.len() as u8 - 1); |
| 462 | }); | 182 | }); |
| 183 | |||
| 184 | #[cfg(stm32g4)] | ||
| 185 | let mut difsel = DifselReg::default(); | ||
| 186 | |||
| 463 | // Configure channels and ranks | 187 | // Configure channels and ranks |
| 464 | for (_i, (channel, sample_time)) in sequence.enumerate() { | 188 | for (_i, ((ch, is_differential), sample_time)) in sequence.enumerate() { |
| 465 | Self::configure_channel(channel, sample_time); | 189 | let sample_time = sample_time.into(); |
| 190 | if ch <= 9 { | ||
| 191 | T::regs().smpr().modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 192 | } else { | ||
| 193 | T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 194 | } | ||
| 195 | |||
| 466 | match _i { | 196 | match _i { |
| 467 | 0..=3 => { | 197 | 0..=3 => { |
| 468 | T::regs().sqr1().modify(|w| { | 198 | T::regs().sqr1().modify(|w| { |
| 469 | w.set_sq(_i, channel.channel()); | 199 | w.set_sq(_i, ch); |
| 470 | }); | 200 | }); |
| 471 | } | 201 | } |
| 472 | 4..=8 => { | 202 | 4..=8 => { |
| 473 | T::regs().sqr2().modify(|w| { | 203 | T::regs().sqr2().modify(|w| { |
| 474 | w.set_sq(_i - 4, channel.channel()); | 204 | w.set_sq(_i - 4, ch); |
| 475 | }); | 205 | }); |
| 476 | } | 206 | } |
| 477 | 9..=13 => { | 207 | 9..=13 => { |
| 478 | T::regs().sqr3().modify(|w| { | 208 | T::regs().sqr3().modify(|w| { |
| 479 | w.set_sq(_i - 9, channel.channel()); | 209 | w.set_sq(_i - 9, ch); |
| 480 | }); | 210 | }); |
| 481 | } | 211 | } |
| 482 | 14..=15 => { | 212 | 14..=15 => { |
| 483 | T::regs().sqr4().modify(|w| { | 213 | T::regs().sqr4().modify(|w| { |
| 484 | w.set_sq(_i - 14, channel.channel()); | 214 | w.set_sq(_i - 14, ch); |
| 485 | }); | 215 | }); |
| 486 | } | 216 | } |
| 487 | _ => unreachable!(), | 217 | _ => unreachable!(), |
| 488 | } | 218 | } |
| 219 | |||
| 220 | #[cfg(stm32g4)] | ||
| 221 | { | ||
| 222 | if ch < 18 { | ||
| 223 | difsel.set_difsel( | ||
| 224 | ch.into(), | ||
| 225 | if is_differential { | ||
| 226 | Difsel::DIFFERENTIAL | ||
| 227 | } else { | ||
| 228 | Difsel::SINGLE_ENDED | ||
| 229 | }, | ||
| 230 | ); | ||
| 231 | } | ||
| 232 | } | ||
| 489 | } | 233 | } |
| 490 | 234 | ||
| 491 | // Set continuous mode with oneshot dma. | 235 | #[cfg(stm32g4)] |
| 492 | // Clear overrun flag before starting transfer. | 236 | { |
| 493 | T::regs().isr().modify(|reg| { | 237 | T::regs().cr().modify(|w| w.set_aden(false)); |
| 494 | reg.set_ovr(true); | 238 | T::regs().difsel().write_value(difsel); |
| 495 | }); | 239 | T::enable(); |
| 240 | } | ||
| 241 | } | ||
| 242 | } | ||
| 496 | 243 | ||
| 497 | T::regs().cfgr().modify(|reg| { | 244 | impl<'d, T: Instance + AnyInstance> Adc<'d, T> { |
| 498 | reg.set_discen(false); | 245 | /// Create a new ADC driver. |
| 499 | reg.set_cont(true); | 246 | pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self { |
| 500 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | 247 | rcc::enable_and_reset::<T>(); |
| 501 | reg.set_dmaen(Dmaen::ENABLE); | ||
| 502 | }); | ||
| 503 | 248 | ||
| 504 | let request = rx_dma.request(); | 249 | let prescaler = from_ker_ck(T::frequency()); |
| 505 | let transfer = unsafe { | 250 | |
| 506 | Transfer::new_read( | 251 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 507 | rx_dma, | 252 | |
| 508 | request, | 253 | let frequency = T::frequency() / prescaler; |
| 509 | T::regs().dr().as_ptr() as *mut u16, | 254 | trace!("ADC frequency set to {}", frequency); |
| 510 | readings, | 255 | |
| 511 | Default::default(), | 256 | if frequency > MAX_ADC_CLK_FREQ { |
| 512 | ) | 257 | panic!( |
| 513 | }; | 258 | "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", |
| 259 | MAX_ADC_CLK_FREQ.0 / 1_000_000 | ||
| 260 | ); | ||
| 261 | } | ||
| 514 | 262 | ||
| 515 | // Start conversion | ||
| 516 | T::regs().cr().modify(|reg| { | 263 | T::regs().cr().modify(|reg| { |
| 517 | reg.set_adstart(true); | 264 | reg.set_deeppwd(false); |
| 265 | reg.set_advregen(true); | ||
| 518 | }); | 266 | }); |
| 519 | 267 | ||
| 520 | // Wait for conversion sequence to finish. | 268 | blocking_delay_us(20); |
| 521 | transfer.await; | ||
| 522 | 269 | ||
| 523 | // Ensure conversions are finished. | 270 | T::regs().difsel().modify(|w| { |
| 524 | Self::stop_regular_conversions(); | 271 | for n in 0..18 { |
| 272 | w.set_difsel(n, Difsel::SINGLE_ENDED); | ||
| 273 | } | ||
| 274 | }); | ||
| 525 | 275 | ||
| 526 | // Reset configuration. | 276 | T::regs().cr().modify(|w| { |
| 527 | T::regs().cfgr().modify(|reg| { | 277 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); |
| 528 | reg.set_cont(false); | ||
| 529 | }); | 278 | }); |
| 530 | } | ||
| 531 | 279 | ||
| 532 | /// Set external trigger for regular conversion sequence | 280 | T::regs().cr().modify(|w| w.set_adcal(true)); |
| 533 | fn set_regular_conversion_trigger(&mut self, trigger: ConversionTrigger) { | 281 | |
| 534 | T::regs().cfgr().modify(|r| { | 282 | while T::regs().cr().read().adcal() {} |
| 535 | r.set_extsel(trigger.channel); | 283 | |
| 536 | r.set_exten(trigger.edge); | 284 | blocking_delay_us(20); |
| 285 | |||
| 286 | T::regs().cr().modify(|w| { | ||
| 287 | w.set_adcaldif(Adcaldif::DIFFERENTIAL); | ||
| 537 | }); | 288 | }); |
| 538 | // Regular conversions uses DMA so no need to generate interrupt | ||
| 539 | T::regs().ier().modify(|r| r.set_eosie(false)); | ||
| 540 | } | ||
| 541 | 289 | ||
| 542 | // Dual ADC mode selection | 290 | T::regs().cr().modify(|w| w.set_adcal(true)); |
| 543 | pub fn configure_dual_mode(&mut self, val: Dual) { | ||
| 544 | T::common_regs().ccr().modify(|reg| { | ||
| 545 | reg.set_dual(val); | ||
| 546 | }) | ||
| 547 | } | ||
| 548 | 291 | ||
| 549 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | 292 | while T::regs().cr().read().adcal() {} |
| 550 | /// | ||
| 551 | /// Use the [`read`] method to retrieve measurements from the DMA ring buffer. The read buffer | ||
| 552 | /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended | ||
| 553 | /// to configure `dma_buf` as a double buffer so that one half can be read while the other half | ||
| 554 | /// is being filled by the DMA, preventing data loss. The trigger period of the ADC effectively | ||
| 555 | /// defines the period at which the buffer should be read. | ||
| 556 | /// | ||
| 557 | /// If continous conversion mode is selected, the provided `dma_buf` must be large enough to prevent | ||
| 558 | /// DMA buffer overruns. Its length should be a multiple of the number of ADC channels being measured. | ||
| 559 | /// For example, if 3 channels are measured and you want to store 40 samples per channel, | ||
| 560 | /// the buffer length should be `3 * 40 = 120`. | ||
| 561 | /// | ||
| 562 | /// # Parameters | ||
| 563 | /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer. | ||
| 564 | /// - `dma_buf`: The buffer where DMA stores ADC samples. | ||
| 565 | /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions. | ||
| 566 | /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered). | ||
| 567 | /// | ||
| 568 | /// # Returns | ||
| 569 | /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. | ||
| 570 | pub fn into_ring_buffered<'a>( | ||
| 571 | mut self, | ||
| 572 | dma: Peri<'a, impl RxDma<T>>, | ||
| 573 | dma_buf: &'a mut [u16], | ||
| 574 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 575 | mode: RegularConversionMode, | ||
| 576 | ) -> RingBufferedAdc<'a, T> { | ||
| 577 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 578 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 579 | assert!( | ||
| 580 | sequence.len() <= 16, | ||
| 581 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 582 | ); | ||
| 583 | // reset conversions and enable the adc | ||
| 584 | Self::stop_regular_conversions(); | ||
| 585 | self.enable(); | ||
| 586 | 293 | ||
| 587 | //adc side setup | 294 | blocking_delay_us(20); |
| 588 | 295 | ||
| 589 | // Set sequence length | 296 | T::enable(); |
| 590 | T::regs().sqr1().modify(|w| { | 297 | |
| 591 | w.set_l(sequence.len() as u8 - 1); | 298 | // single conversion mode, software trigger |
| 299 | T::regs().cfgr().modify(|w| { | ||
| 300 | w.set_cont(false); | ||
| 301 | w.set_exten(Exten::DISABLED); | ||
| 592 | }); | 302 | }); |
| 593 | 303 | ||
| 594 | // Configure channels and ranks | 304 | if let Some(dual) = config.dual_mode { |
| 595 | for (_i, (mut channel, sample_time)) in sequence.enumerate() { | 305 | T::common_regs().ccr().modify(|reg| { |
| 596 | Self::configure_channel(&mut channel, sample_time); | 306 | reg.set_dual(dual); |
| 307 | }) | ||
| 308 | } | ||
| 597 | 309 | ||
| 598 | match _i { | 310 | if let Some(resolution) = config.resolution { |
| 599 | 0..=3 => { | 311 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); |
| 600 | T::regs().sqr1().modify(|w| { | ||
| 601 | w.set_sq(_i, channel.channel()); | ||
| 602 | }); | ||
| 603 | } | ||
| 604 | 4..=8 => { | ||
| 605 | T::regs().sqr2().modify(|w| { | ||
| 606 | w.set_sq(_i - 4, channel.channel()); | ||
| 607 | }); | ||
| 608 | } | ||
| 609 | 9..=13 => { | ||
| 610 | T::regs().sqr3().modify(|w| { | ||
| 611 | w.set_sq(_i - 9, channel.channel()); | ||
| 612 | }); | ||
| 613 | } | ||
| 614 | 14..=15 => { | ||
| 615 | T::regs().sqr4().modify(|w| { | ||
| 616 | w.set_sq(_i - 14, channel.channel()); | ||
| 617 | }); | ||
| 618 | } | ||
| 619 | _ => unreachable!(), | ||
| 620 | } | ||
| 621 | } | 312 | } |
| 622 | 313 | ||
| 623 | // Clear overrun flag before starting transfer. | 314 | #[cfg(stm32g4)] |
| 624 | T::regs().isr().modify(|reg| { | 315 | if let Some(shift) = config.oversampling_shift { |
| 625 | reg.set_ovr(true); | 316 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); |
| 317 | } | ||
| 318 | |||
| 319 | #[cfg(stm32g4)] | ||
| 320 | if let Some(ratio) = config.oversampling_ratio { | ||
| 321 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 322 | } | ||
| 323 | |||
| 324 | #[cfg(stm32g4)] | ||
| 325 | if let Some((mode, trig_mode, enable)) = config.oversampling_mode { | ||
| 326 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 327 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 328 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 329 | } | ||
| 330 | |||
| 331 | Self { adc } | ||
| 332 | } | ||
| 333 | |||
| 334 | /// Enable reading the voltage reference internal channel. | ||
| 335 | pub fn enable_vrefint(&self) -> super::VrefInt | ||
| 336 | where | ||
| 337 | T: super::SpecialConverter<super::VrefInt>, | ||
| 338 | { | ||
| 339 | T::common_regs().ccr().modify(|reg| { | ||
| 340 | reg.set_vrefen(true); | ||
| 626 | }); | 341 | }); |
| 627 | 342 | ||
| 628 | T::regs().cfgr().modify(|reg| { | 343 | super::VrefInt {} |
| 629 | reg.set_discen(false); // Convert all channels for each trigger | 344 | } |
| 630 | reg.set_dmacfg(Dmacfg::CIRCULAR); | 345 | |
| 631 | reg.set_dmaen(Dmaen::ENABLE); | 346 | /// Enable reading the temperature internal channel. |
| 347 | pub fn enable_temperature(&self) -> super::Temperature | ||
| 348 | where | ||
| 349 | T: super::SpecialConverter<super::Temperature>, | ||
| 350 | { | ||
| 351 | T::common_regs().ccr().modify(|reg| { | ||
| 352 | reg.set_vsenseen(true); | ||
| 632 | }); | 353 | }); |
| 633 | 354 | ||
| 634 | match mode { | 355 | super::Temperature {} |
| 635 | RegularConversionMode::Continuous => { | 356 | } |
| 636 | T::regs().cfgr().modify(|reg| { | ||
| 637 | reg.set_cont(true); | ||
| 638 | }); | ||
| 639 | } | ||
| 640 | RegularConversionMode::Triggered(trigger) => { | ||
| 641 | T::regs().cfgr().modify(|r| { | ||
| 642 | r.set_cont(false); // New trigger is neede for each sample to be read | ||
| 643 | }); | ||
| 644 | self.set_regular_conversion_trigger(trigger); | ||
| 645 | } | ||
| 646 | } | ||
| 647 | 357 | ||
| 648 | mem::forget(self); | 358 | /// Enable reading the vbat internal channel. |
| 359 | pub fn enable_vbat(&self) -> super::Vbat | ||
| 360 | where | ||
| 361 | T: super::SpecialConverter<super::Vbat>, | ||
| 362 | { | ||
| 363 | T::common_regs().ccr().modify(|reg| { | ||
| 364 | reg.set_vbaten(true); | ||
| 365 | }); | ||
| 649 | 366 | ||
| 650 | RingBufferedAdc::new(dma, dma_buf) | 367 | super::Vbat {} |
| 651 | } | 368 | } |
| 652 | 369 | ||
| 370 | // Reads that are not implemented as INJECTED in "blocking_read" | ||
| 371 | // #[cfg(stm32g4)] | ||
| 372 | // pub fn enalble_injected_oversampling_mode(&mut self, enable: bool) { | ||
| 373 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); | ||
| 374 | // } | ||
| 375 | |||
| 376 | // #[cfg(stm32g4)] | ||
| 377 | // pub fn enable_oversampling_regular_injected_mode(&mut self, enable: bool) { | ||
| 378 | // // the regularoversampling mode is forced to resumed mode (ROVSM bit ignored), | ||
| 379 | // T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 380 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); | ||
| 381 | // } | ||
| 382 | |||
| 653 | /// Configures the ADC for injected conversions. | 383 | /// Configures the ADC for injected conversions. |
| 654 | /// | 384 | /// |
| 655 | /// Injected conversions are separate from the regular conversion sequence and are typically | 385 | /// Injected conversions are separate from the regular conversion sequence and are typically |
| @@ -678,7 +408,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 678 | /// - Accessing samples beyond `N` will result in a panic; use the returned type | 408 | /// - Accessing samples beyond `N` will result in a panic; use the returned type |
| 679 | /// `InjectedAdc<T, N>` to enforce bounds at compile time. | 409 | /// `InjectedAdc<T, N>` to enforce bounds at compile time. |
| 680 | pub fn setup_injected_conversions<'a, const N: usize>( | 410 | pub fn setup_injected_conversions<'a, const N: usize>( |
| 681 | mut self, | 411 | self, |
| 682 | sequence: [(AnyAdcChannel<T>, SampleTime); N], | 412 | sequence: [(AnyAdcChannel<T>, SampleTime); N], |
| 683 | trigger: ConversionTrigger, | 413 | trigger: ConversionTrigger, |
| 684 | interrupt: bool, | 414 | interrupt: bool, |
| @@ -690,13 +420,21 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 690 | NR_INJECTED_RANKS | 420 | NR_INJECTED_RANKS |
| 691 | ); | 421 | ); |
| 692 | 422 | ||
| 693 | Self::stop_regular_conversions(); | 423 | T::enable(); |
| 694 | self.enable(); | ||
| 695 | 424 | ||
| 696 | T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); | 425 | T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); |
| 697 | 426 | ||
| 698 | for (n, (mut channel, sample_time)) in sequence.into_iter().enumerate() { | 427 | for (n, (channel, sample_time)) in sequence.into_iter().enumerate() { |
| 699 | Self::configure_channel(&mut channel, sample_time); | 428 | let sample_time = sample_time.into(); |
| 429 | if channel.channel() <= 9 { | ||
| 430 | T::regs() | ||
| 431 | .smpr() | ||
| 432 | .modify(|reg| reg.set_smp(channel.channel() as _, sample_time)); | ||
| 433 | } else { | ||
| 434 | T::regs() | ||
| 435 | .smpr2() | ||
| 436 | .modify(|reg| reg.set_smp((channel.channel() - 10) as _, sample_time)); | ||
| 437 | } | ||
| 700 | 438 | ||
| 701 | let idx = match n { | 439 | let idx = match n { |
| 702 | 0..=3 => n, | 440 | 0..=3 => n, |
| @@ -711,8 +449,16 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 711 | 449 | ||
| 712 | T::regs().cfgr().modify(|reg| reg.set_jdiscen(false)); | 450 | T::regs().cfgr().modify(|reg| reg.set_jdiscen(false)); |
| 713 | 451 | ||
| 714 | self.set_injected_conversion_trigger(trigger); | 452 | // Set external trigger for injected conversion sequence |
| 715 | self.enable_injected_eos_interrupt(interrupt); | 453 | // Possible trigger values are seen in Table 167 in RM0440 Rev 9 |
| 454 | T::regs().jsqr().modify(|r| { | ||
| 455 | r.set_jextsel(trigger.channel); | ||
| 456 | r.set_jexten(trigger.edge); | ||
| 457 | }); | ||
| 458 | |||
| 459 | // Enable end of injected sequence interrupt | ||
| 460 | T::regs().ier().modify(|r| r.set_jeosie(interrupt)); | ||
| 461 | |||
| 716 | Self::start_injected_conversions(); | 462 | Self::start_injected_conversions(); |
| 717 | 463 | ||
| 718 | InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels | 464 | InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels |
| @@ -745,22 +491,20 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 745 | self, | 491 | self, |
| 746 | dma: Peri<'a, impl RxDma<T>>, | 492 | dma: Peri<'a, impl RxDma<T>>, |
| 747 | dma_buf: &'a mut [u16], | 493 | dma_buf: &'a mut [u16], |
| 748 | regular_sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | 494 | regular_sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, T::SampleTime)>, |
| 749 | regular_conversion_mode: RegularConversionMode, | 495 | regular_conversion_mode: RegularConversionMode, |
| 750 | injected_sequence: [(AnyAdcChannel<T>, SampleTime); N], | 496 | injected_sequence: [(AnyAdcChannel<T>, SampleTime); N], |
| 751 | injected_trigger: ConversionTrigger, | 497 | injected_trigger: ConversionTrigger, |
| 752 | injected_interrupt: bool, | 498 | injected_interrupt: bool, |
| 753 | ) -> (RingBufferedAdc<'a, T>, InjectedAdc<T, N>) { | 499 | ) -> (super::RingBufferedAdc<'a, T>, InjectedAdc<T, N>) { |
| 754 | unsafe { | 500 | unsafe { |
| 755 | ( | 501 | ( |
| 756 | Self { | 502 | Self { |
| 757 | adc: self.adc.clone_unchecked(), | 503 | adc: self.adc.clone_unchecked(), |
| 758 | sample_time: self.sample_time, | ||
| 759 | } | 504 | } |
| 760 | .into_ring_buffered(dma, dma_buf, regular_sequence, regular_conversion_mode), | 505 | .into_ring_buffered(dma, dma_buf, regular_sequence, regular_conversion_mode), |
| 761 | Self { | 506 | Self { |
| 762 | adc: self.adc.clone_unchecked(), | 507 | adc: self.adc.clone_unchecked(), |
| 763 | sample_time: self.sample_time, | ||
| 764 | } | 508 | } |
| 765 | .setup_injected_conversions(injected_sequence, injected_trigger, injected_interrupt), | 509 | .setup_injected_conversions(injected_sequence, injected_trigger, injected_interrupt), |
| 766 | ) | 510 | ) |
| @@ -785,64 +529,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 785 | reg.set_jadstart(true); | 529 | reg.set_jadstart(true); |
| 786 | }); | 530 | }); |
| 787 | } | 531 | } |
| 788 | |||
| 789 | /// Set external trigger for injected conversion sequence | ||
| 790 | /// Possible trigger values are seen in Table 167 in RM0440 Rev 9 | ||
| 791 | fn set_injected_conversion_trigger(&mut self, trigger: ConversionTrigger) { | ||
| 792 | T::regs().jsqr().modify(|r| { | ||
| 793 | r.set_jextsel(trigger.channel); | ||
| 794 | r.set_jexten(trigger.edge); | ||
| 795 | }); | ||
| 796 | } | ||
| 797 | |||
| 798 | /// Enable end of injected sequence interrupt | ||
| 799 | fn enable_injected_eos_interrupt(&mut self, enable: bool) { | ||
| 800 | T::regs().ier().modify(|r| r.set_jeosie(enable)); | ||
| 801 | } | ||
| 802 | |||
| 803 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | ||
| 804 | // Configure channel | ||
| 805 | Self::set_channel_sample_time(channel.channel(), sample_time); | ||
| 806 | } | ||
| 807 | |||
| 808 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 809 | Self::configure_channel(channel, self.sample_time); | ||
| 810 | #[cfg(stm32h7)] | ||
| 811 | { | ||
| 812 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 813 | T::regs() | ||
| 814 | .pcsel() | ||
| 815 | .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); | ||
| 816 | } | ||
| 817 | |||
| 818 | T::regs().sqr1().write(|reg| { | ||
| 819 | reg.set_sq(0, channel.channel()); | ||
| 820 | reg.set_l(0); | ||
| 821 | }); | ||
| 822 | |||
| 823 | self.convert() | ||
| 824 | } | ||
| 825 | |||
| 826 | fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | ||
| 827 | let sample_time = sample_time.into(); | ||
| 828 | if ch <= 9 { | ||
| 829 | T::regs().smpr().modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 830 | } else { | ||
| 831 | T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 832 | } | ||
| 833 | } | ||
| 834 | |||
| 835 | // Stop regular conversions | ||
| 836 | fn stop_regular_conversions() { | ||
| 837 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 838 | T::regs().cr().modify(|reg| { | ||
| 839 | reg.set_adstp(Adstp::STOP); | ||
| 840 | }); | ||
| 841 | // The software must poll ADSTART until the bit is reset before assuming the | ||
| 842 | // ADC is completely stopped | ||
| 843 | while T::regs().cr().read().adstart() {} | ||
| 844 | } | ||
| 845 | } | ||
| 846 | } | 532 | } |
| 847 | 533 | ||
| 848 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { | 534 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { |
| @@ -860,62 +546,49 @@ impl<T: Instance, const N: usize> InjectedAdc<T, N> { | |||
| 860 | } | 546 | } |
| 861 | } | 547 | } |
| 862 | 548 | ||
| 863 | /// Implemented for ADCs that have a Temperature channel | ||
| 864 | pub trait TemperatureChannel { | ||
| 865 | const CHANNEL: u8; | ||
| 866 | } | ||
| 867 | /// Implemented for ADCs that have a Vref channel | ||
| 868 | pub trait VrefChannel { | ||
| 869 | const CHANNEL: u8; | ||
| 870 | } | ||
| 871 | /// Implemented for ADCs that have a VBat channel | ||
| 872 | pub trait VBatChannel { | ||
| 873 | const CHANNEL: u8; | ||
| 874 | } | ||
| 875 | |||
| 876 | #[cfg(stm32g4)] | 549 | #[cfg(stm32g4)] |
| 877 | mod g4 { | 550 | mod g4 { |
| 878 | pub use super::*; | 551 | use crate::adc::{SealedSpecialConverter, Temperature, Vbat, VrefInt}; |
| 879 | 552 | ||
| 880 | impl TemperatureChannel for crate::peripherals::ADC1 { | 553 | impl SealedSpecialConverter<Temperature> for crate::peripherals::ADC1 { |
| 881 | const CHANNEL: u8 = 16; | 554 | const CHANNEL: u8 = 16; |
| 882 | } | 555 | } |
| 883 | 556 | ||
| 884 | impl VrefChannel for crate::peripherals::ADC1 { | 557 | impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC1 { |
| 885 | const CHANNEL: u8 = 18; | 558 | const CHANNEL: u8 = 18; |
| 886 | } | 559 | } |
| 887 | 560 | ||
| 888 | impl VBatChannel for crate::peripherals::ADC1 { | 561 | impl SealedSpecialConverter<Vbat> for crate::peripherals::ADC1 { |
| 889 | const CHANNEL: u8 = 17; | 562 | const CHANNEL: u8 = 17; |
| 890 | } | 563 | } |
| 891 | 564 | ||
| 892 | #[cfg(peri_adc3_common)] | 565 | #[cfg(peri_adc3_common)] |
| 893 | impl VrefChannel for crate::peripherals::ADC3 { | 566 | impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC3 { |
| 894 | const CHANNEL: u8 = 18; | 567 | const CHANNEL: u8 = 18; |
| 895 | } | 568 | } |
| 896 | 569 | ||
| 897 | #[cfg(peri_adc3_common)] | 570 | #[cfg(peri_adc3_common)] |
| 898 | impl VBatChannel for crate::peripherals::ADC3 { | 571 | impl SealedSpecialConverter<Vbat> for crate::peripherals::ADC3 { |
| 899 | const CHANNEL: u8 = 17; | 572 | const CHANNEL: u8 = 17; |
| 900 | } | 573 | } |
| 901 | 574 | ||
| 902 | #[cfg(not(stm32g4x1))] | 575 | #[cfg(not(stm32g4x1))] |
| 903 | impl VrefChannel for crate::peripherals::ADC4 { | 576 | impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC4 { |
| 904 | const CHANNEL: u8 = 18; | 577 | const CHANNEL: u8 = 18; |
| 905 | } | 578 | } |
| 906 | 579 | ||
| 907 | #[cfg(not(stm32g4x1))] | 580 | #[cfg(not(stm32g4x1))] |
| 908 | impl TemperatureChannel for crate::peripherals::ADC5 { | 581 | impl SealedSpecialConverter<Temperature> for crate::peripherals::ADC5 { |
| 909 | const CHANNEL: u8 = 4; | 582 | const CHANNEL: u8 = 4; |
| 910 | } | 583 | } |
| 911 | 584 | ||
| 912 | #[cfg(not(stm32g4x1))] | 585 | #[cfg(not(stm32g4x1))] |
| 913 | impl VrefChannel for crate::peripherals::ADC5 { | 586 | impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC5 { |
| 914 | const CHANNEL: u8 = 18; | 587 | const CHANNEL: u8 = 18; |
| 915 | } | 588 | } |
| 916 | 589 | ||
| 917 | #[cfg(not(stm32g4x1))] | 590 | #[cfg(not(stm32g4x1))] |
| 918 | impl VBatChannel for crate::peripherals::ADC5 { | 591 | impl SealedSpecialConverter<Vbat> for crate::peripherals::ADC5 { |
| 919 | const CHANNEL: u8 = 17; | 592 | const CHANNEL: u8 = 17; |
| 920 | } | 593 | } |
| 921 | } | 594 | } |
| @@ -923,13 +596,13 @@ mod g4 { | |||
| 923 | // TODO this should look at each ADC individually and impl the correct channels | 596 | // TODO this should look at each ADC individually and impl the correct channels |
| 924 | #[cfg(stm32h7)] | 597 | #[cfg(stm32h7)] |
| 925 | mod h7 { | 598 | mod h7 { |
| 926 | impl<T: Instance> TemperatureChannel for T { | 599 | impl<T: Instance> SealedSpecialConverter<Temperature> for T { |
| 927 | const CHANNEL: u8 = 18; | 600 | const CHANNEL: u8 = 18; |
| 928 | } | 601 | } |
| 929 | impl<T: Instance> VrefChannel for T { | 602 | impl<T: Instance> SealedSpecialConverter<VrefInt> for T { |
| 930 | const CHANNEL: u8 = 19; | 603 | const CHANNEL: u8 = 19; |
| 931 | } | 604 | } |
| 932 | impl<T: Instance> VBatChannel for T { | 605 | impl<T: Instance> SealedSpecialConverter<Vbat> for T { |
| 933 | // TODO this should be 14 for H7a/b/35 | 606 | // TODO this should be 14 for H7a/b/35 |
| 934 | const CHANNEL: u8 = 17; | 607 | const CHANNEL: u8 = 17; |
| 935 | } | 608 | } |
diff --git a/embassy-stm32/src/adc/injected.rs b/embassy-stm32/src/adc/injected.rs index 0e4fe5847..ccaa5d1b2 100644 --- a/embassy-stm32/src/adc/injected.rs +++ b/embassy-stm32/src/adc/injected.rs | |||
| @@ -5,9 +5,9 @@ use core::sync::atomic::{Ordering, compiler_fence}; | |||
| 5 | use embassy_hal_internal::Peri; | 5 | use embassy_hal_internal::Peri; |
| 6 | 6 | ||
| 7 | use super::{AnyAdcChannel, SampleTime}; | 7 | use super::{AnyAdcChannel, SampleTime}; |
| 8 | use crate::adc::Adc; | ||
| 9 | #[allow(unused_imports)] | 8 | #[allow(unused_imports)] |
| 10 | use crate::adc::Instance; | 9 | use crate::adc::Instance; |
| 10 | use crate::adc::{Adc, AnyInstance}; | ||
| 11 | 11 | ||
| 12 | /// Injected ADC sequence with owned channels. | 12 | /// Injected ADC sequence with owned channels. |
| 13 | pub struct InjectedAdc<T: Instance, const N: usize> { | 13 | pub struct InjectedAdc<T: Instance, const N: usize> { |
| @@ -36,9 +36,9 @@ impl<T: Instance, const N: usize> InjectedAdc<T, N> { | |||
| 36 | } | 36 | } |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | impl<T: Instance, const N: usize> Drop for InjectedAdc<T, N> { | 39 | impl<T: Instance + AnyInstance, const N: usize> Drop for InjectedAdc<T, N> { |
| 40 | fn drop(&mut self) { | 40 | fn drop(&mut self) { |
| 41 | Adc::<T>::teardown_adc(); | 41 | T::stop(); |
| 42 | compiler_fence(Ordering::SeqCst); | 42 | compiler_fence(Ordering::SeqCst); |
| 43 | } | 43 | } |
| 44 | } | 44 | } |
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index ea7341f75..13f8a1544 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs | |||
| @@ -17,6 +17,9 @@ | |||
| 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)] |
| @@ -25,30 +28,27 @@ pub use _version::*; | |||
| 25 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; | 28 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; |
| 26 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] | 29 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] |
| 27 | use embassy_sync::waitqueue::AtomicWaker; | 30 | use embassy_sync::waitqueue::AtomicWaker; |
| 31 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 32 | pub use ringbuffered::RingBufferedAdc; | ||
| 28 | 33 | ||
| 29 | #[cfg(any(adc_u5, adc_wba))] | 34 | #[cfg(any(adc_u5, adc_wba))] |
| 30 | #[path = "adc4.rs"] | 35 | #[path = "adc4.rs"] |
| 31 | pub mod adc4; | 36 | pub mod adc4; |
| 32 | 37 | ||
| 38 | #[allow(unused)] | ||
| 39 | pub(self) use crate::block_for_us as blocking_delay_us; | ||
| 33 | pub use crate::pac::adc::vals; | 40 | pub use crate::pac::adc::vals; |
| 34 | #[cfg(not(any(adc_f1, adc_f3v3)))] | 41 | #[cfg(not(any(adc_f1, adc_f3v3)))] |
| 35 | pub use crate::pac::adc::vals::Res as Resolution; | 42 | pub use crate::pac::adc::vals::Res as Resolution; |
| 36 | pub use crate::pac::adc::vals::SampleTime; | 43 | pub use crate::pac::adc::vals::SampleTime; |
| 37 | use crate::peripherals; | 44 | use crate::peripherals; |
| 38 | 45 | ||
| 39 | #[cfg(not(adc_wba))] | 46 | dma_trait!(RxDma, AnyInstance); |
| 40 | 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 | 47 | ||
| 46 | /// Analog to Digital driver. | 48 | /// Analog to Digital driver. |
| 47 | pub struct Adc<'d, T: Instance> { | 49 | pub struct Adc<'d, T: AnyInstance> { |
| 48 | #[allow(unused)] | 50 | #[allow(unused)] |
| 49 | adc: crate::Peri<'d, T>, | 51 | adc: crate::Peri<'d, T>, |
| 50 | #[cfg(not(any(adc_f3v3, adc_f3v2, adc_wba)))] | ||
| 51 | sample_time: SampleTime, | ||
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] | 54 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] |
| @@ -82,26 +82,294 @@ pub(crate) trait SealedAdcChannel<T> { | |||
| 82 | 82 | ||
| 83 | #[allow(unused)] | 83 | #[allow(unused)] |
| 84 | fn channel(&self) -> u8; | 84 | fn channel(&self) -> u8; |
| 85 | |||
| 86 | #[allow(unused)] | ||
| 87 | fn is_differential(&self) -> bool { | ||
| 88 | false | ||
| 89 | } | ||
| 85 | } | 90 | } |
| 86 | 91 | ||
| 87 | /// Performs a busy-wait delay for a specified number of microseconds. | 92 | // Temporary patch for ADCs that have not implemented the standard iface yet |
| 88 | #[allow(unused)] | 93 | #[cfg(any(adc_v1, adc_l0, adc_f1, adc_f3v1, adc_f3v2, adc_f3v3, adc_v1))] |
| 89 | pub(crate) fn blocking_delay_us(us: u32) { | 94 | trait_set::trait_set! { |
| 90 | cfg_if::cfg_if! { | 95 | pub trait AnyInstance = Instance; |
| 91 | // this does strange things on stm32wlx in low power mode depending on exactly when it's called | 96 | } |
| 92 | // as in sometimes 15 us (1 tick) would take > 20 seconds. | 97 | |
| 93 | if #[cfg(all(feature = "time", all(not(feature = "low-power"), not(stm32wlex))))] { | 98 | #[cfg(any( |
| 94 | let duration = embassy_time::Duration::from_micros(us as u64); | 99 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 |
| 95 | embassy_time::block_for(duration); | 100 | ))] |
| 96 | } else { | 101 | pub trait BasicAnyInstance { |
| 97 | let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64; | 102 | type SampleTime; |
| 98 | let us = us as u64; | 103 | } |
| 99 | let cycles = freq * us / 1_000_000; | 104 | |
| 100 | cortex_m::asm::delay(cycles as u32); | 105 | #[cfg(any( |
| 101 | } | 106 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 |
| 107 | ))] | ||
| 108 | pub(self) trait SealedAnyInstance: BasicAnyInstance { | ||
| 109 | fn enable(); | ||
| 110 | fn start(); | ||
| 111 | fn stop(); | ||
| 112 | fn convert() -> u16; | ||
| 113 | fn configure_dma(conversion_mode: ConversionMode); | ||
| 114 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>); | ||
| 115 | #[allow(dead_code)] | ||
| 116 | fn dr() -> *mut u16; | ||
| 117 | } | ||
| 118 | |||
| 119 | // On chips without ADC4, AnyInstance is an Instance | ||
| 120 | #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_g4, adc_c0))] | ||
| 121 | #[allow(private_bounds)] | ||
| 122 | pub trait AnyInstance: SealedAnyInstance + Instance {} | ||
| 123 | |||
| 124 | // On chips with ADC4, AnyInstance is an Instance or adc4::Instance | ||
| 125 | #[cfg(any(adc_v4, adc_u5, adc_wba))] | ||
| 126 | #[allow(private_bounds)] | ||
| 127 | pub trait AnyInstance: SealedAnyInstance + crate::PeripheralType + crate::rcc::RccPeripheral {} | ||
| 128 | |||
| 129 | // Implement AnyInstance automatically for SealedAnyInstance | ||
| 130 | #[cfg(any( | ||
| 131 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 132 | ))] | ||
| 133 | impl<T: SealedAnyInstance + Instance> BasicAnyInstance for T { | ||
| 134 | type SampleTime = SampleTime; | ||
| 135 | } | ||
| 136 | |||
| 137 | #[cfg(any( | ||
| 138 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 139 | ))] | ||
| 140 | impl<T: SealedAnyInstance + Instance> AnyInstance for T {} | ||
| 141 | |||
| 142 | #[cfg(any(adc_c0, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] | ||
| 143 | /// Number of samples used for averaging. | ||
| 144 | #[derive(Copy, Clone, Debug)] | ||
| 145 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 146 | pub enum Averaging { | ||
| 147 | Disabled, | ||
| 148 | Samples2, | ||
| 149 | Samples4, | ||
| 150 | Samples8, | ||
| 151 | Samples16, | ||
| 152 | Samples32, | ||
| 153 | Samples64, | ||
| 154 | Samples128, | ||
| 155 | Samples256, | ||
| 156 | #[cfg(any(adc_c0, adc_v4, adc_u5))] | ||
| 157 | Samples512, | ||
| 158 | #[cfg(any(adc_c0, adc_v4, adc_u5))] | ||
| 159 | Samples1024, | ||
| 160 | } | ||
| 161 | |||
| 162 | #[cfg(any( | ||
| 163 | adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0 | ||
| 164 | ))] | ||
| 165 | pub(crate) enum ConversionMode { | ||
| 166 | // Should match the cfg on "read" below | ||
| 167 | #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] | ||
| 168 | Singular, | ||
| 169 | // Should match the cfg on "into_ring_buffered" below | ||
| 170 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 171 | Repeated(RegularConversionMode), | ||
| 172 | } | ||
| 173 | |||
| 174 | // Should match the cfg on "into_ring_buffered" below | ||
| 175 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 176 | // Conversion mode for regular ADC channels | ||
| 177 | #[derive(Copy, Clone)] | ||
| 178 | pub enum RegularConversionMode { | ||
| 179 | // Samples as fast as possible | ||
| 180 | Continuous, | ||
| 181 | #[cfg(adc_g4)] | ||
| 182 | // Sample at rate determined by external trigger | ||
| 183 | Triggered(ConversionTrigger), | ||
| 184 | } | ||
| 185 | |||
| 186 | impl<'d, T: AnyInstance> Adc<'d, T> { | ||
| 187 | #[cfg(any( | ||
| 188 | adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v4, adc_wba, adc_c0 | ||
| 189 | ))] | ||
| 190 | /// Read an ADC pin. | ||
| 191 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: T::SampleTime) -> u16 { | ||
| 192 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] | ||
| 193 | channel.setup(); | ||
| 194 | |||
| 195 | #[cfg(not(adc_v4))] | ||
| 196 | T::enable(); | ||
| 197 | T::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); | ||
| 198 | |||
| 199 | T::convert() | ||
| 102 | } | 200 | } |
| 201 | |||
| 202 | #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] | ||
| 203 | /// Read one or multiple ADC regular channels using DMA. | ||
| 204 | /// | ||
| 205 | /// `sequence` iterator and `readings` must have the same length. | ||
| 206 | /// | ||
| 207 | /// Example | ||
| 208 | /// ```rust,ignore | ||
| 209 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 210 | /// | ||
| 211 | /// let mut adc = Adc::new(p.ADC1); | ||
| 212 | /// let mut adc_pin0 = p.PA0.into(); | ||
| 213 | /// let mut adc_pin1 = p.PA1.into(); | ||
| 214 | /// let mut measurements = [0u16; 2]; | ||
| 215 | /// | ||
| 216 | /// adc.read( | ||
| 217 | /// p.DMA1_CH2.reborrow(), | ||
| 218 | /// [ | ||
| 219 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 220 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 221 | /// ] | ||
| 222 | /// .into_iter(), | ||
| 223 | /// &mut measurements, | ||
| 224 | /// ) | ||
| 225 | /// .await; | ||
| 226 | /// defmt::info!("measurements: {}", measurements); | ||
| 227 | /// ``` | ||
| 228 | /// | ||
| 229 | /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use | ||
| 230 | /// `into_ring_buffered`, `into_ring_buffered_and_injected` | ||
| 231 | /// | ||
| 232 | /// In STM32C0, channels bigger than 14 cannot be read using sequencer, so you have to use | ||
| 233 | /// either blocking reads or use the mechanism to read in HW order (CHSELRMOD=0). | ||
| 234 | /// | ||
| 235 | /// In addtion, on STM320, this method will panic if the channels are not passed in order | ||
| 236 | pub async fn read( | ||
| 237 | &mut self, | ||
| 238 | rx_dma: embassy_hal_internal::Peri<'_, impl RxDma<T>>, | ||
| 239 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, T::SampleTime)>, | ||
| 240 | readings: &mut [u16], | ||
| 241 | ) { | ||
| 242 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 243 | assert!( | ||
| 244 | sequence.len() == readings.len(), | ||
| 245 | "Sequence length must be equal to readings length" | ||
| 246 | ); | ||
| 247 | assert!( | ||
| 248 | sequence.len() <= 16, | ||
| 249 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 250 | ); | ||
| 251 | |||
| 252 | // Ensure no conversions are ongoing and ADC is enabled. | ||
| 253 | T::stop(); | ||
| 254 | T::enable(); | ||
| 255 | |||
| 256 | T::configure_sequence( | ||
| 257 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 258 | ); | ||
| 259 | |||
| 260 | T::configure_dma(ConversionMode::Singular); | ||
| 261 | |||
| 262 | let request = rx_dma.request(); | ||
| 263 | let transfer = | ||
| 264 | unsafe { crate::dma::Transfer::new_read(rx_dma, request, T::dr(), readings, Default::default()) }; | ||
| 265 | |||
| 266 | T::start(); | ||
| 267 | |||
| 268 | // Wait for conversion sequence to finish. | ||
| 269 | transfer.await; | ||
| 270 | |||
| 271 | // Ensure conversions are finished. | ||
| 272 | T::stop(); | ||
| 273 | } | ||
| 274 | |||
| 275 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 276 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 277 | /// | ||
| 278 | /// Use the [`read`] method to retrieve measurements from the DMA ring buffer. The read buffer | ||
| 279 | /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended | ||
| 280 | /// to configure `dma_buf` as a double buffer so that one half can be read while the other half | ||
| 281 | /// is being filled by the DMA, preventing data loss. The trigger period of the ADC effectively | ||
| 282 | /// defines the period at which the buffer should be read. | ||
| 283 | /// | ||
| 284 | /// If continous conversion mode is selected, the provided `dma_buf` must be large enough to prevent | ||
| 285 | /// DMA buffer overruns. Its length should be a multiple of the number of ADC channels being measured. | ||
| 286 | /// For example, if 3 channels are measured and you want to store 40 samples per channel, | ||
| 287 | /// the buffer length should be `3 * 40 = 120`. | ||
| 288 | /// | ||
| 289 | /// # Parameters | ||
| 290 | /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer. | ||
| 291 | /// - `dma_buf`: The buffer where DMA stores ADC samples. | ||
| 292 | /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions. | ||
| 293 | /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered). | ||
| 294 | /// | ||
| 295 | /// # Returns | ||
| 296 | /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. | ||
| 297 | pub fn into_ring_buffered<'a>( | ||
| 298 | self, | ||
| 299 | dma: embassy_hal_internal::Peri<'a, impl RxDma<T>>, | ||
| 300 | dma_buf: &'a mut [u16], | ||
| 301 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, T::SampleTime)>, | ||
| 302 | mode: RegularConversionMode, | ||
| 303 | ) -> RingBufferedAdc<'a, T> { | ||
| 304 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 305 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 306 | assert!( | ||
| 307 | sequence.len() <= 16, | ||
| 308 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 309 | ); | ||
| 310 | // reset conversions and enable the adc | ||
| 311 | T::stop(); | ||
| 312 | T::enable(); | ||
| 313 | |||
| 314 | //adc side setup | ||
| 315 | T::configure_sequence( | ||
| 316 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 317 | ); | ||
| 318 | |||
| 319 | T::configure_dma(ConversionMode::Repeated(mode)); | ||
| 320 | |||
| 321 | core::mem::forget(self); | ||
| 322 | |||
| 323 | RingBufferedAdc::new(dma, dma_buf) | ||
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 327 | pub(self) trait SpecialChannel {} | ||
| 328 | |||
| 329 | /// Implemented for ADCs that have a special channel | ||
| 330 | trait SealedSpecialConverter<T: SpecialChannel + Sized> { | ||
| 331 | const CHANNEL: u8; | ||
| 103 | } | 332 | } |
| 104 | 333 | ||
| 334 | #[allow(private_bounds)] | ||
| 335 | pub trait SpecialConverter<T: SpecialChannel + Sized>: SealedSpecialConverter<T> {} | ||
| 336 | |||
| 337 | impl<C: SpecialChannel + Sized, T: SealedSpecialConverter<C>> SpecialConverter<C> for T {} | ||
| 338 | |||
| 339 | impl<C: SpecialChannel, T: Instance + SealedSpecialConverter<C>> AdcChannel<T> for C {} | ||
| 340 | impl<C: SpecialChannel, T: Instance + SealedSpecialConverter<C>> SealedAdcChannel<T> for C { | ||
| 341 | fn channel(&self) -> u8 { | ||
| 342 | T::CHANNEL | ||
| 343 | } | ||
| 344 | } | ||
| 345 | |||
| 346 | pub struct VrefInt; | ||
| 347 | impl SpecialChannel for VrefInt {} | ||
| 348 | |||
| 349 | impl VrefInt { | ||
| 350 | #[cfg(any(adc_f3v1, adc_f3v2))] | ||
| 351 | /// The value that vref would be if vdda was at 3300mv | ||
| 352 | pub fn calibrated_value(&self) -> u16 { | ||
| 353 | crate::pac::VREFINTCAL.data().read() | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | /// Internal temperature channel. | ||
| 358 | pub struct Temperature; | ||
| 359 | impl SpecialChannel for Temperature {} | ||
| 360 | |||
| 361 | /// Internal battery voltage channel. | ||
| 362 | pub struct Vbat; | ||
| 363 | impl SpecialChannel for Vbat {} | ||
| 364 | |||
| 365 | /// Vcore channel. | ||
| 366 | pub struct Vcore; | ||
| 367 | impl SpecialChannel for Vcore {} | ||
| 368 | |||
| 369 | /// Internal dac channel. | ||
| 370 | pub struct Dac; | ||
| 371 | impl SpecialChannel for Dac {} | ||
| 372 | |||
| 105 | /// ADC instance. | 373 | /// ADC instance. |
| 106 | #[cfg(not(any( | 374 | #[cfg(not(any( |
| 107 | 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, | 375 | 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, |
| @@ -131,6 +399,7 @@ pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized { | |||
| 131 | 399 | ||
| 132 | AnyAdcChannel { | 400 | AnyAdcChannel { |
| 133 | channel: self.channel(), | 401 | channel: self.channel(), |
| 402 | is_differential: self.is_differential(), | ||
| 134 | _phantom: PhantomData, | 403 | _phantom: PhantomData, |
| 135 | } | 404 | } |
| 136 | } | 405 | } |
| @@ -142,6 +411,7 @@ pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized { | |||
| 142 | /// storing them in an array. | 411 | /// storing them in an array. |
| 143 | pub struct AnyAdcChannel<T> { | 412 | pub struct AnyAdcChannel<T> { |
| 144 | channel: u8, | 413 | channel: u8, |
| 414 | is_differential: bool, | ||
| 145 | _phantom: PhantomData<T>, | 415 | _phantom: PhantomData<T>, |
| 146 | } | 416 | } |
| 147 | impl_peripheral!(AnyAdcChannel<T: Instance>); | 417 | impl_peripheral!(AnyAdcChannel<T: Instance>); |
| @@ -150,6 +420,10 @@ impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<T> { | |||
| 150 | fn channel(&self) -> u8 { | 420 | fn channel(&self) -> u8 { |
| 151 | self.channel | 421 | self.channel |
| 152 | } | 422 | } |
| 423 | |||
| 424 | fn is_differential(&self) -> bool { | ||
| 425 | self.is_differential | ||
| 426 | } | ||
| 153 | } | 427 | } |
| 154 | 428 | ||
| 155 | impl<T> AnyAdcChannel<T> { | 429 | impl<T> AnyAdcChannel<T> { |
| @@ -268,6 +542,39 @@ macro_rules! impl_adc_pin { | |||
| 268 | }; | 542 | }; |
| 269 | } | 543 | } |
| 270 | 544 | ||
| 545 | #[allow(unused_macros)] | ||
| 546 | macro_rules! impl_adc_pair { | ||
| 547 | ($inst:ident, $pin:ident, $npin:ident, $ch:expr) => { | ||
| 548 | impl crate::adc::AdcChannel<peripherals::$inst> | ||
| 549 | for ( | ||
| 550 | crate::Peri<'_, crate::peripherals::$pin>, | ||
| 551 | crate::Peri<'_, crate::peripherals::$npin>, | ||
| 552 | ) | ||
| 553 | { | ||
| 554 | } | ||
| 555 | impl crate::adc::SealedAdcChannel<peripherals::$inst> | ||
| 556 | for ( | ||
| 557 | crate::Peri<'_, crate::peripherals::$pin>, | ||
| 558 | crate::Peri<'_, crate::peripherals::$npin>, | ||
| 559 | ) | ||
| 560 | { | ||
| 561 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] | ||
| 562 | fn setup(&mut self) { | ||
| 563 | <crate::peripherals::$pin as crate::gpio::SealedPin>::set_as_analog(&mut self.0); | ||
| 564 | <crate::peripherals::$npin as crate::gpio::SealedPin>::set_as_analog(&mut self.1); | ||
| 565 | } | ||
| 566 | |||
| 567 | fn channel(&self) -> u8 { | ||
| 568 | $ch | ||
| 569 | } | ||
| 570 | |||
| 571 | fn is_differential(&self) -> bool { | ||
| 572 | true | ||
| 573 | } | ||
| 574 | } | ||
| 575 | }; | ||
| 576 | } | ||
| 577 | |||
| 271 | /// Get the maximum reading value for this resolution. | 578 | /// Get the maximum reading value for this resolution. |
| 272 | /// | 579 | /// |
| 273 | /// This is `2**n - 1`. | 580 | /// This is `2**n - 1`. |
diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs index 971c8195c..a56f8ca0b 100644 --- a/embassy-stm32/src/adc/ringbuffered.rs +++ b/embassy-stm32/src/adc/ringbuffered.rs | |||
| @@ -4,7 +4,7 @@ use core::sync::atomic::{Ordering, compiler_fence}; | |||
| 4 | #[allow(unused_imports)] | 4 | #[allow(unused_imports)] |
| 5 | use embassy_hal_internal::Peri; | 5 | use embassy_hal_internal::Peri; |
| 6 | 6 | ||
| 7 | use crate::adc::Adc; | 7 | use crate::adc::AnyInstance; |
| 8 | #[allow(unused_imports)] | 8 | #[allow(unused_imports)] |
| 9 | use crate::adc::{Instance, RxDma}; | 9 | use crate::adc::{Instance, RxDma}; |
| 10 | #[allow(unused_imports)] | 10 | #[allow(unused_imports)] |
| @@ -19,7 +19,7 @@ pub struct RingBufferedAdc<'d, T: Instance> { | |||
| 19 | ring_buf: ReadableRingBuffer<'d, u16>, | 19 | ring_buf: ReadableRingBuffer<'d, u16>, |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | impl<'d, T: Instance> RingBufferedAdc<'d, T> { | 22 | impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> { |
| 23 | pub(crate) fn new(dma: Peri<'d, impl RxDma<T>>, dma_buf: &'d mut [u16]) -> Self { | 23 | pub(crate) fn new(dma: Peri<'d, impl RxDma<T>>, dma_buf: &'d mut [u16]) -> Self { |
| 24 | //dma side setup | 24 | //dma side setup |
| 25 | let opts = TransferOptions { | 25 | let opts = TransferOptions { |
| @@ -45,11 +45,11 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { | |||
| 45 | compiler_fence(Ordering::SeqCst); | 45 | compiler_fence(Ordering::SeqCst); |
| 46 | self.ring_buf.start(); | 46 | self.ring_buf.start(); |
| 47 | 47 | ||
| 48 | Adc::<T>::start(); | 48 | T::start(); |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | pub fn stop(&mut self) { | 51 | pub fn stop(&mut self) { |
| 52 | Adc::<T>::stop(); | 52 | T::stop(); |
| 53 | 53 | ||
| 54 | self.ring_buf.request_pause(); | 54 | self.ring_buf.request_pause(); |
| 55 | 55 | ||
| @@ -170,9 +170,9 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { | |||
| 170 | } | 170 | } |
| 171 | } | 171 | } |
| 172 | 172 | ||
| 173 | impl<T: Instance> Drop for RingBufferedAdc<'_, T> { | 173 | impl<T: Instance + AnyInstance> Drop for RingBufferedAdc<'_, T> { |
| 174 | fn drop(&mut self) { | 174 | fn drop(&mut self) { |
| 175 | Adc::<T>::teardown_adc(); | 175 | T::stop(); |
| 176 | 176 | ||
| 177 | compiler_fence(Ordering::SeqCst); | 177 | compiler_fence(Ordering::SeqCst); |
| 178 | 178 | ||
diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index a5869d110..58c30935f 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs | |||
| @@ -5,10 +5,11 @@ 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; | ||
| 12 | use crate::{Peri, interrupt, rcc}; | 13 | use crate::{Peri, interrupt, rcc}; |
| 13 | 14 | ||
| 14 | mod watchdog_v1; | 15 | mod watchdog_v1; |
| @@ -42,32 +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) { 18 } else { 16 } | ||
| 70 | } | ||
| 71 | } | 63 | } |
| 72 | 64 | ||
| 73 | impl<'d, T: Instance> Adc<'d, T> { | 65 | impl<'d, T: Instance> Adc<'d, T> { |
| @@ -114,10 +106,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 114 | T::Interrupt::enable(); | 106 | T::Interrupt::enable(); |
| 115 | } | 107 | } |
| 116 | 108 | ||
| 117 | Self { | 109 | Self { adc } |
| 118 | adc, | ||
| 119 | sample_time: SampleTime::from_bits(0), | ||
| 120 | } | ||
| 121 | } | 110 | } |
| 122 | 111 | ||
| 123 | #[cfg(not(adc_l0))] | 112 | #[cfg(not(adc_l0))] |
| @@ -130,12 +119,12 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 130 | Vbat | 119 | Vbat |
| 131 | } | 120 | } |
| 132 | 121 | ||
| 133 | pub fn enable_vref(&self) -> Vref { | 122 | pub fn enable_vref(&self) -> VrefInt { |
| 134 | // Table 28. Embedded internal reference voltage | 123 | // Table 28. Embedded internal reference voltage |
| 135 | // tstart = 10μs | 124 | // tstart = 10μs |
| 136 | T::regs().ccr().modify(|reg| reg.set_vrefen(true)); | 125 | T::regs().ccr().modify(|reg| reg.set_vrefen(true)); |
| 137 | blocking_delay_us(10); | 126 | blocking_delay_us(10); |
| 138 | Vref | 127 | VrefInt |
| 139 | } | 128 | } |
| 140 | 129 | ||
| 141 | pub fn enable_temperature(&self) -> Temperature { | 130 | pub fn enable_temperature(&self) -> Temperature { |
| @@ -149,10 +138,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 149 | Temperature | 138 | Temperature |
| 150 | } | 139 | } |
| 151 | 140 | ||
| 152 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 153 | self.sample_time = sample_time; | ||
| 154 | } | ||
| 155 | |||
| 156 | pub fn set_resolution(&mut self, resolution: Resolution) { | 141 | pub fn set_resolution(&mut self, resolution: Resolution) { |
| 157 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); | 142 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); |
| 158 | } | 143 | } |
| @@ -163,12 +148,13 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 163 | T::regs().cfgr2().modify(|reg| reg.set_ckmode(ckmode)); | 148 | T::regs().cfgr2().modify(|reg| reg.set_ckmode(ckmode)); |
| 164 | } | 149 | } |
| 165 | 150 | ||
| 166 | 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 { |
| 167 | let ch_num = channel.channel(); | 152 | let ch_num = channel.channel(); |
| 168 | channel.setup(); | 153 | channel.setup(); |
| 169 | 154 | ||
| 170 | // A.7.5 Single conversion sequence code example - Software trigger | 155 | // A.7.5 Single conversion sequence code example - Software trigger |
| 171 | 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())); | ||
| 172 | 158 | ||
| 173 | self.convert().await | 159 | self.convert().await |
| 174 | } | 160 | } |
| @@ -179,7 +165,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 179 | reg.set_eosmp(true); | 165 | reg.set_eosmp(true); |
| 180 | }); | 166 | }); |
| 181 | 167 | ||
| 182 | T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.into())); | ||
| 183 | T::regs().ier().modify(|w| w.set_eocie(true)); | 168 | T::regs().ier().modify(|w| w.set_eocie(true)); |
| 184 | T::regs().cr().modify(|reg| reg.set_adstart(true)); | 169 | T::regs().cr().modify(|reg| reg.set_adstart(true)); |
| 185 | 170 | ||
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 90c6294d2..07eaebf7c 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs | |||
| @@ -1,16 +1,12 @@ | |||
| 1 | use core::mem; | ||
| 2 | use core::sync::atomic::{Ordering, compiler_fence}; | 1 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 3 | 2 | ||
| 4 | use super::blocking_delay_us; | 3 | use super::{ConversionMode, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 5 | use crate::adc::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel}; | 4 | use crate::adc::{Adc, Instance, Resolution, SampleTime}; |
| 6 | use crate::pac::adc::vals; | 5 | use crate::pac::adc::vals; |
| 7 | use crate::peripherals::ADC1; | 6 | pub use crate::pac::adccommon::vals::Adcpre; |
| 8 | use crate::time::Hertz; | 7 | use crate::time::Hertz; |
| 9 | use crate::{Peri, rcc}; | 8 | use crate::{Peri, rcc}; |
| 10 | 9 | ||
| 11 | mod ringbuffered; | ||
| 12 | pub use ringbuffered::RingBufferedAdc; | ||
| 13 | |||
| 14 | fn clear_interrupt_flags(r: crate::pac::adc::Adc) { | 10 | fn clear_interrupt_flags(r: crate::pac::adc::Adc) { |
| 15 | r.sr().modify(|regs| { | 11 | r.sr().modify(|regs| { |
| 16 | regs.set_eoc(false); | 12 | regs.set_eoc(false); |
| @@ -23,12 +19,22 @@ pub const VREF_DEFAULT_MV: u32 = 3300; | |||
| 23 | /// VREF voltage used for factory calibration of VREFINTCAL register. | 19 | /// VREF voltage used for factory calibration of VREFINTCAL register. |
| 24 | pub const VREF_CALIB_MV: u32 = 3300; | 20 | pub const VREF_CALIB_MV: u32 = 3300; |
| 25 | 21 | ||
| 26 | pub struct VrefInt; | 22 | impl super::SealedSpecialConverter<super::VrefInt> for crate::peripherals::ADC1 { |
| 27 | impl AdcChannel<ADC1> for VrefInt {} | 23 | const CHANNEL: u8 = 17; |
| 28 | impl super::SealedAdcChannel<ADC1> for VrefInt { | 24 | } |
| 29 | fn channel(&self) -> u8 { | 25 | |
| 30 | 17 | 26 | #[cfg(any(stm32f2, stm32f40x, stm32f41x))] |
| 31 | } | 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; | ||
| 32 | } | 38 | } |
| 33 | 39 | ||
| 34 | impl VrefInt { | 40 | impl VrefInt { |
| @@ -38,20 +44,6 @@ impl VrefInt { | |||
| 38 | } | 44 | } |
| 39 | } | 45 | } |
| 40 | 46 | ||
| 41 | pub struct Temperature; | ||
| 42 | impl AdcChannel<ADC1> for Temperature {} | ||
| 43 | impl super::SealedAdcChannel<ADC1> for Temperature { | ||
| 44 | fn channel(&self) -> u8 { | ||
| 45 | cfg_if::cfg_if! { | ||
| 46 | if #[cfg(any(stm32f2, stm32f40x, stm32f41x))] { | ||
| 47 | 16 | ||
| 48 | } else { | ||
| 49 | 18 | ||
| 50 | } | ||
| 51 | } | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | impl Temperature { | 47 | impl Temperature { |
| 56 | /// Time needed for temperature sensor readings to stabilize | 48 | /// Time needed for temperature sensor readings to stabilize |
| 57 | pub fn start_time_us() -> u32 { | 49 | pub fn start_time_us() -> u32 { |
| @@ -59,168 +51,175 @@ impl Temperature { | |||
| 59 | } | 51 | } |
| 60 | } | 52 | } |
| 61 | 53 | ||
| 62 | pub struct Vbat; | 54 | fn from_pclk2(freq: Hertz) -> Adcpre { |
| 63 | 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). |
| 64 | impl super::SealedAdcChannel<ADC1> for Vbat { | 56 | #[cfg(stm32f2)] |
| 65 | fn channel(&self) -> u8 { | 57 | const MAX_FREQUENCY: Hertz = Hertz(30_000_000); |
| 66 | 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 = 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."), | ||
| 67 | } | 68 | } |
| 68 | } | 69 | } |
| 69 | 70 | ||
| 70 | enum Prescaler { | 71 | /// ADC configuration |
| 71 | Div2, | 72 | #[derive(Default)] |
| 72 | Div4, | 73 | pub struct AdcConfig { |
| 73 | Div6, | 74 | resolution: Option<Resolution>, |
| 74 | Div8, | ||
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | impl Prescaler { | 77 | impl<T: Instance> super::SealedAnyInstance for T { |
| 78 | fn from_pclk2(freq: Hertz) -> Self { | 78 | fn dr() -> *mut u16 { |
| 79 | // Datasheet for F2 specifies min frequency 0.6 MHz, and max 30 MHz (with VDDA 2.4-3.6V). | 79 | T::regs().dr().as_ptr() as *mut u16 |
| 80 | #[cfg(stm32f2)] | ||
| 81 | const MAX_FREQUENCY: Hertz = Hertz(30_000_000); | ||
| 82 | // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. | ||
| 83 | #[cfg(not(stm32f2))] | ||
| 84 | const MAX_FREQUENCY: Hertz = Hertz(36_000_000); | ||
| 85 | let raw_div = freq.0 / MAX_FREQUENCY.0; | ||
| 86 | match raw_div { | ||
| 87 | 0..=1 => Self::Div2, | ||
| 88 | 2..=3 => Self::Div4, | ||
| 89 | 4..=5 => Self::Div6, | ||
| 90 | 6..=7 => Self::Div8, | ||
| 91 | _ => panic!("Selected PCLK2 frequency is too high for ADC with largest possible prescaler."), | ||
| 92 | } | ||
| 93 | } | 80 | } |
| 94 | 81 | ||
| 95 | fn adcpre(&self) -> crate::pac::adccommon::vals::Adcpre { | 82 | fn enable() {} |
| 96 | match self { | ||
| 97 | Prescaler::Div2 => crate::pac::adccommon::vals::Adcpre::DIV2, | ||
| 98 | Prescaler::Div4 => crate::pac::adccommon::vals::Adcpre::DIV4, | ||
| 99 | Prescaler::Div6 => crate::pac::adccommon::vals::Adcpre::DIV6, | ||
| 100 | Prescaler::Div8 => crate::pac::adccommon::vals::Adcpre::DIV8, | ||
| 101 | } | ||
| 102 | } | ||
| 103 | } | ||
| 104 | 83 | ||
| 105 | impl<'d, T> Adc<'d, T> | 84 | fn start() { |
| 106 | where | 85 | // Begin ADC conversions |
| 107 | T: Instance, | ||
| 108 | { | ||
| 109 | pub fn new(adc: Peri<'d, T>) -> Self { | ||
| 110 | rcc::enable_and_reset::<T>(); | ||
| 111 | |||
| 112 | let presc = Prescaler::from_pclk2(T::frequency()); | ||
| 113 | T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); | ||
| 114 | T::regs().cr2().modify(|reg| { | 86 | T::regs().cr2().modify(|reg| { |
| 115 | reg.set_adon(true); | 87 | reg.set_adon(true); |
| 88 | reg.set_swstart(true); | ||
| 116 | }); | 89 | }); |
| 117 | |||
| 118 | blocking_delay_us(3); | ||
| 119 | |||
| 120 | Self { | ||
| 121 | adc, | ||
| 122 | sample_time: SampleTime::from_bits(0), | ||
| 123 | } | ||
| 124 | } | 90 | } |
| 125 | 91 | ||
| 126 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | 92 | fn stop() { |
| 127 | /// | 93 | let r = T::regs(); |
| 128 | /// The `dma_buf` should be large enough to prevent DMA buffer overrun. | ||
| 129 | /// The length of the `dma_buf` should be a multiple of the ADC channel count. | ||
| 130 | /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. | ||
| 131 | /// | ||
| 132 | /// `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. | ||
| 133 | /// It is critical to call `read` frequently to prevent DMA buffer overrun. | ||
| 134 | /// | ||
| 135 | /// [`read`]: #method.read | ||
| 136 | pub fn into_ring_buffered<'a>( | ||
| 137 | self, | ||
| 138 | dma: Peri<'d, impl RxDma<T>>, | ||
| 139 | dma_buf: &'d mut [u16], | ||
| 140 | sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>, | ||
| 141 | ) -> RingBufferedAdc<'d, T> { | ||
| 142 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 143 | 94 | ||
| 144 | T::regs().cr2().modify(|reg| { | 95 | // Stop ADC |
| 145 | reg.set_adon(true); | 96 | r.cr2().modify(|reg| { |
| 97 | // Stop ADC | ||
| 98 | reg.set_swstart(false); | ||
| 99 | // Stop ADC | ||
| 100 | reg.set_adon(false); | ||
| 101 | // Stop DMA | ||
| 102 | reg.set_dma(false); | ||
| 146 | }); | 103 | }); |
| 147 | 104 | ||
| 148 | // Check the sequence is long enough | 105 | r.cr1().modify(|w| { |
| 149 | T::regs().sqr1().modify(|r| { | 106 | // Disable interrupt for end of conversion |
| 150 | r.set_l((sequence.len() - 1).try_into().unwrap()); | 107 | w.set_eocie(false); |
| 108 | // Disable interrupt for overrun | ||
| 109 | w.set_ovrie(false); | ||
| 151 | }); | 110 | }); |
| 152 | 111 | ||
| 153 | for (i, (channel, sample_time)) in sequence.enumerate() { | 112 | clear_interrupt_flags(r); |
| 154 | // Set this GPIO as an analog input. | ||
| 155 | channel.setup(); | ||
| 156 | |||
| 157 | // Set the channel in the right sequence field. | ||
| 158 | T::regs().sqr3().modify(|w| w.set_sq(i, channel.channel())); | ||
| 159 | |||
| 160 | Self::set_channel_sample_time(channel.channel(), sample_time); | ||
| 161 | } | ||
| 162 | 113 | ||
| 163 | compiler_fence(Ordering::SeqCst); | 114 | compiler_fence(Ordering::SeqCst); |
| 115 | } | ||
| 164 | 116 | ||
| 165 | let r = T::regs(); | 117 | fn convert() -> u16 { |
| 166 | 118 | // clear end of conversion flag | |
| 167 | // Clear all interrupts | 119 | T::regs().sr().modify(|reg| { |
| 168 | r.sr().modify(|regs| { | 120 | reg.set_eoc(false); |
| 169 | regs.set_eoc(false); | ||
| 170 | regs.set_ovr(false); | ||
| 171 | regs.set_strt(false); | ||
| 172 | }); | 121 | }); |
| 173 | 122 | ||
| 174 | r.cr1().modify(|w| { | 123 | // Start conversion |
| 175 | // Enable interrupt for end of conversion | 124 | T::regs().cr2().modify(|reg| { |
| 176 | w.set_eocie(true); | 125 | reg.set_swstart(true); |
| 177 | // Enable interrupt for overrun | ||
| 178 | w.set_ovrie(true); | ||
| 179 | // Scanning converisons of multiple channels | ||
| 180 | w.set_scan(true); | ||
| 181 | // Continuous conversion mode | ||
| 182 | w.set_discen(false); | ||
| 183 | }); | 126 | }); |
| 184 | 127 | ||
| 185 | r.cr2().modify(|w| { | 128 | while T::regs().sr().read().strt() == false { |
| 186 | // Enable DMA mode | 129 | // spin //wait for actual start |
| 187 | w.set_dma(true); | 130 | } |
| 188 | // Enable continuous conversions | 131 | while T::regs().sr().read().eoc() == false { |
| 189 | w.set_cont(true); | 132 | // spin //wait for finish |
| 190 | // DMA requests are issues as long as DMA=1 and data are converted. | 133 | } |
| 191 | w.set_dds(vals::Dds::CONTINUOUS); | ||
| 192 | // EOC flag is set at the end of each conversion. | ||
| 193 | w.set_eocs(vals::Eocs::EACH_CONVERSION); | ||
| 194 | }); | ||
| 195 | 134 | ||
| 196 | // Don't disable the clock | 135 | T::regs().dr().read().0 as u16 |
| 197 | mem::forget(self); | 136 | } |
| 198 | 137 | ||
| 199 | RingBufferedAdc::new(dma, dma_buf) | 138 | fn configure_dma(conversion_mode: ConversionMode) { |
| 139 | match conversion_mode { | ||
| 140 | ConversionMode::Repeated(_) => { | ||
| 141 | let r = T::regs(); | ||
| 142 | |||
| 143 | // Clear all interrupts | ||
| 144 | r.sr().modify(|regs| { | ||
| 145 | regs.set_eoc(false); | ||
| 146 | regs.set_ovr(false); | ||
| 147 | regs.set_strt(false); | ||
| 148 | }); | ||
| 149 | |||
| 150 | r.cr1().modify(|w| { | ||
| 151 | // Enable interrupt for end of conversion | ||
| 152 | w.set_eocie(true); | ||
| 153 | // Enable interrupt for overrun | ||
| 154 | w.set_ovrie(true); | ||
| 155 | // Scanning converisons of multiple channels | ||
| 156 | w.set_scan(true); | ||
| 157 | // Continuous conversion mode | ||
| 158 | w.set_discen(false); | ||
| 159 | }); | ||
| 160 | |||
| 161 | r.cr2().modify(|w| { | ||
| 162 | // Enable DMA mode | ||
| 163 | w.set_dma(true); | ||
| 164 | // Enable continuous conversions | ||
| 165 | w.set_cont(true); | ||
| 166 | // DMA requests are issues as long as DMA=1 and data are converted. | ||
| 167 | w.set_dds(vals::Dds::CONTINUOUS); | ||
| 168 | // EOC flag is set at the end of each conversion. | ||
| 169 | w.set_eocs(vals::Eocs::EACH_CONVERSION); | ||
| 170 | }); | ||
| 171 | } | ||
| 172 | } | ||
| 200 | } | 173 | } |
| 201 | 174 | ||
| 202 | pub(super) fn start() { | 175 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| 203 | // Begin ADC conversions | ||
| 204 | T::regs().cr2().modify(|reg| { | 176 | T::regs().cr2().modify(|reg| { |
| 205 | reg.set_adon(true); | 177 | reg.set_adon(true); |
| 206 | reg.set_swstart(true); | ||
| 207 | }); | 178 | }); |
| 208 | } | ||
| 209 | 179 | ||
| 210 | pub(super) fn stop() { | 180 | // Check the sequence is long enough |
| 211 | // Stop ADC | 181 | T::regs().sqr1().modify(|r| { |
| 212 | T::regs().cr2().modify(|reg| { | 182 | r.set_l((sequence.len() - 1).try_into().unwrap()); |
| 213 | // Stop ADC | ||
| 214 | reg.set_swstart(false); | ||
| 215 | }); | 183 | }); |
| 184 | |||
| 185 | for (i, ((ch, _), sample_time)) in sequence.enumerate() { | ||
| 186 | // Set the channel in the right sequence field. | ||
| 187 | T::regs().sqr3().modify(|w| w.set_sq(i, ch)); | ||
| 188 | |||
| 189 | let sample_time = sample_time.into(); | ||
| 190 | if ch <= 9 { | ||
| 191 | T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 192 | } else { | ||
| 193 | T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 194 | } | ||
| 195 | } | ||
| 216 | } | 196 | } |
| 197 | } | ||
| 217 | 198 | ||
| 218 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | 199 | impl<'d, T> Adc<'d, T> |
| 219 | self.sample_time = sample_time; | 200 | where |
| 201 | T: Instance, | ||
| 202 | { | ||
| 203 | pub fn new(adc: Peri<'d, T>) -> Self { | ||
| 204 | Self::new_with_config(adc, Default::default()) | ||
| 220 | } | 205 | } |
| 221 | 206 | ||
| 222 | pub fn set_resolution(&mut self, resolution: Resolution) { | 207 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { |
| 223 | T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); | 208 | rcc::enable_and_reset::<T>(); |
| 209 | |||
| 210 | let presc = from_pclk2(T::frequency()); | ||
| 211 | T::common_regs().ccr().modify(|w| w.set_adcpre(presc)); | ||
| 212 | T::regs().cr2().modify(|reg| { | ||
| 213 | reg.set_adon(true); | ||
| 214 | }); | ||
| 215 | |||
| 216 | blocking_delay_us(3); | ||
| 217 | |||
| 218 | if let Some(resolution) = config.resolution { | ||
| 219 | T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); | ||
| 220 | } | ||
| 221 | |||
| 222 | Self { adc } | ||
| 224 | } | 223 | } |
| 225 | 224 | ||
| 226 | /// Enables internal voltage reference and returns [VrefInt], which can be used in | 225 | /// Enables internal voltage reference and returns [VrefInt], which can be used in |
| @@ -255,77 +254,6 @@ where | |||
| 255 | 254 | ||
| 256 | Vbat {} | 255 | Vbat {} |
| 257 | } | 256 | } |
| 258 | |||
| 259 | /// Perform a single conversion. | ||
| 260 | fn convert(&mut self) -> u16 { | ||
| 261 | // clear end of conversion flag | ||
| 262 | T::regs().sr().modify(|reg| { | ||
| 263 | reg.set_eoc(false); | ||
| 264 | }); | ||
| 265 | |||
| 266 | // Start conversion | ||
| 267 | T::regs().cr2().modify(|reg| { | ||
| 268 | reg.set_swstart(true); | ||
| 269 | }); | ||
| 270 | |||
| 271 | while T::regs().sr().read().strt() == false { | ||
| 272 | // spin //wait for actual start | ||
| 273 | } | ||
| 274 | while T::regs().sr().read().eoc() == false { | ||
| 275 | // spin //wait for finish | ||
| 276 | } | ||
| 277 | |||
| 278 | T::regs().dr().read().0 as u16 | ||
| 279 | } | ||
| 280 | |||
| 281 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 282 | channel.setup(); | ||
| 283 | |||
| 284 | // Configure ADC | ||
| 285 | let channel = channel.channel(); | ||
| 286 | |||
| 287 | // Select channel | ||
| 288 | T::regs().sqr3().write(|reg| reg.set_sq(0, channel)); | ||
| 289 | |||
| 290 | // Configure channel | ||
| 291 | Self::set_channel_sample_time(channel, self.sample_time); | ||
| 292 | |||
| 293 | self.convert() | ||
| 294 | } | ||
| 295 | |||
| 296 | fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | ||
| 297 | let sample_time = sample_time.into(); | ||
| 298 | if ch <= 9 { | ||
| 299 | T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 300 | } else { | ||
| 301 | T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | pub(super) fn teardown_adc() { | ||
| 306 | let r = T::regs(); | ||
| 307 | |||
| 308 | // Stop ADC | ||
| 309 | r.cr2().modify(|reg| { | ||
| 310 | // Stop ADC | ||
| 311 | reg.set_swstart(false); | ||
| 312 | // Stop ADC | ||
| 313 | reg.set_adon(false); | ||
| 314 | // Stop DMA | ||
| 315 | reg.set_dma(false); | ||
| 316 | }); | ||
| 317 | |||
| 318 | r.cr1().modify(|w| { | ||
| 319 | // Disable interrupt for end of conversion | ||
| 320 | w.set_eocie(false); | ||
| 321 | // Disable interrupt for overrun | ||
| 322 | w.set_ovrie(false); | ||
| 323 | }); | ||
| 324 | |||
| 325 | clear_interrupt_flags(r); | ||
| 326 | |||
| 327 | compiler_fence(Ordering::SeqCst); | ||
| 328 | } | ||
| 329 | } | 257 | } |
| 330 | 258 | ||
| 331 | impl<'d, T: Instance> Drop for Adc<'d, T> { | 259 | impl<'d, T: Instance> Drop for Adc<'d, T> { |
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 170b08a25..81eb1e3ee 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs | |||
| @@ -1,25 +1,18 @@ | |||
| 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 | Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, blocking_delay_us, | 13 | use super::SealedAdcChannel; |
| 14 | }; | 14 | use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 15 | 15 | use crate::adc::ConversionMode; | |
| 16 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 17 | mod ringbuffered; | ||
| 18 | |||
| 19 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 20 | use ringbuffered::RingBufferedAdc; | ||
| 21 | |||
| 22 | use crate::dma::Transfer; | ||
| 23 | use crate::{Peri, pac, rcc}; | 16 | use crate::{Peri, pac, rcc}; |
| 24 | 17 | ||
| 25 | /// Default VREF voltage used for sample conversion to millivolts. | 18 | /// Default VREF voltage used for sample conversion to millivolts. |
| @@ -32,70 +25,64 @@ pub const VREF_CALIB_MV: u32 = 3000; | |||
| 32 | // 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 |
| 33 | const SAMPLE_TIMES_CAPACITY: usize = 2; | 26 | const SAMPLE_TIMES_CAPACITY: usize = 2; |
| 34 | 27 | ||
| 35 | pub struct VrefInt; | 28 | #[cfg(adc_g0)] |
| 36 | impl<T: Instance> AdcChannel<T> for VrefInt {} | 29 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 37 | impl<T: Instance> SealedAdcChannel<T> for VrefInt { | 30 | const CHANNEL: u8 = 13; |
| 38 | fn channel(&self) -> u8 { | 31 | } |
| 39 | cfg_if! { | 32 | #[cfg(any(adc_h5, adc_h7rs))] |
| 40 | if #[cfg(adc_g0)] { | 33 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 41 | let val = 13; | 34 | const CHANNEL: u8 = 17; |
| 42 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 35 | } |
| 43 | let val = 17; | 36 | #[cfg(adc_u0)] |
| 44 | } else if #[cfg(adc_u0)] { | 37 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 45 | let val = 12; | 38 | const CHANNEL: u8 = 12; |
| 46 | } else { | 39 | } |
| 47 | let val = 0; | 40 | #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] |
| 48 | } | 41 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 49 | } | 42 | const CHANNEL: u8 = 0; |
| 50 | val | ||
| 51 | } | ||
| 52 | } | 43 | } |
| 53 | 44 | ||
| 54 | pub struct Temperature; | 45 | #[cfg(adc_g0)] |
| 55 | impl<T: Instance> AdcChannel<T> for Temperature {} | 46 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 56 | impl<T: Instance> SealedAdcChannel<T> for Temperature { | 47 | const CHANNEL: u8 = 12; |
| 57 | fn channel(&self) -> u8 { | 48 | } |
| 58 | cfg_if! { | 49 | #[cfg(any(adc_h5, adc_h7rs))] |
| 59 | if #[cfg(adc_g0)] { | 50 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 60 | let val = 12; | 51 | const CHANNEL: u8 = 16; |
| 61 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 52 | } |
| 62 | let val = 16; | 53 | #[cfg(adc_u0)] |
| 63 | } else if #[cfg(adc_u0)] { | 54 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 64 | let val = 11; | 55 | const CHANNEL: u8 = 11; |
| 65 | } else { | 56 | } |
| 66 | let val = 17; | 57 | #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] |
| 67 | } | 58 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 68 | } | 59 | const CHANNEL: u8 = 17; |
| 69 | val | ||
| 70 | } | ||
| 71 | } | 60 | } |
| 72 | 61 | ||
| 73 | pub struct Vbat; | 62 | #[cfg(adc_g0)] |
| 74 | impl<T: Instance> AdcChannel<T> for Vbat {} | 63 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 75 | impl<T: Instance> SealedAdcChannel<T> for Vbat { | 64 | const CHANNEL: u8 = 14; |
| 76 | fn channel(&self) -> u8 { | 65 | } |
| 77 | cfg_if! { | 66 | #[cfg(any(adc_h5, adc_h7rs))] |
| 78 | if #[cfg(adc_g0)] { | 67 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 79 | let val = 14; | 68 | const CHANNEL: u8 = 16; |
| 80 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 69 | } |
| 81 | let val = 2; | 70 | #[cfg(adc_u0)] |
| 82 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 71 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 83 | let val = 13; | 72 | const CHANNEL: u8 = 13; |
| 84 | } else { | 73 | } |
| 85 | let val = 18; | 74 | #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] |
| 86 | } | 75 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 87 | } | 76 | const CHANNEL: u8 = 18; |
| 88 | val | ||
| 89 | } | ||
| 90 | } | 77 | } |
| 91 | 78 | ||
| 92 | cfg_if! { | 79 | cfg_if! { |
| 93 | if #[cfg(any(adc_h5, adc_h7rs))] { | 80 | if #[cfg(any(adc_h5, adc_h7rs))] { |
| 94 | pub struct VddCore; | 81 | pub struct VddCore; |
| 95 | impl<T: Instance> AdcChannel<T> for VddCore {} | 82 | impl<T: Instance> super::AdcChannel<T> for VddCore {} |
| 96 | impl<T: Instance> super::SealedAdcChannel<T> for VddCore { | 83 | impl<T: Instance> super::SealedAdcChannel<T> for VddCore { |
| 97 | fn channel(&self) -> u8 { | 84 | fn channel(&self) -> u8 { |
| 98 | 6 | 85 | 17 |
| 99 | } | 86 | } |
| 100 | } | 87 | } |
| 101 | } | 88 | } |
| @@ -104,7 +91,7 @@ cfg_if! { | |||
| 104 | cfg_if! { | 91 | cfg_if! { |
| 105 | if #[cfg(adc_u0)] { | 92 | if #[cfg(adc_u0)] { |
| 106 | pub struct DacOut; | 93 | pub struct DacOut; |
| 107 | impl<T: Instance> AdcChannel<T> for DacOut {} | 94 | impl<T: Instance> super::AdcChannel<T> for DacOut {} |
| 108 | impl<T: Instance> super::SealedAdcChannel<T> for DacOut { | 95 | impl<T: Instance> super::SealedAdcChannel<T> for DacOut { |
| 109 | fn channel(&self) -> u8 { | 96 | fn channel(&self) -> u8 { |
| 110 | 19 | 97 | 19 |
| @@ -113,21 +100,6 @@ cfg_if! { | |||
| 113 | } | 100 | } |
| 114 | } | 101 | } |
| 115 | 102 | ||
| 116 | /// Number of samples used for averaging. | ||
| 117 | #[derive(Copy, Clone, Debug)] | ||
| 118 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 119 | pub enum Averaging { | ||
| 120 | Disabled, | ||
| 121 | Samples2, | ||
| 122 | Samples4, | ||
| 123 | Samples8, | ||
| 124 | Samples16, | ||
| 125 | Samples32, | ||
| 126 | Samples64, | ||
| 127 | Samples128, | ||
| 128 | Samples256, | ||
| 129 | } | ||
| 130 | |||
| 131 | cfg_if! { if #[cfg(adc_g0)] { | 103 | cfg_if! { if #[cfg(adc_g0)] { |
| 132 | 104 | ||
| 133 | /// Synchronous PCLK prescaler | 105 | /// Synchronous PCLK prescaler |
| @@ -148,327 +120,146 @@ pub enum Clock { | |||
| 148 | 120 | ||
| 149 | }} | 121 | }} |
| 150 | 122 | ||
| 151 | impl<'d, T: Instance> Adc<'d, T> { | 123 | #[cfg(adc_u0)] |
| 152 | /// Enable the voltage regulator | 124 | type Ovss = u8; |
| 153 | fn init_regulator() { | 125 | #[cfg(adc_u0)] |
| 154 | rcc::enable_and_reset::<T>(); | 126 | type Ovsr = u8; |
| 155 | T::regs().cr().modify(|reg| { | 127 | #[cfg(adc_v3)] |
| 156 | #[cfg(not(any(adc_g0, adc_u0)))] | 128 | type Ovss = OversamplingShift; |
| 157 | reg.set_deeppwd(false); | 129 | #[cfg(adc_v3)] |
| 158 | reg.set_advregen(true); | 130 | type Ovsr = OversamplingRatio; |
| 159 | }); | 131 | |
| 160 | 132 | /// Adc configuration | |
| 161 | // If this is false then each ADC_CHSELR bit enables an input channel. | 133 | #[derive(Default)] |
| 162 | // This is the reset value, so has no effect. | 134 | pub struct AdcConfig { |
| 163 | #[cfg(any(adc_g0, adc_u0))] | 135 | #[cfg(any(adc_u0, adc_g0, adc_v3))] |
| 164 | T::regs().cfgr1().modify(|reg| { | 136 | pub oversampling_shift: Option<Ovss>, |
| 165 | reg.set_chselrmod(false); | 137 | #[cfg(any(adc_u0, adc_g0, adc_v3))] |
| 166 | }); | 138 | pub oversampling_ratio: Option<Ovsr>, |
| 139 | #[cfg(any(adc_u0, adc_g0))] | ||
| 140 | pub oversampling_enable: Option<bool>, | ||
| 141 | #[cfg(adc_v3)] | ||
| 142 | pub oversampling_mode: Option<(Rovsm, Trovs, bool)>, | ||
| 143 | #[cfg(adc_g0)] | ||
| 144 | pub clock: Option<Clock>, | ||
| 145 | pub resolution: Option<Resolution>, | ||
| 146 | pub averaging: Option<Averaging>, | ||
| 147 | } | ||
| 167 | 148 | ||
| 168 | blocking_delay_us(20); | 149 | impl<T: Instance> super::SealedAnyInstance for T { |
| 150 | fn dr() -> *mut u16 { | ||
| 151 | T::regs().dr().as_ptr() as *mut u16 | ||
| 169 | } | 152 | } |
| 170 | 153 | ||
| 171 | /// Calibrate to remove conversion offset | 154 | // Enable ADC only when it is not already running. |
| 172 | fn init_calibrate() { | 155 | fn enable() { |
| 173 | T::regs().cr().modify(|reg| { | 156 | // Make sure bits are off |
| 174 | reg.set_adcal(true); | 157 | while T::regs().cr().read().addis() { |
| 175 | }); | ||
| 176 | |||
| 177 | while T::regs().cr().read().adcal() { | ||
| 178 | // spin | 158 | // spin |
| 179 | } | 159 | } |
| 180 | 160 | ||
| 181 | blocking_delay_us(1); | 161 | if !T::regs().cr().read().aden() { |
| 162 | // Enable ADC | ||
| 163 | T::regs().isr().modify(|reg| { | ||
| 164 | reg.set_adrdy(true); | ||
| 165 | }); | ||
| 166 | T::regs().cr().modify(|reg| { | ||
| 167 | reg.set_aden(true); | ||
| 168 | }); | ||
| 169 | |||
| 170 | while !T::regs().isr().read().adrdy() { | ||
| 171 | // spin | ||
| 172 | } | ||
| 173 | } | ||
| 182 | } | 174 | } |
| 183 | 175 | ||
| 184 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | 176 | fn start() { |
| 185 | pub(super) fn start() { | ||
| 186 | // Start adc conversion | ||
| 187 | T::regs().cr().modify(|reg| { | 177 | T::regs().cr().modify(|reg| { |
| 188 | reg.set_adstart(true); | 178 | reg.set_adstart(true); |
| 189 | }); | 179 | }); |
| 190 | } | 180 | } |
| 191 | 181 | ||
| 192 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | 182 | fn stop() { |
| 193 | pub(super) fn stop() { | 183 | // Ensure conversions are finished. |
| 194 | // Stop adc conversion | ||
| 195 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | 184 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 196 | T::regs().cr().modify(|reg| { | 185 | T::regs().cr().modify(|reg| { |
| 197 | reg.set_adstp(true); | 186 | reg.set_adstp(true); |
| 198 | }); | 187 | }); |
| 199 | while T::regs().cr().read().adstart() {} | 188 | while T::regs().cr().read().adstart() {} |
| 200 | } | 189 | } |
| 201 | } | ||
| 202 | 190 | ||
| 203 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | 191 | // Reset configuration. |
| 204 | pub(super) fn teardown_adc() { | ||
| 205 | //disable dma control | ||
| 206 | #[cfg(not(any(adc_g0, adc_u0)))] | 192 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 207 | T::regs().cfgr().modify(|reg| { | 193 | T::regs().cfgr().modify(|reg| { |
| 194 | reg.set_cont(false); | ||
| 208 | reg.set_dmaen(false); | 195 | reg.set_dmaen(false); |
| 209 | }); | 196 | }); |
| 210 | #[cfg(any(adc_g0, adc_u0))] | 197 | #[cfg(any(adc_g0, adc_u0))] |
| 211 | T::regs().cfgr1().modify(|reg| { | 198 | T::regs().cfgr1().modify(|reg| { |
| 199 | reg.set_cont(false); | ||
| 212 | reg.set_dmaen(false); | 200 | reg.set_dmaen(false); |
| 213 | }); | 201 | }); |
| 214 | } | 202 | } |
| 215 | 203 | ||
| 216 | /// Initialize the ADC leaving any analog clock at reset value. | 204 | /// Perform a single conversion. |
| 217 | /// For G0 and WL, this is the async clock without prescaler. | 205 | fn convert() -> u16 { |
| 218 | pub fn new(adc: Peri<'d, T>) -> Self { | 206 | // Some models are affected by an erratum: |
| 219 | Self::init_regulator(); | 207 | // If we perform conversions slower than 1 kHz, the first read ADC value can be |
| 220 | Self::init_calibrate(); | 208 | // corrupted, so we discard it and measure again. |
| 221 | Self { | 209 | // |
| 222 | adc, | 210 | // STM32L471xx: Section 2.7.3 |
| 223 | sample_time: SampleTime::from_bits(0), | 211 | // STM32G4: Section 2.7.3 |
| 224 | } | 212 | #[cfg(any(rcc_l4, rcc_g4))] |
| 225 | } | 213 | let len = 2; |
| 226 | |||
| 227 | #[cfg(adc_g0)] | ||
| 228 | /// Initialize ADC with explicit clock for the analog ADC | ||
| 229 | pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { | ||
| 230 | Self::init_regulator(); | ||
| 231 | |||
| 232 | #[cfg(any(stm32wl5x))] | ||
| 233 | { | ||
| 234 | // Reset value 0 is actually _No clock selected_ in the STM32WL5x reference manual | ||
| 235 | let async_clock_available = pac::RCC.ccipr().read().adcsel() != pac::rcc::vals::Adcsel::_RESERVED_0; | ||
| 236 | match clock { | ||
| 237 | Clock::Async { div: _ } => { | ||
| 238 | assert!(async_clock_available); | ||
| 239 | } | ||
| 240 | Clock::Sync { div: _ } => { | ||
| 241 | if async_clock_available { | ||
| 242 | warn!("Not using configured ADC clock"); | ||
| 243 | } | ||
| 244 | } | ||
| 245 | } | ||
| 246 | } | ||
| 247 | match clock { | ||
| 248 | Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div)), | ||
| 249 | Clock::Sync { div } => T::regs().cfgr2().modify(|reg| { | ||
| 250 | reg.set_ckmode(match div { | ||
| 251 | CkModePclk::DIV1 => Ckmode::PCLK, | ||
| 252 | CkModePclk::DIV2 => Ckmode::PCLK_DIV2, | ||
| 253 | CkModePclk::DIV4 => Ckmode::PCLK_DIV4, | ||
| 254 | }) | ||
| 255 | }), | ||
| 256 | } | ||
| 257 | |||
| 258 | Self::init_calibrate(); | ||
| 259 | |||
| 260 | Self { | ||
| 261 | adc, | ||
| 262 | sample_time: SampleTime::from_bits(0), | ||
| 263 | } | ||
| 264 | } | ||
| 265 | 214 | ||
| 266 | // Enable ADC only when it is not already running. | 215 | #[cfg(not(any(rcc_l4, rcc_g4)))] |
| 267 | fn enable(&mut self) { | 216 | let len = 1; |
| 268 | // Make sure bits are off | ||
| 269 | while T::regs().cr().read().addis() { | ||
| 270 | // spin | ||
| 271 | } | ||
| 272 | 217 | ||
| 273 | if !T::regs().cr().read().aden() { | 218 | for _ in 0..len { |
| 274 | // Enable ADC | ||
| 275 | T::regs().isr().modify(|reg| { | 219 | T::regs().isr().modify(|reg| { |
| 276 | reg.set_adrdy(true); | 220 | reg.set_eos(true); |
| 221 | reg.set_eoc(true); | ||
| 277 | }); | 222 | }); |
| 223 | |||
| 224 | // Start conversion | ||
| 278 | T::regs().cr().modify(|reg| { | 225 | T::regs().cr().modify(|reg| { |
| 279 | reg.set_aden(true); | 226 | reg.set_adstart(true); |
| 280 | }); | 227 | }); |
| 281 | 228 | ||
| 282 | while !T::regs().isr().read().adrdy() { | 229 | while !T::regs().isr().read().eos() { |
| 283 | // spin | 230 | // spin |
| 284 | } | 231 | } |
| 285 | } | 232 | } |
| 286 | } | ||
| 287 | |||
| 288 | pub fn enable_vrefint(&self) -> VrefInt { | ||
| 289 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 290 | T::common_regs().ccr().modify(|reg| { | ||
| 291 | reg.set_vrefen(true); | ||
| 292 | }); | ||
| 293 | #[cfg(any(adc_g0, adc_u0))] | ||
| 294 | T::regs().ccr().modify(|reg| { | ||
| 295 | reg.set_vrefen(true); | ||
| 296 | }); | ||
| 297 | |||
| 298 | // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us | ||
| 299 | // to stabilize the internal voltage reference. | ||
| 300 | blocking_delay_us(15); | ||
| 301 | |||
| 302 | VrefInt {} | ||
| 303 | } | ||
| 304 | |||
| 305 | pub fn enable_temperature(&self) -> Temperature { | ||
| 306 | cfg_if! { | ||
| 307 | if #[cfg(any(adc_g0, adc_u0))] { | ||
| 308 | T::regs().ccr().modify(|reg| { | ||
| 309 | reg.set_tsen(true); | ||
| 310 | }); | ||
| 311 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 312 | T::common_regs().ccr().modify(|reg| { | ||
| 313 | reg.set_tsen(true); | ||
| 314 | }); | ||
| 315 | } else { | ||
| 316 | T::common_regs().ccr().modify(|reg| { | ||
| 317 | reg.set_ch17sel(true); | ||
| 318 | }); | ||
| 319 | } | ||
| 320 | } | ||
| 321 | |||
| 322 | Temperature {} | ||
| 323 | } | ||
| 324 | |||
| 325 | pub fn enable_vbat(&self) -> Vbat { | ||
| 326 | cfg_if! { | ||
| 327 | if #[cfg(any(adc_g0, adc_u0))] { | ||
| 328 | T::regs().ccr().modify(|reg| { | ||
| 329 | reg.set_vbaten(true); | ||
| 330 | }); | ||
| 331 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 332 | T::common_regs().ccr().modify(|reg| { | ||
| 333 | reg.set_vbaten(true); | ||
| 334 | }); | ||
| 335 | } else { | ||
| 336 | T::common_regs().ccr().modify(|reg| { | ||
| 337 | reg.set_ch18sel(true); | ||
| 338 | }); | ||
| 339 | } | ||
| 340 | } | ||
| 341 | |||
| 342 | Vbat {} | ||
| 343 | } | ||
| 344 | |||
| 345 | /// Set the ADC sample time. | ||
| 346 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 347 | self.sample_time = sample_time; | ||
| 348 | } | ||
| 349 | |||
| 350 | /// Get the ADC sample time. | ||
| 351 | pub fn sample_time(&self) -> SampleTime { | ||
| 352 | self.sample_time | ||
| 353 | } | ||
| 354 | 233 | ||
| 355 | /// Set the ADC resolution. | 234 | T::regs().dr().read().0 as u16 |
| 356 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 357 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 358 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 359 | #[cfg(any(adc_g0, adc_u0))] | ||
| 360 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); | ||
| 361 | } | ||
| 362 | |||
| 363 | pub fn set_averaging(&mut self, averaging: Averaging) { | ||
| 364 | let (enable, samples, right_shift) = match averaging { | ||
| 365 | Averaging::Disabled => (false, 0, 0), | ||
| 366 | Averaging::Samples2 => (true, 0, 1), | ||
| 367 | Averaging::Samples4 => (true, 1, 2), | ||
| 368 | Averaging::Samples8 => (true, 2, 3), | ||
| 369 | Averaging::Samples16 => (true, 3, 4), | ||
| 370 | Averaging::Samples32 => (true, 4, 5), | ||
| 371 | Averaging::Samples64 => (true, 5, 6), | ||
| 372 | Averaging::Samples128 => (true, 6, 7), | ||
| 373 | Averaging::Samples256 => (true, 7, 8), | ||
| 374 | }; | ||
| 375 | T::regs().cfgr2().modify(|reg| { | ||
| 376 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 377 | reg.set_rovse(enable); | ||
| 378 | #[cfg(any(adc_g0, adc_u0))] | ||
| 379 | reg.set_ovse(enable); | ||
| 380 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 381 | reg.set_ovsr(samples.into()); | ||
| 382 | #[cfg(not(any(adc_h5, adc_h7rs)))] | ||
| 383 | reg.set_ovsr(samples.into()); | ||
| 384 | reg.set_ovss(right_shift.into()); | ||
| 385 | }) | ||
| 386 | } | ||
| 387 | /* | ||
| 388 | /// Convert a raw sample from the `Temperature` to deg C | ||
| 389 | pub fn to_degrees_centigrade(sample: u16) -> f32 { | ||
| 390 | (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) | ||
| 391 | * (sample as f32 - VtempCal30::get().read() as f32) | ||
| 392 | + 30.0 | ||
| 393 | } | 235 | } |
| 394 | */ | ||
| 395 | 236 | ||
| 396 | /// Perform a single conversion. | 237 | fn configure_dma(conversion_mode: ConversionMode) { |
| 397 | fn convert(&mut self) -> u16 { | 238 | // Set continuous mode with oneshot dma. |
| 239 | // Clear overrun flag before starting transfer. | ||
| 398 | T::regs().isr().modify(|reg| { | 240 | T::regs().isr().modify(|reg| { |
| 399 | reg.set_eos(true); | 241 | reg.set_ovr(true); |
| 400 | reg.set_eoc(true); | ||
| 401 | }); | ||
| 402 | |||
| 403 | // Start conversion | ||
| 404 | T::regs().cr().modify(|reg| { | ||
| 405 | reg.set_adstart(true); | ||
| 406 | }); | 242 | }); |
| 407 | 243 | ||
| 408 | while !T::regs().isr().read().eos() { | 244 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 409 | // spin | 245 | let regs = T::regs().cfgr(); |
| 410 | } | ||
| 411 | 246 | ||
| 412 | T::regs().dr().read().0 as u16 | 247 | #[cfg(any(adc_g0, adc_u0))] |
| 413 | } | 248 | let regs = T::regs().cfgr1(); |
| 414 | 249 | ||
| 415 | /// Read an ADC channel. | 250 | regs.modify(|reg| { |
| 416 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 251 | reg.set_discen(false); |
| 417 | self.read_channel(channel) | 252 | reg.set_cont(true); |
| 253 | reg.set_dmacfg(match conversion_mode { | ||
| 254 | ConversionMode::Singular => Dmacfg::ONE_SHOT, | ||
| 255 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 256 | ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, | ||
| 257 | }); | ||
| 258 | reg.set_dmaen(true); | ||
| 259 | }); | ||
| 418 | } | 260 | } |
| 419 | 261 | ||
| 420 | /// Read one or multiple ADC channels using DMA. | 262 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| 421 | /// | ||
| 422 | /// `readings` must have a length that is a multiple of the length of the | ||
| 423 | /// `sequence` iterator. | ||
| 424 | /// | ||
| 425 | /// Note: The order of values in `readings` is defined by the pin ADC | ||
| 426 | /// channel number and not the pin order in `sequence`. | ||
| 427 | /// | ||
| 428 | /// Example | ||
| 429 | /// ```rust,ignore | ||
| 430 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 431 | /// | ||
| 432 | /// let mut adc = Adc::new(p.ADC1); | ||
| 433 | /// let mut adc_pin0 = p.PA0.degrade_adc(); | ||
| 434 | /// let mut adc_pin1 = p.PA1.degrade_adc(); | ||
| 435 | /// let mut measurements = [0u16; 2]; | ||
| 436 | /// | ||
| 437 | /// adc.read( | ||
| 438 | /// p.DMA1_CH2.reborrow(), | ||
| 439 | /// [ | ||
| 440 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 441 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 442 | /// ] | ||
| 443 | /// .into_iter(), | ||
| 444 | /// &mut measurements, | ||
| 445 | /// ) | ||
| 446 | /// .await; | ||
| 447 | /// defmt::info!("measurements: {}", measurements); | ||
| 448 | /// ``` | ||
| 449 | pub async fn read( | ||
| 450 | &mut self, | ||
| 451 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 452 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 453 | readings: &mut [u16], | ||
| 454 | ) { | ||
| 455 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 456 | assert!( | ||
| 457 | readings.len() % sequence.len() == 0, | ||
| 458 | "Readings length must be a multiple of sequence length" | ||
| 459 | ); | ||
| 460 | assert!( | ||
| 461 | sequence.len() <= 16, | ||
| 462 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 463 | ); | ||
| 464 | |||
| 465 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 466 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 467 | |||
| 468 | // Ensure no conversions are ongoing and ADC is enabled. | ||
| 469 | Self::cancel_conversions(); | ||
| 470 | self.enable(); | ||
| 471 | |||
| 472 | // Set sequence length | 263 | // Set sequence length |
| 473 | #[cfg(not(any(adc_g0, adc_u0)))] | 264 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 474 | T::regs().sqr1().modify(|w| { | 265 | T::regs().sqr1().modify(|w| { |
| @@ -481,10 +272,10 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 481 | 272 | ||
| 482 | T::regs().chselr().write(|chselr| { | 273 | T::regs().chselr().write(|chselr| { |
| 483 | T::regs().smpr().write(|smpr| { | 274 | T::regs().smpr().write(|smpr| { |
| 484 | for (channel, sample_time) in sequence { | 275 | for ((channel, _), sample_time) in sequence { |
| 485 | chselr.set_chsel(channel.channel.into(), true); | 276 | chselr.set_chsel(channel.into(), true); |
| 486 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { | 277 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { |
| 487 | smpr.set_smpsel(channel.channel.into(), (i as u8).into()); | 278 | smpr.set_smpsel(channel.into(), (i as u8).into()); |
| 488 | } else { | 279 | } else { |
| 489 | smpr.set_sample_time(sample_times.len(), sample_time); | 280 | smpr.set_sample_time(sample_times.len(), sample_time); |
| 490 | if let Err(_) = sample_times.push(sample_time) { | 281 | if let Err(_) = sample_times.push(sample_time) { |
| @@ -504,30 +295,63 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 504 | let mut channel_mask = 0; | 295 | let mut channel_mask = 0; |
| 505 | 296 | ||
| 506 | // Configure channels and ranks | 297 | // Configure channels and ranks |
| 507 | for (_i, (channel, sample_time)) in sequence.enumerate() { | 298 | for (_i, ((channel, _), sample_time)) in sequence.enumerate() { |
| 508 | Self::configure_channel(channel, sample_time); | 299 | // RM0492, RM0481, etc. |
| 300 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | ||
| 301 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 302 | if channel == 0 { | ||
| 303 | T::regs().or().modify(|reg| reg.set_op0(true)); | ||
| 304 | } | ||
| 305 | |||
| 306 | // Configure channel | ||
| 307 | cfg_if! { | ||
| 308 | if #[cfg(adc_u0)] { | ||
| 309 | // On G0 and U6 all channels use the same sampling time. | ||
| 310 | T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); | ||
| 311 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 312 | match channel { | ||
| 313 | 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), | ||
| 314 | _ => T::regs().smpr2().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), | ||
| 315 | } | ||
| 316 | } else { | ||
| 317 | let sample_time = sample_time.into(); | ||
| 318 | T::regs() | ||
| 319 | .smpr(channel as usize / 10) | ||
| 320 | .modify(|reg| reg.set_smp(channel as usize % 10, sample_time)); | ||
| 321 | } | ||
| 322 | } | ||
| 323 | |||
| 324 | #[cfg(stm32h7)] | ||
| 325 | { | ||
| 326 | use crate::pac::adc::vals::Pcsel; | ||
| 327 | |||
| 328 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 329 | T::regs() | ||
| 330 | .pcsel() | ||
| 331 | .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); | ||
| 332 | } | ||
| 509 | 333 | ||
| 510 | // Each channel is sampled according to sequence | 334 | // Each channel is sampled according to sequence |
| 511 | #[cfg(not(any(adc_g0, adc_u0)))] | 335 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 512 | match _i { | 336 | match _i { |
| 513 | 0..=3 => { | 337 | 0..=3 => { |
| 514 | T::regs().sqr1().modify(|w| { | 338 | T::regs().sqr1().modify(|w| { |
| 515 | w.set_sq(_i, channel.channel()); | 339 | w.set_sq(_i, channel); |
| 516 | }); | 340 | }); |
| 517 | } | 341 | } |
| 518 | 4..=8 => { | 342 | 4..=8 => { |
| 519 | T::regs().sqr2().modify(|w| { | 343 | T::regs().sqr2().modify(|w| { |
| 520 | w.set_sq(_i - 4, channel.channel()); | 344 | w.set_sq(_i - 4, channel); |
| 521 | }); | 345 | }); |
| 522 | } | 346 | } |
| 523 | 9..=13 => { | 347 | 9..=13 => { |
| 524 | T::regs().sqr3().modify(|w| { | 348 | T::regs().sqr3().modify(|w| { |
| 525 | w.set_sq(_i - 9, channel.channel()); | 349 | w.set_sq(_i - 9, channel); |
| 526 | }); | 350 | }); |
| 527 | } | 351 | } |
| 528 | 14..=15 => { | 352 | 14..=15 => { |
| 529 | T::regs().sqr4().modify(|w| { | 353 | T::regs().sqr4().modify(|w| { |
| 530 | w.set_sq(_i - 14, channel.channel()); | 354 | w.set_sq(_i - 14, channel); |
| 531 | }); | 355 | }); |
| 532 | } | 356 | } |
| 533 | _ => unreachable!(), | 357 | _ => unreachable!(), |
| @@ -535,7 +359,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 535 | 359 | ||
| 536 | #[cfg(adc_u0)] | 360 | #[cfg(adc_u0)] |
| 537 | { | 361 | { |
| 538 | channel_mask |= 1 << channel.channel(); | 362 | channel_mask |= 1 << channel; |
| 539 | } | 363 | } |
| 540 | } | 364 | } |
| 541 | 365 | ||
| @@ -547,312 +371,234 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 547 | reg.set_chsel(channel_mask); | 371 | reg.set_chsel(channel_mask); |
| 548 | }); | 372 | }); |
| 549 | } | 373 | } |
| 550 | // Set continuous mode with oneshot dma. | 374 | } |
| 551 | // Clear overrun flag before starting transfer. | 375 | } |
| 552 | T::regs().isr().modify(|reg| { | ||
| 553 | reg.set_ovr(true); | ||
| 554 | }); | ||
| 555 | 376 | ||
| 556 | #[cfg(not(any(adc_g0, adc_u0)))] | 377 | impl<'d, T: Instance> Adc<'d, T> { |
| 557 | T::regs().cfgr().modify(|reg| { | 378 | /// Enable the voltage regulator |
| 558 | reg.set_discen(false); | 379 | fn init_regulator() { |
| 559 | reg.set_cont(true); | 380 | rcc::enable_and_reset::<T>(); |
| 560 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | 381 | T::regs().cr().modify(|reg| { |
| 561 | reg.set_dmaen(true); | 382 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 383 | reg.set_deeppwd(false); | ||
| 384 | reg.set_advregen(true); | ||
| 562 | }); | 385 | }); |
| 386 | |||
| 387 | // If this is false then each ADC_CHSELR bit enables an input channel. | ||
| 388 | // This is the reset value, so has no effect. | ||
| 563 | #[cfg(any(adc_g0, adc_u0))] | 389 | #[cfg(any(adc_g0, adc_u0))] |
| 564 | T::regs().cfgr1().modify(|reg| { | 390 | T::regs().cfgr1().modify(|reg| { |
| 565 | reg.set_discen(false); | 391 | reg.set_chselrmod(false); |
| 566 | reg.set_cont(true); | ||
| 567 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 568 | reg.set_dmaen(true); | ||
| 569 | }); | 392 | }); |
| 570 | 393 | ||
| 571 | let request = rx_dma.request(); | 394 | blocking_delay_us(20); |
| 572 | let transfer = unsafe { | 395 | } |
| 573 | Transfer::new_read( | ||
| 574 | rx_dma, | ||
| 575 | request, | ||
| 576 | T::regs().dr().as_ptr() as *mut u16, | ||
| 577 | readings, | ||
| 578 | Default::default(), | ||
| 579 | ) | ||
| 580 | }; | ||
| 581 | 396 | ||
| 582 | // Start conversion | 397 | /// Calibrate to remove conversion offset |
| 398 | fn init_calibrate() { | ||
| 583 | T::regs().cr().modify(|reg| { | 399 | T::regs().cr().modify(|reg| { |
| 584 | reg.set_adstart(true); | 400 | reg.set_adcal(true); |
| 585 | }); | 401 | }); |
| 586 | 402 | ||
| 587 | // Wait for conversion sequence to finish. | 403 | while T::regs().cr().read().adcal() { |
| 588 | transfer.await; | 404 | // spin |
| 589 | 405 | } | |
| 590 | // Ensure conversions are finished. | ||
| 591 | Self::cancel_conversions(); | ||
| 592 | 406 | ||
| 593 | // Reset configuration. | 407 | blocking_delay_us(1); |
| 594 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 595 | T::regs().cfgr().modify(|reg| { | ||
| 596 | reg.set_cont(false); | ||
| 597 | }); | ||
| 598 | #[cfg(any(adc_g0, adc_u0))] | ||
| 599 | T::regs().cfgr1().modify(|reg| { | ||
| 600 | reg.set_cont(false); | ||
| 601 | }); | ||
| 602 | } | 408 | } |
| 603 | 409 | ||
| 604 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | 410 | /// Initialize the ADC leaving any analog clock at reset value. |
| 605 | /// | 411 | /// For G0 and WL, this is the async clock without prescaler. |
| 606 | /// The `dma_buf` should be large enough to prevent DMA buffer overrun. | 412 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 607 | /// The length of the `dma_buf` should be a multiple of the ADC channel count. | 413 | Self::init_regulator(); |
| 608 | /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. | 414 | Self::init_calibrate(); |
| 609 | /// | 415 | Self { adc } |
| 610 | /// `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. | 416 | } |
| 611 | /// It is critical to call `read` frequently to prevent DMA buffer overrun. | ||
| 612 | /// | ||
| 613 | /// [`read`]: #method.read | ||
| 614 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 615 | pub fn into_ring_buffered<'a>( | ||
| 616 | &mut self, | ||
| 617 | dma: Peri<'a, impl RxDma<T>>, | ||
| 618 | dma_buf: &'a mut [u16], | ||
| 619 | sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>, | ||
| 620 | ) -> RingBufferedAdc<'a, T> { | ||
| 621 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 622 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 623 | assert!( | ||
| 624 | sequence.len() <= 16, | ||
| 625 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 626 | ); | ||
| 627 | // reset conversions and enable the adc | ||
| 628 | Self::cancel_conversions(); | ||
| 629 | self.enable(); | ||
| 630 | |||
| 631 | //adc side setup | ||
| 632 | 417 | ||
| 633 | // Set sequence length | 418 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { |
| 634 | #[cfg(not(any(adc_g0, adc_u0)))] | 419 | #[cfg(not(adc_g0))] |
| 635 | T::regs().sqr1().modify(|w| { | 420 | let s = Self::new(adc); |
| 636 | w.set_l(sequence.len() as u8 - 1); | ||
| 637 | }); | ||
| 638 | 421 | ||
| 639 | #[cfg(adc_g0)] | 422 | #[cfg(adc_g0)] |
| 640 | { | 423 | let s = match config.clock { |
| 641 | let mut sample_times = Vec::<SampleTime, SAMPLE_TIMES_CAPACITY>::new(); | 424 | Some(clock) => Self::new_with_clock(adc, clock), |
| 425 | None => Self::new(adc), | ||
| 426 | }; | ||
| 642 | 427 | ||
| 643 | T::regs().chselr().write(|chselr| { | 428 | #[cfg(any(adc_g0, adc_u0, adc_v3))] |
| 644 | T::regs().smpr().write(|smpr| { | 429 | if let Some(shift) = config.oversampling_shift { |
| 645 | for (channel, sample_time) in sequence { | 430 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); |
| 646 | chselr.set_chsel(channel.channel.into(), true); | ||
| 647 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { | ||
| 648 | smpr.set_smpsel(channel.channel.into(), (i as u8).into()); | ||
| 649 | } else { | ||
| 650 | smpr.set_sample_time(sample_times.len(), sample_time); | ||
| 651 | if let Err(_) = sample_times.push(sample_time) { | ||
| 652 | panic!( | ||
| 653 | "Implementation is limited to {} unique sample times among all channels.", | ||
| 654 | SAMPLE_TIMES_CAPACITY | ||
| 655 | ); | ||
| 656 | } | ||
| 657 | } | ||
| 658 | } | ||
| 659 | }) | ||
| 660 | }); | ||
| 661 | } | 431 | } |
| 662 | #[cfg(not(adc_g0))] | ||
| 663 | { | ||
| 664 | #[cfg(adc_u0)] | ||
| 665 | let mut channel_mask = 0; | ||
| 666 | 432 | ||
| 667 | // Configure channels and ranks | 433 | #[cfg(any(adc_g0, adc_u0, adc_v3))] |
| 668 | for (_i, (channel, sample_time)) in sequence.enumerate() { | 434 | if let Some(ratio) = config.oversampling_ratio { |
| 669 | Self::configure_channel(channel, sample_time); | 435 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); |
| 436 | } | ||
| 670 | 437 | ||
| 671 | // Each channel is sampled according to sequence | 438 | #[cfg(any(adc_g0, adc_u0))] |
| 672 | #[cfg(not(any(adc_g0, adc_u0)))] | 439 | if let Some(enable) = config.oversampling_enable { |
| 673 | match _i { | 440 | T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); |
| 674 | 0..=3 => { | 441 | } |
| 675 | T::regs().sqr1().modify(|w| { | ||
| 676 | w.set_sq(_i, channel.channel()); | ||
| 677 | }); | ||
| 678 | } | ||
| 679 | 4..=8 => { | ||
| 680 | T::regs().sqr2().modify(|w| { | ||
| 681 | w.set_sq(_i - 4, channel.channel()); | ||
| 682 | }); | ||
| 683 | } | ||
| 684 | 9..=13 => { | ||
| 685 | T::regs().sqr3().modify(|w| { | ||
| 686 | w.set_sq(_i - 9, channel.channel()); | ||
| 687 | }); | ||
| 688 | } | ||
| 689 | 14..=15 => { | ||
| 690 | T::regs().sqr4().modify(|w| { | ||
| 691 | w.set_sq(_i - 14, channel.channel()); | ||
| 692 | }); | ||
| 693 | } | ||
| 694 | _ => unreachable!(), | ||
| 695 | } | ||
| 696 | 442 | ||
| 697 | #[cfg(adc_u0)] | 443 | #[cfg(adc_v3)] |
| 698 | { | 444 | if let Some((mode, trig_mode, enable)) = config.oversampling_mode { |
| 699 | channel_mask |= 1 << channel.channel(); | 445 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); |
| 700 | } | 446 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); |
| 701 | } | 447 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); |
| 448 | } | ||
| 702 | 449 | ||
| 703 | // On G0 and U0 enabled channels are sampled from 0 to last channel. | 450 | if let Some(resolution) = config.resolution { |
| 704 | // It is possible to add up to 8 sequences if CHSELRMOD = 1. | 451 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 705 | // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. | 452 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); |
| 706 | #[cfg(adc_u0)] | 453 | #[cfg(any(adc_g0, adc_u0))] |
| 707 | T::regs().chselr().modify(|reg| { | 454 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); |
| 708 | reg.set_chsel(channel_mask); | ||
| 709 | }); | ||
| 710 | } | 455 | } |
| 711 | // Set continuous mode with Circular dma. | ||
| 712 | // Clear overrun flag before starting transfer. | ||
| 713 | T::regs().isr().modify(|reg| { | ||
| 714 | reg.set_ovr(true); | ||
| 715 | }); | ||
| 716 | 456 | ||
| 717 | #[cfg(not(any(adc_g0, adc_u0)))] | 457 | if let Some(averaging) = config.averaging { |
| 718 | T::regs().cfgr().modify(|reg| { | 458 | let (enable, samples, right_shift) = match averaging { |
| 719 | reg.set_discen(false); | 459 | Averaging::Disabled => (false, 0, 0), |
| 720 | reg.set_cont(true); | 460 | Averaging::Samples2 => (true, 0, 1), |
| 721 | reg.set_dmacfg(Dmacfg::CIRCULAR); | 461 | Averaging::Samples4 => (true, 1, 2), |
| 722 | reg.set_dmaen(true); | 462 | Averaging::Samples8 => (true, 2, 3), |
| 723 | }); | 463 | Averaging::Samples16 => (true, 3, 4), |
| 724 | #[cfg(any(adc_g0, adc_u0))] | 464 | Averaging::Samples32 => (true, 4, 5), |
| 725 | T::regs().cfgr1().modify(|reg| { | 465 | Averaging::Samples64 => (true, 5, 6), |
| 726 | reg.set_discen(false); | 466 | Averaging::Samples128 => (true, 6, 7), |
| 727 | reg.set_cont(true); | 467 | Averaging::Samples256 => (true, 7, 8), |
| 728 | reg.set_dmacfg(Dmacfg::CIRCULAR); | 468 | }; |
| 729 | reg.set_dmaen(true); | 469 | T::regs().cfgr2().modify(|reg| { |
| 730 | }); | 470 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 471 | reg.set_rovse(enable); | ||
| 472 | #[cfg(any(adc_g0, adc_u0))] | ||
| 473 | reg.set_ovse(enable); | ||
| 474 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 475 | reg.set_ovsr(samples.into()); | ||
| 476 | #[cfg(not(any(adc_h5, adc_h7rs)))] | ||
| 477 | reg.set_ovsr(samples.into()); | ||
| 478 | reg.set_ovss(right_shift.into()); | ||
| 479 | }) | ||
| 480 | } | ||
| 731 | 481 | ||
| 732 | RingBufferedAdc::new(dma, dma_buf) | 482 | s |
| 733 | } | 483 | } |
| 734 | 484 | ||
| 735 | #[cfg(not(adc_g0))] | 485 | #[cfg(adc_g0)] |
| 736 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | 486 | /// Initialize ADC with explicit clock for the analog ADC |
| 737 | // RM0492, RM0481, etc. | 487 | pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { |
| 738 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | 488 | Self::init_regulator(); |
| 739 | #[cfg(any(adc_h5, adc_h7rs))] | 489 | |
| 740 | if channel.channel() == 0 { | 490 | #[cfg(any(stm32wl5x))] |
| 741 | T::regs().or().modify(|reg| reg.set_op0(true)); | 491 | { |
| 492 | // Reset value 0 is actually _No clock selected_ in the STM32WL5x reference manual | ||
| 493 | let async_clock_available = pac::RCC.ccipr().read().adcsel() != pac::rcc::vals::Adcsel::_RESERVED_0; | ||
| 494 | match clock { | ||
| 495 | Clock::Async { div: _ } => { | ||
| 496 | assert!(async_clock_available); | ||
| 497 | } | ||
| 498 | Clock::Sync { div: _ } => { | ||
| 499 | if async_clock_available { | ||
| 500 | warn!("Not using configured ADC clock"); | ||
| 501 | } | ||
| 502 | } | ||
| 503 | } | ||
| 742 | } | 504 | } |
| 505 | match clock { | ||
| 506 | Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div)), | ||
| 507 | Clock::Sync { div } => T::regs().cfgr2().modify(|reg| { | ||
| 508 | reg.set_ckmode(match div { | ||
| 509 | CkModePclk::DIV1 => Ckmode::PCLK, | ||
| 510 | CkModePclk::DIV2 => Ckmode::PCLK_DIV2, | ||
| 511 | CkModePclk::DIV4 => Ckmode::PCLK_DIV4, | ||
| 512 | }) | ||
| 513 | }), | ||
| 514 | } | ||
| 515 | |||
| 516 | Self::init_calibrate(); | ||
| 743 | 517 | ||
| 744 | // Configure channel | 518 | Self { adc } |
| 745 | Self::set_channel_sample_time(channel.channel(), sample_time); | ||
| 746 | } | 519 | } |
| 747 | 520 | ||
| 748 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 521 | pub fn enable_vrefint(&self) -> VrefInt { |
| 749 | self.enable(); | ||
| 750 | #[cfg(not(adc_g0))] | ||
| 751 | Self::configure_channel(channel, self.sample_time); | ||
| 752 | #[cfg(adc_g0)] | ||
| 753 | T::regs().smpr().write(|reg| { | ||
| 754 | reg.set_sample_time(0, self.sample_time); | ||
| 755 | reg.set_smpsel(channel.channel().into(), Smpsel::SMP1); | ||
| 756 | }); | ||
| 757 | // Select channel | ||
| 758 | #[cfg(not(any(adc_g0, adc_u0)))] | 522 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 759 | T::regs().sqr1().write(|reg| reg.set_sq(0, channel.channel())); | 523 | T::common_regs().ccr().modify(|reg| { |
| 524 | reg.set_vrefen(true); | ||
| 525 | }); | ||
| 760 | #[cfg(any(adc_g0, adc_u0))] | 526 | #[cfg(any(adc_g0, adc_u0))] |
| 761 | T::regs().chselr().write(|reg| { | 527 | T::regs().ccr().modify(|reg| { |
| 762 | #[cfg(adc_g0)] | 528 | reg.set_vrefen(true); |
| 763 | reg.set_chsel(channel.channel().into(), true); | ||
| 764 | #[cfg(adc_u0)] | ||
| 765 | reg.set_chsel(1 << channel.channel()); | ||
| 766 | }); | 529 | }); |
| 767 | 530 | ||
| 768 | // Some models are affected by an erratum: | 531 | // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us |
| 769 | // If we perform conversions slower than 1 kHz, the first read ADC value can be | 532 | // to stabilize the internal voltage reference. |
| 770 | // corrupted, so we discard it and measure again. | 533 | blocking_delay_us(15); |
| 771 | // | ||
| 772 | // STM32L471xx: Section 2.7.3 | ||
| 773 | // STM32G4: Section 2.7.3 | ||
| 774 | #[cfg(any(rcc_l4, rcc_g4))] | ||
| 775 | let _ = self.convert(); | ||
| 776 | let val = self.convert(); | ||
| 777 | |||
| 778 | T::regs().cr().modify(|reg| reg.set_addis(true)); | ||
| 779 | |||
| 780 | // RM0492, RM0481, etc. | ||
| 781 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | ||
| 782 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 783 | if channel.channel() == 0 { | ||
| 784 | T::regs().or().modify(|reg| reg.set_op0(false)); | ||
| 785 | } | ||
| 786 | |||
| 787 | val | ||
| 788 | } | ||
| 789 | |||
| 790 | #[cfg(adc_g0)] | ||
| 791 | pub fn set_oversampling_shift(&mut self, shift: Ovss) { | ||
| 792 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 793 | } | ||
| 794 | #[cfg(adc_u0)] | ||
| 795 | pub fn set_oversampling_shift(&mut self, shift: u8) { | ||
| 796 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 797 | } | ||
| 798 | 534 | ||
| 799 | #[cfg(adc_g0)] | 535 | VrefInt {} |
| 800 | pub fn set_oversampling_ratio(&mut self, ratio: Ovsr) { | ||
| 801 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 802 | } | ||
| 803 | #[cfg(adc_u0)] | ||
| 804 | pub fn set_oversampling_ratio(&mut self, ratio: u8) { | ||
| 805 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 806 | } | 536 | } |
| 807 | 537 | ||
| 808 | #[cfg(any(adc_g0, adc_u0))] | 538 | pub fn enable_temperature(&self) -> Temperature { |
| 809 | pub fn oversampling_enable(&mut self, enable: bool) { | 539 | cfg_if! { |
| 810 | T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); | 540 | if #[cfg(any(adc_g0, adc_u0))] { |
| 811 | } | 541 | T::regs().ccr().modify(|reg| { |
| 542 | reg.set_tsen(true); | ||
| 543 | }); | ||
| 544 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 545 | T::common_regs().ccr().modify(|reg| { | ||
| 546 | reg.set_tsen(true); | ||
| 547 | }); | ||
| 548 | } else { | ||
| 549 | T::common_regs().ccr().modify(|reg| { | ||
| 550 | reg.set_ch17sel(true); | ||
| 551 | }); | ||
| 552 | } | ||
| 553 | } | ||
| 812 | 554 | ||
| 813 | #[cfg(adc_v3)] | 555 | Temperature {} |
| 814 | pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { | ||
| 815 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 816 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 817 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 818 | } | 556 | } |
| 819 | 557 | ||
| 820 | #[cfg(adc_v3)] | 558 | pub fn enable_vbat(&self) -> Vbat { |
| 821 | pub fn set_oversampling_ratio(&mut self, ratio: OversamplingRatio) { | 559 | cfg_if! { |
| 822 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | 560 | if #[cfg(any(adc_g0, adc_u0))] { |
| 823 | } | 561 | T::regs().ccr().modify(|reg| { |
| 562 | reg.set_vbaten(true); | ||
| 563 | }); | ||
| 564 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 565 | T::common_regs().ccr().modify(|reg| { | ||
| 566 | reg.set_vbaten(true); | ||
| 567 | }); | ||
| 568 | } else { | ||
| 569 | T::common_regs().ccr().modify(|reg| { | ||
| 570 | reg.set_ch18sel(true); | ||
| 571 | }); | ||
| 572 | } | ||
| 573 | } | ||
| 824 | 574 | ||
| 825 | #[cfg(adc_v3)] | 575 | Vbat {} |
| 826 | pub fn set_oversampling_shift(&mut self, shift: OversamplingShift) { | ||
| 827 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 828 | } | 576 | } |
| 829 | 577 | ||
| 830 | #[cfg(not(adc_g0))] | 578 | pub fn disable_vbat(&self) { |
| 831 | fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { | ||
| 832 | cfg_if! { | 579 | cfg_if! { |
| 833 | if #[cfg(adc_u0)] { | 580 | if #[cfg(any(adc_g0, adc_u0))] { |
| 834 | // On G0 and U6 all channels use the same sampling time. | 581 | T::regs().ccr().modify(|reg| { |
| 835 | T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); | 582 | reg.set_vbaten(false); |
| 583 | }); | ||
| 836 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 584 | } else if #[cfg(any(adc_h5, adc_h7rs))] { |
| 837 | match _ch { | 585 | T::common_regs().ccr().modify(|reg| { |
| 838 | 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), | 586 | reg.set_vbaten(false); |
| 839 | _ => T::regs().smpr2().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), | 587 | }); |
| 840 | } | ||
| 841 | } else { | 588 | } else { |
| 842 | let sample_time = sample_time.into(); | 589 | T::common_regs().ccr().modify(|reg| { |
| 843 | T::regs() | 590 | reg.set_ch18sel(false); |
| 844 | .smpr(_ch as usize / 10) | 591 | }); |
| 845 | .modify(|reg| reg.set_smp(_ch as usize % 10, sample_time)); | ||
| 846 | } | 592 | } |
| 847 | } | 593 | } |
| 848 | } | 594 | } |
| 849 | 595 | ||
| 850 | fn cancel_conversions() { | 596 | /* |
| 851 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | 597 | /// Convert a raw sample from the `Temperature` to deg C |
| 852 | T::regs().cr().modify(|reg| { | 598 | pub fn to_degrees_centigrade(sample: u16) -> f32 { |
| 853 | reg.set_adstp(true); | 599 | (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) |
| 854 | }); | 600 | * (sample as f32 - VtempCal30::get().read() as f32) |
| 855 | while T::regs().cr().read().adstart() {} | 601 | + 30.0 |
| 856 | } | ||
| 857 | } | 602 | } |
| 603 | */ | ||
| 858 | } | 604 | } |
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index c7d0103a6..804e63db6 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs | |||
| @@ -4,10 +4,8 @@ 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 | Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, blocking_delay_us, | 8 | use crate::adc::ConversionMode; |
| 9 | }; | ||
| 10 | use crate::dma::Transfer; | ||
| 11 | use crate::time::Hertz; | 9 | use crate::time::Hertz; |
| 12 | use crate::{Peri, pac, rcc}; | 10 | use crate::{Peri, pac, rcc}; |
| 13 | 11 | ||
| @@ -25,149 +23,227 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); | |||
| 25 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55); | 23 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55); |
| 26 | 24 | ||
| 27 | #[cfg(stm32g4)] | 25 | #[cfg(stm32g4)] |
| 28 | const VREF_CHANNEL: u8 = 18; | 26 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 27 | const CHANNEL: u8 = 18; | ||
| 28 | } | ||
| 29 | #[cfg(stm32g4)] | 29 | #[cfg(stm32g4)] |
| 30 | const TEMP_CHANNEL: u8 = 16; | 30 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 31 | const CHANNEL: u8 = 16; | ||
| 32 | } | ||
| 31 | 33 | ||
| 32 | #[cfg(stm32h7)] | 34 | #[cfg(stm32h7)] |
| 33 | const VREF_CHANNEL: u8 = 19; | 35 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 36 | const CHANNEL: u8 = 19; | ||
| 37 | } | ||
| 34 | #[cfg(stm32h7)] | 38 | #[cfg(stm32h7)] |
| 35 | const TEMP_CHANNEL: u8 = 18; | 39 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 40 | const CHANNEL: u8 = 18; | ||
| 41 | } | ||
| 36 | 42 | ||
| 37 | // TODO this should be 14 for H7a/b/35 | 43 | // TODO this should be 14 for H7a/b/35 |
| 38 | #[cfg(not(stm32u5))] | 44 | #[cfg(not(stm32u5))] |
| 39 | const VBAT_CHANNEL: u8 = 17; | 45 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 46 | const CHANNEL: u8 = 17; | ||
| 47 | } | ||
| 40 | 48 | ||
| 41 | #[cfg(stm32u5)] | 49 | #[cfg(stm32u5)] |
| 42 | const VREF_CHANNEL: u8 = 0; | 50 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 51 | const CHANNEL: u8 = 0; | ||
| 52 | } | ||
| 43 | #[cfg(stm32u5)] | 53 | #[cfg(stm32u5)] |
| 44 | const TEMP_CHANNEL: u8 = 19; | 54 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 55 | const CHANNEL: u8 = 19; | ||
| 56 | } | ||
| 45 | #[cfg(stm32u5)] | 57 | #[cfg(stm32u5)] |
| 46 | const VBAT_CHANNEL: u8 = 18; | 58 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 47 | 59 | 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 | } | 60 | } |
| 57 | 61 | ||
| 58 | /// Internal temperature channel. | 62 | fn from_ker_ck(frequency: Hertz) -> Presc { |
| 59 | pub struct Temperature; | 63 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; |
| 60 | impl<T: Instance> AdcChannel<T> for Temperature {} | 64 | match raw_prescaler { |
| 61 | impl<T: Instance> SealedAdcChannel<T> for Temperature { | 65 | 0 => Presc::DIV1, |
| 62 | fn channel(&self) -> u8 { | 66 | 1 => Presc::DIV2, |
| 63 | TEMP_CHANNEL | 67 | 2..=3 => Presc::DIV4, |
| 68 | 4..=5 => Presc::DIV6, | ||
| 69 | 6..=7 => Presc::DIV8, | ||
| 70 | 8..=9 => Presc::DIV10, | ||
| 71 | 10..=11 => Presc::DIV12, | ||
| 72 | _ => unimplemented!(), | ||
| 64 | } | 73 | } |
| 65 | } | 74 | } |
| 66 | 75 | ||
| 67 | /// Internal battery voltage channel. | 76 | /// Adc configuration |
| 68 | pub struct Vbat; | 77 | #[derive(Default)] |
| 69 | impl<T: Instance> AdcChannel<T> for Vbat {} | 78 | pub struct AdcConfig { |
| 70 | impl<T: Instance> SealedAdcChannel<T> for Vbat { | 79 | pub resolution: Option<Resolution>, |
| 71 | fn channel(&self) -> u8 { | 80 | pub averaging: Option<Averaging>, |
| 72 | VBAT_CHANNEL | ||
| 73 | } | ||
| 74 | } | 81 | } |
| 75 | 82 | ||
| 76 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | 83 | impl<T: Instance> super::SealedAnyInstance for T { |
| 77 | // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. | 84 | fn dr() -> *mut u16 { |
| 78 | #[allow(unused)] | 85 | T::regs().dr().as_ptr() as *mut u16 |
| 79 | enum Prescaler { | 86 | } |
| 80 | NotDivided, | 87 | |
| 81 | DividedBy2, | 88 | fn enable() { |
| 82 | DividedBy4, | 89 | T::regs().isr().write(|w| w.set_adrdy(true)); |
| 83 | DividedBy6, | 90 | T::regs().cr().modify(|w| w.set_aden(true)); |
| 84 | DividedBy8, | 91 | while !T::regs().isr().read().adrdy() {} |
| 85 | DividedBy10, | 92 | T::regs().isr().write(|w| w.set_adrdy(true)); |
| 86 | DividedBy12, | 93 | } |
| 87 | DividedBy16, | ||
| 88 | DividedBy32, | ||
| 89 | DividedBy64, | ||
| 90 | DividedBy128, | ||
| 91 | DividedBy256, | ||
| 92 | } | ||
| 93 | 94 | ||
| 94 | impl Prescaler { | 95 | fn start() { |
| 95 | fn from_ker_ck(frequency: Hertz) -> Self { | 96 | // Start conversion |
| 96 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | 97 | T::regs().cr().modify(|reg| { |
| 97 | match raw_prescaler { | 98 | reg.set_adstart(true); |
| 98 | 0 => Self::NotDivided, | 99 | }); |
| 99 | 1 => Self::DividedBy2, | 100 | } |
| 100 | 2..=3 => Self::DividedBy4, | 101 | |
| 101 | 4..=5 => Self::DividedBy6, | 102 | fn stop() { |
| 102 | 6..=7 => Self::DividedBy8, | 103 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 103 | 8..=9 => Self::DividedBy10, | 104 | T::regs().cr().modify(|reg| { |
| 104 | 10..=11 => Self::DividedBy12, | 105 | reg.set_adstp(Adstp::STOP); |
| 105 | _ => unimplemented!(), | 106 | }); |
| 107 | while T::regs().cr().read().adstart() {} | ||
| 106 | } | 108 | } |
| 109 | |||
| 110 | // Reset configuration. | ||
| 111 | T::regs().cfgr().modify(|reg| { | ||
| 112 | reg.set_cont(false); | ||
| 113 | reg.set_dmngt(Dmngt::from_bits(0)); | ||
| 114 | }); | ||
| 107 | } | 115 | } |
| 108 | 116 | ||
| 109 | fn divisor(&self) -> u32 { | 117 | fn convert() -> u16 { |
| 110 | match self { | 118 | T::regs().isr().modify(|reg| { |
| 111 | Prescaler::NotDivided => 1, | 119 | reg.set_eos(true); |
| 112 | Prescaler::DividedBy2 => 2, | 120 | reg.set_eoc(true); |
| 113 | Prescaler::DividedBy4 => 4, | 121 | }); |
| 114 | Prescaler::DividedBy6 => 6, | 122 | |
| 115 | Prescaler::DividedBy8 => 8, | 123 | // Start conversion |
| 116 | Prescaler::DividedBy10 => 10, | 124 | T::regs().cr().modify(|reg| { |
| 117 | Prescaler::DividedBy12 => 12, | 125 | reg.set_adstart(true); |
| 118 | Prescaler::DividedBy16 => 16, | 126 | }); |
| 119 | Prescaler::DividedBy32 => 32, | 127 | |
| 120 | Prescaler::DividedBy64 => 64, | 128 | while !T::regs().isr().read().eos() { |
| 121 | Prescaler::DividedBy128 => 128, | 129 | // spin |
| 122 | Prescaler::DividedBy256 => 256, | ||
| 123 | } | 130 | } |
| 131 | |||
| 132 | T::regs().dr().read().0 as u16 | ||
| 124 | } | 133 | } |
| 125 | 134 | ||
| 126 | fn presc(&self) -> Presc { | 135 | fn configure_dma(conversion_mode: ConversionMode) { |
| 127 | match self { | 136 | match conversion_mode { |
| 128 | Prescaler::NotDivided => Presc::DIV1, | 137 | ConversionMode::Singular => { |
| 129 | Prescaler::DividedBy2 => Presc::DIV2, | 138 | T::regs().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 | T::regs().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(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 | T::regs().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 | T::regs().smpr(0).modify(|reg| reg.set_smp(channel as _, sample_time)); |
| 155 | Samples128, | 162 | } else { |
| 156 | Samples256, | 163 | T::regs() |
| 157 | Samples512, | 164 | .smpr(1) |
| 158 | Samples1024, | 165 | .modify(|reg| reg.set_smp((channel - 10) as _, sample_time)); |
| 166 | } | ||
| 167 | |||
| 168 | #[cfg(any(stm32h7, stm32u5))] | ||
| 169 | { | ||
| 170 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 171 | T::regs() | ||
| 172 | .pcsel() | ||
| 173 | .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); | ||
| 174 | } | ||
| 175 | |||
| 176 | match i { | ||
| 177 | 0..=3 => { | ||
| 178 | T::regs().sqr1().modify(|w| { | ||
| 179 | w.set_sq(i, channel); | ||
| 180 | }); | ||
| 181 | } | ||
| 182 | 4..=8 => { | ||
| 183 | T::regs().sqr2().modify(|w| { | ||
| 184 | w.set_sq(i - 4, channel); | ||
| 185 | }); | ||
| 186 | } | ||
| 187 | 9..=13 => { | ||
| 188 | T::regs().sqr3().modify(|w| { | ||
| 189 | w.set_sq(i - 9, channel); | ||
| 190 | }); | ||
| 191 | } | ||
| 192 | 14..=15 => { | ||
| 193 | T::regs().sqr4().modify(|w| { | ||
| 194 | w.set_sq(i - 14, channel); | ||
| 195 | }); | ||
| 196 | } | ||
| 197 | _ => unreachable!(), | ||
| 198 | } | ||
| 199 | } | ||
| 200 | } | ||
| 159 | } | 201 | } |
| 160 | 202 | ||
| 161 | impl<'d, T: Instance> Adc<'d, T> { | 203 | impl<'d, T: Instance + super::AnyInstance> Adc<'d, T> { |
| 204 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { | ||
| 205 | let s = Self::new(adc); | ||
| 206 | |||
| 207 | // Set the ADC resolution. | ||
| 208 | if let Some(resolution) = config.resolution { | ||
| 209 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 210 | } | ||
| 211 | |||
| 212 | // Set hardware averaging. | ||
| 213 | if let Some(averaging) = config.averaging { | ||
| 214 | let (enable, samples, right_shift) = match averaging { | ||
| 215 | Averaging::Disabled => (false, 0, 0), | ||
| 216 | Averaging::Samples2 => (true, 1, 1), | ||
| 217 | Averaging::Samples4 => (true, 3, 2), | ||
| 218 | Averaging::Samples8 => (true, 7, 3), | ||
| 219 | Averaging::Samples16 => (true, 15, 4), | ||
| 220 | Averaging::Samples32 => (true, 31, 5), | ||
| 221 | Averaging::Samples64 => (true, 63, 6), | ||
| 222 | Averaging::Samples128 => (true, 127, 7), | ||
| 223 | Averaging::Samples256 => (true, 255, 8), | ||
| 224 | Averaging::Samples512 => (true, 511, 9), | ||
| 225 | Averaging::Samples1024 => (true, 1023, 10), | ||
| 226 | }; | ||
| 227 | |||
| 228 | T::regs().cfgr2().modify(|reg| { | ||
| 229 | reg.set_rovse(enable); | ||
| 230 | reg.set_ovsr(samples); | ||
| 231 | reg.set_ovss(right_shift); | ||
| 232 | }) | ||
| 233 | } | ||
| 234 | |||
| 235 | s | ||
| 236 | } | ||
| 237 | |||
| 162 | /// Create a new ADC driver. | 238 | /// Create a new ADC driver. |
| 163 | pub fn new(adc: Peri<'d, T>) -> Self { | 239 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 164 | rcc::enable_and_reset::<T>(); | 240 | rcc::enable_and_reset::<T>(); |
| 165 | 241 | ||
| 166 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 242 | let prescaler = from_ker_ck(T::frequency()); |
| 167 | 243 | ||
| 168 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 244 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 169 | 245 | ||
| 170 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 246 | let frequency = T::frequency() / prescaler; |
| 171 | info!("ADC frequency set to {}", frequency); | 247 | info!("ADC frequency set to {}", frequency); |
| 172 | 248 | ||
| 173 | if frequency > MAX_ADC_CLK_FREQ { | 249 | if frequency > MAX_ADC_CLK_FREQ { |
| @@ -190,40 +266,20 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 190 | }; | 266 | }; |
| 191 | T::regs().cr().modify(|w| w.set_boost(boost)); | 267 | T::regs().cr().modify(|w| w.set_boost(boost)); |
| 192 | } | 268 | } |
| 193 | let mut s = Self { | ||
| 194 | adc, | ||
| 195 | sample_time: SampleTime::from_bits(0), | ||
| 196 | }; | ||
| 197 | s.power_up(); | ||
| 198 | s.configure_differential_inputs(); | ||
| 199 | |||
| 200 | s.calibrate(); | ||
| 201 | blocking_delay_us(1); | ||
| 202 | |||
| 203 | s.enable(); | ||
| 204 | s.configure(); | ||
| 205 | |||
| 206 | s | ||
| 207 | } | ||
| 208 | 269 | ||
| 209 | fn power_up(&mut self) { | ||
| 210 | T::regs().cr().modify(|reg| { | 270 | T::regs().cr().modify(|reg| { |
| 211 | reg.set_deeppwd(false); | 271 | reg.set_deeppwd(false); |
| 212 | reg.set_advregen(true); | 272 | reg.set_advregen(true); |
| 213 | }); | 273 | }); |
| 214 | 274 | ||
| 215 | blocking_delay_us(10); | 275 | blocking_delay_us(10); |
| 216 | } | ||
| 217 | 276 | ||
| 218 | fn configure_differential_inputs(&mut self) { | ||
| 219 | T::regs().difsel().modify(|w| { | 277 | T::regs().difsel().modify(|w| { |
| 220 | for n in 0..20 { | 278 | for n in 0..20 { |
| 221 | w.set_difsel(n, Difsel::SINGLE_ENDED); | 279 | w.set_difsel(n, Difsel::SINGLE_ENDED); |
| 222 | } | 280 | } |
| 223 | }); | 281 | }); |
| 224 | } | ||
| 225 | 282 | ||
| 226 | fn calibrate(&mut self) { | ||
| 227 | T::regs().cr().modify(|w| { | 283 | T::regs().cr().modify(|w| { |
| 228 | #[cfg(not(adc_u5))] | 284 | #[cfg(not(adc_u5))] |
| 229 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); | 285 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); |
| @@ -233,21 +289,18 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 233 | T::regs().cr().modify(|w| w.set_adcal(true)); | 289 | T::regs().cr().modify(|w| w.set_adcal(true)); |
| 234 | 290 | ||
| 235 | while T::regs().cr().read().adcal() {} | 291 | while T::regs().cr().read().adcal() {} |
| 236 | } | ||
| 237 | 292 | ||
| 238 | fn enable(&mut self) { | 293 | blocking_delay_us(1); |
| 239 | T::regs().isr().write(|w| w.set_adrdy(true)); | 294 | |
| 240 | T::regs().cr().modify(|w| w.set_aden(true)); | 295 | T::enable(); |
| 241 | while !T::regs().isr().read().adrdy() {} | ||
| 242 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 243 | } | ||
| 244 | 296 | ||
| 245 | fn configure(&mut self) { | ||
| 246 | // single conversion mode, software trigger | 297 | // single conversion mode, software trigger |
| 247 | T::regs().cfgr().modify(|w| { | 298 | T::regs().cfgr().modify(|w| { |
| 248 | w.set_cont(false); | 299 | w.set_cont(false); |
| 249 | w.set_exten(Exten::DISABLED); | 300 | w.set_exten(Exten::DISABLED); |
| 250 | }); | 301 | }); |
| 302 | |||
| 303 | Self { adc } | ||
| 251 | } | 304 | } |
| 252 | 305 | ||
| 253 | /// Enable reading the voltage reference internal channel. | 306 | /// Enable reading the voltage reference internal channel. |
| @@ -276,228 +329,4 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 276 | 329 | ||
| 277 | Vbat {} | 330 | Vbat {} |
| 278 | } | 331 | } |
| 279 | |||
| 280 | /// Set the ADC sample time. | ||
| 281 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 282 | self.sample_time = sample_time; | ||
| 283 | } | ||
| 284 | |||
| 285 | /// Get the ADC sample time. | ||
| 286 | pub fn sample_time(&self) -> SampleTime { | ||
| 287 | self.sample_time | ||
| 288 | } | ||
| 289 | |||
| 290 | /// Set the ADC resolution. | ||
| 291 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 292 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 293 | } | ||
| 294 | |||
| 295 | /// Set hardware averaging. | ||
| 296 | pub fn set_averaging(&mut self, averaging: Averaging) { | ||
| 297 | let (enable, samples, right_shift) = match averaging { | ||
| 298 | Averaging::Disabled => (false, 0, 0), | ||
| 299 | Averaging::Samples2 => (true, 1, 1), | ||
| 300 | Averaging::Samples4 => (true, 3, 2), | ||
| 301 | Averaging::Samples8 => (true, 7, 3), | ||
| 302 | Averaging::Samples16 => (true, 15, 4), | ||
| 303 | Averaging::Samples32 => (true, 31, 5), | ||
| 304 | Averaging::Samples64 => (true, 63, 6), | ||
| 305 | Averaging::Samples128 => (true, 127, 7), | ||
| 306 | Averaging::Samples256 => (true, 255, 8), | ||
| 307 | Averaging::Samples512 => (true, 511, 9), | ||
| 308 | Averaging::Samples1024 => (true, 1023, 10), | ||
| 309 | }; | ||
| 310 | |||
| 311 | T::regs().cfgr2().modify(|reg| { | ||
| 312 | reg.set_rovse(enable); | ||
| 313 | reg.set_ovsr(samples); | ||
| 314 | reg.set_ovss(right_shift); | ||
| 315 | }) | ||
| 316 | } | ||
| 317 | |||
| 318 | /// Perform a single conversion. | ||
| 319 | fn convert(&mut self) -> u16 { | ||
| 320 | T::regs().isr().modify(|reg| { | ||
| 321 | reg.set_eos(true); | ||
| 322 | reg.set_eoc(true); | ||
| 323 | }); | ||
| 324 | |||
| 325 | // Start conversion | ||
| 326 | T::regs().cr().modify(|reg| { | ||
| 327 | reg.set_adstart(true); | ||
| 328 | }); | ||
| 329 | |||
| 330 | while !T::regs().isr().read().eos() { | ||
| 331 | // spin | ||
| 332 | } | ||
| 333 | |||
| 334 | T::regs().dr().read().0 as u16 | ||
| 335 | } | ||
| 336 | |||
| 337 | /// Read an ADC channel. | ||
| 338 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 339 | self.read_channel(channel) | ||
| 340 | } | ||
| 341 | |||
| 342 | /// Read one or multiple ADC channels using DMA. | ||
| 343 | /// | ||
| 344 | /// `sequence` iterator and `readings` must have the same length. | ||
| 345 | /// | ||
| 346 | /// Example | ||
| 347 | /// ```rust,ignore | ||
| 348 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 349 | /// | ||
| 350 | /// let mut adc = Adc::new(p.ADC1); | ||
| 351 | /// let mut adc_pin0 = p.PA0.into(); | ||
| 352 | /// let mut adc_pin2 = p.PA2.into(); | ||
| 353 | /// let mut measurements = [0u16; 2]; | ||
| 354 | /// | ||
| 355 | /// adc.read( | ||
| 356 | /// p.DMA2_CH0.reborrow(), | ||
| 357 | /// [ | ||
| 358 | /// (&mut *adc_pin0, SampleTime::CYCLES112), | ||
| 359 | /// (&mut *adc_pin2, SampleTime::CYCLES112), | ||
| 360 | /// ] | ||
| 361 | /// .into_iter(), | ||
| 362 | /// &mut measurements, | ||
| 363 | /// ) | ||
| 364 | /// .await; | ||
| 365 | /// defmt::info!("measurements: {}", measurements); | ||
| 366 | /// ``` | ||
| 367 | pub async fn read( | ||
| 368 | &mut self, | ||
| 369 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 370 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 371 | readings: &mut [u16], | ||
| 372 | ) { | ||
| 373 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 374 | assert!( | ||
| 375 | sequence.len() == readings.len(), | ||
| 376 | "Sequence length must be equal to readings length" | ||
| 377 | ); | ||
| 378 | assert!( | ||
| 379 | sequence.len() <= 16, | ||
| 380 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 381 | ); | ||
| 382 | |||
| 383 | // Ensure no conversions are ongoing | ||
| 384 | Self::cancel_conversions(); | ||
| 385 | |||
| 386 | // Set sequence length | ||
| 387 | T::regs().sqr1().modify(|w| { | ||
| 388 | w.set_l(sequence.len() as u8 - 1); | ||
| 389 | }); | ||
| 390 | |||
| 391 | // Configure channels and ranks | ||
| 392 | for (i, (channel, sample_time)) in sequence.enumerate() { | ||
| 393 | Self::configure_channel(channel, sample_time); | ||
| 394 | match i { | ||
| 395 | 0..=3 => { | ||
| 396 | T::regs().sqr1().modify(|w| { | ||
| 397 | w.set_sq(i, channel.channel()); | ||
| 398 | }); | ||
| 399 | } | ||
| 400 | 4..=8 => { | ||
| 401 | T::regs().sqr2().modify(|w| { | ||
| 402 | w.set_sq(i - 4, channel.channel()); | ||
| 403 | }); | ||
| 404 | } | ||
| 405 | 9..=13 => { | ||
| 406 | T::regs().sqr3().modify(|w| { | ||
| 407 | w.set_sq(i - 9, channel.channel()); | ||
| 408 | }); | ||
| 409 | } | ||
| 410 | 14..=15 => { | ||
| 411 | T::regs().sqr4().modify(|w| { | ||
| 412 | w.set_sq(i - 14, channel.channel()); | ||
| 413 | }); | ||
| 414 | } | ||
| 415 | _ => unreachable!(), | ||
| 416 | } | ||
| 417 | } | ||
| 418 | |||
| 419 | // Set continuous mode with oneshot dma. | ||
| 420 | // Clear overrun flag before starting transfer. | ||
| 421 | |||
| 422 | T::regs().isr().modify(|reg| { | ||
| 423 | reg.set_ovr(true); | ||
| 424 | }); | ||
| 425 | T::regs().cfgr().modify(|reg| { | ||
| 426 | reg.set_cont(true); | ||
| 427 | reg.set_dmngt(Dmngt::DMA_ONE_SHOT); | ||
| 428 | }); | ||
| 429 | |||
| 430 | let request = rx_dma.request(); | ||
| 431 | let transfer = unsafe { | ||
| 432 | Transfer::new_read( | ||
| 433 | rx_dma, | ||
| 434 | request, | ||
| 435 | T::regs().dr().as_ptr() as *mut u16, | ||
| 436 | readings, | ||
| 437 | Default::default(), | ||
| 438 | ) | ||
| 439 | }; | ||
| 440 | |||
| 441 | // Start conversion | ||
| 442 | T::regs().cr().modify(|reg| { | ||
| 443 | reg.set_adstart(true); | ||
| 444 | }); | ||
| 445 | |||
| 446 | // Wait for conversion sequence to finish. | ||
| 447 | transfer.await; | ||
| 448 | |||
| 449 | // Ensure conversions are finished. | ||
| 450 | Self::cancel_conversions(); | ||
| 451 | |||
| 452 | // Reset configuration. | ||
| 453 | T::regs().cfgr().modify(|reg| { | ||
| 454 | reg.set_cont(false); | ||
| 455 | reg.set_dmngt(Dmngt::from_bits(0)); | ||
| 456 | }); | ||
| 457 | } | ||
| 458 | |||
| 459 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | ||
| 460 | channel.setup(); | ||
| 461 | |||
| 462 | let channel = channel.channel(); | ||
| 463 | |||
| 464 | Self::set_channel_sample_time(channel, sample_time); | ||
| 465 | |||
| 466 | #[cfg(any(stm32h7, stm32u5))] | ||
| 467 | { | ||
| 468 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 469 | T::regs() | ||
| 470 | .pcsel() | ||
| 471 | .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); | ||
| 472 | } | ||
| 473 | } | ||
| 474 | |||
| 475 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 476 | Self::configure_channel(channel, self.sample_time); | ||
| 477 | |||
| 478 | T::regs().sqr1().modify(|reg| { | ||
| 479 | reg.set_sq(0, channel.channel()); | ||
| 480 | reg.set_l(0); | ||
| 481 | }); | ||
| 482 | |||
| 483 | self.convert() | ||
| 484 | } | ||
| 485 | |||
| 486 | fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | ||
| 487 | let sample_time = sample_time.into(); | ||
| 488 | if ch <= 9 { | ||
| 489 | T::regs().smpr(0).modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 490 | } else { | ||
| 491 | T::regs().smpr(1).modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 492 | } | ||
| 493 | } | ||
| 494 | |||
| 495 | fn cancel_conversions() { | ||
| 496 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 497 | T::regs().cr().modify(|reg| { | ||
| 498 | reg.set_adstp(Adstp::STOP); | ||
| 499 | }); | ||
| 500 | while T::regs().cr().read().adstart() {} | ||
| 501 | } | ||
| 502 | } | ||
| 503 | } | 332 | } |
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/dma/gpdma/mod.rs b/embassy-stm32/src/dma/gpdma/mod.rs index 3e117c331..106558d20 100644 --- a/embassy-stm32/src/dma/gpdma/mod.rs +++ b/embassy-stm32/src/dma/gpdma/mod.rs | |||
| @@ -136,6 +136,7 @@ pub(crate) unsafe fn init(cs: critical_section::CriticalSection, irq_priority: c | |||
| 136 | 136 | ||
| 137 | impl AnyChannel { | 137 | impl AnyChannel { |
| 138 | /// Safety: Must be called with a matching set of parameters for a valid dma channel | 138 | /// Safety: Must be called with a matching set of parameters for a valid dma channel |
| 139 | #[cfg(not(stm32n6))] | ||
| 139 | pub(crate) unsafe fn on_irq(&self) { | 140 | pub(crate) unsafe fn on_irq(&self) { |
| 140 | let info = self.info(); | 141 | let info = self.info(); |
| 141 | #[cfg(feature = "_dual-core")] | 142 | #[cfg(feature = "_dual-core")] |
diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 297fa3674..de7a2c175 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs | |||
| @@ -46,9 +46,11 @@ pub type Request = u8; | |||
| 46 | pub type Request = (); | 46 | pub type Request = (); |
| 47 | 47 | ||
| 48 | pub(crate) trait SealedChannel { | 48 | pub(crate) trait SealedChannel { |
| 49 | #[cfg(not(stm32n6))] | ||
| 49 | fn id(&self) -> u8; | 50 | fn id(&self) -> u8; |
| 50 | } | 51 | } |
| 51 | 52 | ||
| 53 | #[cfg(not(stm32n6))] | ||
| 52 | pub(crate) trait ChannelInterrupt { | 54 | pub(crate) trait ChannelInterrupt { |
| 53 | #[cfg_attr(not(feature = "rt"), allow(unused))] | 55 | #[cfg_attr(not(feature = "rt"), allow(unused))] |
| 54 | unsafe fn on_irq(); | 56 | unsafe fn on_irq(); |
| @@ -58,6 +60,7 @@ pub(crate) trait ChannelInterrupt { | |||
| 58 | #[allow(private_bounds)] | 60 | #[allow(private_bounds)] |
| 59 | pub trait Channel: SealedChannel + PeripheralType + Into<AnyChannel> + 'static {} | 61 | pub trait Channel: SealedChannel + PeripheralType + Into<AnyChannel> + 'static {} |
| 60 | 62 | ||
| 63 | #[cfg(not(stm32n6))] | ||
| 61 | macro_rules! dma_channel_impl { | 64 | macro_rules! dma_channel_impl { |
| 62 | ($channel_peri:ident, $index:expr) => { | 65 | ($channel_peri:ident, $index:expr) => { |
| 63 | impl crate::dma::SealedChannel for crate::peripherals::$channel_peri { | 66 | impl crate::dma::SealedChannel for crate::peripherals::$channel_peri { |
| @@ -96,6 +99,7 @@ impl AnyChannel { | |||
| 96 | } | 99 | } |
| 97 | 100 | ||
| 98 | impl SealedChannel for AnyChannel { | 101 | impl SealedChannel for AnyChannel { |
| 102 | #[cfg(not(stm32n6))] | ||
| 99 | fn id(&self) -> u8 { | 103 | fn id(&self) -> u8 { |
| 100 | self.id | 104 | self.id |
| 101 | } | 105 | } |
diff --git a/embassy-stm32/src/dsihost.rs b/embassy-stm32/src/dsihost.rs index 59a2cbcdb..b8945820c 100644 --- a/embassy-stm32/src/dsihost.rs +++ b/embassy-stm32/src/dsihost.rs | |||
| @@ -5,18 +5,11 @@ 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::{Peri, peripherals}; | 11 | use crate::{Peri, peripherals}; |
| 11 | 12 | ||
| 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 | |||
| 20 | /// PacketTypes extracted from CubeMX | 13 | /// PacketTypes extracted from CubeMX |
| 21 | #[repr(u8)] | 14 | #[repr(u8)] |
| 22 | #[allow(dead_code)] | 15 | #[allow(dead_code)] |
| @@ -334,7 +327,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 334 | if T::regs().gpsr().read().cmdfe() { | 327 | if T::regs().gpsr().read().cmdfe() { |
| 335 | return Ok(()); | 328 | return Ok(()); |
| 336 | } | 329 | } |
| 337 | blocking_delay_ms(1); | 330 | block_for_us(1_000); |
| 338 | } | 331 | } |
| 339 | Err(Error::FifoTimeout) | 332 | Err(Error::FifoTimeout) |
| 340 | } | 333 | } |
| @@ -345,7 +338,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 345 | if !T::regs().gpsr().read().cmdff() { | 338 | if !T::regs().gpsr().read().cmdff() { |
| 346 | return Ok(()); | 339 | return Ok(()); |
| 347 | } | 340 | } |
| 348 | blocking_delay_ms(1); | 341 | block_for_us(1_000); |
| 349 | } | 342 | } |
| 350 | Err(Error::FifoTimeout) | 343 | Err(Error::FifoTimeout) |
| 351 | } | 344 | } |
| @@ -356,7 +349,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 356 | if !self.read_busy() { | 349 | if !self.read_busy() { |
| 357 | return Ok(()); | 350 | return Ok(()); |
| 358 | } | 351 | } |
| 359 | blocking_delay_ms(1); | 352 | block_for_us(1_000); |
| 360 | } | 353 | } |
| 361 | Err(Error::ReadTimeout) | 354 | Err(Error::ReadTimeout) |
| 362 | } | 355 | } |
| @@ -367,7 +360,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 367 | if !T::regs().gpsr().read().prdfe() { | 360 | if !T::regs().gpsr().read().prdfe() { |
| 368 | return Ok(()); | 361 | return Ok(()); |
| 369 | } | 362 | } |
| 370 | blocking_delay_ms(1); | 363 | block_for_us(1_000); |
| 371 | } | 364 | } |
| 372 | Err(Error::FifoTimeout) | 365 | Err(Error::FifoTimeout) |
| 373 | } | 366 | } |
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..947874d7f 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 { |
| @@ -76,19 +77,6 @@ impl GenericPhy { | |||
| 76 | } | 77 | } |
| 77 | } | 78 | } |
| 78 | 79 | ||
| 79 | // TODO: Factor out to shared functionality | ||
| 80 | fn blocking_delay_us(us: u32) { | ||
| 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 { | 80 | impl Phy for GenericPhy { |
| 93 | fn phy_reset<S: StationManagement>(&mut self, sm: &mut S) { | 81 | fn phy_reset<S: StationManagement>(&mut self, sm: &mut S) { |
| 94 | // Detect SMI address | 82 | // Detect SMI address |
diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 2f5c3406a..cb46d362c 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs | |||
| @@ -8,7 +8,7 @@ use core::task::{Context, Poll}; | |||
| 8 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; | 8 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; |
| 9 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 10 | 10 | ||
| 11 | use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, Pull}; | 11 | use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, PinNumber, Pull}; |
| 12 | use crate::pac::EXTI; | 12 | use crate::pac::EXTI; |
| 13 | use crate::pac::exti::regs::Lines; | 13 | use crate::pac::exti::regs::Lines; |
| 14 | use crate::{Peri, interrupt, pac, peripherals}; | 14 | use crate::{Peri, interrupt, pac, peripherals}; |
| @@ -31,11 +31,11 @@ fn cpu_regs() -> pac::exti::Exti { | |||
| 31 | EXTI | 31 | EXTI |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50)))] | 34 | #[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 { | 35 | fn exticr_regs() -> pac::syscfg::Syscfg { |
| 36 | pac::SYSCFG | 36 | pac::SYSCFG |
| 37 | } | 37 | } |
| 38 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] | 38 | #[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 { | 39 | fn exticr_regs() -> pac::exti::Exti { |
| 40 | EXTI | 40 | EXTI |
| 41 | } | 41 | } |
| @@ -45,9 +45,9 @@ fn exticr_regs() -> pac::afio::Afio { | |||
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | unsafe fn on_irq() { | 47 | unsafe fn on_irq() { |
| 48 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] | 48 | #[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; | 49 | let bits = EXTI.pr(0).read().0; |
| 50 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] | 50 | #[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; | 51 | let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0; |
| 52 | 52 | ||
| 53 | // We don't handle or change any EXTI lines above 16. | 53 | // We don't handle or change any EXTI lines above 16. |
| @@ -62,9 +62,9 @@ unsafe fn on_irq() { | |||
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | // Clear pending | 64 | // Clear pending |
| 65 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] | 65 | #[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)); | 66 | EXTI.pr(0).write_value(Lines(bits)); |
| 67 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] | 67 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6))] |
| 68 | { | 68 | { |
| 69 | EXTI.rpr(0).write_value(Lines(bits)); | 69 | EXTI.rpr(0).write_value(Lines(bits)); |
| 70 | EXTI.fpr(0).write_value(Lines(bits)); | 70 | EXTI.fpr(0).write_value(Lines(bits)); |
| @@ -226,12 +226,12 @@ impl<'d> embedded_hal_async::digital::Wait for ExtiInput<'d> { | |||
| 226 | 226 | ||
| 227 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 227 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 228 | struct ExtiInputFuture<'a> { | 228 | struct ExtiInputFuture<'a> { |
| 229 | pin: u8, | 229 | pin: PinNumber, |
| 230 | phantom: PhantomData<&'a mut AnyPin>, | 230 | phantom: PhantomData<&'a mut AnyPin>, |
| 231 | } | 231 | } |
| 232 | 232 | ||
| 233 | impl<'a> ExtiInputFuture<'a> { | 233 | impl<'a> ExtiInputFuture<'a> { |
| 234 | fn new(pin: u8, port: u8, rising: bool, falling: bool) -> Self { | 234 | fn new(pin: PinNumber, port: PinNumber, rising: bool, falling: bool) -> Self { |
| 235 | critical_section::with(|_| { | 235 | critical_section::with(|_| { |
| 236 | let pin = pin as usize; | 236 | let pin = pin as usize; |
| 237 | exticr_regs().exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port)); | 237 | exticr_regs().exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port)); |
| @@ -239,9 +239,9 @@ impl<'a> ExtiInputFuture<'a> { | |||
| 239 | EXTI.ftsr(0).modify(|w| w.set_line(pin, falling)); | 239 | EXTI.ftsr(0).modify(|w| w.set_line(pin, falling)); |
| 240 | 240 | ||
| 241 | // clear pending bit | 241 | // clear pending bit |
| 242 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] | 242 | #[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)); | 243 | 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))] | 244 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6))] |
| 245 | { | 245 | { |
| 246 | EXTI.rpr(0).write(|w| w.set_line(pin, true)); | 246 | EXTI.rpr(0).write(|w| w.set_line(pin, true)); |
| 247 | EXTI.fpr(0).write(|w| w.set_line(pin, true)); | 247 | EXTI.fpr(0).write(|w| w.set_line(pin, true)); |
| @@ -334,20 +334,20 @@ trait SealedChannel {} | |||
| 334 | #[allow(private_bounds)] | 334 | #[allow(private_bounds)] |
| 335 | pub trait Channel: PeripheralType + SealedChannel + Sized { | 335 | pub trait Channel: PeripheralType + SealedChannel + Sized { |
| 336 | /// Get the EXTI channel number. | 336 | /// Get the EXTI channel number. |
| 337 | fn number(&self) -> u8; | 337 | fn number(&self) -> PinNumber; |
| 338 | } | 338 | } |
| 339 | 339 | ||
| 340 | /// Type-erased EXTI channel. | 340 | /// Type-erased EXTI channel. |
| 341 | /// | 341 | /// |
| 342 | /// This represents ownership over any EXTI channel, known at runtime. | 342 | /// This represents ownership over any EXTI channel, known at runtime. |
| 343 | pub struct AnyChannel { | 343 | pub struct AnyChannel { |
| 344 | number: u8, | 344 | number: PinNumber, |
| 345 | } | 345 | } |
| 346 | 346 | ||
| 347 | impl_peripheral!(AnyChannel); | 347 | impl_peripheral!(AnyChannel); |
| 348 | impl SealedChannel for AnyChannel {} | 348 | impl SealedChannel for AnyChannel {} |
| 349 | impl Channel for AnyChannel { | 349 | impl Channel for AnyChannel { |
| 350 | fn number(&self) -> u8 { | 350 | fn number(&self) -> PinNumber { |
| 351 | self.number | 351 | self.number |
| 352 | } | 352 | } |
| 353 | } | 353 | } |
| @@ -356,7 +356,7 @@ macro_rules! impl_exti { | |||
| 356 | ($type:ident, $number:expr) => { | 356 | ($type:ident, $number:expr) => { |
| 357 | impl SealedChannel for peripherals::$type {} | 357 | impl SealedChannel for peripherals::$type {} |
| 358 | impl Channel for peripherals::$type { | 358 | impl Channel for peripherals::$type { |
| 359 | fn number(&self) -> u8 { | 359 | fn number(&self) -> PinNumber { |
| 360 | $number | 360 | $number |
| 361 | } | 361 | } |
| 362 | } | 362 | } |
| @@ -364,7 +364,7 @@ macro_rules! impl_exti { | |||
| 364 | impl From<peripherals::$type> for AnyChannel { | 364 | impl From<peripherals::$type> for AnyChannel { |
| 365 | fn from(val: peripherals::$type) -> Self { | 365 | fn from(val: peripherals::$type) -> Self { |
| 366 | Self { | 366 | Self { |
| 367 | number: val.number() as u8, | 367 | number: val.number() as PinNumber, |
| 368 | } | 368 | } |
| 369 | } | 369 | } |
| 370 | } | 370 | } |
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 b595938a6..60d00e766 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs | |||
| @@ -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/g.rs b/embassy-stm32/src/flash/g.rs index d026541a4..d7ba2f571 100644 --- a/embassy-stm32/src/flash/g.rs +++ b/embassy-stm32/src/flash/g.rs | |||
| @@ -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 | }); |
diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 3e74d857a..39cd9b3a9 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs | |||
| @@ -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/gpio.rs b/embassy-stm32/src/gpio.rs index b55baffdc..17c5a9962 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs | |||
| @@ -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; |
| @@ -688,7 +688,7 @@ fn set_as_analog(pin_port: u8) { | |||
| 688 | } | 688 | } |
| 689 | 689 | ||
| 690 | #[inline(never)] | 690 | #[inline(never)] |
| 691 | fn get_pull(pin_port: u8) -> Pull { | 691 | fn get_pull(pin_port: PinNumber) -> Pull { |
| 692 | let pin = unsafe { AnyPin::steal(pin_port) }; | 692 | let pin = unsafe { AnyPin::steal(pin_port) }; |
| 693 | let r = pin.block(); | 693 | let r = pin.block(); |
| 694 | let n = pin._pin() as usize; | 694 | let n = pin._pin() as usize; |
| @@ -727,15 +727,15 @@ pub struct AfioRemapBool<const V: bool>; | |||
| 727 | pub struct AfioRemapNotApplicable; | 727 | pub struct AfioRemapNotApplicable; |
| 728 | 728 | ||
| 729 | pub(crate) trait SealedPin { | 729 | pub(crate) trait SealedPin { |
| 730 | fn pin_port(&self) -> u8; | 730 | fn pin_port(&self) -> PinNumber; |
| 731 | 731 | ||
| 732 | #[inline] | 732 | #[inline] |
| 733 | fn _pin(&self) -> u8 { | 733 | fn _pin(&self) -> PinNumber { |
| 734 | self.pin_port() % 16 | 734 | self.pin_port() % 16 |
| 735 | } | 735 | } |
| 736 | 736 | ||
| 737 | #[inline] | 737 | #[inline] |
| 738 | fn _port(&self) -> u8 { | 738 | fn _port(&self) -> PinNumber { |
| 739 | self.pin_port() / 16 | 739 | self.pin_port() / 16 |
| 740 | } | 740 | } |
| 741 | 741 | ||
| @@ -798,6 +798,20 @@ pub(crate) trait SealedPin { | |||
| 798 | } | 798 | } |
| 799 | } | 799 | } |
| 800 | 800 | ||
| 801 | /// GPIO pin number type. | ||
| 802 | /// | ||
| 803 | /// Some chips have a total number of ports that exceeds 8, a larger integer | ||
| 804 | /// is needed to hold the total pin number `(ports * number)`. | ||
| 805 | #[cfg(not(stm32n6))] | ||
| 806 | pub type PinNumber = u8; | ||
| 807 | |||
| 808 | /// GPIO pin number type. | ||
| 809 | /// | ||
| 810 | /// Some chips have a total number of ports that exceeds 8, a larger integer | ||
| 811 | /// is needed to hold the total pin number `(ports * number)`. | ||
| 812 | #[cfg(stm32n6)] | ||
| 813 | pub type PinNumber = u16; | ||
| 814 | |||
| 801 | /// GPIO pin trait. | 815 | /// GPIO pin trait. |
| 802 | #[allow(private_bounds)] | 816 | #[allow(private_bounds)] |
| 803 | pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static { | 817 | pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static { |
| @@ -809,20 +823,20 @@ pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static { | |||
| 809 | 823 | ||
| 810 | /// Number of the pin within the port (0..31) | 824 | /// Number of the pin within the port (0..31) |
| 811 | #[inline] | 825 | #[inline] |
| 812 | fn pin(&self) -> u8 { | 826 | fn pin(&self) -> PinNumber { |
| 813 | self._pin() | 827 | self._pin() |
| 814 | } | 828 | } |
| 815 | 829 | ||
| 816 | /// Port of the pin | 830 | /// Port of the pin |
| 817 | #[inline] | 831 | #[inline] |
| 818 | fn port(&self) -> u8 { | 832 | fn port(&self) -> PinNumber { |
| 819 | self._port() | 833 | self._port() |
| 820 | } | 834 | } |
| 821 | } | 835 | } |
| 822 | 836 | ||
| 823 | /// Type-erased GPIO pin | 837 | /// Type-erased GPIO pin |
| 824 | pub struct AnyPin { | 838 | pub struct AnyPin { |
| 825 | pin_port: u8, | 839 | pin_port: PinNumber, |
| 826 | } | 840 | } |
| 827 | 841 | ||
| 828 | impl AnyPin { | 842 | impl AnyPin { |
| @@ -830,12 +844,12 @@ impl AnyPin { | |||
| 830 | /// | 844 | /// |
| 831 | /// `pin_port` is `port_num * 16 + pin_num`, where `port_num` is 0 for port `A`, 1 for port `B`, etc... | 845 | /// `pin_port` is `port_num * 16 + pin_num`, where `port_num` is 0 for port `A`, 1 for port `B`, etc... |
| 832 | #[inline] | 846 | #[inline] |
| 833 | pub unsafe fn steal(pin_port: u8) -> Peri<'static, Self> { | 847 | pub unsafe fn steal(pin_port: PinNumber) -> Peri<'static, Self> { |
| 834 | Peri::new_unchecked(Self { pin_port }) | 848 | Peri::new_unchecked(Self { pin_port }) |
| 835 | } | 849 | } |
| 836 | 850 | ||
| 837 | #[inline] | 851 | #[inline] |
| 838 | fn _port(&self) -> u8 { | 852 | fn _port(&self) -> PinNumber { |
| 839 | self.pin_port / 16 | 853 | self.pin_port / 16 |
| 840 | } | 854 | } |
| 841 | 855 | ||
| @@ -854,7 +868,7 @@ impl Pin for AnyPin { | |||
| 854 | } | 868 | } |
| 855 | impl SealedPin for AnyPin { | 869 | impl SealedPin for AnyPin { |
| 856 | #[inline] | 870 | #[inline] |
| 857 | fn pin_port(&self) -> u8 { | 871 | fn pin_port(&self) -> PinNumber { |
| 858 | self.pin_port | 872 | self.pin_port |
| 859 | } | 873 | } |
| 860 | } | 874 | } |
| @@ -869,7 +883,7 @@ foreach_pin!( | |||
| 869 | } | 883 | } |
| 870 | impl SealedPin for peripherals::$pin_name { | 884 | impl SealedPin for peripherals::$pin_name { |
| 871 | #[inline] | 885 | #[inline] |
| 872 | fn pin_port(&self) -> u8 { | 886 | fn pin_port(&self) -> PinNumber { |
| 873 | $port_num * 16 + $pin_num | 887 | $port_num * 16 + $pin_num |
| 874 | } | 888 | } |
| 875 | } | 889 | } |
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 57a7acee7..61e550ad4 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs | |||
| @@ -98,6 +98,27 @@ pub(crate) unsafe fn on_interrupt<T: Instance>() { | |||
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | 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 | |||
| 101 | pub(crate) fn init(&mut self, config: Config) { | 122 | pub(crate) fn init(&mut self, config: Config) { |
| 102 | self.info.regs.cr1().modify(|reg| { | 123 | self.info.regs.cr1().modify(|reg| { |
| 103 | reg.set_pe(false); | 124 | reg.set_pe(false); |
| @@ -147,12 +168,6 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 147 | // `buffer`. The START bit can be set even if the bus | 168 | // `buffer`. The START bit can be set even if the bus |
| 148 | // is BUSY or I2C is in slave mode. | 169 | // is BUSY or I2C is in slave mode. |
| 149 | 170 | ||
| 150 | let reload = if reload { | ||
| 151 | i2c::vals::Reload::NOT_COMPLETED | ||
| 152 | } else { | ||
| 153 | i2c::vals::Reload::COMPLETED | ||
| 154 | }; | ||
| 155 | |||
| 156 | info.regs.cr2().modify(|w| { | 171 | info.regs.cr2().modify(|w| { |
| 157 | w.set_sadd(address.addr() << 1); | 172 | w.set_sadd(address.addr() << 1); |
| 158 | w.set_add10(address.add_mode()); | 173 | w.set_add10(address.add_mode()); |
| @@ -160,7 +175,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 160 | w.set_nbytes(length as u8); | 175 | w.set_nbytes(length as u8); |
| 161 | w.set_start(true); | 176 | w.set_start(true); |
| 162 | w.set_autoend(stop.autoend()); | 177 | w.set_autoend(stop.autoend()); |
| 163 | w.set_reload(reload); | 178 | w.set_reload(Self::to_reload(reload)); |
| 164 | }); | 179 | }); |
| 165 | 180 | ||
| 166 | Ok(()) | 181 | Ok(()) |
| @@ -172,28 +187,25 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 172 | length: usize, | 187 | length: usize, |
| 173 | stop: Stop, | 188 | stop: Stop, |
| 174 | reload: bool, | 189 | reload: bool, |
| 190 | restart: bool, | ||
| 175 | timeout: Timeout, | 191 | timeout: Timeout, |
| 176 | ) -> Result<(), Error> { | 192 | ) -> Result<(), Error> { |
| 177 | assert!(length < 256); | 193 | assert!(length < 256); |
| 178 | 194 | ||
| 179 | // Wait for any previous address sequence to end | 195 | if !restart { |
| 180 | // automatically. This could be up to 50% of a bus | 196 | // Wait for any previous address sequence to end |
| 181 | // cycle (ie. up to 0.5/freq) | 197 | // automatically. This could be up to 50% of a bus |
| 182 | while info.regs.cr2().read().start() { | 198 | // cycle (ie. up to 0.5/freq) |
| 183 | timeout.check()?; | 199 | while info.regs.cr2().read().start() { |
| 184 | } | 200 | timeout.check()?; |
| 201 | } | ||
| 185 | 202 | ||
| 186 | // Wait for the bus to be free | 203 | // Wait for the bus to be free |
| 187 | while info.regs.isr().read().busy() { | 204 | while info.regs.isr().read().busy() { |
| 188 | timeout.check()?; | 205 | timeout.check()?; |
| 206 | } | ||
| 189 | } | 207 | } |
| 190 | 208 | ||
| 191 | let reload = if reload { | ||
| 192 | i2c::vals::Reload::NOT_COMPLETED | ||
| 193 | } else { | ||
| 194 | i2c::vals::Reload::COMPLETED | ||
| 195 | }; | ||
| 196 | |||
| 197 | // Set START and prepare to send `bytes`. The | 209 | // Set START and prepare to send `bytes`. The |
| 198 | // 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 |
| 199 | // I2C is in slave mode. | 211 | // I2C is in slave mode. |
| @@ -204,28 +216,36 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 204 | w.set_nbytes(length as u8); | 216 | w.set_nbytes(length as u8); |
| 205 | w.set_start(true); | 217 | w.set_start(true); |
| 206 | w.set_autoend(stop.autoend()); | 218 | w.set_autoend(stop.autoend()); |
| 207 | w.set_reload(reload); | 219 | w.set_reload(Self::to_reload(reload)); |
| 208 | }); | 220 | }); |
| 209 | 221 | ||
| 210 | Ok(()) | 222 | Ok(()) |
| 211 | } | 223 | } |
| 212 | 224 | ||
| 213 | 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> { | ||
| 214 | assert!(length < 256 && length > 0); | 232 | assert!(length < 256 && length > 0); |
| 215 | 233 | ||
| 216 | 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 | } | ||
| 217 | timeout.check()?; | 242 | timeout.check()?; |
| 218 | } | 243 | } |
| 219 | 244 | ||
| 220 | let will_reload = if will_reload { | ||
| 221 | i2c::vals::Reload::NOT_COMPLETED | ||
| 222 | } else { | ||
| 223 | i2c::vals::Reload::COMPLETED | ||
| 224 | }; | ||
| 225 | |||
| 226 | info.regs.cr2().modify(|w| { | 245 | info.regs.cr2().modify(|w| { |
| 227 | w.set_nbytes(length as u8); | 246 | w.set_nbytes(length as u8); |
| 228 | w.set_reload(will_reload); | 247 | w.set_reload(Self::to_reload(will_reload)); |
| 248 | w.set_autoend(stop.autoend()); | ||
| 229 | }); | 249 | }); |
| 230 | 250 | ||
| 231 | Ok(()) | 251 | Ok(()) |
| @@ -369,7 +389,9 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 369 | loop { | 389 | loop { |
| 370 | let isr = self.info.regs.isr().read(); | 390 | let isr = self.info.regs.isr().read(); |
| 371 | self.error_occurred(&isr, timeout)?; | 391 | self.error_occurred(&isr, timeout)?; |
| 372 | 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() { | ||
| 373 | return Ok(()); | 395 | return Ok(()); |
| 374 | } | 396 | } |
| 375 | timeout.check()?; | 397 | timeout.check()?; |
| @@ -396,14 +418,20 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 396 | address, | 418 | address, |
| 397 | read.len().min(255), | 419 | read.len().min(255), |
| 398 | Stop::Automatic, | 420 | Stop::Automatic, |
| 399 | last_chunk_idx != 0, | 421 | last_chunk_idx != 0, // reload |
| 400 | restart, | 422 | restart, |
| 401 | timeout, | 423 | timeout, |
| 402 | )?; | 424 | )?; |
| 403 | 425 | ||
| 404 | for (number, chunk) in read.chunks_mut(255).enumerate() { | 426 | for (number, chunk) in read.chunks_mut(255).enumerate() { |
| 405 | if number != 0 { | 427 | if number != 0 { |
| 406 | 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 | )?; | ||
| 407 | } | 435 | } |
| 408 | 436 | ||
| 409 | for byte in chunk { | 437 | for byte in chunk { |
| @@ -441,6 +469,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 441 | write.len().min(255), | 469 | write.len().min(255), |
| 442 | Stop::Software, | 470 | Stop::Software, |
| 443 | last_chunk_idx != 0, | 471 | last_chunk_idx != 0, |
| 472 | false, // restart | ||
| 444 | timeout, | 473 | timeout, |
| 445 | ) { | 474 | ) { |
| 446 | if send_stop { | 475 | if send_stop { |
| @@ -451,7 +480,13 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 451 | 480 | ||
| 452 | for (number, chunk) in write.chunks(255).enumerate() { | 481 | for (number, chunk) in write.chunks(255).enumerate() { |
| 453 | if number != 0 { | 482 | if number != 0 { |
| 454 | 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 | )?; | ||
| 455 | } | 490 | } |
| 456 | 491 | ||
| 457 | for byte in chunk { | 492 | for byte in chunk { |
| @@ -507,9 +542,215 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 507 | /// | 542 | /// |
| 508 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | 543 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction |
| 509 | 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> { |
| 510 | let _ = addr; | 545 | if operations.is_empty() { |
| 511 | let _ = operations; | 546 | return Err(Error::ZeroLengthTransfer); |
| 512 | 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(()) | ||
| 513 | } | 754 | } |
| 514 | 755 | ||
| 515 | /// Blocking write multiple buffers. | 756 | /// Blocking write multiple buffers. |
| @@ -531,6 +772,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 531 | first_length.min(255), | 772 | first_length.min(255), |
| 532 | Stop::Software, | 773 | Stop::Software, |
| 533 | (first_length > 255) || (last_slice_index != 0), | 774 | (first_length > 255) || (last_slice_index != 0), |
| 775 | false, // restart | ||
| 534 | timeout, | 776 | timeout, |
| 535 | ) { | 777 | ) { |
| 536 | self.master_stop(); | 778 | self.master_stop(); |
| @@ -552,6 +794,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 552 | self.info, | 794 | self.info, |
| 553 | slice_len.min(255), | 795 | slice_len.min(255), |
| 554 | (idx != last_slice_index) || (slice_len > 255), | 796 | (idx != last_slice_index) || (slice_len > 255), |
| 797 | Stop::Software, | ||
| 555 | timeout, | 798 | timeout, |
| 556 | ) { | 799 | ) { |
| 557 | if err != Error::Nack { | 800 | if err != Error::Nack { |
| @@ -567,6 +810,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 567 | self.info, | 810 | self.info, |
| 568 | chunk.len(), | 811 | chunk.len(), |
| 569 | (number != last_chunk_idx) || (idx != last_slice_index), | 812 | (number != last_chunk_idx) || (idx != last_slice_index), |
| 813 | Stop::Software, | ||
| 570 | timeout, | 814 | timeout, |
| 571 | ) { | 815 | ) { |
| 572 | if err != Error::Nack { | 816 | if err != Error::Nack { |
| @@ -610,6 +854,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 610 | first_slice: bool, | 854 | first_slice: bool, |
| 611 | last_slice: bool, | 855 | last_slice: bool, |
| 612 | send_stop: bool, | 856 | send_stop: bool, |
| 857 | restart: bool, | ||
| 613 | timeout: Timeout, | 858 | timeout: Timeout, |
| 614 | ) -> Result<(), Error> { | 859 | ) -> Result<(), Error> { |
| 615 | let total_len = write.len(); | 860 | let total_len = write.len(); |
| @@ -676,10 +921,17 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 676 | total_len.min(255), | 921 | total_len.min(255), |
| 677 | Stop::Software, | 922 | Stop::Software, |
| 678 | (total_len > 255) || !last_slice, | 923 | (total_len > 255) || !last_slice, |
| 924 | restart, | ||
| 679 | timeout, | 925 | timeout, |
| 680 | )?; | 926 | )?; |
| 681 | } else { | 927 | } else { |
| 682 | 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 | )?; | ||
| 683 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | 935 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); |
| 684 | } | 936 | } |
| 685 | } else if !(isr.tcr() || isr.tc()) { | 937 | } else if !(isr.tcr() || isr.tc()) { |
| @@ -688,9 +940,13 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 688 | } else if remaining_len == 0 { | 940 | } else if remaining_len == 0 { |
| 689 | return Poll::Ready(Ok(())); | 941 | return Poll::Ready(Ok(())); |
| 690 | } else { | 942 | } else { |
| 691 | let last_piece = (remaining_len <= 255) && last_slice; | 943 | if let Err(e) = Self::reload( |
| 692 | 944 | self.info, | |
| 693 | 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 | ) { | ||
| 694 | return Poll::Ready(Err(e)); | 950 | return Poll::Ready(Err(e)); |
| 695 | } | 951 | } |
| 696 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | 952 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); |
| @@ -702,10 +958,9 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 702 | .await?; | 958 | .await?; |
| 703 | 959 | ||
| 704 | dma_transfer.await; | 960 | dma_transfer.await; |
| 705 | if last_slice { | 961 | |
| 706 | // This should be done already | 962 | // Always wait for TC after DMA completes - needed for consecutive buffers |
| 707 | self.wait_tc(timeout)?; | 963 | self.wait_tc(timeout)?; |
| 708 | } | ||
| 709 | 964 | ||
| 710 | if last_slice & send_stop { | 965 | if last_slice & send_stop { |
| 711 | self.master_stop(); | 966 | self.master_stop(); |
| @@ -780,7 +1035,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 780 | address, | 1035 | address, |
| 781 | total_len.min(255), | 1036 | total_len.min(255), |
| 782 | Stop::Automatic, | 1037 | Stop::Automatic, |
| 783 | total_len > 255, | 1038 | total_len > 255, // reload |
| 784 | restart, | 1039 | restart, |
| 785 | timeout, | 1040 | timeout, |
| 786 | )?; | 1041 | )?; |
| @@ -788,12 +1043,10 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 788 | return Poll::Ready(Ok(())); | 1043 | return Poll::Ready(Ok(())); |
| 789 | } | 1044 | } |
| 790 | } else if isr.tcr() { | 1045 | } else if isr.tcr() { |
| 791 | // poll_fn was woken without an interrupt present | 1046 | // Transfer Complete Reload - need to set up next chunk |
| 792 | return Poll::Pending; | ||
| 793 | } else { | ||
| 794 | let last_piece = remaining_len <= 255; | 1047 | let last_piece = remaining_len <= 255; |
| 795 | 1048 | ||
| 796 | 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) { |
| 797 | return Poll::Ready(Err(e)); | 1050 | return Poll::Ready(Err(e)); |
| 798 | } | 1051 | } |
| 799 | // Return here if we are on last chunk, | 1052 | // Return here if we are on last chunk, |
| @@ -802,6 +1055,9 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 802 | return Poll::Ready(Ok(())); | 1055 | return Poll::Ready(Ok(())); |
| 803 | } | 1056 | } |
| 804 | 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; | ||
| 805 | } | 1061 | } |
| 806 | 1062 | ||
| 807 | remaining_len = remaining_len.saturating_sub(255); | 1063 | remaining_len = remaining_len.saturating_sub(255); |
| @@ -826,7 +1082,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 826 | self.write_internal(address.into(), write, true, timeout) | 1082 | self.write_internal(address.into(), write, true, timeout) |
| 827 | } else { | 1083 | } else { |
| 828 | timeout | 1084 | timeout |
| 829 | .with(self.write_dma_internal(address.into(), write, true, true, true, timeout)) | 1085 | .with(self.write_dma_internal(address.into(), write, true, true, true, false, timeout)) |
| 830 | .await | 1086 | .await |
| 831 | } | 1087 | } |
| 832 | } | 1088 | } |
| @@ -842,16 +1098,24 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 842 | if write.is_empty() { | 1098 | if write.is_empty() { |
| 843 | return Err(Error::ZeroLengthTransfer); | 1099 | return Err(Error::ZeroLengthTransfer); |
| 844 | } | 1100 | } |
| 845 | let mut iter = write.iter(); | ||
| 846 | 1101 | ||
| 1102 | let mut iter = write.iter(); | ||
| 847 | let mut first = true; | 1103 | let mut first = true; |
| 848 | let mut current = iter.next(); | 1104 | let mut current = iter.next(); |
| 1105 | |||
| 849 | while let Some(c) = current { | 1106 | while let Some(c) = current { |
| 850 | let next = iter.next(); | 1107 | let next = iter.next(); |
| 851 | let is_last = next.is_none(); | 1108 | let is_last = next.is_none(); |
| 852 | 1109 | ||
| 853 | let fut = self.write_dma_internal(address, c, first, is_last, is_last, timeout); | 1110 | let fut = self.write_dma_internal( |
| 1111 | address, c, first, // first_slice | ||
| 1112 | is_last, // last_slice | ||
| 1113 | is_last, // send_stop (only on last buffer) | ||
| 1114 | false, // restart (false for all - they're one continuous write) | ||
| 1115 | timeout, | ||
| 1116 | ); | ||
| 854 | timeout.with(fut).await?; | 1117 | timeout.with(fut).await?; |
| 1118 | |||
| 855 | first = false; | 1119 | first = false; |
| 856 | current = next; | 1120 | current = next; |
| 857 | } | 1121 | } |
| @@ -881,7 +1145,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 881 | if write.is_empty() { | 1145 | if write.is_empty() { |
| 882 | self.write_internal(address.into(), write, false, timeout)?; | 1146 | self.write_internal(address.into(), write, false, timeout)?; |
| 883 | } else { | 1147 | } else { |
| 884 | let fut = self.write_dma_internal(address.into(), write, true, true, false, timeout); | 1148 | let fut = self.write_dma_internal(address.into(), write, true, true, false, false, timeout); |
| 885 | timeout.with(fut).await?; | 1149 | timeout.with(fut).await?; |
| 886 | } | 1150 | } |
| 887 | 1151 | ||
| @@ -903,9 +1167,299 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 903 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | 1167 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { |
| 904 | #[cfg(all(feature = "low-power", stm32wlex))] | 1168 | #[cfg(all(feature = "low-power", stm32wlex))] |
| 905 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | 1169 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); |
| 906 | let _ = addr; | 1170 | |
| 907 | let _ = operations; | 1171 | if operations.is_empty() { |
| 908 | todo!() | 1172 | return Err(Error::ZeroLengthTransfer); |
| 1173 | } | ||
| 1174 | |||
| 1175 | let address = addr.into(); | ||
| 1176 | let timeout = self.timeout(); | ||
| 1177 | |||
| 1178 | // Group consecutive operations of the same type | ||
| 1179 | let mut op_idx = 0; | ||
| 1180 | let mut is_first_group = true; | ||
| 1181 | |||
| 1182 | while op_idx < operations.len() { | ||
| 1183 | // Determine the type of current group and find all consecutive operations of same type | ||
| 1184 | let is_read = matches!(operations[op_idx], Operation::Read(_)); | ||
| 1185 | let group_start = op_idx; | ||
| 1186 | |||
| 1187 | // Find end of this group (consecutive operations of same type) | ||
| 1188 | while op_idx < operations.len() && matches!(operations[op_idx], Operation::Read(_)) == is_read { | ||
| 1189 | op_idx += 1; | ||
| 1190 | } | ||
| 1191 | let group_end = op_idx; | ||
| 1192 | let is_last_group = op_idx >= operations.len(); | ||
| 1193 | |||
| 1194 | // Execute this group of operations | ||
| 1195 | if is_read { | ||
| 1196 | self.execute_read_group_async( | ||
| 1197 | address, | ||
| 1198 | &mut operations[group_start..group_end], | ||
| 1199 | is_first_group, | ||
| 1200 | is_last_group, | ||
| 1201 | timeout, | ||
| 1202 | ) | ||
| 1203 | .await?; | ||
| 1204 | } else { | ||
| 1205 | self.execute_write_group_async( | ||
| 1206 | address, | ||
| 1207 | &operations[group_start..group_end], | ||
| 1208 | is_first_group, | ||
| 1209 | is_last_group, | ||
| 1210 | timeout, | ||
| 1211 | ) | ||
| 1212 | .await?; | ||
| 1213 | } | ||
| 1214 | |||
| 1215 | is_first_group = false; | ||
| 1216 | } | ||
| 1217 | |||
| 1218 | Ok(()) | ||
| 1219 | } | ||
| 1220 | |||
| 1221 | async fn execute_write_group_async( | ||
| 1222 | &mut self, | ||
| 1223 | address: Address, | ||
| 1224 | operations: &[Operation<'_>], | ||
| 1225 | is_first_group: bool, | ||
| 1226 | is_last_group: bool, | ||
| 1227 | timeout: Timeout, | ||
| 1228 | ) -> Result<(), Error> { | ||
| 1229 | // Calculate total bytes across all operations in this group | ||
| 1230 | let total_bytes = Self::total_operation_bytes(operations); | ||
| 1231 | |||
| 1232 | if total_bytes == 0 { | ||
| 1233 | // Handle empty write group using blocking call | ||
| 1234 | if is_first_group { | ||
| 1235 | Self::master_write(self.info, address, 0, Stop::Software, false, !is_first_group, timeout)?; | ||
| 1236 | } | ||
| 1237 | if is_last_group { | ||
| 1238 | self.master_stop(); | ||
| 1239 | } | ||
| 1240 | return Ok(()); | ||
| 1241 | } | ||
| 1242 | |||
| 1243 | // Collect all write buffers | ||
| 1244 | let mut write_buffers: heapless::Vec<&[u8], 16> = heapless::Vec::new(); | ||
| 1245 | for operation in operations { | ||
| 1246 | if let Operation::Write(buffer) = operation { | ||
| 1247 | if !buffer.is_empty() { | ||
| 1248 | let _ = write_buffers.push(buffer); | ||
| 1249 | } | ||
| 1250 | } | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | if write_buffers.is_empty() { | ||
| 1254 | return Ok(()); | ||
| 1255 | } | ||
| 1256 | |||
| 1257 | // Send each buffer using DMA | ||
| 1258 | let num_buffers = write_buffers.len(); | ||
| 1259 | for (idx, buffer) in write_buffers.iter().enumerate() { | ||
| 1260 | let is_first_buffer = idx == 0; | ||
| 1261 | let is_last_buffer = idx == num_buffers - 1; | ||
| 1262 | |||
| 1263 | let fut = self.write_dma_internal( | ||
| 1264 | address, | ||
| 1265 | buffer, | ||
| 1266 | is_first_buffer, // first_slice | ||
| 1267 | is_last_buffer, // last_slice | ||
| 1268 | is_last_buffer && is_last_group, // send_stop | ||
| 1269 | is_first_buffer && !is_first_group, // restart (only for first buffer if not first group) | ||
| 1270 | timeout, | ||
| 1271 | ); | ||
| 1272 | timeout.with(fut).await?; | ||
| 1273 | } | ||
| 1274 | |||
| 1275 | Ok(()) | ||
| 1276 | } | ||
| 1277 | |||
| 1278 | async fn execute_read_group_async( | ||
| 1279 | &mut self, | ||
| 1280 | address: Address, | ||
| 1281 | operations: &mut [Operation<'_>], | ||
| 1282 | is_first_group: bool, | ||
| 1283 | is_last_group: bool, | ||
| 1284 | timeout: Timeout, | ||
| 1285 | ) -> Result<(), Error> { | ||
| 1286 | // Calculate total bytes across all operations in this group | ||
| 1287 | let total_bytes = Self::total_operation_bytes(operations); | ||
| 1288 | |||
| 1289 | if total_bytes == 0 { | ||
| 1290 | // Handle empty read group using blocking call | ||
| 1291 | if is_first_group { | ||
| 1292 | Self::master_read( | ||
| 1293 | self.info, | ||
| 1294 | address, | ||
| 1295 | 0, | ||
| 1296 | if is_last_group { Stop::Automatic } else { Stop::Software }, | ||
| 1297 | false, // reload | ||
| 1298 | !is_first_group, | ||
| 1299 | timeout, | ||
| 1300 | )?; | ||
| 1301 | } | ||
| 1302 | if is_last_group { | ||
| 1303 | self.wait_stop(timeout)?; | ||
| 1304 | } | ||
| 1305 | return Ok(()); | ||
| 1306 | } | ||
| 1307 | |||
| 1308 | // Use DMA for read operations - need to handle multiple buffers | ||
| 1309 | let restart = !is_first_group; | ||
| 1310 | let mut total_remaining = total_bytes; | ||
| 1311 | let mut is_first_in_group = true; | ||
| 1312 | |||
| 1313 | for operation in operations { | ||
| 1314 | if let Operation::Read(buffer) = operation { | ||
| 1315 | if buffer.is_empty() { | ||
| 1316 | // Skip empty buffers | ||
| 1317 | continue; | ||
| 1318 | } | ||
| 1319 | |||
| 1320 | let buf_len = buffer.len(); | ||
| 1321 | total_remaining -= buf_len; | ||
| 1322 | let is_last_in_group = total_remaining == 0; | ||
| 1323 | |||
| 1324 | // Perform DMA read | ||
| 1325 | if is_first_in_group { | ||
| 1326 | // First buffer: use read_dma_internal which handles restart properly | ||
| 1327 | // Only use Automatic stop if this is the last buffer in the last group | ||
| 1328 | let stop_mode = if is_last_group && is_last_in_group { | ||
| 1329 | Stop::Automatic | ||
| 1330 | } else { | ||
| 1331 | Stop::Software | ||
| 1332 | }; | ||
| 1333 | |||
| 1334 | // We need a custom DMA read that respects our stop mode | ||
| 1335 | self.read_dma_group_internal(address, buffer, restart, stop_mode, timeout) | ||
| 1336 | .await?; | ||
| 1337 | is_first_in_group = false; | ||
| 1338 | } else { | ||
| 1339 | // Subsequent buffers: need to reload and continue | ||
| 1340 | let stop_mode = if is_last_group && is_last_in_group { | ||
| 1341 | Stop::Automatic | ||
| 1342 | } else { | ||
| 1343 | Stop::Software | ||
| 1344 | }; | ||
| 1345 | |||
| 1346 | self.read_dma_group_internal( | ||
| 1347 | address, buffer, false, // no restart for subsequent buffers in same group | ||
| 1348 | stop_mode, timeout, | ||
| 1349 | ) | ||
| 1350 | .await?; | ||
| 1351 | } | ||
| 1352 | } | ||
| 1353 | } | ||
| 1354 | |||
| 1355 | // Wait for transfer to complete | ||
| 1356 | if is_last_group { | ||
| 1357 | self.wait_stop(timeout)?; | ||
| 1358 | } | ||
| 1359 | |||
| 1360 | Ok(()) | ||
| 1361 | } | ||
| 1362 | |||
| 1363 | /// Internal DMA read helper for transaction groups | ||
| 1364 | async fn read_dma_group_internal( | ||
| 1365 | &mut self, | ||
| 1366 | address: Address, | ||
| 1367 | buffer: &mut [u8], | ||
| 1368 | restart: bool, | ||
| 1369 | stop_mode: Stop, | ||
| 1370 | timeout: Timeout, | ||
| 1371 | ) -> Result<(), Error> { | ||
| 1372 | let total_len = buffer.len(); | ||
| 1373 | |||
| 1374 | let dma_transfer = unsafe { | ||
| 1375 | let regs = self.info.regs; | ||
| 1376 | regs.cr1().modify(|w| { | ||
| 1377 | w.set_rxdmaen(true); | ||
| 1378 | w.set_tcie(true); | ||
| 1379 | w.set_nackie(true); | ||
| 1380 | w.set_errie(true); | ||
| 1381 | }); | ||
| 1382 | let src = regs.rxdr().as_ptr() as *mut u8; | ||
| 1383 | |||
| 1384 | self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) | ||
| 1385 | }; | ||
| 1386 | |||
| 1387 | let mut remaining_len = total_len; | ||
| 1388 | |||
| 1389 | let on_drop = OnDrop::new(|| { | ||
| 1390 | let regs = self.info.regs; | ||
| 1391 | regs.cr1().modify(|w| { | ||
| 1392 | w.set_rxdmaen(false); | ||
| 1393 | w.set_tcie(false); | ||
| 1394 | w.set_nackie(false); | ||
| 1395 | w.set_errie(false); | ||
| 1396 | }); | ||
| 1397 | regs.icr().write(|w| { | ||
| 1398 | w.set_nackcf(true); | ||
| 1399 | w.set_berrcf(true); | ||
| 1400 | w.set_arlocf(true); | ||
| 1401 | w.set_ovrcf(true); | ||
| 1402 | }); | ||
| 1403 | }); | ||
| 1404 | |||
| 1405 | poll_fn(|cx| { | ||
| 1406 | self.state.waker.register(cx.waker()); | ||
| 1407 | |||
| 1408 | let isr = self.info.regs.isr().read(); | ||
| 1409 | |||
| 1410 | if isr.nackf() { | ||
| 1411 | return Poll::Ready(Err(Error::Nack)); | ||
| 1412 | } | ||
| 1413 | if isr.arlo() { | ||
| 1414 | return Poll::Ready(Err(Error::Arbitration)); | ||
| 1415 | } | ||
| 1416 | if isr.berr() { | ||
| 1417 | return Poll::Ready(Err(Error::Bus)); | ||
| 1418 | } | ||
| 1419 | if isr.ovr() { | ||
| 1420 | return Poll::Ready(Err(Error::Overrun)); | ||
| 1421 | } | ||
| 1422 | |||
| 1423 | if remaining_len == total_len { | ||
| 1424 | Self::master_read( | ||
| 1425 | self.info, | ||
| 1426 | address, | ||
| 1427 | total_len.min(255), | ||
| 1428 | stop_mode, | ||
| 1429 | total_len > 255, // reload | ||
| 1430 | restart, | ||
| 1431 | timeout, | ||
| 1432 | )?; | ||
| 1433 | if total_len <= 255 { | ||
| 1434 | return Poll::Ready(Ok(())); | ||
| 1435 | } | ||
| 1436 | } else if isr.tcr() { | ||
| 1437 | // Transfer Complete Reload - need to set up next chunk | ||
| 1438 | let last_piece = remaining_len <= 255; | ||
| 1439 | |||
| 1440 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, stop_mode, timeout) { | ||
| 1441 | return Poll::Ready(Err(e)); | ||
| 1442 | } | ||
| 1443 | // Return here if we are on last chunk, | ||
| 1444 | // end of transfer will be awaited with the DMA below | ||
| 1445 | if last_piece { | ||
| 1446 | return Poll::Ready(Ok(())); | ||
| 1447 | } | ||
| 1448 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | ||
| 1449 | } else { | ||
| 1450 | // poll_fn was woken without TCR interrupt | ||
| 1451 | return Poll::Pending; | ||
| 1452 | } | ||
| 1453 | |||
| 1454 | remaining_len = remaining_len.saturating_sub(255); | ||
| 1455 | Poll::Pending | ||
| 1456 | }) | ||
| 1457 | .await?; | ||
| 1458 | |||
| 1459 | dma_transfer.await; | ||
| 1460 | drop(on_drop); | ||
| 1461 | |||
| 1462 | Ok(()) | ||
| 909 | } | 1463 | } |
| 910 | } | 1464 | } |
| 911 | 1465 | ||
| @@ -1043,7 +1597,13 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1043 | if number == 0 { | 1597 | if number == 0 { |
| 1044 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); | 1598 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); |
| 1045 | } else { | 1599 | } else { |
| 1046 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 1600 | Self::reload( |
| 1601 | self.info, | ||
| 1602 | chunk.len(), | ||
| 1603 | number != last_chunk_idx, | ||
| 1604 | Stop::Software, | ||
| 1605 | timeout, | ||
| 1606 | )?; | ||
| 1047 | } | 1607 | } |
| 1048 | 1608 | ||
| 1049 | let mut index = 0; | 1609 | let mut index = 0; |
| @@ -1092,7 +1652,13 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1092 | if number == 0 { | 1652 | if number == 0 { |
| 1093 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); | 1653 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); |
| 1094 | } else { | 1654 | } else { |
| 1095 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 1655 | Self::reload( |
| 1656 | self.info, | ||
| 1657 | chunk.len(), | ||
| 1658 | number != last_chunk_idx, | ||
| 1659 | Stop::Software, | ||
| 1660 | timeout, | ||
| 1661 | )?; | ||
| 1096 | } | 1662 | } |
| 1097 | 1663 | ||
| 1098 | let mut index = 0; | 1664 | let mut index = 0; |
| @@ -1228,13 +1794,20 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1228 | Poll::Pending | 1794 | Poll::Pending |
| 1229 | } else if isr.tcr() { | 1795 | } else if isr.tcr() { |
| 1230 | let is_last_slice = remaining_len <= 255; | 1796 | let is_last_slice = remaining_len <= 255; |
| 1231 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { | 1797 | if let Err(e) = Self::reload( |
| 1798 | self.info, | ||
| 1799 | remaining_len.min(255), | ||
| 1800 | !is_last_slice, | ||
| 1801 | Stop::Software, | ||
| 1802 | timeout, | ||
| 1803 | ) { | ||
| 1232 | return Poll::Ready(Err(e)); | 1804 | return Poll::Ready(Err(e)); |
| 1233 | } | 1805 | } |
| 1234 | remaining_len = remaining_len.saturating_sub(255); | 1806 | remaining_len = remaining_len.saturating_sub(255); |
| 1235 | regs.cr1().modify(|w| w.set_tcie(true)); | 1807 | regs.cr1().modify(|w| w.set_tcie(true)); |
| 1236 | Poll::Pending | 1808 | Poll::Pending |
| 1237 | } else if isr.stopf() { | 1809 | } else if isr.stopf() { |
| 1810 | remaining_len = remaining_len.saturating_add(dma_transfer.get_remaining_transfers() as usize); | ||
| 1238 | regs.icr().write(|reg| reg.set_stopcf(true)); | 1811 | regs.icr().write(|reg| reg.set_stopcf(true)); |
| 1239 | let poll = Poll::Ready(Ok(total_len - remaining_len)); | 1812 | let poll = Poll::Ready(Ok(total_len - remaining_len)); |
| 1240 | poll | 1813 | poll |
| @@ -1274,7 +1847,8 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1274 | w.set_txdmaen(false); | 1847 | w.set_txdmaen(false); |
| 1275 | w.set_stopie(false); | 1848 | w.set_stopie(false); |
| 1276 | w.set_tcie(false); | 1849 | w.set_tcie(false); |
| 1277 | }) | 1850 | }); |
| 1851 | regs.isr().write(|w| w.set_txe(true)); | ||
| 1278 | }); | 1852 | }); |
| 1279 | 1853 | ||
| 1280 | let state = self.state; | 1854 | let state = self.state; |
| @@ -1290,13 +1864,24 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1290 | Poll::Pending | 1864 | Poll::Pending |
| 1291 | } else if isr.tcr() { | 1865 | } else if isr.tcr() { |
| 1292 | let is_last_slice = remaining_len <= 255; | 1866 | let is_last_slice = remaining_len <= 255; |
| 1293 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { | 1867 | if let Err(e) = Self::reload( |
| 1868 | self.info, | ||
| 1869 | remaining_len.min(255), | ||
| 1870 | !is_last_slice, | ||
| 1871 | Stop::Software, | ||
| 1872 | timeout, | ||
| 1873 | ) { | ||
| 1294 | return Poll::Ready(Err(e)); | 1874 | return Poll::Ready(Err(e)); |
| 1295 | } | 1875 | } |
| 1296 | remaining_len = remaining_len.saturating_sub(255); | 1876 | remaining_len = remaining_len.saturating_sub(255); |
| 1297 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | 1877 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); |
| 1298 | Poll::Pending | 1878 | Poll::Pending |
| 1299 | } else if isr.stopf() { | 1879 | } else if isr.stopf() { |
| 1880 | let mut leftover_bytes = dma_transfer.get_remaining_transfers(); | ||
| 1881 | if !self.info.regs.isr().read().txe() { | ||
| 1882 | leftover_bytes = leftover_bytes.saturating_add(1); | ||
| 1883 | } | ||
| 1884 | remaining_len = remaining_len.saturating_add(leftover_bytes as usize); | ||
| 1300 | self.info.regs.icr().write(|reg| reg.set_stopcf(true)); | 1885 | self.info.regs.icr().write(|reg| reg.set_stopcf(true)); |
| 1301 | if remaining_len > 0 { | 1886 | if remaining_len > 0 { |
| 1302 | dma_transfer.request_pause(); | 1887 | dma_transfer.request_pause(); |
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index e08ab30e6..6e492946a 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -77,6 +77,7 @@ pub mod dts; | |||
| 77 | pub mod eth; | 77 | pub mod eth; |
| 78 | #[cfg(feature = "exti")] | 78 | #[cfg(feature = "exti")] |
| 79 | pub mod exti; | 79 | pub mod exti; |
| 80 | #[cfg(flash)] | ||
| 80 | pub mod flash; | 81 | pub mod flash; |
| 81 | #[cfg(fmc)] | 82 | #[cfg(fmc)] |
| 82 | pub mod fmc; | 83 | pub mod fmc; |
| @@ -177,7 +178,6 @@ pub use crate::_generated::interrupt; | |||
| 177 | /// } | 178 | /// } |
| 178 | /// ); | 179 | /// ); |
| 179 | /// ``` | 180 | /// ``` |
| 180 | |||
| 181 | // developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. | 181 | // developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. |
| 182 | #[macro_export] | 182 | #[macro_export] |
| 183 | macro_rules! bind_interrupts { | 183 | macro_rules! bind_interrupts { |
| @@ -510,6 +510,16 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 510 | critical_section::with(|cs| { | 510 | critical_section::with(|cs| { |
| 511 | let p = Peripherals::take_with_cs(cs); | 511 | let p = Peripherals::take_with_cs(cs); |
| 512 | 512 | ||
| 513 | #[cfg(dbgmcu_n6)] | ||
| 514 | { | ||
| 515 | crate::pac::RCC.miscensr().write(|w| w.set_dbgens(true)); | ||
| 516 | crate::pac::RCC.miscenr().read(); // volatile read | ||
| 517 | crate::pac::DBGMCU | ||
| 518 | .cr() | ||
| 519 | .modify(|w| w.set_dbgclken(stm32_metapac::dbgmcu::vals::Dbgclken::B_0X1)); | ||
| 520 | crate::pac::DBGMCU.cr().read(); | ||
| 521 | } | ||
| 522 | |||
| 513 | #[cfg(dbgmcu)] | 523 | #[cfg(dbgmcu)] |
| 514 | crate::pac::DBGMCU.cr().modify(|cr| { | 524 | crate::pac::DBGMCU.cr().modify(|cr| { |
| 515 | #[cfg(dbgmcu_h5)] | 525 | #[cfg(dbgmcu_h5)] |
| @@ -524,7 +534,7 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 524 | } | 534 | } |
| 525 | #[cfg(any( | 535 | #[cfg(any( |
| 526 | dbgmcu_f1, dbgmcu_f2, dbgmcu_f3, dbgmcu_f4, dbgmcu_f7, dbgmcu_g4, dbgmcu_f7, dbgmcu_l0, dbgmcu_l1, | 536 | dbgmcu_f1, dbgmcu_f2, dbgmcu_f3, dbgmcu_f4, dbgmcu_f7, dbgmcu_g4, dbgmcu_f7, dbgmcu_l0, dbgmcu_l1, |
| 527 | dbgmcu_l4, dbgmcu_wb, dbgmcu_wl | 537 | dbgmcu_l4, dbgmcu_wb, dbgmcu_wl, dbgmcu_n6 |
| 528 | ))] | 538 | ))] |
| 529 | { | 539 | { |
| 530 | cr.set_dbg_sleep(config.enable_debug_during_sleep); | 540 | cr.set_dbg_sleep(config.enable_debug_during_sleep); |
| @@ -545,7 +555,7 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 545 | rcc::enable_and_reset_with_cs::<peripherals::SYSCFG>(cs); | 555 | rcc::enable_and_reset_with_cs::<peripherals::SYSCFG>(cs); |
| 546 | #[cfg(not(any(stm32h5, stm32h7, stm32h7rs, stm32wb, stm32wl)))] | 556 | #[cfg(not(any(stm32h5, stm32h7, stm32h7rs, stm32wb, stm32wl)))] |
| 547 | rcc::enable_and_reset_with_cs::<peripherals::PWR>(cs); | 557 | rcc::enable_and_reset_with_cs::<peripherals::PWR>(cs); |
| 548 | #[cfg(not(any(stm32f2, stm32f4, stm32f7, stm32l0, stm32h5, stm32h7, stm32h7rs)))] | 558 | #[cfg(all(flash, not(any(stm32f2, stm32f4, stm32f7, stm32l0, stm32h5, stm32h7, stm32h7rs))))] |
| 549 | rcc::enable_and_reset_with_cs::<peripherals::FLASH>(cs); | 559 | rcc::enable_and_reset_with_cs::<peripherals::FLASH>(cs); |
| 550 | 560 | ||
| 551 | // Enable the VDDIO2 power supply on chips that have it. | 561 | // Enable the VDDIO2 power supply on chips that have it. |
| @@ -605,7 +615,7 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 605 | #[cfg(ucpd)] | 615 | #[cfg(ucpd)] |
| 606 | ucpd::init( | 616 | ucpd::init( |
| 607 | cs, | 617 | cs, |
| 608 | #[cfg(peri_ucpd1)] | 618 | #[cfg(all(peri_ucpd1, not(stm32n6)))] |
| 609 | config.enable_ucpd1_dead_battery, | 619 | config.enable_ucpd1_dead_battery, |
| 610 | #[cfg(peri_ucpd2)] | 620 | #[cfg(peri_ucpd2)] |
| 611 | config.enable_ucpd2_dead_battery, | 621 | config.enable_ucpd2_dead_battery, |
| @@ -648,3 +658,17 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 648 | p | 658 | p |
| 649 | }) | 659 | }) |
| 650 | } | 660 | } |
| 661 | |||
| 662 | /// Performs a busy-wait delay for a specified number of microseconds. | ||
| 663 | #[allow(unused)] | ||
| 664 | pub(crate) fn block_for_us(us: u64) { | ||
| 665 | cfg_if::cfg_if! { | ||
| 666 | // this does strange things on stm32wlx in low power mode depending on exactly when it's called | ||
| 667 | // as in sometimes 15 us (1 tick) would take > 20 seconds. | ||
| 668 | if #[cfg(all(feature = "time", all(not(feature = "low-power"), not(stm32wlex))))] { | ||
| 669 | embassy_time::block_for(embassy_time::Duration::from_micros(us)); | ||
| 670 | } else { | ||
| 671 | cortex_m::asm::delay(unsafe { rcc::get_freqs().sys.to_hertz().unwrap().0 as u64 * us / 1_000_000 } as u32); | ||
| 672 | } | ||
| 673 | } | ||
| 674 | } | ||
diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index ac8d5de21..4a55f5bd3 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs | |||
| @@ -4,19 +4,12 @@ | |||
| 4 | use embassy_hal_internal::PeripheralType; | 4 | use embassy_hal_internal::PeripheralType; |
| 5 | 5 | ||
| 6 | use crate::Peri; | 6 | use crate::Peri; |
| 7 | #[cfg(opamp_v5)] | ||
| 8 | use crate::block_for_us; | ||
| 7 | use crate::pac::opamp::vals::*; | 9 | use crate::pac::opamp::vals::*; |
| 8 | #[cfg(not(any(stm32g4, stm32f3)))] | 10 | #[cfg(not(any(stm32g4, stm32f3)))] |
| 9 | use crate::rcc::RccInfo; | 11 | use crate::rcc::RccInfo; |
| 10 | 12 | ||
| 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 | |||
| 20 | /// Gain | 13 | /// Gain |
| 21 | #[allow(missing_docs)] | 14 | #[allow(missing_docs)] |
| 22 | #[derive(Clone, Copy)] | 15 | #[derive(Clone, Copy)] |
| @@ -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/rcc/bd.rs b/embassy-stm32/src/rcc/bd.rs index 5b367c043..219be208f 100644 --- a/embassy-stm32/src/rcc/bd.rs +++ b/embassy-stm32/src/rcc/bd.rs | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | #[cfg(not(stm32n6))] | ||
| 1 | use core::sync::atomic::{Ordering, compiler_fence}; | 2 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 2 | 3 | ||
| 4 | #[cfg(not(stm32n6))] | ||
| 3 | use crate::pac::common::{RW, Reg}; | 5 | use crate::pac::common::{RW, Reg}; |
| 4 | #[cfg(backup_sram)] | 6 | #[cfg(backup_sram)] |
| 5 | use crate::pac::pwr::vals::Retention; | 7 | use crate::pac::pwr::vals::Retention; |
| @@ -54,7 +56,7 @@ impl From<LseDrive> for crate::pac::rcc::vals::Lsedrv { | |||
| 54 | } | 56 | } |
| 55 | } | 57 | } |
| 56 | 58 | ||
| 57 | #[cfg(not(any(rtc_v2_l0, rtc_v2_l1, stm32c0)))] | 59 | #[cfg(not(any(rtc_v2_l0, rtc_v2_l1, stm32c0, stm32n6)))] |
| 58 | type Bdcr = crate::pac::rcc::regs::Bdcr; | 60 | type Bdcr = crate::pac::rcc::regs::Bdcr; |
| 59 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] | 61 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] |
| 60 | type Bdcr = crate::pac::rcc::regs::Csr; | 62 | type Bdcr = crate::pac::rcc::regs::Csr; |
| @@ -64,19 +66,22 @@ type Bdcr = crate::pac::rcc::regs::Csr1; | |||
| 64 | #[cfg(any(stm32c0))] | 66 | #[cfg(any(stm32c0))] |
| 65 | fn unlock() {} | 67 | fn unlock() {} |
| 66 | 68 | ||
| 67 | #[cfg(not(any(stm32c0)))] | 69 | #[cfg(not(any(stm32c0, stm32n6)))] |
| 68 | fn unlock() { | 70 | fn unlock() { |
| 69 | #[cfg(any(stm32f0, stm32f1, stm32f2, stm32f3, stm32l0, stm32l1))] | 71 | #[cfg(any(stm32f0, stm32f1, stm32f2, stm32f3, stm32l0, stm32l1))] |
| 70 | let cr = crate::pac::PWR.cr(); | 72 | let cr = crate::pac::PWR.cr(); |
| 71 | #[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 | )))] | ||
| 72 | let cr = crate::pac::PWR.cr1(); | 76 | let cr = crate::pac::PWR.cr1(); |
| 73 | #[cfg(any(stm32u5, stm32h5, stm32wba))] | 77 | #[cfg(any(stm32u5, stm32h5, stm32wba, stm32n6))] |
| 74 | let cr = crate::pac::PWR.dbpcr(); | 78 | let cr = crate::pac::PWR.dbpcr(); |
| 75 | 79 | ||
| 76 | cr.modify(|w| w.set_dbp(true)); | 80 | cr.modify(|w| w.set_dbp(true)); |
| 77 | while !cr.read().dbp() {} | 81 | while !cr.read().dbp() {} |
| 78 | } | 82 | } |
| 79 | 83 | ||
| 84 | #[cfg(not(stm32n6))] | ||
| 80 | fn bdcr() -> Reg<Bdcr, RW> { | 85 | fn bdcr() -> Reg<Bdcr, RW> { |
| 81 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] | 86 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] |
| 82 | return crate::pac::RCC.csr(); | 87 | return crate::pac::RCC.csr(); |
| @@ -150,6 +155,7 @@ impl Default for LsConfig { | |||
| 150 | } | 155 | } |
| 151 | 156 | ||
| 152 | impl LsConfig { | 157 | impl LsConfig { |
| 158 | #[cfg(not(stm32n6))] | ||
| 153 | pub(crate) fn init(&self) -> Option<Hertz> { | 159 | pub(crate) fn init(&self) -> Option<Hertz> { |
| 154 | let rtc_clk = match self.rtc { | 160 | let rtc_clk = match self.rtc { |
| 155 | RtcClockSource::LSI => { | 161 | RtcClockSource::LSI => { |
| @@ -185,14 +191,19 @@ impl LsConfig { | |||
| 185 | if self.lsi { | 191 | if self.lsi { |
| 186 | #[cfg(any(stm32u5, stm32h5, stm32wba))] | 192 | #[cfg(any(stm32u5, stm32h5, stm32wba))] |
| 187 | let csr = crate::pac::RCC.bdcr(); | 193 | let csr = crate::pac::RCC.bdcr(); |
| 188 | #[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)))] | ||
| 189 | let csr = crate::pac::RCC.csr(); | 197 | let csr = crate::pac::RCC.csr(); |
| 190 | #[cfg(any(stm32c0))] | 198 | #[cfg(stm32c0)] |
| 191 | let csr = crate::pac::RCC.csr2(); | 199 | let csr = crate::pac::RCC.csr2(); |
| 192 | 200 | ||
| 193 | #[cfg(not(any(rcc_wb, rcc_wba)))] | 201 | #[cfg(not(any(rcc_wb, rcc_wba, rcc_n6)))] |
| 194 | csr.modify(|w| w.set_lsion(true)); | 202 | csr.modify(|w| w.set_lsion(true)); |
| 195 | 203 | ||
| 204 | #[cfg(rcc_n6)] | ||
| 205 | crate::pac::RCC.cr().modify(|w| w.set_lsion(true)); | ||
| 206 | |||
| 196 | #[cfg(any(rcc_wb, rcc_wba))] | 207 | #[cfg(any(rcc_wb, rcc_wba))] |
| 197 | csr.modify(|w| w.set_lsi1on(true)); | 208 | csr.modify(|w| w.set_lsi1on(true)); |
| 198 | 209 | ||
| @@ -222,25 +233,58 @@ impl LsConfig { | |||
| 222 | // backup domain configuration (LSEON, RTCEN, RTCSEL) is kept across resets. | 233 | // backup domain configuration (LSEON, RTCEN, RTCSEL) is kept across resets. |
| 223 | // once set, changing it requires a backup domain reset. | 234 | // once set, changing it requires a backup domain reset. |
| 224 | // 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. | ||
| 225 | 237 | ||
| 226 | // 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))] | ||
| 227 | 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 | |||
| 228 | let mut ok = true; | 250 | let mut ok = true; |
| 229 | ok &= reg.rtcsel() == self.rtc; | 251 | #[cfg(not(rcc_n6))] |
| 230 | #[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)))] | ||
| 231 | { | 260 | { |
| 232 | ok &= reg.rtcen() == (self.rtc != RtcClockSource::DISABLE); | 261 | ok &= reg.rtcen() == (self.rtc != RtcClockSource::DISABLE); |
| 233 | } | 262 | } |
| 263 | #[cfg(rcc_n6)] | ||
| 264 | { | ||
| 265 | ok &= apb4lenr.rtcen() == (self.rtc != RtcClockSource::DISABLE); | ||
| 266 | } | ||
| 234 | ok &= reg.lseon() == lse_en; | 267 | ok &= reg.lseon() == lse_en; |
| 235 | 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 | } | ||
| 236 | #[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))] |
| 237 | if let Some(lse_sysen) = lse_sysen { | 277 | if let Some(lse_sysen) = lse_sysen { |
| 238 | ok &= reg.lsesysen() == lse_sysen; | 278 | ok &= reg.lsesysen() == lse_sysen; |
| 239 | } | 279 | } |
| 240 | #[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)))] |
| 241 | if let Some(lse_drv) = lse_drv { | 281 | if let Some(lse_drv) = lse_drv { |
| 242 | ok &= reg.lsedrv() == lse_drv.into(); | 282 | ok &= reg.lsedrv() == lse_drv.into(); |
| 243 | } | 283 | } |
| 284 | #[cfg(rcc_n6)] | ||
| 285 | if let Some(lse_drv) = lse_drv { | ||
| 286 | ok &= lsecfgr.lsedrv() == lse_drv.into(); | ||
| 287 | } | ||
| 244 | 288 | ||
| 245 | // if configuration is OK, we're done. | 289 | // if configuration is OK, we're done. |
| 246 | if ok { | 290 | if ok { |
| @@ -249,7 +293,7 @@ impl LsConfig { | |||
| 249 | } | 293 | } |
| 250 | 294 | ||
| 251 | // If not OK, reset backup domain and configure it. | 295 | // If not OK, reset backup domain and configure it. |
| 252 | #[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)))] |
| 253 | { | 297 | { |
| 254 | bdcr().modify(|w| w.set_bdrst(true)); | 298 | bdcr().modify(|w| w.set_bdrst(true)); |
| 255 | bdcr().modify(|w| w.set_bdrst(false)); | 299 | bdcr().modify(|w| w.set_bdrst(false)); |
| @@ -262,7 +306,7 @@ impl LsConfig { | |||
| 262 | // 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 |
| 263 | // 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 |
| 264 | //#[cfg(any(stm32h5, stm32h7rs))] | 308 | //#[cfg(any(stm32h5, stm32h7rs))] |
| 265 | #[cfg(any(stm32h7rs))] | 309 | #[cfg(any(stm32h7rs, stm32n6))] |
| 266 | { | 310 | { |
| 267 | bdcr().modify(|w| w.set_vswrst(true)); | 311 | bdcr().modify(|w| w.set_vswrst(true)); |
| 268 | bdcr().modify(|w| w.set_vswrst(false)); | 312 | bdcr().modify(|w| w.set_vswrst(false)); |
| @@ -274,16 +318,31 @@ impl LsConfig { | |||
| 274 | } | 318 | } |
| 275 | 319 | ||
| 276 | if lse_en { | 320 | if lse_en { |
| 277 | bdcr().modify(|w| { | 321 | #[cfg(not(rcc_n6))] |
| 278 | #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1)))] | 322 | { |
| 279 | if let Some(lse_drv) = lse_drv { | 323 | bdcr().modify(|w| { |
| 280 | w.set_lsedrv(lse_drv.into()); | 324 | #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1)))] |
| 281 | } | 325 | if let Some(lse_drv) = lse_drv { |
| 282 | w.set_lsebyp(lse_byp); | 326 | w.set_lsedrv(lse_drv.into()); |
| 283 | w.set_lseon(true); | 327 | } |
| 284 | }); | 328 | w.set_lsebyp(lse_byp); |
| 329 | w.set_lseon(true); | ||
| 330 | }); | ||
| 285 | 331 | ||
| 286 | while !bdcr().read().lserdy() {} | 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)); | ||
| 343 | |||
| 344 | while !crate::pac::RCC.sr().read().lserdy() {} | ||
| 345 | } | ||
| 287 | 346 | ||
| 288 | #[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))] |
| 289 | if let Some(lse_sysen) = lse_sysen { | 348 | if let Some(lse_sysen) = lse_sysen { |
| @@ -298,6 +357,7 @@ impl LsConfig { | |||
| 298 | } | 357 | } |
| 299 | 358 | ||
| 300 | if self.rtc != RtcClockSource::DISABLE { | 359 | if self.rtc != RtcClockSource::DISABLE { |
| 360 | #[cfg(not(rcc_n6))] | ||
| 301 | bdcr().modify(|w| { | 361 | bdcr().modify(|w| { |
| 302 | #[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))] |
| 303 | assert!(!w.lsecsson(), "RTC is not compatible with LSE CSS, yet."); | 363 | assert!(!w.lsecsson(), "RTC is not compatible with LSE CSS, yet."); |
| @@ -306,6 +366,12 @@ impl LsConfig { | |||
| 306 | w.set_rtcen(true); | 366 | w.set_rtcen(true); |
| 307 | w.set_rtcsel(self.rtc); | 367 | w.set_rtcsel(self.rtc); |
| 308 | }); | 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 | } | ||
| 309 | } | 375 | } |
| 310 | 376 | ||
| 311 | trace!("BDCR configured: {:08x}", bdcr().read().0); | 377 | trace!("BDCR configured: {:08x}", bdcr().read().0); |
diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs index 3d961df03..0624fdf26 100644 --- a/embassy-stm32/src/rcc/mco.rs +++ b/embassy-stm32/src/rcc/mco.rs | |||
| @@ -16,7 +16,8 @@ pub use crate::pac::rcc::vals::Mcopre as McoPrescaler; | |||
| 16 | rcc_h7ab, | 16 | rcc_h7ab, |
| 17 | rcc_h7rm0433, | 17 | rcc_h7rm0433, |
| 18 | rcc_h7, | 18 | rcc_h7, |
| 19 | rcc_h7rs | 19 | rcc_h7rs, |
| 20 | rcc_n6 | ||
| 20 | )))] | 21 | )))] |
| 21 | pub use crate::pac::rcc::vals::Mcosel as McoSource; | 22 | pub use crate::pac::rcc::vals::Mcosel as McoSource; |
| 22 | #[cfg(any( | 23 | #[cfg(any( |
| @@ -29,7 +30,8 @@ pub use crate::pac::rcc::vals::Mcosel as McoSource; | |||
| 29 | rcc_h7ab, | 30 | rcc_h7ab, |
| 30 | rcc_h7rm0433, | 31 | rcc_h7rm0433, |
| 31 | rcc_h7, | 32 | rcc_h7, |
| 32 | rcc_h7rs | 33 | rcc_h7rs, |
| 34 | rcc_n6 | ||
| 33 | ))] | 35 | ))] |
| 34 | 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}; |
| 35 | use crate::{Peri, peripherals}; | 37 | use crate::{Peri, peripherals}; |
| @@ -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); |
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 01fa3a475..592890777 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -28,6 +28,7 @@ pub use hsi48::*; | |||
| 28 | #[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl, stm32u0), path = "l.rs")] | 28 | #[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl, stm32u0), path = "l.rs")] |
| 29 | #[cfg_attr(stm32u5, path = "u5.rs")] | 29 | #[cfg_attr(stm32u5, path = "u5.rs")] |
| 30 | #[cfg_attr(stm32wba, path = "wba.rs")] | 30 | #[cfg_attr(stm32wba, path = "wba.rs")] |
| 31 | #[cfg_attr(stm32n6, path = "n6.rs")] | ||
| 31 | mod _version; | 32 | mod _version; |
| 32 | 33 | ||
| 33 | pub use _version::*; | 34 | pub use _version::*; |
diff --git a/embassy-stm32/src/rcc/n6.rs b/embassy-stm32/src/rcc/n6.rs new file mode 100644 index 000000000..866851bbd --- /dev/null +++ b/embassy-stm32/src/rcc/n6.rs | |||
| @@ -0,0 +1,1046 @@ | |||
| 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 | debug!("setting power supply config"); | ||
| 1007 | |||
| 1008 | power_supply_config(config.supply_config); | ||
| 1009 | |||
| 1010 | let osc = init_osc(config); | ||
| 1011 | let clock_inputs = ClocksInput { | ||
| 1012 | hsi: osc.hsi, | ||
| 1013 | msi: osc.msi, | ||
| 1014 | hse: osc.hse, | ||
| 1015 | }; | ||
| 1016 | let clocks = init_clocks(config, &clock_inputs); | ||
| 1017 | |||
| 1018 | // TODO: sysb, sysc, sysd must have the same clock source | ||
| 1019 | |||
| 1020 | set_clocks!( | ||
| 1021 | sys: Some(clocks.sysclk), | ||
| 1022 | hsi: osc.hsi, | ||
| 1023 | hsi_div: None, | ||
| 1024 | hse: osc.hse, | ||
| 1025 | msi: osc.msi, | ||
| 1026 | hclk1: Some(clocks.ahb), | ||
| 1027 | hclk2: Some(clocks.ahb), | ||
| 1028 | hclk3: Some(clocks.ahb), | ||
| 1029 | hclk4: Some(clocks.ahb), | ||
| 1030 | hclk5: Some(clocks.ahb), | ||
| 1031 | pclk1: Some(clocks.apb1), | ||
| 1032 | pclk2: Some(clocks.apb2), | ||
| 1033 | pclk1_tim: Some(clocks.pclk_tim), | ||
| 1034 | pclk2_tim: Some(clocks.pclk_tim), | ||
| 1035 | pclk4: Some(clocks.apb4), | ||
| 1036 | pclk5: Some(clocks.apb5), | ||
| 1037 | per: None, | ||
| 1038 | rtc: None, | ||
| 1039 | i2s_ckin: None, | ||
| 1040 | ic8: None, | ||
| 1041 | ic9: None, | ||
| 1042 | ic14: None, | ||
| 1043 | ic17: None, | ||
| 1044 | ic20: None, | ||
| 1045 | ); | ||
| 1046 | } | ||
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 06315d7f3..c338b0fd4 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs | |||
| @@ -309,7 +309,9 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 309 | /// Generate a sequence of PWM waveform | 309 | /// Generate a sequence of PWM waveform |
| 310 | /// | 310 | /// |
| 311 | /// Note: | 311 | /// Note: |
| 312 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | 312 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. |
| 313 | /// Also be aware that embassy timers use one of timers internally. It is possible to | ||
| 314 | /// switch this timer by using `time-driver-timX` feature. | ||
| 313 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { | 315 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { |
| 314 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | 316 | #[allow(clippy::let_unit_value)] // eg. stm32f334 |
| 315 | let req = dma.request(); | 317 | let req = dma.request(); |
| @@ -339,14 +341,33 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 339 | ..Default::default() | 341 | ..Default::default() |
| 340 | }; | 342 | }; |
| 341 | 343 | ||
| 342 | Transfer::new_write( | 344 | match self.inner.bits() { |
| 343 | dma, | 345 | TimerBits::Bits16 => { |
| 344 | req, | 346 | Transfer::new_write( |
| 345 | duty, | 347 | dma, |
| 346 | self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, | 348 | req, |
| 347 | dma_transfer_option, | 349 | duty, |
| 348 | ) | 350 | self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, |
| 349 | .await | 351 | dma_transfer_option, |
| 352 | ) | ||
| 353 | .await | ||
| 354 | } | ||
| 355 | #[cfg(not(any(stm32l0)))] | ||
| 356 | TimerBits::Bits32 => { | ||
| 357 | #[cfg(not(any(bdma, gpdma)))] | ||
| 358 | panic!("unsupported timer bits"); | ||
| 359 | |||
| 360 | #[cfg(any(bdma, gpdma))] | ||
| 361 | Transfer::new_write( | ||
| 362 | dma, | ||
| 363 | req, | ||
| 364 | duty, | ||
| 365 | self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32, | ||
| 366 | dma_transfer_option, | ||
| 367 | ) | ||
| 368 | .await | ||
| 369 | } | ||
| 370 | }; | ||
| 350 | }; | 371 | }; |
| 351 | 372 | ||
| 352 | // restore output compare state | 373 | // restore output compare state |
| @@ -378,18 +399,23 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 378 | /// | 399 | /// |
| 379 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: | 400 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: |
| 380 | /// | 401 | /// |
| 402 | /// ```rust,ignore | ||
| 381 | /// let dma_buf: [u16; 16] = [ | 403 | /// let dma_buf: [u16; 16] = [ |
| 382 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 | 404 | /// 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 | 405 | /// 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 | 406 | /// 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 | 407 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 |
| 386 | /// ]; | 408 | /// ]; |
| 409 | /// ``` | ||
| 387 | /// | 410 | /// |
| 388 | /// Each group of N values (where N = number of channels) is transferred on one update event, | 411 | /// 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. | 412 | /// updating the duty cycles of all selected channels simultaneously. |
| 390 | /// | 413 | /// |
| 391 | /// Note: | 414 | /// Note: |
| 392 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | 415 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. |
| 416 | /// Also be aware that embassy timers use one of timers internally. It is possible to | ||
| 417 | /// switch this timer by using `time-driver-timX` feature. | ||
| 418 | /// | ||
| 393 | pub async fn waveform_up_multi_channel( | 419 | pub async fn waveform_up_multi_channel( |
| 394 | &mut self, | 420 | &mut self, |
| 395 | dma: Peri<'_, impl super::UpDma<T>>, | 421 | dma: Peri<'_, impl super::UpDma<T>>, |
diff --git a/embassy-stm32/src/ucpd.rs b/embassy-stm32/src/ucpd.rs index 8f259a917..ae86d28f0 100644 --- a/embassy-stm32/src/ucpd.rs +++ b/embassy-stm32/src/ucpd.rs | |||
| @@ -32,7 +32,7 @@ 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-usb/CHANGELOG.md b/embassy-usb/CHANGELOG.md index 0a30bc24b..cfb1bf021 100644 --- a/embassy-usb/CHANGELOG.md +++ b/embassy-usb/CHANGELOG.md | |||
| @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 8 | <!-- next-header --> | 8 | <!-- next-header --> |
| 9 | ## Unreleased - ReleaseDate | 9 | ## Unreleased - ReleaseDate |
| 10 | 10 | ||
| 11 | - Add support for USB HID Boot Protocol Mode | ||
| 12 | |||
| 11 | ## 0.5.1 - 2025-08-26 | 13 | ## 0.5.1 - 2025-08-26 |
| 12 | 14 | ||
| 13 | ## 0.5.0 - 2025-07-16 | 15 | ## 0.5.0 - 2025-07-16 |
diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index 182e1f83f..64e8fd59f 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs | |||
| @@ -15,8 +15,6 @@ use crate::types::InterfaceNumber; | |||
| 15 | use crate::{Builder, Handler}; | 15 | use crate::{Builder, Handler}; |
| 16 | 16 | ||
| 17 | const USB_CLASS_HID: u8 = 0x03; | 17 | const USB_CLASS_HID: u8 = 0x03; |
| 18 | const USB_SUBCLASS_NONE: u8 = 0x00; | ||
| 19 | const USB_PROTOCOL_NONE: u8 = 0x00; | ||
| 20 | 18 | ||
| 21 | // HID | 19 | // HID |
| 22 | const HID_DESC_DESCTYPE_HID: u8 = 0x21; | 20 | const HID_DESC_DESCTYPE_HID: u8 = 0x21; |
| @@ -31,6 +29,52 @@ const HID_REQ_SET_REPORT: u8 = 0x09; | |||
| 31 | const HID_REQ_GET_PROTOCOL: u8 = 0x03; | 29 | const HID_REQ_GET_PROTOCOL: u8 = 0x03; |
| 32 | const HID_REQ_SET_PROTOCOL: u8 = 0x0b; | 30 | const HID_REQ_SET_PROTOCOL: u8 = 0x0b; |
| 33 | 31 | ||
| 32 | /// Get/Set Protocol mapping | ||
| 33 | /// See (7.2.5 and 7.2.6): <https://www.usb.org/sites/default/files/hid1_11.pdf> | ||
| 34 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 35 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 36 | #[repr(u8)] | ||
| 37 | pub enum HidProtocolMode { | ||
| 38 | /// Hid Boot Protocol Mode | ||
| 39 | Boot = 0, | ||
| 40 | /// Hid Report Protocol Mode | ||
| 41 | Report = 1, | ||
| 42 | } | ||
| 43 | |||
| 44 | impl From<u8> for HidProtocolMode { | ||
| 45 | fn from(mode: u8) -> HidProtocolMode { | ||
| 46 | if mode == HidProtocolMode::Boot as u8 { | ||
| 47 | HidProtocolMode::Boot | ||
| 48 | } else { | ||
| 49 | HidProtocolMode::Report | ||
| 50 | } | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | /// USB HID interface subclass values. | ||
| 55 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 56 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 57 | #[repr(u8)] | ||
| 58 | pub enum HidSubclass { | ||
| 59 | /// No subclass, standard HID device. | ||
| 60 | No = 0, | ||
| 61 | /// Boot interface subclass, supports BIOS boot protocol. | ||
| 62 | Boot = 1, | ||
| 63 | } | ||
| 64 | |||
| 65 | /// USB HID protocol values. | ||
| 66 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 67 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 68 | #[repr(u8)] | ||
| 69 | pub enum HidBootProtocol { | ||
| 70 | /// No boot protocol. | ||
| 71 | None = 0, | ||
| 72 | /// Keyboard boot protocol. | ||
| 73 | Keyboard = 1, | ||
| 74 | /// Mouse boot protocol. | ||
| 75 | Mouse = 2, | ||
| 76 | } | ||
| 77 | |||
| 34 | /// Configuration for the HID class. | 78 | /// Configuration for the HID class. |
| 35 | pub struct Config<'d> { | 79 | pub struct Config<'d> { |
| 36 | /// HID report descriptor. | 80 | /// HID report descriptor. |
| @@ -48,6 +92,12 @@ pub struct Config<'d> { | |||
| 48 | 92 | ||
| 49 | /// Max packet size for both the IN and OUT endpoints. | 93 | /// Max packet size for both the IN and OUT endpoints. |
| 50 | pub max_packet_size: u16, | 94 | pub max_packet_size: u16, |
| 95 | |||
| 96 | /// The HID subclass of this interface | ||
| 97 | pub hid_subclass: HidSubclass, | ||
| 98 | |||
| 99 | /// The HID boot protocol of this interface | ||
| 100 | pub hid_boot_protocol: HidBootProtocol, | ||
| 51 | } | 101 | } |
| 52 | 102 | ||
| 53 | /// Report ID | 103 | /// Report ID |
| @@ -109,10 +159,15 @@ fn build<'d, D: Driver<'d>>( | |||
| 109 | ) -> (Option<D::EndpointOut>, D::EndpointIn, &'d AtomicUsize) { | 159 | ) -> (Option<D::EndpointOut>, D::EndpointIn, &'d AtomicUsize) { |
| 110 | let len = config.report_descriptor.len(); | 160 | let len = config.report_descriptor.len(); |
| 111 | 161 | ||
| 112 | let mut func = builder.function(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE); | 162 | let mut func = builder.function(USB_CLASS_HID, config.hid_subclass as u8, config.hid_boot_protocol as u8); |
| 113 | let mut iface = func.interface(); | 163 | let mut iface = func.interface(); |
| 114 | let if_num = iface.interface_number(); | 164 | let if_num = iface.interface_number(); |
| 115 | let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE, None); | 165 | let mut alt = iface.alt_setting( |
| 166 | USB_CLASS_HID, | ||
| 167 | config.hid_subclass as u8, | ||
| 168 | config.hid_boot_protocol as u8, | ||
| 169 | None, | ||
| 170 | ); | ||
| 116 | 171 | ||
| 117 | // HID descriptor | 172 | // HID descriptor |
| 118 | alt.descriptor( | 173 | alt.descriptor( |
| @@ -389,6 +444,23 @@ pub trait RequestHandler { | |||
| 389 | OutResponse::Rejected | 444 | OutResponse::Rejected |
| 390 | } | 445 | } |
| 391 | 446 | ||
| 447 | /// Gets the current hid protocol. | ||
| 448 | /// | ||
| 449 | /// Returns `Report` protocol by default. | ||
| 450 | fn get_protocol(&self) -> HidProtocolMode { | ||
| 451 | HidProtocolMode::Report | ||
| 452 | } | ||
| 453 | |||
| 454 | /// Sets the current hid protocol to `protocol`. | ||
| 455 | /// | ||
| 456 | /// Accepts only `Report` protocol by default. | ||
| 457 | fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { | ||
| 458 | match protocol { | ||
| 459 | HidProtocolMode::Report => OutResponse::Accepted, | ||
| 460 | HidProtocolMode::Boot => OutResponse::Rejected, | ||
| 461 | } | ||
| 462 | } | ||
| 463 | |||
| 392 | /// Get the idle rate for `id`. | 464 | /// Get the idle rate for `id`. |
| 393 | /// | 465 | /// |
| 394 | /// If `id` is `None`, get the idle rate for all reports. Returning `None` | 466 | /// If `id` is `None`, get the idle rate for all reports. Returning `None` |
| @@ -482,11 +554,14 @@ impl<'d> Handler for Control<'d> { | |||
| 482 | _ => Some(OutResponse::Rejected), | 554 | _ => Some(OutResponse::Rejected), |
| 483 | }, | 555 | }, |
| 484 | HID_REQ_SET_PROTOCOL => { | 556 | HID_REQ_SET_PROTOCOL => { |
| 485 | if req.value == 1 { | 557 | let hid_protocol = HidProtocolMode::from(req.value as u8); |
| 486 | Some(OutResponse::Accepted) | 558 | match (self.request_handler.as_mut(), hid_protocol) { |
| 487 | } else { | 559 | (Some(request_handler), hid_protocol) => Some(request_handler.set_protocol(hid_protocol)), |
| 488 | warn!("HID Boot Protocol is unsupported."); | 560 | (None, HidProtocolMode::Report) => Some(OutResponse::Accepted), |
| 489 | Some(OutResponse::Rejected) // UNSUPPORTED: Boot Protocol | 561 | (None, HidProtocolMode::Boot) => { |
| 562 | info!("Received request to switch to Boot protocol mode, but it is disabled by default."); | ||
| 563 | Some(OutResponse::Rejected) | ||
| 564 | } | ||
| 490 | } | 565 | } |
| 491 | } | 566 | } |
| 492 | _ => Some(OutResponse::Rejected), | 567 | _ => Some(OutResponse::Rejected), |
| @@ -539,8 +614,12 @@ impl<'d> Handler for Control<'d> { | |||
| 539 | } | 614 | } |
| 540 | } | 615 | } |
| 541 | HID_REQ_GET_PROTOCOL => { | 616 | HID_REQ_GET_PROTOCOL => { |
| 542 | // UNSUPPORTED: Boot Protocol | 617 | if let Some(request_handler) = self.request_handler.as_mut() { |
| 543 | buf[0] = 1; | 618 | buf[0] = request_handler.get_protocol() as u8; |
| 619 | } else { | ||
| 620 | // Return `Report` protocol mode by default | ||
| 621 | buf[0] = HidProtocolMode::Report as u8; | ||
| 622 | } | ||
| 544 | Some(InResponse::Accepted(&buf[0..1])) | 623 | Some(InResponse::Accepted(&buf[0..1])) |
| 545 | } | 624 | } |
| 546 | _ => Some(InResponse::Rejected), | 625 | _ => Some(InResponse::Rejected), |
diff --git a/examples/nrf52840/src/bin/gpiote_channel.rs b/examples/nrf52840/src/bin/gpiote_channel.rs index c7ddc1d8d..e358779b2 100644 --- a/examples/nrf52840/src/bin/gpiote_channel.rs +++ b/examples/nrf52840/src/bin/gpiote_channel.rs | |||
| @@ -12,10 +12,10 @@ async fn main(_spawner: Spawner) { | |||
| 12 | let p = embassy_nrf::init(Default::default()); | 12 | let p = embassy_nrf::init(Default::default()); |
| 13 | info!("Starting!"); | 13 | info!("Starting!"); |
| 14 | 14 | ||
| 15 | let ch1 = InputChannel::new(p.GPIOTE_CH0, p.P0_11, Pull::Up, InputChannelPolarity::HiToLo); | 15 | let mut ch1 = InputChannel::new(p.GPIOTE_CH0, p.P0_11, Pull::Up, InputChannelPolarity::HiToLo); |
| 16 | let ch2 = InputChannel::new(p.GPIOTE_CH1, p.P0_12, Pull::Up, InputChannelPolarity::LoToHi); | 16 | let mut ch2 = InputChannel::new(p.GPIOTE_CH1, p.P0_12, Pull::Up, InputChannelPolarity::LoToHi); |
| 17 | let ch3 = InputChannel::new(p.GPIOTE_CH2, p.P0_24, Pull::Up, InputChannelPolarity::Toggle); | 17 | let mut ch3 = InputChannel::new(p.GPIOTE_CH2, p.P0_24, Pull::Up, InputChannelPolarity::Toggle); |
| 18 | let ch4 = InputChannel::new(p.GPIOTE_CH3, p.P0_25, Pull::Up, InputChannelPolarity::Toggle); | 18 | let mut ch4 = InputChannel::new(p.GPIOTE_CH3, p.P0_25, Pull::Up, InputChannelPolarity::Toggle); |
| 19 | 19 | ||
| 20 | let button1 = async { | 20 | let button1 = async { |
| 21 | loop { | 21 | loop { |
diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index 1cd730503..7b7303526 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::sync::atomic::{AtomicBool, Ordering}; | 4 | use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; |
| 5 | 5 | ||
| 6 | use defmt::*; | 6 | use defmt::*; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| @@ -13,7 +13,9 @@ use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; | |||
| 13 | use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; | 13 | use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; |
| 14 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 14 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 15 | use embassy_sync::signal::Signal; | 15 | use embassy_sync::signal::Signal; |
| 16 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; | 16 | use embassy_usb::class::hid::{ |
| 17 | HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State, | ||
| 18 | }; | ||
| 17 | use embassy_usb::control::OutResponse; | 19 | use embassy_usb::control::OutResponse; |
| 18 | use embassy_usb::{Builder, Config, Handler}; | 20 | use embassy_usb::{Builder, Config, Handler}; |
| 19 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; | 21 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; |
| @@ -26,6 +28,8 @@ bind_interrupts!(struct Irqs { | |||
| 26 | 28 | ||
| 27 | static SUSPENDED: AtomicBool = AtomicBool::new(false); | 29 | static SUSPENDED: AtomicBool = AtomicBool::new(false); |
| 28 | 30 | ||
| 31 | static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); | ||
| 32 | |||
| 29 | #[embassy_executor::main] | 33 | #[embassy_executor::main] |
| 30 | async fn main(_spawner: Spawner) { | 34 | async fn main(_spawner: Spawner) { |
| 31 | let p = embassy_nrf::init(Default::default()); | 35 | let p = embassy_nrf::init(Default::default()); |
| @@ -45,6 +49,10 @@ async fn main(_spawner: Spawner) { | |||
| 45 | config.max_power = 100; | 49 | config.max_power = 100; |
| 46 | config.max_packet_size_0 = 64; | 50 | config.max_packet_size_0 = 64; |
| 47 | config.supports_remote_wakeup = true; | 51 | config.supports_remote_wakeup = true; |
| 52 | config.composite_with_iads = false; | ||
| 53 | config.device_class = 0; | ||
| 54 | config.device_sub_class = 0; | ||
| 55 | config.device_protocol = 0; | ||
| 48 | 56 | ||
| 49 | // Create embassy-usb DeviceBuilder using the driver and config. | 57 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 50 | // It needs some buffers for building the descriptors. | 58 | // It needs some buffers for building the descriptors. |
| @@ -74,6 +82,8 @@ async fn main(_spawner: Spawner) { | |||
| 74 | request_handler: None, | 82 | request_handler: None, |
| 75 | poll_ms: 60, | 83 | poll_ms: 60, |
| 76 | max_packet_size: 64, | 84 | max_packet_size: 64, |
| 85 | hid_subclass: HidSubclass::Boot, | ||
| 86 | hid_boot_protocol: HidBootProtocol::Keyboard, | ||
| 77 | }; | 87 | }; |
| 78 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); | 88 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); |
| 79 | 89 | ||
| @@ -106,6 +116,11 @@ async fn main(_spawner: Spawner) { | |||
| 106 | if SUSPENDED.load(Ordering::Acquire) { | 116 | if SUSPENDED.load(Ordering::Acquire) { |
| 107 | info!("Triggering remote wakeup"); | 117 | info!("Triggering remote wakeup"); |
| 108 | remote_wakeup.signal(()); | 118 | remote_wakeup.signal(()); |
| 119 | } else if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { | ||
| 120 | match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await { | ||
| 121 | Ok(()) => {} | ||
| 122 | Err(e) => warn!("Failed to send boot report: {:?}", e), | ||
| 123 | }; | ||
| 109 | } else { | 124 | } else { |
| 110 | let report = KeyboardReport { | 125 | let report = KeyboardReport { |
| 111 | keycodes: [4, 0, 0, 0, 0, 0], | 126 | keycodes: [4, 0, 0, 0, 0, 0], |
| @@ -121,16 +136,23 @@ async fn main(_spawner: Spawner) { | |||
| 121 | 136 | ||
| 122 | button.wait_for_high().await; | 137 | button.wait_for_high().await; |
| 123 | info!("RELEASED"); | 138 | info!("RELEASED"); |
| 124 | let report = KeyboardReport { | 139 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 125 | keycodes: [0, 0, 0, 0, 0, 0], | 140 | match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await { |
| 126 | leds: 0, | 141 | Ok(()) => {} |
| 127 | modifier: 0, | 142 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 128 | reserved: 0, | 143 | }; |
| 129 | }; | 144 | } else { |
| 130 | match writer.write_serialize(&report).await { | 145 | let report = KeyboardReport { |
| 131 | Ok(()) => {} | 146 | keycodes: [0, 0, 0, 0, 0, 0], |
| 132 | Err(e) => warn!("Failed to send report: {:?}", e), | 147 | leds: 0, |
| 133 | }; | 148 | modifier: 0, |
| 149 | reserved: 0, | ||
| 150 | }; | ||
| 151 | match writer.write_serialize(&report).await { | ||
| 152 | Ok(()) => {} | ||
| 153 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 154 | }; | ||
| 155 | } | ||
| 134 | } | 156 | } |
| 135 | }; | 157 | }; |
| 136 | 158 | ||
| @@ -156,6 +178,18 @@ impl RequestHandler for MyRequestHandler { | |||
| 156 | OutResponse::Accepted | 178 | OutResponse::Accepted |
| 157 | } | 179 | } |
| 158 | 180 | ||
| 181 | fn get_protocol(&self) -> HidProtocolMode { | ||
| 182 | let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); | ||
| 183 | info!("The current HID protocol mode is: {}", protocol); | ||
| 184 | protocol | ||
| 185 | } | ||
| 186 | |||
| 187 | fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { | ||
| 188 | info!("Switching to HID protocol mode: {}", protocol); | ||
| 189 | HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); | ||
| 190 | OutResponse::Accepted | ||
| 191 | } | ||
| 192 | |||
| 159 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { | 193 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { |
| 160 | info!("Set idle rate for {:?} to {:?}", id, dur); | 194 | info!("Set idle rate for {:?} to {:?}", id, dur); |
| 161 | } | 195 | } |
diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index 3c0fc04e8..6bee4546b 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::sync::atomic::{AtomicU8, Ordering}; | ||
| 5 | |||
| 4 | use defmt::*; | 6 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 6 | use embassy_futures::join::join; | 8 | use embassy_futures::join::join; |
| @@ -8,7 +10,9 @@ use embassy_nrf::usb::Driver; | |||
| 8 | use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; | 10 | use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; |
| 9 | use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; | 11 | use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; |
| 10 | use embassy_time::Timer; | 12 | use embassy_time::Timer; |
| 11 | use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; | 13 | use embassy_usb::class::hid::{ |
| 14 | HidBootProtocol, HidProtocolMode, HidSubclass, HidWriter, ReportId, RequestHandler, State, | ||
| 15 | }; | ||
| 12 | use embassy_usb::control::OutResponse; | 16 | use embassy_usb::control::OutResponse; |
| 13 | use embassy_usb::{Builder, Config}; | 17 | use embassy_usb::{Builder, Config}; |
| 14 | use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; | 18 | use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; |
| @@ -19,6 +23,8 @@ bind_interrupts!(struct Irqs { | |||
| 19 | CLOCK_POWER => usb::vbus_detect::InterruptHandler; | 23 | CLOCK_POWER => usb::vbus_detect::InterruptHandler; |
| 20 | }); | 24 | }); |
| 21 | 25 | ||
| 26 | static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); | ||
| 27 | |||
| 22 | #[embassy_executor::main] | 28 | #[embassy_executor::main] |
| 23 | async fn main(_spawner: Spawner) { | 29 | async fn main(_spawner: Spawner) { |
| 24 | let p = embassy_nrf::init(Default::default()); | 30 | let p = embassy_nrf::init(Default::default()); |
| @@ -37,6 +43,10 @@ async fn main(_spawner: Spawner) { | |||
| 37 | config.serial_number = Some("12345678"); | 43 | config.serial_number = Some("12345678"); |
| 38 | config.max_power = 100; | 44 | config.max_power = 100; |
| 39 | config.max_packet_size_0 = 64; | 45 | config.max_packet_size_0 = 64; |
| 46 | config.composite_with_iads = false; | ||
| 47 | config.device_class = 0; | ||
| 48 | config.device_sub_class = 0; | ||
| 49 | config.device_protocol = 0; | ||
| 40 | 50 | ||
| 41 | // Create embassy-usb DeviceBuilder using the driver and config. | 51 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 42 | // It needs some buffers for building the descriptors. | 52 | // It needs some buffers for building the descriptors. |
| @@ -63,6 +73,8 @@ async fn main(_spawner: Spawner) { | |||
| 63 | request_handler: Some(&mut request_handler), | 73 | request_handler: Some(&mut request_handler), |
| 64 | poll_ms: 60, | 74 | poll_ms: 60, |
| 65 | max_packet_size: 8, | 75 | max_packet_size: 8, |
| 76 | hid_subclass: HidSubclass::Boot, | ||
| 77 | hid_boot_protocol: HidBootProtocol::Mouse, | ||
| 66 | }; | 78 | }; |
| 67 | 79 | ||
| 68 | let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); | 80 | let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); |
| @@ -80,16 +92,26 @@ async fn main(_spawner: Spawner) { | |||
| 80 | Timer::after_millis(500).await; | 92 | Timer::after_millis(500).await; |
| 81 | 93 | ||
| 82 | y = -y; | 94 | y = -y; |
| 83 | let report = MouseReport { | 95 | |
| 84 | buttons: 0, | 96 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 85 | x: 0, | 97 | let buttons = 0u8; |
| 86 | y, | 98 | let x = 0i8; |
| 87 | wheel: 0, | 99 | match writer.write(&[buttons, x as u8, y as u8]).await { |
| 88 | pan: 0, | 100 | Ok(()) => {} |
| 89 | }; | 101 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 90 | match writer.write_serialize(&report).await { | 102 | } |
| 91 | Ok(()) => {} | 103 | } else { |
| 92 | Err(e) => warn!("Failed to send report: {:?}", e), | 104 | let report = MouseReport { |
| 105 | buttons: 0, | ||
| 106 | x: 0, | ||
| 107 | y, | ||
| 108 | wheel: 0, | ||
| 109 | pan: 0, | ||
| 110 | }; | ||
| 111 | match writer.write_serialize(&report).await { | ||
| 112 | Ok(()) => {} | ||
| 113 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 114 | } | ||
| 93 | } | 115 | } |
| 94 | } | 116 | } |
| 95 | }; | 117 | }; |
| @@ -112,6 +134,18 @@ impl RequestHandler for MyRequestHandler { | |||
| 112 | OutResponse::Accepted | 134 | OutResponse::Accepted |
| 113 | } | 135 | } |
| 114 | 136 | ||
| 137 | fn get_protocol(&self) -> HidProtocolMode { | ||
| 138 | let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); | ||
| 139 | info!("The current HID protocol mode is: {}", protocol); | ||
| 140 | protocol | ||
| 141 | } | ||
| 142 | |||
| 143 | fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { | ||
| 144 | info!("Switching to HID protocol mode: {}", protocol); | ||
| 145 | HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); | ||
| 146 | OutResponse::Accepted | ||
| 147 | } | ||
| 148 | |||
| 115 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { | 149 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { |
| 116 | info!("Set idle rate for {:?} to {:?}", id, dur); | 150 | info!("Set idle rate for {:?} to {:?}", id, dur); |
| 117 | } | 151 | } |
diff --git a/examples/nrf5340/src/bin/gpiote_channel.rs b/examples/nrf5340/src/bin/gpiote_channel.rs index a085310ce..41ee732c3 100644 --- a/examples/nrf5340/src/bin/gpiote_channel.rs +++ b/examples/nrf5340/src/bin/gpiote_channel.rs | |||
| @@ -12,10 +12,10 @@ async fn main(_spawner: Spawner) { | |||
| 12 | let p = embassy_nrf::init(Default::default()); | 12 | let p = embassy_nrf::init(Default::default()); |
| 13 | info!("Starting!"); | 13 | info!("Starting!"); |
| 14 | 14 | ||
| 15 | let ch1 = InputChannel::new(p.GPIOTE_CH0, p.P0_23, Pull::Up, InputChannelPolarity::HiToLo); | 15 | let mut ch1 = InputChannel::new(p.GPIOTE_CH0, p.P0_23, Pull::Up, InputChannelPolarity::HiToLo); |
| 16 | let ch2 = InputChannel::new(p.GPIOTE_CH1, p.P0_24, Pull::Up, InputChannelPolarity::LoToHi); | 16 | let mut ch2 = InputChannel::new(p.GPIOTE_CH1, p.P0_24, Pull::Up, InputChannelPolarity::LoToHi); |
| 17 | let ch3 = InputChannel::new(p.GPIOTE_CH2, p.P0_08, Pull::Up, InputChannelPolarity::Toggle); | 17 | let mut ch3 = InputChannel::new(p.GPIOTE_CH2, p.P0_08, Pull::Up, InputChannelPolarity::Toggle); |
| 18 | let ch4 = InputChannel::new(p.GPIOTE_CH3, p.P0_09, Pull::Up, InputChannelPolarity::Toggle); | 18 | let mut ch4 = InputChannel::new(p.GPIOTE_CH3, p.P0_09, Pull::Up, InputChannelPolarity::Toggle); |
| 19 | 19 | ||
| 20 | let button1 = async { | 20 | let button1 = async { |
| 21 | loop { | 21 | loop { |
diff --git a/examples/nrf54l15/memory.x b/examples/nrf54l15/memory.x index 1064c8a5c..332200828 100644 --- a/examples/nrf54l15/memory.x +++ b/examples/nrf54l15/memory.x | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | MEMORY | 1 | MEMORY |
| 2 | { | 2 | { |
| 3 | FLASH : ORIGIN = 0x00000000, LENGTH = 1536K | 3 | FLASH : ORIGIN = 0x00000000, LENGTH = 1524K |
| 4 | RAM : ORIGIN = 0x20000000, LENGTH = 256K | 4 | RAM : ORIGIN = 0x20000000, LENGTH = 256K |
| 5 | } | 5 | } |
diff --git a/examples/nrf54l15/src/bin/gpiote_channel.rs b/examples/nrf54l15/src/bin/gpiote_channel.rs index 6333250ba..cac8823f8 100644 --- a/examples/nrf54l15/src/bin/gpiote_channel.rs +++ b/examples/nrf54l15/src/bin/gpiote_channel.rs | |||
| @@ -12,10 +12,10 @@ async fn main(_spawner: Spawner) { | |||
| 12 | let p = embassy_nrf::init(Default::default()); | 12 | let p = embassy_nrf::init(Default::default()); |
| 13 | info!("Starting!"); | 13 | info!("Starting!"); |
| 14 | 14 | ||
| 15 | let ch1 = InputChannel::new(p.GPIOTE20_CH0, p.P1_13, Pull::Up, InputChannelPolarity::HiToLo); | 15 | let mut ch1 = InputChannel::new(p.GPIOTE20_CH0, p.P1_13, Pull::Up, InputChannelPolarity::HiToLo); |
| 16 | let ch2 = InputChannel::new(p.GPIOTE20_CH1, p.P1_09, Pull::Up, InputChannelPolarity::LoToHi); | 16 | let mut ch2 = InputChannel::new(p.GPIOTE20_CH1, p.P1_09, Pull::Up, InputChannelPolarity::LoToHi); |
| 17 | let ch3 = InputChannel::new(p.GPIOTE20_CH2, p.P1_08, Pull::Up, InputChannelPolarity::Toggle); | 17 | let mut ch3 = InputChannel::new(p.GPIOTE20_CH2, p.P1_08, Pull::Up, InputChannelPolarity::Toggle); |
| 18 | let ch4 = InputChannel::new(p.GPIOTE30_CH0, p.P0_04, Pull::Up, InputChannelPolarity::Toggle); | 18 | let mut ch4 = InputChannel::new(p.GPIOTE30_CH0, p.P0_04, Pull::Up, InputChannelPolarity::Toggle); |
| 19 | 19 | ||
| 20 | let button1 = async { | 20 | let button1 = async { |
| 21 | loop { | 21 | loop { |
diff --git a/examples/rp/src/bin/usb_hid_keyboard.rs b/examples/rp/src/bin/usb_hid_keyboard.rs index a7cb322d8..2f6d169bf 100644 --- a/examples/rp/src/bin/usb_hid_keyboard.rs +++ b/examples/rp/src/bin/usb_hid_keyboard.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::sync::atomic::{AtomicBool, Ordering}; | 4 | use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; |
| 5 | 5 | ||
| 6 | use defmt::*; | 6 | use defmt::*; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| @@ -10,7 +10,9 @@ use embassy_rp::bind_interrupts; | |||
| 10 | use embassy_rp::gpio::{Input, Pull}; | 10 | use embassy_rp::gpio::{Input, Pull}; |
| 11 | use embassy_rp::peripherals::USB; | 11 | use embassy_rp::peripherals::USB; |
| 12 | use embassy_rp::usb::{Driver, InterruptHandler}; | 12 | use embassy_rp::usb::{Driver, InterruptHandler}; |
| 13 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; | 13 | use embassy_usb::class::hid::{ |
| 14 | HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State, | ||
| 15 | }; | ||
| 14 | use embassy_usb::control::OutResponse; | 16 | use embassy_usb::control::OutResponse; |
| 15 | use embassy_usb::{Builder, Config, Handler}; | 17 | use embassy_usb::{Builder, Config, Handler}; |
| 16 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; | 18 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; |
| @@ -20,6 +22,8 @@ bind_interrupts!(struct Irqs { | |||
| 20 | USBCTRL_IRQ => InterruptHandler<USB>; | 22 | USBCTRL_IRQ => InterruptHandler<USB>; |
| 21 | }); | 23 | }); |
| 22 | 24 | ||
| 25 | static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); | ||
| 26 | |||
| 23 | #[embassy_executor::main] | 27 | #[embassy_executor::main] |
| 24 | async fn main(_spawner: Spawner) { | 28 | async fn main(_spawner: Spawner) { |
| 25 | let p = embassy_rp::init(Default::default()); | 29 | let p = embassy_rp::init(Default::default()); |
| @@ -33,6 +37,10 @@ async fn main(_spawner: Spawner) { | |||
| 33 | config.serial_number = Some("12345678"); | 37 | config.serial_number = Some("12345678"); |
| 34 | config.max_power = 100; | 38 | config.max_power = 100; |
| 35 | config.max_packet_size_0 = 64; | 39 | config.max_packet_size_0 = 64; |
| 40 | config.composite_with_iads = false; | ||
| 41 | config.device_class = 0; | ||
| 42 | config.device_sub_class = 0; | ||
| 43 | config.device_protocol = 0; | ||
| 36 | 44 | ||
| 37 | // Create embassy-usb DeviceBuilder using the driver and config. | 45 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 38 | // It needs some buffers for building the descriptors. | 46 | // It needs some buffers for building the descriptors. |
| @@ -63,6 +71,8 @@ async fn main(_spawner: Spawner) { | |||
| 63 | request_handler: None, | 71 | request_handler: None, |
| 64 | poll_ms: 60, | 72 | poll_ms: 60, |
| 65 | max_packet_size: 64, | 73 | max_packet_size: 64, |
| 74 | hid_subclass: HidSubclass::Boot, | ||
| 75 | hid_boot_protocol: HidBootProtocol::Keyboard, | ||
| 66 | }; | 76 | }; |
| 67 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); | 77 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); |
| 68 | 78 | ||
| @@ -86,30 +96,46 @@ async fn main(_spawner: Spawner) { | |||
| 86 | info!("Waiting for HIGH on pin 16"); | 96 | info!("Waiting for HIGH on pin 16"); |
| 87 | signal_pin.wait_for_high().await; | 97 | signal_pin.wait_for_high().await; |
| 88 | info!("HIGH DETECTED"); | 98 | info!("HIGH DETECTED"); |
| 89 | // Create a report with the A key pressed. (no shift modifier) | 99 | |
| 90 | let report = KeyboardReport { | 100 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 91 | keycodes: [4, 0, 0, 0, 0, 0], | 101 | match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await { |
| 92 | leds: 0, | 102 | Ok(()) => {} |
| 93 | modifier: 0, | 103 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 94 | reserved: 0, | 104 | }; |
| 95 | }; | 105 | } else { |
| 96 | // Send the report. | 106 | // Create a report with the A key pressed. (no shift modifier) |
| 97 | match writer.write_serialize(&report).await { | 107 | let report = KeyboardReport { |
| 98 | Ok(()) => {} | 108 | keycodes: [4, 0, 0, 0, 0, 0], |
| 99 | Err(e) => warn!("Failed to send report: {:?}", e), | 109 | leds: 0, |
| 100 | }; | 110 | modifier: 0, |
| 111 | reserved: 0, | ||
| 112 | }; | ||
| 113 | // Send the report. | ||
| 114 | match writer.write_serialize(&report).await { | ||
| 115 | Ok(()) => {} | ||
| 116 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 117 | }; | ||
| 118 | } | ||
| 119 | |||
| 101 | signal_pin.wait_for_low().await; | 120 | signal_pin.wait_for_low().await; |
| 102 | info!("LOW DETECTED"); | 121 | info!("LOW DETECTED"); |
| 103 | let report = KeyboardReport { | 122 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 104 | keycodes: [0, 0, 0, 0, 0, 0], | 123 | match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await { |
| 105 | leds: 0, | 124 | Ok(()) => {} |
| 106 | modifier: 0, | 125 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 107 | reserved: 0, | 126 | }; |
| 108 | }; | 127 | } else { |
| 109 | match writer.write_serialize(&report).await { | 128 | let report = KeyboardReport { |
| 110 | Ok(()) => {} | 129 | keycodes: [0, 0, 0, 0, 0, 0], |
| 111 | Err(e) => warn!("Failed to send report: {:?}", e), | 130 | leds: 0, |
| 112 | }; | 131 | modifier: 0, |
| 132 | reserved: 0, | ||
| 133 | }; | ||
| 134 | match writer.write_serialize(&report).await { | ||
| 135 | Ok(()) => {} | ||
| 136 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 137 | }; | ||
| 138 | } | ||
| 113 | } | 139 | } |
| 114 | }; | 140 | }; |
| 115 | 141 | ||
| @@ -135,6 +161,18 @@ impl RequestHandler for MyRequestHandler { | |||
| 135 | OutResponse::Accepted | 161 | OutResponse::Accepted |
| 136 | } | 162 | } |
| 137 | 163 | ||
| 164 | fn get_protocol(&self) -> HidProtocolMode { | ||
| 165 | let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); | ||
| 166 | info!("The current HID protocol mode is: {}", protocol); | ||
| 167 | protocol | ||
| 168 | } | ||
| 169 | |||
| 170 | fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { | ||
| 171 | info!("Switching to HID protocol mode: {}", protocol); | ||
| 172 | HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); | ||
| 173 | OutResponse::Accepted | ||
| 174 | } | ||
| 175 | |||
| 138 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { | 176 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { |
| 139 | info!("Set idle rate for {:?} to {:?}", id, dur); | 177 | info!("Set idle rate for {:?} to {:?}", id, dur); |
| 140 | } | 178 | } |
diff --git a/examples/rp/src/bin/usb_hid_mouse.rs b/examples/rp/src/bin/usb_hid_mouse.rs index 4454c593c..dc331cbdd 100755 --- a/examples/rp/src/bin/usb_hid_mouse.rs +++ b/examples/rp/src/bin/usb_hid_mouse.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::sync::atomic::{AtomicBool, Ordering}; | 4 | use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; |
| 5 | 5 | ||
| 6 | use defmt::*; | 6 | use defmt::*; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| @@ -11,7 +11,9 @@ use embassy_rp::clocks::RoscRng; | |||
| 11 | use embassy_rp::peripherals::USB; | 11 | use embassy_rp::peripherals::USB; |
| 12 | use embassy_rp::usb::{Driver, InterruptHandler}; | 12 | use embassy_rp::usb::{Driver, InterruptHandler}; |
| 13 | use embassy_time::Timer; | 13 | use embassy_time::Timer; |
| 14 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; | 14 | use embassy_usb::class::hid::{ |
| 15 | HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State, | ||
| 16 | }; | ||
| 15 | use embassy_usb::control::OutResponse; | 17 | use embassy_usb::control::OutResponse; |
| 16 | use embassy_usb::{Builder, Config, Handler}; | 18 | use embassy_usb::{Builder, Config, Handler}; |
| 17 | use rand::Rng; | 19 | use rand::Rng; |
| @@ -22,6 +24,8 @@ bind_interrupts!(struct Irqs { | |||
| 22 | USBCTRL_IRQ => InterruptHandler<USB>; | 24 | USBCTRL_IRQ => InterruptHandler<USB>; |
| 23 | }); | 25 | }); |
| 24 | 26 | ||
| 27 | static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); | ||
| 28 | |||
| 25 | #[embassy_executor::main] | 29 | #[embassy_executor::main] |
| 26 | async fn main(_spawner: Spawner) { | 30 | async fn main(_spawner: Spawner) { |
| 27 | let p = embassy_rp::init(Default::default()); | 31 | let p = embassy_rp::init(Default::default()); |
| @@ -35,6 +39,10 @@ async fn main(_spawner: Spawner) { | |||
| 35 | config.serial_number = Some("12345678"); | 39 | config.serial_number = Some("12345678"); |
| 36 | config.max_power = 100; | 40 | config.max_power = 100; |
| 37 | config.max_packet_size_0 = 64; | 41 | config.max_packet_size_0 = 64; |
| 42 | config.composite_with_iads = false; | ||
| 43 | config.device_class = 0; | ||
| 44 | config.device_sub_class = 0; | ||
| 45 | config.device_protocol = 0; | ||
| 38 | 46 | ||
| 39 | // Create embassy-usb DeviceBuilder using the driver and config. | 47 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 40 | // It needs some buffers for building the descriptors. | 48 | // It needs some buffers for building the descriptors. |
| @@ -65,6 +73,8 @@ async fn main(_spawner: Spawner) { | |||
| 65 | request_handler: None, | 73 | request_handler: None, |
| 66 | poll_ms: 60, | 74 | poll_ms: 60, |
| 67 | max_packet_size: 64, | 75 | max_packet_size: 64, |
| 76 | hid_subclass: HidSubclass::Boot, | ||
| 77 | hid_boot_protocol: HidBootProtocol::Mouse, | ||
| 68 | }; | 78 | }; |
| 69 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); | 79 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); |
| 70 | 80 | ||
| @@ -83,17 +93,29 @@ async fn main(_spawner: Spawner) { | |||
| 83 | loop { | 93 | loop { |
| 84 | // every 1 second | 94 | // every 1 second |
| 85 | _ = Timer::after_secs(1).await; | 95 | _ = Timer::after_secs(1).await; |
| 86 | let report = MouseReport { | 96 | |
| 87 | buttons: 0, | 97 | let x = rng.random_range(-100..100); // random small x movement |
| 88 | x: rng.random_range(-100..100), // random small x movement | 98 | let y = rng.random_range(-100..100); // random small y movement |
| 89 | y: rng.random_range(-100..100), // random small y movement | 99 | |
| 90 | wheel: 0, | 100 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 91 | pan: 0, | 101 | let buttons = 0u8; |
| 92 | }; | 102 | match writer.write(&[buttons, x as u8, y as u8]).await { |
| 93 | // Send the report. | 103 | Ok(()) => {} |
| 94 | match writer.write_serialize(&report).await { | 104 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 95 | Ok(()) => {} | 105 | } |
| 96 | Err(e) => warn!("Failed to send report: {:?}", e), | 106 | } else { |
| 107 | let report = MouseReport { | ||
| 108 | buttons: 0, | ||
| 109 | x, | ||
| 110 | y, | ||
| 111 | wheel: 0, | ||
| 112 | pan: 0, | ||
| 113 | }; | ||
| 114 | // Send the report. | ||
| 115 | match writer.write_serialize(&report).await { | ||
| 116 | Ok(()) => {} | ||
| 117 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 118 | } | ||
| 97 | } | 119 | } |
| 98 | } | 120 | } |
| 99 | }; | 121 | }; |
| @@ -120,6 +142,18 @@ impl RequestHandler for MyRequestHandler { | |||
| 120 | OutResponse::Accepted | 142 | OutResponse::Accepted |
| 121 | } | 143 | } |
| 122 | 144 | ||
| 145 | fn get_protocol(&self) -> HidProtocolMode { | ||
| 146 | let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); | ||
| 147 | info!("The current HID protocol mode is: {}", protocol); | ||
| 148 | protocol | ||
| 149 | } | ||
| 150 | |||
| 151 | fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { | ||
| 152 | info!("Switching to HID protocol mode: {}", protocol); | ||
| 153 | HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); | ||
| 154 | OutResponse::Accepted | ||
| 155 | } | ||
| 156 | |||
| 123 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { | 157 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { |
| 124 | info!("Set idle rate for {:?} to {:?}", id, dur); | 158 | info!("Set idle rate for {:?} to {:?}", id, dur); |
| 125 | } | 159 | } |
diff --git a/examples/rp235x/src/bin/usb_hid_keyboard.rs b/examples/rp235x/src/bin/usb_hid_keyboard.rs index 6f496e23a..d8f64c470 100644 --- a/examples/rp235x/src/bin/usb_hid_keyboard.rs +++ b/examples/rp235x/src/bin/usb_hid_keyboard.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::sync::atomic::{AtomicBool, Ordering}; | 4 | use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; |
| 5 | 5 | ||
| 6 | use defmt::*; | 6 | use defmt::*; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| @@ -10,7 +10,9 @@ use embassy_rp::bind_interrupts; | |||
| 10 | use embassy_rp::gpio::{Input, Pull}; | 10 | use embassy_rp::gpio::{Input, Pull}; |
| 11 | use embassy_rp::peripherals::USB; | 11 | use embassy_rp::peripherals::USB; |
| 12 | use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler}; | 12 | use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler}; |
| 13 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State as HidState}; | 13 | use embassy_usb::class::hid::{ |
| 14 | HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State as HidState, | ||
| 15 | }; | ||
| 14 | use embassy_usb::control::OutResponse; | 16 | use embassy_usb::control::OutResponse; |
| 15 | use embassy_usb::{Builder, Config, Handler}; | 17 | use embassy_usb::{Builder, Config, Handler}; |
| 16 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; | 18 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; |
| @@ -20,6 +22,8 @@ bind_interrupts!(struct Irqs { | |||
| 20 | USBCTRL_IRQ => InterruptHandler<USB>; | 22 | USBCTRL_IRQ => InterruptHandler<USB>; |
| 21 | }); | 23 | }); |
| 22 | 24 | ||
| 25 | static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); | ||
| 26 | |||
| 23 | #[embassy_executor::main] | 27 | #[embassy_executor::main] |
| 24 | async fn main(_spawner: Spawner) { | 28 | async fn main(_spawner: Spawner) { |
| 25 | let p = embassy_rp::init(Default::default()); | 29 | let p = embassy_rp::init(Default::default()); |
| @@ -33,6 +37,10 @@ async fn main(_spawner: Spawner) { | |||
| 33 | config.serial_number = Some("12345678"); | 37 | config.serial_number = Some("12345678"); |
| 34 | config.max_power = 100; | 38 | config.max_power = 100; |
| 35 | config.max_packet_size_0 = 64; | 39 | config.max_packet_size_0 = 64; |
| 40 | config.composite_with_iads = false; | ||
| 41 | config.device_class = 0; | ||
| 42 | config.device_sub_class = 0; | ||
| 43 | config.device_protocol = 0; | ||
| 36 | 44 | ||
| 37 | // Create embassy-usb DeviceBuilder using the driver and config. | 45 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 38 | // It needs some buffers for building the descriptors. | 46 | // It needs some buffers for building the descriptors. |
| @@ -63,6 +71,8 @@ async fn main(_spawner: Spawner) { | |||
| 63 | request_handler: None, | 71 | request_handler: None, |
| 64 | poll_ms: 60, | 72 | poll_ms: 60, |
| 65 | max_packet_size: 64, | 73 | max_packet_size: 64, |
| 74 | hid_subclass: HidSubclass::Boot, | ||
| 75 | hid_boot_protocol: HidBootProtocol::Keyboard, | ||
| 66 | }; | 76 | }; |
| 67 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); | 77 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); |
| 68 | 78 | ||
| @@ -86,30 +96,45 @@ async fn main(_spawner: Spawner) { | |||
| 86 | info!("Waiting for HIGH on pin 16"); | 96 | info!("Waiting for HIGH on pin 16"); |
| 87 | signal_pin.wait_for_high().await; | 97 | signal_pin.wait_for_high().await; |
| 88 | info!("HIGH DETECTED"); | 98 | info!("HIGH DETECTED"); |
| 89 | // Create a report with the A key pressed. (no shift modifier) | 99 | |
| 90 | let report = KeyboardReport { | 100 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 91 | keycodes: [4, 0, 0, 0, 0, 0], | 101 | match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await { |
| 92 | leds: 0, | 102 | Ok(()) => {} |
| 93 | modifier: 0, | 103 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 94 | reserved: 0, | 104 | }; |
| 95 | }; | 105 | } else { |
| 96 | // Send the report. | 106 | // Create a report with the A key pressed. (no shift modifier) |
| 97 | match writer.write_serialize(&report).await { | 107 | let report = KeyboardReport { |
| 98 | Ok(()) => {} | 108 | keycodes: [4, 0, 0, 0, 0, 0], |
| 99 | Err(e) => warn!("Failed to send report: {:?}", e), | 109 | leds: 0, |
| 100 | }; | 110 | modifier: 0, |
| 111 | reserved: 0, | ||
| 112 | }; | ||
| 113 | // Send the report. | ||
| 114 | match writer.write_serialize(&report).await { | ||
| 115 | Ok(()) => {} | ||
| 116 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 117 | }; | ||
| 118 | } | ||
| 101 | signal_pin.wait_for_low().await; | 119 | signal_pin.wait_for_low().await; |
| 102 | info!("LOW DETECTED"); | 120 | info!("LOW DETECTED"); |
| 103 | let report = KeyboardReport { | 121 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 104 | keycodes: [0, 0, 0, 0, 0, 0], | 122 | match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await { |
| 105 | leds: 0, | 123 | Ok(()) => {} |
| 106 | modifier: 0, | 124 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 107 | reserved: 0, | 125 | }; |
| 108 | }; | 126 | } else { |
| 109 | match writer.write_serialize(&report).await { | 127 | let report = KeyboardReport { |
| 110 | Ok(()) => {} | 128 | keycodes: [0, 0, 0, 0, 0, 0], |
| 111 | Err(e) => warn!("Failed to send report: {:?}", e), | 129 | leds: 0, |
| 112 | }; | 130 | modifier: 0, |
| 131 | reserved: 0, | ||
| 132 | }; | ||
| 133 | match writer.write_serialize(&report).await { | ||
| 134 | Ok(()) => {} | ||
| 135 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 136 | }; | ||
| 137 | } | ||
| 113 | } | 138 | } |
| 114 | }; | 139 | }; |
| 115 | 140 | ||
| @@ -135,6 +160,18 @@ impl RequestHandler for MyRequestHandler { | |||
| 135 | OutResponse::Accepted | 160 | OutResponse::Accepted |
| 136 | } | 161 | } |
| 137 | 162 | ||
| 163 | fn get_protocol(&self) -> HidProtocolMode { | ||
| 164 | let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); | ||
| 165 | info!("The current HID protocol mode is: {}", protocol); | ||
| 166 | protocol | ||
| 167 | } | ||
| 168 | |||
| 169 | fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { | ||
| 170 | info!("Switching to HID protocol mode: {}", protocol); | ||
| 171 | HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); | ||
| 172 | OutResponse::Accepted | ||
| 173 | } | ||
| 174 | |||
| 138 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { | 175 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { |
| 139 | info!("Set idle rate for {:?} to {:?}", id, dur); | 176 | info!("Set idle rate for {:?} to {:?}", id, dur); |
| 140 | } | 177 | } |
diff --git a/examples/stm32c0/src/bin/adc.rs b/examples/stm32c0/src/bin/adc.rs index 1f54b0b18..ad597b63c 100644 --- a/examples/stm32c0/src/bin/adc.rs +++ b/examples/stm32c0/src/bin/adc.rs | |||
| @@ -3,7 +3,6 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::adc::vals::Scandir; | ||
| 7 | use embassy_stm32::adc::{Adc, AdcChannel, AnyAdcChannel, Resolution, SampleTime}; | 6 | use embassy_stm32::adc::{Adc, AdcChannel, AnyAdcChannel, Resolution, SampleTime}; |
| 8 | use embassy_stm32::peripherals::ADC1; | 7 | use embassy_stm32::peripherals::ADC1; |
| 9 | use embassy_time::Timer; | 8 | use embassy_time::Timer; |
| @@ -17,7 +16,7 @@ async fn main(_spawner: Spawner) { | |||
| 17 | info!("ADC STM32C0 example."); | 16 | info!("ADC STM32C0 example."); |
| 18 | 17 | ||
| 19 | // We need to set certain sample time to be able to read temp sensor. | 18 | // We need to set certain sample time to be able to read temp sensor. |
| 20 | let mut adc = Adc::new(p.ADC1, SampleTime::CYCLES12_5, Resolution::BITS12); | 19 | let mut adc = Adc::new(p.ADC1, Resolution::BITS12); |
| 21 | let mut temp = adc.enable_temperature().degrade_adc(); | 20 | let mut temp = adc.enable_temperature().degrade_adc(); |
| 22 | let mut vref = adc.enable_vrefint().degrade_adc(); | 21 | let mut vref = adc.enable_vrefint().degrade_adc(); |
| 23 | let mut pin0 = p.PA0.degrade_adc(); | 22 | let mut pin0 = p.PA0.degrade_adc(); |
| @@ -27,16 +26,20 @@ async fn main(_spawner: Spawner) { | |||
| 27 | 26 | ||
| 28 | loop { | 27 | loop { |
| 29 | info!("============================"); | 28 | info!("============================"); |
| 30 | let blocking_temp = adc.blocking_read(&mut temp); | 29 | let blocking_temp = adc.blocking_read(&mut temp, SampleTime::CYCLES12_5); |
| 31 | let blocking_vref = adc.blocking_read(&mut vref); | 30 | let blocking_vref = adc.blocking_read(&mut vref, SampleTime::CYCLES12_5); |
| 32 | let blocing_pin0 = adc.blocking_read(&mut pin0); | 31 | let blocing_pin0 = adc.blocking_read(&mut pin0, SampleTime::CYCLES12_5); |
| 33 | info!( | 32 | info!( |
| 34 | "Blocking ADC read: vref = {}, temp = {}, pin0 = {}.", | 33 | "Blocking ADC read: vref = {}, temp = {}, pin0 = {}.", |
| 35 | blocking_vref, blocking_temp, blocing_pin0 | 34 | blocking_vref, blocking_temp, blocing_pin0 |
| 36 | ); | 35 | ); |
| 37 | 36 | ||
| 38 | let channels_seqence: [&mut AnyAdcChannel<ADC1>; 3] = [&mut vref, &mut temp, &mut pin0]; | 37 | let channels_sequence: [(&mut AnyAdcChannel<ADC1>, SampleTime); 3] = [ |
| 39 | adc.read(dma.reborrow(), channels_seqence.into_iter(), &mut read_buffer) | 38 | (&mut vref, SampleTime::CYCLES12_5), |
| 39 | (&mut temp, SampleTime::CYCLES12_5), | ||
| 40 | (&mut pin0, SampleTime::CYCLES12_5), | ||
| 41 | ]; | ||
| 42 | adc.read(dma.reborrow(), channels_sequence.into_iter(), &mut read_buffer) | ||
| 40 | .await; | 43 | .await; |
| 41 | // Values are ordered according to hardware ADC channel number! | 44 | // Values are ordered according to hardware ADC channel number! |
| 42 | info!( | 45 | info!( |
| @@ -44,15 +47,6 @@ async fn main(_spawner: Spawner) { | |||
| 44 | read_buffer[0], read_buffer[1], read_buffer[2] | 47 | read_buffer[0], read_buffer[1], read_buffer[2] |
| 45 | ); | 48 | ); |
| 46 | 49 | ||
| 47 | let hw_channel_selection: u32 = | ||
| 48 | (1 << temp.get_hw_channel()) + (1 << vref.get_hw_channel()) + (1 << pin0.get_hw_channel()); | ||
| 49 | adc.read_in_hw_order(dma.reborrow(), hw_channel_selection, Scandir::UP, &mut read_buffer) | ||
| 50 | .await; | ||
| 51 | info!( | ||
| 52 | "DMA ADC read in hardware order: vref = {}, temp = {}, pin0 = {}.", | ||
| 53 | read_buffer[2], read_buffer[1], read_buffer[0] | ||
| 54 | ); | ||
| 55 | |||
| 56 | Timer::after_millis(2000).await; | 50 | Timer::after_millis(2000).await; |
| 57 | } | 51 | } |
| 58 | } | 52 | } |
diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index a78873d21..177dd0ac2 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml | |||
| @@ -16,6 +16,7 @@ panic-probe = { version = "1.0.0", features = ["print-defmt"] } | |||
| 16 | embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } | 16 | embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } |
| 17 | embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 17 | embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 18 | embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 18 | embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 19 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | ||
| 19 | static_cell = "2" | 20 | static_cell = "2" |
| 20 | portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] } | 21 | portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] } |
| 21 | 22 | ||
diff --git a/examples/stm32f0/src/bin/adc-watchdog.rs b/examples/stm32f0/src/bin/adc-watchdog.rs index ff98aac8e..6879dd10a 100644 --- a/examples/stm32f0/src/bin/adc-watchdog.rs +++ b/examples/stm32f0/src/bin/adc-watchdog.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::adc::{self, Adc, WatchdogChannels}; | 6 | use embassy_stm32::adc::{self, Adc, SampleTime, WatchdogChannels}; |
| 7 | use embassy_stm32::bind_interrupts; | 7 | use embassy_stm32::bind_interrupts; |
| 8 | use embassy_stm32::peripherals::ADC1; | 8 | use embassy_stm32::peripherals::ADC1; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -23,12 +23,12 @@ async fn main(_spawner: Spawner) { | |||
| 23 | loop { | 23 | loop { |
| 24 | // Wait for pin to go high | 24 | // Wait for pin to go high |
| 25 | adc.init_watchdog(WatchdogChannels::from_channel(&pin), 0, 0x07F); | 25 | adc.init_watchdog(WatchdogChannels::from_channel(&pin), 0, 0x07F); |
| 26 | let v_high = adc.monitor_watchdog().await; | 26 | let v_high = adc.monitor_watchdog(SampleTime::CYCLES13_5).await; |
| 27 | info!("ADC sample is high {}", v_high); | 27 | info!("ADC sample is high {}", v_high); |
| 28 | 28 | ||
| 29 | // Wait for pin to go low | 29 | // Wait for pin to go low |
| 30 | adc.init_watchdog(WatchdogChannels::from_channel(&pin), 0x01f, 0xFFF); | 30 | adc.init_watchdog(WatchdogChannels::from_channel(&pin), 0x01f, 0xFFF); |
| 31 | let v_low = adc.monitor_watchdog().await; | 31 | let v_low = adc.monitor_watchdog(SampleTime::CYCLES13_5).await; |
| 32 | info!("ADC sample is low {}", v_low); | 32 | info!("ADC sample is low {}", v_low); |
| 33 | } | 33 | } |
| 34 | } | 34 | } |
diff --git a/examples/stm32f0/src/bin/adc.rs b/examples/stm32f0/src/bin/adc.rs index 8825e2687..fafeeffaf 100644 --- a/examples/stm32f0/src/bin/adc.rs +++ b/examples/stm32f0/src/bin/adc.rs | |||
| @@ -19,11 +19,10 @@ async fn main(_spawner: Spawner) { | |||
| 19 | info!("Hello World!"); | 19 | info!("Hello World!"); |
| 20 | 20 | ||
| 21 | let mut adc = Adc::new(p.ADC1, Irqs); | 21 | let mut adc = Adc::new(p.ADC1, Irqs); |
| 22 | adc.set_sample_time(SampleTime::CYCLES71_5); | ||
| 23 | let mut pin = p.PA1; | 22 | let mut pin = p.PA1; |
| 24 | 23 | ||
| 25 | let mut vrefint = adc.enable_vref(); | 24 | let mut vrefint = adc.enable_vref(); |
| 26 | let vrefint_sample = adc.read(&mut vrefint).await; | 25 | let vrefint_sample = adc.read(&mut vrefint, SampleTime::CYCLES13_5).await; |
| 27 | let convert_to_millivolts = |sample| { | 26 | let convert_to_millivolts = |sample| { |
| 28 | // From https://www.st.com/resource/en/datasheet/stm32f031c6.pdf | 27 | // From https://www.st.com/resource/en/datasheet/stm32f031c6.pdf |
| 29 | // 6.3.4 Embedded reference voltage | 28 | // 6.3.4 Embedded reference voltage |
| @@ -33,7 +32,7 @@ async fn main(_spawner: Spawner) { | |||
| 33 | }; | 32 | }; |
| 34 | 33 | ||
| 35 | loop { | 34 | loop { |
| 36 | let v = adc.read(&mut pin).await; | 35 | let v = adc.read(&mut pin, SampleTime::CYCLES13_5).await; |
| 37 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); | 36 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); |
| 38 | Timer::after_millis(100).await; | 37 | Timer::after_millis(100).await; |
| 39 | } | 38 | } |
diff --git a/examples/stm32f0/src/bin/i2c_master.rs b/examples/stm32f0/src/bin/i2c_master.rs new file mode 100644 index 000000000..2e61ecdf7 --- /dev/null +++ b/examples/stm32f0/src/bin/i2c_master.rs | |||
| @@ -0,0 +1,609 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | // Hardware Setup for NUCLEO-F072RB: | ||
| 5 | // - I2C1 pins: PB8 (SCL), PB9 (SDA) on CN5 connector | ||
| 6 | // - Connect to I2C slave device (e.g., Digilent Analog Discovery I2C slave, or EEPROM) | ||
| 7 | // - Default slave address: 0x50 | ||
| 8 | // - Pull-up resistors: 4.7kΩ on both SCL and SDA | ||
| 9 | // - CN5 Pin 10 (PB8/SCL) and CN5 Pin 9 (PB9/SDA) | ||
| 10 | // | ||
| 11 | // Analog Discovery - Waveforms Setup: | ||
| 12 | // - Increase buffer size: Settings -> Device Manager -> Option 4 | ||
| 13 | // - Run Protocol Analyzer | ||
| 14 | // - Configure as I2C Slave at address 0x50 | ||
| 15 | // - Connect and configure DIO pins for SCL and SDA | ||
| 16 | // - Frequency: 100kHz - [✓] Clock Stretching | ||
| 17 | |||
| 18 | use defmt::*; | ||
| 19 | use embassy_executor::Spawner; | ||
| 20 | use embassy_stm32::i2c::{Config, I2c, Master}; | ||
| 21 | use embassy_stm32::mode::{Async, Blocking}; | ||
| 22 | use embassy_stm32::time::Hertz; | ||
| 23 | use embassy_stm32::{bind_interrupts, i2c, peripherals}; | ||
| 24 | use embassy_time::Timer; | ||
| 25 | use embedded_hal_1::i2c::Operation; | ||
| 26 | use {defmt_rtt as _, panic_probe as _}; | ||
| 27 | |||
| 28 | bind_interrupts!(struct Irqs { | ||
| 29 | I2C1 => i2c::EventInterruptHandler<peripherals::I2C1>, i2c::ErrorInterruptHandler<peripherals::I2C1>; | ||
| 30 | }); | ||
| 31 | |||
| 32 | #[embassy_executor::main] | ||
| 33 | async fn main(_spawner: Spawner) { | ||
| 34 | let p = embassy_stm32::init(Default::default()); | ||
| 35 | info!("Run stm32 I2C v2 Master Tests..."); | ||
| 36 | |||
| 37 | let mut i2c_peri = p.I2C1; | ||
| 38 | let mut scl = p.PB8; | ||
| 39 | let mut sda = p.PB9; | ||
| 40 | |||
| 41 | let mut config = Config::default(); | ||
| 42 | config.frequency = Hertz(100_000); | ||
| 43 | |||
| 44 | // I2C slave address for Analog Discovery or test EEPROM | ||
| 45 | let slave_addr = 0x50u8; | ||
| 46 | |||
| 47 | // Wait for slave device to be ready | ||
| 48 | Timer::after_millis(100).await; | ||
| 49 | |||
| 50 | // ========== BLOCKING DIRECT API TESTS ========== | ||
| 51 | info!("========== BLOCKING DIRECT API TESTS =========="); | ||
| 52 | { | ||
| 53 | let mut i2c = I2c::new_blocking(i2c_peri.reborrow(), scl.reborrow(), sda.reborrow(), config); | ||
| 54 | |||
| 55 | info!("=== Test 1: Direct blocking_write ==="); | ||
| 56 | test_blocking_write(&mut i2c, slave_addr); | ||
| 57 | |||
| 58 | info!("=== Test 2: Direct blocking_read ==="); | ||
| 59 | test_blocking_read(&mut i2c, slave_addr); | ||
| 60 | |||
| 61 | info!("=== Test 3: Direct blocking_write_read ==="); | ||
| 62 | test_blocking_write_read(&mut i2c, slave_addr); | ||
| 63 | |||
| 64 | info!("=== Test 4: Direct blocking_write_vectored ==="); | ||
| 65 | test_blocking_write_vectored(&mut i2c, slave_addr); | ||
| 66 | |||
| 67 | info!("=== Test 5: Large buffer (>255 bytes) ==="); | ||
| 68 | test_blocking_large_buffer(&mut i2c, slave_addr); | ||
| 69 | |||
| 70 | info!("Blocking direct API tests OK"); | ||
| 71 | } | ||
| 72 | |||
| 73 | Timer::after_millis(100).await; | ||
| 74 | |||
| 75 | // ========== BLOCKING TRANSACTION TESTS ========== | ||
| 76 | info!("========== BLOCKING TRANSACTION TESTS =========="); | ||
| 77 | { | ||
| 78 | let mut i2c = I2c::new_blocking(i2c_peri.reborrow(), scl.reborrow(), sda.reborrow(), config); | ||
| 79 | |||
| 80 | info!("=== Test 6: Consecutive Writes (Should Merge) ==="); | ||
| 81 | test_consecutive_writes_blocking(&mut i2c, slave_addr); | ||
| 82 | |||
| 83 | info!("=== Test 7: Consecutive Reads (Should Merge) ==="); | ||
| 84 | test_consecutive_reads_blocking(&mut i2c, slave_addr); | ||
| 85 | |||
| 86 | info!("=== Test 8: Write then Read (RESTART) ==="); | ||
| 87 | test_write_then_read_blocking(&mut i2c, slave_addr); | ||
| 88 | |||
| 89 | info!("=== Test 9: Read then Write (RESTART) ==="); | ||
| 90 | test_read_then_write_blocking(&mut i2c, slave_addr); | ||
| 91 | |||
| 92 | info!("=== Test 10: Complex Mixed Sequence ==="); | ||
| 93 | test_mixed_sequence_blocking(&mut i2c, slave_addr); | ||
| 94 | |||
| 95 | info!("=== Test 11: Single Operations ==="); | ||
| 96 | test_single_operations_blocking(&mut i2c, slave_addr); | ||
| 97 | |||
| 98 | info!("Blocking transaction tests OK"); | ||
| 99 | } | ||
| 100 | |||
| 101 | Timer::after_millis(100).await; | ||
| 102 | |||
| 103 | // ========== ASYNC TESTS (DMA) ========== | ||
| 104 | info!("========== ASYNC TESTS (DMA) =========="); | ||
| 105 | { | ||
| 106 | let tx_dma = p.DMA1_CH2; | ||
| 107 | let rx_dma = p.DMA1_CH3; | ||
| 108 | |||
| 109 | let mut i2c = I2c::new(i2c_peri, scl, sda, Irqs, tx_dma, rx_dma, config); | ||
| 110 | |||
| 111 | // Direct API tests (reusing same I2C instance) | ||
| 112 | info!("=== Direct API Test 1: write() ==="); | ||
| 113 | test_async_write(&mut i2c, slave_addr).await; | ||
| 114 | |||
| 115 | info!("=== Direct API Test 2: read() ==="); | ||
| 116 | test_async_read(&mut i2c, slave_addr).await; | ||
| 117 | |||
| 118 | info!("=== Direct API Test 3: write_read() ==="); | ||
| 119 | test_async_write_read(&mut i2c, slave_addr).await; | ||
| 120 | |||
| 121 | info!("=== Direct API Test 4: write_vectored() ==="); | ||
| 122 | test_async_write_vectored(&mut i2c, slave_addr).await; | ||
| 123 | |||
| 124 | info!("=== Direct API Test 5: Large buffer (>255 bytes) ==="); | ||
| 125 | test_async_large_buffer(&mut i2c, slave_addr).await; | ||
| 126 | |||
| 127 | info!("Async Direct API tests OK"); | ||
| 128 | |||
| 129 | // Transaction tests | ||
| 130 | info!("=== Transaction Test 6: Consecutive Writes (Should Merge) ==="); | ||
| 131 | test_consecutive_writes_async(&mut i2c, slave_addr).await; | ||
| 132 | |||
| 133 | info!("=== Transaction Test 7: Consecutive Reads (Should Merge) ==="); | ||
| 134 | test_consecutive_reads_async(&mut i2c, slave_addr).await; | ||
| 135 | |||
| 136 | info!("=== Transaction Test 8: Write then Read (RESTART) ==="); | ||
| 137 | test_write_then_read_async(&mut i2c, slave_addr).await; | ||
| 138 | |||
| 139 | info!("=== Transaction Test 9: Read then Write (RESTART) ==="); | ||
| 140 | test_read_then_write_async(&mut i2c, slave_addr).await; | ||
| 141 | |||
| 142 | info!("=== Transaction Test 10: Complex Mixed Sequence ==="); | ||
| 143 | test_mixed_sequence_async(&mut i2c, slave_addr).await; | ||
| 144 | |||
| 145 | info!("=== Transaction Test 11: Single Operations ==="); | ||
| 146 | test_single_operations_async(&mut i2c, slave_addr).await; | ||
| 147 | |||
| 148 | info!("Async transaction tests OK"); | ||
| 149 | } | ||
| 150 | |||
| 151 | info!("All tests OK"); | ||
| 152 | cortex_m::asm::bkpt(); | ||
| 153 | } | ||
| 154 | |||
| 155 | // ==================== BLOCKING DIRECT API TEST FUNCTIONS ==================== | ||
| 156 | |||
| 157 | fn test_blocking_write(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { | ||
| 158 | let write_data = [0x42, 0x43, 0x44, 0x45]; | ||
| 159 | |||
| 160 | match i2c.blocking_write(addr, &write_data) { | ||
| 161 | Ok(_) => info!("✓ blocking_write succeeded: {:02x}", write_data), | ||
| 162 | Err(e) => { | ||
| 163 | error!("✗ blocking_write failed: {:?}", e); | ||
| 164 | defmt::panic!("Test failed: blocking_write"); | ||
| 165 | } | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | fn test_blocking_read(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { | ||
| 170 | let mut read_buf = [0u8; 8]; | ||
| 171 | |||
| 172 | match i2c.blocking_read(addr, &mut read_buf) { | ||
| 173 | Ok(_) => info!("✓ blocking_read succeeded: {:02x}", read_buf), | ||
| 174 | Err(e) => { | ||
| 175 | error!("✗ blocking_read failed: {:?}", e); | ||
| 176 | defmt::panic!("Test failed: blocking_read"); | ||
| 177 | } | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | fn test_blocking_write_read(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { | ||
| 182 | let write_data = [0x50, 0x51]; | ||
| 183 | let mut read_buf = [0u8; 6]; | ||
| 184 | |||
| 185 | match i2c.blocking_write_read(addr, &write_data, &mut read_buf) { | ||
| 186 | Ok(_) => { | ||
| 187 | info!("✓ blocking_write_read succeeded"); | ||
| 188 | info!(" Written: {:02x}", write_data); | ||
| 189 | info!(" Read: {:02x}", read_buf); | ||
| 190 | } | ||
| 191 | Err(e) => { | ||
| 192 | error!("✗ blocking_write_read failed: {:?}", e); | ||
| 193 | defmt::panic!("Test failed: blocking_write_read"); | ||
| 194 | } | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | fn test_blocking_write_vectored(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { | ||
| 199 | let buf1 = [0x60, 0x61, 0x62]; | ||
| 200 | let buf2 = [0x70, 0x71]; | ||
| 201 | let buf3 = [0x80, 0x81, 0x82, 0x83]; | ||
| 202 | let bufs = [&buf1[..], &buf2[..], &buf3[..]]; | ||
| 203 | |||
| 204 | match i2c.blocking_write_vectored(addr, &bufs) { | ||
| 205 | Ok(_) => info!("✓ blocking_write_vectored succeeded (9 bytes total)"), | ||
| 206 | Err(e) => { | ||
| 207 | error!("✗ blocking_write_vectored failed: {:?}", e); | ||
| 208 | defmt::panic!("Test failed: blocking_write_vectored"); | ||
| 209 | } | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | fn test_blocking_large_buffer(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { | ||
| 214 | // Test with 300 bytes to verify RELOAD mechanism works (needs chunking at 255 bytes) | ||
| 215 | let mut write_buf = [0u8; 300]; | ||
| 216 | for (i, byte) in write_buf.iter_mut().enumerate() { | ||
| 217 | *byte = (i & 0xFF) as u8; | ||
| 218 | } | ||
| 219 | |||
| 220 | match i2c.blocking_write(addr, &write_buf) { | ||
| 221 | Ok(_) => info!("✓ Large buffer write succeeded (300 bytes, tests RELOAD)"), | ||
| 222 | Err(e) => { | ||
| 223 | error!("✗ Large buffer write failed: {:?}", e); | ||
| 224 | defmt::panic!("Test failed: large buffer write"); | ||
| 225 | } | ||
| 226 | } | ||
| 227 | |||
| 228 | // Test large read | ||
| 229 | let mut read_buf = [0u8; 300]; | ||
| 230 | match i2c.blocking_read(addr, &mut read_buf) { | ||
| 231 | Ok(_) => info!("✓ Large buffer read succeeded (300 bytes, tests RELOAD)"), | ||
| 232 | Err(e) => { | ||
| 233 | error!("✗ Large buffer read failed: {:?}", e); | ||
| 234 | defmt::panic!("Test failed: large buffer read"); | ||
| 235 | } | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | // ==================== BLOCKING TRANSACTION TEST FUNCTIONS ==================== | ||
| 240 | |||
| 241 | fn test_consecutive_writes_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { | ||
| 242 | // Expected on bus: START, ADDR+W, data1, data2, data3, STOP | ||
| 243 | // NO intermediate RESTART/STOP between writes - they should be merged | ||
| 244 | let data1 = [0x10, 0x11, 0x12]; | ||
| 245 | let data2 = [0x20, 0x21]; | ||
| 246 | let data3 = [0x30, 0x31, 0x32, 0x33]; | ||
| 247 | |||
| 248 | let mut ops = [ | ||
| 249 | Operation::Write(&data1), | ||
| 250 | Operation::Write(&data2), | ||
| 251 | Operation::Write(&data3), | ||
| 252 | ]; | ||
| 253 | |||
| 254 | match i2c.blocking_transaction(addr, &mut ops) { | ||
| 255 | Ok(_) => info!("✓ Consecutive writes succeeded (merged 9 bytes)"), | ||
| 256 | Err(e) => { | ||
| 257 | error!("✗ Consecutive writes failed: {:?}", e); | ||
| 258 | defmt::panic!("Test failed: consecutive writes"); | ||
| 259 | } | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | fn test_consecutive_reads_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { | ||
| 264 | // Expected on bus: START, ADDR+R, data1, data2, data3, NACK, STOP | ||
| 265 | // NO intermediate RESTART/STOP between reads - they should be merged | ||
| 266 | let mut buf1 = [0u8; 4]; | ||
| 267 | let mut buf2 = [0u8; 3]; | ||
| 268 | let mut buf3 = [0u8; 2]; | ||
| 269 | |||
| 270 | let mut ops = [ | ||
| 271 | Operation::Read(&mut buf1), | ||
| 272 | Operation::Read(&mut buf2), | ||
| 273 | Operation::Read(&mut buf3), | ||
| 274 | ]; | ||
| 275 | |||
| 276 | match i2c.blocking_transaction(addr, &mut ops) { | ||
| 277 | Ok(_) => { | ||
| 278 | info!("✓ Consecutive reads succeeded (merged 9 bytes)"); | ||
| 279 | info!(" buf1: {:02x}", buf1); | ||
| 280 | info!(" buf2: {:02x}", buf2); | ||
| 281 | info!(" buf3: {:02x}", buf3); | ||
| 282 | } | ||
| 283 | Err(e) => { | ||
| 284 | error!("✗ Consecutive reads failed: {:?}", e); | ||
| 285 | defmt::panic!("Test failed: consecutive reads"); | ||
| 286 | } | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | fn test_write_then_read_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { | ||
| 291 | // Expected: START, ADDR+W, data, RESTART, ADDR+R, data, NACK, STOP | ||
| 292 | let write_data = [0xAA, 0xBB]; | ||
| 293 | let mut read_buf = [0u8; 4]; | ||
| 294 | |||
| 295 | let mut ops = [Operation::Write(&write_data), Operation::Read(&mut read_buf)]; | ||
| 296 | |||
| 297 | match i2c.blocking_transaction(addr, &mut ops) { | ||
| 298 | Ok(_) => { | ||
| 299 | info!("✓ Write-then-read succeeded with RESTART"); | ||
| 300 | info!(" Written: {:02x}", write_data); | ||
| 301 | info!(" Read: {:02x}", read_buf); | ||
| 302 | } | ||
| 303 | Err(e) => { | ||
| 304 | error!("✗ Write-then-read failed: {:?}", e); | ||
| 305 | defmt::panic!("Test failed: write-then-read"); | ||
| 306 | } | ||
| 307 | } | ||
| 308 | } | ||
| 309 | |||
| 310 | fn test_read_then_write_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { | ||
| 311 | // Expected: START, ADDR+R, data, NACK, RESTART, ADDR+W, data, STOP | ||
| 312 | let mut read_buf = [0u8; 3]; | ||
| 313 | let write_data = [0xCC, 0xDD, 0xEE]; | ||
| 314 | |||
| 315 | let mut ops = [Operation::Read(&mut read_buf), Operation::Write(&write_data)]; | ||
| 316 | |||
| 317 | match i2c.blocking_transaction(addr, &mut ops) { | ||
| 318 | Ok(_) => { | ||
| 319 | info!("✓ Read-then-write succeeded with RESTART"); | ||
| 320 | info!(" Read: {:02x}", read_buf); | ||
| 321 | info!(" Written: {:02x}", write_data); | ||
| 322 | } | ||
| 323 | Err(e) => { | ||
| 324 | error!("✗ Read-then-write failed: {:?}", e); | ||
| 325 | defmt::panic!("Test failed: read-then-write"); | ||
| 326 | } | ||
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | fn test_mixed_sequence_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { | ||
| 331 | // Complex: W, W, R, R, W, R | ||
| 332 | // Groups: [W,W] RESTART [R,R] RESTART [W] RESTART [R] | ||
| 333 | let w1 = [0x01, 0x02]; | ||
| 334 | let w2 = [0x03, 0x04]; | ||
| 335 | let mut r1 = [0u8; 2]; | ||
| 336 | let mut r2 = [0u8; 2]; | ||
| 337 | let w3 = [0x05]; | ||
| 338 | let mut r3 = [0u8; 1]; | ||
| 339 | |||
| 340 | let mut ops = [ | ||
| 341 | Operation::Write(&w1), | ||
| 342 | Operation::Write(&w2), | ||
| 343 | Operation::Read(&mut r1), | ||
| 344 | Operation::Read(&mut r2), | ||
| 345 | Operation::Write(&w3), | ||
| 346 | Operation::Read(&mut r3), | ||
| 347 | ]; | ||
| 348 | |||
| 349 | match i2c.blocking_transaction(addr, &mut ops) { | ||
| 350 | Ok(_) => { | ||
| 351 | info!("✓ Mixed sequence succeeded"); | ||
| 352 | info!(" Groups: [W4] RESTART [R4] RESTART [W1] RESTART [R1]"); | ||
| 353 | } | ||
| 354 | Err(e) => { | ||
| 355 | error!("✗ Mixed sequence failed: {:?}", e); | ||
| 356 | defmt::panic!("Test failed: mixed sequence"); | ||
| 357 | } | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 361 | fn test_single_operations_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { | ||
| 362 | // Test single write | ||
| 363 | let write_data = [0xFF]; | ||
| 364 | let mut ops = [Operation::Write(&write_data)]; | ||
| 365 | |||
| 366 | match i2c.blocking_transaction(addr, &mut ops) { | ||
| 367 | Ok(_) => info!("✓ Single write succeeded"), | ||
| 368 | Err(e) => { | ||
| 369 | error!("✗ Single write failed: {:?}", e); | ||
| 370 | defmt::panic!("Test failed: single write"); | ||
| 371 | } | ||
| 372 | } | ||
| 373 | |||
| 374 | // Test single read | ||
| 375 | let mut read_buf = [0u8; 1]; | ||
| 376 | let mut ops = [Operation::Read(&mut read_buf)]; | ||
| 377 | |||
| 378 | match i2c.blocking_transaction(addr, &mut ops) { | ||
| 379 | Ok(_) => info!("✓ Single read succeeded, data: 0x{:02x}", read_buf[0]), | ||
| 380 | Err(e) => { | ||
| 381 | error!("✗ Single read failed: {:?}", e); | ||
| 382 | defmt::panic!("Test failed: single read"); | ||
| 383 | } | ||
| 384 | } | ||
| 385 | } | ||
| 386 | |||
| 387 | // ==================== ASYNC DIRECT API TEST FUNCTIONS ==================== | ||
| 388 | |||
| 389 | async fn test_async_write(i2c: &mut I2c<'_, Async, Master>, addr: u8) { | ||
| 390 | let write_data = [0x42, 0x43, 0x44, 0x45]; | ||
| 391 | |||
| 392 | match i2c.write(addr, &write_data).await { | ||
| 393 | Ok(_) => info!("✓ async write succeeded: {:02x}", write_data), | ||
| 394 | Err(e) => { | ||
| 395 | error!("✗ async write failed: {:?}", e); | ||
| 396 | defmt::panic!("Test failed: async write"); | ||
| 397 | } | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | async fn test_async_read(i2c: &mut I2c<'_, Async, Master>, addr: u8) { | ||
| 402 | let mut read_buf = [0u8; 8]; | ||
| 403 | |||
| 404 | match i2c.read(addr, &mut read_buf).await { | ||
| 405 | Ok(_) => info!("✓ async read succeeded: {:02x}", read_buf), | ||
| 406 | Err(e) => { | ||
| 407 | error!("✗ async read failed: {:?}", e); | ||
| 408 | defmt::panic!("Test failed: async read"); | ||
| 409 | } | ||
| 410 | } | ||
| 411 | } | ||
| 412 | |||
| 413 | async fn test_async_write_read(i2c: &mut I2c<'_, Async, Master>, addr: u8) { | ||
| 414 | let write_data = [0x50, 0x51]; | ||
| 415 | let mut read_buf = [0u8; 6]; | ||
| 416 | |||
| 417 | match i2c.write_read(addr, &write_data, &mut read_buf).await { | ||
| 418 | Ok(_) => { | ||
| 419 | info!("✓ async write_read succeeded"); | ||
| 420 | info!(" Written: {:02x}", write_data); | ||
| 421 | info!(" Read: {:02x}", read_buf); | ||
| 422 | } | ||
| 423 | Err(e) => { | ||
| 424 | error!("✗ async write_read failed: {:?}", e); | ||
| 425 | defmt::panic!("Test failed: async write_read"); | ||
| 426 | } | ||
| 427 | } | ||
| 428 | } | ||
| 429 | |||
| 430 | async fn test_async_write_vectored(i2c: &mut I2c<'_, Async, Master>, addr: u8) { | ||
| 431 | let buf1 = [0x60, 0x61, 0x62]; | ||
| 432 | let buf2 = [0x70, 0x71]; | ||
| 433 | let buf3 = [0x80, 0x81, 0x82, 0x83]; | ||
| 434 | let bufs = [&buf1[..], &buf2[..], &buf3[..]]; | ||
| 435 | |||
| 436 | match i2c.write_vectored(addr.into(), &bufs).await { | ||
| 437 | Ok(_) => info!("✓ async write_vectored succeeded (9 bytes total)"), | ||
| 438 | Err(e) => { | ||
| 439 | error!("✗ async write_vectored failed: {:?}", e); | ||
| 440 | defmt::panic!("Test failed: async write_vectored"); | ||
| 441 | } | ||
| 442 | } | ||
| 443 | } | ||
| 444 | |||
| 445 | async fn test_async_large_buffer(i2c: &mut I2c<'_, Async, Master>, addr: u8) { | ||
| 446 | // Test with 300 bytes to verify RELOAD mechanism works with DMA (needs chunking at 255 bytes) | ||
| 447 | let mut write_buf = [0u8; 300]; | ||
| 448 | for (i, byte) in write_buf.iter_mut().enumerate() { | ||
| 449 | *byte = (i & 0xFF) as u8; | ||
| 450 | } | ||
| 451 | |||
| 452 | match i2c.write(addr, &write_buf).await { | ||
| 453 | Ok(_) => info!("✓ Large buffer async write succeeded (300 bytes, tests RELOAD with DMA)"), | ||
| 454 | Err(e) => { | ||
| 455 | error!("✗ Large buffer async write failed: {:?}", e); | ||
| 456 | defmt::panic!("Test failed: large buffer async write"); | ||
| 457 | } | ||
| 458 | } | ||
| 459 | |||
| 460 | // Test large read | ||
| 461 | let mut read_buf = [0u8; 300]; | ||
| 462 | match i2c.read(addr, &mut read_buf).await { | ||
| 463 | Ok(_) => info!("✓ Large buffer async read succeeded (300 bytes, tests RELOAD with DMA)"), | ||
| 464 | Err(e) => { | ||
| 465 | error!("✗ Large buffer async read failed: {:?}", e); | ||
| 466 | defmt::panic!("Test failed: large buffer async read"); | ||
| 467 | } | ||
| 468 | } | ||
| 469 | } | ||
| 470 | |||
| 471 | // ==================== ASYNC TRANSACTION TEST FUNCTIONS ==================== | ||
| 472 | |||
| 473 | async fn test_consecutive_writes_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { | ||
| 474 | let data1 = [0x10, 0x11, 0x12]; | ||
| 475 | let data2 = [0x20, 0x21]; | ||
| 476 | let data3 = [0x30, 0x31, 0x32, 0x33]; | ||
| 477 | |||
| 478 | let mut ops = [ | ||
| 479 | Operation::Write(&data1), | ||
| 480 | Operation::Write(&data2), | ||
| 481 | Operation::Write(&data3), | ||
| 482 | ]; | ||
| 483 | |||
| 484 | match i2c.transaction(addr, &mut ops).await { | ||
| 485 | Ok(_) => info!("✓ Consecutive writes succeeded (merged 9 bytes)"), | ||
| 486 | Err(e) => { | ||
| 487 | error!("✗ Consecutive writes failed: {:?}", e); | ||
| 488 | defmt::panic!("Test failed: consecutive writes"); | ||
| 489 | } | ||
| 490 | } | ||
| 491 | } | ||
| 492 | |||
| 493 | async fn test_consecutive_reads_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { | ||
| 494 | let mut buf1 = [0u8; 4]; | ||
| 495 | let mut buf2 = [0u8; 3]; | ||
| 496 | let mut buf3 = [0u8; 2]; | ||
| 497 | |||
| 498 | let mut ops = [ | ||
| 499 | Operation::Read(&mut buf1), | ||
| 500 | Operation::Read(&mut buf2), | ||
| 501 | Operation::Read(&mut buf3), | ||
| 502 | ]; | ||
| 503 | |||
| 504 | match i2c.transaction(addr, &mut ops).await { | ||
| 505 | Ok(_) => { | ||
| 506 | info!("✓ Consecutive reads succeeded (merged 9 bytes)"); | ||
| 507 | info!(" buf1: {:02x}", buf1); | ||
| 508 | info!(" buf2: {:02x}", buf2); | ||
| 509 | info!(" buf3: {:02x}", buf3); | ||
| 510 | } | ||
| 511 | Err(e) => { | ||
| 512 | error!("✗ Consecutive reads failed: {:?}", e); | ||
| 513 | defmt::panic!("Test failed: consecutive reads"); | ||
| 514 | } | ||
| 515 | } | ||
| 516 | } | ||
| 517 | |||
| 518 | async fn test_write_then_read_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { | ||
| 519 | let write_data = [0xAA, 0xBB]; | ||
| 520 | let mut read_buf = [0u8; 4]; | ||
| 521 | |||
| 522 | let mut ops = [Operation::Write(&write_data), Operation::Read(&mut read_buf)]; | ||
| 523 | |||
| 524 | match i2c.transaction(addr, &mut ops).await { | ||
| 525 | Ok(_) => { | ||
| 526 | info!("✓ Write-then-read succeeded with RESTART"); | ||
| 527 | info!(" Written: {:02x}", write_data); | ||
| 528 | info!(" Read: {:02x}", read_buf); | ||
| 529 | } | ||
| 530 | Err(e) => { | ||
| 531 | error!("✗ Write-then-read failed: {:?}", e); | ||
| 532 | defmt::panic!("Test failed: write-then-read"); | ||
| 533 | } | ||
| 534 | } | ||
| 535 | } | ||
| 536 | |||
| 537 | async fn test_read_then_write_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { | ||
| 538 | let mut read_buf = [0u8; 3]; | ||
| 539 | let write_data = [0xCC, 0xDD, 0xEE]; | ||
| 540 | |||
| 541 | let mut ops = [Operation::Read(&mut read_buf), Operation::Write(&write_data)]; | ||
| 542 | |||
| 543 | match i2c.transaction(addr, &mut ops).await { | ||
| 544 | Ok(_) => { | ||
| 545 | info!("✓ Read-then-write succeeded with RESTART"); | ||
| 546 | info!(" Read: {:02x}", read_buf); | ||
| 547 | info!(" Written: {:02x}", write_data); | ||
| 548 | } | ||
| 549 | Err(e) => { | ||
| 550 | error!("✗ Read-then-write failed: {:?}", e); | ||
| 551 | defmt::panic!("Test failed: read-then-write"); | ||
| 552 | } | ||
| 553 | } | ||
| 554 | } | ||
| 555 | |||
| 556 | async fn test_mixed_sequence_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { | ||
| 557 | let w1 = [0x01, 0x02]; | ||
| 558 | let w2 = [0x03, 0x04]; | ||
| 559 | let mut r1 = [0u8; 2]; | ||
| 560 | let mut r2 = [0u8; 2]; | ||
| 561 | let w3 = [0x05]; | ||
| 562 | let mut r3 = [0u8; 1]; | ||
| 563 | |||
| 564 | let mut ops = [ | ||
| 565 | Operation::Write(&w1), | ||
| 566 | Operation::Write(&w2), | ||
| 567 | Operation::Read(&mut r1), | ||
| 568 | Operation::Read(&mut r2), | ||
| 569 | Operation::Write(&w3), | ||
| 570 | Operation::Read(&mut r3), | ||
| 571 | ]; | ||
| 572 | |||
| 573 | match i2c.transaction(addr, &mut ops).await { | ||
| 574 | Ok(_) => { | ||
| 575 | info!("✓ Mixed sequence succeeded"); | ||
| 576 | info!(" Groups: [W4] RESTART [R4] RESTART [W1] RESTART [R1]"); | ||
| 577 | } | ||
| 578 | Err(e) => { | ||
| 579 | error!("✗ Mixed sequence failed: {:?}", e); | ||
| 580 | defmt::panic!("Test failed: mixed sequence"); | ||
| 581 | } | ||
| 582 | } | ||
| 583 | } | ||
| 584 | |||
| 585 | async fn test_single_operations_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { | ||
| 586 | // Test single write | ||
| 587 | let write_data = [0xFF]; | ||
| 588 | let mut ops = [Operation::Write(&write_data)]; | ||
| 589 | |||
| 590 | match i2c.transaction(addr, &mut ops).await { | ||
| 591 | Ok(_) => info!("✓ Single write succeeded"), | ||
| 592 | Err(e) => { | ||
| 593 | error!("✗ Single write failed: {:?}", e); | ||
| 594 | defmt::panic!("Test failed: single write"); | ||
| 595 | } | ||
| 596 | } | ||
| 597 | |||
| 598 | // Test single read | ||
| 599 | let mut read_buf = [0u8; 1]; | ||
| 600 | let mut ops = [Operation::Read(&mut read_buf)]; | ||
| 601 | |||
| 602 | match i2c.transaction(addr, &mut ops).await { | ||
| 603 | Ok(_) => info!("✓ Single read succeeded, data: 0x{:02x}", read_buf[0]), | ||
| 604 | Err(e) => { | ||
| 605 | error!("✗ Single read failed: {:?}", e); | ||
| 606 | defmt::panic!("Test failed: single read"); | ||
| 607 | } | ||
| 608 | } | ||
| 609 | } | ||
diff --git a/examples/stm32f1/src/bin/adc.rs b/examples/stm32f1/src/bin/adc.rs index 541ff159e..2451aee3d 100644 --- a/examples/stm32f1/src/bin/adc.rs +++ b/examples/stm32f1/src/bin/adc.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::adc::Adc; | 6 | use embassy_stm32::adc::{Adc, SampleTime}; |
| 7 | use embassy_stm32::peripherals::ADC1; | 7 | use embassy_stm32::peripherals::ADC1; |
| 8 | use embassy_stm32::{adc, bind_interrupts}; | 8 | use embassy_stm32::{adc, bind_interrupts}; |
| 9 | use embassy_time::Timer; | 9 | use embassy_time::Timer; |
| @@ -22,7 +22,7 @@ async fn main(_spawner: Spawner) { | |||
| 22 | let mut pin = p.PB1; | 22 | let mut pin = p.PB1; |
| 23 | 23 | ||
| 24 | let mut vrefint = adc.enable_vref(); | 24 | let mut vrefint = adc.enable_vref(); |
| 25 | let vrefint_sample = adc.read(&mut vrefint).await; | 25 | let vrefint_sample = adc.read(&mut vrefint, SampleTime::CYCLES13_5).await; |
| 26 | let convert_to_millivolts = |sample| { | 26 | let convert_to_millivolts = |sample| { |
| 27 | // From http://www.st.com/resource/en/datasheet/CD00161566.pdf | 27 | // From http://www.st.com/resource/en/datasheet/CD00161566.pdf |
| 28 | // 5.3.4 Embedded reference voltage | 28 | // 5.3.4 Embedded reference voltage |
| @@ -32,7 +32,7 @@ async fn main(_spawner: Spawner) { | |||
| 32 | }; | 32 | }; |
| 33 | 33 | ||
| 34 | loop { | 34 | loop { |
| 35 | let v = adc.read(&mut pin).await; | 35 | let v = adc.read(&mut pin, SampleTime::CYCLES13_5).await; |
| 36 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); | 36 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); |
| 37 | Timer::after_millis(100).await; | 37 | Timer::after_millis(100).await; |
| 38 | } | 38 | } |
diff --git a/examples/stm32f334/src/bin/adc.rs b/examples/stm32f334/src/bin/adc.rs index a993b00ca..486f160ec 100644 --- a/examples/stm32f334/src/bin/adc.rs +++ b/examples/stm32f334/src/bin/adc.rs | |||
| @@ -40,24 +40,22 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 40 | 40 | ||
| 41 | let mut adc = Adc::new(p.ADC1, Irqs); | 41 | let mut adc = Adc::new(p.ADC1, Irqs); |
| 42 | 42 | ||
| 43 | adc.set_sample_time(SampleTime::CYCLES601_5); | ||
| 44 | |||
| 45 | info!("enable vrefint..."); | 43 | info!("enable vrefint..."); |
| 46 | 44 | ||
| 47 | let mut vrefint = adc.enable_vref(); | 45 | let mut vrefint = adc.enable_vref(); |
| 48 | let mut temperature = adc.enable_temperature(); | 46 | let mut temperature = adc.enable_temperature(); |
| 49 | 47 | ||
| 50 | loop { | 48 | loop { |
| 51 | let vref = adc.read(&mut vrefint).await; | 49 | let vref = adc.read(&mut vrefint, SampleTime::CYCLES601_5).await; |
| 52 | info!("read vref: {} (should be {})", vref, vrefint.value()); | 50 | info!("read vref: {} (should be {})", vref, vrefint.calibrated_value()); |
| 53 | 51 | ||
| 54 | let temp = adc.read(&mut temperature).await; | 52 | let temp = adc.read(&mut temperature, SampleTime::CYCLES601_5).await; |
| 55 | info!("read temperature: {}", temp); | 53 | info!("read temperature: {}", temp); |
| 56 | 54 | ||
| 57 | let pin = adc.read(&mut p.PA0).await; | 55 | let pin = adc.read(&mut p.PA0, SampleTime::CYCLES601_5).await; |
| 58 | info!("read pin: {}", pin); | 56 | info!("read pin: {}", pin); |
| 59 | 57 | ||
| 60 | let pin_mv = (pin as u32 * vrefint.value() as u32 / vref as u32) * 3300 / 4095; | 58 | let pin_mv = (pin as u32 * vrefint.calibrated_value() as u32 / vref as u32) * 3300 / 4095; |
| 61 | info!("computed pin mv: {}", pin_mv); | 59 | info!("computed pin mv: {}", pin_mv); |
| 62 | 60 | ||
| 63 | Timer::after_millis(500).await; | 61 | Timer::after_millis(500).await; |
diff --git a/examples/stm32f334/src/bin/opamp.rs b/examples/stm32f334/src/bin/opamp.rs index 3e621f2a1..9555fd35d 100644 --- a/examples/stm32f334/src/bin/opamp.rs +++ b/examples/stm32f334/src/bin/opamp.rs | |||
| @@ -42,8 +42,6 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 42 | let mut adc = Adc::new(p.ADC2, Irqs); | 42 | let mut adc = Adc::new(p.ADC2, Irqs); |
| 43 | let mut opamp = OpAmp::new(p.OPAMP2); | 43 | let mut opamp = OpAmp::new(p.OPAMP2); |
| 44 | 44 | ||
| 45 | adc.set_sample_time(SampleTime::CYCLES601_5); | ||
| 46 | |||
| 47 | info!("enable vrefint..."); | 45 | info!("enable vrefint..."); |
| 48 | 46 | ||
| 49 | let mut vrefint = adc.enable_vref(); | 47 | let mut vrefint = adc.enable_vref(); |
| @@ -51,16 +49,16 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 51 | let mut buffer = opamp.buffer_ext(p.PA7.reborrow(), p.PA6.reborrow()); | 49 | let mut buffer = opamp.buffer_ext(p.PA7.reborrow(), p.PA6.reborrow()); |
| 52 | 50 | ||
| 53 | loop { | 51 | loop { |
| 54 | let vref = adc.read(&mut vrefint).await; | 52 | let vref = adc.read(&mut vrefint, SampleTime::CYCLES601_5).await; |
| 55 | info!("read vref: {} (should be {})", vref, vrefint.value()); | 53 | info!("read vref: {} (should be {})", vref, vrefint.calibrated_value()); |
| 56 | 54 | ||
| 57 | let temp = adc.read(&mut temperature).await; | 55 | let temp = adc.read(&mut temperature, SampleTime::CYCLES601_5).await; |
| 58 | info!("read temperature: {}", temp); | 56 | info!("read temperature: {}", temp); |
| 59 | 57 | ||
| 60 | let buffer = adc.read(&mut buffer).await; | 58 | let buffer = adc.read(&mut buffer, SampleTime::CYCLES601_5).await; |
| 61 | info!("read buffer: {}", buffer); | 59 | info!("read buffer: {}", buffer); |
| 62 | 60 | ||
| 63 | let pin_mv = (buffer as u32 * vrefint.value() as u32 / vref as u32) * 3300 / 4095; | 61 | let pin_mv = (buffer as u32 * vrefint.calibrated_value() as u32 / vref as u32) * 3300 / 4095; |
| 64 | info!("computed pin mv: {}", pin_mv); | 62 | info!("computed pin mv: {}", pin_mv); |
| 65 | 63 | ||
| 66 | Timer::after_millis(500).await; | 64 | Timer::after_millis(500).await; |
diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 423d29225..694e85657 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | use cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs; | 4 | use cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs; |
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::adc::{Adc, Temperature, VrefInt}; | 7 | use embassy_stm32::adc::{Adc, SampleTime, Temperature, VrefInt}; |
| 8 | use embassy_time::{Delay, Timer}; | 8 | use embassy_time::{Delay, Timer}; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 10 | ||
| @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { | |||
| 14 | info!("Hello World!"); | 14 | info!("Hello World!"); |
| 15 | 15 | ||
| 16 | let mut delay = Delay; | 16 | let mut delay = Delay; |
| 17 | let mut adc = Adc::new(p.ADC1); | 17 | let mut adc = Adc::new_with_config(p.ADC1, Default::default()); |
| 18 | let mut pin = p.PC1; | 18 | let mut pin = p.PC1; |
| 19 | 19 | ||
| 20 | let mut vrefint = adc.enable_vrefint(); | 20 | let mut vrefint = adc.enable_vrefint(); |
| @@ -23,7 +23,7 @@ async fn main(_spawner: Spawner) { | |||
| 23 | // Startup delay can be combined to the maximum of either | 23 | // Startup delay can be combined to the maximum of either |
| 24 | delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us())); | 24 | delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us())); |
| 25 | 25 | ||
| 26 | let vrefint_sample = adc.blocking_read(&mut vrefint); | 26 | let vrefint_sample = adc.blocking_read(&mut vrefint, SampleTime::CYCLES112); |
| 27 | 27 | ||
| 28 | let convert_to_millivolts = |sample| { | 28 | let convert_to_millivolts = |sample| { |
| 29 | // From http://www.st.com/resource/en/datasheet/DM00071990.pdf | 29 | // From http://www.st.com/resource/en/datasheet/DM00071990.pdf |
| @@ -50,16 +50,16 @@ async fn main(_spawner: Spawner) { | |||
| 50 | 50 | ||
| 51 | loop { | 51 | loop { |
| 52 | // Read pin | 52 | // Read pin |
| 53 | let v = adc.blocking_read(&mut pin); | 53 | let v = adc.blocking_read(&mut pin, SampleTime::CYCLES112); |
| 54 | info!("PC1: {} ({} mV)", v, convert_to_millivolts(v)); | 54 | info!("PC1: {} ({} mV)", v, convert_to_millivolts(v)); |
| 55 | 55 | ||
| 56 | // Read internal temperature | 56 | // Read internal temperature |
| 57 | let v = adc.blocking_read(&mut temp); | 57 | let v = adc.blocking_read(&mut temp, SampleTime::CYCLES112); |
| 58 | let celcius = convert_to_celcius(v); | 58 | let celcius = convert_to_celcius(v); |
| 59 | info!("Internal temp: {} ({} C)", v, celcius); | 59 | info!("Internal temp: {} ({} C)", v, celcius); |
| 60 | 60 | ||
| 61 | // Read internal voltage reference | 61 | // Read internal voltage reference |
| 62 | let v = adc.blocking_read(&mut vrefint); | 62 | let v = adc.blocking_read(&mut vrefint, SampleTime::CYCLES112); |
| 63 | info!("VrefInt: {}", v); | 63 | info!("VrefInt: {}", v); |
| 64 | 64 | ||
| 65 | Timer::after_millis(100).await; | 65 | Timer::after_millis(100).await; |
diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs index f8da91336..d61b1b2eb 100644 --- a/examples/stm32f4/src/bin/adc_dma.rs +++ b/examples/stm32f4/src/bin/adc_dma.rs | |||
| @@ -4,7 +4,7 @@ use cortex_m::singleton; | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::Peripherals; | 6 | use embassy_stm32::Peripherals; |
| 7 | use embassy_stm32::adc::{Adc, AdcChannel, RingBufferedAdc, SampleTime}; | 7 | use embassy_stm32::adc::{Adc, AdcChannel, RegularConversionMode, RingBufferedAdc, SampleTime}; |
| 8 | use embassy_time::Instant; | 8 | use embassy_time::Instant; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 10 | ||
| @@ -20,26 +20,28 @@ async fn adc_task(p: Peripherals) { | |||
| 20 | let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); | 20 | let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); |
| 21 | let adc_data2: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT2 : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); | 21 | let adc_data2: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT2 : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); |
| 22 | 22 | ||
| 23 | let adc = Adc::new(p.ADC1); | 23 | let adc = Adc::new_with_config(p.ADC1, Default::default()); |
| 24 | let adc2 = Adc::new(p.ADC2); | 24 | let adc2 = Adc::new_with_config(p.ADC2, Default::default()); |
| 25 | 25 | ||
| 26 | let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered( | 26 | let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered( |
| 27 | p.DMA2_CH0, | 27 | p.DMA2_CH0, |
| 28 | adc_data, | 28 | adc_data, |
| 29 | [ | 29 | [ |
| 30 | (&mut p.PA0.degrade_adc(), SampleTime::CYCLES112), | 30 | (p.PA0.degrade_adc(), SampleTime::CYCLES112), |
| 31 | (&mut p.PA2.degrade_adc(), SampleTime::CYCLES112), | 31 | (p.PA2.degrade_adc(), SampleTime::CYCLES112), |
| 32 | ] | 32 | ] |
| 33 | .into_iter(), | 33 | .into_iter(), |
| 34 | RegularConversionMode::Continuous, | ||
| 34 | ); | 35 | ); |
| 35 | let mut adc2: RingBufferedAdc<embassy_stm32::peripherals::ADC2> = adc2.into_ring_buffered( | 36 | let mut adc2: RingBufferedAdc<embassy_stm32::peripherals::ADC2> = adc2.into_ring_buffered( |
| 36 | p.DMA2_CH2, | 37 | p.DMA2_CH2, |
| 37 | adc_data2, | 38 | adc_data2, |
| 38 | [ | 39 | [ |
| 39 | (&mut p.PA1.degrade_adc(), SampleTime::CYCLES112), | 40 | (p.PA1.degrade_adc(), SampleTime::CYCLES112), |
| 40 | (&mut p.PA3.degrade_adc(), SampleTime::CYCLES112), | 41 | (p.PA3.degrade_adc(), SampleTime::CYCLES112), |
| 41 | ] | 42 | ] |
| 42 | .into_iter(), | 43 | .into_iter(), |
| 44 | RegularConversionMode::Continuous, | ||
| 43 | ); | 45 | ); |
| 44 | 46 | ||
| 45 | // Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around | 47 | // Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around |
diff --git a/examples/stm32f4/src/bin/usb_hid_keyboard.rs b/examples/stm32f4/src/bin/usb_hid_keyboard.rs index a3afb887c..9971e43f5 100644 --- a/examples/stm32f4/src/bin/usb_hid_keyboard.rs +++ b/examples/stm32f4/src/bin/usb_hid_keyboard.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::sync::atomic::{AtomicBool, Ordering}; | 4 | use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; |
| 5 | 5 | ||
| 6 | use defmt::*; | 6 | use defmt::*; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| @@ -11,7 +11,9 @@ use embassy_stm32::gpio::Pull; | |||
| 11 | use embassy_stm32::time::Hertz; | 11 | use embassy_stm32::time::Hertz; |
| 12 | use embassy_stm32::usb::Driver; | 12 | use embassy_stm32::usb::Driver; |
| 13 | use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; | 13 | use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; |
| 14 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; | 14 | use embassy_usb::class::hid::{ |
| 15 | HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State, | ||
| 16 | }; | ||
| 15 | use embassy_usb::control::OutResponse; | 17 | use embassy_usb::control::OutResponse; |
| 16 | use embassy_usb::{Builder, Handler}; | 18 | use embassy_usb::{Builder, Handler}; |
| 17 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; | 19 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; |
| @@ -21,6 +23,8 @@ bind_interrupts!(struct Irqs { | |||
| 21 | OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>; | 23 | OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>; |
| 22 | }); | 24 | }); |
| 23 | 25 | ||
| 26 | static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); | ||
| 27 | |||
| 24 | // If you are trying this and your USB device doesn't connect, the most | 28 | // If you are trying this and your USB device doesn't connect, the most |
| 25 | // common issues are the RCC config and vbus_detection | 29 | // common issues are the RCC config and vbus_detection |
| 26 | // | 30 | // |
| @@ -70,6 +74,10 @@ async fn main(_spawner: Spawner) { | |||
| 70 | config.serial_number = Some("12345678"); | 74 | config.serial_number = Some("12345678"); |
| 71 | config.max_power = 100; | 75 | config.max_power = 100; |
| 72 | config.max_packet_size_0 = 64; | 76 | config.max_packet_size_0 = 64; |
| 77 | config.composite_with_iads = false; | ||
| 78 | config.device_class = 0; | ||
| 79 | config.device_sub_class = 0; | ||
| 80 | config.device_protocol = 0; | ||
| 73 | 81 | ||
| 74 | // Create embassy-usb DeviceBuilder using the driver and config. | 82 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 75 | // It needs some buffers for building the descriptors. | 83 | // It needs some buffers for building the descriptors. |
| @@ -101,6 +109,8 @@ async fn main(_spawner: Spawner) { | |||
| 101 | request_handler: None, | 109 | request_handler: None, |
| 102 | poll_ms: 60, | 110 | poll_ms: 60, |
| 103 | max_packet_size: 8, | 111 | max_packet_size: 8, |
| 112 | hid_subclass: HidSubclass::Boot, | ||
| 113 | hid_boot_protocol: HidBootProtocol::Keyboard, | ||
| 104 | }; | 114 | }; |
| 105 | 115 | ||
| 106 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); | 116 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); |
| @@ -121,32 +131,46 @@ async fn main(_spawner: Spawner) { | |||
| 121 | button.wait_for_rising_edge().await; | 131 | button.wait_for_rising_edge().await; |
| 122 | // signal_pin.wait_for_high().await; | 132 | // signal_pin.wait_for_high().await; |
| 123 | info!("Button pressed!"); | 133 | info!("Button pressed!"); |
| 124 | // Create a report with the A key pressed. (no shift modifier) | 134 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 125 | let report = KeyboardReport { | 135 | match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await { |
| 126 | keycodes: [4, 0, 0, 0, 0, 0], | 136 | Ok(()) => {} |
| 127 | leds: 0, | 137 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 128 | modifier: 0, | 138 | }; |
| 129 | reserved: 0, | 139 | } else { |
| 130 | }; | 140 | // Create a report with the A key pressed. (no shift modifier) |
| 131 | // Send the report. | 141 | let report = KeyboardReport { |
| 132 | match writer.write_serialize(&report).await { | 142 | keycodes: [4, 0, 0, 0, 0, 0], |
| 133 | Ok(()) => {} | 143 | leds: 0, |
| 134 | Err(e) => warn!("Failed to send report: {:?}", e), | 144 | modifier: 0, |
| 135 | }; | 145 | reserved: 0, |
| 146 | }; | ||
| 147 | // Send the report. | ||
| 148 | match writer.write_serialize(&report).await { | ||
| 149 | Ok(()) => {} | ||
| 150 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 151 | }; | ||
| 152 | } | ||
| 136 | 153 | ||
| 137 | button.wait_for_falling_edge().await; | 154 | button.wait_for_falling_edge().await; |
| 138 | // signal_pin.wait_for_low().await; | 155 | // signal_pin.wait_for_low().await; |
| 139 | info!("Button released!"); | 156 | info!("Button released!"); |
| 140 | let report = KeyboardReport { | 157 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 141 | keycodes: [0, 0, 0, 0, 0, 0], | 158 | match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await { |
| 142 | leds: 0, | 159 | Ok(()) => {} |
| 143 | modifier: 0, | 160 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 144 | reserved: 0, | 161 | }; |
| 145 | }; | 162 | } else { |
| 146 | match writer.write_serialize(&report).await { | 163 | let report = KeyboardReport { |
| 147 | Ok(()) => {} | 164 | keycodes: [0, 0, 0, 0, 0, 0], |
| 148 | Err(e) => warn!("Failed to send report: {:?}", e), | 165 | leds: 0, |
| 149 | }; | 166 | modifier: 0, |
| 167 | reserved: 0, | ||
| 168 | }; | ||
| 169 | match writer.write_serialize(&report).await { | ||
| 170 | Ok(()) => {} | ||
| 171 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 172 | }; | ||
| 173 | } | ||
| 150 | } | 174 | } |
| 151 | }; | 175 | }; |
| 152 | 176 | ||
| @@ -172,6 +196,18 @@ impl RequestHandler for MyRequestHandler { | |||
| 172 | OutResponse::Accepted | 196 | OutResponse::Accepted |
| 173 | } | 197 | } |
| 174 | 198 | ||
| 199 | fn get_protocol(&self) -> HidProtocolMode { | ||
| 200 | let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); | ||
| 201 | info!("The current HID protocol mode is: {}", protocol); | ||
| 202 | protocol | ||
| 203 | } | ||
| 204 | |||
| 205 | fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { | ||
| 206 | info!("Switching to HID protocol mode: {}", protocol); | ||
| 207 | HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); | ||
| 208 | OutResponse::Accepted | ||
| 209 | } | ||
| 210 | |||
| 175 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { | 211 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { |
| 176 | info!("Set idle rate for {:?} to {:?}", id, dur); | 212 | info!("Set idle rate for {:?} to {:?}", id, dur); |
| 177 | } | 213 | } |
diff --git a/examples/stm32f4/src/bin/usb_hid_mouse.rs b/examples/stm32f4/src/bin/usb_hid_mouse.rs index 162a035f2..e83d01f88 100644 --- a/examples/stm32f4/src/bin/usb_hid_mouse.rs +++ b/examples/stm32f4/src/bin/usb_hid_mouse.rs | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::sync::atomic::{AtomicU8, Ordering}; | ||
| 5 | |||
| 4 | use defmt::*; | 6 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 6 | use embassy_futures::join::join; | 8 | use embassy_futures::join::join; |
| @@ -9,7 +11,9 @@ use embassy_stm32::usb::Driver; | |||
| 9 | use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; | 11 | use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; |
| 10 | use embassy_time::Timer; | 12 | use embassy_time::Timer; |
| 11 | use embassy_usb::Builder; | 13 | use embassy_usb::Builder; |
| 12 | use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; | 14 | use embassy_usb::class::hid::{ |
| 15 | HidBootProtocol, HidProtocolMode, HidSubclass, HidWriter, ReportId, RequestHandler, State, | ||
| 16 | }; | ||
| 13 | use embassy_usb::control::OutResponse; | 17 | use embassy_usb::control::OutResponse; |
| 14 | use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; | 18 | use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; |
| 15 | use {defmt_rtt as _, panic_probe as _}; | 19 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -18,6 +22,8 @@ bind_interrupts!(struct Irqs { | |||
| 18 | OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>; | 22 | OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>; |
| 19 | }); | 23 | }); |
| 20 | 24 | ||
| 25 | static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); | ||
| 26 | |||
| 21 | // If you are trying this and your USB device doesn't connect, the most | 27 | // If you are trying this and your USB device doesn't connect, the most |
| 22 | // common issues are the RCC config and vbus_detection | 28 | // common issues are the RCC config and vbus_detection |
| 23 | // | 29 | // |
| @@ -65,6 +71,10 @@ async fn main(_spawner: Spawner) { | |||
| 65 | config.manufacturer = Some("Embassy"); | 71 | config.manufacturer = Some("Embassy"); |
| 66 | config.product = Some("HID mouse example"); | 72 | config.product = Some("HID mouse example"); |
| 67 | config.serial_number = Some("12345678"); | 73 | config.serial_number = Some("12345678"); |
| 74 | config.composite_with_iads = false; | ||
| 75 | config.device_class = 0; | ||
| 76 | config.device_sub_class = 0; | ||
| 77 | config.device_protocol = 0; | ||
| 68 | 78 | ||
| 69 | // Create embassy-usb DeviceBuilder using the driver and config. | 79 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 70 | // It needs some buffers for building the descriptors. | 80 | // It needs some buffers for building the descriptors. |
| @@ -91,6 +101,8 @@ async fn main(_spawner: Spawner) { | |||
| 91 | request_handler: Some(&mut request_handler), | 101 | request_handler: Some(&mut request_handler), |
| 92 | poll_ms: 60, | 102 | poll_ms: 60, |
| 93 | max_packet_size: 8, | 103 | max_packet_size: 8, |
| 104 | hid_subclass: HidSubclass::Boot, | ||
| 105 | hid_boot_protocol: HidBootProtocol::Mouse, | ||
| 94 | }; | 106 | }; |
| 95 | 107 | ||
| 96 | let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); | 108 | let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); |
| @@ -108,16 +120,26 @@ async fn main(_spawner: Spawner) { | |||
| 108 | Timer::after_millis(500).await; | 120 | Timer::after_millis(500).await; |
| 109 | 121 | ||
| 110 | y = -y; | 122 | y = -y; |
| 111 | let report = MouseReport { | 123 | |
| 112 | buttons: 0, | 124 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 113 | x: 0, | 125 | let buttons = 0u8; |
| 114 | y, | 126 | let x = 0i8; |
| 115 | wheel: 0, | 127 | match writer.write(&[buttons, x as u8, y as u8]).await { |
| 116 | pan: 0, | 128 | Ok(()) => {} |
| 117 | }; | 129 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 118 | match writer.write_serialize(&report).await { | 130 | } |
| 119 | Ok(()) => {} | 131 | } else { |
| 120 | Err(e) => warn!("Failed to send report: {:?}", e), | 132 | let report = MouseReport { |
| 133 | buttons: 0, | ||
| 134 | x: 0, | ||
| 135 | y, | ||
| 136 | wheel: 0, | ||
| 137 | pan: 0, | ||
| 138 | }; | ||
| 139 | match writer.write_serialize(&report).await { | ||
| 140 | Ok(()) => {} | ||
| 141 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 142 | } | ||
| 121 | } | 143 | } |
| 122 | } | 144 | } |
| 123 | }; | 145 | }; |
| @@ -140,6 +162,18 @@ impl RequestHandler for MyRequestHandler { | |||
| 140 | OutResponse::Accepted | 162 | OutResponse::Accepted |
| 141 | } | 163 | } |
| 142 | 164 | ||
| 165 | fn get_protocol(&self) -> HidProtocolMode { | ||
| 166 | let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); | ||
| 167 | info!("The current HID protocol mode is: {}", protocol); | ||
| 168 | protocol | ||
| 169 | } | ||
| 170 | |||
| 171 | fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { | ||
| 172 | info!("Switching to HID protocol mode: {}", protocol); | ||
| 173 | HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); | ||
| 174 | OutResponse::Accepted | ||
| 175 | } | ||
| 176 | |||
| 143 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { | 177 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { |
| 144 | info!("Set idle rate for {:?} to {:?}", id, dur); | 178 | info!("Set idle rate for {:?} to {:?}", id, dur); |
| 145 | } | 179 | } |
diff --git a/examples/stm32f469/src/bin/dsi_bsp.rs b/examples/stm32f469/src/bin/dsi_bsp.rs index d659291ff..7ba4da72b 100644 --- a/examples/stm32f469/src/bin/dsi_bsp.rs +++ b/examples/stm32f469/src/bin/dsi_bsp.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::dsihost::{DsiHost, PacketType, blocking_delay_ms}; | 6 | use embassy_stm32::dsihost::{DsiHost, PacketType}; |
| 7 | use embassy_stm32::gpio::{Level, Output, Speed}; | 7 | use embassy_stm32::gpio::{Level, Output, Speed}; |
| 8 | use embassy_stm32::ltdc::Ltdc; | 8 | use embassy_stm32::ltdc::Ltdc; |
| 9 | use embassy_stm32::pac::dsihost::regs::{Ier0, Ier1}; | 9 | use embassy_stm32::pac::dsihost::regs::{Ier0, Ier1}; |
| @@ -13,7 +13,7 @@ use embassy_stm32::rcc::{ | |||
| 13 | AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllMul, PllPDiv, PllPreDiv, PllQDiv, PllRDiv, PllSource, Sysclk, | 13 | AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllMul, PllPDiv, PllPreDiv, PllQDiv, PllRDiv, PllSource, Sysclk, |
| 14 | }; | 14 | }; |
| 15 | use embassy_stm32::time::mhz; | 15 | use embassy_stm32::time::mhz; |
| 16 | use embassy_time::Timer; | 16 | use embassy_time::{Duration, Timer, block_for}; |
| 17 | use {defmt_rtt as _, panic_probe as _}; | 17 | use {defmt_rtt as _, panic_probe as _}; |
| 18 | 18 | ||
| 19 | enum _Orientation { | 19 | enum _Orientation { |
| @@ -444,7 +444,7 @@ async fn main(_spawner: Spawner) { | |||
| 444 | dsi.enable_wrapper_dsi(); | 444 | dsi.enable_wrapper_dsi(); |
| 445 | 445 | ||
| 446 | // First, delay 120 ms (reason unknown, STM32 Cube Example does it) | 446 | // First, delay 120 ms (reason unknown, STM32 Cube Example does it) |
| 447 | blocking_delay_ms(120); | 447 | block_for(Duration::from_millis(120)); |
| 448 | 448 | ||
| 449 | // 1 to 26 | 449 | // 1 to 26 |
| 450 | dsi.write_cmd(0, NT35510_WRITES_0[0], &NT35510_WRITES_0[1..]).unwrap(); | 450 | dsi.write_cmd(0, NT35510_WRITES_0[0], &NT35510_WRITES_0[1..]).unwrap(); |
| @@ -480,7 +480,7 @@ async fn main(_spawner: Spawner) { | |||
| 480 | dsi.write_cmd(0, NT35510_WRITES_37[0], &NT35510_WRITES_37[1..]).unwrap(); | 480 | dsi.write_cmd(0, NT35510_WRITES_37[0], &NT35510_WRITES_37[1..]).unwrap(); |
| 481 | 481 | ||
| 482 | // Add a delay, otherwise MADCTL not taken | 482 | // Add a delay, otherwise MADCTL not taken |
| 483 | blocking_delay_ms(200); | 483 | block_for(Duration::from_millis(200)); |
| 484 | 484 | ||
| 485 | // Configure orientation as landscape | 485 | // Configure orientation as landscape |
| 486 | dsi.write_cmd(0, NT35510_MADCTL_LANDSCAPE[0], &NT35510_MADCTL_LANDSCAPE[1..]) | 486 | dsi.write_cmd(0, NT35510_MADCTL_LANDSCAPE[0], &NT35510_MADCTL_LANDSCAPE[1..]) |
| @@ -494,7 +494,7 @@ async fn main(_spawner: Spawner) { | |||
| 494 | dsi.write_cmd(0, NT35510_WRITES_27[0], &NT35510_WRITES_27[1..]).unwrap(); | 494 | dsi.write_cmd(0, NT35510_WRITES_27[0], &NT35510_WRITES_27[1..]).unwrap(); |
| 495 | 495 | ||
| 496 | // Wait for sleep out exit | 496 | // Wait for sleep out exit |
| 497 | blocking_delay_ms(120); | 497 | block_for(Duration::from_millis(120)); |
| 498 | 498 | ||
| 499 | // Configure COLOR_CODING | 499 | // Configure COLOR_CODING |
| 500 | dsi.write_cmd(0, NT35510_WRITES_37[0], &NT35510_WRITES_37[1..]).unwrap(); | 500 | dsi.write_cmd(0, NT35510_WRITES_37[0], &NT35510_WRITES_37[1..]).unwrap(); |
| @@ -590,7 +590,7 @@ async fn main(_spawner: Spawner) { | |||
| 590 | //LTDC->SRCR = LTDC_SRCR_IMR; | 590 | //LTDC->SRCR = LTDC_SRCR_IMR; |
| 591 | LTDC.srcr().modify(|w| w.set_imr(Imr::RELOAD)); | 591 | LTDC.srcr().modify(|w| w.set_imr(Imr::RELOAD)); |
| 592 | 592 | ||
| 593 | blocking_delay_ms(5000); | 593 | block_for(Duration::from_millis(5000)); |
| 594 | 594 | ||
| 595 | const READ_SIZE: u16 = 1; | 595 | const READ_SIZE: u16 = 1; |
| 596 | let mut data = [1u8; READ_SIZE as usize]; | 596 | let mut data = [1u8; READ_SIZE as usize]; |
| @@ -606,7 +606,7 @@ async fn main(_spawner: Spawner) { | |||
| 606 | .unwrap(); | 606 | .unwrap(); |
| 607 | info!("Display ID3: {:#04x}", data); | 607 | info!("Display ID3: {:#04x}", data); |
| 608 | 608 | ||
| 609 | blocking_delay_ms(500); | 609 | block_for(Duration::from_millis(500)); |
| 610 | 610 | ||
| 611 | info!("Config done, start blinking LED"); | 611 | info!("Config done, start blinking LED"); |
| 612 | loop { | 612 | loop { |
diff --git a/examples/stm32f7/src/bin/adc.rs b/examples/stm32f7/src/bin/adc.rs index 6689e3b5d..0f226d34e 100644 --- a/examples/stm32f7/src/bin/adc.rs +++ b/examples/stm32f7/src/bin/adc.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::adc::Adc; | 6 | use embassy_stm32::adc::{Adc, SampleTime}; |
| 7 | use embassy_time::Timer; | 7 | use embassy_time::Timer; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| @@ -16,7 +16,7 @@ async fn main(_spawner: Spawner) { | |||
| 16 | let mut pin = p.PA3; | 16 | let mut pin = p.PA3; |
| 17 | 17 | ||
| 18 | let mut vrefint = adc.enable_vrefint(); | 18 | let mut vrefint = adc.enable_vrefint(); |
| 19 | let vrefint_sample = adc.blocking_read(&mut vrefint); | 19 | let vrefint_sample = adc.blocking_read(&mut vrefint, SampleTime::CYCLES112); |
| 20 | let convert_to_millivolts = |sample| { | 20 | let convert_to_millivolts = |sample| { |
| 21 | // From http://www.st.com/resource/en/datasheet/DM00273119.pdf | 21 | // From http://www.st.com/resource/en/datasheet/DM00273119.pdf |
| 22 | // 6.3.27 Reference voltage | 22 | // 6.3.27 Reference voltage |
| @@ -26,7 +26,7 @@ async fn main(_spawner: Spawner) { | |||
| 26 | }; | 26 | }; |
| 27 | 27 | ||
| 28 | loop { | 28 | loop { |
| 29 | let v = adc.blocking_read(&mut pin); | 29 | let v = adc.blocking_read(&mut pin, SampleTime::CYCLES112); |
| 30 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); | 30 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); |
| 31 | Timer::after_millis(100).await; | 31 | Timer::after_millis(100).await; |
| 32 | } | 32 | } |
diff --git a/examples/stm32g0/src/bin/adc.rs b/examples/stm32g0/src/bin/adc.rs index 7d8653ef2..972e43b55 100644 --- a/examples/stm32g0/src/bin/adc.rs +++ b/examples/stm32g0/src/bin/adc.rs | |||
| @@ -13,11 +13,10 @@ async fn main(_spawner: Spawner) { | |||
| 13 | info!("Hello World!"); | 13 | info!("Hello World!"); |
| 14 | 14 | ||
| 15 | let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 }); | 15 | let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 }); |
| 16 | adc.set_sample_time(SampleTime::CYCLES79_5); | ||
| 17 | let mut pin = p.PA1; | 16 | let mut pin = p.PA1; |
| 18 | 17 | ||
| 19 | let mut vrefint = adc.enable_vrefint(); | 18 | let mut vrefint = adc.enable_vrefint(); |
| 20 | let vrefint_sample = adc.blocking_read(&mut vrefint); | 19 | let vrefint_sample = adc.blocking_read(&mut vrefint, SampleTime::CYCLES79_5); |
| 21 | let convert_to_millivolts = |sample| { | 20 | let convert_to_millivolts = |sample| { |
| 22 | // From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf | 21 | // From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf |
| 23 | // 6.3.3 Embedded internal reference voltage | 22 | // 6.3.3 Embedded internal reference voltage |
| @@ -27,7 +26,7 @@ async fn main(_spawner: Spawner) { | |||
| 27 | }; | 26 | }; |
| 28 | 27 | ||
| 29 | loop { | 28 | loop { |
| 30 | let v = adc.blocking_read(&mut pin); | 29 | let v = adc.blocking_read(&mut pin, SampleTime::CYCLES79_5); |
| 31 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); | 30 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); |
| 32 | Timer::after_millis(100).await; | 31 | Timer::after_millis(100).await; |
| 33 | } | 32 | } |
diff --git a/examples/stm32g0/src/bin/adc_oversampling.rs b/examples/stm32g0/src/bin/adc_oversampling.rs index 834d1cd4a..aa8b1771b 100644 --- a/examples/stm32g0/src/bin/adc_oversampling.rs +++ b/examples/stm32g0/src/bin/adc_oversampling.rs | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | use defmt::*; | 8 | use defmt::*; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_stm32::adc::{Adc, Clock, Ovsr, Ovss, Presc, SampleTime}; | 10 | use embassy_stm32::adc::{Adc, AdcConfig, Clock, Ovsr, Ovss, Presc, SampleTime}; |
| 11 | use embassy_time::Timer; | 11 | use embassy_time::Timer; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 13 | ||
| @@ -16,16 +16,17 @@ async fn main(_spawner: Spawner) { | |||
| 16 | let p = embassy_stm32::init(Default::default()); | 16 | let p = embassy_stm32::init(Default::default()); |
| 17 | info!("Adc oversample test"); | 17 | info!("Adc oversample test"); |
| 18 | 18 | ||
| 19 | let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 }); | 19 | let mut config = AdcConfig::default(); |
| 20 | adc.set_sample_time(SampleTime::CYCLES1_5); | 20 | config.clock = Some(Clock::Async { div: Presc::DIV1 }); |
| 21 | let mut pin = p.PA1; | 21 | config.oversampling_ratio = Some(Ovsr::MUL16); |
| 22 | config.oversampling_shift = Some(Ovss::NO_SHIFT); | ||
| 23 | config.oversampling_enable = Some(true); | ||
| 22 | 24 | ||
| 23 | adc.set_oversampling_ratio(Ovsr::MUL16); | 25 | let mut adc = Adc::new_with_config(p.ADC1, config); |
| 24 | adc.set_oversampling_shift(Ovss::NO_SHIFT); | 26 | let mut pin = p.PA1; |
| 25 | adc.oversampling_enable(true); | ||
| 26 | 27 | ||
| 27 | loop { | 28 | loop { |
| 28 | let v = adc.blocking_read(&mut pin); | 29 | let v = adc.blocking_read(&mut pin, SampleTime::CYCLES1_5); |
| 29 | info!("--> {} ", v); //max 65520 = 0xFFF0 | 30 | info!("--> {} ", v); //max 65520 = 0xFFF0 |
| 30 | Timer::after_millis(100).await; | 31 | Timer::after_millis(100).await; |
| 31 | } | 32 | } |
diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs index 920142a18..2149e0748 100644 --- a/examples/stm32g4/src/bin/adc.rs +++ b/examples/stm32g4/src/bin/adc.rs | |||
| @@ -28,12 +28,16 @@ async fn main(_spawner: Spawner) { | |||
| 28 | let mut p = embassy_stm32::init(config); | 28 | let mut p = embassy_stm32::init(config); |
| 29 | info!("Hello World!"); | 29 | info!("Hello World!"); |
| 30 | 30 | ||
| 31 | let mut adc = Adc::new(p.ADC2); | 31 | let mut adc = Adc::new(p.ADC2, Default::default()); |
| 32 | adc.set_sample_time(SampleTime::CYCLES24_5); | 32 | |
| 33 | let mut adc_temp = Adc::new(p.ADC1, Default::default()); | ||
| 34 | let mut temperature = adc_temp.enable_temperature(); | ||
| 33 | 35 | ||
| 34 | loop { | 36 | loop { |
| 35 | let measured = adc.blocking_read(&mut p.PA7); | 37 | let measured = adc.blocking_read(&mut p.PA7, SampleTime::CYCLES24_5); |
| 38 | let temperature = adc_temp.blocking_read(&mut temperature, SampleTime::CYCLES24_5); | ||
| 36 | info!("measured: {}", measured); | 39 | info!("measured: {}", measured); |
| 40 | info!("temperature: {}", temperature); | ||
| 37 | Timer::after_millis(500).await; | 41 | Timer::after_millis(500).await; |
| 38 | } | 42 | } |
| 39 | } | 43 | } |
diff --git a/examples/stm32g4/src/bin/adc_differential.rs b/examples/stm32g4/src/bin/adc_differential.rs index 301f0da84..6dedf88d6 100644 --- a/examples/stm32g4/src/bin/adc_differential.rs +++ b/examples/stm32g4/src/bin/adc_differential.rs | |||
| @@ -30,17 +30,16 @@ async fn main(_spawner: Spawner) { | |||
| 30 | config.rcc.mux.adc12sel = mux::Adcsel::SYS; | 30 | config.rcc.mux.adc12sel = mux::Adcsel::SYS; |
| 31 | config.rcc.sys = Sysclk::PLL1_R; | 31 | config.rcc.sys = Sysclk::PLL1_R; |
| 32 | } | 32 | } |
| 33 | let mut p = embassy_stm32::init(config); | 33 | let p = embassy_stm32::init(config); |
| 34 | 34 | ||
| 35 | let mut adc = Adc::new(p.ADC1); | 35 | let mut adc = Adc::new(p.ADC1, Default::default()); |
| 36 | adc.set_sample_time(SampleTime::CYCLES247_5); | 36 | let mut differential_channel = (p.PA0, p.PA1); |
| 37 | adc.set_differential(&mut p.PA0, true); //p:pa0,n:pa1 | ||
| 38 | 37 | ||
| 39 | // can also use | 38 | // can also use |
| 40 | // adc.set_differential_channel(1, true); | 39 | // adc.set_differential_channel(1, true); |
| 41 | info!("adc initialized"); | 40 | info!("adc initialized"); |
| 42 | loop { | 41 | loop { |
| 43 | let measured = adc.blocking_read(&mut p.PA0); | 42 | let measured = adc.blocking_read(&mut differential_channel, SampleTime::CYCLES247_5); |
| 44 | info!("data: {}", measured); | 43 | info!("data: {}", measured); |
| 45 | Timer::after_millis(500).await; | 44 | Timer::after_millis(500).await; |
| 46 | } | 45 | } |
diff --git a/examples/stm32g4/src/bin/adc_dma.rs b/examples/stm32g4/src/bin/adc_dma.rs index ef8b0c3c2..478b6b2ca 100644 --- a/examples/stm32g4/src/bin/adc_dma.rs +++ b/examples/stm32g4/src/bin/adc_dma.rs | |||
| @@ -33,7 +33,7 @@ async fn main(_spawner: Spawner) { | |||
| 33 | 33 | ||
| 34 | info!("Hello World!"); | 34 | info!("Hello World!"); |
| 35 | 35 | ||
| 36 | let mut adc = Adc::new(p.ADC1); | 36 | let mut adc = Adc::new(p.ADC1, Default::default()); |
| 37 | 37 | ||
| 38 | let mut dma = p.DMA1_CH1; | 38 | let mut dma = p.DMA1_CH1; |
| 39 | let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); | 39 | let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); |
diff --git a/examples/stm32g4/src/bin/adc_injected_and_regular.rs b/examples/stm32g4/src/bin/adc_injected_and_regular.rs index 3ae2ff064..1e97fa925 100644 --- a/examples/stm32g4/src/bin/adc_injected_and_regular.rs +++ b/examples/stm32g4/src/bin/adc_injected_and_regular.rs | |||
| @@ -77,7 +77,7 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 77 | pwm.set_mms2(Mms2::UPDATE); | 77 | pwm.set_mms2(Mms2::UPDATE); |
| 78 | 78 | ||
| 79 | // Configure regular conversions with DMA | 79 | // Configure regular conversions with DMA |
| 80 | let adc1 = Adc::new(p.ADC1); | 80 | let adc1 = Adc::new(p.ADC1, Default::default()); |
| 81 | 81 | ||
| 82 | let vrefint_channel = adc1.enable_vrefint().degrade_adc(); | 82 | let vrefint_channel = adc1.enable_vrefint().degrade_adc(); |
| 83 | let pa0 = p.PC1.degrade_adc(); | 83 | let pa0 = p.PC1.degrade_adc(); |
diff --git a/examples/stm32g4/src/bin/adc_oversampling.rs b/examples/stm32g4/src/bin/adc_oversampling.rs index 1e464183a..87ffea4be 100644 --- a/examples/stm32g4/src/bin/adc_oversampling.rs +++ b/examples/stm32g4/src/bin/adc_oversampling.rs | |||
| @@ -9,7 +9,7 @@ use defmt::*; | |||
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_stm32::Config; | 10 | use embassy_stm32::Config; |
| 11 | use embassy_stm32::adc::vals::{Rovsm, Trovs}; | 11 | use embassy_stm32::adc::vals::{Rovsm, Trovs}; |
| 12 | use embassy_stm32::adc::{Adc, SampleTime}; | 12 | use embassy_stm32::adc::{Adc, AdcConfig, SampleTime}; |
| 13 | use embassy_time::Timer; | 13 | use embassy_time::Timer; |
| 14 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {defmt_rtt as _, panic_probe as _}; |
| 15 | 15 | ||
| @@ -32,8 +32,8 @@ async fn main(_spawner: Spawner) { | |||
| 32 | } | 32 | } |
| 33 | let mut p = embassy_stm32::init(config); | 33 | let mut p = embassy_stm32::init(config); |
| 34 | 34 | ||
| 35 | let mut adc = Adc::new(p.ADC1); | 35 | let mut config = AdcConfig::default(); |
| 36 | adc.set_sample_time(SampleTime::CYCLES6_5); | 36 | |
| 37 | // From https://www.st.com/resource/en/reference_manual/rm0440-stm32g4-series-advanced-armbased-32bit-mcus-stmicroelectronics.pdf | 37 | // From https://www.st.com/resource/en/reference_manual/rm0440-stm32g4-series-advanced-armbased-32bit-mcus-stmicroelectronics.pdf |
| 38 | // page652 Oversampler | 38 | // page652 Oversampler |
| 39 | // Table 172. Maximum output results vs N and M. Grayed values indicates truncation | 39 | // Table 172. Maximum output results vs N and M. Grayed values indicates truncation |
| @@ -45,12 +45,14 @@ async fn main(_spawner: Spawner) { | |||
| 45 | // 0x05 oversampling ratio X64 | 45 | // 0x05 oversampling ratio X64 |
| 46 | // 0x06 oversampling ratio X128 | 46 | // 0x06 oversampling ratio X128 |
| 47 | // 0x07 oversampling ratio X256 | 47 | // 0x07 oversampling ratio X256 |
| 48 | adc.set_oversampling_ratio(0x03); // ratio X3 | 48 | config.oversampling_ratio = Some(0x03); // ratio X3 |
| 49 | adc.set_oversampling_shift(0b0000); // no shift | 49 | config.oversampling_shift = Some(0b0000); // no shift |
| 50 | adc.enable_regular_oversampling_mode(Rovsm::RESUMED, Trovs::AUTOMATIC, true); | 50 | config.oversampling_mode = Some((Rovsm::RESUMED, Trovs::AUTOMATIC, true)); |
| 51 | |||
| 52 | let mut adc = Adc::new(p.ADC1, config); | ||
| 51 | 53 | ||
| 52 | loop { | 54 | loop { |
| 53 | let measured = adc.blocking_read(&mut p.PA0); | 55 | let measured = adc.blocking_read(&mut p.PA0, SampleTime::CYCLES6_5); |
| 54 | info!("data: 0x{:X}", measured); //max 0xFFF0 -> 65520 | 56 | info!("data: 0x{:X}", measured); //max 0xFFF0 -> 65520 |
| 55 | Timer::after_millis(500).await; | 57 | Timer::after_millis(500).await; |
| 56 | } | 58 | } |
diff --git a/examples/stm32h5/src/bin/adc.rs b/examples/stm32h5/src/bin/adc.rs index 0566320d4..c919b1a95 100644 --- a/examples/stm32h5/src/bin/adc.rs +++ b/examples/stm32h5/src/bin/adc.rs | |||
| @@ -45,14 +45,12 @@ async fn main(_spawner: Spawner) { | |||
| 45 | 45 | ||
| 46 | let mut adc = Adc::new(p.ADC1); | 46 | let mut adc = Adc::new(p.ADC1); |
| 47 | 47 | ||
| 48 | adc.set_sample_time(SampleTime::CYCLES24_5); | ||
| 49 | |||
| 50 | let mut vrefint_channel = adc.enable_vrefint(); | 48 | let mut vrefint_channel = adc.enable_vrefint(); |
| 51 | 49 | ||
| 52 | loop { | 50 | loop { |
| 53 | let vrefint = adc.blocking_read(&mut vrefint_channel); | 51 | let vrefint = adc.blocking_read(&mut vrefint_channel, SampleTime::CYCLES24_5); |
| 54 | info!("vrefint: {}", vrefint); | 52 | info!("vrefint: {}", vrefint); |
| 55 | let measured = adc.blocking_read(&mut p.PA0); | 53 | let measured = adc.blocking_read(&mut p.PA0, SampleTime::CYCLES24_5); |
| 56 | info!("measured: {}", measured); | 54 | info!("measured: {}", measured); |
| 57 | Timer::after_millis(500).await; | 55 | Timer::after_millis(500).await; |
| 58 | } | 56 | } |
diff --git a/examples/stm32h5/src/bin/adc_dma.rs b/examples/stm32h5/src/bin/adc_dma.rs index fb9fcbc5c..2138257f7 100644 --- a/examples/stm32h5/src/bin/adc_dma.rs +++ b/examples/stm32h5/src/bin/adc_dma.rs | |||
| @@ -6,7 +6,7 @@ use embassy_executor::Spawner; | |||
| 6 | use embassy_stm32::adc::{self, Adc, AdcChannel, RxDma, SampleTime}; | 6 | use embassy_stm32::adc::{self, Adc, AdcChannel, RxDma, SampleTime}; |
| 7 | use embassy_stm32::peripherals::{ADC1, ADC2, GPDMA1_CH0, GPDMA1_CH1, PA0, PA1, PA2, PA3}; | 7 | use embassy_stm32::peripherals::{ADC1, ADC2, GPDMA1_CH0, GPDMA1_CH1, PA0, PA1, PA2, PA3}; |
| 8 | use embassy_stm32::{Config, Peri}; | 8 | use embassy_stm32::{Config, Peri}; |
| 9 | use embassy_time::Instant; | 9 | use embassy_time::{Duration, Instant, Ticker}; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 11 | ||
| 12 | #[embassy_executor::main] | 12 | #[embassy_executor::main] |
| @@ -76,6 +76,9 @@ async fn adc_task<'a, T: adc::Instance>( | |||
| 76 | let mut pin1 = pin1.degrade_adc(); | 76 | let mut pin1 = pin1.degrade_adc(); |
| 77 | let mut pin2 = pin2.degrade_adc(); | 77 | let mut pin2 = pin2.degrade_adc(); |
| 78 | 78 | ||
| 79 | info!("adc init"); | ||
| 80 | |||
| 81 | let mut ticker = Ticker::every(Duration::from_millis(500)); | ||
| 79 | let mut tic = Instant::now(); | 82 | let mut tic = Instant::now(); |
| 80 | let mut buffer = [0u16; 512]; | 83 | let mut buffer = [0u16; 512]; |
| 81 | loop { | 84 | loop { |
| @@ -84,11 +87,13 @@ async fn adc_task<'a, T: adc::Instance>( | |||
| 84 | adc.read( | 87 | adc.read( |
| 85 | dma.reborrow(), | 88 | dma.reborrow(), |
| 86 | [(&mut pin1, SampleTime::CYCLES2_5), (&mut pin2, SampleTime::CYCLES2_5)].into_iter(), | 89 | [(&mut pin1, SampleTime::CYCLES2_5), (&mut pin2, SampleTime::CYCLES2_5)].into_iter(), |
| 87 | &mut buffer, | 90 | &mut buffer[0..2], |
| 88 | ) | 91 | ) |
| 89 | .await; | 92 | .await; |
| 90 | let toc = Instant::now(); | 93 | let toc = Instant::now(); |
| 91 | info!("\n adc1: {} dt = {}", buffer[0..16], (toc - tic).as_micros()); | 94 | info!("\n adc1: {} dt = {}", buffer[0..16], (toc - tic).as_micros()); |
| 92 | tic = toc; | 95 | tic = toc; |
| 96 | |||
| 97 | ticker.next().await; | ||
| 93 | } | 98 | } |
| 94 | } | 99 | } |
diff --git a/examples/stm32h7/src/bin/adc.rs b/examples/stm32h7/src/bin/adc.rs index a53c9d8d5..fc45541bf 100644 --- a/examples/stm32h7/src/bin/adc.rs +++ b/examples/stm32h7/src/bin/adc.rs | |||
| @@ -46,14 +46,12 @@ async fn main(_spawner: Spawner) { | |||
| 46 | 46 | ||
| 47 | let mut adc = Adc::new(p.ADC3); | 47 | let mut adc = Adc::new(p.ADC3); |
| 48 | 48 | ||
| 49 | adc.set_sample_time(SampleTime::CYCLES32_5); | ||
| 50 | |||
| 51 | let mut vrefint_channel = adc.enable_vrefint(); | 49 | let mut vrefint_channel = adc.enable_vrefint(); |
| 52 | 50 | ||
| 53 | loop { | 51 | loop { |
| 54 | let vrefint = adc.blocking_read(&mut vrefint_channel); | 52 | let vrefint = adc.blocking_read(&mut vrefint_channel, SampleTime::CYCLES32_5); |
| 55 | info!("vrefint: {}", vrefint); | 53 | info!("vrefint: {}", vrefint); |
| 56 | let measured = adc.blocking_read(&mut p.PC0); | 54 | let measured = adc.blocking_read(&mut p.PC0, SampleTime::CYCLES32_5); |
| 57 | info!("measured: {}", measured); | 55 | info!("measured: {}", measured); |
| 58 | Timer::after_millis(500).await; | 56 | Timer::after_millis(500).await; |
| 59 | } | 57 | } |
diff --git a/examples/stm32l0/src/bin/adc.rs b/examples/stm32l0/src/bin/adc.rs index 9dd09bc45..83be74ed9 100644 --- a/examples/stm32l0/src/bin/adc.rs +++ b/examples/stm32l0/src/bin/adc.rs | |||
| @@ -19,11 +19,10 @@ async fn main(_spawner: Spawner) { | |||
| 19 | info!("Hello World!"); | 19 | info!("Hello World!"); |
| 20 | 20 | ||
| 21 | let mut adc = Adc::new(p.ADC1, Irqs); | 21 | let mut adc = Adc::new(p.ADC1, Irqs); |
| 22 | adc.set_sample_time(SampleTime::CYCLES79_5); | ||
| 23 | let mut pin = p.PA1; | 22 | let mut pin = p.PA1; |
| 24 | 23 | ||
| 25 | let mut vrefint = adc.enable_vref(); | 24 | let mut vrefint = adc.enable_vref(); |
| 26 | let vrefint_sample = adc.read(&mut vrefint).await; | 25 | let vrefint_sample = adc.read(&mut vrefint, SampleTime::CYCLES79_5).await; |
| 27 | let convert_to_millivolts = |sample| { | 26 | let convert_to_millivolts = |sample| { |
| 28 | // From https://www.st.com/resource/en/datasheet/stm32l051c6.pdf | 27 | // From https://www.st.com/resource/en/datasheet/stm32l051c6.pdf |
| 29 | // 6.3.3 Embedded internal reference voltage | 28 | // 6.3.3 Embedded internal reference voltage |
| @@ -33,7 +32,7 @@ async fn main(_spawner: Spawner) { | |||
| 33 | }; | 32 | }; |
| 34 | 33 | ||
| 35 | loop { | 34 | loop { |
| 36 | let v = adc.read(&mut pin).await; | 35 | let v = adc.read(&mut pin, SampleTime::CYCLES79_5).await; |
| 37 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); | 36 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); |
| 38 | Timer::after_millis(100).await; | 37 | Timer::after_millis(100).await; |
| 39 | } | 38 | } |
diff --git a/examples/stm32l4/src/bin/adc.rs b/examples/stm32l4/src/bin/adc.rs index 40e907940..42766a5e3 100644 --- a/examples/stm32l4/src/bin/adc.rs +++ b/examples/stm32l4/src/bin/adc.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_stm32::Config; | 5 | use embassy_stm32::Config; |
| 6 | use embassy_stm32::adc::{Adc, Resolution}; | 6 | use embassy_stm32::adc::{Adc, AdcConfig, Resolution, SampleTime}; |
| 7 | use {defmt_rtt as _, panic_probe as _}; | 7 | use {defmt_rtt as _, panic_probe as _}; |
| 8 | 8 | ||
| 9 | #[cortex_m_rt::entry] | 9 | #[cortex_m_rt::entry] |
| @@ -17,13 +17,16 @@ fn main() -> ! { | |||
| 17 | } | 17 | } |
| 18 | let p = embassy_stm32::init(config); | 18 | let p = embassy_stm32::init(config); |
| 19 | 19 | ||
| 20 | let mut adc = Adc::new(p.ADC1); | 20 | let mut config = AdcConfig::default(); |
| 21 | config.resolution = Some(Resolution::BITS8); | ||
| 22 | |||
| 23 | let mut adc = Adc::new_with_config(p.ADC1, config); | ||
| 21 | //adc.enable_vref(); | 24 | //adc.enable_vref(); |
| 22 | adc.set_resolution(Resolution::BITS8); | 25 | |
| 23 | let mut channel = p.PC0; | 26 | let mut channel = p.PC0; |
| 24 | 27 | ||
| 25 | loop { | 28 | loop { |
| 26 | let v = adc.blocking_read(&mut channel); | 29 | let v = adc.blocking_read(&mut channel, SampleTime::from_bits(0)); |
| 27 | info!("--> {}", v); | 30 | info!("--> {}", v); |
| 28 | } | 31 | } |
| 29 | } | 32 | } |
diff --git a/examples/stm32l4/src/bin/adc_dma.rs b/examples/stm32l4/src/bin/adc_dma.rs index 7a9200edd..550da95a4 100644 --- a/examples/stm32l4/src/bin/adc_dma.rs +++ b/examples/stm32l4/src/bin/adc_dma.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::Config; | 6 | use embassy_stm32::Config; |
| 7 | use embassy_stm32::adc::{Adc, AdcChannel, SampleTime}; | 7 | use embassy_stm32::adc::{Adc, AdcChannel, RegularConversionMode, SampleTime}; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| 10 | const DMA_BUF_LEN: usize = 512; | 10 | const DMA_BUF_LEN: usize = 512; |
| @@ -20,19 +20,16 @@ async fn main(_spawner: Spawner) { | |||
| 20 | } | 20 | } |
| 21 | let p = embassy_stm32::init(config); | 21 | let p = embassy_stm32::init(config); |
| 22 | 22 | ||
| 23 | let mut adc = Adc::new(p.ADC1); | 23 | let adc = Adc::new(p.ADC1); |
| 24 | let mut adc_pin0 = p.PA0.degrade_adc(); | 24 | let adc_pin0 = p.PA0.degrade_adc(); |
| 25 | let mut adc_pin1 = p.PA1.degrade_adc(); | 25 | let adc_pin1 = p.PA1.degrade_adc(); |
| 26 | let mut adc_dma_buf = [0u16; DMA_BUF_LEN]; | 26 | let mut adc_dma_buf = [0u16; DMA_BUF_LEN]; |
| 27 | let mut measurements = [0u16; DMA_BUF_LEN / 2]; | 27 | let mut measurements = [0u16; DMA_BUF_LEN / 2]; |
| 28 | let mut ring_buffered_adc = adc.into_ring_buffered( | 28 | let mut ring_buffered_adc = adc.into_ring_buffered( |
| 29 | p.DMA1_CH1, | 29 | p.DMA1_CH1, |
| 30 | &mut adc_dma_buf, | 30 | &mut adc_dma_buf, |
| 31 | [ | 31 | [(adc_pin0, SampleTime::CYCLES640_5), (adc_pin1, SampleTime::CYCLES640_5)].into_iter(), |
| 32 | (&mut adc_pin0, SampleTime::CYCLES640_5), | 32 | RegularConversionMode::Continuous, |
| 33 | (&mut adc_pin1, SampleTime::CYCLES640_5), | ||
| 34 | ] | ||
| 35 | .into_iter(), | ||
| 36 | ); | 33 | ); |
| 37 | 34 | ||
| 38 | info!("starting measurement loop"); | 35 | info!("starting measurement loop"); |
diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index b721f5b2e..d8f2de941 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::sync::atomic::{AtomicU8, Ordering}; | ||
| 5 | |||
| 4 | use defmt::*; | 6 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 6 | use embassy_futures::join::join; | 8 | use embassy_futures::join::join; |
| @@ -8,7 +10,9 @@ use embassy_stm32::usb::Driver; | |||
| 8 | use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; | 10 | use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; |
| 9 | use embassy_time::Timer; | 11 | use embassy_time::Timer; |
| 10 | use embassy_usb::Builder; | 12 | use embassy_usb::Builder; |
| 11 | use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; | 13 | use embassy_usb::class::hid::{ |
| 14 | HidBootProtocol, HidProtocolMode, HidSubclass, HidWriter, ReportId, RequestHandler, State, | ||
| 15 | }; | ||
| 12 | use embassy_usb::control::OutResponse; | 16 | use embassy_usb::control::OutResponse; |
| 13 | use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; | 17 | use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; |
| 14 | use {defmt_rtt as _, panic_probe as _}; | 18 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -17,6 +21,8 @@ bind_interrupts!(struct Irqs { | |||
| 17 | USB_FS => usb::InterruptHandler<peripherals::USB>; | 21 | USB_FS => usb::InterruptHandler<peripherals::USB>; |
| 18 | }); | 22 | }); |
| 19 | 23 | ||
| 24 | static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); | ||
| 25 | |||
| 20 | #[embassy_executor::main] | 26 | #[embassy_executor::main] |
| 21 | async fn main(_spawner: Spawner) { | 27 | async fn main(_spawner: Spawner) { |
| 22 | let mut config = Config::default(); | 28 | let mut config = Config::default(); |
| @@ -48,6 +54,10 @@ async fn main(_spawner: Spawner) { | |||
| 48 | config.serial_number = Some("12345678"); | 54 | config.serial_number = Some("12345678"); |
| 49 | config.max_power = 100; | 55 | config.max_power = 100; |
| 50 | config.max_packet_size_0 = 64; | 56 | config.max_packet_size_0 = 64; |
| 57 | config.composite_with_iads = false; | ||
| 58 | config.device_class = 0; | ||
| 59 | config.device_sub_class = 0; | ||
| 60 | config.device_protocol = 0; | ||
| 51 | 61 | ||
| 52 | // Create embassy-usb DeviceBuilder using the driver and config. | 62 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 53 | // It needs some buffers for building the descriptors. | 63 | // It needs some buffers for building the descriptors. |
| @@ -73,6 +83,8 @@ async fn main(_spawner: Spawner) { | |||
| 73 | request_handler: Some(&mut request_handler), | 83 | request_handler: Some(&mut request_handler), |
| 74 | poll_ms: 60, | 84 | poll_ms: 60, |
| 75 | max_packet_size: 8, | 85 | max_packet_size: 8, |
| 86 | hid_subclass: HidSubclass::Boot, | ||
| 87 | hid_boot_protocol: HidBootProtocol::Mouse, | ||
| 76 | }; | 88 | }; |
| 77 | 89 | ||
| 78 | let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); | 90 | let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); |
| @@ -90,16 +102,26 @@ async fn main(_spawner: Spawner) { | |||
| 90 | Timer::after_millis(500).await; | 102 | Timer::after_millis(500).await; |
| 91 | 103 | ||
| 92 | y = -y; | 104 | y = -y; |
| 93 | let report = MouseReport { | 105 | |
| 94 | buttons: 0, | 106 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 95 | x: 0, | 107 | let buttons = 0u8; |
| 96 | y, | 108 | let x = 0i8; |
| 97 | wheel: 0, | 109 | match writer.write(&[buttons, x as u8, y as u8]).await { |
| 98 | pan: 0, | 110 | Ok(()) => {} |
| 99 | }; | 111 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 100 | match writer.write_serialize(&report).await { | 112 | } |
| 101 | Ok(()) => {} | 113 | } else { |
| 102 | Err(e) => warn!("Failed to send report: {:?}", e), | 114 | let report = MouseReport { |
| 115 | buttons: 0, | ||
| 116 | x: 0, | ||
| 117 | y, | ||
| 118 | wheel: 0, | ||
| 119 | pan: 0, | ||
| 120 | }; | ||
| 121 | match writer.write_serialize(&report).await { | ||
| 122 | Ok(()) => {} | ||
| 123 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 124 | } | ||
| 103 | } | 125 | } |
| 104 | } | 126 | } |
| 105 | }; | 127 | }; |
| @@ -122,6 +144,18 @@ impl RequestHandler for MyRequestHandler { | |||
| 122 | OutResponse::Accepted | 144 | OutResponse::Accepted |
| 123 | } | 145 | } |
| 124 | 146 | ||
| 147 | fn get_protocol(&self) -> HidProtocolMode { | ||
| 148 | let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); | ||
| 149 | info!("The current HID protocol mode is: {}", protocol); | ||
| 150 | protocol | ||
| 151 | } | ||
| 152 | |||
| 153 | fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { | ||
| 154 | info!("Switching to HID protocol mode: {}", protocol); | ||
| 155 | HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); | ||
| 156 | OutResponse::Accepted | ||
| 157 | } | ||
| 158 | |||
| 125 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { | 159 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { |
| 126 | info!("Set idle rate for {:?} to {:?}", id, dur); | 160 | info!("Set idle rate for {:?} to {:?}", id, dur); |
| 127 | } | 161 | } |
diff --git a/examples/stm32n6/.cargo/config.toml b/examples/stm32n6/.cargo/config.toml new file mode 100644 index 000000000..2fdd70649 --- /dev/null +++ b/examples/stm32n6/.cargo/config.toml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | [target.thumbv8m.main-none-eabihf] | ||
| 2 | runner = 'probe-rs run --chip STM32N657' | ||
| 3 | |||
| 4 | [build] | ||
| 5 | target = "thumbv8m.main-none-eabihf" | ||
| 6 | |||
| 7 | [env] | ||
| 8 | DEFMT_LOG = "trace" | ||
diff --git a/examples/stm32n6/Cargo.toml b/examples/stm32n6/Cargo.toml new file mode 100644 index 000000000..5ed28eed1 --- /dev/null +++ b/examples/stm32n6/Cargo.toml | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-stm32n6-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | publish = false | ||
| 7 | |||
| 8 | [dependencies] | ||
| 9 | # Change stm32h563zi to your chip name, if necessary. | ||
| 10 | embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = ["defmt", "stm32n657x0", "time-driver-any", "exti", "unstable-pac"] } | ||
| 11 | embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } | ||
| 12 | embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | ||
| 13 | embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | ||
| 14 | embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] } | ||
| 15 | embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] } | ||
| 16 | embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } | ||
| 17 | |||
| 18 | defmt = "1.0.1" | ||
| 19 | defmt-rtt = "1.0.0" | ||
| 20 | |||
| 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 22 | cortex-m-rt = "0.7.0" | ||
| 23 | embedded-hal = "0.2.6" | ||
| 24 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | ||
| 25 | embedded-hal-async = { version = "1.0" } | ||
| 26 | embedded-io-async = { version = "0.6.1" } | ||
| 27 | embedded-nal-async = "0.8.0" | ||
| 28 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | ||
| 29 | heapless = { version = "0.8", default-features = false } | ||
| 30 | critical-section = "1.1" | ||
| 31 | micromath = "2.0.0" | ||
| 32 | stm32-fmc = "0.3.0" | ||
| 33 | embedded-storage = "0.3.1" | ||
| 34 | static_cell = "2" | ||
| 35 | |||
| 36 | |||
| 37 | # cargo build/run | ||
| 38 | [profile.dev] | ||
| 39 | codegen-units = 1 | ||
| 40 | debug = 2 | ||
| 41 | debug-assertions = true # <- | ||
| 42 | incremental = false | ||
| 43 | opt-level = 3 # <- | ||
| 44 | overflow-checks = true # <- | ||
| 45 | |||
| 46 | # cargo test | ||
| 47 | [profile.test] | ||
| 48 | codegen-units = 1 | ||
| 49 | debug = 2 | ||
| 50 | debug-assertions = true # <- | ||
| 51 | incremental = false | ||
| 52 | opt-level = 3 # <- | ||
| 53 | overflow-checks = true # <- | ||
| 54 | |||
| 55 | # cargo build/run --release | ||
| 56 | [profile.release] | ||
| 57 | codegen-units = 1 | ||
| 58 | debug = 2 | ||
| 59 | debug-assertions = false # <- | ||
| 60 | incremental = false | ||
| 61 | lto = 'fat' | ||
| 62 | opt-level = 3 # <- | ||
| 63 | overflow-checks = false # <- | ||
| 64 | |||
| 65 | # cargo test --release | ||
| 66 | [profile.bench] | ||
| 67 | codegen-units = 1 | ||
| 68 | debug = 2 | ||
| 69 | debug-assertions = false # <- | ||
| 70 | incremental = false | ||
| 71 | lto = 'fat' | ||
| 72 | opt-level = 3 # <- | ||
| 73 | overflow-checks = false # <- | ||
| 74 | |||
| 75 | [package.metadata.embassy] | ||
| 76 | build = [ | ||
| 77 | { target = "thumbv8m.main-none-eabihf", artifact-dir = "out/examples/stm32n6" } | ||
| 78 | ] | ||
diff --git a/examples/stm32n6/build.rs b/examples/stm32n6/build.rs new file mode 100644 index 000000000..8cd32d7ed --- /dev/null +++ b/examples/stm32n6/build.rs | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | fn main() { | ||
| 2 | println!("cargo:rustc-link-arg-bins=--nmagic"); | ||
| 3 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | ||
| 4 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 5 | } | ||
diff --git a/examples/stm32n6/memory.x b/examples/stm32n6/memory.x new file mode 100644 index 000000000..59f127adc --- /dev/null +++ b/examples/stm32n6/memory.x | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | FLASH : ORIGIN = 0x34180400, LENGTH = 255K | ||
| 4 | RAM : ORIGIN = 0x341C0000, LENGTH = 256K | ||
| 5 | } | ||
diff --git a/examples/stm32n6/src/bin/blinky.rs b/examples/stm32n6/src/bin/blinky.rs new file mode 100644 index 000000000..018967f08 --- /dev/null +++ b/examples/stm32n6/src/bin/blinky.rs | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::exti::ExtiInput; | ||
| 7 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | ||
| 8 | use embassy_time::Timer; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::task] | ||
| 12 | async fn button_task(mut p: ExtiInput<'static>) { | ||
| 13 | loop { | ||
| 14 | p.wait_for_any_edge().await; | ||
| 15 | info!("button pressed!"); | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 19 | #[embassy_executor::main] | ||
| 20 | async fn main(spawner: Spawner) { | ||
| 21 | let p = embassy_stm32::init(Default::default()); | ||
| 22 | info!("Hello World!"); | ||
| 23 | |||
| 24 | let mut led = Output::new(p.PG10, Level::High, Speed::Low); | ||
| 25 | let button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); | ||
| 26 | |||
| 27 | spawner.spawn(button_task(button).unwrap()); | ||
| 28 | |||
| 29 | loop { | ||
| 30 | led.set_high(); | ||
| 31 | Timer::after_millis(500).await; | ||
| 32 | |||
| 33 | led.set_low(); | ||
| 34 | Timer::after_millis(500).await; | ||
| 35 | } | ||
| 36 | } | ||
diff --git a/examples/stm32u0/src/bin/adc.rs b/examples/stm32u0/src/bin/adc.rs index 32a54299d..53bd37303 100644 --- a/examples/stm32u0/src/bin/adc.rs +++ b/examples/stm32u0/src/bin/adc.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_stm32::Config; | 5 | use embassy_stm32::Config; |
| 6 | use embassy_stm32::adc::{Adc, Resolution}; | 6 | use embassy_stm32::adc::{Adc, AdcConfig, Resolution, SampleTime}; |
| 7 | use embassy_time::Duration; | 7 | use embassy_time::Duration; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| @@ -18,12 +18,13 @@ fn main() -> ! { | |||
| 18 | } | 18 | } |
| 19 | let p = embassy_stm32::init(config); | 19 | let p = embassy_stm32::init(config); |
| 20 | 20 | ||
| 21 | let mut adc = Adc::new(p.ADC1); | 21 | let mut config = AdcConfig::default(); |
| 22 | adc.set_resolution(Resolution::BITS8); | 22 | config.resolution = Some(Resolution::BITS8); |
| 23 | let mut adc = Adc::new_with_config(p.ADC1, config); | ||
| 23 | let mut channel = p.PC0; | 24 | let mut channel = p.PC0; |
| 24 | 25 | ||
| 25 | loop { | 26 | loop { |
| 26 | let v = adc.blocking_read(&mut channel); | 27 | let v = adc.blocking_read(&mut channel, SampleTime::CYCLES12_5); |
| 27 | info!("--> {}", v); | 28 | info!("--> {}", v); |
| 28 | embassy_time::block_for(Duration::from_millis(200)); | 29 | embassy_time::block_for(Duration::from_millis(200)); |
| 29 | } | 30 | } |
diff --git a/examples/stm32u5/src/bin/adc.rs b/examples/stm32u5/src/bin/adc.rs index 91e33053e..ad59c0bea 100644 --- a/examples/stm32u5/src/bin/adc.rs +++ b/examples/stm32u5/src/bin/adc.rs | |||
| @@ -2,8 +2,7 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_stm32::adc; | 5 | use embassy_stm32::adc::{self, Adc, AdcChannel, AdcConfig, SampleTime, adc4}; |
| 6 | use embassy_stm32::adc::{AdcChannel, adc4}; | ||
| 7 | use {defmt_rtt as _, panic_probe as _}; | 6 | use {defmt_rtt as _, panic_probe as _}; |
| 8 | 7 | ||
| 9 | #[embassy_executor::main] | 8 | #[embassy_executor::main] |
| @@ -13,56 +12,55 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 13 | let mut p = embassy_stm32::init(config); | 12 | let mut p = embassy_stm32::init(config); |
| 14 | 13 | ||
| 15 | // **** ADC1 init **** | 14 | // **** ADC1 init **** |
| 16 | let mut adc1 = adc::Adc::new(p.ADC1); | 15 | let mut config = AdcConfig::default(); |
| 16 | config.averaging = Some(adc::Averaging::Samples1024); | ||
| 17 | config.resolution = Some(adc::Resolution::BITS14); | ||
| 18 | let mut adc1 = Adc::new_with_config(p.ADC1, config); | ||
| 17 | let mut adc1_pin1 = p.PA3; // A0 on nucleo u5a5 | 19 | let mut adc1_pin1 = p.PA3; // A0 on nucleo u5a5 |
| 18 | let mut adc1_pin2 = p.PA2; // A1 | 20 | let mut adc1_pin2 = p.PA2; // A1 |
| 19 | adc1.set_resolution(adc::Resolution::BITS14); | ||
| 20 | adc1.set_averaging(adc::Averaging::Samples1024); | ||
| 21 | adc1.set_sample_time(adc::SampleTime::CYCLES160_5); | ||
| 22 | let max1 = adc::resolution_to_max_count(adc::Resolution::BITS14); | 21 | let max1 = adc::resolution_to_max_count(adc::Resolution::BITS14); |
| 23 | 22 | ||
| 24 | // **** ADC2 init **** | 23 | // **** ADC2 init **** |
| 25 | let mut adc2 = adc::Adc::new(p.ADC2); | 24 | let mut config = AdcConfig::default(); |
| 25 | config.averaging = Some(adc::Averaging::Samples1024); | ||
| 26 | config.resolution = Some(adc::Resolution::BITS14); | ||
| 27 | let mut adc2 = Adc::new_with_config(p.ADC2, config); | ||
| 26 | let mut adc2_pin1 = p.PC3; // A2 | 28 | let mut adc2_pin1 = p.PC3; // A2 |
| 27 | let mut adc2_pin2 = p.PB0; // A3 | 29 | let mut adc2_pin2 = p.PB0; // A3 |
| 28 | adc2.set_resolution(adc::Resolution::BITS14); | ||
| 29 | adc2.set_averaging(adc::Averaging::Samples1024); | ||
| 30 | adc2.set_sample_time(adc::SampleTime::CYCLES160_5); | ||
| 31 | let max2 = adc::resolution_to_max_count(adc::Resolution::BITS14); | 30 | let max2 = adc::resolution_to_max_count(adc::Resolution::BITS14); |
| 32 | 31 | ||
| 33 | // **** ADC4 init **** | 32 | // **** ADC4 init **** |
| 34 | let mut adc4 = adc4::Adc4::new(p.ADC4); | 33 | let mut adc4 = Adc::new_adc4(p.ADC4); |
| 35 | let mut adc4_pin1 = p.PC1; // A4 | 34 | let mut adc4_pin1 = p.PC1; // A4 |
| 36 | let mut adc4_pin2 = p.PC0; // A5 | 35 | let mut adc4_pin2 = p.PC0; // A5 |
| 37 | adc4.set_resolution(adc4::Resolution::BITS12); | 36 | adc4.set_resolution_adc4(adc4::Resolution::BITS12); |
| 38 | adc4.set_averaging(adc4::Averaging::Samples256); | 37 | adc4.set_averaging_adc4(adc4::Averaging::Samples256); |
| 39 | adc4.set_sample_time(adc4::SampleTime::CYCLES1_5); | ||
| 40 | let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); | 38 | let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); |
| 41 | 39 | ||
| 42 | // **** ADC1 blocking read **** | 40 | // **** ADC1 blocking read **** |
| 43 | let raw: u16 = adc1.blocking_read(&mut adc1_pin1); | 41 | let raw: u16 = adc1.blocking_read(&mut adc1_pin1, SampleTime::CYCLES160_5); |
| 44 | let volt: f32 = 3.3 * raw as f32 / max1 as f32; | 42 | let volt: f32 = 3.3 * raw as f32 / max1 as f32; |
| 45 | info!("Read adc1 pin 1 {}", volt); | 43 | info!("Read adc1 pin 1 {}", volt); |
| 46 | 44 | ||
| 47 | let raw: u16 = adc1.blocking_read(&mut adc1_pin2); | 45 | let raw: u16 = adc1.blocking_read(&mut adc1_pin2, SampleTime::CYCLES160_5); |
| 48 | let volt: f32 = 3.3 * raw as f32 / max1 as f32; | 46 | let volt: f32 = 3.3 * raw as f32 / max1 as f32; |
| 49 | info!("Read adc1 pin 2 {}", volt); | 47 | info!("Read adc1 pin 2 {}", volt); |
| 50 | 48 | ||
| 51 | // **** ADC2 blocking read **** | 49 | // **** ADC2 blocking read **** |
| 52 | let raw: u16 = adc2.blocking_read(&mut adc2_pin1); | 50 | let raw: u16 = adc2.blocking_read(&mut adc2_pin1, SampleTime::CYCLES160_5); |
| 53 | let volt: f32 = 3.3 * raw as f32 / max2 as f32; | 51 | let volt: f32 = 3.3 * raw as f32 / max2 as f32; |
| 54 | info!("Read adc2 pin 1 {}", volt); | 52 | info!("Read adc2 pin 1 {}", volt); |
| 55 | 53 | ||
| 56 | let raw: u16 = adc2.blocking_read(&mut adc2_pin2); | 54 | let raw: u16 = adc2.blocking_read(&mut adc2_pin2, SampleTime::CYCLES160_5); |
| 57 | let volt: f32 = 3.3 * raw as f32 / max2 as f32; | 55 | let volt: f32 = 3.3 * raw as f32 / max2 as f32; |
| 58 | info!("Read adc2 pin 2 {}", volt); | 56 | info!("Read adc2 pin 2 {}", volt); |
| 59 | 57 | ||
| 60 | // **** ADC4 blocking read **** | 58 | // **** ADC4 blocking read **** |
| 61 | let raw: u16 = adc4.blocking_read(&mut adc4_pin1); | 59 | let raw: u16 = adc4.blocking_read(&mut adc4_pin1, adc4::SampleTime::CYCLES1_5); |
| 62 | let volt: f32 = 3.3 * raw as f32 / max4 as f32; | 60 | let volt: f32 = 3.3 * raw as f32 / max4 as f32; |
| 63 | info!("Read adc4 pin 1 {}", volt); | 61 | info!("Read adc4 pin 1 {}", volt); |
| 64 | 62 | ||
| 65 | let raw: u16 = adc4.blocking_read(&mut adc4_pin2); | 63 | let raw: u16 = adc4.blocking_read(&mut adc4_pin2, adc4::SampleTime::CYCLES1_5); |
| 66 | let volt: f32 = 3.3 * raw as f32 / max4 as f32; | 64 | let volt: f32 = 3.3 * raw as f32 / max4 as f32; |
| 67 | info!("Read adc4 pin 2 {}", volt); | 65 | info!("Read adc4 pin 2 {}", volt); |
| 68 | 66 | ||
| @@ -97,11 +95,14 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 97 | // The channels must be in ascending order and can't repeat for ADC4 | 95 | // The channels must be in ascending order and can't repeat for ADC4 |
| 98 | adc4.read( | 96 | adc4.read( |
| 99 | p.GPDMA1_CH1.reborrow(), | 97 | p.GPDMA1_CH1.reborrow(), |
| 100 | [&mut degraded42, &mut degraded41].into_iter(), | 98 | [ |
| 99 | (&mut degraded42, adc4::SampleTime::CYCLES1_5), | ||
| 100 | (&mut degraded41, adc4::SampleTime::CYCLES1_5), | ||
| 101 | ] | ||
| 102 | .into_iter(), | ||
| 101 | &mut measurements, | 103 | &mut measurements, |
| 102 | ) | 104 | ) |
| 103 | .await | 105 | .await; |
| 104 | .unwrap(); | ||
| 105 | let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32; | 106 | let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32; |
| 106 | let volt1: f32 = 3.3 * measurements[1] as f32 / max4 as f32; | 107 | let volt1: f32 = 3.3 * measurements[1] as f32 / max4 as f32; |
| 107 | info!("Async read 4 pin 1 {}", volt1); | 108 | info!("Async read 4 pin 1 {}", volt1); |
diff --git a/examples/stm32wba/src/bin/adc.rs b/examples/stm32wba/src/bin/adc.rs index 8c80470b8..ade3f5d6a 100644 --- a/examples/stm32wba/src/bin/adc.rs +++ b/examples/stm32wba/src/bin/adc.rs | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_stm32::adc::{AdcChannel, adc4}; | 5 | use embassy_stm32::adc::{Adc, AdcChannel, SampleTime, adc4}; |
| 6 | use {defmt_rtt as _, panic_probe as _}; | 6 | use {defmt_rtt as _, panic_probe as _}; |
| 7 | 7 | ||
| 8 | #[embassy_executor::main] | 8 | #[embassy_executor::main] |
| @@ -12,20 +12,20 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 12 | let mut p = embassy_stm32::init(config); | 12 | let mut p = embassy_stm32::init(config); |
| 13 | 13 | ||
| 14 | // **** ADC4 init **** | 14 | // **** ADC4 init **** |
| 15 | let mut adc4 = adc4::Adc4::new(p.ADC4); | 15 | let mut adc4 = Adc::new_adc4(p.ADC4); |
| 16 | let mut adc4_pin1 = p.PA0; // A4 | 16 | let mut adc4_pin1 = p.PA0; // A4 |
| 17 | let mut adc4_pin2 = p.PA1; // A5 | 17 | let mut adc4_pin2 = p.PA1; // A5 |
| 18 | adc4.set_resolution(adc4::Resolution::BITS12); | 18 | adc4.set_resolution_adc4(adc4::Resolution::BITS12); |
| 19 | adc4.set_averaging(adc4::Averaging::Samples256); | 19 | adc4.set_averaging_adc4(adc4::Averaging::Samples256); |
| 20 | adc4.set_sample_time(adc4::SampleTime::CYCLES1_5); | 20 | |
| 21 | let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); | 21 | let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); |
| 22 | 22 | ||
| 23 | // **** ADC4 blocking read **** | 23 | // **** ADC4 blocking read **** |
| 24 | let raw: u16 = adc4.blocking_read(&mut adc4_pin1); | 24 | let raw: u16 = adc4.blocking_read(&mut adc4_pin1, adc4::SampleTime::CYCLES1_5); |
| 25 | let volt: f32 = 3.0 * raw as f32 / max4 as f32; | 25 | let volt: f32 = 3.0 * raw as f32 / max4 as f32; |
| 26 | info!("Read adc4 pin 1 {}", volt); | 26 | info!("Read adc4 pin 1 {}", volt); |
| 27 | 27 | ||
| 28 | let raw: u16 = adc4.blocking_read(&mut adc4_pin2); | 28 | let raw: u16 = adc4.blocking_read(&mut adc4_pin2, adc4::SampleTime::CYCLES1_5); |
| 29 | let volt: f32 = 3.3 * raw as f32 / max4 as f32; | 29 | let volt: f32 = 3.3 * raw as f32 / max4 as f32; |
| 30 | info!("Read adc4 pin 2 {}", volt); | 30 | info!("Read adc4 pin 2 {}", volt); |
| 31 | 31 | ||
| @@ -37,11 +37,14 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 37 | // The channels must be in ascending order and can't repeat for ADC4 | 37 | // The channels must be in ascending order and can't repeat for ADC4 |
| 38 | adc4.read( | 38 | adc4.read( |
| 39 | p.GPDMA1_CH1.reborrow(), | 39 | p.GPDMA1_CH1.reborrow(), |
| 40 | [&mut degraded42, &mut degraded41].into_iter(), | 40 | [ |
| 41 | (&mut degraded42, SampleTime::CYCLES12_5), | ||
| 42 | (&mut degraded41, SampleTime::CYCLES12_5), | ||
| 43 | ] | ||
| 44 | .into_iter(), | ||
| 41 | &mut measurements, | 45 | &mut measurements, |
| 42 | ) | 46 | ) |
| 43 | .await | 47 | .await; |
| 44 | .unwrap(); | ||
| 45 | let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32; | 48 | let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32; |
| 46 | let volt1: f32 = 3.0 * measurements[1] as f32 / max4 as f32; | 49 | let volt1: f32 = 3.0 * measurements[1] as f32 / max4 as f32; |
| 47 | info!("Async read 4 pin 1 {}", volt1); | 50 | info!("Async read 4 pin 1 {}", volt1); |
diff --git a/examples/stm32wba6/src/bin/adc.rs b/examples/stm32wba6/src/bin/adc.rs index 8c80470b8..9d1f39419 100644 --- a/examples/stm32wba6/src/bin/adc.rs +++ b/examples/stm32wba6/src/bin/adc.rs | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_stm32::adc::{AdcChannel, adc4}; | 5 | use embassy_stm32::adc::{Adc, AdcChannel, SampleTime, adc4}; |
| 6 | use {defmt_rtt as _, panic_probe as _}; | 6 | use {defmt_rtt as _, panic_probe as _}; |
| 7 | 7 | ||
| 8 | #[embassy_executor::main] | 8 | #[embassy_executor::main] |
| @@ -12,20 +12,19 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 12 | let mut p = embassy_stm32::init(config); | 12 | let mut p = embassy_stm32::init(config); |
| 13 | 13 | ||
| 14 | // **** ADC4 init **** | 14 | // **** ADC4 init **** |
| 15 | let mut adc4 = adc4::Adc4::new(p.ADC4); | 15 | let mut adc4 = Adc::new_adc4(p.ADC4); |
| 16 | let mut adc4_pin1 = p.PA0; // A4 | 16 | let mut adc4_pin1 = p.PA0; // A4 |
| 17 | let mut adc4_pin2 = p.PA1; // A5 | 17 | let mut adc4_pin2 = p.PA1; // A5 |
| 18 | adc4.set_resolution(adc4::Resolution::BITS12); | 18 | adc4.set_resolution_adc4(adc4::Resolution::BITS12); |
| 19 | adc4.set_averaging(adc4::Averaging::Samples256); | 19 | adc4.set_averaging_adc4(adc4::Averaging::Samples256); |
| 20 | adc4.set_sample_time(adc4::SampleTime::CYCLES1_5); | ||
| 21 | let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); | 20 | let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); |
| 22 | 21 | ||
| 23 | // **** ADC4 blocking read **** | 22 | // **** ADC4 blocking read **** |
| 24 | let raw: u16 = adc4.blocking_read(&mut adc4_pin1); | 23 | let raw: u16 = adc4.blocking_read(&mut adc4_pin1, adc4::SampleTime::CYCLES1_5); |
| 25 | let volt: f32 = 3.0 * raw as f32 / max4 as f32; | 24 | let volt: f32 = 3.0 * raw as f32 / max4 as f32; |
| 26 | info!("Read adc4 pin 1 {}", volt); | 25 | info!("Read adc4 pin 1 {}", volt); |
| 27 | 26 | ||
| 28 | let raw: u16 = adc4.blocking_read(&mut adc4_pin2); | 27 | let raw: u16 = adc4.blocking_read(&mut adc4_pin2, adc4::SampleTime::CYCLES1_5); |
| 29 | let volt: f32 = 3.3 * raw as f32 / max4 as f32; | 28 | let volt: f32 = 3.3 * raw as f32 / max4 as f32; |
| 30 | info!("Read adc4 pin 2 {}", volt); | 29 | info!("Read adc4 pin 2 {}", volt); |
| 31 | 30 | ||
| @@ -37,11 +36,14 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 37 | // The channels must be in ascending order and can't repeat for ADC4 | 36 | // The channels must be in ascending order and can't repeat for ADC4 |
| 38 | adc4.read( | 37 | adc4.read( |
| 39 | p.GPDMA1_CH1.reborrow(), | 38 | p.GPDMA1_CH1.reborrow(), |
| 40 | [&mut degraded42, &mut degraded41].into_iter(), | 39 | [ |
| 40 | (&mut degraded42, SampleTime::CYCLES12_5), | ||
| 41 | (&mut degraded41, SampleTime::CYCLES12_5), | ||
| 42 | ] | ||
| 43 | .into_iter(), | ||
| 41 | &mut measurements, | 44 | &mut measurements, |
| 42 | ) | 45 | ) |
| 43 | .await | 46 | .await; |
| 44 | .unwrap(); | ||
| 45 | let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32; | 47 | let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32; |
| 46 | let volt1: f32 = 3.0 * measurements[1] as f32 / max4 as f32; | 48 | let volt1: f32 = 3.0 * measurements[1] as f32 / max4 as f32; |
| 47 | info!("Async read 4 pin 1 {}", volt1); | 49 | info!("Async read 4 pin 1 {}", volt1); |
diff --git a/examples/stm32wl/src/bin/adc.rs b/examples/stm32wl/src/bin/adc.rs index 6b21b086b..adabe0df8 100644 --- a/examples/stm32wl/src/bin/adc.rs +++ b/examples/stm32wl/src/bin/adc.rs | |||
| @@ -18,11 +18,11 @@ async fn main(_spawner: Spawner) { | |||
| 18 | info!("Hello World!"); | 18 | info!("Hello World!"); |
| 19 | 19 | ||
| 20 | let mut adc = Adc::new_with_clock(p.ADC1, Clock::Sync { div: CkModePclk::DIV1 }); | 20 | let mut adc = Adc::new_with_clock(p.ADC1, Clock::Sync { div: CkModePclk::DIV1 }); |
| 21 | adc.set_sample_time(SampleTime::CYCLES79_5); | 21 | |
| 22 | let mut pin = p.PB2; | 22 | let mut pin = p.PB2; |
| 23 | 23 | ||
| 24 | let mut vrefint = adc.enable_vrefint(); | 24 | let mut vrefint = adc.enable_vrefint(); |
| 25 | let vrefint_sample = adc.blocking_read(&mut vrefint); | 25 | let vrefint_sample = adc.blocking_read(&mut vrefint, SampleTime::CYCLES79_5); |
| 26 | let convert_to_millivolts = |sample| { | 26 | let convert_to_millivolts = |sample| { |
| 27 | // From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf | 27 | // From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf |
| 28 | // 6.3.3 Embedded internal reference voltage | 28 | // 6.3.3 Embedded internal reference voltage |
| @@ -32,7 +32,7 @@ async fn main(_spawner: Spawner) { | |||
| 32 | }; | 32 | }; |
| 33 | 33 | ||
| 34 | loop { | 34 | loop { |
| 35 | let v = adc.blocking_read(&mut pin); | 35 | let v = adc.blocking_read(&mut pin, SampleTime::CYCLES79_5); |
| 36 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); | 36 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); |
| 37 | Timer::after_millis(100).await; | 37 | Timer::after_millis(100).await; |
| 38 | } | 38 | } |
diff --git a/examples/stm32wle5/src/bin/adc.rs b/examples/stm32wle5/src/bin/adc.rs index 8b830a1e6..4e0574d97 100644 --- a/examples/stm32wle5/src/bin/adc.rs +++ b/examples/stm32wle5/src/bin/adc.rs | |||
| @@ -73,11 +73,10 @@ async fn async_main(_spawner: Spawner) { | |||
| 73 | info!("Hello World!"); | 73 | info!("Hello World!"); |
| 74 | 74 | ||
| 75 | let mut adc = Adc::new(p.ADC1); | 75 | let mut adc = Adc::new(p.ADC1); |
| 76 | adc.set_sample_time(SampleTime::CYCLES79_5); | ||
| 77 | let mut pin = p.PA10; | 76 | let mut pin = p.PA10; |
| 78 | 77 | ||
| 79 | let mut vrefint = adc.enable_vrefint(); | 78 | let mut vrefint = adc.enable_vrefint(); |
| 80 | let vrefint_sample = adc.blocking_read(&mut vrefint); | 79 | let vrefint_sample = adc.blocking_read(&mut vrefint, SampleTime::CYCLES79_5); |
| 81 | let convert_to_millivolts = |sample| { | 80 | let convert_to_millivolts = |sample| { |
| 82 | // From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf | 81 | // From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf |
| 83 | // 6.3.3 Embedded internal reference voltage | 82 | // 6.3.3 Embedded internal reference voltage |
| @@ -87,7 +86,7 @@ async fn async_main(_spawner: Spawner) { | |||
| 87 | }; | 86 | }; |
| 88 | 87 | ||
| 89 | loop { | 88 | loop { |
| 90 | let v = adc.blocking_read(&mut pin); | 89 | let v = adc.blocking_read(&mut pin, SampleTime::CYCLES79_5); |
| 91 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); | 90 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); |
| 92 | Timer::after_secs(1).await; | 91 | Timer::after_secs(1).await; |
| 93 | } | 92 | } |
diff --git a/tests/stm32/src/bin/dac.rs b/tests/stm32/src/bin/dac.rs index d34bbb255..747b11e7f 100644 --- a/tests/stm32/src/bin/dac.rs +++ b/tests/stm32/src/bin/dac.rs | |||
| @@ -10,7 +10,7 @@ use core::f32::consts::PI; | |||
| 10 | use common::*; | 10 | use common::*; |
| 11 | use defmt::assert; | 11 | use defmt::assert; |
| 12 | use embassy_executor::Spawner; | 12 | use embassy_executor::Spawner; |
| 13 | use embassy_stm32::adc::Adc; | 13 | use embassy_stm32::adc::{Adc, SampleTime}; |
| 14 | use embassy_stm32::dac::{DacCh1, Value}; | 14 | use embassy_stm32::dac::{DacCh1, Value}; |
| 15 | use embassy_time::Timer; | 15 | use embassy_time::Timer; |
| 16 | use micromath::F32Ext; | 16 | use micromath::F32Ext; |
| @@ -37,7 +37,7 @@ async fn main(_spawner: Spawner) { | |||
| 37 | dac.set(Value::Bit8(0)); | 37 | dac.set(Value::Bit8(0)); |
| 38 | // Now wait a little to obtain a stable value | 38 | // Now wait a little to obtain a stable value |
| 39 | Timer::after_millis(30).await; | 39 | Timer::after_millis(30).await; |
| 40 | let offset = adc.blocking_read(&mut adc_pin); | 40 | let offset = adc.blocking_read(&mut adc_pin, SampleTime::from_bits(0)); |
| 41 | 41 | ||
| 42 | for v in 0..=255 { | 42 | for v in 0..=255 { |
| 43 | // First set the DAC output value | 43 | // First set the DAC output value |
| @@ -48,7 +48,10 @@ async fn main(_spawner: Spawner) { | |||
| 48 | Timer::after_millis(30).await; | 48 | Timer::after_millis(30).await; |
| 49 | 49 | ||
| 50 | // Need to steal the peripherals here because PA4 is obviously in use already | 50 | // Need to steal the peripherals here because PA4 is obviously in use already |
| 51 | let measured = adc.blocking_read(&mut unsafe { embassy_stm32::Peripherals::steal() }.PA4); | 51 | let measured = adc.blocking_read( |
| 52 | &mut unsafe { embassy_stm32::Peripherals::steal() }.PA4, | ||
| 53 | SampleTime::from_bits(0), | ||
| 54 | ); | ||
| 52 | // Calibrate and normalize the measurement to get close to the dac_output_val | 55 | // Calibrate and normalize the measurement to get close to the dac_output_val |
| 53 | let measured_normalized = ((measured as i32 - offset as i32) / normalization_factor) as i16; | 56 | let measured_normalized = ((measured as i32 - offset as i32) / normalization_factor) as i16; |
| 54 | 57 | ||
diff --git a/tests/stm32/src/bin/dac_l1.rs b/tests/stm32/src/bin/dac_l1.rs index e6400f28e..2fe0cf1f1 100644 --- a/tests/stm32/src/bin/dac_l1.rs +++ b/tests/stm32/src/bin/dac_l1.rs | |||
| @@ -10,7 +10,7 @@ use core::f32::consts::PI; | |||
| 10 | use common::*; | 10 | use common::*; |
| 11 | use defmt::assert; | 11 | use defmt::assert; |
| 12 | use embassy_executor::Spawner; | 12 | use embassy_executor::Spawner; |
| 13 | use embassy_stm32::adc::Adc; | 13 | use embassy_stm32::adc::{Adc, SampleTime}; |
| 14 | use embassy_stm32::dac::{DacCh1, Value}; | 14 | use embassy_stm32::dac::{DacCh1, Value}; |
| 15 | use embassy_stm32::{bind_interrupts, peripherals}; | 15 | use embassy_stm32::{bind_interrupts, peripherals}; |
| 16 | use embassy_time::Timer; | 16 | use embassy_time::Timer; |
| @@ -47,7 +47,7 @@ async fn main(_spawner: Spawner) { | |||
| 47 | dac.set(Value::Bit8(0)); | 47 | dac.set(Value::Bit8(0)); |
| 48 | // Now wait a little to obtain a stable value | 48 | // Now wait a little to obtain a stable value |
| 49 | Timer::after_millis(30).await; | 49 | Timer::after_millis(30).await; |
| 50 | let offset = adc.read(&mut adc_pin).await; | 50 | let offset = adc.read(&mut adc_pin, SampleTime::from_bits(0)).await; |
| 51 | 51 | ||
| 52 | for v in 0..=255 { | 52 | for v in 0..=255 { |
| 53 | // First set the DAC output value | 53 | // First set the DAC output value |
| @@ -58,7 +58,12 @@ async fn main(_spawner: Spawner) { | |||
| 58 | Timer::after_millis(30).await; | 58 | Timer::after_millis(30).await; |
| 59 | 59 | ||
| 60 | // Need to steal the peripherals here because PA4 is obviously in use already | 60 | // Need to steal the peripherals here because PA4 is obviously in use already |
| 61 | let measured = adc.read(&mut unsafe { embassy_stm32::Peripherals::steal() }.PA4).await; | 61 | let measured = adc |
| 62 | .read( | ||
| 63 | &mut unsafe { embassy_stm32::Peripherals::steal() }.PA4, | ||
| 64 | SampleTime::from_bits(0), | ||
| 65 | ) | ||
| 66 | .await; | ||
| 62 | // Calibrate and normalize the measurement to get close to the dac_output_val | 67 | // Calibrate and normalize the measurement to get close to the dac_output_val |
| 63 | let measured_normalized = ((measured as i32 - offset as i32) / normalization_factor) as i16; | 68 | let measured_normalized = ((measured as i32 - offset as i32) / normalization_factor) as i16; |
| 64 | 69 | ||
