Skip to content

Commit e2a4260

Browse files
committed
impl embedded_hal::spi::FullDuplex<u8> for Spi<SPI0>
1 parent 98fb515 commit e2a4260

File tree

1 file changed

+77
-26
lines changed

1 file changed

+77
-26
lines changed

src/spi.rs

+77-26
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,50 @@
11
//! (TODO) Serial Peripheral Interface (SPI)
22
3-
use crate::pac::SPI0;
43
use crate::clock::Clocks;
4+
use crate::pac::spi0::ctrlr0::TMOD_A as transfer_mode;
5+
use crate::pac::SPI0;
56
use crate::sysctl::{self, APB0};
6-
pub use embedded_hal::spi::{Mode, Polarity, Phase};
77
use core::convert::Infallible;
8+
pub use embedded_hal::spi::{Mode, Phase, Polarity};
89

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

1418
impl Spi<SPI0> {
1519
pub fn spi0(
16-
spi: SPI0,
17-
mode: Mode,
18-
frame_format: FrameFormat,
19-
endian: Endian,
20-
clock: &Clocks,
21-
apb0: &mut APB0
20+
spi: SPI0,
21+
cs_id: u8, // todo: currently we presume SPI0_SS<cs_id> is already configured correctly, maybe we can do this for the user?
22+
mode: Mode,
23+
frame_format: FrameFormat,
24+
endian: Endian,
25+
clock: &Clocks,
26+
apb0: &mut APB0,
2227
) -> Self {
2328
let work_mode = hal_mode_to_pac(mode);
2429
let frame_format = frame_format_to_pac(frame_format);
25-
let tmod = crate::pac::spi0::ctrlr0::TMOD_A::TRANS_RECV; // todo other modes
2630
let endian = endian as u32;
2731
let data_bit_length = 8; // todo more length
2832
let _ = clock; // todo
2933
unsafe {
3034
// no interrupts for now
31-
spi.imr.write(|w| w.bits(0x00));
35+
spi.imr.write(|w| w.bits(0x0));
3236
// no dma for now
33-
spi.dmacr.write(|w| w.bits(0x00));
37+
spi.dmacr.write(|w| w.bits(0x0));
3438
spi.dmatdlr.write(|w| w.bits(0x10));
35-
spi.dmardlr.write(|w| w.bits(0x00));
39+
spi.dmardlr.write(|w| w.bits(0x0));
3640
// no slave access for now
37-
spi.ser.write(|w| w.bits(0x00));
38-
spi.ssienr.write(|w| w.bits(0x00));
41+
spi.ser.write(|w| w.bits(0x0));
42+
spi.ssienr.write(|w| w.bits(0x0));
3943
// set control registers
4044
spi.ctrlr0.write(|w| {
45+
// no need to set tmod here, which will (and needs to) be set on each send/recv
4146
w.work_mode()
4247
.variant(work_mode)
43-
.tmod()
44-
.variant(tmod)
4548
.frame_format()
4649
.variant(frame_format)
4750
.data_length()
@@ -53,19 +56,27 @@ impl Spi<SPI0> {
5356
// enable APB0 bus
5457
apb0.enable();
5558
// enable peripheral via sysctl
56-
sysctl::clk_en_peri().modify(|_r, w|
57-
w.spi0_clk_en().set_bit());
58-
Spi { spi }
59+
sysctl::clk_en_peri().modify(|_r, w| w.spi0_clk_en().set_bit());
60+
Spi { spi, cs_id }
5961
}
6062

6163
pub fn release(self) -> SPI0 {
6264
// power off
63-
sysctl::clk_en_peri().modify(|_r, w|
64-
w.spi0_clk_en().clear_bit());
65+
sysctl::clk_en_peri().modify(|_r, w| w.spi0_clk_en().clear_bit());
6566
self.spi
6667
}
68+
69+
/// for making our life easier to use the same SPI interface but with different chip selected
70+
pub fn take_for_cs(self, cs_id: u8) -> Self {
71+
Self {
72+
spi: self.spi,
73+
cs_id,
74+
}
75+
}
6776
}
6877

78+
// todo: Shall we make FrameFormat a type parameter instead?
79+
// so FullDuplex<u8> can be implemented for Spi<SPI0, FrameFormat::Standard> only
6980
impl embedded_hal::spi::FullDuplex<u8> for Spi<SPI0> {
7081
/// An enumeration of SPI errors
7182
type Error = Infallible;
@@ -75,12 +86,52 @@ impl embedded_hal::spi::FullDuplex<u8> for Spi<SPI0> {
7586
/// **NOTE** A word must be sent to the slave before attempting to call this
7687
/// method.
7788
fn try_read(&mut self) -> nb::Result<u8, Self::Error> {
78-
todo!()
89+
self.spi
90+
.ctrlr0
91+
.modify(|_, w| w.tmod().variant(transfer_mode::RECV));
92+
unsafe {
93+
// C sdk said ctrlr1 = rx_len(1) / frame_width(1) - 1;
94+
self.spi.ctrlr1.write(|w| w.bits(0x0));
95+
// enable spi
96+
self.spi.ssienr.write(|w| w.bits(0x1));
97+
// select that chip
98+
self.spi.ser.write(|w| w.bits(0x1 << self.cs_id));
99+
// clear dr
100+
self.spi.dr[0].write(|w| w.bits(0xffffffff));
101+
}
102+
let bytes_in_buffer = self.spi.rxflr.read().bits();
103+
let result = if bytes_in_buffer == 0 {
104+
Err(nb::Error::WouldBlock)
105+
} else {
106+
Ok(self.spi.dr[0].read().bits() as u8)
107+
};
108+
self.spi.ser.reset();
109+
self.spi.ssienr.reset();
110+
result
79111
}
80112

81113
/// Sends a word to the slave
82114
fn try_send(&mut self, word: u8) -> nb::Result<(), Self::Error> {
83-
todo!("{}", word)
115+
self.spi
116+
.ctrlr0
117+
.modify(|_, w| w.tmod().variant(transfer_mode::TRANS));
118+
unsafe {
119+
self.spi.ssienr.write(|w| w.bits(0x0));
120+
self.spi.ser.write(|w| w.bits(0x1 << self.cs_id));
121+
}
122+
const MAX_FIFO_SIZE: u32 = 32;
123+
let empty_in_buffer = MAX_FIFO_SIZE - self.spi.txflr.read().bits();
124+
let result = if empty_in_buffer == 0 {
125+
Err(nb::Error::WouldBlock)
126+
} else {
127+
unsafe {
128+
self.spi.dr[0].write(|w| w.bits(word as u32));
129+
}
130+
Ok(())
131+
};
132+
self.spi.ser.reset();
133+
self.spi.ssienr.reset();
134+
result
84135
}
85136
}
86137

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

0 commit comments

Comments
 (0)