ftdi_embedded_hal/
spi.rs

1use crate::error::Error;
2use crate::{FtInner, PinUse};
3use ftdi_mpsse::{ClockData, ClockDataOut, MpsseCmdBuilder, MpsseCmdExecutor};
4use std::sync::{Arc, Mutex, MutexGuard};
5
6/// FTDI SPI polarity.
7///
8/// This is a helper type to support multiple embedded-hal versions simultaneously.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct Polarity {
11    /// MPSSE command used to clock data in and out simultaneously.
12    ///
13    /// This is set by [`Spi::set_clock_polarity`].
14    clk: ClockData,
15    /// MPSSE command used to clock data out.
16    ///
17    /// This is set by [`Spi::set_clock_polarity`].
18    clk_out: ClockDataOut,
19}
20
21impl From<eh0::spi::Polarity> for Polarity {
22    fn from(cpol: eh0::spi::Polarity) -> Self {
23        match cpol {
24            eh0::spi::Polarity::IdleLow => Polarity {
25                clk: ClockData::MsbPosIn,
26                clk_out: ClockDataOut::MsbNeg,
27            },
28            eh0::spi::Polarity::IdleHigh => Polarity {
29                clk: ClockData::MsbNegIn,
30                clk_out: ClockDataOut::MsbPos,
31            },
32        }
33    }
34}
35
36impl From<eh1::spi::Polarity> for Polarity {
37    fn from(cpol: eh1::spi::Polarity) -> Self {
38        match cpol {
39            eh1::spi::Polarity::IdleLow => Polarity {
40                clk: ClockData::MsbPosIn,
41                clk_out: ClockDataOut::MsbNeg,
42            },
43            eh1::spi::Polarity::IdleHigh => Polarity {
44                clk: ClockData::MsbNegIn,
45                clk_out: ClockDataOut::MsbPos,
46            },
47        }
48    }
49}
50
51impl Default for Polarity {
52    fn default() -> Self {
53        Self {
54            clk: ClockData::MsbPosIn,
55            clk_out: ClockDataOut::MsbNeg,
56        }
57    }
58}
59
60/// FTDI SPI bus.
61///
62/// In embedded-hal version 1 this represents an exclusive SPI bus.
63///
64/// This is created by calling [`FtHal::spi`].
65///
66/// [`FtHal::spi`]: crate::FtHal::spi
67#[derive(Debug)]
68pub struct Spi<Device: MpsseCmdExecutor> {
69    /// Parent FTDI device.
70    mtx: Arc<Mutex<FtInner<Device>>>,
71    /// SPI polarity
72    pol: Polarity,
73}
74
75impl<Device, E> Spi<Device>
76where
77    Device: MpsseCmdExecutor<Error = E>,
78    E: std::error::Error,
79    Error<E>: From<E>,
80{
81    pub(crate) fn new(mtx: Arc<Mutex<FtInner<Device>>>) -> Result<Spi<Device>, Error<E>> {
82        {
83            let mut lock = mtx.lock().expect("Failed to aquire FTDI mutex");
84            lock.allocate_pin(0, PinUse::Spi);
85            lock.allocate_pin(1, PinUse::Spi);
86            lock.allocate_pin(2, PinUse::Spi);
87
88            // clear direction of first 3 pins
89            lock.direction &= !0x07;
90            // set SCK (AD0) and MOSI (AD1) as output pins
91            lock.direction |= 0x03;
92
93            // set GPIO pins to new state
94            let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
95                .set_gpio_lower(lock.value, lock.direction)
96                .send_immediate();
97            lock.ft.send(cmd.as_slice())?;
98        }
99
100        Ok(Spi {
101            mtx,
102            pol: Default::default(),
103        })
104    }
105
106    /// Set the SPI clock polarity.
107    ///
108    /// FTD2XX devices only supports [SPI mode] 0 and 2, clock phase is fixed.
109    ///
110    /// # Example
111    ///
112    /// ```no_run
113    /// use eh1::spi::Polarity;
114    /// use ftdi_embedded_hal as hal;
115    ///
116    /// # #[cfg(feature = "libftd2xx")]
117    /// # {
118    /// let device = libftd2xx::Ft2232h::with_description("Dual RS232-HS A")?;
119    /// let hal = hal::FtHal::init_freq(device, 3_000_000)?;
120    /// let mut spi = hal.spi()?;
121    /// spi.set_clock_polarity(Polarity::IdleLow);
122    /// # }
123    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
124    /// ```
125    ///
126    /// [SPI mode]: https://en.wikipedia.org/wiki/Serial_Peripheral_Interface#Mode_numbers
127    pub fn set_clock_polarity<P: Into<Polarity>>(&mut self, cpol: P) {
128        self.pol = cpol.into()
129    }
130}
131
132impl<Device, E> eh0::blocking::spi::Write<u8> for Spi<Device>
133where
134    Device: MpsseCmdExecutor<Error = E>,
135    E: std::error::Error,
136    Error<E>: From<E>,
137{
138    type Error = Error<E>;
139
140    fn write(&mut self, words: &[u8]) -> Result<(), Error<E>> {
141        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
142            .clock_data_out(self.pol.clk_out, words)
143            .send_immediate();
144
145        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
146        lock.ft.send(cmd.as_slice())?;
147
148        Ok(())
149    }
150}
151
152impl<Device, E> eh0::blocking::spi::Transfer<u8> for Spi<Device>
153where
154    Device: MpsseCmdExecutor<Error = E>,
155    E: std::error::Error,
156    Error<E>: From<E>,
157{
158    type Error = Error<E>;
159
160    fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Error<E>> {
161        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
162            .clock_data(self.pol.clk, words)
163            .send_immediate();
164
165        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
166        lock.ft.send(cmd.as_slice())?;
167        lock.ft.recv(words)?;
168
169        Ok(words)
170    }
171}
172
173impl<Device, E> eh0::spi::FullDuplex<u8> for Spi<Device>
174where
175    Device: MpsseCmdExecutor<Error = E>,
176    E: std::error::Error,
177    Error<E>: From<E>,
178{
179    type Error = Error<E>;
180
181    fn read(&mut self) -> nb::Result<u8, Error<E>> {
182        let mut buf: [u8; 1] = [0];
183        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
184            .clock_data(self.pol.clk, &buf)
185            .send_immediate();
186
187        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
188        match lock.ft.xfer(cmd.as_slice(), &mut buf) {
189            Ok(()) => Ok(buf[0]),
190            Err(e) => Err(nb::Error::Other(Error::from(e))),
191        }
192    }
193
194    fn send(&mut self, byte: u8) -> nb::Result<(), Error<E>> {
195        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
196            .clock_data_out(self.pol.clk_out, &[byte])
197            .send_immediate();
198
199        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
200        match lock.ft.send(cmd.as_slice()) {
201            Ok(()) => Ok(()),
202            Err(e) => Err(nb::Error::Other(Error::from(e))),
203        }
204    }
205}
206
207impl<E> eh1::spi::Error for Error<E>
208where
209    E: std::error::Error,
210    Error<E>: From<E>,
211{
212    fn kind(&self) -> eh1::spi::ErrorKind {
213        eh1::spi::ErrorKind::Other
214    }
215}
216
217impl<Device, E> eh1::spi::ErrorType for Spi<Device>
218where
219    Device: MpsseCmdExecutor<Error = E>,
220    E: std::error::Error,
221    Error<E>: From<E>,
222{
223    type Error = Error<E>;
224}
225
226impl<Device, E> eh1::spi::SpiBus<u8> for Spi<Device>
227where
228    Device: MpsseCmdExecutor<Error = E>,
229    E: std::error::Error,
230    Error<E>: From<E>,
231{
232    fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
233        let data_out: Vec<u8> = vec![0; words.len()];
234        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
235            .clock_data(self.pol.clk, &data_out)
236            .send_immediate();
237
238        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
239        lock.ft.send(cmd.as_slice())?;
240        lock.ft.recv(words)?;
241
242        Ok(())
243    }
244
245    fn write(&mut self, words: &[u8]) -> Result<(), Error<E>> {
246        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
247            .clock_data_out(self.pol.clk_out, words)
248            .send_immediate();
249
250        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
251        lock.ft.send(cmd.as_slice())?;
252
253        Ok(())
254    }
255
256    fn flush(&mut self) -> Result<(), Self::Error> {
257        Ok(())
258    }
259
260    fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error<E>> {
261        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
262            .clock_data(self.pol.clk, words)
263            .send_immediate();
264
265        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
266
267        lock.ft.send(cmd.as_slice())?;
268        lock.ft.recv(words)?;
269
270        Ok(())
271    }
272
273    fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error<E>> {
274        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
275            .clock_data(self.pol.clk, write)
276            .send_immediate();
277
278        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
279        lock.ft.send(cmd.as_slice())?;
280        lock.ft.recv(read)?;
281
282        let remain: usize = write.len().saturating_sub(read.len());
283        if remain != 0 {
284            let mut remain_buf: Vec<u8> = vec![0; remain];
285            lock.ft.recv(&mut remain_buf)?;
286        }
287
288        Ok(())
289    }
290}
291
292impl<Device, E> ehnb1::spi::FullDuplex<u8> for Spi<Device>
293where
294    Device: MpsseCmdExecutor<Error = E>,
295    E: std::error::Error,
296    Error<E>: From<E>,
297{
298    fn read(&mut self) -> nb::Result<u8, Error<E>> {
299        let mut buf: [u8; 1] = [0];
300        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
301            .clock_data(self.pol.clk, &buf)
302            .send_immediate();
303
304        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
305        match lock.ft.xfer(cmd.as_slice(), &mut buf) {
306            Ok(()) => Ok(buf[0]),
307            Err(e) => Err(nb::Error::Other(Error::from(e))),
308        }
309    }
310
311    fn write(&mut self, byte: u8) -> nb::Result<(), Error<E>> {
312        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
313            .clock_data_out(self.pol.clk_out, &[byte])
314            .send_immediate();
315
316        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
317        match lock.ft.send(cmd.as_slice()) {
318            Ok(()) => Ok(()),
319            Err(e) => Err(nb::Error::Other(Error::from(e))),
320        }
321    }
322}
323
324pub struct SpiDeviceBus<'a, Device: MpsseCmdExecutor> {
325    lock: MutexGuard<'a, FtInner<Device>>,
326    pol: Polarity,
327}
328
329impl<Device, E> eh1::spi::ErrorType for SpiDeviceBus<'_, Device>
330where
331    Device: MpsseCmdExecutor<Error = E>,
332    E: std::error::Error,
333    Error<E>: From<E>,
334{
335    type Error = Error<E>;
336}
337
338impl<Device, E> eh1::spi::SpiBus<u8> for SpiDeviceBus<'_, Device>
339where
340    Device: MpsseCmdExecutor<Error = E>,
341    E: std::error::Error,
342    Error<E>: From<E>,
343{
344    fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
345        self.lock.ft.xfer(
346            MpsseCmdBuilder::new()
347                .clock_data(self.pol.clk, words)
348                .send_immediate()
349                .as_slice(),
350            words,
351        )?;
352        Ok(())
353    }
354
355    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
356        self.lock.ft.send(
357            MpsseCmdBuilder::new()
358                .clock_data_out(self.pol.clk_out, words)
359                .send_immediate()
360                .as_slice(),
361        )?;
362        Ok(())
363    }
364
365    fn flush(&mut self) -> Result<(), Self::Error> {
366        Ok(())
367    }
368
369    fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
370        self.lock.ft.xfer(
371            MpsseCmdBuilder::new()
372                .clock_data(self.pol.clk, write)
373                .send_immediate()
374                .as_slice(),
375            read,
376        )?;
377        Ok(())
378    }
379
380    fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
381        self.lock.ft.xfer(
382            MpsseCmdBuilder::new()
383                .clock_data(self.pol.clk, words)
384                .send_immediate()
385                .as_slice(),
386            words,
387        )?;
388        Ok(())
389    }
390}
391
392/// FTDI SPI device, a SPI bus with chip select pin.
393///
394/// This is created by calling [`FtHal::spi_device`].
395///
396/// This is specific to embedded-hal version 1.
397///
398/// [`FtHal::spi_device`]: crate::FtHal::spi_device
399#[derive(Debug)]
400pub struct SpiDevice<Device: MpsseCmdExecutor> {
401    /// Parent FTDI device.
402    mtx: Arc<Mutex<FtInner<Device>>>,
403    /// SPI polarity
404    pol: Polarity,
405    /// Chip select pin index.  0-7 for the FT232H.
406    cs_idx: u8,
407}
408
409impl<Device, E> SpiDevice<Device>
410where
411    Device: MpsseCmdExecutor<Error = E>,
412    E: std::error::Error,
413    Error<E>: From<E>,
414{
415    pub(crate) fn new(
416        mtx: Arc<Mutex<FtInner<Device>>>,
417        cs_idx: u8,
418    ) -> Result<SpiDevice<Device>, Error<E>> {
419        {
420            let mut lock = mtx.lock().expect("Failed to aquire FTDI mutex");
421            lock.allocate_pin(0, PinUse::Spi);
422            lock.allocate_pin(1, PinUse::Spi);
423            lock.allocate_pin(2, PinUse::Spi);
424            lock.allocate_pin(cs_idx, PinUse::Output);
425
426            let cs_mask: u8 = 1 << cs_idx;
427
428            // clear direction of first 3 pins and CS
429            lock.direction &= !(0x07 | cs_mask);
430            // set SCK (AD0) and MOSI (AD1), and CS as output pins
431            lock.direction |= 0x03 | cs_mask;
432
433            // set GPIO pins to new state
434            let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
435                .set_gpio_lower(lock.value, lock.direction)
436                .send_immediate();
437            lock.ft.send(cmd.as_slice())?;
438        }
439
440        Ok(Self {
441            mtx,
442            pol: Default::default(),
443            cs_idx,
444        })
445    }
446
447    pub(crate) fn cs_mask(&self) -> u8 {
448        1 << self.cs_idx
449    }
450
451    /// Set the SPI clock polarity.
452    ///
453    /// FTD2XX devices only supports [SPI mode] 0 and 2, clock phase is fixed.
454    ///
455    /// # Example
456    ///
457    /// ```no_run
458    /// use eh1::spi::Polarity;
459    /// use ftdi_embedded_hal as hal;
460    ///
461    /// # #[cfg(feature = "libftd2xx")]
462    /// # {
463    /// let device = libftd2xx::Ft2232h::with_description("Dual RS232-HS A")?;
464    /// let hal = hal::FtHal::init_freq(device, 3_000_000)?;
465    /// let mut spi = hal.spi_device(3)?;
466    /// spi.set_clock_polarity(Polarity::IdleLow);
467    /// # }
468    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
469    /// ```
470    ///
471    /// [SPI mode]: https://en.wikipedia.org/wiki/Serial_Peripheral_Interface#Mode_numbers
472    pub fn set_clock_polarity<P: Into<Polarity>>(&mut self, cpol: P) {
473        self.pol = cpol.into()
474    }
475}
476
477impl<Device, E> eh1::spi::ErrorType for SpiDevice<Device>
478where
479    Device: MpsseCmdExecutor<Error = E>,
480    E: std::error::Error,
481    Error<E>: From<E>,
482{
483    type Error = Error<E>;
484}
485
486impl<Device, E> eh1::spi::SpiDevice for SpiDevice<Device>
487where
488    Device: MpsseCmdExecutor<Error = E>,
489    E: std::error::Error,
490    Error<E>: From<E>,
491{
492    fn transaction(
493        &mut self,
494        operations: &mut [eh1::spi::Operation<'_, u8>],
495    ) -> Result<(), Self::Error> {
496        // lock the bus
497        let mut lock: MutexGuard<FtInner<Device>> =
498            self.mtx.lock().expect("Failed to aquire FTDI mutex");
499        let direction: u8 = lock.direction;
500
501        // assert the chip select pin
502        let mut value_cs_asserted: u8 = lock.value & !self.cs_mask();
503
504        // drive pin 0, the clock pin according to the CPOL setting
505        // Reference: https://github.com/ftdi-rs/ftdi-embedded-hal/pull/69
506        match self.pol.clk {
507            ClockData::MsbNegIn | ClockData::LsbNegIn => {
508                value_cs_asserted |= 1;
509            }
510            ClockData::MsbPosIn | ClockData::LsbPosIn => {
511                value_cs_asserted &= !1;
512            }
513        }
514
515        lock.ft.send(
516            MpsseCmdBuilder::new()
517                .set_gpio_lower(value_cs_asserted, direction)
518                .send_immediate()
519                .as_slice(),
520        )?;
521
522        let mut bus: SpiDeviceBus<Device> = SpiDeviceBus {
523            lock,
524            pol: self.pol,
525        };
526
527        for op in operations {
528            match op {
529                eh1::spi::Operation::Read(buffer) => {
530                    eh1::spi::SpiBus::read(&mut bus, buffer)?;
531                }
532                eh1::spi::Operation::Write(buffer) => {
533                    eh1::spi::SpiBus::write(&mut bus, buffer)?;
534                }
535                eh1::spi::Operation::Transfer(read, write) => {
536                    eh1::spi::SpiBus::transfer(&mut bus, read, write)?;
537                }
538                eh1::spi::Operation::TransferInPlace(buffer) => {
539                    eh1::spi::SpiBus::transfer_in_place(&mut bus, buffer)?;
540                }
541                eh1::spi::Operation::DelayNs(micros) => {
542                    std::thread::sleep(std::time::Duration::from_nanos((*micros).into()));
543                }
544            }
545        }
546
547        // flush the bus
548        {
549            use eh1::spi::SpiBus;
550            bus.flush()?;
551        }
552
553        let mut lock: MutexGuard<FtInner<Device>> = bus.lock;
554
555        // deassert the chip select pin
556        let mut value_cs_deasserted: u8 = lock.value | self.cs_mask();
557
558        // drive pin 0, the clock pin according to the CPOL setting
559        // Reference: https://github.com/ftdi-rs/ftdi-embedded-hal/pull/69
560        match self.pol.clk {
561            ClockData::MsbNegIn | ClockData::LsbNegIn => {
562                value_cs_deasserted |= 1;
563            }
564            ClockData::MsbPosIn | ClockData::LsbPosIn => {
565                value_cs_deasserted &= !1;
566            }
567        }
568
569        lock.ft.send(
570            MpsseCmdBuilder::new()
571                .set_gpio_lower(value_cs_deasserted, direction)
572                .send_immediate()
573                .as_slice(),
574        )?;
575
576        // unlocking the bus is implicit via Drop
577        Ok(())
578    }
579}