diff --git a/.github/ci/build.sh b/.github/ci/build.sh index 77d2b3cab..68a7c0c34 100755 --- a/.github/ci/build.sh +++ b/.github/ci/build.sh @@ -7,7 +7,7 @@ set -euo pipefail export RUSTUP_HOME=/ci/cache/rustup export CARGO_HOME=/ci/cache/cargo export CARGO_TARGET_DIR=/ci/cache/target -if [ -f /ci/secrets/teleprobe-token.txt ]; then +if [ -f /ci/secrets/teleprobe-token.txt ]; then echo Got teleprobe token! export TELEPROBE_HOST=https://teleprobe.embassy.dev export TELEPROBE_TOKEN=$(cat /ci/secrets/teleprobe-token.txt) diff --git a/.github/ci/test-nightly.sh b/.github/ci/test-nightly.sh index d6e5dc574..1724ffe89 100755 --- a/.github/ci/test-nightly.sh +++ b/.github/ci/test-nightly.sh @@ -11,3 +11,4 @@ mv rust-toolchain-nightly.toml rust-toolchain.toml MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml --features nightly +MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-sync/Cargo.toml diff --git a/.github/ci/test.sh b/.github/ci/test.sh index 8a58939f6..41da644fc 100755 --- a/.github/ci/test.sh +++ b/.github/ci/test.sh @@ -8,9 +8,10 @@ export RUSTUP_HOME=/ci/cache/rustup export CARGO_HOME=/ci/cache/cargo export CARGO_TARGET_DIR=/ci/cache/target -cargo test --manifest-path ./embassy-sync/Cargo.toml -cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml -cargo test --manifest-path ./embassy-hal-internal/Cargo.toml +cargo test --manifest-path ./embassy-futures/Cargo.toml +cargo test --manifest-path ./embassy-sync/Cargo.toml +cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml +cargo test --manifest-path ./embassy-hal-internal/Cargo.toml cargo test --manifest-path ./embassy-time/Cargo.toml --features generic-queue,mock-driver cargo test --manifest-path ./embassy-time-driver/Cargo.toml diff --git a/.vscode/settings.json b/.vscode/settings.json index 0c195a13b..220d25914 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,10 +15,11 @@ "rust-analyzer.cargo.target": "thumbv7em-none-eabi", //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", "rust-analyzer.cargo.features": [ - "stm32f103c8", + "stm32f446re", "time-driver-any", "unstable-pac", "exti", + "rt", ], "rust-analyzer.linkedProjects": [ // Uncomment ONE line for the chip you want to work on. diff --git a/NOTICE.md b/NOTICE.md index 868bec08f..a50b39494 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -12,5 +12,5 @@ listed source code repository logs. This program and the accompanying materials are made available under the terms of the Apache Software License 2.0 which is available at -https://www.apache.org/licenses/LICENSE-2.0, or the MIT license which is +https://www.apache.org/licenses/LICENSE-2.0, or the MIT license which is available at https://opensource.org/licenses/MIT diff --git a/README.md b/README.md index b6f667f75..0bd1ac594 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Embassy is the next-generation framework for embedded applications. Write safe, The Rust programming language is blazingly fast and memory-efficient, with no runtime, garbage collector or OS. It catches a wide variety of bugs at compile time, thanks to its full memory- and thread-safety, and expressive type system. -Rust's async/await allows for unprecedently easy and efficient multitasking in embedded systems. Tasks get transformed at compile time into state machines that get run cooperatively. It requires no dynamic memory allocation, and runs on a single stack, so no per-task stack size tuning is required. It obsoletes the need for a traditional RTOS with kernel context switching, and is faster and smaller than one! +Rust's async/await allows for unprecedentedly easy and efficient multitasking in embedded systems. Tasks get transformed at compile time into state machines that get run cooperatively. It requires no dynamic memory allocation, and runs on a single stack, so no per-task stack size tuning is required. It obsoletes the need for a traditional RTOS with kernel context switching, and is faster and smaller than one! ## Batteries included @@ -89,7 +89,7 @@ async fn main(spawner: Spawner) { ## Examples -Examples are found in the `examples/` folder seperated by the chip manufacturer they are designed to run on. For example: +Examples are found in the `examples/` folder separated by the chip manufacturer they are designed to run on. For example: * `examples/nrf52840` run on the `nrf52840-dk` board (PCA10056) but should be easily adaptable to other nRF52 chips and boards. * `examples/nrf5340` run on the `nrf5340-dk` board (PCA10095). @@ -130,8 +130,8 @@ For more help getting started, see [Getting Started][1] and [Running the Example ## Developing Embassy with Rust Analyzer based editors The [Rust Analyzer](https://rust-analyzer.github.io/) is used by [Visual Studio Code](https://code.visualstudio.com/) -and others. Given the multiple targets that Embassy serves, there is no Cargo workspace file. Instead, the Rust Analyzer -must be told of the target project to work with. In the case of Visual Studio Code, +and others. Given the multiple targets that Embassy serves, there is no Cargo workspace file. Instead, the Rust Analyzer +must be told of the target project to work with. In the case of Visual Studio Code, please refer to the `.vscode/settings.json` file's `rust-analyzer.linkedProjects`setting. ## Minimum supported Rust version (MSRV) diff --git a/ci.sh b/ci.sh index d17f4e13e..e95714b65 100755 --- a/ci.sh +++ b/ci.sh @@ -149,6 +149,9 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h503rb,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h562ag,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb35ce,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u031r8,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u073mb,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc,defmt,exti,time-driver-any,time \ --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features ''\ --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log' \ --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \ @@ -186,6 +189,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32l1 \ --- build --release --manifest-path examples/stm32l4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32l4 \ --- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32l5 \ + --- build --release --manifest-path examples/stm32u0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32u0 \ --- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \ --- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32wb \ --- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32wba \ @@ -250,6 +254,9 @@ rm out/tests/stm32f207zg/eth # doesn't work, gives "noise error", no idea why. usart_dma does pass. rm out/tests/stm32u5a5zj/usart +# flaky, perhaps bad wire +rm out/tests/stm32l152re/usart_rx_ringbuffered + if [[ -z "${TELEPROBE_TOKEN-}" ]]; then echo No teleprobe token found, skipping running HIL tests exit diff --git a/cyw43/src/control.rs b/cyw43/src/control.rs index 135b8c245..a3808f56f 100644 --- a/cyw43/src/control.rs +++ b/cyw43/src/control.rs @@ -124,8 +124,7 @@ impl<'a> Control<'a> { self.set_iovar_u32("apsta", 1).await; // read MAC addr. - let mut mac_addr = [0; 6]; - assert_eq!(self.get_iovar("cur_etheraddr", &mut mac_addr).await, 6); + let mac_addr = self.address().await; debug!("mac addr: {:02x}", Bytes(&mac_addr)); let country = countries::WORLD_WIDE_XX; @@ -574,6 +573,13 @@ impl<'a> Control<'a> { self.ioctl(IoctlType::Set, IOCTL_CMD_DISASSOC, 0, &mut []).await; info!("Disassociated") } + + /// Gets the MAC address of the device + pub async fn address(&mut self) -> [u8; 6] { + let mut mac_addr = [0; 6]; + assert_eq!(self.get_iovar("cur_etheraddr", &mut mac_addr).await, 6); + mac_addr + } } /// WiFi network scanner. diff --git a/docs/modules/ROOT/pages/faq.adoc b/docs/modules/ROOT/pages/faq.adoc index c6b893de5..0944127a5 100644 --- a/docs/modules/ROOT/pages/faq.adoc +++ b/docs/modules/ROOT/pages/faq.adoc @@ -231,3 +231,34 @@ Please refer to the STM32 documentation for the specific values suitable for you Look for the `MEMORY` section and try to determine the FLASH and RAM sizes and section start. If you find a case where the memory.x is wrong, please report it on [this Github issue](https://github.com/embassy-rs/stm32-data/issues/301) so other users are not caught by surprise. + +== Known issues (details and/or mitigations) + +These are issues that are commonly reported. Help wanted fixing them, or improving the UX when possible! + +=== STM32H5 and STM32H7 power issues + +STM32 chips with built-in power management (SMPS and LDO) settings often cause user problems when the configuration does not match how the board was designed. + +Settings from the examples, or even from other working boards, may not work on YOUR board, because they are wired differently. + +Additionally, some PWR settings require a full device reboot (and enough time to discharge any power capacitors!), making this hard to troubleshoot. Also, some +"wrong" power settings will ALMOST work, meaning it will sometimes work on some boots, or for a while, but crash unexpectedly. + +There is not a fix for this yet, as it is board/hardware dependant. See link:https://github.com/embassy-rs/embassy/issues/2806[this tracking issue] for more details + +=== STM32 BDMA only work out of some RAM regions + +The STM32 BDMA controller included in some chips (TODO: list which ones) has a limitation in that it only works out of certain regions of RAM (TODO: list which ones), otherwise the transfer +will fail. + +If you see errors that look like this: + +[source,plain] +---- +DMA: error on BDMA@1234ABCD channel 4 +---- + +You likely need to set up your linker script to define a special region for this area, and copy data to that region before using with BDMA. + +TODO: show how to do that diff --git a/docs/modules/ROOT/pages/layer_by_layer.adoc b/docs/modules/ROOT/pages/layer_by_layer.adoc index 1d7bdc89b..fa419f75e 100644 --- a/docs/modules/ROOT/pages/layer_by_layer.adoc +++ b/docs/modules/ROOT/pages/layer_by_layer.adoc @@ -63,7 +63,7 @@ Luckily, there is an elegant solution to this problem when using Embassy. == Async version -It's time to use the Embassy capabilities to its fullest. At the core, Embassy has an async excecutor, or a runtime for async tasks if you will. The executor polls a set of tasks (defined at compile time), and whenever a task `blocks`, the executor will run another task, or put the microcontroller to sleep. +It's time to use the Embassy capabilities to its fullest. At the core, Embassy has an async executor, or a runtime for async tasks if you will. The executor polls a set of tasks (defined at compile time), and whenever a task `blocks`, the executor will run another task, or put the microcontroller to sleep. [source,rust] ---- diff --git a/embassy-boot-nrf/README.md b/embassy-boot-nrf/README.md index 9dc5b0eb9..f0d87e18c 100644 --- a/embassy-boot-nrf/README.md +++ b/embassy-boot-nrf/README.md @@ -2,7 +2,7 @@ An [Embassy](https://embassy.dev) project. -An adaptation of `embassy-boot` for nRF. +An adaptation of `embassy-boot` for nRF. ## Features diff --git a/embassy-boot/README.md b/embassy-boot/README.md index 3c2d45e96..812c43524 100644 --- a/embassy-boot/README.md +++ b/embassy-boot/README.md @@ -15,7 +15,7 @@ The bootloader divides the storage into 4 main partitions, configurable when cre * BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash, but if you need to debug it and have space available, increasing this to 24kB will allow you to run the bootloader with probe-rs. * ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. The minimum size required for this partition is the size of your application. * DFU - Where the application-to-be-swapped is placed. This partition is written to by the application. This partition must be at least 1 page bigger than the ACTIVE partition. -* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. +* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. For any partition, the following preconditions are required: diff --git a/embassy-futures/src/yield_now.rs b/embassy-futures/src/yield_now.rs index bb3c67d17..4d4e535f2 100644 --- a/embassy-futures/src/yield_now.rs +++ b/embassy-futures/src/yield_now.rs @@ -9,10 +9,16 @@ use core::task::{Context, Poll}; /// hold, while still allowing other tasks to run concurrently (not monopolizing /// the executor thread). /// -/// ```rust,no_run +/// ```rust +/// # use embassy_futures::{block_on, yield_now}; +/// # async fn test_fn() { +/// # let mut iter_count: u32 = 0; +/// # let mut some_condition = || { iter_count += 1; iter_count > 10 }; /// while !some_condition() { /// yield_now().await; /// } +/// # } +/// # block_on(test_fn()); /// ``` /// /// The downside is this will spin in a busy loop, using 100% of the CPU, while diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index be9f1d784..ee7289ad8 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -16,11 +16,11 @@ categories = [ [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/" -features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp", "dhcpv4-hostname"] +features = ["defmt", "tcp", "udp", "raw", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp", "dhcpv4-hostname"] target = "thumbv7em-none-eabi" [package.metadata.docs.rs] -features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp", "dhcpv4-hostname"] +features = ["defmt", "tcp", "udp", "raw", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp", "dhcpv4-hostname"] [features] default = [] @@ -38,6 +38,8 @@ packet-trace = [] ## Enable UDP support udp = ["smoltcp/socket-udp"] +## Enable Raw support +raw = ["smoltcp/socket-raw"] ## Enable TCP support tcp = ["smoltcp/socket-tcp"] ## Enable DNS support diff --git a/embassy-net/README.md b/embassy-net/README.md index 94aa6f550..ce59ea34a 100644 --- a/embassy-net/README.md +++ b/embassy-net/README.md @@ -13,8 +13,8 @@ memory management designed to work well for embedded systems, aiming for a more - TCP, UDP, DNS, DHCPv4, IGMPv4 - TCP sockets implement the `embedded-io` async traits. -See the [`smoltcp`](https://github.com/smoltcp-rs/smoltcp) README for a detailed list of implemented and -unimplemented features of the network protocols. +See the [`smoltcp`](https://github.com/smoltcp-rs/smoltcp) README for a detailed list of implemented and +unimplemented features of the network protocols. ## Hardware support diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 1c0cf1a12..86ced1ded 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -15,6 +15,8 @@ pub(crate) mod fmt; mod device; #[cfg(feature = "dns")] pub mod dns; +#[cfg(feature = "raw")] +pub mod raw; #[cfg(feature = "tcp")] pub mod tcp; mod time; diff --git a/embassy-net/src/raw.rs b/embassy-net/src/raw.rs new file mode 100644 index 000000000..7ecd913e7 --- /dev/null +++ b/embassy-net/src/raw.rs @@ -0,0 +1,120 @@ +//! Raw sockets. + +use core::cell::RefCell; +use core::future::poll_fn; +use core::mem; +use core::task::{Context, Poll}; + +use embassy_net_driver::Driver; +use smoltcp::iface::{Interface, SocketHandle}; +use smoltcp::socket::raw; +pub use smoltcp::socket::raw::PacketMetadata; +use smoltcp::wire::{IpProtocol, IpVersion}; + +use crate::{SocketStack, Stack}; + +/// Error returned by [`RawSocket::recv`] and [`RawSocket::send`]. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum RecvError { + /// Provided buffer was smaller than the received packet. + Truncated, +} + +/// An Raw socket. +pub struct RawSocket<'a> { + stack: &'a RefCell, + handle: SocketHandle, +} + +impl<'a> RawSocket<'a> { + /// Create a new Raw socket using the provided stack and buffers. + pub fn new( + stack: &'a Stack, + ip_version: IpVersion, + ip_protocol: IpProtocol, + rx_meta: &'a mut [PacketMetadata], + rx_buffer: &'a mut [u8], + tx_meta: &'a mut [PacketMetadata], + tx_buffer: &'a mut [u8], + ) -> Self { + let s = &mut *stack.socket.borrow_mut(); + + let rx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(rx_meta) }; + let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; + let tx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(tx_meta) }; + let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) }; + let handle = s.sockets.add(raw::Socket::new( + ip_version, + ip_protocol, + raw::PacketBuffer::new(rx_meta, rx_buffer), + raw::PacketBuffer::new(tx_meta, tx_buffer), + )); + + Self { + stack: &stack.socket, + handle, + } + } + + fn with_mut(&self, f: impl FnOnce(&mut raw::Socket, &mut Interface) -> R) -> R { + let s = &mut *self.stack.borrow_mut(); + let socket = s.sockets.get_mut::(self.handle); + let res = f(socket, &mut s.iface); + s.waker.wake(); + res + } + + /// Receive a datagram. + /// + /// This method will wait until a datagram is received. + pub async fn recv(&self, buf: &mut [u8]) -> Result { + poll_fn(move |cx| self.poll_recv(buf, cx)).await + } + + /// Receive a datagram. + /// + /// When no datagram is available, this method will return `Poll::Pending` and + /// register the current task to be notified when a datagram is received. + pub fn poll_recv(&self, buf: &mut [u8], cx: &mut Context<'_>) -> Poll> { + self.with_mut(|s, _| match s.recv_slice(buf) { + Ok(n) => Poll::Ready(Ok(n)), + // No data ready + Err(raw::RecvError::Truncated) => Poll::Ready(Err(RecvError::Truncated)), + Err(raw::RecvError::Exhausted) => { + s.register_recv_waker(cx.waker()); + Poll::Pending + } + }) + } + + /// Send a datagram. + /// + /// This method will wait until the datagram has been sent.` + pub async fn send(&self, buf: &[u8]) { + poll_fn(move |cx| self.poll_send(buf, cx)).await + } + + /// Send a datagram. + /// + /// When the datagram has been sent, this method will return `Poll::Ready(Ok())`. + /// + /// When the socket's send buffer is full, this method will return `Poll::Pending` + /// and register the current task to be notified when the buffer has space available. + pub fn poll_send(&self, buf: &[u8], cx: &mut Context<'_>) -> Poll<()> { + self.with_mut(|s, _| match s.send_slice(buf) { + // Entire datagram has been sent + Ok(()) => Poll::Ready(()), + Err(raw::SendError::BufferFull) => { + s.register_send_waker(cx.waker()); + Poll::Pending + } + }) + } +} + +impl Drop for RawSocket<'_> { + fn drop(&mut self) { + self.stack.borrow_mut().sockets.remove(self.handle); + } +} diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md new file mode 100644 index 000000000..773a1a108 --- /dev/null +++ b/embassy-nrf/CHANGELOG.md @@ -0,0 +1,40 @@ +# Changelog for embassy-nrf + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +- Drop `sealed` mod +- nrf52840: Add dcdc voltage parameter to configure REG0 regulator +- radio: Add support for IEEE 802.15.4 and BLE via radio peripheral +- spim: Reduce trace-level messages ("Copying SPIM tx buffer..") +- uart: Add support for rx- or tx-only BufferedUart +- uart: Implement splitting Rx/Tx +- spi: Allow specifying OutputDrive for SPI spins +- pdm: Fix gain register value derivation +- spim: Implement chunked DMA transfers +- spi: Add bounds checks for EasyDMA buffer size +- uarte: Add support for handling RX errors +- nrf51: Implement support for nrf51 chip +- pwm: Expose `duty` method +- pwm: Fix infinite loop +- spi: Add support for configuring bit order for bus +- pwm: Expose `pwm::PWM_CLK_HZ` and add `is_enabled` method +- gpio: Drop GPIO Pin generics (API break) + +## 0.1.0 - 2024-01-12 + +- First release with support for following NRF chips: + - nrf52805 + - nrf52810 + - nrf52811 + - nrf52820 + - nrf52832 + - nrf52833 + - nrf52840 + - nrf5340 + - nrf9160 + diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index ef2662c85..c1501e02e 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -1,4 +1,4 @@ -//! Pulse Density Modulation (PDM) mirophone driver +//! Pulse Density Modulation (PDM) microphone driver #![macro_use] diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 89b24f0eb..095427171 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -30,6 +30,7 @@ flavors = [ { regex_feature = "stm32l1.*", target = "thumbv7m-none-eabi" }, { regex_feature = "stm32l4.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32l5.*", target = "thumbv8m.main-none-eabihf", features = ["low-power"] }, + { regex_feature = "stm32u0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32u5.*", target = "thumbv8m.main-none-eabihf" }, { regex_feature = "stm32wb.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32wba.*", target = "thumbv8m.main-none-eabihf" }, @@ -70,7 +71,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b7ec569a5510c324693f0515ac8ea20b12917a9" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-01ac9bfd035961dc75f32dcd6080501538246d5c" } vcell = "0.1.3" nb = "1.0.0" @@ -96,7 +97,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b7ec569a5510c324693f0515ac8ea20b12917a9", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-01ac9bfd035961dc75f32dcd6080501538246d5c", default-features = false, features = ["metadata"]} [features] default = ["rt"] @@ -114,6 +115,9 @@ low-power-debug-with-sleep = [] ## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/) memory-x = ["stm32-metapac/memory-x"] +## Use secure registers when TrustZone is enabled +trustzone-secure = [] + ## Re-export stm32-metapac at `embassy_stm32::pac`. ## This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. ## If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC. @@ -1419,6 +1423,38 @@ stm32l562qe = [ "stm32-metapac/stm32l562qe" ] stm32l562re = [ "stm32-metapac/stm32l562re" ] stm32l562ve = [ "stm32-metapac/stm32l562ve" ] stm32l562ze = [ "stm32-metapac/stm32l562ze" ] +stm32u031c6 = [ "stm32-metapac/stm32u031c6" ] +stm32u031c8 = [ "stm32-metapac/stm32u031c8" ] +stm32u031f4 = [ "stm32-metapac/stm32u031f4" ] +stm32u031f6 = [ "stm32-metapac/stm32u031f6" ] +stm32u031f8 = [ "stm32-metapac/stm32u031f8" ] +stm32u031g6 = [ "stm32-metapac/stm32u031g6" ] +stm32u031g8 = [ "stm32-metapac/stm32u031g8" ] +stm32u031k4 = [ "stm32-metapac/stm32u031k4" ] +stm32u031k6 = [ "stm32-metapac/stm32u031k6" ] +stm32u031k8 = [ "stm32-metapac/stm32u031k8" ] +stm32u031r6 = [ "stm32-metapac/stm32u031r6" ] +stm32u031r8 = [ "stm32-metapac/stm32u031r8" ] +stm32u073c8 = [ "stm32-metapac/stm32u073c8" ] +stm32u073cb = [ "stm32-metapac/stm32u073cb" ] +stm32u073cc = [ "stm32-metapac/stm32u073cc" ] +stm32u073h8 = [ "stm32-metapac/stm32u073h8" ] +stm32u073hb = [ "stm32-metapac/stm32u073hb" ] +stm32u073hc = [ "stm32-metapac/stm32u073hc" ] +stm32u073k8 = [ "stm32-metapac/stm32u073k8" ] +stm32u073kb = [ "stm32-metapac/stm32u073kb" ] +stm32u073kc = [ "stm32-metapac/stm32u073kc" ] +stm32u073m8 = [ "stm32-metapac/stm32u073m8" ] +stm32u073mb = [ "stm32-metapac/stm32u073mb" ] +stm32u073mc = [ "stm32-metapac/stm32u073mc" ] +stm32u073r8 = [ "stm32-metapac/stm32u073r8" ] +stm32u073rb = [ "stm32-metapac/stm32u073rb" ] +stm32u073rc = [ "stm32-metapac/stm32u073rc" ] +stm32u083cc = [ "stm32-metapac/stm32u083cc" ] +stm32u083hc = [ "stm32-metapac/stm32u083hc" ] +stm32u083kc = [ "stm32-metapac/stm32u083kc" ] +stm32u083mc = [ "stm32-metapac/stm32u083mc" ] +stm32u083rc = [ "stm32-metapac/stm32u083rc" ] stm32u535cb = [ "stm32-metapac/stm32u535cb" ] stm32u535cc = [ "stm32-metapac/stm32u535cc" ] stm32u535ce = [ "stm32-metapac/stm32u535ce" ] @@ -1474,6 +1510,7 @@ stm32u599vj = [ "stm32-metapac/stm32u599vj" ] stm32u599zi = [ "stm32-metapac/stm32u599zi" ] stm32u599zj = [ "stm32-metapac/stm32u599zj" ] stm32u5a5aj = [ "stm32-metapac/stm32u5a5aj" ] +stm32u5a5qi = [ "stm32-metapac/stm32u5a5qi" ] stm32u5a5qj = [ "stm32-metapac/stm32u5a5qj" ] stm32u5a5rj = [ "stm32-metapac/stm32u5a5rj" ] stm32u5a5vj = [ "stm32-metapac/stm32u5a5vj" ] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 38b6c480c..67fceda56 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -272,8 +272,6 @@ fn main() { "Bank1" } else if region.name.starts_with("BANK_2") { "Bank2" - } else if region.name == "OTP" { - "Otp" } else { continue; } @@ -664,6 +662,7 @@ fn main() { #(pub use crate::pac::rcc::vals::#enum_names as #enum_names; )* #[derive(Clone, Copy)] + #[non_exhaustive] pub struct ClockMux { #( #struct_fields, )* } diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index cecf67947..80eaecc14 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -3,8 +3,8 @@ use core::marker::PhantomData; use core::task::Poll; use embassy_hal_internal::into_ref; -use embedded_hal_02::blocking::delay::DelayUs; +use super::blocking_delay_us; use crate::adc::{Adc, AdcPin, Instance, SampleTime}; use crate::time::Hertz; use crate::{interrupt, Peripheral}; @@ -48,14 +48,14 @@ impl super::SealedAdcPin for Temperature { } impl<'d, T: Instance> Adc<'d, T> { - pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + pub fn new(adc: impl Peripheral

+ 'd) -> Self { into_ref!(adc); T::enable_and_reset(); T::regs().cr2().modify(|reg| reg.set_adon(true)); // 11.4: Before starting a calibration, the ADC must have been in power-on state (ADON bit = ‘1’) - // for at least two ADC clock cycles - delay.delay_us((1_000_000 * 2) / Self::freq().0 + 1); + // for at least two ADC clock cycles. + blocking_delay_us((1_000_000 * 2) / Self::freq().0 + 1); // Reset calibration T::regs().cr2().modify(|reg| reg.set_rstcal(true)); @@ -70,7 +70,7 @@ impl<'d, T: Instance> Adc<'d, T> { } // One cycle after calibration - delay.delay_us((1_000_000) / Self::freq().0 + 1); + blocking_delay_us((1_000_000 * 1) / Self::freq().0 + 1); Self { adc, @@ -95,7 +95,7 @@ impl<'d, T: Instance> Adc<'d, T> { } } - pub fn enable_vref(&self, _delay: &mut impl DelayUs) -> Vref { + pub fn enable_vref(&self) -> Vref { T::regs().cr2().modify(|reg| { reg.set_tsvrefe(true); }); diff --git a/embassy-stm32/src/adc/f3.rs b/embassy-stm32/src/adc/f3.rs index c5581dba1..c22a3fe4a 100644 --- a/embassy-stm32/src/adc/f3.rs +++ b/embassy-stm32/src/adc/f3.rs @@ -3,8 +3,8 @@ use core::marker::PhantomData; use core::task::Poll; use embassy_hal_internal::into_ref; -use embedded_hal_02::blocking::delay::DelayUs; +use super::blocking_delay_us; use crate::adc::{Adc, AdcPin, Instance, SampleTime}; use crate::interrupt::typelevel::Interrupt; use crate::time::Hertz; @@ -58,7 +58,6 @@ impl<'d, T: Instance> Adc<'d, T> { pub fn new( adc: impl Peripheral

+ 'd, _irq: impl interrupt::typelevel::Binding> + 'd, - delay: &mut impl DelayUs, ) -> Self { use crate::pac::adc::vals; @@ -71,7 +70,7 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().cr().modify(|w| w.set_advregen(vals::Advregen::ENABLED)); // Wait for the regulator to stabilize - delay.delay_us(10); + blocking_delay_us(10); assert!(!T::regs().cr().read().aden()); @@ -81,8 +80,8 @@ impl<'d, T: Instance> Adc<'d, T> { while T::regs().cr().read().adcal() {} - // Wait more than 4 clock cycles after adcal is cleared (RM0364 p. 223) - delay.delay_us(1 + (6 * 1_000_000 / Self::freq().0)); + // Wait more than 4 clock cycles after adcal is cleared (RM0364 p. 223). + blocking_delay_us((1_000_000 * 4) / Self::freq().0 + 1); // Enable the adc T::regs().cr().modify(|w| w.set_aden(true)); @@ -117,7 +116,7 @@ impl<'d, T: Instance> Adc<'d, T> { } } - pub fn enable_vref(&self, _delay: &mut impl DelayUs) -> Vref { + pub fn enable_vref(&self) -> Vref { T::common_regs().ccr().modify(|w| w.set_vrefen(true)); Vref {} diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs new file mode 100644 index 000000000..221cc2a40 --- /dev/null +++ b/embassy-stm32/src/adc/g4.rs @@ -0,0 +1,304 @@ +#[allow(unused)] +use pac::adc::vals::{Adcaldif, Difsel, Exten}; +use pac::adccommon::vals::Presc; + +use super::{blocking_delay_us, Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; +use crate::time::Hertz; +use crate::{pac, Peripheral}; + +/// Default VREF voltage used for sample conversion to millivolts. +pub const VREF_DEFAULT_MV: u32 = 3300; +/// VREF voltage used for factory calibration of VREFINTCAL register. +pub const VREF_CALIB_MV: u32 = 3300; + +/// Max single ADC operation clock frequency +#[cfg(stm32g4)] +const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); +#[cfg(stm32h7)] +const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); + +#[cfg(stm32g4)] +const VREF_CHANNEL: u8 = 18; +#[cfg(stm32g4)] +const TEMP_CHANNEL: u8 = 16; + +#[cfg(stm32h7)] +const VREF_CHANNEL: u8 = 19; +#[cfg(stm32h7)] +const TEMP_CHANNEL: u8 = 18; + +// TODO this should be 14 for H7a/b/35 +const VBAT_CHANNEL: u8 = 17; + +// NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs +/// Internal voltage reference channel. +pub struct VrefInt; +impl InternalChannel for VrefInt {} +impl super::SealedInternalChannel for VrefInt { + fn channel(&self) -> u8 { + VREF_CHANNEL + } +} + +/// Internal temperature channel. +pub struct Temperature; +impl InternalChannel for Temperature {} +impl super::SealedInternalChannel for Temperature { + fn channel(&self) -> u8 { + TEMP_CHANNEL + } +} + +/// Internal battery voltage channel. +pub struct Vbat; +impl InternalChannel for Vbat {} +impl super::SealedInternalChannel for Vbat { + fn channel(&self) -> u8 { + VBAT_CHANNEL + } +} + +// NOTE (unused): The prescaler enum closely copies the hardware capabilities, +// but high prescaling doesn't make a lot of sense in the current implementation and is ommited. +#[allow(unused)] +enum Prescaler { + NotDivided, + DividedBy2, + DividedBy4, + DividedBy6, + DividedBy8, + DividedBy10, + DividedBy12, + DividedBy16, + DividedBy32, + DividedBy64, + DividedBy128, + DividedBy256, +} + +impl Prescaler { + fn from_ker_ck(frequency: Hertz) -> Self { + let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; + match raw_prescaler { + 0 => Self::NotDivided, + 1 => Self::DividedBy2, + 2..=3 => Self::DividedBy4, + 4..=5 => Self::DividedBy6, + 6..=7 => Self::DividedBy8, + 8..=9 => Self::DividedBy10, + 10..=11 => Self::DividedBy12, + _ => unimplemented!(), + } + } + + fn divisor(&self) -> u32 { + match self { + Prescaler::NotDivided => 1, + Prescaler::DividedBy2 => 2, + Prescaler::DividedBy4 => 4, + Prescaler::DividedBy6 => 6, + Prescaler::DividedBy8 => 8, + Prescaler::DividedBy10 => 10, + Prescaler::DividedBy12 => 12, + Prescaler::DividedBy16 => 16, + Prescaler::DividedBy32 => 32, + Prescaler::DividedBy64 => 64, + Prescaler::DividedBy128 => 128, + Prescaler::DividedBy256 => 256, + } + } + + fn presc(&self) -> Presc { + match self { + Prescaler::NotDivided => Presc::DIV1, + Prescaler::DividedBy2 => Presc::DIV2, + Prescaler::DividedBy4 => Presc::DIV4, + Prescaler::DividedBy6 => Presc::DIV6, + Prescaler::DividedBy8 => Presc::DIV8, + Prescaler::DividedBy10 => Presc::DIV10, + Prescaler::DividedBy12 => Presc::DIV12, + Prescaler::DividedBy16 => Presc::DIV16, + Prescaler::DividedBy32 => Presc::DIV32, + Prescaler::DividedBy64 => Presc::DIV64, + Prescaler::DividedBy128 => Presc::DIV128, + Prescaler::DividedBy256 => Presc::DIV256, + } + } +} + +impl<'d, T: Instance> Adc<'d, T> { + /// Create a new ADC driver. + pub fn new(adc: impl Peripheral

+ 'd) -> Self { + embassy_hal_internal::into_ref!(adc); + T::enable_and_reset(); + + let prescaler = Prescaler::from_ker_ck(T::frequency()); + + T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); + + let frequency = Hertz(T::frequency().0 / prescaler.divisor()); + info!("ADC frequency set to {} Hz", frequency.0); + + if frequency > MAX_ADC_CLK_FREQ { + panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); + } + + let mut s = Self { + adc, + sample_time: SampleTime::from_bits(0), + }; + s.power_up(); + s.configure_differential_inputs(); + + s.calibrate(); + blocking_delay_us(1); + + s.enable(); + s.configure(); + + s + } + + fn power_up(&mut self) { + T::regs().cr().modify(|reg| { + reg.set_deeppwd(false); + reg.set_advregen(true); + }); + + blocking_delay_us(10); + } + + fn configure_differential_inputs(&mut self) { + T::regs().difsel().modify(|w| { + for n in 0..18 { + w.set_difsel(n, Difsel::SINGLEENDED); + } + }); + } + + fn calibrate(&mut self) { + T::regs().cr().modify(|w| { + w.set_adcaldif(Adcaldif::SINGLEENDED); + }); + + T::regs().cr().modify(|w| w.set_adcal(true)); + + while T::regs().cr().read().adcal() {} + } + + fn enable(&mut self) { + T::regs().isr().write(|w| w.set_adrdy(true)); + T::regs().cr().modify(|w| w.set_aden(true)); + while !T::regs().isr().read().adrdy() {} + T::regs().isr().write(|w| w.set_adrdy(true)); + } + + fn configure(&mut self) { + // single conversion mode, software trigger + T::regs().cfgr().modify(|w| { + w.set_cont(false); + w.set_exten(Exten::DISABLED); + }); + } + + /// Enable reading the voltage reference internal channel. + pub fn enable_vrefint(&self) -> VrefInt { + T::common_regs().ccr().modify(|reg| { + reg.set_vrefen(true); + }); + + VrefInt {} + } + + /// Enable reading the temperature internal channel. + pub fn enable_temperature(&self) -> Temperature { + T::common_regs().ccr().modify(|reg| { + reg.set_vsenseen(true); + }); + + Temperature {} + } + + /// Enable reading the vbat internal channel. + pub fn enable_vbat(&self) -> Vbat { + T::common_regs().ccr().modify(|reg| { + reg.set_vbaten(true); + }); + + Vbat {} + } + + /// Set the ADC sample time. + pub fn set_sample_time(&mut self, sample_time: SampleTime) { + self.sample_time = sample_time; + } + + /// Set the ADC resolution. + pub fn set_resolution(&mut self, resolution: Resolution) { + T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); + } + + /// Perform a single conversion. + fn convert(&mut self) -> u16 { + T::regs().isr().modify(|reg| { + reg.set_eos(true); + reg.set_eoc(true); + }); + + // Start conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); + + while !T::regs().isr().read().eos() { + // spin + } + + T::regs().dr().read().0 as u16 + } + + /// Read an ADC pin. + pub fn read

(&mut self, pin: &mut P) -> u16 + where + P: AdcPin, + P: crate::gpio::Pin, + { + pin.set_as_analog(); + + self.read_channel(pin.channel()) + } + + /// Read an ADC internal channel. + pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 { + self.read_channel(channel.channel()) + } + + fn read_channel(&mut self, channel: u8) -> u16 { + // Configure channel + Self::set_channel_sample_time(channel, self.sample_time); + + #[cfg(stm32h7)] + { + T::regs().cfgr2().modify(|w| w.set_lshift(0)); + T::regs() + .pcsel() + .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); + } + + T::regs().sqr1().write(|reg| { + reg.set_sq(0, channel); + reg.set_l(0); + }); + + self.convert() + } + + fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { + let sample_time = sample_time.into(); + if ch <= 9 { + T::regs().smpr().modify(|reg| reg.set_smp(ch as _, sample_time)); + } else { + T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); + } + } +} diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index ead2357ce..8ef68490b 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -10,8 +10,9 @@ #[cfg_attr(adc_v1, path = "v1.rs")] #[cfg_attr(adc_l0, path = "v1.rs")] #[cfg_attr(adc_v2, path = "v2.rs")] -#[cfg_attr(any(adc_v3, adc_g0, adc_h5), path = "v3.rs")] +#[cfg_attr(any(adc_v3, adc_g0, adc_h5, adc_u0), path = "v3.rs")] #[cfg_attr(adc_v4, path = "v4.rs")] +#[cfg_attr(adc_g4, path = "g4.rs")] mod _version; #[allow(unused)] @@ -69,14 +70,54 @@ trait SealedInternalChannel { fn channel(&self) -> u8; } +/// Performs a busy-wait delay for a specified number of microseconds. +#[allow(unused)] +pub(crate) fn blocking_delay_us(us: u32) { + #[cfg(time)] + embassy_time::block_for(embassy_time::Duration::from_micros(us)); + #[cfg(not(time))] + { + let freq = unsafe { crate::rcc::get_freqs() }.sys.unwrap().0 as u64; + let us = us as u64; + let cycles = freq * us / 1_000_000; + cortex_m::asm::delay(cycles as u32); + } +} + /// ADC instance. -#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0, adc_h5)))] +#[cfg(not(any( + adc_f1, + adc_v1, + adc_l0, + adc_v2, + adc_v3, + adc_v4, + adc_g4, + adc_f3, + adc_f3_v1_1, + adc_g0, + adc_u0, + adc_h5 +)))] #[allow(private_bounds)] pub trait Instance: SealedInstance + crate::Peripheral

{ type Interrupt: crate::interrupt::typelevel::Interrupt; } /// ADC instance. -#[cfg(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0, adc_h5))] +#[cfg(any( + adc_f1, + adc_v1, + adc_l0, + adc_v2, + adc_v3, + adc_v4, + adc_g4, + adc_f3, + adc_f3_v1_1, + adc_g0, + adc_u0, + adc_h5 +))] #[allow(private_bounds)] pub trait Instance: SealedInstance + crate::Peripheral

+ crate::rcc::RccPeripheral { type Interrupt: crate::interrupt::typelevel::Interrupt; diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index e9b46be80..1dda28cf2 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -3,10 +3,10 @@ use core::marker::PhantomData; use core::task::Poll; use embassy_hal_internal::into_ref; -use embedded_hal_02::blocking::delay::DelayUs; #[cfg(adc_l0)] use stm32_metapac::adc::vals::Ckmode; +use super::blocking_delay_us; use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; use crate::interrupt::typelevel::Interrupt; use crate::peripherals::ADC; @@ -65,7 +65,6 @@ impl<'d, T: Instance> Adc<'d, T> { pub fn new( adc: impl Peripheral

+ 'd, _irq: impl interrupt::typelevel::Binding> + 'd, - delay: &mut impl DelayUs, ) -> Self { into_ref!(adc); T::enable_and_reset(); @@ -74,7 +73,7 @@ impl<'d, T: Instance> Adc<'d, T> { // // Table 57. ADC characteristics // tstab = 14 * 1/fadc - delay.delay_us(1); + blocking_delay_us(1); // set default PCKL/2 on L0s because HSI is disabled in the default clock config #[cfg(adc_l0)] @@ -114,7 +113,7 @@ impl<'d, T: Instance> Adc<'d, T> { } #[cfg(not(adc_l0))] - pub fn enable_vbat(&self, _delay: &mut impl DelayUs) -> Vbat { + pub fn enable_vbat(&self) -> Vbat { // SMP must be ≥ 56 ADC clock cycles when using HSI14. // // 6.3.20 Vbat monitoring characteristics @@ -123,22 +122,22 @@ impl<'d, T: Instance> Adc<'d, T> { Vbat } - pub fn enable_vref(&self, delay: &mut impl DelayUs) -> Vref { + pub fn enable_vref(&self) -> Vref { // Table 28. Embedded internal reference voltage // tstart = 10μs T::regs().ccr().modify(|reg| reg.set_vrefen(true)); - delay.delay_us(10); + blocking_delay_us(10); Vref } - pub fn enable_temperature(&self, delay: &mut impl DelayUs) -> Temperature { + pub fn enable_temperature(&self) -> Temperature { // SMP must be ≥ 56 ADC clock cycles when using HSI14. // // 6.3.19 Temperature sensor characteristics // tstart ≤ 10μs // ts_temp ≥ 4μs T::regs().ccr().modify(|reg| reg.set_tsen(true)); - delay.delay_us(10); + blocking_delay_us(10); Temperature } diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index a43eb72db..7771cf768 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -1,6 +1,6 @@ use embassy_hal_internal::into_ref; -use embedded_hal_02::blocking::delay::DelayUs; +use super::blocking_delay_us; use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; use crate::peripherals::ADC1; use crate::time::Hertz; @@ -11,9 +11,6 @@ pub const VREF_DEFAULT_MV: u32 = 3300; /// VREF voltage used for factory calibration of VREFINTCAL register. pub const VREF_CALIB_MV: u32 = 3300; -/// ADC turn-on time -pub const ADC_POWERUP_TIME_US: u32 = 3; - pub struct VrefInt; impl AdcPin for VrefInt {} impl super::SealedAdcPin for VrefInt { @@ -97,7 +94,7 @@ impl<'d, T> Adc<'d, T> where T: Instance, { - pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + pub fn new(adc: impl Peripheral

+ 'd) -> Self { into_ref!(adc); T::enable_and_reset(); @@ -107,7 +104,7 @@ where reg.set_adon(true); }); - delay.delay_us(ADC_POWERUP_TIME_US); + blocking_delay_us(3); Self { adc, diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index e25630be2..dc418297e 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -1,7 +1,7 @@ use cfg_if::cfg_if; use embassy_hal_internal::into_ref; -use embedded_hal_02::blocking::delay::DelayUs; +use super::blocking_delay_us; use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; use crate::Peripheral; @@ -19,6 +19,8 @@ impl super::SealedAdcPin for VrefInt { let val = 13; } else if #[cfg(adc_h5)] { let val = 17; + } else if #[cfg(adc_u0)] { + let val = 12; } else { let val = 0; } @@ -36,6 +38,8 @@ impl super::SealedAdcPin for Temperature { let val = 12; } else if #[cfg(adc_h5)] { let val = 16; + } else if #[cfg(adc_u0)] { + let val = 11; } else { let val = 17; } @@ -53,6 +57,8 @@ impl super::SealedAdcPin for Vbat { let val = 14; } else if #[cfg(adc_h5)] { let val = 2; + } else if #[cfg(adc_h5)] { + let val = 13; } else { let val = 18; } @@ -73,22 +79,34 @@ cfg_if! { } } +cfg_if! { + if #[cfg(adc_u0)] { + pub struct DacOut; + impl AdcPin for DacOut {} + impl super::SealedAdcPin for DacOut { + fn channel(&self) -> u8 { + 19 + } + } + } +} + impl<'d, T: Instance> Adc<'d, T> { - pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + pub fn new(adc: impl Peripheral

+ 'd) -> Self { into_ref!(adc); T::enable_and_reset(); T::regs().cr().modify(|reg| { - #[cfg(not(adc_g0))] + #[cfg(not(any(adc_g0, adc_u0)))] reg.set_deeppwd(false); reg.set_advregen(true); }); - #[cfg(adc_g0)] + #[cfg(any(adc_g0, adc_u0))] T::regs().cfgr1().modify(|reg| { reg.set_chselrmod(false); }); - delay.delay_us(20); + blocking_delay_us(20); T::regs().cr().modify(|reg| { reg.set_adcal(true); @@ -98,7 +116,7 @@ impl<'d, T: Instance> Adc<'d, T> { // spin } - delay.delay_us(1); + blocking_delay_us(1); Self { adc, @@ -106,28 +124,26 @@ impl<'d, T: Instance> Adc<'d, T> { } } - pub fn enable_vrefint(&self, delay: &mut impl DelayUs) -> VrefInt { - #[cfg(not(adc_g0))] + pub fn enable_vrefint(&self) -> VrefInt { + #[cfg(not(any(adc_g0, adc_u0)))] T::common_regs().ccr().modify(|reg| { reg.set_vrefen(true); }); - #[cfg(adc_g0)] + #[cfg(any(adc_g0, adc_u0))] T::regs().ccr().modify(|reg| { reg.set_vrefen(true); }); // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us - // to stabilize the internal voltage reference, we wait a little more. - // TODO: delay 15us - //cortex_m::asm::delay(20_000_000); - delay.delay_us(15); + // to stabilize the internal voltage reference. + blocking_delay_us(15); VrefInt {} } pub fn enable_temperature(&self) -> Temperature { cfg_if! { - if #[cfg(adc_g0)] { + if #[cfg(any(adc_g0, adc_u0))] { T::regs().ccr().modify(|reg| { reg.set_tsen(true); }); @@ -147,7 +163,7 @@ impl<'d, T: Instance> Adc<'d, T> { pub fn enable_vbat(&self) -> Vbat { cfg_if! { - if #[cfg(adc_g0)] { + if #[cfg(any(adc_g0, adc_u0))] { T::regs().ccr().modify(|reg| { reg.set_vbaten(true); }); @@ -170,9 +186,9 @@ impl<'d, T: Instance> Adc<'d, T> { } pub fn set_resolution(&mut self, resolution: Resolution) { - #[cfg(not(adc_g0))] + #[cfg(not(any(adc_g0, adc_u0)))] T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); - #[cfg(adc_g0)] + #[cfg(any(adc_g0, adc_u0))] T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); } @@ -233,9 +249,9 @@ impl<'d, T: Instance> Adc<'d, T> { Self::set_channel_sample_time(pin.channel(), self.sample_time); // Select channel - #[cfg(not(adc_g0))] + #[cfg(not(any(adc_g0, adc_u0)))] T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel())); - #[cfg(adc_g0)] + #[cfg(any(adc_g0, adc_u0))] T::regs().chselr().write(|reg| reg.set_chsel(1 << pin.channel())); // Some models are affected by an erratum: @@ -263,7 +279,7 @@ impl<'d, T: Instance> Adc<'d, T> { fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { cfg_if! { - if #[cfg(adc_g0)] { + if #[cfg(any(adc_g0, adc_u0))] { T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); } else if #[cfg(adc_h5)] { match _ch { diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 1ae25bea2..ca87b41ee 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -1,9 +1,8 @@ -use embedded_hal_02::blocking::delay::DelayUs; #[allow(unused)] use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; use pac::adccommon::vals::Presc; -use super::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; +use super::{blocking_delay_us, Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; use crate::time::Hertz; use crate::{pac, Peripheral}; @@ -129,7 +128,7 @@ impl Prescaler { impl<'d, T: Instance> Adc<'d, T> { /// Create a new ADC driver. - pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + pub fn new(adc: impl Peripheral

+ 'd) -> Self { embassy_hal_internal::into_ref!(adc); T::enable_and_reset(); @@ -161,11 +160,11 @@ impl<'d, T: Instance> Adc<'d, T> { adc, sample_time: SampleTime::from_bits(0), }; - s.power_up(delay); + s.power_up(); s.configure_differential_inputs(); s.calibrate(); - delay.delay_us(1); + blocking_delay_us(1); s.enable(); s.configure(); @@ -173,13 +172,13 @@ impl<'d, T: Instance> Adc<'d, T> { s } - fn power_up(&mut self, delay: &mut impl DelayUs) { + fn power_up(&mut self) { T::regs().cr().modify(|reg| { reg.set_deeppwd(false); reg.set_advregen(true); }); - delay.delay_us(10); + blocking_delay_us(10); } fn configure_differential_inputs(&mut self) { diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs index e32f19d91..e5cfee528 100644 --- a/embassy-stm32/src/can/fd/peripheral.rs +++ b/embassy-stm32/src/can/fd/peripheral.rs @@ -368,6 +368,7 @@ impl Registers { w.set_rfne(0, true); // Rx Fifo 0 New Msg w.set_rfne(1, true); // Rx Fifo 1 New Msg w.set_tce(true); // Tx Complete + w.set_boe(true); // Bus-Off Status Changed }); self.regs.ile().modify(|w| { w.set_eint0(true); // Interrupt Line 0 diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index e31821ca2..563f542d4 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -44,53 +44,51 @@ impl interrupt::typelevel::Handler for IT0Interrup let ir = regs.ir().read(); - { - if ir.tc() { - regs.ir().write(|w| w.set_tc(true)); - } - if ir.tefn() { - regs.ir().write(|w| w.set_tefn(true)); - } - - match &T::state().tx_mode { - TxMode::NonBuffered(waker) => waker.wake(), - TxMode::ClassicBuffered(buf) => { - if !T::registers().tx_queue_is_full() { - match buf.tx_receiver.try_receive() { - Ok(frame) => { - _ = T::registers().write(&frame); - } - Err(_) => {} - } - } - } - TxMode::FdBuffered(buf) => { - if !T::registers().tx_queue_is_full() { - match buf.tx_receiver.try_receive() { - Ok(frame) => { - _ = T::registers().write(&frame); - } - Err(_) => {} - } - } - } - } + if ir.tc() { + regs.ir().write(|w| w.set_tc(true)); + } + if ir.tefn() { + regs.ir().write(|w| w.set_tefn(true)); } - if ir.ped() || ir.pea() { - regs.ir().write(|w| { - w.set_ped(true); - w.set_pea(true); - }); + match &T::state().tx_mode { + TxMode::NonBuffered(waker) => waker.wake(), + TxMode::ClassicBuffered(buf) => { + if !T::registers().tx_queue_is_full() { + match buf.tx_receiver.try_receive() { + Ok(frame) => { + _ = T::registers().write(&frame); + } + Err(_) => {} + } + } + } + TxMode::FdBuffered(buf) => { + if !T::registers().tx_queue_is_full() { + match buf.tx_receiver.try_receive() { + Ok(frame) => { + _ = T::registers().write(&frame); + } + Err(_) => {} + } + } + } } if ir.rfn(0) { T::state().rx_mode.on_interrupt::(0); } - if ir.rfn(1) { T::state().rx_mode.on_interrupt::(1); } + + if ir.bo() { + regs.ir().write(|w| w.set_bo(true)); + if regs.psr().read().bo() { + // Initiate bus-off recovery sequence by resetting CCCR.INIT + regs.cccr().modify(|w| w.set_init(false)); + } + } } } diff --git a/embassy-stm32/src/crc/v1.rs b/embassy-stm32/src/crc/v1.rs index f8909d438..e8e0270af 100644 --- a/embassy-stm32/src/crc/v1.rs +++ b/embassy-stm32/src/crc/v1.rs @@ -32,6 +32,9 @@ impl<'d> Crc<'d> { /// Feeds a word to the peripheral and returns the current CRC value pub fn feed_word(&mut self, word: u32) -> u32 { // write a single byte to the device, and return the result + #[cfg(not(crc_v1))] + PAC_CRC.dr32().write_value(word); + #[cfg(crc_v1)] PAC_CRC.dr().write_value(word); self.read() } @@ -39,6 +42,9 @@ impl<'d> Crc<'d> { /// Feed a slice of words to the peripheral and return the result. pub fn feed_words(&mut self, words: &[u32]) -> u32 { for word in words { + #[cfg(not(crc_v1))] + PAC_CRC.dr32().write_value(*word); + #[cfg(crc_v1)] PAC_CRC.dr().write_value(*word); } @@ -46,6 +52,12 @@ impl<'d> Crc<'d> { } /// Read the CRC result value. + #[cfg(not(crc_v1))] + pub fn read(&self) -> u32 { + PAC_CRC.dr32().read() + } + /// Read the CRC result value. + #[cfg(crc_v1)] pub fn read(&self) -> u32 { PAC_CRC.dr().read() } diff --git a/embassy-stm32/src/crc/v2v3.rs b/embassy-stm32/src/crc/v2v3.rs index 46f5ea1be..ad7c79f12 100644 --- a/embassy-stm32/src/crc/v2v3.rs +++ b/embassy-stm32/src/crc/v2v3.rs @@ -13,6 +13,8 @@ pub struct Crc<'d> { } /// CRC configuration errlr +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ConfigError { /// The selected polynomial is invalid. InvalidPolynomial, @@ -136,7 +138,7 @@ impl<'d> Crc<'d> { /// Feeds a byte into the CRC peripheral. Returns the computed checksum. pub fn feed_byte(&mut self, byte: u8) -> u32 { PAC_CRC.dr8().write_value(byte); - PAC_CRC.dr().read() + PAC_CRC.dr32().read() } /// Feeds an slice of bytes into the CRC peripheral. Returns the computed checksum. @@ -144,30 +146,30 @@ impl<'d> Crc<'d> { for byte in bytes { PAC_CRC.dr8().write_value(*byte); } - PAC_CRC.dr().read() + PAC_CRC.dr32().read() } /// Feeds a halfword into the CRC peripheral. Returns the computed checksum. pub fn feed_halfword(&mut self, halfword: u16) -> u32 { PAC_CRC.dr16().write_value(halfword); - PAC_CRC.dr().read() + PAC_CRC.dr32().read() } /// Feeds an slice of halfwords into the CRC peripheral. Returns the computed checksum. pub fn feed_halfwords(&mut self, halfwords: &[u16]) -> u32 { for halfword in halfwords { PAC_CRC.dr16().write_value(*halfword); } - PAC_CRC.dr().read() + PAC_CRC.dr32().read() } /// Feeds a words into the CRC peripheral. Returns the computed checksum. pub fn feed_word(&mut self, word: u32) -> u32 { - PAC_CRC.dr().write_value(word as u32); - PAC_CRC.dr().read() + PAC_CRC.dr32().write_value(word as u32); + PAC_CRC.dr32().read() } /// Feeds an slice of words into the CRC peripheral. Returns the computed checksum. pub fn feed_words(&mut self, words: &[u32]) -> u32 { for word in words { - PAC_CRC.dr().write_value(*word as u32); + PAC_CRC.dr32().write_value(*word as u32); } - PAC_CRC.dr().read() + PAC_CRC.dr32().read() } } diff --git a/embassy-stm32/src/dac/tsel.rs b/embassy-stm32/src/dac/tsel.rs index 22d8d3dfa..1877954b9 100644 --- a/embassy-stm32/src/dac/tsel.rs +++ b/embassy-stm32/src/dac/tsel.rs @@ -235,6 +235,23 @@ pub enum TriggerSel { Exti9 = 13, } +/// Trigger selection for U0. +#[cfg(stm32u0)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TriggerSel { + Software = 0, + Tim1 = 1, + Tim2 = 2, + Tim3 = 3, + Tim6 = 5, + Tim7 = 6, + Tim15 = 8, + Lptim1 = 11, + Lptim2 = 12, + Exti9 = 14, +} + /// Trigger selection for G4. #[cfg(stm32g4)] #[derive(Debug, Copy, Clone, Eq, PartialEq)] diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 7e3681469..8766d0a60 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -16,6 +16,9 @@ mod dmamux; #[cfg(dmamux)] pub use dmamux::*; +mod util; +pub(crate) use util::*; + pub(crate) mod ringbuffer; pub mod word; diff --git a/embassy-stm32/src/dma/util.rs b/embassy-stm32/src/dma/util.rs new file mode 100644 index 000000000..962ea2501 --- /dev/null +++ b/embassy-stm32/src/dma/util.rs @@ -0,0 +1,60 @@ +use embassy_hal_internal::PeripheralRef; + +use super::word::Word; +use super::{AnyChannel, Request, Transfer, TransferOptions}; + +/// Convenience wrapper, contains a channel and a request number. +/// +/// Commonly used in peripheral drivers that own DMA channels. +pub(crate) struct ChannelAndRequest<'d> { + pub channel: PeripheralRef<'d, AnyChannel>, + pub request: Request, +} + +impl<'d> ChannelAndRequest<'d> { + pub unsafe fn read<'a, W: Word>( + &'a mut self, + peri_addr: *mut W, + buf: &'a mut [W], + options: TransferOptions, + ) -> Transfer<'a> { + Transfer::new_read(&mut self.channel, self.request, peri_addr, buf, options) + } + + pub unsafe fn read_raw<'a, W: Word>( + &'a mut self, + peri_addr: *mut W, + buf: *mut [W], + options: TransferOptions, + ) -> Transfer<'a> { + Transfer::new_read_raw(&mut self.channel, self.request, peri_addr, buf, options) + } + + pub unsafe fn write<'a, W: Word>( + &'a mut self, + buf: &'a [W], + peri_addr: *mut W, + options: TransferOptions, + ) -> Transfer<'a> { + Transfer::new_write(&mut self.channel, self.request, buf, peri_addr, options) + } + + pub unsafe fn write_raw<'a, W: Word>( + &'a mut self, + buf: *const [W], + peri_addr: *mut W, + options: TransferOptions, + ) -> Transfer<'a> { + Transfer::new_write_raw(&mut self.channel, self.request, buf, peri_addr, options) + } + + pub unsafe fn write_repeated<'a, W: Word>( + &'a mut self, + repeated: &'a W, + count: usize, + peri_addr: *mut W, + options: TransferOptions, + ) -> Transfer<'a> { + Transfer::new_write_repeated(&mut self.channel, self.request, repeated, count, peri_addr, options) + } +} diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 8d5dae436..224d51b84 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -27,11 +27,11 @@ fn cpu_regs() -> pac::exti::Exti { EXTI } -#[cfg(not(any(exti_c0, exti_g0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50)))] +#[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50)))] fn exticr_regs() -> pac::syscfg::Syscfg { pac::SYSCFG } -#[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] +#[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] fn exticr_regs() -> pac::exti::Exti { EXTI } @@ -44,9 +44,9 @@ unsafe fn on_irq() { #[cfg(feature = "low-power")] crate::low_power::on_wakeup_irq(); - #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))] + #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] let bits = EXTI.pr(0).read().0; - #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] + #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0; // We don't handle or change any EXTI lines above 16. @@ -61,9 +61,9 @@ unsafe fn on_irq() { } // Clear pending - #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))] + #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] EXTI.pr(0).write_value(Lines(bits)); - #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] + #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] { EXTI.rpr(0).write_value(Lines(bits)); EXTI.fpr(0).write_value(Lines(bits)); @@ -241,9 +241,9 @@ impl<'a> ExtiInputFuture<'a> { EXTI.ftsr(0).modify(|w| w.set_line(pin, falling)); // clear pending bit - #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))] + #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] EXTI.pr(0).write(|w| w.set_line(pin, true)); - #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] + #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] { EXTI.rpr(0).write(|w| w.set_line(pin, true)); EXTI.fpr(0).write(|w| w.set_line(pin, true)); diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 00e61f2d2..90f13ff29 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -16,7 +16,7 @@ mod alt_regions { use embassy_hal_internal::PeripheralRef; use stm32_metapac::FLASH_SIZE; - use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; + use crate::_generated::flash_regions::{BANK1_REGION1, BANK1_REGION2, BANK1_REGION3}; use crate::flash::{asynch, Async, Bank1Region1, Bank1Region2, Blocking, Error, Flash, FlashBank, FlashRegion}; use crate::peripherals::FLASH; @@ -62,7 +62,6 @@ mod alt_regions { pub bank2_region1: AltBank2Region1<'d, MODE>, pub bank2_region2: AltBank2Region2<'d, MODE>, pub bank2_region3: AltBank2Region3<'d, MODE>, - pub otp_region: OTPRegion<'d, MODE>, } impl<'d> Flash<'d> { @@ -79,7 +78,6 @@ mod alt_regions { bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData), bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData), bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData), - otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData), } } @@ -96,7 +94,6 @@ mod alt_regions { bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData), bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData), bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData), - otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData), } } } diff --git a/embassy-stm32/src/flash/h50.rs b/embassy-stm32/src/flash/h50.rs index db05bef5d..56ea7a421 100644 --- a/embassy-stm32/src/flash/h50.rs +++ b/embassy-stm32/src/flash/h50.rs @@ -55,17 +55,18 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) } pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { - assert!(sector.bank != FlashBank::Otp); assert!(sector.index_in_bank < 8); while busy() {} interrupt::free(|_| { pac::FLASH.nscr().modify(|w| { - w.set_bksel(match sector.bank { - FlashBank::Bank1 => Bksel::B_0X0, - FlashBank::Bank2 => Bksel::B_0X1, - _ => unreachable!(), + // BKSEL ignores SWAP_BANK, so we must take it into account here + w.set_bksel(match (sector.bank, banks_swapped()) { + (FlashBank::Bank1, false) => Bksel::BANK1, + (FlashBank::Bank2, true) => Bksel::BANK1, + (FlashBank::Bank2, false) => Bksel::BANK2, + (FlashBank::Bank1, true) => Bksel::BANK2, }); w.set_snb(sector.index_in_bank); w.set_ser(true); @@ -113,6 +114,47 @@ pub(crate) unsafe fn clear_all_err() { }) } +/// Get the current SWAP_BANK option. +/// +/// This value is only loaded on system or power-on reset. `perform_bank_swap()` +/// will not reflect here. +pub fn banks_swapped() -> bool { + pac::FLASH.optcr().read().swap_bank() +} + +/// Logical, persistent swap of flash banks 1 and 2. +/// +/// This allows the application to write a new firmware blob into bank 2, then +/// swap the banks and perform a reset, loading the new firmware. +/// +/// Swap does not take effect until system or power-on reset. +/// +/// PLEASE READ THE REFERENCE MANUAL - there are nuances to this feature. For +/// instance, erase commands and interrupt enables which take a flash bank as a +/// parameter ignore the swap! +pub fn perform_bank_swap() { + while busy() {} + + unsafe { + clear_all_err(); + } + + // unlock OPTLOCK + pac::FLASH.optkeyr().write(|w| *w = 0x0819_2A3B); + pac::FLASH.optkeyr().write(|w| *w = 0x4C5D_6E7F); + while pac::FLASH.optcr().read().optlock() {} + + // toggle SWAP_BANK option + pac::FLASH.optsr_prg().modify(|w| w.set_swap_bank(!banks_swapped())); + + // load option bytes + pac::FLASH.optcr().modify(|w| w.set_optstrt(true)); + while pac::FLASH.optcr().read().optstrt() {} + + // re-lock OPTLOCK + pac::FLASH.optcr().modify(|w| w.set_optlock(true)); +} + fn sr_busy(sr: Nssr) -> bool { // Note: RM0492 sometimes incorrectly refers to WBNE as NSWBNE sr.bsy() || sr.dbne() || sr.wbne() diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 1d8031e82..8c6ca2471 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -89,8 +89,6 @@ pub enum FlashBank { Bank1 = 0, /// Bank 2 Bank2 = 1, - /// OTP region - Otp, } #[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")] @@ -103,10 +101,11 @@ pub enum FlashBank { #[cfg_attr(flash_h7ab, path = "h7.rs")] #[cfg_attr(flash_u5, path = "u5.rs")] #[cfg_attr(flash_h50, path = "h50.rs")] +#[cfg_attr(flash_u0, path = "u0.rs")] #[cfg_attr( not(any( flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f1, flash_f3, flash_f4, flash_f7, flash_g0, - flash_g4, flash_h7, flash_h7ab, flash_u5, flash_h50 + flash_g4, flash_h7, flash_h7ab, flash_u5, flash_h50, flash_u0 )), path = "other.rs" )] diff --git a/embassy-stm32/src/flash/u0.rs b/embassy-stm32/src/flash/u0.rs new file mode 100644 index 000000000..dfc5a2f76 --- /dev/null +++ b/embassy-stm32/src/flash/u0.rs @@ -0,0 +1,96 @@ +use core::ptr::write_volatile; +use core::sync::atomic::{fence, Ordering}; + +use cortex_m::interrupt; + +use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +use crate::flash::Error; +use crate::pac; + +pub(crate) const fn is_default_layout() -> bool { + true +} + +pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} + +pub(crate) unsafe fn lock() { + pac::FLASH.cr().modify(|w| w.set_lock(true)); +} +pub(crate) unsafe fn unlock() { + // Wait, while the memory interface is busy. + while pac::FLASH.sr().read().bsy1() {} + + // Unlock flash + if pac::FLASH.cr().read().lock() { + pac::FLASH.keyr().write(|w| w.set_key(0x4567_0123)); + pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); + } +} + +pub(crate) unsafe fn enable_blocking_write() { + assert_eq!(0, WRITE_SIZE % 4); + pac::FLASH.cr().write(|w| w.set_pg(true)); +} + +pub(crate) unsafe fn disable_blocking_write() { + pac::FLASH.cr().write(|w| w.set_pg(false)); +} + +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + let mut address = start_address; + for val in buf.chunks(4) { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; + + // prevents parallelism errors + fence(Ordering::SeqCst); + } + + wait_ready_blocking() +} + +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; + while pac::FLASH.sr().read().bsy1() {} + clear_all_err(); + + interrupt::free(|_| { + pac::FLASH.cr().modify(|w| { + w.set_per(true); + w.set_pnb(idx as u8); + w.set_strt(true); + }); + }); + + let ret: Result<(), Error> = wait_ready_blocking(); + pac::FLASH.cr().modify(|w| w.set_per(false)); + ret +} + +pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> { + while pac::FLASH.sr().read().bsy1() {} + + let sr = pac::FLASH.sr().read(); + + if sr.progerr() { + return Err(Error::Prog); + } + + if sr.wrperr() { + return Err(Error::Protected); + } + + if sr.pgaerr() { + return Err(Error::Unaligned); + } + + Ok(()) +} + +pub(crate) unsafe fn clear_all_err() { + // read and write back the same value. + // This clears all "write 1 to clear" bits. + pac::FLASH.sr().modify(|_| {}); +} diff --git a/embassy-stm32/src/flash/u5.rs b/embassy-stm32/src/flash/u5.rs index 580c490da..ddd4d73ff 100644 --- a/embassy-stm32/src/flash/u5.rs +++ b/embassy-stm32/src/flash/u5.rs @@ -14,26 +14,43 @@ pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { } pub(crate) unsafe fn lock() { + #[cfg(feature = "trustzone-secure")] pac::FLASH.seccr().modify(|w| w.set_lock(true)); + #[cfg(not(feature = "trustzone-secure"))] + pac::FLASH.nscr().modify(|w| w.set_lock(true)); } pub(crate) unsafe fn unlock() { + #[cfg(feature = "trustzone-secure")] if pac::FLASH.seccr().read().lock() { pac::FLASH.seckeyr().write_value(0x4567_0123); pac::FLASH.seckeyr().write_value(0xCDEF_89AB); } + #[cfg(not(feature = "trustzone-secure"))] + if pac::FLASH.nscr().read().lock() { + pac::FLASH.nskeyr().write_value(0x4567_0123); + pac::FLASH.nskeyr().write_value(0xCDEF_89AB); + } } pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 4); + #[cfg(feature = "trustzone-secure")] pac::FLASH.seccr().write(|w| { w.set_pg(pac::flash::vals::SeccrPg::B_0X1); }); + #[cfg(not(feature = "trustzone-secure"))] + pac::FLASH.nscr().write(|w| { + w.set_pg(pac::flash::vals::NscrPg::B_0X1); + }); } pub(crate) unsafe fn disable_blocking_write() { + #[cfg(feature = "trustzone-secure")] pac::FLASH.seccr().write(|w| w.set_pg(pac::flash::vals::SeccrPg::B_0X0)); + #[cfg(not(feature = "trustzone-secure"))] + pac::FLASH.nscr().write(|w| w.set_pg(pac::flash::vals::NscrPg::B_0X0)); } pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { @@ -50,19 +67,35 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) } pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + #[cfg(feature = "trustzone-secure")] pac::FLASH.seccr().modify(|w| { w.set_per(pac::flash::vals::SeccrPer::B_0X1); w.set_pnb(sector.index_in_bank) }); + #[cfg(not(feature = "trustzone-secure"))] + pac::FLASH.nscr().modify(|w| { + w.set_per(pac::flash::vals::NscrPer::B_0X1); + w.set_pnb(sector.index_in_bank) + }); + #[cfg(feature = "trustzone-secure")] pac::FLASH.seccr().modify(|w| { w.set_strt(true); }); + #[cfg(not(feature = "trustzone-secure"))] + pac::FLASH.nscr().modify(|w| { + w.set_strt(true); + }); let ret: Result<(), Error> = blocking_wait_ready(); + #[cfg(feature = "trustzone-secure")] pac::FLASH .seccr() .modify(|w| w.set_per(pac::flash::vals::SeccrPer::B_0X0)); + #[cfg(not(feature = "trustzone-secure"))] + pac::FLASH + .nscr() + .modify(|w| w.set_per(pac::flash::vals::NscrPer::B_0X0)); clear_all_err(); ret } @@ -70,12 +103,18 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E pub(crate) unsafe fn clear_all_err() { // read and write back the same value. // This clears all "write 1 to clear" bits. + #[cfg(feature = "trustzone-secure")] pac::FLASH.secsr().modify(|_| {}); + #[cfg(not(feature = "trustzone-secure"))] + pac::FLASH.nssr().modify(|_| {}); } unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { + #[cfg(feature = "trustzone-secure")] let sr = pac::FLASH.secsr().read(); + #[cfg(not(feature = "trustzone-secure"))] + let sr = pac::FLASH.nssr().read(); if !sr.bsy() { if sr.pgserr() { diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index a46061d54..ccbea9831 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -14,9 +14,10 @@ use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; -use crate::dma::NoDma; +use crate::dma::ChannelAndRequest; use crate::gpio::{AFType, Pull}; use crate::interrupt::typelevel::Interrupt; +use crate::mode::{Async, Blocking, Mode}; use crate::time::Hertz; use crate::{interrupt, peripherals}; @@ -71,17 +72,16 @@ impl Default for Config { } /// I2C driver. -pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> { +pub struct I2c<'d, T: Instance, M: Mode> { _peri: PeripheralRef<'d, T>, - #[allow(dead_code)] - tx_dma: PeripheralRef<'d, TXDMA>, - #[allow(dead_code)] - rx_dma: PeripheralRef<'d, RXDMA>, + tx_dma: Option>, + rx_dma: Option>, #[cfg(feature = "time")] timeout: Duration, + _phantom: PhantomData, } -impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { +impl<'d, T: Instance> I2c<'d, T, Async> { /// Create a new I2C driver. pub fn new( peri: impl Peripheral

+ 'd, @@ -90,12 +90,40 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { _irq: impl interrupt::typelevel::Binding> + interrupt::typelevel::Binding> + 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, freq: Hertz, config: Config, ) -> Self { - into_ref!(peri, scl, sda, tx_dma, rx_dma); + Self::new_inner(peri, scl, sda, new_dma!(tx_dma), new_dma!(rx_dma), freq, config) + } +} + +impl<'d, T: Instance> I2c<'d, T, Blocking> { + /// Create a new blocking I2C driver. + pub fn new_blocking( + peri: impl Peripheral

+ 'd, + scl: impl Peripheral

> + 'd, + sda: impl Peripheral

> + 'd, + freq: Hertz, + config: Config, + ) -> Self { + Self::new_inner(peri, scl, sda, None, None, freq, config) + } +} + +impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { + /// Create a new I2C driver. + fn new_inner( + peri: impl Peripheral

+ 'd, + scl: impl Peripheral

> + 'd, + sda: impl Peripheral

> + 'd, + tx_dma: Option>, + rx_dma: Option>, + freq: Hertz, + config: Config, + ) -> Self { + into_ref!(peri, scl, sda); T::enable_and_reset(); @@ -125,6 +153,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { rx_dma, #[cfg(feature = "time")] timeout: config.timeout, + _phantom: PhantomData, }; this.init(freq, config); @@ -249,7 +278,7 @@ foreach_peripheral!( }; ); -impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> { +impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> { type Error = Error; fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -257,7 +286,7 @@ impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> { } } -impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> { +impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> { type Error = Error; fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { @@ -265,7 +294,7 @@ impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> { } } -impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> { +impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> { type Error = Error; fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { @@ -289,11 +318,11 @@ impl embedded_hal_1::i2c::Error for Error { } } -impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for I2c<'d, T, TXDMA, RXDMA> { +impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> { type Error = Error; } -impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> { +impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> { fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, read) } @@ -315,7 +344,7 @@ impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> { } } -impl<'d, T: Instance, TXDMA: TxDma, RXDMA: RxDma> embedded_hal_async::i2c::I2c for I2c<'d, T, TXDMA, RXDMA> { +impl<'d, T: Instance> embedded_hal_async::i2c::I2c for I2c<'d, T, Async> { async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { self.read(address, read).await } diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index d45c48b24..13a473344 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -13,7 +13,7 @@ use embassy_hal_internal::drop::OnDrop; use embedded_hal_1::i2c::Operation; use super::*; -use crate::dma::Transfer; +use crate::mode::Mode as PeriMode; use crate::pac::i2c; // /!\ /!\ @@ -41,7 +41,7 @@ pub unsafe fn on_interrupt() { }); } -impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { +impl<'d, T: Instance, M: PeriMode> I2c<'d, T, M> { pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { T::regs().cr1().modify(|reg| { reg.set_pe(false); @@ -326,11 +326,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { w.set_itevten(true); }); } +} - async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> - where - TXDMA: crate::i2c::TxDma, - { +impl<'d, T: Instance> I2c<'d, T, Async> { + async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> { T::regs().cr2().modify(|w| { // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for // reception. @@ -415,9 +414,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // this address from the memory after each TxE event. let dst = T::regs().dr().as_ptr() as *mut u8; - let ch = &mut self.tx_dma; - let request = ch.request(); - Transfer::new_write(ch, request, write, dst, Default::default()) + self.tx_dma.as_mut().unwrap().write(write, dst, Default::default()) }; // Wait for bytes to be sent, or an error to occur. @@ -479,10 +476,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } /// Write. - pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> - where - TXDMA: crate::i2c::TxDma, - { + pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { self.write_frame(address, write, FrameOptions::FirstAndLastFrame) .await?; @@ -490,20 +484,14 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } /// Read. - pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> - where - RXDMA: crate::i2c::RxDma, - { + pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { self.read_frame(address, buffer, FrameOptions::FirstAndLastFrame) .await?; Ok(()) } - async fn read_frame(&mut self, address: u8, buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error> - where - RXDMA: crate::i2c::RxDma, - { + async fn read_frame(&mut self, address: u8, buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error> { if buffer.is_empty() { return Err(Error::Overrun); } @@ -623,9 +611,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // from this address from the memory after each RxE event. let src = T::regs().dr().as_ptr() as *mut u8; - let ch = &mut self.rx_dma; - let request = ch.request(); - Transfer::new_read(ch, request, src, buffer, Default::default()) + self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) }; // Wait for bytes to be received, or an error to occur. @@ -664,11 +650,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } /// Write, restart, read. - pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> - where - RXDMA: crate::i2c::RxDma, - TXDMA: crate::i2c::TxDma, - { + pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { // Check empty read buffer before starting transaction. Otherwise, we would not generate the // stop condition below. if read.is_empty() { @@ -684,11 +666,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { /// Consecutive operations of same type are merged. See [transaction contract] for details. /// /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction - pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> - where - RXDMA: crate::i2c::RxDma, - TXDMA: crate::i2c::TxDma, - { + pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { for (op, frame) in operation_frames(operations)? { match op { Operation::Read(read) => self.read_frame(addr, read, frame).await?, @@ -700,7 +678,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } -impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> { +impl<'d, T: Instance, M: PeriMode> Drop for I2c<'d, T, M> { fn drop(&mut self) { T::disable(); } @@ -806,7 +784,7 @@ impl Timings { } } -impl<'d, T: Instance> SetConfig for I2c<'d, T> { +impl<'d, T: Instance, M: PeriMode> SetConfig for I2c<'d, T, M> { type Config = Hertz; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index da3b0ee30..12df98534 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -7,7 +7,6 @@ use embassy_hal_internal::drop::OnDrop; use embedded_hal_1::i2c::Operation; use super::*; -use crate::dma::Transfer; use crate::pac::i2c; pub(crate) unsafe fn on_interrupt() { @@ -24,7 +23,7 @@ pub(crate) unsafe fn on_interrupt() { }); } -impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { +impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { T::regs().cr1().modify(|reg| { reg.set_pe(false); @@ -302,276 +301,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { result } - async fn write_dma_internal( - &mut self, - address: u8, - write: &[u8], - first_slice: bool, - last_slice: bool, - timeout: Timeout, - ) -> Result<(), Error> - where - TXDMA: crate::i2c::TxDma, - { - let total_len = write.len(); - - let dma_transfer = unsafe { - let regs = T::regs(); - regs.cr1().modify(|w| { - w.set_txdmaen(true); - if first_slice { - w.set_tcie(true); - } - }); - let dst = regs.txdr().as_ptr() as *mut u8; - - let ch = &mut self.tx_dma; - let request = ch.request(); - Transfer::new_write(ch, request, write, dst, Default::default()) - }; - - let state = T::state(); - let mut remaining_len = total_len; - - let on_drop = OnDrop::new(|| { - let regs = T::regs(); - regs.cr1().modify(|w| { - if last_slice { - w.set_txdmaen(false); - } - w.set_tcie(false); - }) - }); - - poll_fn(|cx| { - state.waker.register(cx.waker()); - - let isr = T::regs().isr().read(); - if remaining_len == total_len { - if first_slice { - Self::master_write( - address, - total_len.min(255), - Stop::Software, - (total_len > 255) || !last_slice, - timeout, - )?; - } else { - Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, timeout)?; - T::regs().cr1().modify(|w| w.set_tcie(true)); - } - } else if !(isr.tcr() || isr.tc()) { - // poll_fn was woken without an interrupt present - return Poll::Pending; - } else if remaining_len == 0 { - return Poll::Ready(Ok(())); - } else { - let last_piece = (remaining_len <= 255) && last_slice; - - if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, timeout) { - return Poll::Ready(Err(e)); - } - T::regs().cr1().modify(|w| w.set_tcie(true)); - } - - remaining_len = remaining_len.saturating_sub(255); - Poll::Pending - }) - .await?; - - dma_transfer.await; - - if last_slice { - // This should be done already - self.wait_tc(timeout)?; - self.master_stop(); - } - - drop(on_drop); - - Ok(()) - } - - async fn read_dma_internal( - &mut self, - address: u8, - buffer: &mut [u8], - restart: bool, - timeout: Timeout, - ) -> Result<(), Error> - where - RXDMA: crate::i2c::RxDma, - { - let total_len = buffer.len(); - - let dma_transfer = unsafe { - let regs = T::regs(); - regs.cr1().modify(|w| { - w.set_rxdmaen(true); - w.set_tcie(true); - }); - let src = regs.rxdr().as_ptr() as *mut u8; - - let ch = &mut self.rx_dma; - let request = ch.request(); - Transfer::new_read(ch, request, src, buffer, Default::default()) - }; - - let state = T::state(); - let mut remaining_len = total_len; - - let on_drop = OnDrop::new(|| { - let regs = T::regs(); - regs.cr1().modify(|w| { - w.set_rxdmaen(false); - w.set_tcie(false); - }) - }); - - poll_fn(|cx| { - state.waker.register(cx.waker()); - - let isr = T::regs().isr().read(); - if remaining_len == total_len { - Self::master_read( - address, - total_len.min(255), - Stop::Software, - total_len > 255, - restart, - timeout, - )?; - } else if !(isr.tcr() || isr.tc()) { - // poll_fn was woken without an interrupt present - return Poll::Pending; - } else if remaining_len == 0 { - return Poll::Ready(Ok(())); - } else { - let last_piece = remaining_len <= 255; - - if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, timeout) { - return Poll::Ready(Err(e)); - } - T::regs().cr1().modify(|w| w.set_tcie(true)); - } - - remaining_len = remaining_len.saturating_sub(255); - Poll::Pending - }) - .await?; - - dma_transfer.await; - - // This should be done already - self.wait_tc(timeout)?; - self.master_stop(); - - drop(on_drop); - - Ok(()) - } - - // ========================= - // Async public API - - /// Write. - pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> - where - TXDMA: crate::i2c::TxDma, - { - let timeout = self.timeout(); - if write.is_empty() { - self.write_internal(address, write, true, timeout) - } else { - timeout - .with(self.write_dma_internal(address, write, true, true, timeout)) - .await - } - } - - /// Write multiple buffers. - /// - /// The buffers are concatenated in a single write transaction. - pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> - where - TXDMA: crate::i2c::TxDma, - { - let timeout = self.timeout(); - - if write.is_empty() { - return Err(Error::ZeroLengthTransfer); - } - let mut iter = write.iter(); - - let mut first = true; - let mut current = iter.next(); - while let Some(c) = current { - let next = iter.next(); - let is_last = next.is_none(); - - let fut = self.write_dma_internal(address, c, first, is_last, timeout); - timeout.with(fut).await?; - first = false; - current = next; - } - Ok(()) - } - - /// Read. - pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> - where - RXDMA: crate::i2c::RxDma, - { - let timeout = self.timeout(); - - if buffer.is_empty() { - self.read_internal(address, buffer, false, timeout) - } else { - let fut = self.read_dma_internal(address, buffer, false, timeout); - timeout.with(fut).await - } - } - - /// Write, restart, read. - pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> - where - TXDMA: super::TxDma, - RXDMA: super::RxDma, - { - let timeout = self.timeout(); - - if write.is_empty() { - self.write_internal(address, write, false, timeout)?; - } else { - let fut = self.write_dma_internal(address, write, true, true, timeout); - timeout.with(fut).await?; - } - - if read.is_empty() { - self.read_internal(address, read, true, timeout)?; - } else { - let fut = self.read_dma_internal(address, read, true, timeout); - timeout.with(fut).await?; - } - - Ok(()) - } - - /// Transaction with operations. - /// - /// Consecutive operations of same type are merged. See [transaction contract] for details. - /// - /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction - pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> - where - RXDMA: crate::i2c::RxDma, - TXDMA: crate::i2c::TxDma, - { - let _ = addr; - let _ = operations; - todo!() - } - // ========================= // Blocking public API @@ -684,7 +413,252 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } -impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> { +impl<'d, T: Instance> I2c<'d, T, Async> { + async fn write_dma_internal( + &mut self, + address: u8, + write: &[u8], + first_slice: bool, + last_slice: bool, + timeout: Timeout, + ) -> Result<(), Error> { + let total_len = write.len(); + + let dma_transfer = unsafe { + let regs = T::regs(); + regs.cr1().modify(|w| { + w.set_txdmaen(true); + if first_slice { + w.set_tcie(true); + } + }); + let dst = regs.txdr().as_ptr() as *mut u8; + + self.tx_dma.as_mut().unwrap().write(write, dst, Default::default()) + }; + + let state = T::state(); + let mut remaining_len = total_len; + + let on_drop = OnDrop::new(|| { + let regs = T::regs(); + regs.cr1().modify(|w| { + if last_slice { + w.set_txdmaen(false); + } + w.set_tcie(false); + }) + }); + + poll_fn(|cx| { + state.waker.register(cx.waker()); + + let isr = T::regs().isr().read(); + if remaining_len == total_len { + if first_slice { + Self::master_write( + address, + total_len.min(255), + Stop::Software, + (total_len > 255) || !last_slice, + timeout, + )?; + } else { + Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, timeout)?; + T::regs().cr1().modify(|w| w.set_tcie(true)); + } + } else if !(isr.tcr() || isr.tc()) { + // poll_fn was woken without an interrupt present + return Poll::Pending; + } else if remaining_len == 0 { + return Poll::Ready(Ok(())); + } else { + let last_piece = (remaining_len <= 255) && last_slice; + + if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, timeout) { + return Poll::Ready(Err(e)); + } + T::regs().cr1().modify(|w| w.set_tcie(true)); + } + + remaining_len = remaining_len.saturating_sub(255); + Poll::Pending + }) + .await?; + + dma_transfer.await; + + if last_slice { + // This should be done already + self.wait_tc(timeout)?; + self.master_stop(); + } + + drop(on_drop); + + Ok(()) + } + + async fn read_dma_internal( + &mut self, + address: u8, + buffer: &mut [u8], + restart: bool, + timeout: Timeout, + ) -> Result<(), Error> { + let total_len = buffer.len(); + + let dma_transfer = unsafe { + let regs = T::regs(); + regs.cr1().modify(|w| { + w.set_rxdmaen(true); + w.set_tcie(true); + }); + let src = regs.rxdr().as_ptr() as *mut u8; + + self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) + }; + + let state = T::state(); + let mut remaining_len = total_len; + + let on_drop = OnDrop::new(|| { + let regs = T::regs(); + regs.cr1().modify(|w| { + w.set_rxdmaen(false); + w.set_tcie(false); + }) + }); + + poll_fn(|cx| { + state.waker.register(cx.waker()); + + let isr = T::regs().isr().read(); + if remaining_len == total_len { + Self::master_read( + address, + total_len.min(255), + Stop::Software, + total_len > 255, + restart, + timeout, + )?; + } else if !(isr.tcr() || isr.tc()) { + // poll_fn was woken without an interrupt present + return Poll::Pending; + } else if remaining_len == 0 { + return Poll::Ready(Ok(())); + } else { + let last_piece = remaining_len <= 255; + + if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, timeout) { + return Poll::Ready(Err(e)); + } + T::regs().cr1().modify(|w| w.set_tcie(true)); + } + + remaining_len = remaining_len.saturating_sub(255); + Poll::Pending + }) + .await?; + + dma_transfer.await; + + // This should be done already + self.wait_tc(timeout)?; + self.master_stop(); + + drop(on_drop); + + Ok(()) + } + + // ========================= + // Async public API + + /// Write. + pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { + let timeout = self.timeout(); + if write.is_empty() { + self.write_internal(address, write, true, timeout) + } else { + timeout + .with(self.write_dma_internal(address, write, true, true, timeout)) + .await + } + } + + /// Write multiple buffers. + /// + /// The buffers are concatenated in a single write transaction. + pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> { + let timeout = self.timeout(); + + if write.is_empty() { + return Err(Error::ZeroLengthTransfer); + } + let mut iter = write.iter(); + + let mut first = true; + let mut current = iter.next(); + while let Some(c) = current { + let next = iter.next(); + let is_last = next.is_none(); + + let fut = self.write_dma_internal(address, c, first, is_last, timeout); + timeout.with(fut).await?; + first = false; + current = next; + } + Ok(()) + } + + /// Read. + pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + let timeout = self.timeout(); + + if buffer.is_empty() { + self.read_internal(address, buffer, false, timeout) + } else { + let fut = self.read_dma_internal(address, buffer, false, timeout); + timeout.with(fut).await + } + } + + /// Write, restart, read. + pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + let timeout = self.timeout(); + + if write.is_empty() { + self.write_internal(address, write, false, timeout)?; + } else { + let fut = self.write_dma_internal(address, write, true, true, timeout); + timeout.with(fut).await?; + } + + if read.is_empty() { + self.read_internal(address, read, true, timeout)?; + } else { + let fut = self.read_dma_internal(address, read, true, timeout); + timeout.with(fut).await?; + } + + Ok(()) + } + + /// Transaction with operations. + /// + /// Consecutive operations of same type are merged. See [transaction contract] for details. + /// + /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction + pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { + let _ = addr; + let _ = operations; + todo!() + } +} + +impl<'d, T: Instance, M: Mode> Drop for I2c<'d, T, M> { fn drop(&mut self) { T::disable(); } @@ -814,7 +788,7 @@ impl Timings { } } -impl<'d, T: Instance> SetConfig for I2c<'d, T> { +impl<'d, T: Instance, M: Mode> SetConfig for I2c<'d, T, M> { type Config = Hertz; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index c5a606b21..9b80dc1d0 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs @@ -2,6 +2,7 @@ use embassy_hal_internal::into_ref; use crate::gpio::{AFType, AnyPin, SealedPin}; +use crate::mode::Async; use crate::pac::spi::vals; use crate::spi::{Config as SpiConfig, *}; use crate::time::Hertz; @@ -152,15 +153,15 @@ impl Default for Config { } /// I2S driver. -pub struct I2S<'d, T: Instance, Tx, Rx> { - _peri: Spi<'d, T, Tx, Rx>, +pub struct I2S<'d, T: Instance> { + _peri: Spi<'d, T, Async>, sd: Option>, ws: Option>, ck: Option>, mck: Option>, } -impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> { +impl<'d, T: Instance> I2S<'d, T> { /// Note: Full-Duplex modes are not supported at this time pub fn new( peri: impl Peripheral

+ 'd, @@ -168,8 +169,8 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> { ws: impl Peripheral

> + 'd, ck: impl Peripheral

> + 'd, mck: impl Peripheral

> + 'd, - txdma: impl Peripheral

+ 'd, - rxdma: impl Peripheral

+ 'd, + txdma: impl Peripheral

> + 'd, + rxdma: impl Peripheral

> + 'd, freq: Hertz, config: Config, ) -> Self { @@ -265,24 +266,17 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> { } /// Write audio data. - pub async fn write(&mut self, data: &[W]) -> Result<(), Error> - where - Tx: TxDma, - { + pub async fn write(&mut self, data: &[W]) -> Result<(), Error> { self._peri.write(data).await } /// Read audio data. - pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> - where - Tx: TxDma, - Rx: RxDma, - { + pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { self._peri.read(data).await } } -impl<'d, T: Instance, Tx, Rx> Drop for I2S<'d, T, Tx, Rx> { +impl<'d, T: Instance> Drop for I2S<'d, T> { fn drop(&mut self) { self.sd.as_ref().map(|x| x.set_as_disconnected()); self.ws.as_ref().map(|x| x.set_as_disconnected()); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index ea17f8477..7d2b49ff4 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -15,8 +15,31 @@ mod fmt; include!(concat!(env!("OUT_DIR"), "/_macros.rs")); // Utilities +mod macros; pub mod time; -mod traits; +/// Operating modes for peripherals. +pub mod mode { + trait SealedMode {} + + /// Operating mode for a peripheral. + #[allow(private_bounds)] + pub trait Mode: SealedMode {} + + macro_rules! impl_mode { + ($name:ident) => { + impl SealedMode for $name {} + impl Mode for $name {} + }; + } + + /// Blocking mode. + pub struct Blocking; + /// Async mode. + pub struct Async; + + impl_mode!(Blocking); + impl_mode!(Async); +} // Always-present hardware pub mod dma; diff --git a/embassy-stm32/src/traits.rs b/embassy-stm32/src/macros.rs similarity index 77% rename from embassy-stm32/src/traits.rs rename to embassy-stm32/src/macros.rs index 13f695821..14137bc37 100644 --- a/embassy-stm32/src/traits.rs +++ b/embassy-stm32/src/macros.rs @@ -69,3 +69,29 @@ macro_rules! dma_trait_impl { } }; } + +macro_rules! new_dma { + ($name:ident) => {{ + let dma = $name.into_ref(); + let request = dma.request(); + Some(crate::dma::ChannelAndRequest { + channel: dma.map_into(), + request, + }) + }}; +} + +macro_rules! new_pin { + ($name:ident, $aftype:expr) => {{ + new_pin!($name, $aftype, crate::gpio::Speed::Medium, crate::gpio::Pull::None) + }}; + ($name:ident, $aftype:expr, $speed:expr) => { + new_pin!($name, $aftype, $speed, crate::gpio::Pull::None) + }; + ($name:ident, $aftype:expr, $speed:expr, $pull:expr) => {{ + let pin = $name.into_ref(); + pin.set_as_af_pull(pin.af_num(), $aftype, $pull); + pin.set_speed($speed); + Some(pin.map_into()) + }}; +} diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs index 9079ddd41..d7235ac7f 100644 --- a/embassy-stm32/src/rcc/l.rs +++ b/embassy-stm32/src/rcc/l.rs @@ -49,6 +49,7 @@ pub struct Config { pub sys: Sysclk, pub ahb_pre: AHBPrescaler, pub apb1_pre: APBPrescaler, + #[cfg(not(stm32u0))] pub apb2_pre: APBPrescaler, #[cfg(any(stm32wl5x, stm32wb))] pub core2_ahb_pre: AHBPrescaler, @@ -75,6 +76,7 @@ impl Default for Config { sys: Sysclk::MSI, ahb_pre: AHBPrescaler::DIV1, apb1_pre: APBPrescaler::DIV1, + #[cfg(not(stm32u0))] apb2_pre: APBPrescaler::DIV1, #[cfg(any(stm32wl5x, stm32wb))] core2_ahb_pre: AHBPrescaler::DIV1, @@ -130,7 +132,7 @@ pub const WPAN_DEFAULT: Config = Config { }; fn msi_enable(range: MSIRange) { - #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))] + #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))] RCC.cr().modify(|w| { #[cfg(not(stm32wb))] w.set_msirgsel(crate::pac::rcc::vals::Msirgsel::CR); @@ -240,7 +242,7 @@ pub(crate) unsafe fn init(config: Config) { let pll_input = PllInput { hse, hsi, - #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))] + #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))] msi, }; let pll = init_pll(PllInstance::Pll, config.pll, &pll_input); @@ -254,6 +256,10 @@ pub(crate) unsafe fn init(config: Config) { Sysclk::HSI => hsi.unwrap(), Sysclk::MSI => msi.unwrap(), Sysclk::PLL1_R => pll.r.unwrap(), + #[cfg(stm32u0)] + Sysclk::LSI | Sysclk::LSE => todo!(), + #[cfg(stm32u0)] + Sysclk::_RESERVED_6 | Sysclk::_RESERVED_7 => unreachable!(), }; #[cfg(rcc_l4plus)] @@ -263,6 +269,7 @@ pub(crate) unsafe fn init(config: Config) { let hclk1 = sys_clk / config.ahb_pre; let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre); + #[cfg(not(stm32u0))] let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre); #[cfg(any(stm32l4, stm32l5, stm32wlex))] let hclk2 = hclk1; @@ -315,6 +322,13 @@ pub(crate) unsafe fn init(config: Config) { ..=64_000_000 => 3, _ => 4, }; + #[cfg(stm32u0)] + let latency = match hclk1.0 { + // VOS RANGE1, others TODO. + ..=24_000_000 => 0, + ..=48_000_000 => 1, + _ => 2, + }; #[cfg(stm32l1)] FLASH.acr().write(|w| w.set_acc64(true)); @@ -326,7 +340,11 @@ pub(crate) unsafe fn init(config: Config) { RCC.cfgr().modify(|w| { w.set_sw(config.sys); w.set_hpre(config.ahb_pre); + #[cfg(stm32u0)] + w.set_ppre(config.apb1_pre); + #[cfg(not(stm32u0))] w.set_ppre1(config.apb1_pre); + #[cfg(not(stm32u0))] w.set_ppre2(config.apb2_pre); }); while RCC.cfgr().read().sws() != config.sys {} @@ -353,8 +371,10 @@ pub(crate) unsafe fn init(config: Config) { #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))] hclk3: Some(hclk3), pclk1: Some(pclk1), + #[cfg(not(stm32u0))] pclk2: Some(pclk2), pclk1_tim: Some(pclk1_tim), + #[cfg(not(stm32u0))] pclk2_tim: Some(pclk2_tim), #[cfg(stm32wl)] pclk3: Some(hclk3), @@ -408,7 +428,7 @@ fn msirange_to_hertz(range: MSIRange) -> Hertz { Hertz(32_768 * (1 << (range as u8 + 1))) } -#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))] +#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))] fn msirange_to_hertz(range: MSIRange) -> Hertz { match range { MSIRange::RANGE100K => Hertz(100_000), @@ -521,7 +541,7 @@ mod pll { } } -#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))] +#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))] mod pll { use super::{pll_enable, PllInstance}; pub use crate::pac::rcc::vals::{ diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs index d8604e07e..4b22a099d 100644 --- a/embassy-stm32/src/rcc/mco.rs +++ b/embassy-stm32/src/rcc/mco.rs @@ -52,7 +52,7 @@ macro_rules! impl_peri { }; } -#[cfg(any(rcc_c0, rcc_g0))] +#[cfg(any(rcc_c0, rcc_g0, rcc_u0))] #[allow(unused_imports)] use self::{McoSource as Mco1Source, McoSource as Mco2Source}; diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index c328344aa..a4e497fe7 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -25,7 +25,7 @@ pub use hsi48::*; #[cfg_attr(stm32g0, path = "g0.rs")] #[cfg_attr(stm32g4, path = "g4.rs")] #[cfg_attr(any(stm32h5, stm32h7), path = "h.rs")] -#[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl), path = "l.rs")] +#[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl, stm32u0), path = "l.rs")] #[cfg_attr(stm32u5, path = "u5.rs")] #[cfg_attr(stm32wba, path = "wba.rs")] mod _version; @@ -119,3 +119,21 @@ mod util { pub fn frequency() -> Hertz { T::frequency() } + +/// Enables and resets peripheral `T`. +/// +/// # Safety +/// +/// Peripheral must not be in use. +pub unsafe fn enable_and_reset() { + T::enable_and_reset(); +} + +/// Disables peripheral `T`. +/// +/// # Safety +/// +/// Peripheral must not be in use. +pub unsafe fn disable() { + T::disable(); +} diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 0b38c4288..c39ef1913 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -1,6 +1,7 @@ //! Serial Peripheral Interface (SPI) #![macro_use] +use core::marker::PhantomData; use core::ptr; use embassy_embedded_hal::SetConfig; @@ -8,8 +9,9 @@ use embassy_futures::join::join; use embassy_hal_internal::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; -use crate::dma::{slice_ptr_parts, word, Transfer}; -use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _}; +use crate::dma::{slice_ptr_parts, word, ChannelAndRequest}; +use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _, Speed}; +use crate::mode::{Async, Blocking, Mode as PeriMode}; use crate::pac::spi::{regs, vals, Spi as Regs}; use crate::rcc::RccPeripheral; use crate::time::Hertz; @@ -81,163 +83,37 @@ impl Config { BitOrder::MsbFirst => vals::Lsbfirst::MSBFIRST, } } -} + fn sck_pull_mode(&self) -> Pull { + match self.mode.polarity { + Polarity::IdleLow => Pull::Down, + Polarity::IdleHigh => Pull::Up, + } + } +} /// SPI driver. -pub struct Spi<'d, T: Instance, Tx, Rx> { +pub struct Spi<'d, T: Instance, M: PeriMode> { _peri: PeripheralRef<'d, T>, sck: Option>, mosi: Option>, miso: Option>, - txdma: PeripheralRef<'d, Tx>, - rxdma: PeripheralRef<'d, Rx>, + tx_dma: Option>, + rx_dma: Option>, + _phantom: PhantomData, current_word_size: word_impl::Config, } -impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { - /// Create a new SPI driver. - pub fn new( - peri: impl Peripheral

+ 'd, - sck: impl Peripheral

> + 'd, - mosi: impl Peripheral

> + 'd, - miso: impl Peripheral

> + 'd, - txdma: impl Peripheral

+ 'd, - rxdma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(peri, sck, mosi, miso); - - let sck_pull_mode = match config.mode.polarity { - Polarity::IdleLow => Pull::Down, - Polarity::IdleHigh => Pull::Up, - }; - - sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, sck_pull_mode); - sck.set_speed(crate::gpio::Speed::VeryHigh); - mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull); - mosi.set_speed(crate::gpio::Speed::VeryHigh); - miso.set_as_af(miso.af_num(), AFType::Input); - miso.set_speed(crate::gpio::Speed::VeryHigh); - - Self::new_inner( - peri, - Some(sck.map_into()), - Some(mosi.map_into()), - Some(miso.map_into()), - txdma, - rxdma, - config, - ) - } - - /// Create a new SPI driver, in RX-only mode (only MISO pin, no MOSI). - pub fn new_rxonly( - peri: impl Peripheral

+ 'd, - sck: impl Peripheral

> + 'd, - miso: impl Peripheral

> + 'd, - txdma: impl Peripheral

+ 'd, // TODO remove - rxdma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(sck, miso); - sck.set_as_af(sck.af_num(), AFType::OutputPushPull); - sck.set_speed(crate::gpio::Speed::VeryHigh); - miso.set_as_af(miso.af_num(), AFType::Input); - miso.set_speed(crate::gpio::Speed::VeryHigh); - - Self::new_inner( - peri, - Some(sck.map_into()), - None, - Some(miso.map_into()), - txdma, - rxdma, - config, - ) - } - - /// Create a new SPI driver, in TX-only mode (only MOSI pin, no MISO). - pub fn new_txonly( - peri: impl Peripheral

+ 'd, - sck: impl Peripheral

> + 'd, - mosi: impl Peripheral

> + 'd, - txdma: impl Peripheral

+ 'd, - rxdma: impl Peripheral

+ 'd, // TODO remove - config: Config, - ) -> Self { - into_ref!(sck, mosi); - sck.set_as_af(sck.af_num(), AFType::OutputPushPull); - sck.set_speed(crate::gpio::Speed::VeryHigh); - mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull); - mosi.set_speed(crate::gpio::Speed::VeryHigh); - - Self::new_inner( - peri, - Some(sck.map_into()), - Some(mosi.map_into()), - None, - txdma, - rxdma, - config, - ) - } - - /// Create a new SPI driver, in TX-only mode, without SCK pin. - /// - /// This can be useful for bit-banging non-SPI protocols. - pub fn new_txonly_nosck( - peri: impl Peripheral

+ 'd, - mosi: impl Peripheral

> + 'd, - txdma: impl Peripheral

+ 'd, - rxdma: impl Peripheral

+ 'd, // TODO: remove - config: Config, - ) -> Self { - into_ref!(mosi); - mosi.set_as_af_pull(mosi.af_num(), AFType::OutputPushPull, Pull::Down); - mosi.set_speed(crate::gpio::Speed::Medium); - - Self::new_inner(peri, None, Some(mosi.map_into()), None, txdma, rxdma, config) - } - - #[cfg(stm32wl)] - /// Useful for on chip peripherals like SUBGHZ which are hardwired. - pub fn new_subghz( - peri: impl Peripheral

+ 'd, - txdma: impl Peripheral

+ 'd, - rxdma: impl Peripheral

+ 'd, - ) -> Self { - // see RM0453 rev 1 section 7.2.13 page 291 - // The SUBGHZSPI_SCK frequency is obtained by PCLK3 divided by two. - // The SUBGHZSPI_SCK clock maximum speed must not exceed 16 MHz. - let pclk3_freq = ::frequency().0; - let freq = Hertz(core::cmp::min(pclk3_freq / 2, 16_000_000)); - let mut config = Config::default(); - config.mode = MODE_0; - config.bit_order = BitOrder::MsbFirst; - config.frequency = freq; - Self::new_inner(peri, None, None, None, txdma, rxdma, config) - } - - #[allow(dead_code)] - pub(crate) fn new_internal( - peri: impl Peripheral

+ 'd, - txdma: impl Peripheral

+ 'd, - rxdma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - Self::new_inner(peri, None, None, None, txdma, rxdma, config) - } - +impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { fn new_inner( peri: impl Peripheral

+ 'd, sck: Option>, mosi: Option>, miso: Option>, - txdma: impl Peripheral

+ 'd, - rxdma: impl Peripheral

+ 'd, + tx_dma: Option>, + rx_dma: Option>, config: Config, ) -> Self { - into_ref!(peri, txdma, rxdma); + into_ref!(peri); let pclk = T::frequency(); let freq = config.frequency; @@ -333,9 +209,10 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { sck, mosi, miso, - txdma, - rxdma, + tx_dma, + rx_dma, current_word_size: ::CONFIG, + _phantom: PhantomData, } } @@ -462,169 +339,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { self.current_word_size = word_size; } - /// SPI write, using DMA. - pub async fn write(&mut self, data: &[W]) -> Result<(), Error> - where - Tx: TxDma, - { - if data.is_empty() { - return Ok(()); - } - - self.set_word_size(W::CONFIG); - T::REGS.cr1().modify(|w| { - w.set_spe(false); - }); - - let tx_request = self.txdma.request(); - let tx_dst = T::REGS.tx_ptr(); - let tx_f = unsafe { Transfer::new_write(&mut self.txdma, tx_request, data, tx_dst, Default::default()) }; - - set_txdmaen(T::REGS, true); - T::REGS.cr1().modify(|w| { - w.set_spe(true); - }); - #[cfg(any(spi_v3, spi_v4, spi_v5))] - T::REGS.cr1().modify(|w| { - w.set_cstart(true); - }); - - tx_f.await; - - finish_dma(T::REGS); - - Ok(()) - } - - /// SPI read, using DMA. - pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> - where - Tx: TxDma, - Rx: RxDma, - { - if data.is_empty() { - return Ok(()); - } - - self.set_word_size(W::CONFIG); - T::REGS.cr1().modify(|w| { - w.set_spe(false); - }); - - // SPIv3 clears rxfifo on SPE=0 - #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] - flush_rx_fifo(T::REGS); - - set_rxdmaen(T::REGS, true); - - let clock_byte_count = data.len(); - - let rx_request = self.rxdma.request(); - let rx_src = T::REGS.rx_ptr(); - let rx_f = unsafe { Transfer::new_read(&mut self.rxdma, rx_request, rx_src, data, Default::default()) }; - - let tx_request = self.txdma.request(); - let tx_dst = T::REGS.tx_ptr(); - let clock_byte = 0x00u8; - let tx_f = unsafe { - Transfer::new_write_repeated( - &mut self.txdma, - tx_request, - &clock_byte, - clock_byte_count, - tx_dst, - Default::default(), - ) - }; - - set_txdmaen(T::REGS, true); - T::REGS.cr1().modify(|w| { - w.set_spe(true); - }); - #[cfg(any(spi_v3, spi_v4, spi_v5))] - T::REGS.cr1().modify(|w| { - w.set_cstart(true); - }); - - join(tx_f, rx_f).await; - - finish_dma(T::REGS); - - Ok(()) - } - - async fn transfer_inner(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error> - where - Tx: TxDma, - Rx: RxDma, - { - let (_, rx_len) = slice_ptr_parts(read); - let (_, tx_len) = slice_ptr_parts(write); - assert_eq!(rx_len, tx_len); - if rx_len == 0 { - return Ok(()); - } - - self.set_word_size(W::CONFIG); - T::REGS.cr1().modify(|w| { - w.set_spe(false); - }); - - // SPIv3 clears rxfifo on SPE=0 - #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] - flush_rx_fifo(T::REGS); - - set_rxdmaen(T::REGS, true); - - let rx_request = self.rxdma.request(); - let rx_src = T::REGS.rx_ptr(); - let rx_f = unsafe { Transfer::new_read_raw(&mut self.rxdma, rx_request, rx_src, read, Default::default()) }; - - let tx_request = self.txdma.request(); - let tx_dst = T::REGS.tx_ptr(); - let tx_f = unsafe { Transfer::new_write_raw(&mut self.txdma, tx_request, write, tx_dst, Default::default()) }; - - set_txdmaen(T::REGS, true); - T::REGS.cr1().modify(|w| { - w.set_spe(true); - }); - #[cfg(any(spi_v3, spi_v4, spi_v5))] - T::REGS.cr1().modify(|w| { - w.set_cstart(true); - }); - - join(tx_f, rx_f).await; - - finish_dma(T::REGS); - - Ok(()) - } - - /// Bidirectional transfer, using DMA. - /// - /// This transfers both buffers at the same time, so it is NOT equivalent to `write` followed by `read`. - /// - /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored. - /// If `write` is shorter it is padded with zero bytes. - pub async fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> - where - Tx: TxDma, - Rx: RxDma, - { - self.transfer_inner(read, write).await - } - - /// In-place bidirectional transfer, using DMA. - /// - /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. - pub async fn transfer_in_place(&mut self, data: &mut [W]) -> Result<(), Error> - where - Tx: TxDma, - Rx: RxDma, - { - self.transfer_inner(data, data).await - } - /// Blocking write. pub fn blocking_write(&mut self, words: &[W]) -> Result<(), Error> { T::REGS.cr1().modify(|w| w.set_spe(true)); @@ -682,7 +396,334 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } } -impl<'d, T: Instance, Tx, Rx> Drop for Spi<'d, T, Tx, Rx> { +impl<'d, T: Instance> Spi<'d, T, Blocking> { + /// Create a new blocking SPI driver. + pub fn new_blocking( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + mosi: impl Peripheral

> + 'd, + miso: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()), + new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(miso, AFType::Input, Speed::VeryHigh), + None, + None, + config, + ) + } + + /// Create a new blocking SPI driver, in RX-only mode (only MISO pin, no MOSI). + pub fn new_blocking_rxonly( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + miso: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()), + None, + new_pin!(miso, AFType::Input, Speed::VeryHigh), + None, + None, + config, + ) + } + + /// Create a new blocking SPI driver, in TX-only mode (only MOSI pin, no MISO). + pub fn new_blocking_txonly( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + mosi: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()), + new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh), + None, + None, + None, + config, + ) + } + + /// Create a new SPI driver, in TX-only mode, without SCK pin. + /// + /// This can be useful for bit-banging non-SPI protocols. + pub fn new_blocking_txonly_nosck( + peri: impl Peripheral

+ 'd, + mosi: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + None, + new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh), + None, + None, + None, + config, + ) + } +} + +impl<'d, T: Instance> Spi<'d, T, Async> { + /// Create a new SPI driver. + pub fn new( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + mosi: impl Peripheral

> + 'd, + miso: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()), + new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(miso, AFType::Input, Speed::VeryHigh), + new_dma!(tx_dma), + new_dma!(rx_dma), + config, + ) + } + + /// Create a new SPI driver, in RX-only mode (only MISO pin, no MOSI). + pub fn new_rxonly( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + miso: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()), + None, + new_pin!(miso, AFType::Input, Speed::VeryHigh), + None, + new_dma!(rx_dma), + config, + ) + } + + /// Create a new SPI driver, in TX-only mode (only MOSI pin, no MISO). + pub fn new_txonly( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + mosi: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()), + new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh), + None, + new_dma!(tx_dma), + None, + config, + ) + } + + /// Create a new SPI driver, in TX-only mode, without SCK pin. + /// + /// This can be useful for bit-banging non-SPI protocols. + pub fn new_txonly_nosck( + peri: impl Peripheral

+ 'd, + mosi: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + None, + new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh), + None, + new_dma!(tx_dma), + None, + config, + ) + } + + #[cfg(stm32wl)] + /// Useful for on chip peripherals like SUBGHZ which are hardwired. + pub fn new_subghz( + peri: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, + ) -> Self { + // see RM0453 rev 1 section 7.2.13 page 291 + // The SUBGHZSPI_SCK frequency is obtained by PCLK3 divided by two. + // The SUBGHZSPI_SCK clock maximum speed must not exceed 16 MHz. + let pclk3_freq = ::frequency().0; + let freq = Hertz(core::cmp::min(pclk3_freq / 2, 16_000_000)); + let mut config = Config::default(); + config.mode = MODE_0; + config.bit_order = BitOrder::MsbFirst; + config.frequency = freq; + + Self::new_inner(peri, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config) + } + + #[allow(dead_code)] + pub(crate) fn new_internal( + peri: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner(peri, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config) + } + + /// SPI write, using DMA. + pub async fn write(&mut self, data: &[W]) -> Result<(), Error> { + if data.is_empty() { + return Ok(()); + } + + self.set_word_size(W::CONFIG); + T::REGS.cr1().modify(|w| { + w.set_spe(false); + }); + + let tx_dst = T::REGS.tx_ptr(); + let tx_f = unsafe { self.tx_dma.as_mut().unwrap().write(data, tx_dst, Default::default()) }; + + set_txdmaen(T::REGS, true); + T::REGS.cr1().modify(|w| { + w.set_spe(true); + }); + #[cfg(any(spi_v3, spi_v4, spi_v5))] + T::REGS.cr1().modify(|w| { + w.set_cstart(true); + }); + + tx_f.await; + + finish_dma(T::REGS); + + Ok(()) + } + + /// SPI read, using DMA. + pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { + if data.is_empty() { + return Ok(()); + } + + self.set_word_size(W::CONFIG); + T::REGS.cr1().modify(|w| { + w.set_spe(false); + }); + + // SPIv3 clears rxfifo on SPE=0 + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] + flush_rx_fifo(T::REGS); + + set_rxdmaen(T::REGS, true); + + let clock_byte_count = data.len(); + + let rx_src = T::REGS.rx_ptr(); + let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read(rx_src, data, Default::default()) }; + + let tx_dst = T::REGS.tx_ptr(); + let clock_byte = 0x00u8; + let tx_f = unsafe { + self.tx_dma + .as_mut() + .unwrap() + .write_repeated(&clock_byte, clock_byte_count, tx_dst, Default::default()) + }; + + set_txdmaen(T::REGS, true); + T::REGS.cr1().modify(|w| { + w.set_spe(true); + }); + #[cfg(any(spi_v3, spi_v4, spi_v5))] + T::REGS.cr1().modify(|w| { + w.set_cstart(true); + }); + + join(tx_f, rx_f).await; + + finish_dma(T::REGS); + + Ok(()) + } + + async fn transfer_inner(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error> { + let (_, rx_len) = slice_ptr_parts(read); + let (_, tx_len) = slice_ptr_parts(write); + assert_eq!(rx_len, tx_len); + if rx_len == 0 { + return Ok(()); + } + + self.set_word_size(W::CONFIG); + T::REGS.cr1().modify(|w| { + w.set_spe(false); + }); + + // SPIv3 clears rxfifo on SPE=0 + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] + flush_rx_fifo(T::REGS); + + set_rxdmaen(T::REGS, true); + + let rx_src = T::REGS.rx_ptr(); + let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read_raw(rx_src, read, Default::default()) }; + + let tx_dst = T::REGS.tx_ptr(); + let tx_f = unsafe { + self.tx_dma + .as_mut() + .unwrap() + .write_raw(write, tx_dst, Default::default()) + }; + + set_txdmaen(T::REGS, true); + T::REGS.cr1().modify(|w| { + w.set_spe(true); + }); + #[cfg(any(spi_v3, spi_v4, spi_v5))] + T::REGS.cr1().modify(|w| { + w.set_cstart(true); + }); + + join(tx_f, rx_f).await; + + finish_dma(T::REGS); + + Ok(()) + } + + /// Bidirectional transfer, using DMA. + /// + /// This transfers both buffers at the same time, so it is NOT equivalent to `write` followed by `read`. + /// + /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored. + /// If `write` is shorter it is padded with zero bytes. + pub async fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { + self.transfer_inner(read, write).await + } + + /// In-place bidirectional transfer, using DMA. + /// + /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. + pub async fn transfer_in_place(&mut self, data: &mut [W]) -> Result<(), Error> { + self.transfer_inner(data, data).await + } +} + +impl<'d, T: Instance, M: PeriMode> Drop for Spi<'d, T, M> { fn drop(&mut self) { self.sck.as_ref().map(|x| x.set_as_disconnected()); self.mosi.as_ref().map(|x| x.set_as_disconnected()); @@ -735,18 +776,22 @@ trait RegsExt { impl RegsExt for Regs { fn tx_ptr(&self) -> *mut W { - #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] + #[cfg(any(spi_v1, spi_f1))] let dr = self.dr(); + #[cfg(spi_v2)] + let dr = self.dr16(); #[cfg(any(spi_v3, spi_v4, spi_v5))] - let dr = self.txdr(); + let dr = self.txdr32(); dr.as_ptr() as *mut W } fn rx_ptr(&self) -> *mut W { - #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] + #[cfg(any(spi_v1, spi_f1))] let dr = self.dr(); + #[cfg(spi_v2)] + let dr = self.dr16(); #[cfg(any(spi_v3, spi_v4, spi_v5))] - let dr = self.rxdr(); + let dr = self.rxdr32(); dr.as_ptr() as *mut W } } @@ -815,11 +860,14 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> { fn flush_rx_fifo(regs: Regs) { #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] while regs.sr().read().rxne() { + #[cfg(not(spi_v2))] let _ = regs.dr().read(); + #[cfg(spi_v2)] + let _ = regs.dr16().read(); } #[cfg(any(spi_v3, spi_v4, spi_v5))] while regs.sr().read().rxp() { - let _ = regs.rxdr().read(); + let _ = regs.rxdr32().read(); } } @@ -893,7 +941,7 @@ fn transfer_word(regs: Regs, tx_word: W) -> Result { // some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289 macro_rules! impl_blocking { ($w:ident) => { - impl<'d, T: Instance, Tx, Rx> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, T, Tx, Rx> { + impl<'d, T: Instance, M: PeriMode> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, T, M> { type Error = Error; fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> { @@ -901,7 +949,7 @@ macro_rules! impl_blocking { } } - impl<'d, T: Instance, Tx, Rx> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, T, Tx, Rx> { + impl<'d, T: Instance, M: PeriMode> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, T, M> { type Error = Error; fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> { @@ -915,11 +963,11 @@ macro_rules! impl_blocking { impl_blocking!(u8); impl_blocking!(u16); -impl<'d, T: Instance, Tx, Rx> embedded_hal_1::spi::ErrorType for Spi<'d, T, Tx, Rx> { +impl<'d, T: Instance, M: PeriMode> embedded_hal_1::spi::ErrorType for Spi<'d, T, M> { type Error = Error; } -impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBus for Spi<'d, T, Tx, Rx> { +impl<'d, T: Instance, W: Word, M: PeriMode> embedded_hal_1::spi::SpiBus for Spi<'d, T, M> { fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } @@ -952,7 +1000,7 @@ impl embedded_hal_1::spi::Error for Error { } } -impl<'d, T: Instance, Tx: TxDma, Rx: RxDma, W: Word> embedded_hal_async::spi::SpiBus for Spi<'d, T, Tx, Rx> { +impl<'d, T: Instance, W: Word> embedded_hal_async::spi::SpiBus for Spi<'d, T, Async> { async fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } @@ -1087,7 +1135,7 @@ foreach_peripheral!( }; ); -impl<'d, T: Instance, Tx, Rx> SetConfig for Spi<'d, T, Tx, Rx> { +impl<'d, T: Instance, M: PeriMode> SetConfig for Spi<'d, T, M> { type Config = Config; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 7c0523a25..d21e5c47c 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -13,9 +13,10 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use futures::future::{select, Either}; -use crate::dma::{NoDma, Transfer}; -use crate::gpio::AFType; +use crate::dma::ChannelAndRequest; +use crate::gpio::{AFType, AnyPin, SealedPin}; use crate::interrupt::typelevel::Interrupt; +use crate::mode::{Async, Blocking, Mode}; #[allow(unused_imports)] #[cfg(not(any(usart_v1, usart_v2)))] use crate::pac::usart::regs::Isr as Sr; @@ -162,6 +163,26 @@ pub struct Config { /// Set this to true to invert RX pin signal values (VDD =0/mark, Gnd = 1/idle). #[cfg(any(usart_v3, usart_v4))] pub invert_rx: bool, + + // private: set by new_half_duplex, not by the user. + half_duplex: bool, +} + +impl Config { + fn tx_af(&self) -> AFType { + #[cfg(any(usart_v3, usart_v4))] + if self.swap_rx_tx { + return AFType::Input; + }; + AFType::OutputPushPull + } + fn rx_af(&self) -> AFType { + #[cfg(any(usart_v3, usart_v4))] + if self.swap_rx_tx { + return AFType::OutputPushPull; + }; + AFType::Input + } } impl Default for Config { @@ -181,6 +202,7 @@ impl Default for Config { invert_tx: false, #[cfg(any(usart_v3, usart_v4))] invert_rx: false, + half_duplex: false, } } } @@ -217,12 +239,12 @@ enum ReadCompletionEvent { /// /// See [`UartRx`] for more details, and see [`BufferedUart`] and [`RingBufferedUartRx`] /// as alternatives that do provide the necessary guarantees for `embedded_io::Read`. -pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> { - tx: UartTx<'d, T, TxDma>, - rx: UartRx<'d, T, RxDma>, +pub struct Uart<'d, T: BasicInstance, M: Mode> { + tx: UartTx<'d, T, M>, + rx: UartRx<'d, T, M>, } -impl<'d, T: BasicInstance, TxDma, RxDma> SetConfig for Uart<'d, T, TxDma, RxDma> { +impl<'d, T: BasicInstance, M: Mode> SetConfig for Uart<'d, T, M> { type Config = Config; type ConfigError = ConfigError; @@ -236,12 +258,15 @@ impl<'d, T: BasicInstance, TxDma, RxDma> SetConfig for Uart<'d, T, TxDma, RxDma> /// /// Can be obtained from [`Uart::split`], or can be constructed independently, /// if you do not need the receiving half of the driver. -pub struct UartTx<'d, T: BasicInstance, TxDma = NoDma> { - phantom: PhantomData<&'d mut T>, - tx_dma: PeripheralRef<'d, TxDma>, +pub struct UartTx<'d, T: BasicInstance, M: Mode> { + _phantom: PhantomData<(T, M)>, + tx: Option>, + cts: Option>, + de: Option>, + tx_dma: Option>, } -impl<'d, T: BasicInstance, TxDma> SetConfig for UartTx<'d, T, TxDma> { +impl<'d, T: BasicInstance, M: Mode> SetConfig for UartTx<'d, T, M> { type Config = Config; type ConfigError = ConfigError; @@ -279,15 +304,17 @@ impl<'d, T: BasicInstance, TxDma> SetConfig for UartTx<'d, T, TxDma> { /// store data received between calls. /// /// Also see [this github comment](https://github.com/embassy-rs/embassy/pull/2185#issuecomment-1810047043). -pub struct UartRx<'d, T: BasicInstance, RxDma = NoDma> { - _peri: PeripheralRef<'d, T>, - rx_dma: PeripheralRef<'d, RxDma>, +pub struct UartRx<'d, T: BasicInstance, M: Mode> { + _phantom: PhantomData<(T, M)>, + rx: Option>, + rts: Option>, + rx_dma: Option>, detect_previous_overrun: bool, #[cfg(any(usart_v1, usart_v2))] buffered_sr: stm32_metapac::usart::regs::Sr, } -impl<'d, T: BasicInstance, RxDma> SetConfig for UartRx<'d, T, RxDma> { +impl<'d, T: BasicInstance, M: Mode> SetConfig for UartRx<'d, T, M> { type Config = Config; type ConfigError = ConfigError; @@ -296,17 +323,21 @@ impl<'d, T: BasicInstance, RxDma> SetConfig for UartRx<'d, T, RxDma> { } } -impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { +impl<'d, T: BasicInstance> UartTx<'d, T, Async> { /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. pub fn new( peri: impl Peripheral

+ 'd, tx: impl Peripheral

> + 'd, - tx_dma: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, config: Config, ) -> Result { - T::enable_and_reset(); - - Self::new_inner(peri, tx, tx_dma, config) + Self::new_inner( + peri, + new_pin!(tx, AFType::OutputPushPull), + None, + new_dma!(tx_dma), + config, + ) } /// Create a new tx-only UART with a clear-to-send pin @@ -314,40 +345,86 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { peri: impl Peripheral

+ 'd, tx: impl Peripheral

> + 'd, cts: impl Peripheral

> + 'd, - tx_dma: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, config: Config, ) -> Result { - into_ref!(cts); - - T::enable_and_reset(); - - cts.set_as_af(cts.af_num(), AFType::Input); - T::regs().cr3().write(|w| { - w.set_ctse(true); - }); - Self::new_inner(peri, tx, tx_dma, config) + Self::new_inner( + peri, + new_pin!(tx, AFType::OutputPushPull), + new_pin!(cts, AFType::Input), + new_dma!(tx_dma), + config, + ) } - fn new_inner( - _peri: impl Peripheral

+ 'd, + /// Initiate an asynchronous UART write + pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + let ch = self.tx_dma.as_mut().unwrap(); + T::regs().cr3().modify(|reg| { + reg.set_dmat(true); + }); + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + let transfer = unsafe { ch.write(buffer, tdr(T::regs()), Default::default()) }; + transfer.await; + Ok(()) + } +} + +impl<'d, T: BasicInstance> UartTx<'d, T, Blocking> { + /// Create a new blocking tx-only UART with no hardware flow control. + /// + /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. + pub fn new_blocking( + peri: impl Peripheral

+ 'd, tx: impl Peripheral

> + 'd, - tx_dma: impl Peripheral

+ 'd, config: Config, ) -> Result { - into_ref!(_peri, tx, tx_dma); + Self::new_inner(peri, new_pin!(tx, AFType::OutputPushPull), None, None, config) + } + + /// Create a new blocking tx-only UART with a clear-to-send pin + pub fn new_blocking_with_cts( + peri: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(tx, AFType::OutputPushPull), + new_pin!(cts, AFType::Input), + None, + config, + ) + } +} + +impl<'d, T: BasicInstance, M: Mode> UartTx<'d, T, M> { + fn new_inner( + _peri: impl Peripheral

+ 'd, + tx: Option>, + cts: Option>, + tx_dma: Option>, + config: Config, + ) -> Result { + T::enable_and_reset(); let r = T::regs(); - - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - + r.cr3().modify(|w| { + w.set_ctse(cts.is_some()); + }); configure(r, &config, T::frequency(), T::KIND, false, true)?; // create state once! let _s = T::state(); Ok(Self { + tx, + cts, + de: None, tx_dma, - phantom: PhantomData, + _phantom: PhantomData, }) } @@ -356,23 +433,6 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { reconfigure::(config) } - /// Initiate an asynchronous UART write - pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> - where - TxDma: crate::usart::TxDma, - { - let ch = &mut self.tx_dma; - let request = ch.request(); - T::regs().cr3().modify(|reg| { - reg.set_dmat(true); - }); - // If we don't assign future to a variable, the data register pointer - // is held across an await and makes the future non-Send. - let transfer = unsafe { Transfer::new_write(ch, request, buffer, tdr(T::regs()), Default::default()) }; - transfer.await; - Ok(()) - } - /// Perform a blocking UART write pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { let r = T::regs(); @@ -391,18 +451,18 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { } } -impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { +impl<'d, T: BasicInstance> UartRx<'d, T, Async> { + /// Create a new rx-only UART with no hardware flow control. + /// /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. pub fn new( peri: impl Peripheral

+ 'd, _irq: impl interrupt::typelevel::Binding> + 'd, rx: impl Peripheral

> + 'd, - rx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

> + 'd, config: Config, ) -> Result { - T::enable_and_reset(); - - Self::new_inner(peri, rx, rx_dma, config) + Self::new_inner(peri, new_pin!(rx, AFType::Input), None, new_dma!(rx_dma), config) } /// Create a new rx-only UART with a request-to-send pin @@ -411,143 +471,27 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { _irq: impl interrupt::typelevel::Binding> + 'd, rx: impl Peripheral

> + 'd, rts: impl Peripheral

> + 'd, - rx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

> + 'd, config: Config, ) -> Result { - into_ref!(rts); - - T::enable_and_reset(); - - rts.set_as_af(rts.af_num(), AFType::OutputPushPull); - T::regs().cr3().write(|w| { - w.set_rtse(true); - }); - - Self::new_inner(peri, rx, rx_dma, config) - } - - fn new_inner( - peri: impl Peripheral

+ 'd, - rx: impl Peripheral

> + 'd, - rx_dma: impl Peripheral

+ 'd, - config: Config, - ) -> Result { - into_ref!(peri, rx, rx_dma); - - let r = T::regs(); - - rx.set_as_af(rx.af_num(), AFType::Input); - - configure(r, &config, T::frequency(), T::KIND, true, false)?; - - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; - - // create state once! - let _s = T::state(); - - Ok(Self { - _peri: peri, - rx_dma, - detect_previous_overrun: config.detect_previous_overrun, - #[cfg(any(usart_v1, usart_v2))] - buffered_sr: stm32_metapac::usart::regs::Sr(0), - }) - } - - /// Reconfigure the driver - pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { - reconfigure::(config) - } - - #[cfg(any(usart_v1, usart_v2))] - fn check_rx_flags(&mut self) -> Result { - let r = T::regs(); - loop { - // Handle all buffered error flags. - if self.buffered_sr.pe() { - self.buffered_sr.set_pe(false); - return Err(Error::Parity); - } else if self.buffered_sr.fe() { - self.buffered_sr.set_fe(false); - return Err(Error::Framing); - } else if self.buffered_sr.ne() { - self.buffered_sr.set_ne(false); - return Err(Error::Noise); - } else if self.buffered_sr.ore() { - self.buffered_sr.set_ore(false); - return Err(Error::Overrun); - } else if self.buffered_sr.rxne() { - self.buffered_sr.set_rxne(false); - return Ok(true); - } else { - // No error flags from previous iterations were set: Check the actual status register - let sr = r.sr().read(); - if !sr.rxne() { - return Ok(false); - } - - // Buffer the status register and let the loop handle the error flags. - self.buffered_sr = sr; - } - } - } - - #[cfg(any(usart_v3, usart_v4))] - fn check_rx_flags(&mut self) -> Result { - let r = T::regs(); - let sr = r.isr().read(); - if sr.pe() { - r.icr().write(|w| w.set_pe(true)); - return Err(Error::Parity); - } else if sr.fe() { - r.icr().write(|w| w.set_fe(true)); - return Err(Error::Framing); - } else if sr.ne() { - r.icr().write(|w| w.set_ne(true)); - return Err(Error::Noise); - } else if sr.ore() { - r.icr().write(|w| w.set_ore(true)); - return Err(Error::Overrun); - } - Ok(sr.rxne()) + Self::new_inner( + peri, + new_pin!(rx, AFType::Input), + new_pin!(rts, AFType::OutputPushPull), + new_dma!(rx_dma), + config, + ) } /// Initiate an asynchronous UART read - pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> - where - RxDma: crate::usart::RxDma, - { + pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { self.inner_read(buffer, false).await?; Ok(()) } - /// Read a single u8 if there is one available, otherwise return WouldBlock - pub fn nb_read(&mut self) -> Result> { - let r = T::regs(); - if self.check_rx_flags()? { - Ok(unsafe { rdr(r).read_volatile() }) - } else { - Err(nb::Error::WouldBlock) - } - } - - /// Perform a blocking read into `buffer` - pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - let r = T::regs(); - for b in buffer { - while !self.check_rx_flags()? {} - unsafe { *b = rdr(r).read_volatile() } - } - Ok(()) - } - /// Initiate an asynchronous read with idle line detection enabled - pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result - where - RxDma: crate::usart::RxDma, - { + pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { self.inner_read(buffer, true).await } @@ -555,10 +499,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { &mut self, buffer: &mut [u8], enable_idle_line_detection: bool, - ) -> Result - where - RxDma: crate::usart::RxDma, - { + ) -> Result { let r = T::regs(); // make sure USART state is restored to neutral state when this future is dropped @@ -581,15 +522,14 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { }); }); - let ch = &mut self.rx_dma; - let request = ch.request(); + let ch = self.rx_dma.as_mut().unwrap(); let buffer_len = buffer.len(); // Start USART DMA // will not do anything yet because DMAR is not yet set // future which will complete when DMA Read request completes - let transfer = unsafe { Transfer::new_read(ch, request, rdr(T::regs()), buffer, Default::default()) }; + let transfer = unsafe { ch.read(rdr(T::regs()), buffer, Default::default()) }; // clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer if !self.detect_previous_overrun { @@ -732,10 +672,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { r } - async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result - where - RxDma: crate::usart::RxDma, - { + async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result { if buffer.is_empty() { return Ok(0); } else if buffer.len() > 0xFFFF { @@ -755,34 +692,186 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { } } -impl<'d, T: BasicInstance, TxDma> Drop for UartTx<'d, T, TxDma> { +impl<'d, T: BasicInstance> UartRx<'d, T, Blocking> { + /// Create a new rx-only UART with no hardware flow control. + /// + /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. + pub fn new_blocking( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner(peri, new_pin!(rx, AFType::Input), None, None, config) + } + + /// Create a new rx-only UART with a request-to-send pin + pub fn new_blocking_with_rts( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, AFType::Input), + new_pin!(rts, AFType::OutputPushPull), + None, + config, + ) + } +} + +impl<'d, T: BasicInstance, M: Mode> UartRx<'d, T, M> { + fn new_inner( + _peri: impl Peripheral

+ 'd, + rx: Option>, + rts: Option>, + rx_dma: Option>, + config: Config, + ) -> Result { + T::enable_and_reset(); + + let r = T::regs(); + r.cr3().write(|w| { + w.set_rtse(rts.is_some()); + }); + configure(r, &config, T::frequency(), T::KIND, true, false)?; + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + // create state once! + let _s = T::state(); + + Ok(Self { + _phantom: PhantomData, + rx, + rts, + rx_dma, + detect_previous_overrun: config.detect_previous_overrun, + #[cfg(any(usart_v1, usart_v2))] + buffered_sr: stm32_metapac::usart::regs::Sr(0), + }) + } + + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + reconfigure::(config) + } + + #[cfg(any(usart_v1, usart_v2))] + fn check_rx_flags(&mut self) -> Result { + let r = T::regs(); + loop { + // Handle all buffered error flags. + if self.buffered_sr.pe() { + self.buffered_sr.set_pe(false); + return Err(Error::Parity); + } else if self.buffered_sr.fe() { + self.buffered_sr.set_fe(false); + return Err(Error::Framing); + } else if self.buffered_sr.ne() { + self.buffered_sr.set_ne(false); + return Err(Error::Noise); + } else if self.buffered_sr.ore() { + self.buffered_sr.set_ore(false); + return Err(Error::Overrun); + } else if self.buffered_sr.rxne() { + self.buffered_sr.set_rxne(false); + return Ok(true); + } else { + // No error flags from previous iterations were set: Check the actual status register + let sr = r.sr().read(); + if !sr.rxne() { + return Ok(false); + } + + // Buffer the status register and let the loop handle the error flags. + self.buffered_sr = sr; + } + } + } + + #[cfg(any(usart_v3, usart_v4))] + fn check_rx_flags(&mut self) -> Result { + let r = T::regs(); + let sr = r.isr().read(); + if sr.pe() { + r.icr().write(|w| w.set_pe(true)); + return Err(Error::Parity); + } else if sr.fe() { + r.icr().write(|w| w.set_fe(true)); + return Err(Error::Framing); + } else if sr.ne() { + r.icr().write(|w| w.set_ne(true)); + return Err(Error::Noise); + } else if sr.ore() { + r.icr().write(|w| w.set_ore(true)); + return Err(Error::Overrun); + } + Ok(sr.rxne()) + } + + /// Read a single u8 if there is one available, otherwise return WouldBlock + pub(crate) fn nb_read(&mut self) -> Result> { + let r = T::regs(); + if self.check_rx_flags()? { + Ok(unsafe { rdr(r).read_volatile() }) + } else { + Err(nb::Error::WouldBlock) + } + } + + /// Perform a blocking read into `buffer` + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + let r = T::regs(); + for b in buffer { + while !self.check_rx_flags()? {} + unsafe { *b = rdr(r).read_volatile() } + } + Ok(()) + } +} + +impl<'d, T: BasicInstance, M: Mode> Drop for UartTx<'d, T, M> { fn drop(&mut self) { + self.tx.as_ref().map(|x| x.set_as_disconnected()); + self.cts.as_ref().map(|x| x.set_as_disconnected()); + self.de.as_ref().map(|x| x.set_as_disconnected()); T::disable(); } } -impl<'d, T: BasicInstance, TxDma> Drop for UartRx<'d, T, TxDma> { +impl<'d, T: BasicInstance, M: Mode> Drop for UartRx<'d, T, M> { fn drop(&mut self) { + self.rx.as_ref().map(|x| x.set_as_disconnected()); + self.rts.as_ref().map(|x| x.set_as_disconnected()); T::disable(); } } -impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { +impl<'d, T: BasicInstance> Uart<'d, T, Async> { /// Create a new bidirectional UART pub fn new( peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, _irq: impl interrupt::typelevel::Binding> + 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, config: Config, ) -> Result { - // UartRx and UartTx have one refcount ea. - T::enable_and_reset(); - T::enable_and_reset(); - - Self::new_inner_configure(peri, rx, tx, tx_dma, rx_dma, config) + Self::new_inner( + peri, + new_pin!(rx, config.rx_af()), + new_pin!(tx, config.tx_af()), + None, + None, + None, + new_dma!(tx_dma), + new_dma!(rx_dma), + config, + ) } /// Create a new bidirectional UART with request-to-send and clear-to-send pins @@ -793,23 +882,21 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { _irq: impl interrupt::typelevel::Binding> + 'd, rts: impl Peripheral

> + 'd, cts: impl Peripheral

> + 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, config: Config, ) -> Result { - into_ref!(cts, rts); - - // UartRx and UartTx have one refcount ea. - T::enable_and_reset(); - T::enable_and_reset(); - - rts.set_as_af(rts.af_num(), AFType::OutputPushPull); - cts.set_as_af(cts.af_num(), AFType::Input); - T::regs().cr3().write(|w| { - w.set_rtse(true); - w.set_ctse(true); - }); - Self::new_inner_configure(peri, rx, tx, tx_dma, rx_dma, config) + Self::new_inner( + peri, + new_pin!(rx, config.rx_af()), + new_pin!(tx, config.tx_af()), + new_pin!(rts, AFType::OutputPushPull), + new_pin!(cts, AFType::Input), + None, + new_dma!(tx_dma), + new_dma!(rx_dma), + config, + ) } #[cfg(not(any(usart_v1, usart_v2)))] @@ -820,21 +907,21 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { tx: impl Peripheral

> + 'd, _irq: impl interrupt::typelevel::Binding> + 'd, de: impl Peripheral

> + 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, config: Config, ) -> Result { - into_ref!(de); - - // UartRx and UartTx have one refcount ea. - T::enable_and_reset(); - T::enable_and_reset(); - - de.set_as_af(de.af_num(), AFType::OutputPushPull); - T::regs().cr3().write(|w| { - w.set_dem(true); - }); - Self::new_inner_configure(peri, rx, tx, tx_dma, rx_dma, config) + Self::new_inner( + peri, + new_pin!(rx, config.rx_af()), + new_pin!(tx, config.tx_af()), + None, + None, + new_pin!(de, AFType::OutputPushPull), + new_dma!(tx_dma), + new_dma!(rx_dma), + config, + ) } /// Create a single-wire half-duplex Uart transceiver on a single Tx pin. @@ -846,28 +933,32 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { /// I/O in idle or in reception. /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict /// on the line must be managed by software (for instance by using a centralized arbiter). - #[cfg(not(any(usart_v1, usart_v2)))] #[doc(alias("HDSEL"))] pub fn new_half_duplex( peri: impl Peripheral

+ 'd, tx: impl Peripheral

> + 'd, _irq: impl interrupt::typelevel::Binding> + 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, mut config: Config, ) -> Result { - // UartRx and UartTx have one refcount ea. - T::enable_and_reset(); - T::enable_and_reset(); + #[cfg(not(any(usart_v1, usart_v2)))] + { + config.swap_rx_tx = false; + } + config.half_duplex = true; - config.swap_rx_tx = false; - - into_ref!(peri, tx, tx_dma, rx_dma); - - T::regs().cr3().write(|w| w.set_hdsel(true)); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - - Self::new_inner(peri, tx_dma, rx_dma, config) + Self::new_inner( + peri, + None, + new_pin!(tx, AFType::OutputPushPull), + None, + None, + None, + new_dma!(tx_dma), + new_dma!(rx_dma), + config, + ) } /// Create a single-wire half-duplex Uart transceiver on a single Rx pin. @@ -885,62 +976,198 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, _irq: impl interrupt::typelevel::Binding> + 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, mut config: Config, ) -> Result { - // UartRx and UartTx have one refcount ea. - T::enable_and_reset(); - T::enable_and_reset(); - config.swap_rx_tx = true; + config.half_duplex = true; - into_ref!(peri, rx, tx_dma, rx_dma); - - T::regs().cr3().write(|w| w.set_hdsel(true)); - rx.set_as_af(rx.af_num(), AFType::OutputPushPull); - - Self::new_inner(peri, tx_dma, rx_dma, config) + Self::new_inner( + peri, + None, + None, + new_pin!(rx, AFType::OutputPushPull), + None, + None, + new_dma!(tx_dma), + new_dma!(rx_dma), + config, + ) } - fn new_inner_configure( + /// Perform an asynchronous write + pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + self.tx.write(buffer).await + } + + /// Perform an asynchronous read into `buffer` + pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + self.rx.read(buffer).await + } + + /// Perform an an asynchronous read with idle line detection enabled + pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { + self.rx.read_until_idle(buffer).await + } +} + +impl<'d, T: BasicInstance> Uart<'d, T, Blocking> { + /// Create a new blocking bidirectional UART. + pub fn new_blocking( peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Result { - into_ref!(peri, rx, tx, tx_dma, rx_dma); - - // Some chips do not have swap_rx_tx bit - cfg_if::cfg_if! { - if #[cfg(any(usart_v3, usart_v4))] { - if config.swap_rx_tx { - let (rx, tx) = (tx, rx); - rx.set_as_af(rx.af_num(), AFType::Input); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - } else { - rx.set_as_af(rx.af_num(), AFType::Input); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - } - } else { - rx.set_as_af(rx.af_num(), AFType::Input); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - } - } - - Self::new_inner(peri, tx_dma, rx_dma, config) + Self::new_inner( + peri, + new_pin!(rx, config.rx_af()), + new_pin!(tx, config.tx_af()), + None, + None, + None, + None, + None, + config, + ) } - fn new_inner( - peri: PeripheralRef<'d, T>, - tx_dma: PeripheralRef<'d, TxDma>, - rx_dma: PeripheralRef<'d, RxDma>, + /// Create a new bidirectional UART with request-to-send and clear-to-send pins + pub fn new_blocking_with_rtscts( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, config: Config, ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, config.rx_af()), + new_pin!(tx, config.tx_af()), + new_pin!(rts, AFType::OutputPushPull), + new_pin!(cts, AFType::Input), + None, + None, + None, + config, + ) + } + + #[cfg(not(any(usart_v1, usart_v2)))] + /// Create a new bidirectional UART with a driver-enable pin + pub fn new_blocking_with_de( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + de: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, config.rx_af()), + new_pin!(tx, config.tx_af()), + None, + None, + new_pin!(de, AFType::OutputPushPull), + None, + None, + config, + ) + } + + /// Create a single-wire half-duplex Uart transceiver on a single Tx pin. + /// + /// See [`new_half_duplex_on_rx`][`Self::new_half_duplex_on_rx`] if you would prefer to use an Rx pin. + /// There is no functional difference between these methods, as both allow bidirectional communication. + /// + /// The pin is always released when no data is transmitted. Thus, it acts as a standard + /// I/O in idle or in reception. + /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict + /// on the line must be managed by software (for instance by using a centralized arbiter). + #[doc(alias("HDSEL"))] + pub fn new_blocking_half_duplex( + peri: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + mut config: Config, + ) -> Result { + #[cfg(not(any(usart_v1, usart_v2)))] + { + config.swap_rx_tx = false; + } + config.half_duplex = true; + + Self::new_inner( + peri, + None, + new_pin!(tx, AFType::OutputPushPull), + None, + None, + None, + None, + None, + config, + ) + } + + /// Create a single-wire half-duplex Uart transceiver on a single Rx pin. + /// + /// See [`new_half_duplex`][`Self::new_half_duplex`] if you would prefer to use an Tx pin. + /// There is no functional difference between these methods, as both allow bidirectional communication. + /// + /// The pin is always released when no data is transmitted. Thus, it acts as a standard + /// I/O in idle or in reception. + /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict + /// on the line must be managed by software (for instance by using a centralized arbiter). + #[cfg(not(any(usart_v1, usart_v2)))] + #[doc(alias("HDSEL"))] + pub fn new_blocking_half_duplex_on_rx( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + mut config: Config, + ) -> Result { + config.swap_rx_tx = true; + config.half_duplex = true; + + Self::new_inner( + peri, + None, + None, + new_pin!(rx, AFType::OutputPushPull), + None, + None, + None, + None, + config, + ) + } +} + +impl<'d, T: BasicInstance, M: Mode> Uart<'d, T, M> { + fn new_inner( + _peri: impl Peripheral

+ 'd, + rx: Option>, + tx: Option>, + rts: Option>, + cts: Option>, + de: Option>, + tx_dma: Option>, + rx_dma: Option>, + config: Config, + ) -> Result { + // UartRx and UartTx have one refcount each. + T::enable_and_reset(); + T::enable_and_reset(); + let r = T::regs(); + r.cr3().write(|w| { + w.set_rtse(rts.is_some()); + w.set_ctse(cts.is_some()); + #[cfg(not(any(usart_v1, usart_v2)))] + w.set_dem(de.is_some()); + }); configure(r, &config, T::frequency(), T::KIND, true, true)?; T::Interrupt::unpend(); @@ -951,11 +1178,16 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { Ok(Self { tx: UartTx { + _phantom: PhantomData, + tx, + cts, + de, tx_dma, - phantom: PhantomData, }, rx: UartRx { - _peri: peri, + _phantom: PhantomData, + rx, + rts, rx_dma, detect_previous_overrun: config.detect_previous_overrun, #[cfg(any(usart_v1, usart_v2))] @@ -964,14 +1196,6 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { }) } - /// Initiate an asynchronous write - pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> - where - TxDma: crate::usart::TxDma, - { - self.tx.write(buffer).await - } - /// Perform a blocking write pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { self.tx.blocking_write(buffer) @@ -982,16 +1206,8 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { self.tx.blocking_flush() } - /// Initiate an asynchronous read into `buffer` - pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> - where - RxDma: crate::usart::RxDma, - { - self.rx.read(buffer).await - } - /// Read a single `u8` or return `WouldBlock` - pub fn nb_read(&mut self) -> Result> { + pub(crate) fn nb_read(&mut self) -> Result> { self.rx.nb_read() } @@ -1000,18 +1216,10 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { self.rx.blocking_read(buffer) } - /// Initiate an an asynchronous read with idle line detection enabled - pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result - where - RxDma: crate::usart::RxDma, - { - self.rx.read_until_idle(buffer).await - } - /// Split the Uart into a transmitter and receiver, which is /// particularly useful when having two tasks correlating to /// transmitting and receiving. - pub fn split(self) -> (UartTx<'d, T, TxDma>, UartRx<'d, T, RxDma>) { + pub fn split(self) -> (UartTx<'d, T, M>, UartRx<'d, T, M>) { (self.tx, self.rx) } } @@ -1150,9 +1358,10 @@ fn configure( } }); - #[cfg(not(usart_v1))] r.cr3().modify(|w| { + #[cfg(not(usart_v1))] w.set_onebit(config.assume_noise_free); + w.set_hdsel(config.half_duplex); }); r.cr1().write(|w| { @@ -1185,14 +1394,14 @@ fn configure( Ok(()) } -impl<'d, T: BasicInstance, RxDma> embedded_hal_02::serial::Read for UartRx<'d, T, RxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_02::serial::Read for UartRx<'d, T, M> { type Error = Error; fn read(&mut self) -> Result> { self.nb_read() } } -impl<'d, T: BasicInstance, TxDma> embedded_hal_02::blocking::serial::Write for UartTx<'d, T, TxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_02::blocking::serial::Write for UartTx<'d, T, M> { type Error = Error; fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) @@ -1202,14 +1411,14 @@ impl<'d, T: BasicInstance, TxDma> embedded_hal_02::blocking::serial::Write f } } -impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_02::serial::Read for Uart<'d, T, TxDma, RxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_02::serial::Read for Uart<'d, T, M> { type Error = Error; fn read(&mut self) -> Result> { self.nb_read() } } -impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_02::blocking::serial::Write for Uart<'d, T, TxDma, RxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_02::blocking::serial::Write for Uart<'d, T, M> { type Error = Error; fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) @@ -1231,25 +1440,25 @@ impl embedded_hal_nb::serial::Error for Error { } } -impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::ErrorType for Uart<'d, T, TxDma, RxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_nb::serial::ErrorType for Uart<'d, T, M> { type Error = Error; } -impl<'d, T: BasicInstance, TxDma> embedded_hal_nb::serial::ErrorType for UartTx<'d, T, TxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_nb::serial::ErrorType for UartTx<'d, T, M> { type Error = Error; } -impl<'d, T: BasicInstance, RxDma> embedded_hal_nb::serial::ErrorType for UartRx<'d, T, RxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_nb::serial::ErrorType for UartRx<'d, T, M> { type Error = Error; } -impl<'d, T: BasicInstance, RxDma> embedded_hal_nb::serial::Read for UartRx<'d, T, RxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, T, M> { fn read(&mut self) -> nb::Result { self.nb_read() } } -impl<'d, T: BasicInstance, TxDma> embedded_hal_nb::serial::Write for UartTx<'d, T, TxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, T, M> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) } @@ -1259,13 +1468,13 @@ impl<'d, T: BasicInstance, TxDma> embedded_hal_nb::serial::Write for UartTx<'d, } } -impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::Read for Uart<'d, T, TxDma, RxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_nb::serial::Read for Uart<'d, T, M> { fn read(&mut self) -> Result> { self.nb_read() } } -impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::Write for Uart<'d, T, TxDma, RxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) } @@ -1281,21 +1490,21 @@ impl embedded_io::Error for Error { } } -impl embedded_io::ErrorType for Uart<'_, T, TxDma, RxDma> +impl embedded_io::ErrorType for Uart<'_, T, M> where T: BasicInstance, { type Error = Error; } -impl embedded_io::ErrorType for UartTx<'_, T, TxDma> +impl embedded_io::ErrorType for UartTx<'_, T, M> where T: BasicInstance, { type Error = Error; } -impl embedded_io::Write for Uart<'_, T, TxDma, RxDma> +impl embedded_io::Write for Uart<'_, T, M> where T: BasicInstance, { @@ -1309,7 +1518,7 @@ where } } -impl embedded_io::Write for UartTx<'_, T, TxDma> +impl embedded_io::Write for UartTx<'_, T, M> where T: BasicInstance, { @@ -1323,10 +1532,9 @@ where } } -impl embedded_io_async::Write for Uart<'_, T, TxDma, RxDma> +impl embedded_io_async::Write for Uart<'_, T, Async> where T: BasicInstance, - TxDma: self::TxDma, { async fn write(&mut self, buf: &[u8]) -> Result { self.write(buf).await?; @@ -1338,10 +1546,9 @@ where } } -impl embedded_io_async::Write for UartTx<'_, T, TxDma> +impl embedded_io_async::Write for UartTx<'_, T, Async> where T: BasicInstance, - TxDma: self::TxDma, { async fn write(&mut self, buf: &[u8]) -> Result { self.write(buf).await?; diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index b852f0176..8eb18fe5b 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -1,21 +1,22 @@ use core::future::poll_fn; +use core::marker::PhantomData; use core::mem; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_embedded_hal::SetConfig; -use embassy_hal_internal::PeripheralRef; use futures::future::{select, Either}; use super::{clear_interrupt_flags, rdr, reconfigure, sr, BasicInstance, Config, ConfigError, Error, UartRx}; use crate::dma::ReadableRingBuffer; +use crate::mode::Async; use crate::usart::{Regs, Sr}; /// Rx-only Ring-buffered UART Driver /// /// Created with [UartRx::into_ring_buffered] pub struct RingBufferedUartRx<'d, T: BasicInstance> { - _peri: PeripheralRef<'d, T>, + _phantom: PhantomData, ring_buf: ReadableRingBuffer<'d, u8>, } @@ -28,26 +29,29 @@ impl<'d, T: BasicInstance> SetConfig for RingBufferedUartRx<'d, T> { } } -impl<'d, T: BasicInstance, RxDma: super::RxDma> UartRx<'d, T, RxDma> { +impl<'d, T: BasicInstance> UartRx<'d, T, Async> { /// Turn the `UartRx` into a buffered uart which can continously receive in the background /// without the possibility of losing bytes. The `dma_buf` is a buffer registered to the /// DMA controller, and must be large enough to prevent overflows. - pub fn into_ring_buffered(self, dma_buf: &'d mut [u8]) -> RingBufferedUartRx<'d, T> { + pub fn into_ring_buffered(mut self, dma_buf: &'d mut [u8]) -> RingBufferedUartRx<'d, T> { assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); - let request = self.rx_dma.request(); let opts = Default::default(); // Safety: we forget the struct before this function returns. - let rx_dma = unsafe { self.rx_dma.clone_unchecked() }; - let _peri = unsafe { self._peri.clone_unchecked() }; + let rx_dma = self.rx_dma.as_mut().unwrap(); + let request = rx_dma.request; + let rx_dma = unsafe { rx_dma.channel.clone_unchecked() }; let ring_buf = unsafe { ReadableRingBuffer::new(rx_dma, request, rdr(T::regs()), dma_buf, opts) }; // Don't disable the clock mem::forget(self); - RingBufferedUartRx { _peri, ring_buf } + RingBufferedUartRx { + _phantom: PhantomData, + ring_buf, + } } } diff --git a/embassy-stm32/src/usb/mod.rs b/embassy-stm32/src/usb/mod.rs index 1e3c44167..349438ec5 100644 --- a/embassy-stm32/src/usb/mod.rs +++ b/embassy-stm32/src/usb/mod.rs @@ -23,7 +23,7 @@ fn common_init() { ) } - #[cfg(any(stm32l4, stm32l5, stm32wb))] + #[cfg(any(stm32l4, stm32l5, stm32wb, stm32u0))] critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true))); #[cfg(pwr_h5)] diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index b0e7067bd..b386c6977 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -317,7 +317,7 @@ impl<'d, T: Instance> Driver<'d, T> { /// /// # Arguments /// - /// * `ep_out_buffer` - An internal buffer used to temporarily store recevied packets. + /// * `ep_out_buffer` - An internal buffer used to temporarily store received packets. /// Must be large enough to fit all OUT endpoint max packet sizes. /// Endpoint allocation will fail if it is too small. pub fn new_fs( @@ -348,7 +348,7 @@ impl<'d, T: Instance> Driver<'d, T> { /// /// # Arguments /// - /// * `ep_out_buffer` - An internal buffer used to temporarily store recevied packets. + /// * `ep_out_buffer` - An internal buffer used to temporarily store received packets. /// Must be large enough to fit all OUT endpoint max packet sizes. /// Endpoint allocation will fail if it is too small. pub fn new_hs_ulpi( @@ -562,51 +562,29 @@ impl<'d, T: Instance> Bus<'d, T> { fn init(&mut self) { super::common_init::(); - #[cfg(stm32f7)] - { - // Enable ULPI clock if external PHY is used - let ulpien = !self.phy_type.internal(); - critical_section::with(|_| { - crate::pac::RCC.ahb1enr().modify(|w| { - if T::HIGH_SPEED { - w.set_usb_otg_hsulpien(ulpien); - } else { - w.set_usb_otg_hsen(ulpien); - } - }); + // Enable ULPI clock if external PHY is used + let _ulpien = !self.phy_type.internal(); - // Low power mode - crate::pac::RCC.ahb1lpenr().modify(|w| { - if T::HIGH_SPEED { - w.set_usb_otg_hsulpilpen(ulpien); - } else { - w.set_usb_otg_hslpen(ulpien); - } - }); + #[cfg(any(stm32f2, stm32f4, stm32f7))] + if T::HIGH_SPEED { + critical_section::with(|_| { + let rcc = crate::pac::RCC; + rcc.ahb1enr().modify(|w| w.set_usb_otg_hsulpien(_ulpien)); + rcc.ahb1lpenr().modify(|w| w.set_usb_otg_hsulpilpen(_ulpien)); }); } #[cfg(stm32h7)] - { - // Enable ULPI clock if external PHY is used - let ulpien = !self.phy_type.internal(); - critical_section::with(|_| { - crate::pac::RCC.ahb1enr().modify(|w| { - if T::HIGH_SPEED { - w.set_usb_otg_hs_ulpien(ulpien); - } else { - w.set_usb_otg_fs_ulpien(ulpien); - } - }); - crate::pac::RCC.ahb1lpenr().modify(|w| { - if T::HIGH_SPEED { - w.set_usb_otg_hs_ulpilpen(ulpien); - } else { - w.set_usb_otg_fs_ulpilpen(ulpien); - } - }); - }); - } + critical_section::with(|_| { + let rcc = crate::pac::RCC; + if T::HIGH_SPEED { + rcc.ahb1enr().modify(|w| w.set_usb_otg_hs_ulpien(_ulpien)); + rcc.ahb1lpenr().modify(|w| w.set_usb_otg_hs_ulpilpen(_ulpien)); + } else { + rcc.ahb1enr().modify(|w| w.set_usb_otg_fs_ulpien(_ulpien)); + rcc.ahb1lpenr().modify(|w| w.set_usb_otg_fs_ulpilpen(_ulpien)); + } + }); let r = T::regs(); let core_id = r.cid().read().0; @@ -694,45 +672,51 @@ impl<'d, T: Instance> Bus<'d, T> { let r = T::regs(); - // Configure RX fifo size. All endpoints share the same FIFO area. - let rx_fifo_size_words = RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out); - trace!("configuring rx fifo size={}", rx_fifo_size_words); + // ERRATA NOTE: Don't interrupt FIFOs being written to. The interrupt + // handler COULD interrupt us here and do FIFO operations, so ensure + // the interrupt does not occur. + critical_section::with(|_| { + // Configure RX fifo size. All endpoints share the same FIFO area. + let rx_fifo_size_words = RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out); + trace!("configuring rx fifo size={}", rx_fifo_size_words); - r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words)); + r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words)); - // Configure TX (USB in direction) fifo size for each endpoint - let mut fifo_top = rx_fifo_size_words; - for i in 0..T::ENDPOINT_COUNT { - if let Some(ep) = self.ep_in[i] { - trace!( - "configuring tx fifo ep={}, offset={}, size={}", - i, - fifo_top, - ep.fifo_size_words - ); + // Configure TX (USB in direction) fifo size for each endpoint + let mut fifo_top = rx_fifo_size_words; + for i in 0..T::ENDPOINT_COUNT { + if let Some(ep) = self.ep_in[i] { + trace!( + "configuring tx fifo ep={}, offset={}, size={}", + i, + fifo_top, + ep.fifo_size_words + ); - let dieptxf = if i == 0 { r.dieptxf0() } else { r.dieptxf(i - 1) }; + let dieptxf = if i == 0 { r.dieptxf0() } else { r.dieptxf(i - 1) }; - dieptxf.write(|w| { - w.set_fd(ep.fifo_size_words); - w.set_sa(fifo_top); - }); + dieptxf.write(|w| { + w.set_fd(ep.fifo_size_words); + w.set_sa(fifo_top); + }); - fifo_top += ep.fifo_size_words; + fifo_top += ep.fifo_size_words; + } } - } - assert!( - fifo_top <= T::FIFO_DEPTH_WORDS, - "FIFO allocations exceeded maximum capacity" - ); + assert!( + fifo_top <= T::FIFO_DEPTH_WORDS, + "FIFO allocations exceeded maximum capacity" + ); - // Flush fifos - r.grstctl().write(|w| { - w.set_rxfflsh(true); - w.set_txfflsh(true); - w.set_txfnum(0x10); + // Flush fifos + r.grstctl().write(|w| { + w.set_rxfflsh(true); + w.set_txfflsh(true); + w.set_txfnum(0x10); + }); }); + loop { let x = r.grstctl().read(); if !x.rxfflsh() && !x.txfflsh() { @@ -1230,27 +1214,31 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { .await } - // Setup transfer size - r.dieptsiz(index).write(|w| { - w.set_mcnt(1); - w.set_pktcnt(1); - w.set_xfrsiz(buf.len() as _); - }); - + // ERRATA: Transmit data FIFO is corrupted when a write sequence to the FIFO is interrupted with + // accesses to certain OTG_FS registers. + // + // Prevent the interrupt (which might poke FIFOs) from executing while copying data to FIFOs. critical_section::with(|_| { + // Setup transfer size + r.dieptsiz(index).write(|w| { + w.set_mcnt(1); + w.set_pktcnt(1); + w.set_xfrsiz(buf.len() as _); + }); + // Enable endpoint r.diepctl(index).modify(|w| { w.set_cnak(true); w.set_epena(true); }); - }); - // Write data to FIFO - for chunk in buf.chunks(4) { - let mut tmp = [0u8; 4]; - tmp[0..chunk.len()].copy_from_slice(chunk); - r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))); - } + // Write data to FIFO + for chunk in buf.chunks(4) { + let mut tmp = [0u8; 4]; + tmp[0..chunk.len()].copy_from_slice(chunk); + r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))); + } + }); trace!("write done ep={:?}", self.info.addr); diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index f48808cb3..81a2d2623 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -107,14 +107,14 @@ const EP_COUNT: usize = 8; #[cfg(any(usbram_16x1_512, usbram_16x2_512))] const USBRAM_SIZE: usize = 512; -#[cfg(usbram_16x2_1024)] +#[cfg(any(usbram_16x2_1024, usbram_32_1024))] const USBRAM_SIZE: usize = 1024; #[cfg(usbram_32_2048)] const USBRAM_SIZE: usize = 2048; -#[cfg(not(usbram_32_2048))] +#[cfg(not(any(usbram_32_2048, usbram_32_1024)))] const USBRAM_ALIGN: usize = 2; -#[cfg(usbram_32_2048)] +#[cfg(any(usbram_32_2048, usbram_32_1024))] const USBRAM_ALIGN: usize = 4; const NEW_AW: AtomicWaker = AtomicWaker::new(); @@ -159,7 +159,7 @@ fn calc_out_len(len: u16) -> (u16, u16) { } } -#[cfg(not(usbram_32_2048))] +#[cfg(not(any(usbram_32_2048, usbram_32_1024)))] mod btable { use super::*; @@ -180,7 +180,7 @@ mod btable { USBRAM.mem(index * 4 + 3).read() } } -#[cfg(usbram_32_2048)] +#[cfg(any(usbram_32_2048, usbram_32_1024))] mod btable { use super::*; @@ -224,9 +224,9 @@ impl EndpointBuffer { let n = USBRAM_ALIGN.min(buf.len() - i * USBRAM_ALIGN); val[..n].copy_from_slice(&buf[i * USBRAM_ALIGN..][..n]); - #[cfg(not(usbram_32_2048))] + #[cfg(not(any(usbram_32_2048, usbram_32_1024)))] let val = u16::from_le_bytes(val); - #[cfg(usbram_32_2048)] + #[cfg(any(usbram_32_2048, usbram_32_1024))] let val = u32::from_le_bytes(val); USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).write_value(val); } diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index 72459d660..b48a408c4 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -3,6 +3,7 @@ //! This module provides a mutex that can be used to synchronize data between asynchronous tasks. use core::cell::{RefCell, UnsafeCell}; use core::future::poll_fn; +use core::mem; use core::ops::{Deref, DerefMut}; use core::task::Poll; @@ -134,6 +135,7 @@ where /// successfully locked the mutex, and grants access to the contents. /// /// Dropping it unlocks the mutex. +#[clippy::has_significant_drop] pub struct MutexGuard<'a, M, T> where M: RawMutex, @@ -142,6 +144,25 @@ where mutex: &'a Mutex, } +impl<'a, M, T> MutexGuard<'a, M, T> +where + M: RawMutex, + T: ?Sized, +{ + /// Returns a locked view over a portion of the locked data. + pub fn map(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> { + let mutex = this.mutex; + let value = fun(unsafe { &mut *this.mutex.inner.get() }); + // Don't run the `drop` method for MutexGuard. The ownership of the underlying + // locked state is being moved to the returned MappedMutexGuard. + mem::forget(this); + MappedMutexGuard { + state: &mutex.state, + value, + } + } +} + impl<'a, M, T> Drop for MutexGuard<'a, M, T> where M: RawMutex, @@ -180,3 +201,115 @@ where unsafe { &mut *(self.mutex.inner.get()) } } } + +/// A handle to a held `Mutex` that has had a function applied to it via [`MutexGuard::map`] or +/// [`MappedMutexGuard::map`]. +/// +/// This can be used to hold a subfield of the protected data. +#[clippy::has_significant_drop] +pub struct MappedMutexGuard<'a, M, T> +where + M: RawMutex, + T: ?Sized, +{ + state: &'a BlockingMutex>, + value: *mut T, +} + +impl<'a, M, T> MappedMutexGuard<'a, M, T> +where + M: RawMutex, + T: ?Sized, +{ + /// Returns a locked view over a portion of the locked data. + pub fn map(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> { + let state = this.state; + let value = fun(unsafe { &mut *this.value }); + // Don't run the `drop` method for MutexGuard. The ownership of the underlying + // locked state is being moved to the returned MappedMutexGuard. + mem::forget(this); + MappedMutexGuard { state, value } + } +} + +impl<'a, M, T> Deref for MappedMutexGuard<'a, M, T> +where + M: RawMutex, + T: ?Sized, +{ + type Target = T; + fn deref(&self) -> &Self::Target { + // Safety: the MutexGuard represents exclusive access to the contents + // of the mutex, so it's OK to get it. + unsafe { &*self.value } + } +} + +impl<'a, M, T> DerefMut for MappedMutexGuard<'a, M, T> +where + M: RawMutex, + T: ?Sized, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + // Safety: the MutexGuard represents exclusive access to the contents + // of the mutex, so it's OK to get it. + unsafe { &mut *self.value } + } +} + +impl<'a, M, T> Drop for MappedMutexGuard<'a, M, T> +where + M: RawMutex, + T: ?Sized, +{ + fn drop(&mut self) { + self.state.lock(|s| { + let mut s = unwrap!(s.try_borrow_mut()); + s.locked = false; + s.waker.wake(); + }) + } +} + +unsafe impl Send for MappedMutexGuard<'_, M, T> +where + M: RawMutex + Sync, + T: Send + ?Sized, +{ +} + +unsafe impl Sync for MappedMutexGuard<'_, M, T> +where + M: RawMutex + Sync, + T: Sync + ?Sized, +{ +} + +#[cfg(test)] +mod tests { + use crate::blocking_mutex::raw::NoopRawMutex; + use crate::mutex::{Mutex, MutexGuard}; + + #[futures_test::test] + async fn mapped_guard_releases_lock_when_dropped() { + let mutex: Mutex = Mutex::new([0, 1]); + + { + let guard = mutex.lock().await; + assert_eq!(*guard, [0, 1]); + let mut mapped = MutexGuard::map(guard, |this| &mut this[1]); + assert_eq!(*mapped, 1); + *mapped = 2; + } + + { + let guard = mutex.lock().await; + assert_eq!(*guard, [0, 2]); + let mut mapped = MutexGuard::map(guard, |this| &mut this[1]); + assert_eq!(*mapped, 2); + *mapped = 3; + } + + assert_eq!(*mutex.lock().await, [0, 3]); + } +} diff --git a/embassy-time-queue-driver/README.md b/embassy-time-queue-driver/README.md index 8852b0358..b9fb12d94 100644 --- a/embassy-time-queue-driver/README.md +++ b/embassy-time-queue-driver/README.md @@ -4,5 +4,5 @@ This crate contains the driver trait used by the [`embassy-time`](https://crates You should rarely need to use this crate directly. Only use it when implementing your own timer queue. -There is two timer queue implementations, one in `embassy-time` enabled by the `generic-queue` feature, and +There is two timer queue implementations, one in `embassy-time` enabled by the `generic-queue` feature, and another in `embassy-executor` enabled by the `integrated-timers` feature. diff --git a/embassy-time/README.md b/embassy-time/README.md index f5d46df7b..6a4b049b4 100644 --- a/embassy-time/README.md +++ b/embassy-time/README.md @@ -5,7 +5,7 @@ Timekeeping, delays and timeouts. Timekeeping is done with elapsed time since system boot. Time is represented in ticks, where the tick rate is defined either by the driver (in the case of a fixed-rate tick) or chosen by the user with a [tick rate](#tick-rate) feature. The chosen -tick rate applies to everything in `embassy-time` and thus determines the maximum +tick rate applies to everything in `embassy-time` and thus determines the maximum timing resolution of (1 / tick_rate) seconds. Tick counts are 64 bits. The default tick rate of 1Mhz supports diff --git a/embassy-usb/README.md b/embassy-usb/README.md index d2adae4f5..400fc6695 100644 --- a/embassy-usb/README.md +++ b/embassy-usb/README.md @@ -34,8 +34,8 @@ They can be set in two ways: - Via Cargo features: enable a feature like `-`. `name` must be in lowercase and use dashes instead of underscores. For example. `max-interface-count-3`. Only a selection of values is available, check `Cargo.toml` for the list. -- Via environment variables at build time: set the variable named `EMBASSY_USB_`. For example -`EMBASSY_USB_MAX_INTERFACE_COUNT=3 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`. +- Via environment variables at build time: set the variable named `EMBASSY_USB_`. For example +`EMBASSY_USB_MAX_INTERFACE_COUNT=3 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`. Any value can be set, unlike with Cargo features. Environment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index c06107396..387b780de 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -38,11 +38,12 @@ pub struct Config<'a> { /// Maximum packet size in bytes for the control endpoint 0. /// - /// Valid values are 8, 16, 32 and 64. There's generally no need to change this from the default - /// value of 8 bytes unless a class uses control transfers for sending large amounts of data, in - /// which case using a larger packet size may be more efficient. + /// Valid values depend on the speed at which the bus is enumerated. + /// - low speed: 8 + /// - full speed: 8, 16, 32, or 64 + /// - high speed: 64 /// - /// Default: 8 bytes + /// Default: 64 bytes pub max_packet_size_0: u8, /// Manufacturer name string descriptor. diff --git a/examples/boot/bootloader/stm32-dual-bank/README.md b/examples/boot/bootloader/stm32-dual-bank/README.md index 3de3171cd..cd6c0bc84 100644 --- a/examples/boot/bootloader/stm32-dual-bank/README.md +++ b/examples/boot/bootloader/stm32-dual-bank/README.md @@ -2,16 +2,16 @@ ## Overview -This bootloader leverages `embassy-boot` to interact with the flash. -This example targets STM32 devices with dual-bank flash memory, with a primary focus on the STM32H747XI series. +This bootloader leverages `embassy-boot` to interact with the flash. +This example targets STM32 devices with dual-bank flash memory, with a primary focus on the STM32H747XI series. Users must modify the `memory.x` configuration file to match with the memory layout of their specific STM32 device. Additionally, this example can be extended to utilize external flash memory, such as QSPI, for storing partitions. ## Memory Configuration -In this example's `memory.x` file, various symbols are defined to assist in effective memory management within the bootloader environment. -For dual-bank STM32 devices, it's crucial to assign these symbols correctly to their respective memory banks. +In this example's `memory.x` file, various symbols are defined to assist in effective memory management within the bootloader environment. +For dual-bank STM32 devices, it's crucial to assign these symbols correctly to their respective memory banks. ### Symbol Definitions diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 585349506..0f58f143c 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -12,7 +12,7 @@ embassy-executor = { version = "0.5.0", path = "../../embassy-executor", feature embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] } +embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet"] } embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } diff --git a/examples/stm32f0/src/bin/adc.rs b/examples/stm32f0/src/bin/adc.rs index c2fb143cd..a5a4186ea 100644 --- a/examples/stm32f0/src/bin/adc.rs +++ b/examples/stm32f0/src/bin/adc.rs @@ -6,7 +6,7 @@ use embassy_executor::Spawner; use embassy_stm32::adc::{Adc, SampleTime}; use embassy_stm32::peripherals::ADC; use embassy_stm32::{adc, bind_interrupts}; -use embassy_time::{Delay, Timer}; +use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -18,11 +18,11 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut adc = Adc::new(p.ADC, Irqs, &mut Delay); + let mut adc = Adc::new(p.ADC, Irqs); adc.set_sample_time(SampleTime::CYCLES71_5); let mut pin = p.PA1; - let mut vrefint = adc.enable_vref(&mut Delay); + let mut vrefint = adc.enable_vref(); let vrefint_sample = adc.read(&mut vrefint).await; let convert_to_millivolts = |sample| { // From https://www.st.com/resource/en/datasheet/stm32f031c6.pdf diff --git a/examples/stm32f1/src/bin/adc.rs b/examples/stm32f1/src/bin/adc.rs index 1440460a9..541ff159e 100644 --- a/examples/stm32f1/src/bin/adc.rs +++ b/examples/stm32f1/src/bin/adc.rs @@ -6,7 +6,7 @@ use embassy_executor::Spawner; use embassy_stm32::adc::Adc; use embassy_stm32::peripherals::ADC1; use embassy_stm32::{adc, bind_interrupts}; -use embassy_time::{Delay, Timer}; +use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -18,10 +18,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut adc = Adc::new(p.ADC1, &mut Delay); + let mut adc = Adc::new(p.ADC1); let mut pin = p.PB1; - let mut vrefint = adc.enable_vref(&mut Delay); + let mut vrefint = adc.enable_vref(); let vrefint_sample = adc.read(&mut vrefint).await; let convert_to_millivolts = |sample| { // From http://www.st.com/resource/en/datasheet/CD00161566.pdf diff --git a/examples/stm32f3/src/bin/usart_dma.rs b/examples/stm32f3/src/bin/usart_dma.rs index 5234e53b9..573a49f19 100644 --- a/examples/stm32f3/src/bin/usart_dma.rs +++ b/examples/stm32f3/src/bin/usart_dma.rs @@ -5,7 +5,6 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use heapless::String; @@ -21,7 +20,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, Irqs, p.DMA1_CH4, NoDma, config).unwrap(); + let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, Irqs, p.DMA1_CH4, p.DMA1_CH5, config).unwrap(); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32f334/.cargo/config.toml b/examples/stm32f334/.cargo/config.toml index caf947be6..f38c90a31 100644 --- a/examples/stm32f334/.cargo/config.toml +++ b/examples/stm32f334/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` -runner = "probe-run --chip STM32F334R8" +runner = "probe-rs run --chip STM32F334R8" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/stm32f334/src/bin/adc.rs b/examples/stm32f334/src/bin/adc.rs index bd126ce68..0528a9637 100644 --- a/examples/stm32f334/src/bin/adc.rs +++ b/examples/stm32f334/src/bin/adc.rs @@ -7,7 +7,7 @@ use embassy_stm32::adc::{Adc, SampleTime}; use embassy_stm32::peripherals::ADC1; use embassy_stm32::time::mhz; use embassy_stm32::{adc, bind_interrupts, Config}; -use embassy_time::{Delay, Timer}; +use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -38,13 +38,13 @@ async fn main(_spawner: Spawner) -> ! { info!("create adc..."); - let mut adc = Adc::new(p.ADC1, Irqs, &mut Delay); + let mut adc = Adc::new(p.ADC1, Irqs); adc.set_sample_time(SampleTime::CYCLES601_5); info!("enable vrefint..."); - let mut vrefint = adc.enable_vref(&mut Delay); + let mut vrefint = adc.enable_vref(); let mut temperature = adc.enable_temperature(); loop { diff --git a/examples/stm32f334/src/bin/opamp.rs b/examples/stm32f334/src/bin/opamp.rs index a5c710aa2..2dbf1bdab 100644 --- a/examples/stm32f334/src/bin/opamp.rs +++ b/examples/stm32f334/src/bin/opamp.rs @@ -8,7 +8,7 @@ use embassy_stm32::opamp::{OpAmp, OpAmpGain}; use embassy_stm32::peripherals::ADC2; use embassy_stm32::time::mhz; use embassy_stm32::{adc, bind_interrupts, Config}; -use embassy_time::{Delay, Timer}; +use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -39,14 +39,14 @@ async fn main(_spawner: Spawner) -> ! { info!("create adc..."); - let mut adc = Adc::new(p.ADC2, Irqs, &mut Delay); + let mut adc = Adc::new(p.ADC2, Irqs); let mut opamp = OpAmp::new(p.OPAMP2); adc.set_sample_time(SampleTime::CYCLES601_5); info!("enable vrefint..."); - let mut vrefint = adc.enable_vref(&mut Delay); + let mut vrefint = adc.enable_vref(); let mut temperature = adc.enable_temperature(); let mut buffer = opamp.buffer_ext(&mut p.PA7, &mut p.PA6, OpAmpGain::Mul1); diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 699c29c05..9473b7b7f 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let mut delay = Delay; - let mut adc = Adc::new(p.ADC1, &mut delay); + let mut adc = Adc::new(p.ADC1); let mut pin = p.PC1; let mut vrefint = adc.enable_vrefint(); diff --git a/examples/stm32f4/src/bin/i2c.rs b/examples/stm32f4/src/bin/i2c.rs index 4b5da774d..4a96357a4 100644 --- a/examples/stm32f4/src/bin/i2c.rs +++ b/examples/stm32f4/src/bin/i2c.rs @@ -3,35 +3,19 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::i2c::{Error, I2c}; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, i2c, peripherals}; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; -bind_interrupts!(struct Irqs { - I2C2_EV => i2c::EventInterruptHandler; - I2C2_ER => i2c::ErrorInterruptHandler; -}); - #[embassy_executor::main] async fn main(_spawner: Spawner) { info!("Hello world!"); let p = embassy_stm32::init(Default::default()); - let mut i2c = I2c::new( - p.I2C2, - p.PB10, - p.PB11, - Irqs, - NoDma, - NoDma, - Hertz(100_000), - Default::default(), - ); + let mut i2c = I2c::new_blocking(p.I2C2, p.PB10, p.PB11, Hertz(100_000), Default::default()); let mut data = [0u8; 1]; diff --git a/examples/stm32f4/src/bin/spi.rs b/examples/stm32f4/src/bin/spi.rs index dc9141c62..970d819fc 100644 --- a/examples/stm32f4/src/bin/spi.rs +++ b/examples/stm32f4/src/bin/spi.rs @@ -3,7 +3,6 @@ use cortex_m_rt::entry; use defmt::*; -use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; use embassy_stm32::time::Hertz; @@ -18,7 +17,7 @@ fn main() -> ! { let mut spi_config = Config::default(); spi_config.frequency = Hertz(1_000_000); - let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, spi_config); + let mut spi = Spi::new_blocking(p.SPI3, p.PC10, p.PC12, p.PC11, spi_config); let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh); diff --git a/examples/stm32f4/src/bin/usart.rs b/examples/stm32f4/src/bin/usart.rs index 40d9d70f1..991bf6673 100644 --- a/examples/stm32f4/src/bin/usart.rs +++ b/examples/stm32f4/src/bin/usart.rs @@ -3,7 +3,6 @@ use cortex_m_rt::entry; use defmt::*; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use {defmt_rtt as _, panic_probe as _}; @@ -19,7 +18,7 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, Irqs, NoDma, NoDma, config).unwrap(); + let mut usart = Uart::new_blocking(p.USART3, p.PD9, p.PD8, config).unwrap(); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32f4/src/bin/usart_dma.rs b/examples/stm32f4/src/bin/usart_dma.rs index dd6de599c..aaf8d6c4f 100644 --- a/examples/stm32f4/src/bin/usart_dma.rs +++ b/examples/stm32f4/src/bin/usart_dma.rs @@ -5,7 +5,6 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use heapless::String; @@ -21,7 +20,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, Irqs, p.DMA1_CH3, NoDma, config).unwrap(); + let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, Irqs, p.DMA1_CH3, p.DMA1_CH1, config).unwrap(); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32f4/src/bin/ws2812_spi.rs b/examples/stm32f4/src/bin/ws2812_spi.rs index a280a3b77..e00d14327 100644 --- a/examples/stm32f4/src/bin/ws2812_spi.rs +++ b/examples/stm32f4/src/bin/ws2812_spi.rs @@ -8,13 +8,13 @@ // If you want to save SPI for other purpose, you may want to take a look at `ws2812_pwm_dma.rs` file, which make use of TIM and DMA. // // Warning: -// DO NOT stare at ws2812 directy (especially after each MCU Reset), its (max) brightness could easily make your eyes feel burn. +// DO NOT stare at ws2812 directly (especially after each MCU Reset), its (max) brightness could easily make your eyes feel burn. #![no_std] #![no_main] +use embassy_stm32::spi; use embassy_stm32::time::khz; -use embassy_stm32::{dma, spi}; use embassy_time::{Duration, Ticker, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -78,7 +78,7 @@ async fn main(_spawner: embassy_executor::Spawner) { spi_config.frequency = khz(12_800); // Since we only output waveform, then the Rx and Sck and RxDma it is not considered - let mut ws2812_spi = spi::Spi::new_txonly_nosck(dp.SPI1, dp.PB5, dp.DMA2_CH3, dma::NoDma, spi_config); + let mut ws2812_spi = spi::Spi::new_txonly_nosck(dp.SPI1, dp.PB5, dp.DMA2_CH3, spi_config); // flip color at 2 Hz let mut ticker = Ticker::every(Duration::from_millis(500)); diff --git a/examples/stm32f7/src/bin/adc.rs b/examples/stm32f7/src/bin/adc.rs index f8d7b691f..641157960 100644 --- a/examples/stm32f7/src/bin/adc.rs +++ b/examples/stm32f7/src/bin/adc.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::adc::Adc; -use embassy_time::{Delay, Timer}; +use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -12,7 +12,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut adc = Adc::new(p.ADC1, &mut Delay); + let mut adc = Adc::new(p.ADC1); let mut pin = p.PA3; let mut vrefint = adc.enable_vrefint(); diff --git a/examples/stm32f7/src/bin/usart_dma.rs b/examples/stm32f7/src/bin/usart_dma.rs index fb604b34f..47456adf2 100644 --- a/examples/stm32f7/src/bin/usart_dma.rs +++ b/examples/stm32f7/src/bin/usart_dma.rs @@ -5,7 +5,6 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use heapless::String; @@ -19,7 +18,7 @@ bind_interrupts!(struct Irqs { async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, Irqs, p.DMA1_CH1, NoDma, config).unwrap(); + let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, Irqs, p.DMA1_CH1, p.DMA1_CH3, config).unwrap(); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32g0/src/bin/spi_neopixel.rs b/examples/stm32g0/src/bin/spi_neopixel.rs index c5ea51721..2deee271d 100644 --- a/examples/stm32g0/src/bin/spi_neopixel.rs +++ b/examples/stm32g0/src/bin/spi_neopixel.rs @@ -4,7 +4,6 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::word::U5; -use embassy_stm32::dma::NoDma; use embassy_stm32::spi::{Config, Spi}; use embassy_stm32::time::Hertz; use embassy_time::Timer; @@ -77,7 +76,7 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.frequency = Hertz(4_000_000); - let mut spi = Spi::new_txonly_nosck(p.SPI1, p.PB5, p.DMA1_CH3, NoDma, config); + let mut spi = Spi::new_txonly_nosck(p.SPI1, p.PB5, p.DMA1_CH3, config); let mut neopixels = Ws2812::new(); diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs index ae64bc8e4..3de38cbd6 100644 --- a/examples/stm32g4/src/bin/adc.rs +++ b/examples/stm32g4/src/bin/adc.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::adc::{Adc, SampleTime}; use embassy_stm32::Config; -use embassy_time::{Delay, Timer}; +use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -28,8 +28,8 @@ async fn main(_spawner: Spawner) { let mut p = embassy_stm32::init(config); info!("Hello World!"); - let mut adc = Adc::new(p.ADC2, &mut Delay); - adc.set_sample_time(SampleTime::CYCLES32_5); + let mut adc = Adc::new(p.ADC2); + adc.set_sample_time(SampleTime::CYCLES24_5); loop { let measured = adc.read(&mut p.PA7); diff --git a/examples/stm32h5/src/bin/usart.rs b/examples/stm32h5/src/bin/usart.rs index f9cbad6af..cc49c2fdb 100644 --- a/examples/stm32h5/src/bin/usart.rs +++ b/examples/stm32h5/src/bin/usart.rs @@ -4,22 +4,16 @@ use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; -use embassy_stm32::{bind_interrupts, peripherals, usart}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; -bind_interrupts!(struct Irqs { - UART7 => usart::InterruptHandler; -}); - #[embassy_executor::task] async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, NoDma, NoDma, config).unwrap(); + let mut usart = Uart::new_blocking(p.UART7, p.PF6, p.PF7, config).unwrap(); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32h5/src/bin/usart_dma.rs b/examples/stm32h5/src/bin/usart_dma.rs index caae0dd18..c644e84bd 100644 --- a/examples/stm32h5/src/bin/usart_dma.rs +++ b/examples/stm32h5/src/bin/usart_dma.rs @@ -6,7 +6,6 @@ use core::fmt::Write; use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use heapless::String; @@ -22,7 +21,7 @@ async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.GPDMA1_CH0, NoDma, config).unwrap(); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.GPDMA1_CH0, p.GPDMA1_CH1, config).unwrap(); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32h5/src/bin/usart_split.rs b/examples/stm32h5/src/bin/usart_split.rs index 92047de8d..77b4caa9e 100644 --- a/examples/stm32h5/src/bin/usart_split.rs +++ b/examples/stm32h5/src/bin/usart_split.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; -use embassy_stm32::peripherals::{GPDMA1_CH1, UART7}; +use embassy_stm32::mode::Async; +use embassy_stm32::peripherals::UART7; use embassy_stm32::usart::{Config, Uart, UartRx}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; @@ -15,18 +15,6 @@ bind_interrupts!(struct Irqs { UART7 => usart::InterruptHandler; }); -#[embassy_executor::task] -async fn writer(mut usart: Uart<'static, UART7, NoDma, NoDma>) { - unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); - info!("wrote Hello, starting echo"); - - let mut buf = [0u8; 1]; - loop { - unwrap!(usart.blocking_read(&mut buf)); - unwrap!(usart.blocking_write(&buf)); - } -} - static CHANNEL: Channel = Channel::new(); #[embassy_executor::main] @@ -50,7 +38,7 @@ async fn main(spawner: Spawner) -> ! { } #[embassy_executor::task] -async fn reader(mut rx: UartRx<'static, UART7, GPDMA1_CH1>) { +async fn reader(mut rx: UartRx<'static, UART7, Async>) { let mut buf = [0; 8]; loop { info!("reading..."); diff --git a/examples/stm32h7/src/bin/adc.rs b/examples/stm32h7/src/bin/adc.rs index a5594d10c..0009103d1 100644 --- a/examples/stm32h7/src/bin/adc.rs +++ b/examples/stm32h7/src/bin/adc.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::adc::{Adc, SampleTime}; use embassy_stm32::Config; -use embassy_time::{Delay, Timer}; +use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -44,7 +44,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); - let mut adc = Adc::new(p.ADC3, &mut Delay); + let mut adc = Adc::new(p.ADC3); adc.set_sample_time(SampleTime::CYCLES32_5); diff --git a/examples/stm32h7/src/bin/spi.rs b/examples/stm32h7/src/bin/spi.rs index aed27723a..aaebdc346 100644 --- a/examples/stm32h7/src/bin/spi.rs +++ b/examples/stm32h7/src/bin/spi.rs @@ -7,7 +7,7 @@ use core::str::from_utf8; use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; -use embassy_stm32::dma::NoDma; +use embassy_stm32::mode::Blocking; use embassy_stm32::peripherals::SPI3; use embassy_stm32::time::mhz; use embassy_stm32::{spi, Config}; @@ -16,7 +16,7 @@ use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::task] -async fn main_task(mut spi: spi::Spi<'static, SPI3, NoDma, NoDma>) { +async fn main_task(mut spi: spi::Spi<'static, SPI3, Blocking>) { for n in 0u32.. { let mut write: String<128> = String::new(); core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap(); @@ -62,7 +62,7 @@ fn main() -> ! { let mut spi_config = spi::Config::default(); spi_config.frequency = mhz(1); - let spi = spi::Spi::new(p.SPI3, p.PB3, p.PB5, p.PB4, NoDma, NoDma, spi_config); + let spi = spi::Spi::new_blocking(p.SPI3, p.PB3, p.PB5, p.PB4, spi_config); let executor = EXECUTOR.init(Executor::new()); diff --git a/examples/stm32h7/src/bin/spi_dma.rs b/examples/stm32h7/src/bin/spi_dma.rs index 54d4d7656..3d3c724eb 100644 --- a/examples/stm32h7/src/bin/spi_dma.rs +++ b/examples/stm32h7/src/bin/spi_dma.rs @@ -7,15 +7,15 @@ use core::str::from_utf8; use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; -use embassy_stm32::peripherals::{DMA1_CH3, DMA1_CH4, SPI3}; +use embassy_stm32::mode::Async; use embassy_stm32::time::mhz; -use embassy_stm32::{spi, Config}; +use embassy_stm32::{peripherals, spi, Config}; use heapless::String; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::task] -async fn main_task(mut spi: spi::Spi<'static, SPI3, DMA1_CH3, DMA1_CH4>) { +async fn main_task(mut spi: spi::Spi<'static, peripherals::SPI3, Async>) { for n in 0u32.. { let mut write: String<128> = String::new(); let mut read = [0; 128]; diff --git a/examples/stm32h7/src/bin/usart.rs b/examples/stm32h7/src/bin/usart.rs index f9cbad6af..cc49c2fdb 100644 --- a/examples/stm32h7/src/bin/usart.rs +++ b/examples/stm32h7/src/bin/usart.rs @@ -4,22 +4,16 @@ use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; -use embassy_stm32::{bind_interrupts, peripherals, usart}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; -bind_interrupts!(struct Irqs { - UART7 => usart::InterruptHandler; -}); - #[embassy_executor::task] async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, NoDma, NoDma, config).unwrap(); + let mut usart = Uart::new_blocking(p.UART7, p.PF6, p.PF7, config).unwrap(); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32h7/src/bin/usart_dma.rs b/examples/stm32h7/src/bin/usart_dma.rs index ae1f3a2e9..6f340d40a 100644 --- a/examples/stm32h7/src/bin/usart_dma.rs +++ b/examples/stm32h7/src/bin/usart_dma.rs @@ -6,7 +6,6 @@ use core::fmt::Write; use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use heapless::String; @@ -22,7 +21,7 @@ async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.DMA1_CH0, NoDma, config).unwrap(); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.DMA1_CH0, p.DMA1_CH1, config).unwrap(); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32h7/src/bin/usart_split.rs b/examples/stm32h7/src/bin/usart_split.rs index b98c40877..4ad8e77ce 100644 --- a/examples/stm32h7/src/bin/usart_split.rs +++ b/examples/stm32h7/src/bin/usart_split.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; -use embassy_stm32::peripherals::{DMA1_CH1, UART7}; +use embassy_stm32::mode::Async; +use embassy_stm32::peripherals::UART7; use embassy_stm32::usart::{Config, Uart, UartRx}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; @@ -15,18 +15,6 @@ bind_interrupts!(struct Irqs { UART7 => usart::InterruptHandler; }); -#[embassy_executor::task] -async fn writer(mut usart: Uart<'static, UART7, NoDma, NoDma>) { - unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); - info!("wrote Hello, starting echo"); - - let mut buf = [0u8; 1]; - loop { - unwrap!(usart.blocking_read(&mut buf)); - unwrap!(usart.blocking_write(&buf)); - } -} - static CHANNEL: Channel = Channel::new(); #[embassy_executor::main] @@ -50,7 +38,7 @@ async fn main(spawner: Spawner) -> ! { } #[embassy_executor::task] -async fn reader(mut rx: UartRx<'static, UART7, DMA1_CH1>) { +async fn reader(mut rx: UartRx<'static, UART7, Async>) { let mut buf = [0; 8]; loop { info!("reading..."); diff --git a/examples/stm32l0/src/bin/adc.rs b/examples/stm32l0/src/bin/adc.rs index 97d41ca4b..507c3204a 100644 --- a/examples/stm32l0/src/bin/adc.rs +++ b/examples/stm32l0/src/bin/adc.rs @@ -6,7 +6,7 @@ use embassy_executor::Spawner; use embassy_stm32::adc::{Adc, SampleTime}; use embassy_stm32::peripherals::ADC; use embassy_stm32::{adc, bind_interrupts}; -use embassy_time::{Delay, Timer}; +use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -18,11 +18,11 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut adc = Adc::new(p.ADC, Irqs, &mut Delay); + let mut adc = Adc::new(p.ADC, Irqs); adc.set_sample_time(SampleTime::CYCLES79_5); let mut pin = p.PA1; - let mut vrefint = adc.enable_vref(&mut Delay); + let mut vrefint = adc.enable_vref(); let vrefint_sample = adc.read(&mut vrefint).await; let convert_to_millivolts = |sample| { // From https://www.st.com/resource/en/datasheet/stm32l051c6.pdf diff --git a/examples/stm32l0/src/bin/spi.rs b/examples/stm32l0/src/bin/spi.rs index f23a537b8..8e0cfdedb 100644 --- a/examples/stm32l0/src/bin/spi.rs +++ b/examples/stm32l0/src/bin/spi.rs @@ -3,7 +3,6 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; use embassy_stm32::time::Hertz; @@ -17,7 +16,7 @@ async fn main(_spawner: Spawner) { let mut spi_config = Config::default(); spi_config.frequency = Hertz(1_000_000); - let mut spi = Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, NoDma, NoDma, spi_config); + let mut spi = Spi::new_blocking(p.SPI1, p.PB3, p.PA7, p.PA6, spi_config); let mut cs = Output::new(p.PA15, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l1/src/bin/spi.rs b/examples/stm32l1/src/bin/spi.rs index 8be686c5a..eabf1bac2 100644 --- a/examples/stm32l1/src/bin/spi.rs +++ b/examples/stm32l1/src/bin/spi.rs @@ -3,7 +3,6 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; use embassy_stm32::time::Hertz; @@ -17,7 +16,7 @@ async fn main(_spawner: Spawner) { let mut spi_config = Config::default(); spi_config.frequency = Hertz(1_000_000); - let mut spi = Spi::new(p.SPI1, p.PA5, p.PA7, p.PA6, NoDma, NoDma, spi_config); + let mut spi = Spi::new_blocking(p.SPI1, p.PA5, p.PA7, p.PA6, spi_config); let mut cs = Output::new(p.PA4, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l4/.cargo/config.toml b/examples/stm32l4/.cargo/config.toml index db3a7ceff..83fc6d6f8 100644 --- a/examples/stm32l4/.cargo/config.toml +++ b/examples/stm32l4/.cargo/config.toml @@ -2,7 +2,7 @@ # replace STM32F429ZITx with your chip as listed in `probe-rs chip list` #runner = "probe-rs run --chip STM32L475VGT6" #runner = "probe-rs run --chip STM32L475VG" -runner = "probe-run --chip STM32L4S5QI" +runner = "probe-rs run --chip STM32L4S5QI" [build] target = "thumbv7em-none-eabi" diff --git a/examples/stm32l4/src/bin/adc.rs b/examples/stm32l4/src/bin/adc.rs index a9f4604aa..7a89334e0 100644 --- a/examples/stm32l4/src/bin/adc.rs +++ b/examples/stm32l4/src/bin/adc.rs @@ -4,7 +4,6 @@ use defmt::*; use embassy_stm32::adc::{Adc, Resolution}; use embassy_stm32::Config; -use embassy_time::Delay; use {defmt_rtt as _, panic_probe as _}; #[cortex_m_rt::entry] @@ -18,7 +17,7 @@ fn main() -> ! { } let p = embassy_stm32::init(config); - let mut adc = Adc::new(p.ADC1, &mut Delay); + let mut adc = Adc::new(p.ADC1); //adc.enable_vref(); adc.set_resolution(Resolution::BITS8); let mut channel = p.PC0; diff --git a/examples/stm32l4/src/bin/i2c.rs b/examples/stm32l4/src/bin/i2c.rs index f553deb82..2861bc091 100644 --- a/examples/stm32l4/src/bin/i2c.rs +++ b/examples/stm32l4/src/bin/i2c.rs @@ -3,33 +3,17 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::i2c::I2c; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, i2c, peripherals}; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; -bind_interrupts!(struct Irqs { - I2C2_EV => i2c::EventInterruptHandler; - I2C2_ER => i2c::ErrorInterruptHandler; -}); - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut i2c = I2c::new( - p.I2C2, - p.PB10, - p.PB11, - Irqs, - NoDma, - NoDma, - Hertz(100_000), - Default::default(), - ); + let mut i2c = I2c::new_blocking(p.I2C2, p.PB10, p.PB11, Hertz(100_000), Default::default()); let mut data = [0u8; 1]; unwrap!(i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data)); diff --git a/examples/stm32l4/src/bin/i2c_blocking_async.rs b/examples/stm32l4/src/bin/i2c_blocking_async.rs index 1b8652bcc..a014b23e0 100644 --- a/examples/stm32l4/src/bin/i2c_blocking_async.rs +++ b/examples/stm32l4/src/bin/i2c_blocking_async.rs @@ -4,34 +4,18 @@ use defmt::*; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::i2c::I2c; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, i2c, peripherals}; use embedded_hal_async::i2c::I2c as I2cTrait; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; -bind_interrupts!(struct Irqs { - I2C2_EV => i2c::EventInterruptHandler; - I2C2_ER => i2c::ErrorInterruptHandler; -}); - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let i2c = I2c::new( - p.I2C2, - p.PB10, - p.PB11, - Irqs, - NoDma, - NoDma, - Hertz(100_000), - Default::default(), - ); + let i2c = I2c::new_blocking(p.I2C2, p.PB10, p.PB11, Hertz(100_000), Default::default()); let mut i2c = BlockingAsync::new(i2c); let mut data = [0u8; 1]; diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index 77aa929ab..694629ede 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs @@ -23,18 +23,23 @@ use embassy_futures::select::{select, Either}; use embassy_futures::yield_now; use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources, StaticConfigV4}; +use embassy_net_adin1110::{Device, Runner, ADIN1110}; +use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; +use embassy_stm32::i2c::{self, Config as I2C_Config, I2c}; +use embassy_stm32::mode::Async; +use embassy_stm32::rng::{self, Rng}; +use embassy_stm32::spi::{Config as SPI_Config, Spi}; +use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, exti, pac, peripherals}; use embassy_time::{Delay, Duration, Ticker, Timer}; use embedded_hal_async::i2c::I2c as I2cBus; +use embedded_hal_bus::spi::ExclusiveDevice; use embedded_io::Write as bWrite; use embedded_io_async::Write; -use hal::gpio::{Input, Level, Output, Speed}; -use hal::i2c::{self, I2c}; -use hal::rng::{self, Rng}; -use hal::{bind_interrupts, exti, pac, peripherals}; use heapless::Vec; +use panic_probe as _; use rand::RngCore; use static_cell::StaticCell; -use {embassy_stm32 as hal, panic_probe as _}; bind_interrupts!(struct Irqs { I2C3_EV => i2c::EventInterruptHandler; @@ -42,13 +47,6 @@ bind_interrupts!(struct Irqs { RNG => rng::InterruptHandler; }); -use embassy_net_adin1110::{Device, Runner, ADIN1110}; -use embedded_hal_bus::spi::ExclusiveDevice; -use hal::gpio::Pull; -use hal::i2c::Config as I2C_Config; -use hal::spi::{Config as SPI_Config, Spi}; -use hal::time::Hertz; - // Basic settings // MAC-address used by the adin1110 const MAC: [u8; 6] = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]; @@ -57,12 +55,12 @@ const IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address([192, 168, 1, 5]), 24); // Listen port for the webserver const HTTP_LISTEN_PORT: u16 = 80; -pub type SpeSpi = Spi<'static, peripherals::SPI2, peripherals::DMA1_CH1, peripherals::DMA1_CH2>; +pub type SpeSpi = Spi<'static, peripherals::SPI2, Async>; pub type SpeSpiCs = ExclusiveDevice, Delay>; pub type SpeInt = exti::ExtiInput<'static>; pub type SpeRst = Output<'static>; pub type Adin1110T = ADIN1110; -pub type TempSensI2c = I2c<'static, peripherals::I2C3, peripherals::DMA1_CH6, peripherals::DMA1_CH7>; +pub type TempSensI2c = I2c<'static, peripherals::I2C3, Async>; static TEMP: AtomicI32 = AtomicI32::new(0); diff --git a/examples/stm32l4/src/bin/spi.rs b/examples/stm32l4/src/bin/spi.rs index 6653e4516..5693a3765 100644 --- a/examples/stm32l4/src/bin/spi.rs +++ b/examples/stm32l4/src/bin/spi.rs @@ -2,7 +2,6 @@ #![no_main] use defmt::*; -use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; use embassy_stm32::time::Hertz; @@ -17,7 +16,7 @@ fn main() -> ! { let mut spi_config = Config::default(); spi_config.frequency = Hertz(1_000_000); - let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, spi_config); + let mut spi = Spi::new_blocking(p.SPI3, p.PC10, p.PC12, p.PC11, spi_config); let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l4/src/bin/spi_blocking_async.rs b/examples/stm32l4/src/bin/spi_blocking_async.rs index 68dbb70ad..1f1089101 100644 --- a/examples/stm32l4/src/bin/spi_blocking_async.rs +++ b/examples/stm32l4/src/bin/spi_blocking_async.rs @@ -4,7 +4,6 @@ use defmt::*; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::spi::{Config, Spi}; use embassy_stm32::time::Hertz; @@ -19,7 +18,7 @@ async fn main(_spawner: Spawner) { let mut spi_config = Config::default(); spi_config.frequency = Hertz(1_000_000); - let spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, spi_config); + let spi = Spi::new_blocking(p.SPI3, p.PC10, p.PC12, p.PC11, spi_config); let mut spi = BlockingAsync::new(spi); diff --git a/examples/stm32l4/src/bin/usart.rs b/examples/stm32l4/src/bin/usart.rs index 7bab23950..d9b388026 100644 --- a/examples/stm32l4/src/bin/usart.rs +++ b/examples/stm32l4/src/bin/usart.rs @@ -2,7 +2,6 @@ #![no_main] use defmt::*; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use {defmt_rtt as _, panic_probe as _}; @@ -18,7 +17,7 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, Irqs, NoDma, NoDma, config).unwrap(); + let mut usart = Uart::new_blocking(p.UART4, p.PA1, p.PA0, config).unwrap(); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32l4/src/bin/usart_dma.rs b/examples/stm32l4/src/bin/usart_dma.rs index 031888f70..b4f7a1643 100644 --- a/examples/stm32l4/src/bin/usart_dma.rs +++ b/examples/stm32l4/src/bin/usart_dma.rs @@ -5,7 +5,6 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use heapless::String; @@ -21,7 +20,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, Irqs, p.DMA1_CH3, NoDma, config).unwrap(); + let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, Irqs, p.DMA1_CH3, p.DMA1_CH4, config).unwrap(); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32u0/.cargo/config.toml b/examples/stm32u0/.cargo/config.toml new file mode 100644 index 000000000..688347084 --- /dev/null +++ b/examples/stm32u0/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# replace stm32u083rctx with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip stm32u083rctx" + +[build] +target = "thumbv6m-none-eabi" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml new file mode 100644 index 000000000..5868372dd --- /dev/null +++ b/examples/stm32u0/Cargo.toml @@ -0,0 +1,29 @@ +[package] +edition = "2021" +name = "embassy-stm32u0-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +# Change stm32u083rc to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti", "chrono"] } +embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } + +defmt = "0.3" +defmt-rtt = "0.4" + +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7.0" +embedded-hal = "0.2.6" +panic-probe = { version = "0.3", features = ["print-defmt"] } +futures = { version = "0.3.17", default-features = false, features = ["async-await"] } +heapless = { version = "0.8", default-features = false } + +micromath = "2.0.0" +chrono = { version = "0.4.38", default-features = false } + +[profile.release] +debug = 2 diff --git a/examples/stm32u0/build.rs b/examples/stm32u0/build.rs new file mode 100644 index 000000000..8cd32d7ed --- /dev/null +++ b/examples/stm32u0/build.rs @@ -0,0 +1,5 @@ +fn main() { + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/stm32u0/src/bin/adc.rs b/examples/stm32u0/src/bin/adc.rs new file mode 100644 index 000000000..4410448f1 --- /dev/null +++ b/examples/stm32u0/src/bin/adc.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_stm32::adc::{Adc, Resolution}; +use embassy_stm32::Config; +use embassy_time::Duration; +use {defmt_rtt as _, panic_probe as _}; + +#[cortex_m_rt::entry] +fn main() -> ! { + info!("Hello World!"); + + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.mux.adcsel = mux::Adcsel::SYS; + } + let p = embassy_stm32::init(config); + + let mut adc = Adc::new(p.ADC1); + adc.set_resolution(Resolution::BITS8); + let mut channel = p.PC0; + + loop { + let v = adc.read(&mut channel); + info!("--> {}", v); + embassy_time::block_for(Duration::from_millis(200)); + } +} diff --git a/examples/stm32u0/src/bin/blinky.rs b/examples/stm32u0/src/bin/blinky.rs new file mode 100644 index 000000000..90e479aae --- /dev/null +++ b/examples/stm32u0/src/bin/blinky.rs @@ -0,0 +1,26 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let mut led = Output::new(p.PA5, Level::High, Speed::Low); + + loop { + info!("high"); + led.set_high(); + Timer::after_millis(300).await; + + info!("low"); + led.set_low(); + Timer::after_millis(300).await; + } +} diff --git a/examples/stm32u0/src/bin/button.rs b/examples/stm32u0/src/bin/button.rs new file mode 100644 index 000000000..8017f0274 --- /dev/null +++ b/examples/stm32u0/src/bin/button.rs @@ -0,0 +1,24 @@ +#![no_std] +#![no_main] + +use cortex_m_rt::entry; +use defmt::*; +use embassy_stm32::gpio::{Input, Pull}; +use {defmt_rtt as _, panic_probe as _}; + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + let p = embassy_stm32::init(Default::default()); + + let button = Input::new(p.PC13, Pull::Up); + + loop { + if button.is_high() { + info!("high"); + } else { + info!("low"); + } + } +} diff --git a/examples/stm32u0/src/bin/button_exti.rs b/examples/stm32u0/src/bin/button_exti.rs new file mode 100644 index 000000000..34a08bbc6 --- /dev/null +++ b/examples/stm32u0/src/bin/button_exti.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::exti::ExtiInput; +use embassy_stm32::gpio::Pull; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); + + info!("Press the USER button..."); + + loop { + button.wait_for_falling_edge().await; + info!("Pressed!"); + button.wait_for_rising_edge().await; + info!("Released!"); + } +} diff --git a/examples/stm32u0/src/bin/crc.rs b/examples/stm32u0/src/bin/crc.rs new file mode 100644 index 000000000..d1b545d5b --- /dev/null +++ b/examples/stm32u0/src/bin/crc.rs @@ -0,0 +1,31 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::crc::{Config, Crc, InputReverseConfig, PolySize}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + // Setup for: https://crccalc.com/?crc=Life, it never dieWomen are my favorite guy&method=crc32&datatype=ascii&outtype=0 + let mut crc = Crc::new( + p.CRC, + unwrap!(Config::new( + InputReverseConfig::Byte, + true, + PolySize::Width32, + 0xFFFFFFFF, + 0x04C11DB7 + )), + ); + + let output = crc.feed_bytes(b"Life, it never die\nWomen are my favorite guy") ^ 0xFFFFFFFF; + + defmt::assert_eq!(output, 0x33F0E26B); + + cortex_m::asm::bkpt(); +} diff --git a/examples/stm32u0/src/bin/dac.rs b/examples/stm32u0/src/bin/dac.rs new file mode 100644 index 000000000..fdbf1d374 --- /dev/null +++ b/examples/stm32u0/src/bin/dac.rs @@ -0,0 +1,35 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_stm32::dac::{DacCh1, Value}; +use embassy_stm32::dma::NoDma; +use {defmt_rtt as _, panic_probe as _}; + +#[cortex_m_rt::entry] +fn main() -> ! { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); + + loop { + for v in 0..=255 { + dac.set(Value::Bit8(to_sine_wave(v))); + } + } +} + +use micromath::F32Ext; + +fn to_sine_wave(v: u8) -> u8 { + if v >= 128 { + // top half + let r = 3.14 * ((v - 128) as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } else { + // bottom half + let r = 3.14 + 3.14 * (v as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } +} diff --git a/examples/stm32u0/src/bin/flash.rs b/examples/stm32u0/src/bin/flash.rs new file mode 100644 index 000000000..01b80a76b --- /dev/null +++ b/examples/stm32u0/src/bin/flash.rs @@ -0,0 +1,43 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::flash::Flash; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let addr: u32 = 0x40000 - 2 * 1024; + + let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region; + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.blocking_read(addr, &mut buf)); + info!("Read: {=[u8]:x}", buf); + info!("Erasing..."); + unwrap!(f.blocking_erase(addr, addr + 2 * 1024)); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.blocking_read(addr, &mut buf)); + info!("Read after erase: {=[u8]:x}", buf); + + info!("Writing..."); + unwrap!(f.blocking_write( + addr, + &[ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32 + ] + )); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.blocking_read(addr, &mut buf)); + info!("Read: {=[u8]:x}", buf); +} diff --git a/examples/stm32u0/src/bin/i2c.rs b/examples/stm32u0/src/bin/i2c.rs new file mode 100644 index 000000000..2861bc091 --- /dev/null +++ b/examples/stm32u0/src/bin/i2c.rs @@ -0,0 +1,21 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::i2c::I2c; +use embassy_stm32::time::Hertz; +use {defmt_rtt as _, panic_probe as _}; + +const ADDRESS: u8 = 0x5F; +const WHOAMI: u8 = 0x0F; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + let mut i2c = I2c::new_blocking(p.I2C2, p.PB10, p.PB11, Hertz(100_000), Default::default()); + + let mut data = [0u8; 1]; + unwrap!(i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data)); + info!("Whoami: {}", data[0]); +} diff --git a/examples/stm32u0/src/bin/rng.rs b/examples/stm32u0/src/bin/rng.rs new file mode 100644 index 000000000..89445b042 --- /dev/null +++ b/examples/stm32u0/src/bin/rng.rs @@ -0,0 +1,43 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::rcc::mux::Clk48sel; +use embassy_stm32::rng::Rng; +use embassy_stm32::{bind_interrupts, peripherals, rng, Config}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + RNG_CRYP => rng::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = true; + config.rcc.pll = Some(Pll { + source: PllSource::HSI, // 16 MHz + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL7, // 16 * 7 = 112 MHz + divp: None, + divq: None, + divr: Some(PllRDiv::DIV2), // 112 / 2 = 56 MHz + }); + config.rcc.sys = Sysclk::PLL1_R; + config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: false }); // needed for RNG + config.rcc.mux.clk48sel = Clk48sel::HSI48; // needed for RNG (or use MSI or PLLQ if you want) + } + + let p = embassy_stm32::init(config); + + info!("Hello World!"); + + let mut rng = Rng::new(p.RNG, Irqs); + + let mut buf = [0u8; 16]; + unwrap!(rng.async_fill_bytes(&mut buf).await); + info!("random bytes: {:02x}", buf); +} diff --git a/examples/stm32u0/src/bin/rtc.rs b/examples/stm32u0/src/bin/rtc.rs new file mode 100644 index 000000000..72fa0fde4 --- /dev/null +++ b/examples/stm32u0/src/bin/rtc.rs @@ -0,0 +1,49 @@ +#![no_std] +#![no_main] + +use chrono::{NaiveDate, NaiveDateTime}; +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::rtc::{Rtc, RtcConfig}; +use embassy_stm32::Config; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.sys = Sysclk::PLL1_R; + config.rcc.hsi = true; + config.rcc.pll = Some(Pll { + source: PllSource::HSI, // 16 MHz + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL7, // 16 * 7 = 112 MHz + divp: None, + divq: None, + divr: Some(PllRDiv::DIV2), // 112 / 2 = 56 MHz + }); + config.rcc.ls = LsConfig::default(); + } + + let p = embassy_stm32::init(config); + + info!("Hello World!"); + + let now = NaiveDate::from_ymd_opt(2020, 5, 15) + .unwrap() + .and_hms_opt(10, 30, 15) + .unwrap(); + + let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); + info!("Got RTC! {:?}", now.and_utc().timestamp()); + + rtc.set_datetime(now.into()).expect("datetime not set"); + + // In reality the delay would be much longer + Timer::after_millis(20000).await; + + let then: NaiveDateTime = rtc.now().unwrap().into(); + info!("Got RTC! {:?}", then.and_utc().timestamp()); +} diff --git a/examples/stm32u0/src/bin/spi.rs b/examples/stm32u0/src/bin/spi.rs new file mode 100644 index 000000000..5693a3765 --- /dev/null +++ b/examples/stm32u0/src/bin/spi.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; +use {defmt_rtt as _, panic_probe as _}; + +#[cortex_m_rt::entry] +fn main() -> ! { + info!("Hello World!"); + + let p = embassy_stm32::init(Default::default()); + + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new_blocking(p.SPI3, p.PC10, p.PC12, p.PC11, spi_config); + + let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh); + + loop { + let mut buf = [0x0Au8; 4]; + cs.set_low(); + unwrap!(spi.blocking_transfer_in_place(&mut buf)); + cs.set_high(); + info!("xfer {=[u8]:x}", buf); + } +} diff --git a/examples/stm32u0/src/bin/usart.rs b/examples/stm32u0/src/bin/usart.rs new file mode 100644 index 000000000..037a5c833 --- /dev/null +++ b/examples/stm32u0/src/bin/usart.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_stm32::usart::{Config, Uart}; +use {defmt_rtt as _, panic_probe as _}; + +#[cortex_m_rt::entry] +fn main() -> ! { + info!("Hello World!"); + + let p = embassy_stm32::init(Default::default()); + + let config = Config::default(); + let mut usart = Uart::new_blocking(p.USART2, p.PA3, p.PA2, config).unwrap(); + + unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); + info!("wrote Hello, starting echo"); + + let mut buf = [0u8; 1]; + loop { + unwrap!(usart.blocking_read(&mut buf)); + unwrap!(usart.blocking_write(&buf)); + } +} diff --git a/examples/stm32u0/src/bin/usb_serial.rs b/examples/stm32u0/src/bin/usb_serial.rs new file mode 100644 index 000000000..9b38fd5dc --- /dev/null +++ b/examples/stm32u0/src/bin/usb_serial.rs @@ -0,0 +1,109 @@ +#![no_std] +#![no_main] + +use defmt::{panic, *}; +use defmt_rtt as _; // global logger +use embassy_executor::Spawner; +use embassy_stm32::usb::{Driver, Instance}; +use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::Builder; +use futures::future::join; +use panic_probe as _; + +bind_interrupts!(struct Irqs { + USB_DRD_FS => usb::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = true; + config.rcc.pll = Some(Pll { + source: PllSource::HSI, // 16 MHz + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL7, + divp: None, + divq: None, + divr: Some(PllRDiv::DIV2), // 56 MHz + }); + config.rcc.sys = Sysclk::PLL1_R; + config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB + config.rcc.mux.clk48sel = mux::Clk48sel::HSI48; // USB uses ICLK + } + + let p = embassy_stm32::init(config); + + info!("Hello World!"); + + // Create the driver, from the HAL. + let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); + + // Create embassy-usb Config + let config = embassy_usb::Config::new(0xc0de, 0xcafe); + //config.max_packet_size_0 = 64; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 7]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut config_descriptor, + &mut bos_descriptor, + &mut [], // no msos descriptors + &mut control_buf, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} diff --git a/examples/stm32u0/src/bin/wdt.rs b/examples/stm32u0/src/bin/wdt.rs new file mode 100644 index 000000000..f6276e2e9 --- /dev/null +++ b/examples/stm32u0/src/bin/wdt.rs @@ -0,0 +1,41 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::wdg::IndependentWatchdog; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let mut led = Output::new(p.PA5, Level::High, Speed::Low); + + let mut wdt = IndependentWatchdog::new(p.IWDG, 1_000_000); + wdt.unleash(); + + let mut i = 0; + + loop { + info!("high"); + led.set_high(); + Timer::after_millis(300).await; + + info!("low"); + led.set_low(); + Timer::after_millis(300).await; + + // Pet watchdog for 5 iterations and then stop. + // MCU should restart in 1 second after the last pet. + if i < 5 { + info!("Petting watchdog"); + wdt.pet(); + } + + i += 1; + } +} diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 03294339d..01320b88d 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -24,5 +24,9 @@ heapless = { version = "0.8", default-features = false } micromath = "2.0.0" +[features] +## Use secure registers when TrustZone is enabled +trustzone-secure = ["embassy-stm32/trustzone-secure"] + [profile.release] debug = 2 diff --git a/examples/stm32u5/src/bin/i2c.rs b/examples/stm32u5/src/bin/i2c.rs index e376c6bc8..19a78eac9 100644 --- a/examples/stm32u5/src/bin/i2c.rs +++ b/examples/stm32u5/src/bin/i2c.rs @@ -3,33 +3,17 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::i2c::I2c; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, i2c, peripherals}; use {defmt_rtt as _, panic_probe as _}; const HTS221_ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; -bind_interrupts!(struct Irqs { - I2C2_EV => i2c::EventInterruptHandler; - I2C2_ER => i2c::ErrorInterruptHandler; -}); - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut i2c = I2c::new( - p.I2C2, - p.PH4, - p.PH5, - Irqs, - NoDma, - NoDma, - Hertz(100_000), - Default::default(), - ); + let mut i2c = I2c::new_blocking(p.I2C2, p.PH4, p.PH5, Hertz(100_000), Default::default()); let mut data = [0u8; 1]; unwrap!(i2c.blocking_write_read(HTS221_ADDRESS, &[WHOAMI], &mut data)); diff --git a/examples/stm32wb/.cargo/config.toml b/examples/stm32wb/.cargo/config.toml index 51c499ee7..8b6d6d754 100644 --- a/examples/stm32wb/.cargo/config.toml +++ b/examples/stm32wb/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # replace STM32WB55CCUx with your chip as listed in `probe-rs chip list` -# runner = "probe-run --chip STM32WB55RGVx --speed 1000 --connect-under-reset" +# runner = "probe-rs run --chip STM32WB55RGVx --speed 1000 --connect-under-reset" runner = "teleprobe local run --chip STM32WB55RG --elf" [build] diff --git a/rust-toolchain-nightly.toml b/rust-toolchain-nightly.toml index 98696fd2b..ac160b995 100644 --- a/rust-toolchain-nightly.toml +++ b/rust-toolchain-nightly.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2024-03-20" +channel = "nightly-2024-04-14" components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ] targets = [ "thumbv7em-none-eabi", diff --git a/tests/stm32/src/bin/dac.rs b/tests/stm32/src/bin/dac.rs index 9d64742df..06501ab14 100644 --- a/tests/stm32/src/bin/dac.rs +++ b/tests/stm32/src/bin/dac.rs @@ -13,7 +13,7 @@ use embassy_executor::Spawner; use embassy_stm32::adc::Adc; use embassy_stm32::dac::{DacCh1, Value}; use embassy_stm32::dma::NoDma; -use embassy_time::{Delay, Timer}; +use embassy_time::Timer; use micromath::F32Ext; use {defmt_rtt as _, panic_probe as _}; @@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) { let mut adc_pin = unsafe { core::ptr::read(&dac_pin) }; let mut dac = DacCh1::new(dac, NoDma, dac_pin); - let mut adc = Adc::new(adc, &mut Delay); + let mut adc = Adc::new(adc); #[cfg(feature = "stm32h755zi")] let normalization_factor = 256; diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index b0bdd477f..59cb0cfd3 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -6,7 +6,6 @@ mod common; use common::*; use defmt::assert_eq; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::spi::{self, Spi}; use embassy_stm32::time::Hertz; @@ -23,11 +22,11 @@ async fn main(_spawner: Spawner) { let mut spi_config = spi::Config::default(); spi_config.frequency = Hertz(1_000_000); - let mut spi = Spi::new( + let mut spi = Spi::new_blocking( spi, sck, // Arduino D13 mosi, // Arduino D11 miso, // Arduino D12 - NoDma, NoDma, spi_config, + spi_config, ); let data: [u8; 9] = [0x00, 0xFF, 0xAA, 0x55, 0xC0, 0xFF, 0xEE, 0xC0, 0xDE]; diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index 9b20eb784..a6e34674d 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs @@ -6,7 +6,6 @@ mod common; use common::*; use defmt::{assert, assert_eq, unreachable}; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, ConfigError, Error, Uart}; use embassy_time::{block_for, Duration, Instant}; @@ -20,11 +19,10 @@ async fn main(_spawner: Spawner) { let mut usart = peri!(p, UART); let mut rx = peri!(p, UART_RX); let mut tx = peri!(p, UART_TX); - let irq = irqs!(UART); { let config = Config::default(); - let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, irq, NoDma, NoDma, config).unwrap(); + let mut usart = Uart::new_blocking(&mut usart, &mut rx, &mut tx, config).unwrap(); // We can't send too many bytes, they have to fit in the FIFO. // This is because we aren't sending+receiving at the same time. @@ -40,7 +38,7 @@ async fn main(_spawner: Spawner) { // Test error handling with with an overflow error { let config = Config::default(); - let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, irq, NoDma, NoDma, config).unwrap(); + let mut usart = Uart::new_blocking(&mut usart, &mut rx, &mut tx, config).unwrap(); // Send enough bytes to fill the RX FIFOs off all USART versions. let data = [0; 64]; @@ -70,7 +68,7 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.baudrate = baudrate; - let mut usart = match Uart::new(&mut usart, &mut rx, &mut tx, irq, NoDma, NoDma, config) { + let mut usart = match Uart::new_blocking(&mut usart, &mut rx, &mut tx, config) { Ok(x) => x, Err(ConfigError::BaudrateTooHigh) => { info!("baudrate too high"); diff --git a/tests/stm32/src/bin/usart_rx_ringbuffered.rs b/tests/stm32/src/bin/usart_rx_ringbuffered.rs index 0c110421d..908452eaf 100644 --- a/tests/stm32/src/bin/usart_rx_ringbuffered.rs +++ b/tests/stm32/src/bin/usart_rx_ringbuffered.rs @@ -8,6 +8,7 @@ mod common; use common::*; use defmt::{assert_eq, panic}; use embassy_executor::Spawner; +use embassy_stm32::mode::Async; use embassy_stm32::usart::{Config, DataBits, Parity, RingBufferedUartRx, StopBits, Uart, UartTx}; use embassy_time::Timer; use rand_chacha::ChaCha8Rng; @@ -51,7 +52,7 @@ async fn main(spawner: Spawner) { } #[embassy_executor::task] -async fn transmit_task(mut tx: UartTx<'static, peris::UART, peris::UART_TX_DMA>) { +async fn transmit_task(mut tx: UartTx<'static, peris::UART, Async>) { // workaround https://github.com/embassy-rs/embassy/issues/1426 Timer::after_millis(100).await;