This repository has been archived by the owner on Dec 6, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
339 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,325 @@ | ||
//! API for the SPI peripheral | ||
use crate::Sealed; | ||
use crate::{ | ||
gpio::pins::{ | ||
AltFunc1, AltFunc2, AltFunc3, Pin, PA10, PA11, PA12, PA13, PA14, PA15, PA16, PA17, PA18, | ||
PA19, PA20, PA21, PA22, PA23, PA24, PA25, PA26, PA27, PA28, PA29, PA30, PA31, PB0, PB1, | ||
PB10, PB11, PB12, PB13, PB14, PB15, PB16, PB17, PB18, PB19, PB2, PB22, PB23, PB3, PB4, PB5, | ||
PB6, PB7, PB8, PB9, | ||
}, | ||
pac::{SPIA, SPIB, SPIC}, | ||
time::Hertz, | ||
}; | ||
use core::fmt::Debug; | ||
use core::marker::PhantomData; | ||
use embedded_hal::spi::{Mode, MODE_0, MODE_1, MODE_2, MODE_3}; | ||
|
||
#[derive(Debug, PartialEq, Copy, Clone)] | ||
pub enum HwChipSelectId { | ||
Id0 = 0, | ||
Id1 = 1, | ||
Id2 = 2, | ||
Id3 = 3, | ||
Id4 = 4, | ||
Id5 = 5, | ||
Id6 = 6, | ||
Id7 = 7, | ||
} | ||
|
||
//================================================================================================== | ||
// Pin type definitions | ||
//================================================================================================== | ||
|
||
pub trait PinSck<SPI>: Sealed {} | ||
pub trait PinMosi<SPI>: Sealed {} | ||
pub trait PinMiso<SPI>: Sealed {} | ||
|
||
pub trait PinHwCs<SPI>: Sealed { | ||
const CS_ID: HwChipSelectId; | ||
} | ||
|
||
macro_rules! hw_cs_pin { | ||
($SPIx:ident, $PXx:ident, $AFx:ident, $HwCsIdent:path) => { | ||
impl PinHwCs<$SPIx> for Pin<$PXx, $AFx> { | ||
const CS_ID: HwChipSelectId = $HwCsIdent; | ||
} | ||
}; | ||
} | ||
|
||
// SPIA | ||
|
||
impl PinSck<SPIA> for Pin<PA31, AltFunc1> {} | ||
impl PinMosi<SPIA> for Pin<PA30, AltFunc1> {} | ||
impl PinMiso<SPIA> for Pin<PA29, AltFunc1> {} | ||
|
||
impl PinSck<SPIA> for Pin<PB9, AltFunc2> {} | ||
impl PinMosi<SPIA> for Pin<PB8, AltFunc2> {} | ||
impl PinMiso<SPIA> for Pin<PB7, AltFunc2> {} | ||
|
||
hw_cs_pin!(SPIA, PA28, AltFunc1, HwChipSelectId::Id0); | ||
hw_cs_pin!(SPIA, PA27, AltFunc1, HwChipSelectId::Id1); | ||
hw_cs_pin!(SPIA, PA26, AltFunc1, HwChipSelectId::Id2); | ||
hw_cs_pin!(SPIA, PA25, AltFunc1, HwChipSelectId::Id3); | ||
hw_cs_pin!(SPIA, PA24, AltFunc1, HwChipSelectId::Id4); | ||
hw_cs_pin!(SPIA, PA23, AltFunc1, HwChipSelectId::Id5); | ||
hw_cs_pin!(SPIA, PA22, AltFunc1, HwChipSelectId::Id6); | ||
hw_cs_pin!(SPIA, PA21, AltFunc1, HwChipSelectId::Id7); | ||
|
||
hw_cs_pin!(SPIA, PB6, AltFunc2, HwChipSelectId::Id0); | ||
hw_cs_pin!(SPIA, PB5, AltFunc2, HwChipSelectId::Id6); | ||
hw_cs_pin!(SPIA, PB4, AltFunc2, HwChipSelectId::Id5); | ||
hw_cs_pin!(SPIA, PB3, AltFunc2, HwChipSelectId::Id4); | ||
hw_cs_pin!(SPIA, PB2, AltFunc2, HwChipSelectId::Id3); | ||
hw_cs_pin!(SPIA, PB1, AltFunc2, HwChipSelectId::Id2); | ||
hw_cs_pin!(SPIA, PB0, AltFunc2, HwChipSelectId::Id1); | ||
|
||
// SPIB | ||
|
||
impl PinSck<SPIB> for Pin<PA20, AltFunc2> {} | ||
impl PinMosi<SPIB> for Pin<PA19, AltFunc2> {} | ||
impl PinMiso<SPIB> for Pin<PA18, AltFunc2> {} | ||
|
||
impl PinSck<SPIB> for Pin<PB19, AltFunc1> {} | ||
impl PinMosi<SPIB> for Pin<PB18, AltFunc1> {} | ||
impl PinMiso<SPIB> for Pin<PB17, AltFunc1> {} | ||
|
||
hw_cs_pin!(SPIB, PB2, AltFunc1, HwChipSelectId::Id0); | ||
hw_cs_pin!(SPIB, PB1, AltFunc1, HwChipSelectId::Id1); | ||
hw_cs_pin!(SPIB, PB0, AltFunc1, HwChipSelectId::Id2); | ||
|
||
hw_cs_pin!(SPIB, PB16, AltFunc1, HwChipSelectId::Id0); | ||
hw_cs_pin!(SPIB, PB15, AltFunc1, HwChipSelectId::Id1); | ||
hw_cs_pin!(SPIB, PB14, AltFunc1, HwChipSelectId::Id2); | ||
hw_cs_pin!(SPIB, PB13, AltFunc1, HwChipSelectId::Id3); | ||
hw_cs_pin!(SPIB, PB12, AltFunc1, HwChipSelectId::Id4); | ||
hw_cs_pin!(SPIB, PB11, AltFunc1, HwChipSelectId::Id5); | ||
hw_cs_pin!(SPIB, PB10, AltFunc1, HwChipSelectId::Id6); | ||
|
||
hw_cs_pin!(SPIB, PB12, AltFunc2, HwChipSelectId::Id0); | ||
hw_cs_pin!(SPIB, PB11, AltFunc2, HwChipSelectId::Id1); | ||
hw_cs_pin!(SPIB, PB10, AltFunc2, HwChipSelectId::Id2); | ||
|
||
hw_cs_pin!(SPIB, PA17, AltFunc2, HwChipSelectId::Id0); | ||
hw_cs_pin!(SPIB, PA16, AltFunc2, HwChipSelectId::Id1); | ||
hw_cs_pin!(SPIB, PA15, AltFunc2, HwChipSelectId::Id2); | ||
hw_cs_pin!(SPIB, PA14, AltFunc2, HwChipSelectId::Id3); | ||
hw_cs_pin!(SPIB, PA13, AltFunc2, HwChipSelectId::Id4); | ||
hw_cs_pin!(SPIB, PA12, AltFunc2, HwChipSelectId::Id5); | ||
hw_cs_pin!(SPIB, PA11, AltFunc2, HwChipSelectId::Id6); | ||
hw_cs_pin!(SPIB, PA10, AltFunc2, HwChipSelectId::Id7); | ||
|
||
hw_cs_pin!(SPIB, PA23, AltFunc2, HwChipSelectId::Id5); | ||
hw_cs_pin!(SPIB, PA22, AltFunc2, HwChipSelectId::Id6); | ||
hw_cs_pin!(SPIB, PA21, AltFunc2, HwChipSelectId::Id7); | ||
|
||
// SPIC | ||
|
||
hw_cs_pin!(SPIC, PB9, AltFunc3, HwChipSelectId::Id1); | ||
hw_cs_pin!(SPIC, PB8, AltFunc3, HwChipSelectId::Id2); | ||
hw_cs_pin!(SPIC, PB7, AltFunc3, HwChipSelectId::Id3); | ||
|
||
hw_cs_pin!(SPIC, PB23, AltFunc3, HwChipSelectId::Id2); | ||
hw_cs_pin!(SPIC, PB22, AltFunc3, HwChipSelectId::Id1); | ||
|
||
hw_cs_pin!(SPIC, PA20, AltFunc1, HwChipSelectId::Id1); | ||
hw_cs_pin!(SPIC, PA19, AltFunc1, HwChipSelectId::Id2); | ||
hw_cs_pin!(SPIC, PB18, AltFunc1, HwChipSelectId::Id3); | ||
|
||
hw_cs_pin!(SPIC, PA23, AltFunc3, HwChipSelectId::Id1); | ||
hw_cs_pin!(SPIC, PA22, AltFunc3, HwChipSelectId::Id2); | ||
hw_cs_pin!(SPIC, PA21, AltFunc3, HwChipSelectId::Id3); | ||
hw_cs_pin!(SPIC, PA20, AltFunc3, HwChipSelectId::Id4); | ||
|
||
//================================================================================================== | ||
// Config | ||
//================================================================================================== | ||
|
||
#[derive(Copy, Clone)] | ||
pub struct TransferConfig { | ||
spi_clk: Hertz, | ||
mode: Mode, | ||
/// This only works if the Slave Output Disable (SOD) bit of the [`SpiConfig`] is set to | ||
/// false | ||
hw_cs: Option<HwChipSelectId>, | ||
sod: bool, | ||
blockmode: bool, | ||
} | ||
|
||
/// Configuration options for a single transfer. These can be used to reconfigure the SPI bus | ||
/// for transaction to different devices | ||
impl TransferConfig { | ||
pub fn new(spi_clk: Hertz, mode: Mode, hw_cs: Option<HwChipSelectId>) -> Self { | ||
TransferConfig { | ||
spi_clk, | ||
mode, | ||
hw_cs, | ||
sod: false, | ||
blockmode: false, | ||
} | ||
} | ||
|
||
pub fn hw_cs(&mut self, hw_cs: HwChipSelectId) { | ||
self.hw_cs = Some(hw_cs); | ||
} | ||
|
||
/// Slave Output Disable | ||
pub fn sod(&mut self, sod: bool) { | ||
self.sod = sod; | ||
} | ||
|
||
pub fn blockmode(&mut self, blockmode: bool) { | ||
self.blockmode = blockmode; | ||
} | ||
|
||
pub fn mode(&mut self, mode: Mode) { | ||
self.mode = mode; | ||
} | ||
|
||
pub fn frequency(&mut self, spi_clk: Hertz) { | ||
self.spi_clk = spi_clk; | ||
} | ||
} | ||
|
||
#[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 | ||
scrdv: u8, | ||
/// 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, | ||
/// Loopback mode. If you use this, don't connect MISO to MOSI, they will be tied internally | ||
lbm: bool, | ||
/// Enable Master Delayer Capture Mode. See Programmers Guide p.92 for more details | ||
mdlycap: bool, | ||
} | ||
|
||
//================================================================================================== | ||
// Spi | ||
//================================================================================================== | ||
|
||
pub struct Spi<SPI, WORD = u8> { | ||
spi: SPI, | ||
cfg: SpiConfig, | ||
sys_clk: Hertz, | ||
_word: PhantomData<WORD>, | ||
} | ||
|
||
impl Spi<SPIA, u8> { | ||
/// Create a new SPI struct | ||
/// | ||
/// ## Arguments | ||
/// | ||
/// * `spia` - SPI peripheral | ||
/// * `spi_cfg` - Configuration of the SPI bus | ||
/// * `transfer_cfg` - Transfer configuration which includes configuration which can change | ||
/// across individual SPI transfers like SPI mode or SPI clock. If only one device is | ||
/// connected, this configuration only needs to be done once. | ||
pub fn new( | ||
spia: SPIA, | ||
sys_clk: Hertz, | ||
spi_cfg: SpiConfig, | ||
transfer_cfg: Option<&TransferConfig>, | ||
) -> Spi<SPIA, u8> { | ||
let SpiConfig { | ||
scrdv, | ||
ms, | ||
sod, | ||
lbm, | ||
mdlycap, | ||
} = spi_cfg; | ||
let mut mode = MODE_0; | ||
let mut clk_prescale = 0x02; | ||
let mut ss = 0; | ||
if let Some(transfer_cfg) = transfer_cfg { | ||
mode = transfer_cfg.mode; | ||
clk_prescale = sys_clk.0 / (transfer_cfg.spi_clk.0 * (scrdv as u32 + 1)); | ||
if let Some(hw_cs) = transfer_cfg.hw_cs { | ||
ss = hw_cs as u8; | ||
} | ||
} | ||
|
||
let (cpo_bit, cph_bit) = match mode { | ||
MODE_0 => (false, false), | ||
MODE_1 => (false, true), | ||
MODE_2 => (true, false), | ||
MODE_3 => (true, true), | ||
}; | ||
spia.ctrl0.write(|w| { | ||
unsafe { | ||
// 0x03 -> 4 bits, 0x07 -> 8 bits, 0x0f -> 16 bits | ||
w.size().bits(0x07); | ||
w.scrdv().bits(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) | ||
} | ||
}); | ||
spia.ctrl1.write(|w| { | ||
w.lbm().bit(lbm); | ||
w.sod().bit(sod); | ||
w.ms().bit(ms); | ||
w.mdlycap().bit(mdlycap); | ||
unsafe { w.ss().bits(ss) } | ||
}); | ||
|
||
spia.clkprescale.write(|w| unsafe { w.bits(clk_prescale) }); | ||
// Enable the peripheral as the last step as recommended in the programmers guide | ||
spia.ctrl1.write(|w| w.enable().set_bit()); | ||
Spi { | ||
spi: spia, | ||
cfg: spi_cfg, | ||
sys_clk, | ||
_word: PhantomData, | ||
} | ||
} | ||
|
||
#[inline] | ||
pub fn cfg_clock(&mut self, spi_clk: Hertz) { | ||
let clk_prescale = self.sys_clk.0 / (spi_clk.0 * (self.cfg.scrdv as u32 + 1)); | ||
self.spi | ||
.clkprescale | ||
.write(|w| unsafe { w.bits(clk_prescale) }); | ||
} | ||
|
||
#[inline] | ||
pub fn cfg_mode(&mut self, mode: Mode) { | ||
let (cpo_bit, cph_bit) = match mode { | ||
MODE_0 => (false, false), | ||
MODE_1 => (false, true), | ||
MODE_2 => (true, false), | ||
MODE_3 => (true, true), | ||
}; | ||
self.spi.ctrl0.write(|w| { | ||
w.spo().bit(cpo_bit); | ||
w.sph().bit(cph_bit) | ||
}); | ||
} | ||
|
||
pub fn cfg_transfer(&mut self, transfer_cfg: &TransferConfig) { | ||
self.cfg_clock(transfer_cfg.spi_clk); | ||
self.cfg_mode(transfer_cfg.mode); | ||
self.spi.ctrl1.modify(|_, w| { | ||
if transfer_cfg.sod { | ||
w.sod().set_bit(); | ||
} else if transfer_cfg.hw_cs.is_some() { | ||
w.sod().clear_bit(); | ||
unsafe { | ||
w.ss().bits(transfer_cfg.hw_cs.unwrap() as u8); | ||
} | ||
} else { | ||
w.sod().clear_bit(); | ||
} | ||
if transfer_cfg.blockmode { | ||
w.blockmode().set_bit(); | ||
} else { | ||
w.blockmode().clear_bit(); | ||
} | ||
w | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters