use core::marker::PhantomData;
use embedded_hal_nb::nb;
use crate::baud::compute_baud;
const CTLW0: usize = 0x00; const BRW: usize = 0x06; const MCTLW: usize = 0x08; const STATW: usize = 0x0A; const RXBUF: usize = 0x0C; const TXBUF: usize = 0x0E; #[cfg_attr(not(feature = "critical-section"), allow(dead_code))]
const IE: usize = 0x1A; const IFG: usize = 0x1C; const IV: usize = 0x1E;
const UCSWRST: u16 = 1 << 0; const UCSSEL_SHIFT: u16 = 6; const UCSPB: u16 = 1 << 11; const UC7BIT: u16 = 1 << 12; const UCPAR: u16 = 1 << 14; const UCPEN: u16 = 1 << 15;
const UCOS16: u16 = 1 << 0;
const UCBUSY: u16 = 1 << 0; const UCRXERR: u16 = 1 << 2; const UCBRK: u16 = 1 << 3; const UCPE: u16 = 1 << 4; const UCOE: u16 = 1 << 5; const UCFE: u16 = 1 << 6;
#[cfg_attr(not(feature = "critical-section"), allow(dead_code))]
const UCRXIE: u16 = 1 << 0;
const UCRXIFG: u16 = 1 << 0; const UCTXIFG: u16 = 1 << 1;
const P2SEL0: usize = 0x020B;
const P2SEL1: usize = 0x020D;
#[inline(always)]
unsafe fn read_reg(addr: usize) -> u16 {
(addr as *const u16).read_volatile()
}
#[inline(always)]
unsafe fn write_reg(addr: usize, val: u16) {
(addr as *mut u16).write_volatile(val);
}
#[inline(always)]
unsafe fn set_bits_u8(addr: usize, mask: u8) {
let p = addr as *mut u8;
p.write_volatile(p.read_volatile() | mask);
}
#[inline(always)]
unsafe fn clear_bits_u8(addr: usize, mask: u8) {
let p = addr as *mut u8;
p.write_volatile(p.read_volatile() & !mask);
}
mod sealed {
pub trait Sealed {}
}
pub trait Instance: sealed::Sealed {
const BASE: usize;
const PIN_MASK: u8;
const DMA_RX_TRIGGER: crate::dma::TriggerSource;
const DMA_TX_TRIGGER: crate::dma::TriggerSource;
}
pub struct UsciA0;
pub struct UsciA1;
impl sealed::Sealed for UsciA0 {}
impl Instance for UsciA0 {
const BASE: usize = 0x05C0;
const PIN_MASK: u8 = (1 << 0) | (1 << 1); const DMA_RX_TRIGGER: crate::dma::TriggerSource = crate::dma::TriggerSource::UcA0Rx;
const DMA_TX_TRIGGER: crate::dma::TriggerSource = crate::dma::TriggerSource::UcA0Tx;
}
impl sealed::Sealed for UsciA1 {}
impl Instance for UsciA1 {
const BASE: usize = 0x05E0;
const PIN_MASK: u8 = (1 << 5) | (1 << 6); const DMA_RX_TRIGGER: crate::dma::TriggerSource = crate::dma::TriggerSource::UcA1Rx;
const DMA_TX_TRIGGER: crate::dma::TriggerSource = crate::dma::TriggerSource::UcA1Tx;
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ClockSource {
Aclk = 1,
Smclk = 2,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Parity {
None,
Even,
Odd,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum StopBits {
One,
Two,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum DataBits {
Seven,
Eight,
}
#[derive(Clone, Copy, Debug)]
pub struct Config {
pub clock_source: ClockSource,
pub clock_freq: u32,
pub baud: u32,
pub parity: Parity,
pub stop_bits: StopBits,
pub data_bits: DataBits,
}
impl Config {
pub fn new(clock_freq: u32) -> Self {
Config {
clock_source: ClockSource::Smclk,
clock_freq,
baud: 9600,
parity: Parity::None,
stop_bits: StopBits::One,
data_bits: DataBits::Eight,
}
}
pub fn baud(mut self, baud: u32) -> Self {
self.baud = baud;
self
}
pub fn clock_source(mut self, src: ClockSource) -> Self {
self.clock_source = src;
self
}
pub fn parity(mut self, parity: Parity) -> Self {
self.parity = parity;
self
}
pub fn stop_bits(mut self, stop_bits: StopBits) -> Self {
self.stop_bits = stop_bits;
self
}
pub fn data_bits(mut self, data_bits: DataBits) -> Self {
self.data_bits = data_bits;
self
}
}
impl Default for Config {
fn default() -> Self {
Config::new(1_000_000)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Error {
Overrun,
Parity,
Framing,
Break,
}
impl embedded_hal_nb::serial::Error for Error {
fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
use embedded_hal_nb::serial::ErrorKind;
match self {
Error::Overrun => ErrorKind::Overrun,
Error::Parity => ErrorKind::Parity,
Error::Framing => ErrorKind::FrameFormat,
Error::Break => ErrorKind::Other,
}
}
}
impl embedded_io::Error for Error {
fn kind(&self) -> embedded_io::ErrorKind {
embedded_io::ErrorKind::Other
}
}
pub struct Serial<USCI> {
_usci: PhantomData<USCI>,
}
pub struct Tx<USCI> {
_usci: PhantomData<USCI>,
}
pub struct Rx<USCI> {
_usci: PhantomData<USCI>,
}
impl<USCI: Instance> Serial<USCI> {
fn init(config: Config) -> Self {
let base = USCI::BASE;
let regs = compute_baud(config.clock_freq, config.baud);
let mut ctlw0 = UCSWRST | ((config.clock_source as u16) << UCSSEL_SHIFT);
match config.parity {
Parity::None => {}
Parity::Even => ctlw0 |= UCPEN | UCPAR,
Parity::Odd => ctlw0 |= UCPEN,
}
if let StopBits::Two = config.stop_bits {
ctlw0 |= UCSPB;
}
if let DataBits::Seven = config.data_bits {
ctlw0 |= UC7BIT;
}
let mut mctlw = ((regs.ucbrs as u16) << 8) | ((regs.ucbrf as u16) << 4);
if regs.oversampling {
mctlw |= UCOS16;
}
unsafe {
write_reg(base + CTLW0, ctlw0);
write_reg(base + BRW, regs.ucbr);
write_reg(base + MCTLW, mctlw);
set_bits_u8(P2SEL1, USCI::PIN_MASK);
clear_bits_u8(P2SEL0, USCI::PIN_MASK);
write_reg(base + CTLW0, ctlw0 & !UCSWRST);
}
Serial {
_usci: PhantomData,
}
}
pub fn split(self) -> (Tx<USCI>, Rx<USCI>) {
(
Tx {
_usci: PhantomData,
},
Rx {
_usci: PhantomData,
},
)
}
}
#[cfg(feature = "critical-section")]
impl<USCI: Instance> Rx<USCI> {
pub fn enable_rx_interrupt(&mut self) {
critical_section::with(|_| unsafe {
let addr = USCI::BASE + IE;
write_reg(addr, read_reg(addr) | UCRXIE);
});
}
pub fn disable_rx_interrupt(&mut self) {
critical_section::with(|_| unsafe {
let addr = USCI::BASE + IE;
write_reg(addr, read_reg(addr) & !UCRXIE);
});
}
}
#[cfg(feature = "critical-section")]
impl<USCI: Instance> Tx<USCI> {
pub fn write_all_dma<const N: u8>(
&mut self,
ch: &mut crate::dma::Channel<N>,
buf: &[u8],
) -> Result<(), Error> {
let Some((&first, rest)) = buf.split_first() else {
return Ok(());
};
if rest.is_empty() {
return nb::block!(try_write_byte::<USCI>(first));
}
while !tx_ready::<USCI>() {}
unsafe {
ch.arm_single_bytes(
USCI::DMA_TX_TRIGGER,
rest.as_ptr(),
crate::dma::AddrMode::Increment,
(USCI::BASE + TXBUF) as *mut u8,
crate::dma::AddrMode::Fixed,
rest.len() as u16,
);
write_reg(USCI::BASE + TXBUF, first as u16);
}
ch.wait_done();
Ok(())
}
}
#[cfg(feature = "critical-section")]
impl<USCI: Instance> Rx<USCI> {
pub unsafe fn start_read_dma<const N: u8>(
&mut self,
ch: &mut crate::dma::Channel<N>,
buf: &mut [u8],
) {
while rx_ready::<USCI>() {
let _ = try_read_byte::<USCI>();
}
ch.arm_single_bytes(
USCI::DMA_RX_TRIGGER,
(USCI::BASE + RXBUF) as *const u8,
crate::dma::AddrMode::Fixed,
buf.as_mut_ptr(),
crate::dma::AddrMode::Increment,
buf.len() as u16,
);
}
pub fn read_exact_dma<const N: u8>(
&mut self,
ch: &mut crate::dma::Channel<N>,
buf: &mut [u8],
) -> Result<(), Error> {
let mut filled = 0;
while filled < buf.len() {
while filled < buf.len() && rx_ready::<USCI>() {
match try_read_byte::<USCI>() {
Ok(b) => {
buf[filled] = b;
filled += 1;
}
Err(nb::Error::WouldBlock) => break,
Err(nb::Error::Other(e)) => return Err(e),
}
}
if filled == buf.len() {
break;
}
let count = (buf.len() - filled) as u16;
unsafe {
ch.arm_single_bytes(
USCI::DMA_RX_TRIGGER,
(USCI::BASE + RXBUF) as *const u8,
crate::dma::AddrMode::Fixed,
buf.as_mut_ptr().add(filled),
crate::dma::AddrMode::Increment,
count,
);
}
if rx_ready::<USCI>() && ch.remaining() == count {
ch.disarm();
continue; }
ch.wait_done();
filled = buf.len();
}
Ok(())
}
}
#[cfg(feature = "critical-section")]
pub struct DmaTx<USCI, const N: u8> {
tx: Tx<USCI>,
ch: crate::dma::Channel<N>,
}
#[cfg(feature = "critical-section")]
impl<USCI: Instance> Tx<USCI> {
pub fn with_dma<const N: u8>(self, ch: crate::dma::Channel<N>) -> DmaTx<USCI, N> {
DmaTx { tx: self, ch }
}
}
#[cfg(feature = "critical-section")]
impl<USCI: Instance, const N: u8> DmaTx<USCI, N> {
pub fn release(self) -> (Tx<USCI>, crate::dma::Channel<N>) {
(self.tx, self.ch)
}
}
#[cfg(feature = "critical-section")]
impl<USCI: Instance, const N: u8> embedded_io::ErrorType for DmaTx<USCI, N> {
type Error = Error;
}
#[cfg(feature = "critical-section")]
impl<USCI: Instance, const N: u8> embedded_io::Write for DmaTx<USCI, N> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
self.tx.write_all_dma(&mut self.ch, buf)?;
Ok(buf.len())
}
fn flush(&mut self) -> Result<(), Self::Error> {
nb::block!(try_flush::<USCI>())
}
}
pub fn read_iv<USCI: Instance>() -> u16 {
unsafe { read_reg(USCI::BASE + IV) }
}
pub fn isr_read_byte<USCI: Instance>() -> nb::Result<u8, Error> {
try_read_byte::<USCI>()
}
pub trait SerialExt {
type Instance: Instance;
fn into_uart(self, config: Config) -> Serial<Self::Instance>;
}
impl SerialExt for pac::UsciA0UartMode {
type Instance = UsciA0;
fn into_uart(self, config: Config) -> Serial<UsciA0> {
Serial::<UsciA0>::init(config)
}
}
impl SerialExt for pac::UsciA1UartMode {
type Instance = UsciA1;
fn into_uart(self, config: Config) -> Serial<UsciA1> {
Serial::<UsciA1>::init(config)
}
}
fn try_write_byte<USCI: Instance>(byte: u8) -> nb::Result<(), Error> {
let base = USCI::BASE;
if unsafe { read_reg(base + IFG) } & UCTXIFG == 0 {
return Err(nb::Error::WouldBlock);
}
unsafe { write_reg(base + TXBUF, byte as u16) };
Ok(())
}
fn try_flush<USCI: Instance>() -> nb::Result<(), Error> {
let base = USCI::BASE;
if unsafe { read_reg(base + STATW) } & UCBUSY != 0 {
return Err(nb::Error::WouldBlock);
}
Ok(())
}
fn try_read_byte<USCI: Instance>() -> nb::Result<u8, Error> {
let base = USCI::BASE;
if unsafe { read_reg(base + IFG) } & UCRXIFG == 0 {
return Err(nb::Error::WouldBlock);
}
let status = unsafe { read_reg(base + STATW) };
let byte = unsafe { read_reg(base + RXBUF) } as u8;
if status & UCRXERR != 0 {
let err = if status & UCOE != 0 {
Error::Overrun
} else if status & UCFE != 0 {
Error::Framing
} else if status & UCPE != 0 {
Error::Parity
} else if status & UCBRK != 0 {
Error::Break
} else {
Error::Framing
};
return Err(nb::Error::Other(err));
}
Ok(byte)
}
#[inline]
fn tx_ready<USCI: Instance>() -> bool {
(unsafe { read_reg(USCI::BASE + IFG) }) & UCTXIFG != 0
}
#[inline]
fn rx_ready<USCI: Instance>() -> bool {
(unsafe { read_reg(USCI::BASE + IFG) }) & UCRXIFG != 0
}
impl<USCI: Instance> embedded_hal_nb::serial::ErrorType for Serial<USCI> {
type Error = Error;
}
impl<USCI: Instance> embedded_hal_nb::serial::ErrorType for Tx<USCI> {
type Error = Error;
}
impl<USCI: Instance> embedded_hal_nb::serial::ErrorType for Rx<USCI> {
type Error = Error;
}
impl<USCI: Instance> embedded_hal_nb::serial::Write<u8> for Serial<USCI> {
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
try_write_byte::<USCI>(word)
}
fn flush(&mut self) -> nb::Result<(), Self::Error> {
try_flush::<USCI>()
}
}
impl<USCI: Instance> embedded_hal_nb::serial::Write<u8> for Tx<USCI> {
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
try_write_byte::<USCI>(word)
}
fn flush(&mut self) -> nb::Result<(), Self::Error> {
try_flush::<USCI>()
}
}
impl<USCI: Instance> embedded_hal_nb::serial::Read<u8> for Serial<USCI> {
fn read(&mut self) -> nb::Result<u8, Self::Error> {
try_read_byte::<USCI>()
}
}
impl<USCI: Instance> embedded_hal_nb::serial::Read<u8> for Rx<USCI> {
fn read(&mut self) -> nb::Result<u8, Self::Error> {
try_read_byte::<USCI>()
}
}
impl<USCI: Instance> embedded_io::ErrorType for Serial<USCI> {
type Error = Error;
}
impl<USCI: Instance> embedded_io::ErrorType for Tx<USCI> {
type Error = Error;
}
impl<USCI: Instance> embedded_io::ErrorType for Rx<USCI> {
type Error = Error;
}
fn io_write<USCI: Instance>(buf: &[u8]) -> Result<usize, Error> {
for &byte in buf {
nb::block!(try_write_byte::<USCI>(byte))?;
}
Ok(buf.len())
}
fn io_read<USCI: Instance>(buf: &mut [u8]) -> Result<usize, Error> {
if buf.is_empty() {
return Ok(0);
}
buf[0] = nb::block!(try_read_byte::<USCI>())?;
let mut n = 1;
while n < buf.len() {
match try_read_byte::<USCI>() {
Ok(b) => {
buf[n] = b;
n += 1;
}
Err(nb::Error::WouldBlock) => break,
Err(nb::Error::Other(e)) => return Err(e),
}
}
Ok(n)
}
impl<USCI: Instance> embedded_io::Write for Serial<USCI> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
io_write::<USCI>(buf)
}
fn flush(&mut self) -> Result<(), Self::Error> {
nb::block!(try_flush::<USCI>())
}
}
impl<USCI: Instance> embedded_io::Write for Tx<USCI> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
io_write::<USCI>(buf)
}
fn flush(&mut self) -> Result<(), Self::Error> {
nb::block!(try_flush::<USCI>())
}
}
impl<USCI: Instance> embedded_io::Read for Serial<USCI> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
io_read::<USCI>(buf)
}
}
impl<USCI: Instance> embedded_io::Read for Rx<USCI> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
io_read::<USCI>(buf)
}
}
impl<USCI: Instance> embedded_io::WriteReady for Serial<USCI> {
fn write_ready(&mut self) -> Result<bool, Self::Error> {
Ok(tx_ready::<USCI>())
}
}
impl<USCI: Instance> embedded_io::WriteReady for Tx<USCI> {
fn write_ready(&mut self) -> Result<bool, Self::Error> {
Ok(tx_ready::<USCI>())
}
}
impl<USCI: Instance> embedded_io::ReadReady for Serial<USCI> {
fn read_ready(&mut self) -> Result<bool, Self::Error> {
Ok(rx_ready::<USCI>())
}
}
impl<USCI: Instance> embedded_io::ReadReady for Rx<USCI> {
fn read_ready(&mut self) -> Result<bool, Self::Error> {
Ok(rx_ready::<USCI>())
}
}