ws2818_rgb_led_spi_driver/
adapter_spi.rs

1//! Adapter for SPI-dev on Linux-systems. This requires std.
2
3use crate::adapter_gen::{HardwareDev, WS28xxAdapter, WS28xxGenAdapter};
4use crate::encoding::encode_rgb_slice;
5use crate::timings::PI_SPI_HZ;
6use alloc::boxed::Box;
7use alloc::fmt::format;
8use alloc::string::{String, ToString};
9use spidev::{SpiModeFlags, Spidev, SpidevOptions};
10use std::io;
11use std::io::Write;
12
13/// Wrapper around Spidev.
14struct SpiHwAdapterDev(Spidev);
15
16// Implement Hardwareabstraction for device.
17impl HardwareDev for SpiHwAdapterDev {
18    fn write_all(&mut self, encoded_data: &[u8]) -> Result<(), String> {
19        self.0.write_all(&encoded_data)
20            .map_err(|_| {
21                format!(
22                    "Failed to send {} bytes via SPI. Perhaps your SPI buffer is too small!\
23                     Check https://www.raspberrypi.org/forums/viewtopic.php?p=309582#p309582 for example.",
24                    encoded_data.len()
25                )
26            })
27    }
28}
29
30impl SpiHwAdapterDev {
31    /// Connects your application with the SPI-device of your device.
32    /// This uses the `spidev`-crate. Returns a new adapter object
33    /// for the WS28xx LEDs.
34    ///
35    /// * `dev` - Device name. Probably "/dev/spidev0.0" if available.
36    ///
37    /// Fails if connection to SPI can't be established.
38    pub fn new(dev: &str) -> io::Result<Self> {
39        let mut spi = Spidev::open(dev)?;
40        let options = SpidevOptions::new()
41            .bits_per_word(8)
42            // According to https://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md
43            .max_speed_hz(PI_SPI_HZ)
44            .mode(SpiModeFlags::SPI_MODE_0)
45            .build();
46        spi.configure(&options)?;
47        Ok(Self(spi))
48    }
49}
50
51/// Adapter that connects your application via SPI to your WS28xx LEDs.
52/// This requires an SPI device on your machine. This doesn't work
53/// with `#[no-std]`.
54pub struct WS28xxSpiAdapter {
55    gen: WS28xxGenAdapter,
56}
57
58impl WS28xxSpiAdapter {
59    /// Connects your application with the SPI-device of your device.
60    /// This uses the `spidev`-crate. Returns a new adapter object
61    /// for the WS28xx LEDs.
62    ///
63    /// * `dev` - Device name. Probably "/dev/spidev0.0" if available.
64    ///
65    /// Fails if connection to SPI can't be established.
66    pub fn new(dev: &str) -> Result<Self, String> {
67        let spi = SpiHwAdapterDev::new(dev).map_err(|err| err.to_string())?;
68        let spi = Box::from(spi);
69        let gen = WS28xxGenAdapter::new(spi);
70        Ok(Self { gen })
71    }
72}
73
74impl WS28xxAdapter for WS28xxSpiAdapter {
75    fn get_hw_dev(&mut self) -> &mut Box<dyn HardwareDev> {
76        // forward to generic adapter
77        // todo this is not the best code design because this requires
78        //  each sub adapter (like a sub class in OOP) to implement
79        //  this manually..
80        self.gen.get_hw_dev()
81    }
82}