bitbang: first example of it actually working
Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
parent
22528a892f
commit
e3bacc1e32
@ -1,10 +1,58 @@
|
|||||||
use ftdi_vcp_sys::{
|
use ftdi_vcp_sys::{
|
||||||
FT_Close, FT_GetComPortNumber, FT_OpenEx, FT_HANDLE, FT_OPEN_BY_DESCRIPTION, FT_STATUS, LONG,
|
FT_Close, FT_GetBitMode, FT_GetComPortNumber, FT_OpenEx, FT_SetBitMode, FT_Write, DWORD,
|
||||||
PVOID,
|
FT_HANDLE, FT_OPEN_BY_DESCRIPTION, FT_STATUS, LONG, LPDWORD, LPVOID, PVOID, UCHAR,
|
||||||
};
|
};
|
||||||
|
use std::convert::TryInto;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
use std::convert::TryInto;
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum BitMode {
|
||||||
|
Reset,
|
||||||
|
AsyncBitbang,
|
||||||
|
MPSSE,
|
||||||
|
SyncBitbang,
|
||||||
|
MCUHost,
|
||||||
|
FastSerial,
|
||||||
|
CBUSBitbang,
|
||||||
|
SyncFIFO,
|
||||||
|
Unknown(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UCHAR> for BitMode {
|
||||||
|
fn from(src: UCHAR) -> Self {
|
||||||
|
use BitMode::*;
|
||||||
|
match src {
|
||||||
|
0x00 => Reset,
|
||||||
|
0x01 => AsyncBitbang,
|
||||||
|
0x02 => MPSSE,
|
||||||
|
0x04 => SyncBitbang,
|
||||||
|
0x08 => MCUHost,
|
||||||
|
0x10 => FastSerial,
|
||||||
|
0x20 => CBUSBitbang,
|
||||||
|
0x40 => SyncFIFO,
|
||||||
|
x => Unknown(x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitMode {
|
||||||
|
pub fn to_u8(&self) -> UCHAR {
|
||||||
|
use BitMode::*;
|
||||||
|
match *self {
|
||||||
|
Reset => 0x00,
|
||||||
|
AsyncBitbang => 0x01,
|
||||||
|
MPSSE => 0x02,
|
||||||
|
SyncBitbang => 0x04,
|
||||||
|
MCUHost => 0x08,
|
||||||
|
FastSerial => 0x10,
|
||||||
|
CBUSBitbang => 0x20,
|
||||||
|
SyncFIFO => 0x40,
|
||||||
|
Unknown(x) => x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@ -29,9 +77,17 @@ pub enum Error {
|
|||||||
OtherError,
|
OtherError,
|
||||||
DeviceListNotReady,
|
DeviceListNotReady,
|
||||||
NoComPortAssigned,
|
NoComPortAssigned,
|
||||||
|
// UnknownBitMode(u8),
|
||||||
UnknownError(FT_STATUS),
|
UnknownError(FT_STATUS),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "VCPError: {:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
impl From<FT_STATUS> for Error {
|
impl From<FT_STATUS> for Error {
|
||||||
fn from(src: FT_STATUS) -> Self {
|
fn from(src: FT_STATUS) -> Self {
|
||||||
match src {
|
match src {
|
||||||
@ -60,8 +116,10 @@ impl From<FT_STATUS> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct VCP {
|
pub struct VCP {
|
||||||
handle: FT_HANDLE,
|
handle: FT_HANDLE,
|
||||||
|
bit_mode: BitMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VCP {
|
impl VCP {
|
||||||
@ -76,19 +134,24 @@ impl VCP {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
if result != Error::NoError {
|
if result != Error::NoError {
|
||||||
Err(result)
|
return Err(result);
|
||||||
} else {
|
|
||||||
Ok(VCP {
|
|
||||||
handle: unsafe { handle.assume_init() },
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
let handle = unsafe { handle.assume_init() };
|
||||||
|
|
||||||
|
let mut bit_mode = MaybeUninit::<UCHAR>::uninit();
|
||||||
|
let result = Error::from(unsafe { FT_GetBitMode(handle, bit_mode.as_mut_ptr()) });
|
||||||
|
if result != Error::NoError {
|
||||||
|
unsafe { FT_Close(handle) };
|
||||||
|
return Err(result);
|
||||||
|
}
|
||||||
|
let bit_mode = BitMode::from(unsafe { bit_mode.assume_init() });
|
||||||
|
Ok(VCP { handle, bit_mode })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn com_port(&self) -> Result<usize, Error> {
|
pub fn com_port(&self) -> Result<usize, Error> {
|
||||||
let mut com_port_number = MaybeUninit::<LONG>::uninit();
|
let mut com_port_number = MaybeUninit::<LONG>::uninit();
|
||||||
let result = Error::from(unsafe {
|
let result =
|
||||||
FT_GetComPortNumber(self.handle, com_port_number.as_mut_ptr())
|
Error::from(unsafe { FT_GetComPortNumber(self.handle, com_port_number.as_mut_ptr()) });
|
||||||
});
|
|
||||||
if result != Error::NoError {
|
if result != Error::NoError {
|
||||||
Err(result)
|
Err(result)
|
||||||
} else {
|
} else {
|
||||||
@ -100,6 +163,88 @@ impl VCP {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the given signals to "OUTPUT". All other signals will be "INPUT".
|
||||||
|
pub fn set_bit_mode(&mut self, outputs: u8) -> Result<(), Error> {
|
||||||
|
let result = Error::from(unsafe {
|
||||||
|
FT_SetBitMode(self.handle, outputs, BitMode::SyncBitbang.to_u8())
|
||||||
|
});
|
||||||
|
if result != Error::NoError {
|
||||||
|
Err(result)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self, out_buffer: &[u8]) -> Result<usize, Error> {
|
||||||
|
let mut bytes_written = MaybeUninit::<DWORD>::uninit();
|
||||||
|
let result = Error::from(unsafe {
|
||||||
|
FT_Write(
|
||||||
|
self.handle,
|
||||||
|
out_buffer.as_ptr() as LPVOID,
|
||||||
|
out_buffer
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.expect("couldn't convert buffer length to DWORD"),
|
||||||
|
bytes_written.as_mut_ptr() as LPDWORD,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
if result != Error::NoError {
|
||||||
|
Err(result)
|
||||||
|
} else {
|
||||||
|
Ok(unsafe { bytes_written.assume_init() }
|
||||||
|
.try_into()
|
||||||
|
.expect("invalid number of bytes written"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for VCP {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
|
let mut bytes_written = MaybeUninit::<DWORD>::uninit();
|
||||||
|
let result = Error::from(unsafe {
|
||||||
|
FT_Write(
|
||||||
|
self.handle,
|
||||||
|
buf.as_ptr() as LPVOID,
|
||||||
|
buf
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.expect("couldn't convert buffer length to DWORD"),
|
||||||
|
bytes_written.as_mut_ptr() as LPDWORD,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
if result != Error::NoError {
|
||||||
|
Err(std::io::Error::new(std::io::ErrorKind::Other, result))
|
||||||
|
} else {
|
||||||
|
Ok(unsafe { bytes_written.assume_init() }
|
||||||
|
.try_into()
|
||||||
|
.expect("invalid number of bytes written"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for VCP {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
|
||||||
|
let mut bytes_written = MaybeUninit::<DWORD>::uninit();
|
||||||
|
let result = Error::from(unsafe {
|
||||||
|
FT_Write(
|
||||||
|
self.handle,
|
||||||
|
buf.as_ptr() as LPVOID,
|
||||||
|
1,
|
||||||
|
bytes_written.as_mut_ptr() as LPDWORD,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
if result != Error::NoError {
|
||||||
|
Err(std::io::Error::new(std::io::ErrorKind::Other, result))
|
||||||
|
} else {
|
||||||
|
Ok(unsafe { bytes_written.assume_init() }
|
||||||
|
.try_into()
|
||||||
|
.expect("invalid number of bytes written"))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for VCP {
|
impl Drop for VCP {
|
||||||
@ -110,11 +255,3 @@ impl Drop for VCP {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
|
||||||
assert_eq!(2 + 2, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
extern crate winapi;
|
extern crate winapi;
|
||||||
// use winapi::um::winnt::{PVOID, ULONG, DWORD};
|
// use winapi::um::winnt::{PVOID, ULONG, DWORD};
|
||||||
pub use winapi::shared::minwindef::{DWORD, LPDWORD, LPLONG, LPVOID};
|
pub use winapi::shared::minwindef::{DWORD, LPDWORD, LPLONG, LPVOID, UCHAR, PUCHAR};
|
||||||
pub use winapi::shared::ntdef::{PVOID, ULONG, LONG};
|
pub use winapi::shared::ntdef::{LONG, PVOID, ULONG};
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub type FT_HANDLE = PVOID;
|
pub type FT_HANDLE = PVOID;
|
||||||
@ -88,6 +88,20 @@ extern "stdcall" {
|
|||||||
pub fn FT_Open(deviceNumber: u32, pHandle: *mut FT_HANDLE) -> FT_STATUS;
|
pub fn FT_Open(deviceNumber: u32, pHandle: *mut FT_HANDLE) -> FT_STATUS;
|
||||||
pub fn FT_OpenEx(pArg1: PVOID, Flags: DWORD, pHandle: *mut FT_HANDLE) -> FT_STATUS;
|
pub fn FT_OpenEx(pArg1: PVOID, Flags: DWORD, pHandle: *mut FT_HANDLE) -> FT_STATUS;
|
||||||
pub fn FT_Close(ftHandle: FT_HANDLE) -> FT_STATUS;
|
pub fn FT_Close(ftHandle: FT_HANDLE) -> FT_STATUS;
|
||||||
|
pub fn FT_Write(
|
||||||
|
ftHandle: FT_HANDLE,
|
||||||
|
lpBuffer: LPVOID,
|
||||||
|
dwBytesToWrite: DWORD,
|
||||||
|
lpBytesWritten: LPDWORD,
|
||||||
|
) -> FT_STATUS;
|
||||||
|
pub fn FT_Read(
|
||||||
|
ftHandle: FT_HANDLE,
|
||||||
|
lpBuffer: LPVOID,
|
||||||
|
dwBytesToRead: DWORD,
|
||||||
|
lpBytesReturned: LPDWORD,
|
||||||
|
) -> FT_STATUS;
|
||||||
|
pub fn FT_SetBitMode(ftHandle: FT_HANDLE, ucMask: UCHAR, ucEnable: UCHAR) -> FT_STATUS;
|
||||||
|
pub fn FT_GetBitMode(ftHandle: FT_HANDLE, pucMode: PUCHAR) -> FT_STATUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_device_info_list() -> Result<usize, usize> {
|
pub fn create_device_info_list() -> Result<usize, usize> {
|
||||||
|
12
src/main.rs
12
src/main.rs
@ -7,8 +7,20 @@
|
|||||||
#define FT_LIST_MASK (FT_LIST_NUMBER_ONLY|FT_LIST_BY_INDEX|FT_LIST_ALL)
|
#define FT_LIST_MASK (FT_LIST_NUMBER_ONLY|FT_LIST_BY_INDEX|FT_LIST_ALL)
|
||||||
*/
|
*/
|
||||||
use ftdi_vcp_rs::VCP;
|
use ftdi_vcp_rs::VCP;
|
||||||
|
use std::thread::sleep;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut vcp = VCP::new_from_name("iCEBreaker V1.0e A").expect("couldn't open vcp");
|
let mut vcp = VCP::new_from_name("iCEBreaker V1.0e A").expect("couldn't open vcp");
|
||||||
|
println!("Opened VCP: {:?}", vcp);
|
||||||
|
vcp.set_bit_mode(0x80).expect("couldn't set bit mode");
|
||||||
|
for i in 0..10 {
|
||||||
|
if i & 1 != 0 {
|
||||||
|
vcp.write(&[0x80]).expect("couldn't set all 1");
|
||||||
|
} else {
|
||||||
|
vcp.write(&[0x00]).expect("couldn't set all 1");
|
||||||
|
}
|
||||||
|
sleep(Duration::from_millis(500));
|
||||||
|
}
|
||||||
println!("VCP COM{}:", vcp.com_port().expect("couldn't get com port"));
|
println!("VCP COM{}:", vcp.com_port().expect("couldn't get com port"));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user