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) -> Result<(), Error<E>> {
128        self.pol = cpol.into();
129        let mut lock = self.mtx.lock().unwrap();
130        match self.pol.clk {
131            ClockData::MsbNegIn | ClockData::LsbNegIn => {
132                lock.lower.value |= 1;
133            }
134            ClockData::MsbPosIn | ClockData::LsbPosIn => {
135                lock.lower.value &= !1;
136            }
137        }
138        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
139            .set_gpio_lower(lock.value, lock.direction)
140            .send_immediate();
141        lock.ft.send(cmd.as_slice())?;
142        Ok(())
143    }
144}
145
146impl<Device, E> eh0::blocking::spi::Write<u8> for Spi<Device>
147where
148    Device: MpsseCmdExecutor<Error = E>,
149    E: std::error::Error,
150    Error<E>: From<E>,
151{
152    type Error = Error<E>;
153
154    fn write(&mut self, words: &[u8]) -> Result<(), Error<E>> {
155        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
156            .clock_data_out(self.pol.clk_out, words)
157            .send_immediate();
158
159        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
160        lock.ft.send(cmd.as_slice())?;
161
162        Ok(())
163    }
164}
165
166impl<Device, E> eh0::blocking::spi::Transfer<u8> for Spi<Device>
167where
168    Device: MpsseCmdExecutor<Error = E>,
169    E: std::error::Error,
170    Error<E>: From<E>,
171{
172    type Error = Error<E>;
173
174    fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Error<E>> {
175        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
176            .clock_data(self.pol.clk, words)
177            .send_immediate();
178
179        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
180        lock.ft.send(cmd.as_slice())?;
181        lock.ft.recv(words)?;
182
183        Ok(words)
184    }
185}
186
187impl<Device, E> eh0::spi::FullDuplex<u8> for Spi<Device>
188where
189    Device: MpsseCmdExecutor<Error = E>,
190    E: std::error::Error,
191    Error<E>: From<E>,
192{
193    type Error = Error<E>;
194
195    fn read(&mut self) -> nb::Result<u8, Error<E>> {
196        let mut buf: [u8; 1] = [0];
197        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
198            .clock_data(self.pol.clk, &buf)
199            .send_immediate();
200
201        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
202        match lock.ft.xfer(cmd.as_slice(), &mut buf) {
203            Ok(()) => Ok(buf[0]),
204            Err(e) => Err(nb::Error::Other(Error::from(e))),
205        }
206    }
207
208    fn send(&mut self, byte: u8) -> nb::Result<(), Error<E>> {
209        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
210            .clock_data_out(self.pol.clk_out, &[byte])
211            .send_immediate();
212
213        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
214        match lock.ft.send(cmd.as_slice()) {
215            Ok(()) => Ok(()),
216            Err(e) => Err(nb::Error::Other(Error::from(e))),
217        }
218    }
219}
220
221impl<E> eh1::spi::Error for Error<E>
222where
223    E: std::error::Error,
224    Error<E>: From<E>,
225{
226    fn kind(&self) -> eh1::spi::ErrorKind {
227        eh1::spi::ErrorKind::Other
228    }
229}
230
231impl<Device, E> eh1::spi::ErrorType for Spi<Device>
232where
233    Device: MpsseCmdExecutor<Error = E>,
234    E: std::error::Error,
235    Error<E>: From<E>,
236{
237    type Error = Error<E>;
238}
239
240impl<Device, E> eh1::spi::SpiBus<u8> for Spi<Device>
241where
242    Device: MpsseCmdExecutor<Error = E>,
243    E: std::error::Error,
244    Error<E>: From<E>,
245{
246    fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
247        let data_out: Vec<u8> = vec![0; words.len()];
248        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
249            .clock_data(self.pol.clk, &data_out)
250            .send_immediate();
251
252        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
253        lock.ft.send(cmd.as_slice())?;
254        lock.ft.recv(words)?;
255
256        Ok(())
257    }
258
259    fn write(&mut self, words: &[u8]) -> Result<(), Error<E>> {
260        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
261            .clock_data_out(self.pol.clk_out, words)
262            .send_immediate();
263
264        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
265        lock.ft.send(cmd.as_slice())?;
266
267        Ok(())
268    }
269
270    fn flush(&mut self) -> Result<(), Self::Error> {
271        Ok(())
272    }
273
274    fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error<E>> {
275        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
276            .clock_data(self.pol.clk, words)
277            .send_immediate();
278
279        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
280
281        lock.ft.send(cmd.as_slice())?;
282        lock.ft.recv(words)?;
283
284        Ok(())
285    }
286
287    fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error<E>> {
288        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
289            .clock_data(self.pol.clk, write)
290            .send_immediate();
291
292        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
293        lock.ft.send(cmd.as_slice())?;
294        lock.ft.recv(read)?;
295
296        let remain: usize = write.len().saturating_sub(read.len());
297        if remain != 0 {
298            let mut remain_buf: Vec<u8> = vec![0; remain];
299            lock.ft.recv(&mut remain_buf)?;
300        }
301
302        Ok(())
303    }
304}
305
306impl<Device, E> ehnb1::spi::FullDuplex<u8> for Spi<Device>
307where
308    Device: MpsseCmdExecutor<Error = E>,
309    E: std::error::Error,
310    Error<E>: From<E>,
311{
312    fn read(&mut self) -> nb::Result<u8, Error<E>> {
313        let mut buf: [u8; 1] = [0];
314        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
315            .clock_data(self.pol.clk, &buf)
316            .send_immediate();
317
318        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
319        match lock.ft.xfer(cmd.as_slice(), &mut buf) {
320            Ok(()) => Ok(buf[0]),
321            Err(e) => Err(nb::Error::Other(Error::from(e))),
322        }
323    }
324
325    fn write(&mut self, byte: u8) -> nb::Result<(), Error<E>> {
326        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
327            .clock_data_out(self.pol.clk_out, &[byte])
328            .send_immediate();
329
330        let mut lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
331        match lock.ft.send(cmd.as_slice()) {
332            Ok(()) => Ok(()),
333            Err(e) => Err(nb::Error::Other(Error::from(e))),
334        }
335    }
336}
337
338pub struct SpiDeviceBus<'a, Device: MpsseCmdExecutor> {
339    lock: MutexGuard<'a, FtInner<Device>>,
340    pol: Polarity,
341}
342
343impl<Device, E> eh1::spi::ErrorType for SpiDeviceBus<'_, Device>
344where
345    Device: MpsseCmdExecutor<Error = E>,
346    E: std::error::Error,
347    Error<E>: From<E>,
348{
349    type Error = Error<E>;
350}
351
352impl<Device, E> eh1::spi::SpiBus<u8> for SpiDeviceBus<'_, Device>
353where
354    Device: MpsseCmdExecutor<Error = E>,
355    E: std::error::Error,
356    Error<E>: From<E>,
357{
358    fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
359        self.lock.ft.xfer(
360            MpsseCmdBuilder::new()
361                .clock_data(self.pol.clk, words)
362                .send_immediate()
363                .as_slice(),
364            words,
365        )?;
366        Ok(())
367    }
368
369    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
370        self.lock.ft.send(
371            MpsseCmdBuilder::new()
372                .clock_data_out(self.pol.clk_out, words)
373                .send_immediate()
374                .as_slice(),
375        )?;
376        Ok(())
377    }
378
379    fn flush(&mut self) -> Result<(), Self::Error> {
380        Ok(())
381    }
382
383    fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
384        self.lock.ft.xfer(
385            MpsseCmdBuilder::new()
386                .clock_data(self.pol.clk, write)
387                .send_immediate()
388                .as_slice(),
389            read,
390        )?;
391        Ok(())
392    }
393
394    fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
395        self.lock.ft.xfer(
396            MpsseCmdBuilder::new()
397                .clock_data(self.pol.clk, words)
398                .send_immediate()
399                .as_slice(),
400            words,
401        )?;
402        Ok(())
403    }
404}
405
406/// FTDI SPI device, a SPI bus with chip select pin.
407///
408/// This is created by calling [`FtHal::spi_device`].
409///
410/// This is specific to embedded-hal version 1.
411///
412/// [`FtHal::spi_device`]: crate::FtHal::spi_device
413#[derive(Debug)]
414pub struct SpiDevice<Device: MpsseCmdExecutor> {
415    /// Parent FTDI device.
416    mtx: Arc<Mutex<FtInner<Device>>>,
417    /// SPI polarity
418    pol: Polarity,
419    /// Chip select pin index.  0-7 for the FT232H.
420    cs_idx: u8,
421}
422
423impl<Device, E> SpiDevice<Device>
424where
425    Device: MpsseCmdExecutor<Error = E>,
426    E: std::error::Error,
427    Error<E>: From<E>,
428{
429    pub(crate) fn new(
430        mtx: Arc<Mutex<FtInner<Device>>>,
431        cs_idx: u8,
432    ) -> Result<SpiDevice<Device>, Error<E>> {
433        {
434            let mut lock = mtx.lock().expect("Failed to aquire FTDI mutex");
435            lock.allocate_pin(0, PinUse::Spi);
436            lock.allocate_pin(1, PinUse::Spi);
437            lock.allocate_pin(2, PinUse::Spi);
438            lock.allocate_pin(cs_idx, PinUse::Output);
439
440            let cs_mask: u8 = 1 << cs_idx;
441
442            // clear direction of first 3 pins and CS
443            lock.direction &= !(0x07 | cs_mask);
444            // set SCK (AD0) and MOSI (AD1), and CS as output pins
445            lock.direction |= 0x03 | cs_mask;
446            // deassert CS
447            lock.value |= cs_mask;
448
449            // set GPIO pins to new state
450            let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
451                .set_gpio_lower(lock.value, lock.direction)
452                .send_immediate();
453            lock.ft.send(cmd.as_slice())?;
454        }
455
456        Ok(Self {
457            mtx,
458            pol: Default::default(),
459            cs_idx,
460        })
461    }
462
463    pub(crate) fn cs_mask(&self) -> u8 {
464        1 << self.cs_idx
465    }
466
467    /// Set the SPI clock polarity.
468    ///
469    /// FTD2XX devices only supports [SPI mode] 0 and 2, clock phase is fixed.
470    ///
471    /// # Example
472    ///
473    /// ```no_run
474    /// use eh1::spi::Polarity;
475    /// use ftdi_embedded_hal as hal;
476    ///
477    /// # #[cfg(feature = "libftd2xx")]
478    /// # {
479    /// let device = libftd2xx::Ft2232h::with_description("Dual RS232-HS A")?;
480    /// let hal = hal::FtHal::init_freq(device, 3_000_000)?;
481    /// let mut spi = hal.spi_device(3)?;
482    /// spi.set_clock_polarity(Polarity::IdleLow)?;
483    /// # }
484    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
485    /// ```
486    ///
487    /// [SPI mode]: https://en.wikipedia.org/wiki/Serial_Peripheral_Interface#Mode_numbers
488    pub fn set_clock_polarity<P: Into<Polarity>>(&mut self, cpol: P) -> Result<(), Error<E>> {
489        self.pol = cpol.into();
490        let mut lock = self.mtx.lock().unwrap();
491        match self.pol.clk {
492            ClockData::MsbNegIn | ClockData::LsbNegIn => {
493                lock.lower.value |= 1;
494            }
495            ClockData::MsbPosIn | ClockData::LsbPosIn => {
496                lock.lower.value &= !1;
497            }
498        }
499        let cmd: MpsseCmdBuilder = MpsseCmdBuilder::new()
500            .set_gpio_lower(lock.value, lock.direction)
501            .send_immediate();
502        lock.ft.send(cmd.as_slice())?;
503        Ok(())
504    }
505}
506
507impl<Device, E> eh1::spi::ErrorType for SpiDevice<Device>
508where
509    Device: MpsseCmdExecutor<Error = E>,
510    E: std::error::Error,
511    Error<E>: From<E>,
512{
513    type Error = Error<E>;
514}
515
516impl<Device, E> eh1::spi::SpiDevice for SpiDevice<Device>
517where
518    Device: MpsseCmdExecutor<Error = E>,
519    E: std::error::Error,
520    Error<E>: From<E>,
521{
522    fn transaction(
523        &mut self,
524        operations: &mut [eh1::spi::Operation<'_, u8>],
525    ) -> Result<(), Self::Error> {
526        // lock the bus
527        let mut lock: MutexGuard<FtInner<Device>> =
528            self.mtx.lock().expect("Failed to aquire FTDI mutex");
529        let direction: u8 = lock.direction;
530
531        // assert the chip select pin
532        let mut value_cs_asserted: u8 = lock.value & !self.cs_mask();
533
534        // drive pin 0, the clock pin according to the CPOL setting
535        // Reference: https://github.com/ftdi-rs/ftdi-embedded-hal/pull/69
536        match self.pol.clk {
537            ClockData::MsbNegIn | ClockData::LsbNegIn => {
538                value_cs_asserted |= 1;
539            }
540            ClockData::MsbPosIn | ClockData::LsbPosIn => {
541                value_cs_asserted &= !1;
542            }
543        }
544
545        lock.ft.send(
546            MpsseCmdBuilder::new()
547                .set_gpio_lower(value_cs_asserted, direction)
548                .send_immediate()
549                .as_slice(),
550        )?;
551
552        let mut bus: SpiDeviceBus<Device> = SpiDeviceBus {
553            lock,
554            pol: self.pol,
555        };
556
557        for op in operations {
558            match op {
559                eh1::spi::Operation::Read(buffer) => {
560                    eh1::spi::SpiBus::read(&mut bus, buffer)?;
561                }
562                eh1::spi::Operation::Write(buffer) => {
563                    eh1::spi::SpiBus::write(&mut bus, buffer)?;
564                }
565                eh1::spi::Operation::Transfer(read, write) => {
566                    eh1::spi::SpiBus::transfer(&mut bus, read, write)?;
567                }
568                eh1::spi::Operation::TransferInPlace(buffer) => {
569                    eh1::spi::SpiBus::transfer_in_place(&mut bus, buffer)?;
570                }
571                eh1::spi::Operation::DelayNs(micros) => {
572                    std::thread::sleep(std::time::Duration::from_nanos((*micros).into()));
573                }
574            }
575        }
576
577        // flush the bus
578        {
579            use eh1::spi::SpiBus;
580            bus.flush()?;
581        }
582
583        let mut lock: MutexGuard<FtInner<Device>> = bus.lock;
584
585        // deassert the chip select pin
586        let mut value_cs_deasserted: u8 = lock.value | self.cs_mask();
587
588        // drive pin 0, the clock pin according to the CPOL setting
589        // Reference: https://github.com/ftdi-rs/ftdi-embedded-hal/pull/69
590        match self.pol.clk {
591            ClockData::MsbNegIn | ClockData::LsbNegIn => {
592                value_cs_deasserted |= 1;
593            }
594            ClockData::MsbPosIn | ClockData::LsbPosIn => {
595                value_cs_deasserted &= !1;
596            }
597        }
598
599        lock.ft.send(
600            MpsseCmdBuilder::new()
601                .set_gpio_lower(value_cs_deasserted, direction)
602                .send_immediate()
603                .as_slice(),
604        )?;
605
606        // unlocking the bus is implicit via Drop
607        Ok(())
608    }
609}