for Temperature {
}
impl<'d, T: Instance> Adc<'d, T> {
- pub fn new(adc: impl Peripheral + 'd, delay: &mut impl DelayUs) -> Self {
+ pub fn new(adc: impl Peripheral + 'd) -> Self {
into_ref!(adc);
T::enable_and_reset();
T::regs().cr2().modify(|reg| reg.set_adon(true));
// 11.4: Before starting a calibration, the ADC must have been in power-on state (ADON bit = ‘1’)
- // for at least two ADC clock cycles
- delay.delay_us((1_000_000 * 2) / Self::freq().0 + 1);
+ // for at least two ADC clock cycles.
+ blocking_delay_us((1_000_000 * 2) / Self::freq().0 + 1);
// Reset calibration
T::regs().cr2().modify(|reg| reg.set_rstcal(true));
@@ -70,7 +70,7 @@ impl<'d, T: Instance> Adc<'d, T> {
}
// One cycle after calibration
- delay.delay_us((1_000_000) / Self::freq().0 + 1);
+ blocking_delay_us((1_000_000 * 1) / Self::freq().0 + 1);
Self {
adc,
@@ -95,7 +95,7 @@ impl<'d, T: Instance> Adc<'d, T> {
}
}
- pub fn enable_vref(&self, _delay: &mut impl DelayUs) -> Vref {
+ pub fn enable_vref(&self) -> Vref {
T::regs().cr2().modify(|reg| {
reg.set_tsvrefe(true);
});
diff --git a/embassy-stm32/src/adc/f3.rs b/embassy-stm32/src/adc/f3.rs
index c5581dba1..c22a3fe4a 100644
--- a/embassy-stm32/src/adc/f3.rs
+++ b/embassy-stm32/src/adc/f3.rs
@@ -3,8 +3,8 @@ use core::marker::PhantomData;
use core::task::Poll;
use embassy_hal_internal::into_ref;
-use embedded_hal_02::blocking::delay::DelayUs;
+use super::blocking_delay_us;
use crate::adc::{Adc, AdcPin, Instance, SampleTime};
use crate::interrupt::typelevel::Interrupt;
use crate::time::Hertz;
@@ -58,7 +58,6 @@ impl<'d, T: Instance> Adc<'d, T> {
pub fn new(
adc: impl Peripheral + 'd,
_irq: impl interrupt::typelevel::Binding> + 'd,
- delay: &mut impl DelayUs,
) -> Self {
use crate::pac::adc::vals;
@@ -71,7 +70,7 @@ impl<'d, T: Instance> Adc<'d, T> {
T::regs().cr().modify(|w| w.set_advregen(vals::Advregen::ENABLED));
// Wait for the regulator to stabilize
- delay.delay_us(10);
+ blocking_delay_us(10);
assert!(!T::regs().cr().read().aden());
@@ -81,8 +80,8 @@ impl<'d, T: Instance> Adc<'d, T> {
while T::regs().cr().read().adcal() {}
- // Wait more than 4 clock cycles after adcal is cleared (RM0364 p. 223)
- delay.delay_us(1 + (6 * 1_000_000 / Self::freq().0));
+ // Wait more than 4 clock cycles after adcal is cleared (RM0364 p. 223).
+ blocking_delay_us((1_000_000 * 4) / Self::freq().0 + 1);
// Enable the adc
T::regs().cr().modify(|w| w.set_aden(true));
@@ -117,7 +116,7 @@ impl<'d, T: Instance> Adc<'d, T> {
}
}
- pub fn enable_vref(&self, _delay: &mut impl DelayUs) -> Vref {
+ pub fn enable_vref(&self) -> Vref {
T::common_regs().ccr().modify(|w| w.set_vrefen(true));
Vref {}
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs
new file mode 100644
index 000000000..221cc2a40
--- /dev/null
+++ b/embassy-stm32/src/adc/g4.rs
@@ -0,0 +1,304 @@
+#[allow(unused)]
+use pac::adc::vals::{Adcaldif, Difsel, Exten};
+use pac::adccommon::vals::Presc;
+
+use super::{blocking_delay_us, Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime};
+use crate::time::Hertz;
+use crate::{pac, Peripheral};
+
+/// Default VREF voltage used for sample conversion to millivolts.
+pub const VREF_DEFAULT_MV: u32 = 3300;
+/// VREF voltage used for factory calibration of VREFINTCAL register.
+pub const VREF_CALIB_MV: u32 = 3300;
+
+/// Max single ADC operation clock frequency
+#[cfg(stm32g4)]
+const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60);
+#[cfg(stm32h7)]
+const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50);
+
+#[cfg(stm32g4)]
+const VREF_CHANNEL: u8 = 18;
+#[cfg(stm32g4)]
+const TEMP_CHANNEL: u8 = 16;
+
+#[cfg(stm32h7)]
+const VREF_CHANNEL: u8 = 19;
+#[cfg(stm32h7)]
+const TEMP_CHANNEL: u8 = 18;
+
+// TODO this should be 14 for H7a/b/35
+const VBAT_CHANNEL: u8 = 17;
+
+// NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs
+/// Internal voltage reference channel.
+pub struct VrefInt;
+impl InternalChannel for VrefInt {}
+impl super::SealedInternalChannel for VrefInt {
+ fn channel(&self) -> u8 {
+ VREF_CHANNEL
+ }
+}
+
+/// Internal temperature channel.
+pub struct Temperature;
+impl InternalChannel for Temperature {}
+impl super::SealedInternalChannel for Temperature {
+ fn channel(&self) -> u8 {
+ TEMP_CHANNEL
+ }
+}
+
+/// Internal battery voltage channel.
+pub struct Vbat;
+impl InternalChannel for Vbat {}
+impl super::SealedInternalChannel for Vbat {
+ fn channel(&self) -> u8 {
+ VBAT_CHANNEL
+ }
+}
+
+// NOTE (unused): The prescaler enum closely copies the hardware capabilities,
+// but high prescaling doesn't make a lot of sense in the current implementation and is ommited.
+#[allow(unused)]
+enum Prescaler {
+ NotDivided,
+ DividedBy2,
+ DividedBy4,
+ DividedBy6,
+ DividedBy8,
+ DividedBy10,
+ DividedBy12,
+ DividedBy16,
+ DividedBy32,
+ DividedBy64,
+ DividedBy128,
+ DividedBy256,
+}
+
+impl Prescaler {
+ fn from_ker_ck(frequency: Hertz) -> Self {
+ let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0;
+ match raw_prescaler {
+ 0 => Self::NotDivided,
+ 1 => Self::DividedBy2,
+ 2..=3 => Self::DividedBy4,
+ 4..=5 => Self::DividedBy6,
+ 6..=7 => Self::DividedBy8,
+ 8..=9 => Self::DividedBy10,
+ 10..=11 => Self::DividedBy12,
+ _ => unimplemented!(),
+ }
+ }
+
+ fn divisor(&self) -> u32 {
+ match self {
+ Prescaler::NotDivided => 1,
+ Prescaler::DividedBy2 => 2,
+ Prescaler::DividedBy4 => 4,
+ Prescaler::DividedBy6 => 6,
+ Prescaler::DividedBy8 => 8,
+ Prescaler::DividedBy10 => 10,
+ Prescaler::DividedBy12 => 12,
+ Prescaler::DividedBy16 => 16,
+ Prescaler::DividedBy32 => 32,
+ Prescaler::DividedBy64 => 64,
+ Prescaler::DividedBy128 => 128,
+ Prescaler::DividedBy256 => 256,
+ }
+ }
+
+ fn presc(&self) -> Presc {
+ match self {
+ Prescaler::NotDivided => Presc::DIV1,
+ Prescaler::DividedBy2 => Presc::DIV2,
+ Prescaler::DividedBy4 => Presc::DIV4,
+ Prescaler::DividedBy6 => Presc::DIV6,
+ Prescaler::DividedBy8 => Presc::DIV8,
+ Prescaler::DividedBy10 => Presc::DIV10,
+ Prescaler::DividedBy12 => Presc::DIV12,
+ Prescaler::DividedBy16 => Presc::DIV16,
+ Prescaler::DividedBy32 => Presc::DIV32,
+ Prescaler::DividedBy64 => Presc::DIV64,
+ Prescaler::DividedBy128 => Presc::DIV128,
+ Prescaler::DividedBy256 => Presc::DIV256,
+ }
+ }
+}
+
+impl<'d, T: Instance> Adc<'d, T> {
+ /// Create a new ADC driver.
+ pub fn new(adc: impl Peripheral + 'd) -> Self {
+ embassy_hal_internal::into_ref!(adc);
+ T::enable_and_reset();
+
+ let prescaler = Prescaler::from_ker_ck(T::frequency());
+
+ T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc()));
+
+ let frequency = Hertz(T::frequency().0 / prescaler.divisor());
+ info!("ADC frequency set to {} Hz", frequency.0);
+
+ if frequency > MAX_ADC_CLK_FREQ {
+ panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 );
+ }
+
+ let mut s = Self {
+ adc,
+ sample_time: SampleTime::from_bits(0),
+ };
+ s.power_up();
+ s.configure_differential_inputs();
+
+ s.calibrate();
+ blocking_delay_us(1);
+
+ s.enable();
+ s.configure();
+
+ s
+ }
+
+ fn power_up(&mut self) {
+ T::regs().cr().modify(|reg| {
+ reg.set_deeppwd(false);
+ reg.set_advregen(true);
+ });
+
+ blocking_delay_us(10);
+ }
+
+ fn configure_differential_inputs(&mut self) {
+ T::regs().difsel().modify(|w| {
+ for n in 0..18 {
+ w.set_difsel(n, Difsel::SINGLEENDED);
+ }
+ });
+ }
+
+ fn calibrate(&mut self) {
+ T::regs().cr().modify(|w| {
+ w.set_adcaldif(Adcaldif::SINGLEENDED);
+ });
+
+ T::regs().cr().modify(|w| w.set_adcal(true));
+
+ while T::regs().cr().read().adcal() {}
+ }
+
+ fn enable(&mut self) {
+ T::regs().isr().write(|w| w.set_adrdy(true));
+ T::regs().cr().modify(|w| w.set_aden(true));
+ while !T::regs().isr().read().adrdy() {}
+ T::regs().isr().write(|w| w.set_adrdy(true));
+ }
+
+ fn configure(&mut self) {
+ // single conversion mode, software trigger
+ T::regs().cfgr().modify(|w| {
+ w.set_cont(false);
+ w.set_exten(Exten::DISABLED);
+ });
+ }
+
+ /// Enable reading the voltage reference internal channel.
+ pub fn enable_vrefint(&self) -> VrefInt {
+ T::common_regs().ccr().modify(|reg| {
+ reg.set_vrefen(true);
+ });
+
+ VrefInt {}
+ }
+
+ /// Enable reading the temperature internal channel.
+ pub fn enable_temperature(&self) -> Temperature {
+ T::common_regs().ccr().modify(|reg| {
+ reg.set_vsenseen(true);
+ });
+
+ Temperature {}
+ }
+
+ /// Enable reading the vbat internal channel.
+ pub fn enable_vbat(&self) -> Vbat {
+ T::common_regs().ccr().modify(|reg| {
+ reg.set_vbaten(true);
+ });
+
+ Vbat {}
+ }
+
+ /// Set the ADC sample time.
+ pub fn set_sample_time(&mut self, sample_time: SampleTime) {
+ self.sample_time = sample_time;
+ }
+
+ /// Set the ADC resolution.
+ pub fn set_resolution(&mut self, resolution: Resolution) {
+ T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
+ }
+
+ /// Perform a single conversion.
+ fn convert(&mut self) -> u16 {
+ T::regs().isr().modify(|reg| {
+ reg.set_eos(true);
+ reg.set_eoc(true);
+ });
+
+ // Start conversion
+ T::regs().cr().modify(|reg| {
+ reg.set_adstart(true);
+ });
+
+ while !T::regs().isr().read().eos() {
+ // spin
+ }
+
+ T::regs().dr().read().0 as u16
+ }
+
+ /// Read an ADC pin.
+ pub fn read
(&mut self, pin: &mut P) -> u16
+ where
+ P: AdcPin,
+ P: crate::gpio::Pin,
+ {
+ pin.set_as_analog();
+
+ self.read_channel(pin.channel())
+ }
+
+ /// Read an ADC internal channel.
+ pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 {
+ self.read_channel(channel.channel())
+ }
+
+ fn read_channel(&mut self, channel: u8) -> u16 {
+ // Configure channel
+ Self::set_channel_sample_time(channel, self.sample_time);
+
+ #[cfg(stm32h7)]
+ {
+ T::regs().cfgr2().modify(|w| w.set_lshift(0));
+ T::regs()
+ .pcsel()
+ .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED));
+ }
+
+ T::regs().sqr1().write(|reg| {
+ reg.set_sq(0, channel);
+ reg.set_l(0);
+ });
+
+ self.convert()
+ }
+
+ fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
+ let sample_time = sample_time.into();
+ if ch <= 9 {
+ T::regs().smpr().modify(|reg| reg.set_smp(ch as _, sample_time));
+ } else {
+ T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
+ }
+ }
+}
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs
index ead2357ce..8ef68490b 100644
--- a/embassy-stm32/src/adc/mod.rs
+++ b/embassy-stm32/src/adc/mod.rs
@@ -10,8 +10,9 @@
#[cfg_attr(adc_v1, path = "v1.rs")]
#[cfg_attr(adc_l0, path = "v1.rs")]
#[cfg_attr(adc_v2, path = "v2.rs")]
-#[cfg_attr(any(adc_v3, adc_g0, adc_h5), path = "v3.rs")]
+#[cfg_attr(any(adc_v3, adc_g0, adc_h5, adc_u0), path = "v3.rs")]
#[cfg_attr(adc_v4, path = "v4.rs")]
+#[cfg_attr(adc_g4, path = "g4.rs")]
mod _version;
#[allow(unused)]
@@ -69,14 +70,54 @@ trait SealedInternalChannel {
fn channel(&self) -> u8;
}
+/// Performs a busy-wait delay for a specified number of microseconds.
+#[allow(unused)]
+pub(crate) fn blocking_delay_us(us: u32) {
+ #[cfg(time)]
+ embassy_time::block_for(embassy_time::Duration::from_micros(us));
+ #[cfg(not(time))]
+ {
+ let freq = unsafe { crate::rcc::get_freqs() }.sys.unwrap().0 as u64;
+ let us = us as u64;
+ let cycles = freq * us / 1_000_000;
+ cortex_m::asm::delay(cycles as u32);
+ }
+}
+
/// ADC instance.
-#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0, adc_h5)))]
+#[cfg(not(any(
+ adc_f1,
+ adc_v1,
+ adc_l0,
+ adc_v2,
+ adc_v3,
+ adc_v4,
+ adc_g4,
+ adc_f3,
+ adc_f3_v1_1,
+ adc_g0,
+ adc_u0,
+ adc_h5
+)))]
#[allow(private_bounds)]
pub trait Instance: SealedInstance + crate::Peripheral {
type Interrupt: crate::interrupt::typelevel::Interrupt;
}
/// ADC instance.
-#[cfg(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0, adc_h5))]
+#[cfg(any(
+ adc_f1,
+ adc_v1,
+ adc_l0,
+ adc_v2,
+ adc_v3,
+ adc_v4,
+ adc_g4,
+ adc_f3,
+ adc_f3_v1_1,
+ adc_g0,
+ adc_u0,
+ adc_h5
+))]
#[allow(private_bounds)]
pub trait Instance: SealedInstance + crate::Peripheral
+ crate::rcc::RccPeripheral {
type Interrupt: crate::interrupt::typelevel::Interrupt;
diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs
index e9b46be80..1dda28cf2 100644
--- a/embassy-stm32/src/adc/v1.rs
+++ b/embassy-stm32/src/adc/v1.rs
@@ -3,10 +3,10 @@ use core::marker::PhantomData;
use core::task::Poll;
use embassy_hal_internal::into_ref;
-use embedded_hal_02::blocking::delay::DelayUs;
#[cfg(adc_l0)]
use stm32_metapac::adc::vals::Ckmode;
+use super::blocking_delay_us;
use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime};
use crate::interrupt::typelevel::Interrupt;
use crate::peripherals::ADC;
@@ -65,7 +65,6 @@ impl<'d, T: Instance> Adc<'d, T> {
pub fn new(
adc: impl Peripheral
+ 'd,
_irq: impl interrupt::typelevel::Binding> + 'd,
- delay: &mut impl DelayUs,
) -> Self {
into_ref!(adc);
T::enable_and_reset();
@@ -74,7 +73,7 @@ impl<'d, T: Instance> Adc<'d, T> {
//
// Table 57. ADC characteristics
// tstab = 14 * 1/fadc
- delay.delay_us(1);
+ blocking_delay_us(1);
// set default PCKL/2 on L0s because HSI is disabled in the default clock config
#[cfg(adc_l0)]
@@ -114,7 +113,7 @@ impl<'d, T: Instance> Adc<'d, T> {
}
#[cfg(not(adc_l0))]
- pub fn enable_vbat(&self, _delay: &mut impl DelayUs) -> Vbat {
+ pub fn enable_vbat(&self) -> Vbat {
// SMP must be ≥ 56 ADC clock cycles when using HSI14.
//
// 6.3.20 Vbat monitoring characteristics
@@ -123,22 +122,22 @@ impl<'d, T: Instance> Adc<'d, T> {
Vbat
}
- pub fn enable_vref(&self, delay: &mut impl DelayUs) -> Vref {
+ pub fn enable_vref(&self) -> Vref {
// Table 28. Embedded internal reference voltage
// tstart = 10μs
T::regs().ccr().modify(|reg| reg.set_vrefen(true));
- delay.delay_us(10);
+ blocking_delay_us(10);
Vref
}
- pub fn enable_temperature(&self, delay: &mut impl DelayUs) -> Temperature {
+ pub fn enable_temperature(&self) -> Temperature {
// SMP must be ≥ 56 ADC clock cycles when using HSI14.
//
// 6.3.19 Temperature sensor characteristics
// tstart ≤ 10μs
// ts_temp ≥ 4μs
T::regs().ccr().modify(|reg| reg.set_tsen(true));
- delay.delay_us(10);
+ blocking_delay_us(10);
Temperature
}
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs
index a43eb72db..7771cf768 100644
--- a/embassy-stm32/src/adc/v2.rs
+++ b/embassy-stm32/src/adc/v2.rs
@@ -1,6 +1,6 @@
use embassy_hal_internal::into_ref;
-use embedded_hal_02::blocking::delay::DelayUs;
+use super::blocking_delay_us;
use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime};
use crate::peripherals::ADC1;
use crate::time::Hertz;
@@ -11,9 +11,6 @@ pub const VREF_DEFAULT_MV: u32 = 3300;
/// VREF voltage used for factory calibration of VREFINTCAL register.
pub const VREF_CALIB_MV: u32 = 3300;
-/// ADC turn-on time
-pub const ADC_POWERUP_TIME_US: u32 = 3;
-
pub struct VrefInt;
impl AdcPin for VrefInt {}
impl super::SealedAdcPin for VrefInt {
@@ -97,7 +94,7 @@ impl<'d, T> Adc<'d, T>
where
T: Instance,
{
- pub fn new(adc: impl Peripheral + 'd, delay: &mut impl DelayUs) -> Self {
+ pub fn new(adc: impl Peripheral + 'd) -> Self {
into_ref!(adc);
T::enable_and_reset();
@@ -107,7 +104,7 @@ where
reg.set_adon(true);
});
- delay.delay_us(ADC_POWERUP_TIME_US);
+ blocking_delay_us(3);
Self {
adc,
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs
index e25630be2..dc418297e 100644
--- a/embassy-stm32/src/adc/v3.rs
+++ b/embassy-stm32/src/adc/v3.rs
@@ -1,7 +1,7 @@
use cfg_if::cfg_if;
use embassy_hal_internal::into_ref;
-use embedded_hal_02::blocking::delay::DelayUs;
+use super::blocking_delay_us;
use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime};
use crate::Peripheral;
@@ -19,6 +19,8 @@ impl super::SealedAdcPin for VrefInt {
let val = 13;
} else if #[cfg(adc_h5)] {
let val = 17;
+ } else if #[cfg(adc_u0)] {
+ let val = 12;
} else {
let val = 0;
}
@@ -36,6 +38,8 @@ impl super::SealedAdcPin for Temperature {
let val = 12;
} else if #[cfg(adc_h5)] {
let val = 16;
+ } else if #[cfg(adc_u0)] {
+ let val = 11;
} else {
let val = 17;
}
@@ -53,6 +57,8 @@ impl super::SealedAdcPin for Vbat {
let val = 14;
} else if #[cfg(adc_h5)] {
let val = 2;
+ } else if #[cfg(adc_h5)] {
+ let val = 13;
} else {
let val = 18;
}
@@ -73,22 +79,34 @@ cfg_if! {
}
}
+cfg_if! {
+ if #[cfg(adc_u0)] {
+ pub struct DacOut;
+ impl AdcPin for DacOut {}
+ impl super::SealedAdcPin for DacOut {
+ fn channel(&self) -> u8 {
+ 19
+ }
+ }
+ }
+}
+
impl<'d, T: Instance> Adc<'d, T> {
- pub fn new(adc: impl Peripheral + 'd, delay: &mut impl DelayUs) -> Self {
+ pub fn new(adc: impl Peripheral + 'd) -> Self {
into_ref!(adc);
T::enable_and_reset();
T::regs().cr().modify(|reg| {
- #[cfg(not(adc_g0))]
+ #[cfg(not(any(adc_g0, adc_u0)))]
reg.set_deeppwd(false);
reg.set_advregen(true);
});
- #[cfg(adc_g0)]
+ #[cfg(any(adc_g0, adc_u0))]
T::regs().cfgr1().modify(|reg| {
reg.set_chselrmod(false);
});
- delay.delay_us(20);
+ blocking_delay_us(20);
T::regs().cr().modify(|reg| {
reg.set_adcal(true);
@@ -98,7 +116,7 @@ impl<'d, T: Instance> Adc<'d, T> {
// spin
}
- delay.delay_us(1);
+ blocking_delay_us(1);
Self {
adc,
@@ -106,28 +124,26 @@ impl<'d, T: Instance> Adc<'d, T> {
}
}
- pub fn enable_vrefint(&self, delay: &mut impl DelayUs) -> VrefInt {
- #[cfg(not(adc_g0))]
+ pub fn enable_vrefint(&self) -> VrefInt {
+ #[cfg(not(any(adc_g0, adc_u0)))]
T::common_regs().ccr().modify(|reg| {
reg.set_vrefen(true);
});
- #[cfg(adc_g0)]
+ #[cfg(any(adc_g0, adc_u0))]
T::regs().ccr().modify(|reg| {
reg.set_vrefen(true);
});
// "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us
- // to stabilize the internal voltage reference, we wait a little more.
- // TODO: delay 15us
- //cortex_m::asm::delay(20_000_000);
- delay.delay_us(15);
+ // to stabilize the internal voltage reference.
+ blocking_delay_us(15);
VrefInt {}
}
pub fn enable_temperature(&self) -> Temperature {
cfg_if! {
- if #[cfg(adc_g0)] {
+ if #[cfg(any(adc_g0, adc_u0))] {
T::regs().ccr().modify(|reg| {
reg.set_tsen(true);
});
@@ -147,7 +163,7 @@ impl<'d, T: Instance> Adc<'d, T> {
pub fn enable_vbat(&self) -> Vbat {
cfg_if! {
- if #[cfg(adc_g0)] {
+ if #[cfg(any(adc_g0, adc_u0))] {
T::regs().ccr().modify(|reg| {
reg.set_vbaten(true);
});
@@ -170,9 +186,9 @@ impl<'d, T: Instance> Adc<'d, T> {
}
pub fn set_resolution(&mut self, resolution: Resolution) {
- #[cfg(not(adc_g0))]
+ #[cfg(not(any(adc_g0, adc_u0)))]
T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
- #[cfg(adc_g0)]
+ #[cfg(any(adc_g0, adc_u0))]
T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into()));
}
@@ -233,9 +249,9 @@ impl<'d, T: Instance> Adc<'d, T> {
Self::set_channel_sample_time(pin.channel(), self.sample_time);
// Select channel
- #[cfg(not(adc_g0))]
+ #[cfg(not(any(adc_g0, adc_u0)))]
T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel()));
- #[cfg(adc_g0)]
+ #[cfg(any(adc_g0, adc_u0))]
T::regs().chselr().write(|reg| reg.set_chsel(1 << pin.channel()));
// Some models are affected by an erratum:
@@ -263,7 +279,7 @@ impl<'d, T: Instance> Adc<'d, T> {
fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) {
cfg_if! {
- if #[cfg(adc_g0)] {
+ if #[cfg(any(adc_g0, adc_u0))] {
T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into()));
} else if #[cfg(adc_h5)] {
match _ch {
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs
index 1ae25bea2..ca87b41ee 100644
--- a/embassy-stm32/src/adc/v4.rs
+++ b/embassy-stm32/src/adc/v4.rs
@@ -1,9 +1,8 @@
-use embedded_hal_02::blocking::delay::DelayUs;
#[allow(unused)]
use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel};
use pac::adccommon::vals::Presc;
-use super::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime};
+use super::{blocking_delay_us, Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime};
use crate::time::Hertz;
use crate::{pac, Peripheral};
@@ -129,7 +128,7 @@ impl Prescaler {
impl<'d, T: Instance> Adc<'d, T> {
/// Create a new ADC driver.
- pub fn new(adc: impl Peripheral + 'd, delay: &mut impl DelayUs) -> Self {
+ pub fn new(adc: impl Peripheral + 'd) -> Self {
embassy_hal_internal::into_ref!(adc);
T::enable_and_reset();
@@ -161,11 +160,11 @@ impl<'d, T: Instance> Adc<'d, T> {
adc,
sample_time: SampleTime::from_bits(0),
};
- s.power_up(delay);
+ s.power_up();
s.configure_differential_inputs();
s.calibrate();
- delay.delay_us(1);
+ blocking_delay_us(1);
s.enable();
s.configure();
@@ -173,13 +172,13 @@ impl<'d, T: Instance> Adc<'d, T> {
s
}
- fn power_up(&mut self, delay: &mut impl DelayUs) {
+ fn power_up(&mut self) {
T::regs().cr().modify(|reg| {
reg.set_deeppwd(false);
reg.set_advregen(true);
});
- delay.delay_us(10);
+ blocking_delay_us(10);
}
fn configure_differential_inputs(&mut self) {
diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs
index e32f19d91..e5cfee528 100644
--- a/embassy-stm32/src/can/fd/peripheral.rs
+++ b/embassy-stm32/src/can/fd/peripheral.rs
@@ -368,6 +368,7 @@ impl Registers {
w.set_rfne(0, true); // Rx Fifo 0 New Msg
w.set_rfne(1, true); // Rx Fifo 1 New Msg
w.set_tce(true); // Tx Complete
+ w.set_boe(true); // Bus-Off Status Changed
});
self.regs.ile().modify(|w| {
w.set_eint0(true); // Interrupt Line 0
diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs
index e31821ca2..563f542d4 100644
--- a/embassy-stm32/src/can/fdcan.rs
+++ b/embassy-stm32/src/can/fdcan.rs
@@ -44,53 +44,51 @@ impl interrupt::typelevel::Handler for IT0Interrup
let ir = regs.ir().read();
- {
- if ir.tc() {
- regs.ir().write(|w| w.set_tc(true));
- }
- if ir.tefn() {
- regs.ir().write(|w| w.set_tefn(true));
- }
-
- match &T::state().tx_mode {
- TxMode::NonBuffered(waker) => waker.wake(),
- TxMode::ClassicBuffered(buf) => {
- if !T::registers().tx_queue_is_full() {
- match buf.tx_receiver.try_receive() {
- Ok(frame) => {
- _ = T::registers().write(&frame);
- }
- Err(_) => {}
- }
- }
- }
- TxMode::FdBuffered(buf) => {
- if !T::registers().tx_queue_is_full() {
- match buf.tx_receiver.try_receive() {
- Ok(frame) => {
- _ = T::registers().write(&frame);
- }
- Err(_) => {}
- }
- }
- }
- }
+ if ir.tc() {
+ regs.ir().write(|w| w.set_tc(true));
+ }
+ if ir.tefn() {
+ regs.ir().write(|w| w.set_tefn(true));
}
- if ir.ped() || ir.pea() {
- regs.ir().write(|w| {
- w.set_ped(true);
- w.set_pea(true);
- });
+ match &T::state().tx_mode {
+ TxMode::NonBuffered(waker) => waker.wake(),
+ TxMode::ClassicBuffered(buf) => {
+ if !T::registers().tx_queue_is_full() {
+ match buf.tx_receiver.try_receive() {
+ Ok(frame) => {
+ _ = T::registers().write(&frame);
+ }
+ Err(_) => {}
+ }
+ }
+ }
+ TxMode::FdBuffered(buf) => {
+ if !T::registers().tx_queue_is_full() {
+ match buf.tx_receiver.try_receive() {
+ Ok(frame) => {
+ _ = T::registers().write(&frame);
+ }
+ Err(_) => {}
+ }
+ }
+ }
}
if ir.rfn(0) {
T::state().rx_mode.on_interrupt::(0);
}
-
if ir.rfn(1) {
T::state().rx_mode.on_interrupt::(1);
}
+
+ if ir.bo() {
+ regs.ir().write(|w| w.set_bo(true));
+ if regs.psr().read().bo() {
+ // Initiate bus-off recovery sequence by resetting CCCR.INIT
+ regs.cccr().modify(|w| w.set_init(false));
+ }
+ }
}
}
diff --git a/embassy-stm32/src/crc/v1.rs b/embassy-stm32/src/crc/v1.rs
index f8909d438..e8e0270af 100644
--- a/embassy-stm32/src/crc/v1.rs
+++ b/embassy-stm32/src/crc/v1.rs
@@ -32,6 +32,9 @@ impl<'d> Crc<'d> {
/// Feeds a word to the peripheral and returns the current CRC value
pub fn feed_word(&mut self, word: u32) -> u32 {
// write a single byte to the device, and return the result
+ #[cfg(not(crc_v1))]
+ PAC_CRC.dr32().write_value(word);
+ #[cfg(crc_v1)]
PAC_CRC.dr().write_value(word);
self.read()
}
@@ -39,6 +42,9 @@ impl<'d> Crc<'d> {
/// Feed a slice of words to the peripheral and return the result.
pub fn feed_words(&mut self, words: &[u32]) -> u32 {
for word in words {
+ #[cfg(not(crc_v1))]
+ PAC_CRC.dr32().write_value(*word);
+ #[cfg(crc_v1)]
PAC_CRC.dr().write_value(*word);
}
@@ -46,6 +52,12 @@ impl<'d> Crc<'d> {
}
/// Read the CRC result value.
+ #[cfg(not(crc_v1))]
+ pub fn read(&self) -> u32 {
+ PAC_CRC.dr32().read()
+ }
+ /// Read the CRC result value.
+ #[cfg(crc_v1)]
pub fn read(&self) -> u32 {
PAC_CRC.dr().read()
}
diff --git a/embassy-stm32/src/crc/v2v3.rs b/embassy-stm32/src/crc/v2v3.rs
index 46f5ea1be..ad7c79f12 100644
--- a/embassy-stm32/src/crc/v2v3.rs
+++ b/embassy-stm32/src/crc/v2v3.rs
@@ -13,6 +13,8 @@ pub struct Crc<'d> {
}
/// CRC configuration errlr
+#[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ConfigError {
/// The selected polynomial is invalid.
InvalidPolynomial,
@@ -136,7 +138,7 @@ impl<'d> Crc<'d> {
/// Feeds a byte into the CRC peripheral. Returns the computed checksum.
pub fn feed_byte(&mut self, byte: u8) -> u32 {
PAC_CRC.dr8().write_value(byte);
- PAC_CRC.dr().read()
+ PAC_CRC.dr32().read()
}
/// Feeds an slice of bytes into the CRC peripheral. Returns the computed checksum.
@@ -144,30 +146,30 @@ impl<'d> Crc<'d> {
for byte in bytes {
PAC_CRC.dr8().write_value(*byte);
}
- PAC_CRC.dr().read()
+ PAC_CRC.dr32().read()
}
/// Feeds a halfword into the CRC peripheral. Returns the computed checksum.
pub fn feed_halfword(&mut self, halfword: u16) -> u32 {
PAC_CRC.dr16().write_value(halfword);
- PAC_CRC.dr().read()
+ PAC_CRC.dr32().read()
}
/// Feeds an slice of halfwords into the CRC peripheral. Returns the computed checksum.
pub fn feed_halfwords(&mut self, halfwords: &[u16]) -> u32 {
for halfword in halfwords {
PAC_CRC.dr16().write_value(*halfword);
}
- PAC_CRC.dr().read()
+ PAC_CRC.dr32().read()
}
/// Feeds a words into the CRC peripheral. Returns the computed checksum.
pub fn feed_word(&mut self, word: u32) -> u32 {
- PAC_CRC.dr().write_value(word as u32);
- PAC_CRC.dr().read()
+ PAC_CRC.dr32().write_value(word as u32);
+ PAC_CRC.dr32().read()
}
/// Feeds an slice of words into the CRC peripheral. Returns the computed checksum.
pub fn feed_words(&mut self, words: &[u32]) -> u32 {
for word in words {
- PAC_CRC.dr().write_value(*word as u32);
+ PAC_CRC.dr32().write_value(*word as u32);
}
- PAC_CRC.dr().read()
+ PAC_CRC.dr32().read()
}
}
diff --git a/embassy-stm32/src/dac/tsel.rs b/embassy-stm32/src/dac/tsel.rs
index 22d8d3dfa..1877954b9 100644
--- a/embassy-stm32/src/dac/tsel.rs
+++ b/embassy-stm32/src/dac/tsel.rs
@@ -235,6 +235,23 @@ pub enum TriggerSel {
Exti9 = 13,
}
+/// Trigger selection for U0.
+#[cfg(stm32u0)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum TriggerSel {
+ Software = 0,
+ Tim1 = 1,
+ Tim2 = 2,
+ Tim3 = 3,
+ Tim6 = 5,
+ Tim7 = 6,
+ Tim15 = 8,
+ Lptim1 = 11,
+ Lptim2 = 12,
+ Exti9 = 14,
+}
+
/// Trigger selection for G4.
#[cfg(stm32g4)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs
index 7e3681469..8766d0a60 100644
--- a/embassy-stm32/src/dma/mod.rs
+++ b/embassy-stm32/src/dma/mod.rs
@@ -16,6 +16,9 @@ mod dmamux;
#[cfg(dmamux)]
pub use dmamux::*;
+mod util;
+pub(crate) use util::*;
+
pub(crate) mod ringbuffer;
pub mod word;
diff --git a/embassy-stm32/src/dma/util.rs b/embassy-stm32/src/dma/util.rs
new file mode 100644
index 000000000..962ea2501
--- /dev/null
+++ b/embassy-stm32/src/dma/util.rs
@@ -0,0 +1,60 @@
+use embassy_hal_internal::PeripheralRef;
+
+use super::word::Word;
+use super::{AnyChannel, Request, Transfer, TransferOptions};
+
+/// Convenience wrapper, contains a channel and a request number.
+///
+/// Commonly used in peripheral drivers that own DMA channels.
+pub(crate) struct ChannelAndRequest<'d> {
+ pub channel: PeripheralRef<'d, AnyChannel>,
+ pub request: Request,
+}
+
+impl<'d> ChannelAndRequest<'d> {
+ pub unsafe fn read<'a, W: Word>(
+ &'a mut self,
+ peri_addr: *mut W,
+ buf: &'a mut [W],
+ options: TransferOptions,
+ ) -> Transfer<'a> {
+ Transfer::new_read(&mut self.channel, self.request, peri_addr, buf, options)
+ }
+
+ pub unsafe fn read_raw<'a, W: Word>(
+ &'a mut self,
+ peri_addr: *mut W,
+ buf: *mut [W],
+ options: TransferOptions,
+ ) -> Transfer<'a> {
+ Transfer::new_read_raw(&mut self.channel, self.request, peri_addr, buf, options)
+ }
+
+ pub unsafe fn write<'a, W: Word>(
+ &'a mut self,
+ buf: &'a [W],
+ peri_addr: *mut W,
+ options: TransferOptions,
+ ) -> Transfer<'a> {
+ Transfer::new_write(&mut self.channel, self.request, buf, peri_addr, options)
+ }
+
+ pub unsafe fn write_raw<'a, W: Word>(
+ &'a mut self,
+ buf: *const [W],
+ peri_addr: *mut W,
+ options: TransferOptions,
+ ) -> Transfer<'a> {
+ Transfer::new_write_raw(&mut self.channel, self.request, buf, peri_addr, options)
+ }
+
+ pub unsafe fn write_repeated<'a, W: Word>(
+ &'a mut self,
+ repeated: &'a W,
+ count: usize,
+ peri_addr: *mut W,
+ options: TransferOptions,
+ ) -> Transfer<'a> {
+ Transfer::new_write_repeated(&mut self.channel, self.request, repeated, count, peri_addr, options)
+ }
+}
diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs
index 8d5dae436..224d51b84 100644
--- a/embassy-stm32/src/exti.rs
+++ b/embassy-stm32/src/exti.rs
@@ -27,11 +27,11 @@ fn cpu_regs() -> pac::exti::Exti {
EXTI
}
-#[cfg(not(any(exti_c0, exti_g0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50)))]
+#[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50)))]
fn exticr_regs() -> pac::syscfg::Syscfg {
pac::SYSCFG
}
-#[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))]
+#[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))]
fn exticr_regs() -> pac::exti::Exti {
EXTI
}
@@ -44,9 +44,9 @@ unsafe fn on_irq() {
#[cfg(feature = "low-power")]
crate::low_power::on_wakeup_irq();
- #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))]
+ #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))]
let bits = EXTI.pr(0).read().0;
- #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))]
+ #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))]
let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0;
// We don't handle or change any EXTI lines above 16.
@@ -61,9 +61,9 @@ unsafe fn on_irq() {
}
// Clear pending
- #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))]
+ #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))]
EXTI.pr(0).write_value(Lines(bits));
- #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))]
+ #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))]
{
EXTI.rpr(0).write_value(Lines(bits));
EXTI.fpr(0).write_value(Lines(bits));
@@ -241,9 +241,9 @@ impl<'a> ExtiInputFuture<'a> {
EXTI.ftsr(0).modify(|w| w.set_line(pin, falling));
// clear pending bit
- #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))]
+ #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))]
EXTI.pr(0).write(|w| w.set_line(pin, true));
- #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))]
+ #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))]
{
EXTI.rpr(0).write(|w| w.set_line(pin, true));
EXTI.fpr(0).write(|w| w.set_line(pin, true));
diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs
index 00e61f2d2..90f13ff29 100644
--- a/embassy-stm32/src/flash/f4.rs
+++ b/embassy-stm32/src/flash/f4.rs
@@ -16,7 +16,7 @@ mod alt_regions {
use embassy_hal_internal::PeripheralRef;
use stm32_metapac::FLASH_SIZE;
- use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION};
+ use crate::_generated::flash_regions::{BANK1_REGION1, BANK1_REGION2, BANK1_REGION3};
use crate::flash::{asynch, Async, Bank1Region1, Bank1Region2, Blocking, Error, Flash, FlashBank, FlashRegion};
use crate::peripherals::FLASH;
@@ -62,7 +62,6 @@ mod alt_regions {
pub bank2_region1: AltBank2Region1<'d, MODE>,
pub bank2_region2: AltBank2Region2<'d, MODE>,
pub bank2_region3: AltBank2Region3<'d, MODE>,
- pub otp_region: OTPRegion<'d, MODE>,
}
impl<'d> Flash<'d> {
@@ -79,7 +78,6 @@ mod alt_regions {
bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData),
bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData),
bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData),
- otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData),
}
}
@@ -96,7 +94,6 @@ mod alt_regions {
bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData),
bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData),
bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData),
- otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData),
}
}
}
diff --git a/embassy-stm32/src/flash/h50.rs b/embassy-stm32/src/flash/h50.rs
index db05bef5d..56ea7a421 100644
--- a/embassy-stm32/src/flash/h50.rs
+++ b/embassy-stm32/src/flash/h50.rs
@@ -55,17 +55,18 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE])
}
pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
- assert!(sector.bank != FlashBank::Otp);
assert!(sector.index_in_bank < 8);
while busy() {}
interrupt::free(|_| {
pac::FLASH.nscr().modify(|w| {
- w.set_bksel(match sector.bank {
- FlashBank::Bank1 => Bksel::B_0X0,
- FlashBank::Bank2 => Bksel::B_0X1,
- _ => unreachable!(),
+ // BKSEL ignores SWAP_BANK, so we must take it into account here
+ w.set_bksel(match (sector.bank, banks_swapped()) {
+ (FlashBank::Bank1, false) => Bksel::BANK1,
+ (FlashBank::Bank2, true) => Bksel::BANK1,
+ (FlashBank::Bank2, false) => Bksel::BANK2,
+ (FlashBank::Bank1, true) => Bksel::BANK2,
});
w.set_snb(sector.index_in_bank);
w.set_ser(true);
@@ -113,6 +114,47 @@ pub(crate) unsafe fn clear_all_err() {
})
}
+/// Get the current SWAP_BANK option.
+///
+/// This value is only loaded on system or power-on reset. `perform_bank_swap()`
+/// will not reflect here.
+pub fn banks_swapped() -> bool {
+ pac::FLASH.optcr().read().swap_bank()
+}
+
+/// Logical, persistent swap of flash banks 1 and 2.
+///
+/// This allows the application to write a new firmware blob into bank 2, then
+/// swap the banks and perform a reset, loading the new firmware.
+///
+/// Swap does not take effect until system or power-on reset.
+///
+/// PLEASE READ THE REFERENCE MANUAL - there are nuances to this feature. For
+/// instance, erase commands and interrupt enables which take a flash bank as a
+/// parameter ignore the swap!
+pub fn perform_bank_swap() {
+ while busy() {}
+
+ unsafe {
+ clear_all_err();
+ }
+
+ // unlock OPTLOCK
+ pac::FLASH.optkeyr().write(|w| *w = 0x0819_2A3B);
+ pac::FLASH.optkeyr().write(|w| *w = 0x4C5D_6E7F);
+ while pac::FLASH.optcr().read().optlock() {}
+
+ // toggle SWAP_BANK option
+ pac::FLASH.optsr_prg().modify(|w| w.set_swap_bank(!banks_swapped()));
+
+ // load option bytes
+ pac::FLASH.optcr().modify(|w| w.set_optstrt(true));
+ while pac::FLASH.optcr().read().optstrt() {}
+
+ // re-lock OPTLOCK
+ pac::FLASH.optcr().modify(|w| w.set_optlock(true));
+}
+
fn sr_busy(sr: Nssr) -> bool {
// Note: RM0492 sometimes incorrectly refers to WBNE as NSWBNE
sr.bsy() || sr.dbne() || sr.wbne()
diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs
index 1d8031e82..8c6ca2471 100644
--- a/embassy-stm32/src/flash/mod.rs
+++ b/embassy-stm32/src/flash/mod.rs
@@ -89,8 +89,6 @@ pub enum FlashBank {
Bank1 = 0,
/// Bank 2
Bank2 = 1,
- /// OTP region
- Otp,
}
#[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")]
@@ -103,10 +101,11 @@ pub enum FlashBank {
#[cfg_attr(flash_h7ab, path = "h7.rs")]
#[cfg_attr(flash_u5, path = "u5.rs")]
#[cfg_attr(flash_h50, path = "h50.rs")]
+#[cfg_attr(flash_u0, path = "u0.rs")]
#[cfg_attr(
not(any(
flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f1, flash_f3, flash_f4, flash_f7, flash_g0,
- flash_g4, flash_h7, flash_h7ab, flash_u5, flash_h50
+ flash_g4, flash_h7, flash_h7ab, flash_u5, flash_h50, flash_u0
)),
path = "other.rs"
)]
diff --git a/embassy-stm32/src/flash/u0.rs b/embassy-stm32/src/flash/u0.rs
new file mode 100644
index 000000000..dfc5a2f76
--- /dev/null
+++ b/embassy-stm32/src/flash/u0.rs
@@ -0,0 +1,96 @@
+use core::ptr::write_volatile;
+use core::sync::atomic::{fence, Ordering};
+
+use cortex_m::interrupt;
+
+use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
+use crate::flash::Error;
+use crate::pac;
+
+pub(crate) const fn is_default_layout() -> bool {
+ true
+}
+
+pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
+ &FLASH_REGIONS
+}
+
+pub(crate) unsafe fn lock() {
+ pac::FLASH.cr().modify(|w| w.set_lock(true));
+}
+pub(crate) unsafe fn unlock() {
+ // Wait, while the memory interface is busy.
+ while pac::FLASH.sr().read().bsy1() {}
+
+ // Unlock flash
+ if pac::FLASH.cr().read().lock() {
+ pac::FLASH.keyr().write(|w| w.set_key(0x4567_0123));
+ pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB));
+ }
+}
+
+pub(crate) unsafe fn enable_blocking_write() {
+ assert_eq!(0, WRITE_SIZE % 4);
+ pac::FLASH.cr().write(|w| w.set_pg(true));
+}
+
+pub(crate) unsafe fn disable_blocking_write() {
+ pac::FLASH.cr().write(|w| w.set_pg(false));
+}
+
+pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
+ let mut address = start_address;
+ for val in buf.chunks(4) {
+ write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
+ address += val.len() as u32;
+
+ // prevents parallelism errors
+ fence(Ordering::SeqCst);
+ }
+
+ wait_ready_blocking()
+}
+
+pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
+ let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32;
+ while pac::FLASH.sr().read().bsy1() {}
+ clear_all_err();
+
+ interrupt::free(|_| {
+ pac::FLASH.cr().modify(|w| {
+ w.set_per(true);
+ w.set_pnb(idx as u8);
+ w.set_strt(true);
+ });
+ });
+
+ let ret: Result<(), Error> = wait_ready_blocking();
+ pac::FLASH.cr().modify(|w| w.set_per(false));
+ ret
+}
+
+pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> {
+ while pac::FLASH.sr().read().bsy1() {}
+
+ let sr = pac::FLASH.sr().read();
+
+ if sr.progerr() {
+ return Err(Error::Prog);
+ }
+
+ if sr.wrperr() {
+ return Err(Error::Protected);
+ }
+
+ if sr.pgaerr() {
+ return Err(Error::Unaligned);
+ }
+
+ Ok(())
+}
+
+pub(crate) unsafe fn clear_all_err() {
+ // read and write back the same value.
+ // This clears all "write 1 to clear" bits.
+ pac::FLASH.sr().modify(|_| {});
+}
diff --git a/embassy-stm32/src/flash/u5.rs b/embassy-stm32/src/flash/u5.rs
index 580c490da..ddd4d73ff 100644
--- a/embassy-stm32/src/flash/u5.rs
+++ b/embassy-stm32/src/flash/u5.rs
@@ -14,26 +14,43 @@ pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
}
pub(crate) unsafe fn lock() {
+ #[cfg(feature = "trustzone-secure")]
pac::FLASH.seccr().modify(|w| w.set_lock(true));
+ #[cfg(not(feature = "trustzone-secure"))]
+ pac::FLASH.nscr().modify(|w| w.set_lock(true));
}
pub(crate) unsafe fn unlock() {
+ #[cfg(feature = "trustzone-secure")]
if pac::FLASH.seccr().read().lock() {
pac::FLASH.seckeyr().write_value(0x4567_0123);
pac::FLASH.seckeyr().write_value(0xCDEF_89AB);
}
+ #[cfg(not(feature = "trustzone-secure"))]
+ if pac::FLASH.nscr().read().lock() {
+ pac::FLASH.nskeyr().write_value(0x4567_0123);
+ pac::FLASH.nskeyr().write_value(0xCDEF_89AB);
+ }
}
pub(crate) unsafe fn enable_blocking_write() {
assert_eq!(0, WRITE_SIZE % 4);
+ #[cfg(feature = "trustzone-secure")]
pac::FLASH.seccr().write(|w| {
w.set_pg(pac::flash::vals::SeccrPg::B_0X1);
});
+ #[cfg(not(feature = "trustzone-secure"))]
+ pac::FLASH.nscr().write(|w| {
+ w.set_pg(pac::flash::vals::NscrPg::B_0X1);
+ });
}
pub(crate) unsafe fn disable_blocking_write() {
+ #[cfg(feature = "trustzone-secure")]
pac::FLASH.seccr().write(|w| w.set_pg(pac::flash::vals::SeccrPg::B_0X0));
+ #[cfg(not(feature = "trustzone-secure"))]
+ pac::FLASH.nscr().write(|w| w.set_pg(pac::flash::vals::NscrPg::B_0X0));
}
pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
@@ -50,19 +67,35 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE])
}
pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
+ #[cfg(feature = "trustzone-secure")]
pac::FLASH.seccr().modify(|w| {
w.set_per(pac::flash::vals::SeccrPer::B_0X1);
w.set_pnb(sector.index_in_bank)
});
+ #[cfg(not(feature = "trustzone-secure"))]
+ pac::FLASH.nscr().modify(|w| {
+ w.set_per(pac::flash::vals::NscrPer::B_0X1);
+ w.set_pnb(sector.index_in_bank)
+ });
+ #[cfg(feature = "trustzone-secure")]
pac::FLASH.seccr().modify(|w| {
w.set_strt(true);
});
+ #[cfg(not(feature = "trustzone-secure"))]
+ pac::FLASH.nscr().modify(|w| {
+ w.set_strt(true);
+ });
let ret: Result<(), Error> = blocking_wait_ready();
+ #[cfg(feature = "trustzone-secure")]
pac::FLASH
.seccr()
.modify(|w| w.set_per(pac::flash::vals::SeccrPer::B_0X0));
+ #[cfg(not(feature = "trustzone-secure"))]
+ pac::FLASH
+ .nscr()
+ .modify(|w| w.set_per(pac::flash::vals::NscrPer::B_0X0));
clear_all_err();
ret
}
@@ -70,12 +103,18 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E
pub(crate) unsafe fn clear_all_err() {
// read and write back the same value.
// This clears all "write 1 to clear" bits.
+ #[cfg(feature = "trustzone-secure")]
pac::FLASH.secsr().modify(|_| {});
+ #[cfg(not(feature = "trustzone-secure"))]
+ pac::FLASH.nssr().modify(|_| {});
}
unsafe fn blocking_wait_ready() -> Result<(), Error> {
loop {
+ #[cfg(feature = "trustzone-secure")]
let sr = pac::FLASH.secsr().read();
+ #[cfg(not(feature = "trustzone-secure"))]
+ let sr = pac::FLASH.nssr().read();
if !sr.bsy() {
if sr.pgserr() {
diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs
index a46061d54..ccbea9831 100644
--- a/embassy-stm32/src/i2c/mod.rs
+++ b/embassy-stm32/src/i2c/mod.rs
@@ -14,9 +14,10 @@ use embassy_sync::waitqueue::AtomicWaker;
#[cfg(feature = "time")]
use embassy_time::{Duration, Instant};
-use crate::dma::NoDma;
+use crate::dma::ChannelAndRequest;
use crate::gpio::{AFType, Pull};
use crate::interrupt::typelevel::Interrupt;
+use crate::mode::{Async, Blocking, Mode};
use crate::time::Hertz;
use crate::{interrupt, peripherals};
@@ -71,17 +72,16 @@ impl Default for Config {
}
/// I2C driver.
-pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> {
+pub struct I2c<'d, T: Instance, M: Mode> {
_peri: PeripheralRef<'d, T>,
- #[allow(dead_code)]
- tx_dma: PeripheralRef<'d, TXDMA>,
- #[allow(dead_code)]
- rx_dma: PeripheralRef<'d, RXDMA>,
+ tx_dma: Option>,
+ rx_dma: Option>,
#[cfg(feature = "time")]
timeout: Duration,
+ _phantom: PhantomData,
}
-impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
+impl<'d, T: Instance> I2c<'d, T, Async> {
/// Create a new I2C driver.
pub fn new(
peri: impl Peripheral + 'd,
@@ -90,12 +90,40 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
_irq: impl interrupt::typelevel::Binding>
+ interrupt::typelevel::Binding>
+ 'd,
- tx_dma: impl Peripheral + 'd,
- rx_dma: impl Peripheral
+ 'd,
+ tx_dma: impl Peripheral
> + 'd,
+ rx_dma: impl Peripheral
> + 'd,
freq: Hertz,
config: Config,
) -> Self {
- into_ref!(peri, scl, sda, tx_dma, rx_dma);
+ Self::new_inner(peri, scl, sda, new_dma!(tx_dma), new_dma!(rx_dma), freq, config)
+ }
+}
+
+impl<'d, T: Instance> I2c<'d, T, Blocking> {
+ /// Create a new blocking I2C driver.
+ pub fn new_blocking(
+ peri: impl Peripheral
+ 'd,
+ scl: impl Peripheral
> + 'd,
+ sda: impl Peripheral
> + 'd,
+ freq: Hertz,
+ config: Config,
+ ) -> Self {
+ Self::new_inner(peri, scl, sda, None, None, freq, config)
+ }
+}
+
+impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
+ /// Create a new I2C driver.
+ fn new_inner(
+ peri: impl Peripheral
+ 'd,
+ scl: impl Peripheral
> + 'd,
+ sda: impl Peripheral
> + 'd,
+ tx_dma: Option>,
+ rx_dma: Option>,
+ freq: Hertz,
+ config: Config,
+ ) -> Self {
+ into_ref!(peri, scl, sda);
T::enable_and_reset();
@@ -125,6 +153,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
rx_dma,
#[cfg(feature = "time")]
timeout: config.timeout,
+ _phantom: PhantomData,
};
this.init(freq, config);
@@ -249,7 +278,7 @@ foreach_peripheral!(
};
);
-impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> {
+impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> {
type Error = Error;
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
@@ -257,7 +286,7 @@ impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> {
}
}
-impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> {
+impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> {
type Error = Error;
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
@@ -265,7 +294,7 @@ impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> {
}
}
-impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> {
+impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> {
type Error = Error;
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
@@ -289,11 +318,11 @@ impl embedded_hal_1::i2c::Error for Error {
}
}
-impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for I2c<'d, T, TXDMA, RXDMA> {
+impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> {
type Error = Error;
}
-impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> {
+impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> {
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(address, read)
}
@@ -315,7 +344,7 @@ impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> {
}
}
-impl<'d, T: Instance, TXDMA: TxDma, RXDMA: RxDma> embedded_hal_async::i2c::I2c for I2c<'d, T, TXDMA, RXDMA> {
+impl<'d, T: Instance> embedded_hal_async::i2c::I2c for I2c<'d, T, Async> {
async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
self.read(address, read).await
}
diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs
index d45c48b24..13a473344 100644
--- a/embassy-stm32/src/i2c/v1.rs
+++ b/embassy-stm32/src/i2c/v1.rs
@@ -13,7 +13,7 @@ use embassy_hal_internal::drop::OnDrop;
use embedded_hal_1::i2c::Operation;
use super::*;
-use crate::dma::Transfer;
+use crate::mode::Mode as PeriMode;
use crate::pac::i2c;
// /!\ /!\
@@ -41,7 +41,7 @@ pub unsafe fn on_interrupt() {
});
}
-impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
+impl<'d, T: Instance, M: PeriMode> I2c<'d, T, M> {
pub(crate) fn init(&mut self, freq: Hertz, _config: Config) {
T::regs().cr1().modify(|reg| {
reg.set_pe(false);
@@ -326,11 +326,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
w.set_itevten(true);
});
}
+}
- async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error>
- where
- TXDMA: crate::i2c::TxDma,
- {
+impl<'d, T: Instance> I2c<'d, T, Async> {
+ async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> {
T::regs().cr2().modify(|w| {
// Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for
// reception.
@@ -415,9 +414,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// this address from the memory after each TxE event.
let dst = T::regs().dr().as_ptr() as *mut u8;
- let ch = &mut self.tx_dma;
- let request = ch.request();
- Transfer::new_write(ch, request, write, dst, Default::default())
+ self.tx_dma.as_mut().unwrap().write(write, dst, Default::default())
};
// Wait for bytes to be sent, or an error to occur.
@@ -479,10 +476,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
}
/// Write.
- pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error>
- where
- TXDMA: crate::i2c::TxDma,
- {
+ pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {
self.write_frame(address, write, FrameOptions::FirstAndLastFrame)
.await?;
@@ -490,20 +484,14 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
}
/// Read.
- pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error>
- where
- RXDMA: crate::i2c::RxDma,
- {
+ pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
self.read_frame(address, buffer, FrameOptions::FirstAndLastFrame)
.await?;
Ok(())
}
- async fn read_frame(&mut self, address: u8, buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error>
- where
- RXDMA: crate::i2c::RxDma,
- {
+ async fn read_frame(&mut self, address: u8, buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error> {
if buffer.is_empty() {
return Err(Error::Overrun);
}
@@ -623,9 +611,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// from this address from the memory after each RxE event.
let src = T::regs().dr().as_ptr() as *mut u8;
- let ch = &mut self.rx_dma;
- let request = ch.request();
- Transfer::new_read(ch, request, src, buffer, Default::default())
+ self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default())
};
// Wait for bytes to be received, or an error to occur.
@@ -664,11 +650,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
}
/// Write, restart, read.
- pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error>
- where
- RXDMA: crate::i2c::RxDma,
- TXDMA: crate::i2c::TxDma,
- {
+ pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
// Check empty read buffer before starting transaction. Otherwise, we would not generate the
// stop condition below.
if read.is_empty() {
@@ -684,11 +666,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
/// Consecutive operations of same type are merged. See [transaction contract] for details.
///
/// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
- pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error>
- where
- RXDMA: crate::i2c::RxDma,
- TXDMA: crate::i2c::TxDma,
- {
+ pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {
for (op, frame) in operation_frames(operations)? {
match op {
Operation::Read(read) => self.read_frame(addr, read, frame).await?,
@@ -700,7 +678,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
}
}
-impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> {
+impl<'d, T: Instance, M: PeriMode> Drop for I2c<'d, T, M> {
fn drop(&mut self) {
T::disable();
}
@@ -806,7 +784,7 @@ impl Timings {
}
}
-impl<'d, T: Instance> SetConfig for I2c<'d, T> {
+impl<'d, T: Instance, M: PeriMode> SetConfig for I2c<'d, T, M> {
type Config = Hertz;
type ConfigError = ();
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs
index da3b0ee30..12df98534 100644
--- a/embassy-stm32/src/i2c/v2.rs
+++ b/embassy-stm32/src/i2c/v2.rs
@@ -7,7 +7,6 @@ use embassy_hal_internal::drop::OnDrop;
use embedded_hal_1::i2c::Operation;
use super::*;
-use crate::dma::Transfer;
use crate::pac::i2c;
pub(crate) unsafe fn on_interrupt() {
@@ -24,7 +23,7 @@ pub(crate) unsafe fn on_interrupt() {
});
}
-impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
+impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
pub(crate) fn init(&mut self, freq: Hertz, _config: Config) {
T::regs().cr1().modify(|reg| {
reg.set_pe(false);
@@ -302,276 +301,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
result
}
- async fn write_dma_internal(
- &mut self,
- address: u8,
- write: &[u8],
- first_slice: bool,
- last_slice: bool,
- timeout: Timeout,
- ) -> Result<(), Error>
- where
- TXDMA: crate::i2c::TxDma,
- {
- let total_len = write.len();
-
- let dma_transfer = unsafe {
- let regs = T::regs();
- regs.cr1().modify(|w| {
- w.set_txdmaen(true);
- if first_slice {
- w.set_tcie(true);
- }
- });
- let dst = regs.txdr().as_ptr() as *mut u8;
-
- let ch = &mut self.tx_dma;
- let request = ch.request();
- Transfer::new_write(ch, request, write, dst, Default::default())
- };
-
- let state = T::state();
- let mut remaining_len = total_len;
-
- let on_drop = OnDrop::new(|| {
- let regs = T::regs();
- regs.cr1().modify(|w| {
- if last_slice {
- w.set_txdmaen(false);
- }
- w.set_tcie(false);
- })
- });
-
- poll_fn(|cx| {
- state.waker.register(cx.waker());
-
- let isr = T::regs().isr().read();
- if remaining_len == total_len {
- if first_slice {
- Self::master_write(
- address,
- total_len.min(255),
- Stop::Software,
- (total_len > 255) || !last_slice,
- timeout,
- )?;
- } else {
- Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, timeout)?;
- T::regs().cr1().modify(|w| w.set_tcie(true));
- }
- } else if !(isr.tcr() || isr.tc()) {
- // poll_fn was woken without an interrupt present
- return Poll::Pending;
- } else if remaining_len == 0 {
- return Poll::Ready(Ok(()));
- } else {
- let last_piece = (remaining_len <= 255) && last_slice;
-
- if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, timeout) {
- return Poll::Ready(Err(e));
- }
- T::regs().cr1().modify(|w| w.set_tcie(true));
- }
-
- remaining_len = remaining_len.saturating_sub(255);
- Poll::Pending
- })
- .await?;
-
- dma_transfer.await;
-
- if last_slice {
- // This should be done already
- self.wait_tc(timeout)?;
- self.master_stop();
- }
-
- drop(on_drop);
-
- Ok(())
- }
-
- async fn read_dma_internal(
- &mut self,
- address: u8,
- buffer: &mut [u8],
- restart: bool,
- timeout: Timeout,
- ) -> Result<(), Error>
- where
- RXDMA: crate::i2c::RxDma,
- {
- let total_len = buffer.len();
-
- let dma_transfer = unsafe {
- let regs = T::regs();
- regs.cr1().modify(|w| {
- w.set_rxdmaen(true);
- w.set_tcie(true);
- });
- let src = regs.rxdr().as_ptr() as *mut u8;
-
- let ch = &mut self.rx_dma;
- let request = ch.request();
- Transfer::new_read(ch, request, src, buffer, Default::default())
- };
-
- let state = T::state();
- let mut remaining_len = total_len;
-
- let on_drop = OnDrop::new(|| {
- let regs = T::regs();
- regs.cr1().modify(|w| {
- w.set_rxdmaen(false);
- w.set_tcie(false);
- })
- });
-
- poll_fn(|cx| {
- state.waker.register(cx.waker());
-
- let isr = T::regs().isr().read();
- if remaining_len == total_len {
- Self::master_read(
- address,
- total_len.min(255),
- Stop::Software,
- total_len > 255,
- restart,
- timeout,
- )?;
- } else if !(isr.tcr() || isr.tc()) {
- // poll_fn was woken without an interrupt present
- return Poll::Pending;
- } else if remaining_len == 0 {
- return Poll::Ready(Ok(()));
- } else {
- let last_piece = remaining_len <= 255;
-
- if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, timeout) {
- return Poll::Ready(Err(e));
- }
- T::regs().cr1().modify(|w| w.set_tcie(true));
- }
-
- remaining_len = remaining_len.saturating_sub(255);
- Poll::Pending
- })
- .await?;
-
- dma_transfer.await;
-
- // This should be done already
- self.wait_tc(timeout)?;
- self.master_stop();
-
- drop(on_drop);
-
- Ok(())
- }
-
- // =========================
- // Async public API
-
- /// Write.
- pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error>
- where
- TXDMA: crate::i2c::TxDma,
- {
- let timeout = self.timeout();
- if write.is_empty() {
- self.write_internal(address, write, true, timeout)
- } else {
- timeout
- .with(self.write_dma_internal(address, write, true, true, timeout))
- .await
- }
- }
-
- /// Write multiple buffers.
- ///
- /// The buffers are concatenated in a single write transaction.
- pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error>
- where
- TXDMA: crate::i2c::TxDma,
- {
- let timeout = self.timeout();
-
- if write.is_empty() {
- return Err(Error::ZeroLengthTransfer);
- }
- let mut iter = write.iter();
-
- let mut first = true;
- let mut current = iter.next();
- while let Some(c) = current {
- let next = iter.next();
- let is_last = next.is_none();
-
- let fut = self.write_dma_internal(address, c, first, is_last, timeout);
- timeout.with(fut).await?;
- first = false;
- current = next;
- }
- Ok(())
- }
-
- /// Read.
- pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error>
- where
- RXDMA: crate::i2c::RxDma,
- {
- let timeout = self.timeout();
-
- if buffer.is_empty() {
- self.read_internal(address, buffer, false, timeout)
- } else {
- let fut = self.read_dma_internal(address, buffer, false, timeout);
- timeout.with(fut).await
- }
- }
-
- /// Write, restart, read.
- pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error>
- where
- TXDMA: super::TxDma,
- RXDMA: super::RxDma,
- {
- let timeout = self.timeout();
-
- if write.is_empty() {
- self.write_internal(address, write, false, timeout)?;
- } else {
- let fut = self.write_dma_internal(address, write, true, true, timeout);
- timeout.with(fut).await?;
- }
-
- if read.is_empty() {
- self.read_internal(address, read, true, timeout)?;
- } else {
- let fut = self.read_dma_internal(address, read, true, timeout);
- timeout.with(fut).await?;
- }
-
- Ok(())
- }
-
- /// Transaction with operations.
- ///
- /// Consecutive operations of same type are merged. See [transaction contract] for details.
- ///
- /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
- pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error>
- where
- RXDMA: crate::i2c::RxDma,
- TXDMA: crate::i2c::TxDma,
- {
- let _ = addr;
- let _ = operations;
- todo!()
- }
-
// =========================
// Blocking public API
@@ -684,7 +413,252 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
}
}
-impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> {
+impl<'d, T: Instance> I2c<'d, T, Async> {
+ async fn write_dma_internal(
+ &mut self,
+ address: u8,
+ write: &[u8],
+ first_slice: bool,
+ last_slice: bool,
+ timeout: Timeout,
+ ) -> Result<(), Error> {
+ let total_len = write.len();
+
+ let dma_transfer = unsafe {
+ let regs = T::regs();
+ regs.cr1().modify(|w| {
+ w.set_txdmaen(true);
+ if first_slice {
+ w.set_tcie(true);
+ }
+ });
+ let dst = regs.txdr().as_ptr() as *mut u8;
+
+ self.tx_dma.as_mut().unwrap().write(write, dst, Default::default())
+ };
+
+ let state = T::state();
+ let mut remaining_len = total_len;
+
+ let on_drop = OnDrop::new(|| {
+ let regs = T::regs();
+ regs.cr1().modify(|w| {
+ if last_slice {
+ w.set_txdmaen(false);
+ }
+ w.set_tcie(false);
+ })
+ });
+
+ poll_fn(|cx| {
+ state.waker.register(cx.waker());
+
+ let isr = T::regs().isr().read();
+ if remaining_len == total_len {
+ if first_slice {
+ Self::master_write(
+ address,
+ total_len.min(255),
+ Stop::Software,
+ (total_len > 255) || !last_slice,
+ timeout,
+ )?;
+ } else {
+ Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, timeout)?;
+ T::regs().cr1().modify(|w| w.set_tcie(true));
+ }
+ } else if !(isr.tcr() || isr.tc()) {
+ // poll_fn was woken without an interrupt present
+ return Poll::Pending;
+ } else if remaining_len == 0 {
+ return Poll::Ready(Ok(()));
+ } else {
+ let last_piece = (remaining_len <= 255) && last_slice;
+
+ if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, timeout) {
+ return Poll::Ready(Err(e));
+ }
+ T::regs().cr1().modify(|w| w.set_tcie(true));
+ }
+
+ remaining_len = remaining_len.saturating_sub(255);
+ Poll::Pending
+ })
+ .await?;
+
+ dma_transfer.await;
+
+ if last_slice {
+ // This should be done already
+ self.wait_tc(timeout)?;
+ self.master_stop();
+ }
+
+ drop(on_drop);
+
+ Ok(())
+ }
+
+ async fn read_dma_internal(
+ &mut self,
+ address: u8,
+ buffer: &mut [u8],
+ restart: bool,
+ timeout: Timeout,
+ ) -> Result<(), Error> {
+ let total_len = buffer.len();
+
+ let dma_transfer = unsafe {
+ let regs = T::regs();
+ regs.cr1().modify(|w| {
+ w.set_rxdmaen(true);
+ w.set_tcie(true);
+ });
+ let src = regs.rxdr().as_ptr() as *mut u8;
+
+ self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default())
+ };
+
+ let state = T::state();
+ let mut remaining_len = total_len;
+
+ let on_drop = OnDrop::new(|| {
+ let regs = T::regs();
+ regs.cr1().modify(|w| {
+ w.set_rxdmaen(false);
+ w.set_tcie(false);
+ })
+ });
+
+ poll_fn(|cx| {
+ state.waker.register(cx.waker());
+
+ let isr = T::regs().isr().read();
+ if remaining_len == total_len {
+ Self::master_read(
+ address,
+ total_len.min(255),
+ Stop::Software,
+ total_len > 255,
+ restart,
+ timeout,
+ )?;
+ } else if !(isr.tcr() || isr.tc()) {
+ // poll_fn was woken without an interrupt present
+ return Poll::Pending;
+ } else if remaining_len == 0 {
+ return Poll::Ready(Ok(()));
+ } else {
+ let last_piece = remaining_len <= 255;
+
+ if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, timeout) {
+ return Poll::Ready(Err(e));
+ }
+ T::regs().cr1().modify(|w| w.set_tcie(true));
+ }
+
+ remaining_len = remaining_len.saturating_sub(255);
+ Poll::Pending
+ })
+ .await?;
+
+ dma_transfer.await;
+
+ // This should be done already
+ self.wait_tc(timeout)?;
+ self.master_stop();
+
+ drop(on_drop);
+
+ Ok(())
+ }
+
+ // =========================
+ // Async public API
+
+ /// Write.
+ pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {
+ let timeout = self.timeout();
+ if write.is_empty() {
+ self.write_internal(address, write, true, timeout)
+ } else {
+ timeout
+ .with(self.write_dma_internal(address, write, true, true, timeout))
+ .await
+ }
+ }
+
+ /// Write multiple buffers.
+ ///
+ /// The buffers are concatenated in a single write transaction.
+ pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> {
+ let timeout = self.timeout();
+
+ if write.is_empty() {
+ return Err(Error::ZeroLengthTransfer);
+ }
+ let mut iter = write.iter();
+
+ let mut first = true;
+ let mut current = iter.next();
+ while let Some(c) = current {
+ let next = iter.next();
+ let is_last = next.is_none();
+
+ let fut = self.write_dma_internal(address, c, first, is_last, timeout);
+ timeout.with(fut).await?;
+ first = false;
+ current = next;
+ }
+ Ok(())
+ }
+
+ /// Read.
+ pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
+ let timeout = self.timeout();
+
+ if buffer.is_empty() {
+ self.read_internal(address, buffer, false, timeout)
+ } else {
+ let fut = self.read_dma_internal(address, buffer, false, timeout);
+ timeout.with(fut).await
+ }
+ }
+
+ /// Write, restart, read.
+ pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
+ let timeout = self.timeout();
+
+ if write.is_empty() {
+ self.write_internal(address, write, false, timeout)?;
+ } else {
+ let fut = self.write_dma_internal(address, write, true, true, timeout);
+ timeout.with(fut).await?;
+ }
+
+ if read.is_empty() {
+ self.read_internal(address, read, true, timeout)?;
+ } else {
+ let fut = self.read_dma_internal(address, read, true, timeout);
+ timeout.with(fut).await?;
+ }
+
+ Ok(())
+ }
+
+ /// Transaction with operations.
+ ///
+ /// Consecutive operations of same type are merged. See [transaction contract] for details.
+ ///
+ /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
+ pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {
+ let _ = addr;
+ let _ = operations;
+ todo!()
+ }
+}
+
+impl<'d, T: Instance, M: Mode> Drop for I2c<'d, T, M> {
fn drop(&mut self) {
T::disable();
}
@@ -814,7 +788,7 @@ impl Timings {
}
}
-impl<'d, T: Instance> SetConfig for I2c<'d, T> {
+impl<'d, T: Instance, M: Mode> SetConfig for I2c<'d, T, M> {
type Config = Hertz;
type ConfigError = ();
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs
index c5a606b21..9b80dc1d0 100644
--- a/embassy-stm32/src/i2s.rs
+++ b/embassy-stm32/src/i2s.rs
@@ -2,6 +2,7 @@
use embassy_hal_internal::into_ref;
use crate::gpio::{AFType, AnyPin, SealedPin};
+use crate::mode::Async;
use crate::pac::spi::vals;
use crate::spi::{Config as SpiConfig, *};
use crate::time::Hertz;
@@ -152,15 +153,15 @@ impl Default for Config {
}
/// I2S driver.
-pub struct I2S<'d, T: Instance, Tx, Rx> {
- _peri: Spi<'d, T, Tx, Rx>,
+pub struct I2S<'d, T: Instance> {
+ _peri: Spi<'d, T, Async>,
sd: Option>,
ws: Option>,
ck: Option>,
mck: Option>,
}
-impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> {
+impl<'d, T: Instance> I2S<'d, T> {
/// Note: Full-Duplex modes are not supported at this time
pub fn new(
peri: impl Peripheral + 'd,
@@ -168,8 +169,8 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> {
ws: impl Peripheral
> + 'd,
ck: impl Peripheral
> + 'd,
mck: impl Peripheral
> + 'd,
- txdma: impl Peripheral
+ 'd,
- rxdma: impl Peripheral
+ 'd,
+ txdma: impl Peripheral
> + 'd,
+ rxdma: impl Peripheral
> + 'd,
freq: Hertz,
config: Config,
) -> Self {
@@ -265,24 +266,17 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> {
}
/// Write audio data.
- pub async fn write(&mut self, data: &[W]) -> Result<(), Error>
- where
- Tx: TxDma,
- {
+ pub async fn write(&mut self, data: &[W]) -> Result<(), Error> {
self._peri.write(data).await
}
/// Read audio data.
- pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error>
- where
- Tx: TxDma,
- Rx: RxDma,
- {
+ pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> {
self._peri.read(data).await
}
}
-impl<'d, T: Instance, Tx, Rx> Drop for I2S<'d, T, Tx, Rx> {
+impl<'d, T: Instance> Drop for I2S<'d, T> {
fn drop(&mut self) {
self.sd.as_ref().map(|x| x.set_as_disconnected());
self.ws.as_ref().map(|x| x.set_as_disconnected());
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index ea17f8477..7d2b49ff4 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -15,8 +15,31 @@ mod fmt;
include!(concat!(env!("OUT_DIR"), "/_macros.rs"));
// Utilities
+mod macros;
pub mod time;
-mod traits;
+/// Operating modes for peripherals.
+pub mod mode {
+ trait SealedMode {}
+
+ /// Operating mode for a peripheral.
+ #[allow(private_bounds)]
+ pub trait Mode: SealedMode {}
+
+ macro_rules! impl_mode {
+ ($name:ident) => {
+ impl SealedMode for $name {}
+ impl Mode for $name {}
+ };
+ }
+
+ /// Blocking mode.
+ pub struct Blocking;
+ /// Async mode.
+ pub struct Async;
+
+ impl_mode!(Blocking);
+ impl_mode!(Async);
+}
// Always-present hardware
pub mod dma;
diff --git a/embassy-stm32/src/traits.rs b/embassy-stm32/src/macros.rs
similarity index 77%
rename from embassy-stm32/src/traits.rs
rename to embassy-stm32/src/macros.rs
index 13f695821..14137bc37 100644
--- a/embassy-stm32/src/traits.rs
+++ b/embassy-stm32/src/macros.rs
@@ -69,3 +69,29 @@ macro_rules! dma_trait_impl {
}
};
}
+
+macro_rules! new_dma {
+ ($name:ident) => {{
+ let dma = $name.into_ref();
+ let request = dma.request();
+ Some(crate::dma::ChannelAndRequest {
+ channel: dma.map_into(),
+ request,
+ })
+ }};
+}
+
+macro_rules! new_pin {
+ ($name:ident, $aftype:expr) => {{
+ new_pin!($name, $aftype, crate::gpio::Speed::Medium, crate::gpio::Pull::None)
+ }};
+ ($name:ident, $aftype:expr, $speed:expr) => {
+ new_pin!($name, $aftype, $speed, crate::gpio::Pull::None)
+ };
+ ($name:ident, $aftype:expr, $speed:expr, $pull:expr) => {{
+ let pin = $name.into_ref();
+ pin.set_as_af_pull(pin.af_num(), $aftype, $pull);
+ pin.set_speed($speed);
+ Some(pin.map_into())
+ }};
+}
diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs
index 9079ddd41..d7235ac7f 100644
--- a/embassy-stm32/src/rcc/l.rs
+++ b/embassy-stm32/src/rcc/l.rs
@@ -49,6 +49,7 @@ pub struct Config {
pub sys: Sysclk,
pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
+ #[cfg(not(stm32u0))]
pub apb2_pre: APBPrescaler,
#[cfg(any(stm32wl5x, stm32wb))]
pub core2_ahb_pre: AHBPrescaler,
@@ -75,6 +76,7 @@ impl Default for Config {
sys: Sysclk::MSI,
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
+ #[cfg(not(stm32u0))]
apb2_pre: APBPrescaler::DIV1,
#[cfg(any(stm32wl5x, stm32wb))]
core2_ahb_pre: AHBPrescaler::DIV1,
@@ -130,7 +132,7 @@ pub const WPAN_DEFAULT: Config = Config {
};
fn msi_enable(range: MSIRange) {
- #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
+ #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))]
RCC.cr().modify(|w| {
#[cfg(not(stm32wb))]
w.set_msirgsel(crate::pac::rcc::vals::Msirgsel::CR);
@@ -240,7 +242,7 @@ pub(crate) unsafe fn init(config: Config) {
let pll_input = PllInput {
hse,
hsi,
- #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
+ #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))]
msi,
};
let pll = init_pll(PllInstance::Pll, config.pll, &pll_input);
@@ -254,6 +256,10 @@ pub(crate) unsafe fn init(config: Config) {
Sysclk::HSI => hsi.unwrap(),
Sysclk::MSI => msi.unwrap(),
Sysclk::PLL1_R => pll.r.unwrap(),
+ #[cfg(stm32u0)]
+ Sysclk::LSI | Sysclk::LSE => todo!(),
+ #[cfg(stm32u0)]
+ Sysclk::_RESERVED_6 | Sysclk::_RESERVED_7 => unreachable!(),
};
#[cfg(rcc_l4plus)]
@@ -263,6 +269,7 @@ pub(crate) unsafe fn init(config: Config) {
let hclk1 = sys_clk / config.ahb_pre;
let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre);
+ #[cfg(not(stm32u0))]
let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre);
#[cfg(any(stm32l4, stm32l5, stm32wlex))]
let hclk2 = hclk1;
@@ -315,6 +322,13 @@ pub(crate) unsafe fn init(config: Config) {
..=64_000_000 => 3,
_ => 4,
};
+ #[cfg(stm32u0)]
+ let latency = match hclk1.0 {
+ // VOS RANGE1, others TODO.
+ ..=24_000_000 => 0,
+ ..=48_000_000 => 1,
+ _ => 2,
+ };
#[cfg(stm32l1)]
FLASH.acr().write(|w| w.set_acc64(true));
@@ -326,7 +340,11 @@ pub(crate) unsafe fn init(config: Config) {
RCC.cfgr().modify(|w| {
w.set_sw(config.sys);
w.set_hpre(config.ahb_pre);
+ #[cfg(stm32u0)]
+ w.set_ppre(config.apb1_pre);
+ #[cfg(not(stm32u0))]
w.set_ppre1(config.apb1_pre);
+ #[cfg(not(stm32u0))]
w.set_ppre2(config.apb2_pre);
});
while RCC.cfgr().read().sws() != config.sys {}
@@ -353,8 +371,10 @@ pub(crate) unsafe fn init(config: Config) {
#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
hclk3: Some(hclk3),
pclk1: Some(pclk1),
+ #[cfg(not(stm32u0))]
pclk2: Some(pclk2),
pclk1_tim: Some(pclk1_tim),
+ #[cfg(not(stm32u0))]
pclk2_tim: Some(pclk2_tim),
#[cfg(stm32wl)]
pclk3: Some(hclk3),
@@ -408,7 +428,7 @@ fn msirange_to_hertz(range: MSIRange) -> Hertz {
Hertz(32_768 * (1 << (range as u8 + 1)))
}
-#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
+#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))]
fn msirange_to_hertz(range: MSIRange) -> Hertz {
match range {
MSIRange::RANGE100K => Hertz(100_000),
@@ -521,7 +541,7 @@ mod pll {
}
}
-#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
+#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))]
mod pll {
use super::{pll_enable, PllInstance};
pub use crate::pac::rcc::vals::{
diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs
index d8604e07e..4b22a099d 100644
--- a/embassy-stm32/src/rcc/mco.rs
+++ b/embassy-stm32/src/rcc/mco.rs
@@ -52,7 +52,7 @@ macro_rules! impl_peri {
};
}
-#[cfg(any(rcc_c0, rcc_g0))]
+#[cfg(any(rcc_c0, rcc_g0, rcc_u0))]
#[allow(unused_imports)]
use self::{McoSource as Mco1Source, McoSource as Mco2Source};
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index c328344aa..a4e497fe7 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -25,7 +25,7 @@ pub use hsi48::*;
#[cfg_attr(stm32g0, path = "g0.rs")]
#[cfg_attr(stm32g4, path = "g4.rs")]
#[cfg_attr(any(stm32h5, stm32h7), path = "h.rs")]
-#[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl), path = "l.rs")]
+#[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl, stm32u0), path = "l.rs")]
#[cfg_attr(stm32u5, path = "u5.rs")]
#[cfg_attr(stm32wba, path = "wba.rs")]
mod _version;
@@ -119,3 +119,21 @@ mod util {
pub fn frequency() -> Hertz {
T::frequency()
}
+
+/// Enables and resets peripheral `T`.
+///
+/// # Safety
+///
+/// Peripheral must not be in use.
+pub unsafe fn enable_and_reset() {
+ T::enable_and_reset();
+}
+
+/// Disables peripheral `T`.
+///
+/// # Safety
+///
+/// Peripheral must not be in use.
+pub unsafe fn disable() {
+ T::disable();
+}
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs
index 0b38c4288..c39ef1913 100644
--- a/embassy-stm32/src/spi/mod.rs
+++ b/embassy-stm32/src/spi/mod.rs
@@ -1,6 +1,7 @@
//! Serial Peripheral Interface (SPI)
#![macro_use]
+use core::marker::PhantomData;
use core::ptr;
use embassy_embedded_hal::SetConfig;
@@ -8,8 +9,9 @@ use embassy_futures::join::join;
use embassy_hal_internal::{into_ref, PeripheralRef};
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
-use crate::dma::{slice_ptr_parts, word, Transfer};
-use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _};
+use crate::dma::{slice_ptr_parts, word, ChannelAndRequest};
+use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _, Speed};
+use crate::mode::{Async, Blocking, Mode as PeriMode};
use crate::pac::spi::{regs, vals, Spi as Regs};
use crate::rcc::RccPeripheral;
use crate::time::Hertz;
@@ -81,163 +83,37 @@ impl Config {
BitOrder::MsbFirst => vals::Lsbfirst::MSBFIRST,
}
}
-}
+ fn sck_pull_mode(&self) -> Pull {
+ match self.mode.polarity {
+ Polarity::IdleLow => Pull::Down,
+ Polarity::IdleHigh => Pull::Up,
+ }
+ }
+}
/// SPI driver.
-pub struct Spi<'d, T: Instance, Tx, Rx> {
+pub struct Spi<'d, T: Instance, M: PeriMode> {
_peri: PeripheralRef<'d, T>,
sck: Option>,
mosi: Option>,
miso: Option>,
- txdma: PeripheralRef<'d, Tx>,
- rxdma: PeripheralRef<'d, Rx>,
+ tx_dma: Option>,
+ rx_dma: Option>,
+ _phantom: PhantomData,
current_word_size: word_impl::Config,
}
-impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
- /// Create a new SPI driver.
- pub fn new(
- peri: impl Peripheral + 'd,
- sck: impl Peripheral
> + 'd,
- mosi: impl Peripheral
> + 'd,
- miso: impl Peripheral
> + 'd,
- txdma: impl Peripheral
+ 'd,
- rxdma: impl Peripheral
+ 'd,
- config: Config,
- ) -> Self {
- into_ref!(peri, sck, mosi, miso);
-
- let sck_pull_mode = match config.mode.polarity {
- Polarity::IdleLow => Pull::Down,
- Polarity::IdleHigh => Pull::Up,
- };
-
- sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, sck_pull_mode);
- sck.set_speed(crate::gpio::Speed::VeryHigh);
- mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull);
- mosi.set_speed(crate::gpio::Speed::VeryHigh);
- miso.set_as_af(miso.af_num(), AFType::Input);
- miso.set_speed(crate::gpio::Speed::VeryHigh);
-
- Self::new_inner(
- peri,
- Some(sck.map_into()),
- Some(mosi.map_into()),
- Some(miso.map_into()),
- txdma,
- rxdma,
- config,
- )
- }
-
- /// Create a new SPI driver, in RX-only mode (only MISO pin, no MOSI).
- pub fn new_rxonly(
- peri: impl Peripheral
+ 'd,
- sck: impl Peripheral
> + 'd,
- miso: impl Peripheral
> + 'd,
- txdma: impl Peripheral
+ 'd, // TODO remove
- rxdma: impl Peripheral
+ 'd,
- config: Config,
- ) -> Self {
- into_ref!(sck, miso);
- sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
- sck.set_speed(crate::gpio::Speed::VeryHigh);
- miso.set_as_af(miso.af_num(), AFType::Input);
- miso.set_speed(crate::gpio::Speed::VeryHigh);
-
- Self::new_inner(
- peri,
- Some(sck.map_into()),
- None,
- Some(miso.map_into()),
- txdma,
- rxdma,
- config,
- )
- }
-
- /// Create a new SPI driver, in TX-only mode (only MOSI pin, no MISO).
- pub fn new_txonly(
- peri: impl Peripheral
+ 'd,
- sck: impl Peripheral
> + 'd,
- mosi: impl Peripheral
> + 'd,
- txdma: impl Peripheral
+ 'd,
- rxdma: impl Peripheral
+ 'd, // TODO remove
- config: Config,
- ) -> Self {
- into_ref!(sck, mosi);
- sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
- sck.set_speed(crate::gpio::Speed::VeryHigh);
- mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull);
- mosi.set_speed(crate::gpio::Speed::VeryHigh);
-
- Self::new_inner(
- peri,
- Some(sck.map_into()),
- Some(mosi.map_into()),
- None,
- txdma,
- rxdma,
- config,
- )
- }
-
- /// Create a new SPI driver, in TX-only mode, without SCK pin.
- ///
- /// This can be useful for bit-banging non-SPI protocols.
- pub fn new_txonly_nosck(
- peri: impl Peripheral
+ 'd,
- mosi: impl Peripheral
> + 'd,
- txdma: impl Peripheral
+ 'd,
- rxdma: impl Peripheral
+ 'd, // TODO: remove
- config: Config,
- ) -> Self {
- into_ref!(mosi);
- mosi.set_as_af_pull(mosi.af_num(), AFType::OutputPushPull, Pull::Down);
- mosi.set_speed(crate::gpio::Speed::Medium);
-
- Self::new_inner(peri, None, Some(mosi.map_into()), None, txdma, rxdma, config)
- }
-
- #[cfg(stm32wl)]
- /// Useful for on chip peripherals like SUBGHZ which are hardwired.
- pub fn new_subghz(
- peri: impl Peripheral
+ 'd,
- txdma: impl Peripheral
+ 'd,
- rxdma: impl Peripheral
+ 'd,
- ) -> Self {
- // see RM0453 rev 1 section 7.2.13 page 291
- // The SUBGHZSPI_SCK frequency is obtained by PCLK3 divided by two.
- // The SUBGHZSPI_SCK clock maximum speed must not exceed 16 MHz.
- let pclk3_freq = ::frequency().0;
- let freq = Hertz(core::cmp::min(pclk3_freq / 2, 16_000_000));
- let mut config = Config::default();
- config.mode = MODE_0;
- config.bit_order = BitOrder::MsbFirst;
- config.frequency = freq;
- Self::new_inner(peri, None, None, None, txdma, rxdma, config)
- }
-
- #[allow(dead_code)]
- pub(crate) fn new_internal(
- peri: impl Peripheral + 'd,
- txdma: impl Peripheral
+ 'd,
- rxdma: impl Peripheral
+ 'd,
- config: Config,
- ) -> Self {
- Self::new_inner(peri, None, None, None, txdma, rxdma, config)
- }
-
+impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> {
fn new_inner(
peri: impl Peripheral
+ 'd,
sck: Option>,
mosi: Option>,
miso: Option>,
- txdma: impl Peripheral + 'd,
- rxdma: impl Peripheral
+ 'd,
+ tx_dma: Option>,
+ rx_dma: Option>,
config: Config,
) -> Self {
- into_ref!(peri, txdma, rxdma);
+ into_ref!(peri);
let pclk = T::frequency();
let freq = config.frequency;
@@ -333,9 +209,10 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
sck,
mosi,
miso,
- txdma,
- rxdma,
+ tx_dma,
+ rx_dma,
current_word_size: ::CONFIG,
+ _phantom: PhantomData,
}
}
@@ -462,169 +339,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
self.current_word_size = word_size;
}
- /// SPI write, using DMA.
- pub async fn write(&mut self, data: &[W]) -> Result<(), Error>
- where
- Tx: TxDma,
- {
- if data.is_empty() {
- return Ok(());
- }
-
- self.set_word_size(W::CONFIG);
- T::REGS.cr1().modify(|w| {
- w.set_spe(false);
- });
-
- let tx_request = self.txdma.request();
- let tx_dst = T::REGS.tx_ptr();
- let tx_f = unsafe { Transfer::new_write(&mut self.txdma, tx_request, data, tx_dst, Default::default()) };
-
- set_txdmaen(T::REGS, true);
- T::REGS.cr1().modify(|w| {
- w.set_spe(true);
- });
- #[cfg(any(spi_v3, spi_v4, spi_v5))]
- T::REGS.cr1().modify(|w| {
- w.set_cstart(true);
- });
-
- tx_f.await;
-
- finish_dma(T::REGS);
-
- Ok(())
- }
-
- /// SPI read, using DMA.
- pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error>
- where
- Tx: TxDma,
- Rx: RxDma,
- {
- if data.is_empty() {
- return Ok(());
- }
-
- self.set_word_size(W::CONFIG);
- T::REGS.cr1().modify(|w| {
- w.set_spe(false);
- });
-
- // SPIv3 clears rxfifo on SPE=0
- #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
- flush_rx_fifo(T::REGS);
-
- set_rxdmaen(T::REGS, true);
-
- let clock_byte_count = data.len();
-
- let rx_request = self.rxdma.request();
- let rx_src = T::REGS.rx_ptr();
- let rx_f = unsafe { Transfer::new_read(&mut self.rxdma, rx_request, rx_src, data, Default::default()) };
-
- let tx_request = self.txdma.request();
- let tx_dst = T::REGS.tx_ptr();
- let clock_byte = 0x00u8;
- let tx_f = unsafe {
- Transfer::new_write_repeated(
- &mut self.txdma,
- tx_request,
- &clock_byte,
- clock_byte_count,
- tx_dst,
- Default::default(),
- )
- };
-
- set_txdmaen(T::REGS, true);
- T::REGS.cr1().modify(|w| {
- w.set_spe(true);
- });
- #[cfg(any(spi_v3, spi_v4, spi_v5))]
- T::REGS.cr1().modify(|w| {
- w.set_cstart(true);
- });
-
- join(tx_f, rx_f).await;
-
- finish_dma(T::REGS);
-
- Ok(())
- }
-
- async fn transfer_inner(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error>
- where
- Tx: TxDma,
- Rx: RxDma,
- {
- let (_, rx_len) = slice_ptr_parts(read);
- let (_, tx_len) = slice_ptr_parts(write);
- assert_eq!(rx_len, tx_len);
- if rx_len == 0 {
- return Ok(());
- }
-
- self.set_word_size(W::CONFIG);
- T::REGS.cr1().modify(|w| {
- w.set_spe(false);
- });
-
- // SPIv3 clears rxfifo on SPE=0
- #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
- flush_rx_fifo(T::REGS);
-
- set_rxdmaen(T::REGS, true);
-
- let rx_request = self.rxdma.request();
- let rx_src = T::REGS.rx_ptr();
- let rx_f = unsafe { Transfer::new_read_raw(&mut self.rxdma, rx_request, rx_src, read, Default::default()) };
-
- let tx_request = self.txdma.request();
- let tx_dst = T::REGS.tx_ptr();
- let tx_f = unsafe { Transfer::new_write_raw(&mut self.txdma, tx_request, write, tx_dst, Default::default()) };
-
- set_txdmaen(T::REGS, true);
- T::REGS.cr1().modify(|w| {
- w.set_spe(true);
- });
- #[cfg(any(spi_v3, spi_v4, spi_v5))]
- T::REGS.cr1().modify(|w| {
- w.set_cstart(true);
- });
-
- join(tx_f, rx_f).await;
-
- finish_dma(T::REGS);
-
- Ok(())
- }
-
- /// Bidirectional transfer, using DMA.
- ///
- /// This transfers both buffers at the same time, so it is NOT equivalent to `write` followed by `read`.
- ///
- /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored.
- /// If `write` is shorter it is padded with zero bytes.
- pub async fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error>
- where
- Tx: TxDma,
- Rx: RxDma,
- {
- self.transfer_inner(read, write).await
- }
-
- /// In-place bidirectional transfer, using DMA.
- ///
- /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time.
- pub async fn transfer_in_place(&mut self, data: &mut [W]) -> Result<(), Error>
- where
- Tx: TxDma,
- Rx: RxDma,
- {
- self.transfer_inner(data, data).await
- }
-
/// Blocking write.
pub fn blocking_write(&mut self, words: &[W]) -> Result<(), Error> {
T::REGS.cr1().modify(|w| w.set_spe(true));
@@ -682,7 +396,334 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
}
}
-impl<'d, T: Instance, Tx, Rx> Drop for Spi<'d, T, Tx, Rx> {
+impl<'d, T: Instance> Spi<'d, T, Blocking> {
+ /// Create a new blocking SPI driver.
+ pub fn new_blocking(
+ peri: impl Peripheral + 'd,
+ sck: impl Peripheral
> + 'd,
+ mosi: impl Peripheral
> + 'd,
+ miso: impl Peripheral
> + 'd,
+ config: Config,
+ ) -> Self {
+ Self::new_inner(
+ peri,
+ new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()),
+ new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh),
+ new_pin!(miso, AFType::Input, Speed::VeryHigh),
+ None,
+ None,
+ config,
+ )
+ }
+
+ /// Create a new blocking SPI driver, in RX-only mode (only MISO pin, no MOSI).
+ pub fn new_blocking_rxonly(
+ peri: impl Peripheral
+ 'd,
+ sck: impl Peripheral
> + 'd,
+ miso: impl Peripheral
> + 'd,
+ config: Config,
+ ) -> Self {
+ Self::new_inner(
+ peri,
+ new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()),
+ None,
+ new_pin!(miso, AFType::Input, Speed::VeryHigh),
+ None,
+ None,
+ config,
+ )
+ }
+
+ /// Create a new blocking SPI driver, in TX-only mode (only MOSI pin, no MISO).
+ pub fn new_blocking_txonly(
+ peri: impl Peripheral
+ 'd,
+ sck: impl Peripheral
> + 'd,
+ mosi: impl Peripheral
> + 'd,
+ config: Config,
+ ) -> Self {
+ Self::new_inner(
+ peri,
+ new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()),
+ new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh),
+ None,
+ None,
+ None,
+ config,
+ )
+ }
+
+ /// Create a new SPI driver, in TX-only mode, without SCK pin.
+ ///
+ /// This can be useful for bit-banging non-SPI protocols.
+ pub fn new_blocking_txonly_nosck(
+ peri: impl Peripheral
+ 'd,
+ mosi: impl Peripheral
> + 'd,
+ config: Config,
+ ) -> Self {
+ Self::new_inner(
+ peri,
+ None,
+ new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh),
+ None,
+ None,
+ None,
+ config,
+ )
+ }
+}
+
+impl<'d, T: Instance> Spi<'d, T, Async> {
+ /// Create a new SPI driver.
+ pub fn new(
+ peri: impl Peripheral
+ 'd,
+ sck: impl Peripheral
> + 'd,
+ mosi: impl Peripheral
> + 'd,
+ miso: impl Peripheral
> + 'd,
+ tx_dma: impl Peripheral
> + 'd,
+ rx_dma: impl Peripheral
> + 'd,
+ config: Config,
+ ) -> Self {
+ Self::new_inner(
+ peri,
+ new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()),
+ new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh),
+ new_pin!(miso, AFType::Input, Speed::VeryHigh),
+ new_dma!(tx_dma),
+ new_dma!(rx_dma),
+ config,
+ )
+ }
+
+ /// Create a new SPI driver, in RX-only mode (only MISO pin, no MOSI).
+ pub fn new_rxonly(
+ peri: impl Peripheral
+ 'd,
+ sck: impl Peripheral
> + 'd,
+ miso: impl Peripheral
> + 'd,
+ rx_dma: impl Peripheral
> + 'd,
+ config: Config,
+ ) -> Self {
+ Self::new_inner(
+ peri,
+ new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()),
+ None,
+ new_pin!(miso, AFType::Input, Speed::VeryHigh),
+ None,
+ new_dma!(rx_dma),
+ config,
+ )
+ }
+
+ /// Create a new SPI driver, in TX-only mode (only MOSI pin, no MISO).
+ pub fn new_txonly(
+ peri: impl Peripheral
+ 'd,
+ sck: impl Peripheral
> + 'd,
+ mosi: impl Peripheral
> + 'd,
+ tx_dma: impl Peripheral
> + 'd,
+ config: Config,
+ ) -> Self {
+ Self::new_inner(
+ peri,
+ new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()),
+ new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh),
+ None,
+ new_dma!(tx_dma),
+ None,
+ config,
+ )
+ }
+
+ /// Create a new SPI driver, in TX-only mode, without SCK pin.
+ ///
+ /// This can be useful for bit-banging non-SPI protocols.
+ pub fn new_txonly_nosck(
+ peri: impl Peripheral
+ 'd,
+ mosi: impl Peripheral
> + 'd,
+ tx_dma: impl Peripheral
> + 'd,
+ config: Config,
+ ) -> Self {
+ Self::new_inner(
+ peri,
+ None,
+ new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh),
+ None,
+ new_dma!(tx_dma),
+ None,
+ config,
+ )
+ }
+
+ #[cfg(stm32wl)]
+ /// Useful for on chip peripherals like SUBGHZ which are hardwired.
+ pub fn new_subghz(
+ peri: impl Peripheral
+ 'd,
+ tx_dma: impl Peripheral
> + 'd,
+ rx_dma: impl Peripheral
> + 'd,
+ ) -> Self {
+ // see RM0453 rev 1 section 7.2.13 page 291
+ // The SUBGHZSPI_SCK frequency is obtained by PCLK3 divided by two.
+ // The SUBGHZSPI_SCK clock maximum speed must not exceed 16 MHz.
+ let pclk3_freq = ::frequency().0;
+ let freq = Hertz(core::cmp::min(pclk3_freq / 2, 16_000_000));
+ let mut config = Config::default();
+ config.mode = MODE_0;
+ config.bit_order = BitOrder::MsbFirst;
+ config.frequency = freq;
+
+ Self::new_inner(peri, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config)
+ }
+
+ #[allow(dead_code)]
+ pub(crate) fn new_internal(
+ peri: impl Peripheral + 'd,
+ tx_dma: impl Peripheral
> + 'd,
+ rx_dma: impl Peripheral
> + 'd,
+ config: Config,
+ ) -> Self {
+ Self::new_inner(peri, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config)
+ }
+
+ /// SPI write, using DMA.
+ pub async fn write(&mut self, data: &[W]) -> Result<(), Error> {
+ if data.is_empty() {
+ return Ok(());
+ }
+
+ self.set_word_size(W::CONFIG);
+ T::REGS.cr1().modify(|w| {
+ w.set_spe(false);
+ });
+
+ let tx_dst = T::REGS.tx_ptr();
+ let tx_f = unsafe { self.tx_dma.as_mut().unwrap().write(data, tx_dst, Default::default()) };
+
+ set_txdmaen(T::REGS, true);
+ T::REGS.cr1().modify(|w| {
+ w.set_spe(true);
+ });
+ #[cfg(any(spi_v3, spi_v4, spi_v5))]
+ T::REGS.cr1().modify(|w| {
+ w.set_cstart(true);
+ });
+
+ tx_f.await;
+
+ finish_dma(T::REGS);
+
+ Ok(())
+ }
+
+ /// SPI read, using DMA.
+ pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> {
+ if data.is_empty() {
+ return Ok(());
+ }
+
+ self.set_word_size(W::CONFIG);
+ T::REGS.cr1().modify(|w| {
+ w.set_spe(false);
+ });
+
+ // SPIv3 clears rxfifo on SPE=0
+ #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
+ flush_rx_fifo(T::REGS);
+
+ set_rxdmaen(T::REGS, true);
+
+ let clock_byte_count = data.len();
+
+ let rx_src = T::REGS.rx_ptr();
+ let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read(rx_src, data, Default::default()) };
+
+ let tx_dst = T::REGS.tx_ptr();
+ let clock_byte = 0x00u8;
+ let tx_f = unsafe {
+ self.tx_dma
+ .as_mut()
+ .unwrap()
+ .write_repeated(&clock_byte, clock_byte_count, tx_dst, Default::default())
+ };
+
+ set_txdmaen(T::REGS, true);
+ T::REGS.cr1().modify(|w| {
+ w.set_spe(true);
+ });
+ #[cfg(any(spi_v3, spi_v4, spi_v5))]
+ T::REGS.cr1().modify(|w| {
+ w.set_cstart(true);
+ });
+
+ join(tx_f, rx_f).await;
+
+ finish_dma(T::REGS);
+
+ Ok(())
+ }
+
+ async fn transfer_inner(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error> {
+ let (_, rx_len) = slice_ptr_parts(read);
+ let (_, tx_len) = slice_ptr_parts(write);
+ assert_eq!(rx_len, tx_len);
+ if rx_len == 0 {
+ return Ok(());
+ }
+
+ self.set_word_size(W::CONFIG);
+ T::REGS.cr1().modify(|w| {
+ w.set_spe(false);
+ });
+
+ // SPIv3 clears rxfifo on SPE=0
+ #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
+ flush_rx_fifo(T::REGS);
+
+ set_rxdmaen(T::REGS, true);
+
+ let rx_src = T::REGS.rx_ptr();
+ let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read_raw(rx_src, read, Default::default()) };
+
+ let tx_dst = T::REGS.tx_ptr();
+ let tx_f = unsafe {
+ self.tx_dma
+ .as_mut()
+ .unwrap()
+ .write_raw(write, tx_dst, Default::default())
+ };
+
+ set_txdmaen(T::REGS, true);
+ T::REGS.cr1().modify(|w| {
+ w.set_spe(true);
+ });
+ #[cfg(any(spi_v3, spi_v4, spi_v5))]
+ T::REGS.cr1().modify(|w| {
+ w.set_cstart(true);
+ });
+
+ join(tx_f, rx_f).await;
+
+ finish_dma(T::REGS);
+
+ Ok(())
+ }
+
+ /// Bidirectional transfer, using DMA.
+ ///
+ /// This transfers both buffers at the same time, so it is NOT equivalent to `write` followed by `read`.
+ ///
+ /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored.
+ /// If `write` is shorter it is padded with zero bytes.
+ pub async fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> {
+ self.transfer_inner(read, write).await
+ }
+
+ /// In-place bidirectional transfer, using DMA.
+ ///
+ /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time.
+ pub async fn transfer_in_place(&mut self, data: &mut [W]) -> Result<(), Error> {
+ self.transfer_inner(data, data).await
+ }
+}
+
+impl<'d, T: Instance, M: PeriMode> Drop for Spi<'d, T, M> {
fn drop(&mut self) {
self.sck.as_ref().map(|x| x.set_as_disconnected());
self.mosi.as_ref().map(|x| x.set_as_disconnected());
@@ -735,18 +776,22 @@ trait RegsExt {
impl RegsExt for Regs {
fn tx_ptr(&self) -> *mut W {
- #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
+ #[cfg(any(spi_v1, spi_f1))]
let dr = self.dr();
+ #[cfg(spi_v2)]
+ let dr = self.dr16();
#[cfg(any(spi_v3, spi_v4, spi_v5))]
- let dr = self.txdr();
+ let dr = self.txdr32();
dr.as_ptr() as *mut W
}
fn rx_ptr(&self) -> *mut W {
- #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
+ #[cfg(any(spi_v1, spi_f1))]
let dr = self.dr();
+ #[cfg(spi_v2)]
+ let dr = self.dr16();
#[cfg(any(spi_v3, spi_v4, spi_v5))]
- let dr = self.rxdr();
+ let dr = self.rxdr32();
dr.as_ptr() as *mut W
}
}
@@ -815,11 +860,14 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> {
fn flush_rx_fifo(regs: Regs) {
#[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
while regs.sr().read().rxne() {
+ #[cfg(not(spi_v2))]
let _ = regs.dr().read();
+ #[cfg(spi_v2)]
+ let _ = regs.dr16().read();
}
#[cfg(any(spi_v3, spi_v4, spi_v5))]
while regs.sr().read().rxp() {
- let _ = regs.rxdr().read();
+ let _ = regs.rxdr32().read();
}
}
@@ -893,7 +941,7 @@ fn transfer_word(regs: Regs, tx_word: W) -> Result {
// some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289
macro_rules! impl_blocking {
($w:ident) => {
- impl<'d, T: Instance, Tx, Rx> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, T, Tx, Rx> {
+ impl<'d, T: Instance, M: PeriMode> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, T, M> {
type Error = Error;
fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> {
@@ -901,7 +949,7 @@ macro_rules! impl_blocking {
}
}
- impl<'d, T: Instance, Tx, Rx> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, T, Tx, Rx> {
+ impl<'d, T: Instance, M: PeriMode> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, T, M> {
type Error = Error;
fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> {
@@ -915,11 +963,11 @@ macro_rules! impl_blocking {
impl_blocking!(u8);
impl_blocking!(u16);
-impl<'d, T: Instance, Tx, Rx> embedded_hal_1::spi::ErrorType for Spi<'d, T, Tx, Rx> {
+impl<'d, T: Instance, M: PeriMode> embedded_hal_1::spi::ErrorType for Spi<'d, T, M> {
type Error = Error;
}
-impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBus for Spi<'d, T, Tx, Rx> {
+impl<'d, T: Instance, W: Word, M: PeriMode> embedded_hal_1::spi::SpiBus for Spi<'d, T, M> {
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
@@ -952,7 +1000,7 @@ impl embedded_hal_1::spi::Error for Error {
}
}
-impl<'d, T: Instance, Tx: TxDma, Rx: RxDma, W: Word> embedded_hal_async::spi::SpiBus for Spi<'d, T, Tx, Rx> {
+impl<'d, T: Instance, W: Word> embedded_hal_async::spi::SpiBus for Spi<'d, T, Async> {
async fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
@@ -1087,7 +1135,7 @@ foreach_peripheral!(
};
);
-impl<'d, T: Instance, Tx, Rx> SetConfig for Spi<'d, T, Tx, Rx> {
+impl<'d, T: Instance, M: PeriMode> SetConfig for Spi<'d, T, M> {
type Config = Config;
type ConfigError = ();
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs
index 7c0523a25..d21e5c47c 100644
--- a/embassy-stm32/src/usart/mod.rs
+++ b/embassy-stm32/src/usart/mod.rs
@@ -13,9 +13,10 @@ use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use futures::future::{select, Either};
-use crate::dma::{NoDma, Transfer};
-use crate::gpio::AFType;
+use crate::dma::ChannelAndRequest;
+use crate::gpio::{AFType, AnyPin, SealedPin};
use crate::interrupt::typelevel::Interrupt;
+use crate::mode::{Async, Blocking, Mode};
#[allow(unused_imports)]
#[cfg(not(any(usart_v1, usart_v2)))]
use crate::pac::usart::regs::Isr as Sr;
@@ -162,6 +163,26 @@ pub struct Config {
/// Set this to true to invert RX pin signal values (VDD =0/mark, Gnd = 1/idle).
#[cfg(any(usart_v3, usart_v4))]
pub invert_rx: bool,
+
+ // private: set by new_half_duplex, not by the user.
+ half_duplex: bool,
+}
+
+impl Config {
+ fn tx_af(&self) -> AFType {
+ #[cfg(any(usart_v3, usart_v4))]
+ if self.swap_rx_tx {
+ return AFType::Input;
+ };
+ AFType::OutputPushPull
+ }
+ fn rx_af(&self) -> AFType {
+ #[cfg(any(usart_v3, usart_v4))]
+ if self.swap_rx_tx {
+ return AFType::OutputPushPull;
+ };
+ AFType::Input
+ }
}
impl Default for Config {
@@ -181,6 +202,7 @@ impl Default for Config {
invert_tx: false,
#[cfg(any(usart_v3, usart_v4))]
invert_rx: false,
+ half_duplex: false,
}
}
}
@@ -217,12 +239,12 @@ enum ReadCompletionEvent {
///
/// See [`UartRx`] for more details, and see [`BufferedUart`] and [`RingBufferedUartRx`]
/// as alternatives that do provide the necessary guarantees for `embedded_io::Read`.
-pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> {
- tx: UartTx<'d, T, TxDma>,
- rx: UartRx<'d, T, RxDma>,
+pub struct Uart<'d, T: BasicInstance, M: Mode> {
+ tx: UartTx<'d, T, M>,
+ rx: UartRx<'d, T, M>,
}
-impl<'d, T: BasicInstance, TxDma, RxDma> SetConfig for Uart<'d, T, TxDma, RxDma> {
+impl<'d, T: BasicInstance, M: Mode> SetConfig for Uart<'d, T, M> {
type Config = Config;
type ConfigError = ConfigError;
@@ -236,12 +258,15 @@ impl<'d, T: BasicInstance, TxDma, RxDma> SetConfig for Uart<'d, T, TxDma, RxDma>
///
/// Can be obtained from [`Uart::split`], or can be constructed independently,
/// if you do not need the receiving half of the driver.
-pub struct UartTx<'d, T: BasicInstance, TxDma = NoDma> {
- phantom: PhantomData<&'d mut T>,
- tx_dma: PeripheralRef<'d, TxDma>,
+pub struct UartTx<'d, T: BasicInstance, M: Mode> {
+ _phantom: PhantomData<(T, M)>,
+ tx: Option>,
+ cts: Option>,
+ de: Option>,
+ tx_dma: Option>,
}
-impl<'d, T: BasicInstance, TxDma> SetConfig for UartTx<'d, T, TxDma> {
+impl<'d, T: BasicInstance, M: Mode> SetConfig for UartTx<'d, T, M> {
type Config = Config;
type ConfigError = ConfigError;
@@ -279,15 +304,17 @@ impl<'d, T: BasicInstance, TxDma> SetConfig for UartTx<'d, T, TxDma> {
/// store data received between calls.
///
/// Also see [this github comment](https://github.com/embassy-rs/embassy/pull/2185#issuecomment-1810047043).
-pub struct UartRx<'d, T: BasicInstance, RxDma = NoDma> {
- _peri: PeripheralRef<'d, T>,
- rx_dma: PeripheralRef<'d, RxDma>,
+pub struct UartRx<'d, T: BasicInstance, M: Mode> {
+ _phantom: PhantomData<(T, M)>,
+ rx: Option>,
+ rts: Option>,
+ rx_dma: Option>,
detect_previous_overrun: bool,
#[cfg(any(usart_v1, usart_v2))]
buffered_sr: stm32_metapac::usart::regs::Sr,
}
-impl<'d, T: BasicInstance, RxDma> SetConfig for UartRx<'d, T, RxDma> {
+impl<'d, T: BasicInstance, M: Mode> SetConfig for UartRx<'d, T, M> {
type Config = Config;
type ConfigError = ConfigError;
@@ -296,17 +323,21 @@ impl<'d, T: BasicInstance, RxDma> SetConfig for UartRx<'d, T, RxDma> {
}
}
-impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> {
+impl<'d, T: BasicInstance> UartTx<'d, T, Async> {
/// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power.
pub fn new(
peri: impl Peripheral + 'd,
tx: impl Peripheral
> + 'd,
- tx_dma: impl Peripheral
+ 'd,
+ tx_dma: impl Peripheral
> + 'd,
config: Config,
) -> Result {
- T::enable_and_reset();
-
- Self::new_inner(peri, tx, tx_dma, config)
+ Self::new_inner(
+ peri,
+ new_pin!(tx, AFType::OutputPushPull),
+ None,
+ new_dma!(tx_dma),
+ config,
+ )
}
/// Create a new tx-only UART with a clear-to-send pin
@@ -314,40 +345,86 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> {
peri: impl Peripheral + 'd,
tx: impl Peripheral
> + 'd,
cts: impl Peripheral
> + 'd,
- tx_dma: impl Peripheral
+ 'd,
+ tx_dma: impl Peripheral
> + 'd,
config: Config,
) -> Result {
- into_ref!(cts);
-
- T::enable_and_reset();
-
- cts.set_as_af(cts.af_num(), AFType::Input);
- T::regs().cr3().write(|w| {
- w.set_ctse(true);
- });
- Self::new_inner(peri, tx, tx_dma, config)
+ Self::new_inner(
+ peri,
+ new_pin!(tx, AFType::OutputPushPull),
+ new_pin!(cts, AFType::Input),
+ new_dma!(tx_dma),
+ config,
+ )
}
- fn new_inner(
- _peri: impl Peripheral + 'd,
+ /// Initiate an asynchronous UART write
+ pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
+ let ch = self.tx_dma.as_mut().unwrap();
+ T::regs().cr3().modify(|reg| {
+ reg.set_dmat(true);
+ });
+ // If we don't assign future to a variable, the data register pointer
+ // is held across an await and makes the future non-Send.
+ let transfer = unsafe { ch.write(buffer, tdr(T::regs()), Default::default()) };
+ transfer.await;
+ Ok(())
+ }
+}
+
+impl<'d, T: BasicInstance> UartTx<'d, T, Blocking> {
+ /// Create a new blocking tx-only UART with no hardware flow control.
+ ///
+ /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power.
+ pub fn new_blocking(
+ peri: impl Peripheral
+ 'd,
tx: impl Peripheral
> + 'd,
- tx_dma: impl Peripheral
+ 'd,
config: Config,
) -> Result {
- into_ref!(_peri, tx, tx_dma);
+ Self::new_inner(peri, new_pin!(tx, AFType::OutputPushPull), None, None, config)
+ }
+
+ /// Create a new blocking tx-only UART with a clear-to-send pin
+ pub fn new_blocking_with_cts(
+ peri: impl Peripheral + 'd,
+ tx: impl Peripheral
> + 'd,
+ cts: impl Peripheral
> + 'd,
+ config: Config,
+ ) -> Result {
+ Self::new_inner(
+ peri,
+ new_pin!(tx, AFType::OutputPushPull),
+ new_pin!(cts, AFType::Input),
+ None,
+ config,
+ )
+ }
+}
+
+impl<'d, T: BasicInstance, M: Mode> UartTx<'d, T, M> {
+ fn new_inner(
+ _peri: impl Peripheral + 'd,
+ tx: Option>,
+ cts: Option>,
+ tx_dma: Option>,
+ config: Config,
+ ) -> Result {
+ T::enable_and_reset();
let r = T::regs();
-
- tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
-
+ r.cr3().modify(|w| {
+ w.set_ctse(cts.is_some());
+ });
configure(r, &config, T::frequency(), T::KIND, false, true)?;
// create state once!
let _s = T::state();
Ok(Self {
+ tx,
+ cts,
+ de: None,
tx_dma,
- phantom: PhantomData,
+ _phantom: PhantomData,
})
}
@@ -356,23 +433,6 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> {
reconfigure::(config)
}
- /// Initiate an asynchronous UART write
- pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error>
- where
- TxDma: crate::usart::TxDma,
- {
- let ch = &mut self.tx_dma;
- let request = ch.request();
- T::regs().cr3().modify(|reg| {
- reg.set_dmat(true);
- });
- // If we don't assign future to a variable, the data register pointer
- // is held across an await and makes the future non-Send.
- let transfer = unsafe { Transfer::new_write(ch, request, buffer, tdr(T::regs()), Default::default()) };
- transfer.await;
- Ok(())
- }
-
/// Perform a blocking UART write
pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
let r = T::regs();
@@ -391,18 +451,18 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> {
}
}
-impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
+impl<'d, T: BasicInstance> UartRx<'d, T, Async> {
+ /// Create a new rx-only UART with no hardware flow control.
+ ///
/// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power.
pub fn new(
peri: impl Peripheral + 'd,
_irq: impl interrupt::typelevel::Binding> + 'd,
rx: impl Peripheral> + 'd,
- rx_dma: impl Peripheral
+ 'd,
+ rx_dma: impl Peripheral
> + 'd,
config: Config,
) -> Result {
- T::enable_and_reset();
-
- Self::new_inner(peri, rx, rx_dma, config)
+ Self::new_inner(peri, new_pin!(rx, AFType::Input), None, new_dma!(rx_dma), config)
}
/// Create a new rx-only UART with a request-to-send pin
@@ -411,143 +471,27 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
_irq: impl interrupt::typelevel::Binding> + 'd,
rx: impl Peripheral> + 'd,
rts: impl Peripheral
> + 'd,
- rx_dma: impl Peripheral
+ 'd,
+ rx_dma: impl Peripheral
> + 'd,
config: Config,
) -> Result {
- into_ref!(rts);
-
- T::enable_and_reset();
-
- rts.set_as_af(rts.af_num(), AFType::OutputPushPull);
- T::regs().cr3().write(|w| {
- w.set_rtse(true);
- });
-
- Self::new_inner(peri, rx, rx_dma, config)
- }
-
- fn new_inner(
- peri: impl Peripheral + 'd,
- rx: impl Peripheral
> + 'd,
- rx_dma: impl Peripheral
+ 'd,
- config: Config,
- ) -> Result {
- into_ref!(peri, rx, rx_dma);
-
- let r = T::regs();
-
- rx.set_as_af(rx.af_num(), AFType::Input);
-
- configure(r, &config, T::frequency(), T::KIND, true, false)?;
-
- T::Interrupt::unpend();
- unsafe { T::Interrupt::enable() };
-
- // create state once!
- let _s = T::state();
-
- Ok(Self {
- _peri: peri,
- rx_dma,
- detect_previous_overrun: config.detect_previous_overrun,
- #[cfg(any(usart_v1, usart_v2))]
- buffered_sr: stm32_metapac::usart::regs::Sr(0),
- })
- }
-
- /// Reconfigure the driver
- pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
- reconfigure::(config)
- }
-
- #[cfg(any(usart_v1, usart_v2))]
- fn check_rx_flags(&mut self) -> Result {
- let r = T::regs();
- loop {
- // Handle all buffered error flags.
- if self.buffered_sr.pe() {
- self.buffered_sr.set_pe(false);
- return Err(Error::Parity);
- } else if self.buffered_sr.fe() {
- self.buffered_sr.set_fe(false);
- return Err(Error::Framing);
- } else if self.buffered_sr.ne() {
- self.buffered_sr.set_ne(false);
- return Err(Error::Noise);
- } else if self.buffered_sr.ore() {
- self.buffered_sr.set_ore(false);
- return Err(Error::Overrun);
- } else if self.buffered_sr.rxne() {
- self.buffered_sr.set_rxne(false);
- return Ok(true);
- } else {
- // No error flags from previous iterations were set: Check the actual status register
- let sr = r.sr().read();
- if !sr.rxne() {
- return Ok(false);
- }
-
- // Buffer the status register and let the loop handle the error flags.
- self.buffered_sr = sr;
- }
- }
- }
-
- #[cfg(any(usart_v3, usart_v4))]
- fn check_rx_flags(&mut self) -> Result {
- let r = T::regs();
- let sr = r.isr().read();
- if sr.pe() {
- r.icr().write(|w| w.set_pe(true));
- return Err(Error::Parity);
- } else if sr.fe() {
- r.icr().write(|w| w.set_fe(true));
- return Err(Error::Framing);
- } else if sr.ne() {
- r.icr().write(|w| w.set_ne(true));
- return Err(Error::Noise);
- } else if sr.ore() {
- r.icr().write(|w| w.set_ore(true));
- return Err(Error::Overrun);
- }
- Ok(sr.rxne())
+ Self::new_inner(
+ peri,
+ new_pin!(rx, AFType::Input),
+ new_pin!(rts, AFType::OutputPushPull),
+ new_dma!(rx_dma),
+ config,
+ )
}
/// Initiate an asynchronous UART read
- pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error>
- where
- RxDma: crate::usart::RxDma,
- {
+ pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
self.inner_read(buffer, false).await?;
Ok(())
}
- /// Read a single u8 if there is one available, otherwise return WouldBlock
- pub fn nb_read(&mut self) -> Result> {
- let r = T::regs();
- if self.check_rx_flags()? {
- Ok(unsafe { rdr(r).read_volatile() })
- } else {
- Err(nb::Error::WouldBlock)
- }
- }
-
- /// Perform a blocking read into `buffer`
- pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
- let r = T::regs();
- for b in buffer {
- while !self.check_rx_flags()? {}
- unsafe { *b = rdr(r).read_volatile() }
- }
- Ok(())
- }
-
/// Initiate an asynchronous read with idle line detection enabled
- pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result
- where
- RxDma: crate::usart::RxDma,
- {
+ pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result {
self.inner_read(buffer, true).await
}
@@ -555,10 +499,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
&mut self,
buffer: &mut [u8],
enable_idle_line_detection: bool,
- ) -> Result
- where
- RxDma: crate::usart::RxDma,
- {
+ ) -> Result {
let r = T::regs();
// make sure USART state is restored to neutral state when this future is dropped
@@ -581,15 +522,14 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
});
});
- let ch = &mut self.rx_dma;
- let request = ch.request();
+ let ch = self.rx_dma.as_mut().unwrap();
let buffer_len = buffer.len();
// Start USART DMA
// will not do anything yet because DMAR is not yet set
// future which will complete when DMA Read request completes
- let transfer = unsafe { Transfer::new_read(ch, request, rdr(T::regs()), buffer, Default::default()) };
+ let transfer = unsafe { ch.read(rdr(T::regs()), buffer, Default::default()) };
// clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer
if !self.detect_previous_overrun {
@@ -732,10 +672,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
r
}
- async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result
- where
- RxDma: crate::usart::RxDma,
- {
+ async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result {
if buffer.is_empty() {
return Ok(0);
} else if buffer.len() > 0xFFFF {
@@ -755,34 +692,186 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
}
}
-impl<'d, T: BasicInstance, TxDma> Drop for UartTx<'d, T, TxDma> {
+impl<'d, T: BasicInstance> UartRx<'d, T, Blocking> {
+ /// Create a new rx-only UART with no hardware flow control.
+ ///
+ /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power.
+ pub fn new_blocking(
+ peri: impl Peripheral + 'd,
+ rx: impl Peripheral
> + 'd,
+ config: Config,
+ ) -> Result {
+ Self::new_inner(peri, new_pin!(rx, AFType::Input), None, None, config)
+ }
+
+ /// Create a new rx-only UART with a request-to-send pin
+ pub fn new_blocking_with_rts(
+ peri: impl Peripheral + 'd,
+ rx: impl Peripheral
> + 'd,
+ rts: impl Peripheral
> + 'd,
+ config: Config,
+ ) -> Result {
+ Self::new_inner(
+ peri,
+ new_pin!(rx, AFType::Input),
+ new_pin!(rts, AFType::OutputPushPull),
+ None,
+ config,
+ )
+ }
+}
+
+impl<'d, T: BasicInstance, M: Mode> UartRx<'d, T, M> {
+ fn new_inner(
+ _peri: impl Peripheral + 'd,
+ rx: Option>,
+ rts: Option>,
+ rx_dma: Option>,
+ config: Config,
+ ) -> Result {
+ T::enable_and_reset();
+
+ let r = T::regs();
+ r.cr3().write(|w| {
+ w.set_rtse(rts.is_some());
+ });
+ configure(r, &config, T::frequency(), T::KIND, true, false)?;
+
+ T::Interrupt::unpend();
+ unsafe { T::Interrupt::enable() };
+
+ // create state once!
+ let _s = T::state();
+
+ Ok(Self {
+ _phantom: PhantomData,
+ rx,
+ rts,
+ rx_dma,
+ detect_previous_overrun: config.detect_previous_overrun,
+ #[cfg(any(usart_v1, usart_v2))]
+ buffered_sr: stm32_metapac::usart::regs::Sr(0),
+ })
+ }
+
+ /// Reconfigure the driver
+ pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
+ reconfigure::(config)
+ }
+
+ #[cfg(any(usart_v1, usart_v2))]
+ fn check_rx_flags(&mut self) -> Result {
+ let r = T::regs();
+ loop {
+ // Handle all buffered error flags.
+ if self.buffered_sr.pe() {
+ self.buffered_sr.set_pe(false);
+ return Err(Error::Parity);
+ } else if self.buffered_sr.fe() {
+ self.buffered_sr.set_fe(false);
+ return Err(Error::Framing);
+ } else if self.buffered_sr.ne() {
+ self.buffered_sr.set_ne(false);
+ return Err(Error::Noise);
+ } else if self.buffered_sr.ore() {
+ self.buffered_sr.set_ore(false);
+ return Err(Error::Overrun);
+ } else if self.buffered_sr.rxne() {
+ self.buffered_sr.set_rxne(false);
+ return Ok(true);
+ } else {
+ // No error flags from previous iterations were set: Check the actual status register
+ let sr = r.sr().read();
+ if !sr.rxne() {
+ return Ok(false);
+ }
+
+ // Buffer the status register and let the loop handle the error flags.
+ self.buffered_sr = sr;
+ }
+ }
+ }
+
+ #[cfg(any(usart_v3, usart_v4))]
+ fn check_rx_flags(&mut self) -> Result {
+ let r = T::regs();
+ let sr = r.isr().read();
+ if sr.pe() {
+ r.icr().write(|w| w.set_pe(true));
+ return Err(Error::Parity);
+ } else if sr.fe() {
+ r.icr().write(|w| w.set_fe(true));
+ return Err(Error::Framing);
+ } else if sr.ne() {
+ r.icr().write(|w| w.set_ne(true));
+ return Err(Error::Noise);
+ } else if sr.ore() {
+ r.icr().write(|w| w.set_ore(true));
+ return Err(Error::Overrun);
+ }
+ Ok(sr.rxne())
+ }
+
+ /// Read a single u8 if there is one available, otherwise return WouldBlock
+ pub(crate) fn nb_read(&mut self) -> Result> {
+ let r = T::regs();
+ if self.check_rx_flags()? {
+ Ok(unsafe { rdr(r).read_volatile() })
+ } else {
+ Err(nb::Error::WouldBlock)
+ }
+ }
+
+ /// Perform a blocking read into `buffer`
+ pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
+ let r = T::regs();
+ for b in buffer {
+ while !self.check_rx_flags()? {}
+ unsafe { *b = rdr(r).read_volatile() }
+ }
+ Ok(())
+ }
+}
+
+impl<'d, T: BasicInstance, M: Mode> Drop for UartTx<'d, T, M> {
fn drop(&mut self) {
+ self.tx.as_ref().map(|x| x.set_as_disconnected());
+ self.cts.as_ref().map(|x| x.set_as_disconnected());
+ self.de.as_ref().map(|x| x.set_as_disconnected());
T::disable();
}
}
-impl<'d, T: BasicInstance, TxDma> Drop for UartRx<'d, T, TxDma> {
+impl<'d, T: BasicInstance, M: Mode> Drop for UartRx<'d, T, M> {
fn drop(&mut self) {
+ self.rx.as_ref().map(|x| x.set_as_disconnected());
+ self.rts.as_ref().map(|x| x.set_as_disconnected());
T::disable();
}
}
-impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
+impl<'d, T: BasicInstance> Uart<'d, T, Async> {
/// Create a new bidirectional UART
pub fn new(
peri: impl Peripheral + 'd,
rx: impl Peripheral
> + 'd,
tx: impl Peripheral
> + 'd,
_irq: impl interrupt::typelevel::Binding> + 'd,
- tx_dma: impl Peripheral + 'd,
- rx_dma: impl Peripheral
+ 'd,
+ tx_dma: impl Peripheral
> + 'd,
+ rx_dma: impl Peripheral
> + 'd,
config: Config,
) -> Result {
- // UartRx and UartTx have one refcount ea.
- T::enable_and_reset();
- T::enable_and_reset();
-
- Self::new_inner_configure(peri, rx, tx, tx_dma, rx_dma, config)
+ Self::new_inner(
+ peri,
+ new_pin!(rx, config.rx_af()),
+ new_pin!(tx, config.tx_af()),
+ None,
+ None,
+ None,
+ new_dma!(tx_dma),
+ new_dma!(rx_dma),
+ config,
+ )
}
/// Create a new bidirectional UART with request-to-send and clear-to-send pins
@@ -793,23 +882,21 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
_irq: impl interrupt::typelevel::Binding> + 'd,
rts: impl Peripheral> + 'd,
cts: impl Peripheral
> + 'd,
- tx_dma: impl Peripheral
+ 'd,
- rx_dma: impl Peripheral
+ 'd,
+ tx_dma: impl Peripheral
> + 'd,
+ rx_dma: impl Peripheral
> + 'd,
config: Config,
) -> Result {
- into_ref!(cts, rts);
-
- // UartRx and UartTx have one refcount ea.
- T::enable_and_reset();
- T::enable_and_reset();
-
- rts.set_as_af(rts.af_num(), AFType::OutputPushPull);
- cts.set_as_af(cts.af_num(), AFType::Input);
- T::regs().cr3().write(|w| {
- w.set_rtse(true);
- w.set_ctse(true);
- });
- Self::new_inner_configure(peri, rx, tx, tx_dma, rx_dma, config)
+ Self::new_inner(
+ peri,
+ new_pin!(rx, config.rx_af()),
+ new_pin!(tx, config.tx_af()),
+ new_pin!(rts, AFType::OutputPushPull),
+ new_pin!(cts, AFType::Input),
+ None,
+ new_dma!(tx_dma),
+ new_dma!(rx_dma),
+ config,
+ )
}
#[cfg(not(any(usart_v1, usart_v2)))]
@@ -820,21 +907,21 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
tx: impl Peripheral> + 'd,
_irq: impl interrupt::typelevel::Binding> + 'd,
de: impl Peripheral> + 'd,
- tx_dma: impl Peripheral
+ 'd,
- rx_dma: impl Peripheral
+ 'd,
+ tx_dma: impl Peripheral
> + 'd,
+ rx_dma: impl Peripheral
> + 'd,
config: Config,
) -> Result {
- into_ref!(de);
-
- // UartRx and UartTx have one refcount ea.
- T::enable_and_reset();
- T::enable_and_reset();
-
- de.set_as_af(de.af_num(), AFType::OutputPushPull);
- T::regs().cr3().write(|w| {
- w.set_dem(true);
- });
- Self::new_inner_configure(peri, rx, tx, tx_dma, rx_dma, config)
+ Self::new_inner(
+ peri,
+ new_pin!(rx, config.rx_af()),
+ new_pin!(tx, config.tx_af()),
+ None,
+ None,
+ new_pin!(de, AFType::OutputPushPull),
+ new_dma!(tx_dma),
+ new_dma!(rx_dma),
+ config,
+ )
}
/// Create a single-wire half-duplex Uart transceiver on a single Tx pin.
@@ -846,28 +933,32 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
/// I/O in idle or in reception.
/// Apart from this, the communication protocol is similar to normal USART mode. Any conflict
/// on the line must be managed by software (for instance by using a centralized arbiter).
- #[cfg(not(any(usart_v1, usart_v2)))]
#[doc(alias("HDSEL"))]
pub fn new_half_duplex(
peri: impl Peripheral + 'd,
tx: impl Peripheral
> + 'd,
_irq: impl interrupt::typelevel::Binding> + 'd,
- tx_dma: impl Peripheral + 'd,
- rx_dma: impl Peripheral
+ 'd,
+ tx_dma: impl Peripheral
> + 'd,
+ rx_dma: impl Peripheral
> + 'd,
mut config: Config,
) -> Result {
- // UartRx and UartTx have one refcount ea.
- T::enable_and_reset();
- T::enable_and_reset();
+ #[cfg(not(any(usart_v1, usart_v2)))]
+ {
+ config.swap_rx_tx = false;
+ }
+ config.half_duplex = true;
- config.swap_rx_tx = false;
-
- into_ref!(peri, tx, tx_dma, rx_dma);
-
- T::regs().cr3().write(|w| w.set_hdsel(true));
- tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
-
- Self::new_inner(peri, tx_dma, rx_dma, config)
+ Self::new_inner(
+ peri,
+ None,
+ new_pin!(tx, AFType::OutputPushPull),
+ None,
+ None,
+ None,
+ new_dma!(tx_dma),
+ new_dma!(rx_dma),
+ config,
+ )
}
/// Create a single-wire half-duplex Uart transceiver on a single Rx pin.
@@ -885,62 +976,198 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
peri: impl Peripheral + 'd,
rx: impl Peripheral
> + 'd,
_irq: impl interrupt::typelevel::Binding> + 'd,
- tx_dma: impl Peripheral + 'd,
- rx_dma: impl Peripheral
+ 'd,
+ tx_dma: impl Peripheral
> + 'd,
+ rx_dma: impl Peripheral
> + 'd,
mut config: Config,
) -> Result {
- // UartRx and UartTx have one refcount ea.
- T::enable_and_reset();
- T::enable_and_reset();
-
config.swap_rx_tx = true;
+ config.half_duplex = true;
- into_ref!(peri, rx, tx_dma, rx_dma);
-
- T::regs().cr3().write(|w| w.set_hdsel(true));
- rx.set_as_af(rx.af_num(), AFType::OutputPushPull);
-
- Self::new_inner(peri, tx_dma, rx_dma, config)
+ Self::new_inner(
+ peri,
+ None,
+ None,
+ new_pin!(rx, AFType::OutputPushPull),
+ None,
+ None,
+ new_dma!(tx_dma),
+ new_dma!(rx_dma),
+ config,
+ )
}
- fn new_inner_configure(
+ /// Perform an asynchronous write
+ pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
+ self.tx.write(buffer).await
+ }
+
+ /// Perform an asynchronous read into `buffer`
+ pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
+ self.rx.read(buffer).await
+ }
+
+ /// Perform an an asynchronous read with idle line detection enabled
+ pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result {
+ self.rx.read_until_idle(buffer).await
+ }
+}
+
+impl<'d, T: BasicInstance> Uart<'d, T, Blocking> {
+ /// Create a new blocking bidirectional UART.
+ pub fn new_blocking(
peri: impl Peripheral + 'd,
rx: impl Peripheral
> + 'd,
tx: impl Peripheral
> + 'd,
- tx_dma: impl Peripheral
+ 'd,
- rx_dma: impl Peripheral
+ 'd,
config: Config,
) -> Result {
- into_ref!(peri, rx, tx, tx_dma, rx_dma);
-
- // Some chips do not have swap_rx_tx bit
- cfg_if::cfg_if! {
- if #[cfg(any(usart_v3, usart_v4))] {
- if config.swap_rx_tx {
- let (rx, tx) = (tx, rx);
- rx.set_as_af(rx.af_num(), AFType::Input);
- tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
- } else {
- rx.set_as_af(rx.af_num(), AFType::Input);
- tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
- }
- } else {
- rx.set_as_af(rx.af_num(), AFType::Input);
- tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
- }
- }
-
- Self::new_inner(peri, tx_dma, rx_dma, config)
+ Self::new_inner(
+ peri,
+ new_pin!(rx, config.rx_af()),
+ new_pin!(tx, config.tx_af()),
+ None,
+ None,
+ None,
+ None,
+ None,
+ config,
+ )
}
- fn new_inner(
- peri: PeripheralRef<'d, T>,
- tx_dma: PeripheralRef<'d, TxDma>,
- rx_dma: PeripheralRef<'d, RxDma>,
+ /// Create a new bidirectional UART with request-to-send and clear-to-send pins
+ pub fn new_blocking_with_rtscts(
+ peri: impl Peripheral + 'd,
+ rx: impl Peripheral
> + 'd,
+ tx: impl Peripheral
> + 'd,
+ rts: impl Peripheral
> + 'd,
+ cts: impl Peripheral
> + 'd,
config: Config,
) -> Result {
+ Self::new_inner(
+ peri,
+ new_pin!(rx, config.rx_af()),
+ new_pin!(tx, config.tx_af()),
+ new_pin!(rts, AFType::OutputPushPull),
+ new_pin!(cts, AFType::Input),
+ None,
+ None,
+ None,
+ config,
+ )
+ }
+
+ #[cfg(not(any(usart_v1, usart_v2)))]
+ /// Create a new bidirectional UART with a driver-enable pin
+ pub fn new_blocking_with_de(
+ peri: impl Peripheral + 'd,
+ rx: impl Peripheral
> + 'd,
+ tx: impl Peripheral
> + 'd,
+ de: impl Peripheral
> + 'd,
+ config: Config,
+ ) -> Result {
+ Self::new_inner(
+ peri,
+ new_pin!(rx, config.rx_af()),
+ new_pin!(tx, config.tx_af()),
+ None,
+ None,
+ new_pin!(de, AFType::OutputPushPull),
+ None,
+ None,
+ config,
+ )
+ }
+
+ /// Create a single-wire half-duplex Uart transceiver on a single Tx pin.
+ ///
+ /// See [`new_half_duplex_on_rx`][`Self::new_half_duplex_on_rx`] if you would prefer to use an Rx pin.
+ /// There is no functional difference between these methods, as both allow bidirectional communication.
+ ///
+ /// The pin is always released when no data is transmitted. Thus, it acts as a standard
+ /// I/O in idle or in reception.
+ /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict
+ /// on the line must be managed by software (for instance by using a centralized arbiter).
+ #[doc(alias("HDSEL"))]
+ pub fn new_blocking_half_duplex(
+ peri: impl Peripheral + 'd,
+ tx: impl Peripheral
> + 'd,
+ mut config: Config,
+ ) -> Result {
+ #[cfg(not(any(usart_v1, usart_v2)))]
+ {
+ config.swap_rx_tx = false;
+ }
+ config.half_duplex = true;
+
+ Self::new_inner(
+ peri,
+ None,
+ new_pin!(tx, AFType::OutputPushPull),
+ None,
+ None,
+ None,
+ None,
+ None,
+ config,
+ )
+ }
+
+ /// Create a single-wire half-duplex Uart transceiver on a single Rx pin.
+ ///
+ /// See [`new_half_duplex`][`Self::new_half_duplex`] if you would prefer to use an Tx pin.
+ /// There is no functional difference between these methods, as both allow bidirectional communication.
+ ///
+ /// The pin is always released when no data is transmitted. Thus, it acts as a standard
+ /// I/O in idle or in reception.
+ /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict
+ /// on the line must be managed by software (for instance by using a centralized arbiter).
+ #[cfg(not(any(usart_v1, usart_v2)))]
+ #[doc(alias("HDSEL"))]
+ pub fn new_blocking_half_duplex_on_rx(
+ peri: impl Peripheral + 'd,
+ rx: impl Peripheral
> + 'd,
+ mut config: Config,
+ ) -> Result {
+ config.swap_rx_tx = true;
+ config.half_duplex = true;
+
+ Self::new_inner(
+ peri,
+ None,
+ None,
+ new_pin!(rx, AFType::OutputPushPull),
+ None,
+ None,
+ None,
+ None,
+ config,
+ )
+ }
+}
+
+impl<'d, T: BasicInstance, M: Mode> Uart<'d, T, M> {
+ fn new_inner(
+ _peri: impl Peripheral + 'd,
+ rx: Option>,
+ tx: Option>,
+ rts: Option>,
+ cts: Option>,
+ de: Option