for bool {
+ fn from(level: Level) -> bool {
+ match level {
+ Level::Low => false,
+ Level::High => true,
+ }
+ }
+}
+
+/// Represents a pull setting for an input.
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+pub enum Pull {
+ /// No pull.
+ None,
+ /// Internal pull-up resistor.
+ Up,
+ /// Internal pull-down resistor.
+ Down,
+}
+
+/// A GPIO bank with up to 32 pins.
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+pub enum Port {
+ /// Port A.
+ PortA = 0,
+
+ /// Port B.
+ #[cfg(gpio_pb)]
+ PortB = 1,
+
+ /// Port C.
+ #[cfg(gpio_pc)]
+ PortC = 2,
+}
+
+/// GPIO flexible pin.
+///
+/// This pin can either be a disconnected, input, or output pin, or both. The level register bit will remain
+/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output
+/// mode.
+pub struct Flex<'d> {
+ pin: PeripheralRef<'d, AnyPin>,
+}
+
+impl<'d> Flex<'d> {
+ /// Wrap the pin in a `Flex`.
+ ///
+ /// The pin remains disconnected. The initial output level is unspecified, but can be changed
+ /// before the pin is put into output mode.
+ #[inline]
+ pub fn new(pin: impl Peripheral + 'd) -> Self {
+ into_ref!(pin);
+
+ // Pin will be in disconnected state.
+ Self { pin: pin.map_into() }
+ }
+
+ /// Set the pin's pull.
+ #[inline]
+ pub fn set_pull(&mut self, pull: Pull) {
+ let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
+
+ pincm.modify(|w| {
+ w.set_pipd(matches!(pull, Pull::Down));
+ w.set_pipu(matches!(pull, Pull::Up));
+ });
+ }
+
+ /// Put the pin into input mode.
+ ///
+ /// The pull setting is left unchanged.
+ #[inline]
+ pub fn set_as_input(&mut self) {
+ let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
+
+ pincm.modify(|w| {
+ w.set_pf(GPIO_PF);
+ w.set_hiz1(false);
+ w.set_pc(true);
+ w.set_inena(true);
+ });
+
+ self.pin.block().doeclr31_0().write(|w| {
+ w.set_dio(self.pin.bit_index(), true);
+ });
+ }
+
+ /// Put the pin into output mode.
+ ///
+ /// The pin level will be whatever was set before (or low by default). If you want it to begin
+ /// at a specific level, call `set_high`/`set_low` on the pin first.
+ #[inline]
+ pub fn set_as_output(&mut self) {
+ let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
+
+ pincm.modify(|w| {
+ w.set_pf(GPIO_PF);
+ w.set_hiz1(false);
+ w.set_pc(true);
+ w.set_inena(false);
+ });
+
+ self.pin.block().doeset31_0().write(|w| {
+ w.set_dio(self.pin.bit_index(), true);
+ });
+ }
+
+ /// Put the pin into input + open-drain output mode.
+ ///
+ /// The hardware will drive the line low if you set it to low, and will leave it floating if you set
+ /// it to high, in which case you can read the input to figure out whether another device
+ /// is driving the line low.
+ ///
+ /// The pin level will be whatever was set before (or low by default). If you want it to begin
+ /// at a specific level, call `set_high`/`set_low` on the pin first.
+ ///
+ /// The internal weak pull-up and pull-down resistors will be disabled.
+ #[inline]
+ pub fn set_as_input_output(&mut self) {
+ let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
+
+ pincm.modify(|w| {
+ w.set_pf(GPIO_PF);
+ w.set_hiz1(true);
+ w.set_pc(true);
+ w.set_inena(false);
+ });
+
+ self.set_pull(Pull::None);
+ }
+
+ /// Set the pin as "disconnected", ie doing nothing and consuming the lowest
+ /// amount of power possible.
+ ///
+ /// This is currently the same as [`Self::set_as_analog()`] but is semantically different
+ /// really. Drivers should `set_as_disconnected()` pins when dropped.
+ ///
+ /// Note that this also disables the internal weak pull-up and pull-down resistors.
+ #[inline]
+ pub fn set_as_disconnected(&mut self) {
+ let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
+
+ pincm.modify(|w| {
+ w.set_pf(DISCONNECT_PF);
+ w.set_hiz1(false);
+ w.set_pc(false);
+ w.set_inena(false);
+ });
+
+ self.set_pull(Pull::None);
+ self.set_inversion(false);
+ }
+
+ /// Configure the logic inversion of this pin.
+ ///
+ /// Logic inversion applies to both the input and output path of this pin.
+ #[inline]
+ pub fn set_inversion(&mut self, invert: bool) {
+ let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
+
+ pincm.modify(|w| {
+ w.set_inv(invert);
+ });
+ }
+
+ // TODO: drive strength, hysteresis, wakeup enable, wakeup compare
+
+ /// Put the pin into the PF mode, unchecked.
+ ///
+ /// This puts the pin into the PF mode, with the request number. This is completely unchecked,
+ /// it can attach the pin to literally any peripheral, so use with care. In addition the pin
+ /// peripheral is connected in the iomux.
+ ///
+ /// The peripheral attached to the pin depends on the part in use. Consult the datasheet
+ /// or technical reference manual for additional details.
+ #[inline]
+ pub fn set_pf_unchecked(&mut self, pf: u8) {
+ // Per SLAU893, PF is only 5 bits
+ assert!((pf & 0x3F) != 0, "PF is out of range");
+
+ let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
+
+ pincm.modify(|w| {
+ w.set_pf(pf);
+ // If the PF is manually set, connect the pin
+ w.set_pc(true);
+ });
+ }
+
+ /// Get whether the pin input level is high.
+ #[inline]
+ pub fn is_high(&self) -> bool {
+ !self.is_low()
+ }
+
+ /// Get whether the pin input level is low.
+ #[inline]
+ pub fn is_low(&self) -> bool {
+ self.pin.block().din31_0().read().dio(self.pin.bit_index())
+ }
+
+ /// Returns current pin level
+ #[inline]
+ pub fn get_level(&self) -> Level {
+ self.is_high().into()
+ }
+
+ /// Set the output as high.
+ #[inline]
+ pub fn set_high(&mut self) {
+ self.pin.block().doutset31_0().write(|w| {
+ w.set_dio(self.pin.bit_index() as usize, true);
+ });
+ }
+
+ /// Set the output as low.
+ #[inline]
+ pub fn set_low(&mut self) {
+ self.pin.block().doutclr31_0().write(|w| {
+ w.set_dio(self.pin.bit_index(), true);
+ });
+ }
+
+ /// Toggle pin output
+ #[inline]
+ pub fn toggle(&mut self) {
+ self.pin.block().douttgl31_0().write(|w| {
+ w.set_dio(self.pin.bit_index(), true);
+ })
+ }
+
+ /// Set the output level.
+ #[inline]
+ pub fn set_level(&mut self, level: Level) {
+ match level {
+ Level::Low => self.set_low(),
+ Level::High => self.set_high(),
+ }
+ }
+
+ /// Get the current pin input level.
+ #[inline]
+ pub fn get_output_level(&self) -> Level {
+ self.is_high().into()
+ }
+
+ /// Is the output level high?
+ #[inline]
+ pub fn is_set_high(&self) -> bool {
+ !self.is_set_low()
+ }
+
+ /// Is the output level low?
+ #[inline]
+ pub fn is_set_low(&self) -> bool {
+ (self.pin.block().dout31_0().read().0 & self.pin.bit_index() as u32) == 0
+ }
+
+ /// Wait until the pin is high. If it is already high, return immediately.
+ #[inline]
+ pub async fn wait_for_high(&mut self) {
+ if self.is_high() {
+ return;
+ }
+
+ self.wait_for_rising_edge().await
+ }
+
+ /// Wait until the pin is low. If it is already low, return immediately.
+ #[inline]
+ pub async fn wait_for_low(&mut self) {
+ if self.is_low() {
+ return;
+ }
+
+ self.wait_for_falling_edge().await
+ }
+
+ /// Wait for the pin to undergo a transition from low to high.
+ #[inline]
+ pub async fn wait_for_rising_edge(&mut self) {
+ InputFuture::new(self.pin.reborrow(), Polarity::RISE).await
+ }
+
+ /// Wait for the pin to undergo a transition from high to low.
+ #[inline]
+ pub async fn wait_for_falling_edge(&mut self) {
+ InputFuture::new(self.pin.reborrow(), Polarity::FALL).await
+ }
+
+ /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
+ #[inline]
+ pub async fn wait_for_any_edge(&mut self) {
+ InputFuture::new(self.pin.reborrow(), Polarity::RISE_FALL).await
+ }
+}
+
+impl<'d> Drop for Flex<'d> {
+ #[inline]
+ fn drop(&mut self) {
+ self.set_as_disconnected();
+ }
+}
+
+/// GPIO input driver.
+pub struct Input<'d> {
+ pin: Flex<'d>,
+}
+
+impl<'d> Input<'d> {
+ /// Create GPIO input driver for a [Pin] with the provided [Pull] configuration.
+ #[inline]
+ pub fn new(pin: impl Peripheral
+ 'd, pull: Pull) -> Self {
+ let mut pin = Flex::new(pin);
+ pin.set_as_input();
+ pin.set_pull(pull);
+ Self { pin }
+ }
+
+ /// Get whether the pin input level is high.
+ #[inline]
+ pub fn is_high(&self) -> bool {
+ self.pin.is_high()
+ }
+
+ /// Get whether the pin input level is low.
+ #[inline]
+ pub fn is_low(&self) -> bool {
+ self.pin.is_low()
+ }
+
+ /// Get the current pin input level.
+ #[inline]
+ pub fn get_level(&self) -> Level {
+ self.pin.get_level()
+ }
+
+ /// Configure the logic inversion of this pin.
+ ///
+ /// Logic inversion applies to the input path of this pin.
+ #[inline]
+ pub fn set_inversion(&mut self, invert: bool) {
+ self.pin.set_inversion(invert)
+ }
+
+ /// Wait until the pin is high. If it is already high, return immediately.
+ #[inline]
+ pub async fn wait_for_high(&mut self) {
+ self.pin.wait_for_high().await
+ }
+
+ /// Wait until the pin is low. If it is already low, return immediately.
+ #[inline]
+ pub async fn wait_for_low(&mut self) {
+ self.pin.wait_for_low().await
+ }
+
+ /// Wait for the pin to undergo a transition from low to high.
+ #[inline]
+ pub async fn wait_for_rising_edge(&mut self) {
+ self.pin.wait_for_rising_edge().await
+ }
+
+ /// Wait for the pin to undergo a transition from high to low.
+ #[inline]
+ pub async fn wait_for_falling_edge(&mut self) {
+ self.pin.wait_for_falling_edge().await
+ }
+
+ /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
+ #[inline]
+ pub async fn wait_for_any_edge(&mut self) {
+ self.pin.wait_for_any_edge().await
+ }
+}
+
+/// GPIO output driver.
+///
+/// Note that pins will **return to their floating state** when `Output` is dropped.
+/// If pins should retain their state indefinitely, either keep ownership of the
+/// `Output`, or pass it to [`core::mem::forget`].
+pub struct Output<'d> {
+ pin: Flex<'d>,
+}
+
+impl<'d> Output<'d> {
+ /// Create GPIO output driver for a [Pin] with the provided [Level] configuration.
+ #[inline]
+ pub fn new(pin: impl Peripheral
+ 'd, initial_output: Level) -> Self {
+ let mut pin = Flex::new(pin);
+ pin.set_as_output();
+ pin.set_level(initial_output);
+ Self { pin }
+ }
+
+ /// Set the output as high.
+ #[inline]
+ pub fn set_high(&mut self) {
+ self.pin.set_high();
+ }
+
+ /// Set the output as low.
+ #[inline]
+ pub fn set_low(&mut self) {
+ self.pin.set_low();
+ }
+
+ /// Set the output level.
+ #[inline]
+ pub fn set_level(&mut self, level: Level) {
+ self.pin.set_level(level)
+ }
+
+ /// Is the output pin set as high?
+ #[inline]
+ pub fn is_set_high(&self) -> bool {
+ self.pin.is_set_high()
+ }
+
+ /// Is the output pin set as low?
+ #[inline]
+ pub fn is_set_low(&self) -> bool {
+ self.pin.is_set_low()
+ }
+
+ /// What level output is set to
+ #[inline]
+ pub fn get_output_level(&self) -> Level {
+ self.pin.get_output_level()
+ }
+
+ /// Toggle pin output
+ #[inline]
+ pub fn toggle(&mut self) {
+ self.pin.toggle();
+ }
+
+ /// Configure the logic inversion of this pin.
+ ///
+ /// Logic inversion applies to the input path of this pin.
+ #[inline]
+ pub fn set_inversion(&mut self, invert: bool) {
+ self.pin.set_inversion(invert)
+ }
+}
+
+/// GPIO output open-drain driver.
+///
+/// Note that pins will **return to their floating state** when `OutputOpenDrain` is dropped.
+/// If pins should retain their state indefinitely, either keep ownership of the
+/// `OutputOpenDrain`, or pass it to [`core::mem::forget`].
+pub struct OutputOpenDrain<'d> {
+ pin: Flex<'d>,
+}
+
+impl<'d> OutputOpenDrain<'d> {
+ /// Create a new GPIO open drain output driver for a [Pin] with the provided [Level].
+ #[inline]
+ pub fn new(pin: impl Peripheral
+ 'd, initial_output: Level) -> Self {
+ let mut pin = Flex::new(pin);
+ pin.set_level(initial_output);
+ pin.set_as_input_output();
+ Self { pin }
+ }
+
+ /// Get whether the pin input level is high.
+ #[inline]
+ pub fn is_high(&self) -> bool {
+ !self.pin.is_low()
+ }
+
+ /// Get whether the pin input level is low.
+ #[inline]
+ pub fn is_low(&self) -> bool {
+ self.pin.is_low()
+ }
+
+ /// Get the current pin input level.
+ #[inline]
+ pub fn get_level(&self) -> Level {
+ self.pin.get_level()
+ }
+
+ /// Set the output as high.
+ #[inline]
+ pub fn set_high(&mut self) {
+ self.pin.set_high();
+ }
+
+ /// Set the output as low.
+ #[inline]
+ pub fn set_low(&mut self) {
+ self.pin.set_low();
+ }
+
+ /// Set the output level.
+ #[inline]
+ pub fn set_level(&mut self, level: Level) {
+ self.pin.set_level(level);
+ }
+
+ /// Get whether the output level is set to high.
+ #[inline]
+ pub fn is_set_high(&self) -> bool {
+ self.pin.is_set_high()
+ }
+
+ /// Get whether the output level is set to low.
+ #[inline]
+ pub fn is_set_low(&self) -> bool {
+ self.pin.is_set_low()
+ }
+
+ /// Get the current output level.
+ #[inline]
+ pub fn get_output_level(&self) -> Level {
+ self.pin.get_output_level()
+ }
+
+ /// Toggle pin output
+ #[inline]
+ pub fn toggle(&mut self) {
+ self.pin.toggle()
+ }
+
+ /// Configure the logic inversion of this pin.
+ ///
+ /// Logic inversion applies to the input path of this pin.
+ #[inline]
+ pub fn set_inversion(&mut self, invert: bool) {
+ self.pin.set_inversion(invert)
+ }
+
+ /// Wait until the pin is high. If it is already high, return immediately.
+ #[inline]
+ pub async fn wait_for_high(&mut self) {
+ self.pin.wait_for_high().await
+ }
+
+ /// Wait until the pin is low. If it is already low, return immediately.
+ #[inline]
+ pub async fn wait_for_low(&mut self) {
+ self.pin.wait_for_low().await
+ }
+
+ /// Wait for the pin to undergo a transition from low to high.
+ #[inline]
+ pub async fn wait_for_rising_edge(&mut self) {
+ self.pin.wait_for_rising_edge().await
+ }
+
+ /// Wait for the pin to undergo a transition from high to low.
+ #[inline]
+ pub async fn wait_for_falling_edge(&mut self) {
+ self.pin.wait_for_falling_edge().await
+ }
+
+ /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
+ #[inline]
+ pub async fn wait_for_any_edge(&mut self) {
+ self.pin.wait_for_any_edge().await
+ }
+}
+
+/// Type-erased GPIO pin
+pub struct AnyPin {
+ pin_port: u8,
+}
+
+impl AnyPin {
+ /// Create an [AnyPin] for a specific pin.
+ ///
+ /// # Safety
+ /// - `pin_port` should not in use by another driver.
+ #[inline]
+ pub unsafe fn steal(pin_port: u8) -> Self {
+ Self { pin_port }
+ }
+}
+
+impl_peripheral!(AnyPin);
+
+impl Pin for AnyPin {}
+impl SealedPin for AnyPin {
+ #[inline]
+ fn pin_port(&self) -> u8 {
+ self.pin_port
+ }
+}
+
+/// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an [AnyPin].
+#[allow(private_bounds)]
+pub trait Pin: Peripheral
+ Into + SealedPin + Sized + 'static {
+ fn degrade(self) -> AnyPin {
+ AnyPin {
+ pin_port: self.pin_port(),
+ }
+ }
+
+ /// The index of this pin in PINCM (pin control management) registers.
+ #[inline]
+ fn pin_cm(&self) -> u8 {
+ self._pin_cm()
+ }
+}
+
+impl<'d> embedded_hal::digital::ErrorType for Flex<'d> {
+ type Error = Infallible;
+}
+
+impl<'d> embedded_hal::digital::InputPin for Flex<'d> {
+ #[inline]
+ fn is_high(&mut self) -> Result {
+ Ok((*self).is_high())
+ }
+
+ #[inline]
+ fn is_low(&mut self) -> Result {
+ Ok((*self).is_low())
+ }
+}
+
+impl<'d> embedded_hal::digital::OutputPin for Flex<'d> {
+ #[inline]
+ fn set_low(&mut self) -> Result<(), Self::Error> {
+ Ok(self.set_low())
+ }
+
+ #[inline]
+ fn set_high(&mut self) -> Result<(), Self::Error> {
+ Ok(self.set_high())
+ }
+}
+
+impl<'d> embedded_hal::digital::StatefulOutputPin for Flex<'d> {
+ #[inline]
+ fn is_set_high(&mut self) -> Result {
+ Ok((*self).is_set_high())
+ }
+
+ #[inline]
+ fn is_set_low(&mut self) -> Result {
+ Ok((*self).is_set_low())
+ }
+}
+
+impl<'d> embedded_hal_async::digital::Wait for Flex<'d> {
+ async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_high().await;
+ Ok(())
+ }
+
+ async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_low().await;
+ Ok(())
+ }
+
+ async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_rising_edge().await;
+ Ok(())
+ }
+
+ async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_falling_edge().await;
+ Ok(())
+ }
+
+ async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_any_edge().await;
+ Ok(())
+ }
+}
+
+impl<'d> embedded_hal::digital::ErrorType for Input<'d> {
+ type Error = Infallible;
+}
+
+impl<'d> embedded_hal::digital::InputPin for Input<'d> {
+ #[inline]
+ fn is_high(&mut self) -> Result {
+ Ok((*self).is_high())
+ }
+
+ #[inline]
+ fn is_low(&mut self) -> Result {
+ Ok((*self).is_low())
+ }
+}
+
+impl<'d> embedded_hal_async::digital::Wait for Input<'d> {
+ async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_high().await;
+ Ok(())
+ }
+
+ async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_low().await;
+ Ok(())
+ }
+
+ async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_rising_edge().await;
+ Ok(())
+ }
+
+ async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_falling_edge().await;
+ Ok(())
+ }
+
+ async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_any_edge().await;
+ Ok(())
+ }
+}
+
+impl<'d> embedded_hal::digital::ErrorType for Output<'d> {
+ type Error = Infallible;
+}
+
+impl<'d> embedded_hal::digital::OutputPin for Output<'d> {
+ #[inline]
+ fn set_low(&mut self) -> Result<(), Self::Error> {
+ Ok(self.set_low())
+ }
+
+ #[inline]
+ fn set_high(&mut self) -> Result<(), Self::Error> {
+ Ok(self.set_high())
+ }
+}
+
+impl<'d> embedded_hal::digital::StatefulOutputPin for Output<'d> {
+ #[inline]
+ fn is_set_high(&mut self) -> Result {
+ Ok((*self).is_set_high())
+ }
+
+ #[inline]
+ fn is_set_low(&mut self) -> Result {
+ Ok((*self).is_set_low())
+ }
+}
+
+impl<'d> embedded_hal::digital::ErrorType for OutputOpenDrain<'d> {
+ type Error = Infallible;
+}
+
+impl<'d> embedded_hal::digital::InputPin for OutputOpenDrain<'d> {
+ #[inline]
+ fn is_high(&mut self) -> Result {
+ Ok((*self).is_high())
+ }
+
+ #[inline]
+ fn is_low(&mut self) -> Result {
+ Ok((*self).is_low())
+ }
+}
+
+impl<'d> embedded_hal::digital::OutputPin for OutputOpenDrain<'d> {
+ #[inline]
+ fn set_low(&mut self) -> Result<(), Self::Error> {
+ Ok(self.set_low())
+ }
+
+ #[inline]
+ fn set_high(&mut self) -> Result<(), Self::Error> {
+ Ok(self.set_high())
+ }
+}
+
+impl<'d> embedded_hal::digital::StatefulOutputPin for OutputOpenDrain<'d> {
+ #[inline]
+ fn is_set_high(&mut self) -> Result {
+ Ok((*self).is_set_high())
+ }
+
+ #[inline]
+ fn is_set_low(&mut self) -> Result {
+ Ok((*self).is_set_low())
+ }
+}
+
+impl<'d> embedded_hal_async::digital::Wait for OutputOpenDrain<'d> {
+ async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_high().await;
+ Ok(())
+ }
+
+ async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_low().await;
+ Ok(())
+ }
+
+ async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_rising_edge().await;
+ Ok(())
+ }
+
+ async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_falling_edge().await;
+ Ok(())
+ }
+
+ async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_any_edge().await;
+ Ok(())
+ }
+}
+
+/// The pin function to disconnect peripherals from the pin.
+///
+/// This is also the pin function used to connect to analog peripherals, such as an ADC.
+const DISCONNECT_PF: u8 = 0;
+
+/// The pin function for the GPIO peripheral.
+///
+/// This is fixed to `1` for every part.
+const GPIO_PF: u8 = 1;
+
+macro_rules! impl_pin {
+ ($name: ident, $port: expr, $pin_num: expr) => {
+ impl crate::gpio::Pin for crate::peripherals::$name {}
+ impl crate::gpio::SealedPin for crate::peripherals::$name {
+ #[inline]
+ fn pin_port(&self) -> u8 {
+ ($port as u8) * 32 + $pin_num
+ }
+ }
+
+ impl From for crate::gpio::AnyPin {
+ fn from(val: crate::peripherals::$name) -> Self {
+ crate::gpio::Pin::degrade(val)
+ }
+ }
+ };
+}
+
+// TODO: Possible micro-op for C110X, not every pin is instantiated even on the 20 pin parts.
+// This would mean cfg guarding to just cfg guarding every pin instance.
+static PORTA_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
+#[cfg(gpio_pb)]
+static PORTB_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
+#[cfg(gpio_pc)]
+static PORTC_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
+
+pub(crate) trait SealedPin {
+ fn pin_port(&self) -> u8;
+
+ fn port(&self) -> Port {
+ match self.pin_port() / 32 {
+ 0 => Port::PortA,
+ #[cfg(gpio_pb)]
+ 1 => Port::PortB,
+ #[cfg(gpio_pc)]
+ 2 => Port::PortC,
+ _ => unreachable!(),
+ }
+ }
+
+ fn waker(&self) -> &AtomicWaker {
+ match self.port() {
+ Port::PortA => &PORTA_WAKERS[self.bit_index()],
+ #[cfg(gpio_pb)]
+ Port::PortB => &PORTB_WAKERS[self.bit_index()],
+ #[cfg(gpio_pc)]
+ Port::PortC => &PORTC_WAKERS[self.bit_index()],
+ }
+ }
+
+ fn _pin_cm(&self) -> u8 {
+ // Some parts like the MSPM0L222x have pincm mappings all over the place.
+ crate::gpio_pincm(self.pin_port())
+ }
+
+ fn bit_index(&self) -> usize {
+ (self.pin_port() % 32) as usize
+ }
+
+ #[inline]
+ fn block(&self) -> gpio::Gpio {
+ match self.pin_port() / 32 {
+ 0 => pac::GPIOA,
+ #[cfg(gpio_pb)]
+ 1 => pac::GPIOB,
+ #[cfg(gpio_pc)]
+ 2 => pac::GPIOC,
+ _ => unreachable!(),
+ }
+ }
+}
+
+#[must_use = "futures do nothing unless you `.await` or poll them"]
+struct InputFuture<'d> {
+ pin: PeripheralRef<'d, AnyPin>,
+}
+
+impl<'d> InputFuture<'d> {
+ fn new(pin: PeripheralRef<'d, AnyPin>, polarity: Polarity) -> Self {
+ let block = pin.block();
+
+ // First clear the bit for this event. Otherwise previous edge events may be recorded.
+ block.cpu_int().iclr().write(|w| {
+ w.set_dio(pin.bit_index(), true);
+ });
+
+ // Selecting which polarity events happens is a RMW operation.
+ //
+ // Guard with a critical section in case two different threads try to select events at the
+ // same time.
+ critical_section::with(|_cs| {
+ // Tell the hardware which pin event we want to receive.
+ if pin.bit_index() >= 16 {
+ block.polarity31_16().modify(|w| {
+ w.set_dio(pin.bit_index() - 16, polarity);
+ });
+ } else {
+ block.polarity15_0().modify(|w| {
+ w.set_dio(pin.bit_index(), polarity);
+ });
+ };
+ });
+
+ Self { pin }
+ }
+}
+
+impl<'d> Future for InputFuture<'d> {
+ type Output = ();
+
+ fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll {
+ // We need to register/re-register the waker for each poll because any
+ // calls to wake will deregister the waker.
+ let waker = self.pin.waker();
+ waker.register(cx.waker());
+
+ // The interrupt handler will mask the interrupt if the event has occurred.
+ if self.pin.block().cpu_int().ris().read().dio(self.pin.bit_index()) {
+ return Poll::Ready(());
+ }
+
+ // Unmasking the interrupt is a RMW operation.
+ //
+ // Guard with a critical section in case two different threads try to unmask at the same time.
+ critical_section::with(|_cs| {
+ self.pin.block().cpu_int().imask().modify(|w| {
+ w.set_dio(self.pin.bit_index(), true);
+ });
+ });
+
+ Poll::Pending
+ }
+}
+
+pub(crate) fn init(gpio: gpio::Gpio) {
+ gpio.gprcm().rstctl().write(|w| {
+ w.set_resetstkyclr(true);
+ w.set_resetassert(true);
+ w.set_key(ResetKey::KEY);
+ });
+
+ gpio.gprcm().pwren().write(|w| {
+ w.set_enable(true);
+ w.set_key(PwrenKey::KEY);
+ });
+
+ gpio.evt_mode().modify(|w| {
+ // The CPU will clear it's own interrupts
+ w.set_cpu_cfg(EvtCfg::SOFTWARE);
+ });
+}
+
+#[cfg(feature = "rt")]
+fn irq_handler(gpio: gpio::Gpio, wakers: &[AtomicWaker; 32]) {
+ // Only consider pins which have interrupts unmasked.
+ let bits = gpio.cpu_int().mis().read().0;
+
+ for i in BitIter(bits) {
+ wakers[i as usize].wake();
+
+ // Notify the future that an edge event has occurred by masking the interrupt for this pin.
+ gpio.cpu_int().imask().modify(|w| {
+ w.set_dio(i as usize, false);
+ });
+ }
+}
+
+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)
+ }
+ }
+ }
+}
+
+// C110x has a dedicated interrupt just for GPIOA, as it does not have a GROUP1 interrupt.
+#[cfg(all(feature = "rt", feature = "mspm0c110x"))]
+#[interrupt]
+fn GPIOA() {
+ gpioa_interrupt();
+}
+
+#[cfg(feature = "rt")]
+pub(crate) fn gpioa_interrupt() {
+ irq_handler(pac::GPIOA, &PORTA_WAKERS);
+}
+
+#[cfg(all(feature = "rt", gpio_pb))]
+pub(crate) fn gpiob_interrupt() {
+ irq_handler(pac::GPIOB, &PORTB_WAKERS);
+}
+
+#[cfg(all(feature = "rt", gpio_pc))]
+pub(crate) fn gpioc_interrupt() {
+ irq_handler(pac::GPIOC, &PORTC_WAKERS);
+}
diff --git a/embassy-mspm0/src/int_group/c110x.rs b/embassy-mspm0/src/int_group/c110x.rs
new file mode 100644
index 000000000..e6a9ddb99
--- /dev/null
+++ b/embassy-mspm0/src/int_group/c110x.rs
@@ -0,0 +1,25 @@
+use crate::pac;
+use crate::pac::interrupt;
+
+#[cfg(feature = "rt")]
+#[interrupt]
+fn GROUP0() {
+ use mspm0_metapac::Group0;
+
+ let group = pac::CPUSS.int_group(0);
+
+ // TODO: Decompose to direct u8
+ let iidx = group.iidx().read().stat().to_bits();
+
+ let Ok(group) = pac::Group0::try_from(iidx as u8) else {
+ debug!("Invalid IIDX for group 0: {}", iidx);
+ return;
+ };
+
+ match group {
+ Group0::WWDT0 => todo!("implement WWDT0"),
+ Group0::DEBUGSS => todo!("implement DEBUGSS"),
+ Group0::FLASHCTL => todo!("implement FLASHCTL"),
+ Group0::SYSCTL => todo!("implement SYSCTL"),
+ }
+}
diff --git a/embassy-mspm0/src/int_group/g350x.rs b/embassy-mspm0/src/int_group/g350x.rs
new file mode 100644
index 000000000..706ba2078
--- /dev/null
+++ b/embassy-mspm0/src/int_group/g350x.rs
@@ -0,0 +1,51 @@
+use crate::pac;
+use crate::pac::interrupt;
+
+#[cfg(feature = "rt")]
+#[interrupt]
+fn GROUP0() {
+ use mspm0_metapac::Group0;
+
+ let group = pac::CPUSS.int_group(0);
+
+ // Must subtract by 1 since NO_INTR is value 0
+ let iidx = group.iidx().read().stat().to_bits() - 1;
+
+ let Ok(group) = pac::Group0::try_from(iidx as u8) else {
+ debug!("Invalid IIDX for group 0: {}", iidx);
+ return;
+ };
+
+ match group {
+ Group0::WWDT0 => todo!("implement WWDT0"),
+ Group0::WWDT1 => todo!("implement WWDT1"),
+ Group0::DEBUGSS => todo!("implement DEBUGSS"),
+ Group0::FLASHCTL => todo!("implement FLASHCTL"),
+ Group0::SYSCTL => todo!("implement SYSCTL"),
+ }
+}
+
+#[cfg(feature = "rt")]
+#[interrupt]
+fn GROUP1() {
+ use mspm0_metapac::Group1;
+
+ let group = pac::CPUSS.int_group(1);
+
+ // Must subtract by 1 since NO_INTR is value 0
+ let iidx = group.iidx().read().stat().to_bits() - 1;
+
+ let Ok(group) = pac::Group1::try_from(iidx as u8) else {
+ debug!("Invalid IIDX for group 1: {}", iidx);
+ return;
+ };
+
+ match group {
+ Group1::GPIOA => crate::gpio::gpioa_interrupt(),
+ Group1::GPIOB => crate::gpio::gpiob_interrupt(),
+ Group1::COMP0 => todo!("implement COMP0"),
+ Group1::COMP1 => todo!("implement COMP1"),
+ Group1::COMP2 => todo!("implement COMP2"),
+ Group1::TRNG => todo!("implement TRNG"),
+ }
+}
diff --git a/embassy-mspm0/src/int_group/g351x.rs b/embassy-mspm0/src/int_group/g351x.rs
new file mode 100644
index 000000000..e785018a7
--- /dev/null
+++ b/embassy-mspm0/src/int_group/g351x.rs
@@ -0,0 +1,52 @@
+use crate::pac;
+use crate::pac::interrupt;
+
+#[cfg(feature = "rt")]
+#[interrupt]
+fn GROUP0() {
+ use mspm0_metapac::Group0;
+
+ let group = pac::CPUSS.int_group(0);
+
+ // Must subtract by 1 since NO_INTR is value 0
+ let iidx = group.iidx().read().stat().to_bits() - 1;
+
+ let Ok(group) = pac::Group0::try_from(iidx as u8) else {
+ debug!("Invalid IIDX for group 0: {}", iidx);
+ return;
+ };
+
+ match group {
+ Group0::WWDT0 => todo!("implement WWDT0"),
+ Group0::WWDT1 => todo!("implement WWDT1"),
+ Group0::DEBUGSS => todo!("implement DEBUGSS"),
+ Group0::FLASHCTL => todo!("implement FLASHCTL"),
+ Group0::SYSCTL => todo!("implement SYSCTL"),
+ }
+}
+
+#[cfg(feature = "rt")]
+#[interrupt]
+fn GROUP1() {
+ use mspm0_metapac::Group1;
+
+ let group = pac::CPUSS.int_group(1);
+
+ // Must subtract by 1 since NO_INTR is value 0
+ let iidx = group.iidx().read().stat().to_bits() - 1;
+
+ let Ok(group) = pac::Group1::try_from(iidx as u8) else {
+ debug!("Invalid IIDX for group 1: {}", iidx);
+ return;
+ };
+
+ match group {
+ Group1::GPIOA => crate::gpio::gpioa_interrupt(),
+ Group1::GPIOB => crate::gpio::gpiob_interrupt(),
+ Group1::COMP0 => todo!("implement COMP0"),
+ Group1::COMP1 => todo!("implement COMP1"),
+ Group1::COMP2 => todo!("implement COMP2"),
+ Group1::TRNG => todo!("implement TRNG"),
+ Group1::GPIOC => crate::gpio::gpioc_interrupt(),
+ }
+}
diff --git a/embassy-mspm0/src/int_group/l130x.rs b/embassy-mspm0/src/int_group/l130x.rs
new file mode 100644
index 000000000..8be5adcad
--- /dev/null
+++ b/embassy-mspm0/src/int_group/l130x.rs
@@ -0,0 +1,46 @@
+use crate::pac;
+use crate::pac::interrupt;
+
+#[cfg(feature = "rt")]
+#[interrupt]
+fn GROUP0() {
+ use mspm0_metapac::Group0;
+
+ let group = pac::CPUSS.int_group(0);
+
+ // Must subtract by 1 since NO_INTR is value 0
+ let iidx = group.iidx().read().stat().to_bits() - 1;
+
+ let Ok(group) = pac::Group0::try_from(iidx as u8) else {
+ debug!("Invalid IIDX for group 0: {}", iidx);
+ return;
+ };
+
+ match group {
+ Group0::WWDT0 => todo!("implement WWDT0"),
+ Group0::DEBUGSS => todo!("implement DEBUGSS"),
+ Group0::FLASHCTL => todo!("implement FLASHCTL"),
+ Group0::SYSCTL => todo!("implement SYSCTL"),
+ }
+}
+
+#[cfg(feature = "rt")]
+#[interrupt]
+fn GROUP1() {
+ use mspm0_metapac::Group1;
+
+ let group = pac::CPUSS.int_group(1);
+
+ // Must subtract by 1 since NO_INTR is value 0
+ let iidx = group.iidx().read().stat().to_bits() - 1;
+
+ let Ok(group) = pac::Group1::try_from(iidx as u8) else {
+ debug!("Invalid IIDX for group 1: {}", iidx);
+ return;
+ };
+
+ match group {
+ Group1::GPIOA => crate::gpio::gpioa_interrupt(),
+ Group1::COMP0 => todo!("implement COMP0"),
+ }
+}
diff --git a/embassy-mspm0/src/int_group/l222x.rs b/embassy-mspm0/src/int_group/l222x.rs
new file mode 100644
index 000000000..eeb2ce70d
--- /dev/null
+++ b/embassy-mspm0/src/int_group/l222x.rs
@@ -0,0 +1,49 @@
+use crate::pac;
+use crate::pac::interrupt;
+
+#[cfg(feature = "rt")]
+#[interrupt]
+fn GROUP0() {
+ use mspm0_metapac::Group0;
+
+ let group = pac::CPUSS.int_group(0);
+
+ // Must subtract by 1 since NO_INTR is value 0
+ let iidx = group.iidx().read().stat().to_bits() - 1;
+
+ let Ok(group) = pac::Group0::try_from(iidx as u8) else {
+ debug!("Invalid IIDX for group 0: {}", iidx);
+ return;
+ };
+
+ match group {
+ Group0::WWDT0 => todo!("implement WWDT0"),
+ Group0::DEBUGSS => todo!("implement DEBUGSS"),
+ Group0::FLASHCTL => todo!("implement FLASHCTL"),
+ Group0::SYSCTL => todo!("implement SYSCTL"),
+ }
+}
+
+#[cfg(feature = "rt")]
+#[interrupt]
+fn GROUP1() {
+ use mspm0_metapac::Group1;
+
+ let group = pac::CPUSS.int_group(1);
+
+ // Must subtract by 1 since NO_INTR is value 0
+ let iidx = group.iidx().read().stat().to_bits() - 1;
+
+ let Ok(group) = pac::Group1::try_from(iidx as u8) else {
+ debug!("Invalid IIDX for group 1: {}", iidx);
+ return;
+ };
+
+ match group {
+ Group1::GPIOA => crate::gpio::gpioa_interrupt(),
+ Group1::GPIOB => crate::gpio::gpiob_interrupt(),
+ Group1::COMP0 => todo!("implement COMP0"),
+ Group1::TRNG => todo!("implement TRNG"),
+ Group1::GPIOC => crate::gpio::gpioc_interrupt(),
+ }
+}
diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs
new file mode 100644
index 000000000..1191b1010
--- /dev/null
+++ b/embassy-mspm0/src/lib.rs
@@ -0,0 +1,107 @@
+#![no_std]
+// 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)))]
+
+// This mod MUST go first, so that the others see its macros.
+pub(crate) mod fmt;
+
+pub mod gpio;
+pub mod timer;
+
+#[cfg(feature = "_time-driver")]
+mod time_driver;
+
+// Interrupt group handlers.
+#[cfg_attr(feature = "mspm0c110x", path = "int_group/c110x.rs")]
+#[cfg_attr(feature = "mspm0g110x", path = "int_group/g110x.rs")]
+#[cfg_attr(feature = "mspm0g150x", path = "int_group/g150x.rs")]
+#[cfg_attr(feature = "mspm0g151x", path = "int_group/g151x.rs")]
+#[cfg_attr(feature = "mspm0g310x", path = "int_group/g310x.rs")]
+#[cfg_attr(feature = "mspm0g350x", path = "int_group/g350x.rs")]
+#[cfg_attr(feature = "mspm0g351x", path = "int_group/g351x.rs")]
+#[cfg_attr(feature = "mspm0l110x", path = "int_group/l110x.rs")]
+#[cfg_attr(feature = "mspm0l122x", path = "int_group/l122x.rs")]
+#[cfg_attr(feature = "mspm0l130x", path = "int_group/l130x.rs")]
+#[cfg_attr(feature = "mspm0l134x", path = "int_group/l134x.rs")]
+#[cfg_attr(feature = "mspm0l222x", path = "int_group/l222x.rs")]
+mod int_group;
+
+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::{into_ref, Peripheral, PeripheralRef};
+#[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;
+
+/// `embassy-mspm0` global configuration.
+#[non_exhaustive]
+#[derive(Clone, Copy)]
+pub struct Config {
+ // TODO
+}
+
+impl Default for Config {
+ fn default() -> Self {
+ Self {
+ // TODO
+ }
+ }
+}
+
+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(feature = "mspm0c110x")]
+ unsafe {
+ use crate::_generated::interrupt::typelevel::Interrupt;
+ crate::interrupt::typelevel::GPIOA::enable();
+ }
+
+ #[cfg(feature = "_time-driver")]
+ time_driver::init(cs);
+
+ peripherals
+ })
+}
diff --git a/embassy-mspm0/src/time_driver.rs b/embassy-mspm0/src/time_driver.rs
new file mode 100644
index 000000000..937ce58d4
--- /dev/null
+++ b/embassy-mspm0/src/time_driver.rs
@@ -0,0 +1,423 @@
+use core::cell::{Cell, RefCell};
+use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
+use core::task::Waker;
+
+use critical_section::{CriticalSection, Mutex};
+use embassy_time_driver::Driver;
+use embassy_time_queue_utils::Queue;
+use mspm0_metapac::interrupt;
+use mspm0_metapac::tim::vals::{Cm, Cvae, CxC, EvtCfg, PwrenKey, Ratio, Repeat, ResetKey};
+use mspm0_metapac::tim::{Counterregs16, Tim};
+
+use crate::peripherals;
+use crate::timer::SealedTimer;
+
+// Currently TIMG12 and TIMG13 are excluded because those are 32-bit timers.
+#[cfg(time_driver_timg0)]
+type T = peripherals::TIMG0;
+#[cfg(time_driver_timg1)]
+type T = peripherals::TIMG1;
+#[cfg(time_driver_timg2)]
+type T = peripherals::TIMG2;
+#[cfg(time_driver_timg3)]
+type T = peripherals::TIMG3;
+#[cfg(time_driver_timg4)]
+type T = peripherals::TIMG4;
+#[cfg(time_driver_timg5)]
+type T = peripherals::TIMG5;
+#[cfg(time_driver_timg6)]
+type T = peripherals::TIMG6;
+#[cfg(time_driver_timg7)]
+type T = peripherals::TIMG7;
+#[cfg(time_driver_timg8)]
+type T = peripherals::TIMG8;
+#[cfg(time_driver_timg9)]
+type T = peripherals::TIMG9;
+#[cfg(time_driver_timg10)]
+type T = peripherals::TIMG10;
+#[cfg(time_driver_timg11)]
+type T = peripherals::TIMG11;
+#[cfg(time_driver_timg14)]
+type T = peripherals::TIMG14;
+#[cfg(time_driver_tima0)]
+type T = peripherals::TIMA0;
+#[cfg(time_driver_tima1)]
+type T = peripherals::TIMA1;
+
+// TODO: RTC
+
+fn regs() -> Tim {
+ unsafe { Tim::from_ptr(T::regs()) }
+}
+
+fn regs_counter(tim: Tim) -> Counterregs16 {
+ unsafe { Counterregs16::from_ptr(tim.counterregs(0).as_ptr()) }
+}
+
+/// Clock timekeeping works with something we call "periods", which are time intervals
+/// of 2^15 ticks. The Clock counter value is 16 bits, so one "overflow cycle" is 2 periods.
+fn calc_now(period: u32, counter: u16) -> u64 {
+ ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64)
+}
+
+/// The TIMx driver uses one of the `TIMG` or `TIMA` timer instances to implement a timer with a 32.768 kHz
+/// tick rate. (TODO: Allow setting the tick rate)
+///
+/// This driver defines a period to be 2^15 ticks. 16-bit timers of course count to 2^16 ticks.
+///
+/// To generate a period every 2^15 ticks, the CC0 value is set to 2^15 and the load value set to 2^16.
+/// Incrementing the period on a CCU0 and load results in the a period of 2^15 ticks.
+///
+/// For a specific timestamp, load the lower 16 bits into the CC1 value. When the period where the timestamp
+/// should be enabled is reached, then the CCU1 (CC1 up) interrupt runs to actually wake the timer.
+///
+/// TODO: Compensate for per part variance. This can supposedly be done with the FCC system.
+/// TODO: Allow using 32-bit timers (TIMG12 and TIMG13).
+struct TimxDriver {
+ /// Number of 2^15 periods elapsed since boot.
+ period: AtomicU32,
+ /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
+ alarm: Mutex| >,
+ queue: Mutex>,
+}
+
+impl TimxDriver {
+ #[inline(never)]
+ fn init(&'static self, _cs: CriticalSection) {
+ // Clock config
+ // TODO: Configurable tick rate up to 4 MHz (32 kHz for now)
+ let regs = regs();
+
+ // Reset timer
+ regs.gprcm(0).rstctl().write(|w| {
+ w.set_resetassert(true);
+ w.set_key(ResetKey::KEY);
+ w.set_resetstkyclr(true);
+ });
+
+ // Power up timer
+ regs.gprcm(0).pwren().write(|w| {
+ w.set_enable(true);
+ w.set_key(PwrenKey::KEY);
+ });
+
+ // Following the instructions according to SLAU847D 23.2.1: TIMCLK Configuration
+
+ // 1. Select TIMCLK source
+ regs.clksel().modify(|w| {
+ // Use LFCLK for a 32.768kHz tick rate
+ w.set_lfclk_sel(true);
+ // TODO: Allow MFCLK for configurable tick rate up to 4 MHz
+ // w.set_mfclk_sel(ClkSel::ENABLE);
+ });
+
+ // 2. Divide by TIMCLK, we don't need to divide further for the 32kHz tick rate
+ regs.clkdiv().modify(|w| {
+ w.set_ratio(Ratio::DIV_BY_1);
+ });
+
+ // 3. To be generic across timer instances, we do not use the prescaler.
+ // TODO: mspm0-sdk always sets this, regardless of timer width?
+ regs.commonregs(0).cps().modify(|w| {
+ w.set_pcnt(0);
+ });
+
+ regs.pdbgctl().modify(|w| {
+ w.set_free(true);
+ });
+
+ // 4. Enable the TIMCLK.
+ regs.commonregs(0).cclkctl().modify(|w| {
+ w.set_clken(true);
+ });
+
+ regs.counterregs(0).ctrctl().modify(|w| {
+ // allow counting during debug
+ w.set_repeat(Repeat::REPEAT_3);
+ w.set_cvae(Cvae::ZEROVAL);
+ w.set_cm(Cm::UP);
+
+ // Must explicitly set CZC, CAC and CLC to 0 in order for all the timers to count.
+ //
+ // The reset value of these registers is 0x07, which is a reserved value.
+ //
+ // Looking at a bit representation of the reset value, this appears to be an AND
+ // of 2-input QEI mode and CCCTL_3 ACOND. Given that TIMG14 and TIMA0 have no QEI
+ // and 4 capture and compare channels, this works by accident for those timer units.
+ w.set_czc(CxC::CCTL0);
+ w.set_cac(CxC::CCTL0);
+ w.set_clc(CxC::CCTL0);
+ });
+
+ // Setup the period
+ let ctr = regs_counter(regs);
+
+ // Middle
+ ctr.cc(0).modify(|w| {
+ w.set_ccval(0x7FFF);
+ });
+
+ ctr.load().modify(|w| {
+ w.set_ld(u16::MAX);
+ });
+
+ // Enable the period interrupts
+ //
+ // This does not appear to ever be set for CPU_INT in the TI SDK and is not technically needed.
+ regs.evt_mode().modify(|w| {
+ w.set_evt_cfg(0, EvtCfg::SOFTWARE);
+ });
+
+ regs.int_event(0).imask().modify(|w| {
+ w.set_l(true);
+ w.set_ccu0(true);
+ });
+
+ unsafe { T::enable_interrupt() };
+
+ // Allow the counter to start counting.
+ regs.counterregs(0).ctrctl().modify(|w| {
+ w.set_en(true);
+ });
+ }
+
+ #[inline(never)]
+ fn next_period(&self) {
+ let r = regs();
+
+ // We only modify the period from the timer interrupt, so we know this can't race.
+ let period = self.period.load(Ordering::Relaxed) + 1;
+ self.period.store(period, Ordering::Relaxed);
+ let t = (period as u64) << 15;
+
+ critical_section::with(move |cs| {
+ r.int_event(0).imask().modify(move |w| {
+ let alarm = self.alarm.borrow(cs);
+ let at = alarm.get();
+
+ if at < t + 0xC000 {
+ // just enable it. `set_alarm` has already set the correct CC1 val.
+ w.set_ccu1(true);
+ }
+ })
+ });
+ }
+
+ #[inline(never)]
+ fn on_interrupt(&self) {
+ let r = regs();
+
+ critical_section::with(|cs| {
+ let mis = r.int_event(0).mis().read();
+
+ // Advance to next period if overflowed
+ if mis.l() {
+ self.next_period();
+
+ r.int_event(0).iclr().write(|w| {
+ w.set_l(true);
+ });
+ }
+
+ if mis.ccu0() {
+ self.next_period();
+
+ r.int_event(0).iclr().write(|w| {
+ w.set_ccu0(true);
+ });
+ }
+
+ if mis.ccu1() {
+ r.int_event(0).iclr().write(|w| {
+ w.set_ccu1(true);
+ });
+
+ self.trigger_alarm(cs);
+ }
+ });
+ }
+
+ fn trigger_alarm(&self, cs: CriticalSection) {
+ let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
+
+ while !self.set_alarm(cs, next) {
+ next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
+ }
+ }
+
+ fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
+ let r = regs();
+ let ctr = regs_counter(r);
+
+ self.alarm.borrow(cs).set(timestamp);
+
+ let t = self.now();
+
+ if timestamp <= t {
+ // If alarm timestamp has passed the alarm will not fire.
+ // Disarm the alarm and return `false` to indicate that.
+ r.int_event(0).imask().modify(|w| w.set_ccu1(false));
+
+ self.alarm.borrow(cs).set(u64::MAX);
+
+ return false;
+ }
+
+ // Write the CC1 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.
+ ctr.cc(1).write(|w| {
+ w.set_ccval(timestamp as u16);
+ });
+
+ // Enable it if it'll happen soon. Otherwise, `next_period` will enable it.
+ let diff = timestamp - t;
+ r.int_event(0).imask().modify(|w| w.set_ccu1(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.int_event(0).imask().modify(|w| w.set_ccu1(false));
+
+ self.alarm.borrow(cs).set(u64::MAX);
+
+ return false;
+ }
+
+ // We're confident the alarm will ring in the future.
+ true
+ }
+}
+
+impl Driver for TimxDriver {
+ fn now(&self) -> u64 {
+ let regs = regs();
+
+ let period = self.period.load(Ordering::Relaxed);
+ // Ensure the compiler does not read the counter before the period.
+ compiler_fence(Ordering::Acquire);
+
+ let counter = regs_counter(regs).ctr().read().cctr() as u16;
+
+ calc_now(period, counter)
+ }
+
+ fn schedule_wake(&self, at: u64, waker: &Waker) {
+ critical_section::with(|cs| {
+ let mut queue = self.queue.borrow(cs).borrow_mut();
+
+ if queue.schedule_wake(at, waker) {
+ let mut next = queue.next_expiration(self.now());
+
+ while !self.set_alarm(cs, next) {
+ next = queue.next_expiration(self.now());
+ }
+ }
+ });
+ }
+}
+
+embassy_time_driver::time_driver_impl!(static DRIVER: TimxDriver = TimxDriver {
+ period: AtomicU32::new(0),
+ alarm: Mutex::new(Cell::new(u64::MAX)),
+ queue: Mutex::new(RefCell::new(Queue::new()))
+});
+
+pub(crate) fn init(cs: CriticalSection) {
+ DRIVER.init(cs);
+}
+
+#[cfg(time_driver_timg0)]
+#[interrupt]
+fn TIMG0() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg1)]
+#[interrupt]
+fn TIMG1() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg2)]
+#[interrupt]
+fn TIMG2() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg3)]
+#[interrupt]
+fn TIMG3() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg4)]
+#[interrupt]
+fn TIMG4() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg5)]
+#[interrupt]
+fn TIMG5() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg6)]
+#[interrupt]
+fn TIMG6() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg7)]
+#[interrupt]
+fn TIMG7() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg8)]
+#[interrupt]
+fn TIMG8() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg9)]
+#[interrupt]
+fn TIMG9() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg10)]
+#[interrupt]
+fn TIMG10() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg11)]
+#[interrupt]
+fn TIMG11() {
+ DRIVER.on_interrupt();
+}
+
+// TODO: TIMG12 and TIMG13
+
+#[cfg(time_driver_timg14)]
+#[interrupt]
+fn TIMG14() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_tima0)]
+#[interrupt]
+fn TIMA0() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_tima1)]
+#[interrupt]
+fn TIMA1() {
+ DRIVER.on_interrupt();
+}
diff --git a/embassy-mspm0/src/timer.rs b/embassy-mspm0/src/timer.rs
new file mode 100644
index 000000000..4441e5640
--- /dev/null
+++ b/embassy-mspm0/src/timer.rs
@@ -0,0 +1,48 @@
+#![macro_use]
+
+/// Amount of bits of a timer.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum TimerBits {
+ /// 16 bits.
+ Bits16,
+ /// 32 bits.
+ Bits32,
+}
+
+#[allow(private_bounds)]
+pub trait Timer: SealedTimer + 'static {
+ /// Amount of bits this timer has.
+ const BITS: TimerBits;
+}
+
+pub(crate) trait SealedTimer {
+ /// Registers for this timer.
+ ///
+ /// This is a raw pointer to the register block. The actual register block layout varies depending on the
+ /// timer type.
+ fn regs() -> *mut ();
+
+ /// Enable the interrupt corresponding to this timer.
+ unsafe fn enable_interrupt();
+}
+
+macro_rules! impl_timer {
+ ($name: ident, $bits: ident) => {
+ impl crate::timer::SealedTimer for crate::peripherals::$name {
+ fn regs() -> *mut () {
+ crate::pac::$name.as_ptr()
+ }
+
+ unsafe fn enable_interrupt() {
+ use embassy_hal_internal::interrupt::InterruptExt;
+ crate::interrupt::$name.unpend();
+ crate::interrupt::$name.enable();
+ }
+ }
+
+ impl crate::timer::Timer for crate::peripherals::$name {
+ const BITS: crate::timer::TimerBits = crate::timer::TimerBits::$bits;
+ }
+ };
+}
diff --git a/examples/mspm0c1104/.cargo/config.toml b/examples/mspm0c1104/.cargo/config.toml
new file mode 100644
index 000000000..204a56b1c
--- /dev/null
+++ b/examples/mspm0c1104/.cargo/config.toml
@@ -0,0 +1,11 @@
+[target.'cfg(all(target_arch = "arm", target_os = "none"))']
+# replace MSPM0C1104 with your chip as listed in `probe-rs chip list`
+runner = "probe-rs run --chip MSPM0C1104 --protocol=swd"
+
+[build]
+target = "thumbv6m-none-eabi"
+
+[env]
+DEFMT_LOG = "debug"
+# defmt's buffer needs to be shrunk since the MSPM0C1104 only has 1KB of ram.
+DEFMT_RTT_BUFFER_SIZE = "72"
diff --git a/examples/mspm0c1104/Cargo.toml b/examples/mspm0c1104/Cargo.toml
new file mode 100644
index 000000000..3996939a5
--- /dev/null
+++ b/examples/mspm0c1104/Cargo.toml
@@ -0,0 +1,32 @@
+[package]
+edition = "2021"
+name = "embassy-mspm0-c1104-examples"
+version = "0.1.0"
+license = "MIT OR Apache-2.0"
+
+[dependencies]
+embassy-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = ["mspm0c110x", "rt", "time-driver-any"] }
+embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["task-arena-size-128", "arch-cortex-m", "executor-thread", "executor-interrupt"] }
+embassy-sync = { version = "0.6.2", path = "../../embassy-sync", features = ["defmt"] }
+embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt"] }
+panic-halt = "0.2.0"
+cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
+cortex-m-rt = { version = "0.7.0"}
+defmt = "0.3"
+defmt-rtt = "0.4"
+panic-probe = { version = "0.3.2", features = ["print-defmt"] }
+panic-semihosting = "0.6.0"
+
+# The chip only has 1KB of ram, so we must optimize binaries regardless
+[profile.dev]
+debug = 0
+opt-level = "z"
+lto = true
+codegen-units = 1
+# strip = true
+
+[profile.release]
+debug = 0
+opt-level = "z"
+lto = true
+codegen-units = 1
diff --git a/examples/mspm0c1104/README.md b/examples/mspm0c1104/README.md
new file mode 100644
index 000000000..e5c9f961d
--- /dev/null
+++ b/examples/mspm0c1104/README.md
@@ -0,0 +1,27 @@
+# Examples for MSPM0C110x family
+
+Run individual examples with
+```
+cargo run --bin
+```
+for example
+```
+cargo run --bin blinky
+```
+
+## Checklist before running examples
+A large number of the examples are written for the [LP-MSPM0C1104](https://www.ti.com/tool/LP-MSPM0C1104) board.
+
+You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.
+
+* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for C1104 it should be `probe-rs run --chip MSPM0C1104`. (use `probe-rs chip list` to find your chip)
+* [ ] Update Cargo.toml to have the correct `embassy-mspm0` feature. For example for C1104 it should be `mspm0c1104`. Look in the `Cargo.toml` file of the `embassy-mspm0` project to find the correct feature flag for your chip.
+* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.
+* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic
+
+If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
+
+* Which example you are trying to run
+* Which chip and board you are using
+
+Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
diff --git a/examples/mspm0c1104/build.rs b/examples/mspm0c1104/build.rs
new file mode 100644
index 000000000..30691aa97
--- /dev/null
+++ b/examples/mspm0c1104/build.rs
@@ -0,0 +1,35 @@
+//! This build script copies the `memory.x` file from the crate root into
+//! a directory where the linker can always find it at build time.
+//! For many projects this is optional, as the linker always searches the
+//! project root directory -- wherever `Cargo.toml` is. However, if you
+//! are using a workspace or have a more complicated build setup, this
+//! build script becomes required. Additionally, by requesting that
+//! Cargo re-run the build script whenever `memory.x` is changed,
+//! updating `memory.x` ensures a rebuild of the application with the
+//! new memory settings.
+
+use std::env;
+use std::fs::File;
+use std::io::Write;
+use std::path::PathBuf;
+
+fn main() {
+ // Put `memory.x` in our output directory and ensure it's
+ // on the linker search path.
+ let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
+ File::create(out.join("memory.x"))
+ .unwrap()
+ .write_all(include_bytes!("memory.x"))
+ .unwrap();
+ println!("cargo:rustc-link-search={}", out.display());
+
+ // By default, Cargo will re-run a build script whenever
+ // any file in the project changes. By specifying `memory.x`
+ // here, we ensure the build script is only re-run when
+ // `memory.x` is changed.
+ println!("cargo:rerun-if-changed=memory.x");
+
+ println!("cargo:rustc-link-arg-bins=--nmagic");
+ println!("cargo:rustc-link-arg-bins=-Tlink.x");
+ println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
+}
diff --git a/examples/mspm0c1104/memory.x b/examples/mspm0c1104/memory.x
new file mode 100644
index 000000000..a9108835a
--- /dev/null
+++ b/examples/mspm0c1104/memory.x
@@ -0,0 +1,5 @@
+MEMORY
+{
+ FLASH : ORIGIN = 0x00000000, LENGTH = 16K
+ RAM : ORIGIN = 0x20000000, LENGTH = 1K
+}
diff --git a/examples/mspm0c1104/src/bin/blinky.rs b/examples/mspm0c1104/src/bin/blinky.rs
new file mode 100644
index 000000000..0d974cc5e
--- /dev/null
+++ b/examples/mspm0c1104/src/bin/blinky.rs
@@ -0,0 +1,25 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_mspm0::gpio::{Level, Output};
+use embassy_mspm0::Config;
+use embassy_time::Timer;
+use {defmt_rtt as _, panic_halt as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) -> ! {
+ info!("Hello world!");
+ let p = embassy_mspm0::init(Config::default());
+
+ let mut led1 = Output::new(p.PA22, Level::Low);
+ led1.set_inversion(true);
+
+ loop {
+ Timer::after_millis(400).await;
+
+ info!("Toggle");
+ led1.toggle();
+ }
+}
diff --git a/examples/mspm0c1104/src/bin/button.rs b/examples/mspm0c1104/src/bin/button.rs
new file mode 100644
index 000000000..7face1618
--- /dev/null
+++ b/examples/mspm0c1104/src/bin/button.rs
@@ -0,0 +1,33 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_mspm0::gpio::{Input, Level, Output, Pull};
+use embassy_mspm0::Config;
+use {defmt_rtt as _, panic_halt as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) -> ! {
+ info!("Hello world!");
+
+ let p = embassy_mspm0::init(Config::default());
+
+ let led1 = p.PA22;
+ let s2 = p.PA16;
+
+ let mut led1 = Output::new(led1, Level::Low);
+
+ let mut s2 = Input::new(s2, Pull::Up);
+
+ // led1 is active low
+ led1.set_high();
+
+ loop {
+ s2.wait_for_falling_edge().await;
+
+ info!("Switch 2 was pressed");
+
+ led1.toggle();
+ }
+}
diff --git a/examples/mspm0g3507/.cargo/config.toml b/examples/mspm0g3507/.cargo/config.toml
new file mode 100644
index 000000000..34c720cdd
--- /dev/null
+++ b/examples/mspm0g3507/.cargo/config.toml
@@ -0,0 +1,9 @@
+[target.'cfg(all(target_arch = "arm", target_os = "none"))']
+# replace MSPM0G3507 with your chip as listed in `probe-rs chip list`
+runner = "probe-rs run --chip MSPM0G3507 --protocol=swd"
+
+[build]
+target = "thumbv6m-none-eabi"
+
+[env]
+DEFMT_LOG = "debug"
diff --git a/examples/mspm0g3507/Cargo.toml b/examples/mspm0g3507/Cargo.toml
new file mode 100644
index 000000000..c1f304174
--- /dev/null
+++ b/examples/mspm0g3507/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+edition = "2021"
+name = "embassy-mspm0-g3507-examples"
+version = "0.1.0"
+license = "MIT OR Apache-2.0"
+
+[dependencies]
+embassy-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = ["mspm0g350x", "rt", "time-driver-any"] }
+embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "executor-interrupt"] }
+embassy-sync = { version = "0.6.2", path = "../../embassy-sync", features = ["defmt"] }
+embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt"] }
+panic-halt = "0.2.0"
+cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
+cortex-m-rt = { version = "0.7.0"}
+defmt = "0.3"
+defmt-rtt = "0.4"
+panic-probe = { version = "0.3.2", features = ["print-defmt"] }
+panic-semihosting = "0.6.0"
+
+[profile.release]
+debug = 2
diff --git a/examples/mspm0g3507/README.md b/examples/mspm0g3507/README.md
new file mode 100644
index 000000000..5e8a83212
--- /dev/null
+++ b/examples/mspm0g3507/README.md
@@ -0,0 +1,27 @@
+# Examples for MSPM0C350x family
+
+Run individual examples with
+```
+cargo run --bin
+```
+for example
+```
+cargo run --bin blinky
+```
+
+## Checklist before running examples
+A large number of the examples are written for the [LP-MSPM0G3507](https://www.ti.com/tool/LP-MSPM0G3507) board.
+
+You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.
+
+* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for G3507 it should be `probe-rs run --chip MSPM0G3507`. (use `probe-rs chip list` to find your chip)
+* [ ] Update Cargo.toml to have the correct `embassy-mspm0` feature. For example for G3507 it should be `mspm0g3507`. Look in the `Cargo.toml` file of the `embassy-mspm0` project to find the correct feature flag for your chip.
+* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.
+* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic
+
+If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
+
+* Which example you are trying to run
+* Which chip and board you are using
+
+Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
diff --git a/examples/mspm0g3507/build.rs b/examples/mspm0g3507/build.rs
new file mode 100644
index 000000000..30691aa97
--- /dev/null
+++ b/examples/mspm0g3507/build.rs
@@ -0,0 +1,35 @@
+//! This build script copies the `memory.x` file from the crate root into
+//! a directory where the linker can always find it at build time.
+//! For many projects this is optional, as the linker always searches the
+//! project root directory -- wherever `Cargo.toml` is. However, if you
+//! are using a workspace or have a more complicated build setup, this
+//! build script becomes required. Additionally, by requesting that
+//! Cargo re-run the build script whenever `memory.x` is changed,
+//! updating `memory.x` ensures a rebuild of the application with the
+//! new memory settings.
+
+use std::env;
+use std::fs::File;
+use std::io::Write;
+use std::path::PathBuf;
+
+fn main() {
+ // Put `memory.x` in our output directory and ensure it's
+ // on the linker search path.
+ let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
+ File::create(out.join("memory.x"))
+ .unwrap()
+ .write_all(include_bytes!("memory.x"))
+ .unwrap();
+ println!("cargo:rustc-link-search={}", out.display());
+
+ // By default, Cargo will re-run a build script whenever
+ // any file in the project changes. By specifying `memory.x`
+ // here, we ensure the build script is only re-run when
+ // `memory.x` is changed.
+ println!("cargo:rerun-if-changed=memory.x");
+
+ println!("cargo:rustc-link-arg-bins=--nmagic");
+ println!("cargo:rustc-link-arg-bins=-Tlink.x");
+ println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
+}
diff --git a/examples/mspm0g3507/memory.x b/examples/mspm0g3507/memory.x
new file mode 100644
index 000000000..37e381fbd
--- /dev/null
+++ b/examples/mspm0g3507/memory.x
@@ -0,0 +1,6 @@
+MEMORY
+{
+ FLASH : ORIGIN = 0x00000000, LENGTH = 128K
+ /* Select non-parity range of SRAM due to SRAM_ERR_01 errata in SLAZ758 */
+ RAM : ORIGIN = 0x20200000, LENGTH = 32K
+}
diff --git a/examples/mspm0g3507/src/bin/blinky.rs b/examples/mspm0g3507/src/bin/blinky.rs
new file mode 100644
index 000000000..055a5cd81
--- /dev/null
+++ b/examples/mspm0g3507/src/bin/blinky.rs
@@ -0,0 +1,25 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_mspm0::gpio::{Level, Output};
+use embassy_mspm0::Config;
+use embassy_time::Timer;
+use {defmt_rtt as _, panic_halt as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) -> ! {
+ info!("Hello world!");
+ let p = embassy_mspm0::init(Config::default());
+
+ let mut led1 = Output::new(p.PA0, Level::Low);
+ led1.set_inversion(true);
+
+ loop {
+ Timer::after_millis(400).await;
+
+ info!("Toggle");
+ led1.toggle();
+ }
+}
diff --git a/examples/mspm0g3507/src/bin/button.rs b/examples/mspm0g3507/src/bin/button.rs
new file mode 100644
index 000000000..cde1f2892
--- /dev/null
+++ b/examples/mspm0g3507/src/bin/button.rs
@@ -0,0 +1,33 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_mspm0::gpio::{Input, Level, Output, Pull};
+use embassy_mspm0::Config;
+use {defmt_rtt as _, panic_halt as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) -> ! {
+ info!("Hello world!");
+
+ let p = embassy_mspm0::init(Config::default());
+
+ let led1 = p.PA0;
+ let s2 = p.PB21;
+
+ let mut led1 = Output::new(led1, Level::Low);
+
+ let mut s2 = Input::new(s2, Pull::Up);
+
+ // led1 is active low
+ led1.set_high();
+
+ loop {
+ s2.wait_for_falling_edge().await;
+
+ info!("Switch 2 was pressed");
+
+ led1.toggle();
+ }
+}
diff --git a/examples/mspm0g3519/.cargo/config.toml b/examples/mspm0g3519/.cargo/config.toml
new file mode 100644
index 000000000..7bba4646f
--- /dev/null
+++ b/examples/mspm0g3519/.cargo/config.toml
@@ -0,0 +1,9 @@
+[target.'cfg(all(target_arch = "arm", target_os = "none"))']
+# replace MSPM0G3519 with your chip as listed in `probe-rs chip list`
+runner = "probe-rs run --restore-unwritten --verify --chip MSPM0G3519 --protocol=swd"
+
+[build]
+target = "thumbv6m-none-eabi"
+
+[env]
+DEFMT_LOG = "trace"
diff --git a/examples/mspm0g3519/Cargo.toml b/examples/mspm0g3519/Cargo.toml
new file mode 100644
index 000000000..fc6f0e31b
--- /dev/null
+++ b/examples/mspm0g3519/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+edition = "2021"
+name = "embassy-mspm0-g3519-examples"
+version = "0.1.0"
+license = "MIT OR Apache-2.0"
+
+[dependencies]
+embassy-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = ["mspm0g351x", "rt", "time-driver-any"] }
+embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "executor-interrupt"] }
+embassy-sync = { version = "0.6.2", path = "../../embassy-sync", features = ["defmt"] }
+embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt"] }
+panic-halt = "0.2.0"
+cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
+cortex-m-rt = { version = "0.7.0"}
+defmt = "0.3"
+defmt-rtt = "0.4"
+panic-probe = { version = "0.3.2", features = ["print-defmt"] }
+panic-semihosting = "0.6.0"
+
+[profile.release]
+debug = 2
diff --git a/examples/mspm0g3519/README.md b/examples/mspm0g3519/README.md
new file mode 100644
index 000000000..5034b1913
--- /dev/null
+++ b/examples/mspm0g3519/README.md
@@ -0,0 +1,27 @@
+# Examples for MSPM0G351x family
+
+Run individual examples with
+```
+cargo run --bin
+```
+for example
+```
+cargo run --bin blinky
+```
+
+## Checklist before running examples
+A large number of the examples are written for the [LP-MSPM0G3519](https://www.ti.com/tool/LP-MSPM0G3519) board.
+
+You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.
+
+* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for G3519 it should be `probe-rs run --chip MSPM0G3519`. (use `probe-rs chip list` to find your chip)
+* [ ] Update Cargo.toml to have the correct `embassy-mspm0` feature. For example for G3519 it should be `mspm0g3519`. Look in the `Cargo.toml` file of the `embassy-mspm0` project to find the correct feature flag for your chip.
+* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.
+* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic
+
+If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
+
+* Which example you are trying to run
+* Which chip and board you are using
+
+Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
diff --git a/examples/mspm0g3519/build.rs b/examples/mspm0g3519/build.rs
new file mode 100644
index 000000000..30691aa97
--- /dev/null
+++ b/examples/mspm0g3519/build.rs
@@ -0,0 +1,35 @@
+//! This build script copies the `memory.x` file from the crate root into
+//! a directory where the linker can always find it at build time.
+//! For many projects this is optional, as the linker always searches the
+//! project root directory -- wherever `Cargo.toml` is. However, if you
+//! are using a workspace or have a more complicated build setup, this
+//! build script becomes required. Additionally, by requesting that
+//! Cargo re-run the build script whenever `memory.x` is changed,
+//! updating `memory.x` ensures a rebuild of the application with the
+//! new memory settings.
+
+use std::env;
+use std::fs::File;
+use std::io::Write;
+use std::path::PathBuf;
+
+fn main() {
+ // Put `memory.x` in our output directory and ensure it's
+ // on the linker search path.
+ let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
+ File::create(out.join("memory.x"))
+ .unwrap()
+ .write_all(include_bytes!("memory.x"))
+ .unwrap();
+ println!("cargo:rustc-link-search={}", out.display());
+
+ // By default, Cargo will re-run a build script whenever
+ // any file in the project changes. By specifying `memory.x`
+ // here, we ensure the build script is only re-run when
+ // `memory.x` is changed.
+ println!("cargo:rerun-if-changed=memory.x");
+
+ println!("cargo:rustc-link-arg-bins=--nmagic");
+ println!("cargo:rustc-link-arg-bins=-Tlink.x");
+ println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
+}
diff --git a/examples/mspm0g3519/memory.x b/examples/mspm0g3519/memory.x
new file mode 100644
index 000000000..e6e0ec9e9
--- /dev/null
+++ b/examples/mspm0g3519/memory.x
@@ -0,0 +1,6 @@
+MEMORY
+{
+ FLASH : ORIGIN = 0x00000000, LENGTH = 512K
+ /* Select non-parity range of SRAM due to SRAM_ERR_01 errata in SLAZ758 */
+ RAM : ORIGIN = 0x20200000, LENGTH = 128K
+}
diff --git a/examples/mspm0g3519/src/bin/blinky.rs b/examples/mspm0g3519/src/bin/blinky.rs
new file mode 100644
index 000000000..055a5cd81
--- /dev/null
+++ b/examples/mspm0g3519/src/bin/blinky.rs
@@ -0,0 +1,25 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_mspm0::gpio::{Level, Output};
+use embassy_mspm0::Config;
+use embassy_time::Timer;
+use {defmt_rtt as _, panic_halt as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) -> ! {
+ info!("Hello world!");
+ let p = embassy_mspm0::init(Config::default());
+
+ let mut led1 = Output::new(p.PA0, Level::Low);
+ led1.set_inversion(true);
+
+ loop {
+ Timer::after_millis(400).await;
+
+ info!("Toggle");
+ led1.toggle();
+ }
+}
diff --git a/examples/mspm0g3519/src/bin/button.rs b/examples/mspm0g3519/src/bin/button.rs
new file mode 100644
index 000000000..c81cc2918
--- /dev/null
+++ b/examples/mspm0g3519/src/bin/button.rs
@@ -0,0 +1,33 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_mspm0::gpio::{Input, Level, Output, Pull};
+use embassy_mspm0::Config;
+use {defmt_rtt as _, panic_halt as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) -> ! {
+ info!("Hello world!");
+
+ let p = embassy_mspm0::init(Config::default());
+
+ let led1 = p.PA0;
+ let s2 = p.PB3;
+
+ let mut led1 = Output::new(led1, Level::Low);
+
+ let mut s2 = Input::new(s2, Pull::Up);
+
+ // led1 is active low
+ led1.set_high();
+
+ loop {
+ s2.wait_for_falling_edge().await;
+
+ info!("Switch 2 was pressed");
+
+ led1.toggle();
+ }
+}
diff --git a/examples/mspm0l1306/.cargo/config.toml b/examples/mspm0l1306/.cargo/config.toml
new file mode 100644
index 000000000..93f148a71
--- /dev/null
+++ b/examples/mspm0l1306/.cargo/config.toml
@@ -0,0 +1,9 @@
+[target.'cfg(all(target_arch = "arm", target_os = "none"))']
+# replace MSPM0L1306 with your chip as listed in `probe-rs chip list`
+runner = "probe-rs run --chip MSPM0L1306 --protocol=swd"
+
+[build]
+target = "thumbv6m-none-eabi"
+
+[env]
+DEFMT_LOG = "trace"
diff --git a/examples/mspm0l1306/Cargo.toml b/examples/mspm0l1306/Cargo.toml
new file mode 100644
index 000000000..6b87916b8
--- /dev/null
+++ b/examples/mspm0l1306/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+edition = "2021"
+name = "embassy-mspm0-l1306-examples"
+version = "0.1.0"
+license = "MIT OR Apache-2.0"
+
+[dependencies]
+embassy-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = ["mspm0l130x", "rt", "time-driver-any"] }
+embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["task-arena-size-1024", "arch-cortex-m", "executor-thread", "executor-interrupt"] }
+embassy-sync = { version = "0.6.2", path = "../../embassy-sync", features = ["defmt"] }
+embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt"] }
+panic-halt = "0.2.0"
+cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
+cortex-m-rt = { version = "0.7.0"}
+defmt = "0.3"
+defmt-rtt = "0.4"
+panic-probe = { version = "0.3.2", features = ["print-defmt"] }
+panic-semihosting = "0.6.0"
+
+[profile.release]
+debug = 2
diff --git a/examples/mspm0l1306/README.md b/examples/mspm0l1306/README.md
new file mode 100644
index 000000000..5a55d721e
--- /dev/null
+++ b/examples/mspm0l1306/README.md
@@ -0,0 +1,27 @@
+# Examples for MSPM0L130x family
+
+Run individual examples with
+```
+cargo run --bin
+```
+for example
+```
+cargo run --bin blinky
+```
+
+## Checklist before running examples
+A large number of the examples are written for the [LP-MSPM0L1306](https://www.ti.com/tool/LP-MSPM0L1306) board.
+
+You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.
+
+* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L1306 it should be `probe-rs run --chip MSPM0L1306`. (use `probe-rs chip list` to find your chip)
+* [ ] Update Cargo.toml to have the correct `embassy-mspm0` feature. For example for L1306 it should be `mspm0l1306`. Look in the `Cargo.toml` file of the `embassy-mspm0` project to find the correct feature flag for your chip.
+* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.
+* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic
+
+If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
+
+* Which example you are trying to run
+* Which chip and board you are using
+
+Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
diff --git a/examples/mspm0l1306/build.rs b/examples/mspm0l1306/build.rs
new file mode 100644
index 000000000..30691aa97
--- /dev/null
+++ b/examples/mspm0l1306/build.rs
@@ -0,0 +1,35 @@
+//! This build script copies the `memory.x` file from the crate root into
+//! a directory where the linker can always find it at build time.
+//! For many projects this is optional, as the linker always searches the
+//! project root directory -- wherever `Cargo.toml` is. However, if you
+//! are using a workspace or have a more complicated build setup, this
+//! build script becomes required. Additionally, by requesting that
+//! Cargo re-run the build script whenever `memory.x` is changed,
+//! updating `memory.x` ensures a rebuild of the application with the
+//! new memory settings.
+
+use std::env;
+use std::fs::File;
+use std::io::Write;
+use std::path::PathBuf;
+
+fn main() {
+ // Put `memory.x` in our output directory and ensure it's
+ // on the linker search path.
+ let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
+ File::create(out.join("memory.x"))
+ .unwrap()
+ .write_all(include_bytes!("memory.x"))
+ .unwrap();
+ println!("cargo:rustc-link-search={}", out.display());
+
+ // By default, Cargo will re-run a build script whenever
+ // any file in the project changes. By specifying `memory.x`
+ // here, we ensure the build script is only re-run when
+ // `memory.x` is changed.
+ println!("cargo:rerun-if-changed=memory.x");
+
+ println!("cargo:rustc-link-arg-bins=--nmagic");
+ println!("cargo:rustc-link-arg-bins=-Tlink.x");
+ println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
+}
diff --git a/examples/mspm0l1306/memory.x b/examples/mspm0l1306/memory.x
new file mode 100644
index 000000000..d93b61f44
--- /dev/null
+++ b/examples/mspm0l1306/memory.x
@@ -0,0 +1,5 @@
+MEMORY
+{
+ FLASH : ORIGIN = 0x00000000, LENGTH = 64K
+ RAM : ORIGIN = 0x20000000, LENGTH = 4K
+}
diff --git a/examples/mspm0l1306/src/bin/blinky.rs b/examples/mspm0l1306/src/bin/blinky.rs
new file mode 100644
index 000000000..055a5cd81
--- /dev/null
+++ b/examples/mspm0l1306/src/bin/blinky.rs
@@ -0,0 +1,25 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_mspm0::gpio::{Level, Output};
+use embassy_mspm0::Config;
+use embassy_time::Timer;
+use {defmt_rtt as _, panic_halt as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) -> ! {
+ info!("Hello world!");
+ let p = embassy_mspm0::init(Config::default());
+
+ let mut led1 = Output::new(p.PA0, Level::Low);
+ led1.set_inversion(true);
+
+ loop {
+ Timer::after_millis(400).await;
+
+ info!("Toggle");
+ led1.toggle();
+ }
+}
diff --git a/examples/mspm0l1306/src/bin/button.rs b/examples/mspm0l1306/src/bin/button.rs
new file mode 100644
index 000000000..d8c85947f
--- /dev/null
+++ b/examples/mspm0l1306/src/bin/button.rs
@@ -0,0 +1,33 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_mspm0::gpio::{Input, Level, Output, Pull};
+use embassy_mspm0::Config;
+use {defmt_rtt as _, panic_halt as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) -> ! {
+ info!("Hello world!");
+
+ let p = embassy_mspm0::init(Config::default());
+
+ let led1 = p.PA0;
+ let s2 = p.PA14;
+
+ let mut led1 = Output::new(led1, Level::Low);
+
+ let mut s2 = Input::new(s2, Pull::Up);
+
+ // led1 is active low
+ led1.set_high();
+
+ loop {
+ s2.wait_for_falling_edge().await;
+
+ info!("Switch 2 was pressed");
+
+ led1.toggle();
+ }
+}
diff --git a/examples/mspm0l2228/.cargo/config.toml b/examples/mspm0l2228/.cargo/config.toml
new file mode 100644
index 000000000..f383afd9e
--- /dev/null
+++ b/examples/mspm0l2228/.cargo/config.toml
@@ -0,0 +1,9 @@
+[target.'cfg(all(target_arch = "arm", target_os = "none"))']
+# replace MSPM0L2228 with your chip as listed in `probe-rs chip list`
+runner = "probe-rs run --restore-unwritten --verify --chip MSPM0L2228 --protocol=swd"
+
+[build]
+target = "thumbv6m-none-eabi"
+
+[env]
+DEFMT_LOG = "trace"
diff --git a/examples/mspm0l2228/Cargo.toml b/examples/mspm0l2228/Cargo.toml
new file mode 100644
index 000000000..9474c2ced
--- /dev/null
+++ b/examples/mspm0l2228/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+edition = "2021"
+name = "embassy-mspm0-l2228-examples"
+version = "0.1.0"
+license = "MIT OR Apache-2.0"
+
+[dependencies]
+embassy-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = ["mspm0l222x", "rt", "time-driver-any"] }
+embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["task-arena-size-1024", "arch-cortex-m", "executor-thread", "executor-interrupt"] }
+embassy-sync = { version = "0.6.2", path = "../../embassy-sync", features = ["defmt"] }
+embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt"] }
+panic-halt = "0.2.0"
+cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
+cortex-m-rt = { version = "0.7.0"}
+defmt = "0.3"
+defmt-rtt = "0.4"
+panic-probe = { version = "0.3.2", features = ["print-defmt"] }
+panic-semihosting = "0.6.0"
+
+[profile.release]
+debug = 2
diff --git a/examples/mspm0l2228/README.md b/examples/mspm0l2228/README.md
new file mode 100644
index 000000000..c73fa13b6
--- /dev/null
+++ b/examples/mspm0l2228/README.md
@@ -0,0 +1,27 @@
+# Examples for MSPM0L222x family
+
+Run individual examples with
+```
+cargo run --bin
+```
+for example
+```
+cargo run --bin blinky
+```
+
+## Checklist before running examples
+A large number of the examples are written for the [LP-MSPM0L2228](https://www.ti.com/tool/LP-MSPM0L2228) board.
+
+You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.
+
+* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L2228 it should be `probe-rs run --chip MSPM0L2228`. (use `probe-rs chip list` to find your chip)
+* [ ] Update Cargo.toml to have the correct `embassy-mspm0` feature. For example for L2228 it should be `mspm0l2228`. Look in the `Cargo.toml` file of the `embassy-mspm0` project to find the correct feature flag for your chip.
+* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.
+* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic
+
+If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
+
+* Which example you are trying to run
+* Which chip and board you are using
+
+Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
diff --git a/examples/mspm0l2228/build.rs b/examples/mspm0l2228/build.rs
new file mode 100644
index 000000000..30691aa97
--- /dev/null
+++ b/examples/mspm0l2228/build.rs
@@ -0,0 +1,35 @@
+//! This build script copies the `memory.x` file from the crate root into
+//! a directory where the linker can always find it at build time.
+//! For many projects this is optional, as the linker always searches the
+//! project root directory -- wherever `Cargo.toml` is. However, if you
+//! are using a workspace or have a more complicated build setup, this
+//! build script becomes required. Additionally, by requesting that
+//! Cargo re-run the build script whenever `memory.x` is changed,
+//! updating `memory.x` ensures a rebuild of the application with the
+//! new memory settings.
+
+use std::env;
+use std::fs::File;
+use std::io::Write;
+use std::path::PathBuf;
+
+fn main() {
+ // Put `memory.x` in our output directory and ensure it's
+ // on the linker search path.
+ let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
+ File::create(out.join("memory.x"))
+ .unwrap()
+ .write_all(include_bytes!("memory.x"))
+ .unwrap();
+ println!("cargo:rustc-link-search={}", out.display());
+
+ // By default, Cargo will re-run a build script whenever
+ // any file in the project changes. By specifying `memory.x`
+ // here, we ensure the build script is only re-run when
+ // `memory.x` is changed.
+ println!("cargo:rerun-if-changed=memory.x");
+
+ println!("cargo:rustc-link-arg-bins=--nmagic");
+ println!("cargo:rustc-link-arg-bins=-Tlink.x");
+ println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
+}
diff --git a/examples/mspm0l2228/memory.x b/examples/mspm0l2228/memory.x
new file mode 100644
index 000000000..aba414a88
--- /dev/null
+++ b/examples/mspm0l2228/memory.x
@@ -0,0 +1,6 @@
+MEMORY
+{
+ FLASH : ORIGIN = 0x00000000, LENGTH = 256K
+ /* Select non-parity range of SRAM due to SRAM_ERR_01 errata in SLAZ758 */
+ RAM : ORIGIN = 0x20200000, LENGTH = 32K
+}
diff --git a/examples/mspm0l2228/src/bin/blinky.rs b/examples/mspm0l2228/src/bin/blinky.rs
new file mode 100644
index 000000000..055a5cd81
--- /dev/null
+++ b/examples/mspm0l2228/src/bin/blinky.rs
@@ -0,0 +1,25 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_mspm0::gpio::{Level, Output};
+use embassy_mspm0::Config;
+use embassy_time::Timer;
+use {defmt_rtt as _, panic_halt as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) -> ! {
+ info!("Hello world!");
+ let p = embassy_mspm0::init(Config::default());
+
+ let mut led1 = Output::new(p.PA0, Level::Low);
+ led1.set_inversion(true);
+
+ loop {
+ Timer::after_millis(400).await;
+
+ info!("Toggle");
+ led1.toggle();
+ }
+}
diff --git a/examples/mspm0l2228/src/bin/button.rs b/examples/mspm0l2228/src/bin/button.rs
new file mode 100644
index 000000000..47bfd274b
--- /dev/null
+++ b/examples/mspm0l2228/src/bin/button.rs
@@ -0,0 +1,33 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_mspm0::gpio::{Input, Level, Output, Pull};
+use embassy_mspm0::Config;
+use {defmt_rtt as _, panic_halt as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) -> ! {
+ info!("Hello world!");
+
+ let p = embassy_mspm0::init(Config::default());
+
+ let led1 = p.PA0;
+ let s2 = p.PB8;
+
+ let mut led1 = Output::new(led1, Level::Low);
+
+ let mut s2 = Input::new(s2, Pull::Up);
+
+ // led1 is active low
+ led1.set_high();
+
+ loop {
+ s2.wait_for_falling_edge().await;
+
+ info!("Switch 2 was pressed");
+
+ led1.toggle();
+ }
+}
|