Skip to content

Commit

Permalink
Switch to typer for the CLI, which gives an awesome user experience
Browse files Browse the repository at this point in the history
  • Loading branch information
multiplemonomials committed Mar 3, 2024
1 parent ed85a38 commit 4f269a5
Show file tree
Hide file tree
Showing 9 changed files with 447 additions and 166 deletions.
23 changes: 0 additions & 23 deletions .github/workflows/pypi_deploy.yml

This file was deleted.

38 changes: 27 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,23 @@ One disadvantage of the CY7C652xx chips is that their software options are somew

However, the available drivers provide absolutely no provision for reprogramming the "configuration block", the binary structure stored in the chip's flash memory which defines its USB attributes. This includes manufacturer-set stuff such as the VID, PID, and serial number, but also the flag that tells it whether it should be a UART, I2C, or SPI bridge, and the default settings which are used for each bus type. The configuration block can only be programmed using the closed-source Cypress USB Serial Configuration Utility (USCU) -- and to add to the pain, this is only available as a Windows GUI application!

This driver is being worked on with the goal of, in addition to providing a translation of the normal C driver into Python, also reverse-engineering the format of the config block and providing utilities to modify and rewrite it. Basic config rewriting functionality is working, and I have been able to dynamically edit the parameters of a CY7C65211 and change it between I2C, SPI, and UART mode! Additionally, since the code quality of the libusb1 driver is... not great (it uses multiple threads internally for no good reason I can find), I've been trying to clean up and simplify the way that data is transferred to and from the device.
This driver is being worked on with the goal of, in addition to providing a translation of the libusb C driver into Python, also reverse-engineering the format of the config block and providing utilities to modify and rewrite it. Basic config rewriting functionality is working, and I have been able to dynamically edit the parameters of a CY7C65211 and change it between I2C, SPI, and UART mode! Additionally, since the code quality of the libusb1 driver is... not great (it uses multiple threads internally rather than the libusb asynchronous API), I've been trying to clean up and simplify the way that data is transferred to and from the device.

### You *should* use this driver if you want to
- Have a nice Python API for interacting with CY7C652xx bridge chips in UART, I2C, and SPI mode
- Switch a CY7C652xx in between UART, I2C, and SPI mode at runtime (the official driver does NOT support this at all)
- Use an OS-agnostic CLI and/or Python API to provision CY7C652xx chips with the correct VID, PID, description, and serial number values

### You should *not* use this driver if you want to
- Ship an easy solution that works with no additional user setup on Windows machines
- Just use the serial bridge chip as a plug-and-play COM port (just use USCU to configure it in that case!)
- Rapidly switch between I2C/SPI/UART modes (it takes almost half a second to reprogram the config block and re-enumerate the device)
- Do continuous UART transfers (see below)
- Ship an easy solution that works with no additional user setup on Windows machines (see Windows section below)
- Just use the serial bridge chip as a USB-UART adapter only (in this case, just use USCU to configure it once and then use a regular serial port library to operate it)
- Rapidly switch between I2C/SPI/UART modes (it takes up to a few seconds to reprogram the config block and re-enumerate the device)

## Warnings

This driver is not very well tested yet, and I would advise against relying on it for anything important. I am testing it only with a CY7C65211 dev kit, and have not tried it with the single-purpose devices or with a dual-channel device like the CY7C65215. It should work for those devices, but I cannot guarantee anything.
This driver is not very well tested yet, and I would advise against relying on it for anything important without additional testing. I am testing it only with a CY7C65211 dev kit, and have not tried it with the single-purpose devices or with a dual-channel device like the CY7C65215. It should work for those devices, but I cannot guarantee anything.

Additionally, I assume that it would be possible to brick your CY7C652xx by loading an incorrect configuration block onto it. I would highly recommend doing a load operation first to download the configuration block from your specific model of chip, and then modifying it and writing it back. Writing back configurations obtained from anywhere else could be a dangerous operation! I take no responsibility for any chips bricked through usage of this tool.
Additionally, I assume that it would be possible to brick your CY7C652xx by loading an incorrect configuration block onto it. Rather than downloading any of the configuration bin files in this repo, I would highly recommend doing a load operation first to download the configuration block from your specific model of chip, and then modifying it and writing it back (this is already how the reconfigure functionality works). Writing back configurations obtained from anywhere else could be a dangerous operation! I take no responsibility for any chips bricked through usage of this tool.

## Functionality
### Currently Supported
Expand All @@ -47,7 +46,7 @@ Additionally, I assume that it would be possible to brick your CY7C652xx by load

## Using the Command-Line Interface

This driver installs a command-line interface script, `cy_serial_bridge_cli`. It supports a number of functions:
This driver installs a command-line interface script, `cy_serial_cli`. It supports a number of functions:
```
usage: cy_serial_cli [-h] [-V VID] [-P PID] [-n NTH] [-s SCB] [-v] {scan,save,load,decode,type,reconfigure} ...
Expand Down Expand Up @@ -95,14 +94,32 @@ Detected Devices:
- 04b4:e011 (Type: UART_CDC) (SerNo: 14224672048496620243684302669570) (Type: UART_CDC) (Serial Port: 'COM6')
```

## Using the Python API

cy_serial_bridge provides a rich Python API that can be used to communicate with the serial bridge in each mode.

### I2C controller mode

First, you must open the device and set the configuration:
```python
import cy_serial_bridge

with cy_serial_bridge.open_device(cy_serial_bridge.DEFAULT_VID,
cy_serial_bridge.DEFAULT_PID,
cy_serial_bridge.OpenMode.I2C_CONTROLLER) as bridge:
bridge.set_i2c_configuration(cy_serial_bridge.driver.CyI2CConfig(frequency=400000))
```

## OS-Specific Info

### Windows
On Windows, cy_serial_bridge (and other libusb based programs) cannot connect to USB devices unless they have the "WinUSB" driver attached to them.

To set this up, you will need to use [Zadig](https://zadig.akeo.ie/). Simply run this program, click "Options > List All Devices" in the menu, find whichever USB devices represent the CY7C652xx (you might have to look at the VID & PID values), and install the WinUSB driver for them. Note that there will be at least two USB devices in the list for each bridge chip -- one for the communication interface and one for the configuration interface. You need to install the driver for *both* for this driver to work.

This process might have to be redone the first time that the bridge is used in each mode -- for example, if I connect a CY7C652xx to a fresh machine in SPI mode and install the driver using Zadig, then change the chip to operate in I2C mode in code, I may have to use Zadig again before Python code can open it in I2C mode. Zadig installation will also have to be redone if the VID or PID is changed, though it should stick for multiple devices in the same mode and with the same VIDs/PIDs.
This process might have to be redone the first time that the bridge is used in each mode -- for example, if I connect a CY7C652xx to a fresh machine in SPI mode and install the driver using Zadig, then change the chip to operate in I2C mode in code, I may have to use Zadig again before Python code can open it in I2C mode. Zadig installation will also have to be redone if the VID or PID is changed, though it only has to be done once per machine for a given VID-PID-operation mode combination.

I believe that it would be possible to script this a bit more gracefully, by writing a script to change the serial bridge into each mode and then invoke Zadig for each interface. This should be looked into more.

Also note that [NirSoft USBLogView](https://www.nirsoft.net/utils/usb_log_view.html) is extremely useful for answering the question of "what are the VID & PID of the USB device I just plugged in".

Expand Down Expand Up @@ -138,9 +155,8 @@ To determine what the VID and PID of your device currently are, you can use:
```shell
cy_serial_cli scan --all
```
This will do a heuristic search of all the USB devices on your machine to find ones which "look like" CY7C652xx chips based on their descriptor layout.

Also note that adding `--randomize-serno` to that command will assign a random serial number to the chip, which is helpful for provisioning new boards.
Also note that adding `--randomize-serno` to the reconfigure command will assign a random serial number to the chip, which is helpful for provisioning new boards.

Another issue: On Windows, if you have a given VID and PID assigned to use the WinUSB driver via Zadig, Windows will not try and use the USB CDC driver to enumerate COM ports from the device. This means that a device in SPI/I2C mode cannot use the same VID and PID as a device in UART CDC mode. To solve this, this driver automatically uses two PIDs for each device. The even PID is used in SPI/I2C mode, and the odd PID is used in UART CDC mode.

Expand Down
118 changes: 111 additions & 7 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "cy_serial_bridge"
# this is used by Ruff to disable "upgrade to feature x" inspections where x was added
# after the given version
requires-python = ">=3.8"
requires-python = ">=3.10"
version = "0.1.0"
description = "Pure Python driver for using and reconfiguring the CY7C652xx family of USB to SPI/I2C/UART bridge ICs."
authors = [
Expand Down Expand Up @@ -55,6 +55,8 @@ cy_serial_cli = 'cy_serial_bridge.cli:main'
python = '^3.8.1'
libusb1 = "^3.0.0"
pyserial = "^3.5"
typer = "^0.9"
rich = "^13.6"

[tool.poetry.group.dev.dependencies]
mypy = '^1.8.0'
Expand Down Expand Up @@ -159,13 +161,15 @@ ignore = [
'PLR2004', # Magic value used in comparison
'ANN101', # Don't require annotations for self. Waaaaay too complainy, plus deprecated in ruff
'ANN401', # Allow typing.any
'UP007', # Allow old style union annotations (typing.Optional/typing.Union). Typer does not support the new style ones.
'PYI025', # Allow using collections.abc.Set
]
ignore-init-module-imports = true
line-length = 120
# preview = true
show-fixes = true
src = ['src',]
target-version = 'py38'
target-version = 'py310'

[tool.ruff.flake8-quotes]
docstring-quotes = 'double'
Expand Down
Loading

0 comments on commit 4f269a5

Please sign in to comment.