#![no_std]
#![allow(unsafe_op_in_unsafe_fn)]
// Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))]
#![cfg_attr(
docsrs,
doc = "
\n\n"
)]
#![doc = include_str!("../README.md")]
// These mods MUST go first, so that the others see the macros.
pub(crate) mod fmt;
mod macros;
pub mod adc;
pub mod dma;
pub mod gpio;
// TODO: I2C unicomm
#[cfg(not(unicomm))]
pub mod i2c;
#[cfg(not(unicomm))]
pub mod i2c_target;
#[cfg(any(mspm0g150x, mspm0g151x, mspm0g350x, mspm0g351x))]
pub mod mathacl;
pub mod timer;
// TODO: UART unicomm
#[cfg(not(unicomm))]
pub mod uart;
pub mod wwdt;
/// Operating modes for peripherals.
pub mod mode {
trait SealedMode {}
/// Operating mode for a peripheral.
#[allow(private_bounds)]
pub trait Mode: SealedMode {}
/// Blocking mode.
pub struct Blocking;
impl SealedMode for Blocking {}
impl Mode for Blocking {}
/// Async mode.
pub struct Async;
impl SealedMode for Async {}
impl Mode for Async {}
}
#[cfg(feature = "_time-driver")]
mod time_driver;
pub(crate) mod _generated {
#![allow(dead_code)]
#![allow(unused_imports)]
#![allow(non_snake_case)]
#![allow(missing_docs)]
include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
}
// Reexports
pub(crate) use _generated::gpio_pincm;
pub use _generated::{Peripherals, peripherals};
pub use embassy_hal_internal::Peri;
#[cfg(feature = "unstable-pac")]
pub use mspm0_metapac as pac;
#[cfg(not(feature = "unstable-pac"))]
pub(crate) use mspm0_metapac as pac;
pub use crate::_generated::interrupt;
/// Macro to bind interrupts to handlers.
///
/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
/// prove at compile-time that the right interrupts have been bound.
///
/// Example of how to bind one interrupt:
///
/// ```rust,ignore
/// use embassy_nrf::{bind_interrupts, spim, peripherals};
///
/// bind_interrupts!(
/// /// Binds the SPIM3 interrupt.
/// struct Irqs {
/// SPIM3 => spim::InterruptHandler;
/// }
/// );
/// ```
///
/// Example of how to bind multiple interrupts in a single macro invocation:
///
/// ```rust,ignore
/// use embassy_nrf::{bind_interrupts, spim, twim, peripherals};
///
/// bind_interrupts!(struct Irqs {
/// SPIM3 => spim::InterruptHandler;
/// TWISPI0 => twim::InterruptHandler;
/// });
/// ```
// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.
#[macro_export]
macro_rules! bind_interrupts {
($(#[$attr:meta])* $vis:vis struct $name:ident {
$(
$(#[cfg($cond_irq:meta)])?
$irq:ident => $(
$(#[cfg($cond_handler:meta)])?
$handler:ty
),*;
)*
}) => {
#[derive(Copy, Clone)]
$(#[$attr])*
$vis struct $name;
$(
#[allow(non_snake_case)]
#[unsafe(no_mangle)]
$(#[cfg($cond_irq)])?
unsafe extern "C" fn $irq() {
unsafe {
$(
$(#[cfg($cond_handler)])?
<$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
)*
}
}
$(#[cfg($cond_irq)])?
$crate::bind_interrupts!(@inner
$(
$(#[cfg($cond_handler)])?
unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}
)*
);
)*
};
(@inner $($t:tt)*) => {
$($t)*
}
}
/// `embassy-mspm0` global configuration.
#[non_exhaustive]
#[derive(Clone, Copy)]
pub struct Config {
// TODO: OSC configuration.
/// The size of DMA block transfer burst.
///
/// If this is set to a value
pub dma_burst_size: dma::BurstSize,
/// Whether the DMA channels are used in a fixed priority or a round robin fashion.
///
/// If [`false`], the DMA priorities are fixed.
///
/// If [`true`], after a channel finishes a transfer it becomes the lowest priority.
pub dma_round_robin: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
dma_burst_size: dma::BurstSize::Complete,
dma_round_robin: false,
}
}
}
pub fn init(config: Config) -> Peripherals {
critical_section::with(|cs| {
let peripherals = Peripherals::take_with_cs(cs);
// TODO: Further clock configuration
pac::SYSCTL.mclkcfg().modify(|w| {
// Enable MFCLK
w.set_usemftick(true);
// MDIV must be disabled if MFCLK is enabled.
w.set_mdiv(0);
});
// Enable MFCLK for peripheral use
//
// TODO: Optional?
pac::SYSCTL.genclken().modify(|w| {
w.set_mfpclken(true);
});
pac::SYSCTL.borthreshold().modify(|w| {
w.set_level(0);
});
gpio::init(pac::GPIOA);
#[cfg(gpio_pb)]
gpio::init(pac::GPIOB);
#[cfg(gpio_pc)]
gpio::init(pac::GPIOC);
_generated::enable_group_interrupts(cs);
#[cfg(any(mspm0c110x, mspm0l110x))]
unsafe {
use crate::_generated::interrupt::typelevel::Interrupt;
crate::interrupt::typelevel::GPIOA::enable();
}
// SAFETY: Peripherals::take_with_cs will only be run once or panic.
unsafe { dma::init(cs, config.dma_burst_size, config.dma_round_robin) };
#[cfg(feature = "_time-driver")]
time_driver::init(cs);
peripherals
})
}
pub(crate) mod sealed {
#[allow(dead_code)]
pub trait Sealed {}
}
struct BitIter(u32);
impl Iterator for BitIter {
type Item = u32;
fn next(&mut self) -> Option {
match self.0.trailing_zeros() {
32 => None,
b => {
self.0 &= !(1 << b);
Some(b)
}
}
}
}
/// Reset cause values from SYSCTL.RSTCAUSE register.
/// Based on MSPM0 L-series Technical Reference Manual Table 2-9 and
/// MSPM0 G-series Technical Reference Manual Table 2-12.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ResetCause {
/// No reset since last read
NoReset,
/// VDD < POR- violation, PMU trim parity fault, or SHUTDNSTOREx parity fault
PorHwFailure,
/// NRST pin reset (>1s)
PorExternalNrst,
/// Software-triggered POR
PorSwTriggered,
/// VDD < BOR- violation
BorSupplyFailure,
/// Wake from SHUTDOWN
BorWakeFromShutdown,
/// Non-PMU trim parity fault
#[cfg(not(any(
mspm0c110x,
mspm0c1105_c1106,
mspm0g110x,
mspm0g150x,
mspm0g151x,
mspm0g310x,
mspm0g350x,
mspm0g351x
)))]
BootrstNonPmuParityFault,
/// Fatal clock fault
BootrstClockFault,
/// Software-triggered BOOTRST
BootrstSwTriggered,
/// NRST pin reset (<1s)
BootrstExternalNrst,
/// WWDT0 violation
BootrstWwdt0Violation,
/// WWDT1 violation (G-series only)
#[cfg(any(mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0g518x))]
SysrstWwdt1Violation,
/// BSL exit (if present)
SysrstBslExit,
/// BSL entry (if present)
SysrstBslEntry,
/// Uncorrectable flash ECC error (if present)
#[cfg(not(any(mspm0c110x, mspm0c1105_c1106, mspm0g351x, mspm0g151x)))]
SysrstFlashEccError,
/// CPU lockup violation
SysrstCpuLockupViolation,
/// Debug-triggered SYSRST
SysrstDebugTriggered,
/// Software-triggered SYSRST
SysrstSwTriggered,
/// Debug-triggered CPURST
CpurstDebugTriggered,
/// Software-triggered CPURST
CpurstSwTriggered,
}
/// Read the reset cause from the SYSCTL.RSTCAUSE register.
///
/// This function reads the reset cause register which indicates why the last
/// system reset occurred. The register is automatically cleared after being read,
/// so this should be called only once per application startup.
///
/// If the reset cause is not recognized, an `Err` containing the raw value is returned.
#[must_use = "Reading reset cause will clear it"]
pub fn read_reset_cause() -> Result {
let cause_raw = pac::SYSCTL.rstcause().read().id();
use ResetCause::*;
use pac::sysctl::vals::Id;
match cause_raw {
Id::NORST => Ok(NoReset),
Id::PORHWFAIL => Ok(PorHwFailure),
Id::POREXNRST => Ok(PorExternalNrst),
Id::PORSW => Ok(PorSwTriggered),
Id::BORSUPPLY => Ok(BorSupplyFailure),
Id::BORWAKESHUTDN => Ok(BorWakeFromShutdown),
#[cfg(not(any(
mspm0c110x,
mspm0c1105_c1106,
mspm0g110x,
mspm0g150x,
mspm0g151x,
mspm0g310x,
mspm0g350x,
mspm0g351x,
mspm0g518x,
)))]
Id::BOOTNONPMUPARITY => Ok(BootrstNonPmuParityFault),
Id::BOOTCLKFAIL => Ok(BootrstClockFault),
Id::BOOTSW => Ok(BootrstSwTriggered),
Id::BOOTEXNRST => Ok(BootrstExternalNrst),
Id::BOOTWWDT0 => Ok(BootrstWwdt0Violation),
Id::SYSBSLEXIT => Ok(SysrstBslExit),
Id::SYSBSLENTRY => Ok(SysrstBslEntry),
#[cfg(any(mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0g518x))]
Id::SYSWWDT1 => Ok(SysrstWwdt1Violation),
#[cfg(not(any(mspm0c110x, mspm0c1105_c1106, mspm0g351x, mspm0g151x)))]
Id::SYSFLASHECC => Ok(SysrstFlashEccError),
Id::SYSCPULOCK => Ok(SysrstCpuLockupViolation),
Id::SYSDBG => Ok(SysrstDebugTriggered),
Id::SYSSW => Ok(SysrstSwTriggered),
Id::CPUDBG => Ok(CpurstDebugTriggered),
Id::CPUSW => Ok(CpurstSwTriggered),
other => Err(other as u8),
}
}