spidriver_hal/
hal.rs

1use embedded_hal::blocking::spi;
2use embedded_hal::digital::v2 as gpiov2;
3
4pub trait Comms {
5    type Error;
6
7    fn set_cs(&self, active: bool) -> Result<(), Self::Error>;
8    fn set_a(&self, active: bool) -> Result<(), Self::Error>;
9    fn set_b(&self, active: bool) -> Result<(), Self::Error>;
10    fn write(&self, data: &[u8]) -> Result<(), Self::Error>;
11    fn transfer<'w>(&self, data: &'w mut [u8]) -> Result<&'w [u8], Self::Error>;
12}
13
14/// `Parts` is a container for the various parts of a SPIDriver that can be
15/// used separately via distinct HAL traits.
16///
17/// The HAL objects inside a particular `Parts` all share a single underlying
18/// communications channel, so it is not possible to access them concurrently
19/// on multiple threads. Instead, coordinate all interactions with a single
20/// SPIDriver on a single thread.
21pub struct Parts<'a, SD: 'a>
22where
23    SD: Comms,
24{
25    /// `spi` is an implementation of the blocking SPI `Write` and `Transfer`
26    /// traits with an 8-bit word size.
27    pub spi: SPI<'a, SD>,
28
29    /// `cs` is an implementation of the digital I/O `OutputPin` trait that
30    /// controls the SPIDriver's Chip Select pin.
31    ///
32    /// Setting this pin to low is implemented as "select" on the SPIDriver and
33    /// setting it to high is implemented as "unselect", for consistency with
34    /// the way driver crates tend to expect a CS pin to behave.
35    pub cs: CS<'a, SD>,
36
37    /// `pin_a` is an implementation of the digital I/O `OutputPin` trait that
38    /// controls the SPIDriver's auxillary output pin "A".
39    pub pin_a: PinA<'a, SD>,
40
41    /// `pin_a` is an implementation of the digital I/O `OutputPin` trait that
42    /// controls the SPIDriver's auxillary output pin "B".
43    pub pin_b: PinB<'a, SD>,
44}
45
46impl<'a, SD: 'a> Parts<'a, SD>
47where
48    SD: Comms,
49{
50    pub(crate) fn new(sd: &'a SD) -> Self {
51        Self {
52            spi: SPI::new(&sd),
53            cs: CS::new(&sd),
54            pin_a: PinA::new(&sd),
55            pin_b: PinB::new(&sd),
56        }
57    }
58}
59
60/// `SPI` implements some of the SPI-related traits from `embedded-hal` in terms
61/// of an SPIDriver device.
62pub struct SPI<'a, SD: Comms>(&'a SD);
63
64impl<'a, SD: 'a> SPI<'a, SD>
65where
66    SD: Comms,
67{
68    fn new(sd: &'a SD) -> Self {
69        Self(sd)
70    }
71}
72
73impl<'a, SD: 'a, E> spi::Transfer<u8> for SPI<'a, SD>
74where
75    SD: Comms<Error = E>,
76{
77    type Error = E;
78
79    /// Implements blocking SPI `Transfer` by passing the given data to the
80    /// SPIDriver in chunks of up to 64 bytes each.
81    ///
82    /// Because of the chunking behavior, larger messages may have inconsistent
83    /// timing at the chunk boundaries, which may affect devices with particularly
84    /// sensitive clock timing constraints.
85    fn transfer<'w>(&mut self, data: &'w mut [u8]) -> Result<&'w [u8], E> {
86        self.0.transfer(data)
87    }
88}
89
90impl<'a, SD: 'a, E> spi::Write<u8> for SPI<'a, SD>
91where
92    SD: Comms<Error = E>,
93{
94    type Error = E;
95
96    /// Implements blocking SPI `Write` by passing the given data to the
97    /// SPIDriver in chunks of up to 64 bytes each.
98    ///
99    /// Because of the chunking behavior, larger messages may have inconsistent
100    /// timing at the chunk boundaries, which may affect devices with particularly
101    /// sensitive clock timing constraints.
102    fn write(&mut self, data: &[u8]) -> Result<(), E> {
103        self.0.write(data)
104    }
105}
106
107/// `CS` implements some of the digital IO traits from `embedded-hal` in
108/// terms of an SPIDriver device's Chip Select pin.
109pub struct CS<'a, SD: Comms>(&'a SD);
110
111impl<'a, SD: 'a> CS<'a, SD>
112where
113    SD: Comms,
114{
115    fn new(sd: &'a SD) -> Self {
116        Self(sd)
117    }
118}
119
120impl<'a, SD: 'a, E> gpiov2::OutputPin for CS<'a, SD>
121where
122    SD: Comms<Error = E>,
123{
124    type Error = E;
125
126    fn set_low(&mut self) -> Result<(), E> {
127        self.0.set_cs(false)
128    }
129
130    fn set_high(&mut self) -> Result<(), E> {
131        self.0.set_cs(true)
132    }
133}
134
135/// `PinA` implements some of the digital IO traits from `embedded-hal` in
136/// terms of an SPIDriver device's auxillary output pin A.
137pub struct PinA<'a, SD: Comms>(&'a SD);
138
139impl<'a, SD: 'a> PinA<'a, SD>
140where
141    SD: Comms,
142{
143    fn new(sd: &'a SD) -> Self {
144        Self(sd)
145    }
146}
147
148impl<'a, SD: 'a, E> gpiov2::OutputPin for PinA<'a, SD>
149where
150    SD: Comms<Error = E>,
151{
152    type Error = E;
153
154    fn set_low(&mut self) -> Result<(), E> {
155        self.0.set_a(false)
156    }
157
158    fn set_high(&mut self) -> Result<(), E> {
159        self.0.set_a(true)
160    }
161}
162
163/// `PinB` implements some of the digital IO traits from `embedded-hal` in
164/// terms of an SPIDriver device's auxillary output pin B.
165pub struct PinB<'a, SD: Comms>(&'a SD);
166
167impl<'a, SD: 'a> PinB<'a, SD>
168where
169    SD: Comms,
170{
171    fn new(sd: &'a SD) -> Self {
172        Self(sd)
173    }
174}
175
176impl<'a, SD: 'a, E> gpiov2::OutputPin for PinB<'a, SD>
177where
178    SD: Comms<Error = E>,
179{
180    type Error = E;
181
182    fn set_low(&mut self) -> Result<(), E> {
183        self.0.set_b(false)
184    }
185
186    fn set_high(&mut self) -> Result<(), E> {
187        self.0.set_b(true)
188    }
189}