SPI improvements / Clock passing update

1. Using `impl Into<Hertz>` instead of Hertz now to increase
   usability for users
2. Update for SPI API to increase usability
This commit is contained in:
Robin Müller 2021-11-21 20:14:56 +01:00
parent e5dba61d6c
commit 4feb74e4a6
5 changed files with 71 additions and 26 deletions

View File

@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- DelayUs and DelayMs trait implementations for timer - DelayUs and DelayMs trait implementations for timer
- SPI implementation for blocking API, supports blockmode as well - SPI implementation for blocking API, supports blockmode as well
### Changed
- API which expects values in Hertz now uses `impl Into<Hertz>` as input parameter
## [0.2.1] ## [0.2.1]
### Added ### Added

View File

@ -62,10 +62,10 @@ fn main() -> ! {
pinsa.pa29.into_funsel_1(), pinsa.pa29.into_funsel_1(),
); );
spia_ref.borrow_mut().replace( spia_ref.borrow_mut().replace(
Spi::spia::<NoneT>( Spi::spia(
dp.SPIA, dp.SPIA,
(sck, miso, mosi), (sck, miso, mosi),
50.mhz().into(), 50.mhz(),
spi_cfg, spi_cfg,
Some(&mut dp.SYSCONFIG), Some(&mut dp.SYSCONFIG),
None, None,
@ -80,10 +80,10 @@ fn main() -> ! {
pinsb.pb7.into_funsel_2(), pinsb.pb7.into_funsel_2(),
); );
spia_ref.borrow_mut().replace( spia_ref.borrow_mut().replace(
Spi::spia::<NoneT>( Spi::spia(
dp.SPIA, dp.SPIA,
(sck, miso, mosi), (sck, miso, mosi),
50.mhz().into(), 50.mhz(),
spi_cfg, spi_cfg,
Some(&mut dp.SYSCONFIG), Some(&mut dp.SYSCONFIG),
None, None,
@ -98,10 +98,10 @@ fn main() -> ! {
pinsb.pb3.into_funsel_1(), pinsb.pb3.into_funsel_1(),
); );
spib_ref.borrow_mut().replace( spib_ref.borrow_mut().replace(
Spi::spib::<NoneT>( Spi::spib(
dp.SPIB, dp.SPIB,
(sck, miso, mosi), (sck, miso, mosi),
50.mhz().into(), 50.mhz(),
spi_cfg, spi_cfg,
Some(&mut dp.SYSCONFIG), Some(&mut dp.SYSCONFIG),
None, None,
@ -114,10 +114,9 @@ fn main() -> ! {
match SPI_BUS_SEL { match SPI_BUS_SEL {
SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => { SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => {
if let Some(ref mut spi) = *spia_ref.borrow_mut() { if let Some(ref mut spi) = *spia_ref.borrow_mut() {
let transfer_cfg = TransferConfig::<NoneT>::new( let transfer_cfg = TransferConfig::new_no_hw_cs(
SPI_SPEED_KHZ.khz().into(), SPI_SPEED_KHZ.khz().into(),
SPI_MODE, SPI_MODE,
None,
BLOCKMODE, BLOCKMODE,
false, false,
); );

View File

@ -40,9 +40,9 @@ pub enum FilterClkSel {
/// The Vorago in powered by an external clock which might have different frequencies. /// The Vorago in powered by an external clock which might have different frequencies.
/// The clock can be set here so it can be used by other software components as well. /// The clock can be set here so it can be used by other software components as well.
/// The clock can be set exactly once /// The clock can be set exactly once
pub fn set_sys_clock(freq: Hertz) { pub fn set_sys_clock(freq: impl Into<Hertz>) {
interrupt::free(|cs| { interrupt::free(|cs| {
SYS_CLOCK.borrow(cs).set(freq).ok(); SYS_CLOCK.borrow(cs).set(freq.into()).ok();
}) })
} }

View File

@ -54,26 +54,28 @@ pub trait PinSck<SPI>: Sealed {}
pub trait PinMosi<SPI>: Sealed {} pub trait PinMosi<SPI>: Sealed {}
pub trait PinMiso<SPI>: Sealed {} pub trait PinMiso<SPI>: Sealed {}
pub trait OptionalHwCs<SPI>: Sealed { pub trait HwCs: Sealed {
const CS_ID: HwChipSelectId; const CS_ID: HwChipSelectId;
} }
pub trait OptionalHwCs<SPI>: HwCs + Sealed {}
macro_rules! hw_cs_pin { macro_rules! hw_cs_pin {
($SPIx:ident, $PXx:ident, $AFx:ident, $HwCsIdent:path, $typedef:ident) => { ($SPIx:ident, $PXx:ident, $AFx:ident, $HwCsIdent:path, $typedef:ident) => {
impl OptionalHwCs<$SPIx> for Pin<$PXx, $AFx> { impl HwCs for Pin<$PXx, $AFx> {
const CS_ID: HwChipSelectId = $HwCsIdent; const CS_ID: HwChipSelectId = $HwCsIdent;
} }
impl OptionalHwCs<$SPIx> for Pin<$PXx, $AFx> {}
pub type $typedef = Pin<$PXx, $AFx>; pub type $typedef = Pin<$PXx, $AFx>;
}; };
} }
impl OptionalHwCs<SPIA> for NoneT { impl HwCs for NoneT {
const CS_ID: HwChipSelectId = HwChipSelectId::Invalid;
}
impl OptionalHwCs<SPIB> for NoneT {
const CS_ID: HwChipSelectId = HwChipSelectId::Invalid; const CS_ID: HwChipSelectId = HwChipSelectId::Invalid;
} }
impl OptionalHwCs<SPIA> for NoneT {}
impl OptionalHwCs<SPIB> for NoneT {}
// SPIA // SPIA
impl PinSck<SPIA> for Pin<PA31, AltFunc1> {} impl PinSck<SPIA> for Pin<PA31, AltFunc1> {}
@ -183,6 +185,7 @@ pub trait GenericTransferConfig {
fn blockmode(&mut self, blockmode: bool); fn blockmode(&mut self, blockmode: bool);
fn mode(&mut self, mode: Mode); fn mode(&mut self, mode: Mode);
fn frequency(&mut self, spi_clk: Hertz); fn frequency(&mut self, spi_clk: Hertz);
fn hw_cs_id(&self) -> u8;
} }
/// This struct contains all configuration parameter which are transfer specific /// This struct contains all configuration parameter which are transfer specific
@ -201,7 +204,32 @@ pub struct TransferConfig<HWCS> {
pub blockmode: bool, pub blockmode: bool,
} }
impl<HWCS> TransferConfig<HWCS> { /// 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,
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 hw_cs: HwChipSelectId,
}
impl TransferConfig<NoneT> {
pub fn new_no_hw_cs(spi_clk: Hertz, mode: Mode, blockmode: bool, sod: bool) -> Self {
TransferConfig {
spi_clk,
mode,
hw_cs: None,
sod,
blockmode,
}
}
}
impl<HWCS: HwCs> TransferConfig<HWCS> {
pub fn new( pub fn new(
spi_clk: Hertz, spi_clk: Hertz,
mode: Mode, mode: Mode,
@ -217,9 +245,19 @@ impl<HWCS> TransferConfig<HWCS> {
blockmode, blockmode,
} }
} }
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,
}
}
} }
impl<HWCS> GenericTransferConfig for TransferConfig<HWCS> { impl<HWCS: HwCs> GenericTransferConfig for TransferConfig<HWCS> {
/// Slave Output Disable /// Slave Output Disable
fn sod(&mut self, sod: bool) { fn sod(&mut self, sod: bool) {
self.sod = sod; self.sod = sod;
@ -236,6 +274,10 @@ impl<HWCS> GenericTransferConfig for TransferConfig<HWCS> {
fn frequency(&mut self, spi_clk: Hertz) { fn frequency(&mut self, spi_clk: Hertz) {
self.spi_clk = spi_clk; self.spi_clk = spi_clk;
} }
fn hw_cs_id(&self) -> u8 {
HWCS::CS_ID as u8
}
} }
#[derive(Default)] #[derive(Default)]
@ -341,13 +383,13 @@ macro_rules! spi {
/// If only one device is connected, this configuration only needs to be done /// If only one device is connected, this configuration only needs to be done
/// once. /// once.
/// * `syscfg` - Can be passed optionally to enable the peripheral clock /// * `syscfg` - Can be passed optionally to enable the peripheral clock
pub fn $spix<HwCs: OptionalHwCs<$SPIX>>( pub fn $spix(
spi: $SPIX, spi: $SPIX,
pins: (Sck, Miso, Mosi), pins: (Sck, Miso, Mosi),
sys_clk: Hertz, sys_clk: impl Into<Hertz> + Copy,
spi_cfg: SpiConfig, spi_cfg: SpiConfig,
syscfg: Option<&mut SYSCONFIG>, syscfg: Option<&mut SYSCONFIG>,
transfer_cfg: Option<&TransferConfig<HwCs>>, transfer_cfg: Option<&ReducedTransferConfig>,
) -> Self { ) -> Self {
if let Some(syscfg) = syscfg { if let Some(syscfg) = syscfg {
enable_peripheral_clock(syscfg, $clk_enb); enable_peripheral_clock(syscfg, $clk_enb);
@ -365,9 +407,9 @@ macro_rules! spi {
let mut init_blockmode = false; let mut init_blockmode = false;
if let Some(transfer_cfg) = transfer_cfg { if let Some(transfer_cfg) = transfer_cfg {
mode = transfer_cfg.mode; mode = transfer_cfg.mode;
clk_prescale = sys_clk.0 / (transfer_cfg.spi_clk.0 * (scrdv as u32 + 1)); clk_prescale = sys_clk.into().0 / (transfer_cfg.spi_clk.0 * (scrdv as u32 + 1));
if transfer_cfg.hw_cs.is_some() { if transfer_cfg.hw_cs != HwChipSelectId::Invalid {
ss = HwCs::CS_ID as u8; ss = transfer_cfg.hw_cs as u8;
} }
init_blockmode = transfer_cfg.blockmode; init_blockmode = transfer_cfg.blockmode;
} }
@ -408,7 +450,7 @@ macro_rules! spi {
spi_base: SpiBase { spi_base: SpiBase {
spi, spi,
cfg: spi_cfg, cfg: spi_cfg,
sys_clk, sys_clk: sys_clk.into(),
blockmode: init_blockmode, blockmode: init_blockmode,
_word: PhantomData, _word: PhantomData,
}, },

View File

@ -46,7 +46,7 @@ pub enum Error {
BreakCondition, BreakCondition,
} }
#[derive(Copy, Clone, PartialEq)] #[derive(Debug, PartialEq, Copy, Clone)]
pub enum Event { pub enum Event {
// Receiver FIFO interrupt enable. Generates interrupt // Receiver FIFO interrupt enable. Generates interrupt
// when FIFO is at least half full. Half full is defined as FIFO // when FIFO is at least half full. Half full is defined as FIFO