Merge branch 'main' of https://github.com/embassy-rs/embassy into hrtim
This commit is contained in:
@ -74,7 +74,7 @@ critical-section = { version = "1.1", features = ["std"] }
|
||||
[build-dependencies]
|
||||
proc-macro2 = "1.0.36"
|
||||
quote = "1.0.15"
|
||||
stm32-metapac = { version = "10", default-features = false, features = ["metadata"]}
|
||||
stm32-metapac = { version = "12", default-features = false, features = ["metadata"]}
|
||||
|
||||
[features]
|
||||
default = ["rt"]
|
||||
|
@ -709,6 +709,8 @@ fn main() {
|
||||
// SDMMCv1 uses the same channel for both directions, so just implement for RX
|
||||
(("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)),
|
||||
(("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)),
|
||||
(("dac", "CH1"), quote!(crate::dac::DmaCh1)),
|
||||
(("dac", "CH2"), quote!(crate::dac::DmaCh2)),
|
||||
]
|
||||
.into();
|
||||
|
||||
|
@ -211,10 +211,8 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
#[cfg(not(stm32g0))]
|
||||
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));
|
||||
} else {
|
||||
T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
|
||||
}
|
||||
T::regs()
|
||||
.smpr(ch as usize / 10)
|
||||
.modify(|reg| reg.set_smp(ch as usize % 10, sample_time));
|
||||
}
|
||||
}
|
||||
|
@ -116,10 +116,10 @@ impl<'d, T: Instance> Can<'d, T> {
|
||||
T::regs().ier().write(|w| {
|
||||
// TODO: fix metapac
|
||||
|
||||
w.set_errie(Errie(1));
|
||||
w.set_fmpie(0, Fmpie(1));
|
||||
w.set_fmpie(1, Fmpie(1));
|
||||
w.set_tmeie(Tmeie(1));
|
||||
w.set_errie(Errie::from_bits(1));
|
||||
w.set_fmpie(0, Fmpie::from_bits(1));
|
||||
w.set_fmpie(1, Fmpie::from_bits(1));
|
||||
w.set_tmeie(Tmeie::from_bits(1));
|
||||
});
|
||||
|
||||
T::regs().mcr().write(|w| {
|
||||
|
@ -1,260 +0,0 @@
|
||||
#![macro_use]
|
||||
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
|
||||
use crate::pac::dac;
|
||||
use crate::rcc::RccPeripheral;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
UnconfiguredChannel,
|
||||
InvalidValue,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Channel {
|
||||
Ch1,
|
||||
Ch2,
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
fn index(&self) -> usize {
|
||||
match self {
|
||||
Channel::Ch1 => 0,
|
||||
Channel::Ch2 => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Ch1Trigger {
|
||||
Tim6,
|
||||
Tim3,
|
||||
Tim7,
|
||||
Tim15,
|
||||
Tim2,
|
||||
Exti9,
|
||||
Software,
|
||||
}
|
||||
|
||||
impl Ch1Trigger {
|
||||
fn tsel(&self) -> dac::vals::Tsel1 {
|
||||
match self {
|
||||
Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO,
|
||||
Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO,
|
||||
Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO,
|
||||
Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO,
|
||||
Ch1Trigger::Tim2 => dac::vals::Tsel1::TIM2_TRGO,
|
||||
Ch1Trigger::Exti9 => dac::vals::Tsel1::EXTI9,
|
||||
Ch1Trigger::Software => dac::vals::Tsel1::SOFTWARE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Ch2Trigger {
|
||||
Tim6,
|
||||
Tim8,
|
||||
Tim7,
|
||||
Tim5,
|
||||
Tim2,
|
||||
Tim4,
|
||||
Exti9,
|
||||
Software,
|
||||
}
|
||||
|
||||
impl Ch2Trigger {
|
||||
fn tsel(&self) -> dac::vals::Tsel2 {
|
||||
match self {
|
||||
Ch2Trigger::Tim6 => dac::vals::Tsel2::TIM6_TRGO,
|
||||
Ch2Trigger::Tim8 => dac::vals::Tsel2::TIM8_TRGO,
|
||||
Ch2Trigger::Tim7 => dac::vals::Tsel2::TIM7_TRGO,
|
||||
Ch2Trigger::Tim5 => dac::vals::Tsel2::TIM5_TRGO,
|
||||
Ch2Trigger::Tim2 => dac::vals::Tsel2::TIM2_TRGO,
|
||||
Ch2Trigger::Tim4 => dac::vals::Tsel2::TIM4_TRGO,
|
||||
Ch2Trigger::Exti9 => dac::vals::Tsel2::EXTI9,
|
||||
Ch2Trigger::Software => dac::vals::Tsel2::SOFTWARE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Alignment {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Value {
|
||||
Bit8(u8),
|
||||
Bit12(u16, Alignment),
|
||||
}
|
||||
|
||||
pub struct Dac<'d, T: Instance> {
|
||||
channels: u8,
|
||||
_peri: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Dac<'d, T> {
|
||||
pub fn new_1ch(peri: impl Peripheral<P = T> + 'd, _ch1: impl Peripheral<P = impl DacPin<T, 1>> + 'd) -> Self {
|
||||
into_ref!(peri);
|
||||
Self::new_inner(peri, 1)
|
||||
}
|
||||
|
||||
pub fn new_2ch(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
_ch1: impl Peripheral<P = impl DacPin<T, 1>> + 'd,
|
||||
_ch2: impl Peripheral<P = impl DacPin<T, 2>> + 'd,
|
||||
) -> Self {
|
||||
into_ref!(peri);
|
||||
Self::new_inner(peri, 2)
|
||||
}
|
||||
|
||||
fn new_inner(peri: PeripheralRef<'d, T>, channels: u8) -> Self {
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
T::regs().cr().modify(|reg| {
|
||||
for ch in 0..channels {
|
||||
reg.set_en(ch as usize, true);
|
||||
}
|
||||
});
|
||||
|
||||
Self { channels, _peri: peri }
|
||||
}
|
||||
|
||||
/// Check the channel is configured
|
||||
fn check_channel_exists(&self, ch: Channel) -> Result<(), Error> {
|
||||
if ch == Channel::Ch2 && self.channels < 2 {
|
||||
Err(Error::UnconfiguredChannel)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn set_channel_enable(&mut self, ch: Channel, on: bool) -> Result<(), Error> {
|
||||
self.check_channel_exists(ch)?;
|
||||
T::regs().cr().modify(|reg| {
|
||||
reg.set_en(ch.index(), on);
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn enable_channel(&mut self, ch: Channel) -> Result<(), Error> {
|
||||
self.set_channel_enable(ch, true)
|
||||
}
|
||||
|
||||
pub fn disable_channel(&mut self, ch: Channel) -> Result<(), Error> {
|
||||
self.set_channel_enable(ch, false)
|
||||
}
|
||||
|
||||
pub fn select_trigger_ch1(&mut self, trigger: Ch1Trigger) -> Result<(), Error> {
|
||||
self.check_channel_exists(Channel::Ch1)?;
|
||||
unwrap!(self.disable_channel(Channel::Ch1));
|
||||
T::regs().cr().modify(|reg| {
|
||||
reg.set_tsel1(trigger.tsel());
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn select_trigger_ch2(&mut self, trigger: Ch2Trigger) -> Result<(), Error> {
|
||||
self.check_channel_exists(Channel::Ch2)?;
|
||||
unwrap!(self.disable_channel(Channel::Ch2));
|
||||
T::regs().cr().modify(|reg| {
|
||||
reg.set_tsel2(trigger.tsel());
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn trigger(&mut self, ch: Channel) -> Result<(), Error> {
|
||||
self.check_channel_exists(ch)?;
|
||||
T::regs().swtrigr().write(|reg| {
|
||||
reg.set_swtrig(ch.index(), true);
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn trigger_all(&mut self) {
|
||||
T::regs().swtrigr().write(|reg| {
|
||||
reg.set_swtrig(Channel::Ch1.index(), true);
|
||||
reg.set_swtrig(Channel::Ch2.index(), true);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set(&mut self, ch: Channel, value: Value) -> Result<(), Error> {
|
||||
self.check_channel_exists(ch)?;
|
||||
match value {
|
||||
Value::Bit8(v) => T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v)),
|
||||
Value::Bit12(v, Alignment::Left) => T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)),
|
||||
Value::Bit12(v, Alignment::Right) => T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v)),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait Instance {
|
||||
fn regs() -> &'static crate::pac::dac::Dac;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance: sealed::Instance + RccPeripheral + 'static {}
|
||||
|
||||
pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {}
|
||||
|
||||
foreach_peripheral!(
|
||||
(dac, $inst:ident) => {
|
||||
// H7 uses single bit for both DAC1 and DAC2, this is a hack until a proper fix is implemented
|
||||
#[cfg(rcc_h7)]
|
||||
impl crate::rcc::sealed::RccPeripheral for peripherals::$inst {
|
||||
fn frequency() -> crate::time::Hertz {
|
||||
critical_section::with(|_| unsafe {
|
||||
crate::rcc::get_freqs().apb1
|
||||
})
|
||||
}
|
||||
|
||||
fn reset() {
|
||||
critical_section::with(|_| {
|
||||
crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true));
|
||||
crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false));
|
||||
})
|
||||
}
|
||||
|
||||
fn enable() {
|
||||
critical_section::with(|_| {
|
||||
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true));
|
||||
})
|
||||
}
|
||||
|
||||
fn disable() {
|
||||
critical_section::with(|_| {
|
||||
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(rcc_h7)]
|
||||
impl crate::rcc::RccPeripheral for peripherals::$inst {}
|
||||
|
||||
impl crate::dac::sealed::Instance for peripherals::$inst {
|
||||
fn regs() -> &'static crate::pac::dac::Dac {
|
||||
&crate::pac::$inst
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::dac::Instance for peripherals::$inst {}
|
||||
};
|
||||
);
|
||||
|
||||
macro_rules! impl_dac_pin {
|
||||
($inst:ident, $pin:ident, $ch:expr) => {
|
||||
impl crate::dac::DacPin<peripherals::$inst, $ch> for crate::peripherals::$pin {}
|
||||
};
|
||||
}
|
570
embassy-stm32/src/dac/mod.rs
Normal file
570
embassy-stm32/src/dac/mod.rs
Normal file
@ -0,0 +1,570 @@
|
||||
#![macro_use]
|
||||
|
||||
//! Provide access to the STM32 digital-to-analog converter (DAC).
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
|
||||
use crate::pac::dac;
|
||||
use crate::rcc::RccPeripheral;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Curstom Errors
|
||||
pub enum Error {
|
||||
UnconfiguredChannel,
|
||||
InvalidValue,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// DAC Channels
|
||||
pub enum Channel {
|
||||
Ch1,
|
||||
Ch2,
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
const fn index(&self) -> usize {
|
||||
match self {
|
||||
Channel::Ch1 => 0,
|
||||
Channel::Ch2 => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Trigger sources for CH1
|
||||
pub enum Ch1Trigger {
|
||||
Tim6,
|
||||
Tim3,
|
||||
Tim7,
|
||||
Tim15,
|
||||
Tim2,
|
||||
Exti9,
|
||||
Software,
|
||||
}
|
||||
|
||||
impl Ch1Trigger {
|
||||
fn tsel(&self) -> dac::vals::Tsel1 {
|
||||
match self {
|
||||
Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO,
|
||||
Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO,
|
||||
Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO,
|
||||
Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO,
|
||||
Ch1Trigger::Tim2 => dac::vals::Tsel1::TIM2_TRGO,
|
||||
Ch1Trigger::Exti9 => dac::vals::Tsel1::EXTI9,
|
||||
Ch1Trigger::Software => dac::vals::Tsel1::SOFTWARE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Trigger sources for CH2
|
||||
pub enum Ch2Trigger {
|
||||
Tim6,
|
||||
Tim8,
|
||||
Tim7,
|
||||
Tim5,
|
||||
Tim2,
|
||||
Tim4,
|
||||
Exti9,
|
||||
Software,
|
||||
}
|
||||
|
||||
impl Ch2Trigger {
|
||||
fn tsel(&self) -> dac::vals::Tsel2 {
|
||||
match self {
|
||||
Ch2Trigger::Tim6 => dac::vals::Tsel2::TIM6_TRGO,
|
||||
Ch2Trigger::Tim8 => dac::vals::Tsel2::TIM8_TRGO,
|
||||
Ch2Trigger::Tim7 => dac::vals::Tsel2::TIM7_TRGO,
|
||||
Ch2Trigger::Tim5 => dac::vals::Tsel2::TIM5_TRGO,
|
||||
Ch2Trigger::Tim2 => dac::vals::Tsel2::TIM2_TRGO,
|
||||
Ch2Trigger::Tim4 => dac::vals::Tsel2::TIM4_TRGO,
|
||||
Ch2Trigger::Exti9 => dac::vals::Tsel2::EXTI9,
|
||||
Ch2Trigger::Software => dac::vals::Tsel2::SOFTWARE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Single 8 or 12 bit value that can be output by the DAC
|
||||
pub enum Value {
|
||||
// 8 bit value
|
||||
Bit8(u8),
|
||||
// 12 bit value stored in a u16, left-aligned
|
||||
Bit12Left(u16),
|
||||
// 12 bit value stored in a u16, right-aligned
|
||||
Bit12Right(u16),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Array variant of [`Value`]
|
||||
pub enum ValueArray<'a> {
|
||||
// 8 bit values
|
||||
Bit8(&'a [u8]),
|
||||
// 12 bit value stored in a u16, left-aligned
|
||||
Bit12Left(&'a [u16]),
|
||||
// 12 bit values stored in a u16, right-aligned
|
||||
Bit12Right(&'a [u16]),
|
||||
}
|
||||
/// Provide common functions for DAC channels
|
||||
pub trait DacChannel<T: Instance, Tx> {
|
||||
const CHANNEL: Channel;
|
||||
|
||||
/// Enable trigger of the given channel
|
||||
fn set_trigger_enable(&mut self, on: bool) -> Result<(), Error> {
|
||||
T::regs().cr().modify(|reg| {
|
||||
reg.set_ten(Self::CHANNEL.index(), on);
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set mode register of the given channel
|
||||
#[cfg(dac_v2)]
|
||||
fn set_channel_mode(&mut self, val: u8) -> Result<(), Error> {
|
||||
T::regs().mcr().modify(|reg| {
|
||||
reg.set_mode(Self::CHANNEL.index(), val);
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set enable register of the given channel
|
||||
fn set_channel_enable(&mut self, on: bool) -> Result<(), Error> {
|
||||
T::regs().cr().modify(|reg| {
|
||||
reg.set_en(Self::CHANNEL.index(), on);
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Enable the DAC channel `ch`
|
||||
fn enable_channel(&mut self) -> Result<(), Error> {
|
||||
self.set_channel_enable(true)
|
||||
}
|
||||
|
||||
/// Disable the DAC channel `ch`
|
||||
fn disable_channel(&mut self) -> Result<(), Error> {
|
||||
self.set_channel_enable(false)
|
||||
}
|
||||
|
||||
/// Perform a software trigger on `ch`
|
||||
fn trigger(&mut self) {
|
||||
T::regs().swtrigr().write(|reg| {
|
||||
reg.set_swtrig(Self::CHANNEL.index(), true);
|
||||
});
|
||||
}
|
||||
|
||||
/// Set a value to be output by the DAC on trigger.
|
||||
///
|
||||
/// The `value` is written to the corresponding "data holding register".
|
||||
fn set(&mut self, value: Value) -> Result<(), Error> {
|
||||
match value {
|
||||
Value::Bit8(v) => T::regs().dhr8r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)),
|
||||
Value::Bit12Left(v) => T::regs().dhr12l(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)),
|
||||
Value::Bit12Right(v) => T::regs().dhr12r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Hold two DAC channels
|
||||
///
|
||||
/// Note: This consumes the DAC `Instance` only once, allowing to get both channels simultaneously.
|
||||
///
|
||||
/// # Example for obtaining both DAC channels
|
||||
///
|
||||
/// ```ignore
|
||||
/// // DMA channels and pins may need to be changed for your controller
|
||||
/// let (dac_ch1, dac_ch2) =
|
||||
/// embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split();
|
||||
/// ```
|
||||
pub struct Dac<'d, T: Instance, TxCh1, TxCh2> {
|
||||
ch1: DacCh1<'d, T, TxCh1>,
|
||||
ch2: DacCh2<'d, T, TxCh2>,
|
||||
}
|
||||
|
||||
/// DAC CH1
|
||||
///
|
||||
/// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously.
|
||||
pub struct DacCh1<'d, T: Instance, Tx> {
|
||||
/// To consume T
|
||||
_peri: PeripheralRef<'d, T>,
|
||||
#[allow(unused)] // For chips whose DMA is not (yet) supported
|
||||
dma: PeripheralRef<'d, Tx>,
|
||||
}
|
||||
|
||||
/// DAC CH2
|
||||
///
|
||||
/// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously.
|
||||
pub struct DacCh2<'d, T: Instance, Tx> {
|
||||
/// Instead of PeripheralRef to consume T
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
#[allow(unused)] // For chips whose DMA is not (yet) supported
|
||||
dma: PeripheralRef<'d, Tx>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> {
|
||||
/// Obtain DAC CH1
|
||||
pub fn new(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
dma: impl Peripheral<P = Tx> + 'd,
|
||||
_pin: impl Peripheral<P = impl DacPin<T, 1>> + 'd,
|
||||
) -> Self {
|
||||
into_ref!(peri, dma);
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
let mut dac = Self { _peri: peri, dma };
|
||||
|
||||
// Configure each activated channel. All results can be `unwrap`ed since they
|
||||
// will only error if the channel is not configured (i.e. ch1, ch2 are false)
|
||||
#[cfg(dac_v2)]
|
||||
dac.set_channel_mode(0).unwrap();
|
||||
dac.enable_channel().unwrap();
|
||||
dac.set_trigger_enable(true).unwrap();
|
||||
|
||||
dac
|
||||
}
|
||||
|
||||
/// Select a new trigger for this channel
|
||||
///
|
||||
/// **Important**: This disables the channel!
|
||||
pub fn select_trigger(&mut self, trigger: Ch1Trigger) -> Result<(), Error> {
|
||||
unwrap!(self.disable_channel());
|
||||
T::regs().cr().modify(|reg| {
|
||||
reg.set_tsel1(trigger.tsel());
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write `data` to the DAC CH1 via DMA.
|
||||
///
|
||||
/// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set.
|
||||
/// This will configure a circular DMA transfer that periodically outputs the `data`.
|
||||
/// Note that for performance reasons in circular mode the transfer complete interrupt is disabled.
|
||||
///
|
||||
/// **Important:** Channel 1 has to be configured for the DAC instance!
|
||||
#[cfg(all(bdma, not(dma)))] // It currently only works with BDMA-only chips (DMA should theoretically work though)
|
||||
pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error>
|
||||
where
|
||||
Tx: DmaCh1<T>,
|
||||
{
|
||||
let channel = Channel::Ch1.index();
|
||||
debug!("Writing to channel {}", channel);
|
||||
|
||||
// Enable DAC and DMA
|
||||
T::regs().cr().modify(|w| {
|
||||
w.set_en(channel, true);
|
||||
w.set_dmaen(channel, true);
|
||||
});
|
||||
|
||||
let tx_request = self.dma.request();
|
||||
let dma_channel = &self.dma;
|
||||
|
||||
let tx_options = crate::dma::TransferOptions {
|
||||
circular,
|
||||
half_transfer_ir: false,
|
||||
complete_transfer_ir: !circular,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Initiate the correct type of DMA transfer depending on what data is passed
|
||||
let tx_f = match data {
|
||||
ValueArray::Bit8(buf) => unsafe {
|
||||
crate::dma::Transfer::new_write(
|
||||
dma_channel,
|
||||
tx_request,
|
||||
buf,
|
||||
T::regs().dhr8r(channel).as_ptr() as *mut u8,
|
||||
tx_options,
|
||||
)
|
||||
},
|
||||
ValueArray::Bit12Left(buf) => unsafe {
|
||||
crate::dma::Transfer::new_write(
|
||||
dma_channel,
|
||||
tx_request,
|
||||
buf,
|
||||
T::regs().dhr12l(channel).as_ptr() as *mut u16,
|
||||
tx_options,
|
||||
)
|
||||
},
|
||||
ValueArray::Bit12Right(buf) => unsafe {
|
||||
crate::dma::Transfer::new_write(
|
||||
dma_channel,
|
||||
tx_request,
|
||||
buf,
|
||||
T::regs().dhr12r(channel).as_ptr() as *mut u16,
|
||||
tx_options,
|
||||
)
|
||||
},
|
||||
};
|
||||
|
||||
tx_f.await;
|
||||
|
||||
// finish dma
|
||||
// TODO: Do we need to check any status registers here?
|
||||
T::regs().cr().modify(|w| {
|
||||
// Disable the DAC peripheral
|
||||
w.set_en(channel, false);
|
||||
// Disable the DMA. TODO: Is this necessary?
|
||||
w.set_dmaen(channel, false);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> {
|
||||
/// Obtain DAC CH2
|
||||
pub fn new(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
dma: impl Peripheral<P = Tx> + 'd,
|
||||
_pin: impl Peripheral<P = impl DacPin<T, 2>> + 'd,
|
||||
) -> Self {
|
||||
into_ref!(_peri, dma);
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
let mut dac = Self {
|
||||
phantom: PhantomData,
|
||||
dma,
|
||||
};
|
||||
|
||||
// Configure each activated channel. All results can be `unwrap`ed since they
|
||||
// will only error if the channel is not configured (i.e. ch1, ch2 are false)
|
||||
#[cfg(dac_v2)]
|
||||
dac.set_channel_mode(0).unwrap();
|
||||
dac.enable_channel().unwrap();
|
||||
dac.set_trigger_enable(true).unwrap();
|
||||
|
||||
dac
|
||||
}
|
||||
|
||||
/// Select a new trigger for this channel
|
||||
pub fn select_trigger(&mut self, trigger: Ch2Trigger) -> Result<(), Error> {
|
||||
unwrap!(self.disable_channel());
|
||||
T::regs().cr().modify(|reg| {
|
||||
reg.set_tsel2(trigger.tsel());
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write `data` to the DAC CH2 via DMA.
|
||||
///
|
||||
/// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set.
|
||||
/// This will configure a circular DMA transfer that periodically outputs the `data`.
|
||||
/// Note that for performance reasons in circular mode the transfer complete interrupt is disabled.
|
||||
///
|
||||
/// **Important:** Channel 2 has to be configured for the DAC instance!
|
||||
#[cfg(all(bdma, not(dma)))] // It currently only works with BDMA-only chips (DMA should theoretically work though)
|
||||
pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error>
|
||||
where
|
||||
Tx: DmaCh2<T>,
|
||||
{
|
||||
let channel = Channel::Ch2.index();
|
||||
debug!("Writing to channel {}", channel);
|
||||
|
||||
// Enable DAC and DMA
|
||||
T::regs().cr().modify(|w| {
|
||||
w.set_en(channel, true);
|
||||
w.set_dmaen(channel, true);
|
||||
});
|
||||
|
||||
let tx_request = self.dma.request();
|
||||
let dma_channel = &self.dma;
|
||||
|
||||
let tx_options = crate::dma::TransferOptions {
|
||||
circular,
|
||||
half_transfer_ir: false,
|
||||
complete_transfer_ir: !circular,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Initiate the correct type of DMA transfer depending on what data is passed
|
||||
let tx_f = match data {
|
||||
ValueArray::Bit8(buf) => unsafe {
|
||||
crate::dma::Transfer::new_write(
|
||||
dma_channel,
|
||||
tx_request,
|
||||
buf,
|
||||
T::regs().dhr8r(channel).as_ptr() as *mut u8,
|
||||
tx_options,
|
||||
)
|
||||
},
|
||||
ValueArray::Bit12Left(buf) => unsafe {
|
||||
crate::dma::Transfer::new_write(
|
||||
dma_channel,
|
||||
tx_request,
|
||||
buf,
|
||||
T::regs().dhr12l(channel).as_ptr() as *mut u16,
|
||||
tx_options,
|
||||
)
|
||||
},
|
||||
ValueArray::Bit12Right(buf) => unsafe {
|
||||
crate::dma::Transfer::new_write(
|
||||
dma_channel,
|
||||
tx_request,
|
||||
buf,
|
||||
T::regs().dhr12r(channel).as_ptr() as *mut u16,
|
||||
tx_options,
|
||||
)
|
||||
},
|
||||
};
|
||||
|
||||
tx_f.await;
|
||||
|
||||
// finish dma
|
||||
// TODO: Do we need to check any status registers here?
|
||||
T::regs().cr().modify(|w| {
|
||||
// Disable the DAC peripheral
|
||||
w.set_en(channel, false);
|
||||
// Disable the DMA. TODO: Is this necessary?
|
||||
w.set_dmaen(channel, false);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> {
|
||||
/// Create a new DAC instance with both channels.
|
||||
///
|
||||
/// This is used to obtain two independent channels via `split()` for use e.g. with DMA.
|
||||
pub fn new(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
dma_ch1: impl Peripheral<P = TxCh1> + 'd,
|
||||
dma_ch2: impl Peripheral<P = TxCh2> + 'd,
|
||||
_pin_ch1: impl Peripheral<P = impl DacPin<T, 1>> + 'd,
|
||||
_pin_ch2: impl Peripheral<P = impl DacPin<T, 2>> + 'd,
|
||||
) -> Self {
|
||||
into_ref!(peri, dma_ch1, dma_ch2);
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
let mut dac_ch1 = DacCh1 {
|
||||
_peri: peri,
|
||||
dma: dma_ch1,
|
||||
};
|
||||
|
||||
let mut dac_ch2 = DacCh2 {
|
||||
phantom: PhantomData,
|
||||
dma: dma_ch2,
|
||||
};
|
||||
|
||||
// Configure each activated channel. All results can be `unwrap`ed since they
|
||||
// will only error if the channel is not configured (i.e. ch1, ch2 are false)
|
||||
#[cfg(dac_v2)]
|
||||
dac_ch1.set_channel_mode(0).unwrap();
|
||||
dac_ch1.enable_channel().unwrap();
|
||||
dac_ch1.set_trigger_enable(true).unwrap();
|
||||
|
||||
#[cfg(dac_v2)]
|
||||
dac_ch2.set_channel_mode(0).unwrap();
|
||||
dac_ch2.enable_channel().unwrap();
|
||||
dac_ch2.set_trigger_enable(true).unwrap();
|
||||
|
||||
Self {
|
||||
ch1: dac_ch1,
|
||||
ch2: dac_ch2,
|
||||
}
|
||||
}
|
||||
|
||||
/// Split the DAC into CH1 and CH2 for independent use.
|
||||
pub fn split(self) -> (DacCh1<'d, T, TxCh1>, DacCh2<'d, T, TxCh2>) {
|
||||
(self.ch1, self.ch2)
|
||||
}
|
||||
|
||||
/// Get mutable reference to CH1
|
||||
pub fn ch1_mut(&mut self) -> &mut DacCh1<'d, T, TxCh1> {
|
||||
&mut self.ch1
|
||||
}
|
||||
|
||||
/// Get mutable reference to CH2
|
||||
pub fn ch2_mut(&mut self) -> &mut DacCh2<'d, T, TxCh2> {
|
||||
&mut self.ch2
|
||||
}
|
||||
|
||||
/// Get reference to CH1
|
||||
pub fn ch1(&mut self) -> &DacCh1<'d, T, TxCh1> {
|
||||
&self.ch1
|
||||
}
|
||||
|
||||
/// Get reference to CH2
|
||||
pub fn ch2(&mut self) -> &DacCh2<'d, T, TxCh2> {
|
||||
&self.ch2
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh1<'d, T, Tx> {
|
||||
const CHANNEL: Channel = Channel::Ch1;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh2<'d, T, Tx> {
|
||||
const CHANNEL: Channel = Channel::Ch2;
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait Instance {
|
||||
fn regs() -> &'static crate::pac::dac::Dac;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance: sealed::Instance + RccPeripheral + 'static {}
|
||||
dma_trait!(DmaCh1, Instance);
|
||||
dma_trait!(DmaCh2, Instance);
|
||||
|
||||
/// Marks a pin that can be used with the DAC
|
||||
pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {}
|
||||
|
||||
foreach_peripheral!(
|
||||
(dac, $inst:ident) => {
|
||||
// H7 uses single bit for both DAC1 and DAC2, this is a hack until a proper fix is implemented
|
||||
#[cfg(rcc_h7)]
|
||||
impl crate::rcc::sealed::RccPeripheral for peripherals::$inst {
|
||||
fn frequency() -> crate::time::Hertz {
|
||||
critical_section::with(|_| unsafe { crate::rcc::get_freqs().apb1 })
|
||||
}
|
||||
|
||||
fn reset() {
|
||||
critical_section::with(|_| {
|
||||
crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true));
|
||||
crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false));
|
||||
})
|
||||
}
|
||||
|
||||
fn enable() {
|
||||
critical_section::with(|_| {
|
||||
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true));
|
||||
})
|
||||
}
|
||||
|
||||
fn disable() {
|
||||
critical_section::with(|_| {
|
||||
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(rcc_h7)]
|
||||
impl crate::rcc::RccPeripheral for peripherals::$inst {}
|
||||
|
||||
impl crate::dac::sealed::Instance for peripherals::$inst {
|
||||
fn regs() -> &'static crate::pac::dac::Dac {
|
||||
&crate::pac::$inst
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::dac::Instance for peripherals::$inst {}
|
||||
};
|
||||
);
|
||||
|
||||
macro_rules! impl_dac_pin {
|
||||
($inst:ident, $pin:ident, $ch:expr) => {
|
||||
impl crate::dac::DacPin<peripherals::$inst, $ch> for crate::peripherals::$pin {}
|
||||
};
|
||||
}
|
@ -21,11 +21,22 @@ use crate::pac::bdma::{regs, vals};
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub struct TransferOptions {}
|
||||
pub struct TransferOptions {
|
||||
/// Enable circular DMA
|
||||
pub circular: bool,
|
||||
/// Enable half transfer interrupt
|
||||
pub half_transfer_ir: bool,
|
||||
/// Enable transfer complete interrupt
|
||||
pub complete_transfer_ir: bool,
|
||||
}
|
||||
|
||||
impl Default for TransferOptions {
|
||||
fn default() -> Self {
|
||||
Self {}
|
||||
Self {
|
||||
circular: false,
|
||||
half_transfer_ir: false,
|
||||
complete_transfer_ir: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -253,7 +264,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
||||
mem_len: usize,
|
||||
incr_mem: bool,
|
||||
data_size: WordSize,
|
||||
_options: TransferOptions,
|
||||
options: TransferOptions,
|
||||
) -> Self {
|
||||
let ch = channel.regs().ch(channel.num());
|
||||
|
||||
@ -283,7 +294,15 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
||||
}
|
||||
w.set_dir(dir.into());
|
||||
w.set_teie(true);
|
||||
w.set_tcie(true);
|
||||
w.set_tcie(options.complete_transfer_ir);
|
||||
w.set_htie(options.half_transfer_ir);
|
||||
if options.circular {
|
||||
w.set_circ(vals::Circ::ENABLED);
|
||||
debug!("Setting circular mode");
|
||||
} else {
|
||||
w.set_circ(vals::Circ::DISABLED);
|
||||
}
|
||||
w.set_pl(vals::Pl::VERYHIGH);
|
||||
w.set_en(true);
|
||||
});
|
||||
|
||||
@ -310,8 +329,9 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
||||
pub fn is_running(&mut self) -> bool {
|
||||
let ch = self.channel.regs().ch(self.channel.num());
|
||||
let en = ch.cr().read().en();
|
||||
let circular = ch.cr().read().circ() == vals::Circ::ENABLED;
|
||||
let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0;
|
||||
en && !tcif
|
||||
en && (circular || !tcif)
|
||||
}
|
||||
|
||||
/// Gets the total remaining transfers for the channel
|
||||
@ -477,6 +497,8 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> {
|
||||
let ch = self.channel.regs().ch(self.channel.num());
|
||||
|
||||
// Disable the channel. Keep the IEs enabled so the irqs still fire.
|
||||
// If the channel is enabled and transfer is not completed, we need to perform
|
||||
// two separate write access to the CR register to disable the channel.
|
||||
ch.cr().write(|w| {
|
||||
w.set_teie(true);
|
||||
w.set_htie(true);
|
||||
|
@ -174,7 +174,7 @@ impl<'a> RDesRing<'a> {
|
||||
// Receive descriptor unavailable
|
||||
Rps::SUSPENDED => RunningState::Stopped,
|
||||
// Closing receive descriptor
|
||||
Rps(0b101) => RunningState::Running,
|
||||
Rps::_RESERVED_5 => RunningState::Running,
|
||||
// Transferring the receive packet data from receive buffer to host memory
|
||||
Rps::RUNNINGWRITING => RunningState::Running,
|
||||
_ => RunningState::Unknown,
|
||||
|
@ -243,7 +243,7 @@ mod tests {
|
||||
for test_run in fn_results {
|
||||
let (ckd, bits) = compute_dead_time_value(test_run.value);
|
||||
|
||||
assert_eq!(ckd.0, test_run.ckd.0);
|
||||
assert_eq!(ckd.to_bits(), test_run.ckd.to_bits());
|
||||
assert_eq!(bits, test_run.bits);
|
||||
}
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
});
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
(HSI_FREQ.0 >> div.0, Sw::HSI)
|
||||
(HSI_FREQ.0 >> div.to_bits(), Sw::HSI)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
@ -157,7 +157,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
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 {
|
||||
if w.latency().to_bits() <= target_flash_latency.to_bits() {
|
||||
// We must increase the number of wait states now
|
||||
w.set_latency(target_flash_latency)
|
||||
} else {
|
||||
@ -171,12 +171,12 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
// > 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);
|
||||
w.set_prften(target_flash_latency.to_bits() > 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 {}
|
||||
while FLASH.acr().read().latency().to_bits() < target_flash_latency.to_bits() {}
|
||||
}
|
||||
|
||||
// Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once
|
||||
@ -218,7 +218,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.0 - 3);
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
use stm32_metapac::flash::vals::Latency;
|
||||
|
||||
use super::{set_freqs, Clocks};
|
||||
use crate::pac::rcc::vals::{Hpre, Pllmul, Pllsrc, Ppre, Sw, Usbsw};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
@ -85,14 +87,11 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
let timer_mul = if ppre == 1 { 1 } else { 2 };
|
||||
|
||||
FLASH.acr().write(|w| {
|
||||
let latency = if real_sysclk <= 24_000_000 {
|
||||
0
|
||||
} else if real_sysclk <= 48_000_000 {
|
||||
1
|
||||
w.set_latency(if real_sysclk <= 24_000_000 {
|
||||
Latency::WS0
|
||||
} else {
|
||||
2
|
||||
};
|
||||
w.latency().0 = latency;
|
||||
Latency::WS1
|
||||
});
|
||||
});
|
||||
|
||||
match (config.hse.is_some(), use_hsi48) {
|
||||
@ -134,20 +133,20 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
// TODO: Option to use CRS (Clock Recovery)
|
||||
|
||||
if let Some(pllmul_bits) = pllmul_bits {
|
||||
RCC.cfgr().modify(|w| w.set_pllmul(Pllmul(pllmul_bits)));
|
||||
RCC.cfgr().modify(|w| w.set_pllmul(Pllmul::from_bits(pllmul_bits)));
|
||||
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_ppre(Ppre(ppre_bits));
|
||||
w.set_hpre(Hpre(hpre_bits));
|
||||
w.set_ppre(Ppre::from_bits(ppre_bits));
|
||||
w.set_hpre(Hpre::from_bits(hpre_bits));
|
||||
w.set_sw(Sw::PLL)
|
||||
});
|
||||
} else {
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_ppre(Ppre(ppre_bits));
|
||||
w.set_hpre(Hpre(hpre_bits));
|
||||
w.set_ppre(Ppre::from_bits(ppre_bits));
|
||||
w.set_hpre(Hpre::from_bits(hpre_bits));
|
||||
|
||||
if config.hse.is_some() {
|
||||
w.set_sw(Sw::HSE);
|
||||
|
@ -106,11 +106,11 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
// Only needed for stm32f103?
|
||||
FLASH.acr().write(|w| {
|
||||
w.set_latency(if real_sysclk <= 24_000_000 {
|
||||
Latency(0b000)
|
||||
Latency::WS0
|
||||
} else if real_sysclk <= 48_000_000 {
|
||||
Latency(0b001)
|
||||
Latency::WS1
|
||||
} else {
|
||||
Latency(0b010)
|
||||
Latency::WS2
|
||||
});
|
||||
});
|
||||
|
||||
@ -147,12 +147,13 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
|
||||
if let Some(pllmul_bits) = pllmul_bits {
|
||||
let pllctpre_flag: u8 = if config.pllxtpre { 1 } else { 0 };
|
||||
RCC.cfgr().modify(|w| w.set_pllxtpre(Pllxtpre(pllctpre_flag)));
|
||||
RCC.cfgr()
|
||||
.modify(|w| w.set_pllxtpre(Pllxtpre::from_bits(pllctpre_flag)));
|
||||
|
||||
// enable PLL and wait for it to be ready
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_pllmul(Pllmul(pllmul_bits));
|
||||
w.set_pllsrc(Pllsrc(config.hse.is_some() as u8));
|
||||
w.set_pllmul(Pllmul::from_bits(pllmul_bits));
|
||||
w.set_pllsrc(Pllsrc::from_bits(config.hse.is_some() as u8));
|
||||
});
|
||||
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
@ -161,22 +162,19 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
|
||||
// Only needed for stm32f103?
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_adcpre(Adcpre(apre_bits));
|
||||
w.set_ppre2(Ppre1(ppre2_bits));
|
||||
w.set_ppre1(Ppre1(ppre1_bits));
|
||||
w.set_hpre(Hpre(hpre_bits));
|
||||
w.set_adcpre(Adcpre::from_bits(apre_bits));
|
||||
w.set_ppre2(Ppre1::from_bits(ppre2_bits));
|
||||
w.set_ppre1(Ppre1::from_bits(ppre1_bits));
|
||||
w.set_hpre(Hpre::from_bits(hpre_bits));
|
||||
#[cfg(not(rcc_f100))]
|
||||
w.set_usbpre(Usbpre(usbpre as u8));
|
||||
w.set_sw(Sw(if pllmul_bits.is_some() {
|
||||
// PLL
|
||||
0b10
|
||||
w.set_usbpre(Usbpre::from_bits(usbpre as u8));
|
||||
w.set_sw(if pllmul_bits.is_some() {
|
||||
Sw::PLL
|
||||
} else if config.hse.is_some() {
|
||||
// HSE
|
||||
0b1
|
||||
Sw::HSE
|
||||
} else {
|
||||
// HSI
|
||||
0b0
|
||||
}));
|
||||
Sw::HSI
|
||||
});
|
||||
});
|
||||
|
||||
set_freqs(Clocks {
|
||||
|
@ -485,7 +485,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
w.set_ppre1(config.apb1_pre.into());
|
||||
w.set_ppre2(config.apb2_pre.into());
|
||||
});
|
||||
while RCC.cfgr().read().sws() != sw.0 {}
|
||||
while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {}
|
||||
|
||||
// Turn off HSI to save power if we don't need it
|
||||
if !config.hsi {
|
||||
|
@ -87,7 +87,7 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, plli2s: Opti
|
||||
|
||||
let sysclk = pllsysclk.unwrap_or(pllsrcclk);
|
||||
if pllsysclk.is_none() && !pll48clk {
|
||||
RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8)));
|
||||
RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc::from_bits(use_hse as u8)));
|
||||
|
||||
return PllResults {
|
||||
use_pll: false,
|
||||
@ -141,9 +141,9 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, plli2s: Opti
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllm(pllm as u8);
|
||||
w.set_plln(plln as u16);
|
||||
w.set_pllp(Pllp(pllp as u8));
|
||||
w.set_pllp(Pllp::from_bits(pllp as u8));
|
||||
w.set_pllq(pllq as u8);
|
||||
w.set_pllsrc(Pllsrc(use_hse as u8));
|
||||
w.set_pllsrc(Pllsrc::from_bits(use_hse as u8));
|
||||
});
|
||||
|
||||
let real_pllsysclk = vco_in * plln / sysclk_div;
|
||||
@ -323,7 +323,7 @@ fn flash_setup(sysclk: u32) {
|
||||
critical_section::with(|_| {
|
||||
FLASH
|
||||
.acr()
|
||||
.modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
|
||||
.modify(|w| w.set_latency(Latency::from_bits(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
|
||||
});
|
||||
}
|
||||
|
||||
@ -440,8 +440,8 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
}
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_ppre2(Ppre(ppre2_bits));
|
||||
w.set_ppre1(Ppre(ppre1_bits));
|
||||
w.set_ppre2(Ppre::from_bits(ppre2_bits));
|
||||
w.set_ppre1(Ppre::from_bits(ppre1_bits));
|
||||
w.set_hpre(hpre_bits);
|
||||
});
|
||||
|
||||
|
@ -30,7 +30,7 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bo
|
||||
|
||||
let sysclk = pllsysclk.unwrap_or(pllsrcclk);
|
||||
if pllsysclk.is_none() && !pll48clk {
|
||||
RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8)));
|
||||
RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc::from_bits(use_hse as u8)));
|
||||
|
||||
return PllResults {
|
||||
use_pll: false,
|
||||
@ -83,9 +83,9 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bo
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllm(pllm as u8);
|
||||
w.set_plln(plln as u16);
|
||||
w.set_pllp(Pllp(pllp as u8));
|
||||
w.set_pllp(Pllp::from_bits(pllp as u8));
|
||||
w.set_pllq(pllq as u8);
|
||||
w.set_pllsrc(Pllsrc(use_hse as u8));
|
||||
w.set_pllsrc(Pllsrc::from_bits(use_hse as u8));
|
||||
});
|
||||
|
||||
let real_pllsysclk = vco_in * plln / sysclk_div;
|
||||
@ -106,7 +106,7 @@ fn flash_setup(sysclk: u32) {
|
||||
critical_section::with(|_| {
|
||||
FLASH
|
||||
.acr()
|
||||
.modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
|
||||
.modify(|w| w.set_latency(Latency::from_bits(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
|
||||
});
|
||||
}
|
||||
|
||||
@ -246,8 +246,8 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
}
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_ppre2(Ppre(ppre2_bits));
|
||||
w.set_ppre1(Ppre(ppre1_bits));
|
||||
w.set_ppre2(Ppre::from_bits(ppre2_bits));
|
||||
w.set_ppre1(Ppre::from_bits(ppre1_bits));
|
||||
w.set_hpre(hpre_bits);
|
||||
});
|
||||
|
||||
|
@ -344,7 +344,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
});
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
(HSI_FREQ.0 >> div.0, Sw::HSI)
|
||||
(HSI_FREQ.0 >> div.to_bits(), Sw::HSI)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
@ -381,7 +381,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
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 {
|
||||
if w.latency().to_bits() <= target_flash_latency.to_bits() {
|
||||
// We must increase the number of wait states now
|
||||
w.set_latency(target_flash_latency)
|
||||
} else {
|
||||
@ -395,12 +395,12 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
// > 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);
|
||||
w.set_prften(target_flash_latency.to_bits() > 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 {}
|
||||
while FLASH.acr().read().latency().to_bits() < target_flash_latency.to_bits() {}
|
||||
}
|
||||
|
||||
// Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once
|
||||
@ -442,7 +442,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.0 - 3);
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw};
|
||||
use stm32_metapac::FLASH;
|
||||
|
||||
use crate::pac::{PWR, RCC};
|
||||
use crate::rcc::sealed::RccPeripheral;
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
|
||||
@ -316,6 +317,27 @@ impl Into<Hpre> for AHBPrescaler {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the source for the 48MHz clock to the USB and RNG peripherals.
|
||||
pub enum Clock48MhzSrc {
|
||||
/// Use the High Speed Internal Oscillator. For USB usage, the CRS must be used to calibrate the
|
||||
/// oscillator to comply with the USB specification for oscillator tolerance.
|
||||
Hsi48(Option<CrsConfig>),
|
||||
/// Use the PLLQ output. The PLL must be configured to output a 48MHz clock. For USB usage the
|
||||
/// PLL needs to be using the HSE source to comply with the USB specification for oscillator
|
||||
/// tolerance.
|
||||
PllQ,
|
||||
}
|
||||
|
||||
/// Sets the sync source for the Clock Recovery System (CRS).
|
||||
pub enum CrsSyncSource {
|
||||
/// Use an external GPIO to sync the CRS.
|
||||
Gpio,
|
||||
/// Use the Low Speed External oscillator to sync the CRS.
|
||||
Lse,
|
||||
/// Use the USB SOF to sync the CRS.
|
||||
Usb,
|
||||
}
|
||||
|
||||
/// Clocks configutation
|
||||
pub struct Config {
|
||||
pub mux: ClockSrc,
|
||||
@ -326,6 +348,14 @@ pub struct Config {
|
||||
/// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration
|
||||
/// MUST turn on the PLLR output.
|
||||
pub pll: Option<Pll>,
|
||||
/// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals.
|
||||
pub clock_48mhz_src: Option<Clock48MhzSrc>,
|
||||
}
|
||||
|
||||
/// Configuration for the Clock Recovery System (CRS) used to trim the HSI48 oscillator.
|
||||
pub struct CrsConfig {
|
||||
/// Sync source for the CRS.
|
||||
pub sync_src: CrsSyncSource,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -338,6 +368,7 @@ impl Default for Config {
|
||||
apb2_pre: APBPrescaler::NotDivided,
|
||||
low_power_run: false,
|
||||
pll: None,
|
||||
clock_48mhz_src: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -430,7 +461,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
assert!(pll_freq.is_some());
|
||||
assert!(pll_freq.as_ref().unwrap().pll_r.is_some());
|
||||
|
||||
let freq = pll_freq.unwrap().pll_r.unwrap().0;
|
||||
let freq = pll_freq.as_ref().unwrap().pll_r.unwrap().0;
|
||||
|
||||
assert!(freq <= 170_000_000);
|
||||
|
||||
@ -497,6 +528,50 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
}
|
||||
};
|
||||
|
||||
// Setup the 48 MHz clock if needed
|
||||
if let Some(clock_48mhz_src) = config.clock_48mhz_src {
|
||||
let source = match clock_48mhz_src {
|
||||
Clock48MhzSrc::PllQ => {
|
||||
// Make sure the PLLQ is enabled and running at 48Mhz
|
||||
let pllq_freq = pll_freq.as_ref().and_then(|f| f.pll_q);
|
||||
assert!(pllq_freq.is_some() && pllq_freq.unwrap().0 == 48_000_000);
|
||||
|
||||
crate::pac::rcc::vals::Clk48sel::PLLQCLK
|
||||
}
|
||||
Clock48MhzSrc::Hsi48(crs_config) => {
|
||||
// Enable HSI48
|
||||
RCC.crrcr().modify(|w| w.set_hsi48on(true));
|
||||
// Wait for HSI48 to turn on
|
||||
while RCC.crrcr().read().hsi48rdy() == false {}
|
||||
|
||||
// Enable and setup CRS if needed
|
||||
if let Some(crs_config) = crs_config {
|
||||
crate::peripherals::CRS::enable();
|
||||
|
||||
let sync_src = match crs_config.sync_src {
|
||||
CrsSyncSource::Gpio => crate::pac::crs::vals::Syncsrc::GPIO,
|
||||
CrsSyncSource::Lse => crate::pac::crs::vals::Syncsrc::LSE,
|
||||
CrsSyncSource::Usb => crate::pac::crs::vals::Syncsrc::USB,
|
||||
};
|
||||
|
||||
crate::pac::CRS.cfgr().modify(|w| {
|
||||
w.set_syncsrc(sync_src);
|
||||
});
|
||||
|
||||
// These are the correct settings for standard USB operation. If other settings
|
||||
// are needed there will need to be additional config options for the CRS.
|
||||
crate::pac::CRS.cr().modify(|w| {
|
||||
w.set_autotrimen(true);
|
||||
w.set_cen(true);
|
||||
});
|
||||
}
|
||||
crate::pac::rcc::vals::Clk48sel::HSI48
|
||||
}
|
||||
};
|
||||
|
||||
RCC.ccipr().modify(|w| w.set_clk48sel(source));
|
||||
}
|
||||
|
||||
if config.low_power_run {
|
||||
assert!(sys_clk <= 2_000_000);
|
||||
PWR.cr1().modify(|w| w.set_lpr(true));
|
||||
|
@ -601,22 +601,22 @@ pub(crate) unsafe fn init(mut config: Config) {
|
||||
|
||||
// Core Prescaler / AHB Prescaler / APB3 Prescaler
|
||||
RCC.d1cfgr().modify(|w| {
|
||||
w.set_d1cpre(Hpre(d1cpre_bits));
|
||||
w.set_d1ppre(Dppre(ppre3_bits));
|
||||
w.set_d1cpre(Hpre::from_bits(d1cpre_bits));
|
||||
w.set_d1ppre(Dppre::from_bits(ppre3_bits));
|
||||
w.set_hpre(hpre_bits)
|
||||
});
|
||||
// Ensure core prescaler value is valid before future lower
|
||||
// core voltage
|
||||
while RCC.d1cfgr().read().d1cpre().0 != d1cpre_bits {}
|
||||
while RCC.d1cfgr().read().d1cpre().to_bits() != d1cpre_bits {}
|
||||
|
||||
// APB1 / APB2 Prescaler
|
||||
RCC.d2cfgr().modify(|w| {
|
||||
w.set_d2ppre1(Dppre(ppre1_bits));
|
||||
w.set_d2ppre2(Dppre(ppre2_bits));
|
||||
w.set_d2ppre1(Dppre::from_bits(ppre1_bits));
|
||||
w.set_d2ppre2(Dppre::from_bits(ppre2_bits));
|
||||
});
|
||||
|
||||
// APB4 Prescaler
|
||||
RCC.d3cfgr().modify(|w| w.set_d3ppre(Dppre(ppre4_bits)));
|
||||
RCC.d3cfgr().modify(|w| w.set_d3ppre(Dppre::from_bits(ppre4_bits)));
|
||||
|
||||
// Peripheral Clock (per_ck)
|
||||
RCC.d1ccipr().modify(|w| w.set_ckpersel(ckpersel));
|
||||
@ -640,7 +640,7 @@ pub(crate) unsafe fn init(mut config: Config) {
|
||||
_ => Sw::HSI,
|
||||
};
|
||||
RCC.cfgr().modify(|w| w.set_sw(sw));
|
||||
while RCC.cfgr().read().sws() != sw.0 {}
|
||||
while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {}
|
||||
|
||||
// IO compensation cell - Requires CSI clock and SYSCFG
|
||||
assert!(RCC.cr().read().csirdy());
|
||||
@ -806,7 +806,8 @@ mod pll {
|
||||
RCC.pllcfgr().modify(|w| w.set_pllfracen(plln, false));
|
||||
let vco_ck = ref_x_ck * pll_x_n;
|
||||
|
||||
RCC.plldivr(plln).modify(|w| w.set_divp1(Divp((pll_x_p - 1) as u8)));
|
||||
RCC.plldivr(plln)
|
||||
.modify(|w| w.set_divp1(Divp::from_bits((pll_x_p - 1) as u8)));
|
||||
RCC.pllcfgr().modify(|w| w.set_divpen(plln, true));
|
||||
|
||||
// Calulate additional output dividers
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw};
|
||||
use crate::pac::RCC;
|
||||
#[cfg(crs)]
|
||||
use crate::pac::{CRS, SYSCFG};
|
||||
use crate::pac::{crs, CRS, SYSCFG};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
|
||||
@ -293,7 +293,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: Hpre = pre.into();
|
||||
let pre = 1 << (pre.0 as u32 - 7);
|
||||
let pre = 1 << (pre.to_bits() as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
@ -302,7 +302,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.0 - 3);
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
@ -312,7 +312,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.0 - 3);
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
@ -338,7 +338,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
CRS.cfgr().write(|w|
|
||||
|
||||
// Select LSE as synchronization source
|
||||
w.set_syncsrc(0b01));
|
||||
w.set_syncsrc(crs::vals::Syncsrc::LSE));
|
||||
CRS.cr().modify(|w| {
|
||||
w.set_autotrimen(true);
|
||||
w.set_cen(true);
|
||||
|
@ -294,7 +294,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: Hpre = pre.into();
|
||||
let pre = 1 << (pre.0 as u32 - 7);
|
||||
let pre = 1 << (pre.to_bits() as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
@ -303,7 +303,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.0 - 3);
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
@ -313,7 +313,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.0 - 3);
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
|
@ -635,7 +635,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: Hpre = pre.into();
|
||||
let pre = 1 << (pre.0 as u32 - 7);
|
||||
let pre = 1 << (pre.to_bits() as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
@ -644,7 +644,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.0 - 3);
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
@ -654,7 +654,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.0 - 3);
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
|
@ -461,7 +461,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: Hpre = pre.into();
|
||||
let pre = 1 << (pre.0 as u32 - 7);
|
||||
let pre = 1 << (pre.to_bits() as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
@ -470,7 +470,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.0 - 3);
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
@ -480,7 +480,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.0 - 3);
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ pub enum PllM {
|
||||
|
||||
impl Into<Pllm> for PllM {
|
||||
fn into(self) -> Pllm {
|
||||
Pllm(self as u8)
|
||||
Pllm::from_bits(self as u8)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
|
||||
#[cfg(rtc_v2wb)]
|
||||
let rtcsel = reg.rtcsel();
|
||||
#[cfg(not(rtc_v2wb))]
|
||||
let rtcsel = reg.rtcsel().0;
|
||||
let rtcsel = reg.rtcsel().to_bits();
|
||||
|
||||
if !reg.rtcen() || rtcsel != clock_config {
|
||||
#[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
|
||||
@ -54,7 +54,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
|
||||
|
||||
// Select RTC source
|
||||
#[cfg(not(rtc_v2wb))]
|
||||
w.set_rtcsel(Rtcsel(clock_config));
|
||||
w.set_rtcsel(Rtcsel::from_bits(clock_config));
|
||||
#[cfg(rtc_v2wb)]
|
||||
w.set_rtcsel(clock_config);
|
||||
w.set_rtcen(true);
|
||||
|
@ -26,7 +26,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
|
||||
|
||||
let config_rtcsel = rtc_config.clock_config as u8;
|
||||
#[cfg(not(any(rcc_wl5, rcc_wle)))]
|
||||
let config_rtcsel = crate::pac::rcc::vals::Rtcsel(config_rtcsel);
|
||||
let config_rtcsel = crate::pac::rcc::vals::Rtcsel::from_bits(config_rtcsel);
|
||||
|
||||
if !reg.rtcen() || reg.rtcsel() != config_rtcsel {
|
||||
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
|
||||
|
@ -227,7 +227,11 @@ const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOp
|
||||
fifo_threshold: Some(crate::dma::FifoThreshold::Full),
|
||||
};
|
||||
#[cfg(all(sdmmc_v1, not(dma)))]
|
||||
const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions {};
|
||||
const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions {
|
||||
circular: false,
|
||||
half_transfer_ir: false,
|
||||
complete_transfer_ir: true,
|
||||
};
|
||||
|
||||
/// SDMMC configuration
|
||||
///
|
||||
|
@ -650,7 +650,7 @@ fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> Br {
|
||||
_ => 0b111,
|
||||
};
|
||||
|
||||
Br(val)
|
||||
Br::from_bits(val)
|
||||
}
|
||||
|
||||
trait RegsExt {
|
||||
@ -772,7 +772,7 @@ fn set_rxdmaen(regs: Regs, val: bool) {
|
||||
|
||||
fn finish_dma(regs: Regs) {
|
||||
#[cfg(spi_v2)]
|
||||
while regs.sr().read().ftlvl() > 0 {}
|
||||
while regs.sr().read().ftlvl().to_bits() > 0 {}
|
||||
|
||||
#[cfg(any(spi_v3, spi_v4, spi_v5))]
|
||||
while !regs.sr().read().txc() {}
|
||||
|
@ -869,7 +869,7 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx:
|
||||
_ => vals::Ps::EVEN,
|
||||
});
|
||||
#[cfg(not(usart_v1))]
|
||||
w.set_over8(vals::Over8(over8 as _));
|
||||
w.set_over8(vals::Over8::from_bits(over8 as _));
|
||||
});
|
||||
|
||||
#[cfg(not(usart_v1))]
|
||||
|
@ -97,8 +97,8 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
}
|
||||
epr.set_dtog_rx(false);
|
||||
epr.set_dtog_tx(false);
|
||||
epr.set_stat_rx(Stat(0));
|
||||
epr.set_stat_tx(Stat(0));
|
||||
epr.set_stat_rx(Stat::from_bits(0));
|
||||
epr.set_stat_tx(Stat::from_bits(0));
|
||||
epr.set_ctr_rx(!epr.ctr_rx());
|
||||
epr.set_ctr_tx(!epr.ctr_tx());
|
||||
regs.epr(index).write_value(epr);
|
||||
@ -143,8 +143,8 @@ fn invariant(mut r: regs::Epr) -> regs::Epr {
|
||||
r.set_ctr_tx(true); // don't clear
|
||||
r.set_dtog_rx(false); // don't toggle
|
||||
r.set_dtog_tx(false); // don't toggle
|
||||
r.set_stat_rx(Stat(0));
|
||||
r.set_stat_tx(Stat(0));
|
||||
r.set_stat_rx(Stat::from_bits(0));
|
||||
r.set_stat_tx(Stat::from_bits(0));
|
||||
r
|
||||
}
|
||||
|
||||
@ -480,56 +480,57 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
poll_fn(move |cx| {
|
||||
BUS_WAKER.register(cx.waker());
|
||||
|
||||
if self.inited {
|
||||
let regs = T::regs();
|
||||
|
||||
if IRQ_RESUME.load(Ordering::Acquire) {
|
||||
IRQ_RESUME.store(false, Ordering::Relaxed);
|
||||
return Poll::Ready(Event::Resume);
|
||||
}
|
||||
|
||||
if IRQ_RESET.load(Ordering::Acquire) {
|
||||
IRQ_RESET.store(false, Ordering::Relaxed);
|
||||
|
||||
trace!("RESET");
|
||||
regs.daddr().write(|w| {
|
||||
w.set_ef(true);
|
||||
w.set_add(0);
|
||||
});
|
||||
|
||||
regs.epr(0).write(|w| {
|
||||
w.set_ep_type(EpType::CONTROL);
|
||||
w.set_stat_rx(Stat::NAK);
|
||||
w.set_stat_tx(Stat::NAK);
|
||||
});
|
||||
|
||||
for i in 1..EP_COUNT {
|
||||
regs.epr(i).write(|w| {
|
||||
w.set_ea(i as _);
|
||||
w.set_ep_type(self.ep_types[i - 1]);
|
||||
})
|
||||
}
|
||||
|
||||
for w in &EP_IN_WAKERS {
|
||||
w.wake()
|
||||
}
|
||||
for w in &EP_OUT_WAKERS {
|
||||
w.wake()
|
||||
}
|
||||
|
||||
return Poll::Ready(Event::Reset);
|
||||
}
|
||||
|
||||
if IRQ_SUSPEND.load(Ordering::Acquire) {
|
||||
IRQ_SUSPEND.store(false, Ordering::Relaxed);
|
||||
return Poll::Ready(Event::Suspend);
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
} else {
|
||||
// TODO: implement VBUS detection.
|
||||
if !self.inited {
|
||||
self.inited = true;
|
||||
return Poll::Ready(Event::PowerDetected);
|
||||
}
|
||||
|
||||
let regs = T::regs();
|
||||
|
||||
if IRQ_RESUME.load(Ordering::Acquire) {
|
||||
IRQ_RESUME.store(false, Ordering::Relaxed);
|
||||
return Poll::Ready(Event::Resume);
|
||||
}
|
||||
|
||||
if IRQ_RESET.load(Ordering::Acquire) {
|
||||
IRQ_RESET.store(false, Ordering::Relaxed);
|
||||
|
||||
trace!("RESET");
|
||||
regs.daddr().write(|w| {
|
||||
w.set_ef(true);
|
||||
w.set_add(0);
|
||||
});
|
||||
|
||||
regs.epr(0).write(|w| {
|
||||
w.set_ep_type(EpType::CONTROL);
|
||||
w.set_stat_rx(Stat::NAK);
|
||||
w.set_stat_tx(Stat::NAK);
|
||||
});
|
||||
|
||||
for i in 1..EP_COUNT {
|
||||
regs.epr(i).write(|w| {
|
||||
w.set_ea(i as _);
|
||||
w.set_ep_type(self.ep_types[i - 1]);
|
||||
})
|
||||
}
|
||||
|
||||
for w in &EP_IN_WAKERS {
|
||||
w.wake()
|
||||
}
|
||||
for w in &EP_OUT_WAKERS {
|
||||
w.wake()
|
||||
}
|
||||
|
||||
return Poll::Ready(Event::Reset);
|
||||
}
|
||||
|
||||
if IRQ_SUSPEND.load(Ordering::Acquire) {
|
||||
IRQ_SUSPEND.store(false, Ordering::Relaxed);
|
||||
return Poll::Ready(Event::Suspend);
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
.await
|
||||
}
|
||||
@ -550,7 +551,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
true => Stat::STALL,
|
||||
};
|
||||
let mut w = invariant(r);
|
||||
w.set_stat_tx(Stat(r.stat_tx().0 ^ want_stat.0));
|
||||
w.set_stat_tx(Stat::from_bits(r.stat_tx().to_bits() ^ want_stat.to_bits()));
|
||||
reg.write_value(w);
|
||||
}
|
||||
}
|
||||
@ -569,7 +570,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
true => Stat::STALL,
|
||||
};
|
||||
let mut w = invariant(r);
|
||||
w.set_stat_rx(Stat(r.stat_rx().0 ^ want_stat.0));
|
||||
w.set_stat_rx(Stat::from_bits(r.stat_rx().to_bits() ^ want_stat.to_bits()));
|
||||
reg.write_value(w);
|
||||
}
|
||||
}
|
||||
@ -605,7 +606,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
break;
|
||||
}
|
||||
let mut w = invariant(r);
|
||||
w.set_stat_tx(Stat(r.stat_tx().0 ^ want_stat.0));
|
||||
w.set_stat_tx(Stat::from_bits(r.stat_tx().to_bits() ^ want_stat.to_bits()));
|
||||
reg.write_value(w);
|
||||
}
|
||||
EP_IN_WAKERS[ep_addr.index()].wake();
|
||||
@ -621,7 +622,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
break;
|
||||
}
|
||||
let mut w = invariant(r);
|
||||
w.set_stat_rx(Stat(r.stat_rx().0 ^ want_stat.0));
|
||||
w.set_stat_rx(Stat::from_bits(r.stat_rx().to_bits() ^ want_stat.to_bits()));
|
||||
reg.write_value(w);
|
||||
}
|
||||
EP_OUT_WAKERS[ep_addr.index()].wake();
|
||||
@ -762,8 +763,8 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> {
|
||||
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_stat_rx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits()));
|
||||
w.set_stat_tx(Stat::from_bits(0));
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
});
|
||||
@ -804,8 +805,8 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> {
|
||||
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_stat_tx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits()));
|
||||
w.set_stat_rx(Stat::from_bits(0));
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
});
|
||||
@ -868,19 +869,19 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
|
||||
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;
|
||||
stat_rx ^= Stat::NAK.to_bits() ^ Stat::VALID.to_bits();
|
||||
stat_tx ^= Stat::NAK.to_bits() ^ Stat::STALL.to_bits();
|
||||
}
|
||||
if last {
|
||||
// change STALL -> VALID
|
||||
stat_tx ^= Stat::STALL.0 ^ Stat::NAK.0;
|
||||
stat_tx ^= Stat::STALL.to_bits() ^ Stat::NAK.to_bits();
|
||||
}
|
||||
// Note: if this is the first AND last transfer, the above effectively
|
||||
// changes stat_tx like NAK -> NAK, so noop.
|
||||
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_stat_rx(Stat::from_bits(stat_rx));
|
||||
w.set_stat_tx(Stat::from_bits(stat_tx));
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
});
|
||||
@ -907,11 +908,11 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
|
||||
|
||||
regs.epr(0).write(|w| {
|
||||
w.set_ep_type(EpType::CONTROL);
|
||||
w.set_stat_rx(Stat(match last {
|
||||
w.set_stat_rx(Stat::from_bits(match last {
|
||||
// If last, set STAT_RX=STALL.
|
||||
true => Stat::NAK.0 ^ Stat::STALL.0,
|
||||
true => Stat::NAK.to_bits() ^ Stat::STALL.to_bits(),
|
||||
// Otherwise, set STAT_RX=VALID, to allow the host to send the next packet.
|
||||
false => Stat::NAK.0 ^ Stat::VALID.0,
|
||||
false => Stat::NAK.to_bits() ^ Stat::VALID.to_bits(),
|
||||
}));
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
@ -936,17 +937,17 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
|
||||
let mut stat_rx = 0;
|
||||
if first {
|
||||
// change NAK -> STALL
|
||||
stat_rx ^= Stat::NAK.0 ^ Stat::STALL.0;
|
||||
stat_rx ^= Stat::NAK.to_bits() ^ Stat::STALL.to_bits();
|
||||
}
|
||||
if last {
|
||||
// change STALL -> VALID
|
||||
stat_rx ^= Stat::STALL.0 ^ Stat::VALID.0;
|
||||
stat_rx ^= Stat::STALL.to_bits() ^ Stat::VALID.to_bits();
|
||||
}
|
||||
// Note: if this is the first AND last transfer, the above effectively
|
||||
// does a change of NAK -> VALID.
|
||||
regs.epr(0).write(|w| {
|
||||
w.set_ep_type(EpType::CONTROL);
|
||||
w.set_stat_rx(Stat(stat_rx));
|
||||
w.set_stat_rx(Stat::from_bits(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
|
||||
@ -976,7 +977,7 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
|
||||
let regs = T::regs();
|
||||
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_tx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits()));
|
||||
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
|
||||
@ -997,8 +998,8 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
|
||||
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_stat_rx(Stat::from_bits(epr.stat_rx().to_bits() ^ Stat::STALL.to_bits()));
|
||||
w.set_stat_tx(Stat::from_bits(epr.stat_tx().to_bits() ^ Stat::VALID.to_bits()));
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
});
|
||||
@ -1028,8 +1029,8 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
|
||||
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_stat_rx(Stat::from_bits(epr.stat_rx().to_bits() ^ Stat::STALL.to_bits()));
|
||||
w.set_stat_tx(Stat::from_bits(epr.stat_tx().to_bits() ^ Stat::STALL.to_bits()));
|
||||
w.set_ctr_rx(true); // don't clear
|
||||
w.set_ctr_tx(true); // don't clear
|
||||
});
|
||||
|
@ -6,8 +6,8 @@ use atomic_polyfill::{AtomicBool, AtomicU16, Ordering};
|
||||
use embassy_hal_common::{into_ref, Peripheral};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use embassy_usb_driver::{
|
||||
self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo, EndpointOut,
|
||||
EndpointType, Event, Unsupported,
|
||||
self, Bus as _, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo,
|
||||
EndpointOut, EndpointType, Event, Unsupported,
|
||||
};
|
||||
use futures::future::poll_fn;
|
||||
|
||||
@ -31,7 +31,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
let state = T::state();
|
||||
|
||||
let ints = r.gintsts().read();
|
||||
if ints.wkupint() || ints.usbsusp() || ints.usbrst() || ints.enumdne() {
|
||||
if ints.wkupint() || ints.usbsusp() || ints.usbrst() || ints.enumdne() || ints.otgint() || ints.srqint() {
|
||||
// Mask interrupts and notify `Bus` to process them
|
||||
r.gintmsk().write(|_| {});
|
||||
T::state().bus_waker.wake();
|
||||
@ -97,7 +97,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
vals::Pktstsd::SETUP_DATA_DONE => {
|
||||
trace!("SETUP_DATA_DONE ep={}", ep_num);
|
||||
}
|
||||
x => trace!("unknown PKTSTS: {}", x.0),
|
||||
x => trace!("unknown PKTSTS: {}", x.to_bits()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,7 +124,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
}
|
||||
|
||||
state.ep_in_wakers[ep_num].wake();
|
||||
trace!("in ep={} irq val={:b}", ep_num, ep_ints.0);
|
||||
trace!("in ep={} irq val={:08x}", ep_num, ep_ints.0);
|
||||
}
|
||||
|
||||
ep_mask >>= 1;
|
||||
@ -144,7 +144,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
// // clear all
|
||||
// r.doepint(ep_num).write_value(ep_ints);
|
||||
// state.ep_out_wakers[ep_num].wake();
|
||||
// trace!("out ep={} irq val={=u32:b}", ep_num, ep_ints.0);
|
||||
// trace!("out ep={} irq val={:08x}", ep_num, ep_ints.0);
|
||||
// }
|
||||
|
||||
// ep_mask >>= 1;
|
||||
@ -256,7 +256,34 @@ struct EndpointData {
|
||||
fifo_size_words: u16,
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub struct Config {
|
||||
/// Enable VBUS detection.
|
||||
///
|
||||
/// The USB spec requires USB devices monitor for USB cable plug/unplug and react accordingly.
|
||||
/// This is done by checkihg whether there is 5V on the VBUS pin or not.
|
||||
///
|
||||
/// If your device is bus-powered (powers itself from the USB host via VBUS), then this is optional.
|
||||
/// (if there's no power in VBUS your device would be off anyway, so it's fine to always assume
|
||||
/// there's power in VBUS, i.e. the USB cable is always plugged in.)
|
||||
///
|
||||
/// If your device is self-powered (i.e. it gets power from a source other than the USB cable, and
|
||||
/// therefore can stay powered through USB cable plug/unplug) then you MUST set this to true.
|
||||
///
|
||||
/// If you set this to true, you must connect VBUS to PA9 for FS, PB13 for HS, possibly with a
|
||||
/// voltage divider. See ST application note AN4879 and the reference manual for more details.
|
||||
pub vbus_detection: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self { vbus_detection: true }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Driver<'d, T: Instance> {
|
||||
config: Config,
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
ep_in: [Option<EndpointData>; MAX_EP_COUNT],
|
||||
ep_out: [Option<EndpointData>; MAX_EP_COUNT],
|
||||
@ -279,6 +306,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
dp: impl Peripheral<P = impl DpPin<T>> + 'd,
|
||||
dm: impl Peripheral<P = impl DmPin<T>> + 'd,
|
||||
ep_out_buffer: &'d mut [u8],
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(dp, dm);
|
||||
|
||||
@ -286,6 +314,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
dm.set_as_af(dm.af_num(), AFType::OutputPushPull);
|
||||
|
||||
Self {
|
||||
config,
|
||||
phantom: PhantomData,
|
||||
ep_in: [None; MAX_EP_COUNT],
|
||||
ep_out: [None; MAX_EP_COUNT],
|
||||
@ -318,6 +347,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
ulpi_d6: impl Peripheral<P = impl UlpiD6Pin<T>> + 'd,
|
||||
ulpi_d7: impl Peripheral<P = impl UlpiD7Pin<T>> + 'd,
|
||||
ep_out_buffer: &'d mut [u8],
|
||||
config: Config,
|
||||
) -> Self {
|
||||
assert!(T::HIGH_SPEED == true, "Peripheral is not capable of high-speed USB");
|
||||
|
||||
@ -327,6 +357,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
);
|
||||
|
||||
Self {
|
||||
config,
|
||||
phantom: PhantomData,
|
||||
ep_in: [None; MAX_EP_COUNT],
|
||||
ep_out: [None; MAX_EP_COUNT],
|
||||
@ -464,11 +495,12 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> {
|
||||
|
||||
(
|
||||
Bus {
|
||||
config: self.config,
|
||||
phantom: PhantomData,
|
||||
ep_in: self.ep_in,
|
||||
ep_out: self.ep_out,
|
||||
phy_type: self.phy_type,
|
||||
enabled: false,
|
||||
inited: false,
|
||||
},
|
||||
ControlPipe {
|
||||
_phantom: PhantomData,
|
||||
@ -481,11 +513,12 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> {
|
||||
}
|
||||
|
||||
pub struct Bus<'d, T: Instance> {
|
||||
config: Config,
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
ep_in: [Option<EndpointData>; MAX_EP_COUNT],
|
||||
ep_out: [Option<EndpointData>; MAX_EP_COUNT],
|
||||
phy_type: PhyType,
|
||||
enabled: bool,
|
||||
inited: bool,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Bus<'d, T> {
|
||||
@ -498,282 +531,14 @@ impl<'d, T: Instance> Bus<'d, T> {
|
||||
w.set_iepint(true);
|
||||
w.set_oepint(true);
|
||||
w.set_rxflvlm(true);
|
||||
w.set_srqim(true);
|
||||
w.set_otgint(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Bus<'d, T> {
|
||||
fn init_fifo(&mut self) {
|
||||
trace!("init_fifo");
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
// Configure RX fifo size. All endpoints share the same FIFO area.
|
||||
let rx_fifo_size_words = RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out);
|
||||
trace!("configuring rx fifo size={}", rx_fifo_size_words);
|
||||
|
||||
r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words));
|
||||
|
||||
// Configure TX (USB in direction) fifo size for each endpoint
|
||||
let mut fifo_top = rx_fifo_size_words;
|
||||
for i in 0..T::ENDPOINT_COUNT {
|
||||
if let Some(ep) = self.ep_in[i] {
|
||||
trace!(
|
||||
"configuring tx fifo ep={}, offset={}, size={}",
|
||||
i,
|
||||
fifo_top,
|
||||
ep.fifo_size_words
|
||||
);
|
||||
|
||||
let dieptxf = if i == 0 { r.dieptxf0() } else { r.dieptxf(i - 1) };
|
||||
|
||||
dieptxf.write(|w| {
|
||||
w.set_fd(ep.fifo_size_words);
|
||||
w.set_sa(fifo_top);
|
||||
});
|
||||
|
||||
fifo_top += ep.fifo_size_words;
|
||||
}
|
||||
}
|
||||
|
||||
assert!(
|
||||
fifo_top <= T::FIFO_DEPTH_WORDS,
|
||||
"FIFO allocations exceeded maximum capacity"
|
||||
);
|
||||
}
|
||||
|
||||
fn configure_endpoints(&mut self) {
|
||||
trace!("configure_endpoints");
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
// Configure IN endpoints
|
||||
for (index, ep) in self.ep_in.iter().enumerate() {
|
||||
if let Some(ep) = ep {
|
||||
critical_section::with(|_| {
|
||||
r.diepctl(index).write(|w| {
|
||||
if index == 0 {
|
||||
w.set_mpsiz(ep0_mpsiz(ep.max_packet_size));
|
||||
} else {
|
||||
w.set_mpsiz(ep.max_packet_size);
|
||||
w.set_eptyp(to_eptyp(ep.ep_type));
|
||||
w.set_sd0pid_sevnfrm(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Configure OUT endpoints
|
||||
for (index, ep) in self.ep_out.iter().enumerate() {
|
||||
if let Some(ep) = ep {
|
||||
critical_section::with(|_| {
|
||||
r.doepctl(index).write(|w| {
|
||||
if index == 0 {
|
||||
w.set_mpsiz(ep0_mpsiz(ep.max_packet_size));
|
||||
} else {
|
||||
w.set_mpsiz(ep.max_packet_size);
|
||||
w.set_eptyp(to_eptyp(ep.ep_type));
|
||||
w.set_sd0pid_sevnfrm(true);
|
||||
}
|
||||
});
|
||||
|
||||
r.doeptsiz(index).modify(|w| {
|
||||
w.set_xfrsiz(ep.max_packet_size as _);
|
||||
if index == 0 {
|
||||
w.set_rxdpid_stupcnt(1);
|
||||
} else {
|
||||
w.set_pktcnt(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Enable IRQs for allocated endpoints
|
||||
r.daintmsk().modify(|w| {
|
||||
w.set_iepm(ep_irq_mask(&self.ep_in));
|
||||
// OUT interrupts not used, handled in RXFLVL
|
||||
// w.set_oepm(ep_irq_mask(&self.ep_out));
|
||||
});
|
||||
}
|
||||
|
||||
fn disable(&mut self) {
|
||||
T::Interrupt::disable();
|
||||
|
||||
<T as RccPeripheral>::disable();
|
||||
|
||||
#[cfg(stm32l4)]
|
||||
crate::pac::PWR.cr2().modify(|w| w.set_usv(false));
|
||||
// Cannot disable PWR, because other peripherals might be using it
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
|
||||
async fn poll(&mut self) -> Event {
|
||||
poll_fn(move |cx| {
|
||||
// TODO: implement VBUS detection
|
||||
if !self.enabled {
|
||||
return Poll::Ready(Event::PowerDetected);
|
||||
}
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
T::state().bus_waker.register(cx.waker());
|
||||
|
||||
let ints = r.gintsts().read();
|
||||
if ints.usbrst() {
|
||||
trace!("reset");
|
||||
|
||||
self.init_fifo();
|
||||
self.configure_endpoints();
|
||||
|
||||
// Reset address
|
||||
critical_section::with(|_| {
|
||||
r.dcfg().modify(|w| {
|
||||
w.set_dad(0);
|
||||
});
|
||||
});
|
||||
|
||||
r.gintsts().write(|w| w.set_usbrst(true)); // clear
|
||||
Self::restore_irqs();
|
||||
}
|
||||
|
||||
if ints.enumdne() {
|
||||
trace!("enumdne");
|
||||
|
||||
let speed = r.dsts().read().enumspd();
|
||||
trace!(" speed={}", speed.0);
|
||||
|
||||
r.gusbcfg().modify(|w| {
|
||||
w.set_trdt(calculate_trdt(speed, T::frequency()));
|
||||
});
|
||||
|
||||
r.gintsts().write(|w| w.set_enumdne(true)); // clear
|
||||
Self::restore_irqs();
|
||||
|
||||
return Poll::Ready(Event::Reset);
|
||||
}
|
||||
|
||||
if ints.usbsusp() {
|
||||
trace!("suspend");
|
||||
r.gintsts().write(|w| w.set_usbsusp(true)); // clear
|
||||
Self::restore_irqs();
|
||||
return Poll::Ready(Event::Suspend);
|
||||
}
|
||||
|
||||
if ints.wkupint() {
|
||||
trace!("resume");
|
||||
r.gintsts().write(|w| w.set_wkupint(true)); // clear
|
||||
Self::restore_irqs();
|
||||
return Poll::Ready(Event::Resume);
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {
|
||||
trace!("endpoint_set_stalled ep={:?} en={}", ep_addr, stalled);
|
||||
|
||||
assert!(
|
||||
ep_addr.index() < T::ENDPOINT_COUNT,
|
||||
"endpoint_set_stalled index {} out of range",
|
||||
ep_addr.index()
|
||||
);
|
||||
|
||||
let regs = T::regs();
|
||||
match ep_addr.direction() {
|
||||
Direction::Out => {
|
||||
critical_section::with(|_| {
|
||||
regs.doepctl(ep_addr.index()).modify(|w| {
|
||||
w.set_stall(stalled);
|
||||
});
|
||||
});
|
||||
|
||||
T::state().ep_out_wakers[ep_addr.index()].wake();
|
||||
}
|
||||
Direction::In => {
|
||||
critical_section::with(|_| {
|
||||
regs.diepctl(ep_addr.index()).modify(|w| {
|
||||
w.set_stall(stalled);
|
||||
});
|
||||
});
|
||||
|
||||
T::state().ep_in_wakers[ep_addr.index()].wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {
|
||||
assert!(
|
||||
ep_addr.index() < T::ENDPOINT_COUNT,
|
||||
"endpoint_is_stalled index {} out of range",
|
||||
ep_addr.index()
|
||||
);
|
||||
|
||||
let regs = T::regs();
|
||||
|
||||
match ep_addr.direction() {
|
||||
Direction::Out => regs.doepctl(ep_addr.index()).read().stall(),
|
||||
Direction::In => regs.diepctl(ep_addr.index()).read().stall(),
|
||||
}
|
||||
}
|
||||
|
||||
fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) {
|
||||
trace!("endpoint_set_enabled ep={:?} en={}", ep_addr, enabled);
|
||||
|
||||
assert!(
|
||||
ep_addr.index() < T::ENDPOINT_COUNT,
|
||||
"endpoint_set_enabled index {} out of range",
|
||||
ep_addr.index()
|
||||
);
|
||||
|
||||
let r = T::regs();
|
||||
match ep_addr.direction() {
|
||||
Direction::Out => {
|
||||
critical_section::with(|_| {
|
||||
// cancel transfer if active
|
||||
if !enabled && r.doepctl(ep_addr.index()).read().epena() {
|
||||
r.doepctl(ep_addr.index()).modify(|w| {
|
||||
w.set_snak(true);
|
||||
w.set_epdis(true);
|
||||
})
|
||||
}
|
||||
|
||||
r.doepctl(ep_addr.index()).modify(|w| {
|
||||
w.set_usbaep(enabled);
|
||||
})
|
||||
});
|
||||
|
||||
// Wake `Endpoint::wait_enabled()`
|
||||
T::state().ep_out_wakers[ep_addr.index()].wake();
|
||||
}
|
||||
Direction::In => {
|
||||
critical_section::with(|_| {
|
||||
// cancel transfer if active
|
||||
if !enabled && r.diepctl(ep_addr.index()).read().epena() {
|
||||
r.diepctl(ep_addr.index()).modify(|w| {
|
||||
w.set_snak(true);
|
||||
w.set_epdis(true);
|
||||
})
|
||||
}
|
||||
|
||||
r.diepctl(ep_addr.index()).modify(|w| {
|
||||
w.set_usbaep(enabled);
|
||||
})
|
||||
});
|
||||
|
||||
// Wake `Endpoint::wait_enabled()`
|
||||
T::state().ep_in_wakers[ep_addr.index()].wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn enable(&mut self) {
|
||||
trace!("enable");
|
||||
|
||||
fn init(&mut self) {
|
||||
#[cfg(stm32l4)]
|
||||
{
|
||||
crate::peripherals::PWR::enable();
|
||||
@ -908,9 +673,9 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
|
||||
|
||||
// F429-like chips have the GCCFG.NOVBUSSENS bit
|
||||
r.gccfg_v1().modify(|w| {
|
||||
w.set_novbussens(true);
|
||||
w.set_novbussens(!self.config.vbus_detection);
|
||||
w.set_vbusasen(false);
|
||||
w.set_vbusbsen(false);
|
||||
w.set_vbusbsen(self.config.vbus_detection);
|
||||
w.set_sofouten(false);
|
||||
});
|
||||
}
|
||||
@ -923,12 +688,12 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
|
||||
});
|
||||
|
||||
r.gccfg_v2().modify(|w| {
|
||||
w.set_vbden(false);
|
||||
w.set_vbden(self.config.vbus_detection);
|
||||
});
|
||||
|
||||
// Force B-peripheral session
|
||||
r.gotgctl().modify(|w| {
|
||||
w.set_bvaloen(true);
|
||||
w.set_bvaloen(!self.config.vbus_detection);
|
||||
w.set_bvaloval(true);
|
||||
});
|
||||
}
|
||||
@ -960,16 +725,352 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
|
||||
|
||||
// Connect
|
||||
r.dctl().write(|w| w.set_sdis(false));
|
||||
}
|
||||
|
||||
self.enabled = true;
|
||||
fn init_fifo(&mut self) {
|
||||
trace!("init_fifo");
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
// Configure RX fifo size. All endpoints share the same FIFO area.
|
||||
let rx_fifo_size_words = RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out);
|
||||
trace!("configuring rx fifo size={}", rx_fifo_size_words);
|
||||
|
||||
r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words));
|
||||
|
||||
// Configure TX (USB in direction) fifo size for each endpoint
|
||||
let mut fifo_top = rx_fifo_size_words;
|
||||
for i in 0..T::ENDPOINT_COUNT {
|
||||
if let Some(ep) = self.ep_in[i] {
|
||||
trace!(
|
||||
"configuring tx fifo ep={}, offset={}, size={}",
|
||||
i,
|
||||
fifo_top,
|
||||
ep.fifo_size_words
|
||||
);
|
||||
|
||||
let dieptxf = if i == 0 { r.dieptxf0() } else { r.dieptxf(i - 1) };
|
||||
|
||||
dieptxf.write(|w| {
|
||||
w.set_fd(ep.fifo_size_words);
|
||||
w.set_sa(fifo_top);
|
||||
});
|
||||
|
||||
fifo_top += ep.fifo_size_words;
|
||||
}
|
||||
}
|
||||
|
||||
assert!(
|
||||
fifo_top <= T::FIFO_DEPTH_WORDS,
|
||||
"FIFO allocations exceeded maximum capacity"
|
||||
);
|
||||
|
||||
// Flush fifos
|
||||
r.grstctl().write(|w| {
|
||||
w.set_rxfflsh(true);
|
||||
w.set_txfflsh(true);
|
||||
w.set_txfnum(0x10);
|
||||
});
|
||||
loop {
|
||||
let x = r.grstctl().read();
|
||||
if !x.rxfflsh() && !x.txfflsh() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn configure_endpoints(&mut self) {
|
||||
trace!("configure_endpoints");
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
// Configure IN endpoints
|
||||
for (index, ep) in self.ep_in.iter().enumerate() {
|
||||
if let Some(ep) = ep {
|
||||
critical_section::with(|_| {
|
||||
r.diepctl(index).write(|w| {
|
||||
if index == 0 {
|
||||
w.set_mpsiz(ep0_mpsiz(ep.max_packet_size));
|
||||
} else {
|
||||
w.set_mpsiz(ep.max_packet_size);
|
||||
w.set_eptyp(to_eptyp(ep.ep_type));
|
||||
w.set_sd0pid_sevnfrm(true);
|
||||
w.set_txfnum(index as _);
|
||||
w.set_snak(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Configure OUT endpoints
|
||||
for (index, ep) in self.ep_out.iter().enumerate() {
|
||||
if let Some(ep) = ep {
|
||||
critical_section::with(|_| {
|
||||
r.doepctl(index).write(|w| {
|
||||
if index == 0 {
|
||||
w.set_mpsiz(ep0_mpsiz(ep.max_packet_size));
|
||||
} else {
|
||||
w.set_mpsiz(ep.max_packet_size);
|
||||
w.set_eptyp(to_eptyp(ep.ep_type));
|
||||
w.set_sd0pid_sevnfrm(true);
|
||||
}
|
||||
});
|
||||
|
||||
r.doeptsiz(index).modify(|w| {
|
||||
w.set_xfrsiz(ep.max_packet_size as _);
|
||||
if index == 0 {
|
||||
w.set_rxdpid_stupcnt(1);
|
||||
} else {
|
||||
w.set_pktcnt(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Enable IRQs for allocated endpoints
|
||||
r.daintmsk().modify(|w| {
|
||||
w.set_iepm(ep_irq_mask(&self.ep_in));
|
||||
// OUT interrupts not used, handled in RXFLVL
|
||||
// w.set_oepm(ep_irq_mask(&self.ep_out));
|
||||
});
|
||||
}
|
||||
|
||||
fn disable_all_endpoints(&mut self) {
|
||||
for i in 0..T::ENDPOINT_COUNT {
|
||||
self.endpoint_set_enabled(EndpointAddress::from_parts(i, Direction::In), false);
|
||||
self.endpoint_set_enabled(EndpointAddress::from_parts(i, Direction::Out), false);
|
||||
}
|
||||
}
|
||||
|
||||
fn disable(&mut self) {
|
||||
T::Interrupt::disable();
|
||||
|
||||
<T as RccPeripheral>::disable();
|
||||
|
||||
#[cfg(stm32l4)]
|
||||
crate::pac::PWR.cr2().modify(|w| w.set_usv(false));
|
||||
// Cannot disable PWR, because other peripherals might be using it
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
|
||||
async fn poll(&mut self) -> Event {
|
||||
poll_fn(move |cx| {
|
||||
if !self.inited {
|
||||
self.init();
|
||||
self.inited = true;
|
||||
|
||||
// If no vbus detection, just return a single PowerDetected event at startup.
|
||||
if !self.config.vbus_detection {
|
||||
return Poll::Ready(Event::PowerDetected);
|
||||
}
|
||||
}
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
T::state().bus_waker.register(cx.waker());
|
||||
|
||||
let ints = r.gintsts().read();
|
||||
|
||||
if ints.srqint() {
|
||||
trace!("vbus detected");
|
||||
|
||||
r.gintsts().write(|w| w.set_srqint(true)); // clear
|
||||
Self::restore_irqs();
|
||||
|
||||
if self.config.vbus_detection {
|
||||
return Poll::Ready(Event::PowerDetected);
|
||||
}
|
||||
}
|
||||
|
||||
if ints.otgint() {
|
||||
let otgints = r.gotgint().read();
|
||||
r.gotgint().write_value(otgints); // clear all
|
||||
Self::restore_irqs();
|
||||
|
||||
if otgints.sedet() {
|
||||
trace!("vbus removed");
|
||||
if self.config.vbus_detection {
|
||||
self.disable_all_endpoints();
|
||||
return Poll::Ready(Event::PowerRemoved);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ints.usbrst() {
|
||||
trace!("reset");
|
||||
|
||||
self.init_fifo();
|
||||
self.configure_endpoints();
|
||||
|
||||
// Reset address
|
||||
critical_section::with(|_| {
|
||||
r.dcfg().modify(|w| {
|
||||
w.set_dad(0);
|
||||
});
|
||||
});
|
||||
|
||||
r.gintsts().write(|w| w.set_usbrst(true)); // clear
|
||||
Self::restore_irqs();
|
||||
}
|
||||
|
||||
if ints.enumdne() {
|
||||
trace!("enumdne");
|
||||
|
||||
let speed = r.dsts().read().enumspd();
|
||||
trace!(" speed={}", speed.to_bits());
|
||||
|
||||
r.gusbcfg().modify(|w| {
|
||||
w.set_trdt(calculate_trdt(speed, T::frequency()));
|
||||
});
|
||||
|
||||
r.gintsts().write(|w| w.set_enumdne(true)); // clear
|
||||
Self::restore_irqs();
|
||||
|
||||
return Poll::Ready(Event::Reset);
|
||||
}
|
||||
|
||||
if ints.usbsusp() {
|
||||
trace!("suspend");
|
||||
r.gintsts().write(|w| w.set_usbsusp(true)); // clear
|
||||
Self::restore_irqs();
|
||||
return Poll::Ready(Event::Suspend);
|
||||
}
|
||||
|
||||
if ints.wkupint() {
|
||||
trace!("resume");
|
||||
r.gintsts().write(|w| w.set_wkupint(true)); // clear
|
||||
Self::restore_irqs();
|
||||
return Poll::Ready(Event::Resume);
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {
|
||||
trace!("endpoint_set_stalled ep={:?} en={}", ep_addr, stalled);
|
||||
|
||||
assert!(
|
||||
ep_addr.index() < T::ENDPOINT_COUNT,
|
||||
"endpoint_set_stalled index {} out of range",
|
||||
ep_addr.index()
|
||||
);
|
||||
|
||||
let regs = T::regs();
|
||||
match ep_addr.direction() {
|
||||
Direction::Out => {
|
||||
critical_section::with(|_| {
|
||||
regs.doepctl(ep_addr.index()).modify(|w| {
|
||||
w.set_stall(stalled);
|
||||
});
|
||||
});
|
||||
|
||||
T::state().ep_out_wakers[ep_addr.index()].wake();
|
||||
}
|
||||
Direction::In => {
|
||||
critical_section::with(|_| {
|
||||
regs.diepctl(ep_addr.index()).modify(|w| {
|
||||
w.set_stall(stalled);
|
||||
});
|
||||
});
|
||||
|
||||
T::state().ep_in_wakers[ep_addr.index()].wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {
|
||||
assert!(
|
||||
ep_addr.index() < T::ENDPOINT_COUNT,
|
||||
"endpoint_is_stalled index {} out of range",
|
||||
ep_addr.index()
|
||||
);
|
||||
|
||||
let regs = T::regs();
|
||||
|
||||
match ep_addr.direction() {
|
||||
Direction::Out => regs.doepctl(ep_addr.index()).read().stall(),
|
||||
Direction::In => regs.diepctl(ep_addr.index()).read().stall(),
|
||||
}
|
||||
}
|
||||
|
||||
fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) {
|
||||
trace!("endpoint_set_enabled ep={:?} en={}", ep_addr, enabled);
|
||||
|
||||
assert!(
|
||||
ep_addr.index() < T::ENDPOINT_COUNT,
|
||||
"endpoint_set_enabled index {} out of range",
|
||||
ep_addr.index()
|
||||
);
|
||||
|
||||
let r = T::regs();
|
||||
match ep_addr.direction() {
|
||||
Direction::Out => {
|
||||
critical_section::with(|_| {
|
||||
// cancel transfer if active
|
||||
if !enabled && r.doepctl(ep_addr.index()).read().epena() {
|
||||
r.doepctl(ep_addr.index()).modify(|w| {
|
||||
w.set_snak(true);
|
||||
w.set_epdis(true);
|
||||
})
|
||||
}
|
||||
|
||||
r.doepctl(ep_addr.index()).modify(|w| {
|
||||
w.set_usbaep(enabled);
|
||||
});
|
||||
|
||||
// Flush tx fifo
|
||||
r.grstctl().write(|w| {
|
||||
w.set_txfflsh(true);
|
||||
w.set_txfnum(ep_addr.index() as _);
|
||||
});
|
||||
loop {
|
||||
let x = r.grstctl().read();
|
||||
if !x.txfflsh() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Wake `Endpoint::wait_enabled()`
|
||||
T::state().ep_out_wakers[ep_addr.index()].wake();
|
||||
}
|
||||
Direction::In => {
|
||||
critical_section::with(|_| {
|
||||
// cancel transfer if active
|
||||
if !enabled && r.diepctl(ep_addr.index()).read().epena() {
|
||||
r.diepctl(ep_addr.index()).modify(|w| {
|
||||
w.set_snak(true); // set NAK
|
||||
w.set_epdis(true);
|
||||
})
|
||||
}
|
||||
|
||||
r.diepctl(ep_addr.index()).modify(|w| {
|
||||
w.set_usbaep(enabled);
|
||||
w.set_cnak(enabled); // clear NAK that might've been set by SNAK above.
|
||||
})
|
||||
});
|
||||
|
||||
// Wake `Endpoint::wait_enabled()`
|
||||
T::state().ep_in_wakers[ep_addr.index()].wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn enable(&mut self) {
|
||||
trace!("enable");
|
||||
// TODO: enable the peripheral once enable/disable semantics are cleared up in embassy-usb
|
||||
}
|
||||
|
||||
async fn disable(&mut self) {
|
||||
trace!("disable");
|
||||
|
||||
Bus::disable(self);
|
||||
|
||||
self.enabled = false;
|
||||
// TODO: disable the peripheral once enable/disable semantics are cleared up in embassy-usb
|
||||
//Bus::disable(self);
|
||||
}
|
||||
|
||||
async fn remote_wakeup(&mut self) -> Result<(), Unsupported> {
|
||||
@ -1112,11 +1213,16 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> {
|
||||
state.ep_in_wakers[index].register(cx.waker());
|
||||
|
||||
let diepctl = r.diepctl(index).read();
|
||||
let dtxfsts = r.dtxfsts(index).read();
|
||||
info!("diepctl {:08x} ftxfsts {:08x}", diepctl.0, dtxfsts.0);
|
||||
if !diepctl.usbaep() {
|
||||
trace!("write ep={:?} wait for prev: error disabled", self.info.addr);
|
||||
Poll::Ready(Err(EndpointError::Disabled))
|
||||
} else if !diepctl.epena() {
|
||||
trace!("write ep={:?} wait for prev: ready", self.info.addr);
|
||||
Poll::Ready(Ok(()))
|
||||
} else {
|
||||
trace!("write ep={:?} wait for prev: pending", self.info.addr);
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
@ -1141,6 +1247,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> {
|
||||
|
||||
Poll::Pending
|
||||
} else {
|
||||
trace!("write ep={:?} wait for fifo: ready", self.info.addr);
|
||||
Poll::Ready(())
|
||||
}
|
||||
})
|
||||
|
@ -49,7 +49,7 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> {
|
||||
|
||||
let wdg = T::regs();
|
||||
wdg.kr().write(|w| w.set_key(Key::ENABLE));
|
||||
wdg.pr().write(|w| w.set_pr(Pr(pr)));
|
||||
wdg.pr().write(|w| w.set_pr(Pr::from_bits(pr)));
|
||||
wdg.rlr().write(|w| w.set_rl(rl));
|
||||
|
||||
trace!(
|
||||
|
Reference in New Issue
Block a user