Skip to content

Commit

Permalink
impl embedded_hal::spi::FullDuplex<u8> for Spi<SPI0>
Browse files Browse the repository at this point in the history
  • Loading branch information
longfangsong committed Feb 21, 2021
1 parent 98fb515 commit e2a4260
Showing 1 changed file with 77 additions and 26 deletions.
103 changes: 77 additions & 26 deletions src/spi.rs
Original file line number Diff line number Diff line change
@@ -1,47 +1,50 @@
//! (TODO) Serial Peripheral Interface (SPI)
use crate::pac::SPI0;
use crate::clock::Clocks;
use crate::pac::spi0::ctrlr0::TMOD_A as transfer_mode;
use crate::pac::SPI0;
use crate::sysctl::{self, APB0};
pub use embedded_hal::spi::{Mode, Polarity, Phase};
use core::convert::Infallible;
pub use embedded_hal::spi::{Mode, Phase, Polarity};

///
pub struct Spi<SPI> {
spi: SPI
spi: SPI,
// Different with other MCUs like STM32 and Atmel
// k210 use fpioa to map a pin directly to SPI SS 0-3 instead of using an ordinary GPIO
// when transferring data, we'll set `pac::SPIX::ser` to (1 << cs_id) to select this device
cs_id: u8,
}

impl Spi<SPI0> {
pub fn spi0(
spi: SPI0,
mode: Mode,
frame_format: FrameFormat,
endian: Endian,
clock: &Clocks,
apb0: &mut APB0
spi: SPI0,
cs_id: u8, // todo: currently we presume SPI0_SS<cs_id> is already configured correctly, maybe we can do this for the user?
mode: Mode,
frame_format: FrameFormat,
endian: Endian,
clock: &Clocks,
apb0: &mut APB0,
) -> Self {
let work_mode = hal_mode_to_pac(mode);
let frame_format = frame_format_to_pac(frame_format);
let tmod = crate::pac::spi0::ctrlr0::TMOD_A::TRANS_RECV; // todo other modes
let endian = endian as u32;
let data_bit_length = 8; // todo more length
let _ = clock; // todo
unsafe {
// no interrupts for now
spi.imr.write(|w| w.bits(0x00));
spi.imr.write(|w| w.bits(0x0));
// no dma for now
spi.dmacr.write(|w| w.bits(0x00));
spi.dmacr.write(|w| w.bits(0x0));
spi.dmatdlr.write(|w| w.bits(0x10));
spi.dmardlr.write(|w| w.bits(0x00));
spi.dmardlr.write(|w| w.bits(0x0));
// no slave access for now
spi.ser.write(|w| w.bits(0x00));
spi.ssienr.write(|w| w.bits(0x00));
spi.ser.write(|w| w.bits(0x0));
spi.ssienr.write(|w| w.bits(0x0));
// set control registers
spi.ctrlr0.write(|w| {
// no need to set tmod here, which will (and needs to) be set on each send/recv
w.work_mode()
.variant(work_mode)
.tmod()
.variant(tmod)
.frame_format()
.variant(frame_format)
.data_length()
Expand All @@ -53,19 +56,27 @@ impl Spi<SPI0> {
// enable APB0 bus
apb0.enable();
// enable peripheral via sysctl
sysctl::clk_en_peri().modify(|_r, w|
w.spi0_clk_en().set_bit());
Spi { spi }
sysctl::clk_en_peri().modify(|_r, w| w.spi0_clk_en().set_bit());
Spi { spi, cs_id }
}

pub fn release(self) -> SPI0 {
// power off
sysctl::clk_en_peri().modify(|_r, w|
w.spi0_clk_en().clear_bit());
sysctl::clk_en_peri().modify(|_r, w| w.spi0_clk_en().clear_bit());
self.spi
}

/// for making our life easier to use the same SPI interface but with different chip selected
pub fn take_for_cs(self, cs_id: u8) -> Self {
Self {
spi: self.spi,
cs_id,
}
}
}

// todo: Shall we make FrameFormat a type parameter instead?
// so FullDuplex<u8> can be implemented for Spi<SPI0, FrameFormat::Standard> only
impl embedded_hal::spi::FullDuplex<u8> for Spi<SPI0> {
/// An enumeration of SPI errors
type Error = Infallible;
Expand All @@ -75,12 +86,52 @@ impl embedded_hal::spi::FullDuplex<u8> for Spi<SPI0> {
/// **NOTE** A word must be sent to the slave before attempting to call this
/// method.
fn try_read(&mut self) -> nb::Result<u8, Self::Error> {
todo!()
self.spi
.ctrlr0
.modify(|_, w| w.tmod().variant(transfer_mode::RECV));
unsafe {
// C sdk said ctrlr1 = rx_len(1) / frame_width(1) - 1;
self.spi.ctrlr1.write(|w| w.bits(0x0));
// enable spi
self.spi.ssienr.write(|w| w.bits(0x1));
// select that chip
self.spi.ser.write(|w| w.bits(0x1 << self.cs_id));
// clear dr
self.spi.dr[0].write(|w| w.bits(0xffffffff));
}
let bytes_in_buffer = self.spi.rxflr.read().bits();
let result = if bytes_in_buffer == 0 {
Err(nb::Error::WouldBlock)
} else {
Ok(self.spi.dr[0].read().bits() as u8)
};
self.spi.ser.reset();
self.spi.ssienr.reset();
result
}

/// Sends a word to the slave
fn try_send(&mut self, word: u8) -> nb::Result<(), Self::Error> {
todo!("{}", word)
self.spi
.ctrlr0
.modify(|_, w| w.tmod().variant(transfer_mode::TRANS));
unsafe {
self.spi.ssienr.write(|w| w.bits(0x0));
self.spi.ser.write(|w| w.bits(0x1 << self.cs_id));
}
const MAX_FIFO_SIZE: u32 = 32;
let empty_in_buffer = MAX_FIFO_SIZE - self.spi.txflr.read().bits();
let result = if empty_in_buffer == 0 {
Err(nb::Error::WouldBlock)
} else {
unsafe {
self.spi.dr[0].write(|w| w.bits(word as u32));
}
Ok(())
};
self.spi.ser.reset();
self.spi.ssienr.reset();
result
}
}

Expand All @@ -101,7 +152,7 @@ pub enum Endian {
#[inline]
fn hal_mode_to_pac(mode: Mode) -> crate::pac::spi0::ctrlr0::WORK_MODE_A {
use crate::pac::spi0::ctrlr0::WORK_MODE_A;
use {Polarity::*, Phase::*};
use {Phase::*, Polarity::*};
match (mode.polarity, mode.phase) {
(IdleLow, CaptureOnFirstTransition) => WORK_MODE_A::MODE0,
(IdleLow, CaptureOnSecondTransition) => WORK_MODE_A::MODE1,
Expand Down

0 comments on commit e2a4260

Please sign in to comment.