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}