#![doc(html_root_url = "https://docs.rs/ftd2xx-embedded-hal/0.1.0")]
#![deny(unsafe_code, missing_docs)]
pub use embedded_hal;
pub use libftd2xx;
use embedded_hal::spi::Polarity;
use libftd2xx::{
ClockData, ClockDataOut, DeviceTypeError, Ft232h, Ftdi, FtdiMpsse, MpsseSettings, TimeoutError,
};
use std::cell::RefCell;
use std::convert::TryFrom;
use std::sync::Mutex;
pub struct Ft232hHal {
mtx: Mutex<RefCell<Ft232h>>,
value: Mutex<u8>,
}
impl Ft232hHal {
pub fn new() -> Result<Ft232hHal, DeviceTypeError> {
let ft: Ft232h = Ft232h::try_from(&mut Ftdi::new()?)?;
Ok(Self::with_ft(ft))
}
pub fn with_ft(ft: Ft232h) -> Ft232hHal {
Ft232hHal {
mtx: Mutex::new(RefCell::new(ft)),
value: Mutex::new(0x00),
}
}
pub fn spi(&self) -> Result<Spi, TimeoutError> {
Spi::new(&self.mtx)
}
pub fn ad0(&self) -> OutputPin {
OutputPin::new(&self.mtx, &self.value, 0)
}
pub fn ad1(&self) -> OutputPin {
OutputPin::new(&self.mtx, &self.value, 1)
}
pub fn ad2(&self) -> OutputPin {
OutputPin::new(&self.mtx, &self.value, 2)
}
pub fn ad3(&self) -> OutputPin {
OutputPin::new(&self.mtx, &self.value, 3)
}
pub fn ad4(&self) -> OutputPin {
OutputPin::new(&self.mtx, &self.value, 4)
}
pub fn ad5(&self) -> OutputPin {
OutputPin::new(&self.mtx, &self.value, 5)
}
pub fn ad6(&self) -> OutputPin {
OutputPin::new(&self.mtx, &self.value, 6)
}
pub fn ad7(&self) -> OutputPin {
OutputPin::new(&self.mtx, &self.value, 7)
}
}
pub struct OutputPin<'a> {
mtx: &'a Mutex<RefCell<Ft232h>>,
value: &'a Mutex<u8>,
mask: u8,
}
pub struct Spi<'a> {
mtx: &'a Mutex<RefCell<Ft232h>>,
clk: ClockData,
clk_out: ClockDataOut,
}
impl<'a> Spi<'a> {
pub(crate) fn new(mtx: &Mutex<RefCell<Ft232h>>) -> Result<Spi, TimeoutError> {
let lock = mtx.lock().unwrap();
let mut ft = lock.borrow_mut();
let mut settings = MpsseSettings::default();
settings.mask = 0x1B;
ft.initialize_mpsse(&settings)?;
ft.set_clock(100_000)?;
ft.set_gpio_lower(0x18, 0x1B)?;
Ok(Spi {
mtx,
clk: ClockData::MsbPosIn,
clk_out: ClockDataOut::MsbNeg,
})
}
pub fn set_clock_polarity(&mut self, cpol: embedded_hal::spi::Polarity) {
self.clk = if cpol == Polarity::IdleLow {
ClockData::MsbPosIn
} else {
ClockData::MsbNegIn
};
self.clk_out = if cpol == Polarity::IdleLow {
ClockDataOut::MsbNeg
} else {
ClockDataOut::MsbPos
};
}
}
impl<'a> embedded_hal::blocking::spi::Write<u8> for Spi<'a> {
type Error = TimeoutError;
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
let lock = self.mtx.lock().unwrap();
let mut ft = lock.borrow_mut();
ft.clock_data_out(self.clk_out, words)
}
}
impl<'a> embedded_hal::blocking::spi::Transfer<u8> for Spi<'a> {
type Error = TimeoutError;
fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
let lock = self.mtx.lock().unwrap();
let mut ft = lock.borrow_mut();
ft.clock_data(self.clk, words)?;
Ok(words)
}
}
impl<'a> OutputPin<'a> {
pub(crate) fn new(
mtx: &'a Mutex<RefCell<Ft232h>>,
value: &'a Mutex<u8>,
bit: u8,
) -> OutputPin<'a> {
let mask: u8 = 1 << bit;
OutputPin { mtx, value, mask }
}
pub(crate) fn set(&self, state: bool) -> Result<(), TimeoutError> {
let mut value = self.value.lock().unwrap();
if state {
*value |= self.mask;
} else {
*value &= !self.mask;
};
let lock = self.mtx.lock().unwrap();
let mut ft = lock.borrow_mut();
ft.set_gpio_lower(*value, 0x1B)
}
}
impl<'a> embedded_hal::digital::v2::OutputPin for OutputPin<'a> {
type Error = TimeoutError;
fn set_low(&mut self) -> Result<(), Self::Error> {
self.set(false)
}
fn set_high(&mut self) -> Result<(), Self::Error> {
self.set(true)
}
}