1use crate::crc::crc7;
2use crate::error::Error;
3use embedded_hal::blocking::spi::Transfer;
4use embedded_hal::digital::v2::OutputPin;
5
6pub mod commands {
9 pub const CMD_DMA_WRITE: u8 = 0xc1; pub const CMD_DMA_READ: u8 = 0xc2; pub const CMD_INTERNAL_WRITE: u8 = 0xc3; pub const CMD_INTERNAL_READ: u8 = 0xc4; pub const CMD_TERMINATE: u8 = 0xc5; pub const CMD_REPEAT: u8 = 0xc6; pub const CMD_DMA_EXT_WRITE: u8 = 0xc7; pub const CMD_DMA_EXT_READ: u8 = 0xc8; pub const CMD_SINGLE_WRITE: u8 = 0xc9; pub const CMD_SINGLE_READ: u8 = 0xca; pub const CMD_RESET: u8 = 0xcf; }
25
26mod sizes {
29 pub const CRC_BIT: usize = 1;
30 pub const RESPONSE: usize = 2;
31 pub const DATA_START: usize = 1;
32 pub const DATA: usize = 4;
33 pub const TYPE_A: usize = 4;
35 pub const TYPE_B: usize = 6;
36 pub const TYPE_C: usize = 7;
37 pub const TYPE_D: usize = 8;
38 pub const TYPE_A_CRC: usize = TYPE_A + CRC_BIT;
40 pub const _TYPE_B_CRC: usize = TYPE_B + CRC_BIT;
41 pub const TYPE_C_CRC: usize = TYPE_C + CRC_BIT;
42 pub const TYPE_D_CRC: usize = TYPE_D + CRC_BIT;
43}
44
45#[repr(u8)]
54#[derive(Eq, PartialEq, PartialOrd)]
55pub enum SpiError {
56 NoError = 0,
57 UnsupportedCommand = 1,
58 UnexpectedDataReceived = 2,
59 Crc7Error = 3,
60 Crc16Error = 4,
61 InternalError = 5,
62 InvalidError,
63}
64
65impl From<u8> for SpiError {
66 fn from(other: u8) -> Self {
69 match other {
70 0 => SpiError::NoError,
71 1 => SpiError::UnsupportedCommand,
72 2 => SpiError::UnexpectedDataReceived,
73 3 => SpiError::Crc7Error,
74 4 => SpiError::Crc16Error,
75 5 => SpiError::InternalError,
76 _ => SpiError::InvalidError,
77 }
78 }
79}
80
81#[repr(u8)]
86enum SpiPacket {
87 _First = 0b11110001,
88 _Neither = 0b11110010,
89 Last = 0b11110011,
90 _Reserved = 0b11111111,
91}
92
93pub struct SpiBus<SPI, O>
97where
98 SPI: Transfer<u8>,
99 O: OutputPin,
100{
101 spi: SPI,
102 cs: O,
103 crc: bool,
104 crc_disabled: bool,
105}
106
107impl<SPI, O> SpiBus<SPI, O>
108where
109 SPI: Transfer<u8>,
110 O: OutputPin,
111{
112 pub fn new(spi: SPI, cs: O, crc: bool) -> Self {
114 Self {
115 spi,
116 cs,
117 crc,
118 crc_disabled: false,
119 }
120 }
121
122 pub fn init_cs(&mut self) -> Result<(), Error> {
125 match self.cs.set_high() {
126 Ok(_) => Ok(()),
127 Err(_) => Err(Error::PinStateError),
128 }
129 }
130
131 pub fn crc_disabled(&mut self) -> Result<(), Error> {
133 self.crc_disabled = true;
134 Ok(())
135 }
136
137 fn transfer(&mut self, words: &'_ mut [u8]) -> Result<(), Error> {
139 if self.cs.set_low().is_err() {
140 return Err(Error::PinStateError);
141 }
142 if self.spi.transfer(words).is_err() {
143 return Err(Error::SpiTransferError);
144 }
145 if self.cs.set_high().is_err() {
146 return Err(Error::PinStateError);
147 }
148 Ok(())
149 }
150
151 pub fn command(
156 &mut self,
157 cmd_buffer: &'_ mut [u8],
158 command: u8,
159 address: u32,
160 data: u32,
161 size: u32,
162 clockless: bool,
163 ) -> Result<(), Error> {
164 cmd_buffer[0] = command;
165 let mut crc_index: usize = 0;
166 match command {
167 commands::CMD_DMA_WRITE => {}
168 commands::CMD_DMA_READ => {
169 cmd_buffer[1] = (address >> 16) as u8;
170 cmd_buffer[2] = (address >> 8) as u8;
171 cmd_buffer[3] = address as u8;
172 cmd_buffer[4] = (size >> 8) as u8;
173 cmd_buffer[5] = size as u8;
174 crc_index = sizes::TYPE_B;
175 }
176 commands::CMD_INTERNAL_WRITE => {
177 cmd_buffer[1] = (address >> 8) as u8;
178 if clockless {
179 cmd_buffer[1] |= 1 << 7;
180 }
181 cmd_buffer[2] = address as u8;
182 cmd_buffer[3] = (data >> 24) as u8;
183 cmd_buffer[4] = (data >> 16) as u8;
184 cmd_buffer[5] = (data >> 8) as u8;
185 cmd_buffer[6] = data as u8;
186 crc_index = sizes::TYPE_C;
187 }
188 commands::CMD_INTERNAL_READ => {
189 cmd_buffer[1] = (address >> 8) as u8;
190 if clockless {
191 cmd_buffer[1] |= 1 << 7;
192 }
193 cmd_buffer[2] = address as u8;
194 cmd_buffer[3] = 0;
195 crc_index = sizes::TYPE_A;
196 }
197 commands::CMD_TERMINATE => {
198 cmd_buffer[1] = 0x0;
199 cmd_buffer[2] = 0x0;
200 cmd_buffer[3] = 0x0;
201 crc_index = sizes::TYPE_A;
202 }
203 commands::CMD_REPEAT => {
204 cmd_buffer[1] = 0x0;
205 cmd_buffer[2] = 0x0;
206 cmd_buffer[3] = 0x0;
207 crc_index = sizes::TYPE_A;
208 }
209 commands::CMD_DMA_EXT_WRITE => {
210 cmd_buffer[1] = (address >> 16) as u8;
211 cmd_buffer[2] = (address >> 8) as u8;
212 cmd_buffer[3] = address as u8;
213 cmd_buffer[4] = (size >> 16) as u8;
214 cmd_buffer[5] = (size >> 8) as u8;
215 cmd_buffer[6] = size as u8;
216 crc_index = 0;
217 }
218 commands::CMD_DMA_EXT_READ => {
219 cmd_buffer[1] = (address >> 16) as u8;
220 cmd_buffer[2] = (address >> 8) as u8;
221 cmd_buffer[3] = address as u8;
222 cmd_buffer[4] = (size >> 16) as u8;
223 cmd_buffer[5] = (size >> 8) as u8;
224 cmd_buffer[6] = size as u8;
225 crc_index = 0;
226 }
227 commands::CMD_SINGLE_WRITE => {
228 cmd_buffer[1] = (address >> 16) as u8;
229 cmd_buffer[2] = (address >> 8) as u8;
230 cmd_buffer[3] = address as u8;
231 cmd_buffer[4] = (data >> 24) as u8;
232 cmd_buffer[5] = (data >> 16) as u8;
233 cmd_buffer[6] = (data >> 8) as u8;
234 cmd_buffer[7] = data as u8;
235 crc_index = sizes::TYPE_D;
236 }
237 commands::CMD_SINGLE_READ => {
238 cmd_buffer[1] = (address >> 16) as u8;
239 cmd_buffer[2] = (address >> 8) as u8;
240 cmd_buffer[3] = address as u8;
241 crc_index = sizes::TYPE_A;
242 }
243 commands::CMD_RESET => {
244 cmd_buffer[1] = 0xff;
245 cmd_buffer[2] = 0xff;
246 cmd_buffer[3] = 0xff;
247 crc_index = sizes::TYPE_A;
248 }
249 _ => {
250 return Err(Error::InvalidSpiCommandError);
251 }
252 }
253 if self.crc || !self.crc_disabled {
254 cmd_buffer[crc_index] = crc7(0x7f, &cmd_buffer[0..crc_index]) << 1;
255 }
256 self.transfer(cmd_buffer)?;
257 Ok(())
258 }
259
260 pub fn read_register(&mut self, address: u32) -> Result<u32, Error> {
263 match self.crc_disabled {
264 true => {
265 const SIZE: usize =
266 sizes::TYPE_A + sizes::RESPONSE + sizes::DATA_START + sizes::DATA;
267 Ok(self.read_reg::<SIZE>(address, 7, 11, 4)?)
271 }
272 false => {
273 const SIZE: usize =
274 sizes::TYPE_A_CRC + sizes::RESPONSE + sizes::DATA_START + sizes::DATA;
275 Ok(self.read_reg::<SIZE>(address, 8, 12, 5)?)
279 }
280 }
281 }
282
283 fn read_reg<const S: usize>(
286 &mut self,
287 address: u32,
288 beg: usize,
289 end: usize,
290 response_start: usize,
291 ) -> Result<u32, Error> {
292 let cmd: u8;
293 let clockless: bool;
294 let mut cmd_buffer: [u8; S] = [0; S];
295 if address <= 0xff {
298 cmd = commands::CMD_INTERNAL_READ;
299 clockless = true;
300 } else {
301 cmd = commands::CMD_SINGLE_READ;
302 clockless = false;
303 }
304 self.command(&mut cmd_buffer, cmd, address, 0, 0, clockless)?;
305 if cmd_buffer[response_start] != cmd || cmd_buffer[response_start + 2] & 0xf0 != 0xf0 {
306 return Err(Error::SpiReadRegisterError);
307 }
308 Ok(combine_bytes_lsb!(cmd_buffer[beg..end]))
309 }
310
311 pub fn read_data(&mut self, data: &mut [u8], address: u32, count: u32) -> Result<(), Error> {
314 match self.crc_disabled {
315 true => {
316 const SIZE: usize = sizes::TYPE_C;
317 Ok(self.read::<SIZE>(data, address, count)?)
318 }
319 false => {
320 const SIZE: usize = sizes::TYPE_C_CRC;
321 Ok(self.read::<SIZE>(data, address, count)?)
322 }
323 }
324 }
325
326 fn read<const S: usize>(
328 &mut self,
329 data: &mut [u8],
330 address: u32,
331 count: u32,
332 ) -> Result<(), Error> {
333 let cmd: u8 = commands::CMD_DMA_EXT_READ;
334 let mut cmd_buffer: [u8; S] = [0; S];
335 let mut response: [u8; sizes::RESPONSE + sizes::DATA_START] =
336 [0; sizes::RESPONSE + sizes::DATA_START];
337 self.command(&mut cmd_buffer, cmd, address, 0, count, false)?;
338 retry_while!(response[0] == 0, retries = 10, {
339 self.transfer(&mut response)?;
340 });
341 if response[0] == cmd {
342 self.transfer(data)?;
343 }
344 Ok(())
345 }
346
347 pub fn write_register(&mut self, address: u32, data: u32) -> Result<(), Error> {
350 match self.crc_disabled {
351 true => {
353 const SIZE: usize = sizes::TYPE_D + sizes::RESPONSE;
354 Ok(self.write_reg::<SIZE>(address, data, 8)?)
355 }
356 false => {
358 const SIZE: usize = sizes::TYPE_D_CRC + sizes::RESPONSE;
359 Ok(self.write_reg::<SIZE>(address, data, 9)?)
360 }
361 }
362 }
363
364 fn write_reg<const S: usize>(
366 &mut self,
367 address: u32,
368 data: u32,
369 response_start: usize,
370 ) -> Result<(), Error> {
371 let cmd: u8;
372 let clockless: bool;
373 let mut cmd_buffer: [u8; S] = [0; S];
374 if address <= 0x30 {
377 cmd = commands::CMD_INTERNAL_WRITE;
378 clockless = true;
379 } else {
380 cmd = commands::CMD_SINGLE_WRITE;
381 clockless = false;
382 }
383 self.command(&mut cmd_buffer, cmd, address, data, 0, clockless)?;
384 if cmd_buffer[response_start] != cmd || cmd_buffer[response_start + 1] != 0 {
385 return Err(Error::SpiWriteRegisterError);
386 }
387 Ok(())
388 }
389
390 pub fn write_data(&mut self, data: &mut [u8], address: u32, count: u32) -> Result<(), Error> {
393 match self.crc_disabled {
394 true => {
395 const SIZE: usize = sizes::TYPE_C;
396 Ok(self.write::<SIZE>(data, address, count)?)
397 }
398 false => {
399 const SIZE: usize = sizes::TYPE_C_CRC;
400 Ok(self.write::<SIZE>(data, address, count)?)
401 }
402 }
403 }
404
405 fn write<const S: usize>(
407 &mut self,
408 data: &mut [u8],
409 address: u32,
410 count: u32,
411 ) -> Result<(), Error> {
412 let cmd: u8 = commands::CMD_DMA_EXT_WRITE;
413 let mut cmd_buffer: [u8; S] = [0; S];
414 let mut response: [u8; sizes::RESPONSE] = [0; sizes::RESPONSE];
415 let data_mark: u8 = SpiPacket::Last as u8;
416 self.command(&mut cmd_buffer, cmd, address, 0, count, false)?;
417 self.transfer(&mut response)?;
418 if response[0] == cmd {
419 self.transfer(&mut [data_mark])?;
420 self.transfer(data)?;
421 response[0] = 0;
422 retry_while!(response[0] != 0xc3, retries = 10, {
423 self.transfer(&mut response[0..1])?;
424 });
425 }
426 Ok(())
427 }
428}