kopia lustrzana https://github.com/jacklinquan/usbserial4a
rodzic
c0f45b73f2
commit
066901d5bb
|
@ -90,6 +90,7 @@ class MainApp(App):
|
||||||
self.device_name_list = []
|
self.device_name_list = []
|
||||||
self.serial_port = None
|
self.serial_port = None
|
||||||
self.read_thread = None
|
self.read_thread = None
|
||||||
|
self.port_thread_lock = threading.Lock()
|
||||||
super(MainApp, self).__init__(*args, **kwargs)
|
super(MainApp, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
|
@ -97,7 +98,8 @@ class MainApp(App):
|
||||||
|
|
||||||
def on_stop(self):
|
def on_stop(self):
|
||||||
if self.serial_port:
|
if self.serial_port:
|
||||||
self.serial_port.close()
|
with self.port_thread_lock:
|
||||||
|
self.serial_port.close()
|
||||||
|
|
||||||
def on_btn_scan_release(self):
|
def on_btn_scan_release(self):
|
||||||
self.uiDict['box_list'].clear_widgets()
|
self.uiDict['box_list'].clear_widgets()
|
||||||
|
@ -106,7 +108,8 @@ class MainApp(App):
|
||||||
if platform == 'android':
|
if platform == 'android':
|
||||||
usb_device_list = usb.get_usb_device_list()
|
usb_device_list = usb.get_usb_device_list()
|
||||||
self.device_name_list = [
|
self.device_name_list = [
|
||||||
device.getDeviceName() for device in usb_device_list]
|
device.getDeviceName() for device in usb_device_list
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
usb_device_list = list_ports.comports()
|
usb_device_list = list_ports.comports()
|
||||||
self.device_name_list = [port.device for port in usb_device_list]
|
self.device_name_list = [port.device for port in usb_device_list]
|
||||||
|
@ -124,7 +127,8 @@ class MainApp(App):
|
||||||
device = usb.get_usb_device(device_name)
|
device = usb.get_usb_device(device_name)
|
||||||
if not device:
|
if not device:
|
||||||
raise SerialException(
|
raise SerialException(
|
||||||
"Device {} not present!".format(device_name))
|
"Device {} not present!".format(device_name)
|
||||||
|
)
|
||||||
if not usb.has_usb_permission(device):
|
if not usb.has_usb_permission(device):
|
||||||
usb.request_usb_permission(device)
|
usb.request_usb_permission(device)
|
||||||
return
|
return
|
||||||
|
@ -134,7 +138,8 @@ class MainApp(App):
|
||||||
8,
|
8,
|
||||||
'N',
|
'N',
|
||||||
1,
|
1,
|
||||||
timeout=1)
|
timeout=1
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.serial_port = Serial(
|
self.serial_port = Serial(
|
||||||
device_name,
|
device_name,
|
||||||
|
@ -142,7 +147,8 @@ class MainApp(App):
|
||||||
8,
|
8,
|
||||||
'N',
|
'N',
|
||||||
1,
|
1,
|
||||||
timeout=1)
|
timeout=1
|
||||||
|
)
|
||||||
|
|
||||||
if self.serial_port.is_open and not self.read_thread:
|
if self.serial_port.is_open and not self.read_thread:
|
||||||
self.read_thread = threading.Thread(target = self.read_msg_thread)
|
self.read_thread = threading.Thread(target = self.read_msg_thread)
|
||||||
|
@ -156,24 +162,30 @@ class MainApp(App):
|
||||||
data = bytes(self.uiDict['txtInput_write'].text + '\n')
|
data = bytes(self.uiDict['txtInput_write'].text + '\n')
|
||||||
else:
|
else:
|
||||||
data = bytes(
|
data = bytes(
|
||||||
(self.uiDict['txtInput_write'].text + '\n'), 'utf8')
|
(self.uiDict['txtInput_write'].text + '\n'),
|
||||||
|
'utf8'
|
||||||
|
)
|
||||||
self.serial_port.write(data)
|
self.serial_port.write(data)
|
||||||
self.uiDict['txtInput_read'].text += '[Sent]{}\n'.format(
|
self.uiDict['txtInput_read'].text += '[Sent]{}\n'.format(
|
||||||
self.uiDict['txtInput_write'].text)
|
self.uiDict['txtInput_write'].text
|
||||||
|
)
|
||||||
self.uiDict['txtInput_write'].text = ''
|
self.uiDict['txtInput_write'].text = ''
|
||||||
|
|
||||||
def read_msg_thread(self):
|
def read_msg_thread(self):
|
||||||
while True:
|
while True:
|
||||||
if not self.serial_port.is_open:
|
|
||||||
break
|
|
||||||
try:
|
try:
|
||||||
received_msg = self.serial_port.read()
|
with self.port_thread_lock:
|
||||||
|
if not self.serial_port.is_open:
|
||||||
|
break
|
||||||
|
received_msg = self.serial_port.read(
|
||||||
|
self.serial_port.in_waiting
|
||||||
|
)
|
||||||
if received_msg:
|
if received_msg:
|
||||||
msg = bytes(received_msg).decode('utf8')
|
msg = bytes(received_msg).decode('utf8')
|
||||||
self.display_received_msg(msg)
|
self.display_received_msg(msg)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
raise ex
|
raise ex
|
||||||
|
|
||||||
@mainthread
|
@mainthread
|
||||||
def display_received_msg(self, msg):
|
def display_received_msg(self, msg):
|
||||||
self.uiDict['txtInput_read'].text += msg
|
self.uiDict['txtInput_read'].text += msg
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -2,7 +2,7 @@ from setuptools import setup
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="usbserial4a",
|
name="usbserial4a",
|
||||||
version="0.1.6",
|
version="0.1.7",
|
||||||
description="Python package for Kivy Android USB serial port.",
|
description="Python package for Kivy Android USB serial port.",
|
||||||
long_description="https://github.com/jacklinquan/usbserial4a",
|
long_description="https://github.com/jacklinquan/usbserial4a",
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
|
|
|
@ -9,4 +9,4 @@ Requires: kivy, pyjnius, pyserial, usb4a
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# Project version
|
# Project version
|
||||||
__version__ = '0.1.6'
|
__version__ = '0.1.7'
|
||||||
|
|
|
@ -4,6 +4,8 @@ Classes:
|
||||||
FtdiSerial(serial.serialutil.SerialBase)
|
FtdiSerial(serial.serialutil.SerialBase)
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
from struct import unpack
|
||||||
|
import time
|
||||||
from serial.serialutil import SerialBase, SerialException, to_bytes, \
|
from serial.serialutil import SerialBase, SerialException, to_bytes, \
|
||||||
portNotOpenError, writeTimeoutError, Timeout
|
portNotOpenError, writeTimeoutError, Timeout
|
||||||
from usb4a import usb
|
from usb4a import usb
|
||||||
|
@ -14,6 +16,19 @@ class FtdiSerial(SerialBase):
|
||||||
FtdiSerial extends serial.serialutil.SerialBase.
|
FtdiSerial extends serial.serialutil.SerialBase.
|
||||||
It can be used in a similar way to serial.Serial from pyserial.
|
It can be used in a similar way to serial.Serial from pyserial.
|
||||||
'''
|
'''
|
||||||
|
# Modem status
|
||||||
|
MODEM_CTS = (1 << 4) # Clear to send
|
||||||
|
MODEM_DSR = (1 << 5) # Data set ready
|
||||||
|
MODEM_RI = (1 << 6) # Ring indicator
|
||||||
|
MODEM_RLSD = (1 << 7) # Carrier detect
|
||||||
|
MODEM_DR = (1 << 8) # Data ready
|
||||||
|
MODEM_OE = (1 << 9) # Overrun error
|
||||||
|
MODEM_PE = (1 << 10) # Parity error
|
||||||
|
MODEM_FE = (1 << 11) # Framing error
|
||||||
|
MODEM_BI = (1 << 12) # Break interrupt
|
||||||
|
MODEM_THRE = (1 << 13) # Transmitter holding register
|
||||||
|
MODEM_TEMT = (1 << 14) # Transmitter empty
|
||||||
|
MODEM_RCVE = (1 << 15) # Error in RCVR FIFO
|
||||||
|
|
||||||
# Requests
|
# Requests
|
||||||
SIO_RESET = 0 # Reset the port
|
SIO_RESET = 0 # Reset the port
|
||||||
|
@ -52,6 +67,28 @@ class FtdiSerial(SerialBase):
|
||||||
SIO_SET_RTS_HIGH = (SIO_SET_RTS_MASK | (SIO_SET_RTS_MASK << 8))
|
SIO_SET_RTS_HIGH = (SIO_SET_RTS_MASK | (SIO_SET_RTS_MASK << 8))
|
||||||
SIO_SET_RTS_LOW = (0x0 | (SIO_SET_RTS_MASK << 8))
|
SIO_SET_RTS_LOW = (0x0 | (SIO_SET_RTS_MASK << 8))
|
||||||
|
|
||||||
|
# Break type
|
||||||
|
BREAK_OFF, BREAK_ON = range(2)
|
||||||
|
|
||||||
|
# cts: Clear to send
|
||||||
|
# dsr: Data set ready
|
||||||
|
# ri: Ring indicator
|
||||||
|
# dcd: Data carrier detect
|
||||||
|
# dr: Data ready
|
||||||
|
# oe: Overrun error
|
||||||
|
# pe: Parity error
|
||||||
|
# fe: Framing error
|
||||||
|
# bi: Break interrupt
|
||||||
|
# thre: Transmitter holding register
|
||||||
|
# temt: Transmitter empty
|
||||||
|
# err: Error in RCVR FIFO
|
||||||
|
MODEM_STATUS = [
|
||||||
|
('', '', '', '', 'cts', 'dsr', 'ri', 'dcd'),
|
||||||
|
('dr', 'overrun', 'parity', 'framing', 'break', 'thre', 'txe', 'rcvr')
|
||||||
|
]
|
||||||
|
|
||||||
|
ERROR_BITS = (0x00, 0x8E)
|
||||||
|
|
||||||
# Clocks and baudrates
|
# Clocks and baudrates
|
||||||
BUS_CLOCK_BASE = 6.0E6 # 6 MHz
|
BUS_CLOCK_BASE = 6.0E6 # 6 MHz
|
||||||
BUS_CLOCK_HIGH = 30.0E6 # 30 MHz
|
BUS_CLOCK_HIGH = 30.0E6 # 30 MHz
|
||||||
|
@ -64,11 +101,13 @@ class FtdiSerial(SerialBase):
|
||||||
FTDI_DEVICE_OUT_REQTYPE = usb.build_usb_control_request_type(
|
FTDI_DEVICE_OUT_REQTYPE = usb.build_usb_control_request_type(
|
||||||
usb.UsbConstants.USB_DIR_OUT,
|
usb.UsbConstants.USB_DIR_OUT,
|
||||||
usb.UsbConstants.USB_TYPE_VENDOR,
|
usb.UsbConstants.USB_TYPE_VENDOR,
|
||||||
usb.USB_RECIPIENT_DEVICE)
|
usb.USB_RECIPIENT_DEVICE
|
||||||
|
)
|
||||||
FTDI_DEVICE_IN_REQTYPE = usb.build_usb_control_request_type(
|
FTDI_DEVICE_IN_REQTYPE = usb.build_usb_control_request_type(
|
||||||
usb.UsbConstants.USB_DIR_IN,
|
usb.UsbConstants.USB_DIR_IN,
|
||||||
usb.UsbConstants.USB_TYPE_VENDOR,
|
usb.UsbConstants.USB_TYPE_VENDOR,
|
||||||
usb.USB_RECIPIENT_DEVICE)
|
usb.USB_RECIPIENT_DEVICE
|
||||||
|
)
|
||||||
|
|
||||||
DEFAULT_READ_BUFFER_SIZE = 1024
|
DEFAULT_READ_BUFFER_SIZE = 1024
|
||||||
DEFAULT_WRITE_BUFFER_SIZE = 16 * 1024
|
DEFAULT_WRITE_BUFFER_SIZE = 16 * 1024
|
||||||
|
@ -88,6 +127,7 @@ class FtdiSerial(SerialBase):
|
||||||
self._control_endpoint = None
|
self._control_endpoint = None
|
||||||
self._read_endpoint = None
|
self._read_endpoint = None
|
||||||
self._write_endpoint = None
|
self._write_endpoint = None
|
||||||
|
self._lineprop = 0
|
||||||
self._read_buffer = bytearray()
|
self._read_buffer = bytearray()
|
||||||
super(FtdiSerial, self).__init__(*args, **kwargs)
|
super(FtdiSerial, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
@ -121,21 +161,28 @@ class FtdiSerial(SerialBase):
|
||||||
self._interface = self._device.getInterface(i)
|
self._interface = self._device.getInterface(i)
|
||||||
if not self._connection.claimInterface(
|
if not self._connection.claimInterface(
|
||||||
self._device.getInterface(i),
|
self._device.getInterface(i),
|
||||||
True):
|
True
|
||||||
|
):
|
||||||
raise SerialException("Could not claim interface {}.".format(i))
|
raise SerialException("Could not claim interface {}.".format(i))
|
||||||
|
|
||||||
self._index = self._interface.getId() + 1
|
self._index = self._interface.getId() + 1
|
||||||
|
|
||||||
for i in range(self._interface.getEndpointCount()):
|
for i in range(self._interface.getEndpointCount()):
|
||||||
ep = self._interface.getEndpoint(i)
|
ep = self._interface.getEndpoint(i)
|
||||||
if ((ep.getDirection() == usb.UsbConstants.USB_DIR_IN) and
|
if (
|
||||||
(ep.getType() == usb.UsbConstants.USB_ENDPOINT_XFER_INT)):
|
(ep.getDirection() == usb.UsbConstants.USB_DIR_IN) \
|
||||||
|
and (ep.getType() == usb.UsbConstants.USB_ENDPOINT_XFER_INT)
|
||||||
|
):
|
||||||
self._control_endpoint = ep
|
self._control_endpoint = ep
|
||||||
elif ((ep.getDirection() == usb.UsbConstants.USB_DIR_IN) and
|
elif (
|
||||||
(ep.getType() == usb.UsbConstants.USB_ENDPOINT_XFER_BULK)):
|
(ep.getDirection() == usb.UsbConstants.USB_DIR_IN) \
|
||||||
|
and (ep.getType() == usb.UsbConstants.USB_ENDPOINT_XFER_BULK)
|
||||||
|
):
|
||||||
self._read_endpoint = ep
|
self._read_endpoint = ep
|
||||||
elif ((ep.getDirection() == usb.UsbConstants.USB_DIR_OUT) and
|
elif (
|
||||||
(ep.getType() == usb.UsbConstants.USB_ENDPOINT_XFER_BULK)):
|
(ep.getDirection() == usb.UsbConstants.USB_DIR_OUT) \
|
||||||
|
and (ep.getType() == usb.UsbConstants.USB_ENDPOINT_XFER_BULK)
|
||||||
|
):
|
||||||
self._write_endpoint = ep
|
self._write_endpoint = ep
|
||||||
|
|
||||||
# Check that all endpoints are good
|
# Check that all endpoints are good
|
||||||
|
@ -145,6 +192,22 @@ class FtdiSerial(SerialBase):
|
||||||
self.is_open = True
|
self.is_open = True
|
||||||
self._reconfigure_port()
|
self._reconfigure_port()
|
||||||
|
|
||||||
|
def _reconfigure_port(self):
|
||||||
|
'''Reconfigure serial port parameters.'''
|
||||||
|
self._setParameters(
|
||||||
|
self.baudrate,
|
||||||
|
self.bytesize,
|
||||||
|
self.parity,
|
||||||
|
self.stopbits
|
||||||
|
)
|
||||||
|
|
||||||
|
if self._rtscts:
|
||||||
|
self._set_flowctrl('hw')
|
||||||
|
elif self._xonxoff:
|
||||||
|
self._set_flowctrl('sw')
|
||||||
|
else:
|
||||||
|
self._set_flowctrl('')
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
'''Close the serial port.'''
|
'''Close the serial port.'''
|
||||||
if self._connection:
|
if self._connection:
|
||||||
|
@ -158,10 +221,13 @@ class FtdiSerial(SerialBase):
|
||||||
result = self._ctrl_transfer_out(
|
result = self._ctrl_transfer_out(
|
||||||
self.SIO_RESET,
|
self.SIO_RESET,
|
||||||
self.SIO_RESET_SIO,
|
self.SIO_RESET_SIO,
|
||||||
0)
|
self._index
|
||||||
|
)
|
||||||
if result != 0:
|
if result != 0:
|
||||||
raise SerialException("Reset failed: result={}".format(result))
|
raise SerialException("Reset failed: result={}".format(result))
|
||||||
|
|
||||||
|
# - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def in_waiting(self):
|
def in_waiting(self):
|
||||||
'''Return the number of bytes currently in the input buffer.
|
'''Return the number of bytes currently in the input buffer.
|
||||||
|
@ -173,6 +239,14 @@ class FtdiSerial(SerialBase):
|
||||||
self._read_buffer.extend(self._read())
|
self._read_buffer.extend(self._read())
|
||||||
return len(self._read_buffer)
|
return len(self._read_buffer)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def out_waiting(self):
|
||||||
|
'''Return the number of bytes currently in the output buffer.
|
||||||
|
|
||||||
|
Always return 0.
|
||||||
|
'''
|
||||||
|
return 0
|
||||||
|
|
||||||
def read(self, size=1):
|
def read(self, size=1):
|
||||||
'''Read data from the serial port.
|
'''Read data from the serial port.
|
||||||
|
|
||||||
|
@ -212,18 +286,21 @@ class FtdiSerial(SerialBase):
|
||||||
offset = 0
|
offset = 0
|
||||||
timeout = int(
|
timeout = int(
|
||||||
self._write_timeout * 1000 if self._write_timeout \
|
self._write_timeout * 1000 if self._write_timeout \
|
||||||
else self.USB_WRITE_TIMEOUT_MILLIS)
|
else self.USB_WRITE_TIMEOUT_MILLIS
|
||||||
|
)
|
||||||
wrote = 0
|
wrote = 0
|
||||||
while offset < len(data):
|
while offset < len(data):
|
||||||
data_length = min(
|
data_length = min(
|
||||||
len(data) - offset,
|
len(data) - offset,
|
||||||
self.DEFAULT_WRITE_BUFFER_SIZE)
|
self.DEFAULT_WRITE_BUFFER_SIZE
|
||||||
|
)
|
||||||
buf = data[offset:offset + data_length]
|
buf = data[offset:offset + data_length]
|
||||||
i = self._connection.bulkTransfer(
|
i = self._connection.bulkTransfer(
|
||||||
self._write_endpoint,
|
self._write_endpoint,
|
||||||
buf,
|
buf,
|
||||||
data_length,
|
data_length,
|
||||||
timeout)
|
timeout
|
||||||
|
)
|
||||||
if i <= 0:
|
if i <= 0:
|
||||||
raise SerialException("Failed to write {}: {}".format(buf, i))
|
raise SerialException("Failed to write {}: {}".format(buf, i))
|
||||||
offset += data_length
|
offset += data_length
|
||||||
|
@ -234,7 +311,151 @@ class FtdiSerial(SerialBase):
|
||||||
'''Simply wait some time to allow all data to be written.'''
|
'''Simply wait some time to allow all data to be written.'''
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def purgeHwBuffers(self, purgeReadBuffers, purgeWriteBuffers):
|
def reset_input_buffer(self):
|
||||||
|
'''Clear input buffer, discarding all that is in the buffer.'''
|
||||||
|
if not self.is_open:
|
||||||
|
raise portNotOpenError
|
||||||
|
self._purgeHwBuffers(True, False)
|
||||||
|
|
||||||
|
def reset_output_buffer(self):
|
||||||
|
'''\
|
||||||
|
Clear output buffer, aborting the current output and discarding all
|
||||||
|
that is in the buffer.
|
||||||
|
'''
|
||||||
|
if not self.is_open:
|
||||||
|
raise portNotOpenError
|
||||||
|
self._purgeHwBuffers(False, True)
|
||||||
|
|
||||||
|
def send_break(self, duration=0.25):
|
||||||
|
'''Send break condition.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
duration (float): break time in seconds.
|
||||||
|
'''
|
||||||
|
if not self.is_open:
|
||||||
|
raise portNotOpenError
|
||||||
|
self._set_break(True)
|
||||||
|
time.sleep(duration)
|
||||||
|
self._set_break(False)
|
||||||
|
|
||||||
|
def _update_break_state(self):
|
||||||
|
'''Send break condition.'''
|
||||||
|
self._set_break(self._break_state)
|
||||||
|
|
||||||
|
def _update_rts_state(self):
|
||||||
|
'''Set terminal status line: Request To Send.'''
|
||||||
|
self._set_rts(self._rts_state)
|
||||||
|
|
||||||
|
def _update_dtr_state(self):
|
||||||
|
'''Set terminal status line: Data Terminal Ready.'''
|
||||||
|
self._set_dtr(self._dtr_state)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cts(self):
|
||||||
|
'''Read terminal status line: Clear To Send.'''
|
||||||
|
status = self._poll_modem_status()
|
||||||
|
return bool(status & self.MODEM_CTS)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dsr(self):
|
||||||
|
'''Read terminal status line: Data Set Ready.'''
|
||||||
|
status = self._poll_modem_status()
|
||||||
|
return bool(status & self.MODEM_DSR)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ri(self):
|
||||||
|
'''Read terminal status line: Ring Indicator.'''
|
||||||
|
status = self._poll_modem_status()
|
||||||
|
return bool(status & self.MODEM_RI)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cd(self):
|
||||||
|
'''Read terminal status line: Carrier Detect.'''
|
||||||
|
status = self._poll_modem_status()
|
||||||
|
return bool(status & self.MODEM_RLSD)
|
||||||
|
|
||||||
|
# - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
def _ctrl_transfer_out(self, request, value, index):
|
||||||
|
'''USB control transfer out.
|
||||||
|
|
||||||
|
This function does the USB configuration job.
|
||||||
|
'''
|
||||||
|
return self._connection.controlTransfer(
|
||||||
|
self.FTDI_DEVICE_OUT_REQTYPE,
|
||||||
|
request,
|
||||||
|
value,
|
||||||
|
index,
|
||||||
|
None,
|
||||||
|
0,
|
||||||
|
self.USB_WRITE_TIMEOUT_MILLIS
|
||||||
|
)
|
||||||
|
|
||||||
|
def _ctrl_transfer_in(self, request, buf, length):
|
||||||
|
'''USB control transfer in.
|
||||||
|
|
||||||
|
Request for a control message from the device.
|
||||||
|
'''
|
||||||
|
return self._connection.controlTransfer(
|
||||||
|
self.FTDI_DEVICE_IN_REQTYPE,
|
||||||
|
request,
|
||||||
|
0,
|
||||||
|
self._index,
|
||||||
|
buf,
|
||||||
|
length,
|
||||||
|
self.USB_READ_TIMEOUT_MILLIS
|
||||||
|
)
|
||||||
|
|
||||||
|
def _set_break(self, break_):
|
||||||
|
'''Start or stop a break exception event on the serial line.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
break_ (bool): either start or stop break event.
|
||||||
|
'''
|
||||||
|
if break_:
|
||||||
|
value = self._lineprop | (0x01 << 14)
|
||||||
|
if self._ctrl_transfer_out(self.SIO_SET_DATA, value, self._index):
|
||||||
|
raise SerialException('Unable to start break sequence')
|
||||||
|
else:
|
||||||
|
value = self._lineprop & ~(0x01 << 14)
|
||||||
|
if self._ctrl_transfer_out(self.SIO_SET_DATA, value, self._index):
|
||||||
|
raise SerialException('Unable to stop break sequence')
|
||||||
|
self._lineprop = value
|
||||||
|
|
||||||
|
def _set_dtr(self, state):
|
||||||
|
'''Set dtr line.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
state (bool): new DTR logical level.
|
||||||
|
'''
|
||||||
|
value = state and self.SIO_SET_DTR_HIGH or self.SIO_SET_DTR_LOW
|
||||||
|
if self._ctrl_transfer_out(self.SIO_SET_MODEM_CTRL, value, self._index):
|
||||||
|
raise SerialException('Unable to set DTR line')
|
||||||
|
|
||||||
|
def _set_rts(self, state):
|
||||||
|
'''Set rts line.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
state (bool): new RTS logical level.
|
||||||
|
'''
|
||||||
|
value = state and self.SIO_SET_RTS_HIGH or self.SIO_SET_RTS_LOW
|
||||||
|
if self._ctrl_transfer_out(self.SIO_SET_MODEM_CTRL, value, self._index):
|
||||||
|
raise SerialException('Unable to set RTS line')
|
||||||
|
|
||||||
|
def _set_dtr_rts(self, dtr, rts):
|
||||||
|
'''Set dtr and rts lines at once.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
dtr (bool): new DTR logical level.
|
||||||
|
rts (bool): new RTS logical level.
|
||||||
|
'''
|
||||||
|
value = 0
|
||||||
|
value |= dtr and self.SIO_SET_DTR_HIGH or self.SIO_SET_DTR_LOW
|
||||||
|
value |= rts and self.SIO_SET_RTS_HIGH or self.SIO_SET_RTS_LOW
|
||||||
|
if self._ctrl_transfer_out(self.SIO_SET_FLOW_CTRL, value, self._index):
|
||||||
|
raise SerialException('Unable to set DTR/RTS lines')
|
||||||
|
|
||||||
|
def _purgeHwBuffers(self, purgeReadBuffers, purgeWriteBuffers):
|
||||||
'''Set serial port parameters.
|
'''Set serial port parameters.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
@ -247,19 +468,22 @@ class FtdiSerial(SerialBase):
|
||||||
result = self._ctrl_transfer_out(
|
result = self._ctrl_transfer_out(
|
||||||
self.SIO_RESET,
|
self.SIO_RESET,
|
||||||
self.SIO_RESET_PURGE_RX,
|
self.SIO_RESET_PURGE_RX,
|
||||||
0)
|
self._index
|
||||||
|
)
|
||||||
if result != 0:
|
if result != 0:
|
||||||
raise SerialException(
|
raise SerialException(
|
||||||
"Flushing RX failed: result={}".format(result))
|
"Flushing RX failed: result={}".format(result)
|
||||||
|
)
|
||||||
|
|
||||||
if purgeWriteBuffers:
|
if purgeWriteBuffers:
|
||||||
result = self._ctrl_transfer_out(
|
result = self._ctrl_transfer_out(
|
||||||
self.SIO_RESET,
|
self.SIO_RESET,
|
||||||
self.SIO_RESET_PURGE_TX,
|
self.SIO_RESET_PURGE_TX,
|
||||||
0)
|
self._index)
|
||||||
if result != 0:
|
if result != 0:
|
||||||
raise SerialException(
|
raise SerialException(
|
||||||
"Flushing TX failed: result={}".format(result))
|
"Flushing TX failed: result={}".format(result)
|
||||||
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -280,11 +504,14 @@ class FtdiSerial(SerialBase):
|
||||||
self._read_endpoint,
|
self._read_endpoint,
|
||||||
buf,
|
buf,
|
||||||
self.DEFAULT_READ_BUFFER_SIZE,
|
self.DEFAULT_READ_BUFFER_SIZE,
|
||||||
self.USB_READ_TIMEOUT_MILLIS)
|
self.USB_READ_TIMEOUT_MILLIS
|
||||||
|
)
|
||||||
if totalBytesRead < self.MODEM_STATUS_HEADER_LENGTH:
|
if totalBytesRead < self.MODEM_STATUS_HEADER_LENGTH:
|
||||||
raise SerialException(
|
raise SerialException(
|
||||||
"Expected at least {} bytes".format(
|
"Expected at least {} bytes".format(
|
||||||
self.MODEM_STATUS_HEADER_LENGTH))
|
self.MODEM_STATUS_HEADER_LENGTH
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# Translate raw data into serial port data.
|
# Translate raw data into serial port data.
|
||||||
read = bytearray()
|
read = bytearray()
|
||||||
|
@ -292,7 +519,8 @@ class FtdiSerial(SerialBase):
|
||||||
buf,
|
buf,
|
||||||
read,
|
read,
|
||||||
totalBytesRead,
|
totalBytesRead,
|
||||||
self._read_endpoint.getMaxPacketSize())
|
self._read_endpoint.getMaxPacketSize()
|
||||||
|
)
|
||||||
|
|
||||||
return bytes(read)
|
return bytes(read)
|
||||||
|
|
||||||
|
@ -315,13 +543,59 @@ class FtdiSerial(SerialBase):
|
||||||
actual, value, index = self._convert_baudrate(baudrate)
|
actual, value, index = self._convert_baudrate(baudrate)
|
||||||
delta = 100*abs(float(actual-baudrate))/baudrate
|
delta = 100*abs(float(actual-baudrate))/baudrate
|
||||||
if delta > self.BAUDRATE_TOLERANCE:
|
if delta > self.BAUDRATE_TOLERANCE:
|
||||||
raise ValueError('Baudrate tolerance exceeded: %.02f%% '
|
raise ValueError(
|
||||||
'(wanted %d, achievable %d)' %
|
'Baudrate tolerance exceeded: %.02f%% '
|
||||||
(delta, baudrate, actual))
|
'(wanted %d, achievable %d)' %
|
||||||
|
(delta, baudrate, actual)
|
||||||
|
)
|
||||||
result = self._ctrl_transfer_out(self.SIO_SET_BAUDRATE, value, index)
|
result = self._ctrl_transfer_out(self.SIO_SET_BAUDRATE, value, index)
|
||||||
if result != 0:
|
if result != 0:
|
||||||
raise SerialException('Unable to set baudrate')
|
raise SerialException('Unable to set baudrate.')
|
||||||
|
|
||||||
|
def _set_line_property(self, bits, stopbits, parity, break_=0):
|
||||||
|
'''Set serial port line property.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
bits (int): number of bits in data(5, 6, 7 or 8).
|
||||||
|
stopbits (float): number of stop bits(1, 1.5, 2).
|
||||||
|
parity (str): 'N', 'E', 'O', 'M' or 'S'.
|
||||||
|
break_ (int): 0 for break off or 1 for break on.
|
||||||
|
'''
|
||||||
|
value = bits & 0x0F
|
||||||
|
|
||||||
|
if parity == 'N':
|
||||||
|
value |= (0x00 << 8)
|
||||||
|
elif parity == 'O':
|
||||||
|
value |= (0x01 << 8)
|
||||||
|
elif parity == 'E':
|
||||||
|
value |= (0x02 << 8)
|
||||||
|
elif parity == 'M':
|
||||||
|
value |= (0x03 << 8)
|
||||||
|
elif parity == 'S':
|
||||||
|
value |= (0x04 << 8)
|
||||||
|
else:
|
||||||
|
raise ValueError('Unknown parity value: {}'.format(parity))
|
||||||
|
|
||||||
|
if stopbits == 1:
|
||||||
|
value |= (0x00 << 11)
|
||||||
|
elif stopbits == 1.5:
|
||||||
|
value |= (0x01 << 11)
|
||||||
|
elif stopbits == 2:
|
||||||
|
value |= (0x02 << 11)
|
||||||
|
else:
|
||||||
|
raise ValueError("Unknown stopbits value: {}".format(stopbits))
|
||||||
|
|
||||||
|
if break_ == self.BREAK_ON:
|
||||||
|
value |= 0x01 << 14
|
||||||
|
|
||||||
|
result = self._ctrl_transfer_out(self.SIO_SET_DATA, value, self._index)
|
||||||
|
if result != 0:
|
||||||
|
raise SerialException(
|
||||||
|
'Setting line property failed: result={}'.format(result)
|
||||||
|
)
|
||||||
|
|
||||||
|
self._lineprop = value
|
||||||
|
|
||||||
def _setParameters(self, baudrate, databits, parity, stopbits):
|
def _setParameters(self, baudrate, databits, parity, stopbits):
|
||||||
'''Set serial port parameters.
|
'''Set serial port parameters.
|
||||||
|
|
||||||
|
@ -333,57 +607,54 @@ class FtdiSerial(SerialBase):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self._set_baudrate(baudrate)
|
self._set_baudrate(baudrate)
|
||||||
|
self._set_line_property(databits, stopbits, parity)
|
||||||
|
|
||||||
config = databits
|
def _poll_modem_status(self):
|
||||||
|
'''Poll modem status information.
|
||||||
|
|
||||||
if parity == 'N':
|
This function allows the retrieve the two status bytes of the
|
||||||
config |= (0x00 << 8)
|
device, useful in UART mode.
|
||||||
elif parity == 'O':
|
FTDI device does not have a so-called USB "interrupt" end-point,
|
||||||
config |= (0x01 << 8)
|
event polling on the UART interface is done through the regular
|
||||||
elif parity == 'E':
|
control endpoint.
|
||||||
config |= (0x02 << 8)
|
Returns:
|
||||||
elif parity == 'M':
|
status (int): modem status, as a proprietary bitfield
|
||||||
config |= (0x03 << 8)
|
'''
|
||||||
elif parity == 'S':
|
buf = bytearray(self.DEFAULT_READ_BUFFER_SIZE)
|
||||||
config |= (0x04 << 8)
|
result = self._ctrl_transfer_in(self.SIO_POLL_MODEM_STATUS, buf, 2)
|
||||||
else:
|
if result != 2:
|
||||||
raise ValueError("Unknown parity value: {}".format(parity))
|
raise SerialException('Unable to get modem status.')
|
||||||
|
buf = buf[:2]
|
||||||
|
status, = unpack('<H', bytes(buf))
|
||||||
|
return status
|
||||||
|
|
||||||
|
def _set_flowctrl(self, flowctrl):
|
||||||
|
'''Select flowcontrol in UART mode.
|
||||||
|
|
||||||
if stopbits == 1:
|
Either hardware flow control through RTS/CTS UART lines,
|
||||||
config |= (0x00 << 11)
|
software or no flow control.
|
||||||
elif stopbits == 1.5:
|
Parameters:
|
||||||
config |= (0x01 << 11)
|
flowctrl (str): 'hw', 'sw' or ''
|
||||||
elif stopbits == 2:
|
'''
|
||||||
config |= (0x02 << 11)
|
ctrl = {
|
||||||
else:
|
'hw': self.SIO_RTS_CTS_HS,
|
||||||
raise ValueError("Unknown stopbits value: {}".format(stopbits))
|
'sw': self.SIO_XON_XOFF_HS,
|
||||||
|
'': self.SIO_DISABLE_FLOW_CTRL
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
value = ctrl[flowctrl] | self._index
|
||||||
|
except KeyError:
|
||||||
|
raise ValueError('Unknown flow control: {}'.format(flowctrl))
|
||||||
|
|
||||||
result = self._ctrl_transfer_out(self.SIO_SET_DATA, config, 0)
|
result = self._ctrl_transfer_out(
|
||||||
|
self.SIO_SET_FLOW_CTRL,
|
||||||
|
0,
|
||||||
|
value
|
||||||
|
)
|
||||||
if result != 0:
|
if result != 0:
|
||||||
raise SerialException(
|
raise SerialException(
|
||||||
"Setting parameters failed: result={}".format(result))
|
"Setting flow control failed: result={}".format(result)
|
||||||
|
)
|
||||||
def _reconfigure_port(self):
|
|
||||||
'''Reconfigure serial port parameters.'''
|
|
||||||
self._setParameters(
|
|
||||||
self.baudrate,
|
|
||||||
self.bytesize,
|
|
||||||
self.parity,
|
|
||||||
self.stopbits)
|
|
||||||
|
|
||||||
def _ctrl_transfer_out(self, request, value, index):
|
|
||||||
'''USB control transfer.
|
|
||||||
|
|
||||||
This function does the USB configuration job.
|
|
||||||
'''
|
|
||||||
return self._connection.controlTransfer(
|
|
||||||
self.FTDI_DEVICE_OUT_REQTYPE,
|
|
||||||
request,
|
|
||||||
value,
|
|
||||||
index,
|
|
||||||
None,
|
|
||||||
0,
|
|
||||||
self.USB_WRITE_TIMEOUT_MILLIS)
|
|
||||||
|
|
||||||
def _has_mpsse(self):
|
def _has_mpsse(self):
|
||||||
'''Tell whether the device supports MPSSE (I2C, SPI, JTAG, ...).
|
'''Tell whether the device supports MPSSE (I2C, SPI, JTAG, ...).
|
||||||
|
@ -500,8 +771,8 @@ class FtdiSerial(SerialBase):
|
||||||
if baud_diff == 0:
|
if baud_diff == 0:
|
||||||
break
|
break
|
||||||
# Encode the best divisor value
|
# Encode the best divisor value
|
||||||
encoded_divisor = (best_divisor >> 3) | \
|
encoded_divisor = (best_divisor >> 3) \
|
||||||
(frac_code[best_divisor & 7] << 14)
|
| (frac_code[best_divisor & 7] << 14)
|
||||||
# Deal with special cases for encoded value
|
# Deal with special cases for encoded value
|
||||||
if encoded_divisor == 1:
|
if encoded_divisor == 1:
|
||||||
encoded_divisor = 0 # 3000000 baud
|
encoded_divisor = 0 # 3000000 baud
|
||||||
|
@ -531,21 +802,23 @@ class FtdiSerial(SerialBase):
|
||||||
Returns:
|
Returns:
|
||||||
result (int): the number of payload bytes.
|
result (int): the number of payload bytes.
|
||||||
'''
|
'''
|
||||||
packetsCount = totalBytesRead // maxPacketSize + (
|
packetsCount = totalBytesRead // maxPacketSize + \
|
||||||
0 if totalBytesRead % maxPacketSize == 0 else 1)
|
(0 if totalBytesRead % maxPacketSize == 0 else 1)
|
||||||
for packetIdx in range(packetsCount):
|
for packetIdx in range(packetsCount):
|
||||||
count = ((totalBytesRead % maxPacketSize) -
|
count = (
|
||||||
self.MODEM_STATUS_HEADER_LENGTH) if (
|
(totalBytesRead % maxPacketSize) \
|
||||||
packetIdx == (packetsCount - 1)) else (
|
- self.MODEM_STATUS_HEADER_LENGTH
|
||||||
maxPacketSize - self.MODEM_STATUS_HEADER_LENGTH)
|
) if (packetIdx == (packetsCount - 1)) else \
|
||||||
|
(maxPacketSize - self.MODEM_STATUS_HEADER_LENGTH)
|
||||||
if count > 0:
|
if count > 0:
|
||||||
usb.arraycopy(
|
usb.arraycopy(
|
||||||
src,
|
src,
|
||||||
packetIdx * maxPacketSize + self.MODEM_STATUS_HEADER_LENGTH,
|
packetIdx * maxPacketSize + self.MODEM_STATUS_HEADER_LENGTH,
|
||||||
dest,
|
dest,
|
||||||
packetIdx * (
|
packetIdx \
|
||||||
maxPacketSize - self.MODEM_STATUS_HEADER_LENGTH),
|
* (maxPacketSize - self.MODEM_STATUS_HEADER_LENGTH),
|
||||||
count)
|
count
|
||||||
|
)
|
||||||
|
|
||||||
return totalBytesRead - (packetsCount * 2)
|
return totalBytesRead - (packetsCount * 2)
|
||||||
|
|
Ładowanie…
Reference in New Issue