Merge branch 'embassy-rs:main' into main

This commit is contained in:
Jack Hogan 2024-04-15 14:15:53 -04:00 committed by GitHub
commit aeb4daa22f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
68 changed files with 1065 additions and 187 deletions

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

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

@ -8,6 +8,7 @@ export RUSTUP_HOME=/ci/cache/rustup
export CARGO_HOME=/ci/cache/cargo
export CARGO_TARGET_DIR=/ci/cache/target
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

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

3
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' \

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

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

@ -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-d674277b78ca7400ecfeeb1b5af4e460a65c1a61" }
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-d674277b78ca7400ecfeeb1b5af4e460a65c1a61", default-features = false, features = ["metadata"]}
[features]
default = ["rt"]
@ -1419,6 +1420,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 +1507,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..20 {
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

@ -12,6 +12,7 @@
#[cfg_attr(adc_v2, path = "v2.rs")]
#[cfg_attr(any(adc_v3, adc_g0, adc_h5), path = "v3.rs")]
#[cfg_attr(adc_v4, path = "v4.rs")]
#[cfg_attr(adc_g4, path = "g4.rs")]
mod _version;
#[allow(unused)]
@ -69,14 +70,47 @@ 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))]
cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.unwrap().0 * us / 1_000_000);
}
/// 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_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_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;
@ -74,7 +74,7 @@ cfg_if! {
}
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| {
@ -88,7 +88,7 @@ impl<'d, T: Instance> Adc<'d, T> {
reg.set_chselrmod(false);
});
delay.delay_us(20);
blocking_delay_us(20);
T::regs().cr().modify(|reg| {
reg.set_adcal(true);
@ -98,7 +98,7 @@ impl<'d, T: Instance> Adc<'d, T> {
// spin
}
delay.delay_us(1);
blocking_delay_us(1);
Self {
adc,
@ -106,7 +106,7 @@ impl<'d, T: Instance> Adc<'d, T> {
}
}
pub fn enable_vrefint(&self, delay: &mut impl DelayUs<u32>) -> VrefInt {
pub fn enable_vrefint(&self) -> VrefInt {
#[cfg(not(adc_g0))]
T::common_regs().ccr().modify(|reg| {
reg.set_vrefen(true);
@ -117,10 +117,8 @@ impl<'d, T: Instance> Adc<'d, T> {
});
// "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 {}
}
@ -222,6 +220,13 @@ impl<'d, T: Instance> Adc<'d, T> {
// spin
}
// RM0492, RM0481, etc.
// "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected."
#[cfg(adc_h5)]
if pin.channel() == 0 {
T::regs().or().modify(|reg| reg.set_op0(true));
}
// Configure channel
Self::set_channel_sample_time(pin.channel(), self.sample_time);
@ -244,6 +249,13 @@ impl<'d, T: Instance> Adc<'d, T> {
T::regs().cr().modify(|reg| reg.set_addis(true));
// RM0492, RM0481, etc.
// "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected."
#[cfg(adc_h5)]
if pin.channel() == 0 {
T::regs().or().modify(|reg| reg.set_op0(false));
}
val
}

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

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

@ -136,7 +136,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 +144,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

@ -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,7 +55,6 @@ 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() {}
@ -63,9 +62,8 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E
interrupt::free(|_| {
pac::FLASH.nscr().modify(|w| {
w.set_bksel(match sector.bank {
FlashBank::Bank1 => Bksel::B_0X0,
FlashBank::Bank2 => Bksel::B_0X1,
_ => unreachable!(),
FlashBank::Bank1 => Bksel::BANK1,
FlashBank::Bank2 => Bksel::BANK2,
});
w.set_snb(sector.index_in_bank);
w.set_ser(true);

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

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;
@ -111,7 +111,7 @@ mod util {
}
}
/// Get the kernel clocok frequency of the peripheral `T`.
/// Get the kernel clock frequency of the peripheral `T`.
///
/// # Panics
///
@ -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

@ -735,18 +735,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 +819,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();
}
}

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;

View File

@ -5,6 +5,10 @@ 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
- Add `len`, `is_empty` and `is_full` functions to `Channel`.
## 0.5.0 - 2023-12-04
- Add a PriorityChannel.
@ -35,7 +39,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Remove unnecessary uses of `atomic-polyfill`
- Add `#[must_use]` to all futures.
## 0.1.0 - 2022-08-26
- First release

View File

@ -449,6 +449,18 @@ impl<T, const N: usize> ChannelState<T, N> {
Poll::Pending
}
}
fn len(&self) -> usize {
self.queue.len()
}
fn is_empty(&self) -> bool {
self.queue.is_empty()
}
fn is_full(&self) -> bool {
self.queue.is_full()
}
}
/// A bounded channel for communicating between asynchronous tasks
@ -572,6 +584,21 @@ where
pub fn try_receive(&self) -> Result<T, TryReceiveError> {
self.lock(|c| c.try_receive())
}
/// Returns the number of elements currently in the channel.
pub fn len(&self) -> usize {
self.lock(|c| c.len())
}
/// Returns whether the channel is empty.
pub fn is_empty(&self) -> bool {
self.lock(|c| c.is_empty())
}
/// Returns whether the channel is full.
pub fn is_full(&self) -> bool {
self.lock(|c| c.is_full())
}
}
/// Implements the DynamicChannel to allow creating types that are unaware of the queue size with the

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

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

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

@ -126,6 +126,11 @@ fn main() -> ! {
// Initialize and create handle for devicer peripherals
let _p = embassy_stm32::init(Default::default());
// STM32s dont have any interrupts exclusively for software use, but they can all be triggered by software as well as
// by the peripheral, so we can just use any free interrupt vectors which arent used by the rest of your application.
// In this case were using UART1 and UART2, but theres nothing special about them. Any otherwise unused interrupt
// vector would work exactly the same.
// High-priority executor: USART1, priority level 6
interrupt::USART1.set_priority(Priority::P6);
let spawner = EXECUTOR_HIGH.start(interrupt::USART1);

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

@ -127,6 +127,11 @@ fn main() -> ! {
let _p = embassy_stm32::init(Default::default());
// STM32s dont have any interrupts exclusively for software use, but they can all be triggered by software as well as
// by the peripheral, so we can just use any free interrupt vectors which arent used by the rest of your application.
// In this case were using UART4 and UART5, but theres nothing special about them. Any otherwise unused interrupt
// vector would work exactly the same.
// High-priority executor: UART4, priority level 6
interrupt::UART4.set_priority(Priority::P6);
let spawner = EXECUTOR_HIGH.start(interrupt::UART4);

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

@ -127,6 +127,11 @@ fn main() -> ! {
let _p = embassy_stm32::init(Default::default());
// STM32s dont have any interrupts exclusively for software use, but they can all be triggered by software as well as
// by the peripheral, so we can just use any free interrupt vectors which arent used by the rest of your application.
// In this case were using UART4 and UART5, but theres nothing special about them. Any otherwise unused interrupt
// vector would work exactly the same.
// High-priority executor: UART4, priority level 6
interrupt::UART4.set_priority(Priority::P6);
let spawner = EXECUTOR_HIGH.start(interrupt::UART4);

View File

@ -49,6 +49,7 @@ async fn main(_spawner: Spawner) {
// Create the driver, from the HAL.
let mut ep_out_buffer = [0u8; 256];
let mut config = embassy_stm32::usb::Config::default();
// If the board youre using doesnt have the VBUS pin wired up correctly for detecting the USB bus voltage (e.g. on the f4 blackpill board), set this to false
config.vbus_detection = true;
let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);

View File

@ -8,7 +8,7 @@
// 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]

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

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

@ -127,6 +127,11 @@ fn main() -> ! {
let _p = embassy_stm32::init(Default::default());
// STM32s dont have any interrupts exclusively for software use, but they can all be triggered by software as well as
// by the peripheral, so we can just use any free interrupt vectors which arent used by the rest of your application.
// In this case were using UART4 and UART5, but theres nothing special about them. Any otherwise unused interrupt
// vector would work exactly the same.
// High-priority executor: UART4, priority level 6
interrupt::UART4.set_priority(Priority::P6);
let spawner = EXECUTOR_HIGH.start(interrupt::UART4);

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

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

@ -0,0 +1,9 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# replace stm32u083rctx with your chip as listed in `probe-rs chip list`
runner = "probe-rs run --chip stm32u083rctx"
[build]
target = "thumbv6m-none-eabi"
[env]
DEFMT_LOG = "trace"

View File

@ -0,0 +1,25 @@
[package]
edition = "2021"
name = "embassy-stm32u0-examples"
version = "0.1.0"
license = "MIT OR Apache-2.0"
[dependencies]
# Change stm32u083rc to your chip name, if necessary.
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti"] }
embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
defmt = "0.3"
defmt-rtt = "0.4"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
panic-probe = { version = "0.3", features = ["print-defmt"] }
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
heapless = { version = "0.8", default-features = false }
[profile.release]
debug = 2

View File

@ -0,0 +1,5 @@
fn main() {
println!("cargo:rustc-link-arg-bins=--nmagic");
println!("cargo:rustc-link-arg-bins=-Tlink.x");
println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
}

View File

@ -0,0 +1,26 @@
#![no_std]
#![no_main]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::gpio::{Level, Output, Speed};
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
info!("Hello World!");
let mut led = Output::new(p.PA5, Level::High, Speed::Low);
loop {
info!("high");
led.set_high();
Timer::after_millis(300).await;
info!("low");
led.set_low();
Timer::after_millis(300).await;
}
}

View File

@ -0,0 +1,24 @@
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use defmt::*;
use embassy_stm32::gpio::{Input, Pull};
use {defmt_rtt as _, panic_probe as _};
#[entry]
fn main() -> ! {
info!("Hello World!");
let p = embassy_stm32::init(Default::default());
let button = Input::new(p.PC13, Pull::Up);
loop {
if button.is_high() {
info!("high");
} else {
info!("low");
}
}
}

View File

@ -0,0 +1,25 @@
#![no_std]
#![no_main]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::exti::ExtiInput;
use embassy_stm32::gpio::Pull;
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
info!("Hello World!");
let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up);
info!("Press the USER button...");
loop {
button.wait_for_falling_edge().await;
info!("Pressed!");
button.wait_for_rising_edge().await;
info!("Released!");
}
}

View File

@ -1,5 +1,5 @@
[toolchain]
channel = "nightly-2024-03-20"
channel = "nightly-2024-04-14"
components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ]
targets = [
"thumbv7em-none-eabi",

View File

@ -13,7 +13,7 @@ use embassy_executor::Spawner;
use embassy_stm32::adc::Adc;
use embassy_stm32::dac::{DacCh1, Value};
use embassy_stm32::dma::NoDma;
use embassy_time::{Delay, Timer};
use embassy_time::Timer;
use micromath::F32Ext;
use {defmt_rtt as _, panic_probe as _};
@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) {
let mut adc_pin = unsafe { core::ptr::read(&dac_pin) };
let mut dac = DacCh1::new(dac, NoDma, dac_pin);
let mut adc = Adc::new(adc, &mut Delay);
let mut adc = Adc::new(adc);
#[cfg(feature = "stm32h755zi")]
let normalization_factor = 256;