omnitiles/hw/
spi.rs

1// SPDX-License-Identifier: MIT
2// © 2025–2026 Christopher Liu
3
4//! Serial Peripheral Interface (SPI) abstraction layer.
5//!
6//! - `SpiBus` wraps a configured HAL SPI instance with 8-bit words.
7//! - `ChipSelect` is an active-low GPIO output wrappr for manual CS control.
8
9use stm32f7xx_hal::{
10    gpio::{self, Output, PinState, PushPull},
11    prelude::*,
12    spi::{self, Enabled, Spi},
13};
14
15/// Wrapper around an enabled HAL SPI instance (8-bit words).
16pub struct SpiBus<I, P> {
17    spi: Spi<I, P, Enabled<u8>>,
18}
19
20impl<I, P> SpiBus<I, P>
21where
22    I: spi::Instance,
23    P: spi::Pins<I>,
24{
25    pub fn new(spi: Spi<I, P, Enabled<u8>>) -> Self {
26        Self { spi }
27    }
28
29    /// Perform a blocking, full-duplex transfer of one byte.
30    pub fn transfer_byte(&mut self, byte: u8) -> Result<u8, spi::Error> {
31        let mut tmp = [byte];
32        self.spi.transfer(&mut tmp)?;
33        Ok(tmp[0])
34    }
35
36    /// Send a byte, ignoring the response.
37    #[inline]
38    pub fn write_byte(&mut self, byte: u8) -> Result<(), spi::Error> {
39        let _ = self.transfer_byte(byte)?;
40        Ok(())
41    }
42
43    /// Read a byte, sending 0x00.
44    #[inline]
45    pub fn read_byte(&mut self) -> Result<u8, spi::Error> {
46        self.transfer_byte(0x00)
47    }
48
49    /// Transfer a byte buffer in-place.
50    pub fn transfer_in_place(&mut self, buf: &mut [u8]) -> Result<(), spi::Error> {
51        for b in buf.iter_mut() {
52            *b = self.transfer_byte(*b)?;
53        }
54        Ok(())
55    }
56
57    pub fn free(self) -> Spi<I, P, Enabled<u8>> {
58        self.spi
59    }
60}
61
62/// Manual chip-select line, active-low, generic over any GPIO pin.
63pub struct ChipSelect<const P: char, const N: u8> {
64    pin: gpio::Pin<P, N, Output<PushPull>>,
65}
66
67impl<const P: char, const N: u8> ChipSelect<P, N> {
68    /// Create an active-low chip select and set to the inactive state (i.e., high).
69    pub fn active_low<MODE>(pin: gpio::Pin<P, N, MODE>) -> Self {
70        let mut pin = pin.into_push_pull_output();
71        pin.set_state(PinState::High);
72        Self { pin }
73    }
74
75    /// Assert the chip select.
76    #[inline]
77    pub fn select(&mut self) {
78        self.pin.set_low();
79    }
80
81    /// Deassert the chip select.
82    #[inline]
83    pub fn deselect(&mut self) {
84        self.pin.set_high();
85    }
86
87    pub fn free(self) -> gpio::Pin<P, N, Output<PushPull>> {
88        self.pin
89    }
90}