1
1
//! (TODO) Serial Peripheral Interface (SPI)
2
2
3
- use crate :: pac:: SPI0 ;
4
3
use crate :: clock:: Clocks ;
4
+ use crate :: pac:: spi0:: ctrlr0:: TMOD_A as transfer_mode;
5
+ use crate :: pac:: SPI0 ;
5
6
use crate :: sysctl:: { self , APB0 } ;
6
- pub use embedded_hal:: spi:: { Mode , Polarity , Phase } ;
7
7
use core:: convert:: Infallible ;
8
+ pub use embedded_hal:: spi:: { Mode , Phase , Polarity } ;
8
9
9
- ///
10
10
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 ,
12
16
}
13
17
14
18
impl Spi < SPI0 > {
15
19
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 ,
22
27
) -> Self {
23
28
let work_mode = hal_mode_to_pac ( mode) ;
24
29
let frame_format = frame_format_to_pac ( frame_format) ;
25
- let tmod = crate :: pac:: spi0:: ctrlr0:: TMOD_A :: TRANS_RECV ; // todo other modes
26
30
let endian = endian as u32 ;
27
31
let data_bit_length = 8 ; // todo more length
28
32
let _ = clock; // todo
29
33
unsafe {
30
34
// no interrupts for now
31
- spi. imr . write ( |w| w. bits ( 0x00 ) ) ;
35
+ spi. imr . write ( |w| w. bits ( 0x0 ) ) ;
32
36
// no dma for now
33
- spi. dmacr . write ( |w| w. bits ( 0x00 ) ) ;
37
+ spi. dmacr . write ( |w| w. bits ( 0x0 ) ) ;
34
38
spi. dmatdlr . write ( |w| w. bits ( 0x10 ) ) ;
35
- spi. dmardlr . write ( |w| w. bits ( 0x00 ) ) ;
39
+ spi. dmardlr . write ( |w| w. bits ( 0x0 ) ) ;
36
40
// 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 ) ) ;
39
43
// set control registers
40
44
spi. ctrlr0 . write ( |w| {
45
+ // no need to set tmod here, which will (and needs to) be set on each send/recv
41
46
w. work_mode ( )
42
47
. variant ( work_mode)
43
- . tmod ( )
44
- . variant ( tmod)
45
48
. frame_format ( )
46
49
. variant ( frame_format)
47
50
. data_length ( )
@@ -53,19 +56,27 @@ impl Spi<SPI0> {
53
56
// enable APB0 bus
54
57
apb0. enable ( ) ;
55
58
// 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 }
59
61
}
60
62
61
63
pub fn release ( self ) -> SPI0 {
62
64
// 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 ( ) ) ;
65
66
self . spi
66
67
}
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
+ }
67
76
}
68
77
78
+ // todo: Shall we make FrameFormat a type parameter instead?
79
+ // so FullDuplex<u8> can be implemented for Spi<SPI0, FrameFormat::Standard> only
69
80
impl embedded_hal:: spi:: FullDuplex < u8 > for Spi < SPI0 > {
70
81
/// An enumeration of SPI errors
71
82
type Error = Infallible ;
@@ -75,12 +86,52 @@ impl embedded_hal::spi::FullDuplex<u8> for Spi<SPI0> {
75
86
/// **NOTE** A word must be sent to the slave before attempting to call this
76
87
/// method.
77
88
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
79
111
}
80
112
81
113
/// Sends a word to the slave
82
114
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
84
135
}
85
136
}
86
137
@@ -101,7 +152,7 @@ pub enum Endian {
101
152
#[ inline]
102
153
fn hal_mode_to_pac ( mode : Mode ) -> crate :: pac:: spi0:: ctrlr0:: WORK_MODE_A {
103
154
use crate :: pac:: spi0:: ctrlr0:: WORK_MODE_A ;
104
- use { Polarity :: * , Phase :: * } ;
155
+ use { Phase :: * , Polarity :: * } ;
105
156
match ( mode. polarity , mode. phase ) {
106
157
( IdleLow , CaptureOnFirstTransition ) => WORK_MODE_A :: MODE0 ,
107
158
( IdleLow , CaptureOnSecondTransition ) => WORK_MODE_A :: MODE1 ,
0 commit comments