diff options
229 files changed, 9774 insertions, 5762 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json index c504f3ccd..73dfa53a4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json | |||
| @@ -5,6 +5,9 @@ | |||
| 5 | "[markdown]": { | 5 | "[markdown]": { |
| 6 | "editor.formatOnSave": false | 6 | "editor.formatOnSave": false |
| 7 | }, | 7 | }, |
| 8 | "rust-analyzer.rustfmt.extraArgs": [ | ||
| 9 | "+nightly" | ||
| 10 | ], | ||
| 8 | "rust-analyzer.check.allTargets": false, | 11 | "rust-analyzer.check.allTargets": false, |
| 9 | "rust-analyzer.check.noDefaultFeatures": true, | 12 | "rust-analyzer.check.noDefaultFeatures": true, |
| 10 | "rust-analyzer.cargo.noDefaultFeatures": true, | 13 | "rust-analyzer.cargo.noDefaultFeatures": true, |
| @@ -61,4 +64,4 @@ | |||
| 61 | // "examples/stm32wl/Cargo.toml", | 64 | // "examples/stm32wl/Cargo.toml", |
| 62 | // "examples/wasm/Cargo.toml", | 65 | // "examples/wasm/Cargo.toml", |
| 63 | ], | 66 | ], |
| 64 | } | 67 | } \ No newline at end of file |
| @@ -35,7 +35,7 @@ rm -rf out/tests/nrf5340-dk | |||
| 35 | # disabled because these boards are not on the shelf | 35 | # disabled because these boards are not on the shelf |
| 36 | rm -rf out/tests/mspm0g3507 | 36 | rm -rf out/tests/mspm0g3507 |
| 37 | 37 | ||
| 38 | rm out/tests/stm32wb55rg/wpan_mac | 38 | # rm out/tests/stm32wb55rg/wpan_mac |
| 39 | rm out/tests/stm32wb55rg/wpan_ble | 39 | rm out/tests/stm32wb55rg/wpan_ble |
| 40 | 40 | ||
| 41 | # unstable, I think it's running out of RAM? | 41 | # unstable, I think it's running out of RAM? |
diff --git a/docs/examples/layer-by-layer/blinky-async/src/main.rs b/docs/examples/layer-by-layer/blinky-async/src/main.rs index 004602816..007f7da46 100644 --- a/docs/examples/layer-by-layer/blinky-async/src/main.rs +++ b/docs/examples/layer-by-layer/blinky-async/src/main.rs | |||
| @@ -2,15 +2,21 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use embassy_executor::Spawner; | 4 | use embassy_executor::Spawner; |
| 5 | use embassy_stm32::exti::ExtiInput; | 5 | use embassy_stm32::exti::{self, ExtiInput}; |
| 6 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | 6 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; |
| 7 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 7 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 8 | 9 | ||
| 10 | bind_interrupts!( | ||
| 11 | pub struct Irqs{ | ||
| 12 | EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10 >; | ||
| 13 | }); | ||
| 14 | |||
| 9 | #[embassy_executor::main] | 15 | #[embassy_executor::main] |
| 10 | async fn main(_spawner: Spawner) { | 16 | async fn main(_spawner: Spawner) { |
| 11 | let p = embassy_stm32::init(Default::default()); | 17 | let p = embassy_stm32::init(Default::default()); |
| 12 | let mut led = Output::new(p.PB14, Level::Low, Speed::VeryHigh); | 18 | let mut led = Output::new(p.PB14, Level::Low, Speed::VeryHigh); |
| 13 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); | 19 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); |
| 14 | 20 | ||
| 15 | loop { | 21 | loop { |
| 16 | button.wait_for_any_edge().await; | 22 | button.wait_for_any_edge().await; |
diff --git a/docs/pages/bootloader.adoc b/docs/pages/bootloader.adoc index b0f0331aa..c010b0622 100644 --- a/docs/pages/bootloader.adoc +++ b/docs/pages/bootloader.adoc | |||
| @@ -43,14 +43,14 @@ Partition Size~dfu~= Partition Size~active~+ Page Size~active~ | |||
| 43 | + | 43 | + |
| 44 | All values are specified in bytes. | 44 | All values are specified in bytes. |
| 45 | 45 | ||
| 46 | * BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a magic field is written to instruct the bootloader that the partitions should be swapped. This partition must be able to store a magic field as well as the partition swap progress. The partition size given by: | 46 | * BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a magic field is written to instruct the bootloader that the partitions should be swapped. This partition must be able to store a magic field as well as the partition swap progress. The partition size is given by: |
| 47 | + | 47 | + |
| 48 | Partition Size~state~ = Write Size~state~ + (2 × Partition Size~active~ / Page Size~active~) | 48 | Partition Size~state~ = (2 × Write Size~state~) + (4 × Write Size~state~ × Partition Size~active~ / Page Size~active~) |
| 49 | + | 49 | + |
| 50 | All values are specified in bytes. | 50 | All values are specified in bytes. |
| 51 | 51 | ||
| 52 | The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash. The page size used by the bootloader is determined by the lowest common multiple of the ACTIVE and DFU page sizes. | 52 | The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash. The page size used by the bootloader is determined by the lowest common multiple of the ACTIVE and DFU page sizes. |
| 53 | The BOOTLOADER_STATE partition must be big enough to store one word per page in the ACTIVE and DFU partitions combined. | 53 | The BOOTLOADER_STATE partition must be big enough to store two words, plus four words per page in the ACTIVE partition. |
| 54 | 54 | ||
| 55 | The bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice. | 55 | The bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice. |
| 56 | 56 | ||
diff --git a/docs/pages/faq.adoc b/docs/pages/faq.adoc index 8098e12ac..e59ef7b46 100644 --- a/docs/pages/faq.adoc +++ b/docs/pages/faq.adoc | |||
| @@ -171,7 +171,11 @@ Note that the git revision should match any other embassy patches or git depende | |||
| 171 | 171 | ||
| 172 | == Can I use manual ISRs alongside Embassy? | 172 | == Can I use manual ISRs alongside Embassy? |
| 173 | 173 | ||
| 174 | Yes! This can be useful if you need to respond to an event as fast as possible, and the latency caused by the usual “ISR, wake, return from ISR, context switch to woken task” flow is too much for your application. Simply define a `#[interrupt] fn INTERRUPT_NAME() {}` handler as you would link:https://docs.rust-embedded.org/book/start/interrupts.html[in any other embedded rust project]. | 174 | Yes! This can be useful if you need to respond to an event as fast as possible, and the latency caused by the usual “ISR, wake, return from ISR, context switch to woken task” flow is too much for your application. |
| 175 | |||
| 176 | You may simply define a `#[interrupt] fn INTERRUPT_NAME() {}` handler as you would link:https://docs.rust-embedded.org/book/start/interrupts.html[in any other embedded rust project]. | ||
| 177 | |||
| 178 | Or you may define a struct implementing the `embassy-[family]::interrupt::typelevel::Handler` trait with an on_interrupt() method, and bind it to the interrupt vector via the `bind_interrupts!` macro, which introduces only a single indirection. This allows the mixing of manual ISRs with Embassy driver-defined ISRs; handlers will be called directly in the order they appear in the macro. | ||
| 175 | 179 | ||
| 176 | == How can I measure resource usage (CPU, RAM, etc.)? | 180 | == How can I measure resource usage (CPU, RAM, etc.)? |
| 177 | 181 | ||
diff --git a/docs/pages/layer_by_layer.adoc b/docs/pages/layer_by_layer.adoc index 0692ee4fa..f554e642a 100644 --- a/docs/pages/layer_by_layer.adoc +++ b/docs/pages/layer_by_layer.adoc | |||
| @@ -76,7 +76,7 @@ The async version looks very similar to the HAL version, apart from a few minor | |||
| 76 | * The peripheral initialization is done by the main macro, and is handed to the main task. | 76 | * The peripheral initialization is done by the main macro, and is handed to the main task. |
| 77 | * Before checking the button state, the application is awaiting a transition in the pin state (low -> high or high -> low). | 77 | * Before checking the button state, the application is awaiting a transition in the pin state (low -> high or high -> low). |
| 78 | 78 | ||
| 79 | When `button.wait_for_any_edge().await` is called, the executor will pause the main task and put the microcontroller in sleep mode, unless there are other tasks that can run. Internally, the Embassy HAL has configured the interrupt handler for the button (in `ExtiInput`), so that whenever an interrupt is raised, the task awaiting the button will be woken up. | 79 | When `button.wait_for_any_edge().await` is called, the executor will pause the main task and put the microcontroller in sleep mode, unless there are other tasks that can run. On this chip, interrupt signals on EXTI lines 10-15 (including the button on EXTI line 13) raise the hardware interrupt EXTI15_10. This interrupt handler has been bound (using `bind_interrupts!`) to call the `InterruptHandler` provided by the exti module, so that whenever an interrupt is raised, the task awaiting the button via `wait_for_any_edge()` will be woken up. |
| 80 | 80 | ||
| 81 | The minimal overhead of the executor and the ability to run multiple tasks "concurrently" combined with the enormous simplification of the application, makes `async` a great fit for embedded. | 81 | The minimal overhead of the executor and the ability to run multiple tasks "concurrently" combined with the enormous simplification of the application, makes `async` a great fit for embedded. |
| 82 | 82 | ||
diff --git a/embassy-boot/CHANGELOG.md b/embassy-boot/CHANGELOG.md index 8d6395357..1d41043cb 100644 --- a/embassy-boot/CHANGELOG.md +++ b/embassy-boot/CHANGELOG.md | |||
| @@ -8,6 +8,9 @@ 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 | - Fixed documentation and assertion of STATE partition size requirements | ||
| 12 | - Added documentation for package features | ||
| 13 | |||
| 11 | ## 0.6.1 - 2025-08-26 | 14 | ## 0.6.1 - 2025-08-26 |
| 12 | 15 | ||
| 13 | - First release with changelog. | 16 | - First release with changelog. |
diff --git a/embassy-boot/Cargo.toml b/embassy-boot/Cargo.toml index 8c5c1f633..754c6e5f1 100644 --- a/embassy-boot/Cargo.toml +++ b/embassy-boot/Cargo.toml | |||
| @@ -26,6 +26,7 @@ features = ["defmt"] | |||
| 26 | [dependencies] | 26 | [dependencies] |
| 27 | defmt = { version = "1.0.1", optional = true } | 27 | defmt = { version = "1.0.1", optional = true } |
| 28 | digest = "0.10" | 28 | digest = "0.10" |
| 29 | document-features = "0.2.7" | ||
| 29 | log = { version = "0.4", optional = true } | 30 | log = { version = "0.4", optional = true } |
| 30 | ed25519-dalek = { version = "2", default-features = false, features = ["digest"], optional = true } | 31 | ed25519-dalek = { version = "2", default-features = false, features = ["digest"], optional = true } |
| 31 | embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" } | 32 | embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" } |
| @@ -45,11 +46,22 @@ critical-section = { version = "1.1.1", features = ["std"] } | |||
| 45 | ed25519-dalek = { version = "2", default-features = false, features = ["std", "rand_core", "digest"] } | 46 | ed25519-dalek = { version = "2", default-features = false, features = ["std", "rand_core", "digest"] } |
| 46 | 47 | ||
| 47 | [features] | 48 | [features] |
| 49 | ## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging | ||
| 48 | defmt = ["dep:defmt"] | 50 | defmt = ["dep:defmt"] |
| 51 | ## Use log for logging | ||
| 49 | log = ["dep:log"] | 52 | log = ["dep:log"] |
| 53 | |||
| 54 | ## Enable for devices that set erased flash bytes to `0x00` instead of the usual `0xFF` | ||
| 55 | flash-erase-zero = [] | ||
| 56 | |||
| 57 | #! ## Firmware Signing | ||
| 58 | #! Enable one of these features to allow verification of DFU signatures with | ||
| 59 | #! `FirmwareUpdater::verify_and_mark_updated`. | ||
| 60 | |||
| 61 | ## Use the `ed25519-dalek` package to verify DFU signatures. | ||
| 50 | ed25519-dalek = ["dep:ed25519-dalek", "_verify"] | 62 | ed25519-dalek = ["dep:ed25519-dalek", "_verify"] |
| 63 | ## Use the `salty` package to verify DFU signatures. | ||
| 51 | ed25519-salty = ["dep:salty", "_verify"] | 64 | ed25519-salty = ["dep:salty", "_verify"] |
| 52 | flash-erase-zero = [] | ||
| 53 | 65 | ||
| 54 | #Internal features | 66 | #Internal features |
| 55 | _verify = [] | 67 | _verify = [] |
diff --git a/embassy-boot/src/boot_loader.rs b/embassy-boot/src/boot_loader.rs index c38940d6e..a3a307051 100644 --- a/embassy-boot/src/boot_loader.rs +++ b/embassy-boot/src/boot_loader.rs | |||
| @@ -135,10 +135,12 @@ pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> { | |||
| 135 | dfu: DFU, | 135 | dfu: DFU, |
| 136 | /// The state partition has the following format: | 136 | /// The state partition has the following format: |
| 137 | /// All ranges are in multiples of WRITE_SIZE bytes. | 137 | /// All ranges are in multiples of WRITE_SIZE bytes. |
| 138 | /// | Range | Description | | 138 | /// N = Active partition size divided by WRITE_SIZE. |
| 139 | /// | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | | 139 | /// | Range | Description | |
| 140 | /// | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. | | 140 | /// | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | |
| 141 | /// | 2..2 + N | Progress index used while swapping or reverting | 141 | /// | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. | |
| 142 | /// | 2..(2 + 2N) | Progress index used while swapping | | ||
| 143 | /// | (2 + 2N)..(2 + 4N) | Progress index used while reverting | ||
| 142 | state: STATE, | 144 | state: STATE, |
| 143 | } | 145 | } |
| 144 | 146 | ||
| @@ -429,7 +431,7 @@ fn assert_partitions<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>( | |||
| 429 | assert_eq!(dfu.capacity() as u32 % page_size, 0); | 431 | assert_eq!(dfu.capacity() as u32 % page_size, 0); |
| 430 | // DFU partition has to be bigger than ACTIVE partition to handle swap algorithm | 432 | // DFU partition has to be bigger than ACTIVE partition to handle swap algorithm |
| 431 | assert!(dfu.capacity() as u32 - active.capacity() as u32 >= page_size); | 433 | assert!(dfu.capacity() as u32 - active.capacity() as u32 >= page_size); |
| 432 | assert!(2 + 2 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32); | 434 | assert!(2 + 4 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32); |
| 433 | } | 435 | } |
| 434 | 436 | ||
| 435 | #[cfg(test)] | 437 | #[cfg(test)] |
diff --git a/embassy-boot/src/lib.rs b/embassy-boot/src/lib.rs index 7dc811f66..3e61d6036 100644 --- a/embassy-boot/src/lib.rs +++ b/embassy-boot/src/lib.rs | |||
| @@ -3,6 +3,10 @@ | |||
| 3 | #![allow(unsafe_op_in_unsafe_fn)] | 3 | #![allow(unsafe_op_in_unsafe_fn)] |
| 4 | #![warn(missing_docs)] | 4 | #![warn(missing_docs)] |
| 5 | #![doc = include_str!("../README.md")] | 5 | #![doc = include_str!("../README.md")] |
| 6 | |||
| 7 | //! ## Feature flags | ||
| 8 | #![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)] | ||
| 9 | |||
| 6 | mod fmt; | 10 | mod fmt; |
| 7 | 11 | ||
| 8 | mod boot_loader; | 12 | mod boot_loader; |
diff --git a/embassy-mspm0/CHANGELOG.md b/embassy-mspm0/CHANGELOG.md index f0b5868f4..6972a8472 100644 --- a/embassy-mspm0/CHANGELOG.md +++ b/embassy-mspm0/CHANGELOG.md | |||
| @@ -19,3 +19,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 19 | - feat: Add i2c target implementation (#4605) | 19 | - feat: Add i2c target implementation (#4605) |
| 20 | - fix: group irq handlers must check for NO_INTR (#4785) | 20 | - fix: group irq handlers must check for NO_INTR (#4785) |
| 21 | - feat: Add read_reset_cause function | 21 | - feat: Add read_reset_cause function |
| 22 | - feat: Add module Mathacl & example for mspm0g3507 (#4897) \ No newline at end of file | ||
diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml index b76bc7e41..254e0209b 100644 --- a/embassy-mspm0/Cargo.toml +++ b/embassy-mspm0/Cargo.toml | |||
| @@ -70,9 +70,10 @@ log = { version = "0.4.14", optional = true } | |||
| 70 | cortex-m-rt = ">=0.6.15,<0.8" | 70 | cortex-m-rt = ">=0.6.15,<0.8" |
| 71 | cortex-m = "0.7.6" | 71 | cortex-m = "0.7.6" |
| 72 | critical-section = "1.2.0" | 72 | critical-section = "1.2.0" |
| 73 | micromath = "2.0.0" | ||
| 73 | 74 | ||
| 74 | # mspm0-metapac = { version = "" } | 75 | # mspm0-metapac = { version = "" } |
| 75 | mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-8542f260cc89645a983b7f1a874c87b21822279e" } | 76 | mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-f21b04e9de074af4965bf67ec3646cb9fe1b9852" } |
| 76 | 77 | ||
| 77 | [build-dependencies] | 78 | [build-dependencies] |
| 78 | proc-macro2 = "1.0.94" | 79 | proc-macro2 = "1.0.94" |
| @@ -80,7 +81,7 @@ quote = "1.0.40" | |||
| 80 | cfg_aliases = "0.2.1" | 81 | cfg_aliases = "0.2.1" |
| 81 | 82 | ||
| 82 | # mspm0-metapac = { version = "", default-features = false, features = ["metadata"] } | 83 | # mspm0-metapac = { version = "", default-features = false, features = ["metadata"] } |
| 83 | mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-8542f260cc89645a983b7f1a874c87b21822279e", default-features = false, features = ["metadata"] } | 84 | mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-f21b04e9de074af4965bf67ec3646cb9fe1b9852", default-features = false, features = ["metadata"] } |
| 84 | 85 | ||
| 85 | [features] | 86 | [features] |
| 86 | default = ["rt"] | 87 | default = ["rt"] |
diff --git a/embassy-mspm0/build.rs b/embassy-mspm0/build.rs index 4942364aa..0fe056c4e 100644 --- a/embassy-mspm0/build.rs +++ b/embassy-mspm0/build.rs | |||
| @@ -591,6 +591,7 @@ fn generate_peripheral_instances() -> TokenStream { | |||
| 591 | "i2c" => Some(quote! { impl_i2c_instance!(#peri, #fifo_size); }), | 591 | "i2c" => Some(quote! { impl_i2c_instance!(#peri, #fifo_size); }), |
| 592 | "wwdt" => Some(quote! { impl_wwdt_instance!(#peri); }), | 592 | "wwdt" => Some(quote! { impl_wwdt_instance!(#peri); }), |
| 593 | "adc" => Some(quote! { impl_adc_instance!(#peri); }), | 593 | "adc" => Some(quote! { impl_adc_instance!(#peri); }), |
| 594 | "mathacl" => Some(quote! { impl_mathacl_instance!(#peri); }), | ||
| 594 | _ => None, | 595 | _ => None, |
| 595 | }; | 596 | }; |
| 596 | 597 | ||
diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs index 9f3e4d5e8..c43c81853 100644 --- a/embassy-mspm0/src/lib.rs +++ b/embassy-mspm0/src/lib.rs | |||
| @@ -19,6 +19,8 @@ pub mod dma; | |||
| 19 | pub mod gpio; | 19 | pub mod gpio; |
| 20 | pub mod i2c; | 20 | pub mod i2c; |
| 21 | pub mod i2c_target; | 21 | pub mod i2c_target; |
| 22 | #[cfg(any(mspm0g150x, mspm0g151x, mspm0g350x, mspm0g351x))] | ||
| 23 | pub mod mathacl; | ||
| 22 | pub mod timer; | 24 | pub mod timer; |
| 23 | pub mod uart; | 25 | pub mod uart; |
| 24 | pub mod wwdt; | 26 | pub mod wwdt; |
diff --git a/embassy-mspm0/src/mathacl.rs b/embassy-mspm0/src/mathacl.rs new file mode 100644 index 000000000..e29f4a59e --- /dev/null +++ b/embassy-mspm0/src/mathacl.rs | |||
| @@ -0,0 +1,255 @@ | |||
| 1 | //! MATHACL | ||
| 2 | //! | ||
| 3 | //! This HAL implements mathematical calculations performed by the CPU. | ||
| 4 | |||
| 5 | #![macro_use] | ||
| 6 | |||
| 7 | use core::f32::consts::PI; | ||
| 8 | use core::marker::PhantomData; | ||
| 9 | |||
| 10 | use embassy_hal_internal::PeripheralType; | ||
| 11 | use micromath::F32Ext; | ||
| 12 | |||
| 13 | use crate::Peri; | ||
| 14 | use crate::pac::mathacl::{Mathacl as Regs, vals}; | ||
| 15 | |||
| 16 | pub enum Precision { | ||
| 17 | High = 31, | ||
| 18 | Medium = 15, | ||
| 19 | Low = 1, | ||
| 20 | } | ||
| 21 | |||
| 22 | /// Serial error | ||
| 23 | #[derive(Debug, Eq, PartialEq, Copy, Clone)] | ||
| 24 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 25 | #[non_exhaustive] | ||
| 26 | pub enum Error { | ||
| 27 | ValueInWrongRange, | ||
| 28 | NBitsTooBig, | ||
| 29 | } | ||
| 30 | |||
| 31 | pub struct Mathacl<'d> { | ||
| 32 | regs: &'static Regs, | ||
| 33 | _phantom: PhantomData<&'d mut ()>, | ||
| 34 | } | ||
| 35 | |||
| 36 | impl<'d> Mathacl<'d> { | ||
| 37 | /// Mathacl initialization. | ||
| 38 | pub fn new<T: Instance>(_instance: Peri<'d, T>) -> Self { | ||
| 39 | // Init power | ||
| 40 | T::regs().gprcm(0).rstctl().write(|w| { | ||
| 41 | w.set_resetstkyclr(vals::Resetstkyclr::CLR); | ||
| 42 | w.set_resetassert(vals::Resetassert::ASSERT); | ||
| 43 | w.set_key(vals::ResetKey::KEY); | ||
| 44 | }); | ||
| 45 | |||
| 46 | // Enable power | ||
| 47 | T::regs().gprcm(0).pwren().write(|w| { | ||
| 48 | w.set_enable(true); | ||
| 49 | w.set_key(vals::PwrenKey::KEY); | ||
| 50 | }); | ||
| 51 | |||
| 52 | // init delay, 16 cycles | ||
| 53 | cortex_m::asm::delay(16); | ||
| 54 | |||
| 55 | Self { | ||
| 56 | regs: T::regs(), | ||
| 57 | _phantom: PhantomData, | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | /// Internal helper SINCOS function. | ||
| 62 | fn sincos(&mut self, rad: f32, precision: Precision, sin: bool) -> Result<f32, Error> { | ||
| 63 | self.regs.ctl().write(|w| { | ||
| 64 | w.set_func(vals::Func::SINCOS); | ||
| 65 | w.set_numiter(precision as u8); | ||
| 66 | }); | ||
| 67 | |||
| 68 | if rad > PI || rad < -PI { | ||
| 69 | return Err(Error::ValueInWrongRange); | ||
| 70 | } | ||
| 71 | |||
| 72 | // TODO: make f32 division on CPU | ||
| 73 | let native = rad / PI; | ||
| 74 | |||
| 75 | match signed_f32_to_register(native, 0) { | ||
| 76 | Ok(val) => self.regs.op1().write(|w| { | ||
| 77 | w.set_data(val); | ||
| 78 | }), | ||
| 79 | Err(er) => return Err(er), | ||
| 80 | }; | ||
| 81 | |||
| 82 | // check if done | ||
| 83 | while self.regs.status().read().busy() == vals::Busy::NOTDONE {} | ||
| 84 | |||
| 85 | match sin { | ||
| 86 | true => register_to_signed_f32(self.regs.res2().read().data(), 0), | ||
| 87 | false => register_to_signed_f32(self.regs.res1().read().data(), 0), | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | /// Calsulates trigonometric sine operation in the range [-1,1) with a give precision. | ||
| 92 | pub fn sin(&mut self, rad: f32, precision: Precision) -> Result<f32, Error> { | ||
| 93 | self.sincos(rad, precision, true) | ||
| 94 | } | ||
| 95 | |||
| 96 | /// Calsulates trigonometric cosine operation in the range [-1,1) with a give precision. | ||
| 97 | pub fn cos(&mut self, rad: f32, precision: Precision) -> Result<f32, Error> { | ||
| 98 | self.sincos(rad, precision, false) | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | pub(crate) trait SealedInstance { | ||
| 103 | fn regs() -> &'static Regs; | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Mathacl instance trait | ||
| 107 | #[allow(private_bounds)] | ||
| 108 | pub trait Instance: SealedInstance + PeripheralType {} | ||
| 109 | |||
| 110 | macro_rules! impl_mathacl_instance { | ||
| 111 | ($instance: ident) => { | ||
| 112 | impl crate::mathacl::SealedInstance for crate::peripherals::$instance { | ||
| 113 | fn regs() -> &'static crate::pac::mathacl::Mathacl { | ||
| 114 | &crate::pac::$instance | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | impl crate::mathacl::Instance for crate::peripherals::$instance {} | ||
| 119 | }; | ||
| 120 | } | ||
| 121 | |||
| 122 | /// Convert f32 data to understandable by M0 format. | ||
| 123 | fn signed_f32_to_register(data: f32, n_bits: u8) -> Result<u32, Error> { | ||
| 124 | let mut res: u32 = 0; | ||
| 125 | // check if negative | ||
| 126 | let negative = data < 0.0; | ||
| 127 | |||
| 128 | // absolute value for extraction | ||
| 129 | let abs = data.abs(); | ||
| 130 | |||
| 131 | // total integer bit count | ||
| 132 | let total_bits = 31; | ||
| 133 | |||
| 134 | // Validate n_bits | ||
| 135 | if n_bits > 31 { | ||
| 136 | return Err(Error::NBitsTooBig); | ||
| 137 | } | ||
| 138 | |||
| 139 | // number of fractional bits | ||
| 140 | let shift = total_bits - n_bits; | ||
| 141 | |||
| 142 | // Compute masks | ||
| 143 | let (n_mask, m_mask) = if n_bits == 0 { | ||
| 144 | (0, 0x7FFFFFFF) | ||
| 145 | } else if n_bits == 31 { | ||
| 146 | (0x7FFFFFFF, 0) | ||
| 147 | } else { | ||
| 148 | ((1u32 << n_bits) - 1, (1u32 << shift) - 1) | ||
| 149 | }; | ||
| 150 | |||
| 151 | // calc. integer(n) & fractional(m) parts | ||
| 152 | let n = abs.floor() as u32; | ||
| 153 | let mut m = ((abs - abs.floor()) * (1u32 << shift) as f32).round() as u32; | ||
| 154 | |||
| 155 | // Handle trimming integer part | ||
| 156 | if n_bits == 0 && n > 0 { | ||
| 157 | m = 0x7FFFFFFF; | ||
| 158 | } | ||
| 159 | |||
| 160 | // calculate result | ||
| 161 | if n_bits > 0 { | ||
| 162 | res = n << shift & n_mask; | ||
| 163 | } | ||
| 164 | if shift > 0 { | ||
| 165 | res = res | m & m_mask; | ||
| 166 | } | ||
| 167 | |||
| 168 | // if negative, do 2’s compliment | ||
| 169 | if negative { | ||
| 170 | res = !res + 1; | ||
| 171 | } | ||
| 172 | Ok(res) | ||
| 173 | } | ||
| 174 | |||
| 175 | /// Reversely converts M0-register format to native f32. | ||
| 176 | fn register_to_signed_f32(data: u32, n_bits: u8) -> Result<f32, Error> { | ||
| 177 | // Validate n_bits | ||
| 178 | if n_bits > 31 { | ||
| 179 | return Err(Error::NBitsTooBig); | ||
| 180 | } | ||
| 181 | |||
| 182 | // total integer bit count | ||
| 183 | let total_bits = 31; | ||
| 184 | |||
| 185 | let negative = (data >> 31) == 1; | ||
| 186 | |||
| 187 | // number of fractional bits | ||
| 188 | let shift = total_bits - n_bits; | ||
| 189 | |||
| 190 | // Compute masks | ||
| 191 | let (n_mask, m_mask) = if n_bits == 0 { | ||
| 192 | (0, 0x7FFFFFFF) | ||
| 193 | } else if n_bits == 31 { | ||
| 194 | (0x7FFFFFFF, 0) | ||
| 195 | } else { | ||
| 196 | ((1u32 << n_bits) - 1, (1u32 << shift) - 1) | ||
| 197 | }; | ||
| 198 | |||
| 199 | // Compute n and m | ||
| 200 | let mut n = if n_bits == 0 { | ||
| 201 | 0 | ||
| 202 | } else if shift >= 32 { | ||
| 203 | data & n_mask | ||
| 204 | } else { | ||
| 205 | (data >> shift) & n_mask | ||
| 206 | }; | ||
| 207 | let mut m = data & m_mask; | ||
| 208 | |||
| 209 | // if negative, do 2’s compliment | ||
| 210 | if negative { | ||
| 211 | n = !n & n_mask; | ||
| 212 | m = (!m & m_mask) + 1; | ||
| 213 | } | ||
| 214 | |||
| 215 | let mut value = (n as f32) + (m as f32) / (1u32 << shift) as f32; | ||
| 216 | if negative { | ||
| 217 | value = -value; | ||
| 218 | } | ||
| 219 | return Ok(value); | ||
| 220 | } | ||
| 221 | |||
| 222 | #[cfg(test)] | ||
| 223 | mod tests { | ||
| 224 | use super::*; | ||
| 225 | |||
| 226 | #[test] | ||
| 227 | fn mathacl_convert_func_errors() { | ||
| 228 | assert_eq!(signed_f32_to_register(0.0, 32), Err(Error::NBitsTooBig)); | ||
| 229 | assert_eq!(register_to_signed_f32(0, 32), Err(Error::NBitsTooBig)); | ||
| 230 | } | ||
| 231 | |||
| 232 | #[test] | ||
| 233 | fn mathacl_signed_f32_to_register() { | ||
| 234 | let mut test_float = 1.0; | ||
| 235 | assert_eq!(signed_f32_to_register(test_float, 0).unwrap(), 0x7FFFFFFF); | ||
| 236 | |||
| 237 | test_float = 0.0; | ||
| 238 | assert_eq!(signed_f32_to_register(test_float, 0).unwrap(), 0x0); | ||
| 239 | |||
| 240 | test_float = -1.0; | ||
| 241 | assert_eq!(signed_f32_to_register(test_float, 0).unwrap(), 0x80000001); | ||
| 242 | } | ||
| 243 | |||
| 244 | #[test] | ||
| 245 | fn mathacl_register_to_signed_f32() { | ||
| 246 | let mut test_u32: u32 = 0x7FFFFFFF; | ||
| 247 | assert_eq!(register_to_signed_f32(test_u32, 0u8).unwrap(), 1.0); | ||
| 248 | |||
| 249 | test_u32 = 0x0; | ||
| 250 | assert_eq!(register_to_signed_f32(test_u32, 0u8).unwrap(), 0.0); | ||
| 251 | |||
| 252 | test_u32 = 0x80000001; | ||
| 253 | assert_eq!(register_to_signed_f32(test_u32, 0u8).unwrap(), -1.0); | ||
| 254 | } | ||
| 255 | } | ||
diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index 72ecb116a..cfb040ef5 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md | |||
| @@ -23,6 +23,12 @@ 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 | ||
| 30 | - changed: add workaround for anomaly 66 on nrf52 | ||
| 31 | - added: expose PPI events available on SPIS peripheral | ||
| 26 | 32 | ||
| 27 | ## 0.8.0 - 2025-09-30 | 33 | ## 0.8.0 - 2025-09-30 |
| 28 | 34 | ||
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-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 2f5ad352f..28d2119ae 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs | |||
| @@ -776,6 +776,30 @@ pub fn init(config: config::Config) -> Peripherals { | |||
| 776 | } | 776 | } |
| 777 | } | 777 | } |
| 778 | 778 | ||
| 779 | // Workaround for anomaly 66 | ||
| 780 | #[cfg(feature = "_nrf52")] | ||
| 781 | { | ||
| 782 | let ficr = pac::FICR; | ||
| 783 | let temp = pac::TEMP; | ||
| 784 | temp.a(0).write_value(ficr.temp().a0().read().0); | ||
| 785 | temp.a(1).write_value(ficr.temp().a1().read().0); | ||
| 786 | temp.a(2).write_value(ficr.temp().a2().read().0); | ||
| 787 | temp.a(3).write_value(ficr.temp().a3().read().0); | ||
| 788 | temp.a(4).write_value(ficr.temp().a4().read().0); | ||
| 789 | temp.a(5).write_value(ficr.temp().a5().read().0); | ||
| 790 | temp.b(0).write_value(ficr.temp().b0().read().0); | ||
| 791 | temp.b(1).write_value(ficr.temp().b1().read().0); | ||
| 792 | temp.b(2).write_value(ficr.temp().b2().read().0); | ||
| 793 | temp.b(3).write_value(ficr.temp().b3().read().0); | ||
| 794 | temp.b(4).write_value(ficr.temp().b4().read().0); | ||
| 795 | temp.b(5).write_value(ficr.temp().b5().read().0); | ||
| 796 | temp.t(0).write_value(ficr.temp().t0().read().0); | ||
| 797 | temp.t(1).write_value(ficr.temp().t1().read().0); | ||
| 798 | temp.t(2).write_value(ficr.temp().t2().read().0); | ||
| 799 | temp.t(3).write_value(ficr.temp().t3().read().0); | ||
| 800 | temp.t(4).write_value(ficr.temp().t4().read().0); | ||
| 801 | } | ||
| 802 | |||
| 779 | // GLITCHDET is only accessible for secure code | 803 | // GLITCHDET is only accessible for secure code |
| 780 | #[cfg(all(feature = "_nrf54l", feature = "_s"))] | 804 | #[cfg(all(feature = "_nrf54l", feature = "_s"))] |
| 781 | { | 805 | { |
diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 96a9c0ae0..6f837c317 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs | |||
| @@ -17,6 +17,7 @@ use crate::gpio::{self, AnyPin, OutputDrive, Pin as GpioPin, SealedPin as _, con | |||
| 17 | use crate::interrupt::typelevel::Interrupt; | 17 | use crate::interrupt::typelevel::Interrupt; |
| 18 | use crate::pac::gpio::vals as gpiovals; | 18 | use crate::pac::gpio::vals as gpiovals; |
| 19 | use crate::pac::spis::vals; | 19 | use crate::pac::spis::vals; |
| 20 | use crate::ppi::Event; | ||
| 20 | use crate::util::slice_in_ram_or; | 21 | use crate::util::slice_in_ram_or; |
| 21 | use crate::{interrupt, pac}; | 22 | use crate::{interrupt, pac}; |
| 22 | 23 | ||
| @@ -334,6 +335,20 @@ impl<'d> Spis<'d> { | |||
| 334 | Ok((n_rx, n_tx)) | 335 | Ok((n_rx, n_tx)) |
| 335 | } | 336 | } |
| 336 | 337 | ||
| 338 | /// Returns the ACQUIRED event, for use with PPI. | ||
| 339 | /// | ||
| 340 | /// This event will fire when the semaphore is acquired. | ||
| 341 | pub fn event_acquired(&self) -> Event<'d> { | ||
| 342 | Event::from_reg(self.r.events_acquired()) | ||
| 343 | } | ||
| 344 | |||
| 345 | /// Returns the END event, for use with PPI. | ||
| 346 | /// | ||
| 347 | /// This event will fire when the slave transaction is complete. | ||
| 348 | pub fn event_end(&self) -> Event<'d> { | ||
| 349 | Event::from_reg(self.r.events_end()) | ||
| 350 | } | ||
| 351 | |||
| 337 | async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> { | 352 | async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> { |
| 338 | match self.async_inner_from_ram(rx, tx).await { | 353 | match self.async_inner_from_ram(rx, tx).await { |
| 339 | Ok(n) => Ok(n), | 354 | Ok(n) => Ok(n), |
diff --git a/embassy-nxp/CHANGELOG.md b/embassy-nxp/CHANGELOG.md index 39f5c75bd..e6f117da4 100644 --- a/embassy-nxp/CHANGELOG.md +++ b/embassy-nxp/CHANGELOG.md | |||
| @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 7 | 7 | ||
| 8 | <!-- next-header --> | 8 | <!-- next-header --> |
| 9 | ## Unreleased - ReleaseDate | 9 | ## Unreleased - ReleaseDate |
| 10 | - Codegen using `nxp-pac` metadata | ||
| 10 | - LPC55: PWM simple | 11 | - LPC55: PWM simple |
| 11 | - LPC55: Move ALT definitions for USART to TX/RX pin impls. | 12 | - LPC55: Move ALT definitions for USART to TX/RX pin impls. |
| 12 | - LPC55: Remove internal match_iocon macro | 13 | - LPC55: Remove internal match_iocon macro |
diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index f8c63ba29..b78c26c77 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml | |||
| @@ -38,13 +38,13 @@ embassy-time-queue-utils = { version = "0.3.0", path = "../embassy-time-queue-ut | |||
| 38 | embedded-io = "0.6.1" | 38 | embedded-io = "0.6.1" |
| 39 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | 39 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } |
| 40 | ## Chip dependencies | 40 | ## Chip dependencies |
| 41 | nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "477dfdbfd5e6c75c0730c56494b601c1b2257263"} | 41 | nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "af5122e1cbe1483833c5d2e5af96b26a34ed5d62"} |
| 42 | 42 | ||
| 43 | imxrt-rt = { version = "0.1.7", optional = true, features = ["device"] } | 43 | imxrt-rt = { version = "0.1.7", optional = true, features = ["device"] } |
| 44 | 44 | ||
| 45 | [build-dependencies] | 45 | [build-dependencies] |
| 46 | cfg_aliases = "0.2.1" | 46 | cfg_aliases = "0.2.1" |
| 47 | nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "477dfdbfd5e6c75c0730c56494b601c1b2257263", features = ["metadata"], optional = true } | 47 | nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "af5122e1cbe1483833c5d2e5af96b26a34ed5d62", features = ["metadata"], optional = true } |
| 48 | proc-macro2 = "1.0.95" | 48 | proc-macro2 = "1.0.95" |
| 49 | quote = "1.0.15" | 49 | quote = "1.0.15" |
| 50 | 50 | ||
diff --git a/embassy-nxp/build.rs b/embassy-nxp/build.rs index f3c062c87..f53c29161 100644 --- a/embassy-nxp/build.rs +++ b/embassy-nxp/build.rs | |||
| @@ -4,10 +4,12 @@ use std::process::Command; | |||
| 4 | use std::{env, fs}; | 4 | use std::{env, fs}; |
| 5 | 5 | ||
| 6 | use cfg_aliases::cfg_aliases; | 6 | use cfg_aliases::cfg_aliases; |
| 7 | #[cfg(feature = "_rt1xxx")] | ||
| 8 | use nxp_pac::metadata; | 7 | use nxp_pac::metadata; |
| 8 | use nxp_pac::metadata::{METADATA, Peripheral}; | ||
| 9 | #[allow(unused)] | 9 | #[allow(unused)] |
| 10 | use proc_macro2::TokenStream; | 10 | use proc_macro2::TokenStream; |
| 11 | use proc_macro2::{Ident, Literal, Span}; | ||
| 12 | use quote::format_ident; | ||
| 11 | #[allow(unused)] | 13 | #[allow(unused)] |
| 12 | use quote::quote; | 14 | use quote::quote; |
| 13 | 15 | ||
| @@ -31,56 +33,188 @@ fn main() { | |||
| 31 | .unwrap() | 33 | .unwrap() |
| 32 | .to_ascii_lowercase(); | 34 | .to_ascii_lowercase(); |
| 33 | 35 | ||
| 36 | let singletons = singletons(&mut cfgs); | ||
| 37 | |||
| 34 | cfg_aliases! { | 38 | cfg_aliases! { |
| 35 | rt1xxx: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, | 39 | rt1xxx: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, |
| 36 | gpio1: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, | ||
| 37 | gpio2: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, | ||
| 38 | gpio3: { feature = "mimxrt1062" }, | ||
| 39 | gpio4: { feature = "mimxrt1062" }, | ||
| 40 | gpio5: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, | ||
| 41 | } | 40 | } |
| 42 | 41 | ||
| 43 | eprintln!("chip: {chip_name}"); | 42 | eprintln!("chip: {chip_name}"); |
| 44 | 43 | ||
| 45 | generate_code(); | 44 | generate_code(&mut cfgs, &singletons); |
| 46 | } | 45 | } |
| 47 | 46 | ||
| 48 | #[cfg(feature = "_rt1xxx")] | 47 | /// A peripheral singleton returned by `embassy_nxp::init`. |
| 49 | fn generate_iomuxc() -> TokenStream { | 48 | struct Singleton { |
| 50 | use proc_macro2::{Ident, Span}; | 49 | name: String, |
| 51 | 50 | ||
| 52 | let pads = metadata::iomuxc::IOMUXC_REGISTERS.iter().map(|registers| { | 51 | /// A cfg guard which indicates whether the `Peripherals` struct will give the user this singleton. |
| 53 | let name = Ident::new(®isters.name, Span::call_site()); | 52 | cfg: Option<TokenStream>, |
| 54 | let address = registers.pad_ctl; | 53 | } |
| 55 | 54 | ||
| 56 | quote! { | 55 | fn singletons(cfgs: &mut common::CfgSet) -> Vec<Singleton> { |
| 57 | pub const #name: u32 = #address; | 56 | let mut singletons = Vec::new(); |
| 57 | |||
| 58 | for peripheral in METADATA.peripherals { | ||
| 59 | // GPIO and DMA are generated in a 2nd pass. | ||
| 60 | let skip_singleton = if peripheral.name.starts_with("GPIO") || peripheral.name.starts_with("DMA") { | ||
| 61 | true | ||
| 62 | } else { | ||
| 63 | false | ||
| 64 | }; | ||
| 65 | |||
| 66 | if !skip_singleton { | ||
| 67 | singletons.push(Singleton { | ||
| 68 | name: peripheral.name.into(), | ||
| 69 | cfg: None, | ||
| 70 | }); | ||
| 58 | } | 71 | } |
| 59 | }); | 72 | } |
| 60 | 73 | ||
| 61 | let muxes = metadata::iomuxc::IOMUXC_REGISTERS.iter().map(|registers| { | 74 | cfgs.declare_all(&[ |
| 62 | let name = Ident::new(®isters.name, Span::call_site()); | 75 | "gpio1", |
| 63 | let address = registers.mux_ctl; | 76 | "gpio1_hi", |
| 77 | "gpio2", | ||
| 78 | "gpio2_hi", | ||
| 79 | "gpio3", | ||
| 80 | "gpio3_hi", | ||
| 81 | "gpio4", | ||
| 82 | "gpio4_hi", | ||
| 83 | "gpio5", | ||
| 84 | "gpio5_hi", | ||
| 85 | "gpio10", | ||
| 86 | "gpio10_hi", | ||
| 87 | ]); | ||
| 64 | 88 | ||
| 65 | quote! { | 89 | for peripheral in METADATA.peripherals.iter().filter(|p| p.name.starts_with("GPIO")) { |
| 66 | pub const #name: u32 = #address; | 90 | let number = peripheral.name.strip_prefix("GPIO").unwrap(); |
| 91 | assert!(number.parse::<u8>().is_ok()); | ||
| 92 | cfgs.enable(format!("gpio{}", number)); | ||
| 93 | |||
| 94 | for signal in peripheral.signals.iter() { | ||
| 95 | let pin_number = signal.name.parse::<u8>().unwrap(); | ||
| 96 | |||
| 97 | if pin_number > 15 { | ||
| 98 | cfgs.enable(format!("gpio{}_hi", number)); | ||
| 99 | } | ||
| 100 | |||
| 101 | // GPIO signals only defined a single signal, on a single pin. | ||
| 102 | assert_eq!(signal.pins.len(), 1); | ||
| 103 | |||
| 104 | singletons.push(Singleton { | ||
| 105 | name: signal.pins[0].pin.into(), | ||
| 106 | cfg: None, | ||
| 107 | }); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | for peripheral in METADATA.peripherals.iter().filter(|p| p.name.starts_with("DMA")) { | ||
| 112 | let instance = peripheral.name.strip_prefix("DMA").unwrap(); | ||
| 113 | assert!(instance.parse::<u8>().is_ok()); | ||
| 114 | |||
| 115 | for signal in peripheral.signals.iter() { | ||
| 116 | let channel_number = signal.name.parse::<u8>().unwrap(); | ||
| 117 | let name = format!("DMA{instance}_CH{channel_number}"); | ||
| 118 | |||
| 119 | // DMA has no pins. | ||
| 120 | assert!(signal.pins.is_empty()); | ||
| 121 | |||
| 122 | singletons.push(Singleton { name, cfg: None }); | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | for peripheral in METADATA.peripherals.iter().filter(|p| p.name.starts_with("SCT")) { | ||
| 127 | let instance = peripheral.name.strip_prefix("SCT").unwrap(); | ||
| 128 | assert!(instance.parse::<u8>().is_ok()); | ||
| 129 | |||
| 130 | for signal in peripheral.signals.iter() { | ||
| 131 | if !signal.name.starts_with("OUT") { | ||
| 132 | continue; | ||
| 133 | } | ||
| 134 | |||
| 135 | let channel_number = signal.name.strip_prefix("OUT").unwrap().parse::<u8>().unwrap(); | ||
| 136 | let name = format!("SCT{instance}_OUT{channel_number}"); | ||
| 137 | |||
| 138 | singletons.push(Singleton { name, cfg: None }); | ||
| 67 | } | 139 | } |
| 140 | } | ||
| 141 | |||
| 142 | singletons | ||
| 143 | } | ||
| 144 | |||
| 145 | #[cfg(feature = "_rt1xxx")] | ||
| 146 | fn generate_iomuxc() -> TokenStream { | ||
| 147 | let iomuxc_pad_impls = metadata::METADATA | ||
| 148 | .pins | ||
| 149 | .iter() | ||
| 150 | .filter(|p| p.iomuxc.as_ref().filter(|i| i.mux.is_some()).is_some()) | ||
| 151 | .map(|pin| { | ||
| 152 | let Some(ref iomuxc) = pin.iomuxc else { | ||
| 153 | panic!("Pin {} has no IOMUXC definitions", pin.name); | ||
| 154 | }; | ||
| 155 | |||
| 156 | let name = Ident::new(pin.name, Span::call_site()); | ||
| 157 | let mux = iomuxc.mux.unwrap(); | ||
| 158 | let pad = iomuxc.pad; | ||
| 159 | |||
| 160 | quote! { | ||
| 161 | impl_iomuxc_pad!(#name, #pad, #mux); | ||
| 162 | } | ||
| 163 | }); | ||
| 164 | |||
| 165 | let base_match_arms = metadata::METADATA | ||
| 166 | .peripherals | ||
| 167 | .iter() | ||
| 168 | .filter(|p| p.name.starts_with("GPIO")) | ||
| 169 | .map(|peripheral| { | ||
| 170 | peripheral.signals.iter().map(|signal| { | ||
| 171 | // All GPIO signals have a single pin. | ||
| 172 | let pin = &signal.pins[0]; | ||
| 173 | let instance = peripheral.name.strip_prefix("GPIO").unwrap(); | ||
| 174 | let bank_match = format_ident!("Gpio{}", instance); | ||
| 175 | let pin_number = signal.name.parse::<u8>().unwrap(); | ||
| 176 | let pin_ident = Ident::new(pin.pin, Span::call_site()); | ||
| 177 | |||
| 178 | quote! { | ||
| 179 | (Bank::#bank_match, #pin_number) => <crate::peripherals::#pin_ident as crate::iomuxc::SealedPad> | ||
| 180 | } | ||
| 181 | }) | ||
| 182 | }) | ||
| 183 | .flatten() | ||
| 184 | .collect::<Vec<_>>(); | ||
| 185 | |||
| 186 | let pad_match_arms = base_match_arms.iter().map(|arm| { | ||
| 187 | quote! { #arm::PAD } | ||
| 188 | }); | ||
| 189 | |||
| 190 | let mux_match_arms = base_match_arms.iter().map(|arm| { | ||
| 191 | quote! { #arm::MUX } | ||
| 68 | }); | 192 | }); |
| 69 | 193 | ||
| 70 | quote! { | 194 | quote! { |
| 71 | pub mod iomuxc { | 195 | #(#iomuxc_pad_impls)* |
| 72 | pub mod pads { | 196 | |
| 73 | #(#pads)* | 197 | pub(crate) fn iomuxc_pad(bank: crate::gpio::Bank, pin: u8) -> *mut () { |
| 198 | use crate::gpio::Bank; | ||
| 199 | |||
| 200 | match (bank, pin) { | ||
| 201 | #(#pad_match_arms),*, | ||
| 202 | _ => unreachable!() | ||
| 74 | } | 203 | } |
| 204 | } | ||
| 205 | |||
| 206 | pub(crate) fn iomuxc_mux(bank: crate::gpio::Bank, pin: u8) -> Option<*mut ()> { | ||
| 207 | use crate::gpio::Bank; | ||
| 75 | 208 | ||
| 76 | pub mod muxes { | 209 | match (bank, pin) { |
| 77 | #(#muxes)* | 210 | #(#mux_match_arms),*, |
| 211 | _ => unreachable!() | ||
| 78 | } | 212 | } |
| 79 | } | 213 | } |
| 80 | } | 214 | } |
| 81 | } | 215 | } |
| 82 | 216 | ||
| 83 | fn generate_code() { | 217 | fn generate_code(cfgs: &mut common::CfgSet, singletons: &[Singleton]) { |
| 84 | #[allow(unused)] | 218 | #[allow(unused)] |
| 85 | use std::fmt::Write; | 219 | use std::fmt::Write; |
| 86 | 220 | ||
| @@ -88,14 +222,179 @@ fn generate_code() { | |||
| 88 | #[allow(unused_mut)] | 222 | #[allow(unused_mut)] |
| 89 | let mut output = String::new(); | 223 | let mut output = String::new(); |
| 90 | 224 | ||
| 225 | writeln!(&mut output, "{}", peripherals(singletons)).unwrap(); | ||
| 226 | |||
| 91 | #[cfg(feature = "_rt1xxx")] | 227 | #[cfg(feature = "_rt1xxx")] |
| 92 | writeln!(&mut output, "{}", generate_iomuxc()).unwrap(); | 228 | writeln!(&mut output, "{}", generate_iomuxc()).unwrap(); |
| 93 | 229 | ||
| 230 | writeln!(&mut output, "{}", interrupts()).unwrap(); | ||
| 231 | writeln!(&mut output, "{}", impl_peripherals(cfgs, singletons)).unwrap(); | ||
| 232 | |||
| 94 | let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); | 233 | let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); |
| 95 | fs::write(&out_file, output).unwrap(); | 234 | fs::write(&out_file, output).unwrap(); |
| 96 | rustfmt(&out_file); | 235 | rustfmt(&out_file); |
| 97 | } | 236 | } |
| 98 | 237 | ||
| 238 | fn interrupts() -> TokenStream { | ||
| 239 | let interrupts = METADATA.interrupts.iter().map(|interrupt| format_ident!("{interrupt}")); | ||
| 240 | |||
| 241 | quote! { | ||
| 242 | embassy_hal_internal::interrupt_mod!(#(#interrupts),*); | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | fn peripherals(singletons: &[Singleton]) -> TokenStream { | ||
| 247 | let defs = singletons.iter().map(|s| { | ||
| 248 | let ident = Ident::new(&s.name, Span::call_site()); | ||
| 249 | quote! { #ident } | ||
| 250 | }); | ||
| 251 | |||
| 252 | let peripherals = singletons.iter().map(|s| { | ||
| 253 | let ident = Ident::new(&s.name, Span::call_site()); | ||
| 254 | let cfg = s.cfg.clone().unwrap_or_else(|| quote! {}); | ||
| 255 | quote! { | ||
| 256 | #cfg | ||
| 257 | #ident | ||
| 258 | } | ||
| 259 | }); | ||
| 260 | |||
| 261 | quote! { | ||
| 262 | embassy_hal_internal::peripherals_definition!(#(#defs),*); | ||
| 263 | embassy_hal_internal::peripherals_struct!(#(#peripherals),*); | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | fn impl_gpio_pin(impls: &mut Vec<TokenStream>, peripheral: &Peripheral) { | ||
| 268 | let instance = peripheral.name.strip_prefix("GPIO").unwrap(); | ||
| 269 | let bank = format_ident!("Gpio{}", instance); | ||
| 270 | // let pin = | ||
| 271 | |||
| 272 | for signal in peripheral.signals.iter() { | ||
| 273 | let pin_number = signal.name.parse::<u8>().unwrap(); | ||
| 274 | let pin = Ident::new(signal.pins[0].pin, Span::call_site()); | ||
| 275 | |||
| 276 | impls.push(quote! { | ||
| 277 | impl_pin!(#pin, #bank, #pin_number); | ||
| 278 | }); | ||
| 279 | } | ||
| 280 | } | ||
| 281 | |||
| 282 | fn impl_dma_channel(impls: &mut Vec<TokenStream>, peripheral: &Peripheral) { | ||
| 283 | let instance = Ident::new(peripheral.name, Span::call_site()); | ||
| 284 | |||
| 285 | for signal in peripheral.signals.iter() { | ||
| 286 | let channel_number = signal.name.parse::<u8>().unwrap(); | ||
| 287 | let channel_name = format_ident!("{instance}_CH{channel_number}"); | ||
| 288 | |||
| 289 | impls.push(quote! { | ||
| 290 | impl_dma_channel!(#instance, #channel_name, #channel_number); | ||
| 291 | }); | ||
| 292 | } | ||
| 293 | } | ||
| 294 | |||
| 295 | fn impl_usart(impls: &mut Vec<TokenStream>, peripheral: &Peripheral) { | ||
| 296 | let instance = Ident::new(peripheral.name, Span::call_site()); | ||
| 297 | let flexcomm = Ident::new( | ||
| 298 | peripheral.flexcomm.expect("LPC55 must specify FLEXCOMM instance"), | ||
| 299 | Span::call_site(), | ||
| 300 | ); | ||
| 301 | let number = Literal::u8_unsuffixed(peripheral.name.strip_prefix("USART").unwrap().parse::<u8>().unwrap()); | ||
| 302 | |||
| 303 | impls.push(quote! { | ||
| 304 | impl_usart_instance!(#instance, #flexcomm, #number); | ||
| 305 | }); | ||
| 306 | |||
| 307 | for signal in peripheral.signals { | ||
| 308 | let r#macro = match signal.name { | ||
| 309 | "TXD" => format_ident!("impl_usart_txd_pin"), | ||
| 310 | "RXD" => format_ident!("impl_usart_rxd_pin"), | ||
| 311 | _ => unreachable!(), | ||
| 312 | }; | ||
| 313 | |||
| 314 | for pin in signal.pins { | ||
| 315 | let alt = format_ident!("ALT{}", pin.alt); | ||
| 316 | let pin = format_ident!("{}", pin.pin); | ||
| 317 | |||
| 318 | impls.push(quote! { | ||
| 319 | #r#macro!(#pin, #instance, #alt); | ||
| 320 | }); | ||
| 321 | } | ||
| 322 | } | ||
| 323 | |||
| 324 | for dma_mux in peripheral.dma_muxing { | ||
| 325 | assert_eq!(dma_mux.mux, "DMA0", "TODO: USART for more than LPC55"); | ||
| 326 | |||
| 327 | let r#macro = match dma_mux.signal { | ||
| 328 | "TX" => format_ident!("impl_usart_tx_channel"), | ||
| 329 | "RX" => format_ident!("impl_usart_rx_channel"), | ||
| 330 | _ => unreachable!(), | ||
| 331 | }; | ||
| 332 | |||
| 333 | let channel = format_ident!("DMA0_CH{}", dma_mux.request); | ||
| 334 | |||
| 335 | impls.push(quote! { | ||
| 336 | #r#macro!(#instance, #channel); | ||
| 337 | }); | ||
| 338 | } | ||
| 339 | } | ||
| 340 | |||
| 341 | fn impl_sct(impls: &mut Vec<TokenStream>, peripheral: &Peripheral) { | ||
| 342 | let instance = Ident::new(peripheral.name, Span::call_site()); | ||
| 343 | |||
| 344 | impls.push(quote! { | ||
| 345 | impl_sct_instance!(#instance); | ||
| 346 | }); | ||
| 347 | |||
| 348 | for signal in peripheral.signals.iter() { | ||
| 349 | if signal.name.starts_with("OUT") { | ||
| 350 | let channel_number = signal.name.strip_prefix("OUT").unwrap().parse::<u8>().unwrap(); | ||
| 351 | |||
| 352 | let channel_name = format_ident!("{instance}_OUT{channel_number}"); | ||
| 353 | |||
| 354 | impls.push(quote! { | ||
| 355 | impl_sct_output_instance!(#instance, #channel_name, #channel_number); | ||
| 356 | }); | ||
| 357 | |||
| 358 | if signal.name.starts_with("OUT") { | ||
| 359 | for pin in signal.pins { | ||
| 360 | let pin_name = format_ident!("{}", pin.pin); | ||
| 361 | let alt = format_ident!("ALT{}", pin.alt); | ||
| 362 | |||
| 363 | impls.push(quote! { | ||
| 364 | impl_sct_output_pin!(#instance, #channel_name, #pin_name, #alt); | ||
| 365 | }); | ||
| 366 | } | ||
| 367 | } | ||
| 368 | } | ||
| 369 | } | ||
| 370 | } | ||
| 371 | |||
| 372 | fn impl_peripherals(_cfgs: &mut common::CfgSet, _singletons: &[Singleton]) -> TokenStream { | ||
| 373 | let mut impls = Vec::new(); | ||
| 374 | |||
| 375 | for peripheral in metadata::METADATA.peripherals.iter() { | ||
| 376 | if peripheral.name.starts_with("GPIO") { | ||
| 377 | impl_gpio_pin(&mut impls, peripheral); | ||
| 378 | } | ||
| 379 | |||
| 380 | if peripheral.name.starts_with("DMA") { | ||
| 381 | impl_dma_channel(&mut impls, peripheral); | ||
| 382 | } | ||
| 383 | |||
| 384 | if peripheral.name.starts_with("USART") { | ||
| 385 | impl_usart(&mut impls, peripheral); | ||
| 386 | } | ||
| 387 | |||
| 388 | if peripheral.name.starts_with("SCT") { | ||
| 389 | impl_sct(&mut impls, peripheral); | ||
| 390 | } | ||
| 391 | } | ||
| 392 | |||
| 393 | quote! { | ||
| 394 | #(#impls)* | ||
| 395 | } | ||
| 396 | } | ||
| 397 | |||
| 99 | /// rustfmt a given path. | 398 | /// rustfmt a given path. |
| 100 | /// Failures are logged to stderr and ignored. | 399 | /// Failures are logged to stderr and ignored. |
| 101 | fn rustfmt(path: impl AsRef<Path>) { | 400 | fn rustfmt(path: impl AsRef<Path>) { |
diff --git a/embassy-nxp/src/chips/lpc55.rs b/embassy-nxp/src/chips/lpc55.rs index e9addddb6..7967e07d1 100644 --- a/embassy-nxp/src/chips/lpc55.rs +++ b/embassy-nxp/src/chips/lpc55.rs | |||
| @@ -1,121 +1,10 @@ | |||
| 1 | pub use nxp_pac as pac; | 1 | pub(crate) mod _generated { |
| 2 | #![allow(dead_code)] | ||
| 3 | #![allow(unused_imports)] | ||
| 4 | #![allow(non_snake_case)] | ||
| 5 | #![allow(missing_docs)] | ||
| 2 | 6 | ||
| 3 | embassy_hal_internal::interrupt_mod!( | 7 | include!(concat!(env!("OUT_DIR"), "/_generated.rs")); |
| 4 | FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7 | ||
| 5 | ); | ||
| 6 | |||
| 7 | embassy_hal_internal::peripherals! { | ||
| 8 | // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other | ||
| 9 | // peripheral types (e.g. I2C). | ||
| 10 | PIO0_0, | ||
| 11 | PIO0_1, | ||
| 12 | PIO0_2, | ||
| 13 | PIO0_3, | ||
| 14 | PIO0_4, | ||
| 15 | PIO0_5, | ||
| 16 | PIO0_6, | ||
| 17 | PIO0_7, | ||
| 18 | PIO0_8, | ||
| 19 | PIO0_9, | ||
| 20 | PIO0_10, | ||
| 21 | PIO0_11, | ||
| 22 | PIO0_12, | ||
| 23 | PIO0_13, | ||
| 24 | PIO0_14, | ||
| 25 | PIO0_15, | ||
| 26 | PIO0_16, | ||
| 27 | PIO0_17, | ||
| 28 | PIO0_18, | ||
| 29 | PIO0_19, | ||
| 30 | PIO0_20, | ||
| 31 | PIO0_21, | ||
| 32 | PIO0_22, | ||
| 33 | PIO0_23, | ||
| 34 | PIO0_24, | ||
| 35 | PIO0_25, | ||
| 36 | PIO0_26, | ||
| 37 | PIO0_27, | ||
| 38 | PIO0_28, | ||
| 39 | PIO0_29, | ||
| 40 | PIO0_30, | ||
| 41 | PIO0_31, | ||
| 42 | PIO1_0, | ||
| 43 | PIO1_1, | ||
| 44 | PIO1_2, | ||
| 45 | PIO1_3, | ||
| 46 | PIO1_4, | ||
| 47 | PIO1_5, | ||
| 48 | PIO1_6, | ||
| 49 | PIO1_7, | ||
| 50 | PIO1_8, | ||
| 51 | PIO1_9, | ||
| 52 | PIO1_10, | ||
| 53 | PIO1_11, | ||
| 54 | PIO1_12, | ||
| 55 | PIO1_13, | ||
| 56 | PIO1_14, | ||
| 57 | PIO1_15, | ||
| 58 | PIO1_16, | ||
| 59 | PIO1_17, | ||
| 60 | PIO1_18, | ||
| 61 | PIO1_19, | ||
| 62 | PIO1_20, | ||
| 63 | PIO1_21, | ||
| 64 | PIO1_22, | ||
| 65 | PIO1_23, | ||
| 66 | PIO1_24, | ||
| 67 | PIO1_25, | ||
| 68 | PIO1_26, | ||
| 69 | PIO1_27, | ||
| 70 | PIO1_28, | ||
| 71 | PIO1_29, | ||
| 72 | PIO1_30, | ||
| 73 | PIO1_31, | ||
| 74 | |||
| 75 | // Direct Memory Access (DMA) channels. They are used for asynchronous modes of peripherals. | ||
| 76 | DMA_CH0, | ||
| 77 | DMA_CH1, | ||
| 78 | DMA_CH2, | ||
| 79 | DMA_CH3, | ||
| 80 | DMA_CH4, | ||
| 81 | DMA_CH5, | ||
| 82 | DMA_CH6, | ||
| 83 | DMA_CH7, | ||
| 84 | DMA_CH8, | ||
| 85 | DMA_CH9, | ||
| 86 | DMA_CH10, | ||
| 87 | DMA_CH11, | ||
| 88 | DMA_CH12, | ||
| 89 | DMA_CH13, | ||
| 90 | DMA_CH14, | ||
| 91 | DMA_CH15, | ||
| 92 | DMA_CH16, | ||
| 93 | DMA_CH17, | ||
| 94 | DMA_CH18, | ||
| 95 | DMA_CH19, | ||
| 96 | DMA_CH20, | ||
| 97 | DMA_CH21, | ||
| 98 | DMA_CH22, | ||
| 99 | |||
| 100 | // Pulse-Width Modulation Outputs. | ||
| 101 | PWM_OUTPUT0, | ||
| 102 | PWM_OUTPUT1, | ||
| 103 | PWM_OUTPUT2, | ||
| 104 | PWM_OUTPUT3, | ||
| 105 | PWM_OUTPUT4, | ||
| 106 | PWM_OUTPUT5, | ||
| 107 | PWM_OUTPUT6, | ||
| 108 | PWM_OUTPUT7, | ||
| 109 | PWM_OUTPUT8, | ||
| 110 | PWM_OUTPUT9, | ||
| 111 | |||
| 112 | // Universal Synchronous/Asynchronous Receiver/Transmitter (USART) instances. | ||
| 113 | USART0, | ||
| 114 | USART1, | ||
| 115 | USART2, | ||
| 116 | USART3, | ||
| 117 | USART4, | ||
| 118 | USART5, | ||
| 119 | USART6, | ||
| 120 | USART7 | ||
| 121 | } | 8 | } |
| 9 | |||
| 10 | pub use _generated::*; | ||
diff --git a/embassy-nxp/src/chips/mimxrt1011.rs b/embassy-nxp/src/chips/mimxrt1011.rs index a74d953fc..d5969a24b 100644 --- a/embassy-nxp/src/chips/mimxrt1011.rs +++ b/embassy-nxp/src/chips/mimxrt1011.rs | |||
| @@ -1,107 +1,5 @@ | |||
| 1 | // This must be imported so that __preinit is defined. | 1 | // This must be imported so that __preinit is defined. |
| 2 | use imxrt_rt as _; | 2 | use imxrt_rt as _; |
| 3 | pub use nxp_pac as pac; | ||
| 4 | |||
| 5 | embassy_hal_internal::peripherals! { | ||
| 6 | // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other | ||
| 7 | // peripheral types (e.g. I2C). | ||
| 8 | GPIO_00, | ||
| 9 | GPIO_01, | ||
| 10 | GPIO_02, | ||
| 11 | GPIO_03, | ||
| 12 | GPIO_04, | ||
| 13 | GPIO_05, | ||
| 14 | GPIO_06, | ||
| 15 | GPIO_07, | ||
| 16 | GPIO_08, | ||
| 17 | GPIO_09, | ||
| 18 | GPIO_10, | ||
| 19 | GPIO_11, | ||
| 20 | GPIO_12, | ||
| 21 | GPIO_13, | ||
| 22 | GPIO_AD_00, | ||
| 23 | GPIO_AD_01, | ||
| 24 | GPIO_AD_02, | ||
| 25 | GPIO_AD_03, | ||
| 26 | GPIO_AD_04, | ||
| 27 | GPIO_AD_05, | ||
| 28 | GPIO_AD_06, | ||
| 29 | GPIO_AD_07, | ||
| 30 | GPIO_AD_08, | ||
| 31 | GPIO_AD_09, | ||
| 32 | GPIO_AD_10, | ||
| 33 | GPIO_AD_11, | ||
| 34 | GPIO_AD_12, | ||
| 35 | GPIO_AD_13, | ||
| 36 | GPIO_AD_14, | ||
| 37 | GPIO_SD_00, | ||
| 38 | GPIO_SD_01, | ||
| 39 | GPIO_SD_02, | ||
| 40 | GPIO_SD_03, | ||
| 41 | GPIO_SD_04, | ||
| 42 | GPIO_SD_05, | ||
| 43 | GPIO_SD_06, | ||
| 44 | GPIO_SD_07, | ||
| 45 | GPIO_SD_08, | ||
| 46 | GPIO_SD_09, | ||
| 47 | GPIO_SD_10, | ||
| 48 | GPIO_SD_11, | ||
| 49 | GPIO_SD_12, | ||
| 50 | GPIO_SD_13, | ||
| 51 | PMIC_ON_REQ, | ||
| 52 | } | ||
| 53 | |||
| 54 | impl_gpio! { | ||
| 55 | // GPIO Bank 1 | ||
| 56 | GPIO_00(Gpio1, 0); | ||
| 57 | GPIO_01(Gpio1, 1); | ||
| 58 | GPIO_02(Gpio1, 2); | ||
| 59 | GPIO_03(Gpio1, 3); | ||
| 60 | GPIO_04(Gpio1, 4); | ||
| 61 | GPIO_05(Gpio1, 5); | ||
| 62 | GPIO_06(Gpio1, 6); | ||
| 63 | GPIO_07(Gpio1, 7); | ||
| 64 | GPIO_08(Gpio1, 8); | ||
| 65 | GPIO_09(Gpio1, 9); | ||
| 66 | GPIO_10(Gpio1, 10); | ||
| 67 | GPIO_11(Gpio1, 11); | ||
| 68 | GPIO_12(Gpio1, 12); | ||
| 69 | GPIO_13(Gpio1, 13); | ||
| 70 | GPIO_AD_00(Gpio1, 14); | ||
| 71 | GPIO_AD_01(Gpio1, 15); | ||
| 72 | GPIO_AD_02(Gpio1, 16); | ||
| 73 | GPIO_AD_03(Gpio1, 17); | ||
| 74 | GPIO_AD_04(Gpio1, 18); | ||
| 75 | GPIO_AD_05(Gpio1, 19); | ||
| 76 | GPIO_AD_06(Gpio1, 20); | ||
| 77 | GPIO_AD_07(Gpio1, 21); | ||
| 78 | GPIO_AD_08(Gpio1, 22); | ||
| 79 | GPIO_AD_09(Gpio1, 23); | ||
| 80 | GPIO_AD_10(Gpio1, 24); | ||
| 81 | GPIO_AD_11(Gpio1, 25); | ||
| 82 | GPIO_AD_12(Gpio1, 26); | ||
| 83 | GPIO_AD_13(Gpio1, 27); | ||
| 84 | GPIO_AD_14(Gpio1, 28); | ||
| 85 | |||
| 86 | // GPIO Bank 2 | ||
| 87 | GPIO_SD_00(Gpio2, 0); | ||
| 88 | GPIO_SD_01(Gpio2, 1); | ||
| 89 | GPIO_SD_02(Gpio2, 2); | ||
| 90 | GPIO_SD_03(Gpio2, 3); | ||
| 91 | GPIO_SD_04(Gpio2, 4); | ||
| 92 | GPIO_SD_05(Gpio2, 5); | ||
| 93 | GPIO_SD_06(Gpio2, 6); | ||
| 94 | GPIO_SD_07(Gpio2, 7); | ||
| 95 | GPIO_SD_08(Gpio2, 8); | ||
| 96 | GPIO_SD_09(Gpio2, 9); | ||
| 97 | GPIO_SD_10(Gpio2, 10); | ||
| 98 | GPIO_SD_11(Gpio2, 11); | ||
| 99 | GPIO_SD_12(Gpio2, 12); | ||
| 100 | GPIO_SD_13(Gpio2, 13); | ||
| 101 | |||
| 102 | // GPIO Bank 5 | ||
| 103 | PMIC_ON_REQ(Gpio5, 0); | ||
| 104 | } | ||
| 105 | 3 | ||
| 106 | pub(crate) mod _generated { | 4 | pub(crate) mod _generated { |
| 107 | #![allow(dead_code)] | 5 | #![allow(dead_code)] |
| @@ -111,3 +9,5 @@ pub(crate) mod _generated { | |||
| 111 | 9 | ||
| 112 | include!(concat!(env!("OUT_DIR"), "/_generated.rs")); | 10 | include!(concat!(env!("OUT_DIR"), "/_generated.rs")); |
| 113 | } | 11 | } |
| 12 | |||
| 13 | pub use _generated::*; | ||
diff --git a/embassy-nxp/src/chips/mimxrt1062.rs b/embassy-nxp/src/chips/mimxrt1062.rs index ef153bd66..d5969a24b 100644 --- a/embassy-nxp/src/chips/mimxrt1062.rs +++ b/embassy-nxp/src/chips/mimxrt1062.rs | |||
| @@ -1,276 +1,5 @@ | |||
| 1 | // This must be imported so that __preinit is defined. | 1 | // This must be imported so that __preinit is defined. |
| 2 | use imxrt_rt as _; | 2 | use imxrt_rt as _; |
| 3 | pub use nxp_pac as pac; | ||
| 4 | |||
| 5 | embassy_hal_internal::peripherals! { | ||
| 6 | // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other | ||
| 7 | // peripheral types (e.g. I2C). | ||
| 8 | GPIO_AD_B0_00, | ||
| 9 | GPIO_AD_B0_01, | ||
| 10 | GPIO_AD_B0_02, | ||
| 11 | GPIO_AD_B0_03, | ||
| 12 | GPIO_AD_B0_04, | ||
| 13 | GPIO_AD_B0_05, | ||
| 14 | GPIO_AD_B0_06, | ||
| 15 | GPIO_AD_B0_07, | ||
| 16 | GPIO_AD_B0_08, | ||
| 17 | GPIO_AD_B0_09, | ||
| 18 | GPIO_AD_B0_10, | ||
| 19 | GPIO_AD_B0_11, | ||
| 20 | GPIO_AD_B0_12, | ||
| 21 | GPIO_AD_B0_13, | ||
| 22 | GPIO_AD_B0_14, | ||
| 23 | GPIO_AD_B0_15, | ||
| 24 | GPIO_AD_B1_00, | ||
| 25 | GPIO_AD_B1_01, | ||
| 26 | GPIO_AD_B1_02, | ||
| 27 | GPIO_AD_B1_03, | ||
| 28 | GPIO_AD_B1_04, | ||
| 29 | GPIO_AD_B1_05, | ||
| 30 | GPIO_AD_B1_06, | ||
| 31 | GPIO_AD_B1_07, | ||
| 32 | GPIO_AD_B1_08, | ||
| 33 | GPIO_AD_B1_09, | ||
| 34 | GPIO_AD_B1_10, | ||
| 35 | GPIO_AD_B1_11, | ||
| 36 | GPIO_AD_B1_12, | ||
| 37 | GPIO_AD_B1_13, | ||
| 38 | GPIO_AD_B1_14, | ||
| 39 | GPIO_AD_B1_15, | ||
| 40 | GPIO_B0_00, | ||
| 41 | GPIO_B0_01, | ||
| 42 | GPIO_B0_02, | ||
| 43 | GPIO_B0_03, | ||
| 44 | GPIO_B0_04, | ||
| 45 | GPIO_B0_05, | ||
| 46 | GPIO_B0_06, | ||
| 47 | GPIO_B0_07, | ||
| 48 | GPIO_B0_08, | ||
| 49 | GPIO_B0_09, | ||
| 50 | GPIO_B0_10, | ||
| 51 | GPIO_B0_11, | ||
| 52 | GPIO_B0_12, | ||
| 53 | GPIO_B0_13, | ||
| 54 | GPIO_B0_14, | ||
| 55 | GPIO_B0_15, | ||
| 56 | GPIO_B1_00, | ||
| 57 | GPIO_B1_01, | ||
| 58 | GPIO_B1_02, | ||
| 59 | GPIO_B1_03, | ||
| 60 | GPIO_B1_04, | ||
| 61 | GPIO_B1_05, | ||
| 62 | GPIO_B1_06, | ||
| 63 | GPIO_B1_07, | ||
| 64 | GPIO_B1_08, | ||
| 65 | GPIO_B1_09, | ||
| 66 | GPIO_B1_10, | ||
| 67 | GPIO_B1_11, | ||
| 68 | GPIO_B1_12, | ||
| 69 | GPIO_B1_13, | ||
| 70 | GPIO_B1_14, | ||
| 71 | GPIO_B1_15, | ||
| 72 | GPIO_EMC_00, | ||
| 73 | GPIO_EMC_01, | ||
| 74 | GPIO_EMC_02, | ||
| 75 | GPIO_EMC_03, | ||
| 76 | GPIO_EMC_04, | ||
| 77 | GPIO_EMC_05, | ||
| 78 | GPIO_EMC_06, | ||
| 79 | GPIO_EMC_07, | ||
| 80 | GPIO_EMC_08, | ||
| 81 | GPIO_EMC_09, | ||
| 82 | GPIO_EMC_10, | ||
| 83 | GPIO_EMC_11, | ||
| 84 | GPIO_EMC_12, | ||
| 85 | GPIO_EMC_13, | ||
| 86 | GPIO_EMC_14, | ||
| 87 | GPIO_EMC_15, | ||
| 88 | GPIO_EMC_16, | ||
| 89 | GPIO_EMC_17, | ||
| 90 | GPIO_EMC_18, | ||
| 91 | GPIO_EMC_19, | ||
| 92 | GPIO_EMC_20, | ||
| 93 | GPIO_EMC_21, | ||
| 94 | GPIO_EMC_22, | ||
| 95 | GPIO_EMC_23, | ||
| 96 | GPIO_EMC_24, | ||
| 97 | GPIO_EMC_25, | ||
| 98 | GPIO_EMC_26, | ||
| 99 | GPIO_EMC_27, | ||
| 100 | GPIO_EMC_28, | ||
| 101 | GPIO_EMC_29, | ||
| 102 | GPIO_EMC_30, | ||
| 103 | GPIO_EMC_31, | ||
| 104 | GPIO_EMC_32, | ||
| 105 | GPIO_EMC_33, | ||
| 106 | GPIO_EMC_34, | ||
| 107 | GPIO_EMC_35, | ||
| 108 | GPIO_EMC_36, | ||
| 109 | GPIO_EMC_37, | ||
| 110 | GPIO_EMC_38, | ||
| 111 | GPIO_EMC_39, | ||
| 112 | GPIO_EMC_40, | ||
| 113 | GPIO_EMC_41, | ||
| 114 | GPIO_SD_B0_00, | ||
| 115 | GPIO_SD_B0_01, | ||
| 116 | GPIO_SD_B0_02, | ||
| 117 | GPIO_SD_B0_03, | ||
| 118 | GPIO_SD_B0_04, | ||
| 119 | GPIO_SD_B0_05, | ||
| 120 | GPIO_SD_B1_00, | ||
| 121 | GPIO_SD_B1_01, | ||
| 122 | GPIO_SD_B1_02, | ||
| 123 | GPIO_SD_B1_03, | ||
| 124 | GPIO_SD_B1_04, | ||
| 125 | GPIO_SD_B1_05, | ||
| 126 | GPIO_SD_B1_06, | ||
| 127 | GPIO_SD_B1_07, | ||
| 128 | GPIO_SD_B1_08, | ||
| 129 | GPIO_SD_B1_09, | ||
| 130 | GPIO_SD_B1_10, | ||
| 131 | GPIO_SD_B1_11, | ||
| 132 | WAKEUP, | ||
| 133 | PMIC_ON_REQ, | ||
| 134 | PMIC_STBY_REQ, | ||
| 135 | } | ||
| 136 | |||
| 137 | impl_gpio! { | ||
| 138 | // GPIO Bank 1 | ||
| 139 | GPIO_AD_B0_00(Gpio1, 0); | ||
| 140 | GPIO_AD_B0_01(Gpio1, 1); | ||
| 141 | GPIO_AD_B0_02(Gpio1, 2); | ||
| 142 | GPIO_AD_B0_03(Gpio1, 3); | ||
| 143 | GPIO_AD_B0_04(Gpio1, 4); | ||
| 144 | GPIO_AD_B0_05(Gpio1, 5); | ||
| 145 | GPIO_AD_B0_06(Gpio1, 6); | ||
| 146 | GPIO_AD_B0_07(Gpio1, 7); | ||
| 147 | GPIO_AD_B0_08(Gpio1, 8); | ||
| 148 | GPIO_AD_B0_09(Gpio1, 9); | ||
| 149 | GPIO_AD_B0_10(Gpio1, 10); | ||
| 150 | GPIO_AD_B0_11(Gpio1, 11); | ||
| 151 | GPIO_AD_B0_12(Gpio1, 12); | ||
| 152 | GPIO_AD_B0_13(Gpio1, 13); | ||
| 153 | GPIO_AD_B0_14(Gpio1, 14); | ||
| 154 | GPIO_AD_B0_15(Gpio1, 15); | ||
| 155 | GPIO_AD_B1_00(Gpio1, 16); | ||
| 156 | GPIO_AD_B1_01(Gpio1, 17); | ||
| 157 | GPIO_AD_B1_02(Gpio1, 18); | ||
| 158 | GPIO_AD_B1_03(Gpio1, 19); | ||
| 159 | GPIO_AD_B1_04(Gpio1, 20); | ||
| 160 | GPIO_AD_B1_05(Gpio1, 21); | ||
| 161 | GPIO_AD_B1_06(Gpio1, 22); | ||
| 162 | GPIO_AD_B1_07(Gpio1, 23); | ||
| 163 | GPIO_AD_B1_08(Gpio1, 24); | ||
| 164 | GPIO_AD_B1_09(Gpio1, 25); | ||
| 165 | GPIO_AD_B1_10(Gpio1, 26); | ||
| 166 | GPIO_AD_B1_11(Gpio1, 27); | ||
| 167 | GPIO_AD_B1_12(Gpio1, 28); | ||
| 168 | GPIO_AD_B1_13(Gpio1, 29); | ||
| 169 | GPIO_AD_B1_14(Gpio1, 30); | ||
| 170 | GPIO_AD_B1_15(Gpio1, 31); | ||
| 171 | |||
| 172 | // GPIO Bank 2 | ||
| 173 | GPIO_B0_00(Gpio2, 0); | ||
| 174 | GPIO_B0_01(Gpio2, 1); | ||
| 175 | GPIO_B0_02(Gpio2, 2); | ||
| 176 | GPIO_B0_03(Gpio2, 3); | ||
| 177 | GPIO_B0_04(Gpio2, 4); | ||
| 178 | GPIO_B0_05(Gpio2, 5); | ||
| 179 | GPIO_B0_06(Gpio2, 6); | ||
| 180 | GPIO_B0_07(Gpio2, 7); | ||
| 181 | GPIO_B0_08(Gpio2, 8); | ||
| 182 | GPIO_B0_09(Gpio2, 9); | ||
| 183 | GPIO_B0_10(Gpio2, 10); | ||
| 184 | GPIO_B0_11(Gpio2, 11); | ||
| 185 | GPIO_B0_12(Gpio2, 12); | ||
| 186 | GPIO_B0_13(Gpio2, 13); | ||
| 187 | GPIO_B0_14(Gpio2, 14); | ||
| 188 | GPIO_B0_15(Gpio2, 15); | ||
| 189 | GPIO_B1_00(Gpio2, 16); | ||
| 190 | GPIO_B1_01(Gpio2, 17); | ||
| 191 | GPIO_B1_02(Gpio2, 18); | ||
| 192 | GPIO_B1_03(Gpio2, 19); | ||
| 193 | GPIO_B1_04(Gpio2, 20); | ||
| 194 | GPIO_B1_05(Gpio2, 21); | ||
| 195 | GPIO_B1_06(Gpio2, 22); | ||
| 196 | GPIO_B1_07(Gpio2, 23); | ||
| 197 | GPIO_B1_08(Gpio2, 24); | ||
| 198 | GPIO_B1_09(Gpio2, 25); | ||
| 199 | GPIO_B1_10(Gpio2, 26); | ||
| 200 | GPIO_B1_11(Gpio2, 27); | ||
| 201 | GPIO_B1_12(Gpio2, 28); | ||
| 202 | GPIO_B1_13(Gpio2, 29); | ||
| 203 | GPIO_B1_14(Gpio2, 30); | ||
| 204 | GPIO_B1_15(Gpio2, 31); | ||
| 205 | |||
| 206 | // GPIO Bank 4 (EMC is 4, then 3) | ||
| 207 | GPIO_EMC_00(Gpio4, 0); | ||
| 208 | GPIO_EMC_01(Gpio4, 1); | ||
| 209 | GPIO_EMC_02(Gpio4, 2); | ||
| 210 | GPIO_EMC_03(Gpio4, 3); | ||
| 211 | GPIO_EMC_04(Gpio4, 4); | ||
| 212 | GPIO_EMC_05(Gpio4, 5); | ||
| 213 | GPIO_EMC_06(Gpio4, 6); | ||
| 214 | GPIO_EMC_07(Gpio4, 7); | ||
| 215 | GPIO_EMC_08(Gpio4, 8); | ||
| 216 | GPIO_EMC_09(Gpio4, 9); | ||
| 217 | GPIO_EMC_10(Gpio4, 10); | ||
| 218 | GPIO_EMC_11(Gpio4, 11); | ||
| 219 | GPIO_EMC_12(Gpio4, 12); | ||
| 220 | GPIO_EMC_13(Gpio4, 13); | ||
| 221 | GPIO_EMC_14(Gpio4, 14); | ||
| 222 | GPIO_EMC_15(Gpio4, 15); | ||
| 223 | GPIO_EMC_16(Gpio4, 16); | ||
| 224 | GPIO_EMC_17(Gpio4, 17); | ||
| 225 | GPIO_EMC_18(Gpio4, 18); | ||
| 226 | GPIO_EMC_19(Gpio4, 19); | ||
| 227 | GPIO_EMC_20(Gpio4, 20); | ||
| 228 | GPIO_EMC_21(Gpio4, 21); | ||
| 229 | GPIO_EMC_22(Gpio4, 22); | ||
| 230 | GPIO_EMC_23(Gpio4, 23); | ||
| 231 | GPIO_EMC_24(Gpio4, 24); | ||
| 232 | GPIO_EMC_25(Gpio4, 25); | ||
| 233 | GPIO_EMC_26(Gpio4, 26); | ||
| 234 | GPIO_EMC_27(Gpio4, 27); | ||
| 235 | GPIO_EMC_28(Gpio4, 28); | ||
| 236 | GPIO_EMC_29(Gpio4, 29); | ||
| 237 | GPIO_EMC_30(Gpio4, 30); | ||
| 238 | GPIO_EMC_31(Gpio4, 31); | ||
| 239 | |||
| 240 | // GPIO Bank 3 | ||
| 241 | GPIO_EMC_32(Gpio3, 18); | ||
| 242 | GPIO_EMC_33(Gpio3, 19); | ||
| 243 | GPIO_EMC_34(Gpio3, 20); | ||
| 244 | GPIO_EMC_35(Gpio3, 21); | ||
| 245 | GPIO_EMC_36(Gpio3, 22); | ||
| 246 | GPIO_EMC_37(Gpio3, 23); | ||
| 247 | GPIO_EMC_38(Gpio3, 24); | ||
| 248 | GPIO_EMC_39(Gpio3, 25); | ||
| 249 | GPIO_EMC_40(Gpio3, 26); | ||
| 250 | GPIO_EMC_41(Gpio3, 27); | ||
| 251 | GPIO_SD_B0_00(Gpio3, 12); | ||
| 252 | GPIO_SD_B0_01(Gpio3, 13); | ||
| 253 | GPIO_SD_B0_02(Gpio3, 14); | ||
| 254 | GPIO_SD_B0_03(Gpio3, 15); | ||
| 255 | GPIO_SD_B0_04(Gpio3, 16); | ||
| 256 | GPIO_SD_B0_05(Gpio3, 17); | ||
| 257 | GPIO_SD_B1_00(Gpio3, 0); | ||
| 258 | GPIO_SD_B1_01(Gpio3, 1); | ||
| 259 | GPIO_SD_B1_02(Gpio3, 2); | ||
| 260 | GPIO_SD_B1_03(Gpio3, 3); | ||
| 261 | GPIO_SD_B1_04(Gpio3, 4); | ||
| 262 | GPIO_SD_B1_05(Gpio3, 5); | ||
| 263 | GPIO_SD_B1_06(Gpio3, 6); | ||
| 264 | GPIO_SD_B1_07(Gpio3, 7); | ||
| 265 | GPIO_SD_B1_08(Gpio3, 8); | ||
| 266 | GPIO_SD_B1_09(Gpio3, 9); | ||
| 267 | GPIO_SD_B1_10(Gpio3, 10); | ||
| 268 | GPIO_SD_B1_11(Gpio3, 11); | ||
| 269 | |||
| 270 | WAKEUP(Gpio5, 0); | ||
| 271 | PMIC_ON_REQ(Gpio5, 1); | ||
| 272 | PMIC_STBY_REQ(Gpio5, 2); | ||
| 273 | } | ||
| 274 | 3 | ||
| 275 | pub(crate) mod _generated { | 4 | pub(crate) mod _generated { |
| 276 | #![allow(dead_code)] | 5 | #![allow(dead_code)] |
| @@ -280,3 +9,5 @@ pub(crate) mod _generated { | |||
| 280 | 9 | ||
| 281 | include!(concat!(env!("OUT_DIR"), "/_generated.rs")); | 10 | include!(concat!(env!("OUT_DIR"), "/_generated.rs")); |
| 282 | } | 11 | } |
| 12 | |||
| 13 | pub use _generated::*; | ||
diff --git a/embassy-nxp/src/dma.rs b/embassy-nxp/src/dma.rs index e2df65fc9..1f479122d 100644 --- a/embassy-nxp/src/dma.rs +++ b/embassy-nxp/src/dma.rs | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | #![macro_use] | ||
| 1 | //! Direct Memory Access (DMA) driver. | 2 | //! Direct Memory Access (DMA) driver. |
| 2 | 3 | ||
| 3 | #[cfg_attr(feature = "lpc55-core0", path = "./dma/lpc55.rs")] | 4 | #[cfg_attr(feature = "lpc55-core0", path = "./dma/lpc55.rs")] |
diff --git a/embassy-nxp/src/dma/lpc55.rs b/embassy-nxp/src/dma/lpc55.rs index 5bd763f03..623644bf1 100644 --- a/embassy-nxp/src/dma/lpc55.rs +++ b/embassy-nxp/src/dma/lpc55.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 1 | use core::cell::RefCell; | 3 | use core::cell::RefCell; |
| 2 | use core::future::Future; | 4 | use core::future::Future; |
| 3 | use core::pin::Pin; | 5 | use core::pin::Pin; |
| @@ -9,9 +11,12 @@ use embassy_hal_internal::interrupt::InterruptExt; | |||
| 9 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; | 11 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; |
| 10 | use embassy_sync::waitqueue::AtomicWaker; | 12 | use embassy_sync::waitqueue::AtomicWaker; |
| 11 | 13 | ||
| 12 | use crate::pac::{DMA0, SYSCON, *}; | 14 | use crate::Peri; |
| 13 | use crate::{Peri, peripherals}; | 15 | #[cfg(feature = "rt")] |
| 16 | use crate::pac::interrupt; | ||
| 17 | use crate::pac::{SYSCON, *}; | ||
| 14 | 18 | ||
| 19 | #[cfg(feature = "rt")] | ||
| 15 | #[interrupt] | 20 | #[interrupt] |
| 16 | fn DMA0() { | 21 | fn DMA0() { |
| 17 | let inta = DMA0.inta0().read().ia(); | 22 | let inta = DMA0.inta0().read().ia(); |
| @@ -278,7 +283,7 @@ static DMA_DESCRIPTORS: Mutex<RefCell<DmaDescriptorTable>> = Mutex::new(RefCell: | |||
| 278 | }; CHANNEL_COUNT], | 283 | }; CHANNEL_COUNT], |
| 279 | })); | 284 | })); |
| 280 | 285 | ||
| 281 | trait SealedChannel {} | 286 | pub(crate) trait SealedChannel {} |
| 282 | trait SealedWord {} | 287 | trait SealedWord {} |
| 283 | 288 | ||
| 284 | /// DMA channel interface. | 289 | /// DMA channel interface. |
| @@ -323,7 +328,7 @@ impl Word for u32 { | |||
| 323 | 328 | ||
| 324 | /// Type erased DMA channel. | 329 | /// Type erased DMA channel. |
| 325 | pub struct AnyChannel { | 330 | pub struct AnyChannel { |
| 326 | number: u8, | 331 | pub(crate) number: u8, |
| 327 | } | 332 | } |
| 328 | 333 | ||
| 329 | impl_peripheral!(AnyChannel); | 334 | impl_peripheral!(AnyChannel); |
| @@ -335,10 +340,10 @@ impl Channel for AnyChannel { | |||
| 335 | } | 340 | } |
| 336 | } | 341 | } |
| 337 | 342 | ||
| 338 | macro_rules! channel { | 343 | macro_rules! impl_dma_channel { |
| 339 | ($name:ident, $num:expr) => { | 344 | ($instance:ident, $name:ident, $num:expr) => { |
| 340 | impl SealedChannel for peripherals::$name {} | 345 | impl crate::dma::SealedChannel for crate::peripherals::$name {} |
| 341 | impl Channel for peripherals::$name { | 346 | impl crate::dma::Channel for crate::peripherals::$name { |
| 342 | fn number(&self) -> u8 { | 347 | fn number(&self) -> u8 { |
| 343 | $num | 348 | $num |
| 344 | } | 349 | } |
| @@ -346,32 +351,10 @@ macro_rules! channel { | |||
| 346 | 351 | ||
| 347 | impl From<peripherals::$name> for crate::dma::AnyChannel { | 352 | impl From<peripherals::$name> for crate::dma::AnyChannel { |
| 348 | fn from(val: peripherals::$name) -> Self { | 353 | fn from(val: peripherals::$name) -> Self { |
| 354 | use crate::dma::Channel; | ||
| 355 | |||
| 349 | Self { number: val.number() } | 356 | Self { number: val.number() } |
| 350 | } | 357 | } |
| 351 | } | 358 | } |
| 352 | }; | 359 | }; |
| 353 | } | 360 | } |
| 354 | |||
| 355 | channel!(DMA_CH0, 0); | ||
| 356 | channel!(DMA_CH1, 1); | ||
| 357 | channel!(DMA_CH2, 2); | ||
| 358 | channel!(DMA_CH3, 3); | ||
| 359 | channel!(DMA_CH4, 4); | ||
| 360 | channel!(DMA_CH5, 5); | ||
| 361 | channel!(DMA_CH6, 6); | ||
| 362 | channel!(DMA_CH7, 7); | ||
| 363 | channel!(DMA_CH8, 8); | ||
| 364 | channel!(DMA_CH9, 9); | ||
| 365 | channel!(DMA_CH10, 10); | ||
| 366 | channel!(DMA_CH11, 11); | ||
| 367 | channel!(DMA_CH12, 12); | ||
| 368 | channel!(DMA_CH13, 13); | ||
| 369 | channel!(DMA_CH14, 14); | ||
| 370 | channel!(DMA_CH15, 15); | ||
| 371 | channel!(DMA_CH16, 16); | ||
| 372 | channel!(DMA_CH17, 17); | ||
| 373 | channel!(DMA_CH18, 18); | ||
| 374 | channel!(DMA_CH19, 19); | ||
| 375 | channel!(DMA_CH20, 20); | ||
| 376 | channel!(DMA_CH21, 21); | ||
| 377 | channel!(DMA_CH22, 22); | ||
diff --git a/embassy-nxp/src/gpio/lpc55.rs b/embassy-nxp/src/gpio/lpc55.rs index 6039d8ca8..6be405463 100644 --- a/embassy-nxp/src/gpio/lpc55.rs +++ b/embassy-nxp/src/gpio/lpc55.rs | |||
| @@ -1,9 +1,11 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 1 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; | 3 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; |
| 2 | 4 | ||
| 5 | use crate::Peri; | ||
| 3 | use crate::pac::common::{RW, Reg}; | 6 | use crate::pac::common::{RW, Reg}; |
| 4 | use crate::pac::iocon::vals::{PioDigimode, PioMode}; | 7 | use crate::pac::iocon::vals::{PioDigimode, PioMode}; |
| 5 | use crate::pac::{GPIO, IOCON, SYSCON, iocon}; | 8 | use crate::pac::{GPIO, IOCON, SYSCON, iocon}; |
| 6 | use crate::{Peri, peripherals}; | ||
| 7 | 9 | ||
| 8 | pub(crate) fn init() { | 10 | pub(crate) fn init() { |
| 9 | // Enable clocks for GPIO, PINT, and IOCON | 11 | // Enable clocks for GPIO, PINT, and IOCON |
| @@ -39,8 +41,8 @@ pub enum Pull { | |||
| 39 | /// The LPC55 boards have two GPIO banks, each with 32 pins. This enum represents the two banks. | 41 | /// The LPC55 boards have two GPIO banks, each with 32 pins. This enum represents the two banks. |
| 40 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | 42 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] |
| 41 | pub enum Bank { | 43 | pub enum Bank { |
| 42 | Bank0 = 0, | 44 | Gpio0 = 0, |
| 43 | Bank1 = 1, | 45 | Gpio1 = 1, |
| 44 | } | 46 | } |
| 45 | 47 | ||
| 46 | /// GPIO output driver. Internally, this is a specialized [Flex] pin. | 48 | /// GPIO output driver. Internally, this is a specialized [Flex] pin. |
| @@ -228,8 +230,8 @@ pub(crate) trait SealedPin: Sized { | |||
| 228 | #[inline] | 230 | #[inline] |
| 229 | fn pio(&self) -> Reg<iocon::regs::Pio, RW> { | 231 | fn pio(&self) -> Reg<iocon::regs::Pio, RW> { |
| 230 | match self.pin_bank() { | 232 | match self.pin_bank() { |
| 231 | Bank::Bank0 => IOCON.pio0(self.pin_number() as usize), | 233 | Bank::Gpio0 => IOCON.pio0(self.pin_number() as usize), |
| 232 | Bank::Bank1 => IOCON.pio1(self.pin_number() as usize), | 234 | Bank::Gpio1 => IOCON.pio1(self.pin_number() as usize), |
| 233 | } | 235 | } |
| 234 | } | 236 | } |
| 235 | } | 237 | } |
| @@ -254,8 +256,8 @@ pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static { | |||
| 254 | 256 | ||
| 255 | /// Type-erased GPIO pin. | 257 | /// Type-erased GPIO pin. |
| 256 | pub struct AnyPin { | 258 | pub struct AnyPin { |
| 257 | pin_bank: Bank, | 259 | pub(crate) pin_bank: Bank, |
| 258 | pin_number: u8, | 260 | pub(crate) pin_number: u8, |
| 259 | } | 261 | } |
| 260 | 262 | ||
| 261 | impl AnyPin { | 263 | impl AnyPin { |
| @@ -285,12 +287,12 @@ impl SealedPin for AnyPin { | |||
| 285 | } | 287 | } |
| 286 | 288 | ||
| 287 | macro_rules! impl_pin { | 289 | macro_rules! impl_pin { |
| 288 | ($name:ident, $bank:expr, $pin_num:expr) => { | 290 | ($name:ident, $bank:ident, $pin_num:expr) => { |
| 289 | impl Pin for peripherals::$name {} | 291 | impl crate::gpio::Pin for peripherals::$name {} |
| 290 | impl SealedPin for peripherals::$name { | 292 | impl crate::gpio::SealedPin for peripherals::$name { |
| 291 | #[inline] | 293 | #[inline] |
| 292 | fn pin_bank(&self) -> Bank { | 294 | fn pin_bank(&self) -> crate::gpio::Bank { |
| 293 | $bank | 295 | crate::gpio::Bank::$bank |
| 294 | } | 296 | } |
| 295 | 297 | ||
| 296 | #[inline] | 298 | #[inline] |
| @@ -301,6 +303,8 @@ macro_rules! impl_pin { | |||
| 301 | 303 | ||
| 302 | impl From<peripherals::$name> for crate::gpio::AnyPin { | 304 | impl From<peripherals::$name> for crate::gpio::AnyPin { |
| 303 | fn from(val: peripherals::$name) -> Self { | 305 | fn from(val: peripherals::$name) -> Self { |
| 306 | use crate::gpio::SealedPin; | ||
| 307 | |||
| 304 | Self { | 308 | Self { |
| 305 | pin_bank: val.pin_bank(), | 309 | pin_bank: val.pin_bank(), |
| 306 | pin_number: val.pin_number(), | 310 | pin_number: val.pin_number(), |
| @@ -309,68 +313,3 @@ macro_rules! impl_pin { | |||
| 309 | } | 313 | } |
| 310 | }; | 314 | }; |
| 311 | } | 315 | } |
| 312 | |||
| 313 | impl_pin!(PIO0_0, Bank::Bank0, 0); | ||
| 314 | impl_pin!(PIO0_1, Bank::Bank0, 1); | ||
| 315 | impl_pin!(PIO0_2, Bank::Bank0, 2); | ||
| 316 | impl_pin!(PIO0_3, Bank::Bank0, 3); | ||
| 317 | impl_pin!(PIO0_4, Bank::Bank0, 4); | ||
| 318 | impl_pin!(PIO0_5, Bank::Bank0, 5); | ||
| 319 | impl_pin!(PIO0_6, Bank::Bank0, 6); | ||
| 320 | impl_pin!(PIO0_7, Bank::Bank0, 7); | ||
| 321 | impl_pin!(PIO0_8, Bank::Bank0, 8); | ||
| 322 | impl_pin!(PIO0_9, Bank::Bank0, 9); | ||
| 323 | impl_pin!(PIO0_10, Bank::Bank0, 10); | ||
| 324 | impl_pin!(PIO0_11, Bank::Bank0, 11); | ||
| 325 | impl_pin!(PIO0_12, Bank::Bank0, 12); | ||
| 326 | impl_pin!(PIO0_13, Bank::Bank0, 13); | ||
| 327 | impl_pin!(PIO0_14, Bank::Bank0, 14); | ||
| 328 | impl_pin!(PIO0_15, Bank::Bank0, 15); | ||
| 329 | impl_pin!(PIO0_16, Bank::Bank0, 16); | ||
| 330 | impl_pin!(PIO0_17, Bank::Bank0, 17); | ||
| 331 | impl_pin!(PIO0_18, Bank::Bank0, 18); | ||
| 332 | impl_pin!(PIO0_19, Bank::Bank0, 19); | ||
| 333 | impl_pin!(PIO0_20, Bank::Bank0, 20); | ||
| 334 | impl_pin!(PIO0_21, Bank::Bank0, 21); | ||
| 335 | impl_pin!(PIO0_22, Bank::Bank0, 22); | ||
| 336 | impl_pin!(PIO0_23, Bank::Bank0, 23); | ||
| 337 | impl_pin!(PIO0_24, Bank::Bank0, 24); | ||
| 338 | impl_pin!(PIO0_25, Bank::Bank0, 25); | ||
| 339 | impl_pin!(PIO0_26, Bank::Bank0, 26); | ||
| 340 | impl_pin!(PIO0_27, Bank::Bank0, 27); | ||
| 341 | impl_pin!(PIO0_28, Bank::Bank0, 28); | ||
| 342 | impl_pin!(PIO0_29, Bank::Bank0, 29); | ||
| 343 | impl_pin!(PIO0_30, Bank::Bank0, 30); | ||
| 344 | impl_pin!(PIO0_31, Bank::Bank0, 31); | ||
| 345 | impl_pin!(PIO1_0, Bank::Bank1, 0); | ||
| 346 | impl_pin!(PIO1_1, Bank::Bank1, 1); | ||
| 347 | impl_pin!(PIO1_2, Bank::Bank1, 2); | ||
| 348 | impl_pin!(PIO1_3, Bank::Bank1, 3); | ||
| 349 | impl_pin!(PIO1_4, Bank::Bank1, 4); | ||
| 350 | impl_pin!(PIO1_5, Bank::Bank1, 5); | ||
| 351 | impl_pin!(PIO1_6, Bank::Bank1, 6); | ||
| 352 | impl_pin!(PIO1_7, Bank::Bank1, 7); | ||
| 353 | impl_pin!(PIO1_8, Bank::Bank1, 8); | ||
| 354 | impl_pin!(PIO1_9, Bank::Bank1, 9); | ||
| 355 | impl_pin!(PIO1_10, Bank::Bank1, 10); | ||
| 356 | impl_pin!(PIO1_11, Bank::Bank1, 11); | ||
| 357 | impl_pin!(PIO1_12, Bank::Bank1, 12); | ||
| 358 | impl_pin!(PIO1_13, Bank::Bank1, 13); | ||
| 359 | impl_pin!(PIO1_14, Bank::Bank1, 14); | ||
| 360 | impl_pin!(PIO1_15, Bank::Bank1, 15); | ||
| 361 | impl_pin!(PIO1_16, Bank::Bank1, 16); | ||
| 362 | impl_pin!(PIO1_17, Bank::Bank1, 17); | ||
| 363 | impl_pin!(PIO1_18, Bank::Bank1, 18); | ||
| 364 | impl_pin!(PIO1_19, Bank::Bank1, 19); | ||
| 365 | impl_pin!(PIO1_20, Bank::Bank1, 20); | ||
| 366 | impl_pin!(PIO1_21, Bank::Bank1, 21); | ||
| 367 | impl_pin!(PIO1_22, Bank::Bank1, 22); | ||
| 368 | impl_pin!(PIO1_23, Bank::Bank1, 23); | ||
| 369 | impl_pin!(PIO1_24, Bank::Bank1, 24); | ||
| 370 | impl_pin!(PIO1_25, Bank::Bank1, 25); | ||
| 371 | impl_pin!(PIO1_26, Bank::Bank1, 26); | ||
| 372 | impl_pin!(PIO1_27, Bank::Bank1, 27); | ||
| 373 | impl_pin!(PIO1_28, Bank::Bank1, 28); | ||
| 374 | impl_pin!(PIO1_29, Bank::Bank1, 29); | ||
| 375 | impl_pin!(PIO1_30, Bank::Bank1, 30); | ||
| 376 | impl_pin!(PIO1_31, Bank::Bank1, 31); | ||
diff --git a/embassy-nxp/src/gpio/rt1xxx.rs b/embassy-nxp/src/gpio/rt1xxx.rs index c4dc110ff..8a560310c 100644 --- a/embassy-nxp/src/gpio/rt1xxx.rs +++ b/embassy-nxp/src/gpio/rt1xxx.rs | |||
| @@ -10,7 +10,7 @@ use embassy_sync::waitqueue::AtomicWaker; | |||
| 10 | use nxp_pac::gpio::vals::Icr; | 10 | use nxp_pac::gpio::vals::Icr; |
| 11 | use nxp_pac::iomuxc::vals::Pus; | 11 | use nxp_pac::iomuxc::vals::Pus; |
| 12 | 12 | ||
| 13 | use crate::chip::{mux_address, pad_address}; | 13 | use crate::chip::{iomuxc_mux, iomuxc_pad}; |
| 14 | use crate::pac::common::{RW, Reg}; | 14 | use crate::pac::common::{RW, Reg}; |
| 15 | use crate::pac::gpio::Gpio; | 15 | use crate::pac::gpio::Gpio; |
| 16 | #[cfg(feature = "rt")] | 16 | #[cfg(feature = "rt")] |
| @@ -121,6 +121,10 @@ pub enum Bank { | |||
| 121 | /// Bank 5 | 121 | /// Bank 5 |
| 122 | #[cfg(gpio5)] | 122 | #[cfg(gpio5)] |
| 123 | Gpio5, | 123 | Gpio5, |
| 124 | |||
| 125 | #[cfg(gpio10)] | ||
| 126 | /// Bank 10 | ||
| 127 | Gpio10, | ||
| 124 | } | 128 | } |
| 125 | 129 | ||
| 126 | /// GPIO flexible pin. | 130 | /// GPIO flexible pin. |
| @@ -656,6 +660,8 @@ static GPIO3_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; | |||
| 656 | static GPIO4_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; | 660 | static GPIO4_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; |
| 657 | #[cfg(gpio5)] | 661 | #[cfg(gpio5)] |
| 658 | static GPIO5_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; | 662 | static GPIO5_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; |
| 663 | #[cfg(gpio10)] | ||
| 664 | static GPIO10_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; | ||
| 659 | 665 | ||
| 660 | /// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate. | 666 | /// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate. |
| 661 | pub(crate) trait SealedPin: Sized { | 667 | pub(crate) trait SealedPin: Sized { |
| @@ -676,13 +682,15 @@ pub(crate) trait SealedPin: Sized { | |||
| 676 | Bank::Gpio4 => pac::GPIO4, | 682 | Bank::Gpio4 => pac::GPIO4, |
| 677 | #[cfg(gpio5)] | 683 | #[cfg(gpio5)] |
| 678 | Bank::Gpio5 => pac::GPIO5, | 684 | Bank::Gpio5 => pac::GPIO5, |
| 685 | #[cfg(gpio10)] | ||
| 686 | Bank::Gpio10 => pac::GPIO10, | ||
| 679 | } | 687 | } |
| 680 | } | 688 | } |
| 681 | 689 | ||
| 682 | #[inline] | 690 | #[inline] |
| 683 | fn mux(&self) -> Reg<MuxCtl, RW> { | 691 | fn mux(&self) -> Reg<MuxCtl, RW> { |
| 684 | // SAFETY: The generated mux address table is valid since it is generated from the SVD files. | 692 | // SAFETY: The generated mux address table is valid since it is generated from the SVD files. |
| 685 | let address = unsafe { mux_address(self._bank(), self.pin_number()).unwrap_unchecked() }; | 693 | let address = unsafe { iomuxc_mux(self._bank(), self.pin_number()).unwrap_unchecked() }; |
| 686 | 694 | ||
| 687 | // SAFETY: The register at the address is an instance of MuxCtl. | 695 | // SAFETY: The register at the address is an instance of MuxCtl. |
| 688 | unsafe { Reg::from_ptr(address as *mut _) } | 696 | unsafe { Reg::from_ptr(address as *mut _) } |
| @@ -690,8 +698,7 @@ pub(crate) trait SealedPin: Sized { | |||
| 690 | 698 | ||
| 691 | #[inline] | 699 | #[inline] |
| 692 | fn pad(&self) -> Reg<Ctl, RW> { | 700 | fn pad(&self) -> Reg<Ctl, RW> { |
| 693 | // SAFETY: The generated pad address table is valid since it is generated from the SVD files. | 701 | let address = iomuxc_pad(self._bank(), self.pin_number()); |
| 694 | let address = unsafe { pad_address(self._bank(), self.pin_number()).unwrap_unchecked() }; | ||
| 695 | 702 | ||
| 696 | // SAFETY: The register at the address is an instance of Ctl. | 703 | // SAFETY: The register at the address is an instance of Ctl. |
| 697 | unsafe { Reg::from_ptr(address as *mut _) } | 704 | unsafe { Reg::from_ptr(address as *mut _) } |
| @@ -709,6 +716,8 @@ pub(crate) trait SealedPin: Sized { | |||
| 709 | Bank::Gpio4 => &GPIO4_WAKERS[self.pin_number() as usize], | 716 | Bank::Gpio4 => &GPIO4_WAKERS[self.pin_number() as usize], |
| 710 | #[cfg(gpio5)] | 717 | #[cfg(gpio5)] |
| 711 | Bank::Gpio5 => &GPIO5_WAKERS[self.pin_number() as usize], | 718 | Bank::Gpio5 => &GPIO5_WAKERS[self.pin_number() as usize], |
| 719 | #[cfg(gpio10)] | ||
| 720 | Bank::Gpio10 => &GPIO10_WAKERS[self.pin_number() as usize], | ||
| 712 | } | 721 | } |
| 713 | } | 722 | } |
| 714 | } | 723 | } |
| @@ -793,39 +802,6 @@ impl<'d> Future for InputFuture<'d> { | |||
| 793 | } | 802 | } |
| 794 | } | 803 | } |
| 795 | 804 | ||
| 796 | /// A macro to generate all GPIO pins. | ||
| 797 | /// | ||
| 798 | /// This generates a lookup table for IOMUX register addresses. | ||
| 799 | macro_rules! impl_gpio { | ||
| 800 | ( | ||
| 801 | $($name: ident($bank: ident, $pin_number: expr);)* | ||
| 802 | ) => { | ||
| 803 | #[inline] | ||
| 804 | pub(crate) const fn pad_address(bank: crate::gpio::Bank, pin: u8) -> Option<u32> { | ||
| 805 | match (bank, pin) { | ||
| 806 | $( | ||
| 807 | (crate::gpio::Bank::$bank, $pin_number) => Some(crate::chip::_generated::iomuxc::pads::$name), | ||
| 808 | )* | ||
| 809 | _ => None | ||
| 810 | } | ||
| 811 | } | ||
| 812 | |||
| 813 | #[inline] | ||
| 814 | pub(crate) const fn mux_address(bank: crate::gpio::Bank, pin: u8) -> Option<u32> { | ||
| 815 | match (bank, pin) { | ||
| 816 | $( | ||
| 817 | (crate::gpio::Bank::$bank, $pin_number) => Some(crate::chip::_generated::iomuxc::muxes::$name), | ||
| 818 | )* | ||
| 819 | _ => None | ||
| 820 | } | ||
| 821 | } | ||
| 822 | |||
| 823 | $( | ||
| 824 | impl_pin!($name, $bank, $pin_number); | ||
| 825 | )* | ||
| 826 | }; | ||
| 827 | } | ||
| 828 | |||
| 829 | macro_rules! impl_pin { | 805 | macro_rules! impl_pin { |
| 830 | ($name: ident, $bank: ident, $pin_num: expr) => { | 806 | ($name: ident, $bank: ident, $pin_num: expr) => { |
| 831 | impl crate::gpio::Pin for crate::peripherals::$name {} | 807 | impl crate::gpio::Pin for crate::peripherals::$name {} |
diff --git a/embassy-nxp/src/iomuxc.rs b/embassy-nxp/src/iomuxc.rs new file mode 100644 index 000000000..c015ecbc2 --- /dev/null +++ b/embassy-nxp/src/iomuxc.rs | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 3 | /// An IOMUXC pad. | ||
| 4 | /// | ||
| 5 | /// This trait does not imply that GPIO can be used with this pad. [`Pin`](crate::gpio::Pin) must | ||
| 6 | /// also be implemented for GPIO. | ||
| 7 | #[allow(private_bounds)] | ||
| 8 | pub trait Pad: SealedPad {} | ||
| 9 | |||
| 10 | pub(crate) trait SealedPad { | ||
| 11 | /// Address of the pad register for this pad. | ||
| 12 | const PAD: *mut (); | ||
| 13 | |||
| 14 | /// Address of the mux register for this pad. | ||
| 15 | /// | ||
| 16 | /// Some pads do not allow muxing (e.g. ONOFF). | ||
| 17 | const MUX: Option<*mut ()>; | ||
| 18 | } | ||
| 19 | |||
| 20 | macro_rules! impl_iomuxc_pad { | ||
| 21 | ($name: ident, $pad: expr, $mux: expr) => { | ||
| 22 | impl crate::iomuxc::SealedPad for crate::peripherals::$name { | ||
| 23 | const PAD: *mut () = $pad as *mut (); | ||
| 24 | const MUX: Option<*mut ()> = Some($mux as *mut ()); | ||
| 25 | } | ||
| 26 | |||
| 27 | impl crate::iomuxc::Pad for crate::peripherals::$name {} | ||
| 28 | }; | ||
| 29 | } | ||
diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs index 4058881a5..4c3dbebb9 100644 --- a/embassy-nxp/src/lib.rs +++ b/embassy-nxp/src/lib.rs | |||
| @@ -12,8 +12,13 @@ pub mod pint; | |||
| 12 | #[cfg(feature = "lpc55-core0")] | 12 | #[cfg(feature = "lpc55-core0")] |
| 13 | pub mod pwm; | 13 | pub mod pwm; |
| 14 | #[cfg(feature = "lpc55-core0")] | 14 | #[cfg(feature = "lpc55-core0")] |
| 15 | pub mod sct; | ||
| 16 | #[cfg(feature = "lpc55-core0")] | ||
| 15 | pub mod usart; | 17 | pub mod usart; |
| 16 | 18 | ||
| 19 | #[cfg(rt1xxx)] | ||
| 20 | mod iomuxc; | ||
| 21 | |||
| 17 | #[cfg(feature = "_time_driver")] | 22 | #[cfg(feature = "_time_driver")] |
| 18 | #[cfg_attr(feature = "time-driver-pit", path = "time_driver/pit.rs")] | 23 | #[cfg_attr(feature = "time-driver-pit", path = "time_driver/pit.rs")] |
| 19 | #[cfg_attr(feature = "time-driver-rtc", path = "time_driver/rtc.rs")] | 24 | #[cfg_attr(feature = "time-driver-rtc", path = "time_driver/rtc.rs")] |
| @@ -25,15 +30,12 @@ mod time_driver; | |||
| 25 | #[cfg_attr(feature = "mimxrt1062", path = "chips/mimxrt1062.rs")] | 30 | #[cfg_attr(feature = "mimxrt1062", path = "chips/mimxrt1062.rs")] |
| 26 | mod chip; | 31 | mod chip; |
| 27 | 32 | ||
| 28 | // TODO: Remove when this module is implemented for other chips | 33 | pub use chip::{Peripherals, interrupt, peripherals}; |
| 29 | #[cfg(feature = "lpc55-core0")] | 34 | pub use embassy_hal_internal::{Peri, PeripheralType}; |
| 30 | pub use chip::interrupt; | ||
| 31 | #[cfg(feature = "unstable-pac")] | 35 | #[cfg(feature = "unstable-pac")] |
| 32 | pub use chip::pac; | 36 | pub use nxp_pac as pac; |
| 33 | #[cfg(not(feature = "unstable-pac"))] | 37 | #[cfg(not(feature = "unstable-pac"))] |
| 34 | pub(crate) use chip::pac; | 38 | pub(crate) use nxp_pac as pac; |
| 35 | pub use chip::{Peripherals, peripherals}; | ||
| 36 | pub use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 37 | 39 | ||
| 38 | /// Macro to bind interrupts to handlers. | 40 | /// Macro to bind interrupts to handlers. |
| 39 | /// (Copied from `embassy-rp`) | 41 | /// (Copied from `embassy-rp`) |
diff --git a/embassy-nxp/src/pwm.rs b/embassy-nxp/src/pwm.rs index 68980924a..c87a39c34 100644 --- a/embassy-nxp/src/pwm.rs +++ b/embassy-nxp/src/pwm.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 1 | //! Pulse-Width Modulation (PWM) driver. | 3 | //! Pulse-Width Modulation (PWM) driver. |
| 2 | 4 | ||
| 3 | #[cfg_attr(feature = "lpc55-core0", path = "./pwm/lpc55.rs")] | 5 | #[cfg_attr(feature = "lpc55-core0", path = "./pwm/lpc55.rs")] |
diff --git a/embassy-nxp/src/pwm/lpc55.rs b/embassy-nxp/src/pwm/lpc55.rs index 197184ad6..4cdbd8526 100644 --- a/embassy-nxp/src/pwm/lpc55.rs +++ b/embassy-nxp/src/pwm/lpc55.rs | |||
| @@ -1,12 +1,15 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 1 | use core::sync::atomic::{AtomicU8, AtomicU32, Ordering}; | 3 | use core::sync::atomic::{AtomicU8, AtomicU32, Ordering}; |
| 2 | 4 | ||
| 3 | use embassy_hal_internal::{Peri, PeripheralType}; | 5 | use embassy_hal_internal::Peri; |
| 4 | 6 | ||
| 5 | use crate::gpio::AnyPin; | 7 | use crate::gpio::AnyPin; |
| 6 | use crate::pac::iocon::vals::{PioDigimode, PioFunc, PioMode, PioOd, PioSlew}; | 8 | use crate::pac::iocon::vals::{PioDigimode, PioMode, PioOd, PioSlew}; |
| 7 | use crate::pac::sct0::vals; | 9 | use crate::pac::sct0::vals; |
| 8 | use crate::pac::syscon::vals::{SctRst, SctclkselSel}; | 10 | use crate::pac::syscon::vals::{SctRst, SctclkselSel}; |
| 9 | use crate::pac::{SCT0, SYSCON}; | 11 | use crate::pac::{SCT0, SYSCON}; |
| 12 | use crate::sct; | ||
| 10 | 13 | ||
| 11 | // Since for now the counter is shared, the TOP value has to be kept. | 14 | // Since for now the counter is shared, the TOP value has to be kept. |
| 12 | static TOP_VALUE: AtomicU32 = AtomicU32::new(0); | 15 | static TOP_VALUE: AtomicU32 = AtomicU32::new(0); |
| @@ -75,7 +78,11 @@ impl<'d> Pwm<'d> { | |||
| 75 | SYSCON.presetctrl1().modify(|w| w.set_sct_rst(SctRst::ASSERTED)); | 78 | SYSCON.presetctrl1().modify(|w| w.set_sct_rst(SctRst::ASSERTED)); |
| 76 | SYSCON.presetctrl1().modify(|w| w.set_sct_rst(SctRst::RELEASED)); | 79 | SYSCON.presetctrl1().modify(|w| w.set_sct_rst(SctRst::RELEASED)); |
| 77 | } | 80 | } |
| 78 | fn new_inner<T: Output>(output: usize, channel: Peri<'d, impl OutputChannelPin<T>>, config: Config) -> Self { | 81 | fn new_inner<T: sct::Instance, O: sct::Output<T>>( |
| 82 | output: usize, | ||
| 83 | channel: Peri<'d, impl sct::OutputPin<T, O>>, | ||
| 84 | config: Config, | ||
| 85 | ) -> Self { | ||
| 79 | // Enable clocks (Syscon is enabled by default) | 86 | // Enable clocks (Syscon is enabled by default) |
| 80 | critical_section::with(|_cs| { | 87 | critical_section::with(|_cs| { |
| 81 | if !SYSCON.ahbclkctrl0().read().iocon() { | 88 | if !SYSCON.ahbclkctrl0().read().iocon() { |
| @@ -109,12 +116,12 @@ impl<'d> Pwm<'d> { | |||
| 109 | 116 | ||
| 110 | /// Create PWM driver with a single 'a' pin as output. | 117 | /// Create PWM driver with a single 'a' pin as output. |
| 111 | #[inline] | 118 | #[inline] |
| 112 | pub fn new_output<T: Output>( | 119 | pub fn new_output<T: sct::Instance, O: sct::Output<T>>( |
| 113 | output: Peri<'d, T>, | 120 | output: Peri<'d, O>, |
| 114 | channel: Peri<'d, impl OutputChannelPin<T>>, | 121 | channel: Peri<'d, impl sct::OutputPin<T, O>>, |
| 115 | config: Config, | 122 | config: Config, |
| 116 | ) -> Self { | 123 | ) -> Self { |
| 117 | Self::new_inner(output.number(), channel, config) | 124 | Self::new_inner::<T, O>(output.number(), channel, config) |
| 118 | } | 125 | } |
| 119 | 126 | ||
| 120 | /// Set the PWM config. | 127 | /// Set the PWM config. |
| @@ -196,18 +203,18 @@ impl<'d> Pwm<'d> { | |||
| 196 | // TODO(frihetselsker): optimize nxp-pac so that `set_clr` and `set_set` are turned into a bit array. | 203 | // TODO(frihetselsker): optimize nxp-pac so that `set_clr` and `set_set` are turned into a bit array. |
| 197 | if config.invert { | 204 | if config.invert { |
| 198 | // Low when event 0 is active | 205 | // Low when event 0 is active |
| 199 | SCT0.out(output_number).out_clr().modify(|w| w.set_clr(1 << 0)); | 206 | SCT0.out(output_number).out_clr().modify(|w| w.set_clr(0, true)); |
| 200 | // High when event `output_number + 1` is active | 207 | // High when event `output_number + 1` is active |
| 201 | SCT0.out(output_number) | 208 | SCT0.out(output_number) |
| 202 | .out_set() | 209 | .out_set() |
| 203 | .modify(|w| w.set_set(1 << (output_number + 1))); | 210 | .modify(|w| w.set_set(output_number, true)); |
| 204 | } else { | 211 | } else { |
| 205 | // High when event 0 is active | 212 | // High when event 0 is active |
| 206 | SCT0.out(output_number).out_set().modify(|w| w.set_set(1 << 0)); | 213 | SCT0.out(output_number).out_set().modify(|w| w.set_set(0, true)); |
| 207 | // Low when event `output_number + 1` is active | 214 | // Low when event `output_number + 1` is active |
| 208 | SCT0.out(output_number) | 215 | SCT0.out(output_number) |
| 209 | .out_clr() | 216 | .out_clr() |
| 210 | .modify(|w| w.set_clr(1 << (output_number + 1))); | 217 | .modify(|w| w.set_clr(output_number, true)); |
| 211 | } | 218 | } |
| 212 | 219 | ||
| 213 | if config.phase_correct { | 220 | if config.phase_correct { |
| @@ -239,87 +246,3 @@ impl<'d> Drop for Pwm<'d> { | |||
| 239 | } | 246 | } |
| 240 | } | 247 | } |
| 241 | } | 248 | } |
| 242 | |||
| 243 | trait SealedOutput { | ||
| 244 | /// Output number. | ||
| 245 | fn number(&self) -> usize; | ||
| 246 | } | ||
| 247 | |||
| 248 | /// PWM Output. | ||
| 249 | #[allow(private_bounds)] | ||
| 250 | pub trait Output: PeripheralType + SealedOutput {} | ||
| 251 | |||
| 252 | macro_rules! output { | ||
| 253 | ($name:ident, $num:expr) => { | ||
| 254 | impl SealedOutput for crate::peripherals::$name { | ||
| 255 | fn number(&self) -> usize { | ||
| 256 | $num | ||
| 257 | } | ||
| 258 | } | ||
| 259 | impl Output for crate::peripherals::$name {} | ||
| 260 | }; | ||
| 261 | } | ||
| 262 | |||
| 263 | output!(PWM_OUTPUT0, 0); | ||
| 264 | output!(PWM_OUTPUT1, 1); | ||
| 265 | output!(PWM_OUTPUT2, 2); | ||
| 266 | output!(PWM_OUTPUT3, 3); | ||
| 267 | output!(PWM_OUTPUT4, 4); | ||
| 268 | output!(PWM_OUTPUT5, 5); | ||
| 269 | output!(PWM_OUTPUT6, 6); | ||
| 270 | output!(PWM_OUTPUT7, 7); | ||
| 271 | output!(PWM_OUTPUT8, 8); | ||
| 272 | output!(PWM_OUTPUT9, 9); | ||
| 273 | |||
| 274 | /// PWM Output Channel. | ||
| 275 | pub trait OutputChannelPin<T: Output>: crate::gpio::Pin { | ||
| 276 | fn pin_func(&self) -> PioFunc; | ||
| 277 | } | ||
| 278 | |||
| 279 | macro_rules! impl_pin { | ||
| 280 | ($pin:ident, $output:ident, $func:ident) => { | ||
| 281 | impl crate::pwm::inner::OutputChannelPin<crate::peripherals::$output> for crate::peripherals::$pin { | ||
| 282 | fn pin_func(&self) -> PioFunc { | ||
| 283 | PioFunc::$func | ||
| 284 | } | ||
| 285 | } | ||
| 286 | }; | ||
| 287 | } | ||
| 288 | |||
| 289 | impl_pin!(PIO0_2, PWM_OUTPUT0, ALT3); | ||
| 290 | impl_pin!(PIO0_17, PWM_OUTPUT0, ALT4); | ||
| 291 | impl_pin!(PIO1_4, PWM_OUTPUT0, ALT4); | ||
| 292 | impl_pin!(PIO1_23, PWM_OUTPUT0, ALT2); | ||
| 293 | |||
| 294 | impl_pin!(PIO0_3, PWM_OUTPUT1, ALT3); | ||
| 295 | impl_pin!(PIO0_18, PWM_OUTPUT1, ALT4); | ||
| 296 | impl_pin!(PIO1_8, PWM_OUTPUT1, ALT4); | ||
| 297 | impl_pin!(PIO1_24, PWM_OUTPUT1, ALT2); | ||
| 298 | |||
| 299 | impl_pin!(PIO0_10, PWM_OUTPUT2, ALT5); | ||
| 300 | impl_pin!(PIO0_15, PWM_OUTPUT2, ALT4); | ||
| 301 | impl_pin!(PIO0_19, PWM_OUTPUT2, ALT4); | ||
| 302 | impl_pin!(PIO1_9, PWM_OUTPUT2, ALT4); | ||
| 303 | impl_pin!(PIO1_25, PWM_OUTPUT2, ALT2); | ||
| 304 | |||
| 305 | impl_pin!(PIO0_22, PWM_OUTPUT3, ALT4); | ||
| 306 | impl_pin!(PIO0_31, PWM_OUTPUT3, ALT4); | ||
| 307 | impl_pin!(PIO1_10, PWM_OUTPUT3, ALT4); | ||
| 308 | impl_pin!(PIO1_26, PWM_OUTPUT3, ALT2); | ||
| 309 | |||
| 310 | impl_pin!(PIO0_23, PWM_OUTPUT4, ALT4); | ||
| 311 | impl_pin!(PIO1_3, PWM_OUTPUT4, ALT4); | ||
| 312 | impl_pin!(PIO1_17, PWM_OUTPUT4, ALT4); | ||
| 313 | |||
| 314 | impl_pin!(PIO0_26, PWM_OUTPUT5, ALT4); | ||
| 315 | impl_pin!(PIO1_18, PWM_OUTPUT5, ALT4); | ||
| 316 | |||
| 317 | impl_pin!(PIO0_27, PWM_OUTPUT6, ALT4); | ||
| 318 | impl_pin!(PIO1_31, PWM_OUTPUT6, ALT4); | ||
| 319 | |||
| 320 | impl_pin!(PIO0_28, PWM_OUTPUT7, ALT4); | ||
| 321 | impl_pin!(PIO1_19, PWM_OUTPUT7, ALT2); | ||
| 322 | |||
| 323 | impl_pin!(PIO0_29, PWM_OUTPUT8, ALT4); | ||
| 324 | |||
| 325 | impl_pin!(PIO0_30, PWM_OUTPUT9, ALT4); | ||
diff --git a/embassy-nxp/src/sct.rs b/embassy-nxp/src/sct.rs new file mode 100644 index 000000000..b6b0e35a9 --- /dev/null +++ b/embassy-nxp/src/sct.rs | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 3 | use embassy_hal_internal::PeripheralType; | ||
| 4 | use nxp_pac::iocon::vals::PioFunc; | ||
| 5 | |||
| 6 | use crate::gpio; | ||
| 7 | |||
| 8 | /// SCT instance. | ||
| 9 | #[allow(private_bounds)] | ||
| 10 | pub trait Instance: SealedInstance + PeripheralType {} | ||
| 11 | |||
| 12 | pub(crate) trait SealedInstance {} | ||
| 13 | |||
| 14 | /// An SCT output. | ||
| 15 | #[allow(private_bounds)] | ||
| 16 | pub trait Output<T: Instance>: SealedOutput + PeripheralType {} | ||
| 17 | |||
| 18 | pub(crate) trait SealedOutput { | ||
| 19 | /// Output number. | ||
| 20 | fn number(&self) -> usize; | ||
| 21 | } | ||
| 22 | |||
| 23 | /// An SCT output capable pin. | ||
| 24 | pub trait OutputPin<T: Instance, O: Output<T>>: gpio::Pin { | ||
| 25 | fn pin_func(&self) -> PioFunc; | ||
| 26 | } | ||
| 27 | |||
| 28 | macro_rules! impl_sct_instance { | ||
| 29 | ($instance: ident) => { | ||
| 30 | impl crate::sct::SealedInstance for crate::peripherals::$instance {} | ||
| 31 | impl crate::sct::Instance for crate::peripherals::$instance {} | ||
| 32 | }; | ||
| 33 | } | ||
| 34 | |||
| 35 | macro_rules! impl_sct_output_instance { | ||
| 36 | ($instance: ident, $name: ident, $num: expr) => { | ||
| 37 | impl crate::sct::SealedOutput for crate::peripherals::$name { | ||
| 38 | fn number(&self) -> usize { | ||
| 39 | $num as usize | ||
| 40 | } | ||
| 41 | } | ||
| 42 | impl crate::sct::Output<crate::peripherals::$instance> for crate::peripherals::$name {} | ||
| 43 | }; | ||
| 44 | } | ||
| 45 | |||
| 46 | macro_rules! impl_sct_output_pin { | ||
| 47 | ($instance: ident, $output_instance: ident, $pin: ident, $alt: ident) => { | ||
| 48 | impl crate::sct::OutputPin<crate::peripherals::$instance, crate::peripherals::$output_instance> | ||
| 49 | for crate::peripherals::$pin | ||
| 50 | { | ||
| 51 | fn pin_func(&self) -> crate::pac::iocon::vals::PioFunc { | ||
| 52 | crate::pac::iocon::vals::PioFunc::$alt | ||
| 53 | } | ||
| 54 | } | ||
| 55 | }; | ||
| 56 | } | ||
diff --git a/embassy-nxp/src/usart.rs b/embassy-nxp/src/usart.rs index 1d8886f24..af039dee4 100644 --- a/embassy-nxp/src/usart.rs +++ b/embassy-nxp/src/usart.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 1 | //! Universal Synchronous/Asynchronous Receiver/Transmitter (USART) driver. | 3 | //! Universal Synchronous/Asynchronous Receiver/Transmitter (USART) driver. |
| 2 | 4 | ||
| 3 | #[cfg_attr(feature = "lpc55-core0", path = "./usart/lpc55.rs")] | 5 | #[cfg_attr(feature = "lpc55-core0", path = "./usart/lpc55.rs")] |
diff --git a/embassy-nxp/src/usart/lpc55.rs b/embassy-nxp/src/usart/lpc55.rs index d54927b25..d77f08fd8 100644 --- a/embassy-nxp/src/usart/lpc55.rs +++ b/embassy-nxp/src/usart/lpc55.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 1 | use core::fmt::Debug; | 3 | use core::fmt::Debug; |
| 2 | use core::future::poll_fn; | 4 | use core::future::poll_fn; |
| 3 | use core::marker::PhantomData; | 5 | use core::marker::PhantomData; |
| @@ -13,7 +15,7 @@ use embedded_io::{self, ErrorKind}; | |||
| 13 | use crate::dma::{AnyChannel, Channel}; | 15 | use crate::dma::{AnyChannel, Channel}; |
| 14 | use crate::gpio::{AnyPin, SealedPin}; | 16 | use crate::gpio::{AnyPin, SealedPin}; |
| 15 | use crate::interrupt::Interrupt; | 17 | use crate::interrupt::Interrupt; |
| 16 | use crate::interrupt::typelevel::{Binding, Interrupt as _}; | 18 | use crate::interrupt::typelevel::Binding; |
| 17 | use crate::pac::flexcomm::Flexcomm as FlexcommReg; | 19 | use crate::pac::flexcomm::Flexcomm as FlexcommReg; |
| 18 | use crate::pac::iocon::vals::PioFunc; | 20 | use crate::pac::iocon::vals::PioFunc; |
| 19 | use crate::pac::usart::Usart as UsartReg; | 21 | use crate::pac::usart::Usart as UsartReg; |
| @@ -113,8 +115,8 @@ impl Default for Config { | |||
| 113 | 115 | ||
| 114 | /// Internal DMA state of UART RX. | 116 | /// Internal DMA state of UART RX. |
| 115 | pub struct DmaState { | 117 | pub struct DmaState { |
| 116 | rx_err_waker: AtomicWaker, | 118 | pub(crate) rx_err_waker: AtomicWaker, |
| 117 | rx_err: AtomicBool, | 119 | pub(crate) rx_err: AtomicBool, |
| 118 | } | 120 | } |
| 119 | 121 | ||
| 120 | /// # Type parameters | 122 | /// # Type parameters |
| @@ -818,13 +820,13 @@ impl<'d> embedded_io::Read for Usart<'d, Blocking> { | |||
| 818 | } | 820 | } |
| 819 | } | 821 | } |
| 820 | 822 | ||
| 821 | struct Info { | 823 | pub(crate) struct Info { |
| 822 | usart_reg: UsartReg, | 824 | pub(crate) usart_reg: UsartReg, |
| 823 | fc_reg: FlexcommReg, | 825 | pub(crate) fc_reg: FlexcommReg, |
| 824 | interrupt: Interrupt, | 826 | pub(crate) interrupt: Interrupt, |
| 825 | } | 827 | } |
| 826 | 828 | ||
| 827 | trait SealedInstance { | 829 | pub(crate) trait SealedInstance { |
| 828 | fn info() -> &'static Info; | 830 | fn info() -> &'static Info; |
| 829 | fn dma_state() -> &'static DmaState; | 831 | fn dma_state() -> &'static DmaState; |
| 830 | fn instance_number() -> usize; | 832 | fn instance_number() -> usize; |
| @@ -837,10 +839,13 @@ pub trait Instance: SealedInstance + PeripheralType { | |||
| 837 | type Interrupt: crate::interrupt::typelevel::Interrupt; | 839 | type Interrupt: crate::interrupt::typelevel::Interrupt; |
| 838 | } | 840 | } |
| 839 | 841 | ||
| 840 | macro_rules! impl_instance { | 842 | macro_rules! impl_usart_instance { |
| 841 | ($inst:ident, $fc:ident, $fc_num:expr) => { | 843 | ($inst:ident, $fc:ident, $fc_num:expr) => { |
| 842 | impl $crate::usart::inner::SealedInstance for $crate::peripherals::$inst { | 844 | impl crate::usart::SealedInstance for $crate::peripherals::$inst { |
| 843 | fn info() -> &'static Info { | 845 | fn info() -> &'static crate::usart::Info { |
| 846 | use crate::interrupt::typelevel::Interrupt; | ||
| 847 | use crate::usart::Info; | ||
| 848 | |||
| 844 | static INFO: Info = Info { | 849 | static INFO: Info = Info { |
| 845 | usart_reg: crate::pac::$inst, | 850 | usart_reg: crate::pac::$inst, |
| 846 | fc_reg: crate::pac::$fc, | 851 | fc_reg: crate::pac::$fc, |
| @@ -849,7 +854,13 @@ macro_rules! impl_instance { | |||
| 849 | &INFO | 854 | &INFO |
| 850 | } | 855 | } |
| 851 | 856 | ||
| 852 | fn dma_state() -> &'static DmaState { | 857 | fn dma_state() -> &'static crate::usart::DmaState { |
| 858 | use core::sync::atomic::AtomicBool; | ||
| 859 | |||
| 860 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 861 | |||
| 862 | use crate::usart::DmaState; | ||
| 863 | |||
| 853 | static STATE: DmaState = DmaState { | 864 | static STATE: DmaState = DmaState { |
| 854 | rx_err_waker: AtomicWaker::new(), | 865 | rx_err_waker: AtomicWaker::new(), |
| 855 | rx_err: AtomicBool::new(false), | 866 | rx_err: AtomicBool::new(false), |
| @@ -867,15 +878,6 @@ macro_rules! impl_instance { | |||
| 867 | }; | 878 | }; |
| 868 | } | 879 | } |
| 869 | 880 | ||
| 870 | impl_instance!(USART0, FLEXCOMM0, 0); | ||
| 871 | impl_instance!(USART1, FLEXCOMM1, 1); | ||
| 872 | impl_instance!(USART2, FLEXCOMM2, 2); | ||
| 873 | impl_instance!(USART3, FLEXCOMM3, 3); | ||
| 874 | impl_instance!(USART4, FLEXCOMM4, 4); | ||
| 875 | impl_instance!(USART5, FLEXCOMM5, 5); | ||
| 876 | impl_instance!(USART6, FLEXCOMM6, 6); | ||
| 877 | impl_instance!(USART7, FLEXCOMM7, 7); | ||
| 878 | |||
| 879 | pub(crate) trait SealedTxPin<T: Instance>: crate::gpio::Pin { | 881 | pub(crate) trait SealedTxPin<T: Instance>: crate::gpio::Pin { |
| 880 | fn pin_func(&self) -> PioFunc; | 882 | fn pin_func(&self) -> PioFunc; |
| 881 | } | 883 | } |
| @@ -892,75 +894,46 @@ pub trait TxPin<T: Instance>: SealedTxPin<T> + crate::gpio::Pin {} | |||
| 892 | #[allow(private_bounds)] | 894 | #[allow(private_bounds)] |
| 893 | pub trait RxPin<T: Instance>: SealedRxPin<T> + crate::gpio::Pin {} | 895 | pub trait RxPin<T: Instance>: SealedRxPin<T> + crate::gpio::Pin {} |
| 894 | 896 | ||
| 895 | macro_rules! impl_tx_pin { | 897 | macro_rules! impl_usart_txd_pin { |
| 896 | ($pin:ident, $instance:ident, $func: ident) => { | 898 | ($pin:ident, $instance:ident, $func: ident) => { |
| 897 | impl SealedTxPin<crate::peripherals::$instance> for crate::peripherals::$pin { | 899 | impl crate::usart::SealedTxPin<crate::peripherals::$instance> for crate::peripherals::$pin { |
| 898 | fn pin_func(&self) -> PioFunc { | 900 | fn pin_func(&self) -> crate::pac::iocon::vals::PioFunc { |
| 901 | use crate::pac::iocon::vals::PioFunc; | ||
| 899 | PioFunc::$func | 902 | PioFunc::$func |
| 900 | } | 903 | } |
| 901 | } | 904 | } |
| 902 | 905 | ||
| 903 | impl TxPin<crate::peripherals::$instance> for crate::peripherals::$pin {} | 906 | impl crate::usart::TxPin<crate::peripherals::$instance> for crate::peripherals::$pin {} |
| 904 | }; | 907 | }; |
| 905 | } | 908 | } |
| 906 | 909 | ||
| 907 | macro_rules! impl_rx_pin { | 910 | macro_rules! impl_usart_rxd_pin { |
| 908 | ($pin:ident, $instance:ident, $func: ident) => { | 911 | ($pin:ident, $instance:ident, $func: ident) => { |
| 909 | impl SealedRxPin<crate::peripherals::$instance> for crate::peripherals::$pin { | 912 | impl crate::usart::SealedRxPin<crate::peripherals::$instance> for crate::peripherals::$pin { |
| 910 | fn pin_func(&self) -> PioFunc { | 913 | fn pin_func(&self) -> crate::pac::iocon::vals::PioFunc { |
| 914 | use crate::pac::iocon::vals::PioFunc; | ||
| 911 | PioFunc::$func | 915 | PioFunc::$func |
| 912 | } | 916 | } |
| 913 | } | 917 | } |
| 914 | 918 | ||
| 915 | impl RxPin<crate::peripherals::$instance> for crate::peripherals::$pin {} | 919 | impl crate::usart::RxPin<crate::peripherals::$instance> for crate::peripherals::$pin {} |
| 916 | }; | 920 | }; |
| 917 | } | 921 | } |
| 918 | 922 | ||
| 919 | impl_tx_pin!(PIO1_6, USART0, ALT1); | 923 | /// Marker trait indicating a DMA channel may be used for USART transmit. |
| 920 | impl_tx_pin!(PIO1_11, USART1, ALT2); | ||
| 921 | impl_tx_pin!(PIO0_27, USART2, ALT1); | ||
| 922 | impl_tx_pin!(PIO0_2, USART3, ALT1); | ||
| 923 | impl_tx_pin!(PIO0_16, USART4, ALT1); | ||
| 924 | impl_tx_pin!(PIO0_9, USART5, ALT3); | ||
| 925 | impl_tx_pin!(PIO1_16, USART6, ALT2); | ||
| 926 | impl_tx_pin!(PIO0_19, USART7, ALT7); | ||
| 927 | |||
| 928 | impl_rx_pin!(PIO1_5, USART0, ALT1); | ||
| 929 | impl_rx_pin!(PIO1_10, USART1, ALT2); | ||
| 930 | impl_rx_pin!(PIO1_24, USART2, ALT1); | ||
| 931 | impl_rx_pin!(PIO0_3, USART3, ALT1); | ||
| 932 | impl_rx_pin!(PIO0_5, USART4, ALT2); | ||
| 933 | impl_rx_pin!(PIO0_8, USART5, ALT3); | ||
| 934 | impl_rx_pin!(PIO1_13, USART6, ALT2); | ||
| 935 | impl_rx_pin!(PIO0_20, USART7, ALT7); | ||
| 936 | |||
| 937 | /// Trait for TX DMA channels. | ||
| 938 | pub trait TxChannel<T: Instance>: crate::dma::Channel {} | 924 | pub trait TxChannel<T: Instance>: crate::dma::Channel {} |
| 939 | /// Trait for RX DMA channels. | 925 | |
| 926 | /// Marker trait indicating a DMA channel may be used for USART recieve. | ||
| 940 | pub trait RxChannel<T: Instance>: crate::dma::Channel {} | 927 | pub trait RxChannel<T: Instance>: crate::dma::Channel {} |
| 941 | 928 | ||
| 942 | macro_rules! impl_channel { | 929 | macro_rules! impl_usart_tx_channel { |
| 943 | ($dma:ident, $instance:ident, Tx) => { | 930 | ($instance: ident, $channel: ident) => { |
| 944 | impl TxChannel<crate::peripherals::$instance> for crate::peripherals::$dma {} | 931 | impl crate::usart::TxChannel<crate::peripherals::$instance> for crate::peripherals::$channel {} |
| 945 | }; | ||
| 946 | ($dma:ident, $instance:ident, Rx) => { | ||
| 947 | impl RxChannel<crate::peripherals::$instance> for crate::peripherals::$dma {} | ||
| 948 | }; | 932 | }; |
| 949 | } | 933 | } |
| 950 | 934 | ||
| 951 | impl_channel!(DMA_CH4, USART0, Rx); | 935 | macro_rules! impl_usart_rx_channel { |
| 952 | impl_channel!(DMA_CH5, USART0, Tx); | 936 | ($instance: ident, $channel: ident) => { |
| 953 | impl_channel!(DMA_CH6, USART1, Rx); | 937 | impl crate::usart::RxChannel<crate::peripherals::$instance> for crate::peripherals::$channel {} |
| 954 | impl_channel!(DMA_CH7, USART1, Tx); | 938 | }; |
| 955 | impl_channel!(DMA_CH10, USART2, Rx); | 939 | } |
| 956 | impl_channel!(DMA_CH11, USART2, Tx); | ||
| 957 | impl_channel!(DMA_CH8, USART3, Rx); | ||
| 958 | impl_channel!(DMA_CH9, USART3, Tx); | ||
| 959 | impl_channel!(DMA_CH12, USART4, Rx); | ||
| 960 | impl_channel!(DMA_CH13, USART4, Tx); | ||
| 961 | impl_channel!(DMA_CH14, USART5, Rx); | ||
| 962 | impl_channel!(DMA_CH15, USART5, Tx); | ||
| 963 | impl_channel!(DMA_CH16, USART6, Rx); | ||
| 964 | impl_channel!(DMA_CH17, USART6, Tx); | ||
| 965 | impl_channel!(DMA_CH18, USART7, Rx); | ||
| 966 | impl_channel!(DMA_CH19, USART7, Tx); | ||
diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md index 3b3cb5351..fa8609bbf 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 |
| @@ -17,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 17 | - Add reset_to_usb_boot for rp235x ([#4705](https://github.com/embassy-rs/embassy/pull/4705)) | 18 | - Add reset_to_usb_boot for rp235x ([#4705](https://github.com/embassy-rs/embassy/pull/4705)) |
| 18 | - Add fix #4822 in PIO onewire. Change to disable the state machine before setting y register ([#4824](https://github.com/embassy-rs/embassy/pull/4824)) | 19 | - Add fix #4822 in PIO onewire. Change to disable the state machine before setting y register ([#4824](https://github.com/embassy-rs/embassy/pull/4824)) |
| 19 | - Add PIO::Ws2812 color order support | 20 | - Add PIO::Ws2812 color order support |
| 21 | - Add TX-only, no SCK SPI support | ||
| 20 | 22 | ||
| 21 | ## 0.8.0 - 2025-08-26 | 23 | ## 0.8.0 - 2025-08-26 |
| 22 | 24 | ||
| @@ -114,3 +116,4 @@ Small release fixing a few gnarly bugs, upgrading is strongly recommended. | |||
| 114 | - rename the Channel trait to Slice and the PwmPin to PwmChannel | 116 | - rename the Channel trait to Slice and the PwmPin to PwmChannel |
| 115 | - i2c: Fix race condition that appears on fast repeated transfers. | 117 | - i2c: Fix race condition that appears on fast repeated transfers. |
| 116 | - Add a basic "read to break" function | 118 | - Add a basic "read to break" function |
| 119 | |||
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-rp/src/spi.rs b/embassy-rp/src/spi.rs index d9410e78d..39f128214 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs | |||
| @@ -308,6 +308,11 @@ impl<'d, T: Instance> Spi<'d, T, Blocking> { | |||
| 308 | ) | 308 | ) |
| 309 | } | 309 | } |
| 310 | 310 | ||
| 311 | /// Create an SPI driver in blocking mode supporting writes only, without SCK pin. | ||
| 312 | pub fn new_blocking_txonly_nosck(inner: Peri<'d, T>, mosi: Peri<'d, impl MosiPin<T> + 'd>, config: Config) -> Self { | ||
| 313 | Self::new_inner(inner, None, Some(mosi.into()), None, None, None, None, config) | ||
| 314 | } | ||
| 315 | |||
| 311 | /// Create an SPI driver in blocking mode supporting reads only. | 316 | /// Create an SPI driver in blocking mode supporting reads only. |
| 312 | pub fn new_blocking_rxonly( | 317 | pub fn new_blocking_rxonly( |
| 313 | inner: Peri<'d, T>, | 318 | inner: Peri<'d, T>, |
| @@ -371,6 +376,26 @@ impl<'d, T: Instance> Spi<'d, T, Async> { | |||
| 371 | ) | 376 | ) |
| 372 | } | 377 | } |
| 373 | 378 | ||
| 379 | /// Create an SPI driver in async mode supporting DMA write operations only, | ||
| 380 | /// without SCK pin. | ||
| 381 | pub fn new_txonly_nosck( | ||
| 382 | inner: Peri<'d, T>, | ||
| 383 | mosi: Peri<'d, impl MosiPin<T> + 'd>, | ||
| 384 | tx_dma: Peri<'d, impl Channel>, | ||
| 385 | config: Config, | ||
| 386 | ) -> Self { | ||
| 387 | Self::new_inner( | ||
| 388 | inner, | ||
| 389 | None, | ||
| 390 | Some(mosi.into()), | ||
| 391 | None, | ||
| 392 | None, | ||
| 393 | Some(tx_dma.into()), | ||
| 394 | None, | ||
| 395 | config, | ||
| 396 | ) | ||
| 397 | } | ||
| 398 | |||
| 374 | /// Create an SPI driver in async mode supporting DMA read operations only. | 399 | /// Create an SPI driver in async mode supporting DMA read operations only. |
| 375 | pub fn new_rxonly( | 400 | pub fn new_rxonly( |
| 376 | inner: Peri<'d, T>, | 401 | inner: Peri<'d, T>, |
diff --git a/embassy-stm32-wpan/CHANGELOG.md b/embassy-stm32-wpan/CHANGELOG.md index 7042ad14c..eb17856fc 100644 --- a/embassy-stm32-wpan/CHANGELOG.md +++ b/embassy-stm32-wpan/CHANGELOG.md | |||
| @@ -8,4 +8,6 @@ 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 | - wpan: restructure hil and test wpan mac | ||
| 12 | - restructure to allow embassy net driver to work. | ||
| 11 | - First release with changelog. | 13 | - First release with changelog. |
diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 0802b7328..05d76f4a6 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml | |||
| @@ -33,6 +33,7 @@ embassy-futures = { version = "0.1.2", path = "../embassy-futures" } | |||
| 33 | embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal" } | 33 | embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal" } |
| 34 | embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" } | 34 | embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" } |
| 35 | embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true } | 35 | embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true } |
| 36 | smoltcp = { version = "0.12.0", optional=true, default-features = false } | ||
| 36 | 37 | ||
| 37 | defmt = { version = "1.0.1", optional = true } | 38 | defmt = { version = "1.0.1", optional = true } |
| 38 | log = { version = "0.4.17", optional = true } | 39 | log = { version = "0.4.17", optional = true } |
| @@ -40,6 +41,7 @@ log = { version = "0.4.17", optional = true } | |||
| 40 | cortex-m = "0.7.6" | 41 | cortex-m = "0.7.6" |
| 41 | heapless = "0.8" | 42 | heapless = "0.8" |
| 42 | aligned = "0.4.1" | 43 | aligned = "0.4.1" |
| 44 | critical-section = "1.1" | ||
| 43 | 45 | ||
| 44 | bit_field = "0.10.2" | 46 | bit_field = "0.10.2" |
| 45 | stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } | 47 | stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } |
| @@ -51,7 +53,7 @@ bitflags = { version = "2.3.3", optional = true } | |||
| 51 | defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "stm32wb-hci?/defmt"] | 53 | defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "stm32wb-hci?/defmt"] |
| 52 | 54 | ||
| 53 | ble = ["dep:stm32wb-hci"] | 55 | ble = ["dep:stm32wb-hci"] |
| 54 | mac = ["dep:bitflags", "dep:embassy-net-driver" ] | 56 | mac = ["dep:bitflags", "dep:embassy-net-driver", "dep:smoltcp", "smoltcp/medium-ieee802154"] |
| 55 | 57 | ||
| 56 | extended = [] | 58 | extended = [] |
| 57 | 59 | ||
diff --git a/embassy-stm32-wpan/src/channels.rs b/embassy-stm32-wpan/src/channels.rs index 9a2be1cfa..58f857136 100644 --- a/embassy-stm32-wpan/src/channels.rs +++ b/embassy-stm32-wpan/src/channels.rs | |||
| @@ -48,9 +48,20 @@ | |||
| 48 | //! |<----HW_IPCC_TRACES_CHANNEL----------------------| | 48 | //! |<----HW_IPCC_TRACES_CHANNEL----------------------| |
| 49 | //! | | | 49 | //! | | |
| 50 | //! | 50 | //! |
| 51 | //! | ||
| 52 | |||
| 53 | #[repr(u8)] | ||
| 54 | pub enum IpccChannel { | ||
| 55 | Channel1 = 1, | ||
| 56 | Channel2 = 2, | ||
| 57 | Channel3 = 3, | ||
| 58 | Channel4 = 4, | ||
| 59 | Channel5 = 5, | ||
| 60 | Channel6 = 6, | ||
| 61 | } | ||
| 51 | 62 | ||
| 52 | pub mod cpu1 { | 63 | pub mod cpu1 { |
| 53 | use embassy_stm32::ipcc::IpccChannel; | 64 | use super::IpccChannel; |
| 54 | 65 | ||
| 55 | pub const IPCC_BLE_CMD_CHANNEL: IpccChannel = IpccChannel::Channel1; | 66 | pub const IPCC_BLE_CMD_CHANNEL: IpccChannel = IpccChannel::Channel1; |
| 56 | pub const IPCC_SYSTEM_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel2; | 67 | pub const IPCC_SYSTEM_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel2; |
| @@ -70,7 +81,7 @@ pub mod cpu1 { | |||
| 70 | } | 81 | } |
| 71 | 82 | ||
| 72 | pub mod cpu2 { | 83 | pub mod cpu2 { |
| 73 | use embassy_stm32::ipcc::IpccChannel; | 84 | use super::IpccChannel; |
| 74 | 85 | ||
| 75 | pub const IPCC_BLE_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel1; | 86 | pub const IPCC_BLE_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel1; |
| 76 | pub const IPCC_SYSTEM_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel2; | 87 | pub const IPCC_SYSTEM_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel2; |
diff --git a/embassy-stm32-wpan/src/cmd.rs b/embassy-stm32-wpan/src/cmd.rs index 5c81a4aa7..787c22c4b 100644 --- a/embassy-stm32-wpan/src/cmd.rs +++ b/embassy-stm32-wpan/src/cmd.rs | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | use core::ptr; | 1 | use core::ptr; |
| 2 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 2 | 3 | ||
| 3 | use crate::PacketHeader; | 4 | use crate::PacketHeader; |
| 4 | use crate::consts::TlPacketType; | 5 | use crate::consts::TlPacketType; |
| @@ -45,11 +46,11 @@ pub struct CmdPacket { | |||
| 45 | 46 | ||
| 46 | impl CmdPacket { | 47 | impl CmdPacket { |
| 47 | pub unsafe fn write_into(cmd_buf: *mut CmdPacket, packet_type: TlPacketType, cmd_code: u16, payload: &[u8]) { | 48 | pub unsafe fn write_into(cmd_buf: *mut CmdPacket, packet_type: TlPacketType, cmd_code: u16, payload: &[u8]) { |
| 48 | let p_cmd_serial = &mut (*cmd_buf).cmdserial as *mut _ as *mut CmdSerialStub; | 49 | let p_cmd_serial = (cmd_buf as *mut u8).add(size_of::<PacketHeader>()); |
| 49 | let p_payload = &mut (*cmd_buf).cmdserial.cmd.payload as *mut _; | 50 | let p_payload = p_cmd_serial.add(size_of::<CmdSerialStub>()); |
| 50 | 51 | ||
| 51 | ptr::write_volatile( | 52 | ptr::write_unaligned( |
| 52 | p_cmd_serial, | 53 | p_cmd_serial as *mut _, |
| 53 | CmdSerialStub { | 54 | CmdSerialStub { |
| 54 | ty: packet_type as u8, | 55 | ty: packet_type as u8, |
| 55 | cmd_code, | 56 | cmd_code, |
| @@ -58,6 +59,8 @@ impl CmdPacket { | |||
| 58 | ); | 59 | ); |
| 59 | 60 | ||
| 60 | ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len()); | 61 | ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len()); |
| 62 | |||
| 63 | compiler_fence(Ordering::Release); | ||
| 61 | } | 64 | } |
| 62 | } | 65 | } |
| 63 | 66 | ||
| @@ -87,11 +90,11 @@ pub struct AclDataPacket { | |||
| 87 | 90 | ||
| 88 | impl AclDataPacket { | 91 | impl AclDataPacket { |
| 89 | pub unsafe fn write_into(cmd_buf: *mut AclDataPacket, packet_type: TlPacketType, handle: u16, payload: &[u8]) { | 92 | pub unsafe fn write_into(cmd_buf: *mut AclDataPacket, packet_type: TlPacketType, handle: u16, payload: &[u8]) { |
| 90 | let p_cmd_serial = &mut (*cmd_buf).acl_data_serial as *mut _ as *mut AclDataSerialStub; | 93 | let p_cmd_serial = (cmd_buf as *mut u8).add(size_of::<PacketHeader>()); |
| 91 | let p_payload = &mut (*cmd_buf).acl_data_serial.acl_data as *mut _; | 94 | let p_payload = p_cmd_serial.add(size_of::<AclDataSerialStub>()); |
| 92 | 95 | ||
| 93 | ptr::write_volatile( | 96 | ptr::write_unaligned( |
| 94 | p_cmd_serial, | 97 | p_cmd_serial as *mut _, |
| 95 | AclDataSerialStub { | 98 | AclDataSerialStub { |
| 96 | ty: packet_type as u8, | 99 | ty: packet_type as u8, |
| 97 | handle: handle, | 100 | handle: handle, |
| @@ -100,5 +103,7 @@ impl AclDataPacket { | |||
| 100 | ); | 103 | ); |
| 101 | 104 | ||
| 102 | ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len()); | 105 | ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len()); |
| 106 | |||
| 107 | compiler_fence(Ordering::Release); | ||
| 103 | } | 108 | } |
| 104 | } | 109 | } |
diff --git a/embassy-stm32-wpan/src/evt.rs b/embassy-stm32-wpan/src/evt.rs index c6528413d..f32821269 100644 --- a/embassy-stm32-wpan/src/evt.rs +++ b/embassy-stm32-wpan/src/evt.rs | |||
| @@ -67,6 +67,7 @@ pub struct EvtSerial { | |||
| 67 | pub struct EvtStub { | 67 | pub struct EvtStub { |
| 68 | pub kind: u8, | 68 | pub kind: u8, |
| 69 | pub evt_code: u8, | 69 | pub evt_code: u8, |
| 70 | pub payload_len: u8, | ||
| 70 | } | 71 | } |
| 71 | 72 | ||
| 72 | /// This format shall be used for all events (asynchronous and command response) reported | 73 | /// This format shall be used for all events (asynchronous and command response) reported |
diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index f6b1f6021..18b0feada 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs | |||
| @@ -26,7 +26,7 @@ use core::sync::atomic::{Ordering, compiler_fence}; | |||
| 26 | 26 | ||
| 27 | use embassy_hal_internal::Peri; | 27 | use embassy_hal_internal::Peri; |
| 28 | use embassy_stm32::interrupt; | 28 | use embassy_stm32::interrupt; |
| 29 | use embassy_stm32::ipcc::{Config, Ipcc, ReceiveInterruptHandler, TransmitInterruptHandler}; | 29 | use embassy_stm32::ipcc::{Config, Ipcc, IpccRxChannel, ReceiveInterruptHandler, TransmitInterruptHandler}; |
| 30 | use embassy_stm32::peripherals::IPCC; | 30 | use embassy_stm32::peripherals::IPCC; |
| 31 | use sub::mm::MemoryManager; | 31 | use sub::mm::MemoryManager; |
| 32 | use sub::sys::Sys; | 32 | use sub::sys::Sys; |
| @@ -53,14 +53,13 @@ type PacketHeader = LinkedListNode; | |||
| 53 | 53 | ||
| 54 | /// Transport Layer for the Mailbox interface | 54 | /// Transport Layer for the Mailbox interface |
| 55 | pub struct TlMbox<'d> { | 55 | pub struct TlMbox<'d> { |
| 56 | _ipcc: Peri<'d, IPCC>, | 56 | pub sys_subsystem: Sys<'d>, |
| 57 | 57 | pub mm_subsystem: MemoryManager<'d>, | |
| 58 | pub sys_subsystem: Sys, | ||
| 59 | pub mm_subsystem: MemoryManager, | ||
| 60 | #[cfg(feature = "ble")] | 58 | #[cfg(feature = "ble")] |
| 61 | pub ble_subsystem: sub::ble::Ble, | 59 | pub ble_subsystem: sub::ble::Ble<'d>, |
| 62 | #[cfg(feature = "mac")] | 60 | #[cfg(feature = "mac")] |
| 63 | pub mac_subsystem: sub::mac::Mac, | 61 | pub mac_subsystem: sub::mac::Mac<'d>, |
| 62 | pub traces: IpccRxChannel<'d>, | ||
| 64 | } | 63 | } |
| 65 | 64 | ||
| 66 | impl<'d> TlMbox<'d> { | 65 | impl<'d> TlMbox<'d> { |
| @@ -92,7 +91,7 @@ impl<'d> TlMbox<'d> { | |||
| 92 | /// be handled by `TL_BLE_Init`; see Figure 66). This completes the procedure laid out in | 91 | /// be handled by `TL_BLE_Init`; see Figure 66). This completes the procedure laid out in |
| 93 | /// Figure 66. | 92 | /// Figure 66. |
| 94 | // TODO: document what the user should do after calling init to use the mac_802_15_4 subsystem | 93 | // TODO: document what the user should do after calling init to use the mac_802_15_4 subsystem |
| 95 | pub fn init( | 94 | pub async fn init( |
| 96 | ipcc: Peri<'d, IPCC>, | 95 | ipcc: Peri<'d, IPCC>, |
| 97 | _irqs: impl interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_RX, ReceiveInterruptHandler> | 96 | _irqs: impl interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_RX, ReceiveInterruptHandler> |
| 98 | + interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_TX, TransmitInterruptHandler>, | 97 | + interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_TX, TransmitInterruptHandler>, |
| @@ -185,16 +184,35 @@ impl<'d> TlMbox<'d> { | |||
| 185 | compiler_fence(Ordering::SeqCst); | 184 | compiler_fence(Ordering::SeqCst); |
| 186 | 185 | ||
| 187 | // this is equivalent to `HW_IPCC_Enable`, which is called by `TL_Enable` | 186 | // this is equivalent to `HW_IPCC_Enable`, which is called by `TL_Enable` |
| 188 | Ipcc::enable(config); | 187 | let [ |
| 188 | (_hw_ipcc_ble_cmd_channel, _ipcc_ble_event_channel), | ||
| 189 | (ipcc_system_cmd_rsp_channel, ipcc_system_event_channel), | ||
| 190 | (_ipcc_mac_802_15_4_cmd_rsp_channel, _ipcc_mac_802_15_4_notification_ack_channel), | ||
| 191 | (ipcc_mm_release_buffer_channel, _ipcc_traces_channel), | ||
| 192 | (_ipcc_ble_lld_cmd_channel, _ipcc_ble_lld_rsp_channel), | ||
| 193 | (_ipcc_hci_acl_data_channel, _), | ||
| 194 | ] = Ipcc::new(ipcc, _irqs, config).split(); | ||
| 195 | |||
| 196 | let mm = sub::mm::MemoryManager::new(ipcc_mm_release_buffer_channel); | ||
| 197 | let mut sys = sub::sys::Sys::new(ipcc_system_cmd_rsp_channel, ipcc_system_event_channel); | ||
| 198 | |||
| 199 | debug!("sys event: {}", sys.read().await.payload()); | ||
| 189 | 200 | ||
| 190 | Self { | 201 | Self { |
| 191 | _ipcc: ipcc, | 202 | sys_subsystem: sys, |
| 192 | sys_subsystem: sub::sys::Sys::new(), | ||
| 193 | #[cfg(feature = "ble")] | 203 | #[cfg(feature = "ble")] |
| 194 | ble_subsystem: sub::ble::Ble::new(), | 204 | ble_subsystem: sub::ble::Ble::new( |
| 205 | _hw_ipcc_ble_cmd_channel, | ||
| 206 | _ipcc_ble_event_channel, | ||
| 207 | _ipcc_hci_acl_data_channel, | ||
| 208 | ), | ||
| 195 | #[cfg(feature = "mac")] | 209 | #[cfg(feature = "mac")] |
| 196 | mac_subsystem: sub::mac::Mac::new(), | 210 | mac_subsystem: sub::mac::Mac::new( |
| 197 | mm_subsystem: sub::mm::MemoryManager::new(), | 211 | _ipcc_mac_802_15_4_cmd_rsp_channel, |
| 212 | _ipcc_mac_802_15_4_notification_ack_channel, | ||
| 213 | ), | ||
| 214 | mm_subsystem: mm, | ||
| 215 | traces: _ipcc_traces_channel, | ||
| 198 | } | 216 | } |
| 199 | } | 217 | } |
| 200 | } | 218 | } |
diff --git a/embassy-stm32-wpan/src/mac/commands.rs b/embassy-stm32-wpan/src/mac/commands.rs index 82b9d2772..d96f0094a 100644 --- a/embassy-stm32-wpan/src/mac/commands.rs +++ b/embassy-stm32-wpan/src/mac/commands.rs | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | 2 | ||
| 3 | use core::{mem, slice}; | 3 | use core::{mem, slice}; |
| 4 | 4 | ||
| 5 | use smoltcp::wire::ieee802154::Frame; | ||
| 6 | |||
| 5 | use super::opcodes::OpcodeM4ToM0; | 7 | use super::opcodes::OpcodeM4ToM0; |
| 6 | use super::typedefs::{ | 8 | use super::typedefs::{ |
| 7 | AddressMode, Capabilities, DisassociationReason, GtsCharacteristics, KeyIdMode, MacAddress, MacChannel, MacStatus, | 9 | AddressMode, Capabilities, DisassociationReason, GtsCharacteristics, KeyIdMode, MacAddress, MacChannel, MacStatus, |
| @@ -379,6 +381,35 @@ impl DataRequest { | |||
| 379 | } | 381 | } |
| 380 | } | 382 | } |
| 381 | 383 | ||
| 384 | impl<'a, T: AsRef<[u8]>> TryFrom<Frame<&'a T>> for DataRequest { | ||
| 385 | type Error = (); | ||
| 386 | |||
| 387 | fn try_from(frame: Frame<&'a T>) -> Result<Self, Self::Error> { | ||
| 388 | // TODO: map the rest of these | ||
| 389 | |||
| 390 | let mut request = DataRequest { | ||
| 391 | src_addr_mode: frame.src_addressing_mode().try_into()?, | ||
| 392 | dst_addr_mode: frame.dst_addressing_mode().try_into()?, | ||
| 393 | dst_pan_id: frame.dst_pan_id().ok_or(())?.into(), | ||
| 394 | dst_address: frame.dst_addr().ok_or(())?.into(), | ||
| 395 | msdu_handle: frame.sequence_number().ok_or(())?, | ||
| 396 | key_source: frame.key_source().unwrap_or_default().try_into().unwrap_or_default(), | ||
| 397 | ack_tx: frame.ack_request() as u8, | ||
| 398 | gts_tx: false, | ||
| 399 | security_level: if frame.security_enabled() { | ||
| 400 | SecurityLevel::Secured | ||
| 401 | } else { | ||
| 402 | SecurityLevel::Unsecure | ||
| 403 | }, | ||
| 404 | ..Default::default() | ||
| 405 | }; | ||
| 406 | |||
| 407 | request.set_buffer(frame.payload().ok_or(())?); | ||
| 408 | |||
| 409 | Ok(request) | ||
| 410 | } | ||
| 411 | } | ||
| 412 | |||
| 382 | impl Default for DataRequest { | 413 | impl Default for DataRequest { |
| 383 | fn default() -> Self { | 414 | fn default() -> Self { |
| 384 | Self { | 415 | Self { |
diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/mac/control.rs index e8d2f9f7b..14c6fdd2b 100644 --- a/embassy-stm32-wpan/src/mac/control.rs +++ b/embassy-stm32-wpan/src/mac/control.rs | |||
| @@ -1,65 +1,186 @@ | |||
| 1 | use core::cell::RefCell; | ||
| 1 | use core::future::Future; | 2 | use core::future::Future; |
| 3 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 2 | use core::task; | 4 | use core::task; |
| 3 | use core::task::Poll; | 5 | use core::task::Poll; |
| 4 | 6 | ||
| 7 | use embassy_net_driver::LinkState; | ||
| 8 | use embassy_sync::blocking_mutex; | ||
| 5 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 9 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 6 | use embassy_sync::mutex::MutexGuard; | 10 | use embassy_sync::mutex::Mutex; |
| 7 | use embassy_sync::signal::Signal; | 11 | use embassy_sync::signal::Signal; |
| 8 | use futures_util::FutureExt; | 12 | use futures_util::FutureExt; |
| 9 | 13 | ||
| 10 | use super::commands::MacCommand; | 14 | use crate::mac::commands::*; |
| 11 | use super::event::MacEvent; | 15 | use crate::mac::driver::NetworkState; |
| 12 | use super::typedefs::MacError; | 16 | use crate::mac::event::MacEvent; |
| 13 | use crate::mac::runner::Runner; | 17 | use crate::mac::runner::ZeroCopyPubSub; |
| 18 | use crate::mac::typedefs::*; | ||
| 19 | use crate::sub::mac::MacTx; | ||
| 14 | 20 | ||
| 15 | pub struct Control<'a> { | 21 | pub struct Control<'a> { |
| 16 | runner: &'a Runner<'a>, | 22 | rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>, |
| 23 | mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx<'a>>, | ||
| 24 | #[allow(unused)] | ||
| 25 | network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>, | ||
| 17 | } | 26 | } |
| 18 | 27 | ||
| 19 | impl<'a> Control<'a> { | 28 | impl<'a> Control<'a> { |
| 20 | pub(crate) fn new(runner: &'a Runner<'a>) -> Self { | 29 | pub(crate) fn new( |
| 21 | Self { runner: runner } | 30 | rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>, |
| 31 | mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx<'a>>, | ||
| 32 | network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>, | ||
| 33 | ) -> Self { | ||
| 34 | Self { | ||
| 35 | rx_event_channel, | ||
| 36 | mac_tx, | ||
| 37 | network_state, | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | pub async fn init_link(&mut self, pan_id: [u8; 2]) { | ||
| 42 | debug!("resetting"); | ||
| 43 | |||
| 44 | debug!( | ||
| 45 | "{:#x}", | ||
| 46 | self.send_command_and_get_response(&ResetRequest { | ||
| 47 | set_default_pib: true, | ||
| 48 | ..Default::default() | ||
| 49 | }) | ||
| 50 | .await | ||
| 51 | .unwrap() | ||
| 52 | .await | ||
| 53 | ); | ||
| 54 | |||
| 55 | let (short_address, mac_address) = critical_section::with(|cs| { | ||
| 56 | let mut network_state = self.network_state.borrow(cs).borrow_mut(); | ||
| 57 | |||
| 58 | network_state.pan_id = pan_id; | ||
| 59 | |||
| 60 | (network_state.short_addr, network_state.mac_addr) | ||
| 61 | }); | ||
| 62 | |||
| 63 | debug!("setting extended address"); | ||
| 64 | debug!( | ||
| 65 | "{:#x}", | ||
| 66 | self.send_command_and_get_response(&SetRequest { | ||
| 67 | pib_attribute_ptr: &u64::from_be_bytes(mac_address) as *const _ as *const u8, | ||
| 68 | pib_attribute: PibId::ExtendedAddress, | ||
| 69 | }) | ||
| 70 | .await | ||
| 71 | .unwrap() | ||
| 72 | .await | ||
| 73 | ); | ||
| 74 | |||
| 75 | debug!("setting short address"); | ||
| 76 | debug!( | ||
| 77 | "{:#x}", | ||
| 78 | self.send_command_and_get_response(&SetRequest { | ||
| 79 | pib_attribute_ptr: &u16::from_be_bytes(short_address) as *const _ as *const u8, | ||
| 80 | pib_attribute: PibId::ShortAddress, | ||
| 81 | }) | ||
| 82 | .await | ||
| 83 | .unwrap() | ||
| 84 | .await | ||
| 85 | ); | ||
| 86 | |||
| 87 | debug!("setting association permit"); | ||
| 88 | let association_permit: bool = true; | ||
| 89 | debug!( | ||
| 90 | "{:#x}", | ||
| 91 | self.send_command_and_get_response(&SetRequest { | ||
| 92 | pib_attribute_ptr: &association_permit as *const _ as *const u8, | ||
| 93 | pib_attribute: PibId::AssociationPermit, | ||
| 94 | }) | ||
| 95 | .await | ||
| 96 | .unwrap() | ||
| 97 | .await | ||
| 98 | ); | ||
| 99 | |||
| 100 | debug!("setting TX power"); | ||
| 101 | let transmit_power: i8 = 2; | ||
| 102 | debug!( | ||
| 103 | "{:#x}", | ||
| 104 | self.send_command_and_get_response(&SetRequest { | ||
| 105 | pib_attribute_ptr: &transmit_power as *const _ as *const u8, | ||
| 106 | pib_attribute: PibId::TransmitPower, | ||
| 107 | }) | ||
| 108 | .await | ||
| 109 | .unwrap() | ||
| 110 | .await | ||
| 111 | ); | ||
| 112 | |||
| 113 | debug!("starting FFD device"); | ||
| 114 | debug!( | ||
| 115 | "{:#x}", | ||
| 116 | self.send_command_and_get_response(&StartRequest { | ||
| 117 | pan_id: PanId(pan_id), | ||
| 118 | channel_number: MacChannel::Channel16, | ||
| 119 | beacon_order: 0x0F, | ||
| 120 | superframe_order: 0x0F, | ||
| 121 | pan_coordinator: true, | ||
| 122 | battery_life_extension: false, | ||
| 123 | ..Default::default() | ||
| 124 | }) | ||
| 125 | .await | ||
| 126 | .unwrap() | ||
| 127 | .await | ||
| 128 | ); | ||
| 129 | |||
| 130 | debug!("setting RX on when idle"); | ||
| 131 | let rx_on_while_idle: bool = true; | ||
| 132 | debug!( | ||
| 133 | "{:#x}", | ||
| 134 | self.send_command_and_get_response(&SetRequest { | ||
| 135 | pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8, | ||
| 136 | pib_attribute: PibId::RxOnWhenIdle, | ||
| 137 | }) | ||
| 138 | .await | ||
| 139 | .unwrap() | ||
| 140 | .await | ||
| 141 | ); | ||
| 142 | |||
| 143 | critical_section::with(|cs| { | ||
| 144 | let mut network_state = self.network_state.borrow(cs).borrow_mut(); | ||
| 145 | |||
| 146 | network_state.link_state = LinkState::Up; | ||
| 147 | network_state.link_waker.wake(); | ||
| 148 | }); | ||
| 22 | } | 149 | } |
| 23 | 150 | ||
| 24 | pub async fn send_command<T>(&self, cmd: &T) -> Result<(), MacError> | 151 | pub async fn send_command<T>(&self, cmd: &T) -> Result<(), MacError> |
| 25 | where | 152 | where |
| 26 | T: MacCommand, | 153 | T: MacCommand, |
| 27 | { | 154 | { |
| 28 | let _wm = self.runner.write_mutex.lock().await; | 155 | self.mac_tx.lock().await.send_command(cmd).await |
| 29 | |||
| 30 | self.runner.mac_subsystem.send_command(cmd).await | ||
| 31 | } | 156 | } |
| 32 | 157 | ||
| 33 | pub async fn send_command_and_get_response<T>(&self, cmd: &T) -> Result<EventToken<'a>, MacError> | 158 | pub async fn send_command_and_get_response<T>(&self, cmd: &T) -> Result<EventToken<'a>, MacError> |
| 34 | where | 159 | where |
| 35 | T: MacCommand, | 160 | T: MacCommand, |
| 36 | { | 161 | { |
| 37 | let rm = self.runner.read_mutex.lock().await; | 162 | let token = EventToken::new(self.rx_event_channel); |
| 38 | let _wm = self.runner.write_mutex.lock().await; | ||
| 39 | let token = EventToken::new(self.runner, rm); | ||
| 40 | 163 | ||
| 41 | self.runner.mac_subsystem.send_command(cmd).await?; | 164 | compiler_fence(Ordering::Release); |
| 165 | |||
| 166 | self.mac_tx.lock().await.send_command(cmd).await?; | ||
| 42 | 167 | ||
| 43 | Ok(token) | 168 | Ok(token) |
| 44 | } | 169 | } |
| 45 | } | 170 | } |
| 46 | 171 | ||
| 47 | pub struct EventToken<'a> { | 172 | pub struct EventToken<'a> { |
| 48 | runner: &'a Runner<'a>, | 173 | rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>, |
| 49 | _mutex_guard: MutexGuard<'a, CriticalSectionRawMutex, ()>, | ||
| 50 | } | 174 | } |
| 51 | 175 | ||
| 52 | impl<'a> EventToken<'a> { | 176 | impl<'a> EventToken<'a> { |
| 53 | pub(crate) fn new(runner: &'a Runner<'a>, mutex_guard: MutexGuard<'a, CriticalSectionRawMutex, ()>) -> Self { | 177 | pub(crate) fn new(rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>) -> Self { |
| 54 | // Enable event receiving | 178 | // Enable event receiving |
| 55 | runner.rx_event_channel.lock(|s| { | 179 | rx_event_channel.lock(|s| { |
| 56 | *s.borrow_mut() = Some(Signal::new()); | 180 | *s.borrow_mut() = Some(Signal::new()); |
| 57 | }); | 181 | }); |
| 58 | 182 | ||
| 59 | Self { | 183 | Self { rx_event_channel } |
| 60 | runner: runner, | ||
| 61 | _mutex_guard: mutex_guard, | ||
| 62 | } | ||
| 63 | } | 184 | } |
| 64 | } | 185 | } |
| 65 | 186 | ||
| @@ -67,20 +188,8 @@ impl<'a> Future for EventToken<'a> { | |||
| 67 | type Output = MacEvent<'a>; | 188 | type Output = MacEvent<'a>; |
| 68 | 189 | ||
| 69 | fn poll(self: core::pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> { | 190 | fn poll(self: core::pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> { |
| 70 | self.get_mut().runner.rx_event_channel.lock(|s| { | 191 | self.rx_event_channel |
| 71 | let signal = s.borrow_mut(); | 192 | .lock(|s| s.borrow_mut().as_mut().unwrap().wait().poll_unpin(cx)) |
| 72 | let signal = match &*signal { | ||
| 73 | Some(s) => s, | ||
| 74 | _ => unreachable!(), | ||
| 75 | }; | ||
| 76 | |||
| 77 | let result = match signal.wait().poll_unpin(cx) { | ||
| 78 | Poll::Ready(mac_event) => Poll::Ready(mac_event), | ||
| 79 | Poll::Pending => Poll::Pending, | ||
| 80 | }; | ||
| 81 | |||
| 82 | result | ||
| 83 | }) | ||
| 84 | } | 193 | } |
| 85 | } | 194 | } |
| 86 | 195 | ||
| @@ -88,7 +197,7 @@ impl<'a> Drop for EventToken<'a> { | |||
| 88 | fn drop(&mut self) { | 197 | fn drop(&mut self) { |
| 89 | // Disable event receiving | 198 | // Disable event receiving |
| 90 | // This will also drop the contained event, if it exists, and will free up receiving the next event | 199 | // This will also drop the contained event, if it exists, and will free up receiving the next event |
| 91 | self.runner.rx_event_channel.lock(|s| { | 200 | self.rx_event_channel.lock(|s| { |
| 92 | *s.borrow_mut() = None; | 201 | *s.borrow_mut() = None; |
| 93 | }); | 202 | }); |
| 94 | } | 203 | } |
diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index 480ac3790..41171ce3d 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs | |||
| @@ -1,22 +1,108 @@ | |||
| 1 | #![deny(unused_must_use)] | 1 | #![deny(unused_must_use)] |
| 2 | 2 | ||
| 3 | use core::cell::RefCell; | ||
| 3 | use core::task::Context; | 4 | use core::task::Context; |
| 4 | 5 | ||
| 5 | use embassy_net_driver::{Capabilities, HardwareAddress, LinkState}; | 6 | use embassy_net_driver::{Capabilities, HardwareAddress, LinkState}; |
| 7 | use embassy_sync::blocking_mutex; | ||
| 6 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 8 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 7 | use embassy_sync::channel::Channel; | 9 | use embassy_sync::channel::Channel; |
| 10 | use embassy_sync::mutex::Mutex; | ||
| 11 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 8 | 12 | ||
| 9 | use crate::mac::MTU; | ||
| 10 | use crate::mac::event::MacEvent; | 13 | use crate::mac::event::MacEvent; |
| 11 | use crate::mac::runner::Runner; | 14 | use crate::mac::indications::{write_frame_from_beacon_indication, write_frame_from_data_indication}; |
| 15 | use crate::mac::runner::{BUF_SIZE, ZeroCopyPubSub}; | ||
| 16 | use crate::mac::{Control, MTU, Runner}; | ||
| 17 | use crate::sub::mac::{Mac, MacRx, MacTx}; | ||
| 18 | |||
| 19 | pub struct NetworkState { | ||
| 20 | pub mac_addr: [u8; 8], | ||
| 21 | pub short_addr: [u8; 2], | ||
| 22 | pub pan_id: [u8; 2], | ||
| 23 | pub link_state: LinkState, | ||
| 24 | pub link_waker: AtomicWaker, | ||
| 25 | } | ||
| 26 | |||
| 27 | impl NetworkState { | ||
| 28 | pub const fn new() -> Self { | ||
| 29 | Self { | ||
| 30 | mac_addr: [0u8; 8], | ||
| 31 | short_addr: [0u8; 2], | ||
| 32 | pan_id: [0u8; 2], | ||
| 33 | link_state: LinkState::Down, | ||
| 34 | link_waker: AtomicWaker::new(), | ||
| 35 | } | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | pub struct DriverState<'d> { | ||
| 40 | pub mac_tx: Mutex<CriticalSectionRawMutex, MacTx<'d>>, | ||
| 41 | pub mac_rx: MacRx<'d>, | ||
| 42 | pub rx_event_channel: ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'d>>, | ||
| 43 | pub rx_data_channel: Channel<CriticalSectionRawMutex, MacEvent<'d>, 1>, | ||
| 44 | pub tx_data_channel: Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), BUF_SIZE>, | ||
| 45 | pub tx_buf_channel: Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], BUF_SIZE>, | ||
| 46 | pub tx_buf_queue: [[u8; MTU]; BUF_SIZE], | ||
| 47 | pub network_state: blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>, | ||
| 48 | } | ||
| 49 | |||
| 50 | impl<'d> DriverState<'d> { | ||
| 51 | pub const fn new(mac: Mac<'d>) -> Self { | ||
| 52 | let (mac_rx, mac_tx) = mac.split(); | ||
| 53 | let mac_tx = Mutex::new(mac_tx); | ||
| 54 | |||
| 55 | Self { | ||
| 56 | mac_tx, | ||
| 57 | mac_rx, | ||
| 58 | rx_event_channel: ZeroCopyPubSub::new(RefCell::new(None)), | ||
| 59 | rx_data_channel: Channel::new(), | ||
| 60 | tx_data_channel: Channel::new(), | ||
| 61 | tx_buf_channel: Channel::new(), | ||
| 62 | tx_buf_queue: [[0u8; MTU]; BUF_SIZE], | ||
| 63 | network_state: blocking_mutex::Mutex::new(RefCell::new(NetworkState::new())), | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 12 | 67 | ||
| 13 | pub struct Driver<'d> { | 68 | pub struct Driver<'d> { |
| 14 | runner: &'d Runner<'d>, | 69 | tx_data_channel: &'d Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), BUF_SIZE>, |
| 70 | tx_buf_channel: &'d Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], BUF_SIZE>, | ||
| 71 | rx_data_channel: &'d Channel<CriticalSectionRawMutex, MacEvent<'d>, 1>, | ||
| 72 | network_state: &'d blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>, | ||
| 15 | } | 73 | } |
| 16 | 74 | ||
| 17 | impl<'d> Driver<'d> { | 75 | impl<'d> Driver<'d> { |
| 18 | pub(crate) fn new(runner: &'d Runner<'d>) -> Self { | 76 | pub fn new( |
| 19 | Self { runner: runner } | 77 | driver_state: &'d mut DriverState<'d>, |
| 78 | short_address: [u8; 2], | ||
| 79 | mac_address: [u8; 8], | ||
| 80 | ) -> (Self, Runner<'d>, Control<'d>) { | ||
| 81 | ( | ||
| 82 | Self { | ||
| 83 | tx_data_channel: &driver_state.tx_data_channel, | ||
| 84 | tx_buf_channel: &driver_state.tx_buf_channel, | ||
| 85 | rx_data_channel: &driver_state.rx_data_channel, | ||
| 86 | network_state: &driver_state.network_state, | ||
| 87 | }, | ||
| 88 | Runner::new( | ||
| 89 | &driver_state.rx_event_channel, | ||
| 90 | &driver_state.rx_data_channel, | ||
| 91 | &mut driver_state.mac_rx, | ||
| 92 | &driver_state.tx_data_channel, | ||
| 93 | &driver_state.tx_buf_channel, | ||
| 94 | &driver_state.mac_tx, | ||
| 95 | &mut driver_state.tx_buf_queue, | ||
| 96 | &driver_state.network_state, | ||
| 97 | short_address, | ||
| 98 | mac_address, | ||
| 99 | ), | ||
| 100 | Control::new( | ||
| 101 | &driver_state.rx_event_channel, | ||
| 102 | &driver_state.mac_tx, | ||
| 103 | &driver_state.network_state, | ||
| 104 | ), | ||
| 105 | ) | ||
| 20 | } | 106 | } |
| 21 | } | 107 | } |
| 22 | 108 | ||
| @@ -33,16 +119,16 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { | |||
| 33 | Self: 'a; | 119 | Self: 'a; |
| 34 | 120 | ||
| 35 | fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { | 121 | fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { |
| 36 | if self.runner.rx_channel.poll_ready_to_receive(cx).is_ready() | 122 | if self.rx_data_channel.poll_ready_to_receive(cx).is_ready() |
| 37 | && self.runner.tx_buf_channel.poll_ready_to_receive(cx).is_ready() | 123 | && self.tx_buf_channel.poll_ready_to_receive(cx).is_ready() |
| 38 | { | 124 | { |
| 39 | Some(( | 125 | Some(( |
| 40 | RxToken { | 126 | RxToken { |
| 41 | rx: &self.runner.rx_channel, | 127 | rx: self.rx_data_channel, |
| 42 | }, | 128 | }, |
| 43 | TxToken { | 129 | TxToken { |
| 44 | tx: &self.runner.tx_channel, | 130 | tx: self.tx_data_channel, |
| 45 | tx_buf: &self.runner.tx_buf_channel, | 131 | tx_buf: self.tx_buf_channel, |
| 46 | }, | 132 | }, |
| 47 | )) | 133 | )) |
| 48 | } else { | 134 | } else { |
| @@ -51,10 +137,10 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { | |||
| 51 | } | 137 | } |
| 52 | 138 | ||
| 53 | fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> { | 139 | fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> { |
| 54 | if self.runner.tx_buf_channel.poll_ready_to_receive(cx).is_ready() { | 140 | if self.tx_buf_channel.poll_ready_to_receive(cx).is_ready() { |
| 55 | Some(TxToken { | 141 | Some(TxToken { |
| 56 | tx: &self.runner.tx_channel, | 142 | tx: self.tx_data_channel, |
| 57 | tx_buf: &self.runner.tx_buf_channel, | 143 | tx_buf: self.tx_buf_channel, |
| 58 | }) | 144 | }) |
| 59 | } else { | 145 | } else { |
| 60 | None | 146 | None |
| @@ -68,13 +154,20 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { | |||
| 68 | caps | 154 | caps |
| 69 | } | 155 | } |
| 70 | 156 | ||
| 71 | fn link_state(&mut self, _cx: &mut Context) -> LinkState { | 157 | fn link_state(&mut self, cx: &mut Context) -> LinkState { |
| 72 | LinkState::Down | 158 | critical_section::with(|cs| { |
| 159 | let network_state = self.network_state.borrow(cs).borrow_mut(); | ||
| 160 | |||
| 161 | // Unconditionally register the waker to avoid a race | ||
| 162 | network_state.link_waker.register(cx.waker()); | ||
| 163 | network_state.link_state | ||
| 164 | }) | ||
| 73 | } | 165 | } |
| 74 | 166 | ||
| 75 | fn hardware_address(&self) -> HardwareAddress { | 167 | fn hardware_address(&self) -> HardwareAddress { |
| 76 | // self.mac_addr | 168 | HardwareAddress::Ieee802154(critical_section::with(|cs| { |
| 77 | HardwareAddress::Ieee802154([0; 8]) | 169 | self.network_state.borrow(cs).borrow().mac_addr |
| 170 | })) | ||
| 78 | } | 171 | } |
| 79 | } | 172 | } |
| 80 | 173 | ||
| @@ -87,20 +180,20 @@ impl<'d> embassy_net_driver::RxToken for RxToken<'d> { | |||
| 87 | where | 180 | where |
| 88 | F: FnOnce(&mut [u8]) -> R, | 181 | F: FnOnce(&mut [u8]) -> R, |
| 89 | { | 182 | { |
| 90 | // Only valid data events should be put into the queue | 183 | let mut buffer = [0u8; MTU]; |
| 91 | 184 | match self.rx.try_receive().unwrap() { | |
| 92 | let data_event = match self.rx.try_receive().unwrap() { | 185 | MacEvent::McpsDataInd(data_event) => write_frame_from_data_indication(data_event, &mut buffer), |
| 93 | MacEvent::McpsDataInd(data_event) => data_event, | 186 | MacEvent::MlmeBeaconNotifyInd(data_event) => write_frame_from_beacon_indication(data_event, &mut buffer), |
| 94 | _ => unreachable!(), | 187 | _ => {} |
| 95 | }; | 188 | }; |
| 96 | 189 | ||
| 97 | f(&mut data_event.payload()) | 190 | f(&mut buffer[..]) |
| 98 | } | 191 | } |
| 99 | } | 192 | } |
| 100 | 193 | ||
| 101 | pub struct TxToken<'d> { | 194 | pub struct TxToken<'d> { |
| 102 | tx: &'d Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), 5>, | 195 | tx: &'d Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), BUF_SIZE>, |
| 103 | tx_buf: &'d Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], 5>, | 196 | tx_buf: &'d Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], BUF_SIZE>, |
| 104 | } | 197 | } |
| 105 | 198 | ||
| 106 | impl<'d> embassy_net_driver::TxToken for TxToken<'d> { | 199 | impl<'d> embassy_net_driver::TxToken for TxToken<'d> { |
diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/mac/event.rs index 9ca4f5a2a..39856e185 100644 --- a/embassy-stm32-wpan/src/mac/event.rs +++ b/embassy-stm32-wpan/src/mac/event.rs | |||
| @@ -10,7 +10,7 @@ use super::responses::{ | |||
| 10 | }; | 10 | }; |
| 11 | use crate::evt::{EvtBox, MemoryManager}; | 11 | use crate::evt::{EvtBox, MemoryManager}; |
| 12 | use crate::mac::opcodes::OpcodeM0ToM4; | 12 | use crate::mac::opcodes::OpcodeM0ToM4; |
| 13 | use crate::sub::mac::{self, Mac}; | 13 | use crate::sub::mac::{self, MacRx}; |
| 14 | 14 | ||
| 15 | pub(crate) trait ParseableMacEvent: Sized { | 15 | pub(crate) trait ParseableMacEvent: Sized { |
| 16 | fn from_buffer<'a>(buf: &'a [u8]) -> Result<&'a Self, ()> { | 16 | fn from_buffer<'a>(buf: &'a [u8]) -> Result<&'a Self, ()> { |
| @@ -53,7 +53,7 @@ pub enum MacEvent<'a> { | |||
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | impl<'a> MacEvent<'a> { | 55 | impl<'a> MacEvent<'a> { |
| 56 | pub(crate) fn new(event_box: EvtBox<Mac>) -> Result<Self, ()> { | 56 | pub(crate) fn new(event_box: EvtBox<MacRx>) -> Result<Self, ()> { |
| 57 | let payload = event_box.payload(); | 57 | let payload = event_box.payload(); |
| 58 | let opcode = u16::from_le_bytes(payload[0..2].try_into().unwrap()); | 58 | let opcode = u16::from_le_bytes(payload[0..2].try_into().unwrap()); |
| 59 | 59 | ||
| @@ -148,6 +148,6 @@ unsafe impl<'a> Send for MacEvent<'a> {} | |||
| 148 | 148 | ||
| 149 | impl<'a> Drop for MacEvent<'a> { | 149 | impl<'a> Drop for MacEvent<'a> { |
| 150 | fn drop(&mut self) { | 150 | fn drop(&mut self) { |
| 151 | unsafe { mac::Mac::drop_event_packet(ptr::null_mut()) }; | 151 | unsafe { mac::MacRx::drop_event_packet(ptr::null_mut()) }; |
| 152 | } | 152 | } |
| 153 | } | 153 | } |
diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index c0b86d745..5673514c9 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs | |||
| @@ -1,11 +1,15 @@ | |||
| 1 | use core::slice; | 1 | use core::slice; |
| 2 | 2 | ||
| 3 | use smoltcp::wire::Ieee802154FrameType; | ||
| 4 | use smoltcp::wire::ieee802154::Frame; | ||
| 5 | |||
| 3 | use super::consts::MAX_PENDING_ADDRESS; | 6 | use super::consts::MAX_PENDING_ADDRESS; |
| 4 | use super::event::ParseableMacEvent; | 7 | use super::event::ParseableMacEvent; |
| 5 | use super::typedefs::{ | 8 | use super::typedefs::{ |
| 6 | AddressMode, Capabilities, DisassociationReason, KeyIdMode, MacAddress, MacChannel, MacStatus, PanDescriptor, | 9 | AddressMode, Capabilities, DisassociationReason, KeyIdMode, MacAddress, MacChannel, MacStatus, PanDescriptor, |
| 7 | PanId, SecurityLevel, | 10 | PanId, SecurityLevel, |
| 8 | }; | 11 | }; |
| 12 | use crate::mac::typedefs::MacAddressAndMode; | ||
| 9 | 13 | ||
| 10 | /// MLME ASSOCIATE Indication which will be used by the MAC | 14 | /// MLME ASSOCIATE Indication which will be used by the MAC |
| 11 | /// to indicate the reception of an association request command | 15 | /// to indicate the reception of an association request command |
| @@ -74,6 +78,22 @@ pub struct BeaconNotifyIndication { | |||
| 74 | 78 | ||
| 75 | impl ParseableMacEvent for BeaconNotifyIndication {} | 79 | impl ParseableMacEvent for BeaconNotifyIndication {} |
| 76 | 80 | ||
| 81 | impl BeaconNotifyIndication { | ||
| 82 | pub fn payload<'a>(&'a self) -> &'a mut [u8] { | ||
| 83 | unsafe { slice::from_raw_parts_mut(self.sdu_ptr as *mut _, self.sdu_length as usize) } | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | pub fn write_frame_from_beacon_indication<'a, T: AsRef<[u8]> + AsMut<[u8]>>( | ||
| 88 | data: &'a BeaconNotifyIndication, | ||
| 89 | buffer: &'a mut T, | ||
| 90 | ) { | ||
| 91 | let mut frame = Frame::new_unchecked(buffer); | ||
| 92 | |||
| 93 | frame.set_frame_type(Ieee802154FrameType::Beacon); | ||
| 94 | frame.set_sequence_number(data.bsn); | ||
| 95 | } | ||
| 96 | |||
| 77 | /// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status | 97 | /// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status |
| 78 | #[repr(C)] | 98 | #[repr(C)] |
| 79 | #[derive(Debug)] | 99 | #[derive(Debug)] |
| @@ -250,6 +270,21 @@ impl DataIndication { | |||
| 250 | } | 270 | } |
| 251 | } | 271 | } |
| 252 | 272 | ||
| 273 | pub fn write_frame_from_data_indication<'a, T: AsRef<[u8]> + AsMut<[u8]>>(data: &'a DataIndication, buffer: &'a mut T) { | ||
| 274 | let mut frame = Frame::new_unchecked(buffer); | ||
| 275 | |||
| 276 | frame.set_frame_type(Ieee802154FrameType::Data); | ||
| 277 | frame.set_src_addr(MacAddressAndMode(data.src_address, data.src_addr_mode).into()); | ||
| 278 | frame.set_dst_addr(MacAddressAndMode(data.dst_address, data.dst_addr_mode).into()); | ||
| 279 | frame.set_dst_pan_id(data.dst_pan_id.into()); | ||
| 280 | frame.set_src_pan_id(data.src_pan_id.into()); | ||
| 281 | frame.set_sequence_number(data.dsn); | ||
| 282 | frame.set_security_enabled(data.security_level == SecurityLevel::Secured); | ||
| 283 | |||
| 284 | // No way around the copy with the current API | ||
| 285 | frame.payload_mut().unwrap().copy_from_slice(data.payload()); | ||
| 286 | } | ||
| 287 | |||
| 253 | /// MLME POLL Indication which will be used for indicating the Data Request | 288 | /// MLME POLL Indication which will be used for indicating the Data Request |
| 254 | /// reception to upper layer as defined in Zigbee r22 - D.8.2 | 289 | /// reception to upper layer as defined in Zigbee r22 - D.8.2 |
| 255 | #[repr(C)] | 290 | #[repr(C)] |
diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/mac/mod.rs index c847a5cca..ac50a6b29 100644 --- a/embassy-stm32-wpan/src/mac/mod.rs +++ b/embassy-stm32-wpan/src/mac/mod.rs | |||
| @@ -11,11 +11,7 @@ pub mod runner; | |||
| 11 | pub mod typedefs; | 11 | pub mod typedefs; |
| 12 | 12 | ||
| 13 | pub use crate::mac::control::Control; | 13 | pub use crate::mac::control::Control; |
| 14 | use crate::mac::driver::Driver; | 14 | pub use crate::mac::driver::{Driver, DriverState}; |
| 15 | pub use crate::mac::runner::Runner; | 15 | pub use crate::mac::runner::Runner; |
| 16 | 16 | ||
| 17 | const MTU: usize = 127; | 17 | const MTU: usize = 127; |
| 18 | |||
| 19 | pub async fn new<'a>(runner: &'a Runner<'a>) -> (Control<'a>, Driver<'a>) { | ||
| 20 | (Control::new(runner), Driver::new(runner)) | ||
| 21 | } | ||
diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index 2409f994d..3b7d895df 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs | |||
| @@ -6,96 +6,138 @@ use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; | |||
| 6 | use embassy_sync::channel::Channel; | 6 | use embassy_sync::channel::Channel; |
| 7 | use embassy_sync::mutex::Mutex; | 7 | use embassy_sync::mutex::Mutex; |
| 8 | use embassy_sync::signal::Signal; | 8 | use embassy_sync::signal::Signal; |
| 9 | use smoltcp::wire::Ieee802154FrameType; | ||
| 10 | use smoltcp::wire::ieee802154::Frame; | ||
| 9 | 11 | ||
| 10 | use crate::mac::MTU; | 12 | use crate::mac::MTU; |
| 11 | use crate::mac::commands::DataRequest; | 13 | use crate::mac::commands::*; |
| 14 | use crate::mac::driver::NetworkState; | ||
| 12 | use crate::mac::event::MacEvent; | 15 | use crate::mac::event::MacEvent; |
| 13 | use crate::mac::typedefs::{AddressMode, MacAddress, PanId, SecurityLevel}; | 16 | use crate::sub::mac::{MacRx, MacTx}; |
| 14 | use crate::sub::mac::Mac; | ||
| 15 | 17 | ||
| 16 | type ZeroCopyPubSub<M, T> = blocking_mutex::Mutex<M, RefCell<Option<Signal<NoopRawMutex, T>>>>; | 18 | pub type ZeroCopyPubSub<M, T> = blocking_mutex::Mutex<M, RefCell<Option<Signal<NoopRawMutex, T>>>>; |
| 19 | |||
| 20 | pub const BUF_SIZE: usize = 3; | ||
| 17 | 21 | ||
| 18 | pub struct Runner<'a> { | 22 | pub struct Runner<'a> { |
| 19 | pub(crate) mac_subsystem: Mac, | ||
| 20 | // rx event backpressure is already provided through the MacEvent drop mechanism | 23 | // rx event backpressure is already provided through the MacEvent drop mechanism |
| 21 | // therefore, we don't need to worry about overwriting events | 24 | // therefore, we don't need to worry about overwriting events |
| 22 | pub(crate) rx_event_channel: ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>, | 25 | rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>, |
| 23 | pub(crate) read_mutex: Mutex<CriticalSectionRawMutex, ()>, | 26 | rx_data_channel: &'a Channel<CriticalSectionRawMutex, MacEvent<'a>, 1>, |
| 24 | pub(crate) write_mutex: Mutex<CriticalSectionRawMutex, ()>, | 27 | mac_rx: Mutex<NoopRawMutex, &'a mut MacRx<'a>>, |
| 25 | pub(crate) rx_channel: Channel<CriticalSectionRawMutex, MacEvent<'a>, 1>, | 28 | |
| 26 | pub(crate) tx_channel: Channel<CriticalSectionRawMutex, (&'a mut [u8; MTU], usize), 5>, | 29 | tx_data_channel: &'a Channel<CriticalSectionRawMutex, (&'a mut [u8; MTU], usize), BUF_SIZE>, |
| 27 | pub(crate) tx_buf_channel: Channel<CriticalSectionRawMutex, &'a mut [u8; MTU], 5>, | 30 | tx_buf_channel: &'a Channel<CriticalSectionRawMutex, &'a mut [u8; MTU], BUF_SIZE>, |
| 31 | mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx<'a>>, | ||
| 32 | |||
| 33 | #[allow(unused)] | ||
| 34 | network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>, | ||
| 28 | } | 35 | } |
| 29 | 36 | ||
| 30 | impl<'a> Runner<'a> { | 37 | impl<'a> Runner<'a> { |
| 31 | pub fn new(mac: Mac, tx_buf_queue: [&'a mut [u8; MTU]; 5]) -> Self { | 38 | pub(crate) fn new( |
| 32 | let this = Self { | 39 | rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>, |
| 33 | mac_subsystem: mac, | 40 | rx_data_channel: &'a Channel<CriticalSectionRawMutex, MacEvent<'a>, 1>, |
| 34 | rx_event_channel: blocking_mutex::Mutex::new(RefCell::new(None)), | 41 | mac_rx: &'a mut MacRx<'a>, |
| 35 | read_mutex: Mutex::new(()), | 42 | tx_data_channel: &'a Channel<CriticalSectionRawMutex, (&'a mut [u8; MTU], usize), BUF_SIZE>, |
| 36 | write_mutex: Mutex::new(()), | 43 | tx_buf_channel: &'a Channel<CriticalSectionRawMutex, &'a mut [u8; MTU], BUF_SIZE>, |
| 37 | rx_channel: Channel::new(), | 44 | mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx<'a>>, |
| 38 | tx_channel: Channel::new(), | 45 | tx_buf_queue: &'a mut [[u8; MTU]; BUF_SIZE], |
| 39 | tx_buf_channel: Channel::new(), | 46 | network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>, |
| 40 | }; | 47 | short_address: [u8; 2], |
| 41 | 48 | mac_address: [u8; 8], | |
| 49 | ) -> Self { | ||
| 42 | for buf in tx_buf_queue { | 50 | for buf in tx_buf_queue { |
| 43 | this.tx_buf_channel.try_send(buf).unwrap(); | 51 | tx_buf_channel.try_send(buf).unwrap(); |
| 52 | } | ||
| 53 | |||
| 54 | critical_section::with(|cs| { | ||
| 55 | let mut network_state = network_state.borrow(cs).borrow_mut(); | ||
| 56 | |||
| 57 | network_state.mac_addr = mac_address; | ||
| 58 | network_state.short_addr = short_address; | ||
| 59 | }); | ||
| 60 | |||
| 61 | Self { | ||
| 62 | rx_event_channel, | ||
| 63 | rx_data_channel, | ||
| 64 | mac_rx: Mutex::new(mac_rx), | ||
| 65 | tx_data_channel, | ||
| 66 | tx_buf_channel, | ||
| 67 | mac_tx, | ||
| 68 | network_state, | ||
| 44 | } | 69 | } |
| 70 | } | ||
| 71 | |||
| 72 | async fn send_request<T: MacCommand, U: TryInto<T>>(&self, frame: U) -> Result<(), ()> | ||
| 73 | where | ||
| 74 | (): From<<U as TryInto<T>>::Error>, | ||
| 75 | { | ||
| 76 | let request: T = frame.try_into()?; | ||
| 77 | self.mac_tx.lock().await.send_command(&request).await.map_err(|_| ())?; | ||
| 45 | 78 | ||
| 46 | this | 79 | Ok(()) |
| 47 | } | 80 | } |
| 48 | 81 | ||
| 49 | pub async fn run(&'a self) -> ! { | 82 | pub async fn run(&'a self) -> ! { |
| 50 | join::join( | 83 | join::join( |
| 51 | async { | 84 | async { |
| 52 | loop { | 85 | loop { |
| 53 | if let Ok(mac_event) = self.mac_subsystem.read().await { | 86 | if let Ok(mac_event) = self.mac_rx.try_lock().unwrap().read().await { |
| 54 | match mac_event { | 87 | match mac_event { |
| 88 | MacEvent::MlmeAssociateCnf(_) | ||
| 89 | | MacEvent::MlmeDisassociateCnf(_) | ||
| 90 | | MacEvent::MlmeGetCnf(_) | ||
| 91 | | MacEvent::MlmeGtsCnf(_) | ||
| 92 | | MacEvent::MlmeResetCnf(_) | ||
| 93 | | MacEvent::MlmeRxEnableCnf(_) | ||
| 94 | | MacEvent::MlmeScanCnf(_) | ||
| 95 | | MacEvent::MlmeSetCnf(_) | ||
| 96 | | MacEvent::MlmeStartCnf(_) | ||
| 97 | | MacEvent::MlmePollCnf(_) | ||
| 98 | | MacEvent::MlmeDpsCnf(_) | ||
| 99 | | MacEvent::MlmeSoundingCnf(_) | ||
| 100 | | MacEvent::MlmeCalibrateCnf(_) | ||
| 101 | | MacEvent::McpsDataCnf(_) | ||
| 102 | | MacEvent::McpsPurgeCnf(_) => { | ||
| 103 | self.rx_event_channel.lock(|s| { | ||
| 104 | s.borrow().as_ref().map(|signal| signal.signal(mac_event)); | ||
| 105 | }); | ||
| 106 | } | ||
| 55 | MacEvent::McpsDataInd(_) => { | 107 | MacEvent::McpsDataInd(_) => { |
| 56 | self.rx_channel.send(mac_event).await; | 108 | // Pattern should match driver |
| 109 | self.rx_data_channel.send(mac_event).await; | ||
| 57 | } | 110 | } |
| 58 | _ => { | 111 | _ => { |
| 59 | self.rx_event_channel.lock(|s| { | 112 | debug!("unhandled mac event: {:#x}", mac_event); |
| 60 | match &*s.borrow() { | ||
| 61 | Some(signal) => { | ||
| 62 | signal.signal(mac_event); | ||
| 63 | } | ||
| 64 | None => {} | ||
| 65 | }; | ||
| 66 | }); | ||
| 67 | } | 113 | } |
| 68 | } | 114 | } |
| 69 | } | 115 | } |
| 70 | } | 116 | } |
| 71 | }, | 117 | }, |
| 72 | async { | 118 | async { |
| 73 | let mut msdu_handle = 0x02; | ||
| 74 | |||
| 75 | loop { | 119 | loop { |
| 76 | let (buf, len) = self.tx_channel.receive().await; | 120 | let (buf, _) = self.tx_data_channel.receive().await; |
| 77 | let _wm = self.write_mutex.lock().await; | 121 | |
| 78 | 122 | // Smoltcp has created this frame, so there's no need to reparse it. | |
| 79 | // The mutex should be dropped on the next loop iteration | 123 | let frame = Frame::new_unchecked(&buf); |
| 80 | self.mac_subsystem | ||
| 81 | .send_command( | ||
| 82 | DataRequest { | ||
| 83 | src_addr_mode: AddressMode::Short, | ||
| 84 | dst_addr_mode: AddressMode::Short, | ||
| 85 | dst_pan_id: PanId([0x1A, 0xAA]), | ||
| 86 | dst_address: MacAddress::BROADCAST, | ||
| 87 | msdu_handle: msdu_handle, | ||
| 88 | ack_tx: 0x00, | ||
| 89 | gts_tx: false, | ||
| 90 | security_level: SecurityLevel::Unsecure, | ||
| 91 | ..Default::default() | ||
| 92 | } | ||
| 93 | .set_buffer(&buf[..len]), | ||
| 94 | ) | ||
| 95 | .await | ||
| 96 | .unwrap(); | ||
| 97 | 124 | ||
| 98 | msdu_handle = msdu_handle.wrapping_add(1); | 125 | let result: Result<(), ()> = match frame.frame_type() { |
| 126 | Ieee802154FrameType::Beacon => Err(()), | ||
| 127 | Ieee802154FrameType::Data => self.send_request::<DataRequest, _>(frame).await, | ||
| 128 | Ieee802154FrameType::Acknowledgement => Err(()), | ||
| 129 | Ieee802154FrameType::MacCommand => Err(()), | ||
| 130 | Ieee802154FrameType::Multipurpose => Err(()), | ||
| 131 | Ieee802154FrameType::FragmentOrFrak => Err(()), | ||
| 132 | Ieee802154FrameType::Extended => Err(()), | ||
| 133 | _ => Err(()), | ||
| 134 | }; | ||
| 135 | |||
| 136 | if result.is_err() { | ||
| 137 | debug!("failed to parse mac frame"); | ||
| 138 | } else { | ||
| 139 | trace!("data frame sent!"); | ||
| 140 | } | ||
| 99 | 141 | ||
| 100 | // The tx channel should always be of equal capacity to the tx_buf channel | 142 | // The tx channel should always be of equal capacity to the tx_buf channel |
| 101 | self.tx_buf_channel.try_send(buf).unwrap(); | 143 | self.tx_buf_channel.try_send(buf).unwrap(); |
diff --git a/embassy-stm32-wpan/src/mac/typedefs.rs b/embassy-stm32-wpan/src/mac/typedefs.rs index 0552b8ea1..175d4a37d 100644 --- a/embassy-stm32-wpan/src/mac/typedefs.rs +++ b/embassy-stm32-wpan/src/mac/typedefs.rs | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | use core::fmt::Debug; | 1 | use core::fmt::Debug; |
| 2 | 2 | ||
| 3 | use smoltcp::wire::ieee802154::{Address, AddressingMode, Pan}; | ||
| 4 | |||
| 3 | use crate::numeric_enum; | 5 | use crate::numeric_enum; |
| 4 | 6 | ||
| 5 | #[derive(Debug)] | 7 | #[derive(Debug)] |
| @@ -109,12 +111,51 @@ numeric_enum! { | |||
| 109 | } | 111 | } |
| 110 | } | 112 | } |
| 111 | 113 | ||
| 114 | impl TryFrom<AddressingMode> for AddressMode { | ||
| 115 | type Error = (); | ||
| 116 | |||
| 117 | fn try_from(value: AddressingMode) -> Result<Self, Self::Error> { | ||
| 118 | match value { | ||
| 119 | AddressingMode::Absent => Ok(Self::NoAddress), | ||
| 120 | AddressingMode::Extended => Ok(Self::Extended), | ||
| 121 | AddressingMode::Short => Ok(Self::Short), | ||
| 122 | AddressingMode::Unknown(_) => Err(()), | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 112 | #[derive(Clone, Copy)] | 127 | #[derive(Clone, Copy)] |
| 113 | pub union MacAddress { | 128 | pub union MacAddress { |
| 114 | pub short: [u8; 2], | 129 | pub short: [u8; 2], |
| 115 | pub extended: [u8; 8], | 130 | pub extended: [u8; 8], |
| 116 | } | 131 | } |
| 117 | 132 | ||
| 133 | impl From<Address> for MacAddress { | ||
| 134 | fn from(value: Address) -> Self { | ||
| 135 | match value { | ||
| 136 | Address::Short(addr) => Self { short: addr }, | ||
| 137 | Address::Extended(addr) => Self { extended: addr }, | ||
| 138 | Address::Absent => Self { short: [0u8; 2] }, | ||
| 139 | } | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | pub struct MacAddressAndMode(pub MacAddress, pub AddressMode); | ||
| 144 | |||
| 145 | impl From<MacAddressAndMode> for Address { | ||
| 146 | fn from(mac_address_and_mode: MacAddressAndMode) -> Self { | ||
| 147 | let address = mac_address_and_mode.0; | ||
| 148 | let mode = mac_address_and_mode.1; | ||
| 149 | |||
| 150 | match mode { | ||
| 151 | AddressMode::Short => Address::Short(unsafe { address.short }), | ||
| 152 | AddressMode::Extended => Address::Extended(unsafe { address.extended }), | ||
| 153 | AddressMode::NoAddress => Address::Absent, | ||
| 154 | AddressMode::Reserved => Address::Absent, | ||
| 155 | } | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 118 | impl Debug for MacAddress { | 159 | impl Debug for MacAddress { |
| 119 | fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | 160 | fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| 120 | unsafe { | 161 | unsafe { |
| @@ -346,7 +387,7 @@ numeric_enum! { | |||
| 346 | 387 | ||
| 347 | numeric_enum! { | 388 | numeric_enum! { |
| 348 | #[repr(u8)] | 389 | #[repr(u8)] |
| 349 | #[derive(Default, Clone, Copy, Debug)] | 390 | #[derive(Default, Clone, Copy, Debug, PartialEq)] |
| 350 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 391 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 351 | pub enum SecurityLevel { | 392 | pub enum SecurityLevel { |
| 352 | /// MAC Unsecured Mode Security | 393 | /// MAC Unsecured Mode Security |
| @@ -379,3 +420,15 @@ pub struct PanId(pub [u8; 2]); | |||
| 379 | impl PanId { | 420 | impl PanId { |
| 380 | pub const BROADCAST: Self = Self([0xFF, 0xFF]); | 421 | pub const BROADCAST: Self = Self([0xFF, 0xFF]); |
| 381 | } | 422 | } |
| 423 | |||
| 424 | impl From<Pan> for PanId { | ||
| 425 | fn from(value: Pan) -> Self { | ||
| 426 | Self(value.0.to_be_bytes()) | ||
| 427 | } | ||
| 428 | } | ||
| 429 | |||
| 430 | impl From<PanId> for Pan { | ||
| 431 | fn from(value: PanId) -> Self { | ||
| 432 | Self(u16::from_be_bytes(value.0)) | ||
| 433 | } | ||
| 434 | } | ||
diff --git a/embassy-stm32-wpan/src/shci.rs b/embassy-stm32-wpan/src/shci.rs index 30d689716..2d94a9cda 100644 --- a/embassy-stm32-wpan/src/shci.rs +++ b/embassy-stm32-wpan/src/shci.rs | |||
| @@ -1,6 +1,10 @@ | |||
| 1 | use core::{mem, slice}; | 1 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 2 | use core::{mem, ptr, slice}; | ||
| 2 | 3 | ||
| 4 | use crate::PacketHeader; | ||
| 5 | use crate::cmd::CmdPacket; | ||
| 3 | use crate::consts::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; | 6 | use crate::consts::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; |
| 7 | use crate::evt::{CcEvt, EvtStub}; | ||
| 4 | 8 | ||
| 5 | const SHCI_OGF: u16 = 0x3F; | 9 | const SHCI_OGF: u16 = 0x3F; |
| 6 | 10 | ||
| @@ -21,6 +25,18 @@ pub enum SchiCommandStatus { | |||
| 21 | ShciFusCmdNotSupported = 0xFF, | 25 | ShciFusCmdNotSupported = 0xFF, |
| 22 | } | 26 | } |
| 23 | 27 | ||
| 28 | impl SchiCommandStatus { | ||
| 29 | pub unsafe fn from_packet(cmd_buf: *const CmdPacket) -> Result<Self, ()> { | ||
| 30 | let p_cmd_serial = (cmd_buf as *mut u8).add(size_of::<PacketHeader>()); | ||
| 31 | let p_evt_payload = p_cmd_serial.add(size_of::<EvtStub>()); | ||
| 32 | |||
| 33 | compiler_fence(Ordering::Acquire); | ||
| 34 | let cc_evt = ptr::read_unaligned(p_evt_payload as *const CcEvt); | ||
| 35 | |||
| 36 | cc_evt.payload[0].try_into() | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 24 | impl TryFrom<u8> for SchiCommandStatus { | 40 | impl TryFrom<u8> for SchiCommandStatus { |
| 25 | type Error = (); | 41 | type Error = (); |
| 26 | 42 | ||
| @@ -274,69 +290,64 @@ pub struct ShciBleInitCmdParam { | |||
| 274 | pub options: u8, | 290 | pub options: u8, |
| 275 | /// Reserved for future use - shall be set to 0 | 291 | /// Reserved for future use - shall be set to 0 |
| 276 | pub hw_version: u8, | 292 | pub hw_version: u8, |
| 277 | // /** | 293 | /// |
| 278 | // * Maximum number of connection-oriented channels in initiator mode. | 294 | /// Maximum number of connection-oriented channels in initiator mode. |
| 279 | // * Range: 0 .. 64 | 295 | /// Range: 0 .. 64 |
| 280 | // */ | 296 | pub max_coc_initiator_nbr: u8, |
| 281 | // pub max_coc_initiator_nbr: u8, | 297 | |
| 282 | // | 298 | /// |
| 283 | // /** | 299 | /// Minimum transmit power in dBm supported by the Controller. |
| 284 | // * Minimum transmit power in dBm supported by the Controller. | 300 | /// Range: -127 .. 20 |
| 285 | // * Range: -127 .. 20 | 301 | pub min_tx_power: i8, |
| 286 | // */ | 302 | |
| 287 | // pub min_tx_power: i8, | 303 | /// |
| 288 | // | 304 | /// Maximum transmit power in dBm supported by the Controller. |
| 289 | // /** | 305 | /// Range: -127 .. 20 |
| 290 | // * Maximum transmit power in dBm supported by the Controller. | 306 | pub max_tx_power: i8, |
| 291 | // * Range: -127 .. 20 | 307 | |
| 292 | // */ | 308 | /// |
| 293 | // pub max_tx_power: i8, | 309 | /// RX model configuration |
| 294 | // | 310 | /// - bit 0: 1: agc_rssi model improved vs RF blockers 0: Legacy agc_rssi model |
| 295 | // /** | 311 | /// - other bits: reserved ( shall be set to 0) |
| 296 | // * RX model configuration | 312 | pub rx_model_config: u8, |
| 297 | // * - bit 0: 1: agc_rssi model improved vs RF blockers 0: Legacy agc_rssi model | 313 | |
| 298 | // * - other bits: reserved ( shall be set to 0) | 314 | /// Maximum number of advertising sets. |
| 299 | // */ | 315 | /// Range: 1 .. 8 with limitation: |
| 300 | // pub rx_model_config: u8, | 316 | /// This parameter is linked to max_adv_data_len such as both compliant with allocated Total memory computed with BLE_EXT_ADV_BUFFER_SIZE based |
| 301 | // | 317 | /// on Max Extended advertising configuration supported. |
| 302 | // /* Maximum number of advertising sets. | 318 | /// This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set |
| 303 | // * Range: 1 .. 8 with limitation: | 319 | pub max_adv_set_nbr: u8, |
| 304 | // * This parameter is linked to max_adv_data_len such as both compliant with allocated Total memory computed with BLE_EXT_ADV_BUFFER_SIZE based | 320 | |
| 305 | // * on Max Extended advertising configuration supported. | 321 | /// Maximum advertising data length (in bytes) |
| 306 | // * This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set | 322 | /// Range: 31 .. 1650 with limitation: |
| 307 | // */ | 323 | /// This parameter is linked to max_adv_set_nbr such as both compliant with allocated Total memory computed with BLE_EXT_ADV_BUFFER_SIZE based |
| 308 | // pub max_adv_set_nbr: u8, | 324 | /// on Max Extended advertising configuration supported. |
| 309 | // | 325 | /// This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set |
| 310 | // /* Maximum advertising data length (in bytes) | 326 | pub max_adv_data_len: u16, |
| 311 | // * Range: 31 .. 1650 with limitation: | 327 | |
| 312 | // * This parameter is linked to max_adv_set_nbr such as both compliant with allocated Total memory computed with BLE_EXT_ADV_BUFFER_SIZE based | 328 | /// RF TX Path Compensation Value (16-bit signed integer). Units: 0.1 dB. |
| 313 | // * on Max Extended advertising configuration supported. | 329 | /// Range: -1280 .. 1280 |
| 314 | // * This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set | 330 | pub tx_path_compens: i16, |
| 315 | // */ | 331 | |
| 316 | // pub max_adv_data_len: u16, | 332 | //// RF RX Path Compensation Value (16-bit signed integer). Units: 0.1 dB. |
| 317 | // | 333 | /// Range: -1280 .. 1280 |
| 318 | // /* RF TX Path Compensation Value (16-bit signed integer). Units: 0.1 dB. | 334 | pub rx_path_compens: i16, |
| 319 | // * Range: -1280 .. 1280 | 335 | |
| 320 | // */ | 336 | /// BLE core specification version (8-bit unsigned integer). |
| 321 | // pub tx_path_compens: i16, | 337 | /// values as: 11(5.2), 12(5.3) |
| 322 | // | 338 | pub ble_core_version: u8, |
| 323 | // /* RF RX Path Compensation Value (16-bit signed integer). Units: 0.1 dB. | 339 | |
| 324 | // * Range: -1280 .. 1280 | 340 | /// Options flags extension |
| 325 | // */ | 341 | /// - bit 0: 1: appearance Writable 0: appearance Read-Only |
| 326 | // pub rx_path_compens: i16, | 342 | /// - bit 1: 1: Enhanced ATT supported 0: Enhanced ATT not supported |
| 327 | // | 343 | /// - other bits: reserved ( shall be set to 0) |
| 328 | // /* BLE core specification version (8-bit unsigned integer). | 344 | pub options_extension: u8, |
| 329 | // * values as: 11(5.2), 12(5.3) | 345 | |
| 330 | // */ | 346 | /// MaxAddEattBearers |
| 331 | // pub ble_core_version: u8, | 347 | /// Maximum number of bearers that can be created for Enhanced ATT |
| 332 | // | 348 | /// in addition to the number of links |
| 333 | // /** | 349 | /// - Range: 0 .. 4 |
| 334 | // * Options flags extension | 350 | pub max_add_eatt_bearers: u8, |
| 335 | // * - bit 0: 1: appearance Writable 0: appearance Read-Only | ||
| 336 | // * - bit 1: 1: Enhanced ATT supported 0: Enhanced ATT not supported | ||
| 337 | // * - other bits: reserved ( shall be set to 0) | ||
| 338 | // */ | ||
| 339 | // pub options_extension: u8, | ||
| 340 | } | 351 | } |
| 341 | 352 | ||
| 342 | impl ShciBleInitCmdParam { | 353 | impl ShciBleInitCmdParam { |
| @@ -351,7 +362,7 @@ impl Default for ShciBleInitCmdParam { | |||
| 351 | p_ble_buffer_address: 0, | 362 | p_ble_buffer_address: 0, |
| 352 | ble_buffer_size: 0, | 363 | ble_buffer_size: 0, |
| 353 | num_attr_record: 68, | 364 | num_attr_record: 68, |
| 354 | num_attr_serv: 8, | 365 | num_attr_serv: 4, |
| 355 | attr_value_arr_size: 1344, | 366 | attr_value_arr_size: 1344, |
| 356 | num_of_links: 2, | 367 | num_of_links: 2, |
| 357 | extended_packet_length_enable: 1, | 368 | extended_packet_length_enable: 1, |
| @@ -366,6 +377,17 @@ impl Default for ShciBleInitCmdParam { | |||
| 366 | viterbi_enable: 1, | 377 | viterbi_enable: 1, |
| 367 | options: 0, | 378 | options: 0, |
| 368 | hw_version: 0, | 379 | hw_version: 0, |
| 380 | max_coc_initiator_nbr: 32, | ||
| 381 | min_tx_power: -40, | ||
| 382 | max_tx_power: 6, | ||
| 383 | rx_model_config: 0, | ||
| 384 | max_adv_set_nbr: 2, | ||
| 385 | max_adv_data_len: 1650, | ||
| 386 | tx_path_compens: 0, | ||
| 387 | rx_path_compens: 0, | ||
| 388 | ble_core_version: 11, | ||
| 389 | options_extension: 0, | ||
| 390 | max_add_eatt_bearers: 4, | ||
| 369 | } | 391 | } |
| 370 | } | 392 | } |
| 371 | } | 393 | } |
diff --git a/embassy-stm32-wpan/src/sub/ble.rs b/embassy-stm32-wpan/src/sub/ble.rs index cd69a0479..afc4a510a 100644 --- a/embassy-stm32-wpan/src/sub/ble.rs +++ b/embassy-stm32-wpan/src/sub/ble.rs | |||
| @@ -1,15 +1,15 @@ | |||
| 1 | use core::ptr; | 1 | use core::ptr; |
| 2 | 2 | ||
| 3 | use embassy_stm32::ipcc::Ipcc; | 3 | use embassy_stm32::ipcc::{IpccRxChannel, IpccTxChannel}; |
| 4 | use hci::Opcode; | 4 | use hci::Opcode; |
| 5 | 5 | ||
| 6 | use crate::cmd::CmdPacket; | 6 | use crate::cmd::CmdPacket; |
| 7 | use crate::consts::{TL_BLEEVT_CC_OPCODE, TL_BLEEVT_CS_OPCODE, TlPacketType}; | 7 | use crate::consts::{TL_BLEEVT_CC_OPCODE, TL_BLEEVT_CS_OPCODE, TlPacketType}; |
| 8 | use crate::evt; | ||
| 8 | use crate::evt::{EvtBox, EvtPacket, EvtStub}; | 9 | use crate::evt::{EvtBox, EvtPacket, EvtStub}; |
| 9 | use crate::sub::mm; | 10 | use crate::sub::mm; |
| 10 | use crate::tables::{BLE_CMD_BUFFER, BleTable, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE}; | 11 | use crate::tables::{BLE_CMD_BUFFER, BleTable, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE}; |
| 11 | use crate::unsafe_linked_list::LinkedListNode; | 12 | use crate::unsafe_linked_list::LinkedListNode; |
| 12 | use crate::{channels, evt}; | ||
| 13 | 13 | ||
| 14 | /// A guard that, once constructed, may be used to send BLE commands to CPU2. | 14 | /// A guard that, once constructed, may be used to send BLE commands to CPU2. |
| 15 | /// | 15 | /// |
| @@ -36,15 +36,21 @@ use crate::{channels, evt}; | |||
| 36 | /// # mbox.ble_subsystem.reset().await; | 36 | /// # mbox.ble_subsystem.reset().await; |
| 37 | /// # let _reset_response = mbox.ble_subsystem.read().await; | 37 | /// # let _reset_response = mbox.ble_subsystem.read().await; |
| 38 | /// ``` | 38 | /// ``` |
| 39 | pub struct Ble { | 39 | pub struct Ble<'a> { |
| 40 | _private: (), | 40 | hw_ipcc_ble_cmd_channel: IpccTxChannel<'a>, |
| 41 | ipcc_ble_event_channel: IpccRxChannel<'a>, | ||
| 42 | ipcc_hci_acl_data_channel: IpccTxChannel<'a>, | ||
| 41 | } | 43 | } |
| 42 | 44 | ||
| 43 | impl Ble { | 45 | impl<'a> Ble<'a> { |
| 44 | /// Constructs a guard that allows for BLE commands to be sent to CPU2. | 46 | /// Constructs a guard that allows for BLE commands to be sent to CPU2. |
| 45 | /// | 47 | /// |
| 46 | /// This takes the place of `TL_BLE_Init`, completing that step as laid out in AN5289, Fig 66. | 48 | /// This takes the place of `TL_BLE_Init`, completing that step as laid out in AN5289, Fig 66. |
| 47 | pub(crate) fn new() -> Self { | 49 | pub(crate) fn new( |
| 50 | hw_ipcc_ble_cmd_channel: IpccTxChannel<'a>, | ||
| 51 | ipcc_ble_event_channel: IpccRxChannel<'a>, | ||
| 52 | ipcc_hci_acl_data_channel: IpccTxChannel<'a>, | ||
| 53 | ) -> Self { | ||
| 48 | unsafe { | 54 | unsafe { |
| 49 | LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); | 55 | LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); |
| 50 | 56 | ||
| @@ -56,44 +62,51 @@ impl Ble { | |||
| 56 | }); | 62 | }); |
| 57 | } | 63 | } |
| 58 | 64 | ||
| 59 | Self { _private: () } | 65 | Self { |
| 66 | hw_ipcc_ble_cmd_channel, | ||
| 67 | ipcc_ble_event_channel, | ||
| 68 | ipcc_hci_acl_data_channel, | ||
| 69 | } | ||
| 60 | } | 70 | } |
| 61 | 71 | ||
| 62 | /// `HW_IPCC_BLE_EvtNot` | 72 | /// `HW_IPCC_BLE_EvtNot` |
| 63 | pub async fn tl_read(&self) -> EvtBox<Self> { | 73 | pub async fn tl_read(&mut self) -> EvtBox<Self> { |
| 64 | Ipcc::receive(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, || unsafe { | 74 | self.ipcc_ble_event_channel |
| 65 | if let Some(node_ptr) = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()) { | 75 | .receive(|| unsafe { |
| 66 | Some(EvtBox::new(node_ptr.cast())) | 76 | if let Some(node_ptr) = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()) { |
| 67 | } else { | 77 | Some(EvtBox::new(node_ptr.cast())) |
| 68 | None | 78 | } else { |
| 69 | } | 79 | None |
| 70 | }) | 80 | } |
| 71 | .await | 81 | }) |
| 82 | .await | ||
| 72 | } | 83 | } |
| 73 | 84 | ||
| 74 | /// `TL_BLE_SendCmd` | 85 | /// `TL_BLE_SendCmd` |
| 75 | pub async fn tl_write(&self, opcode: u16, payload: &[u8]) { | 86 | pub async fn tl_write(&mut self, opcode: u16, payload: &[u8]) { |
| 76 | Ipcc::send(channels::cpu1::IPCC_BLE_CMD_CHANNEL, || unsafe { | 87 | self.hw_ipcc_ble_cmd_channel |
| 77 | CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload); | 88 | .send(|| unsafe { |
| 78 | }) | 89 | CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload); |
| 79 | .await; | 90 | }) |
| 91 | .await; | ||
| 80 | } | 92 | } |
| 81 | 93 | ||
| 82 | /// `TL_BLE_SendAclData` | 94 | /// `TL_BLE_SendAclData` |
| 83 | pub async fn acl_write(&self, handle: u16, payload: &[u8]) { | 95 | pub async fn acl_write(&mut self, handle: u16, payload: &[u8]) { |
| 84 | Ipcc::send(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL, || unsafe { | 96 | self.ipcc_hci_acl_data_channel |
| 85 | CmdPacket::write_into( | 97 | .send(|| unsafe { |
| 86 | HCI_ACL_DATA_BUFFER.as_mut_ptr() as *mut _, | 98 | CmdPacket::write_into( |
| 87 | TlPacketType::AclData, | 99 | HCI_ACL_DATA_BUFFER.as_mut_ptr() as *mut _, |
| 88 | handle, | 100 | TlPacketType::AclData, |
| 89 | payload, | 101 | handle, |
| 90 | ); | 102 | payload, |
| 91 | }) | 103 | ); |
| 92 | .await; | 104 | }) |
| 105 | .await; | ||
| 93 | } | 106 | } |
| 94 | } | 107 | } |
| 95 | 108 | ||
| 96 | impl evt::MemoryManager for Ble { | 109 | impl<'a> evt::MemoryManager for Ble<'a> { |
| 97 | /// SAFETY: passing a pointer to something other than a managed event packet is UB | 110 | /// SAFETY: passing a pointer to something other than a managed event packet is UB |
| 98 | unsafe fn drop_event_packet(evt: *mut EvtPacket) { | 111 | unsafe fn drop_event_packet(evt: *mut EvtPacket) { |
| 99 | let stub = unsafe { | 112 | let stub = unsafe { |
| @@ -110,13 +123,17 @@ impl evt::MemoryManager for Ble { | |||
| 110 | 123 | ||
| 111 | pub extern crate stm32wb_hci as hci; | 124 | pub extern crate stm32wb_hci as hci; |
| 112 | 125 | ||
| 113 | impl hci::Controller for Ble { | 126 | impl<'a> hci::Controller for Ble<'a> { |
| 114 | async fn controller_write(&mut self, opcode: Opcode, payload: &[u8]) { | 127 | async fn controller_write(&mut self, opcode: Opcode, payload: &[u8]) { |
| 115 | self.tl_write(opcode.0, payload).await; | 128 | self.tl_write(opcode.0, payload).await; |
| 116 | } | 129 | } |
| 117 | 130 | ||
| 131 | #[allow(invalid_reference_casting)] | ||
| 118 | async fn controller_read_into(&self, buf: &mut [u8]) { | 132 | async fn controller_read_into(&self, buf: &mut [u8]) { |
| 119 | let evt_box = self.tl_read().await; | 133 | // A complete hack since I cannot update the trait |
| 134 | let s = unsafe { &mut *(self as *const _ as *mut Ble) }; | ||
| 135 | |||
| 136 | let evt_box = s.tl_read().await; | ||
| 120 | let evt_serial = evt_box.serial(); | 137 | let evt_serial = evt_box.serial(); |
| 121 | 138 | ||
| 122 | buf[..evt_serial.len()].copy_from_slice(evt_serial); | 139 | buf[..evt_serial.len()].copy_from_slice(evt_serial); |
diff --git a/embassy-stm32-wpan/src/sub/mac.rs b/embassy-stm32-wpan/src/sub/mac.rs index baf4da979..ce2903e61 100644 --- a/embassy-stm32-wpan/src/sub/mac.rs +++ b/embassy-stm32-wpan/src/sub/mac.rs | |||
| @@ -4,60 +4,78 @@ use core::sync::atomic::{AtomicBool, Ordering}; | |||
| 4 | use core::task::Poll; | 4 | use core::task::Poll; |
| 5 | 5 | ||
| 6 | use embassy_futures::poll_once; | 6 | use embassy_futures::poll_once; |
| 7 | use embassy_stm32::ipcc::Ipcc; | 7 | use embassy_stm32::ipcc::{Ipcc, IpccRxChannel, IpccTxChannel}; |
| 8 | use embassy_sync::waitqueue::AtomicWaker; | 8 | use embassy_sync::waitqueue::AtomicWaker; |
| 9 | 9 | ||
| 10 | use crate::cmd::CmdPacket; | 10 | use crate::cmd::CmdPacket; |
| 11 | use crate::consts::TlPacketType; | 11 | use crate::consts::TlPacketType; |
| 12 | use crate::evt; | ||
| 12 | use crate::evt::{EvtBox, EvtPacket}; | 13 | use crate::evt::{EvtBox, EvtPacket}; |
| 13 | use crate::mac::commands::MacCommand; | 14 | use crate::mac::commands::MacCommand; |
| 14 | use crate::mac::event::MacEvent; | 15 | use crate::mac::event::MacEvent; |
| 15 | use crate::mac::typedefs::MacError; | 16 | use crate::mac::typedefs::MacError; |
| 16 | use crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER}; | 17 | use crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER}; |
| 17 | use crate::{channels, evt}; | 18 | use crate::unsafe_linked_list::LinkedListNode; |
| 18 | 19 | ||
| 19 | static MAC_WAKER: AtomicWaker = AtomicWaker::new(); | 20 | static MAC_WAKER: AtomicWaker = AtomicWaker::new(); |
| 20 | static MAC_EVT_OUT: AtomicBool = AtomicBool::new(false); | 21 | static MAC_EVT_OUT: AtomicBool = AtomicBool::new(false); |
| 21 | 22 | ||
| 22 | pub struct Mac { | 23 | pub struct Mac<'a> { |
| 23 | _private: (), | 24 | ipcc_mac_802_15_4_cmd_rsp_channel: IpccTxChannel<'a>, |
| 25 | ipcc_mac_802_15_4_notification_ack_channel: IpccRxChannel<'a>, | ||
| 24 | } | 26 | } |
| 25 | 27 | ||
| 26 | impl Mac { | 28 | impl<'a> Mac<'a> { |
| 27 | pub(crate) fn new() -> Self { | 29 | pub(crate) fn new( |
| 28 | Self { _private: () } | 30 | ipcc_mac_802_15_4_cmd_rsp_channel: IpccTxChannel<'a>, |
| 29 | } | 31 | ipcc_mac_802_15_4_notification_ack_channel: IpccRxChannel<'a>, |
| 30 | 32 | ) -> Self { | |
| 31 | /// `HW_IPCC_MAC_802_15_4_EvtNot` | 33 | use crate::tables::{ |
| 32 | /// | 34 | MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, Mac802_15_4Table, TL_MAC_802_15_4_TABLE, |
| 33 | /// This function will stall if the previous `EvtBox` has not been dropped | 35 | TL_TRACES_TABLE, TRACES_EVT_QUEUE, TracesTable, |
| 34 | pub async fn tl_read(&self) -> EvtBox<Self> { | 36 | }; |
| 35 | // Wait for the last event box to be dropped | ||
| 36 | poll_fn(|cx| { | ||
| 37 | MAC_WAKER.register(cx.waker()); | ||
| 38 | if MAC_EVT_OUT.load(Ordering::SeqCst) { | ||
| 39 | Poll::Pending | ||
| 40 | } else { | ||
| 41 | Poll::Ready(()) | ||
| 42 | } | ||
| 43 | }) | ||
| 44 | .await; | ||
| 45 | 37 | ||
| 46 | // Return a new event box | 38 | unsafe { |
| 47 | Ipcc::receive(channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL, || unsafe { | 39 | LinkedListNode::init_head(TRACES_EVT_QUEUE.as_mut_ptr() as *mut _); |
| 48 | // The closure is not async, therefore the closure must execute to completion (cannot be dropped) | 40 | |
| 49 | // Therefore, the event box is guaranteed to be cleaned up if it's not leaked | 41 | TL_TRACES_TABLE.as_mut_ptr().write_volatile(TracesTable { |
| 50 | MAC_EVT_OUT.store(true, Ordering::SeqCst); | 42 | traces_queue: TRACES_EVT_QUEUE.as_ptr() as *const _, |
| 43 | }); | ||
| 44 | |||
| 45 | TL_MAC_802_15_4_TABLE.as_mut_ptr().write_volatile(Mac802_15_4Table { | ||
| 46 | p_cmdrsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(), | ||
| 47 | p_notack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(), | ||
| 48 | evt_queue: core::ptr::null_mut(), | ||
| 49 | }); | ||
| 50 | }; | ||
| 51 | |||
| 52 | Self { | ||
| 53 | ipcc_mac_802_15_4_cmd_rsp_channel, | ||
| 54 | ipcc_mac_802_15_4_notification_ack_channel, | ||
| 55 | } | ||
| 56 | } | ||
| 51 | 57 | ||
| 52 | Some(EvtBox::new(MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _)) | 58 | pub const fn split(self) -> (MacRx<'a>, MacTx<'a>) { |
| 53 | }) | 59 | ( |
| 54 | .await | 60 | MacRx { |
| 61 | ipcc_mac_802_15_4_notification_ack_channel: self.ipcc_mac_802_15_4_notification_ack_channel, | ||
| 62 | }, | ||
| 63 | MacTx { | ||
| 64 | ipcc_mac_802_15_4_cmd_rsp_channel: self.ipcc_mac_802_15_4_cmd_rsp_channel, | ||
| 65 | }, | ||
| 66 | ) | ||
| 55 | } | 67 | } |
| 68 | } | ||
| 69 | |||
| 70 | pub struct MacTx<'a> { | ||
| 71 | ipcc_mac_802_15_4_cmd_rsp_channel: IpccTxChannel<'a>, | ||
| 72 | } | ||
| 56 | 73 | ||
| 74 | impl<'a> MacTx<'a> { | ||
| 57 | /// `HW_IPCC_MAC_802_15_4_CmdEvtNot` | 75 | /// `HW_IPCC_MAC_802_15_4_CmdEvtNot` |
| 58 | pub async fn tl_write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 { | 76 | pub async fn tl_write_and_get_response(&mut self, opcode: u16, payload: &[u8]) -> u8 { |
| 59 | self.tl_write(opcode, payload).await; | 77 | self.tl_write(opcode, payload).await; |
| 60 | Ipcc::flush(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL).await; | 78 | self.ipcc_mac_802_15_4_cmd_rsp_channel.flush().await; |
| 61 | 79 | ||
| 62 | unsafe { | 80 | unsafe { |
| 63 | let p_event_packet = MAC_802_15_4_CMD_BUFFER.as_ptr() as *const EvtPacket; | 81 | let p_event_packet = MAC_802_15_4_CMD_BUFFER.as_ptr() as *const EvtPacket; |
| @@ -68,19 +86,20 @@ impl Mac { | |||
| 68 | } | 86 | } |
| 69 | 87 | ||
| 70 | /// `TL_MAC_802_15_4_SendCmd` | 88 | /// `TL_MAC_802_15_4_SendCmd` |
| 71 | pub async fn tl_write(&self, opcode: u16, payload: &[u8]) { | 89 | pub async fn tl_write(&mut self, opcode: u16, payload: &[u8]) { |
| 72 | Ipcc::send(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL, || unsafe { | 90 | self.ipcc_mac_802_15_4_cmd_rsp_channel |
| 73 | CmdPacket::write_into( | 91 | .send(|| unsafe { |
| 74 | MAC_802_15_4_CMD_BUFFER.as_mut_ptr(), | 92 | CmdPacket::write_into( |
| 75 | TlPacketType::MacCmd, | 93 | MAC_802_15_4_CMD_BUFFER.as_mut_ptr(), |
| 76 | opcode, | 94 | TlPacketType::MacCmd, |
| 77 | payload, | 95 | opcode, |
| 78 | ); | 96 | payload, |
| 79 | }) | 97 | ); |
| 80 | .await; | 98 | }) |
| 99 | .await; | ||
| 81 | } | 100 | } |
| 82 | 101 | ||
| 83 | pub async fn send_command<T>(&self, cmd: &T) -> Result<(), MacError> | 102 | pub async fn send_command<T>(&mut self, cmd: &T) -> Result<(), MacError> |
| 84 | where | 103 | where |
| 85 | T: MacCommand, | 104 | T: MacCommand, |
| 86 | { | 105 | { |
| @@ -92,13 +111,46 @@ impl Mac { | |||
| 92 | Err(MacError::from(response)) | 111 | Err(MacError::from(response)) |
| 93 | } | 112 | } |
| 94 | } | 113 | } |
| 114 | } | ||
| 115 | |||
| 116 | pub struct MacRx<'a> { | ||
| 117 | ipcc_mac_802_15_4_notification_ack_channel: IpccRxChannel<'a>, | ||
| 118 | } | ||
| 119 | |||
| 120 | impl<'a> MacRx<'a> { | ||
| 121 | /// `HW_IPCC_MAC_802_15_4_EvtNot` | ||
| 122 | /// | ||
| 123 | /// This function will stall if the previous `EvtBox` has not been dropped | ||
| 124 | pub async fn tl_read(&mut self) -> EvtBox<MacRx<'a>> { | ||
| 125 | // Wait for the last event box to be dropped | ||
| 126 | poll_fn(|cx| { | ||
| 127 | MAC_WAKER.register(cx.waker()); | ||
| 128 | if MAC_EVT_OUT.load(Ordering::Acquire) { | ||
| 129 | Poll::Pending | ||
| 130 | } else { | ||
| 131 | Poll::Ready(()) | ||
| 132 | } | ||
| 133 | }) | ||
| 134 | .await; | ||
| 135 | |||
| 136 | // Return a new event box | ||
| 137 | self.ipcc_mac_802_15_4_notification_ack_channel | ||
| 138 | .receive(|| unsafe { | ||
| 139 | // The closure is not async, therefore the closure must execute to completion (cannot be dropped) | ||
| 140 | // Therefore, the event box is guaranteed to be cleaned up if it's not leaked | ||
| 141 | MAC_EVT_OUT.store(true, Ordering::SeqCst); | ||
| 142 | |||
| 143 | Some(EvtBox::new(MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _)) | ||
| 144 | }) | ||
| 145 | .await | ||
| 146 | } | ||
| 95 | 147 | ||
| 96 | pub async fn read(&self) -> Result<MacEvent<'_>, ()> { | 148 | pub async fn read<'b>(&mut self) -> Result<MacEvent<'b>, ()> { |
| 97 | MacEvent::new(self.tl_read().await) | 149 | MacEvent::new(self.tl_read().await) |
| 98 | } | 150 | } |
| 99 | } | 151 | } |
| 100 | 152 | ||
| 101 | impl evt::MemoryManager for Mac { | 153 | impl<'a> evt::MemoryManager for MacRx<'a> { |
| 102 | /// SAFETY: passing a pointer to something other than a managed event packet is UB | 154 | /// SAFETY: passing a pointer to something other than a managed event packet is UB |
| 103 | unsafe fn drop_event_packet(_: *mut EvtPacket) { | 155 | unsafe fn drop_event_packet(_: *mut EvtPacket) { |
| 104 | trace!("mac drop event"); | 156 | trace!("mac drop event"); |
| @@ -112,13 +164,10 @@ impl evt::MemoryManager for Mac { | |||
| 112 | ); | 164 | ); |
| 113 | 165 | ||
| 114 | // Clear the rx flag | 166 | // Clear the rx flag |
| 115 | let _ = poll_once(Ipcc::receive::<()>( | 167 | let _ = poll_once(Ipcc::receive::<()>(3, || None)); |
| 116 | channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL, | ||
| 117 | || None, | ||
| 118 | )); | ||
| 119 | 168 | ||
| 120 | // Allow a new read call | 169 | // Allow a new read call |
| 121 | MAC_EVT_OUT.store(false, Ordering::SeqCst); | 170 | MAC_EVT_OUT.store(false, Ordering::Release); |
| 122 | MAC_WAKER.wake(); | 171 | MAC_WAKER.wake(); |
| 123 | } | 172 | } |
| 124 | } | 173 | } |
diff --git a/embassy-stm32-wpan/src/sub/mm.rs b/embassy-stm32-wpan/src/sub/mm.rs index 62d0de8bd..aac252929 100644 --- a/embassy-stm32-wpan/src/sub/mm.rs +++ b/embassy-stm32-wpan/src/sub/mm.rs | |||
| @@ -5,26 +5,26 @@ use core::task::Poll; | |||
| 5 | 5 | ||
| 6 | use aligned::{A4, Aligned}; | 6 | use aligned::{A4, Aligned}; |
| 7 | use cortex_m::interrupt; | 7 | use cortex_m::interrupt; |
| 8 | use embassy_stm32::ipcc::Ipcc; | 8 | use embassy_stm32::ipcc::IpccTxChannel; |
| 9 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 10 | 10 | ||
| 11 | use crate::consts::POOL_SIZE; | 11 | use crate::consts::POOL_SIZE; |
| 12 | use crate::evt; | ||
| 12 | use crate::evt::EvtPacket; | 13 | use crate::evt::EvtPacket; |
| 13 | #[cfg(feature = "ble")] | 14 | #[cfg(feature = "ble")] |
| 14 | use crate::tables::BLE_SPARE_EVT_BUF; | 15 | use crate::tables::BLE_SPARE_EVT_BUF; |
| 15 | use crate::tables::{EVT_POOL, FREE_BUF_QUEUE, MemManagerTable, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE}; | 16 | use crate::tables::{EVT_POOL, FREE_BUF_QUEUE, MemManagerTable, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE}; |
| 16 | use crate::unsafe_linked_list::LinkedListNode; | 17 | use crate::unsafe_linked_list::LinkedListNode; |
| 17 | use crate::{channels, evt}; | ||
| 18 | 18 | ||
| 19 | static MM_WAKER: AtomicWaker = AtomicWaker::new(); | 19 | static MM_WAKER: AtomicWaker = AtomicWaker::new(); |
| 20 | static mut LOCAL_FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit()); | 20 | static mut LOCAL_FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit()); |
| 21 | 21 | ||
| 22 | pub struct MemoryManager { | 22 | pub struct MemoryManager<'a> { |
| 23 | _private: (), | 23 | ipcc_mm_release_buffer_channel: IpccTxChannel<'a>, |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | impl MemoryManager { | 26 | impl<'a> MemoryManager<'a> { |
| 27 | pub(crate) fn new() -> Self { | 27 | pub(crate) fn new(ipcc_mm_release_buffer_channel: IpccTxChannel<'a>) -> Self { |
| 28 | unsafe { | 28 | unsafe { |
| 29 | LinkedListNode::init_head(FREE_BUF_QUEUE.as_mut_ptr()); | 29 | LinkedListNode::init_head(FREE_BUF_QUEUE.as_mut_ptr()); |
| 30 | LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); | 30 | LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); |
| @@ -43,10 +43,12 @@ impl MemoryManager { | |||
| 43 | }); | 43 | }); |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | Self { _private: () } | 46 | Self { |
| 47 | ipcc_mm_release_buffer_channel, | ||
| 48 | } | ||
| 47 | } | 49 | } |
| 48 | 50 | ||
| 49 | pub async fn run_queue(&self) { | 51 | pub async fn run_queue(&mut self) -> ! { |
| 50 | loop { | 52 | loop { |
| 51 | poll_fn(|cx| unsafe { | 53 | poll_fn(|cx| unsafe { |
| 52 | MM_WAKER.register(cx.waker()); | 54 | MM_WAKER.register(cx.waker()); |
| @@ -58,20 +60,21 @@ impl MemoryManager { | |||
| 58 | }) | 60 | }) |
| 59 | .await; | 61 | .await; |
| 60 | 62 | ||
| 61 | Ipcc::send(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, || { | 63 | self.ipcc_mm_release_buffer_channel |
| 62 | interrupt::free(|_| unsafe { | 64 | .send(|| { |
| 63 | // CS required while moving nodes | 65 | interrupt::free(|_| unsafe { |
| 64 | while let Some(node_ptr) = LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) { | 66 | // CS required while moving nodes |
| 65 | LinkedListNode::insert_head(FREE_BUF_QUEUE.as_mut_ptr(), node_ptr); | 67 | while let Some(node_ptr) = LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) { |
| 66 | } | 68 | LinkedListNode::insert_head(FREE_BUF_QUEUE.as_mut_ptr(), node_ptr); |
| 69 | } | ||
| 70 | }) | ||
| 67 | }) | 71 | }) |
| 68 | }) | 72 | .await; |
| 69 | .await; | ||
| 70 | } | 73 | } |
| 71 | } | 74 | } |
| 72 | } | 75 | } |
| 73 | 76 | ||
| 74 | impl evt::MemoryManager for MemoryManager { | 77 | impl<'a> evt::MemoryManager for MemoryManager<'a> { |
| 75 | /// SAFETY: passing a pointer to something other than a managed event packet is UB | 78 | /// SAFETY: passing a pointer to something other than a managed event packet is UB |
| 76 | unsafe fn drop_event_packet(evt: *mut EvtPacket) { | 79 | unsafe fn drop_event_packet(evt: *mut EvtPacket) { |
| 77 | interrupt::free(|_| unsafe { | 80 | interrupt::free(|_| unsafe { |
diff --git a/embassy-stm32-wpan/src/sub/sys.rs b/embassy-stm32-wpan/src/sub/sys.rs index 8a3382f86..3ee539bb9 100644 --- a/embassy-stm32-wpan/src/sub/sys.rs +++ b/embassy-stm32-wpan/src/sub/sys.rs | |||
| @@ -1,23 +1,28 @@ | |||
| 1 | use core::ptr; | 1 | use embassy_stm32::ipcc::{IpccRxChannel, IpccTxChannel}; |
| 2 | 2 | ||
| 3 | use crate::cmd::CmdPacket; | 3 | use crate::cmd::CmdPacket; |
| 4 | use crate::consts::TlPacketType; | 4 | use crate::consts::TlPacketType; |
| 5 | use crate::evt::{CcEvt, EvtBox, EvtPacket}; | 5 | use crate::evt::EvtBox; |
| 6 | #[allow(unused_imports)] | 6 | #[cfg(feature = "ble")] |
| 7 | use crate::shci::{SchiCommandStatus, ShciBleInitCmdParam, ShciOpcode}; | 7 | use crate::shci::ShciBleInitCmdParam; |
| 8 | use crate::shci::{SchiCommandStatus, ShciOpcode}; | ||
| 8 | use crate::sub::mm; | 9 | use crate::sub::mm; |
| 9 | use crate::tables::{SysTable, WirelessFwInfoTable}; | 10 | use crate::tables::{SysTable, WirelessFwInfoTable}; |
| 10 | use crate::unsafe_linked_list::LinkedListNode; | 11 | use crate::unsafe_linked_list::LinkedListNode; |
| 11 | use crate::{Ipcc, SYS_CMD_BUF, SYSTEM_EVT_QUEUE, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE, channels}; | 12 | use crate::{SYS_CMD_BUF, SYSTEM_EVT_QUEUE, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE}; |
| 12 | 13 | ||
| 13 | /// A guard that, once constructed, allows for sys commands to be sent to CPU2. | 14 | /// A guard that, once constructed, allows for sys commands to be sent to CPU2. |
| 14 | pub struct Sys { | 15 | pub struct Sys<'a> { |
| 15 | _private: (), | 16 | ipcc_system_cmd_rsp_channel: IpccTxChannel<'a>, |
| 17 | ipcc_system_event_channel: IpccRxChannel<'a>, | ||
| 16 | } | 18 | } |
| 17 | 19 | ||
| 18 | impl Sys { | 20 | impl<'a> Sys<'a> { |
| 19 | /// TL_Sys_Init | 21 | /// TL_Sys_Init |
| 20 | pub(crate) fn new() -> Self { | 22 | pub(crate) fn new( |
| 23 | ipcc_system_cmd_rsp_channel: IpccTxChannel<'a>, | ||
| 24 | ipcc_system_event_channel: IpccRxChannel<'a>, | ||
| 25 | ) -> Self { | ||
| 21 | unsafe { | 26 | unsafe { |
| 22 | LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); | 27 | LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); |
| 23 | 28 | ||
| @@ -27,7 +32,10 @@ impl Sys { | |||
| 27 | }); | 32 | }); |
| 28 | } | 33 | } |
| 29 | 34 | ||
| 30 | Self { _private: () } | 35 | Self { |
| 36 | ipcc_system_cmd_rsp_channel, | ||
| 37 | ipcc_system_event_channel, | ||
| 38 | } | ||
| 31 | } | 39 | } |
| 32 | 40 | ||
| 33 | /// Returns CPU2 wireless firmware information (if present). | 41 | /// Returns CPU2 wireless firmware information (if present). |
| @@ -38,48 +46,28 @@ impl Sys { | |||
| 38 | if info.version != 0 { Some(info) } else { None } | 46 | if info.version != 0 { Some(info) } else { None } |
| 39 | } | 47 | } |
| 40 | 48 | ||
| 41 | pub async fn write(&self, opcode: ShciOpcode, payload: &[u8]) { | 49 | pub async fn write(&mut self, opcode: ShciOpcode, payload: &[u8]) { |
| 42 | Ipcc::send(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, || unsafe { | 50 | self.ipcc_system_cmd_rsp_channel |
| 43 | CmdPacket::write_into(SYS_CMD_BUF.as_mut_ptr(), TlPacketType::SysCmd, opcode as u16, payload); | 51 | .send(|| unsafe { |
| 44 | }) | 52 | CmdPacket::write_into(SYS_CMD_BUF.as_mut_ptr(), TlPacketType::SysCmd, opcode as u16, payload); |
| 45 | .await; | 53 | }) |
| 54 | .await; | ||
| 46 | } | 55 | } |
| 47 | 56 | ||
| 48 | /// `HW_IPCC_SYS_CmdEvtNot` | 57 | /// `HW_IPCC_SYS_CmdEvtNot` |
| 49 | pub async fn write_and_get_response(&self, opcode: ShciOpcode, payload: &[u8]) -> Result<SchiCommandStatus, ()> { | 58 | pub async fn write_and_get_response( |
| 59 | &mut self, | ||
| 60 | opcode: ShciOpcode, | ||
| 61 | payload: &[u8], | ||
| 62 | ) -> Result<SchiCommandStatus, ()> { | ||
| 50 | self.write(opcode, payload).await; | 63 | self.write(opcode, payload).await; |
| 51 | Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await; | 64 | self.ipcc_system_cmd_rsp_channel.flush().await; |
| 52 | |||
| 53 | unsafe { | ||
| 54 | let p_event_packet = SYS_CMD_BUF.as_ptr() as *const EvtPacket; | ||
| 55 | let p_command_event = &((*p_event_packet).evt_serial.evt.payload) as *const _ as *const CcEvt; | ||
| 56 | let p_payload = &((*p_command_event).payload) as *const u8; | ||
| 57 | 65 | ||
| 58 | ptr::read_volatile(p_payload).try_into() | 66 | unsafe { SchiCommandStatus::from_packet(SYS_CMD_BUF.as_ptr()) } |
| 59 | } | ||
| 60 | } | 67 | } |
| 61 | 68 | ||
| 62 | #[cfg(feature = "mac")] | 69 | #[cfg(feature = "mac")] |
| 63 | pub async fn shci_c2_mac_802_15_4_init(&self) -> Result<SchiCommandStatus, ()> { | 70 | pub async fn shci_c2_mac_802_15_4_init(&mut self) -> Result<SchiCommandStatus, ()> { |
| 64 | use crate::tables::{ | ||
| 65 | MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, Mac802_15_4Table, TL_MAC_802_15_4_TABLE, | ||
| 66 | TL_TRACES_TABLE, TRACES_EVT_QUEUE, TracesTable, | ||
| 67 | }; | ||
| 68 | |||
| 69 | unsafe { | ||
| 70 | LinkedListNode::init_head(TRACES_EVT_QUEUE.as_mut_ptr() as *mut _); | ||
| 71 | |||
| 72 | TL_TRACES_TABLE.as_mut_ptr().write_volatile(TracesTable { | ||
| 73 | traces_queue: TRACES_EVT_QUEUE.as_ptr() as *const _, | ||
| 74 | }); | ||
| 75 | |||
| 76 | TL_MAC_802_15_4_TABLE.as_mut_ptr().write_volatile(Mac802_15_4Table { | ||
| 77 | p_cmdrsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(), | ||
| 78 | p_notack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(), | ||
| 79 | evt_queue: core::ptr::null_mut(), | ||
| 80 | }); | ||
| 81 | }; | ||
| 82 | |||
| 83 | self.write_and_get_response(ShciOpcode::Mac802_15_4Init, &[]).await | 71 | self.write_and_get_response(ShciOpcode::Mac802_15_4Init, &[]).await |
| 84 | } | 72 | } |
| 85 | 73 | ||
| @@ -90,7 +78,7 @@ impl Sys { | |||
| 90 | /// `HW_IPCC_SYS_EvtNot`, aka `IoBusCallBackUserEvt` (as detailed in Figure 65), aka | 78 | /// `HW_IPCC_SYS_EvtNot`, aka `IoBusCallBackUserEvt` (as detailed in Figure 65), aka |
| 91 | /// [crate::sub::ble::hci::host::uart::UartHci::read]. | 79 | /// [crate::sub::ble::hci::host::uart::UartHci::read]. |
| 92 | #[cfg(feature = "ble")] | 80 | #[cfg(feature = "ble")] |
| 93 | pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) -> Result<SchiCommandStatus, ()> { | 81 | pub async fn shci_c2_ble_init(&mut self, param: ShciBleInitCmdParam) -> Result<SchiCommandStatus, ()> { |
| 94 | self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await | 82 | self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await |
| 95 | } | 83 | } |
| 96 | 84 | ||
| @@ -99,14 +87,15 @@ impl Sys { | |||
| 99 | /// This method takes the place of the `HW_IPCC_SYS_EvtNot`/`SysUserEvtRx`/`APPE_SysUserEvtRx`, | 87 | /// This method takes the place of the `HW_IPCC_SYS_EvtNot`/`SysUserEvtRx`/`APPE_SysUserEvtRx`, |
| 100 | /// as the embassy implementation avoids the need to call C public bindings, and instead | 88 | /// as the embassy implementation avoids the need to call C public bindings, and instead |
| 101 | /// handles the event channels directly. | 89 | /// handles the event channels directly. |
| 102 | pub async fn read(&self) -> EvtBox<mm::MemoryManager> { | 90 | pub async fn read<'b>(&mut self) -> EvtBox<mm::MemoryManager<'b>> { |
| 103 | Ipcc::receive(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, || unsafe { | 91 | self.ipcc_system_event_channel |
| 104 | if let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) { | 92 | .receive(|| unsafe { |
| 105 | Some(EvtBox::new(node_ptr.cast())) | 93 | if let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) { |
| 106 | } else { | 94 | Some(EvtBox::new(node_ptr.cast())) |
| 107 | None | 95 | } else { |
| 108 | } | 96 | None |
| 109 | }) | 97 | } |
| 110 | .await | 98 | }) |
| 99 | .await | ||
| 111 | } | 100 | } |
| 112 | } | 101 | } |
diff --git a/embassy-stm32-wpan/src/tables.rs b/embassy-stm32-wpan/src/tables.rs index 1dafed159..20d2c190f 100644 --- a/embassy-stm32-wpan/src/tables.rs +++ b/embassy-stm32-wpan/src/tables.rs | |||
| @@ -191,93 +191,93 @@ pub struct RefTable { | |||
| 191 | 191 | ||
| 192 | // --------------------- ref table --------------------- | 192 | // --------------------- ref table --------------------- |
| 193 | #[unsafe(link_section = "TL_REF_TABLE")] | 193 | #[unsafe(link_section = "TL_REF_TABLE")] |
| 194 | pub static mut TL_REF_TABLE: MaybeUninit<RefTable> = MaybeUninit::uninit(); | 194 | pub static mut TL_REF_TABLE: MaybeUninit<RefTable> = MaybeUninit::zeroed(); |
| 195 | 195 | ||
| 196 | #[unsafe(link_section = "MB_MEM1")] | 196 | #[unsafe(link_section = "MB_MEM1")] |
| 197 | pub static mut TL_DEVICE_INFO_TABLE: Aligned<A4, MaybeUninit<DeviceInfoTable>> = Aligned(MaybeUninit::uninit()); | 197 | pub static mut TL_DEVICE_INFO_TABLE: Aligned<A4, MaybeUninit<DeviceInfoTable>> = Aligned(MaybeUninit::zeroed()); |
| 198 | 198 | ||
| 199 | #[unsafe(link_section = "MB_MEM1")] | 199 | #[unsafe(link_section = "MB_MEM1")] |
| 200 | pub static mut TL_BLE_TABLE: Aligned<A4, MaybeUninit<BleTable>> = Aligned(MaybeUninit::uninit()); | 200 | pub static mut TL_BLE_TABLE: Aligned<A4, MaybeUninit<BleTable>> = Aligned(MaybeUninit::zeroed()); |
| 201 | 201 | ||
| 202 | #[unsafe(link_section = "MB_MEM1")] | 202 | #[unsafe(link_section = "MB_MEM1")] |
| 203 | pub static mut TL_THREAD_TABLE: Aligned<A4, MaybeUninit<ThreadTable>> = Aligned(MaybeUninit::uninit()); | 203 | pub static mut TL_THREAD_TABLE: Aligned<A4, MaybeUninit<ThreadTable>> = Aligned(MaybeUninit::zeroed()); |
| 204 | 204 | ||
| 205 | #[unsafe(link_section = "MB_MEM1")] | 205 | #[unsafe(link_section = "MB_MEM1")] |
| 206 | pub static mut TL_LLD_TESTS_TABLE: Aligned<A4, MaybeUninit<LldTestsTable>> = Aligned(MaybeUninit::uninit()); | 206 | pub static mut TL_LLD_TESTS_TABLE: Aligned<A4, MaybeUninit<LldTestsTable>> = Aligned(MaybeUninit::zeroed()); |
| 207 | 207 | ||
| 208 | #[unsafe(link_section = "MB_MEM1")] | 208 | #[unsafe(link_section = "MB_MEM1")] |
| 209 | pub static mut TL_BLE_LLD_TABLE: Aligned<A4, MaybeUninit<BleLldTable>> = Aligned(MaybeUninit::uninit()); | 209 | pub static mut TL_BLE_LLD_TABLE: Aligned<A4, MaybeUninit<BleLldTable>> = Aligned(MaybeUninit::zeroed()); |
| 210 | 210 | ||
| 211 | #[unsafe(link_section = "MB_MEM1")] | 211 | #[unsafe(link_section = "MB_MEM1")] |
| 212 | pub static mut TL_SYS_TABLE: Aligned<A4, MaybeUninit<SysTable>> = Aligned(MaybeUninit::uninit()); | 212 | pub static mut TL_SYS_TABLE: Aligned<A4, MaybeUninit<SysTable>> = Aligned(MaybeUninit::zeroed()); |
| 213 | 213 | ||
| 214 | #[unsafe(link_section = "MB_MEM1")] | 214 | #[unsafe(link_section = "MB_MEM1")] |
| 215 | pub static mut TL_MEM_MANAGER_TABLE: Aligned<A4, MaybeUninit<MemManagerTable>> = Aligned(MaybeUninit::uninit()); | 215 | pub static mut TL_MEM_MANAGER_TABLE: Aligned<A4, MaybeUninit<MemManagerTable>> = Aligned(MaybeUninit::zeroed()); |
| 216 | 216 | ||
| 217 | #[unsafe(link_section = "MB_MEM1")] | 217 | #[unsafe(link_section = "MB_MEM1")] |
| 218 | pub static mut TL_TRACES_TABLE: Aligned<A4, MaybeUninit<TracesTable>> = Aligned(MaybeUninit::uninit()); | 218 | pub static mut TL_TRACES_TABLE: Aligned<A4, MaybeUninit<TracesTable>> = Aligned(MaybeUninit::zeroed()); |
| 219 | 219 | ||
| 220 | #[unsafe(link_section = "MB_MEM1")] | 220 | #[unsafe(link_section = "MB_MEM1")] |
| 221 | pub static mut TL_MAC_802_15_4_TABLE: Aligned<A4, MaybeUninit<Mac802_15_4Table>> = Aligned(MaybeUninit::uninit()); | 221 | pub static mut TL_MAC_802_15_4_TABLE: Aligned<A4, MaybeUninit<Mac802_15_4Table>> = Aligned(MaybeUninit::zeroed()); |
| 222 | 222 | ||
| 223 | #[unsafe(link_section = "MB_MEM1")] | 223 | #[unsafe(link_section = "MB_MEM1")] |
| 224 | pub static mut TL_ZIGBEE_TABLE: Aligned<A4, MaybeUninit<ZigbeeTable>> = Aligned(MaybeUninit::uninit()); | 224 | pub static mut TL_ZIGBEE_TABLE: Aligned<A4, MaybeUninit<ZigbeeTable>> = Aligned(MaybeUninit::zeroed()); |
| 225 | 225 | ||
| 226 | // --------------------- tables --------------------- | 226 | // --------------------- tables --------------------- |
| 227 | #[unsafe(link_section = "MB_MEM1")] | 227 | #[unsafe(link_section = "MB_MEM1")] |
| 228 | pub static mut FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit()); | 228 | pub static mut FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed()); |
| 229 | 229 | ||
| 230 | #[allow(dead_code)] | 230 | #[allow(dead_code)] |
| 231 | #[unsafe(link_section = "MB_MEM1")] | 231 | #[unsafe(link_section = "MB_MEM1")] |
| 232 | pub static mut TRACES_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit()); | 232 | pub static mut TRACES_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed()); |
| 233 | 233 | ||
| 234 | #[unsafe(link_section = "MB_MEM2")] | 234 | #[unsafe(link_section = "MB_MEM2")] |
| 235 | pub static mut CS_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]>> = | 235 | pub static mut CS_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]>> = |
| 236 | Aligned(MaybeUninit::uninit()); | 236 | Aligned(MaybeUninit::zeroed()); |
| 237 | 237 | ||
| 238 | #[unsafe(link_section = "MB_MEM2")] | 238 | #[unsafe(link_section = "MB_MEM2")] |
| 239 | pub static mut EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit()); | 239 | pub static mut EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed()); |
| 240 | 240 | ||
| 241 | #[unsafe(link_section = "MB_MEM2")] | 241 | #[unsafe(link_section = "MB_MEM2")] |
| 242 | pub static mut SYSTEM_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit()); | 242 | pub static mut SYSTEM_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed()); |
| 243 | 243 | ||
| 244 | // --------------------- app tables --------------------- | 244 | // --------------------- app tables --------------------- |
| 245 | #[cfg(feature = "mac")] | 245 | #[cfg(feature = "mac")] |
| 246 | #[unsafe(link_section = "MB_MEM2")] | 246 | #[unsafe(link_section = "MB_MEM2")] |
| 247 | pub static mut MAC_802_15_4_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::uninit()); | 247 | pub static mut MAC_802_15_4_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed()); |
| 248 | 248 | ||
| 249 | #[cfg(feature = "mac")] | 249 | #[cfg(feature = "mac")] |
| 250 | #[unsafe(link_section = "MB_MEM2")] | 250 | #[unsafe(link_section = "MB_MEM2")] |
| 251 | pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit< | 251 | pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit< |
| 252 | Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>, | 252 | Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>, |
| 253 | > = MaybeUninit::uninit(); | 253 | > = MaybeUninit::zeroed(); |
| 254 | 254 | ||
| 255 | #[unsafe(link_section = "MB_MEM2")] | 255 | #[unsafe(link_section = "MB_MEM2")] |
| 256 | pub static mut EVT_POOL: Aligned<A4, MaybeUninit<[u8; POOL_SIZE]>> = Aligned(MaybeUninit::uninit()); | 256 | pub static mut EVT_POOL: Aligned<A4, MaybeUninit<[u8; POOL_SIZE]>> = Aligned(MaybeUninit::zeroed()); |
| 257 | 257 | ||
| 258 | #[unsafe(link_section = "MB_MEM2")] | 258 | #[unsafe(link_section = "MB_MEM2")] |
| 259 | pub static mut SYS_CMD_BUF: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::uninit()); | 259 | pub static mut SYS_CMD_BUF: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed()); |
| 260 | 260 | ||
| 261 | #[unsafe(link_section = "MB_MEM2")] | 261 | #[unsafe(link_section = "MB_MEM2")] |
| 262 | pub static mut SYS_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> = | 262 | pub static mut SYS_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> = |
| 263 | Aligned(MaybeUninit::uninit()); | 263 | Aligned(MaybeUninit::zeroed()); |
| 264 | 264 | ||
| 265 | #[cfg(feature = "mac")] | 265 | #[cfg(feature = "mac")] |
| 266 | #[unsafe(link_section = "MB_MEM2")] | 266 | #[unsafe(link_section = "MB_MEM2")] |
| 267 | pub static mut MAC_802_15_4_CNFINDNOT: Aligned<A4, MaybeUninit<[u8; C_SIZE_CMD_STRING]>> = | 267 | pub static mut MAC_802_15_4_CNFINDNOT: Aligned<A4, MaybeUninit<[u8; C_SIZE_CMD_STRING]>> = |
| 268 | Aligned(MaybeUninit::uninit()); | 268 | Aligned(MaybeUninit::zeroed()); |
| 269 | 269 | ||
| 270 | #[cfg(feature = "ble")] | 270 | #[cfg(feature = "ble")] |
| 271 | #[unsafe(link_section = "MB_MEM1")] | 271 | #[unsafe(link_section = "MB_MEM1")] |
| 272 | pub static mut BLE_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::uninit()); | 272 | pub static mut BLE_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed()); |
| 273 | 273 | ||
| 274 | #[cfg(feature = "ble")] | 274 | #[cfg(feature = "ble")] |
| 275 | #[unsafe(link_section = "MB_MEM2")] | 275 | #[unsafe(link_section = "MB_MEM2")] |
| 276 | pub static mut BLE_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> = | 276 | pub static mut BLE_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> = |
| 277 | Aligned(MaybeUninit::uninit()); | 277 | Aligned(MaybeUninit::zeroed()); |
| 278 | 278 | ||
| 279 | #[cfg(feature = "ble")] | 279 | #[cfg(feature = "ble")] |
| 280 | #[unsafe(link_section = "MB_MEM2")] | 280 | #[unsafe(link_section = "MB_MEM2")] |
| 281 | // fuck these "magic" numbers from ST ---v---v | 281 | // fuck these "magic" numbers from ST ---v---v |
| 282 | pub static mut HCI_ACL_DATA_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]>> = | 282 | pub static mut HCI_ACL_DATA_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]>> = |
| 283 | Aligned(MaybeUninit::uninit()); | 283 | Aligned(MaybeUninit::zeroed()); |
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 3431848d3..d3e5ba48d 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md | |||
| @@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 7 | 7 | ||
| 8 | ## Unreleased - ReleaseDate | 8 | ## Unreleased - ReleaseDate |
| 9 | 9 | ||
| 10 | - feat: Add continuous waveform method to SimplePWM | ||
| 11 | - change: remove waveform timer method | ||
| 12 | - change: low power: store stop mode for dma channels | ||
| 13 | - fix: Fixed ADC4 enable() for WBA | ||
| 14 | - feat: allow use of anyadcchannel for adc4 | ||
| 15 | - fix: fix incorrect logic for buffered usart transmission complete. | ||
| 16 | - feat: add poll_for methods to exti | ||
| 17 | - feat: implement stop for stm32wb. | ||
| 18 | - change: rework hsem and add HIL test for some chips. | ||
| 19 | - change: stm32/eth: ethernet no longer has a hard dependency on station management, and station management can be used independently ([#4871](https://github.com/embassy-rs/embassy/pull/4871)) | ||
| 20 | - feat: allow embassy_executor::main for low power | ||
| 21 | - feat: Add waveform methods to ComplementaryPwm | ||
| 22 | - fix: Avoid generating timer update events when updating the frequency ([#4890](https://github.com/embassy-rs/embassy/pull/4890)) | ||
| 23 | - chore: cleanup low-power add time | ||
| 24 | - fix: Allow setting SAI peripheral `frame_length` to `256` | ||
| 25 | - fix: flash erase on dual-bank STM32Gxxx | ||
| 10 | - feat: Add support for STM32N657X0 | 26 | - feat: Add support for STM32N657X0 |
| 11 | - feat: timer: Add 32-bit timer support to SimplePwm waveform_up method following waveform pattern ([#4717](https://github.com/embassy-rs/embassy/pull/4717)) | 27 | - feat: timer: Add 32-bit timer support to SimplePwm waveform_up method following waveform pattern ([#4717](https://github.com/embassy-rs/embassy/pull/4717)) |
| 12 | - feat: Add support for injected ADC measurements for g4 ([#4840](https://github.com/embassy-rs/embassy/pull/4840)) | 28 | - feat: Add support for injected ADC measurements for g4 ([#4840](https://github.com/embassy-rs/embassy/pull/4840)) |
| @@ -26,7 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 26 | - feat: Configurable gpio speed for QSPI | 42 | - feat: Configurable gpio speed for QSPI |
| 27 | - feat: derive Clone, Copy and defmt::Format for all *SPI-related configs | 43 | - feat: derive Clone, Copy and defmt::Format for all *SPI-related configs |
| 28 | - fix: handle address and data-length errors in OSPI | 44 | - fix: handle address and data-length errors in OSPI |
| 29 | - feat: Allow OSPI DMA writes larger than 64kB using chunking | 45 | - feat: Allow OSPI/HSPI/XSPI DMA writes larger than 64kB using chunking |
| 30 | - feat: More ADC enums for g0 PAC, API change for oversampling, allow separate sample times | 46 | - feat: More ADC enums for g0 PAC, API change for oversampling, allow separate sample times |
| 31 | - feat: Add USB CRS sync support for STM32C071 | 47 | - feat: Add USB CRS sync support for STM32C071 |
| 32 | - fix: RTC register definition for STM32L4P5 and L4Q5 as they use v3 register map. | 48 | - fix: RTC register definition for STM32L4P5 and L4Q5 as they use v3 register map. |
| @@ -37,13 +53,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 37 | - feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668)) | 53 | - feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668)) |
| 38 | - feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options | 54 | - feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options |
| 39 | - change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer | 55 | - change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer |
| 56 | - fix: Properly set the transfer size for OSPI/HSPI/XSPI transfers with word sizes other than 8 bits. | ||
| 57 | - fix: stm32/adc: Calculate the ADC prescaler in a way that it allows for the max frequency to be reached | ||
| 40 | - fix: Prevent a HardFault crash on STM32H5 devices by changing `uid()` to return `[u8; 12]` by value instead of a reference. (Fixes #2696) | 58 | - fix: Prevent a HardFault crash on STM32H5 devices by changing `uid()` to return `[u8; 12]` by value instead of a reference. (Fixes #2696) |
| 41 | - change: timer: added output compare values | 59 | - change: timer: added output compare values |
| 42 | - feat: timer: add ability to set master mode | 60 | - feat: timer: add ability to set master mode |
| 43 | - fix: sdmmc: don't wait for DBCKEND flag on sdmmc_v2 devices as it never fires (Fixes #4723) | 61 | - fix: sdmmc: don't wait for DBCKEND flag on sdmmc_v2 devices as it never fires (Fixes #4723) |
| 44 | - fix: usart: fix race condition in ringbuffered usart | 62 | - fix: usart: fix race condition in ringbuffered usart |
| 45 | - feat: Add backup_sram::init() for H5 devices to access BKPSRAM | 63 | - feat: Add backup_sram::init() for H5 devices to access BKPSRAM |
| 46 | - feat: Add I2C MultiMaster (Slave) support for I2C v1 | 64 | - feat: stm32/i2c v1: Add I2C MultiMaster (Slave) support |
| 65 | - feat: stm32/i2c v2: Add transaction() and blocking_transaction() methods with contract-compliant operation merging | ||
| 47 | - feat: stm32/fdcan: add ability to control automatic recovery from bus off ([#4821](https://github.com/embassy-rs/embassy/pull/4821)) | 66 | - feat: stm32/fdcan: add ability to control automatic recovery from bus off ([#4821](https://github.com/embassy-rs/embassy/pull/4821)) |
| 48 | - low-power: update rtc api to allow reconfig | 67 | - low-power: update rtc api to allow reconfig |
| 49 | - adc: consolidate ringbuffer | 68 | - adc: consolidate ringbuffer |
| @@ -55,6 +74,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 55 | - chore: Updated stm32-metapac and stm32-data dependencies | 74 | - chore: Updated stm32-metapac and stm32-data dependencies |
| 56 | - adc: reogranize and cleanup somewhat. require sample_time to be passed on conversion | 75 | - adc: reogranize and cleanup somewhat. require sample_time to be passed on conversion |
| 57 | - fix: stm32/i2c v2 slave: prevent misaligned reads, error false positives, and incorrect counts of bytes read/written | 76 | - fix: stm32/i2c v2 slave: prevent misaligned reads, error false positives, and incorrect counts of bytes read/written |
| 77 | - feat: add flash support for c0 family ([#4874](https://github.com/embassy-rs/embassy/pull/4874)) | ||
| 78 | - fix: fixing channel numbers on vbat and vddcore for adc on adc | ||
| 79 | - adc: adding disable to vbat | ||
| 80 | - feat: stm32/flash: add async support for h7 family | ||
| 81 | - feat: exti brought in line with other drivers' interrupt rebinding system ([#4922](https://github.com/embassy-rs/embassy/pull/4922)) | ||
| 82 | - removal: ExtiInput no longer accepts AnyPin/AnyChannel; AnyChannel removed entirely | ||
| 83 | - fix: build script ensures EXTI2_TSC is listed as the IRQ of EXTI2 even if the PAC doesn't | ||
| 84 | - feat: stm32/lcd: added implementation | ||
| 58 | 85 | ||
| 59 | ## 0.4.0 - 2025-08-26 | 86 | ## 0.4.0 - 2025-08-26 |
| 60 | 87 | ||
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index a722d2379..e10409112 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -139,6 +139,7 @@ flavors = [ | |||
| 139 | { regex_feature = "stm32wb.*", target = "thumbv7em-none-eabi" }, | 139 | { regex_feature = "stm32wb.*", target = "thumbv7em-none-eabi" }, |
| 140 | { regex_feature = "stm32wba.*", target = "thumbv8m.main-none-eabihf" }, | 140 | { regex_feature = "stm32wba.*", target = "thumbv8m.main-none-eabihf" }, |
| 141 | { regex_feature = "stm32wl.*", target = "thumbv7em-none-eabi" }, | 141 | { regex_feature = "stm32wl.*", target = "thumbv7em-none-eabi" }, |
| 142 | { regex_feature = "stm32n6.*", target = "thumbv8m.main-none-eabihf" }, | ||
| 142 | ] | 143 | ] |
| 143 | 144 | ||
| 144 | [package.metadata.docs.rs] | 145 | [package.metadata.docs.rs] |
| @@ -187,6 +188,7 @@ embedded-io = { version = "0.6.0" } | |||
| 187 | embedded-io-async = { version = "0.6.1" } | 188 | embedded-io-async = { version = "0.6.1" } |
| 188 | chrono = { version = "^0.4", default-features = false, optional = true } | 189 | chrono = { version = "^0.4", default-features = false, optional = true } |
| 189 | bit_field = "0.10.2" | 190 | bit_field = "0.10.2" |
| 191 | trait-set = "0.3.0" | ||
| 190 | document-features = "0.2.7" | 192 | document-features = "0.2.7" |
| 191 | 193 | ||
| 192 | static_assertions = { version = "1.1" } | 194 | static_assertions = { version = "1.1" } |
| @@ -198,11 +200,11 @@ aligned = "0.4.1" | |||
| 198 | heapless = "0.9.1" | 200 | heapless = "0.9.1" |
| 199 | 201 | ||
| 200 | #stm32-metapac = { version = "18" } | 202 | #stm32-metapac = { version = "18" } |
| 201 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b77c8d968f53b18d6bdcd052e354b5070ec2bbc2" } | 203 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f61ed017ef12ec84ff04c49e3147694bda3b29cb" } |
| 202 | 204 | ||
| 203 | [build-dependencies] | 205 | [build-dependencies] |
| 204 | #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} | 206 | #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} |
| 205 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b77c8d968f53b18d6bdcd052e354b5070ec2bbc2", default-features = false, features = ["metadata"] } | 207 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f61ed017ef12ec84ff04c49e3147694bda3b29cb", default-features = false, features = ["metadata"] } |
| 206 | 208 | ||
| 207 | proc-macro2 = "1.0.36" | 209 | proc-macro2 = "1.0.36" |
| 208 | quote = "1.0.15" | 210 | quote = "1.0.15" |
| @@ -1642,7 +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" ] | ||
| 1645 | stm32n657x0 = [ "stm32-metapac/stm32n657x0" ] | 1669 | stm32n657x0 = [ "stm32-metapac/stm32n657x0" ] |
| 1670 | stm32n657z0 = [ "stm32-metapac/stm32n657z0" ] | ||
| 1646 | stm32u031c6 = [ "stm32-metapac/stm32u031c6" ] | 1671 | stm32u031c6 = [ "stm32-metapac/stm32u031c6" ] |
| 1647 | stm32u031c8 = [ "stm32-metapac/stm32u031c8" ] | 1672 | stm32u031c8 = [ "stm32-metapac/stm32u031c8" ] |
| 1648 | stm32u031f4 = [ "stm32-metapac/stm32u031f4" ] | 1673 | stm32u031f4 = [ "stm32-metapac/stm32u031f4" ] |
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 48da475df..109571e8f 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -170,6 +170,11 @@ fn main() { | |||
| 170 | } | 170 | } |
| 171 | singletons.push(p.name.to_string()); | 171 | singletons.push(p.name.to_string()); |
| 172 | } | 172 | } |
| 173 | |||
| 174 | "eth" => { | ||
| 175 | singletons.push(p.name.to_string()); | ||
| 176 | singletons.push("ETH_SMA".to_string()); | ||
| 177 | } | ||
| 173 | //"dbgmcu" => {} | 178 | //"dbgmcu" => {} |
| 174 | //"syscfg" => {} | 179 | //"syscfg" => {} |
| 175 | //"dma" => {} | 180 | //"dma" => {} |
| @@ -348,8 +353,13 @@ fn main() { | |||
| 348 | // ======== | 353 | // ======== |
| 349 | // Generate interrupt declarations | 354 | // Generate interrupt declarations |
| 350 | 355 | ||
| 356 | let mut exti2_tsc_shared_int_present: Option<stm32_metapac::metadata::Interrupt> = None; | ||
| 351 | let mut irqs = Vec::new(); | 357 | let mut irqs = Vec::new(); |
| 352 | for irq in METADATA.interrupts { | 358 | for irq in METADATA.interrupts { |
| 359 | // The PAC doesn't ensure this is listed as the IRQ of EXTI2, so we must do so | ||
| 360 | if irq.name == "EXTI2_TSC" { | ||
| 361 | exti2_tsc_shared_int_present = Some(irq.clone()) | ||
| 362 | } | ||
| 353 | irqs.push(format_ident!("{}", irq.name)); | 363 | irqs.push(format_ident!("{}", irq.name)); |
| 354 | } | 364 | } |
| 355 | 365 | ||
| @@ -1347,6 +1357,9 @@ fn main() { | |||
| 1347 | (("tsc", "G8_IO2"), quote!(crate::tsc::G8IO2Pin)), | 1357 | (("tsc", "G8_IO2"), quote!(crate::tsc::G8IO2Pin)), |
| 1348 | (("tsc", "G8_IO3"), quote!(crate::tsc::G8IO3Pin)), | 1358 | (("tsc", "G8_IO3"), quote!(crate::tsc::G8IO3Pin)), |
| 1349 | (("tsc", "G8_IO4"), quote!(crate::tsc::G8IO4Pin)), | 1359 | (("tsc", "G8_IO4"), quote!(crate::tsc::G8IO4Pin)), |
| 1360 | (("lcd", "SEG"), quote!(crate::lcd::SegPin)), | ||
| 1361 | (("lcd", "COM"), quote!(crate::lcd::ComPin)), | ||
| 1362 | (("lcd", "VLCD"), quote!(crate::lcd::VlcdPin)), | ||
| 1350 | (("dac", "OUT1"), quote!(crate::dac::DacPin<Ch1>)), | 1363 | (("dac", "OUT1"), quote!(crate::dac::DacPin<Ch1>)), |
| 1351 | (("dac", "OUT2"), quote!(crate::dac::DacPin<Ch2>)), | 1364 | (("dac", "OUT2"), quote!(crate::dac::DacPin<Ch2>)), |
| 1352 | ].into(); | 1365 | ].into(); |
| @@ -1354,9 +1367,22 @@ fn main() { | |||
| 1354 | for p in METADATA.peripherals { | 1367 | for p in METADATA.peripherals { |
| 1355 | if let Some(regs) = &p.registers { | 1368 | if let Some(regs) = &p.registers { |
| 1356 | let mut adc_pairs: BTreeMap<u8, (Option<Ident>, Option<Ident>)> = BTreeMap::new(); | 1369 | let mut adc_pairs: BTreeMap<u8, (Option<Ident>, Option<Ident>)> = BTreeMap::new(); |
| 1370 | let mut seen_lcd_seg_pins = HashSet::new(); | ||
| 1357 | 1371 | ||
| 1358 | for pin in p.pins { | 1372 | for pin in p.pins { |
| 1359 | let key = (regs.kind, pin.signal); | 1373 | let mut key = (regs.kind, pin.signal); |
| 1374 | |||
| 1375 | // LCD is special. There are so many pins! | ||
| 1376 | if regs.kind == "lcd" { | ||
| 1377 | key.1 = pin.signal.trim_end_matches(char::is_numeric); | ||
| 1378 | |||
| 1379 | if key.1 == "SEG" && !seen_lcd_seg_pins.insert(pin.pin) { | ||
| 1380 | // LCD has SEG pins multiplexed in the peripheral | ||
| 1381 | // This means we can see them twice. We need to skip those so we're not impl'ing the trait twice | ||
| 1382 | continue; | ||
| 1383 | } | ||
| 1384 | } | ||
| 1385 | |||
| 1360 | if let Some(tr) = signals.get(&key) { | 1386 | if let Some(tr) = signals.get(&key) { |
| 1361 | let mut peri = format_ident!("{}", p.name); | 1387 | let mut peri = format_ident!("{}", p.name); |
| 1362 | 1388 | ||
| @@ -1399,6 +1425,11 @@ fn main() { | |||
| 1399 | } | 1425 | } |
| 1400 | } | 1426 | } |
| 1401 | 1427 | ||
| 1428 | // MDIO and MDC are special | ||
| 1429 | if pin.signal == "MDIO" || pin.signal == "MDC" { | ||
| 1430 | peri = format_ident!("{}", "ETH_SMA"); | ||
| 1431 | } | ||
| 1432 | |||
| 1402 | // XSPI NCS pin to CSSEL mapping | 1433 | // XSPI NCS pin to CSSEL mapping |
| 1403 | if pin.signal.ends_with("NCS1") { | 1434 | if pin.signal.ends_with("NCS1") { |
| 1404 | g.extend(quote! { | 1435 | g.extend(quote! { |
| @@ -1602,13 +1633,13 @@ fn main() { | |||
| 1602 | .into(); | 1633 | .into(); |
| 1603 | 1634 | ||
| 1604 | if chip_name.starts_with("stm32u5") { | 1635 | if chip_name.starts_with("stm32u5") { |
| 1605 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma4)); | 1636 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); |
| 1606 | } else { | 1637 | } else { |
| 1607 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); | 1638 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); |
| 1608 | } | 1639 | } |
| 1609 | 1640 | ||
| 1610 | if chip_name.starts_with("stm32wba") { | 1641 | if chip_name.starts_with("stm32wba") { |
| 1611 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma4)); | 1642 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); |
| 1612 | } | 1643 | } |
| 1613 | 1644 | ||
| 1614 | if chip_name.starts_with("stm32g4") { | 1645 | if chip_name.starts_with("stm32g4") { |
| @@ -1695,70 +1726,88 @@ fn main() { | |||
| 1695 | } | 1726 | } |
| 1696 | 1727 | ||
| 1697 | // ======== | 1728 | // ======== |
| 1698 | // Generate Div/Mul impls for RCC prescalers/dividers/multipliers. | 1729 | // Generate Div/Mul impls for RCC and ADC prescalers/dividers/multipliers. |
| 1699 | for e in rcc_registers.ir.enums { | 1730 | for (kind, psc_enums) in ["rcc", "adc", "adccommon"].iter().filter_map(|kind| { |
| 1700 | fn is_rcc_name(e: &str) -> bool { | 1731 | METADATA |
| 1701 | match e { | 1732 | .peripherals |
| 1702 | "Pllp" | "Pllq" | "Pllr" | "Plldivst" | "Pllm" | "Plln" | "Prediv1" | "Prediv2" | "Hpre5" => true, | 1733 | .iter() |
| 1703 | "Timpre" | "Pllrclkpre" => false, | 1734 | .filter_map(|p| p.registers.as_ref()) |
| 1704 | e if e.ends_with("pre") || e.ends_with("pres") || e.ends_with("div") || e.ends_with("mul") => true, | 1735 | .find(|r| r.kind == *kind) |
| 1705 | _ => false, | 1736 | .map(|r| (*kind, r.ir.enums)) |
| 1737 | }) { | ||
| 1738 | for e in psc_enums.iter() { | ||
| 1739 | fn is_adc_name(e: &str) -> bool { | ||
| 1740 | match e { | ||
| 1741 | "Presc" | "Adc4Presc" | "Adcpre" => true, | ||
| 1742 | _ => false, | ||
| 1743 | } | ||
| 1706 | } | 1744 | } |
| 1707 | } | ||
| 1708 | 1745 | ||
| 1709 | fn parse_num(n: &str) -> Result<Frac, ()> { | 1746 | fn is_rcc_name(e: &str) -> bool { |
| 1710 | for prefix in ["DIV", "MUL"] { | 1747 | match e { |
| 1711 | if let Some(n) = n.strip_prefix(prefix) { | 1748 | "Pllp" | "Pllq" | "Pllr" | "Plldivst" | "Pllm" | "Plln" | "Prediv1" | "Prediv2" | "Hpre5" => true, |
| 1712 | let exponent = n.find('_').map(|e| n.len() - 1 - e).unwrap_or(0) as u32; | 1749 | "Timpre" | "Pllrclkpre" => false, |
| 1713 | let mantissa = n.replace('_', "").parse().map_err(|_| ())?; | 1750 | e if e.ends_with("pre") || e.ends_with("pres") || e.ends_with("div") || e.ends_with("mul") => true, |
| 1714 | let f = Frac { | 1751 | _ => false, |
| 1715 | num: mantissa, | ||
| 1716 | denom: 10u32.pow(exponent), | ||
| 1717 | }; | ||
| 1718 | return Ok(f.simplify()); | ||
| 1719 | } | 1752 | } |
| 1720 | } | 1753 | } |
| 1721 | Err(()) | ||
| 1722 | } | ||
| 1723 | 1754 | ||
| 1724 | if is_rcc_name(e.name) { | 1755 | fn parse_num(n: &str) -> Result<Frac, ()> { |
| 1725 | let enum_name = format_ident!("{}", e.name); | 1756 | for prefix in ["DIV", "MUL"] { |
| 1726 | let mut muls = Vec::new(); | 1757 | if let Some(n) = n.strip_prefix(prefix) { |
| 1727 | let mut divs = Vec::new(); | 1758 | let exponent = n.find('_').map(|e| n.len() - 1 - e).unwrap_or(0) as u32; |
| 1728 | for v in e.variants { | 1759 | let mantissa = n.replace('_', "").parse().map_err(|_| ())?; |
| 1729 | let Ok(val) = parse_num(v.name) else { | 1760 | let f = Frac { |
| 1730 | panic!("could not parse mul/div. enum={} variant={}", e.name, v.name) | 1761 | num: mantissa, |
| 1731 | }; | 1762 | denom: 10u32.pow(exponent), |
| 1732 | let variant_name = format_ident!("{}", v.name); | 1763 | }; |
| 1733 | let variant = quote!(crate::pac::rcc::vals::#enum_name::#variant_name); | 1764 | return Ok(f.simplify()); |
| 1734 | let num = val.num; | 1765 | } |
| 1735 | let denom = val.denom; | 1766 | } |
| 1736 | muls.push(quote!(#variant => self * #num / #denom,)); | 1767 | Err(()) |
| 1737 | divs.push(quote!(#variant => self * #denom / #num,)); | ||
| 1738 | } | 1768 | } |
| 1739 | 1769 | ||
| 1740 | g.extend(quote! { | 1770 | if (kind == "rcc" && is_rcc_name(e.name)) || ((kind == "adccommon" || kind == "adc") && is_adc_name(e.name)) |
| 1741 | impl core::ops::Div<crate::pac::rcc::vals::#enum_name> for crate::time::Hertz { | 1771 | { |
| 1742 | type Output = crate::time::Hertz; | 1772 | let kind = format_ident!("{}", kind); |
| 1743 | fn div(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output { | 1773 | let enum_name = format_ident!("{}", e.name); |
| 1744 | match rhs { | 1774 | let mut muls = Vec::new(); |
| 1745 | #(#divs)* | 1775 | let mut divs = Vec::new(); |
| 1746 | #[allow(unreachable_patterns)] | 1776 | for v in e.variants { |
| 1747 | _ => unreachable!(), | 1777 | let Ok(val) = parse_num(v.name) else { |
| 1778 | panic!("could not parse mul/div. enum={} variant={}", e.name, v.name) | ||
| 1779 | }; | ||
| 1780 | let variant_name = format_ident!("{}", v.name); | ||
| 1781 | let variant = quote!(crate::pac::#kind::vals::#enum_name::#variant_name); | ||
| 1782 | let num = val.num; | ||
| 1783 | let denom = val.denom; | ||
| 1784 | muls.push(quote!(#variant => self * #num / #denom,)); | ||
| 1785 | divs.push(quote!(#variant => self * #denom / #num,)); | ||
| 1786 | } | ||
| 1787 | |||
| 1788 | g.extend(quote! { | ||
| 1789 | impl core::ops::Div<crate::pac::#kind::vals::#enum_name> for crate::time::Hertz { | ||
| 1790 | type Output = crate::time::Hertz; | ||
| 1791 | fn div(self, rhs: crate::pac::#kind::vals::#enum_name) -> Self::Output { | ||
| 1792 | match rhs { | ||
| 1793 | #(#divs)* | ||
| 1794 | #[allow(unreachable_patterns)] | ||
| 1795 | _ => unreachable!(), | ||
| 1796 | } | ||
| 1748 | } | 1797 | } |
| 1749 | } | 1798 | } |
| 1750 | } | 1799 | impl core::ops::Mul<crate::pac::#kind::vals::#enum_name> for crate::time::Hertz { |
| 1751 | impl core::ops::Mul<crate::pac::rcc::vals::#enum_name> for crate::time::Hertz { | 1800 | type Output = crate::time::Hertz; |
| 1752 | type Output = crate::time::Hertz; | 1801 | fn mul(self, rhs: crate::pac::#kind::vals::#enum_name) -> Self::Output { |
| 1753 | fn mul(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output { | 1802 | match rhs { |
| 1754 | match rhs { | 1803 | #(#muls)* |
| 1755 | #(#muls)* | 1804 | #[allow(unreachable_patterns)] |
| 1756 | #[allow(unreachable_patterns)] | 1805 | _ => unreachable!(), |
| 1757 | _ => unreachable!(), | 1806 | } |
| 1758 | } | 1807 | } |
| 1759 | } | 1808 | } |
| 1760 | } | 1809 | }); |
| 1761 | }); | 1810 | } |
| 1762 | } | 1811 | } |
| 1763 | } | 1812 | } |
| 1764 | 1813 | ||
| @@ -1768,7 +1817,19 @@ fn main() { | |||
| 1768 | for p in METADATA.peripherals { | 1817 | for p in METADATA.peripherals { |
| 1769 | let mut pt = TokenStream::new(); | 1818 | let mut pt = TokenStream::new(); |
| 1770 | 1819 | ||
| 1820 | let mut exti2_tsc_injected = false; | ||
| 1821 | if let Some(ref irq) = exti2_tsc_shared_int_present | ||
| 1822 | && p.name == "EXTI" | ||
| 1823 | { | ||
| 1824 | exti2_tsc_injected = true; | ||
| 1825 | let iname = format_ident!("{}", irq.name); | ||
| 1826 | let sname = format_ident!("{}", "EXTI2"); | ||
| 1827 | pt.extend(quote!(pub type #sname = crate::interrupt::typelevel::#iname;)); | ||
| 1828 | } | ||
| 1771 | for irq in p.interrupts { | 1829 | for irq in p.interrupts { |
| 1830 | if exti2_tsc_injected && irq.signal == "EXTI2" { | ||
| 1831 | continue; | ||
| 1832 | } | ||
| 1772 | let iname = format_ident!("{}", irq.interrupt); | 1833 | let iname = format_ident!("{}", irq.interrupt); |
| 1773 | let sname = format_ident!("{}", irq.signal); | 1834 | let sname = format_ident!("{}", irq.signal); |
| 1774 | pt.extend(quote!(pub type #sname = crate::interrupt::typelevel::#iname;)); | 1835 | pt.extend(quote!(pub type #sname = crate::interrupt::typelevel::#iname;)); |
| @@ -1919,6 +1980,19 @@ fn main() { | |||
| 1919 | continue; | 1980 | continue; |
| 1920 | } | 1981 | } |
| 1921 | 1982 | ||
| 1983 | let stop_mode = METADATA | ||
| 1984 | .peripherals | ||
| 1985 | .iter() | ||
| 1986 | .find(|p| p.name == ch.dma) | ||
| 1987 | .map(|p| p.rcc.as_ref().map(|rcc| rcc.stop_mode.clone()).unwrap_or_default()) | ||
| 1988 | .unwrap_or_default(); | ||
| 1989 | |||
| 1990 | let stop_mode = match stop_mode { | ||
| 1991 | StopMode::Standby => quote! { Standby }, | ||
| 1992 | StopMode::Stop2 => quote! { Stop2 }, | ||
| 1993 | StopMode::Stop1 => quote! { Stop1 }, | ||
| 1994 | }; | ||
| 1995 | |||
| 1922 | let name = format_ident!("{}", ch.name); | 1996 | let name = format_ident!("{}", ch.name); |
| 1923 | let idx = ch_idx as u8; | 1997 | let idx = ch_idx as u8; |
| 1924 | #[cfg(feature = "_dual-core")] | 1998 | #[cfg(feature = "_dual-core")] |
| @@ -1931,7 +2005,7 @@ fn main() { | |||
| 1931 | quote!(crate::pac::Interrupt::#irq_name) | 2005 | quote!(crate::pac::Interrupt::#irq_name) |
| 1932 | }; | 2006 | }; |
| 1933 | 2007 | ||
| 1934 | g.extend(quote!(dma_channel_impl!(#name, #idx);)); | 2008 | g.extend(quote!(dma_channel_impl!(#name, #idx, #stop_mode);)); |
| 1935 | 2009 | ||
| 1936 | let dma = format_ident!("{}", ch.dma); | 2010 | let dma = format_ident!("{}", ch.dma); |
| 1937 | let ch_num = ch.channel as usize; | 2011 | let ch_num = ch.channel as usize; |
diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index befa8ed4a..453513309 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 = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); |
| 113 | #[allow(unused)] | 81 | match raw_prescaler { |
| 114 | enum Prescaler { | 82 | 0 => Presc::DIV1, |
| 115 | NotDivided, | 83 | 1 => Presc::DIV2, |
| 116 | DividedBy2, | 84 | 2..=3 => Presc::DIV4, |
| 117 | DividedBy4, | 85 | 4..=5 => Presc::DIV6, |
| 118 | DividedBy6, | 86 | 6..=7 => Presc::DIV8, |
| 119 | DividedBy8, | 87 | 8..=9 => Presc::DIV10, |
| 120 | DividedBy10, | 88 | 10..=11 => Presc::DIV12, |
| 121 | DividedBy12, | 89 | _ => unimplemented!(), |
| 122 | DividedBy16, | ||
| 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,127 @@ 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 | if !ADC4::regs().cr().read().aden() || !ADC4::regs().isr().read().adrdy() { | ||
| 117 | ADC4::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 118 | ADC4::regs().cr().modify(|w| w.set_aden(true)); | ||
| 119 | while !ADC4::regs().isr().read().adrdy() {} | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | fn start() { | ||
| 124 | // Start conversion | ||
| 125 | ADC4::regs().cr().modify(|reg| { | ||
| 126 | reg.set_adstart(true); | ||
| 127 | }); | ||
| 128 | } | ||
| 129 | |||
| 130 | fn stop() { | ||
| 131 | let cr = ADC4::regs().cr().read(); | ||
| 132 | if cr.adstart() { | ||
| 133 | ADC4::regs().cr().modify(|w| w.set_adstp(true)); | ||
| 134 | while ADC4::regs().cr().read().adstart() {} | ||
| 135 | } | ||
| 136 | |||
| 137 | if cr.aden() || cr.adstart() { | ||
| 138 | ADC4::regs().cr().modify(|w| w.set_addis(true)); | ||
| 139 | while ADC4::regs().cr().read().aden() {} | ||
| 140 | } | ||
| 141 | |||
| 142 | // Reset configuration. | ||
| 143 | ADC4::regs().cfgr1().modify(|reg| { | ||
| 144 | reg.set_dmaen(false); | ||
| 145 | }); | ||
| 146 | } | ||
| 147 | |||
| 148 | fn configure_dma(conversion_mode: ConversionMode) { | ||
| 149 | match conversion_mode { | ||
| 150 | ConversionMode::Singular => { | ||
| 151 | ADC4::regs().isr().modify(|reg| { | ||
| 152 | reg.set_ovr(true); | ||
| 153 | reg.set_eos(true); | ||
| 154 | reg.set_eoc(true); | ||
| 155 | }); | ||
| 156 | |||
| 157 | ADC4::regs().cfgr1().modify(|reg| { | ||
| 158 | reg.set_dmaen(true); | ||
| 159 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 160 | #[cfg(stm32u5)] | ||
| 161 | reg.set_chselrmod(false); | ||
| 162 | #[cfg(stm32wba)] | ||
| 163 | reg.set_chselrmod(Chselrmod::ENABLE_INPUT) | ||
| 164 | }); | ||
| 165 | } | ||
| 166 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 167 | _ => unreachable!(), | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { | ||
| 172 | let mut prev_channel: i16 = -1; | ||
| 173 | #[cfg(stm32wba)] | ||
| 174 | ADC4::regs().chselr().write_value(Chselr(0_u32)); | ||
| 175 | #[cfg(stm32u5)] | ||
| 176 | ADC4::regs().chselrmod0().write_value(Chselr(0_u32)); | ||
| 177 | for (_i, ((channel, _), sample_time)) in sequence.enumerate() { | ||
| 178 | ADC4::regs().smpr().modify(|w| { | ||
| 179 | w.set_smp(_i, sample_time); | ||
| 180 | }); | ||
| 181 | |||
| 182 | let channel_num = channel; | ||
| 183 | if channel_num as i16 <= prev_channel { | ||
| 184 | return; | ||
| 185 | }; | ||
| 186 | prev_channel = channel_num as i16; | ||
| 187 | |||
| 188 | #[cfg(stm32wba)] | ||
| 189 | ADC4::regs().chselr().modify(|w| { | ||
| 190 | w.set_chsel0(channel as usize, true); | ||
| 191 | }); | ||
| 192 | #[cfg(stm32u5)] | ||
| 193 | ADC4::regs().chselrmod0().modify(|w| { | ||
| 194 | w.set_chsel(channel as usize, true); | ||
| 195 | }); | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | fn convert() -> u16 { | ||
| 200 | // Reset interrupts | ||
| 201 | ADC4::regs().isr().modify(|reg| { | ||
| 202 | reg.set_eos(true); | ||
| 203 | reg.set_eoc(true); | ||
| 204 | }); | ||
| 205 | |||
| 206 | // Start conversion | ||
| 207 | ADC4::regs().cr().modify(|reg| { | ||
| 208 | reg.set_adstart(true); | ||
| 209 | }); | ||
| 210 | |||
| 211 | while !ADC4::regs().isr().read().eos() { | ||
| 212 | // spin | ||
| 213 | } | ||
| 214 | |||
| 215 | ADC4::regs().dr().read().0 as u16 | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | impl super::AnyInstance for ADC4 {} | ||
| 220 | }; | ||
| 221 | ); | ||
| 222 | |||
| 188 | pub struct Adc4<'d, T: Instance> { | 223 | pub struct Adc4<'d, T: Instance> { |
| 189 | #[allow(unused)] | 224 | #[allow(unused)] |
| 190 | adc: crate::Peri<'d, T>, | 225 | adc: crate::Peri<'d, T>, |
| @@ -196,15 +231,15 @@ pub enum Adc4Error { | |||
| 196 | DMAError, | 231 | DMAError, |
| 197 | } | 232 | } |
| 198 | 233 | ||
| 199 | impl<'d, T: Instance> Adc4<'d, T> { | 234 | impl<'d, T: Instance + super::AnyInstance> super::Adc<'d, T> { |
| 200 | /// Create a new ADC driver. | 235 | /// Create a new ADC driver. |
| 201 | pub fn new(adc: Peri<'d, T>) -> Self { | 236 | pub fn new_adc4(adc: Peri<'d, T>) -> Self { |
| 202 | rcc::enable_and_reset::<T>(); | 237 | rcc::enable_and_reset::<T>(); |
| 203 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 238 | let prescaler = from_ker_ck(T::frequency()); |
| 204 | 239 | ||
| 205 | T::regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 240 | T::regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 206 | 241 | ||
| 207 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 242 | let frequency = T::frequency() / prescaler; |
| 208 | info!("ADC4 frequency set to {}", frequency); | 243 | info!("ADC4 frequency set to {}", frequency); |
| 209 | 244 | ||
| 210 | if frequency > MAX_ADC_CLK_FREQ { | 245 | if frequency > MAX_ADC_CLK_FREQ { |
| @@ -214,20 +249,6 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 214 | ); | 249 | ); |
| 215 | } | 250 | } |
| 216 | 251 | ||
| 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| { | 252 | T::regs().isr().modify(|w| { |
| 232 | w.set_ldordy(true); | 253 | w.set_ldordy(true); |
| 233 | }); | 254 | }); |
| @@ -239,22 +260,15 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 239 | T::regs().isr().modify(|w| { | 260 | T::regs().isr().modify(|w| { |
| 240 | w.set_ldordy(true); | 261 | w.set_ldordy(true); |
| 241 | }); | 262 | }); |
| 242 | } | ||
| 243 | 263 | ||
| 244 | fn calibrate(&mut self) { | ||
| 245 | T::regs().cr().modify(|w| w.set_adcal(true)); | 264 | T::regs().cr().modify(|w| w.set_adcal(true)); |
| 246 | while T::regs().cr().read().adcal() {} | 265 | while T::regs().cr().read().adcal() {} |
| 247 | T::regs().isr().modify(|w| w.set_eocal(true)); | 266 | T::regs().isr().modify(|w| w.set_eocal(true)); |
| 248 | } | ||
| 249 | 267 | ||
| 250 | fn enable(&mut self) { | 268 | blocking_delay_us(1); |
| 251 | T::regs().isr().write(|w| w.set_adrdy(true)); | 269 | |
| 252 | T::regs().cr().modify(|w| w.set_aden(true)); | 270 | T::enable(); |
| 253 | while !T::regs().isr().read().adrdy() {} | ||
| 254 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 255 | } | ||
| 256 | 271 | ||
| 257 | fn configure(&mut self) { | ||
| 258 | // single conversion mode, software trigger | 272 | // single conversion mode, software trigger |
| 259 | T::regs().cfgr1().modify(|w| { | 273 | T::regs().cfgr1().modify(|w| { |
| 260 | #[cfg(stm32u5)] | 274 | #[cfg(stm32u5)] |
| @@ -280,61 +294,63 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 280 | w.set_smpsel(i, Smpsel::SMP1); | 294 | w.set_smpsel(i, Smpsel::SMP1); |
| 281 | } | 295 | } |
| 282 | }); | 296 | }); |
| 297 | |||
| 298 | Self { adc } | ||
| 283 | } | 299 | } |
| 284 | 300 | ||
| 285 | /// Enable reading the voltage reference internal channel. | 301 | /// Enable reading the voltage reference internal channel. |
| 286 | pub fn enable_vrefint(&self) -> VrefInt { | 302 | pub fn enable_vrefint_adc4(&self) -> super::VrefInt { |
| 287 | T::regs().ccr().modify(|w| { | 303 | T::regs().ccr().modify(|w| { |
| 288 | w.set_vrefen(true); | 304 | w.set_vrefen(true); |
| 289 | }); | 305 | }); |
| 290 | 306 | ||
| 291 | VrefInt {} | 307 | super::VrefInt {} |
| 292 | } | 308 | } |
| 293 | 309 | ||
| 294 | /// Enable reading the temperature internal channel. | 310 | /// Enable reading the temperature internal channel. |
| 295 | pub fn enable_temperature(&self) -> Temperature { | 311 | pub fn enable_temperature_adc4(&self) -> super::Temperature { |
| 296 | T::regs().ccr().modify(|w| { | 312 | T::regs().ccr().modify(|w| { |
| 297 | w.set_vsensesel(true); | 313 | w.set_vsensesel(true); |
| 298 | }); | 314 | }); |
| 299 | 315 | ||
| 300 | Temperature {} | 316 | super::Temperature {} |
| 301 | } | 317 | } |
| 302 | 318 | ||
| 303 | /// Enable reading the vbat internal channel. | 319 | /// Enable reading the vbat internal channel. |
| 304 | #[cfg(stm32u5)] | 320 | #[cfg(stm32u5)] |
| 305 | pub fn enable_vbat(&self) -> Vbat { | 321 | pub fn enable_vbat_adc4(&self) -> super::Vbat { |
| 306 | T::regs().ccr().modify(|w| { | 322 | T::regs().ccr().modify(|w| { |
| 307 | w.set_vbaten(true); | 323 | w.set_vbaten(true); |
| 308 | }); | 324 | }); |
| 309 | 325 | ||
| 310 | Vbat {} | 326 | super::Vbat {} |
| 311 | } | 327 | } |
| 312 | 328 | ||
| 313 | /// Enable reading the vbat internal channel. | 329 | /// Enable reading the vbat internal channel. |
| 314 | pub fn enable_vcore(&self) -> Vcore { | 330 | pub fn enable_vcore_adc4(&self) -> super::Vcore { |
| 315 | Vcore {} | 331 | super::Vcore {} |
| 316 | } | 332 | } |
| 317 | 333 | ||
| 318 | /// Enable reading the vbat internal channel. | 334 | /// Enable reading the vbat internal channel. |
| 319 | #[cfg(stm32u5)] | 335 | #[cfg(stm32u5)] |
| 320 | pub fn enable_dac_channel(&self, dac: DacChannel) -> Dac { | 336 | pub fn enable_dac_channel_adc4(&self, dac: DacChannel) -> super::Dac { |
| 321 | let mux; | 337 | let mux; |
| 322 | match dac { | 338 | match dac { |
| 323 | DacChannel::OUT1 => mux = false, | 339 | DacChannel::OUT1 => mux = false, |
| 324 | DacChannel::OUT2 => mux = true, | 340 | DacChannel::OUT2 => mux = true, |
| 325 | } | 341 | } |
| 326 | T::regs().or().modify(|w| w.set_chn21sel(mux)); | 342 | T::regs().or().modify(|w| w.set_chn21sel(mux)); |
| 327 | Dac {} | 343 | super::Dac {} |
| 328 | } | 344 | } |
| 329 | 345 | ||
| 330 | /// Set the ADC resolution. | 346 | /// Set the ADC resolution. |
| 331 | pub fn set_resolution(&mut self, resolution: Resolution) { | 347 | pub fn set_resolution_adc4(&mut self, resolution: Resolution) { |
| 332 | T::regs().cfgr1().modify(|w| w.set_res(resolution.into())); | 348 | T::regs().cfgr1().modify(|w| w.set_res(resolution.into())); |
| 333 | } | 349 | } |
| 334 | 350 | ||
| 335 | /// Set hardware averaging. | 351 | /// Set hardware averaging. |
| 336 | #[cfg(stm32u5)] | 352 | #[cfg(stm32u5)] |
| 337 | pub fn set_averaging(&mut self, averaging: Averaging) { | 353 | pub fn set_averaging_adc4(&mut self, averaging: Averaging) { |
| 338 | let (enable, samples, right_shift) = match averaging { | 354 | let (enable, samples, right_shift) = match averaging { |
| 339 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, 0), | 355 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, 0), |
| 340 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, 1), | 356 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, 1), |
| @@ -354,7 +370,7 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 354 | }) | 370 | }) |
| 355 | } | 371 | } |
| 356 | #[cfg(stm32wba)] | 372 | #[cfg(stm32wba)] |
| 357 | pub fn set_averaging(&mut self, averaging: Averaging) { | 373 | pub fn set_averaging_adc4(&mut self, averaging: Averaging) { |
| 358 | let (enable, samples, right_shift) = match averaging { | 374 | let (enable, samples, right_shift) = match averaging { |
| 359 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT0), | 375 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT0), |
| 360 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT1), | 376 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT1), |
| @@ -373,168 +389,4 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 373 | w.set_ovse(enable) | 389 | w.set_ovse(enable) |
| 374 | }) | 390 | }) |
| 375 | } | 391 | } |
| 376 | |||
| 377 | /// Read an ADC channel. | ||
| 378 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 379 | T::regs().smpr().modify(|w| { | ||
| 380 | w.set_smp(0, sample_time); | ||
| 381 | }); | ||
| 382 | |||
| 383 | channel.setup(); | ||
| 384 | |||
| 385 | // Select channel | ||
| 386 | #[cfg(stm32wba)] | ||
| 387 | { | ||
| 388 | T::regs().chselr().write_value(Chselr(0_u32)); | ||
| 389 | T::regs().chselr().modify(|w| { | ||
| 390 | w.set_chsel0(channel.channel() as usize, true); | ||
| 391 | }); | ||
| 392 | } | ||
| 393 | #[cfg(stm32u5)] | ||
| 394 | { | ||
| 395 | T::regs().chselrmod0().write_value(Chselr(0_u32)); | ||
| 396 | T::regs().chselrmod0().modify(|w| { | ||
| 397 | w.set_chsel(channel.channel() as usize, true); | ||
| 398 | }); | ||
| 399 | } | ||
| 400 | |||
| 401 | // Reset interrupts | ||
| 402 | T::regs().isr().modify(|reg| { | ||
| 403 | reg.set_eos(true); | ||
| 404 | reg.set_eoc(true); | ||
| 405 | }); | ||
| 406 | |||
| 407 | // Start conversion | ||
| 408 | T::regs().cr().modify(|reg| { | ||
| 409 | reg.set_adstart(true); | ||
| 410 | }); | ||
| 411 | |||
| 412 | while !T::regs().isr().read().eos() { | ||
| 413 | // spin | ||
| 414 | } | ||
| 415 | |||
| 416 | T::regs().dr().read().0 as u16 | ||
| 417 | } | ||
| 418 | |||
| 419 | /// Read one or multiple ADC channels using DMA. | ||
| 420 | /// | ||
| 421 | /// `sequence` iterator and `readings` must have the same length. | ||
| 422 | /// The channels in `sequence` must be in ascending order. | ||
| 423 | /// | ||
| 424 | /// Example | ||
| 425 | /// ```rust,ignore | ||
| 426 | /// use embassy_stm32::adc::adc4; | ||
| 427 | /// use embassy_stm32::adc::AdcChannel; | ||
| 428 | /// | ||
| 429 | /// let mut adc4 = adc4::Adc4::new(p.ADC4); | ||
| 430 | /// let mut adc4_pin1 = p.PC1; | ||
| 431 | /// let mut adc4_pin2 = p.PC0; | ||
| 432 | /// let mut.into()d41 = adc4_pin1.into(); | ||
| 433 | /// let mut.into()d42 = adc4_pin2.into(); | ||
| 434 | /// let mut measurements = [0u16; 2]; | ||
| 435 | /// // not that the channels must be in ascending order | ||
| 436 | /// adc4.read( | ||
| 437 | /// &mut p.GPDMA1_CH1, | ||
| 438 | /// [ | ||
| 439 | /// &mut.into()d42, | ||
| 440 | /// &mut.into()d41, | ||
| 441 | /// ] | ||
| 442 | /// .into_iter(), | ||
| 443 | /// &mut measurements, | ||
| 444 | /// ).await.unwrap(); | ||
| 445 | /// ``` | ||
| 446 | pub async fn read( | ||
| 447 | &mut self, | ||
| 448 | rx_dma: Peri<'_, impl RxDma4<T>>, | ||
| 449 | sequence: impl ExactSizeIterator<Item = &mut AnyAdcChannel<T>>, | ||
| 450 | readings: &mut [u16], | ||
| 451 | ) -> Result<(), Adc4Error> { | ||
| 452 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 453 | assert!( | ||
| 454 | sequence.len() == readings.len(), | ||
| 455 | "Sequence length must be equal to readings length" | ||
| 456 | ); | ||
| 457 | |||
| 458 | // Ensure no conversions are ongoing | ||
| 459 | Self::cancel_conversions(); | ||
| 460 | |||
| 461 | T::regs().isr().modify(|reg| { | ||
| 462 | reg.set_ovr(true); | ||
| 463 | reg.set_eos(true); | ||
| 464 | reg.set_eoc(true); | ||
| 465 | }); | ||
| 466 | |||
| 467 | T::regs().cfgr1().modify(|reg| { | ||
| 468 | reg.set_dmaen(true); | ||
| 469 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 470 | #[cfg(stm32u5)] | ||
| 471 | reg.set_chselrmod(false); | ||
| 472 | #[cfg(stm32wba)] | ||
| 473 | reg.set_chselrmod(Chselrmod::ENABLE_INPUT) | ||
| 474 | }); | ||
| 475 | |||
| 476 | // Verify and activate sequence | ||
| 477 | let mut prev_channel: i16 = -1; | ||
| 478 | #[cfg(stm32wba)] | ||
| 479 | T::regs().chselr().write_value(Chselr(0_u32)); | ||
| 480 | #[cfg(stm32u5)] | ||
| 481 | T::regs().chselrmod0().write_value(Chselr(0_u32)); | ||
| 482 | for channel in sequence { | ||
| 483 | let channel_num = channel.channel; | ||
| 484 | if channel_num as i16 <= prev_channel { | ||
| 485 | return Err(Adc4Error::InvalidSequence); | ||
| 486 | }; | ||
| 487 | prev_channel = channel_num as i16; | ||
| 488 | |||
| 489 | #[cfg(stm32wba)] | ||
| 490 | T::regs().chselr().modify(|w| { | ||
| 491 | w.set_chsel0(channel.channel as usize, true); | ||
| 492 | }); | ||
| 493 | #[cfg(stm32u5)] | ||
| 494 | T::regs().chselrmod0().modify(|w| { | ||
| 495 | w.set_chsel(channel.channel as usize, true); | ||
| 496 | }); | ||
| 497 | } | ||
| 498 | |||
| 499 | let request = rx_dma.request(); | ||
| 500 | let transfer = unsafe { | ||
| 501 | Transfer::new_read( | ||
| 502 | rx_dma, | ||
| 503 | request, | ||
| 504 | T::regs().dr().as_ptr() as *mut u16, | ||
| 505 | readings, | ||
| 506 | Default::default(), | ||
| 507 | ) | ||
| 508 | }; | ||
| 509 | |||
| 510 | // Start conversion | ||
| 511 | T::regs().cr().modify(|reg| { | ||
| 512 | reg.set_adstart(true); | ||
| 513 | }); | ||
| 514 | |||
| 515 | transfer.await; | ||
| 516 | |||
| 517 | // Ensure conversions are finished. | ||
| 518 | Self::cancel_conversions(); | ||
| 519 | |||
| 520 | // Reset configuration. | ||
| 521 | T::regs().cfgr1().modify(|reg| { | ||
| 522 | reg.set_dmaen(false); | ||
| 523 | }); | ||
| 524 | |||
| 525 | if T::regs().isr().read().ovr() { | ||
| 526 | Err(Adc4Error::DMAError) | ||
| 527 | } else { | ||
| 528 | Ok(()) | ||
| 529 | } | ||
| 530 | } | ||
| 531 | |||
| 532 | fn cancel_conversions() { | ||
| 533 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 534 | T::regs().cr().modify(|reg| { | ||
| 535 | reg.set_adstp(true); | ||
| 536 | }); | ||
| 537 | while T::regs().cr().read().adstart() {} | ||
| 538 | } | ||
| 539 | } | ||
| 540 | } | 392 | } |
diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index bc97a7c4b..3e109e429 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,7 +17,6 @@ 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 NUM_HW_CHANNELS: u8 = 22; | ||
| 23 | const CHSELR_SQ_SIZE: usize = 8; | 20 | const CHSELR_SQ_SIZE: usize = 8; |
| 24 | const CHSELR_SQ_MAX_CHANNEL: u8 = 14; | 21 | const CHSELR_SQ_MAX_CHANNEL: u8 = 14; |
| 25 | const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111; | 22 | const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111; |
| @@ -32,123 +29,169 @@ impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { | |||
| 32 | const CHANNEL: u8 = 9; | 29 | const CHANNEL: u8 = 9; |
| 33 | } | 30 | } |
| 34 | 31 | ||
| 35 | #[derive(Copy, Clone, Debug)] | 32 | fn from_ker_ck(frequency: Hertz) -> Presc { |
| 36 | pub enum Prescaler { | 33 | let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); |
| 37 | NotDivided, | 34 | match raw_prescaler { |
| 38 | DividedBy2, | 35 | 0 => Presc::DIV1, |
| 39 | DividedBy4, | 36 | 1 => Presc::DIV2, |
| 40 | DividedBy6, | 37 | 2..=3 => Presc::DIV4, |
| 41 | DividedBy8, | 38 | 4..=5 => Presc::DIV6, |
| 42 | DividedBy10, | 39 | 6..=7 => Presc::DIV8, |
| 43 | DividedBy12, | 40 | 8..=9 => Presc::DIV10, |
| 44 | DividedBy16, | 41 | 10..=11 => Presc::DIV12, |
| 45 | DividedBy32, | 42 | _ => unimplemented!(), |
| 46 | DividedBy64, | 43 | } |
| 47 | DividedBy128, | ||
| 48 | DividedBy256, | ||
| 49 | } | 44 | } |
| 50 | 45 | ||
| 51 | impl Prescaler { | 46 | impl<T: Instance> super::SealedAnyInstance for T { |
| 52 | fn from_ker_ck(frequency: Hertz) -> Self { | 47 | fn dr() -> *mut u16 { |
| 53 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | 48 | T::regs().dr().as_ptr() as *mut u16 |
| 54 | match raw_prescaler { | 49 | } |
| 55 | 0 => Self::NotDivided, | 50 | |
| 56 | 1 => Self::DividedBy2, | 51 | fn enable() { |
| 57 | 2..=3 => Self::DividedBy4, | 52 | T::regs().isr().modify(|w| w.set_adrdy(true)); |
| 58 | 4..=5 => Self::DividedBy6, | 53 | T::regs().cr().modify(|w| w.set_aden(true)); |
| 59 | 6..=7 => Self::DividedBy8, | 54 | // ADRDY is "ADC ready". Wait until it will be True. |
| 60 | 8..=9 => Self::DividedBy10, | 55 | while !T::regs().isr().read().adrdy() {} |
| 61 | 10..=11 => Self::DividedBy12, | ||
| 62 | _ => unimplemented!(), | ||
| 63 | } | ||
| 64 | } | 56 | } |
| 65 | 57 | ||
| 66 | #[allow(unused)] | 58 | fn start() { |
| 67 | fn divisor(&self) -> u32 { | 59 | // Start conversion |
| 68 | match self { | 60 | T::regs().cr().modify(|reg| { |
| 69 | Prescaler::NotDivided => 1, | 61 | reg.set_adstart(true); |
| 70 | Prescaler::DividedBy2 => 2, | 62 | }); |
| 71 | Prescaler::DividedBy4 => 4, | 63 | } |
| 72 | Prescaler::DividedBy6 => 6, | 64 | |
| 73 | Prescaler::DividedBy8 => 8, | 65 | fn stop() { |
| 74 | Prescaler::DividedBy10 => 10, | 66 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 75 | Prescaler::DividedBy12 => 12, | 67 | T::regs().cr().modify(|reg| { |
| 76 | Prescaler::DividedBy16 => 16, | 68 | reg.set_adstp(Adstp::STOP); |
| 77 | Prescaler::DividedBy32 => 32, | 69 | }); |
| 78 | Prescaler::DividedBy64 => 64, | 70 | while T::regs().cr().read().adstart() {} |
| 79 | Prescaler::DividedBy128 => 128, | ||
| 80 | Prescaler::DividedBy256 => 256, | ||
| 81 | } | 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 | }); | ||
| 82 | } | 79 | } |
| 83 | 80 | ||
| 84 | fn presc(&self) -> Presc { | 81 | fn configure_dma(conversion_mode: super::ConversionMode) { |
| 85 | match self { | 82 | match conversion_mode { |
| 86 | Prescaler::NotDivided => Presc::DIV1, | 83 | ConversionMode::Singular => { |
| 87 | Prescaler::DividedBy2 => Presc::DIV2, | 84 | // Enable overrun control, so no new DMA requests will be generated until |
| 88 | Prescaler::DividedBy4 => Presc::DIV4, | 85 | // previous DR values is read. |
| 89 | Prescaler::DividedBy6 => Presc::DIV6, | 86 | T::regs().isr().modify(|reg| { |
| 90 | Prescaler::DividedBy8 => Presc::DIV8, | 87 | reg.set_ovr(true); |
| 91 | Prescaler::DividedBy10 => Presc::DIV10, | 88 | }); |
| 92 | Prescaler::DividedBy12 => Presc::DIV12, | 89 | |
| 93 | Prescaler::DividedBy16 => Presc::DIV16, | 90 | // Set continuous mode with oneshot dma. |
| 94 | Prescaler::DividedBy32 => Presc::DIV32, | 91 | T::regs().cfgr1().modify(|reg| { |
| 95 | Prescaler::DividedBy64 => Presc::DIV64, | 92 | reg.set_discen(false); |
| 96 | Prescaler::DividedBy128 => Presc::DIV128, | 93 | reg.set_cont(true); |
| 97 | 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 | } | ||
| 98 | } | 99 | } |
| 99 | } | 100 | } |
| 100 | } | ||
| 101 | 101 | ||
| 102 | #[cfg(feature = "defmt")] | 102 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>) { |
| 103 | impl<'a> defmt::Format for Prescaler { | 103 | let mut needs_hw = sequence.len() == 1 || sequence.len() > CHSELR_SQ_SIZE; |
| 104 | fn format(&self, fmt: defmt::Formatter) { | 104 | let mut is_ordered_up = true; |
| 105 | match self { | 105 | let mut is_ordered_down = true; |
| 106 | Prescaler::NotDivided => defmt::write!(fmt, "Prescaler::NotDivided"), | 106 | |
| 107 | Prescaler::DividedBy2 => defmt::write!(fmt, "Prescaler::DividedBy2"), | 107 | let sequence_len = sequence.len(); |
| 108 | Prescaler::DividedBy4 => defmt::write!(fmt, "Prescaler::DividedBy4"), | 108 | let mut hw_channel_selection: u32 = 0; |
| 109 | Prescaler::DividedBy6 => defmt::write!(fmt, "Prescaler::DividedBy6"), | 109 | let mut last_channel: u8 = 0; |
| 110 | Prescaler::DividedBy8 => defmt::write!(fmt, "Prescaler::DividedBy8"), | 110 | let mut sample_time: Self::SampleTime = SampleTime::CYCLES2_5; |
| 111 | Prescaler::DividedBy10 => defmt::write!(fmt, "Prescaler::DividedBy10"), | 111 | |
| 112 | Prescaler::DividedBy12 => defmt::write!(fmt, "Prescaler::DividedBy12"), | 112 | T::regs().chselr_sq().write(|w| { |
| 113 | Prescaler::DividedBy16 => defmt::write!(fmt, "Prescaler::DividedBy16"), | 113 | for (i, ((channel, _), _sample_time)) in sequence.enumerate() { |
| 114 | Prescaler::DividedBy32 => defmt::write!(fmt, "Prescaler::DividedBy32"), | 114 | assert!( |
| 115 | Prescaler::DividedBy64 => defmt::write!(fmt, "Prescaler::DividedBy64"), | 115 | sample_time == _sample_time || i == 0, |
| 116 | Prescaler::DividedBy128 => defmt::write!(fmt, "Prescaler::DividedBy128"), | 116 | "C0 only supports one sample time for the sequence." |
| 117 | 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) } | ||
| 118 | } | 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() {} | ||
| 119 | } | 166 | } |
| 120 | } | ||
| 121 | 167 | ||
| 122 | /// Number of samples used for averaging. | 168 | fn convert() -> u16 { |
| 123 | /// TODO: Implement hardware averaging setting. | 169 | // Set single conversion mode. |
| 124 | #[allow(unused)] | 170 | T::regs().cfgr1().modify(|w| w.set_cont(false)); |
| 125 | #[derive(Copy, Clone, Debug)] | 171 | |
| 126 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 172 | // Start conversion |
| 127 | pub enum Averaging { | 173 | T::regs().cr().modify(|reg| { |
| 128 | Disabled, | 174 | reg.set_adstart(true); |
| 129 | Samples2, | 175 | }); |
| 130 | Samples4, | 176 | |
| 131 | Samples8, | 177 | // Waiting for End Of Conversion (EOC). |
| 132 | Samples16, | 178 | while !T::regs().isr().read().eoc() {} |
| 133 | Samples32, | 179 | |
| 134 | Samples64, | 180 | T::regs().dr().read().data() as u16 |
| 135 | Samples128, | 181 | } |
| 136 | Samples256, | ||
| 137 | Samples512, | ||
| 138 | Samples1024, | ||
| 139 | } | 182 | } |
| 140 | 183 | ||
| 141 | impl<'d, T: Instance> Adc<'d, T> { | 184 | impl<'d, T: AnyInstance> Adc<'d, T> { |
| 142 | /// Create a new ADC driver. | 185 | /// Create a new ADC driver. |
| 143 | pub fn new(adc: Peri<'d, T>, resolution: Resolution) -> Self { | 186 | pub fn new(adc: Peri<'d, T>, resolution: Resolution) -> Self { |
| 144 | rcc::enable_and_reset::<T>(); | 187 | rcc::enable_and_reset::<T>(); |
| 145 | 188 | ||
| 146 | T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); | 189 | T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); |
| 147 | 190 | ||
| 148 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 191 | let prescaler = from_ker_ck(T::frequency()); |
| 149 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 192 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 150 | 193 | ||
| 151 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 194 | let frequency = T::frequency() / prescaler; |
| 152 | debug!("ADC frequency set to {}", frequency); | 195 | debug!("ADC frequency set to {}", frequency); |
| 153 | 196 | ||
| 154 | if frequency > MAX_ADC_CLK_FREQ { | 197 | if frequency > MAX_ADC_CLK_FREQ { |
| @@ -158,32 +201,16 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 158 | ); | 201 | ); |
| 159 | } | 202 | } |
| 160 | 203 | ||
| 161 | let mut s = Self { adc }; | ||
| 162 | |||
| 163 | s.power_up(); | ||
| 164 | |||
| 165 | s.set_resolution(resolution); | ||
| 166 | |||
| 167 | s.calibrate(); | ||
| 168 | |||
| 169 | s.enable(); | ||
| 170 | |||
| 171 | s.configure_default(); | ||
| 172 | |||
| 173 | s | ||
| 174 | } | ||
| 175 | |||
| 176 | fn power_up(&mut self) { | ||
| 177 | T::regs().cr().modify(|reg| { | 204 | T::regs().cr().modify(|reg| { |
| 178 | reg.set_advregen(true); | 205 | reg.set_advregen(true); |
| 179 | }); | 206 | }); |
| 180 | 207 | ||
| 181 | // "The software must wait for the ADC voltage regulator startup time." | 208 | // "The software must wait for the ADC voltage regulator startup time." |
| 182 | // See datasheet for the value. | 209 | // See datasheet for the value. |
| 183 | blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US + 1); | 210 | blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US as u64 + 1); |
| 184 | } | 211 | |
| 212 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); | ||
| 185 | 213 | ||
| 186 | fn calibrate(&mut self) { | ||
| 187 | // 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. |
| 188 | let autoff_value = T::regs().cfgr1().read().autoff(); | 215 | let autoff_value = T::regs().cfgr1().read().autoff(); |
| 189 | T::regs().cfgr1().modify(|w| w.set_autoff(false)); | 216 | T::regs().cfgr1().modify(|w| w.set_autoff(false)); |
| @@ -197,22 +224,17 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 197 | debug!("ADC calibration value: {}.", T::regs().dr().read().data()); | 224 | debug!("ADC calibration value: {}.", T::regs().dr().read().data()); |
| 198 | 225 | ||
| 199 | T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); | 226 | T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); |
| 200 | } | ||
| 201 | 227 | ||
| 202 | fn enable(&mut self) { | 228 | T::enable(); |
| 203 | T::regs().isr().modify(|w| w.set_adrdy(true)); | ||
| 204 | T::regs().cr().modify(|w| w.set_aden(true)); | ||
| 205 | // ADRDY is "ADC ready". Wait until it will be True. | ||
| 206 | while !T::regs().isr().read().adrdy() {} | ||
| 207 | } | ||
| 208 | 229 | ||
| 209 | fn configure_default(&mut self) { | ||
| 210 | // single conversion mode, software trigger | 230 | // single conversion mode, software trigger |
| 211 | T::regs().cfgr1().modify(|w| { | 231 | T::regs().cfgr1().modify(|w| { |
| 212 | w.set_cont(false); | 232 | w.set_cont(false); |
| 213 | w.set_exten(Exten::DISABLED); | 233 | w.set_exten(Exten::DISABLED); |
| 214 | w.set_align(Align::RIGHT); | 234 | w.set_align(Align::RIGHT); |
| 215 | }); | 235 | }); |
| 236 | |||
| 237 | Self { adc } | ||
| 216 | } | 238 | } |
| 217 | 239 | ||
| 218 | /// Enable reading the voltage reference internal channel. | 240 | /// Enable reading the voltage reference internal channel. |
| @@ -233,219 +255,4 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 233 | 255 | ||
| 234 | super::Temperature {} | 256 | super::Temperature {} |
| 235 | } | 257 | } |
| 236 | |||
| 237 | /// Set the ADC sample time. | ||
| 238 | /// Shall only be called when ADC is not converting. | ||
| 239 | pub fn set_sample_time_all_channels(&mut self, sample_time: SampleTime) { | ||
| 240 | // Set all channels to use SMP1 field as source. | ||
| 241 | T::regs().smpr().modify(|w| { | ||
| 242 | w.smpsel(0); | ||
| 243 | w.set_smp1(sample_time); | ||
| 244 | }); | ||
| 245 | } | ||
| 246 | |||
| 247 | /// Set the ADC resolution. | ||
| 248 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 249 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); | ||
| 250 | } | ||
| 251 | |||
| 252 | /// Perform a single conversion. | ||
| 253 | fn convert(&mut self) -> u16 { | ||
| 254 | // Set single conversion mode. | ||
| 255 | T::regs().cfgr1().modify(|w| w.set_cont(false)); | ||
| 256 | |||
| 257 | // Start conversion | ||
| 258 | T::regs().cr().modify(|reg| { | ||
| 259 | reg.set_adstart(true); | ||
| 260 | }); | ||
| 261 | |||
| 262 | // Waiting for End Of Conversion (EOC). | ||
| 263 | while !T::regs().isr().read().eoc() {} | ||
| 264 | |||
| 265 | T::regs().dr().read().data() as u16 | ||
| 266 | } | ||
| 267 | |||
| 268 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 269 | self.set_sample_time_all_channels(sample_time); | ||
| 270 | |||
| 271 | Self::configure_channel(channel); | ||
| 272 | T::regs().cfgr1().write(|reg| { | ||
| 273 | reg.set_chselrmod(false); | ||
| 274 | reg.set_align(Align::RIGHT); | ||
| 275 | }); | ||
| 276 | self.convert() | ||
| 277 | } | ||
| 278 | |||
| 279 | fn setup_channel_sequencer<'a>(channel_sequence: impl ExactSizeIterator<Item = &'a mut AnyAdcChannel<T>>) { | ||
| 280 | assert!( | ||
| 281 | channel_sequence.len() <= CHSELR_SQ_SIZE, | ||
| 282 | "Seqenced read set cannot be more than {} in size.", | ||
| 283 | CHSELR_SQ_SIZE | ||
| 284 | ); | ||
| 285 | let mut last_sq_set: usize = 0; | ||
| 286 | T::regs().chselr_sq().write(|w| { | ||
| 287 | for (i, channel) in channel_sequence.enumerate() { | ||
| 288 | assert!( | ||
| 289 | channel.channel() <= CHSELR_SQ_MAX_CHANNEL, | ||
| 290 | "Sequencer only support HW channels smaller than {}.", | ||
| 291 | CHSELR_SQ_MAX_CHANNEL | ||
| 292 | ); | ||
| 293 | w.set_sq(i, channel.channel()); | ||
| 294 | last_sq_set = i; | ||
| 295 | } | ||
| 296 | |||
| 297 | for i in (last_sq_set + 1)..CHSELR_SQ_SIZE { | ||
| 298 | w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER); | ||
| 299 | } | ||
| 300 | }); | ||
| 301 | |||
| 302 | Self::apply_channel_conf() | ||
| 303 | } | ||
| 304 | |||
| 305 | async fn dma_convert(&mut self, rx_dma: Peri<'_, impl RxDma<T>>, readings: &mut [u16]) { | ||
| 306 | // Enable overrun control, so no new DMA requests will be generated until | ||
| 307 | // previous DR values is read. | ||
| 308 | T::regs().isr().modify(|reg| { | ||
| 309 | reg.set_ovr(true); | ||
| 310 | }); | ||
| 311 | |||
| 312 | // Set continuous mode with oneshot dma. | ||
| 313 | T::regs().cfgr1().modify(|reg| { | ||
| 314 | reg.set_discen(false); | ||
| 315 | reg.set_cont(true); | ||
| 316 | reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT); | ||
| 317 | reg.set_dmaen(true); | ||
| 318 | reg.set_ovrmod(Ovrmod::PRESERVE); | ||
| 319 | }); | ||
| 320 | |||
| 321 | let request = rx_dma.request(); | ||
| 322 | let transfer = unsafe { | ||
| 323 | Transfer::new_read( | ||
| 324 | rx_dma, | ||
| 325 | request, | ||
| 326 | T::regs().dr().as_ptr() as *mut u16, | ||
| 327 | readings, | ||
| 328 | Default::default(), | ||
| 329 | ) | ||
| 330 | }; | ||
| 331 | |||
| 332 | // Start conversion. | ||
| 333 | T::regs().cr().modify(|reg| { | ||
| 334 | reg.set_adstart(true); | ||
| 335 | }); | ||
| 336 | |||
| 337 | // Wait for conversion sequence to finish. | ||
| 338 | transfer.await; | ||
| 339 | |||
| 340 | // Ensure conversions are finished. | ||
| 341 | Self::cancel_conversions(); | ||
| 342 | |||
| 343 | // Reset configuration. | ||
| 344 | T::regs().cfgr1().modify(|reg| { | ||
| 345 | reg.set_cont(false); | ||
| 346 | reg.set_dmacfg(Dmacfg::from_bits(0)); | ||
| 347 | reg.set_dmaen(false); | ||
| 348 | }); | ||
| 349 | } | ||
| 350 | |||
| 351 | /// Read one or multiple ADC channels using DMA in hardware order. | ||
| 352 | /// Readings will be ordered based on **hardware** ADC channel number and `scandir` setting. | ||
| 353 | /// Readings won't be in the same order as in the `set`! | ||
| 354 | /// | ||
| 355 | /// In STM32C0, channels bigger than 14 cannot be read using sequencer, so you have to use | ||
| 356 | /// either blocking reads or use the mechanism to read in HW order (CHSELRMOD=0). | ||
| 357 | /// TODO(chudsaviet): externalize generic code and merge with read(). | ||
| 358 | pub async fn read_in_hw_order( | ||
| 359 | &mut self, | ||
| 360 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 361 | hw_channel_selection: u32, | ||
| 362 | scandir: Scandir, | ||
| 363 | readings: &mut [u16], | ||
| 364 | ) { | ||
| 365 | assert!( | ||
| 366 | hw_channel_selection != 0, | ||
| 367 | "Some bits in `hw_channel_selection` shall be set." | ||
| 368 | ); | ||
| 369 | assert!( | ||
| 370 | (hw_channel_selection >> NUM_HW_CHANNELS) == 0, | ||
| 371 | "STM32C0 only have {} ADC channels. `hw_channel_selection` cannot have bits higher than this number set.", | ||
| 372 | NUM_HW_CHANNELS | ||
| 373 | ); | ||
| 374 | // To check for correct readings slice size, we shall solve Hamming weight problem, | ||
| 375 | // which is either slow or memory consuming. | ||
| 376 | // Since we have limited resources, we don't do it here. | ||
| 377 | // Not doing this have a great potential for a bug through. | ||
| 378 | |||
| 379 | // Ensure no conversions are ongoing. | ||
| 380 | Self::cancel_conversions(); | ||
| 381 | |||
| 382 | T::regs().cfgr1().modify(|reg| { | ||
| 383 | reg.set_chselrmod(false); | ||
| 384 | reg.set_scandir(scandir); | ||
| 385 | reg.set_align(Align::RIGHT); | ||
| 386 | }); | ||
| 387 | |||
| 388 | // Set required channels for multi-convert. | ||
| 389 | unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } | ||
| 390 | |||
| 391 | Self::apply_channel_conf(); | ||
| 392 | |||
| 393 | self.dma_convert(rx_dma, readings).await | ||
| 394 | } | ||
| 395 | |||
| 396 | // Read ADC channels in specified order using DMA (CHSELRMOD = 1). | ||
| 397 | // In STM32C0, only lower 14 ADC channels can be read this way. | ||
| 398 | // For other channels, use `read_in_hw_order()` or blocking read. | ||
| 399 | pub async fn read( | ||
| 400 | &mut self, | ||
| 401 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 402 | channel_sequence: impl ExactSizeIterator<Item = &mut AnyAdcChannel<T>>, | ||
| 403 | readings: &mut [u16], | ||
| 404 | ) { | ||
| 405 | assert!( | ||
| 406 | channel_sequence.len() != 0, | ||
| 407 | "Asynchronous read channel sequence cannot be empty." | ||
| 408 | ); | ||
| 409 | assert!( | ||
| 410 | channel_sequence.len() == readings.len(), | ||
| 411 | "Channel sequence length must be equal to readings length." | ||
| 412 | ); | ||
| 413 | |||
| 414 | // Ensure no conversions are ongoing. | ||
| 415 | Self::cancel_conversions(); | ||
| 416 | |||
| 417 | T::regs().cfgr1().modify(|reg| { | ||
| 418 | reg.set_chselrmod(true); | ||
| 419 | reg.set_align(Align::RIGHT); | ||
| 420 | }); | ||
| 421 | |||
| 422 | Self::setup_channel_sequencer(channel_sequence); | ||
| 423 | |||
| 424 | self.dma_convert(rx_dma, readings).await | ||
| 425 | } | ||
| 426 | |||
| 427 | fn configure_channel(channel: &mut impl AdcChannel<T>) { | ||
| 428 | channel.setup(); | ||
| 429 | // write() because we want all other bits to be set to 0. | ||
| 430 | T::regs() | ||
| 431 | .chselr() | ||
| 432 | .write(|w| w.set_chsel(channel.channel().into(), true)); | ||
| 433 | |||
| 434 | Self::apply_channel_conf(); | ||
| 435 | } | ||
| 436 | |||
| 437 | fn apply_channel_conf() { | ||
| 438 | // Trigger and wait for the channel selection procedure to complete. | ||
| 439 | T::regs().isr().modify(|w| w.set_ccrdy(false)); | ||
| 440 | while !T::regs().isr().read().ccrdy() {} | ||
| 441 | } | ||
| 442 | |||
| 443 | fn cancel_conversions() { | ||
| 444 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 445 | T::regs().cr().modify(|reg| { | ||
| 446 | reg.set_adstp(Adstp::STOP); | ||
| 447 | }); | ||
| 448 | while T::regs().cr().read().adstart() {} | ||
| 449 | } | ||
| 450 | } | ||
| 451 | } | 258 | } |
diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index f6220de78..d6c6f480b 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs | |||
| @@ -43,7 +43,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 43 | 43 | ||
| 44 | // 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’) |
| 45 | // for at least two ADC clock cycles. | 45 | // for at least two ADC clock cycles. |
| 46 | 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); |
| 47 | 47 | ||
| 48 | // Reset calibration | 48 | // Reset calibration |
| 49 | T::regs().cr2().modify(|reg| reg.set_rstcal(true)); | 49 | T::regs().cr2().modify(|reg| reg.set_rstcal(true)); |
| @@ -58,7 +58,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | // One cycle after calibration | 60 | // One cycle after calibration |
| 61 | 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); |
| 62 | 62 | ||
| 63 | T::Interrupt::unpend(); | 63 | T::Interrupt::unpend(); |
| 64 | unsafe { T::Interrupt::enable() }; | 64 | unsafe { T::Interrupt::enable() }; |
diff --git a/embassy-stm32/src/adc/f3.rs b/embassy-stm32/src/adc/f3.rs index 4a77f3c5b..29bfdac97 100644 --- a/embassy-stm32/src/adc/f3.rs +++ b/embassy-stm32/src/adc/f3.rs | |||
| @@ -62,7 +62,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 62 | while T::regs().cr().read().adcal() {} | 62 | while T::regs().cr().read().adcal() {} |
| 63 | 63 | ||
| 64 | // 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). |
| 65 | 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); |
| 66 | 66 | ||
| 67 | // Enable the adc | 67 | // Enable the adc |
| 68 | T::regs().cr().modify(|w| w.set_aden(true)); | 68 | T::regs().cr().modify(|w| w.set_aden(true)); |
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 6430b0243..1767a3bb3 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs | |||
| @@ -1,24 +1,21 @@ | |||
| 1 | use core::mem; | 1 | #[cfg(stm32g4)] |
| 2 | 2 | use pac::adc::regs::Difsel as DifselReg; | |
| 3 | #[allow(unused)] | ||
| 4 | #[cfg(stm32g4)] | ||
| 5 | pub use pac::adc::vals::{Adcaldif, Adstp, Difsel, Dmacfg, Dmaen, Exten, Rovsm, Trovs}; | ||
| 3 | #[allow(unused)] | 6 | #[allow(unused)] |
| 4 | #[cfg(stm32h7)] | 7 | #[cfg(stm32h7)] |
| 5 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; | 8 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; |
| 6 | #[allow(unused)] | 9 | pub use pac::adccommon::vals::{Dual, Presc}; |
| 7 | #[cfg(stm32g4)] | 10 | |
| 8 | pub use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs}; | 11 | use super::{ |
| 9 | pub use pac::adccommon::vals::Presc; | 12 | Adc, AnyAdcChannel, ConversionMode, Instance, RegularConversionMode, Resolution, RxDma, SampleTime, |
| 10 | pub use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; | 13 | blocking_delay_us, |
| 11 | pub use stm32_metapac::adccommon::vals::Dual; | 14 | }; |
| 12 | 15 | use crate::adc::{AnyInstance, SealedAdcChannel}; | |
| 13 | use super::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, blocking_delay_us}; | ||
| 14 | use crate::adc::SealedAdcChannel; | ||
| 15 | use crate::dma::Transfer; | ||
| 16 | use crate::time::Hertz; | 16 | use crate::time::Hertz; |
| 17 | use crate::{Peri, pac, rcc}; | 17 | use crate::{Peri, pac, rcc}; |
| 18 | 18 | ||
| 19 | mod ringbuffered; | ||
| 20 | pub use ringbuffered::RingBufferedAdc; | ||
| 21 | |||
| 22 | mod injected; | 19 | mod injected; |
| 23 | pub use injected::InjectedAdc; | 20 | pub use injected::InjectedAdc; |
| 24 | 21 | ||
| @@ -35,72 +32,31 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); | |||
| 35 | #[cfg(stm32h7)] | 32 | #[cfg(stm32h7)] |
| 36 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); | 33 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); |
| 37 | 34 | ||
| 38 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | 35 | fn from_ker_ck(frequency: Hertz) -> Presc { |
| 39 | // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. | 36 | let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); |
| 40 | #[allow(unused)] | 37 | match raw_prescaler { |
| 41 | enum Prescaler { | 38 | 0 => Presc::DIV1, |
| 42 | NotDivided, | 39 | 1 => Presc::DIV2, |
| 43 | DividedBy2, | 40 | 2..=3 => Presc::DIV4, |
| 44 | DividedBy4, | 41 | 4..=5 => Presc::DIV6, |
| 45 | DividedBy6, | 42 | 6..=7 => Presc::DIV8, |
| 46 | DividedBy8, | 43 | 8..=9 => Presc::DIV10, |
| 47 | DividedBy10, | 44 | 10..=11 => Presc::DIV12, |
| 48 | DividedBy12, | 45 | _ => unimplemented!(), |
| 49 | DividedBy16, | ||
| 50 | DividedBy32, | ||
| 51 | DividedBy64, | ||
| 52 | DividedBy128, | ||
| 53 | DividedBy256, | ||
| 54 | } | ||
| 55 | |||
| 56 | impl Prescaler { | ||
| 57 | fn from_ker_ck(frequency: Hertz) -> Self { | ||
| 58 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | ||
| 59 | match raw_prescaler { | ||
| 60 | 0 => Self::NotDivided, | ||
| 61 | 1 => Self::DividedBy2, | ||
| 62 | 2..=3 => Self::DividedBy4, | ||
| 63 | 4..=5 => Self::DividedBy6, | ||
| 64 | 6..=7 => Self::DividedBy8, | ||
| 65 | 8..=9 => Self::DividedBy10, | ||
| 66 | 10..=11 => Self::DividedBy12, | ||
| 67 | _ => unimplemented!(), | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | fn divisor(&self) -> u32 { | ||
| 72 | match self { | ||
| 73 | Prescaler::NotDivided => 1, | ||
| 74 | Prescaler::DividedBy2 => 2, | ||
| 75 | Prescaler::DividedBy4 => 4, | ||
| 76 | Prescaler::DividedBy6 => 6, | ||
| 77 | Prescaler::DividedBy8 => 8, | ||
| 78 | Prescaler::DividedBy10 => 10, | ||
| 79 | Prescaler::DividedBy12 => 12, | ||
| 80 | Prescaler::DividedBy16 => 16, | ||
| 81 | Prescaler::DividedBy32 => 32, | ||
| 82 | Prescaler::DividedBy64 => 64, | ||
| 83 | Prescaler::DividedBy128 => 128, | ||
| 84 | Prescaler::DividedBy256 => 256, | ||
| 85 | } | ||
| 86 | } | 46 | } |
| 47 | } | ||
| 87 | 48 | ||
| 88 | fn presc(&self) -> Presc { | 49 | /// ADC configuration |
| 89 | match self { | 50 | #[derive(Default)] |
| 90 | Prescaler::NotDivided => Presc::DIV1, | 51 | pub struct AdcConfig { |
| 91 | Prescaler::DividedBy2 => Presc::DIV2, | 52 | pub dual_mode: Option<Dual>, |
| 92 | Prescaler::DividedBy4 => Presc::DIV4, | 53 | pub resolution: Option<Resolution>, |
| 93 | Prescaler::DividedBy6 => Presc::DIV6, | 54 | #[cfg(stm32g4)] |
| 94 | Prescaler::DividedBy8 => Presc::DIV8, | 55 | pub oversampling_shift: Option<u8>, |
| 95 | Prescaler::DividedBy10 => Presc::DIV10, | 56 | #[cfg(stm32g4)] |
| 96 | Prescaler::DividedBy12 => Presc::DIV12, | 57 | pub oversampling_ratio: Option<u8>, |
| 97 | Prescaler::DividedBy16 => Presc::DIV16, | 58 | #[cfg(stm32g4)] |
| 98 | Prescaler::DividedBy32 => Presc::DIV32, | 59 | pub oversampling_mode: Option<(Rovsm, Trovs, bool)>, |
| 99 | Prescaler::DividedBy64 => Presc::DIV64, | ||
| 100 | Prescaler::DividedBy128 => Presc::DIV128, | ||
| 101 | Prescaler::DividedBy256 => Presc::DIV256, | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | 60 | } |
| 105 | 61 | ||
| 106 | // Trigger source for ADC conversions¨ | 62 | // Trigger source for ADC conversions¨ |
| @@ -112,25 +68,189 @@ pub struct ConversionTrigger { | |||
| 112 | pub edge: Exten, | 68 | pub edge: Exten, |
| 113 | } | 69 | } |
| 114 | 70 | ||
| 115 | // Conversion mode for regular ADC channels | 71 | impl<T: Instance> super::SealedAnyInstance for T { |
| 116 | #[derive(Copy, Clone)] | 72 | fn dr() -> *mut u16 { |
| 117 | pub enum RegularConversionMode { | 73 | T::regs().dr().as_ptr() as *mut u16 |
| 118 | // Samples as fast as possible | 74 | } |
| 119 | Continuous, | 75 | |
| 120 | // Sample at rate determined by external trigger | 76 | fn enable() { |
| 121 | Triggered(ConversionTrigger), | 77 | // Make sure bits are off |
| 78 | while T::regs().cr().read().addis() { | ||
| 79 | // spin | ||
| 80 | } | ||
| 81 | |||
| 82 | if !T::regs().cr().read().aden() { | ||
| 83 | // Enable ADC | ||
| 84 | T::regs().isr().modify(|reg| { | ||
| 85 | reg.set_adrdy(true); | ||
| 86 | }); | ||
| 87 | T::regs().cr().modify(|reg| { | ||
| 88 | reg.set_aden(true); | ||
| 89 | }); | ||
| 90 | |||
| 91 | while !T::regs().isr().read().adrdy() { | ||
| 92 | // spin | ||
| 93 | } | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | fn start() { | ||
| 98 | T::regs().cr().modify(|reg| { | ||
| 99 | reg.set_adstart(true); | ||
| 100 | }); | ||
| 101 | } | ||
| 102 | |||
| 103 | fn stop() { | ||
| 104 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 105 | T::regs().cr().modify(|reg| { | ||
| 106 | reg.set_adstp(Adstp::STOP); | ||
| 107 | }); | ||
| 108 | // The software must poll ADSTART until the bit is reset before assuming the | ||
| 109 | // ADC is completely stopped | ||
| 110 | while T::regs().cr().read().adstart() {} | ||
| 111 | } | ||
| 112 | |||
| 113 | // Disable dma control and continuous conversion, if enabled | ||
| 114 | T::regs().cfgr().modify(|reg| { | ||
| 115 | reg.set_cont(false); | ||
| 116 | reg.set_dmaen(Dmaen::DISABLE); | ||
| 117 | }); | ||
| 118 | } | ||
| 119 | |||
| 120 | fn convert() -> u16 { | ||
| 121 | T::regs().isr().modify(|reg| { | ||
| 122 | reg.set_eos(true); | ||
| 123 | reg.set_eoc(true); | ||
| 124 | }); | ||
| 125 | |||
| 126 | // Start conversion | ||
| 127 | T::regs().cr().modify(|reg| { | ||
| 128 | reg.set_adstart(true); | ||
| 129 | }); | ||
| 130 | |||
| 131 | while !T::regs().isr().read().eos() { | ||
| 132 | // spin | ||
| 133 | } | ||
| 134 | |||
| 135 | T::regs().dr().read().0 as u16 | ||
| 136 | } | ||
| 137 | |||
| 138 | fn configure_dma(conversion_mode: ConversionMode) { | ||
| 139 | T::regs().isr().modify(|reg| { | ||
| 140 | reg.set_ovr(true); | ||
| 141 | }); | ||
| 142 | |||
| 143 | T::regs().cfgr().modify(|reg| { | ||
| 144 | reg.set_discen(false); // Convert all channels for each trigger | ||
| 145 | reg.set_dmacfg(match conversion_mode { | ||
| 146 | ConversionMode::Singular => Dmacfg::ONE_SHOT, | ||
| 147 | ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, | ||
| 148 | }); | ||
| 149 | reg.set_dmaen(Dmaen::ENABLE); | ||
| 150 | }); | ||
| 151 | |||
| 152 | if let ConversionMode::Repeated(mode) = conversion_mode { | ||
| 153 | match mode { | ||
| 154 | RegularConversionMode::Continuous => { | ||
| 155 | T::regs().cfgr().modify(|reg| { | ||
| 156 | reg.set_cont(true); | ||
| 157 | }); | ||
| 158 | } | ||
| 159 | RegularConversionMode::Triggered(trigger) => { | ||
| 160 | T::regs().cfgr().modify(|r| { | ||
| 161 | r.set_cont(false); // New trigger is neede for each sample to be read | ||
| 162 | }); | ||
| 163 | |||
| 164 | T::regs().cfgr().modify(|r| { | ||
| 165 | r.set_extsel(trigger.channel); | ||
| 166 | r.set_exten(trigger.edge); | ||
| 167 | }); | ||
| 168 | |||
| 169 | // Regular conversions uses DMA so no need to generate interrupt | ||
| 170 | T::regs().ier().modify(|r| r.set_eosie(false)); | ||
| 171 | } | ||
| 172 | } | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { | ||
| 177 | T::regs().cr().modify(|w| w.set_aden(false)); | ||
| 178 | |||
| 179 | // Set sequence length | ||
| 180 | T::regs().sqr1().modify(|w| { | ||
| 181 | w.set_l(sequence.len() as u8 - 1); | ||
| 182 | }); | ||
| 183 | |||
| 184 | #[cfg(stm32g4)] | ||
| 185 | let mut difsel = DifselReg::default(); | ||
| 186 | let mut smpr = T::regs().smpr().read(); | ||
| 187 | let mut smpr2 = T::regs().smpr2().read(); | ||
| 188 | let mut sqr1 = T::regs().sqr1().read(); | ||
| 189 | let mut sqr2 = T::regs().sqr2().read(); | ||
| 190 | let mut sqr3 = T::regs().sqr3().read(); | ||
| 191 | let mut sqr4 = T::regs().sqr4().read(); | ||
| 192 | |||
| 193 | // Configure channels and ranks | ||
| 194 | for (_i, ((ch, is_differential), sample_time)) in sequence.enumerate() { | ||
| 195 | let sample_time = sample_time.into(); | ||
| 196 | if ch <= 9 { | ||
| 197 | smpr.set_smp(ch as _, sample_time); | ||
| 198 | } else { | ||
| 199 | smpr2.set_smp((ch - 10) as _, sample_time); | ||
| 200 | } | ||
| 201 | |||
| 202 | match _i { | ||
| 203 | 0..=3 => { | ||
| 204 | sqr1.set_sq(_i, ch); | ||
| 205 | } | ||
| 206 | 4..=8 => { | ||
| 207 | sqr2.set_sq(_i - 4, ch); | ||
| 208 | } | ||
| 209 | 9..=13 => { | ||
| 210 | sqr3.set_sq(_i - 9, ch); | ||
| 211 | } | ||
| 212 | 14..=15 => { | ||
| 213 | sqr4.set_sq(_i - 14, ch); | ||
| 214 | } | ||
| 215 | _ => unreachable!(), | ||
| 216 | } | ||
| 217 | |||
| 218 | #[cfg(stm32g4)] | ||
| 219 | { | ||
| 220 | if ch < 18 { | ||
| 221 | difsel.set_difsel( | ||
| 222 | ch.into(), | ||
| 223 | if is_differential { | ||
| 224 | Difsel::DIFFERENTIAL | ||
| 225 | } else { | ||
| 226 | Difsel::SINGLE_ENDED | ||
| 227 | }, | ||
| 228 | ); | ||
| 229 | } | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | T::regs().smpr().write_value(smpr); | ||
| 234 | T::regs().smpr2().write_value(smpr2); | ||
| 235 | T::regs().sqr1().write_value(sqr1); | ||
| 236 | T::regs().sqr2().write_value(sqr2); | ||
| 237 | T::regs().sqr3().write_value(sqr3); | ||
| 238 | T::regs().sqr4().write_value(sqr4); | ||
| 239 | #[cfg(stm32g4)] | ||
| 240 | T::regs().difsel().write_value(difsel); | ||
| 241 | } | ||
| 122 | } | 242 | } |
| 123 | 243 | ||
| 124 | impl<'d, T: Instance> Adc<'d, T> { | 244 | impl<'d, T: Instance + AnyInstance> Adc<'d, T> { |
| 125 | /// Create a new ADC driver. | 245 | /// Create a new ADC driver. |
| 126 | pub fn new(adc: Peri<'d, T>) -> Self { | 246 | pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self { |
| 127 | rcc::enable_and_reset::<T>(); | 247 | rcc::enable_and_reset::<T>(); |
| 128 | 248 | ||
| 129 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 249 | let prescaler = from_ker_ck(T::frequency()); |
| 130 | 250 | ||
| 131 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 251 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 132 | 252 | ||
| 133 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 253 | let frequency = T::frequency() / prescaler; |
| 134 | trace!("ADC frequency set to {}", frequency); | 254 | trace!("ADC frequency set to {}", frequency); |
| 135 | 255 | ||
| 136 | if frequency > MAX_ADC_CLK_FREQ { | 256 | if frequency > MAX_ADC_CLK_FREQ { |
| @@ -173,7 +293,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 173 | 293 | ||
| 174 | blocking_delay_us(20); | 294 | blocking_delay_us(20); |
| 175 | 295 | ||
| 176 | Self::enable(); | 296 | T::enable(); |
| 177 | 297 | ||
| 178 | // single conversion mode, software trigger | 298 | // single conversion mode, software trigger |
| 179 | T::regs().cfgr().modify(|w| { | 299 | T::regs().cfgr().modify(|w| { |
| @@ -181,28 +301,34 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 181 | w.set_exten(Exten::DISABLED); | 301 | w.set_exten(Exten::DISABLED); |
| 182 | }); | 302 | }); |
| 183 | 303 | ||
| 184 | Self { adc } | 304 | if let Some(dual) = config.dual_mode { |
| 185 | } | 305 | T::common_regs().ccr().modify(|reg| { |
| 306 | reg.set_dual(dual); | ||
| 307 | }) | ||
| 308 | } | ||
| 186 | 309 | ||
| 187 | fn enable() { | 310 | if let Some(resolution) = config.resolution { |
| 188 | // Make sure bits are off | 311 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); |
| 189 | while T::regs().cr().read().addis() { | ||
| 190 | // spin | ||
| 191 | } | 312 | } |
| 192 | 313 | ||
| 193 | if !T::regs().cr().read().aden() { | 314 | #[cfg(stm32g4)] |
| 194 | // Enable ADC | 315 | if let Some(shift) = config.oversampling_shift { |
| 195 | T::regs().isr().modify(|reg| { | 316 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); |
| 196 | reg.set_adrdy(true); | 317 | } |
| 197 | }); | ||
| 198 | T::regs().cr().modify(|reg| { | ||
| 199 | reg.set_aden(true); | ||
| 200 | }); | ||
| 201 | 318 | ||
| 202 | while !T::regs().isr().read().adrdy() { | 319 | #[cfg(stm32g4)] |
| 203 | // spin | 320 | if let Some(ratio) = config.oversampling_ratio { |
| 204 | } | 321 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); |
| 205 | } | 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 } | ||
| 206 | } | 332 | } |
| 207 | 333 | ||
| 208 | /// Enable reading the voltage reference internal channel. | 334 | /// Enable reading the voltage reference internal channel. |
| @@ -241,26 +367,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 241 | super::Vbat {} | 367 | super::Vbat {} |
| 242 | } | 368 | } |
| 243 | 369 | ||
| 244 | /// Set oversampling shift. | ||
| 245 | #[cfg(stm32g4)] | ||
| 246 | pub fn set_oversampling_shift(&mut self, shift: u8) { | ||
| 247 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 248 | } | ||
| 249 | |||
| 250 | /// Set oversampling ratio. | ||
| 251 | #[cfg(stm32g4)] | ||
| 252 | pub fn set_oversampling_ratio(&mut self, ratio: u8) { | ||
| 253 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 254 | } | ||
| 255 | |||
| 256 | /// Enable oversampling in regular mode. | ||
| 257 | #[cfg(stm32g4)] | ||
| 258 | pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { | ||
| 259 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 260 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 261 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 262 | } | ||
| 263 | |||
| 264 | // Reads that are not implemented as INJECTED in "blocking_read" | 370 | // Reads that are not implemented as INJECTED in "blocking_read" |
| 265 | // #[cfg(stm32g4)] | 371 | // #[cfg(stm32g4)] |
| 266 | // pub fn enalble_injected_oversampling_mode(&mut self, enable: bool) { | 372 | // pub fn enalble_injected_oversampling_mode(&mut self, enable: bool) { |
| @@ -274,311 +380,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 274 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); | 380 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); |
| 275 | // } | 381 | // } |
| 276 | 382 | ||
| 277 | /// Set the ADC resolution. | ||
| 278 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 279 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 280 | } | ||
| 281 | |||
| 282 | /// Perform a single conversion. | ||
| 283 | fn convert(&mut self) -> u16 { | ||
| 284 | T::regs().isr().modify(|reg| { | ||
| 285 | reg.set_eos(true); | ||
| 286 | reg.set_eoc(true); | ||
| 287 | }); | ||
| 288 | |||
| 289 | // Start conversion | ||
| 290 | T::regs().cr().modify(|reg| { | ||
| 291 | reg.set_adstart(true); | ||
| 292 | }); | ||
| 293 | |||
| 294 | while !T::regs().isr().read().eos() { | ||
| 295 | // spin | ||
| 296 | } | ||
| 297 | |||
| 298 | T::regs().dr().read().0 as u16 | ||
| 299 | } | ||
| 300 | |||
| 301 | /// Read an ADC pin. | ||
| 302 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 303 | channel.setup(); | ||
| 304 | |||
| 305 | Self::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); | ||
| 306 | |||
| 307 | #[cfg(stm32h7)] | ||
| 308 | { | ||
| 309 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 310 | T::regs() | ||
| 311 | .pcsel() | ||
| 312 | .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); | ||
| 313 | } | ||
| 314 | |||
| 315 | self.convert() | ||
| 316 | } | ||
| 317 | |||
| 318 | /// Start regular adc conversion | ||
| 319 | pub(super) fn start() { | ||
| 320 | T::regs().cr().modify(|reg| { | ||
| 321 | reg.set_adstart(true); | ||
| 322 | }); | ||
| 323 | } | ||
| 324 | |||
| 325 | /// Stop regular conversions | ||
| 326 | pub(super) fn stop() { | ||
| 327 | Self::stop_regular_conversions(); | ||
| 328 | } | ||
| 329 | |||
| 330 | /// Teardown method for stopping regular ADC conversions | ||
| 331 | pub(super) fn teardown_dma() { | ||
| 332 | Self::stop_regular_conversions(); | ||
| 333 | |||
| 334 | // Disable dma control | ||
| 335 | T::regs().cfgr().modify(|reg| { | ||
| 336 | reg.set_dmaen(Dmaen::DISABLE); | ||
| 337 | }); | ||
| 338 | } | ||
| 339 | |||
| 340 | /// Read one or multiple ADC regular channels using DMA. | ||
| 341 | /// | ||
| 342 | /// `sequence` iterator and `readings` must have the same length. | ||
| 343 | /// | ||
| 344 | /// Example | ||
| 345 | /// ```rust,ignore | ||
| 346 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 347 | /// | ||
| 348 | /// let mut adc = Adc::new(p.ADC1); | ||
| 349 | /// let mut adc_pin0 = p.PA0.into(); | ||
| 350 | /// let mut adc_pin1 = p.PA1.into(); | ||
| 351 | /// let mut measurements = [0u16; 2]; | ||
| 352 | /// | ||
| 353 | /// adc.read( | ||
| 354 | /// p.DMA1_CH2.reborrow(), | ||
| 355 | /// [ | ||
| 356 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 357 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 358 | /// ] | ||
| 359 | /// .into_iter(), | ||
| 360 | /// &mut measurements, | ||
| 361 | /// ) | ||
| 362 | /// .await; | ||
| 363 | /// defmt::info!("measurements: {}", measurements); | ||
| 364 | /// ``` | ||
| 365 | /// | ||
| 366 | /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use | ||
| 367 | /// `into_ring_buffered`, `into_ring_buffered_and_injected` | ||
| 368 | pub async fn read( | ||
| 369 | &mut self, | ||
| 370 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 371 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 372 | readings: &mut [u16], | ||
| 373 | ) { | ||
| 374 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 375 | assert!( | ||
| 376 | sequence.len() == readings.len(), | ||
| 377 | "Sequence length must be equal to readings length" | ||
| 378 | ); | ||
| 379 | assert!( | ||
| 380 | sequence.len() <= 16, | ||
| 381 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 382 | ); | ||
| 383 | |||
| 384 | // Ensure no conversions are ongoing and ADC is enabled. | ||
| 385 | Self::stop_regular_conversions(); | ||
| 386 | Self::enable(); | ||
| 387 | |||
| 388 | Self::configure_sequence( | ||
| 389 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 390 | ); | ||
| 391 | |||
| 392 | // Set continuous mode with oneshot dma. | ||
| 393 | // Clear overrun flag before starting transfer. | ||
| 394 | T::regs().isr().modify(|reg| { | ||
| 395 | reg.set_ovr(true); | ||
| 396 | }); | ||
| 397 | |||
| 398 | T::regs().cfgr().modify(|reg| { | ||
| 399 | reg.set_discen(false); | ||
| 400 | reg.set_cont(true); | ||
| 401 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 402 | reg.set_dmaen(Dmaen::ENABLE); | ||
| 403 | }); | ||
| 404 | |||
| 405 | let request = rx_dma.request(); | ||
| 406 | let transfer = unsafe { | ||
| 407 | Transfer::new_read( | ||
| 408 | rx_dma, | ||
| 409 | request, | ||
| 410 | T::regs().dr().as_ptr() as *mut u16, | ||
| 411 | readings, | ||
| 412 | Default::default(), | ||
| 413 | ) | ||
| 414 | }; | ||
| 415 | |||
| 416 | // Start conversion | ||
| 417 | T::regs().cr().modify(|reg| { | ||
| 418 | reg.set_adstart(true); | ||
| 419 | }); | ||
| 420 | |||
| 421 | // Wait for conversion sequence to finish. | ||
| 422 | transfer.await; | ||
| 423 | |||
| 424 | // Ensure conversions are finished. | ||
| 425 | Self::stop_regular_conversions(); | ||
| 426 | |||
| 427 | // Reset configuration. | ||
| 428 | T::regs().cfgr().modify(|reg| { | ||
| 429 | reg.set_cont(false); | ||
| 430 | }); | ||
| 431 | } | ||
| 432 | |||
| 433 | pub(super) fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { | ||
| 434 | // Set sequence length | ||
| 435 | T::regs().sqr1().modify(|w| { | ||
| 436 | w.set_l(sequence.len() as u8 - 1); | ||
| 437 | }); | ||
| 438 | |||
| 439 | // Configure channels and ranks | ||
| 440 | for (_i, ((ch, is_differential), sample_time)) in sequence.enumerate() { | ||
| 441 | let sample_time = sample_time.into(); | ||
| 442 | if ch <= 9 { | ||
| 443 | T::regs().smpr().modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 444 | } else { | ||
| 445 | T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 446 | } | ||
| 447 | |||
| 448 | match _i { | ||
| 449 | 0..=3 => { | ||
| 450 | T::regs().sqr1().modify(|w| { | ||
| 451 | w.set_sq(_i, ch); | ||
| 452 | }); | ||
| 453 | } | ||
| 454 | 4..=8 => { | ||
| 455 | T::regs().sqr2().modify(|w| { | ||
| 456 | w.set_sq(_i - 4, ch); | ||
| 457 | }); | ||
| 458 | } | ||
| 459 | 9..=13 => { | ||
| 460 | T::regs().sqr3().modify(|w| { | ||
| 461 | w.set_sq(_i - 9, ch); | ||
| 462 | }); | ||
| 463 | } | ||
| 464 | 14..=15 => { | ||
| 465 | T::regs().sqr4().modify(|w| { | ||
| 466 | w.set_sq(_i - 14, ch); | ||
| 467 | }); | ||
| 468 | } | ||
| 469 | _ => unreachable!(), | ||
| 470 | } | ||
| 471 | |||
| 472 | #[cfg(stm32g4)] | ||
| 473 | { | ||
| 474 | T::regs().cr().modify(|w| w.set_aden(false)); // disable adc | ||
| 475 | |||
| 476 | T::regs().difsel().modify(|w| { | ||
| 477 | w.set_difsel( | ||
| 478 | ch.into(), | ||
| 479 | if is_differential { | ||
| 480 | Difsel::DIFFERENTIAL | ||
| 481 | } else { | ||
| 482 | Difsel::SINGLE_ENDED | ||
| 483 | }, | ||
| 484 | ); | ||
| 485 | }); | ||
| 486 | |||
| 487 | T::regs().cr().modify(|w| w.set_aden(true)); // enable adc | ||
| 488 | } | ||
| 489 | } | ||
| 490 | } | ||
| 491 | |||
| 492 | /// Set external trigger for regular conversion sequence | ||
| 493 | fn set_regular_conversion_trigger(&mut self, trigger: ConversionTrigger) { | ||
| 494 | T::regs().cfgr().modify(|r| { | ||
| 495 | r.set_extsel(trigger.channel); | ||
| 496 | r.set_exten(trigger.edge); | ||
| 497 | }); | ||
| 498 | // Regular conversions uses DMA so no need to generate interrupt | ||
| 499 | T::regs().ier().modify(|r| r.set_eosie(false)); | ||
| 500 | } | ||
| 501 | |||
| 502 | // Dual ADC mode selection | ||
| 503 | pub fn configure_dual_mode(&mut self, val: Dual) { | ||
| 504 | T::common_regs().ccr().modify(|reg| { | ||
| 505 | reg.set_dual(val); | ||
| 506 | }) | ||
| 507 | } | ||
| 508 | |||
| 509 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 510 | /// | ||
| 511 | /// Use the [`read`] method to retrieve measurements from the DMA ring buffer. The read buffer | ||
| 512 | /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended | ||
| 513 | /// to configure `dma_buf` as a double buffer so that one half can be read while the other half | ||
| 514 | /// is being filled by the DMA, preventing data loss. The trigger period of the ADC effectively | ||
| 515 | /// defines the period at which the buffer should be read. | ||
| 516 | /// | ||
| 517 | /// If continous conversion mode is selected, the provided `dma_buf` must be large enough to prevent | ||
| 518 | /// DMA buffer overruns. Its length should be a multiple of the number of ADC channels being measured. | ||
| 519 | /// For example, if 3 channels are measured and you want to store 40 samples per channel, | ||
| 520 | /// the buffer length should be `3 * 40 = 120`. | ||
| 521 | /// | ||
| 522 | /// # Parameters | ||
| 523 | /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer. | ||
| 524 | /// - `dma_buf`: The buffer where DMA stores ADC samples. | ||
| 525 | /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions. | ||
| 526 | /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered). | ||
| 527 | /// | ||
| 528 | /// # Returns | ||
| 529 | /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. | ||
| 530 | pub fn into_ring_buffered<'a>( | ||
| 531 | mut self, | ||
| 532 | dma: Peri<'a, impl RxDma<T>>, | ||
| 533 | dma_buf: &'a mut [u16], | ||
| 534 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 535 | mode: RegularConversionMode, | ||
| 536 | ) -> RingBufferedAdc<'a, T> { | ||
| 537 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 538 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 539 | assert!( | ||
| 540 | sequence.len() <= 16, | ||
| 541 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 542 | ); | ||
| 543 | // reset conversions and enable the adc | ||
| 544 | Self::stop_regular_conversions(); | ||
| 545 | Self::enable(); | ||
| 546 | |||
| 547 | //adc side setup | ||
| 548 | Self::configure_sequence( | ||
| 549 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 550 | ); | ||
| 551 | |||
| 552 | // Clear overrun flag before starting transfer. | ||
| 553 | T::regs().isr().modify(|reg| { | ||
| 554 | reg.set_ovr(true); | ||
| 555 | }); | ||
| 556 | |||
| 557 | T::regs().cfgr().modify(|reg| { | ||
| 558 | reg.set_discen(false); // Convert all channels for each trigger | ||
| 559 | reg.set_dmacfg(Dmacfg::CIRCULAR); | ||
| 560 | reg.set_dmaen(Dmaen::ENABLE); | ||
| 561 | }); | ||
| 562 | |||
| 563 | match mode { | ||
| 564 | RegularConversionMode::Continuous => { | ||
| 565 | T::regs().cfgr().modify(|reg| { | ||
| 566 | reg.set_cont(true); | ||
| 567 | }); | ||
| 568 | } | ||
| 569 | RegularConversionMode::Triggered(trigger) => { | ||
| 570 | T::regs().cfgr().modify(|r| { | ||
| 571 | r.set_cont(false); // New trigger is neede for each sample to be read | ||
| 572 | }); | ||
| 573 | self.set_regular_conversion_trigger(trigger); | ||
| 574 | } | ||
| 575 | } | ||
| 576 | |||
| 577 | mem::forget(self); | ||
| 578 | |||
| 579 | RingBufferedAdc::new(dma, dma_buf) | ||
| 580 | } | ||
| 581 | |||
| 582 | /// Configures the ADC for injected conversions. | 383 | /// Configures the ADC for injected conversions. |
| 583 | /// | 384 | /// |
| 584 | /// 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 |
| @@ -607,7 +408,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 607 | /// - 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 |
| 608 | /// `InjectedAdc<T, N>` to enforce bounds at compile time. | 409 | /// `InjectedAdc<T, N>` to enforce bounds at compile time. |
| 609 | pub fn setup_injected_conversions<'a, const N: usize>( | 410 | pub fn setup_injected_conversions<'a, const N: usize>( |
| 610 | mut self, | 411 | self, |
| 611 | sequence: [(AnyAdcChannel<T>, SampleTime); N], | 412 | sequence: [(AnyAdcChannel<T>, SampleTime); N], |
| 612 | trigger: ConversionTrigger, | 413 | trigger: ConversionTrigger, |
| 613 | interrupt: bool, | 414 | interrupt: bool, |
| @@ -619,8 +420,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 619 | NR_INJECTED_RANKS | 420 | NR_INJECTED_RANKS |
| 620 | ); | 421 | ); |
| 621 | 422 | ||
| 622 | Self::stop_regular_conversions(); | 423 | T::enable(); |
| 623 | Self::enable(); | ||
| 624 | 424 | ||
| 625 | 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)); |
| 626 | 426 | ||
| @@ -649,8 +449,16 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 649 | 449 | ||
| 650 | T::regs().cfgr().modify(|reg| reg.set_jdiscen(false)); | 450 | T::regs().cfgr().modify(|reg| reg.set_jdiscen(false)); |
| 651 | 451 | ||
| 652 | self.set_injected_conversion_trigger(trigger); | 452 | // Set external trigger for injected conversion sequence |
| 653 | 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 | |||
| 654 | Self::start_injected_conversions(); | 462 | Self::start_injected_conversions(); |
| 655 | 463 | ||
| 656 | InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels | 464 | InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels |
| @@ -683,12 +491,12 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 683 | self, | 491 | self, |
| 684 | dma: Peri<'a, impl RxDma<T>>, | 492 | dma: Peri<'a, impl RxDma<T>>, |
| 685 | dma_buf: &'a mut [u16], | 493 | dma_buf: &'a mut [u16], |
| 686 | regular_sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | 494 | regular_sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, T::SampleTime)>, |
| 687 | regular_conversion_mode: RegularConversionMode, | 495 | regular_conversion_mode: RegularConversionMode, |
| 688 | injected_sequence: [(AnyAdcChannel<T>, SampleTime); N], | 496 | injected_sequence: [(AnyAdcChannel<T>, SampleTime); N], |
| 689 | injected_trigger: ConversionTrigger, | 497 | injected_trigger: ConversionTrigger, |
| 690 | injected_interrupt: bool, | 498 | injected_interrupt: bool, |
| 691 | ) -> (RingBufferedAdc<'a, T>, InjectedAdc<T, N>) { | 499 | ) -> (super::RingBufferedAdc<'a, T>, InjectedAdc<T, N>) { |
| 692 | unsafe { | 500 | unsafe { |
| 693 | ( | 501 | ( |
| 694 | Self { | 502 | Self { |
| @@ -721,32 +529,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 721 | reg.set_jadstart(true); | 529 | reg.set_jadstart(true); |
| 722 | }); | 530 | }); |
| 723 | } | 531 | } |
| 724 | |||
| 725 | /// Set external trigger for injected conversion sequence | ||
| 726 | /// Possible trigger values are seen in Table 167 in RM0440 Rev 9 | ||
| 727 | fn set_injected_conversion_trigger(&mut self, trigger: ConversionTrigger) { | ||
| 728 | T::regs().jsqr().modify(|r| { | ||
| 729 | r.set_jextsel(trigger.channel); | ||
| 730 | r.set_jexten(trigger.edge); | ||
| 731 | }); | ||
| 732 | } | ||
| 733 | |||
| 734 | /// Enable end of injected sequence interrupt | ||
| 735 | fn enable_injected_eos_interrupt(&mut self, enable: bool) { | ||
| 736 | T::regs().ier().modify(|r| r.set_jeosie(enable)); | ||
| 737 | } | ||
| 738 | |||
| 739 | // Stop regular conversions | ||
| 740 | fn stop_regular_conversions() { | ||
| 741 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 742 | T::regs().cr().modify(|reg| { | ||
| 743 | reg.set_adstp(Adstp::STOP); | ||
| 744 | }); | ||
| 745 | // The software must poll ADSTART until the bit is reset before assuming the | ||
| 746 | // ADC is completely stopped | ||
| 747 | while T::regs().cr().read().adstart() {} | ||
| 748 | } | ||
| 749 | } | ||
| 750 | } | 532 | } |
| 751 | 533 | ||
| 752 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { | 534 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { |
diff --git a/embassy-stm32/src/adc/injected.rs b/embassy-stm32/src/adc/injected.rs index f9f1bba2a..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_dma(); | 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 e321c4fa1..6d53d9b91 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,26 +28,25 @@ 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 | } | 52 | } |
| @@ -87,21 +89,262 @@ pub(crate) trait SealedAdcChannel<T> { | |||
| 87 | } | 89 | } |
| 88 | } | 90 | } |
| 89 | 91 | ||
| 90 | /// Performs a busy-wait delay for a specified number of microseconds. | 92 | // Temporary patch for ADCs that have not implemented the standard iface yet |
| 91 | #[allow(unused)] | 93 | #[cfg(any(adc_v1, adc_l0, adc_f1, adc_f3v1, adc_f3v2, adc_f3v3, adc_v1))] |
| 92 | pub(crate) fn blocking_delay_us(us: u32) { | 94 | trait_set::trait_set! { |
| 93 | cfg_if::cfg_if! { | 95 | pub trait AnyInstance = Instance; |
| 94 | // this does strange things on stm32wlx in low power mode depending on exactly when it's called | 96 | } |
| 95 | // as in sometimes 15 us (1 tick) would take > 20 seconds. | 97 | |
| 96 | if #[cfg(all(feature = "time", all(not(feature = "low-power"), not(stm32wlex))))] { | 98 | #[cfg(any( |
| 97 | 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 |
| 98 | embassy_time::block_for(duration); | 100 | ))] |
| 99 | } else { | 101 | pub trait BasicAnyInstance { |
| 100 | let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64; | 102 | type SampleTime; |
| 101 | let us = us as u64; | 103 | } |
| 102 | let cycles = freq * us / 1_000_000; | 104 | |
| 103 | cortex_m::asm::delay(cycles as u32); | 105 | #[cfg(any( |
| 104 | } | 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 | // Ensure no conversions are ongoing | ||
| 196 | T::stop(); | ||
| 197 | #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h7rs, adc_u0, adc_u5, adc_wba, adc_c0))] | ||
| 198 | T::enable(); | ||
| 199 | T::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); | ||
| 200 | |||
| 201 | // On chips with differential channels, enable after configure_sequence to allow setting differential channels | ||
| 202 | // | ||
| 203 | // TODO: If hardware allows, enable after configure_sequence on all chips | ||
| 204 | #[cfg(any(adc_g4, adc_h5))] | ||
| 205 | T::enable(); | ||
| 206 | |||
| 207 | T::convert() | ||
| 208 | } | ||
| 209 | |||
| 210 | #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] | ||
| 211 | /// Read one or multiple ADC regular channels using DMA. | ||
| 212 | /// | ||
| 213 | /// `sequence` iterator and `readings` must have the same length. | ||
| 214 | /// | ||
| 215 | /// Example | ||
| 216 | /// ```rust,ignore | ||
| 217 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 218 | /// | ||
| 219 | /// let mut adc = Adc::new(p.ADC1); | ||
| 220 | /// let mut adc_pin0 = p.PA0.into(); | ||
| 221 | /// let mut adc_pin1 = p.PA1.into(); | ||
| 222 | /// let mut measurements = [0u16; 2]; | ||
| 223 | /// | ||
| 224 | /// adc.read( | ||
| 225 | /// p.DMA1_CH2.reborrow(), | ||
| 226 | /// [ | ||
| 227 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 228 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 229 | /// ] | ||
| 230 | /// .into_iter(), | ||
| 231 | /// &mut measurements, | ||
| 232 | /// ) | ||
| 233 | /// .await; | ||
| 234 | /// defmt::info!("measurements: {}", measurements); | ||
| 235 | /// ``` | ||
| 236 | /// | ||
| 237 | /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use | ||
| 238 | /// `into_ring_buffered`, `into_ring_buffered_and_injected` | ||
| 239 | /// | ||
| 240 | /// Note: Depending on hardware limitations, this method may require channels to be passed | ||
| 241 | /// in order or require the sequence to have the same sample time for all channnels, depending | ||
| 242 | /// on the number and properties of the channels in the sequence. This method will panic if | ||
| 243 | /// the hardware cannot deliver the requested configuration. | ||
| 244 | pub async fn read( | ||
| 245 | &mut self, | ||
| 246 | rx_dma: embassy_hal_internal::Peri<'_, impl RxDma<T>>, | ||
| 247 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, T::SampleTime)>, | ||
| 248 | readings: &mut [u16], | ||
| 249 | ) { | ||
| 250 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 251 | assert!( | ||
| 252 | sequence.len() == readings.len(), | ||
| 253 | "Sequence length must be equal to readings length" | ||
| 254 | ); | ||
| 255 | assert!( | ||
| 256 | sequence.len() <= 16, | ||
| 257 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 258 | ); | ||
| 259 | |||
| 260 | // Ensure no conversions are ongoing | ||
| 261 | T::stop(); | ||
| 262 | #[cfg(any(adc_g0, adc_v3, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] | ||
| 263 | T::enable(); | ||
| 264 | |||
| 265 | T::configure_sequence( | ||
| 266 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 267 | ); | ||
| 268 | |||
| 269 | // On chips with differential channels, enable after configure_sequence to allow setting differential channels | ||
| 270 | // | ||
| 271 | // TODO: If hardware allows, enable after configure_sequence on all chips | ||
| 272 | #[cfg(any(adc_g4, adc_h5))] | ||
| 273 | T::enable(); | ||
| 274 | T::configure_dma(ConversionMode::Singular); | ||
| 275 | |||
| 276 | let request = rx_dma.request(); | ||
| 277 | let transfer = | ||
| 278 | unsafe { crate::dma::Transfer::new_read(rx_dma, request, T::dr(), readings, Default::default()) }; | ||
| 279 | |||
| 280 | T::start(); | ||
| 281 | |||
| 282 | // Wait for conversion sequence to finish. | ||
| 283 | transfer.await; | ||
| 284 | |||
| 285 | // Ensure conversions are finished. | ||
| 286 | T::stop(); | ||
| 287 | } | ||
| 288 | |||
| 289 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 290 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 291 | /// | ||
| 292 | /// Use the [`read`] method to retrieve measurements from the DMA ring buffer. The read buffer | ||
| 293 | /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended | ||
| 294 | /// to configure `dma_buf` as a double buffer so that one half can be read while the other half | ||
| 295 | /// is being filled by the DMA, preventing data loss. The trigger period of the ADC effectively | ||
| 296 | /// defines the period at which the buffer should be read. | ||
| 297 | /// | ||
| 298 | /// If continous conversion mode is selected, the provided `dma_buf` must be large enough to prevent | ||
| 299 | /// DMA buffer overruns. Its length should be a multiple of the number of ADC channels being measured. | ||
| 300 | /// For example, if 3 channels are measured and you want to store 40 samples per channel, | ||
| 301 | /// the buffer length should be `3 * 40 = 120`. | ||
| 302 | /// | ||
| 303 | /// # Parameters | ||
| 304 | /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer. | ||
| 305 | /// - `dma_buf`: The buffer where DMA stores ADC samples. | ||
| 306 | /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions. | ||
| 307 | /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered). | ||
| 308 | /// | ||
| 309 | /// # Returns | ||
| 310 | /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. | ||
| 311 | /// | ||
| 312 | /// Note: Depending on hardware limitations, this method may require channels to be passed | ||
| 313 | /// in order or require the sequence to have the same sample time for all channnels, depending | ||
| 314 | /// on the number and properties of the channels in the sequence. This method will panic if | ||
| 315 | /// the hardware cannot deliver the requested configuration. | ||
| 316 | pub fn into_ring_buffered<'a>( | ||
| 317 | self, | ||
| 318 | dma: embassy_hal_internal::Peri<'a, impl RxDma<T>>, | ||
| 319 | dma_buf: &'a mut [u16], | ||
| 320 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, T::SampleTime)>, | ||
| 321 | mode: RegularConversionMode, | ||
| 322 | ) -> RingBufferedAdc<'a, T> { | ||
| 323 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 324 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 325 | assert!( | ||
| 326 | sequence.len() <= 16, | ||
| 327 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 328 | ); | ||
| 329 | // Ensure no conversions are ongoing | ||
| 330 | T::stop(); | ||
| 331 | #[cfg(any(adc_g0, adc_v3, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] | ||
| 332 | T::enable(); | ||
| 333 | |||
| 334 | T::configure_sequence( | ||
| 335 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 336 | ); | ||
| 337 | |||
| 338 | // On chips with differential channels, enable after configure_sequence to allow setting differential channels | ||
| 339 | // | ||
| 340 | // TODO: If hardware allows, enable after configure_sequence on all chips | ||
| 341 | #[cfg(any(adc_g4, adc_h5))] | ||
| 342 | T::enable(); | ||
| 343 | T::configure_dma(ConversionMode::Repeated(mode)); | ||
| 344 | |||
| 345 | core::mem::forget(self); | ||
| 346 | |||
| 347 | RingBufferedAdc::new(dma, dma_buf) | ||
| 105 | } | 348 | } |
| 106 | } | 349 | } |
| 107 | 350 | ||
| @@ -143,6 +386,14 @@ impl SpecialChannel for Temperature {} | |||
| 143 | pub struct Vbat; | 386 | pub struct Vbat; |
| 144 | impl SpecialChannel for Vbat {} | 387 | impl SpecialChannel for Vbat {} |
| 145 | 388 | ||
| 389 | /// Vcore channel. | ||
| 390 | pub struct Vcore; | ||
| 391 | impl SpecialChannel for Vcore {} | ||
| 392 | |||
| 393 | /// Internal dac channel. | ||
| 394 | pub struct Dac; | ||
| 395 | impl SpecialChannel for Dac {} | ||
| 396 | |||
| 146 | /// ADC instance. | 397 | /// ADC instance. |
| 147 | #[cfg(not(any( | 398 | #[cfg(not(any( |
| 148 | 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, | 399 | 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, |
| @@ -187,9 +438,9 @@ pub struct AnyAdcChannel<T> { | |||
| 187 | is_differential: bool, | 438 | is_differential: bool, |
| 188 | _phantom: PhantomData<T>, | 439 | _phantom: PhantomData<T>, |
| 189 | } | 440 | } |
| 190 | impl_peripheral!(AnyAdcChannel<T: Instance>); | 441 | impl_peripheral!(AnyAdcChannel<T: AnyInstance>); |
| 191 | impl<T: Instance> AdcChannel<T> for AnyAdcChannel<T> {} | 442 | impl<T: AnyInstance> AdcChannel<T> for AnyAdcChannel<T> {} |
| 192 | impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<T> { | 443 | impl<T: AnyInstance> SealedAdcChannel<T> for AnyAdcChannel<T> { |
| 193 | fn channel(&self) -> u8 { | 444 | fn channel(&self) -> u8 { |
| 194 | self.channel | 445 | self.channel |
| 195 | } | 446 | } |
diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs index 024c6acdc..5437866d3 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,12 +45,10 @@ 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(); | ||
| 53 | |||
| 54 | self.ring_buf.request_pause(); | 52 | self.ring_buf.request_pause(); |
| 55 | 53 | ||
| 56 | compiler_fence(Ordering::SeqCst); | 54 | compiler_fence(Ordering::SeqCst); |
| @@ -161,7 +159,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { | |||
| 161 | return Ok(len); | 159 | return Ok(len); |
| 162 | } | 160 | } |
| 163 | Err(_) => { | 161 | Err(_) => { |
| 164 | self.stop(); | 162 | self.ring_buf.request_pause(); |
| 165 | 163 | ||
| 166 | return Err(OverrunError); | 164 | return Err(OverrunError); |
| 167 | } | 165 | } |
| @@ -170,9 +168,9 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { | |||
| 170 | } | 168 | } |
| 171 | } | 169 | } |
| 172 | 170 | ||
| 173 | impl<T: Instance> Drop for RingBufferedAdc<'_, T> { | 171 | impl<T: Instance + AnyInstance> Drop for RingBufferedAdc<'_, T> { |
| 174 | fn drop(&mut self) { | 172 | fn drop(&mut self) { |
| 175 | Adc::<T>::teardown_dma(); | 173 | T::stop(); |
| 176 | 174 | ||
| 177 | compiler_fence(Ordering::SeqCst); | 175 | compiler_fence(Ordering::SeqCst); |
| 178 | 176 | ||
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index efa1cc68c..3c4431ae0 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs | |||
| @@ -1,15 +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::{Temperature, Vbat, VrefInt, 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; |
| 6 | pub use crate::pac::adccommon::vals::Adcpre; | ||
| 7 | use crate::time::Hertz; | 7 | use crate::time::Hertz; |
| 8 | use crate::{Peri, rcc}; | 8 | use crate::{Peri, rcc}; |
| 9 | 9 | ||
| 10 | mod ringbuffered; | ||
| 11 | pub use ringbuffered::RingBufferedAdc; | ||
| 12 | |||
| 13 | fn clear_interrupt_flags(r: crate::pac::adc::Adc) { | 10 | fn clear_interrupt_flags(r: crate::pac::adc::Adc) { |
| 14 | r.sr().modify(|regs| { | 11 | r.sr().modify(|regs| { |
| 15 | regs.set_eoc(false); | 12 | regs.set_eoc(false); |
| @@ -54,156 +51,75 @@ impl Temperature { | |||
| 54 | } | 51 | } |
| 55 | } | 52 | } |
| 56 | 53 | ||
| 57 | enum Prescaler { | 54 | fn from_pclk2(freq: Hertz) -> Adcpre { |
| 58 | Div2, | 55 | // Datasheet for F2 specifies min frequency 0.6 MHz, and max 30 MHz (with VDDA 2.4-3.6V). |
| 59 | Div4, | 56 | #[cfg(stm32f2)] |
| 60 | Div6, | 57 | const MAX_FREQUENCY: Hertz = Hertz(30_000_000); |
| 61 | Div8, | 58 | // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. |
| 62 | } | 59 | #[cfg(not(stm32f2))] |
| 63 | 60 | const MAX_FREQUENCY: Hertz = Hertz(36_000_000); | |
| 64 | impl Prescaler { | 61 | let raw_div = rcc::raw_prescaler(freq.0, MAX_FREQUENCY.0); |
| 65 | fn from_pclk2(freq: Hertz) -> Self { | 62 | match raw_div { |
| 66 | // Datasheet for F2 specifies min frequency 0.6 MHz, and max 30 MHz (with VDDA 2.4-3.6V). | 63 | 0..=1 => Adcpre::DIV2, |
| 67 | #[cfg(stm32f2)] | 64 | 2..=3 => Adcpre::DIV4, |
| 68 | const MAX_FREQUENCY: Hertz = Hertz(30_000_000); | 65 | 4..=5 => Adcpre::DIV6, |
| 69 | // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. | 66 | 6..=7 => Adcpre::DIV8, |
| 70 | #[cfg(not(stm32f2))] | 67 | _ => panic!("Selected PCLK2 frequency is too high for ADC with largest possible prescaler."), |
| 71 | const MAX_FREQUENCY: Hertz = Hertz(36_000_000); | ||
| 72 | let raw_div = freq.0 / MAX_FREQUENCY.0; | ||
| 73 | match raw_div { | ||
| 74 | 0..=1 => Self::Div2, | ||
| 75 | 2..=3 => Self::Div4, | ||
| 76 | 4..=5 => Self::Div6, | ||
| 77 | 6..=7 => Self::Div8, | ||
| 78 | _ => panic!("Selected PCLK2 frequency is too high for ADC with largest possible prescaler."), | ||
| 79 | } | ||
| 80 | } | 68 | } |
| 69 | } | ||
| 81 | 70 | ||
| 82 | fn adcpre(&self) -> crate::pac::adccommon::vals::Adcpre { | 71 | /// ADC configuration |
| 83 | match self { | 72 | #[derive(Default)] |
| 84 | Prescaler::Div2 => crate::pac::adccommon::vals::Adcpre::DIV2, | 73 | pub struct AdcConfig { |
| 85 | Prescaler::Div4 => crate::pac::adccommon::vals::Adcpre::DIV4, | 74 | resolution: Option<Resolution>, |
| 86 | Prescaler::Div6 => crate::pac::adccommon::vals::Adcpre::DIV6, | ||
| 87 | Prescaler::Div8 => crate::pac::adccommon::vals::Adcpre::DIV8, | ||
| 88 | } | ||
| 89 | } | ||
| 90 | } | 75 | } |
| 91 | 76 | ||
| 92 | impl<'d, T> Adc<'d, T> | 77 | impl<T: Instance> super::SealedAnyInstance for T { |
| 93 | where | 78 | fn dr() -> *mut u16 { |
| 94 | T: Instance, | 79 | T::regs().dr().as_ptr() as *mut u16 |
| 95 | { | 80 | } |
| 96 | pub fn new(adc: Peri<'d, T>) -> Self { | ||
| 97 | rcc::enable_and_reset::<T>(); | ||
| 98 | 81 | ||
| 99 | let presc = Prescaler::from_pclk2(T::frequency()); | 82 | fn enable() { |
| 100 | T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); | ||
| 101 | T::regs().cr2().modify(|reg| { | 83 | T::regs().cr2().modify(|reg| { |
| 102 | reg.set_adon(true); | 84 | reg.set_adon(true); |
| 103 | }); | 85 | }); |
| 104 | 86 | ||
| 105 | blocking_delay_us(3); | 87 | blocking_delay_us(3); |
| 106 | |||
| 107 | Self { adc } | ||
| 108 | } | ||
| 109 | |||
| 110 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 111 | /// | ||
| 112 | /// The `dma_buf` should be large enough to prevent DMA buffer overrun. | ||
| 113 | /// The length of the `dma_buf` should be a multiple of the ADC channel count. | ||
| 114 | /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. | ||
| 115 | /// | ||
| 116 | /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length. | ||
| 117 | /// It is critical to call `read` frequently to prevent DMA buffer overrun. | ||
| 118 | /// | ||
| 119 | /// [`read`]: #method.read | ||
| 120 | pub fn into_ring_buffered<'a>( | ||
| 121 | self, | ||
| 122 | dma: Peri<'d, impl RxDma<T>>, | ||
| 123 | dma_buf: &'d mut [u16], | ||
| 124 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 125 | ) -> RingBufferedAdc<'d, T> { | ||
| 126 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 127 | |||
| 128 | Self::configure_sequence(sequence.map(|(mut channel, sample_time)| { | ||
| 129 | channel.setup(); | ||
| 130 | |||
| 131 | (channel.channel, sample_time) | ||
| 132 | })); | ||
| 133 | compiler_fence(Ordering::SeqCst); | ||
| 134 | |||
| 135 | Self::setup_dma(); | ||
| 136 | |||
| 137 | // Don't disable the clock | ||
| 138 | mem::forget(self); | ||
| 139 | |||
| 140 | RingBufferedAdc::new(dma, dma_buf) | ||
| 141 | } | ||
| 142 | |||
| 143 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 144 | channel.setup(); | ||
| 145 | |||
| 146 | // Configure ADC | ||
| 147 | let channel = channel.channel(); | ||
| 148 | |||
| 149 | Self::configure_sequence([(channel, sample_time)].into_iter()); | ||
| 150 | Self::blocking_convert() | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Enables internal voltage reference and returns [VrefInt], which can be used in | ||
| 154 | /// [Adc::read_internal()] to perform conversion. | ||
| 155 | pub fn enable_vrefint(&self) -> VrefInt { | ||
| 156 | T::common_regs().ccr().modify(|reg| { | ||
| 157 | reg.set_tsvrefe(true); | ||
| 158 | }); | ||
| 159 | |||
| 160 | VrefInt {} | ||
| 161 | } | ||
| 162 | |||
| 163 | /// Enables internal temperature sensor and returns [Temperature], which can be used in | ||
| 164 | /// [Adc::read_internal()] to perform conversion. | ||
| 165 | /// | ||
| 166 | /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled, | ||
| 167 | /// temperature sensor will return vbat value. | ||
| 168 | pub fn enable_temperature(&self) -> Temperature { | ||
| 169 | T::common_regs().ccr().modify(|reg| { | ||
| 170 | reg.set_tsvrefe(true); | ||
| 171 | }); | ||
| 172 | |||
| 173 | Temperature {} | ||
| 174 | } | ||
| 175 | |||
| 176 | /// Enables vbat input and returns [Vbat], which can be used in | ||
| 177 | /// [Adc::read_internal()] to perform conversion. | ||
| 178 | pub fn enable_vbat(&self) -> Vbat { | ||
| 179 | T::common_regs().ccr().modify(|reg| { | ||
| 180 | reg.set_vbate(true); | ||
| 181 | }); | ||
| 182 | |||
| 183 | Vbat {} | ||
| 184 | } | 88 | } |
| 185 | 89 | ||
| 186 | pub(super) fn start() { | 90 | fn start() { |
| 187 | // Begin ADC conversions | 91 | // Begin ADC conversions |
| 188 | T::regs().cr2().modify(|reg| { | 92 | T::regs().cr2().modify(|reg| { |
| 189 | reg.set_adon(true); | ||
| 190 | reg.set_swstart(true); | 93 | reg.set_swstart(true); |
| 191 | }); | 94 | }); |
| 192 | } | 95 | } |
| 193 | 96 | ||
| 194 | pub(super) fn stop() { | 97 | fn stop() { |
| 98 | let r = T::regs(); | ||
| 99 | |||
| 195 | // Stop ADC | 100 | // Stop ADC |
| 196 | T::regs().cr2().modify(|reg| { | 101 | r.cr2().modify(|reg| { |
| 197 | // Stop ADC | 102 | // Stop ADC |
| 198 | reg.set_swstart(false); | 103 | reg.set_swstart(false); |
| 104 | // Stop ADC | ||
| 105 | reg.set_adon(false); | ||
| 106 | // Stop DMA | ||
| 107 | reg.set_dma(false); | ||
| 199 | }); | 108 | }); |
| 200 | } | ||
| 201 | 109 | ||
| 202 | pub fn set_resolution(&mut self, resolution: Resolution) { | 110 | r.cr1().modify(|w| { |
| 203 | T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); | 111 | // Disable interrupt for end of conversion |
| 112 | w.set_eocie(false); | ||
| 113 | // Disable interrupt for overrun | ||
| 114 | w.set_ovrie(false); | ||
| 115 | }); | ||
| 116 | |||
| 117 | clear_interrupt_flags(r); | ||
| 118 | |||
| 119 | compiler_fence(Ordering::SeqCst); | ||
| 204 | } | 120 | } |
| 205 | 121 | ||
| 206 | pub(super) fn blocking_convert() -> u16 { | 122 | fn convert() -> u16 { |
| 207 | // clear end of conversion flag | 123 | // clear end of conversion flag |
| 208 | T::regs().sr().modify(|reg| { | 124 | T::regs().sr().modify(|reg| { |
| 209 | reg.set_eoc(false); | 125 | reg.set_eoc(false); |
| @@ -224,7 +140,44 @@ where | |||
| 224 | T::regs().dr().read().0 as u16 | 140 | T::regs().dr().read().0 as u16 |
| 225 | } | 141 | } |
| 226 | 142 | ||
| 227 | pub(super) fn configure_sequence(sequence: impl ExactSizeIterator<Item = (u8, SampleTime)>) { | 143 | fn configure_dma(conversion_mode: ConversionMode) { |
| 144 | match conversion_mode { | ||
| 145 | ConversionMode::Repeated(_) => { | ||
| 146 | let r = T::regs(); | ||
| 147 | |||
| 148 | // Clear all interrupts | ||
| 149 | r.sr().modify(|regs| { | ||
| 150 | regs.set_eoc(false); | ||
| 151 | regs.set_ovr(false); | ||
| 152 | regs.set_strt(false); | ||
| 153 | }); | ||
| 154 | |||
| 155 | r.cr1().modify(|w| { | ||
| 156 | // Enable interrupt for end of conversion | ||
| 157 | w.set_eocie(true); | ||
| 158 | // Enable interrupt for overrun | ||
| 159 | w.set_ovrie(true); | ||
| 160 | // Scanning converisons of multiple channels | ||
| 161 | w.set_scan(true); | ||
| 162 | // Continuous conversion mode | ||
| 163 | w.set_discen(false); | ||
| 164 | }); | ||
| 165 | |||
| 166 | r.cr2().modify(|w| { | ||
| 167 | // Enable DMA mode | ||
| 168 | w.set_dma(true); | ||
| 169 | // Enable continuous conversions | ||
| 170 | w.set_cont(true); | ||
| 171 | // DMA requests are issues as long as DMA=1 and data are converted. | ||
| 172 | w.set_dds(vals::Dds::CONTINUOUS); | ||
| 173 | // EOC flag is set at the end of each conversion. | ||
| 174 | w.set_eocs(vals::Eocs::EACH_CONVERSION); | ||
| 175 | }); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { | ||
| 228 | T::regs().cr2().modify(|reg| { | 181 | T::regs().cr2().modify(|reg| { |
| 229 | reg.set_adon(true); | 182 | reg.set_adon(true); |
| 230 | }); | 183 | }); |
| @@ -234,7 +187,7 @@ where | |||
| 234 | r.set_l((sequence.len() - 1).try_into().unwrap()); | 187 | r.set_l((sequence.len() - 1).try_into().unwrap()); |
| 235 | }); | 188 | }); |
| 236 | 189 | ||
| 237 | for (i, (ch, sample_time)) in sequence.enumerate() { | 190 | for (i, ((ch, _), sample_time)) in sequence.enumerate() { |
| 238 | // Set the channel in the right sequence field. | 191 | // Set the channel in the right sequence field. |
| 239 | T::regs().sqr3().modify(|w| w.set_sq(i, ch)); | 192 | T::regs().sqr3().modify(|w| w.set_sq(i, ch)); |
| 240 | 193 | ||
| @@ -246,63 +199,61 @@ where | |||
| 246 | } | 199 | } |
| 247 | } | 200 | } |
| 248 | } | 201 | } |
| 202 | } | ||
| 249 | 203 | ||
| 250 | pub(super) fn setup_dma() { | 204 | impl<'d, T> Adc<'d, T> |
| 251 | let r = T::regs(); | 205 | where |
| 206 | T: Instance + super::AnyInstance, | ||
| 207 | { | ||
| 208 | pub fn new(adc: Peri<'d, T>) -> Self { | ||
| 209 | Self::new_with_config(adc, Default::default()) | ||
| 210 | } | ||
| 252 | 211 | ||
| 253 | // Clear all interrupts | 212 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { |
| 254 | r.sr().modify(|regs| { | 213 | rcc::enable_and_reset::<T>(); |
| 255 | regs.set_eoc(false); | ||
| 256 | regs.set_ovr(false); | ||
| 257 | regs.set_strt(false); | ||
| 258 | }); | ||
| 259 | 214 | ||
| 260 | r.cr1().modify(|w| { | 215 | let presc = from_pclk2(T::frequency()); |
| 261 | // Enable interrupt for end of conversion | 216 | T::common_regs().ccr().modify(|w| w.set_adcpre(presc)); |
| 262 | w.set_eocie(true); | 217 | T::enable(); |
| 263 | // Enable interrupt for overrun | ||
| 264 | w.set_ovrie(true); | ||
| 265 | // Scanning converisons of multiple channels | ||
| 266 | w.set_scan(true); | ||
| 267 | // Continuous conversion mode | ||
| 268 | w.set_discen(false); | ||
| 269 | }); | ||
| 270 | 218 | ||
| 271 | r.cr2().modify(|w| { | 219 | if let Some(resolution) = config.resolution { |
| 272 | // Enable DMA mode | 220 | T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); |
| 273 | w.set_dma(true); | 221 | } |
| 274 | // Enable continuous conversions | ||
| 275 | w.set_cont(true); | ||
| 276 | // DMA requests are issues as long as DMA=1 and data are converted. | ||
| 277 | w.set_dds(vals::Dds::CONTINUOUS); | ||
| 278 | // EOC flag is set at the end of each conversion. | ||
| 279 | w.set_eocs(vals::Eocs::EACH_CONVERSION); | ||
| 280 | }); | ||
| 281 | } | ||
| 282 | 222 | ||
| 283 | pub(super) fn teardown_dma() { | 223 | Self { adc } |
| 284 | let r = T::regs(); | 224 | } |
| 285 | 225 | ||
| 286 | // Stop ADC | 226 | /// Enables internal voltage reference and returns [VrefInt], which can be used in |
| 287 | r.cr2().modify(|reg| { | 227 | /// [Adc::read_internal()] to perform conversion. |
| 288 | // Stop ADC | 228 | pub fn enable_vrefint(&self) -> VrefInt { |
| 289 | reg.set_swstart(false); | 229 | T::common_regs().ccr().modify(|reg| { |
| 290 | // Stop ADC | 230 | reg.set_tsvrefe(true); |
| 291 | reg.set_adon(false); | ||
| 292 | // Stop DMA | ||
| 293 | reg.set_dma(false); | ||
| 294 | }); | 231 | }); |
| 295 | 232 | ||
| 296 | r.cr1().modify(|w| { | 233 | VrefInt {} |
| 297 | // Disable interrupt for end of conversion | 234 | } |
| 298 | w.set_eocie(false); | 235 | |
| 299 | // Disable interrupt for overrun | 236 | /// Enables internal temperature sensor and returns [Temperature], which can be used in |
| 300 | w.set_ovrie(false); | 237 | /// [Adc::read_internal()] to perform conversion. |
| 238 | /// | ||
| 239 | /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled, | ||
| 240 | /// temperature sensor will return vbat value. | ||
| 241 | pub fn enable_temperature(&self) -> Temperature { | ||
| 242 | T::common_regs().ccr().modify(|reg| { | ||
| 243 | reg.set_tsvrefe(true); | ||
| 301 | }); | 244 | }); |
| 302 | 245 | ||
| 303 | clear_interrupt_flags(r); | 246 | Temperature {} |
| 247 | } | ||
| 304 | 248 | ||
| 305 | compiler_fence(Ordering::SeqCst); | 249 | /// Enables vbat input and returns [Vbat], which can be used in |
| 250 | /// [Adc::read_internal()] to perform conversion. | ||
| 251 | pub fn enable_vbat(&self) -> Vbat { | ||
| 252 | T::common_regs().ccr().modify(|reg| { | ||
| 253 | reg.set_vbate(true); | ||
| 254 | }); | ||
| 255 | |||
| 256 | Vbat {} | ||
| 306 | } | 257 | } |
| 307 | } | 258 | } |
| 308 | 259 | ||
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index cbc217545..b270588c4 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs | |||
| @@ -1,9 +1,9 @@ | |||
| 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)] |
| @@ -11,18 +11,8 @@ pub use pac::adc::vals::{Ovsr, Ovss, Presc}; | |||
| 11 | 11 | ||
| 12 | #[allow(unused_imports)] | 12 | #[allow(unused_imports)] |
| 13 | use super::SealedAdcChannel; | 13 | use super::SealedAdcChannel; |
| 14 | use super::{ | 14 | use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 15 | Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, Temperature, Vbat, VrefInt, | 15 | use crate::adc::ConversionMode; |
| 16 | blocking_delay_us, | ||
| 17 | }; | ||
| 18 | |||
| 19 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 20 | mod ringbuffered; | ||
| 21 | |||
| 22 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 23 | use ringbuffered::RingBufferedAdc; | ||
| 24 | |||
| 25 | use crate::dma::Transfer; | ||
| 26 | use crate::{Peri, pac, rcc}; | 16 | use crate::{Peri, pac, rcc}; |
| 27 | 17 | ||
| 28 | /// Default VREF voltage used for sample conversion to millivolts. | 18 | /// Default VREF voltage used for sample conversion to millivolts. |
| @@ -75,7 +65,7 @@ impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { | |||
| 75 | } | 65 | } |
| 76 | #[cfg(any(adc_h5, adc_h7rs))] | 66 | #[cfg(any(adc_h5, adc_h7rs))] |
| 77 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { | 67 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 78 | const CHANNEL: u8 = 2; | 68 | const CHANNEL: u8 = 16; |
| 79 | } | 69 | } |
| 80 | #[cfg(adc_u0)] | 70 | #[cfg(adc_u0)] |
| 81 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { | 71 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| @@ -89,10 +79,10 @@ impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { | |||
| 89 | cfg_if! { | 79 | cfg_if! { |
| 90 | if #[cfg(any(adc_h5, adc_h7rs))] { | 80 | if #[cfg(any(adc_h5, adc_h7rs))] { |
| 91 | pub struct VddCore; | 81 | pub struct VddCore; |
| 92 | impl<T: Instance> AdcChannel<T> for VddCore {} | 82 | impl<T: Instance> super::AdcChannel<T> for VddCore {} |
| 93 | impl<T: Instance> super::SealedAdcChannel<T> for VddCore { | 83 | impl<T: Instance> super::SealedAdcChannel<T> for VddCore { |
| 94 | fn channel(&self) -> u8 { | 84 | fn channel(&self) -> u8 { |
| 95 | 6 | 85 | 17 |
| 96 | } | 86 | } |
| 97 | } | 87 | } |
| 98 | } | 88 | } |
| @@ -101,7 +91,7 @@ cfg_if! { | |||
| 101 | cfg_if! { | 91 | cfg_if! { |
| 102 | if #[cfg(adc_u0)] { | 92 | if #[cfg(adc_u0)] { |
| 103 | pub struct DacOut; | 93 | pub struct DacOut; |
| 104 | impl<T: Instance> AdcChannel<T> for DacOut {} | 94 | impl<T: Instance> super::AdcChannel<T> for DacOut {} |
| 105 | impl<T: Instance> super::SealedAdcChannel<T> for DacOut { | 95 | impl<T: Instance> super::SealedAdcChannel<T> for DacOut { |
| 106 | fn channel(&self) -> u8 { | 96 | fn channel(&self) -> u8 { |
| 107 | 19 | 97 | 19 |
| @@ -110,21 +100,6 @@ cfg_if! { | |||
| 110 | } | 100 | } |
| 111 | } | 101 | } |
| 112 | 102 | ||
| 113 | /// Number of samples used for averaging. | ||
| 114 | #[derive(Copy, Clone, Debug)] | ||
| 115 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 116 | pub enum Averaging { | ||
| 117 | Disabled, | ||
| 118 | Samples2, | ||
| 119 | Samples4, | ||
| 120 | Samples8, | ||
| 121 | Samples16, | ||
| 122 | Samples32, | ||
| 123 | Samples64, | ||
| 124 | Samples128, | ||
| 125 | Samples256, | ||
| 126 | } | ||
| 127 | |||
| 128 | cfg_if! { if #[cfg(adc_g0)] { | 103 | cfg_if! { if #[cfg(adc_g0)] { |
| 129 | 104 | ||
| 130 | /// Synchronous PCLK prescaler | 105 | /// Synchronous PCLK prescaler |
| @@ -145,310 +120,148 @@ pub enum Clock { | |||
| 145 | 120 | ||
| 146 | }} | 121 | }} |
| 147 | 122 | ||
| 148 | impl<'d, T: Instance> Adc<'d, T> { | 123 | #[cfg(adc_u0)] |
| 149 | /// Enable the voltage regulator | 124 | type Ovss = u8; |
| 150 | fn init_regulator() { | 125 | #[cfg(adc_u0)] |
| 151 | rcc::enable_and_reset::<T>(); | 126 | type Ovsr = u8; |
| 152 | T::regs().cr().modify(|reg| { | 127 | #[cfg(adc_v3)] |
| 153 | #[cfg(not(any(adc_g0, adc_u0)))] | 128 | type Ovss = OversamplingShift; |
| 154 | reg.set_deeppwd(false); | 129 | #[cfg(adc_v3)] |
| 155 | reg.set_advregen(true); | 130 | type Ovsr = OversamplingRatio; |
| 156 | }); | 131 | |
| 157 | 132 | /// Adc configuration | |
| 158 | // If this is false then each ADC_CHSELR bit enables an input channel. | 133 | #[derive(Default)] |
| 159 | // This is the reset value, so has no effect. | 134 | pub struct AdcConfig { |
| 160 | #[cfg(any(adc_g0, adc_u0))] | 135 | #[cfg(any(adc_u0, adc_g0, adc_v3))] |
| 161 | T::regs().cfgr1().modify(|reg| { | 136 | pub oversampling_shift: Option<Ovss>, |
| 162 | reg.set_chselrmod(false); | 137 | #[cfg(any(adc_u0, adc_g0, adc_v3))] |
| 163 | }); | 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 | } | ||
| 164 | 148 | ||
| 165 | 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 | ||
| 166 | } | 152 | } |
| 167 | 153 | ||
| 168 | /// Calibrate to remove conversion offset | 154 | // Enable ADC only when it is not already running. |
| 169 | fn init_calibrate() { | 155 | fn enable() { |
| 170 | T::regs().cr().modify(|reg| { | 156 | // Make sure bits are off |
| 171 | reg.set_adcal(true); | 157 | while T::regs().cr().read().addis() { |
| 172 | }); | ||
| 173 | |||
| 174 | while T::regs().cr().read().adcal() { | ||
| 175 | // spin | 158 | // spin |
| 176 | } | 159 | } |
| 177 | 160 | ||
| 178 | 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 | } | ||
| 179 | } | 174 | } |
| 180 | 175 | ||
| 181 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | 176 | fn start() { |
| 182 | pub(super) fn start() { | ||
| 183 | // Start adc conversion | ||
| 184 | T::regs().cr().modify(|reg| { | 177 | T::regs().cr().modify(|reg| { |
| 185 | reg.set_adstart(true); | 178 | reg.set_adstart(true); |
| 186 | }); | 179 | }); |
| 187 | } | 180 | } |
| 188 | 181 | ||
| 189 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | 182 | fn stop() { |
| 190 | pub(super) fn stop() { | 183 | // Ensure conversions are finished. |
| 191 | // Stop adc conversion | ||
| 192 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | 184 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 193 | T::regs().cr().modify(|reg| { | 185 | T::regs().cr().modify(|reg| { |
| 194 | reg.set_adstp(true); | 186 | reg.set_adstp(true); |
| 195 | }); | 187 | }); |
| 196 | while T::regs().cr().read().adstart() {} | 188 | while T::regs().cr().read().adstart() {} |
| 197 | } | 189 | } |
| 198 | } | ||
| 199 | 190 | ||
| 200 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | 191 | // Reset configuration. |
| 201 | pub(super) fn teardown_dma() { | ||
| 202 | //disable dma control | ||
| 203 | #[cfg(not(any(adc_g0, adc_u0)))] | 192 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 204 | T::regs().cfgr().modify(|reg| { | 193 | T::regs().cfgr().modify(|reg| { |
| 194 | reg.set_cont(false); | ||
| 205 | reg.set_dmaen(false); | 195 | reg.set_dmaen(false); |
| 206 | }); | 196 | }); |
| 207 | #[cfg(any(adc_g0, adc_u0))] | 197 | #[cfg(any(adc_g0, adc_u0))] |
| 208 | T::regs().cfgr1().modify(|reg| { | 198 | T::regs().cfgr1().modify(|reg| { |
| 199 | reg.set_cont(false); | ||
| 209 | reg.set_dmaen(false); | 200 | reg.set_dmaen(false); |
| 210 | }); | 201 | }); |
| 211 | } | 202 | } |
| 212 | 203 | ||
| 213 | /// Initialize the ADC leaving any analog clock at reset value. | 204 | /// Perform a single conversion. |
| 214 | /// For G0 and WL, this is the async clock without prescaler. | 205 | fn convert() -> u16 { |
| 215 | pub fn new(adc: Peri<'d, T>) -> Self { | 206 | // Some models are affected by an erratum: |
| 216 | Self::init_regulator(); | 207 | // If we perform conversions slower than 1 kHz, the first read ADC value can be |
| 217 | Self::init_calibrate(); | 208 | // corrupted, so we discard it and measure again. |
| 218 | Self { adc } | 209 | // |
| 219 | } | 210 | // STM32L471xx: Section 2.7.3 |
| 220 | 211 | // STM32G4: Section 2.7.3 | |
| 221 | #[cfg(adc_g0)] | 212 | #[cfg(any(rcc_l4, rcc_g4))] |
| 222 | /// Initialize ADC with explicit clock for the analog ADC | 213 | let len = 2; |
| 223 | pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { | ||
| 224 | Self::init_regulator(); | ||
| 225 | |||
| 226 | #[cfg(any(stm32wl5x))] | ||
| 227 | { | ||
| 228 | // Reset value 0 is actually _No clock selected_ in the STM32WL5x reference manual | ||
| 229 | let async_clock_available = pac::RCC.ccipr().read().adcsel() != pac::rcc::vals::Adcsel::_RESERVED_0; | ||
| 230 | match clock { | ||
| 231 | Clock::Async { div: _ } => { | ||
| 232 | assert!(async_clock_available); | ||
| 233 | } | ||
| 234 | Clock::Sync { div: _ } => { | ||
| 235 | if async_clock_available { | ||
| 236 | warn!("Not using configured ADC clock"); | ||
| 237 | } | ||
| 238 | } | ||
| 239 | } | ||
| 240 | } | ||
| 241 | match clock { | ||
| 242 | Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div)), | ||
| 243 | Clock::Sync { div } => T::regs().cfgr2().modify(|reg| { | ||
| 244 | reg.set_ckmode(match div { | ||
| 245 | CkModePclk::DIV1 => Ckmode::PCLK, | ||
| 246 | CkModePclk::DIV2 => Ckmode::PCLK_DIV2, | ||
| 247 | CkModePclk::DIV4 => Ckmode::PCLK_DIV4, | ||
| 248 | }) | ||
| 249 | }), | ||
| 250 | } | ||
| 251 | |||
| 252 | Self::init_calibrate(); | ||
| 253 | |||
| 254 | Self { adc } | ||
| 255 | } | ||
| 256 | 214 | ||
| 257 | // Enable ADC only when it is not already running. | 215 | #[cfg(not(any(rcc_l4, rcc_g4)))] |
| 258 | fn enable(&mut self) { | 216 | let len = 1; |
| 259 | // Make sure bits are off | ||
| 260 | while T::regs().cr().read().addis() { | ||
| 261 | // spin | ||
| 262 | } | ||
| 263 | 217 | ||
| 264 | if !T::regs().cr().read().aden() { | 218 | for _ in 0..len { |
| 265 | // Enable ADC | ||
| 266 | T::regs().isr().modify(|reg| { | 219 | T::regs().isr().modify(|reg| { |
| 267 | reg.set_adrdy(true); | 220 | reg.set_eos(true); |
| 221 | reg.set_eoc(true); | ||
| 268 | }); | 222 | }); |
| 223 | |||
| 224 | // Start conversion | ||
| 269 | T::regs().cr().modify(|reg| { | 225 | T::regs().cr().modify(|reg| { |
| 270 | reg.set_aden(true); | 226 | reg.set_adstart(true); |
| 271 | }); | 227 | }); |
| 272 | 228 | ||
| 273 | while !T::regs().isr().read().adrdy() { | 229 | while !T::regs().isr().read().eos() { |
| 274 | // spin | 230 | // spin |
| 275 | } | 231 | } |
| 276 | } | 232 | } |
| 277 | } | ||
| 278 | |||
| 279 | pub fn enable_vrefint(&self) -> VrefInt { | ||
| 280 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 281 | T::common_regs().ccr().modify(|reg| { | ||
| 282 | reg.set_vrefen(true); | ||
| 283 | }); | ||
| 284 | #[cfg(any(adc_g0, adc_u0))] | ||
| 285 | T::regs().ccr().modify(|reg| { | ||
| 286 | reg.set_vrefen(true); | ||
| 287 | }); | ||
| 288 | |||
| 289 | // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us | ||
| 290 | // to stabilize the internal voltage reference. | ||
| 291 | blocking_delay_us(15); | ||
| 292 | |||
| 293 | VrefInt {} | ||
| 294 | } | ||
| 295 | |||
| 296 | pub fn enable_temperature(&self) -> Temperature { | ||
| 297 | cfg_if! { | ||
| 298 | if #[cfg(any(adc_g0, adc_u0))] { | ||
| 299 | T::regs().ccr().modify(|reg| { | ||
| 300 | reg.set_tsen(true); | ||
| 301 | }); | ||
| 302 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 303 | T::common_regs().ccr().modify(|reg| { | ||
| 304 | reg.set_tsen(true); | ||
| 305 | }); | ||
| 306 | } else { | ||
| 307 | T::common_regs().ccr().modify(|reg| { | ||
| 308 | reg.set_ch17sel(true); | ||
| 309 | }); | ||
| 310 | } | ||
| 311 | } | ||
| 312 | |||
| 313 | Temperature {} | ||
| 314 | } | ||
| 315 | 233 | ||
| 316 | pub fn enable_vbat(&self) -> Vbat { | 234 | T::regs().dr().read().0 as u16 |
| 317 | cfg_if! { | ||
| 318 | if #[cfg(any(adc_g0, adc_u0))] { | ||
| 319 | T::regs().ccr().modify(|reg| { | ||
| 320 | reg.set_vbaten(true); | ||
| 321 | }); | ||
| 322 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 323 | T::common_regs().ccr().modify(|reg| { | ||
| 324 | reg.set_vbaten(true); | ||
| 325 | }); | ||
| 326 | } else { | ||
| 327 | T::common_regs().ccr().modify(|reg| { | ||
| 328 | reg.set_ch18sel(true); | ||
| 329 | }); | ||
| 330 | } | ||
| 331 | } | ||
| 332 | |||
| 333 | Vbat {} | ||
| 334 | } | ||
| 335 | |||
| 336 | /// Set the ADC resolution. | ||
| 337 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 338 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 339 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 340 | #[cfg(any(adc_g0, adc_u0))] | ||
| 341 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); | ||
| 342 | } | ||
| 343 | |||
| 344 | pub fn set_averaging(&mut self, averaging: Averaging) { | ||
| 345 | let (enable, samples, right_shift) = match averaging { | ||
| 346 | Averaging::Disabled => (false, 0, 0), | ||
| 347 | Averaging::Samples2 => (true, 0, 1), | ||
| 348 | Averaging::Samples4 => (true, 1, 2), | ||
| 349 | Averaging::Samples8 => (true, 2, 3), | ||
| 350 | Averaging::Samples16 => (true, 3, 4), | ||
| 351 | Averaging::Samples32 => (true, 4, 5), | ||
| 352 | Averaging::Samples64 => (true, 5, 6), | ||
| 353 | Averaging::Samples128 => (true, 6, 7), | ||
| 354 | Averaging::Samples256 => (true, 7, 8), | ||
| 355 | }; | ||
| 356 | T::regs().cfgr2().modify(|reg| { | ||
| 357 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 358 | reg.set_rovse(enable); | ||
| 359 | #[cfg(any(adc_g0, adc_u0))] | ||
| 360 | reg.set_ovse(enable); | ||
| 361 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 362 | reg.set_ovsr(samples.into()); | ||
| 363 | #[cfg(not(any(adc_h5, adc_h7rs)))] | ||
| 364 | reg.set_ovsr(samples.into()); | ||
| 365 | reg.set_ovss(right_shift.into()); | ||
| 366 | }) | ||
| 367 | } | ||
| 368 | /* | ||
| 369 | /// Convert a raw sample from the `Temperature` to deg C | ||
| 370 | pub fn to_degrees_centigrade(sample: u16) -> f32 { | ||
| 371 | (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) | ||
| 372 | * (sample as f32 - VtempCal30::get().read() as f32) | ||
| 373 | + 30.0 | ||
| 374 | } | 235 | } |
| 375 | */ | ||
| 376 | 236 | ||
| 377 | /// Perform a single conversion. | 237 | fn configure_dma(conversion_mode: ConversionMode) { |
| 378 | fn convert(&mut self) -> u16 { | 238 | // Set continuous mode with oneshot dma. |
| 239 | // Clear overrun flag before starting transfer. | ||
| 379 | T::regs().isr().modify(|reg| { | 240 | T::regs().isr().modify(|reg| { |
| 380 | reg.set_eos(true); | 241 | reg.set_ovr(true); |
| 381 | reg.set_eoc(true); | ||
| 382 | }); | ||
| 383 | |||
| 384 | // Start conversion | ||
| 385 | T::regs().cr().modify(|reg| { | ||
| 386 | reg.set_adstart(true); | ||
| 387 | }); | 242 | }); |
| 388 | 243 | ||
| 389 | while !T::regs().isr().read().eos() { | 244 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 390 | // spin | 245 | let regs = T::regs().cfgr(); |
| 391 | } | ||
| 392 | 246 | ||
| 393 | T::regs().dr().read().0 as u16 | 247 | #[cfg(any(adc_g0, adc_u0))] |
| 394 | } | 248 | let regs = T::regs().cfgr1(); |
| 395 | 249 | ||
| 396 | /// Read an ADC channel. | 250 | regs.modify(|reg| { |
| 397 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | 251 | reg.set_discen(false); |
| 398 | self.read_channel(channel, sample_time) | 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 | }); | ||
| 399 | } | 260 | } |
| 400 | 261 | ||
| 401 | /// Read one or multiple ADC channels using DMA. | 262 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| 402 | /// | 263 | #[cfg(adc_h5)] |
| 403 | /// `readings` must have a length that is a multiple of the length of the | 264 | T::regs().cr().modify(|w| w.set_aden(false)); |
| 404 | /// `sequence` iterator. | ||
| 405 | /// | ||
| 406 | /// Note: The order of values in `readings` is defined by the pin ADC | ||
| 407 | /// channel number and not the pin order in `sequence`. | ||
| 408 | /// | ||
| 409 | /// Example | ||
| 410 | /// ```rust,ignore | ||
| 411 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 412 | /// | ||
| 413 | /// let mut adc = Adc::new(p.ADC1); | ||
| 414 | /// let mut adc_pin0 = p.PA0.degrade_adc(); | ||
| 415 | /// let mut adc_pin1 = p.PA1.degrade_adc(); | ||
| 416 | /// let mut measurements = [0u16; 2]; | ||
| 417 | /// | ||
| 418 | /// adc.read( | ||
| 419 | /// p.DMA1_CH2.reborrow(), | ||
| 420 | /// [ | ||
| 421 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 422 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 423 | /// ] | ||
| 424 | /// .into_iter(), | ||
| 425 | /// &mut measurements, | ||
| 426 | /// ) | ||
| 427 | /// .await; | ||
| 428 | /// defmt::info!("measurements: {}", measurements); | ||
| 429 | /// ``` | ||
| 430 | pub async fn read( | ||
| 431 | &mut self, | ||
| 432 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 433 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 434 | readings: &mut [u16], | ||
| 435 | ) { | ||
| 436 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 437 | assert!( | ||
| 438 | readings.len() % sequence.len() == 0, | ||
| 439 | "Readings length must be a multiple of sequence length" | ||
| 440 | ); | ||
| 441 | assert!( | ||
| 442 | sequence.len() <= 16, | ||
| 443 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 444 | ); | ||
| 445 | |||
| 446 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 447 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 448 | |||
| 449 | // Ensure no conversions are ongoing and ADC is enabled. | ||
| 450 | Self::cancel_conversions(); | ||
| 451 | self.enable(); | ||
| 452 | 265 | ||
| 453 | // Set sequence length | 266 | // Set sequence length |
| 454 | #[cfg(not(any(adc_g0, adc_u0)))] | 267 | #[cfg(not(any(adc_g0, adc_u0)))] |
| @@ -462,10 +275,10 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 462 | 275 | ||
| 463 | T::regs().chselr().write(|chselr| { | 276 | T::regs().chselr().write(|chselr| { |
| 464 | T::regs().smpr().write(|smpr| { | 277 | T::regs().smpr().write(|smpr| { |
| 465 | for (channel, sample_time) in sequence { | 278 | for ((channel, _), sample_time) in sequence { |
| 466 | chselr.set_chsel(channel.channel.into(), true); | 279 | chselr.set_chsel(channel.into(), true); |
| 467 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { | 280 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { |
| 468 | smpr.set_smpsel(channel.channel.into(), (i as u8).into()); | 281 | smpr.set_smpsel(channel.into(), (i as u8).into()); |
| 469 | } else { | 282 | } else { |
| 470 | smpr.set_sample_time(sample_times.len(), sample_time); | 283 | smpr.set_sample_time(sample_times.len(), sample_time); |
| 471 | if let Err(_) = sample_times.push(sample_time) { | 284 | if let Err(_) = sample_times.push(sample_time) { |
| @@ -484,42 +297,86 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 484 | #[cfg(adc_u0)] | 297 | #[cfg(adc_u0)] |
| 485 | let mut channel_mask = 0; | 298 | let mut channel_mask = 0; |
| 486 | 299 | ||
| 300 | #[cfg(adc_h5)] | ||
| 301 | let mut difsel = 0u32; | ||
| 302 | |||
| 487 | // Configure channels and ranks | 303 | // Configure channels and ranks |
| 488 | for (_i, (channel, sample_time)) in sequence.enumerate() { | 304 | for (_i, ((channel, _is_differential), sample_time)) in sequence.enumerate() { |
| 489 | Self::configure_channel(channel, sample_time); | 305 | // RM0492, RM0481, etc. |
| 306 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | ||
| 307 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 308 | if channel == 0 { | ||
| 309 | T::regs().or().modify(|reg| reg.set_op0(true)); | ||
| 310 | } | ||
| 311 | |||
| 312 | // Configure channel | ||
| 313 | cfg_if! { | ||
| 314 | if #[cfg(adc_u0)] { | ||
| 315 | // On G0 and U6 all channels use the same sampling time. | ||
| 316 | T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); | ||
| 317 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 318 | match channel { | ||
| 319 | 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), | ||
| 320 | _ => T::regs().smpr2().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), | ||
| 321 | } | ||
| 322 | } else { | ||
| 323 | let sample_time = sample_time.into(); | ||
| 324 | T::regs() | ||
| 325 | .smpr(channel as usize / 10) | ||
| 326 | .modify(|reg| reg.set_smp(channel as usize % 10, sample_time)); | ||
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | #[cfg(stm32h7)] | ||
| 331 | { | ||
| 332 | use crate::pac::adc::vals::Pcsel; | ||
| 333 | |||
| 334 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 335 | T::regs() | ||
| 336 | .pcsel() | ||
| 337 | .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); | ||
| 338 | } | ||
| 490 | 339 | ||
| 491 | // Each channel is sampled according to sequence | 340 | // Each channel is sampled according to sequence |
| 492 | #[cfg(not(any(adc_g0, adc_u0)))] | 341 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 493 | match _i { | 342 | match _i { |
| 494 | 0..=3 => { | 343 | 0..=3 => { |
| 495 | T::regs().sqr1().modify(|w| { | 344 | T::regs().sqr1().modify(|w| { |
| 496 | w.set_sq(_i, channel.channel()); | 345 | w.set_sq(_i, channel); |
| 497 | }); | 346 | }); |
| 498 | } | 347 | } |
| 499 | 4..=8 => { | 348 | 4..=8 => { |
| 500 | T::regs().sqr2().modify(|w| { | 349 | T::regs().sqr2().modify(|w| { |
| 501 | w.set_sq(_i - 4, channel.channel()); | 350 | w.set_sq(_i - 4, channel); |
| 502 | }); | 351 | }); |
| 503 | } | 352 | } |
| 504 | 9..=13 => { | 353 | 9..=13 => { |
| 505 | T::regs().sqr3().modify(|w| { | 354 | T::regs().sqr3().modify(|w| { |
| 506 | w.set_sq(_i - 9, channel.channel()); | 355 | w.set_sq(_i - 9, channel); |
| 507 | }); | 356 | }); |
| 508 | } | 357 | } |
| 509 | 14..=15 => { | 358 | 14..=15 => { |
| 510 | T::regs().sqr4().modify(|w| { | 359 | T::regs().sqr4().modify(|w| { |
| 511 | w.set_sq(_i - 14, channel.channel()); | 360 | w.set_sq(_i - 14, channel); |
| 512 | }); | 361 | }); |
| 513 | } | 362 | } |
| 514 | _ => unreachable!(), | 363 | _ => unreachable!(), |
| 515 | } | 364 | } |
| 516 | 365 | ||
| 366 | #[cfg(adc_h5)] | ||
| 367 | { | ||
| 368 | difsel |= (_is_differential as u32) << channel; | ||
| 369 | } | ||
| 370 | |||
| 517 | #[cfg(adc_u0)] | 371 | #[cfg(adc_u0)] |
| 518 | { | 372 | { |
| 519 | channel_mask |= 1 << channel.channel(); | 373 | channel_mask |= 1 << channel; |
| 520 | } | 374 | } |
| 521 | } | 375 | } |
| 522 | 376 | ||
| 377 | #[cfg(adc_h5)] | ||
| 378 | T::regs().difsel().write(|w| w.set_difsel(difsel)); | ||
| 379 | |||
| 523 | // On G0 and U0 enabled channels are sampled from 0 to last channel. | 380 | // On G0 and U0 enabled channels are sampled from 0 to last channel. |
| 524 | // It is possible to add up to 8 sequences if CHSELRMOD = 1. | 381 | // It is possible to add up to 8 sequences if CHSELRMOD = 1. |
| 525 | // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. | 382 | // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. |
| @@ -528,312 +385,234 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 528 | reg.set_chsel(channel_mask); | 385 | reg.set_chsel(channel_mask); |
| 529 | }); | 386 | }); |
| 530 | } | 387 | } |
| 531 | // Set continuous mode with oneshot dma. | 388 | } |
| 532 | // Clear overrun flag before starting transfer. | 389 | } |
| 533 | T::regs().isr().modify(|reg| { | ||
| 534 | reg.set_ovr(true); | ||
| 535 | }); | ||
| 536 | 390 | ||
| 537 | #[cfg(not(any(adc_g0, adc_u0)))] | 391 | impl<'d, T: Instance> Adc<'d, T> { |
| 538 | T::regs().cfgr().modify(|reg| { | 392 | /// Enable the voltage regulator |
| 539 | reg.set_discen(false); | 393 | fn init_regulator() { |
| 540 | reg.set_cont(true); | 394 | rcc::enable_and_reset::<T>(); |
| 541 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | 395 | T::regs().cr().modify(|reg| { |
| 542 | reg.set_dmaen(true); | 396 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 397 | reg.set_deeppwd(false); | ||
| 398 | reg.set_advregen(true); | ||
| 543 | }); | 399 | }); |
| 400 | |||
| 401 | // If this is false then each ADC_CHSELR bit enables an input channel. | ||
| 402 | // This is the reset value, so has no effect. | ||
| 544 | #[cfg(any(adc_g0, adc_u0))] | 403 | #[cfg(any(adc_g0, adc_u0))] |
| 545 | T::regs().cfgr1().modify(|reg| { | 404 | T::regs().cfgr1().modify(|reg| { |
| 546 | reg.set_discen(false); | 405 | reg.set_chselrmod(false); |
| 547 | reg.set_cont(true); | ||
| 548 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 549 | reg.set_dmaen(true); | ||
| 550 | }); | 406 | }); |
| 551 | 407 | ||
| 552 | let request = rx_dma.request(); | 408 | blocking_delay_us(20); |
| 553 | let transfer = unsafe { | 409 | } |
| 554 | Transfer::new_read( | ||
| 555 | rx_dma, | ||
| 556 | request, | ||
| 557 | T::regs().dr().as_ptr() as *mut u16, | ||
| 558 | readings, | ||
| 559 | Default::default(), | ||
| 560 | ) | ||
| 561 | }; | ||
| 562 | 410 | ||
| 563 | // Start conversion | 411 | /// Calibrate to remove conversion offset |
| 412 | fn init_calibrate() { | ||
| 564 | T::regs().cr().modify(|reg| { | 413 | T::regs().cr().modify(|reg| { |
| 565 | reg.set_adstart(true); | 414 | reg.set_adcal(true); |
| 566 | }); | 415 | }); |
| 567 | 416 | ||
| 568 | // Wait for conversion sequence to finish. | 417 | while T::regs().cr().read().adcal() { |
| 569 | transfer.await; | 418 | // spin |
| 570 | 419 | } | |
| 571 | // Ensure conversions are finished. | ||
| 572 | Self::cancel_conversions(); | ||
| 573 | 420 | ||
| 574 | // Reset configuration. | 421 | blocking_delay_us(1); |
| 575 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 576 | T::regs().cfgr().modify(|reg| { | ||
| 577 | reg.set_cont(false); | ||
| 578 | }); | ||
| 579 | #[cfg(any(adc_g0, adc_u0))] | ||
| 580 | T::regs().cfgr1().modify(|reg| { | ||
| 581 | reg.set_cont(false); | ||
| 582 | }); | ||
| 583 | } | 422 | } |
| 584 | 423 | ||
| 585 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | 424 | /// Initialize the ADC leaving any analog clock at reset value. |
| 586 | /// | 425 | /// For G0 and WL, this is the async clock without prescaler. |
| 587 | /// The `dma_buf` should be large enough to prevent DMA buffer overrun. | 426 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 588 | /// The length of the `dma_buf` should be a multiple of the ADC channel count. | 427 | Self::init_regulator(); |
| 589 | /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. | 428 | Self::init_calibrate(); |
| 590 | /// | 429 | Self { adc } |
| 591 | /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length. | 430 | } |
| 592 | /// It is critical to call `read` frequently to prevent DMA buffer overrun. | ||
| 593 | /// | ||
| 594 | /// [`read`]: #method.read | ||
| 595 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 596 | pub fn into_ring_buffered<'a>( | ||
| 597 | &mut self, | ||
| 598 | dma: Peri<'a, impl RxDma<T>>, | ||
| 599 | dma_buf: &'a mut [u16], | ||
| 600 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 601 | ) -> RingBufferedAdc<'a, T> { | ||
| 602 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 603 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 604 | assert!( | ||
| 605 | sequence.len() <= 16, | ||
| 606 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 607 | ); | ||
| 608 | // reset conversions and enable the adc | ||
| 609 | Self::cancel_conversions(); | ||
| 610 | self.enable(); | ||
| 611 | |||
| 612 | //adc side setup | ||
| 613 | 431 | ||
| 614 | // Set sequence length | 432 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { |
| 615 | #[cfg(not(any(adc_g0, adc_u0)))] | 433 | #[cfg(not(adc_g0))] |
| 616 | T::regs().sqr1().modify(|w| { | 434 | let s = Self::new(adc); |
| 617 | w.set_l(sequence.len() as u8 - 1); | ||
| 618 | }); | ||
| 619 | 435 | ||
| 620 | #[cfg(adc_g0)] | 436 | #[cfg(adc_g0)] |
| 621 | { | 437 | let s = match config.clock { |
| 622 | let mut sample_times = Vec::<SampleTime, SAMPLE_TIMES_CAPACITY>::new(); | 438 | Some(clock) => Self::new_with_clock(adc, clock), |
| 439 | None => Self::new(adc), | ||
| 440 | }; | ||
| 623 | 441 | ||
| 624 | T::regs().chselr().write(|chselr| { | 442 | #[cfg(any(adc_g0, adc_u0, adc_v3))] |
| 625 | T::regs().smpr().write(|smpr| { | 443 | if let Some(shift) = config.oversampling_shift { |
| 626 | for (channel, sample_time) in sequence { | 444 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); |
| 627 | chselr.set_chsel(channel.channel.into(), true); | ||
| 628 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { | ||
| 629 | smpr.set_smpsel(channel.channel.into(), (i as u8).into()); | ||
| 630 | } else { | ||
| 631 | smpr.set_sample_time(sample_times.len(), sample_time); | ||
| 632 | if let Err(_) = sample_times.push(sample_time) { | ||
| 633 | panic!( | ||
| 634 | "Implementation is limited to {} unique sample times among all channels.", | ||
| 635 | SAMPLE_TIMES_CAPACITY | ||
| 636 | ); | ||
| 637 | } | ||
| 638 | } | ||
| 639 | } | ||
| 640 | }) | ||
| 641 | }); | ||
| 642 | } | 445 | } |
| 643 | #[cfg(not(adc_g0))] | ||
| 644 | { | ||
| 645 | #[cfg(adc_u0)] | ||
| 646 | let mut channel_mask = 0; | ||
| 647 | 446 | ||
| 648 | // Configure channels and ranks | 447 | #[cfg(any(adc_g0, adc_u0, adc_v3))] |
| 649 | for (_i, (mut channel, sample_time)) in sequence.enumerate() { | 448 | if let Some(ratio) = config.oversampling_ratio { |
| 650 | Self::configure_channel(&mut channel, sample_time); | 449 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); |
| 450 | } | ||
| 651 | 451 | ||
| 652 | // Each channel is sampled according to sequence | 452 | #[cfg(any(adc_g0, adc_u0))] |
| 653 | #[cfg(not(any(adc_g0, adc_u0)))] | 453 | if let Some(enable) = config.oversampling_enable { |
| 654 | match _i { | 454 | T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); |
| 655 | 0..=3 => { | 455 | } |
| 656 | T::regs().sqr1().modify(|w| { | ||
| 657 | w.set_sq(_i, channel.channel()); | ||
| 658 | }); | ||
| 659 | } | ||
| 660 | 4..=8 => { | ||
| 661 | T::regs().sqr2().modify(|w| { | ||
| 662 | w.set_sq(_i - 4, channel.channel()); | ||
| 663 | }); | ||
| 664 | } | ||
| 665 | 9..=13 => { | ||
| 666 | T::regs().sqr3().modify(|w| { | ||
| 667 | w.set_sq(_i - 9, channel.channel()); | ||
| 668 | }); | ||
| 669 | } | ||
| 670 | 14..=15 => { | ||
| 671 | T::regs().sqr4().modify(|w| { | ||
| 672 | w.set_sq(_i - 14, channel.channel()); | ||
| 673 | }); | ||
| 674 | } | ||
| 675 | _ => unreachable!(), | ||
| 676 | } | ||
| 677 | 456 | ||
| 678 | #[cfg(adc_u0)] | 457 | #[cfg(adc_v3)] |
| 679 | { | 458 | if let Some((mode, trig_mode, enable)) = config.oversampling_mode { |
| 680 | channel_mask |= 1 << channel.channel(); | 459 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); |
| 681 | } | 460 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); |
| 682 | } | 461 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); |
| 462 | } | ||
| 683 | 463 | ||
| 684 | // On G0 and U0 enabled channels are sampled from 0 to last channel. | 464 | if let Some(resolution) = config.resolution { |
| 685 | // It is possible to add up to 8 sequences if CHSELRMOD = 1. | 465 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 686 | // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. | 466 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); |
| 687 | #[cfg(adc_u0)] | 467 | #[cfg(any(adc_g0, adc_u0))] |
| 688 | T::regs().chselr().modify(|reg| { | 468 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); |
| 689 | reg.set_chsel(channel_mask); | ||
| 690 | }); | ||
| 691 | } | 469 | } |
| 692 | // Set continuous mode with Circular dma. | ||
| 693 | // Clear overrun flag before starting transfer. | ||
| 694 | T::regs().isr().modify(|reg| { | ||
| 695 | reg.set_ovr(true); | ||
| 696 | }); | ||
| 697 | 470 | ||
| 698 | #[cfg(not(any(adc_g0, adc_u0)))] | 471 | if let Some(averaging) = config.averaging { |
| 699 | T::regs().cfgr().modify(|reg| { | 472 | let (enable, samples, right_shift) = match averaging { |
| 700 | reg.set_discen(false); | 473 | Averaging::Disabled => (false, 0, 0), |
| 701 | reg.set_cont(true); | 474 | Averaging::Samples2 => (true, 0, 1), |
| 702 | reg.set_dmacfg(Dmacfg::CIRCULAR); | 475 | Averaging::Samples4 => (true, 1, 2), |
| 703 | reg.set_dmaen(true); | 476 | Averaging::Samples8 => (true, 2, 3), |
| 704 | }); | 477 | Averaging::Samples16 => (true, 3, 4), |
| 705 | #[cfg(any(adc_g0, adc_u0))] | 478 | Averaging::Samples32 => (true, 4, 5), |
| 706 | T::regs().cfgr1().modify(|reg| { | 479 | Averaging::Samples64 => (true, 5, 6), |
| 707 | reg.set_discen(false); | 480 | Averaging::Samples128 => (true, 6, 7), |
| 708 | reg.set_cont(true); | 481 | Averaging::Samples256 => (true, 7, 8), |
| 709 | reg.set_dmacfg(Dmacfg::CIRCULAR); | 482 | }; |
| 710 | reg.set_dmaen(true); | 483 | T::regs().cfgr2().modify(|reg| { |
| 711 | }); | 484 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 485 | reg.set_rovse(enable); | ||
| 486 | #[cfg(any(adc_g0, adc_u0))] | ||
| 487 | reg.set_ovse(enable); | ||
| 488 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 489 | reg.set_ovsr(samples.into()); | ||
| 490 | #[cfg(not(any(adc_h5, adc_h7rs)))] | ||
| 491 | reg.set_ovsr(samples.into()); | ||
| 492 | reg.set_ovss(right_shift.into()); | ||
| 493 | }) | ||
| 494 | } | ||
| 712 | 495 | ||
| 713 | RingBufferedAdc::new(dma, dma_buf) | 496 | s |
| 714 | } | 497 | } |
| 715 | 498 | ||
| 716 | #[cfg(not(adc_g0))] | 499 | #[cfg(adc_g0)] |
| 717 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | 500 | /// Initialize ADC with explicit clock for the analog ADC |
| 718 | // RM0492, RM0481, etc. | 501 | pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { |
| 719 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | 502 | Self::init_regulator(); |
| 720 | #[cfg(any(adc_h5, adc_h7rs))] | 503 | |
| 721 | if channel.channel() == 0 { | 504 | #[cfg(any(stm32wl5x))] |
| 722 | T::regs().or().modify(|reg| reg.set_op0(true)); | 505 | { |
| 506 | // Reset value 0 is actually _No clock selected_ in the STM32WL5x reference manual | ||
| 507 | let async_clock_available = pac::RCC.ccipr().read().adcsel() != pac::rcc::vals::Adcsel::_RESERVED_0; | ||
| 508 | match clock { | ||
| 509 | Clock::Async { div: _ } => { | ||
| 510 | assert!(async_clock_available); | ||
| 511 | } | ||
| 512 | Clock::Sync { div: _ } => { | ||
| 513 | if async_clock_available { | ||
| 514 | warn!("Not using configured ADC clock"); | ||
| 515 | } | ||
| 516 | } | ||
| 517 | } | ||
| 518 | } | ||
| 519 | match clock { | ||
| 520 | Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div)), | ||
| 521 | Clock::Sync { div } => T::regs().cfgr2().modify(|reg| { | ||
| 522 | reg.set_ckmode(match div { | ||
| 523 | CkModePclk::DIV1 => Ckmode::PCLK, | ||
| 524 | CkModePclk::DIV2 => Ckmode::PCLK_DIV2, | ||
| 525 | CkModePclk::DIV4 => Ckmode::PCLK_DIV4, | ||
| 526 | }) | ||
| 527 | }), | ||
| 723 | } | 528 | } |
| 724 | 529 | ||
| 725 | // Configure channel | 530 | Self::init_calibrate(); |
| 726 | Self::set_channel_sample_time(channel.channel(), sample_time); | 531 | |
| 532 | Self { adc } | ||
| 727 | } | 533 | } |
| 728 | 534 | ||
| 729 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | 535 | pub fn enable_vrefint(&self) -> VrefInt { |
| 730 | self.enable(); | ||
| 731 | #[cfg(not(adc_g0))] | ||
| 732 | Self::configure_channel(channel, sample_time); | ||
| 733 | #[cfg(adc_g0)] | ||
| 734 | T::regs().smpr().write(|reg| { | ||
| 735 | reg.set_sample_time(0, sample_time); | ||
| 736 | reg.set_smpsel(channel.channel().into(), Smpsel::SMP1); | ||
| 737 | }); | ||
| 738 | // Select channel | ||
| 739 | #[cfg(not(any(adc_g0, adc_u0)))] | 536 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 740 | T::regs().sqr1().write(|reg| reg.set_sq(0, channel.channel())); | 537 | T::common_regs().ccr().modify(|reg| { |
| 538 | reg.set_vrefen(true); | ||
| 539 | }); | ||
| 741 | #[cfg(any(adc_g0, adc_u0))] | 540 | #[cfg(any(adc_g0, adc_u0))] |
| 742 | T::regs().chselr().write(|reg| { | 541 | T::regs().ccr().modify(|reg| { |
| 743 | #[cfg(adc_g0)] | 542 | reg.set_vrefen(true); |
| 744 | reg.set_chsel(channel.channel().into(), true); | ||
| 745 | #[cfg(adc_u0)] | ||
| 746 | reg.set_chsel(1 << channel.channel()); | ||
| 747 | }); | 543 | }); |
| 748 | 544 | ||
| 749 | // Some models are affected by an erratum: | 545 | // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us |
| 750 | // If we perform conversions slower than 1 kHz, the first read ADC value can be | 546 | // to stabilize the internal voltage reference. |
| 751 | // corrupted, so we discard it and measure again. | 547 | blocking_delay_us(15); |
| 752 | // | ||
| 753 | // STM32L471xx: Section 2.7.3 | ||
| 754 | // STM32G4: Section 2.7.3 | ||
| 755 | #[cfg(any(rcc_l4, rcc_g4))] | ||
| 756 | let _ = self.convert(); | ||
| 757 | let val = self.convert(); | ||
| 758 | |||
| 759 | T::regs().cr().modify(|reg| reg.set_addis(true)); | ||
| 760 | |||
| 761 | // RM0492, RM0481, etc. | ||
| 762 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | ||
| 763 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 764 | if channel.channel() == 0 { | ||
| 765 | T::regs().or().modify(|reg| reg.set_op0(false)); | ||
| 766 | } | ||
| 767 | |||
| 768 | val | ||
| 769 | } | ||
| 770 | |||
| 771 | #[cfg(adc_g0)] | ||
| 772 | pub fn set_oversampling_shift(&mut self, shift: Ovss) { | ||
| 773 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 774 | } | ||
| 775 | #[cfg(adc_u0)] | ||
| 776 | pub fn set_oversampling_shift(&mut self, shift: u8) { | ||
| 777 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 778 | } | ||
| 779 | 548 | ||
| 780 | #[cfg(adc_g0)] | 549 | VrefInt {} |
| 781 | pub fn set_oversampling_ratio(&mut self, ratio: Ovsr) { | ||
| 782 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 783 | } | ||
| 784 | #[cfg(adc_u0)] | ||
| 785 | pub fn set_oversampling_ratio(&mut self, ratio: u8) { | ||
| 786 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 787 | } | 550 | } |
| 788 | 551 | ||
| 789 | #[cfg(any(adc_g0, adc_u0))] | 552 | pub fn enable_temperature(&self) -> Temperature { |
| 790 | pub fn oversampling_enable(&mut self, enable: bool) { | 553 | cfg_if! { |
| 791 | T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); | 554 | if #[cfg(any(adc_g0, adc_u0))] { |
| 792 | } | 555 | T::regs().ccr().modify(|reg| { |
| 556 | reg.set_tsen(true); | ||
| 557 | }); | ||
| 558 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 559 | T::common_regs().ccr().modify(|reg| { | ||
| 560 | reg.set_tsen(true); | ||
| 561 | }); | ||
| 562 | } else { | ||
| 563 | T::common_regs().ccr().modify(|reg| { | ||
| 564 | reg.set_ch17sel(true); | ||
| 565 | }); | ||
| 566 | } | ||
| 567 | } | ||
| 793 | 568 | ||
| 794 | #[cfg(adc_v3)] | 569 | Temperature {} |
| 795 | pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { | ||
| 796 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 797 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 798 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 799 | } | 570 | } |
| 800 | 571 | ||
| 801 | #[cfg(adc_v3)] | 572 | pub fn enable_vbat(&self) -> Vbat { |
| 802 | pub fn set_oversampling_ratio(&mut self, ratio: OversamplingRatio) { | 573 | cfg_if! { |
| 803 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | 574 | if #[cfg(any(adc_g0, adc_u0))] { |
| 804 | } | 575 | T::regs().ccr().modify(|reg| { |
| 576 | reg.set_vbaten(true); | ||
| 577 | }); | ||
| 578 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 579 | T::common_regs().ccr().modify(|reg| { | ||
| 580 | reg.set_vbaten(true); | ||
| 581 | }); | ||
| 582 | } else { | ||
| 583 | T::common_regs().ccr().modify(|reg| { | ||
| 584 | reg.set_ch18sel(true); | ||
| 585 | }); | ||
| 586 | } | ||
| 587 | } | ||
| 805 | 588 | ||
| 806 | #[cfg(adc_v3)] | 589 | Vbat {} |
| 807 | pub fn set_oversampling_shift(&mut self, shift: OversamplingShift) { | ||
| 808 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 809 | } | 590 | } |
| 810 | 591 | ||
| 811 | #[cfg(not(adc_g0))] | 592 | pub fn disable_vbat(&self) { |
| 812 | fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { | ||
| 813 | cfg_if! { | 593 | cfg_if! { |
| 814 | if #[cfg(adc_u0)] { | 594 | if #[cfg(any(adc_g0, adc_u0))] { |
| 815 | // On G0 and U6 all channels use the same sampling time. | 595 | T::regs().ccr().modify(|reg| { |
| 816 | T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); | 596 | reg.set_vbaten(false); |
| 597 | }); | ||
| 817 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 598 | } else if #[cfg(any(adc_h5, adc_h7rs))] { |
| 818 | match _ch { | 599 | T::common_regs().ccr().modify(|reg| { |
| 819 | 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), | 600 | reg.set_vbaten(false); |
| 820 | _ => T::regs().smpr2().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), | 601 | }); |
| 821 | } | ||
| 822 | } else { | 602 | } else { |
| 823 | let sample_time = sample_time.into(); | 603 | T::common_regs().ccr().modify(|reg| { |
| 824 | T::regs() | 604 | reg.set_ch18sel(false); |
| 825 | .smpr(_ch as usize / 10) | 605 | }); |
| 826 | .modify(|reg| reg.set_smp(_ch as usize % 10, sample_time)); | ||
| 827 | } | 606 | } |
| 828 | } | 607 | } |
| 829 | } | 608 | } |
| 830 | 609 | ||
| 831 | fn cancel_conversions() { | 610 | /* |
| 832 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | 611 | /// Convert a raw sample from the `Temperature` to deg C |
| 833 | T::regs().cr().modify(|reg| { | 612 | pub fn to_degrees_centigrade(sample: u16) -> f32 { |
| 834 | reg.set_adstp(true); | 613 | (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) |
| 835 | }); | 614 | * (sample as f32 - VtempCal30::get().read() as f32) |
| 836 | while T::regs().cr().read().adstart() {} | 615 | + 30.0 |
| 837 | } | ||
| 838 | } | 616 | } |
| 617 | */ | ||
| 839 | } | 618 | } |
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 1d5d3fb92..a3d9e6176 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs | |||
| @@ -4,11 +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, Temperature, Vbat, | 8 | use crate::adc::ConversionMode; |
| 9 | VrefInt, blocking_delay_us, | ||
| 10 | }; | ||
| 11 | use crate::dma::Transfer; | ||
| 12 | use crate::time::Hertz; | 9 | use crate::time::Hertz; |
| 13 | use crate::{Peri, pac, rcc}; | 10 | use crate::{Peri, pac, rcc}; |
| 14 | 11 | ||
| @@ -62,101 +59,191 @@ impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { | |||
| 62 | const CHANNEL: u8 = 18; | 59 | const CHANNEL: u8 = 18; |
| 63 | } | 60 | } |
| 64 | 61 | ||
| 65 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | 62 | fn from_ker_ck(frequency: Hertz) -> Presc { |
| 66 | // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. | 63 | let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); |
| 67 | #[allow(unused)] | 64 | match raw_prescaler { |
| 68 | enum Prescaler { | 65 | 0 => Presc::DIV1, |
| 69 | NotDivided, | 66 | 1 => Presc::DIV2, |
| 70 | DividedBy2, | 67 | 2..=3 => Presc::DIV4, |
| 71 | DividedBy4, | 68 | 4..=5 => Presc::DIV6, |
| 72 | DividedBy6, | 69 | 6..=7 => Presc::DIV8, |
| 73 | DividedBy8, | 70 | 8..=9 => Presc::DIV10, |
| 74 | DividedBy10, | 71 | 10..=11 => Presc::DIV12, |
| 75 | DividedBy12, | 72 | _ => unimplemented!(), |
| 76 | DividedBy16, | 73 | } |
| 77 | DividedBy32, | 74 | } |
| 78 | DividedBy64, | 75 | |
| 79 | DividedBy128, | 76 | /// Adc configuration |
| 80 | DividedBy256, | 77 | #[derive(Default)] |
| 78 | pub struct AdcConfig { | ||
| 79 | pub resolution: Option<Resolution>, | ||
| 80 | pub averaging: Option<Averaging>, | ||
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | impl Prescaler { | 83 | impl<T: Instance> super::SealedAnyInstance for T { |
| 84 | fn from_ker_ck(frequency: Hertz) -> Self { | 84 | fn dr() -> *mut u16 { |
| 85 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | 85 | T::regs().dr().as_ptr() as *mut u16 |
| 86 | match raw_prescaler { | 86 | } |
| 87 | 0 => Self::NotDivided, | 87 | |
| 88 | 1 => Self::DividedBy2, | 88 | fn enable() { |
| 89 | 2..=3 => Self::DividedBy4, | 89 | T::regs().isr().write(|w| w.set_adrdy(true)); |
| 90 | 4..=5 => Self::DividedBy6, | 90 | T::regs().cr().modify(|w| w.set_aden(true)); |
| 91 | 6..=7 => Self::DividedBy8, | 91 | while !T::regs().isr().read().adrdy() {} |
| 92 | 8..=9 => Self::DividedBy10, | 92 | T::regs().isr().write(|w| w.set_adrdy(true)); |
| 93 | 10..=11 => Self::DividedBy12, | 93 | } |
| 94 | _ => unimplemented!(), | 94 | |
| 95 | fn start() { | ||
| 96 | // Start conversion | ||
| 97 | T::regs().cr().modify(|reg| { | ||
| 98 | reg.set_adstart(true); | ||
| 99 | }); | ||
| 100 | } | ||
| 101 | |||
| 102 | fn stop() { | ||
| 103 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 104 | T::regs().cr().modify(|reg| { | ||
| 105 | reg.set_adstp(Adstp::STOP); | ||
| 106 | }); | ||
| 107 | while T::regs().cr().read().adstart() {} | ||
| 95 | } | 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 | }); | ||
| 96 | } | 115 | } |
| 97 | 116 | ||
| 98 | fn divisor(&self) -> u32 { | 117 | fn convert() -> u16 { |
| 99 | match self { | 118 | T::regs().isr().modify(|reg| { |
| 100 | Prescaler::NotDivided => 1, | 119 | reg.set_eos(true); |
| 101 | Prescaler::DividedBy2 => 2, | 120 | reg.set_eoc(true); |
| 102 | Prescaler::DividedBy4 => 4, | 121 | }); |
| 103 | Prescaler::DividedBy6 => 6, | 122 | |
| 104 | Prescaler::DividedBy8 => 8, | 123 | // Start conversion |
| 105 | Prescaler::DividedBy10 => 10, | 124 | T::regs().cr().modify(|reg| { |
| 106 | Prescaler::DividedBy12 => 12, | 125 | reg.set_adstart(true); |
| 107 | Prescaler::DividedBy16 => 16, | 126 | }); |
| 108 | Prescaler::DividedBy32 => 32, | 127 | |
| 109 | Prescaler::DividedBy64 => 64, | 128 | while !T::regs().isr().read().eos() { |
| 110 | Prescaler::DividedBy128 => 128, | 129 | // spin |
| 111 | Prescaler::DividedBy256 => 256, | ||
| 112 | } | 130 | } |
| 131 | |||
| 132 | T::regs().dr().read().0 as u16 | ||
| 113 | } | 133 | } |
| 114 | 134 | ||
| 115 | fn presc(&self) -> Presc { | 135 | fn configure_dma(conversion_mode: ConversionMode) { |
| 116 | match self { | 136 | match conversion_mode { |
| 117 | Prescaler::NotDivided => Presc::DIV1, | 137 | ConversionMode::Singular => { |
| 118 | Prescaler::DividedBy2 => Presc::DIV2, | 138 | T::regs().isr().modify(|reg| { |
| 119 | Prescaler::DividedBy4 => Presc::DIV4, | 139 | reg.set_ovr(true); |
| 120 | Prescaler::DividedBy6 => Presc::DIV6, | 140 | }); |
| 121 | Prescaler::DividedBy8 => Presc::DIV8, | 141 | T::regs().cfgr().modify(|reg| { |
| 122 | Prescaler::DividedBy10 => Presc::DIV10, | 142 | reg.set_cont(true); |
| 123 | Prescaler::DividedBy12 => Presc::DIV12, | 143 | reg.set_dmngt(Dmngt::DMA_ONE_SHOT); |
| 124 | Prescaler::DividedBy16 => Presc::DIV16, | 144 | }); |
| 125 | Prescaler::DividedBy32 => Presc::DIV32, | 145 | } |
| 126 | Prescaler::DividedBy64 => Presc::DIV64, | 146 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] |
| 127 | Prescaler::DividedBy128 => Presc::DIV128, | 147 | _ => unreachable!(), |
| 128 | Prescaler::DividedBy256 => Presc::DIV256, | ||
| 129 | } | 148 | } |
| 130 | } | 149 | } |
| 131 | } | ||
| 132 | 150 | ||
| 133 | /// Number of samples used for averaging. | 151 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| 134 | #[derive(Copy, Clone, Debug)] | 152 | // Set sequence length |
| 135 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 153 | T::regs().sqr1().modify(|w| { |
| 136 | pub enum Averaging { | 154 | w.set_l(sequence.len() as u8 - 1); |
| 137 | Disabled, | 155 | }); |
| 138 | Samples2, | 156 | |
| 139 | Samples4, | 157 | // Configure channels and ranks |
| 140 | Samples8, | 158 | for (i, ((channel, _), sample_time)) in sequence.enumerate() { |
| 141 | Samples16, | 159 | let sample_time = sample_time.into(); |
| 142 | Samples32, | 160 | if channel <= 9 { |
| 143 | Samples64, | 161 | T::regs().smpr(0).modify(|reg| reg.set_smp(channel as _, sample_time)); |
| 144 | Samples128, | 162 | } else { |
| 145 | Samples256, | 163 | T::regs() |
| 146 | Samples512, | 164 | .smpr(1) |
| 147 | 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 | } | ||
| 148 | } | 201 | } |
| 149 | 202 | ||
| 150 | 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 | |||
| 151 | /// Create a new ADC driver. | 238 | /// Create a new ADC driver. |
| 152 | pub fn new(adc: Peri<'d, T>) -> Self { | 239 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 153 | rcc::enable_and_reset::<T>(); | 240 | rcc::enable_and_reset::<T>(); |
| 154 | 241 | ||
| 155 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 242 | let prescaler = from_ker_ck(T::frequency()); |
| 156 | 243 | ||
| 157 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 244 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 158 | 245 | ||
| 159 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 246 | let frequency = T::frequency() / prescaler; |
| 160 | info!("ADC frequency set to {}", frequency); | 247 | info!("ADC frequency set to {}", frequency); |
| 161 | 248 | ||
| 162 | if frequency > MAX_ADC_CLK_FREQ { | 249 | if frequency > MAX_ADC_CLK_FREQ { |
| @@ -179,37 +266,20 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 179 | }; | 266 | }; |
| 180 | T::regs().cr().modify(|w| w.set_boost(boost)); | 267 | T::regs().cr().modify(|w| w.set_boost(boost)); |
| 181 | } | 268 | } |
| 182 | let mut s = Self { adc }; | ||
| 183 | s.power_up(); | ||
| 184 | s.configure_differential_inputs(); | ||
| 185 | 269 | ||
| 186 | s.calibrate(); | ||
| 187 | blocking_delay_us(1); | ||
| 188 | |||
| 189 | s.enable(); | ||
| 190 | s.configure(); | ||
| 191 | |||
| 192 | s | ||
| 193 | } | ||
| 194 | |||
| 195 | fn power_up(&mut self) { | ||
| 196 | T::regs().cr().modify(|reg| { | 270 | T::regs().cr().modify(|reg| { |
| 197 | reg.set_deeppwd(false); | 271 | reg.set_deeppwd(false); |
| 198 | reg.set_advregen(true); | 272 | reg.set_advregen(true); |
| 199 | }); | 273 | }); |
| 200 | 274 | ||
| 201 | blocking_delay_us(10); | 275 | blocking_delay_us(10); |
| 202 | } | ||
| 203 | 276 | ||
| 204 | fn configure_differential_inputs(&mut self) { | ||
| 205 | T::regs().difsel().modify(|w| { | 277 | T::regs().difsel().modify(|w| { |
| 206 | for n in 0..20 { | 278 | for n in 0..20 { |
| 207 | w.set_difsel(n, Difsel::SINGLE_ENDED); | 279 | w.set_difsel(n, Difsel::SINGLE_ENDED); |
| 208 | } | 280 | } |
| 209 | }); | 281 | }); |
| 210 | } | ||
| 211 | 282 | ||
| 212 | fn calibrate(&mut self) { | ||
| 213 | T::regs().cr().modify(|w| { | 283 | T::regs().cr().modify(|w| { |
| 214 | #[cfg(not(adc_u5))] | 284 | #[cfg(not(adc_u5))] |
| 215 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); | 285 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); |
| @@ -219,21 +289,18 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 219 | T::regs().cr().modify(|w| w.set_adcal(true)); | 289 | T::regs().cr().modify(|w| w.set_adcal(true)); |
| 220 | 290 | ||
| 221 | while T::regs().cr().read().adcal() {} | 291 | while T::regs().cr().read().adcal() {} |
| 222 | } | ||
| 223 | 292 | ||
| 224 | fn enable(&mut self) { | 293 | blocking_delay_us(1); |
| 225 | T::regs().isr().write(|w| w.set_adrdy(true)); | 294 | |
| 226 | T::regs().cr().modify(|w| w.set_aden(true)); | 295 | T::enable(); |
| 227 | while !T::regs().isr().read().adrdy() {} | ||
| 228 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 229 | } | ||
| 230 | 296 | ||
| 231 | fn configure(&mut self) { | ||
| 232 | // single conversion mode, software trigger | 297 | // single conversion mode, software trigger |
| 233 | T::regs().cfgr().modify(|w| { | 298 | T::regs().cfgr().modify(|w| { |
| 234 | w.set_cont(false); | 299 | w.set_cont(false); |
| 235 | w.set_exten(Exten::DISABLED); | 300 | w.set_exten(Exten::DISABLED); |
| 236 | }); | 301 | }); |
| 302 | |||
| 303 | Self { adc } | ||
| 237 | } | 304 | } |
| 238 | 305 | ||
| 239 | /// Enable reading the voltage reference internal channel. | 306 | /// Enable reading the voltage reference internal channel. |
| @@ -262,218 +329,4 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 262 | 329 | ||
| 263 | Vbat {} | 330 | Vbat {} |
| 264 | } | 331 | } |
| 265 | |||
| 266 | /// Set the ADC resolution. | ||
| 267 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 268 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 269 | } | ||
| 270 | |||
| 271 | /// Set hardware averaging. | ||
| 272 | pub fn set_averaging(&mut self, averaging: Averaging) { | ||
| 273 | let (enable, samples, right_shift) = match averaging { | ||
| 274 | Averaging::Disabled => (false, 0, 0), | ||
| 275 | Averaging::Samples2 => (true, 1, 1), | ||
| 276 | Averaging::Samples4 => (true, 3, 2), | ||
| 277 | Averaging::Samples8 => (true, 7, 3), | ||
| 278 | Averaging::Samples16 => (true, 15, 4), | ||
| 279 | Averaging::Samples32 => (true, 31, 5), | ||
| 280 | Averaging::Samples64 => (true, 63, 6), | ||
| 281 | Averaging::Samples128 => (true, 127, 7), | ||
| 282 | Averaging::Samples256 => (true, 255, 8), | ||
| 283 | Averaging::Samples512 => (true, 511, 9), | ||
| 284 | Averaging::Samples1024 => (true, 1023, 10), | ||
| 285 | }; | ||
| 286 | |||
| 287 | T::regs().cfgr2().modify(|reg| { | ||
| 288 | reg.set_rovse(enable); | ||
| 289 | reg.set_ovsr(samples); | ||
| 290 | reg.set_ovss(right_shift); | ||
| 291 | }) | ||
| 292 | } | ||
| 293 | |||
| 294 | /// Perform a single conversion. | ||
| 295 | fn convert(&mut self) -> u16 { | ||
| 296 | T::regs().isr().modify(|reg| { | ||
| 297 | reg.set_eos(true); | ||
| 298 | reg.set_eoc(true); | ||
| 299 | }); | ||
| 300 | |||
| 301 | // Start conversion | ||
| 302 | T::regs().cr().modify(|reg| { | ||
| 303 | reg.set_adstart(true); | ||
| 304 | }); | ||
| 305 | |||
| 306 | while !T::regs().isr().read().eos() { | ||
| 307 | // spin | ||
| 308 | } | ||
| 309 | |||
| 310 | T::regs().dr().read().0 as u16 | ||
| 311 | } | ||
| 312 | |||
| 313 | /// Read an ADC channel. | ||
| 314 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 315 | self.read_channel(channel, sample_time) | ||
| 316 | } | ||
| 317 | |||
| 318 | /// Read one or multiple ADC channels using DMA. | ||
| 319 | /// | ||
| 320 | /// `sequence` iterator and `readings` must have the same length. | ||
| 321 | /// | ||
| 322 | /// Example | ||
| 323 | /// ```rust,ignore | ||
| 324 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 325 | /// | ||
| 326 | /// let mut adc = Adc::new(p.ADC1); | ||
| 327 | /// let mut adc_pin0 = p.PA0.into(); | ||
| 328 | /// let mut adc_pin2 = p.PA2.into(); | ||
| 329 | /// let mut measurements = [0u16; 2]; | ||
| 330 | /// | ||
| 331 | /// adc.read( | ||
| 332 | /// p.DMA2_CH0.reborrow(), | ||
| 333 | /// [ | ||
| 334 | /// (&mut *adc_pin0, SampleTime::CYCLES112), | ||
| 335 | /// (&mut *adc_pin2, SampleTime::CYCLES112), | ||
| 336 | /// ] | ||
| 337 | /// .into_iter(), | ||
| 338 | /// &mut measurements, | ||
| 339 | /// ) | ||
| 340 | /// .await; | ||
| 341 | /// defmt::info!("measurements: {}", measurements); | ||
| 342 | /// ``` | ||
| 343 | pub async fn read( | ||
| 344 | &mut self, | ||
| 345 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 346 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 347 | readings: &mut [u16], | ||
| 348 | ) { | ||
| 349 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 350 | assert!( | ||
| 351 | sequence.len() == readings.len(), | ||
| 352 | "Sequence length must be equal to readings length" | ||
| 353 | ); | ||
| 354 | assert!( | ||
| 355 | sequence.len() <= 16, | ||
| 356 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 357 | ); | ||
| 358 | |||
| 359 | // Ensure no conversions are ongoing | ||
| 360 | Self::cancel_conversions(); | ||
| 361 | |||
| 362 | // Set sequence length | ||
| 363 | T::regs().sqr1().modify(|w| { | ||
| 364 | w.set_l(sequence.len() as u8 - 1); | ||
| 365 | }); | ||
| 366 | |||
| 367 | // Configure channels and ranks | ||
| 368 | for (i, (channel, sample_time)) in sequence.enumerate() { | ||
| 369 | Self::configure_channel(channel, sample_time); | ||
| 370 | match i { | ||
| 371 | 0..=3 => { | ||
| 372 | T::regs().sqr1().modify(|w| { | ||
| 373 | w.set_sq(i, channel.channel()); | ||
| 374 | }); | ||
| 375 | } | ||
| 376 | 4..=8 => { | ||
| 377 | T::regs().sqr2().modify(|w| { | ||
| 378 | w.set_sq(i - 4, channel.channel()); | ||
| 379 | }); | ||
| 380 | } | ||
| 381 | 9..=13 => { | ||
| 382 | T::regs().sqr3().modify(|w| { | ||
| 383 | w.set_sq(i - 9, channel.channel()); | ||
| 384 | }); | ||
| 385 | } | ||
| 386 | 14..=15 => { | ||
| 387 | T::regs().sqr4().modify(|w| { | ||
| 388 | w.set_sq(i - 14, channel.channel()); | ||
| 389 | }); | ||
| 390 | } | ||
| 391 | _ => unreachable!(), | ||
| 392 | } | ||
| 393 | } | ||
| 394 | |||
| 395 | // Set continuous mode with oneshot dma. | ||
| 396 | // Clear overrun flag before starting transfer. | ||
| 397 | |||
| 398 | T::regs().isr().modify(|reg| { | ||
| 399 | reg.set_ovr(true); | ||
| 400 | }); | ||
| 401 | T::regs().cfgr().modify(|reg| { | ||
| 402 | reg.set_cont(true); | ||
| 403 | reg.set_dmngt(Dmngt::DMA_ONE_SHOT); | ||
| 404 | }); | ||
| 405 | |||
| 406 | let request = rx_dma.request(); | ||
| 407 | let transfer = unsafe { | ||
| 408 | Transfer::new_read( | ||
| 409 | rx_dma, | ||
| 410 | request, | ||
| 411 | T::regs().dr().as_ptr() as *mut u16, | ||
| 412 | readings, | ||
| 413 | Default::default(), | ||
| 414 | ) | ||
| 415 | }; | ||
| 416 | |||
| 417 | // Start conversion | ||
| 418 | T::regs().cr().modify(|reg| { | ||
| 419 | reg.set_adstart(true); | ||
| 420 | }); | ||
| 421 | |||
| 422 | // Wait for conversion sequence to finish. | ||
| 423 | transfer.await; | ||
| 424 | |||
| 425 | // Ensure conversions are finished. | ||
| 426 | Self::cancel_conversions(); | ||
| 427 | |||
| 428 | // Reset configuration. | ||
| 429 | T::regs().cfgr().modify(|reg| { | ||
| 430 | reg.set_cont(false); | ||
| 431 | reg.set_dmngt(Dmngt::from_bits(0)); | ||
| 432 | }); | ||
| 433 | } | ||
| 434 | |||
| 435 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | ||
| 436 | channel.setup(); | ||
| 437 | |||
| 438 | let channel = channel.channel(); | ||
| 439 | |||
| 440 | Self::set_channel_sample_time(channel, sample_time); | ||
| 441 | |||
| 442 | #[cfg(any(stm32h7, stm32u5))] | ||
| 443 | { | ||
| 444 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 445 | T::regs() | ||
| 446 | .pcsel() | ||
| 447 | .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); | ||
| 448 | } | ||
| 449 | } | ||
| 450 | |||
| 451 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 452 | Self::configure_channel(channel, sample_time); | ||
| 453 | |||
| 454 | T::regs().sqr1().modify(|reg| { | ||
| 455 | reg.set_sq(0, channel.channel()); | ||
| 456 | reg.set_l(0); | ||
| 457 | }); | ||
| 458 | |||
| 459 | self.convert() | ||
| 460 | } | ||
| 461 | |||
| 462 | fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | ||
| 463 | let sample_time = sample_time.into(); | ||
| 464 | if ch <= 9 { | ||
| 465 | T::regs().smpr(0).modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 466 | } else { | ||
| 467 | T::regs().smpr(1).modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 468 | } | ||
| 469 | } | ||
| 470 | |||
| 471 | fn cancel_conversions() { | ||
| 472 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 473 | T::regs().cr().modify(|reg| { | ||
| 474 | reg.set_adstp(Adstp::STOP); | ||
| 475 | }); | ||
| 476 | while T::regs().cr().read().adstart() {} | ||
| 477 | } | ||
| 478 | } | ||
| 479 | } | 332 | } |
diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index 90dbf4f09..b46ae2813 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs | |||
| @@ -8,7 +8,7 @@ use embassy_sync::waitqueue::AtomicWaker; | |||
| 8 | 8 | ||
| 9 | use super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; | 9 | use super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; |
| 10 | use super::word::{Word, WordSize}; | 10 | use super::word::{Word, WordSize}; |
| 11 | use super::{AnyChannel, Channel, Dir, Request, STATE}; | 11 | use super::{AnyChannel, BusyChannel, Channel, Dir, Request, STATE}; |
| 12 | use crate::interrupt::typelevel::Interrupt; | 12 | use crate::interrupt::typelevel::Interrupt; |
| 13 | use crate::{interrupt, pac}; | 13 | use crate::{interrupt, pac}; |
| 14 | 14 | ||
| @@ -602,7 +602,7 @@ impl AnyChannel { | |||
| 602 | /// DMA transfer. | 602 | /// DMA transfer. |
| 603 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 603 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 604 | pub struct Transfer<'a> { | 604 | pub struct Transfer<'a> { |
| 605 | channel: Peri<'a, AnyChannel>, | 605 | channel: BusyChannel<'a>, |
| 606 | } | 606 | } |
| 607 | 607 | ||
| 608 | impl<'a> Transfer<'a> { | 608 | impl<'a> Transfer<'a> { |
| @@ -713,7 +713,9 @@ impl<'a> Transfer<'a> { | |||
| 713 | _request, dir, peri_addr, mem_addr, mem_len, incr_mem, mem_size, peri_size, options, | 713 | _request, dir, peri_addr, mem_addr, mem_len, incr_mem, mem_size, peri_size, options, |
| 714 | ); | 714 | ); |
| 715 | channel.start(); | 715 | channel.start(); |
| 716 | Self { channel } | 716 | Self { |
| 717 | channel: BusyChannel::new(channel), | ||
| 718 | } | ||
| 717 | } | 719 | } |
| 718 | 720 | ||
| 719 | /// Request the transfer to pause, keeping the existing configuration for this channel. | 721 | /// Request the transfer to pause, keeping the existing configuration for this channel. |
| @@ -816,7 +818,7 @@ impl<'a> DmaCtrl for DmaCtrlImpl<'a> { | |||
| 816 | 818 | ||
| 817 | /// Ringbuffer for receiving data using DMA circular mode. | 819 | /// Ringbuffer for receiving data using DMA circular mode. |
| 818 | pub struct ReadableRingBuffer<'a, W: Word> { | 820 | pub struct ReadableRingBuffer<'a, W: Word> { |
| 819 | channel: Peri<'a, AnyChannel>, | 821 | channel: BusyChannel<'a>, |
| 820 | ringbuf: ReadableDmaRingBuffer<'a, W>, | 822 | ringbuf: ReadableDmaRingBuffer<'a, W>, |
| 821 | } | 823 | } |
| 822 | 824 | ||
| @@ -853,7 +855,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { | |||
| 853 | ); | 855 | ); |
| 854 | 856 | ||
| 855 | Self { | 857 | Self { |
| 856 | channel, | 858 | channel: BusyChannel::new(channel), |
| 857 | ringbuf: ReadableDmaRingBuffer::new(buffer), | 859 | ringbuf: ReadableDmaRingBuffer::new(buffer), |
| 858 | } | 860 | } |
| 859 | } | 861 | } |
| @@ -972,7 +974,7 @@ impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> { | |||
| 972 | 974 | ||
| 973 | /// Ringbuffer for writing data using DMA circular mode. | 975 | /// Ringbuffer for writing data using DMA circular mode. |
| 974 | pub struct WritableRingBuffer<'a, W: Word> { | 976 | pub struct WritableRingBuffer<'a, W: Word> { |
| 975 | channel: Peri<'a, AnyChannel>, | 977 | channel: BusyChannel<'a>, |
| 976 | ringbuf: WritableDmaRingBuffer<'a, W>, | 978 | ringbuf: WritableDmaRingBuffer<'a, W>, |
| 977 | } | 979 | } |
| 978 | 980 | ||
| @@ -1009,7 +1011,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { | |||
| 1009 | ); | 1011 | ); |
| 1010 | 1012 | ||
| 1011 | Self { | 1013 | Self { |
| 1012 | channel, | 1014 | channel: BusyChannel::new(channel), |
| 1013 | ringbuf: WritableDmaRingBuffer::new(buffer), | 1015 | ringbuf: WritableDmaRingBuffer::new(buffer), |
| 1014 | } | 1016 | } |
| 1015 | } | 1017 | } |
diff --git a/embassy-stm32/src/dma/gpdma/mod.rs b/embassy-stm32/src/dma/gpdma/mod.rs index 106558d20..383c74a78 100644 --- a/embassy-stm32/src/dma/gpdma/mod.rs +++ b/embassy-stm32/src/dma/gpdma/mod.rs | |||
| @@ -11,6 +11,7 @@ use linked_list::Table; | |||
| 11 | 11 | ||
| 12 | use super::word::{Word, WordSize}; | 12 | use super::word::{Word, WordSize}; |
| 13 | use super::{AnyChannel, Channel, Dir, Request, STATE}; | 13 | use super::{AnyChannel, Channel, Dir, Request, STATE}; |
| 14 | use crate::dma::BusyChannel; | ||
| 14 | use crate::interrupt::typelevel::Interrupt; | 15 | use crate::interrupt::typelevel::Interrupt; |
| 15 | use crate::pac; | 16 | use crate::pac; |
| 16 | use crate::pac::gpdma::vals; | 17 | use crate::pac::gpdma::vals; |
| @@ -408,7 +409,7 @@ impl AnyChannel { | |||
| 408 | /// Linked-list DMA transfer. | 409 | /// Linked-list DMA transfer. |
| 409 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 410 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 410 | pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> { | 411 | pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> { |
| 411 | channel: Peri<'a, AnyChannel>, | 412 | channel: BusyChannel<'a>, |
| 412 | } | 413 | } |
| 413 | 414 | ||
| 414 | impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { | 415 | impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { |
| @@ -429,7 +430,9 @@ impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { | |||
| 429 | channel.configure_linked_list(&table, options); | 430 | channel.configure_linked_list(&table, options); |
| 430 | channel.start(); | 431 | channel.start(); |
| 431 | 432 | ||
| 432 | Self { channel } | 433 | Self { |
| 434 | channel: BusyChannel::new(channel), | ||
| 435 | } | ||
| 433 | } | 436 | } |
| 434 | 437 | ||
| 435 | /// Request the transfer to pause, keeping the existing configuration for this channel. | 438 | /// Request the transfer to pause, keeping the existing configuration for this channel. |
| @@ -505,7 +508,7 @@ impl<'a, const ITEM_COUNT: usize> Future for LinkedListTransfer<'a, ITEM_COUNT> | |||
| 505 | /// DMA transfer. | 508 | /// DMA transfer. |
| 506 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 509 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 507 | pub struct Transfer<'a> { | 510 | pub struct Transfer<'a> { |
| 508 | channel: Peri<'a, AnyChannel>, | 511 | channel: BusyChannel<'a>, |
| 509 | } | 512 | } |
| 510 | 513 | ||
| 511 | impl<'a> Transfer<'a> { | 514 | impl<'a> Transfer<'a> { |
| @@ -625,7 +628,9 @@ impl<'a> Transfer<'a> { | |||
| 625 | ); | 628 | ); |
| 626 | channel.start(); | 629 | channel.start(); |
| 627 | 630 | ||
| 628 | Self { channel } | 631 | Self { |
| 632 | channel: BusyChannel::new(channel), | ||
| 633 | } | ||
| 629 | } | 634 | } |
| 630 | 635 | ||
| 631 | /// Request the transfer to pause, keeping the existing configuration for this channel. | 636 | /// Request the transfer to pause, keeping the existing configuration for this channel. |
diff --git a/embassy-stm32/src/dma/gpdma/ringbuffered.rs b/embassy-stm32/src/dma/gpdma/ringbuffered.rs index 94c597e0d..54e4d5f71 100644 --- a/embassy-stm32/src/dma/gpdma/ringbuffered.rs +++ b/embassy-stm32/src/dma/gpdma/ringbuffered.rs | |||
| @@ -12,7 +12,7 @@ use super::{AnyChannel, STATE, TransferOptions}; | |||
| 12 | use crate::dma::gpdma::linked_list::{RunMode, Table}; | 12 | use crate::dma::gpdma::linked_list::{RunMode, Table}; |
| 13 | use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; | 13 | use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; |
| 14 | use crate::dma::word::Word; | 14 | use crate::dma::word::Word; |
| 15 | use crate::dma::{Channel, Dir, Request}; | 15 | use crate::dma::{BusyChannel, Channel, Dir, Request}; |
| 16 | 16 | ||
| 17 | struct DmaCtrlImpl<'a>(Peri<'a, AnyChannel>); | 17 | struct DmaCtrlImpl<'a>(Peri<'a, AnyChannel>); |
| 18 | 18 | ||
| @@ -49,7 +49,7 @@ impl<'a> DmaCtrl for DmaCtrlImpl<'a> { | |||
| 49 | 49 | ||
| 50 | /// Ringbuffer for receiving data using GPDMA linked-list mode. | 50 | /// Ringbuffer for receiving data using GPDMA linked-list mode. |
| 51 | pub struct ReadableRingBuffer<'a, W: Word> { | 51 | pub struct ReadableRingBuffer<'a, W: Word> { |
| 52 | channel: Peri<'a, AnyChannel>, | 52 | channel: BusyChannel<'a>, |
| 53 | ringbuf: ReadableDmaRingBuffer<'a, W>, | 53 | ringbuf: ReadableDmaRingBuffer<'a, W>, |
| 54 | table: Table<2>, | 54 | table: Table<2>, |
| 55 | options: TransferOptions, | 55 | options: TransferOptions, |
| @@ -70,7 +70,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { | |||
| 70 | let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::PeripheralToMemory); | 70 | let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::PeripheralToMemory); |
| 71 | 71 | ||
| 72 | Self { | 72 | Self { |
| 73 | channel, | 73 | channel: BusyChannel::new(channel), |
| 74 | ringbuf: ReadableDmaRingBuffer::new(buffer), | 74 | ringbuf: ReadableDmaRingBuffer::new(buffer), |
| 75 | table, | 75 | table, |
| 76 | options, | 76 | options, |
| @@ -189,7 +189,7 @@ impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> { | |||
| 189 | 189 | ||
| 190 | /// Ringbuffer for writing data using GPDMA linked-list mode. | 190 | /// Ringbuffer for writing data using GPDMA linked-list mode. |
| 191 | pub struct WritableRingBuffer<'a, W: Word> { | 191 | pub struct WritableRingBuffer<'a, W: Word> { |
| 192 | channel: Peri<'a, AnyChannel>, | 192 | channel: BusyChannel<'a>, |
| 193 | ringbuf: WritableDmaRingBuffer<'a, W>, | 193 | ringbuf: WritableDmaRingBuffer<'a, W>, |
| 194 | table: Table<2>, | 194 | table: Table<2>, |
| 195 | options: TransferOptions, | 195 | options: TransferOptions, |
| @@ -210,7 +210,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { | |||
| 210 | let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::MemoryToPeripheral); | 210 | let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::MemoryToPeripheral); |
| 211 | 211 | ||
| 212 | Self { | 212 | Self { |
| 213 | channel, | 213 | channel: BusyChannel::new(channel), |
| 214 | ringbuf: WritableDmaRingBuffer::new(buffer), | 214 | ringbuf: WritableDmaRingBuffer::new(buffer), |
| 215 | table, | 215 | table, |
| 216 | options, | 216 | options, |
diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index de7a2c175..4becc2d87 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs | |||
| @@ -3,11 +3,14 @@ | |||
| 3 | 3 | ||
| 4 | #[cfg(any(bdma, dma))] | 4 | #[cfg(any(bdma, dma))] |
| 5 | mod dma_bdma; | 5 | mod dma_bdma; |
| 6 | use core::ops; | ||
| 7 | |||
| 6 | #[cfg(any(bdma, dma))] | 8 | #[cfg(any(bdma, dma))] |
| 7 | pub use dma_bdma::*; | 9 | pub use dma_bdma::*; |
| 8 | 10 | ||
| 9 | #[cfg(gpdma)] | 11 | #[cfg(gpdma)] |
| 10 | pub(crate) mod gpdma; | 12 | pub(crate) mod gpdma; |
| 13 | use embassy_hal_internal::Peri; | ||
| 11 | #[cfg(gpdma)] | 14 | #[cfg(gpdma)] |
| 12 | pub use gpdma::ringbuffered::*; | 15 | pub use gpdma::ringbuffered::*; |
| 13 | #[cfg(gpdma)] | 16 | #[cfg(gpdma)] |
| @@ -48,6 +51,8 @@ pub type Request = (); | |||
| 48 | pub(crate) trait SealedChannel { | 51 | pub(crate) trait SealedChannel { |
| 49 | #[cfg(not(stm32n6))] | 52 | #[cfg(not(stm32n6))] |
| 50 | fn id(&self) -> u8; | 53 | fn id(&self) -> u8; |
| 54 | #[cfg(feature = "low-power")] | ||
| 55 | fn stop_mode(&self) -> crate::rcc::StopMode; | ||
| 51 | } | 56 | } |
| 52 | 57 | ||
| 53 | #[cfg(not(stm32n6))] | 58 | #[cfg(not(stm32n6))] |
| @@ -62,15 +67,25 @@ pub trait Channel: SealedChannel + PeripheralType + Into<AnyChannel> + 'static { | |||
| 62 | 67 | ||
| 63 | #[cfg(not(stm32n6))] | 68 | #[cfg(not(stm32n6))] |
| 64 | macro_rules! dma_channel_impl { | 69 | macro_rules! dma_channel_impl { |
| 65 | ($channel_peri:ident, $index:expr) => { | 70 | ($channel_peri:ident, $index:expr, $stop_mode:ident) => { |
| 66 | impl crate::dma::SealedChannel for crate::peripherals::$channel_peri { | 71 | impl crate::dma::SealedChannel for crate::peripherals::$channel_peri { |
| 67 | fn id(&self) -> u8 { | 72 | fn id(&self) -> u8 { |
| 68 | $index | 73 | $index |
| 69 | } | 74 | } |
| 75 | |||
| 76 | #[cfg(feature = "low-power")] | ||
| 77 | fn stop_mode(&self) -> crate::rcc::StopMode { | ||
| 78 | crate::rcc::StopMode::$stop_mode | ||
| 79 | } | ||
| 70 | } | 80 | } |
| 71 | impl crate::dma::ChannelInterrupt for crate::peripherals::$channel_peri { | 81 | impl crate::dma::ChannelInterrupt for crate::peripherals::$channel_peri { |
| 72 | unsafe fn on_irq() { | 82 | unsafe fn on_irq() { |
| 73 | crate::dma::AnyChannel { id: $index }.on_irq(); | 83 | crate::dma::AnyChannel { |
| 84 | id: $index, | ||
| 85 | #[cfg(feature = "low-power")] | ||
| 86 | stop_mode: crate::rcc::StopMode::$stop_mode, | ||
| 87 | } | ||
| 88 | .on_irq(); | ||
| 74 | } | 89 | } |
| 75 | } | 90 | } |
| 76 | 91 | ||
| @@ -80,15 +95,57 @@ macro_rules! dma_channel_impl { | |||
| 80 | fn from(val: crate::peripherals::$channel_peri) -> Self { | 95 | fn from(val: crate::peripherals::$channel_peri) -> Self { |
| 81 | Self { | 96 | Self { |
| 82 | id: crate::dma::SealedChannel::id(&val), | 97 | id: crate::dma::SealedChannel::id(&val), |
| 98 | #[cfg(feature = "low-power")] | ||
| 99 | stop_mode: crate::dma::SealedChannel::stop_mode(&val), | ||
| 83 | } | 100 | } |
| 84 | } | 101 | } |
| 85 | } | 102 | } |
| 86 | }; | 103 | }; |
| 87 | } | 104 | } |
| 88 | 105 | ||
| 106 | pub(crate) struct BusyChannel<'a> { | ||
| 107 | channel: Peri<'a, AnyChannel>, | ||
| 108 | } | ||
| 109 | |||
| 110 | impl<'a> BusyChannel<'a> { | ||
| 111 | pub fn new(channel: Peri<'a, AnyChannel>) -> Self { | ||
| 112 | #[cfg(feature = "low-power")] | ||
| 113 | critical_section::with(|cs| { | ||
| 114 | crate::rcc::increment_stop_refcount(cs, channel.stop_mode); | ||
| 115 | }); | ||
| 116 | |||
| 117 | Self { channel } | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | impl<'a> Drop for BusyChannel<'a> { | ||
| 122 | fn drop(&mut self) { | ||
| 123 | #[cfg(feature = "low-power")] | ||
| 124 | critical_section::with(|cs| { | ||
| 125 | crate::rcc::decrement_stop_refcount(cs, self.stop_mode); | ||
| 126 | }); | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | impl<'a> ops::Deref for BusyChannel<'a> { | ||
| 131 | type Target = Peri<'a, AnyChannel>; | ||
| 132 | |||
| 133 | fn deref(&self) -> &Self::Target { | ||
| 134 | &self.channel | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | impl<'a> ops::DerefMut for BusyChannel<'a> { | ||
| 139 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 140 | &mut self.channel | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 89 | /// Type-erased DMA channel. | 144 | /// Type-erased DMA channel. |
| 90 | pub struct AnyChannel { | 145 | pub struct AnyChannel { |
| 91 | pub(crate) id: u8, | 146 | pub(crate) id: u8, |
| 147 | #[cfg(feature = "low-power")] | ||
| 148 | pub(crate) stop_mode: crate::rcc::StopMode, | ||
| 92 | } | 149 | } |
| 93 | impl_peripheral!(AnyChannel); | 150 | impl_peripheral!(AnyChannel); |
| 94 | 151 | ||
| @@ -103,6 +160,11 @@ impl SealedChannel for AnyChannel { | |||
| 103 | fn id(&self) -> u8 { | 160 | fn id(&self) -> u8 { |
| 104 | self.id | 161 | self.id |
| 105 | } | 162 | } |
| 163 | |||
| 164 | #[cfg(feature = "low-power")] | ||
| 165 | fn stop_mode(&self) -> crate::rcc::StopMode { | ||
| 166 | self.stop_mode | ||
| 167 | } | ||
| 106 | } | 168 | } |
| 107 | impl Channel for AnyChannel {} | 169 | impl Channel for AnyChannel {} |
| 108 | 170 | ||
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/eth/generic_phy.rs b/embassy-stm32/src/eth/generic_phy.rs index 774beef80..0a5f41de0 100644 --- a/embassy-stm32/src/eth/generic_phy.rs +++ b/embassy-stm32/src/eth/generic_phy.rs | |||
| @@ -8,6 +8,7 @@ use embassy_time::{Duration, Timer}; | |||
| 8 | use futures_util::FutureExt; | 8 | use futures_util::FutureExt; |
| 9 | 9 | ||
| 10 | use super::{Phy, StationManagement}; | 10 | use super::{Phy, StationManagement}; |
| 11 | use crate::block_for_us as blocking_delay_us; | ||
| 11 | 12 | ||
| 12 | #[allow(dead_code)] | 13 | #[allow(dead_code)] |
| 13 | mod phy_consts { | 14 | mod phy_consts { |
| @@ -43,21 +44,23 @@ mod phy_consts { | |||
| 43 | use self::phy_consts::*; | 44 | use self::phy_consts::*; |
| 44 | 45 | ||
| 45 | /// Generic SMI Ethernet PHY implementation | 46 | /// Generic SMI Ethernet PHY implementation |
| 46 | pub struct GenericPhy { | 47 | pub struct GenericPhy<SM: StationManagement> { |
| 47 | phy_addr: u8, | 48 | phy_addr: u8, |
| 49 | sm: SM, | ||
| 48 | #[cfg(feature = "time")] | 50 | #[cfg(feature = "time")] |
| 49 | poll_interval: Duration, | 51 | poll_interval: Duration, |
| 50 | } | 52 | } |
| 51 | 53 | ||
| 52 | impl GenericPhy { | 54 | impl<SM: StationManagement> GenericPhy<SM> { |
| 53 | /// Construct the PHY. It assumes the address `phy_addr` in the SMI communication | 55 | /// Construct the PHY. It assumes the address `phy_addr` in the SMI communication |
| 54 | /// | 56 | /// |
| 55 | /// # Panics | 57 | /// # Panics |
| 56 | /// `phy_addr` must be in range `0..32` | 58 | /// `phy_addr` must be in range `0..32` |
| 57 | pub fn new(phy_addr: u8) -> Self { | 59 | pub fn new(sm: SM, phy_addr: u8) -> Self { |
| 58 | assert!(phy_addr < 32); | 60 | assert!(phy_addr < 32); |
| 59 | Self { | 61 | Self { |
| 60 | phy_addr, | 62 | phy_addr, |
| 63 | sm, | ||
| 61 | #[cfg(feature = "time")] | 64 | #[cfg(feature = "time")] |
| 62 | poll_interval: Duration::from_millis(500), | 65 | poll_interval: Duration::from_millis(500), |
| 63 | } | 66 | } |
| @@ -67,8 +70,9 @@ impl GenericPhy { | |||
| 67 | /// | 70 | /// |
| 68 | /// # Panics | 71 | /// # Panics |
| 69 | /// Initialization panics if PHY didn't respond on any address | 72 | /// Initialization panics if PHY didn't respond on any address |
| 70 | pub fn new_auto() -> Self { | 73 | pub fn new_auto(sm: SM) -> Self { |
| 71 | Self { | 74 | Self { |
| 75 | sm, | ||
| 72 | phy_addr: 0xFF, | 76 | phy_addr: 0xFF, |
| 73 | #[cfg(feature = "time")] | 77 | #[cfg(feature = "time")] |
| 74 | poll_interval: Duration::from_millis(500), | 78 | poll_interval: Duration::from_millis(500), |
| @@ -76,27 +80,14 @@ impl GenericPhy { | |||
| 76 | } | 80 | } |
| 77 | } | 81 | } |
| 78 | 82 | ||
| 79 | // TODO: Factor out to shared functionality | 83 | impl<SM: StationManagement> Phy for GenericPhy<SM> { |
| 80 | fn blocking_delay_us(us: u32) { | 84 | fn phy_reset(&mut self) { |
| 81 | #[cfg(feature = "time")] | ||
| 82 | embassy_time::block_for(Duration::from_micros(us as u64)); | ||
| 83 | #[cfg(not(feature = "time"))] | ||
| 84 | { | ||
| 85 | let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64; | ||
| 86 | let us = us as u64; | ||
| 87 | let cycles = freq * us / 1_000_000; | ||
| 88 | cortex_m::asm::delay(cycles as u32); | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | impl Phy for GenericPhy { | ||
| 93 | fn phy_reset<S: StationManagement>(&mut self, sm: &mut S) { | ||
| 94 | // Detect SMI address | 85 | // Detect SMI address |
| 95 | if self.phy_addr == 0xFF { | 86 | if self.phy_addr == 0xFF { |
| 96 | for addr in 0..32 { | 87 | for addr in 0..32 { |
| 97 | sm.smi_write(addr, PHY_REG_BCR, PHY_REG_BCR_RESET); | 88 | self.sm.smi_write(addr, PHY_REG_BCR, PHY_REG_BCR_RESET); |
| 98 | for _ in 0..10 { | 89 | for _ in 0..10 { |
| 99 | if sm.smi_read(addr, PHY_REG_BCR) & PHY_REG_BCR_RESET != PHY_REG_BCR_RESET { | 90 | if self.sm.smi_read(addr, PHY_REG_BCR) & PHY_REG_BCR_RESET != PHY_REG_BCR_RESET { |
| 100 | trace!("Found ETH PHY on address {}", addr); | 91 | trace!("Found ETH PHY on address {}", addr); |
| 101 | self.phy_addr = addr; | 92 | self.phy_addr = addr; |
| 102 | return; | 93 | return; |
| @@ -108,30 +99,30 @@ impl Phy for GenericPhy { | |||
| 108 | panic!("PHY did not respond"); | 99 | panic!("PHY did not respond"); |
| 109 | } | 100 | } |
| 110 | 101 | ||
| 111 | sm.smi_write(self.phy_addr, PHY_REG_BCR, PHY_REG_BCR_RESET); | 102 | self.sm.smi_write(self.phy_addr, PHY_REG_BCR, PHY_REG_BCR_RESET); |
| 112 | while sm.smi_read(self.phy_addr, PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {} | 103 | while self.sm.smi_read(self.phy_addr, PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {} |
| 113 | } | 104 | } |
| 114 | 105 | ||
| 115 | fn phy_init<S: StationManagement>(&mut self, sm: &mut S) { | 106 | fn phy_init(&mut self) { |
| 116 | // Clear WU CSR | 107 | // Clear WU CSR |
| 117 | self.smi_write_ext(sm, PHY_REG_WUCSR, 0); | 108 | self.smi_write_ext(PHY_REG_WUCSR, 0); |
| 118 | 109 | ||
| 119 | // Enable auto-negotiation | 110 | // Enable auto-negotiation |
| 120 | sm.smi_write( | 111 | self.sm.smi_write( |
| 121 | self.phy_addr, | 112 | self.phy_addr, |
| 122 | PHY_REG_BCR, | 113 | PHY_REG_BCR, |
| 123 | PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M, | 114 | PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M, |
| 124 | ); | 115 | ); |
| 125 | } | 116 | } |
| 126 | 117 | ||
| 127 | fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool { | 118 | fn poll_link(&mut self, cx: &mut Context) -> bool { |
| 128 | #[cfg(not(feature = "time"))] | 119 | #[cfg(not(feature = "time"))] |
| 129 | cx.waker().wake_by_ref(); | 120 | cx.waker().wake_by_ref(); |
| 130 | 121 | ||
| 131 | #[cfg(feature = "time")] | 122 | #[cfg(feature = "time")] |
| 132 | let _ = Timer::after(self.poll_interval).poll_unpin(cx); | 123 | let _ = Timer::after(self.poll_interval).poll_unpin(cx); |
| 133 | 124 | ||
| 134 | let bsr = sm.smi_read(self.phy_addr, PHY_REG_BSR); | 125 | let bsr = self.sm.smi_read(self.phy_addr, PHY_REG_BSR); |
| 135 | 126 | ||
| 136 | // No link without autonegotiate | 127 | // No link without autonegotiate |
| 137 | if bsr & PHY_REG_BSR_ANDONE == 0 { | 128 | if bsr & PHY_REG_BSR_ANDONE == 0 { |
| @@ -148,7 +139,7 @@ impl Phy for GenericPhy { | |||
| 148 | } | 139 | } |
| 149 | 140 | ||
| 150 | /// Public functions for the PHY | 141 | /// Public functions for the PHY |
| 151 | impl GenericPhy { | 142 | impl<SM: StationManagement> GenericPhy<SM> { |
| 152 | /// Set the SMI polling interval. | 143 | /// Set the SMI polling interval. |
| 153 | #[cfg(feature = "time")] | 144 | #[cfg(feature = "time")] |
| 154 | pub fn set_poll_interval(&mut self, poll_interval: Duration) { | 145 | pub fn set_poll_interval(&mut self, poll_interval: Duration) { |
| @@ -156,10 +147,15 @@ impl GenericPhy { | |||
| 156 | } | 147 | } |
| 157 | 148 | ||
| 158 | // Writes a value to an extended PHY register in MMD address space | 149 | // Writes a value to an extended PHY register in MMD address space |
| 159 | fn smi_write_ext<S: StationManagement>(&mut self, sm: &mut S, reg_addr: u16, reg_data: u16) { | 150 | fn smi_write_ext(&mut self, reg_addr: u16, reg_data: u16) { |
| 160 | sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x0003); // set address | 151 | self.sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x0003); // set address |
| 161 | sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_addr); | 152 | self.sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_addr); |
| 162 | sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x4003); // set data | 153 | self.sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x4003); // set data |
| 163 | sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_data); | 154 | self.sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_data); |
| 155 | } | ||
| 156 | |||
| 157 | /// Access the underlying station management. | ||
| 158 | pub fn station_management(&mut self) -> &mut SM { | ||
| 159 | &mut self.sm | ||
| 164 | } | 160 | } |
| 165 | } | 161 | } |
diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 10b3a0517..c8bce0e8a 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #[cfg_attr(eth_v2, path = "v2/mod.rs")] | 5 | #[cfg_attr(eth_v2, path = "v2/mod.rs")] |
| 6 | mod _version; | 6 | mod _version; |
| 7 | mod generic_phy; | 7 | mod generic_phy; |
| 8 | mod sma; | ||
| 8 | 9 | ||
| 9 | use core::mem::MaybeUninit; | 10 | use core::mem::MaybeUninit; |
| 10 | use core::task::Context; | 11 | use core::task::Context; |
| @@ -15,6 +16,7 @@ use embassy_sync::waitqueue::AtomicWaker; | |||
| 15 | 16 | ||
| 16 | pub use self::_version::{InterruptHandler, *}; | 17 | pub use self::_version::{InterruptHandler, *}; |
| 17 | pub use self::generic_phy::*; | 18 | pub use self::generic_phy::*; |
| 19 | pub use self::sma::{Sma, StationManagement}; | ||
| 18 | use crate::rcc::RccPeripheral; | 20 | use crate::rcc::RccPeripheral; |
| 19 | 21 | ||
| 20 | #[allow(unused)] | 22 | #[allow(unused)] |
| @@ -109,7 +111,7 @@ impl<'d, T: Instance, P: Phy> embassy_net_driver::Driver for Ethernet<'d, T, P> | |||
| 109 | } | 111 | } |
| 110 | 112 | ||
| 111 | fn link_state(&mut self, cx: &mut Context) -> LinkState { | 113 | fn link_state(&mut self, cx: &mut Context) -> LinkState { |
| 112 | if self.phy.poll_link(&mut self.station_management, cx) { | 114 | if self.phy.poll_link(cx) { |
| 113 | LinkState::Up | 115 | LinkState::Up |
| 114 | } else { | 116 | } else { |
| 115 | LinkState::Down | 117 | LinkState::Down |
| @@ -157,32 +159,17 @@ impl<'a, 'd> embassy_net_driver::TxToken for TxToken<'a, 'd> { | |||
| 157 | } | 159 | } |
| 158 | } | 160 | } |
| 159 | 161 | ||
| 160 | /// Station Management Interface (SMI) on an ethernet PHY | ||
| 161 | pub trait StationManagement { | ||
| 162 | /// Read a register over SMI. | ||
| 163 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16; | ||
| 164 | /// Write a register over SMI. | ||
| 165 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16); | ||
| 166 | } | ||
| 167 | |||
| 168 | /// Trait for an Ethernet PHY | 162 | /// Trait for an Ethernet PHY |
| 169 | pub trait Phy { | 163 | pub trait Phy { |
| 170 | /// Reset PHY and wait for it to come out of reset. | 164 | /// Reset PHY and wait for it to come out of reset. |
| 171 | fn phy_reset<S: StationManagement>(&mut self, sm: &mut S); | 165 | fn phy_reset(&mut self); |
| 172 | /// PHY initialisation. | 166 | /// PHY initialisation. |
| 173 | fn phy_init<S: StationManagement>(&mut self, sm: &mut S); | 167 | fn phy_init(&mut self); |
| 174 | /// Poll link to see if it is up and FD with 100Mbps | 168 | /// Poll link to see if it is up and FD with 100Mbps |
| 175 | fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool; | 169 | fn poll_link(&mut self, cx: &mut Context) -> bool; |
| 176 | } | 170 | } |
| 177 | 171 | ||
| 178 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | 172 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { |
| 179 | /// Directly expose the SMI interface used by the Ethernet driver. | ||
| 180 | /// | ||
| 181 | /// This can be used to for example configure special PHY registers for compliance testing. | ||
| 182 | pub fn station_management(&mut self) -> &mut impl StationManagement { | ||
| 183 | &mut self.station_management | ||
| 184 | } | ||
| 185 | |||
| 186 | /// Access the user-supplied `Phy`. | 173 | /// Access the user-supplied `Phy`. |
| 187 | pub fn phy(&self) -> &P { | 174 | pub fn phy(&self) -> &P { |
| 188 | &self.phy | 175 | &self.phy |
| @@ -212,8 +199,8 @@ impl Instance for crate::peripherals::ETH {} | |||
| 212 | pin_trait!(RXClkPin, Instance, @A); | 199 | pin_trait!(RXClkPin, Instance, @A); |
| 213 | pin_trait!(TXClkPin, Instance, @A); | 200 | pin_trait!(TXClkPin, Instance, @A); |
| 214 | pin_trait!(RefClkPin, Instance, @A); | 201 | pin_trait!(RefClkPin, Instance, @A); |
| 215 | pin_trait!(MDIOPin, Instance, @A); | 202 | pin_trait!(MDIOPin, sma::Instance, @A); |
| 216 | pin_trait!(MDCPin, Instance, @A); | 203 | pin_trait!(MDCPin, sma::Instance, @A); |
| 217 | pin_trait!(RXDVPin, Instance, @A); | 204 | pin_trait!(RXDVPin, Instance, @A); |
| 218 | pin_trait!(CRSPin, Instance, @A); | 205 | pin_trait!(CRSPin, Instance, @A); |
| 219 | pin_trait!(RXD0Pin, Instance, @A); | 206 | pin_trait!(RXD0Pin, Instance, @A); |
diff --git a/embassy-stm32/src/eth/sma/mod.rs b/embassy-stm32/src/eth/sma/mod.rs new file mode 100644 index 000000000..6c851911d --- /dev/null +++ b/embassy-stm32/src/eth/sma/mod.rs | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | //! Station Management Agent (also known as MDIO or SMI). | ||
| 2 | |||
| 3 | #![macro_use] | ||
| 4 | |||
| 5 | #[cfg_attr(eth_v2, path = "v2.rs")] | ||
| 6 | #[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1.rs")] | ||
| 7 | mod _version; | ||
| 8 | |||
| 9 | use embassy_hal_internal::PeripheralType; | ||
| 10 | use stm32_metapac::common::{RW, Reg}; | ||
| 11 | |||
| 12 | pub use self::_version::*; | ||
| 13 | |||
| 14 | /// Station Management Interface (SMI). | ||
| 15 | pub trait StationManagement { | ||
| 16 | /// Read a register over SMI. | ||
| 17 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16; | ||
| 18 | /// Write a register over SMI. | ||
| 19 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16); | ||
| 20 | } | ||
| 21 | |||
| 22 | trait SealedInstance { | ||
| 23 | fn regs() -> (Reg<AddressRegister, RW>, Reg<DataRegister, RW>); | ||
| 24 | } | ||
| 25 | |||
| 26 | /// MDIO instance. | ||
| 27 | #[allow(private_bounds)] | ||
| 28 | pub trait Instance: SealedInstance + PeripheralType + Send + 'static {} | ||
| 29 | |||
| 30 | impl SealedInstance for crate::peripherals::ETH_SMA { | ||
| 31 | fn regs() -> (Reg<AddressRegister, RW>, Reg<DataRegister, RW>) { | ||
| 32 | let mac = crate::pac::ETH.ethernet_mac(); | ||
| 33 | |||
| 34 | #[cfg(any(eth_v1a, eth_v1b, eth_v1c))] | ||
| 35 | return (mac.macmiiar(), mac.macmiidr()); | ||
| 36 | |||
| 37 | #[cfg(eth_v2)] | ||
| 38 | return (mac.macmdioar(), mac.macmdiodr()); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | impl Instance for crate::peripherals::ETH_SMA {} | ||
diff --git a/embassy-stm32/src/eth/sma/v1.rs b/embassy-stm32/src/eth/sma/v1.rs new file mode 100644 index 000000000..db64a6c78 --- /dev/null +++ b/embassy-stm32/src/eth/sma/v1.rs | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | use embassy_hal_internal::Peri; | ||
| 2 | pub(crate) use regs::{Macmiiar as AddressRegister, Macmiidr as DataRegister}; | ||
| 3 | use stm32_metapac::eth::regs; | ||
| 4 | use stm32_metapac::eth::vals::{Cr, MbProgress, Mw}; | ||
| 5 | |||
| 6 | use super::{Instance, StationManagement}; | ||
| 7 | use crate::eth::{MDCPin, MDIOPin}; | ||
| 8 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; | ||
| 9 | |||
| 10 | /// Station Management Agent. | ||
| 11 | /// | ||
| 12 | /// This peripheral is used for SMI reads and writes to the connected | ||
| 13 | /// ethernet PHY/device(s). | ||
| 14 | pub struct Sma<'d, T: Instance> { | ||
| 15 | _peri: Peri<'d, T>, | ||
| 16 | clock_range: Cr, | ||
| 17 | pins: [Peri<'d, AnyPin>; 2], | ||
| 18 | } | ||
| 19 | |||
| 20 | impl<'d, T: Instance> Sma<'d, T> { | ||
| 21 | /// Create a new instance of this peripheral. | ||
| 22 | pub fn new<#[cfg(afio)] A>( | ||
| 23 | peri: Peri<'d, T>, | ||
| 24 | mdio: Peri<'d, if_afio!(impl MDIOPin<T, A>)>, | ||
| 25 | mdc: Peri<'d, if_afio!(impl MDCPin<T, A>)>, | ||
| 26 | ) -> Self { | ||
| 27 | set_as_af!(mdio, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 28 | set_as_af!(mdc, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 29 | |||
| 30 | // Enable necessary clocks. | ||
| 31 | critical_section::with(|_| { | ||
| 32 | #[cfg(eth_v1a)] | ||
| 33 | let reg = crate::pac::RCC.ahbenr(); | ||
| 34 | |||
| 35 | #[cfg(any(eth_v1b, eth_v1c))] | ||
| 36 | let reg = crate::pac::RCC.ahb1enr(); | ||
| 37 | |||
| 38 | reg.modify(|w| { | ||
| 39 | w.set_ethen(true); | ||
| 40 | }) | ||
| 41 | }); | ||
| 42 | |||
| 43 | let hclk = unsafe { crate::rcc::get_freqs().hclk1.to_hertz() }; | ||
| 44 | let hclk = unwrap!(hclk, "SMA requires HCLK to be enabled, but it was not."); | ||
| 45 | let hclk_mhz = hclk.0 / 1_000_000; | ||
| 46 | |||
| 47 | // Set the MDC clock frequency in the range 1MHz - 2.5MHz | ||
| 48 | let clock_range = match hclk_mhz { | ||
| 49 | 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."), | ||
| 50 | 25..=34 => Cr::CR_20_35, // Divide by 16 | ||
| 51 | 35..=59 => Cr::CR_35_60, // Divide by 26 | ||
| 52 | 60..=99 => Cr::CR_60_100, // Divide by 42 | ||
| 53 | 100..=149 => Cr::CR_100_150, // Divide by 62 | ||
| 54 | 150..=216 => Cr::CR_150_168, // Divide by 102 | ||
| 55 | _ => { | ||
| 56 | panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") | ||
| 57 | } | ||
| 58 | }; | ||
| 59 | |||
| 60 | Self { | ||
| 61 | _peri: peri, | ||
| 62 | clock_range, | ||
| 63 | pins: [mdio.into(), mdc.into()], | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | impl<T: Instance> StationManagement for Sma<'_, T> { | ||
| 69 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { | ||
| 70 | let (macmiiar, macmiidr) = T::regs(); | ||
| 71 | |||
| 72 | macmiiar.modify(|w| { | ||
| 73 | w.set_pa(phy_addr); | ||
| 74 | w.set_mr(reg); | ||
| 75 | w.set_mw(Mw::READ); // read operation | ||
| 76 | w.set_cr(self.clock_range); | ||
| 77 | w.set_mb(MbProgress::BUSY); // indicate that operation is in progress | ||
| 78 | }); | ||
| 79 | while macmiiar.read().mb() == MbProgress::BUSY {} | ||
| 80 | macmiidr.read().md() | ||
| 81 | } | ||
| 82 | |||
| 83 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { | ||
| 84 | let (macmiiar, macmiidr) = T::regs(); | ||
| 85 | |||
| 86 | macmiidr.write(|w| w.set_md(val)); | ||
| 87 | macmiiar.modify(|w| { | ||
| 88 | w.set_pa(phy_addr); | ||
| 89 | w.set_mr(reg); | ||
| 90 | w.set_mw(Mw::WRITE); // write | ||
| 91 | w.set_cr(self.clock_range); | ||
| 92 | w.set_mb(MbProgress::BUSY); | ||
| 93 | }); | ||
| 94 | while macmiiar.read().mb() == MbProgress::BUSY {} | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | impl<T: Instance> Drop for Sma<'_, T> { | ||
| 99 | fn drop(&mut self) { | ||
| 100 | self.pins.iter_mut().for_each(|p| p.set_as_disconnected()); | ||
| 101 | } | ||
| 102 | } | ||
diff --git a/embassy-stm32/src/eth/sma/v2.rs b/embassy-stm32/src/eth/sma/v2.rs new file mode 100644 index 000000000..6bc5230b5 --- /dev/null +++ b/embassy-stm32/src/eth/sma/v2.rs | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | use embassy_hal_internal::Peri; | ||
| 2 | pub(crate) use regs::{Macmdioar as AddressRegister, Macmdiodr as DataRegister}; | ||
| 3 | use stm32_metapac::eth::regs; | ||
| 4 | |||
| 5 | use super::{Instance, StationManagement}; | ||
| 6 | use crate::eth::{MDCPin, MDIOPin}; | ||
| 7 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; | ||
| 8 | |||
| 9 | /// Station Management Agent. | ||
| 10 | /// | ||
| 11 | /// This peripheral is used for SMI reads and writes to the connected | ||
| 12 | /// ethernet PHY/device(s). | ||
| 13 | pub struct Sma<'d, T: Instance> { | ||
| 14 | _peri: Peri<'d, T>, | ||
| 15 | pins: [Peri<'d, AnyPin>; 2], | ||
| 16 | clock_range: u8, | ||
| 17 | } | ||
| 18 | |||
| 19 | impl<'d, T: Instance> Sma<'d, T> { | ||
| 20 | /// Create a new instance of this peripheral. | ||
| 21 | pub fn new(peri: Peri<'d, T>, mdio: Peri<'d, impl MDIOPin<T>>, mdc: Peri<'d, impl MDCPin<T>>) -> Self { | ||
| 22 | set_as_af!(mdio, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 23 | set_as_af!(mdc, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 24 | |||
| 25 | // Enable necessary clocks. | ||
| 26 | critical_section::with(|_| { | ||
| 27 | crate::pac::RCC.ahb1enr().modify(|w| { | ||
| 28 | w.set_ethen(true); | ||
| 29 | }) | ||
| 30 | }); | ||
| 31 | |||
| 32 | let hclk = unsafe { crate::rcc::get_freqs().hclk1.to_hertz() }; | ||
| 33 | let hclk = unwrap!(hclk, "SMA requires HCLK to be enabled, but it was not."); | ||
| 34 | let hclk_mhz = hclk.0 / 1_000_000; | ||
| 35 | |||
| 36 | // Set the MDC clock frequency in the range 1MHz - 2.5MHz | ||
| 37 | let clock_range = match hclk_mhz { | ||
| 38 | 0..=34 => 2, // Divide by 16 | ||
| 39 | 35..=59 => 3, // Divide by 26 | ||
| 40 | 60..=99 => 0, // Divide by 42 | ||
| 41 | 100..=149 => 1, // Divide by 62 | ||
| 42 | 150..=249 => 4, // Divide by 102 | ||
| 43 | 250..=310 => 5, // Divide by 124 | ||
| 44 | _ => { | ||
| 45 | panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") | ||
| 46 | } | ||
| 47 | }; | ||
| 48 | |||
| 49 | Self { | ||
| 50 | _peri: peri, | ||
| 51 | clock_range, | ||
| 52 | pins: [mdio.into(), mdc.into()], | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | impl<T: Instance> StationManagement for Sma<'_, T> { | ||
| 58 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { | ||
| 59 | let (macmdioar, macmdiodr) = T::regs(); | ||
| 60 | |||
| 61 | macmdioar.modify(|w| { | ||
| 62 | w.set_pa(phy_addr); | ||
| 63 | w.set_rda(reg); | ||
| 64 | w.set_goc(0b11); // read | ||
| 65 | w.set_cr(self.clock_range); | ||
| 66 | w.set_mb(true); | ||
| 67 | }); | ||
| 68 | |||
| 69 | while macmdioar.read().mb() {} | ||
| 70 | |||
| 71 | macmdiodr.read().md() | ||
| 72 | } | ||
| 73 | |||
| 74 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { | ||
| 75 | let (macmdioar, macmdiodr) = T::regs(); | ||
| 76 | |||
| 77 | macmdiodr.write(|w| w.set_md(val)); | ||
| 78 | macmdioar.modify(|w| { | ||
| 79 | w.set_pa(phy_addr); | ||
| 80 | w.set_rda(reg); | ||
| 81 | w.set_goc(0b01); // write | ||
| 82 | w.set_cr(self.clock_range); | ||
| 83 | w.set_mb(true); | ||
| 84 | }); | ||
| 85 | |||
| 86 | while macmdioar.read().mb() {} | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | impl<T: Instance> Drop for Sma<'_, T> { | ||
| 91 | fn drop(&mut self) { | ||
| 92 | self.pins.iter_mut().for_each(|p| p.set_as_disconnected()); | ||
| 93 | } | ||
| 94 | } | ||
diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index a77eb8719..8de26ce9d 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs | |||
| @@ -3,11 +3,10 @@ | |||
| 3 | mod rx_desc; | 3 | mod rx_desc; |
| 4 | mod tx_desc; | 4 | mod tx_desc; |
| 5 | 5 | ||
| 6 | use core::marker::PhantomData; | ||
| 7 | use core::sync::atomic::{Ordering, fence}; | 6 | use core::sync::atomic::{Ordering, fence}; |
| 8 | 7 | ||
| 9 | use embassy_hal_internal::Peri; | 8 | use embassy_hal_internal::Peri; |
| 10 | use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; | 9 | use stm32_metapac::eth::vals::{Apcs, Dm, DmaomrSr, Fes, Ftf, Ifg, Pbl, Rsf, St, Tsf}; |
| 11 | 10 | ||
| 12 | pub(crate) use self::rx_desc::{RDes, RDesRing}; | 11 | pub(crate) use self::rx_desc::{RDes, RDesRing}; |
| 13 | pub(crate) use self::tx_desc::{TDes, TDesRing}; | 12 | pub(crate) use self::tx_desc::{TDes, TDesRing}; |
| @@ -22,7 +21,6 @@ use crate::pac::AFIO; | |||
| 22 | #[cfg(any(eth_v1b, eth_v1c))] | 21 | #[cfg(any(eth_v1b, eth_v1c))] |
| 23 | use crate::pac::SYSCFG; | 22 | use crate::pac::SYSCFG; |
| 24 | use crate::pac::{ETH, RCC}; | 23 | use crate::pac::{ETH, RCC}; |
| 25 | use crate::rcc::SealedRccPeripheral; | ||
| 26 | 24 | ||
| 27 | /// Interrupt handler. | 25 | /// Interrupt handler. |
| 28 | pub struct InterruptHandler {} | 26 | pub struct InterruptHandler {} |
| @@ -53,14 +51,13 @@ pub struct Ethernet<'d, T: Instance, P: Phy> { | |||
| 53 | 51 | ||
| 54 | pins: Pins<'d>, | 52 | pins: Pins<'d>, |
| 55 | pub(crate) phy: P, | 53 | pub(crate) phy: P, |
| 56 | pub(crate) station_management: EthernetStationManagement<T>, | ||
| 57 | pub(crate) mac_addr: [u8; 6], | 54 | pub(crate) mac_addr: [u8; 6], |
| 58 | } | 55 | } |
| 59 | 56 | ||
| 60 | /// Pins of ethernet driver. | 57 | /// Pins of ethernet driver. |
| 61 | enum Pins<'d> { | 58 | enum Pins<'d> { |
| 62 | Rmii([Peri<'d, AnyPin>; 9]), | 59 | Rmii([Peri<'d, AnyPin>; 7]), |
| 63 | Mii([Peri<'d, AnyPin>; 14]), | 60 | Mii([Peri<'d, AnyPin>; 12]), |
| 64 | } | 61 | } |
| 65 | 62 | ||
| 66 | #[cfg(eth_v1a)] | 63 | #[cfg(eth_v1a)] |
| @@ -97,68 +94,105 @@ macro_rules! config_pins { | |||
| 97 | }; | 94 | }; |
| 98 | } | 95 | } |
| 99 | 96 | ||
| 100 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | 97 | impl<'d, T: Instance, SMA: sma::Instance> Ethernet<'d, T, GenericPhy<Sma<'d, SMA>>> { |
| 98 | /// Create a new RMII ethernet driver using 7 pins. | ||
| 99 | /// | ||
| 100 | /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the | ||
| 101 | /// provided [`SMA`](sma::Instance), and MDIO and MDC pins. | ||
| 102 | /// | ||
| 103 | /// See [`Ethernet::new_with_phy`] for creating an RMII ethernet | ||
| 104 | /// river with a non-standard PHY. | ||
| 105 | /// | ||
| 101 | /// safety: the returned instance is not leak-safe | 106 | /// safety: the returned instance is not leak-safe |
| 102 | pub fn new<const TX: usize, const RX: usize, #[cfg(afio)] A>( | 107 | pub fn new<const TX: usize, const RX: usize, #[cfg(afio)] A>( |
| 103 | queue: &'d mut PacketQueue<TX, RX>, | 108 | queue: &'d mut PacketQueue<TX, RX>, |
| 104 | peri: Peri<'d, T>, | 109 | peri: Peri<'d, T>, |
| 105 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | 110 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, |
| 106 | ref_clk: Peri<'d, if_afio!(impl RefClkPin<T, A>)>, | 111 | ref_clk: Peri<'d, if_afio!(impl RefClkPin<T, A>)>, |
| 107 | mdio: Peri<'d, if_afio!(impl MDIOPin<T, A>)>, | ||
| 108 | mdc: Peri<'d, if_afio!(impl MDCPin<T, A>)>, | ||
| 109 | crs: Peri<'d, if_afio!(impl CRSPin<T, A>)>, | 112 | crs: Peri<'d, if_afio!(impl CRSPin<T, A>)>, |
| 110 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, | 113 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, |
| 111 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, | 114 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, |
| 112 | tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>, | 115 | tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>, |
| 113 | tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>, | 116 | tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>, |
| 114 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, | 117 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, |
| 115 | phy: P, | ||
| 116 | mac_addr: [u8; 6], | 118 | mac_addr: [u8; 6], |
| 119 | sma: Peri<'d, SMA>, | ||
| 120 | mdio: Peri<'d, if_afio!(impl MDIOPin<SMA, A>)>, | ||
| 121 | mdc: Peri<'d, if_afio!(impl MDCPin<SMA, A>)>, | ||
| 117 | ) -> Self { | 122 | ) -> Self { |
| 118 | // Enable the necessary Clocks | 123 | let sma = Sma::new(sma, mdio, mdc); |
| 119 | #[cfg(eth_v1a)] | 124 | let phy = GenericPhy::new_auto(sma); |
| 120 | critical_section::with(|_| { | ||
| 121 | RCC.apb2enr().modify(|w| w.set_afioen(true)); | ||
| 122 | |||
| 123 | // Select RMII (Reduced Media Independent Interface) | ||
| 124 | // Must be done prior to enabling peripheral clock | ||
| 125 | AFIO.mapr().modify(|w| { | ||
| 126 | w.set_mii_rmii_sel(true); | ||
| 127 | w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); | ||
| 128 | }); | ||
| 129 | 125 | ||
| 130 | RCC.ahbenr().modify(|w| { | 126 | Self::new_with_phy( |
| 131 | w.set_ethen(true); | 127 | queue, peri, irq, ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en, mac_addr, phy, |
| 132 | w.set_ethtxen(true); | 128 | ) |
| 133 | w.set_ethrxen(true); | 129 | } |
| 134 | }); | ||
| 135 | }); | ||
| 136 | 130 | ||
| 137 | #[cfg(any(eth_v1b, eth_v1c))] | 131 | /// Create a new MII ethernet driver using 14 pins. |
| 138 | critical_section::with(|_| { | 132 | /// |
| 139 | RCC.ahb1enr().modify(|w| { | 133 | /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the |
| 140 | w.set_ethen(true); | 134 | /// provided [`SMA`](sma::Instance), and MDIO and MDC pins. |
| 141 | w.set_ethtxen(true); | 135 | /// |
| 142 | w.set_ethrxen(true); | 136 | /// See [`Ethernet::new_mii_with_phy`] for creating an RMII ethernet |
| 143 | }); | 137 | /// river with a non-standard PHY. |
| 138 | pub fn new_mii<const TX: usize, const RX: usize, #[cfg(afio)] A>( | ||
| 139 | queue: &'d mut PacketQueue<TX, RX>, | ||
| 140 | peri: Peri<'d, T>, | ||
| 141 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | ||
| 142 | rx_clk: Peri<'d, if_afio!(impl RXClkPin<T, A>)>, | ||
| 143 | tx_clk: Peri<'d, if_afio!(impl TXClkPin<T, A>)>, | ||
| 144 | rxdv: Peri<'d, if_afio!(impl RXDVPin<T, A>)>, | ||
| 145 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, | ||
| 146 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, | ||
| 147 | rx_d2: Peri<'d, if_afio!(impl RXD2Pin<T, A>)>, | ||
| 148 | rx_d3: Peri<'d, if_afio!(impl RXD3Pin<T, A>)>, | ||
| 149 | tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>, | ||
| 150 | tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>, | ||
| 151 | tx_d2: Peri<'d, if_afio!(impl TXD2Pin<T, A>)>, | ||
| 152 | tx_d3: Peri<'d, if_afio!(impl TXD3Pin<T, A>)>, | ||
| 153 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, | ||
| 154 | mac_addr: [u8; 6], | ||
| 155 | sma: Peri<'d, SMA>, | ||
| 156 | mdio: Peri<'d, if_afio!(impl MDIOPin<SMA, A>)>, | ||
| 157 | mdc: Peri<'d, if_afio!(impl MDCPin<SMA, A>)>, | ||
| 158 | ) -> Self { | ||
| 159 | let sma = Sma::new(sma, mdio, mdc); | ||
| 160 | let phy = GenericPhy::new_auto(sma); | ||
| 144 | 161 | ||
| 145 | // RMII (Reduced Media Independent Interface) | 162 | Self::new_mii_with_phy( |
| 146 | SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(true)); | 163 | queue, peri, irq, rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en, |
| 147 | }); | 164 | mac_addr, phy, |
| 165 | ) | ||
| 166 | } | ||
| 167 | } | ||
| 148 | 168 | ||
| 169 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | ||
| 170 | /// safety: the returned instance is not leak-safe | ||
| 171 | pub fn new_with_phy<const TX: usize, const RX: usize, #[cfg(afio)] A>( | ||
| 172 | queue: &'d mut PacketQueue<TX, RX>, | ||
| 173 | peri: Peri<'d, T>, | ||
| 174 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | ||
| 175 | ref_clk: Peri<'d, if_afio!(impl RefClkPin<T, A>)>, | ||
| 176 | crs: Peri<'d, if_afio!(impl CRSPin<T, A>)>, | ||
| 177 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, | ||
| 178 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, | ||
| 179 | tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>, | ||
| 180 | tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>, | ||
| 181 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, | ||
| 182 | mac_addr: [u8; 6], | ||
| 183 | phy: P, | ||
| 184 | ) -> Self { | ||
| 149 | #[cfg(eth_v1a)] | 185 | #[cfg(eth_v1a)] |
| 150 | { | 186 | { |
| 151 | config_in_pins!(ref_clk, rx_d0, rx_d1); | 187 | config_in_pins!(ref_clk, rx_d0, rx_d1); |
| 152 | config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en); | 188 | config_af_pins!(tx_d0, tx_d1, tx_en); |
| 153 | } | 189 | } |
| 154 | 190 | ||
| 155 | #[cfg(any(eth_v1b, eth_v1c))] | 191 | #[cfg(any(eth_v1b, eth_v1c))] |
| 156 | config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | 192 | config_pins!(ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); |
| 157 | 193 | ||
| 158 | let pins = Pins::Rmii([ | 194 | let pins = Pins::Rmii([ |
| 159 | ref_clk.into(), | 195 | ref_clk.into(), |
| 160 | mdio.into(), | ||
| 161 | mdc.into(), | ||
| 162 | crs.into(), | 196 | crs.into(), |
| 163 | rx_d0.into(), | 197 | rx_d0.into(), |
| 164 | rx_d1.into(), | 198 | rx_d1.into(), |
| @@ -167,7 +201,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 167 | tx_en.into(), | 201 | tx_en.into(), |
| 168 | ]); | 202 | ]); |
| 169 | 203 | ||
| 170 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) | 204 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr, true) |
| 171 | } | 205 | } |
| 172 | 206 | ||
| 173 | fn new_inner<const TX: usize, const RX: usize>( | 207 | fn new_inner<const TX: usize, const RX: usize>( |
| @@ -177,7 +211,39 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 177 | pins: Pins<'d>, | 211 | pins: Pins<'d>, |
| 178 | phy: P, | 212 | phy: P, |
| 179 | mac_addr: [u8; 6], | 213 | mac_addr: [u8; 6], |
| 214 | rmii_mii_sel: bool, | ||
| 180 | ) -> Self { | 215 | ) -> Self { |
| 216 | // Enable the necessary Clocks | ||
| 217 | #[cfg(eth_v1a)] | ||
| 218 | critical_section::with(|_| { | ||
| 219 | RCC.apb2enr().modify(|w| w.set_afioen(true)); | ||
| 220 | |||
| 221 | // Select (R)MII (Reduced Media Independent Interface) | ||
| 222 | // Must be done prior to enabling peripheral clock | ||
| 223 | AFIO.mapr().modify(|w| { | ||
| 224 | w.set_mii_rmii_sel(rmii_mii_sel); | ||
| 225 | w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); | ||
| 226 | }); | ||
| 227 | |||
| 228 | RCC.ahbenr().modify(|w| { | ||
| 229 | w.set_ethen(true); | ||
| 230 | w.set_ethtxen(true); | ||
| 231 | w.set_ethrxen(true); | ||
| 232 | }); | ||
| 233 | }); | ||
| 234 | |||
| 235 | #[cfg(any(eth_v1b, eth_v1c))] | ||
| 236 | critical_section::with(|_| { | ||
| 237 | RCC.ahb1enr().modify(|w| { | ||
| 238 | w.set_ethen(true); | ||
| 239 | w.set_ethtxen(true); | ||
| 240 | w.set_ethrxen(true); | ||
| 241 | }); | ||
| 242 | |||
| 243 | // (R)MII ((Reduced) Media Independent Interface) | ||
| 244 | SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(rmii_mii_sel)); | ||
| 245 | }); | ||
| 246 | |||
| 181 | let dma = T::regs().ethernet_dma(); | 247 | let dma = T::regs().ethernet_dma(); |
| 182 | let mac = T::regs().ethernet_mac(); | 248 | let mac = T::regs().ethernet_mac(); |
| 183 | 249 | ||
| @@ -226,30 +292,10 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 226 | 292 | ||
| 227 | // TODO MTU size setting not found for v1 ethernet, check if correct | 293 | // TODO MTU size setting not found for v1 ethernet, check if correct |
| 228 | 294 | ||
| 229 | let hclk = <T as SealedRccPeripheral>::frequency(); | ||
| 230 | let hclk_mhz = hclk.0 / 1_000_000; | ||
| 231 | |||
| 232 | // Set the MDC clock frequency in the range 1MHz - 2.5MHz | ||
| 233 | let clock_range = match hclk_mhz { | ||
| 234 | 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."), | ||
| 235 | 25..=34 => Cr::CR_20_35, // Divide by 16 | ||
| 236 | 35..=59 => Cr::CR_35_60, // Divide by 26 | ||
| 237 | 60..=99 => Cr::CR_60_100, // Divide by 42 | ||
| 238 | 100..=149 => Cr::CR_100_150, // Divide by 62 | ||
| 239 | 150..=216 => Cr::CR_150_168, // Divide by 102 | ||
| 240 | _ => { | ||
| 241 | panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") | ||
| 242 | } | ||
| 243 | }; | ||
| 244 | |||
| 245 | let mut this = Self { | 295 | let mut this = Self { |
| 246 | _peri: peri, | 296 | _peri: peri, |
| 247 | pins, | 297 | pins, |
| 248 | phy: phy, | 298 | phy: phy, |
| 249 | station_management: EthernetStationManagement { | ||
| 250 | peri: PhantomData, | ||
| 251 | clock_range: clock_range, | ||
| 252 | }, | ||
| 253 | mac_addr, | 299 | mac_addr, |
| 254 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), | 300 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), |
| 255 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), | 301 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), |
| @@ -279,8 +325,8 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 279 | w.set_tie(true); | 325 | w.set_tie(true); |
| 280 | }); | 326 | }); |
| 281 | 327 | ||
| 282 | this.phy.phy_reset(&mut this.station_management); | 328 | this.phy.phy_reset(); |
| 283 | this.phy.phy_init(&mut this.station_management); | 329 | this.phy.phy_init(); |
| 284 | 330 | ||
| 285 | interrupt::ETH.unpend(); | 331 | interrupt::ETH.unpend(); |
| 286 | unsafe { interrupt::ETH.enable() }; | 332 | unsafe { interrupt::ETH.enable() }; |
| @@ -288,15 +334,13 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 288 | this | 334 | this |
| 289 | } | 335 | } |
| 290 | 336 | ||
| 291 | /// Create a new MII ethernet driver using 14 pins. | 337 | /// Create a new MII ethernet driver using 12 pins. |
| 292 | pub fn new_mii<const TX: usize, const RX: usize, #[cfg(afio)] A>( | 338 | pub fn new_mii_with_phy<const TX: usize, const RX: usize, #[cfg(afio)] A>( |
| 293 | queue: &'d mut PacketQueue<TX, RX>, | 339 | queue: &'d mut PacketQueue<TX, RX>, |
| 294 | peri: Peri<'d, T>, | 340 | peri: Peri<'d, T>, |
| 295 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | 341 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, |
| 296 | rx_clk: Peri<'d, if_afio!(impl RXClkPin<T, A>)>, | 342 | rx_clk: Peri<'d, if_afio!(impl RXClkPin<T, A>)>, |
| 297 | tx_clk: Peri<'d, if_afio!(impl TXClkPin<T, A>)>, | 343 | tx_clk: Peri<'d, if_afio!(impl TXClkPin<T, A>)>, |
| 298 | mdio: Peri<'d, if_afio!(impl MDIOPin<T, A>)>, | ||
| 299 | mdc: Peri<'d, if_afio!(impl MDCPin<T, A>)>, | ||
| 300 | rxdv: Peri<'d, if_afio!(impl RXDVPin<T, A>)>, | 344 | rxdv: Peri<'d, if_afio!(impl RXDVPin<T, A>)>, |
| 301 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, | 345 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, |
| 302 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, | 346 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, |
| @@ -307,58 +351,23 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 307 | tx_d2: Peri<'d, if_afio!(impl TXD2Pin<T, A>)>, | 351 | tx_d2: Peri<'d, if_afio!(impl TXD2Pin<T, A>)>, |
| 308 | tx_d3: Peri<'d, if_afio!(impl TXD3Pin<T, A>)>, | 352 | tx_d3: Peri<'d, if_afio!(impl TXD3Pin<T, A>)>, |
| 309 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, | 353 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, |
| 310 | phy: P, | ||
| 311 | mac_addr: [u8; 6], | 354 | mac_addr: [u8; 6], |
| 355 | phy: P, | ||
| 312 | ) -> Self { | 356 | ) -> Self { |
| 313 | // TODO: Handle optional signals like CRS, MII_COL, RX_ER? | ||
| 314 | |||
| 315 | // Enable the necessary Clocks | ||
| 316 | #[cfg(eth_v1a)] | ||
| 317 | critical_section::with(|_| { | ||
| 318 | RCC.apb2enr().modify(|w| w.set_afioen(true)); | ||
| 319 | |||
| 320 | // Select MII (Media Independent Interface) | ||
| 321 | // Must be done prior to enabling peripheral clock | ||
| 322 | AFIO.mapr().modify(|w| { | ||
| 323 | w.set_mii_rmii_sel(false); | ||
| 324 | w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); | ||
| 325 | }); | ||
| 326 | |||
| 327 | RCC.ahbenr().modify(|w| { | ||
| 328 | w.set_ethen(true); | ||
| 329 | w.set_ethtxen(true); | ||
| 330 | w.set_ethrxen(true); | ||
| 331 | }); | ||
| 332 | }); | ||
| 333 | |||
| 334 | #[cfg(any(eth_v1b, eth_v1c))] | ||
| 335 | critical_section::with(|_| { | ||
| 336 | RCC.ahb1enr().modify(|w| { | ||
| 337 | w.set_ethen(true); | ||
| 338 | w.set_ethtxen(true); | ||
| 339 | w.set_ethrxen(true); | ||
| 340 | }); | ||
| 341 | |||
| 342 | // MII (Media Independent Interface) | ||
| 343 | SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(false)); | ||
| 344 | }); | ||
| 345 | |||
| 346 | #[cfg(eth_v1a)] | 357 | #[cfg(eth_v1a)] |
| 347 | { | 358 | { |
| 348 | config_in_pins!(rx_clk, tx_clk, rx_d0, rx_d1, rx_d2, rx_d3, rxdv); | 359 | config_in_pins!(rx_clk, tx_clk, rx_d0, rx_d1, rx_d2, rx_d3, rxdv); |
| 349 | config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); | 360 | config_af_pins!(tx_d0, tx_d1, tx_d2, tx_d3, tx_en); |
| 350 | } | 361 | } |
| 351 | 362 | ||
| 352 | #[cfg(any(eth_v1b, eth_v1c))] | 363 | #[cfg(any(eth_v1b, eth_v1c))] |
| 353 | config_pins!( | 364 | config_pins!( |
| 354 | rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en | 365 | rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en |
| 355 | ); | 366 | ); |
| 356 | 367 | ||
| 357 | let pins = Pins::Mii([ | 368 | let pins = Pins::Mii([ |
| 358 | rx_clk.into(), | 369 | rx_clk.into(), |
| 359 | tx_clk.into(), | 370 | tx_clk.into(), |
| 360 | mdio.into(), | ||
| 361 | mdc.into(), | ||
| 362 | rxdv.into(), | 371 | rxdv.into(), |
| 363 | rx_d0.into(), | 372 | rx_d0.into(), |
| 364 | rx_d1.into(), | 373 | rx_d1.into(), |
| @@ -371,43 +380,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 371 | tx_en.into(), | 380 | tx_en.into(), |
| 372 | ]); | 381 | ]); |
| 373 | 382 | ||
| 374 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) | 383 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr, false) |
| 375 | } | ||
| 376 | } | ||
| 377 | |||
| 378 | /// Ethernet station management interface. | ||
| 379 | pub(crate) struct EthernetStationManagement<T: Instance> { | ||
| 380 | peri: PhantomData<T>, | ||
| 381 | clock_range: Cr, | ||
| 382 | } | ||
| 383 | |||
| 384 | impl<T: Instance> StationManagement for EthernetStationManagement<T> { | ||
| 385 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { | ||
| 386 | let mac = T::regs().ethernet_mac(); | ||
| 387 | |||
| 388 | mac.macmiiar().modify(|w| { | ||
| 389 | w.set_pa(phy_addr); | ||
| 390 | w.set_mr(reg); | ||
| 391 | w.set_mw(Mw::READ); // read operation | ||
| 392 | w.set_cr(self.clock_range); | ||
| 393 | w.set_mb(MbProgress::BUSY); // indicate that operation is in progress | ||
| 394 | }); | ||
| 395 | while mac.macmiiar().read().mb() == MbProgress::BUSY {} | ||
| 396 | mac.macmiidr().read().md() | ||
| 397 | } | ||
| 398 | |||
| 399 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { | ||
| 400 | let mac = T::regs().ethernet_mac(); | ||
| 401 | |||
| 402 | mac.macmiidr().write(|w| w.set_md(val)); | ||
| 403 | mac.macmiiar().modify(|w| { | ||
| 404 | w.set_pa(phy_addr); | ||
| 405 | w.set_mr(reg); | ||
| 406 | w.set_mw(Mw::WRITE); // write | ||
| 407 | w.set_cr(self.clock_range); | ||
| 408 | w.set_mb(MbProgress::BUSY); | ||
| 409 | }); | ||
| 410 | while mac.macmiiar().read().mb() == MbProgress::BUSY {} | ||
| 411 | } | 384 | } |
| 412 | } | 385 | } |
| 413 | 386 | ||
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 39a6e8b0f..7f92e351c 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | mod descriptors; | 1 | mod descriptors; |
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | ||
| 4 | use core::sync::atomic::{Ordering, fence}; | 3 | use core::sync::atomic::{Ordering, fence}; |
| 5 | 4 | ||
| 6 | use embassy_hal_internal::Peri; | 5 | use embassy_hal_internal::Peri; |
| @@ -12,7 +11,6 @@ use crate::gpio::{AfType, AnyPin, OutputType, SealedPin as _, Speed}; | |||
| 12 | use crate::interrupt; | 11 | use crate::interrupt; |
| 13 | use crate::interrupt::InterruptExt; | 12 | use crate::interrupt::InterruptExt; |
| 14 | use crate::pac::ETH; | 13 | use crate::pac::ETH; |
| 15 | use crate::rcc::SealedRccPeripheral; | ||
| 16 | 14 | ||
| 17 | /// Interrupt handler. | 15 | /// Interrupt handler. |
| 18 | pub struct InterruptHandler {} | 16 | pub struct InterruptHandler {} |
| @@ -42,14 +40,13 @@ pub struct Ethernet<'d, T: Instance, P: Phy> { | |||
| 42 | pub(crate) rx: RDesRing<'d>, | 40 | pub(crate) rx: RDesRing<'d>, |
| 43 | pins: Pins<'d>, | 41 | pins: Pins<'d>, |
| 44 | pub(crate) phy: P, | 42 | pub(crate) phy: P, |
| 45 | pub(crate) station_management: EthernetStationManagement<T>, | ||
| 46 | pub(crate) mac_addr: [u8; 6], | 43 | pub(crate) mac_addr: [u8; 6], |
| 47 | } | 44 | } |
| 48 | 45 | ||
| 49 | /// Pins of ethernet driver. | 46 | /// Pins of ethernet driver. |
| 50 | enum Pins<'d> { | 47 | enum Pins<'d> { |
| 51 | Rmii([Peri<'d, AnyPin>; 9]), | 48 | Rmii([Peri<'d, AnyPin>; 7]), |
| 52 | Mii([Peri<'d, AnyPin>; 14]), | 49 | Mii([Peri<'d, AnyPin>; 12]), |
| 53 | } | 50 | } |
| 54 | 51 | ||
| 55 | macro_rules! config_pins { | 52 | macro_rules! config_pins { |
| @@ -63,41 +60,96 @@ macro_rules! config_pins { | |||
| 63 | }; | 60 | }; |
| 64 | } | 61 | } |
| 65 | 62 | ||
| 66 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | 63 | impl<'d, T: Instance, SMA: sma::Instance> Ethernet<'d, T, GenericPhy<Sma<'d, SMA>>> { |
| 67 | /// Create a new RMII ethernet driver using 9 pins. | 64 | /// Create a new RMII ethernet driver using 7 pins. |
| 65 | /// | ||
| 66 | /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the | ||
| 67 | /// provided [`SMA`](sma::Instance), and MDIO and MDC pins. | ||
| 68 | /// | ||
| 69 | /// See [`Ethernet::new_with_phy`] for creating an RMII ethernet | ||
| 70 | /// river with a non-standard PHY. | ||
| 68 | pub fn new<const TX: usize, const RX: usize>( | 71 | pub fn new<const TX: usize, const RX: usize>( |
| 69 | queue: &'d mut PacketQueue<TX, RX>, | 72 | queue: &'d mut PacketQueue<TX, RX>, |
| 70 | peri: Peri<'d, T>, | 73 | peri: Peri<'d, T>, |
| 71 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | 74 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, |
| 72 | ref_clk: Peri<'d, impl RefClkPin<T>>, | 75 | ref_clk: Peri<'d, impl RefClkPin<T>>, |
| 73 | mdio: Peri<'d, impl MDIOPin<T>>, | ||
| 74 | mdc: Peri<'d, impl MDCPin<T>>, | ||
| 75 | crs: Peri<'d, impl CRSPin<T>>, | 76 | crs: Peri<'d, impl CRSPin<T>>, |
| 76 | rx_d0: Peri<'d, impl RXD0Pin<T>>, | 77 | rx_d0: Peri<'d, impl RXD0Pin<T>>, |
| 77 | rx_d1: Peri<'d, impl RXD1Pin<T>>, | 78 | rx_d1: Peri<'d, impl RXD1Pin<T>>, |
| 78 | tx_d0: Peri<'d, impl TXD0Pin<T>>, | 79 | tx_d0: Peri<'d, impl TXD0Pin<T>>, |
| 79 | tx_d1: Peri<'d, impl TXD1Pin<T>>, | 80 | tx_d1: Peri<'d, impl TXD1Pin<T>>, |
| 80 | tx_en: Peri<'d, impl TXEnPin<T>>, | 81 | tx_en: Peri<'d, impl TXEnPin<T>>, |
| 81 | phy: P, | ||
| 82 | mac_addr: [u8; 6], | 82 | mac_addr: [u8; 6], |
| 83 | sma: Peri<'d, SMA>, | ||
| 84 | mdio: Peri<'d, impl MDIOPin<SMA>>, | ||
| 85 | mdc: Peri<'d, impl MDCPin<SMA>>, | ||
| 83 | ) -> Self { | 86 | ) -> Self { |
| 84 | // Enable the necessary clocks | 87 | let sma = Sma::new(sma, mdio, mdc); |
| 85 | critical_section::with(|_| { | 88 | let phy = GenericPhy::new_auto(sma); |
| 86 | crate::pac::RCC.ahb1enr().modify(|w| { | ||
| 87 | w.set_ethen(true); | ||
| 88 | w.set_ethtxen(true); | ||
| 89 | w.set_ethrxen(true); | ||
| 90 | }); | ||
| 91 | 89 | ||
| 92 | crate::pac::SYSCFG.pmcr().modify(|w| w.set_eth_sel_phy(EthSelPhy::RMII)); | 90 | Self::new_with_phy( |
| 93 | }); | 91 | queue, peri, irq, ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en, mac_addr, phy, |
| 92 | ) | ||
| 93 | } | ||
| 94 | |||
| 95 | /// Create a new MII ethernet driver using 14 pins. | ||
| 96 | /// | ||
| 97 | /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the | ||
| 98 | /// provided [`SMA`](sma::Instance), and MDIO and MDC pins. | ||
| 99 | /// | ||
| 100 | /// See [`Ethernet::new_mii_with_phy`] for creating an RMII ethernet | ||
| 101 | /// river with a non-standard PHY. | ||
| 102 | pub fn new_mii<const TX: usize, const RX: usize>( | ||
| 103 | queue: &'d mut PacketQueue<TX, RX>, | ||
| 104 | peri: Peri<'d, T>, | ||
| 105 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | ||
| 106 | rx_clk: Peri<'d, impl RXClkPin<T>>, | ||
| 107 | tx_clk: Peri<'d, impl TXClkPin<T>>, | ||
| 108 | rxdv: Peri<'d, impl RXDVPin<T>>, | ||
| 109 | rx_d0: Peri<'d, impl RXD0Pin<T>>, | ||
| 110 | rx_d1: Peri<'d, impl RXD1Pin<T>>, | ||
| 111 | rx_d2: Peri<'d, impl RXD2Pin<T>>, | ||
| 112 | rx_d3: Peri<'d, impl RXD3Pin<T>>, | ||
| 113 | tx_d0: Peri<'d, impl TXD0Pin<T>>, | ||
| 114 | tx_d1: Peri<'d, impl TXD1Pin<T>>, | ||
| 115 | tx_d2: Peri<'d, impl TXD2Pin<T>>, | ||
| 116 | tx_d3: Peri<'d, impl TXD3Pin<T>>, | ||
| 117 | tx_en: Peri<'d, impl TXEnPin<T>>, | ||
| 118 | mac_addr: [u8; 6], | ||
| 119 | sma: Peri<'d, SMA>, | ||
| 120 | mdio: Peri<'d, impl MDIOPin<SMA>>, | ||
| 121 | mdc: Peri<'d, impl MDCPin<SMA>>, | ||
| 122 | ) -> Self { | ||
| 123 | let sma = Sma::new(sma, mdio, mdc); | ||
| 124 | let phy = GenericPhy::new_auto(sma); | ||
| 125 | |||
| 126 | Self::new_mii_with_phy( | ||
| 127 | queue, peri, irq, rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en, | ||
| 128 | mac_addr, phy, | ||
| 129 | ) | ||
| 130 | } | ||
| 131 | } | ||
| 94 | 132 | ||
| 95 | config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | 133 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { |
| 134 | /// Create a new RMII ethernet driver using 7 pins. | ||
| 135 | pub fn new_with_phy<const TX: usize, const RX: usize>( | ||
| 136 | queue: &'d mut PacketQueue<TX, RX>, | ||
| 137 | peri: Peri<'d, T>, | ||
| 138 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | ||
| 139 | ref_clk: Peri<'d, impl RefClkPin<T>>, | ||
| 140 | crs: Peri<'d, impl CRSPin<T>>, | ||
| 141 | rx_d0: Peri<'d, impl RXD0Pin<T>>, | ||
| 142 | rx_d1: Peri<'d, impl RXD1Pin<T>>, | ||
| 143 | tx_d0: Peri<'d, impl TXD0Pin<T>>, | ||
| 144 | tx_d1: Peri<'d, impl TXD1Pin<T>>, | ||
| 145 | tx_en: Peri<'d, impl TXEnPin<T>>, | ||
| 146 | mac_addr: [u8; 6], | ||
| 147 | phy: P, | ||
| 148 | ) -> Self { | ||
| 149 | config_pins!(ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | ||
| 96 | 150 | ||
| 97 | let pins = Pins::Rmii([ | 151 | let pins = Pins::Rmii([ |
| 98 | ref_clk.into(), | 152 | ref_clk.into(), |
| 99 | mdio.into(), | ||
| 100 | mdc.into(), | ||
| 101 | crs.into(), | 153 | crs.into(), |
| 102 | rx_d0.into(), | 154 | rx_d0.into(), |
| 103 | rx_d1.into(), | 155 | rx_d1.into(), |
| @@ -106,18 +158,16 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 106 | tx_en.into(), | 158 | tx_en.into(), |
| 107 | ]); | 159 | ]); |
| 108 | 160 | ||
| 109 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) | 161 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr, EthSelPhy::RMII) |
| 110 | } | 162 | } |
| 111 | 163 | ||
| 112 | /// Create a new MII ethernet driver using 14 pins. | 164 | /// Create a new MII ethernet driver using 12 pins. |
| 113 | pub fn new_mii<const TX: usize, const RX: usize>( | 165 | pub fn new_mii_with_phy<const TX: usize, const RX: usize>( |
| 114 | queue: &'d mut PacketQueue<TX, RX>, | 166 | queue: &'d mut PacketQueue<TX, RX>, |
| 115 | peri: Peri<'d, T>, | 167 | peri: Peri<'d, T>, |
| 116 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | 168 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, |
| 117 | rx_clk: Peri<'d, impl RXClkPin<T>>, | 169 | rx_clk: Peri<'d, impl RXClkPin<T>>, |
| 118 | tx_clk: Peri<'d, impl TXClkPin<T>>, | 170 | tx_clk: Peri<'d, impl TXClkPin<T>>, |
| 119 | mdio: Peri<'d, impl MDIOPin<T>>, | ||
| 120 | mdc: Peri<'d, impl MDCPin<T>>, | ||
| 121 | rxdv: Peri<'d, impl RXDVPin<T>>, | 171 | rxdv: Peri<'d, impl RXDVPin<T>>, |
| 122 | rx_d0: Peri<'d, impl RXD0Pin<T>>, | 172 | rx_d0: Peri<'d, impl RXD0Pin<T>>, |
| 123 | rx_d1: Peri<'d, impl RXD1Pin<T>>, | 173 | rx_d1: Peri<'d, impl RXD1Pin<T>>, |
| @@ -128,31 +178,16 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 128 | tx_d2: Peri<'d, impl TXD2Pin<T>>, | 178 | tx_d2: Peri<'d, impl TXD2Pin<T>>, |
| 129 | tx_d3: Peri<'d, impl TXD3Pin<T>>, | 179 | tx_d3: Peri<'d, impl TXD3Pin<T>>, |
| 130 | tx_en: Peri<'d, impl TXEnPin<T>>, | 180 | tx_en: Peri<'d, impl TXEnPin<T>>, |
| 131 | phy: P, | ||
| 132 | mac_addr: [u8; 6], | 181 | mac_addr: [u8; 6], |
| 182 | phy: P, | ||
| 133 | ) -> Self { | 183 | ) -> Self { |
| 134 | // Enable the necessary clocks | ||
| 135 | critical_section::with(|_| { | ||
| 136 | crate::pac::RCC.ahb1enr().modify(|w| { | ||
| 137 | w.set_ethen(true); | ||
| 138 | w.set_ethtxen(true); | ||
| 139 | w.set_ethrxen(true); | ||
| 140 | }); | ||
| 141 | |||
| 142 | crate::pac::SYSCFG | ||
| 143 | .pmcr() | ||
| 144 | .modify(|w| w.set_eth_sel_phy(EthSelPhy::MII_GMII)); | ||
| 145 | }); | ||
| 146 | |||
| 147 | config_pins!( | 184 | config_pins!( |
| 148 | rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en | 185 | rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en |
| 149 | ); | 186 | ); |
| 150 | 187 | ||
| 151 | let pins = Pins::Mii([ | 188 | let pins = Pins::Mii([ |
| 152 | rx_clk.into(), | 189 | rx_clk.into(), |
| 153 | tx_clk.into(), | 190 | tx_clk.into(), |
| 154 | mdio.into(), | ||
| 155 | mdc.into(), | ||
| 156 | rxdv.into(), | 191 | rxdv.into(), |
| 157 | rx_d0.into(), | 192 | rx_d0.into(), |
| 158 | rx_d1.into(), | 193 | rx_d1.into(), |
| @@ -165,7 +200,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 165 | tx_en.into(), | 200 | tx_en.into(), |
| 166 | ]); | 201 | ]); |
| 167 | 202 | ||
| 168 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) | 203 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr, EthSelPhy::MII_GMII) |
| 169 | } | 204 | } |
| 170 | 205 | ||
| 171 | fn new_inner<const TX: usize, const RX: usize>( | 206 | fn new_inner<const TX: usize, const RX: usize>( |
| @@ -175,7 +210,19 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 175 | pins: Pins<'d>, | 210 | pins: Pins<'d>, |
| 176 | phy: P, | 211 | phy: P, |
| 177 | mac_addr: [u8; 6], | 212 | mac_addr: [u8; 6], |
| 213 | eth_sel_phy: EthSelPhy, | ||
| 178 | ) -> Self { | 214 | ) -> Self { |
| 215 | // Enable the necessary clocks | ||
| 216 | critical_section::with(|_| { | ||
| 217 | crate::pac::RCC.ahb1enr().modify(|w| { | ||
| 218 | w.set_ethen(true); | ||
| 219 | w.set_ethtxen(true); | ||
| 220 | w.set_ethrxen(true); | ||
| 221 | }); | ||
| 222 | |||
| 223 | crate::pac::SYSCFG.pmcr().modify(|w| w.set_eth_sel_phy(eth_sel_phy)); | ||
| 224 | }); | ||
| 225 | |||
| 179 | let dma = T::regs().ethernet_dma(); | 226 | let dma = T::regs().ethernet_dma(); |
| 180 | let mac = T::regs().ethernet_mac(); | 227 | let mac = T::regs().ethernet_mac(); |
| 181 | let mtl = T::regs().ethernet_mtl(); | 228 | let mtl = T::regs().ethernet_mtl(); |
| @@ -237,32 +284,12 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 237 | w.set_rbsz(RX_BUFFER_SIZE as u16); | 284 | w.set_rbsz(RX_BUFFER_SIZE as u16); |
| 238 | }); | 285 | }); |
| 239 | 286 | ||
| 240 | let hclk = <T as SealedRccPeripheral>::frequency(); | ||
| 241 | let hclk_mhz = hclk.0 / 1_000_000; | ||
| 242 | |||
| 243 | // Set the MDC clock frequency in the range 1MHz - 2.5MHz | ||
| 244 | let clock_range = match hclk_mhz { | ||
| 245 | 0..=34 => 2, // Divide by 16 | ||
| 246 | 35..=59 => 3, // Divide by 26 | ||
| 247 | 60..=99 => 0, // Divide by 42 | ||
| 248 | 100..=149 => 1, // Divide by 62 | ||
| 249 | 150..=249 => 4, // Divide by 102 | ||
| 250 | 250..=310 => 5, // Divide by 124 | ||
| 251 | _ => { | ||
| 252 | panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") | ||
| 253 | } | ||
| 254 | }; | ||
| 255 | |||
| 256 | let mut this = Self { | 287 | let mut this = Self { |
| 257 | _peri: peri, | 288 | _peri: peri, |
| 258 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), | 289 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), |
| 259 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), | 290 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), |
| 260 | pins, | 291 | pins, |
| 261 | phy, | 292 | phy, |
| 262 | station_management: EthernetStationManagement { | ||
| 263 | peri: PhantomData, | ||
| 264 | clock_range: clock_range, | ||
| 265 | }, | ||
| 266 | mac_addr, | 293 | mac_addr, |
| 267 | }; | 294 | }; |
| 268 | 295 | ||
| @@ -288,8 +315,8 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 288 | w.set_tie(true); | 315 | w.set_tie(true); |
| 289 | }); | 316 | }); |
| 290 | 317 | ||
| 291 | this.phy.phy_reset(&mut this.station_management); | 318 | this.phy.phy_reset(); |
| 292 | this.phy.phy_init(&mut this.station_management); | 319 | this.phy.phy_init(); |
| 293 | 320 | ||
| 294 | interrupt::ETH.unpend(); | 321 | interrupt::ETH.unpend(); |
| 295 | unsafe { interrupt::ETH.enable() }; | 322 | unsafe { interrupt::ETH.enable() }; |
| @@ -298,42 +325,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 298 | } | 325 | } |
| 299 | } | 326 | } |
| 300 | 327 | ||
| 301 | /// Ethernet SMI driver. | ||
| 302 | pub struct EthernetStationManagement<T: Instance> { | ||
| 303 | peri: PhantomData<T>, | ||
| 304 | clock_range: u8, | ||
| 305 | } | ||
| 306 | |||
| 307 | impl<T: Instance> StationManagement for EthernetStationManagement<T> { | ||
| 308 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { | ||
| 309 | let mac = T::regs().ethernet_mac(); | ||
| 310 | |||
| 311 | mac.macmdioar().modify(|w| { | ||
| 312 | w.set_pa(phy_addr); | ||
| 313 | w.set_rda(reg); | ||
| 314 | w.set_goc(0b11); // read | ||
| 315 | w.set_cr(self.clock_range); | ||
| 316 | w.set_mb(true); | ||
| 317 | }); | ||
| 318 | while mac.macmdioar().read().mb() {} | ||
| 319 | mac.macmdiodr().read().md() | ||
| 320 | } | ||
| 321 | |||
| 322 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { | ||
| 323 | let mac = T::regs().ethernet_mac(); | ||
| 324 | |||
| 325 | mac.macmdiodr().write(|w| w.set_md(val)); | ||
| 326 | mac.macmdioar().modify(|w| { | ||
| 327 | w.set_pa(phy_addr); | ||
| 328 | w.set_rda(reg); | ||
| 329 | w.set_goc(0b01); // write | ||
| 330 | w.set_cr(self.clock_range); | ||
| 331 | w.set_mb(true); | ||
| 332 | }); | ||
| 333 | while mac.macmdioar().read().mb() {} | ||
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 337 | impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> { | 328 | impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> { |
| 338 | fn drop(&mut self) { | 329 | fn drop(&mut self) { |
| 339 | let dma = T::regs().ethernet_dma(); | 330 | let dma = T::regs().ethernet_dma(); |
diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index cb46d362c..7b7896d46 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs | |||
| @@ -5,13 +5,16 @@ use core::marker::PhantomData; | |||
| 5 | use core::pin::Pin; | 5 | use core::pin::Pin; |
| 6 | use core::task::{Context, Poll}; | 6 | use core::task::{Context, Poll}; |
| 7 | 7 | ||
| 8 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; | 8 | use embassy_hal_internal::PeripheralType; |
| 9 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 10 | use futures_util::FutureExt; | ||
| 10 | 11 | ||
| 11 | use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, PinNumber, Pull}; | 12 | use crate::gpio::{AnyPin, ExtiPin, Input, Level, Pin as GpioPin, PinNumber, Pull}; |
| 13 | use crate::interrupt::Interrupt as InterruptEnum; | ||
| 14 | use crate::interrupt::typelevel::{Binding, Handler, Interrupt as InterruptType}; | ||
| 12 | use crate::pac::EXTI; | 15 | use crate::pac::EXTI; |
| 13 | use crate::pac::exti::regs::Lines; | 16 | use crate::pac::exti::regs::Lines; |
| 14 | use crate::{Peri, interrupt, pac, peripherals}; | 17 | use crate::{Peri, pac}; |
| 15 | 18 | ||
| 16 | const EXTI_COUNT: usize = 16; | 19 | const EXTI_COUNT: usize = 16; |
| 17 | static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT]; | 20 | static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT]; |
| @@ -105,10 +108,17 @@ impl<'d> Unpin for ExtiInput<'d> {} | |||
| 105 | 108 | ||
| 106 | impl<'d> ExtiInput<'d> { | 109 | impl<'d> ExtiInput<'d> { |
| 107 | /// Create an EXTI input. | 110 | /// Create an EXTI input. |
| 108 | pub fn new<T: GpioPin>(pin: Peri<'d, T>, ch: Peri<'d, T::ExtiChannel>, pull: Pull) -> Self { | 111 | /// |
| 109 | // Needed if using AnyPin+AnyChannel. | 112 | /// The Binding must bind the Channel's IRQ to [InterruptHandler]. |
| 110 | assert_eq!(pin.pin(), ch.number()); | 113 | pub fn new<T: ExtiPin + GpioPin>( |
| 111 | 114 | pin: Peri<'d, T>, | |
| 115 | _ch: Peri<'d, T::ExtiChannel>, | ||
| 116 | pull: Pull, | ||
| 117 | _irq: impl Binding< | ||
| 118 | <<T as ExtiPin>::ExtiChannel as Channel>::IRQ, | ||
| 119 | InterruptHandler<<<T as ExtiPin>::ExtiChannel as Channel>::IRQ>, | ||
| 120 | >, | ||
| 121 | ) -> Self { | ||
| 112 | Self { | 122 | Self { |
| 113 | pin: Input::new(pin, pull), | 123 | pin: Input::new(pin, pull), |
| 114 | } | 124 | } |
| @@ -133,7 +143,7 @@ impl<'d> ExtiInput<'d> { | |||
| 133 | /// | 143 | /// |
| 134 | /// This returns immediately if the pin is already high. | 144 | /// This returns immediately if the pin is already high. |
| 135 | pub async fn wait_for_high(&mut self) { | 145 | pub async fn wait_for_high(&mut self) { |
| 136 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false); | 146 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false, true); |
| 137 | if self.is_high() { | 147 | if self.is_high() { |
| 138 | return; | 148 | return; |
| 139 | } | 149 | } |
| @@ -144,7 +154,7 @@ impl<'d> ExtiInput<'d> { | |||
| 144 | /// | 154 | /// |
| 145 | /// This returns immediately if the pin is already low. | 155 | /// This returns immediately if the pin is already low. |
| 146 | pub async fn wait_for_low(&mut self) { | 156 | pub async fn wait_for_low(&mut self) { |
| 147 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true); | 157 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true, true); |
| 148 | if self.is_low() { | 158 | if self.is_low() { |
| 149 | return; | 159 | return; |
| 150 | } | 160 | } |
| @@ -155,19 +165,40 @@ impl<'d> ExtiInput<'d> { | |||
| 155 | /// | 165 | /// |
| 156 | /// If the pin is already high, it will wait for it to go low then back high. | 166 | /// If the pin is already high, it will wait for it to go low then back high. |
| 157 | pub async fn wait_for_rising_edge(&mut self) { | 167 | pub async fn wait_for_rising_edge(&mut self) { |
| 158 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false).await | 168 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false, true).await |
| 169 | } | ||
| 170 | |||
| 171 | /// Asynchronously wait until the pin sees a rising edge. | ||
| 172 | /// | ||
| 173 | /// If the pin is already high, it will wait for it to go low then back high. | ||
| 174 | pub fn poll_for_rising_edge<'a>(&mut self, cx: &mut Context<'a>) { | ||
| 175 | let _ = | ||
| 176 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false, false).poll_unpin(cx); | ||
| 159 | } | 177 | } |
| 160 | 178 | ||
| 161 | /// Asynchronously wait until the pin sees a falling edge. | 179 | /// Asynchronously wait until the pin sees a falling edge. |
| 162 | /// | 180 | /// |
| 163 | /// If the pin is already low, it will wait for it to go high then back low. | 181 | /// If the pin is already low, it will wait for it to go high then back low. |
| 164 | pub async fn wait_for_falling_edge(&mut self) { | 182 | pub async fn wait_for_falling_edge(&mut self) { |
| 165 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true).await | 183 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true, true).await |
| 184 | } | ||
| 185 | |||
| 186 | /// Asynchronously wait until the pin sees a falling edge. | ||
| 187 | /// | ||
| 188 | /// If the pin is already low, it will wait for it to go high then back low. | ||
| 189 | pub fn poll_for_falling_edge<'a>(&mut self, cx: &mut Context<'a>) { | ||
| 190 | let _ = | ||
| 191 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true, false).poll_unpin(cx); | ||
| 166 | } | 192 | } |
| 167 | 193 | ||
| 168 | /// Asynchronously wait until the pin sees any edge (either rising or falling). | 194 | /// Asynchronously wait until the pin sees any edge (either rising or falling). |
| 169 | pub async fn wait_for_any_edge(&mut self) { | 195 | pub async fn wait_for_any_edge(&mut self) { |
| 170 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true).await | 196 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true, true).await |
| 197 | } | ||
| 198 | |||
| 199 | /// Asynchronously wait until the pin sees any edge (either rising or falling). | ||
| 200 | pub fn poll_for_any_edge<'a>(&mut self, cx: &mut Context<'a>) { | ||
| 201 | let _ = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true, false).poll_unpin(cx); | ||
| 171 | } | 202 | } |
| 172 | } | 203 | } |
| 173 | 204 | ||
| @@ -227,11 +258,12 @@ impl<'d> embedded_hal_async::digital::Wait for ExtiInput<'d> { | |||
| 227 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 258 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 228 | struct ExtiInputFuture<'a> { | 259 | struct ExtiInputFuture<'a> { |
| 229 | pin: PinNumber, | 260 | pin: PinNumber, |
| 261 | drop: bool, | ||
| 230 | phantom: PhantomData<&'a mut AnyPin>, | 262 | phantom: PhantomData<&'a mut AnyPin>, |
| 231 | } | 263 | } |
| 232 | 264 | ||
| 233 | impl<'a> ExtiInputFuture<'a> { | 265 | impl<'a> ExtiInputFuture<'a> { |
| 234 | fn new(pin: PinNumber, port: PinNumber, rising: bool, falling: bool) -> Self { | 266 | fn new(pin: PinNumber, port: PinNumber, rising: bool, falling: bool, drop: bool) -> Self { |
| 235 | critical_section::with(|_| { | 267 | critical_section::with(|_| { |
| 236 | let pin = pin as usize; | 268 | let pin = pin as usize; |
| 237 | exticr_regs().exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port)); | 269 | exticr_regs().exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port)); |
| @@ -252,6 +284,7 @@ impl<'a> ExtiInputFuture<'a> { | |||
| 252 | 284 | ||
| 253 | Self { | 285 | Self { |
| 254 | pin, | 286 | pin, |
| 287 | drop, | ||
| 255 | phantom: PhantomData, | 288 | phantom: PhantomData, |
| 256 | } | 289 | } |
| 257 | } | 290 | } |
| @@ -259,10 +292,12 @@ impl<'a> ExtiInputFuture<'a> { | |||
| 259 | 292 | ||
| 260 | impl<'a> Drop for ExtiInputFuture<'a> { | 293 | impl<'a> Drop for ExtiInputFuture<'a> { |
| 261 | fn drop(&mut self) { | 294 | fn drop(&mut self) { |
| 262 | critical_section::with(|_| { | 295 | if self.drop { |
| 263 | let pin = self.pin as _; | 296 | critical_section::with(|_| { |
| 264 | cpu_regs().imr(0).modify(|w| w.set_line(pin, false)); | 297 | let pin = self.pin as _; |
| 265 | }); | 298 | cpu_regs().imr(0).modify(|w| w.set_line(pin, false)); |
| 299 | }); | ||
| 300 | } | ||
| 266 | } | 301 | } |
| 267 | } | 302 | } |
| 268 | 303 | ||
| @@ -302,7 +337,7 @@ macro_rules! foreach_exti_irq { | |||
| 302 | (EXTI15) => { $action!(EXTI15); }; | 337 | (EXTI15) => { $action!(EXTI15); }; |
| 303 | 338 | ||
| 304 | // plus the weird ones | 339 | // plus the weird ones |
| 305 | (EXTI0_1) => { $action!( EXTI0_1 ); }; | 340 | (EXTI0_1) => { $action!(EXTI0_1); }; |
| 306 | (EXTI15_10) => { $action!(EXTI15_10); }; | 341 | (EXTI15_10) => { $action!(EXTI15_10); }; |
| 307 | (EXTI15_4) => { $action!(EXTI15_4); }; | 342 | (EXTI15_4) => { $action!(EXTI15_4); }; |
| 308 | (EXTI1_0) => { $action!(EXTI1_0); }; | 343 | (EXTI1_0) => { $action!(EXTI1_0); }; |
| @@ -315,57 +350,67 @@ macro_rules! foreach_exti_irq { | |||
| 315 | }; | 350 | }; |
| 316 | } | 351 | } |
| 317 | 352 | ||
| 318 | macro_rules! impl_irq { | 353 | ///EXTI interrupt handler. All EXTI interrupt vectors should be bound to this handler. |
| 319 | ($e:ident) => { | 354 | /// |
| 320 | #[allow(non_snake_case)] | 355 | /// It is generic over the [Interrupt](InterruptType) rather |
| 321 | #[cfg(feature = "rt")] | 356 | /// than the [Channel] because it should not be bound multiple |
| 322 | #[interrupt] | 357 | /// times to the same vector on chips which multiplex multiple EXTI interrupts into one vector. |
| 323 | unsafe fn $e() { | 358 | // |
| 324 | on_irq() | 359 | // It technically doesn't need to be generic at all, except to satisfy the generic argument |
| 325 | } | 360 | // of [Handler]. All EXTI interrupts eventually land in the same on_irq() function. |
| 326 | }; | 361 | pub struct InterruptHandler<T: crate::interrupt::typelevel::Interrupt> { |
| 362 | _phantom: PhantomData<T>, | ||
| 327 | } | 363 | } |
| 328 | 364 | ||
| 329 | foreach_exti_irq!(impl_irq); | 365 | impl<T: InterruptType> Handler<T> for InterruptHandler<T> { |
| 366 | unsafe fn on_interrupt() { | ||
| 367 | on_irq() | ||
| 368 | } | ||
| 369 | } | ||
| 330 | 370 | ||
| 331 | trait SealedChannel {} | 371 | trait SealedChannel {} |
| 332 | 372 | ||
| 333 | /// EXTI channel trait. | 373 | /// EXTI channel trait. |
| 334 | #[allow(private_bounds)] | 374 | #[allow(private_bounds)] |
| 335 | pub trait Channel: PeripheralType + SealedChannel + Sized { | 375 | pub trait Channel: PeripheralType + SealedChannel + Sized { |
| 336 | /// Get the EXTI channel number. | 376 | /// EXTI channel number. |
| 337 | fn number(&self) -> PinNumber; | 377 | fn number(&self) -> PinNumber; |
| 378 | /// [Enum-level Interrupt](InterruptEnum), which may be the same for multiple channels. | ||
| 379 | fn irq(&self) -> InterruptEnum; | ||
| 380 | /// [Type-level Interrupt](InterruptType), which may be the same for multiple channels. | ||
| 381 | type IRQ: InterruptType; | ||
| 338 | } | 382 | } |
| 339 | 383 | ||
| 340 | /// Type-erased EXTI channel. | 384 | //Doc isn't hidden in order to surface the explanation to users, even though it's completely inoperable, not just deprecated. |
| 385 | //Entire type along with doc can probably be removed after deprecation has appeared in a release once. | ||
| 386 | /// Deprecated type-erased EXTI channel. | ||
| 341 | /// | 387 | /// |
| 342 | /// This represents ownership over any EXTI channel, known at runtime. | 388 | /// Support for AnyChannel was removed in order to support manually bindable EXTI interrupts via bind_interrupts; [ExtiInput::new()] |
| 389 | /// must know the required IRQ at compile time, and therefore cannot support type-erased channels. | ||
| 390 | #[deprecated = "type-erased EXTI channels are no longer supported, in order to support manually bindable EXTI interrupts (more info: https://github.com/embassy-rs/embassy/pull/4922)"] | ||
| 343 | pub struct AnyChannel { | 391 | pub struct AnyChannel { |
| 392 | #[allow(unused)] | ||
| 344 | number: PinNumber, | 393 | number: PinNumber, |
| 345 | } | 394 | } |
| 346 | 395 | ||
| 347 | impl_peripheral!(AnyChannel); | ||
| 348 | impl SealedChannel for AnyChannel {} | ||
| 349 | impl Channel for AnyChannel { | ||
| 350 | fn number(&self) -> PinNumber { | ||
| 351 | self.number | ||
| 352 | } | ||
| 353 | } | ||
| 354 | |||
| 355 | macro_rules! impl_exti { | 396 | macro_rules! impl_exti { |
| 356 | ($type:ident, $number:expr) => { | 397 | ($type:ident, $number:expr) => { |
| 357 | impl SealedChannel for peripherals::$type {} | 398 | impl SealedChannel for crate::peripherals::$type {} |
| 358 | impl Channel for peripherals::$type { | 399 | impl Channel for crate::peripherals::$type { |
| 359 | fn number(&self) -> PinNumber { | 400 | fn number(&self) -> PinNumber { |
| 360 | $number | 401 | $number |
| 361 | } | 402 | } |
| 403 | fn irq(&self) -> InterruptEnum { | ||
| 404 | crate::_generated::peripheral_interrupts::EXTI::$type::IRQ | ||
| 405 | } | ||
| 406 | type IRQ = crate::_generated::peripheral_interrupts::EXTI::$type; | ||
| 362 | } | 407 | } |
| 363 | 408 | ||
| 364 | impl From<peripherals::$type> for AnyChannel { | 409 | //Still here to surface deprecation messages to the user - remove when removing AnyChannel |
| 365 | fn from(val: peripherals::$type) -> Self { | 410 | #[allow(deprecated)] |
| 366 | Self { | 411 | impl From<crate::peripherals::$type> for AnyChannel { |
| 367 | number: val.number() as PinNumber, | 412 | fn from(_val: crate::peripherals::$type) -> Self { |
| 368 | } | 413 | Self { number: $number } |
| 369 | } | 414 | } |
| 370 | } | 415 | } |
| 371 | }; | 416 | }; |
diff --git a/embassy-stm32/src/flash/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/h7.rs b/embassy-stm32/src/flash/h7.rs index 8a43cce3f..b342f4a83 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs | |||
| @@ -1,10 +1,31 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{Ordering, fence}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 5 | use pac::flash::regs::Sr; | ||
| 6 | |||
| 4 | use super::{BANK1_REGION, FLASH_REGIONS, FlashSector, WRITE_SIZE}; | 7 | use super::{BANK1_REGION, FLASH_REGIONS, FlashSector, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 8 | use crate::flash::Error; |
| 6 | use crate::pac; | 9 | use crate::pac; |
| 7 | 10 | ||
| 11 | static WAKER: AtomicWaker = AtomicWaker::new(); | ||
| 12 | |||
| 13 | pub(crate) unsafe fn on_interrupt() { | ||
| 14 | // Clear IRQ flags | ||
| 15 | pac::FLASH.bank(0).ccr().write(|w| { | ||
| 16 | w.set_clr_eop(true); | ||
| 17 | w.set_clr_operr(true); | ||
| 18 | }); | ||
| 19 | if is_dual_bank() { | ||
| 20 | pac::FLASH.bank(1).ccr().write(|w| { | ||
| 21 | w.set_clr_eop(true); | ||
| 22 | w.set_clr_operr(true); | ||
| 23 | }); | ||
| 24 | } | ||
| 25 | |||
| 26 | WAKER.wake(); | ||
| 27 | } | ||
| 28 | |||
| 8 | const fn is_dual_bank() -> bool { | 29 | const fn is_dual_bank() -> bool { |
| 9 | FLASH_REGIONS.len() >= 2 | 30 | FLASH_REGIONS.len() >= 2 |
| 10 | } | 31 | } |
| @@ -29,12 +50,68 @@ pub(crate) unsafe fn unlock() { | |||
| 29 | } | 50 | } |
| 30 | } | 51 | } |
| 31 | 52 | ||
| 53 | pub(crate) unsafe fn enable_write() { | ||
| 54 | enable_blocking_write(); | ||
| 55 | } | ||
| 56 | |||
| 57 | pub(crate) unsafe fn disable_write() { | ||
| 58 | disable_blocking_write(); | ||
| 59 | } | ||
| 60 | |||
| 32 | pub(crate) unsafe fn enable_blocking_write() { | 61 | pub(crate) unsafe fn enable_blocking_write() { |
| 33 | assert_eq!(0, WRITE_SIZE % 4); | 62 | assert_eq!(0, WRITE_SIZE % 4); |
| 34 | } | 63 | } |
| 35 | 64 | ||
| 36 | pub(crate) unsafe fn disable_blocking_write() {} | 65 | pub(crate) unsafe fn disable_blocking_write() {} |
| 37 | 66 | ||
| 67 | pub(crate) async unsafe fn write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { | ||
| 68 | // We cannot have the write setup sequence in begin_write as it depends on the address | ||
| 69 | let bank = if start_address < BANK1_REGION.end() { | ||
| 70 | pac::FLASH.bank(0) | ||
| 71 | } else { | ||
| 72 | pac::FLASH.bank(1) | ||
| 73 | }; | ||
| 74 | bank.cr().write(|w| { | ||
| 75 | w.set_pg(true); | ||
| 76 | #[cfg(flash_h7)] | ||
| 77 | w.set_psize(2); // 32 bits at once | ||
| 78 | w.set_eopie(true); | ||
| 79 | w.set_operrie(true); | ||
| 80 | }); | ||
| 81 | cortex_m::asm::isb(); | ||
| 82 | cortex_m::asm::dsb(); | ||
| 83 | fence(Ordering::SeqCst); | ||
| 84 | |||
| 85 | let mut res = None; | ||
| 86 | let mut address = start_address; | ||
| 87 | for val in buf.chunks(4) { | ||
| 88 | write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into()))); | ||
| 89 | address += val.len() as u32; | ||
| 90 | |||
| 91 | res = Some(wait_ready(bank).await); | ||
| 92 | bank.sr().modify(|w| { | ||
| 93 | if w.eop() { | ||
| 94 | w.set_eop(true); | ||
| 95 | } | ||
| 96 | }); | ||
| 97 | if unwrap!(res).is_err() { | ||
| 98 | break; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | cortex_m::asm::isb(); | ||
| 103 | cortex_m::asm::dsb(); | ||
| 104 | fence(Ordering::SeqCst); | ||
| 105 | |||
| 106 | bank.cr().write(|w| { | ||
| 107 | w.set_pg(false); | ||
| 108 | w.set_eopie(false); | ||
| 109 | w.set_operrie(false); | ||
| 110 | }); | ||
| 111 | |||
| 112 | unwrap!(res) | ||
| 113 | } | ||
| 114 | |||
| 38 | pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { | 115 | pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { |
| 39 | // We cannot have the write setup sequence in begin_write as it depends on the address | 116 | // We cannot have the write setup sequence in begin_write as it depends on the address |
| 40 | let bank = if start_address < BANK1_REGION.end() { | 117 | let bank = if start_address < BANK1_REGION.end() { |
| @@ -77,6 +154,36 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) | |||
| 77 | unwrap!(res) | 154 | unwrap!(res) |
| 78 | } | 155 | } |
| 79 | 156 | ||
| 157 | pub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Error> { | ||
| 158 | let bank = pac::FLASH.bank(sector.bank as usize); | ||
| 159 | bank.cr().modify(|w| { | ||
| 160 | w.set_ser(true); | ||
| 161 | #[cfg(flash_h7)] | ||
| 162 | w.set_snb(sector.index_in_bank); | ||
| 163 | #[cfg(flash_h7ab)] | ||
| 164 | w.set_ssn(sector.index_in_bank); | ||
| 165 | w.set_eopie(true); | ||
| 166 | w.set_operrie(true); | ||
| 167 | }); | ||
| 168 | |||
| 169 | bank.cr().modify(|w| { | ||
| 170 | w.set_start(true); | ||
| 171 | }); | ||
| 172 | |||
| 173 | cortex_m::asm::isb(); | ||
| 174 | cortex_m::asm::dsb(); | ||
| 175 | fence(Ordering::SeqCst); | ||
| 176 | |||
| 177 | let ret: Result<(), Error> = wait_ready(bank).await; | ||
| 178 | bank.cr().modify(|w| { | ||
| 179 | w.set_ser(false); | ||
| 180 | w.set_eopie(false); | ||
| 181 | w.set_operrie(false); | ||
| 182 | }); | ||
| 183 | bank_clear_all_err(bank); | ||
| 184 | ret | ||
| 185 | } | ||
| 186 | |||
| 80 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { | 187 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { |
| 81 | let bank = pac::FLASH.bank(sector.bank as usize); | 188 | let bank = pac::FLASH.bank(sector.bank as usize); |
| 82 | bank.cr().modify(|w| { | 189 | bank.cr().modify(|w| { |
| @@ -112,46 +219,59 @@ unsafe fn bank_clear_all_err(bank: pac::flash::Bank) { | |||
| 112 | bank.sr().modify(|_| {}); | 219 | bank.sr().modify(|_| {}); |
| 113 | } | 220 | } |
| 114 | 221 | ||
| 222 | async fn wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { | ||
| 223 | use core::future::poll_fn; | ||
| 224 | use core::task::Poll; | ||
| 225 | |||
| 226 | poll_fn(|cx| { | ||
| 227 | WAKER.register(cx.waker()); | ||
| 228 | |||
| 229 | let sr = bank.sr().read(); | ||
| 230 | if !sr.bsy() && !sr.qw() { | ||
| 231 | Poll::Ready(get_result(sr)) | ||
| 232 | } else { | ||
| 233 | return Poll::Pending; | ||
| 234 | } | ||
| 235 | }) | ||
| 236 | .await | ||
| 237 | } | ||
| 238 | |||
| 115 | unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { | 239 | unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { |
| 116 | loop { | 240 | loop { |
| 117 | let sr = bank.sr().read(); | 241 | let sr = bank.sr().read(); |
| 118 | 242 | ||
| 119 | if !sr.bsy() && !sr.qw() { | 243 | if !sr.bsy() && !sr.qw() { |
| 120 | if sr.wrperr() { | 244 | return get_result(sr); |
| 121 | return Err(Error::Protected); | ||
| 122 | } | ||
| 123 | if sr.pgserr() { | ||
| 124 | error!("pgserr"); | ||
| 125 | return Err(Error::Seq); | ||
| 126 | } | ||
| 127 | if sr.incerr() { | ||
| 128 | // writing to a different address when programming 256 bit word was not finished | ||
| 129 | error!("incerr"); | ||
| 130 | return Err(Error::Seq); | ||
| 131 | } | ||
| 132 | if sr.crcrderr() { | ||
| 133 | error!("crcrderr"); | ||
| 134 | return Err(Error::Seq); | ||
| 135 | } | ||
| 136 | if sr.operr() { | ||
| 137 | return Err(Error::Prog); | ||
| 138 | } | ||
| 139 | if sr.sneccerr1() { | ||
| 140 | // single ECC error | ||
| 141 | return Err(Error::Prog); | ||
| 142 | } | ||
| 143 | if sr.dbeccerr() { | ||
| 144 | // double ECC error | ||
| 145 | return Err(Error::Prog); | ||
| 146 | } | ||
| 147 | if sr.rdperr() { | ||
| 148 | return Err(Error::Protected); | ||
| 149 | } | ||
| 150 | if sr.rdserr() { | ||
| 151 | return Err(Error::Protected); | ||
| 152 | } | ||
| 153 | |||
| 154 | return Ok(()); | ||
| 155 | } | 245 | } |
| 156 | } | 246 | } |
| 157 | } | 247 | } |
| 248 | |||
| 249 | fn get_result(sr: Sr) -> Result<(), Error> { | ||
| 250 | if sr.wrperr() { | ||
| 251 | Err(Error::Protected) | ||
| 252 | } else if sr.pgserr() { | ||
| 253 | error!("pgserr"); | ||
| 254 | Err(Error::Seq) | ||
| 255 | } else if sr.incerr() { | ||
| 256 | // writing to a different address when programming 256 bit word was not finished | ||
| 257 | error!("incerr"); | ||
| 258 | Err(Error::Seq) | ||
| 259 | } else if sr.crcrderr() { | ||
| 260 | error!("crcrderr"); | ||
| 261 | Err(Error::Seq) | ||
| 262 | } else if sr.operr() { | ||
| 263 | Err(Error::Prog) | ||
| 264 | } else if sr.sneccerr1() { | ||
| 265 | // single ECC error | ||
| 266 | Err(Error::Prog) | ||
| 267 | } else if sr.dbeccerr() { | ||
| 268 | // double ECC error | ||
| 269 | Err(Error::Prog) | ||
| 270 | } else if sr.rdperr() { | ||
| 271 | Err(Error::Protected) | ||
| 272 | } else if sr.rdserr() { | ||
| 273 | Err(Error::Protected) | ||
| 274 | } else { | ||
| 275 | Ok(()) | ||
| 276 | } | ||
| 277 | } | ||
diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 3e74d857a..6211a37b7 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs | |||
| @@ -1,14 +1,14 @@ | |||
| 1 | //! Flash memory (FLASH) | 1 | //! Flash memory (FLASH) |
| 2 | use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; | 2 | use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; |
| 3 | 3 | ||
| 4 | #[cfg(flash_f4)] | 4 | #[cfg(any(flash_f4, flash_h7, flash_h7ab))] |
| 5 | mod asynch; | 5 | mod asynch; |
| 6 | #[cfg(flash)] | 6 | #[cfg(flash)] |
| 7 | mod common; | 7 | mod common; |
| 8 | #[cfg(eeprom)] | 8 | #[cfg(eeprom)] |
| 9 | mod eeprom; | 9 | mod eeprom; |
| 10 | 10 | ||
| 11 | #[cfg(flash_f4)] | 11 | #[cfg(any(flash_f4, flash_h7, flash_h7ab))] |
| 12 | pub use asynch::InterruptHandler; | 12 | pub use asynch::InterruptHandler; |
| 13 | #[cfg(flash)] | 13 | #[cfg(flash)] |
| 14 | pub use common::*; | 14 | pub use common::*; |
| @@ -99,6 +99,7 @@ compile_error!("The 'eeprom' cfg is enabled for a non-L0/L1 chip family. This is | |||
| 99 | #[cfg_attr(flash_f4, path = "f4.rs")] | 99 | #[cfg_attr(flash_f4, path = "f4.rs")] |
| 100 | #[cfg_attr(flash_f7, path = "f7.rs")] | 100 | #[cfg_attr(flash_f7, path = "f7.rs")] |
| 101 | #[cfg_attr(any(flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4), path = "g.rs")] | 101 | #[cfg_attr(any(flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4), path = "g.rs")] |
| 102 | #[cfg_attr(flash_c0, path = "c.rs")] | ||
| 102 | #[cfg_attr(flash_h7, path = "h7.rs")] | 103 | #[cfg_attr(flash_h7, path = "h7.rs")] |
| 103 | #[cfg_attr(flash_h7ab, path = "h7.rs")] | 104 | #[cfg_attr(flash_h7ab, path = "h7.rs")] |
| 104 | #[cfg_attr(any(flash_u5, flash_wba), path = "u5.rs")] | 105 | #[cfg_attr(any(flash_u5, flash_wba), path = "u5.rs")] |
| @@ -108,7 +109,7 @@ compile_error!("The 'eeprom' cfg is enabled for a non-L0/L1 chip family. This is | |||
| 108 | #[cfg_attr( | 109 | #[cfg_attr( |
| 109 | not(any( | 110 | not(any( |
| 110 | flash_l0, flash_l1, flash_l4, flash_l5, flash_wl, flash_wb, flash_f0, flash_f1, flash_f2, flash_f3, flash_f4, | 111 | flash_l0, flash_l1, flash_l4, flash_l5, flash_wl, flash_wb, flash_f0, flash_f1, flash_f2, flash_f3, flash_f4, |
| 111 | flash_f7, flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4, flash_h7, flash_h7ab, flash_u5, | 112 | flash_f7, flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4, flash_c0, flash_h7, flash_h7ab, flash_u5, |
| 112 | flash_wba, flash_h50, flash_u0, flash_h5, | 113 | flash_wba, flash_h50, flash_u0, flash_h5, |
| 113 | )), | 114 | )), |
| 114 | path = "other.rs" | 115 | path = "other.rs" |
diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index 17c5a9962..e7d4e9ad3 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs | |||
| @@ -812,15 +812,19 @@ pub type PinNumber = u8; | |||
| 812 | #[cfg(stm32n6)] | 812 | #[cfg(stm32n6)] |
| 813 | pub type PinNumber = u16; | 813 | pub type PinNumber = u16; |
| 814 | 814 | ||
| 815 | /// GPIO pin trait. | 815 | /// Pin that can be used to configure an [ExtiInput](crate::exti::ExtiInput). This trait is lost when converting to [AnyPin]. |
| 816 | #[cfg(feature = "exti")] | ||
| 816 | #[allow(private_bounds)] | 817 | #[allow(private_bounds)] |
| 817 | pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static { | 818 | pub trait ExtiPin: PeripheralType + SealedPin { |
| 818 | /// EXTI channel assigned to this pin. | 819 | /// EXTI channel assigned to this pin. |
| 819 | /// | 820 | /// |
| 820 | /// For example, PC4 uses EXTI4. | 821 | /// For example, PC4 uses EXTI4. |
| 821 | #[cfg(feature = "exti")] | ||
| 822 | type ExtiChannel: crate::exti::Channel; | 822 | type ExtiChannel: crate::exti::Channel; |
| 823 | } | ||
| 823 | 824 | ||
| 825 | /// GPIO pin trait. | ||
| 826 | #[allow(private_bounds)] | ||
| 827 | pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static { | ||
| 824 | /// Number of the pin within the port (0..31) | 828 | /// Number of the pin within the port (0..31) |
| 825 | #[inline] | 829 | #[inline] |
| 826 | fn pin(&self) -> PinNumber { | 830 | fn pin(&self) -> PinNumber { |
| @@ -834,7 +838,7 @@ pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static { | |||
| 834 | } | 838 | } |
| 835 | } | 839 | } |
| 836 | 840 | ||
| 837 | /// Type-erased GPIO pin | 841 | /// Type-erased GPIO pin. |
| 838 | pub struct AnyPin { | 842 | pub struct AnyPin { |
| 839 | pin_port: PinNumber, | 843 | pin_port: PinNumber, |
| 840 | } | 844 | } |
| @@ -862,10 +866,7 @@ impl AnyPin { | |||
| 862 | } | 866 | } |
| 863 | 867 | ||
| 864 | impl_peripheral!(AnyPin); | 868 | impl_peripheral!(AnyPin); |
| 865 | impl Pin for AnyPin { | 869 | impl Pin for AnyPin {} |
| 866 | #[cfg(feature = "exti")] | ||
| 867 | type ExtiChannel = crate::exti::AnyChannel; | ||
| 868 | } | ||
| 869 | impl SealedPin for AnyPin { | 870 | impl SealedPin for AnyPin { |
| 870 | #[inline] | 871 | #[inline] |
| 871 | fn pin_port(&self) -> PinNumber { | 872 | fn pin_port(&self) -> PinNumber { |
| @@ -878,7 +879,9 @@ impl SealedPin for AnyPin { | |||
| 878 | foreach_pin!( | 879 | foreach_pin!( |
| 879 | ($pin_name:ident, $port_name:ident, $port_num:expr, $pin_num:expr, $exti_ch:ident) => { | 880 | ($pin_name:ident, $port_name:ident, $port_num:expr, $pin_num:expr, $exti_ch:ident) => { |
| 880 | impl Pin for peripherals::$pin_name { | 881 | impl Pin for peripherals::$pin_name { |
| 881 | #[cfg(feature = "exti")] | 882 | } |
| 883 | #[cfg(feature = "exti")] | ||
| 884 | impl ExtiPin for peripherals::$pin_name { | ||
| 882 | type ExtiChannel = peripherals::$exti_ch; | 885 | type ExtiChannel = peripherals::$exti_ch; |
| 883 | } | 886 | } |
| 884 | impl SealedPin for peripherals::$pin_name { | 887 | impl SealedPin for peripherals::$pin_name { |
diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs index 4d3a5d68d..e62de0454 100644 --- a/embassy-stm32/src/hsem/mod.rs +++ b/embassy-stm32/src/hsem/mod.rs | |||
| @@ -1,14 +1,22 @@ | |||
| 1 | //! Hardware Semaphore (HSEM) | 1 | //! Hardware Semaphore (HSEM) |
| 2 | 2 | ||
| 3 | use core::future::poll_fn; | ||
| 4 | use core::marker::PhantomData; | ||
| 5 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 6 | use core::task::Poll; | ||
| 7 | |||
| 8 | #[cfg(all(stm32wb, feature = "low-power"))] | ||
| 9 | use critical_section::CriticalSection; | ||
| 3 | use embassy_hal_internal::PeripheralType; | 10 | use embassy_hal_internal::PeripheralType; |
| 11 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 4 | 12 | ||
| 5 | // TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. | 13 | // TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. |
| 6 | // Those MCUs have a different HSEM implementation (Secure semaphore lock support, | 14 | // Those MCUs have a different HSEM implementation (Secure semaphore lock support, |
| 7 | // Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), | 15 | // Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), |
| 8 | // which is not yet supported by this code. | 16 | // which is not yet supported by this code. |
| 9 | use crate::Peri; | 17 | use crate::Peri; |
| 10 | use crate::pac; | ||
| 11 | use crate::rcc::{self, RccPeripheral}; | 18 | use crate::rcc::{self, RccPeripheral}; |
| 19 | use crate::{interrupt, pac}; | ||
| 12 | 20 | ||
| 13 | /// HSEM error. | 21 | /// HSEM error. |
| 14 | #[derive(Debug)] | 22 | #[derive(Debug)] |
| @@ -41,63 +49,152 @@ pub enum CoreId { | |||
| 41 | Core1 = 0x8, | 49 | Core1 = 0x8, |
| 42 | } | 50 | } |
| 43 | 51 | ||
| 44 | /// Get the current core id | 52 | impl CoreId { |
| 45 | /// This code assume that it is only executed on a Cortex-M M0+, M4 or M7 core. | 53 | /// Get the current core id |
| 46 | #[inline(always)] | 54 | /// This code assume that it is only executed on a Cortex-M M0+, M4 or M7 core. |
| 47 | pub fn get_current_coreid() -> CoreId { | 55 | pub fn current() -> Self { |
| 48 | let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() }; | 56 | let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() }; |
| 49 | match (cpuid & 0x000000F0) >> 4 { | 57 | match (cpuid & 0x000000F0) >> 4 { |
| 50 | #[cfg(any(stm32wb, stm32wl))] | 58 | #[cfg(any(stm32wb, stm32wl))] |
| 51 | 0x0 => CoreId::Core1, | 59 | 0x0 => CoreId::Core1, |
| 52 | 60 | ||
| 53 | #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))] | 61 | #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))] |
| 54 | 0x4 => CoreId::Core0, | 62 | 0x4 => CoreId::Core0, |
| 55 | 63 | ||
| 56 | #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] | 64 | #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] |
| 57 | 0x4 => CoreId::Core1, | 65 | 0x4 => CoreId::Core1, |
| 58 | 66 | ||
| 59 | #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] | 67 | #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] |
| 60 | 0x7 => CoreId::Core0, | 68 | 0x7 => CoreId::Core0, |
| 61 | _ => panic!("Unknown Cortex-M core"), | 69 | _ => panic!("Unknown Cortex-M core"), |
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | /// Translates the core ID to an index into the interrupt registers. | ||
| 74 | pub fn to_index(&self) -> usize { | ||
| 75 | match &self { | ||
| 76 | CoreId::Core0 => 0, | ||
| 77 | #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))] | ||
| 78 | CoreId::Core1 => 1, | ||
| 79 | } | ||
| 62 | } | 80 | } |
| 63 | } | 81 | } |
| 64 | 82 | ||
| 65 | /// Translates the core ID to an index into the interrupt registers. | 83 | #[cfg(not(all(stm32wb, feature = "low-power")))] |
| 66 | #[inline(always)] | 84 | const PUB_CHANNELS: usize = 6; |
| 67 | fn core_id_to_index(core: CoreId) -> usize { | 85 | |
| 68 | match core { | 86 | #[cfg(all(stm32wb, feature = "low-power"))] |
| 69 | CoreId::Core0 => 0, | 87 | const PUB_CHANNELS: usize = 4; |
| 70 | #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))] | 88 | |
| 71 | CoreId::Core1 => 1, | 89 | /// TX interrupt handler. |
| 90 | pub struct HardwareSemaphoreInterruptHandler<T: Instance> { | ||
| 91 | _phantom: PhantomData<T>, | ||
| 92 | } | ||
| 93 | |||
| 94 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for HardwareSemaphoreInterruptHandler<T> { | ||
| 95 | unsafe fn on_interrupt() { | ||
| 96 | let core_id = CoreId::current(); | ||
| 97 | |||
| 98 | for number in 0..5 { | ||
| 99 | if T::regs().isr(core_id.to_index()).read().isf(number as usize) { | ||
| 100 | T::regs() | ||
| 101 | .icr(core_id.to_index()) | ||
| 102 | .write(|w| w.set_isc(number as usize, true)); | ||
| 103 | |||
| 104 | T::regs() | ||
| 105 | .ier(core_id.to_index()) | ||
| 106 | .modify(|w| w.set_ise(number as usize, false)); | ||
| 107 | |||
| 108 | T::state().waker_for(number).wake(); | ||
| 109 | } | ||
| 110 | } | ||
| 72 | } | 111 | } |
| 73 | } | 112 | } |
| 74 | 113 | ||
| 75 | /// HSEM driver | 114 | /// Hardware semaphore mutex |
| 76 | pub struct HardwareSemaphore<'d, T: Instance> { | 115 | pub struct HardwareSemaphoreMutex<'a, T: Instance> { |
| 77 | _peri: Peri<'d, T>, | 116 | index: u8, |
| 117 | process_id: u8, | ||
| 118 | _lifetime: PhantomData<&'a mut T>, | ||
| 78 | } | 119 | } |
| 79 | 120 | ||
| 80 | impl<'d, T: Instance> HardwareSemaphore<'d, T> { | 121 | impl<'a, T: Instance> Drop for HardwareSemaphoreMutex<'a, T> { |
| 81 | /// Creates a new HardwareSemaphore instance. | 122 | fn drop(&mut self) { |
| 82 | pub fn new(peripheral: Peri<'d, T>) -> Self { | 123 | HardwareSemaphoreChannel::<'a, T> { |
| 83 | rcc::enable_and_reset::<T>(); | 124 | index: self.index, |
| 125 | _lifetime: PhantomData, | ||
| 126 | } | ||
| 127 | .unlock(self.process_id); | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | /// Hardware semaphore channel | ||
| 132 | pub struct HardwareSemaphoreChannel<'a, T: Instance> { | ||
| 133 | index: u8, | ||
| 134 | _lifetime: PhantomData<&'a mut T>, | ||
| 135 | } | ||
| 136 | |||
| 137 | impl<'a, T: Instance> HardwareSemaphoreChannel<'a, T> { | ||
| 138 | pub(crate) const fn new(number: u8) -> Self { | ||
| 139 | core::assert!(number > 0 && number <= 6); | ||
| 140 | |||
| 141 | Self { | ||
| 142 | index: number - 1, | ||
| 143 | _lifetime: PhantomData, | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Locks the semaphore. | ||
| 148 | /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to | ||
| 149 | /// check if the lock has been successful, carried out from the HSEM_Rx register. | ||
| 150 | pub async fn lock(&mut self, process_id: u8) -> HardwareSemaphoreMutex<'a, T> { | ||
| 151 | let core_id = CoreId::current(); | ||
| 152 | |||
| 153 | poll_fn(|cx| { | ||
| 154 | T::state().waker_for(self.index).register(cx.waker()); | ||
| 155 | |||
| 156 | compiler_fence(Ordering::SeqCst); | ||
| 157 | |||
| 158 | T::regs() | ||
| 159 | .ier(core_id.to_index()) | ||
| 160 | .modify(|w| w.set_ise(self.index as usize, true)); | ||
| 161 | |||
| 162 | match self.try_lock(process_id) { | ||
| 163 | Some(mutex) => Poll::Ready(mutex), | ||
| 164 | None => Poll::Pending, | ||
| 165 | } | ||
| 166 | }) | ||
| 167 | .await | ||
| 168 | } | ||
| 84 | 169 | ||
| 85 | HardwareSemaphore { _peri: peripheral } | 170 | /// Try to lock the semaphor |
| 171 | /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to | ||
| 172 | /// check if the lock has been successful, carried out from the HSEM_Rx register. | ||
| 173 | pub fn try_lock(&mut self, process_id: u8) -> Option<HardwareSemaphoreMutex<'a, T>> { | ||
| 174 | if self.two_step_lock(process_id).is_ok() { | ||
| 175 | Some(HardwareSemaphoreMutex { | ||
| 176 | index: self.index, | ||
| 177 | process_id: process_id, | ||
| 178 | _lifetime: PhantomData, | ||
| 179 | }) | ||
| 180 | } else { | ||
| 181 | None | ||
| 182 | } | ||
| 86 | } | 183 | } |
| 87 | 184 | ||
| 88 | /// Locks the semaphore. | 185 | /// Locks the semaphore. |
| 89 | /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to | 186 | /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to |
| 90 | /// check if the lock has been successful, carried out from the HSEM_Rx register. | 187 | /// check if the lock has been successful, carried out from the HSEM_Rx register. |
| 91 | pub fn two_step_lock(&mut self, sem_id: u8, process_id: u8) -> Result<(), HsemError> { | 188 | pub fn two_step_lock(&mut self, process_id: u8) -> Result<(), HsemError> { |
| 92 | T::regs().r(sem_id as usize).write(|w| { | 189 | T::regs().r(self.index as usize).write(|w| { |
| 93 | w.set_procid(process_id); | 190 | w.set_procid(process_id); |
| 94 | w.set_coreid(get_current_coreid() as u8); | 191 | w.set_coreid(CoreId::current() as u8); |
| 95 | w.set_lock(true); | 192 | w.set_lock(true); |
| 96 | }); | 193 | }); |
| 97 | let reg = T::regs().r(sem_id as usize).read(); | 194 | let reg = T::regs().r(self.index as usize).read(); |
| 98 | match ( | 195 | match ( |
| 99 | reg.lock(), | 196 | reg.lock(), |
| 100 | reg.coreid() == get_current_coreid() as u8, | 197 | reg.coreid() == CoreId::current() as u8, |
| 101 | reg.procid() == process_id, | 198 | reg.procid() == process_id, |
| 102 | ) { | 199 | ) { |
| 103 | (true, true, true) => Ok(()), | 200 | (true, true, true) => Ok(()), |
| @@ -108,9 +205,9 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { | |||
| 108 | /// Locks the semaphore. | 205 | /// Locks the semaphore. |
| 109 | /// The 1-step procedure consists in a read to lock and check the semaphore in a single step, | 206 | /// The 1-step procedure consists in a read to lock and check the semaphore in a single step, |
| 110 | /// carried out from the HSEM_RLRx register. | 207 | /// carried out from the HSEM_RLRx register. |
| 111 | pub fn one_step_lock(&mut self, sem_id: u8) -> Result<(), HsemError> { | 208 | pub fn one_step_lock(&mut self) -> Result<(), HsemError> { |
| 112 | let reg = T::regs().rlr(sem_id as usize).read(); | 209 | let reg = T::regs().rlr(self.index as usize).read(); |
| 113 | match (reg.lock(), reg.coreid() == get_current_coreid() as u8, reg.procid()) { | 210 | match (reg.lock(), reg.coreid() == CoreId::current() as u8, reg.procid()) { |
| 114 | (false, true, 0) => Ok(()), | 211 | (false, true, 0) => Ok(()), |
| 115 | _ => Err(HsemError::LockFailed), | 212 | _ => Err(HsemError::LockFailed), |
| 116 | } | 213 | } |
| @@ -119,14 +216,60 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { | |||
| 119 | /// Unlocks the semaphore. | 216 | /// Unlocks the semaphore. |
| 120 | /// Unlocking a semaphore is a protected process, to prevent accidental clearing by a AHB bus | 217 | /// Unlocking a semaphore is a protected process, to prevent accidental clearing by a AHB bus |
| 121 | /// core ID or by a process not having the semaphore lock right. | 218 | /// core ID or by a process not having the semaphore lock right. |
| 122 | pub fn unlock(&mut self, sem_id: u8, process_id: u8) { | 219 | pub fn unlock(&mut self, process_id: u8) { |
| 123 | T::regs().r(sem_id as usize).write(|w| { | 220 | T::regs().r(self.index as usize).write(|w| { |
| 124 | w.set_procid(process_id); | 221 | w.set_procid(process_id); |
| 125 | w.set_coreid(get_current_coreid() as u8); | 222 | w.set_coreid(CoreId::current() as u8); |
| 126 | w.set_lock(false); | 223 | w.set_lock(false); |
| 127 | }); | 224 | }); |
| 128 | } | 225 | } |
| 129 | 226 | ||
| 227 | /// Return the channel number | ||
| 228 | pub const fn channel(&self) -> u8 { | ||
| 229 | self.index + 1 | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | /// HSEM driver | ||
| 234 | pub struct HardwareSemaphore<T: Instance> { | ||
| 235 | _type: PhantomData<T>, | ||
| 236 | } | ||
| 237 | |||
| 238 | impl<T: Instance> HardwareSemaphore<T> { | ||
| 239 | /// Creates a new HardwareSemaphore instance. | ||
| 240 | pub fn new<'d>( | ||
| 241 | _peripheral: Peri<'d, T>, | ||
| 242 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, HardwareSemaphoreInterruptHandler<T>> + 'd, | ||
| 243 | ) -> Self { | ||
| 244 | rcc::enable_and_reset::<T>(); | ||
| 245 | |||
| 246 | HardwareSemaphore { _type: PhantomData } | ||
| 247 | } | ||
| 248 | |||
| 249 | /// Get a single channel, and keep the global struct | ||
| 250 | pub const fn channel_for<'a>(&'a mut self, number: u8) -> HardwareSemaphoreChannel<'a, T> { | ||
| 251 | #[cfg(all(stm32wb, feature = "low-power"))] | ||
| 252 | core::assert!(number != 3 && number != 4); | ||
| 253 | |||
| 254 | HardwareSemaphoreChannel::new(number) | ||
| 255 | } | ||
| 256 | |||
| 257 | /// Split the global struct into channels | ||
| 258 | /// | ||
| 259 | /// If using low-power mode, channels 3 and 4 will not be returned | ||
| 260 | pub const fn split<'a>(self) -> [HardwareSemaphoreChannel<'a, T>; PUB_CHANNELS] { | ||
| 261 | [ | ||
| 262 | HardwareSemaphoreChannel::new(1), | ||
| 263 | HardwareSemaphoreChannel::new(2), | ||
| 264 | #[cfg(not(all(stm32wb, feature = "low-power")))] | ||
| 265 | HardwareSemaphoreChannel::new(3), | ||
| 266 | #[cfg(not(all(stm32wb, feature = "low-power")))] | ||
| 267 | HardwareSemaphoreChannel::new(4), | ||
| 268 | HardwareSemaphoreChannel::new(5), | ||
| 269 | HardwareSemaphoreChannel::new(6), | ||
| 270 | ] | ||
| 271 | } | ||
| 272 | |||
| 130 | /// Unlocks all semaphores. | 273 | /// Unlocks all semaphores. |
| 131 | /// All semaphores locked by a COREID can be unlocked at once by using the HSEM_CR | 274 | /// All semaphores locked by a COREID can be unlocked at once by using the HSEM_CR |
| 132 | /// register. Write COREID and correct KEY value in HSEM_CR. All locked semaphores with a | 275 | /// register. Write COREID and correct KEY value in HSEM_CR. All locked semaphores with a |
| @@ -138,11 +281,6 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { | |||
| 138 | }); | 281 | }); |
| 139 | } | 282 | } |
| 140 | 283 | ||
| 141 | /// Checks if the semaphore is locked. | ||
| 142 | pub fn is_semaphore_locked(&self, sem_id: u8) -> bool { | ||
| 143 | T::regs().r(sem_id as usize).read().lock() | ||
| 144 | } | ||
| 145 | |||
| 146 | /// Sets the clear (unlock) key | 284 | /// Sets the clear (unlock) key |
| 147 | pub fn set_clear_key(&mut self, key: u16) { | 285 | pub fn set_clear_key(&mut self, key: u16) { |
| 148 | T::regs().keyr().modify(|w| w.set_key(key)); | 286 | T::regs().keyr().modify(|w| w.set_key(key)); |
| @@ -152,38 +290,61 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { | |||
| 152 | pub fn get_clear_key(&mut self) -> u16 { | 290 | pub fn get_clear_key(&mut self) -> u16 { |
| 153 | T::regs().keyr().read().key() | 291 | T::regs().keyr().read().key() |
| 154 | } | 292 | } |
| 293 | } | ||
| 155 | 294 | ||
| 156 | /// Sets the interrupt enable bit for the semaphore. | 295 | #[cfg(all(stm32wb, feature = "low-power"))] |
| 157 | pub fn enable_interrupt(&mut self, core_id: CoreId, sem_x: usize, enable: bool) { | 296 | pub(crate) fn init_hsem(cs: CriticalSection) { |
| 158 | T::regs() | 297 | rcc::enable_and_reset_with_cs::<crate::peripherals::HSEM>(cs); |
| 159 | .ier(core_id_to_index(core_id)) | 298 | |
| 160 | .modify(|w| w.set_ise(sem_x, enable)); | 299 | unsafe { |
| 300 | crate::rcc::REFCOUNT_STOP1 = 0; | ||
| 301 | crate::rcc::REFCOUNT_STOP2 = 0; | ||
| 161 | } | 302 | } |
| 303 | } | ||
| 162 | 304 | ||
| 163 | /// Gets the interrupt flag for the semaphore. | 305 | struct State { |
| 164 | pub fn is_interrupt_active(&mut self, core_id: CoreId, sem_x: usize) -> bool { | 306 | wakers: [AtomicWaker; 6], |
| 165 | T::regs().isr(core_id_to_index(core_id)).read().isf(sem_x) | 307 | } |
| 308 | |||
| 309 | impl State { | ||
| 310 | const fn new() -> Self { | ||
| 311 | Self { | ||
| 312 | wakers: [const { AtomicWaker::new() }; 6], | ||
| 313 | } | ||
| 166 | } | 314 | } |
| 167 | 315 | ||
| 168 | /// Clears the interrupt flag for the semaphore. | 316 | const fn waker_for(&self, index: u8) -> &AtomicWaker { |
| 169 | pub fn clear_interrupt(&mut self, core_id: CoreId, sem_x: usize) { | 317 | &self.wakers[index as usize] |
| 170 | T::regs() | ||
| 171 | .icr(core_id_to_index(core_id)) | ||
| 172 | .write(|w| w.set_isc(sem_x, false)); | ||
| 173 | } | 318 | } |
| 174 | } | 319 | } |
| 175 | 320 | ||
| 176 | trait SealedInstance { | 321 | trait SealedInstance { |
| 177 | fn regs() -> pac::hsem::Hsem; | 322 | fn regs() -> pac::hsem::Hsem; |
| 323 | fn state() -> &'static State; | ||
| 178 | } | 324 | } |
| 179 | 325 | ||
| 180 | /// HSEM instance trait. | 326 | /// HSEM instance trait. |
| 181 | #[allow(private_bounds)] | 327 | #[allow(private_bounds)] |
| 182 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {} | 328 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static { |
| 329 | /// Interrupt for this peripheral. | ||
| 330 | type Interrupt: interrupt::typelevel::Interrupt; | ||
| 331 | } | ||
| 183 | 332 | ||
| 184 | impl SealedInstance for crate::peripherals::HSEM { | 333 | impl SealedInstance for crate::peripherals::HSEM { |
| 185 | fn regs() -> crate::pac::hsem::Hsem { | 334 | fn regs() -> crate::pac::hsem::Hsem { |
| 186 | crate::pac::HSEM | 335 | crate::pac::HSEM |
| 187 | } | 336 | } |
| 337 | |||
| 338 | fn state() -> &'static State { | ||
| 339 | static STATE: State = State::new(); | ||
| 340 | &STATE | ||
| 341 | } | ||
| 188 | } | 342 | } |
| 189 | impl Instance for crate::peripherals::HSEM {} | 343 | |
| 344 | foreach_interrupt!( | ||
| 345 | ($inst:ident, hsem, $block:ident, $signal_name:ident, $irq:ident) => { | ||
| 346 | impl Instance for crate::peripherals::$inst { | ||
| 347 | type Interrupt = crate::interrupt::typelevel::$irq; | ||
| 348 | } | ||
| 349 | }; | ||
| 350 | ); | ||
diff --git a/embassy-stm32/src/hspi/mod.rs b/embassy-stm32/src/hspi/mod.rs index 69baa708e..1d3560678 100644 --- a/embassy-stm32/src/hspi/mod.rs +++ b/embassy-stm32/src/hspi/mod.rs | |||
| @@ -391,7 +391,7 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { | |||
| 391 | while T::REGS.sr().read().busy() {} | 391 | while T::REGS.sr().read().busy() {} |
| 392 | 392 | ||
| 393 | T::REGS.cr().modify(|w| { | 393 | T::REGS.cr().modify(|w| { |
| 394 | w.set_fmode(0.into()); | 394 | w.set_fmode(FunctionalMode::IndirectWrite.into()); |
| 395 | }); | 395 | }); |
| 396 | 396 | ||
| 397 | // Configure alternate bytes | 397 | // Configure alternate bytes |
| @@ -498,7 +498,8 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { | |||
| 498 | w.set_dmaen(false); | 498 | w.set_dmaen(false); |
| 499 | }); | 499 | }); |
| 500 | 500 | ||
| 501 | self.configure_command(&transaction, Some(buf.len()))?; | 501 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 502 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 502 | 503 | ||
| 503 | let current_address = T::REGS.ar().read().address(); | 504 | let current_address = T::REGS.ar().read().address(); |
| 504 | let current_instruction = T::REGS.ir().read().instruction(); | 505 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -537,7 +538,8 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { | |||
| 537 | w.set_dmaen(false); | 538 | w.set_dmaen(false); |
| 538 | }); | 539 | }); |
| 539 | 540 | ||
| 540 | self.configure_command(&transaction, Some(buf.len()))?; | 541 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 542 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 541 | 543 | ||
| 542 | T::REGS | 544 | T::REGS |
| 543 | .cr() | 545 | .cr() |
| @@ -767,7 +769,8 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { | |||
| 767 | // Wait for peripheral to be free | 769 | // Wait for peripheral to be free |
| 768 | while T::REGS.sr().read().busy() {} | 770 | while T::REGS.sr().read().busy() {} |
| 769 | 771 | ||
| 770 | self.configure_command(&transaction, Some(buf.len()))?; | 772 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 773 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 771 | 774 | ||
| 772 | let current_address = T::REGS.ar().read().address(); | 775 | let current_address = T::REGS.ar().read().address(); |
| 773 | let current_instruction = T::REGS.ir().read().instruction(); | 776 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -782,16 +785,18 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { | |||
| 782 | T::REGS.ar().write(|v| v.set_address(current_address)); | 785 | T::REGS.ar().write(|v| v.set_address(current_address)); |
| 783 | } | 786 | } |
| 784 | 787 | ||
| 785 | let transfer = unsafe { | 788 | for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { |
| 786 | self.dma | 789 | let transfer = unsafe { |
| 787 | .as_mut() | 790 | self.dma |
| 788 | .unwrap() | 791 | .as_mut() |
| 789 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | 792 | .unwrap() |
| 790 | }; | 793 | .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) |
| 794 | }; | ||
| 791 | 795 | ||
| 792 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 796 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 793 | 797 | ||
| 794 | transfer.blocking_wait(); | 798 | transfer.blocking_wait(); |
| 799 | } | ||
| 795 | 800 | ||
| 796 | finish_dma(T::REGS); | 801 | finish_dma(T::REGS); |
| 797 | 802 | ||
| @@ -807,21 +812,24 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { | |||
| 807 | // Wait for peripheral to be free | 812 | // Wait for peripheral to be free |
| 808 | while T::REGS.sr().read().busy() {} | 813 | while T::REGS.sr().read().busy() {} |
| 809 | 814 | ||
| 810 | self.configure_command(&transaction, Some(buf.len()))?; | 815 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 816 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 811 | T::REGS | 817 | T::REGS |
| 812 | .cr() | 818 | .cr() |
| 813 | .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); | 819 | .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); |
| 814 | 820 | ||
| 815 | let transfer = unsafe { | 821 | for chunk in buf.chunks(0xFFFF / W::size().bytes()) { |
| 816 | self.dma | 822 | let transfer = unsafe { |
| 817 | .as_mut() | 823 | self.dma |
| 818 | .unwrap() | 824 | .as_mut() |
| 819 | .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) | 825 | .unwrap() |
| 820 | }; | 826 | .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) |
| 827 | }; | ||
| 821 | 828 | ||
| 822 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 829 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 823 | 830 | ||
| 824 | transfer.blocking_wait(); | 831 | transfer.blocking_wait(); |
| 832 | } | ||
| 825 | 833 | ||
| 826 | finish_dma(T::REGS); | 834 | finish_dma(T::REGS); |
| 827 | 835 | ||
| @@ -837,7 +845,8 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { | |||
| 837 | // Wait for peripheral to be free | 845 | // Wait for peripheral to be free |
| 838 | while T::REGS.sr().read().busy() {} | 846 | while T::REGS.sr().read().busy() {} |
| 839 | 847 | ||
| 840 | self.configure_command(&transaction, Some(buf.len()))?; | 848 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 849 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 841 | 850 | ||
| 842 | let current_address = T::REGS.ar().read().address(); | 851 | let current_address = T::REGS.ar().read().address(); |
| 843 | let current_instruction = T::REGS.ir().read().instruction(); | 852 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -852,16 +861,18 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { | |||
| 852 | T::REGS.ar().write(|v| v.set_address(current_address)); | 861 | T::REGS.ar().write(|v| v.set_address(current_address)); |
| 853 | } | 862 | } |
| 854 | 863 | ||
| 855 | let transfer = unsafe { | 864 | for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { |
| 856 | self.dma | 865 | let transfer = unsafe { |
| 857 | .as_mut() | 866 | self.dma |
| 858 | .unwrap() | 867 | .as_mut() |
| 859 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | 868 | .unwrap() |
| 860 | }; | 869 | .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) |
| 870 | }; | ||
| 861 | 871 | ||
| 862 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 872 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 863 | 873 | ||
| 864 | transfer.await; | 874 | transfer.await; |
| 875 | } | ||
| 865 | 876 | ||
| 866 | finish_dma(T::REGS); | 877 | finish_dma(T::REGS); |
| 867 | 878 | ||
| @@ -877,21 +888,25 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { | |||
| 877 | // Wait for peripheral to be free | 888 | // Wait for peripheral to be free |
| 878 | while T::REGS.sr().read().busy() {} | 889 | while T::REGS.sr().read().busy() {} |
| 879 | 890 | ||
| 880 | self.configure_command(&transaction, Some(buf.len()))?; | 891 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 892 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 881 | T::REGS | 893 | T::REGS |
| 882 | .cr() | 894 | .cr() |
| 883 | .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); | 895 | .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); |
| 884 | 896 | ||
| 885 | let transfer = unsafe { | 897 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. |
| 886 | self.dma | 898 | for chunk in buf.chunks(0xFFFF / W::size().bytes()) { |
| 887 | .as_mut() | 899 | let transfer = unsafe { |
| 888 | .unwrap() | 900 | self.dma |
| 889 | .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) | 901 | .as_mut() |
| 890 | }; | 902 | .unwrap() |
| 903 | .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) | ||
| 904 | }; | ||
| 891 | 905 | ||
| 892 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 906 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 893 | 907 | ||
| 894 | transfer.await; | 908 | transfer.await; |
| 909 | } | ||
| 895 | 910 | ||
| 896 | finish_dma(T::REGS); | 911 | finish_dma(T::REGS); |
| 897 | 912 | ||
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 4527e55b9..b2ba94e21 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); |
| @@ -819,14 +1075,12 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 819 | 1075 | ||
| 820 | /// Write. | 1076 | /// Write. |
| 821 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { | 1077 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { |
| 822 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 823 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 824 | let timeout = self.timeout(); | 1078 | let timeout = self.timeout(); |
| 825 | if write.is_empty() { | 1079 | if write.is_empty() { |
| 826 | self.write_internal(address.into(), write, true, timeout) | 1080 | self.write_internal(address.into(), write, true, timeout) |
| 827 | } else { | 1081 | } else { |
| 828 | timeout | 1082 | timeout |
| 829 | .with(self.write_dma_internal(address.into(), write, true, true, true, timeout)) | 1083 | .with(self.write_dma_internal(address.into(), write, true, true, true, false, timeout)) |
| 830 | .await | 1084 | .await |
| 831 | } | 1085 | } |
| 832 | } | 1086 | } |
| @@ -835,23 +1089,29 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 835 | /// | 1089 | /// |
| 836 | /// The buffers are concatenated in a single write transaction. | 1090 | /// The buffers are concatenated in a single write transaction. |
| 837 | pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { | 1091 | pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { |
| 838 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 839 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 840 | let timeout = self.timeout(); | 1092 | let timeout = self.timeout(); |
| 841 | 1093 | ||
| 842 | if write.is_empty() { | 1094 | if write.is_empty() { |
| 843 | return Err(Error::ZeroLengthTransfer); | 1095 | return Err(Error::ZeroLengthTransfer); |
| 844 | } | 1096 | } |
| 845 | let mut iter = write.iter(); | ||
| 846 | 1097 | ||
| 1098 | let mut iter = write.iter(); | ||
| 847 | let mut first = true; | 1099 | let mut first = true; |
| 848 | let mut current = iter.next(); | 1100 | let mut current = iter.next(); |
| 1101 | |||
| 849 | while let Some(c) = current { | 1102 | while let Some(c) = current { |
| 850 | let next = iter.next(); | 1103 | let next = iter.next(); |
| 851 | let is_last = next.is_none(); | 1104 | let is_last = next.is_none(); |
| 852 | 1105 | ||
| 853 | let fut = self.write_dma_internal(address, c, first, is_last, is_last, timeout); | 1106 | let fut = self.write_dma_internal( |
| 1107 | address, c, first, // first_slice | ||
| 1108 | is_last, // last_slice | ||
| 1109 | is_last, // send_stop (only on last buffer) | ||
| 1110 | false, // restart (false for all - they're one continuous write) | ||
| 1111 | timeout, | ||
| 1112 | ); | ||
| 854 | timeout.with(fut).await?; | 1113 | timeout.with(fut).await?; |
| 1114 | |||
| 855 | first = false; | 1115 | first = false; |
| 856 | current = next; | 1116 | current = next; |
| 857 | } | 1117 | } |
| @@ -860,8 +1120,6 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 860 | 1120 | ||
| 861 | /// Read. | 1121 | /// Read. |
| 862 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { | 1122 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { |
| 863 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 864 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 865 | let timeout = self.timeout(); | 1123 | let timeout = self.timeout(); |
| 866 | 1124 | ||
| 867 | if buffer.is_empty() { | 1125 | if buffer.is_empty() { |
| @@ -874,14 +1132,12 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 874 | 1132 | ||
| 875 | /// Write, restart, read. | 1133 | /// Write, restart, read. |
| 876 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { | 1134 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { |
| 877 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 878 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 879 | let timeout = self.timeout(); | 1135 | let timeout = self.timeout(); |
| 880 | 1136 | ||
| 881 | if write.is_empty() { | 1137 | if write.is_empty() { |
| 882 | self.write_internal(address.into(), write, false, timeout)?; | 1138 | self.write_internal(address.into(), write, false, timeout)?; |
| 883 | } else { | 1139 | } else { |
| 884 | let fut = self.write_dma_internal(address.into(), write, true, true, false, timeout); | 1140 | let fut = self.write_dma_internal(address.into(), write, true, true, false, false, timeout); |
| 885 | timeout.with(fut).await?; | 1141 | timeout.with(fut).await?; |
| 886 | } | 1142 | } |
| 887 | 1143 | ||
| @@ -901,11 +1157,298 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 901 | /// | 1157 | /// |
| 902 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | 1158 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction |
| 903 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | 1159 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { |
| 904 | #[cfg(all(feature = "low-power", stm32wlex))] | 1160 | if operations.is_empty() { |
| 905 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | 1161 | return Err(Error::ZeroLengthTransfer); |
| 906 | let _ = addr; | 1162 | } |
| 907 | let _ = operations; | 1163 | |
| 908 | todo!() | 1164 | let address = addr.into(); |
| 1165 | let timeout = self.timeout(); | ||
| 1166 | |||
| 1167 | // Group consecutive operations of the same type | ||
| 1168 | let mut op_idx = 0; | ||
| 1169 | let mut is_first_group = true; | ||
| 1170 | |||
| 1171 | while op_idx < operations.len() { | ||
| 1172 | // Determine the type of current group and find all consecutive operations of same type | ||
| 1173 | let is_read = matches!(operations[op_idx], Operation::Read(_)); | ||
| 1174 | let group_start = op_idx; | ||
| 1175 | |||
| 1176 | // Find end of this group (consecutive operations of same type) | ||
| 1177 | while op_idx < operations.len() && matches!(operations[op_idx], Operation::Read(_)) == is_read { | ||
| 1178 | op_idx += 1; | ||
| 1179 | } | ||
| 1180 | let group_end = op_idx; | ||
| 1181 | let is_last_group = op_idx >= operations.len(); | ||
| 1182 | |||
| 1183 | // Execute this group of operations | ||
| 1184 | if is_read { | ||
| 1185 | self.execute_read_group_async( | ||
| 1186 | address, | ||
| 1187 | &mut operations[group_start..group_end], | ||
| 1188 | is_first_group, | ||
| 1189 | is_last_group, | ||
| 1190 | timeout, | ||
| 1191 | ) | ||
| 1192 | .await?; | ||
| 1193 | } else { | ||
| 1194 | self.execute_write_group_async( | ||
| 1195 | address, | ||
| 1196 | &operations[group_start..group_end], | ||
| 1197 | is_first_group, | ||
| 1198 | is_last_group, | ||
| 1199 | timeout, | ||
| 1200 | ) | ||
| 1201 | .await?; | ||
| 1202 | } | ||
| 1203 | |||
| 1204 | is_first_group = false; | ||
| 1205 | } | ||
| 1206 | |||
| 1207 | Ok(()) | ||
| 1208 | } | ||
| 1209 | |||
| 1210 | async fn execute_write_group_async( | ||
| 1211 | &mut self, | ||
| 1212 | address: Address, | ||
| 1213 | operations: &[Operation<'_>], | ||
| 1214 | is_first_group: bool, | ||
| 1215 | is_last_group: bool, | ||
| 1216 | timeout: Timeout, | ||
| 1217 | ) -> Result<(), Error> { | ||
| 1218 | // Calculate total bytes across all operations in this group | ||
| 1219 | let total_bytes = Self::total_operation_bytes(operations); | ||
| 1220 | |||
| 1221 | if total_bytes == 0 { | ||
| 1222 | // Handle empty write group using blocking call | ||
| 1223 | if is_first_group { | ||
| 1224 | Self::master_write(self.info, address, 0, Stop::Software, false, !is_first_group, timeout)?; | ||
| 1225 | } | ||
| 1226 | if is_last_group { | ||
| 1227 | self.master_stop(); | ||
| 1228 | } | ||
| 1229 | return Ok(()); | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | // Collect all write buffers | ||
| 1233 | let mut write_buffers: heapless::Vec<&[u8], 16> = heapless::Vec::new(); | ||
| 1234 | for operation in operations { | ||
| 1235 | if let Operation::Write(buffer) = operation { | ||
| 1236 | if !buffer.is_empty() { | ||
| 1237 | let _ = write_buffers.push(buffer); | ||
| 1238 | } | ||
| 1239 | } | ||
| 1240 | } | ||
| 1241 | |||
| 1242 | if write_buffers.is_empty() { | ||
| 1243 | return Ok(()); | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | // Send each buffer using DMA | ||
| 1247 | let num_buffers = write_buffers.len(); | ||
| 1248 | for (idx, buffer) in write_buffers.iter().enumerate() { | ||
| 1249 | let is_first_buffer = idx == 0; | ||
| 1250 | let is_last_buffer = idx == num_buffers - 1; | ||
| 1251 | |||
| 1252 | let fut = self.write_dma_internal( | ||
| 1253 | address, | ||
| 1254 | buffer, | ||
| 1255 | is_first_buffer, // first_slice | ||
| 1256 | is_last_buffer, // last_slice | ||
| 1257 | is_last_buffer && is_last_group, // send_stop | ||
| 1258 | is_first_buffer && !is_first_group, // restart (only for first buffer if not first group) | ||
| 1259 | timeout, | ||
| 1260 | ); | ||
| 1261 | timeout.with(fut).await?; | ||
| 1262 | } | ||
| 1263 | |||
| 1264 | Ok(()) | ||
| 1265 | } | ||
| 1266 | |||
| 1267 | async fn execute_read_group_async( | ||
| 1268 | &mut self, | ||
| 1269 | address: Address, | ||
| 1270 | operations: &mut [Operation<'_>], | ||
| 1271 | is_first_group: bool, | ||
| 1272 | is_last_group: bool, | ||
| 1273 | timeout: Timeout, | ||
| 1274 | ) -> Result<(), Error> { | ||
| 1275 | // Calculate total bytes across all operations in this group | ||
| 1276 | let total_bytes = Self::total_operation_bytes(operations); | ||
| 1277 | |||
| 1278 | if total_bytes == 0 { | ||
| 1279 | // Handle empty read group using blocking call | ||
| 1280 | if is_first_group { | ||
| 1281 | Self::master_read( | ||
| 1282 | self.info, | ||
| 1283 | address, | ||
| 1284 | 0, | ||
| 1285 | if is_last_group { Stop::Automatic } else { Stop::Software }, | ||
| 1286 | false, // reload | ||
| 1287 | !is_first_group, | ||
| 1288 | timeout, | ||
| 1289 | )?; | ||
| 1290 | } | ||
| 1291 | if is_last_group { | ||
| 1292 | self.wait_stop(timeout)?; | ||
| 1293 | } | ||
| 1294 | return Ok(()); | ||
| 1295 | } | ||
| 1296 | |||
| 1297 | // Use DMA for read operations - need to handle multiple buffers | ||
| 1298 | let restart = !is_first_group; | ||
| 1299 | let mut total_remaining = total_bytes; | ||
| 1300 | let mut is_first_in_group = true; | ||
| 1301 | |||
| 1302 | for operation in operations { | ||
| 1303 | if let Operation::Read(buffer) = operation { | ||
| 1304 | if buffer.is_empty() { | ||
| 1305 | // Skip empty buffers | ||
| 1306 | continue; | ||
| 1307 | } | ||
| 1308 | |||
| 1309 | let buf_len = buffer.len(); | ||
| 1310 | total_remaining -= buf_len; | ||
| 1311 | let is_last_in_group = total_remaining == 0; | ||
| 1312 | |||
| 1313 | // Perform DMA read | ||
| 1314 | if is_first_in_group { | ||
| 1315 | // First buffer: use read_dma_internal which handles restart properly | ||
| 1316 | // Only use Automatic stop if this is the last buffer in the last group | ||
| 1317 | let stop_mode = if is_last_group && is_last_in_group { | ||
| 1318 | Stop::Automatic | ||
| 1319 | } else { | ||
| 1320 | Stop::Software | ||
| 1321 | }; | ||
| 1322 | |||
| 1323 | // We need a custom DMA read that respects our stop mode | ||
| 1324 | self.read_dma_group_internal(address, buffer, restart, stop_mode, timeout) | ||
| 1325 | .await?; | ||
| 1326 | is_first_in_group = false; | ||
| 1327 | } else { | ||
| 1328 | // Subsequent buffers: need to reload and continue | ||
| 1329 | let stop_mode = if is_last_group && is_last_in_group { | ||
| 1330 | Stop::Automatic | ||
| 1331 | } else { | ||
| 1332 | Stop::Software | ||
| 1333 | }; | ||
| 1334 | |||
| 1335 | self.read_dma_group_internal( | ||
| 1336 | address, buffer, false, // no restart for subsequent buffers in same group | ||
| 1337 | stop_mode, timeout, | ||
| 1338 | ) | ||
| 1339 | .await?; | ||
| 1340 | } | ||
| 1341 | } | ||
| 1342 | } | ||
| 1343 | |||
| 1344 | // Wait for transfer to complete | ||
| 1345 | if is_last_group { | ||
| 1346 | self.wait_stop(timeout)?; | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | Ok(()) | ||
| 1350 | } | ||
| 1351 | |||
| 1352 | /// Internal DMA read helper for transaction groups | ||
| 1353 | async fn read_dma_group_internal( | ||
| 1354 | &mut self, | ||
| 1355 | address: Address, | ||
| 1356 | buffer: &mut [u8], | ||
| 1357 | restart: bool, | ||
| 1358 | stop_mode: Stop, | ||
| 1359 | timeout: Timeout, | ||
| 1360 | ) -> Result<(), Error> { | ||
| 1361 | let total_len = buffer.len(); | ||
| 1362 | |||
| 1363 | let dma_transfer = unsafe { | ||
| 1364 | let regs = self.info.regs; | ||
| 1365 | regs.cr1().modify(|w| { | ||
| 1366 | w.set_rxdmaen(true); | ||
| 1367 | w.set_tcie(true); | ||
| 1368 | w.set_nackie(true); | ||
| 1369 | w.set_errie(true); | ||
| 1370 | }); | ||
| 1371 | let src = regs.rxdr().as_ptr() as *mut u8; | ||
| 1372 | |||
| 1373 | self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) | ||
| 1374 | }; | ||
| 1375 | |||
| 1376 | let mut remaining_len = total_len; | ||
| 1377 | |||
| 1378 | let on_drop = OnDrop::new(|| { | ||
| 1379 | let regs = self.info.regs; | ||
| 1380 | regs.cr1().modify(|w| { | ||
| 1381 | w.set_rxdmaen(false); | ||
| 1382 | w.set_tcie(false); | ||
| 1383 | w.set_nackie(false); | ||
| 1384 | w.set_errie(false); | ||
| 1385 | }); | ||
| 1386 | regs.icr().write(|w| { | ||
| 1387 | w.set_nackcf(true); | ||
| 1388 | w.set_berrcf(true); | ||
| 1389 | w.set_arlocf(true); | ||
| 1390 | w.set_ovrcf(true); | ||
| 1391 | }); | ||
| 1392 | }); | ||
| 1393 | |||
| 1394 | poll_fn(|cx| { | ||
| 1395 | self.state.waker.register(cx.waker()); | ||
| 1396 | |||
| 1397 | let isr = self.info.regs.isr().read(); | ||
| 1398 | |||
| 1399 | if isr.nackf() { | ||
| 1400 | return Poll::Ready(Err(Error::Nack)); | ||
| 1401 | } | ||
| 1402 | if isr.arlo() { | ||
| 1403 | return Poll::Ready(Err(Error::Arbitration)); | ||
| 1404 | } | ||
| 1405 | if isr.berr() { | ||
| 1406 | return Poll::Ready(Err(Error::Bus)); | ||
| 1407 | } | ||
| 1408 | if isr.ovr() { | ||
| 1409 | return Poll::Ready(Err(Error::Overrun)); | ||
| 1410 | } | ||
| 1411 | |||
| 1412 | if remaining_len == total_len { | ||
| 1413 | Self::master_read( | ||
| 1414 | self.info, | ||
| 1415 | address, | ||
| 1416 | total_len.min(255), | ||
| 1417 | stop_mode, | ||
| 1418 | total_len > 255, // reload | ||
| 1419 | restart, | ||
| 1420 | timeout, | ||
| 1421 | )?; | ||
| 1422 | if total_len <= 255 { | ||
| 1423 | return Poll::Ready(Ok(())); | ||
| 1424 | } | ||
| 1425 | } else if isr.tcr() { | ||
| 1426 | // Transfer Complete Reload - need to set up next chunk | ||
| 1427 | let last_piece = remaining_len <= 255; | ||
| 1428 | |||
| 1429 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, stop_mode, timeout) { | ||
| 1430 | return Poll::Ready(Err(e)); | ||
| 1431 | } | ||
| 1432 | // Return here if we are on last chunk, | ||
| 1433 | // end of transfer will be awaited with the DMA below | ||
| 1434 | if last_piece { | ||
| 1435 | return Poll::Ready(Ok(())); | ||
| 1436 | } | ||
| 1437 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | ||
| 1438 | } else { | ||
| 1439 | // poll_fn was woken without TCR interrupt | ||
| 1440 | return Poll::Pending; | ||
| 1441 | } | ||
| 1442 | |||
| 1443 | remaining_len = remaining_len.saturating_sub(255); | ||
| 1444 | Poll::Pending | ||
| 1445 | }) | ||
| 1446 | .await?; | ||
| 1447 | |||
| 1448 | dma_transfer.await; | ||
| 1449 | drop(on_drop); | ||
| 1450 | |||
| 1451 | Ok(()) | ||
| 909 | } | 1452 | } |
| 910 | } | 1453 | } |
| 911 | 1454 | ||
| @@ -1043,7 +1586,13 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1043 | if number == 0 { | 1586 | if number == 0 { |
| 1044 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); | 1587 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); |
| 1045 | } else { | 1588 | } else { |
| 1046 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 1589 | Self::reload( |
| 1590 | self.info, | ||
| 1591 | chunk.len(), | ||
| 1592 | number != last_chunk_idx, | ||
| 1593 | Stop::Software, | ||
| 1594 | timeout, | ||
| 1595 | )?; | ||
| 1047 | } | 1596 | } |
| 1048 | 1597 | ||
| 1049 | let mut index = 0; | 1598 | let mut index = 0; |
| @@ -1092,7 +1641,13 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1092 | if number == 0 { | 1641 | if number == 0 { |
| 1093 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); | 1642 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); |
| 1094 | } else { | 1643 | } else { |
| 1095 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 1644 | Self::reload( |
| 1645 | self.info, | ||
| 1646 | chunk.len(), | ||
| 1647 | number != last_chunk_idx, | ||
| 1648 | Stop::Software, | ||
| 1649 | timeout, | ||
| 1650 | )?; | ||
| 1096 | } | 1651 | } |
| 1097 | 1652 | ||
| 1098 | let mut index = 0; | 1653 | let mut index = 0; |
| @@ -1228,7 +1783,13 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1228 | Poll::Pending | 1783 | Poll::Pending |
| 1229 | } else if isr.tcr() { | 1784 | } else if isr.tcr() { |
| 1230 | let is_last_slice = remaining_len <= 255; | 1785 | let is_last_slice = remaining_len <= 255; |
| 1231 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { | 1786 | if let Err(e) = Self::reload( |
| 1787 | self.info, | ||
| 1788 | remaining_len.min(255), | ||
| 1789 | !is_last_slice, | ||
| 1790 | Stop::Software, | ||
| 1791 | timeout, | ||
| 1792 | ) { | ||
| 1232 | return Poll::Ready(Err(e)); | 1793 | return Poll::Ready(Err(e)); |
| 1233 | } | 1794 | } |
| 1234 | remaining_len = remaining_len.saturating_sub(255); | 1795 | remaining_len = remaining_len.saturating_sub(255); |
| @@ -1292,7 +1853,13 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1292 | Poll::Pending | 1853 | Poll::Pending |
| 1293 | } else if isr.tcr() { | 1854 | } else if isr.tcr() { |
| 1294 | let is_last_slice = remaining_len <= 255; | 1855 | let is_last_slice = remaining_len <= 255; |
| 1295 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { | 1856 | if let Err(e) = Self::reload( |
| 1857 | self.info, | ||
| 1858 | remaining_len.min(255), | ||
| 1859 | !is_last_slice, | ||
| 1860 | Stop::Software, | ||
| 1861 | timeout, | ||
| 1862 | ) { | ||
| 1296 | return Poll::Ready(Err(e)); | 1863 | return Poll::Ready(Err(e)); |
| 1297 | } | 1864 | } |
| 1298 | remaining_len = remaining_len.saturating_sub(255); | 1865 | remaining_len = remaining_len.saturating_sub(255); |
diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index e1d8b1c2a..10c4a820b 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs | |||
| @@ -1,9 +1,11 @@ | |||
| 1 | //! Inter-Process Communication Controller (IPCC) | 1 | //! Inter-Process Communication Controller (IPCC) |
| 2 | 2 | ||
| 3 | use core::future::poll_fn; | 3 | use core::future::poll_fn; |
| 4 | use core::marker::PhantomData; | ||
| 4 | use core::sync::atomic::{Ordering, compiler_fence}; | 5 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 5 | use core::task::Poll; | 6 | use core::task::Poll; |
| 6 | 7 | ||
| 8 | use embassy_hal_internal::Peri; | ||
| 7 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 8 | 10 | ||
| 9 | use crate::interrupt::typelevel::Interrupt; | 11 | use crate::interrupt::typelevel::Interrupt; |
| @@ -17,25 +19,16 @@ impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_RX> for Receive | |||
| 17 | unsafe fn on_interrupt() { | 19 | unsafe fn on_interrupt() { |
| 18 | let regs = IPCC::regs(); | 20 | let regs = IPCC::regs(); |
| 19 | 21 | ||
| 20 | let channels = [ | ||
| 21 | IpccChannel::Channel1, | ||
| 22 | IpccChannel::Channel2, | ||
| 23 | IpccChannel::Channel3, | ||
| 24 | IpccChannel::Channel4, | ||
| 25 | IpccChannel::Channel5, | ||
| 26 | IpccChannel::Channel6, | ||
| 27 | ]; | ||
| 28 | |||
| 29 | // Status register gives channel occupied status. For rx, use cpu1. | 22 | // Status register gives channel occupied status. For rx, use cpu1. |
| 30 | let sr = regs.cpu(1).sr().read(); | 23 | let sr = regs.cpu(1).sr().read(); |
| 31 | regs.cpu(0).mr().modify(|w| { | 24 | regs.cpu(0).mr().modify(|w| { |
| 32 | for channel in channels { | 25 | for index in 0..5 { |
| 33 | if sr.chf(channel as usize) { | 26 | if sr.chf(index as usize) { |
| 34 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt | 27 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt |
| 35 | w.set_chom(channel as usize, true); | 28 | w.set_chom(index as usize, true); |
| 36 | 29 | ||
| 37 | // There shouldn't be a race because the channel is masked only if the interrupt has fired | 30 | // There shouldn't be a race because the channel is masked only if the interrupt has fired |
| 38 | IPCC::state().rx_waker_for(channel).wake(); | 31 | IPCC::state().rx_waker_for(index).wake(); |
| 39 | } | 32 | } |
| 40 | } | 33 | } |
| 41 | }) | 34 | }) |
| @@ -49,25 +42,16 @@ impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_TX> for Transmi | |||
| 49 | unsafe fn on_interrupt() { | 42 | unsafe fn on_interrupt() { |
| 50 | let regs = IPCC::regs(); | 43 | let regs = IPCC::regs(); |
| 51 | 44 | ||
| 52 | let channels = [ | ||
| 53 | IpccChannel::Channel1, | ||
| 54 | IpccChannel::Channel2, | ||
| 55 | IpccChannel::Channel3, | ||
| 56 | IpccChannel::Channel4, | ||
| 57 | IpccChannel::Channel5, | ||
| 58 | IpccChannel::Channel6, | ||
| 59 | ]; | ||
| 60 | |||
| 61 | // Status register gives channel occupied status. For tx, use cpu0. | 45 | // Status register gives channel occupied status. For tx, use cpu0. |
| 62 | let sr = regs.cpu(0).sr().read(); | 46 | let sr = regs.cpu(0).sr().read(); |
| 63 | regs.cpu(0).mr().modify(|w| { | 47 | regs.cpu(0).mr().modify(|w| { |
| 64 | for channel in channels { | 48 | for index in 0..5 { |
| 65 | if !sr.chf(channel as usize) { | 49 | if !sr.chf(index as usize) { |
| 66 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt | 50 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt |
| 67 | w.set_chfm(channel as usize, true); | 51 | w.set_chfm(index as usize, true); |
| 68 | 52 | ||
| 69 | // There shouldn't be a race because the channel is masked only if the interrupt has fired | 53 | // There shouldn't be a race because the channel is masked only if the interrupt has fired |
| 70 | IPCC::state().tx_waker_for(channel).wake(); | 54 | IPCC::state().tx_waker_for(index).wake(); |
| 71 | } | 55 | } |
| 72 | } | 56 | } |
| 73 | }); | 57 | }); |
| @@ -82,76 +66,55 @@ pub struct Config { | |||
| 82 | // reserved for future use | 66 | // reserved for future use |
| 83 | } | 67 | } |
| 84 | 68 | ||
| 85 | /// Channel. | 69 | /// IPCC TX Channel |
| 86 | #[allow(missing_docs)] | 70 | pub struct IpccTxChannel<'a> { |
| 87 | #[derive(Debug, Clone, Copy)] | 71 | index: u8, |
| 88 | #[repr(C)] | 72 | _lifetime: PhantomData<&'a mut usize>, |
| 89 | pub enum IpccChannel { | ||
| 90 | Channel1 = 0, | ||
| 91 | Channel2 = 1, | ||
| 92 | Channel3 = 2, | ||
| 93 | Channel4 = 3, | ||
| 94 | Channel5 = 4, | ||
| 95 | Channel6 = 5, | ||
| 96 | } | 73 | } |
| 97 | 74 | ||
| 98 | /// IPCC driver. | 75 | impl<'a> IpccTxChannel<'a> { |
| 99 | pub struct Ipcc; | 76 | pub(crate) const fn new(index: u8) -> Self { |
| 100 | 77 | core::assert!(index < 6); | |
| 101 | impl Ipcc { | ||
| 102 | /// Enable IPCC. | ||
| 103 | pub fn enable(_config: Config) { | ||
| 104 | rcc::enable_and_reset::<IPCC>(); | ||
| 105 | IPCC::set_cpu2(true); | ||
| 106 | |||
| 107 | let regs = IPCC::regs(); | ||
| 108 | |||
| 109 | regs.cpu(0).cr().modify(|w| { | ||
| 110 | w.set_rxoie(true); | ||
| 111 | w.set_txfie(true); | ||
| 112 | }); | ||
| 113 | |||
| 114 | // enable interrupts | ||
| 115 | crate::interrupt::typelevel::IPCC_C1_RX::unpend(); | ||
| 116 | crate::interrupt::typelevel::IPCC_C1_TX::unpend(); | ||
| 117 | 78 | ||
| 118 | unsafe { crate::interrupt::typelevel::IPCC_C1_RX::enable() }; | 79 | Self { |
| 119 | unsafe { crate::interrupt::typelevel::IPCC_C1_TX::enable() }; | 80 | index: index, |
| 81 | _lifetime: PhantomData, | ||
| 82 | } | ||
| 120 | } | 83 | } |
| 121 | 84 | ||
| 122 | /// Send data to an IPCC channel. The closure is called to write the data when appropriate. | 85 | /// Send data to an IPCC channel. The closure is called to write the data when appropriate. |
| 123 | pub async fn send(channel: IpccChannel, f: impl FnOnce()) { | 86 | pub async fn send(&mut self, f: impl FnOnce()) { |
| 124 | let regs = IPCC::regs(); | 87 | let regs = IPCC::regs(); |
| 125 | 88 | ||
| 126 | Self::flush(channel).await; | 89 | self.flush().await; |
| 127 | 90 | ||
| 128 | f(); | 91 | f(); |
| 129 | 92 | ||
| 130 | compiler_fence(Ordering::SeqCst); | 93 | compiler_fence(Ordering::SeqCst); |
| 131 | 94 | ||
| 132 | trace!("ipcc: ch {}: send data", channel as u8); | 95 | trace!("ipcc: ch {}: send data", self.index as u8); |
| 133 | regs.cpu(0).scr().write(|w| w.set_chs(channel as usize, true)); | 96 | regs.cpu(0).scr().write(|w| w.set_chs(self.index as usize, true)); |
| 134 | } | 97 | } |
| 135 | 98 | ||
| 136 | /// Wait for the tx channel to become clear | 99 | /// Wait for the tx channel to become clear |
| 137 | pub async fn flush(channel: IpccChannel) { | 100 | pub async fn flush(&mut self) { |
| 138 | let regs = IPCC::regs(); | 101 | let regs = IPCC::regs(); |
| 139 | 102 | ||
| 140 | // This is a race, but is nice for debugging | 103 | // This is a race, but is nice for debugging |
| 141 | if regs.cpu(0).sr().read().chf(channel as usize) { | 104 | if regs.cpu(0).sr().read().chf(self.index as usize) { |
| 142 | trace!("ipcc: ch {}: wait for tx free", channel as u8); | 105 | trace!("ipcc: ch {}: wait for tx free", self.index as u8); |
| 143 | } | 106 | } |
| 144 | 107 | ||
| 145 | poll_fn(|cx| { | 108 | poll_fn(|cx| { |
| 146 | IPCC::state().tx_waker_for(channel).register(cx.waker()); | 109 | IPCC::state().tx_waker_for(self.index).register(cx.waker()); |
| 147 | // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt | 110 | // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt |
| 148 | regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, false)); | 111 | regs.cpu(0).mr().modify(|w| w.set_chfm(self.index as usize, false)); |
| 149 | 112 | ||
| 150 | compiler_fence(Ordering::SeqCst); | 113 | compiler_fence(Ordering::SeqCst); |
| 151 | 114 | ||
| 152 | if !regs.cpu(0).sr().read().chf(channel as usize) { | 115 | if !regs.cpu(0).sr().read().chf(self.index as usize) { |
| 153 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt | 116 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt |
| 154 | regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true)); | 117 | regs.cpu(0).mr().modify(|w| w.set_chfm(self.index as usize, true)); |
| 155 | 118 | ||
| 156 | Poll::Ready(()) | 119 | Poll::Ready(()) |
| 157 | } else { | 120 | } else { |
| @@ -160,27 +123,44 @@ impl Ipcc { | |||
| 160 | }) | 123 | }) |
| 161 | .await; | 124 | .await; |
| 162 | } | 125 | } |
| 126 | } | ||
| 127 | |||
| 128 | /// IPCC RX Channel | ||
| 129 | pub struct IpccRxChannel<'a> { | ||
| 130 | index: u8, | ||
| 131 | _lifetime: PhantomData<&'a mut usize>, | ||
| 132 | } | ||
| 133 | |||
| 134 | impl<'a> IpccRxChannel<'a> { | ||
| 135 | pub(crate) const fn new(index: u8) -> Self { | ||
| 136 | core::assert!(index < 6); | ||
| 137 | |||
| 138 | Self { | ||
| 139 | index: index, | ||
| 140 | _lifetime: PhantomData, | ||
| 141 | } | ||
| 142 | } | ||
| 163 | 143 | ||
| 164 | /// Receive data from an IPCC channel. The closure is called to read the data when appropriate. | 144 | /// Receive data from an IPCC channel. The closure is called to read the data when appropriate. |
| 165 | pub async fn receive<R>(channel: IpccChannel, mut f: impl FnMut() -> Option<R>) -> R { | 145 | pub async fn receive<R>(&mut self, mut f: impl FnMut() -> Option<R>) -> R { |
| 166 | let regs = IPCC::regs(); | 146 | let regs = IPCC::regs(); |
| 167 | 147 | ||
| 168 | loop { | 148 | loop { |
| 169 | // This is a race, but is nice for debugging | 149 | // This is a race, but is nice for debugging |
| 170 | if !regs.cpu(1).sr().read().chf(channel as usize) { | 150 | if !regs.cpu(1).sr().read().chf(self.index as usize) { |
| 171 | trace!("ipcc: ch {}: wait for rx occupied", channel as u8); | 151 | trace!("ipcc: ch {}: wait for rx occupied", self.index as u8); |
| 172 | } | 152 | } |
| 173 | 153 | ||
| 174 | poll_fn(|cx| { | 154 | poll_fn(|cx| { |
| 175 | IPCC::state().rx_waker_for(channel).register(cx.waker()); | 155 | IPCC::state().rx_waker_for(self.index).register(cx.waker()); |
| 176 | // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt | 156 | // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt |
| 177 | regs.cpu(0).mr().modify(|w| w.set_chom(channel as usize, false)); | 157 | regs.cpu(0).mr().modify(|w| w.set_chom(self.index as usize, false)); |
| 178 | 158 | ||
| 179 | compiler_fence(Ordering::SeqCst); | 159 | compiler_fence(Ordering::SeqCst); |
| 180 | 160 | ||
| 181 | if regs.cpu(1).sr().read().chf(channel as usize) { | 161 | if regs.cpu(1).sr().read().chf(self.index as usize) { |
| 182 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt | 162 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt |
| 183 | regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true)); | 163 | regs.cpu(0).mr().modify(|w| w.set_chfm(self.index as usize, true)); |
| 184 | 164 | ||
| 185 | Poll::Ready(()) | 165 | Poll::Ready(()) |
| 186 | } else { | 166 | } else { |
| @@ -189,21 +169,111 @@ impl Ipcc { | |||
| 189 | }) | 169 | }) |
| 190 | .await; | 170 | .await; |
| 191 | 171 | ||
| 192 | trace!("ipcc: ch {}: read data", channel as u8); | 172 | trace!("ipcc: ch {}: read data", self.index as u8); |
| 193 | 173 | ||
| 194 | match f() { | 174 | match f() { |
| 195 | Some(ret) => return ret, | 175 | Some(ret) => return ret, |
| 196 | None => {} | 176 | None => {} |
| 197 | } | 177 | } |
| 198 | 178 | ||
| 199 | trace!("ipcc: ch {}: clear rx", channel as u8); | 179 | trace!("ipcc: ch {}: clear rx", self.index as u8); |
| 200 | compiler_fence(Ordering::SeqCst); | 180 | compiler_fence(Ordering::SeqCst); |
| 201 | // If the channel is clear and the read function returns none, fetch more data | 181 | // If the channel is clear and the read function returns none, fetch more data |
| 202 | regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)); | 182 | regs.cpu(0).scr().write(|w| w.set_chc(self.index as usize, true)); |
| 203 | } | 183 | } |
| 204 | } | 184 | } |
| 205 | } | 185 | } |
| 206 | 186 | ||
| 187 | /// IPCC Channel | ||
| 188 | pub struct IpccChannel<'a> { | ||
| 189 | index: u8, | ||
| 190 | _lifetime: PhantomData<&'a mut usize>, | ||
| 191 | } | ||
| 192 | |||
| 193 | impl<'a> IpccChannel<'a> { | ||
| 194 | pub(crate) const fn new(number: u8) -> Self { | ||
| 195 | core::assert!(number > 0 && number <= 6); | ||
| 196 | |||
| 197 | Self { | ||
| 198 | index: number - 1, | ||
| 199 | _lifetime: PhantomData, | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | /// Split into a tx and rx channel | ||
| 204 | pub const fn split(self) -> (IpccTxChannel<'a>, IpccRxChannel<'a>) { | ||
| 205 | (IpccTxChannel::new(self.index), IpccRxChannel::new(self.index)) | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | /// IPCC driver. | ||
| 210 | pub struct Ipcc { | ||
| 211 | _private: (), | ||
| 212 | } | ||
| 213 | |||
| 214 | impl Ipcc { | ||
| 215 | /// Creates a new HardwareSemaphore instance. | ||
| 216 | pub fn new<'d>( | ||
| 217 | _peripheral: Peri<'d, crate::peripherals::IPCC>, | ||
| 218 | _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_RX, ReceiveInterruptHandler> | ||
| 219 | + interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_TX, TransmitInterruptHandler> | ||
| 220 | + 'd, | ||
| 221 | _config: Config, | ||
| 222 | ) -> Self { | ||
| 223 | rcc::enable_and_reset::<IPCC>(); | ||
| 224 | IPCC::set_cpu2(true); | ||
| 225 | |||
| 226 | let regs = IPCC::regs(); | ||
| 227 | |||
| 228 | regs.cpu(0).cr().modify(|w| { | ||
| 229 | w.set_rxoie(true); | ||
| 230 | w.set_txfie(true); | ||
| 231 | }); | ||
| 232 | |||
| 233 | // enable interrupts | ||
| 234 | crate::interrupt::typelevel::IPCC_C1_RX::unpend(); | ||
| 235 | crate::interrupt::typelevel::IPCC_C1_TX::unpend(); | ||
| 236 | |||
| 237 | unsafe { crate::interrupt::typelevel::IPCC_C1_RX::enable() }; | ||
| 238 | unsafe { crate::interrupt::typelevel::IPCC_C1_TX::enable() }; | ||
| 239 | |||
| 240 | Self { _private: () } | ||
| 241 | } | ||
| 242 | |||
| 243 | /// Split into a tx and rx channel | ||
| 244 | pub const fn split<'a>(self) -> [(IpccTxChannel<'a>, IpccRxChannel<'a>); 6] { | ||
| 245 | [ | ||
| 246 | IpccChannel::new(1).split(), | ||
| 247 | IpccChannel::new(2).split(), | ||
| 248 | IpccChannel::new(3).split(), | ||
| 249 | IpccChannel::new(4).split(), | ||
| 250 | IpccChannel::new(5).split(), | ||
| 251 | IpccChannel::new(6).split(), | ||
| 252 | ] | ||
| 253 | } | ||
| 254 | |||
| 255 | /// Receive from a channel number | ||
| 256 | pub async unsafe fn receive<R>(number: u8, f: impl FnMut() -> Option<R>) -> R { | ||
| 257 | core::assert!(number > 0 && number <= 6); | ||
| 258 | |||
| 259 | IpccRxChannel::new(number - 1).receive(f).await | ||
| 260 | } | ||
| 261 | |||
| 262 | /// Send to a channel number | ||
| 263 | pub async unsafe fn send(number: u8, f: impl FnOnce()) { | ||
| 264 | core::assert!(number > 0 && number <= 6); | ||
| 265 | |||
| 266 | IpccTxChannel::new(number - 1).send(f).await | ||
| 267 | } | ||
| 268 | |||
| 269 | /// Send to a channel number | ||
| 270 | pub async unsafe fn flush(number: u8) { | ||
| 271 | core::assert!(number > 0 && number <= 6); | ||
| 272 | |||
| 273 | IpccTxChannel::new(number - 1).flush().await | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 207 | impl SealedInstance for crate::peripherals::IPCC { | 277 | impl SealedInstance for crate::peripherals::IPCC { |
| 208 | fn regs() -> crate::pac::ipcc::Ipcc { | 278 | fn regs() -> crate::pac::ipcc::Ipcc { |
| 209 | crate::pac::IPCC | 279 | crate::pac::IPCC |
| @@ -232,26 +302,12 @@ impl State { | |||
| 232 | } | 302 | } |
| 233 | } | 303 | } |
| 234 | 304 | ||
| 235 | const fn rx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker { | 305 | const fn rx_waker_for(&self, index: u8) -> &AtomicWaker { |
| 236 | match channel { | 306 | &self.rx_wakers[index as usize] |
| 237 | IpccChannel::Channel1 => &self.rx_wakers[0], | ||
| 238 | IpccChannel::Channel2 => &self.rx_wakers[1], | ||
| 239 | IpccChannel::Channel3 => &self.rx_wakers[2], | ||
| 240 | IpccChannel::Channel4 => &self.rx_wakers[3], | ||
| 241 | IpccChannel::Channel5 => &self.rx_wakers[4], | ||
| 242 | IpccChannel::Channel6 => &self.rx_wakers[5], | ||
| 243 | } | ||
| 244 | } | 307 | } |
| 245 | 308 | ||
| 246 | const fn tx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker { | 309 | const fn tx_waker_for(&self, index: u8) -> &AtomicWaker { |
| 247 | match channel { | 310 | &self.tx_wakers[index as usize] |
| 248 | IpccChannel::Channel1 => &self.tx_wakers[0], | ||
| 249 | IpccChannel::Channel2 => &self.tx_wakers[1], | ||
| 250 | IpccChannel::Channel3 => &self.tx_wakers[2], | ||
| 251 | IpccChannel::Channel4 => &self.tx_wakers[3], | ||
| 252 | IpccChannel::Channel5 => &self.tx_wakers[4], | ||
| 253 | IpccChannel::Channel6 => &self.tx_wakers[5], | ||
| 254 | } | ||
| 255 | } | 311 | } |
| 256 | } | 312 | } |
| 257 | 313 | ||
diff --git a/embassy-stm32/src/lcd.rs b/embassy-stm32/src/lcd.rs new file mode 100644 index 000000000..ea29f1398 --- /dev/null +++ b/embassy-stm32/src/lcd.rs | |||
| @@ -0,0 +1,510 @@ | |||
| 1 | //! LCD | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | |||
| 4 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 5 | |||
| 6 | use crate::gpio::{AfType, AnyPin, SealedPin}; | ||
| 7 | use crate::peripherals; | ||
| 8 | use crate::rcc::{self, RccPeripheral}; | ||
| 9 | use crate::time::Hertz; | ||
| 10 | |||
| 11 | #[cfg(any(stm32u0, stm32l073, stm32l083))] | ||
| 12 | const NUM_SEGMENTS: u8 = 52; | ||
| 13 | #[cfg(any(stm32wb, stm32l4x6, stm32l15x, stm32l162, stm32l4x3, stm32l4x6))] | ||
| 14 | const NUM_SEGMENTS: u8 = 44; | ||
| 15 | #[cfg(any(stm32l053, stm32l063, stm32l100))] | ||
| 16 | const NUM_SEGMENTS: u8 = 32; | ||
| 17 | |||
| 18 | /// LCD configuration struct | ||
| 19 | #[non_exhaustive] | ||
| 20 | #[derive(Debug, Clone, Copy)] | ||
| 21 | pub struct Config { | ||
| 22 | #[cfg(lcd_v2)] | ||
| 23 | /// Enable the voltage output buffer for higher driving capability. | ||
| 24 | /// | ||
| 25 | /// The LCD driving capability is improved as buffers prevent the LCD capacitive loads from loading the resistor | ||
| 26 | /// bridge unacceptably and interfering with its voltage generation. | ||
| 27 | pub use_voltage_output_buffer: bool, | ||
| 28 | /// Enable SEG pin remapping. SEG[31:28] multiplexed with SEG[43:40] | ||
| 29 | pub use_segment_muxing: bool, | ||
| 30 | /// Bias selector | ||
| 31 | pub bias: Bias, | ||
| 32 | /// Duty selector | ||
| 33 | pub duty: Duty, | ||
| 34 | /// Internal or external voltage source | ||
| 35 | pub voltage_source: VoltageSource, | ||
| 36 | /// The frequency used to update the LCD with. | ||
| 37 | /// Should be between ~30 and ~100. Lower is better for power consumption, but has lower visual fidelity. | ||
| 38 | pub target_fps: Hertz, | ||
| 39 | /// LCD driver selector | ||
| 40 | pub drive: Drive, | ||
| 41 | } | ||
| 42 | |||
| 43 | impl Default for Config { | ||
| 44 | fn default() -> Self { | ||
| 45 | Self { | ||
| 46 | #[cfg(lcd_v2)] | ||
| 47 | use_voltage_output_buffer: false, | ||
| 48 | use_segment_muxing: false, | ||
| 49 | bias: Default::default(), | ||
| 50 | duty: Default::default(), | ||
| 51 | voltage_source: Default::default(), | ||
| 52 | target_fps: Hertz(60), | ||
| 53 | drive: Drive::Medium, | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | /// The number of voltage levels used when driving an LCD. | ||
| 59 | /// Your LCD datasheet should tell you what to use. | ||
| 60 | #[repr(u8)] | ||
| 61 | #[derive(Debug, Default, Clone, Copy)] | ||
| 62 | pub enum Bias { | ||
| 63 | /// 1/4 bias | ||
| 64 | #[default] | ||
| 65 | Quarter = 0b00, | ||
| 66 | /// 1/2 bias | ||
| 67 | Half = 0b01, | ||
| 68 | /// 1/3 bias | ||
| 69 | Third = 0b10, | ||
| 70 | } | ||
| 71 | |||
| 72 | /// The duty used by the LCD driver. | ||
| 73 | /// | ||
| 74 | /// This is essentially how many COM pins you're using. | ||
| 75 | #[repr(u8)] | ||
| 76 | #[derive(Debug, Default, Clone, Copy)] | ||
| 77 | pub enum Duty { | ||
| 78 | #[default] | ||
| 79 | /// Use a single COM pin | ||
| 80 | Static = 0b000, | ||
| 81 | /// Use two COM pins | ||
| 82 | Half = 0b001, | ||
| 83 | /// Use three COM pins | ||
| 84 | Third = 0b010, | ||
| 85 | /// Use four COM pins | ||
| 86 | Quarter = 0b011, | ||
| 87 | /// Use eight COM pins. | ||
| 88 | /// | ||
| 89 | /// In this mode, `COM[7:4]` outputs are available on `SEG[51:48]`. | ||
| 90 | /// This allows reducing the number of available segments. | ||
| 91 | Eigth = 0b100, | ||
| 92 | } | ||
| 93 | |||
| 94 | impl Duty { | ||
| 95 | fn num_com_pins(&self) -> u8 { | ||
| 96 | match self { | ||
| 97 | Duty::Static => 1, | ||
| 98 | Duty::Half => 2, | ||
| 99 | Duty::Third => 3, | ||
| 100 | Duty::Quarter => 4, | ||
| 101 | Duty::Eigth => 8, | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Whether to use the internal or external voltage source to drive the LCD | ||
| 107 | #[repr(u8)] | ||
| 108 | #[derive(Debug, Default, Clone, Copy)] | ||
| 109 | pub enum VoltageSource { | ||
| 110 | #[default] | ||
| 111 | /// Voltage stepup converter | ||
| 112 | Internal, | ||
| 113 | /// VLCD pin | ||
| 114 | External, | ||
| 115 | } | ||
| 116 | |||
| 117 | /// Defines the pulse duration in terms of ck_ps pulses. | ||
| 118 | /// | ||
| 119 | /// A short pulse leads to lower power consumption, but displays with high internal resistance | ||
| 120 | /// may need a longer pulse to achieve satisfactory contrast. | ||
| 121 | /// Note that the pulse is never longer than one half prescaled LCD clock period. | ||
| 122 | /// | ||
| 123 | /// Displays with high internal resistance may need a longer drive time to achieve satisfactory contrast. | ||
| 124 | /// `PermanentHighDrive` is useful in this case if some additional power consumption can be tolerated. | ||
| 125 | /// | ||
| 126 | /// Basically, for power usage, you want this as low as possible while still being able to use the LCD | ||
| 127 | /// with a good enough contrast. | ||
| 128 | #[repr(u8)] | ||
| 129 | #[derive(Debug, Clone, Copy)] | ||
| 130 | pub enum Drive { | ||
| 131 | /// Zero clock pulse on duration | ||
| 132 | Lowest = 0x00, | ||
| 133 | /// One clock pulse on duration | ||
| 134 | VeryLow = 0x01, | ||
| 135 | /// Two clock pulse on duration | ||
| 136 | Low = 0x02, | ||
| 137 | /// Three clock pulse on duration | ||
| 138 | Medium = 0x03, | ||
| 139 | /// Four clock pulse on duration | ||
| 140 | MediumHigh = 0x04, | ||
| 141 | /// Five clock pulse on duration | ||
| 142 | High = 0x05, | ||
| 143 | /// Six clock pulse on duration | ||
| 144 | VeryHigh = 0x06, | ||
| 145 | /// Seven clock pulse on duration | ||
| 146 | Highest = 0x07, | ||
| 147 | /// Enables the highdrive bit of the hardware | ||
| 148 | PermanentHighDrive = 0x09, | ||
| 149 | } | ||
| 150 | |||
| 151 | /// LCD driver. | ||
| 152 | pub struct Lcd<'d, T: Instance> { | ||
| 153 | _peri: PhantomData<&'d mut T>, | ||
| 154 | duty: Duty, | ||
| 155 | ck_div: u32, | ||
| 156 | } | ||
| 157 | |||
| 158 | impl<'d, T: Instance> Lcd<'d, T> { | ||
| 159 | /// Initialize the lcd driver. | ||
| 160 | /// | ||
| 161 | /// The `pins` parameter must contain *all* segment and com pins that are connected to the LCD. | ||
| 162 | /// This is not further checked by this driver. Pins not routed to the LCD can be used for other purposes. | ||
| 163 | pub fn new<const N: usize>( | ||
| 164 | _peripheral: Peri<'d, T>, | ||
| 165 | config: Config, | ||
| 166 | vlcd_pin: Peri<'_, impl VlcdPin<T>>, | ||
| 167 | pins: [LcdPin<'d, T>; N], | ||
| 168 | ) -> Self { | ||
| 169 | rcc::enable_and_reset::<T>(); | ||
| 170 | |||
| 171 | vlcd_pin.set_as_af( | ||
| 172 | vlcd_pin.af_num(), | ||
| 173 | AfType::output(crate::gpio::OutputType::PushPull, crate::gpio::Speed::VeryHigh), | ||
| 174 | ); | ||
| 175 | |||
| 176 | assert_eq!( | ||
| 177 | pins.iter().filter(|pin| !pin.is_seg).count(), | ||
| 178 | config.duty.num_com_pins() as usize, | ||
| 179 | "The number of provided COM pins is not the same as the duty configures" | ||
| 180 | ); | ||
| 181 | |||
| 182 | // Set the pins | ||
| 183 | for pin in pins { | ||
| 184 | pin.pin.set_as_af( | ||
| 185 | pin.af_num, | ||
| 186 | AfType::output(crate::gpio::OutputType::PushPull, crate::gpio::Speed::VeryHigh), | ||
| 187 | ); | ||
| 188 | } | ||
| 189 | |||
| 190 | // Initialize the display ram to 0 | ||
| 191 | for i in 0..8 { | ||
| 192 | T::regs().ram_com(i).low().write_value(0); | ||
| 193 | T::regs().ram_com(i).high().write_value(0); | ||
| 194 | } | ||
| 195 | |||
| 196 | // Calculate the clock dividers | ||
| 197 | let Some(lcd_clk) = (unsafe { rcc::get_freqs().rtc.to_hertz() }) else { | ||
| 198 | panic!("The LCD driver needs the RTC/LCD clock to be running"); | ||
| 199 | }; | ||
| 200 | let duty_divider = match config.duty { | ||
| 201 | Duty::Static => 1, | ||
| 202 | Duty::Half => 2, | ||
| 203 | Duty::Third => 3, | ||
| 204 | Duty::Quarter => 4, | ||
| 205 | Duty::Eigth => 8, | ||
| 206 | }; | ||
| 207 | let target_clock = config.target_fps.0 * duty_divider; | ||
| 208 | let target_division = lcd_clk.0 / target_clock; | ||
| 209 | |||
| 210 | let mut ps = 0; | ||
| 211 | let mut div = 0; | ||
| 212 | let mut best_fps_match = u32::MAX; | ||
| 213 | |||
| 214 | for trial_div in 0..0xF { | ||
| 215 | let trial_ps = (target_division / (trial_div + 16)) | ||
| 216 | .next_power_of_two() | ||
| 217 | .trailing_zeros(); | ||
| 218 | let fps = lcd_clk.0 / ((1 << trial_ps) * (trial_div + 16)) / duty_divider; | ||
| 219 | |||
| 220 | if fps < config.target_fps.0 { | ||
| 221 | continue; | ||
| 222 | } | ||
| 223 | |||
| 224 | if fps < best_fps_match { | ||
| 225 | ps = trial_ps; | ||
| 226 | div = trial_div; | ||
| 227 | best_fps_match = fps; | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | let ck_div = lcd_clk.0 / ((1 << ps) * (div + 16)); | ||
| 232 | |||
| 233 | trace!( | ||
| 234 | "lcd_clk: {}, fps: {}, ps: {}, div: {}, ck_div: {}", | ||
| 235 | lcd_clk, best_fps_match, ps, div, ck_div | ||
| 236 | ); | ||
| 237 | |||
| 238 | if best_fps_match == u32::MAX || ps > 0xF { | ||
| 239 | panic!("Lcd clock error"); | ||
| 240 | } | ||
| 241 | |||
| 242 | // Set the frame control | ||
| 243 | T::regs().fcr().modify(|w| { | ||
| 244 | w.set_ps(ps as u8); | ||
| 245 | w.set_div(div as u8); | ||
| 246 | w.set_cc(0b100); // Init in the middle-ish | ||
| 247 | w.set_dead(0b000); | ||
| 248 | w.set_pon(config.drive as u8 & 0x07); | ||
| 249 | w.set_hd((config.drive as u8 & !0x07) != 0); | ||
| 250 | }); | ||
| 251 | |||
| 252 | // Wait for the frame control to synchronize | ||
| 253 | while !T::regs().sr().read().fcrsf() {} | ||
| 254 | |||
| 255 | // Set the control register values | ||
| 256 | T::regs().cr().modify(|w| { | ||
| 257 | #[cfg(lcd_v2)] | ||
| 258 | w.set_bufen(config.use_voltage_output_buffer); | ||
| 259 | w.set_mux_seg(config.use_segment_muxing); | ||
| 260 | w.set_bias(config.bias as u8); | ||
| 261 | w.set_duty(config.duty as u8); | ||
| 262 | w.set_vsel(matches!(config.voltage_source, VoltageSource::External)); | ||
| 263 | }); | ||
| 264 | |||
| 265 | // Enable the lcd | ||
| 266 | T::regs().cr().modify(|w| w.set_lcden(true)); | ||
| 267 | |||
| 268 | // Wait for the lcd to be enabled | ||
| 269 | while !T::regs().sr().read().ens() {} | ||
| 270 | |||
| 271 | // Wait for the stepup converter to be ready | ||
| 272 | while !T::regs().sr().read().rdy() {} | ||
| 273 | |||
| 274 | Self { | ||
| 275 | _peri: PhantomData, | ||
| 276 | duty: config.duty, | ||
| 277 | ck_div, | ||
| 278 | } | ||
| 279 | } | ||
| 280 | |||
| 281 | /// Change the contrast by changing the voltage being used. | ||
| 282 | /// | ||
| 283 | /// This is from low at 0 to high at 7. | ||
| 284 | pub fn set_contrast_control(&mut self, value: u8) { | ||
| 285 | assert!((0..=7).contains(&value)); | ||
| 286 | T::regs().fcr().modify(|w| w.set_cc(value)); | ||
| 287 | } | ||
| 288 | |||
| 289 | /// Change the contrast by introducing a deadtime to the signals | ||
| 290 | /// where the voltages are held at 0V. | ||
| 291 | /// | ||
| 292 | /// This is from no dead time at 0 to high dead time at 7. | ||
| 293 | pub fn set_dead_time(&mut self, value: u8) { | ||
| 294 | assert!((0..=7).contains(&value)); | ||
| 295 | T::regs() | ||
| 296 | .fcr() | ||
| 297 | .modify(|w: &mut stm32_metapac::lcd::regs::Fcr| w.set_dead(value)); | ||
| 298 | } | ||
| 299 | |||
| 300 | /// Write data into the display RAM. This overwrites the data already in it for the specified com index. | ||
| 301 | /// | ||
| 302 | /// The `com_index` value determines which part of the RAM is written to. | ||
| 303 | /// The `segments` value is a bitmap where each bit represents whether a pixel is turned on or off. | ||
| 304 | /// | ||
| 305 | /// This function waits last update request to be finished, but does not submit the buffer to the LCD with a new request. | ||
| 306 | /// Submission has to be done manually using [Self::submit_frame]. | ||
| 307 | pub fn write_com_segments(&mut self, com_index: u8, segments: u64) { | ||
| 308 | while T::regs().sr().read().udr() {} | ||
| 309 | |||
| 310 | assert!( | ||
| 311 | com_index < self.duty.num_com_pins(), | ||
| 312 | "Com index cannot be higher than number of configured com pins (through the Duty setting in the config)" | ||
| 313 | ); | ||
| 314 | |||
| 315 | assert!( | ||
| 316 | segments.leading_zeros() >= 64 - self.num_segments() as u32, | ||
| 317 | "Invalid segment pixel set", | ||
| 318 | ); | ||
| 319 | |||
| 320 | T::regs() | ||
| 321 | .ram_com(com_index as usize) | ||
| 322 | .low() | ||
| 323 | .write_value((segments & 0xFFFF_FFFF) as u32); | ||
| 324 | T::regs() | ||
| 325 | .ram_com(com_index as usize) | ||
| 326 | .high() | ||
| 327 | .write_value(((segments >> 32) & 0xFFFF_FFFF) as u32); | ||
| 328 | } | ||
| 329 | |||
| 330 | /// Read the data from the display RAM. | ||
| 331 | /// | ||
| 332 | /// The `com_index` value determines which part of the RAM is read from. | ||
| 333 | /// | ||
| 334 | /// This function waits for the last update request to be finished. | ||
| 335 | pub fn read_com_segments(&self, com_index: u8) -> u64 { | ||
| 336 | while T::regs().sr().read().udr() {} | ||
| 337 | |||
| 338 | assert!( | ||
| 339 | com_index < self.duty.num_com_pins(), | ||
| 340 | "Com index cannot be higher than number of configured com pins (through the Duty setting in the config)" | ||
| 341 | ); | ||
| 342 | |||
| 343 | let low = T::regs().ram_com(com_index as usize).low().read(); | ||
| 344 | let high = T::regs().ram_com(com_index as usize).high().read(); | ||
| 345 | |||
| 346 | ((high as u64) << 32) | low as u64 | ||
| 347 | } | ||
| 348 | |||
| 349 | /// Submit the current RAM data to the LCD. | ||
| 350 | /// | ||
| 351 | /// This function waits until the RAM is writable, but does not wait for the frame to be drawn. | ||
| 352 | pub fn submit_frame(&mut self) { | ||
| 353 | while T::regs().sr().read().udr() {} | ||
| 354 | // Clear the update done flag | ||
| 355 | T::regs().sr().write(|w| w.set_udd(true)); | ||
| 356 | // Set the update request flag | ||
| 357 | T::regs().sr().write(|w| w.set_udr(true)); | ||
| 358 | } | ||
| 359 | |||
| 360 | /// Get the number of segments that are supported on this LCD | ||
| 361 | pub fn num_segments(&self) -> u8 { | ||
| 362 | match self.duty { | ||
| 363 | Duty::Eigth => NUM_SEGMENTS - 4, // With 8 coms, 4 of the segment pins turn into com pins | ||
| 364 | _ => NUM_SEGMENTS, | ||
| 365 | } | ||
| 366 | } | ||
| 367 | |||
| 368 | /// Get the pixel mask for the current LCD setup. | ||
| 369 | /// This is a mask of all bits that are allowed to be set in the [Self::write_com_segments] function. | ||
| 370 | pub fn segment_pixel_mask(&self) -> u64 { | ||
| 371 | (1 << self.num_segments()) - 1 | ||
| 372 | } | ||
| 373 | |||
| 374 | /// Get the number of COM pins that were configured through the Drive config | ||
| 375 | pub fn num_com_pins(&self) -> u8 { | ||
| 376 | self.duty.num_com_pins() | ||
| 377 | } | ||
| 378 | |||
| 379 | /// Set the blink behavior on some pixels. | ||
| 380 | /// | ||
| 381 | /// The blink frequency is an approximation. It's divided from the clock selected by the FPS. | ||
| 382 | /// Play with the FPS value if you want the blink frequency to be more accurate. | ||
| 383 | /// | ||
| 384 | /// If a blink frequency cannot be attained, this function will panic. | ||
| 385 | pub fn set_blink(&mut self, selector: BlinkSelector, freq: BlinkFreq) { | ||
| 386 | // Freq * 100 to be able to do integer math | ||
| 387 | let scaled_blink_freq = match freq { | ||
| 388 | BlinkFreq::Hz0_25 => 25, | ||
| 389 | BlinkFreq::Hz0_5 => 50, | ||
| 390 | BlinkFreq::Hz1 => 100, | ||
| 391 | BlinkFreq::Hz2 => 200, | ||
| 392 | BlinkFreq::Hz4 => 400, | ||
| 393 | }; | ||
| 394 | |||
| 395 | let desired_divider = self.ck_div * 100 / scaled_blink_freq; | ||
| 396 | let target_divider = desired_divider.next_power_of_two(); | ||
| 397 | let power_divisions = target_divider.trailing_zeros(); | ||
| 398 | |||
| 399 | trace!( | ||
| 400 | "Setting LCD blink frequency -> desired_divider: {}, target_divider: {}", | ||
| 401 | desired_divider, target_divider | ||
| 402 | ); | ||
| 403 | |||
| 404 | assert!( | ||
| 405 | (8..=1024).contains(&target_divider), | ||
| 406 | "LCD blink frequency cannot be attained" | ||
| 407 | ); | ||
| 408 | |||
| 409 | T::regs().fcr().modify(|reg| { | ||
| 410 | reg.set_blinkf((power_divisions - 3) as u8); | ||
| 411 | reg.set_blink(selector as u8); | ||
| 412 | }) | ||
| 413 | } | ||
| 414 | } | ||
| 415 | |||
| 416 | impl<'d, T: Instance> Drop for Lcd<'d, T> { | ||
| 417 | fn drop(&mut self) { | ||
| 418 | // Disable the lcd | ||
| 419 | T::regs().cr().modify(|w| w.set_lcden(false)); | ||
| 420 | rcc::disable::<T>(); | ||
| 421 | } | ||
| 422 | } | ||
| 423 | |||
| 424 | /// Blink frequency | ||
| 425 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 426 | pub enum BlinkFreq { | ||
| 427 | /// 0.25 hz | ||
| 428 | Hz0_25, | ||
| 429 | /// 0.5 hz | ||
| 430 | Hz0_5, | ||
| 431 | /// 1 hz | ||
| 432 | Hz1, | ||
| 433 | /// 2 hz | ||
| 434 | Hz2, | ||
| 435 | /// 4 hz | ||
| 436 | Hz4, | ||
| 437 | } | ||
| 438 | |||
| 439 | /// Blink pixel selector | ||
| 440 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 441 | #[repr(u8)] | ||
| 442 | pub enum BlinkSelector { | ||
| 443 | /// No pixels blink | ||
| 444 | None = 0b00, | ||
| 445 | /// The SEG0, COM0 pixel blinks if the pixel is set | ||
| 446 | Seg0Com0 = 0b01, | ||
| 447 | /// The SEG0 pixel of all COMs blinks if the pixel is set | ||
| 448 | Seg0ComAll = 0b10, | ||
| 449 | /// All pixels blink if the pixel is set | ||
| 450 | All = 0b11, | ||
| 451 | } | ||
| 452 | |||
| 453 | /// A type-erased pin that can be configured as an LCD pin. | ||
| 454 | /// This is used for passing pins to the new function in the array. | ||
| 455 | pub struct LcdPin<'d, T: Instance> { | ||
| 456 | pin: Peri<'d, AnyPin>, | ||
| 457 | af_num: u8, | ||
| 458 | is_seg: bool, | ||
| 459 | _phantom: PhantomData<T>, | ||
| 460 | } | ||
| 461 | |||
| 462 | impl<'d, T: Instance> LcdPin<'d, T> { | ||
| 463 | /// Construct an LCD pin from any pin that supports it | ||
| 464 | pub fn new_seg(pin: Peri<'d, impl SegPin<T>>) -> Self { | ||
| 465 | let af = pin.af_num(); | ||
| 466 | |||
| 467 | Self { | ||
| 468 | pin: pin.into(), | ||
| 469 | af_num: af, | ||
| 470 | is_seg: true, | ||
| 471 | _phantom: PhantomData, | ||
| 472 | } | ||
| 473 | } | ||
| 474 | |||
| 475 | /// Construct an LCD pin from any pin that supports it | ||
| 476 | pub fn new_com(pin: Peri<'d, impl ComPin<T>>) -> Self { | ||
| 477 | let af = pin.af_num(); | ||
| 478 | |||
| 479 | Self { | ||
| 480 | pin: pin.into(), | ||
| 481 | af_num: af, | ||
| 482 | is_seg: false, | ||
| 483 | _phantom: PhantomData, | ||
| 484 | } | ||
| 485 | } | ||
| 486 | } | ||
| 487 | |||
| 488 | trait SealedInstance: crate::rcc::SealedRccPeripheral + PeripheralType { | ||
| 489 | fn regs() -> crate::pac::lcd::Lcd; | ||
| 490 | } | ||
| 491 | |||
| 492 | /// DSI instance trait. | ||
| 493 | #[allow(private_bounds)] | ||
| 494 | pub trait Instance: SealedInstance + RccPeripheral + 'static {} | ||
| 495 | |||
| 496 | pin_trait!(SegPin, Instance); | ||
| 497 | pin_trait!(ComPin, Instance); | ||
| 498 | pin_trait!(VlcdPin, Instance); | ||
| 499 | |||
| 500 | foreach_peripheral!( | ||
| 501 | (lcd, $inst:ident) => { | ||
| 502 | impl crate::lcd::SealedInstance for peripherals::$inst { | ||
| 503 | fn regs() -> crate::pac::lcd::Lcd { | ||
| 504 | crate::pac::$inst | ||
| 505 | } | ||
| 506 | } | ||
| 507 | |||
| 508 | impl crate::lcd::Instance for peripherals::$inst {} | ||
| 509 | }; | ||
| 510 | ); | ||
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 680edf433..2f783bf64 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -95,6 +95,8 @@ pub mod i2c; | |||
| 95 | pub mod i2s; | 95 | pub mod i2s; |
| 96 | #[cfg(stm32wb)] | 96 | #[cfg(stm32wb)] |
| 97 | pub mod ipcc; | 97 | pub mod ipcc; |
| 98 | #[cfg(lcd)] | ||
| 99 | pub mod lcd; | ||
| 98 | #[cfg(feature = "low-power")] | 100 | #[cfg(feature = "low-power")] |
| 99 | pub mod low_power; | 101 | pub mod low_power; |
| 100 | #[cfg(lptim)] | 102 | #[cfg(lptim)] |
| @@ -151,7 +153,7 @@ pub use crate::_generated::interrupt; | |||
| 151 | /// Macro to bind interrupts to handlers. | 153 | /// Macro to bind interrupts to handlers. |
| 152 | /// | 154 | /// |
| 153 | /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) | 155 | /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) |
| 154 | /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to | 156 | /// and implements the right [`Binding`](crate::interrupt::typelevel::Binding)s for it. You can pass this struct to drivers to |
| 155 | /// prove at compile-time that the right interrupts have been bound. | 157 | /// prove at compile-time that the right interrupts have been bound. |
| 156 | /// | 158 | /// |
| 157 | /// Example of how to bind one interrupt: | 159 | /// Example of how to bind one interrupt: |
| @@ -178,6 +180,10 @@ pub use crate::_generated::interrupt; | |||
| 178 | /// } | 180 | /// } |
| 179 | /// ); | 181 | /// ); |
| 180 | /// ``` | 182 | /// ``` |
| 183 | /// | ||
| 184 | /// Some chips collate multiple interrupt signals into a single interrupt vector. In the above example, I2C2_3 is a | ||
| 185 | /// single vector which is activated by events and errors on both peripherals I2C2 and I2C3. Check your chip's list | ||
| 186 | /// of interrupt vectors if you get an unexpected compile error trying to bind the standard name. | ||
| 181 | // developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. | 187 | // developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. |
| 182 | #[macro_export] | 188 | #[macro_export] |
| 183 | macro_rules! bind_interrupts { | 189 | macro_rules! bind_interrupts { |
| @@ -649,12 +655,26 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 649 | rcc::init_rcc(cs, config.rcc); | 655 | rcc::init_rcc(cs, config.rcc); |
| 650 | 656 | ||
| 651 | #[cfg(feature = "low-power")] | 657 | #[cfg(feature = "low-power")] |
| 652 | crate::rtc::init_rtc(cs, config.rtc); | 658 | rtc::init_rtc(cs, config.rtc, config.min_stop_pause); |
| 653 | 659 | ||
| 654 | #[cfg(feature = "low-power")] | 660 | #[cfg(all(stm32wb, feature = "low-power"))] |
| 655 | crate::time_driver::get_driver().set_min_stop_pause(cs, config.min_stop_pause); | 661 | hsem::init_hsem(cs); |
| 656 | } | 662 | } |
| 657 | 663 | ||
| 658 | p | 664 | p |
| 659 | }) | 665 | }) |
| 660 | } | 666 | } |
| 667 | |||
| 668 | /// Performs a busy-wait delay for a specified number of microseconds. | ||
| 669 | #[allow(unused)] | ||
| 670 | pub(crate) fn block_for_us(us: u64) { | ||
| 671 | cfg_if::cfg_if! { | ||
| 672 | // this does strange things on stm32wlx in low power mode depending on exactly when it's called | ||
| 673 | // as in sometimes 15 us (1 tick) would take > 20 seconds. | ||
| 674 | if #[cfg(all(feature = "time", all(not(feature = "low-power"), not(stm32wlex))))] { | ||
| 675 | embassy_time::block_for(embassy_time::Duration::from_micros(us)); | ||
| 676 | } else { | ||
| 677 | cortex_m::asm::delay(unsafe { rcc::get_freqs().sys.to_hertz().unwrap().0 as u64 * us / 1_000_000 } as u32); | ||
| 678 | } | ||
| 679 | } | ||
| 680 | } | ||
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 696dfe83f..bd8290da0 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | //! | 14 | //! |
| 15 | //! Since entering and leaving low-power modes typically incurs a significant latency, the | 15 | //! Since entering and leaving low-power modes typically incurs a significant latency, the |
| 16 | //! low-power executor will only attempt to enter when the next timer event is at least | 16 | //! low-power executor will only attempt to enter when the next timer event is at least |
| 17 | //! [`time_driver::MIN_STOP_PAUSE`] in the future. | 17 | //! [`time_driver::min_stop_pause`] in the future. |
| 18 | //! | 18 | //! |
| 19 | //! Currently there is no macro analogous to `embassy_executor::main` for this executor; | 19 | //! Currently there is no macro analogous to `embassy_executor::main` for this executor; |
| 20 | //! consequently one must define their entrypoint manually. Moreover, you must relinquish control | 20 | //! consequently one must define their entrypoint manually. Moreover, you must relinquish control |
| @@ -22,21 +22,16 @@ | |||
| 22 | //! | 22 | //! |
| 23 | //! ```rust,no_run | 23 | //! ```rust,no_run |
| 24 | //! use embassy_executor::Spawner; | 24 | //! use embassy_executor::Spawner; |
| 25 | //! use embassy_stm32::low_power::Executor; | 25 | //! use embassy_stm32::low_power; |
| 26 | //! use embassy_stm32::rtc::{Rtc, RtcConfig}; | 26 | //! use embassy_stm32::rtc::{Rtc, RtcConfig}; |
| 27 | //! use static_cell::StaticCell; | 27 | //! use embassy_time::Duration; |
| 28 | //! | 28 | //! |
| 29 | //! #[cortex_m_rt::entry] | 29 | //! #[embassy_executor::main(executor = "low_power::Executor")] |
| 30 | //! fn main() -> ! { | ||
| 31 | //! Executor::take().run(|spawner| { | ||
| 32 | //! spawner.spawn(unwrap!(async_main(spawner))); | ||
| 33 | //! }); | ||
| 34 | //! } | ||
| 35 | //! | ||
| 36 | //! #[embassy_executor::task] | ||
| 37 | //! async fn async_main(spawner: Spawner) { | 30 | //! async fn async_main(spawner: Spawner) { |
| 38 | //! // initialize the platform... | 31 | //! // initialize the platform... |
| 39 | //! let mut config = embassy_stm32::Config::default(); | 32 | //! let mut config = embassy_stm32::Config::default(); |
| 33 | //! // the default value, but can be adjusted | ||
| 34 | //! config.min_stop_pause = Duration::from_millis(250); | ||
| 40 | //! // when enabled the power-consumption is much higher during stop, but debugging and RTT is working | 35 | //! // when enabled the power-consumption is much higher during stop, but debugging and RTT is working |
| 41 | //! config.enable_debug_during_sleep = false; | 36 | //! config.enable_debug_during_sleep = false; |
| 42 | //! let p = embassy_stm32::init(config); | 37 | //! let p = embassy_stm32::init(config); |
| @@ -45,11 +40,9 @@ | |||
| 45 | //! } | 40 | //! } |
| 46 | //! ``` | 41 | //! ``` |
| 47 | 42 | ||
| 48 | // TODO: Usage of `static mut` here is unsound. Fix then remove this `allow`.` | ||
| 49 | #![allow(static_mut_refs)] | ||
| 50 | |||
| 51 | use core::arch::asm; | 43 | use core::arch::asm; |
| 52 | use core::marker::PhantomData; | 44 | use core::marker::PhantomData; |
| 45 | use core::mem; | ||
| 53 | use core::sync::atomic::{Ordering, compiler_fence}; | 46 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 54 | 47 | ||
| 55 | use cortex_m::peripheral::SCB; | 48 | use cortex_m::peripheral::SCB; |
| @@ -57,11 +50,13 @@ use critical_section::CriticalSection; | |||
| 57 | use embassy_executor::*; | 50 | use embassy_executor::*; |
| 58 | 51 | ||
| 59 | use crate::interrupt; | 52 | use crate::interrupt; |
| 53 | pub use crate::rcc::StopMode; | ||
| 54 | use crate::rcc::{RCC_CONFIG, REFCOUNT_STOP1, REFCOUNT_STOP2, decrement_stop_refcount, increment_stop_refcount}; | ||
| 60 | use crate::time_driver::get_driver; | 55 | use crate::time_driver::get_driver; |
| 61 | 56 | ||
| 62 | const THREAD_PENDER: usize = usize::MAX; | 57 | const THREAD_PENDER: usize = usize::MAX; |
| 63 | 58 | ||
| 64 | static mut EXECUTOR: Option<Executor> = None; | 59 | static mut EXECUTOR_TAKEN: bool = false; |
| 65 | 60 | ||
| 66 | /// Prevent the device from going into the stop mode if held | 61 | /// Prevent the device from going into the stop mode if held |
| 67 | pub struct DeviceBusy(StopMode); | 62 | pub struct DeviceBusy(StopMode); |
| @@ -79,15 +74,8 @@ impl DeviceBusy { | |||
| 79 | 74 | ||
| 80 | /// Create a new DeviceBusy. | 75 | /// Create a new DeviceBusy. |
| 81 | pub fn new(stop_mode: StopMode) -> Self { | 76 | pub fn new(stop_mode: StopMode) -> Self { |
| 82 | critical_section::with(|_| unsafe { | 77 | critical_section::with(|cs| { |
| 83 | match stop_mode { | 78 | increment_stop_refcount(cs, stop_mode); |
| 84 | StopMode::Stop1 => { | ||
| 85 | crate::rcc::REFCOUNT_STOP1 += 1; | ||
| 86 | } | ||
| 87 | StopMode::Stop2 => { | ||
| 88 | crate::rcc::REFCOUNT_STOP2 += 1; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | }); | 79 | }); |
| 92 | 80 | ||
| 93 | Self(stop_mode) | 81 | Self(stop_mode) |
| @@ -96,15 +84,8 @@ impl DeviceBusy { | |||
| 96 | 84 | ||
| 97 | impl Drop for DeviceBusy { | 85 | impl Drop for DeviceBusy { |
| 98 | fn drop(&mut self) { | 86 | fn drop(&mut self) { |
| 99 | critical_section::with(|_| unsafe { | 87 | critical_section::with(|cs| { |
| 100 | match self.0 { | 88 | decrement_stop_refcount(cs, self.0); |
| 101 | StopMode::Stop1 => { | ||
| 102 | crate::rcc::REFCOUNT_STOP1 -= 1; | ||
| 103 | } | ||
| 104 | StopMode::Stop2 => { | ||
| 105 | crate::rcc::REFCOUNT_STOP2 -= 1; | ||
| 106 | } | ||
| 107 | } | ||
| 108 | }); | 89 | }); |
| 109 | } | 90 | } |
| 110 | } | 91 | } |
| @@ -137,34 +118,24 @@ foreach_interrupt! { | |||
| 137 | /// prevents entering the given stop mode. | 118 | /// prevents entering the given stop mode. |
| 138 | pub fn stop_ready(stop_mode: StopMode) -> bool { | 119 | pub fn stop_ready(stop_mode: StopMode) -> bool { |
| 139 | critical_section::with(|cs| match Executor::stop_mode(cs) { | 120 | critical_section::with(|cs| match Executor::stop_mode(cs) { |
| 140 | Some(StopMode::Stop2) => true, | 121 | Some(StopMode::Standby | StopMode::Stop2) => true, |
| 141 | Some(StopMode::Stop1) => stop_mode == StopMode::Stop1, | 122 | Some(StopMode::Stop1) => stop_mode == StopMode::Stop1, |
| 142 | None => false, | 123 | None => false, |
| 143 | }) | 124 | }) |
| 144 | } | 125 | } |
| 145 | 126 | ||
| 146 | /// Available Stop modes. | 127 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wb, stm32wlex, stm32u0))] |
| 147 | #[non_exhaustive] | 128 | use crate::pac::pwr::vals::Lpms; |
| 148 | #[derive(PartialEq)] | ||
| 149 | pub enum StopMode { | ||
| 150 | /// STOP 1 | ||
| 151 | Stop1, | ||
| 152 | /// STOP 2 | ||
| 153 | Stop2, | ||
| 154 | } | ||
| 155 | 129 | ||
| 156 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))] | 130 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wb, stm32wlex, stm32u0))] |
| 157 | use stm32_metapac::pwr::vals::Lpms; | ||
| 158 | |||
| 159 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))] | ||
| 160 | impl Into<Lpms> for StopMode { | 131 | impl Into<Lpms> for StopMode { |
| 161 | fn into(self) -> Lpms { | 132 | fn into(self) -> Lpms { |
| 162 | match self { | 133 | match self { |
| 163 | StopMode::Stop1 => Lpms::STOP1, | 134 | StopMode::Stop1 => Lpms::STOP1, |
| 164 | #[cfg(not(stm32wba))] | 135 | #[cfg(not(any(stm32wb, stm32wba)))] |
| 165 | StopMode::Stop2 => Lpms::STOP2, | 136 | StopMode::Standby | StopMode::Stop2 => Lpms::STOP2, |
| 166 | #[cfg(stm32wba)] | 137 | #[cfg(any(stm32wb, stm32wba))] |
| 167 | StopMode::Stop2 => Lpms::STOP1, // TODO: WBA has no STOP2? | 138 | StopMode::Standby | StopMode::Stop2 => Lpms::STOP1, // TODO: WBA has no STOP2? |
| 168 | } | 139 | } |
| 169 | } | 140 | } |
| 170 | } | 141 | } |
| @@ -182,42 +153,47 @@ impl Into<Lpms> for StopMode { | |||
| 182 | pub struct Executor { | 153 | pub struct Executor { |
| 183 | inner: raw::Executor, | 154 | inner: raw::Executor, |
| 184 | not_send: PhantomData<*mut ()>, | 155 | not_send: PhantomData<*mut ()>, |
| 185 | scb: SCB, | ||
| 186 | } | 156 | } |
| 187 | 157 | ||
| 188 | impl Executor { | 158 | impl Executor { |
| 189 | /// Create a new Executor. | 159 | /// Create a new Executor. |
| 190 | pub fn take() -> &'static mut Self { | 160 | pub fn new() -> Self { |
| 191 | critical_section::with(|_| unsafe { | 161 | unsafe { |
| 192 | assert!(EXECUTOR.is_none()); | 162 | if EXECUTOR_TAKEN { |
| 193 | 163 | panic!("Low power executor can only be taken once."); | |
| 194 | EXECUTOR = Some(Self { | 164 | } else { |
| 195 | inner: raw::Executor::new(THREAD_PENDER as *mut ()), | 165 | EXECUTOR_TAKEN = true; |
| 196 | not_send: PhantomData, | 166 | } |
| 197 | scb: cortex_m::Peripherals::steal().SCB, | 167 | } |
| 198 | }); | ||
| 199 | |||
| 200 | let executor = EXECUTOR.as_mut().unwrap(); | ||
| 201 | 168 | ||
| 202 | executor | 169 | Self { |
| 203 | }) | 170 | inner: raw::Executor::new(THREAD_PENDER as *mut ()), |
| 171 | not_send: PhantomData, | ||
| 172 | } | ||
| 204 | } | 173 | } |
| 205 | 174 | ||
| 206 | pub(crate) unsafe fn on_wakeup_irq() { | 175 | pub(crate) unsafe fn on_wakeup_irq() { |
| 207 | critical_section::with(|cs| { | 176 | critical_section::with(|cs| { |
| 208 | #[cfg(stm32wlex)] | 177 | #[cfg(stm32wlex)] |
| 209 | { | 178 | { |
| 210 | let extscr = crate::pac::PWR.extscr().read(); | 179 | use crate::pac::rcc::vals::Sw; |
| 180 | use crate::pac::{PWR, RCC}; | ||
| 181 | use crate::rcc::init as init_rcc; | ||
| 182 | |||
| 183 | let extscr = PWR.extscr().read(); | ||
| 211 | if extscr.c1stop2f() || extscr.c1stopf() { | 184 | if extscr.c1stop2f() || extscr.c1stopf() { |
| 212 | // when we wake from any stop mode we need to re-initialize the rcc | 185 | // when we wake from any stop mode we need to re-initialize the rcc |
| 213 | crate::rcc::apply_resume_config(); | 186 | while RCC.cfgr().read().sws() != Sw::MSI {} |
| 187 | |||
| 188 | init_rcc(RCC_CONFIG.unwrap()); | ||
| 189 | |||
| 214 | if extscr.c1stop2f() { | 190 | if extscr.c1stop2f() { |
| 215 | // when we wake from STOP2, we need to re-initialize the time driver | 191 | // when we wake from STOP2, we need to re-initialize the time driver |
| 216 | crate::time_driver::init_timer(cs); | 192 | get_driver().init_timer(cs); |
| 217 | // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer) | 193 | // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer) |
| 218 | // and given that we just woke from STOP2, we can reset them | 194 | // and given that we just woke from STOP2, we can reset them |
| 219 | crate::rcc::REFCOUNT_STOP2 = 0; | 195 | REFCOUNT_STOP2 = 0; |
| 220 | crate::rcc::REFCOUNT_STOP1 = 0; | 196 | REFCOUNT_STOP1 = 0; |
| 221 | } | 197 | } |
| 222 | } | 198 | } |
| 223 | } | 199 | } |
| @@ -226,19 +202,90 @@ impl Executor { | |||
| 226 | }); | 202 | }); |
| 227 | } | 203 | } |
| 228 | 204 | ||
| 205 | const fn get_scb() -> SCB { | ||
| 206 | unsafe { mem::transmute(()) } | ||
| 207 | } | ||
| 208 | |||
| 229 | fn stop_mode(_cs: CriticalSection) -> Option<StopMode> { | 209 | fn stop_mode(_cs: CriticalSection) -> Option<StopMode> { |
| 230 | if unsafe { crate::rcc::REFCOUNT_STOP2 == 0 && crate::rcc::REFCOUNT_STOP1 == 0 } { | 210 | // We cannot enter standby because we will lose program state. |
| 211 | if unsafe { REFCOUNT_STOP2 == 0 && REFCOUNT_STOP1 == 0 } { | ||
| 212 | trace!("low power: stop 2"); | ||
| 231 | Some(StopMode::Stop2) | 213 | Some(StopMode::Stop2) |
| 232 | } else if unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { | 214 | } else if unsafe { REFCOUNT_STOP1 == 0 } { |
| 215 | trace!("low power: stop 1"); | ||
| 233 | Some(StopMode::Stop1) | 216 | Some(StopMode::Stop1) |
| 234 | } else { | 217 | } else { |
| 218 | trace!("low power: not ready to stop (refcount_stop1: {})", unsafe { | ||
| 219 | REFCOUNT_STOP1 | ||
| 220 | }); | ||
| 235 | None | 221 | None |
| 236 | } | 222 | } |
| 237 | } | 223 | } |
| 238 | 224 | ||
| 225 | #[cfg(all(stm32wb, feature = "low-power"))] | ||
| 226 | fn configure_stop_stm32wb(&self, _cs: CriticalSection) -> Result<(), ()> { | ||
| 227 | use core::task::Poll; | ||
| 228 | |||
| 229 | use embassy_futures::poll_once; | ||
| 230 | |||
| 231 | use crate::hsem::HardwareSemaphoreChannel; | ||
| 232 | use crate::pac::rcc::vals::{Smps, Sw}; | ||
| 233 | use crate::pac::{PWR, RCC}; | ||
| 234 | |||
| 235 | trace!("low power: trying to get sem3"); | ||
| 236 | |||
| 237 | let sem3_mutex = match poll_once(HardwareSemaphoreChannel::<crate::peripherals::HSEM>::new(3).lock(0)) { | ||
| 238 | Poll::Pending => None, | ||
| 239 | Poll::Ready(mutex) => Some(mutex), | ||
| 240 | } | ||
| 241 | .ok_or(())?; | ||
| 242 | |||
| 243 | trace!("low power: got sem3"); | ||
| 244 | |||
| 245 | let sem4_mutex = HardwareSemaphoreChannel::<crate::peripherals::HSEM>::new(4).try_lock(0); | ||
| 246 | if let Some(sem4_mutex) = sem4_mutex { | ||
| 247 | trace!("low power: got sem4"); | ||
| 248 | |||
| 249 | if PWR.extscr().read().c2ds() { | ||
| 250 | drop(sem4_mutex); | ||
| 251 | } else { | ||
| 252 | return Ok(()); | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | // Sem4 not granted | ||
| 257 | // Set HSION | ||
| 258 | RCC.cr().modify(|w| { | ||
| 259 | w.set_hsion(true); | ||
| 260 | }); | ||
| 261 | |||
| 262 | // Wait for HSIRDY | ||
| 263 | while !RCC.cr().read().hsirdy() {} | ||
| 264 | |||
| 265 | // Set SW to HSI | ||
| 266 | RCC.cfgr().modify(|w| { | ||
| 267 | w.set_sw(Sw::HSI); | ||
| 268 | }); | ||
| 269 | |||
| 270 | // Wait for SWS to report HSI | ||
| 271 | while !RCC.cfgr().read().sws().eq(&Sw::HSI) {} | ||
| 272 | |||
| 273 | // Set SMPSSEL to HSI | ||
| 274 | RCC.smpscr().modify(|w| { | ||
| 275 | w.set_smpssel(Smps::HSI); | ||
| 276 | }); | ||
| 277 | |||
| 278 | drop(sem3_mutex); | ||
| 279 | |||
| 280 | Ok(()) | ||
| 281 | } | ||
| 282 | |||
| 239 | #[allow(unused_variables)] | 283 | #[allow(unused_variables)] |
| 240 | fn configure_stop(&mut self, stop_mode: StopMode) { | 284 | fn configure_stop(&self, _cs: CriticalSection, stop_mode: StopMode) -> Result<(), ()> { |
| 241 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba, stm32wlex))] | 285 | #[cfg(all(stm32wb, feature = "low-power"))] |
| 286 | self.configure_stop_stm32wb(_cs)?; | ||
| 287 | |||
| 288 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wb, stm32wba, stm32wlex))] | ||
| 242 | crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); | 289 | crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); |
| 243 | #[cfg(stm32h5)] | 290 | #[cfg(stm32h5)] |
| 244 | crate::pac::PWR.pmcr().modify(|v| { | 291 | crate::pac::PWR.pmcr().modify(|v| { |
| @@ -246,10 +293,12 @@ impl Executor { | |||
| 246 | v.set_lpms(vals::Lpms::STOP); | 293 | v.set_lpms(vals::Lpms::STOP); |
| 247 | v.set_svos(vals::Svos::SCALE3); | 294 | v.set_svos(vals::Svos::SCALE3); |
| 248 | }); | 295 | }); |
| 296 | |||
| 297 | Ok(()) | ||
| 249 | } | 298 | } |
| 250 | 299 | ||
| 251 | fn configure_pwr(&mut self) { | 300 | fn configure_pwr(&self) { |
| 252 | self.scb.clear_sleepdeep(); | 301 | Self::get_scb().clear_sleepdeep(); |
| 253 | // Clear any previous stop flags | 302 | // Clear any previous stop flags |
| 254 | #[cfg(stm32wlex)] | 303 | #[cfg(stm32wlex)] |
| 255 | crate::pac::PWR.extscr().modify(|w| { | 304 | crate::pac::PWR.extscr().modify(|w| { |
| @@ -258,27 +307,18 @@ impl Executor { | |||
| 258 | 307 | ||
| 259 | compiler_fence(Ordering::SeqCst); | 308 | compiler_fence(Ordering::SeqCst); |
| 260 | 309 | ||
| 261 | let stop_mode = critical_section::with(|cs| Self::stop_mode(cs)); | 310 | critical_section::with(|cs| { |
| 262 | 311 | let _ = unsafe { RCC_CONFIG }?; | |
| 263 | if stop_mode.is_none() { | 312 | let stop_mode = Self::stop_mode(cs)?; |
| 264 | trace!("low power: not ready to stop"); | 313 | get_driver().pause_time(cs).ok()?; |
| 265 | return; | 314 | self.configure_stop(cs, stop_mode).ok()?; |
| 266 | } | ||
| 267 | |||
| 268 | if get_driver().pause_time().is_err() { | ||
| 269 | trace!("low power: failed to pause time"); | ||
| 270 | return; | ||
| 271 | } | ||
| 272 | |||
| 273 | let stop_mode = stop_mode.unwrap(); | ||
| 274 | match stop_mode { | ||
| 275 | StopMode::Stop1 => trace!("low power: stop 1"), | ||
| 276 | StopMode::Stop2 => trace!("low power: stop 2"), | ||
| 277 | } | ||
| 278 | self.configure_stop(stop_mode); | ||
| 279 | 315 | ||
| 280 | #[cfg(not(feature = "low-power-debug-with-sleep"))] | 316 | Some(()) |
| 281 | self.scb.set_sleepdeep(); | 317 | }) |
| 318 | .map(|_| { | ||
| 319 | #[cfg(not(feature = "low-power-debug-with-sleep"))] | ||
| 320 | Self::get_scb().set_sleepdeep(); | ||
| 321 | }); | ||
| 282 | } | 322 | } |
| 283 | 323 | ||
| 284 | /// Run the executor. | 324 | /// Run the executor. |
| @@ -300,12 +340,11 @@ impl Executor { | |||
| 300 | /// | 340 | /// |
| 301 | /// This function never returns. | 341 | /// This function never returns. |
| 302 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | 342 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { |
| 303 | let executor = unsafe { EXECUTOR.as_mut().unwrap() }; | 343 | init(self.inner.spawner()); |
| 304 | init(executor.inner.spawner()); | ||
| 305 | 344 | ||
| 306 | loop { | 345 | loop { |
| 307 | unsafe { | 346 | unsafe { |
| 308 | executor.inner.poll(); | 347 | self.inner.poll(); |
| 309 | self.configure_pwr(); | 348 | self.configure_pwr(); |
| 310 | asm!("wfe"); | 349 | asm!("wfe"); |
| 311 | #[cfg(stm32wlex)] | 350 | #[cfg(stm32wlex)] |
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/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 592a8594a..2d5dbd95a 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs | |||
| @@ -451,7 +451,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 451 | } | 451 | } |
| 452 | 452 | ||
| 453 | T::REGS.cr().modify(|w| { | 453 | T::REGS.cr().modify(|w| { |
| 454 | w.set_fmode(0.into()); | 454 | w.set_fmode(vals::FunctionalMode::INDIRECT_WRITE); |
| 455 | }); | 455 | }); |
| 456 | 456 | ||
| 457 | // Configure alternate bytes | 457 | // Configure alternate bytes |
| @@ -577,7 +577,8 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 577 | w.set_dmaen(false); | 577 | w.set_dmaen(false); |
| 578 | }); | 578 | }); |
| 579 | 579 | ||
| 580 | self.configure_command(&transaction, Some(buf.len()))?; | 580 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 581 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 581 | 582 | ||
| 582 | let current_address = T::REGS.ar().read().address(); | 583 | let current_address = T::REGS.ar().read().address(); |
| 583 | let current_instruction = T::REGS.ir().read().instruction(); | 584 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -616,7 +617,8 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 616 | w.set_dmaen(false); | 617 | w.set_dmaen(false); |
| 617 | }); | 618 | }); |
| 618 | 619 | ||
| 619 | self.configure_command(&transaction, Some(buf.len()))?; | 620 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 621 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 620 | 622 | ||
| 621 | T::REGS | 623 | T::REGS |
| 622 | .cr() | 624 | .cr() |
| @@ -1153,7 +1155,8 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1153 | // Wait for peripheral to be free | 1155 | // Wait for peripheral to be free |
| 1154 | while T::REGS.sr().read().busy() {} | 1156 | while T::REGS.sr().read().busy() {} |
| 1155 | 1157 | ||
| 1156 | self.configure_command(&transaction, Some(buf.len()))?; | 1158 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1159 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1157 | 1160 | ||
| 1158 | let current_address = T::REGS.ar().read().address(); | 1161 | let current_address = T::REGS.ar().read().address(); |
| 1159 | let current_instruction = T::REGS.ir().read().instruction(); | 1162 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -1168,16 +1171,18 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1168 | T::REGS.ar().write(|v| v.set_address(current_address)); | 1171 | T::REGS.ar().write(|v| v.set_address(current_address)); |
| 1169 | } | 1172 | } |
| 1170 | 1173 | ||
| 1171 | let transfer = unsafe { | 1174 | for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { |
| 1172 | self.dma | 1175 | let transfer = unsafe { |
| 1173 | .as_mut() | 1176 | self.dma |
| 1174 | .unwrap() | 1177 | .as_mut() |
| 1175 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | 1178 | .unwrap() |
| 1176 | }; | 1179 | .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) |
| 1180 | }; | ||
| 1177 | 1181 | ||
| 1178 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1182 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1179 | 1183 | ||
| 1180 | transfer.blocking_wait(); | 1184 | transfer.blocking_wait(); |
| 1185 | } | ||
| 1181 | 1186 | ||
| 1182 | finish_dma(T::REGS); | 1187 | finish_dma(T::REGS); |
| 1183 | 1188 | ||
| @@ -1193,13 +1198,14 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1193 | // Wait for peripheral to be free | 1198 | // Wait for peripheral to be free |
| 1194 | while T::REGS.sr().read().busy() {} | 1199 | while T::REGS.sr().read().busy() {} |
| 1195 | 1200 | ||
| 1196 | self.configure_command(&transaction, Some(buf.len()))?; | 1201 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1202 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1197 | T::REGS | 1203 | T::REGS |
| 1198 | .cr() | 1204 | .cr() |
| 1199 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); | 1205 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); |
| 1200 | 1206 | ||
| 1201 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. | 1207 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. |
| 1202 | for chunk in buf.chunks(0xFFFF) { | 1208 | for chunk in buf.chunks(0xFFFF / W::size().bytes()) { |
| 1203 | let transfer = unsafe { | 1209 | let transfer = unsafe { |
| 1204 | self.dma | 1210 | self.dma |
| 1205 | .as_mut() | 1211 | .as_mut() |
| @@ -1226,7 +1232,8 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1226 | // Wait for peripheral to be free | 1232 | // Wait for peripheral to be free |
| 1227 | while T::REGS.sr().read().busy() {} | 1233 | while T::REGS.sr().read().busy() {} |
| 1228 | 1234 | ||
| 1229 | self.configure_command(&transaction, Some(buf.len()))?; | 1235 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1236 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1230 | 1237 | ||
| 1231 | let current_address = T::REGS.ar().read().address(); | 1238 | let current_address = T::REGS.ar().read().address(); |
| 1232 | let current_instruction = T::REGS.ir().read().instruction(); | 1239 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -1241,16 +1248,18 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1241 | T::REGS.ar().write(|v| v.set_address(current_address)); | 1248 | T::REGS.ar().write(|v| v.set_address(current_address)); |
| 1242 | } | 1249 | } |
| 1243 | 1250 | ||
| 1244 | let transfer = unsafe { | 1251 | for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { |
| 1245 | self.dma | 1252 | let transfer = unsafe { |
| 1246 | .as_mut() | 1253 | self.dma |
| 1247 | .unwrap() | 1254 | .as_mut() |
| 1248 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | 1255 | .unwrap() |
| 1249 | }; | 1256 | .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) |
| 1257 | }; | ||
| 1250 | 1258 | ||
| 1251 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1259 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1252 | 1260 | ||
| 1253 | transfer.await; | 1261 | transfer.await; |
| 1262 | } | ||
| 1254 | 1263 | ||
| 1255 | finish_dma(T::REGS); | 1264 | finish_dma(T::REGS); |
| 1256 | 1265 | ||
| @@ -1266,13 +1275,14 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1266 | // Wait for peripheral to be free | 1275 | // Wait for peripheral to be free |
| 1267 | while T::REGS.sr().read().busy() {} | 1276 | while T::REGS.sr().read().busy() {} |
| 1268 | 1277 | ||
| 1269 | self.configure_command(&transaction, Some(buf.len()))?; | 1278 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1279 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1270 | T::REGS | 1280 | T::REGS |
| 1271 | .cr() | 1281 | .cr() |
| 1272 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); | 1282 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); |
| 1273 | 1283 | ||
| 1274 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. | 1284 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. |
| 1275 | for chunk in buf.chunks(0xFFFF) { | 1285 | for chunk in buf.chunks(0xFFFF / W::size().bytes()) { |
| 1276 | let transfer = unsafe { | 1286 | let transfer = unsafe { |
| 1277 | self.dma | 1287 | self.dma |
| 1278 | .as_mut() | 1288 | .as_mut() |
diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs index 584957c6d..2e1cbd702 100644 --- a/embassy-stm32/src/rcc/l.rs +++ b/embassy-stm32/src/rcc/l.rs | |||
| @@ -1,6 +1,3 @@ | |||
| 1 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 2 | use core::mem::MaybeUninit; | ||
| 3 | |||
| 4 | #[cfg(any(stm32l0, stm32l1))] | 1 | #[cfg(any(stm32l0, stm32l1))] |
| 5 | pub use crate::pac::pwr::vals::Vos as VoltageScale; | 2 | pub use crate::pac::pwr::vals::Vos as VoltageScale; |
| 6 | use crate::pac::rcc::regs::Cfgr; | 3 | use crate::pac::rcc::regs::Cfgr; |
| @@ -14,42 +11,6 @@ use crate::time::Hertz; | |||
| 14 | /// HSI speed | 11 | /// HSI speed |
| 15 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); | 12 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); |
| 16 | 13 | ||
| 17 | /// Saved RCC Config | ||
| 18 | /// | ||
| 19 | /// Used when exiting STOP2 to re-enable clocks to their last configured state | ||
| 20 | /// for chips that need it. | ||
| 21 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 22 | static mut RESUME_RCC_CONFIG: MaybeUninit<Config> = MaybeUninit::uninit(); | ||
| 23 | |||
| 24 | /// Set the rcc config to be restored when exiting STOP2 | ||
| 25 | /// | ||
| 26 | /// Safety: Sets a mutable global. | ||
| 27 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 28 | pub(crate) unsafe fn set_resume_config(config: Config) { | ||
| 29 | trace!("rcc set_resume_config()"); | ||
| 30 | RESUME_RCC_CONFIG = MaybeUninit::new(config); | ||
| 31 | } | ||
| 32 | |||
| 33 | /// Get the rcc config to be restored when exiting STOP2 | ||
| 34 | /// | ||
| 35 | /// Safety: Reads a mutable global. | ||
| 36 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 37 | pub(crate) unsafe fn get_resume_config() -> Config { | ||
| 38 | *(*core::ptr::addr_of_mut!(RESUME_RCC_CONFIG)).assume_init_ref() | ||
| 39 | } | ||
| 40 | |||
| 41 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 42 | /// Safety: should only be called from low power executable just after resuming from STOP2 | ||
| 43 | pub(crate) unsafe fn apply_resume_config() { | ||
| 44 | trace!("rcc apply_resume_config()"); | ||
| 45 | |||
| 46 | while RCC.cfgr().read().sws() != Sysclk::MSI {} | ||
| 47 | |||
| 48 | let config = get_resume_config(); | ||
| 49 | |||
| 50 | init(config); | ||
| 51 | } | ||
| 52 | |||
| 53 | #[derive(Clone, Copy, Eq, PartialEq)] | 14 | #[derive(Clone, Copy, Eq, PartialEq)] |
| 54 | pub enum HseMode { | 15 | pub enum HseMode { |
| 55 | /// crystal/ceramic oscillator (HSEBYP=0) | 16 | /// crystal/ceramic oscillator (HSEBYP=0) |
| @@ -193,10 +154,6 @@ fn msi_enable(range: MSIRange) { | |||
| 193 | } | 154 | } |
| 194 | 155 | ||
| 195 | pub(crate) unsafe fn init(config: Config) { | 156 | pub(crate) unsafe fn init(config: Config) { |
| 196 | // save the rcc config because if we enter stop 2 we need to re-apply it on wakeup | ||
| 197 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 198 | set_resume_config(config); | ||
| 199 | |||
| 200 | // Switch to MSI to prevent problems with PLL configuration. | 157 | // Switch to MSI to prevent problems with PLL configuration. |
| 201 | if !RCC.cr().read().msion() { | 158 | if !RCC.cr().read().msion() { |
| 202 | // Turn on MSI and configure it to 4MHz. | 159 | // Turn on MSI and configure it to 4MHz. |
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 592890777..85434fa83 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -49,6 +49,9 @@ pub(crate) static mut REFCOUNT_STOP1: u32 = 0; | |||
| 49 | /// May be read without a critical section | 49 | /// May be read without a critical section |
| 50 | pub(crate) static mut REFCOUNT_STOP2: u32 = 0; | 50 | pub(crate) static mut REFCOUNT_STOP2: u32 = 0; |
| 51 | 51 | ||
| 52 | #[cfg(feature = "low-power")] | ||
| 53 | pub(crate) static mut RCC_CONFIG: Option<Config> = None; | ||
| 54 | |||
| 52 | #[cfg(backup_sram)] | 55 | #[cfg(backup_sram)] |
| 53 | pub(crate) static mut BKSRAM_RETAINED: bool = false; | 56 | pub(crate) static mut BKSRAM_RETAINED: bool = false; |
| 54 | 57 | ||
| @@ -108,6 +111,32 @@ pub fn clocks<'a>(_rcc: &'a crate::Peri<'a, crate::peripherals::RCC>) -> &'a Clo | |||
| 108 | unsafe { get_freqs() } | 111 | unsafe { get_freqs() } |
| 109 | } | 112 | } |
| 110 | 113 | ||
| 114 | #[cfg(feature = "low-power")] | ||
| 115 | pub(crate) fn increment_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { | ||
| 116 | match stop_mode { | ||
| 117 | StopMode::Standby => {} | ||
| 118 | StopMode::Stop2 => unsafe { | ||
| 119 | REFCOUNT_STOP2 += 1; | ||
| 120 | }, | ||
| 121 | StopMode::Stop1 => unsafe { | ||
| 122 | REFCOUNT_STOP1 += 1; | ||
| 123 | }, | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | #[cfg(feature = "low-power")] | ||
| 128 | pub(crate) fn decrement_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { | ||
| 129 | match stop_mode { | ||
| 130 | StopMode::Standby => {} | ||
| 131 | StopMode::Stop2 => unsafe { | ||
| 132 | REFCOUNT_STOP2 -= 1; | ||
| 133 | }, | ||
| 134 | StopMode::Stop1 => unsafe { | ||
| 135 | REFCOUNT_STOP1 -= 1; | ||
| 136 | }, | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 111 | pub(crate) trait SealedRccPeripheral { | 140 | pub(crate) trait SealedRccPeripheral { |
| 112 | fn frequency() -> Hertz; | 141 | fn frequency() -> Hertz; |
| 113 | #[allow(dead_code)] | 142 | #[allow(dead_code)] |
| @@ -138,12 +167,19 @@ pub(crate) struct RccInfo { | |||
| 138 | stop_mode: StopMode, | 167 | stop_mode: StopMode, |
| 139 | } | 168 | } |
| 140 | 169 | ||
| 170 | /// Specifies a limit for the stop mode of the peripheral or the stop mode to be entered. | ||
| 171 | /// E.g. if `StopMode::Stop1` is selected, the peripheral prevents the chip from entering Stop1 mode. | ||
| 141 | #[cfg(feature = "low-power")] | 172 | #[cfg(feature = "low-power")] |
| 142 | #[allow(dead_code)] | 173 | #[allow(dead_code)] |
| 143 | pub(crate) enum StopMode { | 174 | #[derive(Debug, Clone, Copy, PartialEq, Default)] |
| 144 | Standby, | 175 | pub enum StopMode { |
| 145 | Stop2, | 176 | #[default] |
| 177 | /// Peripheral prevents chip from entering Stop1 or executor will enter Stop1 | ||
| 146 | Stop1, | 178 | Stop1, |
| 179 | /// Peripheral prevents chip from entering Stop2 or executor will enter Stop2 | ||
| 180 | Stop2, | ||
| 181 | /// Peripheral does not prevent chip from entering Stop | ||
| 182 | Standby, | ||
| 147 | } | 183 | } |
| 148 | 184 | ||
| 149 | impl RccInfo { | 185 | impl RccInfo { |
| @@ -199,15 +235,7 @@ impl RccInfo { | |||
| 199 | } | 235 | } |
| 200 | 236 | ||
| 201 | #[cfg(feature = "low-power")] | 237 | #[cfg(feature = "low-power")] |
| 202 | match self.stop_mode { | 238 | increment_stop_refcount(_cs, self.stop_mode); |
| 203 | StopMode::Standby => {} | ||
| 204 | StopMode::Stop2 => unsafe { | ||
| 205 | REFCOUNT_STOP2 += 1; | ||
| 206 | }, | ||
| 207 | StopMode::Stop1 => unsafe { | ||
| 208 | REFCOUNT_STOP1 += 1; | ||
| 209 | }, | ||
| 210 | } | ||
| 211 | 239 | ||
| 212 | // set the xxxRST bit | 240 | // set the xxxRST bit |
| 213 | let reset_ptr = self.reset_ptr(); | 241 | let reset_ptr = self.reset_ptr(); |
| @@ -265,15 +293,7 @@ impl RccInfo { | |||
| 265 | } | 293 | } |
| 266 | 294 | ||
| 267 | #[cfg(feature = "low-power")] | 295 | #[cfg(feature = "low-power")] |
| 268 | match self.stop_mode { | 296 | decrement_stop_refcount(_cs, self.stop_mode); |
| 269 | StopMode::Standby => {} | ||
| 270 | StopMode::Stop2 => unsafe { | ||
| 271 | REFCOUNT_STOP2 -= 1; | ||
| 272 | }, | ||
| 273 | StopMode::Stop1 => unsafe { | ||
| 274 | REFCOUNT_STOP1 -= 1; | ||
| 275 | }, | ||
| 276 | } | ||
| 277 | 297 | ||
| 278 | // clear the xxxEN bit | 298 | // clear the xxxEN bit |
| 279 | let enable_ptr = self.enable_ptr(); | 299 | let enable_ptr = self.enable_ptr(); |
| @@ -408,8 +428,39 @@ pub(crate) fn init_rcc(_cs: CriticalSection, config: Config) { | |||
| 408 | 428 | ||
| 409 | #[cfg(feature = "low-power")] | 429 | #[cfg(feature = "low-power")] |
| 410 | { | 430 | { |
| 431 | RCC_CONFIG = Some(config); | ||
| 411 | REFCOUNT_STOP2 = 0; | 432 | REFCOUNT_STOP2 = 0; |
| 412 | REFCOUNT_STOP1 = 0; | 433 | REFCOUNT_STOP1 = 0; |
| 413 | } | 434 | } |
| 414 | } | 435 | } |
| 415 | } | 436 | } |
| 437 | |||
| 438 | /// Calculate intermediate prescaler number used to calculate peripheral prescalers | ||
| 439 | /// | ||
| 440 | /// This function is intended to calculate a number indicating a minimum division | ||
| 441 | /// necessary to result in a frequency lower than the provided `freq_max`. | ||
| 442 | /// | ||
| 443 | /// The returned value indicates the `val + 1` divider is necessary to result in | ||
| 444 | /// the output frequency that is below the maximum provided. | ||
| 445 | /// | ||
| 446 | /// For example: | ||
| 447 | /// 0 = divider of 1 => no division necessary as the input frequency is below max | ||
| 448 | /// 1 = divider of 2 => division by 2 necessary | ||
| 449 | /// ... | ||
| 450 | /// | ||
| 451 | /// The provided max frequency is inclusive. So if `freq_in == freq_max` the result | ||
| 452 | /// will be 0, indicating that no division is necessary. To accomplish that we subtract | ||
| 453 | /// 1 from the input frequency so that the integer rounding plays in our favor. | ||
| 454 | /// | ||
| 455 | /// For example: | ||
| 456 | /// Let the input frequency be 110 and the max frequency be 55. | ||
| 457 | /// If we naiively do `110/55 = 2` the renult will indicate that we need a divider by 3 | ||
| 458 | /// which in reality will be rounded up to 4 as usually a 3 division is not available. | ||
| 459 | /// In either case the resulting frequency will be either 36 or 27 which is lower than | ||
| 460 | /// what we would want. The result should be 1. | ||
| 461 | /// If we do the following instead `109/55 = 1` indicating that we need a divide by 2 | ||
| 462 | /// which will result in the correct 55. | ||
| 463 | #[allow(unused)] | ||
| 464 | pub(crate) fn raw_prescaler(freq_in: u32, freq_max: u32) -> u32 { | ||
| 465 | freq_in.saturating_sub(1) / freq_max | ||
| 466 | } | ||
diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs index e5bf30927..f049d6b12 100644 --- a/embassy-stm32/src/rtc/low_power.rs +++ b/embassy-stm32/src/rtc/low_power.rs | |||
| @@ -3,6 +3,7 @@ use embassy_time::{Duration, TICK_HZ}; | |||
| 3 | 3 | ||
| 4 | use super::{DateTimeError, Rtc, RtcError, bcd2_to_byte}; | 4 | use super::{DateTimeError, Rtc, RtcError, bcd2_to_byte}; |
| 5 | use crate::interrupt::typelevel::Interrupt; | 5 | use crate::interrupt::typelevel::Interrupt; |
| 6 | use crate::pac::rtc::vals::Wucksel; | ||
| 6 | use crate::peripherals::RTC; | 7 | use crate::peripherals::RTC; |
| 7 | use crate::rtc::{RtcTimeProvider, SealedInstance}; | 8 | use crate::rtc::{RtcTimeProvider, SealedInstance}; |
| 8 | 9 | ||
| @@ -58,60 +59,16 @@ impl core::ops::Sub for RtcInstant { | |||
| 58 | } | 59 | } |
| 59 | } | 60 | } |
| 60 | 61 | ||
| 61 | #[repr(u8)] | 62 | fn wucksel_compute_min(val: u32) -> (Wucksel, u32) { |
| 62 | #[derive(Clone, Copy, Debug)] | 63 | *[ |
| 63 | pub(crate) enum WakeupPrescaler { | 64 | (Wucksel::DIV2, 2), |
| 64 | Div2 = 2, | 65 | (Wucksel::DIV4, 4), |
| 65 | Div4 = 4, | 66 | (Wucksel::DIV8, 8), |
| 66 | Div8 = 8, | 67 | (Wucksel::DIV16, 16), |
| 67 | Div16 = 16, | 68 | ] |
| 68 | } | 69 | .iter() |
| 69 | 70 | .find(|(_, psc)| *psc as u32 > val) | |
| 70 | #[cfg(any( | 71 | .unwrap_or(&(Wucksel::DIV16, 16)) |
| 71 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba, stm32wlex | ||
| 72 | ))] | ||
| 73 | impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { | ||
| 74 | fn from(val: WakeupPrescaler) -> Self { | ||
| 75 | use crate::pac::rtc::vals::Wucksel; | ||
| 76 | |||
| 77 | match val { | ||
| 78 | WakeupPrescaler::Div2 => Wucksel::DIV2, | ||
| 79 | WakeupPrescaler::Div4 => Wucksel::DIV4, | ||
| 80 | WakeupPrescaler::Div8 => Wucksel::DIV8, | ||
| 81 | WakeupPrescaler::Div16 => Wucksel::DIV16, | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | #[cfg(any( | ||
| 87 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba, stm32wlex | ||
| 88 | ))] | ||
| 89 | impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler { | ||
| 90 | fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { | ||
| 91 | use crate::pac::rtc::vals::Wucksel; | ||
| 92 | |||
| 93 | match val { | ||
| 94 | Wucksel::DIV2 => WakeupPrescaler::Div2, | ||
| 95 | Wucksel::DIV4 => WakeupPrescaler::Div4, | ||
| 96 | Wucksel::DIV8 => WakeupPrescaler::Div8, | ||
| 97 | Wucksel::DIV16 => WakeupPrescaler::Div16, | ||
| 98 | _ => unreachable!(), | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | impl WakeupPrescaler { | ||
| 104 | pub fn compute_min(val: u32) -> Self { | ||
| 105 | *[ | ||
| 106 | WakeupPrescaler::Div2, | ||
| 107 | WakeupPrescaler::Div4, | ||
| 108 | WakeupPrescaler::Div8, | ||
| 109 | WakeupPrescaler::Div16, | ||
| 110 | ] | ||
| 111 | .iter() | ||
| 112 | .find(|psc| **psc as u32 > val) | ||
| 113 | .unwrap_or(&WakeupPrescaler::Div16) | ||
| 114 | } | ||
| 115 | } | 72 | } |
| 116 | 73 | ||
| 117 | impl Rtc { | 74 | impl Rtc { |
| @@ -138,7 +95,7 @@ impl Rtc { | |||
| 138 | let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64); | 95 | let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64); |
| 139 | let rtc_hz = Self::frequency().0 as u64; | 96 | let rtc_hz = Self::frequency().0 as u64; |
| 140 | let rtc_ticks = requested_duration * rtc_hz / TICK_HZ; | 97 | let rtc_ticks = requested_duration * rtc_hz / TICK_HZ; |
| 141 | let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); | 98 | let (wucksel, prescaler) = wucksel_compute_min((rtc_ticks / u16::MAX as u64) as u32); |
| 142 | 99 | ||
| 143 | // adjust the rtc ticks to the prescaler and subtract one rtc tick | 100 | // adjust the rtc ticks to the prescaler and subtract one rtc tick |
| 144 | let rtc_ticks = rtc_ticks / prescaler as u64; | 101 | let rtc_ticks = rtc_ticks / prescaler as u64; |
| @@ -159,7 +116,7 @@ impl Rtc { | |||
| 159 | while !regs.icsr().read().wutwf() {} | 116 | while !regs.icsr().read().wutwf() {} |
| 160 | } | 117 | } |
| 161 | 118 | ||
| 162 | regs.cr().modify(|w| w.set_wucksel(prescaler.into())); | 119 | regs.cr().modify(|w| w.set_wucksel(wucksel)); |
| 163 | regs.wutr().write(|w| w.set_wut(rtc_ticks)); | 120 | regs.wutr().write(|w| w.set_wut(rtc_ticks)); |
| 164 | regs.cr().modify(|w| w.set_wute(true)); | 121 | regs.cr().modify(|w| w.set_wute(true)); |
| 165 | regs.cr().modify(|w| w.set_wutie(true)); | 122 | regs.cr().modify(|w| w.set_wutie(true)); |
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 116b3c7ed..e88bd7ab2 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs | |||
| @@ -379,13 +379,16 @@ trait SealedInstance { | |||
| 379 | } | 379 | } |
| 380 | 380 | ||
| 381 | #[cfg(feature = "low-power")] | 381 | #[cfg(feature = "low-power")] |
| 382 | pub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig) { | 382 | pub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig, min_stop_pause: embassy_time::Duration) { |
| 383 | use crate::time_driver::get_driver; | ||
| 384 | |||
| 383 | #[cfg(feature = "_allow-disable-rtc")] | 385 | #[cfg(feature = "_allow-disable-rtc")] |
| 384 | if config._disable_rtc { | 386 | if config._disable_rtc { |
| 385 | return; | 387 | return; |
| 386 | } | 388 | } |
| 387 | 389 | ||
| 388 | crate::time_driver::get_driver().set_rtc(cs, Rtc::new_inner(config)); | 390 | get_driver().set_rtc(cs, Rtc::new_inner(config)); |
| 391 | get_driver().set_min_stop_pause(cs, min_stop_pause); | ||
| 389 | 392 | ||
| 390 | trace!("low power: stop with rtc configured"); | 393 | trace!("low power: stop with rtc configured"); |
| 391 | } | 394 | } |
diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index 726d1729a..ce4bc43c3 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs | |||
| @@ -391,7 +391,7 @@ pub struct Config { | |||
| 391 | pub frame_sync_polarity: FrameSyncPolarity, | 391 | pub frame_sync_polarity: FrameSyncPolarity, |
| 392 | pub frame_sync_active_level_length: word::U7, | 392 | pub frame_sync_active_level_length: word::U7, |
| 393 | pub frame_sync_definition: FrameSyncDefinition, | 393 | pub frame_sync_definition: FrameSyncDefinition, |
| 394 | pub frame_length: u8, | 394 | pub frame_length: u16, |
| 395 | pub clock_strobe: ClockStrobe, | 395 | pub clock_strobe: ClockStrobe, |
| 396 | pub output_drive: OutputDrive, | 396 | pub output_drive: OutputDrive, |
| 397 | pub master_clock_divider: Option<MasterClockDivider>, | 397 | pub master_clock_divider: Option<MasterClockDivider>, |
| @@ -696,7 +696,7 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 696 | w.set_fspol(config.frame_sync_polarity.fspol()); | 696 | w.set_fspol(config.frame_sync_polarity.fspol()); |
| 697 | w.set_fsdef(config.frame_sync_definition.fsdef()); | 697 | w.set_fsdef(config.frame_sync_definition.fsdef()); |
| 698 | w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1); | 698 | w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1); |
| 699 | w.set_frl(config.frame_length - 1); | 699 | w.set_frl((config.frame_length - 1).try_into().unwrap()); |
| 700 | }); | 700 | }); |
| 701 | 701 | ||
| 702 | ch.slotr().modify(|w| { | 702 | ch.slotr().modify(|w| { |
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 7db51d72e..0b75aef92 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs | |||
| @@ -196,6 +196,11 @@ fn calc_now(period: u32, counter: u16) -> u64 { | |||
| 196 | ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64) | 196 | ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64) |
| 197 | } | 197 | } |
| 198 | 198 | ||
| 199 | #[cfg(feature = "low-power")] | ||
| 200 | fn calc_period_counter(ticks: u64) -> (u32, u16) { | ||
| 201 | (2 * (ticks >> 16) as u32 + (ticks as u16 >= 0x8000) as u32, ticks as u16) | ||
| 202 | } | ||
| 203 | |||
| 199 | struct AlarmState { | 204 | struct AlarmState { |
| 200 | timestamp: Cell<u64>, | 205 | timestamp: Cell<u64>, |
| 201 | } | 206 | } |
| @@ -240,7 +245,7 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { | |||
| 240 | impl RtcDriver { | 245 | impl RtcDriver { |
| 241 | /// initialize the timer, but don't start it. Used for chips like stm32wle5 | 246 | /// initialize the timer, but don't start it. Used for chips like stm32wle5 |
| 242 | /// for low power where the timer config is lost in STOP2. | 247 | /// for low power where the timer config is lost in STOP2. |
| 243 | fn init_timer(&'static self, cs: critical_section::CriticalSection) { | 248 | pub(crate) fn init_timer(&'static self, cs: critical_section::CriticalSection) { |
| 244 | let r = regs_gp16(); | 249 | let r = regs_gp16(); |
| 245 | 250 | ||
| 246 | rcc::enable_and_reset_with_cs::<T>(cs); | 251 | rcc::enable_and_reset_with_cs::<T>(cs); |
| @@ -358,34 +363,10 @@ impl RtcDriver { | |||
| 358 | #[cfg(feature = "low-power")] | 363 | #[cfg(feature = "low-power")] |
| 359 | /// Add the given offset to the current time | 364 | /// Add the given offset to the current time |
| 360 | fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) { | 365 | fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) { |
| 361 | let offset = offset.as_ticks(); | 366 | let (period, counter) = calc_period_counter(self.now() + offset.as_ticks()); |
| 362 | let cnt = regs_gp16().cnt().read().cnt() as u32; | ||
| 363 | let period = self.period.load(Ordering::SeqCst); | ||
| 364 | |||
| 365 | // Correct the race, if it exists | ||
| 366 | let period = if period & 1 == 1 && cnt < u16::MAX as u32 / 2 { | ||
| 367 | period + 1 | ||
| 368 | } else { | ||
| 369 | period | ||
| 370 | }; | ||
| 371 | |||
| 372 | // Normalize to the full overflow | ||
| 373 | let period = (period / 2) * 2; | ||
| 374 | |||
| 375 | // Add the offset | ||
| 376 | let period = period + 2 * (offset / u16::MAX as u64) as u32; | ||
| 377 | let cnt = cnt + (offset % u16::MAX as u64) as u32; | ||
| 378 | |||
| 379 | let (cnt, period) = if cnt > u16::MAX as u32 { | ||
| 380 | (cnt - u16::MAX as u32, period + 2) | ||
| 381 | } else { | ||
| 382 | (cnt, period) | ||
| 383 | }; | ||
| 384 | |||
| 385 | let period = if cnt > u16::MAX as u32 / 2 { period + 1 } else { period }; | ||
| 386 | 367 | ||
| 387 | self.period.store(period, Ordering::SeqCst); | 368 | self.period.store(period, Ordering::SeqCst); |
| 388 | regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); | 369 | regs_gp16().cnt().write(|w| w.set_cnt(counter)); |
| 389 | 370 | ||
| 390 | // Now, recompute alarm | 371 | // Now, recompute alarm |
| 391 | let alarm = self.alarm.borrow(cs); | 372 | let alarm = self.alarm.borrow(cs); |
| @@ -399,13 +380,15 @@ impl RtcDriver { | |||
| 399 | #[cfg(feature = "low-power")] | 380 | #[cfg(feature = "low-power")] |
| 400 | /// Stop the wakeup alarm, if enabled, and add the appropriate offset | 381 | /// Stop the wakeup alarm, if enabled, and add the appropriate offset |
| 401 | fn stop_wakeup_alarm(&self, cs: CriticalSection) { | 382 | fn stop_wakeup_alarm(&self, cs: CriticalSection) { |
| 402 | if let Some(offset) = self.rtc.borrow(cs).borrow_mut().as_mut().unwrap().stop_wakeup_alarm(cs) { | 383 | if !regs_gp16().cr1().read().cen() |
| 384 | && let Some(offset) = self.rtc.borrow(cs).borrow_mut().as_mut().unwrap().stop_wakeup_alarm(cs) | ||
| 385 | { | ||
| 403 | self.add_time(offset, cs); | 386 | self.add_time(offset, cs); |
| 404 | } | 387 | } |
| 405 | } | 388 | } |
| 406 | 389 | ||
| 407 | /* | 390 | /* |
| 408 | Low-power public functions: all create or require a critical section | 391 | Low-power public functions: all require a critical section |
| 409 | */ | 392 | */ |
| 410 | #[cfg(feature = "low-power")] | 393 | #[cfg(feature = "low-power")] |
| 411 | pub(crate) fn set_min_stop_pause(&self, cs: CriticalSection, min_stop_pause: embassy_time::Duration) { | 394 | pub(crate) fn set_min_stop_pause(&self, cs: CriticalSection, min_stop_pause: embassy_time::Duration) { |
| @@ -422,49 +405,36 @@ impl RtcDriver { | |||
| 422 | 405 | ||
| 423 | #[cfg(feature = "low-power")] | 406 | #[cfg(feature = "low-power")] |
| 424 | /// Pause the timer if ready; return err if not | 407 | /// Pause the timer if ready; return err if not |
| 425 | pub(crate) fn pause_time(&self) -> Result<(), ()> { | 408 | pub(crate) fn pause_time(&self, cs: CriticalSection) -> Result<(), ()> { |
| 426 | critical_section::with(|cs| { | 409 | self.stop_wakeup_alarm(cs); |
| 427 | /* | 410 | |
| 428 | If the wakeup timer is currently running, then we need to stop it and | 411 | let time_until_next_alarm = self.time_until_next_alarm(cs); |
| 429 | add the elapsed time to the current time, as this will impact the result | 412 | if time_until_next_alarm < self.min_stop_pause.borrow(cs).get() { |
| 430 | of `time_until_next_alarm`. | 413 | trace!( |
| 431 | */ | 414 | "time_until_next_alarm < self.min_stop_pause ({})", |
| 432 | self.stop_wakeup_alarm(cs); | 415 | time_until_next_alarm |
| 433 | 416 | ); | |
| 434 | let time_until_next_alarm = self.time_until_next_alarm(cs); | 417 | Err(()) |
| 435 | if time_until_next_alarm < self.min_stop_pause.borrow(cs).get() { | 418 | } else { |
| 436 | trace!( | 419 | self.rtc |
| 437 | "time_until_next_alarm < self.min_stop_pause ({})", | 420 | .borrow(cs) |
| 438 | time_until_next_alarm | 421 | .borrow_mut() |
| 439 | ); | 422 | .as_mut() |
| 440 | Err(()) | 423 | .unwrap() |
| 441 | } else { | 424 | .start_wakeup_alarm(time_until_next_alarm, cs); |
| 442 | self.rtc | 425 | |
| 443 | .borrow(cs) | 426 | regs_gp16().cr1().modify(|w| w.set_cen(false)); |
| 444 | .borrow_mut() | 427 | // save the count for the timer as its lost in STOP2 for stm32wlex |
| 445 | .as_mut() | 428 | #[cfg(stm32wlex)] |
| 446 | .unwrap() | 429 | self.saved_count |
| 447 | .start_wakeup_alarm(time_until_next_alarm, cs); | 430 | .store(regs_gp16().cnt().read().cnt() as u16, Ordering::SeqCst); |
| 448 | 431 | Ok(()) | |
| 449 | regs_gp16().cr1().modify(|w| w.set_cen(false)); | 432 | } |
| 450 | // save the count for the timer as its lost in STOP2 for stm32wlex | ||
| 451 | #[cfg(stm32wlex)] | ||
| 452 | self.saved_count | ||
| 453 | .store(regs_gp16().cnt().read().cnt() as u16, Ordering::SeqCst); | ||
| 454 | Ok(()) | ||
| 455 | } | ||
| 456 | }) | ||
| 457 | } | 433 | } |
| 458 | 434 | ||
| 459 | #[cfg(feature = "low-power")] | 435 | #[cfg(feature = "low-power")] |
| 460 | /// Resume the timer with the given offset | 436 | /// Resume the timer with the given offset |
| 461 | pub(crate) fn resume_time(&self, cs: CriticalSection) { | 437 | pub(crate) fn resume_time(&self, cs: CriticalSection) { |
| 462 | if regs_gp16().cr1().read().cen() { | ||
| 463 | // Time isn't currently stopped | ||
| 464 | |||
| 465 | return; | ||
| 466 | } | ||
| 467 | |||
| 468 | self.stop_wakeup_alarm(cs); | 438 | self.stop_wakeup_alarm(cs); |
| 469 | 439 | ||
| 470 | regs_gp16().cr1().modify(|w| w.set_cen(true)); | 440 | regs_gp16().cr1().modify(|w| w.set_cen(true)); |
| @@ -546,8 +516,3 @@ pub(crate) const fn get_driver() -> &'static RtcDriver { | |||
| 546 | pub(crate) fn init(cs: CriticalSection) { | 516 | pub(crate) fn init(cs: CriticalSection) { |
| 547 | DRIVER.init(cs) | 517 | DRIVER.init(cs) |
| 548 | } | 518 | } |
| 549 | |||
| 550 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 551 | pub(crate) fn init_timer(cs: CriticalSection) { | ||
| 552 | DRIVER.init_timer(cs) | ||
| 553 | } | ||
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 9a56a41fb..77f19a37b 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs | |||
| @@ -77,8 +77,6 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 77 | 77 | ||
| 78 | this.inner.set_counting_mode(counting_mode); | 78 | this.inner.set_counting_mode(counting_mode); |
| 79 | this.set_frequency(freq); | 79 | this.set_frequency(freq); |
| 80 | this.inner.start(); | ||
| 81 | |||
| 82 | this.inner.enable_outputs(); | 80 | this.inner.enable_outputs(); |
| 83 | 81 | ||
| 84 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] | 82 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] |
| @@ -89,6 +87,10 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 89 | }); | 87 | }); |
| 90 | this.inner.set_autoreload_preload(true); | 88 | this.inner.set_autoreload_preload(true); |
| 91 | 89 | ||
| 90 | // Generate update event so pre-load registers are written to the shadow registers | ||
| 91 | this.inner.generate_update_event(); | ||
| 92 | this.inner.start(); | ||
| 93 | |||
| 92 | this | 94 | this |
| 93 | } | 95 | } |
| 94 | 96 | ||
| @@ -160,8 +162,8 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 160 | 162 | ||
| 161 | /// Set PWM frequency. | 163 | /// Set PWM frequency. |
| 162 | /// | 164 | /// |
| 163 | /// Note: when you call this, the max duty value changes, so you will have to | 165 | /// Note: that the frequency will not be applied in the timer until an update event |
| 164 | /// call `set_duty` on all channels with the duty calculated based on the new max duty. | 166 | /// occurs. |
| 165 | pub fn set_frequency(&mut self, freq: Hertz) { | 167 | pub fn set_frequency(&mut self, freq: Hertz) { |
| 166 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { | 168 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { |
| 167 | 2u8 | 169 | 2u8 |
| @@ -219,59 +221,53 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 219 | /// Note: | 221 | /// Note: |
| 220 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | 222 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. |
| 221 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { | 223 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { |
| 222 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | 224 | self.inner.enable_channel(channel, true); |
| 223 | let req = dma.request(); | 225 | self.inner.enable_update_dma(true); |
| 224 | 226 | self.inner.setup_update_dma(dma, channel, duty).await; | |
| 225 | let original_duty_state = self.inner.get_compare_value(channel); | 227 | self.inner.enable_update_dma(false); |
| 226 | let original_enable_state = self.inner.get_channel_enable_state(channel); | 228 | } |
| 227 | let original_update_dma_state = self.inner.get_update_dma_state(); | ||
| 228 | |||
| 229 | if !original_update_dma_state { | ||
| 230 | self.inner.enable_update_dma(true); | ||
| 231 | } | ||
| 232 | |||
| 233 | if !original_enable_state { | ||
| 234 | self.inner.enable_channel(channel, true); | ||
| 235 | } | ||
| 236 | |||
| 237 | unsafe { | ||
| 238 | #[cfg(not(any(bdma, gpdma)))] | ||
| 239 | use crate::dma::{Burst, FifoThreshold}; | ||
| 240 | use crate::dma::{Transfer, TransferOptions}; | ||
| 241 | |||
| 242 | let dma_transfer_option = TransferOptions { | ||
| 243 | #[cfg(not(any(bdma, gpdma)))] | ||
| 244 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 245 | #[cfg(not(any(bdma, gpdma)))] | ||
| 246 | mburst: Burst::Incr8, | ||
| 247 | ..Default::default() | ||
| 248 | }; | ||
| 249 | |||
| 250 | Transfer::new_write( | ||
| 251 | dma, | ||
| 252 | req, | ||
| 253 | duty, | ||
| 254 | self.inner.regs_gp16().ccr(channel.index()).as_ptr() as *mut u16, | ||
| 255 | dma_transfer_option, | ||
| 256 | ) | ||
| 257 | .await | ||
| 258 | }; | ||
| 259 | |||
| 260 | // restore output compare state | ||
| 261 | if !original_enable_state { | ||
| 262 | self.inner.enable_channel(channel, false); | ||
| 263 | } | ||
| 264 | |||
| 265 | self.inner.set_compare_value(channel, original_duty_state); | ||
| 266 | 229 | ||
| 267 | // Since DMA is closed before timer update event trigger DMA is turn off, | 230 | /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. |
| 268 | // this can almost always trigger a DMA FIFO error. | 231 | /// |
| 269 | // | 232 | /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers |
| 270 | // optional TODO: | 233 | /// in sequence on each update event (UEV). The data is written via the DMAR register using the |
| 271 | // clean FEIF after disable UDE | 234 | /// DMA base address (DBA) and burst length (DBL) configured in the DCR register. |
| 272 | if !original_update_dma_state { | 235 | /// |
| 273 | self.inner.enable_update_dma(false); | 236 | /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row |
| 274 | } | 237 | /// represents a single update event and each column corresponds to a specific timer channel (starting |
| 238 | /// from `starting_channel` up to and including `ending_channel`). | ||
| 239 | /// | ||
| 240 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: | ||
| 241 | /// | ||
| 242 | /// ```rust,ignore | ||
| 243 | /// let dma_buf: [u16; 16] = [ | ||
| 244 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 | ||
| 245 | /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 | ||
| 246 | /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 | ||
| 247 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 | ||
| 248 | /// ]; | ||
| 249 | /// ``` | ||
| 250 | /// | ||
| 251 | /// Each group of `N` values (where `N` is number of channels) is transferred on one update event, | ||
| 252 | /// updating the duty cycles of all selected channels simultaneously. | ||
| 253 | /// | ||
| 254 | /// Note: | ||
| 255 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. | ||
| 256 | /// Also be aware that embassy timers use one of timers internally. It is possible to | ||
| 257 | /// switch this timer by using `time-driver-timX` feature. | ||
| 258 | /// | ||
| 259 | pub async fn waveform_up_multi_channel( | ||
| 260 | &mut self, | ||
| 261 | dma: Peri<'_, impl super::UpDma<T>>, | ||
| 262 | starting_channel: Channel, | ||
| 263 | ending_channel: Channel, | ||
| 264 | duty: &[u16], | ||
| 265 | ) { | ||
| 266 | self.inner.enable_update_dma(true); | ||
| 267 | self.inner | ||
| 268 | .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) | ||
| 269 | .await; | ||
| 270 | self.inner.enable_update_dma(false); | ||
| 275 | } | 271 | } |
| 276 | } | 272 | } |
| 277 | 273 | ||
diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index 2a4ec2db0..9cf0f8c34 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs | |||
| @@ -60,6 +60,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { | |||
| 60 | this.inner.set_counting_mode(counting_mode); | 60 | this.inner.set_counting_mode(counting_mode); |
| 61 | this.inner.set_tick_freq(freq); | 61 | this.inner.set_tick_freq(freq); |
| 62 | this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details | 62 | this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details |
| 63 | this.inner.generate_update_event(); | ||
| 63 | this.inner.start(); | 64 | this.inner.start(); |
| 64 | 65 | ||
| 65 | // enable NVIC interrupt | 66 | // enable NVIC interrupt |
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 0122fe4f7..aba08081f 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs | |||
| @@ -13,6 +13,7 @@ use embassy_hal_internal::Peri; | |||
| 13 | pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource}; | 13 | pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource}; |
| 14 | 14 | ||
| 15 | use super::*; | 15 | use super::*; |
| 16 | use crate::dma::{Transfer, WritableRingBuffer}; | ||
| 16 | use crate::pac::timer::vals; | 17 | use crate::pac::timer::vals; |
| 17 | use crate::rcc; | 18 | use crate::rcc; |
| 18 | use crate::time::Hertz; | 19 | use crate::time::Hertz; |
| @@ -272,6 +273,17 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 272 | self.regs_core().cr1().modify(|r| r.set_cen(true)); | 273 | self.regs_core().cr1().modify(|r| r.set_cen(true)); |
| 273 | } | 274 | } |
| 274 | 275 | ||
| 276 | /// Generate timer update event from software. | ||
| 277 | /// | ||
| 278 | /// Set URS to avoid generating interrupt or DMA request. This update event is only | ||
| 279 | /// used to load value from pre-load registers. If called when the timer is running, | ||
| 280 | /// it may disrupt the output waveform. | ||
| 281 | pub fn generate_update_event(&self) { | ||
| 282 | self.regs_core().cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | ||
| 283 | self.regs_core().egr().write(|r| r.set_ug(true)); | ||
| 284 | self.regs_core().cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | ||
| 285 | } | ||
| 286 | |||
| 275 | /// Stop the timer. | 287 | /// Stop the timer. |
| 276 | pub fn stop(&self) { | 288 | pub fn stop(&self) { |
| 277 | self.regs_core().cr1().modify(|r| r.set_cen(false)); | 289 | self.regs_core().cr1().modify(|r| r.set_cen(false)); |
| @@ -322,10 +334,6 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 322 | let regs = self.regs_core(); | 334 | let regs = self.regs_core(); |
| 323 | regs.psc().write_value(psc); | 335 | regs.psc().write_value(psc); |
| 324 | regs.arr().write(|r| r.set_arr(arr)); | 336 | regs.arr().write(|r| r.set_arr(arr)); |
| 325 | |||
| 326 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | ||
| 327 | regs.egr().write(|r| r.set_ug(true)); | ||
| 328 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | ||
| 329 | } | 337 | } |
| 330 | #[cfg(not(stm32l0))] | 338 | #[cfg(not(stm32l0))] |
| 331 | TimerBits::Bits32 => { | 339 | TimerBits::Bits32 => { |
| @@ -335,10 +343,6 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 335 | let regs = self.regs_gp32_unchecked(); | 343 | let regs = self.regs_gp32_unchecked(); |
| 336 | regs.psc().write_value(psc); | 344 | regs.psc().write_value(psc); |
| 337 | regs.arr().write_value(arr); | 345 | regs.arr().write_value(arr); |
| 338 | |||
| 339 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | ||
| 340 | regs.egr().write(|r| r.set_ug(true)); | ||
| 341 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | ||
| 342 | } | 346 | } |
| 343 | } | 347 | } |
| 344 | } | 348 | } |
| @@ -656,6 +660,167 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { | |||
| 656 | } | 660 | } |
| 657 | } | 661 | } |
| 658 | 662 | ||
| 663 | /// Setup a ring buffer for the channel | ||
| 664 | pub fn setup_ring_buffer<'a>( | ||
| 665 | &mut self, | ||
| 666 | dma: Peri<'a, impl super::UpDma<T>>, | ||
| 667 | channel: Channel, | ||
| 668 | dma_buf: &'a mut [u16], | ||
| 669 | ) -> WritableRingBuffer<'a, u16> { | ||
| 670 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 671 | let req = dma.request(); | ||
| 672 | |||
| 673 | unsafe { | ||
| 674 | use crate::dma::TransferOptions; | ||
| 675 | #[cfg(not(any(bdma, gpdma)))] | ||
| 676 | use crate::dma::{Burst, FifoThreshold}; | ||
| 677 | |||
| 678 | let dma_transfer_option = TransferOptions { | ||
| 679 | #[cfg(not(any(bdma, gpdma)))] | ||
| 680 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 681 | #[cfg(not(any(bdma, gpdma)))] | ||
| 682 | mburst: Burst::Incr8, | ||
| 683 | ..Default::default() | ||
| 684 | }; | ||
| 685 | |||
| 686 | WritableRingBuffer::new( | ||
| 687 | dma, | ||
| 688 | req, | ||
| 689 | self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, | ||
| 690 | dma_buf, | ||
| 691 | dma_transfer_option, | ||
| 692 | ) | ||
| 693 | } | ||
| 694 | } | ||
| 695 | |||
| 696 | /// Generate a sequence of PWM waveform | ||
| 697 | /// | ||
| 698 | /// Note: | ||
| 699 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | ||
| 700 | pub fn setup_update_dma<'a>( | ||
| 701 | &mut self, | ||
| 702 | dma: Peri<'a, impl super::UpDma<T>>, | ||
| 703 | channel: Channel, | ||
| 704 | duty: &'a [u16], | ||
| 705 | ) -> Transfer<'a> { | ||
| 706 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 707 | let req = dma.request(); | ||
| 708 | |||
| 709 | unsafe { | ||
| 710 | #[cfg(not(any(bdma, gpdma)))] | ||
| 711 | use crate::dma::{Burst, FifoThreshold}; | ||
| 712 | use crate::dma::{Transfer, TransferOptions}; | ||
| 713 | |||
| 714 | let dma_transfer_option = TransferOptions { | ||
| 715 | #[cfg(not(any(bdma, gpdma)))] | ||
| 716 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 717 | #[cfg(not(any(bdma, gpdma)))] | ||
| 718 | mburst: Burst::Incr8, | ||
| 719 | ..Default::default() | ||
| 720 | }; | ||
| 721 | |||
| 722 | match self.bits() { | ||
| 723 | TimerBits::Bits16 => Transfer::new_write( | ||
| 724 | dma, | ||
| 725 | req, | ||
| 726 | duty, | ||
| 727 | self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, | ||
| 728 | dma_transfer_option, | ||
| 729 | ), | ||
| 730 | #[cfg(not(any(stm32l0)))] | ||
| 731 | TimerBits::Bits32 => { | ||
| 732 | #[cfg(not(any(bdma, gpdma)))] | ||
| 733 | panic!("unsupported timer bits"); | ||
| 734 | |||
| 735 | #[cfg(any(bdma, gpdma))] | ||
| 736 | Transfer::new_write( | ||
| 737 | dma, | ||
| 738 | req, | ||
| 739 | duty, | ||
| 740 | self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32, | ||
| 741 | dma_transfer_option, | ||
| 742 | ) | ||
| 743 | } | ||
| 744 | } | ||
| 745 | } | ||
| 746 | } | ||
| 747 | |||
| 748 | /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. | ||
| 749 | /// | ||
| 750 | /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers | ||
| 751 | /// in sequence on each update event (UEV). The data is written via the DMAR register using the | ||
| 752 | /// DMA base address (DBA) and burst length (DBL) configured in the DCR register. | ||
| 753 | /// | ||
| 754 | /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row | ||
| 755 | /// represents a single update event and each column corresponds to a specific timer channel (starting | ||
| 756 | /// from `starting_channel` up to and including `ending_channel`). | ||
| 757 | /// | ||
| 758 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: | ||
| 759 | /// | ||
| 760 | /// ```rust,ignore | ||
| 761 | /// let dma_buf: [u16; 16] = [ | ||
| 762 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 | ||
| 763 | /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 | ||
| 764 | /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 | ||
| 765 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 | ||
| 766 | /// ]; | ||
| 767 | /// ``` | ||
| 768 | /// | ||
| 769 | /// Each group of `N` values (where `N` is number of channels) is transferred on one update event, | ||
| 770 | /// updating the duty cycles of all selected channels simultaneously. | ||
| 771 | /// | ||
| 772 | /// Note: | ||
| 773 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. | ||
| 774 | /// Also be aware that embassy timers use one of timers internally. It is possible to | ||
| 775 | /// switch this timer by using `time-driver-timX` feature. | ||
| 776 | /// | ||
| 777 | pub fn setup_update_dma_burst<'a>( | ||
| 778 | &mut self, | ||
| 779 | dma: Peri<'a, impl super::UpDma<T>>, | ||
| 780 | starting_channel: Channel, | ||
| 781 | ending_channel: Channel, | ||
| 782 | duty: &'a [u16], | ||
| 783 | ) -> Transfer<'a> { | ||
| 784 | let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32; | ||
| 785 | let start_ch_index = starting_channel.index(); | ||
| 786 | let end_ch_index = ending_channel.index(); | ||
| 787 | |||
| 788 | assert!(start_ch_index <= end_ch_index); | ||
| 789 | |||
| 790 | let ccrx_addr = self.regs_gp16().ccr(start_ch_index).as_ptr() as u32; | ||
| 791 | self.regs_gp16() | ||
| 792 | .dcr() | ||
| 793 | .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8)); | ||
| 794 | self.regs_gp16() | ||
| 795 | .dcr() | ||
| 796 | .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8)); | ||
| 797 | |||
| 798 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 799 | let req = dma.request(); | ||
| 800 | |||
| 801 | unsafe { | ||
| 802 | #[cfg(not(any(bdma, gpdma)))] | ||
| 803 | use crate::dma::{Burst, FifoThreshold}; | ||
| 804 | use crate::dma::{Transfer, TransferOptions}; | ||
| 805 | |||
| 806 | let dma_transfer_option = TransferOptions { | ||
| 807 | #[cfg(not(any(bdma, gpdma)))] | ||
| 808 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 809 | #[cfg(not(any(bdma, gpdma)))] | ||
| 810 | mburst: Burst::Incr4, | ||
| 811 | ..Default::default() | ||
| 812 | }; | ||
| 813 | |||
| 814 | Transfer::new_write( | ||
| 815 | dma, | ||
| 816 | req, | ||
| 817 | duty, | ||
| 818 | self.regs_gp16().dmar().as_ptr() as *mut u16, | ||
| 819 | dma_transfer_option, | ||
| 820 | ) | ||
| 821 | } | ||
| 822 | } | ||
| 823 | |||
| 659 | /// Get capture value for a channel. | 824 | /// Get capture value for a channel. |
| 660 | pub fn get_capture_value(&self, channel: Channel) -> u32 { | 825 | pub fn get_capture_value(&self, channel: Channel) -> u32 { |
| 661 | self.get_compare_value(channel) | 826 | self.get_compare_value(channel) |
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 804d1ef37..3fa363881 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs | |||
| @@ -12,6 +12,7 @@ pub mod low_level; | |||
| 12 | pub mod one_pulse; | 12 | pub mod one_pulse; |
| 13 | pub mod pwm_input; | 13 | pub mod pwm_input; |
| 14 | pub mod qei; | 14 | pub mod qei; |
| 15 | pub mod ringbuffered; | ||
| 15 | pub mod simple_pwm; | 16 | pub mod simple_pwm; |
| 16 | 17 | ||
| 17 | use crate::interrupt; | 18 | use crate::interrupt; |
diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index da8a79b09..057ab011a 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs | |||
| @@ -47,6 +47,7 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { | |||
| 47 | inner.set_counting_mode(CountingMode::EdgeAlignedUp); | 47 | inner.set_counting_mode(CountingMode::EdgeAlignedUp); |
| 48 | inner.set_tick_freq(freq); | 48 | inner.set_tick_freq(freq); |
| 49 | inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details | 49 | inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details |
| 50 | inner.generate_update_event(); | ||
| 50 | inner.start(); | 51 | inner.start(); |
| 51 | 52 | ||
| 52 | // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6 | 53 | // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6 |
diff --git a/embassy-stm32/src/timer/ringbuffered.rs b/embassy-stm32/src/timer/ringbuffered.rs new file mode 100644 index 000000000..e8f97bf59 --- /dev/null +++ b/embassy-stm32/src/timer/ringbuffered.rs | |||
| @@ -0,0 +1,169 @@ | |||
| 1 | //! RingBuffered PWM driver. | ||
| 2 | |||
| 3 | use core::mem::ManuallyDrop; | ||
| 4 | use core::task::Waker; | ||
| 5 | |||
| 6 | use super::low_level::Timer; | ||
| 7 | use super::{Channel, GeneralInstance4Channel}; | ||
| 8 | use crate::dma::WritableRingBuffer; | ||
| 9 | use crate::dma::ringbuffer::Error; | ||
| 10 | |||
| 11 | /// A PWM channel that uses a DMA ring buffer for continuous waveform generation. | ||
| 12 | /// | ||
| 13 | /// This allows you to continuously update PWM duty cycles via DMA without blocking the CPU. | ||
| 14 | /// The ring buffer enables smooth, uninterrupted waveform generation by automatically cycling | ||
| 15 | /// through duty cycle values stored in memory. | ||
| 16 | /// | ||
| 17 | /// You can write new duty cycle values to the ring buffer while it's running, enabling | ||
| 18 | /// dynamic waveform generation for applications like motor control, LED dimming, or audio output. | ||
| 19 | /// | ||
| 20 | /// # Example | ||
| 21 | /// ```ignore | ||
| 22 | /// let mut channel = pwm.ch1().into_ring_buffered_channel(dma_ch, &mut buffer); | ||
| 23 | /// channel.start(); // Start DMA transfer | ||
| 24 | /// channel.write(&[100, 200, 300]).ok(); // Update duty cycles | ||
| 25 | /// ``` | ||
| 26 | pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> { | ||
| 27 | timer: ManuallyDrop<Timer<'d, T>>, | ||
| 28 | ring_buf: WritableRingBuffer<'d, u16>, | ||
| 29 | channel: Channel, | ||
| 30 | } | ||
| 31 | |||
| 32 | impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { | ||
| 33 | pub(crate) fn new( | ||
| 34 | timer: ManuallyDrop<Timer<'d, T>>, | ||
| 35 | channel: Channel, | ||
| 36 | ring_buf: WritableRingBuffer<'d, u16>, | ||
| 37 | ) -> Self { | ||
| 38 | Self { | ||
| 39 | timer, | ||
| 40 | ring_buf, | ||
| 41 | channel, | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | /// Start the ring buffer operation. | ||
| 46 | /// | ||
| 47 | /// You must call this after creating it for it to work. | ||
| 48 | pub fn start(&mut self) { | ||
| 49 | self.ring_buf.start() | ||
| 50 | } | ||
| 51 | |||
| 52 | /// Clear all data in the ring buffer. | ||
| 53 | pub fn clear(&mut self) { | ||
| 54 | self.ring_buf.clear() | ||
| 55 | } | ||
| 56 | |||
| 57 | /// Write elements directly to the raw buffer. This can be used to fill the buffer before starting the DMA transfer. | ||
| 58 | pub fn write_immediate(&mut self, buf: &[u16]) -> Result<(usize, usize), Error> { | ||
| 59 | self.ring_buf.write_immediate(buf) | ||
| 60 | } | ||
| 61 | |||
| 62 | /// Write elements from the ring buffer | ||
| 63 | /// Return a tuple of the length written and the length remaining in the buffer | ||
| 64 | pub fn write(&mut self, buf: &[u16]) -> Result<(usize, usize), Error> { | ||
| 65 | self.ring_buf.write(buf) | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Write an exact number of elements to the ringbuffer. | ||
| 69 | pub async fn write_exact(&mut self, buffer: &[u16]) -> Result<usize, Error> { | ||
| 70 | self.ring_buf.write_exact(buffer).await | ||
| 71 | } | ||
| 72 | |||
| 73 | /// Wait for any ring buffer write error. | ||
| 74 | pub async fn wait_write_error(&mut self) -> Result<usize, Error> { | ||
| 75 | self.ring_buf.wait_write_error().await | ||
| 76 | } | ||
| 77 | |||
| 78 | /// The current length of the ringbuffer | ||
| 79 | pub fn len(&mut self) -> Result<usize, Error> { | ||
| 80 | self.ring_buf.len() | ||
| 81 | } | ||
| 82 | |||
| 83 | /// The capacity of the ringbuffer | ||
| 84 | pub const fn capacity(&self) -> usize { | ||
| 85 | self.ring_buf.capacity() | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Set a waker to be woken when at least one byte is send. | ||
| 89 | pub fn set_waker(&mut self, waker: &Waker) { | ||
| 90 | self.ring_buf.set_waker(waker) | ||
| 91 | } | ||
| 92 | |||
| 93 | /// Request the DMA to reset. The configuration for this channel will not be preserved. | ||
| 94 | /// | ||
| 95 | /// This doesn't immediately stop the transfer, you have to wait until is_running returns false. | ||
| 96 | pub fn request_reset(&mut self) { | ||
| 97 | self.ring_buf.request_reset() | ||
| 98 | } | ||
| 99 | |||
| 100 | /// Request the transfer to pause, keeping the existing configuration for this channel. | ||
| 101 | /// To restart the transfer, call [`start`](Self::start) again. | ||
| 102 | /// | ||
| 103 | /// This doesn't immediately stop the transfer, you have to wait until is_running returns false. | ||
| 104 | pub fn request_pause(&mut self) { | ||
| 105 | self.ring_buf.request_pause() | ||
| 106 | } | ||
| 107 | |||
| 108 | /// Return whether DMA is still running. | ||
| 109 | /// | ||
| 110 | /// If this returns false, it can be because either the transfer finished, or it was requested to stop early with request_stop. | ||
| 111 | pub fn is_running(&mut self) -> bool { | ||
| 112 | self.ring_buf.is_running() | ||
| 113 | } | ||
| 114 | |||
| 115 | /// Stop the DMA transfer and await until the buffer is empty. | ||
| 116 | /// | ||
| 117 | /// This disables the DMA transfer's circular mode so that the transfer stops when all available data has been written. | ||
| 118 | /// | ||
| 119 | /// This is designed to be used with streaming output data such as the I2S/SAI or DAC. | ||
| 120 | pub async fn stop(&mut self) { | ||
| 121 | self.ring_buf.stop().await | ||
| 122 | } | ||
| 123 | |||
| 124 | /// Enable the given channel. | ||
| 125 | pub fn enable(&mut self) { | ||
| 126 | self.timer.enable_channel(self.channel, true); | ||
| 127 | } | ||
| 128 | |||
| 129 | /// Disable the given channel. | ||
| 130 | pub fn disable(&mut self) { | ||
| 131 | self.timer.enable_channel(self.channel, false); | ||
| 132 | } | ||
| 133 | |||
| 134 | /// Check whether given channel is enabled | ||
| 135 | pub fn is_enabled(&self) -> bool { | ||
| 136 | self.timer.get_channel_enable_state(self.channel) | ||
| 137 | } | ||
| 138 | |||
| 139 | /// Get max duty value. | ||
| 140 | /// | ||
| 141 | /// This value depends on the configured frequency and the timer's clock rate from RCC. | ||
| 142 | pub fn max_duty_cycle(&self) -> u16 { | ||
| 143 | let max = self.timer.get_max_compare_value(); | ||
| 144 | assert!(max < u16::MAX as u32); | ||
| 145 | max as u16 + 1 | ||
| 146 | } | ||
| 147 | |||
| 148 | /// Set the output polarity for a given channel. | ||
| 149 | pub fn set_polarity(&mut self, polarity: super::low_level::OutputPolarity) { | ||
| 150 | self.timer.set_output_polarity(self.channel, polarity); | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Set the output compare mode for a given channel. | ||
| 154 | pub fn set_output_compare_mode(&mut self, mode: super::low_level::OutputCompareMode) { | ||
| 155 | self.timer.set_output_compare_mode(self.channel, mode); | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | /// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. | ||
| 160 | pub struct RingBufferedPwmChannels<'d, T: GeneralInstance4Channel> { | ||
| 161 | /// Channel 1 | ||
| 162 | pub ch1: RingBufferedPwmChannel<'d, T>, | ||
| 163 | /// Channel 2 | ||
| 164 | pub ch2: RingBufferedPwmChannel<'d, T>, | ||
| 165 | /// Channel 3 | ||
| 166 | pub ch3: RingBufferedPwmChannel<'d, T>, | ||
| 167 | /// Channel 4 | ||
| 168 | pub ch4: RingBufferedPwmChannel<'d, T>, | ||
| 169 | } | ||
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 36303aeb4..484e9fd81 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs | |||
| @@ -4,7 +4,8 @@ use core::marker::PhantomData; | |||
| 4 | use core::mem::ManuallyDrop; | 4 | use core::mem::ManuallyDrop; |
| 5 | 5 | ||
| 6 | use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; | 6 | use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; |
| 7 | use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin}; | 7 | use super::ringbuffered::RingBufferedPwmChannel; |
| 8 | use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; | ||
| 8 | use crate::Peri; | 9 | use crate::Peri; |
| 9 | #[cfg(gpio_v2)] | 10 | #[cfg(gpio_v2)] |
| 10 | use crate::gpio::Pull; | 11 | use crate::gpio::Pull; |
| @@ -158,6 +159,33 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { | |||
| 158 | pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) { | 159 | pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) { |
| 159 | self.timer.set_output_compare_mode(self.channel, mode); | 160 | self.timer.set_output_compare_mode(self.channel, mode); |
| 160 | } | 161 | } |
| 162 | |||
| 163 | /// Convert this PWM channel into a ring-buffered PWM channel. | ||
| 164 | /// | ||
| 165 | /// This allows continuous PWM waveform generation using a DMA ring buffer. | ||
| 166 | /// The ring buffer enables dynamic updates to the PWM duty cycle without blocking. | ||
| 167 | /// | ||
| 168 | /// # Arguments | ||
| 169 | /// * `tx_dma` - The DMA channel to use for transferring duty cycle values | ||
| 170 | /// * `dma_buf` - The buffer to use as a ring buffer (must be non-empty and <= 65535 elements) | ||
| 171 | /// | ||
| 172 | /// # Panics | ||
| 173 | /// Panics if `dma_buf` is empty or longer than 65535 elements. | ||
| 174 | pub fn into_ring_buffered_channel( | ||
| 175 | mut self, | ||
| 176 | tx_dma: Peri<'d, impl super::UpDma<T>>, | ||
| 177 | dma_buf: &'d mut [u16], | ||
| 178 | ) -> RingBufferedPwmChannel<'d, T> { | ||
| 179 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 180 | |||
| 181 | self.timer.enable_update_dma(true); | ||
| 182 | |||
| 183 | RingBufferedPwmChannel::new( | ||
| 184 | unsafe { self.timer.clone_unchecked() }, | ||
| 185 | self.channel, | ||
| 186 | self.timer.setup_ring_buffer(tx_dma, self.channel, dma_buf), | ||
| 187 | ) | ||
| 188 | } | ||
| 161 | } | 189 | } |
| 162 | 190 | ||
| 163 | /// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. | 191 | /// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. |
| @@ -198,7 +226,6 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 198 | this.inner.set_counting_mode(counting_mode); | 226 | this.inner.set_counting_mode(counting_mode); |
| 199 | this.set_frequency(freq); | 227 | this.set_frequency(freq); |
| 200 | this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details | 228 | this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details |
| 201 | this.inner.start(); | ||
| 202 | 229 | ||
| 203 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] | 230 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] |
| 204 | .iter() | 231 | .iter() |
| @@ -207,6 +234,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 207 | 234 | ||
| 208 | this.inner.set_output_compare_preload(channel, true); | 235 | this.inner.set_output_compare_preload(channel, true); |
| 209 | }); | 236 | }); |
| 237 | this.inner.set_autoreload_preload(true); | ||
| 238 | |||
| 239 | // Generate update event so pre-load registers are written to the shadow registers | ||
| 240 | this.inner.generate_update_event(); | ||
| 241 | this.inner.start(); | ||
| 210 | 242 | ||
| 211 | this | 243 | this |
| 212 | } | 244 | } |
| @@ -285,8 +317,8 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 285 | 317 | ||
| 286 | /// Set PWM frequency. | 318 | /// Set PWM frequency. |
| 287 | /// | 319 | /// |
| 288 | /// Note: when you call this, the max duty value changes, so you will have to | 320 | /// Note: that the frequency will not be applied in the timer until an update event |
| 289 | /// call `set_duty` on all channels with the duty calculated based on the new max duty. | 321 | /// occurs. |
| 290 | pub fn set_frequency(&mut self, freq: Hertz) { | 322 | pub fn set_frequency(&mut self, freq: Hertz) { |
| 291 | // TODO: prevent ARR = u16::MAX? | 323 | // TODO: prevent ARR = u16::MAX? |
| 292 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { | 324 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { |
| @@ -309,80 +341,14 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 309 | /// Generate a sequence of PWM waveform | 341 | /// Generate a sequence of PWM waveform |
| 310 | /// | 342 | /// |
| 311 | /// Note: | 343 | /// Note: |
| 312 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | 344 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. |
| 345 | /// Also be aware that embassy timers use one of timers internally. It is possible to | ||
| 346 | /// 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]) { | 347 | 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 | 348 | self.inner.enable_channel(channel, true); |
| 315 | let req = dma.request(); | 349 | self.inner.enable_update_dma(true); |
| 316 | 350 | self.inner.setup_update_dma(dma, channel, duty).await; | |
| 317 | let original_duty_state = self.channel(channel).current_duty_cycle(); | 351 | self.inner.enable_update_dma(false); |
| 318 | let original_enable_state = self.channel(channel).is_enabled(); | ||
| 319 | let original_update_dma_state = self.inner.get_update_dma_state(); | ||
| 320 | |||
| 321 | if !original_update_dma_state { | ||
| 322 | self.inner.enable_update_dma(true); | ||
| 323 | } | ||
| 324 | |||
| 325 | if !original_enable_state { | ||
| 326 | self.channel(channel).enable(); | ||
| 327 | } | ||
| 328 | |||
| 329 | unsafe { | ||
| 330 | #[cfg(not(any(bdma, gpdma)))] | ||
| 331 | use crate::dma::{Burst, FifoThreshold}; | ||
| 332 | use crate::dma::{Transfer, TransferOptions}; | ||
| 333 | |||
| 334 | let dma_transfer_option = TransferOptions { | ||
| 335 | #[cfg(not(any(bdma, gpdma)))] | ||
| 336 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 337 | #[cfg(not(any(bdma, gpdma)))] | ||
| 338 | mburst: Burst::Incr8, | ||
| 339 | ..Default::default() | ||
| 340 | }; | ||
| 341 | |||
| 342 | match self.inner.bits() { | ||
| 343 | TimerBits::Bits16 => { | ||
| 344 | Transfer::new_write( | ||
| 345 | dma, | ||
| 346 | req, | ||
| 347 | duty, | ||
| 348 | self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, | ||
| 349 | dma_transfer_option, | ||
| 350 | ) | ||
| 351 | .await | ||
| 352 | } | ||
| 353 | #[cfg(not(any(stm32l0)))] | ||
| 354 | TimerBits::Bits32 => { | ||
| 355 | #[cfg(not(any(bdma, gpdma)))] | ||
| 356 | panic!("unsupported timer bits"); | ||
| 357 | |||
| 358 | #[cfg(any(bdma, gpdma))] | ||
| 359 | Transfer::new_write( | ||
| 360 | dma, | ||
| 361 | req, | ||
| 362 | duty, | ||
| 363 | self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32, | ||
| 364 | dma_transfer_option, | ||
| 365 | ) | ||
| 366 | .await | ||
| 367 | } | ||
| 368 | }; | ||
| 369 | }; | ||
| 370 | |||
| 371 | // restore output compare state | ||
| 372 | if !original_enable_state { | ||
| 373 | self.channel(channel).disable(); | ||
| 374 | } | ||
| 375 | |||
| 376 | self.channel(channel).set_duty_cycle(original_duty_state); | ||
| 377 | |||
| 378 | // Since DMA is closed before timer update event trigger DMA is turn off, | ||
| 379 | // this can almost always trigger a DMA FIFO error. | ||
| 380 | // | ||
| 381 | // optional TODO: | ||
| 382 | // clean FEIF after disable UDE | ||
| 383 | if !original_update_dma_state { | ||
| 384 | self.inner.enable_update_dma(false); | ||
| 385 | } | ||
| 386 | } | 352 | } |
| 387 | 353 | ||
| 388 | /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. | 354 | /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. |
| @@ -397,18 +363,23 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 397 | /// | 363 | /// |
| 398 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: | 364 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: |
| 399 | /// | 365 | /// |
| 366 | /// ```rust,ignore | ||
| 400 | /// let dma_buf: [u16; 16] = [ | 367 | /// let dma_buf: [u16; 16] = [ |
| 401 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 | 368 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 |
| 402 | /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 | 369 | /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 |
| 403 | /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 | 370 | /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 |
| 404 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 | 371 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 |
| 405 | /// ]; | 372 | /// ]; |
| 373 | /// ``` | ||
| 406 | /// | 374 | /// |
| 407 | /// Each group of N values (where N = number of channels) is transferred on one update event, | 375 | /// Each group of `N` values (where `N` is number of channels) is transferred on one update event, |
| 408 | /// updating the duty cycles of all selected channels simultaneously. | 376 | /// updating the duty cycles of all selected channels simultaneously. |
| 409 | /// | 377 | /// |
| 410 | /// Note: | 378 | /// Note: |
| 411 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | 379 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. |
| 380 | /// Also be aware that embassy timers use one of timers internally. It is possible to | ||
| 381 | /// switch this timer by using `time-driver-timX` feature. | ||
| 382 | /// | ||
| 412 | pub async fn waveform_up_multi_channel( | 383 | pub async fn waveform_up_multi_channel( |
| 413 | &mut self, | 384 | &mut self, |
| 414 | dma: Peri<'_, impl super::UpDma<T>>, | 385 | dma: Peri<'_, impl super::UpDma<T>>, |
| @@ -416,148 +387,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 416 | ending_channel: Channel, | 387 | ending_channel: Channel, |
| 417 | duty: &[u16], | 388 | duty: &[u16], |
| 418 | ) { | 389 | ) { |
| 419 | let cr1_addr = self.inner.regs_gp16().cr1().as_ptr() as u32; | 390 | self.inner.enable_update_dma(true); |
| 420 | let start_ch_index = starting_channel.index(); | ||
| 421 | let end_ch_index = ending_channel.index(); | ||
| 422 | |||
| 423 | assert!(start_ch_index <= end_ch_index); | ||
| 424 | |||
| 425 | let ccrx_addr = self.inner.regs_gp16().ccr(start_ch_index).as_ptr() as u32; | ||
| 426 | self.inner | ||
| 427 | .regs_gp16() | ||
| 428 | .dcr() | ||
| 429 | .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8)); | ||
| 430 | self.inner | 391 | self.inner |
| 431 | .regs_gp16() | 392 | .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) |
| 432 | .dcr() | 393 | .await; |
| 433 | .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8)); | 394 | self.inner.enable_update_dma(false); |
| 434 | |||
| 435 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 436 | let req = dma.request(); | ||
| 437 | |||
| 438 | let original_update_dma_state = self.inner.get_update_dma_state(); | ||
| 439 | if !original_update_dma_state { | ||
| 440 | self.inner.enable_update_dma(true); | ||
| 441 | } | ||
| 442 | |||
| 443 | unsafe { | ||
| 444 | #[cfg(not(any(bdma, gpdma)))] | ||
| 445 | use crate::dma::{Burst, FifoThreshold}; | ||
| 446 | use crate::dma::{Transfer, TransferOptions}; | ||
| 447 | |||
| 448 | let dma_transfer_option = TransferOptions { | ||
| 449 | #[cfg(not(any(bdma, gpdma)))] | ||
| 450 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 451 | #[cfg(not(any(bdma, gpdma)))] | ||
| 452 | mburst: Burst::Incr4, | ||
| 453 | ..Default::default() | ||
| 454 | }; | ||
| 455 | |||
| 456 | Transfer::new_write( | ||
| 457 | dma, | ||
| 458 | req, | ||
| 459 | duty, | ||
| 460 | self.inner.regs_gp16().dmar().as_ptr() as *mut u16, | ||
| 461 | dma_transfer_option, | ||
| 462 | ) | ||
| 463 | .await | ||
| 464 | }; | ||
| 465 | |||
| 466 | if !original_update_dma_state { | ||
| 467 | self.inner.enable_update_dma(false); | ||
| 468 | } | ||
| 469 | } | ||
| 470 | } | ||
| 471 | |||
| 472 | impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | ||
| 473 | /// Generate a sequence of PWM waveform | ||
| 474 | pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) { | ||
| 475 | use crate::pac::timer::vals::Ccds; | ||
| 476 | |||
| 477 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 478 | let req = dma.request(); | ||
| 479 | |||
| 480 | let cc_channel = C::CHANNEL; | ||
| 481 | |||
| 482 | let original_duty_state = self.channel(cc_channel).current_duty_cycle(); | ||
| 483 | let original_enable_state = self.channel(cc_channel).is_enabled(); | ||
| 484 | let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE; | ||
| 485 | let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); | ||
| 486 | |||
| 487 | // redirect CC DMA request onto Update Event | ||
| 488 | if !original_cc_dma_on_update { | ||
| 489 | self.inner.set_cc_dma_selection(Ccds::ON_UPDATE) | ||
| 490 | } | ||
| 491 | |||
| 492 | if !original_cc_dma_enabled { | ||
| 493 | self.inner.set_cc_dma_enable_state(cc_channel, true); | ||
| 494 | } | ||
| 495 | |||
| 496 | if !original_enable_state { | ||
| 497 | self.channel(cc_channel).enable(); | ||
| 498 | } | ||
| 499 | |||
| 500 | unsafe { | ||
| 501 | #[cfg(not(any(bdma, gpdma)))] | ||
| 502 | use crate::dma::{Burst, FifoThreshold}; | ||
| 503 | use crate::dma::{Transfer, TransferOptions}; | ||
| 504 | |||
| 505 | let dma_transfer_option = TransferOptions { | ||
| 506 | #[cfg(not(any(bdma, gpdma)))] | ||
| 507 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 508 | #[cfg(not(any(bdma, gpdma)))] | ||
| 509 | mburst: Burst::Incr8, | ||
| 510 | ..Default::default() | ||
| 511 | }; | ||
| 512 | |||
| 513 | match self.inner.bits() { | ||
| 514 | TimerBits::Bits16 => { | ||
| 515 | Transfer::new_write( | ||
| 516 | dma, | ||
| 517 | req, | ||
| 518 | duty, | ||
| 519 | self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16, | ||
| 520 | dma_transfer_option, | ||
| 521 | ) | ||
| 522 | .await | ||
| 523 | } | ||
| 524 | #[cfg(not(any(stm32l0)))] | ||
| 525 | TimerBits::Bits32 => { | ||
| 526 | #[cfg(not(any(bdma, gpdma)))] | ||
| 527 | panic!("unsupported timer bits"); | ||
| 528 | |||
| 529 | #[cfg(any(bdma, gpdma))] | ||
| 530 | Transfer::new_write( | ||
| 531 | dma, | ||
| 532 | req, | ||
| 533 | duty, | ||
| 534 | self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32, | ||
| 535 | dma_transfer_option, | ||
| 536 | ) | ||
| 537 | .await | ||
| 538 | } | ||
| 539 | }; | ||
| 540 | }; | ||
| 541 | |||
| 542 | // restore output compare state | ||
| 543 | if !original_enable_state { | ||
| 544 | self.channel(cc_channel).disable(); | ||
| 545 | } | ||
| 546 | |||
| 547 | self.channel(cc_channel).set_duty_cycle(original_duty_state); | ||
| 548 | |||
| 549 | // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, | ||
| 550 | // this can almost always trigger a DMA FIFO error. | ||
| 551 | // | ||
| 552 | // optional TODO: | ||
| 553 | // clean FEIF after disable UDE | ||
| 554 | if !original_cc_dma_enabled { | ||
| 555 | self.inner.set_cc_dma_enable_state(cc_channel, false); | ||
| 556 | } | ||
| 557 | |||
| 558 | if !original_cc_dma_on_update { | ||
| 559 | self.inner.set_cc_dma_selection(Ccds::ON_COMPARE) | ||
| 560 | } | ||
| 561 | } | 395 | } |
| 562 | } | 396 | } |
| 563 | 397 | ||
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 69c3a740f..26d2b8991 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs | |||
| @@ -87,7 +87,7 @@ unsafe fn on_interrupt(r: Regs, state: &'static State) { | |||
| 87 | // With `usart_v4` hardware FIFO is enabled and Transmission complete (TC) | 87 | // With `usart_v4` hardware FIFO is enabled and Transmission complete (TC) |
| 88 | // indicates that all bytes are pushed out from the FIFO. | 88 | // indicates that all bytes are pushed out from the FIFO. |
| 89 | // For other usart variants it shows that last byte from the buffer was just sent. | 89 | // For other usart variants it shows that last byte from the buffer was just sent. |
| 90 | if sr_val.tc() { | 90 | if sr_val.tc() && r.cr1().read().tcie() { |
| 91 | // For others it is cleared above with `clear_interrupt_flags`. | 91 | // For others it is cleared above with `clear_interrupt_flags`. |
| 92 | #[cfg(any(usart_v1, usart_v2))] | 92 | #[cfg(any(usart_v1, usart_v2))] |
| 93 | sr(r).modify(|w| w.set_tc(false)); | 93 | sr(r).modify(|w| w.set_tc(false)); |
diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs index a80a2692b..466e1a9b4 100644 --- a/embassy-stm32/src/xspi/mod.rs +++ b/embassy-stm32/src/xspi/mod.rs | |||
| @@ -420,9 +420,9 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { | |||
| 420 | return Err(XspiError::InvalidCommand); | 420 | return Err(XspiError::InvalidCommand); |
| 421 | } | 421 | } |
| 422 | 422 | ||
| 423 | T::REGS.cr().modify(|w| { | 423 | T::REGS |
| 424 | w.set_fmode(0.into()); | 424 | .cr() |
| 425 | }); | 425 | .modify(|w| w.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); |
| 426 | 426 | ||
| 427 | // Configure alternate bytes | 427 | // Configure alternate bytes |
| 428 | if let Some(ab) = command.alternate_bytes { | 428 | if let Some(ab) = command.alternate_bytes { |
| @@ -538,8 +538,8 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { | |||
| 538 | w.set_dmaen(false); | 538 | w.set_dmaen(false); |
| 539 | }); | 539 | }); |
| 540 | 540 | ||
| 541 | // self.configure_command(&transaction, Some(buf.len()))?; | 541 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 542 | self.configure_command(&transaction, Some(buf.len())).unwrap(); | 542 | self.configure_command(&transaction, Some(transfer_size_bytes))?; |
| 543 | 543 | ||
| 544 | let current_address = T::REGS.ar().read().address(); | 544 | let current_address = T::REGS.ar().read().address(); |
| 545 | let current_instruction = T::REGS.ir().read().instruction(); | 545 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -578,7 +578,8 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { | |||
| 578 | w.set_dmaen(false); | 578 | w.set_dmaen(false); |
| 579 | }); | 579 | }); |
| 580 | 580 | ||
| 581 | self.configure_command(&transaction, Some(buf.len()))?; | 581 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 582 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 582 | 583 | ||
| 583 | T::REGS | 584 | T::REGS |
| 584 | .cr() | 585 | .cr() |
| @@ -1145,7 +1146,8 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { | |||
| 1145 | // Wait for peripheral to be free | 1146 | // Wait for peripheral to be free |
| 1146 | while T::REGS.sr().read().busy() {} | 1147 | while T::REGS.sr().read().busy() {} |
| 1147 | 1148 | ||
| 1148 | self.configure_command(&transaction, Some(buf.len()))?; | 1149 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1150 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1149 | 1151 | ||
| 1150 | let current_address = T::REGS.ar().read().address(); | 1152 | let current_address = T::REGS.ar().read().address(); |
| 1151 | let current_instruction = T::REGS.ir().read().instruction(); | 1153 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -1160,16 +1162,18 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { | |||
| 1160 | T::REGS.ar().write(|v| v.set_address(current_address)); | 1162 | T::REGS.ar().write(|v| v.set_address(current_address)); |
| 1161 | } | 1163 | } |
| 1162 | 1164 | ||
| 1163 | let transfer = unsafe { | 1165 | for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { |
| 1164 | self.dma | 1166 | let transfer = unsafe { |
| 1165 | .as_mut() | 1167 | self.dma |
| 1166 | .unwrap() | 1168 | .as_mut() |
| 1167 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | 1169 | .unwrap() |
| 1168 | }; | 1170 | .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) |
| 1171 | }; | ||
| 1169 | 1172 | ||
| 1170 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1173 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1171 | 1174 | ||
| 1172 | transfer.blocking_wait(); | 1175 | transfer.blocking_wait(); |
| 1176 | } | ||
| 1173 | 1177 | ||
| 1174 | finish_dma(T::REGS); | 1178 | finish_dma(T::REGS); |
| 1175 | 1179 | ||
| @@ -1185,21 +1189,24 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { | |||
| 1185 | // Wait for peripheral to be free | 1189 | // Wait for peripheral to be free |
| 1186 | while T::REGS.sr().read().busy() {} | 1190 | while T::REGS.sr().read().busy() {} |
| 1187 | 1191 | ||
| 1188 | self.configure_command(&transaction, Some(buf.len()))?; | 1192 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1193 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1189 | T::REGS | 1194 | T::REGS |
| 1190 | .cr() | 1195 | .cr() |
| 1191 | .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); | 1196 | .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); |
| 1192 | 1197 | ||
| 1193 | let transfer = unsafe { | 1198 | for chunk in buf.chunks(0xFFFF / W::size().bytes()) { |
| 1194 | self.dma | 1199 | let transfer = unsafe { |
| 1195 | .as_mut() | 1200 | self.dma |
| 1196 | .unwrap() | 1201 | .as_mut() |
| 1197 | .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) | 1202 | .unwrap() |
| 1198 | }; | 1203 | .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) |
| 1204 | }; | ||
| 1199 | 1205 | ||
| 1200 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1206 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1201 | 1207 | ||
| 1202 | transfer.blocking_wait(); | 1208 | transfer.blocking_wait(); |
| 1209 | } | ||
| 1203 | 1210 | ||
| 1204 | finish_dma(T::REGS); | 1211 | finish_dma(T::REGS); |
| 1205 | 1212 | ||
| @@ -1215,7 +1222,8 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { | |||
| 1215 | // Wait for peripheral to be free | 1222 | // Wait for peripheral to be free |
| 1216 | while T::REGS.sr().read().busy() {} | 1223 | while T::REGS.sr().read().busy() {} |
| 1217 | 1224 | ||
| 1218 | self.configure_command(&transaction, Some(buf.len()))?; | 1225 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1226 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1219 | 1227 | ||
| 1220 | let current_address = T::REGS.ar().read().address(); | 1228 | let current_address = T::REGS.ar().read().address(); |
| 1221 | let current_instruction = T::REGS.ir().read().instruction(); | 1229 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -1230,16 +1238,18 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { | |||
| 1230 | T::REGS.ar().write(|v| v.set_address(current_address)); | 1238 | T::REGS.ar().write(|v| v.set_address(current_address)); |
| 1231 | } | 1239 | } |
| 1232 | 1240 | ||
| 1233 | let transfer = unsafe { | 1241 | for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { |
| 1234 | self.dma | 1242 | let transfer = unsafe { |
| 1235 | .as_mut() | 1243 | self.dma |
| 1236 | .unwrap() | 1244 | .as_mut() |
| 1237 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | 1245 | .unwrap() |
| 1238 | }; | 1246 | .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) |
| 1247 | }; | ||
| 1239 | 1248 | ||
| 1240 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1249 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1241 | 1250 | ||
| 1242 | transfer.await; | 1251 | transfer.await; |
| 1252 | } | ||
| 1243 | 1253 | ||
| 1244 | finish_dma(T::REGS); | 1254 | finish_dma(T::REGS); |
| 1245 | 1255 | ||
| @@ -1255,21 +1265,25 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { | |||
| 1255 | // Wait for peripheral to be free | 1265 | // Wait for peripheral to be free |
| 1256 | while T::REGS.sr().read().busy() {} | 1266 | while T::REGS.sr().read().busy() {} |
| 1257 | 1267 | ||
| 1258 | self.configure_command(&transaction, Some(buf.len()))?; | 1268 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1269 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1259 | T::REGS | 1270 | T::REGS |
| 1260 | .cr() | 1271 | .cr() |
| 1261 | .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); | 1272 | .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); |
| 1262 | 1273 | ||
| 1263 | let transfer = unsafe { | 1274 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. |
| 1264 | self.dma | 1275 | for chunk in buf.chunks(0xFFFF / W::size().bytes()) { |
| 1265 | .as_mut() | 1276 | let transfer = unsafe { |
| 1266 | .unwrap() | 1277 | self.dma |
| 1267 | .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) | 1278 | .as_mut() |
| 1268 | }; | 1279 | .unwrap() |
| 1280 | .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) | ||
| 1281 | }; | ||
| 1269 | 1282 | ||
| 1270 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1283 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1271 | 1284 | ||
| 1272 | transfer.await; | 1285 | transfer.await; |
| 1286 | } | ||
| 1273 | 1287 | ||
| 1274 | finish_dma(T::REGS); | 1288 | finish_dma(T::REGS); |
| 1275 | 1289 | ||
diff --git a/embassy-time-driver/CHANGELOG.md b/embassy-time-driver/CHANGELOG.md index cdd432437..4951f8c3e 100644 --- a/embassy-time-driver/CHANGELOG.md +++ b/embassy-time-driver/CHANGELOG.md | |||
| @@ -11,7 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 11 | ## 0.2.1 - 2025-08-26 | 11 | ## 0.2.1 - 2025-08-26 |
| 12 | 12 | ||
| 13 | - Allow inlining on time driver boundary | 13 | - Allow inlining on time driver boundary |
| 14 | - add 133MHz tick rate to support PR2040 @ 133MHz when `TIMERx`'s `SOURCE` is set to `SYSCLK` | 14 | - Add 133MHz tick rate to support PR2040 @ 133MHz when `TIMERx`'s `SOURCE` is set to `SYSCLK` |
| 15 | - Add 375KHz tick rate support | ||
| 15 | 16 | ||
| 16 | ## 0.2.0 - 2025-01-02 | 17 | ## 0.2.0 - 2025-01-02 |
| 17 | 18 | ||
diff --git a/embassy-time-driver/Cargo.toml b/embassy-time-driver/Cargo.toml index a52e82433..cbb6168b9 100644 --- a/embassy-time-driver/Cargo.toml +++ b/embassy-time-driver/Cargo.toml | |||
| @@ -118,6 +118,8 @@ tick-hz-256_000 = [] | |||
| 118 | tick-hz-262_144 = [] | 118 | tick-hz-262_144 = [] |
| 119 | ## 320.0kHz Tick Rate | 119 | ## 320.0kHz Tick Rate |
| 120 | tick-hz-320_000 = [] | 120 | tick-hz-320_000 = [] |
| 121 | ## 375.0kHz Tick Rate | ||
| 122 | tick-hz-375_000 = [] | ||
| 121 | ## 512.0kHz Tick Rate | 123 | ## 512.0kHz Tick Rate |
| 122 | tick-hz-512_000 = [] | 124 | tick-hz-512_000 = [] |
| 123 | ## 524.288kHz Tick Rate | 125 | ## 524.288kHz Tick Rate |
diff --git a/embassy-time-driver/gen_tick.py b/embassy-time-driver/gen_tick.py index 080434457..3cb6552df 100644 --- a/embassy-time-driver/gen_tick.py +++ b/embassy-time-driver/gen_tick.py | |||
| @@ -1,5 +1,4 @@ | |||
| 1 | import os | 1 | import os |
| 2 | from glob import glob | ||
| 3 | 2 | ||
| 4 | abspath = os.path.abspath(__file__) | 3 | abspath = os.path.abspath(__file__) |
| 5 | dname = os.path.dirname(abspath) | 4 | dname = os.path.dirname(abspath) |
| @@ -22,6 +21,8 @@ for i in range(1, 30): | |||
| 22 | ticks.append(10 * i * 1_000_000) | 21 | ticks.append(10 * i * 1_000_000) |
| 23 | for i in range(15, 50): | 22 | for i in range(15, 50): |
| 24 | ticks.append(20 * i * 1_000_000) | 23 | ticks.append(20 * i * 1_000_000) |
| 24 | |||
| 25 | ticks.append(375 * 1000) | ||
| 25 | ticks.append(133 * 1_000_000) | 26 | ticks.append(133 * 1_000_000) |
| 26 | 27 | ||
| 27 | seen = set() | 28 | seen = set() |
diff --git a/embassy-time-driver/src/tick.rs b/embassy-time-driver/src/tick.rs index 5059e1628..247ec9ab3 100644 --- a/embassy-time-driver/src/tick.rs +++ b/embassy-time-driver/src/tick.rs | |||
| @@ -74,6 +74,8 @@ pub const TICK_HZ: u64 = 256_000; | |||
| 74 | pub const TICK_HZ: u64 = 262_144; | 74 | pub const TICK_HZ: u64 = 262_144; |
| 75 | #[cfg(feature = "tick-hz-320_000")] | 75 | #[cfg(feature = "tick-hz-320_000")] |
| 76 | pub const TICK_HZ: u64 = 320_000; | 76 | pub const TICK_HZ: u64 = 320_000; |
| 77 | #[cfg(feature = "tick-hz-375_000")] | ||
| 78 | pub const TICK_HZ: u64 = 375_000; | ||
| 77 | #[cfg(feature = "tick-hz-512_000")] | 79 | #[cfg(feature = "tick-hz-512_000")] |
| 78 | pub const TICK_HZ: u64 = 512_000; | 80 | pub const TICK_HZ: u64 = 512_000; |
| 79 | #[cfg(feature = "tick-hz-524_288")] | 81 | #[cfg(feature = "tick-hz-524_288")] |
| @@ -358,6 +360,7 @@ pub const TICK_HZ: u64 = 5_242_880_000; | |||
| 358 | feature = "tick-hz-256_000", | 360 | feature = "tick-hz-256_000", |
| 359 | feature = "tick-hz-262_144", | 361 | feature = "tick-hz-262_144", |
| 360 | feature = "tick-hz-320_000", | 362 | feature = "tick-hz-320_000", |
| 363 | feature = "tick-hz-375_000", | ||
| 361 | feature = "tick-hz-512_000", | 364 | feature = "tick-hz-512_000", |
| 362 | feature = "tick-hz-524_288", | 365 | feature = "tick-hz-524_288", |
| 363 | feature = "tick-hz-640_000", | 366 | feature = "tick-hz-640_000", |
diff --git a/embassy-time/CHANGELOG.md b/embassy-time/CHANGELOG.md index 4a50da8ef..17f8a3837 100644 --- a/embassy-time/CHANGELOG.md +++ b/embassy-time/CHANGELOG.md | |||
| @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 9 | ## Unreleased - ReleaseDate | 9 | ## Unreleased - ReleaseDate |
| 10 | 10 | ||
| 11 | - Add as_nanos and from_nanos where missing | 11 | - Add as_nanos and from_nanos where missing |
| 12 | - Added 375KHz tick rate support | ||
| 12 | 13 | ||
| 13 | ## 0.5.0 - 2025-08-26 | 14 | ## 0.5.0 - 2025-08-26 |
| 14 | 15 | ||
diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 05614dbf5..a7ed51e78 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml | |||
| @@ -178,6 +178,8 @@ tick-hz-256_000 = ["embassy-time-driver/tick-hz-256_000"] | |||
| 178 | tick-hz-262_144 = ["embassy-time-driver/tick-hz-262_144"] | 178 | tick-hz-262_144 = ["embassy-time-driver/tick-hz-262_144"] |
| 179 | ## 320.0kHz Tick Rate | 179 | ## 320.0kHz Tick Rate |
| 180 | tick-hz-320_000 = ["embassy-time-driver/tick-hz-320_000"] | 180 | tick-hz-320_000 = ["embassy-time-driver/tick-hz-320_000"] |
| 181 | ## 375.0kHz Tick Rate | ||
| 182 | tick-hz-375_000 = ["embassy-time-driver/tick-hz-375_000"] | ||
| 181 | ## 512.0kHz Tick Rate | 183 | ## 512.0kHz Tick Rate |
| 182 | tick-hz-512_000 = ["embassy-time-driver/tick-hz-512_000"] | 184 | tick-hz-512_000 = ["embassy-time-driver/tick-hz-512_000"] |
| 183 | ## 524.288kHz Tick Rate | 185 | ## 524.288kHz Tick Rate |
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/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index b608b2e01..da3cbf1e6 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs | |||
| @@ -6,12 +6,18 @@ use defmt_rtt::*; | |||
| 6 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; | 6 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; |
| 7 | use embassy_embedded_hal::adapter::BlockingAsync; | 7 | use embassy_embedded_hal::adapter::BlockingAsync; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_stm32::exti::ExtiInput; | 9 | use embassy_stm32::exti::{self, ExtiInput}; |
| 10 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; | 10 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; |
| 11 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | 11 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; |
| 12 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 12 | use embassy_sync::mutex::Mutex; | 13 | use embassy_sync::mutex::Mutex; |
| 13 | use panic_reset as _; | 14 | use panic_reset as _; |
| 14 | 15 | ||
| 16 | bind_interrupts!( | ||
| 17 | pub struct Irqs{ | ||
| 18 | EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>; | ||
| 19 | }); | ||
| 20 | |||
| 15 | #[cfg(feature = "skip-include")] | 21 | #[cfg(feature = "skip-include")] |
| 16 | static APP_B: &[u8] = &[0, 1, 2, 3]; | 22 | static APP_B: &[u8] = &[0, 1, 2, 3]; |
| 17 | #[cfg(not(feature = "skip-include"))] | 23 | #[cfg(not(feature = "skip-include"))] |
| @@ -23,7 +29,7 @@ async fn main(_spawner: Spawner) { | |||
| 23 | let flash = Flash::new_blocking(p.FLASH); | 29 | let flash = Flash::new_blocking(p.FLASH); |
| 24 | let flash = Mutex::new(BlockingAsync::new(flash)); | 30 | let flash = Mutex::new(BlockingAsync::new(flash)); |
| 25 | 31 | ||
| 26 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); | 32 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); |
| 27 | 33 | ||
| 28 | let mut led = Output::new(p.PA5, Level::Low, Speed::Low); | 34 | let mut led = Output::new(p.PA5, Level::Low, Speed::Low); |
| 29 | led.set_high(); | 35 | led.set_high(); |
diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index 172b4c235..62f1da269 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs | |||
| @@ -7,9 +7,10 @@ use core::cell::RefCell; | |||
| 7 | use defmt_rtt::*; | 7 | use defmt_rtt::*; |
| 8 | use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; | 8 | use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_stm32::exti::ExtiInput; | 10 | use embassy_stm32::exti::{self, ExtiInput}; |
| 11 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; | 11 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; |
| 12 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | 12 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; |
| 13 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 13 | use embassy_sync::blocking_mutex::Mutex; | 14 | use embassy_sync::blocking_mutex::Mutex; |
| 14 | use embedded_storage::nor_flash::NorFlash; | 15 | use embedded_storage::nor_flash::NorFlash; |
| 15 | use panic_reset as _; | 16 | use panic_reset as _; |
| @@ -19,13 +20,18 @@ static APP_B: &[u8] = &[0, 1, 2, 3]; | |||
| 19 | #[cfg(not(feature = "skip-include"))] | 20 | #[cfg(not(feature = "skip-include"))] |
| 20 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | 21 | static APP_B: &[u8] = include_bytes!("../../b.bin"); |
| 21 | 22 | ||
| 23 | bind_interrupts!( | ||
| 24 | pub struct Irqs{ | ||
| 25 | EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>; | ||
| 26 | }); | ||
| 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_stm32::init(Default::default()); | 30 | let p = embassy_stm32::init(Default::default()); |
| 25 | let flash = Flash::new_blocking(p.FLASH); | 31 | let flash = Flash::new_blocking(p.FLASH); |
| 26 | let flash = Mutex::new(RefCell::new(flash)); | 32 | let flash = Mutex::new(RefCell::new(flash)); |
| 27 | 33 | ||
| 28 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); | 34 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); |
| 29 | 35 | ||
| 30 | let mut led = Output::new(p.PB7, Level::Low, Speed::Low); | 36 | let mut led = Output::new(p.PB7, Level::Low, Speed::Low); |
| 31 | led.set_high(); | 37 | led.set_high(); |
diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index c1b1a267a..226971e02 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs | |||
| @@ -7,13 +7,19 @@ use core::cell::RefCell; | |||
| 7 | use defmt_rtt::*; | 7 | use defmt_rtt::*; |
| 8 | use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; | 8 | use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_stm32::exti::ExtiInput; | 10 | use embassy_stm32::exti::{self, ExtiInput}; |
| 11 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; | 11 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; |
| 12 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | 12 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; |
| 13 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 13 | use embassy_sync::blocking_mutex::Mutex; | 14 | use embassy_sync::blocking_mutex::Mutex; |
| 14 | use embedded_storage::nor_flash::NorFlash; | 15 | use embedded_storage::nor_flash::NorFlash; |
| 15 | use panic_reset as _; | 16 | use panic_reset as _; |
| 16 | 17 | ||
| 18 | bind_interrupts!( | ||
| 19 | pub struct Irqs{ | ||
| 20 | EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>; | ||
| 21 | }); | ||
| 22 | |||
| 17 | #[cfg(feature = "skip-include")] | 23 | #[cfg(feature = "skip-include")] |
| 18 | static APP_B: &[u8] = &[0, 1, 2, 3]; | 24 | static APP_B: &[u8] = &[0, 1, 2, 3]; |
| 19 | #[cfg(not(feature = "skip-include"))] | 25 | #[cfg(not(feature = "skip-include"))] |
| @@ -25,7 +31,7 @@ async fn main(_spawner: Spawner) { | |||
| 25 | let flash = Flash::new_blocking(p.FLASH); | 31 | let flash = Flash::new_blocking(p.FLASH); |
| 26 | let flash = Mutex::new(RefCell::new(flash)); | 32 | let flash = Mutex::new(RefCell::new(flash)); |
| 27 | 33 | ||
| 28 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); | 34 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); |
| 29 | 35 | ||
| 30 | let mut led = Output::new(p.PB14, Level::Low, Speed::Low); | 36 | let mut led = Output::new(p.PB14, Level::Low, Speed::Low); |
| 31 | led.set_high(); | 37 | led.set_high(); |
diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs index dcc10e5c6..0aa723eaa 100644 --- a/examples/boot/application/stm32l0/src/bin/a.rs +++ b/examples/boot/application/stm32l0/src/bin/a.rs | |||
| @@ -6,13 +6,19 @@ use defmt_rtt::*; | |||
| 6 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; | 6 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; |
| 7 | use embassy_embedded_hal::adapter::BlockingAsync; | 7 | use embassy_embedded_hal::adapter::BlockingAsync; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_stm32::exti::ExtiInput; | 9 | use embassy_stm32::exti::{self, ExtiInput}; |
| 10 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; | 10 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; |
| 11 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | 11 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; |
| 12 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 12 | use embassy_sync::mutex::Mutex; | 13 | use embassy_sync::mutex::Mutex; |
| 13 | use embassy_time::Timer; | 14 | use embassy_time::Timer; |
| 14 | use panic_reset as _; | 15 | use panic_reset as _; |
| 15 | 16 | ||
| 17 | bind_interrupts!( | ||
| 18 | pub struct Irqs{ | ||
| 19 | EXTI2_3 => exti::InterruptHandler<interrupt::typelevel::EXTI2_3>; | ||
| 20 | }); | ||
| 21 | |||
| 16 | #[cfg(feature = "skip-include")] | 22 | #[cfg(feature = "skip-include")] |
| 17 | static APP_B: &[u8] = &[0, 1, 2, 3]; | 23 | static APP_B: &[u8] = &[0, 1, 2, 3]; |
| 18 | #[cfg(not(feature = "skip-include"))] | 24 | #[cfg(not(feature = "skip-include"))] |
| @@ -24,7 +30,7 @@ async fn main(_spawner: Spawner) { | |||
| 24 | let flash = Flash::new_blocking(p.FLASH); | 30 | let flash = Flash::new_blocking(p.FLASH); |
| 25 | let flash = Mutex::new(BlockingAsync::new(flash)); | 31 | let flash = Mutex::new(BlockingAsync::new(flash)); |
| 26 | 32 | ||
| 27 | let mut button = ExtiInput::new(p.PB2, p.EXTI2, Pull::Up); | 33 | let mut button = ExtiInput::new(p.PB2, p.EXTI2, Pull::Up, Irqs); |
| 28 | 34 | ||
| 29 | let mut led = Output::new(p.PB5, Level::Low, Speed::Low); | 35 | let mut led = Output::new(p.PB5, Level::Low, Speed::Low); |
| 30 | 36 | ||
diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index dcc10e5c6..7ad7046fb 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs | |||
| @@ -6,9 +6,10 @@ use defmt_rtt::*; | |||
| 6 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; | 6 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; |
| 7 | use embassy_embedded_hal::adapter::BlockingAsync; | 7 | use embassy_embedded_hal::adapter::BlockingAsync; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_stm32::exti::ExtiInput; | 9 | use embassy_stm32::exti::{self, ExtiInput}; |
| 10 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; | 10 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; |
| 11 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | 11 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; |
| 12 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 12 | use embassy_sync::mutex::Mutex; | 13 | use embassy_sync::mutex::Mutex; |
| 13 | use embassy_time::Timer; | 14 | use embassy_time::Timer; |
| 14 | use panic_reset as _; | 15 | use panic_reset as _; |
| @@ -18,13 +19,18 @@ static APP_B: &[u8] = &[0, 1, 2, 3]; | |||
| 18 | #[cfg(not(feature = "skip-include"))] | 19 | #[cfg(not(feature = "skip-include"))] |
| 19 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | 20 | static APP_B: &[u8] = include_bytes!("../../b.bin"); |
| 20 | 21 | ||
| 22 | bind_interrupts!( | ||
| 23 | pub struct Irqs{ | ||
| 24 | EXTI2 => exti::InterruptHandler<interrupt::typelevel::EXTI2>; | ||
| 25 | }); | ||
| 26 | |||
| 21 | #[embassy_executor::main] | 27 | #[embassy_executor::main] |
| 22 | async fn main(_spawner: Spawner) { | 28 | async fn main(_spawner: Spawner) { |
| 23 | let p = embassy_stm32::init(Default::default()); | 29 | let p = embassy_stm32::init(Default::default()); |
| 24 | let flash = Flash::new_blocking(p.FLASH); | 30 | let flash = Flash::new_blocking(p.FLASH); |
| 25 | let flash = Mutex::new(BlockingAsync::new(flash)); | 31 | let flash = Mutex::new(BlockingAsync::new(flash)); |
| 26 | 32 | ||
| 27 | let mut button = ExtiInput::new(p.PB2, p.EXTI2, Pull::Up); | 33 | let mut button = ExtiInput::new(p.PB2, p.EXTI2, Pull::Up, Irqs); |
| 28 | 34 | ||
| 29 | let mut led = Output::new(p.PB5, Level::Low, Speed::Low); | 35 | let mut led = Output::new(p.PB5, Level::Low, Speed::Low); |
| 30 | 36 | ||
diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index 7f8015c04..4edd338c5 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs | |||
| @@ -6,9 +6,10 @@ use defmt_rtt::*; | |||
| 6 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; | 6 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; |
| 7 | use embassy_embedded_hal::adapter::BlockingAsync; | 7 | use embassy_embedded_hal::adapter::BlockingAsync; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_stm32::exti::ExtiInput; | 9 | use embassy_stm32::exti::{self, ExtiInput}; |
| 10 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; | 10 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; |
| 11 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | 11 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; |
| 12 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 12 | use embassy_sync::mutex::Mutex; | 13 | use embassy_sync::mutex::Mutex; |
| 13 | use panic_reset as _; | 14 | use panic_reset as _; |
| 14 | 15 | ||
| @@ -17,13 +18,18 @@ static APP_B: &[u8] = &[0, 1, 2, 3]; | |||
| 17 | #[cfg(not(feature = "skip-include"))] | 18 | #[cfg(not(feature = "skip-include"))] |
| 18 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | 19 | static APP_B: &[u8] = include_bytes!("../../b.bin"); |
| 19 | 20 | ||
| 21 | bind_interrupts!( | ||
| 22 | pub struct Irqs{ | ||
| 23 | EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>; | ||
| 24 | }); | ||
| 25 | |||
| 20 | #[embassy_executor::main] | 26 | #[embassy_executor::main] |
| 21 | async fn main(_spawner: Spawner) { | 27 | async fn main(_spawner: Spawner) { |
| 22 | let p = embassy_stm32::init(Default::default()); | 28 | let p = embassy_stm32::init(Default::default()); |
| 23 | let flash = Flash::new_blocking(p.FLASH); | 29 | let flash = Flash::new_blocking(p.FLASH); |
| 24 | let flash = Mutex::new(BlockingAsync::new(flash)); | 30 | let flash = Mutex::new(BlockingAsync::new(flash)); |
| 25 | 31 | ||
| 26 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); | 32 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); |
| 27 | 33 | ||
| 28 | let mut led = Output::new(p.PB14, Level::Low, Speed::Low); | 34 | let mut led = Output::new(p.PB14, Level::Low, Speed::Low); |
| 29 | led.set_high(); | 35 | led.set_high(); |
diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index 3f381fd80..58063eb50 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs | |||
| @@ -8,10 +8,10 @@ use defmt_rtt::*; | |||
| 8 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; | 8 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; |
| 9 | use embassy_embedded_hal::adapter::BlockingAsync; | 9 | use embassy_embedded_hal::adapter::BlockingAsync; |
| 10 | use embassy_executor::Spawner; | 10 | use embassy_executor::Spawner; |
| 11 | use embassy_stm32::SharedData; | 11 | use embassy_stm32::exti::{self, ExtiInput}; |
| 12 | use embassy_stm32::exti::ExtiInput; | ||
| 13 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; | 12 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; |
| 14 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | 13 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; |
| 14 | use embassy_stm32::{SharedData, bind_interrupts, interrupt}; | ||
| 15 | use embassy_sync::mutex::Mutex; | 15 | use embassy_sync::mutex::Mutex; |
| 16 | use panic_reset as _; | 16 | use panic_reset as _; |
| 17 | 17 | ||
| @@ -20,6 +20,11 @@ static APP_B: &[u8] = &[0, 1, 2, 3]; | |||
| 20 | #[cfg(not(feature = "skip-include"))] | 20 | #[cfg(not(feature = "skip-include"))] |
| 21 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | 21 | static APP_B: &[u8] = include_bytes!("../../b.bin"); |
| 22 | 22 | ||
| 23 | bind_interrupts!( | ||
| 24 | pub struct Irqs{ | ||
| 25 | EXTI0 => exti::InterruptHandler<interrupt::typelevel::EXTI0>; | ||
| 26 | }); | ||
| 27 | |||
| 23 | #[unsafe(link_section = ".shared_data")] | 28 | #[unsafe(link_section = ".shared_data")] |
| 24 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); | 29 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); |
| 25 | 30 | ||
| @@ -29,7 +34,7 @@ async fn main(_spawner: Spawner) { | |||
| 29 | let flash = Flash::new_blocking(p.FLASH); | 34 | let flash = Flash::new_blocking(p.FLASH); |
| 30 | let flash = Mutex::new(BlockingAsync::new(flash)); | 35 | let flash = Mutex::new(BlockingAsync::new(flash)); |
| 31 | 36 | ||
| 32 | let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Up); | 37 | let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Up, Irqs); |
| 33 | 38 | ||
| 34 | let mut led = Output::new(p.PB9, Level::Low, Speed::Low); | 39 | let mut led = Output::new(p.PB9, Level::Low, Speed::Low); |
| 35 | led.set_high(); | 40 | led.set_high(); |
diff --git a/examples/lpc55s69/src/bin/pwm.rs b/examples/lpc55s69/src/bin/pwm.rs index 93b898b9d..8a9894b94 100644 --- a/examples/lpc55s69/src/bin/pwm.rs +++ b/examples/lpc55s69/src/bin/pwm.rs | |||
| @@ -10,7 +10,7 @@ use {defmt_rtt as _, panic_halt as _}; | |||
| 10 | #[embassy_executor::main] | 10 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 11 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_nxp::init(Default::default()); | 12 | let p = embassy_nxp::init(Default::default()); |
| 13 | let pwm = Pwm::new_output(p.PWM_OUTPUT1, p.PIO0_18, Config::new(1_000_000_000, 2_000_000_000)); | 13 | let pwm = Pwm::new_output(p.SCT0_OUT1, p.PIO0_18, Config::new(1_000_000_000, 2_000_000_000)); |
| 14 | loop { | 14 | loop { |
| 15 | info!("Counter: {}", pwm.counter()); | 15 | info!("Counter: {}", pwm.counter()); |
| 16 | Timer::after_millis(50).await; | 16 | Timer::after_millis(50).await; |
diff --git a/examples/lpc55s69/src/bin/usart_async.rs b/examples/lpc55s69/src/bin/usart_async.rs index b06abd477..a9815b920 100644 --- a/examples/lpc55s69/src/bin/usart_async.rs +++ b/examples/lpc55s69/src/bin/usart_async.rs | |||
| @@ -38,8 +38,8 @@ async fn main(spawner: Spawner) { | |||
| 38 | p.PIO0_27, | 38 | p.PIO0_27, |
| 39 | p.PIO1_24, | 39 | p.PIO1_24, |
| 40 | Irqs, | 40 | Irqs, |
| 41 | p.DMA_CH11, | 41 | p.DMA0_CH11, |
| 42 | p.DMA_CH10, | 42 | p.DMA0_CH10, |
| 43 | Config::default(), | 43 | Config::default(), |
| 44 | ); | 44 | ); |
| 45 | let led = Output::new(p.PIO1_6, Level::Low); | 45 | let led = Output::new(p.PIO1_6, Level::Low); |
diff --git a/examples/mspm0g3507/src/bin/mathacl_ops.rs b/examples/mspm0g3507/src/bin/mathacl_ops.rs new file mode 100644 index 000000000..25d74b29b --- /dev/null +++ b/examples/mspm0g3507/src/bin/mathacl_ops.rs | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | //! Example of using mathematical calculations performed by the MSPM0G3507 chip. | ||
| 2 | //! | ||
| 3 | //! It prints the result of basics trigonometric calculation. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | |||
| 8 | use core::f32::consts::PI; | ||
| 9 | |||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_mspm0::mathacl::{Mathacl, Precision}; | ||
| 13 | use embassy_time::Timer; | ||
| 14 | use {defmt_rtt as _, panic_halt as _}; | ||
| 15 | |||
| 16 | #[embassy_executor::main] | ||
| 17 | async fn main(_spawner: Spawner) -> ! { | ||
| 18 | info!("Hello world!"); | ||
| 19 | |||
| 20 | let d = embassy_mspm0::init(Default::default()); | ||
| 21 | |||
| 22 | let mut macl = Mathacl::new(d.MATHACL); | ||
| 23 | |||
| 24 | // value radians [-PI; PI] | ||
| 25 | let rads = PI * 0.5; | ||
| 26 | match macl.sin(rads, Precision::High) { | ||
| 27 | Ok(res) => info!("sin({}) = {}", rads, res), | ||
| 28 | Err(e) => error!("sin Error: {:?}", e), | ||
| 29 | } | ||
| 30 | |||
| 31 | match macl.cos(rads, Precision::Medium) { | ||
| 32 | Ok(res) => info!("cos({}) = {}", rads, res), | ||
| 33 | Err(e) => error!("cos Error: {:?}", e), | ||
| 34 | } | ||
| 35 | |||
| 36 | loop { | ||
| 37 | Timer::after_millis(500).await; | ||
| 38 | } | ||
| 39 | } | ||
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/rp/src/bin/wifi_webrequest.rs b/examples/rp/src/bin/wifi_webrequest.rs index b618d2b38..ce85f4b9a 100644 --- a/examples/rp/src/bin/wifi_webrequest.rs +++ b/examples/rp/src/bin/wifi_webrequest.rs | |||
| @@ -1,9 +1,8 @@ | |||
| 1 | //! This example uses the RP Pico W board Wifi chip (cyw43). | 1 | //! This example uses the RP Pico W board Wifi chip (cyw43). |
| 2 | //! Connects to Wifi network and makes a web request to get the current time. | 2 | //! Connects to Wifi network and makes a web request to httpbin.org. |
| 3 | 3 | ||
| 4 | #![no_std] | 4 | #![no_std] |
| 5 | #![no_main] | 5 | #![no_main] |
| 6 | #![allow(async_fn_in_trait)] | ||
| 7 | 6 | ||
| 8 | use core::str::from_utf8; | 7 | use core::str::from_utf8; |
| 9 | 8 | ||
| @@ -20,11 +19,14 @@ use embassy_rp::gpio::{Level, Output}; | |||
| 20 | use embassy_rp::peripherals::{DMA_CH0, PIO0}; | 19 | use embassy_rp::peripherals::{DMA_CH0, PIO0}; |
| 21 | use embassy_rp::pio::{InterruptHandler, Pio}; | 20 | use embassy_rp::pio::{InterruptHandler, Pio}; |
| 22 | use embassy_time::{Duration, Timer}; | 21 | use embassy_time::{Duration, Timer}; |
| 23 | use reqwless::client::{HttpClient, TlsConfig, TlsVerify}; | 22 | use reqwless::client::HttpClient; |
| 23 | // Uncomment these for TLS requests: | ||
| 24 | // use reqwless::client::{HttpClient, TlsConfig, TlsVerify}; | ||
| 24 | use reqwless::request::Method; | 25 | use reqwless::request::Method; |
| 25 | use serde::Deserialize; | 26 | use serde::Deserialize; |
| 27 | use serde_json_core::from_slice; | ||
| 26 | use static_cell::StaticCell; | 28 | use static_cell::StaticCell; |
| 27 | use {defmt_rtt as _, panic_probe as _, serde_json_core}; | 29 | use {defmt_rtt as _, panic_probe as _}; |
| 28 | 30 | ||
| 29 | bind_interrupts!(struct Irqs { | 31 | bind_interrupts!(struct Irqs { |
| 30 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 32 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| @@ -119,64 +121,90 @@ async fn main(spawner: Spawner) { | |||
| 119 | // And now we can use it! | 121 | // And now we can use it! |
| 120 | 122 | ||
| 121 | loop { | 123 | loop { |
| 122 | let mut rx_buffer = [0; 8192]; | 124 | let mut rx_buffer = [0; 4096]; |
| 123 | let mut tls_read_buffer = [0; 16640]; | 125 | // Uncomment these for TLS requests: |
| 124 | let mut tls_write_buffer = [0; 16640]; | 126 | // let mut tls_read_buffer = [0; 16640]; |
| 127 | // let mut tls_write_buffer = [0; 16640]; | ||
| 125 | 128 | ||
| 126 | let client_state = TcpClientState::<1, 1024, 1024>::new(); | 129 | let client_state = TcpClientState::<1, 4096, 4096>::new(); |
| 127 | let tcp_client = TcpClient::new(stack, &client_state); | 130 | let tcp_client = TcpClient::new(stack, &client_state); |
| 128 | let dns_client = DnsSocket::new(stack); | 131 | let dns_client = DnsSocket::new(stack); |
| 129 | let tls_config = TlsConfig::new(seed, &mut tls_read_buffer, &mut tls_write_buffer, TlsVerify::None); | 132 | // Uncomment these for TLS requests: |
| 133 | // let tls_config = TlsConfig::new(seed, &mut tls_read_buffer, &mut tls_write_buffer, TlsVerify::None); | ||
| 130 | 134 | ||
| 131 | let mut http_client = HttpClient::new_with_tls(&tcp_client, &dns_client, tls_config); | 135 | // Using non-TLS HTTP for this example |
| 132 | let url = "https://worldtimeapi.org/api/timezone/Europe/Berlin"; | 136 | let mut http_client = HttpClient::new(&tcp_client, &dns_client); |
| 133 | // for non-TLS requests, use this instead: | 137 | let url = "http://httpbin.org/json"; |
| 134 | // let mut http_client = HttpClient::new(&tcp_client, &dns_client); | 138 | // For TLS requests, use this instead: |
| 135 | // let url = "http://worldtimeapi.org/api/timezone/Europe/Berlin"; | 139 | // let mut http_client = HttpClient::new_with_tls(&tcp_client, &dns_client, tls_config); |
| 140 | // let url = "https://httpbin.org/json"; | ||
| 136 | 141 | ||
| 137 | info!("connecting to {}", &url); | 142 | info!("connecting to {}", &url); |
| 138 | 143 | ||
| 139 | let mut request = match http_client.request(Method::GET, &url).await { | 144 | let mut request = match http_client.request(Method::GET, url).await { |
| 140 | Ok(req) => req, | 145 | Ok(req) => req, |
| 141 | Err(e) => { | 146 | Err(e) => { |
| 142 | error!("Failed to make HTTP request: {:?}", e); | 147 | error!("Failed to make HTTP request: {:?}", e); |
| 143 | return; // handle the error | 148 | Timer::after(Duration::from_secs(5)).await; |
| 149 | continue; | ||
| 144 | } | 150 | } |
| 145 | }; | 151 | }; |
| 146 | 152 | ||
| 147 | let response = match request.send(&mut rx_buffer).await { | 153 | let response = match request.send(&mut rx_buffer).await { |
| 148 | Ok(resp) => resp, | 154 | Ok(resp) => resp, |
| 149 | Err(_e) => { | 155 | Err(e) => { |
| 150 | error!("Failed to send HTTP request"); | 156 | error!("Failed to send HTTP request: {:?}", e); |
| 151 | return; // handle the error; | 157 | Timer::after(Duration::from_secs(5)).await; |
| 158 | continue; | ||
| 152 | } | 159 | } |
| 153 | }; | 160 | }; |
| 154 | 161 | ||
| 155 | let body = match from_utf8(response.body().read_to_end().await.unwrap()) { | 162 | info!("Response status: {}", response.status.0); |
| 163 | |||
| 164 | let body_bytes = match response.body().read_to_end().await { | ||
| 156 | Ok(b) => b, | 165 | Ok(b) => b, |
| 157 | Err(_e) => { | 166 | Err(_e) => { |
| 158 | error!("Failed to read response body"); | 167 | error!("Failed to read response body"); |
| 159 | return; // handle the error | 168 | Timer::after(Duration::from_secs(5)).await; |
| 169 | continue; | ||
| 170 | } | ||
| 171 | }; | ||
| 172 | |||
| 173 | let body = match from_utf8(body_bytes) { | ||
| 174 | Ok(b) => b, | ||
| 175 | Err(_e) => { | ||
| 176 | error!("Failed to parse response body as UTF-8"); | ||
| 177 | Timer::after(Duration::from_secs(5)).await; | ||
| 178 | continue; | ||
| 160 | } | 179 | } |
| 161 | }; | 180 | }; |
| 162 | info!("Response body: {:?}", &body); | 181 | info!("Response body length: {} bytes", body.len()); |
| 163 | 182 | ||
| 164 | // parse the response body and update the RTC | 183 | // Parse the JSON response from httpbin.org/json |
| 184 | #[derive(Deserialize)] | ||
| 185 | struct SlideShow<'a> { | ||
| 186 | author: &'a str, | ||
| 187 | title: &'a str, | ||
| 188 | } | ||
| 165 | 189 | ||
| 166 | #[derive(Deserialize)] | 190 | #[derive(Deserialize)] |
| 167 | struct ApiResponse<'a> { | 191 | struct HttpBinResponse<'a> { |
| 168 | datetime: &'a str, | 192 | #[serde(borrow)] |
| 169 | // other fields as needed | 193 | slideshow: SlideShow<'a>, |
| 170 | } | 194 | } |
| 171 | 195 | ||
| 172 | let bytes = body.as_bytes(); | 196 | let bytes = body.as_bytes(); |
| 173 | match serde_json_core::de::from_slice::<ApiResponse>(bytes) { | 197 | match from_slice::<HttpBinResponse>(bytes) { |
| 174 | Ok((output, _used)) => { | 198 | Ok((output, _used)) => { |
| 175 | info!("Datetime: {:?}", output.datetime); | 199 | info!("Successfully parsed JSON response!"); |
| 200 | info!("Slideshow title: {:?}", output.slideshow.title); | ||
| 201 | info!("Slideshow author: {:?}", output.slideshow.author); | ||
| 176 | } | 202 | } |
| 177 | Err(_e) => { | 203 | Err(e) => { |
| 178 | error!("Failed to parse response body"); | 204 | error!("Failed to parse JSON response: {}", Debug2Format(&e)); |
| 179 | return; // handle the error | 205 | // Log preview of response for debugging |
| 206 | let preview = if body.len() > 200 { &body[..200] } else { body }; | ||
| 207 | info!("Response preview: {:?}", preview); | ||
| 180 | } | 208 | } |
| 181 | } | 209 | } |
| 182 | 210 | ||
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 b52c9e7f8..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; |
| @@ -35,8 +34,12 @@ async fn main(_spawner: Spawner) { | |||
| 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/stm32c0/src/bin/button_exti.rs b/examples/stm32c0/src/bin/button_exti.rs index 34a08bbc6..9d54479da 100644 --- a/examples/stm32c0/src/bin/button_exti.rs +++ b/examples/stm32c0/src/bin/button_exti.rs | |||
| @@ -3,16 +3,22 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::exti::ExtiInput; | 6 | use embassy_stm32::exti::{self, ExtiInput}; |
| 7 | use embassy_stm32::gpio::Pull; | 7 | use embassy_stm32::gpio::Pull; |
| 8 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 11 | bind_interrupts!( | ||
| 12 | pub struct Irqs{ | ||
| 13 | EXTI4_15 => exti::InterruptHandler<interrupt::typelevel::EXTI4_15>; | ||
| 14 | }); | ||
| 15 | |||
| 10 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_stm32::init(Default::default()); | 18 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | 19 | info!("Hello World!"); |
| 14 | 20 | ||
| 15 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); | 21 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); |
| 16 | 22 | ||
| 17 | info!("Press the USER button..."); | 23 | info!("Press the USER button..."); |
| 18 | 24 | ||
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/button_controlled_blink.rs b/examples/stm32f0/src/bin/button_controlled_blink.rs index 0b678af01..9c7bf8a95 100644 --- a/examples/stm32f0/src/bin/button_controlled_blink.rs +++ b/examples/stm32f0/src/bin/button_controlled_blink.rs | |||
| @@ -7,14 +7,19 @@ use core::sync::atomic::{AtomicU32, Ordering}; | |||
| 7 | 7 | ||
| 8 | use defmt::info; | 8 | use defmt::info; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_stm32::Peri; | 10 | use embassy_stm32::exti::{self, ExtiInput}; |
| 11 | use embassy_stm32::exti::ExtiInput; | ||
| 12 | use embassy_stm32::gpio::{AnyPin, Level, Output, Pull, Speed}; | 11 | use embassy_stm32::gpio::{AnyPin, Level, Output, Pull, Speed}; |
| 12 | use embassy_stm32::{Peri, bind_interrupts, interrupt}; | ||
| 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 | ||
| 16 | static BLINK_MS: AtomicU32 = AtomicU32::new(0); | 16 | static BLINK_MS: AtomicU32 = AtomicU32::new(0); |
| 17 | 17 | ||
| 18 | bind_interrupts!( | ||
| 19 | pub struct Irqs{ | ||
| 20 | EXTI4_15 => exti::InterruptHandler<interrupt::typelevel::EXTI4_15>; | ||
| 21 | }); | ||
| 22 | |||
| 18 | #[embassy_executor::task] | 23 | #[embassy_executor::task] |
| 19 | async fn led_task(led: Peri<'static, AnyPin>) { | 24 | async fn led_task(led: Peri<'static, AnyPin>) { |
| 20 | // Configure the LED pin as a push pull output and obtain handler. | 25 | // Configure the LED pin as a push pull output and obtain handler. |
| @@ -37,7 +42,7 @@ async fn main(spawner: Spawner) { | |||
| 37 | 42 | ||
| 38 | // Configure the button pin and obtain handler. | 43 | // Configure the button pin and obtain handler. |
| 39 | // On the Nucleo F091RC there is a button connected to pin PC13. | 44 | // On the Nucleo F091RC there is a button connected to pin PC13. |
| 40 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::None); | 45 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::None, Irqs); |
| 41 | 46 | ||
| 42 | // Create and initialize a delay variable to manage delay loop | 47 | // Create and initialize a delay variable to manage delay loop |
| 43 | let mut del_var = 2000; | 48 | let mut del_var = 2000; |
diff --git a/examples/stm32f0/src/bin/button_exti.rs b/examples/stm32f0/src/bin/button_exti.rs index fd615a215..d1312e1be 100644 --- a/examples/stm32f0/src/bin/button_exti.rs +++ b/examples/stm32f0/src/bin/button_exti.rs | |||
| @@ -3,17 +3,23 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::exti::ExtiInput; | 6 | use embassy_stm32::exti::{self, ExtiInput}; |
| 7 | use embassy_stm32::gpio::Pull; | 7 | use embassy_stm32::gpio::Pull; |
| 8 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 11 | bind_interrupts!( | ||
| 12 | pub struct Irqs{ | ||
| 13 | EXTI4_15 => exti::InterruptHandler<interrupt::typelevel::EXTI4_15>; | ||
| 14 | }); | ||
| 15 | |||
| 10 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 12 | // Initialize and create handle for devicer peripherals | 18 | // Initialize and create handle for devicer peripherals |
| 13 | let p = embassy_stm32::init(Default::default()); | 19 | let p = embassy_stm32::init(Default::default()); |
| 14 | // Configure the button pin and obtain handler. | 20 | // Configure the button pin and obtain handler. |
| 15 | // On the Nucleo F091RC there is a button connected to pin PC13. | 21 | // On the Nucleo F091RC there is a button connected to pin PC13. |
| 16 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); | 22 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); |
| 17 | 23 | ||
| 18 | info!("Press the USER button..."); | 24 | info!("Press the USER button..."); |
| 19 | loop { | 25 | loop { |
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/stm32f3/src/bin/button_events.rs b/examples/stm32f3/src/bin/button_events.rs index 99957a641..643f499ed 100644 --- a/examples/stm32f3/src/bin/button_events.rs +++ b/examples/stm32f3/src/bin/button_events.rs | |||
| @@ -11,13 +11,19 @@ | |||
| 11 | 11 | ||
| 12 | use defmt::*; | 12 | use defmt::*; |
| 13 | use embassy_executor::Spawner; | 13 | use embassy_executor::Spawner; |
| 14 | use embassy_stm32::exti::ExtiInput; | 14 | use embassy_stm32::exti::{self, ExtiInput}; |
| 15 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | 15 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; |
| 16 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 16 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | 17 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; |
| 17 | use embassy_sync::channel::Channel; | 18 | use embassy_sync::channel::Channel; |
| 18 | use embassy_time::{Duration, Timer, with_timeout}; | 19 | use embassy_time::{Duration, Timer, with_timeout}; |
| 19 | use {defmt_rtt as _, panic_probe as _}; | 20 | use {defmt_rtt as _, panic_probe as _}; |
| 20 | 21 | ||
| 22 | bind_interrupts!( | ||
| 23 | pub struct Irqs{ | ||
| 24 | EXTI0 => exti::InterruptHandler<interrupt::typelevel::EXTI0>; | ||
| 25 | }); | ||
| 26 | |||
| 21 | struct Leds<'a> { | 27 | struct Leds<'a> { |
| 22 | leds: [Output<'a>; 8], | 28 | leds: [Output<'a>; 8], |
| 23 | direction: i8, | 29 | direction: i8, |
| @@ -99,7 +105,7 @@ static CHANNEL: Channel<ThreadModeRawMutex, ButtonEvent, 4> = Channel::new(); | |||
| 99 | #[embassy_executor::main] | 105 | #[embassy_executor::main] |
| 100 | async fn main(spawner: Spawner) { | 106 | async fn main(spawner: Spawner) { |
| 101 | let p = embassy_stm32::init(Default::default()); | 107 | let p = embassy_stm32::init(Default::default()); |
| 102 | let button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Down); | 108 | let button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Down, Irqs); |
| 103 | info!("Press the USER button..."); | 109 | info!("Press the USER button..."); |
| 104 | let leds = [ | 110 | let leds = [ |
| 105 | Output::new(p.PE9, Level::Low, Speed::Low), | 111 | Output::new(p.PE9, Level::Low, Speed::Low), |
diff --git a/examples/stm32f3/src/bin/button_exti.rs b/examples/stm32f3/src/bin/button_exti.rs index a55530e0e..1df4735ca 100644 --- a/examples/stm32f3/src/bin/button_exti.rs +++ b/examples/stm32f3/src/bin/button_exti.rs | |||
| @@ -3,16 +3,22 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::exti::ExtiInput; | 6 | use embassy_stm32::exti::{self, ExtiInput}; |
| 7 | use embassy_stm32::gpio::Pull; | 7 | use embassy_stm32::gpio::Pull; |
| 8 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 11 | bind_interrupts!( | ||
| 12 | pub struct Irqs{ | ||
| 13 | EXTI0 => exti::InterruptHandler<interrupt::typelevel::EXTI0>; | ||
| 14 | }); | ||
| 15 | |||
| 10 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_stm32::init(Default::default()); | 18 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | 19 | info!("Hello World!"); |
| 14 | 20 | ||
| 15 | let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Down); | 21 | let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Down, Irqs); |
| 16 | 22 | ||
| 17 | info!("Press the USER button..."); | 23 | info!("Press the USER button..."); |
| 18 | 24 | ||
diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 5628cb827..694e85657 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs | |||
| @@ -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(); |
diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs index 01b881c79..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,8 +20,8 @@ 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, |
| @@ -31,6 +31,7 @@ async fn adc_task(p: Peripherals) { | |||
| 31 | (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, |
| @@ -40,6 +41,7 @@ async fn adc_task(p: Peripherals) { | |||
| 40 | (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/button_exti.rs b/examples/stm32f4/src/bin/button_exti.rs index 2a546dac5..e7e1549a8 100644 --- a/examples/stm32f4/src/bin/button_exti.rs +++ b/examples/stm32f4/src/bin/button_exti.rs | |||
| @@ -3,16 +3,22 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::exti::ExtiInput; | 6 | use embassy_stm32::exti::{self, ExtiInput}; |
| 7 | use embassy_stm32::gpio::Pull; | 7 | use embassy_stm32::gpio::Pull; |
| 8 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 11 | bind_interrupts!( | ||
| 12 | pub struct Irqs{ | ||
| 13 | EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>; | ||
| 14 | }); | ||
| 15 | |||
| 10 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_stm32::init(Default::default()); | 18 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | 19 | info!("Hello World!"); |
| 14 | 20 | ||
| 15 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); | 21 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); |
| 16 | 22 | ||
| 17 | info!("Press the USER button..."); | 23 | info!("Press the USER button..."); |
| 18 | 24 | ||
diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs index 2d72b6b0b..8dfa0916d 100644 --- a/examples/stm32f4/src/bin/eth.rs +++ b/examples/stm32f4/src/bin/eth.rs | |||
| @@ -5,8 +5,8 @@ use defmt::*; | |||
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_net::tcp::TcpSocket; | 6 | use embassy_net::tcp::TcpSocket; |
| 7 | use embassy_net::{Ipv4Address, StackResources}; | 7 | use embassy_net::{Ipv4Address, StackResources}; |
| 8 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; | 8 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma}; |
| 9 | use embassy_stm32::peripherals::ETH; | 9 | use embassy_stm32::peripherals::{ETH, ETH_SMA}; |
| 10 | use embassy_stm32::rng::Rng; | 10 | use embassy_stm32::rng::Rng; |
| 11 | use embassy_stm32::time::Hertz; | 11 | use embassy_stm32::time::Hertz; |
| 12 | use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; | 12 | use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; |
| @@ -20,7 +20,7 @@ bind_interrupts!(struct Irqs { | |||
| 20 | HASH_RNG => rng::InterruptHandler<peripherals::RNG>; | 20 | HASH_RNG => rng::InterruptHandler<peripherals::RNG>; |
| 21 | }); | 21 | }); |
| 22 | 22 | ||
| 23 | type Device = Ethernet<'static, ETH, GenericPhy>; | 23 | type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>; |
| 24 | 24 | ||
| 25 | #[embassy_executor::task] | 25 | #[embassy_executor::task] |
| 26 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { | 26 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { |
| @@ -67,16 +67,16 @@ async fn main(spawner: Spawner) -> ! { | |||
| 67 | p.ETH, | 67 | p.ETH, |
| 68 | Irqs, | 68 | Irqs, |
| 69 | p.PA1, | 69 | p.PA1, |
| 70 | p.PA2, | ||
| 71 | p.PC1, | ||
| 72 | p.PA7, | 70 | p.PA7, |
| 73 | p.PC4, | 71 | p.PC4, |
| 74 | p.PC5, | 72 | p.PC5, |
| 75 | p.PG13, | 73 | p.PG13, |
| 76 | p.PB13, | 74 | p.PB13, |
| 77 | p.PG11, | 75 | p.PG11, |
| 78 | GenericPhy::new_auto(), | ||
| 79 | mac_addr, | 76 | mac_addr, |
| 77 | p.ETH_SMA, | ||
| 78 | p.PA2, | ||
| 79 | p.PC1, | ||
| 80 | ); | 80 | ); |
| 81 | 81 | ||
| 82 | let config = embassy_net::Config::dhcpv4(Default::default()); | 82 | let config = embassy_net::Config::dhcpv4(Default::default()); |
diff --git a/examples/stm32f4/src/bin/eth_compliance_test.rs b/examples/stm32f4/src/bin/eth_compliance_test.rs index 734a14c2c..dc5d7dbb6 100644 --- a/examples/stm32f4/src/bin/eth_compliance_test.rs +++ b/examples/stm32f4/src/bin/eth_compliance_test.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::eth::{Ethernet, GenericPhy, PacketQueue, StationManagement}; | 6 | use embassy_stm32::eth::{Ethernet, PacketQueue, StationManagement}; |
| 7 | use embassy_stm32::time::Hertz; | 7 | use embassy_stm32::time::Hertz; |
| 8 | use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; | 8 | use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; |
| 9 | use embassy_time::Timer; | 9 | use embassy_time::Timer; |
| @@ -43,27 +43,27 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 43 | 43 | ||
| 44 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; | 44 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; |
| 45 | 45 | ||
| 46 | const PHY_ADDR: u8 = 0; | ||
| 47 | static PACKETS: StaticCell<PacketQueue<4, 4>> = StaticCell::new(); | 46 | static PACKETS: StaticCell<PacketQueue<4, 4>> = StaticCell::new(); |
| 48 | let mut device = Ethernet::new( | 47 | let mut device = Ethernet::new( |
| 49 | PACKETS.init(PacketQueue::<4, 4>::new()), | 48 | PACKETS.init(PacketQueue::<4, 4>::new()), |
| 50 | p.ETH, | 49 | p.ETH, |
| 51 | Irqs, | 50 | Irqs, |
| 52 | p.PA1, | 51 | p.PA1, |
| 53 | p.PA2, | ||
| 54 | p.PC1, | ||
| 55 | p.PA7, | 52 | p.PA7, |
| 56 | p.PC4, | 53 | p.PC4, |
| 57 | p.PC5, | 54 | p.PC5, |
| 58 | p.PG13, | 55 | p.PG13, |
| 59 | p.PB13, | 56 | p.PB13, |
| 60 | p.PG11, | 57 | p.PG11, |
| 61 | GenericPhy::new(PHY_ADDR), | ||
| 62 | mac_addr, | 58 | mac_addr, |
| 59 | p.ETH_SMA, | ||
| 60 | p.PA2, | ||
| 61 | p.PC1, | ||
| 63 | ); | 62 | ); |
| 64 | 63 | ||
| 65 | let sm = device.station_management(); | 64 | let sm = device.phy_mut().station_management(); |
| 66 | 65 | ||
| 66 | const PHY_ADDR: u8 = 0; | ||
| 67 | // Just an example. Exact register settings depend on the specific PHY and test. | 67 | // Just an example. Exact register settings depend on the specific PHY and test. |
| 68 | sm.smi_write(PHY_ADDR, 0, 0x2100); | 68 | sm.smi_write(PHY_ADDR, 0, 0x2100); |
| 69 | sm.smi_write(PHY_ADDR, 11, 0xA000); | 69 | sm.smi_write(PHY_ADDR, 11, 0xA000); |
diff --git a/examples/stm32f4/src/bin/eth_w5500.rs b/examples/stm32f4/src/bin/eth_w5500.rs index 0adcda614..e274d2a66 100644 --- a/examples/stm32f4/src/bin/eth_w5500.rs +++ b/examples/stm32f4/src/bin/eth_w5500.rs | |||
| @@ -7,14 +7,14 @@ use embassy_net::tcp::TcpSocket; | |||
| 7 | use embassy_net::{Ipv4Address, StackResources}; | 7 | use embassy_net::{Ipv4Address, StackResources}; |
| 8 | use embassy_net_wiznet::chip::W5500; | 8 | use embassy_net_wiznet::chip::W5500; |
| 9 | use embassy_net_wiznet::{Device, Runner, State}; | 9 | use embassy_net_wiznet::{Device, Runner, State}; |
| 10 | use embassy_stm32::exti::ExtiInput; | 10 | use embassy_stm32::exti::{self, ExtiInput}; |
| 11 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | 11 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; |
| 12 | use embassy_stm32::mode::Async; | 12 | use embassy_stm32::mode::Async; |
| 13 | use embassy_stm32::rng::Rng; | 13 | use embassy_stm32::rng::Rng; |
| 14 | use embassy_stm32::spi::Spi; | 14 | use embassy_stm32::spi::Spi; |
| 15 | use embassy_stm32::spi::mode::Master; | 15 | use embassy_stm32::spi::mode::Master; |
| 16 | use embassy_stm32::time::Hertz; | 16 | use embassy_stm32::time::Hertz; |
| 17 | use embassy_stm32::{Config, bind_interrupts, peripherals, rng, spi}; | 17 | use embassy_stm32::{Config, bind_interrupts, interrupt, peripherals, rng, spi}; |
| 18 | use embassy_time::{Delay, Timer}; | 18 | use embassy_time::{Delay, Timer}; |
| 19 | use embedded_hal_bus::spi::ExclusiveDevice; | 19 | use embedded_hal_bus::spi::ExclusiveDevice; |
| 20 | use embedded_io_async::Write; | 20 | use embedded_io_async::Write; |
| @@ -23,6 +23,7 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 23 | 23 | ||
| 24 | bind_interrupts!(struct Irqs { | 24 | bind_interrupts!(struct Irqs { |
| 25 | HASH_RNG => rng::InterruptHandler<peripherals::RNG>; | 25 | HASH_RNG => rng::InterruptHandler<peripherals::RNG>; |
| 26 | EXTI0 => exti::InterruptHandler<interrupt::typelevel::EXTI0>; | ||
| 26 | }); | 27 | }); |
| 27 | 28 | ||
| 28 | type EthernetSPI = ExclusiveDevice<Spi<'static, Async, Master>, Output<'static>, Delay>; | 29 | type EthernetSPI = ExclusiveDevice<Spi<'static, Async, Master>, Output<'static>, Delay>; |
| @@ -75,7 +76,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 75 | let cs = Output::new(p.PA4, Level::High, Speed::VeryHigh); | 76 | let cs = Output::new(p.PA4, Level::High, Speed::VeryHigh); |
| 76 | let spi = unwrap!(ExclusiveDevice::new(spi, cs, Delay)); | 77 | let spi = unwrap!(ExclusiveDevice::new(spi, cs, Delay)); |
| 77 | 78 | ||
| 78 | let w5500_int = ExtiInput::new(p.PB0, p.EXTI0, Pull::Up); | 79 | let w5500_int = ExtiInput::new(p.PB0, p.EXTI0, Pull::Up, Irqs); |
| 79 | let w5500_reset = Output::new(p.PB1, Level::High, Speed::VeryHigh); | 80 | let w5500_reset = Output::new(p.PB1, Level::High, Speed::VeryHigh); |
| 80 | 81 | ||
| 81 | let mac_addr = [0x02, 234, 3, 4, 82, 231]; | 82 | let mac_addr = [0x02, 234, 3, 4, 82, 231]; |
diff --git a/examples/stm32f4/src/bin/usb_hid_keyboard.rs b/examples/stm32f4/src/bin/usb_hid_keyboard.rs index a3afb887c..2d834dcf7 100644 --- a/examples/stm32f4/src/bin/usb_hid_keyboard.rs +++ b/examples/stm32f4/src/bin/usb_hid_keyboard.rs | |||
| @@ -1,17 +1,19 @@ | |||
| 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; |
| 8 | use embassy_futures::join::join; | 8 | use embassy_futures::join::join; |
| 9 | use embassy_stm32::exti::ExtiInput; | 9 | use embassy_stm32::exti::{self, ExtiInput}; |
| 10 | use embassy_stm32::gpio::Pull; | 10 | 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, interrupt, 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}; |
| @@ -19,8 +21,11 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 19 | 21 | ||
| 20 | bind_interrupts!(struct Irqs { | 22 | bind_interrupts!(struct Irqs { |
| 21 | OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>; | 23 | OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>; |
| 24 | EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>; | ||
| 22 | }); | 25 | }); |
| 23 | 26 | ||
| 27 | static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); | ||
| 28 | |||
| 24 | // If you are trying this and your USB device doesn't connect, the most | 29 | // If you are trying this and your USB device doesn't connect, the most |
| 25 | // common issues are the RCC config and vbus_detection | 30 | // common issues are the RCC config and vbus_detection |
| 26 | // | 31 | // |
| @@ -70,6 +75,10 @@ async fn main(_spawner: Spawner) { | |||
| 70 | config.serial_number = Some("12345678"); | 75 | config.serial_number = Some("12345678"); |
| 71 | config.max_power = 100; | 76 | config.max_power = 100; |
| 72 | config.max_packet_size_0 = 64; | 77 | config.max_packet_size_0 = 64; |
| 78 | config.composite_with_iads = false; | ||
| 79 | config.device_class = 0; | ||
| 80 | config.device_sub_class = 0; | ||
| 81 | config.device_protocol = 0; | ||
| 73 | 82 | ||
| 74 | // Create embassy-usb DeviceBuilder using the driver and config. | 83 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 75 | // It needs some buffers for building the descriptors. | 84 | // It needs some buffers for building the descriptors. |
| @@ -101,6 +110,8 @@ async fn main(_spawner: Spawner) { | |||
| 101 | request_handler: None, | 110 | request_handler: None, |
| 102 | poll_ms: 60, | 111 | poll_ms: 60, |
| 103 | max_packet_size: 8, | 112 | max_packet_size: 8, |
| 113 | hid_subclass: HidSubclass::Boot, | ||
| 114 | hid_boot_protocol: HidBootProtocol::Keyboard, | ||
| 104 | }; | 115 | }; |
| 105 | 116 | ||
| 106 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); | 117 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); |
| @@ -113,7 +124,7 @@ async fn main(_spawner: Spawner) { | |||
| 113 | 124 | ||
| 114 | let (reader, mut writer) = hid.split(); | 125 | let (reader, mut writer) = hid.split(); |
| 115 | 126 | ||
| 116 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); | 127 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); |
| 117 | 128 | ||
| 118 | // Do stuff with the class! | 129 | // Do stuff with the class! |
| 119 | let in_fut = async { | 130 | let in_fut = async { |
| @@ -121,32 +132,46 @@ async fn main(_spawner: Spawner) { | |||
| 121 | button.wait_for_rising_edge().await; | 132 | button.wait_for_rising_edge().await; |
| 122 | // signal_pin.wait_for_high().await; | 133 | // signal_pin.wait_for_high().await; |
| 123 | info!("Button pressed!"); | 134 | info!("Button pressed!"); |
| 124 | // Create a report with the A key pressed. (no shift modifier) | 135 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 125 | let report = KeyboardReport { | 136 | match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await { |
| 126 | keycodes: [4, 0, 0, 0, 0, 0], | 137 | Ok(()) => {} |
| 127 | leds: 0, | 138 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 128 | modifier: 0, | 139 | }; |
| 129 | reserved: 0, | 140 | } else { |
| 130 | }; | 141 | // Create a report with the A key pressed. (no shift modifier) |
| 131 | // Send the report. | 142 | let report = KeyboardReport { |
| 132 | match writer.write_serialize(&report).await { | 143 | keycodes: [4, 0, 0, 0, 0, 0], |
| 133 | Ok(()) => {} | 144 | leds: 0, |
| 134 | Err(e) => warn!("Failed to send report: {:?}", e), | 145 | modifier: 0, |
| 135 | }; | 146 | reserved: 0, |
| 147 | }; | ||
| 148 | // Send the report. | ||
| 149 | match writer.write_serialize(&report).await { | ||
| 150 | Ok(()) => {} | ||
| 151 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 152 | }; | ||
| 153 | } | ||
| 136 | 154 | ||
| 137 | button.wait_for_falling_edge().await; | 155 | button.wait_for_falling_edge().await; |
| 138 | // signal_pin.wait_for_low().await; | 156 | // signal_pin.wait_for_low().await; |
| 139 | info!("Button released!"); | 157 | info!("Button released!"); |
| 140 | let report = KeyboardReport { | 158 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 141 | keycodes: [0, 0, 0, 0, 0, 0], | 159 | match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await { |
| 142 | leds: 0, | 160 | Ok(()) => {} |
| 143 | modifier: 0, | 161 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 144 | reserved: 0, | 162 | }; |
| 145 | }; | 163 | } else { |
| 146 | match writer.write_serialize(&report).await { | 164 | let report = KeyboardReport { |
| 147 | Ok(()) => {} | 165 | keycodes: [0, 0, 0, 0, 0, 0], |
| 148 | Err(e) => warn!("Failed to send report: {:?}", e), | 166 | leds: 0, |
| 149 | }; | 167 | modifier: 0, |
| 168 | reserved: 0, | ||
| 169 | }; | ||
| 170 | match writer.write_serialize(&report).await { | ||
| 171 | Ok(()) => {} | ||
| 172 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 173 | }; | ||
| 174 | } | ||
| 150 | } | 175 | } |
| 151 | }; | 176 | }; |
| 152 | 177 | ||
| @@ -172,6 +197,18 @@ impl RequestHandler for MyRequestHandler { | |||
| 172 | OutResponse::Accepted | 197 | OutResponse::Accepted |
| 173 | } | 198 | } |
| 174 | 199 | ||
| 200 | fn get_protocol(&self) -> HidProtocolMode { | ||
| 201 | let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); | ||
| 202 | info!("The current HID protocol mode is: {}", protocol); | ||
| 203 | protocol | ||
| 204 | } | ||
| 205 | |||
| 206 | fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { | ||
| 207 | info!("Switching to HID protocol mode: {}", protocol); | ||
| 208 | HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); | ||
| 209 | OutResponse::Accepted | ||
| 210 | } | ||
| 211 | |||
| 175 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { | 212 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { |
| 176 | info!("Set idle rate for {:?} to {:?}", id, dur); | 213 | info!("Set idle rate for {:?} to {:?}", id, dur); |
| 177 | } | 214 | } |
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/button_exti.rs b/examples/stm32f7/src/bin/button_exti.rs index 2a546dac5..e7e1549a8 100644 --- a/examples/stm32f7/src/bin/button_exti.rs +++ b/examples/stm32f7/src/bin/button_exti.rs | |||
| @@ -3,16 +3,22 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::exti::ExtiInput; | 6 | use embassy_stm32::exti::{self, ExtiInput}; |
| 7 | use embassy_stm32::gpio::Pull; | 7 | use embassy_stm32::gpio::Pull; |
| 8 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 11 | bind_interrupts!( | ||
| 12 | pub struct Irqs{ | ||
| 13 | EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>; | ||
| 14 | }); | ||
| 15 | |||
| 10 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_stm32::init(Default::default()); | 18 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | 19 | info!("Hello World!"); |
| 14 | 20 | ||
| 15 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); | 21 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); |
| 16 | 22 | ||
| 17 | info!("Press the USER button..."); | 23 | info!("Press the USER button..."); |
| 18 | 24 | ||
diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index f8a129239..8613376b8 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs | |||
| @@ -5,8 +5,8 @@ use defmt::*; | |||
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_net::tcp::TcpSocket; | 6 | use embassy_net::tcp::TcpSocket; |
| 7 | use embassy_net::{Ipv4Address, StackResources}; | 7 | use embassy_net::{Ipv4Address, StackResources}; |
| 8 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; | 8 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma}; |
| 9 | use embassy_stm32::peripherals::ETH; | 9 | use embassy_stm32::peripherals::{ETH, ETH_SMA}; |
| 10 | use embassy_stm32::rng::Rng; | 10 | use embassy_stm32::rng::Rng; |
| 11 | use embassy_stm32::time::Hertz; | 11 | use embassy_stm32::time::Hertz; |
| 12 | use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; | 12 | use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; |
| @@ -20,7 +20,7 @@ bind_interrupts!(struct Irqs { | |||
| 20 | HASH_RNG => rng::InterruptHandler<peripherals::RNG>; | 20 | HASH_RNG => rng::InterruptHandler<peripherals::RNG>; |
| 21 | }); | 21 | }); |
| 22 | 22 | ||
| 23 | type Device = Ethernet<'static, ETH, GenericPhy>; | 23 | type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>; |
| 24 | 24 | ||
| 25 | #[embassy_executor::task] | 25 | #[embassy_executor::task] |
| 26 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { | 26 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { |
| @@ -67,16 +67,16 @@ async fn main(spawner: Spawner) -> ! { | |||
| 67 | p.ETH, | 67 | p.ETH, |
| 68 | Irqs, | 68 | Irqs, |
| 69 | p.PA1, | 69 | p.PA1, |
| 70 | p.PA2, | ||
| 71 | p.PC1, | ||
| 72 | p.PA7, | 70 | p.PA7, |
| 73 | p.PC4, | 71 | p.PC4, |
| 74 | p.PC5, | 72 | p.PC5, |
| 75 | p.PG13, | 73 | p.PG13, |
| 76 | p.PB13, | 74 | p.PB13, |
| 77 | p.PG11, | 75 | p.PG11, |
| 78 | GenericPhy::new_auto(), | ||
| 79 | mac_addr, | 76 | mac_addr, |
| 77 | p.ETH_SMA, | ||
| 78 | p.PA2, | ||
| 79 | p.PC1, | ||
| 80 | ); | 80 | ); |
| 81 | 81 | ||
| 82 | let config = embassy_net::Config::dhcpv4(Default::default()); | 82 | let config = embassy_net::Config::dhcpv4(Default::default()); |
diff --git a/examples/stm32f7/src/bin/pwm.rs b/examples/stm32f7/src/bin/pwm.rs new file mode 100644 index 000000000..b071eb597 --- /dev/null +++ b/examples/stm32f7/src/bin/pwm.rs | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::Config; | ||
| 7 | use embassy_stm32::gpio::OutputType; | ||
| 8 | use embassy_stm32::time::{Hertz, mhz}; | ||
| 9 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | ||
| 10 | use embassy_time::Timer; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | // If you are trying this and your USB device doesn't connect, the most | ||
| 14 | // common issues are the RCC config and vbus_detection | ||
| 15 | // | ||
| 16 | // See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure | ||
| 17 | // for more information. | ||
| 18 | #[embassy_executor::main] | ||
| 19 | async fn main(_spawner: Spawner) { | ||
| 20 | info!("Hello World!"); | ||
| 21 | |||
| 22 | let mut config = Config::default(); | ||
| 23 | { | ||
| 24 | use embassy_stm32::rcc::*; | ||
| 25 | config.rcc.hse = Some(Hse { | ||
| 26 | freq: Hertz(8_000_000), | ||
| 27 | mode: HseMode::Bypass, | ||
| 28 | }); | ||
| 29 | config.rcc.pll_src = PllSource::HSE; | ||
| 30 | config.rcc.pll = Some(Pll { | ||
| 31 | prediv: PllPreDiv::DIV4, | ||
| 32 | mul: PllMul::MUL200, | ||
| 33 | divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 200 / 2 = 200Mhz | ||
| 34 | divq: Some(PllQDiv::DIV4), // 8mhz / 4 * 200 / 4 = 100Mhz | ||
| 35 | divr: None, | ||
| 36 | }); | ||
| 37 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 38 | config.rcc.apb1_pre = APBPrescaler::DIV4; | ||
| 39 | config.rcc.apb2_pre = APBPrescaler::DIV2; | ||
| 40 | config.rcc.sys = Sysclk::PLL1_P; | ||
| 41 | } | ||
| 42 | let p = embassy_stm32::init(config); | ||
| 43 | let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull); | ||
| 44 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, mhz(1), Default::default()); | ||
| 45 | let mut ch1 = pwm.ch1(); | ||
| 46 | ch1.enable(); | ||
| 47 | |||
| 48 | info!("PWM initialized"); | ||
| 49 | info!("PWM max duty {}", ch1.max_duty_cycle()); | ||
| 50 | |||
| 51 | loop { | ||
| 52 | ch1.set_duty_cycle_fully_off(); | ||
| 53 | Timer::after_millis(300).await; | ||
| 54 | ch1.set_duty_cycle_fraction(1, 4); | ||
| 55 | Timer::after_millis(300).await; | ||
| 56 | ch1.set_duty_cycle_fraction(1, 2); | ||
| 57 | Timer::after_millis(300).await; | ||
| 58 | ch1.set_duty_cycle(ch1.max_duty_cycle() - 1); | ||
| 59 | Timer::after_millis(300).await; | ||
| 60 | } | ||
| 61 | } | ||
diff --git a/examples/stm32f7/src/bin/pwm_ringbuffer.rs b/examples/stm32f7/src/bin/pwm_ringbuffer.rs new file mode 100644 index 000000000..4d191ac13 --- /dev/null +++ b/examples/stm32f7/src/bin/pwm_ringbuffer.rs | |||
| @@ -0,0 +1,153 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::Config; | ||
| 7 | use embassy_stm32::gpio::OutputType; | ||
| 8 | use embassy_stm32::time::mhz; | ||
| 9 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | ||
| 10 | use embassy_time::Timer; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | // If you are trying this and your USB device doesn't connect, the most | ||
| 14 | // common issues are the RCC config and vbus_detection | ||
| 15 | // | ||
| 16 | // See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure | ||
| 17 | // for more information. | ||
| 18 | #[embassy_executor::main] | ||
| 19 | async fn main(_spawner: Spawner) { | ||
| 20 | info!("PWM Ring Buffer Example"); | ||
| 21 | |||
| 22 | let mut config = Config::default(); | ||
| 23 | { | ||
| 24 | use embassy_stm32::rcc::*; | ||
| 25 | use embassy_stm32::time::Hertz; | ||
| 26 | config.rcc.hse = Some(Hse { | ||
| 27 | freq: Hertz(8_000_000), | ||
| 28 | mode: HseMode::Bypass, | ||
| 29 | }); | ||
| 30 | config.rcc.pll_src = PllSource::HSE; | ||
| 31 | config.rcc.pll = Some(Pll { | ||
| 32 | prediv: PllPreDiv::DIV4, | ||
| 33 | mul: PllMul::MUL200, | ||
| 34 | divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 200 / 2 = 200Mhz | ||
| 35 | divq: Some(PllQDiv::DIV4), // 8mhz / 4 * 200 / 4 = 100Mhz | ||
| 36 | divr: None, | ||
| 37 | }); | ||
| 38 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 39 | config.rcc.apb1_pre = APBPrescaler::DIV4; | ||
| 40 | config.rcc.apb2_pre = APBPrescaler::DIV2; | ||
| 41 | config.rcc.sys = Sysclk::PLL1_P; | ||
| 42 | } | ||
| 43 | let p = embassy_stm32::init(config); | ||
| 44 | |||
| 45 | // Initialize PWM on TIM1 | ||
| 46 | let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull); | ||
| 47 | let ch2_pin = PwmPin::new(p.PE11, OutputType::PushPull); | ||
| 48 | let mut pwm = SimplePwm::new( | ||
| 49 | p.TIM1, | ||
| 50 | Some(ch1_pin), | ||
| 51 | Some(ch2_pin), | ||
| 52 | None, | ||
| 53 | None, | ||
| 54 | mhz(1), | ||
| 55 | Default::default(), | ||
| 56 | ); | ||
| 57 | |||
| 58 | // Use channel 1 for static PWM at 50% | ||
| 59 | let mut ch1 = pwm.ch1(); | ||
| 60 | ch1.enable(); | ||
| 61 | ch1.set_duty_cycle_fraction(1, 2); | ||
| 62 | info!("Channel 1 (PE9/D6): Static 50% duty cycle"); | ||
| 63 | |||
| 64 | // Get max duty from channel 1 before converting channel 2 | ||
| 65 | let max_duty = ch1.max_duty_cycle(); | ||
| 66 | info!("PWM max duty: {}", max_duty); | ||
| 67 | |||
| 68 | // Create a DMA ring buffer for channel 2 | ||
| 69 | const BUFFER_SIZE: usize = 128; | ||
| 70 | static mut DMA_BUFFER: [u16; BUFFER_SIZE] = [0u16; BUFFER_SIZE]; | ||
| 71 | let dma_buffer = unsafe { &mut *core::ptr::addr_of_mut!(DMA_BUFFER) }; | ||
| 72 | |||
| 73 | // Pre-fill buffer with initial sine wave using lookup table approach | ||
| 74 | for i in 0..BUFFER_SIZE { | ||
| 75 | // Simple sine approximation using triangle wave | ||
| 76 | let phase = (i * 256) / BUFFER_SIZE; | ||
| 77 | let sine_approx = if phase < 128 { | ||
| 78 | phase as u16 * 2 | ||
| 79 | } else { | ||
| 80 | (255 - phase) as u16 * 2 | ||
| 81 | }; | ||
| 82 | dma_buffer[i] = (sine_approx as u32 * max_duty as u32 / 256) as u16; | ||
| 83 | } | ||
| 84 | |||
| 85 | // Convert channel 2 to ring-buffered PWM | ||
| 86 | let mut ring_pwm = pwm.ch1().into_ring_buffered_channel(p.DMA2_CH5, dma_buffer); | ||
| 87 | |||
| 88 | info!("Ring buffer capacity: {}", ring_pwm.capacity()); | ||
| 89 | |||
| 90 | // Pre-write some initial data to the buffer before starting | ||
| 91 | info!("Pre-writing initial waveform data..."); | ||
| 92 | |||
| 93 | ring_pwm.write(&[0; BUFFER_SIZE]).unwrap(); | ||
| 94 | |||
| 95 | // Enable the PWM channel output | ||
| 96 | ring_pwm.enable(); | ||
| 97 | |||
| 98 | // Start the DMA ring buffer | ||
| 99 | ring_pwm.start(); | ||
| 100 | info!("Channel 2 (PE11/D5): Ring buffered sine wave started"); | ||
| 101 | |||
| 102 | // Give DMA time to start consuming | ||
| 103 | Timer::after_millis(10).await; | ||
| 104 | |||
| 105 | // Continuously update the waveform | ||
| 106 | let mut phase: f32 = 0.0; | ||
| 107 | let mut amplitude: f32 = 1.0; | ||
| 108 | let mut amplitude_direction = -0.05; | ||
| 109 | |||
| 110 | loop { | ||
| 111 | // Generate new waveform data with varying amplitude | ||
| 112 | let mut new_data = [0u16; 32]; | ||
| 113 | for i in 0..new_data.len() { | ||
| 114 | // Triangle wave approximation for sine | ||
| 115 | let pos = ((i as u32 + phase as u32) * 4) % 256; | ||
| 116 | let sine_approx = if pos < 128 { | ||
| 117 | pos as u16 * 2 | ||
| 118 | } else { | ||
| 119 | (255 - pos) as u16 * 2 | ||
| 120 | }; | ||
| 121 | let scaled = (sine_approx as u32 * (amplitude * 256.0) as u32) / (256 * 256); | ||
| 122 | new_data[i] = ((scaled * max_duty as u32) / 256) as u16; | ||
| 123 | } | ||
| 124 | |||
| 125 | // Write new data to the ring buffer | ||
| 126 | match ring_pwm.write_exact(&new_data).await { | ||
| 127 | Ok(_remaining) => {} | ||
| 128 | Err(e) => { | ||
| 129 | info!("Write error: {:?}", e); | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | // Update phase for animation effect | ||
| 134 | phase += 2.0; | ||
| 135 | if phase >= 64.0 { | ||
| 136 | phase = 0.0; | ||
| 137 | } | ||
| 138 | |||
| 139 | // Vary amplitude for breathing effect | ||
| 140 | amplitude += amplitude_direction; | ||
| 141 | if amplitude <= 0.2 || amplitude >= 1.0 { | ||
| 142 | amplitude_direction = -amplitude_direction; | ||
| 143 | } | ||
| 144 | |||
| 145 | // Log buffer status periodically | ||
| 146 | if (phase as u32) % 10 == 0 { | ||
| 147 | match ring_pwm.len() { | ||
| 148 | Ok(len) => info!("Ring buffer fill: {}/{}", len, ring_pwm.capacity()), | ||
| 149 | Err(_) => info!("Error reading buffer length"), | ||
| 150 | } | ||
| 151 | } | ||
| 152 | } | ||
| 153 | } | ||
diff --git a/examples/stm32g0/src/bin/adc_oversampling.rs b/examples/stm32g0/src/bin/adc_oversampling.rs index f6979889d..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,12 +16,14 @@ 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 | let mut pin = p.PA1; | 20 | config.clock = Some(Clock::Async { div: Presc::DIV1 }); |
| 21 | config.oversampling_ratio = Some(Ovsr::MUL16); | ||
| 22 | config.oversampling_shift = Some(Ovss::NO_SHIFT); | ||
| 23 | config.oversampling_enable = Some(true); | ||
| 21 | 24 | ||
| 22 | adc.set_oversampling_ratio(Ovsr::MUL16); | 25 | let mut adc = Adc::new_with_config(p.ADC1, config); |
| 23 | adc.set_oversampling_shift(Ovss::NO_SHIFT); | 26 | let mut pin = p.PA1; |
| 24 | adc.oversampling_enable(true); | ||
| 25 | 27 | ||
| 26 | loop { | 28 | loop { |
| 27 | let v = adc.blocking_read(&mut pin, SampleTime::CYCLES1_5); | 29 | let v = adc.blocking_read(&mut pin, SampleTime::CYCLES1_5); |
diff --git a/examples/stm32g0/src/bin/button_exti.rs b/examples/stm32g0/src/bin/button_exti.rs index 34a08bbc6..9d54479da 100644 --- a/examples/stm32g0/src/bin/button_exti.rs +++ b/examples/stm32g0/src/bin/button_exti.rs | |||
| @@ -3,16 +3,22 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::exti::ExtiInput; | 6 | use embassy_stm32::exti::{self, ExtiInput}; |
| 7 | use embassy_stm32::gpio::Pull; | 7 | use embassy_stm32::gpio::Pull; |
| 8 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 11 | bind_interrupts!( | ||
| 12 | pub struct Irqs{ | ||
| 13 | EXTI4_15 => exti::InterruptHandler<interrupt::typelevel::EXTI4_15>; | ||
| 14 | }); | ||
| 15 | |||
| 10 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_stm32::init(Default::default()); | 18 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | 19 | info!("Hello World!"); |
| 14 | 20 | ||
| 15 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); | 21 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); |
| 16 | 22 | ||
| 17 | info!("Press the USER button..."); | 23 | info!("Press the USER button..."); |
| 18 | 24 | ||
diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs index 94315141c..2149e0748 100644 --- a/examples/stm32g4/src/bin/adc.rs +++ b/examples/stm32g4/src/bin/adc.rs | |||
| @@ -28,9 +28,9 @@ 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 | 32 | ||
| 33 | let mut adc_temp = Adc::new(p.ADC1); | 33 | let mut adc_temp = Adc::new(p.ADC1, Default::default()); |
| 34 | let mut temperature = adc_temp.enable_temperature(); | 34 | let mut temperature = adc_temp.enable_temperature(); |
| 35 | 35 | ||
| 36 | loop { | 36 | loop { |
diff --git a/examples/stm32g4/src/bin/adc_differential.rs b/examples/stm32g4/src/bin/adc_differential.rs index 2773723e9..6dedf88d6 100644 --- a/examples/stm32g4/src/bin/adc_differential.rs +++ b/examples/stm32g4/src/bin/adc_differential.rs | |||
| @@ -32,7 +32,7 @@ async fn main(_spawner: Spawner) { | |||
| 32 | } | 32 | } |
| 33 | let 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 | let mut differential_channel = (p.PA0, p.PA1); | 36 | let mut differential_channel = (p.PA0, p.PA1); |
| 37 | 37 | ||
| 38 | // can also use | 38 | // can also use |
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 cb99ab2a7..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,7 +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 | |||
| 36 | // 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 |
| 37 | // page652 Oversampler | 38 | // page652 Oversampler |
| 38 | // 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 |
| @@ -44,9 +45,11 @@ async fn main(_spawner: Spawner) { | |||
| 44 | // 0x05 oversampling ratio X64 | 45 | // 0x05 oversampling ratio X64 |
| 45 | // 0x06 oversampling ratio X128 | 46 | // 0x06 oversampling ratio X128 |
| 46 | // 0x07 oversampling ratio X256 | 47 | // 0x07 oversampling ratio X256 |
| 47 | adc.set_oversampling_ratio(0x03); // ratio X3 | 48 | config.oversampling_ratio = Some(0x03); // ratio X3 |
| 48 | adc.set_oversampling_shift(0b0000); // no shift | 49 | config.oversampling_shift = Some(0b0000); // no shift |
| 49 | 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); | ||
| 50 | 53 | ||
| 51 | loop { | 54 | loop { |
| 52 | let measured = adc.blocking_read(&mut p.PA0, SampleTime::CYCLES6_5); | 55 | let measured = adc.blocking_read(&mut p.PA0, SampleTime::CYCLES6_5); |
diff --git a/examples/stm32g4/src/bin/button_exti.rs b/examples/stm32g4/src/bin/button_exti.rs index 2a546dac5..e7e1549a8 100644 --- a/examples/stm32g4/src/bin/button_exti.rs +++ b/examples/stm32g4/src/bin/button_exti.rs | |||
| @@ -3,16 +3,22 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::exti::ExtiInput; | 6 | use embassy_stm32::exti::{self, ExtiInput}; |
| 7 | use embassy_stm32::gpio::Pull; | 7 | use embassy_stm32::gpio::Pull; |
| 8 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 11 | bind_interrupts!( | ||
| 12 | pub struct Irqs{ | ||
| 13 | EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>; | ||
| 14 | }); | ||
| 15 | |||
| 10 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_stm32::init(Default::default()); | 18 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | 19 | info!("Hello World!"); |
| 14 | 20 | ||
| 15 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); | 21 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); |
| 16 | 22 | ||
| 17 | info!("Press the USER button..."); | 23 | info!("Press the USER button..."); |
| 18 | 24 | ||
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/stm32h5/src/bin/button_exti.rs b/examples/stm32h5/src/bin/button_exti.rs index 2a546dac5..220f89228 100644 --- a/examples/stm32h5/src/bin/button_exti.rs +++ b/examples/stm32h5/src/bin/button_exti.rs | |||
| @@ -3,16 +3,22 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::exti::ExtiInput; | 6 | use embassy_stm32::exti::{self, ExtiInput}; |
| 7 | use embassy_stm32::gpio::Pull; | 7 | use embassy_stm32::gpio::Pull; |
| 8 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 11 | bind_interrupts!( | ||
| 12 | pub struct Irqs{ | ||
| 13 | EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>; | ||
| 14 | }); | ||
| 15 | |||
| 10 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_stm32::init(Default::default()); | 18 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | 19 | info!("Hello World!"); |
| 14 | 20 | ||
| 15 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); | 21 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); |
| 16 | 22 | ||
| 17 | info!("Press the USER button..."); | 23 | info!("Press the USER button..."); |
| 18 | 24 | ||
diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index a5c6cee26..6a3afb2d1 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs | |||
| @@ -5,8 +5,8 @@ use defmt::*; | |||
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_net::tcp::TcpSocket; | 6 | use embassy_net::tcp::TcpSocket; |
| 7 | use embassy_net::{Ipv4Address, StackResources}; | 7 | use embassy_net::{Ipv4Address, StackResources}; |
| 8 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; | 8 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma}; |
| 9 | use embassy_stm32::peripherals::ETH; | 9 | use embassy_stm32::peripherals::{ETH, ETH_SMA}; |
| 10 | use embassy_stm32::rcc::{ | 10 | use embassy_stm32::rcc::{ |
| 11 | AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, | 11 | AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, |
| 12 | }; | 12 | }; |
| @@ -23,7 +23,7 @@ bind_interrupts!(struct Irqs { | |||
| 23 | RNG => rng::InterruptHandler<peripherals::RNG>; | 23 | RNG => rng::InterruptHandler<peripherals::RNG>; |
| 24 | }); | 24 | }); |
| 25 | 25 | ||
| 26 | type Device = Ethernet<'static, ETH, GenericPhy>; | 26 | type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>; |
| 27 | 27 | ||
| 28 | #[embassy_executor::task] | 28 | #[embassy_executor::task] |
| 29 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { | 29 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { |
| @@ -70,16 +70,16 @@ async fn main(spawner: Spawner) -> ! { | |||
| 70 | p.ETH, | 70 | p.ETH, |
| 71 | Irqs, | 71 | Irqs, |
| 72 | p.PA1, | 72 | p.PA1, |
| 73 | p.PA2, | ||
| 74 | p.PC1, | ||
| 75 | p.PA7, | 73 | p.PA7, |
| 76 | p.PC4, | 74 | p.PC4, |
| 77 | p.PC5, | 75 | p.PC5, |
| 78 | p.PG13, | 76 | p.PG13, |
| 79 | p.PB15, | 77 | p.PB15, |
| 80 | p.PG11, | 78 | p.PG11, |
| 81 | GenericPhy::new_auto(), | ||
| 82 | mac_addr, | 79 | mac_addr, |
| 80 | p.ETH_SMA, | ||
| 81 | p.PA2, | ||
| 82 | p.PC1, | ||
| 83 | ); | 83 | ); |
| 84 | 84 | ||
| 85 | let config = embassy_net::Config::dhcpv4(Default::default()); | 85 | let config = embassy_net::Config::dhcpv4(Default::default()); |
diff --git a/examples/stm32h5/src/bin/stop.rs b/examples/stm32h5/src/bin/stop.rs index caebc9daf..8d5456b80 100644 --- a/examples/stm32h5/src/bin/stop.rs +++ b/examples/stm32h5/src/bin/stop.rs | |||
| @@ -7,20 +7,12 @@ | |||
| 7 | use defmt::*; | 7 | use defmt::*; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_stm32::gpio::{AnyPin, Level, Output, Speed}; | 9 | use embassy_stm32::gpio::{AnyPin, Level, Output, Speed}; |
| 10 | use embassy_stm32::low_power::Executor; | ||
| 11 | use embassy_stm32::rcc::{HSIPrescaler, LsConfig}; | 10 | use embassy_stm32::rcc::{HSIPrescaler, LsConfig}; |
| 12 | use embassy_stm32::{Config, Peri}; | 11 | use embassy_stm32::{Config, Peri, low_power}; |
| 13 | use embassy_time::Timer; | 12 | use embassy_time::Timer; |
| 14 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 15 | 14 | ||
| 16 | #[cortex_m_rt::entry] | 15 | #[embassy_executor::main(executor = "low_power::Executor")] |
| 17 | fn main() -> ! { | ||
| 18 | Executor::take().run(|spawner| { | ||
| 19 | spawner.spawn(unwrap!(async_main(spawner))); | ||
| 20 | }) | ||
| 21 | } | ||
| 22 | |||
| 23 | #[embassy_executor::task] | ||
| 24 | async fn async_main(spawner: Spawner) { | 16 | async fn async_main(spawner: Spawner) { |
| 25 | defmt::info!("Program Start"); | 17 | defmt::info!("Program Start"); |
| 26 | 18 | ||
diff --git a/examples/stm32h7/src/bin/button_exti.rs b/examples/stm32h7/src/bin/button_exti.rs index 2a546dac5..e7e1549a8 100644 --- a/examples/stm32h7/src/bin/button_exti.rs +++ b/examples/stm32h7/src/bin/button_exti.rs | |||
| @@ -3,16 +3,22 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::exti::ExtiInput; | 6 | use embassy_stm32::exti::{self, ExtiInput}; |
| 7 | use embassy_stm32::gpio::Pull; | 7 | use embassy_stm32::gpio::Pull; |
| 8 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 11 | bind_interrupts!( | ||
| 12 | pub struct Irqs{ | ||
| 13 | EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>; | ||
| 14 | }); | ||
| 15 | |||
| 10 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_stm32::init(Default::default()); | 18 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | 19 | info!("Hello World!"); |
| 14 | 20 | ||
| 15 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); | 21 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); |
| 16 | 22 | ||
| 17 | info!("Press the USER button..."); | 23 | info!("Press the USER button..."); |
| 18 | 24 | ||
diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 589f4426e..09915799b 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs | |||
| @@ -5,8 +5,8 @@ use defmt::*; | |||
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_net::tcp::TcpSocket; | 6 | use embassy_net::tcp::TcpSocket; |
| 7 | use embassy_net::{Ipv4Address, StackResources}; | 7 | use embassy_net::{Ipv4Address, StackResources}; |
| 8 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; | 8 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma}; |
| 9 | use embassy_stm32::peripherals::ETH; | 9 | use embassy_stm32::peripherals::{ETH, ETH_SMA}; |
| 10 | use embassy_stm32::rng::Rng; | 10 | use embassy_stm32::rng::Rng; |
| 11 | use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; | 11 | use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; |
| 12 | use embassy_time::Timer; | 12 | use embassy_time::Timer; |
| @@ -19,7 +19,7 @@ bind_interrupts!(struct Irqs { | |||
| 19 | RNG => rng::InterruptHandler<peripherals::RNG>; | 19 | RNG => rng::InterruptHandler<peripherals::RNG>; |
| 20 | }); | 20 | }); |
| 21 | 21 | ||
| 22 | type Device = Ethernet<'static, ETH, GenericPhy>; | 22 | type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>; |
| 23 | 23 | ||
| 24 | #[embassy_executor::task] | 24 | #[embassy_executor::task] |
| 25 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { | 25 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { |
| @@ -69,16 +69,16 @@ async fn main(spawner: Spawner) -> ! { | |||
| 69 | p.ETH, | 69 | p.ETH, |
| 70 | Irqs, | 70 | Irqs, |
| 71 | p.PA1, // ref_clk | 71 | p.PA1, // ref_clk |
| 72 | p.PA2, // mdio | ||
| 73 | p.PC1, // eth_mdc | ||
| 74 | p.PA7, // CRS_DV: Carrier Sense | 72 | p.PA7, // CRS_DV: Carrier Sense |
| 75 | p.PC4, // RX_D0: Received Bit 0 | 73 | p.PC4, // RX_D0: Received Bit 0 |
| 76 | p.PC5, // RX_D1: Received Bit 1 | 74 | p.PC5, // RX_D1: Received Bit 1 |
| 77 | p.PG13, // TX_D0: Transmit Bit 0 | 75 | p.PG13, // TX_D0: Transmit Bit 0 |
| 78 | p.PB13, // TX_D1: Transmit Bit 1 | 76 | p.PB13, // TX_D1: Transmit Bit 1 |
| 79 | p.PG11, // TX_EN: Transmit Enable | 77 | p.PG11, // TX_EN: Transmit Enable |
| 80 | GenericPhy::new_auto(), | ||
| 81 | mac_addr, | 78 | mac_addr, |
| 79 | p.ETH_SMA, | ||
| 80 | p.PA2, // mdio | ||
| 81 | p.PC1, // mdc | ||
| 82 | ); | 82 | ); |
| 83 | 83 | ||
| 84 | let config = embassy_net::Config::dhcpv4(Default::default()); | 84 | let config = embassy_net::Config::dhcpv4(Default::default()); |
diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index fed8f1a9c..189c99686 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs | |||
| @@ -7,8 +7,8 @@ use defmt::*; | |||
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 8 | use embassy_net::StackResources; | 8 | use embassy_net::StackResources; |
| 9 | use embassy_net::tcp::client::{TcpClient, TcpClientState}; | 9 | use embassy_net::tcp::client::{TcpClient, TcpClientState}; |
| 10 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; | 10 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma}; |
| 11 | use embassy_stm32::peripherals::ETH; | 11 | use embassy_stm32::peripherals::{ETH, ETH_SMA}; |
| 12 | use embassy_stm32::rng::Rng; | 12 | use embassy_stm32::rng::Rng; |
| 13 | use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; | 13 | use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; |
| 14 | use embassy_time::Timer; | 14 | use embassy_time::Timer; |
| @@ -22,7 +22,7 @@ bind_interrupts!(struct Irqs { | |||
| 22 | RNG => rng::InterruptHandler<peripherals::RNG>; | 22 | RNG => rng::InterruptHandler<peripherals::RNG>; |
| 23 | }); | 23 | }); |
| 24 | 24 | ||
| 25 | type Device = Ethernet<'static, ETH, GenericPhy>; | 25 | type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>; |
| 26 | 26 | ||
| 27 | #[embassy_executor::task] | 27 | #[embassy_executor::task] |
| 28 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { | 28 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { |
| @@ -71,16 +71,16 @@ async fn main(spawner: Spawner) -> ! { | |||
| 71 | p.ETH, | 71 | p.ETH, |
| 72 | Irqs, | 72 | Irqs, |
| 73 | p.PA1, | 73 | p.PA1, |
| 74 | p.PA2, | ||
| 75 | p.PC1, | ||
| 76 | p.PA7, | 74 | p.PA7, |
| 77 | p.PC4, | 75 | p.PC4, |
| 78 | p.PC5, | 76 | p.PC5, |
| 79 | p.PG13, | 77 | p.PG13, |
| 80 | p.PB13, | 78 | p.PB13, |
| 81 | p.PG11, | 79 | p.PG11, |
| 82 | GenericPhy::new_auto(), | ||
| 83 | mac_addr, | 80 | mac_addr, |
| 81 | p.ETH_SMA, | ||
| 82 | p.PA2, | ||
| 83 | p.PC1, | ||
| 84 | ); | 84 | ); |
| 85 | 85 | ||
| 86 | let config = embassy_net::Config::dhcpv4(Default::default()); | 86 | let config = embassy_net::Config::dhcpv4(Default::default()); |
diff --git a/examples/stm32h7/src/bin/eth_client_mii.rs b/examples/stm32h7/src/bin/eth_client_mii.rs index c3c631f0f..92c823567 100644 --- a/examples/stm32h7/src/bin/eth_client_mii.rs +++ b/examples/stm32h7/src/bin/eth_client_mii.rs | |||
| @@ -7,8 +7,8 @@ use defmt::*; | |||
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 8 | use embassy_net::StackResources; | 8 | use embassy_net::StackResources; |
| 9 | use embassy_net::tcp::client::{TcpClient, TcpClientState}; | 9 | use embassy_net::tcp::client::{TcpClient, TcpClientState}; |
| 10 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; | 10 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma}; |
| 11 | use embassy_stm32::peripherals::ETH; | 11 | use embassy_stm32::peripherals::{ETH, ETH_SMA}; |
| 12 | use embassy_stm32::rng::Rng; | 12 | use embassy_stm32::rng::Rng; |
| 13 | use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; | 13 | use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; |
| 14 | use embassy_time::Timer; | 14 | use embassy_time::Timer; |
| @@ -22,7 +22,7 @@ bind_interrupts!(struct Irqs { | |||
| 22 | RNG => rng::InterruptHandler<peripherals::RNG>; | 22 | RNG => rng::InterruptHandler<peripherals::RNG>; |
| 23 | }); | 23 | }); |
| 24 | 24 | ||
| 25 | type Device = Ethernet<'static, ETH, GenericPhy>; | 25 | type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>; |
| 26 | 26 | ||
| 27 | #[embassy_executor::task] | 27 | #[embassy_executor::task] |
| 28 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { | 28 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { |
| @@ -72,8 +72,6 @@ async fn main(spawner: Spawner) -> ! { | |||
| 72 | Irqs, | 72 | Irqs, |
| 73 | p.PA1, | 73 | p.PA1, |
| 74 | p.PC3, | 74 | p.PC3, |
| 75 | p.PA2, | ||
| 76 | p.PC1, | ||
| 77 | p.PA7, | 75 | p.PA7, |
| 78 | p.PC4, | 76 | p.PC4, |
| 79 | p.PC5, | 77 | p.PC5, |
| @@ -84,8 +82,10 @@ async fn main(spawner: Spawner) -> ! { | |||
| 84 | p.PC2, | 82 | p.PC2, |
| 85 | p.PE2, | 83 | p.PE2, |
| 86 | p.PG11, | 84 | p.PG11, |
| 87 | GenericPhy::new_auto(), | ||
| 88 | mac_addr, | 85 | mac_addr, |
| 86 | p.ETH_SMA, | ||
| 87 | p.PA2, | ||
| 88 | p.PC1, | ||
| 89 | ); | 89 | ); |
| 90 | info!("Device created"); | 90 | info!("Device created"); |
| 91 | 91 | ||
diff --git a/examples/stm32h7/src/bin/flash_async.rs b/examples/stm32h7/src/bin/flash_async.rs new file mode 100644 index 000000000..96d1936f3 --- /dev/null +++ b/examples/stm32h7/src/bin/flash_async.rs | |||
| @@ -0,0 +1,84 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::{info, unwrap}; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::flash::{Flash, InterruptHandler}; | ||
| 7 | use embassy_stm32::gpio::{AnyPin, Level, Output, Speed}; | ||
| 8 | use embassy_stm32::{Peri, bind_interrupts}; | ||
| 9 | use embassy_time::Timer; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | bind_interrupts!(struct Irqs { | ||
| 13 | FLASH => InterruptHandler; | ||
| 14 | }); | ||
| 15 | |||
| 16 | #[embassy_executor::main] | ||
| 17 | async fn main(spawner: Spawner) { | ||
| 18 | let p = embassy_stm32::init(Default::default()); | ||
| 19 | info!("Hello Flash!"); | ||
| 20 | |||
| 21 | let mut f = Flash::new(p.FLASH, Irqs); | ||
| 22 | |||
| 23 | // Led should blink uninterrupted during ~2sec erase operation | ||
| 24 | spawner.spawn(blinky(p.PB14.into()).unwrap()); | ||
| 25 | |||
| 26 | // Test on bank 2 in order not to stall CPU. | ||
| 27 | test_flash(&mut f, 1024 * 1024, 128 * 1024).await; | ||
| 28 | } | ||
| 29 | |||
| 30 | #[embassy_executor::task] | ||
| 31 | async fn blinky(p: Peri<'static, AnyPin>) { | ||
| 32 | let mut led = Output::new(p, Level::High, Speed::Low); | ||
| 33 | |||
| 34 | loop { | ||
| 35 | info!("high"); | ||
| 36 | led.set_high(); | ||
| 37 | Timer::after_millis(300).await; | ||
| 38 | |||
| 39 | info!("low"); | ||
| 40 | led.set_low(); | ||
| 41 | Timer::after_millis(300).await; | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | async fn test_flash<'a>(f: &mut Flash<'a>, offset: u32, size: u32) { | ||
| 46 | info!("Testing offset: {=u32:#X}, size: {=u32:#X}", offset, size); | ||
| 47 | |||
| 48 | info!("Reading..."); | ||
| 49 | let mut buf = [0u8; 32]; | ||
| 50 | unwrap!(f.blocking_read(offset, &mut buf)); | ||
| 51 | info!("Read: {=[u8]:x}", buf); | ||
| 52 | |||
| 53 | info!("Erasing..."); | ||
| 54 | unwrap!(f.erase(offset, offset + size).await); | ||
| 55 | |||
| 56 | info!("Reading..."); | ||
| 57 | let mut buf = [0u8; 32]; | ||
| 58 | unwrap!(f.blocking_read(offset, &mut buf)); | ||
| 59 | info!("Read after erase: {=[u8]:x}", buf); | ||
| 60 | |||
| 61 | info!("Writing..."); | ||
| 62 | unwrap!( | ||
| 63 | f.write( | ||
| 64 | offset, | ||
| 65 | &[ | ||
| 66 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, | ||
| 67 | 29, 30, 31, 32 | ||
| 68 | ] | ||
| 69 | ) | ||
| 70 | .await | ||
| 71 | ); | ||
| 72 | |||
| 73 | info!("Reading..."); | ||
| 74 | let mut buf = [0u8; 32]; | ||
| 75 | unwrap!(f.blocking_read(offset, &mut buf)); | ||
| 76 | info!("Read: {=[u8]:x}", buf); | ||
| 77 | assert_eq!( | ||
| 78 | &buf[..], | ||
| 79 | &[ | ||
| 80 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, | ||
| 81 | 30, 31, 32 | ||
| 82 | ] | ||
| 83 | ); | ||
| 84 | } | ||
diff --git a/examples/stm32h723/src/bin/spdifrx.rs b/examples/stm32h723/src/bin/spdifrx.rs index cdbd69b89..5c29602c6 100644 --- a/examples/stm32h723/src/bin/spdifrx.rs +++ b/examples/stm32h723/src/bin/spdifrx.rs | |||
| @@ -167,7 +167,7 @@ fn new_sai_transmitter<'d>( | |||
| 167 | sai_config.slot_count = hal::sai::word::U4(CHANNEL_COUNT as u8); | 167 | sai_config.slot_count = hal::sai::word::U4(CHANNEL_COUNT as u8); |
| 168 | sai_config.slot_enable = 0xFFFF; // All slots | 168 | sai_config.slot_enable = 0xFFFF; // All slots |
| 169 | sai_config.data_size = sai::DataSize::Data32; | 169 | sai_config.data_size = sai::DataSize::Data32; |
| 170 | sai_config.frame_length = (CHANNEL_COUNT * 32) as u8; | 170 | sai_config.frame_length = (CHANNEL_COUNT * 32) as u16; |
| 171 | sai_config.master_clock_divider = None; | 171 | sai_config.master_clock_divider = None; |
| 172 | 172 | ||
| 173 | let (sub_block_tx, _) = hal::sai::split_subblocks(sai); | 173 | let (sub_block_tx, _) = hal::sai::split_subblocks(sai); |
diff --git a/examples/stm32h7rs/src/bin/button_exti.rs b/examples/stm32h7rs/src/bin/button_exti.rs index 34a08bbc6..d63290d42 100644 --- a/examples/stm32h7rs/src/bin/button_exti.rs +++ b/examples/stm32h7rs/src/bin/button_exti.rs | |||
| @@ -3,16 +3,22 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::exti::ExtiInput; | 6 | use embassy_stm32::exti::{self, ExtiInput}; |
| 7 | use embassy_stm32::gpio::Pull; | 7 | use embassy_stm32::gpio::Pull; |
| 8 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 11 | bind_interrupts!( | ||
| 12 | pub struct Irqs{ | ||
| 13 | EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>; | ||
| 14 | }); | ||
| 15 | |||
| 10 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_stm32::init(Default::default()); | 18 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | 19 | info!("Hello World!"); |
| 14 | 20 | ||
| 15 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); | 21 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); |
| 16 | 22 | ||
| 17 | info!("Press the USER button..."); | 23 | info!("Press the USER button..."); |
| 18 | 24 | ||
diff --git a/examples/stm32h7rs/src/bin/eth.rs b/examples/stm32h7rs/src/bin/eth.rs index 5ce1d4765..8e07d0a67 100644 --- a/examples/stm32h7rs/src/bin/eth.rs +++ b/examples/stm32h7rs/src/bin/eth.rs | |||
| @@ -5,8 +5,8 @@ use defmt::*; | |||
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_net::udp::{PacketMetadata, UdpSocket}; | 6 | use embassy_net::udp::{PacketMetadata, UdpSocket}; |
| 7 | use embassy_net::{Ipv4Address, Ipv4Cidr, StackResources}; | 7 | use embassy_net::{Ipv4Address, Ipv4Cidr, StackResources}; |
| 8 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; | 8 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma}; |
| 9 | use embassy_stm32::peripherals::ETH; | 9 | use embassy_stm32::peripherals::{ETH, ETH_SMA}; |
| 10 | use embassy_stm32::rng::Rng; | 10 | use embassy_stm32::rng::Rng; |
| 11 | use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; | 11 | use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; |
| 12 | use embassy_time::Timer; | 12 | use embassy_time::Timer; |
| @@ -19,7 +19,7 @@ bind_interrupts!(struct Irqs { | |||
| 19 | RNG => rng::InterruptHandler<peripherals::RNG>; | 19 | RNG => rng::InterruptHandler<peripherals::RNG>; |
| 20 | }); | 20 | }); |
| 21 | 21 | ||
| 22 | type Device = Ethernet<'static, ETH, GenericPhy>; | 22 | type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>; |
| 23 | 23 | ||
| 24 | #[embassy_executor::task] | 24 | #[embassy_executor::task] |
| 25 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { | 25 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { |
| @@ -69,16 +69,16 @@ async fn main(spawner: Spawner) -> ! { | |||
| 69 | p.ETH, | 69 | p.ETH, |
| 70 | Irqs, | 70 | Irqs, |
| 71 | p.PB6, | 71 | p.PB6, |
| 72 | p.PA2, | ||
| 73 | p.PG6, | ||
| 74 | p.PA7, | 72 | p.PA7, |
| 75 | p.PG4, | 73 | p.PG4, |
| 76 | p.PG5, | 74 | p.PG5, |
| 77 | p.PG13, | 75 | p.PG13, |
| 78 | p.PG12, | 76 | p.PG12, |
| 79 | p.PG11, | 77 | p.PG11, |
| 80 | GenericPhy::new(0), | ||
| 81 | mac_addr, | 78 | mac_addr, |
| 79 | p.ETH_SMA, | ||
| 80 | p.PA2, | ||
| 81 | p.PG6, | ||
| 82 | ); | 82 | ); |
| 83 | 83 | ||
| 84 | // Have to use UDP w/ static config to fit in internal flash | 84 | // Have to use UDP w/ static config to fit in internal flash |
diff --git a/examples/stm32l0/src/bin/button_exti.rs b/examples/stm32l0/src/bin/button_exti.rs index 7ff4a7d52..a118c7a5a 100644 --- a/examples/stm32l0/src/bin/button_exti.rs +++ b/examples/stm32l0/src/bin/button_exti.rs | |||
| @@ -3,17 +3,22 @@ | |||
| 3 | 3 | ||
| 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::exti::{self, ExtiInput}; |
| 7 | use embassy_stm32::exti::ExtiInput; | ||
| 8 | use embassy_stm32::gpio::Pull; | 7 | use embassy_stm32::gpio::Pull; |
| 8 | use embassy_stm32::{Config, bind_interrupts, interrupt}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 10 | ||
| 11 | bind_interrupts!( | ||
| 12 | pub struct Irqs{ | ||
| 13 | EXTI2_3 => exti::InterruptHandler<interrupt::typelevel::EXTI2_3>; | ||
| 14 | }); | ||
| 15 | |||
| 11 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 12 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 13 | let config = Config::default(); | 18 | let config = Config::default(); |
| 14 | let p = embassy_stm32::init(config); | 19 | let p = embassy_stm32::init(config); |
| 15 | 20 | ||
| 16 | let mut button = ExtiInput::new(p.PB2, p.EXTI2, Pull::Up); | 21 | let mut button = ExtiInput::new(p.PB2, p.EXTI2, Pull::Up, Irqs); |
| 17 | 22 | ||
| 18 | info!("Press the USER button..."); | 23 | info!("Press the USER button..."); |
| 19 | 24 | ||
diff --git a/examples/stm32l4/src/bin/adc.rs b/examples/stm32l4/src/bin/adc.rs index 835bf5411..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, SampleTime}; | 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,9 +17,12 @@ 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 { |
diff --git a/examples/stm32l4/src/bin/adc_dma.rs b/examples/stm32l4/src/bin/adc_dma.rs index ab1e9d2e9..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,7 +20,7 @@ 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 adc_pin0 = p.PA0.degrade_adc(); | 24 | let adc_pin0 = p.PA0.degrade_adc(); |
| 25 | let 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]; |
| @@ -29,6 +29,7 @@ async fn main(_spawner: Spawner) { | |||
| 29 | p.DMA1_CH1, | 29 | p.DMA1_CH1, |
| 30 | &mut adc_dma_buf, | 30 | &mut adc_dma_buf, |
| 31 | [(adc_pin0, SampleTime::CYCLES640_5), (adc_pin1, SampleTime::CYCLES640_5)].into_iter(), | 31 | [(adc_pin0, SampleTime::CYCLES640_5), (adc_pin1, SampleTime::CYCLES640_5)].into_iter(), |
| 32 | RegularConversionMode::Continuous, | ||
| 32 | ); | 33 | ); |
| 33 | 34 | ||
| 34 | info!("starting measurement loop"); | 35 | info!("starting measurement loop"); |
diff --git a/examples/stm32l4/src/bin/button_exti.rs b/examples/stm32l4/src/bin/button_exti.rs index 34a08bbc6..c84b11dab 100644 --- a/examples/stm32l4/src/bin/button_exti.rs +++ b/examples/stm32l4/src/bin/button_exti.rs | |||
| @@ -3,16 +3,22 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::exti::ExtiInput; | 6 | use embassy_stm32::exti::{self, ExtiInput}; |
| 7 | use embassy_stm32::gpio::Pull; | 7 | use embassy_stm32::gpio::Pull; |
| 8 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 11 | bind_interrupts!( | ||
| 12 | pub struct Irqs{ | ||
| 13 | EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>; | ||
| 14 | }); | ||
| 15 | |||
| 10 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_stm32::init(Default::default()); | 18 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | 19 | info!("Hello World!"); |
| 14 | 20 | ||
| 15 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); | 21 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); |
| 16 | 22 | ||
| 17 | info!("Press the USER button..."); | 23 | info!("Press the USER button..."); |
| 18 | 24 | ||
diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index 0dbf515cf..8f2510cdc 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs | |||
| @@ -24,6 +24,7 @@ use embassy_futures::yield_now; | |||
| 24 | use embassy_net::tcp::TcpSocket; | 24 | use embassy_net::tcp::TcpSocket; |
| 25 | use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources, StaticConfigV4}; | 25 | use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources, StaticConfigV4}; |
| 26 | use embassy_net_adin1110::{ADIN1110, Device, Runner}; | 26 | use embassy_net_adin1110::{ADIN1110, Device, Runner}; |
| 27 | use embassy_stm32::exti::ExtiInput; | ||
| 27 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; | 28 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; |
| 28 | use embassy_stm32::i2c::{self, Config as I2C_Config, I2c}; | 29 | use embassy_stm32::i2c::{self, Config as I2C_Config, I2c}; |
| 29 | use embassy_stm32::mode::Async; | 30 | use embassy_stm32::mode::Async; |
| @@ -31,7 +32,7 @@ use embassy_stm32::rng::{self, Rng}; | |||
| 31 | use embassy_stm32::spi::mode::Master; | 32 | use embassy_stm32::spi::mode::Master; |
| 32 | use embassy_stm32::spi::{Config as SPI_Config, Spi}; | 33 | use embassy_stm32::spi::{Config as SPI_Config, Spi}; |
| 33 | use embassy_stm32::time::Hertz; | 34 | use embassy_stm32::time::Hertz; |
| 34 | use embassy_stm32::{bind_interrupts, exti, pac, peripherals}; | 35 | use embassy_stm32::{bind_interrupts, exti, interrupt, pac, peripherals}; |
| 35 | use embassy_time::{Delay, Duration, Ticker, Timer}; | 36 | use embassy_time::{Delay, Duration, Ticker, Timer}; |
| 36 | use embedded_hal_async::i2c::I2c as I2cBus; | 37 | use embedded_hal_async::i2c::I2c as I2cBus; |
| 37 | use embedded_hal_bus::spi::ExclusiveDevice; | 38 | use embedded_hal_bus::spi::ExclusiveDevice; |
| @@ -45,6 +46,7 @@ bind_interrupts!(struct Irqs { | |||
| 45 | I2C3_EV => i2c::EventInterruptHandler<peripherals::I2C3>; | 46 | I2C3_EV => i2c::EventInterruptHandler<peripherals::I2C3>; |
| 46 | I2C3_ER => i2c::ErrorInterruptHandler<peripherals::I2C3>; | 47 | I2C3_ER => i2c::ErrorInterruptHandler<peripherals::I2C3>; |
| 47 | RNG => rng::InterruptHandler<peripherals::RNG>; | 48 | RNG => rng::InterruptHandler<peripherals::RNG>; |
| 49 | EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>; | ||
| 48 | }); | 50 | }); |
| 49 | 51 | ||
| 50 | // Basic settings | 52 | // Basic settings |
| @@ -125,7 +127,7 @@ async fn main(spawner: Spawner) { | |||
| 125 | let spe_cfg1 = Input::new(dp.PC9, Pull::None); | 127 | let spe_cfg1 = Input::new(dp.PC9, Pull::None); |
| 126 | let _spe_ts_capt = Output::new(dp.PC6, Level::Low, Speed::Low); | 128 | let _spe_ts_capt = Output::new(dp.PC6, Level::Low, Speed::Low); |
| 127 | 129 | ||
| 128 | let spe_int = exti::ExtiInput::new(dp.PB11, dp.EXTI11, Pull::None); | 130 | let spe_int = ExtiInput::new(dp.PB11, dp.EXTI11, Pull::None, Irqs); |
| 129 | 131 | ||
| 130 | let spe_spi_cs_n = Output::new(dp.PB12, Level::High, Speed::High); | 132 | let spe_spi_cs_n = Output::new(dp.PB12, Level::High, Speed::High); |
| 131 | let spe_spi_sclk = dp.PB13; | 133 | let spe_spi_sclk = dp.PB13; |
diff --git a/examples/stm32l4/src/bin/spi_dma.rs b/examples/stm32l4/src/bin/spi_dma.rs index 946a759b1..970a0c608 100644 --- a/examples/stm32l4/src/bin/spi_dma.rs +++ b/examples/stm32l4/src/bin/spi_dma.rs | |||
| @@ -34,8 +34,8 @@ async fn main(_spawner: Spawner) { | |||
| 34 | info!("waiting for ready"); | 34 | info!("waiting for ready"); |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | let write = [0x0A; 10]; | 37 | let write = [0x0Au8; 10]; |
| 38 | let mut read = [0; 10]; | 38 | let mut read = [0u8; 10]; |
| 39 | cs.set_low(); | 39 | cs.set_low(); |
| 40 | spi.transfer(&mut read, &write).await.ok(); | 40 | spi.transfer(&mut read, &write).await.ok(); |
| 41 | cs.set_high(); | 41 | cs.set_high(); |
diff --git a/examples/stm32l5/src/bin/button_exti.rs b/examples/stm32l5/src/bin/button_exti.rs index e6639d22b..225a7b3fd 100644 --- a/examples/stm32l5/src/bin/button_exti.rs +++ b/examples/stm32l5/src/bin/button_exti.rs | |||
| @@ -3,16 +3,22 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::exti::ExtiInput; | 6 | use embassy_stm32::exti::{self, ExtiInput}; |
| 7 | use embassy_stm32::gpio::Pull; | 7 | use embassy_stm32::gpio::Pull; |
| 8 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 11 | bind_interrupts!( | ||
| 12 | pub struct Irqs{ | ||
| 13 | EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>; | ||
| 14 | }); | ||
| 15 | |||
| 10 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_stm32::init(Default::default()); | 18 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | 19 | info!("Hello World!"); |
| 14 | 20 | ||
| 15 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); | 21 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); |
| 16 | 22 | ||
| 17 | info!("Press the USER button..."); | 23 | info!("Press the USER button..."); |
| 18 | 24 | ||
diff --git a/examples/stm32l5/src/bin/stop.rs b/examples/stm32l5/src/bin/stop.rs index 3d119f90f..fde804fb7 100644 --- a/examples/stm32l5/src/bin/stop.rs +++ b/examples/stm32l5/src/bin/stop.rs | |||
| @@ -4,20 +4,12 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::gpio::{AnyPin, Level, Output, Speed}; | 6 | use embassy_stm32::gpio::{AnyPin, Level, Output, Speed}; |
| 7 | use embassy_stm32::low_power::Executor; | ||
| 8 | use embassy_stm32::rcc::LsConfig; | 7 | use embassy_stm32::rcc::LsConfig; |
| 9 | use embassy_stm32::{Config, Peri}; | 8 | use embassy_stm32::{Config, Peri, low_power}; |
| 10 | use embassy_time::Timer; | 9 | use embassy_time::Timer; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 11 | ||
| 13 | #[cortex_m_rt::entry] | 12 | #[embassy_executor::main(executor = "low_power::Executor")] |
| 14 | fn main() -> ! { | ||
| 15 | Executor::take().run(|spawner| { | ||
| 16 | spawner.spawn(unwrap!(async_main(spawner))); | ||
| 17 | }) | ||
| 18 | } | ||
| 19 | |||
| 20 | #[embassy_executor::task] | ||
| 21 | async fn async_main(spawner: Spawner) { | 13 | async fn async_main(spawner: Spawner) { |
| 22 | let mut config = Config::default(); | 14 | let mut config = Config::default(); |
| 23 | config.rcc.ls = LsConfig::default_lsi(); | 15 | config.rcc.ls = LsConfig::default_lsi(); |
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/src/bin/blinky.rs b/examples/stm32n6/src/bin/blinky.rs index 018967f08..a8baf16af 100644 --- a/examples/stm32n6/src/bin/blinky.rs +++ b/examples/stm32n6/src/bin/blinky.rs | |||
| @@ -3,11 +3,17 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::exti::ExtiInput; | 6 | use embassy_stm32::exti::{self, ExtiInput}; |
| 7 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | 7 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; |
| 8 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 8 | use embassy_time::Timer; | 9 | use embassy_time::Timer; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 11 | ||
| 12 | bind_interrupts!( | ||
| 13 | pub struct Irqs{ | ||
| 14 | EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>; | ||
| 15 | }); | ||
| 16 | |||
| 11 | #[embassy_executor::task] | 17 | #[embassy_executor::task] |
| 12 | async fn button_task(mut p: ExtiInput<'static>) { | 18 | async fn button_task(mut p: ExtiInput<'static>) { |
| 13 | loop { | 19 | loop { |
| @@ -22,7 +28,7 @@ async fn main(spawner: Spawner) { | |||
| 22 | info!("Hello World!"); | 28 | info!("Hello World!"); |
| 23 | 29 | ||
| 24 | let mut led = Output::new(p.PG10, Level::High, Speed::Low); | 30 | let mut led = Output::new(p.PG10, Level::High, Speed::Low); |
| 25 | let button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); | 31 | let button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); |
| 26 | 32 | ||
| 27 | spawner.spawn(button_task(button).unwrap()); | 33 | spawner.spawn(button_task(button).unwrap()); |
| 28 | 34 | ||
diff --git a/examples/stm32u0/.cargo/config.toml b/examples/stm32u0/.cargo/config.toml index 688347084..e9212cacb 100644 --- a/examples/stm32u0/.cargo/config.toml +++ b/examples/stm32u0/.cargo/config.toml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] |
| 2 | # replace stm32u083rctx with your chip as listed in `probe-rs chip list` | 2 | # replace stm32u083mctx with your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-rs run --chip stm32u083rctx" | 3 | runner = "probe-rs run --chip stm32u083mctx" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv6m-none-eabi" | 6 | target = "thumbv6m-none-eabi" |
diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml index 9f5227e3f..8cc894cb3 100644 --- a/examples/stm32u0/Cargo.toml +++ b/examples/stm32u0/Cargo.toml | |||
| @@ -6,8 +6,8 @@ license = "MIT OR Apache-2.0" | |||
| 6 | publish = false | 6 | publish = false |
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | # Change stm32u083rc to your chip name, if necessary. | 9 | # Change stm32u083mc to your chip name, if necessary. |
| 10 | embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti", "chrono"] } | 10 | embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083mc", "memory-x", "unstable-pac", "exti", "chrono"] } |
| 11 | embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } | 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"] } | 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"] } | 13 | embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| @@ -19,9 +19,7 @@ defmt-rtt = "1.0.0" | |||
| 19 | 19 | ||
| 20 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 21 | cortex-m-rt = "0.7.0" | 21 | cortex-m-rt = "0.7.0" |
| 22 | embedded-hal = "0.2.6" | ||
| 23 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | 22 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 24 | heapless = { version = "0.8", default-features = false } | ||
| 25 | 23 | ||
| 26 | micromath = "2.0.0" | 24 | micromath = "2.0.0" |
| 27 | chrono = { version = "0.4.38", default-features = false } | 25 | chrono = { version = "0.4.38", default-features = false } |
diff --git a/examples/stm32u0/src/bin/adc.rs b/examples/stm32u0/src/bin/adc.rs index 4fbc6f17f..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, SampleTime}; | 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,8 +18,9 @@ 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 { |
diff --git a/examples/stm32u0/src/bin/button_exti.rs b/examples/stm32u0/src/bin/button_exti.rs index 34a08bbc6..9d54479da 100644 --- a/examples/stm32u0/src/bin/button_exti.rs +++ b/examples/stm32u0/src/bin/button_exti.rs | |||
| @@ -3,16 +3,22 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::exti::ExtiInput; | 6 | use embassy_stm32::exti::{self, ExtiInput}; |
| 7 | use embassy_stm32::gpio::Pull; | 7 | use embassy_stm32::gpio::Pull; |
| 8 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 11 | bind_interrupts!( | ||
| 12 | pub struct Irqs{ | ||
| 13 | EXTI4_15 => exti::InterruptHandler<interrupt::typelevel::EXTI4_15>; | ||
| 14 | }); | ||
| 15 | |||
| 10 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_stm32::init(Default::default()); | 18 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | 19 | info!("Hello World!"); |
| 14 | 20 | ||
| 15 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); | 21 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); |
| 16 | 22 | ||
| 17 | info!("Press the USER button..."); | 23 | info!("Press the USER button..."); |
| 18 | 24 | ||
diff --git a/examples/stm32u0/src/bin/lcd.rs b/examples/stm32u0/src/bin/lcd.rs new file mode 100644 index 000000000..2b34d4ef1 --- /dev/null +++ b/examples/stm32u0/src/bin/lcd.rs | |||
| @@ -0,0 +1,412 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::lcd::{Bias, BlinkFreq, BlinkSelector, Config, Duty, Lcd, LcdPin}; | ||
| 7 | use embassy_stm32::peripherals::LCD; | ||
| 8 | use embassy_stm32::time::Hertz; | ||
| 9 | use embassy_time::Duration; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) { | ||
| 14 | let mut config = embassy_stm32::Config::default(); | ||
| 15 | // The RTC clock = the LCD clock and must be running | ||
| 16 | { | ||
| 17 | use embassy_stm32::rcc::*; | ||
| 18 | config.rcc.sys = Sysclk::PLL1_R; | ||
| 19 | config.rcc.hsi = true; | ||
| 20 | config.rcc.pll = Some(Pll { | ||
| 21 | source: PllSource::HSI, // 16 MHz | ||
| 22 | prediv: PllPreDiv::DIV1, | ||
| 23 | mul: PllMul::MUL7, // 16 * 7 = 112 MHz | ||
| 24 | divp: None, | ||
| 25 | divq: None, | ||
| 26 | divr: Some(PllRDiv::DIV2), // 112 / 2 = 56 MHz | ||
| 27 | }); | ||
| 28 | config.rcc.ls = LsConfig::default_lsi(); | ||
| 29 | } | ||
| 30 | |||
| 31 | let p = embassy_stm32::init(config); | ||
| 32 | info!("Hello World!"); | ||
| 33 | |||
| 34 | let mut config = Config::default(); | ||
| 35 | config.bias = Bias::Third; | ||
| 36 | config.duty = Duty::Quarter; | ||
| 37 | config.target_fps = Hertz(100); | ||
| 38 | |||
| 39 | let mut lcd = Lcd::new( | ||
| 40 | p.LCD, | ||
| 41 | config, | ||
| 42 | p.PC3, | ||
| 43 | [ | ||
| 44 | LcdPin::new_com(p.PA8), | ||
| 45 | LcdPin::new_com(p.PA9), | ||
| 46 | LcdPin::new_com(p.PA10), | ||
| 47 | LcdPin::new_seg(p.PB1), | ||
| 48 | LcdPin::new_com(p.PB9), | ||
| 49 | LcdPin::new_seg(p.PB11), | ||
| 50 | LcdPin::new_seg(p.PB14), | ||
| 51 | LcdPin::new_seg(p.PB15), | ||
| 52 | LcdPin::new_seg(p.PC4), | ||
| 53 | LcdPin::new_seg(p.PC5), | ||
| 54 | LcdPin::new_seg(p.PC6), | ||
| 55 | LcdPin::new_seg(p.PC8), | ||
| 56 | LcdPin::new_seg(p.PC9), | ||
| 57 | LcdPin::new_seg(p.PC10), | ||
| 58 | LcdPin::new_seg(p.PC11), | ||
| 59 | LcdPin::new_seg(p.PD8), | ||
| 60 | LcdPin::new_seg(p.PD9), | ||
| 61 | LcdPin::new_seg(p.PD12), | ||
| 62 | LcdPin::new_seg(p.PD13), | ||
| 63 | LcdPin::new_seg(p.PD0), | ||
| 64 | LcdPin::new_seg(p.PD1), | ||
| 65 | LcdPin::new_seg(p.PD3), | ||
| 66 | LcdPin::new_seg(p.PD4), | ||
| 67 | LcdPin::new_seg(p.PD5), | ||
| 68 | LcdPin::new_seg(p.PD6), | ||
| 69 | LcdPin::new_seg(p.PE7), | ||
| 70 | LcdPin::new_seg(p.PE8), | ||
| 71 | LcdPin::new_seg(p.PE9), | ||
| 72 | ], | ||
| 73 | ); | ||
| 74 | |||
| 75 | lcd.set_blink(BlinkSelector::All, BlinkFreq::Hz4); | ||
| 76 | { | ||
| 77 | let mut buffer = DisplayBuffer::new(); | ||
| 78 | for i in 0..4 { | ||
| 79 | buffer.write_colon(i); | ||
| 80 | buffer.write(&mut lcd); | ||
| 81 | embassy_time::Timer::after_millis(200).await; | ||
| 82 | buffer.write_dot(i); | ||
| 83 | buffer.write(&mut lcd); | ||
| 84 | embassy_time::Timer::after_millis(200).await; | ||
| 85 | } | ||
| 86 | for i in 0..4 { | ||
| 87 | buffer.write_bar(i); | ||
| 88 | buffer.write(&mut lcd); | ||
| 89 | embassy_time::Timer::after_millis(200).await; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | embassy_time::Timer::after_millis(1000).await; | ||
| 94 | |||
| 95 | lcd.set_blink(BlinkSelector::None, BlinkFreq::Hz4); | ||
| 96 | |||
| 97 | const MESSAGE: &str = "Hello embassy people. Hope you like this LCD demo :} "; | ||
| 98 | loop { | ||
| 99 | print_message(MESSAGE, &mut lcd, Duration::from_millis(250)).await; | ||
| 100 | print_message(characters::ALL_CHARS, &mut lcd, Duration::from_millis(500)).await; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | async fn print_message(message: &str, lcd: &mut Lcd<'_, LCD>, delay: Duration) { | ||
| 105 | let mut display_buffer = DisplayBuffer::new(); | ||
| 106 | |||
| 107 | let mut char_buffer = [' '; 6]; | ||
| 108 | for char in message.chars() { | ||
| 109 | char_buffer.copy_within(1.., 0); | ||
| 110 | char_buffer[5] = char; | ||
| 111 | |||
| 112 | display_buffer.clear(); | ||
| 113 | for (i, char) in char_buffer.iter().enumerate() { | ||
| 114 | display_buffer.write_char(i, *char); | ||
| 115 | } | ||
| 116 | display_buffer.write(lcd); | ||
| 117 | |||
| 118 | embassy_time::Timer::after(delay).await; | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | /// Display layout for the U0-DK | ||
| 123 | mod display_layout { | ||
| 124 | // Character layout. There are 6 characters, left-to-right | ||
| 125 | // T | ||
| 126 | // ───────── | ||
| 127 | // │ N │ | ||
| 128 | // │ │ │ │ │ | ||
| 129 | // TL │ └┐ │ ┌┘ │ TR | ||
| 130 | // │NW│ │ │NE│ | ||
| 131 | // │ │ │ | ||
| 132 | // W─── ───E | ||
| 133 | // │ │ │ | ||
| 134 | // │SW│ │ │SE│ | ||
| 135 | // BL │ ┌┘ │ └┐ │ BR | ||
| 136 | // │ │ │ │ │ | ||
| 137 | // │ S │ | ||
| 138 | // ───────── | ||
| 139 | // B | ||
| 140 | |||
| 141 | pub const CHAR_N_COM: u8 = 3; | ||
| 142 | pub const CHAR_N_SEG: [u8; 6] = [39, 37, 35, 48, 26, 33]; | ||
| 143 | pub const CHAR_NW_COM: u8 = 3; | ||
| 144 | pub const CHAR_NW_SEG: [u8; 6] = [49, 38, 36, 34, 27, 24]; | ||
| 145 | pub const CHAR_W_COM: u8 = 0; | ||
| 146 | pub const CHAR_W_SEG: [u8; 6] = CHAR_NW_SEG; | ||
| 147 | pub const CHAR_SW_COM: u8 = 2; | ||
| 148 | pub const CHAR_SW_SEG: [u8; 6] = CHAR_NW_SEG; | ||
| 149 | pub const CHAR_S_COM: u8 = 2; | ||
| 150 | pub const CHAR_S_SEG: [u8; 6] = [22, 6, 46, 11, 15, 29]; | ||
| 151 | pub const CHAR_SE_COM: u8 = 3; | ||
| 152 | pub const CHAR_SE_SEG: [u8; 6] = CHAR_S_SEG; | ||
| 153 | pub const CHAR_E_COM: u8 = 0; | ||
| 154 | pub const CHAR_E_SEG: [u8; 6] = [23, 45, 47, 14, 28, 32]; | ||
| 155 | pub const CHAR_NE_COM: u8 = 2; | ||
| 156 | pub const CHAR_NE_SEG: [u8; 6] = CHAR_N_SEG; | ||
| 157 | pub const CHAR_T_COM: u8 = 1; | ||
| 158 | pub const CHAR_T_SEG: [u8; 6] = CHAR_N_SEG; | ||
| 159 | pub const CHAR_TL_COM: u8 = 1; | ||
| 160 | pub const CHAR_TL_SEG: [u8; 6] = CHAR_NW_SEG; | ||
| 161 | pub const CHAR_BL_COM: u8 = 0; | ||
| 162 | pub const CHAR_BL_SEG: [u8; 6] = CHAR_S_SEG; | ||
| 163 | pub const CHAR_B_COM: u8 = 1; | ||
| 164 | pub const CHAR_B_SEG: [u8; 6] = CHAR_S_SEG; | ||
| 165 | pub const CHAR_BR_COM: u8 = 1; | ||
| 166 | pub const CHAR_BR_SEG: [u8; 6] = CHAR_E_SEG; | ||
| 167 | pub const CHAR_TR_COM: u8 = 0; | ||
| 168 | pub const CHAR_TR_SEG: [u8; 6] = CHAR_N_SEG; | ||
| 169 | |||
| 170 | pub const COLON_COM: u8 = 2; | ||
| 171 | pub const COLON_SEG: [u8; 4] = [23, 45, 47, 14]; | ||
| 172 | pub const DOT_COM: u8 = 3; | ||
| 173 | pub const DOT_SEG: [u8; 4] = COLON_SEG; | ||
| 174 | /// COM + SEG, bar from top to bottom | ||
| 175 | pub const BAR: [(u8, u8); 4] = [(2, 28), (3, 28), (2, 32), (3, 32)]; | ||
| 176 | } | ||
| 177 | |||
| 178 | mod characters { | ||
| 179 | use super::CharSegment::{self, *}; | ||
| 180 | |||
| 181 | pub const CHAR_0: &[CharSegment] = &[T, TL, BL, B, BR, TR, NW, SE]; | ||
| 182 | pub const CHAR_1: &[CharSegment] = &[NE, TR, BR]; | ||
| 183 | pub const CHAR_2: &[CharSegment] = &[T, BL, B, TR, E, W]; | ||
| 184 | pub const CHAR_3: &[CharSegment] = &[T, B, BR, TR, E]; | ||
| 185 | pub const CHAR_4: &[CharSegment] = &[TL, BR, TR, E, W]; | ||
| 186 | pub const CHAR_5: &[CharSegment] = &[T, TL, B, BR, E, W]; | ||
| 187 | pub const CHAR_6: &[CharSegment] = &[T, TL, BL, B, BR, E, W]; | ||
| 188 | pub const CHAR_7: &[CharSegment] = &[T, NE, S]; | ||
| 189 | pub const CHAR_8: &[CharSegment] = &[T, TL, BL, B, BR, TR, E, W]; | ||
| 190 | pub const CHAR_9: &[CharSegment] = &[T, TL, BR, TR, E, W]; | ||
| 191 | |||
| 192 | pub const CHAR_COLON: &[CharSegment] = &[N, S]; | ||
| 193 | pub const CHAR_SEMICOLON: &[CharSegment] = &[N, SW]; | ||
| 194 | pub const CHAR_EQUALS: &[CharSegment] = &[E, W, B]; | ||
| 195 | pub const CHAR_SLASH: &[CharSegment] = &[SW, NE]; | ||
| 196 | pub const CHAR_BACKSLASH: &[CharSegment] = &[SE, NW]; | ||
| 197 | pub const CHAR_PLUS: &[CharSegment] = &[N, E, S, W]; | ||
| 198 | pub const CHAR_STAR: &[CharSegment] = &[NE, N, NW, SE, S, SW]; | ||
| 199 | pub const CHAR_QUOTE: &[CharSegment] = &[N]; | ||
| 200 | pub const CHAR_BACKTICK: &[CharSegment] = &[NW]; | ||
| 201 | pub const CHAR_DASH: &[CharSegment] = &[W, E]; | ||
| 202 | pub const CHAR_COMMA: &[CharSegment] = &[SW]; | ||
| 203 | pub const CHAR_DOT: &[CharSegment] = &[S]; | ||
| 204 | pub const CHAR_CURLYOPEN: &[CharSegment] = &[T, NW, W, SW, B]; | ||
| 205 | pub const CHAR_CURLYCLOSE: &[CharSegment] = &[T, NE, E, SE, B]; | ||
| 206 | pub const CHAR_AMPERSAND: &[CharSegment] = &[T, NE, NW, W, BL, B, SE]; | ||
| 207 | |||
| 208 | pub const CHAR_A: &[CharSegment] = &[T, TL, TR, E, W, BL, BR]; | ||
| 209 | pub const CHAR_B: &[CharSegment] = &[T, TR, BR, B, N, S, E]; | ||
| 210 | pub const CHAR_C: &[CharSegment] = &[T, TL, BL, B]; | ||
| 211 | pub const CHAR_D: &[CharSegment] = &[T, TR, BR, B, N, S]; | ||
| 212 | pub const CHAR_E: &[CharSegment] = &[T, TL, BL, B, W]; | ||
| 213 | pub const CHAR_F: &[CharSegment] = &[T, TL, BL, W]; | ||
| 214 | pub const CHAR_G: &[CharSegment] = &[T, TL, BL, B, BR, E]; | ||
| 215 | pub const CHAR_H: &[CharSegment] = &[TL, BL, E, W, TR, BR]; | ||
| 216 | pub const CHAR_I: &[CharSegment] = &[T, N, S, B]; | ||
| 217 | pub const CHAR_J: &[CharSegment] = &[TR, BR, B, BL]; | ||
| 218 | pub const CHAR_K: &[CharSegment] = &[TL, BL, W, NE, SE]; | ||
| 219 | pub const CHAR_L: &[CharSegment] = &[TL, BL, B]; | ||
| 220 | pub const CHAR_M: &[CharSegment] = &[BL, TL, NW, NE, TR, BR]; | ||
| 221 | pub const CHAR_N: &[CharSegment] = &[BL, TL, NW, SE, BR, TR]; | ||
| 222 | pub const CHAR_O: &[CharSegment] = &[T, TL, BL, B, BR, TR]; | ||
| 223 | pub const CHAR_P: &[CharSegment] = &[BL, TL, T, TR, E, W]; | ||
| 224 | pub const CHAR_Q: &[CharSegment] = &[T, TL, BL, B, BR, TR, SE]; | ||
| 225 | pub const CHAR_R: &[CharSegment] = &[BL, TL, T, TR, E, W, SE]; | ||
| 226 | pub const CHAR_S: &[CharSegment] = &[T, NW, E, BR, B]; | ||
| 227 | pub const CHAR_T: &[CharSegment] = &[T, N, S]; | ||
| 228 | pub const CHAR_U: &[CharSegment] = &[TL, BL, B, BR, TR]; | ||
| 229 | pub const CHAR_V: &[CharSegment] = &[TL, BL, SW, NE]; | ||
| 230 | pub const CHAR_W: &[CharSegment] = &[TL, BL, SW, SE, BR, TR]; | ||
| 231 | pub const CHAR_X: &[CharSegment] = &[NE, NW, SE, SW]; | ||
| 232 | pub const CHAR_Y: &[CharSegment] = &[NE, NW, S]; | ||
| 233 | pub const CHAR_Z: &[CharSegment] = &[T, NE, SW, B]; | ||
| 234 | |||
| 235 | pub const CHAR_UNKNOWN: &[CharSegment] = &[N, NW, W, SW, S, SE, E, NE, T, TL, BL, B, BR, TR]; | ||
| 236 | |||
| 237 | pub const ALL_CHARS: &str = | ||
| 238 | "0 1 2 3 4 5 6 7 8 9 : ; = / \\ + * ' ` - , . { } & A B C D E F G H I J K L M N O P Q R S T U V W X Y Z � "; | ||
| 239 | |||
| 240 | pub fn get_char_segments(val: char) -> &'static [CharSegment] { | ||
| 241 | match val { | ||
| 242 | val if val.is_whitespace() => &[], | ||
| 243 | |||
| 244 | '0' => CHAR_0, | ||
| 245 | '1' => CHAR_1, | ||
| 246 | '2' => CHAR_2, | ||
| 247 | '3' => CHAR_3, | ||
| 248 | '4' => CHAR_4, | ||
| 249 | '5' => CHAR_5, | ||
| 250 | '6' => CHAR_6, | ||
| 251 | '7' => CHAR_7, | ||
| 252 | '8' => CHAR_8, | ||
| 253 | '9' => CHAR_9, | ||
| 254 | |||
| 255 | ':' => CHAR_COLON, | ||
| 256 | ';' => CHAR_SEMICOLON, | ||
| 257 | '=' => CHAR_EQUALS, | ||
| 258 | '/' => CHAR_SLASH, | ||
| 259 | '\\' => CHAR_BACKSLASH, | ||
| 260 | '+' => CHAR_PLUS, | ||
| 261 | '*' => CHAR_STAR, | ||
| 262 | '\'' => CHAR_QUOTE, | ||
| 263 | '`' => CHAR_BACKTICK, | ||
| 264 | '-' => CHAR_DASH, | ||
| 265 | ',' => CHAR_COMMA, | ||
| 266 | '.' => CHAR_DOT, | ||
| 267 | '{' => CHAR_CURLYOPEN, | ||
| 268 | '}' => CHAR_CURLYCLOSE, | ||
| 269 | '&' => CHAR_AMPERSAND, | ||
| 270 | |||
| 271 | 'A' | 'a' => CHAR_A, | ||
| 272 | 'B' | 'b' => CHAR_B, | ||
| 273 | 'C' | 'c' => CHAR_C, | ||
| 274 | 'D' | 'd' => CHAR_D, | ||
| 275 | 'E' | 'e' => CHAR_E, | ||
| 276 | 'F' | 'f' => CHAR_F, | ||
| 277 | 'G' | 'g' => CHAR_G, | ||
| 278 | 'H' | 'h' => CHAR_H, | ||
| 279 | 'I' | 'i' => CHAR_I, | ||
| 280 | 'J' | 'j' => CHAR_J, | ||
| 281 | 'K' | 'k' => CHAR_K, | ||
| 282 | 'L' | 'l' => CHAR_L, | ||
| 283 | 'M' | 'm' => CHAR_M, | ||
| 284 | 'N' | 'n' => CHAR_N, | ||
| 285 | 'O' | 'o' => CHAR_O, | ||
| 286 | 'P' | 'p' => CHAR_P, | ||
| 287 | 'Q' | 'q' => CHAR_Q, | ||
| 288 | 'R' | 'r' => CHAR_R, | ||
| 289 | 'S' | 's' => CHAR_S, | ||
| 290 | 'T' | 't' => CHAR_T, | ||
| 291 | 'U' | 'u' => CHAR_U, | ||
| 292 | 'V' | 'v' => CHAR_V, | ||
| 293 | 'W' | 'w' => CHAR_W, | ||
| 294 | 'X' | 'x' => CHAR_X, | ||
| 295 | 'Y' | 'y' => CHAR_Y, | ||
| 296 | 'Z' | 'z' => CHAR_Z, | ||
| 297 | |||
| 298 | _ => CHAR_UNKNOWN, | ||
| 299 | } | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | pub struct DisplayBuffer { | ||
| 304 | pixels: [u64; 4], | ||
| 305 | } | ||
| 306 | |||
| 307 | impl DisplayBuffer { | ||
| 308 | pub const fn new() -> Self { | ||
| 309 | Self { pixels: [0; 4] } | ||
| 310 | } | ||
| 311 | |||
| 312 | pub fn clear(&mut self) { | ||
| 313 | *self = Self::new(); | ||
| 314 | } | ||
| 315 | |||
| 316 | fn write_char_segment(&mut self, index: usize, value: CharSegment) { | ||
| 317 | defmt::assert!(index < 6); | ||
| 318 | let (com, segments) = value.get_com_seg(); | ||
| 319 | self.pixels[com as usize] |= 1 << segments[index]; | ||
| 320 | } | ||
| 321 | |||
| 322 | pub fn write_char(&mut self, index: usize, val: char) { | ||
| 323 | let segments = characters::get_char_segments(val); | ||
| 324 | |||
| 325 | for segment in segments { | ||
| 326 | self.write_char_segment(index, *segment); | ||
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | pub fn write(&self, lcd: &mut Lcd<'_, LCD>) { | ||
| 331 | lcd.write_com_segments(0, self.pixels[0]); | ||
| 332 | lcd.write_com_segments(1, self.pixels[1]); | ||
| 333 | lcd.write_com_segments(2, self.pixels[2]); | ||
| 334 | lcd.write_com_segments(3, self.pixels[3]); | ||
| 335 | lcd.submit_frame(); | ||
| 336 | } | ||
| 337 | |||
| 338 | pub fn write_colon(&mut self, index: usize) { | ||
| 339 | defmt::assert!(index < 4); | ||
| 340 | self.pixels[display_layout::COLON_COM as usize] |= 1 << display_layout::COLON_SEG[index]; | ||
| 341 | } | ||
| 342 | |||
| 343 | pub fn write_dot(&mut self, index: usize) { | ||
| 344 | defmt::assert!(index < 4); | ||
| 345 | self.pixels[display_layout::DOT_COM as usize] |= 1 << display_layout::DOT_SEG[index]; | ||
| 346 | } | ||
| 347 | |||
| 348 | pub fn write_bar(&mut self, index: usize) { | ||
| 349 | defmt::assert!(index < 4); | ||
| 350 | let (bar_com, bar_seg) = display_layout::BAR[index]; | ||
| 351 | self.pixels[bar_com as usize] |= 1 << bar_seg; | ||
| 352 | } | ||
| 353 | } | ||
| 354 | |||
| 355 | impl Default for DisplayBuffer { | ||
| 356 | fn default() -> Self { | ||
| 357 | Self::new() | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 361 | #[derive(Debug, Clone, Copy)] | ||
| 362 | enum CharSegment { | ||
| 363 | /// North | ||
| 364 | N, | ||
| 365 | /// North west | ||
| 366 | NW, | ||
| 367 | /// West | ||
| 368 | W, | ||
| 369 | /// South west | ||
| 370 | SW, | ||
| 371 | /// South | ||
| 372 | S, | ||
| 373 | /// South East | ||
| 374 | SE, | ||
| 375 | /// East | ||
| 376 | E, | ||
| 377 | /// North East | ||
| 378 | NE, | ||
| 379 | /// Top | ||
| 380 | T, | ||
| 381 | /// Top left | ||
| 382 | TL, | ||
| 383 | /// Bottom left | ||
| 384 | BL, | ||
| 385 | /// Bottom | ||
| 386 | B, | ||
| 387 | /// Bottom right | ||
| 388 | BR, | ||
| 389 | /// Top right | ||
| 390 | TR, | ||
| 391 | } | ||
| 392 | |||
| 393 | impl CharSegment { | ||
| 394 | fn get_com_seg(&self) -> (u8, [u8; 6]) { | ||
| 395 | match self { | ||
| 396 | CharSegment::N => (display_layout::CHAR_N_COM, display_layout::CHAR_N_SEG), | ||
| 397 | CharSegment::NW => (display_layout::CHAR_NW_COM, display_layout::CHAR_NW_SEG), | ||
| 398 | CharSegment::W => (display_layout::CHAR_W_COM, display_layout::CHAR_W_SEG), | ||
| 399 | CharSegment::SW => (display_layout::CHAR_SW_COM, display_layout::CHAR_SW_SEG), | ||
| 400 | CharSegment::S => (display_layout::CHAR_S_COM, display_layout::CHAR_S_SEG), | ||
| 401 | CharSegment::SE => (display_layout::CHAR_SE_COM, display_layout::CHAR_SE_SEG), | ||
| 402 | CharSegment::E => (display_layout::CHAR_E_COM, display_layout::CHAR_E_SEG), | ||
| 403 | CharSegment::NE => (display_layout::CHAR_NE_COM, display_layout::CHAR_NE_SEG), | ||
| 404 | CharSegment::T => (display_layout::CHAR_T_COM, display_layout::CHAR_T_SEG), | ||
| 405 | CharSegment::TL => (display_layout::CHAR_TL_COM, display_layout::CHAR_TL_SEG), | ||
| 406 | CharSegment::BL => (display_layout::CHAR_BL_COM, display_layout::CHAR_BL_SEG), | ||
| 407 | CharSegment::B => (display_layout::CHAR_B_COM, display_layout::CHAR_B_SEG), | ||
| 408 | CharSegment::BR => (display_layout::CHAR_BR_COM, display_layout::CHAR_BR_SEG), | ||
| 409 | CharSegment::TR => (display_layout::CHAR_TR_COM, display_layout::CHAR_TR_SEG), | ||
| 410 | } | ||
| 411 | } | ||
| 412 | } | ||
diff --git a/examples/stm32u5/src/bin/adc.rs b/examples/stm32u5/src/bin/adc.rs index 99944f7c7..4d2d93aa2 100644 --- a/examples/stm32u5/src/bin/adc.rs +++ b/examples/stm32u5/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::{self, AdcChannel, SampleTime, adc4}; | 5 | use embassy_stm32::adc::{self, Adc, AdcChannel, AdcConfig, 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,27 +12,29 @@ 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 | // **** ADC1 init **** | 14 | // **** ADC1 init **** |
| 15 | 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); | ||
| 16 | let mut adc1_pin1 = p.PA3; // A0 on nucleo u5a5 | 19 | let mut adc1_pin1 = p.PA3; // A0 on nucleo u5a5 |
| 17 | let mut adc1_pin2 = p.PA2; // A1 | 20 | let mut adc1_pin2 = p.PA2; // A1 |
| 18 | adc1.set_resolution(adc::Resolution::BITS14); | ||
| 19 | adc1.set_averaging(adc::Averaging::Samples1024); | ||
| 20 | let max1 = adc::resolution_to_max_count(adc::Resolution::BITS14); | 21 | let max1 = adc::resolution_to_max_count(adc::Resolution::BITS14); |
| 21 | 22 | ||
| 22 | // **** ADC2 init **** | 23 | // **** ADC2 init **** |
| 23 | 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); | ||
| 24 | let mut adc2_pin1 = p.PC3; // A2 | 28 | let mut adc2_pin1 = p.PC3; // A2 |
| 25 | let mut adc2_pin2 = p.PB0; // A3 | 29 | let mut adc2_pin2 = p.PB0; // A3 |
| 26 | adc2.set_resolution(adc::Resolution::BITS14); | ||
| 27 | adc2.set_averaging(adc::Averaging::Samples1024); | ||
| 28 | let max2 = adc::resolution_to_max_count(adc::Resolution::BITS14); | 30 | let max2 = adc::resolution_to_max_count(adc::Resolution::BITS14); |
| 29 | 31 | ||
| 30 | // **** ADC4 init **** | 32 | // **** ADC4 init **** |
| 31 | let mut adc4 = adc4::Adc4::new(p.ADC4); | 33 | let mut adc4 = Adc::new_adc4(p.ADC4); |
| 32 | let mut adc4_pin1 = p.PC1; // A4 | 34 | let mut adc4_pin1 = p.PC1.degrade_adc(); // A4 |
| 33 | let mut adc4_pin2 = p.PC0; // A5 | 35 | let mut adc4_pin2 = p.PC0; // A5 |
| 34 | adc4.set_resolution(adc4::Resolution::BITS12); | 36 | adc4.set_resolution_adc4(adc4::Resolution::BITS12); |
| 35 | adc4.set_averaging(adc4::Averaging::Samples256); | 37 | adc4.set_averaging_adc4(adc4::Averaging::Samples256); |
| 36 | let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); | 38 | let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); |
| 37 | 39 | ||
| 38 | // **** ADC1 blocking read **** | 40 | // **** ADC1 blocking read **** |
| @@ -93,11 +95,14 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 93 | // 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 |
| 94 | adc4.read( | 96 | adc4.read( |
| 95 | p.GPDMA1_CH1.reborrow(), | 97 | p.GPDMA1_CH1.reborrow(), |
| 96 | [&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(), | ||
| 97 | &mut measurements, | 103 | &mut measurements, |
| 98 | ) | 104 | ) |
| 99 | .await | 105 | .await; |
| 100 | .unwrap(); | ||
| 101 | 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; |
| 102 | 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; |
| 103 | info!("Async read 4 pin 1 {}", volt1); | 108 | info!("Async read 4 pin 1 {}", volt1); |
diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 783690c11..83119e3a0 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml | |||
| @@ -7,7 +7,7 @@ publish = false | |||
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | # Change stm32wb55rg to your chip name in both dependencies, if necessary. | 9 | # Change stm32wb55rg to your chip name in both dependencies, if necessary. |
| 10 | embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } | 10 | embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti", "low-power"] } |
| 11 | embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } | 11 | embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } |
| 12 | embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } | 12 | embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } |
| 13 | embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | 13 | embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
diff --git a/examples/stm32wb/src/bin/button_exti.rs b/examples/stm32wb/src/bin/button_exti.rs index 2871fd55f..3c58eb556 100644 --- a/examples/stm32wb/src/bin/button_exti.rs +++ b/examples/stm32wb/src/bin/button_exti.rs | |||
| @@ -3,16 +3,22 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::exti::ExtiInput; | 6 | use embassy_stm32::exti::{self, ExtiInput}; |
| 7 | use embassy_stm32::gpio::Pull; | 7 | use embassy_stm32::gpio::Pull; |
| 8 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 11 | bind_interrupts!( | ||
| 12 | pub struct Irqs{ | ||
| 13 | EXTI4 => exti::InterruptHandler<interrupt::typelevel::EXTI4>; | ||
| 14 | }); | ||
| 15 | |||
| 10 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_stm32::init(Default::default()); | 18 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | 19 | info!("Hello World!"); |
| 14 | 20 | ||
| 15 | let mut button = ExtiInput::new(p.PC4, p.EXTI4, Pull::Up); | 21 | let mut button = ExtiInput::new(p.PC4, p.EXTI4, Pull::Up, Irqs); |
| 16 | 22 | ||
| 17 | info!("Press the USER button..."); | 23 | info!("Press the USER button..."); |
| 18 | 24 | ||
diff --git a/examples/stm32wb/src/bin/eddystone_beacon.rs b/examples/stm32wb/src/bin/eddystone_beacon.rs index f309ca3a2..413b1ac8f 100644 --- a/examples/stm32wb/src/bin/eddystone_beacon.rs +++ b/examples/stm32wb/src/bin/eddystone_beacon.rs | |||
| @@ -57,126 +57,112 @@ async fn main(_spawner: Spawner) { | |||
| 57 | info!("Hello World!"); | 57 | info!("Hello World!"); |
| 58 | 58 | ||
| 59 | let config = Config::default(); | 59 | let config = Config::default(); |
| 60 | let mut mbox = TlMbox::init(p.IPCC, Irqs, config); | 60 | let mbox = TlMbox::init(p.IPCC, Irqs, config).await; |
| 61 | let mut sys = mbox.sys_subsystem; | ||
| 62 | let mut ble = mbox.ble_subsystem; | ||
| 61 | 63 | ||
| 62 | let sys_event = mbox.sys_subsystem.read().await; | 64 | let _ = sys.shci_c2_ble_init(Default::default()).await; |
| 63 | info!("sys event: {}", sys_event.payload()); | ||
| 64 | |||
| 65 | let _ = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; | ||
| 66 | 65 | ||
| 67 | info!("resetting BLE..."); | 66 | info!("resetting BLE..."); |
| 68 | mbox.ble_subsystem.reset().await; | 67 | ble.reset().await; |
| 69 | let response = mbox.ble_subsystem.read().await.unwrap(); | 68 | let response = ble.read().await.unwrap(); |
| 70 | defmt::info!("{}", response); | 69 | defmt::info!("{}", response); |
| 71 | 70 | ||
| 72 | info!("config public address..."); | 71 | info!("config public address..."); |
| 73 | mbox.ble_subsystem | 72 | ble.write_config_data(&ConfigData::public_address(get_bd_addr()).build()) |
| 74 | .write_config_data(&ConfigData::public_address(get_bd_addr()).build()) | ||
| 75 | .await; | 73 | .await; |
| 76 | let response = mbox.ble_subsystem.read().await.unwrap(); | 74 | let response = ble.read().await.unwrap(); |
| 77 | defmt::info!("{}", response); | 75 | defmt::info!("{}", response); |
| 78 | 76 | ||
| 79 | info!("config random address..."); | 77 | info!("config random address..."); |
| 80 | mbox.ble_subsystem | 78 | ble.write_config_data(&ConfigData::random_address(get_random_addr()).build()) |
| 81 | .write_config_data(&ConfigData::random_address(get_random_addr()).build()) | ||
| 82 | .await; | 79 | .await; |
| 83 | let response = mbox.ble_subsystem.read().await.unwrap(); | 80 | let response = ble.read().await.unwrap(); |
| 84 | defmt::info!("{}", response); | 81 | defmt::info!("{}", response); |
| 85 | 82 | ||
| 86 | info!("config identity root..."); | 83 | info!("config identity root..."); |
| 87 | mbox.ble_subsystem | 84 | ble.write_config_data(&ConfigData::identity_root(&get_irk()).build()) |
| 88 | .write_config_data(&ConfigData::identity_root(&get_irk()).build()) | ||
| 89 | .await; | 85 | .await; |
| 90 | let response = mbox.ble_subsystem.read().await.unwrap(); | 86 | let response = ble.read().await.unwrap(); |
| 91 | defmt::info!("{}", response); | 87 | defmt::info!("{}", response); |
| 92 | 88 | ||
| 93 | info!("config encryption root..."); | 89 | info!("config encryption root..."); |
| 94 | mbox.ble_subsystem | 90 | ble.write_config_data(&ConfigData::encryption_root(&get_erk()).build()) |
| 95 | .write_config_data(&ConfigData::encryption_root(&get_erk()).build()) | ||
| 96 | .await; | 91 | .await; |
| 97 | let response = mbox.ble_subsystem.read().await.unwrap(); | 92 | let response = ble.read().await.unwrap(); |
| 98 | defmt::info!("{}", response); | 93 | defmt::info!("{}", response); |
| 99 | 94 | ||
| 100 | info!("config tx power level..."); | 95 | info!("config tx power level..."); |
| 101 | mbox.ble_subsystem.set_tx_power_level(PowerLevel::ZerodBm).await; | 96 | ble.set_tx_power_level(PowerLevel::ZerodBm).await; |
| 102 | let response = mbox.ble_subsystem.read().await.unwrap(); | 97 | let response = ble.read().await.unwrap(); |
| 103 | defmt::info!("{}", response); | 98 | defmt::info!("{}", response); |
| 104 | 99 | ||
| 105 | info!("GATT init..."); | 100 | info!("GATT init..."); |
| 106 | mbox.ble_subsystem.init_gatt().await; | 101 | ble.init_gatt().await; |
| 107 | let response = mbox.ble_subsystem.read().await.unwrap(); | 102 | let response = ble.read().await.unwrap(); |
| 108 | defmt::info!("{}", response); | 103 | defmt::info!("{}", response); |
| 109 | 104 | ||
| 110 | info!("GAP init..."); | 105 | info!("GAP init..."); |
| 111 | mbox.ble_subsystem | 106 | ble.init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH).await; |
| 112 | .init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH) | 107 | let response = ble.read().await.unwrap(); |
| 113 | .await; | ||
| 114 | let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 115 | defmt::info!("{}", response); | 108 | defmt::info!("{}", response); |
| 116 | 109 | ||
| 117 | // info!("set scan response..."); | 110 | // info!("set scan response..."); |
| 118 | // mbox.ble_subsystem.le_set_scan_response_data(&[]).await.unwrap(); | 111 | // ble.le_set_scan_response_data(&[]).await.unwrap(); |
| 119 | // let response = mbox.ble_subsystem.read().await.unwrap(); | 112 | // let response = ble.read().await.unwrap(); |
| 120 | // defmt::info!("{}", response); | 113 | // defmt::info!("{}", response); |
| 121 | 114 | ||
| 122 | info!("set discoverable..."); | 115 | info!("set discoverable..."); |
| 123 | mbox.ble_subsystem | 116 | ble.set_discoverable(&DiscoverableParameters { |
| 124 | .set_discoverable(&DiscoverableParameters { | 117 | advertising_type: AdvertisingType::NonConnectableUndirected, |
| 125 | advertising_type: AdvertisingType::NonConnectableUndirected, | 118 | advertising_interval: Some((Duration::from_millis(250), Duration::from_millis(250))), |
| 126 | advertising_interval: Some((Duration::from_millis(250), Duration::from_millis(250))), | 119 | address_type: OwnAddressType::Public, |
| 127 | address_type: OwnAddressType::Public, | 120 | filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan, |
| 128 | filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan, | 121 | local_name: None, |
| 129 | local_name: None, | 122 | advertising_data: &[], |
| 130 | advertising_data: &[], | 123 | conn_interval: (None, None), |
| 131 | conn_interval: (None, None), | 124 | }) |
| 132 | }) | 125 | .await |
| 133 | .await | 126 | .unwrap(); |
| 134 | .unwrap(); | 127 | |
| 135 | 128 | let response = ble.read().await; | |
| 136 | let response = mbox.ble_subsystem.read().await; | ||
| 137 | defmt::info!("{}", response); | 129 | defmt::info!("{}", response); |
| 138 | 130 | ||
| 139 | // remove some advertisement to decrease the packet size | 131 | // remove some advertisement to decrease the packet size |
| 140 | info!("delete tx power ad type..."); | 132 | info!("delete tx power ad type..."); |
| 141 | mbox.ble_subsystem | 133 | ble.delete_ad_type(AdvertisingDataType::TxPowerLevel).await; |
| 142 | .delete_ad_type(AdvertisingDataType::TxPowerLevel) | 134 | let response = ble.read().await.unwrap(); |
| 143 | .await; | ||
| 144 | let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 145 | defmt::info!("{}", response); | 135 | defmt::info!("{}", response); |
| 146 | 136 | ||
| 147 | info!("delete conn interval ad type..."); | 137 | info!("delete conn interval ad type..."); |
| 148 | mbox.ble_subsystem | 138 | ble.delete_ad_type(AdvertisingDataType::PeripheralConnectionInterval) |
| 149 | .delete_ad_type(AdvertisingDataType::PeripheralConnectionInterval) | ||
| 150 | .await; | 139 | .await; |
| 151 | let response = mbox.ble_subsystem.read().await.unwrap(); | 140 | let response = ble.read().await.unwrap(); |
| 152 | defmt::info!("{}", response); | 141 | defmt::info!("{}", response); |
| 153 | 142 | ||
| 154 | info!("update advertising data..."); | 143 | info!("update advertising data..."); |
| 155 | mbox.ble_subsystem | 144 | ble.update_advertising_data(&eddystone_advertising_data()) |
| 156 | .update_advertising_data(&eddystone_advertising_data()) | ||
| 157 | .await | 145 | .await |
| 158 | .unwrap(); | 146 | .unwrap(); |
| 159 | let response = mbox.ble_subsystem.read().await.unwrap(); | 147 | let response = ble.read().await.unwrap(); |
| 160 | defmt::info!("{}", response); | 148 | defmt::info!("{}", response); |
| 161 | 149 | ||
| 162 | info!("update advertising data type..."); | 150 | info!("update advertising data type..."); |
| 163 | mbox.ble_subsystem | 151 | ble.update_advertising_data(&[3, AdvertisingDataType::UuidCompleteList16 as u8, 0xaa, 0xfe]) |
| 164 | .update_advertising_data(&[3, AdvertisingDataType::UuidCompleteList16 as u8, 0xaa, 0xfe]) | ||
| 165 | .await | 152 | .await |
| 166 | .unwrap(); | 153 | .unwrap(); |
| 167 | let response = mbox.ble_subsystem.read().await.unwrap(); | 154 | let response = ble.read().await.unwrap(); |
| 168 | defmt::info!("{}", response); | 155 | defmt::info!("{}", response); |
| 169 | 156 | ||
| 170 | info!("update advertising data flags..."); | 157 | info!("update advertising data flags..."); |
| 171 | mbox.ble_subsystem | 158 | ble.update_advertising_data(&[ |
| 172 | .update_advertising_data(&[ | 159 | 2, |
| 173 | 2, | 160 | AdvertisingDataType::Flags as u8, |
| 174 | AdvertisingDataType::Flags as u8, | 161 | (0x02 | 0x04) as u8, // BLE general discoverable, without BR/EDR support |
| 175 | (0x02 | 0x04) as u8, // BLE general discoverable, without BR/EDR support | 162 | ]) |
| 176 | ]) | 163 | .await |
| 177 | .await | 164 | .unwrap(); |
| 178 | .unwrap(); | 165 | let response = ble.read().await.unwrap(); |
| 179 | let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 180 | defmt::info!("{}", response); | 166 | defmt::info!("{}", response); |
| 181 | 167 | ||
| 182 | cortex_m::asm::wfi(); | 168 | cortex_m::asm::wfi(); |
diff --git a/examples/stm32wb/src/bin/gatt_server.rs b/examples/stm32wb/src/bin/gatt_server.rs index 2ed257566..3484f1844 100644 --- a/examples/stm32wb/src/bin/gatt_server.rs +++ b/examples/stm32wb/src/bin/gatt_server.rs | |||
| @@ -69,92 +69,85 @@ async fn main(spawner: Spawner) { | |||
| 69 | info!("Hello World!"); | 69 | info!("Hello World!"); |
| 70 | 70 | ||
| 71 | let config = Config::default(); | 71 | let config = Config::default(); |
| 72 | let mut mbox = TlMbox::init(p.IPCC, Irqs, config); | 72 | let mbox = TlMbox::init(p.IPCC, Irqs, config).await; |
| 73 | let mut sys = mbox.sys_subsystem; | ||
| 74 | let mut ble = mbox.ble_subsystem; | ||
| 73 | 75 | ||
| 74 | spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); | 76 | spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); |
| 75 | let sys_event = mbox.sys_subsystem.read().await; | ||
| 76 | info!("sys event: {}", sys_event.payload()); | ||
| 77 | 77 | ||
| 78 | let _ = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; | 78 | let _ = sys.shci_c2_ble_init(Default::default()).await; |
| 79 | 79 | ||
| 80 | info!("resetting BLE..."); | 80 | info!("resetting BLE..."); |
| 81 | mbox.ble_subsystem.reset().await; | 81 | ble.reset().await; |
| 82 | let response = mbox.ble_subsystem.read().await; | 82 | let response = ble.read().await; |
| 83 | defmt::debug!("{}", response); | 83 | defmt::debug!("{}", response); |
| 84 | 84 | ||
| 85 | info!("config public address..."); | 85 | info!("config public address..."); |
| 86 | mbox.ble_subsystem | 86 | ble.write_config_data(&ConfigData::public_address(get_bd_addr()).build()) |
| 87 | .write_config_data(&ConfigData::public_address(get_bd_addr()).build()) | ||
| 88 | .await; | 87 | .await; |
| 89 | let response = mbox.ble_subsystem.read().await; | 88 | let response = ble.read().await; |
| 90 | defmt::debug!("{}", response); | 89 | defmt::debug!("{}", response); |
| 91 | 90 | ||
| 92 | info!("config random address..."); | 91 | info!("config random address..."); |
| 93 | mbox.ble_subsystem | 92 | ble.write_config_data(&ConfigData::random_address(get_random_addr()).build()) |
| 94 | .write_config_data(&ConfigData::random_address(get_random_addr()).build()) | ||
| 95 | .await; | 93 | .await; |
| 96 | let response = mbox.ble_subsystem.read().await; | 94 | let response = ble.read().await; |
| 97 | defmt::debug!("{}", response); | 95 | defmt::debug!("{}", response); |
| 98 | 96 | ||
| 99 | info!("config identity root..."); | 97 | info!("config identity root..."); |
| 100 | mbox.ble_subsystem | 98 | ble.write_config_data(&ConfigData::identity_root(&get_irk()).build()) |
| 101 | .write_config_data(&ConfigData::identity_root(&get_irk()).build()) | ||
| 102 | .await; | 99 | .await; |
| 103 | let response = mbox.ble_subsystem.read().await; | 100 | let response = ble.read().await; |
| 104 | defmt::debug!("{}", response); | 101 | defmt::debug!("{}", response); |
| 105 | 102 | ||
| 106 | info!("config encryption root..."); | 103 | info!("config encryption root..."); |
| 107 | mbox.ble_subsystem | 104 | ble.write_config_data(&ConfigData::encryption_root(&get_erk()).build()) |
| 108 | .write_config_data(&ConfigData::encryption_root(&get_erk()).build()) | ||
| 109 | .await; | 105 | .await; |
| 110 | let response = mbox.ble_subsystem.read().await; | 106 | let response = ble.read().await; |
| 111 | defmt::debug!("{}", response); | 107 | defmt::debug!("{}", response); |
| 112 | 108 | ||
| 113 | info!("config tx power level..."); | 109 | info!("config tx power level..."); |
| 114 | mbox.ble_subsystem.set_tx_power_level(PowerLevel::ZerodBm).await; | 110 | ble.set_tx_power_level(PowerLevel::ZerodBm).await; |
| 115 | let response = mbox.ble_subsystem.read().await; | 111 | let response = ble.read().await; |
| 116 | defmt::debug!("{}", response); | 112 | defmt::debug!("{}", response); |
| 117 | 113 | ||
| 118 | info!("GATT init..."); | 114 | info!("GATT init..."); |
| 119 | mbox.ble_subsystem.init_gatt().await; | 115 | ble.init_gatt().await; |
| 120 | let response = mbox.ble_subsystem.read().await; | 116 | let response = ble.read().await; |
| 121 | defmt::debug!("{}", response); | 117 | defmt::debug!("{}", response); |
| 122 | 118 | ||
| 123 | info!("GAP init..."); | 119 | info!("GAP init..."); |
| 124 | mbox.ble_subsystem | 120 | ble.init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH).await; |
| 125 | .init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH) | 121 | let response = ble.read().await; |
| 126 | .await; | ||
| 127 | let response = mbox.ble_subsystem.read().await; | ||
| 128 | defmt::debug!("{}", response); | 122 | defmt::debug!("{}", response); |
| 129 | 123 | ||
| 130 | info!("set IO capabilities..."); | 124 | info!("set IO capabilities..."); |
| 131 | mbox.ble_subsystem.set_io_capability(IoCapability::DisplayConfirm).await; | 125 | ble.set_io_capability(IoCapability::DisplayConfirm).await; |
| 132 | let response = mbox.ble_subsystem.read().await; | 126 | let response = ble.read().await; |
| 133 | defmt::debug!("{}", response); | 127 | defmt::debug!("{}", response); |
| 134 | 128 | ||
| 135 | info!("set authentication requirements..."); | 129 | info!("set authentication requirements..."); |
| 136 | mbox.ble_subsystem | 130 | ble.set_authentication_requirement(&AuthenticationRequirements { |
| 137 | .set_authentication_requirement(&AuthenticationRequirements { | 131 | bonding_required: false, |
| 138 | bonding_required: false, | 132 | keypress_notification_support: false, |
| 139 | keypress_notification_support: false, | 133 | mitm_protection_required: false, |
| 140 | mitm_protection_required: false, | 134 | encryption_key_size_range: (8, 16), |
| 141 | encryption_key_size_range: (8, 16), | 135 | fixed_pin: Pin::Requested, |
| 142 | fixed_pin: Pin::Requested, | 136 | identity_address_type: AddressType::Public, |
| 143 | identity_address_type: AddressType::Public, | 137 | secure_connection_support: SecureConnectionSupport::Optional, |
| 144 | secure_connection_support: SecureConnectionSupport::Optional, | 138 | }) |
| 145 | }) | 139 | .await |
| 146 | .await | 140 | .unwrap(); |
| 147 | .unwrap(); | 141 | let response = ble.read().await; |
| 148 | let response = mbox.ble_subsystem.read().await; | ||
| 149 | defmt::debug!("{}", response); | 142 | defmt::debug!("{}", response); |
| 150 | 143 | ||
| 151 | info!("set scan response data..."); | 144 | info!("set scan response data..."); |
| 152 | mbox.ble_subsystem.le_set_scan_response_data(b"TXTX").await.unwrap(); | 145 | ble.le_set_scan_response_data(b"TXTX").await.unwrap(); |
| 153 | let response = mbox.ble_subsystem.read().await; | 146 | let response = ble.read().await; |
| 154 | defmt::debug!("{}", response); | 147 | defmt::debug!("{}", response); |
| 155 | 148 | ||
| 156 | defmt::info!("initializing services and characteristics..."); | 149 | defmt::info!("initializing services and characteristics..."); |
| 157 | let mut ble_context = init_gatt_services(&mut mbox.ble_subsystem).await.unwrap(); | 150 | let mut ble_context = init_gatt_services(&mut ble).await.unwrap(); |
| 158 | defmt::info!("{}", ble_context); | 151 | defmt::info!("{}", ble_context); |
| 159 | 152 | ||
| 160 | let discovery_params = DiscoverableParameters { | 153 | let discovery_params = DiscoverableParameters { |
| @@ -168,12 +161,12 @@ async fn main(spawner: Spawner) { | |||
| 168 | }; | 161 | }; |
| 169 | 162 | ||
| 170 | info!("set discoverable..."); | 163 | info!("set discoverable..."); |
| 171 | mbox.ble_subsystem.set_discoverable(&discovery_params).await.unwrap(); | 164 | ble.set_discoverable(&discovery_params).await.unwrap(); |
| 172 | let response = mbox.ble_subsystem.read().await; | 165 | let response = ble.read().await; |
| 173 | defmt::debug!("{}", response); | 166 | defmt::debug!("{}", response); |
| 174 | 167 | ||
| 175 | loop { | 168 | loop { |
| 176 | let response = mbox.ble_subsystem.read().await; | 169 | let response = ble.read().await; |
| 177 | defmt::debug!("{}", response); | 170 | defmt::debug!("{}", response); |
| 178 | 171 | ||
| 179 | if let Ok(Packet::Event(event)) = response { | 172 | if let Ok(Packet::Event(event)) = response { |
| @@ -184,24 +177,23 @@ async fn main(spawner: Spawner) { | |||
| 184 | Event::DisconnectionComplete(_) => { | 177 | Event::DisconnectionComplete(_) => { |
| 185 | defmt::info!("disconnected"); | 178 | defmt::info!("disconnected"); |
| 186 | ble_context.is_subscribed = false; | 179 | ble_context.is_subscribed = false; |
| 187 | mbox.ble_subsystem.set_discoverable(&discovery_params).await.unwrap(); | 180 | ble.set_discoverable(&discovery_params).await.unwrap(); |
| 188 | } | 181 | } |
| 189 | Event::Vendor(vendor_event) => match vendor_event { | 182 | Event::Vendor(vendor_event) => match vendor_event { |
| 190 | VendorEvent::AttReadPermitRequest(read_req) => { | 183 | VendorEvent::AttReadPermitRequest(read_req) => { |
| 191 | defmt::info!("read request received {}, allowing", read_req); | 184 | defmt::info!("read request received {}, allowing", read_req); |
| 192 | mbox.ble_subsystem.allow_read(read_req.conn_handle).await | 185 | ble.allow_read(read_req.conn_handle).await |
| 193 | } | 186 | } |
| 194 | VendorEvent::AttWritePermitRequest(write_req) => { | 187 | VendorEvent::AttWritePermitRequest(write_req) => { |
| 195 | defmt::info!("write request received {}, allowing", write_req); | 188 | defmt::info!("write request received {}, allowing", write_req); |
| 196 | mbox.ble_subsystem | 189 | ble.write_response(&WriteResponseParameters { |
| 197 | .write_response(&WriteResponseParameters { | 190 | conn_handle: write_req.conn_handle, |
| 198 | conn_handle: write_req.conn_handle, | 191 | attribute_handle: write_req.attribute_handle, |
| 199 | attribute_handle: write_req.attribute_handle, | 192 | status: Ok(()), |
| 200 | status: Ok(()), | 193 | value: write_req.value(), |
| 201 | value: write_req.value(), | 194 | }) |
| 202 | }) | 195 | .await |
| 203 | .await | 196 | .unwrap() |
| 204 | .unwrap() | ||
| 205 | } | 197 | } |
| 206 | VendorEvent::GattAttributeModified(attribute) => { | 198 | VendorEvent::GattAttributeModified(attribute) => { |
| 207 | defmt::info!("{}", ble_context); | 199 | defmt::info!("{}", ble_context); |
| @@ -224,7 +216,7 @@ async fn main(spawner: Spawner) { | |||
| 224 | } | 216 | } |
| 225 | 217 | ||
| 226 | #[embassy_executor::task] | 218 | #[embassy_executor::task] |
| 227 | async fn run_mm_queue(memory_manager: mm::MemoryManager) { | 219 | async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) { |
| 228 | memory_manager.run_queue().await; | 220 | memory_manager.run_queue().await; |
| 229 | } | 221 | } |
| 230 | 222 | ||
| @@ -285,7 +277,7 @@ pub struct CharHandles { | |||
| 285 | pub notify: AttributeHandle, | 277 | pub notify: AttributeHandle, |
| 286 | } | 278 | } |
| 287 | 279 | ||
| 288 | pub async fn init_gatt_services(ble_subsystem: &mut Ble) -> Result<BleContext, ()> { | 280 | pub async fn init_gatt_services<'a>(ble_subsystem: &mut Ble<'a>) -> Result<BleContext, ()> { |
| 289 | let service_handle = gatt_add_service(ble_subsystem, Uuid::Uuid16(0x500)).await?; | 281 | let service_handle = gatt_add_service(ble_subsystem, Uuid::Uuid16(0x500)).await?; |
| 290 | 282 | ||
| 291 | let read = gatt_add_char( | 283 | let read = gatt_add_char( |
| @@ -322,7 +314,7 @@ pub async fn init_gatt_services(ble_subsystem: &mut Ble) -> Result<BleContext, ( | |||
| 322 | }) | 314 | }) |
| 323 | } | 315 | } |
| 324 | 316 | ||
| 325 | async fn gatt_add_service(ble_subsystem: &mut Ble, uuid: Uuid) -> Result<AttributeHandle, ()> { | 317 | async fn gatt_add_service<'a>(ble_subsystem: &mut Ble<'a>, uuid: Uuid) -> Result<AttributeHandle, ()> { |
| 326 | ble_subsystem | 318 | ble_subsystem |
| 327 | .add_service(&AddServiceParameters { | 319 | .add_service(&AddServiceParameters { |
| 328 | uuid, | 320 | uuid, |
| @@ -348,8 +340,8 @@ async fn gatt_add_service(ble_subsystem: &mut Ble, uuid: Uuid) -> Result<Attribu | |||
| 348 | } | 340 | } |
| 349 | } | 341 | } |
| 350 | 342 | ||
| 351 | async fn gatt_add_char( | 343 | async fn gatt_add_char<'a>( |
| 352 | ble_subsystem: &mut Ble, | 344 | ble_subsystem: &mut Ble<'a>, |
| 353 | service_handle: AttributeHandle, | 345 | service_handle: AttributeHandle, |
| 354 | characteristic_uuid: Uuid, | 346 | characteristic_uuid: Uuid, |
| 355 | characteristic_properties: CharacteristicProperty, | 347 | characteristic_properties: CharacteristicProperty, |
diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index 18a52e162..4bab6ea9f 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs | |||
| @@ -19,7 +19,7 @@ bind_interrupts!(struct Irqs{ | |||
| 19 | }); | 19 | }); |
| 20 | 20 | ||
| 21 | #[embassy_executor::task] | 21 | #[embassy_executor::task] |
| 22 | async fn run_mm_queue(memory_manager: mm::MemoryManager) { | 22 | async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) { |
| 23 | memory_manager.run_queue().await; | 23 | memory_manager.run_queue().await; |
| 24 | } | 24 | } |
| 25 | 25 | ||
| @@ -54,74 +54,72 @@ async fn main(spawner: Spawner) { | |||
| 54 | info!("Hello World!"); | 54 | info!("Hello World!"); |
| 55 | 55 | ||
| 56 | let config = Config::default(); | 56 | let config = Config::default(); |
| 57 | let mbox = TlMbox::init(p.IPCC, Irqs, config); | 57 | let mbox = TlMbox::init(p.IPCC, Irqs, config).await; |
| 58 | let mut sys = mbox.sys_subsystem; | ||
| 58 | 59 | ||
| 59 | spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); | 60 | spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); |
| 60 | 61 | ||
| 61 | let sys_event = mbox.sys_subsystem.read().await; | 62 | let result = sys.shci_c2_mac_802_15_4_init().await; |
| 62 | info!("sys event: {}", sys_event.payload()); | ||
| 63 | |||
| 64 | core::mem::drop(sys_event); | ||
| 65 | |||
| 66 | let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; | ||
| 67 | info!("initialized mac: {}", result); | 63 | info!("initialized mac: {}", result); |
| 68 | 64 | ||
| 65 | let (mut mac_rx, mut mac_tx) = mbox.mac_subsystem.split(); | ||
| 66 | |||
| 69 | info!("resetting"); | 67 | info!("resetting"); |
| 70 | mbox.mac_subsystem | 68 | mac_tx |
| 71 | .send_command(&ResetRequest { | 69 | .send_command(&ResetRequest { |
| 72 | set_default_pib: true, | 70 | set_default_pib: true, |
| 73 | ..Default::default() | 71 | ..Default::default() |
| 74 | }) | 72 | }) |
| 75 | .await | 73 | .await |
| 76 | .unwrap(); | 74 | .unwrap(); |
| 77 | defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); | 75 | defmt::info!("{:#x}", mac_rx.read().await.unwrap()); |
| 78 | 76 | ||
| 79 | info!("setting extended address"); | 77 | info!("setting extended address"); |
| 80 | let extended_address: u64 = 0xACDE480000000001; | 78 | let extended_address: u64 = 0xACDE480000000001; |
| 81 | mbox.mac_subsystem | 79 | mac_tx |
| 82 | .send_command(&SetRequest { | 80 | .send_command(&SetRequest { |
| 83 | pib_attribute_ptr: &extended_address as *const _ as *const u8, | 81 | pib_attribute_ptr: &extended_address as *const _ as *const u8, |
| 84 | pib_attribute: PibId::ExtendedAddress, | 82 | pib_attribute: PibId::ExtendedAddress, |
| 85 | }) | 83 | }) |
| 86 | .await | 84 | .await |
| 87 | .unwrap(); | 85 | .unwrap(); |
| 88 | defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); | 86 | defmt::info!("{:#x}", mac_rx.read().await.unwrap()); |
| 89 | 87 | ||
| 90 | info!("setting short address"); | 88 | info!("setting short address"); |
| 91 | let short_address: u16 = 0x1122; | 89 | let short_address: u16 = 0x1122; |
| 92 | mbox.mac_subsystem | 90 | mac_tx |
| 93 | .send_command(&SetRequest { | 91 | .send_command(&SetRequest { |
| 94 | pib_attribute_ptr: &short_address as *const _ as *const u8, | 92 | pib_attribute_ptr: &short_address as *const _ as *const u8, |
| 95 | pib_attribute: PibId::ShortAddress, | 93 | pib_attribute: PibId::ShortAddress, |
| 96 | }) | 94 | }) |
| 97 | .await | 95 | .await |
| 98 | .unwrap(); | 96 | .unwrap(); |
| 99 | defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); | 97 | defmt::info!("{:#x}", mac_rx.read().await.unwrap()); |
| 100 | 98 | ||
| 101 | info!("setting association permit"); | 99 | info!("setting association permit"); |
| 102 | let association_permit: bool = true; | 100 | let association_permit: bool = true; |
| 103 | mbox.mac_subsystem | 101 | mac_tx |
| 104 | .send_command(&SetRequest { | 102 | .send_command(&SetRequest { |
| 105 | pib_attribute_ptr: &association_permit as *const _ as *const u8, | 103 | pib_attribute_ptr: &association_permit as *const _ as *const u8, |
| 106 | pib_attribute: PibId::AssociationPermit, | 104 | pib_attribute: PibId::AssociationPermit, |
| 107 | }) | 105 | }) |
| 108 | .await | 106 | .await |
| 109 | .unwrap(); | 107 | .unwrap(); |
| 110 | defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); | 108 | defmt::info!("{:#x}", mac_rx.read().await.unwrap()); |
| 111 | 109 | ||
| 112 | info!("setting TX power"); | 110 | info!("setting TX power"); |
| 113 | let transmit_power: i8 = 2; | 111 | let transmit_power: i8 = 2; |
| 114 | mbox.mac_subsystem | 112 | mac_tx |
| 115 | .send_command(&SetRequest { | 113 | .send_command(&SetRequest { |
| 116 | pib_attribute_ptr: &transmit_power as *const _ as *const u8, | 114 | pib_attribute_ptr: &transmit_power as *const _ as *const u8, |
| 117 | pib_attribute: PibId::TransmitPower, | 115 | pib_attribute: PibId::TransmitPower, |
| 118 | }) | 116 | }) |
| 119 | .await | 117 | .await |
| 120 | .unwrap(); | 118 | .unwrap(); |
| 121 | defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); | 119 | defmt::info!("{:#x}", mac_rx.read().await.unwrap()); |
| 122 | 120 | ||
| 123 | info!("starting FFD device"); | 121 | info!("starting FFD device"); |
| 124 | mbox.mac_subsystem | 122 | mac_tx |
| 125 | .send_command(&StartRequest { | 123 | .send_command(&StartRequest { |
| 126 | pan_id: PanId([0x1A, 0xAA]), | 124 | pan_id: PanId([0x1A, 0xAA]), |
| 127 | channel_number: MacChannel::Channel16, | 125 | channel_number: MacChannel::Channel16, |
| @@ -133,28 +131,27 @@ async fn main(spawner: Spawner) { | |||
| 133 | }) | 131 | }) |
| 134 | .await | 132 | .await |
| 135 | .unwrap(); | 133 | .unwrap(); |
| 136 | defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); | 134 | defmt::info!("{:#x}", mac_rx.read().await.unwrap()); |
| 137 | 135 | ||
| 138 | info!("setting RX on when idle"); | 136 | info!("setting RX on when idle"); |
| 139 | let rx_on_while_idle: bool = true; | 137 | let rx_on_while_idle: bool = true; |
| 140 | mbox.mac_subsystem | 138 | mac_tx |
| 141 | .send_command(&SetRequest { | 139 | .send_command(&SetRequest { |
| 142 | pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8, | 140 | pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8, |
| 143 | pib_attribute: PibId::RxOnWhenIdle, | 141 | pib_attribute: PibId::RxOnWhenIdle, |
| 144 | }) | 142 | }) |
| 145 | .await | 143 | .await |
| 146 | .unwrap(); | 144 | .unwrap(); |
| 147 | defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); | 145 | defmt::info!("{:#x}", mac_rx.read().await.unwrap()); |
| 148 | 146 | ||
| 149 | loop { | 147 | loop { |
| 150 | let evt = mbox.mac_subsystem.read().await; | 148 | let evt = mac_rx.read().await; |
| 151 | if let Ok(evt) = evt { | 149 | if let Ok(evt) = evt { |
| 152 | defmt::info!("parsed mac event"); | 150 | defmt::info!("parsed mac event"); |
| 153 | defmt::info!("{:#x}", evt); | 151 | defmt::info!("{:#x}", evt); |
| 154 | 152 | ||
| 155 | match evt { | 153 | match evt { |
| 156 | MacEvent::MlmeAssociateInd(association) => mbox | 154 | MacEvent::MlmeAssociateInd(association) => mac_tx |
| 157 | .mac_subsystem | ||
| 158 | .send_command(&AssociateResponse { | 155 | .send_command(&AssociateResponse { |
| 159 | device_address: association.device_address, | 156 | device_address: association.device_address, |
| 160 | assoc_short_address: [0x33, 0x44], | 157 | assoc_short_address: [0x33, 0x44], |
diff --git a/examples/stm32wb/src/bin/mac_ffd_net.rs b/examples/stm32wb/src/bin/mac_ffd_net.rs index 5296943a1..b4789e3ee 100644 --- a/examples/stm32wb/src/bin/mac_ffd_net.rs +++ b/examples/stm32wb/src/bin/mac_ffd_net.rs | |||
| @@ -1,32 +1,44 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::net::Ipv6Addr; | ||
| 5 | |||
| 4 | use defmt::*; | 6 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 8 | use embassy_net::udp::{PacketMetadata, UdpSocket}; | ||
| 9 | use embassy_net::{Ipv6Cidr, StackResources, StaticConfigV6}; | ||
| 6 | use embassy_stm32::bind_interrupts; | 10 | use embassy_stm32::bind_interrupts; |
| 7 | use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; | 11 | use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; |
| 12 | use embassy_stm32::peripherals::RNG; | ||
| 8 | use embassy_stm32::rcc::WPAN_DEFAULT; | 13 | use embassy_stm32::rcc::WPAN_DEFAULT; |
| 14 | use embassy_stm32::rng::InterruptHandler as RngInterruptHandler; | ||
| 9 | use embassy_stm32_wpan::TlMbox; | 15 | use embassy_stm32_wpan::TlMbox; |
| 10 | use embassy_stm32_wpan::mac::commands::{ResetRequest, SetRequest, StartRequest}; | 16 | use embassy_stm32_wpan::mac::{Driver, DriverState, Runner}; |
| 11 | use embassy_stm32_wpan::mac::typedefs::{MacChannel, PanId, PibId}; | ||
| 12 | use embassy_stm32_wpan::mac::{self, Runner}; | ||
| 13 | use embassy_stm32_wpan::sub::mm; | 17 | use embassy_stm32_wpan::sub::mm; |
| 18 | use embassy_time::{Duration, Timer}; | ||
| 19 | use heapless::Vec; | ||
| 14 | use static_cell::StaticCell; | 20 | use static_cell::StaticCell; |
| 15 | use {defmt_rtt as _, panic_probe as _}; | 21 | use {defmt_rtt as _, panic_probe as _}; |
| 16 | 22 | ||
| 17 | bind_interrupts!(struct Irqs{ | 23 | bind_interrupts!(struct Irqs{ |
| 18 | IPCC_C1_RX => ReceiveInterruptHandler; | 24 | IPCC_C1_RX => ReceiveInterruptHandler; |
| 19 | IPCC_C1_TX => TransmitInterruptHandler; | 25 | IPCC_C1_TX => TransmitInterruptHandler; |
| 26 | RNG => RngInterruptHandler<RNG>; | ||
| 20 | }); | 27 | }); |
| 21 | 28 | ||
| 22 | #[embassy_executor::task] | 29 | #[embassy_executor::task] |
| 23 | async fn run_mm_queue(memory_manager: mm::MemoryManager) { | 30 | async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) -> ! { |
| 24 | memory_manager.run_queue().await; | 31 | memory_manager.run_queue().await |
| 32 | } | ||
| 33 | |||
| 34 | #[embassy_executor::task] | ||
| 35 | async fn run_mac(runner: &'static Runner<'static>) -> ! { | ||
| 36 | runner.run().await | ||
| 25 | } | 37 | } |
| 26 | 38 | ||
| 27 | #[embassy_executor::task] | 39 | #[embassy_executor::task] |
| 28 | async fn run_mac(runner: &'static Runner<'static>) { | 40 | async fn run_net(mut runner: embassy_net::Runner<'static, Driver<'static>>) -> ! { |
| 29 | runner.run().await; | 41 | runner.run().await |
| 30 | } | 42 | } |
| 31 | 43 | ||
| 32 | #[embassy_executor::main] | 44 | #[embassy_executor::main] |
| @@ -60,118 +72,76 @@ async fn main(spawner: Spawner) { | |||
| 60 | info!("Hello World!"); | 72 | info!("Hello World!"); |
| 61 | 73 | ||
| 62 | let config = Config::default(); | 74 | let config = Config::default(); |
| 63 | let mbox = TlMbox::init(p.IPCC, Irqs, config); | 75 | let mut mbox = TlMbox::init(p.IPCC, Irqs, config).await; |
| 64 | 76 | ||
| 65 | spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); | 77 | spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); |
| 66 | 78 | ||
| 67 | let sys_event = mbox.sys_subsystem.read().await; | ||
| 68 | info!("sys event: {}", sys_event.payload()); | ||
| 69 | |||
| 70 | core::mem::drop(sys_event); | ||
| 71 | |||
| 72 | let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; | 79 | let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; |
| 73 | info!("initialized mac: {}", result); | 80 | info!("initialized mac: {}", result); |
| 74 | 81 | ||
| 75 | info!("resetting"); | 82 | static DRIVER_STATE: StaticCell<DriverState> = StaticCell::new(); |
| 76 | mbox.mac_subsystem | ||
| 77 | .send_command(&ResetRequest { | ||
| 78 | set_default_pib: true, | ||
| 79 | ..Default::default() | ||
| 80 | }) | ||
| 81 | .await | ||
| 82 | .unwrap(); | ||
| 83 | defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); | ||
| 84 | |||
| 85 | info!("setting extended address"); | ||
| 86 | let extended_address: u64 = 0xACDE480000000001; | ||
| 87 | mbox.mac_subsystem | ||
| 88 | .send_command(&SetRequest { | ||
| 89 | pib_attribute_ptr: &extended_address as *const _ as *const u8, | ||
| 90 | pib_attribute: PibId::ExtendedAddress, | ||
| 91 | }) | ||
| 92 | .await | ||
| 93 | .unwrap(); | ||
| 94 | defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); | ||
| 95 | |||
| 96 | info!("setting short address"); | ||
| 97 | let short_address: u16 = 0x1122; | ||
| 98 | mbox.mac_subsystem | ||
| 99 | .send_command(&SetRequest { | ||
| 100 | pib_attribute_ptr: &short_address as *const _ as *const u8, | ||
| 101 | pib_attribute: PibId::ShortAddress, | ||
| 102 | }) | ||
| 103 | .await | ||
| 104 | .unwrap(); | ||
| 105 | defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); | ||
| 106 | |||
| 107 | info!("setting association permit"); | ||
| 108 | let association_permit: bool = true; | ||
| 109 | mbox.mac_subsystem | ||
| 110 | .send_command(&SetRequest { | ||
| 111 | pib_attribute_ptr: &association_permit as *const _ as *const u8, | ||
| 112 | pib_attribute: PibId::AssociationPermit, | ||
| 113 | }) | ||
| 114 | .await | ||
| 115 | .unwrap(); | ||
| 116 | defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); | ||
| 117 | |||
| 118 | info!("setting TX power"); | ||
| 119 | let transmit_power: i8 = 2; | ||
| 120 | mbox.mac_subsystem | ||
| 121 | .send_command(&SetRequest { | ||
| 122 | pib_attribute_ptr: &transmit_power as *const _ as *const u8, | ||
| 123 | pib_attribute: PibId::TransmitPower, | ||
| 124 | }) | ||
| 125 | .await | ||
| 126 | .unwrap(); | ||
| 127 | defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); | ||
| 128 | |||
| 129 | info!("starting FFD device"); | ||
| 130 | mbox.mac_subsystem | ||
| 131 | .send_command(&StartRequest { | ||
| 132 | pan_id: PanId([0x1A, 0xAA]), | ||
| 133 | channel_number: MacChannel::Channel16, | ||
| 134 | beacon_order: 0x0F, | ||
| 135 | superframe_order: 0x0F, | ||
| 136 | pan_coordinator: true, | ||
| 137 | battery_life_extension: false, | ||
| 138 | ..Default::default() | ||
| 139 | }) | ||
| 140 | .await | ||
| 141 | .unwrap(); | ||
| 142 | defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); | ||
| 143 | |||
| 144 | info!("setting RX on when idle"); | ||
| 145 | let rx_on_while_idle: bool = true; | ||
| 146 | mbox.mac_subsystem | ||
| 147 | .send_command(&SetRequest { | ||
| 148 | pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8, | ||
| 149 | pib_attribute: PibId::RxOnWhenIdle, | ||
| 150 | }) | ||
| 151 | .await | ||
| 152 | .unwrap(); | ||
| 153 | defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); | ||
| 154 | |||
| 155 | static TX1: StaticCell<[u8; 127]> = StaticCell::new(); | ||
| 156 | static TX2: StaticCell<[u8; 127]> = StaticCell::new(); | ||
| 157 | static TX3: StaticCell<[u8; 127]> = StaticCell::new(); | ||
| 158 | static TX4: StaticCell<[u8; 127]> = StaticCell::new(); | ||
| 159 | static TX5: StaticCell<[u8; 127]> = StaticCell::new(); | ||
| 160 | let tx_queue = [ | ||
| 161 | TX1.init([0u8; 127]), | ||
| 162 | TX2.init([0u8; 127]), | ||
| 163 | TX3.init([0u8; 127]), | ||
| 164 | TX4.init([0u8; 127]), | ||
| 165 | TX5.init([0u8; 127]), | ||
| 166 | ]; | ||
| 167 | |||
| 168 | static RUNNER: StaticCell<Runner> = StaticCell::new(); | 83 | static RUNNER: StaticCell<Runner> = StaticCell::new(); |
| 169 | let runner = RUNNER.init(Runner::new(mbox.mac_subsystem, tx_queue)); | 84 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 85 | |||
| 86 | let driver_state = DRIVER_STATE.init(DriverState::new(mbox.mac_subsystem)); | ||
| 87 | |||
| 88 | let (driver, mac_runner, mut control) = Driver::new( | ||
| 89 | driver_state, | ||
| 90 | 0x1122u16.to_be_bytes().try_into().unwrap(), | ||
| 91 | 0xACDE480000000001u64.to_be_bytes().try_into().unwrap(), | ||
| 92 | ); | ||
| 93 | |||
| 94 | // TODO: rng does not work for some reason | ||
| 95 | // Generate random seed. | ||
| 96 | // let mut rng = Rng::new(p.RNG, Irqs); | ||
| 97 | let seed = [0; 8]; | ||
| 98 | // let _ = rng.async_fill_bytes(&mut seed).await; | ||
| 99 | let seed = u64::from_le_bytes(seed); | ||
| 100 | |||
| 101 | info!("seed generated"); | ||
| 102 | |||
| 103 | // Init network stack | ||
| 104 | let ipv6_addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff); | ||
| 105 | |||
| 106 | let config = embassy_net::Config::ipv6_static(StaticConfigV6 { | ||
| 107 | address: Ipv6Cidr::new(ipv6_addr, 104), | ||
| 108 | gateway: None, | ||
| 109 | dns_servers: Vec::new(), | ||
| 110 | }); | ||
| 111 | |||
| 112 | let (stack, eth_runner) = embassy_net::new(driver, config, RESOURCES.init(StackResources::new()), seed); | ||
| 113 | |||
| 114 | // wpan runner | ||
| 115 | spawner.spawn(run_mac(RUNNER.init(mac_runner)).unwrap()); | ||
| 116 | |||
| 117 | // Launch network task | ||
| 118 | spawner.spawn(unwrap!(run_net(eth_runner))); | ||
| 119 | |||
| 120 | info!("Network task initialized"); | ||
| 121 | |||
| 122 | control.init_link([0x1A, 0xAA]).await; | ||
| 123 | |||
| 124 | // Ensure DHCP configuration is up before trying connect | ||
| 125 | stack.wait_config_up().await; | ||
| 126 | |||
| 127 | info!("Network up"); | ||
| 128 | |||
| 129 | // Then we can use it! | ||
| 130 | let mut rx_meta = [PacketMetadata::EMPTY]; | ||
| 131 | let mut rx_buffer = [0; 4096]; | ||
| 132 | let mut tx_meta = [PacketMetadata::EMPTY]; | ||
| 133 | let mut tx_buffer = [0; 4096]; | ||
| 134 | |||
| 135 | let mut socket = UdpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer); | ||
| 136 | |||
| 137 | let remote_endpoint = (Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2fb), 8000); | ||
| 138 | |||
| 139 | let send_buf = [0u8; 20]; | ||
| 170 | 140 | ||
| 171 | spawner.spawn(run_mac(runner).unwrap()); | 141 | socket.bind((ipv6_addr, 8000)).unwrap(); |
| 142 | socket.send_to(&send_buf, remote_endpoint).await.unwrap(); | ||
| 172 | 143 | ||
| 173 | let (driver, control) = mac::new(runner).await; | 144 | Timer::after(Duration::from_secs(2)).await; |
| 174 | 145 | ||
| 175 | let _ = driver; | 146 | cortex_m::asm::bkpt(); |
| 176 | let _ = control; | ||
| 177 | } | 147 | } |
diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs index 883179023..dae3c5200 100644 --- a/examples/stm32wb/src/bin/mac_rfd.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs | |||
| @@ -21,7 +21,7 @@ bind_interrupts!(struct Irqs{ | |||
| 21 | }); | 21 | }); |
| 22 | 22 | ||
| 23 | #[embassy_executor::task] | 23 | #[embassy_executor::task] |
| 24 | async fn run_mm_queue(memory_manager: mm::MemoryManager) { | 24 | async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) { |
| 25 | memory_manager.run_queue().await; | 25 | memory_manager.run_queue().await; |
| 26 | } | 26 | } |
| 27 | 27 | ||
| @@ -56,41 +56,39 @@ async fn main(spawner: Spawner) { | |||
| 56 | info!("Hello World!"); | 56 | info!("Hello World!"); |
| 57 | 57 | ||
| 58 | let config = Config::default(); | 58 | let config = Config::default(); |
| 59 | let mbox = TlMbox::init(p.IPCC, Irqs, config); | 59 | let mbox = TlMbox::init(p.IPCC, Irqs, config).await; |
| 60 | let mut sys = mbox.sys_subsystem; | ||
| 60 | 61 | ||
| 61 | spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); | 62 | spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); |
| 62 | 63 | ||
| 63 | let sys_event = mbox.sys_subsystem.read().await; | 64 | let result = sys.shci_c2_mac_802_15_4_init().await; |
| 64 | info!("sys event: {}", sys_event.payload()); | ||
| 65 | |||
| 66 | core::mem::drop(sys_event); | ||
| 67 | |||
| 68 | let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; | ||
| 69 | info!("initialized mac: {}", result); | 65 | info!("initialized mac: {}", result); |
| 70 | 66 | ||
| 67 | let (mut mac_rx, mut mac_tx) = mbox.mac_subsystem.split(); | ||
| 68 | |||
| 71 | info!("resetting"); | 69 | info!("resetting"); |
| 72 | mbox.mac_subsystem | 70 | mac_tx |
| 73 | .send_command(&ResetRequest { | 71 | .send_command(&ResetRequest { |
| 74 | set_default_pib: true, | 72 | set_default_pib: true, |
| 75 | ..Default::default() | 73 | ..Default::default() |
| 76 | }) | 74 | }) |
| 77 | .await | 75 | .await |
| 78 | .unwrap(); | 76 | .unwrap(); |
| 79 | defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); | 77 | defmt::info!("{:#x}", mac_rx.read().await.unwrap()); |
| 80 | 78 | ||
| 81 | info!("setting extended address"); | 79 | info!("setting extended address"); |
| 82 | let extended_address: u64 = 0xACDE480000000002; | 80 | let extended_address: u64 = 0xACDE480000000002; |
| 83 | mbox.mac_subsystem | 81 | mac_tx |
| 84 | .send_command(&SetRequest { | 82 | .send_command(&SetRequest { |
| 85 | pib_attribute_ptr: &extended_address as *const _ as *const u8, | 83 | pib_attribute_ptr: &extended_address as *const _ as *const u8, |
| 86 | pib_attribute: PibId::ExtendedAddress, | 84 | pib_attribute: PibId::ExtendedAddress, |
| 87 | }) | 85 | }) |
| 88 | .await | 86 | .await |
| 89 | .unwrap(); | 87 | .unwrap(); |
| 90 | defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); | 88 | defmt::info!("{:#x}", mac_rx.read().await.unwrap()); |
| 91 | 89 | ||
| 92 | info!("getting extended address"); | 90 | info!("getting extended address"); |
| 93 | mbox.mac_subsystem | 91 | mac_tx |
| 94 | .send_command(&GetRequest { | 92 | .send_command(&GetRequest { |
| 95 | pib_attribute: PibId::ExtendedAddress, | 93 | pib_attribute: PibId::ExtendedAddress, |
| 96 | ..Default::default() | 94 | ..Default::default() |
| @@ -99,7 +97,7 @@ async fn main(spawner: Spawner) { | |||
| 99 | .unwrap(); | 97 | .unwrap(); |
| 100 | 98 | ||
| 101 | { | 99 | { |
| 102 | let evt = mbox.mac_subsystem.read().await.unwrap(); | 100 | let evt = mac_rx.read().await.unwrap(); |
| 103 | info!("{:#x}", evt); | 101 | info!("{:#x}", evt); |
| 104 | 102 | ||
| 105 | if let MacEvent::MlmeGetCnf(evt) = evt { | 103 | if let MacEvent::MlmeGetCnf(evt) = evt { |
| @@ -125,9 +123,9 @@ async fn main(spawner: Spawner) { | |||
| 125 | key_index: 152, | 123 | key_index: 152, |
| 126 | }; | 124 | }; |
| 127 | info!("{}", a); | 125 | info!("{}", a); |
| 128 | mbox.mac_subsystem.send_command(&a).await.unwrap(); | 126 | mac_tx.send_command(&a).await.unwrap(); |
| 129 | let short_addr = { | 127 | let short_addr = { |
| 130 | let evt = mbox.mac_subsystem.read().await.unwrap(); | 128 | let evt = mac_rx.read().await.unwrap(); |
| 131 | info!("{:#x}", evt); | 129 | info!("{:#x}", evt); |
| 132 | 130 | ||
| 133 | if let MacEvent::MlmeAssociateCnf(conf) = evt { | 131 | if let MacEvent::MlmeAssociateCnf(conf) = evt { |
| @@ -138,7 +136,7 @@ async fn main(spawner: Spawner) { | |||
| 138 | }; | 136 | }; |
| 139 | 137 | ||
| 140 | info!("setting short address"); | 138 | info!("setting short address"); |
| 141 | mbox.mac_subsystem | 139 | mac_tx |
| 142 | .send_command(&SetRequest { | 140 | .send_command(&SetRequest { |
| 143 | pib_attribute_ptr: &short_addr as *const _ as *const u8, | 141 | pib_attribute_ptr: &short_addr as *const _ as *const u8, |
| 144 | pib_attribute: PibId::ShortAddress, | 142 | pib_attribute: PibId::ShortAddress, |
| @@ -146,13 +144,13 @@ async fn main(spawner: Spawner) { | |||
| 146 | .await | 144 | .await |
| 147 | .unwrap(); | 145 | .unwrap(); |
| 148 | { | 146 | { |
| 149 | let evt = mbox.mac_subsystem.read().await.unwrap(); | 147 | let evt = mac_rx.read().await.unwrap(); |
| 150 | info!("{:#x}", evt); | 148 | info!("{:#x}", evt); |
| 151 | } | 149 | } |
| 152 | 150 | ||
| 153 | info!("sending data"); | 151 | info!("sending data"); |
| 154 | let data = b"Hello from embassy!"; | 152 | let data = b"Hello from embassy!"; |
| 155 | mbox.mac_subsystem | 153 | mac_tx |
| 156 | .send_command( | 154 | .send_command( |
| 157 | DataRequest { | 155 | DataRequest { |
| 158 | src_addr_mode: AddressMode::Short, | 156 | src_addr_mode: AddressMode::Short, |
| @@ -170,12 +168,12 @@ async fn main(spawner: Spawner) { | |||
| 170 | .await | 168 | .await |
| 171 | .unwrap(); | 169 | .unwrap(); |
| 172 | { | 170 | { |
| 173 | let evt = mbox.mac_subsystem.read().await.unwrap(); | 171 | let evt = mac_rx.read().await.unwrap(); |
| 174 | info!("{:#x}", evt); | 172 | info!("{:#x}", evt); |
| 175 | } | 173 | } |
| 176 | 174 | ||
| 177 | loop { | 175 | loop { |
| 178 | match mbox.mac_subsystem.read().await { | 176 | match mac_rx.read().await { |
| 179 | Ok(evt) => info!("{:#x}", evt), | 177 | Ok(evt) => info!("{:#x}", evt), |
| 180 | _ => continue, | 178 | _ => continue, |
| 181 | }; | 179 | }; |
diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index 4e7f2304d..0902e28e8 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs | |||
| @@ -46,7 +46,7 @@ async fn main(_spawner: Spawner) { | |||
| 46 | info!("Hello World!"); | 46 | info!("Hello World!"); |
| 47 | 47 | ||
| 48 | let config = Config::default(); | 48 | let config = Config::default(); |
| 49 | let mbox = TlMbox::init(p.IPCC, Irqs, config); | 49 | let mbox = TlMbox::init(p.IPCC, Irqs, config).await; |
| 50 | 50 | ||
| 51 | loop { | 51 | loop { |
| 52 | let wireless_fw_info = mbox.sys_subsystem.wireless_fw_info(); | 52 | let wireless_fw_info = mbox.sys_subsystem.wireless_fw_info(); |
diff --git a/examples/stm32wb/src/bin/tl_mbox_ble.rs b/examples/stm32wb/src/bin/tl_mbox_ble.rs index 72a4c18e6..763dc32cd 100644 --- a/examples/stm32wb/src/bin/tl_mbox_ble.rs +++ b/examples/stm32wb/src/bin/tl_mbox_ble.rs | |||
| @@ -7,6 +7,7 @@ use embassy_stm32::bind_interrupts; | |||
| 7 | use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; | 7 | use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; |
| 8 | use embassy_stm32::rcc::WPAN_DEFAULT; | 8 | use embassy_stm32::rcc::WPAN_DEFAULT; |
| 9 | use embassy_stm32_wpan::TlMbox; | 9 | use embassy_stm32_wpan::TlMbox; |
| 10 | use embassy_stm32_wpan::sub::mm; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 12 | ||
| 12 | bind_interrupts!(struct Irqs{ | 13 | bind_interrupts!(struct Irqs{ |
| @@ -14,8 +15,13 @@ bind_interrupts!(struct Irqs{ | |||
| 14 | IPCC_C1_TX => TransmitInterruptHandler; | 15 | IPCC_C1_TX => TransmitInterruptHandler; |
| 15 | }); | 16 | }); |
| 16 | 17 | ||
| 18 | #[embassy_executor::task] | ||
| 19 | async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) { | ||
| 20 | memory_manager.run_queue().await; | ||
| 21 | } | ||
| 22 | |||
| 17 | #[embassy_executor::main] | 23 | #[embassy_executor::main] |
| 18 | async fn main(_spawner: Spawner) { | 24 | async fn main(spawner: Spawner) { |
| 19 | /* | 25 | /* |
| 20 | How to make this work: | 26 | How to make this work: |
| 21 | 27 | ||
| @@ -45,18 +51,19 @@ async fn main(_spawner: Spawner) { | |||
| 45 | info!("Hello World!"); | 51 | info!("Hello World!"); |
| 46 | 52 | ||
| 47 | let config = Config::default(); | 53 | let config = Config::default(); |
| 48 | let mbox = TlMbox::init(p.IPCC, Irqs, config); | 54 | let mbox = TlMbox::init(p.IPCC, Irqs, config).await; |
| 55 | let mut sys = mbox.sys_subsystem; | ||
| 56 | let mut ble = mbox.ble_subsystem; | ||
| 49 | 57 | ||
| 50 | let sys_event = mbox.sys_subsystem.read().await; | 58 | spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); |
| 51 | info!("sys event: {}", sys_event.payload()); | ||
| 52 | 59 | ||
| 53 | let _ = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; | 60 | let _ = sys.shci_c2_ble_init(Default::default()).await; |
| 54 | 61 | ||
| 55 | info!("starting ble..."); | 62 | info!("starting ble..."); |
| 56 | mbox.ble_subsystem.tl_write(0x0c, &[]).await; | 63 | ble.tl_write(0x0c, &[]).await; |
| 57 | 64 | ||
| 58 | info!("waiting for ble..."); | 65 | info!("waiting for ble..."); |
| 59 | let ble_event = mbox.ble_subsystem.tl_read().await; | 66 | let ble_event = ble.tl_read().await; |
| 60 | 67 | ||
| 61 | info!("ble event: {}", ble_event.payload()); | 68 | info!("ble event: {}", ble_event.payload()); |
| 62 | 69 | ||
diff --git a/examples/stm32wb/src/bin/tl_mbox_mac.rs b/examples/stm32wb/src/bin/tl_mbox_mac.rs index 16d0a1527..235a48241 100644 --- a/examples/stm32wb/src/bin/tl_mbox_mac.rs +++ b/examples/stm32wb/src/bin/tl_mbox_mac.rs | |||
| @@ -16,7 +16,7 @@ bind_interrupts!(struct Irqs{ | |||
| 16 | }); | 16 | }); |
| 17 | 17 | ||
| 18 | #[embassy_executor::task] | 18 | #[embassy_executor::task] |
| 19 | async fn run_mm_queue(memory_manager: mm::MemoryManager) { | 19 | async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) { |
| 20 | memory_manager.run_queue().await; | 20 | memory_manager.run_queue().await; |
| 21 | } | 21 | } |
| 22 | 22 | ||
| @@ -51,16 +51,12 @@ async fn main(spawner: Spawner) { | |||
| 51 | info!("Hello World!"); | 51 | info!("Hello World!"); |
| 52 | 52 | ||
| 53 | let config = Config::default(); | 53 | let config = Config::default(); |
| 54 | let mbox = TlMbox::init(p.IPCC, Irqs, config); | 54 | let mbox = TlMbox::init(p.IPCC, Irqs, config).await; |
| 55 | let mut sys = mbox.sys_subsystem; | ||
| 55 | 56 | ||
| 56 | spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); | 57 | spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); |
| 57 | 58 | ||
| 58 | let sys_event = mbox.sys_subsystem.read().await; | 59 | let result = sys.shci_c2_mac_802_15_4_init().await; |
| 59 | info!("sys event: {}", sys_event.payload()); | ||
| 60 | |||
| 61 | core::mem::drop(sys_event); | ||
| 62 | |||
| 63 | let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; | ||
| 64 | info!("initialized mac: {}", result); | 60 | info!("initialized mac: {}", result); |
| 65 | 61 | ||
| 66 | // | 62 | // |
diff --git a/examples/stm32wba/src/bin/adc.rs b/examples/stm32wba/src/bin/adc.rs index 177aab3f3..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,11 +12,11 @@ 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 | 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 | ||
| @@ -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/stm32wba/src/bin/button_exti.rs b/examples/stm32wba/src/bin/button_exti.rs index 34a08bbc6..d63290d42 100644 --- a/examples/stm32wba/src/bin/button_exti.rs +++ b/examples/stm32wba/src/bin/button_exti.rs | |||
| @@ -3,16 +3,22 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::exti::ExtiInput; | 6 | use embassy_stm32::exti::{self, ExtiInput}; |
| 7 | use embassy_stm32::gpio::Pull; | 7 | use embassy_stm32::gpio::Pull; |
| 8 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 11 | bind_interrupts!( | ||
| 12 | pub struct Irqs{ | ||
| 13 | EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>; | ||
| 14 | }); | ||
| 15 | |||
| 10 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_stm32::init(Default::default()); | 18 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | 19 | info!("Hello World!"); |
| 14 | 20 | ||
| 15 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); | 21 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); |
| 16 | 22 | ||
| 17 | info!("Press the USER button..."); | 23 | info!("Press the USER button..."); |
| 18 | 24 | ||
diff --git a/examples/stm32wba6/src/bin/adc.rs b/examples/stm32wba6/src/bin/adc.rs index 0887e124c..51dcff57a 100644 --- a/examples/stm32wba6/src/bin/adc.rs +++ b/examples/stm32wba6/src/bin/adc.rs | |||
| @@ -2,21 +2,47 @@ | |||
| 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::Config; |
| 6 | use embassy_stm32::adc::{Adc, AdcChannel, SampleTime, adc4}; | ||
| 7 | use embassy_stm32::rcc::{ | ||
| 8 | AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, | ||
| 9 | }; | ||
| 6 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 7 | 11 | ||
| 8 | #[embassy_executor::main] | 12 | #[embassy_executor::main] |
| 9 | async fn main(_spawner: embassy_executor::Spawner) { | 13 | async fn main(_spawner: embassy_executor::Spawner) { |
| 10 | let config = embassy_stm32::Config::default(); | 14 | let mut config = Config::default(); |
| 15 | // Fine-tune PLL1 dividers/multipliers | ||
| 16 | config.rcc.pll1 = Some(embassy_stm32::rcc::Pll { | ||
| 17 | source: PllSource::HSI, | ||
| 18 | prediv: PllPreDiv::DIV1, // PLLM = 1 → HSI / 1 = 16 MHz | ||
| 19 | mul: PllMul::MUL30, // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO | ||
| 20 | divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk) | ||
| 21 | // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED) | ||
| 22 | divq: None, | ||
| 23 | divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG) | ||
| 24 | frac: Some(0), // Fractional part (enabled) | ||
| 25 | }); | ||
| 26 | |||
| 27 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 28 | config.rcc.apb1_pre = APBPrescaler::DIV1; | ||
| 29 | config.rcc.apb2_pre = APBPrescaler::DIV1; | ||
| 30 | config.rcc.apb7_pre = APBPrescaler::DIV1; | ||
| 31 | config.rcc.ahb5_pre = AHB5Prescaler::DIV4; | ||
| 32 | |||
| 33 | // voltage scale for max performance | ||
| 34 | config.rcc.voltage_scale = VoltageScale::RANGE1; | ||
| 35 | // route PLL1_P into the USB‐OTG‐HS block | ||
| 36 | config.rcc.sys = Sysclk::PLL1_R; | ||
| 11 | 37 | ||
| 12 | let mut p = embassy_stm32::init(config); | 38 | let mut p = embassy_stm32::init(config); |
| 13 | 39 | ||
| 14 | // **** ADC4 init **** | 40 | // **** ADC4 init **** |
| 15 | let mut adc4 = adc4::Adc4::new(p.ADC4); | 41 | let mut adc4 = Adc::new_adc4(p.ADC4); |
| 16 | let mut adc4_pin1 = p.PA0; // A4 | 42 | let mut adc4_pin1 = p.PA0; // A4 |
| 17 | let mut adc4_pin2 = p.PA1; // A5 | 43 | let mut adc4_pin2 = p.PA1; // A5 |
| 18 | adc4.set_resolution(adc4::Resolution::BITS12); | 44 | adc4.set_resolution_adc4(adc4::Resolution::BITS12); |
| 19 | adc4.set_averaging(adc4::Averaging::Samples256); | 45 | adc4.set_averaging_adc4(adc4::Averaging::Samples256); |
| 20 | let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); | 46 | let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); |
| 21 | 47 | ||
| 22 | // **** ADC4 blocking read **** | 48 | // **** ADC4 blocking read **** |
| @@ -36,11 +62,14 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 36 | // The channels must be in ascending order and can't repeat for ADC4 | 62 | // The channels must be in ascending order and can't repeat for ADC4 |
| 37 | adc4.read( | 63 | adc4.read( |
| 38 | p.GPDMA1_CH1.reborrow(), | 64 | p.GPDMA1_CH1.reborrow(), |
| 39 | [&mut degraded42, &mut degraded41].into_iter(), | 65 | [ |
| 66 | (&mut degraded42, SampleTime::CYCLES12_5), | ||
| 67 | (&mut degraded41, SampleTime::CYCLES12_5), | ||
| 68 | ] | ||
| 69 | .into_iter(), | ||
| 40 | &mut measurements, | 70 | &mut measurements, |
| 41 | ) | 71 | ) |
| 42 | .await | 72 | .await; |
| 43 | .unwrap(); | ||
| 44 | let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32; | 73 | let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32; |
| 45 | let volt1: f32 = 3.0 * measurements[1] as f32 / max4 as f32; | 74 | let volt1: f32 = 3.0 * measurements[1] as f32 / max4 as f32; |
| 46 | info!("Async read 4 pin 1 {}", volt1); | 75 | info!("Async read 4 pin 1 {}", volt1); |
diff --git a/examples/stm32wba6/src/bin/button_exti.rs b/examples/stm32wba6/src/bin/button_exti.rs index 34a08bbc6..d63290d42 100644 --- a/examples/stm32wba6/src/bin/button_exti.rs +++ b/examples/stm32wba6/src/bin/button_exti.rs | |||
| @@ -3,16 +3,22 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::exti::ExtiInput; | 6 | use embassy_stm32::exti::{self, ExtiInput}; |
| 7 | use embassy_stm32::gpio::Pull; | 7 | use embassy_stm32::gpio::Pull; |
| 8 | use embassy_stm32::{bind_interrupts, interrupt}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 11 | bind_interrupts!( | ||
| 12 | pub struct Irqs{ | ||
| 13 | EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>; | ||
| 14 | }); | ||
| 15 | |||
| 10 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_stm32::init(Default::default()); | 18 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | 19 | info!("Hello World!"); |
| 14 | 20 | ||
| 15 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); | 21 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); |
| 16 | 22 | ||
| 17 | info!("Press the USER button..."); | 23 | info!("Press the USER button..."); |
| 18 | 24 | ||
diff --git a/examples/stm32wl/src/bin/button_exti.rs b/examples/stm32wl/src/bin/button_exti.rs index 953b13bac..2bb39c709 100644 --- a/examples/stm32wl/src/bin/button_exti.rs +++ b/examples/stm32wl/src/bin/button_exti.rs | |||
| @@ -5,11 +5,16 @@ use core::mem::MaybeUninit; | |||
| 5 | 5 | ||
| 6 | use defmt::*; | 6 | use defmt::*; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 8 | use embassy_stm32::SharedData; | 8 | use embassy_stm32::exti::{self, ExtiInput}; |
| 9 | use embassy_stm32::exti::ExtiInput; | ||
| 10 | use embassy_stm32::gpio::Pull; | 9 | use embassy_stm32::gpio::Pull; |
| 10 | use embassy_stm32::{SharedData, bind_interrupts, interrupt}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 12 | ||
| 13 | bind_interrupts!( | ||
| 14 | pub struct Irqs{ | ||
| 15 | EXTI0 => exti::InterruptHandler<interrupt::typelevel::EXTI0>; | ||
| 16 | }); | ||
| 17 | |||
| 13 | #[unsafe(link_section = ".shared_data")] | 18 | #[unsafe(link_section = ".shared_data")] |
| 14 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); | 19 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); |
| 15 | 20 | ||
| @@ -18,7 +23,7 @@ async fn main(_spawner: Spawner) { | |||
| 18 | let p = embassy_stm32::init_primary(Default::default(), &SHARED_DATA); | 23 | let p = embassy_stm32::init_primary(Default::default(), &SHARED_DATA); |
| 19 | info!("Hello World!"); | 24 | info!("Hello World!"); |
| 20 | 25 | ||
| 21 | let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Up); | 26 | let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Up, Irqs); |
| 22 | 27 | ||
| 23 | info!("Press the USER button..."); | 28 | info!("Press the USER button..."); |
| 24 | 29 | ||
diff --git a/examples/stm32wle5/src/bin/adc.rs b/examples/stm32wle5/src/bin/adc.rs index 4e0574d97..ea91fb063 100644 --- a/examples/stm32wle5/src/bin/adc.rs +++ b/examples/stm32wle5/src/bin/adc.rs | |||
| @@ -6,20 +6,12 @@ use defmt::*; | |||
| 6 | use defmt_rtt as _; | 6 | use defmt_rtt as _; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 8 | use embassy_stm32::adc::{Adc, SampleTime}; | 8 | use embassy_stm32::adc::{Adc, SampleTime}; |
| 9 | use embassy_stm32::low_power::Executor; | 9 | use embassy_stm32::low_power; |
| 10 | use embassy_time::Timer; | 10 | use embassy_time::Timer; |
| 11 | use panic_probe as _; | 11 | use panic_probe as _; |
| 12 | use static_cell::StaticCell; | 12 | use static_cell::StaticCell; |
| 13 | 13 | ||
| 14 | #[cortex_m_rt::entry] | 14 | #[embassy_executor::main(executor = "low_power::Executor")] |
| 15 | fn main() -> ! { | ||
| 16 | info!("main: Starting!"); | ||
| 17 | Executor::take().run(|spawner| { | ||
| 18 | spawner.spawn(unwrap!(async_main(spawner))); | ||
| 19 | }); | ||
| 20 | } | ||
| 21 | |||
| 22 | #[embassy_executor::task] | ||
| 23 | async fn async_main(_spawner: Spawner) { | 15 | async fn async_main(_spawner: Spawner) { |
| 24 | let mut config = embassy_stm32::Config::default(); | 16 | let mut config = embassy_stm32::Config::default(); |
| 25 | // enable HSI clock | 17 | // enable HSI clock |
diff --git a/examples/stm32wle5/src/bin/blinky.rs b/examples/stm32wle5/src/bin/blinky.rs index b2745fdaf..9f0c04672 100644 --- a/examples/stm32wle5/src/bin/blinky.rs +++ b/examples/stm32wle5/src/bin/blinky.rs | |||
| @@ -6,20 +6,12 @@ use defmt::*; | |||
| 6 | use defmt_rtt as _; | 6 | use defmt_rtt as _; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 8 | use embassy_stm32::gpio::{Level, Output, Speed}; | 8 | use embassy_stm32::gpio::{Level, Output, Speed}; |
| 9 | use embassy_stm32::low_power::Executor; | 9 | use embassy_stm32::low_power; |
| 10 | use embassy_time::Timer; | 10 | use embassy_time::Timer; |
| 11 | use panic_probe as _; | 11 | use panic_probe as _; |
| 12 | use static_cell::StaticCell; | 12 | use static_cell::StaticCell; |
| 13 | 13 | ||
| 14 | #[cortex_m_rt::entry] | 14 | #[embassy_executor::main(executor = "low_power::Executor")] |
| 15 | fn main() -> ! { | ||
| 16 | info!("main: Starting!"); | ||
| 17 | Executor::take().run(|spawner| { | ||
| 18 | spawner.spawn(unwrap!(async_main(spawner))); | ||
| 19 | }); | ||
| 20 | } | ||
| 21 | |||
| 22 | #[embassy_executor::task] | ||
| 23 | async fn async_main(_spawner: Spawner) { | 15 | async fn async_main(_spawner: Spawner) { |
| 24 | let mut config = embassy_stm32::Config::default(); | 16 | let mut config = embassy_stm32::Config::default(); |
| 25 | // enable HSI clock | 17 | // enable HSI clock |
diff --git a/examples/stm32wle5/src/bin/button_exti.rs b/examples/stm32wle5/src/bin/button_exti.rs index db1bff0be..f248b6147 100644 --- a/examples/stm32wle5/src/bin/button_exti.rs +++ b/examples/stm32wle5/src/bin/button_exti.rs | |||
| @@ -5,21 +5,18 @@ use defmt::*; | |||
| 5 | #[cfg(feature = "defmt-rtt")] | 5 | #[cfg(feature = "defmt-rtt")] |
| 6 | use defmt_rtt as _; | 6 | use defmt_rtt as _; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 8 | use embassy_stm32::exti::ExtiInput; | 8 | use embassy_stm32::exti::{self, ExtiInput}; |
| 9 | use embassy_stm32::gpio::Pull; | 9 | use embassy_stm32::gpio::Pull; |
| 10 | use embassy_stm32::low_power::Executor; | 10 | use embassy_stm32::{bind_interrupts, interrupt, low_power}; |
| 11 | use panic_probe as _; | 11 | use panic_probe as _; |
| 12 | use static_cell::StaticCell; | 12 | use static_cell::StaticCell; |
| 13 | 13 | ||
| 14 | #[cortex_m_rt::entry] | 14 | bind_interrupts!( |
| 15 | fn main() -> ! { | 15 | pub struct Irqs{ |
| 16 | info!("main: Starting!"); | 16 | EXTI0 => exti::InterruptHandler<interrupt::typelevel::EXTI0>; |
| 17 | Executor::take().run(|spawner| { | 17 | }); |
| 18 | spawner.spawn(unwrap!(async_main(spawner))); | ||
| 19 | }); | ||
| 20 | } | ||
| 21 | 18 | ||
| 22 | #[embassy_executor::task] | 19 | #[embassy_executor::main(executor = "low_power::Executor")] |
| 23 | async fn async_main(_spawner: Spawner) { | 20 | async fn async_main(_spawner: Spawner) { |
| 24 | let mut config = embassy_stm32::Config::default(); | 21 | let mut config = embassy_stm32::Config::default(); |
| 25 | // enable HSI clock | 22 | // enable HSI clock |
| @@ -72,7 +69,7 @@ async fn async_main(_spawner: Spawner) { | |||
| 72 | 69 | ||
| 73 | info!("Hello World!"); | 70 | info!("Hello World!"); |
| 74 | 71 | ||
| 75 | let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Up); | 72 | let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Up, Irqs); |
| 76 | 73 | ||
| 77 | info!("Press the USER button..."); | 74 | info!("Press the USER button..."); |
| 78 | 75 | ||
diff --git a/examples/stm32wle5/src/bin/i2c.rs b/examples/stm32wle5/src/bin/i2c.rs index c31c673c9..68c17a672 100644 --- a/examples/stm32wle5/src/bin/i2c.rs +++ b/examples/stm32wle5/src/bin/i2c.rs | |||
| @@ -6,9 +6,8 @@ use defmt::*; | |||
| 6 | use defmt_rtt as _; | 6 | use defmt_rtt as _; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 8 | use embassy_stm32::i2c::I2c; | 8 | use embassy_stm32::i2c::I2c; |
| 9 | use embassy_stm32::low_power::Executor; | ||
| 10 | use embassy_stm32::time::Hertz; | 9 | use embassy_stm32::time::Hertz; |
| 11 | use embassy_stm32::{bind_interrupts, i2c, peripherals}; | 10 | use embassy_stm32::{bind_interrupts, i2c, low_power, peripherals}; |
| 12 | use embassy_time::{Duration, Timer}; | 11 | use embassy_time::{Duration, Timer}; |
| 13 | use panic_probe as _; | 12 | use panic_probe as _; |
| 14 | use static_cell::StaticCell; | 13 | use static_cell::StaticCell; |
| @@ -18,15 +17,7 @@ bind_interrupts!(struct IrqsI2C{ | |||
| 18 | I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>; | 17 | I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>; |
| 19 | }); | 18 | }); |
| 20 | 19 | ||
| 21 | #[cortex_m_rt::entry] | 20 | #[embassy_executor::main(executor = "low_power::Executor")] |
| 22 | fn main() -> ! { | ||
| 23 | info!("main: Starting!"); | ||
| 24 | Executor::take().run(|spawner| { | ||
| 25 | spawner.spawn(unwrap!(async_main(spawner))); | ||
| 26 | }); | ||
| 27 | } | ||
| 28 | |||
| 29 | #[embassy_executor::task] | ||
| 30 | async fn async_main(_spawner: Spawner) { | 21 | async fn async_main(_spawner: Spawner) { |
| 31 | let mut config = embassy_stm32::Config::default(); | 22 | let mut config = embassy_stm32::Config::default(); |
| 32 | // enable HSI clock | 23 | // enable HSI clock |
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index b92b47be2..496a9de18 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml | |||
| @@ -31,9 +31,9 @@ stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng", "dual- | |||
| 31 | stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash", "dual-bank"] | 31 | stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash", "dual-bank"] |
| 32 | stm32u585ai = ["embassy-stm32/stm32u585ai", "spi-v345", "chrono", "rng", "hash", "cordic"] | 32 | stm32u585ai = ["embassy-stm32/stm32u585ai", "spi-v345", "chrono", "rng", "hash", "cordic"] |
| 33 | stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "spi-v345", "chrono", "rng", "hash"] # FIXME: cordic test cause it crash | 33 | stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "spi-v345", "chrono", "rng", "hash"] # FIXME: cordic test cause it crash |
| 34 | stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"] | 34 | stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng", "hsem", "stop"] |
| 35 | stm32wba52cg = ["embassy-stm32/stm32wba52cg", "spi-v345", "chrono", "rng", "hash"] | 35 | stm32wba52cg = ["embassy-stm32/stm32wba52cg", "spi-v345", "chrono", "rng", "hash", "adc"] |
| 36 | stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"] | 36 | stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono", "hsem"] |
| 37 | stm32f091rc = ["embassy-stm32/stm32f091rc", "cm0", "not-gpdma", "chrono"] | 37 | stm32f091rc = ["embassy-stm32/stm32f091rc", "cm0", "not-gpdma", "chrono"] |
| 38 | stm32h503rb = ["embassy-stm32/stm32h503rb", "spi-v345", "rng", "stop"] | 38 | stm32h503rb = ["embassy-stm32/stm32h503rb", "spi-v345", "rng", "stop"] |
| 39 | stm32h7s3l8 = ["embassy-stm32/stm32h7s3l8", "spi-v345", "rng", "cordic", "hash-v34"] # TODO: fdcan crashes, cryp dma hangs. | 39 | stm32h7s3l8 = ["embassy-stm32/stm32h7s3l8", "spi-v345", "rng", "cordic", "hash-v34"] # TODO: fdcan crashes, cryp dma hangs. |
| @@ -56,8 +56,10 @@ mac = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/mac"] | |||
| 56 | embassy-stm32-wpan = [] | 56 | embassy-stm32-wpan = [] |
| 57 | not-gpdma = [] | 57 | not-gpdma = [] |
| 58 | dac = [] | 58 | dac = [] |
| 59 | adc = [] | ||
| 59 | ucpd = [] | 60 | ucpd = [] |
| 60 | cordic = ["dep:num-traits"] | 61 | cordic = ["dep:num-traits"] |
| 62 | hsem = [] | ||
| 61 | dual-bank = ["embassy-stm32/dual-bank"] | 63 | dual-bank = ["embassy-stm32/dual-bank"] |
| 62 | single-bank = ["embassy-stm32/single-bank"] | 64 | single-bank = ["embassy-stm32/single-bank"] |
| 63 | eeprom = [] | 65 | eeprom = [] |
| @@ -110,6 +112,11 @@ path = "src/bin/afio.rs" | |||
| 110 | required-features = [ "afio",] | 112 | required-features = [ "afio",] |
| 111 | 113 | ||
| 112 | [[bin]] | 114 | [[bin]] |
| 115 | name = "adc" | ||
| 116 | path = "src/bin/adc.rs" | ||
| 117 | required-features = [ "adc",] | ||
| 118 | |||
| 119 | [[bin]] | ||
| 113 | name = "can" | 120 | name = "can" |
| 114 | path = "src/bin/can.rs" | 121 | path = "src/bin/can.rs" |
| 115 | required-features = [ "can",] | 122 | required-features = [ "can",] |
| @@ -224,6 +231,11 @@ name = "wpan_mac" | |||
| 224 | path = "src/bin/wpan_mac.rs" | 231 | path = "src/bin/wpan_mac.rs" |
| 225 | required-features = [ "mac",] | 232 | required-features = [ "mac",] |
| 226 | 233 | ||
| 234 | [[bin]] | ||
| 235 | name = "hsem" | ||
| 236 | path = "src/bin/hsem.rs" | ||
| 237 | required-features = [ "hsem",] | ||
| 238 | |||
| 227 | # END TESTS | 239 | # END TESTS |
| 228 | 240 | ||
| 229 | [profile.dev] | 241 | [profile.dev] |
diff --git a/tests/stm32/src/bin/adc.rs b/tests/stm32/src/bin/adc.rs new file mode 100644 index 000000000..6cedc6498 --- /dev/null +++ b/tests/stm32/src/bin/adc.rs | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | // required-features: dac | ||
| 5 | |||
| 6 | #[path = "../common.rs"] | ||
| 7 | mod common; | ||
| 8 | |||
| 9 | use common::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_stm32::adc::{Adc, SampleTime}; | ||
| 12 | use embassy_time::Timer; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | #[embassy_executor::main] | ||
| 16 | async fn main(_spawner: Spawner) { | ||
| 17 | // Initialize the board and obtain a Peripherals instance | ||
| 18 | let p: embassy_stm32::Peripherals = init(); | ||
| 19 | |||
| 20 | let adc = peri!(p, ADC); | ||
| 21 | let mut adc_pin = peri!(p, DAC_PIN); | ||
| 22 | |||
| 23 | let mut adc = Adc::new_adc4(adc); | ||
| 24 | |||
| 25 | // Now wait a little to obtain a stable value | ||
| 26 | Timer::after_millis(30).await; | ||
| 27 | let _ = adc.blocking_read(&mut adc_pin, SampleTime::from_bits(0)); | ||
| 28 | |||
| 29 | for _ in 0..=255 { | ||
| 30 | // Now wait a little to obtain a stable value | ||
| 31 | Timer::after_millis(30).await; | ||
| 32 | |||
| 33 | // Need to steal the peripherals here because PA4 is obviously in use already | ||
| 34 | let _ = adc.blocking_read(&mut adc_pin, SampleTime::from_bits(0)); | ||
| 35 | } | ||
| 36 | |||
| 37 | info!("Test OK"); | ||
| 38 | cortex_m::asm::bkpt(); | ||
| 39 | } | ||
diff --git a/tests/stm32/src/bin/eth.rs b/tests/stm32/src/bin/eth.rs index a65682a02..ffc76b96f 100644 --- a/tests/stm32/src/bin/eth.rs +++ b/tests/stm32/src/bin/eth.rs | |||
| @@ -7,8 +7,8 @@ mod common; | |||
| 7 | use common::*; | 7 | use common::*; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_net::StackResources; | 9 | use embassy_net::StackResources; |
| 10 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; | 10 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma}; |
| 11 | use embassy_stm32::peripherals::ETH; | 11 | use embassy_stm32::peripherals::{ETH, ETH_SMA}; |
| 12 | use embassy_stm32::rng::Rng; | 12 | use embassy_stm32::rng::Rng; |
| 13 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng}; | 13 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng}; |
| 14 | use static_cell::StaticCell; | 14 | use static_cell::StaticCell; |
| @@ -27,7 +27,7 @@ bind_interrupts!(struct Irqs { | |||
| 27 | RNG => rng::InterruptHandler<peripherals::RNG>; | 27 | RNG => rng::InterruptHandler<peripherals::RNG>; |
| 28 | }); | 28 | }); |
| 29 | 29 | ||
| 30 | type Device = Ethernet<'static, ETH, GenericPhy>; | 30 | type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>; |
| 31 | 31 | ||
| 32 | #[embassy_executor::task] | 32 | #[embassy_executor::task] |
| 33 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { | 33 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { |
| @@ -69,13 +69,12 @@ async fn main(spawner: Spawner) { | |||
| 69 | const PACKET_QUEUE_SIZE: usize = 4; | 69 | const PACKET_QUEUE_SIZE: usize = 4; |
| 70 | 70 | ||
| 71 | static PACKETS: StaticCell<PacketQueue<PACKET_QUEUE_SIZE, PACKET_QUEUE_SIZE>> = StaticCell::new(); | 71 | static PACKETS: StaticCell<PacketQueue<PACKET_QUEUE_SIZE, PACKET_QUEUE_SIZE>> = StaticCell::new(); |
| 72 | |||
| 72 | let device = Ethernet::new( | 73 | let device = Ethernet::new( |
| 73 | PACKETS.init(PacketQueue::<PACKET_QUEUE_SIZE, PACKET_QUEUE_SIZE>::new()), | 74 | PACKETS.init(PacketQueue::<PACKET_QUEUE_SIZE, PACKET_QUEUE_SIZE>::new()), |
| 74 | p.ETH, | 75 | p.ETH, |
| 75 | Irqs, | 76 | Irqs, |
| 76 | p.PA1, | 77 | p.PA1, |
| 77 | p.PA2, | ||
| 78 | p.PC1, | ||
| 79 | p.PA7, | 78 | p.PA7, |
| 80 | p.PC4, | 79 | p.PC4, |
| 81 | p.PC5, | 80 | p.PC5, |
| @@ -85,8 +84,10 @@ async fn main(spawner: Spawner) { | |||
| 85 | #[cfg(feature = "stm32h563zi")] | 84 | #[cfg(feature = "stm32h563zi")] |
| 86 | p.PB15, | 85 | p.PB15, |
| 87 | p.PG11, | 86 | p.PG11, |
| 88 | GenericPhy::new_auto(), | ||
| 89 | mac_addr, | 87 | mac_addr, |
| 88 | p.ETH_SMA, | ||
| 89 | p.PA2, | ||
| 90 | p.PC1, | ||
| 90 | ); | 91 | ); |
| 91 | 92 | ||
| 92 | let config = embassy_net::Config::dhcpv4(Default::default()); | 93 | let config = embassy_net::Config::dhcpv4(Default::default()); |
diff --git a/tests/stm32/src/bin/hsem.rs b/tests/stm32/src/bin/hsem.rs new file mode 100644 index 000000000..fa69f22b2 --- /dev/null +++ b/tests/stm32/src/bin/hsem.rs | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | // required-features: hsem | ||
| 2 | #![no_std] | ||
| 3 | #![no_main] | ||
| 4 | |||
| 5 | #[path = "../common.rs"] | ||
| 6 | mod common; | ||
| 7 | |||
| 8 | use common::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_stm32::bind_interrupts; | ||
| 11 | use embassy_stm32::hsem::{HardwareSemaphore, HardwareSemaphoreInterruptHandler}; | ||
| 12 | use embassy_stm32::peripherals::HSEM; | ||
| 13 | |||
| 14 | bind_interrupts!(struct Irqs{ | ||
| 15 | HSEM => HardwareSemaphoreInterruptHandler<HSEM>; | ||
| 16 | }); | ||
| 17 | |||
| 18 | #[embassy_executor::main] | ||
| 19 | async fn main(_spawner: Spawner) { | ||
| 20 | let p: embassy_stm32::Peripherals = init(); | ||
| 21 | |||
| 22 | let hsem = HardwareSemaphore::new(p.HSEM, Irqs); | ||
| 23 | |||
| 24 | // if hsem.channel_for(SemaphoreNumber::Channel5).is_semaphore_locked() { | ||
| 25 | // defmt::panic!("Semaphore 5 already locked!") | ||
| 26 | // } | ||
| 27 | // | ||
| 28 | // hsem.channel_for(SemaphoreNumber::Channel5).one_step_lock().unwrap(); | ||
| 29 | // hsem.channel_for(SemaphoreNumber::Channel1).two_step_lock(0).unwrap(); | ||
| 30 | // | ||
| 31 | // hsem.channel_for(SemaphoreNumber::Channel5).unlock(0); | ||
| 32 | |||
| 33 | #[cfg(feature = "stm32wb55rg")] | ||
| 34 | let [_channel1, _channel2, mut channel5, _channel6] = hsem.split(); | ||
| 35 | #[cfg(not(feature = "stm32wb55rg"))] | ||
| 36 | let [_channel1, _channel2, _channel3, _channel4, mut channel5, _channel6] = hsem.split(); | ||
| 37 | |||
| 38 | info!("Locking channel 5"); | ||
| 39 | |||
| 40 | let mutex = channel5.lock(0).await; | ||
| 41 | |||
| 42 | info!("Locked channel 5"); | ||
| 43 | |||
| 44 | drop(mutex); | ||
| 45 | |||
| 46 | info!("Unlocked channel 5"); | ||
| 47 | |||
| 48 | info!("Test OK"); | ||
| 49 | cortex_m::asm::bkpt(); | ||
| 50 | } | ||
diff --git a/tests/stm32/src/bin/stop.rs b/tests/stm32/src/bin/stop.rs index 1fe65d867..83c375bc5 100644 --- a/tests/stm32/src/bin/stop.rs +++ b/tests/stm32/src/bin/stop.rs | |||
| @@ -7,21 +7,13 @@ mod common; | |||
| 7 | 7 | ||
| 8 | use chrono::NaiveDate; | 8 | use chrono::NaiveDate; |
| 9 | use common::*; | 9 | use common::*; |
| 10 | use cortex_m_rt::entry; | ||
| 11 | use embassy_executor::Spawner; | 10 | use embassy_executor::Spawner; |
| 12 | use embassy_stm32::Config; | 11 | use embassy_stm32::low_power::{StopMode, stop_ready}; |
| 13 | use embassy_stm32::low_power::{Executor, StopMode, stop_ready}; | ||
| 14 | use embassy_stm32::rcc::LsConfig; | 12 | use embassy_stm32::rcc::LsConfig; |
| 15 | use embassy_stm32::rtc::Rtc; | 13 | use embassy_stm32::rtc::Rtc; |
| 14 | use embassy_stm32::{Config, low_power}; | ||
| 16 | use embassy_time::Timer; | 15 | use embassy_time::Timer; |
| 17 | 16 | ||
| 18 | #[entry] | ||
| 19 | fn main() -> ! { | ||
| 20 | Executor::take().run(|spawner| { | ||
| 21 | spawner.spawn(unwrap!(async_main(spawner))); | ||
| 22 | }); | ||
| 23 | } | ||
| 24 | |||
| 25 | #[embassy_executor::task] | 17 | #[embassy_executor::task] |
| 26 | async fn task_1() { | 18 | async fn task_1() { |
| 27 | for _ in 0..9 { | 19 | for _ in 0..9 { |
| @@ -43,7 +35,7 @@ async fn task_2() { | |||
| 43 | cortex_m::asm::bkpt(); | 35 | cortex_m::asm::bkpt(); |
| 44 | } | 36 | } |
| 45 | 37 | ||
| 46 | #[embassy_executor::task] | 38 | #[embassy_executor::main(executor = "low_power::Executor")] |
| 47 | async fn async_main(spawner: Spawner) { | 39 | async fn async_main(spawner: Spawner) { |
| 48 | let _ = config(); | 40 | let _ = config(); |
| 49 | 41 | ||
diff --git a/tests/stm32/src/bin/wpan_ble.rs b/tests/stm32/src/bin/wpan_ble.rs index 0f396b848..b4c0cbf56 100644 --- a/tests/stm32/src/bin/wpan_ble.rs +++ b/tests/stm32/src/bin/wpan_ble.rs | |||
| @@ -32,7 +32,7 @@ bind_interrupts!(struct Irqs{ | |||
| 32 | const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7; | 32 | const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7; |
| 33 | 33 | ||
| 34 | #[embassy_executor::task] | 34 | #[embassy_executor::task] |
| 35 | async fn run_mm_queue(memory_manager: mm::MemoryManager) { | 35 | async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) { |
| 36 | memory_manager.run_queue().await; | 36 | memory_manager.run_queue().await; |
| 37 | } | 37 | } |
| 38 | 38 | ||
| @@ -45,14 +45,13 @@ async fn main(spawner: Spawner) { | |||
| 45 | info!("Hello World!"); | 45 | info!("Hello World!"); |
| 46 | 46 | ||
| 47 | let config = Config::default(); | 47 | let config = Config::default(); |
| 48 | let mut mbox = TlMbox::init(p.IPCC, Irqs, config); | 48 | let mbox = TlMbox::init(p.IPCC, Irqs, config).await; |
| 49 | let mut sys = mbox.sys_subsystem; | ||
| 50 | let mut ble = mbox.ble_subsystem; | ||
| 49 | 51 | ||
| 50 | spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); | 52 | spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); |
| 51 | 53 | ||
| 52 | let sys_event = mbox.sys_subsystem.read().await; | 54 | let fw_info = sys.wireless_fw_info().unwrap(); |
| 53 | info!("sys event: {}", sys_event.payload()); | ||
| 54 | |||
| 55 | let fw_info = mbox.sys_subsystem.wireless_fw_info().unwrap(); | ||
| 56 | let version_major = fw_info.version_major(); | 55 | let version_major = fw_info.version_major(); |
| 57 | let version_minor = fw_info.version_minor(); | 56 | let version_minor = fw_info.version_minor(); |
| 58 | let subversion = fw_info.subversion(); | 57 | let subversion = fw_info.subversion(); |
| @@ -65,121 +64,108 @@ async fn main(spawner: Spawner) { | |||
| 65 | version_major, version_minor, subversion, sram2a_size, sram2b_size | 64 | version_major, version_minor, subversion, sram2a_size, sram2b_size |
| 66 | ); | 65 | ); |
| 67 | 66 | ||
| 68 | let _ = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; | 67 | let _ = sys.shci_c2_ble_init(Default::default()).await; |
| 69 | 68 | ||
| 70 | info!("resetting BLE..."); | 69 | info!("resetting BLE..."); |
| 71 | mbox.ble_subsystem.reset().await; | 70 | ble.reset().await; |
| 72 | let response = mbox.ble_subsystem.read().await.unwrap(); | 71 | let response = ble.read().await.unwrap(); |
| 73 | info!("{}", response); | 72 | info!("{}", response); |
| 74 | 73 | ||
| 75 | info!("config public address..."); | 74 | info!("config public address..."); |
| 76 | mbox.ble_subsystem | 75 | ble.write_config_data(&ConfigData::public_address(get_bd_addr()).build()) |
| 77 | .write_config_data(&ConfigData::public_address(get_bd_addr()).build()) | ||
| 78 | .await; | 76 | .await; |
| 79 | let response = mbox.ble_subsystem.read().await.unwrap(); | 77 | let response = ble.read().await.unwrap(); |
| 80 | info!("{}", response); | 78 | info!("{}", response); |
| 81 | 79 | ||
| 82 | info!("config random address..."); | 80 | info!("config random address..."); |
| 83 | mbox.ble_subsystem | 81 | ble.write_config_data(&ConfigData::random_address(get_random_addr()).build()) |
| 84 | .write_config_data(&ConfigData::random_address(get_random_addr()).build()) | ||
| 85 | .await; | 82 | .await; |
| 86 | let response = mbox.ble_subsystem.read().await.unwrap(); | 83 | let response = ble.read().await.unwrap(); |
| 87 | info!("{}", response); | 84 | info!("{}", response); |
| 88 | 85 | ||
| 89 | info!("config identity root..."); | 86 | info!("config identity root..."); |
| 90 | mbox.ble_subsystem | 87 | ble.write_config_data(&ConfigData::identity_root(&get_irk()).build()) |
| 91 | .write_config_data(&ConfigData::identity_root(&get_irk()).build()) | ||
| 92 | .await; | 88 | .await; |
| 93 | let response = mbox.ble_subsystem.read().await.unwrap(); | 89 | let response = ble.read().await.unwrap(); |
| 94 | info!("{}", response); | 90 | info!("{}", response); |
| 95 | 91 | ||
| 96 | info!("config encryption root..."); | 92 | info!("config encryption root..."); |
| 97 | mbox.ble_subsystem | 93 | ble.write_config_data(&ConfigData::encryption_root(&get_erk()).build()) |
| 98 | .write_config_data(&ConfigData::encryption_root(&get_erk()).build()) | ||
| 99 | .await; | 94 | .await; |
| 100 | let response = mbox.ble_subsystem.read().await.unwrap(); | 95 | let response = ble.read().await.unwrap(); |
| 101 | info!("{}", response); | 96 | info!("{}", response); |
| 102 | 97 | ||
| 103 | info!("config tx power level..."); | 98 | info!("config tx power level..."); |
| 104 | mbox.ble_subsystem.set_tx_power_level(PowerLevel::ZerodBm).await; | 99 | ble.set_tx_power_level(PowerLevel::ZerodBm).await; |
| 105 | let response = mbox.ble_subsystem.read().await.unwrap(); | 100 | let response = ble.read().await.unwrap(); |
| 106 | info!("{}", response); | 101 | info!("{}", response); |
| 107 | 102 | ||
| 108 | info!("GATT init..."); | 103 | info!("GATT init..."); |
| 109 | mbox.ble_subsystem.init_gatt().await; | 104 | ble.init_gatt().await; |
| 110 | let response = mbox.ble_subsystem.read().await.unwrap(); | 105 | let response = ble.read().await.unwrap(); |
| 111 | info!("{}", response); | 106 | info!("{}", response); |
| 112 | 107 | ||
| 113 | info!("GAP init..."); | 108 | info!("GAP init..."); |
| 114 | mbox.ble_subsystem | 109 | ble.init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH).await; |
| 115 | .init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH) | 110 | let response = ble.read().await.unwrap(); |
| 116 | .await; | ||
| 117 | let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 118 | info!("{}", response); | 111 | info!("{}", response); |
| 119 | 112 | ||
| 120 | // info!("set scan response..."); | 113 | // info!("set scan response..."); |
| 121 | // mbox.ble_subsystem.le_set_scan_response_data(&[]).await.unwrap(); | 114 | // ble.le_set_scan_response_data(&[]).await.unwrap(); |
| 122 | // let response = mbox.ble_subsystem.read().await.unwrap(); | 115 | // let response = ble.read().await.unwrap(); |
| 123 | // info!("{}", response); | 116 | // info!("{}", response); |
| 124 | 117 | ||
| 125 | info!("set discoverable..."); | 118 | info!("set discoverable..."); |
| 126 | mbox.ble_subsystem | 119 | ble.set_discoverable(&DiscoverableParameters { |
| 127 | .set_discoverable(&DiscoverableParameters { | 120 | advertising_type: AdvertisingType::NonConnectableUndirected, |
| 128 | advertising_type: AdvertisingType::NonConnectableUndirected, | 121 | advertising_interval: Some((Duration::from_millis(250), Duration::from_millis(250))), |
| 129 | advertising_interval: Some((Duration::from_millis(250), Duration::from_millis(250))), | 122 | address_type: OwnAddressType::Public, |
| 130 | address_type: OwnAddressType::Public, | 123 | filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan, |
| 131 | filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan, | 124 | local_name: None, |
| 132 | local_name: None, | 125 | advertising_data: &[], |
| 133 | advertising_data: &[], | 126 | conn_interval: (None, None), |
| 134 | conn_interval: (None, None), | 127 | }) |
| 135 | }) | 128 | .await |
| 136 | .await | 129 | .unwrap(); |
| 137 | .unwrap(); | 130 | |
| 138 | 131 | let response = ble.read().await; | |
| 139 | let response = mbox.ble_subsystem.read().await; | ||
| 140 | info!("{}", response); | 132 | info!("{}", response); |
| 141 | 133 | ||
| 142 | // remove some advertisement to decrease the packet size | 134 | // remove some advertisement to decrease the packet size |
| 143 | info!("delete tx power ad type..."); | 135 | info!("delete tx power ad type..."); |
| 144 | mbox.ble_subsystem | 136 | ble.delete_ad_type(AdvertisingDataType::TxPowerLevel).await; |
| 145 | .delete_ad_type(AdvertisingDataType::TxPowerLevel) | 137 | let response = ble.read().await.unwrap(); |
| 146 | .await; | ||
| 147 | let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 148 | info!("{}", response); | 138 | info!("{}", response); |
| 149 | 139 | ||
| 150 | info!("delete conn interval ad type..."); | 140 | info!("delete conn interval ad type..."); |
| 151 | mbox.ble_subsystem | 141 | ble.delete_ad_type(AdvertisingDataType::PeripheralConnectionInterval) |
| 152 | .delete_ad_type(AdvertisingDataType::PeripheralConnectionInterval) | ||
| 153 | .await; | 142 | .await; |
| 154 | let response = mbox.ble_subsystem.read().await.unwrap(); | 143 | let response = ble.read().await.unwrap(); |
| 155 | info!("{}", response); | 144 | info!("{}", response); |
| 156 | 145 | ||
| 157 | info!("update advertising data..."); | 146 | info!("update advertising data..."); |
| 158 | mbox.ble_subsystem | 147 | ble.update_advertising_data(&eddystone_advertising_data()) |
| 159 | .update_advertising_data(&eddystone_advertising_data()) | ||
| 160 | .await | 148 | .await |
| 161 | .unwrap(); | 149 | .unwrap(); |
| 162 | let response = mbox.ble_subsystem.read().await.unwrap(); | 150 | let response = ble.read().await.unwrap(); |
| 163 | info!("{}", response); | 151 | info!("{}", response); |
| 164 | 152 | ||
| 165 | info!("update advertising data type..."); | 153 | info!("update advertising data type..."); |
| 166 | mbox.ble_subsystem | 154 | ble.update_advertising_data(&[3, AdvertisingDataType::UuidCompleteList16 as u8, 0xaa, 0xfe]) |
| 167 | .update_advertising_data(&[3, AdvertisingDataType::UuidCompleteList16 as u8, 0xaa, 0xfe]) | ||
| 168 | .await | 155 | .await |
| 169 | .unwrap(); | 156 | .unwrap(); |
| 170 | let response = mbox.ble_subsystem.read().await.unwrap(); | 157 | let response = ble.read().await.unwrap(); |
| 171 | info!("{}", response); | 158 | info!("{}", response); |
| 172 | 159 | ||
| 173 | info!("update advertising data flags..."); | 160 | info!("update advertising data flags..."); |
| 174 | mbox.ble_subsystem | 161 | ble.update_advertising_data(&[ |
| 175 | .update_advertising_data(&[ | 162 | 2, |
| 176 | 2, | 163 | AdvertisingDataType::Flags as u8, |
| 177 | AdvertisingDataType::Flags as u8, | 164 | (0x02 | 0x04) as u8, // BLE general discoverable, without BR/EDR support |
| 178 | (0x02 | 0x04) as u8, // BLE general discoverable, without BR/EDR support | 165 | ]) |
| 179 | ]) | 166 | .await |
| 180 | .await | 167 | .unwrap(); |
| 181 | .unwrap(); | 168 | let response = ble.read().await.unwrap(); |
| 182 | let response = mbox.ble_subsystem.read().await.unwrap(); | ||
| 183 | info!("{}", response); | 169 | info!("{}", response); |
| 184 | 170 | ||
| 185 | info!("Test OK"); | 171 | info!("Test OK"); |
diff --git a/tests/stm32/src/bin/wpan_mac.rs b/tests/stm32/src/bin/wpan_mac.rs index f27146c44..42db39e7e 100644 --- a/tests/stm32/src/bin/wpan_mac.rs +++ b/tests/stm32/src/bin/wpan_mac.rs | |||
| @@ -25,7 +25,7 @@ bind_interrupts!(struct Irqs{ | |||
| 25 | }); | 25 | }); |
| 26 | 26 | ||
| 27 | #[embassy_executor::task] | 27 | #[embassy_executor::task] |
| 28 | async fn run_mm_queue(memory_manager: mm::MemoryManager) { | 28 | async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) { |
| 29 | memory_manager.run_queue().await; | 29 | memory_manager.run_queue().await; |
| 30 | } | 30 | } |
| 31 | 31 | ||
| @@ -38,20 +38,17 @@ async fn main(spawner: Spawner) { | |||
| 38 | info!("Hello World!"); | 38 | info!("Hello World!"); |
| 39 | 39 | ||
| 40 | let config = Config::default(); | 40 | let config = Config::default(); |
| 41 | let mbox = TlMbox::init(p.IPCC, Irqs, config); | 41 | let mbox = TlMbox::init(p.IPCC, Irqs, config).await; |
| 42 | let mut sys = mbox.sys_subsystem; | ||
| 43 | let (mut mac_rx, mut mac_tx) = mbox.mac_subsystem.split(); | ||
| 42 | 44 | ||
| 43 | spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); | 45 | spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); |
| 44 | 46 | ||
| 45 | let sys_event = mbox.sys_subsystem.read().await; | 47 | let result = sys.shci_c2_mac_802_15_4_init().await; |
| 46 | info!("sys event: {}", sys_event.payload()); | ||
| 47 | |||
| 48 | core::mem::drop(sys_event); | ||
| 49 | |||
| 50 | let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; | ||
| 51 | info!("initialized mac: {}", result); | 48 | info!("initialized mac: {}", result); |
| 52 | 49 | ||
| 53 | info!("resetting"); | 50 | info!("resetting"); |
| 54 | mbox.mac_subsystem | 51 | mac_tx |
| 55 | .send_command(&ResetRequest { | 52 | .send_command(&ResetRequest { |
| 56 | set_default_pib: true, | 53 | set_default_pib: true, |
| 57 | ..Default::default() | 54 | ..Default::default() |
| @@ -59,13 +56,13 @@ async fn main(spawner: Spawner) { | |||
| 59 | .await | 56 | .await |
| 60 | .unwrap(); | 57 | .unwrap(); |
| 61 | { | 58 | { |
| 62 | let evt = mbox.mac_subsystem.read().await.unwrap(); | 59 | let evt = mac_rx.read().await.unwrap(); |
| 63 | info!("{:#x}", evt); | 60 | info!("{:#x}", evt); |
| 64 | } | 61 | } |
| 65 | 62 | ||
| 66 | info!("setting extended address"); | 63 | info!("setting extended address"); |
| 67 | let extended_address: u64 = 0xACDE480000000002; | 64 | let extended_address: u64 = 0xACDE480000000002; |
| 68 | mbox.mac_subsystem | 65 | mac_tx |
| 69 | .send_command(&SetRequest { | 66 | .send_command(&SetRequest { |
| 70 | pib_attribute_ptr: &extended_address as *const _ as *const u8, | 67 | pib_attribute_ptr: &extended_address as *const _ as *const u8, |
| 71 | pib_attribute: PibId::ExtendedAddress, | 68 | pib_attribute: PibId::ExtendedAddress, |
| @@ -73,12 +70,12 @@ async fn main(spawner: Spawner) { | |||
| 73 | .await | 70 | .await |
| 74 | .unwrap(); | 71 | .unwrap(); |
| 75 | { | 72 | { |
| 76 | let evt = mbox.mac_subsystem.read().await.unwrap(); | 73 | let evt = mac_rx.read().await.unwrap(); |
| 77 | info!("{:#x}", evt); | 74 | info!("{:#x}", evt); |
| 78 | } | 75 | } |
| 79 | 76 | ||
| 80 | info!("getting extended address"); | 77 | info!("getting extended address"); |
| 81 | mbox.mac_subsystem | 78 | mac_tx |
| 82 | .send_command(&GetRequest { | 79 | .send_command(&GetRequest { |
| 83 | pib_attribute: PibId::ExtendedAddress, | 80 | pib_attribute: PibId::ExtendedAddress, |
| 84 | ..Default::default() | 81 | ..Default::default() |
| @@ -87,7 +84,7 @@ async fn main(spawner: Spawner) { | |||
| 87 | .unwrap(); | 84 | .unwrap(); |
| 88 | 85 | ||
| 89 | { | 86 | { |
| 90 | let evt = mbox.mac_subsystem.read().await.unwrap(); | 87 | let evt = mac_rx.read().await.unwrap(); |
| 91 | info!("{:#x}", evt); | 88 | info!("{:#x}", evt); |
| 92 | 89 | ||
| 93 | if let MacEvent::MlmeGetCnf(evt) = evt { | 90 | if let MacEvent::MlmeGetCnf(evt) = evt { |
| @@ -113,8 +110,8 @@ async fn main(spawner: Spawner) { | |||
| 113 | key_index: 152, | 110 | key_index: 152, |
| 114 | }; | 111 | }; |
| 115 | info!("{}", a); | 112 | info!("{}", a); |
| 116 | mbox.mac_subsystem.send_command(&a).await.unwrap(); | 113 | mac_tx.send_command(&a).await.unwrap(); |
| 117 | let short_addr = if let MacEvent::MlmeAssociateCnf(conf) = mbox.mac_subsystem.read().await.unwrap() { | 114 | let short_addr = if let MacEvent::MlmeAssociateCnf(conf) = mac_rx.read().await.unwrap() { |
| 118 | conf.assoc_short_address | 115 | conf.assoc_short_address |
| 119 | } else { | 116 | } else { |
| 120 | defmt::panic!() | 117 | defmt::panic!() |
diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 096cce947..9f88b182a 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs | |||
| @@ -259,6 +259,7 @@ define_peris!( | |||
| 259 | define_peris!( | 259 | define_peris!( |
| 260 | UART = LPUART1, UART_TX = PB5, UART_RX = PA10, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1, | 260 | UART = LPUART1, UART_TX = PB5, UART_RX = PA10, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1, |
| 261 | SPI = SPI1, SPI_SCK = PB4, SPI_MOSI = PA15, SPI_MISO = PB3, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1, | 261 | SPI = SPI1, SPI_SCK = PB4, SPI_MOSI = PA15, SPI_MISO = PB3, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1, |
| 262 | ADC = ADC4, DAC_PIN = PA0, | ||
| 262 | @irq UART = {LPUART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::LPUART1>;}, | 263 | @irq UART = {LPUART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::LPUART1>;}, |
| 263 | ); | 264 | ); |
| 264 | #[cfg(feature = "stm32h7s3l8")] | 265 | #[cfg(feature = "stm32h7s3l8")] |
