aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.vscode/settings.json5
-rwxr-xr-xci.sh2
-rw-r--r--docs/examples/layer-by-layer/blinky-async/src/main.rs10
-rw-r--r--docs/pages/bootloader.adoc6
-rw-r--r--docs/pages/faq.adoc6
-rw-r--r--docs/pages/layer_by_layer.adoc2
-rw-r--r--embassy-boot/CHANGELOG.md3
-rw-r--r--embassy-boot/Cargo.toml14
-rw-r--r--embassy-boot/src/boot_loader.rs12
-rw-r--r--embassy-boot/src/lib.rs4
-rw-r--r--embassy-mspm0/CHANGELOG.md1
-rw-r--r--embassy-mspm0/Cargo.toml5
-rw-r--r--embassy-mspm0/build.rs1
-rw-r--r--embassy-mspm0/src/lib.rs2
-rw-r--r--embassy-mspm0/src/mathacl.rs255
-rw-r--r--embassy-nrf/CHANGELOG.md6
-rw-r--r--embassy-nrf/src/chips/nrf54l15_app.rs2
-rw-r--r--embassy-nrf/src/gpiote.rs68
-rw-r--r--embassy-nrf/src/lib.rs24
-rw-r--r--embassy-nrf/src/spis.rs15
-rw-r--r--embassy-nxp/CHANGELOG.md1
-rw-r--r--embassy-nxp/Cargo.toml4
-rw-r--r--embassy-nxp/build.rs353
-rw-r--r--embassy-nxp/src/chips/lpc55.rs127
-rw-r--r--embassy-nxp/src/chips/mimxrt1011.rs104
-rw-r--r--embassy-nxp/src/chips/mimxrt1062.rs273
-rw-r--r--embassy-nxp/src/dma.rs1
-rw-r--r--embassy-nxp/src/dma/lpc55.rs47
-rw-r--r--embassy-nxp/src/gpio/lpc55.rs93
-rw-r--r--embassy-nxp/src/gpio/rt1xxx.rs50
-rw-r--r--embassy-nxp/src/iomuxc.rs29
-rw-r--r--embassy-nxp/src/lib.rs16
-rw-r--r--embassy-nxp/src/pwm.rs2
-rw-r--r--embassy-nxp/src/pwm/lpc55.rs113
-rw-r--r--embassy-nxp/src/sct.rs56
-rw-r--r--embassy-nxp/src/usart.rs2
-rw-r--r--embassy-nxp/src/usart/lpc55.rs115
-rw-r--r--embassy-rp/CHANGELOG.md3
-rw-r--r--embassy-rp/src/pio/instr.rs16
-rw-r--r--embassy-rp/src/spi.rs25
-rw-r--r--embassy-stm32-wpan/CHANGELOG.md2
-rw-r--r--embassy-stm32-wpan/Cargo.toml4
-rw-r--r--embassy-stm32-wpan/src/channels.rs15
-rw-r--r--embassy-stm32-wpan/src/cmd.rs21
-rw-r--r--embassy-stm32-wpan/src/evt.rs1
-rw-r--r--embassy-stm32-wpan/src/lib.rs46
-rw-r--r--embassy-stm32-wpan/src/mac/commands.rs31
-rw-r--r--embassy-stm32-wpan/src/mac/control.rs185
-rw-r--r--embassy-stm32-wpan/src/mac/driver.rs143
-rw-r--r--embassy-stm32-wpan/src/mac/event.rs6
-rw-r--r--embassy-stm32-wpan/src/mac/indications.rs35
-rw-r--r--embassy-stm32-wpan/src/mac/mod.rs6
-rw-r--r--embassy-stm32-wpan/src/mac/runner.rs158
-rw-r--r--embassy-stm32-wpan/src/mac/typedefs.rs55
-rw-r--r--embassy-stm32-wpan/src/shci.rs152
-rw-r--r--embassy-stm32-wpan/src/sub/ble.rs85
-rw-r--r--embassy-stm32-wpan/src/sub/mac.rs151
-rw-r--r--embassy-stm32-wpan/src/sub/mm.rs37
-rw-r--r--embassy-stm32-wpan/src/sub/sys.rs97
-rw-r--r--embassy-stm32-wpan/src/tables.rs50
-rw-r--r--embassy-stm32/CHANGELOG.md31
-rw-r--r--embassy-stm32/Cargo.toml29
-rw-r--r--embassy-stm32/build.rs188
-rw-r--r--embassy-stm32/src/adc/adc4.rs482
-rw-r--r--embassy-stm32/src/adc/c0.rs493
-rw-r--r--embassy-stm32/src/adc/f1.rs4
-rw-r--r--embassy-stm32/src/adc/f3.rs2
-rw-r--r--embassy-stm32/src/adc/g4.rs716
-rw-r--r--embassy-stm32/src/adc/injected.rs6
-rw-r--r--embassy-stm32/src/adc/mod.rs301
-rw-r--r--embassy-stm32/src/adc/ringbuffered.rs14
-rw-r--r--embassy-stm32/src/adc/v2.rs309
-rw-r--r--embassy-stm32/src/adc/v3.rs885
-rw-r--r--embassy-stm32/src/adc/v4.rs493
-rw-r--r--embassy-stm32/src/dma/dma_bdma.rs16
-rw-r--r--embassy-stm32/src/dma/gpdma/mod.rs13
-rw-r--r--embassy-stm32/src/dma/gpdma/ringbuffered.rs10
-rw-r--r--embassy-stm32/src/dma/mod.rs66
-rw-r--r--embassy-stm32/src/dsihost.rs17
-rw-r--r--embassy-stm32/src/eth/generic_phy.rs64
-rw-r--r--embassy-stm32/src/eth/mod.rs29
-rw-r--r--embassy-stm32/src/eth/sma/mod.rs42
-rw-r--r--embassy-stm32/src/eth/sma/v1.rs102
-rw-r--r--embassy-stm32/src/eth/sma/v2.rs94
-rw-r--r--embassy-stm32/src/eth/v1/mod.rs259
-rw-r--r--embassy-stm32/src/eth/v2/mod.rs197
-rw-r--r--embassy-stm32/src/exti.rs137
-rw-r--r--embassy-stm32/src/flash/c.rs131
-rw-r--r--embassy-stm32/src/flash/common.rs8
-rw-r--r--embassy-stm32/src/flash/g.rs5
-rw-r--r--embassy-stm32/src/flash/h7.rs190
-rw-r--r--embassy-stm32/src/flash/mod.rs7
-rw-r--r--embassy-stm32/src/gpio.rs21
-rw-r--r--embassy-stm32/src/hsem/mod.rs285
-rw-r--r--embassy-stm32/src/hspi/mod.rs93
-rw-r--r--embassy-stm32/src/i2c/v2.rs713
-rw-r--r--embassy-stm32/src/ipcc.rs250
-rw-r--r--embassy-stm32/src/lcd.rs510
-rw-r--r--embassy-stm32/src/lib.rs28
-rw-r--r--embassy-stm32/src/low_power.rs237
-rw-r--r--embassy-stm32/src/opamp.rs13
-rw-r--r--embassy-stm32/src/ospi/mod.rs60
-rw-r--r--embassy-stm32/src/rcc/l.rs43
-rw-r--r--embassy-stm32/src/rcc/mod.rs93
-rw-r--r--embassy-stm32/src/rtc/low_power.rs69
-rw-r--r--embassy-stm32/src/rtc/mod.rs7
-rw-r--r--embassy-stm32/src/sai/mod.rs4
-rw-r--r--embassy-stm32/src/time_driver.rs109
-rw-r--r--embassy-stm32/src/timer/complementary_pwm.rs108
-rw-r--r--embassy-stm32/src/timer/input_capture.rs1
-rw-r--r--embassy-stm32/src/timer/low_level.rs181
-rw-r--r--embassy-stm32/src/timer/mod.rs1
-rw-r--r--embassy-stm32/src/timer/pwm_input.rs1
-rw-r--r--embassy-stm32/src/timer/ringbuffered.rs169
-rw-r--r--embassy-stm32/src/timer/simple_pwm.rs274
-rw-r--r--embassy-stm32/src/usart/buffered.rs2
-rw-r--r--embassy-stm32/src/xspi/mod.rs98
-rw-r--r--embassy-time-driver/CHANGELOG.md3
-rw-r--r--embassy-time-driver/Cargo.toml2
-rw-r--r--embassy-time-driver/gen_tick.py3
-rw-r--r--embassy-time-driver/src/tick.rs3
-rw-r--r--embassy-time/CHANGELOG.md1
-rw-r--r--embassy-time/Cargo.toml2
-rw-r--r--embassy-usb/CHANGELOG.md2
-rw-r--r--embassy-usb/src/class/hid.rs101
-rw-r--r--examples/boot/application/stm32f3/src/bin/a.rs10
-rw-r--r--examples/boot/application/stm32f7/src/bin/a.rs10
-rw-r--r--examples/boot/application/stm32h7/src/bin/a.rs10
-rw-r--r--examples/boot/application/stm32l0/src/bin/a.rs10
-rw-r--r--examples/boot/application/stm32l1/src/bin/a.rs10
-rw-r--r--examples/boot/application/stm32l4/src/bin/a.rs10
-rw-r--r--examples/boot/application/stm32wl/src/bin/a.rs11
-rw-r--r--examples/lpc55s69/src/bin/pwm.rs2
-rw-r--r--examples/lpc55s69/src/bin/usart_async.rs4
-rw-r--r--examples/mspm0g3507/src/bin/mathacl_ops.rs39
-rw-r--r--examples/nrf52840/src/bin/gpiote_channel.rs8
-rw-r--r--examples/nrf52840/src/bin/usb_hid_keyboard.rs58
-rw-r--r--examples/nrf52840/src/bin/usb_hid_mouse.rs56
-rw-r--r--examples/nrf5340/src/bin/gpiote_channel.rs8
-rw-r--r--examples/nrf54l15/memory.x2
-rw-r--r--examples/nrf54l15/src/bin/gpiote_channel.rs8
-rw-r--r--examples/rp/src/bin/usb_hid_keyboard.rs86
-rwxr-xr-xexamples/rp/src/bin/usb_hid_mouse.rs60
-rw-r--r--examples/rp/src/bin/wifi_webrequest.rs90
-rw-r--r--examples/rp235x/src/bin/usb_hid_keyboard.rs85
-rw-r--r--examples/stm32c0/src/bin/adc.rs18
-rw-r--r--examples/stm32c0/src/bin/button_exti.rs10
-rw-r--r--examples/stm32f0/Cargo.toml1
-rw-r--r--examples/stm32f0/src/bin/button_controlled_blink.rs11
-rw-r--r--examples/stm32f0/src/bin/button_exti.rs10
-rw-r--r--examples/stm32f0/src/bin/i2c_master.rs609
-rw-r--r--examples/stm32f3/src/bin/button_events.rs10
-rw-r--r--examples/stm32f3/src/bin/button_exti.rs10
-rw-r--r--examples/stm32f4/src/bin/adc.rs2
-rw-r--r--examples/stm32f4/src/bin/adc_dma.rs8
-rw-r--r--examples/stm32f4/src/bin/button_exti.rs10
-rw-r--r--examples/stm32f4/src/bin/eth.rs12
-rw-r--r--examples/stm32f4/src/bin/eth_compliance_test.rs12
-rw-r--r--examples/stm32f4/src/bin/eth_w5500.rs7
-rw-r--r--examples/stm32f4/src/bin/usb_hid_keyboard.rs91
-rw-r--r--examples/stm32f4/src/bin/usb_hid_mouse.rs56
-rw-r--r--examples/stm32f469/src/bin/dsi_bsp.rs14
-rw-r--r--examples/stm32f7/src/bin/button_exti.rs10
-rw-r--r--examples/stm32f7/src/bin/eth.rs12
-rw-r--r--examples/stm32f7/src/bin/pwm.rs61
-rw-r--r--examples/stm32f7/src/bin/pwm_ringbuffer.rs153
-rw-r--r--examples/stm32g0/src/bin/adc_oversampling.rs14
-rw-r--r--examples/stm32g0/src/bin/button_exti.rs10
-rw-r--r--examples/stm32g4/src/bin/adc.rs4
-rw-r--r--examples/stm32g4/src/bin/adc_differential.rs2
-rw-r--r--examples/stm32g4/src/bin/adc_dma.rs2
-rw-r--r--examples/stm32g4/src/bin/adc_injected_and_regular.rs2
-rw-r--r--examples/stm32g4/src/bin/adc_oversampling.rs13
-rw-r--r--examples/stm32g4/src/bin/button_exti.rs10
-rw-r--r--examples/stm32h5/src/bin/adc_dma.rs9
-rw-r--r--examples/stm32h5/src/bin/button_exti.rs10
-rw-r--r--examples/stm32h5/src/bin/eth.rs12
-rw-r--r--examples/stm32h5/src/bin/stop.rs12
-rw-r--r--examples/stm32h7/src/bin/button_exti.rs10
-rw-r--r--examples/stm32h7/src/bin/eth.rs12
-rw-r--r--examples/stm32h7/src/bin/eth_client.rs12
-rw-r--r--examples/stm32h7/src/bin/eth_client_mii.rs12
-rw-r--r--examples/stm32h7/src/bin/flash_async.rs84
-rw-r--r--examples/stm32h723/src/bin/spdifrx.rs2
-rw-r--r--examples/stm32h7rs/src/bin/button_exti.rs10
-rw-r--r--examples/stm32h7rs/src/bin/eth.rs12
-rw-r--r--examples/stm32l0/src/bin/button_exti.rs11
-rw-r--r--examples/stm32l4/src/bin/adc.rs9
-rw-r--r--examples/stm32l4/src/bin/adc_dma.rs5
-rw-r--r--examples/stm32l4/src/bin/button_exti.rs10
-rw-r--r--examples/stm32l4/src/bin/spe_adin1110_http_server.rs6
-rw-r--r--examples/stm32l4/src/bin/spi_dma.rs4
-rw-r--r--examples/stm32l5/src/bin/button_exti.rs10
-rw-r--r--examples/stm32l5/src/bin/stop.rs12
-rw-r--r--examples/stm32l5/src/bin/usb_hid_mouse.rs56
-rw-r--r--examples/stm32n6/src/bin/blinky.rs10
-rw-r--r--examples/stm32u0/.cargo/config.toml4
-rw-r--r--examples/stm32u0/Cargo.toml6
-rw-r--r--examples/stm32u0/src/bin/adc.rs7
-rw-r--r--examples/stm32u0/src/bin/button_exti.rs10
-rw-r--r--examples/stm32u0/src/bin/lcd.rs412
-rw-r--r--examples/stm32u5/src/bin/adc.rs33
-rw-r--r--examples/stm32wb/Cargo.toml2
-rw-r--r--examples/stm32wb/src/bin/button_exti.rs10
-rw-r--r--examples/stm32wb/src/bin/eddystone_beacon.rs116
-rw-r--r--examples/stm32wb/src/bin/gatt_server.rs118
-rw-r--r--examples/stm32wb/src/bin/mac_ffd.rs47
-rw-r--r--examples/stm32wb/src/bin/mac_ffd_net.rs192
-rw-r--r--examples/stm32wb/src/bin/mac_rfd.rs40
-rw-r--r--examples/stm32wb/src/bin/tl_mbox.rs2
-rw-r--r--examples/stm32wb/src/bin/tl_mbox_ble.rs21
-rw-r--r--examples/stm32wb/src/bin/tl_mbox_mac.rs12
-rw-r--r--examples/stm32wba/src/bin/adc.rs17
-rw-r--r--examples/stm32wba/src/bin/button_exti.rs10
-rw-r--r--examples/stm32wba6/src/bin/adc.rs45
-rw-r--r--examples/stm32wba6/src/bin/button_exti.rs10
-rw-r--r--examples/stm32wl/src/bin/button_exti.rs11
-rw-r--r--examples/stm32wle5/src/bin/adc.rs12
-rw-r--r--examples/stm32wle5/src/bin/blinky.rs12
-rw-r--r--examples/stm32wle5/src/bin/button_exti.rs19
-rw-r--r--examples/stm32wle5/src/bin/i2c.rs13
-rw-r--r--tests/stm32/Cargo.toml18
-rw-r--r--tests/stm32/src/bin/adc.rs39
-rw-r--r--tests/stm32/src/bin/eth.rs13
-rw-r--r--tests/stm32/src/bin/hsem.rs50
-rw-r--r--tests/stm32/src/bin/stop.rs14
-rw-r--r--tests/stm32/src/bin/wpan_ble.rs120
-rw-r--r--tests/stm32/src/bin/wpan_mac.rs29
-rw-r--r--tests/stm32/src/common.rs1
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
diff --git a/ci.sh b/ci.sh
index 6cc2a031d..b4ed0dc18 100755
--- a/ci.sh
+++ b/ci.sh
@@ -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
36rm -rf out/tests/mspm0g3507 36rm -rf out/tests/mspm0g3507
37 37
38rm out/tests/stm32wb55rg/wpan_mac 38# rm out/tests/stm32wb55rg/wpan_mac
39rm out/tests/stm32wb55rg/wpan_ble 39rm 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
4use embassy_executor::Spawner; 4use embassy_executor::Spawner;
5use embassy_stm32::exti::ExtiInput; 5use embassy_stm32::exti::{self, ExtiInput};
6use embassy_stm32::gpio::{Level, Output, Pull, Speed}; 6use embassy_stm32::gpio::{Level, Output, Pull, Speed};
7use embassy_stm32::{bind_interrupts, interrupt};
7use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
8 9
10bind_interrupts!(
11 pub struct Irqs{
12 EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10 >;
13});
14
9#[embassy_executor::main] 15#[embassy_executor::main]
10async fn main(_spawner: Spawner) { 16async 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+
44All values are specified in bytes. 44All 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+
48Partition Size~state~ = Write Size~state~ + (2 × Partition Size~active~ / Page Size~active~) 48Partition Size~state~ = (2 × Write Size~state~) + (4 × Write Size~state~ × Partition Size~active~ / Page Size~active~)
49+ 49+
50All values are specified in bytes. 50All values are specified in bytes.
51 51
52The 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. 52The 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.
53The BOOTLOADER_STATE partition must be big enough to store one word per page in the ACTIVE and DFU partitions combined. 53The BOOTLOADER_STATE partition must be big enough to store two words, plus four words per page in the ACTIVE partition.
54 54
55The 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. 55The 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
174Yes! 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]. 174Yes! 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
176You 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
178Or 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
79When `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. 79When `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
81The 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. 81The 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]
27defmt = { version = "1.0.1", optional = true } 27defmt = { version = "1.0.1", optional = true }
28digest = "0.10" 28digest = "0.10"
29document-features = "0.2.7"
29log = { version = "0.4", optional = true } 30log = { version = "0.4", optional = true }
30ed25519-dalek = { version = "2", default-features = false, features = ["digest"], optional = true } 31ed25519-dalek = { version = "2", default-features = false, features = ["digest"], optional = true }
31embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" } 32embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" }
@@ -45,11 +46,22 @@ critical-section = { version = "1.1.1", features = ["std"] }
45ed25519-dalek = { version = "2", default-features = false, features = ["std", "rand_core", "digest"] } 46ed25519-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
48defmt = ["dep:defmt"] 50defmt = ["dep:defmt"]
51## Use log for logging
49log = ["dep:log"] 52log = ["dep:log"]
53
54## Enable for devices that set erased flash bytes to `0x00` instead of the usual `0xFF`
55flash-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.
50ed25519-dalek = ["dep:ed25519-dalek", "_verify"] 62ed25519-dalek = ["dep:ed25519-dalek", "_verify"]
63## Use the `salty` package to verify DFU signatures.
51ed25519-salty = ["dep:salty", "_verify"] 64ed25519-salty = ["dep:salty", "_verify"]
52flash-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
6mod fmt; 10mod fmt;
7 11
8mod boot_loader; 12mod 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 }
70cortex-m-rt = ">=0.6.15,<0.8" 70cortex-m-rt = ">=0.6.15,<0.8"
71cortex-m = "0.7.6" 71cortex-m = "0.7.6"
72critical-section = "1.2.0" 72critical-section = "1.2.0"
73micromath = "2.0.0"
73 74
74# mspm0-metapac = { version = "" } 75# mspm0-metapac = { version = "" }
75mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-8542f260cc89645a983b7f1a874c87b21822279e" } 76mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-f21b04e9de074af4965bf67ec3646cb9fe1b9852" }
76 77
77[build-dependencies] 78[build-dependencies]
78proc-macro2 = "1.0.94" 79proc-macro2 = "1.0.94"
@@ -80,7 +81,7 @@ quote = "1.0.40"
80cfg_aliases = "0.2.1" 81cfg_aliases = "0.2.1"
81 82
82# mspm0-metapac = { version = "", default-features = false, features = ["metadata"] } 83# mspm0-metapac = { version = "", default-features = false, features = ["metadata"] }
83mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-8542f260cc89645a983b7f1a874c87b21822279e", default-features = false, features = ["metadata"] } 84mspm0-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]
86default = ["rt"] 87default = ["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;
19pub mod gpio; 19pub mod gpio;
20pub mod i2c; 20pub mod i2c;
21pub mod i2c_target; 21pub mod i2c_target;
22#[cfg(any(mspm0g150x, mspm0g151x, mspm0g350x, mspm0g351x))]
23pub mod mathacl;
22pub mod timer; 24pub mod timer;
23pub mod uart; 25pub mod uart;
24pub mod wwdt; 26pub 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
7use core::f32::consts::PI;
8use core::marker::PhantomData;
9
10use embassy_hal_internal::PeripheralType;
11use micromath::F32Ext;
12
13use crate::Peri;
14use crate::pac::mathacl::{Mathacl as Regs, vals};
15
16pub 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]
26pub enum Error {
27 ValueInWrongRange,
28 NBitsTooBig,
29}
30
31pub struct Mathacl<'d> {
32 regs: &'static Regs,
33 _phantom: PhantomData<&'d mut ()>,
34}
35
36impl<'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
102pub(crate) trait SealedInstance {
103 fn regs() -> &'static Regs;
104}
105
106/// Mathacl instance trait
107#[allow(private_bounds)]
108pub trait Instance: SealedInstance + PeripheralType {}
109
110macro_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.
123fn 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.
176fn 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)]
223mod 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)]
207pub const FLASH_SIZE: usize = 1536 * 1024; 207pub const FLASH_SIZE: usize = 1524 * 1024;
208 208
209embassy_hal_internal::peripherals! { 209embassy_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
17use crate::interrupt::typelevel::Interrupt; 17use crate::interrupt::typelevel::Interrupt;
18use crate::pac::gpio::vals as gpiovals; 18use crate::pac::gpio::vals as gpiovals;
19use crate::pac::spis::vals; 19use crate::pac::spis::vals;
20use crate::ppi::Event;
20use crate::util::slice_in_ram_or; 21use crate::util::slice_in_ram_or;
21use crate::{interrupt, pac}; 22use 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
38embedded-io = "0.6.1" 38embedded-io = "0.6.1"
39embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 39embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
40## Chip dependencies 40## Chip dependencies
41nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "477dfdbfd5e6c75c0730c56494b601c1b2257263"} 41nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "af5122e1cbe1483833c5d2e5af96b26a34ed5d62"}
42 42
43imxrt-rt = { version = "0.1.7", optional = true, features = ["device"] } 43imxrt-rt = { version = "0.1.7", optional = true, features = ["device"] }
44 44
45[build-dependencies] 45[build-dependencies]
46cfg_aliases = "0.2.1" 46cfg_aliases = "0.2.1"
47nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "477dfdbfd5e6c75c0730c56494b601c1b2257263", features = ["metadata"], optional = true } 47nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "af5122e1cbe1483833c5d2e5af96b26a34ed5d62", features = ["metadata"], optional = true }
48proc-macro2 = "1.0.95" 48proc-macro2 = "1.0.95"
49quote = "1.0.15" 49quote = "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;
4use std::{env, fs}; 4use std::{env, fs};
5 5
6use cfg_aliases::cfg_aliases; 6use cfg_aliases::cfg_aliases;
7#[cfg(feature = "_rt1xxx")]
8use nxp_pac::metadata; 7use nxp_pac::metadata;
8use nxp_pac::metadata::{METADATA, Peripheral};
9#[allow(unused)] 9#[allow(unused)]
10use proc_macro2::TokenStream; 10use proc_macro2::TokenStream;
11use proc_macro2::{Ident, Literal, Span};
12use quote::format_ident;
11#[allow(unused)] 13#[allow(unused)]
12use quote::quote; 14use 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`.
49fn generate_iomuxc() -> TokenStream { 48struct 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(&registers.name, Span::call_site()); 52 cfg: Option<TokenStream>,
54 let address = registers.pad_ctl; 53}
55 54
56 quote! { 55fn 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(&registers.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")]
146fn 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
83fn generate_code() { 217fn 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
238fn 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
246fn 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
267fn 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
282fn 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
295fn 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
341fn 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
372fn 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.
101fn rustfmt(path: impl AsRef<Path>) { 400fn 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 @@
1pub use nxp_pac as pac; 1pub(crate) mod _generated {
2 #![allow(dead_code)]
3 #![allow(unused_imports)]
4 #![allow(non_snake_case)]
5 #![allow(missing_docs)]
2 6
3embassy_hal_internal::interrupt_mod!( 7 include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
4 FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7
5);
6
7embassy_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
10pub 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.
2use imxrt_rt as _; 2use imxrt_rt as _;
3pub use nxp_pac as pac;
4
5embassy_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
54impl_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
106pub(crate) mod _generated { 4pub(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
13pub 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.
2use imxrt_rt as _; 2use imxrt_rt as _;
3pub use nxp_pac as pac;
4
5embassy_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
137impl_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
275pub(crate) mod _generated { 4pub(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
13pub 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
1use core::cell::RefCell; 3use core::cell::RefCell;
2use core::future::Future; 4use core::future::Future;
3use core::pin::Pin; 5use core::pin::Pin;
@@ -9,9 +11,12 @@ use embassy_hal_internal::interrupt::InterruptExt;
9use embassy_hal_internal::{PeripheralType, impl_peripheral}; 11use embassy_hal_internal::{PeripheralType, impl_peripheral};
10use embassy_sync::waitqueue::AtomicWaker; 12use embassy_sync::waitqueue::AtomicWaker;
11 13
12use crate::pac::{DMA0, SYSCON, *}; 14use crate::Peri;
13use crate::{Peri, peripherals}; 15#[cfg(feature = "rt")]
16use crate::pac::interrupt;
17use crate::pac::{SYSCON, *};
14 18
19#[cfg(feature = "rt")]
15#[interrupt] 20#[interrupt]
16fn DMA0() { 21fn 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
281trait SealedChannel {} 286pub(crate) trait SealedChannel {}
282trait SealedWord {} 287trait 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.
325pub struct AnyChannel { 330pub struct AnyChannel {
326 number: u8, 331 pub(crate) number: u8,
327} 332}
328 333
329impl_peripheral!(AnyChannel); 334impl_peripheral!(AnyChannel);
@@ -335,10 +340,10 @@ impl Channel for AnyChannel {
335 } 340 }
336} 341}
337 342
338macro_rules! channel { 343macro_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
355channel!(DMA_CH0, 0);
356channel!(DMA_CH1, 1);
357channel!(DMA_CH2, 2);
358channel!(DMA_CH3, 3);
359channel!(DMA_CH4, 4);
360channel!(DMA_CH5, 5);
361channel!(DMA_CH6, 6);
362channel!(DMA_CH7, 7);
363channel!(DMA_CH8, 8);
364channel!(DMA_CH9, 9);
365channel!(DMA_CH10, 10);
366channel!(DMA_CH11, 11);
367channel!(DMA_CH12, 12);
368channel!(DMA_CH13, 13);
369channel!(DMA_CH14, 14);
370channel!(DMA_CH15, 15);
371channel!(DMA_CH16, 16);
372channel!(DMA_CH17, 17);
373channel!(DMA_CH18, 18);
374channel!(DMA_CH19, 19);
375channel!(DMA_CH20, 20);
376channel!(DMA_CH21, 21);
377channel!(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
1use embassy_hal_internal::{PeripheralType, impl_peripheral}; 3use embassy_hal_internal::{PeripheralType, impl_peripheral};
2 4
5use crate::Peri;
3use crate::pac::common::{RW, Reg}; 6use crate::pac::common::{RW, Reg};
4use crate::pac::iocon::vals::{PioDigimode, PioMode}; 7use crate::pac::iocon::vals::{PioDigimode, PioMode};
5use crate::pac::{GPIO, IOCON, SYSCON, iocon}; 8use crate::pac::{GPIO, IOCON, SYSCON, iocon};
6use crate::{Peri, peripherals};
7 9
8pub(crate) fn init() { 10pub(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)]
41pub enum Bank { 43pub 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.
256pub struct AnyPin { 258pub 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
261impl AnyPin { 263impl AnyPin {
@@ -285,12 +287,12 @@ impl SealedPin for AnyPin {
285} 287}
286 288
287macro_rules! impl_pin { 289macro_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
313impl_pin!(PIO0_0, Bank::Bank0, 0);
314impl_pin!(PIO0_1, Bank::Bank0, 1);
315impl_pin!(PIO0_2, Bank::Bank0, 2);
316impl_pin!(PIO0_3, Bank::Bank0, 3);
317impl_pin!(PIO0_4, Bank::Bank0, 4);
318impl_pin!(PIO0_5, Bank::Bank0, 5);
319impl_pin!(PIO0_6, Bank::Bank0, 6);
320impl_pin!(PIO0_7, Bank::Bank0, 7);
321impl_pin!(PIO0_8, Bank::Bank0, 8);
322impl_pin!(PIO0_9, Bank::Bank0, 9);
323impl_pin!(PIO0_10, Bank::Bank0, 10);
324impl_pin!(PIO0_11, Bank::Bank0, 11);
325impl_pin!(PIO0_12, Bank::Bank0, 12);
326impl_pin!(PIO0_13, Bank::Bank0, 13);
327impl_pin!(PIO0_14, Bank::Bank0, 14);
328impl_pin!(PIO0_15, Bank::Bank0, 15);
329impl_pin!(PIO0_16, Bank::Bank0, 16);
330impl_pin!(PIO0_17, Bank::Bank0, 17);
331impl_pin!(PIO0_18, Bank::Bank0, 18);
332impl_pin!(PIO0_19, Bank::Bank0, 19);
333impl_pin!(PIO0_20, Bank::Bank0, 20);
334impl_pin!(PIO0_21, Bank::Bank0, 21);
335impl_pin!(PIO0_22, Bank::Bank0, 22);
336impl_pin!(PIO0_23, Bank::Bank0, 23);
337impl_pin!(PIO0_24, Bank::Bank0, 24);
338impl_pin!(PIO0_25, Bank::Bank0, 25);
339impl_pin!(PIO0_26, Bank::Bank0, 26);
340impl_pin!(PIO0_27, Bank::Bank0, 27);
341impl_pin!(PIO0_28, Bank::Bank0, 28);
342impl_pin!(PIO0_29, Bank::Bank0, 29);
343impl_pin!(PIO0_30, Bank::Bank0, 30);
344impl_pin!(PIO0_31, Bank::Bank0, 31);
345impl_pin!(PIO1_0, Bank::Bank1, 0);
346impl_pin!(PIO1_1, Bank::Bank1, 1);
347impl_pin!(PIO1_2, Bank::Bank1, 2);
348impl_pin!(PIO1_3, Bank::Bank1, 3);
349impl_pin!(PIO1_4, Bank::Bank1, 4);
350impl_pin!(PIO1_5, Bank::Bank1, 5);
351impl_pin!(PIO1_6, Bank::Bank1, 6);
352impl_pin!(PIO1_7, Bank::Bank1, 7);
353impl_pin!(PIO1_8, Bank::Bank1, 8);
354impl_pin!(PIO1_9, Bank::Bank1, 9);
355impl_pin!(PIO1_10, Bank::Bank1, 10);
356impl_pin!(PIO1_11, Bank::Bank1, 11);
357impl_pin!(PIO1_12, Bank::Bank1, 12);
358impl_pin!(PIO1_13, Bank::Bank1, 13);
359impl_pin!(PIO1_14, Bank::Bank1, 14);
360impl_pin!(PIO1_15, Bank::Bank1, 15);
361impl_pin!(PIO1_16, Bank::Bank1, 16);
362impl_pin!(PIO1_17, Bank::Bank1, 17);
363impl_pin!(PIO1_18, Bank::Bank1, 18);
364impl_pin!(PIO1_19, Bank::Bank1, 19);
365impl_pin!(PIO1_20, Bank::Bank1, 20);
366impl_pin!(PIO1_21, Bank::Bank1, 21);
367impl_pin!(PIO1_22, Bank::Bank1, 22);
368impl_pin!(PIO1_23, Bank::Bank1, 23);
369impl_pin!(PIO1_24, Bank::Bank1, 24);
370impl_pin!(PIO1_25, Bank::Bank1, 25);
371impl_pin!(PIO1_26, Bank::Bank1, 26);
372impl_pin!(PIO1_27, Bank::Bank1, 27);
373impl_pin!(PIO1_28, Bank::Bank1, 28);
374impl_pin!(PIO1_29, Bank::Bank1, 29);
375impl_pin!(PIO1_30, Bank::Bank1, 30);
376impl_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;
10use nxp_pac::gpio::vals::Icr; 10use nxp_pac::gpio::vals::Icr;
11use nxp_pac::iomuxc::vals::Pus; 11use nxp_pac::iomuxc::vals::Pus;
12 12
13use crate::chip::{mux_address, pad_address}; 13use crate::chip::{iomuxc_mux, iomuxc_pad};
14use crate::pac::common::{RW, Reg}; 14use crate::pac::common::{RW, Reg};
15use crate::pac::gpio::Gpio; 15use 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];
656static GPIO4_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; 660static GPIO4_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
657#[cfg(gpio5)] 661#[cfg(gpio5)]
658static GPIO5_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; 662static GPIO5_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
663#[cfg(gpio10)]
664static 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.
661pub(crate) trait SealedPin: Sized { 667pub(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.
799macro_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
829macro_rules! impl_pin { 805macro_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)]
8pub trait Pad: SealedPad {}
9
10pub(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
20macro_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")]
13pub mod pwm; 13pub mod pwm;
14#[cfg(feature = "lpc55-core0")] 14#[cfg(feature = "lpc55-core0")]
15pub mod sct;
16#[cfg(feature = "lpc55-core0")]
15pub mod usart; 17pub mod usart;
16 18
19#[cfg(rt1xxx)]
20mod 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")]
26mod chip; 31mod chip;
27 32
28// TODO: Remove when this module is implemented for other chips 33pub use chip::{Peripherals, interrupt, peripherals};
29#[cfg(feature = "lpc55-core0")] 34pub use embassy_hal_internal::{Peri, PeripheralType};
30pub use chip::interrupt;
31#[cfg(feature = "unstable-pac")] 35#[cfg(feature = "unstable-pac")]
32pub use chip::pac; 36pub use nxp_pac as pac;
33#[cfg(not(feature = "unstable-pac"))] 37#[cfg(not(feature = "unstable-pac"))]
34pub(crate) use chip::pac; 38pub(crate) use nxp_pac as pac;
35pub use chip::{Peripherals, peripherals};
36pub 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
1use core::sync::atomic::{AtomicU8, AtomicU32, Ordering}; 3use core::sync::atomic::{AtomicU8, AtomicU32, Ordering};
2 4
3use embassy_hal_internal::{Peri, PeripheralType}; 5use embassy_hal_internal::Peri;
4 6
5use crate::gpio::AnyPin; 7use crate::gpio::AnyPin;
6use crate::pac::iocon::vals::{PioDigimode, PioFunc, PioMode, PioOd, PioSlew}; 8use crate::pac::iocon::vals::{PioDigimode, PioMode, PioOd, PioSlew};
7use crate::pac::sct0::vals; 9use crate::pac::sct0::vals;
8use crate::pac::syscon::vals::{SctRst, SctclkselSel}; 10use crate::pac::syscon::vals::{SctRst, SctclkselSel};
9use crate::pac::{SCT0, SYSCON}; 11use crate::pac::{SCT0, SYSCON};
12use 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.
12static TOP_VALUE: AtomicU32 = AtomicU32::new(0); 15static 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
243trait SealedOutput {
244 /// Output number.
245 fn number(&self) -> usize;
246}
247
248/// PWM Output.
249#[allow(private_bounds)]
250pub trait Output: PeripheralType + SealedOutput {}
251
252macro_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
263output!(PWM_OUTPUT0, 0);
264output!(PWM_OUTPUT1, 1);
265output!(PWM_OUTPUT2, 2);
266output!(PWM_OUTPUT3, 3);
267output!(PWM_OUTPUT4, 4);
268output!(PWM_OUTPUT5, 5);
269output!(PWM_OUTPUT6, 6);
270output!(PWM_OUTPUT7, 7);
271output!(PWM_OUTPUT8, 8);
272output!(PWM_OUTPUT9, 9);
273
274/// PWM Output Channel.
275pub trait OutputChannelPin<T: Output>: crate::gpio::Pin {
276 fn pin_func(&self) -> PioFunc;
277}
278
279macro_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
289impl_pin!(PIO0_2, PWM_OUTPUT0, ALT3);
290impl_pin!(PIO0_17, PWM_OUTPUT0, ALT4);
291impl_pin!(PIO1_4, PWM_OUTPUT0, ALT4);
292impl_pin!(PIO1_23, PWM_OUTPUT0, ALT2);
293
294impl_pin!(PIO0_3, PWM_OUTPUT1, ALT3);
295impl_pin!(PIO0_18, PWM_OUTPUT1, ALT4);
296impl_pin!(PIO1_8, PWM_OUTPUT1, ALT4);
297impl_pin!(PIO1_24, PWM_OUTPUT1, ALT2);
298
299impl_pin!(PIO0_10, PWM_OUTPUT2, ALT5);
300impl_pin!(PIO0_15, PWM_OUTPUT2, ALT4);
301impl_pin!(PIO0_19, PWM_OUTPUT2, ALT4);
302impl_pin!(PIO1_9, PWM_OUTPUT2, ALT4);
303impl_pin!(PIO1_25, PWM_OUTPUT2, ALT2);
304
305impl_pin!(PIO0_22, PWM_OUTPUT3, ALT4);
306impl_pin!(PIO0_31, PWM_OUTPUT3, ALT4);
307impl_pin!(PIO1_10, PWM_OUTPUT3, ALT4);
308impl_pin!(PIO1_26, PWM_OUTPUT3, ALT2);
309
310impl_pin!(PIO0_23, PWM_OUTPUT4, ALT4);
311impl_pin!(PIO1_3, PWM_OUTPUT4, ALT4);
312impl_pin!(PIO1_17, PWM_OUTPUT4, ALT4);
313
314impl_pin!(PIO0_26, PWM_OUTPUT5, ALT4);
315impl_pin!(PIO1_18, PWM_OUTPUT5, ALT4);
316
317impl_pin!(PIO0_27, PWM_OUTPUT6, ALT4);
318impl_pin!(PIO1_31, PWM_OUTPUT6, ALT4);
319
320impl_pin!(PIO0_28, PWM_OUTPUT7, ALT4);
321impl_pin!(PIO1_19, PWM_OUTPUT7, ALT2);
322
323impl_pin!(PIO0_29, PWM_OUTPUT8, ALT4);
324
325impl_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
3use embassy_hal_internal::PeripheralType;
4use nxp_pac::iocon::vals::PioFunc;
5
6use crate::gpio;
7
8/// SCT instance.
9#[allow(private_bounds)]
10pub trait Instance: SealedInstance + PeripheralType {}
11
12pub(crate) trait SealedInstance {}
13
14/// An SCT output.
15#[allow(private_bounds)]
16pub trait Output<T: Instance>: SealedOutput + PeripheralType {}
17
18pub(crate) trait SealedOutput {
19 /// Output number.
20 fn number(&self) -> usize;
21}
22
23/// An SCT output capable pin.
24pub trait OutputPin<T: Instance, O: Output<T>>: gpio::Pin {
25 fn pin_func(&self) -> PioFunc;
26}
27
28macro_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
35macro_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
46macro_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
1use core::fmt::Debug; 3use core::fmt::Debug;
2use core::future::poll_fn; 4use core::future::poll_fn;
3use core::marker::PhantomData; 5use core::marker::PhantomData;
@@ -13,7 +15,7 @@ use embedded_io::{self, ErrorKind};
13use crate::dma::{AnyChannel, Channel}; 15use crate::dma::{AnyChannel, Channel};
14use crate::gpio::{AnyPin, SealedPin}; 16use crate::gpio::{AnyPin, SealedPin};
15use crate::interrupt::Interrupt; 17use crate::interrupt::Interrupt;
16use crate::interrupt::typelevel::{Binding, Interrupt as _}; 18use crate::interrupt::typelevel::Binding;
17use crate::pac::flexcomm::Flexcomm as FlexcommReg; 19use crate::pac::flexcomm::Flexcomm as FlexcommReg;
18use crate::pac::iocon::vals::PioFunc; 20use crate::pac::iocon::vals::PioFunc;
19use crate::pac::usart::Usart as UsartReg; 21use 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.
115pub struct DmaState { 117pub 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
821struct Info { 823pub(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
827trait SealedInstance { 829pub(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
840macro_rules! impl_instance { 842macro_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
870impl_instance!(USART0, FLEXCOMM0, 0);
871impl_instance!(USART1, FLEXCOMM1, 1);
872impl_instance!(USART2, FLEXCOMM2, 2);
873impl_instance!(USART3, FLEXCOMM3, 3);
874impl_instance!(USART4, FLEXCOMM4, 4);
875impl_instance!(USART5, FLEXCOMM5, 5);
876impl_instance!(USART6, FLEXCOMM6, 6);
877impl_instance!(USART7, FLEXCOMM7, 7);
878
879pub(crate) trait SealedTxPin<T: Instance>: crate::gpio::Pin { 881pub(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)]
893pub trait RxPin<T: Instance>: SealedRxPin<T> + crate::gpio::Pin {} 895pub trait RxPin<T: Instance>: SealedRxPin<T> + crate::gpio::Pin {}
894 896
895macro_rules! impl_tx_pin { 897macro_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
907macro_rules! impl_rx_pin { 910macro_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
919impl_tx_pin!(PIO1_6, USART0, ALT1); 923/// Marker trait indicating a DMA channel may be used for USART transmit.
920impl_tx_pin!(PIO1_11, USART1, ALT2);
921impl_tx_pin!(PIO0_27, USART2, ALT1);
922impl_tx_pin!(PIO0_2, USART3, ALT1);
923impl_tx_pin!(PIO0_16, USART4, ALT1);
924impl_tx_pin!(PIO0_9, USART5, ALT3);
925impl_tx_pin!(PIO1_16, USART6, ALT2);
926impl_tx_pin!(PIO0_19, USART7, ALT7);
927
928impl_rx_pin!(PIO1_5, USART0, ALT1);
929impl_rx_pin!(PIO1_10, USART1, ALT2);
930impl_rx_pin!(PIO1_24, USART2, ALT1);
931impl_rx_pin!(PIO0_3, USART3, ALT1);
932impl_rx_pin!(PIO0_5, USART4, ALT2);
933impl_rx_pin!(PIO0_8, USART5, ALT3);
934impl_rx_pin!(PIO1_13, USART6, ALT2);
935impl_rx_pin!(PIO0_20, USART7, ALT7);
936
937/// Trait for TX DMA channels.
938pub trait TxChannel<T: Instance>: crate::dma::Channel {} 924pub 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.
940pub trait RxChannel<T: Instance>: crate::dma::Channel {} 927pub trait RxChannel<T: Instance>: crate::dma::Channel {}
941 928
942macro_rules! impl_channel { 929macro_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
951impl_channel!(DMA_CH4, USART0, Rx); 935macro_rules! impl_usart_rx_channel {
952impl_channel!(DMA_CH5, USART0, Tx); 936 ($instance: ident, $channel: ident) => {
953impl_channel!(DMA_CH6, USART1, Rx); 937 impl crate::usart::RxChannel<crate::peripherals::$instance> for crate::peripherals::$channel {}
954impl_channel!(DMA_CH7, USART1, Tx); 938 };
955impl_channel!(DMA_CH10, USART2, Rx); 939}
956impl_channel!(DMA_CH11, USART2, Tx);
957impl_channel!(DMA_CH8, USART3, Rx);
958impl_channel!(DMA_CH9, USART3, Tx);
959impl_channel!(DMA_CH12, USART4, Rx);
960impl_channel!(DMA_CH13, USART4, Tx);
961impl_channel!(DMA_CH14, USART5, Rx);
962impl_channel!(DMA_CH15, USART5, Tx);
963impl_channel!(DMA_CH16, USART6, Rx);
964impl_channel!(DMA_CH17, USART6, Tx);
965impl_channel!(DMA_CH18, USART7, Rx);
966impl_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
6impl<'d, PIO: Instance, const SM: usize> StateMachine<'d, PIO, SM> { 6impl<'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" }
33embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal" } 33embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal" }
34embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" } 34embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" }
35embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true } 35embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true }
36smoltcp = { version = "0.12.0", optional=true, default-features = false }
36 37
37defmt = { version = "1.0.1", optional = true } 38defmt = { version = "1.0.1", optional = true }
38log = { version = "0.4.17", optional = true } 39log = { version = "0.4.17", optional = true }
@@ -40,6 +41,7 @@ log = { version = "0.4.17", optional = true }
40cortex-m = "0.7.6" 41cortex-m = "0.7.6"
41heapless = "0.8" 42heapless = "0.8"
42aligned = "0.4.1" 43aligned = "0.4.1"
44critical-section = "1.1"
43 45
44bit_field = "0.10.2" 46bit_field = "0.10.2"
45stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } 47stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] }
@@ -51,7 +53,7 @@ bitflags = { version = "2.3.3", optional = true }
51defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "stm32wb-hci?/defmt"] 53defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "stm32wb-hci?/defmt"]
52 54
53ble = ["dep:stm32wb-hci"] 55ble = ["dep:stm32wb-hci"]
54mac = ["dep:bitflags", "dep:embassy-net-driver" ] 56mac = ["dep:bitflags", "dep:embassy-net-driver", "dep:smoltcp", "smoltcp/medium-ieee802154"]
55 57
56extended = [] 58extended = []
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)]
54pub enum IpccChannel {
55 Channel1 = 1,
56 Channel2 = 2,
57 Channel3 = 3,
58 Channel4 = 4,
59 Channel5 = 5,
60 Channel6 = 6,
61}
51 62
52pub mod cpu1 { 63pub 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
72pub mod cpu2 { 83pub 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 @@
1use core::ptr; 1use core::ptr;
2use core::sync::atomic::{Ordering, compiler_fence};
2 3
3use crate::PacketHeader; 4use crate::PacketHeader;
4use crate::consts::TlPacketType; 5use crate::consts::TlPacketType;
@@ -45,11 +46,11 @@ pub struct CmdPacket {
45 46
46impl CmdPacket { 47impl 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
88impl AclDataPacket { 91impl 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 {
67pub struct EvtStub { 67pub 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
27use embassy_hal_internal::Peri; 27use embassy_hal_internal::Peri;
28use embassy_stm32::interrupt; 28use embassy_stm32::interrupt;
29use embassy_stm32::ipcc::{Config, Ipcc, ReceiveInterruptHandler, TransmitInterruptHandler}; 29use embassy_stm32::ipcc::{Config, Ipcc, IpccRxChannel, ReceiveInterruptHandler, TransmitInterruptHandler};
30use embassy_stm32::peripherals::IPCC; 30use embassy_stm32::peripherals::IPCC;
31use sub::mm::MemoryManager; 31use sub::mm::MemoryManager;
32use sub::sys::Sys; 32use 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
55pub struct TlMbox<'d> { 55pub 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
66impl<'d> TlMbox<'d> { 65impl<'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
3use core::{mem, slice}; 3use core::{mem, slice};
4 4
5use smoltcp::wire::ieee802154::Frame;
6
5use super::opcodes::OpcodeM4ToM0; 7use super::opcodes::OpcodeM4ToM0;
6use super::typedefs::{ 8use 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
384impl<'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
382impl Default for DataRequest { 413impl 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 @@
1use core::cell::RefCell;
1use core::future::Future; 2use core::future::Future;
3use core::sync::atomic::{Ordering, compiler_fence};
2use core::task; 4use core::task;
3use core::task::Poll; 5use core::task::Poll;
4 6
7use embassy_net_driver::LinkState;
8use embassy_sync::blocking_mutex;
5use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 9use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
6use embassy_sync::mutex::MutexGuard; 10use embassy_sync::mutex::Mutex;
7use embassy_sync::signal::Signal; 11use embassy_sync::signal::Signal;
8use futures_util::FutureExt; 12use futures_util::FutureExt;
9 13
10use super::commands::MacCommand; 14use crate::mac::commands::*;
11use super::event::MacEvent; 15use crate::mac::driver::NetworkState;
12use super::typedefs::MacError; 16use crate::mac::event::MacEvent;
13use crate::mac::runner::Runner; 17use crate::mac::runner::ZeroCopyPubSub;
18use crate::mac::typedefs::*;
19use crate::sub::mac::MacTx;
14 20
15pub struct Control<'a> { 21pub 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
19impl<'a> Control<'a> { 28impl<'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
47pub struct EventToken<'a> { 172pub 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
52impl<'a> EventToken<'a> { 176impl<'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
3use core::cell::RefCell;
3use core::task::Context; 4use core::task::Context;
4 5
5use embassy_net_driver::{Capabilities, HardwareAddress, LinkState}; 6use embassy_net_driver::{Capabilities, HardwareAddress, LinkState};
7use embassy_sync::blocking_mutex;
6use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 8use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
7use embassy_sync::channel::Channel; 9use embassy_sync::channel::Channel;
10use embassy_sync::mutex::Mutex;
11use embassy_sync::waitqueue::AtomicWaker;
8 12
9use crate::mac::MTU;
10use crate::mac::event::MacEvent; 13use crate::mac::event::MacEvent;
11use crate::mac::runner::Runner; 14use crate::mac::indications::{write_frame_from_beacon_indication, write_frame_from_data_indication};
15use crate::mac::runner::{BUF_SIZE, ZeroCopyPubSub};
16use crate::mac::{Control, MTU, Runner};
17use crate::sub::mac::{Mac, MacRx, MacTx};
18
19pub 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
27impl 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
39pub 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
50impl<'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
13pub struct Driver<'d> { 68pub 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
17impl<'d> Driver<'d> { 75impl<'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
101pub struct TxToken<'d> { 194pub 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
106impl<'d> embassy_net_driver::TxToken for TxToken<'d> { 199impl<'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};
11use crate::evt::{EvtBox, MemoryManager}; 11use crate::evt::{EvtBox, MemoryManager};
12use crate::mac::opcodes::OpcodeM0ToM4; 12use crate::mac::opcodes::OpcodeM0ToM4;
13use crate::sub::mac::{self, Mac}; 13use crate::sub::mac::{self, MacRx};
14 14
15pub(crate) trait ParseableMacEvent: Sized { 15pub(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
55impl<'a> MacEvent<'a> { 55impl<'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
149impl<'a> Drop for MacEvent<'a> { 149impl<'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 @@
1use core::slice; 1use core::slice;
2 2
3use smoltcp::wire::Ieee802154FrameType;
4use smoltcp::wire::ieee802154::Frame;
5
3use super::consts::MAX_PENDING_ADDRESS; 6use super::consts::MAX_PENDING_ADDRESS;
4use super::event::ParseableMacEvent; 7use super::event::ParseableMacEvent;
5use super::typedefs::{ 8use 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};
12use 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
75impl ParseableMacEvent for BeaconNotifyIndication {} 79impl ParseableMacEvent for BeaconNotifyIndication {}
76 80
81impl 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
87pub 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
273pub 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;
11pub mod typedefs; 11pub mod typedefs;
12 12
13pub use crate::mac::control::Control; 13pub use crate::mac::control::Control;
14use crate::mac::driver::Driver; 14pub use crate::mac::driver::{Driver, DriverState};
15pub use crate::mac::runner::Runner; 15pub use crate::mac::runner::Runner;
16 16
17const MTU: usize = 127; 17const MTU: usize = 127;
18
19pub 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};
6use embassy_sync::channel::Channel; 6use embassy_sync::channel::Channel;
7use embassy_sync::mutex::Mutex; 7use embassy_sync::mutex::Mutex;
8use embassy_sync::signal::Signal; 8use embassy_sync::signal::Signal;
9use smoltcp::wire::Ieee802154FrameType;
10use smoltcp::wire::ieee802154::Frame;
9 11
10use crate::mac::MTU; 12use crate::mac::MTU;
11use crate::mac::commands::DataRequest; 13use crate::mac::commands::*;
14use crate::mac::driver::NetworkState;
12use crate::mac::event::MacEvent; 15use crate::mac::event::MacEvent;
13use crate::mac::typedefs::{AddressMode, MacAddress, PanId, SecurityLevel}; 16use crate::sub::mac::{MacRx, MacTx};
14use crate::sub::mac::Mac;
15 17
16type ZeroCopyPubSub<M, T> = blocking_mutex::Mutex<M, RefCell<Option<Signal<NoopRawMutex, T>>>>; 18pub type ZeroCopyPubSub<M, T> = blocking_mutex::Mutex<M, RefCell<Option<Signal<NoopRawMutex, T>>>>;
19
20pub const BUF_SIZE: usize = 3;
17 21
18pub struct Runner<'a> { 22pub 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
30impl<'a> Runner<'a> { 37impl<'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 @@
1use core::fmt::Debug; 1use core::fmt::Debug;
2 2
3use smoltcp::wire::ieee802154::{Address, AddressingMode, Pan};
4
3use crate::numeric_enum; 5use crate::numeric_enum;
4 6
5#[derive(Debug)] 7#[derive(Debug)]
@@ -109,12 +111,51 @@ numeric_enum! {
109} 111}
110} 112}
111 113
114impl 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)]
113pub union MacAddress { 128pub 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
133impl 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
143pub struct MacAddressAndMode(pub MacAddress, pub AddressMode);
144
145impl 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
118impl Debug for MacAddress { 159impl 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
347numeric_enum! { 388numeric_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]);
379impl PanId { 420impl PanId {
380 pub const BROADCAST: Self = Self([0xFF, 0xFF]); 421 pub const BROADCAST: Self = Self([0xFF, 0xFF]);
381} 422}
423
424impl From<Pan> for PanId {
425 fn from(value: Pan) -> Self {
426 Self(value.0.to_be_bytes())
427 }
428}
429
430impl 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 @@
1use core::{mem, slice}; 1use core::sync::atomic::{Ordering, compiler_fence};
2use core::{mem, ptr, slice};
2 3
4use crate::PacketHeader;
5use crate::cmd::CmdPacket;
3use crate::consts::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; 6use crate::consts::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE};
7use crate::evt::{CcEvt, EvtStub};
4 8
5const SHCI_OGF: u16 = 0x3F; 9const SHCI_OGF: u16 = 0x3F;
6 10
@@ -21,6 +25,18 @@ pub enum SchiCommandStatus {
21 ShciFusCmdNotSupported = 0xFF, 25 ShciFusCmdNotSupported = 0xFF,
22} 26}
23 27
28impl 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
24impl TryFrom<u8> for SchiCommandStatus { 40impl 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
342impl ShciBleInitCmdParam { 353impl 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 @@
1use core::ptr; 1use core::ptr;
2 2
3use embassy_stm32::ipcc::Ipcc; 3use embassy_stm32::ipcc::{IpccRxChannel, IpccTxChannel};
4use hci::Opcode; 4use hci::Opcode;
5 5
6use crate::cmd::CmdPacket; 6use crate::cmd::CmdPacket;
7use crate::consts::{TL_BLEEVT_CC_OPCODE, TL_BLEEVT_CS_OPCODE, TlPacketType}; 7use crate::consts::{TL_BLEEVT_CC_OPCODE, TL_BLEEVT_CS_OPCODE, TlPacketType};
8use crate::evt;
8use crate::evt::{EvtBox, EvtPacket, EvtStub}; 9use crate::evt::{EvtBox, EvtPacket, EvtStub};
9use crate::sub::mm; 10use crate::sub::mm;
10use crate::tables::{BLE_CMD_BUFFER, BleTable, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE}; 11use crate::tables::{BLE_CMD_BUFFER, BleTable, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE};
11use crate::unsafe_linked_list::LinkedListNode; 12use crate::unsafe_linked_list::LinkedListNode;
12use 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/// ```
39pub struct Ble { 39pub 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
43impl Ble { 45impl<'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
96impl evt::MemoryManager for Ble { 109impl<'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
111pub extern crate stm32wb_hci as hci; 124pub extern crate stm32wb_hci as hci;
112 125
113impl hci::Controller for Ble { 126impl<'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};
4use core::task::Poll; 4use core::task::Poll;
5 5
6use embassy_futures::poll_once; 6use embassy_futures::poll_once;
7use embassy_stm32::ipcc::Ipcc; 7use embassy_stm32::ipcc::{Ipcc, IpccRxChannel, IpccTxChannel};
8use embassy_sync::waitqueue::AtomicWaker; 8use embassy_sync::waitqueue::AtomicWaker;
9 9
10use crate::cmd::CmdPacket; 10use crate::cmd::CmdPacket;
11use crate::consts::TlPacketType; 11use crate::consts::TlPacketType;
12use crate::evt;
12use crate::evt::{EvtBox, EvtPacket}; 13use crate::evt::{EvtBox, EvtPacket};
13use crate::mac::commands::MacCommand; 14use crate::mac::commands::MacCommand;
14use crate::mac::event::MacEvent; 15use crate::mac::event::MacEvent;
15use crate::mac::typedefs::MacError; 16use crate::mac::typedefs::MacError;
16use crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER}; 17use crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER};
17use crate::{channels, evt}; 18use crate::unsafe_linked_list::LinkedListNode;
18 19
19static MAC_WAKER: AtomicWaker = AtomicWaker::new(); 20static MAC_WAKER: AtomicWaker = AtomicWaker::new();
20static MAC_EVT_OUT: AtomicBool = AtomicBool::new(false); 21static MAC_EVT_OUT: AtomicBool = AtomicBool::new(false);
21 22
22pub struct Mac { 23pub 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
26impl Mac { 28impl<'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
70pub struct MacTx<'a> {
71 ipcc_mac_802_15_4_cmd_rsp_channel: IpccTxChannel<'a>,
72}
56 73
74impl<'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
116pub struct MacRx<'a> {
117 ipcc_mac_802_15_4_notification_ack_channel: IpccRxChannel<'a>,
118}
119
120impl<'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
101impl evt::MemoryManager for Mac { 153impl<'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
6use aligned::{A4, Aligned}; 6use aligned::{A4, Aligned};
7use cortex_m::interrupt; 7use cortex_m::interrupt;
8use embassy_stm32::ipcc::Ipcc; 8use embassy_stm32::ipcc::IpccTxChannel;
9use embassy_sync::waitqueue::AtomicWaker; 9use embassy_sync::waitqueue::AtomicWaker;
10 10
11use crate::consts::POOL_SIZE; 11use crate::consts::POOL_SIZE;
12use crate::evt;
12use crate::evt::EvtPacket; 13use crate::evt::EvtPacket;
13#[cfg(feature = "ble")] 14#[cfg(feature = "ble")]
14use crate::tables::BLE_SPARE_EVT_BUF; 15use crate::tables::BLE_SPARE_EVT_BUF;
15use crate::tables::{EVT_POOL, FREE_BUF_QUEUE, MemManagerTable, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE}; 16use crate::tables::{EVT_POOL, FREE_BUF_QUEUE, MemManagerTable, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE};
16use crate::unsafe_linked_list::LinkedListNode; 17use crate::unsafe_linked_list::LinkedListNode;
17use crate::{channels, evt};
18 18
19static MM_WAKER: AtomicWaker = AtomicWaker::new(); 19static MM_WAKER: AtomicWaker = AtomicWaker::new();
20static mut LOCAL_FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit()); 20static mut LOCAL_FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit());
21 21
22pub struct MemoryManager { 22pub struct MemoryManager<'a> {
23 _private: (), 23 ipcc_mm_release_buffer_channel: IpccTxChannel<'a>,
24} 24}
25 25
26impl MemoryManager { 26impl<'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
74impl evt::MemoryManager for MemoryManager { 77impl<'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 @@
1use core::ptr; 1use embassy_stm32::ipcc::{IpccRxChannel, IpccTxChannel};
2 2
3use crate::cmd::CmdPacket; 3use crate::cmd::CmdPacket;
4use crate::consts::TlPacketType; 4use crate::consts::TlPacketType;
5use crate::evt::{CcEvt, EvtBox, EvtPacket}; 5use crate::evt::EvtBox;
6#[allow(unused_imports)] 6#[cfg(feature = "ble")]
7use crate::shci::{SchiCommandStatus, ShciBleInitCmdParam, ShciOpcode}; 7use crate::shci::ShciBleInitCmdParam;
8use crate::shci::{SchiCommandStatus, ShciOpcode};
8use crate::sub::mm; 9use crate::sub::mm;
9use crate::tables::{SysTable, WirelessFwInfoTable}; 10use crate::tables::{SysTable, WirelessFwInfoTable};
10use crate::unsafe_linked_list::LinkedListNode; 11use crate::unsafe_linked_list::LinkedListNode;
11use crate::{Ipcc, SYS_CMD_BUF, SYSTEM_EVT_QUEUE, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE, channels}; 12use 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.
14pub struct Sys { 15pub struct Sys<'a> {
15 _private: (), 16 ipcc_system_cmd_rsp_channel: IpccTxChannel<'a>,
17 ipcc_system_event_channel: IpccRxChannel<'a>,
16} 18}
17 19
18impl Sys { 20impl<'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")]
194pub static mut TL_REF_TABLE: MaybeUninit<RefTable> = MaybeUninit::uninit(); 194pub static mut TL_REF_TABLE: MaybeUninit<RefTable> = MaybeUninit::zeroed();
195 195
196#[unsafe(link_section = "MB_MEM1")] 196#[unsafe(link_section = "MB_MEM1")]
197pub static mut TL_DEVICE_INFO_TABLE: Aligned<A4, MaybeUninit<DeviceInfoTable>> = Aligned(MaybeUninit::uninit()); 197pub 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")]
200pub static mut TL_BLE_TABLE: Aligned<A4, MaybeUninit<BleTable>> = Aligned(MaybeUninit::uninit()); 200pub 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")]
203pub static mut TL_THREAD_TABLE: Aligned<A4, MaybeUninit<ThreadTable>> = Aligned(MaybeUninit::uninit()); 203pub 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")]
206pub static mut TL_LLD_TESTS_TABLE: Aligned<A4, MaybeUninit<LldTestsTable>> = Aligned(MaybeUninit::uninit()); 206pub 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")]
209pub static mut TL_BLE_LLD_TABLE: Aligned<A4, MaybeUninit<BleLldTable>> = Aligned(MaybeUninit::uninit()); 209pub 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")]
212pub static mut TL_SYS_TABLE: Aligned<A4, MaybeUninit<SysTable>> = Aligned(MaybeUninit::uninit()); 212pub 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")]
215pub static mut TL_MEM_MANAGER_TABLE: Aligned<A4, MaybeUninit<MemManagerTable>> = Aligned(MaybeUninit::uninit()); 215pub 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")]
218pub static mut TL_TRACES_TABLE: Aligned<A4, MaybeUninit<TracesTable>> = Aligned(MaybeUninit::uninit()); 218pub 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")]
221pub static mut TL_MAC_802_15_4_TABLE: Aligned<A4, MaybeUninit<Mac802_15_4Table>> = Aligned(MaybeUninit::uninit()); 221pub 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")]
224pub static mut TL_ZIGBEE_TABLE: Aligned<A4, MaybeUninit<ZigbeeTable>> = Aligned(MaybeUninit::uninit()); 224pub 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")]
228pub static mut FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit()); 228pub 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")]
232pub static mut TRACES_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit()); 232pub 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")]
235pub static mut CS_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]>> = 235pub 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")]
239pub static mut EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit()); 239pub 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")]
242pub static mut SYSTEM_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit()); 242pub 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")]
247pub static mut MAC_802_15_4_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::uninit()); 247pub 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")]
251pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit< 251pub 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")]
256pub static mut EVT_POOL: Aligned<A4, MaybeUninit<[u8; POOL_SIZE]>> = Aligned(MaybeUninit::uninit()); 256pub 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")]
259pub static mut SYS_CMD_BUF: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::uninit()); 259pub 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")]
262pub static mut SYS_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> = 262pub 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")]
267pub static mut MAC_802_15_4_CNFINDNOT: Aligned<A4, MaybeUninit<[u8; C_SIZE_CMD_STRING]>> = 267pub 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")]
272pub static mut BLE_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::uninit()); 272pub 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")]
276pub static mut BLE_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> = 276pub 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
282pub static mut HCI_ACL_DATA_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]>> = 282pub 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" }
187embedded-io-async = { version = "0.6.1" } 188embedded-io-async = { version = "0.6.1" }
188chrono = { version = "^0.4", default-features = false, optional = true } 189chrono = { version = "^0.4", default-features = false, optional = true }
189bit_field = "0.10.2" 190bit_field = "0.10.2"
191trait-set = "0.3.0"
190document-features = "0.2.7" 192document-features = "0.2.7"
191 193
192static_assertions = { version = "1.1" } 194static_assertions = { version = "1.1" }
@@ -198,11 +200,11 @@ aligned = "0.4.1"
198heapless = "0.9.1" 200heapless = "0.9.1"
199 201
200#stm32-metapac = { version = "18" } 202#stm32-metapac = { version = "18" }
201stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b77c8d968f53b18d6bdcd052e354b5070ec2bbc2" } 203stm32-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"]}
205stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b77c8d968f53b18d6bdcd052e354b5070ec2bbc2", default-features = false, features = ["metadata"] } 207stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f61ed017ef12ec84ff04c49e3147694bda3b29cb", default-features = false, features = ["metadata"] }
206 208
207proc-macro2 = "1.0.36" 209proc-macro2 = "1.0.36"
208quote = "1.0.15" 210quote = "1.0.15"
@@ -1642,7 +1644,30 @@ stm32l562qe = [ "stm32-metapac/stm32l562qe" ]
1642stm32l562re = [ "stm32-metapac/stm32l562re" ] 1644stm32l562re = [ "stm32-metapac/stm32l562re" ]
1643stm32l562ve = [ "stm32-metapac/stm32l562ve" ] 1645stm32l562ve = [ "stm32-metapac/stm32l562ve" ]
1644stm32l562ze = [ "stm32-metapac/stm32l562ze" ] 1646stm32l562ze = [ "stm32-metapac/stm32l562ze" ]
1647stm32n645a0 = [ "stm32-metapac/stm32n645a0" ]
1648stm32n645b0 = [ "stm32-metapac/stm32n645b0" ]
1649stm32n645i0 = [ "stm32-metapac/stm32n645i0" ]
1650stm32n645l0 = [ "stm32-metapac/stm32n645l0" ]
1651stm32n645x0 = [ "stm32-metapac/stm32n645x0" ]
1652stm32n645z0 = [ "stm32-metapac/stm32n645z0" ]
1653stm32n647a0 = [ "stm32-metapac/stm32n647a0" ]
1654stm32n647b0 = [ "stm32-metapac/stm32n647b0" ]
1655stm32n647i0 = [ "stm32-metapac/stm32n647i0" ]
1656stm32n647l0 = [ "stm32-metapac/stm32n647l0" ]
1657stm32n647x0 = [ "stm32-metapac/stm32n647x0" ]
1658stm32n647z0 = [ "stm32-metapac/stm32n647z0" ]
1659stm32n655a0 = [ "stm32-metapac/stm32n655a0" ]
1660stm32n655b0 = [ "stm32-metapac/stm32n655b0" ]
1661stm32n655i0 = [ "stm32-metapac/stm32n655i0" ]
1662stm32n655l0 = [ "stm32-metapac/stm32n655l0" ]
1663stm32n655x0 = [ "stm32-metapac/stm32n655x0" ]
1664stm32n655z0 = [ "stm32-metapac/stm32n655z0" ]
1665stm32n657a0 = [ "stm32-metapac/stm32n657a0" ]
1666stm32n657b0 = [ "stm32-metapac/stm32n657b0" ]
1667stm32n657i0 = [ "stm32-metapac/stm32n657i0" ]
1668stm32n657l0 = [ "stm32-metapac/stm32n657l0" ]
1645stm32n657x0 = [ "stm32-metapac/stm32n657x0" ] 1669stm32n657x0 = [ "stm32-metapac/stm32n657x0" ]
1670stm32n657z0 = [ "stm32-metapac/stm32n657z0" ]
1646stm32u031c6 = [ "stm32-metapac/stm32u031c6" ] 1671stm32u031c6 = [ "stm32-metapac/stm32u031c6" ]
1647stm32u031c8 = [ "stm32-metapac/stm32u031c8" ] 1672stm32u031c8 = [ "stm32-metapac/stm32u031c8" ]
1648stm32u031f4 = [ "stm32-metapac/stm32u031f4" ] 1673stm32u031f4 = [ "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)]
5use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel}; 5use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel};
6 6
7use super::{AdcChannel, AnyAdcChannel, RxDma4, SealedAdcChannel, blocking_delay_us}; 7use super::blocking_delay_us;
8use crate::dma::Transfer; 8use crate::adc::ConversionMode;
9#[cfg(stm32u5)] 9#[cfg(stm32u5)]
10pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr; 10pub 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.
25pub const VREF_CALIB_MV: u32 = 3300; 25pub const VREF_CALIB_MV: u32 = 3300;
26 26
27const VREF_CHANNEL: u8 = 0; 27impl<'d, T: Instance> super::SealedSpecialConverter<super::VrefInt> for Adc4<'d, T> {
28const VCORE_CHANNEL: u8 = 12; 28 const CHANNEL: u8 = 0;
29const TEMP_CHANNEL: u8 = 13;
30const VBAT_CHANNEL: u8 = 14;
31const 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.
35pub struct VrefInt;
36impl<T: Instance> AdcChannel<T> for VrefInt {}
37impl<T: Instance> SealedAdcChannel<T> for VrefInt {
38 fn channel(&self) -> u8 {
39 VREF_CHANNEL
40 }
41} 29}
42 30
43/// Internal temperature channel. 31impl<'d, T: Instance> super::SealedSpecialConverter<super::Temperature> for Adc4<'d, T> {
44pub struct Temperature; 32 const CHANNEL: u8 = 13;
45impl<T: Instance> AdcChannel<T> for Temperature {}
46impl<T: Instance> SealedAdcChannel<T> for Temperature {
47 fn channel(&self) -> u8 {
48 TEMP_CHANNEL
49 }
50} 33}
51 34
52/// Internal battery voltage channel. 35impl<'d, T: Instance> super::SealedSpecialConverter<super::Vcore> for Adc4<'d, T> {
53pub struct Vbat; 36 const CHANNEL: u8 = 12;
54impl<T: Instance> AdcChannel<T> for Vbat {}
55impl<T: Instance> SealedAdcChannel<T> for Vbat {
56 fn channel(&self) -> u8 {
57 VBAT_CHANNEL
58 }
59} 37}
60 38
61/// Internal DAC channel. 39impl<'d, T: Instance> super::SealedSpecialConverter<super::Vbat> for Adc4<'d, T> {
62pub struct Dac; 40 const CHANNEL: u8 = 14;
63impl<T: Instance> AdcChannel<T> for Dac {}
64impl<T: Instance> SealedAdcChannel<T> for Dac {
65 fn channel(&self) -> u8 {
66 DAC_CHANNEL
67 }
68} 41}
69 42
70/// Internal Vcore channel. 43impl<'d, T: Instance> super::SealedSpecialConverter<super::Dac> for Adc4<'d, T> {
71pub struct Vcore; 44 const CHANNEL: u8 = 21;
72impl<T: Instance> AdcChannel<T> for Vcore {}
73impl<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, 79fn 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 {
114enum 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
129impl 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
102foreach_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
188pub struct Adc4<'d, T: Instance> { 223pub 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
199impl<'d, T: Instance> Adc4<'d, T> { 234impl<'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 @@
1use pac::adc::vals::Scandir;
2#[allow(unused)] 1#[allow(unused)]
3use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr}; 2use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr};
4use pac::adccommon::vals::Presc; 3use pac::adccommon::vals::Presc;
4use stm32_metapac::adc::vals::{SampleTime, Scandir};
5 5
6use super::{ 6use super::{Adc, Instance, Resolution, blocking_delay_us};
7 Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, blocking_delay_us, 7use crate::adc::{AnyInstance, ConversionMode};
8};
9use crate::dma::Transfer;
10use crate::time::Hertz; 8use crate::time::Hertz;
11use crate::{Peri, pac, rcc}; 9use crate::{Peri, pac, rcc};
12 10
@@ -19,7 +17,6 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(25);
19 17
20const TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US: u32 = 20; 18const TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US: u32 = 20;
21 19
22const NUM_HW_CHANNELS: u8 = 22;
23const CHSELR_SQ_SIZE: usize = 8; 20const CHSELR_SQ_SIZE: usize = 8;
24const CHSELR_SQ_MAX_CHANNEL: u8 = 14; 21const CHSELR_SQ_MAX_CHANNEL: u8 = 14;
25const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111; 22const 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)] 32fn from_ker_ck(frequency: Hertz) -> Presc {
36pub 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
51impl Prescaler { 46impl<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)>) {
103impl<'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
127pub 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
141impl<'d, T: Instance> Adc<'d, T> { 184impl<'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 @@
1use core::mem; 1#[cfg(stm32g4)]
2 2use pac::adc::regs::Difsel as DifselReg;
3#[allow(unused)]
4#[cfg(stm32g4)]
5pub use pac::adc::vals::{Adcaldif, Adstp, Difsel, Dmacfg, Dmaen, Exten, Rovsm, Trovs};
3#[allow(unused)] 6#[allow(unused)]
4#[cfg(stm32h7)] 7#[cfg(stm32h7)]
5use pac::adc::vals::{Adcaldif, Difsel, Exten}; 8use pac::adc::vals::{Adcaldif, Difsel, Exten};
6#[allow(unused)] 9pub use pac::adccommon::vals::{Dual, Presc};
7#[cfg(stm32g4)] 10
8pub use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs}; 11use super::{
9pub use pac::adccommon::vals::Presc; 12 Adc, AnyAdcChannel, ConversionMode, Instance, RegularConversionMode, Resolution, RxDma, SampleTime,
10pub use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; 13 blocking_delay_us,
11pub use stm32_metapac::adccommon::vals::Dual; 14};
12 15use crate::adc::{AnyInstance, SealedAdcChannel};
13use super::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, blocking_delay_us};
14use crate::adc::SealedAdcChannel;
15use crate::dma::Transfer;
16use crate::time::Hertz; 16use crate::time::Hertz;
17use crate::{Peri, pac, rcc}; 17use crate::{Peri, pac, rcc};
18 18
19mod ringbuffered;
20pub use ringbuffered::RingBufferedAdc;
21
22mod injected; 19mod injected;
23pub use injected::InjectedAdc; 20pub use injected::InjectedAdc;
24 21
@@ -35,72 +32,31 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60);
35#[cfg(stm32h7)] 32#[cfg(stm32h7)]
36const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); 33const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50);
37 34
38// NOTE (unused): The prescaler enum closely copies the hardware capabilities, 35fn 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 {
41enum 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
56impl 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, 51pub 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 71impl<T: Instance> super::SealedAnyInstance for T {
116#[derive(Copy, Clone)] 72 fn dr() -> *mut u16 {
117pub 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
124impl<'d, T: Instance> Adc<'d, T> { 244impl<'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
752impl<T: Instance, const N: usize> InjectedAdc<T, N> { 534impl<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};
5use embassy_hal_internal::Peri; 5use embassy_hal_internal::Peri;
6 6
7use super::{AnyAdcChannel, SampleTime}; 7use super::{AnyAdcChannel, SampleTime};
8use crate::adc::Adc;
9#[allow(unused_imports)] 8#[allow(unused_imports)]
10use crate::adc::Instance; 9use crate::adc::Instance;
10use crate::adc::{Adc, AnyInstance};
11 11
12/// Injected ADC sequence with owned channels. 12/// Injected ADC sequence with owned channels.
13pub struct InjectedAdc<T: Instance, const N: usize> { 13pub 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
39impl<T: Instance, const N: usize> Drop for InjectedAdc<T, N> { 39impl<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")]
18mod _version; 18mod _version;
19 19
20#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))]
21mod ringbuffered;
22
20use core::marker::PhantomData; 23use core::marker::PhantomData;
21 24
22#[allow(unused)] 25#[allow(unused)]
@@ -25,26 +28,25 @@ pub use _version::*;
25use embassy_hal_internal::{PeripheralType, impl_peripheral}; 28use 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))]
27use embassy_sync::waitqueue::AtomicWaker; 30use embassy_sync::waitqueue::AtomicWaker;
31#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))]
32pub 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"]
31pub mod adc4; 36pub mod adc4;
32 37
38#[allow(unused)]
39pub(self) use crate::block_for_us as blocking_delay_us;
33pub use crate::pac::adc::vals; 40pub use crate::pac::adc::vals;
34#[cfg(not(any(adc_f1, adc_f3v3)))] 41#[cfg(not(any(adc_f1, adc_f3v3)))]
35pub use crate::pac::adc::vals::Res as Resolution; 42pub use crate::pac::adc::vals::Res as Resolution;
36pub use crate::pac::adc::vals::SampleTime; 43pub use crate::pac::adc::vals::SampleTime;
37use crate::peripherals; 44use crate::peripherals;
38 45
39#[cfg(not(adc_wba))] 46dma_trait!(RxDma, AnyInstance);
40dma_trait!(RxDma, Instance);
41#[cfg(adc_u5)]
42dma_trait!(RxDma4, adc4::Instance);
43#[cfg(adc_wba)]
44dma_trait!(RxDma4, adc4::Instance);
45 47
46/// Analog to Digital driver. 48/// Analog to Digital driver.
47pub struct Adc<'d, T: Instance> { 49pub 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))]
92pub(crate) fn blocking_delay_us(us: u32) { 94trait_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 { 101pub 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))]
108pub(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)]
122pub 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)]
127pub 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))]
133impl<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))]
140impl<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))]
146pub 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))]
165pub(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)]
178pub 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
186impl<'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 {}
143pub struct Vbat; 386pub struct Vbat;
144impl SpecialChannel for Vbat {} 387impl SpecialChannel for Vbat {}
145 388
389/// Vcore channel.
390pub struct Vcore;
391impl SpecialChannel for Vcore {}
392
393/// Internal dac channel.
394pub struct Dac;
395impl 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}
190impl_peripheral!(AnyAdcChannel<T: Instance>); 441impl_peripheral!(AnyAdcChannel<T: AnyInstance>);
191impl<T: Instance> AdcChannel<T> for AnyAdcChannel<T> {} 442impl<T: AnyInstance> AdcChannel<T> for AnyAdcChannel<T> {}
192impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<T> { 443impl<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)]
5use embassy_hal_internal::Peri; 5use embassy_hal_internal::Peri;
6 6
7use crate::adc::Adc; 7use crate::adc::AnyInstance;
8#[allow(unused_imports)] 8#[allow(unused_imports)]
9use crate::adc::{Instance, RxDma}; 9use 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
22impl<'d, T: Instance> RingBufferedAdc<'d, T> { 22impl<'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
173impl<T: Instance> Drop for RingBufferedAdc<'_, T> { 171impl<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 @@
1use core::mem;
2use core::sync::atomic::{Ordering, compiler_fence}; 1use core::sync::atomic::{Ordering, compiler_fence};
3 2
4use super::{Temperature, Vbat, VrefInt, blocking_delay_us}; 3use super::{ConversionMode, Temperature, Vbat, VrefInt, blocking_delay_us};
5use crate::adc::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel}; 4use crate::adc::{Adc, Instance, Resolution, SampleTime};
6use crate::pac::adc::vals; 5use crate::pac::adc::vals;
6pub use crate::pac::adccommon::vals::Adcpre;
7use crate::time::Hertz; 7use crate::time::Hertz;
8use crate::{Peri, rcc}; 8use crate::{Peri, rcc};
9 9
10mod ringbuffered;
11pub use ringbuffered::RingBufferedAdc;
12
13fn clear_interrupt_flags(r: crate::pac::adc::Adc) { 10fn 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
57enum Prescaler { 54fn 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);
64impl 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, 73pub 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
92impl<'d, T> Adc<'d, T> 77impl<T: Instance> super::SealedAnyInstance for T {
93where 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() { 204impl<'d, T> Adc<'d, T>
251 let r = T::regs(); 205where
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 @@
1use cfg_if::cfg_if; 1use cfg_if::cfg_if;
2#[cfg(adc_g0)] 2#[cfg(adc_g0)]
3use heapless::Vec; 3use heapless::Vec;
4use pac::adc::vals::Dmacfg;
5#[cfg(adc_g0)] 4#[cfg(adc_g0)]
6use pac::adc::vals::{Ckmode, Smpsel}; 5use pac::adc::vals::Ckmode;
6use pac::adc::vals::Dmacfg;
7#[cfg(adc_v3)] 7#[cfg(adc_v3)]
8use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; 8use 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)]
13use super::SealedAdcChannel; 13use super::SealedAdcChannel;
14use super::{ 14use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us};
15 Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, Temperature, Vbat, VrefInt, 15use crate::adc::ConversionMode;
16 blocking_delay_us,
17};
18
19#[cfg(any(adc_v3, adc_g0, adc_u0))]
20mod ringbuffered;
21
22#[cfg(any(adc_v3, adc_g0, adc_u0))]
23use ringbuffered::RingBufferedAdc;
24
25use crate::dma::Transfer;
26use crate::{Peri, pac, rcc}; 16use 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))]
77impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { 67impl<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)]
81impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { 71impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T {
@@ -89,10 +79,10 @@ impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T {
89cfg_if! { 79cfg_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! {
101cfg_if! { 91cfg_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))]
116pub enum Averaging {
117 Disabled,
118 Samples2,
119 Samples4,
120 Samples8,
121 Samples16,
122 Samples32,
123 Samples64,
124 Samples128,
125 Samples256,
126}
127
128cfg_if! { if #[cfg(adc_g0)] { 103cfg_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
148impl<'d, T: Instance> Adc<'d, T> { 123#[cfg(adc_u0)]
149 /// Enable the voltage regulator 124type Ovss = u8;
150 fn init_regulator() { 125#[cfg(adc_u0)]
151 rcc::enable_and_reset::<T>(); 126type Ovsr = u8;
152 T::regs().cr().modify(|reg| { 127#[cfg(adc_v3)]
153 #[cfg(not(any(adc_g0, adc_u0)))] 128type Ovss = OversamplingShift;
154 reg.set_deeppwd(false); 129#[cfg(adc_v3)]
155 reg.set_advregen(true); 130type 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. 134pub 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); 149impl<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)))] 391impl<'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};
4use pac::adc::vals::{Adstp, Difsel, Dmngt, Exten, Pcsel}; 4use pac::adc::vals::{Adstp, Difsel, Dmngt, Exten, Pcsel};
5use pac::adccommon::vals::Presc; 5use pac::adccommon::vals::Presc;
6 6
7use super::{ 7use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us};
8 Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, Temperature, Vbat, 8use crate::adc::ConversionMode;
9 VrefInt, blocking_delay_us,
10};
11use crate::dma::Transfer;
12use crate::time::Hertz; 9use crate::time::Hertz;
13use crate::{Peri, pac, rcc}; 10use 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, 62fn 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 {
68enum 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)]
78pub struct AdcConfig {
79 pub resolution: Option<Resolution>,
80 pub averaging: Option<Averaging>,
81} 81}
82 82
83impl Prescaler { 83impl<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| {
136pub 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
150impl<'d, T: Instance> Adc<'d, T> { 203impl<'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
9use super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; 9use super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer};
10use super::word::{Word, WordSize}; 10use super::word::{Word, WordSize};
11use super::{AnyChannel, Channel, Dir, Request, STATE}; 11use super::{AnyChannel, BusyChannel, Channel, Dir, Request, STATE};
12use crate::interrupt::typelevel::Interrupt; 12use crate::interrupt::typelevel::Interrupt;
13use crate::{interrupt, pac}; 13use 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"]
604pub struct Transfer<'a> { 604pub struct Transfer<'a> {
605 channel: Peri<'a, AnyChannel>, 605 channel: BusyChannel<'a>,
606} 606}
607 607
608impl<'a> Transfer<'a> { 608impl<'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.
818pub struct ReadableRingBuffer<'a, W: Word> { 820pub 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.
974pub struct WritableRingBuffer<'a, W: Word> { 976pub 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
12use super::word::{Word, WordSize}; 12use super::word::{Word, WordSize};
13use super::{AnyChannel, Channel, Dir, Request, STATE}; 13use super::{AnyChannel, Channel, Dir, Request, STATE};
14use crate::dma::BusyChannel;
14use crate::interrupt::typelevel::Interrupt; 15use crate::interrupt::typelevel::Interrupt;
15use crate::pac; 16use crate::pac;
16use crate::pac::gpdma::vals; 17use 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"]
410pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> { 411pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> {
411 channel: Peri<'a, AnyChannel>, 412 channel: BusyChannel<'a>,
412} 413}
413 414
414impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { 415impl<'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"]
507pub struct Transfer<'a> { 510pub struct Transfer<'a> {
508 channel: Peri<'a, AnyChannel>, 511 channel: BusyChannel<'a>,
509} 512}
510 513
511impl<'a> Transfer<'a> { 514impl<'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};
12use crate::dma::gpdma::linked_list::{RunMode, Table}; 12use crate::dma::gpdma::linked_list::{RunMode, Table};
13use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; 13use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer};
14use crate::dma::word::Word; 14use crate::dma::word::Word;
15use crate::dma::{Channel, Dir, Request}; 15use crate::dma::{BusyChannel, Channel, Dir, Request};
16 16
17struct DmaCtrlImpl<'a>(Peri<'a, AnyChannel>); 17struct 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.
51pub struct ReadableRingBuffer<'a, W: Word> { 51pub 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.
191pub struct WritableRingBuffer<'a, W: Word> { 191pub 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))]
5mod dma_bdma; 5mod dma_bdma;
6use core::ops;
7
6#[cfg(any(bdma, dma))] 8#[cfg(any(bdma, dma))]
7pub use dma_bdma::*; 9pub use dma_bdma::*;
8 10
9#[cfg(gpdma)] 11#[cfg(gpdma)]
10pub(crate) mod gpdma; 12pub(crate) mod gpdma;
13use embassy_hal_internal::Peri;
11#[cfg(gpdma)] 14#[cfg(gpdma)]
12pub use gpdma::ringbuffered::*; 15pub use gpdma::ringbuffered::*;
13#[cfg(gpdma)] 16#[cfg(gpdma)]
@@ -48,6 +51,8 @@ pub type Request = ();
48pub(crate) trait SealedChannel { 51pub(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))]
64macro_rules! dma_channel_impl { 69macro_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
106pub(crate) struct BusyChannel<'a> {
107 channel: Peri<'a, AnyChannel>,
108}
109
110impl<'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
121impl<'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
130impl<'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
138impl<'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.
90pub struct AnyChannel { 145pub 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}
93impl_peripheral!(AnyChannel); 150impl_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}
107impl Channel for AnyChannel {} 169impl 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;
5use embassy_hal_internal::PeripheralType; 5use embassy_hal_internal::PeripheralType;
6 6
7//use crate::gpio::{AnyPin, SealedPin}; 7//use crate::gpio::{AnyPin, SealedPin};
8use crate::block_for_us;
8use crate::gpio::{AfType, AnyPin, OutputType, Speed}; 9use crate::gpio::{AfType, AnyPin, OutputType, Speed};
9use crate::rcc::{self, RccPeripheral}; 10use crate::rcc::{self, RccPeripheral};
10use crate::{Peri, peripherals}; 11use crate::{Peri, peripherals};
11 12
12/// Performs a busy-wait delay for a specified number of microseconds.
13pub 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};
8use futures_util::FutureExt; 8use futures_util::FutureExt;
9 9
10use super::{Phy, StationManagement}; 10use super::{Phy, StationManagement};
11use crate::block_for_us as blocking_delay_us;
11 12
12#[allow(dead_code)] 13#[allow(dead_code)]
13mod phy_consts { 14mod phy_consts {
@@ -43,21 +44,23 @@ mod phy_consts {
43use self::phy_consts::*; 44use self::phy_consts::*;
44 45
45/// Generic SMI Ethernet PHY implementation 46/// Generic SMI Ethernet PHY implementation
46pub struct GenericPhy { 47pub 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
52impl GenericPhy { 54impl<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 83impl<SM: StationManagement> Phy for GenericPhy<SM> {
80fn 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
92impl 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
151impl GenericPhy { 142impl<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")]
6mod _version; 6mod _version;
7mod generic_phy; 7mod generic_phy;
8mod sma;
8 9
9use core::mem::MaybeUninit; 10use core::mem::MaybeUninit;
10use core::task::Context; 11use core::task::Context;
@@ -15,6 +16,7 @@ use embassy_sync::waitqueue::AtomicWaker;
15 16
16pub use self::_version::{InterruptHandler, *}; 17pub use self::_version::{InterruptHandler, *};
17pub use self::generic_phy::*; 18pub use self::generic_phy::*;
19pub use self::sma::{Sma, StationManagement};
18use crate::rcc::RccPeripheral; 20use 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
161pub 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
169pub trait Phy { 163pub 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
178impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { 172impl<'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 {}
212pin_trait!(RXClkPin, Instance, @A); 199pin_trait!(RXClkPin, Instance, @A);
213pin_trait!(TXClkPin, Instance, @A); 200pin_trait!(TXClkPin, Instance, @A);
214pin_trait!(RefClkPin, Instance, @A); 201pin_trait!(RefClkPin, Instance, @A);
215pin_trait!(MDIOPin, Instance, @A); 202pin_trait!(MDIOPin, sma::Instance, @A);
216pin_trait!(MDCPin, Instance, @A); 203pin_trait!(MDCPin, sma::Instance, @A);
217pin_trait!(RXDVPin, Instance, @A); 204pin_trait!(RXDVPin, Instance, @A);
218pin_trait!(CRSPin, Instance, @A); 205pin_trait!(CRSPin, Instance, @A);
219pin_trait!(RXD0Pin, Instance, @A); 206pin_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")]
7mod _version;
8
9use embassy_hal_internal::PeripheralType;
10use stm32_metapac::common::{RW, Reg};
11
12pub use self::_version::*;
13
14/// Station Management Interface (SMI).
15pub 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
22trait SealedInstance {
23 fn regs() -> (Reg<AddressRegister, RW>, Reg<DataRegister, RW>);
24}
25
26/// MDIO instance.
27#[allow(private_bounds)]
28pub trait Instance: SealedInstance + PeripheralType + Send + 'static {}
29
30impl 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
42impl 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 @@
1use embassy_hal_internal::Peri;
2pub(crate) use regs::{Macmiiar as AddressRegister, Macmiidr as DataRegister};
3use stm32_metapac::eth::regs;
4use stm32_metapac::eth::vals::{Cr, MbProgress, Mw};
5
6use super::{Instance, StationManagement};
7use crate::eth::{MDCPin, MDIOPin};
8use 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).
14pub struct Sma<'d, T: Instance> {
15 _peri: Peri<'d, T>,
16 clock_range: Cr,
17 pins: [Peri<'d, AnyPin>; 2],
18}
19
20impl<'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
68impl<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
98impl<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 @@
1use embassy_hal_internal::Peri;
2pub(crate) use regs::{Macmdioar as AddressRegister, Macmdiodr as DataRegister};
3use stm32_metapac::eth::regs;
4
5use super::{Instance, StationManagement};
6use crate::eth::{MDCPin, MDIOPin};
7use 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).
13pub struct Sma<'d, T: Instance> {
14 _peri: Peri<'d, T>,
15 pins: [Peri<'d, AnyPin>; 2],
16 clock_range: u8,
17}
18
19impl<'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
57impl<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
90impl<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 @@
3mod rx_desc; 3mod rx_desc;
4mod tx_desc; 4mod tx_desc;
5 5
6use core::marker::PhantomData;
7use core::sync::atomic::{Ordering, fence}; 6use core::sync::atomic::{Ordering, fence};
8 7
9use embassy_hal_internal::Peri; 8use embassy_hal_internal::Peri;
10use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; 9use stm32_metapac::eth::vals::{Apcs, Dm, DmaomrSr, Fes, Ftf, Ifg, Pbl, Rsf, St, Tsf};
11 10
12pub(crate) use self::rx_desc::{RDes, RDesRing}; 11pub(crate) use self::rx_desc::{RDes, RDesRing};
13pub(crate) use self::tx_desc::{TDes, TDesRing}; 12pub(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))]
23use crate::pac::SYSCFG; 22use crate::pac::SYSCFG;
24use crate::pac::{ETH, RCC}; 23use crate::pac::{ETH, RCC};
25use crate::rcc::SealedRccPeripheral;
26 24
27/// Interrupt handler. 25/// Interrupt handler.
28pub struct InterruptHandler {} 26pub 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.
61enum Pins<'d> { 58enum 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
100impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { 97impl<'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
169impl<'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.
379pub(crate) struct EthernetStationManagement<T: Instance> {
380 peri: PhantomData<T>,
381 clock_range: Cr,
382}
383
384impl<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 @@
1mod descriptors; 1mod descriptors;
2 2
3use core::marker::PhantomData;
4use core::sync::atomic::{Ordering, fence}; 3use core::sync::atomic::{Ordering, fence};
5 4
6use embassy_hal_internal::Peri; 5use embassy_hal_internal::Peri;
@@ -12,7 +11,6 @@ use crate::gpio::{AfType, AnyPin, OutputType, SealedPin as _, Speed};
12use crate::interrupt; 11use crate::interrupt;
13use crate::interrupt::InterruptExt; 12use crate::interrupt::InterruptExt;
14use crate::pac::ETH; 13use crate::pac::ETH;
15use crate::rcc::SealedRccPeripheral;
16 14
17/// Interrupt handler. 15/// Interrupt handler.
18pub struct InterruptHandler {} 16pub 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.
50enum Pins<'d> { 47enum 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
55macro_rules! config_pins { 52macro_rules! config_pins {
@@ -63,41 +60,96 @@ macro_rules! config_pins {
63 }; 60 };
64} 61}
65 62
66impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { 63impl<'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); 133impl<'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.
302pub struct EthernetStationManagement<T: Instance> {
303 peri: PhantomData<T>,
304 clock_range: u8,
305}
306
307impl<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
337impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> { 328impl<'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;
5use core::pin::Pin; 5use core::pin::Pin;
6use core::task::{Context, Poll}; 6use core::task::{Context, Poll};
7 7
8use embassy_hal_internal::{PeripheralType, impl_peripheral}; 8use embassy_hal_internal::PeripheralType;
9use embassy_sync::waitqueue::AtomicWaker; 9use embassy_sync::waitqueue::AtomicWaker;
10use futures_util::FutureExt;
10 11
11use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, PinNumber, Pull}; 12use crate::gpio::{AnyPin, ExtiPin, Input, Level, Pin as GpioPin, PinNumber, Pull};
13use crate::interrupt::Interrupt as InterruptEnum;
14use crate::interrupt::typelevel::{Binding, Handler, Interrupt as InterruptType};
12use crate::pac::EXTI; 15use crate::pac::EXTI;
13use crate::pac::exti::regs::Lines; 16use crate::pac::exti::regs::Lines;
14use crate::{Peri, interrupt, pac, peripherals}; 17use crate::{Peri, pac};
15 18
16const EXTI_COUNT: usize = 16; 19const EXTI_COUNT: usize = 16;
17static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT]; 20static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT];
@@ -105,10 +108,17 @@ impl<'d> Unpin for ExtiInput<'d> {}
105 108
106impl<'d> ExtiInput<'d> { 109impl<'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"]
228struct ExtiInputFuture<'a> { 259struct 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
233impl<'a> ExtiInputFuture<'a> { 265impl<'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
260impl<'a> Drop for ExtiInputFuture<'a> { 293impl<'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
318macro_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 }; 361pub struct InterruptHandler<T: crate::interrupt::typelevel::Interrupt> {
362 _phantom: PhantomData<T>,
327} 363}
328 364
329foreach_exti_irq!(impl_irq); 365impl<T: InterruptType> Handler<T> for InterruptHandler<T> {
366 unsafe fn on_interrupt() {
367 on_irq()
368 }
369}
330 370
331trait SealedChannel {} 371trait SealedChannel {}
332 372
333/// EXTI channel trait. 373/// EXTI channel trait.
334#[allow(private_bounds)] 374#[allow(private_bounds)]
335pub trait Channel: PeripheralType + SealedChannel + Sized { 375pub 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)"]
343pub struct AnyChannel { 391pub struct AnyChannel {
392 #[allow(unused)]
344 number: PinNumber, 393 number: PinNumber,
345} 394}
346 395
347impl_peripheral!(AnyChannel);
348impl SealedChannel for AnyChannel {}
349impl Channel for AnyChannel {
350 fn number(&self) -> PinNumber {
351 self.number
352 }
353}
354
355macro_rules! impl_exti { 396macro_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 @@
1use core::ptr::write_volatile;
2use core::sync::atomic::{Ordering, fence};
3
4use cortex_m::interrupt;
5
6use super::{FlashSector, WRITE_SIZE};
7use crate::flash::Error;
8use crate::pac;
9
10pub(crate) unsafe fn lock() {
11 pac::FLASH.cr().modify(|w| w.set_lock(true));
12}
13pub(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
24pub(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
29pub(crate) unsafe fn disable_blocking_write() {
30 pac::FLASH.cr().write(|w| w.set_pg(false));
31}
32
33pub(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
46pub(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
103pub(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
123pub(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
129fn 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
46pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { 46pub(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 @@
1use core::ptr::write_volatile; 1use core::ptr::write_volatile;
2use core::sync::atomic::{Ordering, fence}; 2use core::sync::atomic::{Ordering, fence};
3 3
4use embassy_sync::waitqueue::AtomicWaker;
5use pac::flash::regs::Sr;
6
4use super::{BANK1_REGION, FLASH_REGIONS, FlashSector, WRITE_SIZE}; 7use super::{BANK1_REGION, FLASH_REGIONS, FlashSector, WRITE_SIZE};
5use crate::flash::Error; 8use crate::flash::Error;
6use crate::pac; 9use crate::pac;
7 10
11static WAKER: AtomicWaker = AtomicWaker::new();
12
13pub(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
8const fn is_dual_bank() -> bool { 29const 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
53pub(crate) unsafe fn enable_write() {
54 enable_blocking_write();
55}
56
57pub(crate) unsafe fn disable_write() {
58 disable_blocking_write();
59}
60
32pub(crate) unsafe fn enable_blocking_write() { 61pub(crate) unsafe fn enable_blocking_write() {
33 assert_eq!(0, WRITE_SIZE % 4); 62 assert_eq!(0, WRITE_SIZE % 4);
34} 63}
35 64
36pub(crate) unsafe fn disable_blocking_write() {} 65pub(crate) unsafe fn disable_blocking_write() {}
37 66
67pub(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
38pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { 115pub(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
157pub(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
80pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { 187pub(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
222async 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
115unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { 239unsafe 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
249fn 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)
2use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; 2use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
3 3
4#[cfg(flash_f4)] 4#[cfg(any(flash_f4, flash_h7, flash_h7ab))]
5mod asynch; 5mod asynch;
6#[cfg(flash)] 6#[cfg(flash)]
7mod common; 7mod common;
8#[cfg(eeprom)] 8#[cfg(eeprom)]
9mod eeprom; 9mod eeprom;
10 10
11#[cfg(flash_f4)] 11#[cfg(any(flash_f4, flash_h7, flash_h7ab))]
12pub use asynch::InterruptHandler; 12pub use asynch::InterruptHandler;
13#[cfg(flash)] 13#[cfg(flash)]
14pub use common::*; 14pub 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)]
813pub type PinNumber = u16; 813pub 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)]
817pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static { 818pub 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)]
827pub 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.
838pub struct AnyPin { 842pub struct AnyPin {
839 pin_port: PinNumber, 843 pin_port: PinNumber,
840} 844}
@@ -862,10 +866,7 @@ impl AnyPin {
862} 866}
863 867
864impl_peripheral!(AnyPin); 868impl_peripheral!(AnyPin);
865impl Pin for AnyPin { 869impl Pin for AnyPin {}
866 #[cfg(feature = "exti")]
867 type ExtiChannel = crate::exti::AnyChannel;
868}
869impl SealedPin for AnyPin { 870impl 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 {
878foreach_pin!( 879foreach_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
3use core::future::poll_fn;
4use core::marker::PhantomData;
5use core::sync::atomic::{Ordering, compiler_fence};
6use core::task::Poll;
7
8#[cfg(all(stm32wb, feature = "low-power"))]
9use critical_section::CriticalSection;
3use embassy_hal_internal::PeripheralType; 10use embassy_hal_internal::PeripheralType;
11use 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.
9use crate::Peri; 17use crate::Peri;
10use crate::pac;
11use crate::rcc::{self, RccPeripheral}; 18use crate::rcc::{self, RccPeripheral};
19use 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 52impl 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.
47pub 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)] 84const PUB_CHANNELS: usize = 6;
67fn core_id_to_index(core: CoreId) -> usize { 85
68 match core { 86#[cfg(all(stm32wb, feature = "low-power"))]
69 CoreId::Core0 => 0, 87const PUB_CHANNELS: usize = 4;
70 #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))] 88
71 CoreId::Core1 => 1, 89/// TX interrupt handler.
90pub struct HardwareSemaphoreInterruptHandler<T: Instance> {
91 _phantom: PhantomData<T>,
92}
93
94impl<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
76pub struct HardwareSemaphore<'d, T: Instance> { 115pub 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
80impl<'d, T: Instance> HardwareSemaphore<'d, T> { 121impl<'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
132pub struct HardwareSemaphoreChannel<'a, T: Instance> {
133 index: u8,
134 _lifetime: PhantomData<&'a mut T>,
135}
136
137impl<'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
234pub struct HardwareSemaphore<T: Instance> {
235 _type: PhantomData<T>,
236}
237
238impl<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) { 296pub(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. 305struct 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
309impl 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
176trait SealedInstance { 321trait 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)]
182pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {} 328pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {
329 /// Interrupt for this peripheral.
330 type Interrupt: interrupt::typelevel::Interrupt;
331}
183 332
184impl SealedInstance for crate::peripherals::HSEM { 333impl 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}
189impl Instance for crate::peripherals::HSEM {} 343
344foreach_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
100impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { 100impl<'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
3use core::future::poll_fn; 3use core::future::poll_fn;
4use core::marker::PhantomData;
4use core::sync::atomic::{Ordering, compiler_fence}; 5use core::sync::atomic::{Ordering, compiler_fence};
5use core::task::Poll; 6use core::task::Poll;
6 7
8use embassy_hal_internal::Peri;
7use embassy_sync::waitqueue::AtomicWaker; 9use embassy_sync::waitqueue::AtomicWaker;
8 10
9use crate::interrupt::typelevel::Interrupt; 11use 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)] 70pub struct IpccTxChannel<'a> {
87#[derive(Debug, Clone, Copy)] 71 index: u8,
88#[repr(C)] 72 _lifetime: PhantomData<&'a mut usize>,
89pub 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. 75impl<'a> IpccTxChannel<'a> {
99pub struct Ipcc; 76 pub(crate) const fn new(index: u8) -> Self {
100 77 core::assert!(index < 6);
101impl 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
129pub struct IpccRxChannel<'a> {
130 index: u8,
131 _lifetime: PhantomData<&'a mut usize>,
132}
133
134impl<'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
188pub struct IpccChannel<'a> {
189 index: u8,
190 _lifetime: PhantomData<&'a mut usize>,
191}
192
193impl<'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.
210pub struct Ipcc {
211 _private: (),
212}
213
214impl 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
207impl SealedInstance for crate::peripherals::IPCC { 277impl 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
2use core::marker::PhantomData;
3
4use embassy_hal_internal::{Peri, PeripheralType};
5
6use crate::gpio::{AfType, AnyPin, SealedPin};
7use crate::peripherals;
8use crate::rcc::{self, RccPeripheral};
9use crate::time::Hertz;
10
11#[cfg(any(stm32u0, stm32l073, stm32l083))]
12const NUM_SEGMENTS: u8 = 52;
13#[cfg(any(stm32wb, stm32l4x6, stm32l15x, stm32l162, stm32l4x3, stm32l4x6))]
14const NUM_SEGMENTS: u8 = 44;
15#[cfg(any(stm32l053, stm32l063, stm32l100))]
16const NUM_SEGMENTS: u8 = 32;
17
18/// LCD configuration struct
19#[non_exhaustive]
20#[derive(Debug, Clone, Copy)]
21pub 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
43impl 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)]
62pub 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)]
77pub 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
94impl 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)]
109pub 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)]
130pub 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.
152pub struct Lcd<'d, T: Instance> {
153 _peri: PhantomData<&'d mut T>,
154 duty: Duty,
155 ck_div: u32,
156}
157
158impl<'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
416impl<'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)]
426pub 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)]
442pub 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.
455pub struct LcdPin<'d, T: Instance> {
456 pin: Peri<'d, AnyPin>,
457 af_num: u8,
458 is_seg: bool,
459 _phantom: PhantomData<T>,
460}
461
462impl<'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
488trait SealedInstance: crate::rcc::SealedRccPeripheral + PeripheralType {
489 fn regs() -> crate::pac::lcd::Lcd;
490}
491
492/// DSI instance trait.
493#[allow(private_bounds)]
494pub trait Instance: SealedInstance + RccPeripheral + 'static {}
495
496pin_trait!(SegPin, Instance);
497pin_trait!(ComPin, Instance);
498pin_trait!(VlcdPin, Instance);
499
500foreach_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;
95pub mod i2s; 95pub mod i2s;
96#[cfg(stm32wb)] 96#[cfg(stm32wb)]
97pub mod ipcc; 97pub mod ipcc;
98#[cfg(lcd)]
99pub mod lcd;
98#[cfg(feature = "low-power")] 100#[cfg(feature = "low-power")]
99pub mod low_power; 101pub 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]
183macro_rules! bind_interrupts { 189macro_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)]
670pub(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
51use core::arch::asm; 43use core::arch::asm;
52use core::marker::PhantomData; 44use core::marker::PhantomData;
45use core::mem;
53use core::sync::atomic::{Ordering, compiler_fence}; 46use core::sync::atomic::{Ordering, compiler_fence};
54 47
55use cortex_m::peripheral::SCB; 48use cortex_m::peripheral::SCB;
@@ -57,11 +50,13 @@ use critical_section::CriticalSection;
57use embassy_executor::*; 50use embassy_executor::*;
58 51
59use crate::interrupt; 52use crate::interrupt;
53pub use crate::rcc::StopMode;
54use crate::rcc::{RCC_CONFIG, REFCOUNT_STOP1, REFCOUNT_STOP2, decrement_stop_refcount, increment_stop_refcount};
60use crate::time_driver::get_driver; 55use crate::time_driver::get_driver;
61 56
62const THREAD_PENDER: usize = usize::MAX; 57const THREAD_PENDER: usize = usize::MAX;
63 58
64static mut EXECUTOR: Option<Executor> = None; 59static 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
67pub struct DeviceBusy(StopMode); 62pub 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
97impl Drop for DeviceBusy { 85impl 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.
138pub fn stop_ready(stop_mode: StopMode) -> bool { 119pub 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] 128use crate::pac::pwr::vals::Lpms;
148#[derive(PartialEq)]
149pub 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))]
157use stm32_metapac::pwr::vals::Lpms;
158
159#[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))]
160impl Into<Lpms> for StopMode { 131impl 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 {
182pub struct Executor { 153pub 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
188impl Executor { 158impl 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 @@
4use embassy_hal_internal::PeripheralType; 4use embassy_hal_internal::PeripheralType;
5 5
6use crate::Peri; 6use crate::Peri;
7#[cfg(opamp_v5)]
8use crate::block_for_us;
7use crate::pac::opamp::vals::*; 9use crate::pac::opamp::vals::*;
8#[cfg(not(any(stm32g4, stm32f3)))] 10#[cfg(not(any(stm32g4, stm32f3)))]
9use crate::rcc::RccInfo; 11use crate::rcc::RccInfo;
10 12
11/// Performs a busy-wait delay for a specified number of microseconds.
12#[cfg(opamp_v5)]
13fn 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))]
2use core::mem::MaybeUninit;
3
4#[cfg(any(stm32l0, stm32l1))] 1#[cfg(any(stm32l0, stm32l1))]
5pub use crate::pac::pwr::vals::Vos as VoltageScale; 2pub use crate::pac::pwr::vals::Vos as VoltageScale;
6use crate::pac::rcc::regs::Cfgr; 3use crate::pac::rcc::regs::Cfgr;
@@ -14,42 +11,6 @@ use crate::time::Hertz;
14/// HSI speed 11/// HSI speed
15pub const HSI_FREQ: Hertz = Hertz(16_000_000); 12pub 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))]
22static 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))]
28pub(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))]
37pub(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
43pub(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)]
54pub enum HseMode { 15pub 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
195pub(crate) unsafe fn init(config: Config) { 156pub(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
50pub(crate) static mut REFCOUNT_STOP2: u32 = 0; 50pub(crate) static mut REFCOUNT_STOP2: u32 = 0;
51 51
52#[cfg(feature = "low-power")]
53pub(crate) static mut RCC_CONFIG: Option<Config> = None;
54
52#[cfg(backup_sram)] 55#[cfg(backup_sram)]
53pub(crate) static mut BKSRAM_RETAINED: bool = false; 56pub(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")]
115pub(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")]
128pub(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
111pub(crate) trait SealedRccPeripheral { 140pub(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)]
143pub(crate) enum StopMode { 174#[derive(Debug, Clone, Copy, PartialEq, Default)]
144 Standby, 175pub 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
149impl RccInfo { 185impl 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)]
464pub(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
4use super::{DateTimeError, Rtc, RtcError, bcd2_to_byte}; 4use super::{DateTimeError, Rtc, RtcError, bcd2_to_byte};
5use crate::interrupt::typelevel::Interrupt; 5use crate::interrupt::typelevel::Interrupt;
6use crate::pac::rtc::vals::Wucksel;
6use crate::peripherals::RTC; 7use crate::peripherals::RTC;
7use crate::rtc::{RtcTimeProvider, SealedInstance}; 8use 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)] 62fn wucksel_compute_min(val: u32) -> (Wucksel, u32) {
62#[derive(Clone, Copy, Debug)] 63 *[
63pub(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))]
73impl 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))]
89impl 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
103impl 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
117impl Rtc { 74impl 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")]
382pub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig) { 382pub(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")]
200fn calc_period_counter(ticks: u64) -> (u32, u16) {
201 (2 * (ticks >> 16) as u32 + (ticks as u16 >= 0x8000) as u32, ticks as u16)
202}
203
199struct AlarmState { 204struct 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 {
240impl RtcDriver { 245impl 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 {
546pub(crate) fn init(cs: CriticalSection) { 516pub(crate) fn init(cs: CriticalSection) {
547 DRIVER.init(cs) 517 DRIVER.init(cs)
548} 518}
549
550#[cfg(all(feature = "low-power", stm32wlex))]
551pub(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;
13pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource}; 13pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource};
14 14
15use super::*; 15use super::*;
16use crate::dma::{Transfer, WritableRingBuffer};
16use crate::pac::timer::vals; 17use crate::pac::timer::vals;
17use crate::rcc; 18use crate::rcc;
18use crate::time::Hertz; 19use 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;
12pub mod one_pulse; 12pub mod one_pulse;
13pub mod pwm_input; 13pub mod pwm_input;
14pub mod qei; 14pub mod qei;
15pub mod ringbuffered;
15pub mod simple_pwm; 16pub mod simple_pwm;
16 17
17use crate::interrupt; 18use 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
3use core::mem::ManuallyDrop;
4use core::task::Waker;
5
6use super::low_level::Timer;
7use super::{Channel, GeneralInstance4Channel};
8use crate::dma::WritableRingBuffer;
9use 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/// ```
26pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> {
27 timer: ManuallyDrop<Timer<'d, T>>,
28 ring_buf: WritableRingBuffer<'d, u16>,
29 channel: Channel,
30}
31
32impl<'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`].
160pub 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;
4use core::mem::ManuallyDrop; 4use core::mem::ManuallyDrop;
5 5
6use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; 6use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer};
7use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin}; 7use super::ringbuffered::RingBufferedPwmChannel;
8use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin};
8use crate::Peri; 9use crate::Peri;
9#[cfg(gpio_v2)] 10#[cfg(gpio_v2)]
10use crate::gpio::Pull; 11use 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
472impl<'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 = []
118tick-hz-262_144 = [] 118tick-hz-262_144 = []
119## 320.0kHz Tick Rate 119## 320.0kHz Tick Rate
120tick-hz-320_000 = [] 120tick-hz-320_000 = []
121## 375.0kHz Tick Rate
122tick-hz-375_000 = []
121## 512.0kHz Tick Rate 123## 512.0kHz Tick Rate
122tick-hz-512_000 = [] 124tick-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 @@
1import os 1import os
2from glob import glob
3 2
4abspath = os.path.abspath(__file__) 3abspath = os.path.abspath(__file__)
5dname = os.path.dirname(abspath) 4dname = 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)
23for i in range(15, 50): 22for i in range(15, 50):
24 ticks.append(20 * i * 1_000_000) 23 ticks.append(20 * i * 1_000_000)
24
25ticks.append(375 * 1000)
25ticks.append(133 * 1_000_000) 26ticks.append(133 * 1_000_000)
26 27
27seen = set() 28seen = 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;
74pub const TICK_HZ: u64 = 262_144; 74pub const TICK_HZ: u64 = 262_144;
75#[cfg(feature = "tick-hz-320_000")] 75#[cfg(feature = "tick-hz-320_000")]
76pub const TICK_HZ: u64 = 320_000; 76pub const TICK_HZ: u64 = 320_000;
77#[cfg(feature = "tick-hz-375_000")]
78pub const TICK_HZ: u64 = 375_000;
77#[cfg(feature = "tick-hz-512_000")] 79#[cfg(feature = "tick-hz-512_000")]
78pub const TICK_HZ: u64 = 512_000; 80pub 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"]
178tick-hz-262_144 = ["embassy-time-driver/tick-hz-262_144"] 178tick-hz-262_144 = ["embassy-time-driver/tick-hz-262_144"]
179## 320.0kHz Tick Rate 179## 320.0kHz Tick Rate
180tick-hz-320_000 = ["embassy-time-driver/tick-hz-320_000"] 180tick-hz-320_000 = ["embassy-time-driver/tick-hz-320_000"]
181## 375.0kHz Tick Rate
182tick-hz-375_000 = ["embassy-time-driver/tick-hz-375_000"]
181## 512.0kHz Tick Rate 183## 512.0kHz Tick Rate
182tick-hz-512_000 = ["embassy-time-driver/tick-hz-512_000"] 184tick-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;
15use crate::{Builder, Handler}; 15use crate::{Builder, Handler};
16 16
17const USB_CLASS_HID: u8 = 0x03; 17const USB_CLASS_HID: u8 = 0x03;
18const USB_SUBCLASS_NONE: u8 = 0x00;
19const USB_PROTOCOL_NONE: u8 = 0x00;
20 18
21// HID 19// HID
22const HID_DESC_DESCTYPE_HID: u8 = 0x21; 20const HID_DESC_DESCTYPE_HID: u8 = 0x21;
@@ -31,6 +29,52 @@ const HID_REQ_SET_REPORT: u8 = 0x09;
31const HID_REQ_GET_PROTOCOL: u8 = 0x03; 29const HID_REQ_GET_PROTOCOL: u8 = 0x03;
32const HID_REQ_SET_PROTOCOL: u8 = 0x0b; 30const 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)]
37pub enum HidProtocolMode {
38 /// Hid Boot Protocol Mode
39 Boot = 0,
40 /// Hid Report Protocol Mode
41 Report = 1,
42}
43
44impl 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)]
58pub 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)]
69pub 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.
35pub struct Config<'d> { 79pub 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::*;
6use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; 6use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig};
7use embassy_embedded_hal::adapter::BlockingAsync; 7use embassy_embedded_hal::adapter::BlockingAsync;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_stm32::exti::ExtiInput; 9use embassy_stm32::exti::{self, ExtiInput};
10use embassy_stm32::flash::{Flash, WRITE_SIZE}; 10use embassy_stm32::flash::{Flash, WRITE_SIZE};
11use embassy_stm32::gpio::{Level, Output, Pull, Speed}; 11use embassy_stm32::gpio::{Level, Output, Pull, Speed};
12use embassy_stm32::{bind_interrupts, interrupt};
12use embassy_sync::mutex::Mutex; 13use embassy_sync::mutex::Mutex;
13use panic_reset as _; 14use panic_reset as _;
14 15
16bind_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")]
16static APP_B: &[u8] = &[0, 1, 2, 3]; 22static 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;
7use defmt_rtt::*; 7use defmt_rtt::*;
8use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; 8use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig};
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_stm32::exti::ExtiInput; 10use embassy_stm32::exti::{self, ExtiInput};
11use embassy_stm32::flash::{Flash, WRITE_SIZE}; 11use embassy_stm32::flash::{Flash, WRITE_SIZE};
12use embassy_stm32::gpio::{Level, Output, Pull, Speed}; 12use embassy_stm32::gpio::{Level, Output, Pull, Speed};
13use embassy_stm32::{bind_interrupts, interrupt};
13use embassy_sync::blocking_mutex::Mutex; 14use embassy_sync::blocking_mutex::Mutex;
14use embedded_storage::nor_flash::NorFlash; 15use embedded_storage::nor_flash::NorFlash;
15use panic_reset as _; 16use 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"))]
20static APP_B: &[u8] = include_bytes!("../../b.bin"); 21static APP_B: &[u8] = include_bytes!("../../b.bin");
21 22
23bind_interrupts!(
24 pub struct Irqs{
25 EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>;
26});
27
22#[embassy_executor::main] 28#[embassy_executor::main]
23async fn main(_spawner: Spawner) { 29async 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;
7use defmt_rtt::*; 7use defmt_rtt::*;
8use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; 8use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig};
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_stm32::exti::ExtiInput; 10use embassy_stm32::exti::{self, ExtiInput};
11use embassy_stm32::flash::{Flash, WRITE_SIZE}; 11use embassy_stm32::flash::{Flash, WRITE_SIZE};
12use embassy_stm32::gpio::{Level, Output, Pull, Speed}; 12use embassy_stm32::gpio::{Level, Output, Pull, Speed};
13use embassy_stm32::{bind_interrupts, interrupt};
13use embassy_sync::blocking_mutex::Mutex; 14use embassy_sync::blocking_mutex::Mutex;
14use embedded_storage::nor_flash::NorFlash; 15use embedded_storage::nor_flash::NorFlash;
15use panic_reset as _; 16use panic_reset as _;
16 17
18bind_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")]
18static APP_B: &[u8] = &[0, 1, 2, 3]; 24static 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::*;
6use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; 6use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig};
7use embassy_embedded_hal::adapter::BlockingAsync; 7use embassy_embedded_hal::adapter::BlockingAsync;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_stm32::exti::ExtiInput; 9use embassy_stm32::exti::{self, ExtiInput};
10use embassy_stm32::flash::{Flash, WRITE_SIZE}; 10use embassy_stm32::flash::{Flash, WRITE_SIZE};
11use embassy_stm32::gpio::{Level, Output, Pull, Speed}; 11use embassy_stm32::gpio::{Level, Output, Pull, Speed};
12use embassy_stm32::{bind_interrupts, interrupt};
12use embassy_sync::mutex::Mutex; 13use embassy_sync::mutex::Mutex;
13use embassy_time::Timer; 14use embassy_time::Timer;
14use panic_reset as _; 15use panic_reset as _;
15 16
17bind_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")]
17static APP_B: &[u8] = &[0, 1, 2, 3]; 23static 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::*;
6use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; 6use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig};
7use embassy_embedded_hal::adapter::BlockingAsync; 7use embassy_embedded_hal::adapter::BlockingAsync;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_stm32::exti::ExtiInput; 9use embassy_stm32::exti::{self, ExtiInput};
10use embassy_stm32::flash::{Flash, WRITE_SIZE}; 10use embassy_stm32::flash::{Flash, WRITE_SIZE};
11use embassy_stm32::gpio::{Level, Output, Pull, Speed}; 11use embassy_stm32::gpio::{Level, Output, Pull, Speed};
12use embassy_stm32::{bind_interrupts, interrupt};
12use embassy_sync::mutex::Mutex; 13use embassy_sync::mutex::Mutex;
13use embassy_time::Timer; 14use embassy_time::Timer;
14use panic_reset as _; 15use 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"))]
19static APP_B: &[u8] = include_bytes!("../../b.bin"); 20static APP_B: &[u8] = include_bytes!("../../b.bin");
20 21
22bind_interrupts!(
23 pub struct Irqs{
24 EXTI2 => exti::InterruptHandler<interrupt::typelevel::EXTI2>;
25});
26
21#[embassy_executor::main] 27#[embassy_executor::main]
22async fn main(_spawner: Spawner) { 28async 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::*;
6use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; 6use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig};
7use embassy_embedded_hal::adapter::BlockingAsync; 7use embassy_embedded_hal::adapter::BlockingAsync;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_stm32::exti::ExtiInput; 9use embassy_stm32::exti::{self, ExtiInput};
10use embassy_stm32::flash::{Flash, WRITE_SIZE}; 10use embassy_stm32::flash::{Flash, WRITE_SIZE};
11use embassy_stm32::gpio::{Level, Output, Pull, Speed}; 11use embassy_stm32::gpio::{Level, Output, Pull, Speed};
12use embassy_stm32::{bind_interrupts, interrupt};
12use embassy_sync::mutex::Mutex; 13use embassy_sync::mutex::Mutex;
13use panic_reset as _; 14use 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"))]
18static APP_B: &[u8] = include_bytes!("../../b.bin"); 19static APP_B: &[u8] = include_bytes!("../../b.bin");
19 20
21bind_interrupts!(
22 pub struct Irqs{
23 EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>;
24});
25
20#[embassy_executor::main] 26#[embassy_executor::main]
21async fn main(_spawner: Spawner) { 27async 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::*;
8use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; 8use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig};
9use embassy_embedded_hal::adapter::BlockingAsync; 9use embassy_embedded_hal::adapter::BlockingAsync;
10use embassy_executor::Spawner; 10use embassy_executor::Spawner;
11use embassy_stm32::SharedData; 11use embassy_stm32::exti::{self, ExtiInput};
12use embassy_stm32::exti::ExtiInput;
13use embassy_stm32::flash::{Flash, WRITE_SIZE}; 12use embassy_stm32::flash::{Flash, WRITE_SIZE};
14use embassy_stm32::gpio::{Level, Output, Pull, Speed}; 13use embassy_stm32::gpio::{Level, Output, Pull, Speed};
14use embassy_stm32::{SharedData, bind_interrupts, interrupt};
15use embassy_sync::mutex::Mutex; 15use embassy_sync::mutex::Mutex;
16use panic_reset as _; 16use 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"))]
21static APP_B: &[u8] = include_bytes!("../../b.bin"); 21static APP_B: &[u8] = include_bytes!("../../b.bin");
22 22
23bind_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")]
24static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); 29static 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]
11async fn main(_spawner: Spawner) { 11async 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
8use core::f32::consts::PI;
9
10use defmt::*;
11use embassy_executor::Spawner;
12use embassy_mspm0::mathacl::{Mathacl, Precision};
13use embassy_time::Timer;
14use {defmt_rtt as _, panic_halt as _};
15
16#[embassy_executor::main]
17async 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
4use core::sync::atomic::{AtomicBool, Ordering}; 4use core::sync::atomic::{AtomicBool, AtomicU8, Ordering};
5 5
6use defmt::*; 6use defmt::*;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
@@ -13,7 +13,9 @@ use embassy_nrf::usb::vbus_detect::HardwareVbusDetect;
13use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; 13use embassy_nrf::{bind_interrupts, pac, peripherals, usb};
14use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 14use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
15use embassy_sync::signal::Signal; 15use embassy_sync::signal::Signal;
16use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; 16use embassy_usb::class::hid::{
17 HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State,
18};
17use embassy_usb::control::OutResponse; 19use embassy_usb::control::OutResponse;
18use embassy_usb::{Builder, Config, Handler}; 20use embassy_usb::{Builder, Config, Handler};
19use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; 21use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};
@@ -26,6 +28,8 @@ bind_interrupts!(struct Irqs {
26 28
27static SUSPENDED: AtomicBool = AtomicBool::new(false); 29static SUSPENDED: AtomicBool = AtomicBool::new(false);
28 30
31static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8);
32
29#[embassy_executor::main] 33#[embassy_executor::main]
30async fn main(_spawner: Spawner) { 34async 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
4use core::sync::atomic::{AtomicU8, Ordering};
5
4use defmt::*; 6use defmt::*;
5use embassy_executor::Spawner; 7use embassy_executor::Spawner;
6use embassy_futures::join::join; 8use embassy_futures::join::join;
@@ -8,7 +10,9 @@ use embassy_nrf::usb::Driver;
8use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; 10use embassy_nrf::usb::vbus_detect::HardwareVbusDetect;
9use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; 11use embassy_nrf::{bind_interrupts, pac, peripherals, usb};
10use embassy_time::Timer; 12use embassy_time::Timer;
11use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; 13use embassy_usb::class::hid::{
14 HidBootProtocol, HidProtocolMode, HidSubclass, HidWriter, ReportId, RequestHandler, State,
15};
12use embassy_usb::control::OutResponse; 16use embassy_usb::control::OutResponse;
13use embassy_usb::{Builder, Config}; 17use embassy_usb::{Builder, Config};
14use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; 18use 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
26static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8);
27
22#[embassy_executor::main] 28#[embassy_executor::main]
23async fn main(_spawner: Spawner) { 29async 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 @@
1MEMORY 1MEMORY
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
4use core::sync::atomic::{AtomicBool, Ordering}; 4use core::sync::atomic::{AtomicBool, AtomicU8, Ordering};
5 5
6use defmt::*; 6use defmt::*;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
@@ -10,7 +10,9 @@ use embassy_rp::bind_interrupts;
10use embassy_rp::gpio::{Input, Pull}; 10use embassy_rp::gpio::{Input, Pull};
11use embassy_rp::peripherals::USB; 11use embassy_rp::peripherals::USB;
12use embassy_rp::usb::{Driver, InterruptHandler}; 12use embassy_rp::usb::{Driver, InterruptHandler};
13use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; 13use embassy_usb::class::hid::{
14 HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State,
15};
14use embassy_usb::control::OutResponse; 16use embassy_usb::control::OutResponse;
15use embassy_usb::{Builder, Config, Handler}; 17use embassy_usb::{Builder, Config, Handler};
16use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; 18use 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
25static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8);
26
23#[embassy_executor::main] 27#[embassy_executor::main]
24async fn main(_spawner: Spawner) { 28async 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
4use core::sync::atomic::{AtomicBool, Ordering}; 4use core::sync::atomic::{AtomicBool, AtomicU8, Ordering};
5 5
6use defmt::*; 6use defmt::*;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
@@ -11,7 +11,9 @@ use embassy_rp::clocks::RoscRng;
11use embassy_rp::peripherals::USB; 11use embassy_rp::peripherals::USB;
12use embassy_rp::usb::{Driver, InterruptHandler}; 12use embassy_rp::usb::{Driver, InterruptHandler};
13use embassy_time::Timer; 13use embassy_time::Timer;
14use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; 14use embassy_usb::class::hid::{
15 HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State,
16};
15use embassy_usb::control::OutResponse; 17use embassy_usb::control::OutResponse;
16use embassy_usb::{Builder, Config, Handler}; 18use embassy_usb::{Builder, Config, Handler};
17use rand::Rng; 19use rand::Rng;
@@ -22,6 +24,8 @@ bind_interrupts!(struct Irqs {
22 USBCTRL_IRQ => InterruptHandler<USB>; 24 USBCTRL_IRQ => InterruptHandler<USB>;
23}); 25});
24 26
27static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8);
28
25#[embassy_executor::main] 29#[embassy_executor::main]
26async fn main(_spawner: Spawner) { 30async 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
8use core::str::from_utf8; 7use core::str::from_utf8;
9 8
@@ -20,11 +19,14 @@ use embassy_rp::gpio::{Level, Output};
20use embassy_rp::peripherals::{DMA_CH0, PIO0}; 19use embassy_rp::peripherals::{DMA_CH0, PIO0};
21use embassy_rp::pio::{InterruptHandler, Pio}; 20use embassy_rp::pio::{InterruptHandler, Pio};
22use embassy_time::{Duration, Timer}; 21use embassy_time::{Duration, Timer};
23use reqwless::client::{HttpClient, TlsConfig, TlsVerify}; 22use reqwless::client::HttpClient;
23// Uncomment these for TLS requests:
24// use reqwless::client::{HttpClient, TlsConfig, TlsVerify};
24use reqwless::request::Method; 25use reqwless::request::Method;
25use serde::Deserialize; 26use serde::Deserialize;
27use serde_json_core::from_slice;
26use static_cell::StaticCell; 28use static_cell::StaticCell;
27use {defmt_rtt as _, panic_probe as _, serde_json_core}; 29use {defmt_rtt as _, panic_probe as _};
28 30
29bind_interrupts!(struct Irqs { 31bind_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
4use core::sync::atomic::{AtomicBool, Ordering}; 4use core::sync::atomic::{AtomicBool, AtomicU8, Ordering};
5 5
6use defmt::*; 6use defmt::*;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
@@ -10,7 +10,9 @@ use embassy_rp::bind_interrupts;
10use embassy_rp::gpio::{Input, Pull}; 10use embassy_rp::gpio::{Input, Pull};
11use embassy_rp::peripherals::USB; 11use embassy_rp::peripherals::USB;
12use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler}; 12use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler};
13use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State as HidState}; 13use embassy_usb::class::hid::{
14 HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State as HidState,
15};
14use embassy_usb::control::OutResponse; 16use embassy_usb::control::OutResponse;
15use embassy_usb::{Builder, Config, Handler}; 17use embassy_usb::{Builder, Config, Handler};
16use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; 18use 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
25static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8);
26
23#[embassy_executor::main] 27#[embassy_executor::main]
24async fn main(_spawner: Spawner) { 28async 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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::adc::vals::Scandir;
7use embassy_stm32::adc::{Adc, AdcChannel, AnyAdcChannel, Resolution, SampleTime}; 6use embassy_stm32::adc::{Adc, AdcChannel, AnyAdcChannel, Resolution, SampleTime};
8use embassy_stm32::peripherals::ADC1; 7use embassy_stm32::peripherals::ADC1;
9use embassy_time::Timer; 8use 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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::exti::ExtiInput; 6use embassy_stm32::exti::{self, ExtiInput};
7use embassy_stm32::gpio::Pull; 7use embassy_stm32::gpio::Pull;
8use embassy_stm32::{bind_interrupts, interrupt};
8use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
9 10
11bind_interrupts!(
12 pub struct Irqs{
13 EXTI4_15 => exti::InterruptHandler<interrupt::typelevel::EXTI4_15>;
14});
15
10#[embassy_executor::main] 16#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 17async 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"] }
16embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } 16embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] }
17embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 17embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
18embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 18embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
19embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
19static_cell = "2" 20static_cell = "2"
20portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] } 21portable-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
8use defmt::info; 8use defmt::info;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_stm32::Peri; 10use embassy_stm32::exti::{self, ExtiInput};
11use embassy_stm32::exti::ExtiInput;
12use embassy_stm32::gpio::{AnyPin, Level, Output, Pull, Speed}; 11use embassy_stm32::gpio::{AnyPin, Level, Output, Pull, Speed};
12use embassy_stm32::{Peri, bind_interrupts, interrupt};
13use embassy_time::Timer; 13use embassy_time::Timer;
14use {defmt_rtt as _, panic_probe as _}; 14use {defmt_rtt as _, panic_probe as _};
15 15
16static BLINK_MS: AtomicU32 = AtomicU32::new(0); 16static BLINK_MS: AtomicU32 = AtomicU32::new(0);
17 17
18bind_interrupts!(
19 pub struct Irqs{
20 EXTI4_15 => exti::InterruptHandler<interrupt::typelevel::EXTI4_15>;
21});
22
18#[embassy_executor::task] 23#[embassy_executor::task]
19async fn led_task(led: Peri<'static, AnyPin>) { 24async 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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::exti::ExtiInput; 6use embassy_stm32::exti::{self, ExtiInput};
7use embassy_stm32::gpio::Pull; 7use embassy_stm32::gpio::Pull;
8use embassy_stm32::{bind_interrupts, interrupt};
8use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
9 10
11bind_interrupts!(
12 pub struct Irqs{
13 EXTI4_15 => exti::InterruptHandler<interrupt::typelevel::EXTI4_15>;
14});
15
10#[embassy_executor::main] 16#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 17async 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
18use defmt::*;
19use embassy_executor::Spawner;
20use embassy_stm32::i2c::{Config, I2c, Master};
21use embassy_stm32::mode::{Async, Blocking};
22use embassy_stm32::time::Hertz;
23use embassy_stm32::{bind_interrupts, i2c, peripherals};
24use embassy_time::Timer;
25use embedded_hal_1::i2c::Operation;
26use {defmt_rtt as _, panic_probe as _};
27
28bind_interrupts!(struct Irqs {
29 I2C1 => i2c::EventInterruptHandler<peripherals::I2C1>, i2c::ErrorInterruptHandler<peripherals::I2C1>;
30});
31
32#[embassy_executor::main]
33async 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
157fn 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
169fn 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
181fn 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
198fn 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
213fn 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
241fn 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
263fn 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
290fn 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
310fn 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
330fn 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
361fn 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
389async 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
401async 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
413async 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
430async 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
445async 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
473async 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
493async 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
518async 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
537async 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
556async 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
585async 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
12use defmt::*; 12use defmt::*;
13use embassy_executor::Spawner; 13use embassy_executor::Spawner;
14use embassy_stm32::exti::ExtiInput; 14use embassy_stm32::exti::{self, ExtiInput};
15use embassy_stm32::gpio::{Level, Output, Pull, Speed}; 15use embassy_stm32::gpio::{Level, Output, Pull, Speed};
16use embassy_stm32::{bind_interrupts, interrupt};
16use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; 17use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
17use embassy_sync::channel::Channel; 18use embassy_sync::channel::Channel;
18use embassy_time::{Duration, Timer, with_timeout}; 19use embassy_time::{Duration, Timer, with_timeout};
19use {defmt_rtt as _, panic_probe as _}; 20use {defmt_rtt as _, panic_probe as _};
20 21
22bind_interrupts!(
23 pub struct Irqs{
24 EXTI0 => exti::InterruptHandler<interrupt::typelevel::EXTI0>;
25});
26
21struct Leds<'a> { 27struct 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]
100async fn main(spawner: Spawner) { 106async 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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::exti::ExtiInput; 6use embassy_stm32::exti::{self, ExtiInput};
7use embassy_stm32::gpio::Pull; 7use embassy_stm32::gpio::Pull;
8use embassy_stm32::{bind_interrupts, interrupt};
8use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
9 10
11bind_interrupts!(
12 pub struct Irqs{
13 EXTI0 => exti::InterruptHandler<interrupt::typelevel::EXTI0>;
14});
15
10#[embassy_executor::main] 16#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 17async 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;
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::Peripherals; 6use embassy_stm32::Peripherals;
7use embassy_stm32::adc::{Adc, AdcChannel, RingBufferedAdc, SampleTime}; 7use embassy_stm32::adc::{Adc, AdcChannel, RegularConversionMode, RingBufferedAdc, SampleTime};
8use embassy_time::Instant; 8use embassy_time::Instant;
9use {defmt_rtt as _, panic_probe as _}; 9use {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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::exti::ExtiInput; 6use embassy_stm32::exti::{self, ExtiInput};
7use embassy_stm32::gpio::Pull; 7use embassy_stm32::gpio::Pull;
8use embassy_stm32::{bind_interrupts, interrupt};
8use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
9 10
11bind_interrupts!(
12 pub struct Irqs{
13 EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>;
14});
15
10#[embassy_executor::main] 16#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 17async 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::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_net::tcp::TcpSocket; 6use embassy_net::tcp::TcpSocket;
7use embassy_net::{Ipv4Address, StackResources}; 7use embassy_net::{Ipv4Address, StackResources};
8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; 8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};
9use embassy_stm32::peripherals::ETH; 9use embassy_stm32::peripherals::{ETH, ETH_SMA};
10use embassy_stm32::rng::Rng; 10use embassy_stm32::rng::Rng;
11use embassy_stm32::time::Hertz; 11use embassy_stm32::time::Hertz;
12use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; 12use 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
23type Device = Ethernet<'static, ETH, GenericPhy>; 23type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;
24 24
25#[embassy_executor::task] 25#[embassy_executor::task]
26async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { 26async 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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, StationManagement}; 6use embassy_stm32::eth::{Ethernet, PacketQueue, StationManagement};
7use embassy_stm32::time::Hertz; 7use embassy_stm32::time::Hertz;
8use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; 8use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};
9use embassy_time::Timer; 9use 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;
7use embassy_net::{Ipv4Address, StackResources}; 7use embassy_net::{Ipv4Address, StackResources};
8use embassy_net_wiznet::chip::W5500; 8use embassy_net_wiznet::chip::W5500;
9use embassy_net_wiznet::{Device, Runner, State}; 9use embassy_net_wiznet::{Device, Runner, State};
10use embassy_stm32::exti::ExtiInput; 10use embassy_stm32::exti::{self, ExtiInput};
11use embassy_stm32::gpio::{Level, Output, Pull, Speed}; 11use embassy_stm32::gpio::{Level, Output, Pull, Speed};
12use embassy_stm32::mode::Async; 12use embassy_stm32::mode::Async;
13use embassy_stm32::rng::Rng; 13use embassy_stm32::rng::Rng;
14use embassy_stm32::spi::Spi; 14use embassy_stm32::spi::Spi;
15use embassy_stm32::spi::mode::Master; 15use embassy_stm32::spi::mode::Master;
16use embassy_stm32::time::Hertz; 16use embassy_stm32::time::Hertz;
17use embassy_stm32::{Config, bind_interrupts, peripherals, rng, spi}; 17use embassy_stm32::{Config, bind_interrupts, interrupt, peripherals, rng, spi};
18use embassy_time::{Delay, Timer}; 18use embassy_time::{Delay, Timer};
19use embedded_hal_bus::spi::ExclusiveDevice; 19use embedded_hal_bus::spi::ExclusiveDevice;
20use embedded_io_async::Write; 20use embedded_io_async::Write;
@@ -23,6 +23,7 @@ use {defmt_rtt as _, panic_probe as _};
23 23
24bind_interrupts!(struct Irqs { 24bind_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
28type EthernetSPI = ExclusiveDevice<Spi<'static, Async, Master>, Output<'static>, Delay>; 29type 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
4use core::sync::atomic::{AtomicBool, Ordering}; 4use core::sync::atomic::{AtomicBool, AtomicU8, Ordering};
5 5
6use defmt::*; 6use defmt::*;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_futures::join::join; 8use embassy_futures::join::join;
9use embassy_stm32::exti::ExtiInput; 9use embassy_stm32::exti::{self, ExtiInput};
10use embassy_stm32::gpio::Pull; 10use embassy_stm32::gpio::Pull;
11use embassy_stm32::time::Hertz; 11use embassy_stm32::time::Hertz;
12use embassy_stm32::usb::Driver; 12use embassy_stm32::usb::Driver;
13use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; 13use embassy_stm32::{Config, bind_interrupts, interrupt, peripherals, usb};
14use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; 14use embassy_usb::class::hid::{
15 HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State,
16};
15use embassy_usb::control::OutResponse; 17use embassy_usb::control::OutResponse;
16use embassy_usb::{Builder, Handler}; 18use embassy_usb::{Builder, Handler};
17use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; 19use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};
@@ -19,8 +21,11 @@ use {defmt_rtt as _, panic_probe as _};
19 21
20bind_interrupts!(struct Irqs { 22bind_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
27static 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
4use core::sync::atomic::{AtomicU8, Ordering};
5
4use defmt::*; 6use defmt::*;
5use embassy_executor::Spawner; 7use embassy_executor::Spawner;
6use embassy_futures::join::join; 8use embassy_futures::join::join;
@@ -9,7 +11,9 @@ use embassy_stm32::usb::Driver;
9use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; 11use embassy_stm32::{Config, bind_interrupts, peripherals, usb};
10use embassy_time::Timer; 12use embassy_time::Timer;
11use embassy_usb::Builder; 13use embassy_usb::Builder;
12use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; 14use embassy_usb::class::hid::{
15 HidBootProtocol, HidProtocolMode, HidSubclass, HidWriter, ReportId, RequestHandler, State,
16};
13use embassy_usb::control::OutResponse; 17use embassy_usb::control::OutResponse;
14use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; 18use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
15use {defmt_rtt as _, panic_probe as _}; 19use {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
25static 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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::dsihost::{DsiHost, PacketType, blocking_delay_ms}; 6use embassy_stm32::dsihost::{DsiHost, PacketType};
7use embassy_stm32::gpio::{Level, Output, Speed}; 7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::ltdc::Ltdc; 8use embassy_stm32::ltdc::Ltdc;
9use embassy_stm32::pac::dsihost::regs::{Ier0, Ier1}; 9use 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};
15use embassy_stm32::time::mhz; 15use embassy_stm32::time::mhz;
16use embassy_time::Timer; 16use embassy_time::{Duration, Timer, block_for};
17use {defmt_rtt as _, panic_probe as _}; 17use {defmt_rtt as _, panic_probe as _};
18 18
19enum _Orientation { 19enum _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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::exti::ExtiInput; 6use embassy_stm32::exti::{self, ExtiInput};
7use embassy_stm32::gpio::Pull; 7use embassy_stm32::gpio::Pull;
8use embassy_stm32::{bind_interrupts, interrupt};
8use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
9 10
11bind_interrupts!(
12 pub struct Irqs{
13 EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>;
14});
15
10#[embassy_executor::main] 16#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 17async 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::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_net::tcp::TcpSocket; 6use embassy_net::tcp::TcpSocket;
7use embassy_net::{Ipv4Address, StackResources}; 7use embassy_net::{Ipv4Address, StackResources};
8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; 8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};
9use embassy_stm32::peripherals::ETH; 9use embassy_stm32::peripherals::{ETH, ETH_SMA};
10use embassy_stm32::rng::Rng; 10use embassy_stm32::rng::Rng;
11use embassy_stm32::time::Hertz; 11use embassy_stm32::time::Hertz;
12use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; 12use 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
23type Device = Ethernet<'static, ETH, GenericPhy>; 23type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;
24 24
25#[embassy_executor::task] 25#[embassy_executor::task]
26async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { 26async 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
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::Config;
7use embassy_stm32::gpio::OutputType;
8use embassy_stm32::time::{Hertz, mhz};
9use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
10use embassy_time::Timer;
11use {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]
19async 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
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::Config;
7use embassy_stm32::gpio::OutputType;
8use embassy_stm32::time::mhz;
9use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
10use embassy_time::Timer;
11use {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]
19async 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
8use defmt::*; 8use defmt::*;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_stm32::adc::{Adc, Clock, Ovsr, Ovss, Presc, SampleTime}; 10use embassy_stm32::adc::{Adc, AdcConfig, Clock, Ovsr, Ovss, Presc, SampleTime};
11use embassy_time::Timer; 11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _}; 12use {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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::exti::ExtiInput; 6use embassy_stm32::exti::{self, ExtiInput};
7use embassy_stm32::gpio::Pull; 7use embassy_stm32::gpio::Pull;
8use embassy_stm32::{bind_interrupts, interrupt};
8use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
9 10
11bind_interrupts!(
12 pub struct Irqs{
13 EXTI4_15 => exti::InterruptHandler<interrupt::typelevel::EXTI4_15>;
14});
15
10#[embassy_executor::main] 16#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 17async 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::*;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_stm32::Config; 10use embassy_stm32::Config;
11use embassy_stm32::adc::vals::{Rovsm, Trovs}; 11use embassy_stm32::adc::vals::{Rovsm, Trovs};
12use embassy_stm32::adc::{Adc, SampleTime}; 12use embassy_stm32::adc::{Adc, AdcConfig, SampleTime};
13use embassy_time::Timer; 13use embassy_time::Timer;
14use {defmt_rtt as _, panic_probe as _}; 14use {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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::exti::ExtiInput; 6use embassy_stm32::exti::{self, ExtiInput};
7use embassy_stm32::gpio::Pull; 7use embassy_stm32::gpio::Pull;
8use embassy_stm32::{bind_interrupts, interrupt};
8use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
9 10
11bind_interrupts!(
12 pub struct Irqs{
13 EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>;
14});
15
10#[embassy_executor::main] 16#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 17async 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;
6use embassy_stm32::adc::{self, Adc, AdcChannel, RxDma, SampleTime}; 6use embassy_stm32::adc::{self, Adc, AdcChannel, RxDma, SampleTime};
7use embassy_stm32::peripherals::{ADC1, ADC2, GPDMA1_CH0, GPDMA1_CH1, PA0, PA1, PA2, PA3}; 7use embassy_stm32::peripherals::{ADC1, ADC2, GPDMA1_CH0, GPDMA1_CH1, PA0, PA1, PA2, PA3};
8use embassy_stm32::{Config, Peri}; 8use embassy_stm32::{Config, Peri};
9use embassy_time::Instant; 9use embassy_time::{Duration, Instant, Ticker};
10use {defmt_rtt as _, panic_probe as _}; 10use {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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::exti::ExtiInput; 6use embassy_stm32::exti::{self, ExtiInput};
7use embassy_stm32::gpio::Pull; 7use embassy_stm32::gpio::Pull;
8use embassy_stm32::{bind_interrupts, interrupt};
8use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
9 10
11bind_interrupts!(
12 pub struct Irqs{
13 EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>;
14});
15
10#[embassy_executor::main] 16#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 17async 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::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_net::tcp::TcpSocket; 6use embassy_net::tcp::TcpSocket;
7use embassy_net::{Ipv4Address, StackResources}; 7use embassy_net::{Ipv4Address, StackResources};
8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; 8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};
9use embassy_stm32::peripherals::ETH; 9use embassy_stm32::peripherals::{ETH, ETH_SMA};
10use embassy_stm32::rcc::{ 10use 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
26type Device = Ethernet<'static, ETH, GenericPhy>; 26type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;
27 27
28#[embassy_executor::task] 28#[embassy_executor::task]
29async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { 29async 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 @@
7use defmt::*; 7use defmt::*;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_stm32::gpio::{AnyPin, Level, Output, Speed}; 9use embassy_stm32::gpio::{AnyPin, Level, Output, Speed};
10use embassy_stm32::low_power::Executor;
11use embassy_stm32::rcc::{HSIPrescaler, LsConfig}; 10use embassy_stm32::rcc::{HSIPrescaler, LsConfig};
12use embassy_stm32::{Config, Peri}; 11use embassy_stm32::{Config, Peri, low_power};
13use embassy_time::Timer; 12use embassy_time::Timer;
14use {defmt_rtt as _, panic_probe as _}; 13use {defmt_rtt as _, panic_probe as _};
15 14
16#[cortex_m_rt::entry] 15#[embassy_executor::main(executor = "low_power::Executor")]
17fn main() -> ! {
18 Executor::take().run(|spawner| {
19 spawner.spawn(unwrap!(async_main(spawner)));
20 })
21}
22
23#[embassy_executor::task]
24async fn async_main(spawner: Spawner) { 16async 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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::exti::ExtiInput; 6use embassy_stm32::exti::{self, ExtiInput};
7use embassy_stm32::gpio::Pull; 7use embassy_stm32::gpio::Pull;
8use embassy_stm32::{bind_interrupts, interrupt};
8use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
9 10
11bind_interrupts!(
12 pub struct Irqs{
13 EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>;
14});
15
10#[embassy_executor::main] 16#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 17async 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::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_net::tcp::TcpSocket; 6use embassy_net::tcp::TcpSocket;
7use embassy_net::{Ipv4Address, StackResources}; 7use embassy_net::{Ipv4Address, StackResources};
8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; 8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};
9use embassy_stm32::peripherals::ETH; 9use embassy_stm32::peripherals::{ETH, ETH_SMA};
10use embassy_stm32::rng::Rng; 10use embassy_stm32::rng::Rng;
11use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; 11use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};
12use embassy_time::Timer; 12use 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
22type Device = Ethernet<'static, ETH, GenericPhy>; 22type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;
23 23
24#[embassy_executor::task] 24#[embassy_executor::task]
25async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { 25async 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::*;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_net::StackResources; 8use embassy_net::StackResources;
9use embassy_net::tcp::client::{TcpClient, TcpClientState}; 9use embassy_net::tcp::client::{TcpClient, TcpClientState};
10use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; 10use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};
11use embassy_stm32::peripherals::ETH; 11use embassy_stm32::peripherals::{ETH, ETH_SMA};
12use embassy_stm32::rng::Rng; 12use embassy_stm32::rng::Rng;
13use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; 13use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};
14use embassy_time::Timer; 14use 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
25type Device = Ethernet<'static, ETH, GenericPhy>; 25type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;
26 26
27#[embassy_executor::task] 27#[embassy_executor::task]
28async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { 28async 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::*;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_net::StackResources; 8use embassy_net::StackResources;
9use embassy_net::tcp::client::{TcpClient, TcpClientState}; 9use embassy_net::tcp::client::{TcpClient, TcpClientState};
10use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; 10use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};
11use embassy_stm32::peripherals::ETH; 11use embassy_stm32::peripherals::{ETH, ETH_SMA};
12use embassy_stm32::rng::Rng; 12use embassy_stm32::rng::Rng;
13use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; 13use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};
14use embassy_time::Timer; 14use 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
25type Device = Ethernet<'static, ETH, GenericPhy>; 25type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;
26 26
27#[embassy_executor::task] 27#[embassy_executor::task]
28async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { 28async 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
4use defmt::{info, unwrap};
5use embassy_executor::Spawner;
6use embassy_stm32::flash::{Flash, InterruptHandler};
7use embassy_stm32::gpio::{AnyPin, Level, Output, Speed};
8use embassy_stm32::{Peri, bind_interrupts};
9use embassy_time::Timer;
10use {defmt_rtt as _, panic_probe as _};
11
12bind_interrupts!(struct Irqs {
13 FLASH => InterruptHandler;
14});
15
16#[embassy_executor::main]
17async 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]
31async 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
45async 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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::exti::ExtiInput; 6use embassy_stm32::exti::{self, ExtiInput};
7use embassy_stm32::gpio::Pull; 7use embassy_stm32::gpio::Pull;
8use embassy_stm32::{bind_interrupts, interrupt};
8use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
9 10
11bind_interrupts!(
12 pub struct Irqs{
13 EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>;
14});
15
10#[embassy_executor::main] 16#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 17async 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::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_net::udp::{PacketMetadata, UdpSocket}; 6use embassy_net::udp::{PacketMetadata, UdpSocket};
7use embassy_net::{Ipv4Address, Ipv4Cidr, StackResources}; 7use embassy_net::{Ipv4Address, Ipv4Cidr, StackResources};
8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; 8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};
9use embassy_stm32::peripherals::ETH; 9use embassy_stm32::peripherals::{ETH, ETH_SMA};
10use embassy_stm32::rng::Rng; 10use embassy_stm32::rng::Rng;
11use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; 11use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};
12use embassy_time::Timer; 12use 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
22type Device = Ethernet<'static, ETH, GenericPhy>; 22type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;
23 23
24#[embassy_executor::task] 24#[embassy_executor::task]
25async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { 25async 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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::Config; 6use embassy_stm32::exti::{self, ExtiInput};
7use embassy_stm32::exti::ExtiInput;
8use embassy_stm32::gpio::Pull; 7use embassy_stm32::gpio::Pull;
8use embassy_stm32::{Config, bind_interrupts, interrupt};
9use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
10 10
11bind_interrupts!(
12 pub struct Irqs{
13 EXTI2_3 => exti::InterruptHandler<interrupt::typelevel::EXTI2_3>;
14});
15
11#[embassy_executor::main] 16#[embassy_executor::main]
12async fn main(_spawner: Spawner) { 17async 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
4use defmt::*; 4use defmt::*;
5use embassy_stm32::Config; 5use embassy_stm32::Config;
6use embassy_stm32::adc::{Adc, Resolution, SampleTime}; 6use embassy_stm32::adc::{Adc, AdcConfig, Resolution, SampleTime};
7use {defmt_rtt as _, panic_probe as _}; 7use {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 @@
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::Config; 6use embassy_stm32::Config;
7use embassy_stm32::adc::{Adc, AdcChannel, SampleTime}; 7use embassy_stm32::adc::{Adc, AdcChannel, RegularConversionMode, SampleTime};
8use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
9 9
10const DMA_BUF_LEN: usize = 512; 10const 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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::exti::ExtiInput; 6use embassy_stm32::exti::{self, ExtiInput};
7use embassy_stm32::gpio::Pull; 7use embassy_stm32::gpio::Pull;
8use embassy_stm32::{bind_interrupts, interrupt};
8use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
9 10
11bind_interrupts!(
12 pub struct Irqs{
13 EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>;
14});
15
10#[embassy_executor::main] 16#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 17async 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;
24use embassy_net::tcp::TcpSocket; 24use embassy_net::tcp::TcpSocket;
25use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources, StaticConfigV4}; 25use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources, StaticConfigV4};
26use embassy_net_adin1110::{ADIN1110, Device, Runner}; 26use embassy_net_adin1110::{ADIN1110, Device, Runner};
27use embassy_stm32::exti::ExtiInput;
27use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; 28use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
28use embassy_stm32::i2c::{self, Config as I2C_Config, I2c}; 29use embassy_stm32::i2c::{self, Config as I2C_Config, I2c};
29use embassy_stm32::mode::Async; 30use embassy_stm32::mode::Async;
@@ -31,7 +32,7 @@ use embassy_stm32::rng::{self, Rng};
31use embassy_stm32::spi::mode::Master; 32use embassy_stm32::spi::mode::Master;
32use embassy_stm32::spi::{Config as SPI_Config, Spi}; 33use embassy_stm32::spi::{Config as SPI_Config, Spi};
33use embassy_stm32::time::Hertz; 34use embassy_stm32::time::Hertz;
34use embassy_stm32::{bind_interrupts, exti, pac, peripherals}; 35use embassy_stm32::{bind_interrupts, exti, interrupt, pac, peripherals};
35use embassy_time::{Delay, Duration, Ticker, Timer}; 36use embassy_time::{Delay, Duration, Ticker, Timer};
36use embedded_hal_async::i2c::I2c as I2cBus; 37use embedded_hal_async::i2c::I2c as I2cBus;
37use embedded_hal_bus::spi::ExclusiveDevice; 38use 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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::exti::ExtiInput; 6use embassy_stm32::exti::{self, ExtiInput};
7use embassy_stm32::gpio::Pull; 7use embassy_stm32::gpio::Pull;
8use embassy_stm32::{bind_interrupts, interrupt};
8use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
9 10
11bind_interrupts!(
12 pub struct Irqs{
13 EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>;
14});
15
10#[embassy_executor::main] 16#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 17async 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 @@
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::gpio::{AnyPin, Level, Output, Speed}; 6use embassy_stm32::gpio::{AnyPin, Level, Output, Speed};
7use embassy_stm32::low_power::Executor;
8use embassy_stm32::rcc::LsConfig; 7use embassy_stm32::rcc::LsConfig;
9use embassy_stm32::{Config, Peri}; 8use embassy_stm32::{Config, Peri, low_power};
10use embassy_time::Timer; 9use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
12 11
13#[cortex_m_rt::entry] 12#[embassy_executor::main(executor = "low_power::Executor")]
14fn main() -> ! {
15 Executor::take().run(|spawner| {
16 spawner.spawn(unwrap!(async_main(spawner)));
17 })
18}
19
20#[embassy_executor::task]
21async fn async_main(spawner: Spawner) { 13async 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
4use core::sync::atomic::{AtomicU8, Ordering};
5
4use defmt::*; 6use defmt::*;
5use embassy_executor::Spawner; 7use embassy_executor::Spawner;
6use embassy_futures::join::join; 8use embassy_futures::join::join;
@@ -8,7 +10,9 @@ use embassy_stm32::usb::Driver;
8use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; 10use embassy_stm32::{Config, bind_interrupts, peripherals, usb};
9use embassy_time::Timer; 11use embassy_time::Timer;
10use embassy_usb::Builder; 12use embassy_usb::Builder;
11use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; 13use embassy_usb::class::hid::{
14 HidBootProtocol, HidProtocolMode, HidSubclass, HidWriter, ReportId, RequestHandler, State,
15};
12use embassy_usb::control::OutResponse; 16use embassy_usb::control::OutResponse;
13use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; 17use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
14use {defmt_rtt as _, panic_probe as _}; 18use {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
24static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8);
25
20#[embassy_executor::main] 26#[embassy_executor::main]
21async fn main(_spawner: Spawner) { 27async 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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::exti::ExtiInput; 6use embassy_stm32::exti::{self, ExtiInput};
7use embassy_stm32::gpio::{Level, Output, Pull, Speed}; 7use embassy_stm32::gpio::{Level, Output, Pull, Speed};
8use embassy_stm32::{bind_interrupts, interrupt};
8use embassy_time::Timer; 9use embassy_time::Timer;
9use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
10 11
12bind_interrupts!(
13 pub struct Irqs{
14 EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>;
15});
16
11#[embassy_executor::task] 17#[embassy_executor::task]
12async fn button_task(mut p: ExtiInput<'static>) { 18async 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`
3runner = "probe-rs run --chip stm32u083rctx" 3runner = "probe-rs run --chip stm32u083mctx"
4 4
5[build] 5[build]
6target = "thumbv6m-none-eabi" 6target = "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"
6publish = false 6publish = 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.
10embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti", "chrono"] } 10embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083mc", "memory-x", "unstable-pac", "exti", "chrono"] }
11embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } 11embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] }
12embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 12embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
13embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 13embassy-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
20cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 20cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
21cortex-m-rt = "0.7.0" 21cortex-m-rt = "0.7.0"
22embedded-hal = "0.2.6"
23panic-probe = { version = "1.0.0", features = ["print-defmt"] } 22panic-probe = { version = "1.0.0", features = ["print-defmt"] }
24heapless = { version = "0.8", default-features = false }
25 23
26micromath = "2.0.0" 24micromath = "2.0.0"
27chrono = { version = "0.4.38", default-features = false } 25chrono = { 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
4use defmt::*; 4use defmt::*;
5use embassy_stm32::Config; 5use embassy_stm32::Config;
6use embassy_stm32::adc::{Adc, Resolution, SampleTime}; 6use embassy_stm32::adc::{Adc, AdcConfig, Resolution, SampleTime};
7use embassy_time::Duration; 7use embassy_time::Duration;
8use {defmt_rtt as _, panic_probe as _}; 8use {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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::exti::ExtiInput; 6use embassy_stm32::exti::{self, ExtiInput};
7use embassy_stm32::gpio::Pull; 7use embassy_stm32::gpio::Pull;
8use embassy_stm32::{bind_interrupts, interrupt};
8use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
9 10
11bind_interrupts!(
12 pub struct Irqs{
13 EXTI4_15 => exti::InterruptHandler<interrupt::typelevel::EXTI4_15>;
14});
15
10#[embassy_executor::main] 16#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 17async 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
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::lcd::{Bias, BlinkFreq, BlinkSelector, Config, Duty, Lcd, LcdPin};
7use embassy_stm32::peripherals::LCD;
8use embassy_stm32::time::Hertz;
9use embassy_time::Duration;
10use {defmt_rtt as _, panic_probe as _};
11
12#[embassy_executor::main]
13async 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
104async 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
123mod 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
178mod 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
303pub struct DisplayBuffer {
304 pixels: [u64; 4],
305}
306
307impl 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
355impl Default for DisplayBuffer {
356 fn default() -> Self {
357 Self::new()
358 }
359}
360
361#[derive(Debug, Clone, Copy)]
362enum 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
393impl 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
4use defmt::*; 4use defmt::*;
5use embassy_stm32::adc::{self, AdcChannel, SampleTime, adc4}; 5use embassy_stm32::adc::{self, Adc, AdcChannel, AdcConfig, SampleTime, adc4};
6use {defmt_rtt as _, panic_probe as _}; 6use {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.
10embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } 10embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti", "low-power"] }
11embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } 11embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] }
12embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } 12embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] }
13embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 13embassy-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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::exti::ExtiInput; 6use embassy_stm32::exti::{self, ExtiInput};
7use embassy_stm32::gpio::Pull; 7use embassy_stm32::gpio::Pull;
8use embassy_stm32::{bind_interrupts, interrupt};
8use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
9 10
11bind_interrupts!(
12 pub struct Irqs{
13 EXTI4 => exti::InterruptHandler<interrupt::typelevel::EXTI4>;
14});
15
10#[embassy_executor::main] 16#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 17async 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]
227async fn run_mm_queue(memory_manager: mm::MemoryManager) { 219async 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
288pub async fn init_gatt_services(ble_subsystem: &mut Ble) -> Result<BleContext, ()> { 280pub 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
325async fn gatt_add_service(ble_subsystem: &mut Ble, uuid: Uuid) -> Result<AttributeHandle, ()> { 317async 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
351async fn gatt_add_char( 343async 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]
22async fn run_mm_queue(memory_manager: mm::MemoryManager) { 22async 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
4use core::net::Ipv6Addr;
5
4use defmt::*; 6use defmt::*;
5use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_net::udp::{PacketMetadata, UdpSocket};
9use embassy_net::{Ipv6Cidr, StackResources, StaticConfigV6};
6use embassy_stm32::bind_interrupts; 10use embassy_stm32::bind_interrupts;
7use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; 11use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};
12use embassy_stm32::peripherals::RNG;
8use embassy_stm32::rcc::WPAN_DEFAULT; 13use embassy_stm32::rcc::WPAN_DEFAULT;
14use embassy_stm32::rng::InterruptHandler as RngInterruptHandler;
9use embassy_stm32_wpan::TlMbox; 15use embassy_stm32_wpan::TlMbox;
10use embassy_stm32_wpan::mac::commands::{ResetRequest, SetRequest, StartRequest}; 16use embassy_stm32_wpan::mac::{Driver, DriverState, Runner};
11use embassy_stm32_wpan::mac::typedefs::{MacChannel, PanId, PibId};
12use embassy_stm32_wpan::mac::{self, Runner};
13use embassy_stm32_wpan::sub::mm; 17use embassy_stm32_wpan::sub::mm;
18use embassy_time::{Duration, Timer};
19use heapless::Vec;
14use static_cell::StaticCell; 20use static_cell::StaticCell;
15use {defmt_rtt as _, panic_probe as _}; 21use {defmt_rtt as _, panic_probe as _};
16 22
17bind_interrupts!(struct Irqs{ 23bind_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]
23async fn run_mm_queue(memory_manager: mm::MemoryManager) { 30async 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]
35async fn run_mac(runner: &'static Runner<'static>) -> ! {
36 runner.run().await
25} 37}
26 38
27#[embassy_executor::task] 39#[embassy_executor::task]
28async fn run_mac(runner: &'static Runner<'static>) { 40async 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]
24async fn run_mm_queue(memory_manager: mm::MemoryManager) { 24async 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;
7use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; 7use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};
8use embassy_stm32::rcc::WPAN_DEFAULT; 8use embassy_stm32::rcc::WPAN_DEFAULT;
9use embassy_stm32_wpan::TlMbox; 9use embassy_stm32_wpan::TlMbox;
10use embassy_stm32_wpan::sub::mm;
10use {defmt_rtt as _, panic_probe as _}; 11use {defmt_rtt as _, panic_probe as _};
11 12
12bind_interrupts!(struct Irqs{ 13bind_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]
19async 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]
18async fn main(_spawner: Spawner) { 24async 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]
19async fn run_mm_queue(memory_manager: mm::MemoryManager) { 19async 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
4use defmt::*; 4use defmt::*;
5use embassy_stm32::adc::{AdcChannel, adc4}; 5use embassy_stm32::adc::{Adc, AdcChannel, SampleTime, adc4};
6use {defmt_rtt as _, panic_probe as _}; 6use {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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::exti::ExtiInput; 6use embassy_stm32::exti::{self, ExtiInput};
7use embassy_stm32::gpio::Pull; 7use embassy_stm32::gpio::Pull;
8use embassy_stm32::{bind_interrupts, interrupt};
8use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
9 10
11bind_interrupts!(
12 pub struct Irqs{
13 EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>;
14});
15
10#[embassy_executor::main] 16#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 17async 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
4use defmt::*; 4use defmt::*;
5use embassy_stm32::adc::{AdcChannel, adc4}; 5use embassy_stm32::Config;
6use embassy_stm32::adc::{Adc, AdcChannel, SampleTime, adc4};
7use embassy_stm32::rcc::{
8 AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,
9};
6use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
7 11
8#[embassy_executor::main] 12#[embassy_executor::main]
9async fn main(_spawner: embassy_executor::Spawner) { 13async 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
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::exti::ExtiInput; 6use embassy_stm32::exti::{self, ExtiInput};
7use embassy_stm32::gpio::Pull; 7use embassy_stm32::gpio::Pull;
8use embassy_stm32::{bind_interrupts, interrupt};
8use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
9 10
11bind_interrupts!(
12 pub struct Irqs{
13 EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>;
14});
15
10#[embassy_executor::main] 16#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 17async 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
6use defmt::*; 6use defmt::*;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_stm32::SharedData; 8use embassy_stm32::exti::{self, ExtiInput};
9use embassy_stm32::exti::ExtiInput;
10use embassy_stm32::gpio::Pull; 9use embassy_stm32::gpio::Pull;
10use embassy_stm32::{SharedData, bind_interrupts, interrupt};
11use {defmt_rtt as _, panic_probe as _}; 11use {defmt_rtt as _, panic_probe as _};
12 12
13bind_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")]
14static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); 19static 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::*;
6use defmt_rtt as _; 6use defmt_rtt as _;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_stm32::adc::{Adc, SampleTime}; 8use embassy_stm32::adc::{Adc, SampleTime};
9use embassy_stm32::low_power::Executor; 9use embassy_stm32::low_power;
10use embassy_time::Timer; 10use embassy_time::Timer;
11use panic_probe as _; 11use panic_probe as _;
12use static_cell::StaticCell; 12use static_cell::StaticCell;
13 13
14#[cortex_m_rt::entry] 14#[embassy_executor::main(executor = "low_power::Executor")]
15fn main() -> ! {
16 info!("main: Starting!");
17 Executor::take().run(|spawner| {
18 spawner.spawn(unwrap!(async_main(spawner)));
19 });
20}
21
22#[embassy_executor::task]
23async fn async_main(_spawner: Spawner) { 15async 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::*;
6use defmt_rtt as _; 6use defmt_rtt as _;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_stm32::gpio::{Level, Output, Speed}; 8use embassy_stm32::gpio::{Level, Output, Speed};
9use embassy_stm32::low_power::Executor; 9use embassy_stm32::low_power;
10use embassy_time::Timer; 10use embassy_time::Timer;
11use panic_probe as _; 11use panic_probe as _;
12use static_cell::StaticCell; 12use static_cell::StaticCell;
13 13
14#[cortex_m_rt::entry] 14#[embassy_executor::main(executor = "low_power::Executor")]
15fn main() -> ! {
16 info!("main: Starting!");
17 Executor::take().run(|spawner| {
18 spawner.spawn(unwrap!(async_main(spawner)));
19 });
20}
21
22#[embassy_executor::task]
23async fn async_main(_spawner: Spawner) { 15async 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")]
6use defmt_rtt as _; 6use defmt_rtt as _;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_stm32::exti::ExtiInput; 8use embassy_stm32::exti::{self, ExtiInput};
9use embassy_stm32::gpio::Pull; 9use embassy_stm32::gpio::Pull;
10use embassy_stm32::low_power::Executor; 10use embassy_stm32::{bind_interrupts, interrupt, low_power};
11use panic_probe as _; 11use panic_probe as _;
12use static_cell::StaticCell; 12use static_cell::StaticCell;
13 13
14#[cortex_m_rt::entry] 14bind_interrupts!(
15fn 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")]
23async fn async_main(_spawner: Spawner) { 20async 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::*;
6use defmt_rtt as _; 6use defmt_rtt as _;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_stm32::i2c::I2c; 8use embassy_stm32::i2c::I2c;
9use embassy_stm32::low_power::Executor;
10use embassy_stm32::time::Hertz; 9use embassy_stm32::time::Hertz;
11use embassy_stm32::{bind_interrupts, i2c, peripherals}; 10use embassy_stm32::{bind_interrupts, i2c, low_power, peripherals};
12use embassy_time::{Duration, Timer}; 11use embassy_time::{Duration, Timer};
13use panic_probe as _; 12use panic_probe as _;
14use static_cell::StaticCell; 13use 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")]
22fn main() -> ! {
23 info!("main: Starting!");
24 Executor::take().run(|spawner| {
25 spawner.spawn(unwrap!(async_main(spawner)));
26 });
27}
28
29#[embassy_executor::task]
30async fn async_main(_spawner: Spawner) { 21async 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-
31stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash", "dual-bank"] 31stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash", "dual-bank"]
32stm32u585ai = ["embassy-stm32/stm32u585ai", "spi-v345", "chrono", "rng", "hash", "cordic"] 32stm32u585ai = ["embassy-stm32/stm32u585ai", "spi-v345", "chrono", "rng", "hash", "cordic"]
33stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "spi-v345", "chrono", "rng", "hash"] # FIXME: cordic test cause it crash 33stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "spi-v345", "chrono", "rng", "hash"] # FIXME: cordic test cause it crash
34stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"] 34stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng", "hsem", "stop"]
35stm32wba52cg = ["embassy-stm32/stm32wba52cg", "spi-v345", "chrono", "rng", "hash"] 35stm32wba52cg = ["embassy-stm32/stm32wba52cg", "spi-v345", "chrono", "rng", "hash", "adc"]
36stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"] 36stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono", "hsem"]
37stm32f091rc = ["embassy-stm32/stm32f091rc", "cm0", "not-gpdma", "chrono"] 37stm32f091rc = ["embassy-stm32/stm32f091rc", "cm0", "not-gpdma", "chrono"]
38stm32h503rb = ["embassy-stm32/stm32h503rb", "spi-v345", "rng", "stop"] 38stm32h503rb = ["embassy-stm32/stm32h503rb", "spi-v345", "rng", "stop"]
39stm32h7s3l8 = ["embassy-stm32/stm32h7s3l8", "spi-v345", "rng", "cordic", "hash-v34"] # TODO: fdcan crashes, cryp dma hangs. 39stm32h7s3l8 = ["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"]
56embassy-stm32-wpan = [] 56embassy-stm32-wpan = []
57not-gpdma = [] 57not-gpdma = []
58dac = [] 58dac = []
59adc = []
59ucpd = [] 60ucpd = []
60cordic = ["dep:num-traits"] 61cordic = ["dep:num-traits"]
62hsem = []
61dual-bank = ["embassy-stm32/dual-bank"] 63dual-bank = ["embassy-stm32/dual-bank"]
62single-bank = ["embassy-stm32/single-bank"] 64single-bank = ["embassy-stm32/single-bank"]
63eeprom = [] 65eeprom = []
@@ -110,6 +112,11 @@ path = "src/bin/afio.rs"
110required-features = [ "afio",] 112required-features = [ "afio",]
111 113
112[[bin]] 114[[bin]]
115name = "adc"
116path = "src/bin/adc.rs"
117required-features = [ "adc",]
118
119[[bin]]
113name = "can" 120name = "can"
114path = "src/bin/can.rs" 121path = "src/bin/can.rs"
115required-features = [ "can",] 122required-features = [ "can",]
@@ -224,6 +231,11 @@ name = "wpan_mac"
224path = "src/bin/wpan_mac.rs" 231path = "src/bin/wpan_mac.rs"
225required-features = [ "mac",] 232required-features = [ "mac",]
226 233
234[[bin]]
235name = "hsem"
236path = "src/bin/hsem.rs"
237required-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"]
7mod common;
8
9use common::*;
10use embassy_executor::Spawner;
11use embassy_stm32::adc::{Adc, SampleTime};
12use embassy_time::Timer;
13use {defmt_rtt as _, panic_probe as _};
14
15#[embassy_executor::main]
16async 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;
7use common::*; 7use common::*;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_net::StackResources; 9use embassy_net::StackResources;
10use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; 10use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};
11use embassy_stm32::peripherals::ETH; 11use embassy_stm32::peripherals::{ETH, ETH_SMA};
12use embassy_stm32::rng::Rng; 12use embassy_stm32::rng::Rng;
13use embassy_stm32::{bind_interrupts, eth, peripherals, rng}; 13use embassy_stm32::{bind_interrupts, eth, peripherals, rng};
14use static_cell::StaticCell; 14use 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
30type Device = Ethernet<'static, ETH, GenericPhy>; 30type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;
31 31
32#[embassy_executor::task] 32#[embassy_executor::task]
33async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { 33async 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"]
6mod common;
7
8use common::*;
9use embassy_executor::Spawner;
10use embassy_stm32::bind_interrupts;
11use embassy_stm32::hsem::{HardwareSemaphore, HardwareSemaphoreInterruptHandler};
12use embassy_stm32::peripherals::HSEM;
13
14bind_interrupts!(struct Irqs{
15 HSEM => HardwareSemaphoreInterruptHandler<HSEM>;
16});
17
18#[embassy_executor::main]
19async 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
8use chrono::NaiveDate; 8use chrono::NaiveDate;
9use common::*; 9use common::*;
10use cortex_m_rt::entry;
11use embassy_executor::Spawner; 10use embassy_executor::Spawner;
12use embassy_stm32::Config; 11use embassy_stm32::low_power::{StopMode, stop_ready};
13use embassy_stm32::low_power::{Executor, StopMode, stop_ready};
14use embassy_stm32::rcc::LsConfig; 12use embassy_stm32::rcc::LsConfig;
15use embassy_stm32::rtc::Rtc; 13use embassy_stm32::rtc::Rtc;
14use embassy_stm32::{Config, low_power};
16use embassy_time::Timer; 15use embassy_time::Timer;
17 16
18#[entry]
19fn main() -> ! {
20 Executor::take().run(|spawner| {
21 spawner.spawn(unwrap!(async_main(spawner)));
22 });
23}
24
25#[embassy_executor::task] 17#[embassy_executor::task]
26async fn task_1() { 18async 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")]
47async fn async_main(spawner: Spawner) { 39async 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{
32const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7; 32const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7;
33 33
34#[embassy_executor::task] 34#[embassy_executor::task]
35async fn run_mm_queue(memory_manager: mm::MemoryManager) { 35async 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]
28async fn run_mm_queue(memory_manager: mm::MemoryManager) { 28async 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!(
259define_peris!( 259define_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")]