Merge branch 'embassy-rs:main' into simplepwm-dma

This commit is contained in:
eZio Pan 2024-01-02 11:55:09 +08:00 committed by GitHub
commit f5a218a018
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 222 additions and 50 deletions

View file

@ -35,8 +35,7 @@ The <a href="https://docs.embassy.dev/embassy-net/">embassy-net</a> network stac
The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
The <a href="https://github.com/embassy-rs/embassy/tree/main/embassy-stm32-wpan">embassy-stm32-wpan</a> crate provides Bluetooth Low Energy 5.x support for stm32wb microcontrollers.
- **LoRa** -
<a href="hthttps://github.com/lora-rs/lora-rs">The lora-rs project</a> provides an async LoRa and LoRaWAN stack that works well on Embassy.
- **LoRa** - The <a href="https://github.com/lora-rs/lora-rs">lora-rs</a> project provides an async LoRa and LoRaWAN stack that works well on Embassy.
- **USB** -
<a href="https://docs.embassy.dev/embassy-usb/">embassy-usb</a> implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.

3
ci.sh
View file

@ -207,6 +207,9 @@ cargo batch \
$BUILD_EXTRA
# failed, a wire must've come loose, will check asap.
rm out/tests/rpi-pico/i2c
rm out/tests/stm32wb55rg/wpan_mac
rm out/tests/stm32wb55rg/wpan_ble

View file

@ -41,7 +41,7 @@ The link:https://docs.embassy.dev/embassy-net/[embassy-net] network stack implem
The link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
=== LoRa
link:https://github.com/embassy-rs/lora-phy[lora-phy] and link:https://docs.embassy.dev/embassy-lora/[embassy-lora] supports LoRa networking on a wide range of LoRa radios, fully integrated with a Rust link:https://github.com/ivajloip/rust-lorawan[LoRaWAN] implementation.
link:https://github.com/lora-rs/lora-rs[lora-rs] supports LoRa networking on a wide range of LoRa radios, fully integrated with a Rust LoRaWAN implementation. It provides four crates — lora-phy, lora-modulation, lorawan-encoding, and lorawan-device — and basic examples for various development boards. It has support for STM32WL wireless microcontrollers or Semtech SX127x transceivers, among others.
=== USB
link:https://docs.embassy.dev/embassy-usb/[embassy-usb] implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.

View file

@ -49,7 +49,7 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStre
},
}
let mut arg_names = Vec::new();
let mut args = Vec::new();
let mut fargs = f.sig.inputs.clone();
for arg in fargs.iter_mut() {
@ -59,8 +59,8 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStre
}
syn::FnArg::Typed(t) => match t.pat.as_mut() {
syn::Pat::Ident(id) => {
arg_names.push(id.ident.clone());
id.mutability = None;
args.push((id.clone(), t.attrs.clone()));
}
_ => {
ctxt.error_spanned_by(arg, "pattern matching in task arguments is not yet supported");
@ -79,13 +79,24 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStre
task_inner.vis = syn::Visibility::Inherited;
task_inner.sig.ident = task_inner_ident.clone();
// assemble the original input arguments,
// including any attributes that may have
// been applied previously
let mut full_args = Vec::new();
for (arg, cfgs) in args {
full_args.push(quote!(
#(#cfgs)*
#arg
));
}
#[cfg(feature = "nightly")]
let mut task_outer: ItemFn = parse_quote! {
#visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> {
type Fut = impl ::core::future::Future + 'static;
const POOL_SIZE: usize = #pool_size;
static POOL: ::embassy_executor::raw::TaskPool<Fut, POOL_SIZE> = ::embassy_executor::raw::TaskPool::new();
unsafe { POOL._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) }
unsafe { POOL._spawn_async_fn(move || #task_inner_ident(#(#full_args,)*)) }
}
};
#[cfg(not(feature = "nightly"))]
@ -93,7 +104,7 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStre
#visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> {
const POOL_SIZE: usize = #pool_size;
static POOL: ::embassy_executor::_export::TaskPoolRef = ::embassy_executor::_export::TaskPoolRef::new();
unsafe { POOL.get::<_, POOL_SIZE>()._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) }
unsafe { POOL.get::<_, POOL_SIZE>()._spawn_async_fn(move || #task_inner_ident(#(#full_args,)*)) }
}
};

View file

@ -135,3 +135,17 @@ fn executor_task_self_wake_twice() {
]
)
}
#[test]
fn executor_task_cfg_args() {
// simulate cfg'ing away argument c
#[task]
async fn task1(a: u32, b: u32, #[cfg(any())] c: u32) {
let (_, _) = (a, b);
}
#[task]
async fn task2(a: u32, b: u32, #[cfg(all())] c: u32) {
let (_, _, _) = (a, b, c);
}
}

View file

@ -222,6 +222,11 @@ impl<'a> UdpSocket<'a> {
pub fn payload_send_capacity(&self) -> usize {
self.with(|s, _| s.payload_send_capacity())
}
/// Set the hop limit field in the IP header of sent packets.
pub fn set_hop_limit(&mut self, hop_limit: Option<u8>) {
self.with_mut(|s, _| s.set_hop_limit(hop_limit))
}
}
impl Drop for UdpSocket<'_> {

View file

@ -342,6 +342,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
s.tx_count.store(0, Ordering::Relaxed);
s.rx_started_count.store(0, Ordering::Relaxed);
s.rx_ended_count.store(0, Ordering::Relaxed);
s.rx_started.store(false, Ordering::Relaxed);
let len = tx_buffer.len();
unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) };
let len = rx_buffer.len();

View file

@ -152,6 +152,12 @@ impl<'d, T: Pin> Output<'d, T> {
self.pin.set_low()
}
/// Toggle the output level.
#[inline]
pub fn toggle(&mut self) {
self.pin.toggle()
}
/// Set the output level.
#[inline]
pub fn set_level(&mut self, level: Level) {
@ -310,6 +316,16 @@ impl<'d, T: Pin> Flex<'d, T> {
self.pin.set_low()
}
/// Toggle the output level.
#[inline]
pub fn toggle(&mut self) {
if self.is_set_low() {
self.set_high()
} else {
self.set_low()
}
}
/// Set the output level.
#[inline]
pub fn set_level(&mut self, level: Level) {
@ -538,6 +554,15 @@ mod eh02 {
}
}
impl<'d, T: Pin> embedded_hal_02::digital::v2::ToggleableOutputPin for Output<'d, T> {
type Error = Infallible;
#[inline]
fn toggle(&mut self) -> Result<(), Self::Error> {
self.toggle();
Ok(())
}
}
/// Implement [`embedded_hal_02::digital::v2::InputPin`] for [`Flex`];
///
/// If the pin is not in input mode the result is unspecified.
@ -574,6 +599,15 @@ mod eh02 {
Ok(self.ref_is_set_low())
}
}
impl<'d, T: Pin> embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'d, T> {
type Error = Infallible;
#[inline]
fn toggle(&mut self) -> Result<(), Self::Error> {
self.toggle();
Ok(())
}
}
}
impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Input<'d, T> {

View file

@ -1,3 +1,9 @@
//! # I2Cv1
//!
//! This implementation is used for STM32F1, STM32F2, STM32F4, and STM32L1 devices.
//!
//! All other devices (as of 2023-12-28) use [`v2`](super::v2) instead.
use core::future::poll_fn;
use core::task::Poll;
@ -10,6 +16,17 @@ use crate::dma::Transfer;
use crate::pac::i2c;
use crate::time::Hertz;
// /!\ /!\
// /!\ Implementation note! /!\
// /!\ /!\
//
// It's somewhat unclear whether using interrupts here in a *strictly* one-shot style is actually
// what we want! If you are looking in this file because you are doing async I2C and your code is
// just totally hanging (sometimes), maybe swing by this issue:
// <https://github.com/embassy-rs/embassy/issues/2372>.
//
// There's some more details there, and we might have a fix for you. But please let us know if you
// hit a case like this!
pub unsafe fn on_interrupt<T: Instance>() {
let regs = T::regs();
// i2c v2 only woke the task on transfer complete interrupts. v1 uses interrupts for a bunch of
@ -375,6 +392,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
T::regs().sr2().read();
Poll::Ready(Ok(()))
} else {
// If we need to go around, then re-enable the interrupts, otherwise nothing
// can wake us up and we'll hang.
Self::enable_interrupts();
Poll::Pending
}
}

View file

@ -208,32 +208,30 @@ pub fn init(config: Config) -> Peripherals {
let p = Peripherals::take_with_cs(cs);
#[cfg(dbgmcu)]
if config.enable_debug_during_sleep {
crate::pac::DBGMCU.cr().modify(|cr| {
#[cfg(any(dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u5, dbgmcu_wba))]
{
cr.set_dbg_stop(true);
cr.set_dbg_standby(true);
}
#[cfg(any(
dbgmcu_f1, dbgmcu_f2, dbgmcu_f3, dbgmcu_f4, dbgmcu_f7, dbgmcu_g4, dbgmcu_f7, dbgmcu_l0, dbgmcu_l1,
dbgmcu_l4, dbgmcu_wb, dbgmcu_wl
))]
{
cr.set_dbg_sleep(true);
cr.set_dbg_stop(true);
cr.set_dbg_standby(true);
}
#[cfg(dbgmcu_h7)]
{
cr.set_d1dbgcken(true);
cr.set_d3dbgcken(true);
cr.set_dbgsleep_d1(true);
cr.set_dbgstby_d1(true);
cr.set_dbgstop_d1(true);
}
});
}
crate::pac::DBGMCU.cr().modify(|cr| {
#[cfg(any(dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u5, dbgmcu_wba))]
{
cr.set_dbg_stop(config.enable_debug_during_sleep);
cr.set_dbg_standby(config.enable_debug_during_sleep);
}
#[cfg(any(
dbgmcu_f1, dbgmcu_f2, dbgmcu_f3, dbgmcu_f4, dbgmcu_f7, dbgmcu_g4, dbgmcu_f7, dbgmcu_l0, dbgmcu_l1,
dbgmcu_l4, dbgmcu_wb, dbgmcu_wl
))]
{
cr.set_dbg_sleep(config.enable_debug_during_sleep);
cr.set_dbg_stop(config.enable_debug_during_sleep);
cr.set_dbg_standby(config.enable_debug_during_sleep);
}
#[cfg(dbgmcu_h7)]
{
cr.set_d1dbgcken(config.enable_debug_during_sleep);
cr.set_d3dbgcken(config.enable_debug_during_sleep);
cr.set_dbgsleep_d1(config.enable_debug_during_sleep);
cr.set_dbgstby_d1(config.enable_debug_during_sleep);
cr.set_dbgstop_d1(config.enable_debug_during_sleep);
}
});
#[cfg(not(any(stm32f1, stm32wb, stm32wl)))]
peripherals::SYSCFG::enable_and_reset_with_cs(cs);

View file

@ -24,7 +24,7 @@ use crate::time::Hertz;
),
path = "v2.rs"
)]
#[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")]
#[cfg_attr(any(rtc_v3, rtc_v3u5, rtc_v3l5), path = "v3.rs")]
mod _version;
#[allow(unused_imports)]
pub use _version::*;
@ -43,7 +43,7 @@ pub(crate) enum WakeupPrescaler {
Div16 = 16,
}
#[cfg(any(stm32wb, stm32f4, stm32l0, stm32g4))]
#[cfg(any(stm32wb, stm32f4, stm32l0, stm32g4, stm32l5))]
impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel {
fn from(val: WakeupPrescaler) -> Self {
use crate::pac::rtc::vals::Wucksel;
@ -57,7 +57,7 @@ impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel {
}
}
#[cfg(any(stm32wb, stm32f4, stm32l0, stm32g4))]
#[cfg(any(stm32wb, stm32f4, stm32l0, stm32g4, stm32l5))]
impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler {
fn from(val: crate::pac::rtc::vals::Wucksel) -> Self {
use crate::pac::rtc::vals::Wucksel;
@ -348,7 +348,7 @@ impl Rtc {
) {
use embassy_time::{Duration, TICK_HZ};
#[cfg(any(rtc_v3, rtc_v3u5))]
#[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))]
use crate::pac::rtc::vals::Calrf;
// Panic if the rcc mod knows we're not using low-power rtc
@ -375,7 +375,7 @@ impl Rtc {
while !regs.isr().read().wutwf() {}
}
#[cfg(any(rtc_v3, rtc_v3u5))]
#[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))]
{
regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR));
while !regs.icsr().read().wutwf() {}
@ -404,7 +404,7 @@ impl Rtc {
/// was called, otherwise none
pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> {
use crate::interrupt::typelevel::Interrupt;
#[cfg(any(rtc_v3, rtc_v3u5))]
#[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))]
use crate::pac::rtc::vals::Calrf;
let instant = self.instant().unwrap();
@ -420,13 +420,19 @@ impl Rtc {
))]
regs.isr().modify(|w| w.set_wutf(false));
#[cfg(any(rtc_v3, rtc_v3u5))]
#[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))]
regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR));
#[cfg(not(stm32l5))]
crate::pac::EXTI
.pr(0)
.modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
#[cfg(stm32l5)]
crate::pac::EXTI
.fpr(0)
.modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
<RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::unpend();
});
}

View file

@ -135,6 +135,12 @@ impl sealed::Instance for crate::peripherals::RTC {
#[cfg(all(feature = "low-power", stm32g4))]
type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP;
#[cfg(all(feature = "low-power", stm32l5))]
const EXTI_WAKEUP_LINE: usize = 17;
#[cfg(all(feature = "low-power", stm32l5))]
type WakeupInterrupt = crate::interrupt::typelevel::RTC;
fn read_backup_register(_rtc: &Rtc, register: usize) -> Option<u32> {
#[allow(clippy::if_same_then_else)]
if register < Self::BACKUP_REGISTER_COUNT {

View file

@ -743,7 +743,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
T::enable_and_reset();
T::enable_and_reset();
Self::new_inner(peri, rx, tx, tx_dma, rx_dma, config)
Self::new_inner_configure(peri, rx, tx, tx_dma, rx_dma, config)
}
/// Create a new bidirectional UART with request-to-send and clear-to-send pins
@ -770,7 +770,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
w.set_rtse(true);
w.set_ctse(true);
});
Self::new_inner(peri, rx, tx, tx_dma, rx_dma, config)
Self::new_inner_configure(peri, rx, tx, tx_dma, rx_dma, config)
}
#[cfg(not(any(usart_v1, usart_v2)))]
@ -795,10 +795,76 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
T::regs().cr3().write(|w| {
w.set_dem(true);
});
Self::new_inner(peri, rx, tx, tx_dma, rx_dma, config)
Self::new_inner_configure(peri, rx, tx, tx_dma, rx_dma, config)
}
fn new_inner(
/// Create a single-wire half-duplex Uart transceiver on a single Tx pin.
///
/// See [`new_half_duplex_on_rx`][`Self::new_half_duplex_on_rx`] if you would prefer to use an Rx pin.
/// There is no functional difference between these methods, as both allow bidirectional communication.
///
/// The pin is always released when no data is transmitted. Thus, it acts as a standard
/// I/O in idle or in reception.
/// Apart from this, the communication protocol is similar to normal USART mode. Any conflict
/// on the line must be managed by software (for instance by using a centralized arbiter).
#[cfg(not(any(usart_v1, usart_v2)))]
#[doc(alias("HDSEL"))]
pub fn new_half_duplex(
peri: impl Peripheral<P = T> + 'd,
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
tx_dma: impl Peripheral<P = TxDma> + 'd,
rx_dma: impl Peripheral<P = RxDma> + 'd,
mut config: Config,
) -> Result<Self, ConfigError> {
// UartRx and UartTx have one refcount ea.
T::enable_and_reset();
T::enable_and_reset();
config.swap_rx_tx = false;
into_ref!(peri, tx, tx_dma, rx_dma);
T::regs().cr3().write(|w| w.set_hdsel(true));
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
Self::new_inner(peri, tx_dma, rx_dma, config)
}
/// Create a single-wire half-duplex Uart transceiver on a single Rx pin.
///
/// See [`new_half_duplex`][`Self::new_half_duplex`] if you would prefer to use an Tx pin.
/// There is no functional difference between these methods, as both allow bidirectional communication.
///
/// The pin is always released when no data is transmitted. Thus, it acts as a standard
/// I/O in idle or in reception.
/// Apart from this, the communication protocol is similar to normal USART mode. Any conflict
/// on the line must be managed by software (for instance by using a centralized arbiter).
#[cfg(not(any(usart_v1, usart_v2)))]
#[doc(alias("HDSEL"))]
pub fn new_half_duplex_on_rx(
peri: impl Peripheral<P = T> + 'd,
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
tx_dma: impl Peripheral<P = TxDma> + 'd,
rx_dma: impl Peripheral<P = RxDma> + 'd,
mut config: Config,
) -> Result<Self, ConfigError> {
// UartRx and UartTx have one refcount ea.
T::enable_and_reset();
T::enable_and_reset();
config.swap_rx_tx = true;
into_ref!(peri, rx, tx_dma, rx_dma);
T::regs().cr3().write(|w| w.set_hdsel(true));
rx.set_as_af(rx.af_num(), AFType::OutputPushPull);
Self::new_inner(peri, tx_dma, rx_dma, config)
}
fn new_inner_configure(
peri: impl Peripheral<P = T> + 'd,
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
@ -808,8 +874,6 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
) -> Result<Self, ConfigError> {
into_ref!(peri, rx, tx, tx_dma, rx_dma);
let r = T::regs();
// Some chips do not have swap_rx_tx bit
cfg_if::cfg_if! {
if #[cfg(any(usart_v3, usart_v4))] {
@ -827,6 +891,17 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
}
}
Self::new_inner(peri, tx_dma, rx_dma, config)
}
fn new_inner(
peri: PeripheralRef<'d, T>,
tx_dma: PeripheralRef<'d, TxDma>,
rx_dma: PeripheralRef<'d, RxDma>,
config: Config,
) -> Result<Self, ConfigError> {
let r = T::regs();
configure(r, &config, T::frequency(), T::KIND, true, true)?;
T::Interrupt::unpend();

View file

@ -701,10 +701,10 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> {
}
async fn wait_enabled(&mut self) {
trace!("wait_enabled OUT WAITING");
trace!("wait_enabled IN WAITING");
let index = self.info.addr.index();
poll_fn(|cx| {
EP_OUT_WAKERS[index].register(cx.waker());
EP_IN_WAKERS[index].register(cx.waker());
let regs = T::regs();
if regs.epr(index).read().stat_tx() == Stat::DISABLED {
Poll::Pending
@ -713,7 +713,7 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> {
}
})
.await;
trace!("wait_enabled OUT OK");
trace!("wait_enabled IN OK");
}
}

View file

@ -1,5 +1,5 @@
[toolchain]
channel = "beta-2023-12-17"
channel = "1.75"
components = [ "rust-src", "rustfmt", "llvm-tools" ]
targets = [
"thumbv7em-none-eabi",