pokeys_lib/protocols/
spi.rs

1//! SPI protocol implementation
2
3use crate::device::PoKeysDevice;
4use crate::error::{PoKeysError, Result};
5
6/// SPI protocol implementation
7impl PoKeysDevice {
8    /// Configure SPI interface
9    ///
10    /// # Arguments
11    /// * `prescaler` - SPI clock prescaler value (affects SPI clock speed)
12    /// * `frame_format` - SPI frame format configuration
13    ///
14    /// This matches the C library function: PK_SPIConfigure(device, prescaler, frameFormat)
15    pub fn spi_configure(&mut self, prescaler: u8, frame_format: u8) -> Result<()> {
16        // Command 0xE5, sub-command 0x01 for SPI configuration
17        self.send_request(0xE5, 0x01, prescaler, frame_format, 0)?;
18        Ok(())
19    }
20
21    /// Write data to SPI bus
22    ///
23    /// # Arguments
24    /// * `buffer` - Data buffer to write (maximum 55 bytes)
25    /// * `pin_cs` - Chip select pin number
26    ///
27    /// This matches the C library function: PK_SPIWrite(device, buffer, length, pinCS)
28    pub fn spi_write(&mut self, buffer: &[u8], pin_cs: u8) -> Result<()> {
29        if buffer.is_empty() {
30            return Err(PoKeysError::Parameter(
31                "SPI buffer cannot be empty".to_string(),
32            ));
33        }
34
35        if buffer.len() > 55 {
36            return Err(PoKeysError::Parameter(
37                "SPI data too long (maximum 55 bytes)".to_string(),
38            ));
39        }
40
41        // Send request with data payload
42        let response = self.send_request_with_data(
43            0xE5,               // Command
44            0x10,               // Sub-command for write
45            buffer.len() as u8, // Data length
46            pin_cs,             // Chip select pin
47            0,                  // Unused parameter
48            buffer,             // Data payload
49        )?;
50
51        // Check response status (byte 3 should be 1 for success)
52        if response.len() > 3 && response[3] == 1 {
53            Ok(())
54        } else {
55            Err(PoKeysError::Protocol("SPI write failed".to_string()))
56        }
57    }
58
59    /// Read data from SPI bus
60    ///
61    /// # Arguments
62    /// * `length` - Number of bytes to read (maximum 55 bytes)
63    ///
64    /// # Returns
65    /// Vector containing the read data
66    ///
67    /// This matches the C library function: PK_SPIRead(device, buffer, length)
68    pub fn spi_read(&mut self, length: u8) -> Result<Vec<u8>> {
69        if length == 0 {
70            return Err(PoKeysError::Parameter(
71                "SPI read length cannot be zero".to_string(),
72            ));
73        }
74
75        if length > 55 {
76            return Err(PoKeysError::Parameter(
77                "SPI read length too long (maximum 55 bytes)".to_string(),
78            ));
79        }
80
81        // Command 0xE5, sub-command 0x20 for SPI read
82        let response = self.send_request(0xE5, 0x20, length, 0, 0)?;
83
84        // Check response status (byte 3 should be 1 for success)
85        if response.len() > 3 && response[3] == 1 {
86            let mut data = Vec::new();
87            // Data starts at byte 8 in the response
88            if response.len() >= 8 + length as usize {
89                data.extend_from_slice(&response[8..8 + length as usize]);
90            }
91            Ok(data)
92        } else {
93            Err(PoKeysError::Protocol("SPI read failed".to_string()))
94        }
95    }
96
97    /// SPI transfer (write and read simultaneously) - convenience method
98    ///
99    /// This is a higher-level method that combines write and read operations
100    /// for full-duplex SPI communication.
101    pub fn spi_transfer(&mut self, write_data: &[u8], pin_cs: u8) -> Result<Vec<u8>> {
102        // For full-duplex operation, we would need a different protocol command
103        // For now, implement as separate write and read operations
104        self.spi_write(write_data, pin_cs)?;
105        self.spi_read(write_data.len() as u8)
106    }
107}