223 lines
4.7 KiB
Rust
223 lines
4.7 KiB
Rust
|
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(())
|
||
|
}
|
||
|
}
|