rppal/spi/
segment.rs

1use std::fmt;
2use std::marker;
3
4/// Part of a multi-segment transfer.
5///
6/// `Segment`s are transferred using the [`Spi::transfer_segments`] method.
7///
8/// Construct a new `Segment` for a simultaneous (full-duplex) read/write
9/// transfer using [`new`]. For read operations without any outgoing data,
10/// use [`with_read`]. For write operations where any incoming data
11/// should be discarded, use [`with_write`].
12///
13/// [`Spi::transfer_segments`]: struct.Spi.html#method.transfer_segments
14/// [`with_read`]: #method.with_read
15/// [`with_write`]: #method.with_write
16/// [`new`]: #method.new
17#[derive(PartialEq, Eq, Copy, Clone)]
18#[repr(C)]
19pub struct Segment<'a, 'b> {
20    // Pointer to write buffer, or 0.
21    tx_buf: u64,
22    // Pointer to read buffer, or 0.
23    rx_buf: u64,
24    // Number of bytes to transfer in this segment.
25    len: u32,
26    // Set a different clock speed for this segment. Default = 0.
27    speed_hz: u32,
28    // Add a delay before the (optional) SS change and the next segment.
29    delay_usecs: u16,
30    // Bits per word for this segment. The Pi only supports 8 bits (or 9 bits in LoSSI mode). Default = 0.
31    bits_per_word: u8,
32    // Set to 1 to briefly set SS inactive between this segment and the next. If this is the last segment, keep SS active.
33    cs_change: u8,
34    // Number of outgoing lines used for dual/quad SPI. Not supported on the Raspberry Pi. Default = 0.
35    tx_nbits: u8,
36    // Number of incoming lines used for dual/quad SPI. Not supported on the Raspberry Pi. Default = 0.
37    rx_nbits: u8,
38    // Padding. Set to 0 for forward compatibility.
39    pad: u16,
40    // Zero-sized variable used to link this struct to the read buffer lifetime.
41    read_buffer_lifetime: marker::PhantomData<&'a mut [u8]>,
42    // Zero-sized variable used to link this struct to the write buffer lifetime.
43    write_buffer_lifetime: marker::PhantomData<&'b [u8]>,
44}
45
46impl<'a, 'b> Segment<'a, 'b> {
47    /// Constructs a new `Segment` with the default settings, and configures it
48    /// for a simultaneous (full-duplex) read/write transfer.
49    ///
50    /// For `Segment`s that only require either a read or write operation, call
51    /// [`with_read`] or [`with_write`] instead of `new`.
52    ///
53    /// [`Spi::transfer_segments`] will only transfer as many bytes as the shortest of
54    /// the two buffers contains.
55    ///
56    /// By default, all customizable settings are set to 0, which means it uses
57    /// the same values as set for [`Spi`].
58    ///
59    /// [`Spi::transfer_segments`]: struct.Spi.html#method.transfer_segments
60    /// [`Spi`]: struct.Spi.html
61    /// [`with_read`]: #method.with_read
62    /// [`with_write`]: #method.with_write
63    pub fn new(read_buffer: &'a mut [u8], write_buffer: &'b [u8]) -> Segment<'a, 'b> {
64        Segment::with_settings(Some(read_buffer), Some(write_buffer), 0, 0, 0, false)
65    }
66
67    /// Constructs a new `Segment` with the default settings, and configures it
68    /// for a read operation.
69    ///
70    /// Incoming data from the slave device is written to `buffer`. The total
71    /// number of bytes read depends on the length of `buffer`. A zero-value
72    /// byte is sent for every byte read.
73    ///
74    /// By default, all customizable settings are set to 0, which means it uses
75    /// the same values as set for [`Spi`].
76    ///
77    /// [`Spi`]: struct.Spi.html
78    pub fn with_read(buffer: &mut [u8]) -> Segment<'_, '_> {
79        Segment::with_settings(Some(buffer), None, 0, 0, 0, false)
80    }
81
82    /// Constructs a new `Segment` with the default settings, and configures it
83    /// for a write operation.
84    ///
85    /// Outgoing data from `buffer` is sent to the slave device. Any
86    /// incoming data is discarded.
87    ///
88    /// By default, all customizable settings are set to 0, which means it uses
89    /// the same values as set for [`Spi`].
90    ///
91    /// [`Spi`]: struct.Spi.html
92    pub fn with_write(buffer: &[u8]) -> Segment<'_, '_> {
93        Segment::with_settings(None, Some(buffer), 0, 0, 0, false)
94    }
95
96    /// Constructs a new `Segment` with the specified settings.
97    ///
98    /// These settings override the values set for [`Spi`], and are only used
99    /// for this specific segment.
100    ///
101    /// If `read_buffer` is set to `None`, any incoming data is discarded.
102    ///
103    /// If `write_buffer` is set to `None`, a zero-value byte is sent for every
104    /// byte read.
105    ///
106    /// If both `read_buffer` and `write_buffer` are specified, [`Spi::transfer_segments`]
107    /// will only transfer as many bytes as the shortest of the two buffers contains.
108    ///
109    /// `clock_speed` sets a custom clock speed in hertz (Hz).
110    ///
111    /// `delay` sets a delay in microseconds (µs).
112    ///
113    /// `bits_per_word` sets the number of bits per word. The Raspberry Pi currently only supports 8 bits per word.
114    ///
115    /// `ss_change` changes how Slave Select behaves in between two segments (toggle SS), or after the final segment (keep SS active).
116    ///
117    /// [`Spi::transfer_segments`]: struct.Spi.html#method.transfer_segments
118    /// [`Spi`]: struct.Spi.html
119    pub fn with_settings(
120        read_buffer: Option<&'a mut [u8]>,
121        write_buffer: Option<&'b [u8]>,
122        clock_speed: u32,
123        delay: u16,
124        bits_per_word: u8,
125        ss_change: bool,
126    ) -> Segment<'a, 'b> {
127        // Len will contain the length of the shortest of the supplied buffers
128        let mut len: u32 = 0;
129
130        let tx_buf = if let Some(buffer) = write_buffer {
131            len = buffer.len() as u32;
132            buffer.as_ptr() as u64
133        } else {
134            0
135        };
136
137        let rx_buf = if let Some(buffer) = read_buffer {
138            if (len > buffer.len() as u32) || tx_buf == 0 {
139                len = buffer.len() as u32;
140            }
141            buffer.as_ptr() as u64
142        } else {
143            0
144        };
145
146        Segment {
147            tx_buf,
148            rx_buf,
149            len,
150            speed_hz: clock_speed,
151            delay_usecs: delay,
152            bits_per_word,
153            cs_change: ss_change as u8,
154            tx_nbits: 0,
155            rx_nbits: 0,
156            pad: 0,
157            read_buffer_lifetime: marker::PhantomData,
158            write_buffer_lifetime: marker::PhantomData,
159        }
160    }
161
162    /// Returns the number of bytes that will be transferred.
163    ///
164    /// If both a read buffer and write buffer are supplied,
165    /// [`Spi::transfer_segments`] only transfers as many bytes as the
166    /// shortest of the two buffers contains.
167    ///
168    /// [`Spi::transfer_segments`]: struct.Spi.html#method.transfer_segments
169    pub fn len(&self) -> usize {
170        self.len as usize
171    }
172
173    /// Returns `true` if this segment won't transfer any bytes.
174    pub fn is_empty(&self) -> bool {
175        self.len == 0
176    }
177
178    /// Gets the custom clock speed in hertz (Hz) for this segment.
179    pub fn clock_speed(&self) -> u32 {
180        self.speed_hz
181    }
182
183    /// Sets a custom clock speed in hertz (Hz) for this segment.
184    ///
185    /// The SPI driver will automatically select the closest valid frequency.
186    ///
187    /// By default, `clock_speed` is set to `0`, which means
188    /// it will use the same value as configured for [`Spi`].
189    ///
190    /// [`Spi`]: struct.Spi.html
191    pub fn set_clock_speed(&mut self, clock_speed: u32) {
192        self.speed_hz = clock_speed;
193    }
194
195    /// Gets the delay in microseconds (µs) for this segment.
196    pub fn delay(&self) -> u16 {
197        self.delay_usecs
198    }
199
200    /// Sets a delay in microseconds (µs) for this segment.
201    ///
202    /// `set_delay` adds a delay at the end of this segment,
203    /// before the (optional) Slave Select change.
204    ///
205    /// By default, `delay` is set to `0`.
206    pub fn set_delay(&mut self, delay: u16) {
207        self.delay_usecs = delay;
208    }
209
210    /// Gets the number of bits per word for this segment.
211    pub fn bits_per_word(&self) -> u8 {
212        self.bits_per_word
213    }
214
215    /// Sets the number of bits per word for this segment.
216    ///
217    /// The Raspberry Pi currently only supports 8 bit words.
218    ///
219    /// By default, `bits_per_word` is set to `0`, which means
220    /// it will use the same value as configured for [`Spi`].
221    ///
222    /// [`Spi`]: struct.Spi.html
223    pub fn set_bits_per_word(&mut self, bits_per_word: u8) {
224        self.bits_per_word = bits_per_word;
225    }
226
227    /// Gets the state of Slave Select change for this segment.
228    pub fn ss_change(&self) -> bool {
229        self.cs_change == 1
230    }
231
232    /// Changes Slave Select's behavior for this segment.
233    ///
234    /// If `ss_change` is set to `true`, and this is not the last
235    /// segment of the transfer, the Slave Select line will briefly
236    /// change to inactive between this segment and the next.
237    /// If this is the last segment, setting `ss_change` to true will
238    /// keep Slave Select active after the transfer ends.
239    ///
240    /// By default, `ss_change` is set to `false`.
241    pub fn set_ss_change(&mut self, ss_change: bool) {
242        self.cs_change = ss_change as u8;
243    }
244}
245
246impl fmt::Debug for Segment<'_, '_> {
247    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248        f.debug_struct("Segment")
249            .field("tx_buf", &self.tx_buf)
250            .field("rx_buf", &self.rx_buf)
251            .field("len", &self.len)
252            .field("speed_hz", &self.speed_hz)
253            .field("delay_usecs", &self.delay_usecs)
254            .field("bits_per_word", &self.bits_per_word)
255            .field("cs_change", &self.cs_change)
256            .field("tx_nbits", &self.tx_nbits)
257            .field("rx_nbits", &self.rx_nbits)
258            .field("pad", &self.pad)
259            .finish()
260    }
261}