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."""
|
||||
|
||||
from .server import mcp, main
|
||||
from .server import main, mcp
|
||||
|
||||
__all__ = ["mcp", "main"]
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
"""Low-level ctypes bindings for libcp210xmanufacturing."""
|
||||
|
||||
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 typing import Optional
|
||||
|
||||
# Type aliases matching the C library
|
||||
CP210x_STATUS = c_int
|
||||
@ -39,17 +38,238 @@ PART_NUMBERS = {
|
||||
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
|
||||
MAX_DEVICE_STRLEN = 256
|
||||
MAX_MANUFACTURER_STRLEN = 45
|
||||
MAX_PRODUCT_STRLEN = 126
|
||||
MAX_SERIAL_STRLEN = 63
|
||||
CP2105_MAX_INTERFACE_STRLEN = 32
|
||||
CP2108_MAX_INTERFACE_STRLEN = 126
|
||||
|
||||
# GetProductString flags
|
||||
RETURN_SERIAL_NUMBER = 0x00
|
||||
RETURN_DESCRIPTION = 0x01
|
||||
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):
|
||||
"""Exception raised for CP210x library errors."""
|
||||
@ -77,16 +297,10 @@ class CP210xError(Exception):
|
||||
class CP210xLibrary:
|
||||
"""Wrapper for the CP210x manufacturing library."""
|
||||
|
||||
def __init__(self, lib_path: Optional[str] = None):
|
||||
"""Load the CP210x manufacturing library.
|
||||
|
||||
Args:
|
||||
lib_path: Path to libcp210xmanufacturing.so, or None to search default paths.
|
||||
"""
|
||||
def __init__(self, lib_path: str | None = None):
|
||||
if lib_path:
|
||||
self._lib = ctypes.CDLL(lib_path)
|
||||
else:
|
||||
# Search common locations
|
||||
search_paths = [
|
||||
"/usr/lib/libcp210xmanufacturing.so",
|
||||
"/usr/local/lib/libcp210xmanufacturing.so",
|
||||
@ -105,158 +319,198 @@ class CP210xLibrary:
|
||||
self._setup_functions()
|
||||
|
||||
def _setup_functions(self):
|
||||
"""Set up function prototypes."""
|
||||
# CP210x_GetNumDevices
|
||||
self._lib.CP210x_GetNumDevices.argtypes = [POINTER(DWORD)]
|
||||
self._lib.CP210x_GetNumDevices.restype = CP210x_STATUS
|
||||
"""Set up function prototypes for all library functions."""
|
||||
lib = self._lib
|
||||
|
||||
# CP210x_GetProductString
|
||||
self._lib.CP210x_GetProductString.argtypes = [DWORD, c_void_p, DWORD]
|
||||
self._lib.CP210x_GetProductString.restype = CP210x_STATUS
|
||||
# --- Device enumeration ---
|
||||
lib.CP210x_GetNumDevices.argtypes = [POINTER(DWORD)]
|
||||
lib.CP210x_GetNumDevices.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_Open
|
||||
self._lib.CP210x_Open.argtypes = [DWORD, POINTER(HANDLE)]
|
||||
self._lib.CP210x_Open.restype = CP210x_STATUS
|
||||
lib.CP210x_GetProductString.argtypes = [DWORD, c_void_p, DWORD]
|
||||
lib.CP210x_GetProductString.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_Close
|
||||
self._lib.CP210x_Close.argtypes = [HANDLE]
|
||||
self._lib.CP210x_Close.restype = CP210x_STATUS
|
||||
lib.CP210x_Open.argtypes = [DWORD, POINTER(HANDLE)]
|
||||
lib.CP210x_Open.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_GetPartNumber
|
||||
self._lib.CP210x_GetPartNumber.argtypes = [HANDLE, POINTER(BYTE)]
|
||||
self._lib.CP210x_GetPartNumber.restype = CP210x_STATUS
|
||||
lib.CP210x_Close.argtypes = [HANDLE]
|
||||
lib.CP210x_Close.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_GetDeviceVid
|
||||
self._lib.CP210x_GetDeviceVid.argtypes = [HANDLE, POINTER(WORD)]
|
||||
self._lib.CP210x_GetDeviceVid.restype = CP210x_STATUS
|
||||
# --- Getters (scalars) ---
|
||||
lib.CP210x_GetPartNumber.argtypes = [HANDLE, POINTER(BYTE)]
|
||||
lib.CP210x_GetPartNumber.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_GetDevicePid
|
||||
self._lib.CP210x_GetDevicePid.argtypes = [HANDLE, POINTER(WORD)]
|
||||
self._lib.CP210x_GetDevicePid.restype = CP210x_STATUS
|
||||
lib.CP210x_GetDeviceVid.argtypes = [HANDLE, POINTER(WORD)]
|
||||
lib.CP210x_GetDeviceVid.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_GetDeviceManufacturerString
|
||||
self._lib.CP210x_GetDeviceManufacturerString.argtypes = [HANDLE, c_void_p, POINTER(BYTE), BOOL]
|
||||
self._lib.CP210x_GetDeviceManufacturerString.restype = CP210x_STATUS
|
||||
lib.CP210x_GetDevicePid.argtypes = [HANDLE, POINTER(WORD)]
|
||||
lib.CP210x_GetDevicePid.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_GetDeviceProductString
|
||||
self._lib.CP210x_GetDeviceProductString.argtypes = [HANDLE, c_void_p, POINTER(BYTE), BOOL]
|
||||
self._lib.CP210x_GetDeviceProductString.restype = CP210x_STATUS
|
||||
lib.CP210x_GetDeviceManufacturerString.argtypes = [HANDLE, c_void_p, POINTER(BYTE), BOOL]
|
||||
lib.CP210x_GetDeviceManufacturerString.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_GetDeviceSerialNumber
|
||||
self._lib.CP210x_GetDeviceSerialNumber.argtypes = [HANDLE, c_void_p, POINTER(BYTE), BOOL]
|
||||
self._lib.CP210x_GetDeviceSerialNumber.restype = CP210x_STATUS
|
||||
lib.CP210x_GetDeviceProductString.argtypes = [HANDLE, c_void_p, POINTER(BYTE), BOOL]
|
||||
lib.CP210x_GetDeviceProductString.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_SetManufacturerString
|
||||
self._lib.CP210x_SetManufacturerString.argtypes = [HANDLE, c_void_p, BYTE, BOOL]
|
||||
self._lib.CP210x_SetManufacturerString.restype = CP210x_STATUS
|
||||
lib.CP210x_GetDeviceSerialNumber.argtypes = [HANDLE, c_void_p, POINTER(BYTE), BOOL]
|
||||
lib.CP210x_GetDeviceSerialNumber.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_SetProductString
|
||||
self._lib.CP210x_SetProductString.argtypes = [HANDLE, c_void_p, BYTE, BOOL]
|
||||
self._lib.CP210x_SetProductString.restype = CP210x_STATUS
|
||||
lib.CP210x_GetDeviceInterfaceString.argtypes = [HANDLE, BYTE, c_void_p, POINTER(BYTE), BOOL]
|
||||
lib.CP210x_GetDeviceInterfaceString.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_SetSerialNumber
|
||||
self._lib.CP210x_SetSerialNumber.argtypes = [HANDLE, c_void_p, BYTE, BOOL]
|
||||
self._lib.CP210x_SetSerialNumber.restype = CP210x_STATUS
|
||||
lib.CP210x_GetMaxPower.argtypes = [HANDLE, POINTER(BYTE)]
|
||||
lib.CP210x_GetMaxPower.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_GetMaxPower
|
||||
self._lib.CP210x_GetMaxPower.argtypes = [HANDLE, POINTER(BYTE)]
|
||||
self._lib.CP210x_GetMaxPower.restype = CP210x_STATUS
|
||||
lib.CP210x_GetSelfPower.argtypes = [HANDLE, POINTER(BOOL)]
|
||||
lib.CP210x_GetSelfPower.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_SetMaxPower
|
||||
self._lib.CP210x_SetMaxPower.argtypes = [HANDLE, BYTE]
|
||||
self._lib.CP210x_SetMaxPower.restype = CP210x_STATUS
|
||||
lib.CP210x_GetDeviceVersion.argtypes = [HANDLE, POINTER(WORD)]
|
||||
lib.CP210x_GetDeviceVersion.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_GetSelfPower
|
||||
self._lib.CP210x_GetSelfPower.argtypes = [HANDLE, POINTER(BOOL)]
|
||||
self._lib.CP210x_GetSelfPower.restype = CP210x_STATUS
|
||||
lib.CP210x_GetFlushBufferConfig.argtypes = [HANDLE, POINTER(WORD)]
|
||||
lib.CP210x_GetFlushBufferConfig.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_SetSelfPower
|
||||
self._lib.CP210x_SetSelfPower.argtypes = [HANDLE, BOOL]
|
||||
self._lib.CP210x_SetSelfPower.restype = CP210x_STATUS
|
||||
lib.CP210x_GetDeviceMode.argtypes = [HANDLE, POINTER(BYTE), POINTER(BYTE)]
|
||||
lib.CP210x_GetDeviceMode.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_GetDeviceVersion
|
||||
self._lib.CP210x_GetDeviceVersion.argtypes = [HANDLE, POINTER(WORD)]
|
||||
self._lib.CP210x_GetDeviceVersion.restype = CP210x_STATUS
|
||||
lib.CP210x_GetLockValue.argtypes = [HANDLE, POINTER(BYTE)]
|
||||
lib.CP210x_GetLockValue.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_SetDeviceVersion
|
||||
self._lib.CP210x_SetDeviceVersion.argtypes = [HANDLE, WORD]
|
||||
self._lib.CP210x_SetDeviceVersion.restype = CP210x_STATUS
|
||||
lib.CP210x_GetFirmwareVersion.argtypes = [HANDLE, POINTER(FIRMWARE_T)]
|
||||
lib.CP210x_GetFirmwareVersion.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_GetLockValue
|
||||
self._lib.CP210x_GetLockValue.argtypes = [HANDLE, POINTER(BYTE)]
|
||||
self._lib.CP210x_GetLockValue.restype = CP210x_STATUS
|
||||
# --- Setters (scalars) ---
|
||||
lib.CP210x_SetVid.argtypes = [HANDLE, WORD]
|
||||
lib.CP210x_SetVid.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_SetLockValue
|
||||
self._lib.CP210x_SetLockValue.argtypes = [HANDLE]
|
||||
self._lib.CP210x_SetLockValue.restype = CP210x_STATUS
|
||||
lib.CP210x_SetPid.argtypes = [HANDLE, WORD]
|
||||
lib.CP210x_SetPid.restype = CP210x_STATUS
|
||||
|
||||
# CP210x_Reset
|
||||
self._lib.CP210x_Reset.argtypes = [HANDLE]
|
||||
self._lib.CP210x_Reset.restype = CP210x_STATUS
|
||||
lib.CP210x_SetManufacturerString.argtypes = [HANDLE, c_void_p, BYTE, BOOL]
|
||||
lib.CP210x_SetManufacturerString.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 = ""):
|
||||
"""Raise exception if status indicates error."""
|
||||
if status != CP210x_SUCCESS:
|
||||
raise CP210xError(status, context)
|
||||
|
||||
# --- Device enumeration ---
|
||||
|
||||
def get_num_devices(self) -> int:
|
||||
"""Get the number of connected CP210x devices."""
|
||||
num = DWORD()
|
||||
status = self._lib.CP210x_GetNumDevices(byref(num))
|
||||
self._check_status(status, "GetNumDevices")
|
||||
return num.value
|
||||
|
||||
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)
|
||||
status = self._lib.CP210x_GetProductString(DWORD(device_index), buf, DWORD(flag))
|
||||
self._check_status(status, "GetProductString")
|
||||
return buf.value.decode('utf-8', errors='replace')
|
||||
|
||||
def open(self, device_index: int) -> HANDLE:
|
||||
"""Open a device by index."""
|
||||
handle = HANDLE()
|
||||
status = self._lib.CP210x_Open(DWORD(device_index), byref(handle))
|
||||
self._check_status(status, f"Open device {device_index}")
|
||||
return handle
|
||||
|
||||
def close(self, handle: HANDLE):
|
||||
"""Close an open device."""
|
||||
status = self._lib.CP210x_Close(handle)
|
||||
self._check_status(status, "Close")
|
||||
|
||||
def get_part_number(self, handle: HANDLE) -> tuple[int, str]:
|
||||
"""Get the part number of an open device.
|
||||
# --- Scalar getters ---
|
||||
|
||||
Returns:
|
||||
Tuple of (part_number_code, part_name)
|
||||
"""
|
||||
def get_part_number(self, handle: HANDLE) -> tuple[int, str]:
|
||||
part = BYTE()
|
||||
status = self._lib.CP210x_GetPartNumber(handle, byref(part))
|
||||
self._check_status(status, "GetPartNumber")
|
||||
return part.value, PART_NUMBERS.get(part.value, f"Unknown (0x{part.value:02X})")
|
||||
|
||||
def get_device_vid(self, handle: HANDLE) -> int:
|
||||
"""Get the USB Vendor ID."""
|
||||
vid = WORD()
|
||||
status = self._lib.CP210x_GetDeviceVid(handle, byref(vid))
|
||||
self._check_status(status, "GetDeviceVid")
|
||||
return vid.value
|
||||
|
||||
def get_device_pid(self, handle: HANDLE) -> int:
|
||||
"""Get the USB Product ID."""
|
||||
pid = WORD()
|
||||
status = self._lib.CP210x_GetDevicePid(handle, byref(pid))
|
||||
self._check_status(status, "GetDevicePid")
|
||||
return pid.value
|
||||
|
||||
def get_manufacturer_string(self, handle: HANDLE) -> str:
|
||||
"""Get the manufacturer string."""
|
||||
buf = ctypes.create_string_buffer(MAX_MANUFACTURER_STRLEN)
|
||||
length = BYTE()
|
||||
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')
|
||||
|
||||
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)
|
||||
length = BYTE()
|
||||
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')
|
||||
|
||||
def get_serial_number(self, handle: HANDLE) -> str:
|
||||
"""Get the serial number."""
|
||||
buf = ctypes.create_string_buffer(MAX_SERIAL_STRLEN)
|
||||
length = BYTE()
|
||||
status = self._lib.CP210x_GetDeviceSerialNumber(handle, buf, byref(length), BOOL(1))
|
||||
self._check_status(status, "GetDeviceSerialNumber")
|
||||
return buf.value.decode('utf-8', errors='replace')
|
||||
|
||||
def set_manufacturer_string(self, handle: HANDLE, manufacturer: str):
|
||||
"""Set the manufacturer string (max 45 chars)."""
|
||||
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):
|
||||
"""Set the product string (max 126 chars)."""
|
||||
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_interface_string(self, handle: HANDLE, interface_number: int) -> str:
|
||||
buf = ctypes.create_string_buffer(CP2108_MAX_INTERFACE_STRLEN)
|
||||
length = BYTE()
|
||||
status = self._lib.CP210x_GetDeviceInterfaceString(
|
||||
handle, BYTE(interface_number), buf, byref(length), BOOL(1)
|
||||
)
|
||||
self._check_status(status, f"GetDeviceInterfaceString(ifc={interface_number})")
|
||||
return buf.value.decode('utf-8', errors='replace')
|
||||
|
||||
def get_max_power(self, handle: HANDLE) -> int:
|
||||
"""Get max power in units of 2mA (multiply by 2 for mA)."""
|
||||
power = BYTE()
|
||||
status = self._lib.CP210x_GetMaxPower(handle, byref(power))
|
||||
self._check_status(status, "GetMaxPower")
|
||||
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:
|
||||
"""Check if device is self-powered."""
|
||||
self_power = BOOL()
|
||||
status = self._lib.CP210x_GetSelfPower(handle, byref(self_power))
|
||||
self._check_status(status, "GetSelfPower")
|
||||
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:
|
||||
"""Get device version (bcdDevice)."""
|
||||
version = WORD()
|
||||
status = self._lib.CP210x_GetDeviceVersion(handle, byref(version))
|
||||
self._check_status(status, "GetDeviceVersion")
|
||||
return version.value
|
||||
|
||||
def set_device_version(self, handle: HANDLE, version: int):
|
||||
"""Set device version (bcdDevice)."""
|
||||
status = self._lib.CP210x_SetDeviceVersion(handle, WORD(version))
|
||||
self._check_status(status, "SetDeviceVersion")
|
||||
def get_flush_buffer_config(self, handle: HANDLE) -> int:
|
||||
config = WORD()
|
||||
status = self._lib.CP210x_GetFlushBufferConfig(handle, byref(config))
|
||||
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:
|
||||
"""Get lock value (0 = unlocked, 1-255 = locked)."""
|
||||
lock = BYTE()
|
||||
status = self._lib.CP210x_GetLockValue(handle, byref(lock))
|
||||
self._check_status(status, "GetLockValue")
|
||||
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):
|
||||
"""Lock the device (PERMANENT - cannot be undone!)."""
|
||||
status = self._lib.CP210x_SetLockValue(handle)
|
||||
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):
|
||||
"""Reset the device (USB disconnect/reconnect)."""
|
||||
status = self._lib.CP210x_Reset(handle)
|
||||
self._check_status(status, "Reset")
|
||||
|
||||
|
||||
# Convenience context manager for device access
|
||||
class CP210xDevice:
|
||||
"""Context manager for safe device access."""
|
||||
|
||||
@ -361,6 +808,7 @@ class CP210xDevice:
|
||||
self.lib = lib
|
||||
self.device_index = device_index
|
||||
self.handle = None
|
||||
self._part_code: int | None = None
|
||||
|
||||
def __enter__(self):
|
||||
self.handle = self.lib.open(self.device_index)
|
||||
@ -372,6 +820,21 @@ class CP210xDevice:
|
||||
self.handle = None
|
||||
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
|
||||
def part_number(self) -> tuple[int, str]:
|
||||
return self.lib.get_part_number(self.handle)
|
||||
@ -410,12 +873,10 @@ class CP210xDevice:
|
||||
|
||||
@property
|
||||
def max_power_ma(self) -> int:
|
||||
"""Max power in milliamps."""
|
||||
return self.lib.get_max_power(self.handle) * 2
|
||||
|
||||
@max_power_ma.setter
|
||||
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)
|
||||
|
||||
@property
|
||||
@ -428,14 +889,49 @@ class CP210xDevice:
|
||||
|
||||
@property
|
||||
def device_version(self) -> str:
|
||||
"""Device version as BCD string (e.g., '1.00')."""
|
||||
v = self.lib.get_device_version(self.handle)
|
||||
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
|
||||
def is_locked(self) -> bool:
|
||||
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):
|
||||
"""Reset the device."""
|
||||
self.lib.reset(self.handle)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user