rpi_embedded/spi.rs
1// Copyright (c) 2017-2019 Rene van der Meer
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the "Software"),
5// to deal in the Software without restriction, including without limitation
6// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7// and/or sell copies of the Software, and to permit persons to whom the
8// Software is furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19// DEALINGS IN THE SOFTWARE.
20
21//! Interface for the main and auxiliary SPI peripherals.
22//!
23//! RPPAL provides access to the available SPI buses by using the `spidev` device
24//! interface through `/dev/spidevB.S`, where B refers to an SPI bus, and S to
25//! a Slave Select pin. Which buses and pins are available depends on your
26//! Raspberry Pi model and configuration, as explained below.
27//!
28//! ## SPI buses
29//!
30//! The Raspberry Pi's GPIO header exposes several SPI buses. SPI0 is available
31//! on all Raspberry Pi models. SPI1 is available on models with a 40-pin
32//! header. SPI2 is only available on the Compute and Compute 3. SPI3 through SPI6
33//! are only available on the Raspberry Pi 4 B.
34//!
35//! ### SPI0
36//!
37//! SPI0 is disabled by default. You can enable it by running
38//! `sudo raspi-config`, or by manually adding `dtparam=spi=on` to
39//! `/boot/config.txt`. The associated pins are listed below.
40//!
41//! * MISO: BCM GPIO 9 (physical pin 21)
42//! * MOSI: BCM GPIO 10 (physical pin 19)
43//! * SCLK: BCM GPIO 11 (physical pin 23)
44//! * SS: [`Ss0`] BCM GPIO 8 (physical pin 24), [`Ss1`] BCM GPIO 7 (physical pin 26)
45//!
46//! ### SPI1
47//!
48//! SPI1 is an auxiliary peripheral that's referred to as mini SPI. According
49//! to the BCM2835 documentation, using higher clock speeds on SPI1 requires
50//! additional CPU time compared to SPI0, caused by smaller FIFOs and no DMA
51//! support. It doesn't support [`Mode1`] or [`Mode3`]. SPI1 can be enabled by
52//! adding `dtoverlay=spi1-1cs` to `/boot/config.txt`. Replace `1cs` with
53//! either `2cs` or `3cs` if you require 2 or 3 Slave Select pins.
54//! The associated pins are listed below.
55//!
56//! * MISO: BCM GPIO 19 (physical pin 35)
57//! * MOSI: BCM GPIO 20 (physical pin 38)
58//! * SCLK: BCM GPIO 21 (physical pin 40)
59//! * SS: [`Ss0`] BCM GPIO 18 (physical pin 12), [`Ss1`] BCM GPIO 17 (physical pin 11), [`Ss2`] BCM GPIO 16 (physical pin 36)
60//!
61//! ### SPI2
62//!
63//! SPI2 shares the same characteristics and limitations as SPI1. It can be
64//! enabled by adding `dtoverlay=spi2-1cs` to `/boot/config.txt`. Replace
65//! `1cs` with either `2cs` or `3cs` if you require 2 or 3 Slave Select
66//! pins. The associated pins are listed below.
67//!
68//! * MISO: BCM GPIO 40
69//! * MOSI: BCM GPIO 41
70//! * SCLK: BCM GPIO 42
71//! * SS: [`Ss0`] BCM GPIO 43, [`Ss1`] BCM GPIO 44, [`Ss2`] BCM GPIO 45
72//!
73//! ### SPI3
74//!
75//! SPI3 can be enabled by adding `dtoverlay=spi3-1cs` to `/boot/config.txt`. Replace
76//! `1cs` with `2cs` if you require 2 Slave Select pins. The associated pins are listed below.
77//!
78//! * MISO: BCM GPIO 1 (physical pin 28)
79//! * MOSI: BCM GPIO 2 (physical pin 3)
80//! * SCLK: BCM GPIO 3 (physical pin 5)
81//! * SS: [`Ss0`] BCM GPIO 0 (physical pin 27), [`Ss1`] BCM GPIO 24 (physical pin 18)
82//!
83//! ### SPI4
84//!
85//! SPI4 can be enabled by adding `dtoverlay=spi4-1cs` to `/boot/config.txt`. Replace
86//! `1cs` with `2cs` if you require 2 Slave Select pins. The associated pins are listed below.
87//!
88//! * MISO: BCM GPIO 5 (physical pin 29)
89//! * MOSI: BCM GPIO 6 (physical pin 31)
90//! * SCLK: BCM GPIO 7 (physical pin 26)
91//! * SS: [`Ss0`] BCM GPIO 4 (physical pin 7), [`Ss1`] BCM GPIO 25 (physical pin 22)
92//!
93//! ### SPI5
94//!
95//! SPI5 can be enabled by adding `dtoverlay=spi5-1cs` to `/boot/config.txt`. Replace
96//! `1cs` with `2cs` if you require 2 Slave Select pins. The associated pins are listed below.
97//!
98//! * MISO: BCM GPIO 13 (physical pin 33)
99//! * MOSI: BCM GPIO 14 (physical pin 8)
100//! * SCLK: BCM GPIO 15 (physical pin 10)
101//! * SS: [`Ss0`] BCM GPIO 12 (physical pin 32), [`Ss1`] BCM GPIO 26 (physical pin 37)
102//!
103//! ### SPI6
104//!
105//! SPI6 can be enabled by adding `dtoverlay=spi6-1cs` to `/boot/config.txt`. Replace
106//! `1cs` with `2cs` if you require 2 Slave Select pins. The associated pins are listed below.
107//!
108//! * MISO: BCM GPIO 19 (physical pin 35)
109//! * MOSI: BCM GPIO 20 (physical pin 38)
110//! * SCLK: BCM GPIO 21 (physical pin 40)
111//! * SS: [`Ss0`] BCM GPIO 18 (physical pin 12), [`Ss1`] BCM GPIO 27 (physical pin 13)
112//!
113//! SPI6 is tied to the same GPIO pins as SPI1. It's not possible to enable both
114//! buses at the same time.
115//!
116//! ### Alternative pins
117//!
118//! The GPIO pin numbers mentioned above are part of the default configuration.
119//! Some of their functionality can be moved to different pins. Read
120//! `/boot/overlays/README` for more information.
121//!
122//! ## Buffer size limits
123//!
124//! By default, `spidev` can handle up to 4096 bytes in a single transfer. You
125//! can increase this limit to a maximum of 65536 bytes by appending
126//! `spidev.bufsiz=65536` to the single line of parameters in `/boot/cmdline.txt`.
127//! Remember to reboot the Raspberry Pi afterwards. The current value of bufsiz
128//! can be checked with `cat /sys/module/spidev/parameters/bufsiz`.
129//!
130//! ## Not supported
131//!
132//! Some features exposed by the generic `spidev` interface aren't fully
133//! supported by the underlying driver or the BCM283x SoC: `SPI_LSB_FIRST` (LSB
134//! first bit order), `SPI_3WIRE` (bidirectional mode), `SPI_LOOP` (loopback mode),
135//! `SPI_NO_CS` (no Slave Select), `SPI_READY` (slave ready signal),
136//! `SPI_TX_DUAL`/`SPI_RX_DUAL` (dual SPI), `SPI_TX_QUAD`/`SPI_RX_QUAD` (quad SPI),
137//! and any number of bits per word other than 8.
138//!
139//! If your slave device requires `SPI_LSB_FIRST`, you can use the
140//! [`reverse_bits`] function instead to reverse the bit order in software.
141//!
142//! `SPI_LOOP` mode can be achieved by connecting the MOSI and MISO pins
143//! together.
144//!
145//! `SPI_NO_CS` can be implemented by connecting the Slave Select pin on your
146//! slave device to any other available GPIO pin on the Pi, and manually
147//! changing it to high and low as needed.
148//!
149//! [`Ss0`]: enum.SlaveSelect.html
150//! [`Ss1`]: enum.SlaveSelect.html
151//! [`Ss2`]: enum.SlaveSelect.html
152//! [`Mode1`]: enum.Mode.html
153//! [`Mode3`]: enum.Mode.html
154//! [`reverse_bits`]: fn.reverse_bits.html
155
156use std::error;
157use std::fmt;
158use std::fs::{File, OpenOptions};
159use std::io;
160use std::io::{Read, Write};
161use std::marker::PhantomData;
162use std::os::unix::io::AsRawFd;
163use std::result;
164
165#[cfg(feature = "hal")]
166mod hal;
167mod ioctl;
168mod segment;
169
170pub use self::segment::Segment;
171
172/// Errors that can occur when accessing the SPI peripheral.
173#[derive(Debug)]
174pub enum Error {
175 /// I/O error.
176 Io(io::Error),
177 /// The specified number of bits per word is not supported.
178 ///
179 /// The Raspberry Pi currently only supports 8 bit words. Any other value
180 /// will trigger this error.
181 BitsPerWordNotSupported(u8),
182 /// The specified bit order is not supported.
183 ///
184 /// The Raspberry Pi currently only supports the [`MsbFirst`] bit order. If you
185 /// need the [`LsbFirst`] bit order, you can use the [`reverse_bits`] function
186 /// instead to reverse the bit order in software by converting your write
187 /// buffer before sending it to the slave device, and your read buffer after
188 /// reading any incoming data.
189 ///
190 /// [`MsbFirst`]: enum.BitOrder.html
191 /// [`LsbFirst`]: enum.BitOrder.html
192 /// [`reverse_bits`]: fn.reverse_bits.html
193 BitOrderNotSupported(BitOrder),
194 /// The specified clock speed is not supported.
195 ClockSpeedNotSupported(u32),
196 /// The specified mode is not supported.
197 ModeNotSupported(Mode),
198 /// The specified Slave Select polarity is not supported.
199 PolarityNotSupported(Polarity),
200}
201
202impl fmt::Display for Error {
203 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204 match *self {
205 Error::Io(ref err) => write!(f, "I/O error: {}", err),
206 Error::BitsPerWordNotSupported(bits_per_word) => {
207 write!(f, "Bits per word value not supported: {}", bits_per_word)
208 }
209 Error::BitOrderNotSupported(bit_order) => {
210 write!(f, "Bit order value not supported: {:?}", bit_order)
211 }
212 Error::ClockSpeedNotSupported(clock_speed) => {
213 write!(f, "Clock speed value not supported: {}", clock_speed)
214 }
215 Error::ModeNotSupported(mode) => write!(f, "Mode value not supported: {:?}", mode),
216 Error::PolarityNotSupported(polarity) => {
217 write!(f, "Polarity value not supported: {:?}", polarity)
218 }
219 }
220 }
221}
222
223impl error::Error for Error {}
224
225impl From<io::Error> for Error {
226 fn from(err: io::Error) -> Error {
227 Error::Io(err)
228 }
229}
230
231/// Result type returned from methods that can have `spi::Error`s.
232pub type Result<T> = result::Result<T, Error>;
233
234const LOOKUP_REVERSE_BITS: [u8; 256] = [
235 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
236 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
237 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
238 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
239 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
240 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
241 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
242 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
243 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
244 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
245 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
246 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
247 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
248 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
249 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
250 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF,
251];
252
253/// Reverses the bits of each byte in `buffer`.
254///
255/// Use this function to switch the bit order between most-significant bit first
256/// and least-significant bit first.
257#[inline(always)]
258pub fn reverse_bits(buffer: &mut [u8]) {
259 for byte in buffer {
260 *byte = LOOKUP_REVERSE_BITS[*byte as usize];
261 }
262}
263
264/// SPI buses.
265///
266/// The Raspberry Pi exposes up to five SPI buses, depending on the model and
267/// your `/boot/config.txt` configuration. More information can be found [here].
268///
269/// [here]: index.html
270#[derive(Debug, PartialEq, Eq, Copy, Clone)]
271pub enum Bus {
272 Spi0 = 0,
273 Spi1 = 1,
274 Spi2 = 2,
275 Spi3 = 3,
276 Spi4 = 4,
277 Spi5 = 5,
278 Spi6 = 6,
279}
280
281impl fmt::Display for Bus {
282 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283 match *self {
284 Bus::Spi0 => write!(f, "Spi0"),
285 Bus::Spi1 => write!(f, "Spi1"),
286 Bus::Spi2 => write!(f, "Spi2"),
287 Bus::Spi3 => write!(f, "Spi3"),
288 Bus::Spi4 => write!(f, "Spi4"),
289 Bus::Spi5 => write!(f, "Spi5"),
290 Bus::Spi6 => write!(f, "Spi6"),
291 }
292 }
293}
294
295/// Slave Select pins.
296///
297/// Slave Select is used to signal which slave device should pay attention to
298/// the SPI bus. Slave Select (SS) is the more commonly used name, but
299/// it's also known as Chip Select (CS) or Chip Enable (CE). Throughout the Raspberry
300/// Pi's documentation, config files and BCM2835 datasheet, multiple different names
301/// are used. Any pins referred to as CE0, CE1, and CE2 or CS0, CS1, and CS2 are equivalent
302/// to `Ss0`, `Ss1`, and `Ss2`.
303///
304/// The number of available Slave Select pins for the selected SPI bus depends
305/// on your `/boot/config.txt` configuration. More information can be found
306/// [here].
307///
308/// [here]: index.html
309#[derive(Debug, PartialEq, Eq, Copy, Clone)]
310pub enum SlaveSelect {
311 Ss0 = 0,
312 Ss1 = 1,
313 Ss2 = 2,
314}
315
316impl fmt::Display for SlaveSelect {
317 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
318 match *self {
319 SlaveSelect::Ss0 => write!(f, "Ss0"),
320 SlaveSelect::Ss1 => write!(f, "Ss1"),
321 SlaveSelect::Ss2 => write!(f, "Ss2"),
322 }
323 }
324}
325
326/// Slave Select polarities.
327#[derive(Debug, PartialEq, Eq, Copy, Clone)]
328pub enum Polarity {
329 ActiveLow = 0,
330 ActiveHigh = 1,
331}
332
333impl fmt::Display for Polarity {
334 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
335 match *self {
336 Polarity::ActiveLow => write!(f, "ActiveLow"),
337 Polarity::ActiveHigh => write!(f, "ActiveHigh"),
338 }
339 }
340}
341
342/// SPI modes indicating the clock polarity and phase.
343///
344/// Select the appropriate SPI mode for your device. Each mode configures the
345/// clock polarity (CPOL) and clock phase (CPHA) as shown below:
346///
347/// * Mode0: CPOL 0, CPHA 0
348/// * Mode1: CPOL 0, CPHA 1
349/// * Mode2: CPOL 1, CPHA 0
350/// * Mode3: CPOL 1, CPHA 1
351///
352/// The [`Spi0`] bus supports all 4 modes. [`Spi1`] and [`Spi2`] only support
353/// `Mode0` and `Mode2`.
354///
355/// More information on clock polarity and phase can be found on [Wikipedia].
356///
357/// [`Spi0`]: enum.Bus.html
358/// [`Spi1`]: enum.Bus.html
359/// [`Spi2`]: enum.Bus.html
360/// [Wikipedia]: https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Clock_polarity_and_phase
361#[derive(Debug, PartialEq, Eq, Copy, Clone)]
362pub enum Mode {
363 Mode0 = 0,
364 Mode1 = 1,
365 Mode2 = 2,
366 Mode3 = 3,
367}
368
369impl fmt::Display for Mode {
370 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
371 match *self {
372 Mode::Mode0 => write!(f, "Mode0"),
373 Mode::Mode1 => write!(f, "Mode1"),
374 Mode::Mode2 => write!(f, "Mode2"),
375 Mode::Mode3 => write!(f, "Mode3"),
376 }
377 }
378}
379
380/// Bit orders.
381///
382/// The bit order determines in what order data is shifted out and shifted in.
383/// Select the bit order that's appropriate for the device you're
384/// communicating with.
385///
386/// `MsbFirst` will transfer the most-significant bit first. `LsbFirst` will
387/// transfer the least-significant bit first.
388///
389/// The Raspberry Pi currently only supports the `MsbFirst` bit order. If you
390/// need the `LsbFirst` bit order, you can use the [`reverse_bits`] function
391/// instead to reverse the bit order in software by converting your write
392/// buffer before sending it to the slave device, and your read buffer after
393/// reading any incoming data.
394///
395/// [`reverse_bits`]: fn.reverse_bits.html
396#[derive(Debug, PartialEq, Eq, Copy, Clone)]
397pub enum BitOrder {
398 MsbFirst = 0,
399 LsbFirst = 1,
400}
401
402impl fmt::Display for BitOrder {
403 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
404 match *self {
405 BitOrder::MsbFirst => write!(f, "MsbFirst"),
406 BitOrder::LsbFirst => write!(f, "LsbFirst"),
407 }
408 }
409}
410
411/// Provides access to the Raspberry Pi's SPI peripherals.
412///
413/// Before using `Spi`, make sure your Raspberry Pi has the necessary SPI buses
414/// and Slave Select pins enabled. More information can be found [here].
415///
416/// The `embedded-hal` [`blocking::spi::Transfer<u8>`], [`blocking::spi::Write<u8>`]
417/// and [`spi::FullDuplex<u8>`] trait
418/// implementations for `Spi` can be enabled by specifying the optional `hal`
419/// feature in the dependency declaration for the `rppal` crate.
420///
421/// [here]: index.html
422/// [`blocking::spi::Transfer<u8>`]: ../../embedded_hal/blocking/spi/trait.Transfer.html
423/// [`blocking::spi::Write<u8>`]: ../../embedded_hal/blocking/spi/trait.Write.html
424/// [`spi::FullDuplex<u8>`]: ../../embedded_hal/spi/trait.FullDuplex.html
425pub struct Spi {
426 spidev: File,
427 // Stores the last read value. Used for embedded_hal::spi::FullDuplex.
428 #[cfg(feature = "hal")]
429 last_read: u8,
430 // The not_sync field is a workaround to force !Sync. Spi isn't safe for
431 // Sync because of ioctl() and the underlying drivers. This avoids needing
432 // #![feature(optin_builtin_traits)] to manually add impl !Sync for Spi.
433 not_sync: PhantomData<*const ()>,
434}
435
436impl Spi {
437 /// Constructs a new `Spi`.
438 ///
439 /// `bus` and `slave_select` specify the selected SPI bus and one of its
440 /// associated Slave Select pins.
441 ///
442 /// `clock_speed` defines the maximum clock frequency in hertz (Hz). The SPI driver
443 /// will automatically round down to the closest valid frequency.
444 ///
445 /// `mode` selects the clock polarity and phase.
446 pub fn new(bus: Bus, slave_select: SlaveSelect, clock_speed: u32, mode: Mode) -> Result<Spi> {
447 // The following options currently aren't supported by spidev in Raspbian Stretch on the Pi:
448 //
449 // LSB_FIRST - ioctl() returns EINVAL when set
450 // 3WIRE - neither MOSI nor MISO show any outgoing data in half-duplex mode
451 // LOOP - ioctl() returns EINVAL when set
452 // NO_CS - SS is still set to active (tried both file write() and ioctl())
453 // READY - ioctl() returns EINVAL when set
454 // TX_DUAL/TX_QUAD/RX_DUAL/RX_QUAD - Not supported by BCM283x
455 // bits per word - any value other than 0 or 8 returns EINVAL when set
456
457 let spidev = OpenOptions::new()
458 .read(true)
459 .write(true)
460 .open(format!("/dev/spidev{}.{}", bus as u8, slave_select as u8))?;
461
462 // Reset all mode flags
463 if let Err(e) = ioctl::set_mode32(spidev.as_raw_fd(), mode as u32) {
464 if e.kind() == io::ErrorKind::InvalidInput {
465 return Err(Error::ModeNotSupported(mode));
466 } else {
467 return Err(Error::Io(e));
468 }
469 }
470
471 let spi = Spi {
472 spidev,
473 #[cfg(feature = "hal")]
474 last_read: 0,
475 not_sync: PhantomData,
476 };
477
478 // Set defaults and user-specified settings
479 spi.set_bits_per_word(8)?;
480 spi.set_clock_speed(clock_speed)?;
481
482 Ok(spi)
483 }
484
485 /// Gets the bit order.
486 pub fn bit_order(&self) -> Result<BitOrder> {
487 let mut bit_order: u8 = 0;
488 ioctl::lsb_first(self.spidev.as_raw_fd(), &mut bit_order)?;
489
490 Ok(match bit_order {
491 0 => BitOrder::MsbFirst,
492 _ => BitOrder::LsbFirst,
493 })
494 }
495
496 /// Sets the order in which bits are shifted out and in.
497 ///
498 /// The Raspberry Pi currently only supports the [`MsbFirst`] bit order. If you
499 /// need the [`LsbFirst`] bit order, you can use the [`reverse_bits`] function
500 /// instead to reverse the bit order in software by converting your write
501 /// buffer before sending it to the slave device, and your read buffer after
502 /// reading any incoming data.
503 ///
504 /// By default, `bit_order` is set to `MsbFirst`.
505 ///
506 /// [`MsbFirst`]: enum.BitOrder.html
507 /// [`LsbFirst`]: enum.BitOrder.html
508 /// [`reverse_bits`]: fn.reverse_bits.html
509 pub fn set_bit_order(&self, bit_order: BitOrder) -> Result<()> {
510 match ioctl::set_lsb_first(self.spidev.as_raw_fd(), bit_order as u8) {
511 Ok(_) => Ok(()),
512 Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {
513 Err(Error::BitOrderNotSupported(bit_order))
514 }
515 Err(e) => Err(Error::Io(e)),
516 }
517 }
518
519 /// Gets the number of bits per word.
520 pub fn bits_per_word(&self) -> Result<u8> {
521 let mut bits_per_word: u8 = 0;
522 ioctl::bits_per_word(self.spidev.as_raw_fd(), &mut bits_per_word)?;
523
524 Ok(bits_per_word)
525 }
526
527 /// Sets the number of bits per word.
528 ///
529 /// The Raspberry Pi currently only supports 8 bit words.
530 ///
531 /// By default, `bits_per_word` is set to 8.
532 pub fn set_bits_per_word(&self, bits_per_word: u8) -> Result<()> {
533 match ioctl::set_bits_per_word(self.spidev.as_raw_fd(), bits_per_word) {
534 Ok(_) => Ok(()),
535 Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {
536 Err(Error::BitsPerWordNotSupported(bits_per_word))
537 }
538 Err(e) => Err(Error::Io(e)),
539 }
540 }
541
542 /// Gets the clock frequency in hertz (Hz).
543 pub fn clock_speed(&self) -> Result<u32> {
544 let mut clock_speed: u32 = 0;
545 ioctl::clock_speed(self.spidev.as_raw_fd(), &mut clock_speed)?;
546
547 Ok(clock_speed)
548 }
549
550 /// Sets the clock frequency in hertz (Hz).
551 ///
552 /// The SPI driver will automatically round down to the closest valid frequency.
553 pub fn set_clock_speed(&self, clock_speed: u32) -> Result<()> {
554 match ioctl::set_clock_speed(self.spidev.as_raw_fd(), clock_speed) {
555 Ok(_) => Ok(()),
556 Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {
557 Err(Error::ClockSpeedNotSupported(clock_speed))
558 }
559 Err(e) => Err(Error::Io(e)),
560 }
561 }
562
563 /// Gets the SPI mode.
564 pub fn mode(&self) -> Result<Mode> {
565 let mut mode: u8 = 0;
566 ioctl::mode(self.spidev.as_raw_fd(), &mut mode)?;
567
568 Ok(match mode & 0x03 {
569 0x01 => Mode::Mode1,
570 0x02 => Mode::Mode2,
571 0x03 => Mode::Mode3,
572 _ => Mode::Mode0,
573 })
574 }
575
576 /// Sets the SPI mode.
577 ///
578 /// The SPI mode indicates the serial clock polarity and phase. Some modes
579 /// may not be available depending on the SPI bus that's used.
580 pub fn set_mode(&self, mode: Mode) -> Result<()> {
581 let mut new_mode: u8 = 0;
582 ioctl::mode(self.spidev.as_raw_fd(), &mut new_mode)?;
583
584 // Make sure we only replace the CPOL/CPHA bits
585 new_mode = (new_mode & !0x03) | (mode as u8);
586
587 match ioctl::set_mode(self.spidev.as_raw_fd(), new_mode) {
588 Ok(_) => Ok(()),
589 Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {
590 Err(Error::ModeNotSupported(mode))
591 }
592 Err(e) => Err(Error::Io(e)),
593 }
594 }
595
596 /// Gets the Slave Select polarity.
597 pub fn ss_polarity(&self) -> Result<Polarity> {
598 let mut mode: u8 = 0;
599 ioctl::mode(self.spidev.as_raw_fd(), &mut mode)?;
600
601 if (mode & ioctl::MODE_CS_HIGH) == 0 {
602 Ok(Polarity::ActiveLow)
603 } else {
604 Ok(Polarity::ActiveHigh)
605 }
606 }
607
608 /// Sets Slave Select polarity.
609 ///
610 /// By default, the Slave Select polarity is set to `ActiveLow`.
611 pub fn set_ss_polarity(&self, polarity: Polarity) -> Result<()> {
612 let mut new_mode: u8 = 0;
613 ioctl::mode(self.spidev.as_raw_fd(), &mut new_mode)?;
614
615 if polarity == Polarity::ActiveHigh {
616 new_mode |= ioctl::MODE_CS_HIGH;
617 } else {
618 new_mode &= !ioctl::MODE_CS_HIGH;
619 }
620
621 match ioctl::set_mode(self.spidev.as_raw_fd(), new_mode) {
622 Ok(_) => Ok(()),
623 Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {
624 Err(Error::PolarityNotSupported(polarity))
625 }
626 Err(e) => Err(Error::Io(e)),
627 }
628 }
629
630 /// Receives incoming data from the slave device and writes it to `buffer`.
631 ///
632 /// The SPI protocol doesn't indicate how much incoming data is waiting,
633 /// so the total number of bytes read depends on the length of `buffer`.
634 ///
635 /// During the read, the MOSI line is kept in a state that results in a
636 /// zero value byte shifted out for every byte `read` receives on the MISO
637 /// line.
638 ///
639 /// Slave Select is set to active at the start of the read, and inactive
640 /// when the read completes.
641 ///
642 /// Returns how many bytes were read.
643 pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
644 Ok(self.spidev.read(buffer)?)
645 }
646
647 /// Sends the outgoing data contained in `buffer` to the slave device.
648 ///
649 /// Any data received on the MISO line from the slave is ignored.
650 ///
651 /// Slave Select is set to active at the start of the write, and inactive
652 /// when the write completes.
653 ///
654 /// Returns how many bytes were written.
655 pub fn write(&mut self, buffer: &[u8]) -> Result<usize> {
656 Ok(self.spidev.write(buffer)?)
657 }
658
659 /// Sends and receives data at the same time.
660 ///
661 /// SPI is a full-duplex protocol that shifts out bits to the slave device
662 /// on the MOSI line while simultaneously shifting in bits it receives on
663 /// the MISO line. `transfer` stores the incoming data in `read_buffer`,
664 /// and sends the outgoing data contained in `write_buffer`.
665 ///
666 /// Because data is sent and received simultaneously, `transfer` will only
667 /// transfer as many bytes as the shortest of the two buffers contains.
668 ///
669 /// Slave Select is set to active at the start of the transfer, and inactive
670 /// when the transfer completes.
671 ///
672 /// Returns how many bytes were transferred.
673 pub fn transfer(&self, read_buffer: &mut [u8], write_buffer: &[u8]) -> Result<usize> {
674 let segment = Segment::new(read_buffer, write_buffer);
675
676 ioctl::transfer(self.spidev.as_raw_fd(), &[segment])?;
677
678 Ok(segment.len())
679 }
680
681 /// Transfers multiple half-duplex or full-duplex segments.
682 ///
683 /// `transfer_segments` transfers multiple segments in a single call. Each
684 /// [`Segment`] contains a reference to either a read buffer or a write buffer,
685 /// or both. Optional settings can be configured that override the SPI bus
686 /// settings for that specific segment.
687 ///
688 /// By default, Slave Select stays active until all segments have been
689 /// transferred. You can change this behavior using [`Segment::set_ss_change`].
690 ///
691 /// [`Segment`]: struct.Segment.html
692 /// [`Segment::set_ss_change`]: struct.Segment.html#method.set_ss_change
693 pub fn transfer_segments(&self, segments: &[Segment<'_, '_>]) -> Result<()> {
694 ioctl::transfer(self.spidev.as_raw_fd(), segments)?;
695
696 Ok(())
697 }
698}
699
700// Send is safe for Spi, but we're marked !Send because of the dummy pointer that's
701// needed to force !Sync.
702unsafe impl Send for Spi {}
703
704impl fmt::Debug for Spi {
705 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
706 f.debug_struct("Spi").field("spidev", &self.spidev).finish()
707 }
708}