use crate::iomuxc::{consts, lpspi};
use crate::ral;
pub use eh02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Tx,
Rx,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum BitOrder {
Msb,
Lsb,
}
impl Default for BitOrder {
fn default() -> Self {
BitOrder::Msb
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LpspiError {
FrameSize,
Fifo(Direction),
Busy,
NoData,
}
pub struct Transaction {
pub byte_swap: bool,
pub bit_order: BitOrder,
pub receive_data_mask: bool,
pub transmit_data_mask: bool,
pub continuous: bool,
pub continuing: bool,
frame_size: u16,
}
impl Transaction {
pub fn new_u32s(data: &[u32]) -> Result<Self, LpspiError> {
Transaction::new_words(data)
}
fn new_words<W>(data: &[W]) -> Result<Self, LpspiError> {
Transaction::new(8 * core::mem::size_of_val(data) as u16)
}
pub fn new(frame_size: u16) -> Result<Self, LpspiError> {
const MIN_FRAME_SIZE: u16 = 8;
const MAX_FRAME_SIZE: u16 = 1 << 12;
if (MIN_FRAME_SIZE..MAX_FRAME_SIZE).contains(&frame_size) {
Ok(Self {
byte_swap: false,
bit_order: Default::default(),
receive_data_mask: false,
transmit_data_mask: false,
frame_size: frame_size - 1,
continuing: false,
continuous: false,
})
} else {
Err(LpspiError::FrameSize)
}
}
}
fn set_spi_clock(source_clock_hz: u32, spi_clock_hz: u32, reg: &ral::lpspi::RegisterBlock) {
let mut div = source_clock_hz / spi_clock_hz;
if source_clock_hz / div > spi_clock_hz {
div += 1;
}
let div = div.saturating_sub(2).clamp(0, 255);
ral::write_reg!(
ral::lpspi,
reg,
CCR,
SCKDIV: div,
DBT: div / 2,
SCKPCS: 0x1F,
PCSSCK: 0x1F
);
}
pub struct Lpspi<P, const N: u8> {
lpspi: ral::lpspi::Instance<N>,
pins: P,
bit_order: BitOrder,
}
pub struct Pins<SDO, SDI, SCK, PCS0> {
pub sdo: SDO,
pub sdi: SDI,
pub sck: SCK,
pub pcs0: PCS0,
}
impl<SDO, SDI, SCK, PCS0, const N: u8> Lpspi<Pins<SDO, SDI, SCK, PCS0>, N>
where
SDO: lpspi::Pin<Module = consts::Const<N>, Signal = lpspi::Sdo>,
SDI: lpspi::Pin<Module = consts::Const<N>, Signal = lpspi::Sdi>,
SCK: lpspi::Pin<Module = consts::Const<N>, Signal = lpspi::Sck>,
PCS0: lpspi::Pin<Module = consts::Const<N>, Signal = lpspi::Pcs0>,
{
pub fn new(lpspi: ral::lpspi::Instance<N>, mut pins: Pins<SDO, SDI, SCK, PCS0>) -> Self {
lpspi::prepare(&mut pins.sdo);
lpspi::prepare(&mut pins.sdi);
lpspi::prepare(&mut pins.sck);
lpspi::prepare(&mut pins.pcs0);
Self::init(lpspi, pins)
}
}
impl<const N: u8> Lpspi<(), N> {
pub fn without_pins(lpspi: ral::lpspi::Instance<N>) -> Self {
Self::init(lpspi, ())
}
}
impl<P, const N: u8> Lpspi<P, N> {
pub const N: u8 = N;
fn init(lpspi: ral::lpspi::Instance<N>, pins: P) -> Self {
let mut spi = Lpspi {
lpspi,
pins,
bit_order: BitOrder::default(),
};
ral::write_reg!(ral::lpspi, spi.lpspi, CR, RST: RST_1);
ral::write_reg!(ral::lpspi, spi.lpspi, CR, RST: RST_0);
ral::write_reg!(
ral::lpspi,
spi.lpspi,
CFGR1,
MASTER: MASTER_1,
SAMPLE: SAMPLE_1
);
Disabled::new(&mut spi.lpspi).set_mode(MODE_0);
ral::write_reg!(ral::lpspi, spi.lpspi, FCR, RXWATER: 0xF, TXWATER: 0xF);
ral::write_reg!(ral::lpspi, spi.lpspi, CR, MEN: MEN_1);
spi
}
pub fn is_enabled(&self) -> bool {
ral::read_reg!(ral::lpspi, self.lpspi, CR, MEN == MEN_1)
}
pub fn set_enable(&mut self, enable: bool) {
ral::modify_reg!(ral::lpspi, self.lpspi, CR, MEN: enable as u32)
}
pub fn reset(&mut self) {
ral::modify_reg!(ral::lpspi, self.lpspi, CR, RST: RST_1);
while ral::read_reg!(ral::lpspi, self.lpspi, CR, RST == RST_1) {
ral::modify_reg!(ral::lpspi, self.lpspi, CR, RST: RST_0);
}
}
pub fn release(self) -> (ral::lpspi::Instance<N>, P) {
(self.lpspi, self.pins)
}
pub fn bit_order(&self) -> BitOrder {
self.bit_order
}
pub fn set_bit_order(&mut self, bit_order: BitOrder) {
self.bit_order = bit_order;
}
pub fn disabled<R>(&mut self, func: impl FnOnce(&mut Disabled<N>) -> R) -> R {
self.clear_fifos();
let mut disabled = Disabled::new(&mut self.lpspi);
func(&mut disabled)
}
pub fn status(&self) -> Status {
Status::from_bits_truncate(ral::read_reg!(ral::lpspi, self.lpspi, SR))
}
pub fn clear_status(&self, flags: Status) {
let flags = flags & Status::W1C;
ral::write_reg!(ral::lpspi, self.lpspi, SR, flags.bits());
}
pub fn interrupts(&self) -> Interrupts {
Interrupts::from_bits_truncate(ral::read_reg!(ral::lpspi, self.lpspi, IER))
}
pub fn set_interrupts(&self, interrupts: Interrupts) {
ral::write_reg!(ral::lpspi, self.lpspi, IER, interrupts.bits());
}
#[inline]
pub fn clear_fifo(&mut self, direction: Direction) {
match direction {
Direction::Tx => ral::modify_reg!(ral::lpspi, self.lpspi, CR, RTF: RTF_1),
Direction::Rx => ral::modify_reg!(ral::lpspi, self.lpspi, CR, RRF: RRF_1),
}
}
pub fn clear_fifos(&mut self) {
ral::modify_reg!(ral::lpspi, self.lpspi, CR, RTF: RTF_1, RRF: RRF_1);
}
#[inline]
pub fn watermark(&self, direction: Direction) -> u8 {
(match direction {
Direction::Rx => ral::read_reg!(ral::lpspi, self.lpspi, FCR, RXWATER),
Direction::Tx => ral::read_reg!(ral::lpspi, self.lpspi, FCR, TXWATER),
}) as u8
}
#[inline]
pub fn fifo_status(&self) -> FifoStatus {
let (rxcount, txcount) = ral::read_reg!(ral::lpspi, self.lpspi, FSR, RXCOUNT, TXCOUNT);
FifoStatus {
rxcount: rxcount as u16,
txcount: txcount as u16,
}
}
fn read_data_unchecked(&self) -> u32 {
ral::read_reg!(ral::lpspi, self.lpspi, RDR)
}
pub fn read_data(&mut self) -> Option<u32> {
if ral::read_reg!(ral::lpspi, self.lpspi, RSR, RXEMPTY == RXEMPTY_0) {
Some(self.read_data_unchecked())
} else {
None
}
}
fn recv_ok(&self) -> Result<(), LpspiError> {
let status = self.status();
if status.intersects(Status::RECEIVE_ERROR) {
Err(LpspiError::Fifo(Direction::Rx))
} else {
Ok(())
}
}
pub fn enqueue_data(&self, word: u32) {
ral::write_reg!(ral::lpspi, self.lpspi, TDR, word);
}
pub(crate) fn wait_for_transmit_fifo_space(&mut self) -> Result<(), LpspiError> {
loop {
let status = self.status();
if status.intersects(Status::TRANSMIT_ERROR) {
return Err(LpspiError::Fifo(Direction::Tx));
}
let fifo_status = self.fifo_status();
if !fifo_status.is_full(Direction::Tx) {
return Ok(());
}
}
}
pub fn enqueue_transaction(&mut self, transaction: &Transaction) {
ral::modify_reg!(ral::lpspi, self.lpspi, TCR,
LSBF: transaction.bit_order as u32,
BYSW: transaction.byte_swap as u32,
RXMSK: transaction.receive_data_mask as u32,
TXMSK: transaction.transmit_data_mask as u32,
FRAMESZ: transaction.frame_size as u32,
CONT: transaction.continuous as u32,
CONTC: transaction.continuing as u32
);
}
fn exchange<W>(&mut self, buffer: &mut [W]) -> Result<(), LpspiError>
where
W: Word,
{
if self.status().intersects(Status::BUSY) {
return Err(LpspiError::Busy);
} else if buffer.is_empty() {
return Err(LpspiError::NoData);
}
self.clear_fifos();
let mut transaction = Transaction::new(8 * core::mem::size_of::<W>() as u16)?;
transaction.bit_order = self.bit_order();
transaction.continuous = true;
let mut tx_idx = 0usize;
let mut rx_idx = 0usize;
while tx_idx < buffer.len() && rx_idx < buffer.len() {
if tx_idx < buffer.len() {
let word = buffer[tx_idx];
self.wait_for_transmit_fifo_space()?;
self.enqueue_transaction(&transaction);
self.wait_for_transmit_fifo_space()?;
self.enqueue_data(word.into());
transaction.continuing = true;
tx_idx += 1;
}
if rx_idx < buffer.len() {
self.recv_ok()?;
if let Some(word) = self.read_data() {
buffer[rx_idx] = word.try_into().unwrap_or(W::MAX);
rx_idx += 1;
}
}
}
transaction.continuing = false;
transaction.continuous = false;
self.wait_for_transmit_fifo_space()?;
self.enqueue_transaction(&transaction);
Ok(())
}
fn write_no_read<W>(&mut self, buffer: &[W]) -> Result<(), LpspiError>
where
W: Word,
{
if self.status().intersects(Status::BUSY) {
return Err(LpspiError::Busy);
} else if buffer.is_empty() {
return Err(LpspiError::NoData);
}
self.clear_fifos();
let mut transaction = Transaction::new(8 * core::mem::size_of::<W>() as u16)?;
transaction.bit_order = self.bit_order();
transaction.continuous = true;
transaction.receive_data_mask = true;
for word in buffer {
self.wait_for_transmit_fifo_space()?;
self.enqueue_transaction(&transaction);
self.wait_for_transmit_fifo_space()?;
self.enqueue_data((*word).into());
transaction.continuing = true;
}
transaction.continuing = false;
transaction.continuous = false;
self.wait_for_transmit_fifo_space()?;
self.enqueue_transaction(&transaction);
Ok(())
}
pub fn enable_dma_receive(&mut self) {
ral::modify_reg!(ral::lpspi, self.lpspi, FCR, RXWATER: 0); ral::modify_reg!(ral::lpspi, self.lpspi, DER, RDDE: 1);
}
pub fn disable_dma_receive(&mut self) {
while ral::read_reg!(ral::lpspi, self.lpspi, DER, RDDE == 1) {
ral::modify_reg!(ral::lpspi, self.lpspi, DER, RDDE: 0);
}
}
pub fn enable_dma_transmit(&mut self) {
ral::modify_reg!(ral::lpspi, self.lpspi, FCR, TXWATER: 0); ral::modify_reg!(ral::lpspi, self.lpspi, DER, TDDE: 1);
}
pub fn disable_dma_transmit(&mut self) {
while ral::read_reg!(ral::lpspi, self.lpspi, DER, TDDE == 1) {
ral::modify_reg!(ral::lpspi, self.lpspi, DER, TDDE: 0);
}
}
pub fn rdr(&self) -> *const ral::RORegister<u32> {
core::ptr::addr_of!(self.lpspi.RDR)
}
pub fn tdr(&self) -> *const ral::WORegister<u32> {
core::ptr::addr_of!(self.lpspi.TDR)
}
}
bitflags::bitflags! {
pub struct Status : u32 {
const BUSY = 1 << 24;
const DATA_MATCH = 1 << 13;
const RECEIVE_ERROR = 1 << 12;
const TRANSMIT_ERROR = 1 << 11;
const TRANSFER_COMPLETE = 1 << 10;
const FRAME_COMPLETE = 1 << 9;
const WORD_COMPLETE = 1 << 8;
const RECEIVE_DATA = 1 << 1;
const TRANSMIT_DATA = 1 << 0;
}
}
impl Status {
const W1C: Self = Self::from_bits_truncate(
Self::DATA_MATCH.bits()
| Self::RECEIVE_ERROR.bits()
| Self::TRANSMIT_ERROR.bits()
| Self::TRANSFER_COMPLETE.bits()
| Self::FRAME_COMPLETE.bits()
| Self::WORD_COMPLETE.bits(),
);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FifoStatus {
pub rxcount: u16,
pub txcount: u16,
}
impl FifoStatus {
#[inline]
pub const fn is_full(self, direction: Direction) -> bool {
const MAX_FIFO_SIZE: u16 = 16;
let count = match direction {
Direction::Tx => self.txcount,
Direction::Rx => self.rxcount,
};
count >= MAX_FIFO_SIZE
}
}
bitflags::bitflags! {
pub struct Interrupts : u32 {
const DATA_MATCH = 1 << 13;
const RECEIVE_ERROR = 1 << 12;
const TRANSMIT_ERROR = 1 << 11;
const TRANSMIT_COMPLETE = 1 << 10;
const FRAME_COMPLETE = 1 << 9;
const WORD_COMPLETE = 1 << 8;
const RECEIVE_DATA = 1 << 1;
const TRANSMIT_DATA = 1 << 0;
}
}
pub struct Disabled<'a, const N: u8> {
lpspi: &'a ral::lpspi::Instance<N>,
men: bool,
}
impl<'a, const N: u8> Disabled<'a, N> {
fn new(lpspi: &'a mut ral::lpspi::Instance<N>) -> Self {
let men = ral::read_reg!(ral::lpspi, lpspi, CR, MEN == MEN_1);
ral::modify_reg!(ral::lpspi, lpspi, CR, MEN: MEN_0);
Self { lpspi, men }
}
pub fn set_mode(&mut self, mode: Mode) {
ral::modify_reg!(
ral::lpspi,
self.lpspi,
TCR,
CPOL: ((mode.polarity == Polarity::IdleHigh) as u32),
CPHA: ((mode.phase == Phase::CaptureOnSecondTransition) as u32)
);
}
pub fn set_clock_hz(&mut self, source_clock_hz: u32, clock_hz: u32) {
set_spi_clock(source_clock_hz, clock_hz, self.lpspi);
}
#[inline]
pub fn set_watermark(&mut self, direction: Direction, watermark: u8) -> u8 {
let max_watermark = match direction {
Direction::Rx => 1 << ral::read_reg!(ral::lpspi, self.lpspi, PARAM, RXFIFO),
Direction::Tx => 1 << ral::read_reg!(ral::lpspi, self.lpspi, PARAM, TXFIFO),
};
let watermark = watermark.min(max_watermark - 1);
match direction {
Direction::Rx => {
ral::modify_reg!(ral::lpspi, self.lpspi, FCR, RXWATER: watermark as u32)
}
Direction::Tx => {
ral::modify_reg!(ral::lpspi, self.lpspi, FCR, TXWATER: watermark as u32)
}
}
watermark
}
}
impl<const N: u8> Drop for Disabled<'_, N> {
fn drop(&mut self) {
ral::modify_reg!(ral::lpspi, self.lpspi, CR, MEN: self.men as u32);
}
}
impl<P, const N: u8> eh02::blocking::spi::Transfer<u8> for Lpspi<P, N> {
type Error = LpspiError;
fn transfer<'a>(&mut self, words: &'a mut [u8]) -> Result<&'a [u8], Self::Error> {
self.exchange(words)?;
Ok(words)
}
}
impl<P, const N: u8> eh02::blocking::spi::Transfer<u16> for Lpspi<P, N> {
type Error = LpspiError;
fn transfer<'a>(&mut self, words: &'a mut [u16]) -> Result<&'a [u16], Self::Error> {
self.exchange(words)?;
Ok(words)
}
}
impl<P, const N: u8> eh02::blocking::spi::Transfer<u32> for Lpspi<P, N> {
type Error = LpspiError;
fn transfer<'a>(&mut self, words: &'a mut [u32]) -> Result<&'a [u32], Self::Error> {
self.exchange(words)?;
Ok(words)
}
}
impl<P, const N: u8> eh02::blocking::spi::Write<u8> for Lpspi<P, N> {
type Error = LpspiError;
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
self.write_no_read(words)
}
}
impl<P, const N: u8> eh02::blocking::spi::Write<u16> for Lpspi<P, N> {
type Error = LpspiError;
fn write(&mut self, words: &[u16]) -> Result<(), Self::Error> {
self.write_no_read(words)
}
}
impl<P, const N: u8> eh02::blocking::spi::Write<u32> for Lpspi<P, N> {
type Error = LpspiError;
fn write(&mut self, words: &[u32]) -> Result<(), Self::Error> {
self.write_no_read(words)
}
}
trait Word: Copy + Into<u32> + TryFrom<u32> {
const MAX: Self;
}
impl Word for u8 {
const MAX: u8 = u8::MAX;
}
impl Word for u16 {
const MAX: u16 = u16::MAX;
}
impl Word for u32 {
const MAX: u32 = u32::MAX;
}