aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbenjaminschlegel87 <[email protected]>2025-07-25 20:39:40 +0200
committerGitHub <[email protected]>2025-07-25 20:39:40 +0200
commitdbc1818acd69e2e15ac574356c9b07cb717df441 (patch)
tree05e6360c1946183b524a1ce82268547fe4bbcfd0
parentadb728009ceba095d2190038ff698aaee08907a9 (diff)
parent996974e313fa5ec2c7c2d9dd0998fab244c0a180 (diff)
Merge branch 'embassy-rs:main' into stm32_adc_v3_hw_oversampling_support
-rwxr-xr-x.github/ci/book.sh2
-rwxr-xr-x.github/ci/doc.sh4
-rw-r--r--.vscode/settings.json2
-rw-r--r--RELEASE.md58
-rwxr-xr-xci.sh24
-rw-r--r--cyw43-pio/CHANGELOG.md9
-rw-r--r--cyw43-pio/Cargo.toml6
-rw-r--r--cyw43-pio/release.toml5
-rw-r--r--cyw43/CHANGELOG.md9
-rw-r--r--cyw43/Cargo.toml4
-rw-r--r--cyw43/release.toml5
-rw-r--r--docs/examples/basic/Cargo.toml2
-rw-r--r--docs/index.adoc1
-rw-r--r--docs/pages/layer_by_layer.adoc2
-rw-r--r--docs/pages/new_project.adoc2
-rw-r--r--docs/pages/overview.adoc2
-rw-r--r--embassy-boot-nrf/Cargo.toml6
-rw-r--r--embassy-boot-rp/Cargo.toml6
-rw-r--r--embassy-boot-stm32/Cargo.toml4
-rw-r--r--embassy-boot/Cargo.toml4
-rw-r--r--embassy-embedded-hal/CHANGELOG.md8
-rw-r--r--embassy-embedded-hal/Cargo.toml5
-rw-r--r--embassy-embedded-hal/release.toml5
-rw-r--r--embassy-embedded-hal/src/adapter/blocking_async.rs28
-rw-r--r--embassy-executor-macros/src/macros/task.rs44
-rw-r--r--embassy-executor/CHANGELOG.md17
-rw-r--r--embassy-executor/Cargo.toml1
-rw-r--r--embassy-executor/build_common.rs32
-rw-r--r--embassy-executor/release.toml5
-rw-r--r--embassy-executor/src/lib.rs51
-rw-r--r--embassy-executor/src/raw/trace.rs12
-rw-r--r--embassy-executor/src/spawner.rs18
-rw-r--r--embassy-executor/tests/test.rs36
-rw-r--r--embassy-executor/tests/ui.rs11
-rw-r--r--embassy-executor/tests/ui/bad_return_impl_future.rs9
-rw-r--r--embassy-executor/tests/ui/bad_return_impl_future.stderr120
-rw-r--r--embassy-executor/tests/ui/bad_return_impl_future_nightly.rs9
-rw-r--r--embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr10
-rw-r--r--embassy-executor/tests/ui/return_impl_future_nonsend.rs21
-rw-r--r--embassy-executor/tests/ui/return_impl_future_nonsend.stderr17
-rw-r--r--embassy-executor/tests/ui/return_impl_send.rs6
-rw-r--r--embassy-executor/tests/ui/return_impl_send.stderr137
-rw-r--r--embassy-executor/tests/ui/return_impl_send_nightly.rs6
-rw-r--r--embassy-executor/tests/ui/return_impl_send_nightly.stderr10
-rw-r--r--embassy-executor/tests/ui/spawn_nonsend.rs16
-rw-r--r--embassy-executor/tests/ui/spawn_nonsend.stderr41
-rw-r--r--embassy-futures/CHANGELOG.md14
-rw-r--r--embassy-futures/release.toml5
-rw-r--r--embassy-hal-internal/Cargo.toml2
-rw-r--r--embassy-imxrt/Cargo.toml4
-rw-r--r--embassy-imxrt/src/lib.rs8
-rw-r--r--embassy-mspm0/Cargo.toml12
-rw-r--r--embassy-mspm0/build.rs21
-rw-r--r--embassy-mspm0/src/dma.rs626
-rw-r--r--embassy-mspm0/src/gpio.rs30
-rw-r--r--embassy-mspm0/src/lib.rs117
-rw-r--r--embassy-mspm0/src/uart/buffered.rs1060
-rw-r--r--embassy-mspm0/src/uart/mod.rs (renamed from embassy-mspm0/src/uart.rs)145
-rw-r--r--embassy-net-adin1110/Cargo.toml2
-rw-r--r--embassy-net-driver-channel/CHANGELOG.md7
-rw-r--r--embassy-net-driver-channel/Cargo.toml2
-rw-r--r--embassy-net-driver-channel/release.toml5
-rw-r--r--embassy-net-esp-hosted/Cargo.toml2
-rw-r--r--embassy-net-nrf91/Cargo.toml4
-rw-r--r--embassy-net-nrf91/src/lib.rs51
-rw-r--r--embassy-net-ppp/Cargo.toml2
-rw-r--r--embassy-net-wiznet/Cargo.toml2
-rw-r--r--embassy-net/src/udp.rs2
-rw-r--r--embassy-nrf/CHANGELOG.md22
-rw-r--r--embassy-nrf/Cargo.toml8
-rw-r--r--embassy-nrf/release.toml5
-rw-r--r--embassy-nrf/src/gpio.rs2
-rw-r--r--embassy-nrf/src/lib.rs84
-rw-r--r--embassy-nrf/src/pwm.rs5
-rw-r--r--embassy-nrf/src/uarte.rs2
-rw-r--r--embassy-nrf/src/usb/mod.rs51
-rw-r--r--embassy-nxp/Cargo.toml57
-rw-r--r--embassy-nxp/build.rs138
-rw-r--r--embassy-nxp/build_common.rs94
-rw-r--r--embassy-nxp/src/chips/lpc55.rs70
-rw-r--r--embassy-nxp/src/chips/mimxrt1011.rs113
-rw-r--r--embassy-nxp/src/chips/mimxrt1062.rs282
-rw-r--r--embassy-nxp/src/fmt.rs284
-rw-r--r--embassy-nxp/src/gpio.rs359
-rw-r--r--embassy-nxp/src/gpio/lpc55.rs (renamed from embassy-nxp/src/pac_utils.rs)355
-rw-r--r--embassy-nxp/src/gpio/rt1xxx.rs945
-rw-r--r--embassy-nxp/src/lib.rs165
-rw-r--r--embassy-nxp/src/pint.rs5
-rw-r--r--embassy-nxp/src/time_driver/pit.rs187
-rw-r--r--embassy-rp/CHANGELOG.md35
-rw-r--r--embassy-rp/Cargo.toml8
-rw-r--r--embassy-rp/release.toml5
-rw-r--r--embassy-rp/src/flash.rs6
-rw-r--r--embassy-rp/src/gpio.rs15
-rw-r--r--embassy-rp/src/i2c.rs1
-rw-r--r--embassy-rp/src/lib.rs12
-rw-r--r--embassy-rp/src/multicore.rs20
-rw-r--r--embassy-rp/src/relocate.rs2
-rw-r--r--embassy-rp/src/rom_data/mod.rs36
-rw-r--r--embassy-rp/src/uart/mod.rs34
-rw-r--r--embassy-rp/src/usb.rs30
-rw-r--r--embassy-stm32-wpan/Cargo.toml4
-rw-r--r--embassy-stm32/CHANGELOG.md1
-rw-r--r--embassy-stm32/Cargo.toml72
-rw-r--r--embassy-stm32/build.rs114
-rw-r--r--embassy-stm32/src/adc/adc4.rs (renamed from embassy-stm32/src/adc/u5_adc4.rs)104
-rw-r--r--embassy-stm32/src/adc/c0.rs3
-rw-r--r--embassy-stm32/src/adc/f3_v1_1.rs2
-rw-r--r--embassy-stm32/src/adc/g4.rs121
-rw-r--r--embassy-stm32/src/adc/mod.rs66
-rw-r--r--embassy-stm32/src/adc/v1.rs23
-rw-r--r--embassy-stm32/src/adc/v3.rs19
-rw-r--r--embassy-stm32/src/adc/v4.rs1
-rw-r--r--embassy-stm32/src/adc/watchdog_v1.rs188
-rw-r--r--embassy-stm32/src/can/bxcan/mod.rs327
-rw-r--r--embassy-stm32/src/can/common.rs111
-rw-r--r--embassy-stm32/src/can/enums.rs2
-rw-r--r--embassy-stm32/src/can/fd/message_ram/extended_filter.rs8
-rw-r--r--embassy-stm32/src/can/fd/message_ram/standard_filter.rs8
-rw-r--r--embassy-stm32/src/can/fd/message_ram/txbuffer_element.rs18
-rw-r--r--embassy-stm32/src/can/fdcan.rs194
-rw-r--r--embassy-stm32/src/cordic/utils.rs2
-rw-r--r--embassy-stm32/src/cryp/mod.rs13
-rw-r--r--embassy-stm32/src/dac/mod.rs40
-rw-r--r--embassy-stm32/src/dma/dma_bdma.rs69
-rw-r--r--embassy-stm32/src/dma/gpdma.rs12
-rw-r--r--embassy-stm32/src/dma/util.rs6
-rw-r--r--embassy-stm32/src/eth/v1/mod.rs10
-rw-r--r--embassy-stm32/src/flash/u5.rs26
-rw-r--r--embassy-stm32/src/hsem/mod.rs6
-rw-r--r--embassy-stm32/src/i2c/config.rs170
-rw-r--r--embassy-stm32/src/i2c/mod.rs173
-rw-r--r--embassy-stm32/src/i2c/v1.rs7
-rw-r--r--embassy-stm32/src/i2c/v2.rs729
-rw-r--r--embassy-stm32/src/ipcc.rs3
-rw-r--r--embassy-stm32/src/lib.rs27
-rw-r--r--embassy-stm32/src/low_power.rs9
-rw-r--r--embassy-stm32/src/lptim/timer/mod.rs50
-rw-r--r--embassy-stm32/src/macros.rs14
-rw-r--r--embassy-stm32/src/opamp.rs83
-rw-r--r--embassy-stm32/src/qspi/enums.rs16
-rw-r--r--embassy-stm32/src/qspi/mod.rs5
-rw-r--r--embassy-stm32/src/rcc/c0.rs2
-rw-r--r--embassy-stm32/src/rcc/hsi48.rs4
-rw-r--r--embassy-stm32/src/rcc/mco.rs2
-rw-r--r--embassy-stm32/src/rcc/mod.rs11
-rw-r--r--embassy-stm32/src/rcc/u5.rs165
-rw-r--r--embassy-stm32/src/rcc/wba.rs1
-rw-r--r--embassy-stm32/src/rtc/low_power.rs12
-rw-r--r--embassy-stm32/src/rtc/mod.rs4
-rw-r--r--embassy-stm32/src/rtc/v3.rs2
-rw-r--r--embassy-stm32/src/sdmmc/mod.rs137
-rw-r--r--embassy-stm32/src/spi/mod.rs2
-rw-r--r--embassy-stm32/src/timer/complementary_pwm.rs58
-rw-r--r--embassy-stm32/src/timer/input_capture.rs32
-rw-r--r--embassy-stm32/src/timer/mod.rs99
-rw-r--r--embassy-stm32/src/timer/one_pulse.rs102
-rw-r--r--embassy-stm32/src/timer/pwm_input.rs11
-rw-r--r--embassy-stm32/src/timer/qei.rs42
-rw-r--r--embassy-stm32/src/timer/simple_pwm.rs241
-rw-r--r--embassy-stm32/src/tsc/acquisition_banks.rs6
-rw-r--r--embassy-stm32/src/usart/buffered.rs2
-rw-r--r--embassy-stm32/src/usart/mod.rs18
-rw-r--r--embassy-stm32/src/usb/mod.rs28
-rw-r--r--embassy-stm32/src/usb/otg.rs29
-rw-r--r--embassy-stm32/src/usb/usb.rs68
-rw-r--r--embassy-sync/Cargo.toml3
-rw-r--r--embassy-sync/src/channel.rs29
-rw-r--r--embassy-sync/src/lazy_lock.rs7
-rw-r--r--embassy-sync/src/mutex.rs2
-rw-r--r--embassy-sync/src/once_lock.rs2
-rw-r--r--embassy-sync/src/pipe.rs8
-rw-r--r--embassy-sync/src/priority_channel.rs4
-rw-r--r--embassy-sync/src/pubsub/mod.rs8
-rw-r--r--embassy-sync/src/pubsub/publisher.rs2
-rw-r--r--embassy-sync/src/pubsub/subscriber.rs2
-rw-r--r--embassy-sync/tests/ui.rs13
-rw-r--r--embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs11
-rw-r--r--embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr24
-rw-r--r--embassy-sync/tests/ui/sync_impl/lazy_lock_type.rs6
-rw-r--r--embassy-sync/tests/ui/sync_impl/lazy_lock_type.stderr9
-rw-r--r--embassy-sync/tests/ui/sync_impl/once_lock.rs6
-rw-r--r--embassy-sync/tests/ui/sync_impl/once_lock.stderr9
-rw-r--r--embassy-time-driver/CHANGELOG.md6
-rw-r--r--embassy-time-driver/release.toml5
-rw-r--r--embassy-time/CHANGELOG.md11
-rw-r--r--embassy-time/Cargo.toml2
-rw-r--r--embassy-time/release.toml5
-rw-r--r--embassy-time/src/timer.rs4
-rw-r--r--embassy-usb-dfu/Cargo.toml4
-rw-r--r--embassy-usb-driver/CHANGELOG.md17
-rw-r--r--embassy-usb-driver/Cargo.toml2
-rw-r--r--embassy-usb-driver/release.toml5
-rw-r--r--embassy-usb-driver/src/lib.rs2
-rw-r--r--embassy-usb-logger/CHANGELOG.md4
-rw-r--r--embassy-usb-logger/Cargo.toml4
-rw-r--r--embassy-usb-synopsys-otg/CHANGELOG.md4
-rw-r--r--embassy-usb-synopsys-otg/Cargo.toml4
-rw-r--r--embassy-usb-synopsys-otg/src/lib.rs62
-rw-r--r--embassy-usb/CHANGELOG.md13
-rw-r--r--embassy-usb/Cargo.toml6
-rw-r--r--embassy-usb/release.toml5
-rw-r--r--embassy-usb/src/builder.rs47
-rw-r--r--embassy-usb/src/class/cdc_acm.rs8
-rw-r--r--embassy-usb/src/class/cdc_ncm/mod.rs6
-rw-r--r--embassy-usb/src/class/cmsis_dap_v2.rs6
-rw-r--r--embassy-usb/src/class/hid.rs4
-rw-r--r--embassy-usb/src/class/midi.rs4
-rw-r--r--embassy-usb/src/class/uac1/speaker.rs3
-rw-r--r--embassy-usb/src/class/web_usb.rs2
-rw-r--r--examples/boot/application/nrf/Cargo.toml8
-rw-r--r--examples/boot/application/rp/Cargo.toml6
-rw-r--r--examples/boot/application/rp/src/bin/a.rs2
-rw-r--r--examples/boot/application/stm32f3/Cargo.toml4
-rw-r--r--examples/boot/application/stm32f7/Cargo.toml4
-rw-r--r--examples/boot/application/stm32h7/Cargo.toml4
-rw-r--r--examples/boot/application/stm32l0/Cargo.toml4
-rw-r--r--examples/boot/application/stm32l1/Cargo.toml4
-rw-r--r--examples/boot/application/stm32l4/Cargo.toml4
-rw-r--r--examples/boot/application/stm32wb-dfu/Cargo.toml6
-rw-r--r--examples/boot/application/stm32wl/Cargo.toml4
-rw-r--r--examples/boot/bootloader/stm32wb-dfu/Cargo.toml2
-rw-r--r--examples/lpc55s69/Cargo.toml2
-rw-r--r--examples/mimxrt1011/.cargo/config.toml8
-rw-r--r--examples/mimxrt1011/Cargo.toml29
-rw-r--r--examples/mimxrt1011/build.rs14
-rw-r--r--examples/mimxrt1011/src/bin/blinky.rs48
-rw-r--r--examples/mimxrt1011/src/bin/button.rs62
-rw-r--r--examples/mimxrt1011/src/lib.rs75
-rw-r--r--examples/mimxrt1062-evk/.cargo/config.toml8
-rw-r--r--examples/mimxrt1062-evk/Cargo.toml29
-rw-r--r--examples/mimxrt1062-evk/build.rs12
-rw-r--r--examples/mimxrt1062-evk/src/bin/blinky.rs25
-rw-r--r--examples/mimxrt1062-evk/src/bin/button.rs36
-rw-r--r--examples/mimxrt1062-evk/src/lib.rs60
-rw-r--r--examples/mspm0g3507/.cargo/config.toml2
-rw-r--r--examples/mspm0g3507/Cargo.toml2
-rw-r--r--examples/nrf-rtos-trace/Cargo.toml2
-rw-r--r--examples/nrf51/Cargo.toml2
-rw-r--r--examples/nrf52810/Cargo.toml2
-rw-r--r--examples/nrf52840-rtic/Cargo.toml2
-rw-r--r--examples/nrf52840/Cargo.toml4
-rw-r--r--examples/nrf52840/src/bin/self_spawn_current_executor.rs3
-rw-r--r--examples/nrf5340/Cargo.toml4
-rw-r--r--examples/nrf54l15/Cargo.toml2
-rw-r--r--examples/nrf9151/ns/Cargo.toml2
-rw-r--r--examples/nrf9151/s/Cargo.toml2
-rw-r--r--examples/nrf9160/Cargo.toml2
-rw-r--r--examples/rp/Cargo.toml22
-rw-r--r--examples/rp/src/bin/usb_raw_bulk.rs4
-rw-r--r--examples/rp/src/bin/usb_webusb.rs4
-rw-r--r--examples/rp235x/Cargo.toml19
-rw-r--r--examples/rp235x/memory.x4
-rw-r--r--examples/rp235x/src/bin/blinky_wifi.rs6
-rw-r--r--examples/rp235x/src/bin/usb_webusb.rs4
-rw-r--r--examples/stm32c0/Cargo.toml3
-rw-r--r--examples/stm32c0/src/bin/rtc.rs35
-rw-r--r--examples/stm32f0/src/bin/adc-watchdog.rs34
-rw-r--r--examples/stm32f1/Cargo.toml2
-rw-r--r--examples/stm32f1/src/bin/input_capture.rs2
-rw-r--r--examples/stm32f1/src/bin/pwm_input.rs2
-rw-r--r--examples/stm32f3/Cargo.toml2
-rw-r--r--examples/stm32f334/Cargo.toml2
-rw-r--r--examples/stm32f4/Cargo.toml2
-rw-r--r--examples/stm32f4/src/bin/input_capture.rs2
-rw-r--r--examples/stm32f4/src/bin/pwm.rs2
-rw-r--r--examples/stm32f4/src/bin/pwm_complementary.rs4
-rw-r--r--examples/stm32f4/src/bin/pwm_input.rs2
-rw-r--r--examples/stm32f4/src/bin/ws2812_pwm.rs2
-rw-r--r--examples/stm32f7/Cargo.toml2
-rw-r--r--examples/stm32f7/src/bin/cryp.rs4
-rw-r--r--examples/stm32f7/src/bin/qspi.rs1
-rw-r--r--examples/stm32g0/Cargo.toml2
-rw-r--r--examples/stm32g0/src/bin/hf_timer.rs4
-rw-r--r--examples/stm32g0/src/bin/input_capture.rs4
-rw-r--r--examples/stm32g0/src/bin/pwm_complementary.rs8
-rw-r--r--examples/stm32g0/src/bin/pwm_input.rs4
-rw-r--r--examples/stm32g4/Cargo.toml2
-rw-r--r--examples/stm32g4/src/bin/i2c_slave.rs149
-rw-r--r--examples/stm32g4/src/bin/pwm.rs2
-rw-r--r--examples/stm32h5/Cargo.toml2
-rw-r--r--examples/stm32h7/Cargo.toml4
-rw-r--r--examples/stm32h7/src/bin/i2c_shared.rs6
-rw-r--r--examples/stm32h7/src/bin/low_level_timer_api.rs10
-rw-r--r--examples/stm32h7/src/bin/pwm.rs2
-rw-r--r--examples/stm32h723/src/bin/spdifrx.rs24
-rw-r--r--examples/stm32h735/Cargo.toml2
-rw-r--r--examples/stm32h742/Cargo.toml2
-rw-r--r--examples/stm32h742/src/bin/qspi.rs1
-rw-r--r--examples/stm32h755cm4/Cargo.toml4
-rw-r--r--examples/stm32h755cm7/Cargo.toml4
-rw-r--r--examples/stm32h7b0/Cargo.toml4
-rw-r--r--examples/stm32h7rs/Cargo.toml2
-rw-r--r--examples/stm32h7rs/src/bin/xspi_memory_mapped.rs850
-rw-r--r--examples/stm32l0/src/bin/dds.rs4
-rw-r--r--examples/stm32l1/Cargo.toml2
-rw-r--r--examples/stm32l4/Cargo.toml4
-rw-r--r--examples/stm32l4/src/bin/spe_adin1110_http_server.rs2
-rw-r--r--examples/stm32l432/src/bin/qspi_mmap.rs3
-rw-r--r--examples/stm32l5/Cargo.toml2
-rw-r--r--examples/stm32u0/Cargo.toml2
-rw-r--r--examples/stm32u5/Cargo.toml2
-rw-r--r--examples/stm32wb/src/bin/gatt_server.rs9
-rw-r--r--examples/stm32wba/src/bin/adc.rs49
-rw-r--r--examples/stm32wl/Cargo.toml2
-rwxr-xr-xrelease/bump-dependency.sh2
-rw-r--r--rust-toolchain-nightly.toml2
-rw-r--r--rust-toolchain.toml2
-rw-r--r--tests/mspm0/Cargo.toml6
-rw-r--r--tests/mspm0/build.rs3
-rw-r--r--tests/mspm0/memory_g3519.x6
-rw-r--r--tests/mspm0/src/bin/dma.rs503
-rw-r--r--tests/mspm0/src/bin/uart.rs5
-rw-r--r--tests/mspm0/src/bin/uart_buffered.rs115
-rw-r--r--tests/nrf/Cargo.toml2
-rw-r--r--tests/rp/Cargo.toml4
-rw-r--r--tests/stm32/Cargo.toml1
-rw-r--r--tests/stm32/build.rs1
-rw-r--r--tests/stm32/src/bin/cryp.rs2
-rw-r--r--tests/stm32/src/bin/sdmmc.rs4
-rw-r--r--tests/stm32/src/common.rs10
321 files changed, 10994 insertions, 2220 deletions
diff --git a/.github/ci/book.sh b/.github/ci/book.sh
index 285cdc8fa..2466f53f5 100755
--- a/.github/ci/book.sh
+++ b/.github/ci/book.sh
@@ -1,5 +1,7 @@
1#!/bin/bash 1#!/bin/bash
2## on push branch=main 2## on push branch=main
3## priority -9
4## dedup dequeue
3 5
4set -euxo pipefail 6set -euxo pipefail
5 7
diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh
index 58ffe5f2e..9162b37ae 100755
--- a/.github/ci/doc.sh
+++ b/.github/ci/doc.sh
@@ -1,5 +1,7 @@
1#!/bin/bash 1#!/bin/bash
2## on push branch=main 2## on push branch=main
3## priority -10
4## dedup dequeue
3 5
4set -euxo pipefail 6set -euxo pipefail
5 7
@@ -26,6 +28,7 @@ docserver-builder -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup
26docserver-builder -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup 28docserver-builder -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup
27docserver-builder -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup 29docserver-builder -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup
28docserver-builder -i ./embassy-mspm0 -o webroot/crates/embassy-mspm0/git.zup 30docserver-builder -i ./embassy-mspm0 -o webroot/crates/embassy-mspm0/git.zup
31docserver-builder -i ./embassy-nxp -o webroot/crates/embassy-nxp/git.zup
29docserver-builder -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup 32docserver-builder -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup
30docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup 33docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup
31docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup 34docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup
@@ -42,6 +45,7 @@ docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/g
42docserver-builder -i ./embassy-usb-synopsys-otg -o webroot/crates/embassy-usb-synopsys-otg/git.zup 45docserver-builder -i ./embassy-usb-synopsys-otg -o webroot/crates/embassy-usb-synopsys-otg/git.zup
43 46
44docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup 47docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup
48docserver-builder -i ./embassy-net-nrf91 -o webroot/crates/embassy-net-nrf91/git.zup
45docserver-builder -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup 49docserver-builder -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup
46docserver-builder -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup 50docserver-builder -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup
47docserver-builder -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup 51docserver-builder -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup
diff --git a/.vscode/settings.json b/.vscode/settings.json
index e4814ff27..6edd9312a 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -36,6 +36,8 @@
36 // "examples/nrf52840-rtic/Cargo.toml", 36 // "examples/nrf52840-rtic/Cargo.toml",
37 // "examples/nrf5340/Cargo.toml", 37 // "examples/nrf5340/Cargo.toml",
38 // "examples/nrf-rtos-trace/Cargo.toml", 38 // "examples/nrf-rtos-trace/Cargo.toml",
39 // "examples/mimxrt1011/Cargo.toml",
40 // "examples/mimxrt1062-evk/Cargo.toml",
39 // "examples/rp/Cargo.toml", 41 // "examples/rp/Cargo.toml",
40 // "examples/std/Cargo.toml", 42 // "examples/std/Cargo.toml",
41 // "examples/stm32c0/Cargo.toml", 43 // "examples/stm32c0/Cargo.toml",
diff --git a/RELEASE.md b/RELEASE.md
new file mode 100644
index 000000000..0ebcfd9fa
--- /dev/null
+++ b/RELEASE.md
@@ -0,0 +1,58 @@
1# RELEASE.md
2
3This document outlines the process for releasing Embassy crates using `cargo-release` and the `release/bump-dependency.sh` script.
4
5When releasing a crate, keep in mind that you may need to recursively release dependencies as well.
6
7## Prerequisites
8
9- Install [`cargo-release`](https://github.com/crate-ci/cargo-release):
10
11 ```sh
12 cargo binstall cargo-release
13
14## Crate release
15
16Check if there are changes to the public API since the last release. If there is a breaking change, follow
17the process for creating a minor release. Otherewise, follow the process for creating a new patch release.
18
19Keep in mind that some crates may need the --features and --target flags passed to cargo release. For more information on that,
20look at the `Cargo.toml` files.
21
22### Patch release (no breaking public API changes)
23
24```
25cd embassy-nrf/
26cargo release patch --features time,defmt,unstable-pac,gpiote,time-driver-rtc1,nrf52840 --target thumbv7em-none-eabi
27
28# If dry-run is OK (no missing dependencies on crates.io)
29cargo release patch --execute --features time,defmt,unstable-pac,gpiote,time-driver-rtc1,nrf52840 --target thumbv7em-none-eabi
30```
31
32### Minor release
33
34```
35# Bump versions in crate files
36./release/bump-dependency.sh embassy-nrf 0.4.0
37
38# Commit version bump
39git commit -am 'chore: update to `embassy-nrf` v0.4.0'
40
41# Release crate
42cd embassy-nrf/
43cargo release minor --features time,defmt,unstable-pac,gpiote,time-driver-rtc1,nrf52840 --target thumbv7em-none-eabi
44
45# If dry-run is OK (no missing dependencies on crates.io)
46cargo release minor --execute --features time,defmt,unstable-pac,gpiote,time-driver-rtc1,nrf52840 --target thumbv7em-none-eabi
47```
48
49## Push tags
50
51Push the git tags that `cargo release` created earlier:
52
53```
54git push --tags
55```
56## Reference
57
58* [PR introducing release automation](https://github.com/embassy-rs/embassy/pull/4289)
diff --git a/ci.sh b/ci.sh
index e1fdf998a..f4db1da03 100755
--- a/ci.sh
+++ b/ci.sh
@@ -40,6 +40,8 @@ cargo batch \
40 --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7r-none-eabihf --features arch-cortex-ar,executor-thread \ 40 --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7r-none-eabihf --features arch-cortex-ar,executor-thread \
41 --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32 \ 41 --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32 \
42 --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread \ 42 --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread \
43 --- build --release --manifest-path embassy-embedded-hal/Cargo.toml --target thumbv7em-none-eabi \
44 --- build --release --manifest-path embassy-embedded-hal/Cargo.toml --target thumbv7em-none-eabi --features time \
43 --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \ 45 --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \
44 --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,mock-driver \ 46 --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,mock-driver \
45 --- build --release --manifest-path embassy-time-queue-utils/Cargo.toml --target thumbv6m-none-eabi \ 47 --- build --release --manifest-path embassy-time-queue-utils/Cargo.toml --target thumbv6m-none-eabi \
@@ -102,6 +104,8 @@ cargo batch \
102 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti \ 104 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti \
103 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt \ 105 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt \
104 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,single-bank,defmt \ 106 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,single-bank,defmt \
107 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c071rb,defmt,exti,time-driver-any,time \
108 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c051f6,defmt,exti,time-driver-any,time \
105 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f038f6,defmt,exti,time-driver-any,time \ 109 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f038f6,defmt,exti,time-driver-any,time \
106 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030c6,defmt,exti,time-driver-any,time \ 110 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030c6,defmt,exti,time-driver-any,time \
107 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f058t8,defmt,exti,time-driver-any,time \ 111 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f058t8,defmt,exti,time-driver-any,time \
@@ -155,11 +159,13 @@ cargo batch \
155 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303c8,defmt,exti,time-driver-any,time \ 159 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303c8,defmt,exti,time-driver-any,time \
156 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f398ve,defmt,exti,time-driver-any,time \ 160 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f398ve,defmt,exti,time-driver-any,time \
157 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f378cc,defmt,exti,time-driver-any,time \ 161 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f378cc,defmt,exti,time-driver-any,time \
162 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g0b0ce,defmt,exti,time-driver-any,time \
158 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g0c1ve,defmt,exti,time-driver-any,time \ 163 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g0c1ve,defmt,exti,time-driver-any,time \
159 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,time \ 164 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,time \
160 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time-driver-any,low-power,time \ 165 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time-driver-any,low-power,time \
161 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32wl54jc-cm0p,defmt,exti,time-driver-any,time \ 166 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32wl54jc-cm0p,defmt,exti,time-driver-any,time \
162 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wle5jb,defmt,exti,time-driver-any,time \ 167 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wle5jb,defmt,exti,time-driver-any,time \
168 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g431kb,defmt,exti,time-driver-any,time \
163 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g474pe,dual-bank,defmt,exti,time-driver-any,time \ 169 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g474pe,dual-bank,defmt,exti,time-driver-any,time \
164 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f107vc,defmt,exti,time-driver-any,time \ 170 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f107vc,defmt,exti,time-driver-any,time \
165 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103re,defmt,exti,time-driver-any,time \ 171 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103re,defmt,exti,time-driver-any,time \
@@ -169,6 +175,8 @@ cargo batch \
169 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h562ag,defmt,exti,time-driver-any,time \ 175 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h562ag,defmt,exti,time-driver-any,time \
170 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba50ke,defmt,exti,time-driver-any,time \ 176 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba50ke,defmt,exti,time-driver-any,time \
171 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba55ug,defmt,exti,time-driver-any,time \ 177 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba55ug,defmt,exti,time-driver-any,time \
178 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba62cg,defmt,exti,time-driver-any,time \
179 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba65ri,defmt,exti,time-driver-any,low-power,time \
172 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5f9zj,defmt,exti,time-driver-any,time \ 180 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5f9zj,defmt,exti,time-driver-any,time \
173 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5g9nj,defmt,exti,time-driver-any,time \ 181 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5g9nj,defmt,exti,time-driver-any,time \
174 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb35ce,defmt,exti,time-driver-any,time \ 182 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb35ce,defmt,exti,time-driver-any,time \
@@ -176,7 +184,9 @@ cargo batch \
176 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u031r8,defmt,exti,time-driver-any,time \ 184 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u031r8,defmt,exti,time-driver-any,time \
177 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u073mb,defmt,exti,time-driver-any,time \ 185 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u073mb,defmt,exti,time-driver-any,time \
178 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc,defmt,exti,time-driver-any,time \ 186 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc,defmt,exti,time-driver-any,time \
179 --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv8m.main-none-eabihf \ 187 --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv8m.main-none-eabihf --features lpc55,defmt \
188 --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv7em-none-eabihf --features mimxrt1011,rt,defmt,time-driver-pit \
189 --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv7em-none-eabihf --features mimxrt1062,rt,defmt,time-driver-pit \
180 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0c1104dgs20,defmt,time-driver-any \ 190 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0c1104dgs20,defmt,time-driver-any \
181 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3507pm,defmt,time-driver-any \ 191 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3507pm,defmt,time-driver-any \
182 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3519pz,defmt,time-driver-any \ 192 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3519pz,defmt,time-driver-any \
@@ -244,6 +254,7 @@ cargo batch \
244 --- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32h5 \ 254 --- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32h5 \
245 --- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h7 \ 255 --- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h7 \
246 --- build --release --manifest-path examples/stm32h7b0/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h7b0 \ 256 --- build --release --manifest-path examples/stm32h7b0/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h7b0 \
257 --- build --release --manifest-path examples/stm32h723/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h723 \
247 --- build --release --manifest-path examples/stm32h735/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h735 \ 258 --- build --release --manifest-path examples/stm32h735/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h735 \
248 --- build --release --manifest-path examples/stm32h755cm4/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h755cm4 \ 259 --- build --release --manifest-path examples/stm32h755cm4/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h755cm4 \
249 --- build --release --manifest-path examples/stm32h755cm7/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h755cm7 \ 260 --- build --release --manifest-path examples/stm32h755cm7/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h755cm7 \
@@ -259,6 +270,8 @@ cargo batch \
259 --- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32wba \ 270 --- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32wba \
260 --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32wl \ 271 --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32wl \
261 --- build --release --manifest-path examples/lpc55s69/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/lpc55s69 \ 272 --- build --release --manifest-path examples/lpc55s69/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/lpc55s69 \
273 --- build --release --manifest-path examples/mimxrt1011/Cargo.toml --target thumbv7em-none-eabihf --artifact-dir out/examples/mimxrt1011 \
274 --- build --release --manifest-path examples/mimxrt1062-evk/Cargo.toml --target thumbv7em-none-eabihf --artifact-dir out/examples/mimxrt1062-evk \
262 --- build --release --manifest-path examples/mspm0g3507/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3507 \ 275 --- build --release --manifest-path examples/mspm0g3507/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3507 \
263 --- build --release --manifest-path examples/mspm0g3519/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3519 \ 276 --- build --release --manifest-path examples/mspm0g3519/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3519 \
264 --- build --release --manifest-path examples/mspm0l1306/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0l1306 \ 277 --- build --release --manifest-path examples/mspm0l1306/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0l1306 \
@@ -294,6 +307,7 @@ cargo batch \
294 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --artifact-dir out/tests/stm32g491re \ 307 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --artifact-dir out/tests/stm32g491re \
295 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --artifact-dir out/tests/stm32g071rb \ 308 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --artifact-dir out/tests/stm32g071rb \
296 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --artifact-dir out/tests/stm32c031c6 \ 309 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --artifact-dir out/tests/stm32c031c6 \
310 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c071rb --artifact-dir out/tests/stm32c071rb \
297 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --artifact-dir out/tests/stm32h755zi \ 311 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --artifact-dir out/tests/stm32h755zi \
298 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h753zi --artifact-dir out/tests/stm32h753zi \ 312 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h753zi --artifact-dir out/tests/stm32h753zi \
299 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7a3zi --artifact-dir out/tests/stm32h7a3zi \ 313 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7a3zi --artifact-dir out/tests/stm32h7a3zi \
@@ -369,8 +383,12 @@ rm out/tests/pimoroni-pico-plus-2/adc
369# temporarily disabled 383# temporarily disabled
370rm out/tests/pimoroni-pico-plus-2/pwm 384rm out/tests/pimoroni-pico-plus-2/pwm
371 385
372# temporarily disabled, bad hardware connection. 386# flaky
373rm -f out/tests/rpi-pico/* 387rm out/tests/rpi-pico/pwm
388rm out/tests/rpi-pico/cyw43-perf
389
390# tests are implemented but the HIL test farm doesn't actually have this board yet
391rm -rf out/tests/stm32c071rb
374 392
375if [[ -z "${TELEPROBE_TOKEN-}" ]]; then 393if [[ -z "${TELEPROBE_TOKEN-}" ]]; then
376 echo No teleprobe token found, skipping running HIL tests 394 echo No teleprobe token found, skipping running HIL tests
diff --git a/cyw43-pio/CHANGELOG.md b/cyw43-pio/CHANGELOG.md
index 4d56973df..218271e15 100644
--- a/cyw43-pio/CHANGELOG.md
+++ b/cyw43-pio/CHANGELOG.md
@@ -5,9 +5,14 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8## Unreleased 8<!-- next-header -->
9## Unreleased - ReleaseDate
9 10
10- Update embassy-rp to 0.4.0 11## 0.5.1 - 2025-07-16
12
13## 0.5.0 - 2025-07-15
14
15- Update embassy-rp to 0.5.0
11 16
12## 0.3.0 - 2025-01-05 17## 0.3.0 - 2025-01-05
13 18
diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml
index 93a2e7089..d60793bdc 100644
--- a/cyw43-pio/Cargo.toml
+++ b/cyw43-pio/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "cyw43-pio" 2name = "cyw43-pio"
3version = "0.4.0" 3version = "0.5.1"
4edition = "2021" 4edition = "2021"
5description = "RP2040 PIO SPI implementation for cyw43" 5description = "RP2040 PIO SPI implementation for cyw43"
6keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"] 6keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"]
@@ -10,8 +10,8 @@ repository = "https://github.com/embassy-rs/embassy"
10documentation = "https://docs.embassy.dev/cyw43-pio" 10documentation = "https://docs.embassy.dev/cyw43-pio"
11 11
12[dependencies] 12[dependencies]
13cyw43 = { version = "0.3.0", path = "../cyw43" } 13cyw43 = { version = "0.4.0", path = "../cyw43" }
14embassy-rp = { version = "0.4.0", path = "../embassy-rp" } 14embassy-rp = { version = "0.6.0", path = "../embassy-rp" }
15fixed = "1.23.1" 15fixed = "1.23.1"
16defmt = { version = "1.0.1", optional = true } 16defmt = { version = "1.0.1", optional = true }
17 17
diff --git a/cyw43-pio/release.toml b/cyw43-pio/release.toml
new file mode 100644
index 000000000..fb6feaf21
--- /dev/null
+++ b/cyw43-pio/release.toml
@@ -0,0 +1,5 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/cyw43/CHANGELOG.md b/cyw43/CHANGELOG.md
index 40a638388..c800e785b 100644
--- a/cyw43/CHANGELOG.md
+++ b/cyw43/CHANGELOG.md
@@ -5,7 +5,14 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8## Unreleased 8<!-- next-header -->
9## Unreleased - ReleaseDate
10
11## 0.4.0 - 2025-07-15
12
13- bump embassy-sync to 0.7.0
14- bump bt-hci to 0.3.0
15- make State::new const fn
9 16
10## 0.3.0 - 2025-01-05 17## 0.3.0 - 2025-01-05
11 18
diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml
index c52a653bd..8aef8963c 100644
--- a/cyw43/Cargo.toml
+++ b/cyw43/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "cyw43" 2name = "cyw43"
3version = "0.3.0" 3version = "0.4.0"
4edition = "2021" 4edition = "2021"
5description = "Rust driver for the CYW43439 WiFi chip, used in the Raspberry Pi Pico W." 5description = "Rust driver for the CYW43439 WiFi chip, used in the Raspberry Pi Pico W."
6keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"] 6keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"]
@@ -21,7 +21,7 @@ firmware-logs = []
21embassy-time = { version = "0.4.0", path = "../embassy-time"} 21embassy-time = { version = "0.4.0", path = "../embassy-time"}
22embassy-sync = { version = "0.7.0", path = "../embassy-sync"} 22embassy-sync = { version = "0.7.0", path = "../embassy-sync"}
23embassy-futures = { version = "0.1.0", path = "../embassy-futures"} 23embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
24embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel"} 24embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel"}
25 25
26defmt = { version = "1.0.1", optional = true } 26defmt = { version = "1.0.1", optional = true }
27log = { version = "0.4.17", optional = true } 27log = { version = "0.4.17", optional = true }
diff --git a/cyw43/release.toml b/cyw43/release.toml
new file mode 100644
index 000000000..fb6feaf21
--- /dev/null
+++ b/cyw43/release.toml
@@ -0,0 +1,5 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/docs/examples/basic/Cargo.toml b/docs/examples/basic/Cargo.toml
index f5cf2b231..c4b72d81a 100644
--- a/docs/examples/basic/Cargo.toml
+++ b/docs/examples/basic/Cargo.toml
@@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
8[dependencies] 8[dependencies]
9embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["defmt", "arch-cortex-m", "executor-thread"] } 9embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["defmt", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt"] } 10embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt"] }
11embassy-nrf = { version = "0.3.1", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } 11embassy-nrf = { version = "0.5.0", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] }
12 12
13defmt = "1.0.1" 13defmt = "1.0.1"
14defmt-rtt = "1.0.0" 14defmt-rtt = "1.0.0"
diff --git a/docs/index.adoc b/docs/index.adoc
index 9c6150196..80754d5a4 100644
--- a/docs/index.adoc
+++ b/docs/index.adoc
@@ -5,6 +5,7 @@
5:toc-placement: left 5:toc-placement: left
6:toclevels: 2 6:toclevels: 2
7:imagesdir: images 7:imagesdir: images
8:source-highlighter: rouge
8 9
9# Embassy Book 10# Embassy Book
10 11
diff --git a/docs/pages/layer_by_layer.adoc b/docs/pages/layer_by_layer.adoc
index 7dba11b5e..0692ee4fa 100644
--- a/docs/pages/layer_by_layer.adoc
+++ b/docs/pages/layer_by_layer.adoc
@@ -8,7 +8,7 @@ The application we'll write is a simple 'push button, blink led' application, wh
8 8
9== PAC version 9== PAC version
10 10
11The PAC is the lowest API for accessing peripherals and registers, if you don't count reading/writing directly to memory addresses. It provides distinct types to make accessing peripheral registers easier, but it does not prevent you from writing unsafe code. 11The PAC is the lowest API for accessing peripherals and registers, if you don't count reading/writing directly to memory addresses. It provides distinct types to make accessing peripheral registers easier, but it does little to prevent you from configuring or coordinating those registers incorrectly.
12 12
13Writing an application using the PAC directly is therefore not recommended, but if the functionality you want to use is not exposed in the upper layers, that's what you need to use. 13Writing an application using the PAC directly is therefore not recommended, but if the functionality you want to use is not exposed in the upper layers, that's what you need to use.
14 14
diff --git a/docs/pages/new_project.adoc b/docs/pages/new_project.adoc
index cd943b4f6..906d89f36 100644
--- a/docs/pages/new_project.adoc
+++ b/docs/pages/new_project.adoc
@@ -11,6 +11,8 @@ Once you’ve successfully xref:#_getting_started[run some example projects], th
11- link:https://github.com/lulf/embassy-template[embassy-template] (STM32, NRF, and RP) 11- link:https://github.com/lulf/embassy-template[embassy-template] (STM32, NRF, and RP)
12- link:https://github.com/bentwire/embassy-rp2040-template[embassy-rp2040-template] (RP) 12- link:https://github.com/bentwire/embassy-rp2040-template[embassy-rp2040-template] (RP)
13 13
14=== esp-generate
15- link:https://github.com/esp-rs/esp-generate[esp-generate] (ESP32 using esp-hal)
14 16
15== Starting a project from scratch 17== Starting a project from scratch
16 18
diff --git a/docs/pages/overview.adoc b/docs/pages/overview.adoc
index acd757795..18eaaeb75 100644
--- a/docs/pages/overview.adoc
+++ b/docs/pages/overview.adoc
@@ -30,7 +30,7 @@ The Embassy project maintains HALs for select hardware, but you can still use HA
30* link:https://docs.embassy.dev/embassy-nrf/[embassy-nrf], for the Nordic Semiconductor nRF52, nRF53, nRF91 series. 30* link:https://docs.embassy.dev/embassy-nrf/[embassy-nrf], for the Nordic Semiconductor nRF52, nRF53, nRF91 series.
31* link:https://docs.embassy.dev/embassy-rp/[embassy-rp], for the Raspberry Pi RP2040 as well as RP235x microcontroller. 31* link:https://docs.embassy.dev/embassy-rp/[embassy-rp], for the Raspberry Pi RP2040 as well as RP235x microcontroller.
32* link:https://docs.embassy.dev/embassy-mspm0/[embassy-mspm0], for the Texas Instruments MSPM0 microcontrollers. 32* link:https://docs.embassy.dev/embassy-mspm0/[embassy-mspm0], for the Texas Instruments MSPM0 microcontrollers.
33* link:https://github.com/esp-rs[esp-rs], for the Espressif Systems ESP32 series of chips. 33* link:https://github.com/esp-rs/esp-hal[esp-hal], for the Espressif Systems ESP32 series of chips.
34* link:https://github.com/ch32-rs/ch32-hal[ch32-hal], for the WCH 32-bit RISC-V(CH32V) series of chips. 34* link:https://github.com/ch32-rs/ch32-hal[ch32-hal], for the WCH 32-bit RISC-V(CH32V) series of chips.
35* link:https://github.com/AlexCharlton/mpfs-hal[mpfs-hal], for the Microchip PolarFire SoC. 35* link:https://github.com/AlexCharlton/mpfs-hal[mpfs-hal], for the Microchip PolarFire SoC.
36* link:https://github.com/py32-rs/py32-hal[py32-hal], for the Puya Semiconductor PY32 series of chips. 36* link:https://github.com/py32-rs/py32-hal[py32-hal], for the Puya Semiconductor PY32 series of chips.
diff --git a/embassy-boot-nrf/Cargo.toml b/embassy-boot-nrf/Cargo.toml
index 3cfaa5a80..81479759c 100644
--- a/embassy-boot-nrf/Cargo.toml
+++ b/embassy-boot-nrf/Cargo.toml
@@ -1,7 +1,7 @@
1[package] 1[package]
2edition = "2021" 2edition = "2021"
3name = "embassy-boot-nrf" 3name = "embassy-boot-nrf"
4version = "0.4.0" 4version = "0.6.0"
5description = "Bootloader lib for nRF chips" 5description = "Bootloader lib for nRF chips"
6license = "MIT OR Apache-2.0" 6license = "MIT OR Apache-2.0"
7repository = "https://github.com/embassy-rs/embassy" 7repository = "https://github.com/embassy-rs/embassy"
@@ -25,8 +25,8 @@ defmt = { version = "1.0.1", optional = true }
25log = { version = "0.4.17", optional = true } 25log = { version = "0.4.17", optional = true }
26 26
27embassy-sync = { version = "0.7.0", path = "../embassy-sync" } 27embassy-sync = { version = "0.7.0", path = "../embassy-sync" }
28embassy-nrf = { version = "0.3.1", path = "../embassy-nrf", default-features = false } 28embassy-nrf = { version = "0.5.0", path = "../embassy-nrf", default-features = false }
29embassy-boot = { version = "0.4.0", path = "../embassy-boot" } 29embassy-boot = { version = "0.5.0", path = "../embassy-boot" }
30cortex-m = { version = "0.7.6" } 30cortex-m = { version = "0.7.6" }
31cortex-m-rt = { version = "0.7" } 31cortex-m-rt = { version = "0.7" }
32embedded-storage = "0.3.1" 32embedded-storage = "0.3.1"
diff --git a/embassy-boot-rp/Cargo.toml b/embassy-boot-rp/Cargo.toml
index afe5d6691..8ca999f67 100644
--- a/embassy-boot-rp/Cargo.toml
+++ b/embassy-boot-rp/Cargo.toml
@@ -1,7 +1,7 @@
1[package] 1[package]
2edition = "2021" 2edition = "2021"
3name = "embassy-boot-rp" 3name = "embassy-boot-rp"
4version = "0.5.0" 4version = "0.6.0"
5description = "Bootloader lib for RP2040 chips" 5description = "Bootloader lib for RP2040 chips"
6license = "MIT OR Apache-2.0" 6license = "MIT OR Apache-2.0"
7repository = "https://github.com/embassy-rs/embassy" 7repository = "https://github.com/embassy-rs/embassy"
@@ -25,8 +25,8 @@ defmt = { version = "1.0.1", optional = true }
25log = { version = "0.4", optional = true } 25log = { version = "0.4", optional = true }
26 26
27embassy-sync = { version = "0.7.0", path = "../embassy-sync" } 27embassy-sync = { version = "0.7.0", path = "../embassy-sync" }
28embassy-rp = { version = "0.4.0", path = "../embassy-rp", default-features = false } 28embassy-rp = { version = "0.6.0", path = "../embassy-rp", default-features = false }
29embassy-boot = { version = "0.4.0", path = "../embassy-boot" } 29embassy-boot = { version = "0.5.0", path = "../embassy-boot" }
30embassy-time = { version = "0.4.0", path = "../embassy-time" } 30embassy-time = { version = "0.4.0", path = "../embassy-time" }
31 31
32cortex-m = { version = "0.7.6" } 32cortex-m = { version = "0.7.6" }
diff --git a/embassy-boot-stm32/Cargo.toml b/embassy-boot-stm32/Cargo.toml
index 11ad453b8..b92d06c54 100644
--- a/embassy-boot-stm32/Cargo.toml
+++ b/embassy-boot-stm32/Cargo.toml
@@ -1,7 +1,7 @@
1[package] 1[package]
2edition = "2021" 2edition = "2021"
3name = "embassy-boot-stm32" 3name = "embassy-boot-stm32"
4version = "0.3.0" 4version = "0.4.0"
5description = "Bootloader lib for STM32 chips" 5description = "Bootloader lib for STM32 chips"
6license = "MIT OR Apache-2.0" 6license = "MIT OR Apache-2.0"
7repository = "https://github.com/embassy-rs/embassy" 7repository = "https://github.com/embassy-rs/embassy"
@@ -26,7 +26,7 @@ log = { version = "0.4", optional = true }
26 26
27embassy-sync = { version = "0.7.0", path = "../embassy-sync" } 27embassy-sync = { version = "0.7.0", path = "../embassy-sync" }
28embassy-stm32 = { version = "0.2.0", path = "../embassy-stm32", default-features = false } 28embassy-stm32 = { version = "0.2.0", path = "../embassy-stm32", default-features = false }
29embassy-boot = { version = "0.4.0", path = "../embassy-boot" } 29embassy-boot = { version = "0.5.0", path = "../embassy-boot" }
30cortex-m = { version = "0.7.6" } 30cortex-m = { version = "0.7.6" }
31cortex-m-rt = { version = "0.7" } 31cortex-m-rt = { version = "0.7" }
32embedded-storage = "0.3.1" 32embedded-storage = "0.3.1"
diff --git a/embassy-boot/Cargo.toml b/embassy-boot/Cargo.toml
index f12e8e304..172330ef2 100644
--- a/embassy-boot/Cargo.toml
+++ b/embassy-boot/Cargo.toml
@@ -1,7 +1,7 @@
1[package] 1[package]
2edition = "2021" 2edition = "2021"
3name = "embassy-boot" 3name = "embassy-boot"
4version = "0.4.0" 4version = "0.5.0"
5description = "A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks." 5description = "A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks."
6license = "MIT OR Apache-2.0" 6license = "MIT OR Apache-2.0"
7repository = "https://github.com/embassy-rs/embassy" 7repository = "https://github.com/embassy-rs/embassy"
@@ -28,7 +28,7 @@ defmt = { version = "1.0.1", optional = true }
28digest = "0.10" 28digest = "0.10"
29log = { version = "0.4", optional = true } 29log = { version = "0.4", optional = true }
30ed25519-dalek = { version = "2", default-features = false, features = ["digest"], optional = true } 30ed25519-dalek = { version = "2", default-features = false, features = ["digest"], optional = true }
31embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal" } 31embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal" }
32embassy-sync = { version = "0.7.0", path = "../embassy-sync" } 32embassy-sync = { version = "0.7.0", path = "../embassy-sync" }
33embedded-storage = "0.3.1" 33embedded-storage = "0.3.1"
34embedded-storage-async = { version = "0.4.1" } 34embedded-storage-async = { version = "0.4.1" }
diff --git a/embassy-embedded-hal/CHANGELOG.md b/embassy-embedded-hal/CHANGELOG.md
index 224036af4..04d95415c 100644
--- a/embassy-embedded-hal/CHANGELOG.md
+++ b/embassy-embedded-hal/CHANGELOG.md
@@ -5,7 +5,13 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8## Unreleased 8<!-- next-header -->
9## Unreleased - ReleaseDate
10
11## 0.3.1 - 2025-07-16
12
13- `SpiDevice` cancel safety: always set CS pin to high on drop
14- Update `embassy-sync` to v0.7.0
9 15
10## 0.3.0 - 2025-01-05 16## 0.3.0 - 2025-01-05
11 17
diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml
index efc3173d4..8277aa291 100644
--- a/embassy-embedded-hal/Cargo.toml
+++ b/embassy-embedded-hal/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "embassy-embedded-hal" 2name = "embassy-embedded-hal"
3version = "0.3.0" 3version = "0.3.1"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6description = "Collection of utilities to use `embedded-hal` and `embedded-storage` traits with Embassy." 6description = "Collection of utilities to use `embedded-hal` and `embedded-storage` traits with Embassy."
@@ -19,10 +19,9 @@ target = "x86_64-unknown-linux-gnu"
19 19
20[features] 20[features]
21time = ["dep:embassy-time"] 21time = ["dep:embassy-time"]
22default = ["time"]
23 22
24[dependencies] 23[dependencies]
25embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal" } 24embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal" }
26embassy-futures = { version = "0.1.0", path = "../embassy-futures" } 25embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
27embassy-sync = { version = "0.7.0", path = "../embassy-sync" } 26embassy-sync = { version = "0.7.0", path = "../embassy-sync" }
28embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } 27embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true }
diff --git a/embassy-embedded-hal/release.toml b/embassy-embedded-hal/release.toml
new file mode 100644
index 000000000..fb6feaf21
--- /dev/null
+++ b/embassy-embedded-hal/release.toml
@@ -0,0 +1,5 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-embedded-hal/src/adapter/blocking_async.rs b/embassy-embedded-hal/src/adapter/blocking_async.rs
index bafc31583..3b6e0ec00 100644
--- a/embassy-embedded-hal/src/adapter/blocking_async.rs
+++ b/embassy-embedded-hal/src/adapter/blocking_async.rs
@@ -1,5 +1,3 @@
1use embedded_hal_02::blocking;
2
3/// Wrapper that implements async traits using blocking implementations. 1/// Wrapper that implements async traits using blocking implementations.
4/// 2///
5/// This allows driver writers to depend on the async traits while still supporting embedded-hal peripheral implementations. 3/// This allows driver writers to depend on the async traits while still supporting embedded-hal peripheral implementations.
@@ -24,7 +22,7 @@ impl<T> BlockingAsync<T> {
24impl<T, E> embedded_hal_1::i2c::ErrorType for BlockingAsync<T> 22impl<T, E> embedded_hal_1::i2c::ErrorType for BlockingAsync<T>
25where 23where
26 E: embedded_hal_1::i2c::Error + 'static, 24 E: embedded_hal_1::i2c::Error + 'static,
27 T: blocking::i2c::WriteRead<Error = E> + blocking::i2c::Read<Error = E> + blocking::i2c::Write<Error = E>, 25 T: embedded_hal_1::i2c::I2c<Error = E>,
28{ 26{
29 type Error = E; 27 type Error = E;
30} 28}
@@ -32,7 +30,7 @@ where
32impl<T, E> embedded_hal_async::i2c::I2c for BlockingAsync<T> 30impl<T, E> embedded_hal_async::i2c::I2c for BlockingAsync<T>
33where 31where
34 E: embedded_hal_1::i2c::Error + 'static, 32 E: embedded_hal_1::i2c::Error + 'static,
35 T: blocking::i2c::WriteRead<Error = E> + blocking::i2c::Read<Error = E> + blocking::i2c::Write<Error = E>, 33 T: embedded_hal_1::i2c::I2c<Error = E>,
36{ 34{
37 async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { 35 async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
38 self.wrapped.read(address, read) 36 self.wrapped.read(address, read)
@@ -51,9 +49,7 @@ where
51 address: u8, 49 address: u8,
52 operations: &mut [embedded_hal_1::i2c::Operation<'_>], 50 operations: &mut [embedded_hal_1::i2c::Operation<'_>],
53 ) -> Result<(), Self::Error> { 51 ) -> Result<(), Self::Error> {
54 let _ = address; 52 self.wrapped.transaction(address, operations)
55 let _ = operations;
56 todo!()
57 } 53 }
58} 54}
59 55
@@ -63,16 +59,16 @@ where
63 59
64impl<T, E> embedded_hal_async::spi::ErrorType for BlockingAsync<T> 60impl<T, E> embedded_hal_async::spi::ErrorType for BlockingAsync<T>
65where 61where
66 E: embedded_hal_1::spi::Error, 62 E: embedded_hal_async::spi::Error,
67 T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>, 63 T: embedded_hal_1::spi::SpiBus<Error = E>,
68{ 64{
69 type Error = E; 65 type Error = E;
70} 66}
71 67
72impl<T, E> embedded_hal_async::spi::SpiBus<u8> for BlockingAsync<T> 68impl<T, E> embedded_hal_async::spi::SpiBus<u8> for BlockingAsync<T>
73where 69where
74 E: embedded_hal_1::spi::Error + 'static, 70 E: embedded_hal_async::spi::Error,
75 T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>, 71 T: embedded_hal_1::spi::SpiBus<Error = E>,
76{ 72{
77 async fn flush(&mut self) -> Result<(), Self::Error> { 73 async fn flush(&mut self) -> Result<(), Self::Error> {
78 Ok(()) 74 Ok(())
@@ -84,21 +80,17 @@ where
84 } 80 }
85 81
86 async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { 82 async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
87 self.wrapped.transfer(data)?; 83 self.wrapped.read(data)?;
88 Ok(()) 84 Ok(())
89 } 85 }
90 86
91 async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { 87 async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
92 // Ensure we write the expected bytes 88 self.wrapped.transfer(read, write)?;
93 for i in 0..core::cmp::min(read.len(), write.len()) {
94 read[i] = write[i].clone();
95 }
96 self.wrapped.transfer(read)?;
97 Ok(()) 89 Ok(())
98 } 90 }
99 91
100 async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { 92 async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
101 self.wrapped.transfer(data)?; 93 self.wrapped.transfer_in_place(data)?;
102 Ok(()) 94 Ok(())
103 } 95 }
104} 96}
diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs
index 91d6beee8..1c5e3571d 100644
--- a/embassy-executor-macros/src/macros/task.rs
+++ b/embassy-executor-macros/src/macros/task.rs
@@ -51,7 +51,11 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream {
51 .embassy_executor 51 .embassy_executor
52 .unwrap_or(Expr::Verbatim(TokenStream::from_str("::embassy_executor").unwrap())); 52 .unwrap_or(Expr::Verbatim(TokenStream::from_str("::embassy_executor").unwrap()));
53 53
54 if f.sig.asyncness.is_none() { 54 let returns_impl_trait = match &f.sig.output {
55 ReturnType::Type(_, ty) => matches!(**ty, Type::ImplTrait(_)),
56 _ => false,
57 };
58 if f.sig.asyncness.is_none() && !returns_impl_trait {
55 error(&mut errors, &f.sig, "task functions must be async"); 59 error(&mut errors, &f.sig, "task functions must be async");
56 } 60 }
57 if !f.sig.generics.params.is_empty() { 61 if !f.sig.generics.params.is_empty() {
@@ -66,17 +70,19 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream {
66 if !f.sig.variadic.is_none() { 70 if !f.sig.variadic.is_none() {
67 error(&mut errors, &f.sig, "task functions must not be variadic"); 71 error(&mut errors, &f.sig, "task functions must not be variadic");
68 } 72 }
69 match &f.sig.output { 73 if f.sig.asyncness.is_some() {
70 ReturnType::Default => {} 74 match &f.sig.output {
71 ReturnType::Type(_, ty) => match &**ty { 75 ReturnType::Default => {}
72 Type::Tuple(tuple) if tuple.elems.is_empty() => {} 76 ReturnType::Type(_, ty) => match &**ty {
73 Type::Never(_) => {} 77 Type::Tuple(tuple) if tuple.elems.is_empty() => {}
74 _ => error( 78 Type::Never(_) => {}
75 &mut errors, 79 _ => error(
76 &f.sig, 80 &mut errors,
77 "task functions must either not return a value, return `()` or return `!`", 81 &f.sig,
78 ), 82 "task functions must either not return a value, return `()` or return `!`",
79 }, 83 ),
84 },
85 }
80 } 86 }
81 87
82 let mut args = Vec::new(); 88 let mut args = Vec::new();
@@ -125,15 +131,21 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream {
125 )); 131 ));
126 } 132 }
127 133
134 let spawn = if returns_impl_trait {
135 quote!(spawn)
136 } else {
137 quote!(_spawn_async_fn)
138 };
139
128 #[cfg(feature = "nightly")] 140 #[cfg(feature = "nightly")]
129 let mut task_outer_body = quote! { 141 let mut task_outer_body = quote! {
130 trait _EmbassyInternalTaskTrait { 142 trait _EmbassyInternalTaskTrait {
131 type Fut: ::core::future::Future + 'static; 143 type Fut: ::core::future::Future<Output: #embassy_executor::_export::TaskReturnValue> + 'static;
132 fn construct(#fargs) -> Self::Fut; 144 fn construct(#fargs) -> Self::Fut;
133 } 145 }
134 146
135 impl _EmbassyInternalTaskTrait for () { 147 impl _EmbassyInternalTaskTrait for () {
136 type Fut = impl core::future::Future + 'static; 148 type Fut = impl core::future::Future<Output: #embassy_executor::_export::TaskReturnValue> + 'static;
137 fn construct(#fargs) -> Self::Fut { 149 fn construct(#fargs) -> Self::Fut {
138 #task_inner_ident(#(#full_args,)*) 150 #task_inner_ident(#(#full_args,)*)
139 } 151 }
@@ -141,7 +153,7 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream {
141 153
142 const POOL_SIZE: usize = #pool_size; 154 const POOL_SIZE: usize = #pool_size;
143 static POOL: #embassy_executor::raw::TaskPool<<() as _EmbassyInternalTaskTrait>::Fut, POOL_SIZE> = #embassy_executor::raw::TaskPool::new(); 155 static POOL: #embassy_executor::raw::TaskPool<<() as _EmbassyInternalTaskTrait>::Fut, POOL_SIZE> = #embassy_executor::raw::TaskPool::new();
144 unsafe { POOL._spawn_async_fn(move || <() as _EmbassyInternalTaskTrait>::construct(#(#full_args,)*)) } 156 unsafe { POOL.#spawn(move || <() as _EmbassyInternalTaskTrait>::construct(#(#full_args,)*)) }
145 }; 157 };
146 #[cfg(not(feature = "nightly"))] 158 #[cfg(not(feature = "nightly"))]
147 let mut task_outer_body = quote! { 159 let mut task_outer_body = quote! {
@@ -158,7 +170,7 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream {
158 {#embassy_executor::_export::task_pool_size::<_, _, _, POOL_SIZE>(#task_inner_ident)}, 170 {#embassy_executor::_export::task_pool_size::<_, _, _, POOL_SIZE>(#task_inner_ident)},
159 {#embassy_executor::_export::task_pool_align::<_, _, _, POOL_SIZE>(#task_inner_ident)}, 171 {#embassy_executor::_export::task_pool_align::<_, _, _, POOL_SIZE>(#task_inner_ident)},
160 > = unsafe { ::core::mem::transmute(#embassy_executor::_export::task_pool_new::<_, _, _, POOL_SIZE>(#task_inner_ident)) }; 172 > = unsafe { ::core::mem::transmute(#embassy_executor::_export::task_pool_new::<_, _, _, POOL_SIZE>(#task_inner_ident)) };
161 unsafe { __task_pool_get(#task_inner_ident)._spawn_async_fn(move || #task_inner_ident(#(#full_args,)*)) } 173 unsafe { __task_pool_get(#task_inner_ident).#spawn(move || #task_inner_ident(#(#full_args,)*)) }
162 }; 174 };
163 175
164 let task_outer_attrs = task_inner.attrs.clone(); 176 let task_outer_attrs = task_inner.attrs.clone();
diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md
index 608c67724..914863a83 100644
--- a/embassy-executor/CHANGELOG.md
+++ b/embassy-executor/CHANGELOG.md
@@ -5,9 +5,22 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8## unreleased 8<!-- next-header -->
9 9## Unreleased - ReleaseDate
10
11- Added `SpawnToken::id`
12- Task pools are now statically allocated on stable rust. All `task-arena-size-*` features have been removed and are no longer necessary.
13- New trace hooks: `_embassy_trace_poll_start` & `_embassy_trace_task_end`
14- Added task naming capability to tracing infrastructure
15- Added `Executor::id` & `Spawner::executor_id`
16- Disable `critical-section/std` for arch-std
17- Added possibility to select an executor in `#[embassy_executor::main]`
18- Fix AVR executor
19- executor: Make state implementations and their conditions match
10- Added support for Cortex-A and Cortex-R 20- Added support for Cortex-A and Cortex-R
21- Added support for `-> impl Future<Output = ()>` in `#[task]`
22- Fixed `Send` unsoundness with `-> impl Future` tasks
23- Marked `Spawner::for_current_executor` as `unsafe`
11 24
12## 0.7.0 - 2025-01-02 25## 0.7.0 - 2025-01-02
13 26
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml
index f014ccf30..2dbf2c29a 100644
--- a/embassy-executor/Cargo.toml
+++ b/embassy-executor/Cargo.toml
@@ -59,6 +59,7 @@ avr-device = { version = "0.7.0", optional = true }
59critical-section = { version = "1.1", features = ["std"] } 59critical-section = { version = "1.1", features = ["std"] }
60trybuild = "1.0" 60trybuild = "1.0"
61embassy-sync = { path = "../embassy-sync" } 61embassy-sync = { path = "../embassy-sync" }
62rustversion = "1.0.21"
62 63
63[features] 64[features]
64 65
diff --git a/embassy-executor/build_common.rs b/embassy-executor/build_common.rs
index b15a8369f..4f24e6d37 100644
--- a/embassy-executor/build_common.rs
+++ b/embassy-executor/build_common.rs
@@ -92,35 +92,3 @@ pub fn set_target_cfgs(cfgs: &mut CfgSet) {
92 92
93 cfgs.set("has_fpu", target.ends_with("-eabihf")); 93 cfgs.set("has_fpu", target.ends_with("-eabihf"));
94} 94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
97pub struct CompilerDate {
98 year: u16,
99 month: u8,
100 day: u8,
101}
102
103impl CompilerDate {
104 fn parse(date: &str) -> Option<Self> {
105 let mut parts = date.split('-');
106 let year = parts.next()?.parse().ok()?;
107 let month = parts.next()?.parse().ok()?;
108 let day = parts.next()?.parse().ok()?;
109 Some(Self { year, month, day })
110 }
111}
112
113impl PartialEq<&str> for CompilerDate {
114 fn eq(&self, other: &&str) -> bool {
115 let Some(other) = Self::parse(other) else {
116 return false;
117 };
118 self.eq(&other)
119 }
120}
121
122impl PartialOrd<&str> for CompilerDate {
123 fn partial_cmp(&self, other: &&str) -> Option<std::cmp::Ordering> {
124 Self::parse(other).map(|other| self.cmp(&other))
125 }
126}
diff --git a/embassy-executor/release.toml b/embassy-executor/release.toml
new file mode 100644
index 000000000..fb6feaf21
--- /dev/null
+++ b/embassy-executor/release.toml
@@ -0,0 +1,5 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs
index dfe420bab..e174a0594 100644
--- a/embassy-executor/src/lib.rs
+++ b/embassy-executor/src/lib.rs
@@ -65,8 +65,17 @@ pub mod _export {
65 65
66 use crate::raw::TaskPool; 66 use crate::raw::TaskPool;
67 67
68 trait TaskReturnValue {}
69 impl TaskReturnValue for () {}
70 impl TaskReturnValue for Never {}
71
72 #[diagnostic::on_unimplemented(
73 message = "task futures must resolve to `()` or `!`",
74 note = "use `async fn` or change the return type to `impl Future<Output = ()>`"
75 )]
76 #[allow(private_bounds)]
68 pub trait TaskFn<Args>: Copy { 77 pub trait TaskFn<Args>: Copy {
69 type Fut: Future + 'static; 78 type Fut: Future<Output: TaskReturnValue> + 'static;
70 } 79 }
71 80
72 macro_rules! task_fn_impl { 81 macro_rules! task_fn_impl {
@@ -74,7 +83,7 @@ pub mod _export {
74 impl<F, Fut, $($Tn,)*> TaskFn<($($Tn,)*)> for F 83 impl<F, Fut, $($Tn,)*> TaskFn<($($Tn,)*)> for F
75 where 84 where
76 F: Copy + FnOnce($($Tn,)*) -> Fut, 85 F: Copy + FnOnce($($Tn,)*) -> Fut,
77 Fut: Future + 'static, 86 Fut: Future<Output: TaskReturnValue> + 'static,
78 { 87 {
79 type Fut = Fut; 88 type Fut = Fut;
80 } 89 }
@@ -205,4 +214,42 @@ pub mod _export {
205 Align268435456: 268435456, 214 Align268435456: 268435456,
206 Align536870912: 536870912, 215 Align536870912: 536870912,
207 ); 216 );
217
218 #[allow(dead_code)]
219 trait HasOutput {
220 type Output;
221 }
222
223 impl<O> HasOutput for fn() -> O {
224 type Output = O;
225 }
226
227 #[allow(dead_code)]
228 type Never = <fn() -> ! as HasOutput>::Output;
229}
230
231/// Implementation details for embassy macros.
232/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
233#[doc(hidden)]
234#[cfg(feature = "nightly")]
235pub mod _export {
236 #[diagnostic::on_unimplemented(
237 message = "task futures must resolve to `()` or `!`",
238 note = "use `async fn` or change the return type to `impl Future<Output = ()>`"
239 )]
240 pub trait TaskReturnValue {}
241 impl TaskReturnValue for () {}
242 impl TaskReturnValue for Never {}
243
244 #[allow(dead_code)]
245 trait HasOutput {
246 type Output;
247 }
248
249 impl<O> HasOutput for fn() -> O {
250 type Output = O;
251 }
252
253 #[allow(dead_code)]
254 type Never = <fn() -> ! as HasOutput>::Output;
208} 255}
diff --git a/embassy-executor/src/raw/trace.rs b/embassy-executor/src/raw/trace.rs
index 6c9cfda25..aa27ab37e 100644
--- a/embassy-executor/src/raw/trace.rs
+++ b/embassy-executor/src/raw/trace.rs
@@ -283,7 +283,17 @@ pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) {
283 } 283 }
284 284
285 #[cfg(feature = "rtos-trace")] 285 #[cfg(feature = "rtos-trace")]
286 rtos_trace::trace::task_new(task.as_ptr() as u32); 286 {
287 rtos_trace::trace::task_new(task.as_ptr() as u32);
288 let name = task.name().unwrap_or("unnamed task\0");
289 let info = rtos_trace::TaskInfo {
290 name,
291 priority: 0,
292 stack_base: 0,
293 stack_size: 0,
294 };
295 rtos_trace::trace::task_send_info(task.id(), info);
296 }
287 297
288 #[cfg(feature = "rtos-trace")] 298 #[cfg(feature = "rtos-trace")]
289 TASK_TRACKER.add(*task); 299 TASK_TRACKER.add(*task);
diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs
index 522d97db3..2909d19a0 100644
--- a/embassy-executor/src/spawner.rs
+++ b/embassy-executor/src/spawner.rs
@@ -122,10 +122,26 @@ impl Spawner {
122 /// This function is `async` just to get access to the current async 122 /// This function is `async` just to get access to the current async
123 /// context. It returns instantly, it does not block/yield. 123 /// context. It returns instantly, it does not block/yield.
124 /// 124 ///
125 /// Using this method is discouraged due to it being unsafe. Consider the following
126 /// alternatives instead:
127 ///
128 /// - Pass the initial `Spawner` as an argument to tasks. Note that it's `Copy`, so you can
129 /// make as many copies of it as you want.
130 /// - Use `SendSpawner::for_current_executor()` instead, which is safe but can only be used
131 /// if task arguments are `Send`.
132 ///
133 /// The only case where using this method is absolutely required is obtaining the `Spawner`
134 /// for an `InterruptExecutor`.
135 ///
136 /// # Safety
137 ///
138 /// You must only execute this with an async `Context` created by the Embassy executor.
139 /// You must not execute it with manually-created `Context`s.
140 ///
125 /// # Panics 141 /// # Panics
126 /// 142 ///
127 /// Panics if the current executor is not an Embassy executor. 143 /// Panics if the current executor is not an Embassy executor.
128 pub fn for_current_executor() -> impl Future<Output = Self> { 144 pub unsafe fn for_current_executor() -> impl Future<Output = Self> {
129 poll_fn(|cx| { 145 poll_fn(|cx| {
130 let task = raw::task_from_waker(cx.waker()); 146 let task = raw::task_from_waker(cx.waker());
131 let executor = unsafe { 147 let executor = unsafe {
diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs
index 78c49c071..c1e7ec5d7 100644
--- a/embassy-executor/tests/test.rs
+++ b/embassy-executor/tests/test.rs
@@ -1,7 +1,8 @@
1#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] 1#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
2#![cfg_attr(feature = "nightly", feature(never_type))]
2 3
3use std::boxed::Box; 4use std::boxed::Box;
4use std::future::poll_fn; 5use std::future::{poll_fn, Future};
5use std::sync::{Arc, Mutex}; 6use std::sync::{Arc, Mutex};
6use std::task::Poll; 7use std::task::Poll;
7 8
@@ -58,6 +59,39 @@ fn executor_task() {
58 trace.push("poll task1") 59 trace.push("poll task1")
59 } 60 }
60 61
62 #[task]
63 async fn task2() -> ! {
64 panic!()
65 }
66
67 let (executor, trace) = setup();
68 executor.spawner().spawn(task1(trace.clone())).unwrap();
69
70 unsafe { executor.poll() };
71 unsafe { executor.poll() };
72
73 assert_eq!(
74 trace.get(),
75 &[
76 "pend", // spawning a task pends the executor
77 "poll task1", // poll only once.
78 ]
79 )
80}
81
82#[test]
83fn executor_task_rpit() {
84 #[task]
85 fn task1(trace: Trace) -> impl Future<Output = ()> {
86 async move { trace.push("poll task1") }
87 }
88
89 #[cfg(feature = "nightly")]
90 #[task]
91 fn task2() -> impl Future<Output = !> {
92 async { panic!() }
93 }
94
61 let (executor, trace) = setup(); 95 let (executor, trace) = setup();
62 executor.spawner().spawn(task1(trace.clone())).unwrap(); 96 executor.spawner().spawn(task1(trace.clone())).unwrap();
63 97
diff --git a/embassy-executor/tests/ui.rs b/embassy-executor/tests/ui.rs
index 278a4b903..7757775ee 100644
--- a/embassy-executor/tests/ui.rs
+++ b/embassy-executor/tests/ui.rs
@@ -17,6 +17,17 @@ fn ui() {
17 t.compile_fail("tests/ui/nonstatic_struct_elided.rs"); 17 t.compile_fail("tests/ui/nonstatic_struct_elided.rs");
18 t.compile_fail("tests/ui/nonstatic_struct_generic.rs"); 18 t.compile_fail("tests/ui/nonstatic_struct_generic.rs");
19 t.compile_fail("tests/ui/not_async.rs"); 19 t.compile_fail("tests/ui/not_async.rs");
20 t.compile_fail("tests/ui/spawn_nonsend.rs");
21 t.compile_fail("tests/ui/return_impl_future_nonsend.rs");
22 if rustversion::cfg!(stable) {
23 // output is slightly different on nightly
24 t.compile_fail("tests/ui/bad_return_impl_future.rs");
25 t.compile_fail("tests/ui/return_impl_send.rs");
26 }
27 if cfg!(feature = "nightly") {
28 t.compile_fail("tests/ui/bad_return_impl_future_nightly.rs");
29 t.compile_fail("tests/ui/return_impl_send_nightly.rs");
30 }
20 t.compile_fail("tests/ui/self_ref.rs"); 31 t.compile_fail("tests/ui/self_ref.rs");
21 t.compile_fail("tests/ui/self.rs"); 32 t.compile_fail("tests/ui/self.rs");
22 t.compile_fail("tests/ui/type_error.rs"); 33 t.compile_fail("tests/ui/type_error.rs");
diff --git a/embassy-executor/tests/ui/bad_return_impl_future.rs b/embassy-executor/tests/ui/bad_return_impl_future.rs
new file mode 100644
index 000000000..baaa7dc5a
--- /dev/null
+++ b/embassy-executor/tests/ui/bad_return_impl_future.rs
@@ -0,0 +1,9 @@
1#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
2use core::future::Future;
3
4#[embassy_executor::task]
5fn task() -> impl Future<Output = u32> {
6 async { 5 }
7}
8
9fn main() {}
diff --git a/embassy-executor/tests/ui/bad_return_impl_future.stderr b/embassy-executor/tests/ui/bad_return_impl_future.stderr
new file mode 100644
index 000000000..57f147714
--- /dev/null
+++ b/embassy-executor/tests/ui/bad_return_impl_future.stderr
@@ -0,0 +1,120 @@
1error[E0277]: task futures must resolve to `()` or `!`
2 --> tests/ui/bad_return_impl_future.rs:5:4
3 |
44 | #[embassy_executor::task]
5 | ------------------------- required by a bound introduced by this call
65 | fn task() -> impl Future<Output = u32> {
7 | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
8 |
9 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
10note: required by a bound in `task_pool_size`
11 --> src/lib.rs
12 |
13 | pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
14 | -------------- required by a bound in this function
15 | where
16 | F: TaskFn<Args, Fut = Fut>,
17 | ^^^^^^^^^ required by this bound in `task_pool_size`
18
19error[E0277]: task futures must resolve to `()` or `!`
20 --> tests/ui/bad_return_impl_future.rs:4:1
21 |
224 | #[embassy_executor::task]
23 | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
24 |
25 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
26note: required by a bound in `task_pool_size`
27 --> src/lib.rs
28 |
29 | pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
30 | -------------- required by a bound in this function
31 | where
32 | F: TaskFn<Args, Fut = Fut>,
33 | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_size`
34 = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
35
36error[E0277]: task futures must resolve to `()` or `!`
37 --> tests/ui/bad_return_impl_future.rs:5:4
38 |
394 | #[embassy_executor::task]
40 | ------------------------- required by a bound introduced by this call
415 | fn task() -> impl Future<Output = u32> {
42 | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
43 |
44 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
45note: required by a bound in `task_pool_align`
46 --> src/lib.rs
47 |
48 | pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
49 | --------------- required by a bound in this function
50 | where
51 | F: TaskFn<Args, Fut = Fut>,
52 | ^^^^^^^^^ required by this bound in `task_pool_align`
53
54error[E0277]: task futures must resolve to `()` or `!`
55 --> tests/ui/bad_return_impl_future.rs:4:1
56 |
574 | #[embassy_executor::task]
58 | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
59 |
60 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
61note: required by a bound in `task_pool_align`
62 --> src/lib.rs
63 |
64 | pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
65 | --------------- required by a bound in this function
66 | where
67 | F: TaskFn<Args, Fut = Fut>,
68 | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_align`
69 = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
70
71error[E0277]: task futures must resolve to `()` or `!`
72 --> tests/ui/bad_return_impl_future.rs:5:4
73 |
744 | #[embassy_executor::task]
75 | ------------------------- required by a bound introduced by this call
765 | fn task() -> impl Future<Output = u32> {
77 | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
78 |
79 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
80note: required by a bound in `__task_pool_get`
81 --> tests/ui/bad_return_impl_future.rs:4:1
82 |
834 | #[embassy_executor::task]
84 | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__task_pool_get`
85 = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
86
87error[E0277]: task futures must resolve to `()` or `!`
88 --> tests/ui/bad_return_impl_future.rs:5:4
89 |
904 | #[embassy_executor::task]
91 | ------------------------- required by a bound introduced by this call
925 | fn task() -> impl Future<Output = u32> {
93 | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
94 |
95 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
96note: required by a bound in `task_pool_new`
97 --> src/lib.rs
98 |
99 | pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE>
100 | ------------- required by a bound in this function
101 | where
102 | F: TaskFn<Args, Fut = Fut>,
103 | ^^^^^^^^^ required by this bound in `task_pool_new`
104
105error[E0277]: task futures must resolve to `()` or `!`
106 --> tests/ui/bad_return_impl_future.rs:4:1
107 |
1084 | #[embassy_executor::task]
109 | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
110 |
111 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
112note: required by a bound in `task_pool_new`
113 --> src/lib.rs
114 |
115 | pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE>
116 | ------------- required by a bound in this function
117 | where
118 | F: TaskFn<Args, Fut = Fut>,
119 | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_new`
120 = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/embassy-executor/tests/ui/bad_return_impl_future_nightly.rs b/embassy-executor/tests/ui/bad_return_impl_future_nightly.rs
new file mode 100644
index 000000000..baaa7dc5a
--- /dev/null
+++ b/embassy-executor/tests/ui/bad_return_impl_future_nightly.rs
@@ -0,0 +1,9 @@
1#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
2use core::future::Future;
3
4#[embassy_executor::task]
5fn task() -> impl Future<Output = u32> {
6 async { 5 }
7}
8
9fn main() {}
diff --git a/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr b/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr
new file mode 100644
index 000000000..73ceb989d
--- /dev/null
+++ b/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr
@@ -0,0 +1,10 @@
1error[E0277]: task futures must resolve to `()` or `!`
2 --> tests/ui/bad_return_impl_future_nightly.rs:4:1
3 |
44 | #[embassy_executor::task]
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskReturnValue` is not implemented for `u32`
6 |
7 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
8 = help: the following other types implement trait `TaskReturnValue`:
9 ()
10 <fn() -> ! as _export::HasOutput>::Output
diff --git a/embassy-executor/tests/ui/return_impl_future_nonsend.rs b/embassy-executor/tests/ui/return_impl_future_nonsend.rs
new file mode 100644
index 000000000..b8c184b21
--- /dev/null
+++ b/embassy-executor/tests/ui/return_impl_future_nonsend.rs
@@ -0,0 +1,21 @@
1#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
2
3use core::future::Future;
4
5use embassy_executor::SendSpawner;
6
7#[embassy_executor::task]
8fn task() -> impl Future<Output = ()> {
9 // runs in spawning thread
10 let non_send: *mut () = core::ptr::null_mut();
11 async move {
12 // runs in executor thread
13 println!("{}", non_send as usize);
14 }
15}
16
17fn send_spawn(s: SendSpawner) {
18 s.spawn(task()).unwrap();
19}
20
21fn main() {}
diff --git a/embassy-executor/tests/ui/return_impl_future_nonsend.stderr b/embassy-executor/tests/ui/return_impl_future_nonsend.stderr
new file mode 100644
index 000000000..8aeb9738a
--- /dev/null
+++ b/embassy-executor/tests/ui/return_impl_future_nonsend.stderr
@@ -0,0 +1,17 @@
1error: future cannot be sent between threads safely
2 --> tests/ui/return_impl_future_nonsend.rs:18:13
3 |
418 | s.spawn(task()).unwrap();
5 | ^^^^^^ future created by async block is not `Send`
6 |
7 = help: within `impl Sized`, the trait `Send` is not implemented for `*mut ()`
8note: captured value is not `Send`
9 --> tests/ui/return_impl_future_nonsend.rs:13:24
10 |
1113 | println!("{}", non_send as usize);
12 | ^^^^^^^^ has type `*mut ()` which is not `Send`
13note: required by a bound in `SendSpawner::spawn`
14 --> src/spawner.rs
15 |
16 | pub fn spawn<S: Send>(&self, token: SpawnToken<S>) -> Result<(), SpawnError> {
17 | ^^^^ required by this bound in `SendSpawner::spawn`
diff --git a/embassy-executor/tests/ui/return_impl_send.rs b/embassy-executor/tests/ui/return_impl_send.rs
new file mode 100644
index 000000000..6ddb0e722
--- /dev/null
+++ b/embassy-executor/tests/ui/return_impl_send.rs
@@ -0,0 +1,6 @@
1#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
2
3#[embassy_executor::task]
4fn task() -> impl Send {}
5
6fn main() {}
diff --git a/embassy-executor/tests/ui/return_impl_send.stderr b/embassy-executor/tests/ui/return_impl_send.stderr
new file mode 100644
index 000000000..759be1cde
--- /dev/null
+++ b/embassy-executor/tests/ui/return_impl_send.stderr
@@ -0,0 +1,137 @@
1error[E0277]: task futures must resolve to `()` or `!`
2 --> tests/ui/return_impl_send.rs:4:4
3 |
43 | #[embassy_executor::task]
5 | ------------------------- required by a bound introduced by this call
64 | fn task() -> impl Send {}
7 | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`
8 |
9 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
10note: required by a bound in `task_pool_size`
11 --> src/lib.rs
12 |
13 | pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
14 | -------------- required by a bound in this function
15 | where
16 | F: TaskFn<Args, Fut = Fut>,
17 | ^^^^^^^^^ required by this bound in `task_pool_size`
18
19error[E0277]: task futures must resolve to `()` or `!`
20 --> tests/ui/return_impl_send.rs:3:1
21 |
223 | #[embassy_executor::task]
23 | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`
24 |
25 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
26note: required by a bound in `task_pool_size`
27 --> src/lib.rs
28 |
29 | pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
30 | -------------- required by a bound in this function
31 | where
32 | F: TaskFn<Args, Fut = Fut>,
33 | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_size`
34 = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
35
36error[E0277]: task futures must resolve to `()` or `!`
37 --> tests/ui/return_impl_send.rs:4:4
38 |
393 | #[embassy_executor::task]
40 | ------------------------- required by a bound introduced by this call
414 | fn task() -> impl Send {}
42 | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`
43 |
44 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
45note: required by a bound in `task_pool_align`
46 --> src/lib.rs
47 |
48 | pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
49 | --------------- required by a bound in this function
50 | where
51 | F: TaskFn<Args, Fut = Fut>,
52 | ^^^^^^^^^ required by this bound in `task_pool_align`
53
54error[E0277]: task futures must resolve to `()` or `!`
55 --> tests/ui/return_impl_send.rs:3:1
56 |
573 | #[embassy_executor::task]
58 | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`
59 |
60 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
61note: required by a bound in `task_pool_align`
62 --> src/lib.rs
63 |
64 | pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
65 | --------------- required by a bound in this function
66 | where
67 | F: TaskFn<Args, Fut = Fut>,
68 | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_align`
69 = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
70
71error[E0277]: task futures must resolve to `()` or `!`
72 --> tests/ui/return_impl_send.rs:4:4
73 |
743 | #[embassy_executor::task]
75 | ------------------------- required by a bound introduced by this call
764 | fn task() -> impl Send {}
77 | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`
78 |
79 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
80note: required by a bound in `__task_pool_get`
81 --> tests/ui/return_impl_send.rs:3:1
82 |
833 | #[embassy_executor::task]
84 | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__task_pool_get`
85 = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
86
87error[E0277]: `impl Send` is not a future
88 --> tests/ui/return_impl_send.rs:3:1
89 |
903 | #[embassy_executor::task]
91 | ^^^^^^^^^^^^^^^^^^^^^^^^^ `impl Send` is not a future
92 |
93 = help: the trait `Future` is not implemented for `impl Send`
94note: required by a bound in `TaskPool::<F, N>::spawn`
95 --> src/raw/mod.rs
96 |
97 | impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
98 | ^^^^^^ required by this bound in `TaskPool::<F, N>::spawn`
99...
100 | pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> {
101 | ----- required by a bound in this associated function
102 = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
103
104error[E0277]: task futures must resolve to `()` or `!`
105 --> tests/ui/return_impl_send.rs:4:4
106 |
1073 | #[embassy_executor::task]
108 | ------------------------- required by a bound introduced by this call
1094 | fn task() -> impl Send {}
110 | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`
111 |
112 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
113note: required by a bound in `task_pool_new`
114 --> src/lib.rs
115 |
116 | pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE>
117 | ------------- required by a bound in this function
118 | where
119 | F: TaskFn<Args, Fut = Fut>,
120 | ^^^^^^^^^ required by this bound in `task_pool_new`
121
122error[E0277]: task futures must resolve to `()` or `!`
123 --> tests/ui/return_impl_send.rs:3:1
124 |
1253 | #[embassy_executor::task]
126 | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`
127 |
128 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
129note: required by a bound in `task_pool_new`
130 --> src/lib.rs
131 |
132 | pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE>
133 | ------------- required by a bound in this function
134 | where
135 | F: TaskFn<Args, Fut = Fut>,
136 | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_new`
137 = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/embassy-executor/tests/ui/return_impl_send_nightly.rs b/embassy-executor/tests/ui/return_impl_send_nightly.rs
new file mode 100644
index 000000000..6ddb0e722
--- /dev/null
+++ b/embassy-executor/tests/ui/return_impl_send_nightly.rs
@@ -0,0 +1,6 @@
1#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
2
3#[embassy_executor::task]
4fn task() -> impl Send {}
5
6fn main() {}
diff --git a/embassy-executor/tests/ui/return_impl_send_nightly.stderr b/embassy-executor/tests/ui/return_impl_send_nightly.stderr
new file mode 100644
index 000000000..de9ba6243
--- /dev/null
+++ b/embassy-executor/tests/ui/return_impl_send_nightly.stderr
@@ -0,0 +1,10 @@
1error[E0277]: `impl Send` is not a future
2 --> tests/ui/return_impl_send_nightly.rs:3:1
3 |
43 | #[embassy_executor::task]
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^
6 | |
7 | `impl Send` is not a future
8 | return type was inferred to be `impl Send` here
9 |
10 = help: the trait `Future` is not implemented for `impl Send`
diff --git a/embassy-executor/tests/ui/spawn_nonsend.rs b/embassy-executor/tests/ui/spawn_nonsend.rs
new file mode 100644
index 000000000..4c4cc7697
--- /dev/null
+++ b/embassy-executor/tests/ui/spawn_nonsend.rs
@@ -0,0 +1,16 @@
1#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
2
3use core::future::Future;
4
5use embassy_executor::SendSpawner;
6
7#[embassy_executor::task]
8async fn task(non_send: *mut ()) {
9 println!("{}", non_send as usize);
10}
11
12fn send_spawn(s: SendSpawner) {
13 s.spawn(task(core::ptr::null_mut())).unwrap();
14}
15
16fn main() {}
diff --git a/embassy-executor/tests/ui/spawn_nonsend.stderr b/embassy-executor/tests/ui/spawn_nonsend.stderr
new file mode 100644
index 000000000..2a06c8b94
--- /dev/null
+++ b/embassy-executor/tests/ui/spawn_nonsend.stderr
@@ -0,0 +1,41 @@
1warning: unused import: `core::future::Future`
2 --> tests/ui/spawn_nonsend.rs:3:5
3 |
43 | use core::future::Future;
5 | ^^^^^^^^^^^^^^^^^^^^
6 |
7 = note: `#[warn(unused_imports)]` on by default
8
9error[E0277]: `*mut ()` cannot be sent between threads safely
10 --> tests/ui/spawn_nonsend.rs:13:13
11 |
127 | #[embassy_executor::task]
13 | ------------------------- within this `impl Sized`
14...
1513 | s.spawn(task(core::ptr::null_mut())).unwrap();
16 | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*mut ()` cannot be sent between threads safely
17 | |
18 | required by a bound introduced by this call
19 |
20 = help: within `impl Sized`, the trait `Send` is not implemented for `*mut ()`
21note: required because it's used within this closure
22 --> tests/ui/spawn_nonsend.rs:7:1
23 |
247 | #[embassy_executor::task]
25 | ^^^^^^^^^^^^^^^^^^^^^^^^^
26note: required because it appears within the type `impl Sized`
27 --> src/raw/mod.rs
28 |
29 | pub unsafe fn _spawn_async_fn<FutFn>(&'static self, future: FutFn) -> SpawnToken<impl Sized>
30 | ^^^^^^^^^^
31note: required because it appears within the type `impl Sized`
32 --> tests/ui/spawn_nonsend.rs:7:1
33 |
347 | #[embassy_executor::task]
35 | ^^^^^^^^^^^^^^^^^^^^^^^^^
36note: required by a bound in `SendSpawner::spawn`
37 --> src/spawner.rs
38 |
39 | pub fn spawn<S: Send>(&self, token: SpawnToken<S>) -> Result<(), SpawnError> {
40 | ^^^^ required by this bound in `SendSpawner::spawn`
41 = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/embassy-futures/CHANGELOG.md b/embassy-futures/CHANGELOG.md
new file mode 100644
index 000000000..eb76cdc4a
--- /dev/null
+++ b/embassy-futures/CHANGELOG.md
@@ -0,0 +1,14 @@
1# Changelog for embassy-futures
2
3All notable changes to this project will be documented in this file.
4
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
8<!-- next-header -->
9## Unreleased - ReleaseDate
10
11- Preserve location information for `defmt` in `fmt` calls ([#3085](https://github.com/embassy-rs/embassy/pull/3085))
12- Fixed soundness issue in `select_slice` ([#3328](https://github.com/embassy-rs/embassy/pull/3328))
13- Added `select5` and `select6` ([#3430](https://github.com/embassy-rs/embassy/pull/3430))
14- Added `is_x` methods for all `EitherN` enum variants (#[3650](https://github.com/embassy-rs/embassy/pull/3650))
diff --git a/embassy-futures/release.toml b/embassy-futures/release.toml
new file mode 100644
index 000000000..fb6feaf21
--- /dev/null
+++ b/embassy-futures/release.toml
@@ -0,0 +1,5 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-hal-internal/Cargo.toml b/embassy-hal-internal/Cargo.toml
index cc360682e..b4c52ccfa 100644
--- a/embassy-hal-internal/Cargo.toml
+++ b/embassy-hal-internal/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "embassy-hal-internal" 2name = "embassy-hal-internal"
3version = "0.2.0" 3version = "0.3.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6description = "Internal implementation details for Embassy HALs. DO NOT USE DIRECTLY." 6description = "Internal implementation details for Embassy HALs. DO NOT USE DIRECTLY."
diff --git a/embassy-imxrt/Cargo.toml b/embassy-imxrt/Cargo.toml
index 49dc8089c..df4687043 100644
--- a/embassy-imxrt/Cargo.toml
+++ b/embassy-imxrt/Cargo.toml
@@ -67,8 +67,8 @@ embassy-sync = { version = "0.7.0", path = "../embassy-sync" }
67embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true } 67embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true }
68embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } 68embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true }
69embassy-time = { version = "0.4", path = "../embassy-time", optional = true } 69embassy-time = { version = "0.4", path = "../embassy-time", optional = true }
70embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } 70embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] }
71embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } 71embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal", default-features = false }
72embassy-futures = { version = "0.1.1", path = "../embassy-futures" } 72embassy-futures = { version = "0.1.1", path = "../embassy-futures" }
73 73
74defmt = { version = "1.0.1", optional = true } 74defmt = { version = "1.0.1", optional = true }
diff --git a/embassy-imxrt/src/lib.rs b/embassy-imxrt/src/lib.rs
index 5846afe5c..a3437c655 100644
--- a/embassy-imxrt/src/lib.rs
+++ b/embassy-imxrt/src/lib.rs
@@ -76,9 +76,11 @@ macro_rules! bind_interrupts {
76 #[allow(non_snake_case)] 76 #[allow(non_snake_case)]
77 #[no_mangle] 77 #[no_mangle]
78 unsafe extern "C" fn $irq() { 78 unsafe extern "C" fn $irq() {
79 $( 79 unsafe {
80 <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); 80 $(
81 )* 81 <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
82 )*
83 }
82 } 84 }
83 85
84 $( 86 $(
diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml
index 456d302af..61afcd76d 100644
--- a/embassy-mspm0/Cargo.toml
+++ b/embassy-mspm0/Cargo.toml
@@ -31,12 +31,15 @@ embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true }
31embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true, features = ["tick-hz-32_768"] } 31embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true, features = ["tick-hz-32_768"] }
32embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } 32embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true }
33embassy-futures = { version = "0.1.0", path = "../embassy-futures" } 33embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
34embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } 34embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] }
35embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } 35embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal", default-features = false }
36embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional = true } 36embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional = true }
37 37
38embedded-hal = { version = "1.0" } 38embedded-hal = { version = "1.0" }
39embedded-hal-nb = { version = "1.0" }
39embedded-hal-async = { version = "1.0" } 40embedded-hal-async = { version = "1.0" }
41embedded-io = "0.6.1"
42embedded-io-async = "0.6.1"
40 43
41defmt = { version = "1.0.1", optional = true } 44defmt = { version = "1.0.1", optional = true }
42fixed = "1.29" 45fixed = "1.29"
@@ -46,14 +49,15 @@ cortex-m = "0.7.6"
46critical-section = "1.2.0" 49critical-section = "1.2.0"
47 50
48# mspm0-metapac = { version = "" } 51# mspm0-metapac = { version = "" }
49mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-26a6f681eda4ef120e8cb614a1631727c848590f" } 52mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-235158ac2865d8aac3a1eceb2d62026eb12bf38f" }
50 53
51[build-dependencies] 54[build-dependencies]
52proc-macro2 = "1.0.94" 55proc-macro2 = "1.0.94"
53quote = "1.0.40" 56quote = "1.0.40"
57cfg_aliases = "0.2.1"
54 58
55# mspm0-metapac = { version = "", default-features = false, features = ["metadata"] } 59# mspm0-metapac = { version = "", default-features = false, features = ["metadata"] }
56mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-26a6f681eda4ef120e8cb614a1631727c848590f", default-features = false, features = ["metadata"] } 60mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-235158ac2865d8aac3a1eceb2d62026eb12bf38f", default-features = false, features = ["metadata"] }
57 61
58[features] 62[features]
59default = ["rt"] 63default = ["rt"]
diff --git a/embassy-mspm0/build.rs b/embassy-mspm0/build.rs
index 6cd62895b..b9ba3aecf 100644
--- a/embassy-mspm0/build.rs
+++ b/embassy-mspm0/build.rs
@@ -67,6 +67,7 @@ fn generate_code() {
67 g.extend(generate_peripheral_instances()); 67 g.extend(generate_peripheral_instances());
68 g.extend(generate_pin_trait_impls()); 68 g.extend(generate_pin_trait_impls());
69 g.extend(generate_groups()); 69 g.extend(generate_groups());
70 g.extend(generate_dma_channel_count());
70 71
71 let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); 72 let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
72 let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); 73 let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string();
@@ -209,6 +210,12 @@ fn generate_groups() -> TokenStream {
209 } 210 }
210} 211}
211 212
213fn generate_dma_channel_count() -> TokenStream {
214 let count = METADATA.dma_channels.len();
215
216 quote! { pub const DMA_CHANNELS: usize = #count; }
217}
218
212#[derive(Debug, Clone)] 219#[derive(Debug, Clone)]
213struct Singleton { 220struct Singleton {
214 name: String, 221 name: String,
@@ -543,8 +550,6 @@ fn generate_peripheral_instances() -> TokenStream {
543 for peripheral in METADATA.peripherals { 550 for peripheral in METADATA.peripherals {
544 let peri = format_ident!("{}", peripheral.name); 551 let peri = format_ident!("{}", peripheral.name);
545 552
546 // Will be filled in when uart implementation is finished
547 let _ = peri;
548 let tokens = match peripheral.kind { 553 let tokens = match peripheral.kind {
549 "uart" => Some(quote! { impl_uart_instance!(#peri); }), 554 "uart" => Some(quote! { impl_uart_instance!(#peri); }),
550 _ => None, 555 _ => None,
@@ -555,6 +560,18 @@ fn generate_peripheral_instances() -> TokenStream {
555 } 560 }
556 } 561 }
557 562
563 // DMA channels
564 for dma_channel in METADATA.dma_channels.iter() {
565 let peri = format_ident!("DMA_CH{}", dma_channel.number);
566 let num = dma_channel.number;
567
568 if dma_channel.full {
569 impls.push(quote! { impl_full_dma_channel!(#peri, #num); });
570 } else {
571 impls.push(quote! { impl_dma_channel!(#peri, #num); });
572 }
573 }
574
558 quote! { 575 quote! {
559 #(#impls)* 576 #(#impls)*
560 } 577 }
diff --git a/embassy-mspm0/src/dma.rs b/embassy-mspm0/src/dma.rs
new file mode 100644
index 000000000..66b79709c
--- /dev/null
+++ b/embassy-mspm0/src/dma.rs
@@ -0,0 +1,626 @@
1//! Direct Memory Access (DMA)
2
3#![macro_use]
4
5use core::future::Future;
6use core::mem;
7use core::pin::Pin;
8use core::sync::atomic::{compiler_fence, Ordering};
9use core::task::{Context, Poll};
10
11use critical_section::CriticalSection;
12use embassy_hal_internal::interrupt::InterruptExt;
13use embassy_hal_internal::{impl_peripheral, PeripheralType};
14use embassy_sync::waitqueue::AtomicWaker;
15use mspm0_metapac::common::{Reg, RW};
16use mspm0_metapac::dma::regs;
17use mspm0_metapac::dma::vals::{self, Autoen, Em, Incr, Preirq, Wdth};
18
19use crate::{interrupt, pac, Peri};
20
21/// The burst size of a DMA transfer.
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum BurstSize {
24 /// The whole block transfer is completed in one transfer without interruption.
25 Complete,
26
27 /// The burst size is 8, after 9 transfers the block transfer is interrupted and the priority
28 /// is reevaluated.
29 _8,
30
31 /// The burst size is 16, after 17 transfers the block transfer is interrupted and the priority
32 /// is reevaluated.
33 _16,
34
35 /// The burst size is 32, after 32 transfers the block transfer is interrupted and the priority
36 /// is reevaluated.
37 _32,
38}
39
40/// DMA channel.
41#[allow(private_bounds)]
42pub trait Channel: Into<AnyChannel> + PeripheralType {}
43
44/// Full DMA channel.
45#[allow(private_bounds)]
46pub trait FullChannel: Channel + Into<AnyFullChannel> {}
47
48/// Type-erased DMA channel.
49pub struct AnyChannel {
50 pub(crate) id: u8,
51}
52impl_peripheral!(AnyChannel);
53
54impl SealedChannel for AnyChannel {
55 fn id(&self) -> u8 {
56 self.id
57 }
58}
59impl Channel for AnyChannel {}
60
61/// Type-erased full DMA channel.
62pub struct AnyFullChannel {
63 pub(crate) id: u8,
64}
65impl_peripheral!(AnyFullChannel);
66
67impl SealedChannel for AnyFullChannel {
68 fn id(&self) -> u8 {
69 self.id
70 }
71}
72impl Channel for AnyFullChannel {}
73impl FullChannel for AnyFullChannel {}
74
75impl From<AnyFullChannel> for AnyChannel {
76 fn from(value: AnyFullChannel) -> Self {
77 Self { id: value.id }
78 }
79}
80
81#[allow(private_bounds)]
82pub trait Word: SealedWord {
83 /// Size in bytes for the width.
84 fn size() -> isize;
85}
86
87impl SealedWord for u8 {
88 fn width() -> vals::Wdth {
89 vals::Wdth::BYTE
90 }
91}
92impl Word for u8 {
93 fn size() -> isize {
94 1
95 }
96}
97
98impl SealedWord for u16 {
99 fn width() -> vals::Wdth {
100 vals::Wdth::HALF
101 }
102}
103impl Word for u16 {
104 fn size() -> isize {
105 2
106 }
107}
108
109impl SealedWord for u32 {
110 fn width() -> vals::Wdth {
111 vals::Wdth::WORD
112 }
113}
114impl Word for u32 {
115 fn size() -> isize {
116 4
117 }
118}
119
120impl SealedWord for u64 {
121 fn width() -> vals::Wdth {
122 vals::Wdth::LONG
123 }
124}
125impl Word for u64 {
126 fn size() -> isize {
127 8
128 }
129}
130
131// TODO: u128 (LONGLONG) support. G350x does support it, but other parts do not such as C110x. More metadata is
132// needed to properly enable this.
133// impl SealedWord for u128 {
134// fn width() -> vals::Wdth {
135// vals::Wdth::LONGLONG
136// }
137// }
138// impl Word for u128 {
139// fn size() -> isize {
140// 16
141// }
142// }
143
144#[derive(Debug, Copy, Clone, PartialEq, Eq)]
145#[cfg_attr(feature = "defmt", derive(defmt::Format))]
146pub enum Error {
147 /// The DMA transfer is too large.
148 ///
149 /// The hardware limits the DMA to 16384 transfers per channel at a time. This means that transferring
150 /// 16384 `u8` and 16384 `u64` are equivalent, since the DMA must copy 16384 values.
151 TooManyTransfers,
152}
153
154/// DMA transfer mode for basic channels.
155#[derive(Debug, Copy, Clone, PartialEq, Eq)]
156#[cfg_attr(feature = "defmt", derive(defmt::Format))]
157pub enum TransferMode {
158 /// Each DMA trigger will transfer a single value.
159 Single,
160
161 /// Each DMA trigger will transfer the complete block with one trigger.
162 Block,
163}
164
165/// DMA transfer options.
166#[derive(Debug, Copy, Clone, PartialEq, Eq)]
167#[cfg_attr(feature = "defmt", derive(defmt::Format))]
168#[non_exhaustive]
169pub struct TransferOptions {
170 /// DMA transfer mode.
171 pub mode: TransferMode,
172 // TODO: Read and write stride.
173}
174
175impl Default for TransferOptions {
176 fn default() -> Self {
177 Self {
178 mode: TransferMode::Single,
179 }
180 }
181}
182
183/// DMA transfer.
184#[must_use = "futures do nothing unless you `.await` or poll them"]
185pub struct Transfer<'a> {
186 channel: Peri<'a, AnyChannel>,
187}
188
189impl<'a> Transfer<'a> {
190 /// Software trigger source.
191 ///
192 /// Using this trigger source means that a transfer will start immediately rather than waiting for
193 /// a hardware event. This can be useful if you want to do a DMA accelerated memcpy.
194 pub const SOFTWARE_TRIGGER: u8 = 0;
195
196 /// Create a new read DMA transfer.
197 pub unsafe fn new_read<SW: Word, DW: Word>(
198 channel: Peri<'a, impl Channel>,
199 trigger_source: u8,
200 src: *mut SW,
201 dst: &'a mut [DW],
202 options: TransferOptions,
203 ) -> Result<Self, Error> {
204 Self::new_read_raw(channel, trigger_source, src, dst, options)
205 }
206
207 /// Create a new read DMA transfer, using raw pointers.
208 pub unsafe fn new_read_raw<SW: Word, DW: Word>(
209 channel: Peri<'a, impl Channel>,
210 trigger_source: u8,
211 src: *mut SW,
212 dst: *mut [DW],
213 options: TransferOptions,
214 ) -> Result<Self, Error> {
215 verify_transfer::<DW>(dst)?;
216
217 let channel = channel.into();
218 channel.configure(
219 trigger_source,
220 src.cast(),
221 SW::width(),
222 dst.cast(),
223 DW::width(),
224 dst.len() as u16,
225 false,
226 true,
227 options,
228 );
229 channel.start();
230
231 Ok(Self { channel })
232 }
233
234 /// Create a new write DMA transfer.
235 pub unsafe fn new_write<SW: Word, DW: Word>(
236 channel: Peri<'a, impl Channel>,
237 trigger_source: u8,
238 src: &'a [SW],
239 dst: *mut DW,
240 options: TransferOptions,
241 ) -> Result<Self, Error> {
242 Self::new_write_raw(channel, trigger_source, src, dst, options)
243 }
244
245 /// Create a new write DMA transfer, using raw pointers.
246 pub unsafe fn new_write_raw<SW: Word, DW: Word>(
247 channel: Peri<'a, impl Channel>,
248 trigger_source: u8,
249 src: *const [SW],
250 dst: *mut DW,
251 options: TransferOptions,
252 ) -> Result<Self, Error> {
253 verify_transfer::<SW>(src)?;
254
255 let channel = channel.into();
256 channel.configure(
257 trigger_source,
258 src.cast(),
259 SW::width(),
260 dst.cast(),
261 DW::width(),
262 src.len() as u16,
263 true,
264 false,
265 options,
266 );
267 channel.start();
268
269 Ok(Self { channel })
270 }
271
272 // TODO: Copy between slices.
273
274 /// Request the transfer to resume.
275 pub fn resume(&mut self) {
276 self.channel.resume();
277 }
278
279 /// Request the transfer to pause, keeping the existing configuration for this channel.
280 /// To restart the transfer, call [`start`](Self::start) again.
281 ///
282 /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
283 pub fn request_pause(&mut self) {
284 self.channel.request_pause();
285 }
286
287 /// Return whether this transfer is still running.
288 ///
289 /// If this returns [`false`], it can be because either the transfer finished, or
290 /// it was requested to stop early with [`request_stop`].
291 pub fn is_running(&mut self) -> bool {
292 self.channel.is_running()
293 }
294
295 /// Blocking wait until the transfer finishes.
296 pub fn blocking_wait(mut self) {
297 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
298 compiler_fence(Ordering::SeqCst);
299
300 while self.is_running() {}
301
302 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
303 compiler_fence(Ordering::SeqCst);
304
305 // Prevent drop from being called since we ran to completion (drop will try to pause).
306 mem::forget(self);
307 }
308}
309
310impl<'a> Unpin for Transfer<'a> {}
311impl<'a> Future for Transfer<'a> {
312 type Output = ();
313
314 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
315 let state: &ChannelState = &STATE[self.channel.id as usize];
316
317 state.waker.register(cx.waker());
318
319 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
320 compiler_fence(Ordering::SeqCst);
321
322 if self.channel.is_running() {
323 Poll::Pending
324 } else {
325 Poll::Ready(())
326 }
327 }
328}
329
330impl<'a> Drop for Transfer<'a> {
331 fn drop(&mut self) {
332 self.channel.request_pause();
333 while self.is_running() {}
334
335 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
336 compiler_fence(Ordering::SeqCst);
337 }
338}
339
340// impl details
341
342fn verify_transfer<W: Word>(ptr: *const [W]) -> Result<(), Error> {
343 if ptr.len() > (u16::MAX as usize) {
344 return Err(Error::TooManyTransfers);
345 }
346
347 // TODO: Stride checks
348
349 Ok(())
350}
351
352fn convert_burst_size(value: BurstSize) -> vals::Burstsz {
353 match value {
354 BurstSize::Complete => vals::Burstsz::INFINITI,
355 BurstSize::_8 => vals::Burstsz::BURST_8,
356 BurstSize::_16 => vals::Burstsz::BURST_16,
357 BurstSize::_32 => vals::Burstsz::BURST_32,
358 }
359}
360
361fn convert_mode(mode: TransferMode) -> vals::Tm {
362 match mode {
363 TransferMode::Single => vals::Tm::SINGLE,
364 TransferMode::Block => vals::Tm::BLOCK,
365 }
366}
367
368const CHANNEL_COUNT: usize = crate::_generated::DMA_CHANNELS;
369static STATE: [ChannelState; CHANNEL_COUNT] = [const { ChannelState::new() }; CHANNEL_COUNT];
370
371struct ChannelState {
372 waker: AtomicWaker,
373}
374
375impl ChannelState {
376 const fn new() -> Self {
377 Self {
378 waker: AtomicWaker::new(),
379 }
380 }
381}
382
383/// SAFETY: Must only be called once.
384///
385/// Changing the burst size mid transfer may have some odd behavior.
386pub(crate) unsafe fn init(_cs: CriticalSection, burst_size: BurstSize, round_robin: bool) {
387 pac::DMA.prio().modify(|prio| {
388 prio.set_burstsz(convert_burst_size(burst_size));
389 prio.set_roundrobin(round_robin);
390 });
391 pac::DMA.int_event(0).imask().modify(|w| {
392 w.set_dataerr(true);
393 w.set_addrerr(true);
394 });
395
396 interrupt::DMA.enable();
397}
398
399pub(crate) trait SealedWord {
400 fn width() -> vals::Wdth;
401}
402
403pub(crate) trait SealedChannel {
404 fn id(&self) -> u8;
405
406 #[inline]
407 fn tctl(&self) -> Reg<regs::Tctl, RW> {
408 pac::DMA.trig(self.id() as usize).tctl()
409 }
410
411 #[inline]
412 fn ctl(&self) -> Reg<regs::Ctl, RW> {
413 pac::DMA.chan(self.id() as usize).ctl()
414 }
415
416 #[inline]
417 fn sa(&self) -> Reg<u32, RW> {
418 pac::DMA.chan(self.id() as usize).sa()
419 }
420
421 #[inline]
422 fn da(&self) -> Reg<u32, RW> {
423 pac::DMA.chan(self.id() as usize).da()
424 }
425
426 #[inline]
427 fn sz(&self) -> Reg<regs::Sz, RW> {
428 pac::DMA.chan(self.id() as usize).sz()
429 }
430
431 #[inline]
432 fn mask_interrupt(&self, enable: bool) {
433 // Enabling interrupts is an RMW operation.
434 critical_section::with(|_cs| {
435 pac::DMA.int_event(0).imask().modify(|w| {
436 w.set_ch(self.id() as usize, enable);
437 });
438 })
439 }
440
441 /// # Safety
442 ///
443 /// - `src` must be valid for the lifetime of the transfer.
444 /// - `dst` must be valid for the lifetime of the transfer.
445 unsafe fn configure(
446 &self,
447 trigger_sel: u8,
448 src: *const u32,
449 src_wdth: Wdth,
450 dst: *const u32,
451 dst_wdth: Wdth,
452 transfer_count: u16,
453 increment_src: bool,
454 increment_dst: bool,
455 options: TransferOptions,
456 ) {
457 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
458 compiler_fence(Ordering::SeqCst);
459
460 self.ctl().modify(|w| {
461 // SLAU 5.2.5:
462 // "The DMATSEL bits should be modified only when the DMACTLx.DMAEN bit is
463 // 0; otherwise, unpredictable DMA triggers can occur."
464 //
465 // We also want to stop any transfers before setup.
466 w.set_en(false);
467 w.set_req(false);
468
469 // Not every part supports auto enable, so force its value to 0.
470 w.set_autoen(Autoen::NONE);
471 w.set_preirq(Preirq::PREIRQ_DISABLE);
472 w.set_srcwdth(src_wdth);
473 w.set_dstwdth(dst_wdth);
474 w.set_srcincr(if increment_src {
475 Incr::INCREMENT
476 } else {
477 Incr::UNCHANGED
478 });
479 w.set_dstincr(if increment_dst {
480 Incr::INCREMENT
481 } else {
482 Incr::UNCHANGED
483 });
484
485 w.set_em(Em::NORMAL);
486 // Single and block will clear the enable bit when the transfers finish.
487 w.set_tm(convert_mode(options.mode));
488 });
489
490 self.tctl().write(|w| {
491 w.set_tsel(trigger_sel);
492 // Basic channels do not implement cross triggering.
493 w.set_tint(vals::Tint::EXTERNAL);
494 });
495
496 self.sz().write(|w| {
497 w.set_size(transfer_count);
498 });
499
500 self.sa().write_value(src as u32);
501 self.da().write_value(dst as u32);
502
503 // Enable the channel.
504 self.ctl().modify(|w| {
505 // FIXME: Why did putting set_req later fix some transfers
506 w.set_en(true);
507 w.set_req(true);
508 });
509 }
510
511 fn start(&self) {
512 self.mask_interrupt(true);
513
514 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
515 compiler_fence(Ordering::SeqCst);
516
517 // Request the DMA transfer to start.
518 self.ctl().modify(|w| {
519 w.set_req(true);
520 });
521 }
522
523 fn resume(&self) {
524 self.mask_interrupt(true);
525
526 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
527 compiler_fence(Ordering::SeqCst);
528
529 self.ctl().modify(|w| {
530 // w.set_en(true);
531 w.set_req(true);
532 });
533 }
534
535 fn request_pause(&self) {
536 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
537 compiler_fence(Ordering::SeqCst);
538
539 // Stop the transfer.
540 //
541 // SLAU846 5.2.6:
542 // "A DMA block transfer in progress can be stopped by clearing the DMAEN bit"
543 self.ctl().modify(|w| {
544 // w.set_en(false);
545 w.set_req(false);
546 });
547 }
548
549 fn is_running(&self) -> bool {
550 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
551 compiler_fence(Ordering::SeqCst);
552
553 let ctl = self.ctl().read();
554
555 // Is the transfer requested?
556 ctl.req()
557 // Is the channel enabled?
558 && ctl.en()
559 }
560}
561
562macro_rules! impl_dma_channel {
563 ($instance: ident, $num: expr) => {
564 impl crate::dma::SealedChannel for crate::peripherals::$instance {
565 fn id(&self) -> u8 {
566 $num
567 }
568 }
569
570 impl From<crate::peripherals::$instance> for crate::dma::AnyChannel {
571 fn from(value: crate::peripherals::$instance) -> Self {
572 use crate::dma::SealedChannel;
573
574 Self { id: value.id() }
575 }
576 }
577
578 impl crate::dma::Channel for crate::peripherals::$instance {}
579 };
580}
581
582// C1104 has no full DMA channels.
583#[allow(unused_macros)]
584macro_rules! impl_full_dma_channel {
585 ($instance: ident, $num: expr) => {
586 impl_dma_channel!($instance, $num);
587
588 impl From<crate::peripherals::$instance> for crate::dma::AnyFullChannel {
589 fn from(value: crate::peripherals::$instance) -> Self {
590 use crate::dma::SealedChannel;
591
592 Self { id: value.id() }
593 }
594 }
595
596 impl crate::dma::FullChannel for crate::peripherals::$instance {}
597 };
598}
599
600#[cfg(feature = "rt")]
601#[interrupt]
602fn DMA() {
603 use crate::BitIter;
604
605 let events = pac::DMA.int_event(0);
606 let mis = events.mis().read();
607
608 // TODO: Handle DATAERR and ADDRERR? However we do not know which channel causes an error.
609 if mis.dataerr() {
610 panic!("DMA data error");
611 } else if mis.addrerr() {
612 panic!("DMA address error")
613 }
614
615 // Ignore preirq interrupts (values greater than 16).
616 for i in BitIter(mis.0 & 0x0000_FFFF) {
617 if let Some(state) = STATE.get(i as usize) {
618 state.waker.wake();
619
620 // Notify the future that the counter size hit zero
621 events.imask().modify(|w| {
622 w.set_ch(i as usize, false);
623 });
624 }
625 }
626}
diff --git a/embassy-mspm0/src/gpio.rs b/embassy-mspm0/src/gpio.rs
index 738d51928..f77848888 100644
--- a/embassy-mspm0/src/gpio.rs
+++ b/embassy-mspm0/src/gpio.rs
@@ -223,13 +223,13 @@ impl<'d> Flex<'d> {
223 /// Get whether the pin input level is high. 223 /// Get whether the pin input level is high.
224 #[inline] 224 #[inline]
225 pub fn is_high(&self) -> bool { 225 pub fn is_high(&self) -> bool {
226 !self.is_low() 226 self.pin.block().din31_0().read().dio(self.pin.bit_index())
227 } 227 }
228 228
229 /// Get whether the pin input level is low. 229 /// Get whether the pin input level is low.
230 #[inline] 230 #[inline]
231 pub fn is_low(&self) -> bool { 231 pub fn is_low(&self) -> bool {
232 self.pin.block().din31_0().read().dio(self.pin.bit_index()) 232 !self.is_high()
233 } 233 }
234 234
235 /// Returns current pin level 235 /// Returns current pin level
@@ -271,22 +271,22 @@ impl<'d> Flex<'d> {
271 } 271 }
272 } 272 }
273 273
274 /// Get the current pin input level. 274 /// Get the current pin output level.
275 #[inline] 275 #[inline]
276 pub fn get_output_level(&self) -> Level { 276 pub fn get_output_level(&self) -> Level {
277 self.is_high().into() 277 self.is_set_high().into()
278 } 278 }
279 279
280 /// Is the output level high? 280 /// Is the output level high?
281 #[inline] 281 #[inline]
282 pub fn is_set_high(&self) -> bool { 282 pub fn is_set_high(&self) -> bool {
283 !self.is_set_low() 283 self.pin.block().dout31_0().read().dio(self.pin.bit_index())
284 } 284 }
285 285
286 /// Is the output level low? 286 /// Is the output level low?
287 #[inline] 287 #[inline]
288 pub fn is_set_low(&self) -> bool { 288 pub fn is_set_low(&self) -> bool {
289 (self.pin.block().dout31_0().read().0 & self.pin.bit_index() as u32) == 0 289 !self.is_set_high()
290 } 290 }
291 291
292 /// Wait until the pin is high. If it is already high, return immediately. 292 /// Wait until the pin is high. If it is already high, return immediately.
@@ -1090,7 +1090,9 @@ pub(crate) fn init(gpio: gpio::Gpio) {
1090 1090
1091#[cfg(feature = "rt")] 1091#[cfg(feature = "rt")]
1092fn irq_handler(gpio: gpio::Gpio, wakers: &[AtomicWaker; 32]) { 1092fn irq_handler(gpio: gpio::Gpio, wakers: &[AtomicWaker; 32]) {
1093 use crate::BitIter;
1093 // Only consider pins which have interrupts unmasked. 1094 // Only consider pins which have interrupts unmasked.
1095
1094 let bits = gpio.cpu_int().mis().read().0; 1096 let bits = gpio.cpu_int().mis().read().0;
1095 1097
1096 for i in BitIter(bits) { 1098 for i in BitIter(bits) {
@@ -1103,22 +1105,6 @@ fn irq_handler(gpio: gpio::Gpio, wakers: &[AtomicWaker; 32]) {
1103 } 1105 }
1104} 1106}
1105 1107
1106struct BitIter(u32);
1107
1108impl Iterator for BitIter {
1109 type Item = u32;
1110
1111 fn next(&mut self) -> Option<Self::Item> {
1112 match self.0.trailing_zeros() {
1113 32 => None,
1114 b => {
1115 self.0 &= !(1 << b);
1116 Some(b)
1117 }
1118 }
1119 }
1120}
1121
1122// C110x and L110x have a dedicated interrupts just for GPIOA. 1108// C110x and L110x have a dedicated interrupts just for GPIOA.
1123// 1109//
1124// These chips do not have a GROUP1 interrupt. 1110// These chips do not have a GROUP1 interrupt.
diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs
index 7ff60e946..629ebfa1f 100644
--- a/embassy-mspm0/src/lib.rs
+++ b/embassy-mspm0/src/lib.rs
@@ -13,6 +13,7 @@ pub(crate) mod fmt;
13// This must be declared early as well for 13// This must be declared early as well for
14mod macros; 14mod macros;
15 15
16pub mod dma;
16pub mod gpio; 17pub mod gpio;
17pub mod timer; 18pub mod timer;
18pub mod uart; 19pub mod uart;
@@ -59,22 +60,108 @@ pub(crate) use mspm0_metapac as pac;
59 60
60pub use crate::_generated::interrupt; 61pub use crate::_generated::interrupt;
61 62
63/// Macro to bind interrupts to handlers.
64///
65/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
66/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
67/// prove at compile-time that the right interrupts have been bound.
68///
69/// Example of how to bind one interrupt:
70///
71/// ```rust,ignore
72/// use embassy_nrf::{bind_interrupts, spim, peripherals};
73///
74/// bind_interrupts!(
75/// /// Binds the SPIM3 interrupt.
76/// struct Irqs {
77/// SPIM3 => spim::InterruptHandler<peripherals::SPI3>;
78/// }
79/// );
80/// ```
81///
82/// Example of how to bind multiple interrupts in a single macro invocation:
83///
84/// ```rust,ignore
85/// use embassy_nrf::{bind_interrupts, spim, twim, peripherals};
86///
87/// bind_interrupts!(struct Irqs {
88/// SPIM3 => spim::InterruptHandler<peripherals::SPI3>;
89/// TWISPI0 => twim::InterruptHandler<peripherals::TWISPI0>;
90/// });
91/// ```
92
93// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.
94#[macro_export]
95macro_rules! bind_interrupts {
96 ($(#[$attr:meta])* $vis:vis struct $name:ident {
97 $(
98 $(#[cfg($cond_irq:meta)])?
99 $irq:ident => $(
100 $(#[cfg($cond_handler:meta)])?
101 $handler:ty
102 ),*;
103 )*
104 }) => {
105 #[derive(Copy, Clone)]
106 $(#[$attr])*
107 $vis struct $name;
108
109 $(
110 #[allow(non_snake_case)]
111 #[no_mangle]
112 $(#[cfg($cond_irq)])?
113 unsafe extern "C" fn $irq() {
114 unsafe {
115 $(
116 $(#[cfg($cond_handler)])?
117 <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
118
119 )*
120 }
121 }
122
123 $(#[cfg($cond_irq)])?
124 $crate::bind_interrupts!(@inner
125 $(
126 $(#[cfg($cond_handler)])?
127 unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}
128 )*
129 );
130 )*
131 };
132 (@inner $($t:tt)*) => {
133 $($t)*
134 }
135}
136
62/// `embassy-mspm0` global configuration. 137/// `embassy-mspm0` global configuration.
63#[non_exhaustive] 138#[non_exhaustive]
64#[derive(Clone, Copy)] 139#[derive(Clone, Copy)]
65pub struct Config { 140pub struct Config {
66 // TODO 141 // TODO: OSC configuration.
142 /// The size of DMA block transfer burst.
143 ///
144 /// If this is set to a value
145 pub dma_burst_size: dma::BurstSize,
146
147 /// Whether the DMA channels are used in a fixed priority or a round robin fashion.
148 ///
149 /// If [`false`], the DMA priorities are fixed.
150 ///
151 /// If [`true`], after a channel finishes a transfer it becomes the lowest priority.
152 pub dma_round_robin: bool,
67} 153}
68 154
69impl Default for Config { 155impl Default for Config {
70 fn default() -> Self { 156 fn default() -> Self {
71 Self { 157 Self {
72 // TODO 158 dma_burst_size: dma::BurstSize::Complete,
159 dma_round_robin: false,
73 } 160 }
74 } 161 }
75} 162}
76 163
77pub fn init(_config: Config) -> Peripherals { 164pub fn init(config: Config) -> Peripherals {
78 critical_section::with(|cs| { 165 critical_section::with(|cs| {
79 let peripherals = Peripherals::take_with_cs(cs); 166 let peripherals = Peripherals::take_with_cs(cs);
80 167
@@ -112,9 +199,33 @@ pub fn init(_config: Config) -> Peripherals {
112 crate::interrupt::typelevel::GPIOA::enable(); 199 crate::interrupt::typelevel::GPIOA::enable();
113 } 200 }
114 201
202 // SAFETY: Peripherals::take_with_cs will only be run once or panic.
203 unsafe { dma::init(cs, config.dma_burst_size, config.dma_round_robin) };
204
115 #[cfg(feature = "_time-driver")] 205 #[cfg(feature = "_time-driver")]
116 time_driver::init(cs); 206 time_driver::init(cs);
117 207
118 peripherals 208 peripherals
119 }) 209 })
120} 210}
211
212pub(crate) mod sealed {
213 #[allow(dead_code)]
214 pub trait Sealed {}
215}
216
217struct BitIter(u32);
218
219impl Iterator for BitIter {
220 type Item = u32;
221
222 fn next(&mut self) -> Option<Self::Item> {
223 match self.0.trailing_zeros() {
224 32 => None,
225 b => {
226 self.0 &= !(1 << b);
227 Some(b)
228 }
229 }
230 }
231}
diff --git a/embassy-mspm0/src/uart/buffered.rs b/embassy-mspm0/src/uart/buffered.rs
new file mode 100644
index 000000000..cbc0b6c80
--- /dev/null
+++ b/embassy-mspm0/src/uart/buffered.rs
@@ -0,0 +1,1060 @@
1use core::future::{poll_fn, Future};
2use core::marker::PhantomData;
3use core::slice;
4use core::sync::atomic::{AtomicU8, Ordering};
5use core::task::Poll;
6
7use embassy_embedded_hal::SetConfig;
8use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
9use embassy_hal_internal::interrupt::InterruptExt;
10use embassy_sync::waitqueue::AtomicWaker;
11use embedded_hal_nb::nb;
12
13use crate::gpio::{AnyPin, SealedPin};
14use crate::interrupt::typelevel::Binding;
15use crate::pac::uart::Uart as Regs;
16use crate::uart::{Config, ConfigError, CtsPin, Error, Info, Instance, RtsPin, RxPin, State, TxPin};
17use crate::{interrupt, Peri};
18
19/// Interrupt handler.
20pub struct BufferedInterruptHandler<T: Instance> {
21 _uart: PhantomData<T>,
22}
23
24impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for BufferedInterruptHandler<T> {
25 unsafe fn on_interrupt() {
26 on_interrupt(T::info().regs, T::buffered_state())
27 }
28}
29
30/// Bidirectional buffered UART which acts as a combination of [`BufferedUartTx`] and [`BufferedUartRx`].
31pub struct BufferedUart<'d> {
32 rx: BufferedUartRx<'d>,
33 tx: BufferedUartTx<'d>,
34}
35
36impl SetConfig for BufferedUart<'_> {
37 type Config = Config;
38 type ConfigError = ConfigError;
39
40 fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
41 self.set_config(config)
42 }
43}
44
45impl<'d> BufferedUart<'d> {
46 /// Create a new bidirectional buffered UART.
47 pub fn new<T: Instance>(
48 uart: Peri<'d, T>,
49 tx: Peri<'d, impl TxPin<T>>,
50 rx: Peri<'d, impl RxPin<T>>,
51 _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
52 tx_buffer: &'d mut [u8],
53 rx_buffer: &'d mut [u8],
54 config: Config,
55 ) -> Result<Self, ConfigError> {
56 Self::new_inner(
57 uart,
58 new_pin!(rx, config.rx_pf()),
59 new_pin!(tx, config.tx_pf()),
60 None,
61 None,
62 tx_buffer,
63 rx_buffer,
64 config,
65 )
66 }
67
68 /// Create a new bidirectional buffered UART with request-to-send and clear-to-send pins
69 pub fn new_with_rtscts<T: Instance>(
70 uart: Peri<'d, T>,
71 tx: Peri<'d, impl TxPin<T>>,
72 rx: Peri<'d, impl RxPin<T>>,
73 rts: Peri<'d, impl RtsPin<T>>,
74 cts: Peri<'d, impl CtsPin<T>>,
75 _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
76 tx_buffer: &'d mut [u8],
77 rx_buffer: &'d mut [u8],
78 config: Config,
79 ) -> Result<Self, ConfigError> {
80 Self::new_inner(
81 uart,
82 new_pin!(rx, config.rx_pf()),
83 new_pin!(tx, config.tx_pf()),
84 new_pin!(rts, config.rts_pf()),
85 new_pin!(cts, config.cts_pf()),
86 tx_buffer,
87 rx_buffer,
88 config,
89 )
90 }
91
92 /// Reconfigure the driver
93 pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
94 self.tx.set_config(config)?;
95 self.rx.set_config(config)
96 }
97
98 /// Set baudrate
99 pub fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {
100 self.rx.set_baudrate(baudrate)
101 }
102
103 /// Write to UART TX buffer, blocking execution until done.
104 pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<usize, Error> {
105 self.tx.blocking_write(buffer)
106 }
107
108 /// Flush UART TX buffer, blocking execution until done.
109 pub fn blocking_flush(&mut self) -> Result<(), Error> {
110 self.tx.blocking_flush()
111 }
112
113 /// Check if UART is busy.
114 pub fn busy(&self) -> bool {
115 self.tx.busy()
116 }
117
118 /// Read from UART RX buffer, blocking execution until done.
119 pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
120 self.rx.blocking_read(buffer)
121 }
122
123 /// Send break character.
124 pub fn send_break(&mut self) {
125 self.tx.send_break()
126 }
127
128 /// Split into separate RX and TX handles.
129 pub fn split(self) -> (BufferedUartTx<'d>, BufferedUartRx<'d>) {
130 (self.tx, self.rx)
131 }
132
133 /// Split into separate RX and TX handles.
134 pub fn split_ref(&mut self) -> (BufferedUartTx<'_>, BufferedUartRx<'_>) {
135 (
136 BufferedUartTx {
137 info: self.tx.info,
138 state: self.tx.state,
139 tx: self.tx.tx.as_mut().map(Peri::reborrow),
140 cts: self.tx.cts.as_mut().map(Peri::reborrow),
141 reborrowed: true,
142 },
143 BufferedUartRx {
144 info: self.rx.info,
145 state: self.rx.state,
146 rx: self.rx.rx.as_mut().map(Peri::reborrow),
147 rts: self.rx.rts.as_mut().map(Peri::reborrow),
148 reborrowed: true,
149 },
150 )
151 }
152}
153
154/// Rx-only buffered UART.
155///
156/// Can be obtained from [`BufferedUart::split`], or can be constructed independently,
157/// if you do not need the transmitting half of the driver.
158pub struct BufferedUartRx<'d> {
159 info: &'static Info,
160 state: &'static BufferedState,
161 rx: Option<Peri<'d, AnyPin>>,
162 rts: Option<Peri<'d, AnyPin>>,
163 reborrowed: bool,
164}
165
166impl SetConfig for BufferedUartRx<'_> {
167 type Config = Config;
168 type ConfigError = ConfigError;
169
170 fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
171 self.set_config(config)
172 }
173}
174
175impl<'d> BufferedUartRx<'d> {
176 /// Create a new rx-only buffered UART with no hardware flow control.
177 ///
178 /// Useful if you only want Uart Rx. It saves 1 pin.
179 pub fn new<T: Instance>(
180 uart: Peri<'d, T>,
181 rx: Peri<'d, impl RxPin<T>>,
182 _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
183 rx_buffer: &'d mut [u8],
184 config: Config,
185 ) -> Result<Self, ConfigError> {
186 Self::new_inner(uart, new_pin!(rx, config.rx_pf()), None, rx_buffer, config)
187 }
188
189 /// Create a new rx-only buffered UART with a request-to-send pin
190 pub fn new_with_rts<T: Instance>(
191 uart: Peri<'d, T>,
192 rx: Peri<'d, impl RxPin<T>>,
193 rts: Peri<'d, impl RtsPin<T>>,
194 _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
195 rx_buffer: &'d mut [u8],
196 config: Config,
197 ) -> Result<Self, ConfigError> {
198 Self::new_inner(
199 uart,
200 new_pin!(rx, config.rx_pf()),
201 new_pin!(rts, config.rts_pf()),
202 rx_buffer,
203 config,
204 )
205 }
206
207 /// Reconfigure the driver
208 pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
209 if let Some(ref rx) = self.rx {
210 rx.update_pf(config.rx_pf());
211 }
212
213 if let Some(ref rts) = self.rts {
214 rts.update_pf(config.rts_pf());
215 }
216
217 super::reconfigure(&self.info, &self.state.state, config)
218 }
219
220 /// Set baudrate
221 pub fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {
222 super::set_baudrate(&self.info, self.state.state.clock.load(Ordering::Relaxed), baudrate)
223 }
224
225 /// Read from UART RX buffer, blocking execution until done.
226 pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
227 self.blocking_read_inner(buffer)
228 }
229}
230
231impl Drop for BufferedUartRx<'_> {
232 fn drop(&mut self) {
233 if !self.reborrowed {
234 let state = self.state;
235
236 // SAFETY: RX is being dropped (and is not reborrowed), so the ring buffer must be deinitialized
237 // in order to meet the requirements of init.
238 unsafe {
239 state.rx_buf.deinit();
240 }
241
242 // TX is inactive if the buffer is not available. If this is true, then disable the
243 // interrupt handler since we are running in RX only mode.
244 if state.tx_buf.len() == 0 {
245 self.info.interrupt.disable();
246 }
247
248 self.rx.as_ref().map(|x| x.set_as_disconnected());
249 self.rts.as_ref().map(|x| x.set_as_disconnected());
250 }
251 }
252}
253
254/// Tx-only buffered UART.
255///
256/// Can be obtained from [`BufferedUart::split`], or can be constructed independently,
257/// if you do not need the receiving half of the driver.
258pub struct BufferedUartTx<'d> {
259 info: &'static Info,
260 state: &'static BufferedState,
261 tx: Option<Peri<'d, AnyPin>>,
262 cts: Option<Peri<'d, AnyPin>>,
263 reborrowed: bool,
264}
265
266impl SetConfig for BufferedUartTx<'_> {
267 type Config = Config;
268 type ConfigError = ConfigError;
269
270 fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
271 self.set_config(config)
272 }
273}
274
275impl<'d> BufferedUartTx<'d> {
276 /// Create a new tx-only buffered UART with no hardware flow control.
277 ///
278 /// Useful if you only want Uart Tx. It saves 1 pin.
279 pub fn new<T: Instance>(
280 uart: Peri<'d, T>,
281 tx: Peri<'d, impl TxPin<T>>,
282 _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
283 tx_buffer: &'d mut [u8],
284 config: Config,
285 ) -> Result<Self, ConfigError> {
286 Self::new_inner(uart, new_pin!(tx, config.tx_pf()), None, tx_buffer, config)
287 }
288
289 /// Create a new tx-only buffered UART with a clear-to-send pin
290 pub fn new_with_rts<T: Instance>(
291 uart: Peri<'d, T>,
292 tx: Peri<'d, impl TxPin<T>>,
293 cts: Peri<'d, impl CtsPin<T>>,
294 _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
295 tx_buffer: &'d mut [u8],
296 config: Config,
297 ) -> Result<Self, ConfigError> {
298 Self::new_inner(
299 uart,
300 new_pin!(tx, config.tx_pf()),
301 new_pin!(cts, config.cts_pf()),
302 tx_buffer,
303 config,
304 )
305 }
306
307 /// Reconfigure the driver
308 pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
309 if let Some(ref tx) = self.tx {
310 tx.update_pf(config.tx_pf());
311 }
312
313 if let Some(ref cts) = self.cts {
314 cts.update_pf(config.cts_pf());
315 }
316
317 super::reconfigure(self.info, &self.state.state, config)
318 }
319
320 /// Set baudrate
321 pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> {
322 super::set_baudrate(&self.info, self.state.state.clock.load(Ordering::Relaxed), baudrate)
323 }
324
325 /// Write to UART TX buffer, blocking execution until done.
326 pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<usize, Error> {
327 self.blocking_write_inner(buffer)
328 }
329
330 /// Flush UART TX buffer, blocking execution until done.
331 pub fn blocking_flush(&mut self) -> Result<(), Error> {
332 let state = self.state;
333
334 loop {
335 if state.tx_buf.is_empty() {
336 return Ok(());
337 }
338 }
339 }
340
341 /// Check if UART is busy.
342 pub fn busy(&self) -> bool {
343 super::busy(self.info.regs)
344 }
345
346 /// Send break character
347 pub fn send_break(&mut self) {
348 let r = self.info.regs;
349
350 r.lcrh().modify(|w| {
351 w.set_brk(true);
352 });
353 }
354}
355
356impl Drop for BufferedUartTx<'_> {
357 fn drop(&mut self) {
358 if !self.reborrowed {
359 let state = self.state;
360
361 // SAFETY: TX is being dropped (and is not reborrowed), so the ring buffer must be deinitialized
362 // in order to meet the requirements of init.
363 unsafe {
364 state.tx_buf.deinit();
365 }
366
367 // RX is inactive if the buffer is not available. If this is true, then disable the
368 // interrupt handler since we are running in TX only mode.
369 if state.rx_buf.len() == 0 {
370 self.info.interrupt.disable();
371 }
372
373 self.tx.as_ref().map(|x| x.set_as_disconnected());
374 self.cts.as_ref().map(|x| x.set_as_disconnected());
375 }
376 }
377}
378
379impl embedded_io_async::ErrorType for BufferedUart<'_> {
380 type Error = Error;
381}
382
383impl embedded_io_async::ErrorType for BufferedUartRx<'_> {
384 type Error = Error;
385}
386
387impl embedded_io_async::ErrorType for BufferedUartTx<'_> {
388 type Error = Error;
389}
390
391impl embedded_io_async::Read for BufferedUart<'_> {
392 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
393 self.rx.read(buf).await
394 }
395}
396
397impl embedded_io_async::Read for BufferedUartRx<'_> {
398 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
399 self.read_inner(buf).await
400 }
401}
402
403impl embedded_io_async::ReadReady for BufferedUart<'_> {
404 fn read_ready(&mut self) -> Result<bool, Self::Error> {
405 self.rx.read_ready()
406 }
407}
408
409impl embedded_io_async::ReadReady for BufferedUartRx<'_> {
410 fn read_ready(&mut self) -> Result<bool, Self::Error> {
411 self.read_ready_inner()
412 }
413}
414
415impl embedded_io_async::BufRead for BufferedUart<'_> {
416 async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
417 self.rx.fill_buf().await
418 }
419
420 fn consume(&mut self, amt: usize) {
421 self.rx.consume(amt);
422 }
423}
424
425impl embedded_io_async::BufRead for BufferedUartRx<'_> {
426 async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
427 self.fill_buf_inner().await
428 }
429
430 fn consume(&mut self, amt: usize) {
431 self.consume_inner(amt);
432 }
433}
434
435impl embedded_io_async::Write for BufferedUart<'_> {
436 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
437 self.tx.write_inner(buf).await
438 }
439}
440
441impl embedded_io_async::Write for BufferedUartTx<'_> {
442 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
443 self.write_inner(buf).await
444 }
445
446 async fn flush(&mut self) -> Result<(), Self::Error> {
447 self.flush_inner().await
448 }
449}
450
451impl embedded_io::Read for BufferedUart<'_> {
452 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
453 self.rx.read(buf)
454 }
455}
456
457impl embedded_io::Read for BufferedUartRx<'_> {
458 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
459 self.blocking_read_inner(buf)
460 }
461}
462
463impl embedded_io::Write for BufferedUart<'_> {
464 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
465 self.tx.write(buf)
466 }
467
468 fn flush(&mut self) -> Result<(), Self::Error> {
469 self.tx.flush()
470 }
471}
472
473impl embedded_io::Write for BufferedUartTx<'_> {
474 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
475 self.blocking_write_inner(buf)
476 }
477
478 fn flush(&mut self) -> Result<(), Self::Error> {
479 self.blocking_flush()
480 }
481}
482
483impl embedded_hal_nb::serial::Error for Error {
484 fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
485 match self {
486 Error::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,
487 Error::Noise => embedded_hal_nb::serial::ErrorKind::Noise,
488 Error::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,
489 Error::Parity => embedded_hal_nb::serial::ErrorKind::Parity,
490 Error::Break => embedded_hal_nb::serial::ErrorKind::Other,
491 }
492 }
493}
494
495impl embedded_hal_nb::serial::ErrorType for BufferedUart<'_> {
496 type Error = Error;
497}
498
499impl embedded_hal_nb::serial::ErrorType for BufferedUartRx<'_> {
500 type Error = Error;
501}
502
503impl embedded_hal_nb::serial::ErrorType for BufferedUartTx<'_> {
504 type Error = Error;
505}
506
507impl embedded_hal_nb::serial::Read for BufferedUart<'_> {
508 fn read(&mut self) -> nb::Result<u8, Self::Error> {
509 self.rx.read()
510 }
511}
512
513impl embedded_hal_nb::serial::Read for BufferedUartRx<'_> {
514 fn read(&mut self) -> nb::Result<u8, Self::Error> {
515 if self.info.regs.stat().read().rxfe() {
516 return Err(nb::Error::WouldBlock);
517 }
518
519 super::read_with_error(self.info.regs).map_err(nb::Error::Other)
520 }
521}
522
523impl embedded_hal_nb::serial::Write for BufferedUart<'_> {
524 fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
525 self.tx.write(word)
526 }
527
528 fn flush(&mut self) -> nb::Result<(), Self::Error> {
529 self.tx.flush()
530 }
531}
532
533impl embedded_hal_nb::serial::Write for BufferedUartTx<'_> {
534 fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
535 self.blocking_write(&[word]).map(drop).map_err(nb::Error::Other)
536 }
537
538 fn flush(&mut self) -> nb::Result<(), Self::Error> {
539 self.blocking_flush().map_err(nb::Error::Other)
540 }
541}
542
543// Impl details
544
545/// Buffered UART state.
546pub(crate) struct BufferedState {
547 /// non-buffered UART state. This is inline in order to avoid [`BufferedUartRx`]/Tx
548 /// needing to carry around a 2nd static reference and waste another 4 bytes.
549 state: State,
550 rx_waker: AtomicWaker,
551 rx_buf: RingBuffer,
552 tx_waker: AtomicWaker,
553 tx_buf: RingBuffer,
554 rx_error: AtomicU8,
555}
556
557// these must match bits 8..12 in RXDATA, but shifted by 8 to the right
558const RXE_NOISE: u8 = 16;
559const RXE_OVERRUN: u8 = 8;
560const RXE_BREAK: u8 = 4;
561const RXE_PARITY: u8 = 2;
562const RXE_FRAMING: u8 = 1;
563
564impl BufferedState {
565 pub const fn new() -> Self {
566 Self {
567 state: State::new(),
568 rx_waker: AtomicWaker::new(),
569 rx_buf: RingBuffer::new(),
570 tx_waker: AtomicWaker::new(),
571 tx_buf: RingBuffer::new(),
572 rx_error: AtomicU8::new(0),
573 }
574 }
575}
576
577impl<'d> BufferedUart<'d> {
578 fn new_inner<T: Instance>(
579 _peri: Peri<'d, T>,
580 rx: Option<Peri<'d, AnyPin>>,
581 tx: Option<Peri<'d, AnyPin>>,
582 rts: Option<Peri<'d, AnyPin>>,
583 cts: Option<Peri<'d, AnyPin>>,
584 tx_buffer: &'d mut [u8],
585 rx_buffer: &'d mut [u8],
586 config: Config,
587 ) -> Result<Self, ConfigError> {
588 let info = T::info();
589 let state = T::buffered_state();
590
591 let mut this = Self {
592 tx: BufferedUartTx {
593 info,
594 state,
595 tx,
596 cts,
597 reborrowed: false,
598 },
599 rx: BufferedUartRx {
600 info,
601 state,
602 rx,
603 rts,
604 reborrowed: false,
605 },
606 };
607 this.enable_and_configure(tx_buffer, rx_buffer, &config)?;
608
609 Ok(this)
610 }
611
612 fn enable_and_configure(
613 &mut self,
614 tx_buffer: &'d mut [u8],
615 rx_buffer: &'d mut [u8],
616 config: &Config,
617 ) -> Result<(), ConfigError> {
618 let info = self.rx.info;
619 let state = self.rx.state;
620
621 assert!(!tx_buffer.is_empty());
622 assert!(!rx_buffer.is_empty());
623
624 init_buffers(info, state, Some(tx_buffer), Some(rx_buffer));
625 super::enable(info.regs);
626 super::configure(
627 info,
628 &state.state,
629 config,
630 true,
631 self.rx.rts.is_some(),
632 true,
633 self.tx.cts.is_some(),
634 )?;
635
636 info.interrupt.unpend();
637 unsafe { info.interrupt.enable() };
638
639 Ok(())
640 }
641}
642
643impl<'d> BufferedUartRx<'d> {
644 fn new_inner<T: Instance>(
645 _peri: Peri<'d, T>,
646 rx: Option<Peri<'d, AnyPin>>,
647 rts: Option<Peri<'d, AnyPin>>,
648 rx_buffer: &'d mut [u8],
649 config: Config,
650 ) -> Result<Self, ConfigError> {
651 let mut this = Self {
652 info: T::info(),
653 state: T::buffered_state(),
654 rx,
655 rts,
656 reborrowed: false,
657 };
658 this.enable_and_configure(rx_buffer, &config)?;
659
660 Ok(this)
661 }
662
663 fn enable_and_configure(&mut self, rx_buffer: &'d mut [u8], config: &Config) -> Result<(), ConfigError> {
664 let info = self.info;
665 let state = self.state;
666
667 init_buffers(info, state, None, Some(rx_buffer));
668 super::enable(info.regs);
669 super::configure(info, &self.state.state, config, true, self.rts.is_some(), false, false)?;
670
671 info.interrupt.unpend();
672 unsafe { info.interrupt.enable() };
673
674 Ok(())
675 }
676
677 async fn read_inner(&self, buf: &mut [u8]) -> Result<usize, Error> {
678 poll_fn(move |cx| {
679 let state = self.state;
680
681 if let Poll::Ready(r) = self.try_read(buf) {
682 return Poll::Ready(r);
683 }
684
685 state.rx_waker.register(cx.waker());
686 Poll::Pending
687 })
688 .await
689 }
690
691 fn blocking_read_inner(&self, buffer: &mut [u8]) -> Result<usize, Error> {
692 loop {
693 match self.try_read(buffer) {
694 Poll::Ready(res) => return res,
695 Poll::Pending => continue,
696 }
697 }
698 }
699
700 fn fill_buf_inner(&self) -> impl Future<Output = Result<&'_ [u8], Error>> {
701 poll_fn(move |cx| {
702 let mut rx_reader = unsafe { self.state.rx_buf.reader() };
703 let (p, n) = rx_reader.pop_buf();
704 let result = if n == 0 {
705 match Self::get_rx_error(self.state) {
706 None => {
707 self.state.rx_waker.register(cx.waker());
708 return Poll::Pending;
709 }
710 Some(e) => Err(e),
711 }
712 } else {
713 let buf = unsafe { slice::from_raw_parts(p, n) };
714 Ok(buf)
715 };
716
717 Poll::Ready(result)
718 })
719 }
720
721 fn consume_inner(&self, amt: usize) {
722 let mut rx_reader = unsafe { self.state.rx_buf.reader() };
723 rx_reader.pop_done(amt);
724
725 // (Re-)Enable the interrupt to receive more data in case it was
726 // disabled because the buffer was full or errors were detected.
727 self.info.regs.cpu_int(0).imask().modify(|w| {
728 w.set_rxint(true);
729 w.set_rtout(true);
730 });
731 }
732
733 /// we are ready to read if there is data in the buffer
734 fn read_ready_inner(&self) -> Result<bool, Error> {
735 Ok(!self.state.rx_buf.is_empty())
736 }
737
738 fn try_read(&self, buf: &mut [u8]) -> Poll<Result<usize, Error>> {
739 let state = self.state;
740
741 if buf.is_empty() {
742 return Poll::Ready(Ok(0));
743 }
744
745 let mut rx_reader = unsafe { state.rx_buf.reader() };
746 let n = rx_reader.pop(|data| {
747 let n = data.len().min(buf.len());
748 buf[..n].copy_from_slice(&data[..n]);
749 n
750 });
751
752 let result = if n == 0 {
753 match Self::get_rx_error(state) {
754 None => return Poll::Pending,
755 Some(e) => Err(e),
756 }
757 } else {
758 Ok(n)
759 };
760
761 // (Re-)Enable the interrupt to receive more data in case it was
762 // disabled because the buffer was full or errors were detected.
763 self.info.regs.cpu_int(0).imask().modify(|w| {
764 w.set_rxint(true);
765 w.set_rtout(true);
766 });
767
768 Poll::Ready(result)
769 }
770
771 fn get_rx_error(state: &BufferedState) -> Option<Error> {
772 // Cortex-M0 has does not support atomic swap, so we must do two operations.
773 let errs = critical_section::with(|_cs| {
774 let errs = state.rx_error.load(Ordering::Relaxed);
775 state.rx_error.store(0, Ordering::Relaxed);
776
777 errs
778 });
779
780 if errs & RXE_NOISE != 0 {
781 Some(Error::Noise)
782 } else if errs & RXE_OVERRUN != 0 {
783 Some(Error::Overrun)
784 } else if errs & RXE_BREAK != 0 {
785 Some(Error::Break)
786 } else if errs & RXE_PARITY != 0 {
787 Some(Error::Parity)
788 } else if errs & RXE_FRAMING != 0 {
789 Some(Error::Framing)
790 } else {
791 None
792 }
793 }
794}
795
796impl<'d> BufferedUartTx<'d> {
797 fn new_inner<T: Instance>(
798 _peri: Peri<'d, T>,
799 tx: Option<Peri<'d, AnyPin>>,
800 cts: Option<Peri<'d, AnyPin>>,
801 tx_buffer: &'d mut [u8],
802 config: Config,
803 ) -> Result<Self, ConfigError> {
804 let mut this = Self {
805 info: T::info(),
806 state: T::buffered_state(),
807 tx,
808 cts,
809 reborrowed: false,
810 };
811
812 this.enable_and_configure(tx_buffer, &config)?;
813
814 Ok(this)
815 }
816
817 async fn write_inner(&self, buf: &[u8]) -> Result<usize, Error> {
818 poll_fn(move |cx| {
819 let state = self.state;
820
821 if buf.is_empty() {
822 return Poll::Ready(Ok(0));
823 }
824
825 let mut tx_writer = unsafe { state.tx_buf.writer() };
826 let n = tx_writer.push(|data| {
827 let n = data.len().min(buf.len());
828 data[..n].copy_from_slice(&buf[..n]);
829 n
830 });
831
832 if n == 0 {
833 state.tx_waker.register(cx.waker());
834 return Poll::Pending;
835 }
836
837 // The TX interrupt only triggers when the there was data in the
838 // FIFO and the number of bytes drops below a threshold. When the
839 // FIFO was empty we have to manually pend the interrupt to shovel
840 // TX data from the buffer into the FIFO.
841 self.info.interrupt.pend();
842 Poll::Ready(Ok(n))
843 })
844 .await
845 }
846
847 fn blocking_write_inner(&self, buffer: &[u8]) -> Result<usize, Error> {
848 let state = self.state;
849
850 loop {
851 let empty = state.tx_buf.is_empty();
852
853 // SAFETY: tx buf must be initialized if BufferedUartTx exists.
854 let mut tx_writer = unsafe { state.tx_buf.writer() };
855 let data = tx_writer.push_slice();
856
857 if !data.is_empty() {
858 let n = data.len().min(buffer.len());
859 data[..n].copy_from_slice(&buffer[..n]);
860 tx_writer.push_done(n);
861
862 if empty {
863 self.info.interrupt.pend();
864 }
865
866 return Ok(n);
867 }
868 }
869 }
870
871 async fn flush_inner(&self) -> Result<(), Error> {
872 poll_fn(move |cx| {
873 let state = self.state;
874
875 if !state.tx_buf.is_empty() {
876 state.tx_waker.register(cx.waker());
877 return Poll::Pending;
878 }
879
880 Poll::Ready(Ok(()))
881 })
882 .await
883 }
884
885 fn enable_and_configure(&mut self, tx_buffer: &'d mut [u8], config: &Config) -> Result<(), ConfigError> {
886 let info = self.info;
887 let state = self.state;
888
889 init_buffers(info, state, Some(tx_buffer), None);
890 super::enable(info.regs);
891 super::configure(info, &state.state, config, false, false, true, self.cts.is_some())?;
892
893 info.interrupt.unpend();
894 unsafe { info.interrupt.enable() };
895
896 Ok(())
897 }
898}
899
900fn init_buffers<'d>(
901 info: &Info,
902 state: &BufferedState,
903 tx_buffer: Option<&'d mut [u8]>,
904 rx_buffer: Option<&'d mut [u8]>,
905) {
906 if let Some(tx_buffer) = tx_buffer {
907 let len = tx_buffer.len();
908 unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) };
909 }
910
911 if let Some(rx_buffer) = rx_buffer {
912 let len = rx_buffer.len();
913 unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) };
914 }
915
916 info.regs.cpu_int(0).imask().modify(|w| {
917 w.set_nerr(true);
918 w.set_frmerr(true);
919 w.set_parerr(true);
920 w.set_brkerr(true);
921 w.set_ovrerr(true);
922 });
923}
924
925fn on_interrupt(r: Regs, state: &'static BufferedState) {
926 let int = r.cpu_int(0).mis().read();
927
928 // Per https://github.com/embassy-rs/embassy/pull/1458, both buffered and unbuffered handlers may be bound.
929 if super::dma_enabled(r) {
930 return;
931 }
932
933 // RX
934 if state.rx_buf.is_available() {
935 // SAFETY: RX must have been initialized if RXE is set.
936 let mut rx_writer = unsafe { state.rx_buf.writer() };
937 let rx_buf = rx_writer.push_slice();
938 let mut n_read = 0;
939 let mut error = false;
940
941 for rx_byte in rx_buf {
942 let stat = r.stat().read();
943
944 if stat.rxfe() {
945 break;
946 }
947
948 let data = r.rxdata().read();
949
950 if (data.0 >> 8) != 0 {
951 // Cortex-M0 does not support atomic fetch_or, must do 2 operations.
952 critical_section::with(|_cs| {
953 let mut value = state.rx_error.load(Ordering::Relaxed);
954 value |= (data.0 >> 8) as u8;
955 state.rx_error.store(value, Ordering::Relaxed);
956 });
957 error = true;
958
959 // only fill the buffer with valid characters. the current character is fine
960 // if the error is an overrun, but if we add it to the buffer we'll report
961 // the overrun one character too late. drop it instead and pretend we were
962 // a bit slower at draining the rx fifo than we actually were.
963 // this is consistent with blocking uart error reporting.
964 break;
965 }
966
967 *rx_byte = data.data();
968 n_read += 1;
969 }
970
971 if n_read > 0 {
972 rx_writer.push_done(n_read);
973 state.rx_waker.wake();
974 } else if error {
975 state.rx_waker.wake();
976 }
977
978 // Disable any further RX interrupts when the buffer becomes full or
979 // errors have occurred. This lets us buffer additional errors in the
980 // fifo without needing more error storage locations, and most applications
981 // will want to do a full reset of their uart state anyway once an error
982 // has happened.
983 if state.rx_buf.is_full() || error {
984 r.cpu_int(0).imask().modify(|w| {
985 w.set_rxint(false);
986 w.set_rtout(false);
987 });
988 }
989 }
990
991 if int.eot() {
992 r.cpu_int(0).imask().modify(|w| {
993 w.set_eot(false);
994 });
995
996 r.cpu_int(0).iclr().write(|w| {
997 w.set_eot(true);
998 });
999
1000 state.tx_waker.wake();
1001 }
1002
1003 // TX
1004 if state.tx_buf.is_available() {
1005 // SAFETY: TX must have been initialized if TXE is set.
1006 let mut tx_reader = unsafe { state.tx_buf.reader() };
1007 let buf = tx_reader.pop_slice();
1008 let mut n_written = 0;
1009
1010 for tx_byte in buf.iter_mut() {
1011 let stat = r.stat().read();
1012
1013 if stat.txff() {
1014 break;
1015 }
1016
1017 r.txdata().write(|w| {
1018 w.set_data(*tx_byte);
1019 });
1020 n_written += 1;
1021 }
1022
1023 if n_written > 0 {
1024 // EOT will wake.
1025 r.cpu_int(0).imask().modify(|w| {
1026 w.set_eot(true);
1027 });
1028
1029 tx_reader.pop_done(n_written);
1030 }
1031 }
1032
1033 // Clear TX and error interrupt flags
1034 // RX interrupt flags are cleared by writing to ICLR.
1035 let mis = r.cpu_int(0).mis().read();
1036 r.cpu_int(0).iclr().write(|w| {
1037 w.set_nerr(mis.nerr());
1038 w.set_frmerr(mis.frmerr());
1039 w.set_parerr(mis.parerr());
1040 w.set_brkerr(mis.brkerr());
1041 w.set_ovrerr(mis.ovrerr());
1042 });
1043
1044 // Errors
1045 if mis.nerr() {
1046 warn!("Noise error");
1047 }
1048 if mis.frmerr() {
1049 warn!("Framing error");
1050 }
1051 if mis.parerr() {
1052 warn!("Parity error");
1053 }
1054 if mis.brkerr() {
1055 warn!("Break error");
1056 }
1057 if mis.ovrerr() {
1058 warn!("Overrun error");
1059 }
1060}
diff --git a/embassy-mspm0/src/uart.rs b/embassy-mspm0/src/uart/mod.rs
index bc1d2e343..6599cea06 100644
--- a/embassy-mspm0/src/uart.rs
+++ b/embassy-mspm0/src/uart/mod.rs
@@ -1,8 +1,11 @@
1#![macro_use] 1#![macro_use]
2 2
3mod buffered;
4
3use core::marker::PhantomData; 5use core::marker::PhantomData;
4use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; 6use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
5 7
8pub use buffered::*;
6use embassy_embedded_hal::SetConfig; 9use embassy_embedded_hal::SetConfig;
7use embassy_hal_internal::PeripheralType; 10use embassy_hal_internal::PeripheralType;
8 11
@@ -128,8 +131,8 @@ pub struct Config {
128 // pub manchester: bool, 131 // pub manchester: bool,
129 132
130 // TODO: majority voting 133 // TODO: majority voting
131 134 /// If true: the built-in FIFO is enabled.
132 // TODO: fifo level select - need power domain info in metapac 135 pub fifo_enable: bool,
133 136
134 // TODO: glitch suppression 137 // TODO: glitch suppression
135 /// If true: invert TX pin signal values (V<sub>DD</sub> = 0/mark, Gnd = 1/idle). 138 /// If true: invert TX pin signal values (V<sub>DD</sub> = 0/mark, Gnd = 1/idle).
@@ -169,6 +172,7 @@ impl Default for Config {
169 msb_order: BitOrder::LsbFirst, 172 msb_order: BitOrder::LsbFirst,
170 loop_back_enable: false, 173 loop_back_enable: false,
171 // manchester: false, 174 // manchester: false,
175 fifo_enable: false,
172 invert_tx: false, 176 invert_tx: false,
173 invert_rx: false, 177 invert_rx: false,
174 invert_rts: false, 178 invert_rts: false,
@@ -185,7 +189,7 @@ impl Default for Config {
185/// 189///
186/// ### Notes on [`embedded_io::Read`] 190/// ### Notes on [`embedded_io::Read`]
187/// 191///
188/// `embedded_io::Read` requires guarantees that the base [`UartRx`] cannot provide. 192/// [`embedded_io::Read`] requires guarantees that the base [`UartRx`] cannot provide.
189/// 193///
190/// See [`UartRx`] for more details, and see [`BufferedUart`] and [`RingBufferedUartRx`] 194/// See [`UartRx`] for more details, and see [`BufferedUart`] and [`RingBufferedUartRx`]
191/// as alternatives that do provide the necessary guarantees for `embedded_io::Read`. 195/// as alternatives that do provide the necessary guarantees for `embedded_io::Read`.
@@ -199,8 +203,7 @@ impl<'d, M: Mode> SetConfig for Uart<'d, M> {
199 type ConfigError = ConfigError; 203 type ConfigError = ConfigError;
200 204
201 fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { 205 fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
202 self.tx.set_config(config)?; 206 self.set_config(config)
203 self.rx.set_config(config)
204 } 207 }
205} 208}
206 209
@@ -236,6 +239,12 @@ impl core::fmt::Display for Error {
236 239
237impl core::error::Error for Error {} 240impl core::error::Error for Error {}
238 241
242impl embedded_io::Error for Error {
243 fn kind(&self) -> embedded_io::ErrorKind {
244 embedded_io::ErrorKind::Other
245 }
246}
247
239/// Rx-only UART Driver. 248/// Rx-only UART Driver.
240/// 249///
241/// Can be obtained from [`Uart::split`], or can be constructed independently, 250/// Can be obtained from [`Uart::split`], or can be constructed independently,
@@ -260,7 +269,7 @@ impl<'d, M: Mode> SetConfig for UartRx<'d, M> {
260impl<'d> UartRx<'d, Blocking> { 269impl<'d> UartRx<'d, Blocking> {
261 /// Create a new rx-only UART with no hardware flow control. 270 /// Create a new rx-only UART with no hardware flow control.
262 /// 271 ///
263 /// Useful if you only want Uart Rx. It saves 1 pin . 272 /// Useful if you only want Uart Rx. It saves 1 pin.
264 pub fn new_blocking<T: Instance>( 273 pub fn new_blocking<T: Instance>(
265 peri: Peri<'d, T>, 274 peri: Peri<'d, T>,
266 rx: Peri<'d, impl RxPin<T>>, 275 rx: Peri<'d, impl RxPin<T>>,
@@ -286,19 +295,6 @@ impl<'d> UartRx<'d, Blocking> {
286} 295}
287 296
288impl<'d, M: Mode> UartRx<'d, M> { 297impl<'d, M: Mode> UartRx<'d, M> {
289 /// Reconfigure the driver
290 pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
291 if let Some(ref rx) = self.rx {
292 rx.update_pf(config.rx_pf());
293 }
294
295 if let Some(ref rts) = self.rts {
296 rts.update_pf(config.rts_pf());
297 }
298
299 reconfigure(self.info, self.state, config)
300 }
301
302 /// Perform a blocking read into `buffer` 298 /// Perform a blocking read into `buffer`
303 pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { 299 pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
304 let r = self.info.regs; 300 let r = self.info.regs;
@@ -315,6 +311,19 @@ impl<'d, M: Mode> UartRx<'d, M> {
315 Ok(()) 311 Ok(())
316 } 312 }
317 313
314 /// Reconfigure the driver
315 pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
316 if let Some(ref rx) = self.rx {
317 rx.update_pf(config.rx_pf());
318 }
319
320 if let Some(ref rts) = self.rts {
321 rts.update_pf(config.rts_pf());
322 }
323
324 reconfigure(self.info, self.state, config)
325 }
326
318 /// Set baudrate 327 /// Set baudrate
319 pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { 328 pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> {
320 set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate) 329 set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate)
@@ -378,19 +387,6 @@ impl<'d> UartTx<'d, Blocking> {
378} 387}
379 388
380impl<'d, M: Mode> UartTx<'d, M> { 389impl<'d, M: Mode> UartTx<'d, M> {
381 /// Reconfigure the driver
382 pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
383 if let Some(ref tx) = self.tx {
384 tx.update_pf(config.tx_pf());
385 }
386
387 if let Some(ref cts) = self.cts {
388 cts.update_pf(config.cts_pf());
389 }
390
391 reconfigure(self.info, self.state, config)
392 }
393
394 /// Perform a blocking UART write 390 /// Perform a blocking UART write
395 pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { 391 pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
396 let r = self.info.regs; 392 let r = self.info.regs;
@@ -427,6 +423,24 @@ impl<'d, M: Mode> UartTx<'d, M> {
427 }); 423 });
428 } 424 }
429 425
426 /// Check if UART is busy.
427 pub fn busy(&self) -> bool {
428 busy(self.info.regs)
429 }
430
431 /// Reconfigure the driver
432 pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
433 if let Some(ref tx) = self.tx {
434 tx.update_pf(config.tx_pf());
435 }
436
437 if let Some(ref cts) = self.cts {
438 cts.update_pf(config.cts_pf());
439 }
440
441 reconfigure(self.info, self.state, config)
442 }
443
430 /// Set baudrate 444 /// Set baudrate
431 pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { 445 pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> {
432 set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate) 446 set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate)
@@ -489,6 +503,11 @@ impl<'d, M: Mode> Uart<'d, M> {
489 self.tx.blocking_flush() 503 self.tx.blocking_flush()
490 } 504 }
491 505
506 /// Check if UART is busy.
507 pub fn busy(&self) -> bool {
508 self.tx.busy()
509 }
510
492 /// Perform a blocking read into `buffer` 511 /// Perform a blocking read into `buffer`
493 pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { 512 pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
494 self.rx.blocking_read(buffer) 513 self.rx.blocking_read(buffer)
@@ -508,6 +527,12 @@ impl<'d, M: Mode> Uart<'d, M> {
508 (&mut self.tx, &mut self.rx) 527 (&mut self.tx, &mut self.rx)
509 } 528 }
510 529
530 /// Reconfigure the driver
531 pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
532 self.tx.set_config(config)?;
533 self.rx.set_config(config)
534 }
535
511 /// Send break character 536 /// Send break character
512 pub fn send_break(&self) { 537 pub fn send_break(&self) {
513 self.tx.send_break(); 538 self.tx.send_break();
@@ -557,8 +582,16 @@ pub(crate) struct Info {
557} 582}
558 583
559pub(crate) struct State { 584pub(crate) struct State {
560 /// The clock rate of the UART. This might be configured. 585 /// The clock rate of the UART in Hz.
561 pub(crate) clock: AtomicU32, 586 clock: AtomicU32,
587}
588
589impl State {
590 pub const fn new() -> Self {
591 Self {
592 clock: AtomicU32::new(0),
593 }
594 }
562} 595}
563 596
564impl<'d, M: Mode> UartRx<'d, M> { 597impl<'d, M: Mode> UartRx<'d, M> {
@@ -746,7 +779,8 @@ fn configure(
746 779
747 info.regs.ctl0().modify(|w| { 780 info.regs.ctl0().modify(|w| {
748 w.set_lbe(config.loop_back_enable); 781 w.set_lbe(config.loop_back_enable);
749 w.set_rxe(enable_rx); 782 // Errata UART_ERR_02, must set RXE to allow use of EOT.
783 w.set_rxe(enable_rx | enable_tx);
750 w.set_txe(enable_tx); 784 w.set_txe(enable_tx);
751 // RXD_OUT_EN and TXD_OUT_EN? 785 // RXD_OUT_EN and TXD_OUT_EN?
752 w.set_menc(false); 786 w.set_menc(false);
@@ -754,13 +788,18 @@ fn configure(
754 w.set_rtsen(enable_rts); 788 w.set_rtsen(enable_rts);
755 w.set_ctsen(enable_cts); 789 w.set_ctsen(enable_cts);
756 // oversampling is set later 790 // oversampling is set later
757 // TODO: config 791 w.set_fen(config.fifo_enable);
758 w.set_fen(false);
759 // TODO: config 792 // TODO: config
760 w.set_majvote(false); 793 w.set_majvote(false);
761 w.set_msbfirst(matches!(config.msb_order, BitOrder::MsbFirst)); 794 w.set_msbfirst(matches!(config.msb_order, BitOrder::MsbFirst));
762 }); 795 });
763 796
797 info.regs.ifls().modify(|w| {
798 // TODO: Need power domain info for other options.
799 w.set_txiflsel(vals::Iflssel::AT_LEAST_ONE);
800 w.set_rxiflsel(vals::Iflssel::AT_LEAST_ONE);
801 });
802
764 info.regs.lcrh().modify(|w| { 803 info.regs.lcrh().modify(|w| {
765 let eps = if matches!(config.parity, Parity::ParityEven) { 804 let eps = if matches!(config.parity, Parity::ParityEven) {
766 vals::Eps::EVEN 805 vals::Eps::EVEN
@@ -1021,9 +1060,29 @@ fn read_with_error(r: Regs) -> Result<u8, Error> {
1021 Ok(rx.data()) 1060 Ok(rx.data())
1022} 1061}
1023 1062
1063/// This function assumes CTL0.ENABLE is set (for errata cases).
1064fn busy(r: Regs) -> bool {
1065 // Errata UART_ERR_08
1066 if cfg!(any(
1067 mspm0g151x, mspm0g351x, mspm0l110x, mspm0l130x, mspm0l134x, mspm0c110x,
1068 )) {
1069 let stat = r.stat().read();
1070 // "Poll TXFIFO status and the CTL0.ENABLE register bit to identify BUSY status."
1071 !stat.txfe()
1072 } else {
1073 r.stat().read().busy()
1074 }
1075}
1076
1077// TODO: Implement when dma uart is implemented.
1078fn dma_enabled(_r: Regs) -> bool {
1079 false
1080}
1081
1024pub(crate) trait SealedInstance { 1082pub(crate) trait SealedInstance {
1025 fn info() -> &'static Info; 1083 fn info() -> &'static Info;
1026 fn state() -> &'static State; 1084 fn state() -> &'static State;
1085 fn buffered_state() -> &'static BufferedState;
1027} 1086}
1028 1087
1029macro_rules! impl_uart_instance { 1088macro_rules! impl_uart_instance {
@@ -1041,12 +1100,16 @@ macro_rules! impl_uart_instance {
1041 } 1100 }
1042 1101
1043 fn state() -> &'static crate::uart::State { 1102 fn state() -> &'static crate::uart::State {
1044 use crate::interrupt::typelevel::Interrupt;
1045 use crate::uart::State; 1103 use crate::uart::State;
1046 1104
1047 static STATE: State = State { 1105 static STATE: State = State::new();
1048 clock: core::sync::atomic::AtomicU32::new(0), 1106 &STATE
1049 }; 1107 }
1108
1109 fn buffered_state() -> &'static crate::uart::BufferedState {
1110 use crate::uart::BufferedState;
1111
1112 static STATE: BufferedState = BufferedState::new();
1050 &STATE 1113 &STATE
1051 } 1114 }
1052 } 1115 }
diff --git a/embassy-net-adin1110/Cargo.toml b/embassy-net-adin1110/Cargo.toml
index a620928cb..9cda4dc5b 100644
--- a/embassy-net-adin1110/Cargo.toml
+++ b/embassy-net-adin1110/Cargo.toml
@@ -16,7 +16,7 @@ log = { version = "0.4", default-features = false, optional = true }
16embedded-hal-1 = { package = "embedded-hal", version = "1.0" } 16embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
17embedded-hal-async = { version = "1.0" } 17embedded-hal-async = { version = "1.0" }
18embedded-hal-bus = { version = "0.1", features = ["async"] } 18embedded-hal-bus = { version = "0.1", features = ["async"] }
19embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } 19embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel" }
20embassy-time = { version = "0.4.0", path = "../embassy-time" } 20embassy-time = { version = "0.4.0", path = "../embassy-time" }
21embassy-futures = { version = "0.1.0", path = "../embassy-futures" } 21embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
22bitfield = "0.14.0" 22bitfield = "0.14.0"
diff --git a/embassy-net-driver-channel/CHANGELOG.md b/embassy-net-driver-channel/CHANGELOG.md
index d7af7e55d..1189e50c3 100644
--- a/embassy-net-driver-channel/CHANGELOG.md
+++ b/embassy-net-driver-channel/CHANGELOG.md
@@ -5,7 +5,12 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8## Unreleased 8<!-- next-header -->
9## Unreleased - ReleaseDate
10
11## 0.3.1 - 2025-07-16
12
13- Update `embassy-sync` to v0.7.0
9 14
10## 0.3.0 - 2024-08-05 15## 0.3.0 - 2024-08-05
11 16
diff --git a/embassy-net-driver-channel/Cargo.toml b/embassy-net-driver-channel/Cargo.toml
index c16c4be74..386d492c6 100644
--- a/embassy-net-driver-channel/Cargo.toml
+++ b/embassy-net-driver-channel/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "embassy-net-driver-channel" 2name = "embassy-net-driver-channel"
3version = "0.3.0" 3version = "0.3.1"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack." 6description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack."
diff --git a/embassy-net-driver-channel/release.toml b/embassy-net-driver-channel/release.toml
new file mode 100644
index 000000000..fb6feaf21
--- /dev/null
+++ b/embassy-net-driver-channel/release.toml
@@ -0,0 +1,5 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-net-esp-hosted/Cargo.toml b/embassy-net-esp-hosted/Cargo.toml
index dea74ed65..7ccef84e8 100644
--- a/embassy-net-esp-hosted/Cargo.toml
+++ b/embassy-net-esp-hosted/Cargo.toml
@@ -20,7 +20,7 @@ log = { version = "0.4.14", optional = true }
20embassy-time = { version = "0.4.0", path = "../embassy-time" } 20embassy-time = { version = "0.4.0", path = "../embassy-time" }
21embassy-sync = { version = "0.7.0", path = "../embassy-sync"} 21embassy-sync = { version = "0.7.0", path = "../embassy-sync"}
22embassy-futures = { version = "0.1.0", path = "../embassy-futures"} 22embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
23embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel"} 23embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel"}
24 24
25embedded-hal = { version = "1.0" } 25embedded-hal = { version = "1.0" }
26embedded-hal-async = { version = "1.0" } 26embedded-hal-async = { version = "1.0" }
diff --git a/embassy-net-nrf91/Cargo.toml b/embassy-net-nrf91/Cargo.toml
index 62813e546..f3c0a0078 100644
--- a/embassy-net-nrf91/Cargo.toml
+++ b/embassy-net-nrf91/Cargo.toml
@@ -23,7 +23,7 @@ cortex-m = "0.7.7"
23embassy-time = { version = "0.4.0", path = "../embassy-time" } 23embassy-time = { version = "0.4.0", path = "../embassy-time" }
24embassy-sync = { version = "0.7.0", path = "../embassy-sync" } 24embassy-sync = { version = "0.7.0", path = "../embassy-sync" }
25embassy-futures = { version = "0.1.0", path = "../embassy-futures" } 25embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
26embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } 26embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel" }
27 27
28heapless = "0.8" 28heapless = "0.8"
29embedded-io = "0.6.1" 29embedded-io = "0.6.1"
@@ -33,7 +33,7 @@ at-commands = "0.5.4"
33src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-nrf91-v$VERSION/embassy-net-nrf91/src/" 33src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-nrf91-v$VERSION/embassy-net-nrf91/src/"
34src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-nrf91/src/" 34src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-nrf91/src/"
35target = "thumbv7em-none-eabi" 35target = "thumbv7em-none-eabi"
36features = ["defmt"] 36features = ["defmt", "nrf-pac/nrf9160"]
37 37
38[package.metadata.docs.rs] 38[package.metadata.docs.rs]
39features = ["defmt"] 39features = ["defmt"]
diff --git a/embassy-net-nrf91/src/lib.rs b/embassy-net-nrf91/src/lib.rs
index 61fcaea1f..0bd9be0d9 100644
--- a/embassy-net-nrf91/src/lib.rs
+++ b/embassy-net-nrf91/src/lib.rs
@@ -119,14 +119,16 @@ async fn new_internal<'a>(
119 let shmem_ptr = shmem.as_mut_ptr() as *mut u8; 119 let shmem_ptr = shmem.as_mut_ptr() as *mut u8;
120 120
121 const SPU_REGION_SIZE: usize = 8192; // 8kb 121 const SPU_REGION_SIZE: usize = 8192; // 8kb
122 assert!(shmem_len != 0); 122 trace!(" shmem_ptr = {}, shmem_len = {}", shmem_ptr, shmem_len);
123
124 assert!(shmem_len != 0, "shmem length must not be zero");
123 assert!( 125 assert!(
124 shmem_len % SPU_REGION_SIZE == 0, 126 shmem_len % SPU_REGION_SIZE == 0,
125 "shmem length must be a multiple of 8kb" 127 "shmem length must be a multiple of 8kb"
126 ); 128 );
127 assert!( 129 assert!(
128 (shmem_ptr as usize) % SPU_REGION_SIZE == 0, 130 (shmem_ptr as usize) % SPU_REGION_SIZE == 0,
129 "shmem length must be a multiple of 8kb" 131 "shmem pointer must be 8kb-aligned"
130 ); 132 );
131 assert!( 133 assert!(
132 (shmem_ptr as usize + shmem_len) < 0x2002_0000, 134 (shmem_ptr as usize + shmem_len) < 0x2002_0000,
@@ -135,8 +137,15 @@ async fn new_internal<'a>(
135 137
136 let spu = pac::SPU_S; 138 let spu = pac::SPU_S;
137 debug!("Setting IPC RAM as nonsecure..."); 139 debug!("Setting IPC RAM as nonsecure...");
140 trace!(
141 " SPU_REGION_SIZE={}, shmem_ptr=0x{:08X}, shmem_len={}",
142 SPU_REGION_SIZE,
143 shmem_ptr as usize,
144 shmem_len
145 );
138 let region_start = (shmem_ptr as usize - 0x2000_0000) / SPU_REGION_SIZE; 146 let region_start = (shmem_ptr as usize - 0x2000_0000) / SPU_REGION_SIZE;
139 let region_end = region_start + shmem_len / SPU_REGION_SIZE; 147 let region_end = region_start + shmem_len / SPU_REGION_SIZE;
148 trace!(" region_start={}, region_end={}", region_start, region_end);
140 for i in region_start..region_end { 149 for i in region_start..region_end {
141 spu.ramregion(i).perm().write(|w| { 150 spu.ramregion(i).perm().write(|w| {
142 w.set_execute(true); 151 w.set_execute(true);
@@ -154,13 +163,18 @@ async fn new_internal<'a>(
154 end: unsafe { shmem_ptr.add(shmem_len) }, 163 end: unsafe { shmem_ptr.add(shmem_len) },
155 _phantom: PhantomData, 164 _phantom: PhantomData,
156 }; 165 };
157 166 trace!(
158 let ipc = pac::IPC_NS; 167 " Allocator: start=0x{:08X}, end=0x{:08X}",
159 let power = pac::POWER_S; 168 alloc.start as usize,
169 alloc.end as usize
170 );
160 171
161 let cb: &mut ControlBlock = alloc.alloc().write(unsafe { mem::zeroed() }); 172 let cb: &mut ControlBlock = alloc.alloc().write(unsafe { mem::zeroed() });
173
162 let rx = alloc.alloc_bytes(RX_SIZE); 174 let rx = alloc.alloc_bytes(RX_SIZE);
175 trace!(" RX buffer at {}, size={}", rx.as_ptr(), RX_SIZE);
163 let trace = alloc.alloc_bytes(TRACE_SIZE); 176 let trace = alloc.alloc_bytes(TRACE_SIZE);
177 trace!(" Trace buffer at {}, size={}", trace.as_ptr(), TRACE_SIZE);
164 178
165 cb.version = 0x00010000; 179 cb.version = 0x00010000;
166 cb.rx_base = rx.as_mut_ptr() as _; 180 cb.rx_base = rx.as_mut_ptr() as _;
@@ -174,8 +188,10 @@ async fn new_internal<'a>(
174 cb.trace.base = trace.as_mut_ptr() as _; 188 cb.trace.base = trace.as_mut_ptr() as _;
175 cb.trace.size = TRACE_SIZE; 189 cb.trace.size = TRACE_SIZE;
176 190
191 let ipc = pac::IPC_NS;
177 ipc.gpmem(0).write_value(cb as *mut _ as u32); 192 ipc.gpmem(0).write_value(cb as *mut _ as u32);
178 ipc.gpmem(1).write_value(0); 193 ipc.gpmem(1).write_value(0);
194 trace!(" GPMEM[0]={:#X}, GPMEM[1]={}", cb as *mut _ as u32, 0);
179 195
180 // connect task/event i to channel i 196 // connect task/event i to channel i
181 for i in 0..8 { 197 for i in 0..8 {
@@ -185,8 +201,9 @@ async fn new_internal<'a>(
185 201
186 compiler_fence(Ordering::SeqCst); 202 compiler_fence(Ordering::SeqCst);
187 203
204 let power = pac::POWER_S;
188 // POWER.LTEMODEM.STARTN = 0 205 // POWER.LTEMODEM.STARTN = 0
189 // The reg is missing in the PAC?? 206 // TODO: The reg is missing in the PAC??
190 let startn = unsafe { (power.as_ptr() as *mut u32).add(0x610 / 4) }; 207 let startn = unsafe { (power.as_ptr() as *mut u32).add(0x610 / 4) };
191 unsafe { startn.write_volatile(0) } 208 unsafe { startn.write_volatile(0) }
192 209
@@ -202,6 +219,8 @@ async fn new_internal<'a>(
202 219
203 rx_control_list: ptr::null_mut(), 220 rx_control_list: ptr::null_mut(),
204 rx_data_list: ptr::null_mut(), 221 rx_data_list: ptr::null_mut(),
222 rx_control_len: 0,
223 rx_data_len: 0,
205 rx_seq_no: 0, 224 rx_seq_no: 0,
206 rx_check: PointerChecker { 225 rx_check: PointerChecker {
207 start: rx.as_mut_ptr() as *mut u8, 226 start: rx.as_mut_ptr() as *mut u8,
@@ -310,6 +329,10 @@ struct StateInner {
310 329
311 rx_control_list: *mut List, 330 rx_control_list: *mut List,
312 rx_data_list: *mut List, 331 rx_data_list: *mut List,
332 /// Number of entries in the control list
333 rx_control_len: usize,
334 /// Number of entries in the data list
335 rx_data_len: usize,
313 rx_seq_no: u16, 336 rx_seq_no: u16,
314 rx_check: PointerChecker, 337 rx_check: PointerChecker,
315 338
@@ -346,8 +369,11 @@ impl StateInner {
346 self.rx_data_list = desc.data_list_ptr; 369 self.rx_data_list = desc.data_list_ptr;
347 let rx_control_len = unsafe { addr_of!((*self.rx_control_list).len).read_volatile() }; 370 let rx_control_len = unsafe { addr_of!((*self.rx_control_list).len).read_volatile() };
348 let rx_data_len = unsafe { addr_of!((*self.rx_data_list).len).read_volatile() }; 371 let rx_data_len = unsafe { addr_of!((*self.rx_data_list).len).read_volatile() };
349 assert_eq!(rx_control_len, LIST_LEN); 372
350 assert_eq!(rx_data_len, LIST_LEN); 373 trace!("modem control list length: {}", rx_control_len);
374 trace!("modem data list length: {}", rx_data_len);
375 self.rx_control_len = rx_control_len;
376 self.rx_data_len = rx_data_len;
351 self.init = true; 377 self.init = true;
352 378
353 debug!("IPC initialized OK!"); 379 debug!("IPC initialized OK!");
@@ -463,7 +489,12 @@ impl StateInner {
463 489
464 fn process(&mut self, list: *mut List, is_control: bool, ch: &mut ch::Runner<MTU>) -> bool { 490 fn process(&mut self, list: *mut List, is_control: bool, ch: &mut ch::Runner<MTU>) -> bool {
465 let mut did_work = false; 491 let mut did_work = false;
466 for i in 0..LIST_LEN { 492 let max = if is_control {
493 self.rx_control_len
494 } else {
495 self.rx_data_len
496 };
497 for i in 0..max {
467 let item_ptr = unsafe { addr_of_mut!((*list).items[i]) }; 498 let item_ptr = unsafe { addr_of_mut!((*list).items[i]) };
468 let preamble = unsafe { addr_of!((*item_ptr).state).read_volatile() }; 499 let preamble = unsafe { addr_of!((*item_ptr).state).read_volatile() };
469 if preamble & 0xFF == 0x01 && preamble >> 16 == self.rx_seq_no as u32 { 500 if preamble & 0xFF == 0x01 && preamble >> 16 == self.rx_seq_no as u32 {
@@ -947,7 +978,7 @@ impl<'a> Runner<'a> {
947 } 978 }
948} 979}
949 980
950const LIST_LEN: usize = 16; 981const LIST_LEN: usize = 32;
951 982
952#[repr(C)] 983#[repr(C)]
953struct ControlBlock { 984struct ControlBlock {
diff --git a/embassy-net-ppp/Cargo.toml b/embassy-net-ppp/Cargo.toml
index 0936626eb..b724401c9 100644
--- a/embassy-net-ppp/Cargo.toml
+++ b/embassy-net-ppp/Cargo.toml
@@ -18,7 +18,7 @@ defmt = { version = "1.0.1", optional = true }
18log = { version = "0.4.14", optional = true } 18log = { version = "0.4.14", optional = true }
19 19
20embedded-io-async = { version = "0.6.1" } 20embedded-io-async = { version = "0.6.1" }
21embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } 21embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel" }
22embassy-futures = { version = "0.1.0", path = "../embassy-futures" } 22embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
23ppproto = { version = "0.2.1"} 23ppproto = { version = "0.2.1"}
24embassy-sync = { version = "0.7.0", path = "../embassy-sync" } 24embassy-sync = { version = "0.7.0", path = "../embassy-sync" }
diff --git a/embassy-net-wiznet/Cargo.toml b/embassy-net-wiznet/Cargo.toml
index a06a09302..3ff01f72b 100644
--- a/embassy-net-wiznet/Cargo.toml
+++ b/embassy-net-wiznet/Cargo.toml
@@ -12,7 +12,7 @@ documentation = "https://docs.embassy.dev/embassy-net-wiznet"
12[dependencies] 12[dependencies]
13embedded-hal = { version = "1.0" } 13embedded-hal = { version = "1.0" }
14embedded-hal-async = { version = "1.0" } 14embedded-hal-async = { version = "1.0" }
15embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } 15embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel" }
16embassy-time = { version = "0.4.0", path = "../embassy-time" } 16embassy-time = { version = "0.4.0", path = "../embassy-time" }
17embassy-futures = { version = "0.1.0", path = "../embassy-futures" } 17embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
18defmt = { version = "1.0.1", optional = true } 18defmt = { version = "1.0.1", optional = true }
diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs
index 63c2f4c75..482eb0e56 100644
--- a/embassy-net/src/udp.rs
+++ b/embassy-net/src/udp.rs
@@ -172,7 +172,7 @@ impl<'a> UdpSocket<'a> {
172 /// register the current task to be notified when a datagram is received. 172 /// register the current task to be notified when a datagram is received.
173 /// 173 ///
174 /// When a datagram is received, this method will call the provided function 174 /// When a datagram is received, this method will call the provided function
175 /// with the number of bytes received and the remote endpoint and return 175 /// with a reference to the received bytes and the remote endpoint and return
176 /// `Poll::Ready` with the function's returned value. 176 /// `Poll::Ready` with the function's returned value.
177 pub async fn recv_from_with<F, R>(&mut self, f: F) -> R 177 pub async fn recv_from_with<F, R>(&mut self, f: F) -> R
178 where 178 where
diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md
index ffa7997f7..a4cb8ceaf 100644
--- a/embassy-nrf/CHANGELOG.md
+++ b/embassy-nrf/CHANGELOG.md
@@ -5,7 +5,27 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8## Unreleased 8<!-- next-header -->
9## Unreleased - ReleaseDate
10
11## 0.5.0 - 2025-07-16
12
13- changed: update to latest embassy-usb-driver
14
15## 0.4.1 - 2025-07-14
16
17- changed: nrf52833: configure internal LDO
18- changed: nrf5340: add more options to clock config
19- bugfix: clean the SAADC's register while dropping
20- changed: Remove Peripheral trait, rename PeripheralRef->Peri.
21- changed: take pins before interrupts in buffered uart init
22- changed: nrf5340: add wdt support
23- changed: remove nrf radio BLE
24- changed: add Blocking/Async Mode param.
25- bugfix: fix PWM loop count
26- bugfix: fixing the nrf54l drive configuration bug
27- changed: add temp driver for nrf5340
28- changed: add support for rand 0.9
9 29
10## 0.3.1 - 2025-01-09 30## 0.3.1 - 2025-01-09
11 31
diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml
index 47eb92697..8fa20580d 100644
--- a/embassy-nrf/Cargo.toml
+++ b/embassy-nrf/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "embassy-nrf" 2name = "embassy-nrf"
3version = "0.3.1" 3version = "0.5.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6description = "Embassy Hardware Abstraction Layer (HAL) for nRF series microcontrollers" 6description = "Embassy Hardware Abstraction Layer (HAL) for nRF series microcontrollers"
@@ -144,9 +144,9 @@ embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", option
144embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } 144embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true }
145embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } 145embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true }
146embassy-sync = { version = "0.7.0", path = "../embassy-sync" } 146embassy-sync = { version = "0.7.0", path = "../embassy-sync" }
147embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } 147embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] }
148embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } 148embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal", default-features = false }
149embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } 149embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" }
150 150
151embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 151embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
152embedded-hal-1 = { package = "embedded-hal", version = "1.0" } 152embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
diff --git a/embassy-nrf/release.toml b/embassy-nrf/release.toml
new file mode 100644
index 000000000..fb6feaf21
--- /dev/null
+++ b/embassy-nrf/release.toml
@@ -0,0 +1,5 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs
index d02da9ac5..65f2d99f7 100644
--- a/embassy-nrf/src/gpio.rs
+++ b/embassy-nrf/src/gpio.rs
@@ -292,7 +292,7 @@ pub(crate) fn convert_drive(w: &mut pac::gpio::regs::PinCnf, drive: OutputDrive)
292 } 292 }
293 293
294 w.set_drive0(convert(drive.low)); 294 w.set_drive0(convert(drive.low));
295 w.set_drive0(convert(drive.high)); 295 w.set_drive1(convert(drive.high));
296 } 296 }
297} 297}
298 298
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index 9d44ae7e6..ba8206d13 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -240,11 +240,13 @@ macro_rules! bind_interrupts {
240 #[no_mangle] 240 #[no_mangle]
241 $(#[cfg($cond_irq)])? 241 $(#[cfg($cond_irq)])?
242 unsafe extern "C" fn $irq() { 242 unsafe extern "C" fn $irq() {
243 $( 243 unsafe {
244 $(#[cfg($cond_handler)])? 244 $(
245 <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); 245 $(#[cfg($cond_handler)])?
246 <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
246 247
247 )* 248 )*
249 }
248 } 250 }
249 251
250 $(#[cfg($cond_irq)])? 252 $(#[cfg($cond_irq)])?
@@ -584,8 +586,12 @@ pub mod config {
584#[allow(unused)] 586#[allow(unused)]
585mod consts { 587mod consts {
586 pub const UICR_APPROTECT: *mut u32 = 0x00FF8000 as *mut u32; 588 pub const UICR_APPROTECT: *mut u32 = 0x00FF8000 as *mut u32;
589 pub const UICR_HFXOSRC: *mut u32 = 0x00FF801C as *mut u32;
590 pub const UICR_HFXOCNT: *mut u32 = 0x00FF8020 as *mut u32;
587 pub const UICR_SECUREAPPROTECT: *mut u32 = 0x00FF802C as *mut u32; 591 pub const UICR_SECUREAPPROTECT: *mut u32 = 0x00FF802C as *mut u32;
588 pub const APPROTECT_ENABLED: u32 = 0x0000_0000; 592 pub const APPROTECT_ENABLED: u32 = 0x0000_0000;
593 #[cfg(feature = "_nrf9120")]
594 pub const APPROTECT_DISABLED: u32 = 0x50FA50FA;
589} 595}
590 596
591#[cfg(feature = "_nrf5340-app")] 597#[cfg(feature = "_nrf5340-app")]
@@ -648,13 +654,18 @@ unsafe fn uicr_write_masked(address: *mut u32, value: u32, mask: u32) -> WriteRe
648 return WriteResult::Failed; 654 return WriteResult::Failed;
649 } 655 }
650 656
651 let nvmc = pac::NVMC; 657 // Nrf9151 errata 7, need to disable interrups + use DSB https://docs.nordicsemi.com/bundle/errata_nRF9151_Rev2/page/ERR/nRF9151/Rev2/latest/anomaly_151_7.html
652 nvmc.config().write(|w| w.set_wen(pac::nvmc::vals::Wen::WEN)); 658 cortex_m::interrupt::free(|_cs| {
653 while !nvmc.ready().read().ready() {} 659 let nvmc = pac::NVMC;
654 address.write_volatile(value | !mask); 660
655 while !nvmc.ready().read().ready() {} 661 nvmc.config().write(|w| w.set_wen(pac::nvmc::vals::Wen::WEN));
656 nvmc.config().write(|_| {}); 662 while !nvmc.ready().read().ready() {}
657 while !nvmc.ready().read().ready() {} 663 address.write_volatile(value | !mask);
664 cortex_m::asm::dsb();
665 while !nvmc.ready().read().ready() {}
666 nvmc.config().write(|_| {});
667 while !nvmc.ready().read().ready() {}
668 });
658 669
659 WriteResult::Written 670 WriteResult::Written
660} 671}
@@ -672,6 +683,28 @@ pub fn init(config: config::Config) -> Peripherals {
672 #[allow(unused_mut)] 683 #[allow(unused_mut)]
673 let mut needs_reset = false; 684 let mut needs_reset = false;
674 685
686 // Workaround used in the nrf mdk: file system_nrf91.c , function SystemInit(), after `#if !defined(NRF_SKIP_UICR_HFXO_WORKAROUND)`
687 #[cfg(all(feature = "_nrf91", feature = "_s"))]
688 {
689 let uicr = pac::UICR_S;
690 let hfxocnt = uicr.hfxocnt().read().hfxocnt().to_bits();
691 let hfxosrc = uicr.hfxosrc().read().hfxosrc().to_bits();
692
693 if hfxosrc == 1 {
694 unsafe {
695 let _ = uicr_write(consts::UICR_HFXOSRC, 0);
696 }
697 needs_reset = true;
698 }
699
700 if hfxocnt == 255 {
701 unsafe {
702 let _ = uicr_write(consts::UICR_HFXOCNT, 32);
703 }
704 needs_reset = true;
705 }
706 }
707
675 // Setup debug protection. 708 // Setup debug protection.
676 #[cfg(not(feature = "_nrf51"))] 709 #[cfg(not(feature = "_nrf51"))]
677 match config.debug { 710 match config.debug {
@@ -768,6 +801,28 @@ pub fn init(config: config::Config) -> Peripherals {
768 } 801 }
769 802
770 // nothing to do on the nrf9160, debug is allowed by default. 803 // nothing to do on the nrf9160, debug is allowed by default.
804
805 // nrf9151, nrf9161 use the new-style approtect that requires writing a register.
806 #[cfg(feature = "nrf9120-s")]
807 unsafe {
808 let p = pac::APPROTECT_S;
809
810 let res = uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_DISABLED);
811 needs_reset |= res == WriteResult::Written;
812 p.approtect()
813 .disable()
814 .write(|w| w.set_disable(pac::approtect::vals::ApprotectDisableDisable::SW_UNPROTECTED));
815
816 let res = uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_DISABLED);
817 needs_reset |= res == WriteResult::Written;
818 p.secureapprotect()
819 .disable()
820 .write(|w| w.set_disable(pac::approtect::vals::SecureapprotectDisableDisable::SW_UNPROTECTED));
821
822 // TODO: maybe add workaround for this errata
823 // It uses extra power, not sure how to let the user choose.
824 // https://docs.nordicsemi.com/bundle/errata_nRF9151_Rev1/page/ERR/nRF9151/Rev1/latest/anomaly_151_36.html#anomaly_151_36
825 }
771 } 826 }
772 config::Debug::Disallowed => { 827 config::Debug::Disallowed => {
773 // TODO: Handle nRF54L 828 // TODO: Handle nRF54L
@@ -783,6 +838,13 @@ pub fn init(config: config::Config) -> Peripherals {
783 let res = uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_ENABLED); 838 let res = uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_ENABLED);
784 needs_reset |= res == WriteResult::Written; 839 needs_reset |= res == WriteResult::Written;
785 } 840 }
841
842 #[cfg(feature = "nrf9120-s")]
843 {
844 let p = pac::APPROTECT_S;
845 p.approtect().forceprotect().write(|w| w.set_forceprotect(true));
846 p.secureapprotect().forceprotect().write(|w| w.set_forceprotect(true));
847 }
786 } 848 }
787 } 849 }
788 config::Debug::NotConfigured => {} 850 config::Debug::NotConfigured => {}
diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs
index a2e153e26..3d76272ac 100644
--- a/embassy-nrf/src/pwm.rs
+++ b/embassy-nrf/src/pwm.rs
@@ -479,9 +479,8 @@ impl<'d, 's, T: Instance> Sequencer<'d, 's, T> {
479 let seqstart_index = if start_seq == StartSequence::One { 1 } else { 0 }; 479 let seqstart_index = if start_seq == StartSequence::One { 1 } else { 0 };
480 480
481 match times { 481 match times {
482 // just the one time, no loop count 482 SequenceMode::Loop(n) => {
483 SequenceMode::Loop(_) => { 483 r.loop_().write(|w| w.set_cnt(vals::LoopCnt(n)));
484 r.loop_().write(|w| w.set_cnt(vals::LoopCnt::DISABLED));
485 } 484 }
486 // to play infinitely, repeat the sequence one time, then have loops done self trigger seq0 again 485 // to play infinitely, repeat the sequence one time, then have loops done self trigger seq0 again
487 SequenceMode::Infinite => { 486 SequenceMode::Infinite => {
diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs
index f377df49e..927a0ac08 100644
--- a/embassy-nrf/src/uarte.rs
+++ b/embassy-nrf/src/uarte.rs
@@ -245,7 +245,7 @@ impl<'d, T: Instance> Uarte<'d, T> {
245 } 245 }
246 246
247 /// Return the endtx event for use with PPI 247 /// Return the endtx event for use with PPI
248 pub fn event_endtx(&self) -> Event { 248 pub fn event_endtx(&self) -> Event<'_> {
249 let r = T::regs(); 249 let r = T::regs();
250 Event::from_reg(r.events_endtx()) 250 Event::from_reg(r.events_endtx())
251 } 251 }
diff --git a/embassy-nrf/src/usb/mod.rs b/embassy-nrf/src/usb/mod.rs
index 6cc1b0111..c6970fc0f 100644
--- a/embassy-nrf/src/usb/mod.rs
+++ b/embassy-nrf/src/usb/mod.rs
@@ -121,10 +121,11 @@ impl<'d, T: Instance, V: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, V
121 fn alloc_endpoint_in( 121 fn alloc_endpoint_in(
122 &mut self, 122 &mut self,
123 ep_type: EndpointType, 123 ep_type: EndpointType,
124 ep_addr: Option<EndpointAddress>,
124 packet_size: u16, 125 packet_size: u16,
125 interval_ms: u8, 126 interval_ms: u8,
126 ) -> Result<Self::EndpointIn, driver::EndpointAllocError> { 127 ) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
127 let index = self.alloc_in.allocate(ep_type)?; 128 let index = self.alloc_in.allocate(ep_type, ep_addr)?;
128 let ep_addr = EndpointAddress::from_parts(index, Direction::In); 129 let ep_addr = EndpointAddress::from_parts(index, Direction::In);
129 Ok(Endpoint::new(EndpointInfo { 130 Ok(Endpoint::new(EndpointInfo {
130 addr: ep_addr, 131 addr: ep_addr,
@@ -137,10 +138,11 @@ impl<'d, T: Instance, V: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, V
137 fn alloc_endpoint_out( 138 fn alloc_endpoint_out(
138 &mut self, 139 &mut self,
139 ep_type: EndpointType, 140 ep_type: EndpointType,
141 ep_addr: Option<EndpointAddress>,
140 packet_size: u16, 142 packet_size: u16,
141 interval_ms: u8, 143 interval_ms: u8,
142 ) -> Result<Self::EndpointOut, driver::EndpointAllocError> { 144 ) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
143 let index = self.alloc_out.allocate(ep_type)?; 145 let index = self.alloc_out.allocate(ep_type, ep_addr)?;
144 let ep_addr = EndpointAddress::from_parts(index, Direction::Out); 146 let ep_addr = EndpointAddress::from_parts(index, Direction::Out);
145 Ok(Endpoint::new(EndpointInfo { 147 Ok(Endpoint::new(EndpointInfo {
146 addr: ep_addr, 148 addr: ep_addr,
@@ -734,7 +736,11 @@ impl Allocator {
734 Self { used: 0 } 736 Self { used: 0 }
735 } 737 }
736 738
737 fn allocate(&mut self, ep_type: EndpointType) -> Result<usize, driver::EndpointAllocError> { 739 fn allocate(
740 &mut self,
741 ep_type: EndpointType,
742 ep_addr: Option<EndpointAddress>,
743 ) -> Result<usize, driver::EndpointAllocError> {
738 // Endpoint addresses are fixed in hardware: 744 // Endpoint addresses are fixed in hardware:
739 // - 0x80 / 0x00 - Control EP0 745 // - 0x80 / 0x00 - Control EP0
740 // - 0x81 / 0x01 - Bulk/Interrupt EP1 746 // - 0x81 / 0x01 - Bulk/Interrupt EP1
@@ -748,16 +754,37 @@ impl Allocator {
748 754
749 // Endpoint directions are allocated individually. 755 // Endpoint directions are allocated individually.
750 756
751 let alloc_index = match ep_type { 757 let alloc_index = if let Some(addr) = ep_addr {
752 EndpointType::Isochronous => 8, 758 // Use the specified endpoint address
753 EndpointType::Control => return Err(driver::EndpointAllocError), 759 let requested_index = addr.index();
754 EndpointType::Interrupt | EndpointType::Bulk => { 760 // Validate the requested index based on endpoint type
755 // Find rightmost zero bit in 1..=7 761 match ep_type {
756 let ones = (self.used >> 1).trailing_ones() as usize; 762 EndpointType::Isochronous => {
757 if ones >= 7 { 763 if requested_index != 8 {
758 return Err(driver::EndpointAllocError); 764 return Err(driver::EndpointAllocError);
765 }
766 }
767 EndpointType::Control => return Err(driver::EndpointAllocError),
768 EndpointType::Interrupt | EndpointType::Bulk => {
769 if requested_index < 1 || requested_index > 7 {
770 return Err(driver::EndpointAllocError);
771 }
772 }
773 }
774 requested_index
775 } else {
776 // Allocate any available endpoint
777 match ep_type {
778 EndpointType::Isochronous => 8,
779 EndpointType::Control => return Err(driver::EndpointAllocError),
780 EndpointType::Interrupt | EndpointType::Bulk => {
781 // Find rightmost zero bit in 1..=7
782 let ones = (self.used >> 1).trailing_ones() as usize;
783 if ones >= 7 {
784 return Err(driver::EndpointAllocError);
785 }
786 ones + 1
759 } 787 }
760 ones + 1
761 } 788 }
762 }; 789 };
763 790
diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml
index 426af06a0..14fa7201d 100644
--- a/embassy-nxp/Cargo.toml
+++ b/embassy-nxp/Cargo.toml
@@ -3,18 +3,69 @@ name = "embassy-nxp"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5 5
6[package.metadata.embassy_docs]
7src_base = "https://github.com/embassy-rs/embassy/blob/embassy-nxp-v$VERSION/embassy-nxp/src/"
8src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-nxp/src/"
9features = ["defmt", "unstable-pac" ] # TODO: Add time-driver-any, as both lpc55 and mimxrt1xxx use different drivers.
10
11flavors = [
12 { regex_feature = "lpc55", target = "thumbv8m.main-none-eabihf" },
13 { regex_feature = "mimxrt.*", target = "thumbv7em-none-eabihf" },
14]
15
6[dependencies] 16[dependencies]
7cortex-m = "0.7.7" 17cortex-m = "0.7.7"
8cortex-m-rt = "0.7.0" 18cortex-m-rt = "0.7.0"
9critical-section = "1.1.2" 19critical-section = "1.1.2"
10embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } 20embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] }
11embassy-sync = { version = "0.7.0", path = "../embassy-sync" } 21embassy-sync = { version = "0.7.0", path = "../embassy-sync" }
12lpc55-pac = "0.5.0"
13defmt = { version = "1", optional = true } 22defmt = { version = "1", optional = true }
23log = { version = "0.4.27", optional = true }
24embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true }
25embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true }
26embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true }
27
28## Chip dependencies
29lpc55-pac = { version = "0.5.0", optional = true }
30nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "be4dd0936c20d5897364a381b1d95a99514c1e7e" }
31
32imxrt-rt = { version = "0.1.7", optional = true, features = ["device"] }
33
34[build-dependencies]
35cfg_aliases = "0.2.1"
36nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "be4dd0936c20d5897364a381b1d95a99514c1e7e", features = ["metadata"], optional = true }
37proc-macro2 = "1.0.95"
38quote = "1.0.15"
14 39
15[features] 40[features]
16default = ["rt"] 41default = ["rt"]
17rt = ["lpc55-pac/rt"] 42# Enable PACs as optional dependencies, since some chip families will use different pac crates (temporarily).
43rt = ["lpc55-pac?/rt", "nxp-pac?/rt"]
18 44
19## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers. 45## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers.
20defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt"] 46defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt"]
47
48log = ["dep:log"]
49
50## Use Periodic Interrupt Timer (PIT) as the time driver for `embassy-time`, with a tick rate of 1 MHz
51time-driver-pit = ["_time_driver", "embassy-time?/tick-hz-1_000_000"]
52
53## Reexport the PAC for the currently enabled chip at `embassy_nxp::pac` (unstable)
54unstable-pac = []
55# This is unstable because semver-minor (non-breaking) releases of embassy-nxp may major-bump (breaking) the PAC version.
56# If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC.
57# There are no plans to make this stable.
58
59## internal use only
60#
61# This feature is unfortunately a hack around the fact that cfg_aliases cannot apply to the buildscript
62# that creates the aliases.
63_rt1xxx = []
64
65# A timer driver is enabled.
66_time_driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils"]
67
68#! ### Chip selection features
69lpc55 = ["dep:lpc55-pac"]
70mimxrt1011 = ["nxp-pac/mimxrt1011", "_rt1xxx", "dep:imxrt-rt"]
71mimxrt1062 = ["nxp-pac/mimxrt1062", "_rt1xxx", "dep:imxrt-rt"]
diff --git a/embassy-nxp/build.rs b/embassy-nxp/build.rs
new file mode 100644
index 000000000..f3c062c87
--- /dev/null
+++ b/embassy-nxp/build.rs
@@ -0,0 +1,138 @@
1use std::io::Write;
2use std::path::{Path, PathBuf};
3use std::process::Command;
4use std::{env, fs};
5
6use cfg_aliases::cfg_aliases;
7#[cfg(feature = "_rt1xxx")]
8use nxp_pac::metadata;
9#[allow(unused)]
10use proc_macro2::TokenStream;
11#[allow(unused)]
12use quote::quote;
13
14#[path = "./build_common.rs"]
15mod common;
16
17fn main() {
18 let mut cfgs = common::CfgSet::new();
19 common::set_target_cfgs(&mut cfgs);
20
21 let chip_name = match env::vars()
22 .map(|(a, _)| a)
23 .filter(|x| x.starts_with("CARGO_FEATURE_MIMXRT") || x.starts_with("CARGO_FEATURE_LPC"))
24 .get_one()
25 {
26 Ok(x) => x,
27 Err(GetOneError::None) => panic!("No mimxrt/lpc Cargo feature enabled"),
28 Err(GetOneError::Multiple) => panic!("Multiple mimxrt/lpc Cargo features enabled"),
29 }
30 .strip_prefix("CARGO_FEATURE_")
31 .unwrap()
32 .to_ascii_lowercase();
33
34 cfg_aliases! {
35 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 }
42
43 eprintln!("chip: {chip_name}");
44
45 generate_code();
46}
47
48#[cfg(feature = "_rt1xxx")]
49fn generate_iomuxc() -> TokenStream {
50 use proc_macro2::{Ident, Span};
51
52 let pads = metadata::iomuxc::IOMUXC_REGISTERS.iter().map(|registers| {
53 let name = Ident::new(&registers.name, Span::call_site());
54 let address = registers.pad_ctl;
55
56 quote! {
57 pub const #name: u32 = #address;
58 }
59 });
60
61 let muxes = metadata::iomuxc::IOMUXC_REGISTERS.iter().map(|registers| {
62 let name = Ident::new(&registers.name, Span::call_site());
63 let address = registers.mux_ctl;
64
65 quote! {
66 pub const #name: u32 = #address;
67 }
68 });
69
70 quote! {
71 pub mod iomuxc {
72 pub mod pads {
73 #(#pads)*
74 }
75
76 pub mod muxes {
77 #(#muxes)*
78 }
79 }
80 }
81}
82
83fn generate_code() {
84 #[allow(unused)]
85 use std::fmt::Write;
86
87 let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
88 #[allow(unused_mut)]
89 let mut output = String::new();
90
91 #[cfg(feature = "_rt1xxx")]
92 writeln!(&mut output, "{}", generate_iomuxc()).unwrap();
93
94 let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string();
95 fs::write(&out_file, output).unwrap();
96 rustfmt(&out_file);
97}
98
99/// rustfmt a given path.
100/// Failures are logged to stderr and ignored.
101fn rustfmt(path: impl AsRef<Path>) {
102 let path = path.as_ref();
103 match Command::new("rustfmt").args([path]).output() {
104 Err(e) => {
105 eprintln!("failed to exec rustfmt {:?}: {:?}", path, e);
106 }
107 Ok(out) => {
108 if !out.status.success() {
109 eprintln!("rustfmt {:?} failed:", path);
110 eprintln!("=== STDOUT:");
111 std::io::stderr().write_all(&out.stdout).unwrap();
112 eprintln!("=== STDERR:");
113 std::io::stderr().write_all(&out.stderr).unwrap();
114 }
115 }
116 }
117}
118
119enum GetOneError {
120 None,
121 Multiple,
122}
123
124trait IteratorExt: Iterator {
125 fn get_one(self) -> Result<Self::Item, GetOneError>;
126}
127
128impl<T: Iterator> IteratorExt for T {
129 fn get_one(mut self) -> Result<Self::Item, GetOneError> {
130 match self.next() {
131 None => Err(GetOneError::None),
132 Some(res) => match self.next() {
133 Some(_) => Err(GetOneError::Multiple),
134 None => Ok(res),
135 },
136 }
137 }
138}
diff --git a/embassy-nxp/build_common.rs b/embassy-nxp/build_common.rs
new file mode 100644
index 000000000..4f24e6d37
--- /dev/null
+++ b/embassy-nxp/build_common.rs
@@ -0,0 +1,94 @@
1// NOTE: this file is copy-pasted between several Embassy crates, because there is no
2// straightforward way to share this code:
3// - it cannot be placed into the root of the repo and linked from each build.rs using `#[path =
4// "../build_common.rs"]`, because `cargo publish` requires that all files published with a crate
5// reside in the crate's directory,
6// - it cannot be symlinked from `embassy-xxx/build_common.rs` to `../build_common.rs`, because
7// symlinks don't work on Windows.
8
9use std::collections::HashSet;
10use std::env;
11
12/// Helper for emitting cargo instruction for enabling configs (`cargo:rustc-cfg=X`) and declaring
13/// them (`cargo:rust-check-cfg=cfg(X)`).
14#[derive(Debug)]
15pub struct CfgSet {
16 enabled: HashSet<String>,
17 declared: HashSet<String>,
18}
19
20impl CfgSet {
21 pub fn new() -> Self {
22 Self {
23 enabled: HashSet::new(),
24 declared: HashSet::new(),
25 }
26 }
27
28 /// Enable a config, which can then be used in `#[cfg(...)]` for conditional compilation.
29 ///
30 /// All configs that can potentially be enabled should be unconditionally declared using
31 /// [`Self::declare()`].
32 pub fn enable(&mut self, cfg: impl AsRef<str>) {
33 if self.enabled.insert(cfg.as_ref().to_owned()) {
34 println!("cargo:rustc-cfg={}", cfg.as_ref());
35 }
36 }
37
38 pub fn enable_all(&mut self, cfgs: &[impl AsRef<str>]) {
39 for cfg in cfgs.iter() {
40 self.enable(cfg.as_ref());
41 }
42 }
43
44 /// Declare a valid config for conditional compilation, without enabling it.
45 ///
46 /// This enables rustc to check that the configs in `#[cfg(...)]` attributes are valid.
47 pub fn declare(&mut self, cfg: impl AsRef<str>) {
48 if self.declared.insert(cfg.as_ref().to_owned()) {
49 println!("cargo:rustc-check-cfg=cfg({})", cfg.as_ref());
50 }
51 }
52
53 pub fn declare_all(&mut self, cfgs: &[impl AsRef<str>]) {
54 for cfg in cfgs.iter() {
55 self.declare(cfg.as_ref());
56 }
57 }
58
59 pub fn set(&mut self, cfg: impl Into<String>, enable: bool) {
60 let cfg = cfg.into();
61 if enable {
62 self.enable(cfg.clone());
63 }
64 self.declare(cfg);
65 }
66}
67
68/// Sets configs that describe the target platform.
69pub fn set_target_cfgs(cfgs: &mut CfgSet) {
70 let target = env::var("TARGET").unwrap();
71
72 if target.starts_with("thumbv6m-") {
73 cfgs.enable_all(&["cortex_m", "armv6m"]);
74 } else if target.starts_with("thumbv7m-") {
75 cfgs.enable_all(&["cortex_m", "armv7m"]);
76 } else if target.starts_with("thumbv7em-") {
77 cfgs.enable_all(&["cortex_m", "armv7m", "armv7em"]);
78 } else if target.starts_with("thumbv8m.base") {
79 cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_base"]);
80 } else if target.starts_with("thumbv8m.main") {
81 cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_main"]);
82 }
83 cfgs.declare_all(&[
84 "cortex_m",
85 "armv6m",
86 "armv7m",
87 "armv7em",
88 "armv8m",
89 "armv8m_base",
90 "armv8m_main",
91 ]);
92
93 cfgs.set("has_fpu", target.ends_with("-eabihf"));
94}
diff --git a/embassy-nxp/src/chips/lpc55.rs b/embassy-nxp/src/chips/lpc55.rs
new file mode 100644
index 000000000..c95218af0
--- /dev/null
+++ b/embassy-nxp/src/chips/lpc55.rs
@@ -0,0 +1,70 @@
1pub use lpc55_pac as pac;
2
3embassy_hal_internal::peripherals! {
4 // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other
5 // peripheral types (e.g. I2C).
6 PIO0_0,
7 PIO0_1,
8 PIO0_2,
9 PIO0_3,
10 PIO0_4,
11 PIO0_5,
12 PIO0_6,
13 PIO0_7,
14 PIO0_8,
15 PIO0_9,
16 PIO0_10,
17 PIO0_11,
18 PIO0_12,
19 PIO0_13,
20 PIO0_14,
21 PIO0_15,
22 PIO0_16,
23 PIO0_17,
24 PIO0_18,
25 PIO0_19,
26 PIO0_20,
27 PIO0_21,
28 PIO0_22,
29 PIO0_23,
30 PIO0_24,
31 PIO0_25,
32 PIO0_26,
33 PIO0_27,
34 PIO0_28,
35 PIO0_29,
36 PIO0_30,
37 PIO0_31,
38 PIO1_0,
39 PIO1_1,
40 PIO1_2,
41 PIO1_3,
42 PIO1_4,
43 PIO1_5,
44 PIO1_6,
45 PIO1_7,
46 PIO1_8,
47 PIO1_9,
48 PIO1_10,
49 PIO1_11,
50 PIO1_12,
51 PIO1_13,
52 PIO1_14,
53 PIO1_15,
54 PIO1_16,
55 PIO1_17,
56 PIO1_18,
57 PIO1_19,
58 PIO1_20,
59 PIO1_21,
60 PIO1_22,
61 PIO1_23,
62 PIO1_24,
63 PIO1_25,
64 PIO1_26,
65 PIO1_27,
66 PIO1_28,
67 PIO1_29,
68 PIO1_30,
69 PIO1_31,
70}
diff --git a/embassy-nxp/src/chips/mimxrt1011.rs b/embassy-nxp/src/chips/mimxrt1011.rs
new file mode 100644
index 000000000..a74d953fc
--- /dev/null
+++ b/embassy-nxp/src/chips/mimxrt1011.rs
@@ -0,0 +1,113 @@
1// This must be imported so that __preinit is defined.
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
106pub(crate) mod _generated {
107 #![allow(dead_code)]
108 #![allow(unused_imports)]
109 #![allow(non_snake_case)]
110 #![allow(missing_docs)]
111
112 include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
113}
diff --git a/embassy-nxp/src/chips/mimxrt1062.rs b/embassy-nxp/src/chips/mimxrt1062.rs
new file mode 100644
index 000000000..ef153bd66
--- /dev/null
+++ b/embassy-nxp/src/chips/mimxrt1062.rs
@@ -0,0 +1,282 @@
1// This must be imported so that __preinit is defined.
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
275pub(crate) mod _generated {
276 #![allow(dead_code)]
277 #![allow(unused_imports)]
278 #![allow(non_snake_case)]
279 #![allow(missing_docs)]
280
281 include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
282}
diff --git a/embassy-nxp/src/fmt.rs b/embassy-nxp/src/fmt.rs
new file mode 100644
index 000000000..27d41ace6
--- /dev/null
+++ b/embassy-nxp/src/fmt.rs
@@ -0,0 +1,284 @@
1//! Copied from embassy-rp
2
3#![macro_use]
4#![allow(unused)]
5
6use core::fmt::{Debug, Display, LowerHex};
7
8#[cfg(all(feature = "defmt", feature = "log"))]
9compile_error!("You may not enable both `defmt` and `log` features.");
10
11#[collapse_debuginfo(yes)]
12macro_rules! assert {
13 ($($x:tt)*) => {
14 {
15 #[cfg(not(feature = "defmt"))]
16 ::core::assert!($($x)*);
17 #[cfg(feature = "defmt")]
18 ::defmt::assert!($($x)*);
19 }
20 };
21}
22
23#[collapse_debuginfo(yes)]
24macro_rules! assert_eq {
25 ($($x:tt)*) => {
26 {
27 #[cfg(not(feature = "defmt"))]
28 ::core::assert_eq!($($x)*);
29 #[cfg(feature = "defmt")]
30 ::defmt::assert_eq!($($x)*);
31 }
32 };
33}
34
35#[collapse_debuginfo(yes)]
36macro_rules! assert_ne {
37 ($($x:tt)*) => {
38 {
39 #[cfg(not(feature = "defmt"))]
40 ::core::assert_ne!($($x)*);
41 #[cfg(feature = "defmt")]
42 ::defmt::assert_ne!($($x)*);
43 }
44 };
45}
46
47#[collapse_debuginfo(yes)]
48macro_rules! debug_assert {
49 ($($x:tt)*) => {
50 {
51 #[cfg(not(feature = "defmt"))]
52 ::core::debug_assert!($($x)*);
53 #[cfg(feature = "defmt")]
54 ::defmt::debug_assert!($($x)*);
55 }
56 };
57}
58
59#[collapse_debuginfo(yes)]
60macro_rules! debug_assert_eq {
61 ($($x:tt)*) => {
62 {
63 #[cfg(not(feature = "defmt"))]
64 ::core::debug_assert_eq!($($x)*);
65 #[cfg(feature = "defmt")]
66 ::defmt::debug_assert_eq!($($x)*);
67 }
68 };
69}
70
71#[collapse_debuginfo(yes)]
72macro_rules! debug_assert_ne {
73 ($($x:tt)*) => {
74 {
75 #[cfg(not(feature = "defmt"))]
76 ::core::debug_assert_ne!($($x)*);
77 #[cfg(feature = "defmt")]
78 ::defmt::debug_assert_ne!($($x)*);
79 }
80 };
81}
82
83#[collapse_debuginfo(yes)]
84macro_rules! todo {
85 ($($x:tt)*) => {
86 {
87 #[cfg(not(feature = "defmt"))]
88 ::core::todo!($($x)*);
89 #[cfg(feature = "defmt")]
90 ::defmt::todo!($($x)*);
91 }
92 };
93}
94
95#[collapse_debuginfo(yes)]
96macro_rules! unreachable {
97 ($($x:tt)*) => {
98 {
99 #[cfg(not(feature = "defmt"))]
100 ::core::unreachable!($($x)*);
101 #[cfg(feature = "defmt")]
102 ::defmt::unreachable!($($x)*);
103 }
104 };
105}
106
107#[collapse_debuginfo(yes)]
108macro_rules! unimplemented {
109 ($($x:tt)*) => {
110 {
111 #[cfg(not(feature = "defmt"))]
112 ::core::unimplemented!($($x)*);
113 #[cfg(feature = "defmt")]
114 ::defmt::unimplemented!($($x)*);
115 }
116 };
117}
118
119#[collapse_debuginfo(yes)]
120macro_rules! panic {
121 ($($x:tt)*) => {
122 {
123 #[cfg(not(feature = "defmt"))]
124 ::core::panic!($($x)*);
125 #[cfg(feature = "defmt")]
126 ::defmt::panic!($($x)*);
127 }
128 };
129}
130
131#[collapse_debuginfo(yes)]
132macro_rules! trace {
133 ($s:literal $(, $x:expr)* $(,)?) => {
134 {
135 #[cfg(feature = "log")]
136 ::log::trace!($s $(, $x)*);
137 #[cfg(feature = "defmt")]
138 ::defmt::trace!($s $(, $x)*);
139 #[cfg(not(any(feature = "log", feature="defmt")))]
140 let _ = ($( & $x ),*);
141 }
142 };
143}
144
145#[collapse_debuginfo(yes)]
146macro_rules! debug {
147 ($s:literal $(, $x:expr)* $(,)?) => {
148 {
149 #[cfg(feature = "log")]
150 ::log::debug!($s $(, $x)*);
151 #[cfg(feature = "defmt")]
152 ::defmt::debug!($s $(, $x)*);
153 #[cfg(not(any(feature = "log", feature="defmt")))]
154 let _ = ($( & $x ),*);
155 }
156 };
157}
158
159#[collapse_debuginfo(yes)]
160macro_rules! info {
161 ($s:literal $(, $x:expr)* $(,)?) => {
162 {
163 #[cfg(feature = "log")]
164 ::log::info!($s $(, $x)*);
165 #[cfg(feature = "defmt")]
166 ::defmt::info!($s $(, $x)*);
167 #[cfg(not(any(feature = "log", feature="defmt")))]
168 let _ = ($( & $x ),*);
169 }
170 };
171}
172
173#[collapse_debuginfo(yes)]
174macro_rules! warn {
175 ($s:literal $(, $x:expr)* $(,)?) => {
176 {
177 #[cfg(feature = "log")]
178 ::log::warn!($s $(, $x)*);
179 #[cfg(feature = "defmt")]
180 ::defmt::warn!($s $(, $x)*);
181 #[cfg(not(any(feature = "log", feature="defmt")))]
182 let _ = ($( & $x ),*);
183 }
184 };
185}
186
187#[collapse_debuginfo(yes)]
188macro_rules! error {
189 ($s:literal $(, $x:expr)* $(,)?) => {
190 {
191 #[cfg(feature = "log")]
192 ::log::error!($s $(, $x)*);
193 #[cfg(feature = "defmt")]
194 ::defmt::error!($s $(, $x)*);
195 #[cfg(not(any(feature = "log", feature="defmt")))]
196 let _ = ($( & $x ),*);
197 }
198 };
199}
200
201#[cfg(feature = "defmt")]
202#[collapse_debuginfo(yes)]
203macro_rules! unwrap {
204 ($($x:tt)*) => {
205 ::defmt::unwrap!($($x)*)
206 };
207}
208
209#[cfg(not(feature = "defmt"))]
210#[collapse_debuginfo(yes)]
211macro_rules! unwrap {
212 ($arg:expr) => {
213 match $crate::fmt::Try::into_result($arg) {
214 ::core::result::Result::Ok(t) => t,
215 ::core::result::Result::Err(e) => {
216 ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
217 }
218 }
219 };
220 ($arg:expr, $($msg:expr),+ $(,)? ) => {
221 match $crate::fmt::Try::into_result($arg) {
222 ::core::result::Result::Ok(t) => t,
223 ::core::result::Result::Err(e) => {
224 ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
225 }
226 }
227 }
228}
229
230#[derive(Debug, Copy, Clone, Eq, PartialEq)]
231pub struct NoneError;
232
233pub trait Try {
234 type Ok;
235 type Error;
236 fn into_result(self) -> Result<Self::Ok, Self::Error>;
237}
238
239impl<T> Try for Option<T> {
240 type Ok = T;
241 type Error = NoneError;
242
243 #[inline]
244 fn into_result(self) -> Result<T, NoneError> {
245 self.ok_or(NoneError)
246 }
247}
248
249impl<T, E> Try for Result<T, E> {
250 type Ok = T;
251 type Error = E;
252
253 #[inline]
254 fn into_result(self) -> Self {
255 self
256 }
257}
258
259pub(crate) struct Bytes<'a>(pub &'a [u8]);
260
261impl<'a> Debug for Bytes<'a> {
262 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
263 write!(f, "{:#02x?}", self.0)
264 }
265}
266
267impl<'a> Display for Bytes<'a> {
268 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
269 write!(f, "{:#02x?}", self.0)
270 }
271}
272
273impl<'a> LowerHex for Bytes<'a> {
274 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
275 write!(f, "{:#02x?}", self.0)
276 }
277}
278
279#[cfg(feature = "defmt")]
280impl<'a> defmt::Format for Bytes<'a> {
281 fn format(&self, fmt: defmt::Formatter) {
282 defmt::write!(fmt, "{:02x}", self.0)
283 }
284}
diff --git a/embassy-nxp/src/gpio.rs b/embassy-nxp/src/gpio.rs
index c7c78ce61..3049cc12d 100644
--- a/embassy-nxp/src/gpio.rs
+++ b/embassy-nxp/src/gpio.rs
@@ -1,354 +1,7 @@
1use embassy_hal_internal::{impl_peripheral, PeripheralType}; 1//! General purpose input/output (GPIO) driver.
2#![macro_use]
2 3
3use crate::pac_utils::*; 4#[cfg_attr(feature = "lpc55", path = "./gpio/lpc55.rs")]
4use crate::{peripherals, Peri}; 5#[cfg_attr(rt1xxx, path = "./gpio/rt1xxx.rs")]
5 6mod inner;
6pub(crate) fn init() { 7pub use inner::*;
7 // Enable clocks for GPIO, PINT, and IOCON
8 syscon_reg()
9 .ahbclkctrl0
10 .modify(|_, w| w.gpio0().enable().gpio1().enable().mux().enable().iocon().enable());
11}
12
13/// The GPIO pin level for pins set on "Digital" mode.
14#[derive(Debug, Eq, PartialEq, Clone, Copy)]
15pub enum Level {
16 /// Logical low. Corresponds to 0V.
17 Low,
18 /// Logical high. Corresponds to VDD.
19 High,
20}
21
22/// Pull setting for a GPIO input set on "Digital" mode.
23#[derive(Debug, Clone, Copy, Eq, PartialEq)]
24pub enum Pull {
25 /// No pull.
26 None,
27 /// Internal pull-up resistor.
28 Up,
29 /// Internal pull-down resistor.
30 Down,
31}
32
33/// The LPC55 boards have two GPIO banks, each with 32 pins. This enum represents the two banks.
34#[derive(Debug, Eq, PartialEq, Clone, Copy)]
35pub enum Bank {
36 Bank0 = 0,
37 Bank1 = 1,
38}
39
40/// GPIO output driver. Internally, this is a specialized [Flex] pin.
41pub struct Output<'d> {
42 pub(crate) pin: Flex<'d>,
43}
44
45impl<'d> Output<'d> {
46 /// Create GPIO output driver for a [Pin] with the provided [initial output](Level).
47 #[inline]
48 pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self {
49 let mut pin = Flex::new(pin);
50 pin.set_as_output();
51 let mut result = Self { pin };
52
53 match initial_output {
54 Level::High => result.set_high(),
55 Level::Low => result.set_low(),
56 };
57
58 result
59 }
60
61 pub fn set_high(&mut self) {
62 gpio_reg().set[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) })
63 }
64
65 pub fn set_low(&mut self) {
66 gpio_reg().clr[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) })
67 }
68
69 pub fn toggle(&mut self) {
70 gpio_reg().not[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) })
71 }
72
73 /// Get the current output level of the pin. Note that the value returned by this function is
74 /// the voltage level reported by the pin, not the value set by the output driver.
75 pub fn level(&self) -> Level {
76 let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits();
77 if bits & self.pin.bit() != 0 {
78 Level::High
79 } else {
80 Level::Low
81 }
82 }
83}
84
85/// GPIO input driver. Internally, this is a specialized [Flex] pin.
86pub struct Input<'d> {
87 pub(crate) pin: Flex<'d>,
88}
89
90impl<'d> Input<'d> {
91 /// Create GPIO output driver for a [Pin] with the provided [Pull].
92 #[inline]
93 pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self {
94 let mut pin = Flex::new(pin);
95 pin.set_as_input();
96 let mut result = Self { pin };
97 result.set_pull(pull);
98
99 result
100 }
101
102 /// Set the pull configuration for the pin. To disable the pull, use [Pull::None].
103 pub fn set_pull(&mut self, pull: Pull) {
104 match_iocon!(register, iocon_reg(), self.pin.pin_bank(), self.pin.pin_number(), {
105 register.modify(|_, w| match pull {
106 Pull::None => w.mode().inactive(),
107 Pull::Up => w.mode().pull_up(),
108 Pull::Down => w.mode().pull_down(),
109 });
110 });
111 }
112
113 /// Get the current input level of the pin.
114 pub fn read(&self) -> Level {
115 let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits();
116 if bits & self.pin.bit() != 0 {
117 Level::High
118 } else {
119 Level::Low
120 }
121 }
122}
123
124/// A flexible GPIO (digital mode) pin whose mode is not yet determined. Under the hood, this is a
125/// reference to a type-erased pin called ["AnyPin"](AnyPin).
126pub struct Flex<'d> {
127 pub(crate) pin: Peri<'d, AnyPin>,
128}
129
130impl<'d> Flex<'d> {
131 /// Wrap the pin in a `Flex`.
132 ///
133 /// Note: you cannot assume that the pin will be in Digital mode after this call.
134 #[inline]
135 pub fn new(pin: Peri<'d, impl Pin>) -> Self {
136 Self { pin: pin.into() }
137 }
138
139 /// Get the bank of this pin. See also [Bank].
140 ///
141 /// # Example
142 ///
143 /// ```
144 /// use embassy_nxp::gpio::{Bank, Flex};
145 ///
146 /// let p = embassy_nxp::init(Default::default());
147 /// let pin = Flex::new(p.PIO1_15);
148 ///
149 /// assert_eq!(pin.pin_bank(), Bank::Bank1);
150 /// ```
151 pub fn pin_bank(&self) -> Bank {
152 self.pin.pin_bank()
153 }
154
155 /// Get the number of this pin within its bank. See also [Bank].
156 ///
157 /// # Example
158 ///
159 /// ```
160 /// use embassy_nxp::gpio::Flex;
161 ///
162 /// let p = embassy_nxp::init(Default::default());
163 /// let pin = Flex::new(p.PIO1_15);
164 ///
165 /// assert_eq!(pin.pin_number(), 15 as u8);
166 /// ```
167 pub fn pin_number(&self) -> u8 {
168 self.pin.pin_number()
169 }
170
171 /// Get the bit mask for this pin. Useful for setting or clearing bits in a register. Note:
172 /// PIOx_0 is bit 0, PIOx_1 is bit 1, etc.
173 ///
174 /// # Example
175 ///
176 /// ```
177 /// use embassy_nxp::gpio::Flex;
178 ///
179 /// let p = embassy_nxp::init(Default::default());
180 /// let pin = Flex::new(p.PIO1_3);
181 ///
182 /// assert_eq!(pin.bit(), 0b0000_1000);
183 /// ```
184 pub fn bit(&self) -> u32 {
185 1 << self.pin.pin_number()
186 }
187
188 /// Set the pin to digital mode. This is required for using a pin as a GPIO pin. The default
189 /// setting for pins is (usually) non-digital.
190 fn set_as_digital(&mut self) {
191 match_iocon!(register, iocon_reg(), self.pin_bank(), self.pin_number(), {
192 register.modify(|_, w| w.digimode().digital());
193 });
194 }
195
196 /// Set the pin in output mode. This implies setting the pin to digital mode, which this
197 /// function handles itself.
198 pub fn set_as_output(&mut self) {
199 self.set_as_digital();
200 gpio_reg().dirset[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirsetp().bits(self.bit()) })
201 }
202
203 pub fn set_as_input(&mut self) {
204 self.set_as_digital();
205 gpio_reg().dirclr[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirclrp().bits(self.bit()) })
206 }
207}
208
209/// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate.
210pub(crate) trait SealedPin: Sized {
211 fn pin_bank(&self) -> Bank;
212 fn pin_number(&self) -> u8;
213}
214
215/// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an
216/// [AnyPin]. By default, this trait is sealed and cannot be implemented outside of the
217/// `embassy-nxp` crate due to the [SealedPin] trait.
218#[allow(private_bounds)]
219pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static {
220 /// Returns the pin number within a bank
221 #[inline]
222 fn pin(&self) -> u8 {
223 self.pin_number()
224 }
225
226 /// Returns the bank of this pin
227 #[inline]
228 fn bank(&self) -> Bank {
229 self.pin_bank()
230 }
231}
232
233/// Type-erased GPIO pin.
234pub struct AnyPin {
235 pin_bank: Bank,
236 pin_number: u8,
237}
238
239impl AnyPin {
240 /// Unsafely create a new type-erased pin.
241 ///
242 /// # Safety
243 ///
244 /// You must ensure that you’re only using one instance of this type at a time.
245 pub unsafe fn steal(pin_bank: Bank, pin_number: u8) -> Peri<'static, Self> {
246 Peri::new_unchecked(Self { pin_bank, pin_number })
247 }
248}
249
250impl_peripheral!(AnyPin);
251
252impl Pin for AnyPin {}
253impl SealedPin for AnyPin {
254 #[inline]
255 fn pin_bank(&self) -> Bank {
256 self.pin_bank
257 }
258
259 #[inline]
260 fn pin_number(&self) -> u8 {
261 self.pin_number
262 }
263}
264
265macro_rules! impl_pin {
266 ($name:ident, $bank:expr, $pin_num:expr) => {
267 impl Pin for peripherals::$name {}
268 impl SealedPin for peripherals::$name {
269 #[inline]
270 fn pin_bank(&self) -> Bank {
271 $bank
272 }
273
274 #[inline]
275 fn pin_number(&self) -> u8 {
276 $pin_num
277 }
278 }
279
280 impl From<peripherals::$name> for crate::gpio::AnyPin {
281 fn from(val: peripherals::$name) -> Self {
282 Self {
283 pin_bank: val.pin_bank(),
284 pin_number: val.pin_number(),
285 }
286 }
287 }
288 };
289}
290
291impl_pin!(PIO0_0, Bank::Bank0, 0);
292impl_pin!(PIO0_1, Bank::Bank0, 1);
293impl_pin!(PIO0_2, Bank::Bank0, 2);
294impl_pin!(PIO0_3, Bank::Bank0, 3);
295impl_pin!(PIO0_4, Bank::Bank0, 4);
296impl_pin!(PIO0_5, Bank::Bank0, 5);
297impl_pin!(PIO0_6, Bank::Bank0, 6);
298impl_pin!(PIO0_7, Bank::Bank0, 7);
299impl_pin!(PIO0_8, Bank::Bank0, 8);
300impl_pin!(PIO0_9, Bank::Bank0, 9);
301impl_pin!(PIO0_10, Bank::Bank0, 10);
302impl_pin!(PIO0_11, Bank::Bank0, 11);
303impl_pin!(PIO0_12, Bank::Bank0, 12);
304impl_pin!(PIO0_13, Bank::Bank0, 13);
305impl_pin!(PIO0_14, Bank::Bank0, 14);
306impl_pin!(PIO0_15, Bank::Bank0, 15);
307impl_pin!(PIO0_16, Bank::Bank0, 16);
308impl_pin!(PIO0_17, Bank::Bank0, 17);
309impl_pin!(PIO0_18, Bank::Bank0, 18);
310impl_pin!(PIO0_19, Bank::Bank0, 19);
311impl_pin!(PIO0_20, Bank::Bank0, 20);
312impl_pin!(PIO0_21, Bank::Bank0, 21);
313impl_pin!(PIO0_22, Bank::Bank0, 22);
314impl_pin!(PIO0_23, Bank::Bank0, 23);
315impl_pin!(PIO0_24, Bank::Bank0, 24);
316impl_pin!(PIO0_25, Bank::Bank0, 25);
317impl_pin!(PIO0_26, Bank::Bank0, 26);
318impl_pin!(PIO0_27, Bank::Bank0, 27);
319impl_pin!(PIO0_28, Bank::Bank0, 28);
320impl_pin!(PIO0_29, Bank::Bank0, 29);
321impl_pin!(PIO0_30, Bank::Bank0, 30);
322impl_pin!(PIO0_31, Bank::Bank0, 31);
323impl_pin!(PIO1_0, Bank::Bank1, 0);
324impl_pin!(PIO1_1, Bank::Bank1, 1);
325impl_pin!(PIO1_2, Bank::Bank1, 2);
326impl_pin!(PIO1_3, Bank::Bank1, 3);
327impl_pin!(PIO1_4, Bank::Bank1, 4);
328impl_pin!(PIO1_5, Bank::Bank1, 5);
329impl_pin!(PIO1_6, Bank::Bank1, 6);
330impl_pin!(PIO1_7, Bank::Bank1, 7);
331impl_pin!(PIO1_8, Bank::Bank1, 8);
332impl_pin!(PIO1_9, Bank::Bank1, 9);
333impl_pin!(PIO1_10, Bank::Bank1, 10);
334impl_pin!(PIO1_11, Bank::Bank1, 11);
335impl_pin!(PIO1_12, Bank::Bank1, 12);
336impl_pin!(PIO1_13, Bank::Bank1, 13);
337impl_pin!(PIO1_14, Bank::Bank1, 14);
338impl_pin!(PIO1_15, Bank::Bank1, 15);
339impl_pin!(PIO1_16, Bank::Bank1, 16);
340impl_pin!(PIO1_17, Bank::Bank1, 17);
341impl_pin!(PIO1_18, Bank::Bank1, 18);
342impl_pin!(PIO1_19, Bank::Bank1, 19);
343impl_pin!(PIO1_20, Bank::Bank1, 20);
344impl_pin!(PIO1_21, Bank::Bank1, 21);
345impl_pin!(PIO1_22, Bank::Bank1, 22);
346impl_pin!(PIO1_23, Bank::Bank1, 23);
347impl_pin!(PIO1_24, Bank::Bank1, 24);
348impl_pin!(PIO1_25, Bank::Bank1, 25);
349impl_pin!(PIO1_26, Bank::Bank1, 26);
350impl_pin!(PIO1_27, Bank::Bank1, 27);
351impl_pin!(PIO1_28, Bank::Bank1, 28);
352impl_pin!(PIO1_29, Bank::Bank1, 29);
353impl_pin!(PIO1_30, Bank::Bank1, 30);
354impl_pin!(PIO1_31, Bank::Bank1, 31);
diff --git a/embassy-nxp/src/pac_utils.rs b/embassy-nxp/src/gpio/lpc55.rs
index 86a807f6c..8f407bb3a 100644
--- a/embassy-nxp/src/pac_utils.rs
+++ b/embassy-nxp/src/gpio/lpc55.rs
@@ -1,3 +1,267 @@
1use embassy_hal_internal::{impl_peripheral, PeripheralType};
2
3use crate::{peripherals, Peri};
4
5pub(crate) fn init() {
6 // Enable clocks for GPIO, PINT, and IOCON
7 syscon_reg()
8 .ahbclkctrl0
9 .modify(|_, w| w.gpio0().enable().gpio1().enable().mux().enable().iocon().enable());
10 info!("GPIO initialized");
11}
12
13/// The GPIO pin level for pins set on "Digital" mode.
14#[derive(Debug, Eq, PartialEq, Clone, Copy)]
15pub enum Level {
16 /// Logical low. Corresponds to 0V.
17 Low,
18 /// Logical high. Corresponds to VDD.
19 High,
20}
21
22/// Pull setting for a GPIO input set on "Digital" mode.
23#[derive(Debug, Clone, Copy, Eq, PartialEq)]
24pub enum Pull {
25 /// No pull.
26 None,
27 /// Internal pull-up resistor.
28 Up,
29 /// Internal pull-down resistor.
30 Down,
31}
32
33/// The LPC55 boards have two GPIO banks, each with 32 pins. This enum represents the two banks.
34#[derive(Debug, Eq, PartialEq, Clone, Copy)]
35pub enum Bank {
36 Bank0 = 0,
37 Bank1 = 1,
38}
39
40/// GPIO output driver. Internally, this is a specialized [Flex] pin.
41pub struct Output<'d> {
42 pub(crate) pin: Flex<'d>,
43}
44
45impl<'d> Output<'d> {
46 /// Create GPIO output driver for a [Pin] with the provided [initial output](Level).
47 #[inline]
48 pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self {
49 let mut pin = Flex::new(pin);
50 pin.set_as_output();
51 let mut result = Self { pin };
52
53 match initial_output {
54 Level::High => result.set_high(),
55 Level::Low => result.set_low(),
56 };
57
58 result
59 }
60
61 pub fn set_high(&mut self) {
62 gpio_reg().set[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) })
63 }
64
65 pub fn set_low(&mut self) {
66 gpio_reg().clr[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) })
67 }
68
69 pub fn toggle(&mut self) {
70 gpio_reg().not[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) })
71 }
72
73 /// Get the current output level of the pin. Note that the value returned by this function is
74 /// the voltage level reported by the pin, not the value set by the output driver.
75 pub fn level(&self) -> Level {
76 let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits();
77 if bits & self.pin.bit() != 0 {
78 Level::High
79 } else {
80 Level::Low
81 }
82 }
83}
84
85/// GPIO input driver. Internally, this is a specialized [Flex] pin.
86pub struct Input<'d> {
87 pub(crate) pin: Flex<'d>,
88}
89
90impl<'d> Input<'d> {
91 /// Create GPIO output driver for a [Pin] with the provided [Pull].
92 #[inline]
93 pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self {
94 let mut pin = Flex::new(pin);
95 pin.set_as_input();
96 let mut result = Self { pin };
97 result.set_pull(pull);
98
99 result
100 }
101
102 /// Set the pull configuration for the pin. To disable the pull, use [Pull::None].
103 pub fn set_pull(&mut self, pull: Pull) {
104 match_iocon!(register, iocon_reg(), self.pin.pin_bank(), self.pin.pin_number(), {
105 register.modify(|_, w| match pull {
106 Pull::None => w.mode().inactive(),
107 Pull::Up => w.mode().pull_up(),
108 Pull::Down => w.mode().pull_down(),
109 });
110 });
111 }
112
113 /// Get the current input level of the pin.
114 pub fn read(&self) -> Level {
115 let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits();
116 if bits & self.pin.bit() != 0 {
117 Level::High
118 } else {
119 Level::Low
120 }
121 }
122}
123
124/// A flexible GPIO (digital mode) pin whose mode is not yet determined. Under the hood, this is a
125/// reference to a type-erased pin called ["AnyPin"](AnyPin).
126pub struct Flex<'d> {
127 pub(crate) pin: Peri<'d, AnyPin>,
128}
129
130impl<'d> Flex<'d> {
131 /// Wrap the pin in a `Flex`.
132 ///
133 /// Note: you cannot assume that the pin will be in Digital mode after this call.
134 #[inline]
135 pub fn new(pin: Peri<'d, impl Pin>) -> Self {
136 Self { pin: pin.into() }
137 }
138
139 /// Get the bank of this pin. See also [Bank].
140 ///
141 /// # Example
142 ///
143 /// ```
144 /// use embassy_nxp::gpio::{Bank, Flex};
145 ///
146 /// let p = embassy_nxp::init(Default::default());
147 /// let pin = Flex::new(p.PIO1_15);
148 ///
149 /// assert_eq!(pin.pin_bank(), Bank::Bank1);
150 /// ```
151 pub fn pin_bank(&self) -> Bank {
152 self.pin.pin_bank()
153 }
154
155 /// Get the number of this pin within its bank. See also [Bank].
156 ///
157 /// # Example
158 ///
159 /// ```
160 /// use embassy_nxp::gpio::Flex;
161 ///
162 /// let p = embassy_nxp::init(Default::default());
163 /// let pin = Flex::new(p.PIO1_15);
164 ///
165 /// assert_eq!(pin.pin_number(), 15 as u8);
166 /// ```
167 pub fn pin_number(&self) -> u8 {
168 self.pin.pin_number()
169 }
170
171 /// Get the bit mask for this pin. Useful for setting or clearing bits in a register. Note:
172 /// PIOx_0 is bit 0, PIOx_1 is bit 1, etc.
173 ///
174 /// # Example
175 ///
176 /// ```
177 /// use embassy_nxp::gpio::Flex;
178 ///
179 /// let p = embassy_nxp::init(Default::default());
180 /// let pin = Flex::new(p.PIO1_3);
181 ///
182 /// assert_eq!(pin.bit(), 0b0000_1000);
183 /// ```
184 pub fn bit(&self) -> u32 {
185 1 << self.pin.pin_number()
186 }
187
188 /// Set the pin to digital mode. This is required for using a pin as a GPIO pin. The default
189 /// setting for pins is (usually) non-digital.
190 fn set_as_digital(&mut self) {
191 match_iocon!(register, iocon_reg(), self.pin_bank(), self.pin_number(), {
192 register.modify(|_, w| w.digimode().digital());
193 });
194 }
195
196 /// Set the pin in output mode. This implies setting the pin to digital mode, which this
197 /// function handles itself.
198 pub fn set_as_output(&mut self) {
199 self.set_as_digital();
200 gpio_reg().dirset[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirsetp().bits(self.bit()) })
201 }
202
203 pub fn set_as_input(&mut self) {
204 self.set_as_digital();
205 gpio_reg().dirclr[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirclrp().bits(self.bit()) })
206 }
207}
208
209/// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate.
210pub(crate) trait SealedPin: Sized {
211 fn pin_bank(&self) -> Bank;
212 fn pin_number(&self) -> u8;
213}
214
215/// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an
216/// [AnyPin]. By default, this trait is sealed and cannot be implemented outside of the
217/// `embassy-nxp` crate due to the [SealedPin] trait.
218#[allow(private_bounds)]
219pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static {
220 /// Returns the pin number within a bank
221 #[inline]
222 fn pin(&self) -> u8 {
223 self.pin_number()
224 }
225
226 /// Returns the bank of this pin
227 #[inline]
228 fn bank(&self) -> Bank {
229 self.pin_bank()
230 }
231}
232
233/// Type-erased GPIO pin.
234pub struct AnyPin {
235 pin_bank: Bank,
236 pin_number: u8,
237}
238
239impl AnyPin {
240 /// Unsafely create a new type-erased pin.
241 ///
242 /// # Safety
243 ///
244 /// You must ensure that you’re only using one instance of this type at a time.
245 pub unsafe fn steal(pin_bank: Bank, pin_number: u8) -> Peri<'static, Self> {
246 Peri::new_unchecked(Self { pin_bank, pin_number })
247 }
248}
249
250impl_peripheral!(AnyPin);
251
252impl Pin for AnyPin {}
253impl SealedPin for AnyPin {
254 #[inline]
255 fn pin_bank(&self) -> Bank {
256 self.pin_bank
257 }
258
259 #[inline]
260 fn pin_number(&self) -> u8 {
261 self.pin_number
262 }
263}
264
1/// Get the GPIO register block. This is used to configure all GPIO pins. 265/// Get the GPIO register block. This is used to configure all GPIO pins.
2/// 266///
3/// # Safety 267/// # Safety
@@ -321,3 +585,94 @@ macro_rules! match_iocon {
321} 585}
322 586
323pub(crate) use match_iocon; 587pub(crate) use match_iocon;
588
589macro_rules! impl_pin {
590 ($name:ident, $bank:expr, $pin_num:expr) => {
591 impl Pin for peripherals::$name {}
592 impl SealedPin for peripherals::$name {
593 #[inline]
594 fn pin_bank(&self) -> Bank {
595 $bank
596 }
597
598 #[inline]
599 fn pin_number(&self) -> u8 {
600 $pin_num
601 }
602 }
603
604 impl From<peripherals::$name> for crate::gpio::AnyPin {
605 fn from(val: peripherals::$name) -> Self {
606 Self {
607 pin_bank: val.pin_bank(),
608 pin_number: val.pin_number(),
609 }
610 }
611 }
612 };
613}
614
615impl_pin!(PIO0_0, Bank::Bank0, 0);
616impl_pin!(PIO0_1, Bank::Bank0, 1);
617impl_pin!(PIO0_2, Bank::Bank0, 2);
618impl_pin!(PIO0_3, Bank::Bank0, 3);
619impl_pin!(PIO0_4, Bank::Bank0, 4);
620impl_pin!(PIO0_5, Bank::Bank0, 5);
621impl_pin!(PIO0_6, Bank::Bank0, 6);
622impl_pin!(PIO0_7, Bank::Bank0, 7);
623impl_pin!(PIO0_8, Bank::Bank0, 8);
624impl_pin!(PIO0_9, Bank::Bank0, 9);
625impl_pin!(PIO0_10, Bank::Bank0, 10);
626impl_pin!(PIO0_11, Bank::Bank0, 11);
627impl_pin!(PIO0_12, Bank::Bank0, 12);
628impl_pin!(PIO0_13, Bank::Bank0, 13);
629impl_pin!(PIO0_14, Bank::Bank0, 14);
630impl_pin!(PIO0_15, Bank::Bank0, 15);
631impl_pin!(PIO0_16, Bank::Bank0, 16);
632impl_pin!(PIO0_17, Bank::Bank0, 17);
633impl_pin!(PIO0_18, Bank::Bank0, 18);
634impl_pin!(PIO0_19, Bank::Bank0, 19);
635impl_pin!(PIO0_20, Bank::Bank0, 20);
636impl_pin!(PIO0_21, Bank::Bank0, 21);
637impl_pin!(PIO0_22, Bank::Bank0, 22);
638impl_pin!(PIO0_23, Bank::Bank0, 23);
639impl_pin!(PIO0_24, Bank::Bank0, 24);
640impl_pin!(PIO0_25, Bank::Bank0, 25);
641impl_pin!(PIO0_26, Bank::Bank0, 26);
642impl_pin!(PIO0_27, Bank::Bank0, 27);
643impl_pin!(PIO0_28, Bank::Bank0, 28);
644impl_pin!(PIO0_29, Bank::Bank0, 29);
645impl_pin!(PIO0_30, Bank::Bank0, 30);
646impl_pin!(PIO0_31, Bank::Bank0, 31);
647impl_pin!(PIO1_0, Bank::Bank1, 0);
648impl_pin!(PIO1_1, Bank::Bank1, 1);
649impl_pin!(PIO1_2, Bank::Bank1, 2);
650impl_pin!(PIO1_3, Bank::Bank1, 3);
651impl_pin!(PIO1_4, Bank::Bank1, 4);
652impl_pin!(PIO1_5, Bank::Bank1, 5);
653impl_pin!(PIO1_6, Bank::Bank1, 6);
654impl_pin!(PIO1_7, Bank::Bank1, 7);
655impl_pin!(PIO1_8, Bank::Bank1, 8);
656impl_pin!(PIO1_9, Bank::Bank1, 9);
657impl_pin!(PIO1_10, Bank::Bank1, 10);
658impl_pin!(PIO1_11, Bank::Bank1, 11);
659impl_pin!(PIO1_12, Bank::Bank1, 12);
660impl_pin!(PIO1_13, Bank::Bank1, 13);
661impl_pin!(PIO1_14, Bank::Bank1, 14);
662impl_pin!(PIO1_15, Bank::Bank1, 15);
663impl_pin!(PIO1_16, Bank::Bank1, 16);
664impl_pin!(PIO1_17, Bank::Bank1, 17);
665impl_pin!(PIO1_18, Bank::Bank1, 18);
666impl_pin!(PIO1_19, Bank::Bank1, 19);
667impl_pin!(PIO1_20, Bank::Bank1, 20);
668impl_pin!(PIO1_21, Bank::Bank1, 21);
669impl_pin!(PIO1_22, Bank::Bank1, 22);
670impl_pin!(PIO1_23, Bank::Bank1, 23);
671impl_pin!(PIO1_24, Bank::Bank1, 24);
672impl_pin!(PIO1_25, Bank::Bank1, 25);
673impl_pin!(PIO1_26, Bank::Bank1, 26);
674impl_pin!(PIO1_27, Bank::Bank1, 27);
675impl_pin!(PIO1_28, Bank::Bank1, 28);
676impl_pin!(PIO1_29, Bank::Bank1, 29);
677impl_pin!(PIO1_30, Bank::Bank1, 30);
678impl_pin!(PIO1_31, Bank::Bank1, 31);
diff --git a/embassy-nxp/src/gpio/rt1xxx.rs b/embassy-nxp/src/gpio/rt1xxx.rs
new file mode 100644
index 000000000..1d60a0d51
--- /dev/null
+++ b/embassy-nxp/src/gpio/rt1xxx.rs
@@ -0,0 +1,945 @@
1#![macro_use]
2
3use core::future::Future;
4use core::ops::Not;
5use core::pin::Pin as FuturePin;
6use core::task::{Context, Poll};
7
8use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType};
9use embassy_sync::waitqueue::AtomicWaker;
10use nxp_pac::gpio::vals::Icr;
11use nxp_pac::iomuxc::vals::Pus;
12
13use crate::chip::{mux_address, pad_address};
14use crate::pac::common::{Reg, RW};
15use crate::pac::gpio::Gpio;
16#[cfg(feature = "rt")]
17use crate::pac::interrupt;
18use crate::pac::iomuxc::regs::{Ctl, MuxCtl};
19use crate::pac::{self};
20
21/// The GPIO pin level for pins set on "Digital" mode.
22#[derive(Debug, Eq, PartialEq, Clone, Copy)]
23pub enum Level {
24 /// Logical low. Corresponds to 0V.
25 Low,
26 /// Logical high. Corresponds to VDD.
27 High,
28}
29
30impl From<bool> for Level {
31 fn from(val: bool) -> Self {
32 match val {
33 true => Self::High,
34 false => Self::Low,
35 }
36 }
37}
38
39impl From<Level> for bool {
40 fn from(level: Level) -> bool {
41 match level {
42 Level::Low => false,
43 Level::High => true,
44 }
45 }
46}
47
48impl Not for Level {
49 type Output = Self;
50
51 fn not(self) -> Self::Output {
52 match self {
53 Level::Low => Level::High,
54 Level::High => Level::Low,
55 }
56 }
57}
58
59/// Pull setting for a GPIO input set on "Digital" mode.
60#[derive(Debug, Clone, Copy, Eq, PartialEq)]
61pub enum Pull {
62 /// No pull.
63 None,
64
65 // TODO: What Does PUE::KEEPER mean here?
66
67 // 22 kOhm pull-up resistor.
68 Up22K,
69
70 // 47 kOhm pull-up resistor.
71 Up47K,
72
73 // 100 kOhm pull-up resistor.
74 Up100K,
75
76 // 100 kOhm pull-down resistor.
77 Down100K,
78}
79
80/// Drive strength of an output
81#[derive(Copy, Clone, Debug, Eq, PartialEq)]
82#[cfg_attr(feature = "defmt", derive(defmt::Format))]
83pub enum Drive {
84 Disabled,
85 _150R,
86 _75R,
87 _50R,
88 _37R,
89 _30R,
90 _25R,
91 _20R,
92}
93
94/// Slew rate of an output
95#[derive(Copy, Clone, Debug, Eq, PartialEq)]
96#[cfg_attr(feature = "defmt", derive(defmt::Format))]
97pub enum SlewRate {
98 Slow,
99
100 Fast,
101}
102
103#[derive(Clone, Copy, Debug, PartialEq, Eq)]
104pub enum Bank {
105 /// Bank 1
106 #[cfg(gpio1)]
107 Gpio1,
108
109 /// Bank 2
110 #[cfg(gpio2)]
111 Gpio2,
112
113 /// Bank 3
114 #[cfg(gpio3)]
115 Gpio3,
116
117 /// Bank 4
118 #[cfg(gpio4)]
119 Gpio4,
120
121 /// Bank 5
122 #[cfg(gpio5)]
123 Gpio5,
124}
125
126/// GPIO flexible pin.
127///
128/// This pin can either be a disconnected, input, or output pin, or both. The level register bit will remain
129/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output
130/// mode.
131pub struct Flex<'d> {
132 pub(crate) pin: Peri<'d, AnyPin>,
133}
134
135impl<'d> Flex<'d> {
136 /// Wrap the pin in a `Flex`.
137 ///
138 /// The pin remains disconnected. The initial output level is unspecified, but can be changed
139 /// before the pin is put into output mode.
140 #[inline]
141 pub fn new(pin: Peri<'d, impl Pin>) -> Self {
142 Self { pin: pin.into() }
143 }
144
145 /// Set the pin's pull.
146 #[inline]
147 pub fn set_pull(&mut self, pull: Pull) {
148 let (pke, pue, pus) = match pull {
149 Pull::None => (false, true, Pus::PUS_0_100K_OHM_PULL_DOWN),
150 Pull::Up22K => (true, true, Pus::PUS_3_22K_OHM_PULL_UP),
151 Pull::Up47K => (true, true, Pus::PUS_1_47K_OHM_PULL_UP),
152 Pull::Up100K => (true, true, Pus::PUS_2_100K_OHM_PULL_UP),
153 Pull::Down100K => (true, true, Pus::PUS_0_100K_OHM_PULL_DOWN),
154 };
155
156 self.pin.pad().modify(|w| {
157 w.set_pke(pke);
158 w.set_pue(pue);
159 w.set_pus(pus);
160 });
161 }
162
163 // Set the pin's slew rate.
164 #[inline]
165 pub fn set_slewrate(&mut self, rate: SlewRate) {
166 self.pin.pad().modify(|w| {
167 w.set_sre(match rate {
168 SlewRate::Slow => false,
169 SlewRate::Fast => true,
170 });
171 });
172 }
173
174 /// Set the pin's Schmitt trigger.
175 #[inline]
176 pub fn set_schmitt(&mut self, enable: bool) {
177 self.pin.pad().modify(|w| {
178 w.set_hys(enable);
179 });
180 }
181
182 /// Put the pin into input mode.
183 ///
184 /// The pull setting is left unchanged.
185 #[inline]
186 pub fn set_as_input(&mut self) {
187 self.pin.mux().modify(|w| {
188 w.set_mux_mode(GPIO_MUX_MODE);
189 });
190
191 // Setting direction is RMW
192 critical_section::with(|_cs| {
193 self.pin.block().gdir().modify(|w| {
194 w.set_gdir(self.pin.pin_number() as usize, false);
195 });
196 })
197 }
198
199 /// Put the pin into output mode.
200 ///
201 /// The pin level will be whatever was set before (or low by default). If you want it to begin
202 /// at a specific level, call `set_high`/`set_low` on the pin first.
203 #[inline]
204 pub fn set_as_output(&mut self) {
205 self.pin.mux().modify(|w| {
206 w.set_mux_mode(GPIO_MUX_MODE);
207 });
208
209 // Setting direction is RMW
210 critical_section::with(|_cs| {
211 self.pin.block().gdir().modify(|w| {
212 w.set_gdir(self.pin.pin_number() as usize, true);
213 });
214 })
215 }
216
217 /// Put the pin into input + open-drain output mode.
218 ///
219 /// The hardware will drive the line low if you set it to low, and will leave it floating if you set
220 /// it to high, in which case you can read the input to figure out whether another device
221 /// is driving the line low.
222 ///
223 /// The pin level will be whatever was set before (or low by default). If you want it to begin
224 /// at a specific level, call `set_high`/`set_low` on the pin first.
225 ///
226 /// The internal weak pull-up and pull-down resistors will be disabled.
227 #[inline]
228 pub fn set_as_input_output(&mut self) {
229 self.pin.pad().modify(|w| {
230 w.set_ode(true);
231 });
232 }
233
234 /// Set the pin as "disconnected", ie doing nothing and consuming the lowest
235 /// amount of power possible.
236 ///
237 /// This is currently the same as [`Self::set_as_analog()`] but is semantically different
238 /// really. Drivers should `set_as_disconnected()` pins when dropped.
239 ///
240 /// Note that this also disables the pull-up and pull-down resistors.
241 #[inline]
242 pub fn set_as_disconnected(&mut self) {
243 self.pin.pad().modify(|w| {
244 w.set_ode(false);
245 w.set_pke(false);
246 w.set_pue(false);
247 w.set_pus(Pus::PUS_0_100K_OHM_PULL_DOWN);
248 });
249 }
250
251 /// Get whether the pin input level is high.
252 #[inline]
253 pub fn is_high(&self) -> bool {
254 self.pin.block().psr().read().psr(self.pin.pin_number() as usize)
255 }
256
257 /// Get whether the pin input level is low.
258 #[inline]
259 pub fn is_low(&self) -> bool {
260 !self.is_high()
261 }
262
263 /// Returns current pin level
264 #[inline]
265 pub fn get_level(&self) -> Level {
266 self.is_high().into()
267 }
268
269 /// Set the output as high.
270 #[inline]
271 pub fn set_high(&mut self) {
272 self.pin.block().dr_set().write(|w| {
273 w.set_dr_set(self.pin.pin_number() as usize, true);
274 });
275 }
276
277 /// Set the output as low.
278 #[inline]
279 pub fn set_low(&mut self) {
280 self.pin.block().dr_clear().write(|w| {
281 w.set_dr_clear(self.pin.pin_number() as usize, true);
282 });
283 }
284
285 /// Toggle pin output
286 #[inline]
287 pub fn toggle(&mut self) {
288 self.pin.block().dr_toggle().write(|w| {
289 w.set_dr_toggle(self.pin.pin_number() as usize, true);
290 });
291 }
292
293 /// Set the output level.
294 #[inline]
295 pub fn set_level(&mut self, level: Level) {
296 match level {
297 Level::Low => self.set_low(),
298 Level::High => self.set_high(),
299 }
300 }
301
302 /// Get the current pin output level.
303 #[inline]
304 pub fn get_output_level(&self) -> Level {
305 self.is_set_high().into()
306 }
307
308 /// Is the output level high?
309 ///
310 /// If the [`Flex`] is set as an input, then this is equivalent to [`Flex::is_high`].
311 #[inline]
312 pub fn is_set_high(&self) -> bool {
313 self.pin.block().dr().read().dr(self.pin.pin_number() as usize)
314 }
315
316 /// Is the output level low?
317 ///
318 /// If the [`Flex`] is set as an input, then this is equivalent to [`Flex::is_low`].
319 #[inline]
320 pub fn is_set_low(&self) -> bool {
321 !self.is_set_high()
322 }
323
324 /// Wait until the pin is high. If it is already high, return immediately.
325 #[inline]
326 pub async fn wait_for_high(&mut self) {
327 InputFuture::new(self.pin.reborrow(), InterruptConfiguration::High).await
328 }
329
330 /// Wait until the pin is low. If it is already low, return immediately.
331 #[inline]
332 pub async fn wait_for_low(&mut self) {
333 InputFuture::new(self.pin.reborrow(), InterruptConfiguration::Low).await
334 }
335
336 /// Wait for the pin to undergo a transition from low to high.
337 #[inline]
338 pub async fn wait_for_rising_edge(&mut self) {
339 InputFuture::new(self.pin.reborrow(), InterruptConfiguration::RisingEdge).await
340 }
341
342 /// Wait for the pin to undergo a transition from high to low.
343 #[inline]
344 pub async fn wait_for_falling_edge(&mut self) {
345 InputFuture::new(self.pin.reborrow(), InterruptConfiguration::FallingEdge).await
346 }
347
348 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
349 #[inline]
350 pub async fn wait_for_any_edge(&mut self) {
351 InputFuture::new(self.pin.reborrow(), InterruptConfiguration::AnyEdge).await
352 }
353}
354
355impl<'d> Drop for Flex<'d> {
356 fn drop(&mut self) {
357 self.set_as_disconnected();
358 }
359}
360
361/// GPIO input driver.
362pub struct Input<'d> {
363 pin: Flex<'d>,
364}
365
366impl<'d> Input<'d> {
367 /// Create GPIO input driver for a [Pin] with the provided [Pull] configuration.
368 #[inline]
369 pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self {
370 let mut pin = Flex::new(pin);
371 pin.set_as_input();
372 pin.set_pull(pull);
373 Self { pin }
374 }
375
376 /// Get whether the pin input level is high.
377 #[inline]
378 pub fn is_high(&self) -> bool {
379 self.pin.is_high()
380 }
381
382 /// Get whether the pin input level is low.
383 #[inline]
384 pub fn is_low(&self) -> bool {
385 self.pin.is_low()
386 }
387
388 /// Get the current pin input level.
389 #[inline]
390 pub fn get_level(&self) -> Level {
391 self.pin.get_level()
392 }
393
394 /// Wait until the pin is high. If it is already high, return immediately.
395 #[inline]
396 pub async fn wait_for_high(&mut self) {
397 self.pin.wait_for_high().await
398 }
399
400 /// Wait until the pin is low. If it is already low, return immediately.
401 #[inline]
402 pub async fn wait_for_low(&mut self) {
403 self.pin.wait_for_low().await
404 }
405
406 /// Wait for the pin to undergo a transition from low to high.
407 #[inline]
408 pub async fn wait_for_rising_edge(&mut self) {
409 self.pin.wait_for_rising_edge().await
410 }
411
412 /// Wait for the pin to undergo a transition from high to low.
413 #[inline]
414 pub async fn wait_for_falling_edge(&mut self) {
415 self.pin.wait_for_falling_edge().await
416 }
417
418 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
419 #[inline]
420 pub async fn wait_for_any_edge(&mut self) {
421 self.pin.wait_for_any_edge().await
422 }
423}
424
425/// GPIO output driver.
426///
427/// Note that pins will **return to their floating state** when `Output` is dropped.
428/// If pins should retain their state indefinitely, either keep ownership of the
429/// `Output`, or pass it to [`core::mem::forget`].
430pub struct Output<'d> {
431 pin: Flex<'d>,
432}
433
434impl<'d> Output<'d> {
435 /// Create GPIO output driver for a [Pin] with the provided [Level] configuration.
436 #[inline]
437 pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self {
438 let mut pin = Flex::new(pin);
439 pin.set_as_output();
440 pin.set_level(initial_output);
441 Self { pin }
442 }
443
444 /// Set the output as high.
445 #[inline]
446 pub fn set_high(&mut self) {
447 self.pin.set_high();
448 }
449
450 /// Set the output as low.
451 #[inline]
452 pub fn set_low(&mut self) {
453 self.pin.set_low();
454 }
455
456 /// Set the output level.
457 #[inline]
458 pub fn set_level(&mut self, level: Level) {
459 self.pin.set_level(level)
460 }
461
462 /// Is the output pin set as high?
463 #[inline]
464 pub fn is_set_high(&self) -> bool {
465 self.pin.is_set_high()
466 }
467
468 /// Is the output pin set as low?
469 #[inline]
470 pub fn is_set_low(&self) -> bool {
471 self.pin.is_set_low()
472 }
473
474 /// What level output is set to
475 #[inline]
476 pub fn get_output_level(&self) -> Level {
477 self.pin.get_output_level()
478 }
479
480 /// Toggle pin output
481 #[inline]
482 pub fn toggle(&mut self) {
483 self.pin.toggle();
484 }
485}
486
487/// GPIO output open-drain driver.
488///
489/// Note that pins will **return to their floating state** when `OutputOpenDrain` is dropped.
490/// If pins should retain their state indefinitely, either keep ownership of the
491/// `OutputOpenDrain`, or pass it to [`core::mem::forget`].
492pub struct OutputOpenDrain<'d> {
493 pin: Flex<'d>,
494}
495
496impl<'d> OutputOpenDrain<'d> {
497 /// Create a new GPIO open drain output driver for a [Pin] with the provided [Level].
498 #[inline]
499 pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self {
500 let mut pin = Flex::new(pin);
501 pin.set_level(initial_output);
502 pin.set_as_input_output();
503 Self { pin }
504 }
505
506 /// Get whether the pin input level is high.
507 #[inline]
508 pub fn is_high(&self) -> bool {
509 !self.pin.is_low()
510 }
511
512 /// Get whether the pin input level is low.
513 #[inline]
514 pub fn is_low(&self) -> bool {
515 self.pin.is_low()
516 }
517
518 /// Get the current pin input level.
519 #[inline]
520 pub fn get_level(&self) -> Level {
521 self.pin.get_level()
522 }
523
524 /// Set the output as high.
525 #[inline]
526 pub fn set_high(&mut self) {
527 self.pin.set_high();
528 }
529
530 /// Set the output as low.
531 #[inline]
532 pub fn set_low(&mut self) {
533 self.pin.set_low();
534 }
535
536 /// Set the output level.
537 #[inline]
538 pub fn set_level(&mut self, level: Level) {
539 self.pin.set_level(level);
540 }
541
542 /// Get whether the output level is set to high.
543 #[inline]
544 pub fn is_set_high(&self) -> bool {
545 self.pin.is_set_high()
546 }
547
548 /// Get whether the output level is set to low.
549 #[inline]
550 pub fn is_set_low(&self) -> bool {
551 self.pin.is_set_low()
552 }
553
554 /// Get the current output level.
555 #[inline]
556 pub fn get_output_level(&self) -> Level {
557 self.pin.get_output_level()
558 }
559
560 /// Toggle pin output
561 #[inline]
562 pub fn toggle(&mut self) {
563 self.pin.toggle()
564 }
565
566 /// Wait until the pin is high. If it is already high, return immediately.
567 #[inline]
568 pub async fn wait_for_high(&mut self) {
569 self.pin.wait_for_high().await
570 }
571
572 /// Wait until the pin is low. If it is already low, return immediately.
573 #[inline]
574 pub async fn wait_for_low(&mut self) {
575 self.pin.wait_for_low().await
576 }
577
578 /// Wait for the pin to undergo a transition from low to high.
579 #[inline]
580 pub async fn wait_for_rising_edge(&mut self) {
581 self.pin.wait_for_rising_edge().await
582 }
583
584 /// Wait for the pin to undergo a transition from high to low.
585 #[inline]
586 pub async fn wait_for_falling_edge(&mut self) {
587 self.pin.wait_for_falling_edge().await
588 }
589
590 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
591 #[inline]
592 pub async fn wait_for_any_edge(&mut self) {
593 self.pin.wait_for_any_edge().await
594 }
595}
596
597#[allow(private_bounds)]
598pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static {
599 /// Returns the pin number within a bank
600 #[inline]
601 fn pin(&self) -> u8 {
602 self.pin_number()
603 }
604
605 #[inline]
606 fn bank(&self) -> Bank {
607 self._bank()
608 }
609}
610
611/// Type-erased GPIO pin.
612pub struct AnyPin {
613 pub(crate) pin_number: u8,
614 pub(crate) bank: Bank,
615}
616
617impl AnyPin {
618 /// Unsafely create a new type-erased pin.
619 ///
620 /// # Safety
621 ///
622 /// You must ensure that you’re only using one instance of this type at a time.
623 pub unsafe fn steal(bank: Bank, pin_number: u8) -> Peri<'static, Self> {
624 Peri::new_unchecked(Self { pin_number, bank })
625 }
626}
627
628impl_peripheral!(AnyPin);
629
630impl Pin for AnyPin {}
631impl SealedPin for AnyPin {
632 #[inline]
633 fn pin_number(&self) -> u8 {
634 self.pin_number
635 }
636
637 #[inline]
638 fn _bank(&self) -> Bank {
639 self.bank
640 }
641}
642
643// Impl details
644
645/// Mux mode for GPIO pins. This is constant across all RT1xxx parts.
646const GPIO_MUX_MODE: u8 = 0b101;
647
648// FIXME: These don't always need to be 32 entries. GPIO5 on RT1101 contains a single pin and GPIO2 only 14.
649#[cfg(gpio1)]
650static GPIO1_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
651#[cfg(gpio2)]
652static GPIO2_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
653#[cfg(gpio3)]
654static GPIO3_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
655#[cfg(gpio4)]
656static GPIO4_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
657#[cfg(gpio5)]
658static GPIO5_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
659
660/// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate.
661pub(crate) trait SealedPin: Sized {
662 fn pin_number(&self) -> u8;
663
664 fn _bank(&self) -> Bank;
665
666 #[inline]
667 fn block(&self) -> Gpio {
668 match self._bank() {
669 #[cfg(gpio1)]
670 Bank::Gpio1 => pac::GPIO1,
671 #[cfg(gpio2)]
672 Bank::Gpio2 => pac::GPIO2,
673 #[cfg(gpio3)]
674 Bank::Gpio3 => pac::GPIO3,
675 #[cfg(gpio4)]
676 Bank::Gpio4 => pac::GPIO4,
677 #[cfg(gpio5)]
678 Bank::Gpio5 => pac::GPIO5,
679 }
680 }
681
682 #[inline]
683 fn mux(&self) -> Reg<MuxCtl, RW> {
684 // 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() };
686
687 // SAFETY: The register at the address is an instance of MuxCtl.
688 unsafe { Reg::from_ptr(address as *mut _) }
689 }
690
691 #[inline]
692 fn pad(&self) -> Reg<Ctl, RW> {
693 // SAFETY: The generated pad address table is valid since it is generated from the SVD files.
694 let address = unsafe { pad_address(self._bank(), self.pin_number()).unwrap_unchecked() };
695
696 // SAFETY: The register at the address is an instance of Ctl.
697 unsafe { Reg::from_ptr(address as *mut _) }
698 }
699
700 fn waker(&self) -> &AtomicWaker {
701 match self._bank() {
702 #[cfg(gpio1)]
703 Bank::Gpio1 => &GPIO1_WAKERS[self.pin_number() as usize],
704 #[cfg(gpio2)]
705 Bank::Gpio2 => &GPIO2_WAKERS[self.pin_number() as usize],
706 #[cfg(gpio3)]
707 Bank::Gpio3 => &GPIO3_WAKERS[self.pin_number() as usize],
708 #[cfg(gpio4)]
709 Bank::Gpio4 => &GPIO4_WAKERS[self.pin_number() as usize],
710 #[cfg(gpio5)]
711 Bank::Gpio5 => &GPIO5_WAKERS[self.pin_number() as usize],
712 }
713 }
714}
715
716/// This enum matches the layout of Icr.
717enum InterruptConfiguration {
718 Low,
719 High,
720 RisingEdge,
721 FallingEdge,
722 AnyEdge,
723}
724
725#[must_use = "futures do nothing unless you `.await` or poll them"]
726struct InputFuture<'d> {
727 pin: Peri<'d, AnyPin>,
728}
729
730impl<'d> InputFuture<'d> {
731 fn new(pin: Peri<'d, AnyPin>, config: InterruptConfiguration) -> Self {
732 let block = pin.block();
733
734 let (icr, edge_sel) = match config {
735 InterruptConfiguration::Low => (Icr::LOW_LEVEL, false),
736 InterruptConfiguration::High => (Icr::HIGH_LEVEL, false),
737 InterruptConfiguration::RisingEdge => (Icr::RISING_EDGE, false),
738 InterruptConfiguration::FallingEdge => (Icr::FALLING_EDGE, false),
739 InterruptConfiguration::AnyEdge => (Icr::FALLING_EDGE, true),
740 };
741
742 let index = if pin.pin_number() > 15 { 1 } else { 0 };
743
744 // Interrupt configuration performs RMW
745 critical_section::with(|_cs| {
746 // Disable interrupt so a level/edge detection change does not cause ISR to be set.
747 block.imr().modify(|w| {
748 w.set_imr(pin.pin_number() as usize, false);
749 });
750
751 block.icr(index).modify(|w| {
752 w.set_pin(pin.pin_number() as usize, icr);
753 });
754
755 block.edge_sel().modify(|w| {
756 w.set_edge_sel(pin.pin_number() as usize, edge_sel);
757 });
758
759 // Clear the previous interrupt.
760 block.isr().modify(|w| {
761 // "Status flags are cleared by writing a 1 to the corresponding bit position."
762 w.set_isr(pin.pin_number() as usize, true);
763 });
764 });
765
766 Self { pin }
767 }
768}
769
770impl<'d> Future for InputFuture<'d> {
771 type Output = ();
772
773 fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
774 // We need to register/re-register the waker for each poll because any
775 // calls to wake will deregister the waker.
776 let waker = self.pin.waker();
777 waker.register(cx.waker());
778
779 // Enabling interrupt is RMW
780 critical_section::with(|_cs| {
781 self.pin.block().imr().modify(|w| {
782 w.set_imr(self.pin.pin_number() as usize, true);
783 });
784 });
785
786 let isr = self.pin.block().isr().read();
787
788 if isr.isr(self.pin.pin_number() as usize) {
789 return Poll::Ready(());
790 }
791
792 Poll::Pending
793 }
794}
795
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 {
830 ($name: ident, $bank: ident, $pin_num: expr) => {
831 impl crate::gpio::Pin for crate::peripherals::$name {}
832 impl crate::gpio::SealedPin for crate::peripherals::$name {
833 #[inline]
834 fn pin_number(&self) -> u8 {
835 $pin_num
836 }
837
838 #[inline]
839 fn _bank(&self) -> crate::gpio::Bank {
840 crate::gpio::Bank::$bank
841 }
842 }
843
844 impl From<peripherals::$name> for crate::gpio::AnyPin {
845 fn from(val: peripherals::$name) -> Self {
846 use crate::gpio::SealedPin;
847
848 Self {
849 pin_number: val.pin_number(),
850 bank: val._bank(),
851 }
852 }
853 }
854 };
855}
856
857pub(crate) fn init() {
858 #[cfg(feature = "rt")]
859 unsafe {
860 use embassy_hal_internal::interrupt::InterruptExt;
861
862 pac::Interrupt::GPIO1_COMBINED_0_15.enable();
863 pac::Interrupt::GPIO1_COMBINED_16_31.enable();
864 pac::Interrupt::GPIO2_COMBINED_0_15.enable();
865 pac::Interrupt::GPIO5_COMBINED_0_15.enable();
866 }
867}
868
869/// IRQ handler for GPIO pins.
870///
871/// If `high_bits` is false, then the interrupt is for pins 0 through 15. If true, then the interrupt
872/// is for pins 16 through 31
873#[cfg(feature = "rt")]
874fn irq_handler(block: Gpio, wakers: &[AtomicWaker; 32], high_bits: bool) {
875 use crate::BitIter;
876
877 let isr = block.isr().read().0;
878 let imr = block.imr().read().0;
879 let mask = if high_bits { 0xFFFF_0000 } else { 0x0000_FFFF };
880 let bits = isr & imr & mask;
881
882 for bit in BitIter(bits) {
883 wakers[bit as usize].wake();
884
885 // Disable further interrupts for this pin. The input future will check ISR (which is kept
886 // until reset).
887 block.imr().modify(|w| {
888 w.set_imr(bit as usize, false);
889 });
890 }
891}
892
893#[cfg(all(any(feature = "mimxrt1011", feature = "mimxrt1062"), feature = "rt"))]
894#[interrupt]
895fn GPIO1_COMBINED_0_15() {
896 irq_handler(pac::GPIO1, &GPIO1_WAKERS, false);
897}
898
899#[cfg(all(any(feature = "mimxrt1011", feature = "mimxrt1062"), feature = "rt"))]
900#[interrupt]
901fn GPIO1_COMBINED_16_31() {
902 irq_handler(pac::GPIO1, &GPIO1_WAKERS, true);
903}
904
905#[cfg(all(any(feature = "mimxrt1011", feature = "mimxrt1062"), feature = "rt"))]
906#[interrupt]
907fn GPIO2_COMBINED_0_15() {
908 irq_handler(pac::GPIO2, &GPIO2_WAKERS, false);
909}
910
911#[cfg(all(feature = "mimxrt1062", feature = "rt"))]
912#[interrupt]
913fn GPIO2_COMBINED_16_31() {
914 irq_handler(pac::GPIO2, &GPIO2_WAKERS, true);
915}
916
917#[cfg(all(feature = "mimxrt1062", feature = "rt"))]
918#[interrupt]
919fn GPIO3_COMBINED_0_15() {
920 irq_handler(pac::GPIO3, &GPIO3_WAKERS, false);
921}
922
923#[cfg(all(feature = "mimxrt1062", feature = "rt"))]
924#[interrupt]
925fn GPIO3_COMBINED_16_31() {
926 irq_handler(pac::GPIO3, &GPIO3_WAKERS, true);
927}
928
929#[cfg(all(feature = "mimxrt1062", feature = "rt"))]
930#[interrupt]
931fn GPIO4_COMBINED_0_15() {
932 irq_handler(pac::GPIO4, &GPIO4_WAKERS, false);
933}
934
935#[cfg(all(feature = "mimxrt1062", feature = "rt"))]
936#[interrupt]
937fn GPIO4_COMBINED_16_31() {
938 irq_handler(pac::GPIO4, &GPIO4_WAKERS, true);
939}
940
941#[cfg(all(any(feature = "mimxrt1011", feature = "mimxrt1062"), feature = "rt"))]
942#[interrupt]
943fn GPIO5_COMBINED_0_15() {
944 irq_handler(pac::GPIO5, &GPIO5_WAKERS, false);
945}
diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs
index ad2056c06..5e77fc0db 100644
--- a/embassy-nxp/src/lib.rs
+++ b/embassy-nxp/src/lib.rs
@@ -1,11 +1,28 @@
1#![no_std] 1#![no_std]
2 2
3// This mod MUST go first, so that the others see its macros.
4pub(crate) mod fmt;
5
3pub mod gpio; 6pub mod gpio;
4mod pac_utils; 7#[cfg(feature = "lpc55")]
5pub mod pint; 8pub mod pint;
6 9
7pub use embassy_hal_internal::Peri; 10#[cfg(feature = "_time_driver")]
8pub use lpc55_pac as pac; 11#[cfg_attr(feature = "time-driver-pit", path = "time_driver/pit.rs")]
12mod time_driver;
13
14// This mod MUST go last, so that it sees all the `impl_foo!` macros
15#[cfg_attr(feature = "lpc55", path = "chips/lpc55.rs")]
16#[cfg_attr(feature = "mimxrt1011", path = "chips/mimxrt1011.rs")]
17#[cfg_attr(feature = "mimxrt1062", path = "chips/mimxrt1062.rs")]
18mod chip;
19
20#[cfg(feature = "unstable-pac")]
21pub use chip::pac;
22#[cfg(not(feature = "unstable-pac"))]
23pub(crate) use chip::pac;
24pub use chip::{peripherals, Peripherals};
25pub use embassy_hal_internal::{Peri, PeripheralType};
9 26
10/// Initialize the `embassy-nxp` HAL with the provided configuration. 27/// Initialize the `embassy-nxp` HAL with the provided configuration.
11/// 28///
@@ -13,79 +30,66 @@ pub use lpc55_pac as pac;
13/// 30///
14/// This should only be called once and at startup, otherwise it panics. 31/// This should only be called once and at startup, otherwise it panics.
15pub fn init(_config: config::Config) -> Peripherals { 32pub fn init(_config: config::Config) -> Peripherals {
33 // Do this first, so that it panics if user is calling `init` a second time
34 // before doing anything important.
35 let peripherals = Peripherals::take();
36
37 #[cfg(feature = "mimxrt1011")]
38 {
39 // The RT1010 Reference manual states that core clock root must be switched before
40 // reprogramming PLL2.
41 pac::CCM.cbcdr().modify(|w| {
42 w.set_periph_clk_sel(pac::ccm::vals::PeriphClkSel::PERIPH_CLK_SEL_1);
43 });
44
45 while matches!(
46 pac::CCM.cdhipr().read().periph_clk_sel_busy(),
47 pac::ccm::vals::PeriphClkSelBusy::PERIPH_CLK_SEL_BUSY_1
48 ) {}
49
50 info!("Core clock root switched");
51
52 // 480 * 18 / 24 = 360
53 pac::CCM_ANALOG.pfd_480().modify(|x| x.set_pfd2_frac(12));
54
55 //480*18/24(pfd0)/4
56 pac::CCM_ANALOG.pfd_480().modify(|x| x.set_pfd0_frac(24));
57 pac::CCM.cscmr1().modify(|x| x.set_flexspi_podf(3.into()));
58
59 // CPU Core
60 pac::CCM_ANALOG.pfd_528().modify(|x| x.set_pfd3_frac(18));
61 cortex_m::asm::delay(500_000);
62
63 // Clock core clock with PLL 2.
64 pac::CCM
65 .cbcdr()
66 .modify(|x| x.set_periph_clk_sel(pac::ccm::vals::PeriphClkSel::PERIPH_CLK_SEL_0)); // false
67
68 while matches!(
69 pac::CCM.cdhipr().read().periph_clk_sel_busy(),
70 pac::ccm::vals::PeriphClkSelBusy::PERIPH_CLK_SEL_BUSY_1
71 ) {}
72
73 pac::CCM
74 .cbcmr()
75 .write(|v| v.set_pre_periph_clk_sel(pac::ccm::vals::PrePeriphClkSel::PRE_PERIPH_CLK_SEL_0));
76
77 // TODO: Some for USB PLLs
78
79 // DCDC clock?
80 pac::CCM.ccgr6().modify(|v| v.set_cg0(1));
81 }
82
83 #[cfg(any(feature = "lpc55", rt1xxx))]
16 gpio::init(); 84 gpio::init();
85
86 #[cfg(feature = "lpc55")]
17 pint::init(); 87 pint::init();
18 88
19 crate::Peripherals::take() 89 #[cfg(feature = "_time_driver")]
20} 90 time_driver::init();
21 91
22embassy_hal_internal::peripherals! { 92 peripherals
23 // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other
24 // peripheral types (e.g. I2C).
25 PIO0_0,
26 PIO0_1,
27 PIO0_2,
28 PIO0_3,
29 PIO0_4,
30 PIO0_5,
31 PIO0_6,
32 PIO0_7,
33 PIO0_8,
34 PIO0_9,
35 PIO0_10,
36 PIO0_11,
37 PIO0_12,
38 PIO0_13,
39 PIO0_14,
40 PIO0_15,
41 PIO0_16,
42 PIO0_17,
43 PIO0_18,
44 PIO0_19,
45 PIO0_20,
46 PIO0_21,
47 PIO0_22,
48 PIO0_23,
49 PIO0_24,
50 PIO0_25,
51 PIO0_26,
52 PIO0_27,
53 PIO0_28,
54 PIO0_29,
55 PIO0_30,
56 PIO0_31,
57 PIO1_0,
58 PIO1_1,
59 PIO1_2,
60 PIO1_3,
61 PIO1_4,
62 PIO1_5,
63 PIO1_6,
64 PIO1_7,
65 PIO1_8,
66 PIO1_9,
67 PIO1_10,
68 PIO1_11,
69 PIO1_12,
70 PIO1_13,
71 PIO1_14,
72 PIO1_15,
73 PIO1_16,
74 PIO1_17,
75 PIO1_18,
76 PIO1_19,
77 PIO1_20,
78 PIO1_21,
79 PIO1_22,
80 PIO1_23,
81 PIO1_24,
82 PIO1_25,
83 PIO1_26,
84 PIO1_27,
85 PIO1_28,
86 PIO1_29,
87 PIO1_30,
88 PIO1_31,
89} 93}
90 94
91/// HAL configuration for the NXP board. 95/// HAL configuration for the NXP board.
@@ -93,3 +97,20 @@ pub mod config {
93 #[derive(Default)] 97 #[derive(Default)]
94 pub struct Config {} 98 pub struct Config {}
95} 99}
100
101#[allow(unused)]
102struct BitIter(u32);
103
104impl Iterator for BitIter {
105 type Item = u32;
106
107 fn next(&mut self) -> Option<Self::Item> {
108 match self.0.trailing_zeros() {
109 32 => None,
110 b => {
111 self.0 &= !(1 << b);
112 Some(b)
113 }
114 }
115 }
116}
diff --git a/embassy-nxp/src/pint.rs b/embassy-nxp/src/pint.rs
index 8d6dc1277..ff414b4e6 100644
--- a/embassy-nxp/src/pint.rs
+++ b/embassy-nxp/src/pint.rs
@@ -7,9 +7,8 @@ use core::task::{Context, Poll};
7use critical_section::Mutex; 7use critical_section::Mutex;
8use embassy_sync::waitqueue::AtomicWaker; 8use embassy_sync::waitqueue::AtomicWaker;
9 9
10use crate::gpio::{self, AnyPin, Level, SealedPin}; 10use crate::gpio::{self, inputmux_reg, pint_reg, syscon_reg, AnyPin, Level, SealedPin};
11use crate::pac::interrupt; 11use crate::pac::interrupt;
12use crate::pac_utils::*;
13use crate::Peri; 12use crate::Peri;
14 13
15struct PinInterrupt { 14struct PinInterrupt {
@@ -102,6 +101,8 @@ pub(crate) fn init() {
102 crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT6); 101 crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT6);
103 crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT7); 102 crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT7);
104 }; 103 };
104
105 info!("Pin interrupts initialized");
105} 106}
106 107
107#[must_use = "futures do nothing unless you `.await` or poll them"] 108#[must_use = "futures do nothing unless you `.await` or poll them"]
diff --git a/embassy-nxp/src/time_driver/pit.rs b/embassy-nxp/src/time_driver/pit.rs
new file mode 100644
index 000000000..985e5e815
--- /dev/null
+++ b/embassy-nxp/src/time_driver/pit.rs
@@ -0,0 +1,187 @@
1//! Time driver using Periodic Interrupt Timer (PIT)
2//!
3//! This driver is used with the iMXRT1xxx parts.
4//!
5//! The PIT is run in lifetime mode. Timer 1 is chained to timer 0 to provide a free-running 64-bit timer.
6//! The 64-bit timer is used to track how many ticks since boot.
7//!
8//! Timer 2 counts how many ticks there are within the current u32::MAX tick period. Timer 2 is restarted when
9//! a new alarm is set (or every u32::MAX ticks). One caveat is that an alarm could be a few ticks late due to
10//! restart. However the Cortex-M7 cores run at 500 MHz easily and the PIT will generally run at 1 MHz or lower.
11//! Along with the fact that scheduling an alarm takes a critical section worst case an alarm may be a few
12//! microseconds late.
13//!
14//! All PIT timers are clocked in lockstep, so the late start will not cause the now() count to drift.
15
16use core::cell::{Cell, RefCell};
17use core::task::Waker;
18
19use critical_section::{CriticalSection, Mutex};
20use embassy_hal_internal::interrupt::InterruptExt;
21use embassy_time_driver::Driver as _;
22use embassy_time_queue_utils::Queue;
23
24use crate::pac::{self, interrupt};
25
26struct Driver {
27 alarm: Mutex<Cell<u64>>,
28 queue: Mutex<RefCell<Queue>>,
29}
30
31impl embassy_time_driver::Driver for Driver {
32 fn now(&self) -> u64 {
33 loop {
34 // Even though reading LTMR64H will latch LTMR64L if another thread preempts between any of the
35 // three reads and calls now() then the value in LTMR64L will be wrong when execution returns to
36 // thread which was preempted.
37 let hi = pac::PIT.ltmr64h().read().lth();
38 let lo = pac::PIT.ltmr64l().read().ltl();
39 let hi2 = pac::PIT.ltmr64h().read().lth();
40
41 if hi == hi2 {
42 // PIT timers always count down.
43 return u64::MAX - ((hi as u64) << 32 | (lo as u64));
44 }
45 }
46 }
47
48 fn schedule_wake(&self, at: u64, waker: &Waker) {
49 critical_section::with(|cs| {
50 let mut queue = self.queue.borrow(cs).borrow_mut();
51
52 if queue.schedule_wake(at, waker) {
53 let mut next = queue.next_expiration(self.now());
54
55 while !self.set_alarm(cs, next) {
56 next = queue.next_expiration(self.now());
57 }
58 }
59 })
60 }
61}
62
63impl Driver {
64 fn init(&'static self) {
65 // Disable PIT clock during mux configuration.
66 pac::CCM.ccgr1().modify(|r| r.set_cg6(0b00));
67
68 // TODO: This forces the PIT to be driven by the oscillator. However that isn't the only option as you
69 // could divide the clock root by up to 64.
70 pac::CCM.cscmr1().modify(|r| {
71 // 1 MHz
72 r.set_perclk_podf(pac::ccm::vals::PerclkPodf::DIVIDE_24);
73 r.set_perclk_clk_sel(pac::ccm::vals::PerclkClkSel::PERCLK_CLK_SEL_1);
74 });
75
76 pac::CCM.ccgr1().modify(|r| r.set_cg6(0b11));
77
78 // Disable clock during init.
79 //
80 // It is important that the PIT clock is prepared to not exceed limit (50 MHz on RT1011), or else
81 // you will need to recover the device with boot mode switches when using any PIT registers.
82 pac::PIT.mcr().modify(|w| {
83 w.set_mdis(true);
84 });
85
86 pac::PIT.timer(0).ldval().write_value(u32::MAX);
87 pac::PIT.timer(1).ldval().write_value(u32::MAX);
88 pac::PIT.timer(2).ldval().write_value(0);
89 pac::PIT.timer(3).ldval().write_value(0);
90
91 pac::PIT.timer(1).tctrl().write(|w| {
92 // In lifetime mode, timer 1 is chained to timer 0 to form a 64-bit timer.
93 w.set_chn(true);
94 w.set_ten(true);
95 w.set_tie(false);
96 });
97
98 pac::PIT.timer(0).tctrl().write(|w| {
99 w.set_chn(false);
100 w.set_ten(true);
101 w.set_tie(false);
102 });
103
104 pac::PIT.timer(2).tctrl().write(|w| {
105 w.set_tie(true);
106 });
107
108 unsafe { interrupt::PIT.enable() };
109
110 pac::PIT.mcr().write(|w| {
111 w.set_mdis(false);
112 });
113 }
114
115 fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
116 let alarm = self.alarm.borrow(cs);
117 alarm.set(timestamp);
118
119 let timer = pac::PIT.timer(2);
120 let now = self.now();
121
122 if timestamp <= now {
123 alarm.set(u64::MAX);
124
125 return false;
126 }
127
128 timer.tctrl().modify(|x| x.set_ten(false));
129 timer.tflg().modify(|x| x.set_tif(true));
130
131 // If the next alarm happens in more than u32::MAX cycles then the alarm will be restarted later.
132 timer.ldval().write_value((timestamp - now) as u32);
133 timer.tctrl().modify(|x| x.set_ten(true));
134
135 true
136 }
137
138 fn trigger_alarm(&self, cs: CriticalSection) {
139 let mut next = self.queue.borrow_ref_mut(cs).next_expiration(self.now());
140
141 while !self.set_alarm(cs, next) {
142 next = self.queue.borrow_ref_mut(cs).next_expiration(self.now());
143 }
144 }
145
146 fn on_interrupt(&self) {
147 critical_section::with(|cs| {
148 let timer = pac::PIT.timer(2);
149 let alarm = self.alarm.borrow(cs);
150 let interrupted = timer.tflg().read().tif();
151 timer.tflg().write(|r| r.set_tif(true));
152
153 if interrupted {
154 // A new load value will not apply until the next timer expiration.
155 //
156 // The expiry may be up to u32::MAX cycles away, so the timer must be restarted.
157 timer.tctrl().modify(|r| r.set_ten(false));
158
159 let now = self.now();
160 let timestamp = alarm.get();
161
162 if timestamp <= now {
163 self.trigger_alarm(cs);
164 } else {
165 // The alarm is not ready. Wait for u32::MAX cycles and check again or set the next alarm.
166 timer.ldval().write_value((timestamp - now) as u32);
167 timer.tctrl().modify(|r| r.set_ten(true));
168 }
169 }
170 });
171 }
172}
173
174embassy_time_driver::time_driver_impl!(static DRIVER: Driver = Driver {
175 alarm: Mutex::new(Cell::new(0)),
176 queue: Mutex::new(RefCell::new(Queue::new()))
177});
178
179pub(crate) fn init() {
180 DRIVER.init();
181}
182
183#[cfg(feature = "rt")]
184#[interrupt]
185fn PIT() {
186 DRIVER.on_interrupt();
187}
diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md
index 7ac0a47cb..36e1ea9b4 100644
--- a/embassy-rp/CHANGELOG.md
+++ b/embassy-rp/CHANGELOG.md
@@ -5,7 +5,40 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8## Unreleased 8<!-- next-header -->
9## Unreleased - ReleaseDate
10
11## 0.6.0 - 2025-07-16
12
13- update to latest embassy-usb-driver
14
15## 0.5.0 - 2025-07-15
16
17- Fix wrong `funcsel` on RP2350 gpout/gpin ([#3975](https://github.com/embassy-rs/embassy/pull/3975))
18- Fix potential race condition in `ADC::wait_for_ready` ([#4012](https://github.com/embassy-rs/embassy/pull/4012))
19- `flash`: rename `BOOTROM_BASE` to `BOOTRAM_BASE` ([#4014](https://github.com/embassy-rs/embassy/pull/4014))
20- Remove `Peripheral` trait & rename `PeripheralRef` to `Peri` ([#3999](https://github.com/embassy-rs/embassy/pull/3999))
21- Fix watchdog count on RP235x ([#4021](https://github.com/embassy-rs/embassy/pull/4021))
22- I2C: ensure that wakers are registered before checking status of `wait_on` helpers ([#4043](https://github.com/embassy-rs/embassy/pull/4043))
23- Modify `Uarte` and `BufferedUarte` initialization to take pins before interrupts ([#3983](https://github.com/embassy-rs/embassy/pull/3983))
24- `uart`: increase RX FIFO watermark from 1/8 to 7/8 ([#4055](https://github.com/embassy-rs/embassy/pull/4055))
25- Add `spinlock_mutex` ([#4017](https://github.com/embassy-rs/embassy/pull/4017))
26- Enable input mode for PWM pins on RP235x and disable it on drop ([#4093](https://github.com/embassy-rs/embassy/pull/4093))
27- Add `impl rand_core::CryptoRng for Trng` ([#4096](https://github.com/embassy-rs/embassy/pull/4096))
28- `pwm`: enable pull-down resistors for pins in `Drop` implementation ([#4115](https://github.com/embassy-rs/embassy/pull/4115))
29- Rewrite PIO onewire implementation ([#4128](https://github.com/embassy-rs/embassy/pull/4128))
30- Implement RP2040 overclocking ([#4150](https://github.com/embassy-rs/embassy/pull/4150))
31- Implement RP235x overclocking ([#4187](https://github.com/embassy-rs/embassy/pull/4187))
32- `trng`: improve error handling ([#4139](https://github.com/embassy-rs/embassy/pull/4139))
33- Remove `<T: Instance>` from `Uart` and `BufferedUart` ([#4155](https://github.com/embassy-rs/embassy/pull/4155))
34- Make bit-depth of I2S PIO program configurable ([#4193](https://github.com/embassy-rs/embassy/pull/4193))
35- Add the possibility to document `bind_interrupts` `struct`s ([#4206](https://github.com/embassy-rs/embassy/pull/4206))
36- Add missing `Debug` and `defmt::Format` `derive`s for ADC & `AnyPin` ([#4205](https://github.com/embassy-rs/embassy/pull/4205))
37- Add `rand-core` v0.9 support ([#4217](https://github.com/embassy-rs/embassy/pull/4217))
38- Update `embassy-sync` to v0.7.0 ([#4234](https://github.com/embassy-rs/embassy/pull/4234))
39- Add compatibility with ws2812 leds that have 4 addressable lights ([#4236](https://github.com/embassy-rs/embassy/pull/4236))
40- Implement input/output inversion ([#4237](https://github.com/embassy-rs/embassy/pull/4237))
41- Add `multicore::current_core` API ([#4362](https://github.com/embassy-rs/embassy/pull/4362))
9 42
10## 0.4.0 - 2025-03-09 43## 0.4.0 - 2025-03-09
11 44
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml
index 5df9e154c..dcf4e7178 100644
--- a/embassy-rp/Cargo.toml
+++ b/embassy-rp/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "embassy-rp" 2name = "embassy-rp"
3version = "0.4.0" 3version = "0.6.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6description = "Embassy Hardware Abstraction Layer (HAL) for the Raspberry Pi RP2040 or RP235x microcontroller" 6description = "Embassy Hardware Abstraction Layer (HAL) for the Raspberry Pi RP2040 or RP235x microcontroller"
@@ -141,9 +141,9 @@ embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", option
141embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } 141embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true }
142embassy-time = { version = "0.4.0", path = "../embassy-time" } 142embassy-time = { version = "0.4.0", path = "../embassy-time" }
143embassy-futures = { version = "0.1.0", path = "../embassy-futures" } 143embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
144embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } 144embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] }
145embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal" } 145embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal" }
146embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } 146embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" }
147atomic-polyfill = "1.0.1" 147atomic-polyfill = "1.0.1"
148defmt = { version = "1.0.1", optional = true } 148defmt = { version = "1.0.1", optional = true }
149log = { version = "0.4.14", optional = true } 149log = { version = "0.4.14", optional = true }
diff --git a/embassy-rp/release.toml b/embassy-rp/release.toml
new file mode 100644
index 000000000..fb6feaf21
--- /dev/null
+++ b/embassy-rp/release.toml
@@ -0,0 +1,5 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs
index ef1cd9212..8c809090e 100644
--- a/embassy-rp/src/flash.rs
+++ b/embassy-rp/src/flash.rs
@@ -482,7 +482,11 @@ mod ram_helpers {
482 /// # Safety 482 /// # Safety
483 /// 483 ///
484 /// `boot2` must contain a valid 2nd stage boot loader which can be called to re-initialize XIP mode 484 /// `boot2` must contain a valid 2nd stage boot loader which can be called to re-initialize XIP mode
485 unsafe fn flash_function_pointers_with_boot2(erase: bool, write: bool, boot2: &[u32; 64]) -> FlashFunctionPointers { 485 unsafe fn flash_function_pointers_with_boot2(
486 erase: bool,
487 write: bool,
488 boot2: &[u32; 64],
489 ) -> FlashFunctionPointers<'_> {
486 let boot2_fn_ptr = (boot2 as *const u32 as *const u8).offset(1); 490 let boot2_fn_ptr = (boot2 as *const u32 as *const u8).offset(1);
487 let boot2_fn: unsafe extern "C" fn() -> () = core::mem::transmute(boot2_fn_ptr); 491 let boot2_fn: unsafe extern "C" fn() -> () = core::mem::transmute(boot2_fn_ptr);
488 FlashFunctionPointers { 492 FlashFunctionPointers {
diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs
index 9b5faac15..f79bf8948 100644
--- a/embassy-rp/src/gpio.rs
+++ b/embassy-rp/src/gpio.rs
@@ -26,6 +26,7 @@ static QSPI_WAKERS: [AtomicWaker; QSPI_PIN_COUNT] = [const { AtomicWaker::new()
26 26
27/// Represents a digital input or output level. 27/// Represents a digital input or output level.
28#[derive(Debug, Eq, PartialEq, Clone, Copy)] 28#[derive(Debug, Eq, PartialEq, Clone, Copy)]
29#[cfg_attr(feature = "defmt", derive(defmt::Format))]
29pub enum Level { 30pub enum Level {
30 /// Logical low. 31 /// Logical low.
31 Low, 32 Low,
@@ -53,6 +54,7 @@ impl From<Level> for bool {
53 54
54/// Represents a pull setting for an input. 55/// Represents a pull setting for an input.
55#[derive(Debug, Clone, Copy, Eq, PartialEq)] 56#[derive(Debug, Clone, Copy, Eq, PartialEq)]
57#[cfg_attr(feature = "defmt", derive(defmt::Format))]
56pub enum Pull { 58pub enum Pull {
57 /// No pull. 59 /// No pull.
58 None, 60 None,
@@ -64,6 +66,7 @@ pub enum Pull {
64 66
65/// Drive strength of an output 67/// Drive strength of an output
66#[derive(Debug, Eq, PartialEq)] 68#[derive(Debug, Eq, PartialEq)]
69#[cfg_attr(feature = "defmt", derive(defmt::Format))]
67pub enum Drive { 70pub enum Drive {
68 /// 2 mA drive. 71 /// 2 mA drive.
69 _2mA, 72 _2mA,
@@ -76,6 +79,7 @@ pub enum Drive {
76} 79}
77/// Slew rate of an output 80/// Slew rate of an output
78#[derive(Debug, Eq, PartialEq)] 81#[derive(Debug, Eq, PartialEq)]
82#[cfg_attr(feature = "defmt", derive(defmt::Format))]
79pub enum SlewRate { 83pub enum SlewRate {
80 /// Fast slew rate. 84 /// Fast slew rate.
81 Fast, 85 Fast,
@@ -85,6 +89,7 @@ pub enum SlewRate {
85 89
86/// A GPIO bank with up to 32 pins. 90/// A GPIO bank with up to 32 pins.
87#[derive(Debug, Eq, PartialEq)] 91#[derive(Debug, Eq, PartialEq)]
92#[cfg_attr(feature = "defmt", derive(defmt::Format))]
88pub enum Bank { 93pub enum Bank {
89 /// Bank 0. 94 /// Bank 0.
90 Bank0 = 0, 95 Bank0 = 0,
@@ -108,6 +113,8 @@ pub struct DormantWakeConfig {
108} 113}
109 114
110/// GPIO input driver. 115/// GPIO input driver.
116#[derive(Debug)]
117#[cfg_attr(feature = "defmt", derive(defmt::Format))]
111pub struct Input<'d> { 118pub struct Input<'d> {
112 pin: Flex<'d>, 119 pin: Flex<'d>,
113} 120}
@@ -358,6 +365,8 @@ impl<'d> Future for InputFuture<'d> {
358} 365}
359 366
360/// GPIO output driver. 367/// GPIO output driver.
368#[derive(Debug)]
369#[cfg_attr(feature = "defmt", derive(defmt::Format))]
361pub struct Output<'d> { 370pub struct Output<'d> {
362 pin: Flex<'d>, 371 pin: Flex<'d>,
363} 372}
@@ -445,6 +454,8 @@ impl<'d> Output<'d> {
445} 454}
446 455
447/// GPIO output open-drain. 456/// GPIO output open-drain.
457#[derive(Debug)]
458#[cfg_attr(feature = "defmt", derive(defmt::Format))]
448pub struct OutputOpenDrain<'d> { 459pub struct OutputOpenDrain<'d> {
449 pin: Flex<'d>, 460 pin: Flex<'d>,
450} 461}
@@ -592,6 +603,8 @@ impl<'d> OutputOpenDrain<'d> {
592/// This pin can be either an input or output pin. The output level register bit will remain 603/// This pin can be either an input or output pin. The output level register bit will remain
593/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output 604/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output
594/// mode. 605/// mode.
606#[derive(Debug)]
607#[cfg_attr(feature = "defmt", derive(defmt::Format))]
595pub struct Flex<'d> { 608pub struct Flex<'d> {
596 pin: Peri<'d, AnyPin>, 609 pin: Peri<'d, AnyPin>,
597} 610}
@@ -864,6 +877,8 @@ impl<'d> Drop for Flex<'d> {
864} 877}
865 878
866/// Dormant wake driver. 879/// Dormant wake driver.
880#[derive(Debug)]
881#[cfg_attr(feature = "defmt", derive(defmt::Format))]
867pub struct DormantWake<'w> { 882pub struct DormantWake<'w> {
868 pin: Peri<'w, AnyPin>, 883 pin: Peri<'w, AnyPin>,
869 cfg: DormantWakeConfig, 884 cfg: DormantWakeConfig,
diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs
index a983b7bc3..172193a07 100644
--- a/embassy-rp/src/i2c.rs
+++ b/embassy-rp/src/i2c.rs
@@ -76,6 +76,7 @@ impl Default for Config {
76pub const FIFO_SIZE: u8 = 16; 76pub const FIFO_SIZE: u8 = 16;
77 77
78/// I2C driver. 78/// I2C driver.
79#[derive(Debug)]
79pub struct I2c<'d, T: Instance, M: Mode> { 80pub struct I2c<'d, T: Instance, M: Mode> {
80 phantom: PhantomData<(&'d mut T, M)>, 81 phantom: PhantomData<(&'d mut T, M)>,
81} 82}
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index f3c5a35bb..eb497de1a 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -189,11 +189,13 @@ macro_rules! bind_interrupts {
189 #[no_mangle] 189 #[no_mangle]
190 $(#[cfg($cond_irq)])? 190 $(#[cfg($cond_irq)])?
191 unsafe extern "C" fn $irq() { 191 unsafe extern "C" fn $irq() {
192 $( 192 unsafe {
193 $(#[cfg($cond_handler)])? 193 $(
194 <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); 194 $(#[cfg($cond_handler)])?
195 <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
195 196
196 )* 197 )*
198 }
197 } 199 }
198 200
199 $(#[cfg($cond_irq)])? 201 $(#[cfg($cond_irq)])?
@@ -567,7 +569,7 @@ unsafe fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> {
567 unsafe { 569 unsafe {
568 core.MPU.ctrl.write(5); // enable mpu with background default map 570 core.MPU.ctrl.write(5); // enable mpu with background default map
569 core.MPU.rbar.write(stack_bottom as u32 & !0xff); // set address 571 core.MPU.rbar.write(stack_bottom as u32 & !0xff); // set address
570 core.MPU.rlar.write(1); // enable region 572 core.MPU.rlar.write(((stack_bottom as usize + 255) as u32) | 1);
571 } 573 }
572 Ok(()) 574 Ok(())
573} 575}
diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs
index d10b6837c..64065fcba 100644
--- a/embassy-rp/src/multicore.rs
+++ b/embassy-rp/src/multicore.rs
@@ -57,6 +57,26 @@ const PAUSE_TOKEN: u32 = 0xDEADBEEF;
57const RESUME_TOKEN: u32 = !0xDEADBEEF; 57const RESUME_TOKEN: u32 = !0xDEADBEEF;
58static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false); 58static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false);
59 59
60/// Represents a partiticular CPU core (SIO_CPUID)
61#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
62#[cfg_attr(feature = "defmt", derive(defmt::Format))]
63#[repr(u8)]
64pub enum CoreId {
65 /// Core 0
66 Core0 = 0x0,
67 /// Core 1
68 Core1 = 0x1,
69}
70
71/// Gets which core we are currently executing from
72pub fn current_core() -> CoreId {
73 if pac::SIO.cpuid().read() == 0 {
74 CoreId::Core0
75 } else {
76 CoreId::Core1
77 }
78}
79
60#[inline(always)] 80#[inline(always)]
61unsafe fn core1_setup(stack_bottom: *mut usize) { 81unsafe fn core1_setup(stack_bottom: *mut usize) {
62 if install_stack_guard(stack_bottom).is_err() { 82 if install_stack_guard(stack_bottom).is_err() {
diff --git a/embassy-rp/src/relocate.rs b/embassy-rp/src/relocate.rs
index 34487819f..6ff40ddd7 100644
--- a/embassy-rp/src/relocate.rs
+++ b/embassy-rp/src/relocate.rs
@@ -39,7 +39,7 @@ pub struct RelocatedProgram<'a, const PROGRAM_SIZE: usize> {
39} 39}
40 40
41impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> { 41impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> {
42 pub fn new_with_origin(program: &Program<PROGRAM_SIZE>, origin: u8) -> RelocatedProgram<PROGRAM_SIZE> { 42 pub fn new_with_origin(program: &Program<PROGRAM_SIZE>, origin: u8) -> RelocatedProgram<'_, PROGRAM_SIZE> {
43 RelocatedProgram { program, origin } 43 RelocatedProgram { program, origin }
44 } 44 }
45 45
diff --git a/embassy-rp/src/rom_data/mod.rs b/embassy-rp/src/rom_data/mod.rs
index e5fcf8e3c..a4aba5737 100644
--- a/embassy-rp/src/rom_data/mod.rs
+++ b/embassy-rp/src/rom_data/mod.rs
@@ -1,29 +1,29 @@
1#![cfg_attr( 1#![cfg_attr(
2 feature = "rp2040", 2 feature = "rp2040",
3 doc = r" 3 doc = r"
4//! Functions and data from the RPI Bootrom. 4Functions and data from the RPI Bootrom.
5//! 5
6//! From the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), Section 2.8.2.1: 6From the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), Section 2.8.2.1:
7//! 7
8//! > The Bootrom contains a number of public functions that provide useful 8> The Bootrom contains a number of public functions that provide useful
9//! > RP2040 functionality that might be needed in the absence of any other code 9> RP2040 functionality that might be needed in the absence of any other code
10//! > on the device, as well as highly optimized versions of certain key 10> on the device, as well as highly optimized versions of certain key
11//! > functionality that would otherwise have to take up space in most user 11> functionality that would otherwise have to take up space in most user
12//! > binaries. 12> binaries.
13" 13"
14)] 14)]
15#![cfg_attr( 15#![cfg_attr(
16 feature = "_rp235x", 16 feature = "_rp235x",
17 doc = r" 17 doc = r"
18//! Functions and data from the RPI Bootrom. 18Functions and data from the RPI Bootrom.
19//! 19
20//! From [Section 5.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the 20From [Section 5.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the
21//! RP2350 datasheet: 21RP2350 datasheet:
22//! 22
23//! > Whilst some ROM space is dedicated to the implementation of the boot 23> Whilst some ROM space is dedicated to the implementation of the boot
24//! > sequence and USB/UART boot interfaces, the bootrom also contains public 24> sequence and USB/UART boot interfaces, the bootrom also contains public
25//! > functions that provide useful RP2350 functionality that may be useful for 25> functions that provide useful RP2350 functionality that may be useful for
26//! > any code or runtime running on the device 26> any code or runtime running on the device
27" 27"
28)] 28)]
29 29
diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs
index c3a15fda5..6f4e2ee07 100644
--- a/embassy-rp/src/uart/mod.rs
+++ b/embassy-rp/src/uart/mod.rs
@@ -495,52 +495,58 @@ impl<'d> UartRx<'d, Async> {
495 unreachable!("unrecognized rx error"); 495 unreachable!("unrecognized rx error");
496 } 496 }
497 497
498 /// Read from the UART, waiting for a line break. 498 /// Read from the UART, waiting for a break.
499 /// 499 ///
500 /// We read until one of the following occurs: 500 /// We read until one of the following occurs:
501 /// 501 ///
502 /// * We read `buffer.len()` bytes without a line break 502 /// * We read `buffer.len()` bytes without a break
503 /// * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))` 503 /// * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))`
504 /// * We read `n` bytes then a line break occurs 504 /// * We read `n` bytes then a break occurs
505 /// * returns `Ok(n)` 505 /// * returns `Ok(n)`
506 /// * We encounter some error OTHER than a line break 506 /// * We encounter some error OTHER than a break
507 /// * returns `Err(ReadToBreakError::Other(error))` 507 /// * returns `Err(ReadToBreakError::Other(error))`
508 /// 508 ///
509 /// **NOTE**: you MUST provide a buffer one byte larger than your largest expected 509 /// **NOTE**: you MUST provide a buffer one byte larger than your largest expected
510 /// message to reliably detect the framing on one single call to `read_to_break()`. 510 /// message to reliably detect the framing on one single call to `read_to_break()`.
511 /// 511 ///
512 /// * If you expect a message of 20 bytes + line break, and provide a 20-byte buffer: 512 /// * If you expect a message of 20 bytes + break, and provide a 20-byte buffer:
513 /// * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))` 513 /// * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))`
514 /// * The next call to `read_to_break()` will immediately return `Ok(0)`, from the "stale" line break 514 /// * The next call to `read_to_break()` will immediately return `Ok(0)`, from the "stale" break
515 /// * If you expect a message of 20 bytes + line break, and provide a 21-byte buffer: 515 /// * If you expect a message of 20 bytes + break, and provide a 21-byte buffer:
516 /// * The first call to `read_to_break()` will return `Ok(20)`. 516 /// * The first call to `read_to_break()` will return `Ok(20)`.
517 /// * The next call to `read_to_break()` will work as expected 517 /// * The next call to `read_to_break()` will work as expected
518 ///
519 /// **NOTE**: In the UART context, a break refers to a break condition (the line being held low for
520 /// for longer than a single character), not an ASCII line break.
518 pub async fn read_to_break(&mut self, buffer: &mut [u8]) -> Result<usize, ReadToBreakError> { 521 pub async fn read_to_break(&mut self, buffer: &mut [u8]) -> Result<usize, ReadToBreakError> {
519 self.read_to_break_with_count(buffer, 0).await 522 self.read_to_break_with_count(buffer, 0).await
520 } 523 }
521 524
522 /// Read from the UART, waiting for a line break as soon as at least `min_count` bytes have been read. 525 /// Read from the UART, waiting for a break as soon as at least `min_count` bytes have been read.
523 /// 526 ///
524 /// We read until one of the following occurs: 527 /// We read until one of the following occurs:
525 /// 528 ///
526 /// * We read `buffer.len()` bytes without a line break 529 /// * We read `buffer.len()` bytes without a break
527 /// * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))` 530 /// * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))`
528 /// * We read `n > min_count` bytes then a line break occurs 531 /// * We read `n > min_count` bytes then a break occurs
529 /// * returns `Ok(n)` 532 /// * returns `Ok(n)`
530 /// * We encounter some error OTHER than a line break 533 /// * We encounter some error OTHER than a break
531 /// * returns `Err(ReadToBreakError::Other(error))` 534 /// * returns `Err(ReadToBreakError::Other(error))`
532 /// 535 ///
533 /// If a line break occurs before `min_count` bytes have been read, the break will be ignored and the read will continue 536 /// If a break occurs before `min_count` bytes have been read, the break will be ignored and the read will continue
534 /// 537 ///
535 /// **NOTE**: you MUST provide a buffer one byte larger than your largest expected 538 /// **NOTE**: you MUST provide a buffer one byte larger than your largest expected
536 /// message to reliably detect the framing on one single call to `read_to_break()`. 539 /// message to reliably detect the framing on one single call to `read_to_break()`.
537 /// 540 ///
538 /// * If you expect a message of 20 bytes + line break, and provide a 20-byte buffer: 541 /// * If you expect a message of 20 bytes + break, and provide a 20-byte buffer:
539 /// * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))` 542 /// * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))`
540 /// * The next call to `read_to_break()` will immediately return `Ok(0)`, from the "stale" line break 543 /// * The next call to `read_to_break()` will immediately return `Ok(0)`, from the "stale" line break
541 /// * If you expect a message of 20 bytes + line break, and provide a 21-byte buffer: 544 /// * If you expect a message of 20 bytes + break, and provide a 21-byte buffer:
542 /// * The first call to `read_to_break()` will return `Ok(20)`. 545 /// * The first call to `read_to_break()` will return `Ok(20)`.
543 /// * The next call to `read_to_break()` will work as expected 546 /// * The next call to `read_to_break()` will work as expected
547 ///
548 /// **NOTE**: In the UART context, a break refers to a break condition (the line being held low for
549 /// for longer than a single character), not an ASCII line break.
544 pub async fn read_to_break_with_count( 550 pub async fn read_to_break_with_count(
545 &mut self, 551 &mut self,
546 buffer: &mut [u8], 552 buffer: &mut [u8],
diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs
index 96541ade6..671ecbd32 100644
--- a/embassy-rp/src/usb.rs
+++ b/embassy-rp/src/usb.rs
@@ -153,6 +153,7 @@ impl<'d, T: Instance> Driver<'d, T> {
153 fn alloc_endpoint<D: Dir>( 153 fn alloc_endpoint<D: Dir>(
154 &mut self, 154 &mut self,
155 ep_type: EndpointType, 155 ep_type: EndpointType,
156 ep_addr: Option<EndpointAddress>,
156 max_packet_size: u16, 157 max_packet_size: u16,
157 interval_ms: u8, 158 interval_ms: u8,
158 ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> { 159 ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> {
@@ -169,12 +170,25 @@ impl<'d, T: Instance> Driver<'d, T> {
169 Direction::In => &mut self.ep_in, 170 Direction::In => &mut self.ep_in,
170 }; 171 };
171 172
172 let index = alloc.iter_mut().enumerate().find(|(i, ep)| { 173 let index = if let Some(addr) = ep_addr {
173 if *i == 0 { 174 // Use the specified endpoint address
174 return false; // reserved for control pipe 175 let requested_index = addr.index();
176 if requested_index == 0 || requested_index >= EP_COUNT {
177 return Err(EndpointAllocError);
175 } 178 }
176 !ep.used 179 if alloc[requested_index].used {
177 }); 180 return Err(EndpointAllocError);
181 }
182 Some((requested_index, &mut alloc[requested_index]))
183 } else {
184 // Find any available endpoint
185 alloc.iter_mut().enumerate().find(|(i, ep)| {
186 if *i == 0 {
187 return false; // reserved for control pipe
188 }
189 !ep.used
190 })
191 };
178 192
179 let (index, ep) = index.ok_or(EndpointAllocError)?; 193 let (index, ep) = index.ok_or(EndpointAllocError)?;
180 assert!(!ep.used); 194 assert!(!ep.used);
@@ -299,19 +313,21 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
299 fn alloc_endpoint_in( 313 fn alloc_endpoint_in(
300 &mut self, 314 &mut self,
301 ep_type: EndpointType, 315 ep_type: EndpointType,
316 ep_addr: Option<EndpointAddress>,
302 max_packet_size: u16, 317 max_packet_size: u16,
303 interval_ms: u8, 318 interval_ms: u8,
304 ) -> Result<Self::EndpointIn, driver::EndpointAllocError> { 319 ) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
305 self.alloc_endpoint(ep_type, max_packet_size, interval_ms) 320 self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms)
306 } 321 }
307 322
308 fn alloc_endpoint_out( 323 fn alloc_endpoint_out(
309 &mut self, 324 &mut self,
310 ep_type: EndpointType, 325 ep_type: EndpointType,
326 ep_addr: Option<EndpointAddress>,
311 max_packet_size: u16, 327 max_packet_size: u16,
312 interval_ms: u8, 328 interval_ms: u8,
313 ) -> Result<Self::EndpointOut, driver::EndpointAllocError> { 329 ) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
314 self.alloc_endpoint(ep_type, max_packet_size, interval_ms) 330 self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms)
315 } 331 }
316 332
317 fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { 333 fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml
index b749625aa..a16aa4b54 100644
--- a/embassy-stm32-wpan/Cargo.toml
+++ b/embassy-stm32-wpan/Cargo.toml
@@ -23,8 +23,8 @@ embassy-stm32 = { version = "0.2.0", path = "../embassy-stm32" }
23embassy-sync = { version = "0.7.0", path = "../embassy-sync" } 23embassy-sync = { version = "0.7.0", path = "../embassy-sync" }
24embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } 24embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true }
25embassy-futures = { version = "0.1.0", path = "../embassy-futures" } 25embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
26embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal" } 26embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal" }
27embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal" } 27embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal" }
28embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true } 28embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true }
29 29
30defmt = { version = "1.0.1", optional = true } 30defmt = { version = "1.0.1", optional = true }
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index b6781905e..c4c2cd013 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8## Unreleased 8## Unreleased
9- Modify BufferedUart initialization to take pins before interrupts ([#3983](https://github.com/embassy-rs/embassy/pull/3983)) 9- Modify BufferedUart initialization to take pins before interrupts ([#3983](https://github.com/embassy-rs/embassy/pull/3983))
10- Added a 'single-bank' and a 'dual-bank' feature so chips with configurable flash bank setups are be supported in embassy ([#4125](https://github.com/embassy-rs/embassy/pull/4125)) 10- Added a 'single-bank' and a 'dual-bank' feature so chips with configurable flash bank setups are be supported in embassy ([#4125](https://github.com/embassy-rs/embassy/pull/4125))
11- Add automatic setting of remap bits when using alternate DMA channels on STM32F0 and STM32F3 devices ([#3653](https://github.com/embassy-rs/embassy/pull/3653))
11 12
12## 0.2.0 - 2025-01-10 13## 0.2.0 - 2025-01-10
13 14
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 034f51df9..02e75733e 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -53,11 +53,11 @@ embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true }
53embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true } 53embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true }
54embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } 54embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true }
55embassy-futures = { version = "0.1.0", path = "../embassy-futures" } 55embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
56embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } 56embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] }
57embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } 57embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal", default-features = false }
58embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } 58embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
59embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } 59embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" }
60embassy-usb-synopsys-otg = { version = "0.2.0", path = "../embassy-usb-synopsys-otg" } 60embassy-usb-synopsys-otg = { version = "0.3.0", path = "../embassy-usb-synopsys-otg" }
61embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional = true } 61embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional = true }
62 62
63embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 63embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
@@ -81,7 +81,7 @@ futures-util = { version = "0.3.30", default-features = false }
81sdio-host = "0.9.0" 81sdio-host = "0.9.0"
82critical-section = "1.1" 82critical-section = "1.1"
83#stm32-metapac = { version = "16" } 83#stm32-metapac = { version = "16" }
84stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-27ef8fba3483187e852eaf3796d827259f61e8ec" } 84stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9fc86ca7b3a8bc05182bf1ce3045602df1f5dce3" }
85 85
86vcell = "0.1.3" 86vcell = "0.1.3"
87nb = "1.0.0" 87nb = "1.0.0"
@@ -110,7 +110,7 @@ proc-macro2 = "1.0.36"
110quote = "1.0.15" 110quote = "1.0.15"
111 111
112#stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} 112#stm32-metapac = { version = "16", default-features = false, features = ["metadata"]}
113stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-27ef8fba3483187e852eaf3796d827259f61e8ec", default-features = false, features = ["metadata"] } 113stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9fc86ca7b3a8bc05182bf1ce3045602df1f5dce3", default-features = false, features = ["metadata"] }
114 114
115[features] 115[features]
116default = ["rt"] 116default = ["rt"]
@@ -129,6 +129,7 @@ defmt = [
129 "embassy-net-driver/defmt", 129 "embassy-net-driver/defmt",
130 "embassy-time?/defmt", 130 "embassy-time?/defmt",
131 "embassy-usb-synopsys-otg/defmt", 131 "embassy-usb-synopsys-otg/defmt",
132 "stm32-metapac/defmt"
132] 133]
133 134
134exti = [] 135exti = []
@@ -237,6 +238,47 @@ stm32c031g4 = [ "stm32-metapac/stm32c031g4" ]
237stm32c031g6 = [ "stm32-metapac/stm32c031g6" ] 238stm32c031g6 = [ "stm32-metapac/stm32c031g6" ]
238stm32c031k4 = [ "stm32-metapac/stm32c031k4" ] 239stm32c031k4 = [ "stm32-metapac/stm32c031k4" ]
239stm32c031k6 = [ "stm32-metapac/stm32c031k6" ] 240stm32c031k6 = [ "stm32-metapac/stm32c031k6" ]
241stm32c051c6 = [ "stm32-metapac/stm32c051c6" ]
242stm32c051c8 = [ "stm32-metapac/stm32c051c8" ]
243stm32c051d8 = [ "stm32-metapac/stm32c051d8" ]
244stm32c051f6 = [ "stm32-metapac/stm32c051f6" ]
245stm32c051f8 = [ "stm32-metapac/stm32c051f8" ]
246stm32c051g6 = [ "stm32-metapac/stm32c051g6" ]
247stm32c051g8 = [ "stm32-metapac/stm32c051g8" ]
248stm32c051k6 = [ "stm32-metapac/stm32c051k6" ]
249stm32c051k8 = [ "stm32-metapac/stm32c051k8" ]
250stm32c071c8 = [ "stm32-metapac/stm32c071c8" ]
251stm32c071cb = [ "stm32-metapac/stm32c071cb" ]
252stm32c071f8 = [ "stm32-metapac/stm32c071f8" ]
253stm32c071fb = [ "stm32-metapac/stm32c071fb" ]
254stm32c071g8 = [ "stm32-metapac/stm32c071g8" ]
255stm32c071gb = [ "stm32-metapac/stm32c071gb" ]
256stm32c071k8 = [ "stm32-metapac/stm32c071k8" ]
257stm32c071kb = [ "stm32-metapac/stm32c071kb" ]
258stm32c071r8 = [ "stm32-metapac/stm32c071r8" ]
259stm32c071rb = [ "stm32-metapac/stm32c071rb" ]
260stm32c091cb = [ "stm32-metapac/stm32c091cb" ]
261stm32c091cc = [ "stm32-metapac/stm32c091cc" ]
262stm32c091ec = [ "stm32-metapac/stm32c091ec" ]
263stm32c091fb = [ "stm32-metapac/stm32c091fb" ]
264stm32c091fc = [ "stm32-metapac/stm32c091fc" ]
265stm32c091gb = [ "stm32-metapac/stm32c091gb" ]
266stm32c091gc = [ "stm32-metapac/stm32c091gc" ]
267stm32c091kb = [ "stm32-metapac/stm32c091kb" ]
268stm32c091kc = [ "stm32-metapac/stm32c091kc" ]
269stm32c091rb = [ "stm32-metapac/stm32c091rb" ]
270stm32c091rc = [ "stm32-metapac/stm32c091rc" ]
271stm32c092cb = [ "stm32-metapac/stm32c092cb" ]
272stm32c092cc = [ "stm32-metapac/stm32c092cc" ]
273stm32c092ec = [ "stm32-metapac/stm32c092ec" ]
274stm32c092fb = [ "stm32-metapac/stm32c092fb" ]
275stm32c092fc = [ "stm32-metapac/stm32c092fc" ]
276stm32c092gb = [ "stm32-metapac/stm32c092gb" ]
277stm32c092gc = [ "stm32-metapac/stm32c092gc" ]
278stm32c092kb = [ "stm32-metapac/stm32c092kb" ]
279stm32c092kc = [ "stm32-metapac/stm32c092kc" ]
280stm32c092rb = [ "stm32-metapac/stm32c092rb" ]
281stm32c092rc = [ "stm32-metapac/stm32c092rc" ]
240stm32f030c6 = [ "stm32-metapac/stm32f030c6" ] 282stm32f030c6 = [ "stm32-metapac/stm32f030c6" ]
241stm32f030c8 = [ "stm32-metapac/stm32f030c8" ] 283stm32f030c8 = [ "stm32-metapac/stm32f030c8" ]
242stm32f030cc = [ "stm32-metapac/stm32f030cc" ] 284stm32f030cc = [ "stm32-metapac/stm32f030cc" ]
@@ -1634,6 +1676,24 @@ stm32wba55he = [ "stm32-metapac/stm32wba55he" ]
1634stm32wba55hg = [ "stm32-metapac/stm32wba55hg" ] 1676stm32wba55hg = [ "stm32-metapac/stm32wba55hg" ]
1635stm32wba55ue = [ "stm32-metapac/stm32wba55ue" ] 1677stm32wba55ue = [ "stm32-metapac/stm32wba55ue" ]
1636stm32wba55ug = [ "stm32-metapac/stm32wba55ug" ] 1678stm32wba55ug = [ "stm32-metapac/stm32wba55ug" ]
1679stm32wba62cg = [ "stm32-metapac/stm32wba62cg" ]
1680stm32wba62ci = [ "stm32-metapac/stm32wba62ci" ]
1681stm32wba62mg = [ "stm32-metapac/stm32wba62mg" ]
1682stm32wba62mi = [ "stm32-metapac/stm32wba62mi" ]
1683stm32wba62pg = [ "stm32-metapac/stm32wba62pg" ]
1684stm32wba62pi = [ "stm32-metapac/stm32wba62pi" ]
1685stm32wba63cg = [ "stm32-metapac/stm32wba63cg" ]
1686stm32wba63ci = [ "stm32-metapac/stm32wba63ci" ]
1687stm32wba64cg = [ "stm32-metapac/stm32wba64cg" ]
1688stm32wba64ci = [ "stm32-metapac/stm32wba64ci" ]
1689stm32wba65cg = [ "stm32-metapac/stm32wba65cg" ]
1690stm32wba65ci = [ "stm32-metapac/stm32wba65ci" ]
1691stm32wba65mg = [ "stm32-metapac/stm32wba65mg" ]
1692stm32wba65mi = [ "stm32-metapac/stm32wba65mi" ]
1693stm32wba65pg = [ "stm32-metapac/stm32wba65pg" ]
1694stm32wba65pi = [ "stm32-metapac/stm32wba65pi" ]
1695stm32wba65rg = [ "stm32-metapac/stm32wba65rg" ]
1696stm32wba65ri = [ "stm32-metapac/stm32wba65ri" ]
1637stm32wl54cc-cm4 = [ "stm32-metapac/stm32wl54cc-cm4", "_dual-core", "_core-cm4" ] 1697stm32wl54cc-cm4 = [ "stm32-metapac/stm32wl54cc-cm4", "_dual-core", "_core-cm4" ]
1638stm32wl54cc-cm0p = [ "stm32-metapac/stm32wl54cc-cm0p", "_dual-core", "_core-cm0p" ] 1698stm32wl54cc-cm0p = [ "stm32-metapac/stm32wl54cc-cm0p", "_dual-core", "_core-cm0p" ]
1639stm32wl54jc-cm4 = [ "stm32-metapac/stm32wl54jc-cm4", "_dual-core", "_core-cm4" ] 1699stm32wl54jc-cm4 = [ "stm32-metapac/stm32wl54jc-cm4", "_dual-core", "_core-cm4" ]
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index bb5ef53d7..73860c64a 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -505,6 +505,13 @@ fn main() {
505 field: "CLK48SEL", 505 field: "CLK48SEL",
506 }, 506 },
507 ); 507 );
508 clock_gen.chained_muxes.insert(
509 "RFWKP",
510 &PeripheralRccRegister {
511 register: "CSR",
512 field: "RFWKPSEL",
513 },
514 );
508 } 515 }
509 if chip_name.starts_with("stm32f7") { 516 if chip_name.starts_with("stm32f7") {
510 clock_gen.chained_muxes.insert( 517 clock_gen.chained_muxes.insert(
@@ -1090,21 +1097,21 @@ fn main() {
1090 (("fmc", "CLK"), quote!(crate::fmc::ClkPin)), 1097 (("fmc", "CLK"), quote!(crate::fmc::ClkPin)),
1091 (("fmc", "BA0"), quote!(crate::fmc::BA0Pin)), 1098 (("fmc", "BA0"), quote!(crate::fmc::BA0Pin)),
1092 (("fmc", "BA1"), quote!(crate::fmc::BA1Pin)), 1099 (("fmc", "BA1"), quote!(crate::fmc::BA1Pin)),
1093 (("timer", "CH1"), quote!(crate::timer::Channel1Pin)), 1100 (("timer", "CH1"), quote!(crate::timer::TimerPin<Ch1>)),
1094 (("timer", "CH1N"), quote!(crate::timer::Channel1ComplementaryPin)), 1101 (("timer", "CH1N"), quote!(crate::timer::TimerComplementaryPin<Ch1>)),
1095 (("timer", "CH2"), quote!(crate::timer::Channel2Pin)), 1102 (("timer", "CH2"), quote!(crate::timer::TimerPin<Ch2>)),
1096 (("timer", "CH2N"), quote!(crate::timer::Channel2ComplementaryPin)), 1103 (("timer", "CH2N"), quote!(crate::timer::TimerComplementaryPin<Ch2>)),
1097 (("timer", "CH3"), quote!(crate::timer::Channel3Pin)), 1104 (("timer", "CH3"), quote!(crate::timer::TimerPin<Ch3>)),
1098 (("timer", "CH3N"), quote!(crate::timer::Channel3ComplementaryPin)), 1105 (("timer", "CH3N"), quote!(crate::timer::TimerComplementaryPin<Ch3>)),
1099 (("timer", "CH4"), quote!(crate::timer::Channel4Pin)), 1106 (("timer", "CH4"), quote!(crate::timer::TimerPin<Ch4>)),
1100 (("timer", "CH4N"), quote!(crate::timer::Channel4ComplementaryPin)), 1107 (("timer", "CH4N"), quote!(crate::timer::TimerComplementaryPin<Ch4>)),
1101 (("timer", "ETR"), quote!(crate::timer::ExternalTriggerPin)), 1108 (("timer", "ETR"), quote!(crate::timer::ExternalTriggerPin)),
1102 (("timer", "BKIN"), quote!(crate::timer::BreakInputPin)), 1109 (("timer", "BKIN"), quote!(crate::timer::BreakInputPin<BkIn1>)),
1103 (("timer", "BKIN_COMP1"), quote!(crate::timer::BreakInputComparator1Pin)), 1110 (("timer", "BKIN_COMP1"), quote!(crate::timer::BreakInputComparator1Pin<BkIn1>)),
1104 (("timer", "BKIN_COMP2"), quote!(crate::timer::BreakInputComparator2Pin)), 1111 (("timer", "BKIN_COMP2"), quote!(crate::timer::BreakInputComparator2Pin<BkIn1>)),
1105 (("timer", "BKIN2"), quote!(crate::timer::BreakInput2Pin)), 1112 (("timer", "BKIN2"), quote!(crate::timer::BreakInputPin<BkIn2>)),
1106 (("timer", "BKIN2_COMP1"), quote!(crate::timer::BreakInput2Comparator1Pin)), 1113 (("timer", "BKIN2_COMP1"), quote!(crate::timer::BreakInputComparator1Pin<BkIn2>)),
1107 (("timer", "BKIN2_COMP2"), quote!(crate::timer::BreakInput2Comparator2Pin)), 1114 (("timer", "BKIN2_COMP2"), quote!(crate::timer::BreakInputComparator2Pin<BkIn2>)),
1108 (("hrtim", "CHA1"), quote!(crate::hrtim::ChannelAPin)), 1115 (("hrtim", "CHA1"), quote!(crate::hrtim::ChannelAPin)),
1109 (("hrtim", "CHA2"), quote!(crate::hrtim::ChannelAComplementaryPin)), 1116 (("hrtim", "CHA2"), quote!(crate::hrtim::ChannelAComplementaryPin)),
1110 (("hrtim", "CHB1"), quote!(crate::hrtim::ChannelBPin)), 1117 (("hrtim", "CHB1"), quote!(crate::hrtim::ChannelBPin)),
@@ -1402,31 +1409,24 @@ fn main() {
1402 } 1409 }
1403 1410
1404 if regs.kind == "opamp" { 1411 if regs.kind == "opamp" {
1405 if pin.signal.starts_with("VP") { 1412 let peri = format_ident!("{}", p.name);
1406 // Impl NonInvertingPin for the VP* signals (VP0, VP1, VP2, etc) 1413 let pin_name = format_ident!("{}", pin.pin);
1407 let peri = format_ident!("{}", p.name); 1414 if let Some(ch_str) = pin.signal.strip_prefix("VINP") {
1408 let pin_name = format_ident!("{}", pin.pin); 1415 // Impl NonInvertingPin for VINP0, VINP1 etc.
1409 let ch: u8 = pin.signal.strip_prefix("VP").unwrap().parse().unwrap(); 1416 if let Ok(ch) = ch_str.parse::<u8>() {
1410 1417 g.extend(quote! {
1411 g.extend(quote! { 1418 impl_opamp_vp_pin!( #peri, #pin_name, #ch );
1412 impl_opamp_vp_pin!( #peri, #pin_name, #ch); 1419 });
1413 }) 1420 }
1414 } else if pin.signal.starts_with("VINM") { 1421 } else if let Some(ch_str) = pin.signal.strip_prefix("VINM") {
1415 // Impl NonInvertingPin for the VINM* signals ( VINM0, VINM1, etc) 1422 // Impl InvertingPin for VINM0, VINM1 etc.
1416 // STM32G4 1423 if let Ok(ch) = ch_str.parse::<u8>() {
1417 let peri = format_ident!("{}", p.name);
1418 let pin_name = format_ident!("{}", pin.pin);
1419 let ch: Result<u8, _> = pin.signal.strip_prefix("VINM").unwrap().parse();
1420
1421 if let Ok(ch) = ch {
1422 g.extend(quote! { 1424 g.extend(quote! {
1423 impl_opamp_vn_pin!( #peri, #pin_name, #ch); 1425 impl_opamp_vn_pin!( #peri, #pin_name, #ch);
1424 }) 1426 });
1425 } 1427 }
1426 } else if pin.signal == "VOUT" { 1428 } else if pin.signal == "VOUT" {
1427 // Impl OutputPin for the VOUT pin 1429 // Impl OutputPin for the VOUT pin
1428 let peri = format_ident!("{}", p.name);
1429 let pin_name = format_ident!("{}", pin.pin);
1430 g.extend(quote! { 1430 g.extend(quote! {
1431 impl_opamp_vout_pin!( #peri, #pin_name ); 1431 impl_opamp_vout_pin!( #peri, #pin_name );
1432 }) 1432 })
@@ -1482,10 +1482,10 @@ fn main() {
1482 (("hash", "IN"), quote!(crate::hash::Dma)), 1482 (("hash", "IN"), quote!(crate::hash::Dma)),
1483 (("cryp", "IN"), quote!(crate::cryp::DmaIn)), 1483 (("cryp", "IN"), quote!(crate::cryp::DmaIn)),
1484 (("cryp", "OUT"), quote!(crate::cryp::DmaOut)), 1484 (("cryp", "OUT"), quote!(crate::cryp::DmaOut)),
1485 (("timer", "CH1"), quote!(crate::timer::Ch1Dma)), 1485 (("timer", "CH1"), quote!(crate::timer::Dma<Ch1>)),
1486 (("timer", "CH2"), quote!(crate::timer::Ch2Dma)), 1486 (("timer", "CH2"), quote!(crate::timer::Dma<Ch2>)),
1487 (("timer", "CH3"), quote!(crate::timer::Ch3Dma)), 1487 (("timer", "CH3"), quote!(crate::timer::Dma<Ch3>)),
1488 (("timer", "CH4"), quote!(crate::timer::Ch4Dma)), 1488 (("timer", "CH4"), quote!(crate::timer::Dma<Ch4>)),
1489 (("cordic", "WRITE"), quote!(crate::cordic::WriteDma)), // FIXME: stm32u5a crash on Cordic driver 1489 (("cordic", "WRITE"), quote!(crate::cordic::WriteDma)), // FIXME: stm32u5a crash on Cordic driver
1490 (("cordic", "READ"), quote!(crate::cordic::ReadDma)), // FIXME: stm32u5a crash on Cordic driver 1490 (("cordic", "READ"), quote!(crate::cordic::ReadDma)), // FIXME: stm32u5a crash on Cordic driver
1491 ] 1491 ]
@@ -1497,6 +1497,10 @@ fn main() {
1497 signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); 1497 signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma));
1498 } 1498 }
1499 1499
1500 if chip_name.starts_with("stm32wba") {
1501 signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma4));
1502 }
1503
1500 if chip_name.starts_with("stm32g4") { 1504 if chip_name.starts_with("stm32g4") {
1501 let line_number = chip_name.chars().skip(8).next().unwrap(); 1505 let line_number = chip_name.chars().skip(8).next().unwrap();
1502 if line_number == '3' || line_number == '4' { 1506 if line_number == '3' || line_number == '4' {
@@ -1554,9 +1558,35 @@ fn main() {
1554 quote!(()) 1558 quote!(())
1555 }; 1559 };
1556 1560
1561 let mut remap = quote!();
1562 for remap_info in ch.remap {
1563 let register = format_ident!("{}", remap_info.register.to_lowercase());
1564 let setter = format_ident!("set_{}", remap_info.field.to_lowercase());
1565
1566 let field_metadata = METADATA
1567 .peripherals
1568 .iter()
1569 .filter(|p| p.name == "SYSCFG")
1570 .flat_map(|p| p.registers.as_ref().unwrap().ir.fieldsets.iter())
1571 .filter(|f| f.name.eq_ignore_ascii_case(remap_info.register))
1572 .flat_map(|f| f.fields.iter())
1573 .find(|f| f.name.eq_ignore_ascii_case(remap_info.field))
1574 .unwrap();
1575
1576 let value = if field_metadata.bit_size == 1 {
1577 let bool_value = format_ident!("{}", remap_info.value > 0);
1578 quote!(#bool_value)
1579 } else {
1580 let value = remap_info.value;
1581 quote!(#value.into())
1582 };
1583
1584 remap.extend(quote!(crate::pac::SYSCFG.#register().modify(|w| w.#setter(#value));));
1585 }
1586
1557 let channel = format_ident!("{}", channel); 1587 let channel = format_ident!("{}", channel);
1558 g.extend(quote! { 1588 g.extend(quote! {
1559 dma_trait_impl!(#tr, #peri, #channel, #request); 1589 dma_trait_impl!(#tr, #peri, #channel, #request, {#remap});
1560 }); 1590 });
1561 } 1591 }
1562 } 1592 }
@@ -1888,9 +1918,9 @@ fn main() {
1888 } 1918 }
1889 1919
1890 g.extend(quote!( 1920 g.extend(quote!(
1891 pub fn gpio_block(n: usize) -> crate::pac::gpio::Gpio {{ 1921 pub fn gpio_block(n: usize) -> crate::pac::gpio::Gpio {
1892 unsafe {{ crate::pac::gpio::Gpio::from_ptr((#gpio_base + #gpio_stride*n) as _) }} 1922 unsafe { crate::pac::gpio::Gpio::from_ptr((#gpio_base + #gpio_stride*n) as _) }
1893 }} 1923 }
1894 )); 1924 ));
1895 1925
1896 // ======== 1926 // ========
diff --git a/embassy-stm32/src/adc/u5_adc4.rs b/embassy-stm32/src/adc/adc4.rs
index 1dd664366..31cbdc0d7 100644
--- a/embassy-stm32/src/adc/u5_adc4.rs
+++ b/embassy-stm32/src/adc/adc4.rs
@@ -1,10 +1,19 @@
1#[cfg(stm32u5)]
2use pac::adc::vals::{Adc4Dmacfg as Dmacfg, Adc4Exten as Exten, Adc4OversamplingRatio as OversamplingRatio};
1#[allow(unused)] 3#[allow(unused)]
2use pac::adc::vals::{Adc4Dmacfg, Adc4Exten, Adc4OversamplingRatio}; 4#[cfg(stm32wba)]
5use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel};
3 6
4use super::{blocking_delay_us, AdcChannel, AnyAdcChannel, RxDma4, SealedAdcChannel}; 7use super::{blocking_delay_us, AdcChannel, AnyAdcChannel, RxDma4, SealedAdcChannel};
5use crate::dma::Transfer; 8use crate::dma::Transfer;
6pub use crate::pac::adc::regs::Adc4Chselrmod0; 9#[cfg(stm32u5)]
10pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr;
11#[cfg(stm32wba)]
12pub use crate::pac::adc::regs::Chselr;
13#[cfg(stm32u5)]
7pub use crate::pac::adc::vals::{Adc4Presc as Presc, Adc4Res as Resolution, Adc4SampleTime as SampleTime}; 14pub use crate::pac::adc::vals::{Adc4Presc as Presc, Adc4Res as Resolution, Adc4SampleTime as SampleTime};
15#[cfg(stm32wba)]
16pub use crate::pac::adc::vals::{Presc, Res as Resolution, SampleTime};
8use crate::time::Hertz; 17use crate::time::Hertz;
9use crate::{pac, rcc, Peri}; 18use crate::{pac, rcc, Peri};
10 19
@@ -67,12 +76,14 @@ impl<T: Instance> SealedAdcChannel<T> for Vcore {
67 } 76 }
68} 77}
69 78
79#[derive(Copy, Clone)]
70pub enum DacChannel { 80pub enum DacChannel {
71 OUT1, 81 OUT1,
72 OUT2, 82 OUT2,
73} 83}
74 84
75/// Number of samples used for averaging. 85/// Number of samples used for averaging.
86#[derive(Copy, Clone)]
76pub enum Averaging { 87pub enum Averaging {
77 Disabled, 88 Disabled,
78 Samples2, 89 Samples2,
@@ -178,7 +189,7 @@ pub struct Adc4<'d, T: Instance> {
178 adc: crate::Peri<'d, T>, 189 adc: crate::Peri<'d, T>,
179} 190}
180 191
181#[derive(Debug)] 192#[derive(Copy, Clone, Debug)]
182pub enum Adc4Error { 193pub enum Adc4Error {
183 InvalidSequence, 194 InvalidSequence,
184 DMAError, 195 DMAError,
@@ -242,17 +253,28 @@ impl<'d, T: Instance> Adc4<'d, T> {
242 fn configure(&mut self) { 253 fn configure(&mut self) {
243 // single conversion mode, software trigger 254 // single conversion mode, software trigger
244 T::regs().cfgr1().modify(|w| { 255 T::regs().cfgr1().modify(|w| {
256 #[cfg(stm32u5)]
245 w.set_cont(false); 257 w.set_cont(false);
258 #[cfg(stm32wba)]
259 w.set_cont(Cont::SINGLE);
246 w.set_discen(false); 260 w.set_discen(false);
247 w.set_exten(Adc4Exten::DISABLED); 261 w.set_exten(Exten::DISABLED);
262 #[cfg(stm32u5)]
248 w.set_chselrmod(false); 263 w.set_chselrmod(false);
264 #[cfg(stm32wba)]
265 w.set_chselrmod(Chselrmod::ENABLE_INPUT);
249 }); 266 });
250 267
251 // only use one channel at the moment 268 // only use one channel at the moment
252 T::regs().smpr().modify(|w| { 269 T::regs().smpr().modify(|w| {
270 #[cfg(stm32u5)]
253 for i in 0..24 { 271 for i in 0..24 {
254 w.set_smpsel(i, false); 272 w.set_smpsel(i, false);
255 } 273 }
274 #[cfg(stm32wba)]
275 for i in 0..14 {
276 w.set_smpsel(i, Smpsel::SMP1);
277 }
256 }); 278 });
257 } 279 }
258 280
@@ -275,6 +297,7 @@ impl<'d, T: Instance> Adc4<'d, T> {
275 } 297 }
276 298
277 /// Enable reading the vbat internal channel. 299 /// Enable reading the vbat internal channel.
300 #[cfg(stm32u5)]
278 pub fn enable_vbat(&self) -> Vbat { 301 pub fn enable_vbat(&self) -> Vbat {
279 T::regs().ccr().modify(|w| { 302 T::regs().ccr().modify(|w| {
280 w.set_vbaten(true); 303 w.set_vbaten(true);
@@ -289,6 +312,7 @@ impl<'d, T: Instance> Adc4<'d, T> {
289 } 312 }
290 313
291 /// Enable reading the vbat internal channel. 314 /// Enable reading the vbat internal channel.
315 #[cfg(stm32u5)]
292 pub fn enable_dac_channel(&self, dac: DacChannel) -> Dac { 316 pub fn enable_dac_channel(&self, dac: DacChannel) -> Dac {
293 let mux; 317 let mux;
294 match dac { 318 match dac {
@@ -317,17 +341,38 @@ impl<'d, T: Instance> Adc4<'d, T> {
317 } 341 }
318 342
319 /// Set hardware averaging. 343 /// Set hardware averaging.
344 #[cfg(stm32u5)]
345 pub fn set_averaging(&mut self, averaging: Averaging) {
346 let (enable, samples, right_shift) = match averaging {
347 Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, 0),
348 Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, 1),
349 Averaging::Samples4 => (true, OversamplingRatio::OVERSAMPLE4X, 2),
350 Averaging::Samples8 => (true, OversamplingRatio::OVERSAMPLE8X, 3),
351 Averaging::Samples16 => (true, OversamplingRatio::OVERSAMPLE16X, 4),
352 Averaging::Samples32 => (true, OversamplingRatio::OVERSAMPLE32X, 5),
353 Averaging::Samples64 => (true, OversamplingRatio::OVERSAMPLE64X, 6),
354 Averaging::Samples128 => (true, OversamplingRatio::OVERSAMPLE128X, 7),
355 Averaging::Samples256 => (true, OversamplingRatio::OVERSAMPLE256X, 8),
356 };
357
358 T::regs().cfgr2().modify(|w| {
359 w.set_ovsr(samples);
360 w.set_ovss(right_shift);
361 w.set_ovse(enable)
362 })
363 }
364 #[cfg(stm32wba)]
320 pub fn set_averaging(&mut self, averaging: Averaging) { 365 pub fn set_averaging(&mut self, averaging: Averaging) {
321 let (enable, samples, right_shift) = match averaging { 366 let (enable, samples, right_shift) = match averaging {
322 Averaging::Disabled => (false, Adc4OversamplingRatio::OVERSAMPLE2X, 0), 367 Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT0),
323 Averaging::Samples2 => (true, Adc4OversamplingRatio::OVERSAMPLE2X, 1), 368 Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT1),
324 Averaging::Samples4 => (true, Adc4OversamplingRatio::OVERSAMPLE4X, 2), 369 Averaging::Samples4 => (true, OversamplingRatio::OVERSAMPLE4X, Ovss::SHIFT2),
325 Averaging::Samples8 => (true, Adc4OversamplingRatio::OVERSAMPLE8X, 3), 370 Averaging::Samples8 => (true, OversamplingRatio::OVERSAMPLE8X, Ovss::SHIFT3),
326 Averaging::Samples16 => (true, Adc4OversamplingRatio::OVERSAMPLE16X, 4), 371 Averaging::Samples16 => (true, OversamplingRatio::OVERSAMPLE16X, Ovss::SHIFT4),
327 Averaging::Samples32 => (true, Adc4OversamplingRatio::OVERSAMPLE32X, 5), 372 Averaging::Samples32 => (true, OversamplingRatio::OVERSAMPLE32X, Ovss::SHIFT5),
328 Averaging::Samples64 => (true, Adc4OversamplingRatio::OVERSAMPLE64X, 6), 373 Averaging::Samples64 => (true, OversamplingRatio::OVERSAMPLE64X, Ovss::SHIFT6),
329 Averaging::Samples128 => (true, Adc4OversamplingRatio::OVERSAMPLE128X, 7), 374 Averaging::Samples128 => (true, OversamplingRatio::OVERSAMPLE128X, Ovss::SHIFT7),
330 Averaging::Samples256 => (true, Adc4OversamplingRatio::OVERSAMPLE256X, 8), 375 Averaging::Samples256 => (true, OversamplingRatio::OVERSAMPLE256X, Ovss::SHIFT8),
331 }; 376 };
332 377
333 T::regs().cfgr2().modify(|w| { 378 T::regs().cfgr2().modify(|w| {
@@ -342,10 +387,20 @@ impl<'d, T: Instance> Adc4<'d, T> {
342 channel.setup(); 387 channel.setup();
343 388
344 // Select channel 389 // Select channel
345 T::regs().chselrmod0().write_value(Adc4Chselrmod0(0_u32)); 390 #[cfg(stm32wba)]
346 T::regs().chselrmod0().modify(|w| { 391 {
347 w.set_chsel(channel.channel() as usize, true); 392 T::regs().chselr().write_value(Chselr(0_u32));
348 }); 393 T::regs().chselr().modify(|w| {
394 w.set_chsel0(channel.channel() as usize, true);
395 });
396 }
397 #[cfg(stm32u5)]
398 {
399 T::regs().chselrmod0().write_value(Chselr(0_u32));
400 T::regs().chselrmod0().modify(|w| {
401 w.set_chsel(channel.channel() as usize, true);
402 });
403 }
349 404
350 // Reset interrupts 405 // Reset interrupts
351 T::regs().isr().modify(|reg| { 406 T::regs().isr().modify(|reg| {
@@ -415,13 +470,19 @@ impl<'d, T: Instance> Adc4<'d, T> {
415 470
416 T::regs().cfgr1().modify(|reg| { 471 T::regs().cfgr1().modify(|reg| {
417 reg.set_dmaen(true); 472 reg.set_dmaen(true);
418 reg.set_dmacfg(Adc4Dmacfg::ONE_SHOT); 473 reg.set_dmacfg(Dmacfg::ONE_SHOT);
474 #[cfg(stm32u5)]
419 reg.set_chselrmod(false); 475 reg.set_chselrmod(false);
476 #[cfg(stm32wba)]
477 reg.set_chselrmod(Chselrmod::ENABLE_INPUT)
420 }); 478 });
421 479
422 // Verify and activate sequence 480 // Verify and activate sequence
423 let mut prev_channel: i16 = -1; 481 let mut prev_channel: i16 = -1;
424 T::regs().chselrmod0().write_value(Adc4Chselrmod0(0_u32)); 482 #[cfg(stm32wba)]
483 T::regs().chselr().write_value(Chselr(0_u32));
484 #[cfg(stm32u5)]
485 T::regs().chselrmod0().write_value(Chselr(0_u32));
425 for channel in sequence { 486 for channel in sequence {
426 let channel_num = channel.channel; 487 let channel_num = channel.channel;
427 if channel_num as i16 <= prev_channel { 488 if channel_num as i16 <= prev_channel {
@@ -429,6 +490,11 @@ impl<'d, T: Instance> Adc4<'d, T> {
429 }; 490 };
430 prev_channel = channel_num as i16; 491 prev_channel = channel_num as i16;
431 492
493 #[cfg(stm32wba)]
494 T::regs().chselr().modify(|w| {
495 w.set_chsel0(channel.channel as usize, true);
496 });
497 #[cfg(stm32u5)]
432 T::regs().chselrmod0().modify(|w| { 498 T::regs().chselrmod0().modify(|w| {
433 w.set_chsel(channel.channel as usize, true); 499 w.set_chsel(channel.channel as usize, true);
434 }); 500 });
diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs
index 936ad7413..f5870801e 100644
--- a/embassy-stm32/src/adc/c0.rs
+++ b/embassy-stm32/src/adc/c0.rs
@@ -48,7 +48,7 @@ impl<T: Instance> SealedAdcChannel<T> for Temperature {
48 } 48 }
49} 49}
50 50
51#[derive(Debug)] 51#[derive(Copy, Clone, Debug)]
52pub enum Prescaler { 52pub enum Prescaler {
53 NotDivided, 53 NotDivided,
54 DividedBy2, 54 DividedBy2,
@@ -138,6 +138,7 @@ impl<'a> defmt::Format for Prescaler {
138/// Number of samples used for averaging. 138/// Number of samples used for averaging.
139/// TODO: Implement hardware averaging setting. 139/// TODO: Implement hardware averaging setting.
140#[allow(unused)] 140#[allow(unused)]
141#[derive(Copy, Clone)]
141pub enum Averaging { 142pub enum Averaging {
142 Disabled, 143 Disabled,
143 Samples2, 144 Samples2,
diff --git a/embassy-stm32/src/adc/f3_v1_1.rs b/embassy-stm32/src/adc/f3_v1_1.rs
index 944e971bb..84613078c 100644
--- a/embassy-stm32/src/adc/f3_v1_1.rs
+++ b/embassy-stm32/src/adc/f3_v1_1.rs
@@ -17,6 +17,7 @@ pub const VDDA_CALIB_MV: u32 = 3300;
17pub const ADC_MAX: u32 = (1 << 12) - 1; 17pub const ADC_MAX: u32 = (1 << 12) - 1;
18pub const VREF_INT: u32 = 1230; 18pub const VREF_INT: u32 = 1230;
19 19
20#[derive(Copy, Clone)]
20pub enum AdcPowerMode { 21pub enum AdcPowerMode {
21 AlwaysOn, 22 AlwaysOn,
22 DelayOff, 23 DelayOff,
@@ -24,6 +25,7 @@ pub enum AdcPowerMode {
24 DelayIdleOff, 25 DelayIdleOff,
25} 26}
26 27
28#[derive(Copy, Clone)]
27pub enum Prescaler { 29pub enum Prescaler {
28 Div1, 30 Div1,
29 Div2, 31 Div2,
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs
index 1fce3085a..43498966f 100644
--- a/embassy-stm32/src/adc/g4.rs
+++ b/embassy-stm32/src/adc/g4.rs
@@ -24,44 +24,31 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60);
24#[cfg(stm32h7)] 24#[cfg(stm32h7)]
25const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); 25const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50);
26 26
27#[cfg(stm32g4)]
28const VREF_CHANNEL: u8 = 18;
29#[cfg(stm32g4)]
30const TEMP_CHANNEL: u8 = 16;
31
32#[cfg(stm32h7)]
33const VREF_CHANNEL: u8 = 19;
34#[cfg(stm32h7)]
35const TEMP_CHANNEL: u8 = 18;
36
37// TODO this should be 14 for H7a/b/35
38const VBAT_CHANNEL: u8 = 17;
39
40// 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 27// NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs
41/// Internal voltage reference channel. 28/// Internal voltage reference channel.
42pub struct VrefInt; 29pub struct VrefInt;
43impl<T: Instance> AdcChannel<T> for VrefInt {} 30impl<T: Instance + VrefChannel> AdcChannel<T> for VrefInt {}
44impl<T: Instance> super::SealedAdcChannel<T> for VrefInt { 31impl<T: Instance + VrefChannel> super::SealedAdcChannel<T> for VrefInt {
45 fn channel(&self) -> u8 { 32 fn channel(&self) -> u8 {
46 VREF_CHANNEL 33 T::CHANNEL
47 } 34 }
48} 35}
49 36
50/// Internal temperature channel. 37/// Internal temperature channel.
51pub struct Temperature; 38pub struct Temperature;
52impl<T: Instance> AdcChannel<T> for Temperature {} 39impl<T: Instance + TemperatureChannel> AdcChannel<T> for Temperature {}
53impl<T: Instance> super::SealedAdcChannel<T> for Temperature { 40impl<T: Instance + TemperatureChannel> super::SealedAdcChannel<T> for Temperature {
54 fn channel(&self) -> u8 { 41 fn channel(&self) -> u8 {
55 TEMP_CHANNEL 42 T::CHANNEL
56 } 43 }
57} 44}
58 45
59/// Internal battery voltage channel. 46/// Internal battery voltage channel.
60pub struct Vbat; 47pub struct Vbat;
61impl<T: Instance> AdcChannel<T> for Vbat {} 48impl<T: Instance + VBatChannel> AdcChannel<T> for Vbat {}
62impl<T: Instance> super::SealedAdcChannel<T> for Vbat { 49impl<T: Instance + VBatChannel> super::SealedAdcChannel<T> for Vbat {
63 fn channel(&self) -> u8 { 50 fn channel(&self) -> u8 {
64 VBAT_CHANNEL 51 T::CHANNEL
65 } 52 }
66} 53}
67 54
@@ -234,7 +221,10 @@ impl<'d, T: Instance> Adc<'d, T> {
234 } 221 }
235 222
236 /// Enable reading the voltage reference internal channel. 223 /// Enable reading the voltage reference internal channel.
237 pub fn enable_vrefint(&self) -> VrefInt { 224 pub fn enable_vrefint(&self) -> VrefInt
225 where
226 T: VrefChannel,
227 {
238 T::common_regs().ccr().modify(|reg| { 228 T::common_regs().ccr().modify(|reg| {
239 reg.set_vrefen(true); 229 reg.set_vrefen(true);
240 }); 230 });
@@ -243,7 +233,10 @@ impl<'d, T: Instance> Adc<'d, T> {
243 } 233 }
244 234
245 /// Enable reading the temperature internal channel. 235 /// Enable reading the temperature internal channel.
246 pub fn enable_temperature(&self) -> Temperature { 236 pub fn enable_temperature(&self) -> Temperature
237 where
238 T: TemperatureChannel,
239 {
247 T::common_regs().ccr().modify(|reg| { 240 T::common_regs().ccr().modify(|reg| {
248 reg.set_vsenseen(true); 241 reg.set_vsenseen(true);
249 }); 242 });
@@ -252,7 +245,10 @@ impl<'d, T: Instance> Adc<'d, T> {
252 } 245 }
253 246
254 /// Enable reading the vbat internal channel. 247 /// Enable reading the vbat internal channel.
255 pub fn enable_vbat(&self) -> Vbat { 248 pub fn enable_vbat(&self) -> Vbat
249 where
250 T: VBatChannel,
251 {
256 T::common_regs().ccr().modify(|reg| { 252 T::common_regs().ccr().modify(|reg| {
257 reg.set_vbaten(true); 253 reg.set_vbaten(true);
258 }); 254 });
@@ -519,3 +515,78 @@ impl<'d, T: Instance> Adc<'d, T> {
519 } 515 }
520 } 516 }
521} 517}
518
519/// Implemented for ADCs that have a Temperature channel
520pub trait TemperatureChannel {
521 const CHANNEL: u8;
522}
523/// Implemented for ADCs that have a Vref channel
524pub trait VrefChannel {
525 const CHANNEL: u8;
526}
527/// Implemented for ADCs that have a VBat channel
528pub trait VBatChannel {
529 const CHANNEL: u8;
530}
531
532#[cfg(stm32g4)]
533mod g4 {
534 pub use super::*;
535
536 impl TemperatureChannel for crate::peripherals::ADC1 {
537 const CHANNEL: u8 = 16;
538 }
539
540 impl VrefChannel for crate::peripherals::ADC1 {
541 const CHANNEL: u8 = 18;
542 }
543
544 impl VBatChannel for crate::peripherals::ADC1 {
545 const CHANNEL: u8 = 17;
546 }
547
548 #[cfg(peri_adc3_common)]
549 impl VrefChannel for crate::peripherals::ADC3 {
550 const CHANNEL: u8 = 18;
551 }
552
553 #[cfg(peri_adc3_common)]
554 impl VBatChannel for crate::peripherals::ADC3 {
555 const CHANNEL: u8 = 17;
556 }
557
558 #[cfg(not(stm32g4x1))]
559 impl VrefChannel for crate::peripherals::ADC4 {
560 const CHANNEL: u8 = 18;
561 }
562
563 #[cfg(not(stm32g4x1))]
564 impl TemperatureChannel for crate::peripherals::ADC5 {
565 const CHANNEL: u8 = 4;
566 }
567
568 #[cfg(not(stm32g4x1))]
569 impl VrefChannel for crate::peripherals::ADC5 {
570 const CHANNEL: u8 = 18;
571 }
572
573 #[cfg(not(stm32g4x1))]
574 impl VBatChannel for crate::peripherals::ADC5 {
575 const CHANNEL: u8 = 17;
576 }
577}
578
579// TODO this should look at each ADC individually and impl the correct channels
580#[cfg(stm32h7)]
581mod h7 {
582 impl<T: Instance> TemperatureChannel for T {
583 const CHANNEL: u8 = 18;
584 }
585 impl<T: Instance> VrefChannel for T {
586 const CHANNEL: u8 = 19;
587 }
588 impl<T: Instance> VBatChannel for T {
589 // TODO this should be 14 for H7a/b/35
590 const CHANNEL: u8 = 17;
591 }
592}
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs
index f46e87f38..778edc6f6 100644
--- a/embassy-stm32/src/adc/mod.rs
+++ b/embassy-stm32/src/adc/mod.rs
@@ -4,7 +4,7 @@
4#![allow(missing_docs)] // TODO 4#![allow(missing_docs)] // TODO
5#![cfg_attr(adc_f3_v2, allow(unused))] 5#![cfg_attr(adc_f3_v2, allow(unused))]
6 6
7#[cfg(not(any(adc_f3_v2)))] 7#[cfg(not(any(adc_f3_v2, adc_wba)))]
8#[cfg_attr(adc_f1, path = "f1.rs")] 8#[cfg_attr(adc_f1, path = "f1.rs")]
9#[cfg_attr(adc_f3, path = "f3.rs")] 9#[cfg_attr(adc_f3, path = "f3.rs")]
10#[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")] 10#[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")]
@@ -20,14 +20,14 @@ mod _version;
20use core::marker::PhantomData; 20use core::marker::PhantomData;
21 21
22#[allow(unused)] 22#[allow(unused)]
23#[cfg(not(any(adc_f3_v2)))] 23#[cfg(not(any(adc_f3_v2, adc_wba)))]
24pub use _version::*; 24pub use _version::*;
25use embassy_hal_internal::{impl_peripheral, PeripheralType}; 25use embassy_hal_internal::{impl_peripheral, PeripheralType};
26#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] 26#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
27use embassy_sync::waitqueue::AtomicWaker; 27use embassy_sync::waitqueue::AtomicWaker;
28 28
29#[cfg(adc_u5)] 29#[cfg(any(adc_u5, adc_wba))]
30#[path = "u5_adc4.rs"] 30#[path = "adc4.rs"]
31pub mod adc4; 31pub mod adc4;
32 32
33pub use crate::pac::adc::vals; 33pub use crate::pac::adc::vals;
@@ -36,15 +36,18 @@ pub use crate::pac::adc::vals::Res as Resolution;
36pub use crate::pac::adc::vals::SampleTime; 36pub use crate::pac::adc::vals::SampleTime;
37use crate::peripherals; 37use crate::peripherals;
38 38
39#[cfg(not(adc_wba))]
39dma_trait!(RxDma, Instance); 40dma_trait!(RxDma, Instance);
40#[cfg(adc_u5)] 41#[cfg(adc_u5)]
41dma_trait!(RxDma4, adc4::Instance); 42dma_trait!(RxDma4, adc4::Instance);
43#[cfg(adc_wba)]
44dma_trait!(RxDma4, adc4::Instance);
42 45
43/// Analog to Digital driver. 46/// Analog to Digital driver.
44pub struct Adc<'d, T: Instance> { 47pub struct Adc<'d, T: Instance> {
45 #[allow(unused)] 48 #[allow(unused)]
46 adc: crate::Peri<'d, T>, 49 adc: crate::Peri<'d, T>,
47 #[cfg(not(any(adc_f3_v2, adc_f3_v1_1)))] 50 #[cfg(not(any(adc_f3_v2, adc_f3_v1_1, adc_wba)))]
48 sample_time: SampleTime, 51 sample_time: SampleTime,
49} 52}
50 53
@@ -63,6 +66,7 @@ impl State {
63} 66}
64 67
65trait SealedInstance { 68trait SealedInstance {
69 #[cfg(not(adc_wba))]
66 #[allow(unused)] 70 #[allow(unused)]
67 fn regs() -> crate::pac::adc::Adc; 71 fn regs() -> crate::pac::adc::Adc;
68 #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))] 72 #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
@@ -73,7 +77,7 @@ trait SealedInstance {
73} 77}
74 78
75pub(crate) trait SealedAdcChannel<T> { 79pub(crate) trait SealedAdcChannel<T> {
76 #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] 80 #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))]
77 fn setup(&mut self) {} 81 fn setup(&mut self) {}
78 82
79 #[allow(unused)] 83 #[allow(unused)]
@@ -110,7 +114,8 @@ pub(crate) fn blocking_delay_us(us: u32) {
110 adc_h5, 114 adc_h5,
111 adc_h7rs, 115 adc_h7rs,
112 adc_u5, 116 adc_u5,
113 adc_c0 117 adc_c0,
118 adc_wba,
114)))] 119)))]
115#[allow(private_bounds)] 120#[allow(private_bounds)]
116pub trait Instance: SealedInstance + crate::PeripheralType { 121pub trait Instance: SealedInstance + crate::PeripheralType {
@@ -132,7 +137,8 @@ pub trait Instance: SealedInstance + crate::PeripheralType {
132 adc_h5, 137 adc_h5,
133 adc_h7rs, 138 adc_h7rs,
134 adc_u5, 139 adc_u5,
135 adc_c0 140 adc_c0,
141 adc_wba,
136))] 142))]
137#[allow(private_bounds)] 143#[allow(private_bounds)]
138pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral { 144pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral {
@@ -144,7 +150,7 @@ pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeri
144pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized { 150pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized {
145 #[allow(unused_mut)] 151 #[allow(unused_mut)]
146 fn degrade_adc(mut self) -> AnyAdcChannel<T> { 152 fn degrade_adc(mut self) -> AnyAdcChannel<T> {
147 #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] 153 #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))]
148 self.setup(); 154 self.setup();
149 155
150 AnyAdcChannel { 156 AnyAdcChannel {
@@ -176,6 +182,36 @@ impl<T> AnyAdcChannel<T> {
176 self.channel 182 self.channel
177 } 183 }
178} 184}
185#[cfg(adc_wba)]
186foreach_adc!(
187 (ADC4, $common_inst:ident, $clock:ident) => {
188 impl crate::adc::adc4::SealedInstance for peripherals::ADC4 {
189 fn regs() -> crate::pac::adc::Adc4 {
190 crate::pac::ADC4
191 }
192 }
193
194 impl crate::adc::adc4::Instance for peripherals::ADC4 {
195 type Interrupt = crate::_generated::peripheral_interrupts::ADC4::GLOBAL;
196 }
197 };
198
199 ($inst:ident, $common_inst:ident, $clock:ident) => {
200 impl crate::adc::SealedInstance for peripherals::$inst {
201 fn regs() -> crate::pac::adc::Adc {
202 crate::pac::$inst
203 }
204
205 fn common_regs() -> crate::pac::adccommon::AdcCommon {
206 return crate::pac::$common_inst
207 }
208 }
209
210 impl crate::adc::Instance for peripherals::$inst {
211 type Interrupt = crate::_generated::peripheral_interrupts::$inst::GLOBAL;
212 }
213 };
214);
179 215
180#[cfg(adc_u5)] 216#[cfg(adc_u5)]
181foreach_adc!( 217foreach_adc!(
@@ -208,15 +244,21 @@ foreach_adc!(
208 }; 244 };
209); 245);
210 246
211#[cfg(not(adc_u5))] 247#[cfg(not(any(adc_u5, adc_wba)))]
212foreach_adc!( 248foreach_adc!(
213 ($inst:ident, $common_inst:ident, $clock:ident) => { 249 ($inst:ident, $common_inst:ident, $clock:ident) => {
214 impl crate::adc::SealedInstance for peripherals::$inst { 250 impl crate::adc::SealedInstance for peripherals::$inst {
251 #[cfg(not(adc_wba))]
215 fn regs() -> crate::pac::adc::Adc { 252 fn regs() -> crate::pac::adc::Adc {
216 crate::pac::$inst 253 crate::pac::$inst
217 } 254 }
218 255
219 #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))] 256 #[cfg(adc_wba)]
257 fn regs() -> crate::pac::adc::Adc4 {
258 crate::pac::$inst
259 }
260
261 #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0, adc_u5, adc_wba)))]
220 fn common_regs() -> crate::pac::adccommon::AdcCommon { 262 fn common_regs() -> crate::pac::adccommon::AdcCommon {
221 return crate::pac::$common_inst 263 return crate::pac::$common_inst
222 } 264 }
@@ -238,7 +280,7 @@ macro_rules! impl_adc_pin {
238 ($inst:ident, $pin:ident, $ch:expr) => { 280 ($inst:ident, $pin:ident, $ch:expr) => {
239 impl crate::adc::AdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> {} 281 impl crate::adc::AdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> {}
240 impl crate::adc::SealedAdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> { 282 impl crate::adc::SealedAdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> {
241 #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] 283 #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))]
242 fn setup(&mut self) { 284 fn setup(&mut self) {
243 <crate::peripherals::$pin as crate::gpio::SealedPin>::set_as_analog(self); 285 <crate::peripherals::$pin as crate::gpio::SealedPin>::set_as_analog(self);
244 } 286 }
diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs
index fb6f5b7d0..7fe502da0 100644
--- a/embassy-stm32/src/adc/v1.rs
+++ b/embassy-stm32/src/adc/v1.rs
@@ -11,6 +11,9 @@ use crate::interrupt::typelevel::Interrupt;
11use crate::peripherals::ADC1; 11use crate::peripherals::ADC1;
12use crate::{interrupt, rcc, Peri}; 12use crate::{interrupt, rcc, Peri};
13 13
14mod watchdog_v1;
15pub use watchdog_v1::WatchdogChannels;
16
14pub const VDDA_CALIB_MV: u32 = 3300; 17pub const VDDA_CALIB_MV: u32 = 3300;
15pub const VREF_INT: u32 = 1230; 18pub const VREF_INT: u32 = 1230;
16 19
@@ -21,8 +24,15 @@ pub struct InterruptHandler<T: Instance> {
21 24
22impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { 25impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
23 unsafe fn on_interrupt() { 26 unsafe fn on_interrupt() {
24 if T::regs().isr().read().eoc() { 27 let isr = T::regs().isr().read();
28 let ier = T::regs().ier().read();
29 if ier.eocie() && isr.eoc() {
30 // eocie is set during adc.read()
25 T::regs().ier().modify(|w| w.set_eocie(false)); 31 T::regs().ier().modify(|w| w.set_eocie(false));
32 } else if ier.awdie() && isr.awd() {
33 // awdie is set during adc.monitor_watchdog()
34 T::regs().cr().read().set_adstp(true);
35 T::regs().ier().modify(|w| w.set_awdie(false));
26 } else { 36 } else {
27 return; 37 return;
28 } 38 }
@@ -186,16 +196,21 @@ impl<'d, T: Instance> Adc<'d, T> {
186 196
187 T::regs().dr().read().data() 197 T::regs().dr().read().data()
188 } 198 }
189}
190 199
191impl<'d, T: Instance> Drop for Adc<'d, T> { 200 fn teardown_adc() {
192 fn drop(&mut self) {
193 // A.7.3 ADC disable code example 201 // A.7.3 ADC disable code example
194 T::regs().cr().modify(|reg| reg.set_adstp(true)); 202 T::regs().cr().modify(|reg| reg.set_adstp(true));
195 while T::regs().cr().read().adstp() {} 203 while T::regs().cr().read().adstp() {}
196 204
197 T::regs().cr().modify(|reg| reg.set_addis(true)); 205 T::regs().cr().modify(|reg| reg.set_addis(true));
198 while T::regs().cr().read().aden() {} 206 while T::regs().cr().read().aden() {}
207 }
208}
209
210impl<'d, T: Instance> Drop for Adc<'d, T> {
211 fn drop(&mut self) {
212 Self::teardown_adc();
213 Self::teardown_awd();
199 214
200 rcc::disable::<T>(); 215 rcc::disable::<T>();
201 } 216 }
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs
index 7b5df80b8..805dae564 100644
--- a/embassy-stm32/src/adc/v3.rs
+++ b/embassy-stm32/src/adc/v3.rs
@@ -1,5 +1,7 @@
1use cfg_if::cfg_if; 1use cfg_if::cfg_if;
2use pac::adc::vals::Dmacfg; 2use pac::adc::vals::Dmacfg;
3#[cfg(adc_v3)]
4use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs};
3 5
4use super::{ 6use super::{
5 blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, 7 blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel,
@@ -506,6 +508,23 @@ impl<'d, T: Instance> Adc<'d, T> {
506 T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); 508 T::regs().cfgr2().modify(|reg| reg.set_ovse(enable));
507 } 509 }
508 510
511 #[cfg(adc_v3)]
512 pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) {
513 T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode));
514 T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode));
515 T::regs().cfgr2().modify(|reg| reg.set_rovse(enable));
516 }
517
518 #[cfg(adc_v3)]
519 pub fn set_oversampling_ratio(&mut self, ratio: OversamplingRatio) {
520 T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio));
521 }
522
523 #[cfg(adc_v3)]
524 pub fn set_oversampling_shift(&mut self, shift: OversamplingShift) {
525 T::regs().cfgr2().modify(|reg| reg.set_ovss(shift));
526 }
527
509 fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { 528 fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) {
510 cfg_if! { 529 cfg_if! {
511 if #[cfg(any(adc_g0, adc_u0))] { 530 if #[cfg(any(adc_g0, adc_u0))] {
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs
index 39e0d51b9..b0871019a 100644
--- a/embassy-stm32/src/adc/v4.rs
+++ b/embassy-stm32/src/adc/v4.rs
@@ -142,6 +142,7 @@ impl Prescaler {
142} 142}
143 143
144/// Number of samples used for averaging. 144/// Number of samples used for averaging.
145#[derive(Copy, Clone)]
145pub enum Averaging { 146pub enum Averaging {
146 Disabled, 147 Disabled,
147 Samples2, 148 Samples2,
diff --git a/embassy-stm32/src/adc/watchdog_v1.rs b/embassy-stm32/src/adc/watchdog_v1.rs
new file mode 100644
index 000000000..bbe8e1971
--- /dev/null
+++ b/embassy-stm32/src/adc/watchdog_v1.rs
@@ -0,0 +1,188 @@
1use core::future::poll_fn;
2use core::task::Poll;
3
4use stm32_metapac::adc::vals::{Align, Awdsgl, Res};
5
6use crate::adc::{Adc, AdcChannel, Instance};
7
8/// This enum is passed into `Adc::init_watchdog` to specify the channels for the watchdog to monitor
9pub enum WatchdogChannels {
10 // Single channel identified by index
11 Single(u8),
12 // Multiple channels identified by mask
13 Multiple(u16),
14}
15
16impl WatchdogChannels {
17 pub fn from_channel<T>(channel: &impl AdcChannel<T>) -> Self {
18 Self::Single(channel.channel())
19 }
20
21 pub fn add_channel<T>(self, channel: &impl AdcChannel<T>) -> Self {
22 WatchdogChannels::Multiple(
23 (match self {
24 WatchdogChannels::Single(ch) => 1 << ch,
25 WatchdogChannels::Multiple(ch) => ch,
26 }) | 1 << channel.channel(),
27 )
28 }
29}
30
31impl<'d, T: Instance> Adc<'d, T> {
32 /// Configure the analog window watchdog to monitor one or more ADC channels
33 ///
34 /// `high_threshold` and `low_threshold` are expressed in the same way as ADC results. The format
35 /// depends on the values of CFGR1.ALIGN and CFGR1.RES.
36 pub fn init_watchdog(&mut self, channels: WatchdogChannels, low_threshold: u16, high_threshold: u16) {
37 Self::stop_awd();
38
39 match channels {
40 WatchdogChannels::Single(ch) => {
41 T::regs().chselr().modify(|w| {
42 w.set_chsel_x(ch.into(), true);
43 });
44 T::regs().cfgr1().modify(|w| {
45 w.set_awdch(ch);
46 w.set_awdsgl(Awdsgl::SINGLE_CHANNEL)
47 });
48 }
49 WatchdogChannels::Multiple(ch) => {
50 T::regs().chselr().modify(|w| w.0 = ch.into());
51 T::regs().cfgr1().modify(|w| {
52 w.set_awdch(0);
53 w.set_awdsgl(Awdsgl::ALL_CHANNELS)
54 });
55 }
56 }
57
58 Self::set_watchdog_thresholds(low_threshold, high_threshold);
59 Self::setup_awd();
60 }
61
62 /// Monitor the voltage on the selected channels; return when it crosses the thresholds.
63 ///
64 /// ```rust,ignore
65 /// // Wait for pin to go high
66 /// adc.init_watchdog(WatchdogChannels::from_channel(&pin), 0, 0x07F);
67 /// let v_high = adc.monitor_watchdog().await;
68 /// info!("ADC sample is high {}", v_high);
69 /// ```
70 pub async fn monitor_watchdog(&mut self) -> u16 {
71 assert!(
72 match T::regs().cfgr1().read().awdsgl() {
73 Awdsgl::SINGLE_CHANNEL => T::regs().cfgr1().read().awdch() != 0,
74 Awdsgl::ALL_CHANNELS => T::regs().cfgr1().read().awdch() == 0,
75 },
76 "`set_channel` should be called before `monitor`",
77 );
78 assert!(T::regs().chselr().read().0 != 0);
79 T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.into()));
80 Self::start_awd();
81
82 let sample = poll_fn(|cx| {
83 T::state().waker.register(cx.waker());
84
85 if T::regs().isr().read().awd() {
86 Poll::Ready(T::regs().dr().read().data())
87 } else {
88 Poll::Pending
89 }
90 })
91 .await;
92
93 self.stop_watchdog();
94 sample
95 }
96
97 /// Stop monitoring the selected channels
98 pub fn stop_watchdog(&mut self) {
99 Self::stop_awd();
100 }
101
102 fn set_watchdog_thresholds(low_threshold: u16, high_threshold: u16) {
103 // This function takes `high_threshold` and `low_threshold` in the same alignment and resolution
104 // as ADC results, and programs them into ADC_DR. Because ADC_DR is always right-aligned on 12 bits,
105 // some bit-shifting may be necessary. See more in table 47 §13.7.1 Analog Watchdog Comparison
106
107 // Verify that the thresholds are in the correct bit positions according to alignment and resolution
108 let threshold_mask = match (T::regs().cfgr1().read().align(), T::regs().cfgr1().read().res()) {
109 (Align::LEFT, Res::BITS6) => 0x00FC,
110 (Align::LEFT, Res::BITS8) => 0xFF00,
111 (Align::LEFT, Res::BITS10) => 0xFFC0,
112 (Align::LEFT, Res::BITS12) => 0xFFF0,
113 (Align::RIGHT, Res::BITS6) => 0x003F,
114 (Align::RIGHT, Res::BITS8) => 0x00FF,
115 (Align::RIGHT, Res::BITS10) => 0x03FF,
116 (Align::RIGHT, Res::BITS12) => 0x0FFF,
117 };
118 assert!(
119 high_threshold & !threshold_mask == 0,
120 "High threshold {:x} is invalid — only bits {:x} are allowed",
121 high_threshold,
122 threshold_mask
123 );
124 assert!(
125 low_threshold & !threshold_mask == 0,
126 "Low threshold {:x} is invalid — only bits {:x} are allowed",
127 low_threshold,
128 threshold_mask
129 );
130
131 T::regs().tr().modify(|w| {
132 w.set_lt(low_threshold << threshold_mask.leading_zeros() >> 4);
133 w.set_ht(high_threshold << threshold_mask.leading_zeros() >> 4);
134 })
135 }
136
137 fn setup_awd() {
138 // Configure AWD
139 assert!(!T::regs().cr().read().adstart());
140 T::regs().cfgr1().modify(|w| w.set_awden(true));
141 }
142
143 fn start_awd() {
144 // Clear AWD interrupt flag
145 while T::regs().isr().read().awd() {
146 T::regs().isr().modify(|regs| {
147 regs.set_awd(true);
148 })
149 }
150
151 // Enable AWD interrupt
152 assert!(!T::regs().cr().read().adstart());
153 T::regs().ier().modify(|w| {
154 w.set_eocie(false);
155 w.set_awdie(true)
156 });
157
158 // Start conversion
159 T::regs().cfgr1().modify(|w| w.set_cont(true));
160 T::regs().cr().modify(|w| w.set_adstart(true));
161 }
162
163 fn stop_awd() {
164 // Stop conversion
165 while T::regs().cr().read().addis() {}
166 if T::regs().cr().read().adstart() {
167 T::regs().cr().write(|x| x.set_adstp(true));
168 while T::regs().cr().read().adstp() {}
169 }
170 T::regs().cfgr1().modify(|w| w.set_cont(false));
171
172 // Disable AWD interrupt
173 assert!(!T::regs().cr().read().adstart());
174 T::regs().ier().modify(|w| w.set_awdie(false));
175
176 // Clear AWD interrupt flag
177 while T::regs().isr().read().awd() {
178 T::regs().isr().modify(|regs| {
179 regs.set_awd(true);
180 })
181 }
182 }
183
184 pub(crate) fn teardown_awd() {
185 Self::stop_awd();
186 T::regs().cfgr1().modify(|w| w.set_awden(false));
187 }
188}
diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs
index 305666d5b..4c0795a2a 100644
--- a/embassy-stm32/src/can/bxcan/mod.rs
+++ b/embassy-stm32/src/can/bxcan/mod.rs
@@ -15,9 +15,10 @@ pub use embedded_can::{ExtendedId, Id, StandardId};
15use self::filter::MasterFilters; 15use self::filter::MasterFilters;
16use self::registers::{Registers, RxFifo}; 16use self::registers::{Registers, RxFifo};
17pub use super::common::{BufferedCanReceiver, BufferedCanSender}; 17pub use super::common::{BufferedCanReceiver, BufferedCanSender};
18use super::common::{InfoRef, RxInfoRef, TxInfoRef};
18use super::frame::{Envelope, Frame}; 19use super::frame::{Envelope, Frame};
19use super::util; 20use super::util;
20use crate::can::enums::{BusError, InternalOperation, TryReadError}; 21use crate::can::enums::{BusError, RefCountOp, TryReadError};
21use crate::gpio::{AfType, OutputType, Pull, Speed}; 22use crate::gpio::{AfType, OutputType, Pull, Speed};
22use crate::interrupt::typelevel::Interrupt; 23use crate::interrupt::typelevel::Interrupt;
23use crate::rcc::{self, RccPeripheral}; 24use crate::rcc::{self, RccPeripheral};
@@ -35,7 +36,9 @@ impl<T: Instance> interrupt::typelevel::Handler<T::TXInterrupt> for TxInterruptH
35 v.set_rqcp(1, true); 36 v.set_rqcp(1, true);
36 v.set_rqcp(2, true); 37 v.set_rqcp(2, true);
37 }); 38 });
38 T::state().tx_mode.on_interrupt::<T>(); 39 T::info().state.lock(|state| {
40 state.borrow().tx_mode.on_interrupt::<T>();
41 });
39 } 42 }
40} 43}
41 44
@@ -46,7 +49,9 @@ pub struct Rx0InterruptHandler<T: Instance> {
46 49
47impl<T: Instance> interrupt::typelevel::Handler<T::RX0Interrupt> for Rx0InterruptHandler<T> { 50impl<T: Instance> interrupt::typelevel::Handler<T::RX0Interrupt> for Rx0InterruptHandler<T> {
48 unsafe fn on_interrupt() { 51 unsafe fn on_interrupt() {
49 T::state().rx_mode.on_interrupt::<T>(RxFifo::Fifo0); 52 T::info().state.lock(|state| {
53 state.borrow().rx_mode.on_interrupt::<T>(RxFifo::Fifo0);
54 });
50 } 55 }
51} 56}
52 57
@@ -57,7 +62,9 @@ pub struct Rx1InterruptHandler<T: Instance> {
57 62
58impl<T: Instance> interrupt::typelevel::Handler<T::RX1Interrupt> for Rx1InterruptHandler<T> { 63impl<T: Instance> interrupt::typelevel::Handler<T::RX1Interrupt> for Rx1InterruptHandler<T> {
59 unsafe fn on_interrupt() { 64 unsafe fn on_interrupt() {
60 T::state().rx_mode.on_interrupt::<T>(RxFifo::Fifo1); 65 T::info().state.lock(|state| {
66 state.borrow().rx_mode.on_interrupt::<T>(RxFifo::Fifo1);
67 });
61 } 68 }
62} 69}
63 70
@@ -73,7 +80,9 @@ impl<T: Instance> interrupt::typelevel::Handler<T::SCEInterrupt> for SceInterrup
73 80
74 if msr_val.slaki() { 81 if msr_val.slaki() {
75 msr.modify(|m| m.set_slaki(true)); 82 msr.modify(|m| m.set_slaki(true));
76 T::state().err_waker.wake(); 83 T::info().state.lock(|state| {
84 state.borrow().err_waker.wake();
85 });
77 } else if msr_val.erri() { 86 } else if msr_val.erri() {
78 // Disable the interrupt, but don't acknowledge the error, so that it can be 87 // Disable the interrupt, but don't acknowledge the error, so that it can be
79 // forwarded off the bus message consumer. If we don't provide some way for 88 // forwarded off the bus message consumer. If we don't provide some way for
@@ -82,8 +91,9 @@ impl<T: Instance> interrupt::typelevel::Handler<T::SCEInterrupt> for SceInterrup
82 // an indefinite amount of time. 91 // an indefinite amount of time.
83 let ier = T::regs().ier(); 92 let ier = T::regs().ier();
84 ier.modify(|i| i.set_errie(false)); 93 ier.modify(|i| i.set_errie(false));
85 94 T::info().state.lock(|state| {
86 T::state().err_waker.wake(); 95 state.borrow().err_waker.wake();
96 });
87 } 97 }
88 } 98 }
89} 99}
@@ -91,7 +101,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::SCEInterrupt> for SceInterrup
91/// Configuration proxy returned by [`Can::modify_config`]. 101/// Configuration proxy returned by [`Can::modify_config`].
92pub struct CanConfig<'a> { 102pub struct CanConfig<'a> {
93 phantom: PhantomData<&'a ()>, 103 phantom: PhantomData<&'a ()>,
94 info: &'static Info, 104 info: InfoRef,
95 periph_clock: crate::time::Hertz, 105 periph_clock: crate::time::Hertz,
96} 106}
97 107
@@ -156,8 +166,7 @@ impl Drop for CanConfig<'_> {
156/// CAN driver 166/// CAN driver
157pub struct Can<'d> { 167pub struct Can<'d> {
158 phantom: PhantomData<&'d ()>, 168 phantom: PhantomData<&'d ()>,
159 info: &'static Info, 169 info: InfoRef,
160 state: &'static State,
161 periph_clock: crate::time::Hertz, 170 periph_clock: crate::time::Hertz,
162} 171}
163 172
@@ -227,8 +236,7 @@ impl<'d> Can<'d> {
227 236
228 Self { 237 Self {
229 phantom: PhantomData, 238 phantom: PhantomData,
230 info: T::info(), 239 info: InfoRef::new(T::info()),
231 state: T::state(),
232 periph_clock: T::frequency(), 240 periph_clock: T::frequency(),
233 } 241 }
234 } 242 }
@@ -248,7 +256,7 @@ impl<'d> Can<'d> {
248 256
249 CanConfig { 257 CanConfig {
250 phantom: self.phantom, 258 phantom: self.phantom,
251 info: self.info, 259 info: InfoRef::new(&self.info),
252 periph_clock: self.periph_clock, 260 periph_clock: self.periph_clock,
253 } 261 }
254 } 262 }
@@ -297,7 +305,9 @@ impl<'d> Can<'d> {
297 self.info.regs.0.mcr().modify(|m| m.set_sleep(true)); 305 self.info.regs.0.mcr().modify(|m| m.set_sleep(true));
298 306
299 poll_fn(|cx| { 307 poll_fn(|cx| {
300 self.state.err_waker.register(cx.waker()); 308 self.info.state.lock(|s| {
309 s.borrow().err_waker.register(cx.waker());
310 });
301 if self.is_sleeping() { 311 if self.is_sleeping() {
302 Poll::Ready(()) 312 Poll::Ready(())
303 } else { 313 } else {
@@ -350,8 +360,7 @@ impl<'d> Can<'d> {
350 pub async fn flush(&self, mb: Mailbox) { 360 pub async fn flush(&self, mb: Mailbox) {
351 CanTx { 361 CanTx {
352 _phantom: PhantomData, 362 _phantom: PhantomData,
353 info: self.info, 363 info: TxInfoRef::new(&self.info),
354 state: self.state,
355 } 364 }
356 .flush_inner(mb) 365 .flush_inner(mb)
357 .await; 366 .await;
@@ -366,8 +375,7 @@ impl<'d> Can<'d> {
366 pub async fn flush_any(&self) { 375 pub async fn flush_any(&self) {
367 CanTx { 376 CanTx {
368 _phantom: PhantomData, 377 _phantom: PhantomData,
369 info: self.info, 378 info: TxInfoRef::new(&self.info),
370 state: self.state,
371 } 379 }
372 .flush_any_inner() 380 .flush_any_inner()
373 .await 381 .await
@@ -377,8 +385,7 @@ impl<'d> Can<'d> {
377 pub async fn flush_all(&self) { 385 pub async fn flush_all(&self) {
378 CanTx { 386 CanTx {
379 _phantom: PhantomData, 387 _phantom: PhantomData,
380 info: self.info, 388 info: TxInfoRef::new(&self.info),
381 state: self.state,
382 } 389 }
383 .flush_all_inner() 390 .flush_all_inner()
384 .await 391 .await
@@ -406,19 +413,19 @@ impl<'d> Can<'d> {
406 /// 413 ///
407 /// Returns a tuple of the time the message was received and the message frame 414 /// Returns a tuple of the time the message was received and the message frame
408 pub async fn read(&mut self) -> Result<Envelope, BusError> { 415 pub async fn read(&mut self) -> Result<Envelope, BusError> {
409 self.state.rx_mode.read(self.info, self.state).await 416 RxMode::read(&self.info).await
410 } 417 }
411 418
412 /// Attempts to read a CAN frame without blocking. 419 /// Attempts to read a CAN frame without blocking.
413 /// 420 ///
414 /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. 421 /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
415 pub fn try_read(&mut self) -> Result<Envelope, TryReadError> { 422 pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
416 self.state.rx_mode.try_read(self.info) 423 RxMode::try_read(&self.info)
417 } 424 }
418 425
419 /// Waits while receive queue is empty. 426 /// Waits while receive queue is empty.
420 pub async fn wait_not_empty(&mut self) { 427 pub async fn wait_not_empty(&mut self) {
421 self.state.rx_mode.wait_not_empty(self.info, self.state).await 428 RxMode::wait_not_empty(&self.info).await
422 } 429 }
423 430
424 /// Split the CAN driver into transmit and receive halves. 431 /// Split the CAN driver into transmit and receive halves.
@@ -428,13 +435,11 @@ impl<'d> Can<'d> {
428 ( 435 (
429 CanTx { 436 CanTx {
430 _phantom: PhantomData, 437 _phantom: PhantomData,
431 info: self.info, 438 info: TxInfoRef::new(&self.info),
432 state: self.state,
433 }, 439 },
434 CanRx { 440 CanRx {
435 _phantom: PhantomData, 441 _phantom: PhantomData,
436 info: self.info, 442 info: RxInfoRef::new(&self.info),
437 state: self.state,
438 }, 443 },
439 ) 444 )
440 } 445 }
@@ -459,7 +464,7 @@ impl<'d> Can<'d> {
459 /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master 464 /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master
460 /// peripheral instead. 465 /// peripheral instead.
461 pub fn modify_filters(&mut self) -> MasterFilters<'_> { 466 pub fn modify_filters(&mut self) -> MasterFilters<'_> {
462 unsafe { MasterFilters::new(self.info) } 467 unsafe { MasterFilters::new(&self.info) }
463 } 468 }
464} 469}
465 470
@@ -514,8 +519,7 @@ impl<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_
514/// CAN driver, transmit half. 519/// CAN driver, transmit half.
515pub struct CanTx<'d> { 520pub struct CanTx<'d> {
516 _phantom: PhantomData<&'d ()>, 521 _phantom: PhantomData<&'d ()>,
517 info: &'static Info, 522 info: TxInfoRef,
518 state: &'static State,
519} 523}
520 524
521impl<'d> CanTx<'d> { 525impl<'d> CanTx<'d> {
@@ -524,7 +528,9 @@ impl<'d> CanTx<'d> {
524 /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure. 528 /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure.
525 pub async fn write(&mut self, frame: &Frame) -> TransmitStatus { 529 pub async fn write(&mut self, frame: &Frame) -> TransmitStatus {
526 poll_fn(|cx| { 530 poll_fn(|cx| {
527 self.state.tx_mode.register(cx.waker()); 531 self.info.state.lock(|s| {
532 s.borrow().tx_mode.register(cx.waker());
533 });
528 if let Ok(status) = self.info.regs.transmit(frame) { 534 if let Ok(status) = self.info.regs.transmit(frame) {
529 return Poll::Ready(status); 535 return Poll::Ready(status);
530 } 536 }
@@ -549,7 +555,9 @@ impl<'d> CanTx<'d> {
549 555
550 async fn flush_inner(&self, mb: Mailbox) { 556 async fn flush_inner(&self, mb: Mailbox) {
551 poll_fn(|cx| { 557 poll_fn(|cx| {
552 self.state.tx_mode.register(cx.waker()); 558 self.info.state.lock(|s| {
559 s.borrow().tx_mode.register(cx.waker());
560 });
553 if self.info.regs.0.tsr().read().tme(mb.index()) { 561 if self.info.regs.0.tsr().read().tme(mb.index()) {
554 return Poll::Ready(()); 562 return Poll::Ready(());
555 } 563 }
@@ -566,7 +574,9 @@ impl<'d> CanTx<'d> {
566 574
567 async fn flush_any_inner(&self) { 575 async fn flush_any_inner(&self) {
568 poll_fn(|cx| { 576 poll_fn(|cx| {
569 self.state.tx_mode.register(cx.waker()); 577 self.info.state.lock(|s| {
578 s.borrow().tx_mode.register(cx.waker());
579 });
570 580
571 let tsr = self.info.regs.0.tsr().read(); 581 let tsr = self.info.regs.0.tsr().read();
572 if tsr.tme(Mailbox::Mailbox0.index()) 582 if tsr.tme(Mailbox::Mailbox0.index())
@@ -593,7 +603,9 @@ impl<'d> CanTx<'d> {
593 603
594 async fn flush_all_inner(&self) { 604 async fn flush_all_inner(&self) {
595 poll_fn(|cx| { 605 poll_fn(|cx| {
596 self.state.tx_mode.register(cx.waker()); 606 self.info.state.lock(|s| {
607 s.borrow().tx_mode.register(cx.waker());
608 });
597 609
598 let tsr = self.info.regs.0.tsr().read(); 610 let tsr = self.info.regs.0.tsr().read();
599 if tsr.tme(Mailbox::Mailbox0.index()) 611 if tsr.tme(Mailbox::Mailbox0.index())
@@ -634,7 +646,7 @@ impl<'d> CanTx<'d> {
634 self, 646 self,
635 txb: &'static mut TxBuf<TX_BUF_SIZE>, 647 txb: &'static mut TxBuf<TX_BUF_SIZE>,
636 ) -> BufferedCanTx<'d, TX_BUF_SIZE> { 648 ) -> BufferedCanTx<'d, TX_BUF_SIZE> {
637 BufferedCanTx::new(self.info, self.state, self, txb) 649 BufferedCanTx::new(&self.info, self, txb)
638 } 650 }
639} 651}
640 652
@@ -643,17 +655,15 @@ pub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Frame,
643 655
644/// Buffered CAN driver, transmit half. 656/// Buffered CAN driver, transmit half.
645pub struct BufferedCanTx<'d, const TX_BUF_SIZE: usize> { 657pub struct BufferedCanTx<'d, const TX_BUF_SIZE: usize> {
646 info: &'static Info, 658 info: TxInfoRef,
647 state: &'static State,
648 _tx: CanTx<'d>, 659 _tx: CanTx<'d>,
649 tx_buf: &'static TxBuf<TX_BUF_SIZE>, 660 tx_buf: &'static TxBuf<TX_BUF_SIZE>,
650} 661}
651 662
652impl<'d, const TX_BUF_SIZE: usize> BufferedCanTx<'d, TX_BUF_SIZE> { 663impl<'d, const TX_BUF_SIZE: usize> BufferedCanTx<'d, TX_BUF_SIZE> {
653 fn new(info: &'static Info, state: &'static State, _tx: CanTx<'d>, tx_buf: &'static TxBuf<TX_BUF_SIZE>) -> Self { 664 fn new(info: &'static Info, _tx: CanTx<'d>, tx_buf: &'static TxBuf<TX_BUF_SIZE>) -> Self {
654 Self { 665 Self {
655 info, 666 info: TxInfoRef::new(info),
656 state,
657 _tx, 667 _tx,
658 tx_buf, 668 tx_buf,
659 } 669 }
@@ -666,11 +676,9 @@ impl<'d, const TX_BUF_SIZE: usize> BufferedCanTx<'d, TX_BUF_SIZE> {
666 let tx_inner = super::common::ClassicBufferedTxInner { 676 let tx_inner = super::common::ClassicBufferedTxInner {
667 tx_receiver: self.tx_buf.receiver().into(), 677 tx_receiver: self.tx_buf.receiver().into(),
668 }; 678 };
669 let state = self.state as *const State; 679 self.info.state.lock(|s| {
670 unsafe { 680 s.borrow_mut().tx_mode = TxMode::Buffered(tx_inner);
671 let mut_state = state as *mut State; 681 });
672 (*mut_state).tx_mode = TxMode::Buffered(tx_inner);
673 }
674 }); 682 });
675 self 683 self
676 } 684 }
@@ -684,27 +692,18 @@ impl<'d, const TX_BUF_SIZE: usize> BufferedCanTx<'d, TX_BUF_SIZE> {
684 692
685 /// Returns a sender that can be used for sending CAN frames. 693 /// Returns a sender that can be used for sending CAN frames.
686 pub fn writer(&self) -> BufferedCanSender { 694 pub fn writer(&self) -> BufferedCanSender {
687 (self.info.internal_operation)(InternalOperation::NotifySenderCreated);
688 BufferedCanSender { 695 BufferedCanSender {
689 tx_buf: self.tx_buf.sender().into(), 696 tx_buf: self.tx_buf.sender().into(),
690 waker: self.info.tx_waker, 697 info: TxInfoRef::new(&self.info),
691 internal_operation: self.info.internal_operation,
692 } 698 }
693 } 699 }
694} 700}
695 701
696impl<'d, const TX_BUF_SIZE: usize> Drop for BufferedCanTx<'d, TX_BUF_SIZE> {
697 fn drop(&mut self) {
698 (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed);
699 }
700}
701
702/// CAN driver, receive half. 702/// CAN driver, receive half.
703#[allow(dead_code)] 703#[allow(dead_code)]
704pub struct CanRx<'d> { 704pub struct CanRx<'d> {
705 _phantom: PhantomData<&'d ()>, 705 _phantom: PhantomData<&'d ()>,
706 info: &'static Info, 706 info: RxInfoRef,
707 state: &'static State,
708} 707}
709 708
710impl<'d> CanRx<'d> { 709impl<'d> CanRx<'d> {
@@ -714,19 +713,19 @@ impl<'d> CanRx<'d> {
714 /// 713 ///
715 /// Returns a tuple of the time the message was received and the message frame 714 /// Returns a tuple of the time the message was received and the message frame
716 pub async fn read(&mut self) -> Result<Envelope, BusError> { 715 pub async fn read(&mut self) -> Result<Envelope, BusError> {
717 self.state.rx_mode.read(self.info, self.state).await 716 RxMode::read(&self.info).await
718 } 717 }
719 718
720 /// Attempts to read a CAN frame without blocking. 719 /// Attempts to read a CAN frame without blocking.
721 /// 720 ///
722 /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. 721 /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
723 pub fn try_read(&mut self) -> Result<Envelope, TryReadError> { 722 pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
724 self.state.rx_mode.try_read(self.info) 723 RxMode::try_read(&self.info)
725 } 724 }
726 725
727 /// Waits while receive queue is empty. 726 /// Waits while receive queue is empty.
728 pub async fn wait_not_empty(&mut self) { 727 pub async fn wait_not_empty(&mut self) {
729 self.state.rx_mode.wait_not_empty(self.info, self.state).await 728 RxMode::wait_not_empty(&self.info).await
730 } 729 }
731 730
732 /// Return a buffered instance of driver. User must supply Buffers 731 /// Return a buffered instance of driver. User must supply Buffers
@@ -734,7 +733,7 @@ impl<'d> CanRx<'d> {
734 self, 733 self,
735 rxb: &'static mut RxBuf<RX_BUF_SIZE>, 734 rxb: &'static mut RxBuf<RX_BUF_SIZE>,
736 ) -> BufferedCanRx<'d, RX_BUF_SIZE> { 735 ) -> BufferedCanRx<'d, RX_BUF_SIZE> {
737 BufferedCanRx::new(self.info, self.state, self, rxb) 736 BufferedCanRx::new(&self.info, self, rxb)
738 } 737 }
739 738
740 /// Accesses the filter banks owned by this CAN peripheral. 739 /// Accesses the filter banks owned by this CAN peripheral.
@@ -742,7 +741,7 @@ impl<'d> CanRx<'d> {
742 /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master 741 /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master
743 /// peripheral instead. 742 /// peripheral instead.
744 pub fn modify_filters(&mut self) -> MasterFilters<'_> { 743 pub fn modify_filters(&mut self) -> MasterFilters<'_> {
745 unsafe { MasterFilters::new(self.info) } 744 unsafe { MasterFilters::new(&self.info) }
746 } 745 }
747} 746}
748 747
@@ -751,17 +750,15 @@ pub type RxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<
751 750
752/// CAN driver, receive half in Buffered mode. 751/// CAN driver, receive half in Buffered mode.
753pub struct BufferedCanRx<'d, const RX_BUF_SIZE: usize> { 752pub struct BufferedCanRx<'d, const RX_BUF_SIZE: usize> {
754 info: &'static Info, 753 info: RxInfoRef,
755 state: &'static State,
756 rx: CanRx<'d>, 754 rx: CanRx<'d>,
757 rx_buf: &'static RxBuf<RX_BUF_SIZE>, 755 rx_buf: &'static RxBuf<RX_BUF_SIZE>,
758} 756}
759 757
760impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> { 758impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> {
761 fn new(info: &'static Info, state: &'static State, rx: CanRx<'d>, rx_buf: &'static RxBuf<RX_BUF_SIZE>) -> Self { 759 fn new(info: &'static Info, rx: CanRx<'d>, rx_buf: &'static RxBuf<RX_BUF_SIZE>) -> Self {
762 BufferedCanRx { 760 BufferedCanRx {
763 info, 761 info: RxInfoRef::new(info),
764 state,
765 rx, 762 rx,
766 rx_buf, 763 rx_buf,
767 } 764 }
@@ -774,11 +771,9 @@ impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> {
774 let rx_inner = super::common::ClassicBufferedRxInner { 771 let rx_inner = super::common::ClassicBufferedRxInner {
775 rx_sender: self.rx_buf.sender().into(), 772 rx_sender: self.rx_buf.sender().into(),
776 }; 773 };
777 let state = self.state as *const State; 774 self.info.state.lock(|s| {
778 unsafe { 775 s.borrow_mut().rx_mode = RxMode::Buffered(rx_inner);
779 let mut_state = state as *mut State; 776 });
780 (*mut_state).rx_mode = RxMode::Buffered(rx_inner);
781 }
782 }); 777 });
783 self 778 self
784 } 779 }
@@ -792,7 +787,7 @@ impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> {
792 /// 787 ///
793 /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. 788 /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
794 pub fn try_read(&mut self) -> Result<Envelope, TryReadError> { 789 pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
795 match &self.state.rx_mode { 790 self.info.state.lock(|s| match &s.borrow().rx_mode {
796 RxMode::Buffered(_) => { 791 RxMode::Buffered(_) => {
797 if let Ok(result) = self.rx_buf.try_receive() { 792 if let Ok(result) = self.rx_buf.try_receive() {
798 match result { 793 match result {
@@ -810,7 +805,7 @@ impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> {
810 _ => { 805 _ => {
811 panic!("Bad Mode") 806 panic!("Bad Mode")
812 } 807 }
813 } 808 })
814 } 809 }
815 810
816 /// Waits while receive queue is empty. 811 /// Waits while receive queue is empty.
@@ -820,10 +815,9 @@ impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> {
820 815
821 /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. 816 /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
822 pub fn reader(&self) -> BufferedCanReceiver { 817 pub fn reader(&self) -> BufferedCanReceiver {
823 (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated);
824 BufferedCanReceiver { 818 BufferedCanReceiver {
825 rx_buf: self.rx_buf.receiver().into(), 819 rx_buf: self.rx_buf.receiver().into(),
826 internal_operation: self.info.internal_operation, 820 info: RxInfoRef::new(&self.info),
827 } 821 }
828 } 822 }
829 823
@@ -836,12 +830,6 @@ impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> {
836 } 830 }
837} 831}
838 832
839impl<'d, const RX_BUF_SIZE: usize> Drop for BufferedCanRx<'d, RX_BUF_SIZE> {
840 fn drop(&mut self) {
841 (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed);
842 }
843}
844
845impl Drop for Can<'_> { 833impl Drop for Can<'_> {
846 fn drop(&mut self) { 834 fn drop(&mut self) {
847 // Cannot call `free()` because it moves the instance. 835 // Cannot call `free()` because it moves the instance.
@@ -929,27 +917,30 @@ impl RxMode {
929 } 917 }
930 } 918 }
931 919
932 pub(crate) async fn read(&self, info: &Info, state: &State) -> Result<Envelope, BusError> { 920 pub(crate) async fn read(info: &Info) -> Result<Envelope, BusError> {
933 match self { 921 poll_fn(|cx| {
934 Self::NonBuffered(waker) => { 922 info.state.lock(|state| {
935 poll_fn(|cx| { 923 let state = state.borrow();
936 state.err_waker.register(cx.waker()); 924 state.err_waker.register(cx.waker());
937 waker.register(cx.waker()); 925 match &state.rx_mode {
938 match self.try_read(info) { 926 Self::NonBuffered(waker) => {
939 Ok(result) => Poll::Ready(Ok(result)), 927 waker.register(cx.waker());
940 Err(TryReadError::Empty) => Poll::Pending,
941 Err(TryReadError::BusError(be)) => Poll::Ready(Err(be)),
942 } 928 }
943 }) 929 _ => {
944 .await 930 panic!("Bad Mode")
945 } 931 }
946 _ => { 932 }
947 panic!("Bad Mode") 933 });
934 match RxMode::try_read(info) {
935 Ok(result) => Poll::Ready(Ok(result)),
936 Err(TryReadError::Empty) => Poll::Pending,
937 Err(TryReadError::BusError(be)) => Poll::Ready(Err(be)),
948 } 938 }
949 } 939 })
940 .await
950 } 941 }
951 pub(crate) fn try_read(&self, info: &Info) -> Result<Envelope, TryReadError> { 942 pub(crate) fn try_read(info: &Info) -> Result<Envelope, TryReadError> {
952 match self { 943 info.state.lock(|state| match state.borrow().rx_mode {
953 Self::NonBuffered(_) => { 944 Self::NonBuffered(_) => {
954 let registers = &info.regs; 945 let registers = &info.regs;
955 if let Some(msg) = registers.receive_fifo(RxFifo::Fifo0) { 946 if let Some(msg) = registers.receive_fifo(RxFifo::Fifo0) {
@@ -975,25 +966,28 @@ impl RxMode {
975 _ => { 966 _ => {
976 panic!("Bad Mode") 967 panic!("Bad Mode")
977 } 968 }
978 } 969 })
979 } 970 }
980 pub(crate) async fn wait_not_empty(&self, info: &Info, state: &State) { 971 pub(crate) async fn wait_not_empty(info: &Info) {
981 match &state.rx_mode { 972 poll_fn(|cx| {
982 Self::NonBuffered(waker) => { 973 info.state.lock(|s| {
983 poll_fn(|cx| { 974 let state = s.borrow();
984 waker.register(cx.waker()); 975 match &state.rx_mode {
985 if info.regs.receive_frame_available() { 976 Self::NonBuffered(waker) => {
986 Poll::Ready(()) 977 waker.register(cx.waker());
987 } else {
988 Poll::Pending
989 } 978 }
990 }) 979 _ => {
991 .await 980 panic!("Bad Mode")
992 } 981 }
993 _ => { 982 }
994 panic!("Bad Mode") 983 });
984 if info.regs.receive_frame_available() {
985 Poll::Ready(())
986 } else {
987 Poll::Pending
995 } 988 }
996 } 989 })
990 .await
997 } 991 }
998} 992}
999 993
@@ -1008,21 +1002,25 @@ impl TxMode {
1008 tsr.tme(Mailbox::Mailbox0.index()) || tsr.tme(Mailbox::Mailbox1.index()) || tsr.tme(Mailbox::Mailbox2.index()) 1002 tsr.tme(Mailbox::Mailbox0.index()) || tsr.tme(Mailbox::Mailbox1.index()) || tsr.tme(Mailbox::Mailbox2.index())
1009 } 1003 }
1010 pub fn on_interrupt<T: Instance>(&self) { 1004 pub fn on_interrupt<T: Instance>(&self) {
1011 match &T::state().tx_mode { 1005 T::info().state.lock(|state| {
1012 TxMode::NonBuffered(waker) => waker.wake(), 1006 let tx_mode = &state.borrow().tx_mode;
1013 TxMode::Buffered(buf) => { 1007
1014 while self.buffer_free::<T>() { 1008 match tx_mode {
1015 match buf.tx_receiver.try_receive() { 1009 TxMode::NonBuffered(waker) => waker.wake(),
1016 Ok(frame) => { 1010 TxMode::Buffered(buf) => {
1017 _ = Registers(T::regs()).transmit(&frame); 1011 while self.buffer_free::<T>() {
1018 } 1012 match buf.tx_receiver.try_receive() {
1019 Err(_) => { 1013 Ok(frame) => {
1020 break; 1014 _ = Registers(T::regs()).transmit(&frame);
1015 }
1016 Err(_) => {
1017 break;
1018 }
1021 } 1019 }
1022 } 1020 }
1023 } 1021 }
1024 } 1022 }
1025 } 1023 });
1026 } 1024 }
1027 1025
1028 fn register(&self, arg: &core::task::Waker) { 1026 fn register(&self, arg: &core::task::Waker) {
@@ -1057,14 +1055,15 @@ impl State {
1057 } 1055 }
1058} 1056}
1059 1057
1058type SharedState = embassy_sync::blocking_mutex::Mutex<CriticalSectionRawMutex, core::cell::RefCell<State>>;
1060pub(crate) struct Info { 1059pub(crate) struct Info {
1061 regs: Registers, 1060 regs: Registers,
1062 tx_interrupt: crate::interrupt::Interrupt, 1061 tx_interrupt: crate::interrupt::Interrupt,
1063 rx0_interrupt: crate::interrupt::Interrupt, 1062 rx0_interrupt: crate::interrupt::Interrupt,
1064 rx1_interrupt: crate::interrupt::Interrupt, 1063 rx1_interrupt: crate::interrupt::Interrupt,
1065 sce_interrupt: crate::interrupt::Interrupt, 1064 sce_interrupt: crate::interrupt::Interrupt,
1066 tx_waker: fn(), 1065 pub(crate) tx_waker: fn(),
1067 internal_operation: fn(InternalOperation), 1066 state: SharedState,
1068 1067
1069 /// The total number of filter banks available to the instance. 1068 /// The total number of filter banks available to the instance.
1070 /// 1069 ///
@@ -1072,12 +1071,37 @@ pub(crate) struct Info {
1072 num_filter_banks: u8, 1071 num_filter_banks: u8,
1073} 1072}
1074 1073
1074impl Info {
1075 pub(crate) fn adjust_reference_counter(&self, val: RefCountOp) {
1076 self.state.lock(|s| {
1077 let mut mut_state = s.borrow_mut();
1078 match val {
1079 RefCountOp::NotifySenderCreated => {
1080 mut_state.sender_instance_count += 1;
1081 }
1082 RefCountOp::NotifySenderDestroyed => {
1083 mut_state.sender_instance_count -= 1;
1084 if 0 == mut_state.sender_instance_count {
1085 (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
1086 }
1087 }
1088 RefCountOp::NotifyReceiverCreated => {
1089 mut_state.receiver_instance_count += 1;
1090 }
1091 RefCountOp::NotifyReceiverDestroyed => {
1092 mut_state.receiver_instance_count -= 1;
1093 if 0 == mut_state.receiver_instance_count {
1094 (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
1095 }
1096 }
1097 }
1098 });
1099 }
1100}
1101
1075trait SealedInstance { 1102trait SealedInstance {
1076 fn info() -> &'static Info; 1103 fn info() -> &'static Info;
1077 fn regs() -> crate::pac::can::Can; 1104 fn regs() -> crate::pac::can::Can;
1078 fn state() -> &'static State;
1079 unsafe fn mut_state() -> &'static mut State;
1080 fn internal_operation(val: InternalOperation);
1081} 1105}
1082 1106
1083/// CAN instance trait. 1107/// CAN instance trait.
@@ -1135,53 +1159,14 @@ foreach_peripheral!(
1135 rx1_interrupt: crate::_generated::peripheral_interrupts::$inst::RX1::IRQ, 1159 rx1_interrupt: crate::_generated::peripheral_interrupts::$inst::RX1::IRQ,
1136 sce_interrupt: crate::_generated::peripheral_interrupts::$inst::SCE::IRQ, 1160 sce_interrupt: crate::_generated::peripheral_interrupts::$inst::SCE::IRQ,
1137 tx_waker: crate::_generated::peripheral_interrupts::$inst::TX::pend, 1161 tx_waker: crate::_generated::peripheral_interrupts::$inst::TX::pend,
1138 internal_operation: peripherals::$inst::internal_operation,
1139 num_filter_banks: peripherals::$inst::NUM_FILTER_BANKS, 1162 num_filter_banks: peripherals::$inst::NUM_FILTER_BANKS,
1163 state: embassy_sync::blocking_mutex::Mutex::new(core::cell::RefCell::new(State::new())),
1140 }; 1164 };
1141 &INFO 1165 &INFO
1142 } 1166 }
1143 fn regs() -> crate::pac::can::Can { 1167 fn regs() -> crate::pac::can::Can {
1144 crate::pac::$inst 1168 crate::pac::$inst
1145 } 1169 }
1146
1147 unsafe fn mut_state() -> & 'static mut State {
1148 static mut STATE: State = State::new();
1149 &mut *core::ptr::addr_of_mut!(STATE)
1150 }
1151 fn state() -> &'static State {
1152 unsafe { peripherals::$inst::mut_state() }
1153 }
1154
1155
1156 fn internal_operation(val: InternalOperation) {
1157 critical_section::with(|_| {
1158 //let state = self.state as *const State;
1159 unsafe {
1160 //let mut_state = state as *mut State;
1161 let mut_state = peripherals::$inst::mut_state();
1162 match val {
1163 InternalOperation::NotifySenderCreated => {
1164 mut_state.sender_instance_count += 1;
1165 }
1166 InternalOperation::NotifySenderDestroyed => {
1167 mut_state.sender_instance_count -= 1;
1168 if ( 0 == mut_state.sender_instance_count) {
1169 (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
1170 }
1171 }
1172 InternalOperation::NotifyReceiverCreated => {
1173 mut_state.receiver_instance_count += 1;
1174 }
1175 InternalOperation::NotifyReceiverDestroyed => {
1176 mut_state.receiver_instance_count -= 1;
1177 if ( 0 == mut_state.receiver_instance_count) {
1178 (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
1179 }
1180 }
1181 }
1182 }
1183 });
1184 }
1185 } 1170 }
1186 1171
1187 impl Instance for peripherals::$inst { 1172 impl Instance for peripherals::$inst {
diff --git a/embassy-stm32/src/can/common.rs b/embassy-stm32/src/can/common.rs
index 386d4467c..980f33a04 100644
--- a/embassy-stm32/src/can/common.rs
+++ b/embassy-stm32/src/can/common.rs
@@ -24,22 +24,21 @@ pub(crate) struct FdBufferedTxInner {
24/// Sender that can be used for sending CAN frames. 24/// Sender that can be used for sending CAN frames.
25pub struct BufferedSender<'ch, FRAME> { 25pub struct BufferedSender<'ch, FRAME> {
26 pub(crate) tx_buf: embassy_sync::channel::SendDynamicSender<'ch, FRAME>, 26 pub(crate) tx_buf: embassy_sync::channel::SendDynamicSender<'ch, FRAME>,
27 pub(crate) waker: fn(), 27 pub(crate) info: TxInfoRef,
28 pub(crate) internal_operation: fn(InternalOperation),
29} 28}
30 29
31impl<'ch, FRAME> BufferedSender<'ch, FRAME> { 30impl<'ch, FRAME> BufferedSender<'ch, FRAME> {
32 /// Async write frame to TX buffer. 31 /// Async write frame to TX buffer.
33 pub fn try_write(&mut self, frame: FRAME) -> Result<(), embassy_sync::channel::TrySendError<FRAME>> { 32 pub fn try_write(&mut self, frame: FRAME) -> Result<(), embassy_sync::channel::TrySendError<FRAME>> {
34 self.tx_buf.try_send(frame)?; 33 self.tx_buf.try_send(frame)?;
35 (self.waker)(); 34 (self.info.tx_waker)();
36 Ok(()) 35 Ok(())
37 } 36 }
38 37
39 /// Async write frame to TX buffer. 38 /// Async write frame to TX buffer.
40 pub async fn write(&mut self, frame: FRAME) { 39 pub async fn write(&mut self, frame: FRAME) {
41 self.tx_buf.send(frame).await; 40 self.tx_buf.send(frame).await;
42 (self.waker)(); 41 (self.info.tx_waker)();
43 } 42 }
44 43
45 /// Allows a poll_fn to poll until the channel is ready to write 44 /// Allows a poll_fn to poll until the channel is ready to write
@@ -50,28 +49,20 @@ impl<'ch, FRAME> BufferedSender<'ch, FRAME> {
50 49
51impl<'ch, FRAME> Clone for BufferedSender<'ch, FRAME> { 50impl<'ch, FRAME> Clone for BufferedSender<'ch, FRAME> {
52 fn clone(&self) -> Self { 51 fn clone(&self) -> Self {
53 (self.internal_operation)(InternalOperation::NotifySenderCreated);
54 Self { 52 Self {
55 tx_buf: self.tx_buf, 53 tx_buf: self.tx_buf,
56 waker: self.waker, 54 info: TxInfoRef::new(&self.info),
57 internal_operation: self.internal_operation,
58 } 55 }
59 } 56 }
60} 57}
61 58
62impl<'ch, FRAME> Drop for BufferedSender<'ch, FRAME> {
63 fn drop(&mut self) {
64 (self.internal_operation)(InternalOperation::NotifySenderDestroyed);
65 }
66}
67
68/// Sender that can be used for sending Classic CAN frames. 59/// Sender that can be used for sending Classic CAN frames.
69pub type BufferedCanSender = BufferedSender<'static, Frame>; 60pub type BufferedCanSender = BufferedSender<'static, Frame>;
70 61
71/// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. 62/// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
72pub struct BufferedReceiver<'ch, ENVELOPE> { 63pub struct BufferedReceiver<'ch, ENVELOPE> {
73 pub(crate) rx_buf: embassy_sync::channel::SendDynamicReceiver<'ch, Result<ENVELOPE, BusError>>, 64 pub(crate) rx_buf: embassy_sync::channel::SendDynamicReceiver<'ch, Result<ENVELOPE, BusError>>,
74 pub(crate) internal_operation: fn(InternalOperation), 65 pub(crate) info: RxInfoRef,
75} 66}
76 67
77impl<'ch, ENVELOPE> BufferedReceiver<'ch, ENVELOPE> { 68impl<'ch, ENVELOPE> BufferedReceiver<'ch, ENVELOPE> {
@@ -106,19 +97,99 @@ impl<'ch, ENVELOPE> BufferedReceiver<'ch, ENVELOPE> {
106 97
107impl<'ch, ENVELOPE> Clone for BufferedReceiver<'ch, ENVELOPE> { 98impl<'ch, ENVELOPE> Clone for BufferedReceiver<'ch, ENVELOPE> {
108 fn clone(&self) -> Self { 99 fn clone(&self) -> Self {
109 (self.internal_operation)(InternalOperation::NotifyReceiverCreated);
110 Self { 100 Self {
111 rx_buf: self.rx_buf, 101 rx_buf: self.rx_buf,
112 internal_operation: self.internal_operation, 102 info: RxInfoRef::new(&self.info),
113 } 103 }
114 } 104 }
115} 105}
116 106
117impl<'ch, ENVELOPE> Drop for BufferedReceiver<'ch, ENVELOPE> { 107/// A BufferedCanReceiver for Classic CAN frames.
108pub type BufferedCanReceiver = BufferedReceiver<'static, Envelope>;
109
110/// Provides a reference to the driver internals and implements RAII for the internal reference
111/// counting. Each type that can operate on the driver should contain either InfoRef
112/// or the similar TxInfoRef or RxInfoRef. The new method and the Drop impl will automatically
113/// call the reference counting function. Like this, the reference counting function does not
114/// need to be called manually for each type.
115pub(crate) struct InfoRef {
116 info: &'static super::Info,
117}
118impl InfoRef {
119 pub(crate) fn new(info: &'static super::Info) -> Self {
120 info.adjust_reference_counter(RefCountOp::NotifyReceiverCreated);
121 info.adjust_reference_counter(RefCountOp::NotifySenderCreated);
122 Self { info }
123 }
124}
125
126impl Drop for InfoRef {
118 fn drop(&mut self) { 127 fn drop(&mut self) {
119 (self.internal_operation)(InternalOperation::NotifyReceiverDestroyed); 128 self.info.adjust_reference_counter(RefCountOp::NotifyReceiverDestroyed);
129 self.info.adjust_reference_counter(RefCountOp::NotifySenderDestroyed);
120 } 130 }
121} 131}
122 132
123/// A BufferedCanReceiver for Classic CAN frames. 133impl core::ops::Deref for InfoRef {
124pub type BufferedCanReceiver = BufferedReceiver<'static, Envelope>; 134 type Target = &'static super::Info;
135
136 fn deref(&self) -> &Self::Target {
137 &self.info
138 }
139}
140
141/// Provides a reference to the driver internals and implements RAII for the internal reference
142/// counting for Tx only types.
143/// See InfoRef for further doc.
144pub(crate) struct TxInfoRef {
145 info: &'static super::Info,
146}
147
148impl TxInfoRef {
149 pub(crate) fn new(info: &'static super::Info) -> Self {
150 info.adjust_reference_counter(RefCountOp::NotifySenderCreated);
151 Self { info }
152 }
153}
154
155impl Drop for TxInfoRef {
156 fn drop(&mut self) {
157 self.info.adjust_reference_counter(RefCountOp::NotifySenderDestroyed);
158 }
159}
160
161impl core::ops::Deref for TxInfoRef {
162 type Target = &'static super::Info;
163
164 fn deref(&self) -> &Self::Target {
165 &self.info
166 }
167}
168
169/// Provides a reference to the driver internals and implements RAII for the internal reference
170/// counting for Rx only types.
171/// See InfoRef for further doc.
172pub(crate) struct RxInfoRef {
173 info: &'static super::Info,
174}
175
176impl RxInfoRef {
177 pub(crate) fn new(info: &'static super::Info) -> Self {
178 info.adjust_reference_counter(RefCountOp::NotifyReceiverCreated);
179 Self { info }
180 }
181}
182
183impl Drop for RxInfoRef {
184 fn drop(&mut self) {
185 self.info.adjust_reference_counter(RefCountOp::NotifyReceiverDestroyed);
186 }
187}
188
189impl core::ops::Deref for RxInfoRef {
190 type Target = &'static super::Info;
191
192 fn deref(&self) -> &Self::Target {
193 &self.info
194 }
195}
diff --git a/embassy-stm32/src/can/enums.rs b/embassy-stm32/src/can/enums.rs
index 97cb47640..6d91020fc 100644
--- a/embassy-stm32/src/can/enums.rs
+++ b/embassy-stm32/src/can/enums.rs
@@ -72,7 +72,7 @@ pub enum TryReadError {
72/// Internal Operation 72/// Internal Operation
73#[derive(Debug)] 73#[derive(Debug)]
74#[cfg_attr(feature = "defmt", derive(defmt::Format))] 74#[cfg_attr(feature = "defmt", derive(defmt::Format))]
75pub enum InternalOperation { 75pub enum RefCountOp {
76 /// Notify receiver created 76 /// Notify receiver created
77 NotifyReceiverCreated, 77 NotifyReceiverCreated,
78 /// Notify receiver destroyed 78 /// Notify receiver destroyed
diff --git a/embassy-stm32/src/can/fd/message_ram/extended_filter.rs b/embassy-stm32/src/can/fd/message_ram/extended_filter.rs
index 453e9056e..ac47901a8 100644
--- a/embassy-stm32/src/can/fd/message_ram/extended_filter.rs
+++ b/embassy-stm32/src/can/fd/message_ram/extended_filter.rs
@@ -115,22 +115,22 @@ impl R {
115impl W { 115impl W {
116 #[doc = "Byte 0 - Bits 0:28 - EFID1"] 116 #[doc = "Byte 0 - Bits 0:28 - EFID1"]
117 #[inline(always)] 117 #[inline(always)]
118 pub fn efid1(&mut self) -> EFID1_W { 118 pub fn efid1(&mut self) -> EFID1_W<'_> {
119 EFID1_W { w: self } 119 EFID1_W { w: self }
120 } 120 }
121 #[doc = "Byte 0 - Bits 29:31 - EFEC"] 121 #[doc = "Byte 0 - Bits 29:31 - EFEC"]
122 #[inline(always)] 122 #[inline(always)]
123 pub fn efec(&mut self) -> EFEC_W { 123 pub fn efec(&mut self) -> EFEC_W<'_> {
124 EFEC_W { w: self } 124 EFEC_W { w: self }
125 } 125 }
126 #[doc = "Byte 1 - Bits 0:28 - EFID2"] 126 #[doc = "Byte 1 - Bits 0:28 - EFID2"]
127 #[inline(always)] 127 #[inline(always)]
128 pub fn efid2(&mut self) -> EFID2_W { 128 pub fn efid2(&mut self) -> EFID2_W<'_> {
129 EFID2_W { w: self } 129 EFID2_W { w: self }
130 } 130 }
131 #[doc = "Byte 1 - Bits 30:31 - EFT"] 131 #[doc = "Byte 1 - Bits 30:31 - EFT"]
132 #[inline(always)] 132 #[inline(always)]
133 pub fn eft(&mut self) -> EFT_W { 133 pub fn eft(&mut self) -> EFT_W<'_> {
134 EFT_W { w: self } 134 EFT_W { w: self }
135 } 135 }
136} 136}
diff --git a/embassy-stm32/src/can/fd/message_ram/standard_filter.rs b/embassy-stm32/src/can/fd/message_ram/standard_filter.rs
index 3a3bbcf12..f52646bfe 100644
--- a/embassy-stm32/src/can/fd/message_ram/standard_filter.rs
+++ b/embassy-stm32/src/can/fd/message_ram/standard_filter.rs
@@ -115,22 +115,22 @@ impl R {
115impl W { 115impl W {
116 #[doc = "Bits 0:10 - SFID2"] 116 #[doc = "Bits 0:10 - SFID2"]
117 #[inline(always)] 117 #[inline(always)]
118 pub fn sfid2(&mut self) -> SFID2_W { 118 pub fn sfid2(&mut self) -> SFID2_W<'_> {
119 SFID2_W { w: self } 119 SFID2_W { w: self }
120 } 120 }
121 #[doc = "Bits 16:26 - SFID1"] 121 #[doc = "Bits 16:26 - SFID1"]
122 #[inline(always)] 122 #[inline(always)]
123 pub fn sfid1(&mut self) -> SFID1_W { 123 pub fn sfid1(&mut self) -> SFID1_W<'_> {
124 SFID1_W { w: self } 124 SFID1_W { w: self }
125 } 125 }
126 #[doc = "Bits 27:29 - SFEC"] 126 #[doc = "Bits 27:29 - SFEC"]
127 #[inline(always)] 127 #[inline(always)]
128 pub fn sfec(&mut self) -> SFEC_W { 128 pub fn sfec(&mut self) -> SFEC_W<'_> {
129 SFEC_W { w: self } 129 SFEC_W { w: self }
130 } 130 }
131 #[doc = "Bits 30:31 - SFT"] 131 #[doc = "Bits 30:31 - SFT"]
132 #[inline(always)] 132 #[inline(always)]
133 pub fn sft(&mut self) -> SFT_W { 133 pub fn sft(&mut self) -> SFT_W<'_> {
134 SFT_W { w: self } 134 SFT_W { w: self }
135 } 135 }
136} 136}
diff --git a/embassy-stm32/src/can/fd/message_ram/txbuffer_element.rs b/embassy-stm32/src/can/fd/message_ram/txbuffer_element.rs
index 455406a1c..6d65a86cb 100644
--- a/embassy-stm32/src/can/fd/message_ram/txbuffer_element.rs
+++ b/embassy-stm32/src/can/fd/message_ram/txbuffer_element.rs
@@ -376,47 +376,47 @@ impl R {
376impl W { 376impl W {
377 #[doc = "Byte 0 - Bits 0:28 - ID"] 377 #[doc = "Byte 0 - Bits 0:28 - ID"]
378 #[inline(always)] 378 #[inline(always)]
379 pub fn id(&mut self) -> ID_W { 379 pub fn id(&mut self) -> ID_W<'_> {
380 ID_W { w: self } 380 ID_W { w: self }
381 } 381 }
382 #[doc = "Byte 0 - Bit 29 - RTR"] 382 #[doc = "Byte 0 - Bit 29 - RTR"]
383 #[inline(always)] 383 #[inline(always)]
384 pub fn rtr(&mut self) -> RTR_W { 384 pub fn rtr(&mut self) -> RTR_W<'_> {
385 RTR_W { w: self } 385 RTR_W { w: self }
386 } 386 }
387 #[doc = "Byte 0 - Bit 30 - XTD"] 387 #[doc = "Byte 0 - Bit 30 - XTD"]
388 #[inline(always)] 388 #[inline(always)]
389 pub fn xtd(&mut self) -> XTD_W { 389 pub fn xtd(&mut self) -> XTD_W<'_> {
390 XTD_W { w: self } 390 XTD_W { w: self }
391 } 391 }
392 #[doc = "Byte 0 - Bit 31 - ESI"] 392 #[doc = "Byte 0 - Bit 31 - ESI"]
393 #[inline(always)] 393 #[inline(always)]
394 pub fn esi(&mut self) -> ESI_W { 394 pub fn esi(&mut self) -> ESI_W<'_> {
395 ESI_W { w: self } 395 ESI_W { w: self }
396 } 396 }
397 #[doc = "Byte 1 - Bit 16:19 - DLC"] 397 #[doc = "Byte 1 - Bit 16:19 - DLC"]
398 #[inline(always)] 398 #[inline(always)]
399 pub fn dlc(&mut self) -> DLC_W { 399 pub fn dlc(&mut self) -> DLC_W<'_> {
400 DLC_W { w: self } 400 DLC_W { w: self }
401 } 401 }
402 #[doc = "Byte 1 - Bit 20 - BRS"] 402 #[doc = "Byte 1 - Bit 20 - BRS"]
403 #[inline(always)] 403 #[inline(always)]
404 pub fn brs(&mut self) -> BRS_W { 404 pub fn brs(&mut self) -> BRS_W<'_> {
405 BRS_W { w: self } 405 BRS_W { w: self }
406 } 406 }
407 #[doc = "Byte 1 - Bit 21 - FDF"] 407 #[doc = "Byte 1 - Bit 21 - FDF"]
408 #[inline(always)] 408 #[inline(always)]
409 pub fn fdf(&mut self) -> FDF_W { 409 pub fn fdf(&mut self) -> FDF_W<'_> {
410 FDF_W { w: self } 410 FDF_W { w: self }
411 } 411 }
412 #[doc = "Byte 1 - Bit 23 - EFC"] 412 #[doc = "Byte 1 - Bit 23 - EFC"]
413 #[inline(always)] 413 #[inline(always)]
414 pub fn efc(&mut self) -> EFC_W { 414 pub fn efc(&mut self) -> EFC_W<'_> {
415 EFC_W { w: self } 415 EFC_W { w: self }
416 } 416 }
417 #[doc = "Byte 1 - Bit 24:31 - MM"] 417 #[doc = "Byte 1 - Bit 24:31 - MM"]
418 #[inline(always)] 418 #[inline(always)]
419 pub fn mm(&mut self) -> MM_W { 419 pub fn mm(&mut self) -> MM_W<'_> {
420 MM_W { w: self } 420 MM_W { w: self }
421 } 421 }
422 #[doc = "Convenience function for setting the data length and frame format"] 422 #[doc = "Convenience function for setting the data length and frame format"]
diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs
index 97d22315a..99e40ba62 100644
--- a/embassy-stm32/src/can/fdcan.rs
+++ b/embassy-stm32/src/can/fdcan.rs
@@ -21,6 +21,7 @@ use self::fd::config::*;
21use self::fd::filter::*; 21use self::fd::filter::*;
22pub use self::fd::{config, filter}; 22pub use self::fd::{config, filter};
23pub use super::common::{BufferedCanReceiver, BufferedCanSender}; 23pub use super::common::{BufferedCanReceiver, BufferedCanSender};
24use super::common::{InfoRef, RxInfoRef, TxInfoRef};
24use super::enums::*; 25use super::enums::*;
25use super::frame::*; 26use super::frame::*;
26use super::util; 27use super::util;
@@ -167,10 +168,10 @@ fn calc_ns_per_timer_tick(
167pub struct CanConfigurator<'d> { 168pub struct CanConfigurator<'d> {
168 _phantom: PhantomData<&'d ()>, 169 _phantom: PhantomData<&'d ()>,
169 config: crate::can::fd::config::FdCanConfig, 170 config: crate::can::fd::config::FdCanConfig,
170 info: &'static Info,
171 /// Reference to internals. 171 /// Reference to internals.
172 properties: Properties, 172 properties: Properties,
173 periph_clock: crate::time::Hertz, 173 periph_clock: crate::time::Hertz,
174 info: InfoRef,
174} 175}
175 176
176impl<'d> CanConfigurator<'d> { 177impl<'d> CanConfigurator<'d> {
@@ -194,8 +195,6 @@ impl<'d> CanConfigurator<'d> {
194 s.borrow_mut().tx_pin_port = Some(tx.pin_port()); 195 s.borrow_mut().tx_pin_port = Some(tx.pin_port());
195 s.borrow_mut().rx_pin_port = Some(rx.pin_port()); 196 s.borrow_mut().rx_pin_port = Some(rx.pin_port());
196 }); 197 });
197 (info.internal_operation)(InternalOperation::NotifySenderCreated);
198 (info.internal_operation)(InternalOperation::NotifyReceiverCreated);
199 198
200 let mut config = crate::can::fd::config::FdCanConfig::default(); 199 let mut config = crate::can::fd::config::FdCanConfig::default();
201 config.timestamp_source = TimestampSource::Prescaler(TimestampPrescaler::_1); 200 config.timestamp_source = TimestampSource::Prescaler(TimestampPrescaler::_1);
@@ -211,9 +210,9 @@ impl<'d> CanConfigurator<'d> {
211 Self { 210 Self {
212 _phantom: PhantomData, 211 _phantom: PhantomData,
213 config, 212 config,
214 info,
215 properties: Properties::new(T::info()), 213 properties: Properties::new(T::info()),
216 periph_clock: T::frequency(), 214 periph_clock: T::frequency(),
215 info: InfoRef::new(info),
217 } 216 }
218 } 217 }
219 218
@@ -262,19 +261,17 @@ impl<'d> CanConfigurator<'d> {
262 261
263 /// Start in mode. 262 /// Start in mode.
264 pub fn start(self, mode: OperatingMode) -> Can<'d> { 263 pub fn start(self, mode: OperatingMode) -> Can<'d> {
265 let ns_per_timer_tick = calc_ns_per_timer_tick(self.info, self.periph_clock, self.config.frame_transmit); 264 let ns_per_timer_tick = calc_ns_per_timer_tick(&self.info, self.periph_clock, self.config.frame_transmit);
266 self.info.state.lock(|s| { 265 self.info.state.lock(|s| {
267 s.borrow_mut().ns_per_timer_tick = ns_per_timer_tick; 266 s.borrow_mut().ns_per_timer_tick = ns_per_timer_tick;
268 }); 267 });
269 self.info.regs.into_mode(self.config, mode); 268 self.info.regs.into_mode(self.config, mode);
270 (self.info.internal_operation)(InternalOperation::NotifySenderCreated);
271 (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated);
272 Can { 269 Can {
273 _phantom: PhantomData, 270 _phantom: PhantomData,
274 config: self.config, 271 config: self.config,
275 info: self.info,
276 _mode: mode, 272 _mode: mode,
277 properties: Properties::new(self.info), 273 properties: Properties::new(&self.info),
274 info: InfoRef::new(&self.info),
278 } 275 }
279 } 276 }
280 277
@@ -294,20 +291,13 @@ impl<'d> CanConfigurator<'d> {
294 } 291 }
295} 292}
296 293
297impl<'d> Drop for CanConfigurator<'d> {
298 fn drop(&mut self) {
299 (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed);
300 (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed);
301 }
302}
303
304/// FDCAN Instance 294/// FDCAN Instance
305pub struct Can<'d> { 295pub struct Can<'d> {
306 _phantom: PhantomData<&'d ()>, 296 _phantom: PhantomData<&'d ()>,
307 config: crate::can::fd::config::FdCanConfig, 297 config: crate::can::fd::config::FdCanConfig,
308 info: &'static Info,
309 _mode: OperatingMode, 298 _mode: OperatingMode,
310 properties: Properties, 299 properties: Properties,
300 info: InfoRef,
311} 301}
312 302
313impl<'d> Can<'d> { 303impl<'d> Can<'d> {
@@ -341,12 +331,12 @@ impl<'d> Can<'d> {
341 /// can be replaced, this call asynchronously waits for a frame to be successfully 331 /// can be replaced, this call asynchronously waits for a frame to be successfully
342 /// transmitted, then tries again. 332 /// transmitted, then tries again.
343 pub async fn write(&mut self, frame: &Frame) -> Option<Frame> { 333 pub async fn write(&mut self, frame: &Frame) -> Option<Frame> {
344 TxMode::write(self.info, frame).await 334 TxMode::write(&self.info, frame).await
345 } 335 }
346 336
347 /// Returns the next received message frame 337 /// Returns the next received message frame
348 pub async fn read(&mut self) -> Result<Envelope, BusError> { 338 pub async fn read(&mut self) -> Result<Envelope, BusError> {
349 RxMode::read_classic(self.info).await 339 RxMode::read_classic(&self.info).await
350 } 340 }
351 341
352 /// Queues the message to be sent but exerts backpressure. If a lower-priority 342 /// Queues the message to be sent but exerts backpressure. If a lower-priority
@@ -354,29 +344,27 @@ impl<'d> Can<'d> {
354 /// can be replaced, this call asynchronously waits for a frame to be successfully 344 /// can be replaced, this call asynchronously waits for a frame to be successfully
355 /// transmitted, then tries again. 345 /// transmitted, then tries again.
356 pub async fn write_fd(&mut self, frame: &FdFrame) -> Option<FdFrame> { 346 pub async fn write_fd(&mut self, frame: &FdFrame) -> Option<FdFrame> {
357 TxMode::write_fd(self.info, frame).await 347 TxMode::write_fd(&self.info, frame).await
358 } 348 }
359 349
360 /// Returns the next received message frame 350 /// Returns the next received message frame
361 pub async fn read_fd(&mut self) -> Result<FdEnvelope, BusError> { 351 pub async fn read_fd(&mut self) -> Result<FdEnvelope, BusError> {
362 RxMode::read_fd(self.info).await 352 RxMode::read_fd(&self.info).await
363 } 353 }
364 354
365 /// Split instance into separate portions: Tx(write), Rx(read), common properties 355 /// Split instance into separate portions: Tx(write), Rx(read), common properties
366 pub fn split(self) -> (CanTx<'d>, CanRx<'d>, Properties) { 356 pub fn split(self) -> (CanTx<'d>, CanRx<'d>, Properties) {
367 (self.info.internal_operation)(InternalOperation::NotifySenderCreated);
368 (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated);
369 ( 357 (
370 CanTx { 358 CanTx {
371 _phantom: PhantomData, 359 _phantom: PhantomData,
372 info: self.info,
373 config: self.config, 360 config: self.config,
374 _mode: self._mode, 361 _mode: self._mode,
362 info: TxInfoRef::new(&self.info),
375 }, 363 },
376 CanRx { 364 CanRx {
377 _phantom: PhantomData, 365 _phantom: PhantomData,
378 info: self.info,
379 _mode: self._mode, 366 _mode: self._mode,
367 info: RxInfoRef::new(&self.info),
380 }, 368 },
381 Properties { 369 Properties {
382 info: self.properties.info, 370 info: self.properties.info,
@@ -385,14 +373,12 @@ impl<'d> Can<'d> {
385 } 373 }
386 /// Join split rx and tx portions back together 374 /// Join split rx and tx portions back together
387 pub fn join(tx: CanTx<'d>, rx: CanRx<'d>) -> Self { 375 pub fn join(tx: CanTx<'d>, rx: CanRx<'d>) -> Self {
388 (tx.info.internal_operation)(InternalOperation::NotifySenderCreated);
389 (tx.info.internal_operation)(InternalOperation::NotifyReceiverCreated);
390 Can { 376 Can {
391 _phantom: PhantomData, 377 _phantom: PhantomData,
392 config: tx.config, 378 config: tx.config,
393 info: tx.info,
394 _mode: rx._mode, 379 _mode: rx._mode,
395 properties: Properties::new(tx.info), 380 properties: Properties::new(&tx.info),
381 info: InfoRef::new(&tx.info),
396 } 382 }
397 } 383 }
398 384
@@ -402,7 +388,7 @@ impl<'d> Can<'d> {
402 tx_buf: &'static mut TxBuf<TX_BUF_SIZE>, 388 tx_buf: &'static mut TxBuf<TX_BUF_SIZE>,
403 rxb: &'static mut RxBuf<RX_BUF_SIZE>, 389 rxb: &'static mut RxBuf<RX_BUF_SIZE>,
404 ) -> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { 390 ) -> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> {
405 BufferedCan::new(self.info, self._mode, tx_buf, rxb) 391 BufferedCan::new(&self.info, self._mode, tx_buf, rxb)
406 } 392 }
407 393
408 /// Return a buffered instance of driver with CAN FD support. User must supply Buffers 394 /// Return a buffered instance of driver with CAN FD support. User must supply Buffers
@@ -411,14 +397,7 @@ impl<'d> Can<'d> {
411 tx_buf: &'static mut TxFdBuf<TX_BUF_SIZE>, 397 tx_buf: &'static mut TxFdBuf<TX_BUF_SIZE>,
412 rxb: &'static mut RxFdBuf<RX_BUF_SIZE>, 398 rxb: &'static mut RxFdBuf<RX_BUF_SIZE>,
413 ) -> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { 399 ) -> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> {
414 BufferedCanFd::new(self.info, self._mode, tx_buf, rxb) 400 BufferedCanFd::new(&self.info, self._mode, tx_buf, rxb)
415 }
416}
417
418impl<'d> Drop for Can<'d> {
419 fn drop(&mut self) {
420 (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed);
421 (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed);
422 } 401 }
423} 402}
424 403
@@ -431,11 +410,11 @@ pub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Frame,
431/// Buffered FDCAN Instance 410/// Buffered FDCAN Instance
432pub struct BufferedCan<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { 411pub struct BufferedCan<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> {
433 _phantom: PhantomData<&'d ()>, 412 _phantom: PhantomData<&'d ()>,
434 info: &'static Info,
435 _mode: OperatingMode, 413 _mode: OperatingMode,
436 tx_buf: &'static TxBuf<TX_BUF_SIZE>, 414 tx_buf: &'static TxBuf<TX_BUF_SIZE>,
437 rx_buf: &'static RxBuf<RX_BUF_SIZE>, 415 rx_buf: &'static RxBuf<RX_BUF_SIZE>,
438 properties: Properties, 416 properties: Properties,
417 info: InfoRef,
439} 418}
440 419
441impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { 420impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> {
@@ -445,15 +424,13 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d,
445 tx_buf: &'static TxBuf<TX_BUF_SIZE>, 424 tx_buf: &'static TxBuf<TX_BUF_SIZE>,
446 rx_buf: &'static RxBuf<RX_BUF_SIZE>, 425 rx_buf: &'static RxBuf<RX_BUF_SIZE>,
447 ) -> Self { 426 ) -> Self {
448 (info.internal_operation)(InternalOperation::NotifySenderCreated);
449 (info.internal_operation)(InternalOperation::NotifyReceiverCreated);
450 BufferedCan { 427 BufferedCan {
451 _phantom: PhantomData, 428 _phantom: PhantomData,
452 info,
453 _mode, 429 _mode,
454 tx_buf, 430 tx_buf,
455 rx_buf, 431 rx_buf,
456 properties: Properties::new(info), 432 properties: Properties::new(info),
433 info: InfoRef::new(info),
457 } 434 }
458 .setup() 435 .setup()
459 } 436 }
@@ -492,31 +469,21 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d,
492 469
493 /// Returns a sender that can be used for sending CAN frames. 470 /// Returns a sender that can be used for sending CAN frames.
494 pub fn writer(&self) -> BufferedCanSender { 471 pub fn writer(&self) -> BufferedCanSender {
495 (self.info.internal_operation)(InternalOperation::NotifySenderCreated);
496 BufferedCanSender { 472 BufferedCanSender {
497 tx_buf: self.tx_buf.sender().into(), 473 tx_buf: self.tx_buf.sender().into(),
498 waker: self.info.tx_waker, 474 info: TxInfoRef::new(&self.info),
499 internal_operation: self.info.internal_operation,
500 } 475 }
501 } 476 }
502 477
503 /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. 478 /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
504 pub fn reader(&self) -> BufferedCanReceiver { 479 pub fn reader(&self) -> BufferedCanReceiver {
505 (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated);
506 BufferedCanReceiver { 480 BufferedCanReceiver {
507 rx_buf: self.rx_buf.receiver().into(), 481 rx_buf: self.rx_buf.receiver().into(),
508 internal_operation: self.info.internal_operation, 482 info: RxInfoRef::new(&self.info),
509 } 483 }
510 } 484 }
511} 485}
512 486
513impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Drop for BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> {
514 fn drop(&mut self) {
515 (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed);
516 (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed);
517 }
518}
519
520/// User supplied buffer for RX Buffering 487/// User supplied buffer for RX Buffering
521pub type RxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<FdEnvelope, BusError>, BUF_SIZE>; 488pub type RxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<FdEnvelope, BusError>, BUF_SIZE>;
522 489
@@ -532,11 +499,11 @@ pub type BufferedFdCanReceiver = super::common::BufferedReceiver<'static, FdEnve
532/// Buffered FDCAN Instance 499/// Buffered FDCAN Instance
533pub struct BufferedCanFd<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { 500pub struct BufferedCanFd<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> {
534 _phantom: PhantomData<&'d ()>, 501 _phantom: PhantomData<&'d ()>,
535 info: &'static Info,
536 _mode: OperatingMode, 502 _mode: OperatingMode,
537 tx_buf: &'static TxFdBuf<TX_BUF_SIZE>, 503 tx_buf: &'static TxFdBuf<TX_BUF_SIZE>,
538 rx_buf: &'static RxFdBuf<RX_BUF_SIZE>, 504 rx_buf: &'static RxFdBuf<RX_BUF_SIZE>,
539 properties: Properties, 505 properties: Properties,
506 info: InfoRef,
540} 507}
541 508
542impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { 509impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> {
@@ -546,15 +513,13 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<'
546 tx_buf: &'static TxFdBuf<TX_BUF_SIZE>, 513 tx_buf: &'static TxFdBuf<TX_BUF_SIZE>,
547 rx_buf: &'static RxFdBuf<RX_BUF_SIZE>, 514 rx_buf: &'static RxFdBuf<RX_BUF_SIZE>,
548 ) -> Self { 515 ) -> Self {
549 (info.internal_operation)(InternalOperation::NotifySenderCreated);
550 (info.internal_operation)(InternalOperation::NotifyReceiverCreated);
551 BufferedCanFd { 516 BufferedCanFd {
552 _phantom: PhantomData, 517 _phantom: PhantomData,
553 info,
554 _mode, 518 _mode,
555 tx_buf, 519 tx_buf,
556 rx_buf, 520 rx_buf,
557 properties: Properties::new(info), 521 properties: Properties::new(info),
522 info: InfoRef::new(info),
558 } 523 }
559 .setup() 524 .setup()
560 } 525 }
@@ -593,36 +558,26 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<'
593 558
594 /// Returns a sender that can be used for sending CAN frames. 559 /// Returns a sender that can be used for sending CAN frames.
595 pub fn writer(&self) -> BufferedFdCanSender { 560 pub fn writer(&self) -> BufferedFdCanSender {
596 (self.info.internal_operation)(InternalOperation::NotifySenderCreated);
597 BufferedFdCanSender { 561 BufferedFdCanSender {
598 tx_buf: self.tx_buf.sender().into(), 562 tx_buf: self.tx_buf.sender().into(),
599 waker: self.info.tx_waker, 563 info: TxInfoRef::new(&self.info),
600 internal_operation: self.info.internal_operation,
601 } 564 }
602 } 565 }
603 566
604 /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. 567 /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
605 pub fn reader(&self) -> BufferedFdCanReceiver { 568 pub fn reader(&self) -> BufferedFdCanReceiver {
606 (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated);
607 BufferedFdCanReceiver { 569 BufferedFdCanReceiver {
608 rx_buf: self.rx_buf.receiver().into(), 570 rx_buf: self.rx_buf.receiver().into(),
609 internal_operation: self.info.internal_operation, 571 info: RxInfoRef::new(&self.info),
610 } 572 }
611 } 573 }
612} 574}
613 575
614impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Drop for BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> {
615 fn drop(&mut self) {
616 (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed);
617 (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed);
618 }
619}
620
621/// FDCAN Rx only Instance 576/// FDCAN Rx only Instance
622pub struct CanRx<'d> { 577pub struct CanRx<'d> {
623 _phantom: PhantomData<&'d ()>, 578 _phantom: PhantomData<&'d ()>,
624 info: &'static Info,
625 _mode: OperatingMode, 579 _mode: OperatingMode,
580 info: RxInfoRef,
626} 581}
627 582
628impl<'d> CanRx<'d> { 583impl<'d> CanRx<'d> {
@@ -637,18 +592,12 @@ impl<'d> CanRx<'d> {
637 } 592 }
638} 593}
639 594
640impl<'d> Drop for CanRx<'d> {
641 fn drop(&mut self) {
642 (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed);
643 }
644}
645
646/// FDCAN Tx only Instance 595/// FDCAN Tx only Instance
647pub struct CanTx<'d> { 596pub struct CanTx<'d> {
648 _phantom: PhantomData<&'d ()>, 597 _phantom: PhantomData<&'d ()>,
649 info: &'static Info,
650 config: crate::can::fd::config::FdCanConfig, 598 config: crate::can::fd::config::FdCanConfig,
651 _mode: OperatingMode, 599 _mode: OperatingMode,
600 info: TxInfoRef,
652} 601}
653 602
654impl<'c, 'd> CanTx<'d> { 603impl<'c, 'd> CanTx<'d> {
@@ -657,7 +606,7 @@ impl<'c, 'd> CanTx<'d> {
657 /// can be replaced, this call asynchronously waits for a frame to be successfully 606 /// can be replaced, this call asynchronously waits for a frame to be successfully
658 /// transmitted, then tries again. 607 /// transmitted, then tries again.
659 pub async fn write(&mut self, frame: &Frame) -> Option<Frame> { 608 pub async fn write(&mut self, frame: &Frame) -> Option<Frame> {
660 TxMode::write(self.info, frame).await 609 TxMode::write(&self.info, frame).await
661 } 610 }
662 611
663 /// Queues the message to be sent but exerts backpressure. If a lower-priority 612 /// Queues the message to be sent but exerts backpressure. If a lower-priority
@@ -665,13 +614,7 @@ impl<'c, 'd> CanTx<'d> {
665 /// can be replaced, this call asynchronously waits for a frame to be successfully 614 /// can be replaced, this call asynchronously waits for a frame to be successfully
666 /// transmitted, then tries again. 615 /// transmitted, then tries again.
667 pub async fn write_fd(&mut self, frame: &FdFrame) -> Option<FdFrame> { 616 pub async fn write_fd(&mut self, frame: &FdFrame) -> Option<FdFrame> {
668 TxMode::write_fd(self.info, frame).await 617 TxMode::write_fd(&self.info, frame).await
669 }
670}
671
672impl<'d> Drop for CanTx<'d> {
673 fn drop(&mut self) {
674 (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed);
675 } 618 }
676} 619}
677 620
@@ -938,21 +881,56 @@ impl State {
938} 881}
939 882
940type SharedState = embassy_sync::blocking_mutex::Mutex<CriticalSectionRawMutex, core::cell::RefCell<State>>; 883type SharedState = embassy_sync::blocking_mutex::Mutex<CriticalSectionRawMutex, core::cell::RefCell<State>>;
941struct Info { 884pub(crate) struct Info {
942 regs: Registers, 885 regs: Registers,
943 interrupt0: crate::interrupt::Interrupt, 886 interrupt0: crate::interrupt::Interrupt,
944 _interrupt1: crate::interrupt::Interrupt, 887 _interrupt1: crate::interrupt::Interrupt,
945 tx_waker: fn(), 888 pub(crate) tx_waker: fn(),
946 internal_operation: fn(InternalOperation),
947 state: SharedState, 889 state: SharedState,
948} 890}
949 891
892impl Info {
893 pub(crate) fn adjust_reference_counter(&self, val: RefCountOp) {
894 self.state.lock(|s| {
895 let mut mut_state = s.borrow_mut();
896 match val {
897 RefCountOp::NotifySenderCreated => {
898 mut_state.sender_instance_count += 1;
899 }
900 RefCountOp::NotifySenderDestroyed => {
901 mut_state.sender_instance_count -= 1;
902 if 0 == mut_state.sender_instance_count {
903 (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
904 }
905 }
906 RefCountOp::NotifyReceiverCreated => {
907 mut_state.receiver_instance_count += 1;
908 }
909 RefCountOp::NotifyReceiverDestroyed => {
910 mut_state.receiver_instance_count -= 1;
911 if 0 == mut_state.receiver_instance_count {
912 (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
913 }
914 }
915 }
916 if mut_state.sender_instance_count == 0 && mut_state.receiver_instance_count == 0 {
917 unsafe {
918 let tx_pin = crate::gpio::AnyPin::steal(mut_state.tx_pin_port.unwrap());
919 tx_pin.set_as_disconnected();
920 let rx_pin = crate::gpio::AnyPin::steal(mut_state.rx_pin_port.unwrap());
921 rx_pin.set_as_disconnected();
922 self.interrupt0.disable();
923 }
924 }
925 });
926 }
927}
928
950trait SealedInstance { 929trait SealedInstance {
951 const MSG_RAM_OFFSET: usize; 930 const MSG_RAM_OFFSET: usize;
952 931
953 fn info() -> &'static Info; 932 fn info() -> &'static Info;
954 fn registers() -> crate::can::fd::peripheral::Registers; 933 fn registers() -> crate::can::fd::peripheral::Registers;
955 fn internal_operation(val: InternalOperation);
956} 934}
957 935
958/// Instance trait 936/// Instance trait
@@ -974,41 +952,6 @@ macro_rules! impl_fdcan {
974 impl SealedInstance for peripherals::$inst { 952 impl SealedInstance for peripherals::$inst {
975 const MSG_RAM_OFFSET: usize = $msg_ram_offset; 953 const MSG_RAM_OFFSET: usize = $msg_ram_offset;
976 954
977 fn internal_operation(val: InternalOperation) {
978 peripherals::$inst::info().state.lock(|s| {
979 let mut mut_state = s.borrow_mut();
980 match val {
981 InternalOperation::NotifySenderCreated => {
982 mut_state.sender_instance_count += 1;
983 }
984 InternalOperation::NotifySenderDestroyed => {
985 mut_state.sender_instance_count -= 1;
986 if ( 0 == mut_state.sender_instance_count) {
987 (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
988 }
989 }
990 InternalOperation::NotifyReceiverCreated => {
991 mut_state.receiver_instance_count += 1;
992 }
993 InternalOperation::NotifyReceiverDestroyed => {
994 mut_state.receiver_instance_count -= 1;
995 if ( 0 == mut_state.receiver_instance_count) {
996 (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
997 }
998 }
999 }
1000 if mut_state.sender_instance_count == 0 && mut_state.receiver_instance_count == 0 {
1001 unsafe {
1002 let tx_pin = crate::gpio::AnyPin::steal(mut_state.tx_pin_port.unwrap());
1003 tx_pin.set_as_disconnected();
1004 let rx_pin = crate::gpio::AnyPin::steal(mut_state.rx_pin_port.unwrap());
1005 rx_pin.set_as_disconnected();
1006 rcc::disable::<peripherals::$inst>();
1007 }
1008 }
1009 });
1010 }
1011
1012 fn info() -> &'static Info { 955 fn info() -> &'static Info {
1013 956
1014 static INFO: Info = Info { 957 static INFO: Info = Info {
@@ -1016,7 +959,6 @@ macro_rules! impl_fdcan {
1016 interrupt0: crate::_generated::peripheral_interrupts::$inst::IT0::IRQ, 959 interrupt0: crate::_generated::peripheral_interrupts::$inst::IT0::IRQ,
1017 _interrupt1: crate::_generated::peripheral_interrupts::$inst::IT1::IRQ, 960 _interrupt1: crate::_generated::peripheral_interrupts::$inst::IT1::IRQ,
1018 tx_waker: crate::_generated::peripheral_interrupts::$inst::IT0::pend, 961 tx_waker: crate::_generated::peripheral_interrupts::$inst::IT0::pend,
1019 internal_operation: peripherals::$inst::internal_operation,
1020 state: embassy_sync::blocking_mutex::Mutex::new(core::cell::RefCell::new(State::new())), 962 state: embassy_sync::blocking_mutex::Mutex::new(core::cell::RefCell::new(State::new())),
1021 }; 963 };
1022 &INFO 964 &INFO
diff --git a/embassy-stm32/src/cordic/utils.rs b/embassy-stm32/src/cordic/utils.rs
index 008f50270..9afa8ef53 100644
--- a/embassy-stm32/src/cordic/utils.rs
+++ b/embassy-stm32/src/cordic/utils.rs
@@ -5,7 +5,7 @@ macro_rules! floating_fixed_convert {
5 ($f_to_q:ident, $q_to_f:ident, $unsigned_bin_typ:ty, $signed_bin_typ:ty, $float_ty:ty, $offset:literal, $min_positive:literal) => { 5 ($f_to_q:ident, $q_to_f:ident, $unsigned_bin_typ:ty, $signed_bin_typ:ty, $float_ty:ty, $offset:literal, $min_positive:literal) => {
6 /// convert float point to fixed point format 6 /// convert float point to fixed point format
7 pub fn $f_to_q(value: $float_ty) -> Result<$unsigned_bin_typ, NumberOutOfRange> { 7 pub fn $f_to_q(value: $float_ty) -> Result<$unsigned_bin_typ, NumberOutOfRange> {
8 const MIN_POSITIVE: $float_ty = unsafe { core::mem::transmute($min_positive) }; 8 const MIN_POSITIVE: $float_ty = <$float_ty>::from_bits($min_positive);
9 9
10 if value < -1.0 { 10 if value < -1.0 {
11 return Err(NumberOutOfRange::BelowLowerBound) 11 return Err(NumberOutOfRange::BelowLowerBound)
diff --git a/embassy-stm32/src/cryp/mod.rs b/embassy-stm32/src/cryp/mod.rs
index fba3c0fd7..35d9f8cce 100644
--- a/embassy-stm32/src/cryp/mod.rs
+++ b/embassy-stm32/src/cryp/mod.rs
@@ -2,7 +2,6 @@
2#[cfg(any(cryp_v2, cryp_v3, cryp_v4))] 2#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]
3use core::cmp::min; 3use core::cmp::min;
4use core::marker::PhantomData; 4use core::marker::PhantomData;
5use core::ptr;
6 5
7use embassy_hal_internal::{Peri, PeripheralType}; 6use embassy_hal_internal::{Peri, PeripheralType};
8use embassy_sync::waitqueue::AtomicWaker; 7use embassy_sync::waitqueue::AtomicWaker;
@@ -1814,14 +1813,12 @@ impl<'d, T: Instance> Cryp<'d, T, Async> {
1814 assert_eq!(blocks.len() % block_size, 0); 1813 assert_eq!(blocks.len() % block_size, 0);
1815 // Configure DMA to transfer input to crypto core. 1814 // Configure DMA to transfer input to crypto core.
1816 let dst_ptr: *mut u32 = T::regs().din().as_ptr(); 1815 let dst_ptr: *mut u32 = T::regs().din().as_ptr();
1817 let num_words = blocks.len() / 4;
1818 let src_ptr: *const [u8] = ptr::slice_from_raw_parts(blocks.as_ptr().cast(), num_words);
1819 let options = TransferOptions { 1816 let options = TransferOptions {
1820 #[cfg(not(gpdma))] 1817 #[cfg(not(gpdma))]
1821 priority: crate::dma::Priority::High, 1818 priority: crate::dma::Priority::High,
1822 ..Default::default() 1819 ..Default::default()
1823 }; 1820 };
1824 let dma_transfer = unsafe { dma.write_raw(src_ptr, dst_ptr, options) }; 1821 let dma_transfer = unsafe { dma.write_raw(blocks, dst_ptr, options) };
1825 T::regs().dmacr().modify(|w| w.set_dien(true)); 1822 T::regs().dmacr().modify(|w| w.set_dien(true));
1826 // Wait for the transfer to complete. 1823 // Wait for the transfer to complete.
1827 dma_transfer.await; 1824 dma_transfer.await;
@@ -1836,14 +1833,12 @@ impl<'d, T: Instance> Cryp<'d, T, Async> {
1836 assert_eq!((blocks.len() * 4) % block_size, 0); 1833 assert_eq!((blocks.len() * 4) % block_size, 0);
1837 // Configure DMA to transfer input to crypto core. 1834 // Configure DMA to transfer input to crypto core.
1838 let dst_ptr: *mut u32 = T::regs().din().as_ptr(); 1835 let dst_ptr: *mut u32 = T::regs().din().as_ptr();
1839 let num_words = blocks.len();
1840 let src_ptr: *const [u32] = ptr::slice_from_raw_parts(blocks.as_ptr().cast(), num_words);
1841 let options = TransferOptions { 1836 let options = TransferOptions {
1842 #[cfg(not(gpdma))] 1837 #[cfg(not(gpdma))]
1843 priority: crate::dma::Priority::High, 1838 priority: crate::dma::Priority::High,
1844 ..Default::default() 1839 ..Default::default()
1845 }; 1840 };
1846 let dma_transfer = unsafe { dma.write_raw(src_ptr, dst_ptr, options) }; 1841 let dma_transfer = unsafe { dma.write_raw(blocks, dst_ptr, options) };
1847 T::regs().dmacr().modify(|w| w.set_dien(true)); 1842 T::regs().dmacr().modify(|w| w.set_dien(true));
1848 // Wait for the transfer to complete. 1843 // Wait for the transfer to complete.
1849 dma_transfer.await; 1844 dma_transfer.await;
@@ -1857,14 +1852,12 @@ impl<'d, T: Instance> Cryp<'d, T, Async> {
1857 assert_eq!(blocks.len() % block_size, 0); 1852 assert_eq!(blocks.len() % block_size, 0);
1858 // Configure DMA to get output from crypto core. 1853 // Configure DMA to get output from crypto core.
1859 let src_ptr = T::regs().dout().as_ptr(); 1854 let src_ptr = T::regs().dout().as_ptr();
1860 let num_words = blocks.len() / 4;
1861 let dst_ptr = ptr::slice_from_raw_parts_mut(blocks.as_mut_ptr().cast(), num_words);
1862 let options = TransferOptions { 1855 let options = TransferOptions {
1863 #[cfg(not(gpdma))] 1856 #[cfg(not(gpdma))]
1864 priority: crate::dma::Priority::VeryHigh, 1857 priority: crate::dma::Priority::VeryHigh,
1865 ..Default::default() 1858 ..Default::default()
1866 }; 1859 };
1867 let dma_transfer = unsafe { dma.read_raw(src_ptr, dst_ptr, options) }; 1860 let dma_transfer = unsafe { dma.read_raw(src_ptr, blocks, options) };
1868 T::regs().dmacr().modify(|w| w.set_doen(true)); 1861 T::regs().dmacr().modify(|w| w.set_doen(true));
1869 // Wait for the transfer to complete. 1862 // Wait for the transfer to complete.
1870 dma_transfer.await; 1863 dma_transfer.await;
diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs
index 30046849b..d8f1f96f2 100644
--- a/embassy-stm32/src/dac/mod.rs
+++ b/embassy-stm32/src/dac/mod.rs
@@ -403,6 +403,46 @@ impl<'d, T: Instance> Dac<'d, T, Async> {
403 Mode::NormalExternalBuffered, 403 Mode::NormalExternalBuffered,
404 ) 404 )
405 } 405 }
406 /// Create a new `Dac` instance with external output pins and unbuffered mode.
407 ///
408 /// This function consumes the underlying DAC peripheral and allows access to both channels.
409 /// The channels are configured for external output with the buffer disabled.
410 ///
411 /// The channels are enabled on creation and begin to drive their output pins.
412 /// Note that some methods, such as `set_trigger()` and `set_mode()`, will
413 /// disable the channel; you must re-enable them with `enable()`.
414 ///
415 /// By default, triggering is disabled, but it can be enabled using the `set_trigger()`
416 /// method on the underlying channels.
417 ///
418 /// # Arguments
419 ///
420 /// * `peri` - The DAC peripheral instance.
421 /// * `dma_ch1` - The DMA channel for DAC channel 1.
422 /// * `dma_ch2` - The DMA channel for DAC channel 2.
423 /// * `pin_ch1` - The GPIO pin for DAC channel 1 output.
424 /// * `pin_ch2` - The GPIO pin for DAC channel 2 output.
425 ///
426 /// # Returns
427 ///
428 /// A new `Dac` instance in unbuffered mode.
429 pub fn new_unbuffered(
430 peri: Peri<'d, T>,
431 dma_ch1: Peri<'d, impl Dma<T, Ch1>>,
432 dma_ch2: Peri<'d, impl Dma<T, Ch2>>,
433 pin_ch1: Peri<'d, impl DacPin<T, Ch1> + crate::gpio::Pin>,
434 pin_ch2: Peri<'d, impl DacPin<T, Ch2> + crate::gpio::Pin>,
435 ) -> Self {
436 pin_ch1.set_as_analog();
437 pin_ch2.set_as_analog();
438 Self::new_inner(
439 peri,
440 new_dma!(dma_ch1),
441 new_dma!(dma_ch2),
442 #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
443 Mode::NormalExternalUnbuffered,
444 )
445 }
406 446
407 /// Create a new `Dac` instance where the external output pins are not used, 447 /// Create a new `Dac` instance where the external output pins are not used,
408 /// so the DAC can only be used to generate internal signals but the GPIO 448 /// so the DAC can only be used to generate internal signals but the GPIO
diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs
index 7dbbe7b72..464823bfc 100644
--- a/embassy-stm32/src/dma/dma_bdma.rs
+++ b/embassy-stm32/src/dma/dma_bdma.rs
@@ -341,9 +341,12 @@ impl AnyChannel {
341 mem_len: usize, 341 mem_len: usize,
342 incr_mem: bool, 342 incr_mem: bool,
343 mem_size: WordSize, 343 mem_size: WordSize,
344 peripheral_size: WordSize, 344 peri_size: WordSize,
345 options: TransferOptions, 345 options: TransferOptions,
346 ) { 346 ) {
347 // "Preceding reads and writes cannot be moved past subsequent writes."
348 fence(Ordering::SeqCst);
349
347 let info = self.info(); 350 let info = self.info();
348 #[cfg(feature = "_dual-core")] 351 #[cfg(feature = "_dual-core")]
349 { 352 {
@@ -354,28 +357,48 @@ impl AnyChannel {
354 #[cfg(dmamux)] 357 #[cfg(dmamux)]
355 super::dmamux::configure_dmamux(&info.dmamux, _request); 358 super::dmamux::configure_dmamux(&info.dmamux, _request);
356 359
357 assert!(mem_len > 0 && mem_len <= 0xFFFF);
358
359 match self.info().dma { 360 match self.info().dma {
360 #[cfg(dma)] 361 #[cfg(dma)]
361 DmaInfo::Dma(r) => { 362 DmaInfo::Dma(r) => {
362 let state: &ChannelState = &STATE[self.id as usize]; 363 let state: &ChannelState = &STATE[self.id as usize];
363 let ch = r.st(info.num); 364 let ch = r.st(info.num);
364 365
365 // "Preceding reads and writes cannot be moved past subsequent writes."
366 fence(Ordering::SeqCst);
367
368 state.complete_count.store(0, Ordering::Release); 366 state.complete_count.store(0, Ordering::Release);
369 self.clear_irqs(); 367 self.clear_irqs();
370 368
369 // NDTR is the number of transfers in the *peripheral* word size.
370 // ex: if mem_size=1, peri_size=4 and ndtr=3 it'll do 12 mem transfers, 3 peri transfers.
371 let ndtr = match (mem_size, peri_size) {
372 (WordSize::FourBytes, WordSize::OneByte) => mem_len * 4,
373 (WordSize::FourBytes, WordSize::TwoBytes) | (WordSize::TwoBytes, WordSize::OneByte) => mem_len * 2,
374 (WordSize::FourBytes, WordSize::FourBytes)
375 | (WordSize::TwoBytes, WordSize::TwoBytes)
376 | (WordSize::OneByte, WordSize::OneByte) => mem_len,
377 (WordSize::TwoBytes, WordSize::FourBytes) | (WordSize::OneByte, WordSize::TwoBytes) => {
378 assert!(mem_len % 2 == 0);
379 mem_len / 2
380 }
381 (WordSize::OneByte, WordSize::FourBytes) => {
382 assert!(mem_len % 4 == 0);
383 mem_len / 4
384 }
385 };
386
387 assert!(ndtr > 0 && ndtr <= 0xFFFF);
388
371 ch.par().write_value(peri_addr as u32); 389 ch.par().write_value(peri_addr as u32);
372 ch.m0ar().write_value(mem_addr as u32); 390 ch.m0ar().write_value(mem_addr as u32);
373 ch.ndtr().write_value(pac::dma::regs::Ndtr(mem_len as _)); 391 ch.ndtr().write_value(pac::dma::regs::Ndtr(ndtr as _));
374 ch.fcr().write(|w| { 392 ch.fcr().write(|w| {
375 if let Some(fth) = options.fifo_threshold { 393 if let Some(fth) = options.fifo_threshold {
376 // FIFO mode 394 // FIFO mode
377 w.set_dmdis(pac::dma::vals::Dmdis::DISABLED); 395 w.set_dmdis(pac::dma::vals::Dmdis::DISABLED);
378 w.set_fth(fth.into()); 396 w.set_fth(fth.into());
397 } else if mem_size != peri_size {
398 // force FIFO mode if msize != psize
399 // packing/unpacking doesn't work in direct mode.
400 w.set_dmdis(pac::dma::vals::Dmdis::DISABLED);
401 w.set_fth(FifoThreshold::Half.into());
379 } else { 402 } else {
380 // Direct mode 403 // Direct mode
381 w.set_dmdis(pac::dma::vals::Dmdis::ENABLED); 404 w.set_dmdis(pac::dma::vals::Dmdis::ENABLED);
@@ -384,7 +407,7 @@ impl AnyChannel {
384 ch.cr().write(|w| { 407 ch.cr().write(|w| {
385 w.set_dir(dir.into()); 408 w.set_dir(dir.into());
386 w.set_msize(mem_size.into()); 409 w.set_msize(mem_size.into());
387 w.set_psize(peripheral_size.into()); 410 w.set_psize(peri_size.into());
388 w.set_pl(options.priority.into()); 411 w.set_pl(options.priority.into());
389 w.set_minc(incr_mem); 412 w.set_minc(incr_mem);
390 w.set_pinc(false); 413 w.set_pinc(false);
@@ -404,6 +427,8 @@ impl AnyChannel {
404 } 427 }
405 #[cfg(bdma)] 428 #[cfg(bdma)]
406 DmaInfo::Bdma(r) => { 429 DmaInfo::Bdma(r) => {
430 assert!(mem_len > 0 && mem_len <= 0xFFFF);
431
407 #[cfg(bdma_v2)] 432 #[cfg(bdma_v2)]
408 critical_section::with(|_| r.cselr().modify(|w| w.set_cs(info.num, _request))); 433 critical_section::with(|_| r.cselr().modify(|w| w.set_cs(info.num, _request)));
409 434
@@ -417,7 +442,7 @@ impl AnyChannel {
417 ch.mar().write_value(mem_addr as u32); 442 ch.mar().write_value(mem_addr as u32);
418 ch.ndtr().write(|w| w.set_ndt(mem_len as u16)); 443 ch.ndtr().write(|w| w.set_ndt(mem_len as u16));
419 ch.cr().write(|w| { 444 ch.cr().write(|w| {
420 w.set_psize(peripheral_size.into()); 445 w.set_psize(peri_size.into());
421 w.set_msize(mem_size.into()); 446 w.set_msize(mem_size.into());
422 w.set_minc(incr_mem); 447 w.set_minc(incr_mem);
423 w.set_dir(dir.into()); 448 w.set_dir(dir.into());
@@ -587,11 +612,11 @@ impl<'a> Transfer<'a> {
587 } 612 }
588 613
589 /// Create a new read DMA transfer (peripheral to memory), using raw pointers. 614 /// Create a new read DMA transfer (peripheral to memory), using raw pointers.
590 pub unsafe fn new_read_raw<W: Word>( 615 pub unsafe fn new_read_raw<MW: Word, PW: Word>(
591 channel: Peri<'a, impl Channel>, 616 channel: Peri<'a, impl Channel>,
592 request: Request, 617 request: Request,
593 peri_addr: *mut W, 618 peri_addr: *mut PW,
594 buf: *mut [W], 619 buf: *mut [MW],
595 options: TransferOptions, 620 options: TransferOptions,
596 ) -> Self { 621 ) -> Self {
597 Self::new_inner( 622 Self::new_inner(
@@ -599,11 +624,11 @@ impl<'a> Transfer<'a> {
599 request, 624 request,
600 Dir::PeripheralToMemory, 625 Dir::PeripheralToMemory,
601 peri_addr as *const u32, 626 peri_addr as *const u32,
602 buf as *mut W as *mut u32, 627 buf as *mut MW as *mut u32,
603 buf.len(), 628 buf.len(),
604 true, 629 true,
605 W::size(), 630 MW::size(),
606 W::size(), 631 PW::size(),
607 options, 632 options,
608 ) 633 )
609 } 634 }
@@ -672,22 +697,14 @@ impl<'a> Transfer<'a> {
672 mem_addr: *mut u32, 697 mem_addr: *mut u32,
673 mem_len: usize, 698 mem_len: usize,
674 incr_mem: bool, 699 incr_mem: bool,
675 data_size: WordSize, 700 mem_size: WordSize,
676 peripheral_size: WordSize, 701 peri_size: WordSize,
677 options: TransferOptions, 702 options: TransferOptions,
678 ) -> Self { 703 ) -> Self {
679 assert!(mem_len > 0 && mem_len <= 0xFFFF); 704 assert!(mem_len > 0 && mem_len <= 0xFFFF);
680 705
681 channel.configure( 706 channel.configure(
682 _request, 707 _request, dir, peri_addr, mem_addr, mem_len, incr_mem, mem_size, peri_size, options,
683 dir,
684 peri_addr,
685 mem_addr,
686 mem_len,
687 incr_mem,
688 data_size,
689 peripheral_size,
690 options,
691 ); 708 );
692 channel.start(); 709 channel.start();
693 Self { channel } 710 Self { channel }
diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs
index ade70fb55..151e4ab9f 100644
--- a/embassy-stm32/src/dma/gpdma.rs
+++ b/embassy-stm32/src/dma/gpdma.rs
@@ -125,11 +125,11 @@ impl<'a> Transfer<'a> {
125 } 125 }
126 126
127 /// Create a new read DMA transfer (peripheral to memory), using raw pointers. 127 /// Create a new read DMA transfer (peripheral to memory), using raw pointers.
128 pub unsafe fn new_read_raw<W: Word>( 128 pub unsafe fn new_read_raw<MW: Word, PW: Word>(
129 channel: Peri<'a, impl Channel>, 129 channel: Peri<'a, impl Channel>,
130 request: Request, 130 request: Request,
131 peri_addr: *mut W, 131 peri_addr: *mut PW,
132 buf: *mut [W], 132 buf: *mut [MW],
133 options: TransferOptions, 133 options: TransferOptions,
134 ) -> Self { 134 ) -> Self {
135 Self::new_inner( 135 Self::new_inner(
@@ -137,11 +137,11 @@ impl<'a> Transfer<'a> {
137 request, 137 request,
138 Dir::PeripheralToMemory, 138 Dir::PeripheralToMemory,
139 peri_addr as *const u32, 139 peri_addr as *const u32,
140 buf as *mut W as *mut u32, 140 buf as *mut MW as *mut u32,
141 buf.len(), 141 buf.len(),
142 true, 142 true,
143 W::size(), 143 PW::size(),
144 W::size(), 144 MW::size(),
145 options, 145 options,
146 ) 146 )
147 } 147 }
diff --git a/embassy-stm32/src/dma/util.rs b/embassy-stm32/src/dma/util.rs
index 8bf89e2fe..3245887c1 100644
--- a/embassy-stm32/src/dma/util.rs
+++ b/embassy-stm32/src/dma/util.rs
@@ -20,10 +20,10 @@ impl<'d> ChannelAndRequest<'d> {
20 Transfer::new_read(self.channel.reborrow(), self.request, peri_addr, buf, options) 20 Transfer::new_read(self.channel.reborrow(), self.request, peri_addr, buf, options)
21 } 21 }
22 22
23 pub unsafe fn read_raw<'a, W: Word>( 23 pub unsafe fn read_raw<'a, MW: Word, PW: Word>(
24 &'a mut self, 24 &'a mut self,
25 peri_addr: *mut W, 25 peri_addr: *mut PW,
26 buf: *mut [W], 26 buf: *mut [MW],
27 options: TransferOptions, 27 options: TransferOptions,
28 ) -> Transfer<'a> { 28 ) -> Transfer<'a> {
29 Transfer::new_read_raw(self.channel.reborrow(), self.request, peri_addr, buf, options) 29 Transfer::new_read_raw(self.channel.reborrow(), self.request, peri_addr, buf, options)
diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs
index 01e321bce..b9746231f 100644
--- a/embassy-stm32/src/eth/v1/mod.rs
+++ b/embassy-stm32/src/eth/v1/mod.rs
@@ -122,7 +122,10 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
122 122
123 // Select RMII (Reduced Media Independent Interface) 123 // Select RMII (Reduced Media Independent Interface)
124 // Must be done prior to enabling peripheral clock 124 // Must be done prior to enabling peripheral clock
125 AFIO.mapr().modify(|w| w.set_mii_rmii_sel(true)); 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 });
126 129
127 RCC.ahbenr().modify(|w| { 130 RCC.ahbenr().modify(|w| {
128 w.set_ethen(true); 131 w.set_ethen(true);
@@ -316,7 +319,10 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
316 319
317 // Select MII (Media Independent Interface) 320 // Select MII (Media Independent Interface)
318 // Must be done prior to enabling peripheral clock 321 // Must be done prior to enabling peripheral clock
319 AFIO.mapr().modify(|w| w.set_mii_rmii_sel(false)); 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 });
320 326
321 RCC.ahbenr().modify(|w| { 327 RCC.ahbenr().modify(|w| {
322 w.set_ethen(true); 328 w.set_ethen(true);
diff --git a/embassy-stm32/src/flash/u5.rs b/embassy-stm32/src/flash/u5.rs
index 131caa195..6c3d4b422 100644
--- a/embassy-stm32/src/flash/u5.rs
+++ b/embassy-stm32/src/flash/u5.rs
@@ -30,19 +30,19 @@ pub(crate) unsafe fn enable_blocking_write() {
30 30
31 #[cfg(feature = "trustzone-secure")] 31 #[cfg(feature = "trustzone-secure")]
32 pac::FLASH.seccr().write(|w| { 32 pac::FLASH.seccr().write(|w| {
33 w.set_pg(pac::flash::vals::SeccrPg::B_0X1); 33 w.set_pg(true);
34 }); 34 });
35 #[cfg(not(feature = "trustzone-secure"))] 35 #[cfg(not(feature = "trustzone-secure"))]
36 pac::FLASH.nscr().write(|w| { 36 pac::FLASH.nscr().write(|w| {
37 w.set_pg(pac::flash::vals::NscrPg::B_0X1); 37 w.set_pg(true);
38 }); 38 });
39} 39}
40 40
41pub(crate) unsafe fn disable_blocking_write() { 41pub(crate) unsafe fn disable_blocking_write() {
42 #[cfg(feature = "trustzone-secure")] 42 #[cfg(feature = "trustzone-secure")]
43 pac::FLASH.seccr().write(|w| w.set_pg(pac::flash::vals::SeccrPg::B_0X0)); 43 pac::FLASH.seccr().write(|w| w.set_pg(false));
44 #[cfg(not(feature = "trustzone-secure"))] 44 #[cfg(not(feature = "trustzone-secure"))]
45 pac::FLASH.nscr().write(|w| w.set_pg(pac::flash::vals::NscrPg::B_0X0)); 45 pac::FLASH.nscr().write(|w| w.set_pg(false));
46} 46}
47 47
48pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { 48pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
@@ -65,19 +65,19 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E
65 w.set_pnb(sector.index_in_bank); 65 w.set_pnb(sector.index_in_bank);
66 // TODO: add check for bank swap 66 // TODO: add check for bank swap
67 w.set_bker(match sector.bank { 67 w.set_bker(match sector.bank {
68 FlashBank::Bank1 => pac::flash::vals::SeccrBker::B_0X0, 68 FlashBank::Bank1 => false,
69 FlashBank::Bank2 => pac::flash::vals::SeccrBker::B_0X1, 69 FlashBank::Bank2 => true,
70 _ => unreachable!(), 70 _ => unreachable!(),
71 }); 71 });
72 }); 72 });
73 #[cfg(not(feature = "trustzone-secure"))] 73 #[cfg(not(feature = "trustzone-secure"))]
74 pac::FLASH.nscr().modify(|w| { 74 pac::FLASH.nscr().modify(|w| {
75 w.set_per(pac::flash::vals::NscrPer::B_0X1); 75 w.set_per(true);
76 w.set_pnb(sector.index_in_bank); 76 w.set_pnb(sector.index_in_bank);
77 // TODO: add check for bank swap 77 // TODO: add check for bank swap
78 w.set_bker(match sector.bank { 78 w.set_bker(match sector.bank {
79 FlashBank::Bank1 => pac::flash::vals::NscrBker::B_0X0, 79 FlashBank::Bank1 => false,
80 FlashBank::Bank2 => pac::flash::vals::NscrBker::B_0X1, 80 FlashBank::Bank2 => true,
81 _ => unreachable!(), 81 _ => unreachable!(),
82 }); 82 });
83 }); 83 });
@@ -93,13 +93,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E
93 93
94 let ret: Result<(), Error> = blocking_wait_ready(); 94 let ret: Result<(), Error> = blocking_wait_ready();
95 #[cfg(feature = "trustzone-secure")] 95 #[cfg(feature = "trustzone-secure")]
96 pac::FLASH 96 pac::FLASH.seccr().modify(|w| w.set_per(false));
97 .seccr()
98 .modify(|w| w.set_per(pac::flash::vals::SeccrPer::B_0X0));
99 #[cfg(not(feature = "trustzone-secure"))] 97 #[cfg(not(feature = "trustzone-secure"))]
100 pac::FLASH 98 pac::FLASH.nscr().modify(|w| w.set_per(false));
101 .nscr()
102 .modify(|w| w.set_per(pac::flash::vals::NscrPer::B_0X0));
103 clear_all_err(); 99 clear_all_err();
104 ret 100 ret
105} 101}
diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs
index 31527bcdb..573a1851d 100644
--- a/embassy-stm32/src/hsem/mod.rs
+++ b/embassy-stm32/src/hsem/mod.rs
@@ -3,7 +3,7 @@
3use embassy_hal_internal::PeripheralType; 3use embassy_hal_internal::PeripheralType;
4 4
5use crate::pac; 5use crate::pac;
6use crate::rcc::RccPeripheral; 6use crate::rcc::{self, RccPeripheral};
7// TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. 7// TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs.
8// Those MCUs have a different HSEM implementation (Secure semaphore lock support, 8// Those MCUs have a different HSEM implementation (Secure semaphore lock support,
9// Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), 9// Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute),
@@ -46,7 +46,7 @@ pub enum CoreId {
46#[inline(always)] 46#[inline(always)]
47pub fn get_current_coreid() -> CoreId { 47pub fn get_current_coreid() -> CoreId {
48 let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() }; 48 let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() };
49 match cpuid & 0x000000F0 { 49 match (cpuid & 0x000000F0) >> 4 {
50 #[cfg(any(stm32wb, stm32wl))] 50 #[cfg(any(stm32wb, stm32wl))]
51 0x0 => CoreId::Core1, 51 0x0 => CoreId::Core1,
52 52
@@ -80,6 +80,8 @@ pub struct HardwareSemaphore<'d, T: Instance> {
80impl<'d, T: Instance> HardwareSemaphore<'d, T> { 80impl<'d, T: Instance> HardwareSemaphore<'d, T> {
81 /// Creates a new HardwareSemaphore instance. 81 /// Creates a new HardwareSemaphore instance.
82 pub fn new(peripheral: Peri<'d, T>) -> Self { 82 pub fn new(peripheral: Peri<'d, T>) -> Self {
83 rcc::enable_and_reset::<T>();
84
83 HardwareSemaphore { _peri: peripheral } 85 HardwareSemaphore { _peri: peripheral }
84 } 86 }
85 87
diff --git a/embassy-stm32/src/i2c/config.rs b/embassy-stm32/src/i2c/config.rs
new file mode 100644
index 000000000..daae43bcd
--- /dev/null
+++ b/embassy-stm32/src/i2c/config.rs
@@ -0,0 +1,170 @@
1#[cfg(gpio_v2)]
2use crate::gpio::Pull;
3use crate::gpio::{AfType, OutputType, Speed};
4
5#[repr(u8)]
6#[derive(Copy, Clone)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8/// Bits of the I2C OA2 register to mask out.
9pub enum AddrMask {
10 /// No mask
11 NOMASK,
12 /// OA2\[1\] is masked and don’t care. Only OA2\[7:2\] are compared.
13 MASK1,
14 /// OA2\[2:1\] are masked and don’t care. Only OA2\[7:3\] are compared.
15 MASK2,
16 /// OA2\[3:1\] are masked and don’t care. Only OA2\[7:4\] are compared.
17 MASK3,
18 /// OA2\[4:1\] are masked and don’t care. Only OA2\[7:5\] are compared.
19 MASK4,
20 /// OA2\[5:1\] are masked and don’t care. Only OA2\[7:6\] are compared.
21 MASK5,
22 /// OA2\[6:1\] are masked and don’t care. Only OA2\[7:6\] are compared.
23 MASK6,
24 /// OA2\[7:1\] are masked and don’t care. No comparison is done, and all (except reserved) 7-bit received addresses are acknowledged
25 MASK7,
26}
27
28#[derive(Debug, Copy, Clone, PartialEq, Eq)]
29#[cfg_attr(feature = "defmt", derive(defmt::Format))]
30/// An I2C address. Either 7 or 10 bit.
31pub enum Address {
32 /// A 7 bit address
33 SevenBit(u8),
34 /// A 10 bit address.
35 ///
36 /// When using an address to configure the Own Address, only the OA1 register can be set to a 10-bit address.
37 TenBit(u16),
38}
39impl From<u8> for Address {
40 fn from(value: u8) -> Self {
41 Address::SevenBit(value)
42 }
43}
44impl From<u16> for Address {
45 fn from(value: u16) -> Self {
46 assert!(value < 0x400, "Ten bit address must be less than 0x400");
47 Address::TenBit(value)
48 }
49}
50impl Address {
51 /// Get the inner address as a u16.
52 ///
53 /// For 7 bit addresses, the u8 that was used to store the address is returned as a u16.
54 pub fn addr(&self) -> u16 {
55 match self {
56 Address::SevenBit(addr) => *addr as u16,
57 Address::TenBit(addr) => *addr,
58 }
59 }
60}
61
62#[derive(Copy, Clone)]
63#[cfg_attr(feature = "defmt", derive(defmt::Format))]
64/// The second Own Address register.
65pub struct OA2 {
66 /// The address.
67 pub addr: u8,
68 /// The bit mask that will affect how the own address 2 register is compared.
69 pub mask: AddrMask,
70}
71
72#[derive(Copy, Clone)]
73#[cfg_attr(feature = "defmt", derive(defmt::Format))]
74/// The Own Address(es) of the I2C peripheral.
75pub enum OwnAddresses {
76 /// Configuration for only the OA1 register.
77 OA1(Address),
78 /// Configuration for only the OA2 register.
79 OA2(OA2),
80 /// Configuration for both the OA1 and OA2 registers.
81 Both {
82 /// The [Address] for the OA1 register.
83 oa1: Address,
84 /// The [OA2] configuration.
85 oa2: OA2,
86 },
87}
88
89/// Slave Configuration
90#[derive(Copy, Clone)]
91#[cfg_attr(feature = "defmt", derive(defmt::Format))]
92pub struct SlaveAddrConfig {
93 /// Target Address(es)
94 pub addr: OwnAddresses,
95 /// Control if the peripheral should respond to the general call address
96 pub general_call: bool,
97}
98impl SlaveAddrConfig {
99 /// Create a new slave address configuration with only the OA1 register set in 7 bit mode and the general call disabled.
100 pub fn basic(addr: u8) -> Self {
101 Self {
102 addr: OwnAddresses::OA1(Address::SevenBit(addr)),
103 general_call: false,
104 }
105 }
106}
107
108/// I2C config
109#[non_exhaustive]
110#[derive(Copy, Clone)]
111pub struct Config {
112 /// Enable internal pullup on SDA.
113 ///
114 /// Using external pullup resistors is recommended for I2C. If you do
115 /// have external pullups you should not enable this.
116 #[cfg(gpio_v2)]
117 pub sda_pullup: bool,
118 /// Enable internal pullup on SCL.
119 ///
120 /// Using external pullup resistors is recommended for I2C. If you do
121 /// have external pullups you should not enable this.
122 #[cfg(gpio_v2)]
123 pub scl_pullup: bool,
124 /// Timeout.
125 #[cfg(feature = "time")]
126 pub timeout: embassy_time::Duration,
127}
128
129impl Default for Config {
130 fn default() -> Self {
131 Self {
132 #[cfg(gpio_v2)]
133 sda_pullup: false,
134 #[cfg(gpio_v2)]
135 scl_pullup: false,
136 #[cfg(feature = "time")]
137 timeout: embassy_time::Duration::from_millis(1000),
138 }
139 }
140}
141
142impl Config {
143 pub(super) fn scl_af(&self) -> AfType {
144 #[cfg(gpio_v1)]
145 return AfType::output(OutputType::OpenDrain, Speed::Medium);
146 #[cfg(gpio_v2)]
147 return AfType::output_pull(
148 OutputType::OpenDrain,
149 Speed::Medium,
150 match self.scl_pullup {
151 true => Pull::Up,
152 false => Pull::Down,
153 },
154 );
155 }
156
157 pub(super) fn sda_af(&self) -> AfType {
158 #[cfg(gpio_v1)]
159 return AfType::output(OutputType::OpenDrain, Speed::Medium);
160 #[cfg(gpio_v2)]
161 return AfType::output_pull(
162 OutputType::OpenDrain,
163 Speed::Medium,
164 match self.sda_pullup {
165 true => Pull::Up,
166 false => Pull::Down,
167 },
168 );
169 }
170}
diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs
index 1689fdb84..825dd240c 100644
--- a/embassy-stm32/src/i2c/mod.rs
+++ b/embassy-stm32/src/i2c/mod.rs
@@ -5,19 +5,22 @@
5#[cfg_attr(any(i2c_v2, i2c_v3), path = "v2.rs")] 5#[cfg_attr(any(i2c_v2, i2c_v3), path = "v2.rs")]
6mod _version; 6mod _version;
7 7
8mod config;
9
8use core::future::Future; 10use core::future::Future;
9use core::iter; 11use core::iter;
10use core::marker::PhantomData; 12use core::marker::PhantomData;
11 13
14pub use config::*;
12use embassy_hal_internal::Peri; 15use embassy_hal_internal::Peri;
13use embassy_sync::waitqueue::AtomicWaker; 16use embassy_sync::waitqueue::AtomicWaker;
14#[cfg(feature = "time")] 17#[cfg(feature = "time")]
15use embassy_time::{Duration, Instant}; 18use embassy_time::{Duration, Instant};
19use mode::MasterMode;
20pub use mode::{Master, MultiMaster};
16 21
17use crate::dma::ChannelAndRequest; 22use crate::dma::ChannelAndRequest;
18#[cfg(gpio_v2)] 23use crate::gpio::{AnyPin, SealedPin as _};
19use crate::gpio::Pull;
20use crate::gpio::{AfType, AnyPin, OutputType, SealedPin as _, Speed};
21use crate::interrupt::typelevel::Interrupt; 24use crate::interrupt::typelevel::Interrupt;
22use crate::mode::{Async, Blocking, Mode}; 25use crate::mode::{Async, Blocking, Mode};
23use crate::rcc::{RccInfo, SealedRccPeripheral}; 26use crate::rcc::{RccInfo, SealedRccPeripheral};
@@ -62,85 +65,89 @@ impl core::fmt::Display for Error {
62 65
63impl core::error::Error for Error {} 66impl core::error::Error for Error {}
64 67
65/// I2C config 68/// I2C modes
66#[non_exhaustive] 69pub mod mode {
67#[derive(Copy, Clone)] 70 trait SealedMode {}
68pub struct Config { 71
69 /// Enable internal pullup on SDA. 72 /// Trait for I2C master operations.
70 /// 73 #[allow(private_bounds)]
71 /// Using external pullup resistors is recommended for I2C. If you do 74 pub trait MasterMode: SealedMode {}
72 /// have external pullups you should not enable this. 75
73 #[cfg(gpio_v2)] 76 /// Mode allowing for I2C master operations.
74 pub sda_pullup: bool, 77 pub struct Master;
75 /// Enable internal pullup on SCL. 78 /// Mode allowing for I2C master and slave operations.
76 /// 79 pub struct MultiMaster;
77 /// Using external pullup resistors is recommended for I2C. If you do 80
78 /// have external pullups you should not enable this. 81 impl SealedMode for Master {}
79 #[cfg(gpio_v2)] 82 impl MasterMode for Master {}
80 pub scl_pullup: bool, 83
81 /// Timeout. 84 impl SealedMode for MultiMaster {}
82 #[cfg(feature = "time")] 85 impl MasterMode for MultiMaster {}
83 pub timeout: embassy_time::Duration,
84} 86}
85 87
86impl Default for Config { 88#[derive(Debug, Clone, PartialEq, Eq)]
87 fn default() -> Self { 89#[cfg_attr(feature = "defmt", derive(defmt::Format))]
88 Self { 90/// The command kind to the slave from the master
89 #[cfg(gpio_v2)] 91pub enum SlaveCommandKind {
90 sda_pullup: false, 92 /// Write to the slave
91 #[cfg(gpio_v2)] 93 Write,
92 scl_pullup: false, 94 /// Read from the slave
93 #[cfg(feature = "time")] 95 Read,
94 timeout: embassy_time::Duration::from_millis(1000),
95 }
96 }
97} 96}
98 97
99impl Config { 98#[derive(Debug, Clone, PartialEq, Eq)]
100 fn scl_af(&self) -> AfType { 99#[cfg_attr(feature = "defmt", derive(defmt::Format))]
101 #[cfg(gpio_v1)] 100/// The command kind to the slave from the master and the address that the slave matched
102 return AfType::output(OutputType::OpenDrain, Speed::Medium); 101pub struct SlaveCommand {
103 #[cfg(gpio_v2)] 102 /// The kind of command
104 return AfType::output_pull( 103 pub kind: SlaveCommandKind,
105 OutputType::OpenDrain, 104 /// The address that the slave matched
106 Speed::Medium, 105 pub address: Address,
107 match self.scl_pullup { 106}
108 true => Pull::Up,
109 false => Pull::None,
110 },
111 );
112 }
113 107
114 fn sda_af(&self) -> AfType { 108#[derive(Debug, Clone, PartialEq, Eq)]
115 #[cfg(gpio_v1)] 109#[cfg_attr(feature = "defmt", derive(defmt::Format))]
116 return AfType::output(OutputType::OpenDrain, Speed::Medium); 110/// The status of the slave send operation
117 #[cfg(gpio_v2)] 111pub enum SendStatus {
118 return AfType::output_pull( 112 /// The slave send operation is done, all bytes have been sent and the master is not requesting more
119 OutputType::OpenDrain, 113 Done,
120 Speed::Medium, 114 /// The slave send operation is done, but there are leftover bytes that the master did not read
121 match self.sda_pullup { 115 LeftoverBytes(usize),
122 true => Pull::Up, 116}
123 false => Pull::None, 117
124 }, 118struct I2CDropGuard<'d> {
125 ); 119 info: &'static Info,
120 scl: Option<Peri<'d, AnyPin>>,
121 sda: Option<Peri<'d, AnyPin>>,
122}
123impl<'d> Drop for I2CDropGuard<'d> {
124 fn drop(&mut self) {
125 if let Some(x) = self.scl.as_ref() {
126 x.set_as_disconnected()
127 }
128 if let Some(x) = self.sda.as_ref() {
129 x.set_as_disconnected()
130 }
131
132 self.info.rcc.disable();
126 } 133 }
127} 134}
128 135
129/// I2C driver. 136/// I2C driver.
130pub struct I2c<'d, M: Mode> { 137pub struct I2c<'d, M: Mode, IM: MasterMode> {
131 info: &'static Info, 138 info: &'static Info,
132 state: &'static State, 139 state: &'static State,
133 kernel_clock: Hertz, 140 kernel_clock: Hertz,
134 scl: Option<Peri<'d, AnyPin>>,
135 sda: Option<Peri<'d, AnyPin>>,
136 tx_dma: Option<ChannelAndRequest<'d>>, 141 tx_dma: Option<ChannelAndRequest<'d>>,
137 rx_dma: Option<ChannelAndRequest<'d>>, 142 rx_dma: Option<ChannelAndRequest<'d>>,
138 #[cfg(feature = "time")] 143 #[cfg(feature = "time")]
139 timeout: Duration, 144 timeout: Duration,
140 _phantom: PhantomData<M>, 145 _phantom: PhantomData<M>,
146 _phantom2: PhantomData<IM>,
147 _drop_guard: I2CDropGuard<'d>,
141} 148}
142 149
143impl<'d> I2c<'d, Async> { 150impl<'d> I2c<'d, Async, Master> {
144 /// Create a new I2C driver. 151 /// Create a new I2C driver.
145 pub fn new<T: Instance>( 152 pub fn new<T: Instance>(
146 peri: Peri<'d, T>, 153 peri: Peri<'d, T>,
@@ -166,7 +173,7 @@ impl<'d> I2c<'d, Async> {
166 } 173 }
167} 174}
168 175
169impl<'d> I2c<'d, Blocking> { 176impl<'d> I2c<'d, Blocking, Master> {
170 /// Create a new blocking I2C driver. 177 /// Create a new blocking I2C driver.
171 pub fn new_blocking<T: Instance>( 178 pub fn new_blocking<T: Instance>(
172 peri: Peri<'d, T>, 179 peri: Peri<'d, T>,
@@ -187,7 +194,7 @@ impl<'d> I2c<'d, Blocking> {
187 } 194 }
188} 195}
189 196
190impl<'d, M: Mode> I2c<'d, M> { 197impl<'d, M: Mode> I2c<'d, M, Master> {
191 /// Create a new I2C driver. 198 /// Create a new I2C driver.
192 fn new_inner<T: Instance>( 199 fn new_inner<T: Instance>(
193 _peri: Peri<'d, T>, 200 _peri: Peri<'d, T>,
@@ -205,15 +212,20 @@ impl<'d, M: Mode> I2c<'d, M> {
205 info: T::info(), 212 info: T::info(),
206 state: T::state(), 213 state: T::state(),
207 kernel_clock: T::frequency(), 214 kernel_clock: T::frequency(),
208 scl,
209 sda,
210 tx_dma, 215 tx_dma,
211 rx_dma, 216 rx_dma,
212 #[cfg(feature = "time")] 217 #[cfg(feature = "time")]
213 timeout: config.timeout, 218 timeout: config.timeout,
214 _phantom: PhantomData, 219 _phantom: PhantomData,
220 _phantom2: PhantomData,
221 _drop_guard: I2CDropGuard {
222 info: T::info(),
223 scl,
224 sda,
225 },
215 }; 226 };
216 this.enable_and_init(freq, config); 227 this.enable_and_init(freq, config);
228
217 this 229 this
218 } 230 }
219 231
@@ -221,7 +233,9 @@ impl<'d, M: Mode> I2c<'d, M> {
221 self.info.rcc.enable_and_reset(); 233 self.info.rcc.enable_and_reset();
222 self.init(freq, config); 234 self.init(freq, config);
223 } 235 }
236}
224 237
238impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> {
225 fn timeout(&self) -> Timeout { 239 fn timeout(&self) -> Timeout {
226 Timeout { 240 Timeout {
227 #[cfg(feature = "time")] 241 #[cfg(feature = "time")]
@@ -230,15 +244,6 @@ impl<'d, M: Mode> I2c<'d, M> {
230 } 244 }
231} 245}
232 246
233impl<'d, M: Mode> Drop for I2c<'d, M> {
234 fn drop(&mut self) {
235 self.scl.as_ref().map(|x| x.set_as_disconnected());
236 self.sda.as_ref().map(|x| x.set_as_disconnected());
237
238 self.info.rcc.disable()
239 }
240}
241
242#[derive(Copy, Clone)] 247#[derive(Copy, Clone)]
243struct Timeout { 248struct Timeout {
244 #[cfg(feature = "time")] 249 #[cfg(feature = "time")]
@@ -347,7 +352,7 @@ foreach_peripheral!(
347 }; 352 };
348); 353);
349 354
350impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M> { 355impl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M, IM> {
351 type Error = Error; 356 type Error = Error;
352 357
353 fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { 358 fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
@@ -355,7 +360,7 @@ impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M> {
355 } 360 }
356} 361}
357 362
358impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M> { 363impl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M, IM> {
359 type Error = Error; 364 type Error = Error;
360 365
361 fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { 366 fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
@@ -363,7 +368,7 @@ impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M> {
363 } 368 }
364} 369}
365 370
366impl<'d, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M> { 371impl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M, IM> {
367 type Error = Error; 372 type Error = Error;
368 373
369 fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { 374 fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
@@ -387,11 +392,11 @@ impl embedded_hal_1::i2c::Error for Error {
387 } 392 }
388} 393}
389 394
390impl<'d, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, M> { 395impl<'d, M: Mode, IM: MasterMode> embedded_hal_1::i2c::ErrorType for I2c<'d, M, IM> {
391 type Error = Error; 396 type Error = Error;
392} 397}
393 398
394impl<'d, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, M> { 399impl<'d, M: Mode, IM: MasterMode> embedded_hal_1::i2c::I2c for I2c<'d, M, IM> {
395 fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { 400 fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
396 self.blocking_read(address, read) 401 self.blocking_read(address, read)
397 } 402 }
@@ -413,7 +418,7 @@ impl<'d, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, M> {
413 } 418 }
414} 419}
415 420
416impl<'d> embedded_hal_async::i2c::I2c for I2c<'d, Async> { 421impl<'d, IM: MasterMode> embedded_hal_async::i2c::I2c for I2c<'d, Async, IM> {
417 async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { 422 async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
418 self.read(address, read).await 423 self.read(address, read).await
419 } 424 }
@@ -529,9 +534,7 @@ fn operation_frames<'a, 'b: 'a>(
529 let mut next_first_frame = true; 534 let mut next_first_frame = true;
530 535
531 Ok(iter::from_fn(move || { 536 Ok(iter::from_fn(move || {
532 let Some(op) = operations.next() else { 537 let op = operations.next()?;
533 return None;
534 };
535 538
536 // Is `op` first frame of its type? 539 // Is `op` first frame of its type?
537 let first_frame = next_first_frame; 540 let first_frame = next_first_frame;
diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs
index 28026f83c..35f13ab46 100644
--- a/embassy-stm32/src/i2c/v1.rs
+++ b/embassy-stm32/src/i2c/v1.rs
@@ -11,6 +11,7 @@ use embassy_embedded_hal::SetConfig;
11use embassy_futures::select::{select, Either}; 11use embassy_futures::select::{select, Either};
12use embassy_hal_internal::drop::OnDrop; 12use embassy_hal_internal::drop::OnDrop;
13use embedded_hal_1::i2c::Operation; 13use embedded_hal_1::i2c::Operation;
14use mode::Master;
14 15
15use super::*; 16use super::*;
16use crate::mode::Mode as PeriMode; 17use crate::mode::Mode as PeriMode;
@@ -41,7 +42,7 @@ pub unsafe fn on_interrupt<T: Instance>() {
41 }); 42 });
42} 43}
43 44
44impl<'d, M: PeriMode> I2c<'d, M> { 45impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> {
45 pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { 46 pub(crate) fn init(&mut self, freq: Hertz, _config: Config) {
46 self.info.regs.cr1().modify(|reg| { 47 self.info.regs.cr1().modify(|reg| {
47 reg.set_pe(false); 48 reg.set_pe(false);
@@ -354,7 +355,7 @@ impl<'d, M: PeriMode> I2c<'d, M> {
354 } 355 }
355} 356}
356 357
357impl<'d> I2c<'d, Async> { 358impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
358 async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> { 359 async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> {
359 self.info.regs.cr2().modify(|w| { 360 self.info.regs.cr2().modify(|w| {
360 // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for 361 // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for
@@ -800,7 +801,7 @@ impl Timings {
800 } 801 }
801} 802}
802 803
803impl<'d, M: PeriMode> SetConfig for I2c<'d, M> { 804impl<'d, M: PeriMode> SetConfig for I2c<'d, M, Master> {
804 type Config = Hertz; 805 type Config = Hertz;
805 type ConfigError = (); 806 type ConfigError = ();
806 fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { 807 fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs
index 50a25754e..e24cce5c6 100644
--- a/embassy-stm32/src/i2c/v2.rs
+++ b/embassy-stm32/src/i2c/v2.rs
@@ -2,22 +2,87 @@ use core::cmp;
2use core::future::poll_fn; 2use core::future::poll_fn;
3use core::task::Poll; 3use core::task::Poll;
4 4
5use config::{Address, OwnAddresses, OA2};
5use embassy_embedded_hal::SetConfig; 6use embassy_embedded_hal::SetConfig;
6use embassy_hal_internal::drop::OnDrop; 7use embassy_hal_internal::drop::OnDrop;
7use embedded_hal_1::i2c::Operation; 8use embedded_hal_1::i2c::Operation;
9use mode::{Master, MultiMaster};
10use stm32_metapac::i2c::vals::{Addmode, Oamsk};
8 11
9use super::*; 12use super::*;
10use crate::pac::i2c; 13use crate::pac::i2c;
11 14
15impl From<AddrMask> for Oamsk {
16 fn from(value: AddrMask) -> Self {
17 match value {
18 AddrMask::NOMASK => Oamsk::NO_MASK,
19 AddrMask::MASK1 => Oamsk::MASK1,
20 AddrMask::MASK2 => Oamsk::MASK2,
21 AddrMask::MASK3 => Oamsk::MASK3,
22 AddrMask::MASK4 => Oamsk::MASK4,
23 AddrMask::MASK5 => Oamsk::MASK5,
24 AddrMask::MASK6 => Oamsk::MASK6,
25 AddrMask::MASK7 => Oamsk::MASK7,
26 }
27 }
28}
29
30impl Address {
31 pub(super) fn add_mode(&self) -> stm32_metapac::i2c::vals::Addmode {
32 match self {
33 Address::SevenBit(_) => stm32_metapac::i2c::vals::Addmode::BIT7,
34 Address::TenBit(_) => stm32_metapac::i2c::vals::Addmode::BIT10,
35 }
36 }
37}
38
39enum ReceiveResult {
40 DataAvailable,
41 StopReceived,
42 NewStart,
43}
44
45fn debug_print_interrupts(isr: stm32_metapac::i2c::regs::Isr) {
46 if isr.tcr() {
47 trace!("interrupt: tcr");
48 }
49 if isr.tc() {
50 trace!("interrupt: tc");
51 }
52 if isr.addr() {
53 trace!("interrupt: addr");
54 }
55 if isr.stopf() {
56 trace!("interrupt: stopf");
57 }
58 if isr.nackf() {
59 trace!("interrupt: nackf");
60 }
61 if isr.berr() {
62 trace!("interrupt: berr");
63 }
64 if isr.arlo() {
65 trace!("interrupt: arlo");
66 }
67 if isr.ovr() {
68 trace!("interrupt: ovr");
69 }
70}
71
12pub(crate) unsafe fn on_interrupt<T: Instance>() { 72pub(crate) unsafe fn on_interrupt<T: Instance>() {
13 let regs = T::info().regs; 73 let regs = T::info().regs;
14 let isr = regs.isr().read(); 74 let isr = regs.isr().read();
15 75
16 if isr.tcr() || isr.tc() || isr.nackf() || isr.berr() || isr.arlo() || isr.ovr() { 76 if isr.tcr() || isr.tc() || isr.addr() || isr.stopf() || isr.nackf() || isr.berr() || isr.arlo() || isr.ovr() {
77 debug_print_interrupts(isr);
78
17 T::state().waker.wake(); 79 T::state().waker.wake();
18 } 80 }
81
19 critical_section::with(|_| { 82 critical_section::with(|_| {
20 regs.cr1().modify(|w| { 83 regs.cr1().modify(|w| {
84 w.set_addrie(false);
85 w.set_stopie(false);
21 // The flag can only be cleared by writting to nbytes, we won't do that here 86 // The flag can only be cleared by writting to nbytes, we won't do that here
22 w.set_tcie(false); 87 w.set_tcie(false);
23 // Error flags are to be read in the routines, so we also don't clear them here 88 // Error flags are to be read in the routines, so we also don't clear them here
@@ -27,7 +92,7 @@ pub(crate) unsafe fn on_interrupt<T: Instance>() {
27 }); 92 });
28} 93}
29 94
30impl<'d, M: Mode> I2c<'d, M> { 95impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> {
31 pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { 96 pub(crate) fn init(&mut self, freq: Hertz, _config: Config) {
32 self.info.regs.cr1().modify(|reg| { 97 self.info.regs.cr1().modify(|reg| {
33 reg.set_pe(false); 98 reg.set_pe(false);
@@ -55,7 +120,7 @@ impl<'d, M: Mode> I2c<'d, M> {
55 120
56 fn master_read( 121 fn master_read(
57 info: &'static Info, 122 info: &'static Info,
58 address: u8, 123 address: Address,
59 length: usize, 124 length: usize,
60 stop: Stop, 125 stop: Stop,
61 reload: bool, 126 reload: bool,
@@ -84,8 +149,8 @@ impl<'d, M: Mode> I2c<'d, M> {
84 }; 149 };
85 150
86 info.regs.cr2().modify(|w| { 151 info.regs.cr2().modify(|w| {
87 w.set_sadd((address << 1 | 0) as u16); 152 w.set_sadd(address.addr() << 1);
88 w.set_add10(i2c::vals::Addmode::BIT7); 153 w.set_add10(address.add_mode());
89 w.set_dir(i2c::vals::Dir::READ); 154 w.set_dir(i2c::vals::Dir::READ);
90 w.set_nbytes(length as u8); 155 w.set_nbytes(length as u8);
91 w.set_start(true); 156 w.set_start(true);
@@ -98,7 +163,7 @@ impl<'d, M: Mode> I2c<'d, M> {
98 163
99 fn master_write( 164 fn master_write(
100 info: &'static Info, 165 info: &'static Info,
101 address: u8, 166 address: Address,
102 length: usize, 167 length: usize,
103 stop: Stop, 168 stop: Stop,
104 reload: bool, 169 reload: bool,
@@ -128,8 +193,8 @@ impl<'d, M: Mode> I2c<'d, M> {
128 // START bit can be set even if the bus is BUSY or 193 // START bit can be set even if the bus is BUSY or
129 // I2C is in slave mode. 194 // I2C is in slave mode.
130 info.regs.cr2().modify(|w| { 195 info.regs.cr2().modify(|w| {
131 w.set_sadd((address << 1 | 0) as u16); 196 w.set_sadd(address.addr() << 1);
132 w.set_add10(i2c::vals::Addmode::BIT7); 197 w.set_add10(address.add_mode());
133 w.set_dir(i2c::vals::Dir::WRITE); 198 w.set_dir(i2c::vals::Dir::WRITE);
134 w.set_nbytes(length as u8); 199 w.set_nbytes(length as u8);
135 w.set_start(true); 200 w.set_start(true);
@@ -140,14 +205,14 @@ impl<'d, M: Mode> I2c<'d, M> {
140 Ok(()) 205 Ok(())
141 } 206 }
142 207
143 fn master_continue(info: &'static Info, length: usize, reload: bool, timeout: Timeout) -> Result<(), Error> { 208 fn reload(info: &'static Info, length: usize, will_reload: bool, timeout: Timeout) -> Result<(), Error> {
144 assert!(length < 256 && length > 0); 209 assert!(length < 256 && length > 0);
145 210
146 while !info.regs.isr().read().tcr() { 211 while !info.regs.isr().read().tcr() {
147 timeout.check()?; 212 timeout.check()?;
148 } 213 }
149 214
150 let reload = if reload { 215 let will_reload = if will_reload {
151 i2c::vals::Reload::NOT_COMPLETED 216 i2c::vals::Reload::NOT_COMPLETED
152 } else { 217 } else {
153 i2c::vals::Reload::COMPLETED 218 i2c::vals::Reload::COMPLETED
@@ -155,7 +220,7 @@ impl<'d, M: Mode> I2c<'d, M> {
155 220
156 info.regs.cr2().modify(|w| { 221 info.regs.cr2().modify(|w| {
157 w.set_nbytes(length as u8); 222 w.set_nbytes(length as u8);
158 w.set_reload(reload); 223 w.set_reload(will_reload);
159 }); 224 });
160 225
161 Ok(()) 226 Ok(())
@@ -163,49 +228,132 @@ impl<'d, M: Mode> I2c<'d, M> {
163 228
164 fn flush_txdr(&self) { 229 fn flush_txdr(&self) {
165 if self.info.regs.isr().read().txis() { 230 if self.info.regs.isr().read().txis() {
166 self.info.regs.txdr().write(|w| w.set_txdata(0)); 231 trace!("Flush TXDATA with zeroes");
232 self.info.regs.txdr().modify(|w| w.set_txdata(0));
167 } 233 }
168 if !self.info.regs.isr().read().txe() { 234 if !self.info.regs.isr().read().txe() {
235 trace!("Flush TXDR");
169 self.info.regs.isr().modify(|w| w.set_txe(true)) 236 self.info.regs.isr().modify(|w| w.set_txe(true))
170 } 237 }
171 } 238 }
172 239
173 fn wait_txe(&self, timeout: Timeout) -> Result<(), Error> { 240 fn error_occurred(&self, isr: &i2c::regs::Isr, timeout: Timeout) -> Result<(), Error> {
241 if isr.nackf() {
242 trace!("NACK triggered.");
243 self.info.regs.icr().modify(|reg| reg.set_nackcf(true));
244 // NACK should be followed by STOP
245 if let Ok(()) = self.wait_stop(timeout) {
246 trace!("Got STOP after NACK, clearing flag.");
247 self.info.regs.icr().modify(|reg| reg.set_stopcf(true));
248 }
249 self.flush_txdr();
250 return Err(Error::Nack);
251 } else if isr.berr() {
252 trace!("BERR triggered.");
253 self.info.regs.icr().modify(|reg| reg.set_berrcf(true));
254 self.flush_txdr();
255 return Err(Error::Bus);
256 } else if isr.arlo() {
257 trace!("ARLO triggered.");
258 self.info.regs.icr().modify(|reg| reg.set_arlocf(true));
259 self.flush_txdr();
260 return Err(Error::Arbitration);
261 } else if isr.ovr() {
262 trace!("OVR triggered.");
263 self.info.regs.icr().modify(|reg| reg.set_ovrcf(true));
264 return Err(Error::Overrun);
265 }
266 return Ok(());
267 }
268
269 fn wait_txis(&self, timeout: Timeout) -> Result<(), Error> {
270 let mut first_loop = true;
271
174 loop { 272 loop {
175 let isr = self.info.regs.isr().read(); 273 let isr = self.info.regs.isr().read();
176 if isr.txe() { 274 self.error_occurred(&isr, timeout)?;
275 if isr.txis() {
276 trace!("TXIS");
177 return Ok(()); 277 return Ok(());
178 } else if isr.berr() {
179 self.info.regs.icr().write(|reg| reg.set_berrcf(true));
180 return Err(Error::Bus);
181 } else if isr.arlo() {
182 self.info.regs.icr().write(|reg| reg.set_arlocf(true));
183 return Err(Error::Arbitration);
184 } else if isr.nackf() {
185 self.info.regs.icr().write(|reg| reg.set_nackcf(true));
186 self.flush_txdr();
187 return Err(Error::Nack);
188 } 278 }
189 279
280 {
281 if first_loop {
282 trace!("Waiting for TXIS...");
283 first_loop = false;
284 }
285 }
286 timeout.check()?;
287 }
288 }
289
290 fn wait_stop_or_err(&self, timeout: Timeout) -> Result<(), Error> {
291 loop {
292 let isr = self.info.regs.isr().read();
293 self.error_occurred(&isr, timeout)?;
294 if isr.stopf() {
295 trace!("STOP triggered.");
296 self.info.regs.icr().modify(|reg| reg.set_stopcf(true));
297 return Ok(());
298 }
299 timeout.check()?;
300 }
301 }
302 fn wait_stop(&self, timeout: Timeout) -> Result<(), Error> {
303 loop {
304 let isr = self.info.regs.isr().read();
305 if isr.stopf() {
306 trace!("STOP triggered.");
307 self.info.regs.icr().modify(|reg| reg.set_stopcf(true));
308 return Ok(());
309 }
190 timeout.check()?; 310 timeout.check()?;
191 } 311 }
192 } 312 }
193 313
194 fn wait_rxne(&self, timeout: Timeout) -> Result<(), Error> { 314 fn wait_af(&self, timeout: Timeout) -> Result<(), Error> {
195 loop { 315 loop {
196 let isr = self.info.regs.isr().read(); 316 let isr = self.info.regs.isr().read();
197 if isr.rxne() { 317 if isr.nackf() {
318 trace!("AF triggered.");
319 self.info.regs.icr().modify(|reg| reg.set_nackcf(true));
198 return Ok(()); 320 return Ok(());
199 } else if isr.berr() { 321 }
200 self.info.regs.icr().write(|reg| reg.set_berrcf(true)); 322 timeout.check()?;
201 return Err(Error::Bus); 323 }
202 } else if isr.arlo() { 324 }
203 self.info.regs.icr().write(|reg| reg.set_arlocf(true)); 325
204 return Err(Error::Arbitration); 326 fn wait_rxne(&self, timeout: Timeout) -> Result<ReceiveResult, Error> {
205 } else if isr.nackf() { 327 let mut first_loop = true;
206 self.info.regs.icr().write(|reg| reg.set_nackcf(true)); 328
207 self.flush_txdr(); 329 loop {
208 return Err(Error::Nack); 330 let isr = self.info.regs.isr().read();
331 self.error_occurred(&isr, timeout)?;
332 if isr.stopf() {
333 trace!("STOP when waiting for RXNE.");
334 if self.info.regs.isr().read().rxne() {
335 trace!("Data received with STOP.");
336 return Ok(ReceiveResult::DataAvailable);
337 }
338 trace!("STOP triggered without data.");
339 return Ok(ReceiveResult::StopReceived);
340 } else if isr.rxne() {
341 trace!("RXNE.");
342 return Ok(ReceiveResult::DataAvailable);
343 } else if isr.addr() {
344 // Another addr event received, which means START was sent again
345 // which happens when accessing memory registers (common i2c interface design)
346 // e.g. master sends: START, write 1 byte (register index), START, read N bytes (until NACK)
347 // Possible to receive this flag at the same time as rxne, so check rxne first
348 trace!("START when waiting for RXNE. Ending receive loop.");
349 // Return without clearing ADDR so `listen` can catch it
350 return Ok(ReceiveResult::NewStart);
351 }
352 {
353 if first_loop {
354 trace!("Waiting for RXNE...");
355 first_loop = false;
356 }
209 } 357 }
210 358
211 timeout.check()?; 359 timeout.check()?;
@@ -215,25 +363,21 @@ impl<'d, M: Mode> I2c<'d, M> {
215 fn wait_tc(&self, timeout: Timeout) -> Result<(), Error> { 363 fn wait_tc(&self, timeout: Timeout) -> Result<(), Error> {
216 loop { 364 loop {
217 let isr = self.info.regs.isr().read(); 365 let isr = self.info.regs.isr().read();
366 self.error_occurred(&isr, timeout)?;
218 if isr.tc() { 367 if isr.tc() {
219 return Ok(()); 368 return Ok(());
220 } else if isr.berr() {
221 self.info.regs.icr().write(|reg| reg.set_berrcf(true));
222 return Err(Error::Bus);
223 } else if isr.arlo() {
224 self.info.regs.icr().write(|reg| reg.set_arlocf(true));
225 return Err(Error::Arbitration);
226 } else if isr.nackf() {
227 self.info.regs.icr().write(|reg| reg.set_nackcf(true));
228 self.flush_txdr();
229 return Err(Error::Nack);
230 } 369 }
231
232 timeout.check()?; 370 timeout.check()?;
233 } 371 }
234 } 372 }
235 373
236 fn read_internal(&mut self, address: u8, read: &mut [u8], restart: bool, timeout: Timeout) -> Result<(), Error> { 374 fn read_internal(
375 &mut self,
376 address: Address,
377 read: &mut [u8],
378 restart: bool,
379 timeout: Timeout,
380 ) -> Result<(), Error> {
237 let completed_chunks = read.len() / 255; 381 let completed_chunks = read.len() / 255;
238 let total_chunks = if completed_chunks * 255 == read.len() { 382 let total_chunks = if completed_chunks * 255 == read.len() {
239 completed_chunks 383 completed_chunks
@@ -254,7 +398,7 @@ impl<'d, M: Mode> I2c<'d, M> {
254 398
255 for (number, chunk) in read.chunks_mut(255).enumerate() { 399 for (number, chunk) in read.chunks_mut(255).enumerate() {
256 if number != 0 { 400 if number != 0 {
257 Self::master_continue(self.info, chunk.len(), number != last_chunk_idx, timeout)?; 401 Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?;
258 } 402 }
259 403
260 for byte in chunk { 404 for byte in chunk {
@@ -267,7 +411,13 @@ impl<'d, M: Mode> I2c<'d, M> {
267 Ok(()) 411 Ok(())
268 } 412 }
269 413
270 fn write_internal(&mut self, address: u8, write: &[u8], send_stop: bool, timeout: Timeout) -> Result<(), Error> { 414 fn write_internal(
415 &mut self,
416 address: Address,
417 write: &[u8],
418 send_stop: bool,
419 timeout: Timeout,
420 ) -> Result<(), Error> {
271 let completed_chunks = write.len() / 255; 421 let completed_chunks = write.len() / 255;
272 let total_chunks = if completed_chunks * 255 == write.len() { 422 let total_chunks = if completed_chunks * 255 == write.len() {
273 completed_chunks 423 completed_chunks
@@ -295,14 +445,14 @@ impl<'d, M: Mode> I2c<'d, M> {
295 445
296 for (number, chunk) in write.chunks(255).enumerate() { 446 for (number, chunk) in write.chunks(255).enumerate() {
297 if number != 0 { 447 if number != 0 {
298 Self::master_continue(self.info, chunk.len(), number != last_chunk_idx, timeout)?; 448 Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?;
299 } 449 }
300 450
301 for byte in chunk { 451 for byte in chunk {
302 // Wait until we are allowed to send data 452 // Wait until we are allowed to send data
303 // (START has been ACKed or last byte when 453 // (START has been ACKed or last byte when
304 // through) 454 // through)
305 if let Err(err) = self.wait_txe(timeout) { 455 if let Err(err) = self.wait_txis(timeout) {
306 if send_stop { 456 if send_stop {
307 self.master_stop(); 457 self.master_stop();
308 } 458 }
@@ -325,20 +475,20 @@ impl<'d, M: Mode> I2c<'d, M> {
325 475
326 /// Blocking read. 476 /// Blocking read.
327 pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { 477 pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> {
328 self.read_internal(address, read, false, self.timeout()) 478 self.read_internal(address.into(), read, false, self.timeout())
329 // Automatic Stop 479 // Automatic Stop
330 } 480 }
331 481
332 /// Blocking write. 482 /// Blocking write.
333 pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { 483 pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {
334 self.write_internal(address, write, true, self.timeout()) 484 self.write_internal(address.into(), write, true, self.timeout())
335 } 485 }
336 486
337 /// Blocking write, restart, read. 487 /// Blocking write, restart, read.
338 pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { 488 pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
339 let timeout = self.timeout(); 489 let timeout = self.timeout();
340 self.write_internal(address, write, false, timeout)?; 490 self.write_internal(address.into(), write, false, timeout)?;
341 self.read_internal(address, read, true, timeout) 491 self.read_internal(address.into(), read, true, timeout)
342 // Automatic Stop 492 // Automatic Stop
343 } 493 }
344 494
@@ -368,7 +518,7 @@ impl<'d, M: Mode> I2c<'d, M> {
368 518
369 if let Err(err) = Self::master_write( 519 if let Err(err) = Self::master_write(
370 self.info, 520 self.info,
371 address, 521 address.into(),
372 first_length.min(255), 522 first_length.min(255),
373 Stop::Software, 523 Stop::Software,
374 (first_length > 255) || (last_slice_index != 0), 524 (first_length > 255) || (last_slice_index != 0),
@@ -389,7 +539,7 @@ impl<'d, M: Mode> I2c<'d, M> {
389 let last_chunk_idx = total_chunks.saturating_sub(1); 539 let last_chunk_idx = total_chunks.saturating_sub(1);
390 540
391 if idx != 0 { 541 if idx != 0 {
392 if let Err(err) = Self::master_continue( 542 if let Err(err) = Self::reload(
393 self.info, 543 self.info,
394 slice_len.min(255), 544 slice_len.min(255),
395 (idx != last_slice_index) || (slice_len > 255), 545 (idx != last_slice_index) || (slice_len > 255),
@@ -402,7 +552,7 @@ impl<'d, M: Mode> I2c<'d, M> {
402 552
403 for (number, chunk) in slice.chunks(255).enumerate() { 553 for (number, chunk) in slice.chunks(255).enumerate() {
404 if number != 0 { 554 if number != 0 {
405 if let Err(err) = Self::master_continue( 555 if let Err(err) = Self::reload(
406 self.info, 556 self.info,
407 chunk.len(), 557 chunk.len(),
408 (number != last_chunk_idx) || (idx != last_slice_index), 558 (number != last_chunk_idx) || (idx != last_slice_index),
@@ -417,7 +567,7 @@ impl<'d, M: Mode> I2c<'d, M> {
417 // Wait until we are allowed to send data 567 // Wait until we are allowed to send data
418 // (START has been ACKed or last byte when 568 // (START has been ACKed or last byte when
419 // through) 569 // through)
420 if let Err(err) = self.wait_txe(timeout) { 570 if let Err(err) = self.wait_txis(timeout) {
421 self.master_stop(); 571 self.master_stop();
422 return Err(err); 572 return Err(err);
423 } 573 }
@@ -435,10 +585,10 @@ impl<'d, M: Mode> I2c<'d, M> {
435 } 585 }
436} 586}
437 587
438impl<'d> I2c<'d, Async> { 588impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
439 async fn write_dma_internal( 589 async fn write_dma_internal(
440 &mut self, 590 &mut self,
441 address: u8, 591 address: Address,
442 write: &[u8], 592 write: &[u8],
443 first_slice: bool, 593 first_slice: bool,
444 last_slice: bool, 594 last_slice: bool,
@@ -512,7 +662,7 @@ impl<'d> I2c<'d, Async> {
512 timeout, 662 timeout,
513 )?; 663 )?;
514 } else { 664 } else {
515 Self::master_continue(self.info, total_len.min(255), (total_len > 255) || !last_slice, timeout)?; 665 Self::reload(self.info, total_len.min(255), (total_len > 255) || !last_slice, timeout)?;
516 self.info.regs.cr1().modify(|w| w.set_tcie(true)); 666 self.info.regs.cr1().modify(|w| w.set_tcie(true));
517 } 667 }
518 } else if !(isr.tcr() || isr.tc()) { 668 } else if !(isr.tcr() || isr.tc()) {
@@ -523,7 +673,7 @@ impl<'d> I2c<'d, Async> {
523 } else { 673 } else {
524 let last_piece = (remaining_len <= 255) && last_slice; 674 let last_piece = (remaining_len <= 255) && last_slice;
525 675
526 if let Err(e) = Self::master_continue(self.info, remaining_len.min(255), !last_piece, timeout) { 676 if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) {
527 return Poll::Ready(Err(e)); 677 return Poll::Ready(Err(e));
528 } 678 }
529 self.info.regs.cr1().modify(|w| w.set_tcie(true)); 679 self.info.regs.cr1().modify(|w| w.set_tcie(true));
@@ -551,7 +701,7 @@ impl<'d> I2c<'d, Async> {
551 701
552 async fn read_dma_internal( 702 async fn read_dma_internal(
553 &mut self, 703 &mut self,
554 address: u8, 704 address: Address,
555 buffer: &mut [u8], 705 buffer: &mut [u8],
556 restart: bool, 706 restart: bool,
557 timeout: Timeout, 707 timeout: Timeout,
@@ -626,7 +776,7 @@ impl<'d> I2c<'d, Async> {
626 } else { 776 } else {
627 let last_piece = remaining_len <= 255; 777 let last_piece = remaining_len <= 255;
628 778
629 if let Err(e) = Self::master_continue(self.info, remaining_len.min(255), !last_piece, timeout) { 779 if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) {
630 return Poll::Ready(Err(e)); 780 return Poll::Ready(Err(e));
631 } 781 }
632 // Return here if we are on last chunk, 782 // Return here if we are on last chunk,
@@ -647,7 +797,6 @@ impl<'d> I2c<'d, Async> {
647 797
648 Ok(()) 798 Ok(())
649 } 799 }
650
651 // ========================= 800 // =========================
652 // Async public API 801 // Async public API
653 802
@@ -655,10 +804,10 @@ impl<'d> I2c<'d, Async> {
655 pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { 804 pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {
656 let timeout = self.timeout(); 805 let timeout = self.timeout();
657 if write.is_empty() { 806 if write.is_empty() {
658 self.write_internal(address, write, true, timeout) 807 self.write_internal(address.into(), write, true, timeout)
659 } else { 808 } else {
660 timeout 809 timeout
661 .with(self.write_dma_internal(address, write, true, true, true, timeout)) 810 .with(self.write_dma_internal(address.into(), write, true, true, true, timeout))
662 .await 811 .await
663 } 812 }
664 } 813 }
@@ -666,7 +815,7 @@ impl<'d> I2c<'d, Async> {
666 /// Write multiple buffers. 815 /// Write multiple buffers.
667 /// 816 ///
668 /// The buffers are concatenated in a single write transaction. 817 /// The buffers are concatenated in a single write transaction.
669 pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> { 818 pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> {
670 let timeout = self.timeout(); 819 let timeout = self.timeout();
671 820
672 if write.is_empty() { 821 if write.is_empty() {
@@ -693,9 +842,9 @@ impl<'d> I2c<'d, Async> {
693 let timeout = self.timeout(); 842 let timeout = self.timeout();
694 843
695 if buffer.is_empty() { 844 if buffer.is_empty() {
696 self.read_internal(address, buffer, false, timeout) 845 self.read_internal(address.into(), buffer, false, timeout)
697 } else { 846 } else {
698 let fut = self.read_dma_internal(address, buffer, false, timeout); 847 let fut = self.read_dma_internal(address.into(), buffer, false, timeout);
699 timeout.with(fut).await 848 timeout.with(fut).await
700 } 849 }
701 } 850 }
@@ -705,16 +854,16 @@ impl<'d> I2c<'d, Async> {
705 let timeout = self.timeout(); 854 let timeout = self.timeout();
706 855
707 if write.is_empty() { 856 if write.is_empty() {
708 self.write_internal(address, write, false, timeout)?; 857 self.write_internal(address.into(), write, false, timeout)?;
709 } else { 858 } else {
710 let fut = self.write_dma_internal(address, write, true, true, false, timeout); 859 let fut = self.write_dma_internal(address.into(), write, true, true, false, timeout);
711 timeout.with(fut).await?; 860 timeout.with(fut).await?;
712 } 861 }
713 862
714 if read.is_empty() { 863 if read.is_empty() {
715 self.read_internal(address, read, true, timeout)?; 864 self.read_internal(address.into(), read, true, timeout)?;
716 } else { 865 } else {
717 let fut = self.read_dma_internal(address, read, true, timeout); 866 let fut = self.read_dma_internal(address.into(), read, true, timeout);
718 timeout.with(fut).await?; 867 timeout.with(fut).await?;
719 } 868 }
720 869
@@ -733,6 +882,414 @@ impl<'d> I2c<'d, Async> {
733 } 882 }
734} 883}
735 884
885impl<'d, M: Mode> I2c<'d, M, Master> {
886 /// Configure the I2C driver for slave operations, allowing for the driver to be used as a slave and a master (multimaster)
887 pub fn into_slave_multimaster(mut self, slave_addr_config: SlaveAddrConfig) -> I2c<'d, M, MultiMaster> {
888 let mut slave = I2c {
889 info: self.info,
890 state: self.state,
891 kernel_clock: self.kernel_clock,
892 tx_dma: self.tx_dma.take(),
893 rx_dma: self.rx_dma.take(),
894 #[cfg(feature = "time")]
895 timeout: self.timeout,
896 _phantom: PhantomData,
897 _phantom2: PhantomData,
898 _drop_guard: self._drop_guard,
899 };
900 slave.init_slave(slave_addr_config);
901 slave
902 }
903}
904
905impl<'d, M: Mode> I2c<'d, M, MultiMaster> {
906 pub(crate) fn init_slave(&mut self, config: SlaveAddrConfig) {
907 self.info.regs.cr1().modify(|reg| {
908 reg.set_pe(false);
909 });
910
911 self.info.regs.cr1().modify(|reg| {
912 reg.set_nostretch(false);
913 reg.set_gcen(config.general_call);
914 reg.set_sbc(true);
915 reg.set_pe(true);
916 });
917
918 self.reconfigure_addresses(config.addr);
919 }
920
921 /// Configure the slave address.
922 pub fn reconfigure_addresses(&mut self, addresses: OwnAddresses) {
923 match addresses {
924 OwnAddresses::OA1(oa1) => self.configure_oa1(oa1),
925 OwnAddresses::OA2(oa2) => self.configure_oa2(oa2),
926 OwnAddresses::Both { oa1, oa2 } => {
927 self.configure_oa1(oa1);
928 self.configure_oa2(oa2);
929 }
930 }
931 }
932
933 fn configure_oa1(&mut self, oa1: Address) {
934 match oa1 {
935 Address::SevenBit(addr) => self.info.regs.oar1().write(|reg| {
936 reg.set_oa1en(false);
937 reg.set_oa1((addr << 1) as u16);
938 reg.set_oa1mode(Addmode::BIT7);
939 reg.set_oa1en(true);
940 }),
941 Address::TenBit(addr) => self.info.regs.oar1().write(|reg| {
942 reg.set_oa1en(false);
943 reg.set_oa1(addr);
944 reg.set_oa1mode(Addmode::BIT10);
945 reg.set_oa1en(true);
946 }),
947 }
948 }
949
950 fn configure_oa2(&mut self, oa2: OA2) {
951 self.info.regs.oar2().write(|reg| {
952 reg.set_oa2en(false);
953 reg.set_oa2msk(oa2.mask.into());
954 reg.set_oa2(oa2.addr << 1);
955 reg.set_oa2en(true);
956 });
957 }
958
959 fn determine_matched_address(&self) -> Result<Address, Error> {
960 let matched = self.info.regs.isr().read().addcode();
961
962 if matched >> 3 == 0b11110 {
963 // is 10-bit address and we need to get the other 8 bits from the rxdr
964 // we do this by doing a blocking read of 1 byte
965 let mut buffer = [0];
966 self.slave_read_internal(&mut buffer, self.timeout())?;
967 Ok(Address::TenBit((matched as u16) << 6 | buffer[0] as u16))
968 } else {
969 Ok(Address::SevenBit(matched))
970 }
971 }
972}
973
974impl<'d, M: Mode> I2c<'d, M, MultiMaster> {
975 /// # Safety
976 /// This function will clear the address flag which will stop the clock stretching.
977 /// This should only be done after the dma transfer has been set up.
978 fn slave_start(info: &'static Info, length: usize, reload: bool) {
979 assert!(length < 256);
980
981 let reload = if reload {
982 i2c::vals::Reload::NOT_COMPLETED
983 } else {
984 i2c::vals::Reload::COMPLETED
985 };
986
987 info.regs.cr2().modify(|w| {
988 w.set_nbytes(length as u8);
989 w.set_reload(reload);
990 });
991
992 // clear the address flag, will stop the clock stretching.
993 // this should only be done after the dma transfer has been set up.
994 info.regs.icr().modify(|reg| reg.set_addrcf(true));
995 trace!("ADDRCF cleared (ADDR interrupt enabled, clock stretching ended)");
996 }
997
998 // A blocking read operation
999 fn slave_read_internal(&self, read: &mut [u8], timeout: Timeout) -> Result<usize, Error> {
1000 let completed_chunks = read.len() / 255;
1001 let total_chunks = if completed_chunks * 255 == read.len() {
1002 completed_chunks
1003 } else {
1004 completed_chunks + 1
1005 };
1006 let last_chunk_idx = total_chunks.saturating_sub(1);
1007 let total_len = read.len();
1008 let mut remaining_len = total_len;
1009
1010 for (number, chunk) in read.chunks_mut(255).enumerate() {
1011 trace!(
1012 "--- Slave RX transmission start - chunk: {}, expected (max) size: {}",
1013 number,
1014 chunk.len()
1015 );
1016 if number == 0 {
1017 Self::slave_start(self.info, chunk.len(), number != last_chunk_idx);
1018 } else {
1019 Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?;
1020 }
1021
1022 let mut index = 0;
1023
1024 for byte in chunk {
1025 // Wait until we have received something
1026 match self.wait_rxne(timeout) {
1027 Ok(ReceiveResult::StopReceived) | Ok(ReceiveResult::NewStart) => {
1028 trace!("--- Slave RX transmission end (early)");
1029 return Ok(total_len - remaining_len); // Return N bytes read
1030 }
1031 Ok(ReceiveResult::DataAvailable) => {
1032 *byte = self.info.regs.rxdr().read().rxdata();
1033 remaining_len = remaining_len.saturating_sub(1);
1034 {
1035 trace!("Slave RX data {}: {:#04x}", index, byte);
1036 index = index + 1;
1037 }
1038 }
1039 Err(e) => return Err(e),
1040 };
1041 }
1042 }
1043 self.wait_stop_or_err(timeout)?;
1044
1045 trace!("--- Slave RX transmission end");
1046 Ok(total_len - remaining_len) // Return N bytes read
1047 }
1048
1049 // A blocking write operation
1050 fn slave_write_internal(&mut self, write: &[u8], timeout: Timeout) -> Result<(), Error> {
1051 let completed_chunks = write.len() / 255;
1052 let total_chunks = if completed_chunks * 255 == write.len() {
1053 completed_chunks
1054 } else {
1055 completed_chunks + 1
1056 };
1057 let last_chunk_idx = total_chunks.saturating_sub(1);
1058
1059 for (number, chunk) in write.chunks(255).enumerate() {
1060 trace!(
1061 "--- Slave TX transmission start - chunk: {}, size: {}",
1062 number,
1063 chunk.len()
1064 );
1065 if number == 0 {
1066 Self::slave_start(self.info, chunk.len(), number != last_chunk_idx);
1067 } else {
1068 Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?;
1069 }
1070
1071 let mut index = 0;
1072
1073 for byte in chunk {
1074 // Wait until we are allowed to send data
1075 // (START has been ACKed or last byte when through)
1076 self.wait_txis(timeout)?;
1077
1078 {
1079 trace!("Slave TX data {}: {:#04x}", index, byte);
1080 index = index + 1;
1081 }
1082 self.info.regs.txdr().write(|w| w.set_txdata(*byte));
1083 }
1084 }
1085 self.wait_af(timeout)?;
1086 self.flush_txdr();
1087 self.wait_stop_or_err(timeout)?;
1088
1089 trace!("--- Slave TX transmission end");
1090 Ok(())
1091 }
1092
1093 /// Listen for incoming I2C messages.
1094 ///
1095 /// The listen method is an asynchronous method but it does not require DMA to be asynchronous.
1096 pub async fn listen(&mut self) -> Result<SlaveCommand, Error> {
1097 let state = self.state;
1098 self.info.regs.cr1().modify(|reg| {
1099 reg.set_addrie(true);
1100 trace!("Enable ADDRIE");
1101 });
1102
1103 poll_fn(|cx| {
1104 state.waker.register(cx.waker());
1105 let isr = self.info.regs.isr().read();
1106 if !isr.addr() {
1107 Poll::Pending
1108 } else {
1109 trace!("ADDR triggered (address match)");
1110 // we do not clear the address flag here as it will be cleared by the dma read/write
1111 // if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it
1112 match isr.dir() {
1113 i2c::vals::Dir::WRITE => {
1114 trace!("DIR: write");
1115 Poll::Ready(Ok(SlaveCommand {
1116 kind: SlaveCommandKind::Write,
1117 address: self.determine_matched_address()?,
1118 }))
1119 }
1120 i2c::vals::Dir::READ => {
1121 trace!("DIR: read");
1122 Poll::Ready(Ok(SlaveCommand {
1123 kind: SlaveCommandKind::Read,
1124 address: self.determine_matched_address()?,
1125 }))
1126 }
1127 }
1128 }
1129 })
1130 .await
1131 }
1132
1133 /// Respond to a write command.
1134 ///
1135 /// Returns total number of bytes received.
1136 pub fn blocking_respond_to_write(&self, read: &mut [u8]) -> Result<usize, Error> {
1137 let timeout = self.timeout();
1138 self.slave_read_internal(read, timeout)
1139 }
1140
1141 /// Respond to a read command.
1142 pub fn blocking_respond_to_read(&mut self, write: &[u8]) -> Result<(), Error> {
1143 let timeout = self.timeout();
1144 self.slave_write_internal(write, timeout)
1145 }
1146}
1147
1148impl<'d> I2c<'d, Async, MultiMaster> {
1149 /// Respond to a write command.
1150 ///
1151 /// Returns the total number of bytes received.
1152 pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
1153 let timeout = self.timeout();
1154 timeout.with(self.read_dma_internal_slave(buffer, timeout)).await
1155 }
1156
1157 /// Respond to a read request from an I2C master.
1158 pub async fn respond_to_read(&mut self, write: &[u8]) -> Result<SendStatus, Error> {
1159 let timeout = self.timeout();
1160 timeout.with(self.write_dma_internal_slave(write, timeout)).await
1161 }
1162
1163 // for data reception in slave mode
1164 //
1165 // returns the total number of bytes received
1166 async fn read_dma_internal_slave(&mut self, buffer: &mut [u8], timeout: Timeout) -> Result<usize, Error> {
1167 let total_len = buffer.len();
1168 let mut remaining_len = total_len;
1169
1170 let regs = self.info.regs;
1171
1172 let dma_transfer = unsafe {
1173 regs.cr1().modify(|w| {
1174 w.set_rxdmaen(true);
1175 w.set_stopie(true);
1176 w.set_tcie(true);
1177 });
1178 let src = regs.rxdr().as_ptr() as *mut u8;
1179
1180 self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default())
1181 };
1182
1183 let state = self.state;
1184
1185 let on_drop = OnDrop::new(|| {
1186 regs.cr1().modify(|w| {
1187 w.set_rxdmaen(false);
1188 w.set_stopie(false);
1189 w.set_tcie(false);
1190 });
1191 });
1192
1193 let total_received = poll_fn(|cx| {
1194 state.waker.register(cx.waker());
1195
1196 let isr = regs.isr().read();
1197
1198 if remaining_len == total_len {
1199 Self::slave_start(self.info, total_len.min(255), total_len > 255);
1200 remaining_len = remaining_len.saturating_sub(255);
1201 Poll::Pending
1202 } else if isr.tcr() {
1203 let is_last_slice = remaining_len <= 255;
1204 if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) {
1205 return Poll::Ready(Err(e));
1206 }
1207 remaining_len = remaining_len.saturating_sub(255);
1208 regs.cr1().modify(|w| w.set_tcie(true));
1209 Poll::Pending
1210 } else if isr.stopf() {
1211 regs.icr().write(|reg| reg.set_stopcf(true));
1212 let poll = Poll::Ready(Ok(total_len - remaining_len));
1213 poll
1214 } else {
1215 Poll::Pending
1216 }
1217 })
1218 .await?;
1219
1220 dma_transfer.await;
1221
1222 drop(on_drop);
1223
1224 Ok(total_received)
1225 }
1226
1227 async fn write_dma_internal_slave(&mut self, buffer: &[u8], timeout: Timeout) -> Result<SendStatus, Error> {
1228 let total_len = buffer.len();
1229 let mut remaining_len = total_len;
1230
1231 let mut dma_transfer = unsafe {
1232 let regs = self.info.regs;
1233 regs.cr1().modify(|w| {
1234 w.set_txdmaen(true);
1235 w.set_stopie(true);
1236 w.set_tcie(true);
1237 });
1238 let dst = regs.txdr().as_ptr() as *mut u8;
1239
1240 self.tx_dma.as_mut().unwrap().write(buffer, dst, Default::default())
1241 };
1242
1243 let on_drop = OnDrop::new(|| {
1244 let regs = self.info.regs;
1245 regs.cr1().modify(|w| {
1246 w.set_txdmaen(false);
1247 w.set_stopie(false);
1248 w.set_tcie(false);
1249 })
1250 });
1251
1252 let state = self.state;
1253
1254 let size = poll_fn(|cx| {
1255 state.waker.register(cx.waker());
1256
1257 let isr = self.info.regs.isr().read();
1258
1259 if remaining_len == total_len {
1260 Self::slave_start(self.info, total_len.min(255), total_len > 255);
1261 remaining_len = remaining_len.saturating_sub(255);
1262 Poll::Pending
1263 } else if isr.tcr() {
1264 let is_last_slice = remaining_len <= 255;
1265 if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) {
1266 return Poll::Ready(Err(e));
1267 }
1268 remaining_len = remaining_len.saturating_sub(255);
1269 self.info.regs.cr1().modify(|w| w.set_tcie(true));
1270 Poll::Pending
1271 } else if isr.stopf() {
1272 self.info.regs.icr().write(|reg| reg.set_stopcf(true));
1273 if remaining_len > 0 {
1274 dma_transfer.request_stop();
1275 Poll::Ready(Ok(SendStatus::LeftoverBytes(remaining_len as usize)))
1276 } else {
1277 Poll::Ready(Ok(SendStatus::Done))
1278 }
1279 } else {
1280 Poll::Pending
1281 }
1282 })
1283 .await?;
1284
1285 dma_transfer.await;
1286
1287 drop(on_drop);
1288
1289 Ok(size)
1290 }
1291}
1292
736/// I2C Stop Configuration 1293/// I2C Stop Configuration
737/// 1294///
738/// Peripheral options for generating the STOP condition 1295/// Peripheral options for generating the STOP condition
@@ -857,7 +1414,7 @@ impl Timings {
857 } 1414 }
858} 1415}
859 1416
860impl<'d, M: Mode> SetConfig for I2c<'d, M> { 1417impl<'d, M: Mode> SetConfig for I2c<'d, M, Master> {
861 type Config = Hertz; 1418 type Config = Hertz;
862 type ConfigError = (); 1419 type ConfigError = ();
863 fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { 1420 fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
@@ -882,3 +1439,21 @@ impl<'d, M: Mode> SetConfig for I2c<'d, M> {
882 Ok(()) 1439 Ok(())
883 } 1440 }
884} 1441}
1442
1443impl<'d, M: Mode> SetConfig for I2c<'d, M, MultiMaster> {
1444 type Config = (Hertz, SlaveAddrConfig);
1445 type ConfigError = ();
1446 fn set_config(&mut self, (config, addr_config): &Self::Config) -> Result<(), ()> {
1447 let timings = Timings::new(self.kernel_clock, *config);
1448 self.info.regs.timingr().write(|reg| {
1449 reg.set_presc(timings.prescale);
1450 reg.set_scll(timings.scll);
1451 reg.set_sclh(timings.sclh);
1452 reg.set_sdadel(timings.sdadel);
1453 reg.set_scldel(timings.scldel);
1454 });
1455 self.init_slave(*addr_config);
1456
1457 Ok(())
1458 }
1459}
diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs
index 20cd20dca..670d8332c 100644
--- a/embassy-stm32/src/ipcc.rs
+++ b/embassy-stm32/src/ipcc.rs
@@ -104,9 +104,6 @@ impl Ipcc {
104 rcc::enable_and_reset::<IPCC>(); 104 rcc::enable_and_reset::<IPCC>();
105 IPCC::set_cpu2(true); 105 IPCC::set_cpu2(true);
106 106
107 // set RF wake-up clock = LSE
108 crate::pac::RCC.csr().modify(|w| w.set_rfwkpsel(0b01));
109
110 let regs = IPCC::regs(); 107 let regs = IPCC::regs();
111 108
112 regs.cpu(0).cr().modify(|w| { 109 regs.cpu(0).cr().modify(|w| {
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 973acc9bb..c7a33ed72 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -178,7 +178,7 @@ pub use crate::_generated::interrupt;
178macro_rules! bind_interrupts { 178macro_rules! bind_interrupts {
179 ($(#[$outer:meta])* $vis:vis struct $name:ident { 179 ($(#[$outer:meta])* $vis:vis struct $name:ident {
180 $( 180 $(
181 $(#[$inner:meta])* 181 $(#[doc = $doc:literal])*
182 $(#[cfg($cond_irq:meta)])? 182 $(#[cfg($cond_irq:meta)])?
183 $irq:ident => $( 183 $irq:ident => $(
184 $(#[cfg($cond_handler:meta)])? 184 $(#[cfg($cond_handler:meta)])?
@@ -194,13 +194,15 @@ macro_rules! bind_interrupts {
194 #[allow(non_snake_case)] 194 #[allow(non_snake_case)]
195 #[no_mangle] 195 #[no_mangle]
196 $(#[cfg($cond_irq)])? 196 $(#[cfg($cond_irq)])?
197 $(#[$inner])* 197 $(#[doc = $doc])*
198 unsafe extern "C" fn $irq() { 198 unsafe extern "C" fn $irq() {
199 $( 199 unsafe {
200 $(#[cfg($cond_handler)])? 200 $(
201 <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); 201 $(#[cfg($cond_handler)])?
202 <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
202 203
203 )* 204 )*
205 }
204 } 206 }
205 207
206 $(#[cfg($cond_irq)])? 208 $(#[cfg($cond_irq)])?
@@ -242,12 +244,12 @@ pub struct Config {
242 #[cfg(dbgmcu)] 244 #[cfg(dbgmcu)]
243 pub enable_debug_during_sleep: bool, 245 pub enable_debug_during_sleep: bool,
244 246
245 /// On low-power boards (eg. `stm32l4`, `stm32l5` and `stm32u5`), 247 /// On low-power boards (eg. `stm32l4`, `stm32l5`, `stm32wba` and `stm32u5`),
246 /// some GPIO pins are powered by an auxiliary, independent power supply (`VDDIO2`), 248 /// some GPIO pins are powered by an auxiliary, independent power supply (`VDDIO2`),
247 /// which needs to be enabled before these pins can be used. 249 /// which needs to be enabled before these pins can be used.
248 /// 250 ///
249 /// May increase power consumption. Defaults to true. 251 /// May increase power consumption. Defaults to true.
250 #[cfg(any(stm32l4, stm32l5, stm32u5))] 252 #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba))]
251 pub enable_independent_io_supply: bool, 253 pub enable_independent_io_supply: bool,
252 254
253 /// On the U5 series all analog peripherals are powered by a separate supply. 255 /// On the U5 series all analog peripherals are powered by a separate supply.
@@ -291,7 +293,7 @@ impl Default for Config {
291 rcc: Default::default(), 293 rcc: Default::default(),
292 #[cfg(dbgmcu)] 294 #[cfg(dbgmcu)]
293 enable_debug_during_sleep: true, 295 enable_debug_during_sleep: true,
294 #[cfg(any(stm32l4, stm32l5, stm32u5))] 296 #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba))]
295 enable_independent_io_supply: true, 297 enable_independent_io_supply: true,
296 #[cfg(stm32u5)] 298 #[cfg(stm32u5)]
297 enable_independent_analog_supply: true, 299 enable_independent_analog_supply: true,
@@ -540,6 +542,13 @@ fn init_hw(config: Config) -> Peripherals {
540 w.set_iosv(config.enable_independent_io_supply); 542 w.set_iosv(config.enable_independent_io_supply);
541 }); 543 });
542 } 544 }
545 #[cfg(stm32wba)]
546 {
547 use crate::pac::pwr::vals;
548 crate::pac::PWR.svmcr().modify(|w| {
549 w.set_io2sv(vals::Io2sv::B_0X1);
550 });
551 }
543 #[cfg(stm32u5)] 552 #[cfg(stm32u5)]
544 { 553 {
545 crate::pac::PWR.svmcr().modify(|w| { 554 crate::pac::PWR.svmcr().modify(|w| {
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs
index 7734365f1..d13df5a6b 100644
--- a/embassy-stm32/src/low_power.rs
+++ b/embassy-stm32/src/low_power.rs
@@ -124,15 +124,18 @@ pub enum StopMode {
124 Stop2, 124 Stop2,
125} 125}
126 126
127#[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))] 127#[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32u0))]
128use stm32_metapac::pwr::vals::Lpms; 128use stm32_metapac::pwr::vals::Lpms;
129 129
130#[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))] 130#[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32u0))]
131impl Into<Lpms> for StopMode { 131impl Into<Lpms> for StopMode {
132 fn into(self) -> Lpms { 132 fn into(self) -> Lpms {
133 match self { 133 match self {
134 StopMode::Stop1 => Lpms::STOP1, 134 StopMode::Stop1 => Lpms::STOP1,
135 #[cfg(not(stm32wba))]
135 StopMode::Stop2 => Lpms::STOP2, 136 StopMode::Stop2 => Lpms::STOP2,
137 #[cfg(stm32wba)]
138 StopMode::Stop2 => Lpms::STOP1, // TODO: WBA has no STOP2?
136 } 139 }
137 } 140 }
138} 141}
@@ -198,7 +201,7 @@ impl Executor {
198 201
199 #[allow(unused_variables)] 202 #[allow(unused_variables)]
200 fn configure_stop(&mut self, stop_mode: StopMode) { 203 fn configure_stop(&mut self, stop_mode: StopMode) {
201 #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))] 204 #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba))]
202 crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); 205 crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into()));
203 #[cfg(stm32h5)] 206 #[cfg(stm32h5)]
204 crate::pac::PWR.pmcr().modify(|v| { 207 crate::pac::PWR.pmcr().modify(|v| {
diff --git a/embassy-stm32/src/lptim/timer/mod.rs b/embassy-stm32/src/lptim/timer/mod.rs
index a629be62b..648da5940 100644
--- a/embassy-stm32/src/lptim/timer/mod.rs
+++ b/embassy-stm32/src/lptim/timer/mod.rs
@@ -115,6 +115,31 @@ impl<'d, T: Instance> Timer<'d, T> {
115 .ccmr(0) 115 .ccmr(0)
116 .modify(|w| w.set_ccsel(channel.index(), direction.into())); 116 .modify(|w| w.set_ccsel(channel.index(), direction.into()));
117 } 117 }
118
119 /// Enable the timer interrupt.
120 pub fn enable_interrupt(&self) {
121 T::regs().dier().modify(|w| w.set_arrmie(true));
122 }
123
124 /// Disable the timer interrupt.
125 pub fn disable_interrupt(&self) {
126 T::regs().dier().modify(|w| w.set_arrmie(false));
127 }
128
129 /// Check if the timer interrupt is enabled.
130 pub fn is_interrupt_enabled(&self) -> bool {
131 T::regs().dier().read().arrmie()
132 }
133
134 /// Check if the timer interrupt is pending.
135 pub fn is_interrupt_pending(&self) -> bool {
136 T::regs().isr().read().arrm()
137 }
138
139 /// Clear the timer interrupt.
140 pub fn clear_interrupt(&self) {
141 T::regs().icr().write(|w| w.set_arrmcf(true));
142 }
118} 143}
119 144
120#[cfg(not(any(lptim_v2a, lptim_v2b)))] 145#[cfg(not(any(lptim_v2a, lptim_v2b)))]
@@ -128,4 +153,29 @@ impl<'d, T: Instance> Timer<'d, T> {
128 pub fn get_compare_value(&self) -> u16 { 153 pub fn get_compare_value(&self) -> u16 {
129 T::regs().cmp().read().cmp() 154 T::regs().cmp().read().cmp()
130 } 155 }
156
157 /// Enable the timer interrupt.
158 pub fn enable_interrupt(&self) {
159 T::regs().ier().modify(|w| w.set_arrmie(true));
160 }
161
162 /// Disable the timer interrupt.
163 pub fn disable_interrupt(&self) {
164 T::regs().ier().modify(|w| w.set_arrmie(false));
165 }
166
167 /// Check if the timer interrupt is enabled.
168 pub fn is_interrupt_enabled(&self) -> bool {
169 T::regs().ier().read().arrmie()
170 }
171
172 /// Check if the timer interrupt is pending.
173 pub fn is_interrupt_pending(&self) -> bool {
174 T::regs().isr().read().arrm()
175 }
176
177 /// Clear the timer interrupt.
178 pub fn clear_interrupt(&self) {
179 T::regs().icr().write(|w| w.set_arrmcf(true));
180 }
131} 181}
diff --git a/embassy-stm32/src/macros.rs b/embassy-stm32/src/macros.rs
index 7526bb180..3a0b490ba 100644
--- a/embassy-stm32/src/macros.rs
+++ b/embassy-stm32/src/macros.rs
@@ -81,17 +81,28 @@ macro_rules! dma_trait {
81 /// Note: in some chips, ST calls this the "channel", and calls channels "streams". 81 /// Note: in some chips, ST calls this the "channel", and calls channels "streams".
82 /// `embassy-stm32` always uses the "channel" and "request number" names. 82 /// `embassy-stm32` always uses the "channel" and "request number" names.
83 fn request(&self) -> crate::dma::Request; 83 fn request(&self) -> crate::dma::Request;
84 #[doc = "Remap the DMA channel"]
85 fn remap(&self);
84 } 86 }
85 }; 87 };
86} 88}
87 89
88#[allow(unused)] 90#[allow(unused)]
89macro_rules! dma_trait_impl { 91macro_rules! dma_trait_impl {
90 (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $channel:ident, $request:expr) => { 92 (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $channel:ident, $request:expr, $remap:expr) => {
91 impl crate::$mod::$trait<crate::peripherals::$instance $(, crate::$mod::$mode)?> for crate::peripherals::$channel { 93 impl crate::$mod::$trait<crate::peripherals::$instance $(, crate::$mod::$mode)?> for crate::peripherals::$channel {
92 fn request(&self) -> crate::dma::Request { 94 fn request(&self) -> crate::dma::Request {
93 $request 95 $request
94 } 96 }
97
98 fn remap(&self) {
99 critical_section::with(|_| {
100 #[allow(unused_unsafe)]
101 unsafe {
102 $remap;
103 }
104 });
105 }
95 } 106 }
96 }; 107 };
97} 108}
@@ -111,6 +122,7 @@ macro_rules! new_dma_nonopt {
111macro_rules! new_dma { 122macro_rules! new_dma {
112 ($name:ident) => {{ 123 ($name:ident) => {{
113 let dma = $name; 124 let dma = $name;
125 dma.remap();
114 let request = dma.request(); 126 let request = dma.request();
115 Some(crate::dma::ChannelAndRequest { 127 Some(crate::dma::ChannelAndRequest {
116 channel: dma.into(), 128 channel: dma.into(),
diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs
index 2eb2e61c1..e36719ef3 100644
--- a/embassy-stm32/src/opamp.rs
+++ b/embassy-stm32/src/opamp.rs
@@ -4,10 +4,12 @@
4use embassy_hal_internal::PeripheralType; 4use embassy_hal_internal::PeripheralType;
5 5
6use crate::pac::opamp::vals::*; 6use crate::pac::opamp::vals::*;
7#[cfg(not(any(stm32g4, stm32f3)))]
8use crate::rcc::RccInfo;
7use crate::Peri; 9use crate::Peri;
8 10
9/// Performs a busy-wait delay for a specified number of microseconds. 11/// Performs a busy-wait delay for a specified number of microseconds.
10#[cfg(opamp_g4)] 12#[cfg(opamp_v5)]
11fn blocking_delay_ms(ms: u32) { 13fn blocking_delay_ms(ms: u32) {
12 #[cfg(feature = "time")] 14 #[cfg(feature = "time")]
13 embassy_time::block_for(embassy_time::Duration::from_millis(ms as u64)); 15 embassy_time::block_for(embassy_time::Duration::from_millis(ms as u64));
@@ -23,13 +25,13 @@ pub enum OpAmpGain {
23 Mul4, 25 Mul4,
24 Mul8, 26 Mul8,
25 Mul16, 27 Mul16,
26 #[cfg(opamp_g4)] 28 #[cfg(opamp_v5)]
27 Mul32, 29 Mul32,
28 #[cfg(opamp_g4)] 30 #[cfg(opamp_v5)]
29 Mul64, 31 Mul64,
30} 32}
31 33
32#[cfg(opamp_g4)] 34#[cfg(opamp_v5)]
33enum OpAmpDifferentialPair { 35enum OpAmpDifferentialPair {
34 P, 36 P,
35 N, 37 N,
@@ -53,7 +55,7 @@ pub struct OpAmpOutput<'d, T: Instance> {
53/// OpAmp internal outputs, wired directly to ADC inputs. 55/// OpAmp internal outputs, wired directly to ADC inputs.
54/// 56///
55/// This struct can be used as an ADC input. 57/// This struct can be used as an ADC input.
56#[cfg(opamp_g4)] 58#[cfg(opamp_v5)]
57pub struct OpAmpInternalOutput<'d, T: Instance> { 59pub struct OpAmpInternalOutput<'d, T: Instance> {
58 _inner: &'d OpAmp<'d, T>, 60 _inner: &'d OpAmp<'d, T>,
59} 61}
@@ -67,8 +69,10 @@ impl<'d, T: Instance> OpAmp<'d, T> {
67 /// Create a new driver instance. 69 /// Create a new driver instance.
68 /// 70 ///
69 /// Does not enable the opamp, but does set the speed mode on some families. 71 /// Does not enable the opamp, but does set the speed mode on some families.
70 pub fn new(opamp: Peri<'d, T>, #[cfg(opamp_g4)] speed: OpAmpSpeed) -> Self { 72 pub fn new(opamp: Peri<'d, T>, #[cfg(opamp_v5)] speed: OpAmpSpeed) -> Self {
71 #[cfg(opamp_g4)] 73 #[cfg(not(any(stm32g4, stm32f3)))]
74 T::info().rcc.enable_and_reset();
75 #[cfg(opamp_v5)]
72 T::regs().csr().modify(|w| { 76 T::regs().csr().modify(|w| {
73 w.set_opahsm(speed == OpAmpSpeed::HighSpeed); 77 w.set_opahsm(speed == OpAmpSpeed::HighSpeed);
74 }); 78 });
@@ -94,15 +98,15 @@ impl<'d, T: Instance> OpAmp<'d, T> {
94 in_pin.set_as_analog(); 98 in_pin.set_as_analog();
95 out_pin.set_as_analog(); 99 out_pin.set_as_analog();
96 100
97 #[cfg(opamp_g4)] 101 #[cfg(opamp_v5)]
98 let vm_sel = VmSel::OUTPUT; 102 let vm_sel = VmSel::OUTPUT;
99 #[cfg(not(opamp_g4))] 103 #[cfg(not(opamp_v5))]
100 let vm_sel = VmSel::from_bits(0b11); 104 let vm_sel = VmSel::from_bits(0b11);
101 105
102 T::regs().csr().modify(|w| { 106 T::regs().csr().modify(|w| {
103 w.set_vp_sel(VpSel::from_bits(in_pin.channel())); 107 w.set_vp_sel(VpSel::from_bits(in_pin.channel()));
104 w.set_vm_sel(vm_sel); 108 w.set_vm_sel(vm_sel);
105 #[cfg(opamp_g4)] 109 #[cfg(opamp_v5)]
106 w.set_opaintoen(false); 110 w.set_opaintoen(false);
107 w.set_opampen(true); 111 w.set_opampen(true);
108 }); 112 });
@@ -129,12 +133,12 @@ impl<'d, T: Instance> OpAmp<'d, T> {
129 in_pin.set_as_analog(); 133 in_pin.set_as_analog();
130 out_pin.set_as_analog(); 134 out_pin.set_as_analog();
131 135
132 #[cfg(opamp_g4)] 136 #[cfg(opamp_v5)]
133 let vm_sel = VmSel::PGA; 137 let vm_sel = VmSel::PGA;
134 #[cfg(not(opamp_g4))] 138 #[cfg(not(opamp_v5))]
135 let vm_sel = VmSel::from_bits(0b10); 139 let vm_sel = VmSel::from_bits(0b10);
136 140
137 #[cfg(opamp_g4)] 141 #[cfg(opamp_v5)]
138 let pga_gain = match gain { 142 let pga_gain = match gain {
139 OpAmpGain::Mul2 => PgaGain::GAIN2, 143 OpAmpGain::Mul2 => PgaGain::GAIN2,
140 OpAmpGain::Mul4 => PgaGain::GAIN4, 144 OpAmpGain::Mul4 => PgaGain::GAIN4,
@@ -143,7 +147,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
143 OpAmpGain::Mul32 => PgaGain::GAIN32, 147 OpAmpGain::Mul32 => PgaGain::GAIN32,
144 OpAmpGain::Mul64 => PgaGain::GAIN64, 148 OpAmpGain::Mul64 => PgaGain::GAIN64,
145 }; 149 };
146 #[cfg(not(opamp_g4))] 150 #[cfg(not(opamp_v5))]
147 let pga_gain = PgaGain::from_bits(match gain { 151 let pga_gain = PgaGain::from_bits(match gain {
148 OpAmpGain::Mul2 => 0b00, 152 OpAmpGain::Mul2 => 0b00,
149 OpAmpGain::Mul4 => 0b01, 153 OpAmpGain::Mul4 => 0b01,
@@ -155,7 +159,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
155 w.set_vp_sel(VpSel::from_bits(in_pin.channel())); 159 w.set_vp_sel(VpSel::from_bits(in_pin.channel()));
156 w.set_vm_sel(vm_sel); 160 w.set_vm_sel(vm_sel);
157 w.set_pga_gain(pga_gain); 161 w.set_pga_gain(pga_gain);
158 #[cfg(opamp_g4)] 162 #[cfg(opamp_v5)]
159 w.set_opaintoen(false); 163 w.set_opaintoen(false);
160 w.set_opampen(true); 164 w.set_opampen(true);
161 }); 165 });
@@ -170,7 +174,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
170 /// preventing it being used elsewhere. The `OpAmpOutput` can then be 174 /// preventing it being used elsewhere. The `OpAmpOutput` can then be
171 /// directly used as an ADC input. The opamp will be disabled when the 175 /// directly used as an ADC input. The opamp will be disabled when the
172 /// [`OpAmpOutput`] is dropped. 176 /// [`OpAmpOutput`] is dropped.
173 #[cfg(opamp_g4)] 177 #[cfg(opamp_v5)]
174 pub fn buffer_dac(&mut self, out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>) -> OpAmpOutput<'_, T> { 178 pub fn buffer_dac(&mut self, out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>) -> OpAmpOutput<'_, T> {
175 out_pin.set_as_analog(); 179 out_pin.set_as_analog();
176 180
@@ -194,7 +198,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
194 /// 198 ///
195 /// The returned `OpAmpInternalOutput` struct may be used as an ADC input. 199 /// The returned `OpAmpInternalOutput` struct may be used as an ADC input.
196 /// The opamp output will be disabled when it is dropped. 200 /// The opamp output will be disabled when it is dropped.
197 #[cfg(opamp_g4)] 201 #[cfg(opamp_v5)]
198 pub fn buffer_int( 202 pub fn buffer_int(
199 &mut self, 203 &mut self,
200 pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>, 204 pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>,
@@ -204,7 +208,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
204 T::regs().csr().modify(|w| { 208 T::regs().csr().modify(|w| {
205 w.set_vp_sel(VpSel::from_bits(pin.channel())); 209 w.set_vp_sel(VpSel::from_bits(pin.channel()));
206 w.set_vm_sel(VmSel::OUTPUT); 210 w.set_vm_sel(VmSel::OUTPUT);
207 #[cfg(opamp_g4)] 211 #[cfg(opamp_v5)]
208 w.set_opaintoen(true); 212 w.set_opaintoen(true);
209 w.set_opampen(true); 213 w.set_opampen(true);
210 }); 214 });
@@ -220,7 +224,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
220 /// 224 ///
221 /// The returned `OpAmpInternalOutput` struct may be used as an ADC input. 225 /// The returned `OpAmpInternalOutput` struct may be used as an ADC input.
222 /// The opamp output will be disabled when it is dropped. 226 /// The opamp output will be disabled when it is dropped.
223 #[cfg(opamp_g4)] 227 #[cfg(opamp_v5)]
224 pub fn pga_int( 228 pub fn pga_int(
225 &mut self, 229 &mut self,
226 pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>, 230 pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>,
@@ -257,7 +261,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
257 /// 261 ///
258 /// The returned `OpAmpInternalOutput` struct may be used as an ADC 262 /// The returned `OpAmpInternalOutput` struct may be used as an ADC
259 /// input. The opamp output will be disabled when it is dropped. 263 /// input. The opamp output will be disabled when it is dropped.
260 #[cfg(opamp_g4)] 264 #[cfg(opamp_v5)]
261 pub fn standalone_dac_int( 265 pub fn standalone_dac_int(
262 &mut self, 266 &mut self,
263 m_pin: Peri<'_, impl InvertingPin<T> + crate::gpio::Pin>, 267 m_pin: Peri<'_, impl InvertingPin<T> + crate::gpio::Pin>,
@@ -285,7 +289,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
285 /// The output pin is held within the returned [`OpAmpOutput`] struct, 289 /// The output pin is held within the returned [`OpAmpOutput`] struct,
286 /// preventing it being used elsewhere. The opamp will be disabled when 290 /// preventing it being used elsewhere. The opamp will be disabled when
287 /// the [`OpAmpOutput`] is dropped. 291 /// the [`OpAmpOutput`] is dropped.
288 #[cfg(opamp_g4)] 292 #[cfg(opamp_v5)]
289 pub fn standalone_dac_ext( 293 pub fn standalone_dac_ext(
290 &mut self, 294 &mut self,
291 m_pin: Peri<'_, impl InvertingPin<T> + crate::gpio::Pin>, 295 m_pin: Peri<'_, impl InvertingPin<T> + crate::gpio::Pin>,
@@ -315,7 +319,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
315 /// The output pin is held within the returned [`OpAmpOutput`] struct, 319 /// The output pin is held within the returned [`OpAmpOutput`] struct,
316 /// preventing it being used elsewhere. The opamp will be disabled when 320 /// preventing it being used elsewhere. The opamp will be disabled when
317 /// the [`OpAmpOutput`] is dropped. 321 /// the [`OpAmpOutput`] is dropped.
318 #[cfg(opamp_g4)] 322 #[cfg(opamp_v5)]
319 pub fn standalone_ext( 323 pub fn standalone_ext(
320 &mut self, 324 &mut self,
321 p_pin: Peri<'d, impl NonInvertingPin<T> + crate::gpio::Pin>, 325 p_pin: Peri<'d, impl NonInvertingPin<T> + crate::gpio::Pin>,
@@ -346,7 +350,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
346 /// 350 ///
347 /// The returned `OpAmpOutput` struct may be used as an ADC 351 /// The returned `OpAmpOutput` struct may be used as an ADC
348 /// input. The opamp output will be disabled when it is dropped. 352 /// input. The opamp output will be disabled when it is dropped.
349 #[cfg(opamp_g4)] 353 #[cfg(opamp_v5)]
350 pub fn standalone_int( 354 pub fn standalone_int(
351 &mut self, 355 &mut self,
352 p_pin: Peri<'d, impl NonInvertingPin<T> + crate::gpio::Pin>, 356 p_pin: Peri<'d, impl NonInvertingPin<T> + crate::gpio::Pin>,
@@ -374,7 +378,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
374 /// while for high-speed mode, only the P differential pair is calibrated. 378 /// while for high-speed mode, only the P differential pair is calibrated.
375 /// 379 ///
376 /// Calibrating a differential pair requires waiting 12ms in the worst case (binary method). 380 /// Calibrating a differential pair requires waiting 12ms in the worst case (binary method).
377 #[cfg(opamp_g4)] 381 #[cfg(opamp_v5)]
378 pub fn calibrate(&mut self) { 382 pub fn calibrate(&mut self) {
379 T::regs().csr().modify(|w| { 383 T::regs().csr().modify(|w| {
380 w.set_opampen(true); 384 w.set_opampen(true);
@@ -403,7 +407,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
403 /// The calibration range is from 0 to 31. 407 /// The calibration range is from 0 to 31.
404 /// 408 ///
405 /// The result is stored in the OPAMP_CSR register. 409 /// The result is stored in the OPAMP_CSR register.
406 #[cfg(opamp_g4)] 410 #[cfg(opamp_v5)]
407 fn calibrate_differential_pair(&mut self, pair: OpAmpDifferentialPair) { 411 fn calibrate_differential_pair(&mut self, pair: OpAmpDifferentialPair) {
408 let mut low = 0; 412 let mut low = 0;
409 let mut high = 31; 413 let mut high = 31;
@@ -452,6 +456,13 @@ impl<'d, T: Instance> OpAmp<'d, T> {
452 } 456 }
453} 457}
454 458
459#[cfg(not(any(stm32g4, stm32f3)))]
460impl<'d, T: Instance> Drop for OpAmp<'d, T> {
461 fn drop(&mut self) {
462 T::info().rcc.disable();
463 }
464}
465
455impl<'d, T: Instance> Drop for OpAmpOutput<'d, T> { 466impl<'d, T: Instance> Drop for OpAmpOutput<'d, T> {
456 fn drop(&mut self) { 467 fn drop(&mut self) {
457 T::regs().csr().modify(|w| { 468 T::regs().csr().modify(|w| {
@@ -460,7 +471,7 @@ impl<'d, T: Instance> Drop for OpAmpOutput<'d, T> {
460 } 471 }
461} 472}
462 473
463#[cfg(opamp_g4)] 474#[cfg(opamp_v5)]
464impl<'d, T: Instance> Drop for OpAmpInternalOutput<'d, T> { 475impl<'d, T: Instance> Drop for OpAmpInternalOutput<'d, T> {
465 fn drop(&mut self) { 476 fn drop(&mut self) {
466 T::regs().csr().modify(|w| { 477 T::regs().csr().modify(|w| {
@@ -469,7 +480,14 @@ impl<'d, T: Instance> Drop for OpAmpInternalOutput<'d, T> {
469 } 480 }
470} 481}
471 482
483#[cfg(not(any(stm32g4, stm32f3)))]
484pub(crate) struct Info {
485 rcc: RccInfo,
486}
487
472pub(crate) trait SealedInstance { 488pub(crate) trait SealedInstance {
489 #[cfg(not(any(stm32g4, stm32f3)))]
490 fn info() -> &'static Info;
473 fn regs() -> crate::pac::opamp::Opamp; 491 fn regs() -> crate::pac::opamp::Opamp;
474} 492}
475 493
@@ -545,7 +563,7 @@ foreach_peripheral!(
545 }; 563 };
546); 564);
547 565
548#[cfg(opamp_g4)] 566#[cfg(opamp_v5)]
549macro_rules! impl_opamp_internal_output { 567macro_rules! impl_opamp_internal_output {
550 ($inst:ident, $adc:ident, $ch:expr) => { 568 ($inst:ident, $adc:ident, $ch:expr) => {
551 foreach_adc!( 569 foreach_adc!(
@@ -567,7 +585,7 @@ macro_rules! impl_opamp_internal_output {
567 }; 585 };
568} 586}
569 587
570#[cfg(opamp_g4)] 588#[cfg(opamp_v5)]
571foreach_peripheral!( 589foreach_peripheral!(
572 (opamp, OPAMP1) => { 590 (opamp, OPAMP1) => {
573 impl_opamp_internal_output!(OPAMP1, ADC1, 13); 591 impl_opamp_internal_output!(OPAMP1, ADC1, 13);
@@ -600,6 +618,15 @@ foreach_peripheral!(
600foreach_peripheral! { 618foreach_peripheral! {
601 (opamp, $inst:ident) => { 619 (opamp, $inst:ident) => {
602 impl SealedInstance for crate::peripherals::$inst { 620 impl SealedInstance for crate::peripherals::$inst {
621 // G4 and F3 use SYSCFGEN, which is always enabled
622 #[cfg(not(any(stm32g4, stm32f3)))]
623 fn info() -> &'static Info {
624 use crate::rcc::SealedRccPeripheral;
625 static INFO: Info = Info {
626 rcc: crate::peripherals::$inst::RCC_INFO,
627 };
628 &INFO
629 }
603 fn regs() -> crate::pac::opamp::Opamp { 630 fn regs() -> crate::pac::opamp::Opamp {
604 crate::pac::$inst 631 crate::pac::$inst
605 } 632 }
diff --git a/embassy-stm32/src/qspi/enums.rs b/embassy-stm32/src/qspi/enums.rs
index 9ec4c1b43..fa5e36d06 100644
--- a/embassy-stm32/src/qspi/enums.rs
+++ b/embassy-stm32/src/qspi/enums.rs
@@ -331,3 +331,19 @@ impl From<DummyCycles> for u8 {
331 } 331 }
332 } 332 }
333} 333}
334
335#[allow(missing_docs)]
336#[derive(Copy, Clone)]
337pub enum SampleShifting {
338 None,
339 HalfCycle,
340}
341
342impl From<SampleShifting> for bool {
343 fn from(value: SampleShifting) -> Self {
344 match value {
345 SampleShifting::None => false,
346 SampleShifting::HalfCycle => true,
347 }
348 }
349}
diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs
index 0df057c53..1e20d7cd3 100644
--- a/embassy-stm32/src/qspi/mod.rs
+++ b/embassy-stm32/src/qspi/mod.rs
@@ -58,6 +58,8 @@ pub struct Config {
58 pub fifo_threshold: FIFOThresholdLevel, 58 pub fifo_threshold: FIFOThresholdLevel,
59 /// Minimum number of cycles that chip select must be high between issued commands 59 /// Minimum number of cycles that chip select must be high between issued commands
60 pub cs_high_time: ChipSelectHighTime, 60 pub cs_high_time: ChipSelectHighTime,
61 /// Shift sampling point of input data (none, or half-cycle)
62 pub sample_shifting: SampleShifting,
61} 63}
62 64
63impl Default for Config { 65impl Default for Config {
@@ -68,6 +70,7 @@ impl Default for Config {
68 prescaler: 128, 70 prescaler: 128,
69 fifo_threshold: FIFOThresholdLevel::_17Bytes, 71 fifo_threshold: FIFOThresholdLevel::_17Bytes,
70 cs_high_time: ChipSelectHighTime::_5Cycle, 72 cs_high_time: ChipSelectHighTime::_5Cycle,
73 sample_shifting: SampleShifting::None,
71 } 74 }
72 } 75 }
73} 76}
@@ -120,7 +123,7 @@ impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> {
120 T::REGS.cr().modify(|w| { 123 T::REGS.cr().modify(|w| {
121 w.set_en(true); 124 w.set_en(true);
122 //w.set_tcen(false); 125 //w.set_tcen(false);
123 w.set_sshift(false); 126 w.set_sshift(config.sample_shifting.into());
124 w.set_fthres(config.fifo_threshold.into()); 127 w.set_fthres(config.fifo_threshold.into());
125 w.set_prescaler(config.prescaler); 128 w.set_prescaler(config.prescaler);
126 w.set_fsel(fsel.into()); 129 w.set_fsel(fsel.into());
diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs
index cac2a9149..c2295bab6 100644
--- a/embassy-stm32/src/rcc/c0.rs
+++ b/embassy-stm32/src/rcc/c0.rs
@@ -190,6 +190,8 @@ pub(crate) unsafe fn init(config: Config) {
190 // TODO 190 // TODO
191 lsi: None, 191 lsi: None,
192 lse: None, 192 lse: None,
193 #[cfg(crs)]
194 hsi48: None,
193 ); 195 );
194 196
195 RCC.ccipr() 197 RCC.ccipr()
diff --git a/embassy-stm32/src/rcc/hsi48.rs b/embassy-stm32/src/rcc/hsi48.rs
index 3ea5c96c9..49be4af5e 100644
--- a/embassy-stm32/src/rcc/hsi48.rs
+++ b/embassy-stm32/src/rcc/hsi48.rs
@@ -39,9 +39,9 @@ pub(crate) fn init_hsi48(config: Hsi48Config) -> Hertz {
39 }); 39 });
40 40
41 // Enable HSI48 41 // Enable HSI48
42 #[cfg(not(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32h7rs, stm32u5, stm32wba, stm32f0)))] 42 #[cfg(not(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32h7rs, stm32u5, stm32wba, stm32f0, stm32c071)))]
43 let r = RCC.crrcr(); 43 let r = RCC.crrcr();
44 #[cfg(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32h7rs, stm32u5, stm32wba))] 44 #[cfg(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32h7rs, stm32u5, stm32wba, stm32c071))]
45 let r = RCC.cr(); 45 let r = RCC.cr();
46 #[cfg(any(stm32f0))] 46 #[cfg(any(stm32f0))]
47 let r = RCC.cr2(); 47 let r = RCC.cr2();
diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs
index c50e071fb..96e628b1a 100644
--- a/embassy-stm32/src/rcc/mco.rs
+++ b/embassy-stm32/src/rcc/mco.rs
@@ -74,7 +74,7 @@ macro_rules! impl_peri {
74 }; 74 };
75} 75}
76 76
77#[cfg(any(rcc_c0, rcc_g0, rcc_u0))] 77#[cfg(any(rcc_c0, rcc_c0v2, rcc_g0x0, rcc_g0x1, rcc_u0))]
78#[allow(unused_imports)] 78#[allow(unused_imports)]
79use self::{McoSource as Mco1Source, McoSource as Mco2Source}; 79use self::{McoSource as Mco1Source, McoSource as Mco2Source};
80 80
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index 3733fed56..c41f81816 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -95,6 +95,15 @@ pub(crate) unsafe fn get_freqs() -> &'static Clocks {
95 unwrap!(CLOCK_FREQS_PTR.load(core::sync::atomic::Ordering::SeqCst).as_ref()).assume_init_ref() 95 unwrap!(CLOCK_FREQS_PTR.load(core::sync::atomic::Ordering::SeqCst).as_ref()).assume_init_ref()
96} 96}
97 97
98/// Get the current clock configuration of the chip.
99pub fn clocks<'a>(_rcc: &'a crate::Peri<'a, crate::peripherals::RCC>) -> &'a Clocks {
100 // Safety: the existence of a `Peri<RCC>` means that `rcc::init()`
101 // has already been called, so `CLOCK_FREQS` must be initialized.
102 // The clocks could be modified again by `reinit()`, but reinit
103 // (for this reason) requires an exclusive reference to `Peri<RCC>`.
104 unsafe { get_freqs() }
105}
106
98pub(crate) trait SealedRccPeripheral { 107pub(crate) trait SealedRccPeripheral {
99 fn frequency() -> Hertz; 108 fn frequency() -> Hertz;
100 #[allow(dead_code)] 109 #[allow(dead_code)]
@@ -381,7 +390,7 @@ pub fn disable<T: RccPeripheral>() {
381/// 390///
382/// This should only be called after `init`. 391/// This should only be called after `init`.
383#[cfg(not(feature = "_dual-core"))] 392#[cfg(not(feature = "_dual-core"))]
384pub fn reinit(config: Config) { 393pub fn reinit<'a>(config: Config, _rcc: &'a mut crate::Peri<'a, crate::peripherals::RCC>) {
385 critical_section::with(|cs| init_rcc(cs, config)) 394 critical_section::with(|cs| init_rcc(cs, config))
386} 395}
387 396
diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs
index 97eb2eb6d..06895a99a 100644
--- a/embassy-stm32/src/rcc/u5.rs
+++ b/embassy-stm32/src/rcc/u5.rs
@@ -5,7 +5,7 @@ pub use crate::pac::rcc::vals::{
5 Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, 5 Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul,
6 Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, 6 Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk,
7}; 7};
8use crate::pac::rcc::vals::{Hseext, Msirgsel, Pllmboost, Pllrge}; 8use crate::pac::rcc::vals::{Hseext, Msipllfast, Msipllsel, Msirgsel, Pllmboost, Pllrge};
9#[cfg(all(peri_usb_otg_hs))] 9#[cfg(all(peri_usb_otg_hs))]
10pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; 10pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG};
11use crate::pac::{FLASH, PWR, RCC}; 11use crate::pac::{FLASH, PWR, RCC};
@@ -64,6 +64,46 @@ pub struct Pll {
64 pub divr: Option<PllDiv>, 64 pub divr: Option<PllDiv>,
65} 65}
66 66
67#[derive(Clone, Copy, PartialEq)]
68pub enum MsiAutoCalibration {
69 /// MSI auto-calibration is disabled
70 Disabled,
71 /// MSIS is given priority for auto-calibration
72 MSIS,
73 /// MSIK is given priority for auto-calibration
74 MSIK,
75 /// MSIS with fast mode (always on)
76 MsisFast,
77 /// MSIK with fast mode (always on)
78 MsikFast,
79}
80
81impl MsiAutoCalibration {
82 const fn default() -> Self {
83 MsiAutoCalibration::Disabled
84 }
85
86 fn base_mode(&self) -> Self {
87 match self {
88 MsiAutoCalibration::Disabled => MsiAutoCalibration::Disabled,
89 MsiAutoCalibration::MSIS => MsiAutoCalibration::MSIS,
90 MsiAutoCalibration::MSIK => MsiAutoCalibration::MSIK,
91 MsiAutoCalibration::MsisFast => MsiAutoCalibration::MSIS,
92 MsiAutoCalibration::MsikFast => MsiAutoCalibration::MSIK,
93 }
94 }
95
96 fn is_fast(&self) -> bool {
97 matches!(self, MsiAutoCalibration::MsisFast | MsiAutoCalibration::MsikFast)
98 }
99}
100
101impl Default for MsiAutoCalibration {
102 fn default() -> Self {
103 Self::default()
104 }
105}
106
67#[derive(Clone, Copy)] 107#[derive(Clone, Copy)]
68pub struct Config { 108pub struct Config {
69 // base clock sources 109 // base clock sources
@@ -95,6 +135,7 @@ pub struct Config {
95 135
96 /// Per-peripheral kernel clock selection muxes 136 /// Per-peripheral kernel clock selection muxes
97 pub mux: super::mux::ClockMux, 137 pub mux: super::mux::ClockMux,
138 pub auto_calibration: MsiAutoCalibration,
98} 139}
99 140
100impl Config { 141impl Config {
@@ -116,6 +157,7 @@ impl Config {
116 voltage_range: VoltageScale::RANGE1, 157 voltage_range: VoltageScale::RANGE1,
117 ls: crate::rcc::LsConfig::new(), 158 ls: crate::rcc::LsConfig::new(),
118 mux: super::mux::ClockMux::default(), 159 mux: super::mux::ClockMux::default(),
160 auto_calibration: MsiAutoCalibration::default(),
119 } 161 }
120 } 162 }
121} 163}
@@ -131,7 +173,42 @@ pub(crate) unsafe fn init(config: Config) {
131 PWR.vosr().modify(|w| w.set_vos(config.voltage_range)); 173 PWR.vosr().modify(|w| w.set_vos(config.voltage_range));
132 while !PWR.vosr().read().vosrdy() {} 174 while !PWR.vosr().read().vosrdy() {}
133 175
134 let msis = config.msis.map(|range| { 176 let lse_calibration_freq = if config.auto_calibration != MsiAutoCalibration::Disabled {
177 // LSE must be configured and peripherals clocked for MSI auto-calibration
178 let lse_config = config
179 .ls
180 .lse
181 .clone()
182 .expect("LSE must be configured for MSI auto-calibration");
183 assert!(lse_config.peripherals_clocked);
184
185 // Expect less than +/- 5% deviation for LSE frequency
186 if (31_100..=34_400).contains(&lse_config.frequency.0) {
187 // Check that the calibration is applied to an active clock
188 match (
189 config.auto_calibration.base_mode(),
190 config.msis.is_some(),
191 config.msik.is_some(),
192 ) {
193 (MsiAutoCalibration::MSIS, true, _) => {
194 // MSIS is active and using LSE for auto-calibration
195 Some(lse_config.frequency)
196 }
197 (MsiAutoCalibration::MSIK, _, true) => {
198 // MSIK is active and using LSE for auto-calibration
199 Some(lse_config.frequency)
200 }
201 // improper configuration
202 _ => panic!("MSIx auto-calibration is enabled for a source that has not been configured."),
203 }
204 } else {
205 panic!("LSE frequency more than 5% off from 32.768 kHz, cannot use for MSI auto-calibration");
206 }
207 } else {
208 None
209 };
210
211 let mut msis = config.msis.map(|range| {
135 // Check MSI output per RM0456 § 11.4.10 212 // Check MSI output per RM0456 § 11.4.10
136 match config.voltage_range { 213 match config.voltage_range {
137 VoltageScale::RANGE4 => { 214 VoltageScale::RANGE4 => {
@@ -156,11 +233,21 @@ pub(crate) unsafe fn init(config: Config) {
156 w.set_msipllen(false); 233 w.set_msipllen(false);
157 w.set_msison(true); 234 w.set_msison(true);
158 }); 235 });
236 let msis = if let (Some(freq), MsiAutoCalibration::MSIS) =
237 (lse_calibration_freq, config.auto_calibration.base_mode())
238 {
239 // Enable the MSIS auto-calibration feature
240 RCC.cr().modify(|w| w.set_msipllsel(Msipllsel::MSIS));
241 RCC.cr().modify(|w| w.set_msipllen(true));
242 calculate_calibrated_msi_frequency(range, freq)
243 } else {
244 msirange_to_hertz(range)
245 };
159 while !RCC.cr().read().msisrdy() {} 246 while !RCC.cr().read().msisrdy() {}
160 msirange_to_hertz(range) 247 msis
161 }); 248 });
162 249
163 let msik = config.msik.map(|range| { 250 let mut msik = config.msik.map(|range| {
164 // Check MSI output per RM0456 § 11.4.10 251 // Check MSI output per RM0456 § 11.4.10
165 match config.voltage_range { 252 match config.voltage_range {
166 VoltageScale::RANGE4 => { 253 VoltageScale::RANGE4 => {
@@ -184,10 +271,44 @@ pub(crate) unsafe fn init(config: Config) {
184 RCC.cr().modify(|w| { 271 RCC.cr().modify(|w| {
185 w.set_msikon(true); 272 w.set_msikon(true);
186 }); 273 });
274 let msik = if let (Some(freq), MsiAutoCalibration::MSIK) =
275 (lse_calibration_freq, config.auto_calibration.base_mode())
276 {
277 // Enable the MSIK auto-calibration feature
278 RCC.cr().modify(|w| w.set_msipllsel(Msipllsel::MSIK));
279 RCC.cr().modify(|w| w.set_msipllen(true));
280 calculate_calibrated_msi_frequency(range, freq)
281 } else {
282 msirange_to_hertz(range)
283 };
187 while !RCC.cr().read().msikrdy() {} 284 while !RCC.cr().read().msikrdy() {}
188 msirange_to_hertz(range) 285 msik
189 }); 286 });
190 287
288 if let Some(lse_freq) = lse_calibration_freq {
289 // If both MSIS and MSIK are enabled, we need to check if they are using the same internal source.
290 if let (Some(msis_range), Some(msik_range)) = (config.msis, config.msik) {
291 if (msis_range as u8 >> 2) == (msik_range as u8 >> 2) {
292 // Clock source is shared, both will be auto calibrated, recalculate other frequency
293 match config.auto_calibration.base_mode() {
294 MsiAutoCalibration::MSIS => {
295 msik = Some(calculate_calibrated_msi_frequency(msik_range, lse_freq));
296 }
297 MsiAutoCalibration::MSIK => {
298 msis = Some(calculate_calibrated_msi_frequency(msis_range, lse_freq));
299 }
300 _ => {}
301 }
302 }
303 }
304 // Check if Fast mode should be used
305 if config.auto_calibration.is_fast() {
306 RCC.cr().modify(|w| {
307 w.set_msipllfast(Msipllfast::FAST);
308 });
309 }
310 }
311
191 let hsi = config.hsi.then(|| { 312 let hsi = config.hsi.then(|| {
192 RCC.cr().modify(|w| w.set_hsion(true)); 313 RCC.cr().modify(|w| w.set_hsion(true));
193 while !RCC.cr().read().hsirdy() {} 314 while !RCC.cr().read().hsirdy() {}
@@ -514,3 +635,37 @@ fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput, voltag
514 635
515 PllOutput { p, q, r } 636 PllOutput { p, q, r }
516} 637}
638
639/// Fraction structure for MSI auto-calibration
640/// Represents the multiplier as numerator/denominator that LSE frequency is multiplied by
641#[derive(Debug, Clone, Copy)]
642struct MsiFraction {
643 numerator: u32,
644 denominator: u32,
645}
646
647impl MsiFraction {
648 const fn new(numerator: u32, denominator: u32) -> Self {
649 Self { numerator, denominator }
650 }
651
652 /// Calculate the calibrated frequency given an LSE frequency
653 fn calculate_frequency(&self, lse_freq: Hertz) -> Hertz {
654 Hertz(lse_freq.0 * self.numerator / self.denominator)
655 }
656}
657
658fn get_msi_calibration_fraction(range: Msirange) -> MsiFraction {
659 // Exploiting the MSIx internals to make calculations compact
660 let denominator = (range as u32 & 0x03) + 1;
661 // Base multipliers are deduced from Table 82: MSI oscillator characteristics in data sheet
662 let numerator = [1465, 122, 94, 12][range as usize >> 2];
663
664 MsiFraction::new(numerator, denominator)
665}
666
667/// Calculate the calibrated MSI frequency for a given range and LSE frequency
668fn calculate_calibrated_msi_frequency(range: Msirange, lse_freq: Hertz) -> Hertz {
669 let fraction = get_msi_calibration_fraction(range);
670 fraction.calculate_frequency(lse_freq)
671}
diff --git a/embassy-stm32/src/rcc/wba.rs b/embassy-stm32/src/rcc/wba.rs
index b9fc4e423..b494997b3 100644
--- a/embassy-stm32/src/rcc/wba.rs
+++ b/embassy-stm32/src/rcc/wba.rs
@@ -176,6 +176,7 @@ pub(crate) unsafe fn init(config: Config) {
176 // TODO 176 // TODO
177 lse: None, 177 lse: None,
178 lsi: None, 178 lsi: None,
179 pll1_p: None,
179 pll1_q: None, 180 pll1_q: None,
180 ); 181 );
181} 182}
diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs
index cd075f3de..78ccd3e6c 100644
--- a/embassy-stm32/src/rtc/low_power.rs
+++ b/embassy-stm32/src/rtc/low_power.rs
@@ -66,7 +66,7 @@ pub(crate) enum WakeupPrescaler {
66} 66}
67 67
68#[cfg(any( 68#[cfg(any(
69 stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0 69 stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba
70))] 70))]
71impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { 71impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel {
72 fn from(val: WakeupPrescaler) -> Self { 72 fn from(val: WakeupPrescaler) -> Self {
@@ -82,7 +82,7 @@ impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel {
82} 82}
83 83
84#[cfg(any( 84#[cfg(any(
85 stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0 85 stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba
86))] 86))]
87impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler { 87impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler {
88 fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { 88 fn from(val: crate::pac::rtc::vals::Wucksel) -> Self {
@@ -227,7 +227,7 @@ impl Rtc {
227 <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend(); 227 <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend();
228 unsafe { <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::enable() }; 228 unsafe { <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::enable() };
229 229
230 #[cfg(not(any(stm32u5, stm32u0)))] 230 #[cfg(not(any(stm32u5, stm32u0, stm32wba)))]
231 { 231 {
232 use crate::pac::EXTI; 232 use crate::pac::EXTI;
233 EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); 233 EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
@@ -247,5 +247,11 @@ impl Rtc {
247 RCC.srdamr().modify(|w| w.set_rtcapbamen(true)); 247 RCC.srdamr().modify(|w| w.set_rtcapbamen(true));
248 RCC.apb3smenr().modify(|w| w.set_rtcapbsmen(true)); 248 RCC.apb3smenr().modify(|w| w.set_rtcapbsmen(true));
249 } 249 }
250 #[cfg(stm32wba)]
251 {
252 use crate::pac::RCC;
253 // RCC.srdamr().modify(|w| w.set_rtcapbamen(true));
254 RCC.apb7smenr().modify(|w| w.set_rtcapbsmen(true));
255 }
250 } 256 }
251} 257}
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs
index 49f423f37..449f3008a 100644
--- a/embassy-stm32/src/rtc/mod.rs
+++ b/embassy-stm32/src/rtc/mod.rs
@@ -25,7 +25,7 @@ use crate::time::Hertz;
25 ), 25 ),
26 path = "v2.rs" 26 path = "v2.rs"
27)] 27)]
28#[cfg_attr(any(rtc_v3, rtc_v3u5, rtc_v3l5, rtc_v3h7rs), path = "v3.rs")] 28#[cfg_attr(any(rtc_v3, rtc_v3u5, rtc_v3l5, rtc_v3h7rs, rtc_v3c0), path = "v3.rs")]
29mod _version; 29mod _version;
30#[allow(unused_imports)] 30#[allow(unused_imports)]
31pub use _version::*; 31pub use _version::*;
@@ -296,7 +296,7 @@ trait SealedInstance {
296 const BACKUP_REGISTER_COUNT: usize; 296 const BACKUP_REGISTER_COUNT: usize;
297 297
298 #[cfg(feature = "low-power")] 298 #[cfg(feature = "low-power")]
299 #[cfg(not(any(stm32u5, stm32u0)))] 299 #[cfg(not(any(stm32wba, stm32u5, stm32u0)))]
300 const EXTI_WAKEUP_LINE: usize; 300 const EXTI_WAKEUP_LINE: usize;
301 301
302 #[cfg(feature = "low-power")] 302 #[cfg(feature = "low-power")]
diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs
index 39aa6c5cb..d0b52049e 100644
--- a/embassy-stm32/src/rtc/v3.rs
+++ b/embassy-stm32/src/rtc/v3.rs
@@ -146,7 +146,7 @@ impl SealedInstance for crate::peripherals::RTC {
146 type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; 146 type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP;
147 } else if #[cfg(any(stm32g0, stm32u0))] { 147 } else if #[cfg(any(stm32g0, stm32u0))] {
148 type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP; 148 type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP;
149 } else if #[cfg(any(stm32l5, stm32h5, stm32u5))] { 149 } else if #[cfg(any(stm32l5, stm32h5, stm32u5, stm32wba))] {
150 type WakeupInterrupt = crate::interrupt::typelevel::RTC; 150 type WakeupInterrupt = crate::interrupt::typelevel::RTC;
151 } 151 }
152 ); 152 );
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs
index 6a02aae70..6e5d735d7 100644
--- a/embassy-stm32/src/sdmmc/mod.rs
+++ b/embassy-stm32/src/sdmmc/mod.rs
@@ -32,25 +32,48 @@ pub struct InterruptHandler<T: Instance> {
32} 32}
33 33
34impl<T: Instance> InterruptHandler<T> { 34impl<T: Instance> InterruptHandler<T> {
35 fn data_interrupts(enable: bool) { 35 fn enable_interrupts() {
36 let regs = T::regs(); 36 let regs = T::regs();
37 regs.maskr().write(|w| { 37 regs.maskr().write(|w| {
38 w.set_dcrcfailie(enable); 38 w.set_dcrcfailie(true);
39 w.set_dtimeoutie(enable); 39 w.set_dtimeoutie(true);
40 w.set_dataendie(enable); 40 w.set_dataendie(true);
41 w.set_dbckendie(true);
41 42
42 #[cfg(sdmmc_v1)] 43 #[cfg(sdmmc_v1)]
43 w.set_stbiterre(enable); 44 w.set_stbiterre(true);
44 #[cfg(sdmmc_v2)] 45 #[cfg(sdmmc_v2)]
45 w.set_dabortie(enable); 46 w.set_dabortie(true);
46 }); 47 });
47 } 48 }
48} 49}
49 50
50impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { 51impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
51 unsafe fn on_interrupt() { 52 unsafe fn on_interrupt() {
52 Self::data_interrupts(false);
53 T::state().wake(); 53 T::state().wake();
54 let status = T::regs().star().read();
55 T::regs().maskr().modify(|w| {
56 if status.dcrcfail() {
57 w.set_dcrcfailie(false)
58 }
59 if status.dtimeout() {
60 w.set_dtimeoutie(false)
61 }
62 if status.dataend() {
63 w.set_dataendie(false)
64 }
65 if status.dbckend() {
66 w.set_dbckendie(false)
67 }
68 #[cfg(sdmmc_v1)]
69 if status.stbiterr() {
70 w.set_stbiterre(false)
71 }
72 #[cfg(sdmmc_v2)]
73 if status.dabort() {
74 w.set_dabortie(false)
75 }
76 });
54 } 77 }
55} 78}
56 79
@@ -225,8 +248,7 @@ fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> {
225 return Ok((true, 0, ker_ck)); 248 return Ok((true, 0, ker_ck));
226 } 249 }
227 250
228 // `ker_ck / sdmmc_ck` rounded up 251 let clk_div = match ker_ck.0.div_ceil(sdmmc_ck) {
229 let clk_div = match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck {
230 0 | 1 => Ok(0), 252 0 | 1 => Ok(0),
231 x @ 2..=258 => Ok((x - 2) as u8), 253 x @ 2..=258 => Ok((x - 2) as u8),
232 _ => Err(Error::BadClock), 254 _ => Err(Error::BadClock),
@@ -244,12 +266,11 @@ fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> {
244/// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. 266/// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency.
245#[cfg(sdmmc_v2)] 267#[cfg(sdmmc_v2)]
246fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { 268fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> {
247 // `ker_ck / sdmmc_ck` rounded up 269 match ker_ck.0.div_ceil(sdmmc_ck) {
248 match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck {
249 0 | 1 => Ok((false, 0, ker_ck)), 270 0 | 1 => Ok((false, 0, ker_ck)),
250 x @ 2..=2046 => { 271 x @ 2..=2046 => {
251 // SDMMC_CK frequency = SDMMCCLK / [CLKDIV * 2] 272 // SDMMC_CK frequency = SDMMCCLK / [CLKDIV * 2]
252 let clk_div = ((x + 1) / 2) as u16; 273 let clk_div = x.div_ceil(2) as u16;
253 let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); 274 let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2));
254 275
255 Ok((false, clk_div, clk)) 276 Ok((false, clk_div, clk))
@@ -751,7 +772,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
751 Self::wait_idle(); 772 Self::wait_idle();
752 Self::clear_interrupt_flags(); 773 Self::clear_interrupt_flags();
753 774
754 regs.dtimer().write(|w| w.set_datatime(config.data_transfer_timeout));
755 regs.dlenr().write(|w| w.set_datalength(length_bytes)); 775 regs.dlenr().write(|w| w.set_datalength(length_bytes));
756 776
757 #[cfg(sdmmc_v1)] 777 #[cfg(sdmmc_v1)]
@@ -789,8 +809,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
789 Self::wait_idle(); 809 Self::wait_idle();
790 Self::clear_interrupt_flags(); 810 Self::clear_interrupt_flags();
791 811
792 regs.dtimer()
793 .write(|w| w.set_datatime(self.config.data_transfer_timeout));
794 regs.dlenr().write(|w| w.set_datalength(length_bytes)); 812 regs.dlenr().write(|w| w.set_datalength(length_bytes));
795 813
796 #[cfg(sdmmc_v1)] 814 #[cfg(sdmmc_v1)]
@@ -1007,14 +1025,14 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
1007 // Wait for the abort 1025 // Wait for the abort
1008 while Self::data_active() {} 1026 while Self::data_active() {}
1009 } 1027 }
1010 InterruptHandler::<T>::data_interrupts(false); 1028 regs.maskr().write(|_| ()); // disable irqs
1011 Self::clear_interrupt_flags(); 1029 Self::clear_interrupt_flags();
1012 Self::stop_datapath(); 1030 Self::stop_datapath();
1013 } 1031 }
1014 1032
1015 /// Wait for a previously started datapath transfer to complete from an interrupt. 1033 /// Wait for a previously started datapath transfer to complete from an interrupt.
1016 #[inline] 1034 #[inline]
1017 async fn complete_datapath_transfer() -> Result<(), Error> { 1035 async fn complete_datapath_transfer(block: bool) -> Result<(), Error> {
1018 let regs = T::regs(); 1036 let regs = T::regs();
1019 1037
1020 let res = poll_fn(|cx| { 1038 let res = poll_fn(|cx| {
@@ -1034,7 +1052,11 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
1034 if status.stbiterr() { 1052 if status.stbiterr() {
1035 return Poll::Ready(Err(Error::StBitErr)); 1053 return Poll::Ready(Err(Error::StBitErr));
1036 } 1054 }
1037 if status.dataend() { 1055 let done = match block {
1056 true => status.dbckend(),
1057 false => status.dataend(),
1058 };
1059 if done {
1038 return Poll::Ready(Ok(())); 1060 return Poll::Ready(Ok(()));
1039 } 1061 }
1040 Poll::Pending 1062 Poll::Pending
@@ -1072,10 +1094,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
1072 512, 1094 512,
1073 9, 1095 9,
1074 ); 1096 );
1075 InterruptHandler::<T>::data_interrupts(true); 1097 InterruptHandler::<T>::enable_interrupts();
1076 Self::cmd(common_cmd::read_single_block(address), true)?; 1098 Self::cmd(common_cmd::read_single_block(address), true)?;
1077 1099
1078 let res = Self::complete_datapath_transfer().await; 1100 let res = Self::complete_datapath_transfer(true).await;
1079 1101
1080 if res.is_ok() { 1102 if res.is_ok() {
1081 on_drop.defuse(); 1103 on_drop.defuse();
@@ -1105,7 +1127,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
1105 }; 1127 };
1106 Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 1128 Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16
1107 1129
1108 let regs = T::regs();
1109 let on_drop = OnDrop::new(|| Self::on_drop()); 1130 let on_drop = OnDrop::new(|| Self::on_drop());
1110 1131
1111 let transfer = Self::prepare_datapath_read( 1132 let transfer = Self::prepare_datapath_read(
@@ -1116,30 +1137,11 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
1116 512 * blocks.len() as u32, 1137 512 * blocks.len() as u32,
1117 9, 1138 9,
1118 ); 1139 );
1119 InterruptHandler::<T>::data_interrupts(true); 1140 InterruptHandler::<T>::enable_interrupts();
1120 1141
1121 Self::cmd(common_cmd::read_multiple_blocks(address), true)?; 1142 Self::cmd(common_cmd::read_multiple_blocks(address), true)?;
1122 1143
1123 let res = poll_fn(|cx| { 1144 let res = Self::complete_datapath_transfer(false).await;
1124 T::state().register(cx.waker());
1125 let status = regs.star().read();
1126
1127 if status.dcrcfail() {
1128 return Poll::Ready(Err(Error::Crc));
1129 }
1130 if status.dtimeout() {
1131 return Poll::Ready(Err(Error::Timeout));
1132 }
1133 #[cfg(sdmmc_v1)]
1134 if status.stbiterr() {
1135 return Poll::Ready(Err(Error::StBitErr));
1136 }
1137 if status.dataend() {
1138 return Poll::Ready(Ok(()));
1139 }
1140 Poll::Pending
1141 })
1142 .await;
1143 1145
1144 Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12 1146 Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12
1145 Self::clear_interrupt_flags(); 1147 Self::clear_interrupt_flags();
@@ -1174,12 +1176,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
1174 Self::cmd(common_cmd::write_single_block(address), true)?; 1176 Self::cmd(common_cmd::write_single_block(address), true)?;
1175 1177
1176 let transfer = self.prepare_datapath_write(buffer, 512, 9); 1178 let transfer = self.prepare_datapath_write(buffer, 512, 9);
1177 InterruptHandler::<T>::data_interrupts(true); 1179 InterruptHandler::<T>::enable_interrupts();
1178 1180
1179 #[cfg(sdmmc_v2)] 1181 #[cfg(sdmmc_v2)]
1180 Self::cmd(common_cmd::write_single_block(address), true)?; 1182 Self::cmd(common_cmd::write_single_block(address), true)?;
1181 1183
1182 let res = Self::complete_datapath_transfer().await; 1184 let res = Self::complete_datapath_transfer(true).await;
1183 1185
1184 match res { 1186 match res {
1185 Ok(_) => { 1187 Ok(_) => {
@@ -1230,7 +1232,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
1230 1232
1231 let block_count = blocks.len(); 1233 let block_count = blocks.len();
1232 1234
1233 let regs = T::regs();
1234 let on_drop = OnDrop::new(|| Self::on_drop()); 1235 let on_drop = OnDrop::new(|| Self::on_drop());
1235 1236
1236 #[cfg(sdmmc_v1)] 1237 #[cfg(sdmmc_v1)]
@@ -1238,36 +1239,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
1238 1239
1239 // Setup write command 1240 // Setup write command
1240 let transfer = self.prepare_datapath_write(buffer, 512 * block_count as u32, 9); 1241 let transfer = self.prepare_datapath_write(buffer, 512 * block_count as u32, 9);
1241 InterruptHandler::<T>::data_interrupts(true); 1242 InterruptHandler::<T>::enable_interrupts();
1242 1243
1243 #[cfg(sdmmc_v2)] 1244 #[cfg(sdmmc_v2)]
1244 Self::cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 1245 Self::cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25
1245 1246
1246 let res = poll_fn(|cx| { 1247 let res = Self::complete_datapath_transfer(false).await;
1247 T::state().register(cx.waker());
1248
1249 let status = regs.star().read();
1250
1251 if status.dcrcfail() {
1252 return Poll::Ready(Err(Error::Crc));
1253 }
1254 if status.dtimeout() {
1255 return Poll::Ready(Err(Error::Timeout));
1256 }
1257 if status.txunderr() {
1258 return Poll::Ready(Err(Error::Underrun));
1259 }
1260 #[cfg(sdmmc_v1)]
1261 if status.stbiterr() {
1262 return Poll::Ready(Err(Error::StBitErr));
1263 }
1264 if status.dataend() {
1265 return Poll::Ready(Ok(()));
1266 }
1267
1268 Poll::Pending
1269 })
1270 .await;
1271 1248
1272 Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12 1249 Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12
1273 Self::clear_interrupt_flags(); 1250 Self::clear_interrupt_flags();
@@ -1349,6 +1326,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
1349 #[cfg(sdmmc_v1)] 1326 #[cfg(sdmmc_v1)]
1350 w.set_bypass(_bypass); 1327 w.set_bypass(_bypass);
1351 }); 1328 });
1329 regs.dtimer()
1330 .write(|w| w.set_datatime(self.config.data_transfer_timeout));
1352 1331
1353 regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); 1332 regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8));
1354 Self::cmd(common_cmd::idle(), false)?; 1333 Self::cmd(common_cmd::idle(), false)?;
@@ -1600,10 +1579,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
1600 64, 1579 64,
1601 6, 1580 6,
1602 ); 1581 );
1603 InterruptHandler::<T>::data_interrupts(true); 1582 InterruptHandler::<T>::enable_interrupts();
1604 Self::cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 1583 Self::cmd(sd_cmd::cmd6(set_function), true)?; // CMD6
1605 1584
1606 let res = Self::complete_datapath_transfer().await; 1585 let res = Self::complete_datapath_transfer(true).await;
1607 1586
1608 // Host is allowed to use the new functions at least 8 1587 // Host is allowed to use the new functions at least 8
1609 // clocks after the end of the switch command 1588 // clocks after the end of the switch command
@@ -1660,10 +1639,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
1660 8, 1639 8,
1661 3, 1640 3,
1662 ); 1641 );
1663 InterruptHandler::<T>::data_interrupts(true); 1642 InterruptHandler::<T>::enable_interrupts();
1664 Self::cmd(sd_cmd::send_scr(), true)?; 1643 Self::cmd(sd_cmd::send_scr(), true)?;
1665 1644
1666 let res = Self::complete_datapath_transfer().await; 1645 let res = Self::complete_datapath_transfer(true).await;
1667 1646
1668 if res.is_ok() { 1647 if res.is_ok() {
1669 on_drop.defuse(); 1648 on_drop.defuse();
@@ -1706,10 +1685,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
1706 64, 1685 64,
1707 6, 1686 6,
1708 ); 1687 );
1709 InterruptHandler::<T>::data_interrupts(true); 1688 InterruptHandler::<T>::enable_interrupts();
1710 Self::cmd(sd_cmd::sd_status(), true)?; 1689 Self::cmd(sd_cmd::sd_status(), true)?;
1711 1690
1712 let res = Self::complete_datapath_transfer().await; 1691 let res = Self::complete_datapath_transfer(true).await;
1713 1692
1714 if res.is_ok() { 1693 if res.is_ok() {
1715 on_drop.defuse(); 1694 on_drop.defuse();
@@ -1756,10 +1735,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
1756 512, 1735 512,
1757 9, 1736 9,
1758 ); 1737 );
1759 InterruptHandler::<T>::data_interrupts(true); 1738 InterruptHandler::<T>::enable_interrupts();
1760 Self::cmd(emmc_cmd::send_ext_csd(), true)?; 1739 Self::cmd(emmc_cmd::send_ext_csd(), true)?;
1761 1740
1762 let res = Self::complete_datapath_transfer().await; 1741 let res = Self::complete_datapath_transfer(true).await;
1763 1742
1764 if res.is_ok() { 1743 if res.is_ok() {
1765 on_drop.defuse(); 1744 on_drop.defuse();
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs
index 9e2ba093a..c8d83f07e 100644
--- a/embassy-stm32/src/spi/mod.rs
+++ b/embassy-stm32/src/spi/mod.rs
@@ -843,7 +843,7 @@ impl<'d> Spi<'d, Async> {
843 843
844 set_rxdmaen(self.info.regs, true); 844 set_rxdmaen(self.info.regs, true);
845 845
846 let rx_src = self.info.regs.rx_ptr(); 846 let rx_src = self.info.regs.rx_ptr::<W>();
847 let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read_raw(rx_src, read, Default::default()) }; 847 let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read_raw(rx_src, read, Default::default()) };
848 848
849 let tx_dst: *mut W = self.info.regs.tx_ptr(); 849 let tx_dst: *mut W = self.info.regs.tx_ptr();
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs
index 8eec6c0c7..b00cc18ad 100644
--- a/embassy-stm32/src/timer/complementary_pwm.rs
+++ b/embassy-stm32/src/timer/complementary_pwm.rs
@@ -5,14 +5,12 @@ use core::marker::PhantomData;
5use stm32_metapac::timer::vals::Ckd; 5use stm32_metapac::timer::vals::Ckd;
6 6
7use super::low_level::{CountingMode, OutputPolarity, Timer}; 7use super::low_level::{CountingMode, OutputPolarity, Timer};
8use super::simple_pwm::{Ch1, Ch2, Ch3, Ch4, PwmPin}; 8use super::simple_pwm::PwmPin;
9use super::{ 9use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin};
10 AdvancedInstance4Channel, Channel, Channel1ComplementaryPin, Channel2ComplementaryPin, Channel3ComplementaryPin,
11 Channel4ComplementaryPin,
12};
13use crate::gpio::{AnyPin, OutputType}; 10use crate::gpio::{AnyPin, OutputType};
14use crate::time::Hertz; 11use crate::time::Hertz;
15use crate::timer::low_level::OutputCompareMode; 12use crate::timer::low_level::OutputCompareMode;
13use crate::timer::TimerChannel;
16use crate::Peri; 14use crate::Peri;
17 15
18/// Complementary PWM pin wrapper. 16/// Complementary PWM pin wrapper.
@@ -23,32 +21,23 @@ pub struct ComplementaryPwmPin<'d, T, C> {
23 phantom: PhantomData<(T, C)>, 21 phantom: PhantomData<(T, C)>,
24} 22}
25 23
26macro_rules! complementary_channel_impl { 24impl<'d, T: AdvancedInstance4Channel, C: TimerChannel> ComplementaryPwmPin<'d, T, C> {
27 ($new_chx:ident, $channel:ident, $pin_trait:ident) => { 25 /// Create a new complementary PWM pin instance.
28 impl<'d, T: AdvancedInstance4Channel> ComplementaryPwmPin<'d, T, $channel> { 26 pub fn new(pin: Peri<'d, impl TimerComplementaryPin<T, C>>, output_type: OutputType) -> Self {
29 #[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")] 27 critical_section::with(|_| {
30 pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, output_type: OutputType) -> Self { 28 pin.set_low();
31 critical_section::with(|_| { 29 pin.set_as_af(
32 pin.set_low(); 30 pin.af_num(),
33 pin.set_as_af( 31 crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh),
34 pin.af_num(), 32 );
35 crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh), 33 });
36 ); 34 ComplementaryPwmPin {
37 }); 35 _pin: pin.into(),
38 ComplementaryPwmPin { 36 phantom: PhantomData,
39 _pin: pin.into(),
40 phantom: PhantomData,
41 }
42 }
43 } 37 }
44 }; 38 }
45} 39}
46 40
47complementary_channel_impl!(new_ch1, Ch1, Channel1ComplementaryPin);
48complementary_channel_impl!(new_ch2, Ch2, Channel2ComplementaryPin);
49complementary_channel_impl!(new_ch3, Ch3, Channel3ComplementaryPin);
50complementary_channel_impl!(new_ch4, Ch4, Channel4ComplementaryPin);
51
52/// PWM driver with support for standard and complementary outputs. 41/// PWM driver with support for standard and complementary outputs.
53pub struct ComplementaryPwm<'d, T: AdvancedInstance4Channel> { 42pub struct ComplementaryPwm<'d, T: AdvancedInstance4Channel> {
54 inner: Timer<'d, T>, 43 inner: Timer<'d, T>,
@@ -88,6 +77,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
88 this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1); 77 this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1);
89 this.inner.set_output_compare_preload(channel, true); 78 this.inner.set_output_compare_preload(channel, true);
90 }); 79 });
80 this.inner.set_autoreload_preload(true);
91 81
92 this 82 this
93 } 83 }
@@ -121,7 +111,11 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
121 /// 111 ///
122 /// This value depends on the configured frequency and the timer's clock rate from RCC. 112 /// This value depends on the configured frequency and the timer's clock rate from RCC.
123 pub fn get_max_duty(&self) -> u16 { 113 pub fn get_max_duty(&self) -> u16 {
124 self.inner.get_max_compare_value() as u16 + 1 114 if self.inner.get_counting_mode().is_center_aligned() {
115 self.inner.get_max_compare_value() as u16
116 } else {
117 self.inner.get_max_compare_value() as u16 + 1
118 }
125 } 119 }
126 120
127 /// Set the duty for a given channel. 121 /// Set the duty for a given channel.
@@ -171,7 +165,11 @@ impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm<
171 } 165 }
172 166
173 fn get_max_duty(&self) -> Self::Duty { 167 fn get_max_duty(&self) -> Self::Duty {
174 self.inner.get_max_compare_value() as u16 + 1 168 if self.inner.get_counting_mode().is_center_aligned() {
169 self.inner.get_max_compare_value() as u16
170 } else {
171 self.inner.get_max_compare_value() as u16 + 1
172 }
175 } 173 }
176 174
177 fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { 175 fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {
diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs
index ec8b1ddf1..dda33e7f1 100644
--- a/embassy-stm32/src/timer/input_capture.rs
+++ b/embassy-stm32/src/timer/input_capture.rs
@@ -6,14 +6,12 @@ use core::pin::Pin;
6use core::task::{Context, Poll}; 6use core::task::{Context, Poll};
7 7
8use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer}; 8use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer};
9use super::{ 9use super::{CaptureCompareInterruptHandler, Channel, GeneralInstance4Channel, TimerPin};
10 CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin,
11 GeneralInstance4Channel,
12};
13pub use super::{Ch1, Ch2, Ch3, Ch4}; 10pub use super::{Ch1, Ch2, Ch3, Ch4};
14use crate::gpio::{AfType, AnyPin, Pull}; 11use crate::gpio::{AfType, AnyPin, Pull};
15use crate::interrupt::typelevel::{Binding, Interrupt}; 12use crate::interrupt::typelevel::{Binding, Interrupt};
16use crate::time::Hertz; 13use crate::time::Hertz;
14use crate::timer::TimerChannel;
17use crate::Peri; 15use crate::Peri;
18 16
19/// Capture pin wrapper. 17/// Capture pin wrapper.
@@ -23,27 +21,17 @@ pub struct CapturePin<'d, T, C> {
23 _pin: Peri<'d, AnyPin>, 21 _pin: Peri<'d, AnyPin>,
24 phantom: PhantomData<(T, C)>, 22 phantom: PhantomData<(T, C)>,
25} 23}
26 24impl<'d, T: GeneralInstance4Channel, C: TimerChannel> CapturePin<'d, T, C> {
27macro_rules! channel_impl { 25 /// Create a new capture pin instance.
28 ($new_chx:ident, $channel:ident, $pin_trait:ident) => { 26 pub fn new(pin: Peri<'d, impl TimerPin<T, C>>, pull: Pull) -> Self {
29 impl<'d, T: GeneralInstance4Channel> CapturePin<'d, T, $channel> { 27 pin.set_as_af(pin.af_num(), AfType::input(pull));
30 #[doc = concat!("Create a new ", stringify!($channel), " capture pin instance.")] 28 CapturePin {
31 pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, pull: Pull) -> Self { 29 _pin: pin.into(),
32 pin.set_as_af(pin.af_num(), AfType::input(pull)); 30 phantom: PhantomData,
33 CapturePin {
34 _pin: pin.into(),
35 phantom: PhantomData,
36 }
37 }
38 } 31 }
39 }; 32 }
40} 33}
41 34
42channel_impl!(new_ch1, Ch1, Channel1Pin);
43channel_impl!(new_ch2, Ch2, Channel2Pin);
44channel_impl!(new_ch3, Ch3, Channel3Pin);
45channel_impl!(new_ch4, Ch4, Channel4Pin);
46
47/// Input capture driver. 35/// Input capture driver.
48pub struct InputCapture<'d, T: GeneralInstance4Channel> { 36pub struct InputCapture<'d, T: GeneralInstance4Channel> {
49 inner: Timer<'d, T>, 37 inner: Timer<'d, T>,
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs
index b29382fc8..7062f5f4c 100644
--- a/embassy-stm32/src/timer/mod.rs
+++ b/embassy-stm32/src/timer/mod.rs
@@ -51,6 +51,80 @@ pub enum Ch3 {}
51/// Channel 4 marker type. 51/// Channel 4 marker type.
52pub enum Ch4 {} 52pub enum Ch4 {}
53 53
54/// Timer channel trait.
55#[allow(private_bounds)]
56pub trait TimerChannel: SealedTimerChannel {
57 /// The runtime channel.
58 const CHANNEL: Channel;
59}
60
61trait SealedTimerChannel {}
62
63impl TimerChannel for Ch1 {
64 const CHANNEL: Channel = Channel::Ch1;
65}
66
67impl TimerChannel for Ch2 {
68 const CHANNEL: Channel = Channel::Ch2;
69}
70
71impl TimerChannel for Ch3 {
72 const CHANNEL: Channel = Channel::Ch3;
73}
74
75impl TimerChannel for Ch4 {
76 const CHANNEL: Channel = Channel::Ch4;
77}
78
79impl SealedTimerChannel for Ch1 {}
80impl SealedTimerChannel for Ch2 {}
81impl SealedTimerChannel for Ch3 {}
82impl SealedTimerChannel for Ch4 {}
83
84/// Timer break input.
85#[derive(Clone, Copy)]
86pub enum BkIn {
87 /// Break input 1.
88 BkIn1,
89 /// Break input 2.
90 BkIn2,
91}
92
93impl BkIn {
94 /// Get the channel index (0..3)
95 pub fn index(&self) -> usize {
96 match self {
97 BkIn::BkIn1 => 0,
98 BkIn::BkIn2 => 1,
99 }
100 }
101}
102
103/// Break input 1 marker type.
104pub enum BkIn1 {}
105/// Break input 2 marker type.
106pub enum BkIn2 {}
107
108/// Timer channel trait.
109#[allow(private_bounds)]
110pub trait BreakInput: SealedBreakInput {
111 /// The runtim timer channel.
112 const INPUT: BkIn;
113}
114
115trait SealedBreakInput {}
116
117impl BreakInput for BkIn1 {
118 const INPUT: BkIn = BkIn::BkIn1;
119}
120
121impl BreakInput for BkIn2 {
122 const INPUT: BkIn = BkIn::BkIn2;
123}
124
125impl SealedBreakInput for BkIn1 {}
126impl SealedBreakInput for BkIn2 {}
127
54/// Amount of bits of a timer. 128/// Amount of bits of a timer.
55#[derive(Clone, Copy, PartialEq, Eq, Debug)] 129#[derive(Clone, Copy, PartialEq, Eq, Debug)]
56#[cfg_attr(feature = "defmt", derive(defmt::Format))] 130#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -149,33 +223,20 @@ pub trait AdvancedInstance2Channel: BasicInstance + GeneralInstance2Channel + Ad
149/// Advanced 16-bit timer with 4 channels instance. 223/// Advanced 16-bit timer with 4 channels instance.
150pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel {} 224pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel {}
151 225
152pin_trait!(Channel1Pin, GeneralInstance4Channel); 226pin_trait!(TimerPin, GeneralInstance4Channel, TimerChannel);
153pin_trait!(Channel2Pin, GeneralInstance4Channel);
154pin_trait!(Channel3Pin, GeneralInstance4Channel);
155pin_trait!(Channel4Pin, GeneralInstance4Channel);
156pin_trait!(ExternalTriggerPin, GeneralInstance4Channel); 227pin_trait!(ExternalTriggerPin, GeneralInstance4Channel);
157 228
158pin_trait!(Channel1ComplementaryPin, AdvancedInstance4Channel); 229pin_trait!(TimerComplementaryPin, AdvancedInstance4Channel, TimerChannel);
159pin_trait!(Channel2ComplementaryPin, AdvancedInstance4Channel);
160pin_trait!(Channel3ComplementaryPin, AdvancedInstance4Channel);
161pin_trait!(Channel4ComplementaryPin, AdvancedInstance4Channel);
162
163pin_trait!(BreakInputPin, AdvancedInstance4Channel);
164pin_trait!(BreakInput2Pin, AdvancedInstance4Channel);
165 230
166pin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel); 231pin_trait!(BreakInputPin, AdvancedInstance4Channel, BreakInput);
167pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel);
168 232
169pin_trait!(BreakInput2Comparator1Pin, AdvancedInstance4Channel); 233pin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel, BreakInput);
170pin_trait!(BreakInput2Comparator2Pin, AdvancedInstance4Channel); 234pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel, BreakInput);
171 235
172// Update Event trigger DMA for every timer 236// Update Event trigger DMA for every timer
173dma_trait!(UpDma, BasicInstance); 237dma_trait!(UpDma, BasicInstance);
174 238
175dma_trait!(Ch1Dma, GeneralInstance4Channel); 239dma_trait!(Dma, GeneralInstance4Channel, TimerChannel);
176dma_trait!(Ch2Dma, GeneralInstance4Channel);
177dma_trait!(Ch3Dma, GeneralInstance4Channel);
178dma_trait!(Ch4Dma, GeneralInstance4Channel);
179 240
180#[allow(unused)] 241#[allow(unused)]
181macro_rules! impl_core_timer { 242macro_rules! impl_core_timer {
diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs
index 933165ef9..498d9c082 100644
--- a/embassy-stm32/src/timer/one_pulse.rs
+++ b/embassy-stm32/src/timer/one_pulse.rs
@@ -7,11 +7,9 @@ use core::pin::Pin;
7use core::task::{Context, Poll}; 7use core::task::{Context, Poll};
8 8
9use super::low_level::{ 9use super::low_level::{
10 CountingMode, FilterValue, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource, 10 CountingMode, FilterValue, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource as Ts,
11};
12use super::{
13 CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, ExternalTriggerPin, GeneralInstance4Channel,
14}; 11};
12use super::{CaptureCompareInterruptHandler, Channel, ExternalTriggerPin, GeneralInstance4Channel, TimerPin};
15pub use super::{Ch1, Ch2}; 13pub use super::{Ch1, Ch2};
16use crate::gpio::{AfType, AnyPin, Pull}; 14use crate::gpio::{AfType, AnyPin, Pull};
17use crate::interrupt::typelevel::{Binding, Interrupt}; 15use crate::interrupt::typelevel::{Binding, Interrupt};
@@ -48,24 +46,78 @@ pub struct TriggerPin<'d, T, C> {
48 phantom: PhantomData<(T, C)>, 46 phantom: PhantomData<(T, C)>,
49} 47}
50 48
51macro_rules! channel_impl { 49trait SealedTriggerSource {}
52 ($new_chx:ident, $channel:ident, $pin_trait:ident) => { 50
53 impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, $channel> { 51/// Marker trait for a trigger source.
54 #[doc = concat!("Create a new ", stringify!($channel), " trigger pin instance.")] 52#[expect(private_bounds)]
55 pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, pull: Pull) -> Self { 53pub trait TriggerSource: SealedTriggerSource {}
56 pin.set_as_af(pin.af_num(), AfType::input(pull)); 54
57 TriggerPin { 55impl TriggerSource for Ch1 {}
58 _pin: pin.into(), 56impl TriggerSource for Ch2 {}
59 phantom: PhantomData, 57impl TriggerSource for Ext {}
60 } 58
61 } 59impl SealedTriggerSource for Ch1 {}
62 } 60impl SealedTriggerSource for Ch2 {}
63 }; 61impl SealedTriggerSource for Ext {}
62
63trait SealedTimerTriggerPin<T, S>: crate::gpio::Pin {}
64
65/// Marker trait for a trigger pin.
66#[expect(private_bounds)]
67// TODO: find better naming scheme than prefixing all pin traits with "Timer".
68// The trait name cannot conflict with the corresponding type's name.
69// Applies to other timer submodules as well.
70pub trait TimerTriggerPin<T, S>: SealedTimerTriggerPin<T, S> {
71 /// Get the AF number needed to use this pin as a trigger source.
72 fn af_num(&self) -> u8;
73}
74
75impl<T, P, C> TimerTriggerPin<T, C> for P
76where
77 T: GeneralInstance4Channel,
78 P: TimerPin<T, C>,
79 C: super::TimerChannel + TriggerSource,
80{
81 fn af_num(&self) -> u8 {
82 TimerPin::af_num(self)
83 }
84}
85
86impl<T, P> TimerTriggerPin<T, Ext> for P
87where
88 T: GeneralInstance4Channel,
89 P: ExternalTriggerPin<T>,
90{
91 fn af_num(&self) -> u8 {
92 ExternalTriggerPin::af_num(self)
93 }
94}
95
96impl<T, P, C> SealedTimerTriggerPin<T, C> for P
97where
98 T: GeneralInstance4Channel,
99 P: TimerPin<T, C>,
100 C: super::TimerChannel + TriggerSource,
101{
64} 102}
65 103
66channel_impl!(new_ch1, Ch1, Channel1Pin); 104impl<T, P> SealedTimerTriggerPin<T, Ext> for P
67channel_impl!(new_ch2, Ch2, Channel2Pin); 105where
68channel_impl!(new_ext, Ext, ExternalTriggerPin); 106 T: GeneralInstance4Channel,
107 P: ExternalTriggerPin<T>,
108{
109}
110
111impl<'d, T: GeneralInstance4Channel, C: TriggerSource> TriggerPin<'d, T, C> {
112 /// "Create a new Ch1 trigger pin instance.
113 pub fn new(pin: Peri<'d, impl TimerTriggerPin<T, C>>, pull: Pull) -> Self {
114 pin.set_as_af(pin.af_num(), AfType::input(pull));
115 TriggerPin {
116 _pin: pin.into(),
117 phantom: PhantomData,
118 }
119 }
120}
69 121
70/// One pulse driver. 122/// One pulse driver.
71/// 123///
@@ -89,7 +141,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {
89 ) -> Self { 141 ) -> Self {
90 let mut this = Self { inner: Timer::new(tim) }; 142 let mut this = Self { inner: Timer::new(tim) };
91 143
92 this.inner.set_trigger_source(TriggerSource::TI1F_ED); 144 this.inner.set_trigger_source(Ts::TI1F_ED);
93 this.inner 145 this.inner
94 .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal); 146 .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal);
95 this.inner 147 this.inner
@@ -114,7 +166,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {
114 ) -> Self { 166 ) -> Self {
115 let mut this = Self { inner: Timer::new(tim) }; 167 let mut this = Self { inner: Timer::new(tim) };
116 168
117 this.inner.set_trigger_source(TriggerSource::TI1FP1); 169 this.inner.set_trigger_source(Ts::TI1FP1);
118 this.inner 170 this.inner
119 .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal); 171 .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal);
120 this.inner 172 this.inner
@@ -131,7 +183,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {
131 /// as an output. 183 /// as an output.
132 pub fn new_ch2( 184 pub fn new_ch2(
133 tim: Peri<'d, T>, 185 tim: Peri<'d, T>,
134 _pin: TriggerPin<'d, T, Ch1>, 186 _pin: TriggerPin<'d, T, Ch2>,
135 _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd, 187 _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,
136 freq: Hertz, 188 freq: Hertz,
137 pulse_end: u32, 189 pulse_end: u32,
@@ -140,7 +192,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {
140 ) -> Self { 192 ) -> Self {
141 let mut this = Self { inner: Timer::new(tim) }; 193 let mut this = Self { inner: Timer::new(tim) };
142 194
143 this.inner.set_trigger_source(TriggerSource::TI2FP2); 195 this.inner.set_trigger_source(Ts::TI2FP2);
144 this.inner 196 this.inner
145 .set_input_ti_selection(Channel::Ch2, InputTISelection::Normal); 197 .set_input_ti_selection(Channel::Ch2, InputTISelection::Normal);
146 this.inner 198 this.inner
@@ -172,7 +224,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {
172 // No filtering 224 // No filtering
173 r.set_etf(FilterValue::NO_FILTER); 225 r.set_etf(FilterValue::NO_FILTER);
174 }); 226 });
175 this.inner.set_trigger_source(TriggerSource::ETRF); 227 this.inner.set_trigger_source(Ts::ETRF);
176 this.new_inner(freq, pulse_end, counting_mode); 228 this.new_inner(freq, pulse_end, counting_mode);
177 229
178 this 230 this
diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs
index 98b798634..1e55f2919 100644
--- a/embassy-stm32/src/timer/pwm_input.rs
+++ b/embassy-stm32/src/timer/pwm_input.rs
@@ -1,12 +1,16 @@
1//! PWM Input driver. 1//! PWM Input driver.
2 2
3use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource}; 3use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource};
4use super::{Channel, Channel1Pin, Channel2Pin, GeneralInstance4Channel}; 4use super::{Ch1, Ch2, Channel, GeneralInstance4Channel, TimerPin};
5use crate::gpio::{AfType, Pull}; 5use crate::gpio::{AfType, Pull};
6use crate::time::Hertz; 6use crate::time::Hertz;
7use crate::Peri; 7use crate::Peri;
8 8
9/// PWM Input driver. 9/// PWM Input driver.
10///
11/// Only works with CH1 or CH2
12/// Note: Not all timer peripherals are supported
13/// Double check your chips reference manual
10pub struct PwmInput<'d, T: GeneralInstance4Channel> { 14pub struct PwmInput<'d, T: GeneralInstance4Channel> {
11 channel: Channel, 15 channel: Channel,
12 inner: Timer<'d, T>, 16 inner: Timer<'d, T>,
@@ -14,14 +18,14 @@ pub struct PwmInput<'d, T: GeneralInstance4Channel> {
14 18
15impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { 19impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> {
16 /// Create a new PWM input driver. 20 /// Create a new PWM input driver.
17 pub fn new(tim: Peri<'d, T>, pin: Peri<'d, impl Channel1Pin<T>>, pull: Pull, freq: Hertz) -> Self { 21 pub fn new_ch1(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin<T, Ch1>>, pull: Pull, freq: Hertz) -> Self {
18 pin.set_as_af(pin.af_num(), AfType::input(pull)); 22 pin.set_as_af(pin.af_num(), AfType::input(pull));
19 23
20 Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2) 24 Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2)
21 } 25 }
22 26
23 /// Create a new PWM input driver. 27 /// Create a new PWM input driver.
24 pub fn new_alt(tim: Peri<'d, T>, pin: Peri<'d, impl Channel2Pin<T>>, pull: Pull, freq: Hertz) -> Self { 28 pub fn new_ch2(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin<T, Ch2>>, pull: Pull, freq: Hertz) -> Self {
25 pin.set_as_af(pin.af_num(), AfType::input(pull)); 29 pin.set_as_af(pin.af_num(), AfType::input(pull));
26 30
27 Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1) 31 Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1)
@@ -37,6 +41,7 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> {
37 41
38 // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6 42 // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6
39 // or ST RM0008 (STM32F103) chapter 15.3.6 Input capture mode 43 // or ST RM0008 (STM32F103) chapter 15.3.6 Input capture mode
44 // or ST RM0440 (STM32G4) chapter 30.4.8 PWM input mode
40 inner.set_input_ti_selection(ch1, InputTISelection::Normal); 45 inner.set_input_ti_selection(ch1, InputTISelection::Normal);
41 inner.set_input_capture_mode(ch1, InputCaptureMode::Rising); 46 inner.set_input_capture_mode(ch1, InputCaptureMode::Rising);
42 47
diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs
index f3c81667c..eabe1b22a 100644
--- a/embassy-stm32/src/timer/qei.rs
+++ b/embassy-stm32/src/timer/qei.rs
@@ -6,8 +6,9 @@ use stm32_metapac::timer::vals;
6 6
7use super::low_level::Timer; 7use super::low_level::Timer;
8pub use super::{Ch1, Ch2}; 8pub use super::{Ch1, Ch2};
9use super::{Channel1Pin, Channel2Pin, GeneralInstance4Channel}; 9use super::{GeneralInstance4Channel, TimerPin};
10use crate::gpio::{AfType, AnyPin, Pull}; 10use crate::gpio::{AfType, AnyPin, Pull};
11use crate::timer::TimerChannel;
11use crate::Peri; 12use crate::Peri;
12 13
13/// Counting direction 14/// Counting direction
@@ -24,26 +25,31 @@ pub struct QeiPin<'d, T, Channel> {
24 phantom: PhantomData<(T, Channel)>, 25 phantom: PhantomData<(T, Channel)>,
25} 26}
26 27
27macro_rules! channel_impl { 28impl<'d, T: GeneralInstance4Channel, C: QeiChannel> QeiPin<'d, T, C> {
28 ($new_chx:ident, $channel:ident, $pin_trait:ident) => { 29 /// Create a new QEI pin instance.
29 impl<'d, T: GeneralInstance4Channel> QeiPin<'d, T, $channel> { 30 pub fn new(pin: Peri<'d, impl TimerPin<T, C>>) -> Self {
30 #[doc = concat!("Create a new ", stringify!($channel), " QEI pin instance.")] 31 critical_section::with(|_| {
31 pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self { 32 pin.set_low();
32 critical_section::with(|_| { 33 pin.set_as_af(pin.af_num(), AfType::input(Pull::None));
33 pin.set_low(); 34 });
34 pin.set_as_af(pin.af_num(), AfType::input(Pull::None)); 35 QeiPin {
35 }); 36 _pin: pin.into(),
36 QeiPin { 37 phantom: PhantomData,
37 _pin: pin.into(),
38 phantom: PhantomData,
39 }
40 }
41 } 38 }
42 }; 39 }
43} 40}
44 41
45channel_impl!(new_ch1, Ch1, Channel1Pin); 42trait SealedQeiChannel: TimerChannel {}
46channel_impl!(new_ch2, Ch2, Channel2Pin); 43
44/// Marker trait for a timer channel eligible for use with QEI.
45#[expect(private_bounds)]
46pub trait QeiChannel: SealedQeiChannel {}
47
48impl QeiChannel for Ch1 {}
49impl QeiChannel for Ch2 {}
50
51impl SealedQeiChannel for Ch1 {}
52impl SealedQeiChannel for Ch2 {}
47 53
48/// Quadrature decoder driver. 54/// Quadrature decoder driver.
49pub struct Qei<'d, T: GeneralInstance4Channel> { 55pub struct Qei<'d, T: GeneralInstance4Channel> {
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs
index f7f433154..c04b1ab97 100644
--- a/embassy-stm32/src/timer/simple_pwm.rs
+++ b/embassy-stm32/src/timer/simple_pwm.rs
@@ -4,22 +4,13 @@ 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::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel, TimerBits}; 7use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin};
8#[cfg(gpio_v2)] 8#[cfg(gpio_v2)]
9use crate::gpio::Pull; 9use crate::gpio::Pull;
10use crate::gpio::{AfType, AnyPin, OutputType, Speed}; 10use crate::gpio::{AfType, AnyPin, OutputType, Speed};
11use crate::time::Hertz; 11use crate::time::Hertz;
12use crate::Peri; 12use crate::Peri;
13 13
14/// Channel 1 marker type.
15pub enum Ch1 {}
16/// Channel 2 marker type.
17pub enum Ch2 {}
18/// Channel 3 marker type.
19pub enum Ch3 {}
20/// Channel 4 marker type.
21pub enum Ch4 {}
22
23/// PWM pin wrapper. 14/// PWM pin wrapper.
24/// 15///
25/// This wraps a pin to make it usable with PWM. 16/// This wraps a pin to make it usable with PWM.
@@ -43,46 +34,37 @@ pub struct PwmPinConfig {
43 pub pull: Pull, 34 pub pull: Pull,
44} 35}
45 36
46macro_rules! channel_impl { 37impl<'d, T: GeneralInstance4Channel, C: TimerChannel> PwmPin<'d, T, C> {
47 ($new_chx:ident, $new_chx_with_config:ident, $channel:ident, $pin_trait:ident) => { 38 /// Create a new PWM pin instance.
48 impl<'d, T: GeneralInstance4Channel> PwmPin<'d, T, $channel> { 39 pub fn new(pin: Peri<'d, impl TimerPin<T, C>>, output_type: OutputType) -> Self {
49 #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] 40 critical_section::with(|_| {
50 pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, output_type: OutputType) -> Self { 41 pin.set_low();
51 critical_section::with(|_| { 42 pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh));
52 pin.set_low(); 43 });
53 pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh)); 44 PwmPin {
54 }); 45 _pin: pin.into(),
55 PwmPin { 46 phantom: PhantomData,
56 _pin: pin.into(),
57 phantom: PhantomData,
58 }
59 }
60
61 #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance with config.")]
62 pub fn $new_chx_with_config(pin: Peri<'d, impl $pin_trait<T>>, pin_config: PwmPinConfig) -> Self {
63 critical_section::with(|_| {
64 pin.set_low();
65 pin.set_as_af(
66 pin.af_num(),
67 #[cfg(gpio_v1)]
68 AfType::output(pin_config.output_type, pin_config.speed),
69 #[cfg(gpio_v2)]
70 AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull),
71 );
72 });
73 PwmPin {
74 _pin: pin.into(),
75 phantom: PhantomData,
76 }
77 }
78 } 47 }
79 }; 48 }
80}
81 49
82channel_impl!(new_ch1, new_ch1_with_config, Ch1, Channel1Pin); 50 /// Create a new PWM pin instance with config.
83channel_impl!(new_ch2, new_ch2_with_config, Ch2, Channel2Pin); 51 pub fn new_with_config(pin: Peri<'d, impl TimerPin<T, C>>, pin_config: PwmPinConfig) -> Self {
84channel_impl!(new_ch3, new_ch3_with_config, Ch3, Channel3Pin); 52 critical_section::with(|_| {
85channel_impl!(new_ch4, new_ch4_with_config, Ch4, Channel4Pin); 53 pin.set_low();
54 pin.set_as_af(
55 pin.af_num(),
56 #[cfg(gpio_v1)]
57 AfType::output(pin_config.output_type, pin_config.speed),
58 #[cfg(gpio_v2)]
59 AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull),
60 );
61 });
62 PwmPin {
63 _pin: pin.into(),
64 phantom: PhantomData,
65 }
66 }
67}
86 68
87/// A single channel of a pwm, obtained from [`SimplePwm::split`], 69/// A single channel of a pwm, obtained from [`SimplePwm::split`],
88/// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc. 70/// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc.
@@ -466,107 +448,98 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
466 } 448 }
467} 449}
468 450
469macro_rules! impl_waveform_chx { 451impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
470 ($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => { 452 /// Generate a sequence of PWM waveform
471 impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { 453 pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) {
472 /// Generate a sequence of PWM waveform 454 use crate::pac::timer::vals::Ccds;
473 pub async fn $fn_name(&mut self, dma: Peri<'_, impl super::$dma_ch<T>>, duty: &[u16]) { 455
474 use crate::pac::timer::vals::Ccds; 456 #[allow(clippy::let_unit_value)] // eg. stm32f334
457 let req = dma.request();
475 458
476 #[allow(clippy::let_unit_value)] // eg. stm32f334 459 let cc_channel = C::CHANNEL;
477 let req = dma.request();
478 460
479 let cc_channel = Channel::$cc_ch; 461 let original_duty_state = self.channel(cc_channel).current_duty_cycle();
462 let original_enable_state = self.channel(cc_channel).is_enabled();
463 let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE;
464 let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel);
480 465
481 let original_duty_state = self.channel(cc_channel).current_duty_cycle(); 466 // redirect CC DMA request onto Update Event
482 let original_enable_state = self.channel(cc_channel).is_enabled(); 467 if !original_cc_dma_on_update {
483 let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE; 468 self.inner.set_cc_dma_selection(Ccds::ON_UPDATE)
484 let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); 469 }
485 470
486 // redirect CC DMA request onto Update Event 471 if !original_cc_dma_enabled {
487 if !original_cc_dma_on_update { 472 self.inner.set_cc_dma_enable_state(cc_channel, true);
488 self.inner.set_cc_dma_selection(Ccds::ON_UPDATE) 473 }
489 }
490 474
491 if !original_cc_dma_enabled { 475 if !original_enable_state {
492 self.inner.set_cc_dma_enable_state(cc_channel, true); 476 self.channel(cc_channel).enable();
493 } 477 }
494 478
495 if !original_enable_state { 479 unsafe {
496 self.channel(cc_channel).enable(); 480 #[cfg(not(any(bdma, gpdma)))]
497 } 481 use crate::dma::{Burst, FifoThreshold};
482 use crate::dma::{Transfer, TransferOptions};
498 483
499 unsafe { 484 let dma_transfer_option = TransferOptions {
485 #[cfg(not(any(bdma, gpdma)))]
486 fifo_threshold: Some(FifoThreshold::Full),
487 #[cfg(not(any(bdma, gpdma)))]
488 mburst: Burst::Incr8,
489 ..Default::default()
490 };
491
492 match self.inner.bits() {
493 TimerBits::Bits16 => {
494 Transfer::new_write(
495 dma,
496 req,
497 duty,
498 self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16,
499 dma_transfer_option,
500 )
501 .await
502 }
503 #[cfg(not(any(stm32l0)))]
504 TimerBits::Bits32 => {
500 #[cfg(not(any(bdma, gpdma)))] 505 #[cfg(not(any(bdma, gpdma)))]
501 use crate::dma::{Burst, FifoThreshold}; 506 panic!("unsupported timer bits");
502 use crate::dma::{Transfer, TransferOptions}; 507
503 508 #[cfg(any(bdma, gpdma))]
504 let dma_transfer_option = TransferOptions { 509 Transfer::new_write(
505 #[cfg(not(any(bdma, gpdma)))] 510 dma,
506 fifo_threshold: Some(FifoThreshold::Full), 511 req,
507 #[cfg(not(any(bdma, gpdma)))] 512 duty,
508 mburst: Burst::Incr8, 513 self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32,
509 ..Default::default() 514 dma_transfer_option,
510 }; 515 )
511 516 .await
512 match self.inner.bits() {
513 TimerBits::Bits16 => {
514 Transfer::new_write(
515 dma,
516 req,
517 duty,
518 self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16,
519 dma_transfer_option,
520 )
521 .await
522 }
523 #[cfg(not(any(stm32l0)))]
524 TimerBits::Bits32 => {
525 #[cfg(not(any(bdma, gpdma)))]
526 panic!("unsupported timer bits");
527
528 #[cfg(any(bdma, gpdma))]
529 Transfer::new_write(
530 dma,
531 req,
532 duty,
533 self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32,
534 dma_transfer_option,
535 )
536 .await
537 }
538 };
539 };
540
541 // restore output compare state
542 if !original_enable_state {
543 self.channel(cc_channel).disable();
544 } 517 }
518 };
519 };
520
521 // restore output compare state
522 if !original_enable_state {
523 self.channel(cc_channel).disable();
524 }
545 525
546 self.channel(cc_channel).set_duty_cycle(original_duty_state); 526 self.channel(cc_channel).set_duty_cycle(original_duty_state);
547 527
548 // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, 528 // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
549 // this can almost always trigger a DMA FIFO error. 529 // this can almost always trigger a DMA FIFO error.
550 // 530 //
551 // optional TODO: 531 // optional TODO:
552 // clean FEIF after disable UDE 532 // clean FEIF after disable UDE
553 if !original_cc_dma_enabled { 533 if !original_cc_dma_enabled {
554 self.inner.set_cc_dma_enable_state(cc_channel, false); 534 self.inner.set_cc_dma_enable_state(cc_channel, false);
555 } 535 }
556 536
557 if !original_cc_dma_on_update { 537 if !original_cc_dma_on_update {
558 self.inner.set_cc_dma_selection(Ccds::ON_COMPARE) 538 self.inner.set_cc_dma_selection(Ccds::ON_COMPARE)
559 }
560 }
561 } 539 }
562 }; 540 }
563} 541}
564 542
565impl_waveform_chx!(waveform_ch1, Ch1Dma, Ch1);
566impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2);
567impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3);
568impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4);
569
570impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> { 543impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> {
571 type Error = core::convert::Infallible; 544 type Error = core::convert::Infallible;
572} 545}
diff --git a/embassy-stm32/src/tsc/acquisition_banks.rs b/embassy-stm32/src/tsc/acquisition_banks.rs
index 6791ef6c1..7d6442b48 100644
--- a/embassy-stm32/src/tsc/acquisition_banks.rs
+++ b/embassy-stm32/src/tsc/acquisition_banks.rs
@@ -32,7 +32,7 @@ impl AcquisitionBankPins {
32 /// Returns an iterator over the pins in this acquisition bank. 32 /// Returns an iterator over the pins in this acquisition bank.
33 /// 33 ///
34 /// This method allows for easy traversal of all configured pins in the bank. 34 /// This method allows for easy traversal of all configured pins in the bank.
35 pub fn iter(&self) -> AcquisitionBankPinsIterator { 35 pub fn iter(&self) -> AcquisitionBankPinsIterator<'_> {
36 AcquisitionBankPinsIterator(AcquisitionBankIterator::new(self)) 36 AcquisitionBankPinsIterator(AcquisitionBankIterator::new(self))
37 } 37 }
38} 38}
@@ -90,7 +90,7 @@ impl<'a> Iterator for AcquisitionBankPinsIterator<'a> {
90 90
91impl AcquisitionBankPins { 91impl AcquisitionBankPins {
92 /// Returns an iterator over the available pins in the bank 92 /// Returns an iterator over the available pins in the bank
93 pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator { 93 pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator<'_> {
94 AcquisitionBankPinsIterator(AcquisitionBankIterator::new(self)) 94 AcquisitionBankPinsIterator(AcquisitionBankIterator::new(self))
95 } 95 }
96} 96}
@@ -107,7 +107,7 @@ pub struct AcquisitionBank {
107 107
108impl AcquisitionBank { 108impl AcquisitionBank {
109 /// Returns an iterator over the available pins in the bank. 109 /// Returns an iterator over the available pins in the bank.
110 pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator { 110 pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator<'_> {
111 self.pins.pins_iterator() 111 self.pins.pins_iterator()
112 } 112 }
113 113
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs
index 73ab46404..729440c46 100644
--- a/embassy-stm32/src/usart/buffered.rs
+++ b/embassy-stm32/src/usart/buffered.rs
@@ -87,6 +87,8 @@ unsafe fn on_interrupt(r: Regs, state: &'static State) {
87 87
88 r.cr1().modify(|w| { 88 r.cr1().modify(|w| {
89 w.set_tcie(false); 89 w.set_tcie(false);
90 // Reenable receiver for half-duplex if it was disabled
91 w.set_re(true);
90 }); 92 });
91 93
92 state.tx_done.store(true, Ordering::Release); 94 state.tx_done.store(true, Ordering::Release);
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs
index b3f8bc00c..5bece6d66 100644
--- a/embassy-stm32/src/usart/mod.rs
+++ b/embassy-stm32/src/usart/mod.rs
@@ -550,6 +550,20 @@ impl<'d, M: Mode> UartTx<'d, M> {
550 reconfigure(self.info, self.kernel_clock, config) 550 reconfigure(self.info, self.kernel_clock, config)
551 } 551 }
552 552
553 /// Write a single u8 if there is tx empty, otherwise return WouldBlock
554 pub(crate) fn nb_write(&mut self, byte: u8) -> Result<(), nb::Error<Error>> {
555 let r = self.info.regs;
556 let sr = sr(r).read();
557 if sr.txe() {
558 unsafe {
559 tdr(r).write_volatile(byte);
560 }
561 Ok(())
562 } else {
563 Err(nb::Error::WouldBlock)
564 }
565 }
566
553 /// Perform a blocking UART write 567 /// Perform a blocking UART write
554 pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { 568 pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
555 let r = self.info.regs; 569 let r = self.info.regs;
@@ -637,7 +651,7 @@ pub fn send_break(regs: &Regs) {
637/// In case of readback, keep Receiver enabled 651/// In case of readback, keep Receiver enabled
638fn half_duplex_set_rx_tx_before_write(r: &Regs, enable_readback: bool) { 652fn half_duplex_set_rx_tx_before_write(r: &Regs, enable_readback: bool) {
639 let mut cr1 = r.cr1().read(); 653 let mut cr1 = r.cr1().read();
640 if r.cr3().read().hdsel() && !cr1.te() { 654 if r.cr3().read().hdsel() {
641 cr1.set_te(true); 655 cr1.set_te(true);
642 cr1.set_re(enable_readback); 656 cr1.set_re(enable_readback);
643 r.cr1().write_value(cr1); 657 r.cr1().write_value(cr1);
@@ -1864,7 +1878,7 @@ impl<'d, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, M> {
1864 1878
1865impl<'d, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, M> { 1879impl<'d, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, M> {
1866 fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { 1880 fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
1867 self.blocking_write(&[char]).map_err(nb::Error::Other) 1881 self.nb_write(char)
1868 } 1882 }
1869 1883
1870 fn flush(&mut self) -> nb::Result<(), Self::Error> { 1884 fn flush(&mut self) -> nb::Result<(), Self::Error> {
diff --git a/embassy-stm32/src/usb/mod.rs b/embassy-stm32/src/usb/mod.rs
index ae5963420..692897b59 100644
--- a/embassy-stm32/src/usb/mod.rs
+++ b/embassy-stm32/src/usb/mod.rs
@@ -15,7 +15,7 @@ fn common_init<T: Instance>() {
15 let freq = T::frequency(); 15 let freq = T::frequency();
16 16
17 // On the H7RS, the USBPHYC embeds a PLL accepting one of the input frequencies listed below and providing 48MHz to OTG_FS and 60MHz to OTG_HS internally 17 // On the H7RS, the USBPHYC embeds a PLL accepting one of the input frequencies listed below and providing 48MHz to OTG_FS and 60MHz to OTG_HS internally
18 #[cfg(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs)))] 18 #[cfg(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs), all(stm32wba, peri_usb_otg_hs)))]
19 if ![16_000_000, 19_200_000, 20_000_000, 24_000_000, 26_000_000, 32_000_000].contains(&freq.0) { 19 if ![16_000_000, 19_200_000, 20_000_000, 24_000_000, 26_000_000, 32_000_000].contains(&freq.0) {
20 panic!( 20 panic!(
21 "USB clock should be one of 16, 19.2, 20, 24, 26, 32Mhz but is {} Hz. Please double-check your RCC settings.", 21 "USB clock should be one of 16, 19.2, 20, 24, 26, 32Mhz but is {} Hz. Please double-check your RCC settings.",
@@ -25,7 +25,7 @@ fn common_init<T: Instance>() {
25 // Check frequency is within the 0.25% tolerance allowed by the spec. 25 // Check frequency is within the 0.25% tolerance allowed by the spec.
26 // Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user 26 // Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user
27 // has tight clock restrictions due to something else (like audio). 27 // has tight clock restrictions due to something else (like audio).
28 #[cfg(not(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs))))] 28 #[cfg(not(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs), all(stm32wba, peri_usb_otg_hs))))]
29 if freq.0.abs_diff(48_000_000) > 120_000 { 29 if freq.0.abs_diff(48_000_000) > 120_000 {
30 panic!( 30 panic!(
31 "USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.", 31 "USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.",
@@ -102,6 +102,30 @@ fn common_init<T: Instance>() {
102 } 102 }
103 } 103 }
104 104
105 #[cfg(stm32wba)]
106 {
107 // Enable USB power
108 critical_section::with(|_| {
109 crate::pac::PWR.svmcr().modify(|w| {
110 w.set_usv(crate::pac::pwr::vals::Usv::B_0X1);
111 // w.set_uvmen(true);
112 })
113 });
114
115 // Wait for USB power to stabilize
116 while !crate::pac::PWR.vosr().read().vdd11usbrdy() {}
117
118 // Now set up transceiver power if it's a OTG-HS
119 #[cfg(peri_usb_otg_hs)]
120 {
121 crate::pac::PWR.vosr().modify(|w| {
122 w.set_usbpwren(true);
123 w.set_usbboosten(true);
124 });
125 while !crate::pac::PWR.vosr().read().usbboostrdy() {}
126 }
127 }
128
105 T::Interrupt::unpend(); 129 T::Interrupt::unpend();
106 unsafe { T::Interrupt::enable() }; 130 unsafe { T::Interrupt::enable() };
107 131
diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs
index 590d1a427..b074cfa1b 100644
--- a/embassy-stm32/src/usb/otg.rs
+++ b/embassy-stm32/src/usb/otg.rs
@@ -105,7 +105,7 @@ impl<'d, T: Instance> Driver<'d, T> {
105 config: Config, 105 config: Config,
106 ) -> Self { 106 ) -> Self {
107 // For STM32U5 High speed pins need to be left in analog mode 107 // For STM32U5 High speed pins need to be left in analog mode
108 #[cfg(not(all(stm32u5, peri_usb_otg_hs)))] 108 #[cfg(not(any(all(stm32u5, peri_usb_otg_hs), all(stm32wba, peri_usb_otg_hs))))]
109 { 109 {
110 _dp.set_as_af(_dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 110 _dp.set_as_af(_dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
111 _dm.set_as_af(_dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 111 _dm.set_as_af(_dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
@@ -231,19 +231,23 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> {
231 fn alloc_endpoint_in( 231 fn alloc_endpoint_in(
232 &mut self, 232 &mut self,
233 ep_type: EndpointType, 233 ep_type: EndpointType,
234 ep_addr: Option<EndpointAddress>,
234 max_packet_size: u16, 235 max_packet_size: u16,
235 interval_ms: u8, 236 interval_ms: u8,
236 ) -> Result<Self::EndpointIn, EndpointAllocError> { 237 ) -> Result<Self::EndpointIn, EndpointAllocError> {
237 self.inner.alloc_endpoint_in(ep_type, max_packet_size, interval_ms) 238 self.inner
239 .alloc_endpoint_in(ep_type, ep_addr, max_packet_size, interval_ms)
238 } 240 }
239 241
240 fn alloc_endpoint_out( 242 fn alloc_endpoint_out(
241 &mut self, 243 &mut self,
242 ep_type: EndpointType, 244 ep_type: EndpointType,
245 ep_addr: Option<EndpointAddress>,
243 max_packet_size: u16, 246 max_packet_size: u16,
244 interval_ms: u8, 247 interval_ms: u8,
245 ) -> Result<Self::EndpointOut, EndpointAllocError> { 248 ) -> Result<Self::EndpointOut, EndpointAllocError> {
246 self.inner.alloc_endpoint_out(ep_type, max_packet_size, interval_ms) 249 self.inner
250 .alloc_endpoint_out(ep_type, ep_addr, max_packet_size, interval_ms)
247 } 251 }
248 252
249 fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { 253 fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
@@ -323,6 +327,20 @@ impl<'d, T: Instance> Bus<'d, T> {
323 }); 327 });
324 } 328 }
325 329
330 #[cfg(all(stm32wba, peri_usb_otg_hs))]
331 {
332 crate::pac::SYSCFG.otghsphycr().modify(|w| {
333 w.set_en(true);
334 });
335
336 critical_section::with(|_| {
337 crate::pac::RCC.ahb2enr().modify(|w| {
338 w.set_usb_otg_hsen(true);
339 w.set_otghsphyen(true);
340 });
341 });
342 }
343
326 let r = T::regs(); 344 let r = T::regs();
327 let core_id = r.cid().read().0; 345 let core_id = r.cid().read().0;
328 trace!("Core id {:08x}", core_id); 346 trace!("Core id {:08x}", core_id);
@@ -464,6 +482,7 @@ foreach_interrupt!(
464 stm32f7, 482 stm32f7,
465 stm32l4, 483 stm32l4,
466 stm32u5, 484 stm32u5,
485 stm32wba,
467 ))] { 486 ))] {
468 const FIFO_DEPTH_WORDS: u16 = 320; 487 const FIFO_DEPTH_WORDS: u16 = 320;
469 const ENDPOINT_COUNT: usize = 6; 488 const ENDPOINT_COUNT: usize = 6;
@@ -473,7 +492,7 @@ foreach_interrupt!(
473 } else if #[cfg(any(stm32h7, stm32h7rs))] { 492 } else if #[cfg(any(stm32h7, stm32h7rs))] {
474 const FIFO_DEPTH_WORDS: u16 = 1024; 493 const FIFO_DEPTH_WORDS: u16 = 1024;
475 const ENDPOINT_COUNT: usize = 9; 494 const ENDPOINT_COUNT: usize = 9;
476 } else if #[cfg(stm32u5)] { 495 } else if #[cfg(any(stm32wba, stm32u5))] {
477 const FIFO_DEPTH_WORDS: u16 = 320; 496 const FIFO_DEPTH_WORDS: u16 = 320;
478 const ENDPOINT_COUNT: usize = 6; 497 const ENDPOINT_COUNT: usize = 6;
479 } else { 498 } else {
@@ -523,7 +542,7 @@ foreach_interrupt!(
523 ))] { 542 ))] {
524 const FIFO_DEPTH_WORDS: u16 = 1024; 543 const FIFO_DEPTH_WORDS: u16 = 1024;
525 const ENDPOINT_COUNT: usize = 9; 544 const ENDPOINT_COUNT: usize = 9;
526 } else if #[cfg(stm32u5)] { 545 } else if #[cfg(any(stm32u5, stm32wba))] {
527 const FIFO_DEPTH_WORDS: u16 = 1024; 546 const FIFO_DEPTH_WORDS: u16 = 1024;
528 const ENDPOINT_COUNT: usize = 9; 547 const ENDPOINT_COUNT: usize = 9;
529 } else { 548 } else {
diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs
index 3e8e74a1f..92c1601cc 100644
--- a/embassy-stm32/src/usb/usb.rs
+++ b/embassy-stm32/src/usb/usb.rs
@@ -359,9 +359,38 @@ impl<'d, T: Instance> Driver<'d, T> {
359 addr 359 addr
360 } 360 }
361 361
362 fn is_endpoint_available<D: Dir>(&self, index: usize, ep_type: EndpointType) -> bool {
363 if index == 0 && ep_type != EndpointType::Control {
364 return false; // EP0 is reserved for control
365 }
366
367 let ep = match self.alloc.get(index) {
368 Some(ep) => ep,
369 None => return false,
370 };
371
372 let used = ep.used_out || ep.used_in;
373
374 if used && ep.ep_type == EndpointType::Isochronous {
375 // Isochronous endpoints are always double-buffered.
376 // Their corresponding endpoint/channel registers are forced to be unidirectional.
377 // Do not reuse this index.
378 // FIXME: Bulk endpoints can be double buffered, but are not in the current implementation.
379 return false;
380 }
381
382 let used_dir = match D::dir() {
383 Direction::Out => ep.used_out,
384 Direction::In => ep.used_in,
385 };
386
387 !used || (ep.ep_type == ep_type && !used_dir)
388 }
389
362 fn alloc_endpoint<D: Dir>( 390 fn alloc_endpoint<D: Dir>(
363 &mut self, 391 &mut self,
364 ep_type: EndpointType, 392 ep_type: EndpointType,
393 ep_addr: Option<EndpointAddress>,
365 max_packet_size: u16, 394 max_packet_size: u16,
366 interval_ms: u8, 395 interval_ms: u8,
367 ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> { 396 ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> {
@@ -373,28 +402,17 @@ impl<'d, T: Instance> Driver<'d, T> {
373 D::dir() 402 D::dir()
374 ); 403 );
375 404
376 let index = self.alloc.iter_mut().enumerate().find(|(i, ep)| { 405 let index = if let Some(addr) = ep_addr {
377 if *i == 0 && ep_type != EndpointType::Control { 406 // Use the specified endpoint address
378 return false; // reserved for control pipe 407 self.is_endpoint_available::<D>(addr.index(), ep_type)
379 } 408 .then_some(addr.index())
380 let used = ep.used_out || ep.used_in; 409 } else {
381 if used && (ep.ep_type == EndpointType::Isochronous) { 410 // Find any available endpoint
382 // Isochronous endpoints are always double-buffered. 411 (0..self.alloc.len()).find(|&i| self.is_endpoint_available::<D>(i, ep_type))
383 // Their corresponding endpoint/channel registers are forced to be unidirectional. 412 };
384 // Do not reuse this index.
385 // FIXME: Bulk endpoints can be double buffered, but are not in the current implementation.
386 return false;
387 }
388
389 let used_dir = match D::dir() {
390 Direction::Out => ep.used_out,
391 Direction::In => ep.used_in,
392 };
393 !used || (ep.ep_type == ep_type && !used_dir)
394 });
395 413
396 let (index, ep) = match index { 414 let (index, ep) = match index {
397 Some(x) => x, 415 Some(i) => (i, &mut self.alloc[i]),
398 None => return Err(EndpointAllocError), 416 None => return Err(EndpointAllocError),
399 }; 417 };
400 418
@@ -479,27 +497,29 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
479 fn alloc_endpoint_in( 497 fn alloc_endpoint_in(
480 &mut self, 498 &mut self,
481 ep_type: EndpointType, 499 ep_type: EndpointType,
500 ep_addr: Option<EndpointAddress>,
482 max_packet_size: u16, 501 max_packet_size: u16,
483 interval_ms: u8, 502 interval_ms: u8,
484 ) -> Result<Self::EndpointIn, driver::EndpointAllocError> { 503 ) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
485 self.alloc_endpoint(ep_type, max_packet_size, interval_ms) 504 self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms)
486 } 505 }
487 506
488 fn alloc_endpoint_out( 507 fn alloc_endpoint_out(
489 &mut self, 508 &mut self,
490 ep_type: EndpointType, 509 ep_type: EndpointType,
510 ep_addr: Option<EndpointAddress>,
491 max_packet_size: u16, 511 max_packet_size: u16,
492 interval_ms: u8, 512 interval_ms: u8,
493 ) -> Result<Self::EndpointOut, driver::EndpointAllocError> { 513 ) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
494 self.alloc_endpoint(ep_type, max_packet_size, interval_ms) 514 self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms)
495 } 515 }
496 516
497 fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { 517 fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
498 let ep_out = self 518 let ep_out = self
499 .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) 519 .alloc_endpoint(EndpointType::Control, None, control_max_packet_size, 0)
500 .unwrap(); 520 .unwrap();
501 let ep_in = self 521 let ep_in = self
502 .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) 522 .alloc_endpoint(EndpointType::Control, None, control_max_packet_size, 0)
503 .unwrap(); 523 .unwrap();
504 assert_eq!(ep_out.info.addr.index(), 0); 524 assert_eq!(ep_out.info.addr.index(), 0);
505 assert_eq!(ep_in.info.addr.index(), 0); 525 assert_eq!(ep_in.info.addr.index(), 0);
diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml
index 99962f9f6..9e5c39f5e 100644
--- a/embassy-sync/Cargo.toml
+++ b/embassy-sync/Cargo.toml
@@ -28,7 +28,7 @@ defmt = { version = "1.0.1", optional = true }
28log = { version = "0.4.14", optional = true } 28log = { version = "0.4.14", optional = true }
29 29
30futures-sink = { version = "0.3", default-features = false, features = [] } 30futures-sink = { version = "0.3", default-features = false, features = [] }
31futures-util = { version = "0.3.17", default-features = false } 31futures-core = { version = "0.3.31", default-features = false }
32critical-section = "1.1" 32critical-section = "1.1"
33heapless = "0.8" 33heapless = "0.8"
34cfg-if = "1.0.0" 34cfg-if = "1.0.0"
@@ -43,3 +43,4 @@ futures-util = { version = "0.3.17", features = [ "channel", "sink" ] }
43# Enable critical-section implementation for std, for tests 43# Enable critical-section implementation for std, for tests
44critical-section = { version = "1.1", features = ["std"] } 44critical-section = { version = "1.1", features = ["std"] }
45static_cell = { version = "2" } 45static_cell = { version = "2" }
46trybuild = "1.0.105"
diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs
index 856551417..a0e39fcb5 100644
--- a/embassy-sync/src/channel.rs
+++ b/embassy-sync/src/channel.rs
@@ -17,6 +17,31 @@
17//! messages that it can store, and if this limit is reached, trying to send 17//! messages that it can store, and if this limit is reached, trying to send
18//! another message will result in an error being returned. 18//! another message will result in an error being returned.
19//! 19//!
20//! # Example: Message passing between task and interrupt handler
21//!
22//! ```rust
23//! use embassy_sync::channel::Channel;
24//! use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
25//!
26//! static SHARED_CHANNEL: Channel<CriticalSectionRawMutex, u32, 8> = Channel::new();
27//!
28//! fn my_interrupt_handler() {
29//! // Do some work..
30//! // ...
31//! if let Err(e) = SHARED_CHANNEL.sender().try_send(42) {
32//! // Channel is full..
33//! }
34//! }
35//!
36//! async fn my_async_task() {
37//! // ...
38//! let receiver = SHARED_CHANNEL.receiver();
39//! loop {
40//! let data_from_interrupt = receiver.receive().await;
41//! // Do something with the data.
42//! }
43//! }
44//! ```
20 45
21use core::cell::RefCell; 46use core::cell::RefCell;
22use core::future::Future; 47use core::future::Future;
@@ -443,7 +468,7 @@ where
443 } 468 }
444} 469}
445 470
446impl<'ch, M, T, const N: usize> futures_util::Stream for Receiver<'ch, M, T, N> 471impl<'ch, M, T, const N: usize> futures_core::Stream for Receiver<'ch, M, T, N>
447where 472where
448 M: RawMutex, 473 M: RawMutex,
449{ 474{
@@ -962,7 +987,7 @@ where
962 } 987 }
963} 988}
964 989
965impl<M, T, const N: usize> futures_util::Stream for Channel<M, T, N> 990impl<M, T, const N: usize> futures_core::Stream for Channel<M, T, N>
966where 991where
967 M: RawMutex, 992 M: RawMutex,
968{ 993{
diff --git a/embassy-sync/src/lazy_lock.rs b/embassy-sync/src/lazy_lock.rs
index 18e3c2019..f1bd88b61 100644
--- a/embassy-sync/src/lazy_lock.rs
+++ b/embassy-sync/src/lazy_lock.rs
@@ -31,7 +31,12 @@ union Data<T, F> {
31 f: ManuallyDrop<F>, 31 f: ManuallyDrop<F>,
32} 32}
33 33
34unsafe impl<T, F> Sync for LazyLock<T, F> {} 34unsafe impl<T, F> Sync for LazyLock<T, F>
35where
36 T: Sync,
37 F: Sync,
38{
39}
35 40
36impl<T, F: FnOnce() -> T> LazyLock<T, F> { 41impl<T, F: FnOnce() -> T> LazyLock<T, F> {
37 /// Create a new uninitialized `StaticLock`. 42 /// Create a new uninitialized `StaticLock`.
diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs
index 7528a9f68..8496f34bf 100644
--- a/embassy-sync/src/mutex.rs
+++ b/embassy-sync/src/mutex.rs
@@ -23,7 +23,7 @@ struct State {
23 23
24/// Async mutex. 24/// Async mutex.
25/// 25///
26/// The mutex is generic over a blocking [`RawMutex`](crate::blocking_mutex::raw::RawMutex). 26/// The mutex is generic over a blocking [`RawMutex`].
27/// The raw mutex is used to guard access to the internal "is locked" flag. It 27/// The raw mutex is used to guard access to the internal "is locked" flag. It
28/// is held for very short periods only, while locking and unlocking. It is *not* held 28/// is held for very short periods only, while locking and unlocking. It is *not* held
29/// for the entire time the async Mutex is locked. 29/// for the entire time the async Mutex is locked.
diff --git a/embassy-sync/src/once_lock.rs b/embassy-sync/src/once_lock.rs
index cd05b986d..1e848685a 100644
--- a/embassy-sync/src/once_lock.rs
+++ b/embassy-sync/src/once_lock.rs
@@ -42,7 +42,7 @@ pub struct OnceLock<T> {
42 data: Cell<MaybeUninit<T>>, 42 data: Cell<MaybeUninit<T>>,
43} 43}
44 44
45unsafe impl<T> Sync for OnceLock<T> {} 45unsafe impl<T> Sync for OnceLock<T> where T: Sync {}
46 46
47impl<T> OnceLock<T> { 47impl<T> OnceLock<T> {
48 /// Create a new uninitialized `OnceLock`. 48 /// Create a new uninitialized `OnceLock`.
diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs
index 2598652d2..df3b28b45 100644
--- a/embassy-sync/src/pipe.rs
+++ b/embassy-sync/src/pipe.rs
@@ -152,7 +152,7 @@ where
152 152
153impl<'p, M, const N: usize> Unpin for ReadFuture<'p, M, N> where M: RawMutex {} 153impl<'p, M, const N: usize> Unpin for ReadFuture<'p, M, N> where M: RawMutex {}
154 154
155/// Future returned by [`Pipe::fill_buf`] and [`Reader::fill_buf`]. 155/// Future returned by [`Reader::fill_buf`].
156#[must_use = "futures do nothing unless you `.await` or poll them"] 156#[must_use = "futures do nothing unless you `.await` or poll them"]
157pub struct FillBufFuture<'p, M, const N: usize> 157pub struct FillBufFuture<'p, M, const N: usize>
158where 158where
@@ -587,7 +587,7 @@ where
587 } 587 }
588} 588}
589 589
590/// Write-only access to a [`DynamicPipe`]. 590/// Write-only access to the dynamic pipe.
591pub struct DynamicWriter<'p> { 591pub struct DynamicWriter<'p> {
592 pipe: &'p dyn DynamicPipe, 592 pipe: &'p dyn DynamicPipe,
593} 593}
@@ -657,7 +657,7 @@ where
657 } 657 }
658} 658}
659 659
660/// Read-only access to a [`DynamicPipe`]. 660/// Read-only access to a dynamic pipe.
661pub struct DynamicReader<'p> { 661pub struct DynamicReader<'p> {
662 pipe: &'p dyn DynamicPipe, 662 pipe: &'p dyn DynamicPipe,
663} 663}
@@ -742,7 +742,7 @@ where
742 } 742 }
743} 743}
744 744
745/// Future returned by [`DynamicPipe::fill_buf`] and [`DynamicReader::fill_buf`]. 745/// Future returned by [`DynamicReader::fill_buf`].
746#[must_use = "futures do nothing unless you `.await` or poll them"] 746#[must_use = "futures do nothing unless you `.await` or poll them"]
747pub struct DynamicFillBufFuture<'p> { 747pub struct DynamicFillBufFuture<'p> {
748 pipe: Option<&'p dyn DynamicPipe>, 748 pipe: Option<&'p dyn DynamicPipe>,
diff --git a/embassy-sync/src/priority_channel.rs b/embassy-sync/src/priority_channel.rs
index 623c52993..715a20e86 100644
--- a/embassy-sync/src/priority_channel.rs
+++ b/embassy-sync/src/priority_channel.rs
@@ -1,7 +1,7 @@
1//! A queue for sending values between asynchronous tasks. 1//! A queue for sending values between asynchronous tasks.
2//! 2//!
3//! Similar to a [`Channel`](crate::channel::Channel), however [`PriorityChannel`] sifts higher priority items to the front of the queue. 3//! Similar to a [`Channel`](crate::channel::Channel), however [`PriorityChannel`] sifts higher priority items to the front of the queue.
4//! Priority is determined by the `Ord` trait. Priority behavior is determined by the [`Kind`](heapless::binary_heap::Kind) parameter of the channel. 4//! Priority is determined by the `Ord` trait. Priority behavior is determined by the [`Kind`] parameter of the channel.
5 5
6use core::cell::RefCell; 6use core::cell::RefCell;
7use core::future::Future; 7use core::future::Future;
@@ -473,7 +473,7 @@ where
473/// received from the channel. 473/// received from the channel.
474/// 474///
475/// Sent data may be reordered based on their priority within the channel. 475/// Sent data may be reordered based on their priority within the channel.
476/// For example, in a [`Max`](heapless::binary_heap::Max) [`PriorityChannel`] 476/// For example, in a [`Max`] [`PriorityChannel`]
477/// containing `u32`'s, data sent in the following order `[1, 2, 3]` will be received as `[3, 2, 1]`. 477/// containing `u32`'s, data sent in the following order `[1, 2, 3]` will be received as `[3, 2, 1]`.
478pub struct PriorityChannel<M, T, K, const N: usize> 478pub struct PriorityChannel<M, T, K, const N: usize>
479where 479where
diff --git a/embassy-sync/src/pubsub/mod.rs b/embassy-sync/src/pubsub/mod.rs
index 606efff0a..9206b9383 100644
--- a/embassy-sync/src/pubsub/mod.rs
+++ b/embassy-sync/src/pubsub/mod.rs
@@ -88,7 +88,7 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi
88 /// Create a new subscriber. It will only receive messages that are published after its creation. 88 /// Create a new subscriber. It will only receive messages that are published after its creation.
89 /// 89 ///
90 /// If there are no subscriber slots left, an error will be returned. 90 /// If there are no subscriber slots left, an error will be returned.
91 pub fn subscriber(&self) -> Result<Subscriber<M, T, CAP, SUBS, PUBS>, Error> { 91 pub fn subscriber(&self) -> Result<Subscriber<'_, M, T, CAP, SUBS, PUBS>, Error> {
92 self.inner.lock(|inner| { 92 self.inner.lock(|inner| {
93 let mut s = inner.borrow_mut(); 93 let mut s = inner.borrow_mut();
94 94
@@ -120,7 +120,7 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi
120 /// Create a new publisher 120 /// Create a new publisher
121 /// 121 ///
122 /// If there are no publisher slots left, an error will be returned. 122 /// If there are no publisher slots left, an error will be returned.
123 pub fn publisher(&self) -> Result<Publisher<M, T, CAP, SUBS, PUBS>, Error> { 123 pub fn publisher(&self) -> Result<Publisher<'_, M, T, CAP, SUBS, PUBS>, Error> {
124 self.inner.lock(|inner| { 124 self.inner.lock(|inner| {
125 let mut s = inner.borrow_mut(); 125 let mut s = inner.borrow_mut();
126 126
@@ -151,13 +151,13 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi
151 151
152 /// Create a new publisher that can only send immediate messages. 152 /// Create a new publisher that can only send immediate messages.
153 /// This kind of publisher does not take up a publisher slot. 153 /// This kind of publisher does not take up a publisher slot.
154 pub fn immediate_publisher(&self) -> ImmediatePublisher<M, T, CAP, SUBS, PUBS> { 154 pub fn immediate_publisher(&self) -> ImmediatePublisher<'_, M, T, CAP, SUBS, PUBS> {
155 ImmediatePublisher(ImmediatePub::new(self)) 155 ImmediatePublisher(ImmediatePub::new(self))
156 } 156 }
157 157
158 /// Create a new publisher that can only send immediate messages. 158 /// Create a new publisher that can only send immediate messages.
159 /// This kind of publisher does not take up a publisher slot. 159 /// This kind of publisher does not take up a publisher slot.
160 pub fn dyn_immediate_publisher(&self) -> DynImmediatePublisher<T> { 160 pub fn dyn_immediate_publisher(&self) -> DynImmediatePublisher<'_, T> {
161 DynImmediatePublisher(ImmediatePub::new(self)) 161 DynImmediatePublisher(ImmediatePub::new(self))
162 } 162 }
163 163
diff --git a/embassy-sync/src/pubsub/publisher.rs b/embassy-sync/src/pubsub/publisher.rs
index 7a1ab66de..52a52f926 100644
--- a/embassy-sync/src/pubsub/publisher.rs
+++ b/embassy-sync/src/pubsub/publisher.rs
@@ -75,7 +75,7 @@ impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Pub<'a, PSB, T> {
75 self.channel.is_full() 75 self.channel.is_full()
76 } 76 }
77 77
78 /// Create a [`futures::Sink`] adapter for this publisher. 78 /// Create a [`futures_sink::Sink`] adapter for this publisher.
79 #[inline] 79 #[inline]
80 pub const fn sink(&self) -> PubSink<'a, '_, PSB, T> { 80 pub const fn sink(&self) -> PubSink<'a, '_, PSB, T> {
81 PubSink { publ: self, fut: None } 81 PubSink { publ: self, fut: None }
diff --git a/embassy-sync/src/pubsub/subscriber.rs b/embassy-sync/src/pubsub/subscriber.rs
index 6ad660cb3..649382cf1 100644
--- a/embassy-sync/src/pubsub/subscriber.rs
+++ b/embassy-sync/src/pubsub/subscriber.rs
@@ -115,7 +115,7 @@ impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Unpin for Sub<'a, PSB, T> {}
115 115
116/// Warning: The stream implementation ignores lag results and returns all messages. 116/// Warning: The stream implementation ignores lag results and returns all messages.
117/// This might miss some messages without you knowing it. 117/// This might miss some messages without you knowing it.
118impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> futures_util::Stream for Sub<'a, PSB, T> { 118impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> futures_core::Stream for Sub<'a, PSB, T> {
119 type Item = T; 119 type Item = T;
120 120
121 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { 121 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
diff --git a/embassy-sync/tests/ui.rs b/embassy-sync/tests/ui.rs
new file mode 100644
index 000000000..e8b1080d8
--- /dev/null
+++ b/embassy-sync/tests/ui.rs
@@ -0,0 +1,13 @@
1#[cfg(not(miri))]
2#[test]
3fn ui() {
4 let t = trybuild::TestCases::new();
5
6 // These test cases should fail to compile since OnceLock and LazyLock should not unconditionally implement sync
7 // for all types. These tests are regression tests against the following issues:
8 // * https://github.com/embassy-rs/embassy/issues/4307
9 // * https://github.com/embassy-rs/embassy/issues/3904
10 t.compile_fail("tests/ui/sync_impl/lazy_lock_function.rs");
11 t.compile_fail("tests/ui/sync_impl/lazy_lock_type.rs");
12 t.compile_fail("tests/ui/sync_impl/once_lock.rs");
13}
diff --git a/embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs
new file mode 100644
index 000000000..35f5587c0
--- /dev/null
+++ b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs
@@ -0,0 +1,11 @@
1use embassy_sync::lazy_lock::LazyLock;
2
3fn main() {
4 let x = 128u8;
5 let x_ptr: *const u8 = core::ptr::addr_of!(x);
6 let closure_capturing_non_sync_variable = || unsafe { core::ptr::read(x_ptr) };
7
8 check_sync(LazyLock::new(closure_capturing_non_sync_variable));
9}
10
11fn check_sync<T: Sync>(_lazy_lock: T) {}
diff --git a/embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr
new file mode 100644
index 000000000..daf79ad28
--- /dev/null
+++ b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr
@@ -0,0 +1,24 @@
1error[E0277]: `*const u8` cannot be shared between threads safely
2 --> tests/ui/sync_impl/lazy_lock_function.rs:8:16
3 |
46 | let closure_capturing_non_sync_variable = || unsafe { core::ptr::read(x_ptr) };
5 | -- within this `{closure@$DIR/tests/ui/sync_impl/lazy_lock_function.rs:6:47: 6:49}`
67 |
78 | check_sync(LazyLock::new(closure_capturing_non_sync_variable));
8 | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const u8` cannot be shared between threads safely
9 | |
10 | required by a bound introduced by this call
11 |
12 = help: within `{closure@$DIR/tests/ui/sync_impl/lazy_lock_function.rs:6:47: 6:49}`, the trait `Sync` is not implemented for `*const u8`
13 = note: required because it appears within the type `&*const u8`
14note: required because it's used within this closure
15 --> tests/ui/sync_impl/lazy_lock_function.rs:6:47
16 |
176 | let closure_capturing_non_sync_variable = || unsafe { core::ptr::read(x_ptr) };
18 | ^^
19 = note: required for `embassy_sync::lazy_lock::LazyLock<u8, {closure@$DIR/tests/ui/sync_impl/lazy_lock_function.rs:6:47: 6:49}>` to implement `Sync`
20note: required by a bound in `check_sync`
21 --> tests/ui/sync_impl/lazy_lock_function.rs:11:18
22 |
2311 | fn check_sync<T: Sync>(_lazy_lock: T) {}
24 | ^^^^ required by this bound in `check_sync`
diff --git a/embassy-sync/tests/ui/sync_impl/lazy_lock_type.rs b/embassy-sync/tests/ui/sync_impl/lazy_lock_type.rs
new file mode 100644
index 000000000..4e1383143
--- /dev/null
+++ b/embassy-sync/tests/ui/sync_impl/lazy_lock_type.rs
@@ -0,0 +1,6 @@
1use embassy_sync::lazy_lock::LazyLock;
2
3// *mut u8 is not Sync, so LazyLock should not implement Sync for this type. This should fail to compile.
4static GLOBAL: LazyLock<*mut u8> = LazyLock::new(|| core::ptr::null_mut());
5
6fn main() {}
diff --git a/embassy-sync/tests/ui/sync_impl/lazy_lock_type.stderr b/embassy-sync/tests/ui/sync_impl/lazy_lock_type.stderr
new file mode 100644
index 000000000..1ccc54c7a
--- /dev/null
+++ b/embassy-sync/tests/ui/sync_impl/lazy_lock_type.stderr
@@ -0,0 +1,9 @@
1error[E0277]: `*mut u8` cannot be shared between threads safely
2 --> tests/ui/sync_impl/lazy_lock_type.rs:4:16
3 |
44 | static GLOBAL: LazyLock<*mut u8> = LazyLock::new(|| core::ptr::null_mut());
5 | ^^^^^^^^^^^^^^^^^ `*mut u8` cannot be shared between threads safely
6 |
7 = help: the trait `Sync` is not implemented for `*mut u8`
8 = note: required for `embassy_sync::lazy_lock::LazyLock<*mut u8>` to implement `Sync`
9 = note: shared static variables must have a type that implements `Sync`
diff --git a/embassy-sync/tests/ui/sync_impl/once_lock.rs b/embassy-sync/tests/ui/sync_impl/once_lock.rs
new file mode 100644
index 000000000..8f50d583b
--- /dev/null
+++ b/embassy-sync/tests/ui/sync_impl/once_lock.rs
@@ -0,0 +1,6 @@
1use embassy_sync::once_lock::OnceLock;
2
3// *mut u8 is not Sync, so OnceLock should not implement Sync for this type. This should fail to compile.
4static GLOBAL: OnceLock<*mut u8> = OnceLock::new();
5
6fn main() {}
diff --git a/embassy-sync/tests/ui/sync_impl/once_lock.stderr b/embassy-sync/tests/ui/sync_impl/once_lock.stderr
new file mode 100644
index 000000000..e2419f844
--- /dev/null
+++ b/embassy-sync/tests/ui/sync_impl/once_lock.stderr
@@ -0,0 +1,9 @@
1error[E0277]: `*mut u8` cannot be shared between threads safely
2 --> tests/ui/sync_impl/once_lock.rs:4:16
3 |
44 | static GLOBAL: OnceLock<*mut u8> = OnceLock::new();
5 | ^^^^^^^^^^^^^^^^^ `*mut u8` cannot be shared between threads safely
6 |
7 = help: the trait `Sync` is not implemented for `*mut u8`
8 = note: required for `embassy_sync::once_lock::OnceLock<*mut u8>` to implement `Sync`
9 = note: shared static variables must have a type that implements `Sync`
diff --git a/embassy-time-driver/CHANGELOG.md b/embassy-time-driver/CHANGELOG.md
index 744b0f648..b61a10bf6 100644
--- a/embassy-time-driver/CHANGELOG.md
+++ b/embassy-time-driver/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8<!-- next-header -->
9## Unreleased - ReleaseDate
10
11- Allow inlining on time driver boundary
12- add 133MHz tick rate to support PR2040 @ 133MHz when `TIMERx`'s `SOURCE` is set to `SYSCLK`
13
8## 0.2.0 - 2025-01-02 14## 0.2.0 - 2025-01-02
9 15
10- The `allocate_alarm`, `set_alarm_callback`, `set_alarm` functions have been removed. 16- The `allocate_alarm`, `set_alarm_callback`, `set_alarm` functions have been removed.
diff --git a/embassy-time-driver/release.toml b/embassy-time-driver/release.toml
new file mode 100644
index 000000000..fb6feaf21
--- /dev/null
+++ b/embassy-time-driver/release.toml
@@ -0,0 +1,5 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-time/CHANGELOG.md b/embassy-time/CHANGELOG.md
index 09e951ce4..a0c1abe8d 100644
--- a/embassy-time/CHANGELOG.md
+++ b/embassy-time/CHANGELOG.md
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8<!-- next-header -->
9## Unreleased - ReleaseDate
10
11- Allow inlining on time driver boundary
12- Add `saturating_add` and `saturating_sub` to `Instant`
13- Add `Instant::try_from_*` constructor functions
14- Add `Duration::try_from_*` constructor functions
15- Don't select `critical-section` impl for `std`
16- Manually implement the future for `with_timeout`
17- Add 133MHz tick rate to support PR2040 @ 133MHz when `TIMERx`'s `SOURCE` is set to `SYSCLK`
18
8## 0.4.0 - 2025-01-02 19## 0.4.0 - 2025-01-02
9 20
10- `embassy-time-driver` updated from v0.1 to v0.2. 21- `embassy-time-driver` updated from v0.1 to v0.2.
diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml
index 76983d880..2284906b0 100644
--- a/embassy-time/Cargo.toml
+++ b/embassy-time/Cargo.toml
@@ -427,7 +427,7 @@ embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" }
427embedded-hal-1 = { package = "embedded-hal", version = "1.0" } 427embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
428embedded-hal-async = { version = "1.0" } 428embedded-hal-async = { version = "1.0" }
429 429
430futures-util = { version = "0.3.17", default-features = false } 430futures-core = { version = "0.3.31", default-features = false }
431critical-section = "1.1" 431critical-section = "1.1"
432cfg-if = "1.0.0" 432cfg-if = "1.0.0"
433 433
diff --git a/embassy-time/release.toml b/embassy-time/release.toml
new file mode 100644
index 000000000..fb6feaf21
--- /dev/null
+++ b/embassy-time/release.toml
@@ -0,0 +1,5 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs
index d1162eadd..d3f1e1621 100644
--- a/embassy-time/src/timer.rs
+++ b/embassy-time/src/timer.rs
@@ -2,8 +2,8 @@ use core::future::{poll_fn, Future};
2use core::pin::Pin; 2use core::pin::Pin;
3use core::task::{Context, Poll}; 3use core::task::{Context, Poll};
4 4
5use futures_util::stream::FusedStream; 5use futures_core::stream::FusedStream;
6use futures_util::Stream; 6use futures_core::Stream;
7 7
8use crate::{Duration, Instant}; 8use crate::{Duration, Instant};
9 9
diff --git a/embassy-usb-dfu/Cargo.toml b/embassy-usb-dfu/Cargo.toml
index 8b2467bca..011046ba4 100644
--- a/embassy-usb-dfu/Cargo.toml
+++ b/embassy-usb-dfu/Cargo.toml
@@ -31,11 +31,11 @@ log = { version = "0.4.17", optional = true }
31 31
32bitflags = "2.4.1" 32bitflags = "2.4.1"
33cortex-m = { version = "0.7.7", features = ["inline-asm"], optional = true } 33cortex-m = { version = "0.7.7", features = ["inline-asm"], optional = true }
34embassy-boot = { version = "0.4.0", path = "../embassy-boot" } 34embassy-boot = { version = "0.5.0", path = "../embassy-boot" }
35embassy-futures = { version = "0.1.1", path = "../embassy-futures" } 35embassy-futures = { version = "0.1.1", path = "../embassy-futures" }
36embassy-sync = { version = "0.7.0", path = "../embassy-sync" } 36embassy-sync = { version = "0.7.0", path = "../embassy-sync" }
37embassy-time = { version = "0.4.0", path = "../embassy-time" } 37embassy-time = { version = "0.4.0", path = "../embassy-time" }
38embassy-usb = { version = "0.4.0", path = "../embassy-usb", default-features = false } 38embassy-usb = { version = "0.5.0", path = "../embassy-usb", default-features = false }
39embedded-storage = { version = "0.3.1" } 39embedded-storage = { version = "0.3.1" }
40esp32c3-hal = { version = "0.13.0", optional = true, default-features = false } 40esp32c3-hal = { version = "0.13.0", optional = true, default-features = false }
41 41
diff --git a/embassy-usb-driver/CHANGELOG.md b/embassy-usb-driver/CHANGELOG.md
new file mode 100644
index 000000000..15875e087
--- /dev/null
+++ b/embassy-usb-driver/CHANGELOG.md
@@ -0,0 +1,17 @@
1# Changelog for embassy-usb-driver
2
3All notable changes to this project will be documented in this file.
4
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
8<!-- next-header -->
9## Unreleased - ReleaseDate
10
11## 0.2.0 - 2025-07-16
12
13- Make USB endpoint allocator methods accept an optional `EndpointAddress`.
14
15## 0.1.1 - 2025-07-15
16
17- Add `embedded_io_async::Error` implementation for `EndpointError` ([#4176](https://github.com/embassy-rs/embassy/pull/4176))
diff --git a/embassy-usb-driver/Cargo.toml b/embassy-usb-driver/Cargo.toml
index e40421649..de69bf694 100644
--- a/embassy-usb-driver/Cargo.toml
+++ b/embassy-usb-driver/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "embassy-usb-driver" 2name = "embassy-usb-driver"
3version = "0.1.0" 3version = "0.2.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6description = "Driver trait for `embassy-usb`, an async USB device stack for embedded devices." 6description = "Driver trait for `embassy-usb`, an async USB device stack for embedded devices."
diff --git a/embassy-usb-driver/release.toml b/embassy-usb-driver/release.toml
new file mode 100644
index 000000000..fb6feaf21
--- /dev/null
+++ b/embassy-usb-driver/release.toml
@@ -0,0 +1,5 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs
index d204e4d85..99616f1ec 100644
--- a/embassy-usb-driver/src/lib.rs
+++ b/embassy-usb-driver/src/lib.rs
@@ -136,6 +136,7 @@ pub trait Driver<'a> {
136 fn alloc_endpoint_out( 136 fn alloc_endpoint_out(
137 &mut self, 137 &mut self,
138 ep_type: EndpointType, 138 ep_type: EndpointType,
139 ep_addr: Option<EndpointAddress>,
139 max_packet_size: u16, 140 max_packet_size: u16,
140 interval_ms: u8, 141 interval_ms: u8,
141 ) -> Result<Self::EndpointOut, EndpointAllocError>; 142 ) -> Result<Self::EndpointOut, EndpointAllocError>;
@@ -153,6 +154,7 @@ pub trait Driver<'a> {
153 fn alloc_endpoint_in( 154 fn alloc_endpoint_in(
154 &mut self, 155 &mut self,
155 ep_type: EndpointType, 156 ep_type: EndpointType,
157 ep_addr: Option<EndpointAddress>,
156 max_packet_size: u16, 158 max_packet_size: u16,
157 interval_ms: u8, 159 interval_ms: u8,
158 ) -> Result<Self::EndpointIn, EndpointAllocError>; 160 ) -> Result<Self::EndpointIn, EndpointAllocError>;
diff --git a/embassy-usb-logger/CHANGELOG.md b/embassy-usb-logger/CHANGELOG.md
index 86a9fb032..79ea25839 100644
--- a/embassy-usb-logger/CHANGELOG.md
+++ b/embassy-usb-logger/CHANGELOG.md
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7 7
8## Unreleased 8## Unreleased
9 9
10## 0.5.0 - 2025-07-22
11
12- Update `embassy-usb` to 0.5.0
13
10## 0.4.0 - 2025-01-15 14## 0.4.0 - 2025-01-15
11 15
12- Update `embassy-usb` to 0.4.0 16- Update `embassy-usb` to 0.4.0
diff --git a/embassy-usb-logger/Cargo.toml b/embassy-usb-logger/Cargo.toml
index 6b8e9af47..68b11ad8a 100644
--- a/embassy-usb-logger/Cargo.toml
+++ b/embassy-usb-logger/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "embassy-usb-logger" 2name = "embassy-usb-logger"
3version = "0.4.0" 3version = "0.5.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6description = "`log` implementation for USB serial using `embassy-usb`." 6description = "`log` implementation for USB serial using `embassy-usb`."
@@ -15,7 +15,7 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-l
15target = "thumbv7em-none-eabi" 15target = "thumbv7em-none-eabi"
16 16
17[dependencies] 17[dependencies]
18embassy-usb = { version = "0.4.0", path = "../embassy-usb" } 18embassy-usb = { version = "0.5.0", path = "../embassy-usb" }
19embassy-sync = { version = "0.7.0", path = "../embassy-sync" } 19embassy-sync = { version = "0.7.0", path = "../embassy-sync" }
20embassy-futures = { version = "0.1.0", path = "../embassy-futures" } 20embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
21log = "0.4" 21log = "0.4"
diff --git a/embassy-usb-synopsys-otg/CHANGELOG.md b/embassy-usb-synopsys-otg/CHANGELOG.md
index 293363d9a..9913ee533 100644
--- a/embassy-usb-synopsys-otg/CHANGELOG.md
+++ b/embassy-usb-synopsys-otg/CHANGELOG.md
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7 7
8## Unreleased 8## Unreleased
9 9
10## 0.3.0 - 2025-07-22
11
12- Bump `embassy-usb-driver` to v0.2.0
13
10## 0.2.0 - 2024-12-06 14## 0.2.0 - 2024-12-06
11 15
12- Fix corruption in CONTROL OUT transfers (and remove `quirk_setup_late_cnak`) 16- Fix corruption in CONTROL OUT transfers (and remove `quirk_setup_late_cnak`)
diff --git a/embassy-usb-synopsys-otg/Cargo.toml b/embassy-usb-synopsys-otg/Cargo.toml
index 6252feaef..78cce24de 100644
--- a/embassy-usb-synopsys-otg/Cargo.toml
+++ b/embassy-usb-synopsys-otg/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "embassy-usb-synopsys-otg" 2name = "embassy-usb-synopsys-otg"
3version = "0.2.0" 3version = "0.3.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6description = "`embassy-usb-driver` implementation for Synopsys OTG USB controllers" 6description = "`embassy-usb-driver` implementation for Synopsys OTG USB controllers"
@@ -19,7 +19,7 @@ target = "thumbv7em-none-eabi"
19critical-section = "1.1" 19critical-section = "1.1"
20 20
21embassy-sync = { version = "0.7.0", path = "../embassy-sync" } 21embassy-sync = { version = "0.7.0", path = "../embassy-sync" }
22embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } 22embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" }
23 23
24defmt = { version = "1.0.1", optional = true } 24defmt = { version = "1.0.1", optional = true }
25log = { version = "0.4.14", optional = true } 25log = { version = "0.4.14", optional = true }
diff --git a/embassy-usb-synopsys-otg/src/lib.rs b/embassy-usb-synopsys-otg/src/lib.rs
index fc4428b54..9d74c046d 100644
--- a/embassy-usb-synopsys-otg/src/lib.rs
+++ b/embassy-usb-synopsys-otg/src/lib.rs
@@ -345,6 +345,7 @@ impl<'d, const MAX_EP_COUNT: usize> Driver<'d, MAX_EP_COUNT> {
345 fn alloc_endpoint<D: Dir>( 345 fn alloc_endpoint<D: Dir>(
346 &mut self, 346 &mut self,
347 ep_type: EndpointType, 347 ep_type: EndpointType,
348 ep_addr: Option<EndpointAddress>,
348 max_packet_size: u16, 349 max_packet_size: u16,
349 interval_ms: u8, 350 interval_ms: u8,
350 ) -> Result<Endpoint<'d, D>, EndpointAllocError> { 351 ) -> Result<Endpoint<'d, D>, EndpointAllocError> {
@@ -379,15 +380,31 @@ impl<'d, const MAX_EP_COUNT: usize> Driver<'d, MAX_EP_COUNT> {
379 Direction::In => &mut self.ep_in[..self.instance.endpoint_count], 380 Direction::In => &mut self.ep_in[..self.instance.endpoint_count],
380 }; 381 };
381 382
382 // Find free endpoint slot 383 // Find endpoint slot
383 let slot = eps.iter_mut().enumerate().find(|(i, ep)| { 384 let slot = if let Some(addr) = ep_addr {
384 if *i == 0 && ep_type != EndpointType::Control { 385 // Use the specified endpoint address
385 // reserved for control pipe 386 let requested_index = addr.index();
386 false 387 if requested_index >= self.instance.endpoint_count {
387 } else { 388 return Err(EndpointAllocError);
388 ep.is_none()
389 } 389 }
390 }); 390 if requested_index == 0 && ep_type != EndpointType::Control {
391 return Err(EndpointAllocError); // EP0 is reserved for control
392 }
393 if eps[requested_index].is_some() {
394 return Err(EndpointAllocError); // Already allocated
395 }
396 Some((requested_index, &mut eps[requested_index]))
397 } else {
398 // Find any free endpoint slot
399 eps.iter_mut().enumerate().find(|(i, ep)| {
400 if *i == 0 && ep_type != EndpointType::Control {
401 // reserved for control pipe
402 false
403 } else {
404 ep.is_none()
405 }
406 })
407 };
391 408
392 let index = match slot { 409 let index = match slot {
393 Some((index, ep)) => { 410 Some((index, ep)) => {
@@ -438,27 +455,29 @@ impl<'d, const MAX_EP_COUNT: usize> embassy_usb_driver::Driver<'d> for Driver<'d
438 fn alloc_endpoint_in( 455 fn alloc_endpoint_in(
439 &mut self, 456 &mut self,
440 ep_type: EndpointType, 457 ep_type: EndpointType,
458 ep_addr: Option<EndpointAddress>,
441 max_packet_size: u16, 459 max_packet_size: u16,
442 interval_ms: u8, 460 interval_ms: u8,
443 ) -> Result<Self::EndpointIn, EndpointAllocError> { 461 ) -> Result<Self::EndpointIn, EndpointAllocError> {
444 self.alloc_endpoint(ep_type, max_packet_size, interval_ms) 462 self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms)
445 } 463 }
446 464
447 fn alloc_endpoint_out( 465 fn alloc_endpoint_out(
448 &mut self, 466 &mut self,
449 ep_type: EndpointType, 467 ep_type: EndpointType,
468 ep_addr: Option<EndpointAddress>,
450 max_packet_size: u16, 469 max_packet_size: u16,
451 interval_ms: u8, 470 interval_ms: u8,
452 ) -> Result<Self::EndpointOut, EndpointAllocError> { 471 ) -> Result<Self::EndpointOut, EndpointAllocError> {
453 self.alloc_endpoint(ep_type, max_packet_size, interval_ms) 472 self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms)
454 } 473 }
455 474
456 fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { 475 fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
457 let ep_out = self 476 let ep_out = self
458 .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) 477 .alloc_endpoint(EndpointType::Control, None, control_max_packet_size, 0)
459 .unwrap(); 478 .unwrap();
460 let ep_in = self 479 let ep_in = self
461 .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) 480 .alloc_endpoint(EndpointType::Control, None, control_max_packet_size, 0)
462 .unwrap(); 481 .unwrap();
463 assert_eq!(ep_out.info.addr.index(), 0); 482 assert_eq!(ep_out.info.addr.index(), 0);
464 assert_eq!(ep_in.info.addr.index(), 0); 483 assert_eq!(ep_in.info.addr.index(), 0);
@@ -1210,10 +1229,23 @@ impl<'d> embassy_usb_driver::EndpointIn for Endpoint<'d, In> {
1210 }); 1229 });
1211 1230
1212 // Write data to FIFO 1231 // Write data to FIFO
1213 for chunk in buf.chunks(4) { 1232 let chunks = buf.chunks_exact(4);
1233 // Stash the last partial chunk
1234 let rem = chunks.remainder();
1235 let last_chunk = (!rem.is_empty()).then(|| {
1214 let mut tmp = [0u8; 4]; 1236 let mut tmp = [0u8; 4];
1215 tmp[0..chunk.len()].copy_from_slice(chunk); 1237 tmp[0..rem.len()].copy_from_slice(rem);
1216 self.regs.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))); 1238 u32::from_ne_bytes(tmp)
1239 });
1240
1241 let fifo = self.regs.fifo(index);
1242 for chunk in chunks {
1243 let val = u32::from_ne_bytes(chunk.try_into().unwrap());
1244 fifo.write_value(regs::Fifo(val));
1245 }
1246 // Write any last chunk
1247 if let Some(val) = last_chunk {
1248 fifo.write_value(regs::Fifo(val));
1217 } 1249 }
1218 }); 1250 });
1219 1251
diff --git a/embassy-usb/CHANGELOG.md b/embassy-usb/CHANGELOG.md
index 76fafed31..3ee75e226 100644
--- a/embassy-usb/CHANGELOG.md
+++ b/embassy-usb/CHANGELOG.md
@@ -5,7 +5,18 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8## Unreleased 8<!-- next-header -->
9## Unreleased - ReleaseDate
10
11## 0.5.0 - 2025-07-16
12
13- `UAC1`: unmute by default ([#3992](https://github.com/embassy-rs/embassy/pull/3992))
14- `cdc_acm`: `State::new` is now `const` ([#4000](https://github.com/embassy-rs/embassy/pull/4000))
15- Add support for CMSIS-DAP v2 USB class ([#4107](https://github.com/embassy-rs/embassy/pull/4107))
16- Reduce `UsbDevice` builder logs to `trace` ([#4130](https://github.com/embassy-rs/embassy/pull/4130))
17- Implement `embedded-io-async` traits for USB CDC ACM ([#4176](https://github.com/embassy-rs/embassy/pull/4176))
18- Update `embassy-sync` to v0.7.0
19- Fix CDC ACM BufferedReceiver buffer calculation
9 20
10## 0.4.0 - 2025-01-15 21## 0.4.0 - 2025-01-15
11 22
diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml
index 31fd1c1e0..4743fde65 100644
--- a/embassy-usb/Cargo.toml
+++ b/embassy-usb/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "embassy-usb" 2name = "embassy-usb"
3version = "0.4.0" 3version = "0.5.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6description = "Async USB device stack for embedded devices in Rust." 6description = "Async USB device stack for embedded devices in Rust."
@@ -47,9 +47,9 @@ max-handler-count-8 = []
47 47
48[dependencies] 48[dependencies]
49embassy-futures = { version = "0.1.0", path = "../embassy-futures" } 49embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
50embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } 50embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" }
51embassy-sync = { version = "0.7.0", path = "../embassy-sync" } 51embassy-sync = { version = "0.7.0", path = "../embassy-sync" }
52embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } 52embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel" }
53 53
54defmt = { version = "1", optional = true } 54defmt = { version = "1", optional = true }
55log = { version = "0.4.14", optional = true } 55log = { version = "0.4.14", optional = true }
diff --git a/embassy-usb/release.toml b/embassy-usb/release.toml
new file mode 100644
index 000000000..fb6feaf21
--- /dev/null
+++ b/embassy-usb/release.toml
@@ -0,0 +1,5 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs
index 6c4b3f9a4..8d7abe46c 100644
--- a/embassy-usb/src/builder.rs
+++ b/embassy-usb/src/builder.rs
@@ -2,7 +2,7 @@ use heapless::Vec;
2 2
3use crate::config::MAX_HANDLER_COUNT; 3use crate::config::MAX_HANDLER_COUNT;
4use crate::descriptor::{BosWriter, DescriptorWriter, SynchronizationType, UsageType}; 4use crate::descriptor::{BosWriter, DescriptorWriter, SynchronizationType, UsageType};
5use crate::driver::{Driver, Endpoint, EndpointInfo, EndpointType}; 5use crate::driver::{Driver, Endpoint, EndpointAddress, EndpointInfo, EndpointType};
6use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; 6use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter};
7use crate::types::{InterfaceNumber, StringIndex}; 7use crate::types::{InterfaceNumber, StringIndex};
8use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; 8use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START};
@@ -465,11 +465,17 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
465 /// Allocate an IN endpoint, without writing its descriptor. 465 /// Allocate an IN endpoint, without writing its descriptor.
466 /// 466 ///
467 /// Used for granular control over the order of endpoint and descriptor creation. 467 /// Used for granular control over the order of endpoint and descriptor creation.
468 pub fn alloc_endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { 468 pub fn alloc_endpoint_in(
469 &mut self,
470 ep_type: EndpointType,
471 ep_addr: Option<EndpointAddress>,
472 max_packet_size: u16,
473 interval_ms: u8,
474 ) -> D::EndpointIn {
469 let ep = self 475 let ep = self
470 .builder 476 .builder
471 .driver 477 .driver
472 .alloc_endpoint_in(ep_type, max_packet_size, interval_ms) 478 .alloc_endpoint_in(ep_type, ep_addr, max_packet_size, interval_ms)
473 .expect("alloc_endpoint_in failed"); 479 .expect("alloc_endpoint_in failed");
474 480
475 ep 481 ep
@@ -478,13 +484,14 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
478 fn endpoint_in( 484 fn endpoint_in(
479 &mut self, 485 &mut self,
480 ep_type: EndpointType, 486 ep_type: EndpointType,
487 ep_addr: Option<EndpointAddress>,
481 max_packet_size: u16, 488 max_packet_size: u16,
482 interval_ms: u8, 489 interval_ms: u8,
483 synchronization_type: SynchronizationType, 490 synchronization_type: SynchronizationType,
484 usage_type: UsageType, 491 usage_type: UsageType,
485 extra_fields: &[u8], 492 extra_fields: &[u8],
486 ) -> D::EndpointIn { 493 ) -> D::EndpointIn {
487 let ep = self.alloc_endpoint_in(ep_type, max_packet_size, interval_ms); 494 let ep = self.alloc_endpoint_in(ep_type, ep_addr, max_packet_size, interval_ms);
488 self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields); 495 self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields);
489 496
490 ep 497 ep
@@ -496,13 +503,14 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
496 pub fn alloc_endpoint_out( 503 pub fn alloc_endpoint_out(
497 &mut self, 504 &mut self,
498 ep_type: EndpointType, 505 ep_type: EndpointType,
506 ep_addr: Option<EndpointAddress>,
499 max_packet_size: u16, 507 max_packet_size: u16,
500 interval_ms: u8, 508 interval_ms: u8,
501 ) -> D::EndpointOut { 509 ) -> D::EndpointOut {
502 let ep = self 510 let ep = self
503 .builder 511 .builder
504 .driver 512 .driver
505 .alloc_endpoint_out(ep_type, max_packet_size, interval_ms) 513 .alloc_endpoint_out(ep_type, ep_addr, max_packet_size, interval_ms)
506 .expect("alloc_endpoint_out failed"); 514 .expect("alloc_endpoint_out failed");
507 515
508 ep 516 ep
@@ -511,13 +519,14 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
511 fn endpoint_out( 519 fn endpoint_out(
512 &mut self, 520 &mut self,
513 ep_type: EndpointType, 521 ep_type: EndpointType,
522 ep_addr: Option<EndpointAddress>,
514 max_packet_size: u16, 523 max_packet_size: u16,
515 interval_ms: u8, 524 interval_ms: u8,
516 synchronization_type: SynchronizationType, 525 synchronization_type: SynchronizationType,
517 usage_type: UsageType, 526 usage_type: UsageType,
518 extra_fields: &[u8], 527 extra_fields: &[u8],
519 ) -> D::EndpointOut { 528 ) -> D::EndpointOut {
520 let ep = self.alloc_endpoint_out(ep_type, max_packet_size, interval_ms); 529 let ep = self.alloc_endpoint_out(ep_type, ep_addr, max_packet_size, interval_ms);
521 self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields); 530 self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields);
522 531
523 ep 532 ep
@@ -527,9 +536,10 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
527 /// 536 ///
528 /// Descriptors are written in the order builder functions are called. Note that some 537 /// Descriptors are written in the order builder functions are called. Note that some
529 /// classes care about the order. 538 /// classes care about the order.
530 pub fn endpoint_bulk_in(&mut self, max_packet_size: u16) -> D::EndpointIn { 539 pub fn endpoint_bulk_in(&mut self, ep_addr: Option<EndpointAddress>, max_packet_size: u16) -> D::EndpointIn {
531 self.endpoint_in( 540 self.endpoint_in(
532 EndpointType::Bulk, 541 EndpointType::Bulk,
542 ep_addr,
533 max_packet_size, 543 max_packet_size,
534 0, 544 0,
535 SynchronizationType::NoSynchronization, 545 SynchronizationType::NoSynchronization,
@@ -542,9 +552,10 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
542 /// 552 ///
543 /// Descriptors are written in the order builder functions are called. Note that some 553 /// Descriptors are written in the order builder functions are called. Note that some
544 /// classes care about the order. 554 /// classes care about the order.
545 pub fn endpoint_bulk_out(&mut self, max_packet_size: u16) -> D::EndpointOut { 555 pub fn endpoint_bulk_out(&mut self, ep_addr: Option<EndpointAddress>, max_packet_size: u16) -> D::EndpointOut {
546 self.endpoint_out( 556 self.endpoint_out(
547 EndpointType::Bulk, 557 EndpointType::Bulk,
558 ep_addr,
548 max_packet_size, 559 max_packet_size,
549 0, 560 0,
550 SynchronizationType::NoSynchronization, 561 SynchronizationType::NoSynchronization,
@@ -557,9 +568,15 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
557 /// 568 ///
558 /// Descriptors are written in the order builder functions are called. Note that some 569 /// Descriptors are written in the order builder functions are called. Note that some
559 /// classes care about the order. 570 /// classes care about the order.
560 pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { 571 pub fn endpoint_interrupt_in(
572 &mut self,
573 ep_addr: Option<EndpointAddress>,
574 max_packet_size: u16,
575 interval_ms: u8,
576 ) -> D::EndpointIn {
561 self.endpoint_in( 577 self.endpoint_in(
562 EndpointType::Interrupt, 578 EndpointType::Interrupt,
579 ep_addr,
563 max_packet_size, 580 max_packet_size,
564 interval_ms, 581 interval_ms,
565 SynchronizationType::NoSynchronization, 582 SynchronizationType::NoSynchronization,
@@ -569,9 +586,15 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
569 } 586 }
570 587
571 /// Allocate a INTERRUPT OUT endpoint and write its descriptor. 588 /// Allocate a INTERRUPT OUT endpoint and write its descriptor.
572 pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut { 589 pub fn endpoint_interrupt_out(
590 &mut self,
591 ep_addr: Option<EndpointAddress>,
592 max_packet_size: u16,
593 interval_ms: u8,
594 ) -> D::EndpointOut {
573 self.endpoint_out( 595 self.endpoint_out(
574 EndpointType::Interrupt, 596 EndpointType::Interrupt,
597 ep_addr,
575 max_packet_size, 598 max_packet_size,
576 interval_ms, 599 interval_ms,
577 SynchronizationType::NoSynchronization, 600 SynchronizationType::NoSynchronization,
@@ -586,6 +609,7 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
586 /// classes care about the order. 609 /// classes care about the order.
587 pub fn endpoint_isochronous_in( 610 pub fn endpoint_isochronous_in(
588 &mut self, 611 &mut self,
612 ep_addr: Option<EndpointAddress>,
589 max_packet_size: u16, 613 max_packet_size: u16,
590 interval_ms: u8, 614 interval_ms: u8,
591 synchronization_type: SynchronizationType, 615 synchronization_type: SynchronizationType,
@@ -594,6 +618,7 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
594 ) -> D::EndpointIn { 618 ) -> D::EndpointIn {
595 self.endpoint_in( 619 self.endpoint_in(
596 EndpointType::Isochronous, 620 EndpointType::Isochronous,
621 ep_addr,
597 max_packet_size, 622 max_packet_size,
598 interval_ms, 623 interval_ms,
599 synchronization_type, 624 synchronization_type,
@@ -605,6 +630,7 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
605 /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor. 630 /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor.
606 pub fn endpoint_isochronous_out( 631 pub fn endpoint_isochronous_out(
607 &mut self, 632 &mut self,
633 ep_addr: Option<EndpointAddress>,
608 max_packet_size: u16, 634 max_packet_size: u16,
609 interval_ms: u8, 635 interval_ms: u8,
610 synchronization_type: SynchronizationType, 636 synchronization_type: SynchronizationType,
@@ -613,6 +639,7 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
613 ) -> D::EndpointOut { 639 ) -> D::EndpointOut {
614 self.endpoint_out( 640 self.endpoint_out(
615 EndpointType::Isochronous, 641 EndpointType::Isochronous,
642 ep_addr,
616 max_packet_size, 643 max_packet_size,
617 interval_ms, 644 interval_ms,
618 synchronization_type, 645 synchronization_type,
diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs
index 732a433f8..0a1a5e64f 100644
--- a/embassy-usb/src/class/cdc_acm.rs
+++ b/embassy-usb/src/class/cdc_acm.rs
@@ -254,14 +254,14 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
254 ], 254 ],
255 ); 255 );
256 256
257 let comm_ep = alt.endpoint_interrupt_in(8, 255); 257 let comm_ep = alt.endpoint_interrupt_in(None, 8, 255);
258 258
259 // Data interface 259 // Data interface
260 let mut iface = func.interface(); 260 let mut iface = func.interface();
261 let data_if = iface.interface_number(); 261 let data_if = iface.interface_number();
262 let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NONE, None); 262 let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NONE, None);
263 let read_ep = alt.endpoint_bulk_out(max_packet_size); 263 let read_ep = alt.endpoint_bulk_out(None, max_packet_size);
264 let write_ep = alt.endpoint_bulk_in(max_packet_size); 264 let write_ep = alt.endpoint_bulk_in(None, max_packet_size);
265 265
266 drop(func); 266 drop(func);
267 267
@@ -501,7 +501,7 @@ impl<'d, D: Driver<'d>> BufferedReceiver<'d, D> {
501 fn read_from_buffer(&mut self, buf: &mut [u8]) -> usize { 501 fn read_from_buffer(&mut self, buf: &mut [u8]) -> usize {
502 let available = &self.buffer[self.start..self.end]; 502 let available = &self.buffer[self.start..self.end];
503 let len = core::cmp::min(available.len(), buf.len()); 503 let len = core::cmp::min(available.len(), buf.len());
504 buf[..len].copy_from_slice(&self.buffer[..len]); 504 buf[..len].copy_from_slice(&available[..len]);
505 self.start += len; 505 self.start += len;
506 len 506 len
507 } 507 }
diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs
index 09d923d2a..3af853091 100644
--- a/embassy-usb/src/class/cdc_ncm/mod.rs
+++ b/embassy-usb/src/class/cdc_ncm/mod.rs
@@ -313,15 +313,15 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
313 ], 313 ],
314 ); 314 );
315 315
316 let comm_ep = alt.endpoint_interrupt_in(8, 255); 316 let comm_ep = alt.endpoint_interrupt_in(None, 8, 255);
317 317
318 // Data interface 318 // Data interface
319 let mut iface = func.interface(); 319 let mut iface = func.interface();
320 let data_if = iface.interface_number(); 320 let data_if = iface.interface_number();
321 let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); 321 let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None);
322 let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); 322 let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None);
323 let read_ep = alt.endpoint_bulk_out(max_packet_size); 323 let read_ep = alt.endpoint_bulk_out(None, max_packet_size);
324 let write_ep = alt.endpoint_bulk_in(max_packet_size); 324 let write_ep = alt.endpoint_bulk_in(None, max_packet_size);
325 325
326 drop(func); 326 drop(func);
327 327
diff --git a/embassy-usb/src/class/cmsis_dap_v2.rs b/embassy-usb/src/class/cmsis_dap_v2.rs
index a94e3ddb7..a9fd9cdf0 100644
--- a/embassy-usb/src/class/cmsis_dap_v2.rs
+++ b/embassy-usb/src/class/cmsis_dap_v2.rs
@@ -61,10 +61,10 @@ impl<'d, D: Driver<'d>> CmsisDapV2Class<'d, D> {
61 )); 61 ));
62 let mut interface = function.interface(); 62 let mut interface = function.interface();
63 let mut alt = interface.alt_setting(0xFF, 0, 0, Some(iface_string)); 63 let mut alt = interface.alt_setting(0xFF, 0, 0, Some(iface_string));
64 let read_ep = alt.endpoint_bulk_out(max_packet_size); 64 let read_ep = alt.endpoint_bulk_out(None, max_packet_size);
65 let write_ep = alt.endpoint_bulk_in(max_packet_size); 65 let write_ep = alt.endpoint_bulk_in(None, max_packet_size);
66 let trace_ep = if trace { 66 let trace_ep = if trace {
67 Some(alt.endpoint_bulk_in(max_packet_size)) 67 Some(alt.endpoint_bulk_in(None, max_packet_size))
68 } else { 68 } else {
69 None 69 None
70 }; 70 };
diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs
index 6d9e0aced..182e1f83f 100644
--- a/embassy-usb/src/class/hid.rs
+++ b/embassy-usb/src/class/hid.rs
@@ -133,9 +133,9 @@ fn build<'d, D: Driver<'d>>(
133 ], 133 ],
134 ); 134 );
135 135
136 let ep_in = alt.endpoint_interrupt_in(config.max_packet_size, config.poll_ms); 136 let ep_in = alt.endpoint_interrupt_in(None, config.max_packet_size, config.poll_ms);
137 let ep_out = if with_out_endpoint { 137 let ep_out = if with_out_endpoint {
138 Some(alt.endpoint_interrupt_out(config.max_packet_size, config.poll_ms)) 138 Some(alt.endpoint_interrupt_out(None, config.max_packet_size, config.poll_ms))
139 } else { 139 } else {
140 None 140 None
141 }; 141 };
diff --git a/embassy-usb/src/class/midi.rs b/embassy-usb/src/class/midi.rs
index 52a96f278..1d152ca44 100644
--- a/embassy-usb/src/class/midi.rs
+++ b/embassy-usb/src/class/midi.rs
@@ -129,14 +129,14 @@ impl<'d, D: Driver<'d>> MidiClass<'d, D> {
129 for i in 0..n_out_jacks { 129 for i in 0..n_out_jacks {
130 endpoint_data[2 + i as usize] = in_jack_id_emb(i); 130 endpoint_data[2 + i as usize] = in_jack_id_emb(i);
131 } 131 }
132 let read_ep = alt.endpoint_bulk_out(max_packet_size); 132 let read_ep = alt.endpoint_bulk_out(None, max_packet_size);
133 alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_out_jacks as usize]); 133 alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_out_jacks as usize]);
134 134
135 endpoint_data[1] = n_in_jacks; 135 endpoint_data[1] = n_in_jacks;
136 for i in 0..n_in_jacks { 136 for i in 0..n_in_jacks {
137 endpoint_data[2 + i as usize] = out_jack_id_emb(i); 137 endpoint_data[2 + i as usize] = out_jack_id_emb(i);
138 } 138 }
139 let write_ep = alt.endpoint_bulk_in(max_packet_size); 139 let write_ep = alt.endpoint_bulk_in(None, max_packet_size);
140 alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_in_jacks as usize]); 140 alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_in_jacks as usize]);
141 141
142 MidiClass { read_ep, write_ep } 142 MidiClass { read_ep, write_ep }
diff --git a/embassy-usb/src/class/uac1/speaker.rs b/embassy-usb/src/class/uac1/speaker.rs
index 1ff29088c..9565e2a25 100644
--- a/embassy-usb/src/class/uac1/speaker.rs
+++ b/embassy-usb/src/class/uac1/speaker.rs
@@ -268,9 +268,10 @@ impl<'d, D: Driver<'d>> Speaker<'d, D> {
268 268
269 alt.descriptor(CS_INTERFACE, &format_descriptor); 269 alt.descriptor(CS_INTERFACE, &format_descriptor);
270 270
271 let streaming_endpoint = alt.alloc_endpoint_out(EndpointType::Isochronous, max_packet_size, 1); 271 let streaming_endpoint = alt.alloc_endpoint_out(EndpointType::Isochronous, None, max_packet_size, 1);
272 let feedback_endpoint = alt.alloc_endpoint_in( 272 let feedback_endpoint = alt.alloc_endpoint_in(
273 EndpointType::Isochronous, 273 EndpointType::Isochronous,
274 None,
274 4, // Feedback packets are 24 bit (10.14 format). 275 4, // Feedback packets are 24 bit (10.14 format).
275 1, 276 1,
276 ); 277 );
diff --git a/embassy-usb/src/class/web_usb.rs b/embassy-usb/src/class/web_usb.rs
index 405944f14..154b219ca 100644
--- a/embassy-usb/src/class/web_usb.rs
+++ b/embassy-usb/src/class/web_usb.rs
@@ -84,7 +84,7 @@ impl<'d> Control<'d> {
84} 84}
85 85
86impl<'d> Handler for Control<'d> { 86impl<'d> Handler for Control<'d> {
87 fn control_in(&mut self, req: Request, _data: &mut [u8]) -> Option<InResponse> { 87 fn control_in(&mut self, req: Request, _data: &mut [u8]) -> Option<InResponse<'_>> {
88 let landing_value = if self.landing_url.is_some() { 1 } else { 0 }; 88 let landing_value = if self.landing_url.is_some() { 1 } else { 0 };
89 if req.request_type == RequestType::Vendor 89 if req.request_type == RequestType::Vendor
90 && req.recipient == Recipient::Device 90 && req.recipient == Recipient::Device
diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml
index 4d633e8a8..37183df97 100644
--- a/examples/boot/application/nrf/Cargo.toml
+++ b/examples/boot/application/nrf/Cargo.toml
@@ -8,10 +8,10 @@ license = "MIT OR Apache-2.0"
8embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] } 9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [] } 10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [] }
11embassy-nrf = { version = "0.3.1", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } 11embassy-nrf = { version = "0.5.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] }
12embassy-boot = { version = "0.4.0", path = "../../../../embassy-boot", features = [] } 12embassy-boot = { version = "0.5.0", path = "../../../../embassy-boot", features = [] }
13embassy-boot-nrf = { version = "0.4.0", path = "../../../../embassy-boot-nrf", features = [] } 13embassy-boot-nrf = { version = "0.6.0", path = "../../../../embassy-boot-nrf", features = [] }
14embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } 14embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" }
15 15
16defmt = { version = "1.0.1", optional = true } 16defmt = { version = "1.0.1", optional = true }
17defmt-rtt = { version = "1.0.0", optional = true } 17defmt-rtt = { version = "1.0.0", optional = true }
diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml
index be283fb27..e5568f6bb 100644
--- a/examples/boot/application/rp/Cargo.toml
+++ b/examples/boot/application/rp/Cargo.toml
@@ -8,9 +8,9 @@ license = "MIT OR Apache-2.0"
8embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } 9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [] } 10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [] }
11embassy-rp = { version = "0.4.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } 11embassy-rp = { version = "0.6.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] }
12embassy-boot-rp = { version = "0.5.0", path = "../../../../embassy-boot-rp", features = [] } 12embassy-boot-rp = { version = "0.6.0", path = "../../../../embassy-boot-rp", features = [] }
13embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } 13embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" }
14 14
15defmt = "1.0.1" 15defmt = "1.0.1"
16defmt-rtt = "1.0.0" 16defmt-rtt = "1.0.0"
diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs
index ede0c07da..e6d7b3d4f 100644
--- a/examples/boot/application/rp/src/bin/a.rs
+++ b/examples/boot/application/rp/src/bin/a.rs
@@ -54,7 +54,7 @@ async fn main(_s: Spawner) {
54 for chunk in APP_B.chunks(4096) { 54 for chunk in APP_B.chunks(4096) {
55 buf.0[..chunk.len()].copy_from_slice(chunk); 55 buf.0[..chunk.len()].copy_from_slice(chunk);
56 defmt::info!("writing block at offset {}", offset); 56 defmt::info!("writing block at offset {}", offset);
57 writer.write(offset, &buf.0[..]).unwrap(); 57 writer.write(offset, &buf.0[..chunk.len()]).unwrap();
58 offset += chunk.len() as u32; 58 offset += chunk.len() as u32;
59 } 59 }
60 watchdog.feed(); 60 watchdog.feed();
diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml
index b3466e288..be8b7bff1 100644
--- a/examples/boot/application/stm32f3/Cargo.toml
+++ b/examples/boot/application/stm32f3/Cargo.toml
@@ -9,8 +9,8 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } 9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } 10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32f303re", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32f303re", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32" } 12embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32" }
13embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } 13embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" }
14 14
15defmt = { version = "1.0.1", optional = true } 15defmt = { version = "1.0.1", optional = true }
16defmt-rtt = { version = "1.0.0", optional = true } 16defmt-rtt = { version = "1.0.0", optional = true }
diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml
index 72dbded5f..2b0175a0c 100644
--- a/examples/boot/application/stm32f7/Cargo.toml
+++ b/examples/boot/application/stm32f7/Cargo.toml
@@ -9,8 +9,8 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } 9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } 10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti", "single-bank"] } 11embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti", "single-bank"] }
12embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } 12embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] }
13embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } 13embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" }
14 14
15defmt = { version = "1.0.1", optional = true } 15defmt = { version = "1.0.1", optional = true }
16defmt-rtt = { version = "1.0.0", optional = true } 16defmt-rtt = { version = "1.0.0", optional = true }
diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml
index 57fb8312c..3c88f4241 100644
--- a/examples/boot/application/stm32h7/Cargo.toml
+++ b/examples/boot/application/stm32h7/Cargo.toml
@@ -9,8 +9,8 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } 9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } 10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32h743zi", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32h743zi", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } 12embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] }
13embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } 13embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" }
14 14
15defmt = { version = "1.0.1", optional = true } 15defmt = { version = "1.0.1", optional = true }
16defmt-rtt = { version = "1.0.0", optional = true } 16defmt-rtt = { version = "1.0.0", optional = true }
diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml
index 7dbbba138..b4e7e090a 100644
--- a/examples/boot/application/stm32l0/Cargo.toml
+++ b/examples/boot/application/stm32l0/Cargo.toml
@@ -9,8 +9,8 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } 9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } 10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l072cz", "time-driver-any", "exti", "memory-x"] } 11embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l072cz", "time-driver-any", "exti", "memory-x"] }
12embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } 12embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] }
13embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } 13embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" }
14 14
15defmt = { version = "1.0.1", optional = true } 15defmt = { version = "1.0.1", optional = true }
16defmt-rtt = { version = "1.0.0", optional = true } 16defmt-rtt = { version = "1.0.0", optional = true }
diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml
index 9549b2048..394578e1a 100644
--- a/examples/boot/application/stm32l1/Cargo.toml
+++ b/examples/boot/application/stm32l1/Cargo.toml
@@ -9,8 +9,8 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } 9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } 10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l151cb-a", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l151cb-a", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } 12embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] }
13embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } 13embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" }
14 14
15defmt = { version = "1.0.1", optional = true } 15defmt = { version = "1.0.1", optional = true }
16defmt-rtt = { version = "1.0.0", optional = true } 16defmt-rtt = { version = "1.0.0", optional = true }
diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml
index 03daeb0bc..abe0451fd 100644
--- a/examples/boot/application/stm32l4/Cargo.toml
+++ b/examples/boot/application/stm32l4/Cargo.toml
@@ -9,8 +9,8 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } 9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } 10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } 12embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] }
13embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } 13embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" }
14 14
15defmt = { version = "1.0.1", optional = true } 15defmt = { version = "1.0.1", optional = true }
16defmt-rtt = { version = "1.0.0", optional = true } 16defmt-rtt = { version = "1.0.0", optional = true }
diff --git a/examples/boot/application/stm32wb-dfu/Cargo.toml b/examples/boot/application/stm32wb-dfu/Cargo.toml
index e582628aa..bc4681f79 100644
--- a/examples/boot/application/stm32wb-dfu/Cargo.toml
+++ b/examples/boot/application/stm32wb-dfu/Cargo.toml
@@ -9,9 +9,9 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } 9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } 10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32wb55rg", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32wb55rg", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } 12embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] }
13embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } 13embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" }
14embassy-usb = { version = "0.4.0", path = "../../../../embassy-usb" } 14embassy-usb = { version = "0.5.0", path = "../../../../embassy-usb" }
15embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["application", "cortex-m"] } 15embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["application", "cortex-m"] }
16 16
17defmt = { version = "1.0.1", optional = true } 17defmt = { version = "1.0.1", optional = true }
diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml
index 3ed04b472..0552d109a 100644
--- a/examples/boot/application/stm32wl/Cargo.toml
+++ b/examples/boot/application/stm32wl/Cargo.toml
@@ -9,8 +9,8 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } 9embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } 10embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32wl55jc-cm4", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32wl55jc-cm4", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } 12embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] }
13embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } 13embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" }
14 14
15defmt = { version = "1.0.1", optional = true } 15defmt = { version = "1.0.1", optional = true }
16defmt-rtt = { version = "1.0.0", optional = true } 16defmt-rtt = { version = "1.0.0", optional = true }
diff --git a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml
index 0bb93b12e..d101e6ace 100644
--- a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml
+++ b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml
@@ -18,7 +18,7 @@ embedded-storage = "0.3.1"
18embedded-storage-async = "0.4.0" 18embedded-storage-async = "0.4.0"
19cfg-if = "1.0.0" 19cfg-if = "1.0.0"
20embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["dfu", "cortex-m"] } 20embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["dfu", "cortex-m"] }
21embassy-usb = { version = "0.4.0", path = "../../../../embassy-usb", default-features = false } 21embassy-usb = { version = "0.5.0", path = "../../../../embassy-usb", default-features = false }
22embassy-futures = { version = "0.1.1", path = "../../../../embassy-futures" } 22embassy-futures = { version = "0.1.1", path = "../../../../embassy-futures" }
23 23
24[features] 24[features]
diff --git a/examples/lpc55s69/Cargo.toml b/examples/lpc55s69/Cargo.toml
index 7f81e9c7f..1724a22d4 100644
--- a/examples/lpc55s69/Cargo.toml
+++ b/examples/lpc55s69/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7 7
8[dependencies] 8[dependencies]
9embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["rt"] } 9embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["lpc55", "rt", "defmt"] }
10embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] } 10embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] }
11embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 11embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt"] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt"] }
diff --git a/examples/mimxrt1011/.cargo/config.toml b/examples/mimxrt1011/.cargo/config.toml
new file mode 100644
index 000000000..12f4b27b2
--- /dev/null
+++ b/examples/mimxrt1011/.cargo/config.toml
@@ -0,0 +1,8 @@
1[target.thumbv7em-none-eabihf]
2runner = 'probe-rs run --chip MIMXRT1010'
3
4[build]
5target = "thumbv7em-none-eabihf" # Cortex-M7
6
7[env]
8DEFMT_LOG = "trace"
diff --git a/examples/mimxrt1011/Cargo.toml b/examples/mimxrt1011/Cargo.toml
new file mode 100644
index 000000000..cf4e4c163
--- /dev/null
+++ b/examples/mimxrt1011/Cargo.toml
@@ -0,0 +1,29 @@
1[package]
2name = "embassy-imxrt1011-examples"
3version = "0.1.0"
4edition = "2021"
5license = "MIT or Apache-2.0"
6
7[dependencies]
8cortex-m = { version = "0.7.7", features = ["inline-asm", "critical-section-single-core"] }
9cortex-m-rt = "0.7.3"
10defmt = "1.0.1"
11defmt-rtt = "1.0.0"
12
13embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
14embassy-futures = { version = "0.1.1", path = "../../embassy-futures" }
15embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["defmt", "mimxrt1011", "unstable-pac", "time-driver-pit"] }
16embassy-time = { version = "0.4", path = "../../embassy-time", features = ["defmt", ] } # "defmt-timestamp-uptime" # RT1011 hard faults currently with this enabled.
17embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
18embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
19embedded-hal-async = "1.0.0"
20
21imxrt-boot-gen = { version = "0.3.4", features = ["imxrt1010"] }
22panic-probe = { version = "1.0.0", features = ["print-defmt"] }
23panic-semihosting = "0.6.0"
24
25[build-dependencies]
26imxrt-rt = { version = "0.1.7", features = ["device"] }
27
28[profile.release]
29debug = 2
diff --git a/examples/mimxrt1011/build.rs b/examples/mimxrt1011/build.rs
new file mode 100644
index 000000000..99e172aba
--- /dev/null
+++ b/examples/mimxrt1011/build.rs
@@ -0,0 +1,14 @@
1use imxrt_rt::{Family, RuntimeBuilder};
2
3fn main() {
4 // The IMXRT1010-EVK technically has 128M of flash, but we only ever use 8MB so that the examples
5 // will build fine on the Adafruit Metro M7 boards.
6 RuntimeBuilder::from_flexspi(Family::Imxrt1010, 8 * 1024 * 1024)
7 .build()
8 .unwrap();
9
10 println!("cargo:rustc-link-arg-bins=--nmagic");
11 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
12 // Not link.x, as imxrt-rt needs to do some special things
13 println!("cargo:rustc-link-arg-bins=-Timxrt-link.x");
14}
diff --git a/examples/mimxrt1011/src/bin/blinky.rs b/examples/mimxrt1011/src/bin/blinky.rs
new file mode 100644
index 000000000..a5d5de6b3
--- /dev/null
+++ b/examples/mimxrt1011/src/bin/blinky.rs
@@ -0,0 +1,48 @@
1//! This example works on the following boards:
2//! - IMXRT1010-EVK
3//! - Adafruit Metro M7 (with microSD or with AirLift), requires an external button
4//! - Makerdiary iMX RT1011 Nano Kit (TODO: currently untested, please change this)
5//!
6//! Although beware you will need to change the GPIO pins being used (scroll down).
7
8#![no_std]
9#![no_main]
10
11use defmt::info;
12use embassy_executor::Spawner;
13use embassy_nxp::gpio::{Level, Output};
14use embassy_time::Timer;
15// Must include `embassy_imxrt1011_examples` to ensure the FCB gets linked.
16use {defmt_rtt as _, embassy_imxrt1011_examples as _, panic_probe as _};
17
18#[embassy_executor::main]
19async fn main(_spawner: Spawner) -> ! {
20 let p = embassy_nxp::init(Default::default());
21 info!("Hello world!");
22
23 /* Pick the pins to use depending on your board. */
24
25 // IMXRT1010-EVK
26 //
27 // LED (D25)
28 let led = p.GPIO_11;
29
30 // Adafruit Metro M7 (both microSD and AirLift variants)
31 //
32 // The LED is connected to D13 on the board.
33 // let led = p.GPIO_03;
34
35 // Makerdiary iMX RT1011 Nano Kit
36 //
37 // LED0
38 // let led = p.GPIO_SD_04;
39
40 let mut led = Output::new(led, Level::Low);
41
42 loop {
43 Timer::after_millis(500).await;
44
45 info!("Toggle");
46 led.toggle();
47 }
48}
diff --git a/examples/mimxrt1011/src/bin/button.rs b/examples/mimxrt1011/src/bin/button.rs
new file mode 100644
index 000000000..e63d7171d
--- /dev/null
+++ b/examples/mimxrt1011/src/bin/button.rs
@@ -0,0 +1,62 @@
1//! This example works on the following boards:
2//! - IMXRT1010-EVK
3//! - Adafruit Metro M7 (with microSD or with AirLift), requires an external button
4//! - Makerdiary iMX RT1011 Nano Kit (TODO: currently untested, please change this)
5//!
6//! Although beware you will need to change the GPIO pins being used (scroll down).
7
8#![no_std]
9#![no_main]
10
11use defmt::info;
12use embassy_executor::Spawner;
13use embassy_nxp::gpio::{Input, Level, Output, Pull};
14// Must include `embassy_imxrt1011_examples` to ensure the FCB gets linked.
15use {defmt_rtt as _, embassy_imxrt1011_examples as _, panic_probe as _};
16
17#[embassy_executor::main]
18async fn main(_spawner: Spawner) -> ! {
19 let p = embassy_nxp::init(Default::default());
20 info!("Hello world!");
21
22 /* Pick the pins to use depending on your board. */
23
24 // IMXRT1010-EVK
25 //
26 // LED (D25) and user button (SW4)
27 let (led, button) = (p.GPIO_11, p.GPIO_SD_05);
28
29 // Adafruit Metro M7 (both microSD and AirLift variants)
30 //
31 // The LED is connected to D13 on the board.
32 //
33 // In particular the Metro M7 has no board user buttons, so you will need to connect a button.
34 // Any other GPIO pin can be used. GPIO_04 is used for example since it is on pin D12.
35 // let (led, button) = (p.GPIO_03, p.GPIO_04);
36
37 // Makerdiary iMX RT1011 Nano Kit
38 //
39 // LED0 and user button.
40 // let (led, button) = (p.GPIO_SD_04, p.GPIO_SD_03);
41
42 let mut button = Input::new(button, Pull::Up100K);
43 let mut led = Output::new(led, Level::Low);
44 led.set_high();
45
46 loop {
47 button.wait_for_falling_edge().await;
48
49 info!("Toggled");
50 led.toggle();
51
52 // The RT1010EVK has a 100 nF debouncing capacitor which results in false positive events
53 // when listening for a falling edge in a loop, wait for the rising edge and then wait for
54 // stabilization.
55 button.wait_for_rising_edge().await;
56
57 // Stabilization.
58 for _ in 0..100_000 {
59 cortex_m::asm::nop();
60 }
61 }
62}
diff --git a/examples/mimxrt1011/src/lib.rs b/examples/mimxrt1011/src/lib.rs
new file mode 100644
index 000000000..f0391ef57
--- /dev/null
+++ b/examples/mimxrt1011/src/lib.rs
@@ -0,0 +1,75 @@
1//! FlexSPI configuration block (FCB) for iMXRT1011 boards.
2//!
3//! This is a generic FCB that should work with most QSPI flash.
4
5#![no_std]
6
7use imxrt_boot_gen::flexspi;
8use imxrt_boot_gen::flexspi::opcodes::sdr::*;
9use imxrt_boot_gen::flexspi::{
10 ColumnAddressWidth, Command, DeviceModeConfiguration, FlashPadType, Instr, LookupTable, Pads,
11 ReadSampleClockSource, Sequence, SequenceBuilder, SerialClockFrequency, SerialFlashRegion,
12 WaitTimeConfigurationCommands,
13};
14use imxrt_boot_gen::serial_flash::nor;
15
16/// While the IMXRT1010-EVK and Makerdiary iMX RT1011 Nano Kit have 128MBit of flash we limit to 64Mbit
17/// to allow the Metro M7 boards to use the same FCB configuration.
18const DENSITY_BITS: u32 = 64 * 1024 * 1024;
19const DENSITY_BYTES: u32 = DENSITY_BITS / 8;
20
21const SEQ_READ: Sequence = SequenceBuilder::new()
22 .instr(Instr::new(CMD, Pads::One, 0xEB))
23 .instr(Instr::new(RADDR, Pads::Four, 0x18))
24 .instr(Instr::new(DUMMY, Pads::Four, 0x06))
25 .instr(Instr::new(READ, Pads::Four, 0x04))
26 .build();
27
28const SEQ_READ_STATUS: Sequence = SequenceBuilder::new()
29 .instr(Instr::new(CMD, Pads::One, 0x05))
30 .instr(Instr::new(READ, Pads::One, 0x01))
31 .build();
32
33const SEQ_WRITE_ENABLE: Sequence = SequenceBuilder::new().instr(Instr::new(CMD, Pads::One, 0x06)).build();
34
35const SEQ_ERASE_SECTOR: Sequence = SequenceBuilder::new()
36 .instr(Instr::new(CMD, Pads::One, 0x20))
37 .instr(Instr::new(RADDR, Pads::One, 0x18))
38 .build();
39
40const SEQ_PAGE_PROGRAM: Sequence = SequenceBuilder::new()
41 .instr(Instr::new(CMD, Pads::One, 0x02))
42 .instr(Instr::new(RADDR, Pads::One, 0x18))
43 .instr(Instr::new(WRITE, Pads::One, 0x04))
44 .build();
45
46const SEQ_CHIP_ERASE: Sequence = SequenceBuilder::new().instr(Instr::new(CMD, Pads::One, 0x60)).build();
47
48const LUT: LookupTable = LookupTable::new()
49 .command(Command::Read, SEQ_READ)
50 .command(Command::ReadStatus, SEQ_READ_STATUS)
51 .command(Command::WriteEnable, SEQ_WRITE_ENABLE)
52 .command(Command::EraseSector, SEQ_ERASE_SECTOR)
53 .command(Command::PageProgram, SEQ_PAGE_PROGRAM)
54 .command(Command::ChipErase, SEQ_CHIP_ERASE);
55
56const COMMON_CONFIGURATION_BLOCK: flexspi::ConfigurationBlock = flexspi::ConfigurationBlock::new(LUT)
57 .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)
58 .cs_hold_time(0x03)
59 .cs_setup_time(0x03)
60 .column_address_width(ColumnAddressWidth::OtherDevices)
61 .device_mode_configuration(DeviceModeConfiguration::Disabled)
62 .wait_time_cfg_commands(WaitTimeConfigurationCommands::disable())
63 .flash_size(SerialFlashRegion::A1, DENSITY_BYTES)
64 .serial_clk_freq(SerialClockFrequency::MHz120)
65 .serial_flash_pad_type(FlashPadType::Quad);
66
67pub const SERIAL_NOR_CONFIGURATION_BLOCK: nor::ConfigurationBlock =
68 nor::ConfigurationBlock::new(COMMON_CONFIGURATION_BLOCK)
69 .page_size(256)
70 .sector_size(4096)
71 .ip_cmd_serial_clk_freq(nor::SerialClockFrequency::MHz30);
72
73#[unsafe(no_mangle)]
74#[cfg_attr(all(target_arch = "arm", target_os = "none"), link_section = ".fcb")]
75pub static FLEXSPI_CONFIGURATION_BLOCK: nor::ConfigurationBlock = SERIAL_NOR_CONFIGURATION_BLOCK;
diff --git a/examples/mimxrt1062-evk/.cargo/config.toml b/examples/mimxrt1062-evk/.cargo/config.toml
new file mode 100644
index 000000000..ca4c606dc
--- /dev/null
+++ b/examples/mimxrt1062-evk/.cargo/config.toml
@@ -0,0 +1,8 @@
1[target.thumbv7em-none-eabihf]
2runner = 'probe-rs run --chip MIMXRT1060'
3
4[build]
5target = "thumbv7em-none-eabihf" # Cortex-M7
6
7[env]
8DEFMT_LOG = "trace"
diff --git a/examples/mimxrt1062-evk/Cargo.toml b/examples/mimxrt1062-evk/Cargo.toml
new file mode 100644
index 000000000..430a26b41
--- /dev/null
+++ b/examples/mimxrt1062-evk/Cargo.toml
@@ -0,0 +1,29 @@
1[package]
2name = "embassy-imxrt1062-evk-examples"
3version = "0.1.0"
4edition = "2021"
5license = "MIT or Apache-2.0"
6
7[dependencies]
8cortex-m = { version = "0.7.7", features = ["inline-asm", "critical-section-single-core"] }
9cortex-m-rt = "0.7.3"
10defmt = "1.0.1"
11defmt-rtt = "1.0.0"
12
13embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
14embassy-futures = { version = "0.1.1", path = "../../embassy-futures" }
15embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["defmt", "mimxrt1062", "unstable-pac", "time-driver-pit"] }
16embassy-time = { version = "0.4", path = "../../embassy-time", features = ["defmt", ] } # "defmt-timestamp-uptime"
17embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
18embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
19embedded-hal-async = "1.0.0"
20
21imxrt-boot-gen = { version = "0.3.4", features = ["imxrt1060"] }
22panic-probe = { version = "1.0.0", features = ["print-defmt"] }
23panic-semihosting = "0.6.0"
24
25[build-dependencies]
26imxrt-rt = { version = "0.1.7", features = ["device"] }
27
28[profile.release]
29debug = 2
diff --git a/examples/mimxrt1062-evk/build.rs b/examples/mimxrt1062-evk/build.rs
new file mode 100644
index 000000000..e0e0d547e
--- /dev/null
+++ b/examples/mimxrt1062-evk/build.rs
@@ -0,0 +1,12 @@
1use imxrt_rt::{Family, RuntimeBuilder};
2
3fn main() {
4 RuntimeBuilder::from_flexspi(Family::Imxrt1060, 8 * 1024 * 1024)
5 .build()
6 .unwrap();
7
8 println!("cargo:rustc-link-arg-bins=--nmagic");
9 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
10 // Not link.x, as imxrt-rt needs to do some special things
11 println!("cargo:rustc-link-arg-bins=-Timxrt-link.x");
12}
diff --git a/examples/mimxrt1062-evk/src/bin/blinky.rs b/examples/mimxrt1062-evk/src/bin/blinky.rs
new file mode 100644
index 000000000..b6d90d94d
--- /dev/null
+++ b/examples/mimxrt1062-evk/src/bin/blinky.rs
@@ -0,0 +1,25 @@
1#![no_std]
2#![no_main]
3
4use defmt::info;
5use embassy_executor::Spawner;
6use embassy_nxp::gpio::{Level, Output};
7use embassy_time::Timer;
8// Must include `embassy_imxrt1062_evk_examples` to ensure the FCB gets linked.
9use {defmt_rtt as _, embassy_imxrt1062_evk_examples as _, panic_probe as _};
10
11#[embassy_executor::main]
12async fn main(_spawner: Spawner) -> ! {
13 let p = embassy_nxp::init(Default::default());
14 info!("Hello world!");
15
16 let led = p.GPIO_AD_B0_08;
17 let mut led = Output::new(led, Level::Low);
18
19 loop {
20 Timer::after_millis(500).await;
21
22 info!("Toggle");
23 led.toggle();
24 }
25}
diff --git a/examples/mimxrt1062-evk/src/bin/button.rs b/examples/mimxrt1062-evk/src/bin/button.rs
new file mode 100644
index 000000000..d60fa3dac
--- /dev/null
+++ b/examples/mimxrt1062-evk/src/bin/button.rs
@@ -0,0 +1,36 @@
1#![no_std]
2#![no_main]
3
4use defmt::info;
5use embassy_executor::Spawner;
6use embassy_nxp::gpio::{Input, Level, Output, Pull};
7use {defmt_rtt as _, embassy_imxrt1062_evk_examples as _, panic_probe as _};
8
9#[embassy_executor::main]
10async fn main(_spawner: Spawner) -> ! {
11 let p = embassy_nxp::init(Default::default());
12 info!("Hello world!");
13
14 // User LED (D8)
15 let led = p.GPIO_AD_B0_08;
16 // User button (SW5)
17 let button = p.WAKEUP;
18 let mut button = Input::new(button, Pull::Up100K);
19 let mut led = Output::new(led, Level::Low);
20 led.set_high();
21
22 loop {
23 button.wait_for_falling_edge().await;
24
25 info!("Toggled");
26 led.toggle();
27
28 // Software debounce.
29 button.wait_for_rising_edge().await;
30
31 // Stabilization.
32 for _ in 0..100_000 {
33 cortex_m::asm::nop();
34 }
35 }
36}
diff --git a/examples/mimxrt1062-evk/src/lib.rs b/examples/mimxrt1062-evk/src/lib.rs
new file mode 100644
index 000000000..3f99f9db3
--- /dev/null
+++ b/examples/mimxrt1062-evk/src/lib.rs
@@ -0,0 +1,60 @@
1//! FlexSPI configuration block (FCB) for the iMXRT1060-EVK
2//!
3//! This uses IS25WP QuadSPI flash.
4
5#![no_std]
6
7use imxrt_boot_gen::flexspi::opcodes::sdr::*;
8use imxrt_boot_gen::flexspi::{self, FlashPadType, ReadSampleClockSource, SerialClockFrequency, SerialFlashRegion, *};
9use imxrt_boot_gen::serial_flash::*;
10pub use nor::ConfigurationBlock;
11
12const SEQ_READ: Sequence = SequenceBuilder::new()
13 .instr(Instr::new(CMD, Pads::One, 0xEB))
14 .instr(Instr::new(RADDR, Pads::Four, 0x18))
15 .instr(Instr::new(DUMMY, Pads::Four, 0x06))
16 .instr(Instr::new(READ, Pads::Four, 0x04))
17 .build();
18const SEQ_READ_STATUS: Sequence = SequenceBuilder::new()
19 .instr(Instr::new(CMD, Pads::One, 0x05))
20 .instr(Instr::new(READ, Pads::One, 0x04))
21 .build();
22const SEQ_WRITE_ENABLE: Sequence = SequenceBuilder::new().instr(Instr::new(CMD, Pads::One, 0x06)).build();
23const SEQ_ERASE_SECTOR: Sequence = SequenceBuilder::new()
24 .instr(Instr::new(CMD, Pads::One, 0x20))
25 .instr(Instr::new(RADDR, Pads::One, 0x18))
26 .build();
27const SEQ_PAGE_PROGRAM: Sequence = SequenceBuilder::new()
28 .instr(Instr::new(CMD, Pads::One, 0x02))
29 .instr(Instr::new(RADDR, Pads::One, 0x18))
30 .instr(Instr::new(WRITE, Pads::One, 0x04))
31 .build();
32const SEQ_CHIP_ERASE: Sequence = SequenceBuilder::new().instr(Instr::new(CMD, Pads::One, 0x60)).build();
33
34const LUT: LookupTable = LookupTable::new()
35 .command(Command::Read, SEQ_READ)
36 .command(Command::ReadStatus, SEQ_READ_STATUS)
37 .command(Command::WriteEnable, SEQ_WRITE_ENABLE)
38 .command(Command::EraseSector, SEQ_ERASE_SECTOR)
39 .command(Command::PageProgram, SEQ_PAGE_PROGRAM)
40 .command(Command::ChipErase, SEQ_CHIP_ERASE);
41
42const COMMON_CONFIGURATION_BLOCK: flexspi::ConfigurationBlock = flexspi::ConfigurationBlock::new(LUT)
43 .version(Version::new(1, 4, 0))
44 .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)
45 .cs_hold_time(3)
46 .cs_setup_time(3)
47 .controller_misc_options(0x10)
48 .serial_flash_pad_type(FlashPadType::Quad)
49 .serial_clk_freq(SerialClockFrequency::MHz133)
50 .flash_size(SerialFlashRegion::A1, 8 * 1024 * 1024);
51
52pub const SERIAL_NOR_CONFIGURATION_BLOCK: nor::ConfigurationBlock =
53 nor::ConfigurationBlock::new(COMMON_CONFIGURATION_BLOCK)
54 .page_size(256)
55 .sector_size(4096)
56 .ip_cmd_serial_clk_freq(nor::SerialClockFrequency::MHz30);
57
58#[no_mangle]
59#[cfg_attr(all(target_arch = "arm", target_os = "none"), link_section = ".fcb")]
60pub static FLEXSPI_CONFIGURATION_BLOCK: nor::ConfigurationBlock = SERIAL_NOR_CONFIGURATION_BLOCK;
diff --git a/examples/mspm0g3507/.cargo/config.toml b/examples/mspm0g3507/.cargo/config.toml
index 34c720cdd..e711afaf2 100644
--- a/examples/mspm0g3507/.cargo/config.toml
+++ b/examples/mspm0g3507/.cargo/config.toml
@@ -6,4 +6,4 @@ runner = "probe-rs run --chip MSPM0G3507 --protocol=swd"
6target = "thumbv6m-none-eabi" 6target = "thumbv6m-none-eabi"
7 7
8[env] 8[env]
9DEFMT_LOG = "debug" 9DEFMT_LOG = "trace"
diff --git a/examples/mspm0g3507/Cargo.toml b/examples/mspm0g3507/Cargo.toml
index b6621c9c5..cc40b3109 100644
--- a/examples/mspm0g3507/Cargo.toml
+++ b/examples/mspm0g3507/Cargo.toml
@@ -17,5 +17,7 @@ defmt-rtt = "1.0.0"
17panic-probe = { version = "1.0.0", features = ["print-defmt"] } 17panic-probe = { version = "1.0.0", features = ["print-defmt"] }
18panic-semihosting = "0.6.0" 18panic-semihosting = "0.6.0"
19 19
20embedded-io-async = "0.6.1"
21
20[profile.release] 22[profile.release]
21debug = 2 23debug = 2
diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml
index d9e8ca2f9..f1c40192d 100644
--- a/examples/nrf-rtos-trace/Cargo.toml
+++ b/examples/nrf-rtos-trace/Cargo.toml
@@ -18,7 +18,7 @@ log = [
18embassy-sync = { version = "0.7.0", path = "../../embassy-sync" } 18embassy-sync = { version = "0.7.0", path = "../../embassy-sync" }
19embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace"] } 19embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace"] }
20embassy-time = { version = "0.4.0", path = "../../embassy-time" } 20embassy-time = { version = "0.4.0", path = "../../embassy-time" }
21embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } 21embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
22 22
23cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } 23cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
24cortex-m-rt = "0.7.0" 24cortex-m-rt = "0.7.0"
diff --git a/examples/nrf51/Cargo.toml b/examples/nrf51/Cargo.toml
index 91f78737f..a21d7f6ce 100644
--- a/examples/nrf51/Cargo.toml
+++ b/examples/nrf51/Cargo.toml
@@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 8embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
9embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 9embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
10embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } 10embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] }
11 11
12defmt = "1.0.1" 12defmt = "1.0.1"
13defmt-rtt = "1.0.0" 13defmt-rtt = "1.0.0"
diff --git a/examples/nrf52810/Cargo.toml b/examples/nrf52810/Cargo.toml
index 87da89efe..baa873f22 100644
--- a/examples/nrf52810/Cargo.toml
+++ b/examples/nrf52810/Cargo.toml
@@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
9embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 9embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
10embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 10embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
11embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 11embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
12embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 12embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
13 13
14defmt = "1.0.1" 14defmt = "1.0.1"
15defmt-rtt = "1.0.0" 15defmt-rtt = "1.0.0"
diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml
index afd269f72..3e499e9bc 100644
--- a/examples/nrf52840-rtic/Cargo.toml
+++ b/examples/nrf52840-rtic/Cargo.toml
@@ -11,7 +11,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
11embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 11embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime"] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime"] }
13embassy-time-queue-utils = { version = "0.1", path = "../../embassy-time-queue-utils", features = ["generic-queue-8"] } 13embassy-time-queue-utils = { version = "0.1", path = "../../embassy-time-queue-utils", features = ["generic-queue-8"] }
14embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 14embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
15 15
16defmt = "1.0.1" 16defmt = "1.0.1"
17defmt-rtt = "1.0.0" 17defmt-rtt = "1.0.0"
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml
index 4140e49d2..d2baebf8e 100644
--- a/examples/nrf52840/Cargo.toml
+++ b/examples/nrf52840/Cargo.toml
@@ -9,9 +9,9 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
9embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 9embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
10embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 10embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
11embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 11embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
12embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 12embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
13embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } 13embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] }
14embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
15embedded-io = { version = "0.6.0", features = ["defmt-03"] } 15embedded-io = { version = "0.6.0", features = ["defmt-03"] }
16embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } 16embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
17embassy-net-esp-hosted = { version = "0.2.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } 17embassy-net-esp-hosted = { version = "0.2.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] }
diff --git a/examples/nrf52840/src/bin/self_spawn_current_executor.rs b/examples/nrf52840/src/bin/self_spawn_current_executor.rs
index ec9569a64..ddb40dc53 100644
--- a/examples/nrf52840/src/bin/self_spawn_current_executor.rs
+++ b/examples/nrf52840/src/bin/self_spawn_current_executor.rs
@@ -10,7 +10,8 @@ use {defmt_rtt as _, panic_probe as _};
10async fn my_task(n: u32) { 10async fn my_task(n: u32) {
11 Timer::after_secs(1).await; 11 Timer::after_secs(1).await;
12 info!("Spawning self! {}", n); 12 info!("Spawning self! {}", n);
13 unwrap!(Spawner::for_current_executor().await.spawn(my_task(n + 1))); 13 let spawner = unsafe { Spawner::for_current_executor().await };
14 unwrap!(spawner.spawn(my_task(n + 1)));
14} 15}
15 16
16#[embassy_executor::main] 17#[embassy_executor::main]
diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml
index dc4fba4fd..bdebd5386 100644
--- a/examples/nrf5340/Cargo.toml
+++ b/examples/nrf5340/Cargo.toml
@@ -9,9 +9,9 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
9embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 9embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
10embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 10embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
11embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 11embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
12embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } 12embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] }
13embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } 13embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] }
14embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
15embedded-io-async = { version = "0.6.1" } 15embedded-io-async = { version = "0.6.1" }
16 16
17defmt = "1.0.1" 17defmt = "1.0.1"
diff --git a/examples/nrf54l15/Cargo.toml b/examples/nrf54l15/Cargo.toml
index 4b229d06d..27d5babee 100644
--- a/examples/nrf54l15/Cargo.toml
+++ b/examples/nrf54l15/Cargo.toml
@@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 8embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
9embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 9embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
10embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 10embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
11 11
12defmt = "1.0.1" 12defmt = "1.0.1"
13defmt-rtt = "1.0.0" 13defmt-rtt = "1.0.0"
diff --git a/examples/nrf9151/ns/Cargo.toml b/examples/nrf9151/ns/Cargo.toml
index a083aa5e7..2a492b595 100644
--- a/examples/nrf9151/ns/Cargo.toml
+++ b/examples/nrf9151/ns/Cargo.toml
@@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 8embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
9embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 9embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
10embassy-nrf = { version = "0.3.1", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 10embassy-nrf = { version = "0.5.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
11 11
12defmt = "1.0.1" 12defmt = "1.0.1"
13defmt-rtt = "1.0.0" 13defmt-rtt = "1.0.0"
diff --git a/examples/nrf9151/s/Cargo.toml b/examples/nrf9151/s/Cargo.toml
index ae98631ef..62ef3e4ce 100644
--- a/examples/nrf9151/s/Cargo.toml
+++ b/examples/nrf9151/s/Cargo.toml
@@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 8embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
9embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 9embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
10embassy-nrf = { version = "0.3.1", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 10embassy-nrf = { version = "0.5.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
11 11
12defmt = "1.0.1" 12defmt = "1.0.1"
13defmt-rtt = "1.0.0" 13defmt-rtt = "1.0.0"
diff --git a/examples/nrf9160/Cargo.toml b/examples/nrf9160/Cargo.toml
index 25aedf624..c896afdbe 100644
--- a/examples/nrf9160/Cargo.toml
+++ b/examples/nrf9160/Cargo.toml
@@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 8embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
9embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 9embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
10embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 10embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
11embassy-net-nrf91 = { version = "0.1.0", path = "../../embassy-net-nrf91", features = ["defmt"] } 11embassy-net-nrf91 = { version = "0.1.0", path = "../../embassy-net-nrf91", features = ["defmt"] }
12embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "proto-ipv4", "medium-ip"] } 12embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "proto-ipv4", "medium-ip"] }
13 13
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index c8a132a5e..eefd69315 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -6,18 +6,18 @@ license = "MIT OR Apache-2.0"
6 6
7 7
8[dependencies] 8[dependencies]
9embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal", features = ["defmt"] } 9embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal", features = ["defmt"] }
10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
13embassy-rp = { version = "0.4.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } 13embassy-rp = { version = "0.6.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] }
14embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "icmp", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns", "proto-ipv4", "proto-ipv6", "multicast"] } 15embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "icmp", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns", "proto-ipv4", "proto-ipv6", "multicast"] }
16embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } 16embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
17embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 17embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
18embassy-usb-logger = { version = "0.4.0", path = "../../embassy-usb-logger" } 18embassy-usb-logger = { version = "0.5.0", path = "../../embassy-usb-logger" }
19cyw43 = { version = "0.3.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } 19cyw43 = { version = "0.4.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] }
20cyw43-pio = { version = "0.4.0", path = "../../cyw43-pio", features = ["defmt"] } 20cyw43-pio = { version = "0.5.1", path = "../../cyw43-pio", features = ["defmt"] }
21 21
22defmt = "1.0.1" 22defmt = "1.0.1"
23defmt-rtt = "1.0.0" 23defmt-rtt = "1.0.0"
@@ -58,11 +58,5 @@ rand = { version = "0.9.0", default-features = false }
58embedded-sdmmc = "0.7.0" 58embedded-sdmmc = "0.7.0"
59 59
60[profile.release] 60[profile.release]
61debug = 2 61# Enable generation of debug symbols even on release builds
62lto = true 62debug = true
63opt-level = 'z'
64
65[profile.dev]
66debug = 2
67lto = true
68opt-level = "z"
diff --git a/examples/rp/src/bin/usb_raw_bulk.rs b/examples/rp/src/bin/usb_raw_bulk.rs
index 103269791..0747901d1 100644
--- a/examples/rp/src/bin/usb_raw_bulk.rs
+++ b/examples/rp/src/bin/usb_raw_bulk.rs
@@ -96,8 +96,8 @@ async fn main(_spawner: Spawner) {
96 let mut function = builder.function(0xFF, 0, 0); 96 let mut function = builder.function(0xFF, 0, 0);
97 let mut interface = function.interface(); 97 let mut interface = function.interface();
98 let mut alt = interface.alt_setting(0xFF, 0, 0, None); 98 let mut alt = interface.alt_setting(0xFF, 0, 0, None);
99 let mut read_ep = alt.endpoint_bulk_out(64); 99 let mut read_ep = alt.endpoint_bulk_out(None, 64);
100 let mut write_ep = alt.endpoint_bulk_in(64); 100 let mut write_ep = alt.endpoint_bulk_in(None, 64);
101 drop(function); 101 drop(function);
102 102
103 // Build the builder. 103 // Build the builder.
diff --git a/examples/rp/src/bin/usb_webusb.rs b/examples/rp/src/bin/usb_webusb.rs
index a5dc94d5b..5cecb92f0 100644
--- a/examples/rp/src/bin/usb_webusb.rs
+++ b/examples/rp/src/bin/usb_webusb.rs
@@ -125,8 +125,8 @@ impl<'d, D: Driver<'d>> WebEndpoints<'d, D> {
125 let mut iface = func.interface(); 125 let mut iface = func.interface();
126 let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None); 126 let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None);
127 127
128 let write_ep = alt.endpoint_bulk_in(config.max_packet_size); 128 let write_ep = alt.endpoint_bulk_in(None, config.max_packet_size);
129 let read_ep = alt.endpoint_bulk_out(config.max_packet_size); 129 let read_ep = alt.endpoint_bulk_out(None, config.max_packet_size);
130 130
131 WebEndpoints { write_ep, read_ep } 131 WebEndpoints { write_ep, read_ep }
132 } 132 }
diff --git a/examples/rp235x/Cargo.toml b/examples/rp235x/Cargo.toml
index c81b79ae1..4d3dc77b5 100644
--- a/examples/rp235x/Cargo.toml
+++ b/examples/rp235x/Cargo.toml
@@ -6,18 +6,18 @@ license = "MIT OR Apache-2.0"
6 6
7 7
8[dependencies] 8[dependencies]
9embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal", features = ["defmt"] } 9embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal", features = ["defmt"] }
10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
13embassy-rp = { version = "0.4.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } 13embassy-rp = { version = "0.6.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] }
14embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } 15embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] }
16embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } 16embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
17embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 17embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
18embassy-usb-logger = { version = "0.4.0", path = "../../embassy-usb-logger" } 18embassy-usb-logger = { version = "0.5.0", path = "../../embassy-usb-logger" }
19cyw43 = { version = "0.3.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } 19cyw43 = { version = "0.4.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] }
20cyw43-pio = { version = "0.4.0", path = "../../cyw43-pio", features = ["defmt"] } 20cyw43-pio = { version = "0.5.1", path = "../../cyw43-pio", features = ["defmt"] }
21 21
22defmt = "1.0.1" 22defmt = "1.0.1"
23defmt-rtt = "1.0.0" 23defmt-rtt = "1.0.0"
@@ -58,8 +58,5 @@ log = "0.4"
58embedded-sdmmc = "0.7.0" 58embedded-sdmmc = "0.7.0"
59 59
60[profile.release] 60[profile.release]
61debug = 2 61# Enable generation of debug symbols even on release builds
62 62debug = true
63[profile.dev]
64lto = true
65opt-level = "z"
diff --git a/examples/rp235x/memory.x b/examples/rp235x/memory.x
index c803896f6..4382e2065 100644
--- a/examples/rp235x/memory.x
+++ b/examples/rp235x/memory.x
@@ -17,8 +17,8 @@ MEMORY {
17 * of access times. 17 * of access times.
18 * Example: Separate stacks for core0 and core1. 18 * Example: Separate stacks for core0 and core1.
19 */ 19 */
20 SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K 20 SRAM8 : ORIGIN = 0x20080000, LENGTH = 4K
21 SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K 21 SRAM9 : ORIGIN = 0x20081000, LENGTH = 4K
22} 22}
23 23
24SECTIONS { 24SECTIONS {
diff --git a/examples/rp235x/src/bin/blinky_wifi.rs b/examples/rp235x/src/bin/blinky_wifi.rs
index 8c352ebc4..ef6057a1c 100644
--- a/examples/rp235x/src/bin/blinky_wifi.rs
+++ b/examples/rp235x/src/bin/blinky_wifi.rs
@@ -5,7 +5,7 @@
5#![no_std] 5#![no_std]
6#![no_main] 6#![no_main]
7 7
8use cyw43_pio::{PioSpi, DEFAULT_CLOCK_DIVIDER}; 8use cyw43_pio::{PioSpi, RM2_CLOCK_DIVIDER};
9use defmt::*; 9use defmt::*;
10use embassy_executor::Spawner; 10use embassy_executor::Spawner;
11use embassy_rp::bind_interrupts; 11use embassy_rp::bind_interrupts;
@@ -58,7 +58,9 @@ async fn main(spawner: Spawner) {
58 let spi = PioSpi::new( 58 let spi = PioSpi::new(
59 &mut pio.common, 59 &mut pio.common,
60 pio.sm0, 60 pio.sm0,
61 DEFAULT_CLOCK_DIVIDER, 61 // SPI communication won't work if the speed is too high, so we use a divider larger than `DEFAULT_CLOCK_DIVIDER`.
62 // See: https://github.com/embassy-rs/embassy/issues/3960.
63 RM2_CLOCK_DIVIDER,
62 pio.irq0, 64 pio.irq0,
63 cs, 65 cs,
64 p.PIN_24, 66 p.PIN_24,
diff --git a/examples/rp235x/src/bin/usb_webusb.rs b/examples/rp235x/src/bin/usb_webusb.rs
index 75d28c853..a68163b61 100644
--- a/examples/rp235x/src/bin/usb_webusb.rs
+++ b/examples/rp235x/src/bin/usb_webusb.rs
@@ -125,8 +125,8 @@ impl<'d, D: Driver<'d>> WebEndpoints<'d, D> {
125 let mut iface = func.interface(); 125 let mut iface = func.interface();
126 let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None); 126 let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None);
127 127
128 let write_ep = alt.endpoint_bulk_in(config.max_packet_size); 128 let write_ep = alt.endpoint_bulk_in(None, config.max_packet_size);
129 let read_ep = alt.endpoint_bulk_out(config.max_packet_size); 129 let read_ep = alt.endpoint_bulk_out(None, config.max_packet_size);
130 130
131 WebEndpoints { write_ep, read_ep } 131 WebEndpoints { write_ep, read_ep }
132 } 132 }
diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml
index 4cf07cef4..70a7b0895 100644
--- a/examples/stm32c0/Cargo.toml
+++ b/examples/stm32c0/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8# Change stm32c031c6 to your chip name, if necessary. 8# Change stm32c031c6 to your chip name, if necessary.
9embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } 9embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti", "chrono"] }
10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
@@ -19,6 +19,7 @@ cortex-m-rt = "0.7.0"
19embedded-hal = "0.2.6" 19embedded-hal = "0.2.6"
20panic-probe = { version = "1.0.0", features = ["print-defmt"] } 20panic-probe = { version = "1.0.0", features = ["print-defmt"] }
21heapless = { version = "0.8", default-features = false } 21heapless = { version = "0.8", default-features = false }
22chrono = { version = "^0.4", default-features = false}
22 23
23[profile.release] 24[profile.release]
24debug = 2 25debug = 2
diff --git a/examples/stm32c0/src/bin/rtc.rs b/examples/stm32c0/src/bin/rtc.rs
new file mode 100644
index 000000000..82d8a37ba
--- /dev/null
+++ b/examples/stm32c0/src/bin/rtc.rs
@@ -0,0 +1,35 @@
1#![no_std]
2#![no_main]
3
4use chrono::{NaiveDate, NaiveDateTime};
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::rtc::{Rtc, RtcConfig};
8use embassy_stm32::Config;
9use embassy_time::Timer;
10use {defmt_rtt as _, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 let config = Config::default();
15 let p = embassy_stm32::init(config);
16
17 info!("Hello World!");
18
19 let now = NaiveDate::from_ymd_opt(2020, 5, 15)
20 .unwrap()
21 .and_hms_opt(10, 30, 15)
22 .unwrap();
23
24 let mut rtc = Rtc::new(p.RTC, RtcConfig::default());
25
26 rtc.set_datetime(now.into()).expect("datetime not set");
27
28 loop {
29 let now: NaiveDateTime = rtc.now().unwrap().into();
30
31 info!("{}", now.and_utc().timestamp());
32
33 Timer::after_millis(1000).await;
34 }
35}
diff --git a/examples/stm32f0/src/bin/adc-watchdog.rs b/examples/stm32f0/src/bin/adc-watchdog.rs
new file mode 100644
index 000000000..ff98aac8e
--- /dev/null
+++ b/examples/stm32f0/src/bin/adc-watchdog.rs
@@ -0,0 +1,34 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::adc::{self, Adc, WatchdogChannels};
7use embassy_stm32::bind_interrupts;
8use embassy_stm32::peripherals::ADC1;
9use {defmt_rtt as _, panic_probe as _};
10
11bind_interrupts!(struct Irqs {
12 ADC1_COMP => adc::InterruptHandler<ADC1>;
13});
14
15#[embassy_executor::main]
16async fn main(_spawner: Spawner) {
17 let p = embassy_stm32::init(Default::default());
18 info!("ADC watchdog example");
19
20 let mut adc = Adc::new(p.ADC1, Irqs);
21 let pin = p.PC1;
22
23 loop {
24 // Wait for pin to go high
25 adc.init_watchdog(WatchdogChannels::from_channel(&pin), 0, 0x07F);
26 let v_high = adc.monitor_watchdog().await;
27 info!("ADC sample is high {}", v_high);
28
29 // Wait for pin to go low
30 adc.init_watchdog(WatchdogChannels::from_channel(&pin), 0x01f, 0xFFF);
31 let v_low = adc.monitor_watchdog().await;
32 info!("ADC sample is low {}", v_low);
33 }
34}
diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml
index 261733305..01f4fd84a 100644
--- a/examples/stm32f1/Cargo.toml
+++ b/examples/stm32f1/Cargo.toml
@@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [
10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } 13embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
15 15
16defmt = "1.0.1" 16defmt = "1.0.1"
diff --git a/examples/stm32f1/src/bin/input_capture.rs b/examples/stm32f1/src/bin/input_capture.rs
index 6fe8e0b50..84811fb95 100644
--- a/examples/stm32f1/src/bin/input_capture.rs
+++ b/examples/stm32f1/src/bin/input_capture.rs
@@ -39,7 +39,7 @@ async fn main(spawner: Spawner) {
39 39
40 unwrap!(spawner.spawn(blinky(p.PC13))); 40 unwrap!(spawner.spawn(blinky(p.PC13)));
41 41
42 let ch3 = CapturePin::new_ch3(p.PA2, Pull::None); 42 let ch3 = CapturePin::new(p.PA2, Pull::None);
43 let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); 43 let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default());
44 44
45 loop { 45 loop {
diff --git a/examples/stm32f1/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/pwm_input.rs
index afbef3edb..aa6a11ff8 100644
--- a/examples/stm32f1/src/bin/pwm_input.rs
+++ b/examples/stm32f1/src/bin/pwm_input.rs
@@ -38,7 +38,7 @@ async fn main(spawner: Spawner) {
38 38
39 unwrap!(spawner.spawn(blinky(p.PC13))); 39 unwrap!(spawner.spawn(blinky(p.PC13)));
40 40
41 let mut pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, khz(10)); 41 let mut pwm_input = PwmInput::new_ch1(p.TIM2, p.PA0, Pull::None, khz(10));
42 pwm_input.enable(); 42 pwm_input.enable();
43 43
44 loop { 44 loop {
diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml
index f675b0be1..ab7d6b255 100644
--- a/examples/stm32f3/Cargo.toml
+++ b/examples/stm32f3/Cargo.toml
@@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [
10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } 13embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
15 15
16defmt = "1.0.1" 16defmt = "1.0.1"
diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml
index b47a81e1b..6595804e1 100644
--- a/examples/stm32f334/Cargo.toml
+++ b/examples/stm32f334/Cargo.toml
@@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["de
9embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 9embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
10embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
12embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } 12embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
14 14
15defmt = "1.0.1" 15defmt = "1.0.1"
diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml
index edab9ea00..80c43f2ab 100644
--- a/examples/stm32f4/Cargo.toml
+++ b/examples/stm32f4/Cargo.toml
@@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["
10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt" ] } 13embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt" ] }
14embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } 14embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] }
15embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } 15embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
16embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 16embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
diff --git a/examples/stm32f4/src/bin/input_capture.rs b/examples/stm32f4/src/bin/input_capture.rs
index fe5e2bdfc..e15b4d26e 100644
--- a/examples/stm32f4/src/bin/input_capture.rs
+++ b/examples/stm32f4/src/bin/input_capture.rs
@@ -39,7 +39,7 @@ async fn main(spawner: Spawner) {
39 39
40 unwrap!(spawner.spawn(blinky(p.PB2))); 40 unwrap!(spawner.spawn(blinky(p.PB2)));
41 41
42 let ch3 = CapturePin::new_ch3(p.PB10, Pull::None); 42 let ch3 = CapturePin::new(p.PB10, Pull::None);
43 let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); 43 let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default());
44 44
45 loop { 45 loop {
diff --git a/examples/stm32f4/src/bin/pwm.rs b/examples/stm32f4/src/bin/pwm.rs
index 04811162b..e385842f0 100644
--- a/examples/stm32f4/src/bin/pwm.rs
+++ b/examples/stm32f4/src/bin/pwm.rs
@@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) {
14 let p = embassy_stm32::init(Default::default()); 14 let p = embassy_stm32::init(Default::default());
15 info!("Hello World!"); 15 info!("Hello World!");
16 16
17 let ch1_pin = PwmPin::new_ch1(p.PE9, OutputType::PushPull); 17 let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull);
18 let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default()); 18 let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default());
19 let mut ch1 = pwm.ch1(); 19 let mut ch1 = pwm.ch1();
20 ch1.enable(); 20 ch1.enable();
diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs
index 161f43c48..c981f1a76 100644
--- a/examples/stm32f4/src/bin/pwm_complementary.rs
+++ b/examples/stm32f4/src/bin/pwm_complementary.rs
@@ -16,8 +16,8 @@ async fn main(_spawner: Spawner) {
16 let p = embassy_stm32::init(Default::default()); 16 let p = embassy_stm32::init(Default::default());
17 info!("Hello World!"); 17 info!("Hello World!");
18 18
19 let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); 19 let ch1 = PwmPin::new(p.PE9, OutputType::PushPull);
20 let ch1n = ComplementaryPwmPin::new_ch1(p.PA7, OutputType::PushPull); 20 let ch1n = ComplementaryPwmPin::new(p.PA7, OutputType::PushPull);
21 let mut pwm = ComplementaryPwm::new( 21 let mut pwm = ComplementaryPwm::new(
22 p.TIM1, 22 p.TIM1,
23 Some(ch1), 23 Some(ch1),
diff --git a/examples/stm32f4/src/bin/pwm_input.rs b/examples/stm32f4/src/bin/pwm_input.rs
index 465cbe4f5..74167cbf2 100644
--- a/examples/stm32f4/src/bin/pwm_input.rs
+++ b/examples/stm32f4/src/bin/pwm_input.rs
@@ -38,7 +38,7 @@ async fn main(spawner: Spawner) {
38 38
39 unwrap!(spawner.spawn(blinky(p.PB2))); 39 unwrap!(spawner.spawn(blinky(p.PB2)));
40 40
41 let mut pwm_input = PwmInput::new(p.TIM3, p.PA6, Pull::None, khz(10)); 41 let mut pwm_input = PwmInput::new_ch1(p.TIM3, p.PA6, Pull::None, khz(10));
42 pwm_input.enable(); 42 pwm_input.enable();
43 43
44 loop { 44 loop {
diff --git a/examples/stm32f4/src/bin/ws2812_pwm.rs b/examples/stm32f4/src/bin/ws2812_pwm.rs
index ca924e181..5153e1cfd 100644
--- a/examples/stm32f4/src/bin/ws2812_pwm.rs
+++ b/examples/stm32f4/src/bin/ws2812_pwm.rs
@@ -50,7 +50,7 @@ async fn main(_spawner: Spawner) {
50 50
51 let mut ws2812_pwm = SimplePwm::new( 51 let mut ws2812_pwm = SimplePwm::new(
52 dp.TIM3, 52 dp.TIM3,
53 Some(PwmPin::new_ch1(dp.PB4, OutputType::PushPull)), 53 Some(PwmPin::new(dp.PB4, OutputType::PushPull)),
54 None, 54 None,
55 None, 55 None,
56 None, 56 None,
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml
index c5801ea90..5995211d2 100644
--- a/examples/stm32f7/Cargo.toml
+++ b/examples/stm32f7/Cargo.toml
@@ -12,7 +12,7 @@ embassy-executor = { version = "0.7.0", path = "../../embassy-executor", feature
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } 13embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] }
14embedded-io-async = { version = "0.6.1" } 14embedded-io-async = { version = "0.6.1" }
15embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } 15embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
16embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 16embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
17 17
18defmt = "1.0.1" 18defmt = "1.0.1"
diff --git a/examples/stm32f7/src/bin/cryp.rs b/examples/stm32f7/src/bin/cryp.rs
index 235853cb9..a31e9b4f2 100644
--- a/examples/stm32f7/src/bin/cryp.rs
+++ b/examples/stm32f7/src/bin/cryp.rs
@@ -68,7 +68,9 @@ async fn main(_spawner: Spawner) -> ! {
68 ); 68 );
69 69
70 // Decrypt in software using AES-GCM 128-bit 70 // Decrypt in software using AES-GCM 128-bit
71 let _ = cipher.decrypt_in_place(&iv.into(), aad.into(), &mut payload_vec); 71 cipher
72 .decrypt_in_place(&iv.into(), aad.into(), &mut payload_vec)
73 .unwrap();
72 74
73 let sw_end_time = Instant::now(); 75 let sw_end_time = Instant::now();
74 let sw_execution_time = sw_end_time - sw_start_time; 76 let sw_execution_time = sw_end_time - sw_start_time;
diff --git a/examples/stm32f7/src/bin/qspi.rs b/examples/stm32f7/src/bin/qspi.rs
index bd3287964..ab29ddeff 100644
--- a/examples/stm32f7/src/bin/qspi.rs
+++ b/examples/stm32f7/src/bin/qspi.rs
@@ -279,6 +279,7 @@ async fn main(_spawner: Spawner) -> ! {
279 prescaler: 16, 279 prescaler: 16,
280 cs_high_time: ChipSelectHighTime::_1Cycle, 280 cs_high_time: ChipSelectHighTime::_1Cycle,
281 fifo_threshold: FIFOThresholdLevel::_16Bytes, 281 fifo_threshold: FIFOThresholdLevel::_16Bytes,
282 sample_shifting: SampleShifting::None,
282 }; 283 };
283 let driver = Qspi::new_bank1( 284 let driver = Qspi::new_bank1(
284 p.QUADSPI, p.PF8, p.PF9, p.PE2, p.PF6, p.PF10, p.PB10, p.DMA2_CH7, config, 285 p.QUADSPI, p.PF8, p.PF9, p.PE2, p.PF6, p.PF10, p.PB10, p.DMA2_CH7, config,
diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml
index bf1e7250e..1c290fcfa 100644
--- a/examples/stm32g0/Cargo.toml
+++ b/examples/stm32g0/Cargo.toml
@@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [
10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-usb = { version = "0.4.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } 13embassy-usb = { version = "0.5.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] }
14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
15 15
16defmt = "1.0.1" 16defmt = "1.0.1"
diff --git a/examples/stm32g0/src/bin/hf_timer.rs b/examples/stm32g0/src/bin/hf_timer.rs
index dfb6e0edc..705905f01 100644
--- a/examples/stm32g0/src/bin/hf_timer.rs
+++ b/examples/stm32g0/src/bin/hf_timer.rs
@@ -37,8 +37,8 @@ async fn main(_spawner: Spawner) {
37 } 37 }
38 let p = embassy_stm32::init(config); 38 let p = embassy_stm32::init(config);
39 39
40 let ch1 = PwmPin::new_ch1(p.PA8, OutputType::PushPull); 40 let ch1 = PwmPin::new(p.PA8, OutputType::PushPull);
41 let ch1n = ComplementaryPwmPin::new_ch1(p.PA7, OutputType::PushPull); 41 let ch1n = ComplementaryPwmPin::new(p.PA7, OutputType::PushPull);
42 42
43 let mut pwm = ComplementaryPwm::new( 43 let mut pwm = ComplementaryPwm::new(
44 p.TIM1, 44 p.TIM1,
diff --git a/examples/stm32g0/src/bin/input_capture.rs b/examples/stm32g0/src/bin/input_capture.rs
index 08df4e043..df339d541 100644
--- a/examples/stm32g0/src/bin/input_capture.rs
+++ b/examples/stm32g0/src/bin/input_capture.rs
@@ -47,12 +47,12 @@ async fn main(spawner: Spawner) {
47 unwrap!(spawner.spawn(blinky(p.PB1))); 47 unwrap!(spawner.spawn(blinky(p.PB1)));
48 48
49 // Connect PB1 and PA8 with a 1k Ohm resistor 49 // Connect PB1 and PA8 with a 1k Ohm resistor
50 let ch1_pin = PwmPin::new_ch1(p.PA8, OutputType::PushPull); 50 let ch1_pin = PwmPin::new(p.PA8, OutputType::PushPull);
51 let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default()); 51 let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default());
52 pwm.ch1().enable(); 52 pwm.ch1().enable();
53 pwm.ch1().set_duty_cycle(50); 53 pwm.ch1().set_duty_cycle(50);
54 54
55 let ch1 = CapturePin::new_ch1(p.PA0, Pull::None); 55 let ch1 = CapturePin::new(p.PA0, Pull::None);
56 let mut ic = InputCapture::new(p.TIM2, Some(ch1), None, None, None, Irqs, khz(1000), Default::default()); 56 let mut ic = InputCapture::new(p.TIM2, Some(ch1), None, None, None, Irqs, khz(1000), Default::default());
57 57
58 let mut old_capture = 0; 58 let mut old_capture = 0;
diff --git a/examples/stm32g0/src/bin/pwm_complementary.rs b/examples/stm32g0/src/bin/pwm_complementary.rs
index 97b163c40..dbd9194c9 100644
--- a/examples/stm32g0/src/bin/pwm_complementary.rs
+++ b/examples/stm32g0/src/bin/pwm_complementary.rs
@@ -26,10 +26,10 @@ use {defmt_rtt as _, panic_probe as _};
26async fn main(_spawner: Spawner) { 26async fn main(_spawner: Spawner) {
27 let p = embassy_stm32::init(Default::default()); 27 let p = embassy_stm32::init(Default::default());
28 28
29 let ch1 = PwmPin::new_ch1(p.PA8, OutputType::PushPull); 29 let ch1 = PwmPin::new(p.PA8, OutputType::PushPull);
30 let ch1n = ComplementaryPwmPin::new_ch1(p.PA7, OutputType::PushPull); 30 let ch1n = ComplementaryPwmPin::new(p.PA7, OutputType::PushPull);
31 let ch2 = PwmPin::new_ch2(p.PB3, OutputType::PushPull); 31 let ch2 = PwmPin::new(p.PB3, OutputType::PushPull);
32 let ch2n = ComplementaryPwmPin::new_ch2(p.PB0, OutputType::PushPull); 32 let ch2n = ComplementaryPwmPin::new(p.PB0, OutputType::PushPull);
33 33
34 let mut pwm = ComplementaryPwm::new( 34 let mut pwm = ComplementaryPwm::new(
35 p.TIM1, 35 p.TIM1,
diff --git a/examples/stm32g0/src/bin/pwm_input.rs b/examples/stm32g0/src/bin/pwm_input.rs
index 9d6b5fe97..fd4f53f1e 100644
--- a/examples/stm32g0/src/bin/pwm_input.rs
+++ b/examples/stm32g0/src/bin/pwm_input.rs
@@ -42,12 +42,12 @@ async fn main(spawner: Spawner) {
42 42
43 unwrap!(spawner.spawn(blinky(p.PB1))); 43 unwrap!(spawner.spawn(blinky(p.PB1)));
44 // Connect PA8 and PA6 with a 1k Ohm resistor 44 // Connect PA8 and PA6 with a 1k Ohm resistor
45 let ch1_pin = PwmPin::new_ch1(p.PA8, OutputType::PushPull); 45 let ch1_pin = PwmPin::new(p.PA8, OutputType::PushPull);
46 let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default()); 46 let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default());
47 pwm.ch1().set_duty_cycle_fraction(1, 4); 47 pwm.ch1().set_duty_cycle_fraction(1, 4);
48 pwm.ch1().enable(); 48 pwm.ch1().enable();
49 49
50 let mut pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, khz(1000)); 50 let mut pwm_input = PwmInput::new_ch1(p.TIM2, p.PA0, Pull::None, khz(1000));
51 pwm_input.enable(); 51 pwm_input.enable();
52 52
53 loop { 53 loop {
diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml
index 3d2c2aa7d..a503420e5 100644
--- a/examples/stm32g4/Cargo.toml
+++ b/examples/stm32g4/Cargo.toml
@@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [
10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } 13embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
15usbd-hid = "0.8.1" 15usbd-hid = "0.8.1"
16 16
diff --git a/examples/stm32g4/src/bin/i2c_slave.rs b/examples/stm32g4/src/bin/i2c_slave.rs
new file mode 100644
index 000000000..a723a0e18
--- /dev/null
+++ b/examples/stm32g4/src/bin/i2c_slave.rs
@@ -0,0 +1,149 @@
1//! This example shows how to use an stm32 as both a master and a slave.
2#![no_std]
3#![no_main]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::i2c::{Address, OwnAddresses, SlaveCommandKind};
8use embassy_stm32::mode::Async;
9use embassy_stm32::time::Hertz;
10use embassy_stm32::{bind_interrupts, i2c, peripherals};
11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _};
13
14bind_interrupts!(struct Irqs {
15 I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;
16 I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
17 I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
18 I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
19});
20
21const DEV_ADDR: u8 = 0x42;
22
23#[embassy_executor::task]
24async fn device_task(mut dev: i2c::I2c<'static, Async, i2c::MultiMaster>) -> ! {
25 info!("Device start");
26
27 let mut state = 0;
28
29 loop {
30 let mut buf = [0u8; 128];
31 match dev.listen().await {
32 Ok(i2c::SlaveCommand {
33 kind: SlaveCommandKind::Read,
34 address: Address::SevenBit(DEV_ADDR),
35 }) => match dev.respond_to_read(&[state]).await {
36 Ok(i2c::SendStatus::LeftoverBytes(x)) => info!("tried to write {} extra bytes", x),
37 Ok(i2c::SendStatus::Done) => {}
38 Err(e) => error!("error while responding {}", e),
39 },
40 Ok(i2c::SlaveCommand {
41 kind: SlaveCommandKind::Write,
42 address: Address::SevenBit(DEV_ADDR),
43 }) => match dev.respond_to_write(&mut buf).await {
44 Ok(len) => {
45 info!("Device received write: {}", buf[..len]);
46
47 if match buf[0] {
48 // Set the state
49 0xC2 => {
50 state = buf[1];
51 true
52 }
53 // Reset State
54 0xC8 => {
55 state = 0;
56 true
57 }
58 x => {
59 error!("Invalid Write Read {:x}", x);
60 false
61 }
62 } {
63 match dev.respond_to_read(&[state]).await {
64 Ok(read_status) => info!(
65 "This read is part of a write/read transaction. The response read status {}",
66 read_status
67 ),
68 Err(i2c::Error::Timeout) => {
69 info!("The device only performed a write and it not also do a read")
70 }
71 Err(e) => error!("error while responding {}", e),
72 }
73 }
74 }
75 Err(e) => error!("error while receiving {}", e),
76 },
77 Ok(i2c::SlaveCommand { address, .. }) => {
78 defmt::unreachable!(
79 "The slave matched address: {}, which it was not configured for",
80 address
81 );
82 }
83 Err(e) => error!("{}", e),
84 }
85 }
86}
87
88#[embassy_executor::task]
89async fn controller_task(mut con: i2c::I2c<'static, Async, i2c::Master>) {
90 info!("Controller start");
91
92 loop {
93 let mut resp_buff = [0u8; 1];
94 for i in 0..10 {
95 match con.write_read(DEV_ADDR, &[0xC2, i], &mut resp_buff).await {
96 Ok(_) => {
97 info!("write_read response: {}", resp_buff);
98 defmt::assert_eq!(resp_buff[0], i);
99 }
100 Err(e) => error!("Error writing {}", e),
101 }
102
103 Timer::after_millis(100).await;
104 }
105 match con.read(DEV_ADDR, &mut resp_buff).await {
106 Ok(_) => {
107 info!("read response: {}", resp_buff);
108 // assert that the state is the last index that was written
109 defmt::assert_eq!(resp_buff[0], 9);
110 }
111 Err(e) => error!("Error writing {}", e),
112 }
113 match con.write_read(DEV_ADDR, &[0xC8], &mut resp_buff).await {
114 Ok(_) => {
115 info!("write_read response: {}", resp_buff);
116 // assert that the state has been reset
117 defmt::assert_eq!(resp_buff[0], 0);
118 }
119 Err(e) => error!("Error writing {}", e),
120 }
121 Timer::after_millis(100).await;
122 }
123}
124
125#[embassy_executor::main]
126async fn main(spawner: Spawner) {
127 let p = embassy_stm32::init(Default::default());
128 info!("Hello World!");
129
130 let speed = Hertz::khz(400);
131 let config = i2c::Config::default();
132
133 let d_addr_config = i2c::SlaveAddrConfig {
134 addr: OwnAddresses::OA1(Address::SevenBit(DEV_ADDR)),
135 general_call: false,
136 };
137 let d_sda = p.PA8;
138 let d_scl = p.PA9;
139 let device = i2c::I2c::new(p.I2C2, d_scl, d_sda, Irqs, p.DMA1_CH1, p.DMA1_CH2, speed, config)
140 .into_slave_multimaster(d_addr_config);
141
142 unwrap!(spawner.spawn(device_task(device)));
143
144 let c_sda = p.PB8;
145 let c_scl = p.PB7;
146 let controller = i2c::I2c::new(p.I2C1, c_sda, c_scl, Irqs, p.DMA1_CH3, p.DMA1_CH4, speed, config);
147
148 unwrap!(spawner.spawn(controller_task(controller)));
149}
diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs
index 6c965012c..5b4194927 100644
--- a/examples/stm32g4/src/bin/pwm.rs
+++ b/examples/stm32g4/src/bin/pwm.rs
@@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) {
14 let p = embassy_stm32::init(Default::default()); 14 let p = embassy_stm32::init(Default::default());
15 info!("Hello World!"); 15 info!("Hello World!");
16 16
17 let ch1_pin = PwmPin::new_ch1(p.PC0, OutputType::PushPull); 17 let ch1_pin = PwmPin::new(p.PC0, OutputType::PushPull);
18 let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default()); 18 let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default());
19 let mut ch1 = pwm.ch1(); 19 let mut ch1 = pwm.ch1();
20 ch1.enable(); 20 ch1.enable();
diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml
index f3fda7ff3..0ac9016d4 100644
--- a/examples/stm32h5/Cargo.toml
+++ b/examples/stm32h5/Cargo.toml
@@ -11,7 +11,7 @@ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["de
11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] } 13embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] }
14embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 15embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
16 16
17defmt = "1.0.1" 17defmt = "1.0.1"
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml
index 27c59d980..9053289ea 100644
--- a/examples/stm32h7/Cargo.toml
+++ b/examples/stm32h7/Cargo.toml
@@ -8,11 +8,11 @@ license = "MIT OR Apache-2.0"
8# Change stm32h743bi to your chip name, if necessary. 8# Change stm32h743bi to your chip name, if necessary.
9embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h743bi", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } 9embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h743bi", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] }
10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } 11embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" }
12embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 12embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
13embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 13embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
14embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } 14embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] }
15embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } 15embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
16embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 16embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
17 17
18defmt = "1.0.1" 18defmt = "1.0.1"
diff --git a/examples/stm32h7/src/bin/i2c_shared.rs b/examples/stm32h7/src/bin/i2c_shared.rs
index 136b91eeb..655ff901f 100644
--- a/examples/stm32h7/src/bin/i2c_shared.rs
+++ b/examples/stm32h7/src/bin/i2c_shared.rs
@@ -25,7 +25,7 @@ const SHTC3_WAKEUP: [u8; 2] = [0x35, 0x17];
25const SHTC3_MEASURE_RH_FIRST: [u8; 2] = [0x5c, 0x24]; 25const SHTC3_MEASURE_RH_FIRST: [u8; 2] = [0x5c, 0x24];
26const SHTC3_SLEEP: [u8; 2] = [0xb0, 0x98]; 26const SHTC3_SLEEP: [u8; 2] = [0xb0, 0x98];
27 27
28static I2C_BUS: StaticCell<NoopMutex<RefCell<I2c<'static, Async>>>> = StaticCell::new(); 28static I2C_BUS: StaticCell<NoopMutex<RefCell<I2c<'static, Async, i2c::Master>>>> = StaticCell::new();
29 29
30bind_interrupts!(struct Irqs { 30bind_interrupts!(struct Irqs {
31 I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>; 31 I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
@@ -33,7 +33,7 @@ bind_interrupts!(struct Irqs {
33}); 33});
34 34
35#[embassy_executor::task] 35#[embassy_executor::task]
36async fn temperature(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async>>) { 36async fn temperature(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async, i2c::Master>>) {
37 let mut data = [0u8; 2]; 37 let mut data = [0u8; 2];
38 38
39 loop { 39 loop {
@@ -50,7 +50,7 @@ async fn temperature(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Asyn
50} 50}
51 51
52#[embassy_executor::task] 52#[embassy_executor::task]
53async fn humidity(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async>>) { 53async fn humidity(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async, i2c::Master>>) {
54 let mut data = [0u8; 6]; 54 let mut data = [0u8; 6];
55 55
56 loop { 56 loop {
diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs
index 8de31ea5b..12abb8693 100644
--- a/examples/stm32h7/src/bin/low_level_timer_api.rs
+++ b/examples/stm32h7/src/bin/low_level_timer_api.rs
@@ -6,7 +6,7 @@ use embassy_executor::Spawner;
6use embassy_stm32::gpio::{AfType, Flex, OutputType, Speed}; 6use embassy_stm32::gpio::{AfType, Flex, OutputType, Speed};
7use embassy_stm32::time::{khz, Hertz}; 7use embassy_stm32::time::{khz, Hertz};
8use embassy_stm32::timer::low_level::{OutputCompareMode, Timer as LLTimer}; 8use embassy_stm32::timer::low_level::{OutputCompareMode, Timer as LLTimer};
9use embassy_stm32::timer::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance32bit4Channel}; 9use embassy_stm32::timer::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance32bit4Channel, TimerPin};
10use embassy_stm32::{Config, Peri}; 10use embassy_stm32::{Config, Peri};
11use embassy_time::Timer; 11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _}; 12use {defmt_rtt as _, panic_probe as _};
@@ -67,10 +67,10 @@ pub struct SimplePwm32<'d, T: GeneralInstance32bit4Channel> {
67impl<'d, T: GeneralInstance32bit4Channel> SimplePwm32<'d, T> { 67impl<'d, T: GeneralInstance32bit4Channel> SimplePwm32<'d, T> {
68 pub fn new( 68 pub fn new(
69 tim: Peri<'d, T>, 69 tim: Peri<'d, T>,
70 ch1: Peri<'d, impl Channel1Pin<T>>, 70 ch1: Peri<'d, impl TimerPin<T, Ch1>>,
71 ch2: Peri<'d, impl Channel2Pin<T>>, 71 ch2: Peri<'d, impl TimerPin<T, Ch2>>,
72 ch3: Peri<'d, impl Channel3Pin<T>>, 72 ch3: Peri<'d, impl TimerPin<T, Ch3>>,
73 ch4: Peri<'d, impl Channel4Pin<T>>, 73 ch4: Peri<'d, impl TimerPin<T, Ch4>>,
74 freq: Hertz, 74 freq: Hertz,
75 ) -> Self { 75 ) -> Self {
76 let af1 = ch1.af_num(); 76 let af1 = ch1.af_num();
diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs
index a1c53fc3f..73b43be69 100644
--- a/examples/stm32h7/src/bin/pwm.rs
+++ b/examples/stm32h7/src/bin/pwm.rs
@@ -36,7 +36,7 @@ async fn main(_spawner: Spawner) {
36 let p = embassy_stm32::init(config); 36 let p = embassy_stm32::init(config);
37 info!("Hello World!"); 37 info!("Hello World!");
38 38
39 let ch1_pin = PwmPin::new_ch1(p.PA6, OutputType::PushPull); 39 let ch1_pin = PwmPin::new(p.PA6, OutputType::PushPull);
40 let mut pwm = SimplePwm::new(p.TIM3, Some(ch1_pin), None, None, None, khz(10), Default::default()); 40 let mut pwm = SimplePwm::new(p.TIM3, Some(ch1_pin), None, None, None, khz(10), Default::default());
41 let mut ch1 = pwm.ch1(); 41 let mut ch1 = pwm.ch1();
42 ch1.enable(); 42 ch1.enable();
diff --git a/examples/stm32h723/src/bin/spdifrx.rs b/examples/stm32h723/src/bin/spdifrx.rs
index a04d7cb34..6d29e8a4d 100644
--- a/examples/stm32h723/src/bin/spdifrx.rs
+++ b/examples/stm32h723/src/bin/spdifrx.rs
@@ -7,9 +7,9 @@
7 7
8use defmt::{info, trace}; 8use defmt::{info, trace};
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_futures::select::{self, select, Either}; 10use embassy_futures::select::{select, Either};
11use embassy_stm32::spdifrx::{self, Spdifrx}; 11use embassy_stm32::spdifrx::{self, Spdifrx};
12use embassy_stm32::{bind_interrupts, peripherals, sai}; 12use embassy_stm32::{bind_interrupts, peripherals, sai, Peri};
13use grounded::uninit::GroundedArrayCell; 13use grounded::uninit::GroundedArrayCell;
14use hal::sai::*; 14use hal::sai::*;
15use {defmt_rtt as _, embassy_stm32 as hal, panic_probe as _}; 15use {defmt_rtt as _, embassy_stm32 as hal, panic_probe as _};
@@ -25,10 +25,10 @@ const DMA_BUFFER_LENGTH: usize = HALF_DMA_BUFFER_LENGTH * 2; // 2 half-blocks
25 25
26// DMA buffers must be in special regions. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions 26// DMA buffers must be in special regions. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions
27#[unsafe(link_section = ".sram1")] 27#[unsafe(link_section = ".sram1")]
28static mut SPDIFRX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit(); 28static SPDIFRX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit();
29 29
30#[unsafe(link_section = ".sram4")] 30#[unsafe(link_section = ".sram4")]
31static mut SAI_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit(); 31static SAI_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit();
32 32
33#[embassy_executor::main] 33#[embassy_executor::main]
34async fn main(_spawner: Spawner) { 34async fn main(_spawner: Spawner) {
@@ -144,9 +144,9 @@ async fn main(_spawner: Spawner) {
144/// 144///
145/// Used (again) after dropping the SPDIFRX instance, in case of errors (e.g. source disconnect). 145/// Used (again) after dropping the SPDIFRX instance, in case of errors (e.g. source disconnect).
146fn new_spdif_receiver<'d>( 146fn new_spdif_receiver<'d>(
147 spdifrx: &'d mut peripherals::SPDIFRX1, 147 spdifrx: Peri<'d, peripherals::SPDIFRX1>,
148 input_pin: &'d mut peripherals::PD7, 148 input_pin: Peri<'d, peripherals::PD7>,
149 dma: &'d mut peripherals::DMA2_CH7, 149 dma: Peri<'d, peripherals::DMA2_CH7>,
150 buf: &'d mut [u32], 150 buf: &'d mut [u32],
151) -> Spdifrx<'d, peripherals::SPDIFRX1> { 151) -> Spdifrx<'d, peripherals::SPDIFRX1> {
152 Spdifrx::new(spdifrx, Irqs, spdifrx::Config::default(), input_pin, dma, buf) 152 Spdifrx::new(spdifrx, Irqs, spdifrx::Config::default(), input_pin, dma, buf)
@@ -156,11 +156,11 @@ fn new_spdif_receiver<'d>(
156/// 156///
157/// Used (again) after dropping the SAI4 instance, in case of errors (e.g. buffer overrun). 157/// Used (again) after dropping the SAI4 instance, in case of errors (e.g. buffer overrun).
158fn new_sai_transmitter<'d>( 158fn new_sai_transmitter<'d>(
159 sai: &'d mut peripherals::SAI4, 159 sai: Peri<'d, peripherals::SAI4>,
160 sck: &'d mut peripherals::PD13, 160 sck: Peri<'d, peripherals::PD13>,
161 sd: &'d mut peripherals::PC1, 161 sd: Peri<'d, peripherals::PC1>,
162 fs: &'d mut peripherals::PD12, 162 fs: Peri<'d, peripherals::PD12>,
163 dma: &'d mut peripherals::BDMA_CH0, 163 dma: Peri<'d, peripherals::BDMA_CH0>,
164 buf: &'d mut [u32], 164 buf: &'d mut [u32],
165) -> Sai<'d, peripherals::SAI4, u32> { 165) -> Sai<'d, peripherals::SAI4, u32> {
166 let mut sai_config = hal::sai::Config::default(); 166 let mut sai_config = hal::sai::Config::default();
diff --git a/examples/stm32h735/Cargo.toml b/examples/stm32h735/Cargo.toml
index 8d23c346a..e4938e15b 100644
--- a/examples/stm32h735/Cargo.toml
+++ b/examples/stm32h735/Cargo.toml
@@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h735ig", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } 8embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h735ig", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] }
9embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 9embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
10embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } 10embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" }
11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
diff --git a/examples/stm32h742/Cargo.toml b/examples/stm32h742/Cargo.toml
index 31eff4379..efa71e144 100644
--- a/examples/stm32h742/Cargo.toml
+++ b/examples/stm32h742/Cargo.toml
@@ -34,7 +34,7 @@ embassy-net = { version = "0.7.0", path = "../../embassy-net", features = [
34 "medium-ethernet", 34 "medium-ethernet",
35] } 35] }
36embedded-io-async = { version = "0.6.1" } 36embedded-io-async = { version = "0.6.1" }
37embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = [ 37embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = [
38 "defmt", 38 "defmt",
39] } 39] }
40embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 40embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
diff --git a/examples/stm32h742/src/bin/qspi.rs b/examples/stm32h742/src/bin/qspi.rs
index aee07f3f2..50e37ec52 100644
--- a/examples/stm32h742/src/bin/qspi.rs
+++ b/examples/stm32h742/src/bin/qspi.rs
@@ -272,6 +272,7 @@ async fn main(_spawner: Spawner) -> ! {
272 prescaler: 16, 272 prescaler: 16,
273 cs_high_time: ChipSelectHighTime::_1Cycle, 273 cs_high_time: ChipSelectHighTime::_1Cycle,
274 fifo_threshold: FIFOThresholdLevel::_16Bytes, 274 fifo_threshold: FIFOThresholdLevel::_16Bytes,
275 sample_shifting: SampleShifting::None,
275 }; 276 };
276 let driver = Qspi::new_blocking_bank1(p.QUADSPI, p.PD11, p.PD12, p.PE2, p.PD13, p.PB2, p.PB10, config); 277 let driver = Qspi::new_blocking_bank1(p.QUADSPI, p.PD11, p.PD12, p.PE2, p.PD13, p.PB2, p.PB10, config);
277 let mut flash = FlashMemory::new(driver); 278 let mut flash = FlashMemory::new(driver);
diff --git a/examples/stm32h755cm4/Cargo.toml b/examples/stm32h755cm4/Cargo.toml
index 71bd50d60..eaaeb1ac0 100644
--- a/examples/stm32h755cm4/Cargo.toml
+++ b/examples/stm32h755cm4/Cargo.toml
@@ -8,11 +8,11 @@ license = "MIT OR Apache-2.0"
8# Change stm32h755zi-cm4 to your chip name, if necessary. 8# Change stm32h755zi-cm4 to your chip name, if necessary.
9embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm4", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } 9embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm4", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] }
10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } 11embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" }
12embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 12embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
13embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 13embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
14embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } 14embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] }
15embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } 15embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
16embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 16embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
17 17
18defmt = "1.0.1" 18defmt = "1.0.1"
diff --git a/examples/stm32h755cm7/Cargo.toml b/examples/stm32h755cm7/Cargo.toml
index 8e960932a..e7fc05948 100644
--- a/examples/stm32h755cm7/Cargo.toml
+++ b/examples/stm32h755cm7/Cargo.toml
@@ -8,11 +8,11 @@ license = "MIT OR Apache-2.0"
8# Change stm32h743bi to your chip name, if necessary. 8# Change stm32h743bi to your chip name, if necessary.
9embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm7", "time-driver-tim3", "exti", "memory-x", "unstable-pac", "chrono"] } 9embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm7", "time-driver-tim3", "exti", "memory-x", "unstable-pac", "chrono"] }
10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } 11embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" }
12embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 12embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
13embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 13embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
14embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } 14embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] }
15embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } 15embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
16embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 16embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
17 17
18defmt = "1.0.1" 18defmt = "1.0.1"
diff --git a/examples/stm32h7b0/Cargo.toml b/examples/stm32h7b0/Cargo.toml
index 72f86e0cf..bfa7f9202 100644
--- a/examples/stm32h7b0/Cargo.toml
+++ b/examples/stm32h7b0/Cargo.toml
@@ -7,11 +7,11 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7b0vb", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } 8embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7b0vb", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] }
9embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 9embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
10embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } 10embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" }
11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } 13embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] }
14embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 15embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
16 16
17defmt = "1.0.1" 17defmt = "1.0.1"
diff --git a/examples/stm32h7rs/Cargo.toml b/examples/stm32h7rs/Cargo.toml
index 5f1ce8dfc..9794c6dbd 100644
--- a/examples/stm32h7rs/Cargo.toml
+++ b/examples/stm32h7rs/Cargo.toml
@@ -11,7 +11,7 @@ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["de
11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "udp", "medium-ethernet", "medium-ip", "proto-ipv4"] } 13embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "udp", "medium-ethernet", "medium-ip", "proto-ipv4"] }
14embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 15embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
16 16
17defmt = "1.0.1" 17defmt = "1.0.1"
diff --git a/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs b/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs
index 88d914180..59045ca2e 100644
--- a/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs
+++ b/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs
@@ -3,7 +3,8 @@
3 3
4//! For Nucleo STM32H7S3L8 MB1737, has MX25UW25645GXDI00 4//! For Nucleo STM32H7S3L8 MB1737, has MX25UW25645GXDI00
5//! 5//!
6//! TODO: Currently this only uses single SPI, pending flash chip documentation for octo SPI. 6
7use core::cmp::min;
7 8
8use defmt::info; 9use defmt::info;
9use embassy_executor::Spawner; 10use embassy_executor::Spawner;
@@ -52,14 +53,16 @@ async fn main(_spawner: Spawner) {
52 fifo_threshold: FIFOThresholdLevel::_4Bytes, 53 fifo_threshold: FIFOThresholdLevel::_4Bytes,
53 memory_type: MemoryType::Macronix, 54 memory_type: MemoryType::Macronix,
54 delay_hold_quarter_cycle: true, 55 delay_hold_quarter_cycle: true,
55 // memory_type: MemoryType::Micron,
56 // delay_hold_quarter_cycle: false,
57 device_size: MemorySize::_32MiB, 56 device_size: MemorySize::_32MiB,
58 chip_select_high_time: ChipSelectHighTime::_2Cycle, 57 chip_select_high_time: ChipSelectHighTime::_2Cycle,
59 free_running_clock: false, 58 free_running_clock: false,
60 clock_mode: false, 59 clock_mode: false,
61 wrap_size: WrapSize::None, 60 wrap_size: WrapSize::None,
62 // 300mhz / (4+1) = 60mhz. Unsure the limit, need to find a MX25UW25645GXDI00 datasheet. 61 // 300 MHz clock / (3 + 1) = 75 MHz. This is above the max for READ instructions so the
62 // FAST READ must be used. The nucleo board's flash can run at up to 133 MHz in SPI mode
63 // and 200 MHz in OPI mode. This clock prescaler must be even otherwise the clock will not
64 // have symmetric high and low times.
65 // The clock can also be fed by one of the PLLs to allow for more flexible clock rates.
63 clock_prescaler: 3, 66 clock_prescaler: 3,
64 sample_shifting: false, 67 sample_shifting: false,
65 chip_select_boundary: 0, 68 chip_select_boundary: 0,
@@ -71,28 +74,41 @@ async fn main(_spawner: Spawner) {
71 74
72 // Not necessary, but recommended if using XIP 75 // Not necessary, but recommended if using XIP
73 cor.SCB.enable_icache(); 76 cor.SCB.enable_icache();
77 // Note: Enabling data cache can cause issues with DMA transfers.
74 cor.SCB.enable_dcache(&mut cor.CPUID); 78 cor.SCB.enable_dcache(&mut cor.CPUID);
75 79
76 let xspi = embassy_stm32::xspi::Xspi::new_blocking_xspi( 80 let xspi = embassy_stm32::xspi::Xspi::new_blocking_xspi(
77 p.XSPI2, p.PN6, p.PN2, p.PN3, p.PN4, p.PN5, p.PN8, p.PN9, p.PN10, p.PN11, p.PN1, spi_config, 81 p.XSPI2, p.PN6, p.PN2, p.PN3, p.PN4, p.PN5, p.PN8, p.PN9, p.PN10, p.PN11, p.PN1, spi_config,
78 ); 82 );
79 83
80 let mut flash = FlashMemory::new(xspi).await; 84 let mut flash = SpiFlashMemory::new(xspi);
81 85
82 let flash_id = flash.read_id(); 86 let flash_id = flash.read_id();
83 info!("FLASH ID: {=[u8]:x}", flash_id); 87 info!("FLASH ID: {=[u8]:x}", flash_id);
84 88
85 let mut wr_buf = [0u8; 8]; 89 // Erase the first sector
86 for i in 0..8 { 90 flash.erase_sector(0);
87 wr_buf[i] = 0x90 + i as u8; 91
88 } 92 // Write some data into the flash. This writes more than one page to test that functionality.
89 let mut rd_buf = [0u8; 8]; 93 let mut wr_buf = [0u8; 512];
90 flash.erase_sector(0).await; 94 let base_number: u8 = 0x90;
91 flash.write_memory(0, &wr_buf, true).await; 95 for i in 0..512 {
92 flash.read_memory(0, &mut rd_buf, true); 96 wr_buf[i] = base_number.wrapping_add(i as u8);
93 info!("WRITE BUF: {=[u8]:#X}", wr_buf); 97 }
94 info!("READ BUF: {=[u8]:#X}", rd_buf); 98 flash.write_memory(0, &wr_buf);
95 flash.enable_mm().await; 99
100 // Read the data back and verify it.
101 let mut rd_buf = [0u8; 512];
102 let start_time = embassy_time::Instant::now();
103 flash.read_memory(0, &mut rd_buf);
104 let elapsed = start_time.elapsed();
105 info!("Read 512 bytes in {} us in SPI mode", elapsed.as_micros());
106 info!("WRITE BUF: {=[u8]:#X}", wr_buf[0..32]);
107 info!("READ BUF: {=[u8]:#X}", rd_buf[0..32]);
108
109 assert_eq!(wr_buf, rd_buf, "Read buffer does not match write buffer");
110
111 flash.enable_mm();
96 info!("Enabled memory mapped mode"); 112 info!("Enabled memory mapped mode");
97 113
98 let first_u32 = unsafe { *(0x70000000 as *const u32) }; 114 let first_u32 = unsafe { *(0x70000000 as *const u32) };
@@ -103,10 +119,53 @@ async fn main(_spawner: Spawner) {
103 assert_eq!(second_u32, 0x97969594); 119 assert_eq!(second_u32, 0x97969594);
104 info!("second_u32 {:08x}", first_u32); 120 info!("second_u32 {:08x}", first_u32);
105 121
106 flash.disable_mm().await; 122 flash.disable_mm();
107 info!("Disabled memory mapped mode"); 123 info!("Disabled memory mapped mode");
108 124
125 let flash_id = flash.read_id();
126 info!("FLASH ID: {=[u8]:x}", flash_id);
127
128 let mut flash = flash.into_octo();
129
130 Timer::after_millis(100).await;
131
132 let flash_id = flash.read_id();
133 info!("FLASH ID in OPI mode: {=[u8]:x}", flash_id);
134
135 flash.erase_sector(0);
136
137 let mut rd_buf = [0u8; 512];
138 flash.read_memory(0, &mut rd_buf);
139 info!("READ BUF after erase: {=[u8]:#X}", rd_buf[0..32]);
140
141 assert_eq!(rd_buf, [0xFF; 512], "Read buffer is not all 0xFF after erase");
142
143 flash.write_memory(0, &wr_buf);
144 let start = embassy_time::Instant::now();
145 flash.read_memory(0, &mut rd_buf);
146 let elapsed = start.elapsed();
147 info!("Read 512 bytes in {} us in OPI mode", elapsed.as_micros());
148 info!("READ BUF after write: {=[u8]:#X}", rd_buf[0..32]);
149 assert_eq!(wr_buf, rd_buf, "Read buffer does not match write buffer in OPI mode");
150
151 flash.enable_mm();
152 info!("Enabled memory mapped mode in OPI mode");
153 let first_u32 = unsafe { *(0x70000000 as *const u32) };
154 assert_eq!(first_u32, 0x93929190);
155 info!("first_u32 {:08x}", first_u32);
156 let second_u32 = unsafe { *(0x70000004 as *const u32) };
157 assert_eq!(second_u32, 0x97969594);
158 info!("second_u32 {:08x}", first_u32);
159 flash.disable_mm();
160 info!("Disabled memory mapped mode in OPI mode");
161
162 // Reset back to SPI mode
163 let mut flash = flash.into_spi();
164 let flash_id = flash.read_id();
165 info!("FLASH ID back in SPI mode: {=[u8]:x}", flash_id);
166
109 info!("DONE"); 167 info!("DONE");
168
110 // Output pin PE3 169 // Output pin PE3
111 let mut led = Output::new(p.PE3, Level::Low, Speed::Low); 170 let mut led = Output::new(p.PE3, Level::Low, Speed::Low);
112 171
@@ -116,80 +175,268 @@ async fn main(_spawner: Spawner) {
116 } 175 }
117} 176}
118 177
119const MEMORY_PAGE_SIZE: usize = 8; 178const MEMORY_PAGE_SIZE: usize = 256;
120
121const CMD_READ: u8 = 0x0B;
122const _CMD_QUAD_READ: u8 = 0x6B;
123
124const CMD_WRITE_PG: u8 = 0x02;
125const _CMD_QUAD_WRITE_PG: u8 = 0x32;
126
127const CMD_READ_ID: u8 = 0x9F;
128const CMD_READ_ID_OCTO: u16 = 0x9F60;
129 179
130const CMD_ENABLE_RESET: u8 = 0x66; 180/// Implementation of access to flash chip using SPI.
131const CMD_RESET: u8 = 0x99; 181///
132 182/// Chip commands are hardcoded as it depends on used chip.
133const CMD_WRITE_ENABLE: u8 = 0x06; 183/// This targets a MX25UW25645GXDI00.
134 184pub struct SpiFlashMemory<I: Instance> {
135const CMD_CHIP_ERASE: u8 = 0xC7; 185 xspi: Xspi<'static, I, Blocking>,
136const CMD_SECTOR_ERASE: u8 = 0x20; 186}
137const CMD_BLOCK_ERASE_32K: u8 = 0x52;
138const CMD_BLOCK_ERASE_64K: u8 = 0xD8;
139
140const CMD_READ_SR: u8 = 0x05;
141const CMD_READ_CR: u8 = 0x35;
142
143const CMD_WRITE_SR: u8 = 0x01;
144const CMD_WRITE_CR: u8 = 0x31;
145 187
146/// Implementation of access to flash chip. 188/// Implementation of access to flash chip using Octo SPI.
147/// 189///
148/// Chip commands are hardcoded as it depends on used chip. 190/// Chip commands are hardcoded as it depends on used chip.
149/// This targets a MX25UW25645GXDI00. 191/// This targets a MX25UW25645GXDI00.
150pub struct FlashMemory<I: Instance> { 192pub struct OpiFlashMemory<I: Instance> {
151 xspi: Xspi<'static, I, Blocking>, 193 xspi: Xspi<'static, I, Blocking>,
152} 194}
153 195
154impl<I: Instance> FlashMemory<I> { 196/// SPI mode commands for MX25UW25645G flash memory
155 pub async fn new(xspi: Xspi<'static, I, Blocking>) -> Self { 197#[allow(dead_code)]
156 let mut memory = Self { xspi }; 198#[repr(u8)]
199enum SpiCommand {
200 // Array access commands
201 /// Read data bytes using 3-byte address (up to 50 MHz)
202 Read3B = 0x03,
203 /// Fast read data bytes using 3-byte address with 8 dummy cycles (up to 133 MHz)
204 FastRead3B = 0x0B,
205 /// Program 1-256 bytes of data using 3-byte address
206 PageProgram3B = 0x02,
207 /// Erase 4KB sector using 3-byte address
208 SectorErase3B = 0x20,
209 /// Erase 64KB block using 3-byte address
210 BlockErase3B = 0xD8,
211 /// Read data bytes using 4-byte address (up to 50 MHz)
212 Read4B = 0x13,
213 /// Fast read data bytes using 4-byte address with 8 dummy cycles (up to 133 MHz)
214 FastRead4B = 0x0C,
215 /// Program 1-256 bytes of data using 4-byte address
216 PageProgram4B = 0x12,
217 /// Erase 4KB sector using 4-byte address
218 SectorErase4B = 0x21,
219 /// Erase 64KB block using 4-byte address
220 BlockErase4B = 0xDC,
221 /// Erase entire chip (only if no blocks are protected)
222 ChipErase = 0x60,
223
224 // Write Buffer Access commands
225 /// Read data from the 256-byte page buffer
226 ReadBuffer = 0x25,
227 /// Initialize write-to-buffer sequence, clears buffer and writes initial data
228 WriteBufferInitial = 0x22,
229 /// Continue writing data to buffer (used between WRBI and WRCF)
230 WriteBufferContinue = 0x24,
231 /// Confirm write operation, programs buffer contents to flash array
232 WriteBufferConfirm = 0x31,
233
234 // Device operation commands
235 /// Set Write Enable Latch (WEL) bit, required before write/program/erase operations
236 WriteEnable = 0x06,
237 /// Clear Write Enable Latch (WEL) bit
238 WriteDisable = 0x04,
239 /// Select write protection mode (BP mode or Advanced Sector Protection)
240 WriteProtectSelection = 0x68,
241 /// Suspend ongoing program or erase operation to allow read access
242 ProgramEraseSuspend = 0xB0,
243 /// Resume suspended program or erase operation
244 ProgramEraseResume = 0x30,
245 /// Enter deep power-down mode for minimum power consumption
246 DeepPowerDown = 0xB9,
247 /// Exit deep power-down mode and return to standby
248 ReleaseFromDeepPowerDown = 0xAB,
249 /// No operation, can terminate Reset Enable command
250 NoOperation = 0x00,
251 /// Enable reset operation (must precede Reset Memory command)
252 ResetEnable = 0x66,
253 /// Reset device to power-on state (requires prior Reset Enable)
254 ResetMemory = 0x99,
255 /// Protect all sectors using Dynamic Protection Bits (DPB)
256 GangBlockLock = 0x7E,
257 /// Unprotect all sectors by clearing Dynamic Protection Bits (DPB)
258 GangBlockUnlock = 0x98,
259
260 // Register Access commands
261 /// Read 3-byte device identification (manufacturer ID + device ID)
262 ReadIdentification = 0x9F,
263 /// Read Serial Flash Discoverable Parameters (SFDP) table
264 ReadSFDP = 0x5A,
265 /// Read 8-bit Status Register (WIP, WEL, BP bits, etc.)
266 ReadStatusRegister = 0x05,
267 /// Read 8-bit Configuration Register (ODS, TB, PBE bits)
268 ReadConfigurationRegister = 0x15,
269 /// Write Status and/or Configuration Register (1-2 bytes)
270 WriteStatusConfigurationRegister = 0x01,
271 /// Read Configuration Register 2 from specified 4-byte address
272 ReadConfigurationRegister2 = 0x71,
273 /// Write Configuration Register 2 to specified 4-byte address
274 WriteConfigurationRegister2 = 0x72,
275 /// Read 8-bit Security Register (protection status, suspend bits)
276 ReadSecurityRegister = 0x2B,
277 /// Write Security Register to set customer lock-down bit
278 WriteSecurityRegister = 0x2F,
279 /// Read 32-bit Fast Boot Register (boot address and configuration)
280 ReadFastBootRegister = 0x16,
281 /// Write 32-bit Fast Boot Register
282 WriteFastBootRegister = 0x17,
283 /// Erase Fast Boot Register (disable fast boot feature)
284 EraseFastBootRegister = 0x18,
285 /// Set burst/wrap length for read operations (16/32/64 bytes)
286 SetBurstLength = 0xC0,
287 /// Enter 8K-bit secured OTP mode for programming unique identifiers
288 EnterSecuredOTP = 0xB1,
289 /// Exit secured OTP mode and return to main array access
290 ExitSecuredOTP = 0xC1,
291 /// Write Lock Register to control SPB protection mode
292 WriteLockRegister = 0x2C,
293 /// Read Lock Register status
294 ReadLockRegister = 0x2D,
295 /// Program Solid Protection Bit (SPB) for specified sector/block
296 WriteSPB = 0xE3,
297 /// Erase all Solid Protection Bits (SPB)
298 EraseSPB = 0xE4,
299 /// Read Solid Protection Bit (SPB) status for specified sector/block
300 ReadSPB = 0xE2,
301 /// Write Dynamic Protection Bit (DPB) for specified sector
302 WriteDPB = 0xE1,
303 /// Read Dynamic Protection Bit (DPB) status for specified sector
304 ReadDPB = 0xE0,
305 /// Read 64-bit password register (only in Solid Protection mode)
306 ReadPassword = 0x27,
307 /// Write 64-bit password register
308 WritePassword = 0x28,
309 /// Unlock SPB operations using 64-bit password
310 PasswordUnlock = 0x29,
311}
157 312
158 memory.reset_memory().await; 313/// OPI mode commands for MX25UW25645G flash memory
159 memory.enable_octo(); 314#[allow(dead_code)]
160 memory 315#[repr(u16)]
161 } 316enum OpiCommand {
317 // Array access commands
318 /// Read data using 8 I/O lines in STR mode with configurable dummy cycles (up to 200 MHz)
319 OctaRead = 0xEC13,
320 /// Read data using 8 I/O lines in DTR mode with configurable dummy cycles (up to 200 MHz)
321 OctaDTRRead = 0xEE11,
322 /// Program 1-256 bytes using 4-byte address and 8 I/O lines
323 PageProgram4B = 0x12ED,
324 /// Erase 4KB sector using 4-byte address
325 SectorErase4B = 0x21DE,
326 /// Erase 64KB block using 4-byte address
327 BlockErase4B = 0xDC23,
328 /// Erase entire chip (only if no blocks are protected)
329 ChipErase = 0x609F,
330
331 // Write Buffer Access commands
332 /// Read data from the 256-byte page buffer using 4-byte address
333 ReadBuffer = 0x25DA,
334 /// Initialize interruptible write-to-buffer sequence with 4-byte address
335 WriteBufferInitial = 0x22DD,
336 /// Continue writing data to buffer during interruptible sequence
337 WriteBufferContinue = 0x24DB,
338 /// Confirm and execute write operation from buffer to flash array
339 WriteBufferConfirm = 0x31CE,
340
341 // Device operation commands
342 /// Set Write Enable Latch (WEL) bit, required before write/program/erase operations
343 WriteEnable = 0x06F9,
344 /// Clear Write Enable Latch (WEL) bit, aborts write-to-buffer sequence
345 WriteDisable = 0x04FB,
346 /// Select write protection mode (BP mode or Advanced Sector Protection) - OTP bit
347 WriteProtectSelection = 0x6897,
348 /// Suspend ongoing program or erase operation to allow read from other banks
349 ProgramEraseSuspend = 0xB04F,
350 /// Resume suspended program or erase operation
351 ProgramEraseResume = 0x30CF,
352 /// Enter deep power-down mode for minimum power consumption
353 DeepPowerDown = 0xB946,
354 /// Exit deep power-down mode and return to standby
355 ReleaseFromDeepPowerDown = 0xAB54,
356 /// No operation, can terminate Reset Enable command
357 NoOperation = 0x00FF,
358 /// Enable reset operation (must precede Reset Memory command)
359 ResetEnable = 0x6699,
360 /// Reset device to power-on state, clears volatile settings
361 ResetMemory = 0x9966,
362 /// Protect all sectors using Dynamic Protection Bits (DPB)
363 GangBlockLock = 0x7E81,
364 /// Unprotect all sectors by clearing Dynamic Protection Bits (DPB)
365 GangBlockUnlock = 0x9867,
366
367 // Register Access commands
368 /// Read 3-byte device identification with 4-byte dummy address
369 ReadIdentification = 0x9F60,
370 /// Read Serial Flash Discoverable Parameters (SFDP) table with 4-byte address
371 ReadSFDP = 0x5AA5,
372 /// Read 8-bit Status Register with 4-byte dummy address
373 ReadStatusRegister = 0x05FA,
374 /// Read 8-bit Configuration Register with specific address (00000001h)
375 ReadConfigurationRegister = 0x15EA,
376 /// Write 8-bit Status Register with specific address (00000000h) or Configuration Register with address (00000001h)
377 WriteStatusConfigurationRegister = 0x01FE,
378 /// Read Configuration Register 2 from specified 4-byte address
379 ReadConfigurationRegister2 = 0x718E,
380 /// Write Configuration Register 2 to specified 4-byte address
381 WriteConfigurationRegister2 = 0x728D,
382 /// Read 8-bit Security Register with 4-byte dummy address
383 ReadSecurityRegister = 0x2BD4,
384 /// Write Security Register to set customer lock-down bit
385 WriteSecurityRegister = 0x2FD0,
386 /// Set burst/wrap length for read operations with 4-byte dummy address
387 SetBurstLength = 0xC03F,
388 /// Read 32-bit Fast Boot Register with 4-byte dummy address
389 ReadFastBootRegister = 0x16E9,
390 /// Write 32-bit Fast Boot Register with 4-byte dummy address
391 WriteFastBootRegister = 0x17E8,
392 /// Erase Fast Boot Register (disable fast boot feature)
393 EraseFastBootRegister = 0x18E7,
394 /// Enter 8K-bit secured OTP mode for programming unique identifiers
395 EnterSecuredOTP = 0xB14E,
396 /// Exit secured OTP mode and return to main array access
397 ExitSecuredOTP = 0xC13E,
398 /// Write Lock Register to control SPB protection mode with 4-byte dummy address
399 WriteLockRegister = 0x2CD3,
400 /// Read Lock Register status with 4-byte dummy address
401 ReadLockRegister = 0x2DD2,
402 /// Program Solid Protection Bit (SPB) for specified 4-byte address
403 WriteSPB = 0xE31C,
404 /// Erase all Solid Protection Bits (SPB)
405 EraseSPB = 0xE41B,
406 /// Read Solid Protection Bit (SPB) status for specified 4-byte address
407 ReadSPB = 0xE21D,
408 /// Write Dynamic Protection Bit (DPB) for specified 4-byte address
409 WriteDPB = 0xE11E,
410 /// Read Dynamic Protection Bit (DPB) status for specified 4-byte address
411 ReadDPB = 0xE01F,
412 /// Read 64-bit password register with 4-byte dummy address and 20 dummy cycles
413 ReadPassword = 0x27D8,
414 /// Write 64-bit password register with 4-byte dummy address
415 WritePassword = 0x28D7,
416 /// Unlock SPB operations using 64-bit password with 4-byte dummy address
417 PasswordUnlock = 0x29D6,
418}
162 419
163 async fn qpi_mode(&mut self) { 420impl<I: Instance> SpiFlashMemory<I> {
164 // Enter qpi mode 421 pub fn new(xspi: Xspi<'static, I, Blocking>) -> Self {
165 self.exec_command(0x38).await; 422 let mut memory = Self { xspi };
166 423
167 // Set read param 424 memory.reset_memory();
168 let transaction = TransferConfig { 425 memory
169 iwidth: XspiWidth::QUAD,
170 dwidth: XspiWidth::QUAD,
171 instruction: Some(0xC0),
172 ..Default::default()
173 };
174 self.enable_write().await;
175 self.xspi.blocking_write(&[0x30_u8], transaction).unwrap();
176 self.wait_write_finish();
177 } 426 }
178 427
179 pub async fn disable_mm(&mut self) { 428 pub fn disable_mm(&mut self) {
180 self.xspi.disable_memory_mapped_mode(); 429 self.xspi.disable_memory_mapped_mode();
181 } 430 }
182 431
183 pub async fn enable_mm(&mut self) { 432 pub fn enable_mm(&mut self) {
184 self.qpi_mode().await;
185
186 let read_config = TransferConfig { 433 let read_config = TransferConfig {
187 iwidth: XspiWidth::SING, 434 iwidth: XspiWidth::SING,
188 isize: AddressSize::_8bit, 435 isize: AddressSize::_8bit,
189 adwidth: XspiWidth::SING, 436 adwidth: XspiWidth::SING,
190 adsize: AddressSize::_24bit, 437 adsize: AddressSize::_32bit,
191 dwidth: XspiWidth::SING, 438 dwidth: XspiWidth::SING,
192 instruction: Some(CMD_READ as u32), 439 instruction: Some(SpiCommand::FastRead4B as u32),
193 dummy: DummyCycles::_8, 440 dummy: DummyCycles::_8,
194 ..Default::default() 441 ..Default::default()
195 }; 442 };
@@ -198,42 +445,28 @@ impl<I: Instance> FlashMemory<I> {
198 iwidth: XspiWidth::SING, 445 iwidth: XspiWidth::SING,
199 isize: AddressSize::_8bit, 446 isize: AddressSize::_8bit,
200 adwidth: XspiWidth::SING, 447 adwidth: XspiWidth::SING,
201 adsize: AddressSize::_24bit, 448 adsize: AddressSize::_32bit,
202 dwidth: XspiWidth::SING, 449 dwidth: XspiWidth::SING,
203 instruction: Some(CMD_WRITE_PG as u32), 450 instruction: Some(SpiCommand::PageProgram4B as u32),
204 dummy: DummyCycles::_0, 451 dummy: DummyCycles::_0,
205 ..Default::default() 452 ..Default::default()
206 }; 453 };
207 self.xspi.enable_memory_mapped_mode(read_config, write_config).unwrap(); 454 self.xspi.enable_memory_mapped_mode(read_config, write_config).unwrap();
208 } 455 }
209 456
210 fn enable_octo(&mut self) { 457 fn into_octo(mut self) -> OpiFlashMemory<I> {
211 let cr = self.read_cr(); 458 self.enable_opi_mode();
212 // info!("Read cr: {:x}", cr); 459 OpiFlashMemory { xspi: self.xspi }
213 self.write_cr(cr | 0x02);
214 // info!("Read cr after writing: {:x}", cr);
215 } 460 }
216 461
217 pub fn disable_octo(&mut self) { 462 fn enable_opi_mode(&mut self) {
218 let cr = self.read_cr(); 463 let cr2_0 = self.read_cr2(0);
219 self.write_cr(cr & (!(0x02))); 464 info!("Read CR2 at 0x0: {:x}", cr2_0);
220 } 465 self.enable_write();
221 466 self.write_cr2(0, cr2_0 | 0x01); // Set bit 0 to enable octo SPI in STR
222 async fn exec_command_4(&mut self, cmd: u8) {
223 let transaction = TransferConfig {
224 iwidth: XspiWidth::QUAD,
225 adwidth: XspiWidth::NONE,
226 // adsize: AddressSize::_24bit,
227 dwidth: XspiWidth::NONE,
228 instruction: Some(cmd as u32),
229 address: None,
230 dummy: DummyCycles::_0,
231 ..Default::default()
232 };
233 self.xspi.blocking_command(&transaction).unwrap();
234 } 467 }
235 468
236 async fn exec_command(&mut self, cmd: u8) { 469 fn exec_command(&mut self, cmd: u8) {
237 let transaction = TransferConfig { 470 let transaction = TransferConfig {
238 iwidth: XspiWidth::SING, 471 iwidth: XspiWidth::SING,
239 adwidth: XspiWidth::NONE, 472 adwidth: XspiWidth::NONE,
@@ -248,16 +481,14 @@ impl<I: Instance> FlashMemory<I> {
248 self.xspi.blocking_command(&transaction).unwrap(); 481 self.xspi.blocking_command(&transaction).unwrap();
249 } 482 }
250 483
251 pub async fn reset_memory(&mut self) { 484 pub fn reset_memory(&mut self) {
252 self.exec_command_4(CMD_ENABLE_RESET).await; 485 self.exec_command(SpiCommand::ResetEnable as u8);
253 self.exec_command_4(CMD_RESET).await; 486 self.exec_command(SpiCommand::ResetMemory as u8);
254 self.exec_command(CMD_ENABLE_RESET).await;
255 self.exec_command(CMD_RESET).await;
256 self.wait_write_finish(); 487 self.wait_write_finish();
257 } 488 }
258 489
259 pub async fn enable_write(&mut self) { 490 pub fn enable_write(&mut self) {
260 self.exec_command(CMD_WRITE_ENABLE).await; 491 self.exec_command(SpiCommand::WriteEnable as u8);
261 } 492 }
262 493
263 pub fn read_id(&mut self) -> [u8; 3] { 494 pub fn read_id(&mut self) -> [u8; 3] {
@@ -266,92 +497,64 @@ impl<I: Instance> FlashMemory<I> {
266 iwidth: XspiWidth::SING, 497 iwidth: XspiWidth::SING,
267 isize: AddressSize::_8bit, 498 isize: AddressSize::_8bit,
268 adwidth: XspiWidth::NONE, 499 adwidth: XspiWidth::NONE,
269 // adsize: AddressSize::_24bit,
270 dwidth: XspiWidth::SING, 500 dwidth: XspiWidth::SING,
271 instruction: Some(CMD_READ_ID as u32), 501 instruction: Some(SpiCommand::ReadIdentification as u32),
272 ..Default::default() 502 ..Default::default()
273 }; 503 };
274 // info!("Reading id: 0x{:X}", transaction.instruction);
275 self.xspi.blocking_read(&mut buffer, transaction).unwrap(); 504 self.xspi.blocking_read(&mut buffer, transaction).unwrap();
276 buffer 505 buffer
277 } 506 }
278 507
279 pub fn read_id_8(&mut self) -> [u8; 3] { 508 pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8]) {
280 let mut buffer = [0; 3];
281 let transaction: TransferConfig = TransferConfig {
282 iwidth: XspiWidth::OCTO,
283 isize: AddressSize::_16bit,
284 adwidth: XspiWidth::OCTO,
285 address: Some(0),
286 adsize: AddressSize::_32bit,
287 dwidth: XspiWidth::OCTO,
288 instruction: Some(CMD_READ_ID_OCTO as u32),
289 dummy: DummyCycles::_4,
290 ..Default::default()
291 };
292 info!("Reading id: {:#X}", transaction.instruction);
293 self.xspi.blocking_read(&mut buffer, transaction).unwrap();
294 buffer
295 }
296
297 pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) {
298 let transaction = TransferConfig { 509 let transaction = TransferConfig {
299 iwidth: XspiWidth::SING, 510 iwidth: XspiWidth::SING,
300 adwidth: XspiWidth::SING, 511 adwidth: XspiWidth::SING,
301 adsize: AddressSize::_24bit, 512 adsize: AddressSize::_32bit,
302 dwidth: XspiWidth::SING, 513 dwidth: XspiWidth::SING,
303 instruction: Some(CMD_READ as u32), 514 instruction: Some(SpiCommand::FastRead4B as u32),
304 dummy: DummyCycles::_8, 515 dummy: DummyCycles::_8,
305 // dwidth: XspiWidth::QUAD,
306 // instruction: Some(CMD_QUAD_READ as u32),
307 // dummy: DummyCycles::_8,
308 address: Some(addr), 516 address: Some(addr),
309 ..Default::default() 517 ..Default::default()
310 }; 518 };
311 if use_dma { 519
312 self.xspi.blocking_read(buffer, transaction).unwrap(); 520 self.xspi.blocking_read(buffer, transaction).unwrap();
313 } else {
314 self.xspi.blocking_read(buffer, transaction).unwrap();
315 }
316 } 521 }
317 522
318 fn wait_write_finish(&mut self) { 523 fn wait_write_finish(&mut self) {
319 while (self.read_sr() & 0x01) != 0 {} 524 while (self.read_sr() & 0x01) != 0 {}
320 } 525 }
321 526
322 async fn perform_erase(&mut self, addr: u32, cmd: u8) { 527 fn perform_erase(&mut self, addr: u32, cmd: u8) {
323 let transaction = TransferConfig { 528 let transaction = TransferConfig {
324 iwidth: XspiWidth::SING, 529 iwidth: XspiWidth::SING,
325 adwidth: XspiWidth::SING, 530 adwidth: XspiWidth::SING,
326 adsize: AddressSize::_24bit, 531 adsize: AddressSize::_32bit,
327 dwidth: XspiWidth::NONE, 532 dwidth: XspiWidth::NONE,
328 instruction: Some(cmd as u32), 533 instruction: Some(cmd as u32),
329 address: Some(addr), 534 address: Some(addr),
330 dummy: DummyCycles::_0, 535 dummy: DummyCycles::_0,
331 ..Default::default() 536 ..Default::default()
332 }; 537 };
333 self.enable_write().await; 538 self.enable_write();
334 self.xspi.blocking_command(&transaction).unwrap(); 539 self.xspi.blocking_command(&transaction).unwrap();
335 self.wait_write_finish(); 540 self.wait_write_finish();
336 } 541 }
337 542
338 pub async fn erase_sector(&mut self, addr: u32) { 543 pub fn erase_sector(&mut self, addr: u32) {
339 self.perform_erase(addr, CMD_SECTOR_ERASE).await; 544 self.perform_erase(addr, SpiCommand::SectorErase4B as u8);
340 } 545 }
341 546
342 pub async fn erase_block_32k(&mut self, addr: u32) { 547 pub fn erase_block_64k(&mut self, addr: u32) {
343 self.perform_erase(addr, CMD_BLOCK_ERASE_32K).await; 548 self.perform_erase(addr, SpiCommand::BlockErase4B as u8);
344 } 549 }
345 550
346 pub async fn erase_block_64k(&mut self, addr: u32) { 551 pub fn erase_chip(&mut self) {
347 self.perform_erase(addr, CMD_BLOCK_ERASE_64K).await; 552 self.enable_write();
348 } 553 self.exec_command(SpiCommand::ChipErase as u8);
349 554 self.wait_write_finish();
350 pub async fn erase_chip(&mut self) {
351 self.exec_command(CMD_CHIP_ERASE).await;
352 } 555 }
353 556
354 async fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) { 557 fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize) {
355 assert!( 558 assert!(
356 (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32, 559 (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32,
357 "write_page(): page write length exceeds page boundary (len = {}, addr = {:X}", 560 "write_page(): page write length exceeds page boundary (len = {}, addr = {:X}",
@@ -361,48 +564,43 @@ impl<I: Instance> FlashMemory<I> {
361 564
362 let transaction = TransferConfig { 565 let transaction = TransferConfig {
363 iwidth: XspiWidth::SING, 566 iwidth: XspiWidth::SING,
364 adsize: AddressSize::_24bit, 567 adsize: AddressSize::_32bit,
365 adwidth: XspiWidth::SING, 568 adwidth: XspiWidth::SING,
366 dwidth: XspiWidth::SING, 569 dwidth: XspiWidth::SING,
367 instruction: Some(CMD_WRITE_PG as u32), 570 instruction: Some(SpiCommand::PageProgram4B as u32),
368 // dwidth: XspiWidth::QUAD,
369 // instruction: Some(CMD_QUAD_WRITE_PG as u32),
370 address: Some(addr), 571 address: Some(addr),
371 dummy: DummyCycles::_0, 572 dummy: DummyCycles::_0,
372 ..Default::default() 573 ..Default::default()
373 }; 574 };
374 self.enable_write().await; 575 self.enable_write();
375 if use_dma { 576 self.xspi.blocking_write(buffer, transaction).unwrap();
376 self.xspi.blocking_write(buffer, transaction).unwrap();
377 } else {
378 self.xspi.blocking_write(buffer, transaction).unwrap();
379 }
380 self.wait_write_finish(); 577 self.wait_write_finish();
381 } 578 }
382 579
383 pub async fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) { 580 pub fn write_memory(&mut self, addr: u32, buffer: &[u8]) {
384 let mut left = buffer.len(); 581 let mut left = buffer.len();
385 let mut place = addr; 582 let mut place = addr;
386 let mut chunk_start = 0; 583 let mut chunk_start = 0;
387 584
388 while left > 0 { 585 while left > 0 {
389 let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize; 586 let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize;
390 let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left }; 587 let chunk_size = min(max_chunk_size, left);
391 let chunk = &buffer[chunk_start..(chunk_start + chunk_size)]; 588 let chunk = &buffer[chunk_start..(chunk_start + chunk_size)];
392 self.write_page(place, chunk, chunk_size, use_dma).await; 589 self.write_page(place, chunk, chunk_size);
393 place += chunk_size as u32; 590 place += chunk_size as u32;
394 left -= chunk_size; 591 left -= chunk_size;
395 chunk_start += chunk_size; 592 chunk_start += chunk_size;
396 } 593 }
397 } 594 }
398 595
596 // Note: read_register cannot be used to read the configuration register 2 since there is an
597 // address required for that read.
399 fn read_register(&mut self, cmd: u8) -> u8 { 598 fn read_register(&mut self, cmd: u8) -> u8 {
400 let mut buffer = [0; 1]; 599 let mut buffer = [0; 1];
401 let transaction: TransferConfig = TransferConfig { 600 let transaction: TransferConfig = TransferConfig {
402 iwidth: XspiWidth::SING, 601 iwidth: XspiWidth::SING,
403 isize: AddressSize::_8bit, 602 isize: AddressSize::_8bit,
404 adwidth: XspiWidth::NONE, 603 adwidth: XspiWidth::NONE,
405 adsize: AddressSize::_24bit,
406 dwidth: XspiWidth::SING, 604 dwidth: XspiWidth::SING,
407 instruction: Some(cmd as u32), 605 instruction: Some(cmd as u32),
408 address: None, 606 address: None,
@@ -410,39 +608,345 @@ impl<I: Instance> FlashMemory<I> {
410 ..Default::default() 608 ..Default::default()
411 }; 609 };
412 self.xspi.blocking_read(&mut buffer, transaction).unwrap(); 610 self.xspi.blocking_read(&mut buffer, transaction).unwrap();
413 // info!("Read w25q64 register: 0x{:x}", buffer[0]);
414 buffer[0] 611 buffer[0]
415 } 612 }
416 613
417 fn write_register(&mut self, cmd: u8, value: u8) { 614 pub fn read_sr(&mut self) -> u8 {
418 let buffer = [value; 1]; 615 self.read_register(SpiCommand::ReadStatusRegister as u8)
616 }
617
618 pub fn read_cr(&mut self) -> u8 {
619 self.read_register(SpiCommand::ReadConfigurationRegister as u8)
620 }
621
622 pub fn write_sr_cr(&mut self, sr: u8, cr: u8) {
623 let buffer = [sr, cr];
419 let transaction: TransferConfig = TransferConfig { 624 let transaction: TransferConfig = TransferConfig {
420 iwidth: XspiWidth::SING, 625 iwidth: XspiWidth::SING,
421 isize: AddressSize::_8bit, 626 isize: AddressSize::_8bit,
422 instruction: Some(cmd as u32), 627 instruction: Some(SpiCommand::WriteStatusConfigurationRegister as u32),
423 adsize: AddressSize::_24bit,
424 adwidth: XspiWidth::NONE, 628 adwidth: XspiWidth::NONE,
425 dwidth: XspiWidth::SING, 629 dwidth: XspiWidth::SING,
426 address: None, 630 address: None,
427 dummy: DummyCycles::_0, 631 dummy: DummyCycles::_0,
428 ..Default::default() 632 ..Default::default()
429 }; 633 };
634 self.enable_write();
635 self.xspi.blocking_write(&buffer, transaction).unwrap();
636 self.wait_write_finish();
637 }
638
639 pub fn read_cr2(&mut self, address: u32) -> u8 {
640 let mut buffer = [0; 1];
641 let transaction: TransferConfig = TransferConfig {
642 iwidth: XspiWidth::SING,
643 isize: AddressSize::_8bit,
644 instruction: Some(SpiCommand::ReadConfigurationRegister2 as u32),
645 adsize: AddressSize::_32bit,
646 adwidth: XspiWidth::SING,
647 dwidth: XspiWidth::SING,
648 address: Some(address),
649 dummy: DummyCycles::_0,
650 ..Default::default()
651 };
652 self.xspi.blocking_read(&mut buffer, transaction).unwrap();
653 buffer[0]
654 }
655
656 pub fn write_cr2(&mut self, address: u32, value: u8) {
657 let buffer = [value; 1];
658 let transaction: TransferConfig = TransferConfig {
659 iwidth: XspiWidth::SING,
660 isize: AddressSize::_8bit,
661 instruction: Some(SpiCommand::WriteConfigurationRegister2 as u32),
662 adsize: AddressSize::_32bit,
663 adwidth: XspiWidth::SING,
664 dwidth: XspiWidth::SING,
665 address: Some(address),
666 dummy: DummyCycles::_0,
667 ..Default::default()
668 };
430 self.xspi.blocking_write(&buffer, transaction).unwrap(); 669 self.xspi.blocking_write(&buffer, transaction).unwrap();
670 self.wait_write_finish();
671 }
672}
673
674impl<I: Instance> OpiFlashMemory<I> {
675 pub fn into_spi(mut self) -> SpiFlashMemory<I> {
676 self.disable_opi_mode();
677 SpiFlashMemory { xspi: self.xspi }
678 }
679
680 /// Disable OPI mode and return to SPI
681 pub fn disable_opi_mode(&mut self) {
682 // Clear SOPI and DOPI bits in CR2 volatile register
683 let cr2_0 = self.read_cr2(0x00000000);
684 self.write_cr2(0x00000000, cr2_0 & 0xFC); // Clear bits 0 and 1
685 }
686
687 /// Enable memory-mapped mode for OPI
688 pub fn enable_mm(&mut self) {
689 let read_config = TransferConfig {
690 iwidth: XspiWidth::OCTO,
691 isize: AddressSize::_16bit, // 2-byte command for OPI
692 adwidth: XspiWidth::OCTO,
693 adsize: AddressSize::_32bit,
694 dwidth: XspiWidth::OCTO,
695 instruction: Some(OpiCommand::OctaRead as u32),
696 dummy: DummyCycles::_20, // Default dummy cycles for OPI
697 ..Default::default()
698 };
699
700 let write_config = TransferConfig {
701 iwidth: XspiWidth::OCTO,
702 isize: AddressSize::_16bit,
703 adwidth: XspiWidth::OCTO,
704 adsize: AddressSize::_32bit,
705 dwidth: XspiWidth::OCTO,
706 instruction: Some(OpiCommand::PageProgram4B as u32),
707 dummy: DummyCycles::_0,
708 ..Default::default()
709 };
710
711 self.xspi.enable_memory_mapped_mode(read_config, write_config).unwrap();
712 }
713
714 pub fn disable_mm(&mut self) {
715 self.xspi.disable_memory_mapped_mode();
716 }
717
718 /// Execute OPI command (2-byte command)
719 fn exec_command(&mut self, cmd: OpiCommand) {
720 let transaction = TransferConfig {
721 iwidth: XspiWidth::OCTO,
722 isize: AddressSize::_16bit, // 2-byte command
723 adwidth: XspiWidth::NONE,
724 dwidth: XspiWidth::NONE,
725 instruction: Some(cmd as u32),
726 address: None,
727 dummy: DummyCycles::_0,
728 ..Default::default()
729 };
730 self.xspi.blocking_command(&transaction).unwrap();
731 }
732
733 /// Reset memory using OPI commands
734 pub fn reset_memory(&mut self) {
735 self.exec_command(OpiCommand::ResetEnable);
736 self.exec_command(OpiCommand::ResetMemory);
737 self.wait_write_finish();
431 } 738 }
432 739
740 /// Enable write using OPI command
741 pub fn enable_write(&mut self) {
742 self.exec_command(OpiCommand::WriteEnable);
743 }
744
745 /// Read device ID in OPI mode
746 pub fn read_id(&mut self) -> [u8; 3] {
747 let mut buffer = [0; 3];
748 let transaction = TransferConfig {
749 iwidth: XspiWidth::OCTO,
750 isize: AddressSize::_16bit,
751 adwidth: XspiWidth::OCTO,
752 adsize: AddressSize::_32bit,
753 dwidth: XspiWidth::OCTO,
754 instruction: Some(OpiCommand::ReadIdentification as u32),
755 address: Some(0x00000000), // Dummy address required
756 dummy: DummyCycles::_4,
757 ..Default::default()
758 };
759 self.xspi.blocking_read(&mut buffer, transaction).unwrap();
760 buffer
761 }
762
763 /// Read memory using OPI mode
764 pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8]) {
765 let transaction = TransferConfig {
766 iwidth: XspiWidth::OCTO,
767 isize: AddressSize::_16bit,
768 adwidth: XspiWidth::OCTO,
769 adsize: AddressSize::_32bit,
770 dwidth: XspiWidth::OCTO,
771 instruction: Some(OpiCommand::OctaRead as u32),
772 address: Some(addr),
773 dummy: DummyCycles::_20, // Default for 200MHz operation
774 ..Default::default()
775 };
776 self.xspi.blocking_read(buffer, transaction).unwrap();
777 }
778
779 /// Wait for write completion using OPI status read
780 fn wait_write_finish(&mut self) {
781 while (self.read_sr() & 0x01) != 0 {}
782 }
783
784 /// Perform erase operation using OPI command
785 fn perform_erase(&mut self, addr: u32, cmd: OpiCommand) {
786 let transaction = TransferConfig {
787 iwidth: XspiWidth::OCTO,
788 isize: AddressSize::_16bit,
789 adwidth: XspiWidth::OCTO,
790 adsize: AddressSize::_32bit,
791 dwidth: XspiWidth::NONE,
792 instruction: Some(cmd as u32),
793 address: Some(addr),
794 dummy: DummyCycles::_0,
795 ..Default::default()
796 };
797 self.enable_write();
798 self.xspi.blocking_command(&transaction).unwrap();
799 self.wait_write_finish();
800 }
801
802 /// Erase 4KB sector using OPI
803 pub fn erase_sector(&mut self, addr: u32) {
804 self.perform_erase(addr, OpiCommand::SectorErase4B);
805 }
806
807 /// Erase 64KB block using OPI
808 pub fn erase_block_64k(&mut self, addr: u32) {
809 self.perform_erase(addr, OpiCommand::BlockErase4B);
810 }
811
812 /// Erase entire chip using OPI
813 pub fn erase_chip(&mut self) {
814 self.enable_write();
815 self.exec_command(OpiCommand::ChipErase);
816 self.wait_write_finish();
817 }
818
819 /// Write single page using OPI
820 fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize) {
821 assert!(
822 (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32,
823 "write_page(): page write length exceeds page boundary (len = {}, addr = {:X})",
824 len,
825 addr
826 );
827
828 let transaction = TransferConfig {
829 iwidth: XspiWidth::OCTO,
830 isize: AddressSize::_16bit,
831 adwidth: XspiWidth::OCTO,
832 adsize: AddressSize::_32bit,
833 dwidth: XspiWidth::OCTO,
834 instruction: Some(OpiCommand::PageProgram4B as u32),
835 address: Some(addr),
836 dummy: DummyCycles::_0,
837 ..Default::default()
838 };
839 self.enable_write();
840 self.xspi.blocking_write(buffer, transaction).unwrap();
841 self.wait_write_finish();
842 }
843
844 /// Write memory using OPI (handles page boundaries)
845 pub fn write_memory(&mut self, addr: u32, buffer: &[u8]) {
846 let mut left = buffer.len();
847 let mut place = addr;
848 let mut chunk_start = 0;
849
850 while left > 0 {
851 let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize;
852 let chunk_size = min(max_chunk_size, left);
853 let chunk = &buffer[chunk_start..(chunk_start + chunk_size)];
854 self.write_page(place, chunk, chunk_size);
855 place += chunk_size as u32;
856 left -= chunk_size;
857 chunk_start += chunk_size;
858 }
859 }
860
861 /// Read register using OPI mode
862 fn read_register(&mut self, cmd: OpiCommand, dummy_addr: u32, dummy_cycles: DummyCycles) -> u8 {
863 let mut buffer = [0; 1];
864 let transaction = TransferConfig {
865 iwidth: XspiWidth::OCTO,
866 isize: AddressSize::_16bit,
867 adwidth: XspiWidth::OCTO,
868 adsize: AddressSize::_32bit,
869 dwidth: XspiWidth::OCTO,
870 instruction: Some(cmd as u32),
871 address: Some(dummy_addr),
872 dummy: dummy_cycles,
873 ..Default::default()
874 };
875 self.xspi.blocking_read(&mut buffer, transaction).unwrap();
876 buffer[0]
877 }
878
879 /// Read Status Register using OPI
433 pub fn read_sr(&mut self) -> u8 { 880 pub fn read_sr(&mut self) -> u8 {
434 self.read_register(CMD_READ_SR) 881 self.read_register(
882 OpiCommand::ReadStatusRegister,
883 0x00000000, // Dummy address
884 DummyCycles::_4,
885 )
435 } 886 }
436 887
888 /// Read Configuration Register using OPI
437 pub fn read_cr(&mut self) -> u8 { 889 pub fn read_cr(&mut self) -> u8 {
438 self.read_register(CMD_READ_CR) 890 self.read_register(
891 OpiCommand::ReadConfigurationRegister,
892 0x00000001, // Address for CR
893 DummyCycles::_4,
894 )
439 } 895 }
440 896
441 pub fn write_sr(&mut self, value: u8) { 897 /// Write Status/Configuration Register using OPI
442 self.write_register(CMD_WRITE_SR, value); 898 pub fn write_sr_cr(&mut self, sr: u8, cr: u8) {
899 let transaction = TransferConfig {
900 iwidth: XspiWidth::OCTO,
901 isize: AddressSize::_16bit,
902 adwidth: XspiWidth::OCTO,
903 adsize: AddressSize::_32bit,
904 dwidth: XspiWidth::OCTO,
905 instruction: Some(OpiCommand::WriteStatusConfigurationRegister as u32),
906 address: Some(0x00000000),
907 dummy: DummyCycles::_0,
908 ..Default::default()
909 };
910
911 self.enable_write();
912 self.xspi.blocking_write(&[sr, cr], transaction).unwrap();
913 self.wait_write_finish();
914 }
915
916 /// Read Configuration Register 2 using OPI
917 pub fn read_cr2(&mut self, address: u32) -> u8 {
918 let mut buffer = [0; 1];
919 let transaction = TransferConfig {
920 iwidth: XspiWidth::OCTO,
921 isize: AddressSize::_16bit,
922 adwidth: XspiWidth::OCTO,
923 adsize: AddressSize::_32bit,
924 dwidth: XspiWidth::OCTO,
925 instruction: Some(OpiCommand::ReadConfigurationRegister2 as u32),
926 address: Some(address),
927 dummy: DummyCycles::_4,
928 ..Default::default()
929 };
930 self.xspi.blocking_read(&mut buffer, transaction).unwrap();
931 buffer[0]
443 } 932 }
444 933
445 pub fn write_cr(&mut self, value: u8) { 934 /// Write Configuration Register 2 using OPI
446 self.write_register(CMD_WRITE_CR, value); 935 pub fn write_cr2(&mut self, address: u32, value: u8) {
936 let transaction = TransferConfig {
937 iwidth: XspiWidth::OCTO,
938 isize: AddressSize::_16bit,
939 adwidth: XspiWidth::OCTO,
940 adsize: AddressSize::_32bit,
941 dwidth: XspiWidth::OCTO,
942 instruction: Some(OpiCommand::WriteConfigurationRegister2 as u32),
943 address: Some(address),
944 dummy: DummyCycles::_0,
945 ..Default::default()
946 };
947
948 self.enable_write();
949 self.xspi.blocking_write(&[value], transaction).unwrap();
950 self.wait_write_finish();
447 } 951 }
448} 952}
diff --git a/examples/stm32l0/src/bin/dds.rs b/examples/stm32l0/src/bin/dds.rs
index a54b28a93..eaa7a61a8 100644
--- a/examples/stm32l0/src/bin/dds.rs
+++ b/examples/stm32l0/src/bin/dds.rs
@@ -11,7 +11,7 @@ use embassy_stm32::rcc::*;
11use embassy_stm32::time::hz; 11use embassy_stm32::time::hz;
12use embassy_stm32::timer::low_level::{Timer as LLTimer, *}; 12use embassy_stm32::timer::low_level::{Timer as LLTimer, *};
13use embassy_stm32::timer::simple_pwm::PwmPin; 13use embassy_stm32::timer::simple_pwm::PwmPin;
14use embassy_stm32::timer::Channel; 14use embassy_stm32::timer::{Ch3, Channel};
15use embassy_stm32::{interrupt, pac, Config}; 15use embassy_stm32::{interrupt, pac, Config};
16use panic_probe as _; 16use panic_probe as _;
17 17
@@ -70,7 +70,7 @@ async fn main(_spawner: Spawner) {
70 let p = embassy_stm32::init(config); 70 let p = embassy_stm32::init(config);
71 71
72 // setup PWM pin in AF mode 72 // setup PWM pin in AF mode
73 let _ch3 = PwmPin::new_ch3(p.PA2, OutputType::PushPull); 73 let _ch3 = PwmPin::<_, Ch3>::new(p.PA2, OutputType::PushPull);
74 74
75 // initialize timer 75 // initialize timer
76 // we cannot use SimplePWM here because the Time is privately encapsulated 76 // we cannot use SimplePWM here because the Time is privately encapsulated
diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml
index a0a7916a7..5d5d401f6 100644
--- a/examples/stm32l1/Cargo.toml
+++ b/examples/stm32l1/Cargo.toml
@@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["de
9embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 9embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
10embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } 11embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] }
12embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } 12embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
14 14
15defmt = "1.0.1" 15defmt = "1.0.1"
diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml
index 7da09e5b0..a6b4efcf8 100644
--- a/examples/stm32l4/Cargo.toml
+++ b/examples/stm32l4/Cargo.toml
@@ -10,8 +10,8 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [
10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] }
13embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } 13embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" }
14embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-net-adin1110 = { version = "0.3.0", path = "../../embassy-net-adin1110" } 15embassy-net-adin1110 = { version = "0.3.0", path = "../../embassy-net-adin1110" }
16embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "udp", "tcp", "dhcpv4", "medium-ethernet"] } 16embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "udp", "tcp", "dhcpv4", "medium-ethernet"] }
17embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 17embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs
index 354ac90b2..dc90a3b85 100644
--- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs
+++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs
@@ -59,7 +59,7 @@ pub type SpeSpiCs = ExclusiveDevice<SpeSpi, Output<'static>, Delay>;
59pub type SpeInt = exti::ExtiInput<'static>; 59pub type SpeInt = exti::ExtiInput<'static>;
60pub type SpeRst = Output<'static>; 60pub type SpeRst = Output<'static>;
61pub type Adin1110T = ADIN1110<SpeSpiCs>; 61pub type Adin1110T = ADIN1110<SpeSpiCs>;
62pub type TempSensI2c = I2c<'static, Async>; 62pub type TempSensI2c = I2c<'static, Async, i2c::Master>;
63 63
64static TEMP: AtomicI32 = AtomicI32::new(0); 64static TEMP: AtomicI32 = AtomicI32::new(0);
65 65
diff --git a/examples/stm32l432/src/bin/qspi_mmap.rs b/examples/stm32l432/src/bin/qspi_mmap.rs
index 86a20eb3d..075458fe5 100644
--- a/examples/stm32l432/src/bin/qspi_mmap.rs
+++ b/examples/stm32l432/src/bin/qspi_mmap.rs
@@ -7,7 +7,7 @@
7use defmt::info; 7use defmt::info;
8use embassy_stm32::mode; 8use embassy_stm32::mode;
9use embassy_stm32::qspi::enums::{ 9use embassy_stm32::qspi::enums::{
10 AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, MemorySize, QspiWidth, 10 AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, MemorySize, QspiWidth, SampleShifting,
11}; 11};
12use embassy_stm32::qspi::{self, Instance, TransferConfig}; 12use embassy_stm32::qspi::{self, Instance, TransferConfig};
13pub struct FlashMemory<I: Instance> { 13pub struct FlashMemory<I: Instance> {
@@ -252,6 +252,7 @@ async fn main(_spawner: Spawner) {
252 prescaler: 200, 252 prescaler: 200,
253 cs_high_time: ChipSelectHighTime::_1Cycle, 253 cs_high_time: ChipSelectHighTime::_1Cycle,
254 fifo_threshold: FIFOThresholdLevel::_16Bytes, 254 fifo_threshold: FIFOThresholdLevel::_16Bytes,
255 sample_shifting: SampleShifting::None,
255 }; 256 };
256 let driver = qspi::Qspi::new_bank1(p.QUADSPI, p.PB1, p.PB0, p.PA7, p.PA6, p.PA3, p.PA2, p.DMA2_CH7, config); 257 let driver = qspi::Qspi::new_bank1(p.QUADSPI, p.PB1, p.PB0, p.PA7, p.PA6, p.PA3, p.PA2, p.DMA2_CH7, config);
257 let mut flash = FlashMemory::new(driver); 258 let mut flash = FlashMemory::new(driver);
diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml
index 3ea3bcd5c..1379d963c 100644
--- a/examples/stm32l5/Cargo.toml
+++ b/examples/stm32l5/Cargo.toml
@@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [
10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } 13embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
14embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } 14embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] }
15embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 15embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
16usbd-hid = "0.8.1" 16usbd-hid = "0.8.1"
diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml
index 3aa45dc79..612d12ac2 100644
--- a/examples/stm32u0/Cargo.toml
+++ b/examples/stm32u0/Cargo.toml
@@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [
10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-usb = { version = "0.4.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } 13embassy-usb = { version = "0.5.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] }
14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
15 15
16defmt = "1.0.1" 16defmt = "1.0.1"
diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml
index 777d3ed4c..f92e85852 100644
--- a/examples/stm32u5/Cargo.toml
+++ b/examples/stm32u5/Cargo.toml
@@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["
10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } 13embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
15 15
16defmt = "1.0.1" 16defmt = "1.0.1"
diff --git a/examples/stm32wb/src/bin/gatt_server.rs b/examples/stm32wb/src/bin/gatt_server.rs
index 041dc0cf5..9864fa026 100644
--- a/examples/stm32wb/src/bin/gatt_server.rs
+++ b/examples/stm32wb/src/bin/gatt_server.rs
@@ -27,6 +27,7 @@ use embassy_stm32_wpan::hci::vendor::event::{self, AttributeHandle, VendorEvent}
27use embassy_stm32_wpan::hci::{BdAddr, Event}; 27use embassy_stm32_wpan::hci::{BdAddr, Event};
28use embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp; 28use embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp;
29use embassy_stm32_wpan::sub::ble::Ble; 29use embassy_stm32_wpan::sub::ble::Ble;
30use embassy_stm32_wpan::sub::mm;
30use embassy_stm32_wpan::TlMbox; 31use embassy_stm32_wpan::TlMbox;
31use {defmt_rtt as _, panic_probe as _}; 32use {defmt_rtt as _, panic_probe as _};
32 33
@@ -38,7 +39,7 @@ bind_interrupts!(struct Irqs{
38const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7; 39const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7;
39 40
40#[embassy_executor::main] 41#[embassy_executor::main]
41async fn main(_spawner: Spawner) { 42async fn main(spawner: Spawner) {
42 /* 43 /*
43 How to make this work: 44 How to make this work:
44 45
@@ -70,6 +71,7 @@ async fn main(_spawner: Spawner) {
70 let config = Config::default(); 71 let config = Config::default();
71 let mut mbox = TlMbox::init(p.IPCC, Irqs, config); 72 let mut mbox = TlMbox::init(p.IPCC, Irqs, config);
72 73
74 spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap();
73 let sys_event = mbox.sys_subsystem.read().await; 75 let sys_event = mbox.sys_subsystem.read().await;
74 info!("sys event: {}", sys_event.payload()); 76 info!("sys event: {}", sys_event.payload());
75 77
@@ -221,6 +223,11 @@ async fn main(_spawner: Spawner) {
221 } 223 }
222} 224}
223 225
226#[embassy_executor::task]
227async fn run_mm_queue(memory_manager: mm::MemoryManager) {
228 memory_manager.run_queue().await;
229}
230
224fn get_bd_addr() -> BdAddr { 231fn get_bd_addr() -> BdAddr {
225 let mut bytes = [0u8; 6]; 232 let mut bytes = [0u8; 6];
226 233
diff --git a/examples/stm32wba/src/bin/adc.rs b/examples/stm32wba/src/bin/adc.rs
new file mode 100644
index 000000000..a9651d57e
--- /dev/null
+++ b/examples/stm32wba/src/bin/adc.rs
@@ -0,0 +1,49 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_stm32::adc::{adc4, AdcChannel};
6use {defmt_rtt as _, panic_probe as _};
7
8#[embassy_executor::main]
9async fn main(_spawner: embassy_executor::Spawner) {
10 let config = embassy_stm32::Config::default();
11
12 let mut p = embassy_stm32::init(config);
13
14 // **** ADC4 init ****
15 let mut adc4 = adc4::Adc4::new(p.ADC4);
16 let mut adc4_pin1 = p.PA0; // A4
17 let mut adc4_pin2 = p.PA1; // A5
18 adc4.set_resolution(adc4::Resolution::BITS12);
19 adc4.set_averaging(adc4::Averaging::Samples256);
20 adc4.set_sample_time(adc4::SampleTime::CYCLES1_5);
21 let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12);
22
23 // **** ADC4 blocking read ****
24 let raw: u16 = adc4.blocking_read(&mut adc4_pin1);
25 let volt: f32 = 3.0 * raw as f32 / max4 as f32;
26 info!("Read adc4 pin 1 {}", volt);
27
28 let raw: u16 = adc4.blocking_read(&mut adc4_pin2);
29 let volt: f32 = 3.3 * raw as f32 / max4 as f32;
30 info!("Read adc4 pin 2 {}", volt);
31
32 // **** ADC4 async read ****
33 let mut degraded41 = adc4_pin1.degrade_adc();
34 let mut degraded42 = adc4_pin2.degrade_adc();
35 let mut measurements = [0u16; 2];
36
37 // The channels must be in ascending order and can't repeat for ADC4
38 adc4.read(
39 p.GPDMA1_CH1.reborrow(),
40 [&mut degraded42, &mut degraded41].into_iter(),
41 &mut measurements,
42 )
43 .await
44 .unwrap();
45 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;
47 info!("Async read 4 pin 1 {}", volt1);
48 info!("Async read 4 pin 2 {}", volt2);
49}
diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml
index 5ecd77443..1c5d9c07a 100644
--- a/examples/stm32wl/Cargo.toml
+++ b/examples/stm32wl/Cargo.toml
@@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["
10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } 13embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" }
14 14
15defmt = "1.0.1" 15defmt = "1.0.1"
16defmt-rtt = "1.0.0" 16defmt-rtt = "1.0.0"
diff --git a/release/bump-dependency.sh b/release/bump-dependency.sh
index 07511d229..97f73cddc 100755
--- a/release/bump-dependency.sh
+++ b/release/bump-dependency.sh
@@ -8,4 +8,4 @@
8# 8#
9CRATE=$1 9CRATE=$1
10TARGET_VER=$2 10TARGET_VER=$2
11find . -name "Cargo.toml" | xargs sed -rie "s/($CRATE = \{.*version = \")[0-9]+.[0-9]+.?[0-9]*(\".*)/\1$TARGET_VER\2/g" 11find . -name "Cargo.toml" | xargs sed -ri "s/($CRATE = \{.*version = \")[0-9]+.[0-9]+.?[0-9]*(\".*)/\1$TARGET_VER\2/g"
diff --git a/rust-toolchain-nightly.toml b/rust-toolchain-nightly.toml
index e75ea40cc..411cc6946 100644
--- a/rust-toolchain-nightly.toml
+++ b/rust-toolchain-nightly.toml
@@ -1,5 +1,5 @@
1[toolchain] 1[toolchain]
2channel = "nightly-2025-03-12" 2channel = "nightly-2025-06-29"
3components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ] 3components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ]
4targets = [ 4targets = [
5 "thumbv7em-none-eabi", 5 "thumbv7em-none-eabi",
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
index 870904c3a..e24864037 100644
--- a/rust-toolchain.toml
+++ b/rust-toolchain.toml
@@ -1,5 +1,5 @@
1[toolchain] 1[toolchain]
2channel = "1.85" 2channel = "1.88"
3components = [ "rust-src", "rustfmt", "llvm-tools" ] 3components = [ "rust-src", "rustfmt", "llvm-tools" ]
4targets = [ 4targets = [
5 "thumbv7em-none-eabi", 5 "thumbv7em-none-eabi",
diff --git a/tests/mspm0/Cargo.toml b/tests/mspm0/Cargo.toml
index 298522d09..5ba3e586b 100644
--- a/tests/mspm0/Cargo.toml
+++ b/tests/mspm0/Cargo.toml
@@ -6,15 +6,17 @@ license = "MIT OR Apache-2.0"
6 6
7[features] 7[features]
8mspm0g3507 = [ "embassy-mspm0/mspm0g3507pm" ] 8mspm0g3507 = [ "embassy-mspm0/mspm0g3507pm" ]
9mspm0g3519 = [ "embassy-mspm0/mspm0g3519pz" ]
9 10
10[dependencies] 11[dependencies]
11teleprobe-meta = "1.1" 12teleprobe-meta = "1.1"
12 13
13embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = [ "defmt" ] } 14embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = [ "defmt" ] }
14embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = [ "arch-cortex-m", "executor-thread", "defmt" ] } 15embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = [ "arch-cortex-m", "executor-thread", "defmt" ] }
16embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
15embassy-time = { version = "0.4.0", path = "../../embassy-time", features = [ "defmt" ] } 17embassy-time = { version = "0.4.0", path = "../../embassy-time", features = [ "defmt" ] }
16embassy-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = [ "rt", "defmt", "unstable-pac", "time-driver-any" ] } 18embassy-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = [ "rt", "defmt", "unstable-pac", "time-driver-any" ] }
17embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal/"} 19embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal/"}
18 20
19defmt = "1.0.1" 21defmt = "1.0.1"
20defmt-rtt = "1.0.0" 22defmt-rtt = "1.0.0"
@@ -23,6 +25,8 @@ cortex-m = { version = "0.7.6", features = [ "inline-asm", "critical-section-sin
23cortex-m-rt = "0.7.0" 25cortex-m-rt = "0.7.0"
24embedded-hal = { package = "embedded-hal", version = "1.0" } 26embedded-hal = { package = "embedded-hal", version = "1.0" }
25embedded-hal-async = { version = "1.0" } 27embedded-hal-async = { version = "1.0" }
28embedded-io = { version = "0.6.1", features = ["defmt-03"] }
29embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
26panic-probe = { version = "1.0.0", features = ["print-defmt"] } 30panic-probe = { version = "1.0.0", features = ["print-defmt"] }
27static_cell = "2" 31static_cell = "2"
28portable-atomic = { version = "1.5", features = ["critical-section"] } 32portable-atomic = { version = "1.5", features = ["critical-section"] }
diff --git a/tests/mspm0/build.rs b/tests/mspm0/build.rs
index 0b58fb9e9..43a9ac04f 100644
--- a/tests/mspm0/build.rs
+++ b/tests/mspm0/build.rs
@@ -8,6 +8,9 @@ fn main() -> Result<(), Box<dyn Error>> {
8 #[cfg(feature = "mspm0g3507")] 8 #[cfg(feature = "mspm0g3507")]
9 let memory_x = include_bytes!("memory_g3507.x"); 9 let memory_x = include_bytes!("memory_g3507.x");
10 10
11 #[cfg(feature = "mspm0g3519")]
12 let memory_x = include_bytes!("memory_g3519.x");
13
11 fs::write(out.join("memory.x"), memory_x).unwrap(); 14 fs::write(out.join("memory.x"), memory_x).unwrap();
12 15
13 println!("cargo:rustc-link-search={}", out.display()); 16 println!("cargo:rustc-link-search={}", out.display());
diff --git a/tests/mspm0/memory_g3519.x b/tests/mspm0/memory_g3519.x
new file mode 100644
index 000000000..d62f10360
--- /dev/null
+++ b/tests/mspm0/memory_g3519.x
@@ -0,0 +1,6 @@
1MEMORY
2{
3 FLASH : ORIGIN = 0x00000000, LENGTH = 128K
4 /* Select non-parity range of SRAM due to SRAM_ERR_01 errata in SLAZ758 */
5 RAM : ORIGIN = 0x20200000, LENGTH = 64K
6}
diff --git a/tests/mspm0/src/bin/dma.rs b/tests/mspm0/src/bin/dma.rs
new file mode 100644
index 000000000..6fd973a18
--- /dev/null
+++ b/tests/mspm0/src/bin/dma.rs
@@ -0,0 +1,503 @@
1#![no_std]
2#![no_main]
3
4#[cfg(feature = "mspm0g3507")]
5teleprobe_meta::target!(b"lp-mspm0g3507");
6
7#[cfg(feature = "mspm0g3519")]
8teleprobe_meta::target!(b"lp-mspm0g3519");
9
10use core::slice;
11
12use defmt::{assert, assert_eq, *};
13use embassy_executor::Spawner;
14use embassy_mspm0::dma::{Channel, Transfer, TransferMode, TransferOptions, Word};
15use embassy_mspm0::Peri;
16use {defmt_rtt as _, panic_probe as _};
17
18#[embassy_executor::main]
19async fn main(_spawner: Spawner) {
20 let mut p = embassy_mspm0::init(Default::default());
21 info!("Hello World!");
22
23 {
24 info!("Single u8 read (blocking)");
25 single_read(p.DMA_CH0.reborrow(), 0x41_u8);
26
27 info!("Single u16 read (blocking)");
28 single_read(p.DMA_CH0.reborrow(), 0xFF41_u16);
29
30 info!("Single u32 read (blocking)");
31 single_read(p.DMA_CH0.reborrow(), 0xFFEE_FF41_u32);
32
33 info!("Single u64 read (blocking)");
34 single_read(p.DMA_CH0.reborrow(), 0x0011_2233_FFEE_FF41_u64);
35 }
36
37 // Widening transfers
38 {
39 info!("Single u8 read to u16");
40 widening_single_read::<u8, u16>(p.DMA_CH0.reborrow(), 0x41);
41
42 info!("Single u8 read to u32");
43 widening_single_read::<u8, u32>(p.DMA_CH0.reborrow(), 0x43);
44
45 info!("Single u8 read to u64");
46 widening_single_read::<u8, u64>(p.DMA_CH0.reborrow(), 0x47);
47
48 info!("Single u16 read to u32");
49 widening_single_read::<u16, u32>(p.DMA_CH0.reborrow(), 0xAE43);
50
51 info!("Single u16 read to u64");
52 widening_single_read::<u16, u64>(p.DMA_CH0.reborrow(), 0xAF47);
53
54 info!("Single u32 read to u64");
55 widening_single_read::<u32, u64>(p.DMA_CH0.reborrow(), 0xDEAD_AF47);
56 }
57
58 // Narrowing transfers.
59 {
60 info!("Single u16 read to u8");
61 narrowing_single_read::<u16, u8>(p.DMA_CH0.reborrow(), 0x4142);
62
63 info!("Single u32 read to u8");
64 narrowing_single_read::<u32, u8>(p.DMA_CH0.reborrow(), 0x4142_2414);
65
66 info!("Single u64 read to u8");
67 narrowing_single_read::<u64, u8>(p.DMA_CH0.reborrow(), 0x4142_2414_5153_7776);
68
69 info!("Single u32 read to u16");
70 narrowing_single_read::<u32, u16>(p.DMA_CH0.reborrow(), 0x4142_2414);
71
72 info!("Single u64 read to u16");
73 narrowing_single_read::<u64, u16>(p.DMA_CH0.reborrow(), 0x4142_2414_5153_7776);
74
75 info!("Single u64 read to u32");
76 narrowing_single_read::<u64, u32>(p.DMA_CH0.reborrow(), 0x4142_2414_5153_7776);
77 }
78
79 {
80 info!("Single u8 read (async)");
81 async_single_read(p.DMA_CH0.reborrow(), 0x42_u8).await;
82
83 info!("Single u16 read (async)");
84 async_single_read(p.DMA_CH0.reborrow(), 0xAE42_u16).await;
85
86 info!("Single u32 read (async)");
87 async_single_read(p.DMA_CH0.reborrow(), 0xFE44_1500_u32).await;
88
89 info!("Single u64 read (async)");
90 async_single_read(p.DMA_CH0.reborrow(), 0x8F7F_6F5F_4F3F_2F1F_u64).await;
91 }
92
93 {
94 info!("Multiple u8 reads (blocking)");
95 block_read::<_, 16>(p.DMA_CH0.reborrow(), 0x98_u8);
96
97 info!("Multiple u16 reads (blocking)");
98 block_read::<_, 2>(p.DMA_CH0.reborrow(), 0x9801_u16);
99
100 info!("Multiple u32 reads (blocking)");
101 block_read::<_, 4>(p.DMA_CH0.reborrow(), 0x9821_9801_u32);
102
103 info!("Multiple u64 reads (blocking)");
104 block_read::<_, 4>(p.DMA_CH0.reborrow(), 0xABCD_EF01_2345_6789_u64);
105 }
106
107 {
108 info!("Multiple u8 reads (async)");
109 async_block_read::<_, 8>(p.DMA_CH0.reborrow(), 0x86_u8).await;
110
111 info!("Multiple u16 reads (async)");
112 async_block_read::<_, 6>(p.DMA_CH0.reborrow(), 0x7777_u16).await;
113
114 info!("Multiple u32 reads (async)");
115 async_block_read::<_, 3>(p.DMA_CH0.reborrow(), 0xA5A5_A5A5_u32).await;
116
117 info!("Multiple u64 reads (async)");
118 async_block_read::<_, 14>(p.DMA_CH0.reborrow(), 0x5A5A_5A5A_A5A5_A5A5_u64).await;
119 }
120
121 // Intentionally skip testing multiple reads in single transfer mode.
122 //
123 // If the destination length is greater than 1 and single transfer mode is used then two transfers
124 // are performed in a trigger. Similarly with any other length of destination above 2, only 2 transfers
125 // are performed. Issuing another trigger (resume) results in no further progress. More than likely
126 // the test does not work due to some combination of a hardware bug and the datasheet being unclear
127 // regarding what ends a software trigger.
128 //
129 // However this case works fine with a hardware trigger (such as the ADC hardware trigger).
130
131 {
132 info!("Single u8 write (blocking)");
133 single_write(p.DMA_CH0.reborrow(), 0x41_u8);
134
135 info!("Single u16 write (blocking)");
136 single_write(p.DMA_CH0.reborrow(), 0x4142_u16);
137
138 info!("Single u32 write (blocking)");
139 single_write(p.DMA_CH0.reborrow(), 0x4142_4344_u32);
140
141 info!("Single u64 write (blocking)");
142 single_write(p.DMA_CH0.reborrow(), 0x4142_4344_4546_4748_u64);
143 }
144
145 {
146 info!("Single u8 write (async)");
147 async_single_write(p.DMA_CH0.reborrow(), 0xAA_u8).await;
148
149 info!("Single u16 write (async)");
150 async_single_write(p.DMA_CH0.reborrow(), 0xBBBB_u16).await;
151
152 info!("Single u32 write (async)");
153 async_single_write(p.DMA_CH0.reborrow(), 0xCCCC_CCCC_u32).await;
154
155 info!("Single u64 write (async)");
156 async_single_write(p.DMA_CH0.reborrow(), 0xDDDD_DDDD_DDDD_DDDD_u64).await;
157 }
158
159 {
160 info!("Multiple u8 writes (blocking)");
161 block_write(p.DMA_CH0.reborrow(), &[0xFF_u8, 0x7F, 0x3F, 0x1F]);
162
163 info!("Multiple u16 writes (blocking)");
164 block_write(p.DMA_CH0.reborrow(), &[0xFFFF_u16, 0xFF7F, 0xFF3F, 0xFF1F]);
165
166 info!("Multiple u32 writes (blocking)");
167 block_write(
168 p.DMA_CH0.reborrow(),
169 &[0xFF00_00FF_u32, 0xFF00_007F, 0x0000_FF3F, 0xFF1F_0000],
170 );
171
172 info!("Multiple u64 writes (blocking)");
173 block_write(
174 p.DMA_CH0.reborrow(),
175 &[
176 0xFF00_0000_0000_00FF_u64,
177 0x0000_FF00_007F_0000,
178 0x0000_FF3F_0000_0000,
179 0xFF1F_0000_1111_837A,
180 ],
181 );
182 }
183
184 {
185 info!("Multiple u8 writes (async)");
186 async_block_write(p.DMA_CH0.reborrow(), &[0u8, 1, 2, 3]).await;
187
188 info!("Multiple u16 writes (async)");
189 async_block_write(p.DMA_CH0.reborrow(), &[0x9801u16, 0x9802, 0x9803, 0x9800, 0x9000]).await;
190
191 info!("Multiple u32 writes (async)");
192 async_block_write(p.DMA_CH0.reborrow(), &[0x9801_ABCDu32, 0xFFAC_9802, 0xDEAD_9803]).await;
193
194 info!("Multiple u64 writes (async)");
195 async_block_write(
196 p.DMA_CH0.reborrow(),
197 &[
198 0xA55A_1111_3333_5555_u64,
199 0x1111_A55A_3333_5555,
200 0x5555_A55A_3333_1111,
201 0x01234_5678_89AB_CDEF,
202 ],
203 )
204 .await;
205 }
206
207 // TODO: Mixed byte and word transfers.
208
209 info!("Test OK");
210 cortex_m::asm::bkpt();
211}
212
213fn single_read<W: Word + Copy + Default + Eq + defmt::Format>(mut channel: Peri<'_, impl Channel>, mut src: W) {
214 let options = TransferOptions::default();
215 let mut dst = W::default();
216
217 // SAFETY: src and dst outlive the transfer.
218 let transfer = unsafe {
219 unwrap!(Transfer::new_read(
220 channel.reborrow(),
221 Transfer::SOFTWARE_TRIGGER,
222 &mut src,
223 slice::from_mut(&mut dst),
224 options,
225 ))
226 };
227 transfer.blocking_wait();
228
229 assert_eq!(src, dst);
230}
231
232async fn async_single_read<W: Word + Copy + Default + Eq + defmt::Format>(
233 mut channel: Peri<'_, impl Channel>,
234 mut src: W,
235) {
236 let options = TransferOptions::default();
237 let mut dst = W::default();
238
239 // SAFETY: src and dst outlive the transfer.
240 let transfer = unsafe {
241 unwrap!(Transfer::new_read(
242 channel.reborrow(),
243 Transfer::SOFTWARE_TRIGGER,
244 &mut src,
245 slice::from_mut(&mut dst),
246 options,
247 ))
248 };
249 transfer.await;
250
251 assert_eq!(src, dst);
252}
253
254fn block_read<W: Word + Copy + Default + Eq + defmt::Format, const N: usize>(
255 mut channel: Peri<'_, impl Channel>,
256 mut src: W,
257) {
258 let mut options = TransferOptions::default();
259 // Complete the entire transfer.
260 options.mode = TransferMode::Block;
261
262 let mut dst = [W::default(); N];
263
264 // SAFETY: src and dst outlive the transfer.
265 let transfer = unsafe {
266 unwrap!(Transfer::new_read(
267 channel.reborrow(),
268 Transfer::SOFTWARE_TRIGGER,
269 &mut src,
270 &mut dst[..],
271 options,
272 ))
273 };
274 transfer.blocking_wait();
275
276 assert_eq!(dst, [src; N]);
277}
278
279async fn async_block_read<W: Word + Copy + Default + Eq + defmt::Format, const N: usize>(
280 mut channel: Peri<'_, impl Channel>,
281 mut src: W,
282) {
283 let mut options = TransferOptions::default();
284 // Complete the entire transfer.
285 options.mode = TransferMode::Block;
286
287 let mut dst = [W::default(); N];
288
289 // SAFETY: src and dst outlive the transfer.
290 let transfer = unsafe {
291 unwrap!(Transfer::new_read(
292 channel.reborrow(),
293 Transfer::SOFTWARE_TRIGGER,
294 &mut src,
295 &mut dst[..],
296 options,
297 ))
298 };
299 transfer.await;
300
301 assert_eq!(dst, [src; N]);
302}
303
304fn single_write<W: Word + Default + Eq + defmt::Format>(mut channel: Peri<'_, impl Channel>, src: W) {
305 let options = TransferOptions::default();
306 let mut dst = W::default();
307
308 // SAFETY: src and dst outlive the transfer.
309 let transfer = unsafe {
310 unwrap!(Transfer::new_write(
311 channel.reborrow(),
312 Transfer::SOFTWARE_TRIGGER,
313 slice::from_ref(&src),
314 &mut dst,
315 options,
316 ))
317 };
318 transfer.blocking_wait();
319
320 assert_eq!(src, dst);
321}
322
323async fn async_single_write<W: Word + Default + Eq + defmt::Format>(mut channel: Peri<'_, impl Channel>, src: W) {
324 let options = TransferOptions::default();
325 let mut dst = W::default();
326
327 // SAFETY: src and dst outlive the transfer.
328 let transfer = unsafe {
329 unwrap!(Transfer::new_write(
330 channel.reborrow(),
331 Transfer::SOFTWARE_TRIGGER,
332 slice::from_ref(&src),
333 &mut dst,
334 options,
335 ))
336 };
337 transfer.await;
338
339 assert_eq!(src, dst);
340}
341
342fn block_write<W: Word + Default + Eq + defmt::Format>(mut channel: Peri<'_, impl Channel>, src: &[W]) {
343 let mut options = TransferOptions::default();
344 // Complete the entire transfer.
345 options.mode = TransferMode::Block;
346
347 let mut dst = W::default();
348
349 // Starting from 1 because a zero length transfer does nothing.
350 for i in 1..src.len() {
351 info!("-> {} write(s)", i);
352
353 // SAFETY: src and dst outlive the transfer.
354 let transfer = unsafe {
355 unwrap!(Transfer::new_write(
356 channel.reborrow(),
357 Transfer::SOFTWARE_TRIGGER,
358 &src[..i],
359 &mut dst,
360 options,
361 ))
362 };
363 transfer.blocking_wait();
364
365 // The result will be the last value written.
366 assert_eq!(dst, src[i - 1]);
367 }
368}
369
370async fn async_block_write<W: Word + Default + Eq + defmt::Format>(mut channel: Peri<'_, impl Channel>, src: &[W]) {
371 let mut options = TransferOptions::default();
372 // Complete the entire transfer.
373 options.mode = TransferMode::Block;
374
375 let mut dst = W::default();
376
377 // Starting from 1 because a zero length transfer does nothing.
378 for i in 1..src.len() {
379 info!("-> {} write(s)", i);
380 // SAFETY: src and dst outlive the transfer.
381 let transfer = unsafe {
382 unwrap!(Transfer::new_write(
383 channel.reborrow(),
384 Transfer::SOFTWARE_TRIGGER,
385 &src[..i],
386 &mut dst,
387 options,
388 ))
389 };
390 transfer.await;
391
392 // The result will be the last value written.
393 assert_eq!(dst, src[i - 1]);
394 }
395}
396
397/// [`single_read`], but testing when the destination is wider than the source.
398///
399/// The MSPM0 DMA states that the upper bytes when the destination is longer than the source are zeroed.
400/// This matches the behavior in Rust for all unsigned integer types.
401fn widening_single_read<SW, DW>(mut channel: Peri<'_, impl Channel>, mut src: SW)
402where
403 SW: Word + Copy + Default + Eq + defmt::Format,
404 DW: Word + Copy + Default + Eq + defmt::Format + From<SW>,
405{
406 assert!(
407 DW::size() > SW::size(),
408 "This test only works when the destination is larger than the source"
409 );
410
411 let options = TransferOptions::default();
412 let mut dst = DW::default();
413
414 // SAFETY: src and dst outlive the transfer.
415 let transfer = unsafe {
416 unwrap!(Transfer::new_read(
417 channel.reborrow(),
418 Transfer::SOFTWARE_TRIGGER,
419 &mut src,
420 slice::from_mut(&mut dst),
421 options,
422 ))
423 };
424 transfer.blocking_wait();
425
426 assert_eq!(DW::from(src), dst);
427}
428
429/// [`single_read`], but testing when the destination is narrower than the source.
430///
431/// The MSPM0 DMA states that the upper bytes when the source is longer than the destination are dropped.
432/// This matches the behavior in Rust for all unsigned integer types.
433fn narrowing_single_read<SW, DW>(mut channel: Peri<'_, impl Channel>, mut src: SW)
434where
435 SW: Word + Copy + Default + Eq + defmt::Format + From<DW>,
436 DW: Word + Copy + Default + Eq + defmt::Format + Narrow<SW>,
437{
438 assert!(
439 SW::size() > DW::size(),
440 "This test only works when the source is larger than the destination"
441 );
442
443 let options = TransferOptions::default();
444 let mut dst = DW::default();
445
446 // SAFETY: src and dst outlive the transfer.
447 let transfer = unsafe {
448 unwrap!(Transfer::new_read(
449 channel.reborrow(),
450 Transfer::SOFTWARE_TRIGGER,
451 &mut src,
452 slice::from_mut(&mut dst),
453 options,
454 ))
455 };
456 transfer.blocking_wait();
457
458 // The expected value is the source value masked by the maximum destination value.
459 // This is effectively `src as DW as SW` to drop the upper byte(s).
460 let expect = SW::from(DW::narrow(src));
461 assert_eq!(expect, dst.into());
462}
463
464/// A pseudo `as` trait to allow downcasting integer types (TryFrom could fail).
465trait Narrow<T> {
466 fn narrow(value: T) -> Self;
467}
468
469impl Narrow<u16> for u8 {
470 fn narrow(value: u16) -> Self {
471 value as u8
472 }
473}
474
475impl Narrow<u32> for u8 {
476 fn narrow(value: u32) -> Self {
477 value as u8
478 }
479}
480
481impl Narrow<u64> for u8 {
482 fn narrow(value: u64) -> Self {
483 value as u8
484 }
485}
486
487impl Narrow<u32> for u16 {
488 fn narrow(value: u32) -> Self {
489 value as u16
490 }
491}
492
493impl Narrow<u64> for u16 {
494 fn narrow(value: u64) -> Self {
495 value as u16
496 }
497}
498
499impl Narrow<u64> for u32 {
500 fn narrow(value: u64) -> Self {
501 value as u32
502 }
503}
diff --git a/tests/mspm0/src/bin/uart.rs b/tests/mspm0/src/bin/uart.rs
index 458129d44..916ce0d4b 100644
--- a/tests/mspm0/src/bin/uart.rs
+++ b/tests/mspm0/src/bin/uart.rs
@@ -4,6 +4,9 @@
4#[cfg(feature = "mspm0g3507")] 4#[cfg(feature = "mspm0g3507")]
5teleprobe_meta::target!(b"lp-mspm0g3507"); 5teleprobe_meta::target!(b"lp-mspm0g3507");
6 6
7#[cfg(feature = "mspm0g3519")]
8teleprobe_meta::target!(b"lp-mspm0g3519");
9
7use defmt::{assert_eq, unwrap, *}; 10use defmt::{assert_eq, unwrap, *};
8use embassy_executor::Spawner; 11use embassy_executor::Spawner;
9use embassy_mspm0::mode::Blocking; 12use embassy_mspm0::mode::Blocking;
@@ -23,7 +26,7 @@ async fn main(_spawner: Spawner) {
23 26
24 // TODO: Allow creating a looped-back UART (so pins are not needed). 27 // TODO: Allow creating a looped-back UART (so pins are not needed).
25 // Do not select default UART since the virtual COM port is attached to UART0. 28 // Do not select default UART since the virtual COM port is attached to UART0.
26 #[cfg(feature = "mspm0g3507")] 29 #[cfg(any(feature = "mspm0g3507", feature = "mspm0g3519"))]
27 let (mut tx, mut rx, mut uart) = (p.PA8, p.PA9, p.UART1); 30 let (mut tx, mut rx, mut uart) = (p.PA8, p.PA9, p.UART1);
28 31
29 const MFCLK_BUAD_RATES: &[u32] = &[1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200]; 32 const MFCLK_BUAD_RATES: &[u32] = &[1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200];
diff --git a/tests/mspm0/src/bin/uart_buffered.rs b/tests/mspm0/src/bin/uart_buffered.rs
new file mode 100644
index 000000000..135ac1287
--- /dev/null
+++ b/tests/mspm0/src/bin/uart_buffered.rs
@@ -0,0 +1,115 @@
1#![no_std]
2#![no_main]
3
4#[cfg(feature = "mspm0g3507")]
5teleprobe_meta::target!(b"lp-mspm0g3507");
6
7use defmt::{assert_eq, unwrap, *};
8use embassy_executor::Spawner;
9use embassy_mspm0::uart::{BufferedInterruptHandler, BufferedUart, Config};
10use embassy_mspm0::{bind_interrupts, peripherals};
11use {defmt_rtt as _, panic_probe as _};
12
13bind_interrupts!(struct Irqs {
14 UART1 => BufferedInterruptHandler<peripherals::UART1>;
15});
16
17#[embassy_executor::main]
18async fn main(_spawner: Spawner) {
19 let p = embassy_mspm0::init(Default::default());
20 info!("Hello World!");
21
22 // TODO: Allow creating a looped-back UART (so pins are not needed).
23 // Do not select default UART since the virtual COM port is attached to UART0.
24 #[cfg(any(feature = "mspm0g3507"))]
25 let (mut tx, mut rx, mut uart) = (p.PA8, p.PA9, p.UART1);
26
27 {
28 use embedded_io_async::{Read, Write};
29
30 let mut config = Config::default();
31 config.loop_back_enable = true;
32 config.fifo_enable = false;
33
34 let tx_buf = &mut [0u8; 16];
35 let rx_buf = &mut [0u8; 16];
36 let mut uart = unwrap!(BufferedUart::new(
37 uart.reborrow(),
38 tx.reborrow(),
39 rx.reborrow(),
40 Irqs,
41 tx_buf,
42 rx_buf,
43 config
44 ));
45
46 let mut buf = [0; 16];
47 for (j, b) in buf.iter_mut().enumerate() {
48 *b = j as u8;
49 }
50
51 unwrap!(uart.write_all(&buf).await);
52 unwrap!(uart.flush().await);
53
54 unwrap!(uart.read_exact(&mut buf).await);
55 for (j, b) in buf.iter().enumerate() {
56 assert_eq!(*b, j as u8);
57 }
58
59 // Buffer is unclogged, should be able to write again.
60 unwrap!(uart.write_all(&buf).await);
61 unwrap!(uart.flush().await);
62
63 unwrap!(uart.read_exact(&mut buf).await);
64 for (j, b) in buf.iter().enumerate() {
65 assert_eq!(*b, j as u8);
66 }
67 }
68
69 info!("Blocking buffered");
70 {
71 use embedded_io::{Read, Write};
72
73 let mut config = Config::default();
74 config.loop_back_enable = true;
75 config.fifo_enable = false;
76
77 let tx_buf = &mut [0u8; 16];
78 let rx_buf = &mut [0u8; 16];
79 let mut uart = unwrap!(BufferedUart::new(
80 uart.reborrow(),
81 tx.reborrow(),
82 rx.reborrow(),
83 Irqs,
84 tx_buf,
85 rx_buf,
86 config
87 ));
88
89 let mut buf = [0; 16];
90
91 for (j, b) in buf.iter_mut().enumerate() {
92 *b = j as u8;
93 }
94
95 unwrap!(uart.write_all(&buf));
96 unwrap!(uart.blocking_flush());
97 unwrap!(uart.read_exact(&mut buf));
98
99 for (j, b) in buf.iter().enumerate() {
100 assert_eq!(*b, j as u8);
101 }
102
103 // Buffer is unclogged, should be able to write again.
104 unwrap!(uart.write_all(&buf));
105 unwrap!(uart.blocking_flush());
106 unwrap!(uart.read_exact(&mut buf));
107
108 for (j, b) in buf.iter().enumerate() {
109 assert_eq!(*b, j as u8, "at {}", j);
110 }
111 }
112
113 info!("Test OK");
114 cortex_m::asm::bkpt();
115}
diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml
index 30c4223b7..b167c589e 100644
--- a/tests/nrf/Cargo.toml
+++ b/tests/nrf/Cargo.toml
@@ -11,7 +11,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
11embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt", ] } 11embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt", ] }
12embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 12embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
13embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 13embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
14embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } 14embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] }
15embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } 15embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
16embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } 16embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] }
17embassy-net-esp-hosted = { version = "0.2.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } 17embassy-net-esp-hosted = { version = "0.2.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] }
diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml
index 2be37f525..298955fe3 100644
--- a/tests/rp/Cargo.toml
+++ b/tests/rp/Cargo.toml
@@ -15,11 +15,11 @@ teleprobe-meta = "1.1"
15embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } 15embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
16embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 16embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
17embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", ] } 17embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", ] }
18embassy-rp = { version = "0.4.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } 18embassy-rp = { version = "0.6.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] }
19embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 19embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
20embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] } 20embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] }
21embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } 21embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
22embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal/"} 22embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal/"}
23cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] } 23cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] }
24cyw43-pio = { path = "../../cyw43-pio", features = ["defmt"] } 24cyw43-pio = { path = "../../cyw43-pio", features = ["defmt"] }
25perf-client = { path = "../perf-client" } 25perf-client = { path = "../perf-client" }
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml
index 8d10f6593..7c32c0ce1 100644
--- a/tests/stm32/Cargo.toml
+++ b/tests/stm32/Cargo.toml
@@ -7,6 +7,7 @@ autobins = false
7 7
8[features] 8[features]
9stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"] 9stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"]
10stm32c071rb = ["embassy-stm32/stm32c071rb", "cm0", "not-gpdma"]
10stm32f103c8 = ["embassy-stm32/stm32f103c8", "spi-v1", "not-gpdma"] 11stm32f103c8 = ["embassy-stm32/stm32f103c8", "spi-v1", "not-gpdma"]
11stm32f207zg = ["embassy-stm32/stm32f207zg", "spi-v1", "chrono", "not-gpdma", "eth", "rng"] 12stm32f207zg = ["embassy-stm32/stm32f207zg", "spi-v1", "chrono", "not-gpdma", "eth", "rng"]
12stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"] 13stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"]
diff --git a/tests/stm32/build.rs b/tests/stm32/build.rs
index 722671bf1..556d77a20 100644
--- a/tests/stm32/build.rs
+++ b/tests/stm32/build.rs
@@ -12,6 +12,7 @@ fn main() -> Result<(), Box<dyn Error>> {
12 // too little RAM to run from RAM. 12 // too little RAM to run from RAM.
13 feature = "stm32f103c8", // 20 kb 13 feature = "stm32f103c8", // 20 kb
14 feature = "stm32c031c6", // 6 kb 14 feature = "stm32c031c6", // 6 kb
15 feature = "stm32c071rb", // 24 kb
15 feature = "stm32l073rz", // 20 kb 16 feature = "stm32l073rz", // 20 kb
16 feature = "stm32h503rb", // 32 kb 17 feature = "stm32h503rb", // 32 kb
17 // no VTOR, so interrupts can't work when running from RAM 18 // no VTOR, so interrupts can't work when running from RAM
diff --git a/tests/stm32/src/bin/cryp.rs b/tests/stm32/src/bin/cryp.rs
index 028775ac8..f54c99cc3 100644
--- a/tests/stm32/src/bin/cryp.rs
+++ b/tests/stm32/src/bin/cryp.rs
@@ -72,7 +72,7 @@ async fn main(_spawner: Spawner) {
72 defmt::assert!(encrypt_tag == payload_vec[ciphertext.len()..ciphertext.len() + encrypt_tag.len()]); 72 defmt::assert!(encrypt_tag == payload_vec[ciphertext.len()..ciphertext.len() + encrypt_tag.len()]);
73 73
74 // Decrypt in software using AES-GCM 128-bit 74 // Decrypt in software using AES-GCM 128-bit
75 let _ = cipher.decrypt_in_place(&iv.into(), &aad, &mut payload_vec); 75 cipher.decrypt_in_place(&iv.into(), &aad, &mut payload_vec).unwrap();
76 76
77 info!("Test OK"); 77 info!("Test OK");
78 cortex_m::asm::bkpt(); 78 cortex_m::asm::bkpt();
diff --git a/tests/stm32/src/bin/sdmmc.rs b/tests/stm32/src/bin/sdmmc.rs
index 34a53a725..9f9c526e1 100644
--- a/tests/stm32/src/bin/sdmmc.rs
+++ b/tests/stm32/src/bin/sdmmc.rs
@@ -95,6 +95,9 @@ async fn main(_spawner: Spawner) {
95 95
96 drop(s); 96 drop(s);
97 97
98 // FIXME: this hangs on Rust 1.86 and higher.
99 // I haven't been able to figure out why.
100 /*
98 // ======== Try 1bit. ============== 101 // ======== Try 1bit. ==============
99 info!("initializing in 1-bit mode..."); 102 info!("initializing in 1-bit mode...");
100 let mut s = Sdmmc::new_1bit( 103 let mut s = Sdmmc::new_1bit(
@@ -151,6 +154,7 @@ async fn main(_spawner: Spawner) {
151 assert_eq!(&blocks, &patterns); 154 assert_eq!(&blocks, &patterns);
152 155
153 drop(s); 156 drop(s);
157 */
154 158
155 info!("Test OK"); 159 info!("Test OK");
156 cortex_m::asm::bkpt(); 160 cortex_m::asm::bkpt();
diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs
index 829f2cff0..a4d8048ce 100644
--- a/tests/stm32/src/common.rs
+++ b/tests/stm32/src/common.rs
@@ -34,6 +34,8 @@ teleprobe_meta::target!(b"nucleo-stm32u5a5zj");
34teleprobe_meta::target!(b"nucleo-stm32h563zi"); 34teleprobe_meta::target!(b"nucleo-stm32h563zi");
35#[cfg(feature = "stm32c031c6")] 35#[cfg(feature = "stm32c031c6")]
36teleprobe_meta::target!(b"nucleo-stm32c031c6"); 36teleprobe_meta::target!(b"nucleo-stm32c031c6");
37#[cfg(feature = "stm32c071rb")]
38teleprobe_meta::target!(b"nucleo-stm32c071rb");
37#[cfg(feature = "stm32l073rz")] 39#[cfg(feature = "stm32l073rz")]
38teleprobe_meta::target!(b"nucleo-stm32l073rz"); 40teleprobe_meta::target!(b"nucleo-stm32l073rz");
39#[cfg(feature = "stm32l152re")] 41#[cfg(feature = "stm32l152re")]
@@ -186,6 +188,12 @@ define_peris!(
186 SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2, 188 SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2,
187 @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;}, 189 @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
188); 190);
191#[cfg(feature = "stm32c071rb")]
192define_peris!(
193 UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2,
194 SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2,
195 @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
196);
189#[cfg(feature = "stm32l496zg")] 197#[cfg(feature = "stm32l496zg")]
190define_peris!( 198define_peris!(
191 UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = DMA1_CH2, UART_RX_DMA = DMA1_CH3, 199 UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = DMA1_CH2, UART_RX_DMA = DMA1_CH3,
@@ -271,7 +279,7 @@ pub fn config() -> Config {
271 #[allow(unused_mut)] 279 #[allow(unused_mut)]
272 let mut config = Config::default(); 280 let mut config = Config::default();
273 281
274 #[cfg(feature = "stm32c031c6")] 282 #[cfg(any(feature = "stm32c031c6", feature = "stm32c071rb"))]
275 { 283 {
276 config.rcc.hsi = Some(Hsi { 284 config.rcc.hsi = Some(Hsi {
277 sys_div: HsiSysDiv::DIV1, // 48Mhz 285 sys_div: HsiSysDiv::DIV1, // 48Mhz