11//! (TODO) Serial Peripheral Interface (SPI)
22
3- use crate :: pac:: SPI0 ;
43use crate :: clock:: Clocks ;
4+ use crate :: pac:: spi0:: ctrlr0:: TMOD_A as transfer_mode;
5+ use crate :: pac:: SPI0 ;
56use crate :: sysctl:: { self , APB0 } ;
6- pub use embedded_hal:: spi:: { Mode , Polarity , Phase } ;
77use core:: convert:: Infallible ;
8+ pub use embedded_hal:: spi:: { Mode , Phase , Polarity } ;
89
9- ///
1010pub 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
1418impl 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
6980impl 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]
102153fn 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