在 Ubuntu 20.04 上使用 Python3 从 FTDI D2XX 设备读取

Reading from FTDI D2XX device using Python3 on Ubuntu 20.04

我正在使用 FTDI 设备,该设备具有适用于 Windows 的本机软件,但没有适用于 Linux 的软件。我正在尝试使用 pylibftdi 从设备读取数据。我想翻译设备制造商提供的 C# code,据称可以工作(不清楚是否属实),但没有成功。到目前为止,我已经完成了以下工作:

  1. 基于这些 instructions 安装了 Linux D2XX 驱动程序。安装成功。

  2. 按照说明 here and here 使 FTDI 设备能够连接到 Linux 系统。

  3. 将 FTDI 设备插入 Linux 系统 USB 端口后:

$ lsusb
Bus 006 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 005 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 003: ID 046d:c52b Logitech, Inc. Unifying Receiver
Bus 001 Device 002: ID 04f2:0833 Chicony Electronics Co., Ltd KU-0833 Keyboard
**Bus 001 Device 006: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC**
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

粗体设备 (Bus 001 Device 006: ID 0403:6001) 是我想要阅读的设备。

  1. 然后安装 pylibftdi 并验证设备可通过 pylibftdi API:
  2. 读取
$ python3
Python 3.9.5 (default, Jun  4 2021, 12:28:51) 
[GCC 7.5.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys, pylibftdi as ftdi
>>> print(ftdi.Driver().list_devices())
[('FTDI', 'Cognionics Quick-20 20CH 1706Q20N', 'AI2SUN90')]
  1. 很明显,设备已连接并被识别。但是,当我尝试从设备读取时,我收到空数组:
>>> d = ftdi.Device()
>>> vars(d)
{'_opened': True, 'driver': <pylibftdi.driver.Driver object at 0x7fd819320910>, 'fdll': <CDLL 'libftdi.so.1', handle 557bc3ca6560 at 0x7fd8190aee80>, 'device_id': None, 'mode': 'b', 'encoding': 'latin1', 'encoder': <encodings.latin_1.IncrementalEncoder object at 0x7fd819320a60>, 'decoder': <encodings.latin_1.IncrementalDecoder object at 0x7fd8190aefd0>, '_baudrate': 9600, 'interface_select': None, 'device_index': 0, 'list_index': None, 'ctx': <ctypes.c_char_Array_1024 object at 0x7fd819342c40>}
>>> d.read(100)
b''
>>> d.read(100)
b''
>>> d.read(100)
b''
  1. 这个 C# code(由制造商提供)据称有效,但我无法对其进行测试。看起来最简单的方法是将其转换为 python 但即使这样也具有挑战性,因为我不知道如何复制正在使用的常量和 ftdi 函数调用。提供的 C# 代码是:
UInt32 ftDevCount = 0;
ftStatus = ftDev.GetNumberOfDevices(ref ftDevCount);

ftdiDeviceList = new FTDI.FT_DEVICE_INFO_NODE[ftDevCount];
ftStatus = ftDev.GetDeviceList(ftdiDeviceList);
String[] deviceNames = new String[ftDevCount];

for (int c = 0; c < ftDevCount; c++)
{
deviceNames[c] = ftdiDeviceList[c].Description.ToString();
}

Connecting to a device and configuring the serial port settings

if (ftDev.OpenBySerialNumber(ftdiDeviceList[devID].SerialNumber) == FTDI.FT_STATUS.FT_OK)
{    ftDev.SetFlowControl(FTDI.FT_FLOW_CONTROL.FT_FLOW_RTS_CTS, 0x11, 0x13);
    ftDev.SetDataCharacteristics(FTDI.FT_DATA_BITS.FT_BITS_8, FTDI.FT_STOP_BITS.FT_STOP_BITS_1, FTDI.FT_PARITY.FT_PARITY_NONE);
    ftDev.SetLatency(2);
    ftDev.SetBaudRate((uint)3000000);

    connectedName = ftdiDeviceList[devID].Description.ToString();

    return true;
}
else
{
    return false; //failed to open!

}

public byte ReadByte()
{
    UInt32 bytesRead = 0;
    byte[] t_data = new byte[1];
    ftDev.Read(t_data, 1, ref bytesRead);
    return t_data[0];
}

public void WriteByte(byte dat)
{
    UInt32 bytesWritten = 0;
    byte[] data = new byte[1];
    data[0] = dat;
    ftDev.Write(data, 1, ref bytesWritten);
}

//wait for sync byte 0xFF
while (byteInterface.ReadByte() != 255) {};

//read packet counter
int packetCount = byteInterface.ReadByte();

//read the 20 EEG channels
int NumEEG = 20;
for (int c = 0; c < NumEEG; c++)
{
    msb =  byteInterface.ReadByte();
    lsb2 = byteInterface.ReadByte();
    lsb1 = byteInterface.ReadByte();

    int tempEEG = (msb << 24) | (lsb2 << 17) | (lsb1 << 10);
}

int NumACC = 3;
//read the 3 ACC channels
for (int c = 0; c < NumACC; c++)
{
    msb =  byteInterface.ReadByte();
    lsb2 = byteInterface.ReadByte();
    lsb1 = byteInterface.ReadByte();

    int tempACC = (msb << 24) | (lsb2 << 17) | (lsb1 << 10);
}


//read packet tail
int impStatus = byteInterface.ReadByte();

//read battery voltage
int batteryByte = byteInterface.ReadByte();

//read trigger
int trigger = (byteInterface.ReadByte()<<8) + byteInterface.ReadByte();
  1. 根据 pylibftdi github 存储库中的文档,我可以找到一些包装器 function calls as well as a few constants,但我不知道如何只打开设置片段,例如:
ftDev.SetFlowControl(FTDI.FT_FLOW_CONTROL.FT_FLOW_RTS_CTS, 0x11, 0x13);
ftDev.SetDataCharacteristics(FTDI.FT_DATA_BITS.FT_BITS_8, FTDI.FT_STOP_BITS.FT_STOP_BITS_1, FTDI.FT_PARITY.FT_PARITY_NONE);
ftDev.SetLatency(2);
ftDev.SetBaudRate((uint)3000000);

进入 python 中的内容。我想我可以使用 d.baudrate = 3000000 重置波特率,我可以使用 d.ftdi_fn.ftdi_set_latency_timer(2) 更改延迟计时器,但我不知道如何设置数据特性,常量的含义(FTDI.FT_DATA_BITS.FT_BITS_8,等),以及如何设置与 C# 代码相同的流量控制。

  1. 其他 SO 帖子也提到了找到的 D2XX 程序员指南 here 但没有找到将其应用于此问题的方法

如有任何建议,我们将不胜感激。

作为对评论的回应和后续答复:

我无法根据个人经验证实 D2XX 比 VCP 更可靠的说法,而第二种说法只是部分正确:例如,可以在大多数情况下使用 VID:PID 组合 IIRC.

我强烈建议坚持使用更简单的 VCP + pyserial 解决方案。但是如果你真的想(或需要)使用pylibftdi,你可以看看https://github.com/codedstructure/pylibftdi/blob/4662ebe069eefd5a89709d4165e3be808cad636c/docs/advanced_usage.rst - it describes how to access none exposed functionality directly. The naming is slightly different e.g. ftdi_setflowctrl instead of SetFlowControl but you will figure it out. Just check https://www.intra2net.com/en/developer/libftdi/documentation/ftdi_8c.html .