Python pylibftdi Device.read跳过一些字节

Python pylibftdi Device.read跳过一些字节,python,fpga,ftdi,Python,Fpga,Ftdi,我有一个通过FT2232H在USB总线上传输数据的FPGA,我观察到大约10%的数据必须被丢弃,因为帧中缺少一些字节。以下是技术细节: FPGA是Artix 7。每9毫秒就有一批4002字节的数据准备就绪。因此,计算出来的数据为444667字节/秒 我的笔记本电脑在Ubuntu 18.04LTS上运行python 3.7(来自anaconda) FPGA/FT2232H通过以下初始化线路打开: SYNCFF=0x40 SIO_RTS_CTS_HS=(0x1FT2232H具有容量为~4 kbi

我有一个通过FT2232H在USB总线上传输数据的FPGA,我观察到大约10%的数据必须被丢弃,因为帧中缺少一些字节。以下是技术细节:

  • FPGA是Artix 7。每9毫秒就有一批4002字节的数据准备就绪。因此,计算出来的数据为444667字节/秒
  • 我的笔记本电脑在Ubuntu 18.04LTS上运行python 3.7(来自anaconda)
  • FPGA/FT2232H通过以下初始化线路打开:
SYNCFF=0x40

SIO_RTS_CTS_HS=(0x1FT2232H具有容量为~4 kbits的内部FIFO缓冲区。您可能受到这些缓冲区的限制。不确定pylibftdi如何处理它们,但如果您可以使用VCP驱动程序,则可能使用另一种方法。这允许您将FT2232H作为标准组件进行寻址,例如通过pyserial

我的一个项目中的一些摘录,其实际工作波特率大于12 Mbps(UART限制为12 Mbps,但例如fast opto可以达到约25 Mbps):

我尝试c&p一个最小的例子。它是未经测试的,所以请原谅我,如果它不是开箱即用的。要使这个工作,实际上需要确保VCP而不是D2XX驱动程序加载

附言:实际上,在扫描我的文件时,我意识到pylibftdi方法应该和我使用的“decorator”类一样有效,以防加载D2XX驱动程序:

try:    import pylibftdi
except: pylibftdi = None


class pylibftdi_device:
    def __init__(self,speed):
        self.dev = pylibftdi.Device(interface_select=2)
        self.dev.baudrate = speed
        self.buf = b''

    def write(self, data):
        self.dev.write(data)

    def read(self, bytecount):
        while bytecount > len(self.buf):
            self._read()

        ret      = self.buf[:bytecount]
        self.buf = self.buf[bytecount:]
        return ret

    def flushInput(self):
        self.dev.flush_input()#FT_PURGE_RX
        self.buf = b''

    def _read(self):
        self.buf += self.dev.read(2048)

    @property
    def in_waiting(self):
        self._read()
        return len(self.buf)

    def close(self):
        self.dev.close()


def find_device_UART(baudrate=12000000,index=1, search_string="USB VID:PID=0403:6010 SER="):
    if pylibftdi:
        return pylibftdi_device(baudrate),"pylibftdi_device"
    try:
        ports = [x.device for x in serial.tools.list_ports.comports() if search_string in x.hwid]
        module_logger.info(str(ports))
        if len(ports) == 0:
            return None,"no device found"
        else:
            ser = serial.Serial(ports[index],baudrate)
            return ser,"found device %s %d"%(ser.name,ser.baudrate)
    except serial.SerialException as e:
        return None,"error during device detection - \n"+str(e)

因此,与您的示例的主要区别在于,recv缓冲区被更频繁地读取,并放入一个缓冲区中,然后在稍后搜索数据包。也许这一切对您的应用程序来说是一个完全的过度杀伤力,您只需进行较小的读取调用,以确保缓冲区永不溢出。

是FPGA发送二进制数据,但您e告诉
设备
处于文本模式?是的,这一点很好。FPGA发送二进制数据,由设备在文本模式下读取,然后转换为
字节
进行处理。我需要更新该数据,但我可以确认,当设备设置为二进制模式时,我遇到了相同的问题。很抱歉造成混淆!FT2232H具有4kB内部缓冲区。你可能会受到它们的限制。对于我的应用程序,我使用pyserial和一个子进程来收集数据,并通过管道将数据发送到主进程,以解决GIL问题。管道本身在windows下有一个~8kb的内部缓冲区,因此有时还需要对其进行调整。不确定plibftdi在内部做什么以及是否切换简单的VCP将是您的一个选择。啊,“偏差”是正常的,因为我们谈论的是USB。它的行为基本上就像一个“面向共享数据包的管道”,具有松散的时间保证。您可以通过更改驱动程序延迟设置来减少延迟(至少在windows下)但是我从来没有使用过它。啊!我不知道所有这些。谢谢你提供的信息。你有我可以用来开始与人打交道的示例代码的链接吗?我从来没有使用过这个软件包,但是如果我能够绕过GIL并最终能够停止丢弃字节,那就太好了!听起来像是一个计划,非常感谢示例代码!Do您预见到这两种方法在性能上的差异吗?根据我的经验,主要的限制是FT2232H的内部缓冲区相对较小,并且驱动程序没有缓冲更高的缓冲量。因此频繁和定期的读取非常重要。因此,由于pylibftdi已经启动并运行,我很可能会坚持使用它。好吗我将在下周尝试,看看我能减少多少帧丢失。我尝试了几种不同的方法,但结果不一。首先,我将代码的读取部分移动到一个单独的进程中,该进程通过管道进行通信。这个进程唯一做的事就是读取FPGA。其次,我尝试频繁地读取FPGA,以便缩短持续时间并将其放入缓冲区以供稍后处理->这没有帮助,很短的读取实际上可能会使情况变得更糟。因此,我的下一个选择是给pyserial一个机会,但我怀疑pylibftdi是垃圾:我一定忽略了一些内容。但并非所有内容都不好:代码现在看起来更干净了
import traceback
import serial
import serial.tools.list_ports
import multiprocessing
import multiprocessing.connection

def IO_proc(cntr_pipe, data_pipe):
    try:
        search_str="USB VID:PID=0403:6010 SER="
        ports     = [x.device for x in serial.tools.list_ports.comports() if search_str in x.hwid]
        baud_rate = 12000000 #only matters for uart and not for fast opto or fifo mode
        ser       = serial.Serial(port, baud_rate)

    while not cntr_pipe.closed:
        time.sleep(0)
        in_data = ser.read(ser.inWaiting())

        [...do some pattern matching, package identification etc...]

        data_pipe.send_bytes(in_data)

        except EOFError:
            ret_code = 2
        except Exception as e:
            cntr_pipe.send(traceback.format_exc())
            cntr_pipe.close()
            ret_code = 4
        finally:
            cntr_pipe.close()
            ser.close()

multiprocessing.connection.BUFSIZE = 2 ** 20 #only required for windows
child_cntr, parent_cntr = multiprocessing.Pipe()
child_data, parent_data = multiprocessing.Pipe()
process                 = multiprocessing.Process(target = IO_proc, args=(child_cntr, child_data))

#called frequently
def update():
    if child_cntr.poll():
        raise Exception("error",child_cntr.recv())

        buf = bytes()

        while parent_data.poll():
            buf += parent_data.recv_bytes()

    [...do something fancy...]
try:    import pylibftdi
except: pylibftdi = None


class pylibftdi_device:
    def __init__(self,speed):
        self.dev = pylibftdi.Device(interface_select=2)
        self.dev.baudrate = speed
        self.buf = b''

    def write(self, data):
        self.dev.write(data)

    def read(self, bytecount):
        while bytecount > len(self.buf):
            self._read()

        ret      = self.buf[:bytecount]
        self.buf = self.buf[bytecount:]
        return ret

    def flushInput(self):
        self.dev.flush_input()#FT_PURGE_RX
        self.buf = b''

    def _read(self):
        self.buf += self.dev.read(2048)

    @property
    def in_waiting(self):
        self._read()
        return len(self.buf)

    def close(self):
        self.dev.close()


def find_device_UART(baudrate=12000000,index=1, search_string="USB VID:PID=0403:6010 SER="):
    if pylibftdi:
        return pylibftdi_device(baudrate),"pylibftdi_device"
    try:
        ports = [x.device for x in serial.tools.list_ports.comports() if search_string in x.hwid]
        module_logger.info(str(ports))
        if len(ports) == 0:
            return None,"no device found"
        else:
            ser = serial.Serial(ports[index],baudrate)
            return ser,"found device %s %d"%(ser.name,ser.baudrate)
    except serial.SerialException as e:
        return None,"error during device detection - \n"+str(e)