Rebase on master
This commit is contained in:
@ -17,8 +17,9 @@ flavors = [
|
||||
{ regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" },
|
||||
{ regex_feature = "stm32f2.*", target = "thumbv7m-none-eabi" },
|
||||
{ regex_feature = "stm32f3.*", target = "thumbv7em-none-eabi" },
|
||||
{ regex_feature = "stm32f42.*", target = "thumbv7em-none-eabi" },
|
||||
{ regex_feature = "stm32f4.*", target = "thumbv7em-none-eabi" },
|
||||
{ regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" },
|
||||
{ regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" },
|
||||
{ regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" },
|
||||
{ regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi" },
|
||||
{ regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" },
|
||||
@ -39,12 +40,12 @@ embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]}
|
||||
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" }
|
||||
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||
embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true }
|
||||
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
||||
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true }
|
||||
|
||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true}
|
||||
embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true}
|
||||
embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true}
|
||||
embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true}
|
||||
|
||||
embedded-storage = "0.3.0"
|
||||
@ -57,7 +58,7 @@ cortex-m = "0.7.6"
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||
rand_core = "0.6.3"
|
||||
sdio-host = "0.5.0"
|
||||
embedded-sdmmc = { git = "https://github.com/thalesfragoso/embedded-sdmmc-rs", branch = "async", optional = true }
|
||||
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true }
|
||||
critical-section = "1.1"
|
||||
atomic-polyfill = "1.0.1"
|
||||
stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] }
|
||||
@ -67,7 +68,7 @@ nb = "1.0.0"
|
||||
stm32-fmc = "0.2.4"
|
||||
seq-macro = "0.3.0"
|
||||
cfg-if = "1.0.0"
|
||||
embedded-io = { version = "0.3.0", features = ["async"], optional = true }
|
||||
embedded-io = { version = "0.4.0", features = ["async"], optional = true }
|
||||
chrono = { version = "^0.4", default-features = false, optional = true}
|
||||
|
||||
[build-dependencies]
|
||||
@ -76,16 +77,17 @@ quote = "1.0.15"
|
||||
stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]}
|
||||
|
||||
[features]
|
||||
defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"]
|
||||
sdmmc-rs = ["embedded-sdmmc"]
|
||||
net = ["embassy-net" ]
|
||||
defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"]
|
||||
memory-x = ["stm32-metapac/memory-x"]
|
||||
subghz = []
|
||||
exti = []
|
||||
|
||||
# Enables additional driver features that depend on embassy-time
|
||||
time = ["dep:embassy-time"]
|
||||
|
||||
# Features starting with `_` are for internal use only. They're not intended
|
||||
# to be enabled by other crates, and are not covered by semver guarantees.
|
||||
_time-driver = ["dep:embassy-time"]
|
||||
_time-driver = ["time"]
|
||||
time-driver-any = ["_time-driver"]
|
||||
time-driver-tim2 = ["_time-driver"]
|
||||
time-driver-tim3 = ["_time-driver"]
|
||||
@ -109,6 +111,19 @@ unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"]
|
||||
|
||||
# BEGIN GENERATED FEATURES
|
||||
# Generated by stm32-gen-features. DO NOT EDIT.
|
||||
stm32c011d6 = [ "stm32-metapac/stm32c011d6" ]
|
||||
stm32c011f4 = [ "stm32-metapac/stm32c011f4" ]
|
||||
stm32c011f6 = [ "stm32-metapac/stm32c011f6" ]
|
||||
stm32c011j4 = [ "stm32-metapac/stm32c011j4" ]
|
||||
stm32c011j6 = [ "stm32-metapac/stm32c011j6" ]
|
||||
stm32c031c4 = [ "stm32-metapac/stm32c031c4" ]
|
||||
stm32c031c6 = [ "stm32-metapac/stm32c031c6" ]
|
||||
stm32c031f4 = [ "stm32-metapac/stm32c031f4" ]
|
||||
stm32c031f6 = [ "stm32-metapac/stm32c031f6" ]
|
||||
stm32c031g4 = [ "stm32-metapac/stm32c031g4" ]
|
||||
stm32c031g6 = [ "stm32-metapac/stm32c031g6" ]
|
||||
stm32c031k4 = [ "stm32-metapac/stm32c031k4" ]
|
||||
stm32c031k6 = [ "stm32-metapac/stm32c031k6" ]
|
||||
stm32f030c6 = [ "stm32-metapac/stm32f030c6" ]
|
||||
stm32f030c8 = [ "stm32-metapac/stm32f030c8" ]
|
||||
stm32f030cc = [ "stm32-metapac/stm32f030cc" ]
|
||||
@ -1317,11 +1332,9 @@ stm32u575zi = [ "stm32-metapac/stm32u575zi" ]
|
||||
stm32u585ai = [ "stm32-metapac/stm32u585ai" ]
|
||||
stm32u585ci = [ "stm32-metapac/stm32u585ci" ]
|
||||
stm32u585oi = [ "stm32-metapac/stm32u585oi" ]
|
||||
stm32u585qe = [ "stm32-metapac/stm32u585qe" ]
|
||||
stm32u585qi = [ "stm32-metapac/stm32u585qi" ]
|
||||
stm32u585ri = [ "stm32-metapac/stm32u585ri" ]
|
||||
stm32u585vi = [ "stm32-metapac/stm32u585vi" ]
|
||||
stm32u585ze = [ "stm32-metapac/stm32u585ze" ]
|
||||
stm32u585zi = [ "stm32-metapac/stm32u585zi" ]
|
||||
stm32wb10cc = [ "stm32-metapac/stm32wb10cc" ]
|
||||
stm32wb15cc = [ "stm32-metapac/stm32wb15cc" ]
|
||||
@ -1339,7 +1352,6 @@ stm32wb55vc = [ "stm32-metapac/stm32wb55vc" ]
|
||||
stm32wb55ve = [ "stm32-metapac/stm32wb55ve" ]
|
||||
stm32wb55vg = [ "stm32-metapac/stm32wb55vg" ]
|
||||
stm32wb55vy = [ "stm32-metapac/stm32wb55vy" ]
|
||||
stm32wb5mmg = [ "stm32-metapac/stm32wb5mmg" ]
|
||||
stm32wl54cc-cm4 = [ "stm32-metapac/stm32wl54cc-cm4" ]
|
||||
stm32wl54cc-cm0p = [ "stm32-metapac/stm32wl54cc-cm0p" ]
|
||||
stm32wl54jc-cm4 = [ "stm32-metapac/stm32wl54jc-cm4" ]
|
||||
@ -1348,8 +1360,6 @@ stm32wl55cc-cm4 = [ "stm32-metapac/stm32wl55cc-cm4" ]
|
||||
stm32wl55cc-cm0p = [ "stm32-metapac/stm32wl55cc-cm0p" ]
|
||||
stm32wl55jc-cm4 = [ "stm32-metapac/stm32wl55jc-cm4" ]
|
||||
stm32wl55jc-cm0p = [ "stm32-metapac/stm32wl55jc-cm0p" ]
|
||||
stm32wl55uc-cm4 = [ "stm32-metapac/stm32wl55uc-cm4" ]
|
||||
stm32wl55uc-cm0p = [ "stm32-metapac/stm32wl55uc-cm0p" ]
|
||||
stm32wle4c8 = [ "stm32-metapac/stm32wle4c8" ]
|
||||
stm32wle4cb = [ "stm32-metapac/stm32wle4cb" ]
|
||||
stm32wle4cc = [ "stm32-metapac/stm32wle4cc" ]
|
||||
@ -1362,6 +1372,4 @@ stm32wle5cc = [ "stm32-metapac/stm32wle5cc" ]
|
||||
stm32wle5j8 = [ "stm32-metapac/stm32wle5j8" ]
|
||||
stm32wle5jb = [ "stm32-metapac/stm32wle5jb" ]
|
||||
stm32wle5jc = [ "stm32-metapac/stm32wle5jc" ]
|
||||
stm32wle5u8 = [ "stm32-metapac/stm32wle5u8" ]
|
||||
stm32wle5ub = [ "stm32-metapac/stm32wle5ub" ]
|
||||
# END GENERATED FEATURES
|
||||
|
@ -244,11 +244,13 @@ fn main() {
|
||||
(("usart", "CTS"), quote!(crate::usart::CtsPin)),
|
||||
(("usart", "RTS"), quote!(crate::usart::RtsPin)),
|
||||
(("usart", "CK"), quote!(crate::usart::CkPin)),
|
||||
(("usart", "DE"), quote!(crate::usart::DePin)),
|
||||
(("lpuart", "TX"), quote!(crate::usart::TxPin)),
|
||||
(("lpuart", "RX"), quote!(crate::usart::RxPin)),
|
||||
(("lpuart", "CTS"), quote!(crate::usart::CtsPin)),
|
||||
(("lpuart", "RTS"), quote!(crate::usart::RtsPin)),
|
||||
(("lpuart", "CK"), quote!(crate::usart::CkPin)),
|
||||
(("lpuart", "DE"), quote!(crate::usart::DePin)),
|
||||
(("spi", "SCK"), quote!(crate::spi::SckPin)),
|
||||
(("spi", "MOSI"), quote!(crate::spi::MosiPin)),
|
||||
(("spi", "MISO"), quote!(crate::spi::MisoPin)),
|
||||
@ -275,22 +277,20 @@ fn main() {
|
||||
(("dcmi", "PIXCLK"), quote!(crate::dcmi::PixClkPin)),
|
||||
(("usb", "DP"), quote!(crate::usb::DpPin)),
|
||||
(("usb", "DM"), quote!(crate::usb::DmPin)),
|
||||
(("otgfs", "DP"), quote!(crate::usb_otg::DpPin)),
|
||||
(("otgfs", "DM"), quote!(crate::usb_otg::DmPin)),
|
||||
(("otghs", "DP"), quote!(crate::usb_otg::DpPin)),
|
||||
(("otghs", "DM"), quote!(crate::usb_otg::DmPin)),
|
||||
(("otghs", "ULPI_CK"), quote!(crate::usb_otg::UlpiClkPin)),
|
||||
(("otghs", "ULPI_DIR"), quote!(crate::usb_otg::UlpiDirPin)),
|
||||
(("otghs", "ULPI_NXT"), quote!(crate::usb_otg::UlpiNxtPin)),
|
||||
(("otghs", "ULPI_STP"), quote!(crate::usb_otg::UlpiStpPin)),
|
||||
(("otghs", "ULPI_D0"), quote!(crate::usb_otg::UlpiD0Pin)),
|
||||
(("otghs", "ULPI_D1"), quote!(crate::usb_otg::UlpiD1Pin)),
|
||||
(("otghs", "ULPI_D2"), quote!(crate::usb_otg::UlpiD2Pin)),
|
||||
(("otghs", "ULPI_D3"), quote!(crate::usb_otg::UlpiD3Pin)),
|
||||
(("otghs", "ULPI_D4"), quote!(crate::usb_otg::UlpiD4Pin)),
|
||||
(("otghs", "ULPI_D5"), quote!(crate::usb_otg::UlpiD5Pin)),
|
||||
(("otghs", "ULPI_D6"), quote!(crate::usb_otg::UlpiD6Pin)),
|
||||
(("otghs", "ULPI_D7"), quote!(crate::usb_otg::UlpiD7Pin)),
|
||||
(("otg", "DP"), quote!(crate::usb_otg::DpPin)),
|
||||
(("otg", "DM"), quote!(crate::usb_otg::DmPin)),
|
||||
(("otg", "ULPI_CK"), quote!(crate::usb_otg::UlpiClkPin)),
|
||||
(("otg", "ULPI_DIR"), quote!(crate::usb_otg::UlpiDirPin)),
|
||||
(("otg", "ULPI_NXT"), quote!(crate::usb_otg::UlpiNxtPin)),
|
||||
(("otg", "ULPI_STP"), quote!(crate::usb_otg::UlpiStpPin)),
|
||||
(("otg", "ULPI_D0"), quote!(crate::usb_otg::UlpiD0Pin)),
|
||||
(("otg", "ULPI_D1"), quote!(crate::usb_otg::UlpiD1Pin)),
|
||||
(("otg", "ULPI_D2"), quote!(crate::usb_otg::UlpiD2Pin)),
|
||||
(("otg", "ULPI_D3"), quote!(crate::usb_otg::UlpiD3Pin)),
|
||||
(("otg", "ULPI_D4"), quote!(crate::usb_otg::UlpiD4Pin)),
|
||||
(("otg", "ULPI_D5"), quote!(crate::usb_otg::UlpiD5Pin)),
|
||||
(("otg", "ULPI_D6"), quote!(crate::usb_otg::UlpiD6Pin)),
|
||||
(("otg", "ULPI_D7"), quote!(crate::usb_otg::UlpiD7Pin)),
|
||||
(("can", "TX"), quote!(crate::can::TxPin)),
|
||||
(("can", "RX"), quote!(crate::can::RxPin)),
|
||||
(("eth", "REF_CLK"), quote!(crate::eth::RefClkPin)),
|
||||
|
@ -1,9 +1,7 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_common::into_ref;
|
||||
use embedded_hal_02::blocking::delay::DelayUs;
|
||||
|
||||
use crate::adc::{AdcPin, Instance};
|
||||
use crate::adc::{Adc, AdcPin, Instance, SampleTime};
|
||||
use crate::rcc::get_freqs;
|
||||
use crate::time::Hertz;
|
||||
use crate::Peripheral;
|
||||
@ -29,70 +27,9 @@ impl<T: Instance> super::sealed::AdcPin<T> for Temperature {
|
||||
}
|
||||
}
|
||||
|
||||
mod sample_time {
|
||||
/// ADC sample time
|
||||
///
|
||||
/// The default setting is 1.5 ADC clock cycles.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum SampleTime {
|
||||
/// 1.5 ADC clock cycles
|
||||
Cycles1_5 = 0b000,
|
||||
|
||||
/// 7.5 ADC clock cycles
|
||||
Cycles7_5 = 0b001,
|
||||
|
||||
/// 13.5 ADC clock cycles
|
||||
Cycles13_5 = 0b010,
|
||||
|
||||
/// 28.5 ADC clock cycles
|
||||
Cycles28_5 = 0b011,
|
||||
|
||||
/// 41.5 ADC clock cycles
|
||||
Cycles41_5 = 0b100,
|
||||
|
||||
/// 55.5 ADC clock cycles
|
||||
Cycles55_5 = 0b101,
|
||||
|
||||
/// 71.5 ADC clock cycles
|
||||
Cycles71_5 = 0b110,
|
||||
|
||||
/// 239.5 ADC clock cycles
|
||||
Cycles239_5 = 0b111,
|
||||
}
|
||||
|
||||
impl SampleTime {
|
||||
pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::SampleTime {
|
||||
match self {
|
||||
SampleTime::Cycles1_5 => crate::pac::adc::vals::SampleTime::CYCLES1_5,
|
||||
SampleTime::Cycles7_5 => crate::pac::adc::vals::SampleTime::CYCLES7_5,
|
||||
SampleTime::Cycles13_5 => crate::pac::adc::vals::SampleTime::CYCLES13_5,
|
||||
SampleTime::Cycles28_5 => crate::pac::adc::vals::SampleTime::CYCLES28_5,
|
||||
SampleTime::Cycles41_5 => crate::pac::adc::vals::SampleTime::CYCLES41_5,
|
||||
SampleTime::Cycles55_5 => crate::pac::adc::vals::SampleTime::CYCLES55_5,
|
||||
SampleTime::Cycles71_5 => crate::pac::adc::vals::SampleTime::CYCLES71_5,
|
||||
SampleTime::Cycles239_5 => crate::pac::adc::vals::SampleTime::CYCLES239_5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SampleTime {
|
||||
fn default() -> Self {
|
||||
Self::Cycles28_5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use sample_time::SampleTime;
|
||||
|
||||
pub struct Adc<'d, T: Instance> {
|
||||
sample_time: SampleTime,
|
||||
calibrated_vdda: u32,
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Adc<'d, T> {
|
||||
pub fn new(_peri: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
|
||||
into_ref!(_peri);
|
||||
pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
|
||||
into_ref!(adc);
|
||||
T::enable();
|
||||
T::reset();
|
||||
unsafe {
|
||||
@ -121,9 +58,8 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
delay.delay_us((1_000_000) / Self::freq().0 + 1);
|
||||
|
||||
Self {
|
||||
adc,
|
||||
sample_time: Default::default(),
|
||||
calibrated_vdda: VDDA_CALIB_MV,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,29 +98,10 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
Temperature {}
|
||||
}
|
||||
|
||||
/// Calculates the system VDDA by sampling the internal VREF channel and comparing
|
||||
/// to the expected value. If the chip's VDDA is not stable, run this before each ADC
|
||||
/// conversion.
|
||||
pub fn calibrate(&mut self, vref: &mut Vref) -> u32 {
|
||||
let old_sample_time = self.sample_time;
|
||||
self.sample_time = SampleTime::Cycles239_5;
|
||||
|
||||
let vref_samp = self.read(vref);
|
||||
self.sample_time = old_sample_time;
|
||||
|
||||
self.calibrated_vdda = (ADC_MAX * VREF_INT) / u32::from(vref_samp);
|
||||
self.calibrated_vdda
|
||||
}
|
||||
|
||||
pub fn set_sample_time(&mut self, sample_time: SampleTime) {
|
||||
self.sample_time = sample_time;
|
||||
}
|
||||
|
||||
/// Convert a measurement to millivolts
|
||||
pub fn to_millivolts(&self, sample: u16) -> u16 {
|
||||
((u32::from(sample) * self.calibrated_vdda) / ADC_MAX) as u16
|
||||
}
|
||||
|
||||
/// Perform a single conversion.
|
||||
fn convert(&mut self) -> u16 {
|
||||
unsafe {
|
||||
@ -222,14 +139,11 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
}
|
||||
|
||||
unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
||||
let sample_time = sample_time.into();
|
||||
if ch <= 9 {
|
||||
T::regs()
|
||||
.smpr2()
|
||||
.modify(|reg| reg.set_smp(ch as _, sample_time.sample_time()));
|
||||
T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time));
|
||||
} else {
|
||||
T::regs()
|
||||
.smpr1()
|
||||
.modify(|reg| reg.set_smp((ch - 10) as _, sample_time.sample_time()));
|
||||
T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,38 @@
|
||||
#![macro_use]
|
||||
|
||||
#[cfg_attr(adc_v4, path = "v4.rs")]
|
||||
#[cfg_attr(adc_v3, path = "v3.rs")]
|
||||
#[cfg_attr(adc_v2, path = "v2.rs")]
|
||||
#[cfg_attr(adc_g0, path = "v3.rs")]
|
||||
#[cfg_attr(adc_f1, path = "f1.rs")]
|
||||
#[cfg_attr(adc_v1, path = "v1.rs")]
|
||||
#[cfg_attr(adc_v2, path = "v2.rs")]
|
||||
#[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")]
|
||||
#[cfg_attr(adc_v4, path = "v4.rs")]
|
||||
mod _version;
|
||||
|
||||
#[cfg(not(any(adc_f1, adc_v1)))]
|
||||
mod resolution;
|
||||
#[cfg(not(adc_v1))]
|
||||
mod sample_time;
|
||||
|
||||
#[allow(unused)]
|
||||
pub use _version::*;
|
||||
#[cfg(not(any(adc_f1, adc_v1)))]
|
||||
pub use resolution::Resolution;
|
||||
#[cfg(not(adc_v1))]
|
||||
pub use sample_time::SampleTime;
|
||||
|
||||
use crate::peripherals;
|
||||
|
||||
#[cfg(not(adc_v1))]
|
||||
pub struct Adc<'d, T: Instance> {
|
||||
#[allow(unused)]
|
||||
adc: crate::PeripheralRef<'d, T>,
|
||||
sample_time: SampleTime,
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait Instance {
|
||||
fn regs() -> &'static crate::pac::adc::Adc;
|
||||
fn regs() -> crate::pac::adc::Adc;
|
||||
#[cfg(all(not(adc_f1), not(adc_v1)))]
|
||||
fn common_regs() -> &'static crate::pac::adccommon::AdcCommon;
|
||||
}
|
||||
|
||||
#[cfg(all(not(adc_f1), not(adc_v1)))]
|
||||
pub trait Common {
|
||||
fn regs() -> &'static crate::pac::adccommon::AdcCommon;
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon;
|
||||
}
|
||||
|
||||
pub trait AdcPin<T: Instance> {
|
||||
@ -34,12 +44,11 @@ pub(crate) mod sealed {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(adc_f1, adc_v2)))]
|
||||
pub trait Instance: sealed::Instance + 'static {}
|
||||
#[cfg(any(adc_f1, adc_v2))]
|
||||
pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {}
|
||||
#[cfg(all(not(adc_f1), not(adc_v1)))]
|
||||
pub trait Common: sealed::Common + 'static {}
|
||||
#[cfg(not(any(adc_f1, adc_v2, adc_v4)))]
|
||||
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {}
|
||||
#[cfg(any(adc_f1, adc_v2, adc_v4))]
|
||||
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {}
|
||||
|
||||
pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {}
|
||||
pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
|
||||
|
||||
@ -47,14 +56,14 @@ pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
|
||||
foreach_peripheral!(
|
||||
(adc, $inst:ident) => {
|
||||
impl crate::adc::sealed::Instance for peripherals::$inst {
|
||||
fn regs() -> &'static crate::pac::adc::Adc {
|
||||
&crate::pac::$inst
|
||||
fn regs() -> crate::pac::adc::Adc {
|
||||
crate::pac::$inst
|
||||
}
|
||||
#[cfg(all(not(adc_f1), not(adc_v1)))]
|
||||
fn common_regs() -> &'static crate::pac::adccommon::AdcCommon {
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon {
|
||||
foreach_peripheral!{
|
||||
(adccommon, $common_inst:ident) => {
|
||||
return &crate::pac::$common_inst
|
||||
return crate::pac::$common_inst
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -68,14 +77,14 @@ foreach_peripheral!(
|
||||
foreach_peripheral!(
|
||||
(adc, ADC3) => {
|
||||
impl crate::adc::sealed::Instance for peripherals::ADC3 {
|
||||
fn regs() -> &'static crate::pac::adc::Adc {
|
||||
&crate::pac::ADC3
|
||||
fn regs() -> crate::pac::adc::Adc {
|
||||
crate::pac::ADC3
|
||||
}
|
||||
#[cfg(all(not(adc_f1), not(adc_v1)))]
|
||||
fn common_regs() -> &'static crate::pac::adccommon::AdcCommon {
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon {
|
||||
foreach_peripheral!{
|
||||
(adccommon, ADC3_COMMON) => {
|
||||
return &crate::pac::ADC3_COMMON
|
||||
return crate::pac::ADC3_COMMON
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -85,14 +94,14 @@ foreach_peripheral!(
|
||||
};
|
||||
(adc, $inst:ident) => {
|
||||
impl crate::adc::sealed::Instance for peripherals::$inst {
|
||||
fn regs() -> &'static crate::pac::adc::Adc {
|
||||
&crate::pac::$inst
|
||||
fn regs() -> crate::pac::adc::Adc {
|
||||
crate::pac::$inst
|
||||
}
|
||||
#[cfg(all(not(adc_f1), not(adc_v1)))]
|
||||
fn common_regs() -> &'static crate::pac::adccommon::AdcCommon {
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon {
|
||||
foreach_peripheral!{
|
||||
(adccommon, ADC_COMMON) => {
|
||||
return &crate::pac::ADC_COMMON
|
||||
return crate::pac::ADC_COMMON
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -102,19 +111,6 @@ foreach_peripheral!(
|
||||
};
|
||||
);
|
||||
|
||||
#[cfg(all(not(adc_f1), not(adc_v1)))]
|
||||
foreach_peripheral!(
|
||||
(adccommon, $inst:ident) => {
|
||||
impl sealed::Common for peripherals::$inst {
|
||||
fn regs() -> &'static crate::pac::adccommon::AdcCommon {
|
||||
&crate::pac::$inst
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::adc::Common for peripherals::$inst {}
|
||||
};
|
||||
);
|
||||
|
||||
macro_rules! impl_adc_pin {
|
||||
($inst:ident, $pin:ident, $ch:expr) => {
|
||||
impl crate::adc::AdcPin<peripherals::$inst> for crate::peripherals::$pin {}
|
||||
|
63
embassy-stm32/src/adc/resolution.rs
Normal file
63
embassy-stm32/src/adc/resolution.rs
Normal file
@ -0,0 +1,63 @@
|
||||
#[cfg(any(adc_v2, adc_v3, adc_g0))]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Resolution {
|
||||
TwelveBit,
|
||||
TenBit,
|
||||
EightBit,
|
||||
SixBit,
|
||||
}
|
||||
|
||||
#[cfg(adc_v4)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Resolution {
|
||||
SixteenBit,
|
||||
FourteenBit,
|
||||
TwelveBit,
|
||||
TenBit,
|
||||
EightBit,
|
||||
}
|
||||
|
||||
impl Default for Resolution {
|
||||
fn default() -> Self {
|
||||
#[cfg(any(adc_v2, adc_v3, adc_g0))]
|
||||
{
|
||||
Self::TwelveBit
|
||||
}
|
||||
#[cfg(adc_v4)]
|
||||
{
|
||||
Self::SixteenBit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Resolution> for crate::pac::adc::vals::Res {
|
||||
fn from(res: Resolution) -> crate::pac::adc::vals::Res {
|
||||
match res {
|
||||
#[cfg(adc_v4)]
|
||||
Resolution::SixteenBit => crate::pac::adc::vals::Res::SIXTEENBIT,
|
||||
#[cfg(adc_v4)]
|
||||
Resolution::FourteenBit => crate::pac::adc::vals::Res::FOURTEENBITV,
|
||||
Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT,
|
||||
Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT,
|
||||
Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT,
|
||||
#[cfg(any(adc_v2, adc_v3, adc_g0))]
|
||||
Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolution {
|
||||
pub fn to_max_count(&self) -> u32 {
|
||||
match self {
|
||||
#[cfg(adc_v4)]
|
||||
Resolution::SixteenBit => (1 << 16) - 1,
|
||||
#[cfg(adc_v4)]
|
||||
Resolution::FourteenBit => (1 << 14) - 1,
|
||||
Resolution::TwelveBit => (1 << 12) - 1,
|
||||
Resolution::TenBit => (1 << 10) - 1,
|
||||
Resolution::EightBit => (1 << 8) - 1,
|
||||
#[cfg(any(adc_v2, adc_v3, adc_g0))]
|
||||
Resolution::SixBit => (1 << 6) - 1,
|
||||
}
|
||||
}
|
||||
}
|
111
embassy-stm32/src/adc/sample_time.rs
Normal file
111
embassy-stm32/src/adc/sample_time.rs
Normal file
@ -0,0 +1,111 @@
|
||||
macro_rules! impl_sample_time {
|
||||
($default_doc:expr, $default:ident, $pac:ty, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => {
|
||||
#[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum SampleTime {
|
||||
$(
|
||||
#[doc = concat!($doc, " ADC clock cycles.")]
|
||||
$variant,
|
||||
)*
|
||||
}
|
||||
|
||||
impl From<SampleTime> for $pac {
|
||||
fn from(sample_time: SampleTime) -> $pac {
|
||||
match sample_time {
|
||||
$(SampleTime::$variant => <$pac>::$pac_variant),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SampleTime {
|
||||
fn default() -> Self {
|
||||
Self::$default
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(adc_f1)]
|
||||
impl_sample_time!(
|
||||
"1.5",
|
||||
Cycles1_5,
|
||||
crate::pac::adc::vals::SampleTime,
|
||||
(
|
||||
("1.5", Cycles1_5, CYCLES1_5),
|
||||
("7.5", Cycles7_5, CYCLES7_5),
|
||||
("13.5", Cycles13_5, CYCLES13_5),
|
||||
("28.5", Cycles28_5, CYCLES28_5),
|
||||
("41.5", Cycles41_5, CYCLES41_5),
|
||||
("55.5", Cycles55_5, CYCLES55_5),
|
||||
("71.5", Cycles71_5, CYCLES71_5),
|
||||
("239.5", Cycles239_5, CYCLES239_5)
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(adc_v2)]
|
||||
impl_sample_time!(
|
||||
"3",
|
||||
Cycles3,
|
||||
crate::pac::adc::vals::Smp,
|
||||
(
|
||||
("3", Cycles3, CYCLES3),
|
||||
("15", Cycles15, CYCLES15),
|
||||
("28", Cycles28, CYCLES28),
|
||||
("56", Cycles56, CYCLES56),
|
||||
("84", Cycles84, CYCLES84),
|
||||
("112", Cycles112, CYCLES112),
|
||||
("144", Cycles144, CYCLES144),
|
||||
("480", Cycles480, CYCLES480)
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(adc_v3)]
|
||||
impl_sample_time!(
|
||||
"2.5",
|
||||
Cycles2_5,
|
||||
crate::pac::adc::vals::SampleTime,
|
||||
(
|
||||
("2.5", Cycles2_5, CYCLES2_5),
|
||||
("6.5", Cycles6_5, CYCLES6_5),
|
||||
("12.5", Cycles12_5, CYCLES12_5),
|
||||
("24.5", Cycles24_5, CYCLES24_5),
|
||||
("47.5", Cycles47_5, CYCLES47_5),
|
||||
("92.5", Cycles92_5, CYCLES92_5),
|
||||
("247.5", Cycles247_5, CYCLES247_5),
|
||||
("640.5", Cycles640_5, CYCLES640_5)
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(adc_g0)]
|
||||
impl_sample_time!(
|
||||
"1.5",
|
||||
Cycles1_5,
|
||||
crate::pac::adc::vals::SampleTime,
|
||||
(
|
||||
("1.5", Cycles1_5, CYCLES1_5),
|
||||
("3.5", Cycles3_5, CYCLES3_5),
|
||||
("7.5", Cycles7_5, CYCLES7_5),
|
||||
("12.5", Cycles12_5, CYCLES12_5),
|
||||
("19.5", Cycles19_5, CYCLES19_5),
|
||||
("39.5", Cycles39_5, CYCLES39_5),
|
||||
("79.5", Cycles79_5, CYCLES79_5),
|
||||
("160.5", Cycles160_5, CYCLES160_5)
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(adc_v4)]
|
||||
impl_sample_time!(
|
||||
"1.5",
|
||||
Cycles1_5,
|
||||
crate::pac::adc::vals::Smp,
|
||||
(
|
||||
("1.5", Cycles1_5, CYCLES1_5),
|
||||
("2.5", Cycles2_5, CYCLES2_5),
|
||||
("8.5", Cycles8_5, CYCLES8_5),
|
||||
("16.5", Cycles16_5, CYCLES16_5),
|
||||
("32.5", Cycles32_5, CYCLES32_5),
|
||||
("64.5", Cycles64_5, CYCLES64_5),
|
||||
("387.5", Cycles387_5, CYCLES387_5),
|
||||
("810.5", Cycles810_5, CYCLES810_5)
|
||||
)
|
||||
);
|
@ -1,10 +1,8 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_common::into_ref;
|
||||
use embedded_hal_02::blocking::delay::DelayUs;
|
||||
|
||||
use super::InternalChannel;
|
||||
use crate::adc::{AdcPin, Instance};
|
||||
use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime};
|
||||
use crate::peripherals::ADC1;
|
||||
use crate::time::Hertz;
|
||||
use crate::Peripheral;
|
||||
@ -17,39 +15,6 @@ pub const VREF_CALIB_MV: u32 = 3300;
|
||||
/// ADC turn-on time
|
||||
pub const ADC_POWERUP_TIME_US: u32 = 3;
|
||||
|
||||
pub enum Resolution {
|
||||
TwelveBit,
|
||||
TenBit,
|
||||
EightBit,
|
||||
SixBit,
|
||||
}
|
||||
|
||||
impl Default for Resolution {
|
||||
fn default() -> Self {
|
||||
Self::TwelveBit
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolution {
|
||||
fn res(&self) -> crate::pac::adc::vals::Res {
|
||||
match self {
|
||||
Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT,
|
||||
Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT,
|
||||
Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT,
|
||||
Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_max_count(&self) -> u32 {
|
||||
match self {
|
||||
Resolution::TwelveBit => (1 << 12) - 1,
|
||||
Resolution::TenBit => (1 << 10) - 1,
|
||||
Resolution::EightBit => (1 << 8) - 1,
|
||||
Resolution::SixBit => (1 << 6) - 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VrefInt;
|
||||
impl InternalChannel<ADC1> for VrefInt {}
|
||||
impl super::sealed::InternalChannel<ADC1> for VrefInt {
|
||||
@ -80,15 +45,6 @@ impl super::sealed::InternalChannel<ADC1> for Temperature {
|
||||
}
|
||||
|
||||
impl Temperature {
|
||||
/// Converts temperature sensor reading in millivolts to degrees celcius
|
||||
pub fn to_celcius(sample_mv: u16) -> f32 {
|
||||
// From 6.3.22 Temperature sensor characteristics
|
||||
const V25: i32 = 760; // mV
|
||||
const AVG_SLOPE: f32 = 2.5; // mV/C
|
||||
|
||||
(sample_mv as i32 - V25) as f32 / AVG_SLOPE + 25.0
|
||||
}
|
||||
|
||||
/// Time needed for temperature sensor readings to stabilize
|
||||
pub fn start_time_us() -> u32 {
|
||||
10
|
||||
@ -103,42 +59,6 @@ impl super::sealed::InternalChannel<ADC1> for Vbat {
|
||||
}
|
||||
}
|
||||
|
||||
/// ADC sample time
|
||||
///
|
||||
/// The default setting is 3 ADC clock cycles.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum SampleTime {
|
||||
Cycles3 = 0b000,
|
||||
Cycles15 = 0b001,
|
||||
Cycles28 = 0b010,
|
||||
Cycles56 = 0b011,
|
||||
Cycles85 = 0b100,
|
||||
Cycles112 = 0b101,
|
||||
Cycles144 = 0b110,
|
||||
Cycles480 = 0b111,
|
||||
}
|
||||
|
||||
impl SampleTime {
|
||||
pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::Smp {
|
||||
match self {
|
||||
SampleTime::Cycles3 => crate::pac::adc::vals::Smp::CYCLES3,
|
||||
SampleTime::Cycles15 => crate::pac::adc::vals::Smp::CYCLES15,
|
||||
SampleTime::Cycles28 => crate::pac::adc::vals::Smp::CYCLES28,
|
||||
SampleTime::Cycles56 => crate::pac::adc::vals::Smp::CYCLES56,
|
||||
SampleTime::Cycles85 => crate::pac::adc::vals::Smp::CYCLES84,
|
||||
SampleTime::Cycles112 => crate::pac::adc::vals::Smp::CYCLES112,
|
||||
SampleTime::Cycles144 => crate::pac::adc::vals::Smp::CYCLES144,
|
||||
SampleTime::Cycles480 => crate::pac::adc::vals::Smp::CYCLES480,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SampleTime {
|
||||
fn default() -> Self {
|
||||
Self::Cycles3
|
||||
}
|
||||
}
|
||||
|
||||
enum Prescaler {
|
||||
Div2,
|
||||
Div4,
|
||||
@ -170,19 +90,12 @@ impl Prescaler {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Adc<'d, T: Instance> {
|
||||
sample_time: SampleTime,
|
||||
vref_mv: u32,
|
||||
resolution: Resolution,
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
|
||||
impl<'d, T> Adc<'d, T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
pub fn new(_peri: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
|
||||
into_ref!(_peri);
|
||||
pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
|
||||
into_ref!(adc);
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
@ -198,10 +111,8 @@ where
|
||||
delay.delay_us(ADC_POWERUP_TIME_US);
|
||||
|
||||
Self {
|
||||
adc,
|
||||
sample_time: Default::default(),
|
||||
resolution: Resolution::default(),
|
||||
vref_mv: VREF_DEFAULT_MV,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,19 +121,9 @@ where
|
||||
}
|
||||
|
||||
pub fn set_resolution(&mut self, resolution: Resolution) {
|
||||
self.resolution = resolution;
|
||||
}
|
||||
|
||||
/// Set VREF value in millivolts. This value is used for [to_millivolts()] sample conversion.
|
||||
///
|
||||
/// Use this if you have a known precise VREF (VDDA) pin reference voltage.
|
||||
pub fn set_vref_mv(&mut self, vref_mv: u32) {
|
||||
self.vref_mv = vref_mv;
|
||||
}
|
||||
|
||||
/// Convert a measurement to millivolts
|
||||
pub fn to_millivolts(&self, sample: u16) -> u16 {
|
||||
((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16
|
||||
unsafe {
|
||||
T::regs().cr1().modify(|reg| reg.set_res(resolution.into()));
|
||||
}
|
||||
}
|
||||
|
||||
/// Enables internal voltage reference and returns [VrefInt], which can be used in
|
||||
@ -306,7 +207,6 @@ where
|
||||
|
||||
unsafe fn read_channel(&mut self, channel: u8) -> u16 {
|
||||
// Configure ADC
|
||||
T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res()));
|
||||
|
||||
// Select channel
|
||||
T::regs().sqr3().write(|reg| reg.set_sq(0, channel));
|
||||
@ -320,14 +220,11 @@ where
|
||||
}
|
||||
|
||||
unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
||||
let sample_time = sample_time.into();
|
||||
if ch <= 9 {
|
||||
T::regs()
|
||||
.smpr2()
|
||||
.modify(|reg| reg.set_smp(ch as _, sample_time.sample_time()));
|
||||
T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time));
|
||||
} else {
|
||||
T::regs()
|
||||
.smpr1()
|
||||
.modify(|reg| reg.set_smp((ch - 10) as _, sample_time.sample_time()));
|
||||
T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_common::into_ref;
|
||||
use embedded_hal_02::blocking::delay::DelayUs;
|
||||
|
||||
use crate::adc::{AdcPin, Instance};
|
||||
use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime};
|
||||
use crate::Peripheral;
|
||||
|
||||
/// Default VREF voltage used for sample conversion to millivolts.
|
||||
@ -24,39 +22,6 @@ fn enable() {
|
||||
});
|
||||
}
|
||||
|
||||
pub enum Resolution {
|
||||
TwelveBit,
|
||||
TenBit,
|
||||
EightBit,
|
||||
SixBit,
|
||||
}
|
||||
|
||||
impl Default for Resolution {
|
||||
fn default() -> Self {
|
||||
Self::TwelveBit
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolution {
|
||||
fn res(&self) -> crate::pac::adc::vals::Res {
|
||||
match self {
|
||||
Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT,
|
||||
Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT,
|
||||
Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT,
|
||||
Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_max_count(&self) -> u32 {
|
||||
match self {
|
||||
Resolution::TwelveBit => (1 << 12) - 1,
|
||||
Resolution::TenBit => (1 << 10) - 1,
|
||||
Resolution::EightBit => (1 << 8) - 1,
|
||||
Resolution::SixBit => (1 << 6) - 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VrefInt;
|
||||
impl<T: Instance> AdcPin<T> for VrefInt {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for VrefInt {
|
||||
@ -93,126 +58,9 @@ impl<T: Instance> super::sealed::AdcPin<T> for Vbat {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(adc_g0))]
|
||||
mod sample_time {
|
||||
/// ADC sample time
|
||||
///
|
||||
/// The default setting is 2.5 ADC clock cycles.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum SampleTime {
|
||||
/// 2.5 ADC clock cycles
|
||||
Cycles2_5 = 0b000,
|
||||
|
||||
/// 6.5 ADC clock cycles
|
||||
Cycles6_5 = 0b001,
|
||||
|
||||
/// 12.5 ADC clock cycles
|
||||
Cycles12_5 = 0b010,
|
||||
|
||||
/// 24.5 ADC clock cycles
|
||||
Cycles24_5 = 0b011,
|
||||
|
||||
/// 47.5 ADC clock cycles
|
||||
Cycles47_5 = 0b100,
|
||||
|
||||
/// 92.5 ADC clock cycles
|
||||
Cycles92_5 = 0b101,
|
||||
|
||||
/// 247.5 ADC clock cycles
|
||||
Cycles247_5 = 0b110,
|
||||
|
||||
/// 640.5 ADC clock cycles
|
||||
Cycles640_5 = 0b111,
|
||||
}
|
||||
|
||||
impl SampleTime {
|
||||
pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::SampleTime {
|
||||
match self {
|
||||
SampleTime::Cycles2_5 => crate::pac::adc::vals::SampleTime::CYCLES2_5,
|
||||
SampleTime::Cycles6_5 => crate::pac::adc::vals::SampleTime::CYCLES6_5,
|
||||
SampleTime::Cycles12_5 => crate::pac::adc::vals::SampleTime::CYCLES12_5,
|
||||
SampleTime::Cycles24_5 => crate::pac::adc::vals::SampleTime::CYCLES24_5,
|
||||
SampleTime::Cycles47_5 => crate::pac::adc::vals::SampleTime::CYCLES47_5,
|
||||
SampleTime::Cycles92_5 => crate::pac::adc::vals::SampleTime::CYCLES92_5,
|
||||
SampleTime::Cycles247_5 => crate::pac::adc::vals::SampleTime::CYCLES247_5,
|
||||
SampleTime::Cycles640_5 => crate::pac::adc::vals::SampleTime::CYCLES640_5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SampleTime {
|
||||
fn default() -> Self {
|
||||
Self::Cycles2_5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(adc_g0)]
|
||||
mod sample_time {
|
||||
/// ADC sample time
|
||||
///
|
||||
/// The default setting is 1.5 ADC clock cycles.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum SampleTime {
|
||||
/// 1.5 ADC clock cycles
|
||||
Cycles1_5 = 0b000,
|
||||
|
||||
/// 3.5 ADC clock cycles
|
||||
Cycles3_5 = 0b001,
|
||||
|
||||
/// 7.5 ADC clock cycles
|
||||
Cycles7_5 = 0b010,
|
||||
|
||||
/// 12.5 ADC clock cycles
|
||||
Cycles12_5 = 0b011,
|
||||
|
||||
/// 19.5 ADC clock cycles
|
||||
Cycles19_5 = 0b100,
|
||||
|
||||
/// 39.5 ADC clock cycles
|
||||
Cycles39_5 = 0b101,
|
||||
|
||||
/// 79.5 ADC clock cycles
|
||||
Cycles79_5 = 0b110,
|
||||
|
||||
/// 160.5 ADC clock cycles
|
||||
Cycles160_5 = 0b111,
|
||||
}
|
||||
|
||||
impl SampleTime {
|
||||
pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::SampleTime {
|
||||
match self {
|
||||
SampleTime::Cycles1_5 => crate::pac::adc::vals::SampleTime::CYCLES1_5,
|
||||
SampleTime::Cycles3_5 => crate::pac::adc::vals::SampleTime::CYCLES3_5,
|
||||
SampleTime::Cycles7_5 => crate::pac::adc::vals::SampleTime::CYCLES7_5,
|
||||
SampleTime::Cycles12_5 => crate::pac::adc::vals::SampleTime::CYCLES12_5,
|
||||
SampleTime::Cycles19_5 => crate::pac::adc::vals::SampleTime::CYCLES19_5,
|
||||
SampleTime::Cycles39_5 => crate::pac::adc::vals::SampleTime::CYCLES39_5,
|
||||
SampleTime::Cycles79_5 => crate::pac::adc::vals::SampleTime::CYCLES79_5,
|
||||
SampleTime::Cycles160_5 => crate::pac::adc::vals::SampleTime::CYCLES160_5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SampleTime {
|
||||
fn default() -> Self {
|
||||
Self::Cycles1_5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use sample_time::SampleTime;
|
||||
|
||||
pub struct Adc<'d, T: Instance> {
|
||||
sample_time: SampleTime,
|
||||
vref_mv: u32,
|
||||
resolution: Resolution,
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Adc<'d, T> {
|
||||
pub fn new(_peri: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
|
||||
into_ref!(_peri);
|
||||
pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
|
||||
into_ref!(adc);
|
||||
enable();
|
||||
unsafe {
|
||||
T::regs().cr().modify(|reg| {
|
||||
@ -223,7 +71,7 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
|
||||
#[cfg(adc_g0)]
|
||||
T::regs().cfgr1().modify(|reg| {
|
||||
reg.set_chselrmod(true);
|
||||
reg.set_chselrmod(false);
|
||||
});
|
||||
}
|
||||
|
||||
@ -242,10 +90,8 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
delay.delay_us(1);
|
||||
|
||||
Self {
|
||||
adc,
|
||||
sample_time: Default::default(),
|
||||
resolution: Resolution::default(),
|
||||
vref_mv: VREF_DEFAULT_MV,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@ -285,49 +131,17 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
Vbat {}
|
||||
}
|
||||
|
||||
/// Calculates the system VDDA by sampling the internal VREFINT channel and comparing
|
||||
/// the result with the value stored at the factory. If the chip's VDDA is not stable, run
|
||||
/// this before each ADC conversion.
|
||||
#[cfg(not(stm32g0))] // TODO is this supposed to be public?
|
||||
#[allow(unused)] // TODO is this supposed to be public?
|
||||
fn calibrate(&mut self, vrefint: &mut VrefInt) {
|
||||
#[cfg(stm32l5)]
|
||||
let vrefint_cal: u32 = todo!();
|
||||
#[cfg(not(stm32l5))]
|
||||
let vrefint_cal = unsafe { crate::pac::VREFINTCAL.data().read().value() };
|
||||
let old_sample_time = self.sample_time;
|
||||
|
||||
// "Table 24. Embedded internal voltage reference" states that the sample time needs to be
|
||||
// at a minimum 4 us. With 640.5 ADC cycles we have a minimum of 8 us at 80 MHz, leaving
|
||||
// some headroom.
|
||||
self.sample_time = SampleTime::Cycles640_5;
|
||||
|
||||
// This can't actually fail, it's just in a result to satisfy hal trait
|
||||
let vrefint_samp = self.read(vrefint);
|
||||
|
||||
self.sample_time = old_sample_time;
|
||||
|
||||
self.vref_mv = (VREF_CALIB_MV * u32::from(vrefint_cal)) / u32::from(vrefint_samp);
|
||||
}
|
||||
|
||||
pub fn set_sample_time(&mut self, sample_time: SampleTime) {
|
||||
self.sample_time = sample_time;
|
||||
}
|
||||
|
||||
pub fn set_resolution(&mut self, resolution: Resolution) {
|
||||
self.resolution = resolution;
|
||||
}
|
||||
|
||||
/// Set VREF value in millivolts. This value is used for [to_millivolts()] sample conversion.
|
||||
///
|
||||
/// Use this if you have a known precise VREF (VDDA) pin reference voltage.
|
||||
pub fn set_vref_mv(&mut self, vref_mv: u32) {
|
||||
self.vref_mv = vref_mv;
|
||||
}
|
||||
|
||||
/// Convert a measurement to millivolts
|
||||
pub fn to_millivolts(&self, sample: u16) -> u16 {
|
||||
((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16
|
||||
unsafe {
|
||||
#[cfg(not(stm32g0))]
|
||||
T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
|
||||
#[cfg(stm32g0)]
|
||||
T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into()));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -379,12 +193,6 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
// spin
|
||||
}
|
||||
|
||||
// Configure ADC
|
||||
#[cfg(not(stm32g0))]
|
||||
T::regs().cfgr().modify(|reg| reg.set_res(self.resolution.res()));
|
||||
#[cfg(stm32g0)]
|
||||
T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res()));
|
||||
|
||||
// Configure channel
|
||||
Self::set_channel_sample_time(pin.channel(), self.sample_time);
|
||||
|
||||
@ -392,7 +200,7 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
#[cfg(not(stm32g0))]
|
||||
T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel()));
|
||||
#[cfg(stm32g0)]
|
||||
T::regs().chselr().write(|reg| reg.set_chsel(pin.channel() as u32));
|
||||
T::regs().chselr().write(|reg| reg.set_chsel(1 << pin.channel()));
|
||||
|
||||
// Some models are affected by an erratum:
|
||||
// If we perform conversions slower than 1 kHz, the first read ADC value can be
|
||||
@ -413,19 +221,16 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
|
||||
#[cfg(stm32g0)]
|
||||
unsafe fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) {
|
||||
T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.sample_time()));
|
||||
T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into()));
|
||||
}
|
||||
|
||||
#[cfg(not(stm32g0))]
|
||||
unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
||||
let sample_time = sample_time.into();
|
||||
if ch <= 9 {
|
||||
T::regs()
|
||||
.smpr1()
|
||||
.modify(|reg| reg.set_smp(ch as _, sample_time.sample_time()));
|
||||
T::regs().smpr1().modify(|reg| reg.set_smp(ch as _, sample_time));
|
||||
} else {
|
||||
T::regs()
|
||||
.smpr2()
|
||||
.modify(|reg| reg.set_smp((ch - 10) as _, sample_time.sample_time()));
|
||||
T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
use core::marker::PhantomData;
|
||||
use core::sync::atomic::{AtomicU8, Ordering};
|
||||
|
||||
use atomic_polyfill::{AtomicU8, Ordering};
|
||||
use embedded_hal_02::blocking::delay::DelayUs;
|
||||
use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel};
|
||||
use pac::adccommon::vals::Presc;
|
||||
|
||||
use super::{AdcPin, Instance, InternalChannel};
|
||||
use super::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime};
|
||||
use crate::time::Hertz;
|
||||
use crate::{pac, Peripheral};
|
||||
|
||||
@ -14,42 +13,6 @@ pub const VREF_DEFAULT_MV: u32 = 3300;
|
||||
/// VREF voltage used for factory calibration of VREFINTCAL register.
|
||||
pub const VREF_CALIB_MV: u32 = 3300;
|
||||
|
||||
pub enum Resolution {
|
||||
SixteenBit,
|
||||
FourteenBit,
|
||||
TwelveBit,
|
||||
TenBit,
|
||||
EightBit,
|
||||
}
|
||||
|
||||
impl Default for Resolution {
|
||||
fn default() -> Self {
|
||||
Self::SixteenBit
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolution {
|
||||
fn res(&self) -> pac::adc::vals::Res {
|
||||
match self {
|
||||
Resolution::SixteenBit => pac::adc::vals::Res::SIXTEENBIT,
|
||||
Resolution::FourteenBit => pac::adc::vals::Res::FOURTEENBITV,
|
||||
Resolution::TwelveBit => pac::adc::vals::Res::TWELVEBITV,
|
||||
Resolution::TenBit => pac::adc::vals::Res::TENBIT,
|
||||
Resolution::EightBit => pac::adc::vals::Res::EIGHTBIT,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_max_count(&self) -> u32 {
|
||||
match self {
|
||||
Resolution::SixteenBit => (1 << 16) - 1,
|
||||
Resolution::FourteenBit => (1 << 14) - 1,
|
||||
Resolution::TwelveBit => (1 << 12) - 1,
|
||||
Resolution::TenBit => (1 << 10) - 1,
|
||||
Resolution::EightBit => (1 << 8) - 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Vrefint/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs
|
||||
pub struct VrefInt;
|
||||
impl<T: Instance> InternalChannel<T> for VrefInt {}
|
||||
@ -193,57 +156,6 @@ foreach_peripheral!(
|
||||
};
|
||||
);
|
||||
|
||||
/// ADC sample time
|
||||
///
|
||||
/// The default setting is 2.5 ADC clock cycles.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum SampleTime {
|
||||
/// 1.5 ADC clock cycles
|
||||
Cycles1_5,
|
||||
|
||||
/// 2.5 ADC clock cycles
|
||||
Cycles2_5,
|
||||
|
||||
/// 8.5 ADC clock cycles
|
||||
Cycles8_5,
|
||||
|
||||
/// 16.5 ADC clock cycles
|
||||
Cycles16_5,
|
||||
|
||||
/// 32.5 ADC clock cycles
|
||||
Cycles32_5,
|
||||
|
||||
/// 64.5 ADC clock cycles
|
||||
Cycles64_5,
|
||||
|
||||
/// 387.5 ADC clock cycles
|
||||
Cycles387_5,
|
||||
|
||||
/// 810.5 ADC clock cycles
|
||||
Cycles810_5,
|
||||
}
|
||||
|
||||
impl SampleTime {
|
||||
pub(crate) fn sample_time(&self) -> pac::adc::vals::Smp {
|
||||
match self {
|
||||
SampleTime::Cycles1_5 => pac::adc::vals::Smp::CYCLES1_5,
|
||||
SampleTime::Cycles2_5 => pac::adc::vals::Smp::CYCLES2_5,
|
||||
SampleTime::Cycles8_5 => pac::adc::vals::Smp::CYCLES8_5,
|
||||
SampleTime::Cycles16_5 => pac::adc::vals::Smp::CYCLES16_5,
|
||||
SampleTime::Cycles32_5 => pac::adc::vals::Smp::CYCLES32_5,
|
||||
SampleTime::Cycles64_5 => pac::adc::vals::Smp::CYCLES64_5,
|
||||
SampleTime::Cycles387_5 => pac::adc::vals::Smp::CYCLES387_5,
|
||||
SampleTime::Cycles810_5 => pac::adc::vals::Smp::CYCLES810_5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SampleTime {
|
||||
fn default() -> Self {
|
||||
Self::Cycles1_5
|
||||
}
|
||||
}
|
||||
|
||||
// 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)]
|
||||
@ -312,16 +224,9 @@ impl Prescaler {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Adc<'d, T: Instance> {
|
||||
sample_time: SampleTime,
|
||||
vref_mv: u32,
|
||||
resolution: Resolution,
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> {
|
||||
pub fn new(_peri: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u16>) -> Self {
|
||||
embassy_hal_common::into_ref!(_peri);
|
||||
impl<'d, T: Instance> Adc<'d, T> {
|
||||
pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u16>) -> Self {
|
||||
embassy_hal_common::into_ref!(adc);
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
@ -351,10 +256,8 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> {
|
||||
}
|
||||
|
||||
let mut s = Self {
|
||||
adc,
|
||||
sample_time: Default::default(),
|
||||
vref_mv: VREF_DEFAULT_MV,
|
||||
resolution: Resolution::default(),
|
||||
phantom: PhantomData,
|
||||
};
|
||||
s.power_up(delay);
|
||||
s.configure_differential_inputs();
|
||||
@ -456,19 +359,9 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> {
|
||||
}
|
||||
|
||||
pub fn set_resolution(&mut self, resolution: Resolution) {
|
||||
self.resolution = resolution;
|
||||
}
|
||||
|
||||
/// Set VREF value in millivolts. This value is used for [to_millivolts()] sample conversion.
|
||||
///
|
||||
/// Use this if you have a known precise VREF (VDDA) pin reference voltage.
|
||||
pub fn set_vref_mv(&mut self, vref_mv: u32) {
|
||||
self.vref_mv = vref_mv;
|
||||
}
|
||||
|
||||
/// Convert a measurement to millivolts
|
||||
pub fn to_millivolts(&self, sample: u16) -> u16 {
|
||||
((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16
|
||||
unsafe {
|
||||
T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform a single conversion.
|
||||
@ -509,9 +402,6 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> {
|
||||
}
|
||||
|
||||
unsafe fn read_channel(&mut self, channel: u8) -> u16 {
|
||||
// Configure ADC
|
||||
T::regs().cfgr().modify(|reg| reg.set_res(self.resolution.res()));
|
||||
|
||||
// Configure channel
|
||||
Self::set_channel_sample_time(channel, self.sample_time);
|
||||
|
||||
@ -528,14 +418,11 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> {
|
||||
}
|
||||
|
||||
unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
||||
let sample_time = sample_time.into();
|
||||
if ch <= 9 {
|
||||
T::regs()
|
||||
.smpr(0)
|
||||
.modify(|reg| reg.set_smp(ch as _, sample_time.sample_time()));
|
||||
T::regs().smpr(0).modify(|reg| reg.set_smp(ch as _, sample_time));
|
||||
} else {
|
||||
T::regs()
|
||||
.smpr(1)
|
||||
.modify(|reg| reg.set_smp((ch - 10) as _, sample_time.sample_time()));
|
||||
T::regs().smpr(1).modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
use core::task::Waker;
|
||||
|
||||
use embassy_cortex_m::interrupt::Priority;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use super::{TransferOptions, Word, WordSize};
|
||||
@ -38,10 +39,12 @@ impl State {
|
||||
static STATE: State = State::new();
|
||||
|
||||
/// safety: must be called only once
|
||||
pub(crate) unsafe fn init() {
|
||||
pub(crate) unsafe fn init(irq_priority: Priority) {
|
||||
foreach_interrupt! {
|
||||
($peri:ident, bdma, $block:ident, $signal_name:ident, $irq:ident) => {
|
||||
crate::interrupt::$irq::steal().enable();
|
||||
let irq = crate::interrupt::$irq::steal();
|
||||
irq.set_priority(irq_priority);
|
||||
irq.enable();
|
||||
};
|
||||
}
|
||||
crate::_generated::init_bdma();
|
||||
@ -75,8 +78,7 @@ foreach_dma_channel! {
|
||||
);
|
||||
}
|
||||
|
||||
unsafe fn start_write_repeated<W: Word>(&mut self, _request: Request, repeated: W, count: usize, reg_addr: *mut W, options: TransferOptions) {
|
||||
let buf = [repeated];
|
||||
unsafe fn start_write_repeated<W: Word>(&mut self, _request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) {
|
||||
low_level_api::start_transfer(
|
||||
pac::$dma_peri,
|
||||
$channel_num,
|
||||
@ -84,7 +86,7 @@ foreach_dma_channel! {
|
||||
_request,
|
||||
vals::Dir::FROMMEMORY,
|
||||
reg_addr as *const u32,
|
||||
buf.as_ptr() as *mut u32,
|
||||
repeated as *mut u32,
|
||||
count,
|
||||
false,
|
||||
vals::Size::from(W::bits()),
|
||||
|
@ -1,6 +1,7 @@
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
use core::task::Waker;
|
||||
|
||||
use embassy_cortex_m::interrupt::Priority;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use super::{Burst, FlowControl, Request, TransferOptions, Word, WordSize};
|
||||
@ -67,10 +68,12 @@ impl State {
|
||||
static STATE: State = State::new();
|
||||
|
||||
/// safety: must be called only once
|
||||
pub(crate) unsafe fn init() {
|
||||
pub(crate) unsafe fn init(irq_priority: Priority) {
|
||||
foreach_interrupt! {
|
||||
($peri:ident, dma, $block:ident, $signal_name:ident, $irq:ident) => {
|
||||
interrupt::$irq::steal().enable();
|
||||
let irq = interrupt::$irq::steal();
|
||||
irq.set_priority(irq_priority);
|
||||
irq.enable();
|
||||
};
|
||||
}
|
||||
crate::_generated::init_dma();
|
||||
@ -99,15 +102,14 @@ foreach_dma_channel! {
|
||||
)
|
||||
}
|
||||
|
||||
unsafe fn start_write_repeated<W: Word>(&mut self, request: Request, repeated: W, count: usize, reg_addr: *mut W, options: TransferOptions) {
|
||||
let buf = [repeated];
|
||||
unsafe fn start_write_repeated<W: Word>(&mut self, request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) {
|
||||
low_level_api::start_transfer(
|
||||
pac::$dma_peri,
|
||||
$channel_num,
|
||||
request,
|
||||
vals::Dir::MEMORYTOPERIPHERAL,
|
||||
reg_addr as *const u32,
|
||||
buf.as_ptr() as *mut u32,
|
||||
repeated as *mut u32,
|
||||
count,
|
||||
false,
|
||||
vals::Size::from(W::bits()),
|
||||
|
@ -75,15 +75,14 @@ foreach_dma_channel! {
|
||||
)
|
||||
}
|
||||
|
||||
unsafe fn start_write_repeated<W: Word>(&mut self, request: Request, repeated: W, count: usize, reg_addr: *mut W, options: TransferOptions) {
|
||||
let buf = [repeated];
|
||||
unsafe fn start_write_repeated<W: Word>(&mut self, request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) {
|
||||
low_level_api::start_transfer(
|
||||
pac::$dma_peri,
|
||||
$channel_num,
|
||||
request,
|
||||
low_level_api::Dir::MemoryToPeripheral,
|
||||
reg_addr as *const u32,
|
||||
buf.as_ptr() as *mut u32,
|
||||
repeated as *mut u32,
|
||||
count,
|
||||
false,
|
||||
W::bits(),
|
||||
|
@ -12,6 +12,8 @@ use core::mem;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll, Waker};
|
||||
|
||||
#[cfg(any(dma, bdma))]
|
||||
use embassy_cortex_m::interrupt::Priority;
|
||||
use embassy_hal_common::{impl_peripheral, into_ref};
|
||||
|
||||
#[cfg(dmamux)]
|
||||
@ -57,7 +59,7 @@ pub(crate) mod sealed {
|
||||
unsafe fn start_write_repeated<W: super::Word>(
|
||||
&mut self,
|
||||
request: Request,
|
||||
repeated: W,
|
||||
repeated: *const W,
|
||||
count: usize,
|
||||
reg_addr: *mut W,
|
||||
options: TransferOptions,
|
||||
@ -244,7 +246,7 @@ mod transfers {
|
||||
pub fn write_repeated<'a, W: Word>(
|
||||
channel: impl Peripheral<P = impl Channel> + 'a,
|
||||
request: Request,
|
||||
repeated: W,
|
||||
repeated: *const W,
|
||||
count: usize,
|
||||
reg_addr: *mut W,
|
||||
) -> impl Future<Output = ()> + 'a {
|
||||
@ -294,11 +296,11 @@ pub struct NoDma;
|
||||
impl_peripheral!(NoDma);
|
||||
|
||||
// safety: must be called only once at startup
|
||||
pub(crate) unsafe fn init() {
|
||||
pub(crate) unsafe fn init(#[cfg(bdma)] bdma_priority: Priority, #[cfg(dma)] dma_priority: Priority) {
|
||||
#[cfg(bdma)]
|
||||
bdma::init();
|
||||
bdma::init(bdma_priority);
|
||||
#[cfg(dma)]
|
||||
dma::init();
|
||||
dma::init(dma_priority);
|
||||
#[cfg(dmamux)]
|
||||
dmamux::init();
|
||||
#[cfg(gpdma)]
|
||||
|
@ -1,13 +1,125 @@
|
||||
#![macro_use]
|
||||
|
||||
#[cfg(feature = "net")]
|
||||
#[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1/mod.rs")]
|
||||
#[cfg_attr(eth_v2, path = "v2/mod.rs")]
|
||||
mod _version;
|
||||
pub mod generic_smi;
|
||||
|
||||
#[cfg(feature = "net")]
|
||||
pub use _version::*;
|
||||
use core::task::Context;
|
||||
|
||||
use embassy_net_driver::{Capabilities, LinkState};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
pub use self::_version::*;
|
||||
|
||||
#[allow(unused)]
|
||||
const MTU: usize = 1514;
|
||||
const TX_BUFFER_SIZE: usize = 1514;
|
||||
const RX_BUFFER_SIZE: usize = 1536;
|
||||
|
||||
#[repr(C, align(8))]
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) struct Packet<const N: usize>([u8; N]);
|
||||
|
||||
pub struct PacketQueue<const TX: usize, const RX: usize> {
|
||||
tx_desc: [TDes; TX],
|
||||
rx_desc: [RDes; RX],
|
||||
tx_buf: [Packet<TX_BUFFER_SIZE>; TX],
|
||||
rx_buf: [Packet<RX_BUFFER_SIZE>; RX],
|
||||
}
|
||||
|
||||
impl<const TX: usize, const RX: usize> PacketQueue<TX, RX> {
|
||||
pub const fn new() -> Self {
|
||||
const NEW_TDES: TDes = TDes::new();
|
||||
const NEW_RDES: RDes = RDes::new();
|
||||
Self {
|
||||
tx_desc: [NEW_TDES; TX],
|
||||
rx_desc: [NEW_RDES; RX],
|
||||
tx_buf: [Packet([0; TX_BUFFER_SIZE]); TX],
|
||||
rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
|
||||
impl<'d, T: Instance, P: PHY> embassy_net_driver::Driver for Ethernet<'d, T, P> {
|
||||
type RxToken<'a> = RxToken<'a, 'd> where Self: 'a;
|
||||
type TxToken<'a> = TxToken<'a, 'd> where Self: 'a;
|
||||
|
||||
fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
||||
WAKER.register(cx.waker());
|
||||
if self.rx.available().is_some() && self.tx.available().is_some() {
|
||||
Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
|
||||
WAKER.register(cx.waker());
|
||||
if self.tx.available().is_some() {
|
||||
Some(TxToken { tx: &mut self.tx })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn capabilities(&self) -> Capabilities {
|
||||
let mut caps = Capabilities::default();
|
||||
caps.max_transmission_unit = MTU;
|
||||
caps.max_burst_size = Some(self.tx.len());
|
||||
caps
|
||||
}
|
||||
|
||||
fn link_state(&mut self, cx: &mut Context) -> LinkState {
|
||||
// TODO: wake cx.waker on link state change
|
||||
cx.waker().wake_by_ref();
|
||||
if P::poll_link(self) {
|
||||
LinkState::Up
|
||||
} else {
|
||||
LinkState::Down
|
||||
}
|
||||
}
|
||||
|
||||
fn ethernet_address(&self) -> [u8; 6] {
|
||||
self.mac_addr
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RxToken<'a, 'd> {
|
||||
rx: &'a mut RDesRing<'d>,
|
||||
}
|
||||
|
||||
impl<'a, 'd> embassy_net_driver::RxToken for RxToken<'a, 'd> {
|
||||
fn consume<R, F>(self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> R,
|
||||
{
|
||||
// NOTE(unwrap): we checked the queue wasn't full when creating the token.
|
||||
let pkt = unwrap!(self.rx.available());
|
||||
let r = f(pkt);
|
||||
self.rx.pop_packet();
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TxToken<'a, 'd> {
|
||||
tx: &'a mut TDesRing<'d>,
|
||||
}
|
||||
|
||||
impl<'a, 'd> embassy_net_driver::TxToken for TxToken<'a, 'd> {
|
||||
fn consume<R, F>(self, len: usize, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> R,
|
||||
{
|
||||
// NOTE(unwrap): we checked the queue wasn't full when creating the token.
|
||||
let pkt = unwrap!(self.tx.available());
|
||||
let r = f(&mut pkt[..len]);
|
||||
self.tx.transmit(len);
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
/// Station Management Interface (SMI) on an ethernet PHY
|
||||
///
|
||||
|
@ -1,21 +0,0 @@
|
||||
use crate::eth::_version::rx_desc::RDesRing;
|
||||
use crate::eth::_version::tx_desc::TDesRing;
|
||||
|
||||
pub struct DescriptorRing<const T: usize, const R: usize> {
|
||||
pub(crate) tx: TDesRing<T>,
|
||||
pub(crate) rx: RDesRing<R>,
|
||||
}
|
||||
|
||||
impl<const T: usize, const R: usize> DescriptorRing<T, R> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
tx: TDesRing::new(),
|
||||
rx: RDesRing::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(&mut self) {
|
||||
self.tx.init();
|
||||
self.rx.init();
|
||||
}
|
||||
}
|
@ -1,16 +1,19 @@
|
||||
// The v1c ethernet driver was ported to embassy from the awesome stm32-eth project (https://github.com/stm32-rs/stm32-eth).
|
||||
|
||||
use core::marker::PhantomData;
|
||||
mod rx_desc;
|
||||
mod tx_desc;
|
||||
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
use core::task::Waker;
|
||||
|
||||
use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
|
||||
use embassy_cortex_m::interrupt::InterruptExt;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_net::{Device, DeviceCapabilities, LinkState, PacketBuf, MTU};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf};
|
||||
|
||||
pub(crate) use self::rx_desc::{RDes, RDesRing};
|
||||
pub(crate) use self::tx_desc::{TDes, TDesRing};
|
||||
use super::*;
|
||||
use crate::gpio::sealed::{AFType, Pin as __GpioPin};
|
||||
use crate::gpio::{AnyPin, Speed};
|
||||
use crate::gpio::AnyPin;
|
||||
#[cfg(eth_v1a)]
|
||||
use crate::pac::AFIO;
|
||||
#[cfg(any(eth_v1b, eth_v1c))]
|
||||
@ -18,29 +21,16 @@ use crate::pac::SYSCFG;
|
||||
use crate::pac::{ETH, RCC};
|
||||
use crate::Peripheral;
|
||||
|
||||
mod descriptors;
|
||||
mod rx_desc;
|
||||
mod tx_desc;
|
||||
pub struct Ethernet<'d, T: Instance, P: PHY> {
|
||||
_peri: PeripheralRef<'d, T>,
|
||||
pub(crate) tx: TDesRing<'d>,
|
||||
pub(crate) rx: RDesRing<'d>,
|
||||
|
||||
use descriptors::DescriptorRing;
|
||||
use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct State<'d, T: Instance, const TX: usize, const RX: usize>(StateStorage<Inner<'d, T, TX, RX>>);
|
||||
impl<'d, T: Instance, const TX: usize, const RX: usize> State<'d, T, TX, RX> {
|
||||
pub const fn new() -> Self {
|
||||
Self(StateStorage::new())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Ethernet<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> {
|
||||
state: PeripheralMutex<'d, Inner<'d, T, TX, RX>>,
|
||||
pins: [PeripheralRef<'d, AnyPin>; 9],
|
||||
_phy: P,
|
||||
clock_range: Cr,
|
||||
phy_addr: u8,
|
||||
mac_addr: [u8; 6],
|
||||
pub(crate) mac_addr: [u8; 6],
|
||||
}
|
||||
|
||||
#[cfg(eth_v1a)]
|
||||
@ -76,16 +66,16 @@ macro_rules! config_pins {
|
||||
critical_section::with(|_| {
|
||||
$(
|
||||
$pin.set_as_af($pin.af_num(), AFType::OutputPushPull);
|
||||
$pin.set_speed(Speed::VeryHigh);
|
||||
$pin.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
)*
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, P, TX, RX> {
|
||||
impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
/// safety: the returned instance is not leak-safe
|
||||
pub unsafe fn new(
|
||||
state: &'d mut State<'d, T, TX, RX>,
|
||||
pub fn new<const TX: usize, const RX: usize>(
|
||||
queue: &'d mut PacketQueue<TX, RX>,
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
interrupt: impl Peripheral<P = crate::interrupt::ETH> + 'd,
|
||||
ref_clk: impl Peripheral<P = impl RefClkPin<T>> + 'd,
|
||||
@ -101,134 +91,131 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T,
|
||||
mac_addr: [u8; 6],
|
||||
phy_addr: u8,
|
||||
) -> Self {
|
||||
into_ref!(interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
|
||||
into_ref!(peri, interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
|
||||
|
||||
// Enable the necessary Clocks
|
||||
// NOTE(unsafe) We have exclusive access to the registers
|
||||
#[cfg(eth_v1a)]
|
||||
critical_section::with(|_| {
|
||||
RCC.apb2enr().modify(|w| w.set_afioen(true));
|
||||
unsafe {
|
||||
// Enable the necessary Clocks
|
||||
// NOTE(unsafe) We have exclusive access to the registers
|
||||
#[cfg(eth_v1a)]
|
||||
critical_section::with(|_| {
|
||||
RCC.apb2enr().modify(|w| w.set_afioen(true));
|
||||
|
||||
// Select RMII (Reduced Media Independent Interface)
|
||||
// Must be done prior to enabling peripheral clock
|
||||
AFIO.mapr().modify(|w| w.set_mii_rmii_sel(true));
|
||||
// Select RMII (Reduced Media Independent Interface)
|
||||
// Must be done prior to enabling peripheral clock
|
||||
AFIO.mapr().modify(|w| w.set_mii_rmii_sel(true));
|
||||
|
||||
RCC.ahbenr().modify(|w| {
|
||||
w.set_ethen(true);
|
||||
w.set_ethtxen(true);
|
||||
w.set_ethrxen(true);
|
||||
});
|
||||
});
|
||||
|
||||
#[cfg(any(eth_v1b, eth_v1c))]
|
||||
critical_section::with(|_| {
|
||||
RCC.apb2enr().modify(|w| w.set_syscfgen(true));
|
||||
RCC.ahb1enr().modify(|w| {
|
||||
w.set_ethen(true);
|
||||
w.set_ethtxen(true);
|
||||
w.set_ethrxen(true);
|
||||
RCC.ahbenr().modify(|w| {
|
||||
w.set_ethen(true);
|
||||
w.set_ethtxen(true);
|
||||
w.set_ethrxen(true);
|
||||
});
|
||||
});
|
||||
|
||||
// RMII (Reduced Media Independent Interface)
|
||||
SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(true));
|
||||
});
|
||||
#[cfg(any(eth_v1b, eth_v1c))]
|
||||
critical_section::with(|_| {
|
||||
RCC.apb2enr().modify(|w| w.set_syscfgen(true));
|
||||
RCC.ahb1enr().modify(|w| {
|
||||
w.set_ethen(true);
|
||||
w.set_ethtxen(true);
|
||||
w.set_ethrxen(true);
|
||||
});
|
||||
|
||||
#[cfg(eth_v1a)]
|
||||
{
|
||||
config_in_pins!(ref_clk, rx_d0, rx_d1);
|
||||
config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en);
|
||||
}
|
||||
// RMII (Reduced Media Independent Interface)
|
||||
SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(true));
|
||||
});
|
||||
|
||||
#[cfg(any(eth_v1b, eth_v1c))]
|
||||
config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
|
||||
|
||||
// NOTE(unsafe) We are ourselves not leak-safe.
|
||||
let state = PeripheralMutex::new(interrupt, &mut state.0, || Inner::new(peri));
|
||||
|
||||
// NOTE(unsafe) We have exclusive access to the registers
|
||||
let dma = ETH.ethernet_dma();
|
||||
let mac = ETH.ethernet_mac();
|
||||
|
||||
// Reset and wait
|
||||
dma.dmabmr().modify(|w| w.set_sr(true));
|
||||
while dma.dmabmr().read().sr() {}
|
||||
|
||||
mac.maccr().modify(|w| {
|
||||
w.set_ifg(Ifg::IFG96); // inter frame gap 96 bit times
|
||||
w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping
|
||||
w.set_fes(Fes::FES100); // fast ethernet speed
|
||||
w.set_dm(Dm::FULLDUPLEX); // full duplex
|
||||
// TODO: Carrier sense ? ECRSFD
|
||||
});
|
||||
|
||||
// Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core,
|
||||
// so the LR write must happen after the HR write.
|
||||
mac.maca0hr()
|
||||
.modify(|w| w.set_maca0h(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8)));
|
||||
mac.maca0lr().write(|w| {
|
||||
w.set_maca0l(
|
||||
u32::from(mac_addr[0])
|
||||
| (u32::from(mac_addr[1]) << 8)
|
||||
| (u32::from(mac_addr[2]) << 16)
|
||||
| (u32::from(mac_addr[3]) << 24),
|
||||
)
|
||||
});
|
||||
|
||||
// pause time
|
||||
mac.macfcr().modify(|w| w.set_pt(0x100));
|
||||
|
||||
// Transfer and Forward, Receive and Forward
|
||||
dma.dmaomr().modify(|w| {
|
||||
w.set_tsf(Tsf::STOREFORWARD);
|
||||
w.set_rsf(Rsf::STOREFORWARD);
|
||||
});
|
||||
|
||||
dma.dmabmr().modify(|w| {
|
||||
w.set_pbl(Pbl::PBL32) // programmable burst length - 32 ?
|
||||
});
|
||||
|
||||
// TODO MTU size setting not found for v1 ethernet, check if correct
|
||||
|
||||
// NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called
|
||||
let hclk = crate::rcc::get_freqs().ahb1;
|
||||
let hclk_mhz = hclk.0 / 1_000_000;
|
||||
|
||||
// Set the MDC clock frequency in the range 1MHz - 2.5MHz
|
||||
let clock_range = match hclk_mhz {
|
||||
0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."),
|
||||
25..=34 => Cr::CR_20_35, // Divide by 16
|
||||
35..=59 => Cr::CR_35_60, // Divide by 26
|
||||
60..=99 => Cr::CR_60_100, // Divide by 42
|
||||
100..=149 => Cr::CR_100_150, // Divide by 62
|
||||
150..=216 => Cr::CR_150_168, // Divide by 102
|
||||
_ => {
|
||||
panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider")
|
||||
#[cfg(eth_v1a)]
|
||||
{
|
||||
config_in_pins!(ref_clk, rx_d0, rx_d1);
|
||||
config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en);
|
||||
}
|
||||
};
|
||||
|
||||
let pins = [
|
||||
ref_clk.map_into(),
|
||||
mdio.map_into(),
|
||||
mdc.map_into(),
|
||||
crs.map_into(),
|
||||
rx_d0.map_into(),
|
||||
rx_d1.map_into(),
|
||||
tx_d0.map_into(),
|
||||
tx_d1.map_into(),
|
||||
tx_en.map_into(),
|
||||
];
|
||||
#[cfg(any(eth_v1b, eth_v1c))]
|
||||
config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
|
||||
|
||||
let mut this = Self {
|
||||
state,
|
||||
pins,
|
||||
_phy: phy,
|
||||
clock_range,
|
||||
phy_addr,
|
||||
mac_addr,
|
||||
};
|
||||
// NOTE(unsafe) We have exclusive access to the registers
|
||||
let dma = ETH.ethernet_dma();
|
||||
let mac = ETH.ethernet_mac();
|
||||
|
||||
this.state.with(|s| {
|
||||
s.desc_ring.init();
|
||||
// Reset and wait
|
||||
dma.dmabmr().modify(|w| w.set_sr(true));
|
||||
while dma.dmabmr().read().sr() {}
|
||||
|
||||
mac.maccr().modify(|w| {
|
||||
w.set_ifg(Ifg::IFG96); // inter frame gap 96 bit times
|
||||
w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping
|
||||
w.set_fes(Fes::FES100); // fast ethernet speed
|
||||
w.set_dm(Dm::FULLDUPLEX); // full duplex
|
||||
// TODO: Carrier sense ? ECRSFD
|
||||
});
|
||||
|
||||
// Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core,
|
||||
// so the LR write must happen after the HR write.
|
||||
mac.maca0hr()
|
||||
.modify(|w| w.set_maca0h(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8)));
|
||||
mac.maca0lr().write(|w| {
|
||||
w.set_maca0l(
|
||||
u32::from(mac_addr[0])
|
||||
| (u32::from(mac_addr[1]) << 8)
|
||||
| (u32::from(mac_addr[2]) << 16)
|
||||
| (u32::from(mac_addr[3]) << 24),
|
||||
)
|
||||
});
|
||||
|
||||
// pause time
|
||||
mac.macfcr().modify(|w| w.set_pt(0x100));
|
||||
|
||||
// Transfer and Forward, Receive and Forward
|
||||
dma.dmaomr().modify(|w| {
|
||||
w.set_tsf(Tsf::STOREFORWARD);
|
||||
w.set_rsf(Rsf::STOREFORWARD);
|
||||
});
|
||||
|
||||
dma.dmabmr().modify(|w| {
|
||||
w.set_pbl(Pbl::PBL32) // programmable burst length - 32 ?
|
||||
});
|
||||
|
||||
// TODO MTU size setting not found for v1 ethernet, check if correct
|
||||
|
||||
// NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called
|
||||
let hclk = crate::rcc::get_freqs().ahb1;
|
||||
let hclk_mhz = hclk.0 / 1_000_000;
|
||||
|
||||
// Set the MDC clock frequency in the range 1MHz - 2.5MHz
|
||||
let clock_range = match hclk_mhz {
|
||||
0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."),
|
||||
25..=34 => Cr::CR_20_35, // Divide by 16
|
||||
35..=59 => Cr::CR_35_60, // Divide by 26
|
||||
60..=99 => Cr::CR_60_100, // Divide by 42
|
||||
100..=149 => Cr::CR_100_150, // Divide by 62
|
||||
150..=216 => Cr::CR_150_168, // Divide by 102
|
||||
_ => {
|
||||
panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider")
|
||||
}
|
||||
};
|
||||
|
||||
let pins = [
|
||||
ref_clk.map_into(),
|
||||
mdio.map_into(),
|
||||
mdc.map_into(),
|
||||
crs.map_into(),
|
||||
rx_d0.map_into(),
|
||||
rx_d1.map_into(),
|
||||
tx_d0.map_into(),
|
||||
tx_d1.map_into(),
|
||||
tx_en.map_into(),
|
||||
];
|
||||
|
||||
let mut this = Self {
|
||||
_peri: peri,
|
||||
pins,
|
||||
_phy: phy,
|
||||
clock_range,
|
||||
phy_addr,
|
||||
mac_addr,
|
||||
tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf),
|
||||
rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf),
|
||||
};
|
||||
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
@ -245,23 +232,45 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T,
|
||||
w.set_sr(DmaomrSr::STARTED); // start receiving channel
|
||||
});
|
||||
|
||||
this.rx.demand_poll();
|
||||
|
||||
// Enable interrupts
|
||||
dma.dmaier().modify(|w| {
|
||||
w.set_nise(true);
|
||||
w.set_rie(true);
|
||||
w.set_tie(true);
|
||||
});
|
||||
});
|
||||
P::phy_reset(&mut this);
|
||||
P::phy_init(&mut this);
|
||||
|
||||
this
|
||||
P::phy_reset(&mut this);
|
||||
P::phy_init(&mut this);
|
||||
|
||||
interrupt.set_handler(Self::on_interrupt);
|
||||
interrupt.enable();
|
||||
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
fn on_interrupt(_cx: *mut ()) {
|
||||
WAKER.wake();
|
||||
|
||||
// TODO: Check and clear more flags
|
||||
unsafe {
|
||||
let dma = ETH.ethernet_dma();
|
||||
|
||||
dma.dmasr().modify(|w| {
|
||||
w.set_ts(true);
|
||||
w.set_rs(true);
|
||||
w.set_nis(true);
|
||||
});
|
||||
// Delay two peripheral's clock
|
||||
dma.dmasr().read();
|
||||
dma.dmasr().read();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationManagement
|
||||
for Ethernet<'d, T, P, TX, RX>
|
||||
{
|
||||
unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> {
|
||||
fn smi_read(&mut self, reg: u8) -> u16 {
|
||||
// NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self`
|
||||
unsafe {
|
||||
@ -297,44 +306,7 @@ unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationMa
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Device for Ethernet<'d, T, P, TX, RX> {
|
||||
fn is_transmit_ready(&mut self) -> bool {
|
||||
self.state.with(|s| s.desc_ring.tx.available())
|
||||
}
|
||||
|
||||
fn transmit(&mut self, pkt: PacketBuf) {
|
||||
self.state.with(|s| unwrap!(s.desc_ring.tx.transmit(pkt)));
|
||||
}
|
||||
|
||||
fn receive(&mut self) -> Option<PacketBuf> {
|
||||
self.state.with(|s| s.desc_ring.rx.pop_packet())
|
||||
}
|
||||
|
||||
fn register_waker(&mut self, waker: &Waker) {
|
||||
WAKER.register(waker);
|
||||
}
|
||||
|
||||
fn capabilities(&self) -> DeviceCapabilities {
|
||||
let mut caps = DeviceCapabilities::default();
|
||||
caps.max_transmission_unit = MTU;
|
||||
caps.max_burst_size = Some(TX.min(RX));
|
||||
caps
|
||||
}
|
||||
|
||||
fn link_state(&mut self) -> LinkState {
|
||||
if P::poll_link(self) {
|
||||
LinkState::Up
|
||||
} else {
|
||||
LinkState::Down
|
||||
}
|
||||
}
|
||||
|
||||
fn ethernet_address(&self) -> [u8; 6] {
|
||||
self.mac_addr
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop for Ethernet<'d, T, P, TX, RX> {
|
||||
impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> {
|
||||
fn drop(&mut self) {
|
||||
// NOTE(unsafe) We have `&mut self` and the interrupt doesn't use this registers
|
||||
unsafe {
|
||||
@ -361,46 +333,3 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop for Etherne
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
struct Inner<'d, T: Instance, const TX: usize, const RX: usize> {
|
||||
_peri: PhantomData<&'d mut T>,
|
||||
desc_ring: DescriptorRing<TX, RX>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const TX: usize, const RX: usize> Inner<'d, T, TX, RX> {
|
||||
pub fn new(_peri: impl Peripheral<P = T> + 'd) -> Self {
|
||||
Self {
|
||||
_peri: PhantomData,
|
||||
desc_ring: DescriptorRing::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const TX: usize, const RX: usize> PeripheralState for Inner<'d, T, TX, RX> {
|
||||
type Interrupt = crate::interrupt::ETH;
|
||||
|
||||
fn on_interrupt(&mut self) {
|
||||
unwrap!(self.desc_ring.tx.on_interrupt());
|
||||
self.desc_ring.rx.on_interrupt();
|
||||
|
||||
WAKER.wake();
|
||||
|
||||
// TODO: Check and clear more flags
|
||||
unsafe {
|
||||
let dma = ETH.ethernet_dma();
|
||||
|
||||
dma.dmasr().modify(|w| {
|
||||
w.set_ts(true);
|
||||
w.set_rs(true);
|
||||
w.set_nis(true);
|
||||
});
|
||||
// Delay two peripheral's clock
|
||||
dma.dmasr().read();
|
||||
dma.dmasr().read();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
|
@ -1,9 +1,9 @@
|
||||
use core::sync::atomic::{compiler_fence, fence, Ordering};
|
||||
|
||||
use embassy_net::{Packet, PacketBox, PacketBoxExt, PacketBuf};
|
||||
use stm32_metapac::eth::vals::{DmaomrSr, Rpd, Rps};
|
||||
use stm32_metapac::eth::vals::{Rpd, Rps};
|
||||
use vcell::VolatileCell;
|
||||
|
||||
use crate::eth::RX_BUFFER_SIZE;
|
||||
use crate::pac::ETH;
|
||||
|
||||
mod rx_consts {
|
||||
@ -28,6 +28,8 @@ mod rx_consts {
|
||||
|
||||
use rx_consts::*;
|
||||
|
||||
use super::Packet;
|
||||
|
||||
/// Receive Descriptor representation
|
||||
///
|
||||
/// * rdes0: OWN and Status
|
||||
@ -35,7 +37,7 @@ use rx_consts::*;
|
||||
/// * rdes2: data buffer address
|
||||
/// * rdes3: next descriptor address
|
||||
#[repr(C)]
|
||||
struct RDes {
|
||||
pub(crate) struct RDes {
|
||||
rdes0: VolatileCell<u32>,
|
||||
rdes1: VolatileCell<u32>,
|
||||
rdes2: VolatileCell<u32>,
|
||||
@ -54,7 +56,7 @@ impl RDes {
|
||||
|
||||
/// Return true if this RDes is acceptable to us
|
||||
#[inline(always)]
|
||||
pub fn valid(&self) -> bool {
|
||||
fn valid(&self) -> bool {
|
||||
// Write-back descriptor is valid if:
|
||||
//
|
||||
// Contains first buffer of packet AND contains last buf of
|
||||
@ -64,15 +66,16 @@ impl RDes {
|
||||
|
||||
/// Return true if this RDes is not currently owned by the DMA
|
||||
#[inline(always)]
|
||||
pub fn available(&self) -> bool {
|
||||
fn available(&self) -> bool {
|
||||
self.rdes0.get() & RXDESC_0_OWN == 0 // Owned by us
|
||||
}
|
||||
|
||||
/// Configures the reception buffer address and length and passed descriptor ownership to the DMA
|
||||
#[inline(always)]
|
||||
pub fn set_ready(&mut self, buf_addr: u32, buf_len: usize) {
|
||||
self.rdes1.set(self.rdes1.get() | (buf_len as u32) & RXDESC_1_RBS_MASK);
|
||||
self.rdes2.set(buf_addr);
|
||||
fn set_ready(&self, buf: *mut u8) {
|
||||
self.rdes1
|
||||
.set(self.rdes1.get() | (RX_BUFFER_SIZE as u32) & RXDESC_1_RBS_MASK);
|
||||
self.rdes2.set(buf as u32);
|
||||
|
||||
// "Preceding reads and writes cannot be moved past subsequent writes."
|
||||
fence(Ordering::Release);
|
||||
@ -88,12 +91,12 @@ impl RDes {
|
||||
|
||||
// points to next descriptor (RCH)
|
||||
#[inline(always)]
|
||||
fn set_buffer2(&mut self, buffer: *const u8) {
|
||||
fn set_buffer2(&self, buffer: *const u8) {
|
||||
self.rdes3.set(buffer as u32);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set_end_of_ring(&mut self) {
|
||||
fn set_end_of_ring(&self) {
|
||||
self.rdes1.set(self.rdes1.get() | RXDESC_1_RER);
|
||||
}
|
||||
|
||||
@ -102,7 +105,7 @@ impl RDes {
|
||||
((self.rdes0.get() >> RXDESC_0_FL_SHIFT) & RXDESC_0_FL_MASK) as usize
|
||||
}
|
||||
|
||||
pub fn setup(&mut self, next: Option<&Self>) {
|
||||
fn setup(&self, next: Option<&Self>, buf: *mut u8) {
|
||||
// Defer this initialization to this function, so we can have `RingEntry` on bss.
|
||||
self.rdes1.set(self.rdes1.get() | RXDESC_1_RCH);
|
||||
|
||||
@ -113,8 +116,11 @@ impl RDes {
|
||||
self.set_end_of_ring();
|
||||
}
|
||||
}
|
||||
|
||||
self.set_ready(buf);
|
||||
}
|
||||
}
|
||||
|
||||
/// Running state of the `RxRing`
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub enum RunningState {
|
||||
@ -123,116 +129,42 @@ pub enum RunningState {
|
||||
Running,
|
||||
}
|
||||
|
||||
impl RunningState {
|
||||
/// whether self equals to `RunningState::Running`
|
||||
pub fn is_running(&self) -> bool {
|
||||
*self == RunningState::Running
|
||||
}
|
||||
}
|
||||
|
||||
/// Rx ring of descriptors and packets
|
||||
///
|
||||
/// This ring has three major locations that work in lock-step. The DMA will never write to the tail
|
||||
/// index, so the `read_index` must never pass the tail index. The `next_tail_index` is always 1
|
||||
/// slot ahead of the real tail index, and it must never pass the `read_index` or it could overwrite
|
||||
/// a packet still to be passed to the application.
|
||||
///
|
||||
/// nt can't pass r (no alloc)
|
||||
/// +---+---+---+---+ Read ok +---+---+---+---+ No Read +---+---+---+---+
|
||||
/// | | | | | ------------> | | | | | ------------> | | | | |
|
||||
/// +---+---+---+---+ Allocation ok +---+---+---+---+ +---+---+---+---+
|
||||
/// ^ ^t ^t ^ ^t ^
|
||||
/// |r |r |r
|
||||
/// |nt |nt |nt
|
||||
///
|
||||
///
|
||||
/// +---+---+---+---+ Read ok +---+---+---+---+ Can't read +---+---+---+---+
|
||||
/// | | | | | ------------> | | | | | ------------> | | | | |
|
||||
/// +---+---+---+---+ Allocation fail +---+---+---+---+ Allocation ok +---+---+---+---+
|
||||
/// ^ ^t ^ ^t ^ ^ ^ ^t
|
||||
/// |r | |r | | |r
|
||||
/// |nt |nt |nt
|
||||
///
|
||||
pub(crate) struct RDesRing<const N: usize> {
|
||||
descriptors: [RDes; N],
|
||||
buffers: [Option<PacketBox>; N],
|
||||
read_index: usize,
|
||||
next_tail_index: usize,
|
||||
pub(crate) struct RDesRing<'a> {
|
||||
descriptors: &'a mut [RDes],
|
||||
buffers: &'a mut [Packet<RX_BUFFER_SIZE>],
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<const N: usize> RDesRing<N> {
|
||||
pub const fn new() -> Self {
|
||||
const RDES: RDes = RDes::new();
|
||||
const BUFFERS: Option<PacketBox> = None;
|
||||
impl<'a> RDesRing<'a> {
|
||||
pub(crate) fn new(descriptors: &'a mut [RDes], buffers: &'a mut [Packet<RX_BUFFER_SIZE>]) -> Self {
|
||||
assert!(descriptors.len() > 1);
|
||||
assert!(descriptors.len() == buffers.len());
|
||||
|
||||
Self {
|
||||
descriptors: [RDES; N],
|
||||
buffers: [BUFFERS; N],
|
||||
read_index: 0,
|
||||
next_tail_index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn init(&mut self) {
|
||||
assert!(N > 1);
|
||||
let mut last_index = 0;
|
||||
for (index, buf) in self.buffers.iter_mut().enumerate() {
|
||||
let pkt = match PacketBox::new(Packet::new()) {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
if index == 0 {
|
||||
panic!("Could not allocate at least one buffer for Ethernet receiving");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
self.descriptors[index].set_ready(pkt.as_ptr() as u32, pkt.len());
|
||||
*buf = Some(pkt);
|
||||
last_index = index;
|
||||
}
|
||||
self.next_tail_index = (last_index + 1) % N;
|
||||
|
||||
// not sure if this is supposed to span all of the descriptor or just those that contain buffers
|
||||
{
|
||||
let mut previous: Option<&mut RDes> = None;
|
||||
for entry in self.descriptors.iter_mut() {
|
||||
if let Some(prev) = &mut previous {
|
||||
prev.setup(Some(entry));
|
||||
}
|
||||
previous = Some(entry);
|
||||
}
|
||||
|
||||
if let Some(entry) = &mut previous {
|
||||
entry.setup(None);
|
||||
}
|
||||
for (i, entry) in descriptors.iter().enumerate() {
|
||||
entry.setup(descriptors.get(i + 1), buffers[i].0.as_mut_ptr());
|
||||
}
|
||||
|
||||
// Register txdescriptor start
|
||||
// Register rx descriptor start
|
||||
// NOTE (unsafe) Used for atomic writes
|
||||
unsafe {
|
||||
ETH.ethernet_dma()
|
||||
.dmardlar()
|
||||
.write(|w| w.0 = &self.descriptors as *const _ as u32);
|
||||
.write(|w| w.0 = descriptors.as_ptr() as u32);
|
||||
};
|
||||
// We already have fences in `set_owned`, which is called in `setup`
|
||||
|
||||
// Start receive
|
||||
unsafe { ETH.ethernet_dma().dmaomr().modify(|w| w.set_sr(DmaomrSr::STARTED)) };
|
||||
|
||||
self.demand_poll();
|
||||
Self {
|
||||
descriptors,
|
||||
buffers,
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn demand_poll(&self) {
|
||||
pub(crate) fn demand_poll(&self) {
|
||||
unsafe { ETH.ethernet_dma().dmarpdr().write(|w| w.set_rpd(Rpd::POLL)) };
|
||||
}
|
||||
|
||||
pub(crate) fn on_interrupt(&mut self) {
|
||||
// XXX: Do we need to do anything here ? Maybe we should try to advance the tail ptr, but it
|
||||
// would soon hit the read ptr anyway, and we will wake smoltcp's stack on the interrupt
|
||||
// which should try to pop a packet...
|
||||
}
|
||||
|
||||
/// Get current `RunningState`
|
||||
fn running_state(&self) -> RunningState {
|
||||
match unsafe { ETH.ethernet_dma().dmasr().read().rps() } {
|
||||
@ -252,52 +184,52 @@ impl<const N: usize> RDesRing<N> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn pop_packet(&mut self) -> Option<PacketBuf> {
|
||||
if !self.running_state().is_running() {
|
||||
/// Get a received packet if any, or None.
|
||||
pub(crate) fn available(&mut self) -> Option<&mut [u8]> {
|
||||
if self.running_state() != RunningState::Running {
|
||||
self.demand_poll();
|
||||
}
|
||||
|
||||
// Not sure if the contents of the write buffer on the M7 can affects reads, so we are using
|
||||
// a DMB here just in case, it also serves as a hint to the compiler that we're syncing the
|
||||
// buffer (I think .-.)
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
let read_available = self.descriptors[self.read_index].available();
|
||||
let tail_index = (self.next_tail_index + N - 1) % N;
|
||||
|
||||
let pkt = if read_available && self.read_index != tail_index {
|
||||
let pkt = self.buffers[self.read_index].take();
|
||||
let len = self.descriptors[self.read_index].packet_len();
|
||||
|
||||
assert!(pkt.is_some());
|
||||
let valid = self.descriptors[self.read_index].valid();
|
||||
|
||||
self.read_index = (self.read_index + 1) % N;
|
||||
if valid {
|
||||
pkt.map(|p| p.slice(0..len))
|
||||
} else {
|
||||
None
|
||||
// We might have to process many packets, in case some have been rx'd but are invalid.
|
||||
loop {
|
||||
let descriptor = &mut self.descriptors[self.index];
|
||||
if !descriptor.available() {
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Try to advance the tail_index
|
||||
if self.next_tail_index != self.read_index {
|
||||
match PacketBox::new(Packet::new()) {
|
||||
Some(b) => {
|
||||
let addr = b.as_ptr() as u32;
|
||||
let buffer_len = b.len();
|
||||
self.buffers[self.next_tail_index].replace(b);
|
||||
self.descriptors[self.next_tail_index].set_ready(addr, buffer_len);
|
||||
|
||||
// "Preceding reads and writes cannot be moved past subsequent writes."
|
||||
fence(Ordering::Release);
|
||||
|
||||
self.next_tail_index = (self.next_tail_index + 1) % N;
|
||||
}
|
||||
None => {}
|
||||
// If packet is invalid, pop it and try again.
|
||||
if !descriptor.valid() {
|
||||
warn!("invalid packet: {:08x}", descriptor.rdes0.get());
|
||||
self.pop_packet();
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
let descriptor = &mut self.descriptors[self.index];
|
||||
let len = descriptor.packet_len();
|
||||
return Some(&mut self.buffers[self.index].0[..len]);
|
||||
}
|
||||
|
||||
/// Pop the packet previously returned by `available`.
|
||||
pub(crate) fn pop_packet(&mut self) {
|
||||
let descriptor = &mut self.descriptors[self.index];
|
||||
assert!(descriptor.available());
|
||||
|
||||
self.descriptors[self.index].set_ready(self.buffers[self.index].0.as_mut_ptr());
|
||||
|
||||
self.demand_poll();
|
||||
|
||||
// Increment index.
|
||||
self.index += 1;
|
||||
if self.index == self.descriptors.len() {
|
||||
self.index = 0
|
||||
}
|
||||
pkt
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,10 @@
|
||||
use core::sync::atomic::{compiler_fence, fence, Ordering};
|
||||
|
||||
use embassy_net::PacketBuf;
|
||||
use stm32_metapac::eth::vals::St;
|
||||
use vcell::VolatileCell;
|
||||
|
||||
use crate::eth::TX_BUFFER_SIZE;
|
||||
use crate::pac::ETH;
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
NoBufferAvailable,
|
||||
// TODO: Break down this error into several others
|
||||
TransmissionError,
|
||||
}
|
||||
|
||||
/// Transmit and Receive Descriptor fields
|
||||
#[allow(dead_code)]
|
||||
mod tx_consts {
|
||||
@ -37,6 +27,8 @@ mod tx_consts {
|
||||
}
|
||||
use tx_consts::*;
|
||||
|
||||
use super::Packet;
|
||||
|
||||
/// Transmit Descriptor representation
|
||||
///
|
||||
/// * tdes0: control
|
||||
@ -44,7 +36,7 @@ use tx_consts::*;
|
||||
/// * tdes2: data buffer address
|
||||
/// * tdes3: next descriptor address
|
||||
#[repr(C)]
|
||||
struct TDes {
|
||||
pub(crate) struct TDes {
|
||||
tdes0: VolatileCell<u32>,
|
||||
tdes1: VolatileCell<u32>,
|
||||
tdes2: VolatileCell<u32>,
|
||||
@ -62,7 +54,7 @@ impl TDes {
|
||||
}
|
||||
|
||||
/// Return true if this TDes is not currently owned by the DMA
|
||||
pub fn available(&self) -> bool {
|
||||
fn available(&self) -> bool {
|
||||
(self.tdes0.get() & TXDESC_0_OWN) == 0
|
||||
}
|
||||
|
||||
@ -79,26 +71,26 @@ impl TDes {
|
||||
fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
fn set_buffer1(&mut self, buffer: *const u8) {
|
||||
fn set_buffer1(&self, buffer: *const u8) {
|
||||
self.tdes2.set(buffer as u32);
|
||||
}
|
||||
|
||||
fn set_buffer1_len(&mut self, len: usize) {
|
||||
fn set_buffer1_len(&self, len: usize) {
|
||||
self.tdes1
|
||||
.set((self.tdes1.get() & !TXDESC_1_TBS_MASK) | ((len as u32) << TXDESC_1_TBS_SHIFT));
|
||||
}
|
||||
|
||||
// points to next descriptor (RCH)
|
||||
fn set_buffer2(&mut self, buffer: *const u8) {
|
||||
fn set_buffer2(&self, buffer: *const u8) {
|
||||
self.tdes3.set(buffer as u32);
|
||||
}
|
||||
|
||||
fn set_end_of_ring(&mut self) {
|
||||
fn set_end_of_ring(&self) {
|
||||
self.tdes0.set(self.tdes0.get() | TXDESC_0_TER);
|
||||
}
|
||||
|
||||
// set up as a part fo the ring buffer - configures the tdes
|
||||
pub fn setup(&mut self, next: Option<&Self>) {
|
||||
fn setup(&self, next: Option<&Self>) {
|
||||
// Defer this initialization to this function, so we can have `RingEntry` on bss.
|
||||
self.tdes0.set(TXDESC_0_TCH | TXDESC_0_IOC | TXDESC_0_FS | TXDESC_0_LS);
|
||||
match next {
|
||||
@ -111,85 +103,58 @@ impl TDes {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct TDesRing<const N: usize> {
|
||||
descriptors: [TDes; N],
|
||||
buffers: [Option<PacketBuf>; N],
|
||||
next_entry: usize,
|
||||
pub(crate) struct TDesRing<'a> {
|
||||
descriptors: &'a mut [TDes],
|
||||
buffers: &'a mut [Packet<TX_BUFFER_SIZE>],
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<const N: usize> TDesRing<N> {
|
||||
pub const fn new() -> Self {
|
||||
const TDES: TDes = TDes::new();
|
||||
const BUFFERS: Option<PacketBuf> = None;
|
||||
|
||||
Self {
|
||||
descriptors: [TDES; N],
|
||||
buffers: [BUFFERS; N],
|
||||
next_entry: 0,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TDesRing<'a> {
|
||||
/// Initialise this TDesRing. Assume TDesRing is corrupt
|
||||
///
|
||||
/// The current memory address of the buffers inside this TDesRing
|
||||
/// will be stored in the descriptors, so ensure the TDesRing is
|
||||
/// not moved after initialisation.
|
||||
pub(crate) fn init(&mut self) {
|
||||
assert!(N > 0);
|
||||
pub(crate) fn new(descriptors: &'a mut [TDes], buffers: &'a mut [Packet<TX_BUFFER_SIZE>]) -> Self {
|
||||
assert!(descriptors.len() > 0);
|
||||
assert!(descriptors.len() == buffers.len());
|
||||
|
||||
{
|
||||
let mut previous: Option<&mut TDes> = None;
|
||||
for entry in self.descriptors.iter_mut() {
|
||||
if let Some(prev) = &mut previous {
|
||||
prev.setup(Some(entry));
|
||||
}
|
||||
previous = Some(entry);
|
||||
}
|
||||
|
||||
if let Some(entry) = &mut previous {
|
||||
entry.setup(None);
|
||||
}
|
||||
for (i, entry) in descriptors.iter().enumerate() {
|
||||
entry.setup(descriptors.get(i + 1));
|
||||
}
|
||||
self.next_entry = 0;
|
||||
|
||||
// Register txdescriptor start
|
||||
// NOTE (unsafe) Used for atomic writes
|
||||
unsafe {
|
||||
ETH.ethernet_dma()
|
||||
.dmatdlar()
|
||||
.write(|w| w.0 = &self.descriptors as *const _ as u32);
|
||||
.write(|w| w.0 = descriptors.as_ptr() as u32);
|
||||
}
|
||||
|
||||
// "Preceding reads and writes cannot be moved past subsequent writes."
|
||||
#[cfg(feature = "fence")]
|
||||
fence(Ordering::Release);
|
||||
|
||||
// We don't need a compiler fence here because all interactions with `Descriptor` are
|
||||
// volatiles
|
||||
|
||||
// Start transmission
|
||||
unsafe { ETH.ethernet_dma().dmaomr().modify(|w| w.set_st(St::STARTED)) };
|
||||
}
|
||||
|
||||
/// Return true if a TDes is available for use
|
||||
pub(crate) fn available(&self) -> bool {
|
||||
self.descriptors[self.next_entry].available()
|
||||
}
|
||||
|
||||
pub(crate) fn transmit(&mut self, pkt: PacketBuf) -> Result<(), Error> {
|
||||
if !self.available() {
|
||||
return Err(Error::NoBufferAvailable);
|
||||
Self {
|
||||
descriptors,
|
||||
buffers,
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
let descriptor = &mut self.descriptors[self.next_entry];
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
self.descriptors.len()
|
||||
}
|
||||
|
||||
let pkt_len = pkt.len();
|
||||
let address = pkt.as_ptr() as *const u8;
|
||||
/// Return the next available packet buffer for transmitting, or None
|
||||
pub(crate) fn available(&mut self) -> Option<&mut [u8]> {
|
||||
let descriptor = &mut self.descriptors[self.index];
|
||||
if descriptor.available() {
|
||||
Some(&mut self.buffers[self.index].0)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
descriptor.set_buffer1(address);
|
||||
descriptor.set_buffer1_len(pkt_len);
|
||||
/// Transmit the packet written in a buffer returned by `available`.
|
||||
pub(crate) fn transmit(&mut self, len: usize) {
|
||||
let descriptor = &mut self.descriptors[self.index];
|
||||
assert!(descriptor.available());
|
||||
|
||||
self.buffers[self.next_entry].replace(pkt);
|
||||
descriptor.set_buffer1(self.buffers[self.index].0.as_ptr());
|
||||
descriptor.set_buffer1_len(len);
|
||||
|
||||
descriptor.set_owned();
|
||||
|
||||
@ -198,36 +163,12 @@ impl<const N: usize> TDesRing<N> {
|
||||
// "Preceding reads and writes cannot be moved past subsequent writes."
|
||||
fence(Ordering::Release);
|
||||
|
||||
// Move the tail pointer (TPR) to the next descriptor
|
||||
self.next_entry = (self.next_entry + 1) % N;
|
||||
|
||||
// Move the index to the next descriptor
|
||||
self.index += 1;
|
||||
if self.index == self.descriptors.len() {
|
||||
self.index = 0
|
||||
}
|
||||
// Request the DMA engine to poll the latest tx descriptor
|
||||
unsafe { ETH.ethernet_dma().dmatpdr().modify(|w| w.0 = 1) }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn on_interrupt(&mut self) -> Result<(), Error> {
|
||||
let previous = (self.next_entry + N - 1) % N;
|
||||
let td = &self.descriptors[previous];
|
||||
|
||||
// DMB to ensure that we are reading an updated value, probably not needed at the hardware
|
||||
// level, but this is also a hint to the compiler that we're syncing on the buffer.
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
let tdes0 = td.tdes0.get();
|
||||
|
||||
if tdes0 & TXDESC_0_OWN != 0 {
|
||||
// Transmission isn't done yet, probably a receive interrupt that fired this
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Release the buffer
|
||||
self.buffers[previous].take();
|
||||
|
||||
if tdes0 & TXDESC_0_ES != 0 {
|
||||
Err(Error::TransmissionError)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,10 @@
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
use embassy_net::{Packet, PacketBox, PacketBoxExt, PacketBuf};
|
||||
use vcell::VolatileCell;
|
||||
|
||||
use crate::eth::{Packet, RX_BUFFER_SIZE, TX_BUFFER_SIZE};
|
||||
use crate::pac::ETH;
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
NoBufferAvailable,
|
||||
// TODO: Break down this error into several others
|
||||
TransmissionError,
|
||||
}
|
||||
|
||||
/// Transmit and Receive Descriptor fields
|
||||
#[allow(dead_code)]
|
||||
mod emac_consts {
|
||||
@ -41,7 +32,7 @@ use emac_consts::*;
|
||||
/// * tdes2: buffer lengths
|
||||
/// * tdes3: control and payload/frame length
|
||||
#[repr(C)]
|
||||
struct TDes {
|
||||
pub(crate) struct TDes {
|
||||
tdes0: VolatileCell<u32>,
|
||||
tdes1: VolatileCell<u32>,
|
||||
tdes2: VolatileCell<u32>,
|
||||
@ -59,41 +50,26 @@ impl TDes {
|
||||
}
|
||||
|
||||
/// Return true if this TDes is not currently owned by the DMA
|
||||
pub fn available(&self) -> bool {
|
||||
fn available(&self) -> bool {
|
||||
self.tdes3.get() & EMAC_DES3_OWN == 0
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct TDesRing<const N: usize> {
|
||||
td: [TDes; N],
|
||||
buffers: [Option<PacketBuf>; N],
|
||||
tdidx: usize,
|
||||
pub(crate) struct TDesRing<'a> {
|
||||
descriptors: &'a mut [TDes],
|
||||
buffers: &'a mut [Packet<TX_BUFFER_SIZE>],
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<const N: usize> TDesRing<N> {
|
||||
pub const fn new() -> Self {
|
||||
const TDES: TDes = TDes::new();
|
||||
const BUFFERS: Option<PacketBuf> = None;
|
||||
impl<'a> TDesRing<'a> {
|
||||
/// Initialise this TDesRing. Assume TDesRing is corrupt.
|
||||
pub fn new(descriptors: &'a mut [TDes], buffers: &'a mut [Packet<TX_BUFFER_SIZE>]) -> Self {
|
||||
assert!(descriptors.len() > 0);
|
||||
assert!(descriptors.len() == buffers.len());
|
||||
|
||||
Self {
|
||||
td: [TDES; N],
|
||||
buffers: [BUFFERS; N],
|
||||
tdidx: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialise this TDesRing. Assume TDesRing is corrupt
|
||||
///
|
||||
/// The current memory address of the buffers inside this TDesRing
|
||||
/// will be stored in the descriptors, so ensure the TDesRing is
|
||||
/// not moved after initialisation.
|
||||
pub(crate) fn init(&mut self) {
|
||||
assert!(N > 0);
|
||||
|
||||
for td in self.td.iter_mut() {
|
||||
for td in descriptors.iter_mut() {
|
||||
*td = TDes::new();
|
||||
}
|
||||
self.tdidx = 0;
|
||||
|
||||
// Initialize the pointers in the DMA engine. (There will be a memory barrier later
|
||||
// before the DMA engine is enabled.)
|
||||
@ -101,80 +77,60 @@ impl<const N: usize> TDesRing<N> {
|
||||
unsafe {
|
||||
let dma = ETH.ethernet_dma();
|
||||
|
||||
dma.dmactx_dlar().write(|w| w.0 = &self.td as *const _ as u32);
|
||||
dma.dmactx_rlr().write(|w| w.set_tdrl((N as u16) - 1));
|
||||
dma.dmactx_dtpr().write(|w| w.0 = &self.td[0] as *const _ as u32);
|
||||
dma.dmactx_dlar().write(|w| w.0 = descriptors.as_mut_ptr() as u32);
|
||||
dma.dmactx_rlr().write(|w| w.set_tdrl((descriptors.len() as u16) - 1));
|
||||
dma.dmactx_dtpr().write(|w| w.0 = 0);
|
||||
}
|
||||
|
||||
Self {
|
||||
descriptors,
|
||||
buffers,
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return true if a TDes is available for use
|
||||
pub(crate) fn available(&self) -> bool {
|
||||
self.td[self.tdidx].available()
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
self.descriptors.len()
|
||||
}
|
||||
|
||||
pub(crate) fn transmit(&mut self, pkt: PacketBuf) -> Result<(), Error> {
|
||||
if !self.available() {
|
||||
return Err(Error::NoBufferAvailable);
|
||||
/// Return the next available packet buffer for transmitting, or None
|
||||
pub(crate) fn available(&mut self) -> Option<&mut [u8]> {
|
||||
let d = &mut self.descriptors[self.index];
|
||||
if d.available() {
|
||||
Some(&mut self.buffers[self.index].0)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
let x = self.tdidx;
|
||||
let td = &mut self.td[x];
|
||||
}
|
||||
|
||||
let pkt_len = pkt.len();
|
||||
assert!(pkt_len as u32 <= EMAC_TDES2_B1L);
|
||||
let address = pkt.as_ptr() as u32;
|
||||
/// Transmit the packet written in a buffer returned by `available`.
|
||||
pub(crate) fn transmit(&mut self, len: usize) {
|
||||
let td = &mut self.descriptors[self.index];
|
||||
assert!(td.available());
|
||||
assert!(len as u32 <= EMAC_TDES2_B1L);
|
||||
|
||||
// Read format
|
||||
td.tdes0.set(address);
|
||||
td.tdes2.set(pkt_len as u32 & EMAC_TDES2_B1L | EMAC_TDES2_IOC);
|
||||
td.tdes0.set(self.buffers[self.index].0.as_ptr() as u32);
|
||||
td.tdes2.set(len as u32 & EMAC_TDES2_B1L | EMAC_TDES2_IOC);
|
||||
|
||||
// FD: Contains first buffer of packet
|
||||
// LD: Contains last buffer of packet
|
||||
// Give the DMA engine ownership
|
||||
td.tdes3.set(EMAC_DES3_FD | EMAC_DES3_LD | EMAC_DES3_OWN);
|
||||
|
||||
self.buffers[x].replace(pkt);
|
||||
|
||||
// Ensure changes to the descriptor are committed before DMA engine sees tail pointer store.
|
||||
// This will generate an DMB instruction.
|
||||
// "Preceding reads and writes cannot be moved past subsequent writes."
|
||||
fence(Ordering::Release);
|
||||
|
||||
// Move the tail pointer (TPR) to the next descriptor
|
||||
let x = (x + 1) % N;
|
||||
self.index = self.index + 1;
|
||||
if self.index == self.descriptors.len() {
|
||||
self.index = 0;
|
||||
}
|
||||
|
||||
// signal DMA it can try again.
|
||||
// NOTE(unsafe) Atomic write
|
||||
unsafe {
|
||||
ETH.ethernet_dma()
|
||||
.dmactx_dtpr()
|
||||
.write(|w| w.0 = &self.td[x] as *const _ as u32);
|
||||
}
|
||||
self.tdidx = x;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn on_interrupt(&mut self) -> Result<(), Error> {
|
||||
let previous = (self.tdidx + N - 1) % N;
|
||||
let td = &self.td[previous];
|
||||
|
||||
// DMB to ensure that we are reading an updated value, probably not needed at the hardware
|
||||
// level, but this is also a hint to the compiler that we're syncing on the buffer.
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
let tdes3 = td.tdes3.get();
|
||||
|
||||
if tdes3 & EMAC_DES3_OWN != 0 {
|
||||
// Transmission isn't done yet, probably a receive interrupt that fired this
|
||||
return Ok(());
|
||||
}
|
||||
assert!(tdes3 & EMAC_DES3_CTXT == 0);
|
||||
|
||||
// Release the buffer
|
||||
self.buffers[previous].take();
|
||||
|
||||
if tdes3 & EMAC_DES3_ES != 0 {
|
||||
Err(Error::TransmissionError)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
unsafe { ETH.ethernet_dma().dmactx_dtpr().write(|w| w.0 = 0) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -185,7 +141,7 @@ impl<const N: usize> TDesRing<N> {
|
||||
/// * rdes2:
|
||||
/// * rdes3: OWN and Status
|
||||
#[repr(C)]
|
||||
struct RDes {
|
||||
pub(crate) struct RDes {
|
||||
rdes0: VolatileCell<u32>,
|
||||
rdes1: VolatileCell<u32>,
|
||||
rdes2: VolatileCell<u32>,
|
||||
@ -204,7 +160,7 @@ impl RDes {
|
||||
|
||||
/// Return true if this RDes is acceptable to us
|
||||
#[inline(always)]
|
||||
pub fn valid(&self) -> bool {
|
||||
fn valid(&self) -> bool {
|
||||
// Write-back descriptor is valid if:
|
||||
//
|
||||
// Contains first buffer of packet AND contains last buf of
|
||||
@ -215,177 +171,96 @@ impl RDes {
|
||||
|
||||
/// Return true if this RDes is not currently owned by the DMA
|
||||
#[inline(always)]
|
||||
pub fn available(&self) -> bool {
|
||||
fn available(&self) -> bool {
|
||||
self.rdes3.get() & EMAC_DES3_OWN == 0 // Owned by us
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_ready(&mut self, buf_addr: u32) {
|
||||
self.rdes0.set(buf_addr);
|
||||
fn set_ready(&mut self, buf: *mut u8) {
|
||||
self.rdes0.set(buf as u32);
|
||||
self.rdes3.set(EMAC_RDES3_BUF1V | EMAC_RDES3_IOC | EMAC_DES3_OWN);
|
||||
}
|
||||
}
|
||||
|
||||
/// Rx ring of descriptors and packets
|
||||
///
|
||||
/// This ring has three major locations that work in lock-step. The DMA will never write to the tail
|
||||
/// index, so the `read_index` must never pass the tail index. The `next_tail_index` is always 1
|
||||
/// slot ahead of the real tail index, and it must never pass the `read_index` or it could overwrite
|
||||
/// a packet still to be passed to the application.
|
||||
///
|
||||
/// nt can't pass r (no alloc)
|
||||
/// +---+---+---+---+ Read ok +---+---+---+---+ No Read +---+---+---+---+
|
||||
/// | | | | | ------------> | | | | | ------------> | | | | |
|
||||
/// +---+---+---+---+ Allocation ok +---+---+---+---+ +---+---+---+---+
|
||||
/// ^ ^t ^t ^ ^t ^
|
||||
/// |r |r |r
|
||||
/// |nt |nt |nt
|
||||
///
|
||||
///
|
||||
/// +---+---+---+---+ Read ok +---+---+---+---+ Can't read +---+---+---+---+
|
||||
/// | | | | | ------------> | | | | | ------------> | | | | |
|
||||
/// +---+---+---+---+ Allocation fail +---+---+---+---+ Allocation ok +---+---+---+---+
|
||||
/// ^ ^t ^ ^t ^ ^ ^ ^t
|
||||
/// |r | |r | | |r
|
||||
/// |nt |nt |nt
|
||||
///
|
||||
pub(crate) struct RDesRing<const N: usize> {
|
||||
rd: [RDes; N],
|
||||
buffers: [Option<PacketBox>; N],
|
||||
read_idx: usize,
|
||||
next_tail_idx: usize,
|
||||
pub(crate) struct RDesRing<'a> {
|
||||
descriptors: &'a mut [RDes],
|
||||
buffers: &'a mut [Packet<RX_BUFFER_SIZE>],
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<const N: usize> RDesRing<N> {
|
||||
pub const fn new() -> Self {
|
||||
const RDES: RDes = RDes::new();
|
||||
const BUFFERS: Option<PacketBox> = None;
|
||||
impl<'a> RDesRing<'a> {
|
||||
pub(crate) fn new(descriptors: &'a mut [RDes], buffers: &'a mut [Packet<RX_BUFFER_SIZE>]) -> Self {
|
||||
assert!(descriptors.len() > 1);
|
||||
assert!(descriptors.len() == buffers.len());
|
||||
|
||||
Self {
|
||||
rd: [RDES; N],
|
||||
buffers: [BUFFERS; N],
|
||||
read_idx: 0,
|
||||
next_tail_idx: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn init(&mut self) {
|
||||
assert!(N > 1);
|
||||
|
||||
for desc in self.rd.iter_mut() {
|
||||
for (i, desc) in descriptors.iter_mut().enumerate() {
|
||||
*desc = RDes::new();
|
||||
desc.set_ready(buffers[i].0.as_mut_ptr());
|
||||
}
|
||||
|
||||
let mut last_index = 0;
|
||||
for (index, buf) in self.buffers.iter_mut().enumerate() {
|
||||
let pkt = match PacketBox::new(Packet::new()) {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
if index == 0 {
|
||||
panic!("Could not allocate at least one buffer for Ethernet receiving");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
let addr = pkt.as_ptr() as u32;
|
||||
*buf = Some(pkt);
|
||||
self.rd[index].set_ready(addr);
|
||||
last_index = index;
|
||||
}
|
||||
self.next_tail_idx = (last_index + 1) % N;
|
||||
|
||||
unsafe {
|
||||
let dma = ETH.ethernet_dma();
|
||||
|
||||
dma.dmacrx_dlar().write(|w| w.0 = self.rd.as_ptr() as u32);
|
||||
dma.dmacrx_rlr().write(|w| w.set_rdrl((N as u16) - 1));
|
||||
dma.dmacrx_dlar().write(|w| w.0 = descriptors.as_mut_ptr() as u32);
|
||||
dma.dmacrx_rlr().write(|w| w.set_rdrl((descriptors.len() as u16) - 1));
|
||||
dma.dmacrx_dtpr().write(|w| w.0 = 0);
|
||||
}
|
||||
|
||||
// We manage to allocate all buffers, set the index to the last one, that means
|
||||
// that the DMA won't consider the last one as ready, because it (unfortunately)
|
||||
// stops at the tail ptr and wraps at the end of the ring, which means that we
|
||||
// can't tell it to stop after the last buffer.
|
||||
let tail_ptr = &self.rd[last_index] as *const _ as u32;
|
||||
fence(Ordering::Release);
|
||||
|
||||
dma.dmacrx_dtpr().write(|w| w.0 = tail_ptr);
|
||||
Self {
|
||||
descriptors,
|
||||
buffers,
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn on_interrupt(&mut self) {
|
||||
// XXX: Do we need to do anything here ? Maybe we should try to advance the tail ptr, but it
|
||||
// would soon hit the read ptr anyway, and we will wake smoltcp's stack on the interrupt
|
||||
// which should try to pop a packet...
|
||||
}
|
||||
|
||||
pub(crate) fn pop_packet(&mut self) -> Option<PacketBuf> {
|
||||
/// Get a received packet if any, or None.
|
||||
pub(crate) fn available(&mut self) -> Option<&mut [u8]> {
|
||||
// Not sure if the contents of the write buffer on the M7 can affects reads, so we are using
|
||||
// a DMB here just in case, it also serves as a hint to the compiler that we're syncing the
|
||||
// buffer (I think .-.)
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
let read_available = self.rd[self.read_idx].available();
|
||||
let tail_index = (self.next_tail_idx + N - 1) % N;
|
||||
|
||||
let pkt = if read_available && self.read_idx != tail_index {
|
||||
let pkt = self.buffers[self.read_idx].take();
|
||||
let len = (self.rd[self.read_idx].rdes3.get() & EMAC_RDES3_PKTLEN) as usize;
|
||||
|
||||
assert!(pkt.is_some());
|
||||
let valid = self.rd[self.read_idx].valid();
|
||||
|
||||
self.read_idx = (self.read_idx + 1) % N;
|
||||
if valid {
|
||||
pkt.map(|p| p.slice(0..len))
|
||||
} else {
|
||||
None
|
||||
// We might have to process many packets, in case some have been rx'd but are invalid.
|
||||
loop {
|
||||
let descriptor = &mut self.descriptors[self.index];
|
||||
if !descriptor.available() {
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Try to advance the tail_idx
|
||||
if self.next_tail_idx != self.read_idx {
|
||||
match PacketBox::new(Packet::new()) {
|
||||
Some(b) => {
|
||||
let addr = b.as_ptr() as u32;
|
||||
self.buffers[self.next_tail_idx].replace(b);
|
||||
self.rd[self.next_tail_idx].set_ready(addr);
|
||||
|
||||
// "Preceding reads and writes cannot be moved past subsequent writes."
|
||||
fence(Ordering::Release);
|
||||
|
||||
// NOTE(unsafe) atomic write
|
||||
unsafe {
|
||||
ETH.ethernet_dma()
|
||||
.dmacrx_dtpr()
|
||||
.write(|w| w.0 = &self.rd[self.next_tail_idx] as *const _ as u32);
|
||||
}
|
||||
|
||||
self.next_tail_idx = (self.next_tail_idx + 1) % N;
|
||||
}
|
||||
None => {}
|
||||
// If packet is invalid, pop it and try again.
|
||||
if !descriptor.valid() {
|
||||
warn!("invalid packet: {:08x}", descriptor.rdes0.get());
|
||||
self.pop_packet();
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
pkt
|
||||
|
||||
let descriptor = &mut self.descriptors[self.index];
|
||||
let len = (descriptor.rdes3.get() & EMAC_RDES3_PKTLEN) as usize;
|
||||
return Some(&mut self.buffers[self.index].0[..len]);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DescriptorRing<const T: usize, const R: usize> {
|
||||
pub(crate) tx: TDesRing<T>,
|
||||
pub(crate) rx: RDesRing<R>,
|
||||
}
|
||||
/// Pop the packet previously returned by `available`.
|
||||
pub(crate) fn pop_packet(&mut self) {
|
||||
let descriptor = &mut self.descriptors[self.index];
|
||||
assert!(descriptor.available());
|
||||
|
||||
impl<const T: usize, const R: usize> DescriptorRing<T, R> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
tx: TDesRing::new(),
|
||||
rx: RDesRing::new(),
|
||||
self.descriptors[self.index].set_ready(self.buffers[self.index].0.as_mut_ptr());
|
||||
|
||||
// "Preceding reads and writes cannot be moved past subsequent writes."
|
||||
fence(Ordering::Release);
|
||||
|
||||
// signal DMA it can try again.
|
||||
// NOTE(unsafe) Atomic write
|
||||
unsafe { ETH.ethernet_dma().dmacrx_dtpr().write(|w| w.0 = 0) }
|
||||
|
||||
// Increment index.
|
||||
self.index += 1;
|
||||
if self.index == self.descriptors.len() {
|
||||
self.index = 0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(&mut self) {
|
||||
self.tx.init();
|
||||
self.rx.init();
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +1,28 @@
|
||||
use core::marker::PhantomData;
|
||||
mod descriptors;
|
||||
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
use core::task::Waker;
|
||||
|
||||
use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
|
||||
use embassy_cortex_m::interrupt::InterruptExt;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_net::{Device, DeviceCapabilities, LinkState, PacketBuf, MTU};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing};
|
||||
use super::*;
|
||||
use crate::gpio::sealed::{AFType, Pin as _};
|
||||
use crate::gpio::{AnyPin, Speed};
|
||||
use crate::pac::{ETH, RCC, SYSCFG};
|
||||
use crate::Peripheral;
|
||||
|
||||
mod descriptors;
|
||||
use descriptors::DescriptorRing;
|
||||
const MTU: usize = 1514; // 14 Ethernet header + 1500 IP packet
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct State<'d, T: Instance, const TX: usize, const RX: usize>(StateStorage<Inner<'d, T, TX, RX>>);
|
||||
impl<'d, T: Instance, const TX: usize, const RX: usize> State<'d, T, TX, RX> {
|
||||
pub const fn new() -> Self {
|
||||
Self(StateStorage::new())
|
||||
}
|
||||
}
|
||||
pub struct Ethernet<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> {
|
||||
state: PeripheralMutex<'d, Inner<'d, T, TX, RX>>,
|
||||
pub struct Ethernet<'d, T: Instance, P: PHY> {
|
||||
_peri: PeripheralRef<'d, T>,
|
||||
pub(crate) tx: TDesRing<'d>,
|
||||
pub(crate) rx: RDesRing<'d>,
|
||||
pins: [PeripheralRef<'d, AnyPin>; 9],
|
||||
_phy: P,
|
||||
clock_range: u8,
|
||||
phy_addr: u8,
|
||||
mac_addr: [u8; 6],
|
||||
pub(crate) mac_addr: [u8; 6],
|
||||
}
|
||||
|
||||
macro_rules! config_pins {
|
||||
@ -44,10 +37,9 @@ macro_rules! config_pins {
|
||||
};
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, P, TX, RX> {
|
||||
/// safety: the returned instance is not leak-safe
|
||||
pub unsafe fn new(
|
||||
state: &'d mut State<'d, T, TX, RX>,
|
||||
impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
pub fn new<const TX: usize, const RX: usize>(
|
||||
queue: &'d mut PacketQueue<TX, RX>,
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
interrupt: impl Peripheral<P = crate::interrupt::ETH> + 'd,
|
||||
ref_clk: impl Peripheral<P = impl RefClkPin<T>> + 'd,
|
||||
@ -63,108 +55,123 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T,
|
||||
mac_addr: [u8; 6],
|
||||
phy_addr: u8,
|
||||
) -> Self {
|
||||
into_ref!(interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
|
||||
into_ref!(peri, interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
|
||||
|
||||
// Enable the necessary Clocks
|
||||
// NOTE(unsafe) We have exclusive access to the registers
|
||||
critical_section::with(|_| {
|
||||
RCC.apb4enr().modify(|w| w.set_syscfgen(true));
|
||||
RCC.ahb1enr().modify(|w| {
|
||||
w.set_eth1macen(true);
|
||||
w.set_eth1txen(true);
|
||||
w.set_eth1rxen(true);
|
||||
unsafe {
|
||||
// Enable the necessary Clocks
|
||||
// NOTE(unsafe) We have exclusive access to the registers
|
||||
critical_section::with(|_| {
|
||||
RCC.apb4enr().modify(|w| w.set_syscfgen(true));
|
||||
RCC.ahb1enr().modify(|w| {
|
||||
w.set_eth1macen(true);
|
||||
w.set_eth1txen(true);
|
||||
w.set_eth1rxen(true);
|
||||
});
|
||||
|
||||
// RMII
|
||||
SYSCFG.pmcr().modify(|w| w.set_epis(0b100));
|
||||
});
|
||||
|
||||
// RMII
|
||||
SYSCFG.pmcr().modify(|w| w.set_epis(0b100));
|
||||
});
|
||||
config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
|
||||
|
||||
config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
|
||||
// NOTE(unsafe) We have exclusive access to the registers
|
||||
let dma = ETH.ethernet_dma();
|
||||
let mac = ETH.ethernet_mac();
|
||||
let mtl = ETH.ethernet_mtl();
|
||||
|
||||
// NOTE(unsafe) We are ourselves not leak-safe.
|
||||
let state = PeripheralMutex::new(interrupt, &mut state.0, || Inner::new(peri));
|
||||
// Reset and wait
|
||||
dma.dmamr().modify(|w| w.set_swr(true));
|
||||
while dma.dmamr().read().swr() {}
|
||||
|
||||
// NOTE(unsafe) We have exclusive access to the registers
|
||||
let dma = ETH.ethernet_dma();
|
||||
let mac = ETH.ethernet_mac();
|
||||
let mtl = ETH.ethernet_mtl();
|
||||
mac.maccr().modify(|w| {
|
||||
w.set_ipg(0b000); // 96 bit times
|
||||
w.set_acs(true);
|
||||
w.set_fes(true);
|
||||
w.set_dm(true);
|
||||
// TODO: Carrier sense ? ECRSFD
|
||||
});
|
||||
|
||||
// Reset and wait
|
||||
dma.dmamr().modify(|w| w.set_swr(true));
|
||||
while dma.dmamr().read().swr() {}
|
||||
// Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core,
|
||||
// so the LR write must happen after the HR write.
|
||||
mac.maca0hr()
|
||||
.modify(|w| w.set_addrhi(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8)));
|
||||
mac.maca0lr().write(|w| {
|
||||
w.set_addrlo(
|
||||
u32::from(mac_addr[0])
|
||||
| (u32::from(mac_addr[1]) << 8)
|
||||
| (u32::from(mac_addr[2]) << 16)
|
||||
| (u32::from(mac_addr[3]) << 24),
|
||||
)
|
||||
});
|
||||
|
||||
mac.maccr().modify(|w| {
|
||||
w.set_ipg(0b000); // 96 bit times
|
||||
w.set_acs(true);
|
||||
w.set_fes(true);
|
||||
w.set_dm(true);
|
||||
// TODO: Carrier sense ? ECRSFD
|
||||
});
|
||||
mac.macqtx_fcr().modify(|w| w.set_pt(0x100));
|
||||
|
||||
// Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core,
|
||||
// so the LR write must happen after the HR write.
|
||||
mac.maca0hr()
|
||||
.modify(|w| w.set_addrhi(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8)));
|
||||
mac.maca0lr().write(|w| {
|
||||
w.set_addrlo(
|
||||
u32::from(mac_addr[0])
|
||||
| (u32::from(mac_addr[1]) << 8)
|
||||
| (u32::from(mac_addr[2]) << 16)
|
||||
| (u32::from(mac_addr[3]) << 24),
|
||||
)
|
||||
});
|
||||
// disable all MMC RX interrupts
|
||||
mac.mmc_rx_interrupt_mask().write(|w| {
|
||||
w.set_rxcrcerpim(true);
|
||||
w.set_rxalgnerpim(true);
|
||||
w.set_rxucgpim(true);
|
||||
w.set_rxlpiuscim(true);
|
||||
w.set_rxlpitrcim(true)
|
||||
});
|
||||
|
||||
mac.macqtx_fcr().modify(|w| w.set_pt(0x100));
|
||||
// disable all MMC TX interrupts
|
||||
mac.mmc_tx_interrupt_mask().write(|w| {
|
||||
w.set_txscolgpim(true);
|
||||
w.set_txmcolgpim(true);
|
||||
w.set_txgpktim(true);
|
||||
w.set_txlpiuscim(true);
|
||||
w.set_txlpitrcim(true);
|
||||
});
|
||||
|
||||
mtl.mtlrx_qomr().modify(|w| w.set_rsf(true));
|
||||
mtl.mtltx_qomr().modify(|w| w.set_tsf(true));
|
||||
mtl.mtlrx_qomr().modify(|w| w.set_rsf(true));
|
||||
mtl.mtltx_qomr().modify(|w| w.set_tsf(true));
|
||||
|
||||
dma.dmactx_cr().modify(|w| w.set_txpbl(1)); // 32 ?
|
||||
dma.dmacrx_cr().modify(|w| {
|
||||
w.set_rxpbl(1); // 32 ?
|
||||
w.set_rbsz(MTU as u16);
|
||||
});
|
||||
dma.dmactx_cr().modify(|w| w.set_txpbl(1)); // 32 ?
|
||||
dma.dmacrx_cr().modify(|w| {
|
||||
w.set_rxpbl(1); // 32 ?
|
||||
w.set_rbsz(MTU as u16);
|
||||
});
|
||||
|
||||
// NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called
|
||||
let hclk = crate::rcc::get_freqs().ahb1;
|
||||
let hclk_mhz = hclk.0 / 1_000_000;
|
||||
// NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called
|
||||
let hclk = crate::rcc::get_freqs().ahb1;
|
||||
let hclk_mhz = hclk.0 / 1_000_000;
|
||||
|
||||
// Set the MDC clock frequency in the range 1MHz - 2.5MHz
|
||||
let clock_range = match hclk_mhz {
|
||||
0..=34 => 2, // Divide by 16
|
||||
35..=59 => 3, // Divide by 26
|
||||
60..=99 => 0, // Divide by 42
|
||||
100..=149 => 1, // Divide by 62
|
||||
150..=249 => 4, // Divide by 102
|
||||
250..=310 => 5, // Divide by 124
|
||||
_ => {
|
||||
panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider")
|
||||
}
|
||||
};
|
||||
// Set the MDC clock frequency in the range 1MHz - 2.5MHz
|
||||
let clock_range = match hclk_mhz {
|
||||
0..=34 => 2, // Divide by 16
|
||||
35..=59 => 3, // Divide by 26
|
||||
60..=99 => 0, // Divide by 42
|
||||
100..=149 => 1, // Divide by 62
|
||||
150..=249 => 4, // Divide by 102
|
||||
250..=310 => 5, // Divide by 124
|
||||
_ => {
|
||||
panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider")
|
||||
}
|
||||
};
|
||||
|
||||
let pins = [
|
||||
ref_clk.map_into(),
|
||||
mdio.map_into(),
|
||||
mdc.map_into(),
|
||||
crs.map_into(),
|
||||
rx_d0.map_into(),
|
||||
rx_d1.map_into(),
|
||||
tx_d0.map_into(),
|
||||
tx_d1.map_into(),
|
||||
tx_en.map_into(),
|
||||
];
|
||||
let pins = [
|
||||
ref_clk.map_into(),
|
||||
mdio.map_into(),
|
||||
mdc.map_into(),
|
||||
crs.map_into(),
|
||||
rx_d0.map_into(),
|
||||
rx_d1.map_into(),
|
||||
tx_d0.map_into(),
|
||||
tx_d1.map_into(),
|
||||
tx_en.map_into(),
|
||||
];
|
||||
|
||||
let mut this = Self {
|
||||
state,
|
||||
pins,
|
||||
_phy: phy,
|
||||
clock_range,
|
||||
phy_addr,
|
||||
mac_addr,
|
||||
};
|
||||
|
||||
this.state.with(|s| {
|
||||
s.desc_ring.init();
|
||||
let mut this = Self {
|
||||
_peri: peri,
|
||||
tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf),
|
||||
rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf),
|
||||
pins,
|
||||
_phy: phy,
|
||||
clock_range,
|
||||
phy_addr,
|
||||
mac_addr,
|
||||
};
|
||||
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
@ -187,17 +194,37 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T,
|
||||
w.set_rie(true);
|
||||
w.set_tie(true);
|
||||
});
|
||||
});
|
||||
P::phy_reset(&mut this);
|
||||
P::phy_init(&mut this);
|
||||
|
||||
this
|
||||
P::phy_reset(&mut this);
|
||||
P::phy_init(&mut this);
|
||||
|
||||
interrupt.set_handler(Self::on_interrupt);
|
||||
interrupt.enable();
|
||||
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
fn on_interrupt(_cx: *mut ()) {
|
||||
WAKER.wake();
|
||||
|
||||
// TODO: Check and clear more flags
|
||||
unsafe {
|
||||
let dma = ETH.ethernet_dma();
|
||||
|
||||
dma.dmacsr().modify(|w| {
|
||||
w.set_ti(true);
|
||||
w.set_ri(true);
|
||||
w.set_nis(true);
|
||||
});
|
||||
// Delay two peripheral's clock
|
||||
dma.dmacsr().read();
|
||||
dma.dmacsr().read();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationManagement
|
||||
for Ethernet<'d, T, P, TX, RX>
|
||||
{
|
||||
unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> {
|
||||
fn smi_read(&mut self, reg: u8) -> u16 {
|
||||
// NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self`
|
||||
unsafe {
|
||||
@ -233,44 +260,7 @@ unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationMa
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Device for Ethernet<'d, T, P, TX, RX> {
|
||||
fn is_transmit_ready(&mut self) -> bool {
|
||||
self.state.with(|s| s.desc_ring.tx.available())
|
||||
}
|
||||
|
||||
fn transmit(&mut self, pkt: PacketBuf) {
|
||||
self.state.with(|s| unwrap!(s.desc_ring.tx.transmit(pkt)));
|
||||
}
|
||||
|
||||
fn receive(&mut self) -> Option<PacketBuf> {
|
||||
self.state.with(|s| s.desc_ring.rx.pop_packet())
|
||||
}
|
||||
|
||||
fn register_waker(&mut self, waker: &Waker) {
|
||||
WAKER.register(waker);
|
||||
}
|
||||
|
||||
fn capabilities(&self) -> DeviceCapabilities {
|
||||
let mut caps = DeviceCapabilities::default();
|
||||
caps.max_transmission_unit = MTU;
|
||||
caps.max_burst_size = Some(TX.min(RX));
|
||||
caps
|
||||
}
|
||||
|
||||
fn link_state(&mut self) -> LinkState {
|
||||
if P::poll_link(self) {
|
||||
LinkState::Up
|
||||
} else {
|
||||
LinkState::Down
|
||||
}
|
||||
}
|
||||
|
||||
fn ethernet_address(&self) -> [u8; 6] {
|
||||
self.mac_addr
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop for Ethernet<'d, T, P, TX, RX> {
|
||||
impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> {
|
||||
fn drop(&mut self) {
|
||||
// NOTE(unsafe) We have `&mut self` and the interrupt doesn't use this registers
|
||||
unsafe {
|
||||
@ -307,46 +297,3 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop for Etherne
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
struct Inner<'d, T: Instance, const TX: usize, const RX: usize> {
|
||||
_peri: PhantomData<&'d mut T>,
|
||||
desc_ring: DescriptorRing<TX, RX>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const TX: usize, const RX: usize> Inner<'d, T, TX, RX> {
|
||||
pub fn new(_peri: impl Peripheral<P = T> + 'd) -> Self {
|
||||
Self {
|
||||
_peri: PhantomData,
|
||||
desc_ring: DescriptorRing::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const TX: usize, const RX: usize> PeripheralState for Inner<'d, T, TX, RX> {
|
||||
type Interrupt = crate::interrupt::ETH;
|
||||
|
||||
fn on_interrupt(&mut self) {
|
||||
unwrap!(self.desc_ring.tx.on_interrupt());
|
||||
self.desc_ring.rx.on_interrupt();
|
||||
|
||||
WAKER.wake();
|
||||
|
||||
// TODO: Check and clear more flags
|
||||
unsafe {
|
||||
let dma = ETH.ethernet_dma();
|
||||
|
||||
dma.dmacsr().modify(|w| {
|
||||
w.set_ti(true);
|
||||
w.set_ri(true);
|
||||
w.set_nis(true);
|
||||
});
|
||||
// Delay two peripheral's clock
|
||||
dma.dmacsr().read();
|
||||
dma.dmacsr().read();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
|
@ -25,11 +25,11 @@ fn cpu_regs() -> pac::exti::Exti {
|
||||
EXTI
|
||||
}
|
||||
|
||||
#[cfg(not(any(exti_g0, exti_l5, gpio_v1, exti_u5)))]
|
||||
#[cfg(not(any(exti_c0, exti_g0, exti_l5, gpio_v1, exti_u5)))]
|
||||
fn exticr_regs() -> pac::syscfg::Syscfg {
|
||||
pac::SYSCFG
|
||||
}
|
||||
#[cfg(any(exti_g0, exti_l5, exti_u5))]
|
||||
#[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))]
|
||||
fn exticr_regs() -> pac::exti::Exti {
|
||||
EXTI
|
||||
}
|
||||
@ -39,9 +39,9 @@ fn exticr_regs() -> pac::afio::Afio {
|
||||
}
|
||||
|
||||
pub unsafe fn on_irq() {
|
||||
#[cfg(not(any(exti_g0, exti_l5, exti_u5)))]
|
||||
#[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))]
|
||||
let bits = EXTI.pr(0).read().0;
|
||||
#[cfg(any(exti_g0, exti_l5, exti_u5))]
|
||||
#[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))]
|
||||
let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0;
|
||||
|
||||
// Mask all the channels that fired.
|
||||
@ -53,9 +53,9 @@ pub unsafe fn on_irq() {
|
||||
}
|
||||
|
||||
// Clear pending
|
||||
#[cfg(not(any(exti_g0, exti_l5, exti_u5)))]
|
||||
#[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))]
|
||||
EXTI.pr(0).write_value(Lines(bits));
|
||||
#[cfg(any(exti_g0, exti_l5, exti_u5))]
|
||||
#[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))]
|
||||
{
|
||||
EXTI.rpr(0).write_value(Lines(bits));
|
||||
EXTI.fpr(0).write_value(Lines(bits));
|
||||
@ -167,39 +167,33 @@ mod eh1 {
|
||||
}
|
||||
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
|
||||
mod eha {
|
||||
use futures::FutureExt;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for ExtiInput<'d, T> {
|
||||
type WaitForHighFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> {
|
||||
self.wait_for_high().map(Ok)
|
||||
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_high().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
type WaitForLowFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> {
|
||||
self.wait_for_low().map(Ok)
|
||||
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_low().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
type WaitForRisingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> {
|
||||
self.wait_for_rising_edge().map(Ok)
|
||||
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_rising_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
type WaitForFallingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> {
|
||||
self.wait_for_falling_edge().map(Ok)
|
||||
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_falling_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
type WaitForAnyEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> {
|
||||
self.wait_for_any_edge().map(Ok)
|
||||
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_any_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -218,9 +212,9 @@ impl<'a> ExtiInputFuture<'a> {
|
||||
EXTI.ftsr(0).modify(|w| w.set_line(pin, falling));
|
||||
|
||||
// clear pending bit
|
||||
#[cfg(not(any(exti_g0, exti_l5, exti_u5)))]
|
||||
#[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))]
|
||||
EXTI.pr(0).write(|w| w.set_line(pin, true));
|
||||
#[cfg(any(exti_g0, exti_l5, exti_u5))]
|
||||
#[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))]
|
||||
{
|
||||
EXTI.rpr(0).write(|w| w.set_line(pin, true));
|
||||
EXTI.fpr(0).write(|w| w.set_line(pin, true));
|
||||
|
@ -1,7 +1,6 @@
|
||||
use core::convert::TryInto;
|
||||
use core::ptr::write_volatile;
|
||||
|
||||
use atomic_polyfill::{fence, Ordering};
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
use super::{ERASE_SIZE, FLASH_BASE, FLASH_SIZE};
|
||||
use crate::flash::Error;
|
||||
|
@ -1,7 +1,6 @@
|
||||
use core::convert::TryInto;
|
||||
use core::ptr::write_volatile;
|
||||
|
||||
use atomic_polyfill::{fence, Ordering};
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
use crate::flash::Error;
|
||||
use crate::pac;
|
||||
|
@ -39,6 +39,10 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error
|
||||
w.set_psize(2); // 32 bits at once
|
||||
});
|
||||
|
||||
cortex_m::asm::isb();
|
||||
cortex_m::asm::dsb();
|
||||
core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
|
||||
|
||||
let ret = {
|
||||
let mut ret: Result<(), Error> = Ok(());
|
||||
let mut offset = offset;
|
||||
@ -64,6 +68,10 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error
|
||||
|
||||
bank.cr().write(|w| w.set_pg(false));
|
||||
|
||||
cortex_m::asm::isb();
|
||||
cortex_m::asm::dsb();
|
||||
core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
@ -71,24 +79,19 @@ pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
|
||||
let from = from - super::FLASH_BASE as u32;
|
||||
let to = to - super::FLASH_BASE as u32;
|
||||
|
||||
let bank_size = (super::FLASH_SIZE / 2) as u32;
|
||||
|
||||
let (bank, start, end) = if to <= bank_size {
|
||||
let (start, end) = if to <= super::FLASH_SIZE as u32 {
|
||||
let start_sector = from / super::ERASE_SIZE as u32;
|
||||
let end_sector = to / super::ERASE_SIZE as u32;
|
||||
(0, start_sector, end_sector)
|
||||
} else if from >= SECOND_BANK_OFFSET as u32 && to <= (SECOND_BANK_OFFSET as u32 + bank_size) {
|
||||
let start_sector = (from - SECOND_BANK_OFFSET as u32) / super::ERASE_SIZE as u32;
|
||||
let end_sector = (to - SECOND_BANK_OFFSET as u32) / super::ERASE_SIZE as u32;
|
||||
(1, start_sector, end_sector)
|
||||
(start_sector, end_sector)
|
||||
} else {
|
||||
error!("Attempting to write outside of defined sectors");
|
||||
error!("Attempting to write outside of defined sectors {:x} {:x}", from, to);
|
||||
return Err(Error::Unaligned);
|
||||
};
|
||||
|
||||
trace!("Erasing bank {}, sectors from {} to {}", bank, start, end);
|
||||
trace!("Erasing sectors from {} to {}", start, end);
|
||||
for sector in start..end {
|
||||
let ret = erase_sector(pac::FLASH.bank(bank), sector as u8);
|
||||
let bank = if sector >= 8 { 1 } else { 0 };
|
||||
let ret = erase_sector(pac::FLASH.bank(bank), (sector % 8) as u8);
|
||||
if ret.is_err() {
|
||||
return ret;
|
||||
}
|
||||
|
@ -6,9 +6,6 @@ use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::{Pull, Speed};
|
||||
use crate::Peripheral;
|
||||
|
||||
mod pins;
|
||||
pub use pins::*;
|
||||
|
||||
pub struct Fmc<'d, T: Instance> {
|
||||
peri: PhantomData<&'d mut T>,
|
||||
}
|
||||
@ -19,7 +16,7 @@ unsafe impl<'d, T> stm32_fmc::FmcPeripheral for Fmc<'d, T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
const REGISTERS: *const () = crate::pac::FMC.0 as *const _;
|
||||
const REGISTERS: *const () = T::REGS.0 as *const _;
|
||||
|
||||
fn enable(&mut self) {
|
||||
<T as crate::rcc::sealed::RccPeripheral>::enable();
|
||||
@ -27,9 +24,13 @@ where
|
||||
}
|
||||
|
||||
fn memory_controller_enable(&mut self) {
|
||||
// The FMCEN bit of the FMC_BCR2..4 registers is don’t
|
||||
// care. It is only enabled through the FMC_BCR1 register.
|
||||
unsafe { T::regs().bcr1().modify(|r| r.set_fmcen(true)) };
|
||||
// fmc v1 and v2 does not have the fmcen bit
|
||||
// fsmc v1, v2 and v3 does not have the fmcen bit
|
||||
// This is a "not" because it is expected that all future versions have this bit
|
||||
#[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v1x3, fsmc_v2x3, fsmc_v3x1)))]
|
||||
unsafe {
|
||||
T::REGS.bcr1().modify(|r| r.set_fmcen(true))
|
||||
};
|
||||
}
|
||||
|
||||
fn source_clock_hz(&self) -> u32 {
|
||||
@ -107,6 +108,24 @@ impl<'d, T: Instance> Fmc<'d, T> {
|
||||
]
|
||||
));
|
||||
|
||||
fmc_sdram_constructor!(sdram_a12bits_d16bits_4banks_bank2: (
|
||||
bank: stm32_fmc::SdramTargetBank::Bank2,
|
||||
addr: [
|
||||
(a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin)
|
||||
],
|
||||
ba: [(ba0: BA0Pin), (ba1: BA1Pin)],
|
||||
d: [
|
||||
(d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin),
|
||||
(d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin)
|
||||
],
|
||||
nbl: [
|
||||
(nbl0: NBL0Pin), (nbl1: NBL1Pin)
|
||||
],
|
||||
ctrl: [
|
||||
(sdcke: SDCKE1Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE1Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin)
|
||||
]
|
||||
));
|
||||
|
||||
fmc_sdram_constructor!(sdram_a12bits_d32bits_4banks_bank2: (
|
||||
bank: stm32_fmc::SdramTargetBank::Bank2,
|
||||
addr: [
|
||||
@ -128,13 +147,130 @@ impl<'d, T: Instance> Fmc<'d, T> {
|
||||
));
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait Instance: crate::rcc::sealed::RccPeripheral {
|
||||
const REGS: crate::pac::fmc::Fmc;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance: sealed::Instance + 'static {}
|
||||
|
||||
foreach_peripheral!(
|
||||
(fmc, $inst:ident) => {
|
||||
impl crate::fmc::sealed::Instance for crate::peripherals::$inst {
|
||||
fn regs() -> stm32_metapac::fmc::Fmc {
|
||||
crate::pac::$inst
|
||||
}
|
||||
const REGS: crate::pac::fmc::Fmc = crate::pac::$inst;
|
||||
}
|
||||
impl crate::fmc::Instance for crate::peripherals::$inst {}
|
||||
};
|
||||
);
|
||||
|
||||
pin_trait!(SDNWEPin, Instance);
|
||||
pin_trait!(SDNCASPin, Instance);
|
||||
pin_trait!(SDNRASPin, Instance);
|
||||
|
||||
pin_trait!(SDNE0Pin, Instance);
|
||||
pin_trait!(SDNE1Pin, Instance);
|
||||
|
||||
pin_trait!(SDCKE0Pin, Instance);
|
||||
pin_trait!(SDCKE1Pin, Instance);
|
||||
|
||||
pin_trait!(SDCLKPin, Instance);
|
||||
|
||||
pin_trait!(NBL0Pin, Instance);
|
||||
pin_trait!(NBL1Pin, Instance);
|
||||
pin_trait!(NBL2Pin, Instance);
|
||||
pin_trait!(NBL3Pin, Instance);
|
||||
|
||||
pin_trait!(INTPin, Instance);
|
||||
pin_trait!(NLPin, Instance);
|
||||
pin_trait!(NWaitPin, Instance);
|
||||
|
||||
pin_trait!(NE1Pin, Instance);
|
||||
pin_trait!(NE2Pin, Instance);
|
||||
pin_trait!(NE3Pin, Instance);
|
||||
pin_trait!(NE4Pin, Instance);
|
||||
|
||||
pin_trait!(NCEPin, Instance);
|
||||
pin_trait!(NOEPin, Instance);
|
||||
pin_trait!(NWEPin, Instance);
|
||||
pin_trait!(ClkPin, Instance);
|
||||
|
||||
pin_trait!(BA0Pin, Instance);
|
||||
pin_trait!(BA1Pin, Instance);
|
||||
|
||||
pin_trait!(D0Pin, Instance);
|
||||
pin_trait!(D1Pin, Instance);
|
||||
pin_trait!(D2Pin, Instance);
|
||||
pin_trait!(D3Pin, Instance);
|
||||
pin_trait!(D4Pin, Instance);
|
||||
pin_trait!(D5Pin, Instance);
|
||||
pin_trait!(D6Pin, Instance);
|
||||
pin_trait!(D7Pin, Instance);
|
||||
pin_trait!(D8Pin, Instance);
|
||||
pin_trait!(D9Pin, Instance);
|
||||
pin_trait!(D10Pin, Instance);
|
||||
pin_trait!(D11Pin, Instance);
|
||||
pin_trait!(D12Pin, Instance);
|
||||
pin_trait!(D13Pin, Instance);
|
||||
pin_trait!(D14Pin, Instance);
|
||||
pin_trait!(D15Pin, Instance);
|
||||
pin_trait!(D16Pin, Instance);
|
||||
pin_trait!(D17Pin, Instance);
|
||||
pin_trait!(D18Pin, Instance);
|
||||
pin_trait!(D19Pin, Instance);
|
||||
pin_trait!(D20Pin, Instance);
|
||||
pin_trait!(D21Pin, Instance);
|
||||
pin_trait!(D22Pin, Instance);
|
||||
pin_trait!(D23Pin, Instance);
|
||||
pin_trait!(D24Pin, Instance);
|
||||
pin_trait!(D25Pin, Instance);
|
||||
pin_trait!(D26Pin, Instance);
|
||||
pin_trait!(D27Pin, Instance);
|
||||
pin_trait!(D28Pin, Instance);
|
||||
pin_trait!(D29Pin, Instance);
|
||||
pin_trait!(D30Pin, Instance);
|
||||
pin_trait!(D31Pin, Instance);
|
||||
|
||||
pin_trait!(DA0Pin, Instance);
|
||||
pin_trait!(DA1Pin, Instance);
|
||||
pin_trait!(DA2Pin, Instance);
|
||||
pin_trait!(DA3Pin, Instance);
|
||||
pin_trait!(DA4Pin, Instance);
|
||||
pin_trait!(DA5Pin, Instance);
|
||||
pin_trait!(DA6Pin, Instance);
|
||||
pin_trait!(DA7Pin, Instance);
|
||||
pin_trait!(DA8Pin, Instance);
|
||||
pin_trait!(DA9Pin, Instance);
|
||||
pin_trait!(DA10Pin, Instance);
|
||||
pin_trait!(DA11Pin, Instance);
|
||||
pin_trait!(DA12Pin, Instance);
|
||||
pin_trait!(DA13Pin, Instance);
|
||||
pin_trait!(DA14Pin, Instance);
|
||||
pin_trait!(DA15Pin, Instance);
|
||||
|
||||
pin_trait!(A0Pin, Instance);
|
||||
pin_trait!(A1Pin, Instance);
|
||||
pin_trait!(A2Pin, Instance);
|
||||
pin_trait!(A3Pin, Instance);
|
||||
pin_trait!(A4Pin, Instance);
|
||||
pin_trait!(A5Pin, Instance);
|
||||
pin_trait!(A6Pin, Instance);
|
||||
pin_trait!(A7Pin, Instance);
|
||||
pin_trait!(A8Pin, Instance);
|
||||
pin_trait!(A9Pin, Instance);
|
||||
pin_trait!(A10Pin, Instance);
|
||||
pin_trait!(A11Pin, Instance);
|
||||
pin_trait!(A12Pin, Instance);
|
||||
pin_trait!(A13Pin, Instance);
|
||||
pin_trait!(A14Pin, Instance);
|
||||
pin_trait!(A15Pin, Instance);
|
||||
pin_trait!(A16Pin, Instance);
|
||||
pin_trait!(A17Pin, Instance);
|
||||
pin_trait!(A18Pin, Instance);
|
||||
pin_trait!(A19Pin, Instance);
|
||||
pin_trait!(A20Pin, Instance);
|
||||
pin_trait!(A21Pin, Instance);
|
||||
pin_trait!(A22Pin, Instance);
|
||||
pin_trait!(A23Pin, Instance);
|
||||
pin_trait!(A24Pin, Instance);
|
||||
pin_trait!(A25Pin, Instance);
|
@ -1,118 +0,0 @@
|
||||
pub(crate) mod sealed {
|
||||
pub trait Instance: crate::rcc::sealed::RccPeripheral {
|
||||
fn regs() -> crate::pac::fmc::Fmc;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance: sealed::Instance + 'static {}
|
||||
|
||||
pin_trait!(SDNWEPin, Instance);
|
||||
pin_trait!(SDNCASPin, Instance);
|
||||
pin_trait!(SDNRASPin, Instance);
|
||||
|
||||
pin_trait!(SDNE0Pin, Instance);
|
||||
pin_trait!(SDNE1Pin, Instance);
|
||||
|
||||
pin_trait!(SDCKE0Pin, Instance);
|
||||
pin_trait!(SDCKE1Pin, Instance);
|
||||
|
||||
pin_trait!(SDCLKPin, Instance);
|
||||
|
||||
pin_trait!(NBL0Pin, Instance);
|
||||
pin_trait!(NBL1Pin, Instance);
|
||||
pin_trait!(NBL2Pin, Instance);
|
||||
pin_trait!(NBL3Pin, Instance);
|
||||
|
||||
pin_trait!(INTPin, Instance);
|
||||
pin_trait!(NLPin, Instance);
|
||||
pin_trait!(NWaitPin, Instance);
|
||||
|
||||
pin_trait!(NE1Pin, Instance);
|
||||
pin_trait!(NE2Pin, Instance);
|
||||
pin_trait!(NE3Pin, Instance);
|
||||
pin_trait!(NE4Pin, Instance);
|
||||
|
||||
pin_trait!(NCEPin, Instance);
|
||||
pin_trait!(NOEPin, Instance);
|
||||
pin_trait!(NWEPin, Instance);
|
||||
pin_trait!(ClkPin, Instance);
|
||||
|
||||
pin_trait!(BA0Pin, Instance);
|
||||
pin_trait!(BA1Pin, Instance);
|
||||
|
||||
pin_trait!(D0Pin, Instance);
|
||||
pin_trait!(D1Pin, Instance);
|
||||
pin_trait!(D2Pin, Instance);
|
||||
pin_trait!(D3Pin, Instance);
|
||||
pin_trait!(D4Pin, Instance);
|
||||
pin_trait!(D5Pin, Instance);
|
||||
pin_trait!(D6Pin, Instance);
|
||||
pin_trait!(D7Pin, Instance);
|
||||
pin_trait!(D8Pin, Instance);
|
||||
pin_trait!(D9Pin, Instance);
|
||||
pin_trait!(D10Pin, Instance);
|
||||
pin_trait!(D11Pin, Instance);
|
||||
pin_trait!(D12Pin, Instance);
|
||||
pin_trait!(D13Pin, Instance);
|
||||
pin_trait!(D14Pin, Instance);
|
||||
pin_trait!(D15Pin, Instance);
|
||||
pin_trait!(D16Pin, Instance);
|
||||
pin_trait!(D17Pin, Instance);
|
||||
pin_trait!(D18Pin, Instance);
|
||||
pin_trait!(D19Pin, Instance);
|
||||
pin_trait!(D20Pin, Instance);
|
||||
pin_trait!(D21Pin, Instance);
|
||||
pin_trait!(D22Pin, Instance);
|
||||
pin_trait!(D23Pin, Instance);
|
||||
pin_trait!(D24Pin, Instance);
|
||||
pin_trait!(D25Pin, Instance);
|
||||
pin_trait!(D26Pin, Instance);
|
||||
pin_trait!(D27Pin, Instance);
|
||||
pin_trait!(D28Pin, Instance);
|
||||
pin_trait!(D29Pin, Instance);
|
||||
pin_trait!(D30Pin, Instance);
|
||||
pin_trait!(D31Pin, Instance);
|
||||
|
||||
pin_trait!(DA0Pin, Instance);
|
||||
pin_trait!(DA1Pin, Instance);
|
||||
pin_trait!(DA2Pin, Instance);
|
||||
pin_trait!(DA3Pin, Instance);
|
||||
pin_trait!(DA4Pin, Instance);
|
||||
pin_trait!(DA5Pin, Instance);
|
||||
pin_trait!(DA6Pin, Instance);
|
||||
pin_trait!(DA7Pin, Instance);
|
||||
pin_trait!(DA8Pin, Instance);
|
||||
pin_trait!(DA9Pin, Instance);
|
||||
pin_trait!(DA10Pin, Instance);
|
||||
pin_trait!(DA11Pin, Instance);
|
||||
pin_trait!(DA12Pin, Instance);
|
||||
pin_trait!(DA13Pin, Instance);
|
||||
pin_trait!(DA14Pin, Instance);
|
||||
pin_trait!(DA15Pin, Instance);
|
||||
|
||||
pin_trait!(A0Pin, Instance);
|
||||
pin_trait!(A1Pin, Instance);
|
||||
pin_trait!(A2Pin, Instance);
|
||||
pin_trait!(A3Pin, Instance);
|
||||
pin_trait!(A4Pin, Instance);
|
||||
pin_trait!(A5Pin, Instance);
|
||||
pin_trait!(A6Pin, Instance);
|
||||
pin_trait!(A7Pin, Instance);
|
||||
pin_trait!(A8Pin, Instance);
|
||||
pin_trait!(A9Pin, Instance);
|
||||
pin_trait!(A10Pin, Instance);
|
||||
pin_trait!(A11Pin, Instance);
|
||||
pin_trait!(A12Pin, Instance);
|
||||
pin_trait!(A13Pin, Instance);
|
||||
pin_trait!(A14Pin, Instance);
|
||||
pin_trait!(A15Pin, Instance);
|
||||
pin_trait!(A16Pin, Instance);
|
||||
pin_trait!(A17Pin, Instance);
|
||||
pin_trait!(A18Pin, Instance);
|
||||
pin_trait!(A19Pin, Instance);
|
||||
pin_trait!(A20Pin, Instance);
|
||||
pin_trait!(A21Pin, Instance);
|
||||
pin_trait!(A22Pin, Instance);
|
||||
pin_trait!(A23Pin, Instance);
|
||||
pin_trait!(A24Pin, Instance);
|
||||
pin_trait!(A25Pin, Instance);
|
@ -28,6 +28,21 @@ impl<'d, T: Pin> Flex<'d, T> {
|
||||
Self { pin }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn degrade(mut self) -> Flex<'d, AnyPin> {
|
||||
// Safety: We are about to drop the other copy of this pin, so
|
||||
// this clone is safe.
|
||||
let pin = unsafe { self.pin.clone_unchecked() };
|
||||
|
||||
// We don't want to run the destructor here, because that would
|
||||
// deconfigure the pin.
|
||||
core::mem::forget(self);
|
||||
|
||||
Flex {
|
||||
pin: pin.map_into::<AnyPin>(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Put the pin into input mode.
|
||||
#[inline]
|
||||
pub fn set_as_input(&mut self, pull: Pull) {
|
||||
@ -286,6 +301,13 @@ impl<'d, T: Pin> Input<'d, T> {
|
||||
Self { pin }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn degrade(self) -> Input<'d, AnyPin> {
|
||||
Input {
|
||||
pin: self.pin.degrade(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_high(&self) -> bool {
|
||||
self.pin.is_high()
|
||||
@ -345,6 +367,13 @@ impl<'d, T: Pin> Output<'d, T> {
|
||||
Self { pin }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn degrade(self) -> Output<'d, AnyPin> {
|
||||
Output {
|
||||
pin: self.pin.degrade(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the output as high.
|
||||
#[inline]
|
||||
pub fn set_high(&mut self) {
|
||||
@ -407,6 +436,13 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> {
|
||||
Self { pin }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn degrade(self) -> Output<'d, AnyPin> {
|
||||
Output {
|
||||
pin: self.pin.degrade(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_high(&self) -> bool {
|
||||
!self.pin.is_low()
|
||||
|
@ -7,6 +7,11 @@ use crate::interrupt::Interrupt;
|
||||
mod _version;
|
||||
pub use _version::*;
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
mod timeout;
|
||||
#[cfg(feature = "time")]
|
||||
pub use timeout::*;
|
||||
|
||||
use crate::peripherals;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
142
embassy-stm32/src/i2c/timeout.rs
Normal file
142
embassy-stm32/src/i2c/timeout.rs
Normal file
@ -0,0 +1,142 @@
|
||||
use embassy_time::{Duration, Instant};
|
||||
|
||||
use super::{Error, I2c, Instance};
|
||||
|
||||
/// An I2C wrapper, which provides `embassy-time` based timeouts for all `embedded-hal` trait methods.
|
||||
///
|
||||
/// This is useful for recovering from a shorted bus or a device stuck in a clock stretching state.
|
||||
/// A regular [I2c] would freeze until condition is removed.
|
||||
pub struct TimeoutI2c<'d, T: Instance, TXDMA, RXDMA> {
|
||||
i2c: &'d mut I2c<'d, T, TXDMA, RXDMA>,
|
||||
timeout: Duration,
|
||||
}
|
||||
|
||||
fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> {
|
||||
let deadline = Instant::now() + timeout;
|
||||
move || {
|
||||
if Instant::now() > deadline {
|
||||
Err(Error::Timeout)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, TXDMA, RXDMA> TimeoutI2c<'d, T, TXDMA, RXDMA> {
|
||||
pub fn new(i2c: &'d mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration) -> Self {
|
||||
Self { i2c, timeout }
|
||||
}
|
||||
|
||||
/// Blocking read with a custom timeout
|
||||
pub fn blocking_read_timeout(&mut self, addr: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> {
|
||||
self.i2c.blocking_read_timeout(addr, buffer, timeout_fn(timeout))
|
||||
}
|
||||
|
||||
/// Blocking read with default timeout, provided in [`TimeoutI2c::new()`]
|
||||
pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.blocking_read_timeout(addr, buffer, self.timeout)
|
||||
}
|
||||
|
||||
/// Blocking write with a custom timeout
|
||||
pub fn blocking_write_timeout(&mut self, addr: u8, bytes: &[u8], timeout: Duration) -> Result<(), Error> {
|
||||
self.i2c.blocking_write_timeout(addr, bytes, timeout_fn(timeout))
|
||||
}
|
||||
|
||||
/// Blocking write with default timeout, provided in [`TimeoutI2c::new()`]
|
||||
pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
|
||||
self.blocking_write_timeout(addr, bytes, self.timeout)
|
||||
}
|
||||
|
||||
/// Blocking write-read with a custom timeout
|
||||
pub fn blocking_write_read_timeout(
|
||||
&mut self,
|
||||
addr: u8,
|
||||
bytes: &[u8],
|
||||
buffer: &mut [u8],
|
||||
timeout: Duration,
|
||||
) -> Result<(), Error> {
|
||||
self.i2c
|
||||
.blocking_write_read_timeout(addr, bytes, buffer, timeout_fn(timeout))
|
||||
}
|
||||
|
||||
/// Blocking write-read with default timeout, provided in [`TimeoutI2c::new()`]
|
||||
pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.blocking_write_read_timeout(addr, bytes, buffer, self.timeout)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T, TXDMA, RXDMA> {
|
||||
type Error = Error;
|
||||
|
||||
fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_read(addr, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T, TXDMA, RXDMA> {
|
||||
type Error = Error;
|
||||
|
||||
fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(addr, bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T, TXDMA, RXDMA> {
|
||||
type Error = Error;
|
||||
|
||||
fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write_read(addr, bytes, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-traits")]
|
||||
mod eh1 {
|
||||
use super::*;
|
||||
|
||||
impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'d, T, TXDMA, RXDMA> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::I2c for TimeoutI2c<'d, T, TXDMA, RXDMA> {
|
||||
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_read(address, buffer)
|
||||
}
|
||||
|
||||
fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(address, buffer)
|
||||
}
|
||||
|
||||
fn write_iter<B>(&mut self, _address: u8, _bytes: B) -> Result<(), Self::Error>
|
||||
where
|
||||
B: IntoIterator<Item = u8>,
|
||||
{
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn write_iter_read<B>(&mut self, _address: u8, _bytes: B, _buffer: &mut [u8]) -> Result<(), Self::Error>
|
||||
where
|
||||
B: IntoIterator<Item = u8>,
|
||||
{
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write_read(address, wr_buffer, rd_buffer)
|
||||
}
|
||||
|
||||
fn transaction<'a>(
|
||||
&mut self,
|
||||
_address: u8,
|
||||
_operations: &mut [embedded_hal_1::i2c::Operation<'a>],
|
||||
) -> Result<(), Self::Error> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error>
|
||||
where
|
||||
O: IntoIterator<Item = embedded_hal_1::i2c::Operation<'a>>,
|
||||
{
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_embedded_hal::SetConfig;
|
||||
use embassy_hal_common::into_ref;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
|
||||
use crate::dma::NoDma;
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::Pull;
|
||||
use crate::i2c::{Error, Instance, SclPin, SdaPin};
|
||||
@ -34,19 +35,26 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct I2c<'d, T: Instance> {
|
||||
pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
#[allow(dead_code)]
|
||||
tx_dma: PeripheralRef<'d, TXDMA>,
|
||||
#[allow(dead_code)]
|
||||
rx_dma: PeripheralRef<'d, RXDMA>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> I2c<'d, T> {
|
||||
impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
pub fn new(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
|
||||
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
|
||||
_irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
tx_dma: impl Peripheral<P = TXDMA> + 'd,
|
||||
rx_dma: impl Peripheral<P = RXDMA> + 'd,
|
||||
freq: Hertz,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(scl, sda);
|
||||
into_ref!(scl, sda, tx_dma, rx_dma);
|
||||
|
||||
T::enable();
|
||||
T::reset();
|
||||
@ -99,7 +107,11 @@ impl<'d, T: Instance> I2c<'d, T> {
|
||||
});
|
||||
}
|
||||
|
||||
Self { phantom: PhantomData }
|
||||
Self {
|
||||
phantom: PhantomData,
|
||||
tx_dma,
|
||||
rx_dma,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn check_and_clear_error_flags(&self) -> Result<i2c::regs::Sr1, Error> {
|
||||
@ -141,7 +153,12 @@ impl<'d, T: Instance> I2c<'d, T> {
|
||||
Ok(sr1)
|
||||
}
|
||||
|
||||
unsafe fn write_bytes(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
|
||||
unsafe fn write_bytes(
|
||||
&mut self,
|
||||
addr: u8,
|
||||
bytes: &[u8],
|
||||
check_timeout: impl Fn() -> Result<(), Error>,
|
||||
) -> Result<(), Error> {
|
||||
// Send a START condition
|
||||
|
||||
T::regs().cr1().modify(|reg| {
|
||||
@ -149,7 +166,9 @@ impl<'d, T: Instance> I2c<'d, T> {
|
||||
});
|
||||
|
||||
// Wait until START condition was generated
|
||||
while !self.check_and_clear_error_flags()?.start() {}
|
||||
while !self.check_and_clear_error_flags()?.start() {
|
||||
check_timeout()?;
|
||||
}
|
||||
|
||||
// Also wait until signalled we're master and everything is waiting for us
|
||||
while {
|
||||
@ -157,7 +176,9 @@ impl<'d, T: Instance> I2c<'d, T> {
|
||||
|
||||
let sr2 = T::regs().sr2().read();
|
||||
!sr2.msl() && !sr2.busy()
|
||||
} {}
|
||||
} {
|
||||
check_timeout()?;
|
||||
}
|
||||
|
||||
// Set up current address, we're trying to talk to
|
||||
T::regs().dr().write(|reg| reg.set_dr(addr << 1));
|
||||
@ -165,26 +186,30 @@ impl<'d, T: Instance> I2c<'d, T> {
|
||||
// Wait until address was sent
|
||||
// Wait for the address to be acknowledged
|
||||
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
|
||||
while !self.check_and_clear_error_flags()?.addr() {}
|
||||
while !self.check_and_clear_error_flags()?.addr() {
|
||||
check_timeout()?;
|
||||
}
|
||||
|
||||
// Clear condition by reading SR2
|
||||
let _ = T::regs().sr2().read();
|
||||
|
||||
// Send bytes
|
||||
for c in bytes {
|
||||
self.send_byte(*c)?;
|
||||
self.send_byte(*c, &check_timeout)?;
|
||||
}
|
||||
|
||||
// Fallthrough is success
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn send_byte(&self, byte: u8) -> Result<(), Error> {
|
||||
unsafe fn send_byte(&self, byte: u8, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> {
|
||||
// Wait until we're ready for sending
|
||||
while {
|
||||
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
|
||||
!self.check_and_clear_error_flags()?.txe()
|
||||
} {}
|
||||
} {
|
||||
check_timeout()?;
|
||||
}
|
||||
|
||||
// Push out a byte of data
|
||||
T::regs().dr().write(|reg| reg.set_dr(byte));
|
||||
@ -193,24 +218,33 @@ impl<'d, T: Instance> I2c<'d, T> {
|
||||
while {
|
||||
// Check for any potential error conditions.
|
||||
!self.check_and_clear_error_flags()?.btf()
|
||||
} {}
|
||||
} {
|
||||
check_timeout()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn recv_byte(&self) -> Result<u8, Error> {
|
||||
unsafe fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<u8, Error> {
|
||||
while {
|
||||
// Check for any potential error conditions.
|
||||
self.check_and_clear_error_flags()?;
|
||||
|
||||
!T::regs().sr1().read().rxne()
|
||||
} {}
|
||||
} {
|
||||
check_timeout()?;
|
||||
}
|
||||
|
||||
let value = T::regs().dr().read().dr();
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
pub fn blocking_read_timeout(
|
||||
&mut self,
|
||||
addr: u8,
|
||||
buffer: &mut [u8],
|
||||
check_timeout: impl Fn() -> Result<(), Error>,
|
||||
) -> Result<(), Error> {
|
||||
if let Some((last, buffer)) = buffer.split_last_mut() {
|
||||
// Send a START condition and set ACK bit
|
||||
unsafe {
|
||||
@ -221,27 +255,33 @@ impl<'d, T: Instance> I2c<'d, T> {
|
||||
}
|
||||
|
||||
// Wait until START condition was generated
|
||||
while unsafe { !T::regs().sr1().read().start() } {}
|
||||
while unsafe { !self.check_and_clear_error_flags()?.start() } {
|
||||
check_timeout()?;
|
||||
}
|
||||
|
||||
// Also wait until signalled we're master and everything is waiting for us
|
||||
while {
|
||||
let sr2 = unsafe { T::regs().sr2().read() };
|
||||
!sr2.msl() && !sr2.busy()
|
||||
} {}
|
||||
} {
|
||||
check_timeout()?;
|
||||
}
|
||||
|
||||
// Set up current address, we're trying to talk to
|
||||
unsafe { T::regs().dr().write(|reg| reg.set_dr((addr << 1) + 1)) }
|
||||
|
||||
// Wait until address was sent
|
||||
// Wait for the address to be acknowledged
|
||||
while unsafe { !self.check_and_clear_error_flags()?.addr() } {}
|
||||
while unsafe { !self.check_and_clear_error_flags()?.addr() } {
|
||||
check_timeout()?;
|
||||
}
|
||||
|
||||
// Clear condition by reading SR2
|
||||
let _ = unsafe { T::regs().sr2().read() };
|
||||
|
||||
// Receive bytes into buffer
|
||||
for c in buffer {
|
||||
*c = unsafe { self.recv_byte()? };
|
||||
*c = unsafe { self.recv_byte(&check_timeout)? };
|
||||
}
|
||||
|
||||
// Prepare to send NACK then STOP after next byte
|
||||
@ -253,10 +293,12 @@ impl<'d, T: Instance> I2c<'d, T> {
|
||||
}
|
||||
|
||||
// Receive last byte
|
||||
*last = unsafe { self.recv_byte()? };
|
||||
*last = unsafe { self.recv_byte(&check_timeout)? };
|
||||
|
||||
// Wait for the STOP to be sent.
|
||||
while unsafe { T::regs().cr1().read().stop() } {}
|
||||
while unsafe { T::regs().cr1().read().stop() } {
|
||||
check_timeout()?;
|
||||
}
|
||||
|
||||
// Fallthrough is success
|
||||
Ok(())
|
||||
@ -265,25 +307,50 @@ impl<'d, T: Instance> I2c<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
|
||||
pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.blocking_read_timeout(addr, buffer, || Ok(()))
|
||||
}
|
||||
|
||||
pub fn blocking_write_timeout(
|
||||
&mut self,
|
||||
addr: u8,
|
||||
bytes: &[u8],
|
||||
check_timeout: impl Fn() -> Result<(), Error>,
|
||||
) -> Result<(), Error> {
|
||||
unsafe {
|
||||
self.write_bytes(addr, bytes)?;
|
||||
self.write_bytes(addr, bytes, &check_timeout)?;
|
||||
// Send a STOP condition
|
||||
T::regs().cr1().modify(|reg| reg.set_stop(true));
|
||||
// Wait for STOP condition to transmit.
|
||||
while T::regs().cr1().read().stop() {}
|
||||
while T::regs().cr1().read().stop() {
|
||||
check_timeout()?;
|
||||
}
|
||||
};
|
||||
|
||||
// Fallthrough is success
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
|
||||
unsafe { self.write_bytes(addr, bytes)? };
|
||||
self.blocking_read(addr, buffer)?;
|
||||
pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
|
||||
self.blocking_write_timeout(addr, bytes, || Ok(()))
|
||||
}
|
||||
|
||||
pub fn blocking_write_read_timeout(
|
||||
&mut self,
|
||||
addr: u8,
|
||||
bytes: &[u8],
|
||||
buffer: &mut [u8],
|
||||
check_timeout: impl Fn() -> Result<(), Error>,
|
||||
) -> Result<(), Error> {
|
||||
unsafe { self.write_bytes(addr, bytes, &check_timeout)? };
|
||||
self.blocking_read_timeout(addr, buffer, &check_timeout)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.blocking_write_read_timeout(addr, bytes, buffer, || Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> {
|
||||
|
@ -1,8 +1,8 @@
|
||||
use core::cmp;
|
||||
use core::future::poll_fn;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
use atomic_polyfill::{AtomicUsize, Ordering};
|
||||
use embassy_embedded_hal::SetConfig;
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
@ -131,7 +131,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
|
||||
if isr.tcr() || isr.tc() {
|
||||
let state = T::state();
|
||||
state.chunks_transferred.fetch_add(1, Ordering::Relaxed);
|
||||
let transferred = state.chunks_transferred.load(Ordering::Relaxed);
|
||||
state.chunks_transferred.store(transferred + 1, Ordering::Relaxed);
|
||||
state.waker.wake();
|
||||
}
|
||||
// The flag can only be cleared by writting to nbytes, we won't do that here, so disable
|
||||
@ -147,14 +148,23 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn master_read(address: u8, length: usize, stop: Stop, reload: bool, restart: bool) {
|
||||
unsafe fn master_read(
|
||||
address: u8,
|
||||
length: usize,
|
||||
stop: Stop,
|
||||
reload: bool,
|
||||
restart: bool,
|
||||
check_timeout: impl Fn() -> Result<(), Error>,
|
||||
) -> Result<(), Error> {
|
||||
assert!(length < 256);
|
||||
|
||||
if !restart {
|
||||
// Wait for any previous address sequence to end
|
||||
// automatically. This could be up to 50% of a bus
|
||||
// cycle (ie. up to 0.5/freq)
|
||||
while T::regs().cr2().read().start() {}
|
||||
while T::regs().cr2().read().start() {
|
||||
check_timeout()?;
|
||||
}
|
||||
}
|
||||
|
||||
// Set START and prepare to receive bytes into
|
||||
@ -176,15 +186,25 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
w.set_autoend(stop.autoend());
|
||||
w.set_reload(reload);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn master_write(address: u8, length: usize, stop: Stop, reload: bool) {
|
||||
unsafe fn master_write(
|
||||
address: u8,
|
||||
length: usize,
|
||||
stop: Stop,
|
||||
reload: bool,
|
||||
check_timeout: impl Fn() -> Result<(), Error>,
|
||||
) -> Result<(), Error> {
|
||||
assert!(length < 256);
|
||||
|
||||
// Wait for any previous address sequence to end
|
||||
// automatically. This could be up to 50% of a bus
|
||||
// cycle (ie. up to 0.5/freq)
|
||||
while T::regs().cr2().read().start() {}
|
||||
while T::regs().cr2().read().start() {
|
||||
check_timeout()?;
|
||||
}
|
||||
|
||||
let reload = if reload {
|
||||
i2c::vals::Reload::NOTCOMPLETED
|
||||
@ -204,12 +224,20 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
w.set_autoend(stop.autoend());
|
||||
w.set_reload(reload);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn master_continue(length: usize, reload: bool) {
|
||||
unsafe fn master_continue(
|
||||
length: usize,
|
||||
reload: bool,
|
||||
check_timeout: impl Fn() -> Result<(), Error>,
|
||||
) -> Result<(), Error> {
|
||||
assert!(length < 256 && length > 0);
|
||||
|
||||
while !T::regs().isr().read().tcr() {}
|
||||
while !T::regs().isr().read().tcr() {
|
||||
check_timeout()?;
|
||||
}
|
||||
|
||||
let reload = if reload {
|
||||
i2c::vals::Reload::NOTCOMPLETED
|
||||
@ -221,6 +249,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
w.set_nbytes(length as u8);
|
||||
w.set_reload(reload);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush_txdr(&self) {
|
||||
@ -243,7 +273,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
//}
|
||||
}
|
||||
|
||||
fn wait_txe(&self) -> Result<(), Error> {
|
||||
fn wait_txe(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> {
|
||||
loop {
|
||||
unsafe {
|
||||
let isr = T::regs().isr().read();
|
||||
@ -261,10 +291,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
return Err(Error::Nack);
|
||||
}
|
||||
}
|
||||
|
||||
check_timeout()?;
|
||||
}
|
||||
}
|
||||
|
||||
fn wait_rxne(&self) -> Result<(), Error> {
|
||||
fn wait_rxne(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> {
|
||||
loop {
|
||||
unsafe {
|
||||
let isr = T::regs().isr().read();
|
||||
@ -282,10 +314,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
return Err(Error::Nack);
|
||||
}
|
||||
}
|
||||
|
||||
check_timeout()?;
|
||||
}
|
||||
}
|
||||
|
||||
fn wait_tc(&self) -> Result<(), Error> {
|
||||
fn wait_tc(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> {
|
||||
loop {
|
||||
unsafe {
|
||||
let isr = T::regs().isr().read();
|
||||
@ -303,10 +337,18 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
return Err(Error::Nack);
|
||||
}
|
||||
}
|
||||
|
||||
check_timeout()?;
|
||||
}
|
||||
}
|
||||
|
||||
fn read_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool) -> Result<(), Error> {
|
||||
fn read_internal(
|
||||
&mut self,
|
||||
address: u8,
|
||||
buffer: &mut [u8],
|
||||
restart: bool,
|
||||
check_timeout: impl Fn() -> Result<(), Error>,
|
||||
) -> Result<(), Error> {
|
||||
let completed_chunks = buffer.len() / 255;
|
||||
let total_chunks = if completed_chunks * 255 == buffer.len() {
|
||||
completed_chunks
|
||||
@ -322,20 +364,21 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
Stop::Automatic,
|
||||
last_chunk_idx != 0,
|
||||
restart,
|
||||
);
|
||||
&check_timeout,
|
||||
)?;
|
||||
}
|
||||
|
||||
for (number, chunk) in buffer.chunks_mut(255).enumerate() {
|
||||
if number != 0 {
|
||||
// NOTE(unsafe) We have &mut self
|
||||
unsafe {
|
||||
Self::master_continue(chunk.len(), number != last_chunk_idx);
|
||||
Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?;
|
||||
}
|
||||
}
|
||||
|
||||
for byte in chunk {
|
||||
// Wait until we have received something
|
||||
self.wait_rxne()?;
|
||||
self.wait_rxne(&check_timeout)?;
|
||||
|
||||
unsafe {
|
||||
*byte = T::regs().rxdr().read().rxdata();
|
||||
@ -345,7 +388,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_internal(&mut self, address: u8, bytes: &[u8], send_stop: bool) -> Result<(), Error> {
|
||||
fn write_internal(
|
||||
&mut self,
|
||||
address: u8,
|
||||
bytes: &[u8],
|
||||
send_stop: bool,
|
||||
check_timeout: impl Fn() -> Result<(), Error>,
|
||||
) -> Result<(), Error> {
|
||||
let completed_chunks = bytes.len() / 255;
|
||||
let total_chunks = if completed_chunks * 255 == bytes.len() {
|
||||
completed_chunks
|
||||
@ -359,14 +408,20 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
// ST SAD+W
|
||||
// NOTE(unsafe) We have &mut self
|
||||
unsafe {
|
||||
Self::master_write(address, bytes.len().min(255), Stop::Software, last_chunk_idx != 0);
|
||||
Self::master_write(
|
||||
address,
|
||||
bytes.len().min(255),
|
||||
Stop::Software,
|
||||
last_chunk_idx != 0,
|
||||
&check_timeout,
|
||||
)?;
|
||||
}
|
||||
|
||||
for (number, chunk) in bytes.chunks(255).enumerate() {
|
||||
if number != 0 {
|
||||
// NOTE(unsafe) We have &mut self
|
||||
unsafe {
|
||||
Self::master_continue(chunk.len(), number != last_chunk_idx);
|
||||
Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -374,7 +429,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
// Wait until we are allowed to send data
|
||||
// (START has been ACKed or last byte when
|
||||
// through)
|
||||
self.wait_txe()?;
|
||||
self.wait_txe(&check_timeout)?;
|
||||
|
||||
unsafe {
|
||||
T::regs().txdr().write(|w| w.set_txdata(*byte));
|
||||
@ -382,7 +437,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
}
|
||||
}
|
||||
// Wait until the write finishes
|
||||
self.wait_tc()?;
|
||||
self.wait_tc(&check_timeout)?;
|
||||
|
||||
if send_stop {
|
||||
self.master_stop();
|
||||
@ -396,6 +451,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
bytes: &[u8],
|
||||
first_slice: bool,
|
||||
last_slice: bool,
|
||||
check_timeout: impl Fn() -> Result<(), Error>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
TXDMA: crate::i2c::TxDma<T>,
|
||||
@ -427,7 +483,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
state.chunks_transferred.store(0, Ordering::Relaxed);
|
||||
let mut remaining_len = total_len;
|
||||
|
||||
let _on_drop = OnDrop::new(|| {
|
||||
let on_drop = OnDrop::new(|| {
|
||||
let regs = T::regs();
|
||||
unsafe {
|
||||
regs.cr1().modify(|w| {
|
||||
@ -447,11 +503,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
total_len.min(255),
|
||||
Stop::Software,
|
||||
(total_chunks != 1) || !last_slice,
|
||||
);
|
||||
&check_timeout,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
Self::master_continue(total_len.min(255), (total_chunks != 1) || !last_slice);
|
||||
Self::master_continue(total_len.min(255), (total_chunks != 1) || !last_slice, &check_timeout)?;
|
||||
T::regs().cr1().modify(|w| w.set_tcie(true));
|
||||
}
|
||||
}
|
||||
@ -461,32 +518,43 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed);
|
||||
|
||||
if chunks_transferred == total_chunks {
|
||||
return Poll::Ready(());
|
||||
return Poll::Ready(Ok(()));
|
||||
} else if chunks_transferred != 0 {
|
||||
remaining_len = remaining_len.saturating_sub(255);
|
||||
let last_piece = (chunks_transferred + 1 == total_chunks) && last_slice;
|
||||
|
||||
// NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
|
||||
unsafe {
|
||||
Self::master_continue(remaining_len.min(255), !last_piece);
|
||||
if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) {
|
||||
return Poll::Ready(Err(e));
|
||||
}
|
||||
T::regs().cr1().modify(|w| w.set_tcie(true));
|
||||
}
|
||||
}
|
||||
Poll::Pending
|
||||
})
|
||||
.await;
|
||||
.await?;
|
||||
|
||||
dma_transfer.await;
|
||||
|
||||
if last_slice {
|
||||
// This should be done already
|
||||
self.wait_tc()?;
|
||||
self.wait_tc(&check_timeout)?;
|
||||
self.master_stop();
|
||||
}
|
||||
|
||||
drop(on_drop);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn read_dma_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool) -> Result<(), Error>
|
||||
async fn read_dma_internal(
|
||||
&mut self,
|
||||
address: u8,
|
||||
buffer: &mut [u8],
|
||||
restart: bool,
|
||||
check_timeout: impl Fn() -> Result<(), Error>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
RXDMA: crate::i2c::RxDma<T>,
|
||||
{
|
||||
@ -515,7 +583,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
state.chunks_transferred.store(0, Ordering::Relaxed);
|
||||
let mut remaining_len = total_len;
|
||||
|
||||
let _on_drop = OnDrop::new(|| {
|
||||
let on_drop = OnDrop::new(|| {
|
||||
let regs = T::regs();
|
||||
unsafe {
|
||||
regs.cr1().modify(|w| {
|
||||
@ -527,7 +595,14 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
|
||||
// NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
|
||||
unsafe {
|
||||
Self::master_read(address, total_len.min(255), Stop::Software, total_chunks != 1, restart);
|
||||
Self::master_read(
|
||||
address,
|
||||
total_len.min(255),
|
||||
Stop::Software,
|
||||
total_chunks != 1,
|
||||
restart,
|
||||
&check_timeout,
|
||||
)?;
|
||||
}
|
||||
|
||||
poll_fn(|cx| {
|
||||
@ -535,26 +610,31 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed);
|
||||
|
||||
if chunks_transferred == total_chunks {
|
||||
return Poll::Ready(());
|
||||
return Poll::Ready(Ok(()));
|
||||
} else if chunks_transferred != 0 {
|
||||
remaining_len = remaining_len.saturating_sub(255);
|
||||
let last_piece = chunks_transferred + 1 == total_chunks;
|
||||
|
||||
// NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
|
||||
unsafe {
|
||||
Self::master_continue(remaining_len.min(255), !last_piece);
|
||||
if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) {
|
||||
return Poll::Ready(Err(e));
|
||||
}
|
||||
T::regs().cr1().modify(|w| w.set_tcie(true));
|
||||
}
|
||||
}
|
||||
Poll::Pending
|
||||
})
|
||||
.await;
|
||||
.await?;
|
||||
|
||||
dma_transfer.await;
|
||||
|
||||
// This should be done already
|
||||
self.wait_tc()?;
|
||||
self.wait_tc(&check_timeout)?;
|
||||
self.master_stop();
|
||||
|
||||
drop(on_drop);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -566,9 +646,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
TXDMA: crate::i2c::TxDma<T>,
|
||||
{
|
||||
if bytes.is_empty() {
|
||||
self.write_internal(address, bytes, true)
|
||||
self.write_internal(address, bytes, true, || Ok(()))
|
||||
} else {
|
||||
self.write_dma_internal(address, bytes, true, true).await
|
||||
self.write_dma_internal(address, bytes, true, true, || Ok(())).await
|
||||
}
|
||||
}
|
||||
|
||||
@ -587,7 +667,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
let next = iter.next();
|
||||
let is_last = next.is_none();
|
||||
|
||||
self.write_dma_internal(address, c, first, is_last).await?;
|
||||
self.write_dma_internal(address, c, first, is_last, || Ok(())).await?;
|
||||
first = false;
|
||||
current = next;
|
||||
}
|
||||
@ -599,9 +679,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
RXDMA: crate::i2c::RxDma<T>,
|
||||
{
|
||||
if buffer.is_empty() {
|
||||
self.read_internal(address, buffer, false)
|
||||
self.read_internal(address, buffer, false, || Ok(()))
|
||||
} else {
|
||||
self.read_dma_internal(address, buffer, false).await
|
||||
self.read_dma_internal(address, buffer, false, || Ok(())).await
|
||||
}
|
||||
}
|
||||
|
||||
@ -611,15 +691,15 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
RXDMA: super::RxDma<T>,
|
||||
{
|
||||
if bytes.is_empty() {
|
||||
self.write_internal(address, bytes, false)?;
|
||||
self.write_internal(address, bytes, false, || Ok(()))?;
|
||||
} else {
|
||||
self.write_dma_internal(address, bytes, true, true).await?;
|
||||
self.write_dma_internal(address, bytes, true, true, || Ok(())).await?;
|
||||
}
|
||||
|
||||
if buffer.is_empty() {
|
||||
self.read_internal(address, buffer, true)?;
|
||||
self.read_internal(address, buffer, true, || Ok(()))?;
|
||||
} else {
|
||||
self.read_dma_internal(address, buffer, true).await?;
|
||||
self.read_dma_internal(address, buffer, true, || Ok(())).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -628,22 +708,55 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
// =========================
|
||||
// Blocking public API
|
||||
|
||||
pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.read_internal(address, buffer, false)
|
||||
pub fn blocking_read_timeout(
|
||||
&mut self,
|
||||
address: u8,
|
||||
buffer: &mut [u8],
|
||||
check_timeout: impl Fn() -> Result<(), Error>,
|
||||
) -> Result<(), Error> {
|
||||
self.read_internal(address, buffer, false, &check_timeout)
|
||||
// Automatic Stop
|
||||
}
|
||||
|
||||
pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.blocking_read_timeout(address, buffer, || Ok(()))
|
||||
}
|
||||
|
||||
pub fn blocking_write_timeout(
|
||||
&mut self,
|
||||
address: u8,
|
||||
bytes: &[u8],
|
||||
check_timeout: impl Fn() -> Result<(), Error>,
|
||||
) -> Result<(), Error> {
|
||||
self.write_internal(address, bytes, true, &check_timeout)
|
||||
}
|
||||
|
||||
pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> {
|
||||
self.write_internal(address, bytes, true)
|
||||
self.blocking_write_timeout(address, bytes, || Ok(()))
|
||||
}
|
||||
|
||||
pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.write_internal(address, bytes, false)?;
|
||||
self.read_internal(address, buffer, true)
|
||||
pub fn blocking_write_read_timeout(
|
||||
&mut self,
|
||||
address: u8,
|
||||
bytes: &[u8],
|
||||
buffer: &mut [u8],
|
||||
check_timeout: impl Fn() -> Result<(), Error>,
|
||||
) -> Result<(), Error> {
|
||||
self.write_internal(address, bytes, false, &check_timeout)?;
|
||||
self.read_internal(address, buffer, true, &check_timeout)
|
||||
// Automatic Stop
|
||||
}
|
||||
|
||||
pub fn blocking_write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> {
|
||||
pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.blocking_write_read_timeout(address, bytes, buffer, || Ok(()))
|
||||
}
|
||||
|
||||
pub fn blocking_write_vectored_timeout(
|
||||
&mut self,
|
||||
address: u8,
|
||||
bytes: &[&[u8]],
|
||||
check_timeout: impl Fn() -> Result<(), Error>,
|
||||
) -> Result<(), Error> {
|
||||
if bytes.is_empty() {
|
||||
return Err(Error::ZeroLengthTransfer);
|
||||
}
|
||||
@ -657,7 +770,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
first_length.min(255),
|
||||
Stop::Software,
|
||||
(first_length > 255) || (last_slice_index != 0),
|
||||
);
|
||||
&check_timeout,
|
||||
)?;
|
||||
}
|
||||
|
||||
for (idx, slice) in bytes.iter().enumerate() {
|
||||
@ -673,7 +787,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
if idx != 0 {
|
||||
// NOTE(unsafe) We have &mut self
|
||||
unsafe {
|
||||
Self::master_continue(slice_len.min(255), (idx != last_slice_index) || (slice_len > 255));
|
||||
Self::master_continue(
|
||||
slice_len.min(255),
|
||||
(idx != last_slice_index) || (slice_len > 255),
|
||||
&check_timeout,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -681,7 +799,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
if number != 0 {
|
||||
// NOTE(unsafe) We have &mut self
|
||||
unsafe {
|
||||
Self::master_continue(chunk.len(), (number != last_chunk_idx) || (idx != last_slice_index));
|
||||
Self::master_continue(
|
||||
chunk.len(),
|
||||
(number != last_chunk_idx) || (idx != last_slice_index),
|
||||
&check_timeout,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -689,7 +811,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
// Wait until we are allowed to send data
|
||||
// (START has been ACKed or last byte when
|
||||
// through)
|
||||
self.wait_txe()?;
|
||||
self.wait_txe(&check_timeout)?;
|
||||
|
||||
// Put byte on the wire
|
||||
//self.i2c.txdr.write(|w| w.txdata().bits(*byte));
|
||||
@ -700,11 +822,15 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
}
|
||||
}
|
||||
// Wait until the write finishes
|
||||
self.wait_tc()?;
|
||||
self.wait_tc(&check_timeout)?;
|
||||
self.master_stop();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn blocking_write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> {
|
||||
self.blocking_write_vectored_timeout(address, bytes, || Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
mod eh02 {
|
||||
@ -929,43 +1055,35 @@ mod eh1 {
|
||||
|
||||
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
|
||||
mod eha {
|
||||
use core::future::Future;
|
||||
|
||||
use super::super::{RxDma, TxDma};
|
||||
use super::*;
|
||||
|
||||
impl<'d, T: Instance, TXDMA: TxDma<T>, RXDMA: RxDma<T>> embedded_hal_async::i2c::I2c for I2c<'d, T, TXDMA, RXDMA> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(address, buffer)
|
||||
async fn read<'a>(&'a mut self, address: u8, read: &'a mut [u8]) -> Result<(), Self::Error> {
|
||||
self.read(address, read).await
|
||||
}
|
||||
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(address, bytes)
|
||||
async fn write<'a>(&'a mut self, address: u8, write: &'a [u8]) -> Result<(), Self::Error> {
|
||||
self.write(address, write).await
|
||||
}
|
||||
|
||||
type WriteReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
fn write_read<'a>(
|
||||
async fn write_read<'a>(
|
||||
&'a mut self,
|
||||
address: u8,
|
||||
bytes: &'a [u8],
|
||||
buffer: &'a mut [u8],
|
||||
) -> Self::WriteReadFuture<'a> {
|
||||
self.write_read(address, bytes, buffer)
|
||||
write: &'a [u8],
|
||||
read: &'a mut [u8],
|
||||
) -> Result<(), Self::Error> {
|
||||
self.write_read(address, write, read).await
|
||||
}
|
||||
|
||||
type TransactionFuture<'a, 'b> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a, 'b: 'a;
|
||||
|
||||
fn transaction<'a, 'b>(
|
||||
async fn transaction<'a, 'b>(
|
||||
&'a mut self,
|
||||
address: u8,
|
||||
operations: &'a mut [embedded_hal_async::i2c::Operation<'b>],
|
||||
) -> Self::TransactionFuture<'a, 'b> {
|
||||
operations: &'a mut [embedded_hal_1::i2c::Operation<'b>],
|
||||
) -> Result<(), Self::Error> {
|
||||
let _ = address;
|
||||
let _ = operations;
|
||||
async move { todo!() }
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,9 @@
|
||||
#![no_std]
|
||||
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
|
||||
#![cfg_attr(
|
||||
feature = "nightly",
|
||||
feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections)
|
||||
)]
|
||||
#![cfg_attr(feature = "nightly", allow(incomplete_features))]
|
||||
|
||||
// This must go FIRST so that all the other modules see its macros.
|
||||
pub mod fmt;
|
||||
@ -54,9 +58,9 @@ pub mod sdmmc;
|
||||
pub mod spi;
|
||||
#[cfg(usart)]
|
||||
pub mod usart;
|
||||
#[cfg(usb)]
|
||||
#[cfg(all(usb, feature = "time"))]
|
||||
pub mod usb;
|
||||
#[cfg(any(otgfs, otghs))]
|
||||
#[cfg(otg)]
|
||||
pub mod usb_otg;
|
||||
|
||||
#[cfg(iwdg)]
|
||||
@ -77,6 +81,8 @@ pub(crate) mod _generated {
|
||||
// Reexports
|
||||
pub use _generated::{peripherals, Peripherals};
|
||||
pub use embassy_cortex_m::executor;
|
||||
#[cfg(any(dma, bdma))]
|
||||
use embassy_cortex_m::interrupt::Priority;
|
||||
pub use embassy_cortex_m::interrupt::_export::interrupt;
|
||||
pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
|
||||
#[cfg(feature = "unstable-pac")]
|
||||
@ -89,6 +95,10 @@ pub struct Config {
|
||||
pub rcc: rcc::Config,
|
||||
#[cfg(dbgmcu)]
|
||||
pub enable_debug_during_sleep: bool,
|
||||
#[cfg(bdma)]
|
||||
pub bdma_interrupt_priority: Priority,
|
||||
#[cfg(dma)]
|
||||
pub dma_interrupt_priority: Priority,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -97,6 +107,10 @@ impl Default for Config {
|
||||
rcc: Default::default(),
|
||||
#[cfg(dbgmcu)]
|
||||
enable_debug_during_sleep: true,
|
||||
#[cfg(bdma)]
|
||||
bdma_interrupt_priority: Priority::P0,
|
||||
#[cfg(dma)]
|
||||
dma_interrupt_priority: Priority::P0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -109,7 +123,7 @@ pub fn init(config: Config) -> Peripherals {
|
||||
#[cfg(dbgmcu)]
|
||||
if config.enable_debug_during_sleep {
|
||||
crate::pac::DBGMCU.cr().modify(|cr| {
|
||||
#[cfg(any(dbgmcu_f0, dbgmcu_g0, dbgmcu_u5))]
|
||||
#[cfg(any(dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u5))]
|
||||
{
|
||||
cr.set_dbg_stop(true);
|
||||
cr.set_dbg_standby(true);
|
||||
@ -135,7 +149,12 @@ pub fn init(config: Config) -> Peripherals {
|
||||
}
|
||||
|
||||
gpio::init();
|
||||
dma::init();
|
||||
dma::init(
|
||||
#[cfg(bdma)]
|
||||
config.bdma_interrupt_priority,
|
||||
#[cfg(dma)]
|
||||
config.dma_interrupt_priority,
|
||||
);
|
||||
#[cfg(feature = "exti")]
|
||||
exti::init();
|
||||
|
||||
|
233
embassy-stm32/src/rcc/c0.rs
Normal file
233
embassy-stm32/src/rcc/c0.rs
Normal file
@ -0,0 +1,233 @@
|
||||
use crate::pac::flash::vals::Latency;
|
||||
use crate::pac::rcc::vals::{Hpre, Hsidiv, Ppre, Sw};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(48_000_000);
|
||||
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
|
||||
/// System clock mux source
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ClockSrc {
|
||||
HSE(Hertz),
|
||||
HSI(HSIPrescaler),
|
||||
LSI,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum HSIPrescaler {
|
||||
NotDivided,
|
||||
Div2,
|
||||
Div4,
|
||||
Div8,
|
||||
Div16,
|
||||
Div32,
|
||||
Div64,
|
||||
Div128,
|
||||
}
|
||||
|
||||
impl Into<Hsidiv> for HSIPrescaler {
|
||||
fn into(self) -> Hsidiv {
|
||||
match self {
|
||||
HSIPrescaler::NotDivided => Hsidiv::DIV1,
|
||||
HSIPrescaler::Div2 => Hsidiv::DIV2,
|
||||
HSIPrescaler::Div4 => Hsidiv::DIV4,
|
||||
HSIPrescaler::Div8 => Hsidiv::DIV8,
|
||||
HSIPrescaler::Div16 => Hsidiv::DIV16,
|
||||
HSIPrescaler::Div32 => Hsidiv::DIV32,
|
||||
HSIPrescaler::Div64 => Hsidiv::DIV64,
|
||||
HSIPrescaler::Div128 => Hsidiv::DIV128,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// AHB prescaler
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum AHBPrescaler {
|
||||
NotDivided,
|
||||
Div2,
|
||||
Div4,
|
||||
Div8,
|
||||
Div16,
|
||||
Div64,
|
||||
Div128,
|
||||
Div256,
|
||||
Div512,
|
||||
}
|
||||
|
||||
/// APB prescaler
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum APBPrescaler {
|
||||
NotDivided,
|
||||
Div2,
|
||||
Div4,
|
||||
Div8,
|
||||
Div16,
|
||||
}
|
||||
|
||||
impl Into<Ppre> for APBPrescaler {
|
||||
fn into(self) -> Ppre {
|
||||
match self {
|
||||
APBPrescaler::NotDivided => Ppre::DIV1,
|
||||
APBPrescaler::Div2 => Ppre::DIV2,
|
||||
APBPrescaler::Div4 => Ppre::DIV4,
|
||||
APBPrescaler::Div8 => Ppre::DIV8,
|
||||
APBPrescaler::Div16 => Ppre::DIV16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Hpre> for AHBPrescaler {
|
||||
fn into(self) -> Hpre {
|
||||
match self {
|
||||
AHBPrescaler::NotDivided => Hpre::DIV1,
|
||||
AHBPrescaler::Div2 => Hpre::DIV2,
|
||||
AHBPrescaler::Div4 => Hpre::DIV4,
|
||||
AHBPrescaler::Div8 => Hpre::DIV8,
|
||||
AHBPrescaler::Div16 => Hpre::DIV16,
|
||||
AHBPrescaler::Div64 => Hpre::DIV64,
|
||||
AHBPrescaler::Div128 => Hpre::DIV128,
|
||||
AHBPrescaler::Div256 => Hpre::DIV256,
|
||||
AHBPrescaler::Div512 => Hpre::DIV512,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clocks configutation
|
||||
pub struct Config {
|
||||
pub mux: ClockSrc,
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb_pre: APBPrescaler,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
#[inline]
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
mux: ClockSrc::HSI(HSIPrescaler::NotDivided),
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb_pre: APBPrescaler::NotDivided,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let (sys_clk, sw) = match config.mux {
|
||||
ClockSrc::HSI(div) => {
|
||||
// Enable HSI
|
||||
let div: Hsidiv = div.into();
|
||||
RCC.cr().write(|w| {
|
||||
w.set_hsidiv(div);
|
||||
w.set_hsion(true)
|
||||
});
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
(HSI_FREQ.0 >> div.0, Sw::HSI)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
(freq.0, Sw::HSE)
|
||||
}
|
||||
ClockSrc::LSI => {
|
||||
// Enable LSI
|
||||
RCC.csr2().write(|w| w.set_lsion(true));
|
||||
while !RCC.csr2().read().lsirdy() {}
|
||||
(LSI_FREQ.0, Sw::LSI)
|
||||
}
|
||||
};
|
||||
|
||||
// Determine the flash latency implied by the target clock speed
|
||||
// RM0454 § 3.3.4:
|
||||
let target_flash_latency = if sys_clk <= 24_000_000 {
|
||||
Latency::WS0
|
||||
} else {
|
||||
Latency::WS1
|
||||
};
|
||||
|
||||
// Increase the number of cycles we wait for flash if the new value is higher
|
||||
// There's no harm in waiting a little too much before the clock change, but we'll
|
||||
// crash immediately if we don't wait enough after the clock change
|
||||
let mut set_flash_latency_after = false;
|
||||
FLASH.acr().modify(|w| {
|
||||
// Is the current flash latency less than what we need at the new SYSCLK?
|
||||
if w.latency().0 <= target_flash_latency.0 {
|
||||
// We must increase the number of wait states now
|
||||
w.set_latency(target_flash_latency)
|
||||
} else {
|
||||
// We may decrease the number of wait states later
|
||||
set_flash_latency_after = true;
|
||||
}
|
||||
|
||||
// RM0490 § 3.3.4:
|
||||
// > Prefetch is enabled by setting the PRFTEN bit of the FLASH access control register
|
||||
// > (FLASH_ACR). This feature is useful if at least one wait state is needed to access the
|
||||
// > Flash memory.
|
||||
//
|
||||
// Enable flash prefetching if we have at least one wait state, and disable it otherwise.
|
||||
w.set_prften(target_flash_latency.0 > 0);
|
||||
});
|
||||
|
||||
if !set_flash_latency_after {
|
||||
// Spin until the effective flash latency is compatible with the clock change
|
||||
while FLASH.acr().read().latency().0 < target_flash_latency.0 {}
|
||||
}
|
||||
|
||||
// Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once
|
||||
let (sw, hpre, ppre) = (sw.into(), config.ahb_pre.into(), config.apb_pre.into());
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(sw);
|
||||
w.set_hpre(hpre);
|
||||
w.set_ppre(ppre);
|
||||
});
|
||||
|
||||
if set_flash_latency_after {
|
||||
// We can make the flash require fewer wait states
|
||||
// Spin until the SYSCLK changes have taken effect
|
||||
loop {
|
||||
let cfgr = RCC.cfgr().read();
|
||||
if cfgr.sw() == sw && cfgr.hpre() == hpre && cfgr.ppre() == ppre {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the flash latency to require fewer wait states
|
||||
FLASH.acr().modify(|w| w.set_latency(target_flash_latency));
|
||||
}
|
||||
|
||||
let ahb_div = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => 1,
|
||||
AHBPrescaler::Div2 => 2,
|
||||
AHBPrescaler::Div4 => 4,
|
||||
AHBPrescaler::Div8 => 8,
|
||||
AHBPrescaler::Div16 => 16,
|
||||
AHBPrescaler::Div64 => 64,
|
||||
AHBPrescaler::Div128 => 128,
|
||||
AHBPrescaler::Div256 => 256,
|
||||
AHBPrescaler::Div512 => 512,
|
||||
};
|
||||
let ahb_freq = sys_clk / ahb_div;
|
||||
|
||||
let (apb_freq, apb_tim_freq) = match config.apb_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.0 - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sys_clk),
|
||||
ahb1: Hertz(ahb_freq),
|
||||
apb1: Hertz(apb_freq),
|
||||
apb1_tim: Hertz(apb_tim_freq),
|
||||
});
|
||||
}
|
@ -10,6 +10,7 @@ use crate::time::Hertz;
|
||||
#[cfg_attr(rcc_f3, path = "f3.rs")]
|
||||
#[cfg_attr(any(rcc_f4, rcc_f410), path = "f4.rs")]
|
||||
#[cfg_attr(rcc_f7, path = "f7.rs")]
|
||||
#[cfg_attr(rcc_c0, path = "c0.rs")]
|
||||
#[cfg_attr(rcc_g0, path = "g0.rs")]
|
||||
#[cfg_attr(rcc_g4, path = "g4.rs")]
|
||||
#[cfg_attr(any(rcc_h7, rcc_h7ab), path = "h7.rs")]
|
||||
@ -23,16 +24,17 @@ use crate::time::Hertz;
|
||||
mod _version;
|
||||
pub use _version::*;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Clocks {
|
||||
pub sys: Hertz,
|
||||
|
||||
// APB
|
||||
pub apb1: Hertz,
|
||||
pub apb1_tim: Hertz,
|
||||
#[cfg(not(rcc_g0))]
|
||||
#[cfg(not(any(rcc_c0, rcc_g0)))]
|
||||
pub apb2: Hertz,
|
||||
#[cfg(not(rcc_g0))]
|
||||
#[cfg(not(any(rcc_c0, rcc_g0)))]
|
||||
pub apb2_tim: Hertz,
|
||||
#[cfg(any(rcc_wl5, rcc_wle, rcc_u5))]
|
||||
pub apb3: Hertz,
|
||||
@ -71,6 +73,7 @@ static mut CLOCK_FREQS: MaybeUninit<Clocks> = MaybeUninit::uninit();
|
||||
///
|
||||
/// Safety: Sets a mutable global.
|
||||
pub(crate) unsafe fn set_freqs(freqs: Clocks) {
|
||||
debug!("rcc: {:?}", freqs);
|
||||
CLOCK_FREQS.as_mut_ptr().write(freqs);
|
||||
}
|
||||
|
||||
|
@ -295,6 +295,7 @@ pub struct Config {
|
||||
pub apb1_pre: APBPrescaler,
|
||||
pub apb2_pre: APBPrescaler,
|
||||
pub apb3_pre: APBPrescaler,
|
||||
pub hsi48: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -305,6 +306,7 @@ impl Default for Config {
|
||||
apb1_pre: Default::default(),
|
||||
apb2_pre: Default::default(),
|
||||
apb3_pre: Default::default(),
|
||||
hsi48: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -320,7 +322,6 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
RCC.cr().write(|w| {
|
||||
w.set_msipllen(false);
|
||||
w.set_msison(true);
|
||||
w.set_msison(true);
|
||||
});
|
||||
while !RCC.cr().read().msisrdy() {}
|
||||
|
||||
@ -340,9 +341,20 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
}
|
||||
ClockSrc::PLL1R(src, m, n, div) => {
|
||||
let freq = match src {
|
||||
PllSrc::MSI(_) => MSIRange::default().into(),
|
||||
PllSrc::HSE(hertz) => hertz.0,
|
||||
PllSrc::HSI16 => HSI_FREQ.0,
|
||||
PllSrc::MSI(_) => {
|
||||
// TODO: enable MSI
|
||||
MSIRange::default().into()
|
||||
}
|
||||
PllSrc::HSE(hertz) => {
|
||||
// TODO: enable HSE
|
||||
hertz.0
|
||||
}
|
||||
PllSrc::HSI16 => {
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
HSI_FREQ.0
|
||||
}
|
||||
};
|
||||
|
||||
// disable
|
||||
@ -355,6 +367,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
RCC.pll1cfgr().write(|w| {
|
||||
w.set_pllm(m.into());
|
||||
w.set_pllsrc(src.into());
|
||||
w.set_pllren(true);
|
||||
});
|
||||
|
||||
RCC.pll1divr().modify(|w| {
|
||||
@ -365,15 +378,16 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
// Enable PLL
|
||||
RCC.cr().modify(|w| w.set_pllon(0, true));
|
||||
while !RCC.cr().read().pllrdy(0) {}
|
||||
RCC.pll1cfgr().modify(|w| w.set_pllren(true));
|
||||
|
||||
RCC.cr().write(|w| w.set_pllon(0, true));
|
||||
while !RCC.cr().read().pllrdy(0) {}
|
||||
|
||||
pll_ck
|
||||
}
|
||||
};
|
||||
|
||||
if config.hsi48 {
|
||||
RCC.cr().modify(|w| w.set_hsi48on(true));
|
||||
while !RCC.cr().read().hsi48rdy() {}
|
||||
}
|
||||
|
||||
// TODO make configurable
|
||||
let power_vos = VoltageScale::Range4;
|
||||
|
||||
|
@ -64,7 +64,7 @@ impl Into<u8> for APBPrescaler {
|
||||
impl Into<u8> for AHBPrescaler {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
AHBPrescaler::NotDivided => 1,
|
||||
AHBPrescaler::NotDivided => 0x0,
|
||||
AHBPrescaler::Div2 => 0x08,
|
||||
AHBPrescaler::Div3 => 0x01,
|
||||
AHBPrescaler::Div4 => 0x09,
|
||||
|
@ -158,7 +158,7 @@ impl Into<u8> for APBPrescaler {
|
||||
impl Into<u8> for AHBPrescaler {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
AHBPrescaler::NotDivided => 1,
|
||||
AHBPrescaler::NotDivided => 0x0,
|
||||
AHBPrescaler::Div2 => 0x08,
|
||||
AHBPrescaler::Div3 => 0x01,
|
||||
AHBPrescaler::Div4 => 0x09,
|
||||
|
@ -32,6 +32,11 @@ impl<'d, T: Instance> Rng<'d, T> {
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
// rng_v2 locks up on seed error, needs reset
|
||||
#[cfg(rng_v2)]
|
||||
if unsafe { T::regs().sr().read().seis() } {
|
||||
T::reset();
|
||||
}
|
||||
unsafe {
|
||||
T::regs().cr().modify(|reg| {
|
||||
reg.set_rngen(true);
|
||||
@ -90,8 +95,10 @@ impl<'d, T: Instance> Rng<'d, T> {
|
||||
impl<'d, T: Instance> RngCore for Rng<'d, T> {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
loop {
|
||||
let bits = unsafe { T::regs().sr().read() };
|
||||
if bits.drdy() {
|
||||
let sr = unsafe { T::regs().sr().read() };
|
||||
if sr.seis() | sr.ceis() {
|
||||
self.reset();
|
||||
} else if sr.drdy() {
|
||||
return unsafe { T::regs().dr().read() };
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,9 @@ use crate::rcc::RccPeripheral;
|
||||
use crate::time::Hertz;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
/// Frequency used for SD Card initialization. Must be no higher than 400 kHz.
|
||||
const SD_INIT_FREQ: Hertz = Hertz(400_000);
|
||||
|
||||
/// The signalling scheme used on the SDMMC bus
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
@ -295,7 +298,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> {
|
||||
T::reset();
|
||||
|
||||
let inner = T::inner();
|
||||
let clock = unsafe { inner.new_inner(T::frequency()) };
|
||||
unsafe { inner.new_inner() };
|
||||
|
||||
irq.set_handler(Self::on_interrupt);
|
||||
irq.unpend();
|
||||
@ -314,7 +317,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> {
|
||||
d3,
|
||||
|
||||
config,
|
||||
clock,
|
||||
clock: SD_INIT_FREQ,
|
||||
signalling: Default::default(),
|
||||
card: None,
|
||||
}
|
||||
@ -415,7 +418,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> {
|
||||
T::reset();
|
||||
|
||||
let inner = T::inner();
|
||||
let clock = unsafe { inner.new_inner(T::frequency()) };
|
||||
unsafe { inner.new_inner() };
|
||||
|
||||
irq.set_handler(Self::on_interrupt);
|
||||
irq.unpend();
|
||||
@ -434,7 +437,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> {
|
||||
d3,
|
||||
|
||||
config,
|
||||
clock,
|
||||
clock: SD_INIT_FREQ,
|
||||
signalling: Default::default(),
|
||||
card: None,
|
||||
}
|
||||
@ -561,16 +564,10 @@ impl SdmmcInner {
|
||||
/// # Safety
|
||||
///
|
||||
/// Access to `regs` registers should be exclusive
|
||||
unsafe fn new_inner(&self, kernel_clk: Hertz) -> Hertz {
|
||||
unsafe fn new_inner(&self) {
|
||||
let regs = self.0;
|
||||
|
||||
// While the SD/SDIO card or eMMC is in identification mode,
|
||||
// the SDMMC_CK frequency must be less than 400 kHz.
|
||||
let (clkdiv, clock) = unwrap!(clk_div(kernel_clk, 400_000));
|
||||
|
||||
regs.clkcr().write(|w| {
|
||||
w.set_widbus(0);
|
||||
w.set_clkdiv(clkdiv);
|
||||
w.set_pwrsav(false);
|
||||
w.set_negedge(false);
|
||||
w.set_hwfc_en(true);
|
||||
@ -582,8 +579,6 @@ impl SdmmcInner {
|
||||
// Power off, writen 00: Clock to the card is stopped;
|
||||
// D[7:0], CMD, and CK are driven high.
|
||||
regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8));
|
||||
|
||||
clock
|
||||
}
|
||||
|
||||
/// Initializes card (if present) and sets the bus at the
|
||||
@ -605,6 +600,19 @@ impl SdmmcInner {
|
||||
|
||||
// NOTE(unsafe) We have exclusive access to the peripheral
|
||||
unsafe {
|
||||
// While the SD/SDIO card or eMMC is in identification mode,
|
||||
// the SDMMC_CK frequency must be no more than 400 kHz.
|
||||
let (clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0));
|
||||
*clock = init_clock;
|
||||
|
||||
// CPSMACT and DPSMACT must be 0 to set WIDBUS
|
||||
self.wait_idle();
|
||||
|
||||
regs.clkcr().modify(|w| {
|
||||
w.set_widbus(0);
|
||||
w.set_clkdiv(clkdiv);
|
||||
});
|
||||
|
||||
regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8));
|
||||
self.cmd(Cmd::idle(), false)?;
|
||||
|
||||
@ -1524,7 +1532,7 @@ foreach_peripheral!(
|
||||
};
|
||||
);
|
||||
|
||||
#[cfg(feature = "sdmmc-rs")]
|
||||
#[cfg(feature = "embedded-sdmmc")]
|
||||
mod sdmmc_rs {
|
||||
use core::future::Future;
|
||||
|
||||
@ -1532,16 +1540,16 @@ mod sdmmc_rs {
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<'d, T: Instance, P: Pins<T>> BlockDevice for Sdmmc<'d, T, P> {
|
||||
impl<'d, T: Instance, Dma: SdmmcDma<T>> BlockDevice for Sdmmc<'d, T, Dma> {
|
||||
type Error = Error;
|
||||
type ReadFuture<'a>
|
||||
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a,
|
||||
= impl Future<Output = Result<(), Self::Error>> + 'a;
|
||||
type WriteFuture<'a>
|
||||
Self: 'a;
|
||||
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a,
|
||||
= impl Future<Output = Result<(), Self::Error>> + 'a;
|
||||
Self: 'a;
|
||||
|
||||
fn read<'a>(
|
||||
&'a mut self,
|
||||
@ -1550,19 +1558,14 @@ mod sdmmc_rs {
|
||||
_reason: &str,
|
||||
) -> Self::ReadFuture<'a> {
|
||||
async move {
|
||||
let card_capacity = self.card()?.card_type;
|
||||
let inner = T::inner();
|
||||
let state = T::state();
|
||||
let mut address = start_block_idx.0;
|
||||
|
||||
for block in blocks.iter_mut() {
|
||||
let block: &mut [u8; 512] = &mut block.contents;
|
||||
|
||||
// NOTE(unsafe) Block uses align(4)
|
||||
let buf = unsafe { &mut *(block as *mut [u8; 512] as *mut [u32; 128]) };
|
||||
inner
|
||||
.read_block(address, buf, card_capacity, state, self.config.data_transfer_timeout)
|
||||
.await?;
|
||||
let block = unsafe { &mut *(block as *mut _ as *mut DataBlock) };
|
||||
self.read_block(address, block).await?;
|
||||
address += 1;
|
||||
}
|
||||
Ok(())
|
||||
@ -1571,19 +1574,14 @@ mod sdmmc_rs {
|
||||
|
||||
fn write<'a>(&'a mut self, blocks: &'a [Block], start_block_idx: BlockIdx) -> Self::WriteFuture<'a> {
|
||||
async move {
|
||||
let card = self.card.as_mut().ok_or(Error::NoCard)?;
|
||||
let inner = T::inner();
|
||||
let state = T::state();
|
||||
let mut address = start_block_idx.0;
|
||||
|
||||
for block in blocks.iter() {
|
||||
let block: &[u8; 512] = &block.contents;
|
||||
|
||||
// NOTE(unsafe) DataBlock uses align 4
|
||||
let buf = unsafe { &*(block as *const [u8; 512] as *const [u32; 128]) };
|
||||
inner
|
||||
.write_block(address, buf, card, state, self.config.data_transfer_timeout)
|
||||
.await?;
|
||||
let block = unsafe { &*(block as *const _ as *const DataBlock) };
|
||||
self.write_block(address, block).await?;
|
||||
address += 1;
|
||||
}
|
||||
Ok(())
|
||||
|
@ -8,9 +8,9 @@ use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
|
||||
|
||||
use self::sealed::WordSize;
|
||||
use crate::dma::{slice_ptr_parts, NoDma, Transfer};
|
||||
use crate::dma::{slice_ptr_parts, Transfer};
|
||||
use crate::gpio::sealed::{AFType, Pin as _};
|
||||
use crate::gpio::AnyPin;
|
||||
use crate::gpio::{AnyPin, Pull};
|
||||
use crate::pac::spi::{regs, vals, Spi as Regs};
|
||||
use crate::rcc::RccPeripheral;
|
||||
use crate::time::Hertz;
|
||||
@ -93,15 +93,18 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
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,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
|
||||
#[cfg(any(spi_v2, spi_v3, spi_v4))]
|
||||
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);
|
||||
#[cfg(any(spi_v2, spi_v3, spi_v4))]
|
||||
mosi.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
miso.set_as_af(miso.af_num(), AFType::Input);
|
||||
#[cfg(any(spi_v2, spi_v3, spi_v4))]
|
||||
miso.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
}
|
||||
|
||||
@ -129,10 +132,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
into_ref!(sck, miso);
|
||||
unsafe {
|
||||
sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
|
||||
#[cfg(any(spi_v2, spi_v3, spi_v4))]
|
||||
sck.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
miso.set_as_af(miso.af_num(), AFType::Input);
|
||||
#[cfg(any(spi_v2, spi_v3, spi_v4))]
|
||||
miso.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
}
|
||||
|
||||
@ -160,10 +161,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
into_ref!(sck, mosi);
|
||||
unsafe {
|
||||
sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
|
||||
#[cfg(any(spi_v2, spi_v3, spi_v4))]
|
||||
sck.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull);
|
||||
#[cfg(any(spi_v2, spi_v3, spi_v4))]
|
||||
mosi.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
}
|
||||
|
||||
@ -474,7 +473,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
let tx_request = self.txdma.request();
|
||||
let tx_dst = T::REGS.tx_ptr();
|
||||
let clock_byte = 0x00u8;
|
||||
let tx_f = crate::dma::write_repeated(&mut self.txdma, tx_request, clock_byte, clock_byte_count, tx_dst);
|
||||
let tx_f = crate::dma::write_repeated(&mut self.txdma, tx_request, &clock_byte, clock_byte_count, tx_dst);
|
||||
|
||||
unsafe {
|
||||
set_txdmaen(T::REGS, true);
|
||||
@ -772,10 +771,13 @@ fn finish_dma(regs: Regs) {
|
||||
#[cfg(not(any(spi_v3, spi_v4)))]
|
||||
while regs.sr().read().bsy() {}
|
||||
|
||||
// Disable the spi peripheral
|
||||
regs.cr1().modify(|w| {
|
||||
w.set_spe(false);
|
||||
});
|
||||
|
||||
// The peripheral automatically disables the DMA stream on completion without error,
|
||||
// but it does not clear the RXDMAEN/TXDMAEN flag in CR2.
|
||||
#[cfg(not(any(spi_v3, spi_v4)))]
|
||||
regs.cr2().modify(|reg| {
|
||||
reg.set_txdmaen(false);
|
||||
@ -812,7 +814,7 @@ mod eh02 {
|
||||
// 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> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, T, NoDma, NoDma> {
|
||||
impl<'d, T: Instance, Tx, Rx> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, T, Tx, Rx> {
|
||||
type Error = Error;
|
||||
|
||||
fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> {
|
||||
@ -820,7 +822,7 @@ mod eh02 {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, T, NoDma, NoDma> {
|
||||
impl<'d, T: Instance, Tx, Rx> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, T, Tx, Rx> {
|
||||
type Error = Error;
|
||||
|
||||
fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> {
|
||||
@ -849,19 +851,19 @@ mod eh1 {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, W: Word> embedded_hal_1::spi::SpiBusRead<W> for Spi<'d, T, NoDma, NoDma> {
|
||||
impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBusRead<W> for Spi<'d, T, Tx, Rx> {
|
||||
fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> {
|
||||
self.blocking_read(words)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, W: Word> embedded_hal_1::spi::SpiBusWrite<W> for Spi<'d, T, NoDma, NoDma> {
|
||||
impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBusWrite<W> for Spi<'d, T, Tx, Rx> {
|
||||
fn write(&mut self, words: &[W]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(words)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, W: Word> embedded_hal_1::spi::SpiBus<W> for Spi<'d, T, NoDma, NoDma> {
|
||||
impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBus<W> for Spi<'d, T, Tx, Rx> {
|
||||
fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> {
|
||||
self.blocking_transfer(read, write)
|
||||
}
|
||||
@ -885,46 +887,34 @@ mod eh1 {
|
||||
|
||||
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
|
||||
mod eha {
|
||||
use core::future::Future;
|
||||
|
||||
use super::*;
|
||||
impl<'d, T: Instance, Tx, Rx> embedded_hal_async::spi::SpiBusFlush for Spi<'d, T, Tx, Rx> {
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
async { Ok(()) }
|
||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Tx: TxDma<T>, Rx, W: Word> embedded_hal_async::spi::SpiBusWrite<W> for Spi<'d, T, Tx, Rx> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, data: &'a [W]) -> Self::WriteFuture<'a> {
|
||||
self.write(data)
|
||||
async fn write(&mut self, words: &[W]) -> Result<(), Self::Error> {
|
||||
self.write(words).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Tx: TxDma<T>, Rx: RxDma<T>, W: Word> embedded_hal_async::spi::SpiBusRead<W>
|
||||
for Spi<'d, T, Tx, Rx>
|
||||
{
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, data: &'a mut [W]) -> Self::ReadFuture<'a> {
|
||||
self.read(data)
|
||||
async fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> {
|
||||
self.read(words).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Tx: TxDma<T>, Rx: RxDma<T>, W: Word> embedded_hal_async::spi::SpiBus<W> for Spi<'d, T, Tx, Rx> {
|
||||
type TransferFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn transfer<'a>(&'a mut self, rx: &'a mut [W], tx: &'a [W]) -> Self::TransferFuture<'a> {
|
||||
self.transfer(rx, tx)
|
||||
async fn transfer<'a>(&'a mut self, read: &'a mut [W], write: &'a [W]) -> Result<(), Self::Error> {
|
||||
self.transfer(read, write).await
|
||||
}
|
||||
|
||||
type TransferInPlaceFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn transfer_in_place<'a>(&'a mut self, words: &'a mut [W]) -> Self::TransferInPlaceFuture<'a> {
|
||||
self.transfer_in_place(words)
|
||||
async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [W]) -> Result<(), Self::Error> {
|
||||
self.transfer_in_place(words).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -439,6 +439,7 @@ impl From<Timeout> for [u8; 3] {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
impl From<Timeout> for embassy_time::Duration {
|
||||
fn from(to: Timeout) -> Self {
|
||||
embassy_time::Duration::from_micros(to.as_micros().into())
|
||||
|
@ -44,6 +44,7 @@ impl From<RampTime> for core::time::Duration {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
impl From<RampTime> for embassy_time::Duration {
|
||||
fn from(rt: RampTime) -> Self {
|
||||
match rt {
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
/// Hertz
|
||||
#[derive(PartialEq, PartialOrd, Clone, Copy, Debug, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Hertz(pub u32);
|
||||
|
||||
impl Hertz {
|
||||
|
@ -292,19 +292,23 @@ impl Driver for RtcDriver {
|
||||
})
|
||||
}
|
||||
|
||||
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) {
|
||||
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
|
||||
critical_section::with(|cs| {
|
||||
let r = T::regs_gp16();
|
||||
|
||||
let n = alarm.id() as _;
|
||||
let n = alarm.id() as usize;
|
||||
let alarm = self.get_alarm(cs, alarm);
|
||||
alarm.timestamp.set(timestamp);
|
||||
|
||||
let t = self.now();
|
||||
if timestamp <= t {
|
||||
// If alarm timestamp has passed the alarm will not fire.
|
||||
// Disarm the alarm and return `false` to indicate that.
|
||||
unsafe { r.dier().modify(|w| w.set_ccie(n + 1, false)) };
|
||||
self.trigger_alarm(n, cs);
|
||||
return;
|
||||
|
||||
alarm.timestamp.set(u64::MAX);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
let safe_timestamp = timestamp.max(t + 3);
|
||||
@ -317,6 +321,8 @@ impl Driver for RtcDriver {
|
||||
let diff = timestamp - t;
|
||||
// NOTE(unsafe) We're in a critical section
|
||||
unsafe { r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000)) };
|
||||
|
||||
true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use core::cell::RefCell;
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::future::poll_fn;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
use atomic_polyfill::{compiler_fence, Ordering};
|
||||
use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
|
||||
use embassy_hal_common::ring_buffer::RingBuffer;
|
||||
use embassy_sync::waitqueue::WakerRegistration;
|
||||
@ -46,16 +46,102 @@ impl<'d, T: BasicInstance> Unpin for BufferedUart<'d, T> {}
|
||||
impl<'d, T: BasicInstance> BufferedUart<'d, T> {
|
||||
pub fn new(
|
||||
state: &'d mut State<'d, T>,
|
||||
_uart: Uart<'d, T, NoDma, NoDma>,
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
tx_buffer: &'d mut [u8],
|
||||
rx_buffer: &'d mut [u8],
|
||||
config: Config,
|
||||
) -> BufferedUart<'d, T> {
|
||||
into_ref!(irq);
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config)
|
||||
}
|
||||
|
||||
pub fn new_with_rtscts(
|
||||
state: &'d mut State<'d, T>,
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
rts: impl Peripheral<P = impl RtsPin<T>> + 'd,
|
||||
cts: impl Peripheral<P = impl CtsPin<T>> + 'd,
|
||||
tx_buffer: &'d mut [u8],
|
||||
rx_buffer: &'d mut [u8],
|
||||
config: Config,
|
||||
) -> BufferedUart<'d, T> {
|
||||
into_ref!(cts, rts);
|
||||
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
unsafe {
|
||||
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(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config)
|
||||
}
|
||||
|
||||
#[cfg(not(usart_v1))]
|
||||
pub fn new_with_de(
|
||||
state: &'d mut State<'d, T>,
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
de: impl Peripheral<P = impl DePin<T>> + 'd,
|
||||
tx_buffer: &'d mut [u8],
|
||||
rx_buffer: &'d mut [u8],
|
||||
config: Config,
|
||||
) -> BufferedUart<'d, T> {
|
||||
into_ref!(de);
|
||||
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
unsafe {
|
||||
de.set_as_af(de.af_num(), AFType::OutputPushPull);
|
||||
T::regs().cr3().write(|w| {
|
||||
w.set_dem(true);
|
||||
});
|
||||
}
|
||||
|
||||
Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config)
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
state: &'d mut State<'d, T>,
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
tx_buffer: &'d mut [u8],
|
||||
rx_buffer: &'d mut [u8],
|
||||
config: Config,
|
||||
) -> BufferedUart<'d, T> {
|
||||
into_ref!(_peri, rx, tx, irq);
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
unsafe {
|
||||
rx.set_as_af(rx.af_num(), AFType::Input);
|
||||
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
|
||||
}
|
||||
|
||||
configure(r, &config, T::frequency(), T::MULTIPLIER, true, true);
|
||||
|
||||
unsafe {
|
||||
r.cr1().modify(|w| {
|
||||
#[cfg(lpuart_v2)]
|
||||
w.set_fifoen(true);
|
||||
|
||||
w.set_rxneie(true);
|
||||
w.set_idleie(true);
|
||||
});
|
||||
@ -283,32 +369,20 @@ impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartTx<'u, 'd, T> {
|
||||
}
|
||||
|
||||
impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.inner_read(buf)
|
||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
self.inner_read(buf).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'u, 'd, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.inner.inner_read(buf)
|
||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
self.inner.inner_read(buf).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> {
|
||||
type FillBufFuture<'a> = impl Future<Output = Result<&'a [u8], Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> {
|
||||
self.inner_fill_buf()
|
||||
async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
|
||||
self.inner_fill_buf().await
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
@ -317,12 +391,8 @@ impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T>
|
||||
}
|
||||
|
||||
impl<'u, 'd, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'u, 'd, T> {
|
||||
type FillBufFuture<'a> = impl Future<Output = Result<&'a [u8], Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> {
|
||||
self.inner.inner_fill_buf()
|
||||
async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
|
||||
self.inner.inner_fill_buf().await
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
@ -331,37 +401,21 @@ impl<'u, 'd, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'
|
||||
}
|
||||
|
||||
impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.inner_write(buf)
|
||||
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
self.inner_write(buf).await
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
self.inner_flush()
|
||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
self.inner_flush().await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, 'd, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.inner.inner_write(buf)
|
||||
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
self.inner.inner_write(buf).await
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
self.inner.inner_flush()
|
||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
self.inner.inner_flush().await
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,13 @@
|
||||
#![macro_use]
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_cortex_m::interrupt::InterruptExt;
|
||||
use embassy_futures::select::{select, Either};
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
|
||||
use crate::dma::NoDma;
|
||||
@ -10,6 +16,7 @@ use crate::gpio::sealed::AFType;
|
||||
use crate::pac::lpuart::{regs, vals, Lpuart as Regs};
|
||||
#[cfg(not(any(lpuart_v1, lpuart_v2)))]
|
||||
use crate::pac::usart::{regs, vals, Usart as Regs};
|
||||
use crate::time::Hertz;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
@ -44,6 +51,10 @@ pub struct Config {
|
||||
pub data_bits: DataBits,
|
||||
pub stop_bits: StopBits,
|
||||
pub parity: Parity,
|
||||
/// if true, on read-like method, if there is a latent error pending,
|
||||
/// read will abort, the error reported and cleared
|
||||
/// if false, the error is ignored and cleared
|
||||
pub detect_previous_overrun: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -53,6 +64,8 @@ impl Default for Config {
|
||||
data_bits: DataBits::DataBits8,
|
||||
stop_bits: StopBits::STOP1,
|
||||
parity: Parity::ParityNone,
|
||||
// historical behavior
|
||||
detect_previous_overrun: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -70,10 +83,18 @@ pub enum Error {
|
||||
Overrun,
|
||||
/// Parity check error
|
||||
Parity,
|
||||
/// Buffer too large for DMA
|
||||
BufferTooLong,
|
||||
}
|
||||
|
||||
enum ReadCompletionEvent {
|
||||
// DMA Read transfer completed first
|
||||
DmaCompleted,
|
||||
// Idle line detected first
|
||||
Idle,
|
||||
}
|
||||
|
||||
pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
tx: UartTx<'d, T, TxDma>,
|
||||
rx: UartRx<'d, T, RxDma>,
|
||||
}
|
||||
@ -84,12 +105,65 @@ pub struct UartTx<'d, T: BasicInstance, TxDma = NoDma> {
|
||||
}
|
||||
|
||||
pub struct UartRx<'d, T: BasicInstance, RxDma = NoDma> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
_peri: PeripheralRef<'d, T>,
|
||||
rx_dma: PeripheralRef<'d, RxDma>,
|
||||
detect_previous_overrun: bool,
|
||||
}
|
||||
|
||||
impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> {
|
||||
fn new(tx_dma: PeripheralRef<'d, TxDma>) -> Self {
|
||||
/// usefull if you only want Uart Tx. It saves 1 pin and consumes a little less power
|
||||
pub fn new(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
tx_dma: impl Peripheral<P = TxDma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
Self::new_inner(peri, tx, tx_dma, config)
|
||||
}
|
||||
|
||||
pub fn new_with_cts(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
cts: impl Peripheral<P = impl CtsPin<T>> + 'd,
|
||||
tx_dma: impl Peripheral<P = TxDma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(cts);
|
||||
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
unsafe {
|
||||
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)
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
tx_dma: impl Peripheral<P = TxDma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(_peri, tx, tx_dma);
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
unsafe {
|
||||
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
|
||||
}
|
||||
|
||||
configure(r, &config, T::frequency(), T::MULTIPLIER, false, true);
|
||||
|
||||
// create state once!
|
||||
let _s = T::state();
|
||||
|
||||
Self {
|
||||
tx_dma,
|
||||
phantom: PhantomData,
|
||||
@ -135,10 +209,120 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> {
|
||||
}
|
||||
|
||||
impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
||||
fn new(rx_dma: PeripheralRef<'d, RxDma>) -> Self {
|
||||
/// usefull if you only want Uart Rx. It saves 1 pin and consumes a little less power
|
||||
pub fn new(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
rx_dma: impl Peripheral<P = RxDma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
Self::new_inner(peri, irq, rx, rx_dma, config)
|
||||
}
|
||||
|
||||
pub fn new_with_rts(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
rts: impl Peripheral<P = impl RtsPin<T>> + 'd,
|
||||
rx_dma: impl Peripheral<P = RxDma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(rts);
|
||||
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
unsafe {
|
||||
rts.set_as_af(rts.af_num(), AFType::OutputPushPull);
|
||||
T::regs().cr3().write(|w| {
|
||||
w.set_rtse(true);
|
||||
});
|
||||
}
|
||||
|
||||
Self::new_inner(peri, irq, rx, rx_dma, config)
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
rx_dma: impl Peripheral<P = RxDma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(peri, irq, rx, rx_dma);
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
unsafe {
|
||||
rx.set_as_af(rx.af_num(), AFType::Input);
|
||||
}
|
||||
|
||||
configure(r, &config, T::frequency(), T::MULTIPLIER, true, false);
|
||||
|
||||
irq.set_handler(Self::on_interrupt);
|
||||
irq.unpend();
|
||||
irq.enable();
|
||||
|
||||
// create state once!
|
||||
let _s = T::state();
|
||||
|
||||
Self {
|
||||
_peri: peri,
|
||||
rx_dma,
|
||||
phantom: PhantomData,
|
||||
detect_previous_overrun: config.detect_previous_overrun,
|
||||
}
|
||||
}
|
||||
|
||||
fn on_interrupt(_: *mut ()) {
|
||||
let r = T::regs();
|
||||
let s = T::state();
|
||||
|
||||
let (sr, cr1, cr3) = unsafe { (sr(r).read(), r.cr1().read(), r.cr3().read()) };
|
||||
|
||||
let has_errors = (sr.pe() && cr1.peie()) || ((sr.fe() || sr.ne() || sr.ore()) && cr3.eie());
|
||||
|
||||
if has_errors {
|
||||
// clear all interrupts and DMA Rx Request
|
||||
unsafe {
|
||||
r.cr1().modify(|w| {
|
||||
// disable RXNE interrupt
|
||||
w.set_rxneie(false);
|
||||
// disable parity interrupt
|
||||
w.set_peie(false);
|
||||
// disable idle line interrupt
|
||||
w.set_idleie(false);
|
||||
});
|
||||
r.cr3().modify(|w| {
|
||||
// disable Error Interrupt: (Frame error, Noise error, Overrun error)
|
||||
w.set_eie(false);
|
||||
// disable DMA Rx Request
|
||||
w.set_dmar(false);
|
||||
});
|
||||
}
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
s.rx_waker.wake();
|
||||
} else if cr1.idleie() && sr.idle() {
|
||||
// IDLE detected: no more data will come
|
||||
unsafe {
|
||||
r.cr1().modify(|w| {
|
||||
// disable idle line detection
|
||||
w.set_idleie(false);
|
||||
});
|
||||
|
||||
r.cr3().modify(|w| {
|
||||
// disable DMA Rx Request
|
||||
w.set_dmar(false);
|
||||
});
|
||||
}
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
s.rx_waker.wake();
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,17 +330,8 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
||||
where
|
||||
RxDma: crate::usart::RxDma<T>,
|
||||
{
|
||||
let ch = &mut self.rx_dma;
|
||||
let request = ch.request();
|
||||
unsafe {
|
||||
T::regs().cr3().modify(|reg| {
|
||||
reg.set_dmar(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 = crate::dma::read(ch, request, rdr(T::regs()), buffer);
|
||||
transfer.await;
|
||||
self.inner_read(buffer, false).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -211,57 +386,332 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error>
|
||||
where
|
||||
RxDma: crate::usart::RxDma<T>,
|
||||
{
|
||||
self.inner_read(buffer, true).await
|
||||
}
|
||||
|
||||
async fn inner_read_run(
|
||||
&mut self,
|
||||
buffer: &mut [u8],
|
||||
enable_idle_line_detection: bool,
|
||||
) -> Result<ReadCompletionEvent, Error>
|
||||
where
|
||||
RxDma: crate::usart::RxDma<T>,
|
||||
{
|
||||
let r = T::regs();
|
||||
|
||||
// make sure USART state is restored to neutral state when this future is dropped
|
||||
let on_drop = OnDrop::new(move || {
|
||||
// defmt::trace!("Clear all USART interrupts and DMA Read Request");
|
||||
// clear all interrupts and DMA Rx Request
|
||||
// SAFETY: only clears Rx related flags
|
||||
unsafe {
|
||||
r.cr1().modify(|w| {
|
||||
// disable RXNE interrupt
|
||||
w.set_rxneie(false);
|
||||
// disable parity interrupt
|
||||
w.set_peie(false);
|
||||
// disable idle line interrupt
|
||||
w.set_idleie(false);
|
||||
});
|
||||
r.cr3().modify(|w| {
|
||||
// disable Error Interrupt: (Frame error, Noise error, Overrun error)
|
||||
w.set_eie(false);
|
||||
// disable DMA Rx Request
|
||||
w.set_dmar(false);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let ch = &mut self.rx_dma;
|
||||
let request = ch.request();
|
||||
|
||||
// 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 = crate::dma::read(ch, request, rdr(T::regs()), buffer);
|
||||
|
||||
// SAFETY: The only way we might have a problem is using split rx and tx
|
||||
// here we only modify or read Rx related flags, interrupts and DMA channel
|
||||
unsafe {
|
||||
// clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer
|
||||
if !self.detect_previous_overrun {
|
||||
let sr = sr(r).read();
|
||||
// This read also clears the error and idle interrupt flags on v1.
|
||||
rdr(r).read_volatile();
|
||||
clear_interrupt_flags(r, sr);
|
||||
}
|
||||
|
||||
r.cr1().modify(|w| {
|
||||
// disable RXNE interrupt
|
||||
w.set_rxneie(false);
|
||||
// enable parity interrupt if not ParityNone
|
||||
w.set_peie(w.pce());
|
||||
});
|
||||
|
||||
r.cr3().modify(|w| {
|
||||
// enable Error Interrupt: (Frame error, Noise error, Overrun error)
|
||||
w.set_eie(true);
|
||||
// enable DMA Rx Request
|
||||
w.set_dmar(true);
|
||||
});
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
// In case of errors already pending when reception started, interrupts may have already been raised
|
||||
// and lead to reception abortion (Overrun error for instance). In such a case, all interrupts
|
||||
// have been disabled in interrupt handler and DMA Rx Request has been disabled.
|
||||
|
||||
let cr3 = r.cr3().read();
|
||||
|
||||
if !cr3.dmar() {
|
||||
// something went wrong
|
||||
// because the only way to get this flag cleared is to have an interrupt
|
||||
|
||||
// DMA will be stopped when transfer is dropped
|
||||
|
||||
let sr = sr(r).read();
|
||||
// This read also clears the error and idle interrupt flags on v1.
|
||||
rdr(r).read_volatile();
|
||||
clear_interrupt_flags(r, sr);
|
||||
|
||||
if sr.pe() {
|
||||
return Err(Error::Parity);
|
||||
}
|
||||
if sr.fe() {
|
||||
return Err(Error::Framing);
|
||||
}
|
||||
if sr.ne() {
|
||||
return Err(Error::Noise);
|
||||
}
|
||||
if sr.ore() {
|
||||
return Err(Error::Overrun);
|
||||
}
|
||||
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
if !enable_idle_line_detection {
|
||||
transfer.await;
|
||||
|
||||
return Ok(ReadCompletionEvent::DmaCompleted);
|
||||
}
|
||||
|
||||
// clear idle flag
|
||||
let sr = sr(r).read();
|
||||
// This read also clears the error and idle interrupt flags on v1.
|
||||
rdr(r).read_volatile();
|
||||
clear_interrupt_flags(r, sr);
|
||||
|
||||
// enable idle interrupt
|
||||
r.cr1().modify(|w| {
|
||||
w.set_idleie(true);
|
||||
});
|
||||
}
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
// future which completes when idle line is detected
|
||||
let idle = poll_fn(move |cx| {
|
||||
let s = T::state();
|
||||
|
||||
s.rx_waker.register(cx.waker());
|
||||
|
||||
// SAFETY: read only and we only use Rx related flags
|
||||
let sr = unsafe { sr(r).read() };
|
||||
|
||||
// SAFETY: only clears Rx related flags
|
||||
unsafe {
|
||||
// This read also clears the error and idle interrupt flags on v1.
|
||||
rdr(r).read_volatile();
|
||||
clear_interrupt_flags(r, sr);
|
||||
}
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
let has_errors = sr.pe() || sr.fe() || sr.ne() || sr.ore();
|
||||
|
||||
if has_errors {
|
||||
// all Rx interrupts and Rx DMA Request have already been cleared in interrupt handler
|
||||
|
||||
if sr.pe() {
|
||||
return Poll::Ready(Err(Error::Parity));
|
||||
}
|
||||
if sr.fe() {
|
||||
return Poll::Ready(Err(Error::Framing));
|
||||
}
|
||||
if sr.ne() {
|
||||
return Poll::Ready(Err(Error::Noise));
|
||||
}
|
||||
if sr.ore() {
|
||||
return Poll::Ready(Err(Error::Overrun));
|
||||
}
|
||||
}
|
||||
|
||||
if sr.idle() {
|
||||
// Idle line detected
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
});
|
||||
|
||||
// wait for the first of DMA request or idle line detected to completes
|
||||
// select consumes its arguments
|
||||
// when transfer is dropped, it will stop the DMA request
|
||||
let r = match select(transfer, idle).await {
|
||||
// DMA transfer completed first
|
||||
Either::First(()) => Ok(ReadCompletionEvent::DmaCompleted),
|
||||
|
||||
// Idle line detected first
|
||||
Either::Second(Ok(())) => Ok(ReadCompletionEvent::Idle),
|
||||
|
||||
// error occurred
|
||||
Either::Second(Err(e)) => Err(e),
|
||||
};
|
||||
|
||||
drop(on_drop);
|
||||
|
||||
r
|
||||
}
|
||||
|
||||
async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result<usize, Error>
|
||||
where
|
||||
RxDma: crate::usart::RxDma<T>,
|
||||
{
|
||||
if buffer.is_empty() {
|
||||
return Ok(0);
|
||||
} else if buffer.len() > 0xFFFF {
|
||||
return Err(Error::BufferTooLong);
|
||||
}
|
||||
|
||||
let buffer_len = buffer.len();
|
||||
|
||||
// wait for DMA to complete or IDLE line detection if requested
|
||||
let res = self.inner_read_run(buffer, enable_idle_line_detection).await;
|
||||
|
||||
let ch = &mut self.rx_dma;
|
||||
|
||||
match res {
|
||||
Ok(ReadCompletionEvent::DmaCompleted) => Ok(buffer_len),
|
||||
Ok(ReadCompletionEvent::Idle) => {
|
||||
let n = buffer_len - (ch.remaining_transfers() as usize);
|
||||
Ok(n)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
|
||||
pub fn new(
|
||||
_inner: impl Peripheral<P = T> + 'd,
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
tx_dma: impl Peripheral<P = TxDma> + 'd,
|
||||
rx_dma: impl Peripheral<P = RxDma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(_inner, rx, tx, tx_dma, rx_dma);
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config)
|
||||
}
|
||||
|
||||
pub fn new_with_rtscts(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
rts: impl Peripheral<P = impl RtsPin<T>> + 'd,
|
||||
cts: impl Peripheral<P = impl CtsPin<T>> + 'd,
|
||||
tx_dma: impl Peripheral<P = TxDma> + 'd,
|
||||
rx_dma: impl Peripheral<P = RxDma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(cts, rts);
|
||||
|
||||
T::enable();
|
||||
T::reset();
|
||||
let pclk_freq = T::frequency();
|
||||
|
||||
// TODO: better calculation, including error checking and OVER8 if possible.
|
||||
let div = (pclk_freq.0 + (config.baudrate / 2)) / config.baudrate * T::MULTIPLIER;
|
||||
unsafe {
|
||||
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(peri, rx, tx, irq, tx_dma, rx_dma, config)
|
||||
}
|
||||
|
||||
#[cfg(not(usart_v1))]
|
||||
pub fn new_with_de(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
de: impl Peripheral<P = impl DePin<T>> + 'd,
|
||||
tx_dma: impl Peripheral<P = TxDma> + 'd,
|
||||
rx_dma: impl Peripheral<P = RxDma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(de);
|
||||
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
unsafe {
|
||||
de.set_as_af(de.af_num(), AFType::OutputPushPull);
|
||||
T::regs().cr3().write(|w| {
|
||||
w.set_dem(true);
|
||||
});
|
||||
}
|
||||
Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config)
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
tx_dma: impl Peripheral<P = TxDma> + 'd,
|
||||
rx_dma: impl Peripheral<P = RxDma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(peri, rx, tx, irq, tx_dma, rx_dma);
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
unsafe {
|
||||
rx.set_as_af(rx.af_num(), AFType::Input);
|
||||
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
|
||||
|
||||
r.cr2().write(|_w| {});
|
||||
r.cr3().write(|_w| {});
|
||||
r.brr().write_value(regs::Brr(div));
|
||||
r.cr1().write(|w| {
|
||||
w.set_ue(true);
|
||||
w.set_te(true);
|
||||
w.set_re(true);
|
||||
w.set_m0(if config.parity != Parity::ParityNone {
|
||||
vals::M0::BIT9
|
||||
} else {
|
||||
vals::M0::BIT8
|
||||
});
|
||||
w.set_pce(config.parity != Parity::ParityNone);
|
||||
w.set_ps(match config.parity {
|
||||
Parity::ParityOdd => vals::Ps::ODD,
|
||||
Parity::ParityEven => vals::Ps::EVEN,
|
||||
_ => vals::Ps::EVEN,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
configure(r, &config, T::frequency(), T::MULTIPLIER, true, true);
|
||||
|
||||
irq.set_handler(UartRx::<T, RxDma>::on_interrupt);
|
||||
irq.unpend();
|
||||
irq.enable();
|
||||
|
||||
// create state once!
|
||||
let _s = T::state();
|
||||
|
||||
Self {
|
||||
tx: UartTx::new(tx_dma),
|
||||
rx: UartRx::new(rx_dma),
|
||||
phantom: PhantomData {},
|
||||
tx: UartTx {
|
||||
tx_dma,
|
||||
phantom: PhantomData,
|
||||
},
|
||||
rx: UartRx {
|
||||
_peri: peri,
|
||||
rx_dma,
|
||||
detect_previous_overrun: config.detect_previous_overrun,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -295,6 +745,13 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
|
||||
self.rx.blocking_read(buffer)
|
||||
}
|
||||
|
||||
pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error>
|
||||
where
|
||||
RxDma: crate::usart::RxDma<T>,
|
||||
{
|
||||
self.rx.read_until_idle(buffer).await
|
||||
}
|
||||
|
||||
/// Split the Uart into a transmitter and receiver, which is
|
||||
/// particuarly useful when having two tasks correlating to
|
||||
/// transmitting and receiving.
|
||||
@ -303,6 +760,48 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
|
||||
}
|
||||
}
|
||||
|
||||
fn configure(r: Regs, config: &Config, pclk_freq: Hertz, multiplier: u32, enable_rx: bool, enable_tx: bool) {
|
||||
if !enable_rx && !enable_tx {
|
||||
panic!("USART: At least one of RX or TX should be enabled");
|
||||
}
|
||||
|
||||
// TODO: better calculation, including error checking and OVER8 if possible.
|
||||
let div = (pclk_freq.0 + (config.baudrate / 2)) / config.baudrate * multiplier;
|
||||
|
||||
unsafe {
|
||||
r.brr().write_value(regs::Brr(div));
|
||||
r.cr2().write(|w| {
|
||||
w.set_stop(match config.stop_bits {
|
||||
StopBits::STOP0P5 => vals::Stop::STOP0P5,
|
||||
StopBits::STOP1 => vals::Stop::STOP1,
|
||||
StopBits::STOP1P5 => vals::Stop::STOP1P5,
|
||||
StopBits::STOP2 => vals::Stop::STOP2,
|
||||
});
|
||||
});
|
||||
r.cr1().write(|w| {
|
||||
// enable uart
|
||||
w.set_ue(true);
|
||||
// enable transceiver
|
||||
w.set_te(enable_tx);
|
||||
// enable receiver
|
||||
w.set_re(enable_rx);
|
||||
// configure word size
|
||||
w.set_m0(if config.parity != Parity::ParityNone {
|
||||
vals::M0::BIT9
|
||||
} else {
|
||||
vals::M0::BIT8
|
||||
});
|
||||
// configure parity
|
||||
w.set_pce(config.parity != Parity::ParityNone);
|
||||
w.set_ps(match config.parity {
|
||||
Parity::ParityOdd => vals::Ps::ODD,
|
||||
Parity::ParityEven => vals::Ps::EVEN,
|
||||
_ => vals::Ps::EVEN,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
mod eh02 {
|
||||
use super::*;
|
||||
|
||||
@ -352,6 +851,7 @@ mod eh1 {
|
||||
Self::Noise => embedded_hal_1::serial::ErrorKind::Noise,
|
||||
Self::Overrun => embedded_hal_1::serial::ErrorKind::Overrun,
|
||||
Self::Parity => embedded_hal_1::serial::ErrorKind::Parity,
|
||||
Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -421,6 +921,58 @@ mod eh1 {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
|
||||
mod eio {
|
||||
use embedded_io::asynch::Write;
|
||||
use embedded_io::Io;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<T, TxDma, RxDma> Io for Uart<'_, T, TxDma, RxDma>
|
||||
where
|
||||
T: BasicInstance,
|
||||
{
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<T, TxDma, RxDma> Write for Uart<'_, T, TxDma, RxDma>
|
||||
where
|
||||
T: BasicInstance,
|
||||
TxDma: super::TxDma<T>,
|
||||
{
|
||||
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
self.write(buf).await?;
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
self.blocking_flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, TxDma> Io for UartTx<'_, T, TxDma>
|
||||
where
|
||||
T: BasicInstance,
|
||||
{
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<T, TxDma> Write for UartTx<'_, T, TxDma>
|
||||
where
|
||||
T: BasicInstance,
|
||||
TxDma: super::TxDma<T>,
|
||||
{
|
||||
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
self.write(buf).await?;
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
self.blocking_flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
feature = "unstable-traits",
|
||||
feature = "nightly",
|
||||
@ -536,13 +1088,30 @@ unsafe fn clear_interrupt_flags(r: Regs, sr: regs::Isr) {
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct State {
|
||||
pub rx_waker: AtomicWaker,
|
||||
pub tx_waker: AtomicWaker,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
rx_waker: AtomicWaker::new(),
|
||||
tx_waker: AtomicWaker::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BasicInstance: crate::rcc::RccPeripheral {
|
||||
const MULTIPLIER: u32;
|
||||
type Interrupt: crate::interrupt::Interrupt;
|
||||
|
||||
fn regs() -> Regs;
|
||||
fn state() -> &'static State;
|
||||
}
|
||||
|
||||
pub trait FullInstance: BasicInstance {
|
||||
@ -550,7 +1119,7 @@ pub(crate) mod sealed {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BasicInstance: sealed::BasicInstance {}
|
||||
pub trait BasicInstance: Peripheral<P = Self> + sealed::BasicInstance + 'static + Send {}
|
||||
|
||||
pub trait FullInstance: sealed::FullInstance {}
|
||||
|
||||
@ -559,6 +1128,7 @@ pin_trait!(TxPin, BasicInstance);
|
||||
pin_trait!(CtsPin, BasicInstance);
|
||||
pin_trait!(RtsPin, BasicInstance);
|
||||
pin_trait!(CkPin, BasicInstance);
|
||||
pin_trait!(DePin, BasicInstance);
|
||||
|
||||
dma_trait!(TxDma, BasicInstance);
|
||||
dma_trait!(RxDma, BasicInstance);
|
||||
@ -572,6 +1142,11 @@ macro_rules! impl_lpuart {
|
||||
fn regs() -> Regs {
|
||||
Regs(crate::pac::$inst.0)
|
||||
}
|
||||
|
||||
fn state() -> &'static crate::usart::sealed::State {
|
||||
static STATE: crate::usart::sealed::State = crate::usart::sealed::State::new();
|
||||
&STATE
|
||||
}
|
||||
}
|
||||
|
||||
impl BasicInstance for peripherals::$inst {}
|
||||
@ -580,7 +1155,7 @@ macro_rules! impl_lpuart {
|
||||
|
||||
foreach_interrupt!(
|
||||
($inst:ident, lpuart, $block:ident, $signal_name:ident, $irq:ident) => {
|
||||
impl_lpuart!($inst, $irq, 255);
|
||||
impl_lpuart!($inst, $irq, 256);
|
||||
};
|
||||
|
||||
($inst:ident, usart, $block:ident, $signal_name:ident, $irq:ident) => {
|
||||
|
@ -1,11 +1,10 @@
|
||||
#![macro_use]
|
||||
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::sync::atomic::Ordering;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
use atomic_polyfill::{AtomicBool, AtomicU8};
|
||||
use embassy_hal_common::into_ref;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use embassy_time::{block_for, Duration};
|
||||
@ -35,10 +34,9 @@ static BUS_WAKER: AtomicWaker = NEW_AW;
|
||||
static EP0_SETUP: AtomicBool = AtomicBool::new(false);
|
||||
static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT];
|
||||
static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT];
|
||||
static IRQ_FLAGS: AtomicU8 = AtomicU8::new(0);
|
||||
const IRQ_FLAG_RESET: u8 = 0x01;
|
||||
const IRQ_FLAG_SUSPEND: u8 = 0x02;
|
||||
const IRQ_FLAG_RESUME: u8 = 0x04;
|
||||
static IRQ_RESET: AtomicBool = AtomicBool::new(false);
|
||||
static IRQ_SUSPEND: AtomicBool = AtomicBool::new(false);
|
||||
static IRQ_RESUME: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
fn convert_type(t: EndpointType) -> EpType {
|
||||
match t {
|
||||
@ -156,6 +154,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
|
||||
block_for(Duration::from_millis(100));
|
||||
|
||||
#[cfg(not(usb_v4))]
|
||||
regs.btable().write(|w| w.set_btable(0));
|
||||
|
||||
dp.set_as_af(dp.af_num(), AFType::OutputPushPull);
|
||||
@ -184,47 +183,51 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
|
||||
let istr = regs.istr().read();
|
||||
|
||||
let mut flags: u8 = 0;
|
||||
|
||||
if istr.susp() {
|
||||
//trace!("USB IRQ: susp");
|
||||
flags |= IRQ_FLAG_SUSPEND;
|
||||
IRQ_SUSPEND.store(true, Ordering::Relaxed);
|
||||
regs.cntr().modify(|w| {
|
||||
w.set_fsusp(true);
|
||||
w.set_lpmode(true);
|
||||
})
|
||||
});
|
||||
|
||||
// Write 0 to clear.
|
||||
let mut clear = regs::Istr(!0);
|
||||
clear.set_susp(false);
|
||||
regs.istr().write_value(clear);
|
||||
|
||||
// Wake main thread.
|
||||
BUS_WAKER.wake();
|
||||
}
|
||||
|
||||
if istr.wkup() {
|
||||
//trace!("USB IRQ: wkup");
|
||||
flags |= IRQ_FLAG_RESUME;
|
||||
IRQ_RESUME.store(true, Ordering::Relaxed);
|
||||
regs.cntr().modify(|w| {
|
||||
w.set_fsusp(false);
|
||||
w.set_lpmode(false);
|
||||
})
|
||||
});
|
||||
|
||||
// Write 0 to clear.
|
||||
let mut clear = regs::Istr(!0);
|
||||
clear.set_wkup(false);
|
||||
regs.istr().write_value(clear);
|
||||
|
||||
// Wake main thread.
|
||||
BUS_WAKER.wake();
|
||||
}
|
||||
|
||||
if istr.reset() {
|
||||
//trace!("USB IRQ: reset");
|
||||
flags |= IRQ_FLAG_RESET;
|
||||
IRQ_RESET.store(true, Ordering::Relaxed);
|
||||
|
||||
// Write 0 to clear.
|
||||
let mut clear = regs::Istr(!0);
|
||||
clear.set_reset(false);
|
||||
regs.istr().write_value(clear);
|
||||
}
|
||||
|
||||
if flags != 0 {
|
||||
// Send irqs to main thread.
|
||||
IRQ_FLAGS.fetch_or(flags, Ordering::AcqRel);
|
||||
// Wake main thread.
|
||||
BUS_WAKER.wake();
|
||||
|
||||
// Clear them
|
||||
let mut mask = regs::Istr(0);
|
||||
mask.set_wkup(true);
|
||||
mask.set_susp(true);
|
||||
mask.set_reset(true);
|
||||
regs.istr().write_value(regs::Istr(!(istr.0 & mask.0)));
|
||||
}
|
||||
|
||||
if istr.ctr() {
|
||||
@ -265,13 +268,13 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
&mut self,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
interval_ms: u8,
|
||||
) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> {
|
||||
trace!(
|
||||
"allocating type={:?} mps={:?} interval={}, dir={:?}",
|
||||
"allocating type={:?} mps={:?} interval_ms={}, dir={:?}",
|
||||
ep_type,
|
||||
max_packet_size,
|
||||
interval,
|
||||
interval_ms,
|
||||
D::dir()
|
||||
);
|
||||
|
||||
@ -342,7 +345,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
addr: EndpointAddress::from_parts(index, D::dir()),
|
||||
ep_type,
|
||||
max_packet_size,
|
||||
interval,
|
||||
interval_ms,
|
||||
},
|
||||
buf,
|
||||
})
|
||||
@ -359,18 +362,18 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
|
||||
&mut self,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
interval_ms: u8,
|
||||
) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
|
||||
self.alloc_endpoint(ep_type, max_packet_size, interval)
|
||||
self.alloc_endpoint(ep_type, max_packet_size, interval_ms)
|
||||
}
|
||||
|
||||
fn alloc_endpoint_out(
|
||||
&mut self,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
interval_ms: u8,
|
||||
) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
|
||||
self.alloc_endpoint(ep_type, max_packet_size, interval)
|
||||
self.alloc_endpoint(ep_type, max_packet_size, interval_ms)
|
||||
}
|
||||
|
||||
fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
|
||||
@ -429,26 +432,22 @@ pub struct Bus<'d, T: Instance> {
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
type PollFuture<'a> = impl Future<Output = Event> + 'a where Self: 'a;
|
||||
|
||||
fn poll<'a>(&'a mut self) -> Self::PollFuture<'a> {
|
||||
async fn poll(&mut self) -> Event {
|
||||
poll_fn(move |cx| unsafe {
|
||||
BUS_WAKER.register(cx.waker());
|
||||
|
||||
if self.inited {
|
||||
let regs = T::regs();
|
||||
|
||||
let flags = IRQ_FLAGS.load(Ordering::Acquire);
|
||||
|
||||
if flags & IRQ_FLAG_RESUME != 0 {
|
||||
IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESUME, Ordering::AcqRel);
|
||||
if IRQ_RESUME.load(Ordering::Acquire) {
|
||||
IRQ_RESUME.store(false, Ordering::Relaxed);
|
||||
return Poll::Ready(Event::Resume);
|
||||
}
|
||||
|
||||
if flags & IRQ_FLAG_RESET != 0 {
|
||||
IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESET, Ordering::AcqRel);
|
||||
if IRQ_RESET.load(Ordering::Acquire) {
|
||||
IRQ_RESET.store(false, Ordering::Relaxed);
|
||||
|
||||
trace!("RESET REGS WRITINGINGING");
|
||||
trace!("RESET");
|
||||
regs.daddr().write(|w| {
|
||||
w.set_ef(true);
|
||||
w.set_add(0);
|
||||
@ -477,8 +476,8 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
return Poll::Ready(Event::Reset);
|
||||
}
|
||||
|
||||
if flags & IRQ_FLAG_SUSPEND != 0 {
|
||||
IRQ_FLAGS.fetch_and(!IRQ_FLAG_SUSPEND, Ordering::AcqRel);
|
||||
if IRQ_SUSPEND.load(Ordering::Acquire) {
|
||||
IRQ_SUSPEND.store(false, Ordering::Relaxed);
|
||||
return Poll::Ready(Event::Suspend);
|
||||
}
|
||||
|
||||
@ -488,18 +487,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
return Poll::Ready(Event::PowerDetected);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_address(&mut self, addr: u8) {
|
||||
let regs = T::regs();
|
||||
trace!("setting addr: {}", addr);
|
||||
unsafe {
|
||||
regs.daddr().write(|w| {
|
||||
w.set_ef(true);
|
||||
w.set_add(addr);
|
||||
})
|
||||
}
|
||||
.await
|
||||
}
|
||||
|
||||
fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {
|
||||
@ -598,22 +586,11 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
trace!("EPR after: {:04x}", unsafe { reg.read() }.0);
|
||||
}
|
||||
|
||||
type EnableFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
|
||||
async fn enable(&mut self) {}
|
||||
async fn disable(&mut self) {}
|
||||
|
||||
fn enable(&mut self) -> Self::EnableFuture<'_> {
|
||||
async move {}
|
||||
}
|
||||
|
||||
type DisableFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
|
||||
|
||||
fn disable(&mut self) -> Self::DisableFuture<'_> {
|
||||
async move {}
|
||||
}
|
||||
|
||||
type RemoteWakeupFuture<'a> = impl Future<Output = Result<(), Unsupported>> + 'a where Self: 'a;
|
||||
|
||||
fn remote_wakeup(&mut self) -> Self::RemoteWakeupFuture<'_> {
|
||||
async move { Err(Unsupported) }
|
||||
async fn remote_wakeup(&mut self) -> Result<(), Unsupported> {
|
||||
Err(Unsupported)
|
||||
}
|
||||
}
|
||||
|
||||
@ -676,24 +653,20 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> {
|
||||
&self.info
|
||||
}
|
||||
|
||||
type WaitEnabledFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
|
||||
|
||||
fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> {
|
||||
async move {
|
||||
trace!("wait_enabled OUT WAITING");
|
||||
let index = self.info.addr.index();
|
||||
poll_fn(|cx| {
|
||||
EP_OUT_WAKERS[index].register(cx.waker());
|
||||
let regs = T::regs();
|
||||
if unsafe { regs.epr(index).read() }.stat_tx() == Stat::DISABLED {
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(())
|
||||
}
|
||||
})
|
||||
.await;
|
||||
trace!("wait_enabled OUT OK");
|
||||
}
|
||||
async fn wait_enabled(&mut self) {
|
||||
trace!("wait_enabled OUT WAITING");
|
||||
let index = self.info.addr.index();
|
||||
poll_fn(|cx| {
|
||||
EP_OUT_WAKERS[index].register(cx.waker());
|
||||
let regs = T::regs();
|
||||
if unsafe { regs.epr(index).read() }.stat_tx() == Stat::DISABLED {
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(())
|
||||
}
|
||||
})
|
||||
.await;
|
||||
trace!("wait_enabled OUT OK");
|
||||
}
|
||||
}
|
||||
|
||||
@ -702,116 +675,104 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, Out> {
|
||||
&self.info
|
||||
}
|
||||
|
||||
type WaitEnabledFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
|
||||
|
||||
fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> {
|
||||
async move {
|
||||
trace!("wait_enabled OUT WAITING");
|
||||
let index = self.info.addr.index();
|
||||
poll_fn(|cx| {
|
||||
EP_OUT_WAKERS[index].register(cx.waker());
|
||||
let regs = T::regs();
|
||||
if unsafe { regs.epr(index).read() }.stat_rx() == Stat::DISABLED {
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(())
|
||||
}
|
||||
})
|
||||
.await;
|
||||
trace!("wait_enabled OUT OK");
|
||||
}
|
||||
async fn wait_enabled(&mut self) {
|
||||
trace!("wait_enabled OUT WAITING");
|
||||
let index = self.info.addr.index();
|
||||
poll_fn(|cx| {
|
||||
EP_OUT_WAKERS[index].register(cx.waker());
|
||||
let regs = T::regs();
|
||||
if unsafe { regs.epr(index).read() }.stat_rx() == Stat::DISABLED {
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(())
|
||||
}
|
||||
})
|
||||
.await;
|
||||
trace!("wait_enabled OUT OK");
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<usize, EndpointError>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
async move {
|
||||
trace!("READ WAITING, buf.len() = {}", buf.len());
|
||||
let index = self.info.addr.index();
|
||||
let stat = poll_fn(|cx| {
|
||||
EP_OUT_WAKERS[index].register(cx.waker());
|
||||
let regs = T::regs();
|
||||
let stat = unsafe { regs.epr(index).read() }.stat_rx();
|
||||
if matches!(stat, Stat::NAK | Stat::DISABLED) {
|
||||
Poll::Ready(stat)
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
if stat == Stat::DISABLED {
|
||||
return Err(EndpointError::Disabled);
|
||||
}
|
||||
|
||||
let rx_len = self.read_data(buf)?;
|
||||
|
||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {
|
||||
trace!("READ WAITING, buf.len() = {}", buf.len());
|
||||
let index = self.info.addr.index();
|
||||
let stat = poll_fn(|cx| {
|
||||
EP_OUT_WAKERS[index].register(cx.waker());
|
||||
let regs = T::regs();
|
||||
unsafe {
|
||||
regs.epr(index).write(|w| {
|
||||
w.set_ep_type(convert_type(self.info.ep_type));
|
||||
w.set_ea(self.info.addr.index() as _);
|
||||
w.set_stat_rx(Stat(Stat::NAK.0 ^ Stat::VALID.0));
|
||||
w.set_stat_tx(Stat(0));
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
})
|
||||
};
|
||||
trace!("READ OK, rx_len = {}", rx_len);
|
||||
let stat = unsafe { regs.epr(index).read() }.stat_rx();
|
||||
if matches!(stat, Stat::NAK | Stat::DISABLED) {
|
||||
Poll::Ready(stat)
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
Ok(rx_len)
|
||||
if stat == Stat::DISABLED {
|
||||
return Err(EndpointError::Disabled);
|
||||
}
|
||||
|
||||
let rx_len = self.read_data(buf)?;
|
||||
|
||||
let regs = T::regs();
|
||||
unsafe {
|
||||
regs.epr(index).write(|w| {
|
||||
w.set_ep_type(convert_type(self.info.ep_type));
|
||||
w.set_ea(self.info.addr.index() as _);
|
||||
w.set_stat_rx(Stat(Stat::NAK.0 ^ Stat::VALID.0));
|
||||
w.set_stat_tx(Stat(0));
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
})
|
||||
};
|
||||
trace!("READ OK, rx_len = {}", rx_len);
|
||||
|
||||
Ok(rx_len)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), EndpointError>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
async move {
|
||||
if buf.len() > self.info.max_packet_size as usize {
|
||||
return Err(EndpointError::BufferOverflow);
|
||||
}
|
||||
|
||||
let index = self.info.addr.index();
|
||||
|
||||
trace!("WRITE WAITING");
|
||||
let stat = poll_fn(|cx| {
|
||||
EP_IN_WAKERS[index].register(cx.waker());
|
||||
let regs = T::regs();
|
||||
let stat = unsafe { regs.epr(index).read() }.stat_tx();
|
||||
if matches!(stat, Stat::NAK | Stat::DISABLED) {
|
||||
Poll::Ready(stat)
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
if stat == Stat::DISABLED {
|
||||
return Err(EndpointError::Disabled);
|
||||
}
|
||||
|
||||
self.write_data(buf);
|
||||
|
||||
let regs = T::regs();
|
||||
unsafe {
|
||||
regs.epr(index).write(|w| {
|
||||
w.set_ep_type(convert_type(self.info.ep_type));
|
||||
w.set_ea(self.info.addr.index() as _);
|
||||
w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0));
|
||||
w.set_stat_rx(Stat(0));
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
})
|
||||
};
|
||||
|
||||
trace!("WRITE OK");
|
||||
|
||||
Ok(())
|
||||
async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> {
|
||||
if buf.len() > self.info.max_packet_size as usize {
|
||||
return Err(EndpointError::BufferOverflow);
|
||||
}
|
||||
|
||||
let index = self.info.addr.index();
|
||||
|
||||
trace!("WRITE WAITING");
|
||||
let stat = poll_fn(|cx| {
|
||||
EP_IN_WAKERS[index].register(cx.waker());
|
||||
let regs = T::regs();
|
||||
let stat = unsafe { regs.epr(index).read() }.stat_tx();
|
||||
if matches!(stat, Stat::NAK | Stat::DISABLED) {
|
||||
Poll::Ready(stat)
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
if stat == Stat::DISABLED {
|
||||
return Err(EndpointError::Disabled);
|
||||
}
|
||||
|
||||
self.write_data(buf);
|
||||
|
||||
let regs = T::regs();
|
||||
unsafe {
|
||||
regs.epr(index).write(|w| {
|
||||
w.set_ep_type(convert_type(self.info.ep_type));
|
||||
w.set_ea(self.info.addr.index() as _);
|
||||
w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0));
|
||||
w.set_stat_rx(Stat(0));
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
})
|
||||
};
|
||||
|
||||
trace!("WRITE OK");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -823,84 +784,16 @@ pub struct ControlPipe<'d, T: Instance> {
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
|
||||
type SetupFuture<'a> = impl Future<Output = [u8;8]> + 'a where Self: 'a;
|
||||
type DataOutFuture<'a> = impl Future<Output = Result<usize, EndpointError>> + 'a where Self: 'a;
|
||||
type DataInFuture<'a> = impl Future<Output = Result<(), EndpointError>> + 'a where Self: 'a;
|
||||
type AcceptFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
|
||||
type RejectFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
|
||||
|
||||
fn max_packet_size(&self) -> usize {
|
||||
usize::from(self.max_packet_size)
|
||||
}
|
||||
|
||||
fn setup<'a>(&'a mut self) -> Self::SetupFuture<'a> {
|
||||
async move {
|
||||
loop {
|
||||
trace!("SETUP read waiting");
|
||||
poll_fn(|cx| {
|
||||
EP_OUT_WAKERS[0].register(cx.waker());
|
||||
if EP0_SETUP.load(Ordering::Relaxed) {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
let mut buf = [0; 8];
|
||||
let rx_len = self.ep_out.read_data(&mut buf);
|
||||
if rx_len != Ok(8) {
|
||||
trace!("SETUP read failed: {:?}", rx_len);
|
||||
continue;
|
||||
}
|
||||
|
||||
EP0_SETUP.store(false, Ordering::Relaxed);
|
||||
|
||||
trace!("SETUP read ok");
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn data_out<'a>(&'a mut self, buf: &'a mut [u8], first: bool, last: bool) -> Self::DataOutFuture<'a> {
|
||||
async move {
|
||||
let regs = T::regs();
|
||||
|
||||
// When a SETUP is received, Stat/Stat is set to NAK.
|
||||
// On first transfer, we must set Stat=VALID, to get the OUT data stage.
|
||||
// We want Stat=STALL so that the host gets a STALL if it switches to the status
|
||||
// stage too soon, except in the last transfer we set Stat=NAK so that it waits
|
||||
// for the status stage, which we will ACK or STALL later.
|
||||
if first || last {
|
||||
let mut stat_rx = 0;
|
||||
let mut stat_tx = 0;
|
||||
if first {
|
||||
// change NAK -> VALID
|
||||
stat_rx ^= Stat::NAK.0 ^ Stat::VALID.0;
|
||||
stat_tx ^= Stat::NAK.0 ^ Stat::STALL.0;
|
||||
}
|
||||
if last {
|
||||
// change STALL -> VALID
|
||||
stat_tx ^= Stat::STALL.0 ^ Stat::NAK.0;
|
||||
}
|
||||
// Note: if this is the first AND last transfer, the above effectively
|
||||
// changes stat_tx like NAK -> NAK, so noop.
|
||||
unsafe {
|
||||
regs.epr(0).write(|w| {
|
||||
w.set_ep_type(EpType::CONTROL);
|
||||
w.set_stat_rx(Stat(stat_rx));
|
||||
w.set_stat_tx(Stat(stat_tx));
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
trace!("data_out WAITING, buf.len() = {}", buf.len());
|
||||
async fn setup(&mut self) -> [u8; 8] {
|
||||
loop {
|
||||
trace!("SETUP read waiting");
|
||||
poll_fn(|cx| {
|
||||
EP_OUT_WAKERS[0].register(cx.waker());
|
||||
let regs = T::regs();
|
||||
if unsafe { regs.epr(0).read() }.stat_rx() == Stat::NAK {
|
||||
if EP0_SETUP.load(Ordering::Relaxed) {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
@ -908,157 +801,222 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
|
||||
})
|
||||
.await;
|
||||
|
||||
if EP0_SETUP.load(Ordering::Relaxed) {
|
||||
trace!("received another SETUP, aborting data_out.");
|
||||
return Err(EndpointError::Disabled);
|
||||
let mut buf = [0; 8];
|
||||
let rx_len = self.ep_out.read_data(&mut buf);
|
||||
if rx_len != Ok(8) {
|
||||
trace!("SETUP read failed: {:?}", rx_len);
|
||||
continue;
|
||||
}
|
||||
|
||||
let rx_len = self.ep_out.read_data(buf)?;
|
||||
EP0_SETUP.store(false, Ordering::Relaxed);
|
||||
|
||||
trace!("SETUP read ok");
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result<usize, EndpointError> {
|
||||
let regs = T::regs();
|
||||
|
||||
// When a SETUP is received, Stat/Stat is set to NAK.
|
||||
// On first transfer, we must set Stat=VALID, to get the OUT data stage.
|
||||
// We want Stat=STALL so that the host gets a STALL if it switches to the status
|
||||
// stage too soon, except in the last transfer we set Stat=NAK so that it waits
|
||||
// for the status stage, which we will ACK or STALL later.
|
||||
if first || last {
|
||||
let mut stat_rx = 0;
|
||||
let mut stat_tx = 0;
|
||||
if first {
|
||||
// change NAK -> VALID
|
||||
stat_rx ^= Stat::NAK.0 ^ Stat::VALID.0;
|
||||
stat_tx ^= Stat::NAK.0 ^ Stat::STALL.0;
|
||||
}
|
||||
if last {
|
||||
// change STALL -> VALID
|
||||
stat_tx ^= Stat::STALL.0 ^ Stat::NAK.0;
|
||||
}
|
||||
// Note: if this is the first AND last transfer, the above effectively
|
||||
// changes stat_tx like NAK -> NAK, so noop.
|
||||
unsafe {
|
||||
regs.epr(0).write(|w| {
|
||||
w.set_ep_type(EpType::CONTROL);
|
||||
w.set_stat_rx(Stat(match last {
|
||||
// If last, set STAT_RX=STALL.
|
||||
true => Stat::NAK.0 ^ Stat::STALL.0,
|
||||
// Otherwise, set STAT_RX=VALID, to allow the host to send the next packet.
|
||||
false => Stat::NAK.0 ^ Stat::VALID.0,
|
||||
}));
|
||||
w.set_stat_rx(Stat(stat_rx));
|
||||
w.set_stat_tx(Stat(stat_tx));
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
})
|
||||
};
|
||||
|
||||
Ok(rx_len)
|
||||
}
|
||||
}
|
||||
|
||||
trace!("data_out WAITING, buf.len() = {}", buf.len());
|
||||
poll_fn(|cx| {
|
||||
EP_OUT_WAKERS[0].register(cx.waker());
|
||||
let regs = T::regs();
|
||||
if unsafe { regs.epr(0).read() }.stat_rx() == Stat::NAK {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
if EP0_SETUP.load(Ordering::Relaxed) {
|
||||
trace!("received another SETUP, aborting data_out.");
|
||||
return Err(EndpointError::Disabled);
|
||||
}
|
||||
|
||||
let rx_len = self.ep_out.read_data(buf)?;
|
||||
|
||||
unsafe {
|
||||
regs.epr(0).write(|w| {
|
||||
w.set_ep_type(EpType::CONTROL);
|
||||
w.set_stat_rx(Stat(match last {
|
||||
// If last, set STAT_RX=STALL.
|
||||
true => Stat::NAK.0 ^ Stat::STALL.0,
|
||||
// Otherwise, set STAT_RX=VALID, to allow the host to send the next packet.
|
||||
false => Stat::NAK.0 ^ Stat::VALID.0,
|
||||
}));
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
})
|
||||
};
|
||||
|
||||
Ok(rx_len)
|
||||
}
|
||||
|
||||
fn data_in<'a>(&'a mut self, buf: &'a [u8], first: bool, last: bool) -> Self::DataInFuture<'a> {
|
||||
async move {
|
||||
trace!("control: data_in");
|
||||
async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError> {
|
||||
trace!("control: data_in");
|
||||
|
||||
if buf.len() > self.ep_in.info.max_packet_size as usize {
|
||||
return Err(EndpointError::BufferOverflow);
|
||||
if data.len() > self.ep_in.info.max_packet_size as usize {
|
||||
return Err(EndpointError::BufferOverflow);
|
||||
}
|
||||
|
||||
let regs = T::regs();
|
||||
|
||||
// When a SETUP is received, Stat is set to NAK.
|
||||
// We want it to be STALL in non-last transfers.
|
||||
// We want it to be VALID in last transfer, so the HW does the status stage.
|
||||
if first || last {
|
||||
let mut stat_rx = 0;
|
||||
if first {
|
||||
// change NAK -> STALL
|
||||
stat_rx ^= Stat::NAK.0 ^ Stat::STALL.0;
|
||||
}
|
||||
|
||||
let regs = T::regs();
|
||||
|
||||
// When a SETUP is received, Stat is set to NAK.
|
||||
// We want it to be STALL in non-last transfers.
|
||||
// We want it to be VALID in last transfer, so the HW does the status stage.
|
||||
if first || last {
|
||||
let mut stat_rx = 0;
|
||||
if first {
|
||||
// change NAK -> STALL
|
||||
stat_rx ^= Stat::NAK.0 ^ Stat::STALL.0;
|
||||
}
|
||||
if last {
|
||||
// change STALL -> VALID
|
||||
stat_rx ^= Stat::STALL.0 ^ Stat::VALID.0;
|
||||
}
|
||||
// Note: if this is the first AND last transfer, the above effectively
|
||||
// does a change of NAK -> VALID.
|
||||
unsafe {
|
||||
regs.epr(0).write(|w| {
|
||||
w.set_ep_type(EpType::CONTROL);
|
||||
w.set_stat_rx(Stat(stat_rx));
|
||||
w.set_ep_kind(last); // set OUT_STATUS if last.
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
})
|
||||
}
|
||||
if last {
|
||||
// change STALL -> VALID
|
||||
stat_rx ^= Stat::STALL.0 ^ Stat::VALID.0;
|
||||
}
|
||||
|
||||
trace!("WRITE WAITING");
|
||||
poll_fn(|cx| {
|
||||
EP_IN_WAKERS[0].register(cx.waker());
|
||||
EP_OUT_WAKERS[0].register(cx.waker());
|
||||
let regs = T::regs();
|
||||
if unsafe { regs.epr(0).read() }.stat_tx() == Stat::NAK {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
if EP0_SETUP.load(Ordering::Relaxed) {
|
||||
trace!("received another SETUP, aborting data_in.");
|
||||
return Err(EndpointError::Disabled);
|
||||
}
|
||||
|
||||
self.ep_in.write_data(buf);
|
||||
|
||||
let regs = T::regs();
|
||||
// Note: if this is the first AND last transfer, the above effectively
|
||||
// does a change of NAK -> VALID.
|
||||
unsafe {
|
||||
regs.epr(0).write(|w| {
|
||||
w.set_ep_type(EpType::CONTROL);
|
||||
w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0));
|
||||
w.set_stat_rx(Stat(stat_rx));
|
||||
w.set_ep_kind(last); // set OUT_STATUS if last.
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
})
|
||||
};
|
||||
|
||||
trace!("WRITE OK");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn accept<'a>(&'a mut self) -> Self::AcceptFuture<'a> {
|
||||
async move {
|
||||
let regs = T::regs();
|
||||
trace!("control: accept");
|
||||
|
||||
self.ep_in.write_data(&[]);
|
||||
|
||||
// Set OUT=stall, IN=accept
|
||||
unsafe {
|
||||
let epr = regs.epr(0).read();
|
||||
regs.epr(0).write(|w| {
|
||||
w.set_ep_type(EpType::CONTROL);
|
||||
w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0));
|
||||
w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::VALID.0));
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
});
|
||||
}
|
||||
trace!("control: accept WAITING");
|
||||
}
|
||||
|
||||
// Wait is needed, so that we don't set the address too soon, breaking the status stage.
|
||||
// (embassy-usb sets the address after accept() returns)
|
||||
poll_fn(|cx| {
|
||||
EP_IN_WAKERS[0].register(cx.waker());
|
||||
let regs = T::regs();
|
||||
if unsafe { regs.epr(0).read() }.stat_tx() == Stat::NAK {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
trace!("WRITE WAITING");
|
||||
poll_fn(|cx| {
|
||||
EP_IN_WAKERS[0].register(cx.waker());
|
||||
EP_OUT_WAKERS[0].register(cx.waker());
|
||||
let regs = T::regs();
|
||||
if unsafe { regs.epr(0).read() }.stat_tx() == Stat::NAK {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
if EP0_SETUP.load(Ordering::Relaxed) {
|
||||
trace!("received another SETUP, aborting data_in.");
|
||||
return Err(EndpointError::Disabled);
|
||||
}
|
||||
|
||||
self.ep_in.write_data(data);
|
||||
|
||||
let regs = T::regs();
|
||||
unsafe {
|
||||
regs.epr(0).write(|w| {
|
||||
w.set_ep_type(EpType::CONTROL);
|
||||
w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0));
|
||||
w.set_ep_kind(last); // set OUT_STATUS if last.
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
})
|
||||
.await;
|
||||
};
|
||||
|
||||
trace!("control: accept OK");
|
||||
trace!("WRITE OK");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn accept(&mut self) {
|
||||
let regs = T::regs();
|
||||
trace!("control: accept");
|
||||
|
||||
self.ep_in.write_data(&[]);
|
||||
|
||||
// Set OUT=stall, IN=accept
|
||||
unsafe {
|
||||
let epr = regs.epr(0).read();
|
||||
regs.epr(0).write(|w| {
|
||||
w.set_ep_type(EpType::CONTROL);
|
||||
w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0));
|
||||
w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::VALID.0));
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
});
|
||||
}
|
||||
trace!("control: accept WAITING");
|
||||
|
||||
// Wait is needed, so that we don't set the address too soon, breaking the status stage.
|
||||
// (embassy-usb sets the address after accept() returns)
|
||||
poll_fn(|cx| {
|
||||
EP_IN_WAKERS[0].register(cx.waker());
|
||||
let regs = T::regs();
|
||||
if unsafe { regs.epr(0).read() }.stat_tx() == Stat::NAK {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
trace!("control: accept OK");
|
||||
}
|
||||
|
||||
async fn reject(&mut self) {
|
||||
let regs = T::regs();
|
||||
trace!("control: reject");
|
||||
|
||||
// Set IN+OUT to stall
|
||||
unsafe {
|
||||
let epr = regs.epr(0).read();
|
||||
regs.epr(0).write(|w| {
|
||||
w.set_ep_type(EpType::CONTROL);
|
||||
w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0));
|
||||
w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::STALL.0));
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn reject<'a>(&'a mut self) -> Self::RejectFuture<'a> {
|
||||
async move {
|
||||
let regs = T::regs();
|
||||
trace!("control: reject");
|
||||
async fn accept_set_address(&mut self, addr: u8) {
|
||||
self.accept().await;
|
||||
|
||||
// Set IN+OUT to stall
|
||||
unsafe {
|
||||
let epr = regs.epr(0).read();
|
||||
regs.epr(0).write(|w| {
|
||||
w.set_ep_type(EpType::CONTROL);
|
||||
w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0));
|
||||
w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::STALL.0));
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
});
|
||||
}
|
||||
let regs = T::regs();
|
||||
trace!("setting addr: {}", addr);
|
||||
unsafe {
|
||||
regs.daddr().write(|w| {
|
||||
w.set_ef(true);
|
||||
w.set_add(addr);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,213 +0,0 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_common::into_ref;
|
||||
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::rcc::RccPeripheral;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
macro_rules! config_ulpi_pins {
|
||||
($($pin:ident),*) => {
|
||||
into_ref!($($pin),*);
|
||||
// NOTE(unsafe) Exclusive access to the registers
|
||||
critical_section::with(|_| unsafe {
|
||||
$(
|
||||
$pin.set_as_af($pin.af_num(), AFType::OutputPushPull);
|
||||
#[cfg(gpio_v2)]
|
||||
$pin.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
)*
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
/// USB PHY type
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum PhyType {
|
||||
/// Internal Full-Speed PHY
|
||||
///
|
||||
/// Available on most High-Speed peripherals.
|
||||
InternalFullSpeed,
|
||||
/// Internal High-Speed PHY
|
||||
///
|
||||
/// Available on a few STM32 chips.
|
||||
InternalHighSpeed,
|
||||
/// External ULPI High-Speed PHY
|
||||
ExternalHighSpeed,
|
||||
}
|
||||
|
||||
pub struct UsbOtg<'d, T: Instance> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
_phy_type: PhyType,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> UsbOtg<'d, T> {
|
||||
/// Initializes USB OTG peripheral with internal Full-Speed PHY
|
||||
pub fn new_fs(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
dp: impl Peripheral<P = impl DpPin<T>> + 'd,
|
||||
dm: impl Peripheral<P = impl DmPin<T>> + 'd,
|
||||
) -> Self {
|
||||
into_ref!(dp, dm);
|
||||
|
||||
unsafe {
|
||||
dp.set_as_af(dp.af_num(), AFType::OutputPushPull);
|
||||
dm.set_as_af(dm.af_num(), AFType::OutputPushPull);
|
||||
}
|
||||
|
||||
Self {
|
||||
phantom: PhantomData,
|
||||
_phy_type: PhyType::InternalFullSpeed,
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes USB OTG peripheral with external High-Speed PHY
|
||||
pub fn new_hs_ulpi(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
ulpi_clk: impl Peripheral<P = impl UlpiClkPin<T>> + 'd,
|
||||
ulpi_dir: impl Peripheral<P = impl UlpiDirPin<T>> + 'd,
|
||||
ulpi_nxt: impl Peripheral<P = impl UlpiNxtPin<T>> + 'd,
|
||||
ulpi_stp: impl Peripheral<P = impl UlpiStpPin<T>> + 'd,
|
||||
ulpi_d0: impl Peripheral<P = impl UlpiD0Pin<T>> + 'd,
|
||||
ulpi_d1: impl Peripheral<P = impl UlpiD1Pin<T>> + 'd,
|
||||
ulpi_d2: impl Peripheral<P = impl UlpiD2Pin<T>> + 'd,
|
||||
ulpi_d3: impl Peripheral<P = impl UlpiD3Pin<T>> + 'd,
|
||||
ulpi_d4: impl Peripheral<P = impl UlpiD4Pin<T>> + 'd,
|
||||
ulpi_d5: impl Peripheral<P = impl UlpiD5Pin<T>> + 'd,
|
||||
ulpi_d6: impl Peripheral<P = impl UlpiD6Pin<T>> + 'd,
|
||||
ulpi_d7: impl Peripheral<P = impl UlpiD7Pin<T>> + 'd,
|
||||
) -> Self {
|
||||
config_ulpi_pins!(
|
||||
ulpi_clk, ulpi_dir, ulpi_nxt, ulpi_stp, ulpi_d0, ulpi_d1, ulpi_d2, ulpi_d3, ulpi_d4, ulpi_d5, ulpi_d6,
|
||||
ulpi_d7
|
||||
);
|
||||
|
||||
Self {
|
||||
phantom: PhantomData,
|
||||
_phy_type: PhyType::ExternalHighSpeed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Drop for UsbOtg<'d, T> {
|
||||
fn drop(&mut self) {
|
||||
T::reset();
|
||||
T::disable();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait Instance {
|
||||
const REGISTERS: *const ();
|
||||
const HIGH_SPEED: bool;
|
||||
const FIFO_DEPTH_WORDS: usize;
|
||||
const ENDPOINT_COUNT: usize;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance: sealed::Instance + RccPeripheral {}
|
||||
|
||||
// Internal PHY pins
|
||||
pin_trait!(DpPin, Instance);
|
||||
pin_trait!(DmPin, Instance);
|
||||
|
||||
// External PHY pins
|
||||
pin_trait!(UlpiClkPin, Instance);
|
||||
pin_trait!(UlpiDirPin, Instance);
|
||||
pin_trait!(UlpiNxtPin, Instance);
|
||||
pin_trait!(UlpiStpPin, Instance);
|
||||
pin_trait!(UlpiD0Pin, Instance);
|
||||
pin_trait!(UlpiD1Pin, Instance);
|
||||
pin_trait!(UlpiD2Pin, Instance);
|
||||
pin_trait!(UlpiD3Pin, Instance);
|
||||
pin_trait!(UlpiD4Pin, Instance);
|
||||
pin_trait!(UlpiD5Pin, Instance);
|
||||
pin_trait!(UlpiD6Pin, Instance);
|
||||
pin_trait!(UlpiD7Pin, Instance);
|
||||
|
||||
foreach_peripheral!(
|
||||
(otgfs, $inst:ident) => {
|
||||
impl sealed::Instance for peripherals::$inst {
|
||||
const REGISTERS: *const () = crate::pac::$inst.0 as *const ();
|
||||
const HIGH_SPEED: bool = false;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(stm32f1)] {
|
||||
const FIFO_DEPTH_WORDS: usize = 128;
|
||||
const ENDPOINT_COUNT: usize = 8;
|
||||
} else if #[cfg(any(
|
||||
stm32f2,
|
||||
stm32f401,
|
||||
stm32f405,
|
||||
stm32f407,
|
||||
stm32f411,
|
||||
stm32f415,
|
||||
stm32f417,
|
||||
stm32f427,
|
||||
stm32f429,
|
||||
stm32f437,
|
||||
stm32f439,
|
||||
))] {
|
||||
const FIFO_DEPTH_WORDS: usize = 320;
|
||||
const ENDPOINT_COUNT: usize = 4;
|
||||
} else if #[cfg(any(
|
||||
stm32f412,
|
||||
stm32f413,
|
||||
stm32f423,
|
||||
stm32f446,
|
||||
stm32f469,
|
||||
stm32f479,
|
||||
stm32f7,
|
||||
stm32l4,
|
||||
stm32u5,
|
||||
))] {
|
||||
const FIFO_DEPTH_WORDS: usize = 320;
|
||||
const ENDPOINT_COUNT: usize = 6;
|
||||
} else if #[cfg(stm32g0x1)] {
|
||||
const FIFO_DEPTH_WORDS: usize = 512;
|
||||
const ENDPOINT_COUNT: usize = 8;
|
||||
} else {
|
||||
compile_error!("USB_OTG_FS peripheral is not supported by this chip.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance for peripherals::$inst {}
|
||||
};
|
||||
|
||||
(otghs, $inst:ident) => {
|
||||
impl sealed::Instance for peripherals::$inst {
|
||||
const REGISTERS: *const () = crate::pac::$inst.0 as *const ();
|
||||
const HIGH_SPEED: bool = true;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(
|
||||
stm32f2,
|
||||
stm32f405,
|
||||
stm32f407,
|
||||
stm32f415,
|
||||
stm32f417,
|
||||
stm32f427,
|
||||
stm32f429,
|
||||
stm32f437,
|
||||
stm32f439,
|
||||
))] {
|
||||
const FIFO_DEPTH_WORDS: usize = 1024;
|
||||
const ENDPOINT_COUNT: usize = 6;
|
||||
} else if #[cfg(any(
|
||||
stm32f446,
|
||||
stm32f469,
|
||||
stm32f479,
|
||||
stm32f7,
|
||||
stm32h7,
|
||||
))] {
|
||||
const FIFO_DEPTH_WORDS: usize = 1024;
|
||||
const ENDPOINT_COUNT: usize = 9;
|
||||
} else {
|
||||
compile_error!("USB_OTG_HS peripheral is not supported by this chip.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance for peripherals::$inst {}
|
||||
};
|
||||
);
|
161
embassy-stm32/src/usb_otg/mod.rs
Normal file
161
embassy-stm32/src/usb_otg/mod.rs
Normal file
@ -0,0 +1,161 @@
|
||||
use embassy_cortex_m::interrupt::Interrupt;
|
||||
|
||||
use crate::peripherals;
|
||||
use crate::rcc::RccPeripheral;
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
mod usb;
|
||||
#[cfg(feature = "nightly")]
|
||||
pub use usb::*;
|
||||
|
||||
// Using Instance::ENDPOINT_COUNT requires feature(const_generic_expr) so just define maximum eps
|
||||
#[cfg(feature = "nightly")]
|
||||
const MAX_EP_COUNT: usize = 9;
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait Instance {
|
||||
const HIGH_SPEED: bool;
|
||||
const FIFO_DEPTH_WORDS: u16;
|
||||
const ENDPOINT_COUNT: usize;
|
||||
|
||||
fn regs() -> crate::pac::otg::Otg;
|
||||
#[cfg(feature = "nightly")]
|
||||
fn state() -> &'static super::State<{ super::MAX_EP_COUNT }>;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance: sealed::Instance + RccPeripheral {
|
||||
type Interrupt: Interrupt;
|
||||
}
|
||||
|
||||
// Internal PHY pins
|
||||
pin_trait!(DpPin, Instance);
|
||||
pin_trait!(DmPin, Instance);
|
||||
|
||||
// External PHY pins
|
||||
pin_trait!(UlpiClkPin, Instance);
|
||||
pin_trait!(UlpiDirPin, Instance);
|
||||
pin_trait!(UlpiNxtPin, Instance);
|
||||
pin_trait!(UlpiStpPin, Instance);
|
||||
pin_trait!(UlpiD0Pin, Instance);
|
||||
pin_trait!(UlpiD1Pin, Instance);
|
||||
pin_trait!(UlpiD2Pin, Instance);
|
||||
pin_trait!(UlpiD3Pin, Instance);
|
||||
pin_trait!(UlpiD4Pin, Instance);
|
||||
pin_trait!(UlpiD5Pin, Instance);
|
||||
pin_trait!(UlpiD6Pin, Instance);
|
||||
pin_trait!(UlpiD7Pin, Instance);
|
||||
|
||||
foreach_interrupt!(
|
||||
(USB_OTG_FS, otg, $block:ident, GLOBAL, $irq:ident) => {
|
||||
impl sealed::Instance for peripherals::USB_OTG_FS {
|
||||
const HIGH_SPEED: bool = false;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(stm32f1)] {
|
||||
const FIFO_DEPTH_WORDS: u16 = 128;
|
||||
const ENDPOINT_COUNT: usize = 8;
|
||||
} else if #[cfg(any(
|
||||
stm32f2,
|
||||
stm32f401,
|
||||
stm32f405,
|
||||
stm32f407,
|
||||
stm32f411,
|
||||
stm32f415,
|
||||
stm32f417,
|
||||
stm32f427,
|
||||
stm32f429,
|
||||
stm32f437,
|
||||
stm32f439,
|
||||
))] {
|
||||
const FIFO_DEPTH_WORDS: u16 = 320;
|
||||
const ENDPOINT_COUNT: usize = 4;
|
||||
} else if #[cfg(any(
|
||||
stm32f412,
|
||||
stm32f413,
|
||||
stm32f423,
|
||||
stm32f446,
|
||||
stm32f469,
|
||||
stm32f479,
|
||||
stm32f7,
|
||||
stm32l4,
|
||||
stm32u5,
|
||||
))] {
|
||||
const FIFO_DEPTH_WORDS: u16 = 320;
|
||||
const ENDPOINT_COUNT: usize = 6;
|
||||
} else if #[cfg(stm32g0x1)] {
|
||||
const FIFO_DEPTH_WORDS: u16 = 512;
|
||||
const ENDPOINT_COUNT: usize = 8;
|
||||
} else if #[cfg(stm32h7)] {
|
||||
const FIFO_DEPTH_WORDS: u16 = 1024;
|
||||
const ENDPOINT_COUNT: usize = 9;
|
||||
} else {
|
||||
compile_error!("USB_OTG_FS peripheral is not supported by this chip.");
|
||||
}
|
||||
}
|
||||
|
||||
fn regs() -> crate::pac::otg::Otg {
|
||||
crate::pac::USB_OTG_FS
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
fn state() -> &'static State<MAX_EP_COUNT> {
|
||||
static STATE: State<MAX_EP_COUNT> = State::new();
|
||||
&STATE
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance for peripherals::USB_OTG_FS {
|
||||
type Interrupt = crate::interrupt::$irq;
|
||||
}
|
||||
};
|
||||
|
||||
(USB_OTG_HS, otg, $block:ident, GLOBAL, $irq:ident) => {
|
||||
impl sealed::Instance for peripherals::USB_OTG_HS {
|
||||
const HIGH_SPEED: bool = true;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(
|
||||
stm32f2,
|
||||
stm32f405,
|
||||
stm32f407,
|
||||
stm32f415,
|
||||
stm32f417,
|
||||
stm32f427,
|
||||
stm32f429,
|
||||
stm32f437,
|
||||
stm32f439,
|
||||
))] {
|
||||
const FIFO_DEPTH_WORDS: u16 = 1024;
|
||||
const ENDPOINT_COUNT: usize = 6;
|
||||
} else if #[cfg(any(
|
||||
stm32f446,
|
||||
stm32f469,
|
||||
stm32f479,
|
||||
stm32f7,
|
||||
stm32h7,
|
||||
))] {
|
||||
const FIFO_DEPTH_WORDS: u16 = 1024;
|
||||
const ENDPOINT_COUNT: usize = 9;
|
||||
} else {
|
||||
compile_error!("USB_OTG_HS peripheral is not supported by this chip.");
|
||||
}
|
||||
}
|
||||
|
||||
fn regs() -> crate::pac::otg::Otg {
|
||||
// OTG HS registers are a superset of FS registers
|
||||
crate::pac::otg::Otg(crate::pac::USB_OTG_HS.0)
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
fn state() -> &'static State<MAX_EP_COUNT> {
|
||||
static STATE: State<MAX_EP_COUNT> = State::new();
|
||||
&STATE
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance for peripherals::USB_OTG_HS {
|
||||
type Interrupt = crate::interrupt::$irq;
|
||||
}
|
||||
};
|
||||
);
|
1385
embassy-stm32/src/usb_otg/usb.rs
Normal file
1385
embassy-stm32/src/usb_otg/usb.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -13,12 +13,12 @@ pub struct IndependentWatchdog<'d, T: Instance> {
|
||||
const MAX_RL: u16 = 0xFFF;
|
||||
|
||||
/// Calculates maximum watchdog timeout in us (RL = 0xFFF) for a given prescaler
|
||||
const fn max_timeout(prescaler: u8) -> u32 {
|
||||
const fn max_timeout(prescaler: u16) -> u32 {
|
||||
1_000_000 * MAX_RL as u32 / (LSI_FREQ.0 / prescaler as u32)
|
||||
}
|
||||
|
||||
/// Calculates watchdog reload value for the given prescaler and desired timeout
|
||||
const fn reload_value(prescaler: u8, timeout_us: u32) -> u16 {
|
||||
const fn reload_value(prescaler: u16, timeout_us: u32) -> u16 {
|
||||
(timeout_us / prescaler as u32 * LSI_FREQ.0 / 1_000_000) as u16
|
||||
}
|
||||
|
||||
@ -33,12 +33,12 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> {
|
||||
// Find lowest prescaler value, which makes watchdog period longer or equal to timeout.
|
||||
// This iterates from 4 (2^2) to 256 (2^8).
|
||||
let psc_power = unwrap!((2..=8).find(|psc_power| {
|
||||
let psc = 2u8.pow(*psc_power);
|
||||
let psc = 2u16.pow(*psc_power);
|
||||
timeout_us <= max_timeout(psc)
|
||||
}));
|
||||
|
||||
// Prescaler value
|
||||
let psc = 2u8.pow(psc_power);
|
||||
let psc = 2u16.pow(psc_power);
|
||||
|
||||
// Convert prescaler power to PR register value
|
||||
let pr = psc_power as u8 - 2;
|
||||
|
Reference in New Issue
Block a user