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}