ftdi_embedded_hal/
lib.rs

1//! This is an [embedded-hal] implementation for the FTDI chips
2//! that can use various drivers including [libftd2xx] and [ftdi-rs].
3//!
4//! This enables development of embedded device drivers without the use of
5//! a microcontroller. The FTDI devices interface with PC via USB, and
6//! provide a multi-protocol synchronous serial engine to interface
7//! with most GPIO, SPI, I2C embedded devices.
8//!
9//! **Note:**
10//! This is strictly a development tool.
11//! The crate contains runtime borrow checks and explicit panics to adapt the
12//! FTDI device into the [embedded-hal] traits.
13//!
14//! # Quickstart
15//!
16//! * Enable the "libftd2xx-static" feature flag to use static linking with libftd2xx driver.
17//! * Linux users only: Add [udev rules].
18//!
19//! ```toml
20//! [dependencies.ftdi-embedded-hal]
21//! version = "0.23.0"
22//! features = ["libftd2xx", "libftd2xx-static"]
23//! ```
24//!
25//! # Limitations
26//!
27//! * Limited trait support: SPI, I2C, Delay, InputPin, and OutputPin traits are implemented.
28//! * Limited device support: FT232H, FT2232H, FT4232H.
29//! * Limited SPI modes support: MODE0, MODE2.
30//!
31//! # Examples
32//!
33//! ## SPI
34//!
35//! Pin setup:
36//!
37//! * D0 - SCK
38//! * D1 - SDO (MOSI)
39//! * D2 - SDI (MISO)
40//! * D3..D7 - Available for CS
41//!
42//! Communicate with SPI devices using [ftdi-rs] driver:
43//! ```no_run
44//! use ftdi_embedded_hal as hal;
45//!
46//! # #[cfg(feature = "ftdi")]
47//! # {
48//! let device = ftdi::find_by_vid_pid(0x0403, 0x6010)
49//!     .interface(ftdi::Interface::A)
50//!     .open()?;
51//!
52//! let hal = hal::FtHal::init_freq(device, 3_000_000)?;
53//! let spi = hal.spi()?;
54//! # }
55//! # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
56//! ```
57//!
58//! Communicate with SPI devices using [libftd2xx] driver:
59//! ```no_run
60//! use ftdi_embedded_hal as hal;
61//!
62//! # #[cfg(feature = "libftd2xx")]
63//! # {
64//! let device = libftd2xx::Ft2232h::with_description("Dual RS232-HS A")?;
65//!
66//! let hal = hal::FtHal::init_freq(device, 3_000_000)?;
67//! let spi = hal.spi()?;
68//! # }
69//! # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
70//! ```
71//!
72//! ## I2C
73//!
74//! Communicate with I2C devices using [ftdi-rs] driver:
75//! ```no_run
76//! use ftdi_embedded_hal as hal;
77//!
78//! # #[cfg(feature = "ftdi")]
79//! # {
80//! let device = ftdi::find_by_vid_pid(0x0403, 0x6010)
81//!     .interface(ftdi::Interface::A)
82//!     .open()?;
83//!
84//! let hal = hal::FtHal::init_freq(device, 400_000)?;
85//! let i2c = hal.i2c()?;
86//! # }
87//! # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
88//! ```
89//!
90//! Communicate with I2C devices using [libftd2xx] driver:
91//! ```no_run
92//! use ftdi_embedded_hal as hal;
93//!
94//! # #[cfg(feature = "libftd2xx")]
95//! # {
96//! let device = libftd2xx::Ft232h::with_description("Single RS232-HS")?;
97//!
98//! let hal = hal::FtHal::init_freq(device, 400_000)?;
99//! let i2c = hal.i2c()?;
100//! # }
101//! # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
102//! ```
103//!
104//! ## GPIO
105//!
106//! Control GPIO pins using [libftd2xx] driver:
107//! ```no_run
108//! use ftdi_embedded_hal as hal;
109//!
110//! # #[cfg(feature = "libftd2xx")]
111//! # {
112//! let device = libftd2xx::Ft232h::with_description("Single RS232-HS")?;
113//!
114//! let hal = hal::FtHal::init_default(device)?;
115//! let gpio = hal.ad6();
116//! # }
117//! # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
118//! ```
119//!
120//! Control GPIO pins using [ftdi-rs] driver:
121//! ```no_run
122//! use ftdi_embedded_hal as hal;
123//!
124//! # #[cfg(feature = "ftdi")]
125//! # {
126//! let device = ftdi::find_by_vid_pid(0x0403, 0x6010)
127//!     .interface(ftdi::Interface::A)
128//!     .open()?;
129//!
130//! let hal = hal::FtHal::init_default(device)?;
131//! let gpio = hal.ad6();
132//! # }
133//! # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
134//! ```
135//!
136//! ## More examples
137//!
138//! * [newAM/eeprom25aa02e48-rs]: read data from Microchip 25AA02E48 SPI EEPROM
139//! * [newAM/bme280-rs]: read samples from Bosch BME280 sensor via I2C protocol
140//!
141//! [embedded-hal]: https://github.com/rust-embedded/embedded-hal
142//! [ftdi-rs]: https://github.com/tanriol/ftdi-rs
143//! [libftd2xx crate]: https://github.com/ftdi-rs/libftd2xx-rs/
144//! [libftd2xx]: https://github.com/ftdi-rs/libftd2xx-rs
145//! [newAM/eeprom25aa02e48-rs]: https://github.com/newAM/eeprom25aa02e48-rs/blob/main/examples/ftdi.rs
146//! [newAM/bme280-rs]: https://github.com/newAM/bme280-rs/blob/main/examples/ftdi-i2c.rs
147//! [udev rules]: https://github.com/ftdi-rs/libftd2xx-rs/#udev-rules
148//! [setup executable]: https://www.ftdichip.com/Drivers/CDM/CDM21228_Setup.zip
149#![forbid(missing_docs)]
150#![forbid(unsafe_code)]
151
152pub use eh0;
153pub use eh1;
154pub use ftdi_mpsse;
155
156#[cfg(feature = "ftdi")]
157pub use ftdi;
158
159#[cfg(feature = "libftd2xx")]
160pub use libftd2xx;
161
162mod delay;
163mod error;
164mod gpio;
165mod i2c;
166mod spi;
167
168pub use crate::error::{Error, ErrorKind};
169pub use delay::Delay;
170pub use gpio::{InputPin, OutputPin};
171pub use i2c::I2c;
172pub use spi::{Spi, SpiDevice};
173
174use gpio::Pin;
175
176use ftdi_mpsse::{MpsseCmdExecutor, MpsseSettings};
177use std::sync::{Arc, Mutex};
178
179/// State tracker for each pin on the FTDI chip.
180#[derive(Debug, Clone, Copy)]
181enum PinUse {
182    I2c,
183    Spi,
184    Output,
185    Input,
186}
187
188impl std::fmt::Display for PinUse {
189    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190        match self {
191            PinUse::I2c => write!(f, "I2C"),
192            PinUse::Spi => write!(f, "SPI"),
193            PinUse::Output => write!(f, "OUTPUT"),
194            PinUse::Input => write!(f, "INPUT"),
195        }
196    }
197}
198
199#[derive(Debug, Default)]
200struct GpioByte {
201    /// GPIO direction.
202    direction: u8,
203    /// GPIO value.
204    value: u8,
205    /// Pin allocation.
206    pins: [Option<PinUse>; 8],
207}
208
209#[derive(Debug)]
210struct FtInner<Device: MpsseCmdExecutor> {
211    /// FTDI device.
212    ft: Device,
213    lower: GpioByte,
214    upper: GpioByte,
215}
216
217// FtInner deref's into .lower because SPI and I2C code were not adjusted yet to handle the split;
218// once those are updated, the Deref implementation can go away again
219
220impl<Device: MpsseCmdExecutor> core::ops::Deref for FtInner<Device> {
221    type Target = GpioByte;
222    fn deref(&self) -> &GpioByte {
223        &self.lower
224    }
225}
226
227impl<Device: MpsseCmdExecutor> core::ops::DerefMut for FtInner<Device> {
228    fn deref_mut(&mut self) -> &mut GpioByte {
229        &mut self.lower
230    }
231}
232
233impl<Device: MpsseCmdExecutor> FtInner<Device> {
234    /// Allocate a pin in the lower byte for a specific use.
235    pub fn allocate_pin(&mut self, idx: u8, purpose: PinUse) {
236        assert!(idx < 8, "Pin index {idx} is out of range 0 - 7");
237
238        if let Some(current) = self.lower.pins[usize::from(idx)] {
239            panic!(
240                "Unable to allocate pin {idx} for {purpose}, pin is already allocated for {current}"
241            );
242        } else {
243            self.lower.pins[usize::from(idx)] = Some(purpose)
244        }
245    }
246
247    /// Allocate a pin for a specific use.
248    pub fn allocate_pin_any(&mut self, pin: Pin, purpose: PinUse) {
249        let (byte, idx) = match pin {
250            Pin::Lower(idx) => (&mut self.lower, idx),
251            Pin::Upper(idx) => (&mut self.upper, idx),
252        };
253        assert!(idx < 8, "Pin index {idx} is out of range 0 - 7");
254
255        if let Some(current) = byte.pins[usize::from(idx)] {
256            panic!(
257                "Unable to allocate pin {idx} for {purpose}, pin is already allocated for {current}"
258            );
259        } else {
260            byte.pins[usize::from(idx)] = Some(purpose)
261        }
262    }
263}
264
265impl<Device: MpsseCmdExecutor> From<Device> for FtInner<Device> {
266    fn from(ft: Device) -> Self {
267        FtInner {
268            ft,
269            lower: Default::default(),
270            upper: Default::default(),
271        }
272    }
273}
274
275/// FTxxx device.
276#[derive(Debug)]
277pub struct FtHal<Device: MpsseCmdExecutor> {
278    mtx: Arc<Mutex<FtInner<Device>>>,
279}
280
281impl<Device, E> FtHal<Device>
282where
283    Device: MpsseCmdExecutor<Error = E>,
284    E: std::error::Error,
285    Error<E>: From<E>,
286{
287    /// Initialize the FTDI MPSSE with sane defaults.
288    ///
289    /// Default values:
290    ///
291    /// * Reset the FTDI device.
292    /// * 4k USB transfer size.
293    /// * 1s USB read timeout.
294    /// * 1s USB write timeout.
295    /// * 16ms latency timer.
296    /// * 100kHz clock frequency.
297    ///
298    /// # Example
299    ///
300    /// ```no_run
301    /// use ftdi_embedded_hal as hal;
302    ///
303    /// # #[cfg(feature = "libftd2xx")]
304    /// # {
305    /// let device = libftd2xx::Ft232h::with_description("Single RS232-HS")?;
306    /// let hal = hal::FtHal::init_default(device)?;
307    /// # }
308    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
309    /// ```
310    pub fn init_default(device: Device) -> Result<FtHal<Device>, Error<E>> {
311        let settings: MpsseSettings = MpsseSettings {
312            clock_frequency: Some(100_000),
313            ..Default::default()
314        };
315
316        Ok(FtHal::init(device, &settings)?)
317    }
318
319    /// Initialize the FTDI MPSSE with sane defaults and custom frequency
320    ///
321    /// # Example
322    ///
323    /// ```no_run
324    /// use ftdi_embedded_hal as hal;
325    ///
326    /// # #[cfg(feature = "libftd2xx")]
327    /// # {
328    /// let device = libftd2xx::Ft232h::with_description("Single RS232-HS")?;
329    /// let hal = hal::FtHal::init_freq(device, 3_000_000)?;
330    /// # }
331    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
332    /// ```
333    pub fn init_freq(device: Device, freq: u32) -> Result<FtHal<Device>, Error<E>> {
334        let settings: MpsseSettings = MpsseSettings {
335            clock_frequency: Some(freq),
336            ..Default::default()
337        };
338
339        Ok(FtHal::init(device, &settings)?)
340    }
341
342    /// Initialize the FTDI MPSSE with custom values.
343    ///
344    /// **Note:** The `mask` field of [`MpsseSettings`] is ignored for this function.
345    ///
346    /// **Note:** The clock frequency will be 2/3 of the specified value when in
347    /// I2C mode.
348    ///
349    /// # Panics
350    ///
351    /// Panics if the `clock_frequency` field of [`MpsseSettings`] is `None`.
352    ///
353    /// # Example
354    ///
355    /// ```no_run
356    /// use ftdi_embedded_hal as hal;
357    /// use ftdi_mpsse::MpsseSettings;
358    /// use std::time::Duration;
359    ///
360    /// let mpsse = MpsseSettings {
361    ///     reset: false,
362    ///     in_transfer_size: 4096,
363    ///     read_timeout: Duration::from_secs(5),
364    ///     write_timeout: Duration::from_secs(5),
365    ///     latency_timer: Duration::from_millis(32),
366    ///     mask: 0x00,
367    ///     clock_frequency: Some(400_000),
368    /// };
369    ///
370    /// # #[cfg(feature = "libftd2xx")]
371    /// # {
372    /// let device = libftd2xx::Ft232h::with_description("Single RS232-HS")?;
373    /// let hal = hal::FtHal::init(device, &mpsse)?;
374    /// # }
375    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
376    /// ```
377    ///
378    /// [`MpsseSettings`]: ftdi_mpsse::MpsseSettings
379    pub fn init(mut device: Device, mpsse_settings: &MpsseSettings) -> Result<FtHal<Device>, E> {
380        device.init(mpsse_settings)?;
381
382        Ok(FtHal {
383            mtx: Arc::new(Mutex::new(device.into())),
384        })
385    }
386}
387
388impl<Device, E> FtHal<Device>
389where
390    Device: MpsseCmdExecutor<Error = E>,
391    E: std::error::Error,
392    Error<E>: From<E>,
393{
394    /// Aquire the SPI peripheral for the FT232H.
395    ///
396    /// Pin assignments:
397    /// * AD0 => SCK
398    /// * AD1 => MOSI
399    /// * AD2 => MISO
400    ///
401    /// # Panics
402    ///
403    /// Panics if pin 0, 1, or 2 are already in use.
404    ///
405    /// # Example
406    ///
407    /// ```no_run
408    /// use ftdi_embedded_hal as hal;
409    ///
410    /// # #[cfg(feature = "libftd2xx")]
411    /// # {
412    /// let device = libftd2xx::Ft2232h::with_description("Dual RS232-HS A")?;
413    /// let hal = hal::FtHal::init_freq(device, 3_000_000)?;
414    /// let spi = hal.spi()?;
415    /// # }
416    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
417    /// ```
418    pub fn spi(&self) -> Result<Spi<Device>, Error<E>> {
419        Spi::new(self.mtx.clone())
420    }
421
422    /// Aquire the SPI peripheral with a chip select pin.
423    ///
424    /// This is specific to embedded-hal version 1.
425    ///
426    /// Pin assignments:
427    /// * AD0 => SCK
428    /// * AD1 => MOSI
429    /// * AD2 => MISO
430    ///
431    /// # Panics
432    ///
433    /// Panics if pin 0, 1, 2 or the CS pin are already in use.
434    ///
435    /// # Example
436    ///
437    /// ```no_run
438    /// use ftdi_embedded_hal as hal;
439    ///
440    /// # #[cfg(feature = "libftd2xx")]
441    /// # {
442    /// let device = libftd2xx::Ft2232h::with_description("Dual RS232-HS A")?;
443    /// let hal = hal::FtHal::init_freq(device, 3_000_000)?;
444    /// let spi = hal.spi_device(3)?;
445    /// # }
446    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
447    /// ```
448    pub fn spi_device(&self, cs_idx: u8) -> Result<SpiDevice<Device>, Error<E>> {
449        SpiDevice::new(self.mtx.clone(), cs_idx)
450    }
451
452    /// Aquire the I2C peripheral for the FT232H.
453    ///
454    /// Pin assignments:
455    /// * AD0 => SCL
456    /// * AD1 => SDA
457    /// * AD2 => SDA
458    ///
459    /// Yes, AD1 and AD2 are both SDA.
460    /// These pins must be shorted together for I2C operation.
461    ///
462    /// # Panics
463    ///
464    /// Panics if pin 0, 1, or 2 are already in use.
465    ///
466    /// # Example
467    ///
468    /// ```no_run
469    /// use ftdi_embedded_hal as hal;
470    ///
471    /// # #[cfg(feature = "libftd2xx")]
472    /// # {
473    /// let device = libftd2xx::Ft2232h::with_description("Dual RS232-HS A")?;
474    /// let hal = hal::FtHal::init_freq(device, 3_000_000)?;
475    /// let i2c = hal.i2c()?;
476    /// # }
477    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
478    /// ```
479    pub fn i2c(&self) -> Result<I2c<Device>, Error<E>> {
480        I2c::new(self.mtx.clone())
481    }
482
483    /// Aquire the digital output pin 0 for the FT232H.
484    ///
485    /// # Panics
486    ///
487    /// Panics if the pin is already in-use.
488    pub fn ad0(&self) -> Result<OutputPin<Device>, Error<E>> {
489        OutputPin::new(self.mtx.clone(), Pin::Lower(0))
490    }
491
492    /// Aquire the digital input pin 0 for the FT232H.
493    ///
494    /// # Panics
495    ///
496    /// Panics if the pin is already in-use.
497    pub fn adi0(&self) -> Result<InputPin<Device>, Error<E>> {
498        InputPin::new(self.mtx.clone(), Pin::Lower(0))
499    }
500
501    /// Aquire the digital output pin 1 for the FT232H.
502    ///
503    /// # Panics
504    ///
505    /// Panics if the pin is already in-use.
506    pub fn ad1(&self) -> Result<OutputPin<Device>, Error<E>> {
507        OutputPin::new(self.mtx.clone(), Pin::Lower(1))
508    }
509
510    /// Aquire the digital input pin 1 for the FT232H.
511    ///
512    /// # Panics
513    ///
514    /// Panics if the pin is already in-use.
515    pub fn adi1(&self) -> Result<InputPin<Device>, Error<E>> {
516        InputPin::new(self.mtx.clone(), Pin::Lower(1))
517    }
518
519    /// Aquire the digital output pin 2 for the FT232H.
520    ///
521    /// # Panics
522    ///
523    /// Panics if the pin is already in-use.
524    pub fn ad2(&self) -> Result<OutputPin<Device>, Error<E>> {
525        OutputPin::new(self.mtx.clone(), Pin::Lower(2))
526    }
527
528    /// Aquire the digital input pin 2 for the FT232H.
529    ///
530    /// # Panics
531    ///
532    /// Panics if the pin is already in-use.
533    pub fn adi2(&self) -> Result<InputPin<Device>, Error<E>> {
534        InputPin::new(self.mtx.clone(), Pin::Lower(2))
535    }
536
537    /// Aquire the digital output pin 3 for the FT232H.
538    ///
539    /// # Panics
540    ///
541    /// Panics if the pin is already in-use.
542    pub fn ad3(&self) -> Result<OutputPin<Device>, Error<E>> {
543        OutputPin::new(self.mtx.clone(), Pin::Lower(3))
544    }
545
546    /// Aquire the digital input pin 3 for the FT232H.
547    ///
548    /// # Panics
549    ///
550    /// Panics if the pin is already in-use.
551    pub fn adi3(&self) -> Result<InputPin<Device>, Error<E>> {
552        InputPin::new(self.mtx.clone(), Pin::Lower(3))
553    }
554
555    /// Aquire the digital output pin 4 for the FT232H.
556    ///
557    /// # Panics
558    ///
559    /// Panics if the pin is already in-use.
560    pub fn ad4(&self) -> Result<OutputPin<Device>, Error<E>> {
561        OutputPin::new(self.mtx.clone(), Pin::Lower(4))
562    }
563
564    /// Aquire the digital input pin 4 for the FT232H.
565    ///
566    /// # Panics
567    ///
568    /// Panics if the pin is already in-use.
569    pub fn adi4(&self) -> Result<InputPin<Device>, Error<E>> {
570        InputPin::new(self.mtx.clone(), Pin::Lower(4))
571    }
572
573    /// Aquire the digital output pin 5 for the FT232H.
574    ///
575    /// # Panics
576    ///
577    /// Panics if the pin is already in-use.
578    pub fn ad5(&self) -> Result<OutputPin<Device>, Error<E>> {
579        OutputPin::new(self.mtx.clone(), Pin::Lower(5))
580    }
581
582    /// Aquire the digital input pin 5 for the FT232H.
583    ///
584    /// # Panics
585    ///
586    /// Panics if the pin is already in-use.
587    pub fn adi5(&self) -> Result<InputPin<Device>, Error<E>> {
588        InputPin::new(self.mtx.clone(), Pin::Lower(5))
589    }
590
591    /// Aquire the digital output pin 6 for the FT232H.
592    ///
593    /// # Panics
594    ///
595    /// Panics if the pin is already in-use.
596    pub fn ad6(&self) -> Result<OutputPin<Device>, Error<E>> {
597        OutputPin::new(self.mtx.clone(), Pin::Lower(6))
598    }
599
600    /// Aquire the digital input pin 6 for the FT232H.
601    ///
602    /// # Panics
603    ///
604    /// Panics if the pin is already in-use.
605    pub fn adi6(&self) -> Result<InputPin<Device>, Error<E>> {
606        InputPin::new(self.mtx.clone(), Pin::Lower(6))
607    }
608
609    /// Aquire the digital output pin 7 for the FT232H.
610    ///
611    /// # Panics
612    ///
613    /// Panics if the pin is already in-use.
614    pub fn ad7(&self) -> Result<OutputPin<Device>, Error<E>> {
615        OutputPin::new(self.mtx.clone(), Pin::Lower(7))
616    }
617
618    /// Aquire the digital input pin 7 for the FT232H.
619    ///
620    /// # Panics
621    ///
622    /// Panics if the pin is already in-use.
623    pub fn adi7(&self) -> Result<InputPin<Device>, Error<E>> {
624        InputPin::new(self.mtx.clone(), Pin::Lower(7))
625    }
626
627    /// Aquire the digital output upper pin 0 for the FT232H.
628    ///
629    /// # Panics
630    ///
631    /// Panics if the pin is already in-use.
632    pub fn c0(&self) -> Result<OutputPin<Device>, Error<E>> {
633        OutputPin::new(self.mtx.clone(), Pin::Upper(0))
634    }
635
636    /// Aquire the digital input upper pin 0 for the FT232H.
637    ///
638    /// # Panics
639    ///
640    /// Panics if the pin is already in-use.
641    pub fn ci0(&self) -> Result<InputPin<Device>, Error<E>> {
642        InputPin::new(self.mtx.clone(), Pin::Upper(0))
643    }
644
645    /// Aquire the digital output upper pin 1 for the FT232H.
646    ///
647    /// # Panics
648    ///
649    /// Panics if the pin is already in-use.
650    pub fn c1(&self) -> Result<OutputPin<Device>, Error<E>> {
651        OutputPin::new(self.mtx.clone(), Pin::Upper(1))
652    }
653
654    /// Aquire the digital input upper pin 1 for the FT232H.
655    ///
656    /// # Panics
657    ///
658    /// Panics if the pin is already in-use.
659    pub fn ci1(&self) -> Result<InputPin<Device>, Error<E>> {
660        InputPin::new(self.mtx.clone(), Pin::Upper(1))
661    }
662
663    /// Aquire the digital output upper pin 2 for the FT232H.
664    ///
665    /// # Panics
666    ///
667    /// Panics if the pin is already in-use.
668    pub fn c2(&self) -> Result<OutputPin<Device>, Error<E>> {
669        OutputPin::new(self.mtx.clone(), Pin::Upper(2))
670    }
671
672    /// Aquire the digital input upper pin 2 for the FT232H.
673    ///
674    /// # Panics
675    ///
676    /// Panics if the pin is already in-use.
677    pub fn ci2(&self) -> Result<InputPin<Device>, Error<E>> {
678        InputPin::new(self.mtx.clone(), Pin::Upper(2))
679    }
680
681    /// Aquire the digital output upper pin 3 for the FT232H.
682    ///
683    /// # Panics
684    ///
685    /// Panics if the pin is already in-use.
686    pub fn c3(&self) -> Result<OutputPin<Device>, Error<E>> {
687        OutputPin::new(self.mtx.clone(), Pin::Upper(3))
688    }
689
690    /// Aquire the digital input upper pin 3 for the FT232H.
691    ///
692    /// # Panics
693    ///
694    /// Panics if the pin is already in-use.
695    pub fn ci3(&self) -> Result<InputPin<Device>, Error<E>> {
696        InputPin::new(self.mtx.clone(), Pin::Upper(3))
697    }
698
699    /// Aquire the digital output upper pin 4 for the FT232H.
700    ///
701    /// # Panics
702    ///
703    /// Panics if the pin is already in-use.
704    pub fn c4(&self) -> Result<OutputPin<Device>, Error<E>> {
705        OutputPin::new(self.mtx.clone(), Pin::Upper(4))
706    }
707
708    /// Aquire the digital input upper pin 4 for the FT232H.
709    ///
710    /// # Panics
711    ///
712    /// Panics if the pin is already in-use.
713    pub fn ci4(&self) -> Result<InputPin<Device>, Error<E>> {
714        InputPin::new(self.mtx.clone(), Pin::Upper(4))
715    }
716
717    /// Aquire the digital output upper pin 5 for the FT232H.
718    ///
719    /// # Panics
720    ///
721    /// Panics if the pin is already in-use.
722    pub fn c5(&self) -> Result<OutputPin<Device>, Error<E>> {
723        OutputPin::new(self.mtx.clone(), Pin::Upper(5))
724    }
725
726    /// Aquire the digital input upper pin 5 for the FT232H.
727    ///
728    /// # Panics
729    ///
730    /// Panics if the pin is already in-use.
731    pub fn ci5(&self) -> Result<InputPin<Device>, Error<E>> {
732        InputPin::new(self.mtx.clone(), Pin::Upper(5))
733    }
734
735    /// Aquire the digital output upper pin 6 for the FT232H.
736    ///
737    /// # Panics
738    ///
739    /// Panics if the pin is already in-use.
740    pub fn c6(&self) -> Result<OutputPin<Device>, Error<E>> {
741        OutputPin::new(self.mtx.clone(), Pin::Upper(6))
742    }
743
744    /// Aquire the digital input upper pin 6 for the FT232H.
745    ///
746    /// # Panics
747    ///
748    /// Panics if the pin is already in-use.
749    pub fn ci6(&self) -> Result<InputPin<Device>, Error<E>> {
750        InputPin::new(self.mtx.clone(), Pin::Upper(6))
751    }
752
753    /// Aquire the digital output upper pin 7 for the FT232H.
754    ///
755    /// # Panics
756    ///
757    /// Panics if the pin is already in-use.
758    pub fn c7(&self) -> Result<OutputPin<Device>, Error<E>> {
759        OutputPin::new(self.mtx.clone(), Pin::Upper(7))
760    }
761
762    /// Aquire the digital input upper pin 7 for the FT232H.
763    ///
764    /// # Panics
765    ///
766    /// Panics if the pin is already in-use.
767    pub fn ci7(&self) -> Result<InputPin<Device>, Error<E>> {
768        InputPin::new(self.mtx.clone(), Pin::Upper(7))
769    }
770}