added asynch SPI implementation
shared-hal-ci / Check build (push) Has been cancelled
shared-hal-ci / Check formatting (push) Has been cancelled
shared-hal-ci / Check Documentation Build (push) Has been cancelled
shared-hal-ci / Clippy (push) Has been cancelled
va108xx-ci / Check build (push) Has been cancelled
va108xx-ci / Run Tests (push) Has been cancelled
va108xx-ci / Check formatting (push) Has been cancelled
va108xx-ci / Check Documentation Build (push) Has been cancelled
va108xx-ci / Clippy (push) Has been cancelled
va416xx-ci / Check build (push) Has been cancelled
va416xx-ci / Run Tests (push) Has been cancelled
va416xx-ci / Check formatting (push) Has been cancelled
va416xx-ci / Check Documentation Build (push) Has been cancelled
va416xx-ci / Clippy (push) Has been cancelled
shared-hal-ci / Check build (pull_request) Has been cancelled
shared-hal-ci / Check formatting (pull_request) Has been cancelled
shared-hal-ci / Check Documentation Build (pull_request) Has been cancelled
shared-hal-ci / Clippy (pull_request) Has been cancelled
va108xx-ci / Check build (pull_request) Has been cancelled
va108xx-ci / Run Tests (pull_request) Has been cancelled
va108xx-ci / Check formatting (pull_request) Has been cancelled
va108xx-ci / Check Documentation Build (pull_request) Has been cancelled
va108xx-ci / Clippy (pull_request) Has been cancelled
va416xx-ci / Check build (pull_request) Has been cancelled
va416xx-ci / Run Tests (pull_request) Has been cancelled
va416xx-ci / Check formatting (pull_request) Has been cancelled
va416xx-ci / Check Documentation Build (pull_request) Has been cancelled
va416xx-ci / Clippy (pull_request) Has been cancelled
shared-hal-ci / Check build (push) Has been cancelled
shared-hal-ci / Check formatting (push) Has been cancelled
shared-hal-ci / Check Documentation Build (push) Has been cancelled
shared-hal-ci / Clippy (push) Has been cancelled
va108xx-ci / Check build (push) Has been cancelled
va108xx-ci / Run Tests (push) Has been cancelled
va108xx-ci / Check formatting (push) Has been cancelled
va108xx-ci / Check Documentation Build (push) Has been cancelled
va108xx-ci / Clippy (push) Has been cancelled
va416xx-ci / Check build (push) Has been cancelled
va416xx-ci / Run Tests (push) Has been cancelled
va416xx-ci / Check formatting (push) Has been cancelled
va416xx-ci / Check Documentation Build (push) Has been cancelled
va416xx-ci / Clippy (push) Has been cancelled
shared-hal-ci / Check build (pull_request) Has been cancelled
shared-hal-ci / Check formatting (pull_request) Has been cancelled
shared-hal-ci / Check Documentation Build (pull_request) Has been cancelled
shared-hal-ci / Clippy (pull_request) Has been cancelled
va108xx-ci / Check build (pull_request) Has been cancelled
va108xx-ci / Run Tests (pull_request) Has been cancelled
va108xx-ci / Check formatting (pull_request) Has been cancelled
va108xx-ci / Check Documentation Build (pull_request) Has been cancelled
va108xx-ci / Clippy (pull_request) Has been cancelled
va416xx-ci / Check build (pull_request) Has been cancelled
va416xx-ci / Run Tests (pull_request) Has been cancelled
va416xx-ci / Check formatting (pull_request) Has been cancelled
va416xx-ci / Check Documentation Build (pull_request) Has been cancelled
va416xx-ci / Clippy (pull_request) Has been cancelled
This commit is contained in:
@@ -225,4 +225,11 @@ pub(crate) mod shared {
|
||||
#[bit(0, w)]
|
||||
rx_fifo: bool,
|
||||
}
|
||||
|
||||
impl FifoClear {
|
||||
pub const ALL: Self = Self::builder()
|
||||
.with_tx_fifo(true)
|
||||
.with_rx_fifo(true)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,554 @@
|
||||
use core::{cell::RefCell, convert::Infallible};
|
||||
|
||||
use arbitrary_int::u5;
|
||||
use critical_section::Mutex;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use portable_atomic::AtomicBool;
|
||||
use raw_slice::{RawBufSlice, RawBufSliceMut};
|
||||
|
||||
use crate::{
|
||||
shared::{FifoClear, TriggerLevel},
|
||||
spi::regs::{Data, InterruptClear, InterruptControl, InterruptStatus},
|
||||
};
|
||||
|
||||
static WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
|
||||
static TRANSFER_CONTEXTS: [Mutex<RefCell<TransferContext>>; 2] =
|
||||
[const { Mutex::new(RefCell::new(TransferContext::new())) }; 2];
|
||||
// Completion flag. Kept outside of the context structure as an atomic to avoid
|
||||
// critical section.
|
||||
static DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
||||
|
||||
/// This is a generic interrupt handler to handle asynchronous SPI operations for a given
|
||||
/// SPI peripheral.
|
||||
///
|
||||
/// The user has to call this once in the interrupt handler responsible for the SPI interrupts on
|
||||
/// the given SPI bank.
|
||||
pub fn on_interrupt(peripheral: super::Bank) {
|
||||
let mut spi = unsafe { peripheral.steal_regs() };
|
||||
let idx = peripheral as usize;
|
||||
let interrupt_enabled = spi.read_interrupt_enable();
|
||||
// IRQ is not related.
|
||||
if interrupt_enabled.raw_value() == 0 {
|
||||
return;
|
||||
}
|
||||
// Prevent spurious interrupts from messing with out logic here.
|
||||
spi.write_interrupt_enable(InterruptControl::DISABLE_ALL);
|
||||
let isr = spi.read_interrupt_status();
|
||||
spi.write_interrupt_clear(InterruptClear::ALL);
|
||||
let mut context = critical_section::with(|cs| {
|
||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||
*context_ref.borrow()
|
||||
});
|
||||
// No transfer active.
|
||||
if context.transfer_type.is_none() {
|
||||
return;
|
||||
}
|
||||
let transfer_type = context.transfer_type.unwrap();
|
||||
match transfer_type {
|
||||
TransferType::Read => on_interrupt_read(idx, &mut context, &mut spi, isr),
|
||||
TransferType::Write => on_interrupt_write(idx, &mut context, &mut spi, isr),
|
||||
TransferType::Transfer => on_interrupt_transfer(idx, &mut context, &mut spi, isr),
|
||||
TransferType::TransferInPlace => {
|
||||
on_interrupt_transfer_in_place(idx, &mut context, &mut spi, isr)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn on_interrupt_read(
|
||||
idx: usize,
|
||||
context: &mut TransferContext,
|
||||
spi: &mut super::regs::MmioSpi<'static>,
|
||||
isr: InterruptStatus,
|
||||
) {
|
||||
let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
|
||||
let transfer_len = read_slice.len();
|
||||
|
||||
// Read data from RX FIFO first.
|
||||
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
|
||||
(0..read_len).for_each(|_| {
|
||||
read_slice[context.rx_progress] = (spi.read_data().data() & 0xFF) as u8;
|
||||
context.rx_progress += 1;
|
||||
});
|
||||
|
||||
// The FIFO still needs to be pumped.
|
||||
while context.tx_progress < read_slice.len() && !spi.read_status().tx_not_full() {
|
||||
spi.write_data(Data::new_with_raw_value(0));
|
||||
context.tx_progress += 1;
|
||||
}
|
||||
|
||||
isr_finish_handler(idx, spi, context, transfer_len)
|
||||
}
|
||||
|
||||
fn on_interrupt_write(
|
||||
idx: usize,
|
||||
context: &mut TransferContext,
|
||||
spi: &mut super::regs::MmioSpi<'static>,
|
||||
isr: InterruptStatus,
|
||||
) {
|
||||
let write_slice = unsafe { context.tx_slice.get().unwrap() };
|
||||
let transfer_len = write_slice.len();
|
||||
|
||||
// Read data from RX FIFO first.
|
||||
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
|
||||
(0..read_len).for_each(|_| {
|
||||
spi.read_data();
|
||||
context.rx_progress += 1;
|
||||
});
|
||||
|
||||
// Data still needs to be sent
|
||||
while context.tx_progress < transfer_len && !spi.read_status().tx_not_full() {
|
||||
spi.write_data(Data::new_with_raw_value(
|
||||
write_slice[context.tx_progress] as u32,
|
||||
));
|
||||
context.tx_progress += 1;
|
||||
}
|
||||
|
||||
isr_finish_handler(idx, spi, context, transfer_len)
|
||||
}
|
||||
|
||||
fn on_interrupt_transfer(
|
||||
idx: usize,
|
||||
context: &mut TransferContext,
|
||||
spi: &mut super::regs::MmioSpi<'static>,
|
||||
isr: InterruptStatus,
|
||||
) {
|
||||
let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
|
||||
let read_len = read_slice.len();
|
||||
let write_slice = unsafe { context.tx_slice.get().unwrap() };
|
||||
let write_len = write_slice.len();
|
||||
let transfer_len = core::cmp::max(read_len, write_len);
|
||||
|
||||
// Send data first to avoid overwriting data that still needs to be sent.
|
||||
while context.tx_progress < transfer_len && !spi.read_status().tx_not_full() {
|
||||
if context.tx_progress < write_len {
|
||||
spi.write_data(Data::new_with_raw_value(
|
||||
write_slice[context.tx_progress] as u32,
|
||||
));
|
||||
} else {
|
||||
// Dummy write.
|
||||
spi.write_data(Data::new_with_raw_value(0));
|
||||
}
|
||||
context.tx_progress += 1;
|
||||
}
|
||||
|
||||
// Read data from RX FIFO.
|
||||
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
|
||||
(0..read_len).for_each(|_| {
|
||||
if context.rx_progress < read_len {
|
||||
read_slice[context.rx_progress] = (spi.read_data().data() & 0xFF) as u8;
|
||||
} else {
|
||||
spi.read_data();
|
||||
}
|
||||
context.rx_progress += 1;
|
||||
});
|
||||
|
||||
isr_finish_handler(idx, spi, context, transfer_len)
|
||||
}
|
||||
|
||||
fn on_interrupt_transfer_in_place(
|
||||
idx: usize,
|
||||
context: &mut TransferContext,
|
||||
spi: &mut super::regs::MmioSpi<'static>,
|
||||
isr: InterruptStatus,
|
||||
) {
|
||||
let transfer_slice = unsafe { context.rx_slice.get_mut().unwrap() };
|
||||
let transfer_len = transfer_slice.len();
|
||||
// Send data first to avoid overwriting data that still needs to be sent.
|
||||
while context.tx_progress < transfer_len && !spi.read_status().tx_not_full() {
|
||||
spi.write_data(Data::new_with_raw_value(
|
||||
transfer_slice[context.tx_progress] as u32,
|
||||
));
|
||||
context.tx_progress += 1;
|
||||
}
|
||||
// Read data from RX FIFO.
|
||||
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
|
||||
(0..read_len).for_each(|_| {
|
||||
transfer_slice[context.rx_progress] = (spi.read_data().data() & 0xFF) as u8;
|
||||
context.rx_progress += 1;
|
||||
});
|
||||
|
||||
isr_finish_handler(idx, spi, context, transfer_len)
|
||||
}
|
||||
|
||||
fn calculate_read_len(
|
||||
spi: &mut super::regs::MmioSpi<'static>,
|
||||
isr: InterruptStatus,
|
||||
total_read_len: usize,
|
||||
rx_progress: usize,
|
||||
) -> usize {
|
||||
if isr.rx() {
|
||||
core::cmp::min(super::FIFO_DEPTH, total_read_len - rx_progress)
|
||||
} else if spi.read_status().rx_not_empty() {
|
||||
let fifo_level = spi.read_state().rx_fifo();
|
||||
core::cmp::min(total_read_len - rx_progress, fifo_level as usize)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic handler after RX FIFO and TX FIFO were handled. Checks and handles finished
|
||||
/// and unfinished conditions.
|
||||
fn isr_finish_handler(
|
||||
idx: usize,
|
||||
spi: &mut super::regs::MmioSpi<'static>,
|
||||
context: &mut TransferContext,
|
||||
transfer_len: usize,
|
||||
) {
|
||||
// Transfer finish condition.
|
||||
if context.rx_progress == context.tx_progress && context.rx_progress == transfer_len {
|
||||
finish_transfer(idx, context, spi);
|
||||
return;
|
||||
}
|
||||
|
||||
unfinished_transfer(spi, transfer_len, context.rx_progress);
|
||||
|
||||
// If the transfer is done, the context structure was already written back.
|
||||
// Write back updated context structure.
|
||||
critical_section::with(|cs| {
|
||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||
*context_ref.borrow_mut() = *context;
|
||||
});
|
||||
}
|
||||
|
||||
fn finish_transfer(
|
||||
idx: usize,
|
||||
context: &mut TransferContext,
|
||||
spi: &mut super::regs::MmioSpi<'static>,
|
||||
) {
|
||||
// Write back updated context structure.
|
||||
critical_section::with(|cs| {
|
||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||
*context_ref.borrow_mut() = *context;
|
||||
});
|
||||
// Write default values.
|
||||
spi.write_rx_fifo_trigger(TriggerLevel::new(u5::new(0x08)));
|
||||
spi.write_tx_fifo_trigger(TriggerLevel::new(u5::new(0x08)));
|
||||
// Interrupts were already disabled and cleared.
|
||||
DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||
WAKERS[idx].wake();
|
||||
}
|
||||
|
||||
fn unfinished_transfer(
|
||||
spi: &mut super::regs::MmioSpi<'static>,
|
||||
transfer_len: usize,
|
||||
rx_progress: usize,
|
||||
) {
|
||||
let new_trig_level = core::cmp::min(super::FIFO_DEPTH, transfer_len - rx_progress);
|
||||
spi.write_rx_fifo_trigger(TriggerLevel::new(u5::new(new_trig_level as u8)));
|
||||
// Re-enable interrupts with the new RX FIFO trigger level.
|
||||
spi.write_interrupt_enable(InterruptControl::ENABLE_ALL);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum TransferType {
|
||||
Read,
|
||||
Write,
|
||||
Transfer,
|
||||
TransferInPlace,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone)]
|
||||
pub struct TransferContext {
|
||||
transfer_type: Option<TransferType>,
|
||||
tx_progress: usize,
|
||||
rx_progress: usize,
|
||||
tx_slice: RawBufSlice,
|
||||
rx_slice: RawBufSliceMut,
|
||||
}
|
||||
|
||||
#[allow(clippy::new_without_default)]
|
||||
impl TransferContext {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
transfer_type: None,
|
||||
tx_progress: 0,
|
||||
rx_progress: 0,
|
||||
tx_slice: RawBufSlice::new_nulled(),
|
||||
rx_slice: RawBufSliceMut::new_nulled(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SpiFuture<'spi> {
|
||||
bank: super::Bank,
|
||||
spi: &'spi mut super::Spi<u8>,
|
||||
finished_regularly: core::cell::Cell<bool>,
|
||||
}
|
||||
|
||||
impl<'spi> SpiFuture<'spi> {
|
||||
fn new_for_read(spi: &'spi mut super::Spi<u8>, bank: super::Bank, words: &mut [u8]) -> Self {
|
||||
if words.is_empty() {
|
||||
panic!("words length unexpectedly 0");
|
||||
}
|
||||
let idx = bank as usize;
|
||||
DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
|
||||
spi.regs
|
||||
.write_interrupt_enable(InterruptControl::DISABLE_ALL);
|
||||
spi.regs.write_fifo_clear(FifoClear::ALL);
|
||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(true));
|
||||
let write_idx = core::cmp::min(super::FIFO_DEPTH, words.len());
|
||||
// Send dummy bytes.
|
||||
(0..write_idx).for_each(|_| {
|
||||
spi.regs.write_data(Data::new_with_raw_value(0));
|
||||
});
|
||||
|
||||
Self::set_triggers(spi, write_idx, words.len());
|
||||
|
||||
critical_section::with(|cs| {
|
||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||
let mut context = context_ref.borrow_mut();
|
||||
context.transfer_type = Some(TransferType::Read);
|
||||
unsafe {
|
||||
context.rx_slice.set(words);
|
||||
}
|
||||
context.tx_slice.set_null();
|
||||
context.tx_progress = write_idx;
|
||||
context.rx_progress = 0;
|
||||
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
||||
spi.regs
|
||||
.write_interrupt_enable(InterruptControl::ENABLE_ALL);
|
||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
||||
});
|
||||
Self {
|
||||
bank,
|
||||
spi,
|
||||
finished_regularly: core::cell::Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_for_write(spi: &'spi mut super::Spi<u8>, bank: super::Bank, words: &[u8]) -> Self {
|
||||
if words.is_empty() {
|
||||
panic!("words length unexpectedly 0");
|
||||
}
|
||||
let (idx, write_idx) = Self::generic_init_transfer(spi, bank, words);
|
||||
critical_section::with(|cs| {
|
||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||
let mut context = context_ref.borrow_mut();
|
||||
context.transfer_type = Some(TransferType::Write);
|
||||
unsafe {
|
||||
context.tx_slice.set(words);
|
||||
}
|
||||
context.rx_slice.set_null();
|
||||
context.tx_progress = write_idx;
|
||||
context.rx_progress = 0;
|
||||
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
||||
spi.regs
|
||||
.write_interrupt_enable(InterruptControl::ENABLE_ALL);
|
||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
||||
});
|
||||
Self {
|
||||
bank,
|
||||
spi,
|
||||
finished_regularly: core::cell::Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_for_transfer(
|
||||
spi: &'spi mut super::Spi<u8>,
|
||||
spi_id: super::Bank,
|
||||
read: &mut [u8],
|
||||
write: &[u8],
|
||||
) -> Self {
|
||||
if read.is_empty() || write.is_empty() {
|
||||
panic!("read or write buffer unexpectedly empty");
|
||||
}
|
||||
let (idx, write_idx) = Self::generic_init_transfer(spi, spi_id, write);
|
||||
critical_section::with(|cs| {
|
||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||
let mut context = context_ref.borrow_mut();
|
||||
context.transfer_type = Some(TransferType::Transfer);
|
||||
unsafe {
|
||||
context.tx_slice.set(write);
|
||||
context.rx_slice.set(read);
|
||||
}
|
||||
context.tx_progress = write_idx;
|
||||
context.rx_progress = 0;
|
||||
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
||||
spi.regs
|
||||
.write_interrupt_enable(InterruptControl::ENABLE_ALL);
|
||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
||||
});
|
||||
Self {
|
||||
bank: spi_id,
|
||||
spi,
|
||||
finished_regularly: core::cell::Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_for_transfer_in_place(
|
||||
spi: &'spi mut super::Spi<u8>,
|
||||
spi_id: super::Bank,
|
||||
words: &mut [u8],
|
||||
) -> Self {
|
||||
if words.is_empty() {
|
||||
panic!("read and write buffer unexpectedly empty");
|
||||
}
|
||||
let (idx, write_idx) = Self::generic_init_transfer(spi, spi_id, words);
|
||||
critical_section::with(|cs| {
|
||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||
let mut context = context_ref.borrow_mut();
|
||||
context.transfer_type = Some(TransferType::TransferInPlace);
|
||||
unsafe {
|
||||
context.rx_slice.set(words);
|
||||
}
|
||||
context.tx_slice.set_null();
|
||||
context.tx_progress = write_idx;
|
||||
context.rx_progress = 0;
|
||||
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
||||
spi.regs
|
||||
.write_interrupt_enable(InterruptControl::ENABLE_ALL);
|
||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
||||
});
|
||||
Self {
|
||||
bank: spi_id,
|
||||
spi,
|
||||
finished_regularly: core::cell::Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
fn generic_init_transfer(
|
||||
spi: &mut super::Spi<u8>,
|
||||
bank: super::Bank,
|
||||
write: &[u8],
|
||||
) -> (usize, usize) {
|
||||
let idx = bank as usize;
|
||||
DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
|
||||
spi.regs
|
||||
.write_interrupt_enable(InterruptControl::DISABLE_ALL);
|
||||
spi.regs.write_fifo_clear(FifoClear::ALL);
|
||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(true));
|
||||
|
||||
let write_idx = core::cmp::min(super::FIFO_DEPTH, write.len());
|
||||
(0..write_idx).for_each(|idx| {
|
||||
spi.regs
|
||||
.write_data(Data::new_with_raw_value(write[idx] as u32));
|
||||
});
|
||||
|
||||
Self::set_triggers(spi, write_idx, write.len());
|
||||
(idx, write_idx)
|
||||
}
|
||||
|
||||
fn set_triggers(spi: &mut super::Spi<u8>, write_idx: usize, write_len: usize) {
|
||||
// This should never fail because it is never larger than the FIFO depth.
|
||||
spi.regs
|
||||
.write_rx_fifo_trigger(TriggerLevel::new(u5::new(write_idx as u8)));
|
||||
// We want to re-fill the TX FIFO before it is completely empty if the full transfer size
|
||||
// is larger than the FIFO depth. I am not sure whether the default value of 1 ensures
|
||||
// this because the PG says that this interrupt is triggered when the FIFO has less than
|
||||
// threshold entries.
|
||||
if write_len > super::FIFO_DEPTH {
|
||||
spi.regs
|
||||
.write_tx_fifo_trigger(TriggerLevel::new(u5::new(2)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'spi> Future for SpiFuture<'spi> {
|
||||
type Output = ();
|
||||
|
||||
fn poll(
|
||||
self: core::pin::Pin<&mut Self>,
|
||||
cx: &mut core::task::Context<'_>,
|
||||
) -> core::task::Poll<Self::Output> {
|
||||
WAKERS[self.bank as usize].register(cx.waker());
|
||||
if DONE[self.bank as usize].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
||||
critical_section::with(|cs| {
|
||||
let mut ctx = TRANSFER_CONTEXTS[self.bank as usize]
|
||||
.borrow(cs)
|
||||
.borrow_mut();
|
||||
*ctx = TransferContext::default();
|
||||
});
|
||||
self.finished_regularly.set(true);
|
||||
return core::task::Poll::Ready(());
|
||||
}
|
||||
core::task::Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
impl<'spi> Drop for SpiFuture<'spi> {
|
||||
fn drop(&mut self) {
|
||||
if !self.finished_regularly.get() {
|
||||
// It might be sufficient to disable and enable the SPI.. But this definitely
|
||||
// ensures the SPI is fully reset.
|
||||
self.spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
||||
self.spi
|
||||
.regs
|
||||
.write_interrupt_enable(InterruptControl::DISABLE_ALL);
|
||||
self.spi.regs.write_fifo_clear(FifoClear::ALL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Asynchronous SPI driver.
|
||||
///
|
||||
/// This is the primary data structure used to perform non-blocking SPI operations.
|
||||
/// It implements the [embedded_hal_async::spi::SpiBus] as well.
|
||||
pub struct SpiAsync(pub super::Spi<u8>);
|
||||
|
||||
impl SpiAsync {
|
||||
pub fn new(spi: super::Spi<u8>) -> Self {
|
||||
Self(spi)
|
||||
}
|
||||
|
||||
fn read(&mut self, words: &mut [u8]) -> Option<SpiFuture<'_>> {
|
||||
if words.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let id = self.0.id;
|
||||
Some(SpiFuture::new_for_read(&mut self.0, id, words))
|
||||
}
|
||||
|
||||
fn write(&mut self, words: &[u8]) -> Option<SpiFuture<'_>> {
|
||||
if words.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let id = self.0.id;
|
||||
Some(SpiFuture::new_for_write(&mut self.0, id, words))
|
||||
}
|
||||
|
||||
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Option<SpiFuture<'_>> {
|
||||
if read.is_empty() || write.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let id = self.0.id;
|
||||
Some(SpiFuture::new_for_transfer(&mut self.0, id, read, write))
|
||||
}
|
||||
|
||||
fn transfer_in_place(&mut self, words: &mut [u8]) -> Option<SpiFuture<'_>> {
|
||||
if words.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let id = self.0.id;
|
||||
Some(SpiFuture::new_for_transfer_in_place(&mut self.0, id, words))
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal_async::spi::ErrorType for SpiAsync {
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl embedded_hal_async::spi::SpiBus for SpiAsync {
|
||||
async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.read(words).unwrap().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||
self.write(words).unwrap().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
||||
self.transfer(read, write).unwrap().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.transfer_in_place(words).unwrap().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,11 @@ use va416xx as pac;
|
||||
|
||||
pub use regs::{Bank, HwChipSelectId};
|
||||
|
||||
pub mod asynch;
|
||||
pub mod regs;
|
||||
|
||||
pub const FIFO_DEPTH: usize = 16;
|
||||
|
||||
pub fn configure_pin_as_hw_cs_pin<P: AnyPin + HwCsProvider>(_pin: P) -> HwChipSelectId {
|
||||
IoPeriphPin::new(P::ID, P::FUN_SEL, None);
|
||||
P::CS_ID
|
||||
|
||||
@@ -25,11 +25,11 @@ cfg_if::cfg_if! {
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Bank {
|
||||
Spi0,
|
||||
Spi1,
|
||||
Spi2,
|
||||
Spi0 = 0,
|
||||
Spi1 = 1,
|
||||
Spi2 = 2,
|
||||
#[cfg(feature = "vor4x")]
|
||||
Spi3,
|
||||
Spi3 = 3,
|
||||
}
|
||||
|
||||
impl Bank {
|
||||
@@ -157,7 +157,7 @@ impl ClockPrescaler {
|
||||
}
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))]
|
||||
#[bitbybit::bitfield(u32, debug, default = 0x0, defmt_bitfields(feature = "defmt"))]
|
||||
pub struct InterruptControl {
|
||||
/// TX FIFO count <= TX FIFO trigger level.
|
||||
#[bit(3, rw)]
|
||||
@@ -174,6 +174,16 @@ pub struct InterruptControl {
|
||||
rx_overrun: bool,
|
||||
}
|
||||
|
||||
impl InterruptControl {
|
||||
pub const DISABLE_ALL: Self = Self::ZERO;
|
||||
pub const ENABLE_ALL: Self = Self::builder()
|
||||
.with_tx(true)
|
||||
.with_rx(true)
|
||||
.with_rx_timeout(true)
|
||||
.with_rx_overrun(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))]
|
||||
pub struct InterruptStatus {
|
||||
/// TX FIFO count <= TX FIFO trigger level.
|
||||
@@ -191,7 +201,7 @@ pub struct InterruptStatus {
|
||||
rx_overrun: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||
#[derive(Debug)]
|
||||
pub struct InterruptClear {
|
||||
/// Clearing the RX interrupt or reading data from the FIFO resets the timeout counter.
|
||||
@@ -201,6 +211,13 @@ pub struct InterruptClear {
|
||||
rx_overrun: bool,
|
||||
}
|
||||
|
||||
impl InterruptClear {
|
||||
pub const ALL: Self = Self::builder()
|
||||
.with_rx_timeout(true)
|
||||
.with_rx_overrun(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))]
|
||||
pub struct State {
|
||||
#[bits(0..=7, r)]
|
||||
@@ -221,21 +238,21 @@ pub struct Spi {
|
||||
#[mmio(PureRead)]
|
||||
status: Status,
|
||||
clkprescale: ClockPrescaler,
|
||||
irq_enb: InterruptControl,
|
||||
interrupt_enable: InterruptControl,
|
||||
/// Raw interrupt status.
|
||||
#[mmio(PureRead)]
|
||||
irq_raw: InterruptStatus,
|
||||
interrupt_status_raw: InterruptStatus,
|
||||
/// Enabled interrupt status.
|
||||
#[mmio(PureRead)]
|
||||
irq_status: InterruptStatus,
|
||||
interrupt_status: InterruptStatus,
|
||||
#[mmio(Write)]
|
||||
irq_clear: InterruptClear,
|
||||
interrupt_clear: InterruptClear,
|
||||
rx_fifo_trigger: TriggerLevel,
|
||||
tx_fifo_trigger: TriggerLevel,
|
||||
#[mmio(Write)]
|
||||
fifo_clear: FifoClear,
|
||||
#[mmio(PureRead)]
|
||||
state: u32,
|
||||
state: State,
|
||||
#[cfg(feature = "vor1x")]
|
||||
_reserved: [u32; 0x3F2],
|
||||
#[cfg(feature = "vor4x")]
|
||||
|
||||
Reference in New Issue
Block a user