Commit da09d702 authored by Matthieu Cattin's avatar Matthieu Cattin

Add cp210x EEPROM read/write utility, to store calibration box data.

parent a6e17fb6
# -*- coding: utf-8 -*-
__author__ = "Johannes Hölzl <johannes.hoelzl@gmx.de>"
__license__ = "GNU LGPL"
# -*- coding: utf-8 -*-
# Copyright (c) 2007 Johannes Hölzl <johannes.hoelzl@gmx.de>
#
# This library is covered by the GNU LGPL, read LICENSE for details.
"""Provides access to the EEPROM of Silabs CP210x devices
The following classes are available:
class Cp210xProgrammer:
Provides direct access to the CP2101, can be used to write single data
directly or via an EEPROM image.
class EEPROM:
Can be used to read or write a hex file containing the EEPROM content
of an CP2101. Provides also access to the single fields in the EEPROM.
"""
import ctypes
import usb
__all__ = ['Cp210xProgrammer', 'Cp210xError']
CP2101_UART = 0x00
CP2101_CONFIG = 0xFF
CP2101_UART_ENABLE = 0x0001
CP2101_UART_DISABLE = 0x0000
REG_VENDOR_ID = 0x3701
REG_PRODUCT_ID = 0x3702
REG_PRODUCT_STRING = 0x3703
REG_SERIAL_NUMBER = 0x3704
REG_CFG_ATTRIBUTES = 0x3705
REG_MAX_POWER = 0x3706
REG_VERSION = 0x3707
REG_UNKNOWN = 0x3708
REG_EEPROM = 0x3709
REG_LOCK_VALUE = 0x370A
REG_PART_NUMBER = 0x370B
SIZE_EEPROM = 0x0400
SIZE_PRODUCT_STRING = 0x007D
SIZE_SERIAL_NUMBER = 0x003F
SIZE_BAUDRATES = 32
SIZE_BAUDRATE_CFG = 10
SIZE_BAUDRATE_TABLE = SIZE_BAUDRATES * SIZE_BAUDRATE_CFG
SIZE_VENDOR_STRING = 24
LCK_LOCKED = 0x00
LCK_UNLOCKED = 0xFF
VID_SILABS = 0x10C4
PID_CP210x = 0xEA60
VALUES = [
('product_string', 'string'),
('serial_number', 'string'),
('product_id', 'id'),
('vendor_id', 'id'),
('version', 'version'),
('bus_powered', 'boolean'),
('max_power', 'int'),
('locked', 'boolean'),
('part_number', 'int'),
('vendor_string', 'string'),
('baudrate_table', 'list'),
]
def iif(v, a, b):
if v:
return a
else:
return b
def to_div2(p):
value = int(p / 2)
if (value * 2) < p:
value += 1
return value
def to_bcd(i):
assert i >= 0 and i <= 99
return (i // 10) << 4 | (i % 10)
def to_bcd2( (i, j) ):
return to_bcd(i) << 8 | to_bcd(j)
def from_bcd(num):
return num & 0x0F + (num >> 4) * 10
def from_bcd2(data):
return (from_bcd(data >> 8), from_bcd(data & 0xFF))
def from_binary(data, le=True):
value = 0
if le:
data = data[::-1]
for byte in data:
value = value << 8 | ord(byte)
return value
def to_binary(value, size=2, le=True):
data = ''
for i in range(size):
data += chr(value & 0xFF)
value >>= 8
if le:
return data
else:
return data[::-1]
def parse_baudrate_cfg(data):
return (from_binary(data[0:2], le=False),
from_binary(data[2:4], le=False),
from_binary(data[4:5]),
from_binary(data[6:10]))
def build_baudrate_cfg(baudgen, timer0reload, prescaler, baudrate):
return (to_binary(baudgen, le=False) + to_binary(timer0reload, le=False) +
to_binary(prescaler, 1) + '\x00' + to_binary(baudrate, 4))
class Cp210xError(IOError):
pass
class DeviceLocked(Cp210xError):
pass
class Cp210xProgrammer(object):
"""Program an Silabs CP2101, CP2102 or CP2103
This modul provides access to Silabs CP210x devices to set some USB
descriptor fields and some USB descriptor strings.
The following fields can be set:
* Vendor ID
* Product ID
* Product String
* Serial Number
* Device Version
* Bus Powered
* max. Power consumption
Either use libusb to find a device, and provide the device description
to the constructor, or use Cp210xProgrammer.list_device() to list all
devices matching certain pattern.
To progamm the device open() it, set the data, and close() it. To have the
changed fields reread call reset() before closing it.
"""
TIMEOUT = 300 #ms
@classmethod
def list_devices(self, patterns=[{ 'idVendor': VID_SILABS,
'idProduct': PID_CP210x }]):
"""Yields a list of devices matching certain patterns.
param patterns: This must be a list of dictionaries or pairs of string.
Each device in the usb tree is matched against all pattern in the
list.
When an item is a dictionary all fields of the descriptors
are compared against the corresponding values in the dictionary. If
each value is equal, the device is yielded.
When an item is a pair of strings. The first string must be the
dirname of the bus and the second string the filename of the device.
For example:
>> list(Cp210xProgrammer.list_device([{ 'idVendor': VID_SILABS,
'idProduct': PID_CP210x }]))
[device(...)]
"""
usb.find_busses()
usb.find_devices()
bus = usb.get_busses()
while bus:
dev = bus.contents.devices
while dev:
for pattern in patterns:
if isinstance(pattern, dict):
for name, value in pattern.items():
if getattr(dev.contents.descriptor, name) != value:
break
else:
yield self(dev)
break
elif isinstance(pattern, tuple):
if (bus.contents.dirname == pattern[0] and
dev.contents.filename == pattern[1]):
yield self(dev)
break
dev = dev.contents.next
bus = bus.contents.next
def __init__(self, dev_info):
self.dev_info = dev_info
self.handle = None
self._locked = None
def open(self):
"""Opens the device.
Only after an successful call to open() data can be read from and
written to the device.
Claims all resources associated with this device.
"""
self.handle = usb.open(self.dev_info)
if self.handle == 0:
self.handle = None
raise Cp210xError("Can't open device.")
usb.set_configuration(self.handle, 1)
usb.claim_interface(self.handle, 0)
def reset(self):
"""Force the USB stack to reset the device.
Resets the device through an hard reset over the port to which the
device is connected. After that happend the EEPROM content in the device
is reread and the device's descriptors are the one written to it.
"""
assert self.handle is not None
usb.reset(self.handle)
def close(self):
"""Closes the device.
Releases all resources associated with this device.
"""
assert self.handle is not None
usb.release_interface(self.handle, 0)
usb.close(self.handle)
self.handle = None
def __del__(self):
if self.handle is not None:
self.close()
def _set_config(self, value, index=0, data=None, request=CP2101_CONFIG):
assert self.handle is not None
if self.get_locked():
raise DeviceLocked()
if data is not None:
data_length = len(data)
else:
data_length = 0
res = usb.control_msg(self.handle, usb.ENDPOINT_OUT | usb.TYPE_VENDOR,
request, value, index, data, data_length,
self.TIMEOUT)
if res < 0:
raise Cp210xError("Unable to send request %04X result=%d"
% (value, res))
def _set_config_string(self, value, content, max_length):
assert isinstance(content, basestring)
encoded = content.encode('utf-16-le')
assert len(encoded) <= max_length
self._set_config(value, data=chr(len(encoded) + 2) + "\x03" + encoded)
def _get_config(self, value, length, index=0, request=CP2101_CONFIG):
assert self.handle is not None
data = ctypes.create_string_buffer(length)
res = usb.control_msg(self.handle, usb.ENDPOINT_IN | usb.TYPE_VENDOR,
request, value, index, data, length,
self.TIMEOUT)
if res < 0:
raise Cp210xError("Unable to send request, %04X result=%d"
% (value, res))
return data.raw[:res]
def _get_int8_config(self, value, index=0, request=CP2101_CONFIG):
return ord(self._get_config(value, 1, index=index, request=request))
def _get_int16_config(self, value, index=0, request=CP2101_CONFIG):
data = self._get_config(value, 2, index=index, request=request)
return ord(data[0]) << 8 | ord(data[1])
def get_eeprom_content(self):
"""Reads the entire EEPROM content as one big 1024-byte blob.
"""
return self._get_config(REG_EEPROM, SIZE_EEPROM)
def get_baudrate_content(self):
"""Return the baudrate table as binary data.
"""
return self._get_config(REG_EEPROM, SIZE_BAUDRATE_TABLE)
def get_baudrate_table(self):
"""Returns the baudrate table.
A list containing 4-tuples are returnes.
Each tuple containes the following data:
* BaudGen: Value used to generate the real baudrate.
* Time0Reset: Value used to generate the usb timeout.
* Prescaler: Used to generate the real baudrate.
* Baudrate: The baudrate which activates this entry.
"""
data = self.get_baudrate_content()
return [parse_baudrate_cfg(data[pos:pos+SIZE_BAUDRATE_CFG])
for pos in range(0, SIZE_BAUDRATE_TABLE, SIZE_BAUDRATE_CFG)]
def set_baudrate_table(self, baudrates):
"""Writes the baudrate table.
See get_baudrate_table() for the structure of the table.
"""
assert len(baudrates) == SIZE_BAUDRATES
self.set_baudrate_content(data=''.join(build_baudrate_cfg(*cfg)
for cfg in baudrates))
baudrate_table = property(get_baudrate_table, set_baudrate_table)
def get_part_number(self):
""" The part number of the device.
Returns: 1 for an CP2101
2 for an CP2102
3 for an CP2103
"""
return self._get_int8_config(REG_PART_NUMBER)
def get_locked(self):
""" The lock value of the device.
When True is returnes no data can be written to the device.
"""
if self._locked is None:
self._locked = self._get_int8_config(REG_LOCK_VALUE) == LCK_LOCKED
return self._locked
def set_eeprom_content(self, content):
"""Writes an 1024-byte blob to the EEPROM
"""
assert len(content) == SIZE_EEPROM, ("EEPROM data must be %i bytes."
% SIZE_EEPROM)
assert isinstance(content, str), "EEPROM data must be string."
self._set_config(REG_EEPROM, data=content)
def set_product_id(self, pid):
"""Sets the Product ID
"""
assert pid > 0x0000 and pid < 0xFFFF
self._set_config(REG_PRODUCT_ID, pid)
def set_vendor_id(self, vid):
"""Sets the Vendor ID
"""
assert vid > 0x0000 and vid < 0xFFFF
self._set_config(REG_VENDOR_ID, vid)
def set_product_string(self, product_string):
"""Sets the product string.
Be aware that the string will be stored as UTF-16 encoded and should not
exceed SIZE_PRODUCT_STRING
"""
self._set_config_string(REG_PRODUCT_STRING, product_string,
SIZE_PRODUCT_STRING)
def set_serial_number(self, serial_number):
self._set_config_string(REG_SERIAL_NUMBER, serial_number,
SIZE_SERIAL_NUMBER)
def set_max_power(self, max_power):
assert max_power >= 0 and max_power <= 500
self._set_config(REG_MAX_POWER, to_div2(max_power))
def set_bus_powered(self, bus_powered):
if bus_powered:
self._set_config(REG_CFG_ATTRIBUTES, 0xC0)
else:
self._set_config(REG_CFG_ATTRIBUTES, 0x80)
def set_version(self, version):
self._set_config(REG_VERSION, to_bcd2(version))
def set_locked(self, locked):
""" The lock value of the device.
When True is returnes no data can be written to the device.
"""
if locked:
self._set_config(REG_LOCK_VALUE, LCK_LOCKED)
else:
self._set_config(REG_LOCK_VALUE, LCK_UNLOCKED)
def set_values(self, values):
for name, value in values.items():
if name not in ['part_number', 'vendor_string']:
getattr(self, "set_" + name) (value)
# -*- coding: utf-8 -*-
# Copyright (c) 2007 Johannes Hölzl <johannes.hoelzl@gmx.de>
#
# This library is covered by the GNU LGPL, read LICENSE for details.
import cp210x
from cp210x import from_binary, to_binary, iif, VALUES
__all__ = ['EEPROM', 'HexFileError']
POS_BAUDRATE_TABLE = 0x0000
POS_PART_NUMBER = 0x01FF
POS_PRODUCT_STRING = 0x0208
POS_SERIAL_NUMBER = 0x0307
POS_PRODUCT_ID = 0x0390
POS_VENDOR_ID = 0x0392
POS_VERSION = 0x0394
POS_CFG_ATTRIBUTES = 0x03A1
POS_MAX_POWER = 0x03A2
POS_VENDOR_STRING = 0x03C3
POS_LOCK_VALUE = 0x03FF
class HexFileError(StandardError):
pass
def checksum(line):
return sum(ord(c) for c in line) & 0xFF
def _int_value(position, size, read=lambda x:x, write=lambda x:x):
def get(self):
return read(from_binary(self.get(position, size)))
def set(self, value):
self.set(position, to_binary(write(value), size))
return property(get, set)
def _str_value(position, max_size):
def get(self):
size = from_binary(self.get(position, 1))
assert size <= (max_size + 2) and size >= 2
assert self.get(position + 1, 1) == '\x03', "Missing 0x03 at %04X" % (position + 1)
return self.get(position + 2, size - 2).decode('utf-16-le')
def set(self, value):
encoded = value.encode('utf-16-le')
assert len(encoded) <= max_size
self.set(position, chr(len(encoded) + 2) + '\x03' + encoded)
return property(get, set)
class EEPROM(object):
START_ADDRESS = 0x3600
def __init__(self, content=None):
if isinstance(content, str) or content is None:
assert content is None or len(content) == cp210x.SIZE_EEPROM
self.content = content
elif isinstance(content, cp210x.Cp210xProgrammer):
self.content = content.get_eeprom_content()
else:
self.parse_hex_file(content.read())
def write_to_cp210x(self, cp210xDevice):
cp210xDevice.set_eeprom_content(self.content)
def parse_hex_file(self, hex_content):
self.content = ''
address = self.START_ADDRESS
for tag in hex_content.split('\n'):
if not tag.startswith(':'):
raise HexFileError("Line doesn't start with ':'")
try:
content = tag[1:].decode('hex')
except TypeError:
raise HexFileError("Hex data expected")
if len(content) < 5:
raise HexFileError("Line to short")
if checksum(content) != 0:
raise HexFileError("Checksum error")
size = from_binary(content[0])
tag_address = from_binary(content[1:3], le=False)
tag_type = from_binary(content[3:4])
line = content[4:-1]
if tag_type == 0x00:
if tag_address != address:
raise HexFileError("Expected address %04X but found %04X"
% (address, tag_address))
self.content += line
address += len(line)
elif tag_type == 0x01:
if size != 0 or len(line) != 0:
raise HexFileError("Defekt end tag")
break
else:
raise HexFileError("Unknown tag type %02X" % tag_type)
def build_hex_file(self):
for tag_start in range(0, len(self.content), 0x10):
line = self.content[tag_start:tag_start+0x10]
address = self.START_ADDRESS + tag_start
tag = (to_binary(len(line), 1) +
to_binary(address, le=False) +
'\x00' +
line)
cs = checksum(tag)
if cs == 0:
tag += '\x00'
else:
tag += chr(0x100 - cs)
yield ":%s\n" % tag.encode('hex')
yield ":00000001FF\n"
def write_hex_file(self, f):
if isinstance(f, str):
f = file(f, 'wb')
do_close = True
else:
do_close = False
for line in self.build_hex_file():
f.write(line)
if do_close:
f.close()
def read_hex_file(self, f):
if isinstance(f, str):
f = file(f, 'rb')
do_close = True
else:
do_close = False
self.parse_hex_file(f.read())
if do_close:
f.close()
def get(self, pos, length):
return self.content[pos:pos+length]
def set(self, pos, data):
self.content = (self.content[:pos] +
data +
self.content[pos + len(data):])
def _get_baudrate_table(self):
dat = self.get(POS_BAUDRATE_TABLE, cp210x.SIZE_BAUDRATE_TABLE)
return [cp210x.parse_baudrate_cfg(dat[pos:pos+cp210x.SIZE_BAUDRATE_CFG])
for pos in range(0, cp210x.SIZE_BAUDRATE_TABLE,
cp210x.SIZE_BAUDRATE_CFG)]
def _set_baudrate_table(self, baudrates):
assert len(baudrates) == cp210x.SIZE_BAUDRATES
self.set(POS_BAUDRATE_TABLE,
''.join(cp210x.build_baudrate_cfg(*cfg) for cfg in baudrates))
baudrate_table = property(_get_baudrate_table, _set_baudrate_table)
product_string = _str_value(POS_PRODUCT_STRING, cp210x.SIZE_PRODUCT_STRING)
serial_number = _str_value(POS_SERIAL_NUMBER, cp210x.SIZE_SERIAL_NUMBER)
part_number = _int_value(POS_PART_NUMBER, 1)
product_id = _int_value(POS_PRODUCT_ID, 2)
vendor_id = _int_value(POS_VENDOR_ID, 2)
version = _int_value(POS_VERSION, 2,
cp210x.from_bcd2, cp210x.to_bcd2)
bus_powered = _int_value(POS_CFG_ATTRIBUTES, 1,
lambda a: bool(a & 0x40),
lambda a: iif(a, 0xC0, 0x80))
max_power = _int_value(POS_MAX_POWER, 1, lambda p: p*2, cp210x.to_div2)
vendor_string = _str_value(POS_VENDOR_STRING, cp210x.SIZE_VENDOR_STRING)
locked = _int_value(POS_LOCK_VALUE, 1,
lambda l: l == cp210x.LCK_LOCKED,
lambda b: iif(b, cp210x.LCK_LOCKED,
cp210x.LCK_UNLOCKED))
def get_values(self):
return dict((name, getattr(self, name)) for name, type in VALUES)
def set_values(self, values):
for name, value in values.items():
setattr(self, name, value)
# Python interface for "usb.h" version 0.1.4.4.4
#
# Copyright (c) 2005 Robert Hoelzl <robert.hoelzl@gmx.de>
# Copyright (c) 2007 Johannes Hoelzl <johannes.hoelzl@gmx.de>
#
# This library is covered by the GNU LGPL, read LICENSE for details.
from ctypes import *
import sys
class LibUsbNotInstalled(OSError):
pass
try:
if sys.platform == 'darwin':
PATH_MAX = 1024
dll=cdll.LoadLibrary("libusb.dylib")
elif sys.platform == 'linux2':
PATH_MAX = 4096
dll=cdll.LoadLibrary("libusb.so")
else:
raise NotImplementedError("Platform %s not supported by usb.py" % sys.platform)
except OSError:
raise LibUsbNotInstalled()
# helper functions
def func(f, *args, **retval):
f.restype = retval.get('retval', None)
f.argtypes = args
if retval.has_key('rename'): globals()[retval['rename']] = f
else: globals()[f.__name__[4:]] = f
# constants
CLASS_PER_INTERFACE = 0
USB_CLASS_AUDIO = 1
CLASS_COMM = 2
CLASS_HID = 3
CLASS_PRINTER = 7
CLASS_PTP = 6
CLASS_MASS_STORAGE = 8
CLASS_HUB = 9
CLASS_DATA = 10
CLASS_VENDOR_SPEC = 0xff
DT_DEVICE = 0x01
DT_CONFIG = 0x02
DT_STRING = 0x03
DT_INTERFACE = 0x04
DT_ENDPOINT = 0x05
DT_HID = 0x21
DT_REPORT = 0x22
DT_PHYSICAL = 0x23
DT_HUB = 0x29
DT_DEVICE_SIZE = 18
DT_CONFIG_SIZE = 9
DT_INTERFACE_SIZE = 9
DT_ENDPOINT_SIZE = 7
DT_ENDPOINT_AUDIO_SIZE = 9 # Audio extension
DT_HUB_NONVAR_SIZE = 7
class descriptor_header(Structure): _fields_ = [
("bLength", c_uint8),
("bDescriptorType", c_uint8) ]
class string_descriptor(Structure): _fields_ = [
("bLength", c_uint8),
("bDescriptorType", c_uint8),
("wData", c_uint*1) ]
class hid_descriptor(Structure): _fields_ = [
("bLength", c_uint8),
("bDescriptorType", c_uint8),
("bcdHID", c_uint16),
("bCountryCode", c_uint8),
("bNumDescriptors", c_uint8) ]
MAXENDPOINTS = 32
class endpoint_descriptor(Structure): _fields_ = [
("bLength", c_uint8),
("bDescriptorType", c_uint8),
("bEndpointAddress", c_uint8),
("bmAttributes", c_uint8),
("wMaxPacketSize", c_uint16),
("bInterval", c_uint8),
("bRefresh", c_uint8),
("bSynchAddress", c_uint8),
("extra", POINTER(c_uint8)),
("extralen", c_int) ]
ENDPOINT_ADDRESS_MASK = 0x0f # in bEndpointAddress
ENDPOINT_DIR_MASK = 0x80
ENDPOINT_TYPE_MASK = 0x03 # in bmAttributes
ENDPOINT_TYPE_CONTROL = 0
ENDPOINT_TYPE_ISOCHRONOUS = 1
ENDPOINT_TYPE_BULK = 2
ENDPOINT_TYPE_INTERRUPT = 3
MAXINTERFACES = 32
class interface_descriptor(Structure): _fields_ = [
("bLength", c_uint8),
("bDescriptorType", c_uint8),
("bInterfaceNumber", c_uint8),
("bAlternateSetting", c_uint8),
("bNumEndpoints", c_uint8),
("bInterfaceClass", c_uint8),
("bInterfaceSubClass", c_uint8),
("bInterfaceProtocol", c_uint8),
("iInterface", c_uint8),
("endpoint", POINTER(endpoint_descriptor)),
("extra", POINTER(c_uint8)),
("extralen", c_int) ]
MAXALTSETTING = 128 # Hard limit
class interface(Structure): _fields_ = [
("altsetting", POINTER(interface_descriptor)),
("num_altsetting", c_int) ]
MAXCONFIG = 8
class config_descriptor(Structure): _fields_ = [
("bLength", c_uint8),
("bDescriptorType", c_uint8),
("wTotalLength", c_uint16),
("bNumInterfaces", c_uint8),
("bConfigurationValue", c_uint8),
("iConfiguration", c_uint16),
("bmAttributes", c_uint8),
("MaxPower", c_uint8),
("interface", POINTER(interface)),
("extra", POINTER(c_uint8)), # Extra descriptors
("extralen", c_int) ]
class device_descriptor(Structure): _fields_ = [
("bLength", c_uint8),
("bDescriptorType", c_uint8),
("bcdUSB", c_uint16),
("bDeviceClass", c_uint8),
("bDeviceSubClass", c_uint8),
("bDeviceProtocol", c_uint8),
("bMaxPacketSize0", c_uint8),
("idVendor", c_uint16),
("idProduct", c_uint16),
("bcdDevice", c_uint16),
("iManufacturer", c_uint8),
("iProduct", c_uint8),
("iSerialNumber", c_uint8),
("bNumConfigurations", c_uint8) ]
class ctrl_setup(Structure): _fields_ = [
("bRequestType", c_uint8),
("bRequest", c_uint8),
("wValue", c_uint16),
("wIndex", c_uint16),
("wLength", c_uint16) ]
REQ_GET_STATUS = 0x00
REQ_CLEAR_FEATURE = 0x01
# 0x02 is reserved
REQ_SET_FEATURE = 0x03
# 0x04 is reserved
REQ_SET_ADDRESS = 0x05
REQ_GET_DESCRIPTOR = 0x06
REQ_SET_DESCRIPTOR = 0x07
REQ_GET_CONFIGURATION = 0x08
REQ_SET_CONFIGURATION = 0x09
REQ_GET_INTERFACE = 0x0A
REQ_SET_INTERFACE = 0x0B
REQ_SYNCH_FRAME = 0x0C
TYPE_STANDARD = (0x00 << 5)
TYPE_CLASS = (0x01 << 5)
TYPE_VENDOR = (0x02 << 5)
TYPE_RESERVED = (0x03 << 5)
RECIP_DEVICE = 0x00
RECIP_INTERFACE = 0x01
RECIP_ENDPOINT = 0x02
RECIP_OTHER = 0x03
ENDPOINT_IN = 0x80
ENDPOINT_OUT = 0x00
# Error codes
ERROR_BEGIN = 500000
#if 1
#define USB_LE16_TO_CPU(x) do { x = ((x & 0xff) << 8) | ((x & 0xff00) >> 8); } while(0)
#else
#define USB_LE16_TO_CPU(x)
#endif
device_p = POINTER("device")
bus_p = POINTER("bus")
class device(Structure): _fields_ = [
("next", device_p),
("prev", device_p),
("filename", c_char*(PATH_MAX + 1)),
("bus", bus_p),
("descriptor", device_descriptor),
("config", POINTER(config_descriptor)),
("dev", c_void_p), # Darwin support
("devnum", c_char),
("num_children", c_uint8),
("children", POINTER(device_p)) ]
SetPointerType(device_p, device)
class bus(Structure): _fields_ = [
("next", bus_p),
("prev", bus_p),
("dirname", c_char*(PATH_MAX + 1)),
("devices", device_p),
("location", c_uint),
("root_dev", device_p) ]
SetPointerType(bus_p, bus)
dev_handle_p = c_void_p
func(dll.usb_open, device_p, retval=dev_handle_p, rename="_open")
func(dll.usb_close, dev_handle_p, retval=c_int)
func(dll.usb_get_string, dev_handle_p, c_int, c_int, c_char_p, c_int, retval=c_int)
func(dll.usb_get_string_simple, dev_handle_p, c_int, c_char_p, c_int, retval=c_int)
func(dll.usb_get_descriptor_by_endpoint, dev_handle_p, c_int, c_uint8, c_uint8, c_void_p, c_int, retval=c_int)
func(dll.usb_get_descriptor, dev_handle_p, c_uint8, c_uint8, c_void_p, c_int, retval=c_int)
func(dll.usb_bulk_write, dev_handle_p, c_int, c_char_p, c_int, c_int, retval=c_int)
func(dll.usb_bulk_read, dev_handle_p, c_int, c_char_p, c_int, c_int, retval=c_int)
func(dll.usb_interrupt_write, dev_handle_p, c_int, c_char_p, c_int, c_int, retval=c_int)
func(dll.usb_interrupt_read, dev_handle_p, c_int, c_char_p, c_int, c_int, retval=c_int)
func(dll.usb_control_msg, dev_handle_p, c_int, c_int, c_int, c_int, c_char_p, c_int, c_int, retval=c_int)
func(dll.usb_set_configuration, dev_handle_p, c_int, retval=c_int)
func(dll.usb_claim_interface, dev_handle_p, c_int, retval=c_int)
func(dll.usb_release_interface, dev_handle_p, c_int, retval=c_int)
func(dll.usb_set_altinterface, dev_handle_p, c_int, retval=c_int)
func(dll.usb_resetep, dev_handle_p, c_uint16, retval=c_int)
func(dll.usb_clear_halt, dev_handle_p, c_uint16, retval=c_int)
func(dll.usb_reset, dev_handle_p, retval=c_int)
func(dll.usb_strerror, retval=c_char_p)
func(dll.usb_init)
func(dll.usb_set_debug, c_int)
func(dll.usb_find_busses, retval=c_int)
func(dll.usb_find_devices, retval=c_int)
func(dll.usb_device, dev_handle_p, retval=device_p, rename="get_device")
func(dll.usb_get_busses, retval=bus_p)
func(dll.usb_detach_kernel_driver_np, dev_handle_p, c_int, retval=c_int)
# workaround for bug in ctypes 0.9.6 (cannot create functions with c_void_p as retval)
def open(dev):
return cast(_open(dev), dev_handle_p)
# -*- coding: utf-8 -*-
# Copyright (c) 2007 Johannes Hölzl <johannes.hoelzl@gmx.de>
#
# This library is covered by the GNU LGPL, read LICENSE for details.
# For documentation of the baudrate table see:
#
# [AN205] Silicon Labs Application Note 205 Rev. 0.3
# http://www.silabs.com/public/documents/tpub_doc/anote/Microcontrollers/Interface/en/an205.pdf
import re
from ConfigParser import ConfigParser
import cp210x
from cp210x import VALUES, SIZE_BAUDRATES
__all__ = ['read_file', 'write_file', 'update_values', 'PrescalerIsZero',
'ValuesFileError']
class ValuesError(StandardError):
pass
class PrescalerIsZero(ValuesError):
pass
class ValuesFileError(ValuesError):
pass
version_pattern = re.compile(r'^\s*(\d\d?)\.(\d\d?)\s*$')
def read_version(s):
match = version_pattern.match(s)
if match is None:
raise ValueError("Version does not match 'xx.yy'")
return (int(match.group(1)), int(match.group(2)))
def write_version(v):
return "%d.%02d" % v
def read_hex(s):
return int(s.strip(), 16)
def write_hex(num):
return "%04X" % num
def write_bool(b):
if b:
return 'yes'
else:
return 'no'
def read_bool(s):
s = s.strip().lower()
if s not in ['true', 'yes', 'false', 'no']:
raise ValueError("Boolean must be either 'true', 'yes', 'false' or 'no'.")
return s in ['true', 'yes']
def read_baudrate_info(s):
values = s.split(',')
if len(values) != 3:
raise ValueError("Baudrate info must be three comma-separated items")
try:
baudgen = read_hex(values[0])
except ValueError:
raise ValueError("The first baudrate info must be a hex-value")
try:
timer0 = read_hex(values[1])
except ValueError:
raise ValueError("The second baudrate info must be a hex-value")
try:
prescale = int(values[2])
except ValueError:
raise ValueError("The thirdbaudrate info must be a number")
return (baudgen, timer0, prescale)
TYPES = {
'boolean': (read_bool, write_bool),
'int': (int, str),
'id': (read_hex, write_hex),
'string': (str, str),
'version': (read_version, write_version),
}
def read_file(fp):
cp = ConfigParser()
if isinstance(fp, str):
cp.read([fp])
else:
cp.readfp(fp)
values = {}
for name, type in VALUES:
if name is 'baudrate_table':
continue
reader, _ = TYPES[type]
if cp.has_option('usb device', name):
try:
values[name] = reader(cp.get('usb device', name))
except ValueError, err:
raise ValuesFileError("Key '%s': %s" % (name, str(err)))
if cp.has_section('baudrate table'):
baudrate_table = []
for name, value in cp.items('baudrate table'):
try:
baudrate = int(name)
except ValueError:
raise ValuesFileError("Key names in 'baudrate table' must be"
" baudrate numbers.")
try:
baudrate_table.append(read_baudrate_info(value) + (baudrate, ))
except ValueError, err:
raise ValuesFileError("Wrong baudrate info %i: %s"
% (baudrate, str(err)))
baudrate_table.sort(key=(lambda i: i[3]), reverse=True)
values['baudrate_table'] = baudrate_table
return values
def write_file(fp, values):
fp.write("[usb device]\n")
for name, type in VALUES:
if name == 'baudrate_table':
continue
_, writer = TYPES[type]
if name in values:
fp.write("%s = %s\n" % (name, writer(values[name])))
if 'baudrate_table' in values:
fp.write("\n")
fp.write("[baudrate table]\n")
for (baudgen, timegen, prescaler,
baudrate) in sorted(values['baudrate_table'], key=(lambda i: i[3]),
reverse=True):
fp.write("%7d = %04X, %04X, %d # %s\n"
% (baudrate, baudgen, timegen, prescaler,
show_baudrate(baudgen, timegen, prescaler)))
def calc_baudrate(baudgen, timegen, prescaler):
# This formulas are from AN205 page 5.
if prescaler == 0:
raise PrescalerIsZero("Prescaler is 0")
baudrate = (24000000. / prescaler) / (0x10000 - baudgen)
return (baudrate, (0x10000 - timegen) * 2)
def show_baudrate(baudgen, timegen, prescaler):
try:
baudrate, timeout = calc_baudrate(baudgen, timegen, prescaler)
except PrescalerIsZero:
return "Wrong data, Prescaler is 0."
if timeout >= 1000:
timeout = "%1.3f ms" % (float(timeout) / 1000)
else:
timeout = "%d us" % timeout
if baudrate is None:
return ", %s" % (baudrate, timeout)
else:
return "%7.0f Baud, %s" % (baudrate, timeout)
def update_values(v, new, dev):
old_baudrate_table = v.get('baudrate_table')
new_baudrate_table = new.get('baudrate_table')
v.update(new)
# update baudrate table
# it is needed, that the baudrate table has 32 entries when it is written
# to the eeprom or device.
if ((old_baudrate_table is not None or new_baudrate_table is not None) and
(new_baudrate_table is None or
len(new_baudrate_table) < SIZE_BAUDRATES)):
if old_baudrate_table is not None:
if len(old_baudrate_table) < SIZE_BAUDRATES:
baudrate_table = old_baudrate_table
else:
baudrate_table = list(merge_baudrate_table(dev.baudrate_table,
old_baudrate_table))
else:
baudrate_table = dev.baudrate_table
if new_baudrate_table:
baudrate_table = list(merge_baudrate_table(baudrate_table,
new_baudrate_table))
v['baudrate_table'] = baudrate_table
def merge_baudrate_table(old, new):
for (old_info, (start, stop)) in zip(old, REQUEST_BAUDRATE_RANGES):
for baudgen, timer, prescaler, baudrate in new:
if ((start is None or baudrate <= start) and
baudrate >= stop):
yield (baudgen, timer, prescaler, baudrate)
break
else:
yield old_info
REQUEST_BAUDRATE_RANGES = [
# The table data is from AN205 Table 1 on page 1.
# Start End Default Baudrate
(None, 2457601), # Undefined
(2457600, 1474561), # Undefined
(1474560, 1053258), # Undefined
(1053257, 670255), # 921600
( 670254, 567139), # 576000
( 567138, 491521), # 500000
( 491520, 273067), # 460800
( 273066, 254235), # 256000
( 254234, 237833), # 250000
( 237832, 156869), # 230400
( 156868, 129348), # 153600
( 129347, 117029), # 128000
( 117028, 77609), # 115200
( 77608, 64112), # 76800
( 64111, 58054), # 64000
( 58053, 56281), # 57600
( 56280, 51559), # 56000
( 51558, 38602), # 51200
( 38601, 28913), # 38400
( 28912, 19251), # 28800
( 19250, 16063), # 19200
( 16062, 14429), # 16000
( 14428, 9613), # 14400
( 9612, 7208), # 9600
( 7207, 4804), # 7200
( 4803, 4001), # 4800
( 4000, 2401), # 4000
( 2400, 1801), # 2400
( 1800, 1201), # 1800
( 1200, 601), # 1200
( 600, 301), # 600
( 300, 57), # 300
]
#! /usr/bin/env python
# coding: utf8
# Copyright CERN, 2011
# Author: Matthieu Cattin <matthieu.cattin@cern.ch>
# Licence: GPL v2 or later.
# Website: http://www.ohwr.org
import sys
import rr
import time
import os
import re
from ctypes import *
from ptsexcept import *
from cp210x_eeprom import usb, valuefile, cp210x
from cp210x_eeprom.eeprom import EEPROM
"""
test21: Store calibration data to calibraton box CP2103 EEPROM
"""
# Calibration box vendor and device IDs
BOX_USB_VID = "10C4" # Cygnal Integrated Products, Inc.
BOX_USB_PID = "EA60" # CP210x Composite Device
CALIBR_RANGES = ['10V', '1V', '100mV']
def find_device(vid,pid):
usb_patterns = []
vid = int(vid, 16)
pid = int(pid, 16)
usb_patterns.append(dict(idVendor=vid, idProduct=pid))
for dev in cp210x.Cp210xProgrammer.list_devices(usb_patterns):
return dev
def get_calibr_data(dev):
dev.open()
try:
eeprom = EEPROM(dev)
finally:
dev.close()
eeprom_value = eeprom.get_values()
product_string = eeprom_value['product_string']
print "Product string: \"%s\"" % product_string
calibr_string_list = product_string.split(' ')
if len(calibr_string_list) != 3:
raise Exception('Product string has the wrong format.')
calibr_data = {}
for i in range(len(calibr_string_list)):
pattern = r'\b[0-9]\.[0-9]{8}\b'
if re.search(pattern, calibr_string_list[i]):
calibr_data[CALIBR_RANGES[i]] = calibr_string_list[i]
else:
raise Exception('Product string has the wrong format.')
return calibr_data
def set_calibr_data(dev, data):
dev.open()
product_string = data[CALIBR_RANGES[0]] + ' ' + data[CALIBR_RANGES[1]] + ' ' + data[CALIBR_RANGES[2]]
print "New product string value: \"%s\"" % product_string
try:
dev.set_product_string(product_string)
print "Calibration data written to cp210x EEPROM."
finally:
dev.close()
def input_calibr_data(range_str):
while(True):
calibr_string = raw_input("Enter calibration voltage for "+range_str+" range (in volts, max 10 char): ")
pattern = r'\b[0-9]\.[0-9]{8}\b'
if re.search(pattern, calibr_string):
return calibr_string
else:
print "[ERROR] Calibration string format is wrong, MUST be '\\b[0-9]\.[0-9]{8}\\b' (e.g 0.00000000).\n"
continue
def main ():
usb.init()
dev = find_device(BOX_USB_VID, BOX_USB_PID)
# Ask user to input calibration data
calibr_data = {}
for range_str in CALIBR_RANGES:
calibr_data[range_str] = input_calibr_data(range_str)
# Write calibration data to cp210x EEPROM
set_calibr_data(dev, calibr_data)
print "Readback calibration data from cp210x EEPROM."
calibr_data = get_calibr_data(dev)
for range_str, value in calibr_data.iteritems():
print "%5s range calibration voltage is: %s V" % (range_str, value)
if __name__ == '__main__' :
main()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment