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}