Merge branch 'main' into prep-embedded-nal-async-udp

This commit is contained in:
chrysn 2024-04-19 15:22:57 +02:00
commit e6bf6c7a49
126 changed files with 3244 additions and 1467 deletions

2
.github/ci/build.sh vendored
View File

@ -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)

View File

@ -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

7
.github/ci/test.sh vendored
View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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 <a href="https://rust-lang.github.io/async-book/">async/await</a> 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 <a href="https://tweedegolf.nl/en/blog/65/async-rust-vs-rtos-showdown">faster and smaller than one!</a>
Rust's <a href="https://rust-lang.github.io/async-book/">async/await</a> 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 <a href="https://tweedegolf.nl/en/blog/65/async-rust-vs-rtos-showdown">faster and smaller than one!</a>
## 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)

7
ci.sh
View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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]
----

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

120
embassy-net/src/raw.rs Normal file
View File

@ -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<SocketStack>,
handle: SocketHandle,
}
impl<'a> RawSocket<'a> {
/// Create a new Raw socket using the provided stack and buffers.
pub fn new<D: Driver>(
stack: &'a Stack<D>,
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<R>(&self, f: impl FnOnce(&mut raw::Socket, &mut Interface) -> R) -> R {
let s = &mut *self.stack.borrow_mut();
let socket = s.sockets.get_mut::<raw::Socket>(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<usize, RecvError> {
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<Result<usize, RecvError>> {
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);
}
}

40
embassy-nrf/CHANGELOG.md Normal file
View File

@ -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

View File

@ -1,4 +1,4 @@
//! Pulse Density Modulation (PDM) mirophone driver
//! Pulse Density Modulation (PDM) microphone driver
#![macro_use]

View File

@ -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" ]

View File

@ -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, )*
}

View File

@ -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<T: Instance> super::SealedAdcPin<T> for Temperature {
}
impl<'d, T: Instance> Adc<'d, T> {
pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
pub fn new(adc: impl Peripheral<P = T> + '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<u32>) -> Vref {
pub fn enable_vref(&self) -> Vref {
T::regs().cr2().modify(|reg| {
reg.set_tsvrefe(true);
});

View File

@ -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<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
delay: &mut impl DelayUs<u32>,
) -> 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<u32>) -> Vref {
pub fn enable_vref(&self) -> Vref {
T::common_regs().ccr().modify(|w| w.set_vrefen(true));
Vref {}

304
embassy-stm32/src/adc/g4.rs Normal file
View File

@ -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<T: Instance> InternalChannel<T> for VrefInt {}
impl<T: Instance> super::SealedInternalChannel<T> for VrefInt {
fn channel(&self) -> u8 {
VREF_CHANNEL
}
}
/// Internal temperature channel.
pub struct Temperature;
impl<T: Instance> InternalChannel<T> for Temperature {}
impl<T: Instance> super::SealedInternalChannel<T> for Temperature {
fn channel(&self) -> u8 {
TEMP_CHANNEL
}
}
/// Internal battery voltage channel.
pub struct Vbat;
impl<T: Instance> InternalChannel<T> for Vbat {}
impl<T: Instance> super::SealedInternalChannel<T> 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<P = T> + '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<P>(&mut self, pin: &mut P) -> u16
where
P: AdcPin<T>,
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<T>) -> 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));
}
}
}

View File

@ -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<T> {
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<P = Self> {
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<P = Self> + crate::rcc::RccPeripheral {
type Interrupt: crate::interrupt::typelevel::Interrupt;

View File

@ -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<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
delay: &mut impl DelayUs<u32>,
) -> 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<u32>) -> 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<u32>) -> 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<u32>) -> 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
}

View File

@ -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<ADC1> for VrefInt {}
impl super::SealedAdcPin<ADC1> for VrefInt {
@ -97,7 +94,7 @@ impl<'d, T> Adc<'d, T>
where
T: Instance,
{
pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
pub fn new(adc: impl Peripheral<P = T> + '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,

View File

@ -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<T: Instance> super::SealedAdcPin<T> 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<T: Instance> super::SealedAdcPin<T> 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<T: Instance> super::SealedAdcPin<T> 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<T: Instance> AdcPin<T> for DacOut {}
impl<T: Instance> super::SealedAdcPin<T> for DacOut {
fn channel(&self) -> u8 {
19
}
}
}
}
impl<'d, T: Instance> Adc<'d, T> {
pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
pub fn new(adc: impl Peripheral<P = T> + '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<u32>) -> 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 {

View File

@ -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<P = T> + 'd, delay: &mut impl DelayUs<u16>) -> Self {
pub fn new(adc: impl Peripheral<P = T> + '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<u16>) {
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) {

View File

@ -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

View File

@ -44,53 +44,51 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> 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::<T>(0);
}
if ir.rfn(1) {
T::state().rx_mode.on_interrupt::<T>(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));
}
}
}
}

View File

@ -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()
}

View File

@ -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()
}
}

View File

@ -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)]

View File

@ -16,6 +16,9 @@ mod dmamux;
#[cfg(dmamux)]
pub use dmamux::*;
mod util;
pub(crate) use util::*;
pub(crate) mod ringbuffer;
pub mod word;

View File

@ -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)
}
}

View File

@ -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));

View File

@ -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),
}
}
}

View File

@ -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()

View File

@ -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"
)]

View File

@ -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(|_| {});
}

View File

@ -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() {

View File

@ -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<ChannelAndRequest<'d>>,
rx_dma: Option<ChannelAndRequest<'d>>,
#[cfg(feature = "time")]
timeout: Duration,
_phantom: PhantomData<M>,
}
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<P = T> + 'd,
@ -90,12 +90,40 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
_irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>>
+ interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>>
+ 'd,
tx_dma: impl Peripheral<P = TXDMA> + 'd,
rx_dma: impl Peripheral<P = RXDMA> + 'd,
tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd,
rx_dma: impl Peripheral<P = impl RxDma<T>> + '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<P = T> + 'd,
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
sda: impl Peripheral<P = impl SdaPin<T>> + '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<P = T> + 'd,
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
tx_dma: Option<ChannelAndRequest<'d>>,
rx_dma: Option<ChannelAndRequest<'d>>,
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<T>, RXDMA: RxDma<T>> 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
}

View File

@ -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<T: Instance>() {
});
}
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<T>,
{
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<T>,
{
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<T>,
{
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<T>,
{
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<T>,
TXDMA: crate::i2c::TxDma<T>,
{
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<T>,
TXDMA: crate::i2c::TxDma<T>,
{
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<(), ()> {

View File

@ -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<T: Instance>() {
@ -24,7 +23,7 @@ pub(crate) unsafe fn on_interrupt<T: Instance>() {
});
}
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<T>,
{
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<T>,
{
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<T>,
{
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<T>,
{
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<T>,
{
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<T>,
RXDMA: super::RxDma<T>,
{
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<T>,
TXDMA: crate::i2c::TxDma<T>,
{
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<(), ()> {

View File

@ -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<PeripheralRef<'d, AnyPin>>,
ws: Option<PeripheralRef<'d, AnyPin>>,
ck: Option<PeripheralRef<'d, AnyPin>>,
mck: Option<PeripheralRef<'d, AnyPin>>,
}
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<P = T> + 'd,
@ -168,8 +169,8 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> {
ws: impl Peripheral<P = impl WsPin<T>> + 'd,
ck: impl Peripheral<P = impl CkPin<T>> + 'd,
mck: impl Peripheral<P = impl MckPin<T>> + 'd,
txdma: impl Peripheral<P = Tx> + 'd,
rxdma: impl Peripheral<P = Rx> + 'd,
txdma: impl Peripheral<P = impl TxDma<T>> + 'd,
rxdma: impl Peripheral<P = impl RxDma<T>> + '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<W: Word>(&mut self, data: &[W]) -> Result<(), Error>
where
Tx: TxDma<T>,
{
pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> {
self._peri.write(data).await
}
/// Read audio data.
pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error>
where
Tx: TxDma<T>,
Rx: RxDma<T>,
{
pub async fn read<W: Word>(&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());

View File

@ -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;

View File

@ -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())
}};
}

View File

@ -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::{

View File

@ -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};

View File

@ -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<T: RccPeripheral>() -> Hertz {
T::frequency()
}
/// Enables and resets peripheral `T`.
///
/// # Safety
///
/// Peripheral must not be in use.
pub unsafe fn enable_and_reset<T: RccPeripheral>() {
T::enable_and_reset();
}
/// Disables peripheral `T`.
///
/// # Safety
///
/// Peripheral must not be in use.
pub unsafe fn disable<T: RccPeripheral>() {
T::disable();
}

View File

@ -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<PeripheralRef<'d, AnyPin>>,
mosi: Option<PeripheralRef<'d, AnyPin>>,
miso: Option<PeripheralRef<'d, AnyPin>>,
txdma: PeripheralRef<'d, Tx>,
rxdma: PeripheralRef<'d, Rx>,
tx_dma: Option<ChannelAndRequest<'d>>,
rx_dma: Option<ChannelAndRequest<'d>>,
_phantom: PhantomData<M>,
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<P = T> + 'd,
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
miso: impl Peripheral<P = impl MisoPin<T>> + 'd,
txdma: impl Peripheral<P = Tx> + 'd,
rxdma: impl Peripheral<P = Rx> + '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<P = T> + 'd,
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
miso: impl Peripheral<P = impl MisoPin<T>> + 'd,
txdma: impl Peripheral<P = Tx> + 'd, // TODO remove
rxdma: impl Peripheral<P = Rx> + '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<P = T> + 'd,
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
txdma: impl Peripheral<P = Tx> + 'd,
rxdma: impl Peripheral<P = Rx> + '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<P = T> + 'd,
mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
txdma: impl Peripheral<P = Tx> + 'd,
rxdma: impl Peripheral<P = Rx> + '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<P = T> + 'd,
txdma: impl Peripheral<P = Tx> + 'd,
rxdma: impl Peripheral<P = Rx> + '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 = <peripherals::SUBGHZSPI as crate::rcc::SealedRccPeripheral>::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<P = T> + 'd,
txdma: impl Peripheral<P = Tx> + 'd,
rxdma: impl Peripheral<P = Rx> + '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<P = T> + 'd,
sck: Option<PeripheralRef<'d, AnyPin>>,
mosi: Option<PeripheralRef<'d, AnyPin>>,
miso: Option<PeripheralRef<'d, AnyPin>>,
txdma: impl Peripheral<P = Tx> + 'd,
rxdma: impl Peripheral<P = Rx> + 'd,
tx_dma: Option<ChannelAndRequest<'d>>,
rx_dma: Option<ChannelAndRequest<'d>>,
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: <u8 as SealedWord>::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<W: Word>(&mut self, data: &[W]) -> Result<(), Error>
where
Tx: TxDma<T>,
{
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<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error>
where
Tx: TxDma<T>,
Rx: RxDma<T>,
{
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<W: Word>(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error>
where
Tx: TxDma<T>,
Rx: RxDma<T>,
{
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<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error>
where
Tx: TxDma<T>,
Rx: RxDma<T>,
{
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<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error>
where
Tx: TxDma<T>,
Rx: RxDma<T>,
{
self.transfer_inner(data, data).await
}
/// Blocking write.
pub fn blocking_write<W: Word>(&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<P = T> + 'd,
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
miso: impl Peripheral<P = impl MisoPin<T>> + '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<P = T> + 'd,
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
miso: impl Peripheral<P = impl MisoPin<T>> + '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<P = T> + 'd,
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
mosi: impl Peripheral<P = impl MosiPin<T>> + '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<P = T> + 'd,
mosi: impl Peripheral<P = impl MosiPin<T>> + '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<P = T> + 'd,
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
miso: impl Peripheral<P = impl MisoPin<T>> + 'd,
tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd,
rx_dma: impl Peripheral<P = impl RxDma<T>> + '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<P = T> + 'd,
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
miso: impl Peripheral<P = impl MisoPin<T>> + 'd,
rx_dma: impl Peripheral<P = impl RxDma<T>> + '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<P = T> + 'd,
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
tx_dma: impl Peripheral<P = impl TxDma<T>> + '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<P = T> + 'd,
mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
tx_dma: impl Peripheral<P = impl TxDma<T>> + '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<P = T> + 'd,
tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd,
rx_dma: impl Peripheral<P = impl RxDma<T>> + '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 = <peripherals::SUBGHZSPI as crate::rcc::SealedRccPeripheral>::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<P = T> + 'd,
tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd,
rx_dma: impl Peripheral<P = impl RxDma<T>> + '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<W: Word>(&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<W: Word>(&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<W: Word>(&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<W: Word>(&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<W: Word>(&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<W>(&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<W>(&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<W: Word>(regs: Regs, tx_word: W) -> Result<W, Error> {
// 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<W> for Spi<'d, T, Tx, Rx> {
impl<'d, T: Instance, W: Word, M: PeriMode> embedded_hal_1::spi::SpiBus<W> 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<T>, Rx: RxDma<T>, W: Word> embedded_hal_async::spi::SpiBus<W> for Spi<'d, T, Tx, Rx> {
impl<'d, T: Instance, W: Word> embedded_hal_async::spi::SpiBus<W> 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<(), ()> {

File diff suppressed because it is too large Load Diff

View File

@ -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<T>,
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<T>> 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,
}
}
}

View File

@ -23,7 +23,7 @@ fn common_init<T: Instance>() {
)
}
#[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)]

View File

@ -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::<T>();
#[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);

View File

@ -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<T: Instance> EndpointBuffer<T> {
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);
}

View File

@ -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<M, T>,
}
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<U>(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<M, RefCell<State>>,
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<U>(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<M, T> Send for MappedMutexGuard<'_, M, T>
where
M: RawMutex + Sync,
T: Send + ?Sized,
{
}
unsafe impl<M, T> 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<NoopRawMutex, [i32; 2]> = 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]);
}
}

View File

@ -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.

View File

@ -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 <code>(1 / tick_rate) seconds</code>.
Tick counts are 64 bits. The default tick rate of 1Mhz supports

View File

@ -34,8 +34,8 @@ They can be set in two ways:
- Via Cargo features: enable a feature like `<name>-<value>`. `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_<value>`. 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_<value>`. 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

View File

@ -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.

View File

@ -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

View File

@ -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" }

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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"

View File

@ -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 {

View File

@ -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);

View File

@ -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();

View File

@ -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<peripherals::I2C2>;
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
});
#[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];

View File

@ -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);

View File

@ -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");

View File

@ -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();

View File

@ -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));

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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);

View File

@ -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<peripherals::UART7>;
});
#[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");

View File

@ -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();

View File

@ -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<peripherals::UART7>;
});
#[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<ThreadModeRawMutex, [u8; 8], 1> = 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...");

View File

@ -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);

View File

@ -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());

View File

@ -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];

View File

@ -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<peripherals::UART7>;
});
#[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");

View File

@ -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();

View File

@ -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<peripherals::UART7>;
});
#[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<ThreadModeRawMutex, [u8; 8], 1> = 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...");

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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"

View File

@ -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;

View File

@ -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<peripherals::I2C2>;
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
});
#[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));

View File

@ -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<peripherals::I2C2>;
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
});
#[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];

View File

@ -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<peripherals::I2C3>;
@ -42,13 +47,6 @@ bind_interrupts!(struct Irqs {
RNG => rng::InterruptHandler<peripherals::RNG>;
});
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<SpeSpi, Output<'static>, Delay>;
pub type SpeInt = exti::ExtiInput<'static>;
pub type SpeRst = Output<'static>;
pub type Adin1110T = ADIN1110<SpeSpiCs>;
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);

View File

@ -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);

View File

@ -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);

View File

@ -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");

Some files were not shown because too many files have changed in this diff Show More