ftdi_embedded_hal/
lib.rs

1//! This is an [embedded-hal] implementation for the FTDI chips
2//! that use the [libftd2xx] or [ftdi-rs] drivers.
3//!
4//! This enables development of embedded device drivers without the use of
5//! a microcontroller. The FTDI devices interface with a PC via USB, and
6//! provide a multi-protocol synchronous serial engine to interface
7//! with most GPIO, SPI, and 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.2"
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    /// Executes the closure with the device.
395    ///
396    /// Useful for accessing EEPROM, or other device-specific functionality.
397    ///
398    /// # Example
399    ///
400    /// ```no_run
401    /// use ftdi_embedded_hal as hal;
402    /// # #[cfg(feature = "libftd2xx")]
403    /// use hal::libftd2xx::FtdiEeprom;
404    ///
405    /// # #[cfg(feature = "libftd2xx")]
406    /// # {
407    /// let device = libftd2xx::Ft2232h::with_description("Dual RS232-HS A")?;
408    /// let mut hal = hal::FtHal::init_default(device)?;
409    /// let serial_number: String =
410    ///     hal.with_device(|d| d.eeprom_read().map(|(_, strings)| strings.serial_number()))?;
411    /// # }
412    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
413    /// ```
414    pub fn with_device<T, F>(&mut self, mut f: F) -> T
415    where
416        F: FnMut(&mut Device) -> T,
417    {
418        let mut inner = self.mtx.lock().expect("Failed to aquire FTDI mutex");
419        let result: T = f(&mut inner.ft);
420        result
421    }
422
423    /// Aquire the SPI peripheral for the FT232H.
424    ///
425    /// Pin assignments:
426    /// * AD0 => SCK
427    /// * AD1 => MOSI
428    /// * AD2 => MISO
429    ///
430    /// # Panics
431    ///
432    /// Panics if pin 0, 1, or 2 are already in use.
433    ///
434    /// # Example
435    ///
436    /// ```no_run
437    /// use ftdi_embedded_hal as hal;
438    ///
439    /// # #[cfg(feature = "libftd2xx")]
440    /// # {
441    /// let device = libftd2xx::Ft2232h::with_description("Dual RS232-HS A")?;
442    /// let hal = hal::FtHal::init_freq(device, 3_000_000)?;
443    /// let spi = hal.spi()?;
444    /// # }
445    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
446    /// ```
447    pub fn spi(&self) -> Result<Spi<Device>, Error<E>> {
448        Spi::new(self.mtx.clone())
449    }
450
451    /// Aquire the SPI peripheral with a chip select pin.
452    ///
453    /// This is specific to embedded-hal version 1.
454    ///
455    /// Pin assignments:
456    /// * AD0 => SCK
457    /// * AD1 => MOSI
458    /// * AD2 => MISO
459    ///
460    /// # Panics
461    ///
462    /// Panics if pin 0, 1, 2 or the CS pin are already in use.
463    ///
464    /// # Example
465    ///
466    /// ```no_run
467    /// use ftdi_embedded_hal as hal;
468    ///
469    /// # #[cfg(feature = "libftd2xx")]
470    /// # {
471    /// let device = libftd2xx::Ft2232h::with_description("Dual RS232-HS A")?;
472    /// let hal = hal::FtHal::init_freq(device, 3_000_000)?;
473    /// let spi = hal.spi_device(3)?;
474    /// # }
475    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
476    /// ```
477    pub fn spi_device(&self, cs_idx: u8) -> Result<SpiDevice<Device>, Error<E>> {
478        SpiDevice::new(self.mtx.clone(), cs_idx)
479    }
480
481    /// Aquire the I2C peripheral for the FT232H.
482    ///
483    /// Pin assignments:
484    /// * AD0 => SCL
485    /// * AD1 => SDA
486    /// * AD2 => SDA
487    ///
488    /// Yes, AD1 and AD2 are both SDA.
489    /// These pins must be shorted together for I2C operation.
490    ///
491    /// # Panics
492    ///
493    /// Panics if pin 0, 1, or 2 are already in use.
494    ///
495    /// # Example
496    ///
497    /// ```no_run
498    /// use ftdi_embedded_hal as hal;
499    ///
500    /// # #[cfg(feature = "libftd2xx")]
501    /// # {
502    /// let device = libftd2xx::Ft2232h::with_description("Dual RS232-HS A")?;
503    /// let hal = hal::FtHal::init_freq(device, 3_000_000)?;
504    /// let i2c = hal.i2c()?;
505    /// # }
506    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
507    /// ```
508    pub fn i2c(&self) -> Result<I2c<Device>, Error<E>> {
509        I2c::new(self.mtx.clone())
510    }
511
512    /// Aquire the digital output pin 0 for the FT232H.
513    ///
514    /// # Panics
515    ///
516    /// Panics if the pin is already in-use.
517    pub fn ad0(&self) -> Result<OutputPin<Device>, Error<E>> {
518        OutputPin::new(self.mtx.clone(), Pin::Lower(0))
519    }
520
521    /// Aquire the digital input pin 0 for the FT232H.
522    ///
523    /// # Panics
524    ///
525    /// Panics if the pin is already in-use.
526    pub fn adi0(&self) -> Result<InputPin<Device>, Error<E>> {
527        InputPin::new(self.mtx.clone(), Pin::Lower(0))
528    }
529
530    /// Aquire the digital output pin 1 for the FT232H.
531    ///
532    /// # Panics
533    ///
534    /// Panics if the pin is already in-use.
535    pub fn ad1(&self) -> Result<OutputPin<Device>, Error<E>> {
536        OutputPin::new(self.mtx.clone(), Pin::Lower(1))
537    }
538
539    /// Aquire the digital input pin 1 for the FT232H.
540    ///
541    /// # Panics
542    ///
543    /// Panics if the pin is already in-use.
544    pub fn adi1(&self) -> Result<InputPin<Device>, Error<E>> {
545        InputPin::new(self.mtx.clone(), Pin::Lower(1))
546    }
547
548    /// Aquire the digital output pin 2 for the FT232H.
549    ///
550    /// # Panics
551    ///
552    /// Panics if the pin is already in-use.
553    pub fn ad2(&self) -> Result<OutputPin<Device>, Error<E>> {
554        OutputPin::new(self.mtx.clone(), Pin::Lower(2))
555    }
556
557    /// Aquire the digital input pin 2 for the FT232H.
558    ///
559    /// # Panics
560    ///
561    /// Panics if the pin is already in-use.
562    pub fn adi2(&self) -> Result<InputPin<Device>, Error<E>> {
563        InputPin::new(self.mtx.clone(), Pin::Lower(2))
564    }
565
566    /// Aquire the digital output pin 3 for the FT232H.
567    ///
568    /// # Panics
569    ///
570    /// Panics if the pin is already in-use.
571    pub fn ad3(&self) -> Result<OutputPin<Device>, Error<E>> {
572        OutputPin::new(self.mtx.clone(), Pin::Lower(3))
573    }
574
575    /// Aquire the digital input pin 3 for the FT232H.
576    ///
577    /// # Panics
578    ///
579    /// Panics if the pin is already in-use.
580    pub fn adi3(&self) -> Result<InputPin<Device>, Error<E>> {
581        InputPin::new(self.mtx.clone(), Pin::Lower(3))
582    }
583
584    /// Aquire the digital output pin 4 for the FT232H.
585    ///
586    /// # Panics
587    ///
588    /// Panics if the pin is already in-use.
589    pub fn ad4(&self) -> Result<OutputPin<Device>, Error<E>> {
590        OutputPin::new(self.mtx.clone(), Pin::Lower(4))
591    }
592
593    /// Aquire the digital input pin 4 for the FT232H.
594    ///
595    /// # Panics
596    ///
597    /// Panics if the pin is already in-use.
598    pub fn adi4(&self) -> Result<InputPin<Device>, Error<E>> {
599        InputPin::new(self.mtx.clone(), Pin::Lower(4))
600    }
601
602    /// Aquire the digital output pin 5 for the FT232H.
603    ///
604    /// # Panics
605    ///
606    /// Panics if the pin is already in-use.
607    pub fn ad5(&self) -> Result<OutputPin<Device>, Error<E>> {
608        OutputPin::new(self.mtx.clone(), Pin::Lower(5))
609    }
610
611    /// Aquire the digital input pin 5 for the FT232H.
612    ///
613    /// # Panics
614    ///
615    /// Panics if the pin is already in-use.
616    pub fn adi5(&self) -> Result<InputPin<Device>, Error<E>> {
617        InputPin::new(self.mtx.clone(), Pin::Lower(5))
618    }
619
620    /// Aquire the digital output pin 6 for the FT232H.
621    ///
622    /// # Panics
623    ///
624    /// Panics if the pin is already in-use.
625    pub fn ad6(&self) -> Result<OutputPin<Device>, Error<E>> {
626        OutputPin::new(self.mtx.clone(), Pin::Lower(6))
627    }
628
629    /// Aquire the digital input pin 6 for the FT232H.
630    ///
631    /// # Panics
632    ///
633    /// Panics if the pin is already in-use.
634    pub fn adi6(&self) -> Result<InputPin<Device>, Error<E>> {
635        InputPin::new(self.mtx.clone(), Pin::Lower(6))
636    }
637
638    /// Aquire the digital output pin 7 for the FT232H.
639    ///
640    /// # Panics
641    ///
642    /// Panics if the pin is already in-use.
643    pub fn ad7(&self) -> Result<OutputPin<Device>, Error<E>> {
644        OutputPin::new(self.mtx.clone(), Pin::Lower(7))
645    }
646
647    /// Aquire the digital input pin 7 for the FT232H.
648    ///
649    /// # Panics
650    ///
651    /// Panics if the pin is already in-use.
652    pub fn adi7(&self) -> Result<InputPin<Device>, Error<E>> {
653        InputPin::new(self.mtx.clone(), Pin::Lower(7))
654    }
655
656    /// Aquire the digital output upper pin 0 for the FT232H.
657    ///
658    /// # Panics
659    ///
660    /// Panics if the pin is already in-use.
661    pub fn c0(&self) -> Result<OutputPin<Device>, Error<E>> {
662        OutputPin::new(self.mtx.clone(), Pin::Upper(0))
663    }
664
665    /// Aquire the digital input upper pin 0 for the FT232H.
666    ///
667    /// # Panics
668    ///
669    /// Panics if the pin is already in-use.
670    pub fn ci0(&self) -> Result<InputPin<Device>, Error<E>> {
671        InputPin::new(self.mtx.clone(), Pin::Upper(0))
672    }
673
674    /// Aquire the digital output upper pin 1 for the FT232H.
675    ///
676    /// # Panics
677    ///
678    /// Panics if the pin is already in-use.
679    pub fn c1(&self) -> Result<OutputPin<Device>, Error<E>> {
680        OutputPin::new(self.mtx.clone(), Pin::Upper(1))
681    }
682
683    /// Aquire the digital input upper pin 1 for the FT232H.
684    ///
685    /// # Panics
686    ///
687    /// Panics if the pin is already in-use.
688    pub fn ci1(&self) -> Result<InputPin<Device>, Error<E>> {
689        InputPin::new(self.mtx.clone(), Pin::Upper(1))
690    }
691
692    /// Aquire the digital output upper pin 2 for the FT232H.
693    ///
694    /// # Panics
695    ///
696    /// Panics if the pin is already in-use.
697    pub fn c2(&self) -> Result<OutputPin<Device>, Error<E>> {
698        OutputPin::new(self.mtx.clone(), Pin::Upper(2))
699    }
700
701    /// Aquire the digital input upper pin 2 for the FT232H.
702    ///
703    /// # Panics
704    ///
705    /// Panics if the pin is already in-use.
706    pub fn ci2(&self) -> Result<InputPin<Device>, Error<E>> {
707        InputPin::new(self.mtx.clone(), Pin::Upper(2))
708    }
709
710    /// Aquire the digital output upper pin 3 for the FT232H.
711    ///
712    /// # Panics
713    ///
714    /// Panics if the pin is already in-use.
715    pub fn c3(&self) -> Result<OutputPin<Device>, Error<E>> {
716        OutputPin::new(self.mtx.clone(), Pin::Upper(3))
717    }
718
719    /// Aquire the digital input upper pin 3 for the FT232H.
720    ///
721    /// # Panics
722    ///
723    /// Panics if the pin is already in-use.
724    pub fn ci3(&self) -> Result<InputPin<Device>, Error<E>> {
725        InputPin::new(self.mtx.clone(), Pin::Upper(3))
726    }
727
728    /// Aquire the digital output upper pin 4 for the FT232H.
729    ///
730    /// # Panics
731    ///
732    /// Panics if the pin is already in-use.
733    pub fn c4(&self) -> Result<OutputPin<Device>, Error<E>> {
734        OutputPin::new(self.mtx.clone(), Pin::Upper(4))
735    }
736
737    /// Aquire the digital input upper pin 4 for the FT232H.
738    ///
739    /// # Panics
740    ///
741    /// Panics if the pin is already in-use.
742    pub fn ci4(&self) -> Result<InputPin<Device>, Error<E>> {
743        InputPin::new(self.mtx.clone(), Pin::Upper(4))
744    }
745
746    /// Aquire the digital output upper pin 5 for the FT232H.
747    ///
748    /// # Panics
749    ///
750    /// Panics if the pin is already in-use.
751    pub fn c5(&self) -> Result<OutputPin<Device>, Error<E>> {
752        OutputPin::new(self.mtx.clone(), Pin::Upper(5))
753    }
754
755    /// Aquire the digital input upper pin 5 for the FT232H.
756    ///
757    /// # Panics
758    ///
759    /// Panics if the pin is already in-use.
760    pub fn ci5(&self) -> Result<InputPin<Device>, Error<E>> {
761        InputPin::new(self.mtx.clone(), Pin::Upper(5))
762    }
763
764    /// Aquire the digital output upper pin 6 for the FT232H.
765    ///
766    /// # Panics
767    ///
768    /// Panics if the pin is already in-use.
769    pub fn c6(&self) -> Result<OutputPin<Device>, Error<E>> {
770        OutputPin::new(self.mtx.clone(), Pin::Upper(6))
771    }
772
773    /// Aquire the digital input upper pin 6 for the FT232H.
774    ///
775    /// # Panics
776    ///
777    /// Panics if the pin is already in-use.
778    pub fn ci6(&self) -> Result<InputPin<Device>, Error<E>> {
779        InputPin::new(self.mtx.clone(), Pin::Upper(6))
780    }
781
782    /// Aquire the digital output upper pin 7 for the FT232H.
783    ///
784    /// # Panics
785    ///
786    /// Panics if the pin is already in-use.
787    pub fn c7(&self) -> Result<OutputPin<Device>, Error<E>> {
788        OutputPin::new(self.mtx.clone(), Pin::Upper(7))
789    }
790
791    /// Aquire the digital input upper pin 7 for the FT232H.
792    ///
793    /// # Panics
794    ///
795    /// Panics if the pin is already in-use.
796    pub fn ci7(&self) -> Result<InputPin<Device>, Error<E>> {
797        InputPin::new(self.mtx.clone(), Pin::Upper(7))
798    }
799}