Add full CP210x API coverage: 29 tools, all part families
Complete ctypes bindings for all 45 libcp210xmanufacturing functions including structs (BAUD_CONFIG, PORT_CONFIG, DUAL_PORT_CONFIG, QUAD_PORT_CONFIG, FIRMWARE_T), part-number gating, bitmask helpers, and three-tier safety model (none/normal/strict elicitation). New tools: set_vid, set_pid, get/set_interface_string, get/set_flush_buffer_config, get/set_device_mode, get_firmware_version, set_device_version, get/set_baud_rate_config, set_baud_rate_alias, get/set_port_config, get/set_raw_config, create_hex_file, update_firmware.
This commit is contained in:
parent
80220f5728
commit
c0bedde54b
@ -1,5 +1,5 @@
|
|||||||
"""mcp210x-uart - MCP server for Silicon Labs CP210x USB-UART bridge customization."""
|
"""mcp210x-uart - MCP server for Silicon Labs CP210x USB-UART bridge customization."""
|
||||||
|
|
||||||
from .server import mcp, main
|
from .server import main, mcp
|
||||||
|
|
||||||
__all__ = ["mcp", "main"]
|
__all__ = ["mcp", "main"]
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
"""Low-level ctypes bindings for libcp210xmanufacturing."""
|
"""Low-level ctypes bindings for libcp210xmanufacturing."""
|
||||||
|
|
||||||
import ctypes
|
import ctypes
|
||||||
from ctypes import c_int, c_uint, c_ushort, c_ubyte, c_char_p, c_void_p, POINTER, byref
|
from ctypes import POINTER, Structure, byref, c_char_p, c_int, c_ubyte, c_uint, c_ushort, c_void_p
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
# Type aliases matching the C library
|
# Type aliases matching the C library
|
||||||
CP210x_STATUS = c_int
|
CP210x_STATUS = c_int
|
||||||
@ -39,17 +38,238 @@ PART_NUMBERS = {
|
|||||||
0x22: "CP2102N-QFN20",
|
0x22: "CP2102N-QFN20",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Part number groups for feature gating
|
||||||
|
PARTS_CP2102N = {0x20, 0x21, 0x22}
|
||||||
|
PARTS_WITH_FLUSH_CONFIG = {0x04, 0x05, 0x08} # CP2104, CP2105, CP2108
|
||||||
|
PARTS_WITH_DEVICE_MODE = {0x05} # CP2105 only
|
||||||
|
PARTS_WITH_INTERFACE_STRING = {0x05, 0x08} # CP2105, CP2108
|
||||||
|
PARTS_WITH_PORT_CONFIG = {0x03, 0x04} # CP2103, CP2104
|
||||||
|
PARTS_WITH_DUAL_PORT_CONFIG = {0x05} # CP2105
|
||||||
|
PARTS_WITH_QUAD_PORT_CONFIG = {0x08} # CP2108
|
||||||
|
|
||||||
# Buffer sizes
|
# Buffer sizes
|
||||||
MAX_DEVICE_STRLEN = 256
|
MAX_DEVICE_STRLEN = 256
|
||||||
MAX_MANUFACTURER_STRLEN = 45
|
MAX_MANUFACTURER_STRLEN = 45
|
||||||
MAX_PRODUCT_STRLEN = 126
|
MAX_PRODUCT_STRLEN = 126
|
||||||
MAX_SERIAL_STRLEN = 63
|
MAX_SERIAL_STRLEN = 63
|
||||||
|
CP2105_MAX_INTERFACE_STRLEN = 32
|
||||||
|
CP2108_MAX_INTERFACE_STRLEN = 126
|
||||||
|
|
||||||
# GetProductString flags
|
# GetProductString flags
|
||||||
RETURN_SERIAL_NUMBER = 0x00
|
RETURN_SERIAL_NUMBER = 0x00
|
||||||
RETURN_DESCRIPTION = 0x01
|
RETURN_DESCRIPTION = 0x01
|
||||||
RETURN_FULL_PATH = 0x02
|
RETURN_FULL_PATH = 0x02
|
||||||
|
|
||||||
|
# Flush buffer config flags (CP2104)
|
||||||
|
FC_OPEN_TX = 0x01
|
||||||
|
FC_OPEN_RX = 0x02
|
||||||
|
FC_CLOSE_TX = 0x04
|
||||||
|
FC_CLOSE_RX = 0x08
|
||||||
|
# CP2105 standard port
|
||||||
|
FC_OPEN_TX_SCI = FC_OPEN_TX
|
||||||
|
FC_OPEN_RX_SCI = FC_OPEN_RX
|
||||||
|
FC_CLOSE_TX_SCI = FC_CLOSE_TX
|
||||||
|
FC_CLOSE_RX_SCI = FC_CLOSE_RX
|
||||||
|
# CP2105 enhanced port
|
||||||
|
FC_OPEN_TX_ECI = 0x10
|
||||||
|
FC_OPEN_RX_ECI = 0x20
|
||||||
|
FC_CLOSE_TX_ECI = 0x40
|
||||||
|
FC_CLOSE_RX_ECI = 0x80
|
||||||
|
# CP2108 per-interface
|
||||||
|
FC_OPEN_TX_IFC0 = 0x0001
|
||||||
|
FC_OPEN_RX_IFC0 = 0x0002
|
||||||
|
FC_CLOSE_TX_IFC0 = 0x0004
|
||||||
|
FC_CLOSE_RX_IFC0 = 0x0008
|
||||||
|
FC_OPEN_TX_IFC1 = 0x0010
|
||||||
|
FC_OPEN_RX_IFC1 = 0x0020
|
||||||
|
FC_CLOSE_TX_IFC1 = 0x0040
|
||||||
|
FC_CLOSE_RX_IFC1 = 0x0080
|
||||||
|
FC_OPEN_TX_IFC2 = 0x0100
|
||||||
|
FC_OPEN_RX_IFC2 = 0x0200
|
||||||
|
FC_CLOSE_TX_IFC2 = 0x0400
|
||||||
|
FC_CLOSE_RX_IFC2 = 0x0800
|
||||||
|
FC_OPEN_TX_IFC3 = 0x1000
|
||||||
|
FC_OPEN_RX_IFC3 = 0x2000
|
||||||
|
FC_CLOSE_TX_IFC3 = 0x4000
|
||||||
|
FC_CLOSE_RX_IFC3 = 0x8000
|
||||||
|
|
||||||
|
# Baud rate config
|
||||||
|
NUM_BAUD_CONFIGS = 32
|
||||||
|
BAUD_CONFIG_SIZE = 10
|
||||||
|
|
||||||
|
# Port config pin flags (CP2103/CP2104)
|
||||||
|
PORT_RI_ON = 0x0001
|
||||||
|
PORT_DCD_ON = 0x0002
|
||||||
|
PORT_DTR_ON = 0x0004
|
||||||
|
PORT_DSR_ON = 0x0008
|
||||||
|
PORT_TXD_ON = 0x0010
|
||||||
|
PORT_RXD_ON = 0x0020
|
||||||
|
PORT_RTS_ON = 0x0040
|
||||||
|
PORT_CTS_ON = 0x0080
|
||||||
|
PORT_GPIO_0_ON = 0x0100
|
||||||
|
PORT_GPIO_1_ON = 0x0200
|
||||||
|
PORT_GPIO_2_ON = 0x0400
|
||||||
|
PORT_GPIO_3_ON = 0x0800
|
||||||
|
PORT_SUSPEND_ON = 0x4000
|
||||||
|
PORT_SUSPEND_BAR_ON = 0x8000
|
||||||
|
|
||||||
|
# Enhanced function flags (CP2103/CP2104)
|
||||||
|
EF_GPIO_0_TXLED = 0x01
|
||||||
|
EF_GPIO_1_RXLED = 0x02
|
||||||
|
EF_GPIO_2_RS485 = 0x04
|
||||||
|
EF_RS485_INVERT = 0x08
|
||||||
|
EF_WEAKPULLUP = 0x10
|
||||||
|
EF_RESERVED_1 = 0x20
|
||||||
|
EF_SERIAL_DYNAMIC_SUSPEND = 0x40
|
||||||
|
EF_GPIO_DYNAMIC_SUSPEND = 0x80
|
||||||
|
|
||||||
|
# Flush buffer flag names for human-readable output
|
||||||
|
FLUSH_FLAGS_CP2104 = {
|
||||||
|
FC_OPEN_TX: "open_tx",
|
||||||
|
FC_OPEN_RX: "open_rx",
|
||||||
|
FC_CLOSE_TX: "close_tx",
|
||||||
|
FC_CLOSE_RX: "close_rx",
|
||||||
|
}
|
||||||
|
|
||||||
|
FLUSH_FLAGS_CP2105 = {
|
||||||
|
FC_OPEN_TX_SCI: "open_tx_sci",
|
||||||
|
FC_OPEN_RX_SCI: "open_rx_sci",
|
||||||
|
FC_CLOSE_TX_SCI: "close_tx_sci",
|
||||||
|
FC_CLOSE_RX_SCI: "close_rx_sci",
|
||||||
|
FC_OPEN_TX_ECI: "open_tx_eci",
|
||||||
|
FC_OPEN_RX_ECI: "open_rx_eci",
|
||||||
|
FC_CLOSE_TX_ECI: "close_tx_eci",
|
||||||
|
FC_CLOSE_RX_ECI: "close_rx_eci",
|
||||||
|
}
|
||||||
|
|
||||||
|
ENHANCED_FXN_FLAGS = {
|
||||||
|
EF_GPIO_0_TXLED: "gpio0_txled",
|
||||||
|
EF_GPIO_1_RXLED: "gpio1_rxled",
|
||||||
|
EF_GPIO_2_RS485: "gpio2_rs485",
|
||||||
|
EF_RS485_INVERT: "rs485_invert",
|
||||||
|
EF_WEAKPULLUP: "weak_pullup",
|
||||||
|
EF_SERIAL_DYNAMIC_SUSPEND: "serial_dynamic_suspend",
|
||||||
|
EF_GPIO_DYNAMIC_SUSPEND: "gpio_dynamic_suspend",
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT_PIN_FLAGS = {
|
||||||
|
PORT_RI_ON: "RI",
|
||||||
|
PORT_DCD_ON: "DCD",
|
||||||
|
PORT_DTR_ON: "DTR",
|
||||||
|
PORT_DSR_ON: "DSR",
|
||||||
|
PORT_TXD_ON: "TXD",
|
||||||
|
PORT_RXD_ON: "RXD",
|
||||||
|
PORT_RTS_ON: "RTS",
|
||||||
|
PORT_CTS_ON: "CTS",
|
||||||
|
PORT_GPIO_0_ON: "GPIO0",
|
||||||
|
PORT_GPIO_1_ON: "GPIO1",
|
||||||
|
PORT_GPIO_2_ON: "GPIO2",
|
||||||
|
PORT_GPIO_3_ON: "GPIO3",
|
||||||
|
PORT_SUSPEND_ON: "SUSPEND",
|
||||||
|
PORT_SUSPEND_BAR_ON: "SUSPEND_BAR",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def decode_bitmask(value: int, flag_map: dict) -> dict[str, bool]:
|
||||||
|
"""Decode a bitmask into a dict of named flags."""
|
||||||
|
return {name: bool(value & bit) for bit, name in flag_map.items()}
|
||||||
|
|
||||||
|
|
||||||
|
def encode_bitmask(flags: dict[str, bool], flag_map: dict) -> int:
|
||||||
|
"""Encode a dict of named flags back into a bitmask."""
|
||||||
|
name_to_bit = {name: bit for bit, name in flag_map.items()}
|
||||||
|
value = 0
|
||||||
|
for name, enabled in flags.items():
|
||||||
|
if name in name_to_bit and enabled:
|
||||||
|
value |= name_to_bit[name]
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
# --- ctypes Structures ---
|
||||||
|
|
||||||
|
class BAUD_CONFIG(Structure):
|
||||||
|
_pack_ = 1
|
||||||
|
_fields_ = [
|
||||||
|
("BaudGen", WORD),
|
||||||
|
("Timer0Reload", WORD),
|
||||||
|
("Prescaler", BYTE),
|
||||||
|
("_pad", BYTE),
|
||||||
|
("BaudRate", DWORD),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
assert ctypes.sizeof(BAUD_CONFIG) == BAUD_CONFIG_SIZE
|
||||||
|
|
||||||
|
BAUD_CONFIG_DATA = BAUD_CONFIG * NUM_BAUD_CONFIGS
|
||||||
|
|
||||||
|
|
||||||
|
class PORT_CONFIG(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
("Mode", WORD),
|
||||||
|
("Reset_Latch", WORD),
|
||||||
|
("Suspend_Latch", WORD),
|
||||||
|
("EnhancedFxn", BYTE),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class DUAL_PORT_CONFIG(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
("Mode", WORD),
|
||||||
|
("Reset_Latch", WORD),
|
||||||
|
("Suspend_Latch", WORD),
|
||||||
|
("EnhancedFxn_ECI", BYTE),
|
||||||
|
("EnhancedFxn_SCI", BYTE),
|
||||||
|
("EnhancedFxn_Device", BYTE),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class QUAD_PORT_STATE(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
("Mode_PB0", WORD),
|
||||||
|
("Mode_PB1", WORD),
|
||||||
|
("Mode_PB2", WORD),
|
||||||
|
("Mode_PB3", WORD),
|
||||||
|
("Mode_PB4", WORD),
|
||||||
|
("LowPower_PB0", WORD),
|
||||||
|
("LowPower_PB1", WORD),
|
||||||
|
("LowPower_PB2", WORD),
|
||||||
|
("LowPower_PB3", WORD),
|
||||||
|
("LowPower_PB4", WORD),
|
||||||
|
("Latch_PB0", WORD),
|
||||||
|
("Latch_PB1", WORD),
|
||||||
|
("Latch_PB2", WORD),
|
||||||
|
("Latch_PB3", WORD),
|
||||||
|
("Latch_PB4", WORD),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class QUAD_PORT_CONFIG(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
("Reset_Latch", QUAD_PORT_STATE),
|
||||||
|
("Suspend_Latch", QUAD_PORT_STATE),
|
||||||
|
("IPDelay_IFC0", BYTE),
|
||||||
|
("IPDelay_IFC1", BYTE),
|
||||||
|
("IPDelay_IFC2", BYTE),
|
||||||
|
("IPDelay_IFC3", BYTE),
|
||||||
|
("EnhancedFxn_IFC0", BYTE),
|
||||||
|
("EnhancedFxn_IFC1", BYTE),
|
||||||
|
("EnhancedFxn_IFC2", BYTE),
|
||||||
|
("EnhancedFxn_IFC3", BYTE),
|
||||||
|
("EnhancedFxn_Device", BYTE),
|
||||||
|
("ExtClk0Freq", BYTE),
|
||||||
|
("ExtClk1Freq", BYTE),
|
||||||
|
("ExtClk2Freq", BYTE),
|
||||||
|
("ExtClk3Freq", BYTE),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class FIRMWARE_T(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
("major", BYTE),
|
||||||
|
("minor", BYTE),
|
||||||
|
("build", BYTE),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class CP210xError(Exception):
|
class CP210xError(Exception):
|
||||||
"""Exception raised for CP210x library errors."""
|
"""Exception raised for CP210x library errors."""
|
||||||
@ -77,16 +297,10 @@ class CP210xError(Exception):
|
|||||||
class CP210xLibrary:
|
class CP210xLibrary:
|
||||||
"""Wrapper for the CP210x manufacturing library."""
|
"""Wrapper for the CP210x manufacturing library."""
|
||||||
|
|
||||||
def __init__(self, lib_path: Optional[str] = None):
|
def __init__(self, lib_path: str | None = None):
|
||||||
"""Load the CP210x manufacturing library.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
lib_path: Path to libcp210xmanufacturing.so, or None to search default paths.
|
|
||||||
"""
|
|
||||||
if lib_path:
|
if lib_path:
|
||||||
self._lib = ctypes.CDLL(lib_path)
|
self._lib = ctypes.CDLL(lib_path)
|
||||||
else:
|
else:
|
||||||
# Search common locations
|
|
||||||
search_paths = [
|
search_paths = [
|
||||||
"/usr/lib/libcp210xmanufacturing.so",
|
"/usr/lib/libcp210xmanufacturing.so",
|
||||||
"/usr/local/lib/libcp210xmanufacturing.so",
|
"/usr/local/lib/libcp210xmanufacturing.so",
|
||||||
@ -105,158 +319,198 @@ class CP210xLibrary:
|
|||||||
self._setup_functions()
|
self._setup_functions()
|
||||||
|
|
||||||
def _setup_functions(self):
|
def _setup_functions(self):
|
||||||
"""Set up function prototypes."""
|
"""Set up function prototypes for all library functions."""
|
||||||
# CP210x_GetNumDevices
|
lib = self._lib
|
||||||
self._lib.CP210x_GetNumDevices.argtypes = [POINTER(DWORD)]
|
|
||||||
self._lib.CP210x_GetNumDevices.restype = CP210x_STATUS
|
|
||||||
|
|
||||||
# CP210x_GetProductString
|
# --- Device enumeration ---
|
||||||
self._lib.CP210x_GetProductString.argtypes = [DWORD, c_void_p, DWORD]
|
lib.CP210x_GetNumDevices.argtypes = [POINTER(DWORD)]
|
||||||
self._lib.CP210x_GetProductString.restype = CP210x_STATUS
|
lib.CP210x_GetNumDevices.restype = CP210x_STATUS
|
||||||
|
|
||||||
# CP210x_Open
|
lib.CP210x_GetProductString.argtypes = [DWORD, c_void_p, DWORD]
|
||||||
self._lib.CP210x_Open.argtypes = [DWORD, POINTER(HANDLE)]
|
lib.CP210x_GetProductString.restype = CP210x_STATUS
|
||||||
self._lib.CP210x_Open.restype = CP210x_STATUS
|
|
||||||
|
|
||||||
# CP210x_Close
|
lib.CP210x_Open.argtypes = [DWORD, POINTER(HANDLE)]
|
||||||
self._lib.CP210x_Close.argtypes = [HANDLE]
|
lib.CP210x_Open.restype = CP210x_STATUS
|
||||||
self._lib.CP210x_Close.restype = CP210x_STATUS
|
|
||||||
|
|
||||||
# CP210x_GetPartNumber
|
lib.CP210x_Close.argtypes = [HANDLE]
|
||||||
self._lib.CP210x_GetPartNumber.argtypes = [HANDLE, POINTER(BYTE)]
|
lib.CP210x_Close.restype = CP210x_STATUS
|
||||||
self._lib.CP210x_GetPartNumber.restype = CP210x_STATUS
|
|
||||||
|
|
||||||
# CP210x_GetDeviceVid
|
# --- Getters (scalars) ---
|
||||||
self._lib.CP210x_GetDeviceVid.argtypes = [HANDLE, POINTER(WORD)]
|
lib.CP210x_GetPartNumber.argtypes = [HANDLE, POINTER(BYTE)]
|
||||||
self._lib.CP210x_GetDeviceVid.restype = CP210x_STATUS
|
lib.CP210x_GetPartNumber.restype = CP210x_STATUS
|
||||||
|
|
||||||
# CP210x_GetDevicePid
|
lib.CP210x_GetDeviceVid.argtypes = [HANDLE, POINTER(WORD)]
|
||||||
self._lib.CP210x_GetDevicePid.argtypes = [HANDLE, POINTER(WORD)]
|
lib.CP210x_GetDeviceVid.restype = CP210x_STATUS
|
||||||
self._lib.CP210x_GetDevicePid.restype = CP210x_STATUS
|
|
||||||
|
|
||||||
# CP210x_GetDeviceManufacturerString
|
lib.CP210x_GetDevicePid.argtypes = [HANDLE, POINTER(WORD)]
|
||||||
self._lib.CP210x_GetDeviceManufacturerString.argtypes = [HANDLE, c_void_p, POINTER(BYTE), BOOL]
|
lib.CP210x_GetDevicePid.restype = CP210x_STATUS
|
||||||
self._lib.CP210x_GetDeviceManufacturerString.restype = CP210x_STATUS
|
|
||||||
|
|
||||||
# CP210x_GetDeviceProductString
|
lib.CP210x_GetDeviceManufacturerString.argtypes = [HANDLE, c_void_p, POINTER(BYTE), BOOL]
|
||||||
self._lib.CP210x_GetDeviceProductString.argtypes = [HANDLE, c_void_p, POINTER(BYTE), BOOL]
|
lib.CP210x_GetDeviceManufacturerString.restype = CP210x_STATUS
|
||||||
self._lib.CP210x_GetDeviceProductString.restype = CP210x_STATUS
|
|
||||||
|
|
||||||
# CP210x_GetDeviceSerialNumber
|
lib.CP210x_GetDeviceProductString.argtypes = [HANDLE, c_void_p, POINTER(BYTE), BOOL]
|
||||||
self._lib.CP210x_GetDeviceSerialNumber.argtypes = [HANDLE, c_void_p, POINTER(BYTE), BOOL]
|
lib.CP210x_GetDeviceProductString.restype = CP210x_STATUS
|
||||||
self._lib.CP210x_GetDeviceSerialNumber.restype = CP210x_STATUS
|
|
||||||
|
|
||||||
# CP210x_SetManufacturerString
|
lib.CP210x_GetDeviceSerialNumber.argtypes = [HANDLE, c_void_p, POINTER(BYTE), BOOL]
|
||||||
self._lib.CP210x_SetManufacturerString.argtypes = [HANDLE, c_void_p, BYTE, BOOL]
|
lib.CP210x_GetDeviceSerialNumber.restype = CP210x_STATUS
|
||||||
self._lib.CP210x_SetManufacturerString.restype = CP210x_STATUS
|
|
||||||
|
|
||||||
# CP210x_SetProductString
|
lib.CP210x_GetDeviceInterfaceString.argtypes = [HANDLE, BYTE, c_void_p, POINTER(BYTE), BOOL]
|
||||||
self._lib.CP210x_SetProductString.argtypes = [HANDLE, c_void_p, BYTE, BOOL]
|
lib.CP210x_GetDeviceInterfaceString.restype = CP210x_STATUS
|
||||||
self._lib.CP210x_SetProductString.restype = CP210x_STATUS
|
|
||||||
|
|
||||||
# CP210x_SetSerialNumber
|
lib.CP210x_GetMaxPower.argtypes = [HANDLE, POINTER(BYTE)]
|
||||||
self._lib.CP210x_SetSerialNumber.argtypes = [HANDLE, c_void_p, BYTE, BOOL]
|
lib.CP210x_GetMaxPower.restype = CP210x_STATUS
|
||||||
self._lib.CP210x_SetSerialNumber.restype = CP210x_STATUS
|
|
||||||
|
|
||||||
# CP210x_GetMaxPower
|
lib.CP210x_GetSelfPower.argtypes = [HANDLE, POINTER(BOOL)]
|
||||||
self._lib.CP210x_GetMaxPower.argtypes = [HANDLE, POINTER(BYTE)]
|
lib.CP210x_GetSelfPower.restype = CP210x_STATUS
|
||||||
self._lib.CP210x_GetMaxPower.restype = CP210x_STATUS
|
|
||||||
|
|
||||||
# CP210x_SetMaxPower
|
lib.CP210x_GetDeviceVersion.argtypes = [HANDLE, POINTER(WORD)]
|
||||||
self._lib.CP210x_SetMaxPower.argtypes = [HANDLE, BYTE]
|
lib.CP210x_GetDeviceVersion.restype = CP210x_STATUS
|
||||||
self._lib.CP210x_SetMaxPower.restype = CP210x_STATUS
|
|
||||||
|
|
||||||
# CP210x_GetSelfPower
|
lib.CP210x_GetFlushBufferConfig.argtypes = [HANDLE, POINTER(WORD)]
|
||||||
self._lib.CP210x_GetSelfPower.argtypes = [HANDLE, POINTER(BOOL)]
|
lib.CP210x_GetFlushBufferConfig.restype = CP210x_STATUS
|
||||||
self._lib.CP210x_GetSelfPower.restype = CP210x_STATUS
|
|
||||||
|
|
||||||
# CP210x_SetSelfPower
|
lib.CP210x_GetDeviceMode.argtypes = [HANDLE, POINTER(BYTE), POINTER(BYTE)]
|
||||||
self._lib.CP210x_SetSelfPower.argtypes = [HANDLE, BOOL]
|
lib.CP210x_GetDeviceMode.restype = CP210x_STATUS
|
||||||
self._lib.CP210x_SetSelfPower.restype = CP210x_STATUS
|
|
||||||
|
|
||||||
# CP210x_GetDeviceVersion
|
lib.CP210x_GetLockValue.argtypes = [HANDLE, POINTER(BYTE)]
|
||||||
self._lib.CP210x_GetDeviceVersion.argtypes = [HANDLE, POINTER(WORD)]
|
lib.CP210x_GetLockValue.restype = CP210x_STATUS
|
||||||
self._lib.CP210x_GetDeviceVersion.restype = CP210x_STATUS
|
|
||||||
|
|
||||||
# CP210x_SetDeviceVersion
|
lib.CP210x_GetFirmwareVersion.argtypes = [HANDLE, POINTER(FIRMWARE_T)]
|
||||||
self._lib.CP210x_SetDeviceVersion.argtypes = [HANDLE, WORD]
|
lib.CP210x_GetFirmwareVersion.restype = CP210x_STATUS
|
||||||
self._lib.CP210x_SetDeviceVersion.restype = CP210x_STATUS
|
|
||||||
|
|
||||||
# CP210x_GetLockValue
|
# --- Setters (scalars) ---
|
||||||
self._lib.CP210x_GetLockValue.argtypes = [HANDLE, POINTER(BYTE)]
|
lib.CP210x_SetVid.argtypes = [HANDLE, WORD]
|
||||||
self._lib.CP210x_GetLockValue.restype = CP210x_STATUS
|
lib.CP210x_SetVid.restype = CP210x_STATUS
|
||||||
|
|
||||||
# CP210x_SetLockValue
|
lib.CP210x_SetPid.argtypes = [HANDLE, WORD]
|
||||||
self._lib.CP210x_SetLockValue.argtypes = [HANDLE]
|
lib.CP210x_SetPid.restype = CP210x_STATUS
|
||||||
self._lib.CP210x_SetLockValue.restype = CP210x_STATUS
|
|
||||||
|
|
||||||
# CP210x_Reset
|
lib.CP210x_SetManufacturerString.argtypes = [HANDLE, c_void_p, BYTE, BOOL]
|
||||||
self._lib.CP210x_Reset.argtypes = [HANDLE]
|
lib.CP210x_SetManufacturerString.restype = CP210x_STATUS
|
||||||
self._lib.CP210x_Reset.restype = CP210x_STATUS
|
|
||||||
|
lib.CP210x_SetProductString.argtypes = [HANDLE, c_void_p, BYTE, BOOL]
|
||||||
|
lib.CP210x_SetProductString.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_SetSerialNumber.argtypes = [HANDLE, c_void_p, BYTE, BOOL]
|
||||||
|
lib.CP210x_SetSerialNumber.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_SetInterfaceString.argtypes = [HANDLE, BYTE, c_void_p, BYTE, BOOL]
|
||||||
|
lib.CP210x_SetInterfaceString.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_SetMaxPower.argtypes = [HANDLE, BYTE]
|
||||||
|
lib.CP210x_SetMaxPower.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_SetSelfPower.argtypes = [HANDLE, BOOL]
|
||||||
|
lib.CP210x_SetSelfPower.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_SetDeviceVersion.argtypes = [HANDLE, WORD]
|
||||||
|
lib.CP210x_SetDeviceVersion.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_SetFlushBufferConfig.argtypes = [HANDLE, WORD]
|
||||||
|
lib.CP210x_SetFlushBufferConfig.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_SetDeviceMode.argtypes = [HANDLE, BYTE, BYTE]
|
||||||
|
lib.CP210x_SetDeviceMode.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_SetLockValue.argtypes = [HANDLE]
|
||||||
|
lib.CP210x_SetLockValue.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
# --- Struct-based configs ---
|
||||||
|
lib.CP210x_GetBaudRateConfig.argtypes = [HANDLE, POINTER(BAUD_CONFIG)]
|
||||||
|
lib.CP210x_GetBaudRateConfig.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_SetBaudRateConfig.argtypes = [HANDLE, POINTER(BAUD_CONFIG)]
|
||||||
|
lib.CP210x_SetBaudRateConfig.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_GetPortConfig.argtypes = [HANDLE, POINTER(PORT_CONFIG)]
|
||||||
|
lib.CP210x_GetPortConfig.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_SetPortConfig.argtypes = [HANDLE, POINTER(PORT_CONFIG)]
|
||||||
|
lib.CP210x_SetPortConfig.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_GetDualPortConfig.argtypes = [HANDLE, POINTER(DUAL_PORT_CONFIG)]
|
||||||
|
lib.CP210x_GetDualPortConfig.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_SetDualPortConfig.argtypes = [HANDLE, POINTER(DUAL_PORT_CONFIG)]
|
||||||
|
lib.CP210x_SetDualPortConfig.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_GetQuadPortConfig.argtypes = [HANDLE, POINTER(QUAD_PORT_CONFIG)]
|
||||||
|
lib.CP210x_GetQuadPortConfig.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_SetQuadPortConfig.argtypes = [HANDLE, POINTER(QUAD_PORT_CONFIG)]
|
||||||
|
lib.CP210x_SetQuadPortConfig.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
# --- Advanced / raw ---
|
||||||
|
lib.CP210x_GetConfig.argtypes = [HANDLE, POINTER(BYTE), WORD]
|
||||||
|
lib.CP210x_GetConfig.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_SetConfig.argtypes = [HANDLE, POINTER(BYTE), WORD]
|
||||||
|
lib.CP210x_SetConfig.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_GetGeneric.argtypes = [HANDLE, POINTER(BYTE), WORD]
|
||||||
|
lib.CP210x_GetGeneric.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_SetGeneric.argtypes = [HANDLE, POINTER(BYTE), WORD]
|
||||||
|
lib.CP210x_SetGeneric.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_CreateHexFile.argtypes = [HANDLE, c_char_p]
|
||||||
|
lib.CP210x_CreateHexFile.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_UpdateFirmware.argtypes = [HANDLE]
|
||||||
|
lib.CP210x_UpdateFirmware.restype = CP210x_STATUS
|
||||||
|
|
||||||
|
lib.CP210x_Reset.argtypes = [HANDLE]
|
||||||
|
lib.CP210x_Reset.restype = CP210x_STATUS
|
||||||
|
|
||||||
def _check_status(self, status: int, context: str = ""):
|
def _check_status(self, status: int, context: str = ""):
|
||||||
"""Raise exception if status indicates error."""
|
|
||||||
if status != CP210x_SUCCESS:
|
if status != CP210x_SUCCESS:
|
||||||
raise CP210xError(status, context)
|
raise CP210xError(status, context)
|
||||||
|
|
||||||
|
# --- Device enumeration ---
|
||||||
|
|
||||||
def get_num_devices(self) -> int:
|
def get_num_devices(self) -> int:
|
||||||
"""Get the number of connected CP210x devices."""
|
|
||||||
num = DWORD()
|
num = DWORD()
|
||||||
status = self._lib.CP210x_GetNumDevices(byref(num))
|
status = self._lib.CP210x_GetNumDevices(byref(num))
|
||||||
self._check_status(status, "GetNumDevices")
|
self._check_status(status, "GetNumDevices")
|
||||||
return num.value
|
return num.value
|
||||||
|
|
||||||
def get_product_string(self, device_index: int, flag: int = RETURN_DESCRIPTION) -> str:
|
def get_product_string(self, device_index: int, flag: int = RETURN_DESCRIPTION) -> str:
|
||||||
"""Get device string without opening the device.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
device_index: Zero-based device index
|
|
||||||
flag: What to return - RETURN_SERIAL_NUMBER, RETURN_DESCRIPTION, or RETURN_FULL_PATH
|
|
||||||
"""
|
|
||||||
buf = ctypes.create_string_buffer(MAX_DEVICE_STRLEN)
|
buf = ctypes.create_string_buffer(MAX_DEVICE_STRLEN)
|
||||||
status = self._lib.CP210x_GetProductString(DWORD(device_index), buf, DWORD(flag))
|
status = self._lib.CP210x_GetProductString(DWORD(device_index), buf, DWORD(flag))
|
||||||
self._check_status(status, "GetProductString")
|
self._check_status(status, "GetProductString")
|
||||||
return buf.value.decode('utf-8', errors='replace')
|
return buf.value.decode('utf-8', errors='replace')
|
||||||
|
|
||||||
def open(self, device_index: int) -> HANDLE:
|
def open(self, device_index: int) -> HANDLE:
|
||||||
"""Open a device by index."""
|
|
||||||
handle = HANDLE()
|
handle = HANDLE()
|
||||||
status = self._lib.CP210x_Open(DWORD(device_index), byref(handle))
|
status = self._lib.CP210x_Open(DWORD(device_index), byref(handle))
|
||||||
self._check_status(status, f"Open device {device_index}")
|
self._check_status(status, f"Open device {device_index}")
|
||||||
return handle
|
return handle
|
||||||
|
|
||||||
def close(self, handle: HANDLE):
|
def close(self, handle: HANDLE):
|
||||||
"""Close an open device."""
|
|
||||||
status = self._lib.CP210x_Close(handle)
|
status = self._lib.CP210x_Close(handle)
|
||||||
self._check_status(status, "Close")
|
self._check_status(status, "Close")
|
||||||
|
|
||||||
def get_part_number(self, handle: HANDLE) -> tuple[int, str]:
|
# --- Scalar getters ---
|
||||||
"""Get the part number of an open device.
|
|
||||||
|
|
||||||
Returns:
|
def get_part_number(self, handle: HANDLE) -> tuple[int, str]:
|
||||||
Tuple of (part_number_code, part_name)
|
|
||||||
"""
|
|
||||||
part = BYTE()
|
part = BYTE()
|
||||||
status = self._lib.CP210x_GetPartNumber(handle, byref(part))
|
status = self._lib.CP210x_GetPartNumber(handle, byref(part))
|
||||||
self._check_status(status, "GetPartNumber")
|
self._check_status(status, "GetPartNumber")
|
||||||
return part.value, PART_NUMBERS.get(part.value, f"Unknown (0x{part.value:02X})")
|
return part.value, PART_NUMBERS.get(part.value, f"Unknown (0x{part.value:02X})")
|
||||||
|
|
||||||
def get_device_vid(self, handle: HANDLE) -> int:
|
def get_device_vid(self, handle: HANDLE) -> int:
|
||||||
"""Get the USB Vendor ID."""
|
|
||||||
vid = WORD()
|
vid = WORD()
|
||||||
status = self._lib.CP210x_GetDeviceVid(handle, byref(vid))
|
status = self._lib.CP210x_GetDeviceVid(handle, byref(vid))
|
||||||
self._check_status(status, "GetDeviceVid")
|
self._check_status(status, "GetDeviceVid")
|
||||||
return vid.value
|
return vid.value
|
||||||
|
|
||||||
def get_device_pid(self, handle: HANDLE) -> int:
|
def get_device_pid(self, handle: HANDLE) -> int:
|
||||||
"""Get the USB Product ID."""
|
|
||||||
pid = WORD()
|
pid = WORD()
|
||||||
status = self._lib.CP210x_GetDevicePid(handle, byref(pid))
|
status = self._lib.CP210x_GetDevicePid(handle, byref(pid))
|
||||||
self._check_status(status, "GetDevicePid")
|
self._check_status(status, "GetDevicePid")
|
||||||
return pid.value
|
return pid.value
|
||||||
|
|
||||||
def get_manufacturer_string(self, handle: HANDLE) -> str:
|
def get_manufacturer_string(self, handle: HANDLE) -> str:
|
||||||
"""Get the manufacturer string."""
|
|
||||||
buf = ctypes.create_string_buffer(MAX_MANUFACTURER_STRLEN)
|
buf = ctypes.create_string_buffer(MAX_MANUFACTURER_STRLEN)
|
||||||
length = BYTE()
|
length = BYTE()
|
||||||
status = self._lib.CP210x_GetDeviceManufacturerString(handle, buf, byref(length), BOOL(1))
|
status = self._lib.CP210x_GetDeviceManufacturerString(handle, buf, byref(length), BOOL(1))
|
||||||
@ -264,7 +518,6 @@ class CP210xLibrary:
|
|||||||
return buf.value.decode('utf-8', errors='replace')
|
return buf.value.decode('utf-8', errors='replace')
|
||||||
|
|
||||||
def get_product_string_device(self, handle: HANDLE) -> str:
|
def get_product_string_device(self, handle: HANDLE) -> str:
|
||||||
"""Get the product string from an open device."""
|
|
||||||
buf = ctypes.create_string_buffer(MAX_PRODUCT_STRLEN)
|
buf = ctypes.create_string_buffer(MAX_PRODUCT_STRLEN)
|
||||||
length = BYTE()
|
length = BYTE()
|
||||||
status = self._lib.CP210x_GetDeviceProductString(handle, buf, byref(length), BOOL(1))
|
status = self._lib.CP210x_GetDeviceProductString(handle, buf, byref(length), BOOL(1))
|
||||||
@ -272,88 +525,282 @@ class CP210xLibrary:
|
|||||||
return buf.value.decode('utf-8', errors='replace')
|
return buf.value.decode('utf-8', errors='replace')
|
||||||
|
|
||||||
def get_serial_number(self, handle: HANDLE) -> str:
|
def get_serial_number(self, handle: HANDLE) -> str:
|
||||||
"""Get the serial number."""
|
|
||||||
buf = ctypes.create_string_buffer(MAX_SERIAL_STRLEN)
|
buf = ctypes.create_string_buffer(MAX_SERIAL_STRLEN)
|
||||||
length = BYTE()
|
length = BYTE()
|
||||||
status = self._lib.CP210x_GetDeviceSerialNumber(handle, buf, byref(length), BOOL(1))
|
status = self._lib.CP210x_GetDeviceSerialNumber(handle, buf, byref(length), BOOL(1))
|
||||||
self._check_status(status, "GetDeviceSerialNumber")
|
self._check_status(status, "GetDeviceSerialNumber")
|
||||||
return buf.value.decode('utf-8', errors='replace')
|
return buf.value.decode('utf-8', errors='replace')
|
||||||
|
|
||||||
def set_manufacturer_string(self, handle: HANDLE, manufacturer: str):
|
def get_interface_string(self, handle: HANDLE, interface_number: int) -> str:
|
||||||
"""Set the manufacturer string (max 45 chars)."""
|
buf = ctypes.create_string_buffer(CP2108_MAX_INTERFACE_STRLEN)
|
||||||
data = manufacturer.encode('utf-8')[:MAX_MANUFACTURER_STRLEN]
|
length = BYTE()
|
||||||
status = self._lib.CP210x_SetManufacturerString(handle, data, BYTE(len(data)), BOOL(1))
|
status = self._lib.CP210x_GetDeviceInterfaceString(
|
||||||
self._check_status(status, "SetManufacturerString")
|
handle, BYTE(interface_number), buf, byref(length), BOOL(1)
|
||||||
|
)
|
||||||
def set_product_string(self, handle: HANDLE, product: str):
|
self._check_status(status, f"GetDeviceInterfaceString(ifc={interface_number})")
|
||||||
"""Set the product string (max 126 chars)."""
|
return buf.value.decode('utf-8', errors='replace')
|
||||||
data = product.encode('utf-8')[:MAX_PRODUCT_STRLEN]
|
|
||||||
status = self._lib.CP210x_SetProductString(handle, data, BYTE(len(data)), BOOL(1))
|
|
||||||
self._check_status(status, "SetProductString")
|
|
||||||
|
|
||||||
def set_serial_number(self, handle: HANDLE, serial: str):
|
|
||||||
"""Set the serial number (max 63 chars)."""
|
|
||||||
data = serial.encode('utf-8')[:MAX_SERIAL_STRLEN]
|
|
||||||
status = self._lib.CP210x_SetSerialNumber(handle, data, BYTE(len(data)), BOOL(1))
|
|
||||||
self._check_status(status, "SetSerialNumber")
|
|
||||||
|
|
||||||
def get_max_power(self, handle: HANDLE) -> int:
|
def get_max_power(self, handle: HANDLE) -> int:
|
||||||
"""Get max power in units of 2mA (multiply by 2 for mA)."""
|
|
||||||
power = BYTE()
|
power = BYTE()
|
||||||
status = self._lib.CP210x_GetMaxPower(handle, byref(power))
|
status = self._lib.CP210x_GetMaxPower(handle, byref(power))
|
||||||
self._check_status(status, "GetMaxPower")
|
self._check_status(status, "GetMaxPower")
|
||||||
return power.value
|
return power.value
|
||||||
|
|
||||||
def set_max_power(self, handle: HANDLE, power_2ma: int):
|
|
||||||
"""Set max power in units of 2mA (e.g., 50 = 100mA)."""
|
|
||||||
if power_2ma > 250:
|
|
||||||
raise ValueError("Max power cannot exceed 250 (500mA)")
|
|
||||||
status = self._lib.CP210x_SetMaxPower(handle, BYTE(power_2ma))
|
|
||||||
self._check_status(status, "SetMaxPower")
|
|
||||||
|
|
||||||
def get_self_power(self, handle: HANDLE) -> bool:
|
def get_self_power(self, handle: HANDLE) -> bool:
|
||||||
"""Check if device is self-powered."""
|
|
||||||
self_power = BOOL()
|
self_power = BOOL()
|
||||||
status = self._lib.CP210x_GetSelfPower(handle, byref(self_power))
|
status = self._lib.CP210x_GetSelfPower(handle, byref(self_power))
|
||||||
self._check_status(status, "GetSelfPower")
|
self._check_status(status, "GetSelfPower")
|
||||||
return bool(self_power.value)
|
return bool(self_power.value)
|
||||||
|
|
||||||
def set_self_power(self, handle: HANDLE, self_powered: bool):
|
|
||||||
"""Set whether device is self-powered."""
|
|
||||||
status = self._lib.CP210x_SetSelfPower(handle, BOOL(1 if self_powered else 0))
|
|
||||||
self._check_status(status, "SetSelfPower")
|
|
||||||
|
|
||||||
def get_device_version(self, handle: HANDLE) -> int:
|
def get_device_version(self, handle: HANDLE) -> int:
|
||||||
"""Get device version (bcdDevice)."""
|
|
||||||
version = WORD()
|
version = WORD()
|
||||||
status = self._lib.CP210x_GetDeviceVersion(handle, byref(version))
|
status = self._lib.CP210x_GetDeviceVersion(handle, byref(version))
|
||||||
self._check_status(status, "GetDeviceVersion")
|
self._check_status(status, "GetDeviceVersion")
|
||||||
return version.value
|
return version.value
|
||||||
|
|
||||||
def set_device_version(self, handle: HANDLE, version: int):
|
def get_flush_buffer_config(self, handle: HANDLE) -> int:
|
||||||
"""Set device version (bcdDevice)."""
|
config = WORD()
|
||||||
status = self._lib.CP210x_SetDeviceVersion(handle, WORD(version))
|
status = self._lib.CP210x_GetFlushBufferConfig(handle, byref(config))
|
||||||
self._check_status(status, "SetDeviceVersion")
|
self._check_status(status, "GetFlushBufferConfig")
|
||||||
|
return config.value
|
||||||
|
|
||||||
|
def get_device_mode(self, handle: HANDLE) -> tuple[int, int]:
|
||||||
|
eci = BYTE()
|
||||||
|
sci = BYTE()
|
||||||
|
status = self._lib.CP210x_GetDeviceMode(handle, byref(eci), byref(sci))
|
||||||
|
self._check_status(status, "GetDeviceMode")
|
||||||
|
return eci.value, sci.value
|
||||||
|
|
||||||
def get_lock_value(self, handle: HANDLE) -> int:
|
def get_lock_value(self, handle: HANDLE) -> int:
|
||||||
"""Get lock value (0 = unlocked, 1-255 = locked)."""
|
|
||||||
lock = BYTE()
|
lock = BYTE()
|
||||||
status = self._lib.CP210x_GetLockValue(handle, byref(lock))
|
status = self._lib.CP210x_GetLockValue(handle, byref(lock))
|
||||||
self._check_status(status, "GetLockValue")
|
self._check_status(status, "GetLockValue")
|
||||||
return lock.value
|
return lock.value
|
||||||
|
|
||||||
|
def get_firmware_version(self, handle: HANDLE) -> tuple[int, int, int]:
|
||||||
|
fw = FIRMWARE_T()
|
||||||
|
status = self._lib.CP210x_GetFirmwareVersion(handle, byref(fw))
|
||||||
|
self._check_status(status, "GetFirmwareVersion")
|
||||||
|
return fw.major, fw.minor, fw.build
|
||||||
|
|
||||||
|
# --- Scalar setters ---
|
||||||
|
|
||||||
|
def set_vid(self, handle: HANDLE, vid: int):
|
||||||
|
status = self._lib.CP210x_SetVid(handle, WORD(vid))
|
||||||
|
self._check_status(status, "SetVid")
|
||||||
|
|
||||||
|
def set_pid(self, handle: HANDLE, pid: int):
|
||||||
|
status = self._lib.CP210x_SetPid(handle, WORD(pid))
|
||||||
|
self._check_status(status, "SetPid")
|
||||||
|
|
||||||
|
def set_manufacturer_string(self, handle: HANDLE, manufacturer: str):
|
||||||
|
data = manufacturer.encode('utf-8')[:MAX_MANUFACTURER_STRLEN]
|
||||||
|
status = self._lib.CP210x_SetManufacturerString(handle, data, BYTE(len(data)), BOOL(1))
|
||||||
|
self._check_status(status, "SetManufacturerString")
|
||||||
|
|
||||||
|
def set_product_string(self, handle: HANDLE, product: str):
|
||||||
|
data = product.encode('utf-8')[:MAX_PRODUCT_STRLEN]
|
||||||
|
status = self._lib.CP210x_SetProductString(handle, data, BYTE(len(data)), BOOL(1))
|
||||||
|
self._check_status(status, "SetProductString")
|
||||||
|
|
||||||
|
def set_serial_number(self, handle: HANDLE, serial: str):
|
||||||
|
data = serial.encode('utf-8')[:MAX_SERIAL_STRLEN]
|
||||||
|
status = self._lib.CP210x_SetSerialNumber(handle, data, BYTE(len(data)), BOOL(1))
|
||||||
|
self._check_status(status, "SetSerialNumber")
|
||||||
|
|
||||||
|
def set_interface_string(self, handle: HANDLE, interface_number: int, value: str):
|
||||||
|
data = value.encode('utf-8')[:CP2108_MAX_INTERFACE_STRLEN]
|
||||||
|
status = self._lib.CP210x_SetInterfaceString(
|
||||||
|
handle, BYTE(interface_number), data, BYTE(len(data)), BOOL(1)
|
||||||
|
)
|
||||||
|
self._check_status(status, f"SetInterfaceString(ifc={interface_number})")
|
||||||
|
|
||||||
|
def set_max_power(self, handle: HANDLE, power_2ma: int):
|
||||||
|
if power_2ma > 250:
|
||||||
|
raise ValueError("Max power cannot exceed 250 (500mA)")
|
||||||
|
status = self._lib.CP210x_SetMaxPower(handle, BYTE(power_2ma))
|
||||||
|
self._check_status(status, "SetMaxPower")
|
||||||
|
|
||||||
|
def set_self_power(self, handle: HANDLE, self_powered: bool):
|
||||||
|
status = self._lib.CP210x_SetSelfPower(handle, BOOL(1 if self_powered else 0))
|
||||||
|
self._check_status(status, "SetSelfPower")
|
||||||
|
|
||||||
|
def set_device_version(self, handle: HANDLE, version: int):
|
||||||
|
status = self._lib.CP210x_SetDeviceVersion(handle, WORD(version))
|
||||||
|
self._check_status(status, "SetDeviceVersion")
|
||||||
|
|
||||||
|
def set_flush_buffer_config(self, handle: HANDLE, config: int):
|
||||||
|
status = self._lib.CP210x_SetFlushBufferConfig(handle, WORD(config))
|
||||||
|
self._check_status(status, "SetFlushBufferConfig")
|
||||||
|
|
||||||
|
def set_device_mode(self, handle: HANDLE, eci_mode: int, sci_mode: int):
|
||||||
|
status = self._lib.CP210x_SetDeviceMode(handle, BYTE(eci_mode), BYTE(sci_mode))
|
||||||
|
self._check_status(status, "SetDeviceMode")
|
||||||
|
|
||||||
def lock_device(self, handle: HANDLE):
|
def lock_device(self, handle: HANDLE):
|
||||||
"""Lock the device (PERMANENT - cannot be undone!)."""
|
|
||||||
status = self._lib.CP210x_SetLockValue(handle)
|
status = self._lib.CP210x_SetLockValue(handle)
|
||||||
self._check_status(status, "SetLockValue")
|
self._check_status(status, "SetLockValue")
|
||||||
|
|
||||||
|
# --- Struct-based configs ---
|
||||||
|
|
||||||
|
def get_baud_rate_config(self, handle: HANDLE) -> list[dict]:
|
||||||
|
data = BAUD_CONFIG_DATA()
|
||||||
|
status = self._lib.CP210x_GetBaudRateConfig(handle, data)
|
||||||
|
self._check_status(status, "GetBaudRateConfig")
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"index": i,
|
||||||
|
"baud_gen": data[i].BaudGen,
|
||||||
|
"timer0_reload": data[i].Timer0Reload,
|
||||||
|
"prescaler": data[i].Prescaler,
|
||||||
|
"baud_rate": data[i].BaudRate,
|
||||||
|
}
|
||||||
|
for i in range(NUM_BAUD_CONFIGS)
|
||||||
|
]
|
||||||
|
|
||||||
|
def set_baud_rate_config(self, handle: HANDLE, configs: list[dict]):
|
||||||
|
if len(configs) != NUM_BAUD_CONFIGS:
|
||||||
|
raise ValueError(f"Expected {NUM_BAUD_CONFIGS} baud configs, got {len(configs)}")
|
||||||
|
data = BAUD_CONFIG_DATA()
|
||||||
|
for i, cfg in enumerate(configs):
|
||||||
|
data[i].BaudGen = cfg["baud_gen"]
|
||||||
|
data[i].Timer0Reload = cfg["timer0_reload"]
|
||||||
|
data[i].Prescaler = cfg["prescaler"]
|
||||||
|
data[i]._pad = 0
|
||||||
|
data[i].BaudRate = cfg["baud_rate"]
|
||||||
|
status = self._lib.CP210x_SetBaudRateConfig(handle, data)
|
||||||
|
self._check_status(status, "SetBaudRateConfig")
|
||||||
|
|
||||||
|
def get_port_config(self, handle: HANDLE) -> dict:
|
||||||
|
cfg = PORT_CONFIG()
|
||||||
|
status = self._lib.CP210x_GetPortConfig(handle, byref(cfg))
|
||||||
|
self._check_status(status, "GetPortConfig")
|
||||||
|
return {
|
||||||
|
"mode": cfg.Mode,
|
||||||
|
"reset_latch": cfg.Reset_Latch,
|
||||||
|
"suspend_latch": cfg.Suspend_Latch,
|
||||||
|
"enhanced_fxn": cfg.EnhancedFxn,
|
||||||
|
}
|
||||||
|
|
||||||
|
def set_port_config(self, handle: HANDLE, mode: int, reset_latch: int, suspend_latch: int, enhanced_fxn: int):
|
||||||
|
cfg = PORT_CONFIG()
|
||||||
|
cfg.Mode = mode
|
||||||
|
cfg.Reset_Latch = reset_latch
|
||||||
|
cfg.Suspend_Latch = suspend_latch
|
||||||
|
cfg.EnhancedFxn = enhanced_fxn
|
||||||
|
status = self._lib.CP210x_SetPortConfig(handle, byref(cfg))
|
||||||
|
self._check_status(status, "SetPortConfig")
|
||||||
|
|
||||||
|
def get_dual_port_config(self, handle: HANDLE) -> dict:
|
||||||
|
cfg = DUAL_PORT_CONFIG()
|
||||||
|
status = self._lib.CP210x_GetDualPortConfig(handle, byref(cfg))
|
||||||
|
self._check_status(status, "GetDualPortConfig")
|
||||||
|
return {
|
||||||
|
"mode": cfg.Mode,
|
||||||
|
"reset_latch": cfg.Reset_Latch,
|
||||||
|
"suspend_latch": cfg.Suspend_Latch,
|
||||||
|
"enhanced_fxn_eci": cfg.EnhancedFxn_ECI,
|
||||||
|
"enhanced_fxn_sci": cfg.EnhancedFxn_SCI,
|
||||||
|
"enhanced_fxn_device": cfg.EnhancedFxn_Device,
|
||||||
|
}
|
||||||
|
|
||||||
|
def set_dual_port_config(self, handle: HANDLE, mode: int, reset_latch: int,
|
||||||
|
suspend_latch: int, enhanced_fxn_eci: int,
|
||||||
|
enhanced_fxn_sci: int, enhanced_fxn_device: int):
|
||||||
|
cfg = DUAL_PORT_CONFIG()
|
||||||
|
cfg.Mode = mode
|
||||||
|
cfg.Reset_Latch = reset_latch
|
||||||
|
cfg.Suspend_Latch = suspend_latch
|
||||||
|
cfg.EnhancedFxn_ECI = enhanced_fxn_eci
|
||||||
|
cfg.EnhancedFxn_SCI = enhanced_fxn_sci
|
||||||
|
cfg.EnhancedFxn_Device = enhanced_fxn_device
|
||||||
|
status = self._lib.CP210x_SetDualPortConfig(handle, byref(cfg))
|
||||||
|
self._check_status(status, "SetDualPortConfig")
|
||||||
|
|
||||||
|
def _quad_port_state_to_dict(self, qps: QUAD_PORT_STATE) -> dict:
|
||||||
|
return {
|
||||||
|
f"pb{i}": {"mode": getattr(qps, f"Mode_PB{i}"),
|
||||||
|
"low_power": getattr(qps, f"LowPower_PB{i}"),
|
||||||
|
"latch": getattr(qps, f"Latch_PB{i}")}
|
||||||
|
for i in range(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_quad_port_config(self, handle: HANDLE) -> dict:
|
||||||
|
cfg = QUAD_PORT_CONFIG()
|
||||||
|
status = self._lib.CP210x_GetQuadPortConfig(handle, byref(cfg))
|
||||||
|
self._check_status(status, "GetQuadPortConfig")
|
||||||
|
return {
|
||||||
|
"reset_latch": self._quad_port_state_to_dict(cfg.Reset_Latch),
|
||||||
|
"suspend_latch": self._quad_port_state_to_dict(cfg.Suspend_Latch),
|
||||||
|
"ip_delay": [cfg.IPDelay_IFC0, cfg.IPDelay_IFC1, cfg.IPDelay_IFC2, cfg.IPDelay_IFC3],
|
||||||
|
"enhanced_fxn": [cfg.EnhancedFxn_IFC0, cfg.EnhancedFxn_IFC1,
|
||||||
|
cfg.EnhancedFxn_IFC2, cfg.EnhancedFxn_IFC3],
|
||||||
|
"enhanced_fxn_device": cfg.EnhancedFxn_Device,
|
||||||
|
"ext_clk_freq": [cfg.ExtClk0Freq, cfg.ExtClk1Freq,
|
||||||
|
cfg.ExtClk2Freq, cfg.ExtClk3Freq],
|
||||||
|
}
|
||||||
|
|
||||||
|
def set_quad_port_config(self, handle: HANDLE, config_dict: dict):
|
||||||
|
cfg = QUAD_PORT_CONFIG()
|
||||||
|
|
||||||
|
for state_name in ("reset_latch", "suspend_latch"):
|
||||||
|
state = getattr(cfg, state_name.title().replace("_l", "_L"))
|
||||||
|
src = config_dict[state_name]
|
||||||
|
for i in range(5):
|
||||||
|
pb = src[f"pb{i}"]
|
||||||
|
setattr(state, f"Mode_PB{i}", pb["mode"])
|
||||||
|
setattr(state, f"LowPower_PB{i}", pb["low_power"])
|
||||||
|
setattr(state, f"Latch_PB{i}", pb["latch"])
|
||||||
|
|
||||||
|
for i in range(4):
|
||||||
|
setattr(cfg, f"IPDelay_IFC{i}", config_dict["ip_delay"][i])
|
||||||
|
setattr(cfg, f"EnhancedFxn_IFC{i}", config_dict["enhanced_fxn"][i])
|
||||||
|
|
||||||
|
cfg.EnhancedFxn_Device = config_dict["enhanced_fxn_device"]
|
||||||
|
|
||||||
|
for i in range(4):
|
||||||
|
setattr(cfg, f"ExtClk{i}Freq", config_dict["ext_clk_freq"][i])
|
||||||
|
|
||||||
|
status = self._lib.CP210x_SetQuadPortConfig(handle, byref(cfg))
|
||||||
|
self._check_status(status, "SetQuadPortConfig")
|
||||||
|
|
||||||
|
# --- Advanced / raw ---
|
||||||
|
|
||||||
|
def get_config(self, handle: HANDLE, size: int = 512) -> bytes:
|
||||||
|
buf = (BYTE * size)()
|
||||||
|
status = self._lib.CP210x_GetConfig(handle, buf, WORD(size))
|
||||||
|
self._check_status(status, "GetConfig")
|
||||||
|
return bytes(buf)
|
||||||
|
|
||||||
|
def set_config(self, handle: HANDLE, data: bytes):
|
||||||
|
buf = (BYTE * len(data))(*data)
|
||||||
|
status = self._lib.CP210x_SetConfig(handle, buf, WORD(len(data)))
|
||||||
|
self._check_status(status, "SetConfig")
|
||||||
|
|
||||||
|
def get_generic(self, handle: HANDLE, size: int = 512) -> bytes:
|
||||||
|
buf = (BYTE * size)()
|
||||||
|
status = self._lib.CP210x_GetGeneric(handle, buf, WORD(size))
|
||||||
|
self._check_status(status, "GetGeneric")
|
||||||
|
return bytes(buf)
|
||||||
|
|
||||||
|
def set_generic(self, handle: HANDLE, data: bytes):
|
||||||
|
buf = (BYTE * len(data))(*data)
|
||||||
|
status = self._lib.CP210x_SetGeneric(handle, buf, WORD(len(data)))
|
||||||
|
self._check_status(status, "SetGeneric")
|
||||||
|
|
||||||
|
def create_hex_file(self, handle: HANDLE, filename: str):
|
||||||
|
status = self._lib.CP210x_CreateHexFile(handle, filename.encode('utf-8'))
|
||||||
|
self._check_status(status, "CreateHexFile")
|
||||||
|
|
||||||
|
def update_firmware(self, handle: HANDLE):
|
||||||
|
status = self._lib.CP210x_UpdateFirmware(handle)
|
||||||
|
self._check_status(status, "UpdateFirmware")
|
||||||
|
|
||||||
def reset(self, handle: HANDLE):
|
def reset(self, handle: HANDLE):
|
||||||
"""Reset the device (USB disconnect/reconnect)."""
|
|
||||||
status = self._lib.CP210x_Reset(handle)
|
status = self._lib.CP210x_Reset(handle)
|
||||||
self._check_status(status, "Reset")
|
self._check_status(status, "Reset")
|
||||||
|
|
||||||
|
|
||||||
# Convenience context manager for device access
|
|
||||||
class CP210xDevice:
|
class CP210xDevice:
|
||||||
"""Context manager for safe device access."""
|
"""Context manager for safe device access."""
|
||||||
|
|
||||||
@ -361,6 +808,7 @@ class CP210xDevice:
|
|||||||
self.lib = lib
|
self.lib = lib
|
||||||
self.device_index = device_index
|
self.device_index = device_index
|
||||||
self.handle = None
|
self.handle = None
|
||||||
|
self._part_code: int | None = None
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.handle = self.lib.open(self.device_index)
|
self.handle = self.lib.open(self.device_index)
|
||||||
@ -372,6 +820,21 @@ class CP210xDevice:
|
|||||||
self.handle = None
|
self.handle = None
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _get_part_code(self) -> int:
|
||||||
|
if self._part_code is None:
|
||||||
|
self._part_code = self.lib.get_part_number(self.handle)[0]
|
||||||
|
return self._part_code
|
||||||
|
|
||||||
|
def _require_part(self, allowed: set[int], feature: str):
|
||||||
|
code = self._get_part_code()
|
||||||
|
if code not in allowed:
|
||||||
|
name = PART_NUMBERS.get(code, f"0x{code:02X}")
|
||||||
|
allowed_names = ", ".join(PART_NUMBERS.get(c, f"0x{c:02X}") for c in sorted(allowed))
|
||||||
|
raise CP210xError(
|
||||||
|
CP210x_FUNCTION_NOT_SUPPORTED,
|
||||||
|
f"{feature} not supported on {name} (requires {allowed_names})",
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def part_number(self) -> tuple[int, str]:
|
def part_number(self) -> tuple[int, str]:
|
||||||
return self.lib.get_part_number(self.handle)
|
return self.lib.get_part_number(self.handle)
|
||||||
@ -410,12 +873,10 @@ class CP210xDevice:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def max_power_ma(self) -> int:
|
def max_power_ma(self) -> int:
|
||||||
"""Max power in milliamps."""
|
|
||||||
return self.lib.get_max_power(self.handle) * 2
|
return self.lib.get_max_power(self.handle) * 2
|
||||||
|
|
||||||
@max_power_ma.setter
|
@max_power_ma.setter
|
||||||
def max_power_ma(self, value: int):
|
def max_power_ma(self, value: int):
|
||||||
"""Set max power in milliamps (will be rounded down to nearest 2mA)."""
|
|
||||||
self.lib.set_max_power(self.handle, value // 2)
|
self.lib.set_max_power(self.handle, value // 2)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -428,14 +889,49 @@ class CP210xDevice:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def device_version(self) -> str:
|
def device_version(self) -> str:
|
||||||
"""Device version as BCD string (e.g., '1.00')."""
|
|
||||||
v = self.lib.get_device_version(self.handle)
|
v = self.lib.get_device_version(self.handle)
|
||||||
return f"{(v >> 8) & 0xFF}.{v & 0xFF:02d}"
|
return f"{(v >> 8) & 0xFF}.{v & 0xFF:02d}"
|
||||||
|
|
||||||
|
@device_version.setter
|
||||||
|
def device_version(self, value: int):
|
||||||
|
self.lib.set_device_version(self.handle, value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_locked(self) -> bool:
|
def is_locked(self) -> bool:
|
||||||
return self.lib.get_lock_value(self.handle) != 0
|
return self.lib.get_lock_value(self.handle) != 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def firmware_version(self) -> str | None:
|
||||||
|
"""Firmware version string (CP2102N only). Returns None if not supported."""
|
||||||
|
try:
|
||||||
|
major, minor, build = self.lib.get_firmware_version(self.handle)
|
||||||
|
return f"{major}.{minor}.{build}"
|
||||||
|
except CP210xError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def flush_buffer_config(self) -> int | None:
|
||||||
|
"""Raw flush buffer config word. Returns None if not supported."""
|
||||||
|
try:
|
||||||
|
return self.lib.get_flush_buffer_config(self.handle)
|
||||||
|
except CP210xError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_mode(self) -> tuple[int, int] | None:
|
||||||
|
"""(ECI mode, SCI mode) for CP2105. Returns None if not supported."""
|
||||||
|
try:
|
||||||
|
return self.lib.get_device_mode(self.handle)
|
||||||
|
except CP210xError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_interface_string(self, interface_number: int) -> str:
|
||||||
|
self._require_part(PARTS_WITH_INTERFACE_STRING, "Interface strings")
|
||||||
|
return self.lib.get_interface_string(self.handle, interface_number)
|
||||||
|
|
||||||
|
def set_interface_string(self, interface_number: int, value: str):
|
||||||
|
self._require_part(PARTS_WITH_INTERFACE_STRING, "Interface strings")
|
||||||
|
self.lib.set_interface_string(self.handle, interface_number, value)
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"""Reset the device."""
|
|
||||||
self.lib.reset(self.handle)
|
self.lib.reset(self.handle)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user