Skip to content
This repository has been archived by the owner on Dec 6, 2021. It is now read-only.

Commit

Permalink
started SPI implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
robamu committed Nov 13, 2021
1 parent 3804249 commit 7c3a425
Show file tree
Hide file tree
Showing 3 changed files with 339 additions and 16 deletions.
4 changes: 1 addition & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@ pub use va108xx as pac;
pub mod clock;
pub mod gpio;
pub mod prelude;
pub mod spi;
pub mod time;
pub mod timer;
pub mod spi;
pub mod typelevel;
pub mod uart;

pub use va108xx as pac;

mod private {
/// Super trait used to mark traits with an exhaustive set of
/// implementations
Expand Down
325 changes: 325 additions & 0 deletions src/spi.rs
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
});
}
}
26 changes: 13 additions & 13 deletions src/uart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ use crate::clock::enable_peripheral_clock;
use crate::{
clock,
gpio::pins::{
Alternate, Funsel1, Funsel2, Funsel3, Pin, PA16, PA17, PA18, PA19, PA2, PA26, PA27, PA3,
PA30, PA31, PA8, PA9, PB18, PB19, PB20, PB21, PB22, PB23, PB6, PB7, PB8, PB9,
AltFunc1, AltFunc2, AltFunc3, Pin, PA16, PA17, PA18, PA19, PA2, PA26, PA27, PA3, PA30,
PA31, PA8, PA9, PB18, PB19, PB20, PB21, PB22, PB23, PB6, PB7, PB8, PB9,
},
pac::{uarta as uart_base, SYSCONFIG, UARTA, UARTB},
prelude::*,
Expand All @@ -23,20 +23,20 @@ use embedded_hal::{blocking, serial};

pub trait Pins<UART> {}

impl Pins<UARTA> for (Pin<PA9, Alternate<Funsel2>>, Pin<PA8, Alternate<Funsel2>>) {}
impl Pins<UARTA> for (Pin<PA17, Alternate<Funsel3>>, Pin<PA16, Alternate<Funsel3>>) {}
impl Pins<UARTA> for (Pin<PA31, Alternate<Funsel3>>, Pin<PA30, Alternate<Funsel3>>) {}
impl Pins<UARTA> for (Pin<PA9, AltFunc2>, Pin<PA8, AltFunc2>) {}
impl Pins<UARTA> for (Pin<PA17, AltFunc3>, Pin<PA16, AltFunc3>) {}
impl Pins<UARTA> for (Pin<PA31, AltFunc3>, Pin<PA30, AltFunc3>) {}

impl Pins<UARTA> for (Pin<PB9, Alternate<Funsel1>>, Pin<PB8, Alternate<Funsel1>>) {}
impl Pins<UARTA> for (Pin<PB23, Alternate<Funsel1>>, Pin<PB22, Alternate<Funsel1>>) {}
impl Pins<UARTA> for (Pin<PB9, AltFunc1>, Pin<PB8, AltFunc1>) {}
impl Pins<UARTA> for (Pin<PB23, AltFunc1>, Pin<PB22, AltFunc1>) {}

impl Pins<UARTB> for (Pin<PA3, Alternate<Funsel2>>, Pin<PA2, Alternate<Funsel2>>) {}
impl Pins<UARTB> for (Pin<PA19, Alternate<Funsel3>>, Pin<PA18, Alternate<Funsel3>>) {}
impl Pins<UARTB> for (Pin<PA27, Alternate<Funsel3>>, Pin<PA26, Alternate<Funsel3>>) {}
impl Pins<UARTB> for (Pin<PA3, AltFunc2>, Pin<PA2, AltFunc2>) {}
impl Pins<UARTB> for (Pin<PA19, AltFunc3>, Pin<PA18, AltFunc3>) {}
impl Pins<UARTB> for (Pin<PA27, AltFunc3>, Pin<PA26, AltFunc3>) {}

impl Pins<UARTB> for (Pin<PB7, Alternate<Funsel1>>, Pin<PB6, Alternate<Funsel1>>) {}
impl Pins<UARTB> for (Pin<PB19, Alternate<Funsel2>>, Pin<PB18, Alternate<Funsel2>>) {}
impl Pins<UARTB> for (Pin<PB21, Alternate<Funsel1>>, Pin<PB20, Alternate<Funsel1>>) {}
impl Pins<UARTB> for (Pin<PB7, AltFunc1>, Pin<PB6, AltFunc1>) {}
impl Pins<UARTB> for (Pin<PB19, AltFunc2>, Pin<PB18, AltFunc2>) {}
impl Pins<UARTB> for (Pin<PB21, AltFunc1>, Pin<PB20, AltFunc1>) {}

#[derive(Debug)]
pub enum Error {
Expand Down

0 comments on commit 7c3a425

Please sign in to comment.