aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorOlof <[email protected]>2024-12-18 01:48:25 +0100
committerGitHub <[email protected]>2024-12-18 01:48:25 +0100
commit7cf96e4730964d085015320648c870a05fbaf431 (patch)
tree04072529b62082cb66443377b589fe08169f83be /examples
parent8678911028a591d72fd1d8418407b5885ed4c417 (diff)
parent341036a8b865609767fbf9015b482ea70ed4f23f (diff)
Merge branch 'embassy-rs:main' into u5_adc
Diffstat (limited to 'examples')
-rw-r--r--examples/boot/application/nrf/Cargo.toml4
-rw-r--r--examples/boot/application/rp/Cargo.toml4
-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.toml4
-rw-r--r--examples/boot/application/stm32wl/Cargo.toml4
-rw-r--r--examples/boot/bootloader/nrf/Cargo.toml2
-rw-r--r--examples/boot/bootloader/nrf/src/main.rs6
-rw-r--r--examples/boot/bootloader/rp/Cargo.toml2
-rw-r--r--examples/boot/bootloader/stm32-dual-bank/Cargo.toml2
-rw-r--r--examples/boot/bootloader/stm32/Cargo.toml2
-rw-r--r--examples/boot/bootloader/stm32wb-dfu/Cargo.toml2
-rw-r--r--examples/boot/bootloader/stm32wb-dfu/README.md32
-rw-r--r--examples/boot/bootloader/stm32wb-dfu/src/main.rs17
-rw-r--r--examples/lpc55s69/.cargo/config.toml8
-rw-r--r--examples/lpc55s69/Cargo.toml22
-rw-r--r--examples/lpc55s69/build.rs35
-rw-r--r--examples/lpc55s69/memory.x28
-rw-r--r--examples/lpc55s69/src/bin/blinky_nop.rs33
-rw-r--r--examples/lpc55s69/src/bin/button_executor.rs25
-rw-r--r--examples/nrf-rtos-trace/Cargo.toml4
-rw-r--r--examples/nrf51/Cargo.toml2
-rw-r--r--examples/nrf52810/Cargo.toml4
-rw-r--r--examples/nrf52840-rtic/Cargo.toml5
-rw-r--r--examples/nrf52840-rtic/src/bin/blinky.rs2
-rw-r--r--examples/nrf52840/Cargo.toml6
-rw-r--r--examples/nrf52840/src/bin/buffered_uart.rs2
-rw-r--r--examples/nrf52840/src/bin/multiprio.rs16
-rw-r--r--examples/nrf52840/src/bin/nfct.rs79
-rw-r--r--examples/nrf52840/src/bin/spis.rs2
-rw-r--r--examples/nrf52840/src/bin/twim.rs2
-rw-r--r--examples/nrf52840/src/bin/twim_lowpower.rs2
-rw-r--r--examples/nrf52840/src/bin/twis.rs2
-rw-r--r--examples/nrf52840/src/bin/uart.rs2
-rw-r--r--examples/nrf52840/src/bin/uart_idle.rs2
-rw-r--r--examples/nrf52840/src/bin/uart_split.rs2
-rw-r--r--examples/nrf52840/src/bin/usb_ethernet.rs9
-rw-r--r--examples/nrf52840/src/bin/usb_hid_keyboard.rs8
-rw-r--r--examples/nrf52840/src/bin/usb_hid_mouse.rs9
-rw-r--r--examples/nrf52840/src/bin/usb_serial.rs9
-rw-r--r--examples/nrf52840/src/bin/usb_serial_multitask.rs9
-rw-r--r--examples/nrf52840/src/bin/usb_serial_winusb.rs9
-rw-r--r--examples/nrf52840/src/bin/wdt.rs4
-rw-r--r--examples/nrf5340/Cargo.toml6
-rw-r--r--examples/nrf54l15/.cargo/config.toml9
-rw-r--r--examples/nrf54l15/Cargo.toml20
-rw-r--r--examples/nrf54l15/build.rs35
-rw-r--r--examples/nrf54l15/memory.x5
-rw-r--r--examples/nrf54l15/src/bin/blinky.rs23
-rw-r--r--examples/nrf9151/ns/Cargo.toml2
-rw-r--r--examples/nrf9151/ns/src/bin/uart.rs2
-rw-r--r--examples/nrf9151/s/Cargo.toml2
-rw-r--r--examples/nrf9160/Cargo.toml4
-rw-r--r--examples/nrf9160/src/bin/modem_tcp_client.rs15
-rw-r--r--examples/rp/Cargo.toml19
-rw-r--r--examples/rp/src/bin/pio_hd44780.rs201
-rw-r--r--examples/rp/src/bin/pio_i2s.rs71
-rw-r--r--examples/rp/src/bin/pio_onewire.rs98
-rw-r--r--examples/rp/src/bin/pio_pwm.rs90
-rw-r--r--examples/rp/src/bin/pio_rotary_encoder.rs83
-rw-r--r--examples/rp/src/bin/pio_servo.rs96
-rw-r--r--examples/rp/src/bin/pio_stepper.rs135
-rw-r--r--examples/rp/src/bin/pio_uart.rs223
-rw-r--r--examples/rp/src/bin/pio_ws2812.rs105
-rw-r--r--examples/rp/src/bin/pwm.rs62
-rw-r--r--examples/rp/src/bin/spi_display.rs165
-rw-r--r--examples/rp/src/bin/spi_gc9a01.rs126
-rw-r--r--examples/rp/src/bin/spi_sdmmc.rs3
-rw-r--r--examples/rp/src/bin/usb_serial_with_handler.rs64
-rw-r--r--examples/rp/src/bin/wifi_blinky.rs4
-rw-r--r--examples/rp/src/bin/wifi_scan.rs5
-rw-r--r--examples/rp23/Cargo.toml19
-rw-r--r--examples/rp23/memory.x1
-rw-r--r--examples/rp23/src/bin/adc.rs12
-rw-r--r--examples/rp23/src/bin/adc_dma.rs10
-rw-r--r--examples/rp23/src/bin/assign_resources.rs10
-rw-r--r--examples/rp23/src/bin/blinky.rs11
-rw-r--r--examples/rp23/src/bin/blinky_two_channels.rs10
-rw-r--r--examples/rp23/src/bin/blinky_two_tasks.rs10
-rw-r--r--examples/rp23/src/bin/button.rs10
-rw-r--r--examples/rp23/src/bin/debounce.rs10
-rw-r--r--examples/rp23/src/bin/flash.rs10
-rw-r--r--examples/rp23/src/bin/gpio_async.rs10
-rw-r--r--examples/rp23/src/bin/gpout.rs10
-rw-r--r--examples/rp23/src/bin/i2c_async.rs10
-rw-r--r--examples/rp23/src/bin/i2c_async_embassy.rs10
-rw-r--r--examples/rp23/src/bin/i2c_blocking.rs10
-rw-r--r--examples/rp23/src/bin/i2c_slave.rs10
-rw-r--r--examples/rp23/src/bin/interrupt.rs10
-rw-r--r--examples/rp23/src/bin/multicore.rs10
-rw-r--r--examples/rp23/src/bin/multiprio.rs10
-rw-r--r--examples/rp23/src/bin/otp.rs10
-rw-r--r--examples/rp23/src/bin/pio_async.rs10
-rw-r--r--examples/rp23/src/bin/pio_dma.rs10
-rw-r--r--examples/rp23/src/bin/pio_hd44780.rs211
-rw-r--r--examples/rp23/src/bin/pio_i2s.rs88
-rw-r--r--examples/rp23/src/bin/pio_onewire.rs88
-rw-r--r--examples/rp23/src/bin/pio_pwm.rs100
-rw-r--r--examples/rp23/src/bin/pio_rotary_encoder.rs93
-rw-r--r--examples/rp23/src/bin/pio_servo.rs106
-rw-r--r--examples/rp23/src/bin/pio_stepper.rs145
-rw-r--r--examples/rp23/src/bin/pio_uart.rs202
-rw-r--r--examples/rp23/src/bin/pio_ws2812.rs115
-rw-r--r--examples/rp23/src/bin/pwm.rs74
-rw-r--r--examples/rp23/src/bin/pwm_input.rs10
-rw-r--r--examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs110
-rw-r--r--examples/rp23/src/bin/rosc.rs10
-rw-r--r--examples/rp23/src/bin/shared_bus.rs10
-rw-r--r--examples/rp23/src/bin/sharing.rs10
-rw-r--r--examples/rp23/src/bin/spi.rs10
-rw-r--r--examples/rp23/src/bin/spi_async.rs10
-rw-r--r--examples/rp23/src/bin/spi_display.rs173
-rw-r--r--examples/rp23/src/bin/spi_sdmmc.rs12
-rw-r--r--examples/rp23/src/bin/trng.rs10
-rw-r--r--examples/rp23/src/bin/uart.rs10
-rw-r--r--examples/rp23/src/bin/uart_buffered_split.rs10
-rw-r--r--examples/rp23/src/bin/uart_r503.rs10
-rw-r--r--examples/rp23/src/bin/uart_unidir.rs10
-rw-r--r--examples/rp23/src/bin/usb_hid_keyboard.rs193
-rw-r--r--examples/rp23/src/bin/usb_webusb.rs10
-rw-r--r--examples/rp23/src/bin/watchdog.rs10
-rw-r--r--examples/rp23/src/bin/zerocopy.rs10
-rw-r--r--examples/std/Cargo.toml6
-rw-r--r--examples/std/src/bin/net_ppp.rs6
-rw-r--r--examples/stm32c0/Cargo.toml4
-rw-r--r--examples/stm32f0/Cargo.toml6
-rw-r--r--examples/stm32f1/Cargo.toml4
-rw-r--r--examples/stm32f2/Cargo.toml4
-rw-r--r--examples/stm32f3/Cargo.toml6
-rw-r--r--examples/stm32f3/README.md24
-rw-r--r--examples/stm32f3/src/bin/blocking-tsc.rs98
-rw-r--r--examples/stm32f3/src/bin/tsc_blocking.rs138
-rw-r--r--examples/stm32f3/src/bin/tsc_multipin.rs204
-rw-r--r--examples/stm32f334/Cargo.toml4
-rw-r--r--examples/stm32f334/src/bin/pwm.rs4
-rw-r--r--examples/stm32f4/Cargo.toml9
-rw-r--r--examples/stm32f4/src/bin/i2s_dma.rs13
-rw-r--r--examples/stm32f4/src/bin/pwm.rs19
-rw-r--r--examples/stm32f4/src/bin/usb_uac_speaker.rs390
-rw-r--r--examples/stm32f4/src/bin/ws2812_pwm.rs4
-rw-r--r--examples/stm32f469/Cargo.toml2
-rw-r--r--examples/stm32f7/Cargo.toml6
-rw-r--r--examples/stm32f7/src/bin/qspi.rs4
-rw-r--r--examples/stm32g0/Cargo.toml4
-rw-r--r--examples/stm32g0/src/bin/input_capture.rs8
-rw-r--r--examples/stm32g0/src/bin/pwm_input.rs10
-rw-r--r--examples/stm32g4/Cargo.toml4
-rw-r--r--examples/stm32g4/src/bin/adc_dma.rs60
-rw-r--r--examples/stm32g4/src/bin/pwm.rs19
-rw-r--r--examples/stm32h5/Cargo.toml8
-rw-r--r--examples/stm32h5/src/bin/adc.rs59
-rw-r--r--examples/stm32h5/src/bin/usb_uac_speaker.rs381
-rw-r--r--examples/stm32h7/Cargo.toml8
-rw-r--r--examples/stm32h7/src/bin/eth_client.rs4
-rw-r--r--examples/stm32h7/src/bin/eth_client_mii.rs4
-rw-r--r--examples/stm32h7/src/bin/i2c_shared.rs6
-rw-r--r--examples/stm32h7/src/bin/pwm.rs19
-rw-r--r--examples/stm32h7/src/bin/sai.rs13
-rw-r--r--examples/stm32h7/src/bin/spi_bdma.rs7
-rw-r--r--examples/stm32h723/.cargo/config.toml8
-rw-r--r--examples/stm32h723/Cargo.toml69
-rw-r--r--examples/stm32h723/build.rs35
-rw-r--r--examples/stm32h723/memory.x106
-rw-r--r--examples/stm32h723/src/bin/spdifrx.rs165
-rw-r--r--examples/stm32h735/Cargo.toml4
-rw-r--r--examples/stm32h755cm4/Cargo.toml8
-rw-r--r--examples/stm32h755cm7/Cargo.toml8
-rw-r--r--examples/stm32h7b0/.cargo/config.toml8
-rw-r--r--examples/stm32h7b0/Cargo.toml74
-rw-r--r--examples/stm32h7b0/build.rs35
-rw-r--r--examples/stm32h7b0/memory.x5
-rw-r--r--examples/stm32h7b0/src/bin/ospi_memory_mapped.rs433
-rw-r--r--examples/stm32h7rs/Cargo.toml8
-rw-r--r--examples/stm32l0/.cargo/config.toml2
-rw-r--r--examples/stm32l0/Cargo.toml6
-rw-r--r--examples/stm32l0/README.md24
-rw-r--r--examples/stm32l0/src/bin/async-tsc.rs122
-rw-r--r--examples/stm32l0/src/bin/blocking-tsc.rs116
-rw-r--r--examples/stm32l0/src/bin/tsc_async.rs116
-rw-r--r--examples/stm32l0/src/bin/tsc_blocking.rs142
-rw-r--r--examples/stm32l0/src/bin/tsc_multipin.rs209
-rw-r--r--examples/stm32l1/Cargo.toml4
-rw-r--r--examples/stm32l1/src/bin/usart.rs37
-rw-r--r--examples/stm32l4/.cargo/config.toml3
-rw-r--r--examples/stm32l4/Cargo.toml8
-rw-r--r--examples/stm32l4/README.md24
-rw-r--r--examples/stm32l4/src/bin/spe_adin1110_http_server.rs2
-rw-r--r--examples/stm32l4/src/bin/tsc_async.rs108
-rw-r--r--examples/stm32l4/src/bin/tsc_blocking.rs147
-rw-r--r--examples/stm32l4/src/bin/tsc_multipin.rs198
-rw-r--r--examples/stm32l5/Cargo.toml6
-rw-r--r--examples/stm32u0/Cargo.toml4
-rw-r--r--examples/stm32u5/.cargo/config.toml4
-rw-r--r--examples/stm32u5/Cargo.toml10
-rw-r--r--examples/stm32u5/src/bin/ferris.bmpbin0 -> 6794 bytes
-rw-r--r--examples/stm32u5/src/bin/i2c.rs2
-rw-r--r--examples/stm32u5/src/bin/ltdc.rs461
-rw-r--r--examples/stm32u5/src/bin/tsc.rs75
-rw-r--r--examples/stm32u5/src/bin/usb_hs_serial.rs129
-rw-r--r--examples/stm32u5/src/bin/usb_serial.rs4
-rw-r--r--examples/stm32wb/Cargo.toml6
-rw-r--r--examples/stm32wba/Cargo.toml6
-rw-r--r--examples/stm32wl/Cargo.toml4
-rw-r--r--examples/wasm/Cargo.toml4
208 files changed, 5632 insertions, 3089 deletions
diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml
index 93e49faef..45ad341fc 100644
--- a/examples/boot/application/nrf/Cargo.toml
+++ b/examples/boot/application/nrf/Cargo.toml
@@ -5,8 +5,8 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } 9embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] } 10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] }
11embassy-nrf = { version = "0.2.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } 11embassy-nrf = { version = "0.2.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] }
12embassy-boot = { version = "0.3.0", path = "../../../../embassy-boot", features = [] } 12embassy-boot = { version = "0.3.0", path = "../../../../embassy-boot", features = [] }
diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml
index 8bb8afdfe..ec99f2605 100644
--- a/examples/boot/application/rp/Cargo.toml
+++ b/examples/boot/application/rp/Cargo.toml
@@ -5,8 +5,8 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } 9embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] } 10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] }
11embassy-rp = { version = "0.2.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } 11embassy-rp = { version = "0.2.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] }
12embassy-boot-rp = { version = "0.3.0", path = "../../../../embassy-boot-rp", features = [] } 12embassy-boot-rp = { version = "0.3.0", path = "../../../../embassy-boot-rp", features = [] }
diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml
index 1c2934298..d2138db87 100644
--- a/examples/boot/application/stm32f3/Cargo.toml
+++ b/examples/boot/application/stm32f3/Cargo.toml
@@ -5,8 +5,8 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } 9embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } 10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f303re", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f303re", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32" } 12embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32" }
diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml
index 09e34c7df..b86c66f5d 100644
--- a/examples/boot/application/stm32f7/Cargo.toml
+++ b/examples/boot/application/stm32f7/Cargo.toml
@@ -5,8 +5,8 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } 9embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } 10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } 12embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml
index 5e7f4d5e7..e2e2fe711 100644
--- a/examples/boot/application/stm32h7/Cargo.toml
+++ b/examples/boot/application/stm32h7/Cargo.toml
@@ -5,8 +5,8 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } 9embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } 10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32h743zi", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32h743zi", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } 12embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml
index 60fdcfafb..7e9c52ffa 100644
--- a/examples/boot/application/stm32l0/Cargo.toml
+++ b/examples/boot/application/stm32l0/Cargo.toml
@@ -5,8 +5,8 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } 9embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } 10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l072cz", "time-driver-any", "exti", "memory-x"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l072cz", "time-driver-any", "exti", "memory-x"] }
12embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } 12embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml
index fe3ab2c04..42353a24c 100644
--- a/examples/boot/application/stm32l1/Cargo.toml
+++ b/examples/boot/application/stm32l1/Cargo.toml
@@ -5,8 +5,8 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } 9embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } 10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l151cb-a", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l151cb-a", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } 12embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml
index 169856358..cf0b0242a 100644
--- a/examples/boot/application/stm32l4/Cargo.toml
+++ b/examples/boot/application/stm32l4/Cargo.toml
@@ -5,8 +5,8 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } 9embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } 10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } 12embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
diff --git a/examples/boot/application/stm32wb-dfu/Cargo.toml b/examples/boot/application/stm32wb-dfu/Cargo.toml
index 7cef8fe0d..ea2879fb5 100644
--- a/examples/boot/application/stm32wb-dfu/Cargo.toml
+++ b/examples/boot/application/stm32wb-dfu/Cargo.toml
@@ -5,8 +5,8 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } 9embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } 10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wb55rg", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wb55rg", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } 12embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml
index 860a835a9..6417b8430 100644
--- a/examples/boot/application/stm32wl/Cargo.toml
+++ b/examples/boot/application/stm32wl/Cargo.toml
@@ -5,8 +5,8 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } 9embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } 10embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wl55jc-cm4", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wl55jc-cm4", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } 12embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml
index 9d5d51a13..c2e8bbe53 100644
--- a/examples/boot/bootloader/nrf/Cargo.toml
+++ b/examples/boot/bootloader/nrf/Cargo.toml
@@ -12,7 +12,7 @@ defmt-rtt = { version = "0.4", optional = true }
12embassy-nrf = { path = "../../../../embassy-nrf", features = [] } 12embassy-nrf = { path = "../../../../embassy-nrf", features = [] }
13embassy-boot-nrf = { path = "../../../../embassy-boot-nrf" } 13embassy-boot-nrf = { path = "../../../../embassy-boot-nrf" }
14cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } 14cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
15embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } 15embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
16cortex-m-rt = { version = "0.7" } 16cortex-m-rt = { version = "0.7" }
17cfg-if = "1.0.0" 17cfg-if = "1.0.0"
18 18
diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs
index 67c700437..b849a0df3 100644
--- a/examples/boot/bootloader/nrf/src/main.rs
+++ b/examples/boot/bootloader/nrf/src/main.rs
@@ -8,7 +8,7 @@ use cortex_m_rt::{entry, exception};
8use defmt_rtt as _; 8use defmt_rtt as _;
9use embassy_boot_nrf::*; 9use embassy_boot_nrf::*;
10use embassy_nrf::nvmc::Nvmc; 10use embassy_nrf::nvmc::Nvmc;
11use embassy_nrf::wdt; 11use embassy_nrf::wdt::{self, HaltConfig, SleepConfig};
12use embassy_sync::blocking_mutex::Mutex; 12use embassy_sync::blocking_mutex::Mutex;
13 13
14#[entry] 14#[entry]
@@ -25,8 +25,8 @@ fn main() -> ! {
25 25
26 let mut wdt_config = wdt::Config::default(); 26 let mut wdt_config = wdt::Config::default();
27 wdt_config.timeout_ticks = 32768 * 5; // timeout seconds 27 wdt_config.timeout_ticks = 32768 * 5; // timeout seconds
28 wdt_config.run_during_sleep = true; 28 wdt_config.action_during_sleep = SleepConfig::RUN;
29 wdt_config.run_during_debug_halt = false; 29 wdt_config.action_during_debug_halt = HaltConfig::PAUSE;
30 30
31 let flash = WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config); 31 let flash = WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config);
32 let flash = Mutex::new(RefCell::new(flash)); 32 let flash = Mutex::new(RefCell::new(flash));
diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml
index 9df396e5e..24df3da82 100644
--- a/examples/boot/bootloader/rp/Cargo.toml
+++ b/examples/boot/bootloader/rp/Cargo.toml
@@ -11,7 +11,7 @@ defmt-rtt = { version = "0.4", optional = true }
11 11
12embassy-rp = { path = "../../../../embassy-rp", features = ["rp2040"] } 12embassy-rp = { path = "../../../../embassy-rp", features = ["rp2040"] }
13embassy-boot-rp = { path = "../../../../embassy-boot-rp" } 13embassy-boot-rp = { path = "../../../../embassy-boot-rp" }
14embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } 14embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
15embassy-time = { path = "../../../../embassy-time", features = [] } 15embassy-time = { path = "../../../../embassy-time", features = [] }
16 16
17cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } 17cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
diff --git a/examples/boot/bootloader/stm32-dual-bank/Cargo.toml b/examples/boot/bootloader/stm32-dual-bank/Cargo.toml
index b91b05412..81e0026c6 100644
--- a/examples/boot/bootloader/stm32-dual-bank/Cargo.toml
+++ b/examples/boot/bootloader/stm32-dual-bank/Cargo.toml
@@ -15,7 +15,7 @@ cortex-m = { version = "0.7.6", features = [
15 "inline-asm", 15 "inline-asm",
16 "critical-section-single-core", 16 "critical-section-single-core",
17] } 17] }
18embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } 18embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
19cortex-m-rt = { version = "0.7" } 19cortex-m-rt = { version = "0.7" }
20embedded-storage = "0.3.1" 20embedded-storage = "0.3.1"
21embedded-storage-async = "0.4.0" 21embedded-storage-async = "0.4.0"
diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml
index 541186949..f35e4e713 100644
--- a/examples/boot/bootloader/stm32/Cargo.toml
+++ b/examples/boot/bootloader/stm32/Cargo.toml
@@ -12,7 +12,7 @@ defmt-rtt = { version = "0.4", optional = true }
12embassy-stm32 = { path = "../../../../embassy-stm32", features = [] } 12embassy-stm32 = { path = "../../../../embassy-stm32", features = [] }
13embassy-boot-stm32 = { path = "../../../../embassy-boot-stm32" } 13embassy-boot-stm32 = { path = "../../../../embassy-boot-stm32" }
14cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } 14cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
15embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } 15embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
16cortex-m-rt = { version = "0.7" } 16cortex-m-rt = { version = "0.7" }
17embedded-storage = "0.3.1" 17embedded-storage = "0.3.1"
18embedded-storage-async = "0.4.0" 18embedded-storage-async = "0.4.0"
diff --git a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml
index 050b672ce..1431e7cc3 100644
--- a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml
+++ b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml
@@ -12,7 +12,7 @@ defmt-rtt = { version = "0.4", optional = true }
12embassy-stm32 = { path = "../../../../embassy-stm32", features = [] } 12embassy-stm32 = { path = "../../../../embassy-stm32", features = [] }
13embassy-boot-stm32 = { path = "../../../../embassy-boot-stm32" } 13embassy-boot-stm32 = { path = "../../../../embassy-boot-stm32" }
14cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } 14cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
15embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } 15embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
16cortex-m-rt = { version = "0.7" } 16cortex-m-rt = { version = "0.7" }
17embedded-storage = "0.3.1" 17embedded-storage = "0.3.1"
18embedded-storage-async = "0.4.0" 18embedded-storage-async = "0.4.0"
diff --git a/examples/boot/bootloader/stm32wb-dfu/README.md b/examples/boot/bootloader/stm32wb-dfu/README.md
index d5c6ea57c..3c5f268a0 100644
--- a/examples/boot/bootloader/stm32wb-dfu/README.md
+++ b/examples/boot/bootloader/stm32wb-dfu/README.md
@@ -1,11 +1,37 @@
1# Bootloader for STM32 1# Bootloader for STM32
2 2
3The bootloader uses `embassy-boot` to interact with the flash. 3This bootloader implementation uses `embassy-boot` and `embassy-usb-dfu` to manage firmware updates and interact with the flash memory on STM32WB55 devices.
4 4
5# Usage 5## Prerequisites
6 6
7Flash the bootloader 7- Rust toolchain with `cargo` installed
8- `cargo-flash` for flashing the bootloader
9- `dfu-util` for firmware updates
10- `cargo-binutils` for binary generation
11
12## Usage
13
14### 1. Flash the Bootloader
15
16First, flash the bootloader to your device:
8 17
9``` 18```
10cargo flash --features embassy-stm32/stm32wb55rg --release --chip STM32WB55RGVx 19cargo flash --features embassy-stm32/stm32wb55rg --release --chip STM32WB55RGVx
11``` 20```
21
22### 2. Build and Flash Application
23
24Generate your application binary and flash it using DFU:
25
26```
27cargo objcopy --release -- -O binary fw.bin
28dfu-util -d c0de:cafe -w -D fw.bin
29```
30
31## Troubleshooting
32
33- Make sure your device is in DFU mode before flashing
34- Verify the USB VID:PID matches your device (c0de:cafe)
35- Check USB connections if the device is not detected
36- Make sure the transfer size option of `dfu-util` matches the bootloader configuration. By default, `dfu-util` will use the transfer size reported by the device, but you can override it with the `-t` option if needed.
37- Make sure `control_buf` size is larger than or equal to the `usb_dfu` `BLOCK_SIZE` parameter (in this example, both are set to 4096 bytes).
diff --git a/examples/boot/bootloader/stm32wb-dfu/src/main.rs b/examples/boot/bootloader/stm32wb-dfu/src/main.rs
index 093b39f9d..b09d53cf0 100644
--- a/examples/boot/bootloader/stm32wb-dfu/src/main.rs
+++ b/examples/boot/bootloader/stm32wb-dfu/src/main.rs
@@ -12,7 +12,7 @@ use embassy_stm32::rcc::WPAN_DEFAULT;
12use embassy_stm32::usb::Driver; 12use embassy_stm32::usb::Driver;
13use embassy_stm32::{bind_interrupts, peripherals, usb}; 13use embassy_stm32::{bind_interrupts, peripherals, usb};
14use embassy_sync::blocking_mutex::Mutex; 14use embassy_sync::blocking_mutex::Mutex;
15use embassy_usb::Builder; 15use embassy_usb::{msos, Builder};
16use embassy_usb_dfu::consts::DfuAttributes; 16use embassy_usb_dfu::consts::DfuAttributes;
17use embassy_usb_dfu::{usb_dfu, Control, ResetImmediate}; 17use embassy_usb_dfu::{usb_dfu, Control, ResetImmediate};
18 18
@@ -20,6 +20,9 @@ bind_interrupts!(struct Irqs {
20 USB_LP => usb::InterruptHandler<peripherals::USB>; 20 USB_LP => usb::InterruptHandler<peripherals::USB>;
21}); 21});
22 22
23// This is a randomly generated GUID to allow clients on Windows to find our device
24const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321}"];
25
23#[entry] 26#[entry]
24fn main() -> ! { 27fn main() -> ! {
25 let mut config = embassy_stm32::Config::default(); 28 let mut config = embassy_stm32::Config::default();
@@ -62,6 +65,18 @@ fn main() -> ! {
62 &mut control_buf, 65 &mut control_buf,
63 ); 66 );
64 67
68 // We add MSOS headers so that the device automatically gets assigned the WinUSB driver on Windows.
69 // Otherwise users need to do this manually using a tool like Zadig.
70 //
71 // It seems it is important for the DFU class that these headers be on the Device level.
72 //
73 builder.msos_descriptor(msos::windows_version::WIN8_1, 2);
74 builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
75 builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
76 "DeviceInterfaceGUIDs",
77 msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
78 ));
79
65 usb_dfu::<_, _, _, ResetImmediate, 4096>(&mut builder, &mut state); 80 usb_dfu::<_, _, _, ResetImmediate, 4096>(&mut builder, &mut state);
66 81
67 let mut dev = builder.build(); 82 let mut dev = builder.build();
diff --git a/examples/lpc55s69/.cargo/config.toml b/examples/lpc55s69/.cargo/config.toml
new file mode 100644
index 000000000..9556de72f
--- /dev/null
+++ b/examples/lpc55s69/.cargo/config.toml
@@ -0,0 +1,8 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2runner = "probe-rs run --chip LPC55S69JBD100"
3
4[build]
5target = "thumbv8m.main-none-eabihf"
6
7[env]
8DEFMT_LOG = "debug"
diff --git a/examples/lpc55s69/Cargo.toml b/examples/lpc55s69/Cargo.toml
new file mode 100644
index 000000000..41a88f082
--- /dev/null
+++ b/examples/lpc55s69/Cargo.toml
@@ -0,0 +1,22 @@
1[package]
2edition = "2021"
3name = "embassy-nxp-lpc55s69-examples"
4version = "0.1.0"
5license = "MIT OR Apache-2.0"
6
7
8[dependencies]
9embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["rt"] }
10embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt"] }
11embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
12embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt"] }
13panic-halt = "0.2.0"
14cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
15cortex-m-rt = {version = "0.7.0"}
16defmt = "0.3"
17defmt-rtt = "0.4"
18panic-probe = { version = "0.3.2", features = ["print-defmt"] }
19panic-semihosting = "0.6.0"
20
21[profile.release]
22debug = 2
diff --git a/examples/lpc55s69/build.rs b/examples/lpc55s69/build.rs
new file mode 100644
index 000000000..30691aa97
--- /dev/null
+++ b/examples/lpc55s69/build.rs
@@ -0,0 +1,35 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
35}
diff --git a/examples/lpc55s69/memory.x b/examples/lpc55s69/memory.x
new file mode 100644
index 000000000..1483b2fad
--- /dev/null
+++ b/examples/lpc55s69/memory.x
@@ -0,0 +1,28 @@
1/* File originally from lpc55-hal repo: https://github.com/lpc55/lpc55-hal/blob/main/memory.x */
2MEMORY
3{
4 FLASH : ORIGIN = 0x00000000, LENGTH = 512K
5
6 /* for use with standard link.x */
7 RAM : ORIGIN = 0x20000000, LENGTH = 256K
8
9 /* would be used with proper link.x */
10 /* needs changes to r0 (initialization code) */
11 /* SRAM0 : ORIGIN = 0x20000000, LENGTH = 64K */
12 /* SRAM1 : ORIGIN = 0x20010000, LENGTH = 64K */
13 /* SRAM2 : ORIGIN = 0x20020000, LENGTH = 64K */
14 /* SRAM3 : ORIGIN = 0x20030000, LENGTH = 64K */
15
16 /* CASPER SRAM regions */
17 /* SRAMX0: ORIGIN = 0x1400_0000, LENGTH = 4K /1* to 0x1400_0FFF *1/ */
18 /* SRAMX1: ORIGIN = 0x1400_4000, LENGTH = 4K /1* to 0x1400_4FFF *1/ */
19
20 /* USB1 SRAM regin */
21 /* USB1_SRAM : ORIGIN = 0x40100000, LENGTH = 16K */
22
23 /* To define our own USB RAM section in one regular */
24 /* RAM, probably easiest to shorten length of RAM */
25 /* above, and use this freed RAM section */
26
27}
28
diff --git a/examples/lpc55s69/src/bin/blinky_nop.rs b/examples/lpc55s69/src/bin/blinky_nop.rs
new file mode 100644
index 000000000..58e2d9808
--- /dev/null
+++ b/examples/lpc55s69/src/bin/blinky_nop.rs
@@ -0,0 +1,33 @@
1//! This example has been made with the LPCXpresso55S69 board in mind, which has a built-in LED on PIO1_6.
2
3#![no_std]
4#![no_main]
5
6use cortex_m::asm::nop;
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_nxp::gpio::{Level, Output};
10use {defmt_rtt as _, panic_halt as _};
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 let p = embassy_nxp::init(Default::default());
15
16 let mut led = Output::new(p.PIO1_6, Level::Low);
17
18 loop {
19 info!("led off!");
20 led.set_high();
21
22 for _ in 0..200_000 {
23 nop();
24 }
25
26 info!("led on!");
27 led.set_low();
28
29 for _ in 0..200_000 {
30 nop();
31 }
32 }
33}
diff --git a/examples/lpc55s69/src/bin/button_executor.rs b/examples/lpc55s69/src/bin/button_executor.rs
new file mode 100644
index 000000000..836b1c9eb
--- /dev/null
+++ b/examples/lpc55s69/src/bin/button_executor.rs
@@ -0,0 +1,25 @@
1//! This example has been made with the LPCXpresso55S69 board in mind, which has a built-in LED on
2//! PIO1_6 and a button (labeled "user") on PIO1_9.
3
4#![no_std]
5#![no_main]
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_nxp::gpio::{Input, Level, Output, Pull};
10use {defmt_rtt as _, panic_halt as _};
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) -> ! {
14 let p = embassy_nxp::init(Default::default());
15
16 let mut led = Output::new(p.PIO1_6, Level::Low);
17 let mut button = Input::new(p.PIO1_9, Pull::Up);
18
19 info!("Entered main loop");
20 loop {
21 button.wait_for_rising_edge().await;
22 info!("Button pressed");
23 led.toggle();
24 }
25}
diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml
index 98a678815..6d13d668a 100644
--- a/examples/nrf-rtos-trace/Cargo.toml
+++ b/examples/nrf-rtos-trace/Cargo.toml
@@ -15,8 +15,8 @@ log = [
15] 15]
16 16
17[dependencies] 17[dependencies]
18embassy-sync = { version = "0.6.0", path = "../../embassy-sync" } 18embassy-sync = { version = "0.6.1", path = "../../embassy-sync" }
19embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "integrated-timers"] } 19embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace"] }
20embassy-time = { version = "0.3.2", path = "../../embassy-time" } 20embassy-time = { version = "0.3.2", path = "../../embassy-time" }
21embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } 21embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
22 22
diff --git a/examples/nrf51/Cargo.toml b/examples/nrf51/Cargo.toml
index 93a19bea7..8d995cfd8 100644
--- a/examples/nrf51/Cargo.toml
+++ b/examples/nrf51/Cargo.toml
@@ -5,7 +5,7 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 8embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
9embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 9embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
10embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } 10embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] }
11 11
diff --git a/examples/nrf52810/Cargo.toml b/examples/nrf52810/Cargo.toml
index 0e3e81c3f..fa2a27aaa 100644
--- a/examples/nrf52810/Cargo.toml
+++ b/examples/nrf52810/Cargo.toml
@@ -6,8 +6,8 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 8embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
9embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 9embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
10embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 10embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
11embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 11embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
12embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 12embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
13 13
diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml
index 7fae7aefc..6b15b24da 100644
--- a/examples/nrf52840-rtic/Cargo.toml
+++ b/examples/nrf52840-rtic/Cargo.toml
@@ -8,8 +8,9 @@ license = "MIT OR Apache-2.0"
8rtic = { version = "2", features = ["thumbv7-backend"] } 8rtic = { version = "2", features = ["thumbv7-backend"] }
9 9
10embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 10embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
11embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 11embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime", "generic-queue"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime"] }
13embassy-time-queue-driver = { version = "0.1.0", path = "../../embassy-time-queue-driver", features = ["generic-queue-8"] }
13embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 14embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
14 15
15defmt = "0.3" 16defmt = "0.3"
diff --git a/examples/nrf52840-rtic/src/bin/blinky.rs b/examples/nrf52840-rtic/src/bin/blinky.rs
index 060bb9ebc..5a074ea17 100644
--- a/examples/nrf52840-rtic/src/bin/blinky.rs
+++ b/examples/nrf52840-rtic/src/bin/blinky.rs
@@ -4,7 +4,7 @@
4 4
5use {defmt_rtt as _, panic_probe as _}; 5use {defmt_rtt as _, panic_probe as _};
6 6
7#[rtic::app(device = embassy_nrf, peripherals = false, dispatchers = [SWI0_EGU0, SWI1_EGU1])] 7#[rtic::app(device = embassy_nrf, peripherals = false, dispatchers = [EGU0_SWI0, EGU1_SWI1])]
8mod app { 8mod app {
9 use defmt::info; 9 use defmt::info;
10 use embassy_nrf::gpio::{Level, Output, OutputDrive}; 10 use embassy_nrf::gpio::{Level, Output, OutputDrive};
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml
index 17fa6234d..fa29d52b9 100644
--- a/examples/nrf52840/Cargo.toml
+++ b/examples/nrf52840/Cargo.toml
@@ -6,11 +6,11 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 8embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
9embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 9embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
10embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 10embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
11embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 11embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
12embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 12embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
13embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } 13embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] }
14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.3.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"] }
diff --git a/examples/nrf52840/src/bin/buffered_uart.rs b/examples/nrf52840/src/bin/buffered_uart.rs
index 6ac72bcaf..77d017964 100644
--- a/examples/nrf52840/src/bin/buffered_uart.rs
+++ b/examples/nrf52840/src/bin/buffered_uart.rs
@@ -9,7 +9,7 @@ use embedded_io_async::Write;
9use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
10 10
11bind_interrupts!(struct Irqs { 11bind_interrupts!(struct Irqs {
12 UARTE0_UART0 => buffered_uarte::InterruptHandler<peripherals::UARTE0>; 12 UARTE0 => buffered_uarte::InterruptHandler<peripherals::UARTE0>;
13}); 13});
14 14
15#[embassy_executor::main] 15#[embassy_executor::main]
diff --git a/examples/nrf52840/src/bin/multiprio.rs b/examples/nrf52840/src/bin/multiprio.rs
index 797be93a7..d58613da4 100644
--- a/examples/nrf52840/src/bin/multiprio.rs
+++ b/examples/nrf52840/src/bin/multiprio.rs
@@ -112,12 +112,12 @@ static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new();
112static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new(); 112static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();
113 113
114#[interrupt] 114#[interrupt]
115unsafe fn SWI1_EGU1() { 115unsafe fn EGU1_SWI1() {
116 EXECUTOR_HIGH.on_interrupt() 116 EXECUTOR_HIGH.on_interrupt()
117} 117}
118 118
119#[interrupt] 119#[interrupt]
120unsafe fn SWI0_EGU0() { 120unsafe fn EGU0_SWI0() {
121 EXECUTOR_MED.on_interrupt() 121 EXECUTOR_MED.on_interrupt()
122} 122}
123 123
@@ -127,14 +127,14 @@ fn main() -> ! {
127 127
128 let _p = embassy_nrf::init(Default::default()); 128 let _p = embassy_nrf::init(Default::default());
129 129
130 // High-priority executor: SWI1_EGU1, priority level 6 130 // High-priority executor: EGU1_SWI1, priority level 6
131 interrupt::SWI1_EGU1.set_priority(Priority::P6); 131 interrupt::EGU1_SWI1.set_priority(Priority::P6);
132 let spawner = EXECUTOR_HIGH.start(interrupt::SWI1_EGU1); 132 let spawner = EXECUTOR_HIGH.start(interrupt::EGU1_SWI1);
133 unwrap!(spawner.spawn(run_high())); 133 unwrap!(spawner.spawn(run_high()));
134 134
135 // Medium-priority executor: SWI0_EGU0, priority level 7 135 // Medium-priority executor: EGU0_SWI0, priority level 7
136 interrupt::SWI0_EGU0.set_priority(Priority::P7); 136 interrupt::EGU0_SWI0.set_priority(Priority::P7);
137 let spawner = EXECUTOR_MED.start(interrupt::SWI0_EGU0); 137 let spawner = EXECUTOR_MED.start(interrupt::EGU0_SWI0);
138 unwrap!(spawner.spawn(run_med())); 138 unwrap!(spawner.spawn(run_med()));
139 139
140 // Low priority executor: runs in thread mode, using WFE/SEV 140 // Low priority executor: runs in thread mode, using WFE/SEV
diff --git a/examples/nrf52840/src/bin/nfct.rs b/examples/nrf52840/src/bin/nfct.rs
new file mode 100644
index 000000000..d559d006a
--- /dev/null
+++ b/examples/nrf52840/src/bin/nfct.rs
@@ -0,0 +1,79 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_nrf::config::HfclkSource;
7use embassy_nrf::nfct::{Config as NfcConfig, NfcId, NfcT};
8use embassy_nrf::{bind_interrupts, nfct};
9use {defmt_rtt as _, embassy_nrf as _, panic_probe as _};
10
11bind_interrupts!(struct Irqs {
12 NFCT => nfct::InterruptHandler;
13});
14
15#[embassy_executor::main]
16async fn main(_spawner: Spawner) {
17 let mut config = embassy_nrf::config::Config::default();
18 config.hfclk_source = HfclkSource::ExternalXtal;
19 let p = embassy_nrf::init(config);
20
21 dbg!("Setting up...");
22 let config = NfcConfig {
23 nfcid1: NfcId::DoubleSize([0x04, 0x68, 0x95, 0x71, 0xFA, 0x5C, 0x64]),
24 sdd_pat: nfct::SddPat::SDD00100,
25 plat_conf: 0b0000,
26 protocol: nfct::SelResProtocol::Type4A,
27 };
28
29 let mut nfc = NfcT::new(p.NFCT, Irqs, &config);
30
31 let mut buf = [0u8; 256];
32
33 loop {
34 info!("activating");
35 nfc.activate().await;
36
37 loop {
38 info!("rxing");
39 let n = match nfc.receive(&mut buf).await {
40 Ok(n) => n,
41 Err(e) => {
42 error!("rx error {}", e);
43 break;
44 }
45 };
46 let req = &buf[..n];
47 info!("received frame {:02x}", req);
48
49 let mut deselect = false;
50 let resp = match req {
51 [0xe0, ..] => {
52 info!("Got RATS, tx'ing ATS");
53 &[0x06, 0x77, 0x77, 0x81, 0x02, 0x80][..]
54 }
55 [0xc2] => {
56 info!("Got deselect!");
57 deselect = true;
58 &[0xc2]
59 }
60 _ => {
61 info!("Got unknown command!");
62 &[0xFF]
63 }
64 };
65
66 match nfc.transmit(resp).await {
67 Ok(()) => {}
68 Err(e) => {
69 error!("tx error {}", e);
70 break;
71 }
72 }
73
74 if deselect {
75 break;
76 }
77 }
78 }
79}
diff --git a/examples/nrf52840/src/bin/spis.rs b/examples/nrf52840/src/bin/spis.rs
index 613cd37ab..4f28da07e 100644
--- a/examples/nrf52840/src/bin/spis.rs
+++ b/examples/nrf52840/src/bin/spis.rs
@@ -8,7 +8,7 @@ use embassy_nrf::{bind_interrupts, peripherals, spis};
8use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
9 9
10bind_interrupts!(struct Irqs { 10bind_interrupts!(struct Irqs {
11 SPIM2_SPIS2_SPI2 => spis::InterruptHandler<peripherals::SPI2>; 11 SPI2 => spis::InterruptHandler<peripherals::SPI2>;
12}); 12});
13 13
14#[embassy_executor::main] 14#[embassy_executor::main]
diff --git a/examples/nrf52840/src/bin/twim.rs b/examples/nrf52840/src/bin/twim.rs
index a9a0765e8..ceaafd784 100644
--- a/examples/nrf52840/src/bin/twim.rs
+++ b/examples/nrf52840/src/bin/twim.rs
@@ -14,7 +14,7 @@ use {defmt_rtt as _, panic_probe as _};
14const ADDRESS: u8 = 0x50; 14const ADDRESS: u8 = 0x50;
15 15
16bind_interrupts!(struct Irqs { 16bind_interrupts!(struct Irqs {
17 SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler<peripherals::TWISPI0>; 17 TWISPI0 => twim::InterruptHandler<peripherals::TWISPI0>;
18}); 18});
19 19
20#[embassy_executor::main] 20#[embassy_executor::main]
diff --git a/examples/nrf52840/src/bin/twim_lowpower.rs b/examples/nrf52840/src/bin/twim_lowpower.rs
index c743614b8..e2efbdd8d 100644
--- a/examples/nrf52840/src/bin/twim_lowpower.rs
+++ b/examples/nrf52840/src/bin/twim_lowpower.rs
@@ -19,7 +19,7 @@ use {defmt_rtt as _, panic_probe as _};
19const ADDRESS: u8 = 0x50; 19const ADDRESS: u8 = 0x50;
20 20
21bind_interrupts!(struct Irqs { 21bind_interrupts!(struct Irqs {
22 SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler<peripherals::TWISPI0>; 22 TWISPI0 => twim::InterruptHandler<peripherals::TWISPI0>;
23}); 23});
24 24
25#[embassy_executor::main] 25#[embassy_executor::main]
diff --git a/examples/nrf52840/src/bin/twis.rs b/examples/nrf52840/src/bin/twis.rs
index 88bd4cceb..856b34140 100644
--- a/examples/nrf52840/src/bin/twis.rs
+++ b/examples/nrf52840/src/bin/twis.rs
@@ -10,7 +10,7 @@ use embassy_nrf::{bind_interrupts, peripherals};
10use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
11 11
12bind_interrupts!(struct Irqs { 12bind_interrupts!(struct Irqs {
13 SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twis::InterruptHandler<peripherals::TWISPI0>; 13 TWISPI0 => twis::InterruptHandler<peripherals::TWISPI0>;
14}); 14});
15 15
16#[embassy_executor::main] 16#[embassy_executor::main]
diff --git a/examples/nrf52840/src/bin/uart.rs b/examples/nrf52840/src/bin/uart.rs
index accaccea1..23154672f 100644
--- a/examples/nrf52840/src/bin/uart.rs
+++ b/examples/nrf52840/src/bin/uart.rs
@@ -7,7 +7,7 @@ use embassy_nrf::{bind_interrupts, peripherals, uarte};
7use {defmt_rtt as _, panic_probe as _}; 7use {defmt_rtt as _, panic_probe as _};
8 8
9bind_interrupts!(struct Irqs { 9bind_interrupts!(struct Irqs {
10 UARTE0_UART0 => uarte::InterruptHandler<peripherals::UARTE0>; 10 UARTE0 => uarte::InterruptHandler<peripherals::UARTE0>;
11}); 11});
12 12
13#[embassy_executor::main] 13#[embassy_executor::main]
diff --git a/examples/nrf52840/src/bin/uart_idle.rs b/examples/nrf52840/src/bin/uart_idle.rs
index fa93bcf21..a42e84fa4 100644
--- a/examples/nrf52840/src/bin/uart_idle.rs
+++ b/examples/nrf52840/src/bin/uart_idle.rs
@@ -8,7 +8,7 @@ use embassy_nrf::{bind_interrupts, uarte};
8use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
9 9
10bind_interrupts!(struct Irqs { 10bind_interrupts!(struct Irqs {
11 UARTE0_UART0 => uarte::InterruptHandler<UARTE0>; 11 UARTE0 => uarte::InterruptHandler<UARTE0>;
12}); 12});
13 13
14#[embassy_executor::main] 14#[embassy_executor::main]
diff --git a/examples/nrf52840/src/bin/uart_split.rs b/examples/nrf52840/src/bin/uart_split.rs
index c7510a9a8..94af4be86 100644
--- a/examples/nrf52840/src/bin/uart_split.rs
+++ b/examples/nrf52840/src/bin/uart_split.rs
@@ -13,7 +13,7 @@ use {defmt_rtt as _, panic_probe as _};
13static CHANNEL: Channel<ThreadModeRawMutex, [u8; 8], 1> = Channel::new(); 13static CHANNEL: Channel<ThreadModeRawMutex, [u8; 8], 1> = Channel::new();
14 14
15bind_interrupts!(struct Irqs { 15bind_interrupts!(struct Irqs {
16 UARTE0_UART0 => uarte::InterruptHandler<UARTE0>; 16 UARTE0 => uarte::InterruptHandler<UARTE0>;
17}); 17});
18 18
19#[embassy_executor::main] 19#[embassy_executor::main]
diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs
index b07adac1f..88314b749 100644
--- a/examples/nrf52840/src/bin/usb_ethernet.rs
+++ b/examples/nrf52840/src/bin/usb_ethernet.rs
@@ -1,8 +1,6 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3 3
4use core::mem;
5
6use defmt::*; 4use defmt::*;
7use embassy_executor::Spawner; 5use embassy_executor::Spawner;
8use embassy_net::tcp::TcpSocket; 6use embassy_net::tcp::TcpSocket;
@@ -20,7 +18,7 @@ use {defmt_rtt as _, panic_probe as _};
20 18
21bind_interrupts!(struct Irqs { 19bind_interrupts!(struct Irqs {
22 USBD => usb::InterruptHandler<peripherals::USBD>; 20 USBD => usb::InterruptHandler<peripherals::USBD>;
23 POWER_CLOCK => usb::vbus_detect::InterruptHandler; 21 CLOCK_POWER => usb::vbus_detect::InterruptHandler;
24 RNG => rng::InterruptHandler<peripherals::RNG>; 22 RNG => rng::InterruptHandler<peripherals::RNG>;
25}); 23});
26 24
@@ -46,11 +44,10 @@ async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static, MTU>>
46#[embassy_executor::main] 44#[embassy_executor::main]
47async fn main(spawner: Spawner) { 45async fn main(spawner: Spawner) {
48 let p = embassy_nrf::init(Default::default()); 46 let p = embassy_nrf::init(Default::default());
49 let clock: pac::CLOCK = unsafe { mem::transmute(()) };
50 47
51 info!("Enabling ext hfosc..."); 48 info!("Enabling ext hfosc...");
52 clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); 49 pac::CLOCK.tasks_hfclkstart().write_value(1);
53 while clock.events_hfclkstarted.read().bits() != 1 {} 50 while pac::CLOCK.events_hfclkstarted().read() != 1 {}
54 51
55 // Create the driver, from the HAL. 52 // Create the driver, from the HAL.
56 let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); 53 let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs));
diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs
index e33ee5866..5a9dc90a2 100644
--- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs
+++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs
@@ -1,7 +1,6 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3 3
4use core::mem;
5use core::sync::atomic::{AtomicBool, Ordering}; 4use core::sync::atomic::{AtomicBool, Ordering};
6 5
7use defmt::*; 6use defmt::*;
@@ -22,7 +21,7 @@ use {defmt_rtt as _, panic_probe as _};
22 21
23bind_interrupts!(struct Irqs { 22bind_interrupts!(struct Irqs {
24 USBD => usb::InterruptHandler<peripherals::USBD>; 23 USBD => usb::InterruptHandler<peripherals::USBD>;
25 POWER_CLOCK => usb::vbus_detect::InterruptHandler; 24 CLOCK_POWER => usb::vbus_detect::InterruptHandler;
26}); 25});
27 26
28static SUSPENDED: AtomicBool = AtomicBool::new(false); 27static SUSPENDED: AtomicBool = AtomicBool::new(false);
@@ -30,11 +29,10 @@ static SUSPENDED: AtomicBool = AtomicBool::new(false);
30#[embassy_executor::main] 29#[embassy_executor::main]
31async fn main(_spawner: Spawner) { 30async fn main(_spawner: Spawner) {
32 let p = embassy_nrf::init(Default::default()); 31 let p = embassy_nrf::init(Default::default());
33 let clock: pac::CLOCK = unsafe { mem::transmute(()) };
34 32
35 info!("Enabling ext hfosc..."); 33 info!("Enabling ext hfosc...");
36 clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); 34 pac::CLOCK.tasks_hfclkstart().write_value(1);
37 while clock.events_hfclkstarted.read().bits() != 1 {} 35 while pac::CLOCK.events_hfclkstarted().read() != 1 {}
38 36
39 // Create the driver, from the HAL. 37 // Create the driver, from the HAL.
40 let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); 38 let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs));
diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs
index 8076ac283..80cda70e3 100644
--- a/examples/nrf52840/src/bin/usb_hid_mouse.rs
+++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs
@@ -1,8 +1,6 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3 3
4use core::mem;
5
6use defmt::*; 4use defmt::*;
7use embassy_executor::Spawner; 5use embassy_executor::Spawner;
8use embassy_futures::join::join; 6use embassy_futures::join::join;
@@ -18,17 +16,16 @@ use {defmt_rtt as _, panic_probe as _};
18 16
19bind_interrupts!(struct Irqs { 17bind_interrupts!(struct Irqs {
20 USBD => usb::InterruptHandler<peripherals::USBD>; 18 USBD => usb::InterruptHandler<peripherals::USBD>;
21 POWER_CLOCK => usb::vbus_detect::InterruptHandler; 19 CLOCK_POWER => usb::vbus_detect::InterruptHandler;
22}); 20});
23 21
24#[embassy_executor::main] 22#[embassy_executor::main]
25async fn main(_spawner: Spawner) { 23async fn main(_spawner: Spawner) {
26 let p = embassy_nrf::init(Default::default()); 24 let p = embassy_nrf::init(Default::default());
27 let clock: pac::CLOCK = unsafe { mem::transmute(()) };
28 25
29 info!("Enabling ext hfosc..."); 26 info!("Enabling ext hfosc...");
30 clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); 27 pac::CLOCK.tasks_hfclkstart().write_value(1);
31 while clock.events_hfclkstarted.read().bits() != 1 {} 28 while pac::CLOCK.events_hfclkstarted().read() != 1 {}
32 29
33 // Create the driver, from the HAL. 30 // Create the driver, from the HAL.
34 let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); 31 let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs));
diff --git a/examples/nrf52840/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs
index 02048e692..a534046d9 100644
--- a/examples/nrf52840/src/bin/usb_serial.rs
+++ b/examples/nrf52840/src/bin/usb_serial.rs
@@ -1,8 +1,6 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3 3
4use core::mem;
5
6use defmt::{info, panic}; 4use defmt::{info, panic};
7use embassy_executor::Spawner; 5use embassy_executor::Spawner;
8use embassy_futures::join::join; 6use embassy_futures::join::join;
@@ -16,17 +14,16 @@ use {defmt_rtt as _, panic_probe as _};
16 14
17bind_interrupts!(struct Irqs { 15bind_interrupts!(struct Irqs {
18 USBD => usb::InterruptHandler<peripherals::USBD>; 16 USBD => usb::InterruptHandler<peripherals::USBD>;
19 POWER_CLOCK => usb::vbus_detect::InterruptHandler; 17 CLOCK_POWER => usb::vbus_detect::InterruptHandler;
20}); 18});
21 19
22#[embassy_executor::main] 20#[embassy_executor::main]
23async fn main(_spawner: Spawner) { 21async fn main(_spawner: Spawner) {
24 let p = embassy_nrf::init(Default::default()); 22 let p = embassy_nrf::init(Default::default());
25 let clock: pac::CLOCK = unsafe { mem::transmute(()) };
26 23
27 info!("Enabling ext hfosc..."); 24 info!("Enabling ext hfosc...");
28 clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); 25 pac::CLOCK.tasks_hfclkstart().write_value(1);
29 while clock.events_hfclkstarted.read().bits() != 1 {} 26 while pac::CLOCK.events_hfclkstarted().read() != 1 {}
30 27
31 // Create the driver, from the HAL. 28 // Create the driver, from the HAL.
32 let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); 29 let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs));
diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs
index 895cca8b9..32fc5e094 100644
--- a/examples/nrf52840/src/bin/usb_serial_multitask.rs
+++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs
@@ -1,8 +1,6 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3 3
4use core::mem;
5
6use defmt::{info, panic, unwrap}; 4use defmt::{info, panic, unwrap};
7use embassy_executor::Spawner; 5use embassy_executor::Spawner;
8use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; 6use embassy_nrf::usb::vbus_detect::HardwareVbusDetect;
@@ -16,7 +14,7 @@ use {defmt_rtt as _, panic_probe as _};
16 14
17bind_interrupts!(struct Irqs { 15bind_interrupts!(struct Irqs {
18 USBD => usb::InterruptHandler<peripherals::USBD>; 16 USBD => usb::InterruptHandler<peripherals::USBD>;
19 POWER_CLOCK => usb::vbus_detect::InterruptHandler; 17 CLOCK_POWER => usb::vbus_detect::InterruptHandler;
20}); 18});
21 19
22type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; 20type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>;
@@ -39,11 +37,10 @@ async fn echo_task(mut class: CdcAcmClass<'static, MyDriver>) {
39#[embassy_executor::main] 37#[embassy_executor::main]
40async fn main(spawner: Spawner) { 38async fn main(spawner: Spawner) {
41 let p = embassy_nrf::init(Default::default()); 39 let p = embassy_nrf::init(Default::default());
42 let clock: pac::CLOCK = unsafe { mem::transmute(()) };
43 40
44 info!("Enabling ext hfosc..."); 41 info!("Enabling ext hfosc...");
45 clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); 42 pac::CLOCK.tasks_hfclkstart().write_value(1);
46 while clock.events_hfclkstarted.read().bits() != 1 {} 43 while pac::CLOCK.events_hfclkstarted().read() != 1 {}
47 44
48 // Create the driver, from the HAL. 45 // Create the driver, from the HAL.
49 let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); 46 let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs));
diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs
index c6675a3d3..0352f9c66 100644
--- a/examples/nrf52840/src/bin/usb_serial_winusb.rs
+++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs
@@ -1,8 +1,6 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3 3
4use core::mem;
5
6use defmt::{info, panic}; 4use defmt::{info, panic};
7use embassy_executor::Spawner; 5use embassy_executor::Spawner;
8use embassy_futures::join::join; 6use embassy_futures::join::join;
@@ -18,7 +16,7 @@ use {defmt_rtt as _, panic_probe as _};
18 16
19bind_interrupts!(struct Irqs { 17bind_interrupts!(struct Irqs {
20 USBD => usb::InterruptHandler<peripherals::USBD>; 18 USBD => usb::InterruptHandler<peripherals::USBD>;
21 POWER_CLOCK => usb::vbus_detect::InterruptHandler; 19 CLOCK_POWER => usb::vbus_detect::InterruptHandler;
22}); 20});
23 21
24// This is a randomly generated GUID to allow clients on Windows to find our device 22// This is a randomly generated GUID to allow clients on Windows to find our device
@@ -27,11 +25,10 @@ const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321
27#[embassy_executor::main] 25#[embassy_executor::main]
28async fn main(_spawner: Spawner) { 26async fn main(_spawner: Spawner) {
29 let p = embassy_nrf::init(Default::default()); 27 let p = embassy_nrf::init(Default::default());
30 let clock: pac::CLOCK = unsafe { mem::transmute(()) };
31 28
32 info!("Enabling ext hfosc..."); 29 info!("Enabling ext hfosc...");
33 clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); 30 pac::CLOCK.tasks_hfclkstart().write_value(1);
34 while clock.events_hfclkstarted.read().bits() != 1 {} 31 while pac::CLOCK.events_hfclkstarted().read() != 1 {}
35 32
36 // Create the driver, from the HAL. 33 // Create the driver, from the HAL.
37 let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); 34 let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs));
diff --git a/examples/nrf52840/src/bin/wdt.rs b/examples/nrf52840/src/bin/wdt.rs
index ede88cc26..0d9ee3cf8 100644
--- a/examples/nrf52840/src/bin/wdt.rs
+++ b/examples/nrf52840/src/bin/wdt.rs
@@ -4,7 +4,7 @@
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_nrf::gpio::{Input, Pull}; 6use embassy_nrf::gpio::{Input, Pull};
7use embassy_nrf::wdt::{Config, Watchdog}; 7use embassy_nrf::wdt::{Config, HaltConfig, Watchdog};
8use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
9 9
10#[embassy_executor::main] 10#[embassy_executor::main]
@@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) {
17 17
18 // This is needed for `probe-rs run` to be able to catch the panic message 18 // This is needed for `probe-rs run` to be able to catch the panic message
19 // in the WDT interrupt. The core resets 2 ticks after firing the interrupt. 19 // in the WDT interrupt. The core resets 2 ticks after firing the interrupt.
20 config.run_during_debug_halt = false; 20 config.action_during_debug_halt = HaltConfig::PAUSE;
21 21
22 let (_wdt, [mut handle]) = match Watchdog::try_new(p.WDT, config) { 22 let (_wdt, [mut handle]) = match Watchdog::try_new(p.WDT, config) {
23 Ok(x) => x, 23 Ok(x) => x,
diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml
index 0da85be07..1792b277c 100644
--- a/examples/nrf5340/Cargo.toml
+++ b/examples/nrf5340/Cargo.toml
@@ -6,11 +6,11 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 8embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
9embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 9embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
10embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 10embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] }
11embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 11embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
12embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } 12embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] }
13embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } 13embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] }
14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
15embedded-io-async = { version = "0.6.1" } 15embedded-io-async = { version = "0.6.1" }
16 16
diff --git a/examples/nrf54l15/.cargo/config.toml b/examples/nrf54l15/.cargo/config.toml
new file mode 100644
index 000000000..4a026ebbd
--- /dev/null
+++ b/examples/nrf54l15/.cargo/config.toml
@@ -0,0 +1,9 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`
3runner = "../../sshprobe.sh"
4
5[build]
6target = "thumbv8m.main-none-eabihf"
7
8[env]
9DEFMT_LOG = "trace"
diff --git a/examples/nrf54l15/Cargo.toml b/examples/nrf54l15/Cargo.toml
new file mode 100644
index 000000000..7288ef6af
--- /dev/null
+++ b/examples/nrf54l15/Cargo.toml
@@ -0,0 +1,20 @@
1[package]
2edition = "2021"
3name = "embassy-nrf54l15-examples"
4version = "0.1.0"
5license = "MIT OR Apache-2.0"
6
7[dependencies]
8embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
9embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
10embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
11
12defmt = "0.3"
13defmt-rtt = "0.4"
14panic-probe = { version = "0.3", features = ["print-defmt"] }
15
16cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
17cortex-m-rt = "0.7.0"
18
19[profile.release]
20debug = 2
diff --git a/examples/nrf54l15/build.rs b/examples/nrf54l15/build.rs
new file mode 100644
index 000000000..30691aa97
--- /dev/null
+++ b/examples/nrf54l15/build.rs
@@ -0,0 +1,35 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
35}
diff --git a/examples/nrf54l15/memory.x b/examples/nrf54l15/memory.x
new file mode 100644
index 000000000..1064c8a5c
--- /dev/null
+++ b/examples/nrf54l15/memory.x
@@ -0,0 +1,5 @@
1MEMORY
2{
3 FLASH : ORIGIN = 0x00000000, LENGTH = 1536K
4 RAM : ORIGIN = 0x20000000, LENGTH = 256K
5}
diff --git a/examples/nrf54l15/src/bin/blinky.rs b/examples/nrf54l15/src/bin/blinky.rs
new file mode 100644
index 000000000..71fcc461f
--- /dev/null
+++ b/examples/nrf54l15/src/bin/blinky.rs
@@ -0,0 +1,23 @@
1#![no_std]
2#![no_main]
3
4use defmt::info;
5use embassy_executor::Spawner;
6use embassy_nrf::gpio::{Level, Output, OutputDrive};
7use embassy_time::Timer;
8use {defmt_rtt as _, panic_probe as _};
9
10#[embassy_executor::main]
11async fn main(_spawner: Spawner) {
12 let p = embassy_nrf::init(Default::default());
13 let mut led = Output::new(p.P2_09, Level::Low, OutputDrive::Standard);
14
15 loop {
16 info!("high!");
17 led.set_high();
18 Timer::after_millis(300).await;
19 info!("low!");
20 led.set_low();
21 Timer::after_millis(300).await;
22 }
23}
diff --git a/examples/nrf9151/ns/Cargo.toml b/examples/nrf9151/ns/Cargo.toml
index 17fe27b67..0353cf598 100644
--- a/examples/nrf9151/ns/Cargo.toml
+++ b/examples/nrf9151/ns/Cargo.toml
@@ -5,7 +5,7 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-executor = { version = "0.6.0", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 8embassy-executor = { version = "0.6.3", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
9embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 9embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
10embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 10embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
11 11
diff --git a/examples/nrf9151/ns/src/bin/uart.rs b/examples/nrf9151/ns/src/bin/uart.rs
index 2220dccfb..234ff35f2 100644
--- a/examples/nrf9151/ns/src/bin/uart.rs
+++ b/examples/nrf9151/ns/src/bin/uart.rs
@@ -7,7 +7,7 @@ use embassy_nrf::{bind_interrupts, peripherals, uarte};
7use {defmt_rtt as _, panic_probe as _}; 7use {defmt_rtt as _, panic_probe as _};
8 8
9bind_interrupts!(struct Irqs { 9bind_interrupts!(struct Irqs {
10 SPIM0_SPIS0_TWIM0_TWIS0_UARTE0 => uarte::InterruptHandler<peripherals::SERIAL0>; 10 SERIAL0 => uarte::InterruptHandler<peripherals::SERIAL0>;
11}); 11});
12 12
13#[embassy_executor::main] 13#[embassy_executor::main]
diff --git a/examples/nrf9151/s/Cargo.toml b/examples/nrf9151/s/Cargo.toml
index 7253fc4be..5d2302574 100644
--- a/examples/nrf9151/s/Cargo.toml
+++ b/examples/nrf9151/s/Cargo.toml
@@ -5,7 +5,7 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-executor = { version = "0.6.0", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 8embassy-executor = { version = "0.6.3", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
9embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 9embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
10embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 10embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
11 11
diff --git a/examples/nrf9160/Cargo.toml b/examples/nrf9160/Cargo.toml
index 9aeb99317..b52cd4af0 100644
--- a/examples/nrf9160/Cargo.toml
+++ b/examples/nrf9160/Cargo.toml
@@ -5,11 +5,11 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 8embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
9embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 9embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
10embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 10embassy-nrf = { version = "0.2.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.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "proto-ipv4", "medium-ip"] } 12embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "proto-ipv4", "medium-ip"] }
13 13
14defmt = "0.3" 14defmt = "0.3"
15defmt-rtt = "0.4" 15defmt-rtt = "0.4"
diff --git a/examples/nrf9160/src/bin/modem_tcp_client.rs b/examples/nrf9160/src/bin/modem_tcp_client.rs
index 929883884..35900cdd8 100644
--- a/examples/nrf9160/src/bin/modem_tcp_client.rs
+++ b/examples/nrf9160/src/bin/modem_tcp_client.rs
@@ -9,7 +9,7 @@ use core::str::FromStr;
9 9
10use defmt::{info, unwrap, warn}; 10use defmt::{info, unwrap, warn};
11use embassy_executor::Spawner; 11use embassy_executor::Spawner;
12use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources}; 12use embassy_net::{Ipv4Cidr, Stack, StackResources};
13use embassy_net_nrf91::context::Status; 13use embassy_net_nrf91::context::Status;
14use embassy_net_nrf91::{context, Runner, State, TraceBuffer, TraceReader}; 14use embassy_net_nrf91::{context, Runner, State, TraceBuffer, TraceReader};
15use embassy_nrf::buffered_uarte::{self, BufferedUarteTx}; 15use embassy_nrf::buffered_uarte::{self, BufferedUarteTx};
@@ -28,7 +28,7 @@ fn IPC() {
28} 28}
29 29
30bind_interrupts!(struct Irqs { 30bind_interrupts!(struct Irqs {
31 UARTE0_SPIM0_SPIS0_TWIM0_TWIS0 => buffered_uarte::InterruptHandler<peripherals::SERIAL0>; 31 SERIAL0 => buffered_uarte::InterruptHandler<peripherals::SERIAL0>;
32}); 32});
33 33
34#[embassy_executor::task] 34#[embassy_executor::task]
@@ -70,18 +70,16 @@ fn status_to_config(status: &Status) -> embassy_net::ConfigV4 {
70 let Some(IpAddr::V4(addr)) = status.ip else { 70 let Some(IpAddr::V4(addr)) = status.ip else {
71 panic!("Unexpected IP address"); 71 panic!("Unexpected IP address");
72 }; 72 };
73 let addr = Ipv4Address(addr.octets());
74 73
75 let gateway = if let Some(IpAddr::V4(addr)) = status.gateway { 74 let gateway = match status.gateway {
76 Some(Ipv4Address(addr.octets())) 75 Some(IpAddr::V4(addr)) => Some(addr),
77 } else { 76 _ => None,
78 None
79 }; 77 };
80 78
81 let mut dns_servers = Vec::new(); 79 let mut dns_servers = Vec::new();
82 for dns in status.dns.iter() { 80 for dns in status.dns.iter() {
83 if let IpAddr::V4(ip) = dns { 81 if let IpAddr::V4(ip) = dns {
84 unwrap!(dns_servers.push(Ipv4Address(ip.octets()))); 82 unwrap!(dns_servers.push(*ip));
85 } 83 }
86 } 84 }
87 85
@@ -163,6 +161,7 @@ async fn main(spawner: Spawner) {
163 apn: b"iot.nat.es", 161 apn: b"iot.nat.es",
164 auth_prot: context::AuthProt::Pap, 162 auth_prot: context::AuthProt::Pap,
165 auth: Some((b"orange", b"orange")), 163 auth: Some((b"orange", b"orange")),
164 pin: None,
166 }, 165 },
167 stack 166 stack
168 ))); 167 )));
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index 04b4c6317..ce812b2e0 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -7,12 +7,12 @@ license = "MIT OR Apache-2.0"
7 7
8[dependencies] 8[dependencies]
9embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] } 9embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
13embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } 13embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] }
14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } 15embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns", "proto-ipv4", "proto-ipv6", "multicast"] }
16embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } 16embassy-net-wiznet = { version = "0.1.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.2.0", path = "../../embassy-usb-logger" } 18embassy-usb-logger = { version = "0.2.0", path = "../../embassy-usb-logger" }
@@ -25,7 +25,7 @@ fixed = "1.23.1"
25fixed-macro = "1.2" 25fixed-macro = "1.2"
26 26
27# for web request example 27# for web request example
28reqwless = { version = "0.12.0", features = ["defmt",]} 28reqwless = { version = "0.13.0", features = ["defmt"] }
29serde = { version = "1.0.203", default-features = false, features = ["derive"] } 29serde = { version = "1.0.203", default-features = false, features = ["derive"] }
30serde-json-core = "0.5.1" 30serde-json-core = "0.5.1"
31 31
@@ -37,14 +37,15 @@ cortex-m = { version = "0.7.6", features = ["inline-asm"] }
37cortex-m-rt = "0.7.0" 37cortex-m-rt = "0.7.0"
38critical-section = "1.1" 38critical-section = "1.1"
39panic-probe = { version = "0.3", features = ["print-defmt"] } 39panic-probe = { version = "0.3", features = ["print-defmt"] }
40display-interface-spi = "0.4.1" 40display-interface-spi = "0.5.0"
41embedded-graphics = "0.7.1" 41embedded-graphics = "0.8.1"
42st7789 = "0.6.1" 42mipidsi = "0.8.0"
43display-interface = "0.4.1" 43display-interface = "0.5.0"
44byte-slice-cast = { version = "1.2.0", default-features = false } 44byte-slice-cast = { version = "1.2.0", default-features = false }
45smart-leds = "0.3.0" 45smart-leds = "0.4.0"
46heapless = "0.8" 46heapless = "0.8"
47usbd-hid = "0.8.1" 47usbd-hid = "0.8.1"
48rand_core = "0.6.4"
48 49
49embedded-hal-1 = { package = "embedded-hal", version = "1.0" } 50embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
50embedded-hal-async = "1.0" 51embedded-hal-async = "1.0"
diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs
index 6c02630e0..164e6f8d3 100644
--- a/examples/rp/src/bin/pio_hd44780.rs
+++ b/examples/rp/src/bin/pio_hd44780.rs
@@ -7,13 +7,11 @@
7use core::fmt::Write; 7use core::fmt::Write;
8 8
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_rp::dma::{AnyChannel, Channel}; 10use embassy_rp::bind_interrupts;
11use embassy_rp::peripherals::PIO0; 11use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{ 12use embassy_rp::pio::{InterruptHandler, Pio};
13 Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, 13use embassy_rp::pio_programs::hd44780::{PioHD44780, PioHD44780CommandSequenceProgram, PioHD44780CommandWordProgram};
14};
15use embassy_rp::pwm::{self, Pwm}; 14use embassy_rp::pwm::{self, Pwm};
16use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef};
17use embassy_time::{Instant, Timer}; 15use embassy_time::{Instant, Timer};
18use {defmt_rtt as _, panic_probe as _}; 16use {defmt_rtt as _, panic_probe as _};
19 17
@@ -43,8 +41,27 @@ async fn main(_spawner: Spawner) {
43 c 41 c
44 }); 42 });
45 43
46 let mut hd = HD44780::new( 44 let Pio {
47 p.PIO0, Irqs, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6, 45 mut common, sm0, irq0, ..
46 } = Pio::new(p.PIO0, Irqs);
47
48 let word_prg = PioHD44780CommandWordProgram::new(&mut common);
49 let seq_prg = PioHD44780CommandSequenceProgram::new(&mut common);
50
51 let mut hd = PioHD44780::new(
52 &mut common,
53 sm0,
54 irq0,
55 p.DMA_CH3,
56 p.PIN_0,
57 p.PIN_1,
58 p.PIN_2,
59 p.PIN_3,
60 p.PIN_4,
61 p.PIN_5,
62 p.PIN_6,
63 &word_prg,
64 &seq_prg,
48 ) 65 )
49 .await; 66 .await;
50 67
@@ -68,173 +85,3 @@ async fn main(_spawner: Spawner) {
68 Timer::after_secs(1).await; 85 Timer::after_secs(1).await;
69 } 86 }
70} 87}
71
72pub struct HD44780<'l> {
73 dma: PeripheralRef<'l, AnyChannel>,
74 sm: StateMachine<'l, PIO0, 0>,
75
76 buf: [u8; 40],
77}
78
79impl<'l> HD44780<'l> {
80 pub async fn new(
81 pio: impl Peripheral<P = PIO0> + 'l,
82 irq: Irqs,
83 dma: impl Peripheral<P = impl Channel> + 'l,
84 rs: impl PioPin,
85 rw: impl PioPin,
86 e: impl PioPin,
87 db4: impl PioPin,
88 db5: impl PioPin,
89 db6: impl PioPin,
90 db7: impl PioPin,
91 ) -> HD44780<'l> {
92 into_ref!(dma);
93
94 let Pio {
95 mut common,
96 mut irq0,
97 mut sm0,
98 ..
99 } = Pio::new(pio, irq);
100
101 // takes command words (<wait:24> <command:4> <0:4>)
102 let prg = pio_proc::pio_asm!(
103 r#"
104 .side_set 1 opt
105 .origin 20
106
107 loop:
108 out x, 24
109 delay:
110 jmp x--, delay
111 out pins, 4 side 1
112 out null, 4 side 0
113 jmp !osre, loop
114 irq 0
115 "#,
116 );
117
118 let rs = common.make_pio_pin(rs);
119 let rw = common.make_pio_pin(rw);
120 let e = common.make_pio_pin(e);
121 let db4 = common.make_pio_pin(db4);
122 let db5 = common.make_pio_pin(db5);
123 let db6 = common.make_pio_pin(db6);
124 let db7 = common.make_pio_pin(db7);
125
126 sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]);
127
128 let mut cfg = Config::default();
129 cfg.use_program(&common.load_program(&prg.program), &[&e]);
130 cfg.clock_divider = 125u8.into();
131 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
132 cfg.shift_out = ShiftConfig {
133 auto_fill: true,
134 direction: ShiftDirection::Left,
135 threshold: 32,
136 };
137 cfg.fifo_join = FifoJoin::TxOnly;
138 sm0.set_config(&cfg);
139
140 sm0.set_enable(true);
141 // init to 8 bit thrice
142 sm0.tx().push((50000 << 8) | 0x30);
143 sm0.tx().push((5000 << 8) | 0x30);
144 sm0.tx().push((200 << 8) | 0x30);
145 // init 4 bit
146 sm0.tx().push((200 << 8) | 0x20);
147 // set font and lines
148 sm0.tx().push((50 << 8) | 0x20);
149 sm0.tx().push(0b1100_0000);
150
151 irq0.wait().await;
152 sm0.set_enable(false);
153
154 // takes command sequences (<rs:1> <count:7>, data...)
155 // many side sets are only there to free up a delay bit!
156 let prg = pio_proc::pio_asm!(
157 r#"
158 .origin 27
159 .side_set 1
160
161 .wrap_target
162 pull side 0
163 out x 1 side 0 ; !rs
164 out y 7 side 0 ; #data - 1
165
166 ; rs/rw to e: >= 60ns
167 ; e high time: >= 500ns
168 ; e low time: >= 500ns
169 ; read data valid after e falling: ~5ns
170 ; write data hold after e falling: ~10ns
171
172 loop:
173 pull side 0
174 jmp !x data side 0
175 command:
176 set pins 0b00 side 0
177 jmp shift side 0
178 data:
179 set pins 0b01 side 0
180 shift:
181 out pins 4 side 1 [9]
182 nop side 0 [9]
183 out pins 4 side 1 [9]
184 mov osr null side 0 [7]
185 out pindirs 4 side 0
186 set pins 0b10 side 0
187 busy:
188 nop side 1 [9]
189 jmp pin more side 0 [9]
190 mov osr ~osr side 1 [9]
191 nop side 0 [4]
192 out pindirs 4 side 0
193 jmp y-- loop side 0
194 .wrap
195 more:
196 nop side 1 [9]
197 jmp busy side 0 [9]
198 "#
199 );
200
201 let mut cfg = Config::default();
202 cfg.use_program(&common.load_program(&prg.program), &[&e]);
203 cfg.clock_divider = 8u8.into(); // ~64ns/insn
204 cfg.set_jmp_pin(&db7);
205 cfg.set_set_pins(&[&rs, &rw]);
206 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
207 cfg.shift_out.direction = ShiftDirection::Left;
208 cfg.fifo_join = FifoJoin::TxOnly;
209 sm0.set_config(&cfg);
210
211 sm0.set_enable(true);
212
213 // display on and cursor on and blinking, reset display
214 sm0.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await;
215
216 Self {
217 dma: dma.map_into(),
218 sm: sm0,
219 buf: [0x20; 40],
220 }
221 }
222
223 pub async fn add_line(&mut self, s: &[u8]) {
224 // move cursor to 0:0, prepare 16 characters
225 self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]);
226 // move line 2 up
227 self.buf.copy_within(22..38, 3);
228 // move cursor to 1:0, prepare 16 characters
229 self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]);
230 // file line 2 with spaces
231 self.buf[22..38].fill(0x20);
232 // copy input line
233 let len = s.len().min(16);
234 self.buf[22..22 + len].copy_from_slice(&s[0..len]);
235 // set cursor to 1:15
236 self.buf[38..].copy_from_slice(&[0x80, 0xcf]);
237
238 self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await;
239 }
240}
diff --git a/examples/rp/src/bin/pio_i2s.rs b/examples/rp/src/bin/pio_i2s.rs
index cf60e5b30..447100ddf 100644
--- a/examples/rp/src/bin/pio_i2s.rs
+++ b/examples/rp/src/bin/pio_i2s.rs
@@ -13,10 +13,10 @@
13use core::mem; 13use core::mem;
14 14
15use embassy_executor::Spawner; 15use embassy_executor::Spawner;
16use embassy_rp::bind_interrupts;
16use embassy_rp::peripherals::PIO0; 17use embassy_rp::peripherals::PIO0;
17use embassy_rp::pio::{Config, FifoJoin, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; 18use embassy_rp::pio::{InterruptHandler, Pio};
18use embassy_rp::{bind_interrupts, Peripheral}; 19use embassy_rp::pio_programs::i2s::{PioI2sOut, PioI2sOutProgram};
19use fixed::traits::ToFixed;
20use static_cell::StaticCell; 20use static_cell::StaticCell;
21use {defmt_rtt as _, panic_probe as _}; 21use {defmt_rtt as _, panic_probe as _};
22 22
@@ -25,61 +25,32 @@ bind_interrupts!(struct Irqs {
25}); 25});
26 26
27const SAMPLE_RATE: u32 = 48_000; 27const SAMPLE_RATE: u32 = 48_000;
28const BIT_DEPTH: u32 = 16;
29const CHANNELS: u32 = 2;
28 30
29#[embassy_executor::main] 31#[embassy_executor::main]
30async fn main(_spawner: Spawner) { 32async fn main(_spawner: Spawner) {
31 let mut p = embassy_rp::init(Default::default()); 33 let mut p = embassy_rp::init(Default::default());
32 34
33 // Setup pio state machine for i2s output 35 // Setup pio state machine for i2s output
34 let mut pio = Pio::new(p.PIO0, Irqs); 36 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
35
36 #[rustfmt::skip]
37 let pio_program = pio_proc::pio_asm!(
38 ".side_set 2",
39 " set x, 14 side 0b01", // side 0bWB - W = Word Clock, B = Bit Clock
40 "left_data:",
41 " out pins, 1 side 0b00",
42 " jmp x-- left_data side 0b01",
43 " out pins 1 side 0b10",
44 " set x, 14 side 0b11",
45 "right_data:",
46 " out pins 1 side 0b10",
47 " jmp x-- right_data side 0b11",
48 " out pins 1 side 0b00",
49 );
50 37
51 let bit_clock_pin = p.PIN_18; 38 let bit_clock_pin = p.PIN_18;
52 let left_right_clock_pin = p.PIN_19; 39 let left_right_clock_pin = p.PIN_19;
53 let data_pin = p.PIN_20; 40 let data_pin = p.PIN_20;
54 41
55 let data_pin = pio.common.make_pio_pin(data_pin); 42 let program = PioI2sOutProgram::new(&mut common);
56 let bit_clock_pin = pio.common.make_pio_pin(bit_clock_pin); 43 let mut i2s = PioI2sOut::new(
57 let left_right_clock_pin = pio.common.make_pio_pin(left_right_clock_pin); 44 &mut common,
58 45 sm0,
59 let cfg = { 46 p.DMA_CH0,
60 let mut cfg = Config::default(); 47 data_pin,
61 cfg.use_program( 48 bit_clock_pin,
62 &pio.common.load_program(&pio_program.program), 49 left_right_clock_pin,
63 &[&bit_clock_pin, &left_right_clock_pin], 50 SAMPLE_RATE,
64 ); 51 BIT_DEPTH,
65 cfg.set_out_pins(&[&data_pin]); 52 CHANNELS,
66 const BIT_DEPTH: u32 = 16; 53 &program,
67 const CHANNELS: u32 = 2;
68 let clock_frequency = SAMPLE_RATE * BIT_DEPTH * CHANNELS;
69 cfg.clock_divider = (125_000_000. / clock_frequency as f64 / 2.).to_fixed();
70 cfg.shift_out = ShiftConfig {
71 threshold: 32,
72 direction: ShiftDirection::Left,
73 auto_fill: true,
74 };
75 // join fifos to have twice the time to start the next dma transfer
76 cfg.fifo_join = FifoJoin::TxOnly;
77 cfg
78 };
79 pio.sm0.set_config(&cfg);
80 pio.sm0.set_pin_dirs(
81 embassy_rp::pio::Direction::Out,
82 &[&data_pin, &left_right_clock_pin, &bit_clock_pin],
83 ); 54 );
84 55
85 // create two audio buffers (back and front) which will take turns being 56 // create two audio buffers (back and front) which will take turns being
@@ -90,17 +61,13 @@ async fn main(_spawner: Spawner) {
90 let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE); 61 let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE);
91 62
92 // start pio state machine 63 // start pio state machine
93 pio.sm0.set_enable(true);
94 let tx = pio.sm0.tx();
95 let mut dma_ref = p.DMA_CH0.into_ref();
96
97 let mut fade_value: i32 = 0; 64 let mut fade_value: i32 = 0;
98 let mut phase: i32 = 0; 65 let mut phase: i32 = 0;
99 66
100 loop { 67 loop {
101 // trigger transfer of front buffer data to the pio fifo 68 // trigger transfer of front buffer data to the pio fifo
102 // but don't await the returned future, yet 69 // but don't await the returned future, yet
103 let dma_future = tx.dma_push(dma_ref.reborrow(), front_buffer); 70 let dma_future = i2s.write(front_buffer);
104 71
105 // fade in audio when bootsel is pressed 72 // fade in audio when bootsel is pressed
106 let fade_target = if p.BOOTSEL.is_pressed() { i32::MAX } else { 0 }; 73 let fade_target = if p.BOOTSEL.is_pressed() { i32::MAX } else { 0 };
diff --git a/examples/rp/src/bin/pio_onewire.rs b/examples/rp/src/bin/pio_onewire.rs
index 5076101ec..991510851 100644
--- a/examples/rp/src/bin/pio_onewire.rs
+++ b/examples/rp/src/bin/pio_onewire.rs
@@ -6,7 +6,8 @@ use defmt::*;
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_rp::bind_interrupts; 7use embassy_rp::bind_interrupts;
8use embassy_rp::peripherals::PIO0; 8use embassy_rp::peripherals::PIO0;
9use embassy_rp::pio::{self, Common, Config, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; 9use embassy_rp::pio::{self, InterruptHandler, Pio};
10use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram};
10use embassy_time::Timer; 11use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _}; 12use {defmt_rtt as _, panic_probe as _};
12 13
@@ -18,7 +19,11 @@ bind_interrupts!(struct Irqs {
18async fn main(_spawner: Spawner) { 19async fn main(_spawner: Spawner) {
19 let p = embassy_rp::init(Default::default()); 20 let p = embassy_rp::init(Default::default());
20 let mut pio = Pio::new(p.PIO0, Irqs); 21 let mut pio = Pio::new(p.PIO0, Irqs);
21 let mut sensor = Ds18b20::new(&mut pio.common, pio.sm0, p.PIN_2); 22
23 let prg = PioOneWireProgram::new(&mut pio.common);
24 let onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg);
25
26 let mut sensor = Ds18b20::new(onewire);
22 27
23 loop { 28 loop {
24 sensor.start().await; // Start a new measurement 29 sensor.start().await; // Start a new measurement
@@ -33,89 +38,12 @@ async fn main(_spawner: Spawner) {
33 38
34/// DS18B20 temperature sensor driver 39/// DS18B20 temperature sensor driver
35pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> { 40pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> {
36 sm: StateMachine<'d, PIO, SM>, 41 wire: PioOneWire<'d, PIO, SM>,
37} 42}
38 43
39impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> { 44impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> {
40 /// Create a new instance the driver 45 pub fn new(wire: PioOneWire<'d, PIO, SM>) -> Self {
41 pub fn new(common: &mut Common<'d, PIO>, mut sm: StateMachine<'d, PIO, SM>, pin: impl PioPin) -> Self { 46 Self { wire }
42 let prg = pio_proc::pio_asm!(
43 r#"
44 .wrap_target
45 again:
46 pull block
47 mov x, osr
48 jmp !x, read
49 write:
50 set pindirs, 1
51 set pins, 0
52 loop1:
53 jmp x--,loop1
54 set pindirs, 0 [31]
55 wait 1 pin 0 [31]
56 pull block
57 mov x, osr
58 bytes1:
59 pull block
60 set y, 7
61 set pindirs, 1
62 bit1:
63 set pins, 0 [1]
64 out pins,1 [31]
65 set pins, 1 [20]
66 jmp y--,bit1
67 jmp x--,bytes1
68 set pindirs, 0 [31]
69 jmp again
70 read:
71 pull block
72 mov x, osr
73 bytes2:
74 set y, 7
75 bit2:
76 set pindirs, 1
77 set pins, 0 [1]
78 set pindirs, 0 [5]
79 in pins,1 [10]
80 jmp y--,bit2
81 jmp x--,bytes2
82 .wrap
83 "#,
84 );
85
86 let pin = common.make_pio_pin(pin);
87 let mut cfg = Config::default();
88 cfg.use_program(&common.load_program(&prg.program), &[]);
89 cfg.set_out_pins(&[&pin]);
90 cfg.set_in_pins(&[&pin]);
91 cfg.set_set_pins(&[&pin]);
92 cfg.shift_in = ShiftConfig {
93 auto_fill: true,
94 direction: ShiftDirection::Right,
95 threshold: 8,
96 };
97 cfg.clock_divider = 255_u8.into();
98 sm.set_config(&cfg);
99 sm.set_enable(true);
100 Self { sm }
101 }
102
103 /// Write bytes over the wire
104 async fn write_bytes(&mut self, bytes: &[u8]) {
105 self.sm.tx().wait_push(250).await;
106 self.sm.tx().wait_push(bytes.len() as u32 - 1).await;
107 for b in bytes {
108 self.sm.tx().wait_push(*b as u32).await;
109 }
110 }
111
112 /// Read bytes from the wire
113 async fn read_bytes(&mut self, bytes: &mut [u8]) {
114 self.sm.tx().wait_push(0).await;
115 self.sm.tx().wait_push(bytes.len() as u32 - 1).await;
116 for b in bytes.iter_mut() {
117 *b = (self.sm.rx().wait_pull().await >> 24) as u8;
118 }
119 } 47 }
120 48
121 /// Calculate CRC8 of the data 49 /// Calculate CRC8 of the data
@@ -139,14 +67,14 @@ impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> {
139 67
140 /// Start a new measurement. Allow at least 1000ms before getting `temperature`. 68 /// Start a new measurement. Allow at least 1000ms before getting `temperature`.
141 pub async fn start(&mut self) { 69 pub async fn start(&mut self) {
142 self.write_bytes(&[0xCC, 0x44]).await; 70 self.wire.write_bytes(&[0xCC, 0x44]).await;
143 } 71 }
144 72
145 /// Read the temperature. Ensure >1000ms has passed since `start` before calling this. 73 /// Read the temperature. Ensure >1000ms has passed since `start` before calling this.
146 pub async fn temperature(&mut self) -> Result<f32, ()> { 74 pub async fn temperature(&mut self) -> Result<f32, ()> {
147 self.write_bytes(&[0xCC, 0xBE]).await; 75 self.wire.write_bytes(&[0xCC, 0xBE]).await;
148 let mut data = [0; 9]; 76 let mut data = [0; 9];
149 self.read_bytes(&mut data).await; 77 self.wire.read_bytes(&mut data).await;
150 match Self::crc8(&data) == 0 { 78 match Self::crc8(&data) == 0 {
151 true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.), 79 true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.),
152 false => Err(()), 80 false => Err(()),
diff --git a/examples/rp/src/bin/pio_pwm.rs b/examples/rp/src/bin/pio_pwm.rs
index 23d63d435..7eabb2289 100644
--- a/examples/rp/src/bin/pio_pwm.rs
+++ b/examples/rp/src/bin/pio_pwm.rs
@@ -5,12 +5,11 @@
5use core::time::Duration; 5use core::time::Duration;
6 6
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_rp::gpio::Level; 8use embassy_rp::bind_interrupts;
9use embassy_rp::peripherals::PIO0; 9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; 10use embassy_rp::pio::{InterruptHandler, Pio};
11use embassy_rp::{bind_interrupts, clocks}; 11use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram};
12use embassy_time::Timer; 12use embassy_time::Timer;
13use pio::InstructionOperands;
14use {defmt_rtt as _, panic_probe as _}; 13use {defmt_rtt as _, panic_probe as _};
15 14
16const REFRESH_INTERVAL: u64 = 20000; 15const REFRESH_INTERVAL: u64 = 20000;
@@ -19,93 +18,14 @@ bind_interrupts!(struct Irqs {
19 PIO0_IRQ_0 => InterruptHandler<PIO0>; 18 PIO0_IRQ_0 => InterruptHandler<PIO0>;
20}); 19});
21 20
22pub fn to_pio_cycles(duration: Duration) -> u32 {
23 (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
24}
25
26pub struct PwmPio<'d, T: Instance, const SM: usize> {
27 sm: StateMachine<'d, T, SM>,
28}
29
30impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
31 pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
32 let prg = pio_proc::pio_asm!(
33 ".side_set 1 opt"
34 "pull noblock side 0"
35 "mov x, osr"
36 "mov y, isr"
37 "countloop:"
38 "jmp x!=y noset"
39 "jmp skip side 1"
40 "noset:"
41 "nop"
42 "skip:"
43 "jmp y-- countloop"
44 );
45
46 pio.load_program(&prg.program);
47 let pin = pio.make_pio_pin(pin);
48 sm.set_pins(Level::High, &[&pin]);
49 sm.set_pin_dirs(Direction::Out, &[&pin]);
50
51 let mut cfg = Config::default();
52 cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
53
54 sm.set_config(&cfg);
55
56 Self { sm }
57 }
58
59 pub fn start(&mut self) {
60 self.sm.set_enable(true);
61 }
62
63 pub fn stop(&mut self) {
64 self.sm.set_enable(false);
65 }
66
67 pub fn set_period(&mut self, duration: Duration) {
68 let is_enabled = self.sm.is_enabled();
69 while !self.sm.tx().empty() {} // Make sure that the queue is empty
70 self.sm.set_enable(false);
71 self.sm.tx().push(to_pio_cycles(duration));
72 unsafe {
73 self.sm.exec_instr(
74 InstructionOperands::PULL {
75 if_empty: false,
76 block: false,
77 }
78 .encode(),
79 );
80 self.sm.exec_instr(
81 InstructionOperands::OUT {
82 destination: ::pio::OutDestination::ISR,
83 bit_count: 32,
84 }
85 .encode(),
86 );
87 };
88 if is_enabled {
89 self.sm.set_enable(true) // Enable if previously enabled
90 }
91 }
92
93 pub fn set_level(&mut self, level: u32) {
94 self.sm.tx().push(level);
95 }
96
97 pub fn write(&mut self, duration: Duration) {
98 self.set_level(to_pio_cycles(duration));
99 }
100}
101
102#[embassy_executor::main] 21#[embassy_executor::main]
103async fn main(_spawner: Spawner) { 22async fn main(_spawner: Spawner) {
104 let p = embassy_rp::init(Default::default()); 23 let p = embassy_rp::init(Default::default());
105 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); 24 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
106 25
107 // Note that PIN_25 is the led pin on the Pico 26 // Note that PIN_25 is the led pin on the Pico
108 let mut pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_25); 27 let prg = PioPwmProgram::new(&mut common);
28 let mut pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_25, &prg);
109 pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL)); 29 pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL));
110 pwm_pio.start(); 30 pwm_pio.start();
111 31
diff --git a/examples/rp/src/bin/pio_rotary_encoder.rs b/examples/rp/src/bin/pio_rotary_encoder.rs
index 58bdadbc0..2750f61ae 100644
--- a/examples/rp/src/bin/pio_rotary_encoder.rs
+++ b/examples/rp/src/bin/pio_rotary_encoder.rs
@@ -5,70 +5,30 @@
5 5
6use defmt::info; 6use defmt::info;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_rp::gpio::Pull; 8use embassy_rp::bind_interrupts;
9use embassy_rp::peripherals::PIO0; 9use embassy_rp::peripherals::PIO0;
10use embassy_rp::{bind_interrupts, pio}; 10use embassy_rp::pio::{InterruptHandler, Pio};
11use fixed::traits::ToFixed; 11use embassy_rp::pio_programs::rotary_encoder::{Direction, PioEncoder, PioEncoderProgram};
12use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine};
13use {defmt_rtt as _, panic_probe as _}; 12use {defmt_rtt as _, panic_probe as _};
14 13
15bind_interrupts!(struct Irqs { 14bind_interrupts!(struct Irqs {
16 PIO0_IRQ_0 => InterruptHandler<PIO0>; 15 PIO0_IRQ_0 => InterruptHandler<PIO0>;
17}); 16});
18 17
19pub struct PioEncoder<'d, T: Instance, const SM: usize> { 18#[embassy_executor::task]
20 sm: StateMachine<'d, T, SM>, 19async fn encoder_0(mut encoder: PioEncoder<'static, PIO0, 0>) {
21} 20 let mut count = 0;
22 21 loop {
23impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> { 22 info!("Count: {}", count);
24 pub fn new( 23 count += match encoder.read().await {
25 pio: &mut Common<'d, T>, 24 Direction::Clockwise => 1,
26 mut sm: StateMachine<'d, T, SM>, 25 Direction::CounterClockwise => -1,
27 pin_a: impl PioPin, 26 };
28 pin_b: impl PioPin,
29 ) -> Self {
30 let mut pin_a = pio.make_pio_pin(pin_a);
31 let mut pin_b = pio.make_pio_pin(pin_b);
32 pin_a.set_pull(Pull::Up);
33 pin_b.set_pull(Pull::Up);
34 sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]);
35
36 let prg = pio_proc::pio_asm!("wait 1 pin 1", "wait 0 pin 1", "in pins, 2", "push",);
37
38 let mut cfg = Config::default();
39 cfg.set_in_pins(&[&pin_a, &pin_b]);
40 cfg.fifo_join = FifoJoin::RxOnly;
41 cfg.shift_in.direction = ShiftDirection::Left;
42 cfg.clock_divider = 10_000.to_fixed();
43 cfg.use_program(&pio.load_program(&prg.program), &[]);
44 sm.set_config(&cfg);
45 sm.set_enable(true);
46 Self { sm }
47 }
48
49 pub async fn read(&mut self) -> Direction {
50 loop {
51 match self.sm.rx().wait_pull().await {
52 0 => return Direction::CounterClockwise,
53 1 => return Direction::Clockwise,
54 _ => {}
55 }
56 }
57 } 27 }
58} 28}
59 29
60pub enum Direction { 30#[embassy_executor::task]
61 Clockwise, 31async fn encoder_1(mut encoder: PioEncoder<'static, PIO0, 1>) {
62 CounterClockwise,
63}
64
65#[embassy_executor::main]
66async fn main(_spawner: Spawner) {
67 let p = embassy_rp::init(Default::default());
68 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
69
70 let mut encoder = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5);
71
72 let mut count = 0; 32 let mut count = 0;
73 loop { 33 loop {
74 info!("Count: {}", count); 34 info!("Count: {}", count);
@@ -78,3 +38,18 @@ async fn main(_spawner: Spawner) {
78 }; 38 };
79 } 39 }
80} 40}
41
42#[embassy_executor::main]
43async fn main(spawner: Spawner) {
44 let p = embassy_rp::init(Default::default());
45 let Pio {
46 mut common, sm0, sm1, ..
47 } = Pio::new(p.PIO0, Irqs);
48
49 let prg = PioEncoderProgram::new(&mut common);
50 let encoder0 = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5, &prg);
51 let encoder1 = PioEncoder::new(&mut common, sm1, p.PIN_6, p.PIN_7, &prg);
52
53 spawner.must_spawn(encoder_0(encoder0));
54 spawner.must_spawn(encoder_1(encoder1));
55}
diff --git a/examples/rp/src/bin/pio_servo.rs b/examples/rp/src/bin/pio_servo.rs
index a79540479..c52ee7492 100644
--- a/examples/rp/src/bin/pio_servo.rs
+++ b/examples/rp/src/bin/pio_servo.rs
@@ -5,12 +5,11 @@
5use core::time::Duration; 5use core::time::Duration;
6 6
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_rp::gpio::Level; 8use embassy_rp::bind_interrupts;
9use embassy_rp::peripherals::PIO0; 9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; 10use embassy_rp::pio::{Instance, InterruptHandler, Pio};
11use embassy_rp::{bind_interrupts, clocks}; 11use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram};
12use embassy_time::Timer; 12use embassy_time::Timer;
13use pio::InstructionOperands;
14use {defmt_rtt as _, panic_probe as _}; 13use {defmt_rtt as _, panic_probe as _};
15 14
16const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo 15const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo
@@ -22,88 +21,8 @@ bind_interrupts!(struct Irqs {
22 PIO0_IRQ_0 => InterruptHandler<PIO0>; 21 PIO0_IRQ_0 => InterruptHandler<PIO0>;
23}); 22});
24 23
25pub fn to_pio_cycles(duration: Duration) -> u32 {
26 (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
27}
28
29pub struct PwmPio<'d, T: Instance, const SM: usize> {
30 sm: StateMachine<'d, T, SM>,
31}
32
33impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
34 pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
35 let prg = pio_proc::pio_asm!(
36 ".side_set 1 opt"
37 "pull noblock side 0"
38 "mov x, osr"
39 "mov y, isr"
40 "countloop:"
41 "jmp x!=y noset"
42 "jmp skip side 1"
43 "noset:"
44 "nop"
45 "skip:"
46 "jmp y-- countloop"
47 );
48
49 pio.load_program(&prg.program);
50 let pin = pio.make_pio_pin(pin);
51 sm.set_pins(Level::High, &[&pin]);
52 sm.set_pin_dirs(Direction::Out, &[&pin]);
53
54 let mut cfg = Config::default();
55 cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
56
57 sm.set_config(&cfg);
58
59 Self { sm }
60 }
61
62 pub fn start(&mut self) {
63 self.sm.set_enable(true);
64 }
65
66 pub fn stop(&mut self) {
67 self.sm.set_enable(false);
68 }
69
70 pub fn set_period(&mut self, duration: Duration) {
71 let is_enabled = self.sm.is_enabled();
72 while !self.sm.tx().empty() {} // Make sure that the queue is empty
73 self.sm.set_enable(false);
74 self.sm.tx().push(to_pio_cycles(duration));
75 unsafe {
76 self.sm.exec_instr(
77 InstructionOperands::PULL {
78 if_empty: false,
79 block: false,
80 }
81 .encode(),
82 );
83 self.sm.exec_instr(
84 InstructionOperands::OUT {
85 destination: ::pio::OutDestination::ISR,
86 bit_count: 32,
87 }
88 .encode(),
89 );
90 };
91 if is_enabled {
92 self.sm.set_enable(true) // Enable if previously enabled
93 }
94 }
95
96 pub fn set_level(&mut self, level: u32) {
97 self.sm.tx().push(level);
98 }
99
100 pub fn write(&mut self, duration: Duration) {
101 self.set_level(to_pio_cycles(duration));
102 }
103}
104
105pub struct ServoBuilder<'d, T: Instance, const SM: usize> { 24pub struct ServoBuilder<'d, T: Instance, const SM: usize> {
106 pwm: PwmPio<'d, T, SM>, 25 pwm: PioPwm<'d, T, SM>,
107 period: Duration, 26 period: Duration,
108 min_pulse_width: Duration, 27 min_pulse_width: Duration,
109 max_pulse_width: Duration, 28 max_pulse_width: Duration,
@@ -111,7 +30,7 @@ pub struct ServoBuilder<'d, T: Instance, const SM: usize> {
111} 30}
112 31
113impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> { 32impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> {
114 pub fn new(pwm: PwmPio<'d, T, SM>) -> Self { 33 pub fn new(pwm: PioPwm<'d, T, SM>) -> Self {
115 Self { 34 Self {
116 pwm, 35 pwm,
117 period: Duration::from_micros(REFRESH_INTERVAL), 36 period: Duration::from_micros(REFRESH_INTERVAL),
@@ -153,7 +72,7 @@ impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> {
153} 72}
154 73
155pub struct Servo<'d, T: Instance, const SM: usize> { 74pub struct Servo<'d, T: Instance, const SM: usize> {
156 pwm: PwmPio<'d, T, SM>, 75 pwm: PioPwm<'d, T, SM>,
157 min_pulse_width: Duration, 76 min_pulse_width: Duration,
158 max_pulse_width: Duration, 77 max_pulse_width: Duration,
159 max_degree_rotation: u64, 78 max_degree_rotation: u64,
@@ -190,7 +109,8 @@ async fn main(_spawner: Spawner) {
190 let p = embassy_rp::init(Default::default()); 109 let p = embassy_rp::init(Default::default());
191 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); 110 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
192 111
193 let pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_1); 112 let prg = PioPwmProgram::new(&mut common);
113 let pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_1, &prg);
194 let mut servo = ServoBuilder::new(pwm_pio) 114 let mut servo = ServoBuilder::new(pwm_pio)
195 .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo 115 .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo
196 .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment. 116 .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment.
diff --git a/examples/rp/src/bin/pio_stepper.rs b/examples/rp/src/bin/pio_stepper.rs
index 4952f4fbd..3862c248b 100644
--- a/examples/rp/src/bin/pio_stepper.rs
+++ b/examples/rp/src/bin/pio_stepper.rs
@@ -3,143 +3,20 @@
3 3
4#![no_std] 4#![no_std]
5#![no_main] 5#![no_main]
6use core::mem::{self, MaybeUninit};
7 6
8use defmt::info; 7use defmt::info;
9use embassy_executor::Spawner; 8use embassy_executor::Spawner;
10use embassy_rp::bind_interrupts; 9use embassy_rp::bind_interrupts;
11use embassy_rp::peripherals::PIO0; 10use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Irq, Pio, PioPin, StateMachine}; 11use embassy_rp::pio::{InterruptHandler, Pio};
12use embassy_rp::pio_programs::stepper::{PioStepper, PioStepperProgram};
13use embassy_time::{with_timeout, Duration, Timer}; 13use embassy_time::{with_timeout, Duration, Timer};
14use fixed::traits::ToFixed;
15use fixed::types::extra::U8;
16use fixed::FixedU32;
17use {defmt_rtt as _, panic_probe as _}; 14use {defmt_rtt as _, panic_probe as _};
18 15
19bind_interrupts!(struct Irqs { 16bind_interrupts!(struct Irqs {
20 PIO0_IRQ_0 => InterruptHandler<PIO0>; 17 PIO0_IRQ_0 => InterruptHandler<PIO0>;
21}); 18});
22 19
23pub struct PioStepper<'d, T: Instance, const SM: usize> {
24 irq: Irq<'d, T, SM>,
25 sm: StateMachine<'d, T, SM>,
26}
27
28impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> {
29 pub fn new(
30 pio: &mut Common<'d, T>,
31 mut sm: StateMachine<'d, T, SM>,
32 irq: Irq<'d, T, SM>,
33 pin0: impl PioPin,
34 pin1: impl PioPin,
35 pin2: impl PioPin,
36 pin3: impl PioPin,
37 ) -> Self {
38 let prg = pio_proc::pio_asm!(
39 "pull block",
40 "mov x, osr",
41 "pull block",
42 "mov y, osr",
43 "jmp !x end",
44 "loop:",
45 "jmp !osre step",
46 "mov osr, y",
47 "step:",
48 "out pins, 4 [31]"
49 "jmp x-- loop",
50 "end:",
51 "irq 0 rel"
52 );
53 let pin0 = pio.make_pio_pin(pin0);
54 let pin1 = pio.make_pio_pin(pin1);
55 let pin2 = pio.make_pio_pin(pin2);
56 let pin3 = pio.make_pio_pin(pin3);
57 sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]);
58 let mut cfg = Config::default();
59 cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]);
60 cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed();
61 cfg.use_program(&pio.load_program(&prg.program), &[]);
62 sm.set_config(&cfg);
63 sm.set_enable(true);
64 Self { irq, sm }
65 }
66
67 // Set pulse frequency
68 pub fn set_frequency(&mut self, freq: u32) {
69 let clock_divider: FixedU32<U8> = (125_000_000 / (freq * 136)).to_fixed();
70 assert!(clock_divider <= 65536, "clkdiv must be <= 65536");
71 assert!(clock_divider >= 1, "clkdiv must be >= 1");
72 self.sm.set_clock_divider(clock_divider);
73 self.sm.clkdiv_restart();
74 }
75
76 // Full step, one phase
77 pub async fn step(&mut self, steps: i32) {
78 if steps > 0 {
79 self.run(steps, 0b1000_0100_0010_0001_1000_0100_0010_0001).await
80 } else {
81 self.run(-steps, 0b0001_0010_0100_1000_0001_0010_0100_1000).await
82 }
83 }
84
85 // Full step, two phase
86 pub async fn step2(&mut self, steps: i32) {
87 if steps > 0 {
88 self.run(steps, 0b1001_1100_0110_0011_1001_1100_0110_0011).await
89 } else {
90 self.run(-steps, 0b0011_0110_1100_1001_0011_0110_1100_1001).await
91 }
92 }
93
94 // Half step
95 pub async fn step_half(&mut self, steps: i32) {
96 if steps > 0 {
97 self.run(steps, 0b1001_1000_1100_0100_0110_0010_0011_0001).await
98 } else {
99 self.run(-steps, 0b0001_0011_0010_0110_0100_1100_1000_1001).await
100 }
101 }
102
103 async fn run(&mut self, steps: i32, pattern: u32) {
104 self.sm.tx().wait_push(steps as u32).await;
105 self.sm.tx().wait_push(pattern).await;
106 let drop = OnDrop::new(|| {
107 self.sm.clear_fifos();
108 unsafe {
109 self.sm.exec_instr(
110 pio::InstructionOperands::JMP {
111 address: 0,
112 condition: pio::JmpCondition::Always,
113 }
114 .encode(),
115 );
116 }
117 });
118 self.irq.wait().await;
119 drop.defuse();
120 }
121}
122
123struct OnDrop<F: FnOnce()> {
124 f: MaybeUninit<F>,
125}
126
127impl<F: FnOnce()> OnDrop<F> {
128 pub fn new(f: F) -> Self {
129 Self { f: MaybeUninit::new(f) }
130 }
131
132 pub fn defuse(self) {
133 mem::forget(self)
134 }
135}
136
137impl<F: FnOnce()> Drop for OnDrop<F> {
138 fn drop(&mut self) {
139 unsafe { self.f.as_ptr().read()() }
140 }
141}
142
143#[embassy_executor::main] 20#[embassy_executor::main]
144async fn main(_spawner: Spawner) { 21async fn main(_spawner: Spawner) {
145 let p = embassy_rp::init(Default::default()); 22 let p = embassy_rp::init(Default::default());
@@ -147,14 +24,18 @@ async fn main(_spawner: Spawner) {
147 mut common, irq0, sm0, .. 24 mut common, irq0, sm0, ..
148 } = Pio::new(p.PIO0, Irqs); 25 } = Pio::new(p.PIO0, Irqs);
149 26
150 let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7); 27 let prg = PioStepperProgram::new(&mut common);
28 let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7, &prg);
151 stepper.set_frequency(120); 29 stepper.set_frequency(120);
152 loop { 30 loop {
153 info!("CW full steps"); 31 info!("CW full steps");
154 stepper.step(1000).await; 32 stepper.step(1000).await;
155 33
156 info!("CCW full steps, drop after 1 sec"); 34 info!("CCW full steps, drop after 1 sec");
157 if let Err(_) = with_timeout(Duration::from_secs(1), stepper.step(i32::MIN)).await { 35 if with_timeout(Duration::from_secs(1), stepper.step(-i32::MAX))
36 .await
37 .is_err()
38 {
158 info!("Time's up!"); 39 info!("Time's up!");
159 Timer::after(Duration::from_secs(1)).await; 40 Timer::after(Duration::from_secs(1)).await;
160 } 41 }
diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs
index 53b696309..aaf2a524f 100644
--- a/examples/rp/src/bin/pio_uart.rs
+++ b/examples/rp/src/bin/pio_uart.rs
@@ -13,10 +13,10 @@
13use defmt::{info, panic, trace}; 13use defmt::{info, panic, trace};
14use embassy_executor::Spawner; 14use embassy_executor::Spawner;
15use embassy_futures::join::{join, join3}; 15use embassy_futures::join::{join, join3};
16use embassy_rp::bind_interrupts;
17use embassy_rp::peripherals::{PIO0, USB}; 16use embassy_rp::peripherals::{PIO0, USB};
18use embassy_rp::pio::InterruptHandler as PioInterruptHandler; 17use embassy_rp::pio_programs::uart::{PioUartRx, PioUartRxProgram, PioUartTx, PioUartTxProgram};
19use embassy_rp::usb::{Driver, Instance, InterruptHandler}; 18use embassy_rp::usb::{Driver, Instance, InterruptHandler};
19use embassy_rp::{bind_interrupts, pio};
20use embassy_sync::blocking_mutex::raw::NoopRawMutex; 20use embassy_sync::blocking_mutex::raw::NoopRawMutex;
21use embassy_sync::pipe::Pipe; 21use embassy_sync::pipe::Pipe;
22use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; 22use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State};
@@ -25,13 +25,11 @@ use embassy_usb::{Builder, Config};
25use embedded_io_async::{Read, Write}; 25use embedded_io_async::{Read, Write};
26use {defmt_rtt as _, panic_probe as _}; 26use {defmt_rtt as _, panic_probe as _};
27 27
28use crate::uart::PioUart; 28//use crate::uart::PioUart;
29use crate::uart_rx::PioUartRx;
30use crate::uart_tx::PioUartTx;
31 29
32bind_interrupts!(struct Irqs { 30bind_interrupts!(struct Irqs {
33 USBCTRL_IRQ => InterruptHandler<USB>; 31 USBCTRL_IRQ => InterruptHandler<USB>;
34 PIO0_IRQ_0 => PioInterruptHandler<PIO0>; 32 PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;
35}); 33});
36 34
37#[embassy_executor::main] 35#[embassy_executor::main]
@@ -85,8 +83,15 @@ async fn main(_spawner: Spawner) {
85 let usb_fut = usb.run(); 83 let usb_fut = usb.run();
86 84
87 // PIO UART setup 85 // PIO UART setup
88 let uart = PioUart::new(9600, p.PIO0, p.PIN_4, p.PIN_5); 86 let pio::Pio {
89 let (mut uart_tx, mut uart_rx) = uart.split(); 87 mut common, sm0, sm1, ..
88 } = pio::Pio::new(p.PIO0, Irqs);
89
90 let tx_program = PioUartTxProgram::new(&mut common);
91 let mut uart_tx = PioUartTx::new(9600, &mut common, sm0, p.PIN_4, &tx_program);
92
93 let rx_program = PioUartRxProgram::new(&mut common);
94 let mut uart_rx = PioUartRx::new(9600, &mut common, sm1, p.PIN_5, &rx_program);
90 95
91 // Pipe setup 96 // Pipe setup
92 let mut usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new(); 97 let mut usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new();
@@ -163,8 +168,8 @@ async fn usb_write<'d, T: Instance + 'd>(
163} 168}
164 169
165/// Read from the UART and write it to the USB TX pipe 170/// Read from the UART and write it to the USB TX pipe
166async fn uart_read( 171async fn uart_read<PIO: pio::Instance, const SM: usize>(
167 uart_rx: &mut PioUartRx<'_>, 172 uart_rx: &mut PioUartRx<'_, PIO, SM>,
168 usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, 173 usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>,
169) -> ! { 174) -> ! {
170 let mut buf = [0; 64]; 175 let mut buf = [0; 64];
@@ -180,8 +185,8 @@ async fn uart_read(
180} 185}
181 186
182/// Read from the UART TX pipe and write it to the UART 187/// Read from the UART TX pipe and write it to the UART
183async fn uart_write( 188async fn uart_write<PIO: pio::Instance, const SM: usize>(
184 uart_tx: &mut PioUartTx<'_>, 189 uart_tx: &mut PioUartTx<'_, PIO, SM>,
185 uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, 190 uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>,
186) -> ! { 191) -> ! {
187 let mut buf = [0; 64]; 192 let mut buf = [0; 64];
@@ -192,197 +197,3 @@ async fn uart_write(
192 let _ = uart_tx.write(&data).await; 197 let _ = uart_tx.write(&data).await;
193 } 198 }
194} 199}
195
196mod uart {
197 use embassy_rp::peripherals::PIO0;
198 use embassy_rp::pio::{Pio, PioPin};
199 use embassy_rp::Peripheral;
200
201 use crate::uart_rx::PioUartRx;
202 use crate::uart_tx::PioUartTx;
203 use crate::Irqs;
204
205 pub struct PioUart<'a> {
206 tx: PioUartTx<'a>,
207 rx: PioUartRx<'a>,
208 }
209
210 impl<'a> PioUart<'a> {
211 pub fn new(
212 baud: u64,
213 pio: impl Peripheral<P = PIO0> + 'a,
214 tx_pin: impl PioPin,
215 rx_pin: impl PioPin,
216 ) -> PioUart<'a> {
217 let Pio {
218 mut common, sm0, sm1, ..
219 } = Pio::new(pio, Irqs);
220
221 let tx = PioUartTx::new(&mut common, sm0, tx_pin, baud);
222 let rx = PioUartRx::new(&mut common, sm1, rx_pin, baud);
223
224 PioUart { tx, rx }
225 }
226
227 pub fn split(self) -> (PioUartTx<'a>, PioUartRx<'a>) {
228 (self.tx, self.rx)
229 }
230 }
231}
232
233mod uart_tx {
234 use core::convert::Infallible;
235
236 use embassy_rp::gpio::Level;
237 use embassy_rp::peripherals::PIO0;
238 use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine};
239 use embedded_io_async::{ErrorType, Write};
240 use fixed::traits::ToFixed;
241 use fixed_macro::types::U56F8;
242
243 pub struct PioUartTx<'a> {
244 sm_tx: StateMachine<'a, PIO0, 0>,
245 }
246
247 impl<'a> PioUartTx<'a> {
248 pub fn new(
249 common: &mut Common<'a, PIO0>,
250 mut sm_tx: StateMachine<'a, PIO0, 0>,
251 tx_pin: impl PioPin,
252 baud: u64,
253 ) -> Self {
254 let prg = pio_proc::pio_asm!(
255 r#"
256 .side_set 1 opt
257
258 ; An 8n1 UART transmit program.
259 ; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin.
260
261 pull side 1 [7] ; Assert stop bit, or stall with line in idle state
262 set x, 7 side 0 [7] ; Preload bit counter, assert start bit for 8 clocks
263 bitloop: ; This loop will run 8 times (8n1 UART)
264 out pins, 1 ; Shift 1 bit from OSR to the first OUT pin
265 jmp x-- bitloop [6] ; Each loop iteration is 8 cycles.
266 "#
267 );
268 let tx_pin = common.make_pio_pin(tx_pin);
269 sm_tx.set_pins(Level::High, &[&tx_pin]);
270 sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]);
271
272 let mut cfg = Config::default();
273
274 cfg.set_out_pins(&[&tx_pin]);
275 cfg.use_program(&common.load_program(&prg.program), &[&tx_pin]);
276 cfg.shift_out.auto_fill = false;
277 cfg.shift_out.direction = ShiftDirection::Right;
278 cfg.fifo_join = FifoJoin::TxOnly;
279 cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed();
280 sm_tx.set_config(&cfg);
281 sm_tx.set_enable(true);
282
283 Self { sm_tx }
284 }
285
286 pub async fn write_u8(&mut self, data: u8) {
287 self.sm_tx.tx().wait_push(data as u32).await;
288 }
289 }
290
291 impl ErrorType for PioUartTx<'_> {
292 type Error = Infallible;
293 }
294
295 impl Write for PioUartTx<'_> {
296 async fn write(&mut self, buf: &[u8]) -> Result<usize, Infallible> {
297 for byte in buf {
298 self.write_u8(*byte).await;
299 }
300 Ok(buf.len())
301 }
302 }
303}
304
305mod uart_rx {
306 use core::convert::Infallible;
307
308 use embassy_rp::gpio::Level;
309 use embassy_rp::peripherals::PIO0;
310 use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine};
311 use embedded_io_async::{ErrorType, Read};
312 use fixed::traits::ToFixed;
313 use fixed_macro::types::U56F8;
314
315 pub struct PioUartRx<'a> {
316 sm_rx: StateMachine<'a, PIO0, 1>,
317 }
318
319 impl<'a> PioUartRx<'a> {
320 pub fn new(
321 common: &mut Common<'a, PIO0>,
322 mut sm_rx: StateMachine<'a, PIO0, 1>,
323 rx_pin: impl PioPin,
324 baud: u64,
325 ) -> Self {
326 let prg = pio_proc::pio_asm!(
327 r#"
328 ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and
329 ; break conditions more gracefully.
330 ; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.
331
332 start:
333 wait 0 pin 0 ; Stall until start bit is asserted
334 set x, 7 [10] ; Preload bit counter, then delay until halfway through
335 rx_bitloop: ; the first data bit (12 cycles incl wait, set).
336 in pins, 1 ; Shift data bit into ISR
337 jmp x-- rx_bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles
338 jmp pin good_rx_stop ; Check stop bit (should be high)
339
340 irq 4 rel ; Either a framing error or a break. Set a sticky flag,
341 wait 1 pin 0 ; and wait for line to return to idle state.
342 jmp start ; Don't push data if we didn't see good framing.
343
344 good_rx_stop: ; No delay before returning to start; a little slack is
345 in null 24
346 push ; important in case the TX clock is slightly too fast.
347 "#
348 );
349 let mut cfg = Config::default();
350 cfg.use_program(&common.load_program(&prg.program), &[]);
351
352 let rx_pin = common.make_pio_pin(rx_pin);
353 sm_rx.set_pins(Level::High, &[&rx_pin]);
354 cfg.set_in_pins(&[&rx_pin]);
355 cfg.set_jmp_pin(&rx_pin);
356 sm_rx.set_pin_dirs(Direction::In, &[&rx_pin]);
357
358 cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed();
359 cfg.shift_in.auto_fill = false;
360 cfg.shift_in.direction = ShiftDirection::Right;
361 cfg.shift_in.threshold = 32;
362 cfg.fifo_join = FifoJoin::RxOnly;
363 sm_rx.set_config(&cfg);
364 sm_rx.set_enable(true);
365
366 Self { sm_rx }
367 }
368
369 pub async fn read_u8(&mut self) -> u8 {
370 self.sm_rx.rx().wait_pull().await as u8
371 }
372 }
373
374 impl ErrorType for PioUartRx<'_> {
375 type Error = Infallible;
376 }
377
378 impl Read for PioUartRx<'_> {
379 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Infallible> {
380 let mut i = 0;
381 while i < buf.len() {
382 buf[i] = self.read_u8().await;
383 i += 1;
384 }
385 Ok(i)
386 }
387 }
388}
diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs
index ac145933c..d1fcfc471 100644
--- a/examples/rp/src/bin/pio_ws2812.rs
+++ b/examples/rp/src/bin/pio_ws2812.rs
@@ -6,15 +6,11 @@
6 6
7use defmt::*; 7use defmt::*;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_rp::dma::{AnyChannel, Channel}; 9use embassy_rp::bind_interrupts;
10use embassy_rp::peripherals::PIO0; 10use embassy_rp::peripherals::PIO0;
11use embassy_rp::pio::{ 11use embassy_rp::pio::{InterruptHandler, Pio};
12 Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, 12use embassy_rp::pio_programs::ws2812::{PioWs2812, PioWs2812Program};
13}; 13use embassy_time::{Duration, Ticker};
14use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef};
15use embassy_time::{Duration, Ticker, Timer};
16use fixed::types::U24F8;
17use fixed_macro::fixed;
18use smart_leds::RGB8; 14use smart_leds::RGB8;
19use {defmt_rtt as _, panic_probe as _}; 15use {defmt_rtt as _, panic_probe as _};
20 16
@@ -22,96 +18,6 @@ bind_interrupts!(struct Irqs {
22 PIO0_IRQ_0 => InterruptHandler<PIO0>; 18 PIO0_IRQ_0 => InterruptHandler<PIO0>;
23}); 19});
24 20
25pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> {
26 dma: PeripheralRef<'d, AnyChannel>,
27 sm: StateMachine<'d, P, S>,
28}
29
30impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> {
31 pub fn new(
32 pio: &mut Common<'d, P>,
33 mut sm: StateMachine<'d, P, S>,
34 dma: impl Peripheral<P = impl Channel> + 'd,
35 pin: impl PioPin,
36 ) -> Self {
37 into_ref!(dma);
38
39 // Setup sm0
40
41 // prepare the PIO program
42 let side_set = pio::SideSet::new(false, 1, false);
43 let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set);
44
45 const T1: u8 = 2; // start bit
46 const T2: u8 = 5; // data bit
47 const T3: u8 = 3; // stop bit
48 const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32;
49
50 let mut wrap_target = a.label();
51 let mut wrap_source = a.label();
52 let mut do_zero = a.label();
53 a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0);
54 a.bind(&mut wrap_target);
55 // Do stop bit
56 a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0);
57 // Do start bit
58 a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1);
59 // Do data bit = 1
60 a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1);
61 a.bind(&mut do_zero);
62 // Do data bit = 0
63 a.nop_with_delay_and_side_set(T2 - 1, 0);
64 a.bind(&mut wrap_source);
65
66 let prg = a.assemble_with_wrap(wrap_source, wrap_target);
67 let mut cfg = Config::default();
68
69 // Pin config
70 let out_pin = pio.make_pio_pin(pin);
71 cfg.set_out_pins(&[&out_pin]);
72 cfg.set_set_pins(&[&out_pin]);
73
74 cfg.use_program(&pio.load_program(&prg), &[&out_pin]);
75
76 // Clock config, measured in kHz to avoid overflows
77 // TODO CLOCK_FREQ should come from embassy_rp
78 let clock_freq = U24F8::from_num(clocks::clk_sys_freq() / 1000);
79 let ws2812_freq = fixed!(800: U24F8);
80 let bit_freq = ws2812_freq * CYCLES_PER_BIT;
81 cfg.clock_divider = clock_freq / bit_freq;
82
83 // FIFO config
84 cfg.fifo_join = FifoJoin::TxOnly;
85 cfg.shift_out = ShiftConfig {
86 auto_fill: true,
87 threshold: 24,
88 direction: ShiftDirection::Left,
89 };
90
91 sm.set_config(&cfg);
92 sm.set_enable(true);
93
94 Self {
95 dma: dma.map_into(),
96 sm,
97 }
98 }
99
100 pub async fn write(&mut self, colors: &[RGB8; N]) {
101 // Precompute the word bytes from the colors
102 let mut words = [0u32; N];
103 for i in 0..N {
104 let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8);
105 words[i] = word;
106 }
107
108 // DMA transfer
109 self.sm.tx().dma_push(self.dma.reborrow(), &words).await;
110
111 Timer::after_micros(55).await;
112 }
113}
114
115/// Input a value 0 to 255 to get a color value 21/// Input a value 0 to 255 to get a color value
116/// The colours are a transition r - g - b - back to r. 22/// The colours are a transition r - g - b - back to r.
117fn wheel(mut wheel_pos: u8) -> RGB8 { 23fn wheel(mut wheel_pos: u8) -> RGB8 {
@@ -142,7 +48,8 @@ async fn main(_spawner: Spawner) {
142 // Common neopixel pins: 48 // Common neopixel pins:
143 // Thing plus: 8 49 // Thing plus: 8
144 // Adafruit Feather: 16; Adafruit Feather+RFM95: 4 50 // Adafruit Feather: 16; Adafruit Feather+RFM95: 4
145 let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16); 51 let program = PioWs2812Program::new(&mut common);
52 let mut ws2812 = PioWs2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16, &program);
146 53
147 // Loop forever making RGB values and pushing them out to the WS2812. 54 // Loop forever making RGB values and pushing them out to the WS2812.
148 let mut ticker = Ticker::every(Duration::from_millis(10)); 55 let mut ticker = Ticker::every(Duration::from_millis(10));
diff --git a/examples/rp/src/bin/pwm.rs b/examples/rp/src/bin/pwm.rs
index 26e233260..2f5f94870 100644
--- a/examples/rp/src/bin/pwm.rs
+++ b/examples/rp/src/bin/pwm.rs
@@ -1,24 +1,36 @@
1//! This example shows how to use PWM (Pulse Width Modulation) in the RP2040 chip. 1//! This example shows how to use PWM (Pulse Width Modulation) in the RP2040 chip.
2//! 2//!
3//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin. 3//! We demonstrate two ways of using PWM:
4//! 1. Via config
5//! 2. Via setting a duty cycle
4 6
5#![no_std] 7#![no_std]
6#![no_main] 8#![no_main]
7 9
8use defmt::*; 10use defmt::*;
9use embassy_executor::Spawner; 11use embassy_executor::Spawner;
10use embassy_rp::pwm::{Config, Pwm}; 12use embassy_rp::peripherals::{PIN_25, PIN_4, PWM_SLICE2, PWM_SLICE4};
13use embassy_rp::pwm::{Config, Pwm, SetDutyCycle};
11use embassy_time::Timer; 14use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _}; 15use {defmt_rtt as _, panic_probe as _};
13 16
14#[embassy_executor::main] 17#[embassy_executor::main]
15async fn main(_spawner: Spawner) { 18async fn main(spawner: Spawner) {
16 let p = embassy_rp::init(Default::default()); 19 let p = embassy_rp::init(Default::default());
20 spawner.spawn(pwm_set_config(p.PWM_SLICE4, p.PIN_25)).unwrap();
21 spawner.spawn(pwm_set_dutycycle(p.PWM_SLICE2, p.PIN_4)).unwrap();
22}
17 23
18 let mut c: Config = Default::default(); 24/// Demonstrate PWM by modifying & applying the config
19 c.top = 0x8000; 25///
26/// Using the onboard led, if You are using a different Board than plain Pico2 (i.e. W variant)
27/// you must use another slice & pin and an appropriate resistor.
28#[embassy_executor::task]
29async fn pwm_set_config(slice4: PWM_SLICE4, pin25: PIN_25) {
30 let mut c = Config::default();
31 c.top = 32_768;
20 c.compare_b = 8; 32 c.compare_b = 8;
21 let mut pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, c.clone()); 33 let mut pwm = Pwm::new_output_b(slice4, pin25, c.clone());
22 34
23 loop { 35 loop {
24 info!("current LED duty cycle: {}/32768", c.compare_b); 36 info!("current LED duty cycle: {}/32768", c.compare_b);
@@ -27,3 +39,41 @@ async fn main(_spawner: Spawner) {
27 pwm.set_config(&c); 39 pwm.set_config(&c);
28 } 40 }
29} 41}
42
43/// Demonstrate PWM by setting duty cycle
44///
45/// Using GP4 in Slice2, make sure to use an appropriate resistor.
46#[embassy_executor::task]
47async fn pwm_set_dutycycle(slice2: PWM_SLICE2, pin4: PIN_4) {
48 // If we aim for a specific frequency, here is how we can calculate the top value.
49 // The top value sets the period of the PWM cycle, so a counter goes from 0 to top and then wraps around to 0.
50 // Every such wraparound is one PWM cycle. So here is how we get 25KHz:
51 let desired_freq_hz = 25_000;
52 let clock_freq_hz = embassy_rp::clocks::clk_sys_freq();
53 let divider = 16u8;
54 let period = (clock_freq_hz / (desired_freq_hz * divider as u32)) as u16 - 1;
55
56 let mut c = Config::default();
57 c.top = period;
58 c.divider = divider.into();
59
60 let mut pwm = Pwm::new_output_a(slice2, pin4, c.clone());
61
62 loop {
63 // 100% duty cycle, fully on
64 pwm.set_duty_cycle_fully_on().unwrap();
65 Timer::after_secs(1).await;
66
67 // 66% duty cycle. Expressed as simple percentage.
68 pwm.set_duty_cycle_percent(66).unwrap();
69 Timer::after_secs(1).await;
70
71 // 25% duty cycle. Expressed as 32768/4 = 8192.
72 pwm.set_duty_cycle(c.top / 4).unwrap();
73 Timer::after_secs(1).await;
74
75 // 0% duty cycle, fully off.
76 pwm.set_duty_cycle_fully_off().unwrap();
77 Timer::after_secs(1).await;
78 }
79}
diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs
index e937b9d0a..dd114a4ae 100644
--- a/examples/rp/src/bin/spi_display.rs
+++ b/examples/rp/src/bin/spi_display.rs
@@ -9,11 +9,12 @@
9use core::cell::RefCell; 9use core::cell::RefCell;
10 10
11use defmt::*; 11use defmt::*;
12use display_interface_spi::SPIInterface;
12use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig; 13use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig;
13use embassy_executor::Spawner; 14use embassy_executor::Spawner;
14use embassy_rp::gpio::{Level, Output}; 15use embassy_rp::gpio::{Level, Output};
15use embassy_rp::spi; 16use embassy_rp::spi;
16use embassy_rp::spi::{Blocking, Spi}; 17use embassy_rp::spi::Spi;
17use embassy_sync::blocking_mutex::raw::NoopRawMutex; 18use embassy_sync::blocking_mutex::raw::NoopRawMutex;
18use embassy_sync::blocking_mutex::Mutex; 19use embassy_sync::blocking_mutex::Mutex;
19use embassy_time::Delay; 20use embassy_time::Delay;
@@ -24,10 +25,11 @@ use embedded_graphics::pixelcolor::Rgb565;
24use embedded_graphics::prelude::*; 25use embedded_graphics::prelude::*;
25use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; 26use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle};
26use embedded_graphics::text::Text; 27use embedded_graphics::text::Text;
27use st7789::{Orientation, ST7789}; 28use mipidsi::models::ST7789;
29use mipidsi::options::{Orientation, Rotation};
30use mipidsi::Builder;
28use {defmt_rtt as _, panic_probe as _}; 31use {defmt_rtt as _, panic_probe as _};
29 32
30use crate::my_display_interface::SPIDeviceInterface;
31use crate::touch::Touch; 33use crate::touch::Touch;
32 34
33const DISPLAY_FREQ: u32 = 64_000_000; 35const DISPLAY_FREQ: u32 = 64_000_000;
@@ -58,7 +60,7 @@ async fn main(_spawner: Spawner) {
58 touch_config.phase = spi::Phase::CaptureOnSecondTransition; 60 touch_config.phase = spi::Phase::CaptureOnSecondTransition;
59 touch_config.polarity = spi::Polarity::IdleHigh; 61 touch_config.polarity = spi::Polarity::IdleHigh;
60 62
61 let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone()); 63 let spi = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone());
62 let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi)); 64 let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi));
63 65
64 let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config); 66 let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config);
@@ -74,17 +76,15 @@ async fn main(_spawner: Spawner) {
74 let _bl = Output::new(bl, Level::High); 76 let _bl = Output::new(bl, Level::High);
75 77
76 // display interface abstraction from SPI and DC 78 // display interface abstraction from SPI and DC
77 let di = SPIDeviceInterface::new(display_spi, dcx); 79 let di = SPIInterface::new(display_spi, dcx);
78 80
79 // create driver 81 // Define the display from the display interface and initialize it
80 let mut display = ST7789::new(di, rst, 240, 320); 82 let mut display = Builder::new(ST7789, di)
81 83 .display_size(240, 320)
82 // initialize 84 .reset_pin(rst)
83 display.init(&mut Delay).unwrap(); 85 .orientation(Orientation::new().rotate(Rotation::Deg90))
84 86 .init(&mut Delay)
85 // set default orientation 87 .unwrap();
86 display.set_orientation(Orientation::Landscape).unwrap();
87
88 display.clear(Rgb565::BLACK).unwrap(); 88 display.clear(Rgb565::BLACK).unwrap();
89 89
90 let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86); 90 let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86);
@@ -175,138 +175,3 @@ mod touch {
175 } 175 }
176 } 176 }
177} 177}
178
179mod my_display_interface {
180 use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand};
181 use embedded_hal_1::digital::OutputPin;
182 use embedded_hal_1::spi::SpiDevice;
183
184 /// SPI display interface.
185 ///
186 /// This combines the SPI peripheral and a data/command pin
187 pub struct SPIDeviceInterface<SPI, DC> {
188 spi: SPI,
189 dc: DC,
190 }
191
192 impl<SPI, DC> SPIDeviceInterface<SPI, DC>
193 where
194 SPI: SpiDevice,
195 DC: OutputPin,
196 {
197 /// Create new SPI interface for communciation with a display driver
198 pub fn new(spi: SPI, dc: DC) -> Self {
199 Self { spi, dc }
200 }
201 }
202
203 impl<SPI, DC> WriteOnlyDataCommand for SPIDeviceInterface<SPI, DC>
204 where
205 SPI: SpiDevice,
206 DC: OutputPin,
207 {
208 fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> {
209 // 1 = data, 0 = command
210 self.dc.set_low().map_err(|_| DisplayError::DCError)?;
211
212 send_u8(&mut self.spi, cmds).map_err(|_| DisplayError::BusWriteError)?;
213 Ok(())
214 }
215
216 fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> {
217 // 1 = data, 0 = command
218 self.dc.set_high().map_err(|_| DisplayError::DCError)?;
219
220 send_u8(&mut self.spi, buf).map_err(|_| DisplayError::BusWriteError)?;
221 Ok(())
222 }
223 }
224
225 fn send_u8<T: SpiDevice>(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> {
226 match words {
227 DataFormat::U8(slice) => spi.write(slice),
228 DataFormat::U16(slice) => {
229 use byte_slice_cast::*;
230 spi.write(slice.as_byte_slice())
231 }
232 DataFormat::U16LE(slice) => {
233 use byte_slice_cast::*;
234 for v in slice.as_mut() {
235 *v = v.to_le();
236 }
237 spi.write(slice.as_byte_slice())
238 }
239 DataFormat::U16BE(slice) => {
240 use byte_slice_cast::*;
241 for v in slice.as_mut() {
242 *v = v.to_be();
243 }
244 spi.write(slice.as_byte_slice())
245 }
246 DataFormat::U8Iter(iter) => {
247 let mut buf = [0; 32];
248 let mut i = 0;
249
250 for v in iter.into_iter() {
251 buf[i] = v;
252 i += 1;
253
254 if i == buf.len() {
255 spi.write(&buf)?;
256 i = 0;
257 }
258 }
259
260 if i > 0 {
261 spi.write(&buf[..i])?;
262 }
263
264 Ok(())
265 }
266 DataFormat::U16LEIter(iter) => {
267 use byte_slice_cast::*;
268 let mut buf = [0; 32];
269 let mut i = 0;
270
271 for v in iter.map(u16::to_le) {
272 buf[i] = v;
273 i += 1;
274
275 if i == buf.len() {
276 spi.write(&buf.as_byte_slice())?;
277 i = 0;
278 }
279 }
280
281 if i > 0 {
282 spi.write(&buf[..i].as_byte_slice())?;
283 }
284
285 Ok(())
286 }
287 DataFormat::U16BEIter(iter) => {
288 use byte_slice_cast::*;
289 let mut buf = [0; 64];
290 let mut i = 0;
291 let len = buf.len();
292
293 for v in iter.map(u16::to_be) {
294 buf[i] = v;
295 i += 1;
296
297 if i == len {
298 spi.write(&buf.as_byte_slice())?;
299 i = 0;
300 }
301 }
302
303 if i > 0 {
304 spi.write(&buf[..i].as_byte_slice())?;
305 }
306
307 Ok(())
308 }
309 _ => unimplemented!(),
310 }
311 }
312}
diff --git a/examples/rp/src/bin/spi_gc9a01.rs b/examples/rp/src/bin/spi_gc9a01.rs
new file mode 100644
index 000000000..30afc253d
--- /dev/null
+++ b/examples/rp/src/bin/spi_gc9a01.rs
@@ -0,0 +1,126 @@
1//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.
2//!
3//! Example written for a display using the GC9A01 chip. Possibly the Waveshare RP2040-LCD-1.28
4//! (https://www.waveshare.com/wiki/RP2040-LCD-1.28)
5
6#![no_std]
7#![no_main]
8
9use core::cell::RefCell;
10
11use defmt::*;
12use display_interface_spi::SPIInterface;
13use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig;
14use embassy_executor::Spawner;
15use embassy_rp::clocks::RoscRng;
16use embassy_rp::gpio::{Level, Output};
17use embassy_rp::spi;
18use embassy_rp::spi::{Blocking, Spi};
19use embassy_sync::blocking_mutex::raw::NoopRawMutex;
20use embassy_sync::blocking_mutex::Mutex;
21use embassy_time::{Delay, Duration, Timer};
22use embedded_graphics::image::{Image, ImageRawLE};
23use embedded_graphics::pixelcolor::Rgb565;
24use embedded_graphics::prelude::*;
25use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle};
26use mipidsi::models::GC9A01;
27use mipidsi::options::{ColorInversion, ColorOrder};
28use mipidsi::Builder;
29use rand_core::RngCore;
30use {defmt_rtt as _, panic_probe as _};
31
32const DISPLAY_FREQ: u32 = 64_000_000;
33const LCD_X_RES: i32 = 240;
34const LCD_Y_RES: i32 = 240;
35const FERRIS_WIDTH: u32 = 86;
36const FERRIS_HEIGHT: u32 = 64;
37
38#[embassy_executor::main]
39async fn main(_spawner: Spawner) {
40 let p = embassy_rp::init(Default::default());
41 let mut rng = RoscRng;
42
43 info!("Hello World!");
44
45 let bl = p.PIN_25;
46 let rst = p.PIN_12;
47 let display_cs = p.PIN_9;
48 let dcx = p.PIN_8;
49 let mosi = p.PIN_11;
50 let clk = p.PIN_10;
51
52 // create SPI
53 let mut display_config = spi::Config::default();
54 display_config.frequency = DISPLAY_FREQ;
55 display_config.phase = spi::Phase::CaptureOnSecondTransition;
56 display_config.polarity = spi::Polarity::IdleHigh;
57
58 let spi: Spi<'_, _, Blocking> = Spi::new_blocking_txonly(p.SPI1, clk, mosi, display_config.clone());
59 let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi));
60
61 let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config);
62 let dcx = Output::new(dcx, Level::Low);
63 let rst = Output::new(rst, Level::Low);
64 // dcx: 0 = command, 1 = data
65
66 // Enable LCD backlight
67 let _bl = Output::new(bl, Level::High);
68
69 // display interface abstraction from SPI and DC
70 let di = SPIInterface::new(display_spi, dcx);
71
72 // Define the display from the display interface and initialize it
73 let mut display = Builder::new(GC9A01, di)
74 .display_size(240, 240)
75 .reset_pin(rst)
76 .color_order(ColorOrder::Bgr)
77 .invert_colors(ColorInversion::Inverted)
78 .init(&mut Delay)
79 .unwrap();
80 display.clear(Rgb565::BLACK).unwrap();
81
82 let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), FERRIS_WIDTH);
83 let mut ferris = Image::new(&raw_image_data, Point::zero());
84
85 let r = rng.next_u32();
86 let mut delta = Point {
87 x: ((r % 10) + 5) as i32,
88 y: (((r >> 8) % 10) + 5) as i32,
89 };
90 loop {
91 // Move Ferris
92 let bb = ferris.bounding_box();
93 let tl = bb.top_left;
94 let br = bb.bottom_right().unwrap();
95 if tl.x < 0 || br.x > LCD_X_RES {
96 delta.x = -delta.x;
97 }
98 if tl.y < 0 || br.y > LCD_Y_RES {
99 delta.y = -delta.y;
100 }
101
102 // Erase ghosting
103 let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::BLACK).build();
104 let mut off = Point { x: 0, y: 0 };
105 if delta.x < 0 {
106 off.x = FERRIS_WIDTH as i32;
107 }
108 Rectangle::new(tl + off, Size::new(delta.x as u32, FERRIS_HEIGHT))
109 .into_styled(style)
110 .draw(&mut display)
111 .unwrap();
112 off = Point { x: 0, y: 0 };
113 if delta.y < 0 {
114 off.y = FERRIS_HEIGHT as i32;
115 }
116 Rectangle::new(tl + off, Size::new(FERRIS_WIDTH, delta.y as u32))
117 .into_styled(style)
118 .draw(&mut display)
119 .unwrap();
120 // Translate Ferris
121 ferris.translate_mut(delta);
122 // Display the image
123 ferris.draw(&mut display).unwrap();
124 Timer::after(Duration::from_millis(50)).await;
125 }
126}
diff --git a/examples/rp/src/bin/spi_sdmmc.rs b/examples/rp/src/bin/spi_sdmmc.rs
index 4cbc82f7b..a60850d0f 100644
--- a/examples/rp/src/bin/spi_sdmmc.rs
+++ b/examples/rp/src/bin/spi_sdmmc.rs
@@ -7,7 +7,6 @@
7#![no_main] 7#![no_main]
8 8
9use defmt::*; 9use defmt::*;
10use embassy_embedded_hal::SetConfig;
11use embassy_executor::Spawner; 10use embassy_executor::Spawner;
12use embassy_rp::spi::Spi; 11use embassy_rp::spi::Spi;
13use embassy_rp::{gpio, spi}; 12use embassy_rp::{gpio, spi};
@@ -51,7 +50,7 @@ async fn main(_spawner: Spawner) {
51 // Now that the card is initialized, the SPI clock can go faster 50 // Now that the card is initialized, the SPI clock can go faster
52 let mut config = spi::Config::default(); 51 let mut config = spi::Config::default();
53 config.frequency = 16_000_000; 52 config.frequency = 16_000_000;
54 sdcard.spi(|dev| dev.bus_mut().set_config(&config)).ok(); 53 sdcard.spi(|dev| dev.bus_mut().set_config(&config));
55 54
56 // Now let's look for volumes (also known as partitions) on our block device. 55 // Now let's look for volumes (also known as partitions) on our block device.
57 // To do this we need a Volume Manager. It will take ownership of the block device. 56 // To do this we need a Volume Manager. It will take ownership of the block device.
diff --git a/examples/rp/src/bin/usb_serial_with_handler.rs b/examples/rp/src/bin/usb_serial_with_handler.rs
new file mode 100644
index 000000000..a9e65be70
--- /dev/null
+++ b/examples/rp/src/bin/usb_serial_with_handler.rs
@@ -0,0 +1,64 @@
1//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip.
2//!
3//! This creates the possibility to send log::info/warn/error/debug! to USB serial port.
4
5#![no_std]
6#![no_main]
7
8use core::str;
9
10use embassy_executor::Spawner;
11use embassy_rp::bind_interrupts;
12use embassy_rp::peripherals::USB;
13use embassy_rp::rom_data::reset_to_usb_boot;
14use embassy_rp::usb::{Driver, InterruptHandler};
15use embassy_time::Timer;
16use embassy_usb_logger::ReceiverHandler;
17use {defmt_rtt as _, panic_probe as _};
18
19bind_interrupts!(struct Irqs {
20 USBCTRL_IRQ => InterruptHandler<USB>;
21});
22
23struct Handler;
24
25impl ReceiverHandler for Handler {
26 async fn handle_data(&self, data: &[u8]) {
27 if let Ok(data) = str::from_utf8(data) {
28 let data = data.trim();
29
30 // If you are using elf2uf2-term with the '-t' flag, then when closing the serial monitor,
31 // this will automatically put the pico into boot mode
32 if data == "q" || data == "elf2uf2-term" {
33 reset_to_usb_boot(0, 0); // Restart the chip
34 } else if data.eq_ignore_ascii_case("hello") {
35 log::info!("World!");
36 } else {
37 log::info!("Recieved: {:?}", data);
38 }
39 }
40 }
41
42 fn new() -> Self {
43 Self
44 }
45}
46
47#[embassy_executor::task]
48async fn logger_task(driver: Driver<'static, USB>) {
49 embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver, Handler);
50}
51
52#[embassy_executor::main]
53async fn main(spawner: Spawner) {
54 let p = embassy_rp::init(Default::default());
55 let driver = Driver::new(p.USB, Irqs);
56 spawner.spawn(logger_task(driver)).unwrap();
57
58 let mut counter = 0;
59 loop {
60 counter += 1;
61 log::info!("Tick {}", counter);
62 Timer::after_secs(1).await;
63 }
64}
diff --git a/examples/rp/src/bin/wifi_blinky.rs b/examples/rp/src/bin/wifi_blinky.rs
index 04a61bbd5..0107a2326 100644
--- a/examples/rp/src/bin/wifi_blinky.rs
+++ b/examples/rp/src/bin/wifi_blinky.rs
@@ -33,8 +33,8 @@ async fn main(spawner: Spawner) {
33 33
34 // To make flashing faster for development, you may want to flash the firmwares independently 34 // To make flashing faster for development, you may want to flash the firmwares independently
35 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: 35 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
36 // probe-rs download 43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000 36 // probe-rs download ../../cyw43-firmware/43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000
37 // probe-rs download 43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000 37 // probe-rs download ../../cyw43-firmware/43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000
38 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) }; 38 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) };
39 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; 39 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
40 40
diff --git a/examples/rp/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs
index 434f0074c..2ef899080 100644
--- a/examples/rp/src/bin/wifi_scan.rs
+++ b/examples/rp/src/bin/wifi_scan.rs
@@ -26,11 +26,6 @@ async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'stat
26 runner.run().await 26 runner.run().await
27} 27}
28 28
29#[embassy_executor::task]
30async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! {
31 runner.run().await
32}
33
34#[embassy_executor::main] 29#[embassy_executor::main]
35async fn main(spawner: Spawner) { 30async fn main(spawner: Spawner) {
36 info!("Hello World!"); 31 info!("Hello World!");
diff --git a/examples/rp23/Cargo.toml b/examples/rp23/Cargo.toml
index 087f6fd69..72eef222d 100644
--- a/examples/rp23/Cargo.toml
+++ b/examples/rp23/Cargo.toml
@@ -7,12 +7,12 @@ license = "MIT OR Apache-2.0"
7 7
8[dependencies] 8[dependencies]
9embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] } 9embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
13embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } 13embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] }
14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } 15embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] }
16embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } 16embassy-net-wiznet = { version = "0.1.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.2.0", path = "../../embassy-usb-logger" } 18embassy-usb-logger = { version = "0.2.0", path = "../../embassy-usb-logger" }
@@ -24,23 +24,24 @@ defmt-rtt = "0.4"
24fixed = "1.23.1" 24fixed = "1.23.1"
25fixed-macro = "1.2" 25fixed-macro = "1.2"
26 26
27# for web request example
28reqwless = { version = "0.12.0", features = ["defmt",]}
29serde = { version = "1.0.203", default-features = false, features = ["derive"] } 27serde = { version = "1.0.203", default-features = false, features = ["derive"] }
30serde-json-core = "0.5.1" 28serde-json-core = "0.5.1"
31 29
32# for assign resources example 30# for assign resources example
33assign-resources = { git = "https://github.com/adamgreig/assign-resources", rev = "94ad10e2729afdf0fd5a77cd12e68409a982f58a" } 31assign-resources = { git = "https://github.com/adamgreig/assign-resources", rev = "94ad10e2729afdf0fd5a77cd12e68409a982f58a" }
34 32
33# for TB6612FNG example
34tb6612fng = "1.0.0"
35
35#cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 36#cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
36cortex-m = { version = "0.7.6", features = ["inline-asm"] } 37cortex-m = { version = "0.7.6", features = ["inline-asm"] }
37cortex-m-rt = "0.7.0" 38cortex-m-rt = "0.7.0"
38critical-section = "1.1" 39critical-section = "1.1"
39panic-probe = { version = "0.3", features = ["print-defmt"] } 40panic-probe = { version = "0.3", features = ["print-defmt"] }
40display-interface-spi = "0.4.1" 41display-interface-spi = "0.5.0"
41embedded-graphics = "0.7.1" 42embedded-graphics = "0.8.1"
42st7789 = "0.6.1" 43mipidsi = "0.8.0"
43display-interface = "0.4.1" 44display-interface = "0.5.0"
44byte-slice-cast = { version = "1.2.0", default-features = false } 45byte-slice-cast = { version = "1.2.0", default-features = false }
45smart-leds = "0.3.0" 46smart-leds = "0.3.0"
46heapless = "0.8" 47heapless = "0.8"
diff --git a/examples/rp23/memory.x b/examples/rp23/memory.x
index 777492062..c803896f6 100644
--- a/examples/rp23/memory.x
+++ b/examples/rp23/memory.x
@@ -31,6 +31,7 @@ SECTIONS {
31 { 31 {
32 __start_block_addr = .; 32 __start_block_addr = .;
33 KEEP(*(.start_block)); 33 KEEP(*(.start_block));
34 KEEP(*(.boot_info));
34 } > FLASH 35 } > FLASH
35 36
36} INSERT AFTER .vector_table; 37} INSERT AFTER .vector_table;
diff --git a/examples/rp23/src/bin/adc.rs b/examples/rp23/src/bin/adc.rs
index d1f053d39..f7db9653a 100644
--- a/examples/rp23/src/bin/adc.rs
+++ b/examples/rp23/src/bin/adc.rs
@@ -1,4 +1,4 @@
1//! This example test the ADC (Analog to Digital Conversion) of the RS2040 pin 26, 27 and 28. 1//! This example test the ADC (Analog to Digital Conversion) of the RP2350A pins 26, 27 and 28.
2//! It also reads the temperature sensor in the chip. 2//! It also reads the temperature sensor in the chip.
3 3
4#![no_std] 4#![no_std]
@@ -17,16 +17,6 @@ use {defmt_rtt as _, panic_probe as _};
17#[used] 17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19 19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info::rp_program_name!(c"example"),
25 embassy_rp::binary_info::rp_cargo_version!(),
26 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info::rp_program_build_attribute!(),
28];
29
30bind_interrupts!(struct Irqs { 20bind_interrupts!(struct Irqs {
31 ADC_IRQ_FIFO => InterruptHandler; 21 ADC_IRQ_FIFO => InterruptHandler;
32}); 22});
diff --git a/examples/rp23/src/bin/adc_dma.rs b/examples/rp23/src/bin/adc_dma.rs
index 5046e5530..a6814c23a 100644
--- a/examples/rp23/src/bin/adc_dma.rs
+++ b/examples/rp23/src/bin/adc_dma.rs
@@ -17,16 +17,6 @@ use {defmt_rtt as _, panic_probe as _};
17#[used] 17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19 19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info::rp_program_name!(c"example"),
25 embassy_rp::binary_info::rp_cargo_version!(),
26 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info::rp_program_build_attribute!(),
28];
29
30bind_interrupts!(struct Irqs { 20bind_interrupts!(struct Irqs {
31 ADC_IRQ_FIFO => InterruptHandler; 21 ADC_IRQ_FIFO => InterruptHandler;
32}); 22});
diff --git a/examples/rp23/src/bin/assign_resources.rs b/examples/rp23/src/bin/assign_resources.rs
index 2f9783917..0d4ad8dc3 100644
--- a/examples/rp23/src/bin/assign_resources.rs
+++ b/examples/rp23/src/bin/assign_resources.rs
@@ -24,16 +24,6 @@ use {defmt_rtt as _, panic_probe as _};
24#[used] 24#[used]
25pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 25pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
26 26
27// Program metadata for `picotool info`
28#[link_section = ".bi_entries"]
29#[used]
30pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
31 embassy_rp::binary_info::rp_program_name!(c"example"),
32 embassy_rp::binary_info::rp_cargo_version!(),
33 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
34 embassy_rp::binary_info::rp_program_build_attribute!(),
35];
36
37#[embassy_executor::main] 27#[embassy_executor::main]
38async fn main(spawner: Spawner) { 28async fn main(spawner: Spawner) {
39 // initialize the peripherals 29 // initialize the peripherals
diff --git a/examples/rp23/src/bin/blinky.rs b/examples/rp23/src/bin/blinky.rs
index 9e45679c8..c1ddbb7d2 100644
--- a/examples/rp23/src/bin/blinky.rs
+++ b/examples/rp23/src/bin/blinky.rs
@@ -17,20 +17,23 @@ use {defmt_rtt as _, panic_probe as _};
17#[used] 17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19 19
20// Program metadata for `picotool info` 20// Program metadata for `picotool info`.
21// This isn't needed, but it's recomended to have these minimal entries.
21#[link_section = ".bi_entries"] 22#[link_section = ".bi_entries"]
22#[used] 23#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ 24pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info::rp_program_name!(c"example"), 25 embassy_rp::binary_info::rp_program_name!(c"Blinky Example"),
26 embassy_rp::binary_info::rp_program_description!(
27 c"This example tests the RP Pico on board LED, connected to gpio 25"
28 ),
25 embassy_rp::binary_info::rp_cargo_version!(), 29 embassy_rp::binary_info::rp_cargo_version!(),
26 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info::rp_program_build_attribute!(), 30 embassy_rp::binary_info::rp_program_build_attribute!(),
28]; 31];
29 32
30#[embassy_executor::main] 33#[embassy_executor::main]
31async fn main(_spawner: Spawner) { 34async fn main(_spawner: Spawner) {
32 let p = embassy_rp::init(Default::default()); 35 let p = embassy_rp::init(Default::default());
33 let mut led = Output::new(p.PIN_2, Level::Low); 36 let mut led = Output::new(p.PIN_25, Level::Low);
34 37
35 loop { 38 loop {
36 info!("led on!"); 39 info!("led on!");
diff --git a/examples/rp23/src/bin/blinky_two_channels.rs b/examples/rp23/src/bin/blinky_two_channels.rs
index 87fc58bbc..ce482858e 100644
--- a/examples/rp23/src/bin/blinky_two_channels.rs
+++ b/examples/rp23/src/bin/blinky_two_channels.rs
@@ -19,16 +19,6 @@ use {defmt_rtt as _, panic_probe as _};
19#[used] 19#[used]
20pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 20pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
21 21
22// Program metadata for `picotool info`
23#[link_section = ".bi_entries"]
24#[used]
25pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
26 embassy_rp::binary_info::rp_program_name!(c"example"),
27 embassy_rp::binary_info::rp_cargo_version!(),
28 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
29 embassy_rp::binary_info::rp_program_build_attribute!(),
30];
31
32enum LedState { 22enum LedState {
33 Toggle, 23 Toggle,
34} 24}
diff --git a/examples/rp23/src/bin/blinky_two_tasks.rs b/examples/rp23/src/bin/blinky_two_tasks.rs
index 40236c53b..5dc62245d 100644
--- a/examples/rp23/src/bin/blinky_two_tasks.rs
+++ b/examples/rp23/src/bin/blinky_two_tasks.rs
@@ -19,16 +19,6 @@ use {defmt_rtt as _, panic_probe as _};
19#[used] 19#[used]
20pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 20pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
21 21
22// Program metadata for `picotool info`
23#[link_section = ".bi_entries"]
24#[used]
25pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
26 embassy_rp::binary_info::rp_program_name!(c"example"),
27 embassy_rp::binary_info::rp_cargo_version!(),
28 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
29 embassy_rp::binary_info::rp_program_build_attribute!(),
30];
31
32type LedType = Mutex<ThreadModeRawMutex, Option<Output<'static>>>; 22type LedType = Mutex<ThreadModeRawMutex, Option<Output<'static>>>;
33static LED: LedType = Mutex::new(None); 23static LED: LedType = Mutex::new(None);
34 24
diff --git a/examples/rp23/src/bin/button.rs b/examples/rp23/src/bin/button.rs
index fb067a370..85f1bcae3 100644
--- a/examples/rp23/src/bin/button.rs
+++ b/examples/rp23/src/bin/button.rs
@@ -14,16 +14,6 @@ use {defmt_rtt as _, panic_probe as _};
14#[used] 14#[used]
15pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 15pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
16 16
17// Program metadata for `picotool info`
18#[link_section = ".bi_entries"]
19#[used]
20pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
21 embassy_rp::binary_info::rp_program_name!(c"example"),
22 embassy_rp::binary_info::rp_cargo_version!(),
23 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
24 embassy_rp::binary_info::rp_program_build_attribute!(),
25];
26
27#[embassy_executor::main] 17#[embassy_executor::main]
28async fn main(_spawner: Spawner) { 18async fn main(_spawner: Spawner) {
29 let p = embassy_rp::init(Default::default()); 19 let p = embassy_rp::init(Default::default());
diff --git a/examples/rp23/src/bin/debounce.rs b/examples/rp23/src/bin/debounce.rs
index e672521ec..4c8b80d92 100644
--- a/examples/rp23/src/bin/debounce.rs
+++ b/examples/rp23/src/bin/debounce.rs
@@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _};
15#[used] 15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17 17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info::rp_program_name!(c"example"),
23 embassy_rp::binary_info::rp_cargo_version!(),
24 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info::rp_program_build_attribute!(),
26];
27
28pub struct Debouncer<'a> { 18pub struct Debouncer<'a> {
29 input: Input<'a>, 19 input: Input<'a>,
30 debounce: Duration, 20 debounce: Duration,
diff --git a/examples/rp23/src/bin/flash.rs b/examples/rp23/src/bin/flash.rs
index 84011e394..28dec24c9 100644
--- a/examples/rp23/src/bin/flash.rs
+++ b/examples/rp23/src/bin/flash.rs
@@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _};
15#[used] 15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17 17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info::rp_program_name!(c"example"),
23 embassy_rp::binary_info::rp_cargo_version!(),
24 embassy_rp::binary_info::rp_program_description!(c"Flash"),
25 embassy_rp::binary_info::rp_program_build_attribute!(),
26];
27
28const ADDR_OFFSET: u32 = 0x100000; 18const ADDR_OFFSET: u32 = 0x100000;
29const FLASH_SIZE: usize = 2 * 1024 * 1024; 19const FLASH_SIZE: usize = 2 * 1024 * 1024;
30 20
diff --git a/examples/rp23/src/bin/gpio_async.rs b/examples/rp23/src/bin/gpio_async.rs
index ff12367bf..bfb9a3f95 100644
--- a/examples/rp23/src/bin/gpio_async.rs
+++ b/examples/rp23/src/bin/gpio_async.rs
@@ -17,16 +17,6 @@ use {defmt_rtt as _, panic_probe as _};
17#[used] 17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19 19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info::rp_program_name!(c"example"),
25 embassy_rp::binary_info::rp_cargo_version!(),
26 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info::rp_program_build_attribute!(),
28];
29
30/// It requires an external signal to be manually triggered on PIN 16. For 20/// It requires an external signal to be manually triggered on PIN 16. For
31/// example, this could be accomplished using an external power source with a 21/// example, this could be accomplished using an external power source with a
32/// button so that it is possible to toggle the signal from low to high. 22/// button so that it is possible to toggle the signal from low to high.
diff --git a/examples/rp23/src/bin/gpout.rs b/examples/rp23/src/bin/gpout.rs
index d2ee55197..3cc2ea938 100644
--- a/examples/rp23/src/bin/gpout.rs
+++ b/examples/rp23/src/bin/gpout.rs
@@ -16,16 +16,6 @@ use {defmt_rtt as _, panic_probe as _};
16#[used] 16#[used]
17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
18 18
19// Program metadata for `picotool info`
20#[link_section = ".bi_entries"]
21#[used]
22pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
23 embassy_rp::binary_info::rp_program_name!(c"example"),
24 embassy_rp::binary_info::rp_cargo_version!(),
25 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
26 embassy_rp::binary_info::rp_program_build_attribute!(),
27];
28
29#[embassy_executor::main] 19#[embassy_executor::main]
30async fn main(_spawner: Spawner) { 20async fn main(_spawner: Spawner) {
31 let p = embassy_rp::init(Default::default()); 21 let p = embassy_rp::init(Default::default());
diff --git a/examples/rp23/src/bin/i2c_async.rs b/examples/rp23/src/bin/i2c_async.rs
index c8d918b56..b30088bae 100644
--- a/examples/rp23/src/bin/i2c_async.rs
+++ b/examples/rp23/src/bin/i2c_async.rs
@@ -20,16 +20,6 @@ use {defmt_rtt as _, panic_probe as _};
20#[used] 20#[used]
21pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 21pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
22 22
23// Program metadata for `picotool info`
24#[link_section = ".bi_entries"]
25#[used]
26pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
27 embassy_rp::binary_info::rp_program_name!(c"example"),
28 embassy_rp::binary_info::rp_cargo_version!(),
29 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
30 embassy_rp::binary_info::rp_program_build_attribute!(),
31];
32
33bind_interrupts!(struct Irqs { 23bind_interrupts!(struct Irqs {
34 I2C1_IRQ => InterruptHandler<I2C1>; 24 I2C1_IRQ => InterruptHandler<I2C1>;
35}); 25});
diff --git a/examples/rp23/src/bin/i2c_async_embassy.rs b/examples/rp23/src/bin/i2c_async_embassy.rs
index cce0abcde..c783a80c5 100644
--- a/examples/rp23/src/bin/i2c_async_embassy.rs
+++ b/examples/rp23/src/bin/i2c_async_embassy.rs
@@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _};
15#[used] 15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17 17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info::rp_program_name!(c"example"),
23 embassy_rp::binary_info::rp_cargo_version!(),
24 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info::rp_program_build_attribute!(),
26];
27
28// Our anonymous hypotetical temperature sensor could be: 18// Our anonymous hypotetical temperature sensor could be:
29// a 12-bit sensor, with 100ms startup time, range of -40*C - 125*C, and precision 0.25*C 19// a 12-bit sensor, with 100ms startup time, range of -40*C - 125*C, and precision 0.25*C
30// It requires no configuration or calibration, works with all i2c bus speeds, 20// It requires no configuration or calibration, works with all i2c bus speeds,
diff --git a/examples/rp23/src/bin/i2c_blocking.rs b/examples/rp23/src/bin/i2c_blocking.rs
index 85c33bf0d..a68677311 100644
--- a/examples/rp23/src/bin/i2c_blocking.rs
+++ b/examples/rp23/src/bin/i2c_blocking.rs
@@ -18,16 +18,6 @@ use {defmt_rtt as _, panic_probe as _};
18#[used] 18#[used]
19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
20 20
21// Program metadata for `picotool info`
22#[link_section = ".bi_entries"]
23#[used]
24pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
25 embassy_rp::binary_info::rp_program_name!(c"example"),
26 embassy_rp::binary_info::rp_cargo_version!(),
27 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
28 embassy_rp::binary_info::rp_program_build_attribute!(),
29];
30
31#[allow(dead_code)] 21#[allow(dead_code)]
32mod mcp23017 { 22mod mcp23017 {
33 pub const ADDR: u8 = 0x20; // default addr 23 pub const ADDR: u8 = 0x20; // default addr
diff --git a/examples/rp23/src/bin/i2c_slave.rs b/examples/rp23/src/bin/i2c_slave.rs
index fb5f3cda1..8817538c0 100644
--- a/examples/rp23/src/bin/i2c_slave.rs
+++ b/examples/rp23/src/bin/i2c_slave.rs
@@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _};
15#[used] 15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17 17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info::rp_program_name!(c"example"),
23 embassy_rp::binary_info::rp_cargo_version!(),
24 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info::rp_program_build_attribute!(),
26];
27
28bind_interrupts!(struct Irqs { 18bind_interrupts!(struct Irqs {
29 I2C0_IRQ => i2c::InterruptHandler<I2C0>; 19 I2C0_IRQ => i2c::InterruptHandler<I2C0>;
30 I2C1_IRQ => i2c::InterruptHandler<I2C1>; 20 I2C1_IRQ => i2c::InterruptHandler<I2C1>;
diff --git a/examples/rp23/src/bin/interrupt.rs b/examples/rp23/src/bin/interrupt.rs
index ee3d9bfe7..d9b662253 100644
--- a/examples/rp23/src/bin/interrupt.rs
+++ b/examples/rp23/src/bin/interrupt.rs
@@ -29,16 +29,6 @@ use {defmt_rtt as _, panic_probe as _};
29#[used] 29#[used]
30pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 30pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
31 31
32// Program metadata for `picotool info`
33#[link_section = ".bi_entries"]
34#[used]
35pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
36 embassy_rp::binary_info::rp_program_name!(c"example"),
37 embassy_rp::binary_info::rp_cargo_version!(),
38 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
39 embassy_rp::binary_info::rp_program_build_attribute!(),
40];
41
42static COUNTER: AtomicU32 = AtomicU32::new(0); 32static COUNTER: AtomicU32 = AtomicU32::new(0);
43static PWM: Mutex<CriticalSectionRawMutex, RefCell<Option<Pwm>>> = Mutex::new(RefCell::new(None)); 33static PWM: Mutex<CriticalSectionRawMutex, RefCell<Option<Pwm>>> = Mutex::new(RefCell::new(None));
44static ADC: Mutex<CriticalSectionRawMutex, RefCell<Option<(Adc<Blocking>, adc::Channel)>>> = 34static ADC: Mutex<CriticalSectionRawMutex, RefCell<Option<(Adc<Blocking>, adc::Channel)>>> =
diff --git a/examples/rp23/src/bin/multicore.rs b/examples/rp23/src/bin/multicore.rs
index 9ab43d7a5..d4d470fa2 100644
--- a/examples/rp23/src/bin/multicore.rs
+++ b/examples/rp23/src/bin/multicore.rs
@@ -20,16 +20,6 @@ use {defmt_rtt as _, panic_probe as _};
20#[used] 20#[used]
21pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 21pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
22 22
23// Program metadata for `picotool info`
24#[link_section = ".bi_entries"]
25#[used]
26pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
27 embassy_rp::binary_info::rp_program_name!(c"example"),
28 embassy_rp::binary_info::rp_cargo_version!(),
29 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
30 embassy_rp::binary_info::rp_program_build_attribute!(),
31];
32
33static mut CORE1_STACK: Stack<4096> = Stack::new(); 23static mut CORE1_STACK: Stack<4096> = Stack::new();
34static EXECUTOR0: StaticCell<Executor> = StaticCell::new(); 24static EXECUTOR0: StaticCell<Executor> = StaticCell::new();
35static EXECUTOR1: StaticCell<Executor> = StaticCell::new(); 25static EXECUTOR1: StaticCell<Executor> = StaticCell::new();
diff --git a/examples/rp23/src/bin/multiprio.rs b/examples/rp23/src/bin/multiprio.rs
index 27cd3656e..787854aa9 100644
--- a/examples/rp23/src/bin/multiprio.rs
+++ b/examples/rp23/src/bin/multiprio.rs
@@ -70,16 +70,6 @@ use {defmt_rtt as _, panic_probe as _};
70#[used] 70#[used]
71pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 71pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
72 72
73// Program metadata for `picotool info`
74#[link_section = ".bi_entries"]
75#[used]
76pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
77 embassy_rp::binary_info::rp_program_name!(c"example"),
78 embassy_rp::binary_info::rp_cargo_version!(),
79 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
80 embassy_rp::binary_info::rp_program_build_attribute!(),
81];
82
83#[embassy_executor::task] 73#[embassy_executor::task]
84async fn run_high() { 74async fn run_high() {
85 loop { 75 loop {
diff --git a/examples/rp23/src/bin/otp.rs b/examples/rp23/src/bin/otp.rs
index 106e514ca..c67c9821a 100644
--- a/examples/rp23/src/bin/otp.rs
+++ b/examples/rp23/src/bin/otp.rs
@@ -14,16 +14,6 @@ use {defmt_rtt as _, panic_probe as _};
14#[used] 14#[used]
15pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 15pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
16 16
17// Program metadata for `picotool info`
18#[link_section = ".bi_entries"]
19#[used]
20pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
21 embassy_rp::binary_info::rp_program_name!(c"OTP Read Example"),
22 embassy_rp::binary_info::rp_cargo_version!(),
23 embassy_rp::binary_info::rp_program_description!(c"OTP Read Example"),
24 embassy_rp::binary_info::rp_program_build_attribute!(),
25];
26
27#[embassy_executor::main] 17#[embassy_executor::main]
28async fn main(_spawner: Spawner) { 18async fn main(_spawner: Spawner) {
29 let _ = embassy_rp::init(Default::default()); 19 let _ = embassy_rp::init(Default::default());
diff --git a/examples/rp23/src/bin/pio_async.rs b/examples/rp23/src/bin/pio_async.rs
index 231afc80e..896447e28 100644
--- a/examples/rp23/src/bin/pio_async.rs
+++ b/examples/rp23/src/bin/pio_async.rs
@@ -16,16 +16,6 @@ use {defmt_rtt as _, panic_probe as _};
16#[used] 16#[used]
17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
18 18
19// Program metadata for `picotool info`
20#[link_section = ".bi_entries"]
21#[used]
22pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
23 embassy_rp::binary_info::rp_program_name!(c"example"),
24 embassy_rp::binary_info::rp_cargo_version!(),
25 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
26 embassy_rp::binary_info::rp_program_build_attribute!(),
27];
28
29bind_interrupts!(struct Irqs { 19bind_interrupts!(struct Irqs {
30 PIO0_IRQ_0 => InterruptHandler<PIO0>; 20 PIO0_IRQ_0 => InterruptHandler<PIO0>;
31}); 21});
diff --git a/examples/rp23/src/bin/pio_dma.rs b/examples/rp23/src/bin/pio_dma.rs
index 60fbcb83a..b5f754798 100644
--- a/examples/rp23/src/bin/pio_dma.rs
+++ b/examples/rp23/src/bin/pio_dma.rs
@@ -17,16 +17,6 @@ use {defmt_rtt as _, panic_probe as _};
17#[used] 17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19 19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info::rp_program_name!(c"example"),
25 embassy_rp::binary_info::rp_cargo_version!(),
26 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info::rp_program_build_attribute!(),
28];
29
30bind_interrupts!(struct Irqs { 20bind_interrupts!(struct Irqs {
31 PIO0_IRQ_0 => InterruptHandler<PIO0>; 21 PIO0_IRQ_0 => InterruptHandler<PIO0>;
32}); 22});
diff --git a/examples/rp23/src/bin/pio_hd44780.rs b/examples/rp23/src/bin/pio_hd44780.rs
index 92aa858f9..c6f5f6db0 100644
--- a/examples/rp23/src/bin/pio_hd44780.rs
+++ b/examples/rp23/src/bin/pio_hd44780.rs
@@ -7,14 +7,12 @@
7use core::fmt::Write; 7use core::fmt::Write;
8 8
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_rp::bind_interrupts;
10use embassy_rp::block::ImageDef; 11use embassy_rp::block::ImageDef;
11use embassy_rp::dma::{AnyChannel, Channel};
12use embassy_rp::peripherals::PIO0; 12use embassy_rp::peripherals::PIO0;
13use embassy_rp::pio::{ 13use embassy_rp::pio::{InterruptHandler, Pio};
14 Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, 14use embassy_rp::pio_programs::hd44780::{PioHD44780, PioHD44780CommandSequenceProgram, PioHD44780CommandWordProgram};
15};
16use embassy_rp::pwm::{self, Pwm}; 15use embassy_rp::pwm::{self, Pwm};
17use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef};
18use embassy_time::{Instant, Timer}; 16use embassy_time::{Instant, Timer};
19use {defmt_rtt as _, panic_probe as _}; 17use {defmt_rtt as _, panic_probe as _};
20 18
@@ -22,16 +20,6 @@ use {defmt_rtt as _, panic_probe as _};
22#[used] 20#[used]
23pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 21pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
24 22
25// Program metadata for `picotool info`
26#[link_section = ".bi_entries"]
27#[used]
28pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
29 embassy_rp::binary_info::rp_program_name!(c"example"),
30 embassy_rp::binary_info::rp_cargo_version!(),
31 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
32 embassy_rp::binary_info::rp_program_build_attribute!(),
33];
34
35bind_interrupts!(pub struct Irqs { 23bind_interrupts!(pub struct Irqs {
36 PIO0_IRQ_0 => InterruptHandler<PIO0>; 24 PIO0_IRQ_0 => InterruptHandler<PIO0>;
37}); 25});
@@ -58,8 +46,27 @@ async fn main(_spawner: Spawner) {
58 c 46 c
59 }); 47 });
60 48
61 let mut hd = HD44780::new( 49 let Pio {
62 p.PIO0, Irqs, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6, 50 mut common, sm0, irq0, ..
51 } = Pio::new(p.PIO0, Irqs);
52
53 let word_prg = PioHD44780CommandWordProgram::new(&mut common);
54 let seq_prg = PioHD44780CommandSequenceProgram::new(&mut common);
55
56 let mut hd = PioHD44780::new(
57 &mut common,
58 sm0,
59 irq0,
60 p.DMA_CH3,
61 p.PIN_0,
62 p.PIN_1,
63 p.PIN_2,
64 p.PIN_3,
65 p.PIN_4,
66 p.PIN_5,
67 p.PIN_6,
68 &word_prg,
69 &seq_prg,
63 ) 70 )
64 .await; 71 .await;
65 72
@@ -83,173 +90,3 @@ async fn main(_spawner: Spawner) {
83 Timer::after_secs(1).await; 90 Timer::after_secs(1).await;
84 } 91 }
85} 92}
86
87pub struct HD44780<'l> {
88 dma: PeripheralRef<'l, AnyChannel>,
89 sm: StateMachine<'l, PIO0, 0>,
90
91 buf: [u8; 40],
92}
93
94impl<'l> HD44780<'l> {
95 pub async fn new(
96 pio: impl Peripheral<P = PIO0> + 'l,
97 irq: Irqs,
98 dma: impl Peripheral<P = impl Channel> + 'l,
99 rs: impl PioPin,
100 rw: impl PioPin,
101 e: impl PioPin,
102 db4: impl PioPin,
103 db5: impl PioPin,
104 db6: impl PioPin,
105 db7: impl PioPin,
106 ) -> HD44780<'l> {
107 into_ref!(dma);
108
109 let Pio {
110 mut common,
111 mut irq0,
112 mut sm0,
113 ..
114 } = Pio::new(pio, irq);
115
116 // takes command words (<wait:24> <command:4> <0:4>)
117 let prg = pio_proc::pio_asm!(
118 r#"
119 .side_set 1 opt
120 .origin 20
121
122 loop:
123 out x, 24
124 delay:
125 jmp x--, delay
126 out pins, 4 side 1
127 out null, 4 side 0
128 jmp !osre, loop
129 irq 0
130 "#,
131 );
132
133 let rs = common.make_pio_pin(rs);
134 let rw = common.make_pio_pin(rw);
135 let e = common.make_pio_pin(e);
136 let db4 = common.make_pio_pin(db4);
137 let db5 = common.make_pio_pin(db5);
138 let db6 = common.make_pio_pin(db6);
139 let db7 = common.make_pio_pin(db7);
140
141 sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]);
142
143 let mut cfg = Config::default();
144 cfg.use_program(&common.load_program(&prg.program), &[&e]);
145 cfg.clock_divider = 125u8.into();
146 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
147 cfg.shift_out = ShiftConfig {
148 auto_fill: true,
149 direction: ShiftDirection::Left,
150 threshold: 32,
151 };
152 cfg.fifo_join = FifoJoin::TxOnly;
153 sm0.set_config(&cfg);
154
155 sm0.set_enable(true);
156 // init to 8 bit thrice
157 sm0.tx().push((50000 << 8) | 0x30);
158 sm0.tx().push((5000 << 8) | 0x30);
159 sm0.tx().push((200 << 8) | 0x30);
160 // init 4 bit
161 sm0.tx().push((200 << 8) | 0x20);
162 // set font and lines
163 sm0.tx().push((50 << 8) | 0x20);
164 sm0.tx().push(0b1100_0000);
165
166 irq0.wait().await;
167 sm0.set_enable(false);
168
169 // takes command sequences (<rs:1> <count:7>, data...)
170 // many side sets are only there to free up a delay bit!
171 let prg = pio_proc::pio_asm!(
172 r#"
173 .origin 27
174 .side_set 1
175
176 .wrap_target
177 pull side 0
178 out x 1 side 0 ; !rs
179 out y 7 side 0 ; #data - 1
180
181 ; rs/rw to e: >= 60ns
182 ; e high time: >= 500ns
183 ; e low time: >= 500ns
184 ; read data valid after e falling: ~5ns
185 ; write data hold after e falling: ~10ns
186
187 loop:
188 pull side 0
189 jmp !x data side 0
190 command:
191 set pins 0b00 side 0
192 jmp shift side 0
193 data:
194 set pins 0b01 side 0
195 shift:
196 out pins 4 side 1 [9]
197 nop side 0 [9]
198 out pins 4 side 1 [9]
199 mov osr null side 0 [7]
200 out pindirs 4 side 0
201 set pins 0b10 side 0
202 busy:
203 nop side 1 [9]
204 jmp pin more side 0 [9]
205 mov osr ~osr side 1 [9]
206 nop side 0 [4]
207 out pindirs 4 side 0
208 jmp y-- loop side 0
209 .wrap
210 more:
211 nop side 1 [9]
212 jmp busy side 0 [9]
213 "#
214 );
215
216 let mut cfg = Config::default();
217 cfg.use_program(&common.load_program(&prg.program), &[&e]);
218 cfg.clock_divider = 8u8.into(); // ~64ns/insn
219 cfg.set_jmp_pin(&db7);
220 cfg.set_set_pins(&[&rs, &rw]);
221 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
222 cfg.shift_out.direction = ShiftDirection::Left;
223 cfg.fifo_join = FifoJoin::TxOnly;
224 sm0.set_config(&cfg);
225
226 sm0.set_enable(true);
227
228 // display on and cursor on and blinking, reset display
229 sm0.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await;
230
231 Self {
232 dma: dma.map_into(),
233 sm: sm0,
234 buf: [0x20; 40],
235 }
236 }
237
238 pub async fn add_line(&mut self, s: &[u8]) {
239 // move cursor to 0:0, prepare 16 characters
240 self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]);
241 // move line 2 up
242 self.buf.copy_within(22..38, 3);
243 // move cursor to 1:0, prepare 16 characters
244 self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]);
245 // file line 2 with spaces
246 self.buf[22..38].fill(0x20);
247 // copy input line
248 let len = s.len().min(16);
249 self.buf[22..22 + len].copy_from_slice(&s[0..len]);
250 // set cursor to 1:15
251 self.buf[38..].copy_from_slice(&[0x80, 0xcf]);
252
253 self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await;
254 }
255}
diff --git a/examples/rp23/src/bin/pio_i2s.rs b/examples/rp23/src/bin/pio_i2s.rs
index d6d2d0ade..1fd34357b 100644
--- a/examples/rp23/src/bin/pio_i2s.rs
+++ b/examples/rp23/src/bin/pio_i2s.rs
@@ -13,11 +13,12 @@
13use core::mem; 13use core::mem;
14 14
15use embassy_executor::Spawner; 15use embassy_executor::Spawner;
16use embassy_rp::bind_interrupts;
16use embassy_rp::block::ImageDef; 17use embassy_rp::block::ImageDef;
18use embassy_rp::gpio::{Input, Pull};
17use embassy_rp::peripherals::PIO0; 19use embassy_rp::peripherals::PIO0;
18use embassy_rp::pio::{Config, FifoJoin, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; 20use embassy_rp::pio::{InterruptHandler, Pio};
19use embassy_rp::{bind_interrupts, Peripheral}; 21use embassy_rp::pio_programs::i2s::{PioI2sOut, PioI2sOutProgram};
20use fixed::traits::ToFixed;
21use static_cell::StaticCell; 22use static_cell::StaticCell;
22use {defmt_rtt as _, panic_probe as _}; 23use {defmt_rtt as _, panic_probe as _};
23 24
@@ -25,78 +26,41 @@ use {defmt_rtt as _, panic_probe as _};
25#[used] 26#[used]
26pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 27pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
27 28
28// Program metadata for `picotool info`
29#[link_section = ".bi_entries"]
30#[used]
31pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
32 embassy_rp::binary_info::rp_program_name!(c"example"),
33 embassy_rp::binary_info::rp_cargo_version!(),
34 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
35 embassy_rp::binary_info::rp_program_build_attribute!(),
36];
37
38bind_interrupts!(struct Irqs { 29bind_interrupts!(struct Irqs {
39 PIO0_IRQ_0 => InterruptHandler<PIO0>; 30 PIO0_IRQ_0 => InterruptHandler<PIO0>;
40}); 31});
41 32
42const SAMPLE_RATE: u32 = 48_000; 33const SAMPLE_RATE: u32 = 48_000;
34const BIT_DEPTH: u32 = 16;
35const CHANNELS: u32 = 2;
43 36
44#[embassy_executor::main] 37#[embassy_executor::main]
45async fn main(_spawner: Spawner) { 38async fn main(_spawner: Spawner) {
46 let p = embassy_rp::init(Default::default()); 39 let p = embassy_rp::init(Default::default());
47 40
48 // Setup pio state machine for i2s output 41 // Setup pio state machine for i2s output
49 let mut pio = Pio::new(p.PIO0, Irqs); 42 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
50
51 #[rustfmt::skip]
52 let pio_program = pio_proc::pio_asm!(
53 ".side_set 2",
54 " set x, 14 side 0b01", // side 0bWB - W = Word Clock, B = Bit Clock
55 "left_data:",
56 " out pins, 1 side 0b00",
57 " jmp x-- left_data side 0b01",
58 " out pins 1 side 0b10",
59 " set x, 14 side 0b11",
60 "right_data:",
61 " out pins 1 side 0b10",
62 " jmp x-- right_data side 0b11",
63 " out pins 1 side 0b00",
64 );
65 43
66 let bit_clock_pin = p.PIN_18; 44 let bit_clock_pin = p.PIN_18;
67 let left_right_clock_pin = p.PIN_19; 45 let left_right_clock_pin = p.PIN_19;
68 let data_pin = p.PIN_20; 46 let data_pin = p.PIN_20;
69 47
70 let data_pin = pio.common.make_pio_pin(data_pin); 48 let program = PioI2sOutProgram::new(&mut common);
71 let bit_clock_pin = pio.common.make_pio_pin(bit_clock_pin); 49 let mut i2s = PioI2sOut::new(
72 let left_right_clock_pin = pio.common.make_pio_pin(left_right_clock_pin); 50 &mut common,
73 51 sm0,
74 let cfg = { 52 p.DMA_CH0,
75 let mut cfg = Config::default(); 53 data_pin,
76 cfg.use_program( 54 bit_clock_pin,
77 &pio.common.load_program(&pio_program.program), 55 left_right_clock_pin,
78 &[&bit_clock_pin, &left_right_clock_pin], 56 SAMPLE_RATE,
79 ); 57 BIT_DEPTH,
80 cfg.set_out_pins(&[&data_pin]); 58 CHANNELS,
81 const BIT_DEPTH: u32 = 16; 59 &program,
82 const CHANNELS: u32 = 2;
83 let clock_frequency = SAMPLE_RATE * BIT_DEPTH * CHANNELS;
84 cfg.clock_divider = (125_000_000. / clock_frequency as f64 / 2.).to_fixed();
85 cfg.shift_out = ShiftConfig {
86 threshold: 32,
87 direction: ShiftDirection::Left,
88 auto_fill: true,
89 };
90 // join fifos to have twice the time to start the next dma transfer
91 cfg.fifo_join = FifoJoin::TxOnly;
92 cfg
93 };
94 pio.sm0.set_config(&cfg);
95 pio.sm0.set_pin_dirs(
96 embassy_rp::pio::Direction::Out,
97 &[&data_pin, &left_right_clock_pin, &bit_clock_pin],
98 ); 60 );
99 61
62 let fade_input = Input::new(p.PIN_0, Pull::Up);
63
100 // create two audio buffers (back and front) which will take turns being 64 // create two audio buffers (back and front) which will take turns being
101 // filled with new audio data and being sent to the pio fifo using dma 65 // filled with new audio data and being sent to the pio fifo using dma
102 const BUFFER_SIZE: usize = 960; 66 const BUFFER_SIZE: usize = 960;
@@ -105,20 +69,16 @@ async fn main(_spawner: Spawner) {
105 let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE); 69 let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE);
106 70
107 // start pio state machine 71 // start pio state machine
108 pio.sm0.set_enable(true);
109 let tx = pio.sm0.tx();
110 let mut dma_ref = p.DMA_CH0.into_ref();
111
112 let mut fade_value: i32 = 0; 72 let mut fade_value: i32 = 0;
113 let mut phase: i32 = 0; 73 let mut phase: i32 = 0;
114 74
115 loop { 75 loop {
116 // trigger transfer of front buffer data to the pio fifo 76 // trigger transfer of front buffer data to the pio fifo
117 // but don't await the returned future, yet 77 // but don't await the returned future, yet
118 let dma_future = tx.dma_push(dma_ref.reborrow(), front_buffer); 78 let dma_future = i2s.write(front_buffer);
119 79
120 // fade in audio 80 // fade in audio when bootsel is pressed
121 let fade_target = i32::MAX; 81 let fade_target = if fade_input.is_low() { i32::MAX } else { 0 };
122 82
123 // fill back buffer with fresh audio samples before awaiting the dma future 83 // fill back buffer with fresh audio samples before awaiting the dma future
124 for s in back_buffer.iter_mut() { 84 for s in back_buffer.iter_mut() {
diff --git a/examples/rp23/src/bin/pio_onewire.rs b/examples/rp23/src/bin/pio_onewire.rs
new file mode 100644
index 000000000..7f227d04b
--- /dev/null
+++ b/examples/rp23/src/bin/pio_onewire.rs
@@ -0,0 +1,88 @@
1//! This example shows how you can use PIO to read a `DS18B20` one-wire temperature sensor.
2
3#![no_std]
4#![no_main]
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_rp::bind_interrupts;
8use embassy_rp::block::ImageDef;
9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{self, InterruptHandler, Pio};
11use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram};
12use embassy_time::Timer;
13use {defmt_rtt as _, panic_probe as _};
14
15#[link_section = ".start_block"]
16#[used]
17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
18
19bind_interrupts!(struct Irqs {
20 PIO0_IRQ_0 => InterruptHandler<PIO0>;
21});
22
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) {
25 let p = embassy_rp::init(Default::default());
26 let mut pio = Pio::new(p.PIO0, Irqs);
27
28 let prg = PioOneWireProgram::new(&mut pio.common);
29 let onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg);
30
31 let mut sensor = Ds18b20::new(onewire);
32
33 loop {
34 sensor.start().await; // Start a new measurement
35 Timer::after_secs(1).await; // Allow 1s for the measurement to finish
36 match sensor.temperature().await {
37 Ok(temp) => info!("temp = {:?} deg C", temp),
38 _ => error!("sensor error"),
39 }
40 Timer::after_secs(1).await;
41 }
42}
43
44/// DS18B20 temperature sensor driver
45pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> {
46 wire: PioOneWire<'d, PIO, SM>,
47}
48
49impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> {
50 pub fn new(wire: PioOneWire<'d, PIO, SM>) -> Self {
51 Self { wire }
52 }
53
54 /// Calculate CRC8 of the data
55 fn crc8(data: &[u8]) -> u8 {
56 let mut temp;
57 let mut data_byte;
58 let mut crc = 0;
59 for b in data {
60 data_byte = *b;
61 for _ in 0..8 {
62 temp = (crc ^ data_byte) & 0x01;
63 crc >>= 1;
64 if temp != 0 {
65 crc ^= 0x8C;
66 }
67 data_byte >>= 1;
68 }
69 }
70 crc
71 }
72
73 /// Start a new measurement. Allow at least 1000ms before getting `temperature`.
74 pub async fn start(&mut self) {
75 self.wire.write_bytes(&[0xCC, 0x44]).await;
76 }
77
78 /// Read the temperature. Ensure >1000ms has passed since `start` before calling this.
79 pub async fn temperature(&mut self) -> Result<f32, ()> {
80 self.wire.write_bytes(&[0xCC, 0xBE]).await;
81 let mut data = [0; 9];
82 self.wire.read_bytes(&mut data).await;
83 match Self::crc8(&data) == 0 {
84 true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.),
85 false => Err(()),
86 }
87 }
88}
diff --git a/examples/rp23/src/bin/pio_pwm.rs b/examples/rp23/src/bin/pio_pwm.rs
index 587f91ac3..11af62a7a 100644
--- a/examples/rp23/src/bin/pio_pwm.rs
+++ b/examples/rp23/src/bin/pio_pwm.rs
@@ -5,122 +5,32 @@
5use core::time::Duration; 5use core::time::Duration;
6 6
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_rp::bind_interrupts;
8use embassy_rp::block::ImageDef; 9use embassy_rp::block::ImageDef;
9use embassy_rp::gpio::Level;
10use embassy_rp::peripherals::PIO0; 10use embassy_rp::peripherals::PIO0;
11use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; 11use embassy_rp::pio::{InterruptHandler, Pio};
12use embassy_rp::{bind_interrupts, clocks}; 12use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram};
13use embassy_time::Timer; 13use embassy_time::Timer;
14use pio::InstructionOperands;
15use {defmt_rtt as _, panic_probe as _}; 14use {defmt_rtt as _, panic_probe as _};
16 15
17#[link_section = ".start_block"] 16#[link_section = ".start_block"]
18#[used] 17#[used]
19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
20 19
21// Program metadata for `picotool info`
22#[link_section = ".bi_entries"]
23#[used]
24pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
25 embassy_rp::binary_info::rp_program_name!(c"example"),
26 embassy_rp::binary_info::rp_cargo_version!(),
27 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
28 embassy_rp::binary_info::rp_program_build_attribute!(),
29];
30
31const REFRESH_INTERVAL: u64 = 20000; 20const REFRESH_INTERVAL: u64 = 20000;
32 21
33bind_interrupts!(struct Irqs { 22bind_interrupts!(struct Irqs {
34 PIO0_IRQ_0 => InterruptHandler<PIO0>; 23 PIO0_IRQ_0 => InterruptHandler<PIO0>;
35}); 24});
36 25
37pub fn to_pio_cycles(duration: Duration) -> u32 {
38 (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
39}
40
41pub struct PwmPio<'d, T: Instance, const SM: usize> {
42 sm: StateMachine<'d, T, SM>,
43}
44
45impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
46 pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
47 let prg = pio_proc::pio_asm!(
48 ".side_set 1 opt"
49 "pull noblock side 0"
50 "mov x, osr"
51 "mov y, isr"
52 "countloop:"
53 "jmp x!=y noset"
54 "jmp skip side 1"
55 "noset:"
56 "nop"
57 "skip:"
58 "jmp y-- countloop"
59 );
60
61 pio.load_program(&prg.program);
62 let pin = pio.make_pio_pin(pin);
63 sm.set_pins(Level::High, &[&pin]);
64 sm.set_pin_dirs(Direction::Out, &[&pin]);
65
66 let mut cfg = Config::default();
67 cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
68
69 sm.set_config(&cfg);
70
71 Self { sm }
72 }
73
74 pub fn start(&mut self) {
75 self.sm.set_enable(true);
76 }
77
78 pub fn stop(&mut self) {
79 self.sm.set_enable(false);
80 }
81
82 pub fn set_period(&mut self, duration: Duration) {
83 let is_enabled = self.sm.is_enabled();
84 while !self.sm.tx().empty() {} // Make sure that the queue is empty
85 self.sm.set_enable(false);
86 self.sm.tx().push(to_pio_cycles(duration));
87 unsafe {
88 self.sm.exec_instr(
89 InstructionOperands::PULL {
90 if_empty: false,
91 block: false,
92 }
93 .encode(),
94 );
95 self.sm.exec_instr(
96 InstructionOperands::OUT {
97 destination: ::pio::OutDestination::ISR,
98 bit_count: 32,
99 }
100 .encode(),
101 );
102 };
103 if is_enabled {
104 self.sm.set_enable(true) // Enable if previously enabled
105 }
106 }
107
108 pub fn set_level(&mut self, level: u32) {
109 self.sm.tx().push(level);
110 }
111
112 pub fn write(&mut self, duration: Duration) {
113 self.set_level(to_pio_cycles(duration));
114 }
115}
116
117#[embassy_executor::main] 26#[embassy_executor::main]
118async fn main(_spawner: Spawner) { 27async fn main(_spawner: Spawner) {
119 let p = embassy_rp::init(Default::default()); 28 let p = embassy_rp::init(Default::default());
120 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); 29 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
121 30
122 // Note that PIN_25 is the led pin on the Pico 31 // Note that PIN_25 is the led pin on the Pico
123 let mut pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_25); 32 let prg = PioPwmProgram::new(&mut common);
33 let mut pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_25, &prg);
124 pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL)); 34 pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL));
125 pwm_pio.start(); 35 pwm_pio.start();
126 36
diff --git a/examples/rp23/src/bin/pio_rotary_encoder.rs b/examples/rp23/src/bin/pio_rotary_encoder.rs
index c147351e8..2bb0e67f9 100644
--- a/examples/rp23/src/bin/pio_rotary_encoder.rs
+++ b/examples/rp23/src/bin/pio_rotary_encoder.rs
@@ -5,85 +5,35 @@
5 5
6use defmt::info; 6use defmt::info;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_rp::bind_interrupts;
8use embassy_rp::block::ImageDef; 9use embassy_rp::block::ImageDef;
9use embassy_rp::gpio::Pull;
10use embassy_rp::peripherals::PIO0; 10use embassy_rp::peripherals::PIO0;
11use embassy_rp::{bind_interrupts, pio}; 11use embassy_rp::pio::{InterruptHandler, Pio};
12use fixed::traits::ToFixed; 12use embassy_rp::pio_programs::rotary_encoder::{Direction, PioEncoder, PioEncoderProgram};
13use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine};
14use {defmt_rtt as _, panic_probe as _}; 13use {defmt_rtt as _, panic_probe as _};
15 14
16#[link_section = ".start_block"] 15#[link_section = ".start_block"]
17#[used] 16#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19 18
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info::rp_program_name!(c"example"),
25 embassy_rp::binary_info::rp_cargo_version!(),
26 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info::rp_program_build_attribute!(),
28];
29
30bind_interrupts!(struct Irqs { 19bind_interrupts!(struct Irqs {
31 PIO0_IRQ_0 => InterruptHandler<PIO0>; 20 PIO0_IRQ_0 => InterruptHandler<PIO0>;
32}); 21});
33 22
34pub struct PioEncoder<'d, T: Instance, const SM: usize> { 23#[embassy_executor::task]
35 sm: StateMachine<'d, T, SM>, 24async fn encoder_0(mut encoder: PioEncoder<'static, PIO0, 0>) {
36} 25 let mut count = 0;
37 26 loop {
38impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> { 27 info!("Count: {}", count);
39 pub fn new( 28 count += match encoder.read().await {
40 pio: &mut Common<'d, T>, 29 Direction::Clockwise => 1,
41 mut sm: StateMachine<'d, T, SM>, 30 Direction::CounterClockwise => -1,
42 pin_a: impl PioPin, 31 };
43 pin_b: impl PioPin,
44 ) -> Self {
45 let mut pin_a = pio.make_pio_pin(pin_a);
46 let mut pin_b = pio.make_pio_pin(pin_b);
47 pin_a.set_pull(Pull::Up);
48 pin_b.set_pull(Pull::Up);
49 sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]);
50
51 let prg = pio_proc::pio_asm!("wait 1 pin 1", "wait 0 pin 1", "in pins, 2", "push",);
52
53 let mut cfg = Config::default();
54 cfg.set_in_pins(&[&pin_a, &pin_b]);
55 cfg.fifo_join = FifoJoin::RxOnly;
56 cfg.shift_in.direction = ShiftDirection::Left;
57 cfg.clock_divider = 10_000.to_fixed();
58 cfg.use_program(&pio.load_program(&prg.program), &[]);
59 sm.set_config(&cfg);
60 sm.set_enable(true);
61 Self { sm }
62 }
63
64 pub async fn read(&mut self) -> Direction {
65 loop {
66 match self.sm.rx().wait_pull().await {
67 0 => return Direction::CounterClockwise,
68 1 => return Direction::Clockwise,
69 _ => {}
70 }
71 }
72 } 32 }
73} 33}
74 34
75pub enum Direction { 35#[embassy_executor::task]
76 Clockwise, 36async fn encoder_1(mut encoder: PioEncoder<'static, PIO0, 1>) {
77 CounterClockwise,
78}
79
80#[embassy_executor::main]
81async fn main(_spawner: Spawner) {
82 let p = embassy_rp::init(Default::default());
83 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
84
85 let mut encoder = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5);
86
87 let mut count = 0; 37 let mut count = 0;
88 loop { 38 loop {
89 info!("Count: {}", count); 39 info!("Count: {}", count);
@@ -93,3 +43,18 @@ async fn main(_spawner: Spawner) {
93 }; 43 };
94 } 44 }
95} 45}
46
47#[embassy_executor::main]
48async fn main(spawner: Spawner) {
49 let p = embassy_rp::init(Default::default());
50 let Pio {
51 mut common, sm0, sm1, ..
52 } = Pio::new(p.PIO0, Irqs);
53
54 let prg = PioEncoderProgram::new(&mut common);
55 let encoder0 = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5, &prg);
56 let encoder1 = PioEncoder::new(&mut common, sm1, p.PIN_6, p.PIN_7, &prg);
57
58 spawner.must_spawn(encoder_0(encoder0));
59 spawner.must_spawn(encoder_1(encoder1));
60}
diff --git a/examples/rp23/src/bin/pio_servo.rs b/examples/rp23/src/bin/pio_servo.rs
index 5e8714178..4e94103f1 100644
--- a/examples/rp23/src/bin/pio_servo.rs
+++ b/examples/rp23/src/bin/pio_servo.rs
@@ -5,29 +5,18 @@
5use core::time::Duration; 5use core::time::Duration;
6 6
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_rp::bind_interrupts;
8use embassy_rp::block::ImageDef; 9use embassy_rp::block::ImageDef;
9use embassy_rp::gpio::Level;
10use embassy_rp::peripherals::PIO0; 10use embassy_rp::peripherals::PIO0;
11use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; 11use embassy_rp::pio::{Instance, InterruptHandler, Pio};
12use embassy_rp::{bind_interrupts, clocks}; 12use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram};
13use embassy_time::Timer; 13use embassy_time::Timer;
14use pio::InstructionOperands;
15use {defmt_rtt as _, panic_probe as _}; 14use {defmt_rtt as _, panic_probe as _};
16 15
17#[link_section = ".start_block"] 16#[link_section = ".start_block"]
18#[used] 17#[used]
19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
20 19
21// Program metadata for `picotool info`
22#[link_section = ".bi_entries"]
23#[used]
24pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
25 embassy_rp::binary_info::rp_program_name!(c"example"),
26 embassy_rp::binary_info::rp_cargo_version!(),
27 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
28 embassy_rp::binary_info::rp_program_build_attribute!(),
29];
30
31const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo 20const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo
32const DEFAULT_MAX_PULSE_WIDTH: u64 = 2000; // uncalibrated default, the longest duty cycle sent to a servo 21const DEFAULT_MAX_PULSE_WIDTH: u64 = 2000; // uncalibrated default, the longest duty cycle sent to a servo
33const DEFAULT_MAX_DEGREE_ROTATION: u64 = 160; // 160 degrees is typical 22const DEFAULT_MAX_DEGREE_ROTATION: u64 = 160; // 160 degrees is typical
@@ -37,88 +26,8 @@ bind_interrupts!(struct Irqs {
37 PIO0_IRQ_0 => InterruptHandler<PIO0>; 26 PIO0_IRQ_0 => InterruptHandler<PIO0>;
38}); 27});
39 28
40pub fn to_pio_cycles(duration: Duration) -> u32 {
41 (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
42}
43
44pub struct PwmPio<'d, T: Instance, const SM: usize> {
45 sm: StateMachine<'d, T, SM>,
46}
47
48impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
49 pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
50 let prg = pio_proc::pio_asm!(
51 ".side_set 1 opt"
52 "pull noblock side 0"
53 "mov x, osr"
54 "mov y, isr"
55 "countloop:"
56 "jmp x!=y noset"
57 "jmp skip side 1"
58 "noset:"
59 "nop"
60 "skip:"
61 "jmp y-- countloop"
62 );
63
64 pio.load_program(&prg.program);
65 let pin = pio.make_pio_pin(pin);
66 sm.set_pins(Level::High, &[&pin]);
67 sm.set_pin_dirs(Direction::Out, &[&pin]);
68
69 let mut cfg = Config::default();
70 cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
71
72 sm.set_config(&cfg);
73
74 Self { sm }
75 }
76
77 pub fn start(&mut self) {
78 self.sm.set_enable(true);
79 }
80
81 pub fn stop(&mut self) {
82 self.sm.set_enable(false);
83 }
84
85 pub fn set_period(&mut self, duration: Duration) {
86 let is_enabled = self.sm.is_enabled();
87 while !self.sm.tx().empty() {} // Make sure that the queue is empty
88 self.sm.set_enable(false);
89 self.sm.tx().push(to_pio_cycles(duration));
90 unsafe {
91 self.sm.exec_instr(
92 InstructionOperands::PULL {
93 if_empty: false,
94 block: false,
95 }
96 .encode(),
97 );
98 self.sm.exec_instr(
99 InstructionOperands::OUT {
100 destination: ::pio::OutDestination::ISR,
101 bit_count: 32,
102 }
103 .encode(),
104 );
105 };
106 if is_enabled {
107 self.sm.set_enable(true) // Enable if previously enabled
108 }
109 }
110
111 pub fn set_level(&mut self, level: u32) {
112 self.sm.tx().push(level);
113 }
114
115 pub fn write(&mut self, duration: Duration) {
116 self.set_level(to_pio_cycles(duration));
117 }
118}
119
120pub struct ServoBuilder<'d, T: Instance, const SM: usize> { 29pub struct ServoBuilder<'d, T: Instance, const SM: usize> {
121 pwm: PwmPio<'d, T, SM>, 30 pwm: PioPwm<'d, T, SM>,
122 period: Duration, 31 period: Duration,
123 min_pulse_width: Duration, 32 min_pulse_width: Duration,
124 max_pulse_width: Duration, 33 max_pulse_width: Duration,
@@ -126,7 +35,7 @@ pub struct ServoBuilder<'d, T: Instance, const SM: usize> {
126} 35}
127 36
128impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> { 37impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> {
129 pub fn new(pwm: PwmPio<'d, T, SM>) -> Self { 38 pub fn new(pwm: PioPwm<'d, T, SM>) -> Self {
130 Self { 39 Self {
131 pwm, 40 pwm,
132 period: Duration::from_micros(REFRESH_INTERVAL), 41 period: Duration::from_micros(REFRESH_INTERVAL),
@@ -168,7 +77,7 @@ impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> {
168} 77}
169 78
170pub struct Servo<'d, T: Instance, const SM: usize> { 79pub struct Servo<'d, T: Instance, const SM: usize> {
171 pwm: PwmPio<'d, T, SM>, 80 pwm: PioPwm<'d, T, SM>,
172 min_pulse_width: Duration, 81 min_pulse_width: Duration,
173 max_pulse_width: Duration, 82 max_pulse_width: Duration,
174 max_degree_rotation: u64, 83 max_degree_rotation: u64,
@@ -205,7 +114,8 @@ async fn main(_spawner: Spawner) {
205 let p = embassy_rp::init(Default::default()); 114 let p = embassy_rp::init(Default::default());
206 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); 115 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
207 116
208 let pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_1); 117 let prg = PioPwmProgram::new(&mut common);
118 let pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_1, &prg);
209 let mut servo = ServoBuilder::new(pwm_pio) 119 let mut servo = ServoBuilder::new(pwm_pio)
210 .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo 120 .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo
211 .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment. 121 .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment.
diff --git a/examples/rp23/src/bin/pio_stepper.rs b/examples/rp23/src/bin/pio_stepper.rs
index 24785443b..4fabe78ca 100644
--- a/examples/rp23/src/bin/pio_stepper.rs
+++ b/examples/rp23/src/bin/pio_stepper.rs
@@ -3,158 +3,25 @@
3 3
4#![no_std] 4#![no_std]
5#![no_main] 5#![no_main]
6use core::mem::{self, MaybeUninit};
7 6
8use defmt::info; 7use defmt::info;
9use embassy_executor::Spawner; 8use embassy_executor::Spawner;
10use embassy_rp::bind_interrupts; 9use embassy_rp::bind_interrupts;
11use embassy_rp::block::ImageDef; 10use embassy_rp::block::ImageDef;
12use embassy_rp::peripherals::PIO0; 11use embassy_rp::peripherals::PIO0;
13use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Irq, Pio, PioPin, StateMachine}; 12use embassy_rp::pio::{InterruptHandler, Pio};
13use embassy_rp::pio_programs::stepper::{PioStepper, PioStepperProgram};
14use embassy_time::{with_timeout, Duration, Timer}; 14use embassy_time::{with_timeout, Duration, Timer};
15use fixed::traits::ToFixed;
16use fixed::types::extra::U8;
17use fixed::FixedU32;
18use {defmt_rtt as _, panic_probe as _}; 15use {defmt_rtt as _, panic_probe as _};
19 16
20#[link_section = ".start_block"] 17#[link_section = ".start_block"]
21#[used] 18#[used]
22pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
23 20
24// Program metadata for `picotool info`
25#[link_section = ".bi_entries"]
26#[used]
27pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
28 embassy_rp::binary_info::rp_program_name!(c"example"),
29 embassy_rp::binary_info::rp_cargo_version!(),
30 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
31 embassy_rp::binary_info::rp_program_build_attribute!(),
32];
33
34bind_interrupts!(struct Irqs { 21bind_interrupts!(struct Irqs {
35 PIO0_IRQ_0 => InterruptHandler<PIO0>; 22 PIO0_IRQ_0 => InterruptHandler<PIO0>;
36}); 23});
37 24
38pub struct PioStepper<'d, T: Instance, const SM: usize> {
39 irq: Irq<'d, T, SM>,
40 sm: StateMachine<'d, T, SM>,
41}
42
43impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> {
44 pub fn new(
45 pio: &mut Common<'d, T>,
46 mut sm: StateMachine<'d, T, SM>,
47 irq: Irq<'d, T, SM>,
48 pin0: impl PioPin,
49 pin1: impl PioPin,
50 pin2: impl PioPin,
51 pin3: impl PioPin,
52 ) -> Self {
53 let prg = pio_proc::pio_asm!(
54 "pull block",
55 "mov x, osr",
56 "pull block",
57 "mov y, osr",
58 "jmp !x end",
59 "loop:",
60 "jmp !osre step",
61 "mov osr, y",
62 "step:",
63 "out pins, 4 [31]"
64 "jmp x-- loop",
65 "end:",
66 "irq 0 rel"
67 );
68 let pin0 = pio.make_pio_pin(pin0);
69 let pin1 = pio.make_pio_pin(pin1);
70 let pin2 = pio.make_pio_pin(pin2);
71 let pin3 = pio.make_pio_pin(pin3);
72 sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]);
73 let mut cfg = Config::default();
74 cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]);
75 cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed();
76 cfg.use_program(&pio.load_program(&prg.program), &[]);
77 sm.set_config(&cfg);
78 sm.set_enable(true);
79 Self { irq, sm }
80 }
81
82 // Set pulse frequency
83 pub fn set_frequency(&mut self, freq: u32) {
84 let clock_divider: FixedU32<U8> = (125_000_000 / (freq * 136)).to_fixed();
85 assert!(clock_divider <= 65536, "clkdiv must be <= 65536");
86 assert!(clock_divider >= 1, "clkdiv must be >= 1");
87 self.sm.set_clock_divider(clock_divider);
88 self.sm.clkdiv_restart();
89 }
90
91 // Full step, one phase
92 pub async fn step(&mut self, steps: i32) {
93 if steps > 0 {
94 self.run(steps, 0b1000_0100_0010_0001_1000_0100_0010_0001).await
95 } else {
96 self.run(-steps, 0b0001_0010_0100_1000_0001_0010_0100_1000).await
97 }
98 }
99
100 // Full step, two phase
101 pub async fn step2(&mut self, steps: i32) {
102 if steps > 0 {
103 self.run(steps, 0b1001_1100_0110_0011_1001_1100_0110_0011).await
104 } else {
105 self.run(-steps, 0b0011_0110_1100_1001_0011_0110_1100_1001).await
106 }
107 }
108
109 // Half step
110 pub async fn step_half(&mut self, steps: i32) {
111 if steps > 0 {
112 self.run(steps, 0b1001_1000_1100_0100_0110_0010_0011_0001).await
113 } else {
114 self.run(-steps, 0b0001_0011_0010_0110_0100_1100_1000_1001).await
115 }
116 }
117
118 async fn run(&mut self, steps: i32, pattern: u32) {
119 self.sm.tx().wait_push(steps as u32).await;
120 self.sm.tx().wait_push(pattern).await;
121 let drop = OnDrop::new(|| {
122 self.sm.clear_fifos();
123 unsafe {
124 self.sm.exec_instr(
125 pio::InstructionOperands::JMP {
126 address: 0,
127 condition: pio::JmpCondition::Always,
128 }
129 .encode(),
130 );
131 }
132 });
133 self.irq.wait().await;
134 drop.defuse();
135 }
136}
137
138struct OnDrop<F: FnOnce()> {
139 f: MaybeUninit<F>,
140}
141
142impl<F: FnOnce()> OnDrop<F> {
143 pub fn new(f: F) -> Self {
144 Self { f: MaybeUninit::new(f) }
145 }
146
147 pub fn defuse(self) {
148 mem::forget(self)
149 }
150}
151
152impl<F: FnOnce()> Drop for OnDrop<F> {
153 fn drop(&mut self) {
154 unsafe { self.f.as_ptr().read()() }
155 }
156}
157
158#[embassy_executor::main] 25#[embassy_executor::main]
159async fn main(_spawner: Spawner) { 26async fn main(_spawner: Spawner) {
160 let p = embassy_rp::init(Default::default()); 27 let p = embassy_rp::init(Default::default());
@@ -162,14 +29,18 @@ async fn main(_spawner: Spawner) {
162 mut common, irq0, sm0, .. 29 mut common, irq0, sm0, ..
163 } = Pio::new(p.PIO0, Irqs); 30 } = Pio::new(p.PIO0, Irqs);
164 31
165 let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7); 32 let prg = PioStepperProgram::new(&mut common);
33 let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7, &prg);
166 stepper.set_frequency(120); 34 stepper.set_frequency(120);
167 loop { 35 loop {
168 info!("CW full steps"); 36 info!("CW full steps");
169 stepper.step(1000).await; 37 stepper.step(1000).await;
170 38
171 info!("CCW full steps, drop after 1 sec"); 39 info!("CCW full steps, drop after 1 sec");
172 if let Err(_) = with_timeout(Duration::from_secs(1), stepper.step(i32::MIN)).await { 40 if with_timeout(Duration::from_secs(1), stepper.step(-i32::MAX))
41 .await
42 .is_err()
43 {
173 info!("Time's up!"); 44 info!("Time's up!");
174 Timer::after(Duration::from_secs(1)).await; 45 Timer::after(Duration::from_secs(1)).await;
175 } 46 }
diff --git a/examples/rp23/src/bin/pio_uart.rs b/examples/rp23/src/bin/pio_uart.rs
new file mode 100644
index 000000000..f8398c22a
--- /dev/null
+++ b/examples/rp23/src/bin/pio_uart.rs
@@ -0,0 +1,202 @@
1//! This example shows how to use the PIO module in the RP2040 chip to implement a duplex UART.
2//! The PIO module is a very powerful peripheral that can be used to implement many different
3//! protocols. It is a very flexible state machine that can be programmed to do almost anything.
4//!
5//! This example opens up a USB device that implements a CDC ACM serial port. It then uses the
6//! PIO module to implement a UART that is connected to the USB serial port. This allows you to
7//! communicate with a device connected to the RP2040 over USB serial.
8
9#![no_std]
10#![no_main]
11#![allow(async_fn_in_trait)]
12
13use defmt::{info, panic, trace};
14use embassy_executor::Spawner;
15use embassy_futures::join::{join, join3};
16use embassy_rp::block::ImageDef;
17use embassy_rp::peripherals::{PIO0, USB};
18use embassy_rp::pio_programs::uart::{PioUartRx, PioUartRxProgram, PioUartTx, PioUartTxProgram};
19use embassy_rp::usb::{Driver, Instance, InterruptHandler};
20use embassy_rp::{bind_interrupts, pio};
21use embassy_sync::blocking_mutex::raw::NoopRawMutex;
22use embassy_sync::pipe::Pipe;
23use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State};
24use embassy_usb::driver::EndpointError;
25use embassy_usb::{Builder, Config};
26use embedded_io_async::{Read, Write};
27use {defmt_rtt as _, panic_probe as _};
28
29#[link_section = ".start_block"]
30#[used]
31pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
32
33bind_interrupts!(struct Irqs {
34 USBCTRL_IRQ => InterruptHandler<USB>;
35 PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;
36});
37
38#[embassy_executor::main]
39async fn main(_spawner: Spawner) {
40 info!("Hello there!");
41
42 let p = embassy_rp::init(Default::default());
43
44 // Create the driver, from the HAL.
45 let driver = Driver::new(p.USB, Irqs);
46
47 // Create embassy-usb Config
48 let mut config = Config::new(0xc0de, 0xcafe);
49 config.manufacturer = Some("Embassy");
50 config.product = Some("PIO UART example");
51 config.serial_number = Some("12345678");
52 config.max_power = 100;
53 config.max_packet_size_0 = 64;
54
55 // Required for windows compatibility.
56 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
57 config.device_class = 0xEF;
58 config.device_sub_class = 0x02;
59 config.device_protocol = 0x01;
60 config.composite_with_iads = true;
61
62 // Create embassy-usb DeviceBuilder using the driver and config.
63 // It needs some buffers for building the descriptors.
64 let mut config_descriptor = [0; 256];
65 let mut bos_descriptor = [0; 256];
66 let mut control_buf = [0; 64];
67
68 let mut state = State::new();
69
70 let mut builder = Builder::new(
71 driver,
72 config,
73 &mut config_descriptor,
74 &mut bos_descriptor,
75 &mut [], // no msos descriptors
76 &mut control_buf,
77 );
78
79 // Create classes on the builder.
80 let class = CdcAcmClass::new(&mut builder, &mut state, 64);
81
82 // Build the builder.
83 let mut usb = builder.build();
84
85 // Run the USB device.
86 let usb_fut = usb.run();
87
88 // PIO UART setup
89 let pio::Pio {
90 mut common, sm0, sm1, ..
91 } = pio::Pio::new(p.PIO0, Irqs);
92
93 let tx_program = PioUartTxProgram::new(&mut common);
94 let mut uart_tx = PioUartTx::new(9600, &mut common, sm0, p.PIN_4, &tx_program);
95
96 let rx_program = PioUartRxProgram::new(&mut common);
97 let mut uart_rx = PioUartRx::new(9600, &mut common, sm1, p.PIN_5, &rx_program);
98
99 // Pipe setup
100 let mut usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new();
101 let (mut usb_pipe_reader, mut usb_pipe_writer) = usb_pipe.split();
102
103 let mut uart_pipe: Pipe<NoopRawMutex, 20> = Pipe::new();
104 let (mut uart_pipe_reader, mut uart_pipe_writer) = uart_pipe.split();
105
106 let (mut usb_tx, mut usb_rx) = class.split();
107
108 // Read + write from USB
109 let usb_future = async {
110 loop {
111 info!("Wait for USB connection");
112 usb_rx.wait_connection().await;
113 info!("Connected");
114 let _ = join(
115 usb_read(&mut usb_rx, &mut uart_pipe_writer),
116 usb_write(&mut usb_tx, &mut usb_pipe_reader),
117 )
118 .await;
119 info!("Disconnected");
120 }
121 };
122
123 // Read + write from UART
124 let uart_future = join(
125 uart_read(&mut uart_rx, &mut usb_pipe_writer),
126 uart_write(&mut uart_tx, &mut uart_pipe_reader),
127 );
128
129 // Run everything concurrently.
130 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
131 join3(usb_fut, usb_future, uart_future).await;
132}
133
134struct Disconnected {}
135
136impl From<EndpointError> for Disconnected {
137 fn from(val: EndpointError) -> Self {
138 match val {
139 EndpointError::BufferOverflow => panic!("Buffer overflow"),
140 EndpointError::Disabled => Disconnected {},
141 }
142 }
143}
144
145/// Read from the USB and write it to the UART TX pipe
146async fn usb_read<'d, T: Instance + 'd>(
147 usb_rx: &mut Receiver<'d, Driver<'d, T>>,
148 uart_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>,
149) -> Result<(), Disconnected> {
150 let mut buf = [0; 64];
151 loop {
152 let n = usb_rx.read_packet(&mut buf).await?;
153 let data = &buf[..n];
154 trace!("USB IN: {:x}", data);
155 (*uart_pipe_writer).write(data).await;
156 }
157}
158
159/// Read from the USB TX pipe and write it to the USB
160async fn usb_write<'d, T: Instance + 'd>(
161 usb_tx: &mut Sender<'d, Driver<'d, T>>,
162 usb_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>,
163) -> Result<(), Disconnected> {
164 let mut buf = [0; 64];
165 loop {
166 let n = (*usb_pipe_reader).read(&mut buf).await;
167 let data = &buf[..n];
168 trace!("USB OUT: {:x}", data);
169 usb_tx.write_packet(&data).await?;
170 }
171}
172
173/// Read from the UART and write it to the USB TX pipe
174async fn uart_read<PIO: pio::Instance, const SM: usize>(
175 uart_rx: &mut PioUartRx<'_, PIO, SM>,
176 usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>,
177) -> ! {
178 let mut buf = [0; 64];
179 loop {
180 let n = uart_rx.read(&mut buf).await.expect("UART read error");
181 if n == 0 {
182 continue;
183 }
184 let data = &buf[..n];
185 trace!("UART IN: {:x}", buf);
186 (*usb_pipe_writer).write(data).await;
187 }
188}
189
190/// Read from the UART TX pipe and write it to the UART
191async fn uart_write<PIO: pio::Instance, const SM: usize>(
192 uart_tx: &mut PioUartTx<'_, PIO, SM>,
193 uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>,
194) -> ! {
195 let mut buf = [0; 64];
196 loop {
197 let n = (*uart_pipe_reader).read(&mut buf).await;
198 let data = &buf[..n];
199 trace!("UART OUT: {:x}", data);
200 let _ = uart_tx.write(&data).await;
201 }
202}
diff --git a/examples/rp23/src/bin/pio_ws2812.rs b/examples/rp23/src/bin/pio_ws2812.rs
index 00fe5e396..4d258234e 100644
--- a/examples/rp23/src/bin/pio_ws2812.rs
+++ b/examples/rp23/src/bin/pio_ws2812.rs
@@ -6,16 +6,12 @@
6 6
7use defmt::*; 7use defmt::*;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_rp::bind_interrupts;
9use embassy_rp::block::ImageDef; 10use embassy_rp::block::ImageDef;
10use embassy_rp::dma::{AnyChannel, Channel};
11use embassy_rp::peripherals::PIO0; 11use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{ 12use embassy_rp::pio::{InterruptHandler, Pio};
13 Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, 13use embassy_rp::pio_programs::ws2812::{PioWs2812, PioWs2812Program};
14}; 14use embassy_time::{Duration, Ticker};
15use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef};
16use embassy_time::{Duration, Ticker, Timer};
17use fixed::types::U24F8;
18use fixed_macro::fixed;
19use smart_leds::RGB8; 15use smart_leds::RGB8;
20use {defmt_rtt as _, panic_probe as _}; 16use {defmt_rtt as _, panic_probe as _};
21 17
@@ -23,110 +19,10 @@ use {defmt_rtt as _, panic_probe as _};
23#[used] 19#[used]
24pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 20pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
25 21
26// Program metadata for `picotool info`
27#[link_section = ".bi_entries"]
28#[used]
29pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
30 embassy_rp::binary_info::rp_program_name!(c"example"),
31 embassy_rp::binary_info::rp_cargo_version!(),
32 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
33 embassy_rp::binary_info::rp_program_build_attribute!(),
34];
35
36bind_interrupts!(struct Irqs { 22bind_interrupts!(struct Irqs {
37 PIO0_IRQ_0 => InterruptHandler<PIO0>; 23 PIO0_IRQ_0 => InterruptHandler<PIO0>;
38}); 24});
39 25
40pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> {
41 dma: PeripheralRef<'d, AnyChannel>,
42 sm: StateMachine<'d, P, S>,
43}
44
45impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> {
46 pub fn new(
47 pio: &mut Common<'d, P>,
48 mut sm: StateMachine<'d, P, S>,
49 dma: impl Peripheral<P = impl Channel> + 'd,
50 pin: impl PioPin,
51 ) -> Self {
52 into_ref!(dma);
53
54 // Setup sm0
55
56 // prepare the PIO program
57 let side_set = pio::SideSet::new(false, 1, false);
58 let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set);
59
60 const T1: u8 = 2; // start bit
61 const T2: u8 = 5; // data bit
62 const T3: u8 = 3; // stop bit
63 const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32;
64
65 let mut wrap_target = a.label();
66 let mut wrap_source = a.label();
67 let mut do_zero = a.label();
68 a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0);
69 a.bind(&mut wrap_target);
70 // Do stop bit
71 a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0);
72 // Do start bit
73 a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1);
74 // Do data bit = 1
75 a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1);
76 a.bind(&mut do_zero);
77 // Do data bit = 0
78 a.nop_with_delay_and_side_set(T2 - 1, 0);
79 a.bind(&mut wrap_source);
80
81 let prg = a.assemble_with_wrap(wrap_source, wrap_target);
82 let mut cfg = Config::default();
83
84 // Pin config
85 let out_pin = pio.make_pio_pin(pin);
86 cfg.set_out_pins(&[&out_pin]);
87 cfg.set_set_pins(&[&out_pin]);
88
89 cfg.use_program(&pio.load_program(&prg), &[&out_pin]);
90
91 // Clock config, measured in kHz to avoid overflows
92 // TODO CLOCK_FREQ should come from embassy_rp
93 let clock_freq = U24F8::from_num(clocks::clk_sys_freq() / 1000);
94 let ws2812_freq = fixed!(800: U24F8);
95 let bit_freq = ws2812_freq * CYCLES_PER_BIT;
96 cfg.clock_divider = clock_freq / bit_freq;
97
98 // FIFO config
99 cfg.fifo_join = FifoJoin::TxOnly;
100 cfg.shift_out = ShiftConfig {
101 auto_fill: true,
102 threshold: 24,
103 direction: ShiftDirection::Left,
104 };
105
106 sm.set_config(&cfg);
107 sm.set_enable(true);
108
109 Self {
110 dma: dma.map_into(),
111 sm,
112 }
113 }
114
115 pub async fn write(&mut self, colors: &[RGB8; N]) {
116 // Precompute the word bytes from the colors
117 let mut words = [0u32; N];
118 for i in 0..N {
119 let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8);
120 words[i] = word;
121 }
122
123 // DMA transfer
124 self.sm.tx().dma_push(self.dma.reborrow(), &words).await;
125
126 Timer::after_micros(55).await;
127 }
128}
129
130/// Input a value 0 to 255 to get a color value 26/// Input a value 0 to 255 to get a color value
131/// The colours are a transition r - g - b - back to r. 27/// The colours are a transition r - g - b - back to r.
132fn wheel(mut wheel_pos: u8) -> RGB8 { 28fn wheel(mut wheel_pos: u8) -> RGB8 {
@@ -157,7 +53,8 @@ async fn main(_spawner: Spawner) {
157 // Common neopixel pins: 53 // Common neopixel pins:
158 // Thing plus: 8 54 // Thing plus: 8
159 // Adafruit Feather: 16; Adafruit Feather+RFM95: 4 55 // Adafruit Feather: 16; Adafruit Feather+RFM95: 4
160 let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16); 56 let program = PioWs2812Program::new(&mut common);
57 let mut ws2812 = PioWs2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16, &program);
161 58
162 // Loop forever making RGB values and pushing them out to the WS2812. 59 // Loop forever making RGB values and pushing them out to the WS2812.
163 let mut ticker = Ticker::every(Duration::from_millis(10)); 60 let mut ticker = Ticker::every(Duration::from_millis(10));
diff --git a/examples/rp23/src/bin/pwm.rs b/examples/rp23/src/bin/pwm.rs
index bfc2c6f67..ed3c94f15 100644
--- a/examples/rp23/src/bin/pwm.rs
+++ b/examples/rp23/src/bin/pwm.rs
@@ -1,6 +1,8 @@
1//! This example shows how to use PWM (Pulse Width Modulation) in the RP2040 chip. 1//! This example shows how to use PWM (Pulse Width Modulation) in the RP235x chip.
2//! 2//!
3//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin. 3//! We demonstrate two ways of using PWM:
4//! 1. Via config
5//! 2. Via setting a duty cycle
4 6
5#![no_std] 7#![no_std]
6#![no_main] 8#![no_main]
@@ -8,7 +10,8 @@
8use defmt::*; 10use defmt::*;
9use embassy_executor::Spawner; 11use embassy_executor::Spawner;
10use embassy_rp::block::ImageDef; 12use embassy_rp::block::ImageDef;
11use embassy_rp::pwm::{Config, Pwm}; 13use embassy_rp::peripherals::{PIN_25, PIN_4, PWM_SLICE2, PWM_SLICE4};
14use embassy_rp::pwm::{Config, Pwm, SetDutyCycle};
12use embassy_time::Timer; 15use embassy_time::Timer;
13use {defmt_rtt as _, panic_probe as _}; 16use {defmt_rtt as _, panic_probe as _};
14 17
@@ -16,24 +19,23 @@ use {defmt_rtt as _, panic_probe as _};
16#[used] 19#[used]
17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 20pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
18 21
19// Program metadata for `picotool info`
20#[link_section = ".bi_entries"]
21#[used]
22pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
23 embassy_rp::binary_info::rp_program_name!(c"example"),
24 embassy_rp::binary_info::rp_cargo_version!(),
25 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
26 embassy_rp::binary_info::rp_program_build_attribute!(),
27];
28
29#[embassy_executor::main] 22#[embassy_executor::main]
30async fn main(_spawner: Spawner) { 23async fn main(spawner: Spawner) {
31 let p = embassy_rp::init(Default::default()); 24 let p = embassy_rp::init(Default::default());
25 spawner.spawn(pwm_set_config(p.PWM_SLICE4, p.PIN_25)).unwrap();
26 spawner.spawn(pwm_set_dutycycle(p.PWM_SLICE2, p.PIN_4)).unwrap();
27}
32 28
33 let mut c: Config = Default::default(); 29/// Demonstrate PWM by modifying & applying the config
34 c.top = 0x8000; 30///
31/// Using the onboard led, if You are using a different Board than plain Pico2 (i.e. W variant)
32/// you must use another slice & pin and an appropriate resistor.
33#[embassy_executor::task]
34async fn pwm_set_config(slice4: PWM_SLICE4, pin25: PIN_25) {
35 let mut c = Config::default();
36 c.top = 32_768;
35 c.compare_b = 8; 37 c.compare_b = 8;
36 let mut pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, c.clone()); 38 let mut pwm = Pwm::new_output_b(slice4, pin25, c.clone());
37 39
38 loop { 40 loop {
39 info!("current LED duty cycle: {}/32768", c.compare_b); 41 info!("current LED duty cycle: {}/32768", c.compare_b);
@@ -42,3 +44,41 @@ async fn main(_spawner: Spawner) {
42 pwm.set_config(&c); 44 pwm.set_config(&c);
43 } 45 }
44} 46}
47
48/// Demonstrate PWM by setting duty cycle
49///
50/// Using GP4 in Slice2, make sure to use an appropriate resistor.
51#[embassy_executor::task]
52async fn pwm_set_dutycycle(slice2: PWM_SLICE2, pin4: PIN_4) {
53 // If we aim for a specific frequency, here is how we can calculate the top value.
54 // The top value sets the period of the PWM cycle, so a counter goes from 0 to top and then wraps around to 0.
55 // Every such wraparound is one PWM cycle. So here is how we get 25KHz:
56 let desired_freq_hz = 25_000;
57 let clock_freq_hz = embassy_rp::clocks::clk_sys_freq();
58 let divider = 16u8;
59 let period = (clock_freq_hz / (desired_freq_hz * divider as u32)) as u16 - 1;
60
61 let mut c = Config::default();
62 c.top = period;
63 c.divider = divider.into();
64
65 let mut pwm = Pwm::new_output_a(slice2, pin4, c.clone());
66
67 loop {
68 // 100% duty cycle, fully on
69 pwm.set_duty_cycle_fully_on().unwrap();
70 Timer::after_secs(1).await;
71
72 // 66% duty cycle. Expressed as simple percentage.
73 pwm.set_duty_cycle_percent(66).unwrap();
74 Timer::after_secs(1).await;
75
76 // 25% duty cycle. Expressed as 32768/4 = 8192.
77 pwm.set_duty_cycle(c.top / 4).unwrap();
78 Timer::after_secs(1).await;
79
80 // 0% duty cycle, fully off.
81 pwm.set_duty_cycle_fully_off().unwrap();
82 Timer::after_secs(1).await;
83 }
84}
diff --git a/examples/rp23/src/bin/pwm_input.rs b/examples/rp23/src/bin/pwm_input.rs
index b65f2778b..ef87fe8b5 100644
--- a/examples/rp23/src/bin/pwm_input.rs
+++ b/examples/rp23/src/bin/pwm_input.rs
@@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _};
15#[used] 15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17 17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info::rp_program_name!(c"example"),
23 embassy_rp::binary_info::rp_cargo_version!(),
24 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info::rp_program_build_attribute!(),
26];
27
28#[embassy_executor::main] 18#[embassy_executor::main]
29async fn main(_spawner: Spawner) { 19async fn main(_spawner: Spawner) {
30 let p = embassy_rp::init(Default::default()); 20 let p = embassy_rp::init(Default::default());
diff --git a/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs b/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs
new file mode 100644
index 000000000..0682888e8
--- /dev/null
+++ b/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs
@@ -0,0 +1,110 @@
1//! # PWM TB6612FNG motor driver
2//!
3//! This example shows the use of a TB6612FNG motor driver. The driver is built on top of embedded_hal and the example demonstrates how embassy_rp can be used to interact with ist.
4
5#![no_std]
6#![no_main]
7
8use assign_resources::assign_resources;
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_rp::block::ImageDef;
12use embassy_rp::config::Config;
13use embassy_rp::gpio::Output;
14use embassy_rp::{gpio, peripherals, pwm};
15use embassy_time::{Duration, Timer};
16use tb6612fng::{DriveCommand, Motor, Tb6612fng};
17use {defmt_rtt as _, panic_probe as _};
18
19#[link_section = ".start_block"]
20#[used]
21pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
22
23assign_resources! {
24 motor: MotorResources {
25 standby_pin: PIN_22,
26 left_slice: PWM_SLICE6,
27 left_pwm_pin: PIN_28,
28 left_forward_pin: PIN_21,
29 left_backward_pin: PIN_20,
30 right_slice: PWM_SLICE5,
31 right_pwm_pin: PIN_27,
32 right_forward_pin: PIN_19,
33 right_backward_pin: PIN_18,
34 },
35}
36
37#[embassy_executor::main]
38async fn main(_spawner: Spawner) {
39 let p = embassy_rp::init(Config::default());
40 let s = split_resources!(p);
41 let r = s.motor;
42
43 // we want a PWM frequency of 10KHz, especially cheaper motors do not respond well to higher frequencies
44 let desired_freq_hz = 10_000;
45 let clock_freq_hz = embassy_rp::clocks::clk_sys_freq();
46 let divider = 16u8;
47 let period = (clock_freq_hz / (desired_freq_hz * divider as u32)) as u16 - 1;
48
49 // we need a standby output and two motors to construct a full TB6612FNG
50
51 // standby pin
52 let stby = Output::new(r.standby_pin, gpio::Level::Low);
53
54 // motor A, here defined to be the left motor
55 let left_fwd = gpio::Output::new(r.left_forward_pin, gpio::Level::Low);
56 let left_bckw = gpio::Output::new(r.left_backward_pin, gpio::Level::Low);
57 let mut left_speed = pwm::Config::default();
58 left_speed.top = period;
59 left_speed.divider = divider.into();
60 let left_pwm = pwm::Pwm::new_output_a(r.left_slice, r.left_pwm_pin, left_speed);
61 let left_motor = Motor::new(left_fwd, left_bckw, left_pwm).unwrap();
62
63 // motor B, here defined to be the right motor
64 let right_fwd = gpio::Output::new(r.right_forward_pin, gpio::Level::Low);
65 let right_bckw = gpio::Output::new(r.right_backward_pin, gpio::Level::Low);
66 let mut right_speed = pwm::Config::default();
67 right_speed.top = period;
68 right_speed.divider = divider.into();
69 let right_pwm = pwm::Pwm::new_output_b(r.right_slice, r.right_pwm_pin, right_speed);
70 let right_motor = Motor::new(right_fwd, right_bckw, right_pwm).unwrap();
71
72 // construct the motor driver
73 let mut control = Tb6612fng::new(left_motor, right_motor, stby).unwrap();
74
75 loop {
76 // wake up the motor driver
77 info!("end standby");
78 control.disable_standby().unwrap();
79 Timer::after(Duration::from_millis(100)).await;
80
81 // drive a straight line forward at 20% speed for 5s
82 info!("drive straight");
83 control.motor_a.drive(DriveCommand::Forward(80)).unwrap();
84 control.motor_b.drive(DriveCommand::Forward(80)).unwrap();
85 Timer::after(Duration::from_secs(5)).await;
86
87 // coast for 2s
88 info!("coast");
89 control.motor_a.drive(DriveCommand::Stop).unwrap();
90 control.motor_b.drive(DriveCommand::Stop).unwrap();
91 Timer::after(Duration::from_secs(2)).await;
92
93 // actively brake
94 info!("brake");
95 control.motor_a.drive(DriveCommand::Brake).unwrap();
96 control.motor_b.drive(DriveCommand::Brake).unwrap();
97 Timer::after(Duration::from_secs(1)).await;
98
99 // slowly turn for 3s
100 info!("turn");
101 control.motor_a.drive(DriveCommand::Backward(50)).unwrap();
102 control.motor_b.drive(DriveCommand::Forward(50)).unwrap();
103 Timer::after(Duration::from_secs(3)).await;
104
105 // and put the driver in standby mode and wait for 5s
106 info!("standby");
107 control.enable_standby().unwrap();
108 Timer::after(Duration::from_secs(5)).await;
109 }
110}
diff --git a/examples/rp23/src/bin/rosc.rs b/examples/rp23/src/bin/rosc.rs
index f65b236b1..a096f0b7a 100644
--- a/examples/rp23/src/bin/rosc.rs
+++ b/examples/rp23/src/bin/rosc.rs
@@ -17,16 +17,6 @@ use {defmt_rtt as _, panic_probe as _};
17#[used] 17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19 19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info::rp_program_name!(c"example"),
25 embassy_rp::binary_info::rp_cargo_version!(),
26 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info::rp_program_build_attribute!(),
28];
29
30#[embassy_executor::main] 20#[embassy_executor::main]
31async fn main(_spawner: Spawner) { 21async fn main(_spawner: Spawner) {
32 let mut config = embassy_rp::config::Config::default(); 22 let mut config = embassy_rp::config::Config::default();
diff --git a/examples/rp23/src/bin/shared_bus.rs b/examples/rp23/src/bin/shared_bus.rs
index b3fde13e3..2151ccb56 100644
--- a/examples/rp23/src/bin/shared_bus.rs
+++ b/examples/rp23/src/bin/shared_bus.rs
@@ -23,16 +23,6 @@ use {defmt_rtt as _, panic_probe as _};
23#[used] 23#[used]
24pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 24pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
25 25
26// Program metadata for `picotool info`
27#[link_section = ".bi_entries"]
28#[used]
29pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
30 embassy_rp::binary_info::rp_program_name!(c"example"),
31 embassy_rp::binary_info::rp_cargo_version!(),
32 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
33 embassy_rp::binary_info::rp_program_build_attribute!(),
34];
35
36type Spi1Bus = Mutex<NoopRawMutex, Spi<'static, SPI1, spi::Async>>; 26type Spi1Bus = Mutex<NoopRawMutex, Spi<'static, SPI1, spi::Async>>;
37type I2c1Bus = Mutex<NoopRawMutex, I2c<'static, I2C1, i2c::Async>>; 27type I2c1Bus = Mutex<NoopRawMutex, I2c<'static, I2C1, i2c::Async>>;
38 28
diff --git a/examples/rp23/src/bin/sharing.rs b/examples/rp23/src/bin/sharing.rs
index 4a3301cfd..68eb5d133 100644
--- a/examples/rp23/src/bin/sharing.rs
+++ b/examples/rp23/src/bin/sharing.rs
@@ -36,16 +36,6 @@ use {defmt_rtt as _, panic_probe as _};
36#[used] 36#[used]
37pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 37pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
38 38
39// Program metadata for `picotool info`
40#[link_section = ".bi_entries"]
41#[used]
42pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
43 embassy_rp::binary_info::rp_program_name!(c"example"),
44 embassy_rp::binary_info::rp_cargo_version!(),
45 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
46 embassy_rp::binary_info::rp_program_build_attribute!(),
47];
48
49type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, UART0, uart::Async>>; 39type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, UART0, uart::Async>>;
50 40
51struct MyType { 41struct MyType {
diff --git a/examples/rp23/src/bin/spi.rs b/examples/rp23/src/bin/spi.rs
index 924873e60..aacb8c7db 100644
--- a/examples/rp23/src/bin/spi.rs
+++ b/examples/rp23/src/bin/spi.rs
@@ -17,16 +17,6 @@ use {defmt_rtt as _, panic_probe as _};
17#[used] 17#[used]
18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 18pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
19 19
20// Program metadata for `picotool info`
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info::rp_program_name!(c"example"),
25 embassy_rp::binary_info::rp_cargo_version!(),
26 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
27 embassy_rp::binary_info::rp_program_build_attribute!(),
28];
29
30#[embassy_executor::main] 20#[embassy_executor::main]
31async fn main(_spawner: Spawner) { 21async fn main(_spawner: Spawner) {
32 let p = embassy_rp::init(Default::default()); 22 let p = embassy_rp::init(Default::default());
diff --git a/examples/rp23/src/bin/spi_async.rs b/examples/rp23/src/bin/spi_async.rs
index 4a74f991c..ac7f02fa8 100644
--- a/examples/rp23/src/bin/spi_async.rs
+++ b/examples/rp23/src/bin/spi_async.rs
@@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _};
15#[used] 15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17 17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info::rp_program_name!(c"example"),
23 embassy_rp::binary_info::rp_cargo_version!(),
24 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info::rp_program_build_attribute!(),
26];
27
28#[embassy_executor::main] 18#[embassy_executor::main]
29async fn main(_spawner: Spawner) { 19async fn main(_spawner: Spawner) {
30 let p = embassy_rp::init(Default::default()); 20 let p = embassy_rp::init(Default::default());
diff --git a/examples/rp23/src/bin/spi_display.rs b/examples/rp23/src/bin/spi_display.rs
index 71dd84658..6b7c0781f 100644
--- a/examples/rp23/src/bin/spi_display.rs
+++ b/examples/rp23/src/bin/spi_display.rs
@@ -1,4 +1,4 @@
1//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip. 1//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2350 chip.
2//! 2//!
3//! Example written for a display using the ST7789 chip. Possibly the Waveshare Pico-ResTouch 3//! Example written for a display using the ST7789 chip. Possibly the Waveshare Pico-ResTouch
4//! (https://www.waveshare.com/wiki/Pico-ResTouch-LCD-2.8) 4//! (https://www.waveshare.com/wiki/Pico-ResTouch-LCD-2.8)
@@ -9,6 +9,7 @@
9use core::cell::RefCell; 9use core::cell::RefCell;
10 10
11use defmt::*; 11use defmt::*;
12use display_interface_spi::SPIInterface;
12use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig; 13use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig;
13use embassy_executor::Spawner; 14use embassy_executor::Spawner;
14use embassy_rp::block::ImageDef; 15use embassy_rp::block::ImageDef;
@@ -25,24 +26,15 @@ use embedded_graphics::pixelcolor::Rgb565;
25use embedded_graphics::prelude::*; 26use embedded_graphics::prelude::*;
26use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; 27use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle};
27use embedded_graphics::text::Text; 28use embedded_graphics::text::Text;
28use st7789::{Orientation, ST7789}; 29use mipidsi::models::ST7789;
30use mipidsi::options::{Orientation, Rotation};
31use mipidsi::Builder;
29use {defmt_rtt as _, panic_probe as _}; 32use {defmt_rtt as _, panic_probe as _};
30 33
31#[link_section = ".start_block"] 34#[link_section = ".start_block"]
32#[used] 35#[used]
33pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 36pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
34 37
35// Program metadata for `picotool info`
36#[link_section = ".bi_entries"]
37#[used]
38pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
39 embassy_rp::binary_info::rp_program_name!(c"example"),
40 embassy_rp::binary_info::rp_cargo_version!(),
41 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
42 embassy_rp::binary_info::rp_program_build_attribute!(),
43];
44
45use crate::my_display_interface::SPIDeviceInterface;
46use crate::touch::Touch; 38use crate::touch::Touch;
47 39
48const DISPLAY_FREQ: u32 = 64_000_000; 40const DISPLAY_FREQ: u32 = 64_000_000;
@@ -89,17 +81,15 @@ async fn main(_spawner: Spawner) {
89 let _bl = Output::new(bl, Level::High); 81 let _bl = Output::new(bl, Level::High);
90 82
91 // display interface abstraction from SPI and DC 83 // display interface abstraction from SPI and DC
92 let di = SPIDeviceInterface::new(display_spi, dcx); 84 let di = SPIInterface::new(display_spi, dcx);
93 85
94 // create driver 86 // Define the display from the display interface and initialize it
95 let mut display = ST7789::new(di, rst, 240, 320); 87 let mut display = Builder::new(ST7789, di)
96 88 .display_size(240, 320)
97 // initialize 89 .reset_pin(rst)
98 display.init(&mut Delay).unwrap(); 90 .orientation(Orientation::new().rotate(Rotation::Deg90))
99 91 .init(&mut Delay)
100 // set default orientation 92 .unwrap();
101 display.set_orientation(Orientation::Landscape).unwrap();
102
103 display.clear(Rgb565::BLACK).unwrap(); 93 display.clear(Rgb565::BLACK).unwrap();
104 94
105 let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86); 95 let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86);
@@ -190,138 +180,3 @@ mod touch {
190 } 180 }
191 } 181 }
192} 182}
193
194mod my_display_interface {
195 use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand};
196 use embedded_hal_1::digital::OutputPin;
197 use embedded_hal_1::spi::SpiDevice;
198
199 /// SPI display interface.
200 ///
201 /// This combines the SPI peripheral and a data/command pin
202 pub struct SPIDeviceInterface<SPI, DC> {
203 spi: SPI,
204 dc: DC,
205 }
206
207 impl<SPI, DC> SPIDeviceInterface<SPI, DC>
208 where
209 SPI: SpiDevice,
210 DC: OutputPin,
211 {
212 /// Create new SPI interface for communciation with a display driver
213 pub fn new(spi: SPI, dc: DC) -> Self {
214 Self { spi, dc }
215 }
216 }
217
218 impl<SPI, DC> WriteOnlyDataCommand for SPIDeviceInterface<SPI, DC>
219 where
220 SPI: SpiDevice,
221 DC: OutputPin,
222 {
223 fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> {
224 // 1 = data, 0 = command
225 self.dc.set_low().map_err(|_| DisplayError::DCError)?;
226
227 send_u8(&mut self.spi, cmds).map_err(|_| DisplayError::BusWriteError)?;
228 Ok(())
229 }
230
231 fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> {
232 // 1 = data, 0 = command
233 self.dc.set_high().map_err(|_| DisplayError::DCError)?;
234
235 send_u8(&mut self.spi, buf).map_err(|_| DisplayError::BusWriteError)?;
236 Ok(())
237 }
238 }
239
240 fn send_u8<T: SpiDevice>(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> {
241 match words {
242 DataFormat::U8(slice) => spi.write(slice),
243 DataFormat::U16(slice) => {
244 use byte_slice_cast::*;
245 spi.write(slice.as_byte_slice())
246 }
247 DataFormat::U16LE(slice) => {
248 use byte_slice_cast::*;
249 for v in slice.as_mut() {
250 *v = v.to_le();
251 }
252 spi.write(slice.as_byte_slice())
253 }
254 DataFormat::U16BE(slice) => {
255 use byte_slice_cast::*;
256 for v in slice.as_mut() {
257 *v = v.to_be();
258 }
259 spi.write(slice.as_byte_slice())
260 }
261 DataFormat::U8Iter(iter) => {
262 let mut buf = [0; 32];
263 let mut i = 0;
264
265 for v in iter.into_iter() {
266 buf[i] = v;
267 i += 1;
268
269 if i == buf.len() {
270 spi.write(&buf)?;
271 i = 0;
272 }
273 }
274
275 if i > 0 {
276 spi.write(&buf[..i])?;
277 }
278
279 Ok(())
280 }
281 DataFormat::U16LEIter(iter) => {
282 use byte_slice_cast::*;
283 let mut buf = [0; 32];
284 let mut i = 0;
285
286 for v in iter.map(u16::to_le) {
287 buf[i] = v;
288 i += 1;
289
290 if i == buf.len() {
291 spi.write(&buf.as_byte_slice())?;
292 i = 0;
293 }
294 }
295
296 if i > 0 {
297 spi.write(&buf[..i].as_byte_slice())?;
298 }
299
300 Ok(())
301 }
302 DataFormat::U16BEIter(iter) => {
303 use byte_slice_cast::*;
304 let mut buf = [0; 64];
305 let mut i = 0;
306 let len = buf.len();
307
308 for v in iter.map(u16::to_be) {
309 buf[i] = v;
310 i += 1;
311
312 if i == len {
313 spi.write(&buf.as_byte_slice())?;
314 i = 0;
315 }
316 }
317
318 if i > 0 {
319 spi.write(&buf[..i].as_byte_slice())?;
320 }
321
322 Ok(())
323 }
324 _ => unimplemented!(),
325 }
326 }
327}
diff --git a/examples/rp23/src/bin/spi_sdmmc.rs b/examples/rp23/src/bin/spi_sdmmc.rs
index dabf41ab8..cfc38dfd9 100644
--- a/examples/rp23/src/bin/spi_sdmmc.rs
+++ b/examples/rp23/src/bin/spi_sdmmc.rs
@@ -21,16 +21,6 @@ use {defmt_rtt as _, panic_probe as _};
21#[used] 21#[used]
22pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 22pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
23 23
24// Program metadata for `picotool info`
25#[link_section = ".bi_entries"]
26#[used]
27pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
28 embassy_rp::binary_info::rp_program_name!(c"example"),
29 embassy_rp::binary_info::rp_cargo_version!(),
30 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
31 embassy_rp::binary_info::rp_program_build_attribute!(),
32];
33
34struct DummyTimesource(); 24struct DummyTimesource();
35 25
36impl embedded_sdmmc::TimeSource for DummyTimesource { 26impl embedded_sdmmc::TimeSource for DummyTimesource {
@@ -66,7 +56,7 @@ async fn main(_spawner: Spawner) {
66 // Now that the card is initialized, the SPI clock can go faster 56 // Now that the card is initialized, the SPI clock can go faster
67 let mut config = spi::Config::default(); 57 let mut config = spi::Config::default();
68 config.frequency = 16_000_000; 58 config.frequency = 16_000_000;
69 sdcard.spi(|dev| dev.bus_mut().set_config(&config)).ok(); 59 sdcard.spi(|dev| SetConfig::set_config(dev.bus_mut(), &config)).ok();
70 60
71 // Now let's look for volumes (also known as partitions) on our block device. 61 // Now let's look for volumes (also known as partitions) on our block device.
72 // To do this we need a Volume Manager. It will take ownership of the block device. 62 // To do this we need a Volume Manager. It will take ownership of the block device.
diff --git a/examples/rp23/src/bin/trng.rs b/examples/rp23/src/bin/trng.rs
index e146baa2e..8251ebd8b 100644
--- a/examples/rp23/src/bin/trng.rs
+++ b/examples/rp23/src/bin/trng.rs
@@ -18,16 +18,6 @@ use {defmt_rtt as _, panic_probe as _};
18#[used] 18#[used]
19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
20 20
21// Program metadata for `picotool info`
22#[link_section = ".bi_entries"]
23#[used]
24pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
25 embassy_rp::binary_info::rp_program_name!(c"example"),
26 embassy_rp::binary_info::rp_cargo_version!(),
27 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
28 embassy_rp::binary_info::rp_program_build_attribute!(),
29];
30
31bind_interrupts!(struct Irqs { 21bind_interrupts!(struct Irqs {
32 TRNG_IRQ => embassy_rp::trng::InterruptHandler<TRNG>; 22 TRNG_IRQ => embassy_rp::trng::InterruptHandler<TRNG>;
33}); 23});
diff --git a/examples/rp23/src/bin/uart.rs b/examples/rp23/src/bin/uart.rs
index 0ffe0b293..fe28bb046 100644
--- a/examples/rp23/src/bin/uart.rs
+++ b/examples/rp23/src/bin/uart.rs
@@ -16,16 +16,6 @@ use {defmt_rtt as _, panic_probe as _};
16#[used] 16#[used]
17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
18 18
19// Program metadata for `picotool info`
20#[link_section = ".bi_entries"]
21#[used]
22pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
23 embassy_rp::binary_info::rp_program_name!(c"example"),
24 embassy_rp::binary_info::rp_cargo_version!(),
25 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
26 embassy_rp::binary_info::rp_program_build_attribute!(),
27];
28
29#[embassy_executor::main] 19#[embassy_executor::main]
30async fn main(_spawner: Spawner) { 20async fn main(_spawner: Spawner) {
31 let p = embassy_rp::init(Default::default()); 21 let p = embassy_rp::init(Default::default());
diff --git a/examples/rp23/src/bin/uart_buffered_split.rs b/examples/rp23/src/bin/uart_buffered_split.rs
index 4e69a20c4..9ed130727 100644
--- a/examples/rp23/src/bin/uart_buffered_split.rs
+++ b/examples/rp23/src/bin/uart_buffered_split.rs
@@ -22,16 +22,6 @@ use {defmt_rtt as _, panic_probe as _};
22#[used] 22#[used]
23pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 23pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
24 24
25// Program metadata for `picotool info`
26#[link_section = ".bi_entries"]
27#[used]
28pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
29 embassy_rp::binary_info::rp_program_name!(c"example"),
30 embassy_rp::binary_info::rp_cargo_version!(),
31 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
32 embassy_rp::binary_info::rp_program_build_attribute!(),
33];
34
35bind_interrupts!(struct Irqs { 25bind_interrupts!(struct Irqs {
36 UART0_IRQ => BufferedInterruptHandler<UART0>; 26 UART0_IRQ => BufferedInterruptHandler<UART0>;
37}); 27});
diff --git a/examples/rp23/src/bin/uart_r503.rs b/examples/rp23/src/bin/uart_r503.rs
index 5ac8839e3..9aed42785 100644
--- a/examples/rp23/src/bin/uart_r503.rs
+++ b/examples/rp23/src/bin/uart_r503.rs
@@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _};
15#[used] 15#[used]
16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 16pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
17 17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info::rp_program_name!(c"example"),
23 embassy_rp::binary_info::rp_cargo_version!(),
24 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
25 embassy_rp::binary_info::rp_program_build_attribute!(),
26];
27
28bind_interrupts!(pub struct Irqs { 18bind_interrupts!(pub struct Irqs {
29 UART0_IRQ => UARTInterruptHandler<UART0>; 19 UART0_IRQ => UARTInterruptHandler<UART0>;
30}); 20});
diff --git a/examples/rp23/src/bin/uart_unidir.rs b/examples/rp23/src/bin/uart_unidir.rs
index 988e44a79..12214c4c2 100644
--- a/examples/rp23/src/bin/uart_unidir.rs
+++ b/examples/rp23/src/bin/uart_unidir.rs
@@ -21,16 +21,6 @@ use {defmt_rtt as _, panic_probe as _};
21#[used] 21#[used]
22pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 22pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
23 23
24// Program metadata for `picotool info`
25#[link_section = ".bi_entries"]
26#[used]
27pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
28 embassy_rp::binary_info::rp_program_name!(c"example"),
29 embassy_rp::binary_info::rp_cargo_version!(),
30 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
31 embassy_rp::binary_info::rp_program_build_attribute!(),
32];
33
34bind_interrupts!(struct Irqs { 24bind_interrupts!(struct Irqs {
35 UART1_IRQ => InterruptHandler<UART1>; 25 UART1_IRQ => InterruptHandler<UART1>;
36}); 26});
diff --git a/examples/rp23/src/bin/usb_hid_keyboard.rs b/examples/rp23/src/bin/usb_hid_keyboard.rs
new file mode 100644
index 000000000..ec1e88746
--- /dev/null
+++ b/examples/rp23/src/bin/usb_hid_keyboard.rs
@@ -0,0 +1,193 @@
1#![no_std]
2#![no_main]
3
4use core::sync::atomic::{AtomicBool, Ordering};
5
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_futures::join::join;
9use embassy_rp::bind_interrupts;
10use embassy_rp::block::ImageDef;
11use embassy_rp::gpio::{Input, Pull};
12use embassy_rp::peripherals::USB;
13use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler};
14use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State as HidState};
15use embassy_usb::control::OutResponse;
16use embassy_usb::{Builder, Config, Handler};
17use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};
18use {defmt_rtt as _, panic_probe as _};
19
20#[link_section = ".start_block"]
21#[used]
22pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
23
24bind_interrupts!(struct Irqs {
25 USBCTRL_IRQ => InterruptHandler<USB>;
26});
27
28#[embassy_executor::main]
29async fn main(_spawner: Spawner) {
30 let p = embassy_rp::init(Default::default());
31 // Create the driver, from the HAL.
32 let driver = UsbDriver::new(p.USB, Irqs);
33
34 // Create embassy-usb Config
35 let mut config = Config::new(0xc0de, 0xcafe);
36 config.manufacturer = Some("Embassy");
37 config.product = Some("HID keyboard example");
38 config.serial_number = Some("12345678");
39 config.max_power = 100;
40 config.max_packet_size_0 = 64;
41
42 // Create embassy-usb DeviceBuilder using the driver and config.
43 // It needs some buffers for building the descriptors.
44 let mut config_descriptor = [0; 256];
45 let mut bos_descriptor = [0; 256];
46 // You can also add a Microsoft OS descriptor.
47 let mut msos_descriptor = [0; 256];
48 let mut control_buf = [0; 64];
49 let mut request_handler = MyRequestHandler {};
50 let mut device_handler = MyDeviceHandler::new();
51
52 let mut state = HidState::new();
53
54 let mut builder = Builder::new(
55 driver,
56 config,
57 &mut config_descriptor,
58 &mut bos_descriptor,
59 &mut msos_descriptor,
60 &mut control_buf,
61 );
62
63 builder.handler(&mut device_handler);
64
65 // Create classes on the builder.
66 let config = embassy_usb::class::hid::Config {
67 report_descriptor: KeyboardReport::desc(),
68 request_handler: None,
69 poll_ms: 60,
70 max_packet_size: 64,
71 };
72 let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config);
73
74 // Build the builder.
75 let mut usb = builder.build();
76
77 // Run the USB device.
78 let usb_fut = usb.run();
79
80 // Set up the signal pin that will be used to trigger the keyboard.
81 let mut signal_pin = Input::new(p.PIN_16, Pull::None);
82
83 // Enable the schmitt trigger to slightly debounce.
84 signal_pin.set_schmitt(true);
85
86 let (reader, mut writer) = hid.split();
87
88 // Do stuff with the class!
89 let in_fut = async {
90 loop {
91 info!("Waiting for HIGH on pin 16");
92 signal_pin.wait_for_high().await;
93 info!("HIGH DETECTED");
94 // Create a report with the A key pressed. (no shift modifier)
95 let report = KeyboardReport {
96 keycodes: [4, 0, 0, 0, 0, 0],
97 leds: 0,
98 modifier: 0,
99 reserved: 0,
100 };
101 // Send the report.
102 match writer.write_serialize(&report).await {
103 Ok(()) => {}
104 Err(e) => warn!("Failed to send report: {:?}", e),
105 };
106 signal_pin.wait_for_low().await;
107 info!("LOW DETECTED");
108 let report = KeyboardReport {
109 keycodes: [0, 0, 0, 0, 0, 0],
110 leds: 0,
111 modifier: 0,
112 reserved: 0,
113 };
114 match writer.write_serialize(&report).await {
115 Ok(()) => {}
116 Err(e) => warn!("Failed to send report: {:?}", e),
117 };
118 }
119 };
120
121 let out_fut = async {
122 reader.run(false, &mut request_handler).await;
123 };
124
125 // Run everything concurrently.
126 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
127 join(usb_fut, join(in_fut, out_fut)).await;
128}
129
130struct MyRequestHandler {}
131
132impl RequestHandler for MyRequestHandler {
133 fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {
134 info!("Get report for {:?}", id);
135 None
136 }
137
138 fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse {
139 info!("Set report for {:?}: {=[u8]}", id, data);
140 OutResponse::Accepted
141 }
142
143 fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) {
144 info!("Set idle rate for {:?} to {:?}", id, dur);
145 }
146
147 fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> {
148 info!("Get idle rate for {:?}", id);
149 None
150 }
151}
152
153struct MyDeviceHandler {
154 configured: AtomicBool,
155}
156
157impl MyDeviceHandler {
158 fn new() -> Self {
159 MyDeviceHandler {
160 configured: AtomicBool::new(false),
161 }
162 }
163}
164
165impl Handler for MyDeviceHandler {
166 fn enabled(&mut self, enabled: bool) {
167 self.configured.store(false, Ordering::Relaxed);
168 if enabled {
169 info!("Device enabled");
170 } else {
171 info!("Device disabled");
172 }
173 }
174
175 fn reset(&mut self) {
176 self.configured.store(false, Ordering::Relaxed);
177 info!("Bus reset, the Vbus current limit is 100mA");
178 }
179
180 fn addressed(&mut self, addr: u8) {
181 self.configured.store(false, Ordering::Relaxed);
182 info!("USB address set to: {}", addr);
183 }
184
185 fn configured(&mut self, configured: bool) {
186 self.configured.store(configured, Ordering::Relaxed);
187 if configured {
188 info!("Device configured, it may now draw up to the configured current limit from Vbus.")
189 } else {
190 info!("Device is no longer configured, the Vbus current limit is 100mA.");
191 }
192 }
193}
diff --git a/examples/rp23/src/bin/usb_webusb.rs b/examples/rp23/src/bin/usb_webusb.rs
index 3ade2226b..15279cabc 100644
--- a/examples/rp23/src/bin/usb_webusb.rs
+++ b/examples/rp23/src/bin/usb_webusb.rs
@@ -34,16 +34,6 @@ use {defmt_rtt as _, panic_probe as _};
34#[used] 34#[used]
35pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 35pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
36 36
37// Program metadata for `picotool info`
38#[link_section = ".bi_entries"]
39#[used]
40pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
41 embassy_rp::binary_info::rp_program_name!(c"example"),
42 embassy_rp::binary_info::rp_cargo_version!(),
43 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
44 embassy_rp::binary_info::rp_program_build_attribute!(),
45];
46
47bind_interrupts!(struct Irqs { 37bind_interrupts!(struct Irqs {
48 USBCTRL_IRQ => InterruptHandler<USB>; 38 USBCTRL_IRQ => InterruptHandler<USB>;
49}); 39});
diff --git a/examples/rp23/src/bin/watchdog.rs b/examples/rp23/src/bin/watchdog.rs
index a901c1164..efc24c4e3 100644
--- a/examples/rp23/src/bin/watchdog.rs
+++ b/examples/rp23/src/bin/watchdog.rs
@@ -18,16 +18,6 @@ use {defmt_rtt as _, panic_probe as _};
18#[used] 18#[used]
19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
20 20
21// Program metadata for `picotool info`
22#[link_section = ".bi_entries"]
23#[used]
24pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
25 embassy_rp::binary_info::rp_program_name!(c"example"),
26 embassy_rp::binary_info::rp_cargo_version!(),
27 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
28 embassy_rp::binary_info::rp_program_build_attribute!(),
29];
30
31#[embassy_executor::main] 21#[embassy_executor::main]
32async fn main(_spawner: Spawner) { 22async fn main(_spawner: Spawner) {
33 let p = embassy_rp::init(Default::default()); 23 let p = embassy_rp::init(Default::default());
diff --git a/examples/rp23/src/bin/zerocopy.rs b/examples/rp23/src/bin/zerocopy.rs
index 86fca6f12..d317c4b56 100644
--- a/examples/rp23/src/bin/zerocopy.rs
+++ b/examples/rp23/src/bin/zerocopy.rs
@@ -23,16 +23,6 @@ use {defmt_rtt as _, panic_probe as _};
23#[used] 23#[used]
24pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); 24pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
25 25
26// Program metadata for `picotool info`
27#[link_section = ".bi_entries"]
28#[used]
29pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
30 embassy_rp::binary_info::rp_program_name!(c"example"),
31 embassy_rp::binary_info::rp_cargo_version!(),
32 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
33 embassy_rp::binary_info::rp_program_build_attribute!(),
34];
35
36type SampleBuffer = [u16; 512]; 26type SampleBuffer = [u16; 512];
37 27
38bind_interrupts!(struct Irqs { 28bind_interrupts!(struct Irqs {
diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml
index 87491b1d2..e43fd77c8 100644
--- a/examples/std/Cargo.toml
+++ b/examples/std/Cargo.toml
@@ -5,10 +5,10 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["log"] } 8embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["log"] }
9embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-std", "executor-thread", "log", "integrated-timers"] } 9embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-std", "executor-thread", "log"] }
10embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "std", ] } 10embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "std", ] }
11embassy-net = { version = "0.4.0", path = "../../embassy-net", features=[ "std", "log", "medium-ethernet", "medium-ip", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] } 11embassy-net = { version = "0.5.0", path = "../../embassy-net", features=[ "std", "log", "medium-ethernet", "medium-ip", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] }
12embassy-net-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" } 12embassy-net-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" }
13embassy-net-ppp = { version = "0.1.0", path = "../../embassy-net-ppp", features = ["log"]} 13embassy-net-ppp = { version = "0.1.0", path = "../../embassy-net-ppp", features = ["log"]}
14embedded-io-async = { version = "0.6.1" } 14embedded-io-async = { version = "0.6.1" }
diff --git a/examples/std/src/bin/net_ppp.rs b/examples/std/src/bin/net_ppp.rs
index 7d0f1327f..ea3fbebef 100644
--- a/examples/std/src/bin/net_ppp.rs
+++ b/examples/std/src/bin/net_ppp.rs
@@ -16,7 +16,7 @@ use async_io::Async;
16use clap::Parser; 16use clap::Parser;
17use embassy_executor::{Executor, Spawner}; 17use embassy_executor::{Executor, Spawner};
18use embassy_net::tcp::TcpSocket; 18use embassy_net::tcp::TcpSocket;
19use embassy_net::{Config, ConfigV4, Ipv4Address, Ipv4Cidr, Stack, StackResources}; 19use embassy_net::{Config, ConfigV4, Ipv4Cidr, Stack, StackResources};
20use embassy_net_ppp::Runner; 20use embassy_net_ppp::Runner;
21use embedded_io_async::Write; 21use embedded_io_async::Write;
22use futures::io::BufReader; 22use futures::io::BufReader;
@@ -60,10 +60,10 @@ async fn ppp_task(stack: Stack<'static>, mut runner: Runner<'static>, port: Seri
60 }; 60 };
61 let mut dns_servers = Vec::new(); 61 let mut dns_servers = Vec::new();
62 for s in ipv4.dns_servers.iter().flatten() { 62 for s in ipv4.dns_servers.iter().flatten() {
63 let _ = dns_servers.push(Ipv4Address::from_bytes(&s.0)); 63 let _ = dns_servers.push(*s);
64 } 64 }
65 let config = ConfigV4::Static(embassy_net::StaticConfigV4 { 65 let config = ConfigV4::Static(embassy_net::StaticConfigV4 {
66 address: Ipv4Cidr::new(Ipv4Address::from_bytes(&addr.0), 0), 66 address: Ipv4Cidr::new(addr, 0),
67 gateway: None, 67 gateway: None,
68 dns_servers, 68 dns_servers,
69 }); 69 });
diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml
index 9102467eb..5ac3018e1 100644
--- a/examples/stm32c0/Cargo.toml
+++ b/examples/stm32c0/Cargo.toml
@@ -7,8 +7,8 @@ license = "MIT OR Apache-2.0"
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.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13 13
14defmt = "0.3" 14defmt = "0.3"
diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml
index 724efdaff..af3ef7abb 100644
--- a/examples/stm32f0/Cargo.toml
+++ b/examples/stm32f0/Cargo.toml
@@ -6,14 +6,14 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8# Change stm32f091rc to your chip name, if necessary. 8# Change stm32f091rc to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "memory-x", "stm32f091rc", "time-driver-tim2", "exti", "unstable-pac"] }
10cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } 10cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
11cortex-m-rt = "0.7.0" 11cortex-m-rt = "0.7.0"
12defmt = "0.3" 12defmt = "0.3"
13defmt-rtt = "0.4" 13defmt-rtt = "0.4"
14panic-probe = { version = "0.3", features = ["print-defmt"] } 14panic-probe = { version = "0.3", features = ["print-defmt"] }
15embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 15embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
16embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 16embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
17embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 17embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
18static_cell = "2" 18static_cell = "2"
19portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] } 19portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] }
diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml
index 0084651a3..538e95dfb 100644
--- a/examples/stm32f1/Cargo.toml
+++ b/examples/stm32f1/Cargo.toml
@@ -7,8 +7,8 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8# Change stm32f103c8 to your chip name, if necessary. 8# Change stm32f103c8 to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any" ] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any" ] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 13embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml
index 60eb0eb93..48d524b90 100644
--- a/examples/stm32f2/Cargo.toml
+++ b/examples/stm32f2/Cargo.toml
@@ -7,8 +7,8 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8# Change stm32f207zg to your chip name, if necessary. 8# Change stm32f207zg to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13 13
14defmt = "0.3" 14defmt = "0.3"
diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml
index 7fda410d9..66fb34223 100644
--- a/examples/stm32f3/Cargo.toml
+++ b/examples/stm32f3/Cargo.toml
@@ -6,9 +6,9 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8# Change stm32f303ze to your chip name, if necessary. 8# Change stm32f303ze to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-tim2", "exti"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 13embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
diff --git a/examples/stm32f3/README.md b/examples/stm32f3/README.md
new file mode 100644
index 000000000..0a85c4858
--- /dev/null
+++ b/examples/stm32f3/README.md
@@ -0,0 +1,24 @@
1# Examples for STM32F3 family
2Run individual examples with
3```
4cargo run --bin <module-name>
5```
6for example
7```
8cargo run --bin blinky
9```
10
11## Checklist before running examples
12You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.
13
14* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for F303ZE it should be `probe-rs run --chip STM32F303ZETx`. (use `probe-rs chip list` to find your chip)
15* [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for F303ZE it should be `stm32f303ze`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip.
16* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.
17* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic
18
19If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
20
21* Which example you are trying to run
22* Which chip and board you are using
23
24Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
diff --git a/examples/stm32f3/src/bin/blocking-tsc.rs b/examples/stm32f3/src/bin/blocking-tsc.rs
deleted file mode 100644
index 5c8dac94f..000000000
--- a/examples/stm32f3/src/bin/blocking-tsc.rs
+++ /dev/null
@@ -1,98 +0,0 @@
1// Example of polling TSC (Touch Sensing Controller) that lights an LED when touch is detected.
2//
3// Suggested physical setup on STM32F303ZE Nucleo board:
4// - Connect a 1000pF capacitor between pin A0 and GND. This is your sampling capacitor.
5// - Connect one end of a 1K resistor to pin A1 and leave the other end loose.
6// The loose end will act as touch sensor which will register your touch.
7//
8// Troubleshooting the setup:
9// - If no touch seems to be registered, then try to disconnect the sampling capacitor from GND momentarily,
10// now the led should light up. Next try using a different value for the sampling capacitor.
11// Also experiment with increasing the values for `ct_pulse_high_length`, `ct_pulse_low_length`, `pulse_generator_prescaler`, `max_count_value` and `discharge_delay`.
12//
13// All configuration values and sampling capacitor value have been determined experimentally.
14// Suitable configuration and discharge delay values are highly dependent on the value of the sample capacitor. For example, a shorter discharge delay can be used with smaller capacitor values.
15//
16#![no_std]
17#![no_main]
18
19use defmt::*;
20use embassy_stm32::gpio::{Level, Output, Speed};
21use embassy_stm32::tsc::{self, *};
22use embassy_time::Timer;
23use {defmt_rtt as _, panic_probe as _};
24
25/// This example is written for the nucleo-stm32f303ze, with a stm32f303ze chip.
26///
27/// Make sure you check/update the following (whether you use the F303ZE or another board):
28///
29/// * [ ] Update .cargo/config.toml with the correct `probe-rs run --chip STM32F303ZETx`chip name.
30/// * [ ] Update Cargo.toml to have the correct `embassy-stm32` feature, for F303ZE it should be `stm32f303ze`.
31/// * [ ] If your board has a special clock or power configuration, make sure that it is
32/// set up appropriately.
33/// * [ ] If your board has different pin mapping, update any pin numbers or peripherals
34/// to match your schematic
35///
36/// If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
37///
38/// * Which example you are trying to run
39/// * Which chip and board you are using
40///
41/// Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
42#[embassy_executor::main]
43async fn main(_spawner: embassy_executor::Spawner) {
44 let device_config = embassy_stm32::Config::default();
45 let context = embassy_stm32::init(device_config);
46
47 let tsc_conf = Config {
48 ct_pulse_high_length: ChargeTransferPulseCycle::_8,
49 ct_pulse_low_length: ChargeTransferPulseCycle::_8,
50 spread_spectrum: false,
51 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
52 spread_spectrum_prescaler: false,
53 pulse_generator_prescaler: PGPrescalerDivider::_32,
54 max_count_value: MaxCount::_255,
55 io_default_mode: false,
56 synchro_pin_polarity: false,
57 acquisition_mode: false,
58 max_count_interrupt: false,
59 channel_ios: TscIOPin::Group1Io1.into(),
60 shield_ios: 0, // no shield
61 sampling_ios: TscIOPin::Group1Io2.into(),
62 };
63
64 let mut g1: PinGroup<embassy_stm32::peripherals::TSC, G1> = PinGroup::new();
65 g1.set_io1(context.PA0, PinType::Sample);
66 g1.set_io2(context.PA1, PinType::Channel);
67
68 let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, Some(g1), None, None, None, None, None, tsc_conf);
69
70 // LED2 on the STM32F303ZE nucleo-board
71 let mut led = Output::new(context.PB7, Level::High, Speed::Low);
72
73 // smaller sample capacitor discharge faster and can be used with shorter delay.
74 let discharge_delay = 5; // ms
75
76 // the interval at which the loop polls for new touch sensor values
77 let polling_interval = 100; // ms
78
79 info!("polling for touch");
80 loop {
81 touch_controller.start();
82 touch_controller.poll_for_acquisition();
83 touch_controller.discharge_io(true);
84 Timer::after_millis(discharge_delay).await;
85
86 let grp1_status = touch_controller.group_get_status(Group::One);
87 match grp1_status {
88 GroupStatus::Complete => {
89 let group_one_val = touch_controller.group_get_value(Group::One);
90 info!("{}", group_one_val);
91 led.set_high();
92 }
93 GroupStatus::Ongoing => led.set_low(),
94 }
95
96 Timer::after_millis(polling_interval).await;
97 }
98}
diff --git a/examples/stm32f3/src/bin/tsc_blocking.rs b/examples/stm32f3/src/bin/tsc_blocking.rs
new file mode 100644
index 000000000..2c33838e5
--- /dev/null
+++ b/examples/stm32f3/src/bin/tsc_blocking.rs
@@ -0,0 +1,138 @@
1// Example of blocking TSC (Touch Sensing Controller) that lights an LED when touch is detected.
2//
3// This example demonstrates:
4// 1. Configuring a single TSC channel pin
5// 2. Using the blocking TSC interface with polling
6// 3. Waiting for acquisition completion using `poll_for_acquisition`
7// 4. Reading touch values and controlling an LED based on the results
8//
9// Suggested physical setup on STM32F303ZE Nucleo board:
10// - Connect a 1000pF capacitor between pin PA10 and GND. This is your sampling capacitor.
11// - Connect one end of a 1K resistor to pin PA9 and leave the other end loose.
12// The loose end will act as the touch sensor which will register your touch.
13//
14// The example uses two pins from Group 4 of the TSC:
15// - PA10 as the sampling capacitor, TSC group 4 IO2 (D68 on the STM32F303ZE nucleo-board)
16// - PA9 as the channel pin, TSC group 4 IO1 (D69 on the STM32F303ZE nucleo-board)
17//
18// The program continuously reads the touch sensor value:
19// - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value.
20// - The LED is turned on when touch is detected (sensor value < 40).
21// - Touch values are logged to the console.
22//
23// Troubleshooting:
24// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value.
25// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length,
26// pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.
27//
28// Note: Configuration values and sampling capacitor value have been determined experimentally.
29// Optimal values may vary based on your specific hardware setup.
30// Pins have been chosen for their convenient locations on the STM32F303ZE board. Refer to the
31// official relevant STM32 datasheets and user nucleo-board user manuals to find suitable
32// alternative pins.
33
34#![no_std]
35#![no_main]
36
37use defmt::*;
38use embassy_stm32::gpio::{Level, Output, Speed};
39use embassy_stm32::tsc::{self, *};
40use embassy_stm32::{mode, peripherals};
41use embassy_time::Timer;
42use {defmt_rtt as _, panic_probe as _};
43
44const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup
45
46#[embassy_executor::main]
47async fn main(_spawner: embassy_executor::Spawner) {
48 let device_config = embassy_stm32::Config::default();
49 let context = embassy_stm32::init(device_config);
50
51 let tsc_conf = Config {
52 ct_pulse_high_length: ChargeTransferPulseCycle::_4,
53 ct_pulse_low_length: ChargeTransferPulseCycle::_4,
54 spread_spectrum: false,
55 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
56 spread_spectrum_prescaler: false,
57 pulse_generator_prescaler: PGPrescalerDivider::_16,
58 max_count_value: MaxCount::_255,
59 io_default_mode: false,
60 synchro_pin_polarity: false,
61 acquisition_mode: false,
62 max_count_interrupt: false,
63 };
64
65 let mut g: PinGroupWithRoles<peripherals::TSC, G4> = PinGroupWithRoles::default();
66 // D68 on the STM32F303ZE nucleo-board
67 g.set_io2::<tsc::pin_roles::Sample>(context.PA10);
68 // D69 on the STM32F303ZE nucleo-board
69 let tsc_sensor = g.set_io1::<tsc::pin_roles::Channel>(context.PA9);
70
71 let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
72 g4: Some(g.pin_group),
73 ..Default::default()
74 };
75
76 let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, tsc_conf).unwrap();
77
78 // Check if TSC is ready
79 if touch_controller.get_state() != State::Ready {
80 crate::panic!("TSC not ready!");
81 }
82 info!("TSC initialized successfully");
83
84 // LED2 on the STM32F303ZE nucleo-board
85 let mut led = Output::new(context.PB7, Level::High, Speed::Low);
86
87 // smaller sample capacitor discharge faster and can be used with shorter delay.
88 let discharge_delay = 5; // ms
89
90 // the interval at which the loop polls for new touch sensor values
91 let polling_interval = 100; // ms
92
93 info!("polling for touch");
94 loop {
95 touch_controller.set_active_channels_mask(tsc_sensor.pin.into());
96 touch_controller.start();
97 touch_controller.poll_for_acquisition();
98 touch_controller.discharge_io(true);
99 Timer::after_millis(discharge_delay).await;
100
101 match read_touch_value(&mut touch_controller, tsc_sensor.pin).await {
102 Some(v) => {
103 info!("sensor value {}", v);
104 if v < SENSOR_THRESHOLD {
105 led.set_high();
106 } else {
107 led.set_low();
108 }
109 }
110 None => led.set_low(),
111 }
112
113 Timer::after_millis(polling_interval).await;
114 }
115}
116
117const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10;
118
119// attempt to read group status and delay when still ongoing
120async fn read_touch_value(
121 touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>,
122 sensor_pin: tsc::IOPin,
123) -> Option<u16> {
124 for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS {
125 match touch_controller.group_get_status(sensor_pin.group()) {
126 GroupStatus::Complete => {
127 return Some(touch_controller.group_get_value(sensor_pin.group()));
128 }
129 GroupStatus::Ongoing => {
130 // if you end up here a lot, then you prob need to increase discharge_delay
131 // or consider changing the code to adjust the discharge_delay dynamically
132 info!("Acquisition still ongoing");
133 Timer::after_millis(1).await;
134 }
135 }
136 }
137 None
138}
diff --git a/examples/stm32f3/src/bin/tsc_multipin.rs b/examples/stm32f3/src/bin/tsc_multipin.rs
new file mode 100644
index 000000000..c524c3760
--- /dev/null
+++ b/examples/stm32f3/src/bin/tsc_multipin.rs
@@ -0,0 +1,204 @@
1// Example of TSC (Touch Sensing Controller) using multiple pins from the same tsc-group.
2//
3// What is special about using multiple TSC pins as sensor channels from the same TSC group,
4// is that only one TSC pin for each TSC group can be acquired and read at the time.
5// To control which channel pins are acquired and read, we must write a mask before initiating an
6// acquisition. To help manage and abstract all this business away, we can organize our channel
7// pins into acquisition banks. Each acquisition bank can contain exactly one channel pin per TSC
8// group and it will contain the relevant mask.
9//
10// This example demonstrates how to:
11// 1. Configure multiple channel pins within a single TSC group
12// 2. Use the set_active_channels_bank method to switch between sets of different channels (acquisition banks)
13// 3. Read and interpret touch values from multiple channels in the same group
14//
15// Suggested physical setup on STM32F303ZE Nucleo board:
16// - Connect a 1000pF capacitor between pin PA10 and GND. This is the sampling capacitor for TSC
17// group 4.
18// - Connect one end of a 1K resistor to pin PA9 and leave the other end loose.
19// The loose end will act as a touch sensor.
20//
21// - Connect a 1000pF capacitor between pin PA7 and GND. This is the sampling capacitor for TSC
22// group 2.
23// - Connect one end of another 1K resistor to pin PA6 and leave the other end loose.
24// The loose end will act as a touch sensor.
25// - Connect one end of another 1K resistor to pin PA5 and leave the other end loose.
26// The loose end will act as a touch sensor.
27//
28// The example uses pins from two TSC groups.
29// - PA10 as sampling capacitor, TSC group 4 IO2
30// - PA9 as channel, TSC group 4 IO1
31// - PA7 as sampling capacitor, TSC group 2 IO4
32// - PA6 as channel, TSC group 2 IO3
33// - PA5 as channel, TSC group 2 IO2
34//
35// The pins have been chosen to make it easy to simply add capacitors directly onto the board and
36// connect one leg to GND, and to easily add resistors to the board with no special connectors,
37// breadboards, special wires or soldering required. All you need is the capacitors and resistors.
38//
39// The program reads the designated channel pins and adjusts the LED blinking
40// pattern based on which sensor(s) are touched:
41// - No touch: LED off
42// - one sensor touched: Slow blinking
43// - two sensors touched: Fast blinking
44// - three sensors touched: LED constantly on
45//
46// ## Troubleshooting:
47//
48// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value (currently set to 20).
49// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.
50// - Be aware that for some boards there will be overlapping concerns between some pins, for
51// example UART connection for the programmer to the MCU and a TSC pin. No errors or warning will
52// be emitted if you try to use such a pin for TSC, but you will get strange sensor readings.
53//
54// Note: Configuration values and sampling capacitor values have been determined experimentally. Optimal values may vary based on your specific hardware setup. Refer to the official STM32 datasheet and user manuals for more information on pin configurations and TSC functionality.
55
56#![no_std]
57#![no_main]
58
59use defmt::*;
60use embassy_stm32::gpio::{Level, Output, Speed};
61use embassy_stm32::tsc::{self, *};
62use embassy_stm32::{mode, peripherals};
63use embassy_time::Timer;
64use {defmt_rtt as _, panic_probe as _};
65
66const SENSOR_THRESHOLD: u16 = 10;
67
68async fn acquire_sensors(
69 touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Blocking>,
70 tsc_acquisition_bank: &AcquisitionBank,
71) {
72 touch_controller.set_active_channels_bank(tsc_acquisition_bank);
73 touch_controller.start();
74 touch_controller.poll_for_acquisition();
75 touch_controller.discharge_io(true);
76 let discharge_delay = 5; // ms
77 Timer::after_millis(discharge_delay).await;
78}
79
80#[embassy_executor::main]
81async fn main(_spawner: embassy_executor::Spawner) {
82 let device_config = embassy_stm32::Config::default();
83 let context = embassy_stm32::init(device_config);
84
85 // ---------- initial configuration of TSC ----------
86 //
87 let mut pin_group4: PinGroupWithRoles<peripherals::TSC, G4> = PinGroupWithRoles::default();
88 // D68 on the STM32F303ZE nucleo-board
89 pin_group4.set_io2::<tsc::pin_roles::Sample>(context.PA10);
90 // D69 on the STM32F303ZE nucleo-board
91 let tsc_sensor0 = pin_group4.set_io1(context.PA9);
92
93 let mut pin_group2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default();
94 // D11 on the STM32F303ZE nucleo-board
95 pin_group2.set_io4::<tsc::pin_roles::Sample>(context.PA7);
96 // D12 on the STM32F303ZE nucleo-board
97 let tsc_sensor1 = pin_group2.set_io3(context.PA6);
98 // D13 on the STM32F303ZE nucleo-board
99 let tsc_sensor2 = pin_group2.set_io2(context.PA5);
100
101 let config = Config {
102 ct_pulse_high_length: ChargeTransferPulseCycle::_4,
103 ct_pulse_low_length: ChargeTransferPulseCycle::_4,
104 spread_spectrum: false,
105 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
106 spread_spectrum_prescaler: false,
107 pulse_generator_prescaler: PGPrescalerDivider::_16,
108 max_count_value: MaxCount::_255,
109 io_default_mode: false,
110 synchro_pin_polarity: false,
111 acquisition_mode: false,
112 max_count_interrupt: false,
113 };
114
115 let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
116 g4: Some(pin_group4.pin_group),
117 g2: Some(pin_group2.pin_group),
118 ..Default::default()
119 };
120
121 let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, config).unwrap();
122
123 // ---------- setting up acquisition banks ----------
124 // sensor0 and sensor1 in this example belong to different TSC-groups,
125 // therefore we can acquire and read them both in one go.
126 let bank1 = touch_controller.create_acquisition_bank(AcquisitionBankPins {
127 g4_pin: Some(tsc_sensor0),
128 g2_pin: Some(tsc_sensor1),
129 ..Default::default()
130 });
131 // `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to
132 // acquire them one at the time. Therefore, we organize them into different acquisition banks.
133 let bank2 = touch_controller.create_acquisition_bank(AcquisitionBankPins {
134 g2_pin: Some(tsc_sensor2),
135 ..Default::default()
136 });
137
138 // Check if TSC is ready
139 if touch_controller.get_state() != State::Ready {
140 crate::panic!("TSC not ready!");
141 }
142
143 info!("TSC initialized successfully");
144
145 // LED2 on the STM32F303ZE nucleo-board
146 let mut led = Output::new(context.PB7, Level::High, Speed::Low);
147
148 let mut led_state = false;
149
150 loop {
151 acquire_sensors(&mut touch_controller, &bank1).await;
152 let readings1 = touch_controller.get_acquisition_bank_values(&bank1);
153 acquire_sensors(&mut touch_controller, &bank2).await;
154 let readings2 = touch_controller.get_acquisition_bank_values(&bank2);
155
156 let mut touched_sensors_count = 0;
157 for reading in readings1.iter() {
158 info!("{}", reading);
159 if reading.sensor_value < SENSOR_THRESHOLD {
160 touched_sensors_count += 1;
161 }
162 }
163 for reading in readings2.iter() {
164 info!("{}", reading);
165 if reading.sensor_value < SENSOR_THRESHOLD {
166 touched_sensors_count += 1;
167 }
168 }
169
170 match touched_sensors_count {
171 0 => {
172 // No sensors touched, turn off the LED
173 led.set_low();
174 led_state = false;
175 }
176 1 => {
177 // One sensor touched, blink slowly
178 led_state = !led_state;
179 if led_state {
180 led.set_high();
181 } else {
182 led.set_low();
183 }
184 Timer::after_millis(200).await;
185 }
186 2 => {
187 // Two sensors touched, blink faster
188 led_state = !led_state;
189 if led_state {
190 led.set_high();
191 } else {
192 led.set_low();
193 }
194 Timer::after_millis(50).await;
195 }
196 3 => {
197 // All three sensors touched, LED constantly on
198 led.set_high();
199 led_state = true;
200 }
201 _ => crate::unreachable!(), // This case should never occur with 3 sensors
202 }
203 }
204}
diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml
index 1cc0a97da..c6b311fa5 100644
--- a/examples/stm32f334/Cargo.toml
+++ b/examples/stm32f334/Cargo.toml
@@ -5,8 +5,8 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 9embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
10embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
12embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 12embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs
index e6d1a6c02..2b0686121 100644
--- a/examples/stm32f334/src/bin/pwm.rs
+++ b/examples/stm32f334/src/bin/pwm.rs
@@ -57,14 +57,14 @@ async fn main(_spawner: Spawner) {
57 // embassy_stm32::pac::HRTIM1 57 // embassy_stm32::pac::HRTIM1
58 // .tim(0) 58 // .tim(0)
59 // .setr(0) 59 // .setr(0)
60 // .modify(|w| w.set_sst(Activeeffect::SETACTIVE)); 60 // .modify(|w| w.set_sst(true));
61 // 61 //
62 // Timer::after_millis(500).await; 62 // Timer::after_millis(500).await;
63 // 63 //
64 // embassy_stm32::pac::HRTIM1 64 // embassy_stm32::pac::HRTIM1
65 // .tim(0) 65 // .tim(0)
66 // .rstr(0) 66 // .rstr(0)
67 // .modify(|w| w.set_srt(Inactiveeffect::SETINACTIVE)); 67 // .modify(|w| w.set_srt(true));
68 68
69 let max_duty = buck_converter.get_max_compare_value(); 69 let max_duty = buck_converter.get_max_compare_value();
70 70
diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml
index b85361596..4f0629fc6 100644
--- a/examples/stm32f4/Cargo.toml
+++ b/examples/stm32f4/Cargo.toml
@@ -6,12 +6,12 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8# Change stm32f429zi to your chip name, if necessary. 8# Change stm32f429zi to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-tim4", "exti", "chrono"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt" ] } 13embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt" ] }
14embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } 14embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] }
15embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } 15embassy-net-wiznet = { version = "0.1.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" }
17 17
@@ -27,6 +27,7 @@ embedded-io-async = { version = "0.6.1" }
27panic-probe = { version = "0.3", features = ["print-defmt"] } 27panic-probe = { version = "0.3", features = ["print-defmt"] }
28futures-util = { version = "0.3.30", default-features = false } 28futures-util = { version = "0.3.30", default-features = false }
29heapless = { version = "0.8", default-features = false } 29heapless = { version = "0.8", default-features = false }
30critical-section = "1.1"
30nb = "1.0.0" 31nb = "1.0.0"
31embedded-storage = "0.3.1" 32embedded-storage = "0.3.1"
32micromath = "2.0.0" 33micromath = "2.0.0"
diff --git a/examples/stm32f4/src/bin/i2s_dma.rs b/examples/stm32f4/src/bin/i2s_dma.rs
index 27b165f1b..68392847b 100644
--- a/examples/stm32f4/src/bin/i2s_dma.rs
+++ b/examples/stm32f4/src/bin/i2s_dma.rs
@@ -1,13 +1,10 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3 3
4use core::fmt::Write;
5
6use defmt::*; 4use defmt::*;
7use embassy_executor::Spawner; 5use embassy_executor::Spawner;
8use embassy_stm32::i2s::{Config, I2S}; 6use embassy_stm32::i2s::{Config, I2S};
9use embassy_stm32::time::Hertz; 7use embassy_stm32::time::Hertz;
10use heapless::String;
11use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
12 9
13#[embassy_executor::main] 10#[embassy_executor::main]
@@ -15,6 +12,8 @@ async fn main(_spawner: Spawner) {
15 let p = embassy_stm32::init(Default::default()); 12 let p = embassy_stm32::init(Default::default());
16 info!("Hello World!"); 13 info!("Hello World!");
17 14
15 let mut dma_buffer = [0x00_u16; 128];
16
18 let mut i2s = I2S::new_txonly( 17 let mut i2s = I2S::new_txonly(
19 p.SPI2, 18 p.SPI2,
20 p.PC3, // sd 19 p.PC3, // sd
@@ -22,13 +21,13 @@ async fn main(_spawner: Spawner) {
22 p.PB10, // ck 21 p.PB10, // ck
23 p.PC6, // mck 22 p.PC6, // mck
24 p.DMA1_CH4, 23 p.DMA1_CH4,
24 &mut dma_buffer,
25 Hertz(1_000_000), 25 Hertz(1_000_000),
26 Config::default(), 26 Config::default(),
27 ); 27 );
28 28
29 for n in 0u32.. { 29 for i in 0_u16.. {
30 let mut write: String<128> = String::new(); 30 i2s.write(&mut [i * 2; 64]).await.ok();
31 core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap(); 31 i2s.write(&mut [i * 2 + 1; 64]).await.ok();
32 i2s.write(&mut write.as_bytes()).await.ok();
33 } 32 }
34} 33}
diff --git a/examples/stm32f4/src/bin/pwm.rs b/examples/stm32f4/src/bin/pwm.rs
index 8844a9f0e..04811162b 100644
--- a/examples/stm32f4/src/bin/pwm.rs
+++ b/examples/stm32f4/src/bin/pwm.rs
@@ -6,7 +6,6 @@ use embassy_executor::Spawner;
6use embassy_stm32::gpio::OutputType; 6use embassy_stm32::gpio::OutputType;
7use embassy_stm32::time::khz; 7use embassy_stm32::time::khz;
8use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; 8use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
9use embassy_stm32::timer::Channel;
10use embassy_time::Timer; 9use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
12 11
@@ -15,22 +14,22 @@ async fn main(_spawner: Spawner) {
15 let p = embassy_stm32::init(Default::default()); 14 let p = embassy_stm32::init(Default::default());
16 info!("Hello World!"); 15 info!("Hello World!");
17 16
18 let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); 17 let ch1_pin = PwmPin::new_ch1(p.PE9, OutputType::PushPull);
19 let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), 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());
20 let max = pwm.get_max_duty(); 19 let mut ch1 = pwm.ch1();
21 pwm.enable(Channel::Ch1); 20 ch1.enable();
22 21
23 info!("PWM initialized"); 22 info!("PWM initialized");
24 info!("PWM max duty {}", max); 23 info!("PWM max duty {}", ch1.max_duty_cycle());
25 24
26 loop { 25 loop {
27 pwm.set_duty(Channel::Ch1, 0); 26 ch1.set_duty_cycle_fully_off();
28 Timer::after_millis(300).await; 27 Timer::after_millis(300).await;
29 pwm.set_duty(Channel::Ch1, max / 4); 28 ch1.set_duty_cycle_fraction(1, 4);
30 Timer::after_millis(300).await; 29 Timer::after_millis(300).await;
31 pwm.set_duty(Channel::Ch1, max / 2); 30 ch1.set_duty_cycle_fraction(1, 2);
32 Timer::after_millis(300).await; 31 Timer::after_millis(300).await;
33 pwm.set_duty(Channel::Ch1, max - 1); 32 ch1.set_duty_cycle(ch1.max_duty_cycle() - 1);
34 Timer::after_millis(300).await; 33 Timer::after_millis(300).await;
35 } 34 }
36} 35}
diff --git a/examples/stm32f4/src/bin/usb_uac_speaker.rs b/examples/stm32f4/src/bin/usb_uac_speaker.rs
new file mode 100644
index 000000000..e22e07e63
--- /dev/null
+++ b/examples/stm32f4/src/bin/usb_uac_speaker.rs
@@ -0,0 +1,390 @@
1#![no_std]
2#![no_main]
3
4use core::cell::{Cell, RefCell};
5
6use defmt::{panic, *};
7use embassy_executor::Spawner;
8use embassy_stm32::time::Hertz;
9use embassy_stm32::{bind_interrupts, interrupt, peripherals, timer, usb, Config};
10use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};
11use embassy_sync::blocking_mutex::Mutex;
12use embassy_sync::signal::Signal;
13use embassy_sync::zerocopy_channel;
14use embassy_usb::class::uac1;
15use embassy_usb::class::uac1::speaker::{self, Speaker};
16use embassy_usb::driver::EndpointError;
17use heapless::Vec;
18use micromath::F32Ext;
19use static_cell::StaticCell;
20use {defmt_rtt as _, panic_probe as _};
21
22bind_interrupts!(struct Irqs {
23 OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>;
24});
25
26static TIMER: Mutex<CriticalSectionRawMutex, RefCell<Option<timer::low_level::Timer<peripherals::TIM2>>>> =
27 Mutex::new(RefCell::new(None));
28
29// A counter signal that is written by the feedback timer, once every `FEEDBACK_REFRESH_PERIOD`.
30// At that point, a feedback value is sent to the host.
31pub static FEEDBACK_SIGNAL: Signal<CriticalSectionRawMutex, u32> = Signal::new();
32
33// Stereo input
34pub const INPUT_CHANNEL_COUNT: usize = 2;
35
36// This example uses a fixed sample rate of 48 kHz.
37pub const SAMPLE_RATE_HZ: u32 = 48_000;
38pub const FEEDBACK_COUNTER_TICK_RATE: u32 = 42_000_000;
39
40// Use 32 bit samples, which allow for a lot of (software) volume adjustment without degradation of quality.
41pub const SAMPLE_WIDTH: uac1::SampleWidth = uac1::SampleWidth::Width4Byte;
42pub const SAMPLE_WIDTH_BIT: usize = SAMPLE_WIDTH.in_bit();
43pub const SAMPLE_SIZE: usize = SAMPLE_WIDTH as usize;
44pub const SAMPLE_SIZE_PER_S: usize = (SAMPLE_RATE_HZ as usize) * INPUT_CHANNEL_COUNT * SAMPLE_SIZE;
45
46// Size of audio samples per 1 ms - for the full-speed USB frame period of 1 ms.
47pub const USB_FRAME_SIZE: usize = SAMPLE_SIZE_PER_S.div_ceil(1000);
48
49// Select front left and right audio channels.
50pub const AUDIO_CHANNELS: [uac1::Channel; INPUT_CHANNEL_COUNT] = [uac1::Channel::LeftFront, uac1::Channel::RightFront];
51
52// Factor of two as a margin for feedback (this is an excessive amount)
53pub const USB_MAX_PACKET_SIZE: usize = 2 * USB_FRAME_SIZE;
54pub const USB_MAX_SAMPLE_COUNT: usize = USB_MAX_PACKET_SIZE / SAMPLE_SIZE;
55
56// The data type that is exchanged via the zero-copy channel (a sample vector).
57pub type SampleBlock = Vec<u32, USB_MAX_SAMPLE_COUNT>;
58
59// Feedback is provided in 10.14 format for full-speed endpoints.
60pub const FEEDBACK_REFRESH_PERIOD: uac1::FeedbackRefresh = uac1::FeedbackRefresh::Period8Frames;
61const FEEDBACK_SHIFT: usize = 14;
62
63const TICKS_PER_SAMPLE: f32 = (FEEDBACK_COUNTER_TICK_RATE as f32) / (SAMPLE_RATE_HZ as f32);
64
65struct Disconnected {}
66
67impl From<EndpointError> for Disconnected {
68 fn from(val: EndpointError) -> Self {
69 match val {
70 EndpointError::BufferOverflow => panic!("Buffer overflow"),
71 EndpointError::Disabled => Disconnected {},
72 }
73 }
74}
75
76/// Sends feedback messages to the host.
77async fn feedback_handler<'d, T: usb::Instance + 'd>(
78 feedback: &mut speaker::Feedback<'d, usb::Driver<'d, T>>,
79 feedback_factor: f32,
80) -> Result<(), Disconnected> {
81 let mut packet: Vec<u8, 4> = Vec::new();
82
83 // Collects the fractional component of the feedback value that is lost by rounding.
84 let mut rest = 0.0_f32;
85
86 loop {
87 let counter = FEEDBACK_SIGNAL.wait().await;
88
89 packet.clear();
90
91 let raw_value = counter as f32 * feedback_factor + rest;
92 let value = raw_value.round();
93 rest = raw_value - value;
94
95 let value = value as u32;
96 packet.push(value as u8).unwrap();
97 packet.push((value >> 8) as u8).unwrap();
98 packet.push((value >> 16) as u8).unwrap();
99
100 feedback.write_packet(&packet).await?;
101 }
102}
103
104/// Handles streaming of audio data from the host.
105async fn stream_handler<'d, T: usb::Instance + 'd>(
106 stream: &mut speaker::Stream<'d, usb::Driver<'d, T>>,
107 sender: &mut zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>,
108) -> Result<(), Disconnected> {
109 loop {
110 let mut usb_data = [0u8; USB_MAX_PACKET_SIZE];
111 let data_size = stream.read_packet(&mut usb_data).await?;
112
113 let word_count = data_size / SAMPLE_SIZE;
114
115 if word_count * SAMPLE_SIZE == data_size {
116 // Obtain a buffer from the channel
117 let samples = sender.send().await;
118 samples.clear();
119
120 for w in 0..word_count {
121 let byte_offset = w * SAMPLE_SIZE;
122 let sample = u32::from_le_bytes(usb_data[byte_offset..byte_offset + SAMPLE_SIZE].try_into().unwrap());
123
124 // Fill the sample buffer with data.
125 samples.push(sample).unwrap();
126 }
127
128 sender.send_done();
129 } else {
130 debug!("Invalid USB buffer size of {}, skipped.", data_size);
131 }
132 }
133}
134
135/// Receives audio samples from the USB streaming task and can play them back.
136#[embassy_executor::task]
137async fn audio_receiver_task(mut usb_audio_receiver: zerocopy_channel::Receiver<'static, NoopRawMutex, SampleBlock>) {
138 loop {
139 let _samples = usb_audio_receiver.receive().await;
140 // Use the samples, for example play back via the SAI peripheral.
141
142 // Notify the channel that the buffer is now ready to be reused
143 usb_audio_receiver.receive_done();
144 }
145}
146
147/// Receives audio samples from the host.
148#[embassy_executor::task]
149async fn usb_streaming_task(
150 mut stream: speaker::Stream<'static, usb::Driver<'static, peripherals::USB_OTG_FS>>,
151 mut sender: zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>,
152) {
153 loop {
154 stream.wait_connection().await;
155 _ = stream_handler(&mut stream, &mut sender).await;
156 }
157}
158
159/// Sends sample rate feedback to the host.
160///
161/// The `feedback_factor` scales the feedback timer's counter value so that the result is the number of samples that
162/// this device played back or "consumed" during one SOF period (1 ms) - in 10.14 format.
163///
164/// Ideally, the `feedback_factor` that is calculated below would be an integer for avoiding numerical errors.
165/// This is achieved by having `TICKS_PER_SAMPLE` be a power of two. For audio applications at a sample rate of 48 kHz,
166/// 24.576 MHz would be one such option.
167///
168/// A good choice for the STM32F4, which also has to generate a 48 MHz clock from its HSE (e.g. running at 8 MHz)
169/// for USB, is to clock the feedback timer from the MCLK output of the SAI peripheral. The SAI peripheral then uses an
170/// external clock. In that case, wiring the MCLK output to the timer clock input is required.
171///
172/// This simple example just uses the internal clocks for supplying the feedback timer,
173/// and does not even set up a SAI peripheral.
174#[embassy_executor::task]
175async fn usb_feedback_task(mut feedback: speaker::Feedback<'static, usb::Driver<'static, peripherals::USB_OTG_FS>>) {
176 let feedback_factor =
177 ((1 << FEEDBACK_SHIFT) as f32 / TICKS_PER_SAMPLE) / FEEDBACK_REFRESH_PERIOD.frame_count() as f32;
178
179 // Should be 2.3405714285714287...
180 info!("Using a feedback factor of {}.", feedback_factor);
181
182 loop {
183 feedback.wait_connection().await;
184 _ = feedback_handler(&mut feedback, feedback_factor).await;
185 }
186}
187
188#[embassy_executor::task]
189async fn usb_task(mut usb_device: embassy_usb::UsbDevice<'static, usb::Driver<'static, peripherals::USB_OTG_FS>>) {
190 usb_device.run().await;
191}
192
193/// Checks for changes on the control monitor of the class.
194///
195/// In this case, monitor changes of volume or mute state.
196#[embassy_executor::task]
197async fn usb_control_task(control_monitor: speaker::ControlMonitor<'static>) {
198 loop {
199 control_monitor.changed().await;
200
201 for channel in AUDIO_CHANNELS {
202 let volume = control_monitor.volume(channel).unwrap();
203 info!("Volume changed to {} on channel {}.", volume, channel);
204 }
205 }
206}
207
208/// Feedback value measurement and calculation
209///
210/// Used for measuring/calculating the number of samples that were received from the host during the
211/// `FEEDBACK_REFRESH_PERIOD`.
212///
213/// Configured in this example with
214/// - a refresh period of 8 ms, and
215/// - a tick rate of 42 MHz.
216///
217/// This gives an (ideal) counter value of 336.000 for every update of the `FEEDBACK_SIGNAL`.
218#[interrupt]
219fn TIM2() {
220 static LAST_TICKS: Mutex<CriticalSectionRawMutex, Cell<u32>> = Mutex::new(Cell::new(0));
221 static FRAME_COUNT: Mutex<CriticalSectionRawMutex, Cell<usize>> = Mutex::new(Cell::new(0));
222
223 critical_section::with(|cs| {
224 // Read timer counter.
225 let timer = TIMER.borrow(cs).borrow().as_ref().unwrap().regs_gp32();
226
227 let status = timer.sr().read();
228
229 const CHANNEL_INDEX: usize = 0;
230 if status.ccif(CHANNEL_INDEX) {
231 let ticks = timer.ccr(CHANNEL_INDEX).read();
232
233 let frame_count = FRAME_COUNT.borrow(cs);
234 let last_ticks = LAST_TICKS.borrow(cs);
235
236 frame_count.set(frame_count.get() + 1);
237 if frame_count.get() >= FEEDBACK_REFRESH_PERIOD.frame_count() {
238 frame_count.set(0);
239 FEEDBACK_SIGNAL.signal(ticks.wrapping_sub(last_ticks.get()));
240 last_ticks.set(ticks);
241 }
242 };
243
244 // Clear trigger interrupt flag.
245 timer.sr().modify(|r| r.set_tif(false));
246 });
247}
248
249// If you are trying this and your USB device doesn't connect, the most
250// common issues are the RCC config and vbus_detection
251//
252// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure
253// for more information.
254#[embassy_executor::main]
255async fn main(spawner: Spawner) {
256 info!("Hello World!");
257
258 let mut config = Config::default();
259 {
260 use embassy_stm32::rcc::*;
261 config.rcc.hse = Some(Hse {
262 freq: Hertz(8_000_000),
263 mode: HseMode::Bypass,
264 });
265 config.rcc.pll_src = PllSource::HSE;
266 config.rcc.pll = Some(Pll {
267 prediv: PllPreDiv::DIV4,
268 mul: PllMul::MUL168,
269 divp: Some(PllPDiv::DIV2), // ((8 MHz / 4) * 168) / 2 = 168 Mhz.
270 divq: Some(PllQDiv::DIV7), // ((8 MHz / 4) * 168) / 7 = 48 Mhz.
271 divr: None,
272 });
273 config.rcc.ahb_pre = AHBPrescaler::DIV1;
274 config.rcc.apb1_pre = APBPrescaler::DIV4;
275 config.rcc.apb2_pre = APBPrescaler::DIV2;
276 config.rcc.sys = Sysclk::PLL1_P;
277 config.rcc.mux.clk48sel = mux::Clk48sel::PLL1_Q;
278 }
279 let p = embassy_stm32::init(config);
280
281 // Configure all required buffers in a static way.
282 debug!("USB packet size is {} byte", USB_MAX_PACKET_SIZE);
283 static CONFIG_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new();
284 let config_descriptor = CONFIG_DESCRIPTOR.init([0; 256]);
285
286 static BOS_DESCRIPTOR: StaticCell<[u8; 32]> = StaticCell::new();
287 let bos_descriptor = BOS_DESCRIPTOR.init([0; 32]);
288
289 const CONTROL_BUF_SIZE: usize = 64;
290 static CONTROL_BUF: StaticCell<[u8; CONTROL_BUF_SIZE]> = StaticCell::new();
291 let control_buf = CONTROL_BUF.init([0; CONTROL_BUF_SIZE]);
292
293 const FEEDBACK_BUF_SIZE: usize = 4;
294 static EP_OUT_BUFFER: StaticCell<[u8; FEEDBACK_BUF_SIZE + CONTROL_BUF_SIZE + USB_MAX_PACKET_SIZE]> =
295 StaticCell::new();
296 let ep_out_buffer = EP_OUT_BUFFER.init([0u8; FEEDBACK_BUF_SIZE + CONTROL_BUF_SIZE + USB_MAX_PACKET_SIZE]);
297
298 static STATE: StaticCell<speaker::State> = StaticCell::new();
299 let state = STATE.init(speaker::State::new());
300
301 // Create the driver, from the HAL.
302 let mut usb_config = usb::Config::default();
303
304 // Do not enable vbus_detection. This is a safe default that works in all boards.
305 // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need
306 // to enable vbus_detection to comply with the USB spec. If you enable it, the board
307 // has to support it or USB won't work at all. See docs on `vbus_detection` for details.
308 usb_config.vbus_detection = false;
309
310 let usb_driver = usb::Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, ep_out_buffer, usb_config);
311
312 // Basic USB device configuration
313 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
314 config.manufacturer = Some("Embassy");
315 config.product = Some("USB-audio-speaker example");
316 config.serial_number = Some("12345678");
317
318 // Required for windows compatibility.
319 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
320 config.device_class = 0xEF;
321 config.device_sub_class = 0x02;
322 config.device_protocol = 0x01;
323 config.composite_with_iads = true;
324
325 let mut builder = embassy_usb::Builder::new(
326 usb_driver,
327 config,
328 config_descriptor,
329 bos_descriptor,
330 &mut [], // no msos descriptors
331 control_buf,
332 );
333
334 // Create the UAC1 Speaker class components
335 let (stream, feedback, control_monitor) = Speaker::new(
336 &mut builder,
337 state,
338 USB_MAX_PACKET_SIZE as u16,
339 uac1::SampleWidth::Width4Byte,
340 &[SAMPLE_RATE_HZ],
341 &AUDIO_CHANNELS,
342 FEEDBACK_REFRESH_PERIOD,
343 );
344
345 // Create the USB device
346 let usb_device = builder.build();
347
348 // Establish a zero-copy channel for transferring received audio samples between tasks
349 static SAMPLE_BLOCKS: StaticCell<[SampleBlock; 2]> = StaticCell::new();
350 let sample_blocks = SAMPLE_BLOCKS.init([Vec::new(), Vec::new()]);
351
352 static CHANNEL: StaticCell<zerocopy_channel::Channel<'_, NoopRawMutex, SampleBlock>> = StaticCell::new();
353 let channel = CHANNEL.init(zerocopy_channel::Channel::new(sample_blocks));
354 let (sender, receiver) = channel.split();
355
356 // Run a timer for counting between SOF interrupts.
357 let mut tim2 = timer::low_level::Timer::new(p.TIM2);
358 tim2.set_tick_freq(Hertz(FEEDBACK_COUNTER_TICK_RATE));
359 tim2.set_trigger_source(timer::low_level::TriggerSource::ITR1); // The USB SOF signal.
360
361 const TIMER_CHANNEL: timer::Channel = timer::Channel::Ch1;
362 tim2.set_input_ti_selection(TIMER_CHANNEL, timer::low_level::InputTISelection::TRC);
363 tim2.set_input_capture_prescaler(TIMER_CHANNEL, 0);
364 tim2.set_input_capture_filter(TIMER_CHANNEL, timer::low_level::FilterValue::FCK_INT_N2);
365
366 // Reset all interrupt flags.
367 tim2.regs_gp32().sr().write(|r| r.0 = 0);
368
369 // Enable routing of SOF to the timer.
370 tim2.regs_gp32().or().write(|r| *r = 0b10 << 10);
371
372 tim2.enable_channel(TIMER_CHANNEL, true);
373 tim2.enable_input_interrupt(TIMER_CHANNEL, true);
374
375 tim2.start();
376
377 TIMER.lock(|p| p.borrow_mut().replace(tim2));
378
379 // Unmask the TIM2 interrupt.
380 unsafe {
381 cortex_m::peripheral::NVIC::unmask(interrupt::TIM2);
382 }
383
384 // Launch USB audio tasks.
385 unwrap!(spawner.spawn(usb_control_task(control_monitor)));
386 unwrap!(spawner.spawn(usb_streaming_task(stream, sender)));
387 unwrap!(spawner.spawn(usb_feedback_task(feedback)));
388 unwrap!(spawner.spawn(usb_task(usb_device)));
389 unwrap!(spawner.spawn(audio_receiver_task(receiver)));
390}
diff --git a/examples/stm32f4/src/bin/ws2812_pwm.rs b/examples/stm32f4/src/bin/ws2812_pwm.rs
index cbaff75fc..3ab93d6e0 100644
--- a/examples/stm32f4/src/bin/ws2812_pwm.rs
+++ b/examples/stm32f4/src/bin/ws2812_pwm.rs
@@ -61,7 +61,7 @@ async fn main(_spawner: Spawner) {
61 // construct ws2812 non-return-to-zero (NRZ) code bit by bit 61 // construct ws2812 non-return-to-zero (NRZ) code bit by bit
62 // ws2812 only need 24 bits for each LED, but we add one bit more to keep PWM output low 62 // ws2812 only need 24 bits for each LED, but we add one bit more to keep PWM output low
63 63
64 let max_duty = ws2812_pwm.get_max_duty() as u16; 64 let max_duty = ws2812_pwm.max_duty_cycle();
65 let n0 = 8 * max_duty / 25; // ws2812 Bit 0 high level timing 65 let n0 = 8 * max_duty / 25; // ws2812 Bit 0 high level timing
66 let n1 = 2 * n0; // ws2812 Bit 1 high level timing 66 let n1 = 2 * n0; // ws2812 Bit 1 high level timing
67 67
@@ -84,7 +84,7 @@ async fn main(_spawner: Spawner) {
84 let pwm_channel = Channel::Ch1; 84 let pwm_channel = Channel::Ch1;
85 85
86 // make sure PWM output keep low on first start 86 // make sure PWM output keep low on first start
87 ws2812_pwm.set_duty(pwm_channel, 0); 87 ws2812_pwm.channel(pwm_channel).set_duty_cycle(0);
88 88
89 // flip color at 2 Hz 89 // flip color at 2 Hz
90 let mut ticker = Ticker::every(Duration::from_millis(500)); 90 let mut ticker = Ticker::every(Duration::from_millis(500));
diff --git a/examples/stm32f469/Cargo.toml b/examples/stm32f469/Cargo.toml
index 6a5bd0b29..a80409801 100644
--- a/examples/stm32f469/Cargo.toml
+++ b/examples/stm32f469/Cargo.toml
@@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8# Specific examples only for stm32f469 8# Specific examples only for stm32f469
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f469ni", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f469ni", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] }
10embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 10embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
11embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 11embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
12 12
13defmt = "0.3" 13defmt = "0.3"
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml
index 8c591ebd2..520b8bc42 100644
--- a/examples/stm32f7/Cargo.toml
+++ b/examples/stm32f7/Cargo.toml
@@ -7,10 +7,10 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8# Change stm32f777zi to your chip name, if necessary. 8# Change stm32f777zi to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } 13embassy-net = { version = "0.5.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.3.0", path = "../../embassy-usb", features = ["defmt"] } 15embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
16embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 16embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
diff --git a/examples/stm32f7/src/bin/qspi.rs b/examples/stm32f7/src/bin/qspi.rs
index 90d319b7a..bd3287964 100644
--- a/examples/stm32f7/src/bin/qspi.rs
+++ b/examples/stm32f7/src/bin/qspi.rs
@@ -72,7 +72,7 @@ impl<I: Instance> FlashMemory<I> {
72 address: None, 72 address: None,
73 dummy: DummyCycles::_0, 73 dummy: DummyCycles::_0,
74 }; 74 };
75 self.qspi.command(transaction); 75 self.qspi.blocking_command(transaction);
76 } 76 }
77 77
78 pub fn reset_memory(&mut self) { 78 pub fn reset_memory(&mut self) {
@@ -143,7 +143,7 @@ impl<I: Instance> FlashMemory<I> {
143 dummy: DummyCycles::_0, 143 dummy: DummyCycles::_0,
144 }; 144 };
145 self.enable_write(); 145 self.enable_write();
146 self.qspi.command(transaction); 146 self.qspi.blocking_command(transaction);
147 self.wait_write_finish(); 147 self.wait_write_finish();
148 } 148 }
149 149
diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml
index a50074ce0..3d11610ce 100644
--- a/examples/stm32g0/Cargo.toml
+++ b/examples/stm32g0/Cargo.toml
@@ -7,8 +7,8 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8# Change stm32g0b1re to your chip name, if necessary. 8# Change stm32g0b1re to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g0b1re", "memory-x", "unstable-pac", "exti"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g0b1re", "memory-x", "unstable-pac", "exti"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } 13embassy-usb = { version = "0.3.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" }
diff --git a/examples/stm32g0/src/bin/input_capture.rs b/examples/stm32g0/src/bin/input_capture.rs
index 69fdae96d..bc814cb13 100644
--- a/examples/stm32g0/src/bin/input_capture.rs
+++ b/examples/stm32g0/src/bin/input_capture.rs
@@ -47,10 +47,10 @@ 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 = PwmPin::new_ch1(p.PA8, OutputType::PushPull); 50 let ch1_pin = PwmPin::new_ch1(p.PA8, OutputType::PushPull);
51 let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), 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.enable(Channel::Ch1); 52 pwm.ch1().enable();
53 pwm.set_duty(Channel::Ch1, 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_ch1(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());
diff --git a/examples/stm32g0/src/bin/pwm_input.rs b/examples/stm32g0/src/bin/pwm_input.rs
index 152ecda86..db9cf4f8a 100644
--- a/examples/stm32g0/src/bin/pwm_input.rs
+++ b/examples/stm32g0/src/bin/pwm_input.rs
@@ -14,7 +14,6 @@ use embassy_stm32::gpio::{Level, Output, OutputType, Pull, Speed};
14use embassy_stm32::time::khz; 14use embassy_stm32::time::khz;
15use embassy_stm32::timer::pwm_input::PwmInput; 15use embassy_stm32::timer::pwm_input::PwmInput;
16use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; 16use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
17use embassy_stm32::timer::Channel;
18use embassy_stm32::{bind_interrupts, peripherals, timer}; 17use embassy_stm32::{bind_interrupts, peripherals, timer};
19use embassy_time::Timer; 18use embassy_time::Timer;
20use {defmt_rtt as _, panic_probe as _}; 19use {defmt_rtt as _, panic_probe as _};
@@ -43,11 +42,10 @@ async fn main(spawner: Spawner) {
43 42
44 unwrap!(spawner.spawn(blinky(p.PB1))); 43 unwrap!(spawner.spawn(blinky(p.PB1)));
45 // Connect PA8 and PA6 with a 1k Ohm resistor 44 // Connect PA8 and PA6 with a 1k Ohm resistor
46 let ch1 = PwmPin::new_ch1(p.PA8, OutputType::PushPull); 45 let ch1_pin = PwmPin::new_ch1(p.PA8, OutputType::PushPull);
47 let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), 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());
48 let max = pwm.get_max_duty(); 47 pwm.ch1().set_duty_cycle_fraction(1, 4);
49 pwm.set_duty(Channel::Ch1, max / 4); 48 pwm.ch1().enable();
50 pwm.enable(Channel::Ch1);
51 49
52 let mut pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, khz(1000)); 50 let mut pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, khz(1000));
53 pwm_input.enable(); 51 pwm_input.enable();
diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml
index 2768147a1..87fa2c53a 100644
--- a/examples/stm32g4/Cargo.toml
+++ b/examples/stm32g4/Cargo.toml
@@ -7,8 +7,8 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8# Change stm32g491re to your chip name, if necessary. 8# Change stm32g491re to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 13embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
diff --git a/examples/stm32g4/src/bin/adc_dma.rs b/examples/stm32g4/src/bin/adc_dma.rs
new file mode 100644
index 000000000..970623b32
--- /dev/null
+++ b/examples/stm32g4/src/bin/adc_dma.rs
@@ -0,0 +1,60 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime};
7use embassy_stm32::Config;
8use embassy_time::Timer;
9use {defmt_rtt as _, panic_probe as _};
10
11static mut DMA_BUF: [u16; 2] = [0; 2];
12
13#[embassy_executor::main]
14async fn main(_spawner: Spawner) {
15 let mut read_buffer = unsafe { &mut DMA_BUF[..] };
16
17 let mut config = Config::default();
18 {
19 use embassy_stm32::rcc::*;
20 config.rcc.pll = Some(Pll {
21 source: PllSource::HSI,
22 prediv: PllPreDiv::DIV4,
23 mul: PllMul::MUL85,
24 divp: None,
25 divq: None,
26 // Main system clock at 170 MHz
27 divr: Some(PllRDiv::DIV2),
28 });
29 config.rcc.mux.adc12sel = mux::Adcsel::SYS;
30 config.rcc.sys = Sysclk::PLL1_R;
31 }
32 let p = embassy_stm32::init(config);
33
34 info!("Hello World!");
35
36 let mut adc = Adc::new(p.ADC1);
37
38 let mut dma = p.DMA1_CH1;
39 let mut vrefint_channel = adc.enable_vrefint().degrade_adc();
40 let mut pa0 = p.PA0.degrade_adc();
41
42 loop {
43 adc.read(
44 &mut dma,
45 [
46 (&mut vrefint_channel, SampleTime::CYCLES247_5),
47 (&mut pa0, SampleTime::CYCLES247_5),
48 ]
49 .into_iter(),
50 &mut read_buffer,
51 )
52 .await;
53
54 let vrefint = read_buffer[0];
55 let measured = read_buffer[1];
56 info!("vrefint: {}", vrefint);
57 info!("measured: {}", measured);
58 Timer::after_millis(500).await;
59 }
60}
diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs
index d4809a481..6c965012c 100644
--- a/examples/stm32g4/src/bin/pwm.rs
+++ b/examples/stm32g4/src/bin/pwm.rs
@@ -6,7 +6,6 @@ use embassy_executor::Spawner;
6use embassy_stm32::gpio::OutputType; 6use embassy_stm32::gpio::OutputType;
7use embassy_stm32::time::khz; 7use embassy_stm32::time::khz;
8use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; 8use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
9use embassy_stm32::timer::Channel;
10use embassy_time::Timer; 9use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
12 11
@@ -15,22 +14,22 @@ async fn main(_spawner: Spawner) {
15 let p = embassy_stm32::init(Default::default()); 14 let p = embassy_stm32::init(Default::default());
16 info!("Hello World!"); 15 info!("Hello World!");
17 16
18 let ch1 = PwmPin::new_ch1(p.PC0, OutputType::PushPull); 17 let ch1_pin = PwmPin::new_ch1(p.PC0, OutputType::PushPull);
19 let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), 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());
20 let max = pwm.get_max_duty(); 19 let mut ch1 = pwm.ch1();
21 pwm.enable(Channel::Ch1); 20 ch1.enable();
22 21
23 info!("PWM initialized"); 22 info!("PWM initialized");
24 info!("PWM max duty {}", max); 23 info!("PWM max duty {}", ch1.max_duty_cycle());
25 24
26 loop { 25 loop {
27 pwm.set_duty(Channel::Ch1, 0); 26 ch1.set_duty_cycle_fully_off();
28 Timer::after_millis(300).await; 27 Timer::after_millis(300).await;
29 pwm.set_duty(Channel::Ch1, max / 4); 28 ch1.set_duty_cycle_fraction(1, 4);
30 Timer::after_millis(300).await; 29 Timer::after_millis(300).await;
31 pwm.set_duty(Channel::Ch1, max / 2); 30 ch1.set_duty_cycle_fraction(1, 2);
32 Timer::after_millis(300).await; 31 Timer::after_millis(300).await;
33 pwm.set_duty(Channel::Ch1, max - 1); 32 ch1.set_duty_cycle(ch1.max_duty_cycle() - 1);
34 Timer::after_millis(300).await; 33 Timer::after_millis(300).await;
35 } 34 }
36} 35}
diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml
index 30b1d2be9..516d491e5 100644
--- a/examples/stm32h5/Cargo.toml
+++ b/examples/stm32h5/Cargo.toml
@@ -7,10 +7,10 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8# Change stm32h563zi to your chip name, if necessary. 8# Change stm32h563zi to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h563zi", "memory-x", "time-driver-any", "exti", "unstable-pac", "low-power"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h563zi", "memory-x", "time-driver-any", "exti", "unstable-pac", "low-power"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] } 13embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] }
14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.3.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
@@ -23,7 +23,7 @@ embedded-hal = "0.2.6"
23embedded-hal-1 = { package = "embedded-hal", version = "1.0" } 23embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
24embedded-hal-async = { version = "1.0" } 24embedded-hal-async = { version = "1.0" }
25embedded-io-async = { version = "0.6.1" } 25embedded-io-async = { version = "0.6.1" }
26embedded-nal-async = { version = "0.7.1" } 26embedded-nal-async = "0.8.0"
27panic-probe = { version = "0.3", features = ["print-defmt"] } 27panic-probe = { version = "0.3", features = ["print-defmt"] }
28heapless = { version = "0.8", default-features = false } 28heapless = { version = "0.8", default-features = false }
29rand_core = "0.6.3" 29rand_core = "0.6.3"
diff --git a/examples/stm32h5/src/bin/adc.rs b/examples/stm32h5/src/bin/adc.rs
new file mode 100644
index 000000000..c5d508ece
--- /dev/null
+++ b/examples/stm32h5/src/bin/adc.rs
@@ -0,0 +1,59 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::adc::{Adc, SampleTime};
7use embassy_stm32::Config;
8use embassy_time::Timer;
9use {defmt_rtt as _, panic_probe as _};
10
11#[embassy_executor::main]
12async fn main(_spawner: Spawner) {
13 let mut config = Config::default();
14 {
15 use embassy_stm32::rcc::*;
16 config.rcc.hsi = Some(HSIPrescaler::DIV1);
17 config.rcc.csi = true;
18 config.rcc.pll1 = Some(Pll {
19 source: PllSource::HSI,
20 prediv: PllPreDiv::DIV4,
21 mul: PllMul::MUL25,
22 divp: Some(PllDiv::DIV2),
23 divq: Some(PllDiv::DIV4), // SPI1 cksel defaults to pll1_q
24 divr: None,
25 });
26 config.rcc.pll2 = Some(Pll {
27 source: PllSource::HSI,
28 prediv: PllPreDiv::DIV4,
29 mul: PllMul::MUL25,
30 divp: None,
31 divq: None,
32 divr: Some(PllDiv::DIV4), // 100mhz
33 });
34 config.rcc.sys = Sysclk::PLL1_P; // 200 Mhz
35 config.rcc.ahb_pre = AHBPrescaler::DIV1; // 200 Mhz
36 config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
37 config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
38 config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
39 config.rcc.voltage_scale = VoltageScale::Scale1;
40 config.rcc.mux.adcdacsel = mux::Adcdacsel::PLL2_R;
41 }
42 let mut p = embassy_stm32::init(config);
43
44 info!("Hello World!");
45
46 let mut adc = Adc::new(p.ADC1);
47
48 adc.set_sample_time(SampleTime::CYCLES24_5);
49
50 let mut vrefint_channel = adc.enable_vrefint();
51
52 loop {
53 let vrefint = adc.blocking_read(&mut vrefint_channel);
54 info!("vrefint: {}", vrefint);
55 let measured = adc.blocking_read(&mut p.PA0);
56 info!("measured: {}", measured);
57 Timer::after_millis(500).await;
58 }
59}
diff --git a/examples/stm32h5/src/bin/usb_uac_speaker.rs b/examples/stm32h5/src/bin/usb_uac_speaker.rs
new file mode 100644
index 000000000..8c24fa916
--- /dev/null
+++ b/examples/stm32h5/src/bin/usb_uac_speaker.rs
@@ -0,0 +1,381 @@
1#![no_std]
2#![no_main]
3
4use core::cell::{Cell, RefCell};
5
6use defmt::{panic, *};
7use embassy_executor::Spawner;
8use embassy_stm32::time::Hertz;
9use embassy_stm32::{bind_interrupts, interrupt, peripherals, timer, usb, Config};
10use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};
11use embassy_sync::blocking_mutex::Mutex;
12use embassy_sync::signal::Signal;
13use embassy_sync::zerocopy_channel;
14use embassy_usb::class::uac1;
15use embassy_usb::class::uac1::speaker::{self, Speaker};
16use embassy_usb::driver::EndpointError;
17use heapless::Vec;
18use micromath::F32Ext;
19use static_cell::StaticCell;
20use {defmt_rtt as _, panic_probe as _};
21
22bind_interrupts!(struct Irqs {
23 USB_DRD_FS => usb::InterruptHandler<peripherals::USB>;
24});
25
26static TIMER: Mutex<CriticalSectionRawMutex, RefCell<Option<timer::low_level::Timer<peripherals::TIM5>>>> =
27 Mutex::new(RefCell::new(None));
28
29// A counter signal that is written by the feedback timer, once every `FEEDBACK_REFRESH_PERIOD`.
30// At that point, a feedback value is sent to the host.
31pub static FEEDBACK_SIGNAL: Signal<CriticalSectionRawMutex, u32> = Signal::new();
32
33// Stereo input
34pub const INPUT_CHANNEL_COUNT: usize = 2;
35
36// This example uses a fixed sample rate of 48 kHz.
37pub const SAMPLE_RATE_HZ: u32 = 48_000;
38pub const FEEDBACK_COUNTER_TICK_RATE: u32 = 31_250_000;
39
40// Use 32 bit samples, which allow for a lot of (software) volume adjustment without degradation of quality.
41pub const SAMPLE_WIDTH: uac1::SampleWidth = uac1::SampleWidth::Width4Byte;
42pub const SAMPLE_WIDTH_BIT: usize = SAMPLE_WIDTH.in_bit();
43pub const SAMPLE_SIZE: usize = SAMPLE_WIDTH as usize;
44pub const SAMPLE_SIZE_PER_S: usize = (SAMPLE_RATE_HZ as usize) * INPUT_CHANNEL_COUNT * SAMPLE_SIZE;
45
46// Size of audio samples per 1 ms - for the full-speed USB frame period of 1 ms.
47pub const USB_FRAME_SIZE: usize = SAMPLE_SIZE_PER_S.div_ceil(1000);
48
49// Select front left and right audio channels.
50pub const AUDIO_CHANNELS: [uac1::Channel; INPUT_CHANNEL_COUNT] = [uac1::Channel::LeftFront, uac1::Channel::RightFront];
51
52// Factor of two as a margin for feedback (this is an excessive amount)
53pub const USB_MAX_PACKET_SIZE: usize = 2 * USB_FRAME_SIZE;
54pub const USB_MAX_SAMPLE_COUNT: usize = USB_MAX_PACKET_SIZE / SAMPLE_SIZE;
55
56// The data type that is exchanged via the zero-copy channel (a sample vector).
57pub type SampleBlock = Vec<u32, USB_MAX_SAMPLE_COUNT>;
58
59// Feedback is provided in 10.14 format for full-speed endpoints.
60pub const FEEDBACK_REFRESH_PERIOD: uac1::FeedbackRefresh = uac1::FeedbackRefresh::Period8Frames;
61const FEEDBACK_SHIFT: usize = 14;
62
63const TICKS_PER_SAMPLE: f32 = (FEEDBACK_COUNTER_TICK_RATE as f32) / (SAMPLE_RATE_HZ as f32);
64
65struct Disconnected {}
66
67impl From<EndpointError> for Disconnected {
68 fn from(val: EndpointError) -> Self {
69 match val {
70 EndpointError::BufferOverflow => panic!("Buffer overflow"),
71 EndpointError::Disabled => Disconnected {},
72 }
73 }
74}
75
76/// Sends feedback messages to the host.
77async fn feedback_handler<'d, T: usb::Instance + 'd>(
78 feedback: &mut speaker::Feedback<'d, usb::Driver<'d, T>>,
79 feedback_factor: f32,
80) -> Result<(), Disconnected> {
81 let mut packet: Vec<u8, 4> = Vec::new();
82
83 // Collects the fractional component of the feedback value that is lost by rounding.
84 let mut rest = 0.0_f32;
85
86 loop {
87 let counter = FEEDBACK_SIGNAL.wait().await;
88
89 packet.clear();
90
91 let raw_value = counter as f32 * feedback_factor + rest;
92 let value = raw_value.round();
93 rest = raw_value - value;
94
95 let value = value as u32;
96
97 debug!("Feedback value: {}", value);
98
99 packet.push(value as u8).unwrap();
100 packet.push((value >> 8) as u8).unwrap();
101 packet.push((value >> 16) as u8).unwrap();
102
103 feedback.write_packet(&packet).await?;
104 }
105}
106
107/// Handles streaming of audio data from the host.
108async fn stream_handler<'d, T: usb::Instance + 'd>(
109 stream: &mut speaker::Stream<'d, usb::Driver<'d, T>>,
110 sender: &mut zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>,
111) -> Result<(), Disconnected> {
112 loop {
113 let mut usb_data = [0u8; USB_MAX_PACKET_SIZE];
114 let data_size = stream.read_packet(&mut usb_data).await?;
115
116 let word_count = data_size / SAMPLE_SIZE;
117
118 if word_count * SAMPLE_SIZE == data_size {
119 // Obtain a buffer from the channel
120 let samples = sender.send().await;
121 samples.clear();
122
123 for w in 0..word_count {
124 let byte_offset = w * SAMPLE_SIZE;
125 let sample = u32::from_le_bytes(usb_data[byte_offset..byte_offset + SAMPLE_SIZE].try_into().unwrap());
126
127 // Fill the sample buffer with data.
128 samples.push(sample).unwrap();
129 }
130
131 sender.send_done();
132 } else {
133 debug!("Invalid USB buffer size of {}, skipped.", data_size);
134 }
135 }
136}
137
138/// Receives audio samples from the USB streaming task and can play them back.
139#[embassy_executor::task]
140async fn audio_receiver_task(mut usb_audio_receiver: zerocopy_channel::Receiver<'static, NoopRawMutex, SampleBlock>) {
141 loop {
142 let _samples = usb_audio_receiver.receive().await;
143 // Use the samples, for example play back via the SAI peripheral.
144
145 // Notify the channel that the buffer is now ready to be reused
146 usb_audio_receiver.receive_done();
147 }
148}
149
150/// Receives audio samples from the host.
151#[embassy_executor::task]
152async fn usb_streaming_task(
153 mut stream: speaker::Stream<'static, usb::Driver<'static, peripherals::USB>>,
154 mut sender: zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>,
155) {
156 loop {
157 stream.wait_connection().await;
158 info!("USB connected.");
159 _ = stream_handler(&mut stream, &mut sender).await;
160 info!("USB disconnected.");
161 }
162}
163
164/// Sends sample rate feedback to the host.
165///
166/// The `feedback_factor` scales the feedback timer's counter value so that the result is the number of samples that
167/// this device played back or "consumed" during one SOF period (1 ms) - in 10.14 format.
168///
169/// Ideally, the `feedback_factor` that is calculated below would be an integer for avoiding numerical errors.
170/// This is achieved by having `TICKS_PER_SAMPLE` be a power of two. For audio applications at a sample rate of 48 kHz,
171/// 24.576 MHz would be one such option.
172#[embassy_executor::task]
173async fn usb_feedback_task(mut feedback: speaker::Feedback<'static, usb::Driver<'static, peripherals::USB>>) {
174 let feedback_factor =
175 ((1 << FEEDBACK_SHIFT) as f32 / TICKS_PER_SAMPLE) / FEEDBACK_REFRESH_PERIOD.frame_count() as f32;
176
177 loop {
178 feedback.wait_connection().await;
179 _ = feedback_handler(&mut feedback, feedback_factor).await;
180 }
181}
182
183#[embassy_executor::task]
184async fn usb_task(mut usb_device: embassy_usb::UsbDevice<'static, usb::Driver<'static, peripherals::USB>>) {
185 usb_device.run().await;
186}
187
188/// Checks for changes on the control monitor of the class.
189///
190/// In this case, monitor changes of volume or mute state.
191#[embassy_executor::task]
192async fn usb_control_task(control_monitor: speaker::ControlMonitor<'static>) {
193 loop {
194 control_monitor.changed().await;
195
196 for channel in AUDIO_CHANNELS {
197 let volume = control_monitor.volume(channel).unwrap();
198 info!("Volume changed to {} on channel {}.", volume, channel);
199 }
200 }
201}
202
203/// Feedback value measurement and calculation
204///
205/// Used for measuring/calculating the number of samples that were received from the host during the
206/// `FEEDBACK_REFRESH_PERIOD`.
207///
208/// Configured in this example with
209/// - a refresh period of 8 ms, and
210/// - a tick rate of 42 MHz.
211///
212/// This gives an (ideal) counter value of 336.000 for every update of the `FEEDBACK_SIGNAL`.
213#[interrupt]
214fn TIM5() {
215 static LAST_TICKS: Mutex<CriticalSectionRawMutex, Cell<u32>> = Mutex::new(Cell::new(0));
216 static FRAME_COUNT: Mutex<CriticalSectionRawMutex, Cell<usize>> = Mutex::new(Cell::new(0));
217
218 critical_section::with(|cs| {
219 // Read timer counter.
220 let timer = TIMER.borrow(cs).borrow().as_ref().unwrap().regs_gp32();
221
222 let status = timer.sr().read();
223
224 const CHANNEL_INDEX: usize = 0;
225 if status.ccif(CHANNEL_INDEX) {
226 let ticks = timer.ccr(CHANNEL_INDEX).read();
227
228 let frame_count = FRAME_COUNT.borrow(cs);
229 let last_ticks = LAST_TICKS.borrow(cs);
230
231 frame_count.set(frame_count.get() + 1);
232 if frame_count.get() >= FEEDBACK_REFRESH_PERIOD.frame_count() {
233 frame_count.set(0);
234 FEEDBACK_SIGNAL.signal(ticks.wrapping_sub(last_ticks.get()));
235 last_ticks.set(ticks);
236 }
237 };
238
239 // Clear trigger interrupt flag.
240 timer.sr().modify(|r| r.set_tif(false));
241 });
242}
243
244// If you are trying this and your USB device doesn't connect, the most
245// common issues are the RCC config and vbus_detection
246//
247// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure
248// for more information.
249#[embassy_executor::main]
250async fn main(spawner: Spawner) {
251 let mut config = Config::default();
252 {
253 use embassy_stm32::rcc::*;
254 config.rcc.hsi = None;
255 config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB
256 config.rcc.hse = Some(Hse {
257 freq: Hertz(8_000_000),
258 mode: HseMode::BypassDigital,
259 });
260 config.rcc.pll1 = Some(Pll {
261 source: PllSource::HSE,
262 prediv: PllPreDiv::DIV2,
263 mul: PllMul::MUL125,
264 divp: Some(PllDiv::DIV2), // 250 Mhz
265 divq: None,
266 divr: None,
267 });
268 config.rcc.pll2 = Some(Pll {
269 source: PllSource::HSE,
270 prediv: PllPreDiv::DIV4,
271 mul: PllMul::MUL123,
272 divp: Some(PllDiv::DIV20), // 12.3 Mhz, close to 12.288 MHz for 48 kHz audio
273 divq: None,
274 divr: None,
275 });
276 config.rcc.ahb_pre = AHBPrescaler::DIV2;
277 config.rcc.apb1_pre = APBPrescaler::DIV4;
278 config.rcc.apb2_pre = APBPrescaler::DIV2;
279 config.rcc.apb3_pre = APBPrescaler::DIV4;
280 config.rcc.sys = Sysclk::PLL1_P;
281 config.rcc.voltage_scale = VoltageScale::Scale0;
282 config.rcc.mux.usbsel = mux::Usbsel::HSI48;
283 config.rcc.mux.sai2sel = mux::Saisel::PLL2_P;
284 }
285 let p = embassy_stm32::init(config);
286
287 info!("Hello World!");
288
289 // Configure all required buffers in a static way.
290 debug!("USB packet size is {} byte", USB_MAX_PACKET_SIZE);
291 static CONFIG_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new();
292 let config_descriptor = CONFIG_DESCRIPTOR.init([0; 256]);
293
294 static BOS_DESCRIPTOR: StaticCell<[u8; 32]> = StaticCell::new();
295 let bos_descriptor = BOS_DESCRIPTOR.init([0; 32]);
296
297 const CONTROL_BUF_SIZE: usize = 64;
298 static CONTROL_BUF: StaticCell<[u8; CONTROL_BUF_SIZE]> = StaticCell::new();
299 let control_buf = CONTROL_BUF.init([0; CONTROL_BUF_SIZE]);
300
301 static STATE: StaticCell<speaker::State> = StaticCell::new();
302 let state = STATE.init(speaker::State::new());
303
304 let usb_driver = usb::Driver::new(p.USB, Irqs, p.PA12, p.PA11);
305
306 // Basic USB device configuration
307 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
308 config.manufacturer = Some("Embassy");
309 config.product = Some("USB-audio-speaker example");
310 config.serial_number = Some("12345678");
311
312 // Required for windows compatibility.
313 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
314 config.device_class = 0xEF;
315 config.device_sub_class = 0x02;
316 config.device_protocol = 0x01;
317 config.composite_with_iads = true;
318
319 let mut builder = embassy_usb::Builder::new(
320 usb_driver,
321 config,
322 config_descriptor,
323 bos_descriptor,
324 &mut [], // no msos descriptors
325 control_buf,
326 );
327
328 // Create the UAC1 Speaker class components
329 let (stream, feedback, control_monitor) = Speaker::new(
330 &mut builder,
331 state,
332 USB_MAX_PACKET_SIZE as u16,
333 uac1::SampleWidth::Width4Byte,
334 &[SAMPLE_RATE_HZ],
335 &AUDIO_CHANNELS,
336 FEEDBACK_REFRESH_PERIOD,
337 );
338
339 // Create the USB device
340 let usb_device = builder.build();
341
342 // Establish a zero-copy channel for transferring received audio samples between tasks
343 static SAMPLE_BLOCKS: StaticCell<[SampleBlock; 2]> = StaticCell::new();
344 let sample_blocks = SAMPLE_BLOCKS.init([Vec::new(), Vec::new()]);
345
346 static CHANNEL: StaticCell<zerocopy_channel::Channel<'_, NoopRawMutex, SampleBlock>> = StaticCell::new();
347 let channel = CHANNEL.init(zerocopy_channel::Channel::new(sample_blocks));
348 let (sender, receiver) = channel.split();
349
350 // Run a timer for counting between SOF interrupts.
351 let mut tim5 = timer::low_level::Timer::new(p.TIM5);
352 tim5.set_tick_freq(Hertz(FEEDBACK_COUNTER_TICK_RATE));
353 tim5.set_trigger_source(timer::low_level::TriggerSource::ITR12); // The USB SOF signal.
354
355 const TIMER_CHANNEL: timer::Channel = timer::Channel::Ch1;
356 tim5.set_input_ti_selection(TIMER_CHANNEL, timer::low_level::InputTISelection::TRC);
357 tim5.set_input_capture_prescaler(TIMER_CHANNEL, 0);
358 tim5.set_input_capture_filter(TIMER_CHANNEL, timer::low_level::FilterValue::FCK_INT_N2);
359
360 // Reset all interrupt flags.
361 tim5.regs_gp32().sr().write(|r| r.0 = 0);
362
363 tim5.enable_channel(TIMER_CHANNEL, true);
364 tim5.enable_input_interrupt(TIMER_CHANNEL, true);
365
366 tim5.start();
367
368 TIMER.lock(|p| p.borrow_mut().replace(tim5));
369
370 // Unmask the TIM5 interrupt.
371 unsafe {
372 cortex_m::peripheral::NVIC::unmask(interrupt::TIM5);
373 }
374
375 // Launch USB audio tasks.
376 unwrap!(spawner.spawn(usb_control_task(control_monitor)));
377 unwrap!(spawner.spawn(usb_streaming_task(stream, sender)));
378 unwrap!(spawner.spawn(usb_feedback_task(feedback)));
379 unwrap!(spawner.spawn(usb_task(usb_device)));
380 unwrap!(spawner.spawn(audio_receiver_task(receiver)));
381}
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml
index 13fce7dc7..68a0c3d88 100644
--- a/examples/stm32h7/Cargo.toml
+++ b/examples/stm32h7/Cargo.toml
@@ -7,11 +7,11 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8# Change stm32h743bi to your chip name, if necessary. 8# Change stm32h743bi to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h743bi", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h743bi", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } 11embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" }
12embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 12embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
13embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 13embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
14embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } 14embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] }
15embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 15embassy-usb = { version = "0.3.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
@@ -23,7 +23,7 @@ cortex-m-rt = "0.7.0"
23embedded-hal = "0.2.6" 23embedded-hal = "0.2.6"
24embedded-hal-1 = { package = "embedded-hal", version = "1.0" } 24embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
25embedded-hal-async = { version = "1.0" } 25embedded-hal-async = { version = "1.0" }
26embedded-nal-async = { version = "0.7.1" } 26embedded-nal-async = "0.8.0"
27embedded-io-async = { version = "0.6.1" } 27embedded-io-async = { version = "0.6.1" }
28panic-probe = { version = "0.3", features = ["print-defmt"] } 28panic-probe = { version = "0.3", features = ["print-defmt"] }
29heapless = { version = "0.8", default-features = false } 29heapless = { version = "0.8", default-features = false }
diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs
index 24983ca85..a1558b079 100644
--- a/examples/stm32h7/src/bin/eth_client.rs
+++ b/examples/stm32h7/src/bin/eth_client.rs
@@ -1,6 +1,8 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3 3
4use core::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
5
4use defmt::*; 6use defmt::*;
5use embassy_executor::Spawner; 7use embassy_executor::Spawner;
6use embassy_net::tcp::client::{TcpClient, TcpClientState}; 8use embassy_net::tcp::client::{TcpClient, TcpClientState};
@@ -12,7 +14,7 @@ use embassy_stm32::rng::Rng;
12use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; 14use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config};
13use embassy_time::Timer; 15use embassy_time::Timer;
14use embedded_io_async::Write; 16use embedded_io_async::Write;
15use embedded_nal_async::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpConnect}; 17use embedded_nal_async::TcpConnect;
16use rand_core::RngCore; 18use rand_core::RngCore;
17use static_cell::StaticCell; 19use static_cell::StaticCell;
18use {defmt_rtt as _, panic_probe as _}; 20use {defmt_rtt as _, panic_probe as _};
diff --git a/examples/stm32h7/src/bin/eth_client_mii.rs b/examples/stm32h7/src/bin/eth_client_mii.rs
index 768d85993..a352ef444 100644
--- a/examples/stm32h7/src/bin/eth_client_mii.rs
+++ b/examples/stm32h7/src/bin/eth_client_mii.rs
@@ -1,6 +1,8 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3 3
4use core::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
5
4use defmt::*; 6use defmt::*;
5use embassy_executor::Spawner; 7use embassy_executor::Spawner;
6use embassy_net::tcp::client::{TcpClient, TcpClientState}; 8use embassy_net::tcp::client::{TcpClient, TcpClientState};
@@ -12,7 +14,7 @@ use embassy_stm32::rng::Rng;
12use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; 14use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config};
13use embassy_time::Timer; 15use embassy_time::Timer;
14use embedded_io_async::Write; 16use embedded_io_async::Write;
15use embedded_nal_async::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpConnect}; 17use embedded_nal_async::TcpConnect;
16use rand_core::RngCore; 18use rand_core::RngCore;
17use static_cell::StaticCell; 19use static_cell::StaticCell;
18use {defmt_rtt as _, panic_probe as _}; 20use {defmt_rtt as _, panic_probe as _};
diff --git a/examples/stm32h7/src/bin/i2c_shared.rs b/examples/stm32h7/src/bin/i2c_shared.rs
index 6f4815582..136b91eeb 100644
--- a/examples/stm32h7/src/bin/i2c_shared.rs
+++ b/examples/stm32h7/src/bin/i2c_shared.rs
@@ -10,8 +10,10 @@ use embassy_stm32::i2c::{self, I2c};
10use embassy_stm32::mode::Async; 10use embassy_stm32::mode::Async;
11use embassy_stm32::time::Hertz; 11use embassy_stm32::time::Hertz;
12use embassy_stm32::{bind_interrupts, peripherals}; 12use embassy_stm32::{bind_interrupts, peripherals};
13use embassy_sync::blocking_mutex::raw::NoopRawMutex;
13use embassy_sync::blocking_mutex::NoopMutex; 14use embassy_sync::blocking_mutex::NoopMutex;
14use embassy_time::{Duration, Timer}; 15use embassy_time::{Duration, Timer};
16use embedded_hal_1::i2c::I2c as _;
15use static_cell::StaticCell; 17use static_cell::StaticCell;
16use {defmt_rtt as _, panic_probe as _}; 18use {defmt_rtt as _, panic_probe as _};
17 19
@@ -31,7 +33,7 @@ bind_interrupts!(struct Irqs {
31}); 33});
32 34
33#[embassy_executor::task] 35#[embassy_executor::task]
34async fn temperature(mut i2c: impl embedded_hal_1::i2c::I2c + 'static) { 36async fn temperature(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async>>) {
35 let mut data = [0u8; 2]; 37 let mut data = [0u8; 2];
36 38
37 loop { 39 loop {
@@ -48,7 +50,7 @@ async fn temperature(mut i2c: impl embedded_hal_1::i2c::I2c + 'static) {
48} 50}
49 51
50#[embassy_executor::task] 52#[embassy_executor::task]
51async fn humidity(mut i2c: impl embedded_hal_1::i2c::I2c + 'static) { 53async fn humidity(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async>>) {
52 let mut data = [0u8; 6]; 54 let mut data = [0u8; 6];
53 55
54 loop { 56 loop {
diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs
index 1e48ba67b..a1c53fc3f 100644
--- a/examples/stm32h7/src/bin/pwm.rs
+++ b/examples/stm32h7/src/bin/pwm.rs
@@ -6,7 +6,6 @@ use embassy_executor::Spawner;
6use embassy_stm32::gpio::OutputType; 6use embassy_stm32::gpio::OutputType;
7use embassy_stm32::time::khz; 7use embassy_stm32::time::khz;
8use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; 8use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
9use embassy_stm32::timer::Channel;
10use embassy_stm32::Config; 9use embassy_stm32::Config;
11use embassy_time::Timer; 10use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _}; 11use {defmt_rtt as _, panic_probe as _};
@@ -37,22 +36,22 @@ async fn main(_spawner: Spawner) {
37 let p = embassy_stm32::init(config); 36 let p = embassy_stm32::init(config);
38 info!("Hello World!"); 37 info!("Hello World!");
39 38
40 let ch1 = PwmPin::new_ch1(p.PA6, OutputType::PushPull); 39 let ch1_pin = PwmPin::new_ch1(p.PA6, OutputType::PushPull);
41 let mut pwm = SimplePwm::new(p.TIM3, Some(ch1), 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());
42 let max = pwm.get_max_duty(); 41 let mut ch1 = pwm.ch1();
43 pwm.enable(Channel::Ch1); 42 ch1.enable();
44 43
45 info!("PWM initialized"); 44 info!("PWM initialized");
46 info!("PWM max duty {}", max); 45 info!("PWM max duty {}", ch1.max_duty_cycle());
47 46
48 loop { 47 loop {
49 pwm.set_duty(Channel::Ch1, 0); 48 ch1.set_duty_cycle_fully_off();
50 Timer::after_millis(300).await; 49 Timer::after_millis(300).await;
51 pwm.set_duty(Channel::Ch1, max / 4); 50 ch1.set_duty_cycle_fraction(1, 4);
52 Timer::after_millis(300).await; 51 Timer::after_millis(300).await;
53 pwm.set_duty(Channel::Ch1, max / 2); 52 ch1.set_duty_cycle_fraction(1, 2);
54 Timer::after_millis(300).await; 53 Timer::after_millis(300).await;
55 pwm.set_duty(Channel::Ch1, max - 1); 54 ch1.set_duty_cycle(ch1.max_duty_cycle() - 1);
56 Timer::after_millis(300).await; 55 Timer::after_millis(300).await;
57 } 56 }
58} 57}
diff --git a/examples/stm32h7/src/bin/sai.rs b/examples/stm32h7/src/bin/sai.rs
index f6735e235..95ffe257a 100644
--- a/examples/stm32h7/src/bin/sai.rs
+++ b/examples/stm32h7/src/bin/sai.rs
@@ -81,8 +81,9 @@ async fn main(_spawner: Spawner) {
81 rx_config.sync_output = false; 81 rx_config.sync_output = false;
82 82
83 let tx_buffer: &mut [u32] = unsafe { 83 let tx_buffer: &mut [u32] = unsafe {
84 TX_BUFFER.initialize_all_copied(0); 84 let buf = &mut *core::ptr::addr_of_mut!(TX_BUFFER);
85 let (ptr, len) = TX_BUFFER.get_ptr_len(); 85 buf.initialize_all_copied(0);
86 let (ptr, len) = buf.get_ptr_len();
86 core::slice::from_raw_parts_mut(ptr, len) 87 core::slice::from_raw_parts_mut(ptr, len)
87 }; 88 };
88 89
@@ -98,15 +99,15 @@ async fn main(_spawner: Spawner) {
98 ); 99 );
99 100
100 let rx_buffer: &mut [u32] = unsafe { 101 let rx_buffer: &mut [u32] = unsafe {
101 RX_BUFFER.initialize_all_copied(0); 102 let buf = &mut *core::ptr::addr_of_mut!(RX_BUFFER);
102 let (ptr, len) = RX_BUFFER.get_ptr_len(); 103 buf.initialize_all_copied(0);
104 let (ptr, len) = buf.get_ptr_len();
103 core::slice::from_raw_parts_mut(ptr, len) 105 core::slice::from_raw_parts_mut(ptr, len)
104 }; 106 };
105 107
106 let mut sai_receiver = Sai::new_synchronous(sub_block_rx, p.PE3, p.DMA1_CH1, rx_buffer, rx_config); 108 let mut sai_receiver = Sai::new_synchronous(sub_block_rx, p.PE3, p.DMA1_CH1, rx_buffer, rx_config);
107 109
108 sai_receiver.start(); 110 sai_receiver.start().unwrap();
109 sai_transmitter.start();
110 111
111 let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH]; 112 let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH];
112 113
diff --git a/examples/stm32h7/src/bin/spi_bdma.rs b/examples/stm32h7/src/bin/spi_bdma.rs
index 43fb6b41c..9166fe9b6 100644
--- a/examples/stm32h7/src/bin/spi_bdma.rs
+++ b/examples/stm32h7/src/bin/spi_bdma.rs
@@ -22,10 +22,11 @@ static mut RAM_D3: GroundedArrayCell<u8, 256> = GroundedArrayCell::uninit();
22#[embassy_executor::task] 22#[embassy_executor::task]
23async fn main_task(mut spi: spi::Spi<'static, Async>) { 23async fn main_task(mut spi: spi::Spi<'static, Async>) {
24 let (read_buffer, write_buffer) = unsafe { 24 let (read_buffer, write_buffer) = unsafe {
25 RAM_D3.initialize_all_copied(0); 25 let ram = &mut *core::ptr::addr_of_mut!(RAM_D3);
26 ram.initialize_all_copied(0);
26 ( 27 (
27 RAM_D3.get_subslice_mut_unchecked(0, 128), 28 ram.get_subslice_mut_unchecked(0, 128),
28 RAM_D3.get_subslice_mut_unchecked(128, 128), 29 ram.get_subslice_mut_unchecked(128, 128),
29 ) 30 )
30 }; 31 };
31 32
diff --git a/examples/stm32h723/.cargo/config.toml b/examples/stm32h723/.cargo/config.toml
new file mode 100644
index 000000000..2e53663c5
--- /dev/null
+++ b/examples/stm32h723/.cargo/config.toml
@@ -0,0 +1,8 @@
1[target.thumbv7em-none-eabihf]
2runner = 'probe-rs run --chip STM32H723ZGTx'
3
4[build]
5target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
6
7[env]
8DEFMT_LOG = "trace"
diff --git a/examples/stm32h723/Cargo.toml b/examples/stm32h723/Cargo.toml
new file mode 100644
index 000000000..82f3cb9c2
--- /dev/null
+++ b/examples/stm32h723/Cargo.toml
@@ -0,0 +1,69 @@
1[package]
2edition = "2021"
3name = "embassy-stm32h723-examples"
4version = "0.1.0"
5license = "MIT OR Apache-2.0"
6
7[dependencies]
8# Change stm32h723zg to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h723zg", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] }
10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
14
15defmt = "0.3"
16defmt-rtt = "0.4"
17
18cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
19cortex-m-rt = "0.7.0"
20embedded-hal = "0.2.6"
21embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
22embedded-hal-async = { version = "1.0" }
23embedded-nal-async = "0.8.0"
24embedded-io-async = { version = "0.6.1" }
25panic-probe = { version = "0.3", features = ["print-defmt"] }
26heapless = { version = "0.8", default-features = false }
27rand_core = "0.6.3"
28critical-section = "1.1"
29static_cell = "2"
30chrono = { version = "^0.4", default-features = false }
31grounded = "0.2.0"
32
33# cargo build/run
34[profile.dev]
35codegen-units = 1
36debug = 2
37debug-assertions = true # <-
38incremental = false
39opt-level = 3 # <-
40overflow-checks = true # <-
41
42# cargo test
43[profile.test]
44codegen-units = 1
45debug = 2
46debug-assertions = true # <-
47incremental = false
48opt-level = 3 # <-
49overflow-checks = true # <-
50
51# cargo build/run --release
52[profile.release]
53codegen-units = 1
54debug = 2
55debug-assertions = false # <-
56incremental = false
57lto = 'fat'
58opt-level = 3 # <-
59overflow-checks = false # <-
60
61# cargo test --release
62[profile.bench]
63codegen-units = 1
64debug = 2
65debug-assertions = false # <-
66incremental = false
67lto = 'fat'
68opt-level = 3 # <-
69overflow-checks = false # <-
diff --git a/examples/stm32h723/build.rs b/examples/stm32h723/build.rs
new file mode 100644
index 000000000..30691aa97
--- /dev/null
+++ b/examples/stm32h723/build.rs
@@ -0,0 +1,35 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
35}
diff --git a/examples/stm32h723/memory.x b/examples/stm32h723/memory.x
new file mode 100644
index 000000000..aa4c00505
--- /dev/null
+++ b/examples/stm32h723/memory.x
@@ -0,0 +1,106 @@
1MEMORY
2{
3 /* This file is intended for parts in the STM32H723 family. (RM0468) */
4 /* - FLASH and RAM are mandatory memory sections. */
5 /* - The sum of all non-FLASH sections must add to 564k total device RAM. */
6 /* - The FLASH section size must match your device, see table below. */
7
8 /* FLASH */
9 /* Select the appropriate FLASH size for your device. */
10 /* - STM32H730xB 128K */
11 /* - STM32H723xE/725xE 512K */
12 /* - STM32H723xG/725xG/733xG/735xG 1M */
13 FLASH1 : ORIGIN = 0x08000000, LENGTH = 1M
14
15 /* Data TCM */
16 /* - Two contiguous 64KB RAMs. */
17 /* - Used for interrupt handlers, stacks and general RAM. */
18 /* - Zero wait-states. */
19 /* - The DTCM is taken as the origin of the base ram. (See below.) */
20 /* This is also where the interrupt table and such will live, */
21 /* which is required for deterministic performance. */
22 DTCM : ORIGIN = 0x20000000, LENGTH = 128K
23
24 /* Instruction TCM */
25 /* - More memory can be assigned to ITCM. See AXI SRAM notes, below. */
26 /* - Used for latency-critical interrupt handlers etc. */
27 /* - Zero wait-states. */
28 ITCM : ORIGIN = 0x00000000, LENGTH = 64K + 0K
29
30 /* AXI SRAM */
31 /* - AXISRAM is in D1 and accessible by all system masters except BDMA. */
32 /* - Suitable for application data not stored in DTCM. */
33 /* - Zero wait-states. */
34 /* - The 192k of extra shared RAM is fully allotted to the AXI SRAM by default. */
35 /* As a result: 64k (64k + 0k) for ITCM and 320k (128k + 192k) for AXI SRAM. */
36 /* This can be re-configured via the TCM_AXI_SHARED[1,0] register when more */
37 /* ITCM is required. */
38 AXISRAM : ORIGIN = 0x24000000, LENGTH = 128K + 192K
39
40 /* AHB SRAM */
41 /* - SRAM1-2 are in D2 and accessible by all system masters except BDMA, LTDC */
42 /* and SDMMC1. Suitable for use as DMA buffers. */
43 /* - SRAM4 is in D3 and additionally accessible by the BDMA. Used for BDMA */
44 /* buffers, for storing application data in lower-power modes. */
45 /* - Zero wait-states. */
46 SRAM1 : ORIGIN = 0x30000000, LENGTH = 16K
47 SRAM2 : ORIGIN = 0x30040000, LENGTH = 16K
48 SRAM4 : ORIGIN = 0x38000000, LENGTH = 16K
49
50 /* Backup SRAM */
51 /* Used to store data during low-power sleeps. */
52 BSRAM : ORIGIN = 0x38800000, LENGTH = 4K
53}
54
55/*
56/* Assign the memory regions defined above for use. */
57/*
58
59/* Provide the mandatory FLASH and RAM definitions for cortex-m-rt's linker script. */
60REGION_ALIAS(FLASH, FLASH1);
61REGION_ALIAS(RAM, DTCM);
62
63/* The location of the stack can be overridden using the `_stack_start` symbol. */
64/* - Set the stack location at the end of RAM, using all remaining space. */
65_stack_start = ORIGIN(RAM) + LENGTH(RAM);
66
67/* The location of the .text section can be overridden using the */
68/* `_stext` symbol. By default it will place after .vector_table. */
69/* _stext = ORIGIN(FLASH) + 0x40c; */
70
71/* Define sections for placing symbols into the extra memory regions above. */
72/* This makes them accessible from code. */
73/* - ITCM, DTCM and AXISRAM connect to a 64-bit wide bus -> align to 8 bytes. */
74/* - All other memories connect to a 32-bit wide bus -> align to 4 bytes. */
75SECTIONS {
76 .itcm (NOLOAD) : ALIGN(8) {
77 *(.itcm .itcm.*);
78 . = ALIGN(8);
79 } > ITCM
80
81 .axisram (NOLOAD) : ALIGN(8) {
82 *(.axisram .axisram.*);
83 . = ALIGN(8);
84 } > AXISRAM
85
86 .sram1 (NOLOAD) : ALIGN(4) {
87 *(.sram1 .sram1.*);
88 . = ALIGN(4);
89 } > SRAM1
90
91 .sram2 (NOLOAD) : ALIGN(4) {
92 *(.sram2 .sram2.*);
93 . = ALIGN(4);
94 } > SRAM2
95
96 .sram4 (NOLOAD) : ALIGN(4) {
97 *(.sram4 .sram4.*);
98 . = ALIGN(4);
99 } > SRAM4
100
101 .bsram (NOLOAD) : ALIGN(4) {
102 *(.bsram .bsram.*);
103 . = ALIGN(4);
104 } > BSRAM
105
106};
diff --git a/examples/stm32h723/src/bin/spdifrx.rs b/examples/stm32h723/src/bin/spdifrx.rs
new file mode 100644
index 000000000..69ef5cd07
--- /dev/null
+++ b/examples/stm32h723/src/bin/spdifrx.rs
@@ -0,0 +1,165 @@
1//! This example receives inputs on SPDIFRX and outputs on SAI4.
2//!
3//! Only very few controllers connect the SPDIFRX symbol clock to a SAI peripheral's clock input.
4//! However, this is necessary for synchronizing the symbol rates and avoiding glitches.
5#![no_std]
6#![no_main]
7
8use defmt::{info, trace};
9use embassy_executor::Spawner;
10use embassy_futures::select::{self, select, Either};
11use embassy_stm32::spdifrx::{self, Spdifrx};
12use embassy_stm32::{bind_interrupts, peripherals, sai};
13use grounded::uninit::GroundedArrayCell;
14use hal::sai::*;
15use {defmt_rtt as _, embassy_stm32 as hal, panic_probe as _};
16
17bind_interrupts!(struct Irqs {
18 SPDIF_RX => spdifrx::GlobalInterruptHandler<peripherals::SPDIFRX1>;
19});
20
21const CHANNEL_COUNT: usize = 2;
22const BLOCK_LENGTH: usize = 64;
23const HALF_DMA_BUFFER_LENGTH: usize = BLOCK_LENGTH * CHANNEL_COUNT;
24const DMA_BUFFER_LENGTH: usize = HALF_DMA_BUFFER_LENGTH * 2; // 2 half-blocks
25
26// DMA buffers must be in special regions. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions
27#[link_section = ".sram1"]
28static mut SPDIFRX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit();
29
30#[link_section = ".sram4"]
31static mut SAI_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit();
32
33#[embassy_executor::main]
34async fn main(_spawner: Spawner) {
35 let mut peripheral_config = embassy_stm32::Config::default();
36 {
37 use embassy_stm32::rcc::*;
38 peripheral_config.rcc.hsi = Some(HSIPrescaler::DIV1);
39 peripheral_config.rcc.pll1 = Some(Pll {
40 source: PllSource::HSI,
41 prediv: PllPreDiv::DIV16,
42 mul: PllMul::MUL200,
43 divp: Some(PllDiv::DIV2), // 400 MHz
44 divq: Some(PllDiv::DIV2),
45 divr: Some(PllDiv::DIV2),
46 });
47 peripheral_config.rcc.sys = Sysclk::PLL1_P;
48 peripheral_config.rcc.ahb_pre = AHBPrescaler::DIV2;
49 peripheral_config.rcc.apb1_pre = APBPrescaler::DIV2;
50 peripheral_config.rcc.apb2_pre = APBPrescaler::DIV2;
51 peripheral_config.rcc.apb3_pre = APBPrescaler::DIV2;
52 peripheral_config.rcc.apb4_pre = APBPrescaler::DIV2;
53
54 peripheral_config.rcc.mux.spdifrxsel = mux::Spdifrxsel::PLL1_Q;
55 }
56 let mut p = embassy_stm32::init(peripheral_config);
57
58 info!("SPDIFRX to SAI4 bridge");
59
60 // Use SPDIFRX clock for SAI.
61 // This ensures equal rates of sample production and consumption.
62 let clk_source = embassy_stm32::pac::rcc::vals::Saiasel::_RESERVED_5;
63 embassy_stm32::pac::RCC.d3ccipr().modify(|w| {
64 w.set_sai4asel(clk_source);
65 });
66
67 let sai_buffer: &mut [u32] = unsafe {
68 SAI_BUFFER.initialize_all_copied(0);
69 let (ptr, len) = SAI_BUFFER.get_ptr_len();
70 core::slice::from_raw_parts_mut(ptr, len)
71 };
72
73 let spdifrx_buffer: &mut [u32] = unsafe {
74 SPDIFRX_BUFFER.initialize_all_copied(0);
75 let (ptr, len) = SPDIFRX_BUFFER.get_ptr_len();
76 core::slice::from_raw_parts_mut(ptr, len)
77 };
78
79 let mut sai_transmitter = new_sai_transmitter(
80 &mut p.SAI4,
81 &mut p.PD13,
82 &mut p.PC1,
83 &mut p.PD12,
84 &mut p.BDMA_CH0,
85 sai_buffer,
86 );
87 let mut spdif_receiver = new_spdif_receiver(&mut p.SPDIFRX1, &mut p.PD7, &mut p.DMA2_CH7, spdifrx_buffer);
88 spdif_receiver.start();
89
90 let mut renew_sai = false;
91 loop {
92 let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH];
93
94 if renew_sai {
95 renew_sai = false;
96 trace!("Renew SAI.");
97 drop(sai_transmitter);
98 sai_transmitter = new_sai_transmitter(
99 &mut p.SAI4,
100 &mut p.PD13,
101 &mut p.PC1,
102 &mut p.PD12,
103 &mut p.BDMA_CH0,
104 sai_buffer,
105 );
106 }
107
108 match select(spdif_receiver.read(&mut buf), sai_transmitter.wait_write_error()).await {
109 Either::First(spdif_read_result) => match spdif_read_result {
110 Ok(_) => (),
111 Err(spdifrx::Error::RingbufferError(_)) => {
112 trace!("SPDIFRX ringbuffer error. Renew.");
113 drop(spdif_receiver);
114 spdif_receiver = new_spdif_receiver(&mut p.SPDIFRX1, &mut p.PD7, &mut p.DMA2_CH7, spdifrx_buffer);
115 spdif_receiver.start();
116 continue;
117 }
118 Err(spdifrx::Error::ChannelSyncError) => {
119 trace!("SPDIFRX channel sync (left/right assignment) error.");
120 continue;
121 }
122 },
123 Either::Second(_) => {
124 renew_sai = true;
125 continue;
126 }
127 };
128
129 renew_sai = sai_transmitter.write(&buf).await.is_err();
130 }
131}
132
133/// Creates a new SPDIFRX instance for receiving sample data.
134///
135/// Used (again) after dropping the SPDIFRX instance, in case of errors (e.g. source disconnect).
136fn new_spdif_receiver<'d>(
137 spdifrx: &'d mut peripherals::SPDIFRX1,
138 input_pin: &'d mut peripherals::PD7,
139 dma: &'d mut peripherals::DMA2_CH7,
140 buf: &'d mut [u32],
141) -> Spdifrx<'d, peripherals::SPDIFRX1> {
142 Spdifrx::new(spdifrx, Irqs, spdifrx::Config::default(), input_pin, dma, buf)
143}
144
145/// Creates a new SAI4 instance for transmitting sample data.
146///
147/// Used (again) after dropping the SAI4 instance, in case of errors (e.g. buffer overrun).
148fn new_sai_transmitter<'d>(
149 sai: &'d mut peripherals::SAI4,
150 sck: &'d mut peripherals::PD13,
151 sd: &'d mut peripherals::PC1,
152 fs: &'d mut peripherals::PD12,
153 dma: &'d mut peripherals::BDMA_CH0,
154 buf: &'d mut [u32],
155) -> Sai<'d, peripherals::SAI4, u32> {
156 let mut sai_config = hal::sai::Config::default();
157 sai_config.slot_count = hal::sai::word::U4(CHANNEL_COUNT as u8);
158 sai_config.slot_enable = 0xFFFF; // All slots
159 sai_config.data_size = sai::DataSize::Data32;
160 sai_config.frame_length = (CHANNEL_COUNT * 32) as u8;
161 sai_config.master_clock_divider = hal::sai::MasterClockDivider::MasterClockDisabled;
162
163 let (sub_block_tx, _) = hal::sai::split_subblocks(sai);
164 Sai::new_asynchronous(sub_block_tx, sck, sd, fs, dma, buf, sai_config)
165}
diff --git a/examples/stm32h735/Cargo.toml b/examples/stm32h735/Cargo.toml
index 93e9575b6..a517b9727 100644
--- a/examples/stm32h735/Cargo.toml
+++ b/examples/stm32h735/Cargo.toml
@@ -6,9 +6,9 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h735ig", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } 8embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h735ig", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] }
9embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 9embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
10embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } 10embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.2", 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" }
14 14
diff --git a/examples/stm32h755cm4/Cargo.toml b/examples/stm32h755cm4/Cargo.toml
index 7a42fbdaa..1d4d3eb85 100644
--- a/examples/stm32h755cm4/Cargo.toml
+++ b/examples/stm32h755cm4/Cargo.toml
@@ -7,11 +7,11 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8# Change stm32h755zi-cm4 to your chip name, if necessary. 8# Change stm32h755zi-cm4 to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm4", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm4", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } 11embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" }
12embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 12embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
13embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 13embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
14embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } 14embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] }
15embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 15embassy-usb = { version = "0.3.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
@@ -23,7 +23,7 @@ cortex-m-rt = "0.7.0"
23embedded-hal = "0.2.6" 23embedded-hal = "0.2.6"
24embedded-hal-1 = { package = "embedded-hal", version = "1.0" } 24embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
25embedded-hal-async = { version = "1.0" } 25embedded-hal-async = { version = "1.0" }
26embedded-nal-async = { version = "0.7.1" } 26embedded-nal-async = "0.8.0"
27embedded-io-async = { version = "0.6.1" } 27embedded-io-async = { version = "0.6.1" }
28panic-probe = { version = "0.3", features = ["print-defmt"] } 28panic-probe = { version = "0.3", features = ["print-defmt"] }
29heapless = { version = "0.8", default-features = false } 29heapless = { version = "0.8", default-features = false }
diff --git a/examples/stm32h755cm7/Cargo.toml b/examples/stm32h755cm7/Cargo.toml
index 4f0f69c3f..76c88c806 100644
--- a/examples/stm32h755cm7/Cargo.toml
+++ b/examples/stm32h755cm7/Cargo.toml
@@ -7,11 +7,11 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8# Change stm32h743bi to your chip name, if necessary. 8# Change stm32h743bi to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm7", "time-driver-tim3", "exti", "memory-x", "unstable-pac", "chrono"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm7", "time-driver-tim3", "exti", "memory-x", "unstable-pac", "chrono"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } 11embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" }
12embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 12embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
13embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 13embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
14embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } 14embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] }
15embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 15embassy-usb = { version = "0.3.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
@@ -23,7 +23,7 @@ cortex-m-rt = "0.7.0"
23embedded-hal = "0.2.6" 23embedded-hal = "0.2.6"
24embedded-hal-1 = { package = "embedded-hal", version = "1.0" } 24embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
25embedded-hal-async = { version = "1.0" } 25embedded-hal-async = { version = "1.0" }
26embedded-nal-async = { version = "0.7.1" } 26embedded-nal-async = "0.8.0"
27embedded-io-async = { version = "0.6.1" } 27embedded-io-async = { version = "0.6.1" }
28panic-probe = { version = "0.3", features = ["print-defmt"] } 28panic-probe = { version = "0.3", features = ["print-defmt"] }
29heapless = { version = "0.8", default-features = false } 29heapless = { version = "0.8", default-features = false }
diff --git a/examples/stm32h7b0/.cargo/config.toml b/examples/stm32h7b0/.cargo/config.toml
new file mode 100644
index 000000000..870849a27
--- /dev/null
+++ b/examples/stm32h7b0/.cargo/config.toml
@@ -0,0 +1,8 @@
1[target.thumbv7em-none-eabihf]
2runner = 'probe-rs run --chip STM32H7B0VBTx'
3
4[build]
5target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
6
7[env]
8DEFMT_LOG = "trace"
diff --git a/examples/stm32h7b0/Cargo.toml b/examples/stm32h7b0/Cargo.toml
new file mode 100644
index 000000000..aba398fa5
--- /dev/null
+++ b/examples/stm32h7b0/Cargo.toml
@@ -0,0 +1,74 @@
1[package]
2edition = "2021"
3name = "embassy-stm32h7b0-examples"
4version = "0.1.0"
5license = "MIT OR Apache-2.0"
6
7[dependencies]
8embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7b0vb", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] }
9embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
10embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" }
11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] }
14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
16
17defmt = "0.3"
18defmt-rtt = "0.4"
19
20cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
21cortex-m-rt = "0.7.0"
22embedded-hal = "0.2.6"
23embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
24embedded-hal-async = { version = "1.0" }
25embedded-nal-async = "0.8.0"
26embedded-io-async = { version = "0.6.1" }
27panic-probe = { version = "0.3", features = ["print-defmt"] }
28heapless = { version = "0.8", default-features = false }
29rand_core = "0.6.3"
30critical-section = "1.1"
31micromath = "2.0.0"
32stm32-fmc = "0.3.0"
33embedded-storage = "0.3.1"
34static_cell = "2"
35chrono = { version = "^0.4", default-features = false }
36grounded = "0.2.0"
37
38# cargo build/run
39[profile.dev]
40codegen-units = 1
41debug = 2
42debug-assertions = true # <-
43incremental = false
44opt-level = 3 # <-
45overflow-checks = true # <-
46
47# cargo test
48[profile.test]
49codegen-units = 1
50debug = 2
51debug-assertions = true # <-
52incremental = false
53opt-level = 3 # <-
54overflow-checks = true # <-
55
56# cargo build/run --release
57[profile.release]
58codegen-units = 1
59debug = 2
60debug-assertions = false # <-
61incremental = false
62lto = 'fat'
63opt-level = 3 # <-
64overflow-checks = false # <-
65
66# cargo test --release
67[profile.bench]
68codegen-units = 1
69debug = 2
70debug-assertions = false # <-
71incremental = false
72lto = 'fat'
73opt-level = 3 # <-
74overflow-checks = false # <-
diff --git a/examples/stm32h7b0/build.rs b/examples/stm32h7b0/build.rs
new file mode 100644
index 000000000..30691aa97
--- /dev/null
+++ b/examples/stm32h7b0/build.rs
@@ -0,0 +1,35 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
35}
diff --git a/examples/stm32h7b0/memory.x b/examples/stm32h7b0/memory.x
new file mode 100644
index 000000000..6eb1bb7c1
--- /dev/null
+++ b/examples/stm32h7b0/memory.x
@@ -0,0 +1,5 @@
1MEMORY
2{
3 FLASH : ORIGIN = 0x08000000, LENGTH = 128K /* BANK_1 */
4 RAM : ORIGIN = 0x24000000, LENGTH = 512K /* SRAM */
5} \ No newline at end of file
diff --git a/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs b/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs
new file mode 100644
index 000000000..dffb740a9
--- /dev/null
+++ b/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs
@@ -0,0 +1,433 @@
1#![no_main]
2#![no_std]
3
4// Tested on weact stm32h7b0 board + w25q64 spi flash
5
6use defmt::info;
7use embassy_executor::Spawner;
8use embassy_stm32::gpio::{Level, Output, Speed};
9use embassy_stm32::mode::Blocking;
10use embassy_stm32::ospi::{
11 AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, Instance, MemorySize, MemoryType, Ospi,
12 OspiWidth, TransferConfig, WrapSize,
13};
14use embassy_stm32::time::Hertz;
15use embassy_stm32::Config;
16use embassy_time::Timer;
17use {defmt_rtt as _, panic_probe as _};
18
19#[embassy_executor::main]
20async fn main(_spawner: Spawner) {
21 // RCC config
22 let mut config = Config::default();
23 info!("START");
24 {
25 use embassy_stm32::rcc::*;
26 config.rcc.hsi = Some(HSIPrescaler::DIV1);
27 config.rcc.csi = true;
28 // Needed for USB
29 config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true });
30 // External oscillator 25MHZ
31 config.rcc.hse = Some(Hse {
32 freq: Hertz(25_000_000),
33 mode: HseMode::Oscillator,
34 });
35 config.rcc.pll1 = Some(Pll {
36 source: PllSource::HSE,
37 prediv: PllPreDiv::DIV5,
38 mul: PllMul::MUL112,
39 divp: Some(PllDiv::DIV2),
40 divq: Some(PllDiv::DIV2),
41 divr: Some(PllDiv::DIV2),
42 });
43 config.rcc.sys = Sysclk::PLL1_P;
44 config.rcc.ahb_pre = AHBPrescaler::DIV2;
45 config.rcc.apb1_pre = APBPrescaler::DIV2;
46 config.rcc.apb2_pre = APBPrescaler::DIV2;
47 config.rcc.apb3_pre = APBPrescaler::DIV2;
48 config.rcc.apb4_pre = APBPrescaler::DIV2;
49 config.rcc.voltage_scale = VoltageScale::Scale0;
50 }
51
52 // Initialize peripherals
53 let p = embassy_stm32::init(config);
54
55 let qspi_config = embassy_stm32::ospi::Config {
56 fifo_threshold: FIFOThresholdLevel::_16Bytes,
57 memory_type: MemoryType::Micron,
58 device_size: MemorySize::_8MiB,
59 chip_select_high_time: ChipSelectHighTime::_1Cycle,
60 free_running_clock: false,
61 clock_mode: false,
62 wrap_size: WrapSize::None,
63 clock_prescaler: 4,
64 sample_shifting: true,
65 delay_hold_quarter_cycle: false,
66 chip_select_boundary: 0,
67 delay_block_bypass: true,
68 max_transfer: 0,
69 refresh: 0,
70 };
71 let ospi = embassy_stm32::ospi::Ospi::new_blocking_quadspi(
72 p.OCTOSPI1,
73 p.PB2,
74 p.PD11,
75 p.PD12,
76 p.PE2,
77 p.PD13,
78 p.PB6,
79 qspi_config,
80 );
81
82 let mut flash = FlashMemory::new(ospi).await;
83
84 let flash_id = flash.read_id();
85 info!("FLASH ID: {=[u8]:x}", flash_id);
86 let mut wr_buf = [0u8; 8];
87 for i in 0..8 {
88 wr_buf[i] = i as u8;
89 }
90 let mut rd_buf = [0u8; 8];
91 flash.erase_sector(0).await;
92 flash.write_memory(0, &wr_buf, true).await;
93 flash.read_memory(0, &mut rd_buf, true);
94 info!("WRITE BUF: {=[u8]:#X}", wr_buf);
95 info!("READ BUF: {=[u8]:#X}", rd_buf);
96 flash.enable_mm().await;
97 info!("Enabled memory mapped mode");
98
99 let first_u32 = unsafe { *(0x90000000 as *const u32) };
100 assert_eq!(first_u32, 0x03020100);
101
102 let second_u32 = unsafe { *(0x90000004 as *const u32) };
103 assert_eq!(second_u32, 0x07060504);
104 flash.disable_mm().await;
105
106 info!("DONE");
107 // Output pin PE3
108 let mut led = Output::new(p.PE3, Level::Low, Speed::Low);
109
110 loop {
111 led.toggle();
112 Timer::after_millis(1000).await;
113 }
114}
115
116const MEMORY_PAGE_SIZE: usize = 8;
117
118const CMD_QUAD_READ: u8 = 0x6B;
119
120const CMD_QUAD_WRITE_PG: u8 = 0x32;
121
122const CMD_READ_ID: u8 = 0x9F;
123
124const CMD_ENABLE_RESET: u8 = 0x66;
125const CMD_RESET: u8 = 0x99;
126
127const CMD_WRITE_ENABLE: u8 = 0x06;
128
129const CMD_CHIP_ERASE: u8 = 0xC7;
130const CMD_SECTOR_ERASE: u8 = 0x20;
131const CMD_BLOCK_ERASE_32K: u8 = 0x52;
132const CMD_BLOCK_ERASE_64K: u8 = 0xD8;
133
134const CMD_READ_SR: u8 = 0x05;
135const CMD_READ_CR: u8 = 0x35;
136
137const CMD_WRITE_SR: u8 = 0x01;
138const CMD_WRITE_CR: u8 = 0x31;
139
140/// Implementation of access to flash chip.
141/// Chip commands are hardcoded as it depends on used chip.
142/// This implementation is using chip GD25Q64C from Giga Device
143pub struct FlashMemory<I: Instance> {
144 ospi: Ospi<'static, I, Blocking>,
145}
146
147impl<I: Instance> FlashMemory<I> {
148 pub async fn new(ospi: Ospi<'static, I, Blocking>) -> Self {
149 let mut memory = Self { ospi };
150
151 memory.reset_memory().await;
152 memory.enable_quad();
153 memory
154 }
155
156 async fn qpi_mode(&mut self) {
157 // Enter qpi mode
158 self.exec_command(0x38).await;
159
160 // Set read param
161 let transaction = TransferConfig {
162 iwidth: OspiWidth::QUAD,
163 dwidth: OspiWidth::QUAD,
164 instruction: Some(0xC0),
165 ..Default::default()
166 };
167 self.enable_write().await;
168 self.ospi.blocking_write(&[0x30_u8], transaction).unwrap();
169 self.wait_write_finish();
170 }
171
172 pub async fn disable_mm(&mut self) {
173 self.ospi.disable_memory_mapped_mode();
174 }
175
176 pub async fn enable_mm(&mut self) {
177 self.qpi_mode().await;
178
179 let read_config = TransferConfig {
180 iwidth: OspiWidth::QUAD,
181 isize: AddressSize::_8Bit,
182 adwidth: OspiWidth::QUAD,
183 adsize: AddressSize::_24bit,
184 dwidth: OspiWidth::QUAD,
185 instruction: Some(0x0B), // Fast read in QPI mode
186 dummy: DummyCycles::_8,
187 ..Default::default()
188 };
189
190 let write_config = TransferConfig {
191 iwidth: OspiWidth::SING,
192 isize: AddressSize::_8Bit,
193 adwidth: OspiWidth::SING,
194 adsize: AddressSize::_24bit,
195 dwidth: OspiWidth::QUAD,
196 instruction: Some(0x32), // Write config
197 dummy: DummyCycles::_0,
198 ..Default::default()
199 };
200 self.ospi.enable_memory_mapped_mode(read_config, write_config).unwrap();
201 }
202
203 fn enable_quad(&mut self) {
204 let cr = self.read_cr();
205 // info!("Read cr: {:x}", cr);
206 self.write_cr(cr | 0x02);
207 // info!("Read cr after writing: {:x}", cr);
208 }
209
210 pub fn disable_quad(&mut self) {
211 let cr = self.read_cr();
212 self.write_cr(cr & (!(0x02)));
213 }
214
215 async fn exec_command_4(&mut self, cmd: u8) {
216 let transaction = TransferConfig {
217 iwidth: OspiWidth::QUAD,
218 adwidth: OspiWidth::NONE,
219 // adsize: AddressSize::_24bit,
220 dwidth: OspiWidth::NONE,
221 instruction: Some(cmd as u32),
222 address: None,
223 dummy: DummyCycles::_0,
224 ..Default::default()
225 };
226 self.ospi.blocking_command(&transaction).unwrap();
227 }
228
229 async fn exec_command(&mut self, cmd: u8) {
230 let transaction = TransferConfig {
231 iwidth: OspiWidth::SING,
232 adwidth: OspiWidth::NONE,
233 // adsize: AddressSize::_24bit,
234 dwidth: OspiWidth::NONE,
235 instruction: Some(cmd as u32),
236 address: None,
237 dummy: DummyCycles::_0,
238 ..Default::default()
239 };
240 // info!("Excuting command: {:x}", transaction.instruction);
241 self.ospi.blocking_command(&transaction).unwrap();
242 }
243
244 pub async fn reset_memory(&mut self) {
245 self.exec_command_4(CMD_ENABLE_RESET).await;
246 self.exec_command_4(CMD_RESET).await;
247 self.exec_command(CMD_ENABLE_RESET).await;
248 self.exec_command(CMD_RESET).await;
249 self.wait_write_finish();
250 }
251
252 pub async fn enable_write(&mut self) {
253 self.exec_command(CMD_WRITE_ENABLE).await;
254 }
255
256 pub fn read_id(&mut self) -> [u8; 3] {
257 let mut buffer = [0; 3];
258 let transaction: TransferConfig = TransferConfig {
259 iwidth: OspiWidth::SING,
260 isize: AddressSize::_8Bit,
261 adwidth: OspiWidth::NONE,
262 // adsize: AddressSize::_24bit,
263 dwidth: OspiWidth::SING,
264 instruction: Some(CMD_READ_ID as u32),
265 ..Default::default()
266 };
267 // info!("Reading id: 0x{:X}", transaction.instruction);
268 self.ospi.blocking_read(&mut buffer, transaction).unwrap();
269 buffer
270 }
271
272 pub fn read_id_4(&mut self) -> [u8; 3] {
273 let mut buffer = [0; 3];
274 let transaction: TransferConfig = TransferConfig {
275 iwidth: OspiWidth::SING,
276 isize: AddressSize::_8Bit,
277 adwidth: OspiWidth::NONE,
278 dwidth: OspiWidth::QUAD,
279 instruction: Some(CMD_READ_ID as u32),
280 ..Default::default()
281 };
282 info!("Reading id: 0x{:X}", transaction.instruction);
283 self.ospi.blocking_read(&mut buffer, transaction).unwrap();
284 buffer
285 }
286
287 pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) {
288 let transaction = TransferConfig {
289 iwidth: OspiWidth::SING,
290 adwidth: OspiWidth::SING,
291 adsize: AddressSize::_24bit,
292 dwidth: OspiWidth::QUAD,
293 instruction: Some(CMD_QUAD_READ as u32),
294 address: Some(addr),
295 dummy: DummyCycles::_8,
296 ..Default::default()
297 };
298 if use_dma {
299 self.ospi.blocking_read(buffer, transaction).unwrap();
300 } else {
301 self.ospi.blocking_read(buffer, transaction).unwrap();
302 }
303 }
304
305 fn wait_write_finish(&mut self) {
306 while (self.read_sr() & 0x01) != 0 {}
307 }
308
309 async fn perform_erase(&mut self, addr: u32, cmd: u8) {
310 let transaction = TransferConfig {
311 iwidth: OspiWidth::SING,
312 adwidth: OspiWidth::SING,
313 adsize: AddressSize::_24bit,
314 dwidth: OspiWidth::NONE,
315 instruction: Some(cmd as u32),
316 address: Some(addr),
317 dummy: DummyCycles::_0,
318 ..Default::default()
319 };
320 self.enable_write().await;
321 self.ospi.blocking_command(&transaction).unwrap();
322 self.wait_write_finish();
323 }
324
325 pub async fn erase_sector(&mut self, addr: u32) {
326 self.perform_erase(addr, CMD_SECTOR_ERASE).await;
327 }
328
329 pub async fn erase_block_32k(&mut self, addr: u32) {
330 self.perform_erase(addr, CMD_BLOCK_ERASE_32K).await;
331 }
332
333 pub async fn erase_block_64k(&mut self, addr: u32) {
334 self.perform_erase(addr, CMD_BLOCK_ERASE_64K).await;
335 }
336
337 pub async fn erase_chip(&mut self) {
338 self.exec_command(CMD_CHIP_ERASE).await;
339 }
340
341 async fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) {
342 assert!(
343 (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32,
344 "write_page(): page write length exceeds page boundary (len = {}, addr = {:X}",
345 len,
346 addr
347 );
348
349 let transaction = TransferConfig {
350 iwidth: OspiWidth::SING,
351 adsize: AddressSize::_24bit,
352 adwidth: OspiWidth::SING,
353 dwidth: OspiWidth::QUAD,
354 instruction: Some(CMD_QUAD_WRITE_PG as u32),
355 address: Some(addr),
356 dummy: DummyCycles::_0,
357 ..Default::default()
358 };
359 self.enable_write().await;
360 if use_dma {
361 self.ospi.blocking_write(buffer, transaction).unwrap();
362 } else {
363 self.ospi.blocking_write(buffer, transaction).unwrap();
364 }
365 self.wait_write_finish();
366 }
367
368 pub async fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) {
369 let mut left = buffer.len();
370 let mut place = addr;
371 let mut chunk_start = 0;
372
373 while left > 0 {
374 let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize;
375 let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left };
376 let chunk = &buffer[chunk_start..(chunk_start + chunk_size)];
377 self.write_page(place, chunk, chunk_size, use_dma).await;
378 place += chunk_size as u32;
379 left -= chunk_size;
380 chunk_start += chunk_size;
381 }
382 }
383
384 fn read_register(&mut self, cmd: u8) -> u8 {
385 let mut buffer = [0; 1];
386 let transaction: TransferConfig = TransferConfig {
387 iwidth: OspiWidth::SING,
388 isize: AddressSize::_8Bit,
389 adwidth: OspiWidth::NONE,
390 adsize: AddressSize::_24bit,
391 dwidth: OspiWidth::SING,
392 instruction: Some(cmd as u32),
393 address: None,
394 dummy: DummyCycles::_0,
395 ..Default::default()
396 };
397 self.ospi.blocking_read(&mut buffer, transaction).unwrap();
398 // info!("Read w25q64 register: 0x{:x}", buffer[0]);
399 buffer[0]
400 }
401
402 fn write_register(&mut self, cmd: u8, value: u8) {
403 let buffer = [value; 1];
404 let transaction: TransferConfig = TransferConfig {
405 iwidth: OspiWidth::SING,
406 isize: AddressSize::_8Bit,
407 instruction: Some(cmd as u32),
408 adsize: AddressSize::_24bit,
409 adwidth: OspiWidth::NONE,
410 dwidth: OspiWidth::SING,
411 address: None,
412 dummy: DummyCycles::_0,
413 ..Default::default()
414 };
415 self.ospi.blocking_write(&buffer, transaction).unwrap();
416 }
417
418 pub fn read_sr(&mut self) -> u8 {
419 self.read_register(CMD_READ_SR)
420 }
421
422 pub fn read_cr(&mut self) -> u8 {
423 self.read_register(CMD_READ_CR)
424 }
425
426 pub fn write_sr(&mut self, value: u8) {
427 self.write_register(CMD_WRITE_SR, value);
428 }
429
430 pub fn write_cr(&mut self, value: u8) {
431 self.write_register(CMD_WRITE_CR, value);
432 }
433}
diff --git a/examples/stm32h7rs/Cargo.toml b/examples/stm32h7rs/Cargo.toml
index f97dfd722..1d957e2cc 100644
--- a/examples/stm32h7rs/Cargo.toml
+++ b/examples/stm32h7rs/Cargo.toml
@@ -7,10 +7,10 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8# Change stm32h743bi to your chip name, if necessary. 8# Change stm32h743bi to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7s3l8", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7s3l8", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } 13embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] }
14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.3.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
@@ -22,7 +22,7 @@ cortex-m-rt = "0.7.0"
22embedded-hal = "0.2.6" 22embedded-hal = "0.2.6"
23embedded-hal-1 = { package = "embedded-hal", version = "1.0" } 23embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
24embedded-hal-async = { version = "1.0" } 24embedded-hal-async = { version = "1.0" }
25embedded-nal-async = { version = "0.7.1" } 25embedded-nal-async = "0.8.0"
26embedded-io-async = { version = "0.6.1" } 26embedded-io-async = { version = "0.6.1" }
27panic-probe = { version = "0.3", features = ["print-defmt"] } 27panic-probe = { version = "0.3", features = ["print-defmt"] }
28heapless = { version = "0.8", default-features = false } 28heapless = { version = "0.8", default-features = false }
diff --git a/examples/stm32l0/.cargo/config.toml b/examples/stm32l0/.cargo/config.toml
index b050334b2..fed9cf9ce 100644
--- a/examples/stm32l0/.cargo/config.toml
+++ b/examples/stm32l0/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace your chip as listed in `probe-rs chip list` 2# replace your chip as listed in `probe-rs chip list`
3runner = "probe-rs run --chip STM32L053R8Tx" 3runner = "probe-rs run --chip STM32L073RZTx"
4 4
5[build] 5[build]
6target = "thumbv6m-none-eabi" 6target = "thumbv6m-none-eabi"
diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml
index 2577f19e0..5cc312a50 100644
--- a/examples/stm32l0/Cargo.toml
+++ b/examples/stm32l0/Cargo.toml
@@ -6,9 +6,9 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8# Change stm32l072cz to your chip name, if necessary. 8# Change stm32l072cz to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "unstable-pac", "time-driver-any", "exti", "memory-x"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l073rz", "unstable-pac", "time-driver-any", "exti", "memory-x"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13 13
14defmt = "0.3" 14defmt = "0.3"
diff --git a/examples/stm32l0/README.md b/examples/stm32l0/README.md
new file mode 100644
index 000000000..82d222027
--- /dev/null
+++ b/examples/stm32l0/README.md
@@ -0,0 +1,24 @@
1# Examples for STM32L0 family
2Run individual examples with
3```
4cargo run --bin <module-name>
5```
6for example
7```
8cargo run --bin blinky
9```
10
11## Checklist before running examples
12You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.
13
14* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L073RZ it should be `probe-rs run --chip STM32L073RZTx`. (use `probe-rs chip list` to find your chip)
15* [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for L073RZ it should be `stm32l073rz`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip.
16* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.
17* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic
18
19If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
20
21* Which example you are trying to run
22* Which chip and board you are using
23
24Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
diff --git a/examples/stm32l0/src/bin/async-tsc.rs b/examples/stm32l0/src/bin/async-tsc.rs
deleted file mode 100644
index c40b86af9..000000000
--- a/examples/stm32l0/src/bin/async-tsc.rs
+++ /dev/null
@@ -1,122 +0,0 @@
1// Example of async TSC (Touch Sensing Controller) that lights an LED when touch is detected.
2//
3// Suggested physical setup on STM32L073RZ Nucleo board:
4// - Connect a 1000pF capacitor between pin A0 and GND. This is your sampling capacitor.
5// - Connect one end of a 1K resistor to pin A1 and leave the other end loose.
6// The loose end will act as touch sensor which will register your touch.
7//
8// Troubleshooting the setup:
9// - If no touch seems to be registered, then try to disconnect the sampling capacitor from GND momentarily,
10// now the led should light up. Next try using a different value for the sampling capacitor.
11// Also experiment with increasing the values for `ct_pulse_high_length`, `ct_pulse_low_length`, `pulse_generator_prescaler`, `max_count_value` and `discharge_delay`.
12//
13// All configuration values and sampling capacitor value have been determined experimentally.
14// Suitable configuration and discharge delay values are highly dependent on the value of the sample capacitor. For example, a shorter discharge delay can be used with smaller capacitor values.
15//
16#![no_std]
17#![no_main]
18
19use defmt::*;
20use embassy_stm32::bind_interrupts;
21use embassy_stm32::gpio::{Level, Output, Speed};
22use embassy_stm32::tsc::{self, *};
23use embassy_time::Timer;
24use {defmt_rtt as _, panic_probe as _};
25
26bind_interrupts!(struct Irqs {
27 TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;
28});
29
30#[cortex_m_rt::exception]
31unsafe fn HardFault(_: &cortex_m_rt::ExceptionFrame) -> ! {
32 cortex_m::peripheral::SCB::sys_reset();
33}
34
35/// This example is written for the nucleo-stm32l073rz, with a stm32l073rz chip.
36///
37/// Make sure you check/update the following (whether you use the L073RZ or another board):
38///
39/// * [ ] Update .cargo/config.toml with the correct `probe-rs run --chip STM32L073RZTx`chip name.
40/// * [ ] Update Cargo.toml to have the correct `embassy-stm32` feature, for L073RZ it should be `stm32l073rz`.
41/// * [ ] If your board has a special clock or power configuration, make sure that it is
42/// set up appropriately.
43/// * [ ] If your board has different pin mapping, update any pin numbers or peripherals
44/// to match your schematic
45///
46/// If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
47///
48/// * Which example you are trying to run
49/// * Which chip and board you are using
50///
51/// Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
52#[embassy_executor::main]
53async fn main(_spawner: embassy_executor::Spawner) {
54 let device_config = embassy_stm32::Config::default();
55 let context = embassy_stm32::init(device_config);
56
57 let config = tsc::Config {
58 ct_pulse_high_length: ChargeTransferPulseCycle::_4,
59 ct_pulse_low_length: ChargeTransferPulseCycle::_4,
60 spread_spectrum: false,
61 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
62 spread_spectrum_prescaler: false,
63 pulse_generator_prescaler: PGPrescalerDivider::_16,
64 max_count_value: MaxCount::_255,
65 io_default_mode: false,
66 synchro_pin_polarity: false,
67 acquisition_mode: false,
68 max_count_interrupt: false,
69 channel_ios: TscIOPin::Group1Io1.into(),
70 shield_ios: 0, // no shield
71 sampling_ios: TscIOPin::Group1Io2.into(),
72 };
73
74 let mut g1: PinGroup<embassy_stm32::peripherals::TSC, G1> = PinGroup::new();
75 g1.set_io1(context.PA0, PinType::Sample);
76 g1.set_io2(context.PA1, PinType::Channel);
77
78 let mut touch_controller = tsc::Tsc::new_async(
79 context.TSC,
80 Some(g1),
81 None,
82 None,
83 None,
84 None,
85 None,
86 None,
87 None,
88 config,
89 Irqs,
90 );
91
92 // Check if TSC is ready
93 if touch_controller.get_state() != State::Ready {
94 info!("TSC not ready!");
95 loop {} // Halt execution
96 }
97 info!("TSC initialized successfully");
98
99 // LED2 on the STM32L073RZ nucleo-board (PA5)
100 let mut led = Output::new(context.PA5, Level::High, Speed::Low);
101
102 // smaller sample capacitor discharge faster and can be used with shorter delay.
103 let discharge_delay = 5; // ms
104
105 info!("Starting touch_controller interface");
106 loop {
107 touch_controller.start();
108 touch_controller.pend_for_acquisition().await;
109 touch_controller.discharge_io(true);
110 Timer::after_millis(discharge_delay).await;
111
112 let grp1_status = touch_controller.group_get_status(Group::One);
113 match grp1_status {
114 GroupStatus::Complete => {
115 let group_one_val = touch_controller.group_get_value(Group::One);
116 info!("{}", group_one_val);
117 led.set_high();
118 }
119 GroupStatus::Ongoing => led.set_low(),
120 }
121 }
122}
diff --git a/examples/stm32l0/src/bin/blocking-tsc.rs b/examples/stm32l0/src/bin/blocking-tsc.rs
deleted file mode 100644
index 7e4f40946..000000000
--- a/examples/stm32l0/src/bin/blocking-tsc.rs
+++ /dev/null
@@ -1,116 +0,0 @@
1// Example of polling TSC (Touch Sensing Controller) that lights an LED when touch is detected.
2//
3// Suggested physical setup on STM32L073RZ Nucleo board:
4// - Connect a 1000pF capacitor between pin A0 and GND. This is your sampling capacitor.
5// - Connect one end of a 1K resistor to pin A1 and leave the other end loose.
6// The loose end will act as touch sensor which will register your touch.
7//
8// Troubleshooting the setup:
9// - If no touch seems to be registered, then try to disconnect the sampling capacitor from GND momentarily,
10// now the led should light up. Next try using a different value for the sampling capacitor.
11// Also experiment with increasing the values for `ct_pulse_high_length`, `ct_pulse_low_length`, `pulse_generator_prescaler`, `max_count_value` and `discharge_delay`.
12//
13// All configuration values and sampling capacitor value have been determined experimentally.
14// Suitable configuration and discharge delay values are highly dependent on the value of the sample capacitor. For example, a shorter discharge delay can be used with smaller capacitor values.
15//
16#![no_std]
17#![no_main]
18
19use defmt::*;
20use embassy_stm32::gpio::{Level, Output, Speed};
21use embassy_stm32::tsc::{self, *};
22use embassy_time::Timer;
23use {defmt_rtt as _, panic_probe as _};
24
25/// This example is written for the nucleo-stm32l073rz, with a stm32l073rz chip.
26///
27/// Make sure you check/update the following (whether you use the L073RZ or another board):
28///
29/// * [ ] Update .cargo/config.toml with the correct `probe-rs run --chip STM32L073RZTx`chip name.
30/// * [ ] Update Cargo.toml to have the correct `embassy-stm32` feature, for L073RZ it should be `stm32l073rz`.
31/// * [ ] If your board has a special clock or power configuration, make sure that it is
32/// set up appropriately.
33/// * [ ] If your board has different pin mapping, update any pin numbers or peripherals
34/// to match your schematic
35///
36/// If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
37///
38/// * Which example you are trying to run
39/// * Which chip and board you are using
40///
41/// Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
42#[embassy_executor::main]
43async fn main(_spawner: embassy_executor::Spawner) {
44 let device_config = embassy_stm32::Config::default();
45 let context = embassy_stm32::init(device_config);
46
47 let tsc_conf = Config {
48 ct_pulse_high_length: ChargeTransferPulseCycle::_4,
49 ct_pulse_low_length: ChargeTransferPulseCycle::_4,
50 spread_spectrum: false,
51 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
52 spread_spectrum_prescaler: false,
53 pulse_generator_prescaler: PGPrescalerDivider::_16,
54 max_count_value: MaxCount::_255,
55 io_default_mode: false,
56 synchro_pin_polarity: false,
57 acquisition_mode: false,
58 max_count_interrupt: false,
59 channel_ios: TscIOPin::Group1Io1.into(),
60 shield_ios: 0, // no shield
61 sampling_ios: TscIOPin::Group1Io2.into(),
62 };
63
64 let mut g1: PinGroup<embassy_stm32::peripherals::TSC, G1> = PinGroup::new();
65 g1.set_io1(context.PA0, PinType::Sample);
66 g1.set_io2(context.PA1, PinType::Channel);
67
68 let mut touch_controller = tsc::Tsc::new_blocking(
69 context.TSC,
70 Some(g1),
71 None,
72 None,
73 None,
74 None,
75 None,
76 None,
77 None,
78 tsc_conf,
79 );
80
81 // Check if TSC is ready
82 if touch_controller.get_state() != State::Ready {
83 info!("TSC not ready!");
84 loop {} // Halt execution
85 }
86 info!("TSC initialized successfully");
87
88 // LED2 on the STM32L073RZ nucleo-board (PA5)
89 let mut led = Output::new(context.PA5, Level::High, Speed::Low);
90
91 // smaller sample capacitor discharge faster and can be used with shorter delay.
92 let discharge_delay = 5; // ms
93
94 // the interval at which the loop polls for new touch sensor values
95 let polling_interval = 100; // ms
96
97 info!("polling for touch");
98 loop {
99 touch_controller.start();
100 touch_controller.poll_for_acquisition();
101 touch_controller.discharge_io(true);
102 Timer::after_millis(discharge_delay).await;
103
104 let grp1_status = touch_controller.group_get_status(Group::One);
105 match grp1_status {
106 GroupStatus::Complete => {
107 let group_one_val = touch_controller.group_get_value(Group::One);
108 info!("{}", group_one_val);
109 led.set_high();
110 }
111 GroupStatus::Ongoing => led.set_low(),
112 }
113
114 Timer::after_millis(polling_interval).await;
115 }
116}
diff --git a/examples/stm32l0/src/bin/tsc_async.rs b/examples/stm32l0/src/bin/tsc_async.rs
new file mode 100644
index 000000000..dae351c2e
--- /dev/null
+++ b/examples/stm32l0/src/bin/tsc_async.rs
@@ -0,0 +1,116 @@
1// Example of async TSC (Touch Sensing Controller) that lights an LED when touch is detected.
2//
3// This example demonstrates:
4// 1. Configuring a single TSC channel pin
5// 2. Using the blocking TSC interface with polling
6// 3. Waiting for acquisition completion using `poll_for_acquisition`
7// 4. Reading touch values and controlling an LED based on the results
8//
9// Suggested physical setup on STM32L073RZ Nucleo board:
10// - Connect a 1000pF capacitor between pin PA0 and GND. This is your sampling capacitor.
11// - Connect one end of a 1K resistor to pin PA1 and leave the other end loose.
12// The loose end will act as the touch sensor which will register your touch.
13//
14// The example uses two pins from Group 1 of the TSC on the STM32L073RZ Nucleo board:
15// - PA0 as the sampling capacitor, TSC group 1 IO1 (label A0)
16// - PA1 as the channel pin, TSC group 1 IO2 (label A1)
17//
18// The program continuously reads the touch sensor value:
19// - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value.
20// - The LED is turned on when touch is detected (sensor value < 25).
21// - Touch values are logged to the console.
22//
23// Troubleshooting:
24// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value.
25// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length,
26// pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.
27//
28// Note: Configuration values and sampling capacitor value have been determined experimentally.
29// Optimal values may vary based on your specific hardware setup.
30// Pins have been chosen for their convenient locations on the STM32L073RZ board. Refer to the
31// official relevant STM32 datasheets and nucleo-board user manuals to find suitable
32// alternative pins.
33//
34// Beware for STM32L073RZ nucleo-board, that PA2 and PA3 is used for the uart connection to
35// the programmer chip. If you try to use these two pins for TSC, you will get strange
36// readings, unless you somehow reconfigure/re-wire your nucleo-board.
37// No errors or warnings will be emitted, they will just silently not work as expected.
38// (see nucleo user manual UM1724, Rev 14, page 25)
39
40#![no_std]
41#![no_main]
42
43use defmt::*;
44use embassy_stm32::gpio::{Level, Output, Speed};
45use embassy_stm32::tsc::{self, *};
46use embassy_stm32::{bind_interrupts, peripherals};
47use embassy_time::Timer;
48use {defmt_rtt as _, panic_probe as _};
49
50bind_interrupts!(struct Irqs {
51 TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;
52});
53const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup
54
55#[embassy_executor::main]
56async fn main(_spawner: embassy_executor::Spawner) {
57 let device_config = embassy_stm32::Config::default();
58 let context = embassy_stm32::init(device_config);
59
60 let mut pin_group: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();
61 pin_group.set_io1::<tsc::pin_roles::Sample>(context.PA0);
62 let sensor = pin_group.set_io2::<tsc::pin_roles::Channel>(context.PA1);
63
64 let tsc_conf = Config {
65 ct_pulse_high_length: ChargeTransferPulseCycle::_4,
66 ct_pulse_low_length: ChargeTransferPulseCycle::_4,
67 spread_spectrum: false,
68 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
69 spread_spectrum_prescaler: false,
70 pulse_generator_prescaler: PGPrescalerDivider::_16,
71 max_count_value: MaxCount::_255,
72 io_default_mode: false,
73 synchro_pin_polarity: false,
74 acquisition_mode: false,
75 max_count_interrupt: false,
76 };
77
78 let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
79 g1: Some(pin_group.pin_group),
80 ..Default::default()
81 };
82
83 let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, tsc_conf, Irqs).unwrap();
84
85 // Check if TSC is ready
86 if touch_controller.get_state() != State::Ready {
87 info!("TSC not ready!");
88 return;
89 }
90 info!("TSC initialized successfully");
91
92 // LED2 on the STM32L073RZ nucleo-board (PA5)
93 let mut led = Output::new(context.PA5, Level::Low, Speed::Low);
94
95 let discharge_delay = 5; // ms
96
97 info!("Starting touch_controller interface");
98 loop {
99 touch_controller.set_active_channels_mask(sensor.pin.into());
100 touch_controller.start();
101 touch_controller.pend_for_acquisition().await;
102 touch_controller.discharge_io(true);
103 Timer::after_millis(discharge_delay).await;
104
105 let group_val = touch_controller.group_get_value(sensor.pin.group());
106 info!("Touch value: {}", group_val);
107
108 if group_val < SENSOR_THRESHOLD {
109 led.set_high();
110 } else {
111 led.set_low();
112 }
113
114 Timer::after_millis(100).await;
115 }
116}
diff --git a/examples/stm32l0/src/bin/tsc_blocking.rs b/examples/stm32l0/src/bin/tsc_blocking.rs
new file mode 100644
index 000000000..e1f24639b
--- /dev/null
+++ b/examples/stm32l0/src/bin/tsc_blocking.rs
@@ -0,0 +1,142 @@
1// Example of blocking TSC (Touch Sensing Controller) that lights an LED when touch is detected.
2//
3// This example demonstrates:
4// 1. Configuring a single TSC channel pin
5// 2. Using the blocking TSC interface with polling
6// 3. Waiting for acquisition completion using `poll_for_acquisition`
7// 4. Reading touch values and controlling an LED based on the results
8//
9// Suggested physical setup on STM32L073RZ Nucleo board:
10// - Connect a 1000pF capacitor between pin PA0 and GND. This is your sampling capacitor.
11// - Connect one end of a 1K resistor to pin PA1 and leave the other end loose.
12// The loose end will act as the touch sensor which will register your touch.
13//
14// The example uses two pins from Group 1 of the TSC on the STM32L073RZ Nucleo board:
15// - PA0 as the sampling capacitor, TSC group 1 IO1 (label A0)
16// - PA1 as the channel pin, TSC group 1 IO2 (label A1)
17//
18// The program continuously reads the touch sensor value:
19// - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value.
20// - The LED is turned on when touch is detected (sensor value < 25).
21// - Touch values are logged to the console.
22//
23// Troubleshooting:
24// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value.
25// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length,
26// pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.
27//
28// Note: Configuration values and sampling capacitor value have been determined experimentally.
29// Optimal values may vary based on your specific hardware setup.
30// Pins have been chosen for their convenient locations on the STM32L073RZ board. Refer to the
31// official relevant STM32 datasheets and nucleo-board user manuals to find suitable
32// alternative pins.
33//
34// Beware for STM32L073RZ nucleo-board, that PA2 and PA3 is used for the uart connection to
35// the programmer chip. If you try to use these two pins for TSC, you will get strange
36// readings, unless you somehow reconfigure/re-wire your nucleo-board.
37// No errors or warnings will be emitted, they will just silently not work as expected.
38// (see nucleo user manual UM1724, Rev 14, page 25)
39
40#![no_std]
41#![no_main]
42
43use defmt::*;
44use embassy_stm32::gpio::{Level, Output, Speed};
45use embassy_stm32::tsc::{self, *};
46use embassy_stm32::{mode, peripherals};
47use embassy_time::Timer;
48use {defmt_rtt as _, panic_probe as _};
49
50const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup
51
52#[embassy_executor::main]
53async fn main(_spawner: embassy_executor::Spawner) {
54 let device_config = embassy_stm32::Config::default();
55 let context = embassy_stm32::init(device_config);
56
57 let tsc_conf = Config {
58 ct_pulse_high_length: ChargeTransferPulseCycle::_4,
59 ct_pulse_low_length: ChargeTransferPulseCycle::_4,
60 spread_spectrum: false,
61 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
62 spread_spectrum_prescaler: false,
63 pulse_generator_prescaler: PGPrescalerDivider::_16,
64 max_count_value: MaxCount::_255,
65 io_default_mode: false,
66 synchro_pin_polarity: false,
67 acquisition_mode: false,
68 max_count_interrupt: false,
69 };
70
71 let mut g1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();
72 g1.set_io1::<tsc::pin_roles::Sample>(context.PA0);
73 let tsc_sensor = g1.set_io2::<tsc::pin_roles::Channel>(context.PA1);
74
75 let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
76 g1: Some(g1.pin_group),
77 ..Default::default()
78 };
79
80 let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, tsc_conf).unwrap();
81
82 // Check if TSC is ready
83 if touch_controller.get_state() != State::Ready {
84 crate::panic!("TSC not ready!");
85 }
86 info!("TSC initialized successfully");
87
88 // LED2 on the STM32L073RZ nucleo-board (PA5)
89 let mut led = Output::new(context.PA5, Level::High, Speed::Low);
90
91 // smaller sample capacitor discharge faster and can be used with shorter delay.
92 let discharge_delay = 5; // ms
93
94 // the interval at which the loop polls for new touch sensor values
95 let polling_interval = 100; // ms
96
97 info!("polling for touch");
98 loop {
99 touch_controller.set_active_channels_mask(tsc_sensor.pin.into());
100 touch_controller.start();
101 touch_controller.poll_for_acquisition();
102 touch_controller.discharge_io(true);
103 Timer::after_millis(discharge_delay).await;
104
105 match read_touch_value(&mut touch_controller, tsc_sensor.pin).await {
106 Some(v) => {
107 info!("sensor value {}", v);
108 if v < SENSOR_THRESHOLD {
109 led.set_high();
110 } else {
111 led.set_low();
112 }
113 }
114 None => led.set_low(),
115 }
116
117 Timer::after_millis(polling_interval).await;
118 }
119}
120
121const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10;
122
123// attempt to read group status and delay when still ongoing
124async fn read_touch_value(
125 touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>,
126 sensor_pin: tsc::IOPin,
127) -> Option<u16> {
128 for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS {
129 match touch_controller.group_get_status(sensor_pin.group()) {
130 GroupStatus::Complete => {
131 return Some(touch_controller.group_get_value(sensor_pin.group()));
132 }
133 GroupStatus::Ongoing => {
134 // if you end up here a lot, then you prob need to increase discharge_delay
135 // or consider changing the code to adjust the discharge_delay dynamically
136 info!("Acquisition still ongoing");
137 Timer::after_millis(1).await;
138 }
139 }
140 }
141 None
142}
diff --git a/examples/stm32l0/src/bin/tsc_multipin.rs b/examples/stm32l0/src/bin/tsc_multipin.rs
new file mode 100644
index 000000000..6343de141
--- /dev/null
+++ b/examples/stm32l0/src/bin/tsc_multipin.rs
@@ -0,0 +1,209 @@
1// Example of TSC (Touch Sensing Controller) using multiple pins from the same tsc-group.
2//
3// What is special about using multiple TSC pins as sensor channels from the same TSC group,
4// is that only one TSC pin for each TSC group can be acquired and read at the time.
5// To control which channel pins are acquired and read, we must write a mask before initiating an
6// acquisition. To help manage and abstract all this business away, we can organize our channel
7// pins into acquisition banks. Each acquisition bank can contain exactly one channel pin per TSC
8// group and it will contain the relevant mask.
9//
10// This example demonstrates how to:
11// 1. Configure multiple channel pins within a single TSC group
12// 2. Use the set_active_channels_bank method to switch between sets of different channels (acquisition banks)
13// 3. Read and interpret touch values from multiple channels in the same group
14//
15// Suggested physical setup on STM32L073RZ Nucleo board:
16// - Connect a 1000pF capacitor between pin PA0 (label A0) and GND. This is the sampling capacitor for TSC
17// group 1.
18// - Connect one end of a 1K resistor to pin PA1 (label A1) and leave the other end loose.
19// The loose end will act as a touch sensor.
20//
21// - Connect a 1000pF capacitor between pin PB3 (label D3) and GND. This is the sampling capacitor for TSC
22// group 5.
23// - Connect one end of another 1K resistor to pin PB4 and leave the other end loose.
24// The loose end will act as a touch sensor.
25// - Connect one end of another 1K resistor to pin PB6 and leave the other end loose.
26// The loose end will act as a touch sensor.
27//
28// The example uses pins from two TSC groups.
29// - PA0 as sampling capacitor, TSC group 1 IO1 (label A0)
30// - PA1 as channel, TSC group 1 IO2 (label A1)
31// - PB3 as sampling capacitor, TSC group 5 IO1 (label D3)
32// - PB4 as channel, TSC group 5 IO2 (label D10)
33// - PB6 as channel, TSC group 5 IO3 (label D5)
34//
35// The pins have been chosen to make it easy to simply add capacitors directly onto the board and
36// connect one leg to GND, and to easily add resistors to the board with no special connectors,
37// breadboards, special wires or soldering required. All you need is the capacitors and resistors.
38//
39// The program reads the designated channel pins and adjusts the LED blinking
40// pattern based on which sensor(s) are touched:
41// - No touch: LED off
42// - one sensor touched: Slow blinking
43// - two sensors touched: Fast blinking
44// - three sensors touched: LED constantly on
45//
46// Troubleshooting:
47// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value.
48// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length,
49// pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.
50//
51// Note: Configuration values and sampling capacitor value have been determined experimentally.
52// Optimal values may vary based on your specific hardware setup.
53// Pins have been chosen for their convenient locations on the STM32L073RZ board. Refer to the
54// official relevant STM32 datasheets and nucleo-board user manuals to find suitable
55// alternative pins.
56//
57// Beware for STM32L073RZ nucleo-board, that PA2 and PA3 is used for the uart connection to
58// the programmer chip. If you try to use these two pins for TSC, you will get strange
59// readings, unless you somehow reconfigure/re-wire your nucleo-board.
60// No errors or warnings will be emitted, they will just silently not work as expected.
61// (see nucleo user manual UM1724, Rev 14, page 25)
62
63#![no_std]
64#![no_main]
65
66use defmt::*;
67use embassy_stm32::gpio::{Level, Output, Speed};
68use embassy_stm32::tsc::{self, *};
69use embassy_stm32::{bind_interrupts, mode, peripherals};
70use embassy_time::Timer;
71use {defmt_rtt as _, panic_probe as _};
72
73bind_interrupts!(struct Irqs {
74 TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;
75});
76
77const SENSOR_THRESHOLD: u16 = 35;
78
79async fn acquire_sensors(
80 touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Async>,
81 tsc_acquisition_bank: &AcquisitionBank,
82) {
83 touch_controller.set_active_channels_bank(tsc_acquisition_bank);
84 touch_controller.start();
85 touch_controller.pend_for_acquisition().await;
86 touch_controller.discharge_io(true);
87 let discharge_delay = 5; // ms
88 Timer::after_millis(discharge_delay).await;
89}
90
91#[embassy_executor::main]
92async fn main(_spawner: embassy_executor::Spawner) {
93 let device_config = embassy_stm32::Config::default();
94 let context = embassy_stm32::init(device_config);
95
96 // ---------- initial configuration of TSC ----------
97 let mut pin_group1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();
98 pin_group1.set_io1::<tsc::pin_roles::Sample>(context.PA0);
99 let tsc_sensor0 = pin_group1.set_io2(context.PA1);
100
101 let mut pin_group5: PinGroupWithRoles<peripherals::TSC, G5> = PinGroupWithRoles::default();
102 pin_group5.set_io1::<tsc::pin_roles::Sample>(context.PB3);
103 let tsc_sensor1 = pin_group5.set_io2(context.PB4);
104 let tsc_sensor2 = pin_group5.set_io3(context.PB6);
105
106 let config = tsc::Config {
107 ct_pulse_high_length: ChargeTransferPulseCycle::_16,
108 ct_pulse_low_length: ChargeTransferPulseCycle::_16,
109 spread_spectrum: false,
110 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
111 spread_spectrum_prescaler: false,
112 pulse_generator_prescaler: PGPrescalerDivider::_16,
113 max_count_value: MaxCount::_255,
114 io_default_mode: false,
115 synchro_pin_polarity: false,
116 acquisition_mode: false,
117 max_count_interrupt: false,
118 };
119
120 let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
121 g1: Some(pin_group1.pin_group),
122 g5: Some(pin_group5.pin_group),
123 ..Default::default()
124 };
125
126 let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap();
127
128 // ---------- setting up acquisition banks ----------
129 // sensor0 and sensor1 in this example belong to different TSC-groups,
130 // therefore we can acquire and read them both in one go.
131 let bank1 = touch_controller.create_acquisition_bank(AcquisitionBankPins {
132 g1_pin: Some(tsc_sensor0),
133 g5_pin: Some(tsc_sensor1),
134 ..Default::default()
135 });
136 // `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to
137 // acquire them one at the time. Therefore, we organize them into different acquisition banks.
138 let bank2 = touch_controller.create_acquisition_bank(AcquisitionBankPins {
139 g5_pin: Some(tsc_sensor2),
140 ..Default::default()
141 });
142
143 // Check if TSC is ready
144 if touch_controller.get_state() != State::Ready {
145 crate::panic!("TSC not ready!");
146 }
147
148 info!("TSC initialized successfully");
149
150 // LED2 on the STM32L073RZ nucleo-board (PA5)
151 let mut led = Output::new(context.PA5, Level::High, Speed::Low);
152
153 let mut led_state = false;
154
155 loop {
156 acquire_sensors(&mut touch_controller, &bank1).await;
157 let readings1 = touch_controller.get_acquisition_bank_values(&bank1);
158 acquire_sensors(&mut touch_controller, &bank2).await;
159 let readings2 = touch_controller.get_acquisition_bank_values(&bank2);
160
161 let mut touched_sensors_count = 0;
162 for reading in readings1.iter() {
163 info!("{}", reading);
164 if reading.sensor_value < SENSOR_THRESHOLD {
165 touched_sensors_count += 1;
166 }
167 }
168 for reading in readings2.iter() {
169 info!("{}", reading);
170 if reading.sensor_value < SENSOR_THRESHOLD {
171 touched_sensors_count += 1;
172 }
173 }
174
175 match touched_sensors_count {
176 0 => {
177 // No sensors touched, turn off the LED
178 led.set_low();
179 led_state = false;
180 }
181 1 => {
182 // One sensor touched, blink slowly
183 led_state = !led_state;
184 if led_state {
185 led.set_high();
186 } else {
187 led.set_low();
188 }
189 Timer::after_millis(200).await;
190 }
191 2 => {
192 // Two sensors touched, blink faster
193 led_state = !led_state;
194 if led_state {
195 led.set_high();
196 } else {
197 led.set_low();
198 }
199 Timer::after_millis(50).await;
200 }
201 3 => {
202 // All three sensors touched, LED constantly on
203 led.set_high();
204 led_state = true;
205 }
206 _ => crate::unreachable!(), // This case should never occur with 3 sensors
207 }
208 }
209}
diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml
index 062044f32..31b6785fa 100644
--- a/examples/stm32l1/Cargo.toml
+++ b/examples/stm32l1/Cargo.toml
@@ -5,8 +5,8 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 9embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
10embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] }
12embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 12embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
diff --git a/examples/stm32l1/src/bin/usart.rs b/examples/stm32l1/src/bin/usart.rs
new file mode 100644
index 000000000..dba79b8b4
--- /dev/null
+++ b/examples/stm32l1/src/bin/usart.rs
@@ -0,0 +1,37 @@
1#![no_std]
2#![no_main]
3
4use cortex_m_rt::entry;
5use defmt::*;
6use embassy_stm32::usart::{Config, Uart};
7use embassy_stm32::{bind_interrupts, peripherals, usart};
8use {defmt_rtt as _, panic_probe as _};
9
10bind_interrupts!(struct Irqs {
11 USART2 => usart::InterruptHandler<peripherals::USART2>;
12});
13
14#[entry]
15fn main() -> ! {
16 info!("Hello World!");
17
18 let p = embassy_stm32::init(Default::default());
19
20 let config = Config::default();
21 let mut usart = Uart::new_blocking(p.USART2, p.PA3, p.PA2, config).unwrap();
22 let desired_baudrate = 9600; // Default is 115200 and 9600 is used as example
23
24 match usart.set_baudrate(desired_baudrate) {
25 Ok(_) => info!("Baud rate set to {}", desired_baudrate),
26 Err(err) => error!("Error setting baudrate to {}: {}", desired_baudrate, err),
27 }
28
29 unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n"));
30 info!("wrote Hello, starting echo");
31
32 let mut buf = [0u8; 1];
33 loop {
34 unwrap!(usart.blocking_read(&mut buf));
35 unwrap!(usart.blocking_write(&buf));
36 }
37}
diff --git a/examples/stm32l4/.cargo/config.toml b/examples/stm32l4/.cargo/config.toml
index 83fc6d6f8..d71fb1517 100644
--- a/examples/stm32l4/.cargo/config.toml
+++ b/examples/stm32l4/.cargo/config.toml
@@ -2,7 +2,8 @@
2# replace STM32F429ZITx with your chip as listed in `probe-rs chip list` 2# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
3#runner = "probe-rs run --chip STM32L475VGT6" 3#runner = "probe-rs run --chip STM32L475VGT6"
4#runner = "probe-rs run --chip STM32L475VG" 4#runner = "probe-rs run --chip STM32L475VG"
5runner = "probe-rs run --chip STM32L4S5QI" 5#runner = "probe-rs run --chip STM32L4S5QI"
6runner = "probe-rs run --chip STM32L4R5ZITxP"
6 7
7[build] 8[build]
8target = "thumbv7em-none-eabi" 9target = "thumbv7em-none-eabi"
diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml
index c5478b17b..3fde18ecd 100644
--- a/examples/stm32l4/Cargo.toml
+++ b/examples/stm32l4/Cargo.toml
@@ -6,14 +6,14 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8# Change stm32l4s5vi to your chip name, if necessary. 8# Change stm32l4s5vi to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4s5qi", "memory-x", "time-driver-any", "exti", "chrono"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4r5zi", "memory-x", "time-driver-any", "exti", "chrono"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] }
13embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } 13embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" }
14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-net-adin1110 = { version = "0.2.0", path = "../../embassy-net-adin1110" } 15embassy-net-adin1110 = { version = "0.2.0", path = "../../embassy-net-adin1110" }
16embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "udp", "tcp", "dhcpv4", "medium-ethernet"] } 16embassy-net = { version = "0.5.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" }
18embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } 18embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
19embedded-io = { version = "0.6.0", features = ["defmt-03"] } 19embedded-io = { version = "0.6.0", features = ["defmt-03"] }
diff --git a/examples/stm32l4/README.md b/examples/stm32l4/README.md
new file mode 100644
index 000000000..e463c18a0
--- /dev/null
+++ b/examples/stm32l4/README.md
@@ -0,0 +1,24 @@
1# Examples for STM32L4 family
2Run individual examples with
3```
4cargo run --bin <module-name>
5```
6for example
7```
8cargo run --bin blinky
9```
10
11## Checklist before running examples
12You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.
13
14* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L4R5ZI-P it should be `probe-rs run --chip STM32L4R5ZITxP`. (use `probe-rs chip list` to find your chip)
15* [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for L4R5ZI-P it should be `stm32l4r5zi`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip.
16* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.
17* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic
18
19If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
20
21* Which example you are trying to run
22* Which chip and board you are using
23
24Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs
index be4270ada..4a7c01f9f 100644
--- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs
+++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs
@@ -51,7 +51,7 @@ bind_interrupts!(struct Irqs {
51// MAC-address used by the adin1110 51// MAC-address used by the adin1110
52const MAC: [u8; 6] = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]; 52const MAC: [u8; 6] = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff];
53// Static IP settings 53// Static IP settings
54const IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address([192, 168, 1, 5]), 24); 54const IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 5), 24);
55// Listen port for the webserver 55// Listen port for the webserver
56const HTTP_LISTEN_PORT: u16 = 80; 56const HTTP_LISTEN_PORT: u16 = 80;
57 57
diff --git a/examples/stm32l4/src/bin/tsc_async.rs b/examples/stm32l4/src/bin/tsc_async.rs
new file mode 100644
index 000000000..b9a059e2e
--- /dev/null
+++ b/examples/stm32l4/src/bin/tsc_async.rs
@@ -0,0 +1,108 @@
1// Example of async TSC (Touch Sensing Controller) that lights an LED when touch is detected.
2//
3// This example demonstrates:
4// 1. Configuring a single TSC channel pin
5// 2. Using the async TSC interface
6// 3. Waiting for acquisition completion using `pend_for_acquisition`
7// 4. Reading touch values and controlling an LED based on the results
8//
9// Suggested physical setup on STM32L4R5ZI-P board:
10// - Connect a 1000pF capacitor between pin PB4 (D25) and GND. This is your sampling capacitor.
11// - Connect one end of a 1K resistor to pin PB5 (D21) and leave the other end loose.
12// The loose end will act as the touch sensor which will register your touch.
13//
14// The example uses two pins from Group 2 of the TSC:
15// - PB4 (D25) as the sampling capacitor, TSC group 2 IO1
16// - PB5 (D21) as the channel pin, TSC group 2 IO2
17//
18// The program continuously reads the touch sensor value:
19// - It starts acquisition, waits for completion using `pend_for_acquisition`, and reads the value.
20// - The LED (connected to PB14) is turned on when touch is detected (sensor value < SENSOR_THRESHOLD).
21// - Touch values are logged to the console.
22//
23// Troubleshooting:
24// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value.
25// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length,
26// pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.
27//
28// Note: Configuration values and sampling capacitor value have been determined experimentally.
29// Optimal values may vary based on your specific hardware setup.
30
31#![no_std]
32#![no_main]
33
34use defmt::*;
35use embassy_stm32::gpio::{Level, Output, Speed};
36use embassy_stm32::tsc::{self, *};
37use embassy_stm32::{bind_interrupts, peripherals};
38use embassy_time::Timer;
39use {defmt_rtt as _, panic_probe as _};
40
41bind_interrupts!(struct Irqs {
42 TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;
43});
44const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup
45
46#[embassy_executor::main]
47async fn main(_spawner: embassy_executor::Spawner) {
48 let device_config = embassy_stm32::Config::default();
49 let context = embassy_stm32::init(device_config);
50
51 let mut pin_group: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default();
52 // D25
53 pin_group.set_io1::<tsc::pin_roles::Sample>(context.PB4);
54 // D21
55 let tsc_sensor = pin_group.set_io2::<tsc::pin_roles::Channel>(context.PB5);
56
57 let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
58 g2: Some(pin_group.pin_group),
59 ..Default::default()
60 };
61
62 let tsc_conf = Config {
63 ct_pulse_high_length: ChargeTransferPulseCycle::_4,
64 ct_pulse_low_length: ChargeTransferPulseCycle::_4,
65 spread_spectrum: false,
66 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
67 spread_spectrum_prescaler: false,
68 pulse_generator_prescaler: PGPrescalerDivider::_16,
69 max_count_value: MaxCount::_255,
70 io_default_mode: false,
71 synchro_pin_polarity: false,
72 acquisition_mode: false,
73 max_count_interrupt: false,
74 };
75
76 let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, tsc_conf, Irqs).unwrap();
77
78 // Check if TSC is ready
79 if touch_controller.get_state() != State::Ready {
80 info!("TSC not ready!");
81 return;
82 }
83 info!("TSC initialized successfully");
84
85 let mut led = Output::new(context.PB14, Level::High, Speed::Low);
86
87 let discharge_delay = 1; // ms
88
89 info!("Starting touch_controller interface");
90 loop {
91 touch_controller.set_active_channels_mask(tsc_sensor.pin.into());
92 touch_controller.start();
93 touch_controller.pend_for_acquisition().await;
94 touch_controller.discharge_io(true);
95 Timer::after_millis(discharge_delay).await;
96
97 let group_val = touch_controller.group_get_value(tsc_sensor.pin.group());
98 info!("Touch value: {}", group_val);
99
100 if group_val < SENSOR_THRESHOLD {
101 led.set_high();
102 } else {
103 led.set_low();
104 }
105
106 Timer::after_millis(100).await;
107 }
108}
diff --git a/examples/stm32l4/src/bin/tsc_blocking.rs b/examples/stm32l4/src/bin/tsc_blocking.rs
new file mode 100644
index 000000000..12084f8e2
--- /dev/null
+++ b/examples/stm32l4/src/bin/tsc_blocking.rs
@@ -0,0 +1,147 @@
1// # Example of blocking TSC (Touch Sensing Controller) that lights an LED when touch is detected
2//
3// This example demonstrates how to use the Touch Sensing Controller (TSC) in blocking mode on an STM32L4R5ZI-P board.
4//
5// ## This example demonstrates:
6//
7// 1. Configuring a single TSC channel pin
8// 2. Using the blocking TSC interface with polling
9// 3. Waiting for acquisition completion using `poll_for_acquisition`
10// 4. Reading touch values and controlling an LED based on the results
11//
12// ## Suggested physical setup on STM32L4R5ZI-P board:
13//
14// - Connect a 1000pF capacitor between pin PB4 (D25) and GND. This is your sampling capacitor.
15// - Connect one end of a 1K resistor to pin PB5 (D21) and leave the other end loose.
16// The loose end will act as the touch sensor which will register your touch.
17//
18// ## Pin Configuration:
19//
20// The example uses two pins from Group 2 of the TSC:
21// - PB4 (D25) as the sampling capacitor, TSC group 2 IO1
22// - PB5 (D21) as the channel pin, TSC group 2 IO2
23//
24// ## Program Behavior:
25//
26// The program continuously reads the touch sensor value:
27// - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value.
28// - The LED (connected to PB14) is turned on when touch is detected (sensor value < SENSOR_THRESHOLD).
29// - Touch values are logged to the console.
30//
31// ## Troubleshooting:
32//
33// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value (currently set to 25).
34// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length,
35// pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.
36// - Be aware that for some boards, there might be overlapping concerns between some pins,
37// such as UART connections for the programmer. No errors or warnings will be emitted if you
38// try to use such a pin for TSC, but you may get strange sensor readings.
39//
40// Note: Configuration values and sampling capacitor value have been determined experimentally.
41// Optimal values may vary based on your specific hardware setup. Refer to the official
42// STM32L4R5ZI-P datasheet and user manuals for more information on pin configurations and TSC functionality.
43
44#![no_std]
45#![no_main]
46
47use defmt::*;
48use embassy_stm32::gpio::{Level, Output, Speed};
49use embassy_stm32::tsc::{self, *};
50use embassy_stm32::{mode, peripherals};
51use embassy_time::Timer;
52use {defmt_rtt as _, panic_probe as _};
53
54const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup
55
56#[embassy_executor::main]
57async fn main(_spawner: embassy_executor::Spawner) {
58 let device_config = embassy_stm32::Config::default();
59 let context = embassy_stm32::init(device_config);
60
61 let tsc_conf = Config {
62 ct_pulse_high_length: ChargeTransferPulseCycle::_4,
63 ct_pulse_low_length: ChargeTransferPulseCycle::_4,
64 spread_spectrum: false,
65 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
66 spread_spectrum_prescaler: false,
67 pulse_generator_prescaler: PGPrescalerDivider::_16,
68 max_count_value: MaxCount::_255,
69 io_default_mode: false,
70 synchro_pin_polarity: false,
71 acquisition_mode: false,
72 max_count_interrupt: false,
73 };
74
75 let mut g2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default();
76 // D25
77 g2.set_io1::<tsc::pin_roles::Sample>(context.PB4);
78 // D21
79 let tsc_sensor = g2.set_io2::<tsc::pin_roles::Channel>(context.PB5);
80
81 let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
82 g2: Some(g2.pin_group),
83 ..Default::default()
84 };
85
86 let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, tsc_conf).unwrap();
87
88 // Check if TSC is ready
89 if touch_controller.get_state() != State::Ready {
90 crate::panic!("TSC not ready!");
91 }
92 info!("TSC initialized successfully");
93
94 let mut led = Output::new(context.PB14, Level::High, Speed::Low);
95
96 // smaller sample capacitor discharge faster and can be used with shorter delay.
97 let discharge_delay = 5; // ms
98
99 // the interval at which the loop polls for new touch sensor values
100 let polling_interval = 100; // ms
101
102 info!("polling for touch");
103 loop {
104 touch_controller.set_active_channels_mask(tsc_sensor.pin.into());
105 touch_controller.start();
106 touch_controller.poll_for_acquisition();
107 touch_controller.discharge_io(true);
108 Timer::after_millis(discharge_delay).await;
109
110 match read_touch_value(&mut touch_controller, tsc_sensor.pin).await {
111 Some(v) => {
112 info!("sensor value {}", v);
113 if v < SENSOR_THRESHOLD {
114 led.set_high();
115 } else {
116 led.set_low();
117 }
118 }
119 None => led.set_low(),
120 }
121
122 Timer::after_millis(polling_interval).await;
123 }
124}
125
126const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10;
127
128// attempt to read group status and delay when still ongoing
129async fn read_touch_value(
130 touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>,
131 sensor_pin: tsc::IOPin,
132) -> Option<u16> {
133 for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS {
134 match touch_controller.group_get_status(sensor_pin.group()) {
135 GroupStatus::Complete => {
136 return Some(touch_controller.group_get_value(sensor_pin.group()));
137 }
138 GroupStatus::Ongoing => {
139 // if you end up here a lot, then you prob need to increase discharge_delay
140 // or consider changing the code to adjust the discharge_delay dynamically
141 info!("Acquisition still ongoing");
142 Timer::after_millis(1).await;
143 }
144 }
145 }
146 None
147}
diff --git a/examples/stm32l4/src/bin/tsc_multipin.rs b/examples/stm32l4/src/bin/tsc_multipin.rs
new file mode 100644
index 000000000..8fec5ddc4
--- /dev/null
+++ b/examples/stm32l4/src/bin/tsc_multipin.rs
@@ -0,0 +1,198 @@
1// # Example of TSC (Touch Sensing Controller) using multiple pins from the same TSC group
2//
3// This example demonstrates how to use the Touch Sensing Controller (TSC) with multiple pins, including pins from the same TSC group, on an STM32L4R5ZI-P board.
4//
5// ## Key Concepts
6//
7// - Only one TSC pin for each TSC group can be acquired and read at a time.
8// - To control which channel pins are acquired and read, we must write a mask before initiating an acquisition.
9// - We organize channel pins into acquisition banks to manage this process efficiently.
10// - Each acquisition bank can contain exactly one channel pin per TSC group and will contain the relevant mask.
11//
12// ## This example demonstrates how to:
13//
14// 1. Configure multiple channel pins within a single TSC group
15// 2. Use the set_active_channels_bank method to switch between sets of different channels (acquisition banks)
16// 3. Read and interpret touch values from multiple channels in the same group
17//
18// ## Suggested physical setup on STM32L4R5ZI-P board:
19//
20// - Connect a 1000pF capacitor between pin PB12 (D19) and GND. This is the sampling capacitor for TSC group 1.
21// - Connect one end of a 1K resistor to pin PB13 (D18) and leave the other end loose. This will act as a touch sensor.
22// - Connect a 1000pF capacitor between pin PB4 (D25) and GND. This is the sampling capacitor for TSC group 2.
23// - Connect one end of a 1K resistor to pin PB5 (D22) and leave the other end loose. This will act as a touch sensor.
24// - Connect one end of another 1K resistor to pin PB6 (D71) and leave the other end loose. This will act as a touch sensor.
25//
26// ## Pin Configuration:
27//
28// The example uses pins from two TSC groups:
29//
30// - Group 1:
31// - PB12 (D19) as sampling capacitor (TSC group 1 IO1)
32// - PB13 (D18) as channel (TSC group 1 IO2)
33// - Group 2:
34// - PB4 (D25) as sampling capacitor (TSC group 2 IO1)
35// - PB5 (D22) as channel (TSC group 2 IO2)
36// - PB6 (D71) as channel (TSC group 2 IO3)
37//
38// The pins have been chosen for their convenient locations on the STM32L4R5ZI-P board, making it easy to add capacitors and resistors directly to the board without special connectors, breadboards, or soldering.
39//
40// ## Program Behavior:
41//
42// The program reads the designated channel pins and adjusts the LED (connected to PB14) blinking pattern based on which sensor(s) are touched:
43//
44// - No touch: LED off
45// - One sensor touched: Slow blinking
46// - Two sensors touched: Fast blinking
47// - Three sensors touched: LED constantly on
48//
49// ## Troubleshooting:
50//
51// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value (currently set to 20).
52// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.
53// - Be aware that for some boards there will be overlapping concerns between some pins, for
54// example UART connection for the programmer to the MCU and a TSC pin. No errors or warning will
55// be emitted if you try to use such a pin for TSC, but you will get strange sensor readings.
56//
57// Note: Configuration values and sampling capacitor values have been determined experimentally. Optimal values may vary based on your specific hardware setup. Refer to the official STM32L4R5ZI-P datasheet and user manuals for more information on pin configurations and TSC functionality.
58
59#![no_std]
60#![no_main]
61
62use defmt::*;
63use embassy_stm32::gpio::{Level, Output, Speed};
64use embassy_stm32::tsc::{self, *};
65use embassy_stm32::{bind_interrupts, mode, peripherals};
66use embassy_time::Timer;
67use {defmt_rtt as _, panic_probe as _};
68
69bind_interrupts!(struct Irqs {
70 TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;
71});
72
73const SENSOR_THRESHOLD: u16 = 20;
74
75async fn acquire_sensors(
76 touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Async>,
77 tsc_acquisition_bank: &AcquisitionBank,
78) {
79 touch_controller.set_active_channels_bank(tsc_acquisition_bank);
80 touch_controller.start();
81 touch_controller.pend_for_acquisition().await;
82 touch_controller.discharge_io(true);
83 let discharge_delay = 1; // ms
84 Timer::after_millis(discharge_delay).await;
85}
86
87#[embassy_executor::main]
88async fn main(_spawner: embassy_executor::Spawner) {
89 let device_config = embassy_stm32::Config::default();
90 let context = embassy_stm32::init(device_config);
91
92 // ---------- initial configuration of TSC ----------
93 let mut g1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();
94 g1.set_io1::<tsc::pin_roles::Sample>(context.PB12);
95 let sensor0 = g1.set_io2::<tsc::pin_roles::Channel>(context.PB13);
96
97 let mut g2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default();
98 g2.set_io1::<tsc::pin_roles::Sample>(context.PB4);
99 let sensor1 = g2.set_io2(context.PB5);
100 let sensor2 = g2.set_io3(context.PB6);
101
102 let config = tsc::Config {
103 ct_pulse_high_length: ChargeTransferPulseCycle::_16,
104 ct_pulse_low_length: ChargeTransferPulseCycle::_16,
105 spread_spectrum: false,
106 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
107 spread_spectrum_prescaler: false,
108 pulse_generator_prescaler: PGPrescalerDivider::_16,
109 max_count_value: MaxCount::_255,
110 io_default_mode: false,
111 synchro_pin_polarity: false,
112 acquisition_mode: false,
113 max_count_interrupt: false,
114 };
115
116 let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
117 g1: Some(g1.pin_group),
118 g2: Some(g2.pin_group),
119 ..Default::default()
120 };
121
122 let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap();
123
124 // ---------- setting up acquisition banks ----------
125 // sensor0 and sensor1 belong to different TSC-groups, therefore we can acquire and
126 // read them both in one go.
127 let bank1 = touch_controller.create_acquisition_bank(AcquisitionBankPins {
128 g1_pin: Some(sensor0),
129 g2_pin: Some(sensor1),
130 ..Default::default()
131 });
132 // `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to
133 // acquire them one at the time. We do this by organizing them into different acquisition banks.
134 let bank2 = touch_controller.create_acquisition_bank(AcquisitionBankPins {
135 g2_pin: Some(sensor2),
136 ..Default::default()
137 });
138
139 // Check if TSC is ready
140 if touch_controller.get_state() != State::Ready {
141 crate::panic!("TSC not ready!");
142 }
143
144 info!("TSC initialized successfully");
145
146 let mut led = Output::new(context.PB14, Level::High, Speed::Low);
147
148 let mut led_state = false;
149
150 loop {
151 acquire_sensors(&mut touch_controller, &bank1).await;
152 let readings1 = touch_controller.get_acquisition_bank_values(&bank1);
153 acquire_sensors(&mut touch_controller, &bank2).await;
154 let readings2 = touch_controller.get_acquisition_bank_values(&bank2);
155
156 let mut touched_sensors_count = 0;
157 for reading in readings1.iter().chain(readings2.iter()) {
158 info!("{}", reading);
159 if reading.sensor_value < SENSOR_THRESHOLD {
160 touched_sensors_count += 1;
161 }
162 }
163
164 match touched_sensors_count {
165 0 => {
166 // No sensors touched, turn off the LED
167 led.set_low();
168 led_state = false;
169 }
170 1 => {
171 // One sensor touched, blink slowly
172 led_state = !led_state;
173 if led_state {
174 led.set_high();
175 } else {
176 led.set_low();
177 }
178 Timer::after_millis(200).await;
179 }
180 2 => {
181 // Two sensors touched, blink faster
182 led_state = !led_state;
183 if led_state {
184 led.set_high();
185 } else {
186 led.set_low();
187 }
188 Timer::after_millis(50).await;
189 }
190 3 => {
191 // All three sensors touched, LED constantly on
192 led.set_high();
193 led_state = true;
194 }
195 _ => crate::unreachable!(), // This case should never occur with 3 sensors
196 }
197 }
198}
diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml
index 16c184de2..2b8a2c064 100644
--- a/examples/stm32l5/Cargo.toml
+++ b/examples/stm32l5/Cargo.toml
@@ -7,11 +7,11 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8# Change stm32l552ze to your chip name, if necessary. 8# Change stm32l552ze to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "memory-x", "low-power"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "memory-x", "low-power"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 13embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
14embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } 14embassy-net = { version = "0.5.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"
17 17
diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml
index 2e890cdb5..11953acfc 100644
--- a/examples/stm32u0/Cargo.toml
+++ b/examples/stm32u0/Cargo.toml
@@ -7,8 +7,8 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8# Change stm32u083rc to your chip name, if necessary. 8# Change stm32u083rc to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti", "chrono"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti", "chrono"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } 13embassy-usb = { version = "0.3.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" }
diff --git a/examples/stm32u5/.cargo/config.toml b/examples/stm32u5/.cargo/config.toml
index 36c5b63a6..bdbd86354 100644
--- a/examples/stm32u5/.cargo/config.toml
+++ b/examples/stm32u5/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32U585AIIx with your chip as listed in `probe-rs chip list` 2# replace STM32U5G9ZJTxQ with your chip as listed in `probe-rs chip list`
3runner = "probe-rs run --chip STM32U585AIIx" 3runner = "probe-rs run --chip STM32U5G9ZJTxQ"
4 4
5[build] 5[build]
6target = "thumbv8m.main-none-eabihf" 6target = "thumbv8m.main-none-eabihf"
diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml
index 20d64c6f7..68a17ce43 100644
--- a/examples/stm32u5/Cargo.toml
+++ b/examples/stm32u5/Cargo.toml
@@ -5,10 +5,10 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8# Change stm32u585ai to your chip name, if necessary. 8# Change stm32u5g9zj to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "unstable-pac", "stm32u5g9zj", "time-driver-any", "memory-x" ] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 13embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 14embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
@@ -21,6 +21,8 @@ cortex-m-rt = "0.7.0"
21embedded-hal = "0.2.6" 21embedded-hal = "0.2.6"
22panic-probe = { version = "0.3", features = ["print-defmt"] } 22panic-probe = { version = "0.3", features = ["print-defmt"] }
23heapless = { version = "0.8", default-features = false } 23heapless = { version = "0.8", default-features = false }
24embedded-graphics = { version = "0.8.1" }
25tinybmp = { version = "0.6.0" }
24 26
25micromath = "2.0.0" 27micromath = "2.0.0"
26 28
diff --git a/examples/stm32u5/src/bin/ferris.bmp b/examples/stm32u5/src/bin/ferris.bmp
new file mode 100644
index 000000000..7a222ab84
--- /dev/null
+++ b/examples/stm32u5/src/bin/ferris.bmp
Binary files differ
diff --git a/examples/stm32u5/src/bin/i2c.rs b/examples/stm32u5/src/bin/i2c.rs
index 19a78eac9..d5f5d6f60 100644
--- a/examples/stm32u5/src/bin/i2c.rs
+++ b/examples/stm32u5/src/bin/i2c.rs
@@ -13,7 +13,7 @@ const WHOAMI: u8 = 0x0F;
13#[embassy_executor::main] 13#[embassy_executor::main]
14async fn main(_spawner: Spawner) { 14async fn main(_spawner: Spawner) {
15 let p = embassy_stm32::init(Default::default()); 15 let p = embassy_stm32::init(Default::default());
16 let mut i2c = I2c::new_blocking(p.I2C2, p.PH4, p.PH5, Hertz(100_000), Default::default()); 16 let mut i2c = I2c::new_blocking(p.I2C2, p.PF1, p.PF0, Hertz(100_000), Default::default());
17 17
18 let mut data = [0u8; 1]; 18 let mut data = [0u8; 1];
19 unwrap!(i2c.blocking_write_read(HTS221_ADDRESS, &[WHOAMI], &mut data)); 19 unwrap!(i2c.blocking_write_read(HTS221_ADDRESS, &[WHOAMI], &mut data));
diff --git a/examples/stm32u5/src/bin/ltdc.rs b/examples/stm32u5/src/bin/ltdc.rs
new file mode 100644
index 000000000..bd59a9148
--- /dev/null
+++ b/examples/stm32u5/src/bin/ltdc.rs
@@ -0,0 +1,461 @@
1#![no_std]
2#![no_main]
3#![macro_use]
4#![allow(static_mut_refs)]
5
6/// This example was derived from examples\stm32h735\src\bin\ltdc.rs
7/// It demonstrates the LTDC lcd display peripheral and was tested on an STM32U5G9J-DK2 demo board (embassy-stm32 feature "stm32u5g9zj" and probe-rs chip "STM32U5G9ZJTxQ")
8///
9use bouncy_box::BouncyBox;
10use defmt::{info, unwrap};
11use embassy_executor::Spawner;
12use embassy_stm32::gpio::{Level, Output, Speed};
13use embassy_stm32::ltdc::{self, Ltdc, LtdcConfiguration, LtdcLayer, LtdcLayerConfig, PolarityActive, PolarityEdge};
14use embassy_stm32::{bind_interrupts, peripherals};
15use embassy_time::{Duration, Timer};
16use embedded_graphics::draw_target::DrawTarget;
17use embedded_graphics::geometry::{OriginDimensions, Point, Size};
18use embedded_graphics::image::Image;
19use embedded_graphics::pixelcolor::raw::RawU24;
20use embedded_graphics::pixelcolor::Rgb888;
21use embedded_graphics::prelude::*;
22use embedded_graphics::primitives::Rectangle;
23use embedded_graphics::Pixel;
24use heapless::{Entry, FnvIndexMap};
25use tinybmp::Bmp;
26use {defmt_rtt as _, panic_probe as _};
27
28const DISPLAY_WIDTH: usize = 800;
29const DISPLAY_HEIGHT: usize = 480;
30const MY_TASK_POOL_SIZE: usize = 2;
31
32// the following two display buffers consume 261120 bytes that just about fits into axis ram found on the mcu
33pub static mut FB1: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT];
34pub static mut FB2: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT];
35
36bind_interrupts!(struct Irqs {
37 LTDC => ltdc::InterruptHandler<peripherals::LTDC>;
38});
39
40const NUM_COLORS: usize = 256;
41
42#[embassy_executor::main]
43async fn main(spawner: Spawner) {
44 let p = rcc_setup::stm32u5g9zj_init();
45
46 // enable ICACHE
47 embassy_stm32::pac::ICACHE.cr().write(|w| {
48 w.set_en(true);
49 });
50
51 // blink the led on another task
52 let led = Output::new(p.PD2, Level::High, Speed::Low);
53 unwrap!(spawner.spawn(led_task(led)));
54
55 // numbers from STM32U5G9J-DK2.ioc
56 const RK050HR18H_HSYNC: u16 = 5; // Horizontal synchronization
57 const RK050HR18H_HBP: u16 = 8; // Horizontal back porch
58 const RK050HR18H_HFP: u16 = 8; // Horizontal front porch
59 const RK050HR18H_VSYNC: u16 = 5; // Vertical synchronization
60 const RK050HR18H_VBP: u16 = 8; // Vertical back porch
61 const RK050HR18H_VFP: u16 = 8; // Vertical front porch
62
63 // NOTE: all polarities have to be reversed with respect to the STM32U5G9J-DK2 CubeMX parametrization
64 let ltdc_config = LtdcConfiguration {
65 active_width: DISPLAY_WIDTH as _,
66 active_height: DISPLAY_HEIGHT as _,
67 h_back_porch: RK050HR18H_HBP,
68 h_front_porch: RK050HR18H_HFP,
69 v_back_porch: RK050HR18H_VBP,
70 v_front_porch: RK050HR18H_VFP,
71 h_sync: RK050HR18H_HSYNC,
72 v_sync: RK050HR18H_VSYNC,
73 h_sync_polarity: PolarityActive::ActiveHigh,
74 v_sync_polarity: PolarityActive::ActiveHigh,
75 data_enable_polarity: PolarityActive::ActiveHigh,
76 pixel_clock_polarity: PolarityEdge::RisingEdge,
77 };
78
79 info!("init ltdc");
80 let mut ltdc_de = Output::new(p.PD6, Level::Low, Speed::High);
81 let mut ltdc_disp_ctrl = Output::new(p.PE4, Level::Low, Speed::High);
82 let mut ltdc_bl_ctrl = Output::new(p.PE6, Level::Low, Speed::High);
83 let mut ltdc = Ltdc::new_with_pins(
84 p.LTDC, // PERIPHERAL
85 Irqs, // IRQS
86 p.PD3, // CLK
87 p.PE0, // HSYNC
88 p.PD13, // VSYNC
89 p.PB9, // B0
90 p.PB2, // B1
91 p.PD14, // B2
92 p.PD15, // B3
93 p.PD0, // B4
94 p.PD1, // B5
95 p.PE7, // B6
96 p.PE8, // B7
97 p.PC8, // G0
98 p.PC9, // G1
99 p.PE9, // G2
100 p.PE10, // G3
101 p.PE11, // G4
102 p.PE12, // G5
103 p.PE13, // G6
104 p.PE14, // G7
105 p.PC6, // R0
106 p.PC7, // R1
107 p.PE15, // R2
108 p.PD8, // R3
109 p.PD9, // R4
110 p.PD10, // R5
111 p.PD11, // R6
112 p.PD12, // R7
113 );
114 ltdc.init(&ltdc_config);
115 ltdc_de.set_low();
116 ltdc_bl_ctrl.set_high();
117 ltdc_disp_ctrl.set_high();
118
119 // we only need to draw on one layer for this example (not to be confused with the double buffer)
120 info!("enable bottom layer");
121 let layer_config = LtdcLayerConfig {
122 pixel_format: ltdc::PixelFormat::L8, // 1 byte per pixel
123 layer: LtdcLayer::Layer1,
124 window_x0: 0,
125 window_x1: DISPLAY_WIDTH as _,
126 window_y0: 0,
127 window_y1: DISPLAY_HEIGHT as _,
128 };
129
130 let ferris_bmp: Bmp<Rgb888> = Bmp::from_slice(include_bytes!("./ferris.bmp")).unwrap();
131 let color_map = build_color_lookup_map(&ferris_bmp);
132 let clut = build_clut(&color_map);
133
134 // enable the bottom layer with a 256 color lookup table
135 ltdc.init_layer(&layer_config, Some(&clut));
136
137 // Safety: the DoubleBuffer controls access to the statically allocated frame buffers
138 // and it is the only thing that mutates their content
139 let mut double_buffer = DoubleBuffer::new(
140 unsafe { FB1.as_mut() },
141 unsafe { FB2.as_mut() },
142 layer_config,
143 color_map,
144 );
145
146 // this allows us to perform some simple animation for every frame
147 let mut bouncy_box = BouncyBox::new(
148 ferris_bmp.bounding_box(),
149 Rectangle::new(Point::zero(), Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32)),
150 2,
151 );
152
153 loop {
154 // cpu intensive drawing to the buffer that is NOT currently being copied to the LCD screen
155 double_buffer.clear();
156 let position = bouncy_box.next_point();
157 let ferris = Image::new(&ferris_bmp, position);
158 unwrap!(ferris.draw(&mut double_buffer));
159
160 // perform async dma data transfer to the lcd screen
161 unwrap!(double_buffer.swap(&mut ltdc).await);
162 }
163}
164
165/// builds the color look-up table from all unique colors found in the bitmap. This should be a 256 color indexed bitmap to work.
166fn build_color_lookup_map(bmp: &Bmp<Rgb888>) -> FnvIndexMap<u32, u8, NUM_COLORS> {
167 let mut color_map: FnvIndexMap<u32, u8, NUM_COLORS> = heapless::FnvIndexMap::new();
168 let mut counter: u8 = 0;
169
170 // add black to position 0
171 color_map.insert(Rgb888::new(0, 0, 0).into_storage(), counter).unwrap();
172 counter += 1;
173
174 for Pixel(_point, color) in bmp.pixels() {
175 let raw = color.into_storage();
176 if let Entry::Vacant(v) = color_map.entry(raw) {
177 v.insert(counter).expect("more than 256 colors detected");
178 counter += 1;
179 }
180 }
181 color_map
182}
183
184/// builds the color look-up table from the color map provided
185fn build_clut(color_map: &FnvIndexMap<u32, u8, NUM_COLORS>) -> [ltdc::RgbColor; NUM_COLORS] {
186 let mut clut = [ltdc::RgbColor::default(); NUM_COLORS];
187 for (color, index) in color_map.iter() {
188 let color = Rgb888::from(RawU24::new(*color));
189 clut[*index as usize] = ltdc::RgbColor {
190 red: color.r(),
191 green: color.g(),
192 blue: color.b(),
193 };
194 }
195
196 clut
197}
198
199#[embassy_executor::task(pool_size = MY_TASK_POOL_SIZE)]
200async fn led_task(mut led: Output<'static>) {
201 let mut counter = 0;
202 loop {
203 info!("blink: {}", counter);
204 counter += 1;
205
206 // on
207 led.set_low();
208 Timer::after(Duration::from_millis(50)).await;
209
210 // off
211 led.set_high();
212 Timer::after(Duration::from_millis(450)).await;
213 }
214}
215
216pub type TargetPixelType = u8;
217
218// A simple double buffer
219pub struct DoubleBuffer {
220 buf0: &'static mut [TargetPixelType],
221 buf1: &'static mut [TargetPixelType],
222 is_buf0: bool,
223 layer_config: LtdcLayerConfig,
224 color_map: FnvIndexMap<u32, u8, NUM_COLORS>,
225}
226
227impl DoubleBuffer {
228 pub fn new(
229 buf0: &'static mut [TargetPixelType],
230 buf1: &'static mut [TargetPixelType],
231 layer_config: LtdcLayerConfig,
232 color_map: FnvIndexMap<u32, u8, NUM_COLORS>,
233 ) -> Self {
234 Self {
235 buf0,
236 buf1,
237 is_buf0: true,
238 layer_config,
239 color_map,
240 }
241 }
242
243 pub fn current(&mut self) -> (&FnvIndexMap<u32, u8, NUM_COLORS>, &mut [TargetPixelType]) {
244 if self.is_buf0 {
245 (&self.color_map, self.buf0)
246 } else {
247 (&self.color_map, self.buf1)
248 }
249 }
250
251 pub async fn swap<T: ltdc::Instance>(&mut self, ltdc: &mut Ltdc<'_, T>) -> Result<(), ltdc::Error> {
252 let (_, buf) = self.current();
253 let frame_buffer = buf.as_ptr();
254 self.is_buf0 = !self.is_buf0;
255 ltdc.set_buffer(self.layer_config.layer, frame_buffer as *const _).await
256 }
257
258 /// Clears the buffer
259 pub fn clear(&mut self) {
260 let (color_map, buf) = self.current();
261 let black = Rgb888::new(0, 0, 0).into_storage();
262 let color_index = color_map.get(&black).expect("no black found in the color map");
263
264 for a in buf.iter_mut() {
265 *a = *color_index; // solid black
266 }
267 }
268}
269
270// Implement DrawTarget for
271impl DrawTarget for DoubleBuffer {
272 type Color = Rgb888;
273 type Error = ();
274
275 /// Draw a pixel
276 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
277 where
278 I: IntoIterator<Item = Pixel<Self::Color>>,
279 {
280 let size = self.size();
281 let width = size.width as i32;
282 let height = size.height as i32;
283 let (color_map, buf) = self.current();
284
285 for pixel in pixels {
286 let Pixel(point, color) = pixel;
287
288 if point.x >= 0 && point.y >= 0 && point.x < width && point.y < height {
289 let index = point.y * width + point.x;
290 let raw_color = color.into_storage();
291
292 match color_map.get(&raw_color) {
293 Some(x) => {
294 buf[index as usize] = *x;
295 }
296 None => panic!("color not found in color map: {}", raw_color),
297 };
298 } else {
299 // Ignore invalid points
300 }
301 }
302
303 Ok(())
304 }
305}
306
307impl OriginDimensions for DoubleBuffer {
308 /// Return the size of the display
309 fn size(&self) -> Size {
310 Size::new(
311 (self.layer_config.window_x1 - self.layer_config.window_x0) as _,
312 (self.layer_config.window_y1 - self.layer_config.window_y0) as _,
313 )
314 }
315}
316
317mod rcc_setup {
318
319 use embassy_stm32::time::Hertz;
320 use embassy_stm32::{rcc, Config, Peripherals};
321
322 /// Sets up clocks for the stm32u5g9zj mcu
323 /// change this if you plan to use a different microcontroller
324 pub fn stm32u5g9zj_init() -> Peripherals {
325 // setup power and clocks for an STM32U5G9J-DK2 run from an external 16 Mhz external oscillator
326 let mut config = Config::default();
327 config.rcc.hse = Some(rcc::Hse {
328 freq: Hertz(16_000_000),
329 mode: rcc::HseMode::Oscillator,
330 });
331 config.rcc.pll1 = Some(rcc::Pll {
332 source: rcc::PllSource::HSE,
333 prediv: rcc::PllPreDiv::DIV1,
334 mul: rcc::PllMul::MUL10,
335 divp: None,
336 divq: None,
337 divr: Some(rcc::PllDiv::DIV1),
338 });
339 config.rcc.sys = rcc::Sysclk::PLL1_R; // 160 Mhz
340 config.rcc.pll3 = Some(rcc::Pll {
341 source: rcc::PllSource::HSE,
342 prediv: rcc::PllPreDiv::DIV4, // PLL_M
343 mul: rcc::PllMul::MUL125, // PLL_N
344 divp: None,
345 divq: None,
346 divr: Some(rcc::PllDiv::DIV20),
347 });
348 config.rcc.mux.ltdcsel = rcc::mux::Ltdcsel::PLL3_R; // 25 MHz
349 embassy_stm32::init(config)
350 }
351}
352
353mod bouncy_box {
354 use embedded_graphics::geometry::Point;
355 use embedded_graphics::primitives::Rectangle;
356
357 enum Direction {
358 DownLeft,
359 DownRight,
360 UpLeft,
361 UpRight,
362 }
363
364 pub struct BouncyBox {
365 direction: Direction,
366 child_rect: Rectangle,
367 parent_rect: Rectangle,
368 current_point: Point,
369 move_by: usize,
370 }
371
372 // This calculates the coordinates of a chile rectangle bounced around inside a parent bounded box
373 impl BouncyBox {
374 pub fn new(child_rect: Rectangle, parent_rect: Rectangle, move_by: usize) -> Self {
375 let center_box = parent_rect.center();
376 let center_img = child_rect.center();
377 let current_point = Point::new(center_box.x - center_img.x / 2, center_box.y - center_img.y / 2);
378 Self {
379 direction: Direction::DownRight,
380 child_rect,
381 parent_rect,
382 current_point,
383 move_by,
384 }
385 }
386
387 pub fn next_point(&mut self) -> Point {
388 let direction = &self.direction;
389 let img_height = self.child_rect.size.height as i32;
390 let box_height = self.parent_rect.size.height as i32;
391 let img_width = self.child_rect.size.width as i32;
392 let box_width = self.parent_rect.size.width as i32;
393 let move_by = self.move_by as i32;
394
395 match direction {
396 Direction::DownLeft => {
397 self.current_point.x -= move_by;
398 self.current_point.y += move_by;
399
400 let x_out_of_bounds = self.current_point.x < 0;
401 let y_out_of_bounds = (self.current_point.y + img_height) > box_height;
402
403 if x_out_of_bounds && y_out_of_bounds {
404 self.direction = Direction::UpRight
405 } else if x_out_of_bounds && !y_out_of_bounds {
406 self.direction = Direction::DownRight
407 } else if !x_out_of_bounds && y_out_of_bounds {
408 self.direction = Direction::UpLeft
409 }
410 }
411 Direction::DownRight => {
412 self.current_point.x += move_by;
413 self.current_point.y += move_by;
414
415 let x_out_of_bounds = (self.current_point.x + img_width) > box_width;
416 let y_out_of_bounds = (self.current_point.y + img_height) > box_height;
417
418 if x_out_of_bounds && y_out_of_bounds {
419 self.direction = Direction::UpLeft
420 } else if x_out_of_bounds && !y_out_of_bounds {
421 self.direction = Direction::DownLeft
422 } else if !x_out_of_bounds && y_out_of_bounds {
423 self.direction = Direction::UpRight
424 }
425 }
426 Direction::UpLeft => {
427 self.current_point.x -= move_by;
428 self.current_point.y -= move_by;
429
430 let x_out_of_bounds = self.current_point.x < 0;
431 let y_out_of_bounds = self.current_point.y < 0;
432
433 if x_out_of_bounds && y_out_of_bounds {
434 self.direction = Direction::DownRight
435 } else if x_out_of_bounds && !y_out_of_bounds {
436 self.direction = Direction::UpRight
437 } else if !x_out_of_bounds && y_out_of_bounds {
438 self.direction = Direction::DownLeft
439 }
440 }
441 Direction::UpRight => {
442 self.current_point.x += move_by;
443 self.current_point.y -= move_by;
444
445 let x_out_of_bounds = (self.current_point.x + img_width) > box_width;
446 let y_out_of_bounds = self.current_point.y < 0;
447
448 if x_out_of_bounds && y_out_of_bounds {
449 self.direction = Direction::DownLeft
450 } else if x_out_of_bounds && !y_out_of_bounds {
451 self.direction = Direction::UpLeft
452 } else if !x_out_of_bounds && y_out_of_bounds {
453 self.direction = Direction::DownRight
454 }
455 }
456 }
457
458 self.current_point
459 }
460 }
461}
diff --git a/examples/stm32u5/src/bin/tsc.rs b/examples/stm32u5/src/bin/tsc.rs
index eb15d275a..a85acc4c7 100644
--- a/examples/stm32u5/src/bin/tsc.rs
+++ b/examples/stm32u5/src/bin/tsc.rs
@@ -2,8 +2,8 @@
2#![no_main] 2#![no_main]
3 3
4use defmt::*; 4use defmt::*;
5use embassy_stm32::bind_interrupts;
6use embassy_stm32::tsc::{self, *}; 5use embassy_stm32::tsc::{self, *};
6use embassy_stm32::{bind_interrupts, peripherals};
7use embassy_time::Timer; 7use embassy_time::Timer;
8use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
9 9
@@ -33,63 +33,52 @@ async fn main(_spawner: embassy_executor::Spawner) {
33 synchro_pin_polarity: false, 33 synchro_pin_polarity: false,
34 acquisition_mode: false, 34 acquisition_mode: false,
35 max_count_interrupt: false, 35 max_count_interrupt: false,
36 channel_ios: TscIOPin::Group2Io2 | TscIOPin::Group7Io3,
37 shield_ios: TscIOPin::Group1Io3.into(),
38 sampling_ios: TscIOPin::Group1Io2 | TscIOPin::Group2Io1 | TscIOPin::Group7Io2,
39 }; 36 };
40 37
41 let mut g1: PinGroup<embassy_stm32::peripherals::TSC, G1> = PinGroup::new(); 38 let mut g1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();
42 g1.set_io2(context.PB13, PinType::Sample); 39 g1.set_io2::<tsc::pin_roles::Sample>(context.PB13);
43 g1.set_io3(context.PB14, PinType::Shield); 40 g1.set_io3::<tsc::pin_roles::Shield>(context.PB14);
44 41
45 let mut g2: PinGroup<embassy_stm32::peripherals::TSC, G2> = PinGroup::new(); 42 let mut g2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default();
46 g2.set_io1(context.PB4, PinType::Sample); 43 g2.set_io1::<tsc::pin_roles::Sample>(context.PB4);
47 g2.set_io2(context.PB5, PinType::Channel); 44 let sensor0 = g2.set_io2(context.PB5);
48 45
49 let mut g7: PinGroup<embassy_stm32::peripherals::TSC, G7> = PinGroup::new(); 46 let mut g7: PinGroupWithRoles<peripherals::TSC, G7> = PinGroupWithRoles::default();
50 g7.set_io2(context.PE3, PinType::Sample); 47 g7.set_io2::<tsc::pin_roles::Sample>(context.PE3);
51 g7.set_io3(context.PE4, PinType::Channel); 48 let sensor1 = g7.set_io3(context.PE4);
52 49
53 let mut touch_controller = tsc::Tsc::new_async( 50 let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
54 context.TSC, 51 g1: Some(g1.pin_group),
55 Some(g1), 52 g2: Some(g2.pin_group),
56 Some(g2), 53 g7: Some(g7.pin_group),
57 None, 54 ..Default::default()
58 None, 55 };
59 None, 56
60 None, 57 let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap();
61 Some(g7),
62 None,
63 config,
64 Irqs,
65 );
66 58
67 touch_controller.discharge_io(true); 59 let acquisition_bank = touch_controller.create_acquisition_bank(AcquisitionBankPins {
68 Timer::after_millis(1).await; 60 g2_pin: Some(sensor0),
61 g7_pin: Some(sensor1),
62 ..Default::default()
63 });
69 64
70 touch_controller.start(); 65 touch_controller.set_active_channels_bank(&acquisition_bank);
71 66
72 let mut group_two_val = 0;
73 let mut group_seven_val = 0;
74 info!("Starting touch_controller interface"); 67 info!("Starting touch_controller interface");
75 loop { 68 loop {
69 touch_controller.start();
76 touch_controller.pend_for_acquisition().await; 70 touch_controller.pend_for_acquisition().await;
77 touch_controller.discharge_io(true); 71 touch_controller.discharge_io(true);
78 Timer::after_millis(1).await; 72 Timer::after_millis(1).await;
79 73
80 if touch_controller.group_get_status(Group::Two) == GroupStatus::Complete { 74 let status = touch_controller.get_acquisition_bank_status(&acquisition_bank);
81 group_two_val = touch_controller.group_get_value(Group::Two);
82 }
83 75
84 if touch_controller.group_get_status(Group::Seven) == GroupStatus::Complete { 76 if status.all_complete() {
85 group_seven_val = touch_controller.group_get_value(Group::Seven); 77 let read_values = touch_controller.get_acquisition_bank_values(&acquisition_bank);
78 let group2_reading = read_values.get_group_reading(Group::Two).unwrap();
79 let group7_reading = read_values.get_group_reading(Group::Seven).unwrap();
80 info!("group 2 value: {}", group2_reading.sensor_value);
81 info!("group 7 value: {}", group7_reading.sensor_value);
86 } 82 }
87
88 info!(
89 "Group Two value: {}, Group Seven value: {},",
90 group_two_val, group_seven_val
91 );
92
93 touch_controller.start();
94 } 83 }
95} 84}
diff --git a/examples/stm32u5/src/bin/usb_hs_serial.rs b/examples/stm32u5/src/bin/usb_hs_serial.rs
new file mode 100644
index 000000000..5549e2cbb
--- /dev/null
+++ b/examples/stm32u5/src/bin/usb_hs_serial.rs
@@ -0,0 +1,129 @@
1#![no_std]
2#![no_main]
3
4use defmt::{panic, *};
5use defmt_rtt as _; // global logger
6use embassy_executor::Spawner;
7use embassy_futures::join::join;
8use embassy_stm32::usb::{Driver, Instance};
9use embassy_stm32::{bind_interrupts, peripherals, usb, Config};
10use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
11use embassy_usb::driver::EndpointError;
12use embassy_usb::Builder;
13use panic_probe as _;
14
15bind_interrupts!(struct Irqs {
16 OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>;
17});
18
19#[embassy_executor::main]
20async fn main(_spawner: Spawner) {
21 info!("Hello World!");
22
23 let mut config = Config::default();
24 {
25 use embassy_stm32::rcc::*;
26 use embassy_stm32::time::Hertz;
27 config.rcc.hse = Some(Hse {
28 freq: Hertz(16_000_000),
29 mode: HseMode::Oscillator,
30 });
31 config.rcc.pll1 = Some(Pll {
32 source: PllSource::HSE,
33 prediv: PllPreDiv::DIV2, // HSE / 2 = 8MHz
34 mul: PllMul::MUL60, // 8MHz * 60 = 480MHz
35 divr: Some(PllDiv::DIV3), // 480MHz / 3 = 160MHz (sys_ck)
36 divq: Some(PllDiv::DIV10), // 480MHz / 10 = 48MHz (USB)
37 divp: Some(PllDiv::DIV15), // 480MHz / 15 = 32MHz (USBOTG)
38 });
39 config.rcc.mux.otghssel = mux::Otghssel::PLL1_P;
40 config.rcc.voltage_range = VoltageScale::RANGE1;
41 config.rcc.sys = Sysclk::PLL1_R;
42 }
43
44 let p = embassy_stm32::init(config);
45
46 // Create the driver, from the HAL.
47 let mut ep_out_buffer = [0u8; 256];
48 let mut config = embassy_stm32::usb::Config::default();
49 // Do not enable vbus_detection. This is a safe default that works in all boards.
50 // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need
51 // to enable vbus_detection to comply with the USB spec. If you enable it, the board
52 // has to support it or USB won't work at all. See docs on `vbus_detection` for details.
53 config.vbus_detection = false;
54 let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
55
56 // Create embassy-usb Config
57 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
58 config.manufacturer = Some("Embassy");
59 config.product = Some("USB-serial example");
60 config.serial_number = Some("12345678");
61
62 // Required for windows compatibility.
63 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
64 config.device_class = 0xEF;
65 config.device_sub_class = 0x02;
66 config.device_protocol = 0x01;
67 config.composite_with_iads = true;
68
69 // Create embassy-usb DeviceBuilder using the driver and config.
70 // It needs some buffers for building the descriptors.
71 let mut config_descriptor = [0; 256];
72 let mut bos_descriptor = [0; 256];
73 let mut control_buf = [0; 64];
74
75 let mut state = State::new();
76
77 let mut builder = Builder::new(
78 driver,
79 config,
80 &mut config_descriptor,
81 &mut bos_descriptor,
82 &mut [], // no msos descriptors
83 &mut control_buf,
84 );
85
86 // Create classes on the builder.
87 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
88
89 // Build the builder.
90 let mut usb = builder.build();
91
92 // Run the USB device.
93 let usb_fut = usb.run();
94
95 // Do stuff with the class!
96 let echo_fut = async {
97 loop {
98 class.wait_connection().await;
99 info!("Connected");
100 let _ = echo(&mut class).await;
101 info!("Disconnected");
102 }
103 };
104
105 // Run everything concurrently.
106 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
107 join(usb_fut, echo_fut).await;
108}
109
110struct Disconnected {}
111
112impl From<EndpointError> for Disconnected {
113 fn from(val: EndpointError) -> Self {
114 match val {
115 EndpointError::BufferOverflow => panic!("Buffer overflow"),
116 EndpointError::Disabled => Disconnected {},
117 }
118 }
119}
120
121async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {
122 let mut buf = [0; 64];
123 loop {
124 let n = class.read_packet(&mut buf).await?;
125 let data = &buf[..n];
126 info!("data: {:x}", data);
127 class.write_packet(data).await?;
128 }
129}
diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs
index 4d56395da..4bb1a6079 100644
--- a/examples/stm32u5/src/bin/usb_serial.rs
+++ b/examples/stm32u5/src/bin/usb_serial.rs
@@ -13,7 +13,7 @@ use embassy_usb::Builder;
13use panic_probe as _; 13use panic_probe as _;
14 14
15bind_interrupts!(struct Irqs { 15bind_interrupts!(struct Irqs {
16 OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>; 16 OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>;
17}); 17});
18 18
19#[embassy_executor::main] 19#[embassy_executor::main]
@@ -48,7 +48,7 @@ async fn main(_spawner: Spawner) {
48 // to enable vbus_detection to comply with the USB spec. If you enable it, the board 48 // to enable vbus_detection to comply with the USB spec. If you enable it, the board
49 // has to support it or USB won't work at all. See docs on `vbus_detection` for details. 49 // has to support it or USB won't work at all. See docs on `vbus_detection` for details.
50 config.vbus_detection = false; 50 config.vbus_detection = false;
51 let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); 51 let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
52 52
53 // Create embassy-usb Config 53 // Create embassy-usb Config
54 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); 54 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml
index 1e1a0efe2..ecc72397b 100644
--- a/examples/stm32wb/Cargo.toml
+++ b/examples/stm32wb/Cargo.toml
@@ -8,10 +8,10 @@ license = "MIT OR Apache-2.0"
8# Change stm32wb55rg to your chip name in both dependencies, if necessary. 8# Change stm32wb55rg to your chip name in both dependencies, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] }
10embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } 10embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] }
11embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 11embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
12embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 12embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] }
13embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 13embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
14embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } 14embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true }
15 15
16defmt = "0.3" 16defmt = "0.3"
17defmt-rtt = "0.4" 17defmt-rtt = "0.4"
diff --git a/examples/stm32wba/Cargo.toml b/examples/stm32wba/Cargo.toml
index 401281c0b..7735dfdde 100644
--- a/examples/stm32wba/Cargo.toml
+++ b/examples/stm32wba/Cargo.toml
@@ -6,10 +6,10 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wba52cg", "time-driver-any", "memory-x", "exti"] } 8embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wba52cg", "time-driver-any", "memory-x", "exti"] }
9embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 9embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
10embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 10embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] }
11embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 11embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
12embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } 12embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true }
13 13
14defmt = "0.3" 14defmt = "0.3"
15defmt-rtt = "0.4" 15defmt-rtt = "0.4"
diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml
index 46af5218c..0182745e5 100644
--- a/examples/stm32wl/Cargo.toml
+++ b/examples/stm32wl/Cargo.toml
@@ -7,8 +7,8 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8# Change stm32wl55jc-cm4 to your chip name, if necessary. 8# Change stm32wl55jc-cm4 to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti", "chrono"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti", "chrono"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } 13embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" }
14 14
diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml
index 75de079b7..f5dcdc0a2 100644
--- a/examples/wasm/Cargo.toml
+++ b/examples/wasm/Cargo.toml
@@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0"
8crate-type = ["cdylib"] 8crate-type = ["cdylib"]
9 9
10[dependencies] 10[dependencies]
11embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["log"] } 11embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["log"] }
12embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "integrated-timers"] } 12embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log"] }
13embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "wasm", ] } 13embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "wasm", ] }
14 14
15wasm-logger = "0.2.0" 15wasm-logger = "0.2.0"