atwinc1500/
spi.rs

1use crate::crc::crc7;
2use crate::error::Error;
3use embedded_hal::blocking::spi::Transfer;
4use embedded_hal::digital::v2::OutputPin;
5
6/// This module contains the valid
7/// Spi commands for the Atwinc1500
8pub mod commands {
9    // Command start + Command Type
10    // Command start = 0b1100
11    // Example: 0b1100 | 0b0001 into one byte
12    // CMD_DMA_WRITE = 0b11000001
13    pub const CMD_DMA_WRITE: u8 = 0xc1; // type B
14    pub const CMD_DMA_READ: u8 = 0xc2; // type B
15    pub const CMD_INTERNAL_WRITE: u8 = 0xc3; // type C
16    pub const CMD_INTERNAL_READ: u8 = 0xc4; // type A
17    pub const CMD_TERMINATE: u8 = 0xc5; // type A
18    pub const CMD_REPEAT: u8 = 0xc6; // type A
19    pub const CMD_DMA_EXT_WRITE: u8 = 0xc7; // type C
20    pub const CMD_DMA_EXT_READ: u8 = 0xc8; // type C
21    pub const CMD_SINGLE_WRITE: u8 = 0xc9; // type D
22    pub const CMD_SINGLE_READ: u8 = 0xca; // type A
23    pub const CMD_RESET: u8 = 0xcf; // type A
24}
25
26/// This module contains the different
27/// sizes for each Spi command type
28mod 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    // Command size without crc bit
34    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    // Full command packet size with crc bit
39    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/// These are the error values defined
46/// in the Atwinc data sheet. InvalidError is
47/// a catch all for error values greater than
48/// 5 that are not real errors. If InvalidError
49/// is caught, then the responses are no longer
50/// being read correctly. These errors should be
51/// handled with the error recovery mechanisms
52/// also defined in the data sheet.
53#[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    /// For easily converting a response byte
67    /// to an SpiError type
68    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/// These bytes are used to determine if
82/// there are more packets to be read when
83/// doing multi packet transfers. They also
84/// help with readability
85#[repr(u8)]
86enum SpiPacket {
87    _First = 0b11110001,
88    _Neither = 0b11110010,
89    Last = 0b11110011,
90    _Reserved = 0b11111111,
91}
92
93/// The SpiBus struct
94/// handles all reads/writes that
95/// happen over the FullDuplex spi bus
96pub 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    /// Creates a new SpiBus struct
113    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    /// Pulls the chip select high
123    /// as it is active low
124    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    /// Sets crc_disabled to true
132    pub fn crc_disabled(&mut self) -> Result<(), Error> {
133        self.crc_disabled = true;
134        Ok(())
135    }
136
137    /// Sends some data then receives some data on the spi bus
138    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    /// Matches the command argument and formats
152    /// the address, data, and size arguments
153    /// into the cmd_buffer as described in the
154    /// software design guide then sends the command
155    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    /// Wraps the read_reg method to pass it the size
261    /// of the command buffer based on crc being enabled
262    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                // 7..11 is the range of the data returned from the atwinc
268                // when crc is disabled and 4 is where the response from
269                // the atwinc starts
270                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                // 8..12 is the range of the data returned from the atwinc
276                // when crc is enabled and 5 is where the response from
277                // the atwinc starts
278                Ok(self.read_reg::<SIZE>(address, 8, 12, 5)?)
279            }
280        }
281    }
282
283    /// Reads a value from a register at a given address
284    /// and returns it
285    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        // The Atmel driver does a clockless read
296        // if address is less than 0xff (0b11111111).
297        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    /// Wraps the read method to change the command buffer size
312    /// depending on crc being enabled or not
313    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    /// Reads a block of data
327    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    /// Wraps the read_reg method to pass it the size
348    /// of the command buffer based on crc being enabled
349    pub fn write_register(&mut self, address: u32, data: u32) -> Result<(), Error> {
350        match self.crc_disabled {
351            // response starts at index 8
352            true => {
353                const SIZE: usize = sizes::TYPE_D + sizes::RESPONSE;
354                Ok(self.write_reg::<SIZE>(address, data, 8)?)
355            }
356            // response starts at index 9
357            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    /// Writes a value to a register at a given address
365    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        // The Atmel driver does a clockless write
375        // if address is less than 0x30 (0b00110000).
376        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    /// Wraps the write method to change the command buffer size
391    /// depending on crc being enabled or not
392    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    /// Writes a block of data to the atwinc1500
406    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}