diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml
index 28a8e7724..df996ff4b 100644
--- a/embassy-mspm0/Cargo.toml
+++ b/embassy-mspm0/Cargo.toml
@@ -39,6 +39,7 @@ embedded-hal = { version = "1.0" }
embedded-hal-async = { version = "1.0" }
defmt = { version = "0.3", optional = true }
+fixed = "1.29"
log = { version = "0.4.14", optional = true }
cortex-m-rt = ">=0.6.15,<0.8"
cortex-m = "0.7.6"
diff --git a/embassy-mspm0/build.rs b/embassy-mspm0/build.rs
index 39d8b2f8a..08209df2a 100644
--- a/embassy-mspm0/build.rs
+++ b/embassy-mspm0/build.rs
@@ -381,7 +381,7 @@ fn generate_peripheral_instances() -> TokenStream {
// Will be filled in when uart implementation is finished
let _ = peri;
let tokens = match peripheral.kind {
- // "uart" => Some(quote! { impl_uart_instance!(#peri); }),
+ "uart" => Some(quote! { impl_uart_instance!(#peri); }),
_ => None,
};
@@ -412,10 +412,10 @@ fn generate_pin_trait_impls() -> TokenStream {
let _ = pf;
let tokens = match key {
- // ("uart", "TX") => Some(quote! { impl_uart_tx_pin!(#peri, #pin_name, #pf); }),
- // ("uart", "RX") => Some(quote! { impl_uart_rx_pin!(#peri, #pin_name, #pf); }),
- // ("uart", "CTS") => Some(quote! { impl_uart_cts_pin!(#peri, #pin_name, #pf); }),
- // ("uart", "RTS") => Some(quote! { impl_uart_rts_pin!(#peri, #pin_name, #pf); }),
+ ("uart", "TX") => Some(quote! { impl_uart_tx_pin!(#peri, #pin_name, #pf); }),
+ ("uart", "RX") => Some(quote! { impl_uart_rx_pin!(#peri, #pin_name, #pf); }),
+ ("uart", "CTS") => Some(quote! { impl_uart_cts_pin!(#peri, #pin_name, #pf); }),
+ ("uart", "RTS") => Some(quote! { impl_uart_rts_pin!(#peri, #pin_name, #pf); }),
_ => None,
};
diff --git a/embassy-mspm0/src/gpio.rs b/embassy-mspm0/src/gpio.rs
index 2edadbc5a..7e6b01649 100644
--- a/embassy-mspm0/src/gpio.rs
+++ b/embassy-mspm0/src/gpio.rs
@@ -836,6 +836,31 @@ impl<'d> embedded_hal_async::digital::Wait for OutputOpenDrain<'d> {
}
}
+#[derive(Copy, Clone)]
+pub struct PfType {
+ pull: Pull,
+ input: bool,
+ invert: bool,
+}
+
+impl PfType {
+ pub const fn input(pull: Pull, invert: bool) -> Self {
+ Self {
+ pull,
+ input: true,
+ invert,
+ }
+ }
+
+ pub const fn output(pull: Pull, invert: bool) -> Self {
+ Self {
+ pull,
+ input: false,
+ invert,
+ }
+ }
+}
+
/// 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.
@@ -907,6 +932,40 @@ pub(crate) trait SealedPin {
(self.pin_port() % 32) as usize
}
+ #[inline]
+ fn set_as_analog(&self) {
+ let pincm = pac::IOMUX.pincm(self._pin_cm() as usize);
+
+ pincm.modify(|w| {
+ w.set_pf(DISCONNECT_PF);
+ w.set_pipu(false);
+ w.set_pipd(false);
+ });
+ }
+
+ fn update_pf(&self, ty: PfType) {
+ let pincm = pac::IOMUX.pincm(self._pin_cm() as usize);
+ let pf = pincm.read().pf();
+
+ set_pf(self._pin_cm() as usize, pf, ty);
+ }
+
+ fn set_as_pf(&self, pf: u8, ty: PfType) {
+ set_pf(self._pin_cm() as usize, pf, ty)
+ }
+
+ /// 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]
+ fn set_as_disconnected(&self) {
+ self.set_as_analog();
+ }
+
#[inline]
fn block(&self) -> gpio::Gpio {
match self.pin_port() / 32 {
@@ -920,6 +979,18 @@ pub(crate) trait SealedPin {
}
}
+#[inline(never)]
+fn set_pf(pincm: usize, pf: u8, ty: PfType) {
+ pac::IOMUX.pincm(pincm).modify(|w| {
+ w.set_pf(pf);
+ w.set_pc(true);
+ w.set_pipu(ty.pull == Pull::Up);
+ w.set_pipd(ty.pull == Pull::Down);
+ w.set_inena(ty.input);
+ w.set_inv(ty.invert);
+ });
+}
+
#[must_use = "futures do nothing unless you `.await` or poll them"]
struct InputFuture<'d> {
pin: Peri<'d, AnyPin>,
diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs
index 99b7ed4a1..e8f5971d5 100644
--- a/embassy-mspm0/src/lib.rs
+++ b/embassy-mspm0/src/lib.rs
@@ -5,8 +5,12 @@
// This mod MUST go first, so that the others see its macros.
pub(crate) mod fmt;
+// This must be declared early as well for
+mod macros;
+
pub mod gpio;
pub mod timer;
+pub mod uart;
/// Operating modes for peripherals.
pub mod mode {
diff --git a/embassy-mspm0/src/macros.rs b/embassy-mspm0/src/macros.rs
new file mode 100644
index 000000000..5355e7d59
--- /dev/null
+++ b/embassy-mspm0/src/macros.rs
@@ -0,0 +1,9 @@
+#![macro_use]
+
+macro_rules! new_pin {
+ ($name: ident, $pf_type: expr) => {{
+ let pin = $name;
+ pin.set_as_pf(pin.pf_num(), $pf_type);
+ Some(pin.into())
+ }};
+}
diff --git a/embassy-mspm0/src/uart.rs b/embassy-mspm0/src/uart.rs
new file mode 100644
index 000000000..45094a000
--- /dev/null
+++ b/embassy-mspm0/src/uart.rs
@@ -0,0 +1,1085 @@
+#![macro_use]
+
+use core::marker::PhantomData;
+use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
+
+use embassy_embedded_hal::SetConfig;
+use embassy_hal_internal::PeripheralType;
+
+use crate::gpio::{AnyPin, PfType, Pull, SealedPin};
+use crate::interrupt::{Interrupt, InterruptExt};
+use crate::mode::{Blocking, Mode};
+use crate::pac::uart::{vals, Uart as Regs};
+use crate::Peri;
+
+/// The clock source for the UART.
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum ClockSel {
+ /// Use the low frequency clock.
+ ///
+ /// The LFCLK runs at 32.768 kHz.
+ LfClk,
+
+ /// Use the middle frequency clock.
+ ///
+ /// The MCLK runs at 4 MHz.
+ MfClk,
+ // BusClk,
+ // BusClk depends on the timer's power domain.
+ // This will be implemented later.
+}
+
+#[non_exhaustive]
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+/// The order of bits in byte.
+pub enum BitOrder {
+ /// The most significant bit is first.
+ MsbFirst,
+
+ /// The least significant bit is first.
+ LsbFirst,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+/// Number of data bits
+pub enum DataBits {
+ /// 5 Data Bits
+ DataBits5,
+
+ /// 6 Data Bits
+ DataBits6,
+
+ /// 7 Data Bits
+ DataBits7,
+
+ /// 8 Data Bits
+ DataBits8,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+/// Parity
+pub enum Parity {
+ /// No parity
+ ParityNone,
+
+ /// Even Parity
+ ParityEven,
+
+ /// Odd Parity
+ ParityOdd,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+/// Number of stop bits
+pub enum StopBits {
+ /// One stop bit
+ Stop1,
+
+ /// Two stop bits
+ Stop2,
+}
+
+#[non_exhaustive]
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+/// Config Error
+pub enum ConfigError {
+ /// Rx or Tx not enabled
+ RxOrTxNotEnabled,
+
+ /// The baud rate could not be configured with the given clocks.
+ InvalidBaudRate,
+}
+
+#[non_exhaustive]
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+/// Config
+pub struct Config {
+ /// UART clock source.
+ pub clock_source: ClockSel,
+
+ /// Baud rate
+ pub baudrate: u32,
+
+ /// Number of data bits.
+ pub data_bits: DataBits,
+
+ /// Number of stop bits.
+ pub stop_bits: StopBits,
+
+ /// Parity type.
+ pub parity: Parity,
+
+ /// The order of bits in a transmitted/received byte.
+ pub msb_order: BitOrder,
+
+ /// If true: the `TX` is internally connected to `RX`.
+ pub loop_back_enable: bool,
+
+ // TODO: Pending way to check if uart is extended
+ // /// If true: [manchester coding] is used.
+ // ///
+ // /// [manchester coding]: https://en.wikipedia.org/wiki/Manchester_code
+ // pub manchester: bool,
+
+ // TODO: majority voting
+
+ // TODO: fifo level select - need power domain info in metapac
+
+ // TODO: glitch suppression
+ /// If true: invert TX pin signal values (VDD = 0/mark, Gnd = 1/idle).
+ pub invert_tx: bool,
+
+ /// If true: invert RX pin signal values (VDD = 0/mark, Gnd = 1/idle).
+ pub invert_rx: bool,
+
+ /// If true: invert RTS pin signal values (VDD = 0/mark, Gnd = 1/idle).
+ pub invert_rts: bool,
+
+ /// If true: invert CTS pin signal values (VDD = 0/mark, Gnd = 1/idle).
+ pub invert_cts: bool,
+
+ /// Set the pull configuration for the TX pin.
+ pub tx_pull: Pull,
+
+ /// Set the pull configuration for the RX pin.
+ pub rx_pull: Pull,
+
+ /// Set the pull configuration for the RTS pin.
+ pub rts_pull: Pull,
+
+ /// Set the pull configuration for the CTS pin.
+ pub cts_pull: Pull,
+}
+
+impl Default for Config {
+ fn default() -> Self {
+ Self {
+ clock_source: ClockSel::MfClk,
+ baudrate: 115200,
+ data_bits: DataBits::DataBits8,
+ stop_bits: StopBits::Stop1,
+ parity: Parity::ParityNone,
+ // hardware default
+ msb_order: BitOrder::LsbFirst,
+ loop_back_enable: false,
+ // manchester: false,
+ invert_tx: false,
+ invert_rx: false,
+ invert_rts: false,
+ invert_cts: false,
+ tx_pull: Pull::None,
+ rx_pull: Pull::None,
+ rts_pull: Pull::None,
+ cts_pull: Pull::None,
+ }
+ }
+}
+
+/// Bidirectional UART Driver, which acts as a combination of [`UartTx`] and [`UartRx`].
+///
+/// ### Notes on [`embedded_io::Read`]
+///
+/// `embedded_io::Read` requires guarantees that the base [`UartRx`] cannot provide.
+///
+/// See [`UartRx`] for more details, and see [`BufferedUart`] and [`RingBufferedUartRx`]
+/// as alternatives that do provide the necessary guarantees for `embedded_io::Read`.
+pub struct Uart<'d, M: Mode> {
+ tx: UartTx<'d, M>,
+ rx: UartRx<'d, M>,
+}
+
+impl<'d, M: Mode> SetConfig for Uart<'d, M> {
+ type Config = Config;
+ type ConfigError = ConfigError;
+
+ fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
+ self.tx.set_config(config)?;
+ self.rx.set_config(config)
+ }
+}
+
+/// Serial error
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+#[non_exhaustive]
+pub enum Error {
+ Framing,
+
+ Noise,
+
+ Overrun,
+
+ Parity,
+
+ Break,
+}
+
+impl core::fmt::Display for Error {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ let message = match self {
+ Self::Framing => "Framing Error",
+ Self::Noise => "Noise Error",
+ Self::Overrun => "RX Buffer Overrun",
+ Self::Parity => "Parity Check Error",
+ Self::Break => "Break Error",
+ };
+
+ write!(f, "{}", message)
+ }
+}
+
+impl core::error::Error for Error {}
+
+/// Rx-only UART Driver.
+///
+/// Can be obtained from [`Uart::split`], or can be constructed independently,
+/// if you do not need the transmitting half of the driver.
+pub struct UartRx<'d, M: Mode> {
+ info: &'static Info,
+ state: &'static State,
+ rx: Option>,
+ rts: Option>,
+ _phantom: PhantomData,
+}
+
+impl<'d, M: Mode> SetConfig for UartRx<'d, M> {
+ type Config = Config;
+ type ConfigError = ConfigError;
+
+ fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
+ self.set_config(config)
+ }
+}
+
+impl<'d> UartRx<'d, Blocking> {
+ /// Create a new rx-only UART with no hardware flow control.
+ ///
+ /// Useful if you only want Uart Rx. It saves 1 pin .
+ pub fn new_blocking(
+ peri: Peri<'d, T>,
+ rx: Peri<'d, impl RxPin>,
+ config: Config,
+ ) -> Result {
+ Self::new_inner(peri, new_pin!(rx, config.rx_pf()), None, config)
+ }
+
+ /// Create a new rx-only UART with a request-to-send pin
+ pub fn new_blocking_with_rts(
+ peri: Peri<'d, T>,
+ rx: Peri<'d, impl RxPin>,
+ rts: Peri<'d, impl RtsPin>,
+ config: Config,
+ ) -> Result {
+ Self::new_inner(
+ peri,
+ new_pin!(rx, config.rx_pf()),
+ new_pin!(rts, config.rts_pf()),
+ config,
+ )
+ }
+}
+
+impl<'d, M: Mode> UartRx<'d, M> {
+ /// Reconfigure the driver
+ pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
+ if let Some(ref rx) = self.rx {
+ rx.update_pf(config.rx_pf());
+ }
+
+ if let Some(ref rts) = self.rts {
+ rts.update_pf(config.rts_pf());
+ }
+
+ reconfigure(self.info, self.state, config)
+ }
+
+ /// Perform a blocking read into `buffer`
+ pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
+ let r = self.info.regs;
+
+ for b in buffer {
+ // Wait if nothing has arrived yet.
+ while r.stat().read().rxfe() {}
+
+ // Prevent the compiler from reading from buffer too early
+ compiler_fence(Ordering::Acquire);
+ *b = read_with_error(r)?;
+ }
+
+ Ok(())
+ }
+
+ /// Set baudrate
+ pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> {
+ set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate)
+ }
+}
+
+impl<'d, M: Mode> Drop for UartRx<'d, M> {
+ fn drop(&mut self) {
+ self.rx.as_ref().map(|x| x.set_as_disconnected());
+ self.rts.as_ref().map(|x| x.set_as_disconnected());
+ }
+}
+
+/// Tx-only UART Driver.
+///
+/// Can be obtained from [`Uart::split`], or can be constructed independently,
+/// if you do not need the receiving half of the driver.
+pub struct UartTx<'d, M: Mode> {
+ info: &'static Info,
+ state: &'static State,
+ tx: Option>,
+ cts: Option>,
+ _phantom: PhantomData,
+}
+
+impl<'d, M: Mode> SetConfig for UartTx<'d, M> {
+ type Config = Config;
+ type ConfigError = ConfigError;
+
+ fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
+ reconfigure(self.info, self.state, config)
+ }
+}
+
+impl<'d> UartTx<'d, Blocking> {
+ /// Create a new blocking tx-only UART with no hardware flow control.
+ ///
+ /// Useful if you only want Uart Tx. It saves 1 pin.
+ pub fn new_blocking(
+ peri: Peri<'d, T>,
+ tx: Peri<'d, impl TxPin>,
+ config: Config,
+ ) -> Result {
+ Self::new_inner(peri, new_pin!(tx, config.tx_pf()), None, config)
+ }
+
+ /// Create a new blocking tx-only UART with a clear-to-send pin
+ pub fn new_blocking_with_cts(
+ peri: Peri<'d, T>,
+ tx: Peri<'d, impl TxPin>,
+ cts: Peri<'d, impl CtsPin>,
+ config: Config,
+ ) -> Result {
+ Self::new_inner(
+ peri,
+ new_pin!(tx, config.tx_pf()),
+ new_pin!(cts, config.cts_pf()),
+ config,
+ )
+ }
+}
+
+impl<'d, M: Mode> UartTx<'d, M> {
+ /// Reconfigure the driver
+ pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
+ if let Some(ref tx) = self.tx {
+ tx.update_pf(config.tx_pf());
+ }
+
+ if let Some(ref cts) = self.cts {
+ cts.update_pf(config.cts_pf());
+ }
+
+ reconfigure(self.info, self.state, config)
+ }
+
+ /// Perform a blocking UART write
+ pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
+ let r = self.info.regs;
+
+ for &b in buffer {
+ // Wait if there is no space
+ while !r.stat().read().txfe() {}
+
+ // Prevent the compiler from writing to buffer too early
+ compiler_fence(Ordering::Release);
+ r.txdata().write(|w| {
+ w.set_data(b);
+ });
+ }
+
+ Ok(())
+ }
+
+ /// Block until transmission complete
+ pub fn blocking_flush(&mut self) -> Result<(), Error> {
+ let r = self.info.regs;
+
+ // Wait until TX fifo/buffer is empty
+ while r.stat().read().txfe() {}
+ Ok(())
+ }
+
+ /// Send break character
+ pub fn send_break(&self) {
+ let r = self.info.regs;
+
+ r.lcrh().modify(|w| {
+ w.set_brk(true);
+ });
+ }
+
+ /// Set baudrate
+ pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> {
+ set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate)
+ }
+}
+
+impl<'d, M: Mode> Drop for UartTx<'d, M> {
+ fn drop(&mut self) {
+ self.tx.as_ref().map(|x| x.set_as_disconnected());
+ self.cts.as_ref().map(|x| x.set_as_disconnected());
+ }
+}
+
+impl<'d> Uart<'d, Blocking> {
+ /// Create a new blocking bidirectional UART.
+ pub fn new_blocking(
+ peri: Peri<'d, T>,
+ rx: Peri<'d, impl RxPin>,
+ tx: Peri<'d, impl TxPin>,
+ config: Config,
+ ) -> Result {
+ Self::new_inner(
+ peri,
+ new_pin!(rx, config.rx_pf()),
+ new_pin!(tx, config.tx_pf()),
+ None,
+ None,
+ config,
+ )
+ }
+
+ /// Create a new bidirectional UART with request-to-send and clear-to-send pins
+ pub fn new_blocking_with_rtscts(
+ peri: Peri<'d, T>,
+ rx: Peri<'d, impl RxPin>,
+ tx: Peri<'d, impl TxPin>,
+ rts: Peri<'d, impl RtsPin>,
+ cts: Peri<'d, impl CtsPin>,
+ config: Config,
+ ) -> Result {
+ Self::new_inner(
+ peri,
+ new_pin!(rx, config.rx_pf()),
+ new_pin!(tx, config.tx_pf()),
+ new_pin!(rts, config.rts_pf()),
+ new_pin!(cts, config.cts_pf()),
+ config,
+ )
+ }
+}
+
+impl<'d, M: Mode> Uart<'d, M> {
+ /// Perform a blocking write
+ pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
+ self.tx.blocking_write(buffer)
+ }
+
+ /// Block until transmission complete
+ pub fn blocking_flush(&mut self) -> Result<(), Error> {
+ self.tx.blocking_flush()
+ }
+
+ /// Perform a blocking read into `buffer`
+ pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
+ self.rx.blocking_read(buffer)
+ }
+
+ /// Split the Uart into a transmitter and receiver, which is
+ /// particularly useful when having two tasks correlating to
+ /// transmitting and receiving.
+ pub fn split(self) -> (UartTx<'d, M>, UartRx<'d, M>) {
+ (self.tx, self.rx)
+ }
+
+ /// Split the Uart into a transmitter and receiver by mutable reference,
+ /// which is particularly useful when having two tasks correlating to
+ /// transmitting and receiving.
+ pub fn split_ref(&mut self) -> (&mut UartTx<'d, M>, &mut UartRx<'d, M>) {
+ (&mut self.tx, &mut self.rx)
+ }
+
+ /// Send break character
+ pub fn send_break(&self) {
+ self.tx.send_break();
+ }
+
+ /// Set baudrate
+ pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> {
+ set_baudrate(&self.tx.info, self.tx.state.clock.load(Ordering::Relaxed), baudrate)
+ }
+}
+
+/// Peripheral instance trait.
+#[allow(private_bounds)]
+pub trait Instance: SealedInstance + PeripheralType {
+ type Interrupt: crate::interrupt::typelevel::Interrupt;
+}
+
+/// UART `TX` pin trait
+pub trait TxPin: crate::gpio::Pin {
+ /// Get the PF number needed to use this pin as `TX`.
+ fn pf_num(&self) -> u8;
+}
+
+/// UART `RX` pin trait
+pub trait RxPin: crate::gpio::Pin {
+ /// Get the PF number needed to use this pin as `RX`.
+ fn pf_num(&self) -> u8;
+}
+
+/// UART `CTS` pin trait
+pub trait CtsPin: crate::gpio::Pin {
+ /// Get the PF number needed to use this pin as `CTS`.
+ fn pf_num(&self) -> u8;
+}
+
+/// UART `RTS` pin trait
+pub trait RtsPin: crate::gpio::Pin {
+ /// Get the PF number needed to use this pin as `RTS`.
+ fn pf_num(&self) -> u8;
+}
+
+// ==== IMPL types ====
+
+pub(crate) struct Info {
+ pub(crate) regs: Regs,
+ pub(crate) interrupt: Interrupt,
+}
+
+pub(crate) struct State {
+ /// The clock rate of the UART. This might be configured.
+ pub(crate) clock: AtomicU32,
+}
+
+impl<'d, M: Mode> UartRx<'d, M> {
+ fn new_inner(
+ _peri: Peri<'d, T>,
+ rx: Option>,
+ rts: Option>,
+ config: Config,
+ ) -> Result {
+ let mut this = Self {
+ info: T::info(),
+ state: T::state(),
+ rx,
+ rts,
+ _phantom: PhantomData,
+ };
+ this.enable_and_configure(&config)?;
+
+ Ok(this)
+ }
+
+ fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> {
+ let info = self.info;
+
+ enable(info.regs);
+ configure(info, self.state, config, true, self.rts.is_some(), false, false)?;
+
+ Ok(())
+ }
+}
+
+impl<'d, M: Mode> UartTx<'d, M> {
+ fn new_inner(
+ _peri: Peri<'d, T>,
+ tx: Option>,
+ cts: Option>,
+ config: Config,
+ ) -> Result {
+ let mut this = Self {
+ info: T::info(),
+ state: T::state(),
+ tx,
+ cts,
+ _phantom: PhantomData,
+ };
+ this.enable_and_configure(&config)?;
+
+ Ok(this)
+ }
+
+ fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> {
+ let info = self.info;
+ let state = self.state;
+
+ enable(info.regs);
+ configure(info, state, config, false, false, true, self.cts.is_some())?;
+
+ Ok(())
+ }
+}
+
+impl<'d, M: Mode> Uart<'d, M> {
+ fn new_inner(
+ _peri: Peri<'d, T>,
+ rx: Option>,
+ tx: Option>,
+ rts: Option>,
+ cts: Option>,
+ config: Config,
+ ) -> Result {
+ let info = T::info();
+ let state = T::state();
+
+ let mut this = Self {
+ tx: UartTx {
+ info,
+ state,
+ tx,
+ cts,
+ _phantom: PhantomData,
+ },
+ rx: UartRx {
+ info,
+ state,
+ rx,
+ rts,
+ _phantom: PhantomData,
+ },
+ };
+ this.enable_and_configure(&config)?;
+
+ Ok(this)
+ }
+
+ fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> {
+ let info = self.rx.info;
+ let state = self.rx.state;
+
+ enable(info.regs);
+ configure(
+ info,
+ state,
+ config,
+ true,
+ self.rx.rts.is_some(),
+ true,
+ self.tx.cts.is_some(),
+ )?;
+
+ info.interrupt.unpend();
+ unsafe { info.interrupt.enable() };
+
+ Ok(())
+ }
+}
+
+impl Config {
+ fn tx_pf(&self) -> PfType {
+ PfType::output(self.tx_pull, self.invert_tx)
+ }
+
+ fn rx_pf(&self) -> PfType {
+ PfType::input(self.rx_pull, self.invert_rx)
+ }
+
+ fn rts_pf(&self) -> PfType {
+ PfType::output(self.rts_pull, self.invert_rts)
+ }
+
+ fn cts_pf(&self) -> PfType {
+ PfType::input(self.rts_pull, self.invert_rts)
+ }
+}
+
+fn enable(regs: Regs) {
+ let gprcm = regs.gprcm(0);
+
+ gprcm.rstctl().write(|w| {
+ w.set_resetstkyclr(true);
+ w.set_resetassert(true);
+ w.set_key(vals::ResetKey::KEY);
+ });
+
+ gprcm.pwren().write(|w| {
+ w.set_enable(true);
+ w.set_key(vals::PwrenKey::KEY);
+ });
+}
+
+fn configure(
+ info: &Info,
+ state: &State,
+ config: &Config,
+ enable_rx: bool,
+ enable_rts: bool,
+ enable_tx: bool,
+ enable_cts: bool,
+) -> Result<(), ConfigError> {
+ let r = info.regs;
+
+ if !enable_rx && !enable_tx {
+ return Err(ConfigError::RxOrTxNotEnabled);
+ }
+
+ // SLAU846B says that clocks should be enabled before disabling the uart.
+ r.clksel().write(|w| match config.clock_source {
+ ClockSel::LfClk => {
+ w.set_lfclk_sel(true);
+ w.set_mfclk_sel(false);
+ w.set_busclk_sel(false);
+ }
+ ClockSel::MfClk => {
+ w.set_mfclk_sel(true);
+ w.set_lfclk_sel(false);
+ w.set_busclk_sel(false);
+ }
+ });
+
+ let clock = match config.clock_source {
+ ClockSel::LfClk => 32768,
+ ClockSel::MfClk => 4_000_000,
+ };
+
+ state.clock.store(clock, Ordering::Relaxed);
+
+ info.regs.ctl0().modify(|w| {
+ w.set_lbe(config.loop_back_enable);
+ w.set_rxe(enable_rx);
+ w.set_txe(enable_tx);
+ // RXD_OUT_EN and TXD_OUT_EN?
+ w.set_menc(false);
+ w.set_mode(vals::Mode::UART);
+ w.set_rtsen(enable_rts);
+ w.set_ctsen(enable_cts);
+ // oversampling is set later
+ // TODO: config
+ w.set_fen(false);
+ // TODO: config
+ w.set_majvote(false);
+ w.set_msbfirst(matches!(config.msb_order, BitOrder::MsbFirst));
+ });
+
+ info.regs.lcrh().modify(|w| {
+ let eps = if matches!(config.parity, Parity::ParityEven) {
+ vals::Eps::EVEN
+ } else {
+ vals::Eps::ODD
+ };
+
+ let wlen = match config.data_bits {
+ DataBits::DataBits5 => vals::Wlen::DATABIT5,
+ DataBits::DataBits6 => vals::Wlen::DATABIT6,
+ DataBits::DataBits7 => vals::Wlen::DATABIT7,
+ DataBits::DataBits8 => vals::Wlen::DATABIT8,
+ };
+
+ // Used in LIN mode only
+ w.set_brk(false);
+ w.set_pen(config.parity != Parity::ParityNone);
+ w.set_eps(eps);
+ w.set_stp2(matches!(config.stop_bits, StopBits::Stop2));
+ w.set_wlen(wlen);
+ // appears to only be used in RS-485 mode.
+ w.set_sps(false);
+ // IDLE pattern?
+ w.set_sendidle(false);
+ // ignore extdir_setup and extdir_hold, only used in RS-485 mode.
+ });
+
+ set_baudrate_inner(info.regs, clock, config.baudrate)?;
+
+ r.ctl0().modify(|w| {
+ w.set_enable(true);
+ });
+
+ Ok(())
+}
+
+fn reconfigure(info: &Info, state: &State, config: &Config) -> Result<(), ConfigError> {
+ info.interrupt.disable();
+ let r = info.regs;
+ let ctl0 = r.ctl0().read();
+ configure(info, state, config, ctl0.rxe(), ctl0.rtsen(), ctl0.txe(), ctl0.ctsen())?;
+
+ info.interrupt.unpend();
+ unsafe { info.interrupt.enable() };
+
+ Ok(())
+}
+
+/// Set the baud rate and clock settings.
+///
+/// This should be done relatively late during configuration since some clock settings are invalid depending on mode.
+fn set_baudrate(info: &Info, clock: u32, baudrate: u32) -> Result<(), ConfigError> {
+ let r = info.regs;
+
+ info.interrupt.disable();
+
+ // Programming baud rate requires that the peripheral is disabled
+ critical_section::with(|_cs| {
+ r.ctl0().modify(|w| {
+ w.set_enable(false);
+ });
+ });
+
+ // Wait for end of transmission per suggestion in SLAU 845 section 18.3.28
+ while !r.stat().read().txfe() {}
+
+ set_baudrate_inner(r, clock, baudrate)?;
+
+ critical_section::with(|_cs| {
+ r.ctl0().modify(|w| {
+ w.set_enable(true);
+ });
+ });
+
+ info.interrupt.unpend();
+ unsafe { info.interrupt.enable() };
+
+ Ok(())
+}
+
+fn set_baudrate_inner(regs: Regs, clock: u32, baudrate: u32) -> Result<(), ConfigError> {
+ // Quoting SLAU846 section 18.2.3.4:
+ // "When IBRD = 0, FBRD is ignored and no data gets transferred by the UART."
+ const MIN_IBRD: u16 = 1;
+
+ // FBRD can be 0
+ // FBRD is at most a 6-bit number.
+ const MAX_FBRD: u8 = 2_u8.pow(6);
+
+ const DIVS: [(u8, vals::Clkdiv); 8] = [
+ (1, vals::Clkdiv::DIV_BY_1),
+ (2, vals::Clkdiv::DIV_BY_2),
+ (3, vals::Clkdiv::DIV_BY_3),
+ (4, vals::Clkdiv::DIV_BY_4),
+ (5, vals::Clkdiv::DIV_BY_5),
+ (6, vals::Clkdiv::DIV_BY_6),
+ (7, vals::Clkdiv::DIV_BY_7),
+ (8, vals::Clkdiv::DIV_BY_8),
+ ];
+
+ // Quoting from SLAU 846 section 18.2.3.4:
+ // "Select oversampling by 3 or 8 to achieve higher speed with UARTclk/8 or UARTclk/3. In this case
+ // the receiver tolerance to clock deviation is reduced."
+ //
+ // "Select oversampling by 16 to increase the tolerance of the receiver to clock deviations. The
+ // maximum speed is limited to UARTclk/16."
+ //
+ // Based on these requirements, prioritize higher oversampling first to increase tolerance to clock
+ // deviation. If no valid BRD valud can be found satisifying the highest sample rate, then reduce
+ // sample rate until valid parameters are found.
+ const OVS: [(u8, vals::Hse); 3] = [(16, vals::Hse::OVS16), (8, vals::Hse::OVS8), (3, vals::Hse::OVS3)];
+
+ // 3x oversampling is not supported with manchester coding, DALI or IrDA.
+ let x3_invalid = {
+ let ctl0 = regs.ctl0().read();
+ let irctl = regs.irctl().read();
+
+ ctl0.menc() || matches!(ctl0.mode(), vals::Mode::DALI) || irctl.iren()
+ };
+ let mut found = None;
+
+ for &(oversampling, hse_value) in &OVS {
+ if matches!(hse_value, vals::Hse::OVS3) && !x3_invalid {
+ continue;
+ }
+
+ for &(div, div_value) in &DIVS {
+ let Some((ibrd, fbrd)) = calculate_brd(clock, div, baudrate, oversampling) else {
+ continue;
+ };
+
+ if ibrd < MIN_IBRD || fbrd > MAX_FBRD {
+ continue;
+ }
+
+ found = Some((hse_value, div_value, ibrd, fbrd));
+ }
+ }
+
+ let Some((hse, div, ibrd, fbrd)) = found else {
+ return Err(ConfigError::InvalidBaudRate);
+ };
+
+ regs.clkdiv().write(|w| {
+ w.set_ratio(div);
+ });
+
+ regs.ibrd().write(|w| {
+ w.set_divint(ibrd);
+ });
+
+ regs.fbrd().write(|w| {
+ w.set_divfrac(fbrd);
+ });
+
+ regs.ctl0().modify(|w| {
+ w.set_hse(hse);
+ });
+
+ Ok(())
+}
+
+/// Calculate the integer and fractional parts of the `BRD` value.
+///
+/// Returns [`None`] if calculating this results in overflows.
+///
+/// Values returned are `(ibrd, fbrd)`
+fn calculate_brd(clock: u32, div: u8, baud: u32, oversampling: u8) -> Option<(u16, u8)> {
+ use fixed::types::U26F6;
+
+ // Calculate BRD according to SLAU 846 section 18.2.3.4.
+ //
+ // BRD is a 22-bit value with 16 integer bits and 6 fractional bits.
+ //
+ // uart_clock = clock / div
+ // brd = ibrd.fbrd = uart_clock / (oversampling * baud)"
+ //
+ // It is tempting to rearrange the equation such that there is only a single division in
+ // order to reduce error. However this is wrong since the denominator ends up being too
+ // small to represent in 6 fraction bits. This means that FBRD would always be 0.
+ //
+ // Calculations are done in a U16F6 format. However the fixed crate has no such representation.
+ // U26F6 is used since it has the same number of fractional bits and we verify at the end that
+ // the integer part did not overflow.
+ let clock = U26F6::from_num(clock);
+ let div = U26F6::from_num(div);
+ let oversampling = U26F6::from_num(oversampling);
+ let baud = U26F6::from_num(baud);
+
+ let uart_clock = clock.checked_div(div)?;
+
+ // oversampling * baud
+ let denom = oversampling.checked_mul(baud)?;
+ // uart_clock / (oversampling * baud)
+ let brd = uart_clock.checked_div(denom)?;
+
+ // Checked is used to determine overflow in the 10 most singificant bits since the
+ // actual representation of BRD is U16F6.
+ let ibrd = brd.checked_to_num::()?;
+
+ // We need to scale FBRD's representation to an integer.
+ let fbrd_scale = U26F6::from_num(2_u32.checked_pow(U26F6::FRAC_NBITS)?);
+
+ // It is suggested that 0.5 is added to ensure that any fractional parts round up to the next
+ // integer. If it doesn't round up then it'll get discarded which is okay.
+ let half = U26F6::from_num(1) / U26F6::from_num(2);
+ // fbrd = INT(((FRAC(BRD) * 64) + 0.5))
+ let fbrd = brd
+ .frac()
+ .checked_mul(fbrd_scale)?
+ .checked_add(half)?
+ .checked_to_num::()?;
+
+ Some((ibrd, fbrd))
+}
+
+fn read_with_error(r: Regs) -> Result {
+ let rx = r.rxdata().read();
+
+ if rx.frmerr() {
+ return Err(Error::Framing);
+ } else if rx.parerr() {
+ return Err(Error::Parity);
+ } else if rx.brkerr() {
+ return Err(Error::Break);
+ } else if rx.ovrerr() {
+ return Err(Error::Overrun);
+ } else if rx.nerr() {
+ return Err(Error::Noise);
+ }
+
+ Ok(rx.data())
+}
+
+pub(crate) trait SealedInstance {
+ fn info() -> &'static Info;
+ fn state() -> &'static State;
+}
+
+macro_rules! impl_uart_instance {
+ ($instance: ident) => {
+ impl crate::uart::SealedInstance for crate::peripherals::$instance {
+ fn info() -> &'static crate::uart::Info {
+ use crate::interrupt::typelevel::Interrupt;
+ use crate::uart::Info;
+
+ const INFO: Info = Info {
+ regs: crate::pac::$instance,
+ interrupt: crate::interrupt::typelevel::$instance::IRQ,
+ };
+ &INFO
+ }
+
+ fn state() -> &'static crate::uart::State {
+ use crate::interrupt::typelevel::Interrupt;
+ use crate::uart::State;
+
+ static STATE: State = State {
+ clock: core::sync::atomic::AtomicU32::new(0),
+ };
+ &STATE
+ }
+ }
+
+ impl crate::uart::Instance for crate::peripherals::$instance {
+ type Interrupt = crate::interrupt::typelevel::$instance;
+ }
+ };
+}
+
+macro_rules! impl_uart_tx_pin {
+ ($instance: ident, $pin: ident, $pf: expr) => {
+ impl crate::uart::TxPin for crate::peripherals::$pin {
+ fn pf_num(&self) -> u8 {
+ $pf
+ }
+ }
+ };
+}
+
+macro_rules! impl_uart_rx_pin {
+ ($instance: ident, $pin: ident, $pf: expr) => {
+ impl crate::uart::RxPin for crate::peripherals::$pin {
+ fn pf_num(&self) -> u8 {
+ $pf
+ }
+ }
+ };
+}
+
+macro_rules! impl_uart_cts_pin {
+ ($instance: ident, $pin: ident, $pf: expr) => {
+ impl crate::uart::CtsPin for crate::peripherals::$pin {
+ fn pf_num(&self) -> u8 {
+ $pf
+ }
+ }
+ };
+}
+
+macro_rules! impl_uart_rts_pin {
+ ($instance: ident, $pin: ident, $pf: expr) => {
+ impl crate::uart::RtsPin for crate::peripherals::$pin {
+ fn pf_num(&self) -> u8 {
+ $pf
+ }
+ }
+ };
+}
+
+#[cfg(test)]
+mod tests {
+ use super::calculate_brd;
+
+ /// This is a smoke test based on the example in SLAU 846 section 18.2.3.4.
+ #[test]
+ fn datasheet() {
+ let brd = calculate_brd(40_000_000, 1, 19200, 16);
+
+ assert!(matches!(brd, Some((130, 13))));
+ }
+}
diff --git a/examples/mspm0c1104/Cargo.toml b/examples/mspm0c1104/Cargo.toml
index 7c382482a..ba64a578d 100644
--- a/examples/mspm0c1104/Cargo.toml
+++ b/examples/mspm0c1104/Cargo.toml
@@ -5,7 +5,7 @@ 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-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = ["mspm0c110x", "defmt", "rt", "time-driver-any"] }
embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["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"] }
diff --git a/examples/mspm0c1104/src/bin/uart.rs b/examples/mspm0c1104/src/bin/uart.rs
new file mode 100644
index 000000000..da611aaac
--- /dev/null
+++ b/examples/mspm0c1104/src/bin/uart.rs
@@ -0,0 +1,35 @@
+//! Example of using blocking uart
+//!
+//! This uses the virtual COM port provided on the LP-MSPM0C1104 board.
+
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_mspm0::uart::{Config, Uart};
+use {defmt_rtt as _, panic_halt as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) -> ! {
+ info!("Hello world!");
+
+ let p = embassy_mspm0::init(Default::default());
+
+ let instance = p.UART0;
+ let tx = p.PA27;
+ let rx = p.PA26;
+
+ let config = Config::default();
+ let mut uart = unwrap!(Uart::new_blocking(instance, rx, tx, config));
+
+ unwrap!(uart.blocking_write(b"Hello Embassy World!\r\n"));
+ info!("wrote Hello, starting echo");
+
+ let mut buf = [0u8; 1];
+
+ loop {
+ unwrap!(uart.blocking_read(&mut buf));
+ unwrap!(uart.blocking_write(&buf));
+ }
+}
diff --git a/examples/mspm0g3507/Cargo.toml b/examples/mspm0g3507/Cargo.toml
index 9bc82151c..f6fed091d 100644
--- a/examples/mspm0g3507/Cargo.toml
+++ b/examples/mspm0g3507/Cargo.toml
@@ -5,7 +5,7 @@ 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-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = ["mspm0g350x", "defmt", "rt", "time-driver-any"] }
embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["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"] }
diff --git a/examples/mspm0g3507/src/bin/uart.rs b/examples/mspm0g3507/src/bin/uart.rs
new file mode 100644
index 000000000..7e7e6db0e
--- /dev/null
+++ b/examples/mspm0g3507/src/bin/uart.rs
@@ -0,0 +1,35 @@
+//! Example of using blocking uart
+//!
+//! This uses the virtual COM port provided on the LP-MSPM0G3507 board.
+
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_mspm0::uart::{Config, Uart};
+use {defmt_rtt as _, panic_halt as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) -> ! {
+ info!("Hello world!");
+
+ let p = embassy_mspm0::init(Default::default());
+
+ let instance = p.UART0;
+ let tx = p.PA10;
+ let rx = p.PA11;
+
+ let config = Config::default();
+ let mut uart = unwrap!(Uart::new_blocking(instance, rx, tx, config));
+
+ unwrap!(uart.blocking_write(b"Hello Embassy World!\r\n"));
+ info!("wrote Hello, starting echo");
+
+ let mut buf = [0u8; 1];
+
+ loop {
+ unwrap!(uart.blocking_read(&mut buf));
+ unwrap!(uart.blocking_write(&buf));
+ }
+}
diff --git a/examples/mspm0g3519/Cargo.toml b/examples/mspm0g3519/Cargo.toml
index a28ce2f11..1662e1f8d 100644
--- a/examples/mspm0g3519/Cargo.toml
+++ b/examples/mspm0g3519/Cargo.toml
@@ -5,7 +5,7 @@ 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-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = ["mspm0g351x", "defmt", "rt", "time-driver-any"] }
embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["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"] }
diff --git a/examples/mspm0g3519/src/bin/uart.rs b/examples/mspm0g3519/src/bin/uart.rs
new file mode 100644
index 000000000..498377c61
--- /dev/null
+++ b/examples/mspm0g3519/src/bin/uart.rs
@@ -0,0 +1,35 @@
+//! Example of using blocking uart
+//!
+//! This uses the virtual COM port provided on the LP-MSPM0G3519 board.
+
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_mspm0::uart::{Config, Uart};
+use {defmt_rtt as _, panic_halt as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) -> ! {
+ info!("Hello world!");
+
+ let p = embassy_mspm0::init(Default::default());
+
+ let instance = p.UART0;
+ let tx = p.PA10;
+ let rx = p.PA11;
+
+ let config = Config::default();
+ let mut uart = unwrap!(Uart::new_blocking(instance, rx, tx, config));
+
+ unwrap!(uart.blocking_write(b"Hello Embassy World!\r\n"));
+ info!("wrote Hello, starting echo");
+
+ let mut buf = [0u8; 1];
+
+ loop {
+ unwrap!(uart.blocking_read(&mut buf));
+ unwrap!(uart.blocking_write(&buf));
+ }
+}
diff --git a/examples/mspm0l1306/Cargo.toml b/examples/mspm0l1306/Cargo.toml
index 3962eb156..609b3f205 100644
--- a/examples/mspm0l1306/Cargo.toml
+++ b/examples/mspm0l1306/Cargo.toml
@@ -5,7 +5,7 @@ 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-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = ["mspm0l130x", "defmt", "rt", "time-driver-any"] }
embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["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"] }
diff --git a/examples/mspm0l1306/src/bin/uart.rs b/examples/mspm0l1306/src/bin/uart.rs
new file mode 100644
index 000000000..95c56fdd3
--- /dev/null
+++ b/examples/mspm0l1306/src/bin/uart.rs
@@ -0,0 +1,35 @@
+//! Example of using blocking uart
+//!
+//! This uses the virtual COM port provided on the LP-MSPM0L1306 board.
+
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_mspm0::uart::{Config, Uart};
+use {defmt_rtt as _, panic_halt as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) -> ! {
+ info!("Hello world!");
+
+ let p = embassy_mspm0::init(Default::default());
+
+ let instance = p.UART0;
+ let tx = p.PA8;
+ let rx = p.PA9;
+
+ let config = Config::default();
+ let mut uart = unwrap!(Uart::new_blocking(instance, rx, tx, config));
+
+ unwrap!(uart.blocking_write(b"Hello Embassy World!\r\n"));
+ info!("wrote Hello, starting echo");
+
+ let mut buf = [0u8; 1];
+
+ loop {
+ unwrap!(uart.blocking_read(&mut buf));
+ unwrap!(uart.blocking_write(&buf));
+ }
+}
diff --git a/examples/mspm0l2228/Cargo.toml b/examples/mspm0l2228/Cargo.toml
index abebcc00d..bbca011a1 100644
--- a/examples/mspm0l2228/Cargo.toml
+++ b/examples/mspm0l2228/Cargo.toml
@@ -5,7 +5,7 @@ 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-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = ["mspm0l222x", "defmt", "rt", "time-driver-any"] }
embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["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"] }
diff --git a/examples/mspm0l2228/src/bin/uart.rs b/examples/mspm0l2228/src/bin/uart.rs
new file mode 100644
index 000000000..a266add47
--- /dev/null
+++ b/examples/mspm0l2228/src/bin/uart.rs
@@ -0,0 +1,35 @@
+//! Example of using blocking uart
+//!
+//! This uses the virtual COM port provided on the LP-MSPM0L2228 board.
+
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_mspm0::uart::{Config, Uart};
+use {defmt_rtt as _, panic_halt as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) -> ! {
+ info!("Hello world!");
+
+ let p = embassy_mspm0::init(Default::default());
+
+ let instance = p.UART0;
+ let tx = p.PA10;
+ let rx = p.PA11;
+
+ let config = Config::default();
+ let mut uart = unwrap!(Uart::new_blocking(instance, rx, tx, config));
+
+ unwrap!(uart.blocking_write(b"Hello Embassy World!\r\n"));
+ info!("wrote Hello, starting echo");
+
+ let mut buf = [0u8; 1];
+
+ loop {
+ unwrap!(uart.blocking_read(&mut buf));
+ unwrap!(uart.blocking_write(&buf));
+ }
+}