Compare commits

...

25 Commits

Author SHA1 Message Date
2b497c1e57 Fix nb on rp uart 2023-12-18 18:38:13 +01:00
88e77c733c Merge pull request #2303 from embassy-rs/nor-flash-multiwrite
feat: support multiwrite flash traits if configured
2023-12-18 13:06:53 +00:00
2a542bc143 feat: support multiwrite flash traits if configured 2023-12-18 13:58:12 +01:00
c0cfd68c0c Merge pull request #2297 from embassy-rs/stm32-docs
stm32: add some docs.
2023-12-17 23:59:29 +00:00
80c9d04bbd stm32: add some docs. 2023-12-18 00:53:18 +01:00
9959c8c3e3 Merge pull request #2300 from RobertTDowling/stm32-fix-time-driver-race
STM32: Fix race in alarm setting, which impacted scheduling.
2023-12-17 23:51:43 +00:00
b857334f92 STM32: Fix race in alarm setting, which impacted scheduling.
Detect potential race condition (should be rare) and return false back
to caller, allowing them to handle the possibility that either the
alarm was never set because it was in the past (old meaning of false),
or that in fact the alarm was set and may have fired within the race
window (new meaning of false). In either case, the caller needs to
make sure the callback got called.
2023-12-17 15:35:35 -08:00
a2d4bab2f8 Merge pull request #2281 from dstric-aqueduct/main
allow for optional override of `Suspend` event for a UsbDevice
2023-12-16 13:44:54 +00:00
a5379e708c remove suspendable field from embassy_usb::builder::Config 2023-12-16 08:19:52 -05:00
2a7a44477e Merge pull request #2294 from adamgreig/g4-flash
STM32: Enable flash support for STM32G4
2023-12-16 06:41:21 +00:00
f6bc96dfbd STM32: Enable flash support for STM32G4 2023-12-16 03:50:34 +00:00
ccf602b333 Merge pull request #2293 from esden/fix_qspi_flash_select
STM32 QSPI: Fix flash selection.
2023-12-16 01:00:57 +00:00
3568e4a5ff STM32 QSPI: Fix flash selection. 2023-12-15 16:47:56 -08:00
858987263b Merge pull request #2290 from eZioPan/stm32f4-example-ws2812
add ws2812 example for stm32f4 with PWM and DMA
2023-12-15 23:05:32 +00:00
b966f55883 Merge pull request #2292 from sourcebox/stm32h7-rm0468-fixes
[embassy-stm32]: Fixes for STM32H7 series MCUs referenced in RM0468
2023-12-15 23:02:42 +00:00
ea1e1973eb unify channel assign 2023-12-16 02:15:56 +08:00
560e728132 STM32H7: adjust flash latency and programming delay for series in RM0468 2023-12-15 14:14:30 +01:00
c17fee27bb STM32H7: limit max frequency to 520MHz until cpu frequency boost option is implemented 2023-12-15 13:53:06 +01:00
a8d0da91dc STM32H7: adjust frequency limits for series in RM0468 2023-12-15 12:22:17 +01:00
e5e85ba02b STM32H7: Allow PLL1 DIVP of 1 for certain series 2023-12-15 11:42:58 +01:00
77e372e842 cargo fmt 2023-12-15 14:15:45 +08:00
a165d73eed add ws2812 example for stm32f4 with PWM and DMA 2023-12-15 14:10:11 +08:00
df0f41c41c Merge pull request #2289 from embassy-rs/ehm-rc4
use released embedded-hal-mock.
2023-12-14 20:14:37 +00:00
6bf70e14fb Update usb.rs
- add check of `dev_resume_from_host` interrupt register to catch wake event
2023-12-13 14:50:13 -05:00
d596a1091d add susependable field to embassy_usb::builder::Config
- allow for optional override of `Suspend` event for a UsbDevice
2023-12-13 10:17:07 -05:00
46 changed files with 760 additions and 146 deletions

View File

@ -64,6 +64,11 @@ nfc-pins-as-gpio = []
# nrf52820, nrf52833, nrf52840: P0_18
reset-pin-as-gpio = []
# Implements the MultiwriteNorFlash trait for QSPI. Should only be enabled if your external
# flash supports the semantics described in
# https://docs.rs/embedded-storage/0.3.1/embedded_storage/nor_flash/trait.MultiwriteNorFlash.html
qspi-multiwrite-flash = []
# 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.

View File

@ -50,19 +50,19 @@ impl<'d, T: Pin> Input<'d, T> {
Self { pin }
}
/// Test if current pin level is high.
/// Get whether the pin input level is high.
#[inline]
pub fn is_high(&mut self) -> bool {
self.pin.is_high()
}
/// Test if current pin level is low.
/// Get whether the pin input level is low.
#[inline]
pub fn is_low(&mut self) -> bool {
self.pin.is_low()
}
/// Returns current pin level
/// Get the pin input level.
#[inline]
pub fn get_level(&mut self) -> Level {
self.pin.get_level()
@ -158,19 +158,19 @@ impl<'d, T: Pin> Output<'d, T> {
self.pin.set_level(level)
}
/// Is the output pin set as high?
/// Get whether the output level is set to high.
#[inline]
pub fn is_set_high(&mut self) -> bool {
self.pin.is_set_high()
}
/// Is the output pin set as low?
/// Get whether the output level is set to low.
#[inline]
pub fn is_set_low(&mut self) -> bool {
self.pin.is_set_low()
}
/// What level output is set to
/// Get the current output level.
#[inline]
pub fn get_output_level(&mut self) -> Level {
self.pin.get_output_level()
@ -275,13 +275,13 @@ impl<'d, T: Pin> Flex<'d, T> {
self.pin.conf().reset();
}
/// Test if current pin level is high.
/// Get whether the pin input level is high.
#[inline]
pub fn is_high(&mut self) -> bool {
!self.is_low()
}
/// Test if current pin level is low.
/// Get whether the pin input level is low.
#[inline]
pub fn is_low(&mut self) -> bool {
self.ref_is_low()
@ -292,7 +292,7 @@ impl<'d, T: Pin> Flex<'d, T> {
self.pin.block().in_.read().bits() & (1 << self.pin.pin()) == 0
}
/// Returns current pin level
/// Get the pin input level.
#[inline]
pub fn get_level(&mut self) -> Level {
self.is_high().into()
@ -319,25 +319,24 @@ impl<'d, T: Pin> Flex<'d, T> {
}
}
/// Is the output pin set as high?
/// Get whether the output level is set to high.
#[inline]
pub fn is_set_high(&mut self) -> bool {
!self.is_set_low()
}
/// Is the output pin set as low?
/// Get whether the output level is set to low.
#[inline]
pub fn is_set_low(&mut self) -> bool {
self.ref_is_set_low()
}
/// Is the output pin set as low?
#[inline]
pub(crate) fn ref_is_set_low(&self) -> bool {
self.pin.block().out.read().bits() & (1 << self.pin.pin()) == 0
}
/// What level output is set to
/// Get the current output level.
#[inline]
pub fn get_output_level(&mut self) -> Level {
self.is_set_high().into()

View File

@ -605,6 +605,9 @@ impl<'d, T: Instance> NorFlash for Qspi<'d, T> {
}
}
#[cfg(feature = "qspi-multiwrite-flash")]
impl<'d, T: Instance> embedded_storage::nor_flash::MultiwriteNorFlash for Qspi<'d, T> {}
mod _eh1 {
use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};

View File

@ -820,6 +820,10 @@ impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for Uart<'d, T
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, T, M> {
fn read(&mut self) -> nb::Result<u8, Self::Error> {
let r = T::regs();
if r.uartfr().read().rxfe() {
return Err(nb::Error::WouldBlock);
}
let dr = r.uartdr().read();
if dr.oe() {
@ -830,10 +834,8 @@ impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, T, M
Err(nb::Error::Other(Error::Parity))
} else if dr.fe() {
Err(nb::Error::Other(Error::Framing))
} else if dr.fe() {
Ok(dr.data())
} else {
Err(nb::Error::WouldBlock)
Ok(dr.data())
}
}
}

View File

@ -363,7 +363,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
let siestatus = regs.sie_status().read();
let intrstatus = regs.intr().read();
if siestatus.resume() {
if siestatus.resume() || intrstatus.dev_resume_from_host() {
regs.sie_status().write(|w| w.set_resume(true));
return Poll::Ready(Event::Resume);
}

View File

@ -1,3 +1,4 @@
//! Analog to Digital (ADC) converter driver.
#![macro_use]
#[cfg(not(adc_f3_v2))]
@ -24,6 +25,7 @@ pub use sample_time::SampleTime;
use crate::peripherals;
/// Analog to Digital driver.
pub struct Adc<'d, T: Instance> {
#[allow(unused)]
adc: crate::PeripheralRef<'d, T>,
@ -75,12 +77,16 @@ pub(crate) mod sealed {
}
}
/// ADC instance.
#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0)))]
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {}
/// ADC instance.
#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0))]
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {}
/// ADC pin.
pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {}
/// ADC internal channel.
pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
foreach_adc!(

View File

@ -1,3 +1,5 @@
/// ADC resolution
#[allow(missing_docs)]
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -8,6 +10,8 @@ pub enum Resolution {
SixBit,
}
/// ADC resolution
#[allow(missing_docs)]
#[cfg(adc_v4)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -49,6 +53,9 @@ impl From<Resolution> for crate::pac::adc::vals::Res {
}
impl Resolution {
/// Get the maximum reading value for this resolution.
///
/// This is `2**n - 1`.
pub fn to_max_count(&self) -> u32 {
match self {
#[cfg(adc_v4)]

View File

@ -32,6 +32,7 @@ const TEMP_CHANNEL: u8 = 18;
const VBAT_CHANNEL: u8 = 17;
// NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs
/// Internal voltage reference channel.
pub struct VrefInt;
impl<T: Instance> InternalChannel<T> for VrefInt {}
impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt {
@ -40,6 +41,7 @@ impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt {
}
}
/// Internal temperature channel.
pub struct Temperature;
impl<T: Instance> InternalChannel<T> for Temperature {}
impl<T: Instance> super::sealed::InternalChannel<T> for Temperature {
@ -48,6 +50,7 @@ impl<T: Instance> super::sealed::InternalChannel<T> for Temperature {
}
}
/// Internal battery voltage channel.
pub struct Vbat;
impl<T: Instance> InternalChannel<T> for Vbat {}
impl<T: Instance> super::sealed::InternalChannel<T> for Vbat {
@ -125,6 +128,7 @@ impl Prescaler {
}
impl<'d, T: Instance> Adc<'d, T> {
/// Create a new ADC driver.
pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u16>) -> Self {
embassy_hal_internal::into_ref!(adc);
T::enable_and_reset();
@ -212,6 +216,7 @@ impl<'d, T: Instance> Adc<'d, T> {
});
}
/// Enable reading the voltage reference internal channel.
pub fn enable_vrefint(&self) -> VrefInt {
T::common_regs().ccr().modify(|reg| {
reg.set_vrefen(true);
@ -220,6 +225,7 @@ impl<'d, T: Instance> Adc<'d, T> {
VrefInt {}
}
/// Enable reading the temperature internal channel.
pub fn enable_temperature(&self) -> Temperature {
T::common_regs().ccr().modify(|reg| {
reg.set_vsenseen(true);
@ -228,6 +234,7 @@ impl<'d, T: Instance> Adc<'d, T> {
Temperature {}
}
/// Enable reading the vbat internal channel.
pub fn enable_vbat(&self) -> Vbat {
T::common_regs().ccr().modify(|reg| {
reg.set_vbaten(true);
@ -236,10 +243,12 @@ impl<'d, T: Instance> Adc<'d, T> {
Vbat {}
}
/// Set the ADC sample time.
pub fn set_sample_time(&mut self, sample_time: SampleTime) {
self.sample_time = sample_time;
}
/// Set the ADC resolution.
pub fn set_resolution(&mut self, resolution: Resolution) {
T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
}
@ -263,6 +272,7 @@ impl<'d, T: Instance> Adc<'d, T> {
T::regs().dr().read().0 as u16
}
/// Read an ADC pin.
pub fn read<P>(&mut self, pin: &mut P) -> u16
where
P: AdcPin<T>,
@ -273,6 +283,7 @@ impl<'d, T: Instance> Adc<'d, T> {
self.read_channel(pin.channel())
}
/// Read an ADC internal channel.
pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 {
self.read_channel(channel.channel())
}

View File

@ -1,6 +1,3 @@
pub use bxcan;
use embassy_hal_internal::PeripheralRef;
use crate::peripherals;
pub(crate) mod sealed {
@ -25,27 +22,19 @@ pub(crate) mod sealed {
}
pub trait Instance {
const REGISTERS: *mut bxcan::RegisterBlock;
fn regs() -> &'static crate::pac::can::Fdcan;
fn state() -> &'static State;
}
}
/// Interruptable FDCAN instance.
pub trait InterruptableInstance {}
/// FDCAN instance.
pub trait Instance: sealed::Instance + InterruptableInstance + 'static {}
pub struct BxcanInstance<'a, T>(PeripheralRef<'a, T>);
unsafe impl<'d, T: Instance> bxcan::Instance for BxcanInstance<'d, T> {
const REGISTERS: *mut bxcan::RegisterBlock = T::REGISTERS;
}
foreach_peripheral!(
(can, $inst:ident) => {
impl sealed::Instance for peripherals::$inst {
const REGISTERS: *mut bxcan::RegisterBlock = crate::pac::$inst.as_ptr() as *mut _;
fn regs() -> &'static crate::pac::can::Fdcan {
&crate::pac::$inst
}

View File

@ -6,15 +6,19 @@ use crate::peripherals::CRC;
use crate::rcc::sealed::RccPeripheral;
use crate::Peripheral;
/// CRC driver.
pub struct Crc<'d> {
_peripheral: PeripheralRef<'d, CRC>,
_config: Config,
}
/// CRC configuration errlr
pub enum ConfigError {
/// The selected polynomial is invalid.
InvalidPolynomial,
}
/// CRC configuration
pub struct Config {
reverse_in: InputReverseConfig,
reverse_out: bool,
@ -25,14 +29,20 @@ pub struct Config {
crc_poly: u32,
}
/// Input reverse configuration.
pub enum InputReverseConfig {
/// Don't reverse anything
None,
/// Reverse bytes
Byte,
/// Reverse 16-bit halfwords.
Halfword,
/// Reverse 32-bit words.
Word,
}
impl Config {
/// Create a new CRC config.
pub fn new(
reverse_in: InputReverseConfig,
reverse_out: bool,
@ -57,7 +67,9 @@ impl Config {
}
}
/// Polynomial size
#[cfg(crc_v3)]
#[allow(missing_docs)]
pub enum PolySize {
Width7,
Width8,
@ -81,6 +93,7 @@ impl<'d> Crc<'d> {
instance
}
/// Reset the CRC engine.
pub fn reset(&mut self) {
PAC_CRC.cr().modify(|w| w.set_reset(true));
}

View File

@ -62,11 +62,11 @@ impl Mode {
///
/// 12-bit values outside the permitted range are silently truncated.
pub enum Value {
// 8 bit value
/// 8 bit value
Bit8(u8),
// 12 bit value stored in a u16, left-aligned
/// 12 bit value stored in a u16, left-aligned
Bit12Left(u16),
// 12 bit value stored in a u16, right-aligned
/// 12 bit value stored in a u16, right-aligned
Bit12Right(u16),
}
@ -76,11 +76,11 @@ pub enum Value {
///
/// 12-bit values outside the permitted range are silently truncated.
pub enum DualValue {
// 8 bit value
/// 8 bit value
Bit8(u8, u8),
// 12 bit value stored in a u16, left-aligned
/// 12 bit value stored in a u16, left-aligned
Bit12Left(u16, u16),
// 12 bit value stored in a u16, right-aligned
/// 12 bit value stored in a u16, right-aligned
Bit12Right(u16, u16),
}
@ -88,11 +88,11 @@ pub enum DualValue {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
/// Array variant of [`Value`].
pub enum ValueArray<'a> {
// 8 bit values
/// 8 bit values
Bit8(&'a [u8]),
// 12 bit value stored in a u16, left-aligned
/// 12 bit value stored in a u16, left-aligned
Bit12Left(&'a [u16]),
// 12 bit values stored in a u16, right-aligned
/// 12 bit values stored in a u16, right-aligned
Bit12Right(&'a [u16]),
}
@ -106,7 +106,9 @@ pub struct DacChannel<'d, T: Instance, const N: u8, DMA = NoDma> {
dma: PeripheralRef<'d, DMA>,
}
/// DAC channel 1 type alias.
pub type DacCh1<'d, T, DMA = NoDma> = DacChannel<'d, T, 1, DMA>;
/// DAC channel 2 type alias.
pub type DacCh2<'d, T, DMA = NoDma> = DacChannel<'d, T, 2, DMA>;
impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> {
@ -492,6 +494,7 @@ pub(crate) mod sealed {
}
}
/// DAC instance.
pub trait Instance: sealed::Instance + RccPeripheral + 'static {}
dma_trait!(DacDma1, Instance);
dma_trait!(DacDma2, Instance);

View File

@ -1,3 +1,5 @@
#![allow(missing_docs)]
/// Trigger selection for STM32F0.
#[cfg(stm32f0)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]

View File

@ -36,6 +36,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
}
/// The level on the VSync pin when the data is not valid on the parallel interface.
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq)]
pub enum VSyncDataInvalidLevel {
Low,
@ -43,6 +44,7 @@ pub enum VSyncDataInvalidLevel {
}
/// The level on the VSync pin when the data is not valid on the parallel interface.
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq)]
pub enum HSyncDataInvalidLevel {
Low,
@ -50,14 +52,16 @@ pub enum HSyncDataInvalidLevel {
}
#[derive(Clone, Copy, PartialEq)]
#[allow(missing_docs)]
pub enum PixelClockPolarity {
RisingEdge,
FallingEdge,
}
pub struct State {
struct State {
waker: AtomicWaker,
}
impl State {
const fn new() -> State {
State {
@ -68,18 +72,25 @@ impl State {
static STATE: State = State::new();
/// DCMI error.
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
/// Overrun error: the hardware generated data faster than we could read it.
Overrun,
/// Internal peripheral error.
PeripheralError,
}
/// DCMI configuration.
#[non_exhaustive]
pub struct Config {
/// VSYNC level.
pub vsync_level: VSyncDataInvalidLevel,
/// HSYNC level.
pub hsync_level: HSyncDataInvalidLevel,
/// PIXCLK polarity.
pub pixclk_polarity: PixelClockPolarity,
}
@ -105,6 +116,7 @@ macro_rules! config_pins {
};
}
/// DCMI driver.
pub struct Dcmi<'d, T: Instance, Dma: FrameDma<T>> {
inner: PeripheralRef<'d, T>,
dma: PeripheralRef<'d, Dma>,
@ -115,6 +127,7 @@ where
T: Instance,
Dma: FrameDma<T>,
{
/// Create a new DCMI driver with 8 data bits.
pub fn new_8bit(
peri: impl Peripheral<P = T> + 'd,
dma: impl Peripheral<P = Dma> + 'd,
@ -139,6 +152,7 @@ where
Self::new_inner(peri, dma, config, false, 0b00)
}
/// Create a new DCMI driver with 10 data bits.
pub fn new_10bit(
peri: impl Peripheral<P = T> + 'd,
dma: impl Peripheral<P = Dma> + 'd,
@ -165,6 +179,7 @@ where
Self::new_inner(peri, dma, config, false, 0b01)
}
/// Create a new DCMI driver with 12 data bits.
pub fn new_12bit(
peri: impl Peripheral<P = T> + 'd,
dma: impl Peripheral<P = Dma> + 'd,
@ -193,6 +208,7 @@ where
Self::new_inner(peri, dma, config, false, 0b10)
}
/// Create a new DCMI driver with 14 data bits.
pub fn new_14bit(
peri: impl Peripheral<P = T> + 'd,
dma: impl Peripheral<P = Dma> + 'd,
@ -223,6 +239,7 @@ where
Self::new_inner(peri, dma, config, false, 0b11)
}
/// Create a new DCMI driver with 8 data bits, with embedded synchronization.
pub fn new_es_8bit(
peri: impl Peripheral<P = T> + 'd,
dma: impl Peripheral<P = Dma> + 'd,
@ -245,6 +262,7 @@ where
Self::new_inner(peri, dma, config, true, 0b00)
}
/// Create a new DCMI driver with 10 data bits, with embedded synchronization.
pub fn new_es_10bit(
peri: impl Peripheral<P = T> + 'd,
dma: impl Peripheral<P = Dma> + 'd,
@ -269,6 +287,7 @@ where
Self::new_inner(peri, dma, config, true, 0b01)
}
/// Create a new DCMI driver with 12 data bits, with embedded synchronization.
pub fn new_es_12bit(
peri: impl Peripheral<P = T> + 'd,
dma: impl Peripheral<P = Dma> + 'd,
@ -295,6 +314,7 @@ where
Self::new_inner(peri, dma, config, true, 0b10)
}
/// Create a new DCMI driver with 14 data bits, with embedded synchronization.
pub fn new_es_14bit(
peri: impl Peripheral<P = T> + 'd,
dma: impl Peripheral<P = Dma> + 'd,
@ -538,7 +558,9 @@ mod sealed {
}
}
/// DCMI instance.
pub trait Instance: sealed::Instance + 'static {
/// Interrupt for this instance.
type Interrupt: interrupt::typelevel::Interrupt;
}

View File

@ -1,4 +1,4 @@
#![macro_use]
//! Basic Direct Memory Acccess (BDMA)
use core::future::Future;
use core::pin::Pin;
@ -17,6 +17,7 @@ use crate::interrupt::Priority;
use crate::pac;
use crate::pac::bdma::{regs, vals};
/// BDMA transfer options.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
@ -140,13 +141,17 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::bdma::Dma, channel_num: usize, index
STATE.ch_wakers[index].wake();
}
/// DMA request type alias.
#[cfg(any(bdma_v2, dmamux))]
pub type Request = u8;
/// DMA request type alias.
#[cfg(not(any(bdma_v2, dmamux)))]
pub type Request = ();
/// DMA channel.
#[cfg(dmamux)]
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {}
/// DMA channel.
#[cfg(not(dmamux))]
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
@ -161,12 +166,14 @@ pub(crate) mod sealed {
}
}
/// DMA transfer.
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct Transfer<'a, C: Channel> {
channel: PeripheralRef<'a, C>,
}
impl<'a, C: Channel> Transfer<'a, C> {
/// Create a new read DMA transfer (peripheral to memory).
pub unsafe fn new_read<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
@ -177,6 +184,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
Self::new_read_raw(channel, request, peri_addr, buf, options)
}
/// Create a new read DMA transfer (peripheral to memory), using raw pointers.
pub unsafe fn new_read_raw<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
@ -202,6 +210,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
)
}
/// Create a new write DMA transfer (memory to peripheral).
pub unsafe fn new_write<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
@ -212,6 +221,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
Self::new_write_raw(channel, request, buf, peri_addr, options)
}
/// Create a new write DMA transfer (memory to peripheral), using raw pointers.
pub unsafe fn new_write_raw<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
@ -237,6 +247,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
)
}
/// Create a new write DMA transfer (memory to peripheral), writing the same value repeatedly.
pub unsafe fn new_write_repeated<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
@ -321,6 +332,9 @@ impl<'a, C: Channel> Transfer<'a, C> {
});
}
/// Request the transfer to stop.
///
/// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
pub fn request_stop(&mut self) {
let ch = self.channel.regs().ch(self.channel.num());
@ -331,6 +345,10 @@ impl<'a, C: Channel> Transfer<'a, C> {
});
}
/// Return whether this transfer is still running.
///
/// If this returns `false`, it can be because either the transfer finished, or
/// it was requested to stop early with [`request_stop`](Self::request_stop).
pub fn is_running(&mut self) -> bool {
let ch = self.channel.regs().ch(self.channel.num());
let en = ch.cr().read().en();
@ -339,13 +357,15 @@ impl<'a, C: Channel> Transfer<'a, C> {
en && (circular || !tcif)
}
/// Gets the total remaining transfers for the channel
/// Note: this will be zero for transfers that completed without cancellation.
/// Get the total remaining transfers for the channel.
///
/// This will be zero for transfers that completed instead of being canceled with [`request_stop`](Self::request_stop).
pub fn get_remaining_transfers(&self) -> u16 {
let ch = self.channel.regs().ch(self.channel.num());
ch.ndtr().read().ndt()
}
/// Blocking wait until the transfer finishes.
pub fn blocking_wait(mut self) {
while self.is_running() {}
self.request_stop();
@ -411,6 +431,7 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> {
}
}
/// Ringbuffer for reading data using DMA circular mode.
pub struct ReadableRingBuffer<'a, C: Channel, W: Word> {
cr: regs::Cr,
channel: PeripheralRef<'a, C>,
@ -418,7 +439,8 @@ pub struct ReadableRingBuffer<'a, C: Channel, W: Word> {
}
impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
pub unsafe fn new_read(
/// Create a new ring buffer.
pub unsafe fn new(
channel: impl Peripheral<P = C> + 'a,
_request: Request,
peri_addr: *mut W,
@ -473,11 +495,15 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
this
}
/// Start the ring buffer operation.
///
/// You must call this after creating it for it to work.
pub fn start(&mut self) {
let ch = self.channel.regs().ch(self.channel.num());
ch.cr().write_value(self.cr)
}
/// Clear all data in the ring buffer.
pub fn clear(&mut self) {
self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
}
@ -509,10 +535,11 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
}
/// The capacity of the ringbuffer.
pub const fn cap(&self) -> usize {
pub const fn capacity(&self) -> usize {
self.ringbuf.cap()
}
/// Set a waker to be woken when at least one byte is received.
pub fn set_waker(&mut self, waker: &Waker) {
DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
}
@ -526,6 +553,9 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
});
}
/// Request DMA to stop.
///
/// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
pub fn request_stop(&mut self) {
let ch = self.channel.regs().ch(self.channel.num());
@ -539,6 +569,10 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
});
}
/// Return whether DMA is still running.
///
/// If this returns `false`, it can be because either the transfer finished, or
/// it was requested to stop early with [`request_stop`](Self::request_stop).
pub fn is_running(&mut self) -> bool {
let ch = self.channel.regs().ch(self.channel.num());
ch.cr().read().en()
@ -555,6 +589,7 @@ impl<'a, C: Channel, W: Word> Drop for ReadableRingBuffer<'a, C, W> {
}
}
/// Ringbuffer for writing data using DMA circular mode.
pub struct WritableRingBuffer<'a, C: Channel, W: Word> {
cr: regs::Cr,
channel: PeripheralRef<'a, C>,
@ -562,7 +597,8 @@ pub struct WritableRingBuffer<'a, C: Channel, W: Word> {
}
impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
pub unsafe fn new_write(
/// Create a new ring buffer.
pub unsafe fn new(
channel: impl Peripheral<P = C> + 'a,
_request: Request,
peri_addr: *mut W,
@ -617,11 +653,15 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
this
}
/// Start the ring buffer operation.
///
/// You must call this after creating it for it to work.
pub fn start(&mut self) {
let ch = self.channel.regs().ch(self.channel.num());
ch.cr().write_value(self.cr)
}
/// Clear all data in the ring buffer.
pub fn clear(&mut self) {
self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
}
@ -640,10 +680,11 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
}
/// The capacity of the ringbuffer.
pub const fn cap(&self) -> usize {
pub const fn capacity(&self) -> usize {
self.ringbuf.cap()
}
/// Set a waker to be woken when at least one byte is sent.
pub fn set_waker(&mut self, waker: &Waker) {
DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
}
@ -657,6 +698,9 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
});
}
/// Request DMA to stop.
///
/// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
pub fn request_stop(&mut self) {
let ch = self.channel.regs().ch(self.channel.num());
@ -670,6 +714,10 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
});
}
/// Return whether DMA is still running.
///
/// If this returns `false`, it can be because either the transfer finished, or
/// it was requested to stop early with [`request_stop`](Self::request_stop).
pub fn is_running(&mut self) -> bool {
let ch = self.channel.regs().ch(self.channel.num());
ch.cr().read().en()

View File

@ -16,6 +16,7 @@ use crate::interrupt::Priority;
use crate::pac::dma::{regs, vals};
use crate::{interrupt, pac};
/// DMA transfer options.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
@ -69,6 +70,7 @@ impl From<Dir> for vals::Dir {
}
}
/// DMA transfer burst setting.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Burst {
@ -93,6 +95,7 @@ impl From<Burst> for vals::Burst {
}
}
/// DMA flow control setting.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FlowControl {
@ -111,6 +114,7 @@ impl From<FlowControl> for vals::Pfctrl {
}
}
/// DMA FIFO threshold.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FifoThreshold {
@ -208,13 +212,17 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::dma::Dma, channel_num: usize, index:
STATE.ch_wakers[index].wake();
}
/// DMA request type alias. (also known as DMA channel number in some chips)
#[cfg(any(dma_v2, dmamux))]
pub type Request = u8;
/// DMA request type alias. (also known as DMA channel number in some chips)
#[cfg(not(any(dma_v2, dmamux)))]
pub type Request = ();
/// DMA channel.
#[cfg(dmamux)]
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {}
/// DMA channel.
#[cfg(not(dmamux))]
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
@ -229,12 +237,14 @@ pub(crate) mod sealed {
}
}
/// DMA transfer.
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct Transfer<'a, C: Channel> {
channel: PeripheralRef<'a, C>,
}
impl<'a, C: Channel> Transfer<'a, C> {
/// Create a new read DMA transfer (peripheral to memory).
pub unsafe fn new_read<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
@ -245,6 +255,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
Self::new_read_raw(channel, request, peri_addr, buf, options)
}
/// Create a new read DMA transfer (peripheral to memory), using raw pointers.
pub unsafe fn new_read_raw<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
@ -270,6 +281,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
)
}
/// Create a new write DMA transfer (memory to peripheral).
pub unsafe fn new_write<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
@ -280,6 +292,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
Self::new_write_raw(channel, request, buf, peri_addr, options)
}
/// Create a new write DMA transfer (memory to peripheral), using raw pointers.
pub unsafe fn new_write_raw<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
@ -305,6 +318,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
)
}
/// Create a new write DMA transfer (memory to peripheral), writing the same value repeatedly.
pub unsafe fn new_write_repeated<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
@ -407,6 +421,9 @@ impl<'a, C: Channel> Transfer<'a, C> {
});
}
/// Request the transfer to stop.
///
/// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
pub fn request_stop(&mut self) {
let ch = self.channel.regs().st(self.channel.num());
@ -417,6 +434,10 @@ impl<'a, C: Channel> Transfer<'a, C> {
});
}
/// Return whether this transfer is still running.
///
/// If this returns `false`, it can be because either the transfer finished, or
/// it was requested to stop early with [`request_stop`](Self::request_stop).
pub fn is_running(&mut self) -> bool {
let ch = self.channel.regs().st(self.channel.num());
ch.cr().read().en()
@ -429,6 +450,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
ch.ndtr().read().ndt()
}
/// Blocking wait until the transfer finishes.
pub fn blocking_wait(mut self) {
while self.is_running() {}
@ -465,12 +487,14 @@ impl<'a, C: Channel> Future for Transfer<'a, C> {
// ==================================
/// Double-buffered DMA transfer.
pub struct DoubleBuffered<'a, C: Channel, W: Word> {
channel: PeripheralRef<'a, C>,
_phantom: PhantomData<W>,
}
impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> {
/// Create a new read DMA transfer (peripheral to memory).
pub unsafe fn new_read(
channel: impl Peripheral<P = C> + 'a,
_request: Request,
@ -554,25 +578,36 @@ impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> {
});
}
/// Set the first buffer address.
///
/// You may call this while DMA is transferring the other buffer.
pub unsafe fn set_buffer0(&mut self, buffer: *mut W) {
let ch = self.channel.regs().st(self.channel.num());
ch.m0ar().write_value(buffer as _);
}
/// Set the second buffer address.
///
/// You may call this while DMA is transferring the other buffer.
pub unsafe fn set_buffer1(&mut self, buffer: *mut W) {
let ch = self.channel.regs().st(self.channel.num());
ch.m1ar().write_value(buffer as _);
}
/// Returh whether buffer0 is accessible (i.e. whether DMA is transferring buffer1 now)
pub fn is_buffer0_accessible(&mut self) -> bool {
let ch = self.channel.regs().st(self.channel.num());
ch.cr().read().ct() == vals::Ct::MEMORY1
}
/// Set a waker to be woken when one of the buffers is being transferred.
pub fn set_waker(&mut self, waker: &Waker) {
STATE.ch_wakers[self.channel.index()].register(waker);
}
/// Request the transfer to stop.
///
/// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
pub fn request_stop(&mut self) {
let ch = self.channel.regs().st(self.channel.num());
@ -583,6 +618,10 @@ impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> {
});
}
/// Return whether this transfer is still running.
///
/// If this returns `false`, it can be because either the transfer finished, or
/// it was requested to stop early with [`request_stop`](Self::request_stop).
pub fn is_running(&mut self) -> bool {
let ch = self.channel.regs().st(self.channel.num());
ch.cr().read().en()
@ -629,6 +668,7 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> {
}
}
/// Ringbuffer for receiving data using DMA circular mode.
pub struct ReadableRingBuffer<'a, C: Channel, W: Word> {
cr: regs::Cr,
channel: PeripheralRef<'a, C>,
@ -636,7 +676,8 @@ pub struct ReadableRingBuffer<'a, C: Channel, W: Word> {
}
impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
pub unsafe fn new_read(
/// Create a new ring buffer.
pub unsafe fn new(
channel: impl Peripheral<P = C> + 'a,
_request: Request,
peri_addr: *mut W,
@ -706,11 +747,15 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
this
}
/// Start the ring buffer operation.
///
/// You must call this after creating it for it to work.
pub fn start(&mut self) {
let ch = self.channel.regs().st(self.channel.num());
ch.cr().write_value(self.cr);
}
/// Clear all data in the ring buffer.
pub fn clear(&mut self) {
self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
}
@ -741,11 +786,12 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
.await
}
// The capacity of the ringbuffer
pub const fn cap(&self) -> usize {
/// The capacity of the ringbuffer
pub const fn capacity(&self) -> usize {
self.ringbuf.cap()
}
/// Set a waker to be woken when at least one byte is received.
pub fn set_waker(&mut self, waker: &Waker) {
DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
}
@ -763,6 +809,9 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
});
}
/// Request DMA to stop.
///
/// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
pub fn request_stop(&mut self) {
let ch = self.channel.regs().st(self.channel.num());
@ -774,6 +823,10 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
});
}
/// Return whether DMA is still running.
///
/// If this returns `false`, it can be because either the transfer finished, or
/// it was requested to stop early with [`request_stop`](Self::request_stop).
pub fn is_running(&mut self) -> bool {
let ch = self.channel.regs().st(self.channel.num());
ch.cr().read().en()
@ -790,6 +843,7 @@ impl<'a, C: Channel, W: Word> Drop for ReadableRingBuffer<'a, C, W> {
}
}
/// Ringbuffer for writing data using DMA circular mode.
pub struct WritableRingBuffer<'a, C: Channel, W: Word> {
cr: regs::Cr,
channel: PeripheralRef<'a, C>,
@ -797,7 +851,8 @@ pub struct WritableRingBuffer<'a, C: Channel, W: Word> {
}
impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
pub unsafe fn new_write(
/// Create a new ring buffer.
pub unsafe fn new(
channel: impl Peripheral<P = C> + 'a,
_request: Request,
peri_addr: *mut W,
@ -867,11 +922,15 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
this
}
/// Start the ring buffer operation.
///
/// You must call this after creating it for it to work.
pub fn start(&mut self) {
let ch = self.channel.regs().st(self.channel.num());
ch.cr().write_value(self.cr);
}
/// Clear all data in the ring buffer.
pub fn clear(&mut self) {
self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
}
@ -889,11 +948,12 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
.await
}
// The capacity of the ringbuffer
pub const fn cap(&self) -> usize {
/// The capacity of the ringbuffer
pub const fn capacity(&self) -> usize {
self.ringbuf.cap()
}
/// Set a waker to be woken when at least one byte is received.
pub fn set_waker(&mut self, waker: &Waker) {
DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
}
@ -911,6 +971,9 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
});
}
/// Request DMA to stop.
///
/// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
pub fn request_stop(&mut self) {
let ch = self.channel.regs().st(self.channel.num());
@ -922,6 +985,10 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
});
}
/// Return whether DMA is still running.
///
/// If this returns `false`, it can be because either the transfer finished, or
/// it was requested to stop early with [`request_stop`](Self::request_stop).
pub fn is_running(&mut self) -> bool {
let ch = self.channel.regs().st(self.channel.num());
ch.cr().read().en()

View File

@ -22,11 +22,15 @@ pub(crate) mod dmamux_sealed {
}
}
/// DMAMUX1 instance.
pub struct DMAMUX1;
/// DMAMUX2 instance.
#[cfg(stm32h7)]
pub struct DMAMUX2;
/// DMAMUX channel trait.
pub trait MuxChannel: dmamux_sealed::MuxChannel {
/// DMAMUX instance this channel is on.
type Mux;
}

View File

@ -39,6 +39,13 @@ enum Dir {
PeripheralToMemory,
}
/// "No DMA" placeholder.
///
/// You may pass this in place of a real DMA channel when creating a driver
/// to indicate it should not use DMA.
///
/// This often causes async functionality to not be available on the instance,
/// leaving only blocking functionality.
pub struct NoDma;
impl_peripheral!(NoDma);

View File

@ -1,3 +1,6 @@
//! DMA word sizes.
#[allow(missing_docs)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum WordSize {
@ -7,6 +10,7 @@ pub enum WordSize {
}
impl WordSize {
/// Amount of bytes of this word size.
pub fn bytes(&self) -> usize {
match self {
Self::OneByte => 1,
@ -20,8 +24,13 @@ mod sealed {
pub trait Word {}
}
/// DMA word trait.
///
/// This is implemented for u8, u16, u32, etc.
pub trait Word: sealed::Word + Default + Copy + 'static {
/// Word size
fn size() -> WordSize;
/// Amount of bits of this word size.
fn bits() -> usize;
}
@ -40,6 +49,7 @@ macro_rules! impl_word {
($T:ident, $uX:ident, $bits:literal, $size:ident) => {
#[repr(transparent)]
#[derive(Copy, Clone, Default)]
#[doc = concat!(stringify!($T), " word size")]
pub struct $T(pub $uX);
impl_word!(_, $T, $bits, $size);
};

View File

@ -102,6 +102,7 @@ unsafe impl PHY for GenericSMI {
/// Public functions for the PHY
impl GenericSMI {
/// Set the SMI polling interval.
#[cfg(feature = "time")]
pub fn set_poll_interval(&mut self, poll_interval: Duration) {
self.poll_interval = poll_interval

View File

@ -22,6 +22,14 @@ const RX_BUFFER_SIZE: usize = 1536;
#[derive(Copy, Clone)]
pub(crate) struct Packet<const N: usize>([u8; N]);
/// Ethernet packet queue.
///
/// This struct owns the memory used for reading and writing packets.
///
/// `TX` is the number of packets in the transmit queue, `RX` in the receive
/// queue. A bigger queue allows the hardware to receive more packets while the
/// CPU is busy doing other things, which may increase performance (especially for RX)
/// at the cost of more RAM usage.
pub struct PacketQueue<const TX: usize, const RX: usize> {
tx_desc: [TDes; TX],
rx_desc: [RDes; RX],
@ -30,6 +38,7 @@ pub struct PacketQueue<const TX: usize, const RX: usize> {
}
impl<const TX: usize, const RX: usize> PacketQueue<TX, RX> {
/// Create a new packet queue.
pub const fn new() -> Self {
const NEW_TDES: TDes = TDes::new();
const NEW_RDES: RDes = RDes::new();
@ -41,7 +50,18 @@ impl<const TX: usize, const RX: usize> PacketQueue<TX, RX> {
}
}
// Allow to initialize a Self without requiring it to go on the stack
/// Initialize a packet queue in-place.
///
/// This can be helpful to avoid accidentally stack-allocating the packet queue in the stack. The
/// Rust compiler can sometimes be a bit dumb when working with large owned values: if you call `new()`
/// and then store the returned PacketQueue in its final place (like a `static`), the compiler might
/// place it temporarily on the stack then move it. Since this struct is quite big, it may result
/// in a stack overflow.
///
/// With this function, you can create an uninitialized `static` with type `MaybeUninit<PacketQueue<...>>`
/// and initialize it in-place, guaranteeing no stack usage.
///
/// After calling this function, calling `assume_init` on the MaybeUninit is guaranteed safe.
pub fn init(this: &mut MaybeUninit<Self>) {
unsafe {
this.as_mut_ptr().write_bytes(0u8, 1);
@ -93,6 +113,7 @@ impl<'d, T: Instance, P: PHY> embassy_net_driver::Driver for Ethernet<'d, T, P>
}
}
/// `embassy-net` RX token.
pub struct RxToken<'a, 'd> {
rx: &'a mut RDesRing<'d>,
}
@ -110,6 +131,7 @@ impl<'a, 'd> embassy_net_driver::RxToken for RxToken<'a, 'd> {
}
}
/// `embassy-net` TX token.
pub struct TxToken<'a, 'd> {
tx: &'a mut TDesRing<'d>,
}
@ -159,6 +181,7 @@ pub(crate) mod sealed {
}
}
/// Ethernet instance.
pub trait Instance: sealed::Instance + Send + 'static {}
impl sealed::Instance for crate::peripherals::ETH {

View File

@ -34,6 +34,7 @@ impl interrupt::typelevel::Handler<interrupt::typelevel::ETH> for InterruptHandl
}
}
/// Ethernet driver.
pub struct Ethernet<'d, T: Instance, P: PHY> {
_peri: PeripheralRef<'d, T>,
pub(crate) tx: TDesRing<'d>,
@ -56,6 +57,7 @@ macro_rules! config_pins {
}
impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
/// Create a new Ethernet driver.
pub fn new<const TX: usize, const RX: usize>(
queue: &'d mut PacketQueue<TX, RX>,
peri: impl Peripheral<P = T> + 'd,
@ -237,6 +239,7 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
}
}
/// Ethernet SMI driver.
pub struct EthernetStationManagement<T: Instance> {
peri: PhantomData<T>,
clock_range: u8,

View File

@ -39,7 +39,7 @@ fn exticr_regs() -> pac::afio::Afio {
pac::AFIO
}
pub unsafe fn on_irq() {
unsafe fn on_irq() {
#[cfg(feature = "low-power")]
crate::low_power::on_wakeup_irq();
@ -85,7 +85,13 @@ impl Iterator for BitIter {
}
}
/// EXTI input driver
/// EXTI input driver.
///
/// This driver augments a GPIO `Input` with EXTI functionality. EXTI is not
/// built into `Input` itself because it needs to take ownership of the corresponding
/// EXTI channel, which is a limited resource.
///
/// Pins PA5, PB5, PC5... all use EXTI channel 5, so you can't use EXTI on, say, PA5 and PC5 at the same time.
pub struct ExtiInput<'d, T: GpioPin> {
pin: Input<'d, T>,
}
@ -93,23 +99,30 @@ pub struct ExtiInput<'d, T: GpioPin> {
impl<'d, T: GpioPin> Unpin for ExtiInput<'d, T> {}
impl<'d, T: GpioPin> ExtiInput<'d, T> {
/// Create an EXTI input.
pub fn new(pin: Input<'d, T>, _ch: impl Peripheral<P = T::ExtiChannel> + 'd) -> Self {
Self { pin }
}
/// Get whether the pin is high.
pub fn is_high(&mut self) -> bool {
self.pin.is_high()
}
/// Get whether the pin is low.
pub fn is_low(&mut self) -> bool {
self.pin.is_low()
}
/// Get the pin level.
pub fn get_level(&mut self) -> Level {
self.pin.get_level()
}
pub async fn wait_for_high<'a>(&'a mut self) {
/// Asynchronously wait until the pin is high.
///
/// This returns immediately if the pin is already high.
pub async fn wait_for_high(&mut self) {
let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false);
if self.is_high() {
return;
@ -117,7 +130,10 @@ impl<'d, T: GpioPin> ExtiInput<'d, T> {
fut.await
}
pub async fn wait_for_low<'a>(&'a mut self) {
/// Asynchronously wait until the pin is low.
///
/// This returns immediately if the pin is already low.
pub async fn wait_for_low(&mut self) {
let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true);
if self.is_low() {
return;
@ -125,15 +141,22 @@ impl<'d, T: GpioPin> ExtiInput<'d, T> {
fut.await
}
pub async fn wait_for_rising_edge<'a>(&'a mut self) {
/// Asynchronously wait until the pin sees a rising edge.
///
/// If the pin is already high, it will wait for it to go low then back high.
pub async fn wait_for_rising_edge(&mut self) {
ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false).await
}
pub async fn wait_for_falling_edge<'a>(&'a mut self) {
/// Asynchronously wait until the pin sees a falling edge.
///
/// If the pin is already low, it will wait for it to go high then back low.
pub async fn wait_for_falling_edge(&mut self) {
ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true).await
}
pub async fn wait_for_any_edge<'a>(&'a mut self) {
/// Asynchronously wait until the pin sees any edge (either rising or falling).
pub async fn wait_for_any_edge(&mut self) {
ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true).await
}
}
@ -284,6 +307,7 @@ macro_rules! foreach_exti_irq {
macro_rules! impl_irq {
($e:ident) => {
#[allow(non_snake_case)]
#[cfg(feature = "rt")]
#[interrupt]
unsafe fn $e() {
@ -298,8 +322,16 @@ pub(crate) mod sealed {
pub trait Channel {}
}
/// EXTI channel trait.
pub trait Channel: sealed::Channel + Sized {
/// Get the EXTI channel number.
fn number(&self) -> usize;
/// Type-erase (degrade) this channel into an `AnyChannel`.
///
/// This converts EXTI channel singletons (`EXTI0`, `EXTI1`, ...), which
/// are all different types, into the same type. It is useful for
/// creating arrays of channels, or avoiding generics.
fn degrade(self) -> AnyChannel {
AnyChannel {
number: self.number() as u8,
@ -307,9 +339,13 @@ pub trait Channel: sealed::Channel + Sized {
}
}
/// Type-erased (degraded) EXTI channel.
///
/// This represents ownership over any EXTI channel, known at runtime.
pub struct AnyChannel {
number: u8,
}
impl_peripheral!(AnyChannel);
impl sealed::Channel for AnyChannel {}
impl Channel for AnyChannel {

View File

@ -59,7 +59,7 @@ impl embedded_storage_async::nor_flash::ReadNorFlash for Flash<'_, Async> {
const READ_SIZE: usize = super::READ_SIZE;
async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
self.read(offset, bytes)
self.blocking_read(offset, bytes)
}
fn capacity(&self) -> usize {

View File

@ -12,12 +12,14 @@ use super::{
use crate::peripherals::FLASH;
use crate::Peripheral;
/// Internal flash memory driver.
pub struct Flash<'d, MODE = Async> {
pub(crate) inner: PeripheralRef<'d, FLASH>,
pub(crate) _mode: PhantomData<MODE>,
}
impl<'d> Flash<'d, Blocking> {
/// Create a new flash driver, usable in blocking mode.
pub fn new_blocking(p: impl Peripheral<P = FLASH> + 'd) -> Self {
into_ref!(p);
@ -29,15 +31,26 @@ impl<'d> Flash<'d, Blocking> {
}
impl<'d, MODE> Flash<'d, MODE> {
/// Split this flash driver into one instance per flash memory region.
///
/// See module-level documentation for details on how memory regions work.
pub fn into_blocking_regions(self) -> FlashLayout<'d, Blocking> {
assert!(family::is_default_layout());
FlashLayout::new(self.inner)
}
pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
/// Blocking read.
///
/// NOTE: `offset` is an offset from the flash start, NOT an absolute address.
/// For example, to read address `0x0800_1234` you have to use offset `0x1234`.
pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
blocking_read(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes)
}
/// Blocking write.
///
/// NOTE: `offset` is an offset from the flash start, NOT an absolute address.
/// For example, to write address `0x0800_1234` you have to use offset `0x1234`.
pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
unsafe {
blocking_write(
@ -50,6 +63,10 @@ impl<'d, MODE> Flash<'d, MODE> {
}
}
/// Blocking erase.
///
/// NOTE: `from` and `to` are offsets from the flash start, NOT an absolute address.
/// For example, to erase address `0x0801_0000` you have to use offset `0x1_0000`.
pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
unsafe { blocking_erase(FLASH_BASE as u32, from, to, erase_sector_unlocked) }
}
@ -206,7 +223,7 @@ impl<MODE> embedded_storage::nor_flash::ReadNorFlash for Flash<'_, MODE> {
const READ_SIZE: usize = READ_SIZE;
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
self.read(offset, bytes)
self.blocking_read(offset, bytes)
}
fn capacity(&self) -> usize {
@ -230,16 +247,28 @@ impl<MODE> embedded_storage::nor_flash::NorFlash for Flash<'_, MODE> {
foreach_flash_region! {
($type_name:ident, $write_size:literal, $erase_size:literal) => {
impl<MODE> crate::_generated::flash_regions::$type_name<'_, MODE> {
/// Blocking read.
///
/// NOTE: `offset` is an offset from the flash start, NOT an absolute address.
/// For example, to read address `0x0800_1234` you have to use offset `0x1234`.
pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
blocking_read(self.0.base, self.0.size, offset, bytes)
}
}
impl crate::_generated::flash_regions::$type_name<'_, Blocking> {
/// Blocking write.
///
/// NOTE: `offset` is an offset from the flash start, NOT an absolute address.
/// For example, to write address `0x0800_1234` you have to use offset `0x1234`.
pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
unsafe { blocking_write(self.0.base, self.0.size, offset, bytes, write_chunk_with_critical_section) }
}
/// Blocking erase.
///
/// NOTE: `from` and `to` are offsets from the flash start, NOT an absolute address.
/// For example, to erase address `0x0801_0000` you have to use offset `0x1_0000`.
pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
unsafe { blocking_erase(self.0.base, from, to, erase_sector_with_critical_section) }
}

View File

@ -6,11 +6,11 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
use crate::flash::Error;
use crate::pac;
pub const fn is_default_layout() -> bool {
pub(crate) const fn is_default_layout() -> bool {
true
}
pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
&FLASH_REGIONS
}
@ -79,7 +79,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E
pub(crate) unsafe fn clear_all_err() {
// read and write back the same value.
// This clears all "write 0 to clear" bits.
// This clears all "write 1 to clear" bits.
pac::FLASH.sr().modify(|_| {});
}

View File

@ -6,11 +6,11 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
use crate::flash::Error;
use crate::pac;
pub const fn is_default_layout() -> bool {
pub(crate) const fn is_default_layout() -> bool {
true
}
pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
&FLASH_REGIONS
}
@ -79,7 +79,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E
pub(crate) unsafe fn clear_all_err() {
// read and write back the same value.
// This clears all "write 0 to clear" bits.
// This clears all "write 1 to clear" bits.
pac::FLASH.sr().modify(|_| {});
}

View File

@ -337,7 +337,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E
pub(crate) fn clear_all_err() {
// read and write back the same value.
// This clears all "write 0 to clear" bits.
// This clears all "write 1 to clear" bits.
pac::FLASH.sr().modify(|_| {});
}

View File

@ -6,11 +6,11 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
use crate::flash::Error;
use crate::pac;
pub const fn is_default_layout() -> bool {
pub(crate) const fn is_default_layout() -> bool {
true
}
pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
&FLASH_REGIONS
}
@ -69,7 +69,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E
pub(crate) unsafe fn clear_all_err() {
// read and write back the same value.
// This clears all "write 0 to clear" bits.
// This clears all "write 1 to clear" bits.
pac::FLASH.sr().modify(|_| {});
}

View File

@ -8,11 +8,11 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
use crate::flash::Error;
use crate::pac;
pub const fn is_default_layout() -> bool {
pub(crate) const fn is_default_layout() -> bool {
true
}
pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
&FLASH_REGIONS
}
@ -92,6 +92,6 @@ pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> {
pub(crate) unsafe fn clear_all_err() {
// read and write back the same value.
// This clears all "write 0 to clear" bits.
// This clears all "write 1 to clear" bits.
pac::FLASH.sr().modify(|_| {});
}

View File

@ -6,7 +6,7 @@ use super::{FlashRegion, FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE};
use crate::flash::Error;
use crate::pac;
pub const fn is_default_layout() -> bool {
pub(crate) const fn is_default_layout() -> bool {
true
}
@ -14,7 +14,7 @@ const fn is_dual_bank() -> bool {
FLASH_REGIONS.len() >= 2
}
pub fn get_flash_regions() -> &'static [&'static FlashRegion] {
pub(crate) fn get_flash_regions() -> &'static [&'static FlashRegion] {
&FLASH_REGIONS
}
@ -113,7 +113,7 @@ pub(crate) unsafe fn clear_all_err() {
unsafe fn bank_clear_all_err(bank: pac::flash::Bank) {
// read and write back the same value.
// This clears all "write 0 to clear" bits.
// This clears all "write 1 to clear" bits.
bank.sr().modify(|_| {});
}

View File

@ -5,11 +5,11 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
use crate::flash::Error;
use crate::pac;
pub const fn is_default_layout() -> bool {
pub(crate) const fn is_default_layout() -> bool {
true
}
pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
&FLASH_REGIONS
}
@ -120,7 +120,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E
pub(crate) unsafe fn clear_all_err() {
// read and write back the same value.
// This clears all "write 0 to clear" bits.
// This clears all "write 1 to clear" bits.
pac::FLASH.sr().modify(|_| {});
}

View File

@ -14,62 +14,96 @@ pub use crate::_generated::flash_regions::*;
pub use crate::_generated::MAX_ERASE_SIZE;
pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE};
/// Get whether the default flash layout is being used.
///
/// In some chips, dual-bank is not default. This will then return `false`
/// when dual-bank is enabled.
pub fn is_default_layout() -> bool {
family::is_default_layout()
}
/// Get all flash regions.
pub fn get_flash_regions() -> &'static [&'static FlashRegion] {
family::get_flash_regions()
}
/// Read size (always 1)
pub const READ_SIZE: usize = 1;
pub struct Blocking;
pub struct Async;
/// Blocking flash mode typestate.
pub enum Blocking {}
/// Async flash mode typestate.
pub enum Async {}
/// Flash memory region
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FlashRegion {
/// Bank number.
pub bank: FlashBank,
/// Absolute base address.
pub base: u32,
/// Size in bytes.
pub size: u32,
/// Erase size (sector size).
pub erase_size: u32,
/// Minimum write size.
pub write_size: u32,
/// Erase value (usually `0xFF`, but is `0x00` in some chips)
pub erase_value: u8,
pub(crate) _ensure_internal: (),
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FlashSector {
pub bank: FlashBank,
pub index_in_bank: u8,
pub start: u32,
pub size: u32,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FlashBank {
Bank1 = 0,
Bank2 = 1,
Otp,
}
impl FlashRegion {
/// Absolute end address.
pub const fn end(&self) -> u32 {
self.base + self.size
}
/// Number of sectors in the region.
pub const fn sectors(&self) -> u8 {
(self.size / self.erase_size) as u8
}
}
/// Flash sector.
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FlashSector {
/// Bank number.
pub bank: FlashBank,
/// Sector number within the bank.
pub index_in_bank: u8,
/// Absolute start address.
pub start: u32,
/// Size in bytes.
pub size: u32,
}
/// Flash bank.
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FlashBank {
/// Bank 1
Bank1 = 0,
/// Bank 2
Bank2 = 1,
/// OTP region
Otp,
}
#[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")]
#[cfg_attr(flash_f0, path = "f0.rs")]
#[cfg_attr(flash_f3, path = "f3.rs")]
#[cfg_attr(flash_f4, path = "f4.rs")]
#[cfg_attr(flash_f7, path = "f7.rs")]
#[cfg_attr(flash_g0, path = "g0.rs")]
#[cfg_attr(any(flash_g0, flash_g4), path = "g.rs")]
#[cfg_attr(flash_h7, path = "h7.rs")]
#[cfg_attr(flash_h7ab, path = "h7.rs")]
#[cfg_attr(
not(any(
flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f3, flash_f4, flash_f7, flash_g0, flash_h7,
flash_h7ab
flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f3, flash_f4, flash_f7, flash_g0, flash_g4,
flash_h7, flash_h7ab
)),
path = "other.rs"
)]
@ -78,6 +112,10 @@ mod family;
#[allow(unused_imports)]
pub use family::*;
/// Flash error
///
/// See STM32 Reference Manual for your chip for details.
#[allow(missing_docs)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {

View File

@ -2,11 +2,11 @@
use super::{Error, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
pub const fn is_default_layout() -> bool {
pub(crate) const fn is_default_layout() -> bool {
true
}
pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
&FLASH_REGIONS
}

View File

@ -29,6 +29,11 @@ impl<'d, T: Pin> Flex<'d, T> {
Self { pin }
}
/// Type-erase (degrade) this pin into an `AnyPin`.
///
/// This converts pin singletons (`PA5`, `PB6`, ...), which
/// are all different types, into the same type. It is useful for
/// creating arrays of pins, or avoiding generics.
#[inline]
pub fn degrade(self) -> Flex<'d, AnyPin> {
// Safety: We are about to drop the other copy of this pin, so
@ -141,11 +146,13 @@ impl<'d, T: Pin> Flex<'d, T> {
});
}
/// Get whether the pin input level is high.
#[inline]
pub fn is_high(&mut self) -> bool {
!self.ref_is_low()
}
/// Get whether the pin input level is low.
#[inline]
pub fn is_low(&mut self) -> bool {
self.ref_is_low()
@ -157,17 +164,19 @@ impl<'d, T: Pin> Flex<'d, T> {
state == vals::Idr::LOW
}
/// Get the current pin input level.
#[inline]
pub fn get_level(&mut self) -> Level {
self.is_high().into()
}
/// Get whether the output level is set to high.
#[inline]
pub fn is_set_high(&mut self) -> bool {
!self.ref_is_set_low()
}
/// Is the output pin set as low?
/// Get whether the output level is set to low.
#[inline]
pub fn is_set_low(&mut self) -> bool {
self.ref_is_set_low()
@ -179,12 +188,13 @@ impl<'d, T: Pin> Flex<'d, T> {
state == vals::Odr::LOW
}
/// What level output is set to
/// Get the current output level.
#[inline]
pub fn get_output_level(&mut self) -> Level {
self.is_set_high().into()
}
/// Set the output as high.
#[inline]
pub fn set_high(&mut self) {
self.pin.set_high();
@ -196,6 +206,7 @@ impl<'d, T: Pin> Flex<'d, T> {
self.pin.set_low();
}
/// Set the output level.
#[inline]
pub fn set_level(&mut self, level: Level) {
match level {
@ -204,7 +215,7 @@ impl<'d, T: Pin> Flex<'d, T> {
}
}
/// Toggle pin output
/// Toggle the output level.
#[inline]
pub fn toggle(&mut self) {
if self.is_set_low() {
@ -242,8 +253,11 @@ impl<'d, T: Pin> Drop for Flex<'d, T> {
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Pull {
/// No pull
None,
/// Pull up
Up,
/// Pull down
Down,
}
@ -261,6 +275,9 @@ impl From<Pull> for vals::Pupdr {
}
/// Speed settings
///
/// These vary dpeending on the chip, ceck the reference manual or datasheet for details.
#[allow(missing_docs)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Speed {
@ -305,6 +322,7 @@ pub struct Input<'d, T: Pin> {
}
impl<'d, T: Pin> Input<'d, T> {
/// Create GPIO input driver for a [Pin] with the provided [Pull] configuration.
#[inline]
pub fn new(pin: impl Peripheral<P = T> + 'd, pull: Pull) -> Self {
let mut pin = Flex::new(pin);
@ -312,6 +330,11 @@ impl<'d, T: Pin> Input<'d, T> {
Self { pin }
}
/// Type-erase (degrade) this pin into an `AnyPin`.
///
/// This converts pin singletons (`PA5`, `PB6`, ...), which
/// are all different types, into the same type. It is useful for
/// creating arrays of pins, or avoiding generics.
#[inline]
pub fn degrade(self) -> Input<'d, AnyPin> {
Input {
@ -319,16 +342,19 @@ impl<'d, T: Pin> Input<'d, T> {
}
}
/// Get whether the pin input level is high.
#[inline]
pub fn is_high(&mut self) -> bool {
self.pin.is_high()
}
/// Get whether the pin input level is low.
#[inline]
pub fn is_low(&mut self) -> bool {
self.pin.is_low()
}
/// Get the current pin input level.
#[inline]
pub fn get_level(&mut self) -> Level {
self.pin.get_level()
@ -339,7 +365,9 @@ impl<'d, T: Pin> Input<'d, T> {
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Level {
/// Low
Low,
/// High
High,
}
@ -371,6 +399,7 @@ pub struct Output<'d, T: Pin> {
}
impl<'d, T: Pin> Output<'d, T> {
/// Create GPIO output driver for a [Pin] with the provided [Level] and [Speed] configuration.
#[inline]
pub fn new(pin: impl Peripheral<P = T> + 'd, initial_output: Level, speed: Speed) -> Self {
let mut pin = Flex::new(pin);
@ -382,6 +411,11 @@ impl<'d, T: Pin> Output<'d, T> {
Self { pin }
}
/// Type-erase (degrade) this pin into an `AnyPin`.
///
/// This converts pin singletons (`PA5`, `PB6`, ...), which
/// are all different types, into the same type. It is useful for
/// creating arrays of pins, or avoiding generics.
#[inline]
pub fn degrade(self) -> Output<'d, AnyPin> {
Output {
@ -442,6 +476,7 @@ pub struct OutputOpenDrain<'d, T: Pin> {
}
impl<'d, T: Pin> OutputOpenDrain<'d, T> {
/// Create a new GPIO open drain output driver for a [Pin] with the provided [Level] and [Speed], [Pull] configuration.
#[inline]
pub fn new(pin: impl Peripheral<P = T> + 'd, initial_output: Level, speed: Speed, pull: Pull) -> Self {
let mut pin = Flex::new(pin);
@ -455,6 +490,11 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> {
Self { pin }
}
/// Type-erase (degrade) this pin into an `AnyPin`.
///
/// This converts pin singletons (`PA5`, `PB6`, ...), which
/// are all different types, into the same type. It is useful for
/// creating arrays of pins, or avoiding generics.
#[inline]
pub fn degrade(self) -> Output<'d, AnyPin> {
Output {
@ -462,17 +502,19 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> {
}
}
/// Get whether the pin input level is high.
#[inline]
pub fn is_high(&mut self) -> bool {
!self.pin.is_low()
}
/// Get whether the pin input level is low.
#[inline]
pub fn is_low(&mut self) -> bool {
self.pin.is_low()
}
/// Returns current pin level
/// Get the current pin input level.
#[inline]
pub fn get_level(&mut self) -> Level {
self.pin.get_level()
@ -496,19 +538,19 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> {
self.pin.set_level(level);
}
/// Is the output pin set as high?
/// Get whether the output level is set to high.
#[inline]
pub fn is_set_high(&mut self) -> bool {
self.pin.is_set_high()
}
/// Is the output pin set as low?
/// Get whether the output level is set to low.
#[inline]
pub fn is_set_low(&mut self) -> bool {
self.pin.is_set_low()
}
/// What level output is set to
/// Get the current output level.
#[inline]
pub fn get_output_level(&mut self) -> Level {
self.pin.get_output_level()
@ -521,8 +563,11 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> {
}
}
/// GPIO output type
pub enum OutputType {
/// Drive the pin both high or low.
PushPull,
/// Drive the pin low, or don't drive it at all if the output level is high.
OpenDrain,
}
@ -535,6 +580,7 @@ impl From<OutputType> for sealed::AFType {
}
}
#[allow(missing_docs)]
pub(crate) mod sealed {
use super::*;
@ -542,8 +588,11 @@ pub(crate) mod sealed {
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AFType {
/// Input
Input,
/// Output, drive the pin both high or low.
OutputPushPull,
/// Output, drive the pin low, or don't drive it at all if the output level is high.
OutputOpenDrain,
}
@ -686,7 +735,11 @@ pub(crate) mod sealed {
}
}
/// GPIO pin trait.
pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + sealed::Pin + Sized + 'static {
/// EXTI channel assigned to this pin.
///
/// For example, PC4 uses EXTI4.
#[cfg(feature = "exti")]
type ExtiChannel: crate::exti::Channel;
@ -702,7 +755,11 @@ pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + sealed::Pin + Sized + 'stat
self._port()
}
/// Convert from concrete pin type PX_XX to type erased `AnyPin`.
/// Type-erase (degrade) this pin into an `AnyPin`.
///
/// This converts pin singletons (`PA5`, `PB6`, ...), which
/// are all different types, into the same type. It is useful for
/// creating arrays of pins, or avoiding generics.
#[inline]
fn degrade(self) -> AnyPin {
AnyPin {
@ -711,12 +768,15 @@ pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + sealed::Pin + Sized + 'stat
}
}
// Type-erased GPIO pin
/// Type-erased GPIO pin
pub struct AnyPin {
pin_port: u8,
}
impl AnyPin {
/// Unsafely create an `AnyPin` from a pin+port number.
///
/// `pin_port` is `port_num * 16 + pin_num`, where `port_num` is 0 for port `A`, 1 for port `B`, etc...
#[inline]
pub unsafe fn steal(pin_port: u8) -> Self {
Self { pin_port }
@ -727,6 +787,8 @@ impl AnyPin {
self.pin_port / 16
}
/// Get the GPIO register block for this pin.
#[cfg(feature = "unstable-pac")]
#[inline]
pub fn block(&self) -> gpio::Gpio {
pac::GPIO(self._port() as _)
@ -1072,6 +1134,7 @@ impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> {
}
}
/// Low-level GPIO manipulation.
#[cfg(feature = "unstable-pac")]
pub mod low_level {
pub use super::sealed::*;

View File

@ -13,15 +13,23 @@ use embassy_sync::waitqueue::AtomicWaker;
use crate::peripherals;
/// I2C error.
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
/// Bus error
Bus,
/// Arbitration lost
Arbitration,
/// ACK not received (either to the address or to a data byte)
Nack,
/// Timeout
Timeout,
/// CRC error
Crc,
/// Overrun error
Overrun,
/// Zero-length transfers are not allowed.
ZeroLengthTransfer,
}
@ -47,8 +55,11 @@ pub(crate) mod sealed {
}
}
/// I2C peripheral instance
pub trait Instance: sealed::Instance + 'static {
/// Event interrupt for this instance
type EventInterrupt: interrupt::typelevel::Interrupt;
/// Error interrupt for this instance
type ErrorInterrupt: interrupt::typelevel::Interrupt;
}
@ -57,7 +68,7 @@ pin_trait!(SdaPin, Instance);
dma_trait!(RxDma, Instance);
dma_trait!(TxDma, Instance);
/// Interrupt handler.
/// Event interrupt handler.
pub struct EventInterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
@ -68,6 +79,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::EventInterrupt> for EventInte
}
}
/// Error interrupt handler.
pub struct ErrorInterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}

View File

@ -18,12 +18,17 @@ impl Into<u8> for QspiMode {
}
}
/// QSPI lane width
#[allow(dead_code)]
#[derive(Copy, Clone)]
pub enum QspiWidth {
/// None
NONE,
/// Single lane
SING,
/// Dual lanes
DUAL,
/// Quad lanes
QUAD,
}
@ -38,10 +43,13 @@ impl Into<u8> for QspiWidth {
}
}
/// Flash bank selection
#[allow(dead_code)]
#[derive(Copy, Clone)]
pub enum FlashSelection {
/// Bank 1
Flash1,
/// Bank 2
Flash2,
}
@ -54,6 +62,8 @@ impl Into<bool> for FlashSelection {
}
}
/// QSPI memory size.
#[allow(missing_docs)]
#[derive(Copy, Clone)]
pub enum MemorySize {
_1KiB,
@ -113,11 +123,16 @@ impl Into<u8> for MemorySize {
}
}
/// QSPI Address size
#[derive(Copy, Clone)]
pub enum AddressSize {
/// 8-bit address
_8Bit,
/// 16-bit address
_16Bit,
/// 24-bit address
_24bit,
/// 32-bit address
_32bit,
}
@ -132,8 +147,10 @@ impl Into<u8> for AddressSize {
}
}
/// Time the Chip Select line stays high.
#[allow(missing_docs)]
#[derive(Copy, Clone)]
pub enum ChipSelectHightTime {
pub enum ChipSelectHighTime {
_1Cycle,
_2Cycle,
_3Cycle,
@ -144,21 +161,23 @@ pub enum ChipSelectHightTime {
_8Cycle,
}
impl Into<u8> for ChipSelectHightTime {
impl Into<u8> for ChipSelectHighTime {
fn into(self) -> u8 {
match self {
ChipSelectHightTime::_1Cycle => 0,
ChipSelectHightTime::_2Cycle => 1,
ChipSelectHightTime::_3Cycle => 2,
ChipSelectHightTime::_4Cycle => 3,
ChipSelectHightTime::_5Cycle => 4,
ChipSelectHightTime::_6Cycle => 5,
ChipSelectHightTime::_7Cycle => 6,
ChipSelectHightTime::_8Cycle => 7,
ChipSelectHighTime::_1Cycle => 0,
ChipSelectHighTime::_2Cycle => 1,
ChipSelectHighTime::_3Cycle => 2,
ChipSelectHighTime::_4Cycle => 3,
ChipSelectHighTime::_5Cycle => 4,
ChipSelectHighTime::_6Cycle => 5,
ChipSelectHighTime::_7Cycle => 6,
ChipSelectHighTime::_8Cycle => 7,
}
}
}
/// FIFO threshold.
#[allow(missing_docs)]
#[derive(Copy, Clone)]
pub enum FIFOThresholdLevel {
_1Bytes,
@ -234,6 +253,8 @@ impl Into<u8> for FIFOThresholdLevel {
}
}
/// Dummy cycle count
#[allow(missing_docs)]
#[derive(Copy, Clone)]
pub enum DummyCycles {
_0,

View File

@ -54,7 +54,7 @@ pub struct Config {
/// Number of bytes to trigger FIFO threshold flag.
pub fifo_threshold: FIFOThresholdLevel,
/// Minimum number of cycles that chip select must be high between issued commands
pub cs_high_time: ChipSelectHightTime,
pub cs_high_time: ChipSelectHighTime,
}
impl Default for Config {
@ -64,7 +64,7 @@ impl Default for Config {
address_size: AddressSize::_24bit,
prescaler: 128,
fifo_threshold: FIFOThresholdLevel::_17Bytes,
cs_high_time: ChipSelectHightTime::_5Cycle,
cs_high_time: ChipSelectHighTime::_5Cycle,
}
}
}
@ -119,7 +119,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
Some(nss.map_into()),
dma,
config,
FlashSelection::Flash2,
FlashSelection::Flash1,
)
}

View File

@ -70,7 +70,9 @@ pub struct Pll {
pub mul: PllMul,
/// PLL P division factor. If None, PLL P output is disabled.
/// On PLL1, it must be even (in particular, it cannot be 1.)
/// On PLL1, it must be even for most series (in particular,
/// it cannot be 1 in series other than STM32H723/733,
/// STM32H725/735 and STM32H730.)
pub divp: Option<PllDiv>,
/// PLL Q division factor. If None, PLL Q output is disabled.
pub divq: Option<PllDiv>,
@ -476,7 +478,14 @@ pub(crate) unsafe fn init(config: Config) {
VoltageScale::Scale2 => (Hertz(160_000_000), Hertz(160_000_000), Hertz(80_000_000)),
VoltageScale::Scale3 => (Hertz(88_000_000), Hertz(88_000_000), Hertz(44_000_000)),
};
#[cfg(all(stm32h7, not(pwr_h7rm0455)))]
#[cfg(pwr_h7rm0468)]
let (d1cpre_clk_max, hclk_max, pclk_max) = match config.voltage_scale {
VoltageScale::Scale0 => (Hertz(520_000_000), Hertz(275_000_000), Hertz(137_500_000)),
VoltageScale::Scale1 => (Hertz(400_000_000), Hertz(200_000_000), Hertz(100_000_000)),
VoltageScale::Scale2 => (Hertz(300_000_000), Hertz(150_000_000), Hertz(75_000_000)),
VoltageScale::Scale3 => (Hertz(170_000_000), Hertz(85_000_000), Hertz(42_500_000)),
};
#[cfg(all(stm32h7, not(any(pwr_h7rm0455, pwr_h7rm0468))))]
let (d1cpre_clk_max, hclk_max, pclk_max) = match config.voltage_scale {
VoltageScale::Scale0 => (Hertz(480_000_000), Hertz(240_000_000), Hertz(120_000_000)),
VoltageScale::Scale1 => (Hertz(400_000_000), Hertz(200_000_000), Hertz(100_000_000)),
@ -729,9 +738,12 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
let p = config.divp.map(|div| {
if num == 0 {
// on PLL1, DIVP must be even.
// on PLL1, DIVP must be even for most series.
// The enum value is 1 less than the divider, so check it's odd.
#[cfg(not(pwr_h7rm0468))]
assert!(div.to_bits() % 2 == 1);
#[cfg(pwr_h7rm0468)]
assert!(div.to_bits() % 2 == 1 || div.to_bits() == 0);
}
vco_clk / div
@ -820,7 +832,7 @@ fn flash_setup(clk: Hertz, vos: VoltageScale) {
_ => unreachable!(),
};
#[cfg(flash_h7)]
#[cfg(all(flash_h7, not(pwr_h7rm0468)))]
let (latency, wrhighfreq) = match (vos, clk.0) {
// VOS 0 range VCORE 1.26V - 1.40V
(VoltageScale::Scale0, ..=70_000_000) => (0, 0),
@ -849,6 +861,30 @@ fn flash_setup(clk: Hertz, vos: VoltageScale) {
_ => unreachable!(),
};
// See RM0468 Rev 3 Table 16. FLASH recommended number of wait
// states and programming delay
#[cfg(all(flash_h7, pwr_h7rm0468))]
let (latency, wrhighfreq) = match (vos, clk.0) {
// VOS 0 range VCORE 1.26V - 1.40V
(VoltageScale::Scale0, ..=70_000_000) => (0, 0),
(VoltageScale::Scale0, ..=140_000_000) => (1, 1),
(VoltageScale::Scale0, ..=210_000_000) => (2, 2),
(VoltageScale::Scale0, ..=275_000_000) => (3, 3),
// VOS 1 range VCORE 1.15V - 1.26V
(VoltageScale::Scale1, ..=67_000_000) => (0, 0),
(VoltageScale::Scale1, ..=133_000_000) => (1, 1),
(VoltageScale::Scale1, ..=200_000_000) => (2, 2),
// VOS 2 range VCORE 1.05V - 1.15V
(VoltageScale::Scale2, ..=50_000_000) => (0, 0),
(VoltageScale::Scale2, ..=100_000_000) => (1, 1),
(VoltageScale::Scale2, ..=150_000_000) => (2, 2),
// VOS 3 range VCORE 0.95V - 1.05V
(VoltageScale::Scale3, ..=35_000_000) => (0, 0),
(VoltageScale::Scale3, ..=70_000_000) => (1, 1),
(VoltageScale::Scale3, ..=85_000_000) => (2, 2),
_ => unreachable!(),
};
// See RM0455 Rev 10 Table 16. FLASH recommended number of wait
// states and programming delay
#[cfg(flash_h7ab)]

View File

@ -583,10 +583,10 @@ fn get_ring_buffer<'d, T: Instance, C: Channel, W: word::Word>(
};
match tx_rx {
TxRx::Transmitter => RingBuffer::Writable(unsafe {
WritableRingBuffer::new_write(dma, request, dr(T::REGS, sub_block), dma_buf, opts)
WritableRingBuffer::new(dma, request, dr(T::REGS, sub_block), dma_buf, opts)
}),
TxRx::Receiver => RingBuffer::Readable(unsafe {
ReadableRingBuffer::new_read(dma, request, dr(T::REGS, sub_block), dma_buf, opts)
ReadableRingBuffer::new(dma, request, dr(T::REGS, sub_block), dma_buf, opts)
}),
}
}

View File

@ -474,16 +474,29 @@ impl Driver for RtcDriver {
return false;
}
let safe_timestamp = timestamp.max(t + 3);
// Write the CCR value regardless of whether we're going to enable it now or not.
// This way, when we enable it later, the right value is already set.
r.ccr(n + 1).write(|w| w.set_ccr(safe_timestamp as u16));
r.ccr(n + 1).write(|w| w.set_ccr(timestamp as u16));
// Enable it if it'll happen soon. Otherwise, `next_period` will enable it.
let diff = timestamp - t;
r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000));
// Reevaluate if the alarm timestamp is still in the future
let t = self.now();
if timestamp <= t {
// If alarm timestamp has passed since we set it, we have a race condition and
// the alarm may or may not have fired.
// Disarm the alarm and return `false` to indicate that.
// It is the caller's responsibility to handle this ambiguity.
r.dier().modify(|w| w.set_ccie(n + 1, false));
alarm.timestamp.set(u64::MAX);
return false;
}
// We're confident the alarm will ring in the future.
true
})
}

View File

@ -2,7 +2,9 @@
macro_rules! pin_trait {
($signal:ident, $instance:path) => {
#[doc = concat!(stringify!($signal), " pin trait")]
pub trait $signal<T: $instance>: crate::gpio::Pin {
#[doc = concat!("Get the AF number needed to use this pin as", stringify!($signal))]
fn af_num(&self) -> u8;
}
};
@ -22,7 +24,11 @@ macro_rules! pin_trait_impl {
macro_rules! dma_trait {
($signal:ident, $instance:path) => {
#[doc = concat!(stringify!($signal), " DMA request trait")]
pub trait $signal<T: $instance>: crate::dma::Channel {
#[doc = concat!("Get the DMA request number needed to use this channel as", stringify!($signal))]
/// Note: in some chips, ST calls this the "channel", and calls channels "streams".
/// `embassy-stm32` always uses the "channel" and "request number" names.
fn request(&self) -> crate::dma::Request;
}
};

View File

@ -39,7 +39,7 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> UartRx<'d, T, RxDma> {
let rx_dma = unsafe { self.rx_dma.clone_unchecked() };
let _peri = unsafe { self._peri.clone_unchecked() };
let ring_buf = unsafe { ReadableRingBuffer::new_read(rx_dma, request, rdr(T::regs()), dma_buf, opts) };
let ring_buf = unsafe { ReadableRingBuffer::new(rx_dma, request, rdr(T::regs()), dma_buf, opts) };
// Don't disable the clock
mem::forget(self);

View File

@ -108,6 +108,10 @@ pub trait Driver: Send + Sync + 'static {
/// The `Driver` implementation should guarantee that the alarm callback is never called synchronously from `set_alarm`.
/// Rather - if `timestamp` is already in the past - `false` should be returned and alarm should not be set,
/// or alternatively, the driver should return `true` and arrange to call the alarm callback as soon as possible, but not synchronously.
/// There is a rare third possibility that the alarm was barely in the future, and by the time it was enabled, it had slipped into the
/// past. This is can be detected by double-checking that the alarm is still in the future after enabling it; if it isn't, `false`
/// should also be returned to indicate that the callback may have been called already by the alarm, but it is not guaranteed, so the
/// caller should also call the callback, just like in the more common `false` case. (Note: This requires idempotency of the callback.)
///
/// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp.
///

View File

@ -31,7 +31,7 @@ fn test_flash(f: &mut Flash<'_, Blocking>, offset: u32, size: u32) {
info!("Reading...");
let mut buf = [0u8; 32];
unwrap!(f.read(offset, &mut buf));
unwrap!(f.blocking_read(offset, &mut buf));
info!("Read: {=[u8]:x}", buf);
info!("Erasing...");
@ -39,7 +39,7 @@ fn test_flash(f: &mut Flash<'_, Blocking>, offset: u32, size: u32) {
info!("Reading...");
let mut buf = [0u8; 32];
unwrap!(f.read(offset, &mut buf));
unwrap!(f.blocking_read(offset, &mut buf));
info!("Read after erase: {=[u8]:x}", buf);
info!("Writing...");
@ -53,7 +53,7 @@ fn test_flash(f: &mut Flash<'_, Blocking>, offset: u32, size: u32) {
info!("Reading...");
let mut buf = [0u8; 32];
unwrap!(f.read(offset, &mut buf));
unwrap!(f.blocking_read(offset, &mut buf));
info!("Read: {=[u8]:x}", buf);
assert_eq!(
&buf[..],

View File

@ -48,7 +48,7 @@ async fn test_flash<'a>(f: &mut Flash<'a>, offset: u32, size: u32) {
info!("Reading...");
let mut buf = [0u8; 32];
unwrap!(f.read(offset, &mut buf));
unwrap!(f.blocking_read(offset, &mut buf));
info!("Read: {=[u8]:x}", buf);
info!("Erasing...");
@ -56,7 +56,7 @@ async fn test_flash<'a>(f: &mut Flash<'a>, offset: u32, size: u32) {
info!("Reading...");
let mut buf = [0u8; 32];
unwrap!(f.read(offset, &mut buf));
unwrap!(f.blocking_read(offset, &mut buf));
info!("Read after erase: {=[u8]:x}", buf);
info!("Writing...");
@ -73,7 +73,7 @@ async fn test_flash<'a>(f: &mut Flash<'a>, offset: u32, size: u32) {
info!("Reading...");
let mut buf = [0u8; 32];
unwrap!(f.read(offset, &mut buf));
unwrap!(f.blocking_read(offset, &mut buf));
info!("Read: {=[u8]:x}", buf);
assert_eq!(
&buf[..],

View File

@ -0,0 +1,131 @@
// Configure TIM3 in PWM mode, and start DMA Transfer(s) to send color data into ws2812.
// We assume the DIN pin of ws2812 connect to GPIO PB4, and ws2812 is properly powered.
//
// This demo is a combination of HAL, PAC, and manually invoke `dma::Transfer`
//
// Warning:
// DO NOT stare at ws2812 directy (especially after each MCU Reset), its (max) brightness could easily make your eyes feel burn.
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use embassy_executor::Spawner;
use embassy_stm32::gpio::OutputType;
use embassy_stm32::pac;
use embassy_stm32::time::khz;
use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
use embassy_stm32::timer::{Channel, CountingMode};
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let mut device_config = embassy_stm32::Config::default();
// set SYSCLK/HCLK/PCLK2 to 20 MHz, thus each tick is 0.05 us,
// and ws2812 timings are integer multiples of 0.05 us
{
use embassy_stm32::rcc::*;
use embassy_stm32::time::*;
device_config.enable_debug_during_sleep = true;
device_config.rcc.hse = Some(Hse {
freq: mhz(12),
mode: HseMode::Oscillator,
});
device_config.rcc.sys = Sysclk::PLL1_P;
device_config.rcc.pll_src = PllSource::HSE;
device_config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV6,
mul: PllMul::MUL80,
divp: Some(PllPDiv::DIV8),
divq: None,
divr: None,
});
}
let mut dp = embassy_stm32::init(device_config);
let mut ws2812_pwm = SimplePwm::new(
dp.TIM3,
Some(PwmPin::new_ch1(dp.PB4, OutputType::PushPull)),
None,
None,
None,
khz(800), // data rate of ws2812
CountingMode::EdgeAlignedUp,
);
// PAC level hacking,
// enable auto-reload preload, and enable timer-update-event trigger DMA
{
pac::TIM3.cr1().modify(|v| v.set_arpe(true));
pac::TIM3.dier().modify(|v| v.set_ude(true));
}
// construct ws2812 non-return-to-zero (NRZ) code bit by bit
let max_duty = ws2812_pwm.get_max_duty();
let n0 = 8 * max_duty / 25; // ws2812 Bit 0 high level timing
let n1 = 2 * n0; // ws2812 Bit 1 high level timing
let turn_off = [
n0, n0, n0, n0, n0, n0, n0, n0, // Green
n0, n0, n0, n0, n0, n0, n0, n0, // Red
n0, n0, n0, n0, n0, n0, n0, n0, // Blue
0, // keep PWM output low after a transfer
];
let dim_white = [
n0, n0, n0, n0, n0, n0, n1, n0, // Green
n0, n0, n0, n0, n0, n0, n1, n0, // Red
n0, n0, n0, n0, n0, n0, n1, n0, // Blue
0, // keep PWM output low after a transfer
];
let color_list = [&turn_off, &dim_white];
let pwm_channel = Channel::Ch1;
// make sure PWM output keep low on first start
ws2812_pwm.set_duty(pwm_channel, 0);
{
use embassy_stm32::dma::{Burst, FifoThreshold, Transfer, TransferOptions};
// configure FIFO and MBURST of DMA, to minimize DMA occupation on AHB/APB
let mut dma_transfer_option = TransferOptions::default();
dma_transfer_option.fifo_threshold = Some(FifoThreshold::Full);
dma_transfer_option.mburst = Burst::Incr8;
let mut color_list_index = 0;
loop {
// start PWM output
ws2812_pwm.enable(pwm_channel);
unsafe {
Transfer::new_write(
// with &mut, we can easily reuse same DMA channel multiple times
&mut dp.DMA1_CH2,
5,
color_list[color_list_index],
pac::TIM3.ccr(pwm_channel.raw()).as_ptr() as *mut _,
dma_transfer_option,
)
.await;
// ws2812 need at least 50 us low level input to confirm the input data and change it's state
Timer::after_micros(50).await;
}
// stop PWM output for saving some energy
ws2812_pwm.disable(pwm_channel);
// wait another half second, so that we can see color change
Timer::after_millis(500).await;
// flip the index bit so that next round DMA transfer the other color data
color_list_index ^= 1;
}
}
}