#![doc(html_root_url = "https://docs.rs/ftd2xx-embedded-hal/0.7.0")]
#![forbid(missing_docs)]
#![forbid(unsafe_code)]
pub use embedded_hal;
pub use libftd2xx;
mod delay;
mod gpio;
mod i2c;
mod spi;
pub use delay::Delay;
pub use gpio::OutputPin;
pub use i2c::{I2c, I2cError};
pub use spi::Spi;
use libftd2xx::{
DeviceTypeError, Ft2232h, Ft232h, Ft4232h, Ftdi, FtdiCommon, FtdiMpsse, MpsseSettings,
TimeoutError,
};
use std::convert::TryFrom;
use std::{cell::RefCell, convert::TryInto, sync::Mutex, time::Duration};
#[derive(Debug, Clone, Copy)]
enum PinUse {
I2c,
Spi,
Output,
}
impl std::fmt::Display for PinUse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PinUse::I2c => write!(f, "I2C"),
PinUse::Spi => write!(f, "SPI"),
PinUse::Output => write!(f, "GPIO"),
}
}
}
#[derive(Debug)]
struct FtInner<Device> {
ft: Device,
direction: u8,
value: u8,
pins: [Option<PinUse>; 8],
}
impl<Device: FtdiCommon> FtInner<Device> {
pub fn allocate_pin(&mut self, idx: u8, purpose: PinUse) {
assert!(idx < 8, "Pin index {} is out of range 0 - 7", idx);
if let Some(current) = self.pins[usize::from(idx)] {
panic!(
"Unable to allocate pin {} for {}, pin is already allocated for {}",
idx, purpose, current
);
} else {
self.pins[usize::from(idx)] = Some(purpose)
}
}
}
impl<Device: FtdiCommon> From<Device> for FtInner<Device> {
fn from(ft: Device) -> Self {
FtInner {
ft,
direction: 0xFB,
value: 0x00,
pins: [None; 8],
}
}
}
pub struct Initialized;
pub struct Uninitialized;
pub type Ft232hHal<T> = FtHal<Ft232h, T>;
pub type Ft2232hHal<T> = FtHal<Ft2232h, T>;
pub type Ft4232hHal<T> = FtHal<Ft4232h, T>;
#[derive(Debug)]
pub struct FtHal<Device, INITIALIZED> {
#[allow(dead_code)]
init: INITIALIZED,
mtx: Mutex<RefCell<FtInner<Device>>>,
}
impl<Device: FtdiCommon + TryFrom<Ftdi, Error = DeviceTypeError> + FtdiMpsse>
FtHal<Device, Uninitialized>
{
pub fn new() -> Result<FtHal<Device, Uninitialized>, DeviceTypeError> {
let ft: Device = Ftdi::new()?.try_into()?;
Ok(ft.into())
}
pub fn with_serial_number(sn: &str) -> Result<FtHal<Device, Uninitialized>, DeviceTypeError> {
let ft: Device = Ftdi::with_serial_number(sn)?.try_into()?;
Ok(ft.into())
}
pub fn with_description(
description: &str,
) -> Result<FtHal<Device, Uninitialized>, DeviceTypeError> {
let ft: Device = Ftdi::with_description(description)?.try_into()?;
Ok(ft.into())
}
pub fn init_default(self) -> Result<FtHal<Device, Initialized>, TimeoutError> {
const DEFAULT: MpsseSettings = MpsseSettings {
reset: true,
in_transfer_size: 4096,
read_timeout: Duration::from_secs(1),
write_timeout: Duration::from_secs(1),
latency_timer: Duration::from_millis(16),
mask: 0x00,
clock_frequency: Some(100_000),
};
self.init(&DEFAULT)
}
pub fn init(
self,
mpsse_settings: &MpsseSettings,
) -> Result<FtHal<Device, Initialized>, TimeoutError> {
{
let lock = self.mtx.lock().expect("Failed to aquire FTDI mutex");
let mut inner = lock.borrow_mut();
let mut settings = *mpsse_settings;
settings.mask = inner.direction;
inner.ft.initialize_mpsse(&mpsse_settings)?;
}
Ok(FtHal {
init: Initialized,
mtx: self.mtx,
})
}
}
impl<Device: FtdiCommon> From<Device> for FtHal<Device, Uninitialized> {
fn from(ft: Device) -> Self {
FtHal {
init: Uninitialized,
mtx: Mutex::new(RefCell::new(ft.into())),
}
}
}
impl<Device: FtdiCommon> FtHal<Device, Initialized> {
pub fn spi(&self) -> Result<Spi<Device>, TimeoutError> {
Spi::new(&self.mtx)
}
pub fn i2c(&self) -> Result<I2c<Device>, TimeoutError> {
I2c::new(&self.mtx)
}
pub fn ad0(&self) -> OutputPin<Device> {
OutputPin::new(&self.mtx, 0)
}
pub fn ad1(&self) -> OutputPin<Device> {
OutputPin::new(&self.mtx, 1)
}
pub fn ad2(&self) -> OutputPin<Device> {
OutputPin::new(&self.mtx, 2)
}
pub fn ad3(&self) -> OutputPin<Device> {
OutputPin::new(&self.mtx, 3)
}
pub fn ad4(&self) -> OutputPin<Device> {
OutputPin::new(&self.mtx, 4)
}
pub fn ad5(&self) -> OutputPin<Device> {
OutputPin::new(&self.mtx, 5)
}
pub fn ad6(&self) -> OutputPin<Device> {
OutputPin::new(&self.mtx, 6)
}
pub fn ad7(&self) -> OutputPin<Device> {
OutputPin::new(&self.mtx, 7)
}
}