spidump/src/spi_slave.rs

223 lines
4.7 KiB
Rust
Raw Normal View History

2024-01-19 11:55:42 +00:00
use embassy_futures::join::join;
use embassy_stm32::{
dma::Transfer,
gpio::{self, low_level::AFType, AnyPin},
into_ref,
pac::spi::vals,
spi::{
self, BitOrder, CsPin, Error, Instance, MisoPin, Mode, MosiPin, Phase, Polarity,
RxDma, SckPin, TxDma
},
Peripheral, PeripheralRef
};
/// SPI configuration.
#[non_exhaustive]
#[derive(Copy, Clone)]
pub struct Config {
/// SPI mode.
pub mode: Mode,
/// Bit order.
pub bit_order: BitOrder
}
impl Default for Config {
fn default() -> Self {
Self {
mode: spi::Config::default().mode,
bit_order: BitOrder::MsbFirst
}
}
}
impl Config {
fn raw_phase(&self) -> vals::Cpha {
match self.mode.phase {
Phase::CaptureOnSecondTransition => vals::Cpha::SECONDEDGE,
Phase::CaptureOnFirstTransition => vals::Cpha::FIRSTEDGE
}
}
fn raw_polarity(&self) -> vals::Cpol {
match self.mode.polarity {
Polarity::IdleHigh => vals::Cpol::IDLEHIGH,
Polarity::IdleLow => vals::Cpol::IDLELOW
}
}
fn raw_byte_order(&self) -> vals::Lsbfirst {
match self.bit_order {
BitOrder::LsbFirst => vals::Lsbfirst::LSBFIRST,
BitOrder::MsbFirst => vals::Lsbfirst::MSBFIRST
}
}
}
pub struct SpiSlave<'d, T: Instance, Tx, Rx> {
_peri: PeripheralRef<'d, T>,
sck: Option<PeripheralRef<'d, AnyPin>>,
mosi: Option<PeripheralRef<'d, AnyPin>>,
miso: Option<PeripheralRef<'d, AnyPin>>,
cs: Option<PeripheralRef<'d, AnyPin>>,
txdma: PeripheralRef<'d, Tx>,
rxdma: PeripheralRef<'d, Rx>
}
impl<'d, T: Instance, Tx, Rx> SpiSlave<'d, T, Tx, Rx> {
/// Create a new SPI driver.
pub fn new(
peri: impl Peripheral<P = T> + 'd,
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
miso: impl Peripheral<P = impl MisoPin<T>> + 'd,
cs: impl Peripheral<P = impl CsPin<T>> + 'd,
txdma: impl Peripheral<P = Tx> + 'd,
rxdma: impl Peripheral<P = Rx> + 'd,
config: Config
) -> Self {
into_ref!(peri, sck, mosi, miso, cs);
sck.set_as_af(sck.af_num(), AFType::Input);
sck.set_speed(gpio::Speed::VeryHigh);
mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull);
mosi.set_speed(gpio::Speed::VeryHigh);
miso.set_as_af(miso.af_num(), AFType::Input);
miso.set_speed(gpio::Speed::VeryHigh);
cs.set_as_af(cs.af_num(), AFType::Input);
cs.set_speed(gpio::Speed::VeryHigh);
Self::new_inner(
peri,
Some(sck.map_into()),
Some(mosi.map_into()),
Some(miso.map_into()),
Some(cs.map_into()),
txdma,
rxdma,
config
)
}
fn new_inner(
peri: impl Peripheral<P = T> + 'd,
sck: Option<PeripheralRef<'d, AnyPin>>,
mosi: Option<PeripheralRef<'d, AnyPin>>,
miso: Option<PeripheralRef<'d, AnyPin>>,
cs: Option<PeripheralRef<'d, AnyPin>>,
txdma: impl Peripheral<P = Tx> + 'd,
rxdma: impl Peripheral<P = Rx> + 'd,
config: Config
) -> Self {
into_ref!(peri, txdma, rxdma);
let cpha = config.raw_phase();
let cpol = config.raw_polarity();
let lsbfirst = config.raw_byte_order();
T::enable_and_reset();
T::REGS.cr2().modify(|w| {
w.set_ssoe(false);
});
T::REGS.cr1().modify(|w| {
w.set_cpha(cpha);
w.set_cpol(cpol);
w.set_mstr(vals::Mstr::SLAVE);
w.set_spe(true);
w.set_lsbfirst(lsbfirst);
w.set_ssm(false);
w.set_crcen(false);
w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL);
if mosi.is_none() {
w.set_rxonly(vals::Rxonly::OUTPUTDISABLED);
}
w.set_dff(vals::Dff::EIGHTBIT);
});
Self {
_peri: peri,
sck,
mosi,
miso,
cs,
txdma,
rxdma
}
}
/// SPI read, using DMA.
pub async fn read(&mut self, data: &mut [u8]) -> Result<(), Error>
where
Tx: TxDma<T>,
Rx: RxDma<T>
{
if data.is_empty() {
return Ok(());
}
T::REGS.cr1().modify(|w| {
w.set_spe(false);
});
// clear rxfifo
while T::REGS.sr().read().rxne() {
let _ = T::REGS.dr().read();
}
T::REGS.cr2().modify(|w| w.set_rxdmaen(true));
let clock_byte_count = data.len();
let rx_request = self.rxdma.request();
let rx_src = T::REGS.dr().as_ptr() as *mut u8;
let rx_f = unsafe {
Transfer::new_read(
&mut self.rxdma,
rx_request,
rx_src,
data,
Default::default()
)
};
let tx_request = self.txdma.request();
let tx_dst = T::REGS.dr().as_ptr() as *mut u8;
let clock_byte = 0x00u8;
let tx_f = unsafe {
Transfer::new_write_repeated(
&mut self.txdma,
tx_request,
&clock_byte,
clock_byte_count,
tx_dst,
Default::default()
)
};
T::REGS.cr2().modify(|w| w.set_txdmaen(true));
T::REGS.cr1().modify(|w| {
w.set_spe(true);
});
#[cfg(any(spi_v3, spi_v4, spi_v5))]
T::REGS.cr1().modify(|w| {
w.set_cstart(true);
});
join(tx_f, rx_f).await;
// finish dma
while T::REGS.sr().read().bsy() {}
T::REGS.cr1().modify(|w| {
w.set_spe(false);
});
T::REGS.cr2().modify(|reg| {
reg.set_txdmaen(false);
reg.set_rxdmaen(false);
});
Ok(())
}
}