use core::marker::PhantomData;
use crate::pac;
const CTLW0: usize = 0x00; const BRW: usize = 0x06; const RXBUF: usize = 0x0C; const TXBUF: usize = 0x0E;
const UCSWRST: u16 = 1 << 0; const UCSSEL_SMCLK: u16 = 0b10 << 6; const UCSYNC: u16 = 1 << 8; const UCMST: u16 = 1 << 11; const UCMSB: u16 = 1 << 13; const UCCKPL: u16 = 1 << 14; const UCCKPH: u16 = 1 << 15;
const UCRXIFG: u16 = 1 << 0; const UCTXIFG: u16 = 1 << 1;
const P1SEL0: usize = 0x020A;
const P1SEL1: usize = 0x020C;
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 IFG: usize;
const P1_PINS: u8;
const P2_PINS: u8;
const DMA_RX_TRIGGER: crate::dma::TriggerSource;
const DMA_TX_TRIGGER: crate::dma::TriggerSource;
}
pub struct UsciA0;
pub struct UsciA1;
pub struct UsciB0;
impl sealed::Sealed for UsciA0 {}
impl Instance for UsciA0 {
const BASE: usize = 0x05C0;
const IFG: usize = 0x1C;
const P1_PINS: u8 = 1 << 5; const P2_PINS: 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 IFG: usize = 0x1C;
const P1_PINS: u8 = 0;
const P2_PINS: u8 = (1 << 4) | (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;
}
impl sealed::Sealed for UsciB0 {}
impl Instance for UsciB0 {
const BASE: usize = 0x0640;
const IFG: usize = 0x2C;
const P1_PINS: u8 = (1 << 6) | (1 << 7); const P2_PINS: u8 = 1 << 2; const DMA_RX_TRIGGER: crate::dma::TriggerSource = crate::dma::TriggerSource::UcB0Rx0;
const DMA_TX_TRIGGER: crate::dma::TriggerSource = crate::dma::TriggerSource::UcB0Tx0;
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum SpiMode {
Mode0,
Mode1,
Mode2,
Mode3,
}
impl SpiMode {
fn ckph_ckpl(self) -> (bool, bool) {
match self {
SpiMode::Mode0 => (true, false),
SpiMode::Mode1 => (false, false),
SpiMode::Mode2 => (true, true),
SpiMode::Mode3 => (false, true),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum BitOrder {
MsbFirst,
LsbFirst,
}
#[derive(Clone, Copy, Debug)]
pub struct Config {
pub clock_freq: u32,
pub bit_rate: u32,
pub mode: SpiMode,
pub bit_order: BitOrder,
}
impl Config {
pub fn new(clock_freq: u32) -> Self {
Config {
clock_freq,
bit_rate: 1_000_000,
mode: SpiMode::Mode0,
bit_order: BitOrder::MsbFirst,
}
}
pub fn bit_rate(mut self, bit_rate: u32) -> Self {
self.bit_rate = bit_rate;
self
}
pub fn mode(mut self, mode: SpiMode) -> Self {
self.mode = mode;
self
}
pub fn bit_order(mut self, bit_order: BitOrder) -> Self {
self.bit_order = bit_order;
self
}
fn prescaler(&self) -> u16 {
let div = self.clock_freq / self.bit_rate.max(1);
div.clamp(1, u16::MAX as u32) as u16
}
}
pub struct Spi<USCI> {
_usci: PhantomData<USCI>,
_not_send: PhantomData<*const ()>,
}
impl<USCI: Instance> Spi<USCI> {
fn init(config: Config) -> Self {
let base = USCI::BASE;
let (ckph, ckpl) = config.mode.ckph_ckpl();
let mut ctlw0 = UCSWRST | UCSYNC | UCMST | UCSSEL_SMCLK;
if let BitOrder::MsbFirst = config.bit_order {
ctlw0 |= UCMSB;
}
if ckph {
ctlw0 |= UCCKPH;
}
if ckpl {
ctlw0 |= UCCKPL;
}
unsafe {
write_reg(base + CTLW0, ctlw0);
write_reg(base + BRW, config.prescaler() as u16);
if USCI::P1_PINS != 0 {
set_bits_u8(P1SEL1, USCI::P1_PINS);
clear_bits_u8(P1SEL0, USCI::P1_PINS);
}
if USCI::P2_PINS != 0 {
set_bits_u8(P2SEL1, USCI::P2_PINS);
clear_bits_u8(P2SEL0, USCI::P2_PINS);
}
write_reg(base + CTLW0, ctlw0 & !UCSWRST);
}
Spi {
_usci: PhantomData,
_not_send: PhantomData,
}
}
pub fn transfer_byte(&mut self, byte: u8) -> u8 {
let base = USCI::BASE;
unsafe {
while read_reg(base + USCI::IFG) & UCTXIFG == 0 {}
write_reg(base + TXBUF, byte as u16);
while read_reg(base + USCI::IFG) & UCRXIFG == 0 {}
read_reg(base + RXBUF) as u8
}
}
}
pub trait SpiExt {
type Instance: Instance;
fn into_spi(self, config: Config) -> Spi<Self::Instance>;
}
impl SpiExt for pac::UsciB0SpiMode {
type Instance = UsciB0;
fn into_spi(self, config: Config) -> Spi<UsciB0> {
Spi::<UsciB0>::init(config)
}
}
impl SpiExt for pac::UsciA0SpiMode {
type Instance = UsciA0;
fn into_spi(self, config: Config) -> Spi<UsciA0> {
Spi::<UsciA0>::init(config)
}
}
impl SpiExt for pac::UsciA1SpiMode {
type Instance = UsciA1;
fn into_spi(self, config: Config) -> Spi<UsciA1> {
Spi::<UsciA1>::init(config)
}
}
impl<USCI: Instance> embedded_hal::spi::ErrorType for Spi<USCI> {
type Error = core::convert::Infallible;
}
impl<USCI: Instance> embedded_hal::spi::SpiBus<u8> for Spi<USCI> {
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
for w in words.iter_mut() {
*w = self.transfer_byte(0x00);
}
Ok(())
}
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
for &w in words.iter() {
self.transfer_byte(w);
}
Ok(())
}
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
let n = read.len().max(write.len());
for i in 0..n {
let tx = write.get(i).copied().unwrap_or(0x00);
let rx = self.transfer_byte(tx);
if let Some(slot) = read.get_mut(i) {
*slot = rx;
}
}
Ok(())
}
fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
for w in words.iter_mut() {
*w = self.transfer_byte(*w);
}
Ok(())
}
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
#[cfg(feature = "critical-section")]
unsafe fn dma_clock_bytes<USCI: Instance, const RN: u8, const TN: u8>(
rx_ch: &mut crate::dma::Channel<RN>,
tx_ch: &mut crate::dma::Channel<TN>,
first: u8,
tx_rest: *const u8,
tx_mode: crate::dma::AddrMode,
rx_dst: *mut u8,
rx_mode: crate::dma::AddrMode,
n: u16,
) {
let base = USCI::BASE;
while read_reg(base + USCI::IFG) & UCTXIFG == 0 {}
if read_reg(base + USCI::IFG) & UCRXIFG != 0 {
let _ = read_reg(base + RXBUF);
}
rx_ch.arm_single_bytes(
USCI::DMA_RX_TRIGGER,
(base + RXBUF) as *const u8,
crate::dma::AddrMode::Fixed,
rx_dst,
rx_mode,
n,
);
if n > 1 {
tx_ch.arm_single_bytes(
USCI::DMA_TX_TRIGGER,
tx_rest,
tx_mode,
(base + TXBUF) as *mut u8,
crate::dma::AddrMode::Fixed,
n - 1,
);
}
write_reg(base + TXBUF, first as u16);
rx_ch.wait_done();
if n > 1 {
tx_ch.wait_done(); }
}
#[cfg(feature = "critical-section")]
impl<USCI: Instance> Spi<USCI> {
pub fn write_dma<const RN: u8, const TN: u8>(
&mut self,
rx_ch: &mut crate::dma::Channel<RN>,
tx_ch: &mut crate::dma::Channel<TN>,
words: &[u8],
) {
let Some((&first, rest)) = words.split_first() else {
return;
};
let mut scratch = 0u8;
unsafe {
dma_clock_bytes::<USCI, RN, TN>(
rx_ch,
tx_ch,
first,
rest.as_ptr(),
crate::dma::AddrMode::Increment,
&mut scratch,
crate::dma::AddrMode::Fixed,
words.len() as u16,
);
}
}
pub fn read_dma<const RN: u8, const TN: u8>(
&mut self,
rx_ch: &mut crate::dma::Channel<RN>,
tx_ch: &mut crate::dma::Channel<TN>,
words: &mut [u8],
) {
let n = words.len();
if n == 0 {
return;
}
let dummy = 0u8;
unsafe {
dma_clock_bytes::<USCI, RN, TN>(
rx_ch,
tx_ch,
dummy,
&dummy,
crate::dma::AddrMode::Fixed,
words.as_mut_ptr(),
crate::dma::AddrMode::Increment,
n as u16,
);
}
}
pub fn transfer_in_place_dma<const RN: u8, const TN: u8>(
&mut self,
rx_ch: &mut crate::dma::Channel<RN>,
tx_ch: &mut crate::dma::Channel<TN>,
words: &mut [u8],
) {
let n = words.len();
let Some(&first) = words.first() else {
return;
};
unsafe {
dma_clock_bytes::<USCI, RN, TN>(
rx_ch,
tx_ch,
first,
words.as_ptr().add(1),
crate::dma::AddrMode::Increment,
words.as_mut_ptr(),
crate::dma::AddrMode::Increment,
n as u16,
);
}
}
pub fn transfer_dma<const RN: u8, const TN: u8>(
&mut self,
rx_ch: &mut crate::dma::Channel<RN>,
tx_ch: &mut crate::dma::Channel<TN>,
read: &mut [u8],
write: &[u8],
) {
let common = read.len().min(write.len());
if common > 0 {
unsafe {
dma_clock_bytes::<USCI, RN, TN>(
rx_ch,
tx_ch,
write[0],
write.as_ptr().add(1),
crate::dma::AddrMode::Increment,
read.as_mut_ptr(),
crate::dma::AddrMode::Increment,
common as u16,
);
}
}
if write.len() > common {
self.write_dma(rx_ch, tx_ch, &write[common..]);
} else if read.len() > common {
self.read_dma(rx_ch, tx_ch, &mut read[common..]);
}
}
pub fn with_dma<const RN: u8, const TN: u8>(
self,
rx_ch: crate::dma::Channel<RN>,
tx_ch: crate::dma::Channel<TN>,
) -> SpiDma<USCI, RN, TN> {
SpiDma {
spi: self,
rx_ch,
tx_ch,
}
}
}
#[cfg(feature = "critical-section")]
pub struct SpiDma<USCI, const RN: u8, const TN: u8> {
spi: Spi<USCI>,
rx_ch: crate::dma::Channel<RN>,
tx_ch: crate::dma::Channel<TN>,
}
#[cfg(feature = "critical-section")]
impl<USCI: Instance, const RN: u8, const TN: u8> SpiDma<USCI, RN, TN> {
pub fn release(self) -> (Spi<USCI>, crate::dma::Channel<RN>, crate::dma::Channel<TN>) {
(self.spi, self.rx_ch, self.tx_ch)
}
}
#[cfg(feature = "critical-section")]
impl<USCI: Instance, const RN: u8, const TN: u8> embedded_hal::spi::ErrorType
for SpiDma<USCI, RN, TN>
{
type Error = core::convert::Infallible;
}
#[cfg(feature = "critical-section")]
impl<USCI: Instance, const RN: u8, const TN: u8> embedded_hal::spi::SpiBus<u8>
for SpiDma<USCI, RN, TN>
{
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.spi.read_dma(&mut self.rx_ch, &mut self.tx_ch, words);
Ok(())
}
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
self.spi.write_dma(&mut self.rx_ch, &mut self.tx_ch, words);
Ok(())
}
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
self.spi
.transfer_dma(&mut self.rx_ch, &mut self.tx_ch, read, write);
Ok(())
}
fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.spi
.transfer_in_place_dma(&mut self.rx_ch, &mut self.tx_ch, words);
Ok(())
}
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}