Последовательный модуль Python не может настроить порт

Я использую Python3.5.1 последовательный модуль. Когда я открываю порт, он выходит из строя с ошибкой ОС 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 с пакетом обновления 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)

Я убедился, что драйвер установлен правильно, но я получаю эту ошибку с двумя разными последовательными преобразователями. Поэтому я считаю, что проблема не связана с оборудованием или драйверами.


person Valarauca    schedule 04.05.2016    source источник
comment
Добавьте код для печати значений b, p, s и bs в случае сбоя. Вероятно, это неподдерживаемая скорость передачи данных, 300600 между 300 и 1200 выглядит очень странно.   -  person unwind    schedule 04.05.2016
comment
Добавьте выходные данные отладки, которые вы предлагаете. Сбой происходит на первой скорости передачи, а не на ошибочной.   -  person Valarauca    schedule 04.05.2016
comment
Это хорошо. Вы также можете попробовать переупорядочить другие параметры, сделав их наиболее вероятными первыми, то есть 8 битов данных, без четности и 1 стоповый бит. Возможно, в наши дни сварливые драйверы не реализуют 2 стоповых бита. По крайней мере, это возможно и даст больше информации. Если старый добрый 8N1 не работает, это более удивительно, чем сбой эзотерических настроек я считаю.   -  person unwind    schedule 04.05.2016
comment
Похоже, что скорость передачи ‹100 рассматривается как ошибка конфигурации в Windows.   -  person Valarauca    schedule 04.05.2016
comment
Вы ссылаетесь на неправильные документы. Вы используете pySerial.   -  person Eryk Sun    schedule 05.05.2016


Ответы (2)


Вы говорите, что вам «нужно просто знать настройки устройства, но это не идеальный мир». Но Windows позволяет запрашивать свойства устройства связи через GetCommProperties. pySerial не поддерживает это, но вы можете использовать ctypes для прямого вызова этой функции.

Далее определяется функция get_comm_properties для запроса устанавливаемых свойств коммуникационного порта. Он принимает либо существующий дескриптор устройства (например, атрибут _handle порта pySerial), либо имя устройства 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
person Eryk Sun    schedule 05.05.2016

Скорость передачи ‹100 рассматривается как ошибка конфигурации в Windows 7. Таким образом, запуск цикла на скорости 50,75 бод приведет к ошибкам. 110 бод не возвращает ошибку.

person Valarauca    schedule 04.05.2016