continue update package
This commit is contained in:
@ -26,6 +26,8 @@ use embedded_hal::spi::{Mode, MODE_0, MODE_1, MODE_2, MODE_3};
|
||||
// FIFO has a depth of 16.
|
||||
const FILL_DEPTH: usize = 12;
|
||||
|
||||
pub const DEFAULT_CLK_DIV: u16 = 2;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub enum HwChipSelectId {
|
||||
Id0 = 0,
|
||||
@ -221,6 +223,17 @@ hw_cs_pins!(
|
||||
|
||||
// SPIC
|
||||
|
||||
pub struct RomSck;
|
||||
pub struct RomMosi;
|
||||
pub struct RomMiso;
|
||||
|
||||
impl Sealed for RomSck {}
|
||||
impl PinSck<pac::Spic> for RomSck {}
|
||||
impl Sealed for RomMosi {}
|
||||
impl PinMosi<pac::Spic> for RomMosi {}
|
||||
impl Sealed for RomMiso {}
|
||||
impl PinMiso<pac::Spic> for RomMiso {}
|
||||
|
||||
hw_cs_pins!(
|
||||
pac::Spic, SpiPort::Portc:
|
||||
(PB9, AltFunc3, HwChipSelectId::Id1, HwCs1SpiCPortB0),
|
||||
@ -241,35 +254,28 @@ hw_cs_pins!(
|
||||
// Config
|
||||
//==================================================================================================
|
||||
|
||||
pub trait GenericTransferConfig {
|
||||
pub trait TransferConfigProvider {
|
||||
fn sod(&mut self, sod: bool);
|
||||
fn blockmode(&mut self, blockmode: bool);
|
||||
fn mode(&mut self, mode: Mode);
|
||||
fn frequency(&mut self, spi_clk: Hertz);
|
||||
fn clk_cfg(&mut self, clk_cfg: SpiClkConfig);
|
||||
fn hw_cs_id(&self) -> u8;
|
||||
}
|
||||
|
||||
/// This struct contains all configuration parameter which are transfer specific
|
||||
/// and might change for transfers to different SPI slaves
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct TransferConfig<HwCs> {
|
||||
pub spi_clk: Hertz,
|
||||
pub mode: Mode,
|
||||
/// This only works if the Slave Output Disable (SOD) bit of the [`SpiConfig`] is set to
|
||||
/// false
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct TransferConfigWithHwcs<HwCs> {
|
||||
pub hw_cs: Option<HwCs>,
|
||||
pub sod: bool,
|
||||
/// If this is enabled, all data in the FIFO is transmitted in a single frame unless
|
||||
/// the BMSTOP bit is set on a dataword. A frame is defined as CSn being active for the
|
||||
/// duration of multiple data words
|
||||
pub blockmode: bool,
|
||||
pub cfg: CommonTransferConfig,
|
||||
}
|
||||
|
||||
/// Type erased variant of the transfer configuration. This is required to avoid generics in
|
||||
/// the SPI constructor.
|
||||
pub struct ReducedTransferConfig {
|
||||
pub spi_clk: Hertz,
|
||||
pub mode: Mode,
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct CommonTransferConfig {
|
||||
pub clk_cfg: Option<SpiClkConfig>,
|
||||
pub mode: Option<Mode>,
|
||||
pub sod: bool,
|
||||
/// If this is enabled, all data in the FIFO is transmitted in a single frame unless
|
||||
/// the BMSTOP bit is set on a dataword. A frame is defined as CSn being active for the
|
||||
@ -278,62 +284,67 @@ pub struct ReducedTransferConfig {
|
||||
pub hw_cs: HwChipSelectId,
|
||||
}
|
||||
|
||||
impl TransferConfig<NoneT> {
|
||||
pub fn new_no_hw_cs(spi_clk: impl Into<Hertz>, mode: Mode, blockmode: bool, sod: bool) -> Self {
|
||||
TransferConfig {
|
||||
spi_clk: spi_clk.into(),
|
||||
mode,
|
||||
impl TransferConfigWithHwcs<NoneT> {
|
||||
pub fn new_no_hw_cs(
|
||||
clk_cfg: Option<SpiClkConfig>,
|
||||
mode: Option<Mode>,
|
||||
blockmode: bool,
|
||||
sod: bool,
|
||||
) -> Self {
|
||||
TransferConfigWithHwcs {
|
||||
hw_cs: None,
|
||||
sod,
|
||||
blockmode,
|
||||
cfg: CommonTransferConfig {
|
||||
clk_cfg,
|
||||
mode,
|
||||
sod,
|
||||
blockmode,
|
||||
hw_cs: HwChipSelectId::Invalid,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<HwCs: HwCsProvider> TransferConfig<HwCs> {
|
||||
impl<HwCs: HwCsProvider> TransferConfigWithHwcs<HwCs> {
|
||||
pub fn new(
|
||||
spi_clk: impl Into<Hertz>,
|
||||
mode: Mode,
|
||||
clk_cfg: Option<SpiClkConfig>,
|
||||
mode: Option<Mode>,
|
||||
hw_cs: Option<HwCs>,
|
||||
blockmode: bool,
|
||||
sod: bool,
|
||||
) -> Self {
|
||||
TransferConfig {
|
||||
spi_clk: spi_clk.into(),
|
||||
mode,
|
||||
TransferConfigWithHwcs {
|
||||
hw_cs,
|
||||
sod,
|
||||
blockmode,
|
||||
cfg: CommonTransferConfig {
|
||||
clk_cfg,
|
||||
mode,
|
||||
sod,
|
||||
blockmode,
|
||||
hw_cs: HwCs::CS_ID,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn downgrade(self) -> ReducedTransferConfig {
|
||||
ReducedTransferConfig {
|
||||
spi_clk: self.spi_clk,
|
||||
mode: self.mode,
|
||||
sod: self.sod,
|
||||
blockmode: self.blockmode,
|
||||
hw_cs: HwCs::CS_ID,
|
||||
}
|
||||
pub fn downgrade(self) -> CommonTransferConfig {
|
||||
self.cfg
|
||||
}
|
||||
}
|
||||
|
||||
impl<HwCs: HwCsProvider> GenericTransferConfig for TransferConfig<HwCs> {
|
||||
impl<HwCs: HwCsProvider> TransferConfigProvider for TransferConfigWithHwcs<HwCs> {
|
||||
/// Slave Output Disable
|
||||
fn sod(&mut self, sod: bool) {
|
||||
self.sod = sod;
|
||||
self.cfg.sod = sod;
|
||||
}
|
||||
|
||||
fn blockmode(&mut self, blockmode: bool) {
|
||||
self.blockmode = blockmode;
|
||||
self.cfg.blockmode = blockmode;
|
||||
}
|
||||
|
||||
fn mode(&mut self, mode: Mode) {
|
||||
self.mode = mode;
|
||||
self.cfg.mode = Some(mode);
|
||||
}
|
||||
|
||||
fn frequency(&mut self, spi_clk: Hertz) {
|
||||
self.spi_clk = spi_clk;
|
||||
fn clk_cfg(&mut self, clk_cfg: SpiClkConfig) {
|
||||
self.cfg.clk_cfg = Some(clk_cfg);
|
||||
}
|
||||
|
||||
fn hw_cs_id(&self) -> u8 {
|
||||
@ -341,26 +352,40 @@ impl<HwCs: HwCsProvider> GenericTransferConfig for TransferConfig<HwCs> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
/// Configuration options for the whole SPI bus. See Programmer Guide p.92 for more details
|
||||
pub struct SpiConfig {
|
||||
/// Serial clock rate divider. Together with the CLKPRESCALE register, it determines
|
||||
/// the SPI clock rate in master mode. 0 by default. Specifying a higher value
|
||||
/// limits the maximum attainable SPI speed
|
||||
pub scrdv: u8,
|
||||
clk: SpiClkConfig,
|
||||
/// By default, configure SPI for master mode (ms == false)
|
||||
ms: bool,
|
||||
/// Slave output disable. Useful if separate GPIO pins or decoders are used for CS control
|
||||
sod: bool,
|
||||
pub slave_output_disable: bool,
|
||||
/// Loopback mode. If you use this, don't connect MISO to MOSI, they will be tied internally
|
||||
lbm: bool,
|
||||
pub loopback_mode: bool,
|
||||
/// Enable Master Delayer Capture Mode. See Programmers Guide p.92 for more details
|
||||
pub mdlycap: bool,
|
||||
pub master_delayer_capture: bool,
|
||||
}
|
||||
|
||||
impl Default for SpiConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
// Default value is definitely valid.
|
||||
clk: SpiClkConfig::from_div(DEFAULT_CLK_DIV).unwrap(),
|
||||
ms: Default::default(),
|
||||
slave_output_disable: Default::default(),
|
||||
loopback_mode: Default::default(),
|
||||
master_delayer_capture: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SpiConfig {
|
||||
pub fn loopback(mut self, enable: bool) -> Self {
|
||||
self.lbm = enable;
|
||||
self.loopback_mode = enable;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn clk_cfg(mut self, clk_cfg: SpiClkConfig) -> Self {
|
||||
self.clk = clk_cfg;
|
||||
self
|
||||
}
|
||||
|
||||
@ -370,7 +395,7 @@ impl SpiConfig {
|
||||
}
|
||||
|
||||
pub fn slave_output_disable(mut self, sod: bool) -> Self {
|
||||
self.sod = sod;
|
||||
self.slave_output_disable = sod;
|
||||
self
|
||||
}
|
||||
}
|
||||
@ -419,6 +444,119 @@ pub struct Spi<SpiInstance, Pins, Word = u8> {
|
||||
pins: Pins,
|
||||
}
|
||||
|
||||
pub fn mode_to_cpo_cph_bit(mode: embedded_hal::spi::Mode) -> (bool, bool) {
|
||||
match mode {
|
||||
embedded_hal::spi::MODE_0 => (false, false),
|
||||
embedded_hal::spi::MODE_1 => (false, true),
|
||||
embedded_hal::spi::MODE_2 => (true, false),
|
||||
embedded_hal::spi::MODE_3 => (true, true),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct SpiClkConfig {
|
||||
prescale_val: u16,
|
||||
scrdv: u8,
|
||||
}
|
||||
|
||||
impl SpiClkConfig {
|
||||
pub fn prescale_val(&self) -> u16 {
|
||||
self.prescale_val
|
||||
}
|
||||
pub fn scrdv(&self) -> u8 {
|
||||
self.scrdv
|
||||
}
|
||||
}
|
||||
|
||||
impl SpiClkConfig {
|
||||
pub fn new(prescale_val: u16, scrdv: u8) -> Self {
|
||||
Self {
|
||||
prescale_val,
|
||||
scrdv,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_div(div: u16) -> Result<Self, SpiClkConfigError> {
|
||||
spi_clk_config_from_div(div)
|
||||
}
|
||||
|
||||
pub fn from_clk(sys_clk: impl Into<Hertz>, spi_clk: impl Into<Hertz>) -> Option<Self> {
|
||||
clk_div_for_target_clock(sys_clk, spi_clk).map(|div| spi_clk_config_from_div(div).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SpiClkConfigError {
|
||||
DivIsZero,
|
||||
DivideValueNotEven,
|
||||
ScrdvValueTooLarge,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn spi_clk_config_from_div(mut div: u16) -> Result<SpiClkConfig, SpiClkConfigError> {
|
||||
if div == 0 {
|
||||
return Err(SpiClkConfigError::DivIsZero);
|
||||
}
|
||||
if div % 2 != 0 {
|
||||
return Err(SpiClkConfigError::DivideValueNotEven);
|
||||
}
|
||||
let mut prescale_val = 0;
|
||||
|
||||
// find largest (even) prescale value that divides into div
|
||||
for i in (2..=0xfe).rev().step_by(2) {
|
||||
if div % i == 0 {
|
||||
prescale_val = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if prescale_val == 0 {
|
||||
return Err(SpiClkConfigError::DivideValueNotEven);
|
||||
}
|
||||
|
||||
div /= prescale_val;
|
||||
if div > u8::MAX as u16 + 1 {
|
||||
return Err(SpiClkConfigError::ScrdvValueTooLarge);
|
||||
}
|
||||
Ok(SpiClkConfig {
|
||||
prescale_val,
|
||||
scrdv: (div - 1) as u8,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clk_div_for_target_clock(
|
||||
sys_clk: impl Into<Hertz>,
|
||||
spi_clk: impl Into<Hertz>,
|
||||
) -> Option<u16> {
|
||||
let spi_clk = spi_clk.into();
|
||||
let sys_clk = sys_clk.into();
|
||||
if spi_clk > sys_clk {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Step 1: Calculate raw divider.
|
||||
let raw_div = sys_clk.raw() / spi_clk.raw();
|
||||
let remainder = sys_clk.raw() % spi_clk.raw();
|
||||
|
||||
// Step 2: Round up if necessary.
|
||||
let mut rounded_div = if remainder * 2 >= spi_clk.raw() {
|
||||
raw_div + 1
|
||||
} else {
|
||||
raw_div
|
||||
};
|
||||
|
||||
if rounded_div % 2 != 0 {
|
||||
// Take slower clock conservatively.
|
||||
rounded_div += 1;
|
||||
}
|
||||
if rounded_div > u16::MAX as u32 {
|
||||
return None;
|
||||
}
|
||||
Some(rounded_div as u16)
|
||||
}
|
||||
|
||||
// Re-export this so it can be used for the constructor
|
||||
pub use crate::typelevel::NoneT;
|
||||
|
||||
@ -453,59 +591,56 @@ where
|
||||
spi: SpiI,
|
||||
pins: (Sck, Miso, Mosi),
|
||||
spi_cfg: SpiConfig,
|
||||
transfer_cfg: Option<&ReducedTransferConfig>,
|
||||
transfer_cfg: Option<&CommonTransferConfig>,
|
||||
) -> Self {
|
||||
enable_peripheral_clock(syscfg, SpiI::PERIPH_SEL);
|
||||
let SpiConfig {
|
||||
scrdv,
|
||||
clk,
|
||||
ms,
|
||||
sod,
|
||||
lbm,
|
||||
mdlycap,
|
||||
slave_output_disable,
|
||||
loopback_mode,
|
||||
master_delayer_capture,
|
||||
} = spi_cfg;
|
||||
let mut mode = MODE_0;
|
||||
let mut clk_prescale = 0x02;
|
||||
let mut init_mode = embedded_hal::spi::MODE_0;
|
||||
let mut ss = 0;
|
||||
let mut init_blockmode = false;
|
||||
if let Some(transfer_cfg) = transfer_cfg {
|
||||
mode = transfer_cfg.mode;
|
||||
clk_prescale = sys_clk.into().raw() / (transfer_cfg.spi_clk.raw() * (scrdv as u32 + 1));
|
||||
if let Some(mode) = transfer_cfg.mode {
|
||||
init_mode = mode;
|
||||
}
|
||||
if transfer_cfg.hw_cs != HwChipSelectId::Invalid {
|
||||
ss = transfer_cfg.hw_cs as u8;
|
||||
}
|
||||
init_blockmode = transfer_cfg.blockmode;
|
||||
}
|
||||
|
||||
let (cpo_bit, cph_bit) = match mode {
|
||||
MODE_0 => (false, false),
|
||||
MODE_1 => (false, true),
|
||||
MODE_2 => (true, false),
|
||||
MODE_3 => (true, true),
|
||||
};
|
||||
let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(init_mode);
|
||||
spi.ctrl0().write(|w| {
|
||||
unsafe {
|
||||
w.size().bits(Word::word_reg());
|
||||
w.scrdv().bits(scrdv);
|
||||
w.scrdv().bits(clk.scrdv);
|
||||
// Clear clock phase and polarity. Will be set to correct value for each
|
||||
// transfer
|
||||
w.spo().bit(cpo_bit);
|
||||
w.sph().bit(cph_bit)
|
||||
}
|
||||
});
|
||||
|
||||
spi.ctrl1().write(|w| {
|
||||
w.lbm().bit(lbm);
|
||||
w.sod().bit(sod);
|
||||
w.lbm().bit(loopback_mode);
|
||||
w.sod().bit(slave_output_disable);
|
||||
w.ms().bit(ms);
|
||||
w.mdlycap().bit(mdlycap);
|
||||
w.mdlycap().bit(master_delayer_capture);
|
||||
w.blockmode().bit(init_blockmode);
|
||||
unsafe { w.ss().bits(ss) }
|
||||
});
|
||||
spi.clkprescale()
|
||||
.write(|w| unsafe { w.bits(clk.prescale_val as u32) });
|
||||
|
||||
spi.fifo_clr().write(|w| {
|
||||
w.rxfifo().set_bit();
|
||||
w.txfifo().set_bit()
|
||||
});
|
||||
spi.clkprescale().write(|w| unsafe { w.bits(clk_prescale) });
|
||||
// Enable the peripheral as the last step as recommended in the
|
||||
// programmers guide
|
||||
spi.ctrl1().modify(|_, w| w.enable().set_bit());
|
||||
@ -522,33 +657,33 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cfg_clock(&mut self, spi_clk: impl Into<Hertz>) {
|
||||
self.inner.cfg_clock(spi_clk);
|
||||
}
|
||||
delegate::delegate! {
|
||||
to self.inner {
|
||||
#[inline]
|
||||
pub fn cfg_clock(&mut self, cfg: SpiClkConfig);
|
||||
|
||||
#[inline]
|
||||
pub fn cfg_mode(&mut self, mode: Mode) {
|
||||
self.inner.cfg_mode(mode);
|
||||
#[inline]
|
||||
pub fn cfg_clock_from_div(&mut self, div: u16) -> Result<(), SpiClkConfigError>;
|
||||
|
||||
#[inline]
|
||||
pub fn cfg_mode(&mut self, mode: Mode);
|
||||
|
||||
#[inline]
|
||||
pub fn perid(&self) -> u32;
|
||||
|
||||
#[inline]
|
||||
pub fn fill_word(&self) -> Word;
|
||||
|
||||
pub fn cfg_transfer<HwCs: OptionalHwCs<SpiI>>(
|
||||
&mut self, transfer_cfg: &TransferConfigWithHwcs<HwCs>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_fill_word(&mut self, fill_word: Word) {
|
||||
self.inner.fill_word = fill_word;
|
||||
}
|
||||
|
||||
pub fn fill_word(&self) -> Word {
|
||||
self.inner.fill_word
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn perid(&self) -> u32 {
|
||||
self.inner.perid()
|
||||
}
|
||||
|
||||
pub fn cfg_transfer<HwCs: OptionalHwCs<SpiI>>(&mut self, transfer_cfg: &TransferConfig<HwCs>) {
|
||||
self.inner.cfg_transfer(transfer_cfg);
|
||||
}
|
||||
|
||||
/// Releases the SPI peripheral and associated pins
|
||||
pub fn release(self) -> (SpiI, (Sck, Miso, Mosi), SpiConfig) {
|
||||
(self.inner.spi, self.pins, self.inner.cfg)
|
||||
@ -564,12 +699,20 @@ where
|
||||
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
|
||||
{
|
||||
#[inline]
|
||||
pub fn cfg_clock(&mut self, spi_clk: impl Into<Hertz>) {
|
||||
let clk_prescale =
|
||||
self.sys_clk.raw() / (spi_clk.into().raw() * (self.cfg.scrdv as u32 + 1));
|
||||
pub fn cfg_clock(&mut self, cfg: SpiClkConfig) {
|
||||
self.spi
|
||||
.ctrl0()
|
||||
.modify(|_, w| unsafe { w.scrdv().bits(cfg.scrdv) });
|
||||
self.spi
|
||||
.clkprescale()
|
||||
.write(|w| unsafe { w.bits(clk_prescale) });
|
||||
.write(|w| unsafe { w.bits(cfg.prescale_val as u32) });
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cfg_clock_from_div(&mut self, div: u16) -> Result<(), SpiClkConfigError> {
|
||||
let val = spi_clk_config_from_div(div)?;
|
||||
self.cfg_clock(val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -586,6 +729,11 @@ where
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fill_word(&self) -> Word {
|
||||
self.fill_word
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_tx_fifo(&self) {
|
||||
self.spi.fifo_clr().write(|w| w.txfifo().set_bit());
|
||||
@ -629,13 +777,17 @@ where
|
||||
|
||||
pub fn cfg_transfer<HwCs: OptionalHwCs<SpiInstance>>(
|
||||
&mut self,
|
||||
transfer_cfg: &TransferConfig<HwCs>,
|
||||
transfer_cfg: &TransferConfigWithHwcs<HwCs>,
|
||||
) {
|
||||
self.cfg_clock(transfer_cfg.spi_clk);
|
||||
self.cfg_mode(transfer_cfg.mode);
|
||||
self.blockmode = transfer_cfg.blockmode;
|
||||
if let Some(trans_clk_div) = transfer_cfg.cfg.clk_cfg {
|
||||
self.cfg_clock(trans_clk_div);
|
||||
}
|
||||
if let Some(mode) = transfer_cfg.cfg.mode {
|
||||
self.cfg_mode(mode);
|
||||
}
|
||||
self.blockmode = transfer_cfg.cfg.blockmode;
|
||||
self.spi.ctrl1().modify(|_, w| {
|
||||
if transfer_cfg.sod {
|
||||
if transfer_cfg.cfg.sod {
|
||||
w.sod().set_bit();
|
||||
} else if transfer_cfg.hw_cs.is_some() {
|
||||
w.sod().clear_bit();
|
||||
@ -645,7 +797,7 @@ where
|
||||
} else {
|
||||
w.sod().clear_bit();
|
||||
}
|
||||
if transfer_cfg.blockmode {
|
||||
if transfer_cfg.cfg.blockmode {
|
||||
w.blockmode().set_bit();
|
||||
} else {
|
||||
w.blockmode().clear_bit();
|
||||
@ -682,34 +834,52 @@ where
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn flush_internal(&self) {
|
||||
let mut status_reg = self.spi.status().read();
|
||||
while status_reg.tfe().bit_is_clear()
|
||||
|| status_reg.rne().bit_is_set()
|
||||
|| status_reg.busy().bit_is_set()
|
||||
{
|
||||
if status_reg.rne().bit_is_set() {
|
||||
self.read_single_word();
|
||||
}
|
||||
status_reg = self.spi.status().read();
|
||||
}
|
||||
}
|
||||
|
||||
fn transfer_preparation(&self, words: &[Word]) -> Result<(), Infallible> {
|
||||
if words.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut status_reg = self.spi.status().read();
|
||||
// Wait until all bytes have been transferred.
|
||||
while status_reg.tfe().bit_is_clear() {
|
||||
// Ignore all received read words.
|
||||
if status_reg.rne().bit_is_set() {
|
||||
self.clear_rx_fifo();
|
||||
}
|
||||
status_reg = self.spi.status().read();
|
||||
}
|
||||
// Ignore all received read words.
|
||||
if status_reg.rne().bit_is_set() {
|
||||
self.clear_rx_fifo();
|
||||
}
|
||||
self.flush_internal();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn initial_send_fifo_pumping(&self, words: Option<&[Word]>) -> usize {
|
||||
// Returns the actual bytes sent.
|
||||
fn initial_send_fifo_pumping_with_words(&self, words: &[Word]) -> usize {
|
||||
if self.blockmode {
|
||||
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit())
|
||||
}
|
||||
// Fill the first half of the write FIFO
|
||||
let mut current_write_idx = 0;
|
||||
for _ in 0..core::cmp::min(FILL_DEPTH, words.map_or(0, |words| words.len())) {
|
||||
self.send_blocking(words.map_or(self.fill_word, |words| words[current_write_idx]));
|
||||
for _ in 0..core::cmp::min(FILL_DEPTH, words.len()) {
|
||||
self.send_blocking(words[current_write_idx]);
|
||||
current_write_idx += 1;
|
||||
}
|
||||
if self.blockmode {
|
||||
self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit())
|
||||
}
|
||||
current_write_idx
|
||||
}
|
||||
|
||||
fn initial_send_fifo_pumping_with_fill_words(&self, send_len: usize) -> usize {
|
||||
if self.blockmode {
|
||||
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit())
|
||||
}
|
||||
// Fill the first half of the write FIFO
|
||||
let mut current_write_idx = 0;
|
||||
for _ in 0..core::cmp::min(FILL_DEPTH, send_len) {
|
||||
self.send_blocking(self.fill_word);
|
||||
current_write_idx += 1;
|
||||
}
|
||||
if self.blockmode {
|
||||
@ -778,7 +948,7 @@ where
|
||||
fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
|
||||
self.transfer_preparation(words)?;
|
||||
let mut current_read_idx = 0;
|
||||
let mut current_write_idx = self.initial_send_fifo_pumping(None);
|
||||
let mut current_write_idx = self.initial_send_fifo_pumping_with_fill_words(words.len());
|
||||
loop {
|
||||
if current_write_idx < words.len() {
|
||||
self.send_blocking(self.fill_word);
|
||||
@ -797,7 +967,7 @@ where
|
||||
|
||||
fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> {
|
||||
self.transfer_preparation(words)?;
|
||||
let mut current_write_idx = self.initial_send_fifo_pumping(Some(words));
|
||||
let mut current_write_idx = self.initial_send_fifo_pumping_with_words(words);
|
||||
while current_write_idx < words.len() {
|
||||
self.send_blocking(words[current_write_idx]);
|
||||
current_write_idx += 1;
|
||||
@ -812,7 +982,7 @@ where
|
||||
fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
|
||||
self.transfer_preparation(write)?;
|
||||
let mut current_read_idx = 0;
|
||||
let mut current_write_idx = self.initial_send_fifo_pumping(Some(write));
|
||||
let mut current_write_idx = self.initial_send_fifo_pumping_with_words(write);
|
||||
while current_read_idx < read.len() || current_write_idx < write.len() {
|
||||
if current_write_idx < write.len() {
|
||||
self.send_blocking(write[current_write_idx]);
|
||||
@ -830,7 +1000,7 @@ where
|
||||
fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
|
||||
self.transfer_preparation(words)?;
|
||||
let mut current_read_idx = 0;
|
||||
let mut current_write_idx = self.initial_send_fifo_pumping(Some(words));
|
||||
let mut current_write_idx = self.initial_send_fifo_pumping_with_words(words);
|
||||
|
||||
while current_read_idx < words.len() || current_write_idx < words.len() {
|
||||
if current_write_idx < words.len() {
|
||||
@ -846,12 +1016,7 @@ where
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
let status_reg = self.spi.status().read();
|
||||
while status_reg.tfe().bit_is_clear() || status_reg.rne().bit_is_set() {
|
||||
if status_reg.rne().bit_is_set() {
|
||||
self.read_single_word();
|
||||
}
|
||||
}
|
||||
self.flush_internal();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -877,23 +1042,13 @@ impl<
|
||||
where
|
||||
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
|
||||
{
|
||||
fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
|
||||
self.inner.read(words)
|
||||
}
|
||||
|
||||
fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> {
|
||||
self.inner.write(words)
|
||||
}
|
||||
|
||||
fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
|
||||
self.inner.transfer(read, write)
|
||||
}
|
||||
|
||||
fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
|
||||
self.inner.transfer_in_place(words)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
self.inner.flush()
|
||||
delegate::delegate! {
|
||||
to self.inner {
|
||||
fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>;
|
||||
fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>;
|
||||
fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error>;
|
||||
fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error>;
|
||||
fn flush(&mut self) -> Result<(), Self::Error>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user