【问题标题】:how to get FTDI FT4232 chip location information or serial number in windows with python?如何使用 python 在 windows 中获取 FTDI FT4232 芯片位置信息或序列号?
【发布时间】:2020-04-28 06:28:14
【问题描述】:

我正在使用 FTDI FT4232 芯片来扩展 Windows 10 上的四个 COM 端口。

这些扩展的 COM 端口链接到四个不同的设备。因此,如果我想通过这些 COM 端口与其他设备通信,我需要知道要调用哪些端口。

我正在用 python 编码,这是我用 pyserial 得到的。

>python -m serial.tools.list_ports -v
COM3
    desc: USB Serial Port (COM3)
    hwid: USB VID:PID=0403:6011 SER=5
COM4
    desc: USB Serial Port (COM4)
    hwid: USB VID:PID=0403:6011 SER=5
COM5
    desc: USB Serial Port (COM5)
    hwid: USB VID:PID=0403:6011 SER=5
COM6
    desc: USB Serial Port (COM6)
    hwid: USB VID:PID=0403:6011 SER=5
4 ports found

但是在Linux中,pyserial可以获取位置信息,可以用来区分这四个端口。

~$:python3 -m serial.tools.list_ports -v
/dev/ttyUSB0        
    desc: Quad RS232-HS
    hwid: USB VID:PID=0403:6011 LOCATION=1-2.1:1.0
/dev/ttyUSB1        
    desc: Quad RS232-HS
    hwid: USB VID:PID=0403:6011 LOCATION=1-2.1:1.1
/dev/ttyUSB2        
    desc: Quad RS232-HS
    hwid: USB VID:PID=0403:6011 LOCATION=1-2.1:1.2
/dev/ttyUSB3        
    desc: Quad RS232-HS
    hwid: USB VID:PID=0403:6011 LOCATION=1-2.1:1.3
4 ports found

有人知道这个问题吗?

临时解决方案: 修改 list_ports_windows.py 如下:

    elif szHardwareID_str.startswith('FTDIBUS'):
        m = re.search(r'VID_([0-9a-f]{4})\+PID_([0-9a-f]{4})(\+(\w*))&(\w*)&(\w*)&(\w*)&(\w*)?', szHardwareID_str, re.I)
        if m:
            info.vid = int(m.group(1), 16)
            info.pid = int(m.group(2), 16)
            if m.group(8):
                info.serial_number =  int(m.group(8),16)
        # USB location is hidden by FDTI driver :(
        info.hwid = info.usb_info()

然后你就可以用vid+pid+serial_number调用你想要的端口了。

【问题讨论】:

    标签: pyserial ftdi


    【解决方案1】:

    其实serial.tools.list_ports.comports返回的ListPortInfo中有一个location字段。但是,list_ports (list_ports_windows.py) 的 windows 实现似乎无法正确恢复此信息。我使用日志记录模块通过将以下行添加到我的 pyserial(安装在 win10 64,python3.8 上)来对此进行调查。添加

    logging.debug("szHardwareID_str: %s" % szHardwareID_str) #added this lin
    

    之后

    # stringify
    szHardwareID_str = szHardwareID.value
    

    为我屈服:

    DEBUG:root:szHardwareID_str: ACPI\PNP0501\0
    DEBUG:root:szHardwareID_str: FTDIBUS\VID_0403+PID_6010+6&192CD50C&0&13&2\0000
    DEBUG:root:szHardwareID_str: FTDIBUS\VID_0403+PID_6010+6&192CD50C&0&13&1\0000
    

    So on 可以看到该位置实际上在这个字符串中,但是后面的字符串分析未能正确提取此信息。即实现的搜索与 locationID 不匹配:

    m = re.search(r'VID_([0-9a-f]{4})(&PID_([0-9a-f]{4}))?(&MI_(\d{2}))?(\\(\w+))?', szHardwareID_str, re.I)
    

    长话短说:据我所知,您要么修改 pyserial(通过创建补丁+拉取请求在本地或全局范围内),要么使用 pyserial.tools.list_ports 模块手动“提取”信息。例如:

    import ctypes
    import serial
    import logging
    
    import serial.tools.list_ports_windows as lp
    
    logging.basicConfig(level=logging.DEBUG)
    
    ################copied from list_ports_windows.py
    GUIDs = (lp.GUID * 8)()  # so far only seen one used, so hope 8 are enough...
    guids_size = lp.DWORD()
    if not lp.SetupDiClassGuidsFromName(
            "Ports",
            GUIDs,
            ctypes.sizeof(GUIDs),
            ctypes.byref(guids_size)):
        raise ctypes.WinError()
    
    # repeat for all possible GUIDs
    for index in range(guids_size.value):
        bInterfaceNumber = None
        g_hdi = lp.SetupDiGetClassDevs(
            ctypes.byref(GUIDs[index]),
            None,
            lp.NULL,
            lp.DIGCF_PRESENT)  # was DIGCF_PRESENT|DIGCF_DEVICEINTERFACE which misses CDC ports
    
        devinfo = lp.SP_DEVINFO_DATA()
        devinfo.cbSize = ctypes.sizeof(devinfo)
        index = 0
        while lp.SetupDiEnumDeviceInfo(g_hdi, index, ctypes.byref(devinfo)):
            index += 1
    
            # get the real com port name
            hkey = lp.SetupDiOpenDevRegKey(
                g_hdi,
                ctypes.byref(devinfo),
                lp.DICS_FLAG_GLOBAL,
                0,
                lp.DIREG_DEV,  # DIREG_DRV for SW info
                lp.KEY_READ)
            port_name_buffer = ctypes.create_unicode_buffer(250)
            port_name_length = lp.ULONG(ctypes.sizeof(port_name_buffer))
            lp.RegQueryValueEx(
                hkey,
                "PortName",
                None,
                None,
                ctypes.byref(port_name_buffer),
                ctypes.byref(port_name_length))
            lp.RegCloseKey(hkey)
    
            # unfortunately does this method also include parallel ports.
            # we could check for names starting with COM or just exclude LPT
            # and hope that other "unknown" names are serial ports...
            if port_name_buffer.value.startswith('LPT'):
                continue
    
            # hardware ID
            szHardwareID = ctypes.create_unicode_buffer(250)
            # try to get ID that includes serial number
            if not lp.SetupDiGetDeviceInstanceId(
                    g_hdi,
                    ctypes.byref(devinfo),
                    #~ ctypes.byref(szHardwareID),
                    szHardwareID,
                    ctypes.sizeof(szHardwareID) - 1,
                    None):
                # fall back to more generic hardware ID if that would fail
                if not lp.SetupDiGetDeviceRegistryProperty(
                        g_hdi,
                        ctypes.byref(devinfo),
                        lp.SPDRP_HARDWAREID,
                        None,
                        ctypes.byref(szHardwareID),
                        ctypes.sizeof(szHardwareID) - 1,
                        None):
                    # Ignore ERROR_INSUFFICIENT_BUFFER
                    if ctypes.GetLastError() != lp.ERROR_INSUFFICIENT_BUFFER:
                        raise ctypes.WinError()
            # stringify
            szHardwareID_str = szHardwareID.value
    
            print(szHardwareID_str)
    

    但是,恕我直言,一般解决方案(即补丁)将是最好的方法。也许您可以在官方 pyserial 存储库中打开一个 ISSUE。

    【讨论】:

    • 谢谢你,克里斯蒂安。您为我找到解决方案指明了方向。我深入研究了 list_port_windows.py 的代码,发现 FTDI 芯片是单独处理的。并且作者已经清楚地指出了位置信息被 FTDI 隐藏了。所以我修改了序列号的解析方式。我从硬件 id 字符串中获取了硬件信息的最后一个字符并将其用作序列号。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-23
    相关资源
    最近更新 更多