Python串口模块配置端口失败

Python serial module failing to configure port

我正在使用 Python3.5.1 serial module。当我打开一个端口时,它失败并显示 OS 错误 22(Windows 错误 87),它表示 failure to configure port, one of the arguments in OPEN system call were incorrect, or malformed.

我的代码使用串行设置循环 - 向设备发送错误的数据包,直到设备以(可读的)错误消息响应(因此我知道我的串行端口配置正确)。是的,我应该只知道设备的设置,但这不是一个完美的世界。

  import serial
  import time
  baud_rate = [50,75,110,134,150,200,300600,1200,1800,2400,4800,9600,19200,38400,57600,115200]
  parity = [serial.PARITY_ODD,serial.PARITY_EVEN,serial.PARITY_NONE]
  stop_bits = [serial.STOPBITS_TWO, serial.STOPBITS_ONE]
  bytesize = [serial.SEVENBITS,serial.EIGHTBITS]
  timeout = 5000
  for b in baud_rate:
     for p in parity:
         for s in stop_bits:
             for bs in bytesize:
                 ser = serial.Serial(port='COM3',baudrate=b,parity=p,stopbits=s,bytesize=bs)
                 try:
                     if ser.isOpen():
                         ser.write(b'TEST')
                         ser.reset_output_buffer()
                         time.sleep(1)
                         out = ser.read(3)
                         if out[0] == 64 and out[1] == 67 and out[2] == 32:
                             print("dumping settings")
                             print(ser.get_settings())
                     else:
                         ser.close()
                 except SerialException:
                    print("Serial Exception occured.")
                    pass

问题发生在windows 7 x64 service pack 1下。python版本是3.5。 cmd.exe 实例是 运行 作为管理员。

当我 运行 脚本

时,我非常确定 COM3 存在
 import serial.tools.list_ports
 ports = list(serial.tools.list_ports.comports())
 for p in ports:
     print(p)

我收到输出:

 >python list_serial.py
 COM3 - Prolific USB-to-Serial Comm Port (COM3)

所以我相信端口 URL/URI (idfk) 是正确的。

完整错误文本:

 Traceback (most recent call last):
   File "serial_reader.py", line 13, in <module>
     ser = serial.Serial(port='COM3',baudrate=b,parity=p,stopbits=s,bytesize=bs)
   File "C:\Users\FA1\AppData\Local\Programs\Python\Python35-32\lib\site-packages\serial\serialwin32.py", line 31, in __init__
     SerialBase.__init__(self, *args, **kwargs)
   File "C:\Users\FA1\AppData\Local\Programs\Python\Python35-32\lib\site-packages\serial\serialutil.py", line 180, in __init__
     self.open()
   File "C:\Users\FA1\AppData\Local\Programs\Python\Python35-32\lib\site-packages\serial\serialwin32.py", line 78, in open
     self._reconfigure_port()
   File "C:\Users\FA1\AppData\Local\Programs\Python\Python35-32\lib\site-packages\serial\serialwin32.py", line 220, in _reconfigure_port
     raise SerialException("Cannot configure port, something went wrong. Original message: %r" % ctypes.WinError())
 serial.serialutil.SerialException: Cannot configure port, something went wrong. Original message: OSError(22, 'The parameter is incorrect.', None, 87)

我已确保驱动程序已正确安装,但我在使用 2 个不同的串行转换器时收到此错误。所以我认为问题与硬件或驱动程序无关。

波特率 <100 在 Windows 7 中被视为配置错误。因此,以 50,75 波特开始循环都会产生错误。 110 波特不会 return 错误。

你说你"should just know the device's settings but this isn't a prefect world"。但是Windows does allow querying communication device properties via GetCommProperties. pySerial 似乎不支持这个,但是你可以使用ctypes直接调用这个函数。

下面定义了一个get_comm_properties函数来查询comm口的可设置属性。它接受现有的设备句柄(例如 pySerial 端口的 _handle 属性)或 DOS 设备名称(例如 COM1)或 WinAPI 设备名称(例如 \.\COM1)。

import collections
import ctypes
from ctypes import wintypes

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

GENERIC_READ  = 0x80000000
GENERIC_WRITE = 0x40000000
OPEN_EXISTING = 3

INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value
ERROR_FILE_NOT_FOUND = 0x0002

class COMMPROP(ctypes.Structure):
    _fields_= (('wPacketLength',       wintypes.WORD),
               ('wPacketVersion',      wintypes.WORD),
               ('dwServiceMask',       wintypes.DWORD),
               ('dwReserved1',         wintypes.DWORD),
               ('dwMaxTxQueue',        wintypes.DWORD),
               ('dwMaxRxQueue',        wintypes.DWORD),
               ('dwMaxBaud',           wintypes.DWORD),
               ('dwProvSubType',       wintypes.DWORD),
               ('dwProvCapabilities',  wintypes.DWORD),
               ('dwSettableParams',    wintypes.DWORD),
               ('dwSettableBaud',      wintypes.DWORD),
               ('wSettableData',       wintypes.WORD),
               ('wSettableStopParity', wintypes.WORD),
               ('dwCurrentTxQueue',    wintypes.DWORD),
               ('dwCurrentRxQueue',    wintypes.DWORD),
               ('dwProvSpec1',         wintypes.DWORD),
               ('dwProvSpec2',         wintypes.DWORD),
               ('wcProvChar',          wintypes.WCHAR * 1))

    class _CONST:
        COMMPROP_INITIALIZED = 0xE73CF52E
        SP_SERIALCOMM = 0x00000001
        BAUD_USER = 0x10000000 # programmable baud rate
        DATABITS_16X = 0x0020 # hardware wide data path

        PROV_SUBTYPE = collections.OrderedDict([
            ('UNSPECIFIED',    0x00000000),
            ('RS232',          0x00000001),
            ('PARALLELPORT',   0x00000002),
            ('RS422',          0x00000003),
            ('RS423',          0x00000004),
            ('RS449',          0x00000005),
            ('MODEM',          0x00000006),
            ('FAX',            0x00000021),
            ('SCANNER',        0x00000022),
            ('NETWORK_BRIDGE', 0x00000100),
            ('LAT',            0x00000101),
            ('TCPIP_TELNET',   0x00000102),
            ('X25',            0x00000103),
        ])

        PROV_CAPABILITIES = collections.OrderedDict([
            ('DTRDSR',        0x0001), # data-terminal-ready / data-set-ready
            ('RTSCTS',        0x0002), # request-to-send / clear-to-send
            ('RLSD',          0x0004), # receive-line-signal-detect
            ('PARITY_CHECK',  0x0008),
            ('XONXOFF',       0x0010), # XON/XOFF flow control
            ('SETXCHAR',      0x0020), # settable XON/XOFF
            ('TOTALTIMEOUTS', 0x0040), # total (elapsed) time-outs
            ('INTTIMEOUTS',   0x0080), # interval time-outs
            ('SPECIALCHARS',  0x0100),
            ('16BITMODE',     0x0200),
        ])

        SETTABLE_PARAMS = collections.OrderedDict([
            ('PARITY',       0x0001),
            ('BAUD',         0x0002),
            ('DATABITS',     0x0004),
            ('STOPBITS',     0x0008),
            ('HANDSHAKING',  0x0010), # flow control
            ('PARITY_CHECK', 0x0020),
            ('RLSD',         0x0040), # receive-line-signal-detect
        ])

        SETTABLE_BAUD = collections.OrderedDict([
            (75,     0x00000001),
            (110,    0x00000002),
            (134.5,  0x00000004),
            (150,    0x00000008),
            (300,    0x00000010),
            (600,    0x00000020),
            (1200,   0x00000040),
            (1800,   0x00000080),
            (2400,   0x00000100),
            (4800,   0x00000200),
            (7200,   0x00000400),
            (9600,   0x00000800),
            (14400,  0x00001000),
            (19200,  0x00002000),
            (38400,  0x00004000),
            (56000,  0x00008000),
            (57600,  0x00040000),
            (115200, 0x00020000),
            (128000, 0x00010000),
        ])

        SETTABLE_DATA = collections.OrderedDict([
            (5,  0x0001), # 5 data bits
            (6,  0x0002), # 6 data bits
            (7,  0x0004), # 7 data bits
            (8,  0x0008), # 8 data bits
            (16, 0x0010), # 16 data bits
        ])

        SETTABLE_STOP = collections.OrderedDict([
            (1,   0x0001), # 1 stop bit
            (1.5, 0x0002), # 1.5 stop bits
            (2,   0x0004), # 2 stop bits
        ])

        SETTABLE_PARITY = collections.OrderedDict([
            ('NONE',  0x0100), # no parity
            ('ODD',   0x0200), # odd parity
            ('EVEN',  0x0400), # even parity
            ('MARK',  0x0800), # mark parity
            ('SPACE', 0x1000), # space parity
        ])

    @property
    def max_baud(self):
        s = self.dwMaxBaud
        m = self._CONST.SETTABLE_BAUD
        if s == self._CONST.BAUD_USER:
            return 0
        else:
            return m[s]

    @property
    def prov_subtype(self):
        s = self.dwProvSubType
        m = self._CONST.PROV_SUBTYPE
        return [x for x, c in m.items() if c & s]

    @property
    def prov_capabilities(self):
        s = self.dwProvCapabilities
        m = self._CONST.PROV_CAPABILITIES
        return [x for x, c in m.items() if c & s]

    @property
    def settable_params(self):
        s = self.dwSettableParams
        m = self._CONST.SETTABLE_PARAMS
        return [x for x, c in m.items() if c & s]

    @property
    def settable_baud(self):
        s = self.dwSettableBaud
        m = self._CONST.SETTABLE_BAUD
        return [x for x, c in m.items() if c & s]

    @property
    def user_settable_baud(self):
        return bool(self.dwSettableBaud & self._CONST.BAUD_USER)

    @property
    def settable_data(self):
        s = self.wSettableData
        m = self._CONST.SETTABLE_DATA
        return [x for x, c in m.items() if c & s]

    @property
    def wide_settable_data(self):
        return bool(self.wSettableData & self._CONST.DATABITS_16X)

    @property
    def settable_stop(self):
        s = self.wSettableStopParity
        m = self._CONST.SETTABLE_STOP
        return [x for x, c in m.items() if c & s]

    @property
    def settable_parity(self):
        s = self.wSettableStopParity
        m = self._CONST.SETTABLE_PARITY
        return [x for x, c in m.items() if c & s]


LPCOMMPROP = ctypes.POINTER(COMMPROP)

class SECURITY_ATTRIBUTES(ctypes.Structure):
    _fields_ = (('nLength',              wintypes.DWORD),
                ('lpSecurityDescriptor', wintypes.LPVOID),
                ('bInheritHandle',       wintypes.BOOL))

LPSECURITY_ATTRIBUTES = ctypes.POINTER(SECURITY_ATTRIBUTES)

kernel32.CreateFileW.restype = wintypes.HANDLE
kernel32.CreateFileW.argtypes = (
    wintypes.LPCWSTR,      # _In_     lpFileName
    wintypes.DWORD,        # _In_     dwDesiredAccess
    wintypes.DWORD,        # _In_     dwShareMode
    LPSECURITY_ATTRIBUTES, # _In_opt_ lpSecurityAttributes
    wintypes.DWORD,        # _In_     dwCreationDisposition
    wintypes.DWORD,        # _In_     dwFlagsAndAttributes
    wintypes.HANDLE)       # _In_opt_ hTemplateFile

kernel32.CloseHandle.argtypes = (wintypes.HANDLE,)

kernel32.GetCommProperties.argtypes = (
    wintypes.HANDLE, # _In_  hFile
    LPCOMMPROP)      # _Out_ lpCommProp

def get_comm_properties(handle_or_port):
    if isinstance(handle_or_port, str):
        handle = kernel32.CreateFileW(
                        handle_or_port,
                        GENERIC_READ | GENERIC_WRITE,
                        0,    # exclusive access
                        None, # default security
                        OPEN_EXISTING,
                        0,
                        None)
        if handle == INVALID_HANDLE_VALUE:
            raise ctypes.WinError(ctypes.get_last_error())
        close_handle = True
    else:
        handle = handle_or_port
        close_handle = False
    try:
        prop = COMMPROP()
        if not kernel32.GetCommProperties(handle, ctypes.byref(prop)):
            raise ctypes.WinError(ctypes.get_last_error())
    finally:
        if close_handle:
            kernel32.CloseHandle(handle)
    return prop

示例:

if __name__ == '__main__':
    for i in range(1, 10):
        port = r'\.\COM%d' % i
        try:
            prop = get_comm_properties(port)
        except WindowsError as e:
            if e.winerror == ERROR_FILE_NOT_FOUND:
                continue
        print('%s properties' % port)
        x = prop.dwMaxTxQueue if prop.dwMaxTxQueue else 'no limit'
        print('\tMax output buffer size: %s' % x)
        x = prop.dwMaxRxQueue if prop.dwMaxRxQueue else 'no limit'
        print('\tMax input buffer size: %s' % x)
        x = prop.dwCurrentTxQueue if prop.dwCurrentTxQueue else 'unavailable'
        print('\tCurrent output buffer size: %s' % x)
        x = prop.dwCurrentRxQueue if prop.dwCurrentRxQueue else 'unavailable'
        print('\tCurrent input buffer size: %s' % x)
        x = prop.max_baud if prop.max_baud else 'user programmable'
        print('\tMax baud rate: %s' % x)
        print('\tProvider subtypes:\n\t\t%s' %
                    '\n\t\t'.join(prop.prov_subtype))
        print('\tProvider capabilities:\n\t\t%s' %
                    '\n\t\t'.join(prop.prov_capabilities))
        print('\tSettable parameters:\n\t\t%s' %
                    '\n\t\t'.join(prop.settable_params))
        print('\tSettable baud rates:\n\t\t%s' %
                    '\n\t\t'.join([str(x) for x in prop.settable_baud]))
        print('\tSettable user baud rates: %s' %
                    prop.user_settable_baud)
        print('\tSettable data bits:\n\t\t%s' %
                    '\n\t\t'.join([str(x) for x in prop.settable_data]))
        print('\tSettable wide data bits: %s' %
                    prop.wide_settable_data)
        print('\tSettable stop bits:\n\t\t%s' %
                    '\n\t\t'.join([str(x) for x in prop.settable_stop]))
        print('\tSettable parity:\n\t\t%s' %
                    '\n\t\t'.join(prop.settable_parity))

输出:

\.\COM1 properties
        Max output buffer size: no limit
        Max input buffer size: no limit
        Current output buffer size: unavailable
        Current input buffer size: 4096
        Max baud rate: user programmable
        Provider subtypes:
                RS232
                RS422
                RS449
                FAX
                LAT
                X25
        Provider capabilities:
                DTRDSR
                RTSCTS
                RLSD
                PARITY_CHECK
                XONXOFF
                SETXCHAR
                TOTALTIMEOUTS
                INTTIMEOUTS
        Settable parameters:
                PARITY
                BAUD
                DATABITS
                STOPBITS
                HANDSHAKING
                PARITY_CHECK
                RLSD
        Settable baud rates:
                75
                110
                134.5
                150
                300
                600
                1200
                1800
                2400
                4800
                7200
                9600
                14400
                19200
                38400
                56000
                57600
                115200
        Settable user baud rates: True
        Settable data bits:
                5
                6
                7
                8
        Settable wide data bits: False
        Settable stop bits:
                1
                1.5
                2
        Settable parity:
                NONE
                ODD
                EVEN
                MARK
                SPACE