Skip to content

EV2300 — USB-to-I2C Adapter

Controls Texas Instruments EV2300A USB-to-SMBus/I2C adapters for communicating with battery fuel gauges, charger ICs, and other I2C peripherals.

  • The EV2300 is auto-detected via USB HID — no VISA or NI-VISA required.
  • Addresses and registers accept hex (0x08) or decimal (8) notation.
  • 16-bit word reads/writes use big-endian wire order (high byte at the lower register address). This matches the BQ76920/BQ76940 register-pair convention (e.g. VC1_HI at 0x0C, VC1_LO at 0x0D). To update a single 8-bit register, use write_byte -- a write_word always writes TWO consecutive registers.

USB HID adapter for SMBus/I2C communication. VID 0x0451, PID 0x0036. Pure-Python driver — no TI drivers or bqStudio required for basic register access.


ev2300 info

Show device identification: VID, PID, serial number, product name, manufacturer, and firmware version.

ev2300 info
  vid           : 0x0451
  pid           : 0x0036
  serial        : HPA02 FW:2.0a/TUSB3210:0
  product       : EV2300A
  manufacturer  : Texas Inst
  version       : 0x0002

ev2300 read_word

Read a 16-bit little-endian word from an I2C device register.

ev2300 read_word <i2c_addr> <register>
Parameter Required Values Description
i2c_addr required 0x00 – 0x7F 7-bit I2C slave address.
register required 0x00 – 0xFF Register (command code) to read.
ev2300 read_word 0x08 0x0C    # cell 1 voltage on a BQ76920 (VC1_HI/LO)
ev2300 read_word 0x08 0x2A    # pack voltage (BAT_HI/LO)
ev2300 read_word 0x08 0x32    # coulomb counter (CC_HI/LO)
ev2300 read_word 8 12         # same as 0x08 0x0C in decimal

Returns the value in both hex and decimal: 0x24BB (9403) — raw 14-bit ADC for ~3.51 V cell at GAIN=369 uV/LSB.


ev2300 write_word

Write a 16-bit little-endian word to an I2C device register.

ev2300 write_word <i2c_addr> <register> <value>
Parameter Required Values Description
i2c_addr required 0x00 – 0x7F 7-bit I2C slave address.
register required 0x00 – 0xFF Register (command code) to write.
value required 0x0000 – 0xFFFF 16-bit value to write.
ev2300 write_word 0x08 0x09 0xAC97    # atomic write: OV_TRIP=0xAC, UV_TRIP=0x97
                                      # (high byte 0xAC -> reg 0x09, low byte 0x97 -> reg 0x0A)

write_word always touches TWO consecutive registers

write_word 0x08 0x0A 0x0097 looks like "write 0x97 to UV_TRIP", but it actually writes the high byte 0x00 to UV_TRIP and the low byte 0x97 to CC_CFG (0x0B) -- corrupting the CC_CFG=0x19 init value that BQ76920 datasheet SLUSBK2I Section 8.5 Register Maps requires ("CC_CFG ... Must be programmed to 0x19"). To set a single 8-bit threshold register, always use write_byte:

ev2300 write_byte 0x08 0x09 0xAC    # only OV_TRIP -- safe
ev2300 write_byte 0x08 0x0A 0x97    # only UV_TRIP -- safe

ev2300 read_byte

Read a single byte from an I2C device register.

ev2300 read_byte <i2c_addr> <register>
Parameter Required Values Description
i2c_addr required 0x00 – 0x7F 7-bit I2C slave address.
register required 0x00 – 0xFF Register to read.
ev2300 read_byte 0x08 0x00    # SYS_STAT  (alert flags)
ev2300 read_byte 0x08 0x04    # SYS_CTRL1 (ADC_EN bit 4)
ev2300 read_byte 0x08 0x0B    # CC_CFG    (must be 0x19 after init)
ev2300 read_byte 0x08 0x50    # ADCGAIN1  (factory ADC gain trim)

Returns the value in hex and decimal: 0x19 (25) — confirms BQ76920 init wrote CC_CFG correctly.


ev2300 write_byte

Write a single byte to an I2C device register.

ev2300 write_byte <i2c_addr> <register> <value>
Parameter Required Values Description
i2c_addr required 0x00 – 0x7F 7-bit I2C slave address.
register required 0x00 – 0xFF Register to write.
value required 0x00 – 0xFF Byte value to write.
ev2300 write_byte 0x08 0x0B 0x19    # CC_CFG := 0x19 (datasheet-required init)
ev2300 write_byte 0x08 0x05 0x40    # SYS_CTRL2 := CC_EN (bit 6)
ev2300 write_byte 0x08 0x00 0xFF    # clear all SYS_STAT fault bits (write-1-to-clear)

SYS_STAT (register 0x00) is write-1-to-clear, NOT normal R/W

write_byte 0x08 0x00 0x80 does NOT replace SYS_STAT with 0x80. Per the BQ76920 datasheet (SLUSBK2I, section 8.5.1): "Bits in SYS_STAT may be cleared by writing a '1' to the corresponding bit. Writing a '0' does not change the state of the corresponding bit."

So writing 0x80 clears only bit 7 (CC_READY) and leaves every other bit untouched. Any latched fault (OV at bit 2, UV at bit 3, DEVICE_XREADY at bit 5, SCD at bit 1, OCD at bit 0) stays latched. Prefer the dedicated helpers ev2300 sys_stat and ev2300 clear_faults instead of raw write_byte for this register -- see below.


ev2300 sys_stat

Read SYS_STAT (register 0x00) and decode each bit so you can see exactly which faults are latched instead of trying to interpret an opaque hex value yourself.

ev2300 sys_stat [i2c_addr]
Parameter Required Default Description
i2c_addr optional 0x08 BQ76920 7-bit I2C address.

Example output after triggering overvoltage:

SYS_STAT (0x00) = 0x84 [10000100]
  bits set: CC_READY + OV
  latched faults: OV
  -> run 'ev2300 clear_faults' once the underlying condition is gone

Bit map (datasheet Table 8-3, p. 30):

Bit Name Latched? Meaning
7 CC_READY no (auto-re-asserts) Coulomb counter reading ready
6 RSVD - Reserved
5 DEVICE_XREADY yes Internal chip fault detected
4 OVRD_ALERT yes External ALERT pin override active
3 UV yes Undervoltage fault detected
2 OV yes Overvoltage fault detected
1 SCD yes Short-circuit in discharge fault
0 OCD yes Overcurrent in discharge fault

ev2300 clear_faults

Clear latched faults in SYS_STAT via write-1-to-clear semantics.

ev2300 clear_faults [i2c_addr] [mask]
Parameter Required Default Description
i2c_addr optional 0x08 BQ76920 7-bit I2C address.
mask optional 0xFF Bits to clear; default clears all.

The command writes mask to register 0x00 (W1C clears every bit set to 1 in the mask) and then re-reads SYS_STAT so you immediately see whether any fault re-latched. A fault bit will re-latch if the underlying protection condition is still true -- e.g. OV stays set while the cell voltage is above OV_TRIP (register 0x09).

ev2300 clear_faults            # clear everything (mask 0xFF)
ev2300 clear_faults 0x08 0x04  # clear only OV (bit 2)
ev2300 clear_faults 0x18       # clear all on alternate BQ address

Example output after an overvoltage was cleared by lowering the supply:

Wrote 0xFF to SYS_STAT (write-1-to-clear; bits set in mask are cleared)
SYS_STAT before: 0x84  ->  after: 0x00
  cleared: OV

If the underlying overvoltage condition is still active when you clear, the command tells you explicitly so you don't think the write failed:

SYS_STAT before: 0x84  ->  after: 0x04
  STILL active: OV (underlying condition still true -- check cell
  voltages / OV_TRIP / UV_TRIP)

Issue #128 -- hardware-verified reproduction and fix

A student wrote 0x80 to SYS_STAT after triggering OV with a 25 V supply, expecting that to "overwrite" the fault bit. The fault stayed latched. Live bench trace (HP E3631A P25V channel + BQ76920 EVM + EV2300):

baseline (PSU=18 V)                       SYS_STAT=0x00
after psu_set_voltage 25 V                SYS_STAT=0x80  bits=CC_READY
after psu_set_voltage 18 V (OV latched)   SYS_STAT=0x84  bits=CC_READY+OV  faults=OV
after wrong write_byte 0x80               SYS_STAT=0x04  bits=OV          faults=OV
after ev2300_clear_bq_faults              SYS_STAT=0x00  bits=(none)      faults=(none)

The write_byte 0x80 step is the one that confused the student: SYS_STAT dropped from 0x84 to 0x04, not because the OV bit changed, but because the CC_READY bit (bit 7) cleared and OV (bit 2) stayed exactly where it was. The fix is ev2300 clear_faults, which writes 0xFF so every set bit gets cleared by the W1C semantics in one shot.


ev2300 read_block

Read a variable-length block of bytes from an I2C device register (SMBus Block Read).

ev2300 read_block <i2c_addr> <register>
Parameter Required Values Description
i2c_addr required 0x00 – 0x7F 7-bit I2C slave address.
register required 0x00 – 0xFF Register to read.
ev2300 read_block 0x08 0x06    # PROTECT1, PROTECT2 (auto-incrementing address)

Returns the block as hex bytes: [2 bytes] 00 00

read_block returns 2 bytes by default on this driver

The toolkit's read_block does not include an explicit byte-count in the request, so the bridge defaults to a 2-byte transfer. To read more bytes (e.g. 6 consecutive registers PROTECT1..CC_CFG), issue six read_byte calls -- the BQ76920 auto-increments the register pointer per byte (datasheet SLUSBK2I Section 8.3.1.4 Communications Subsystem).

Tip

Block reads are commonly used to retrieve string registers. The returned bytes are raw — interpret them as ASCII if reading a text field.


ev2300 write_block

Write a variable-length block of bytes to an I2C device register (SMBus Block Write).

ev2300 write_block <i2c_addr> <register> <hex_bytes>
Parameter Required Values Description
i2c_addr required 0x00 – 0x7F 7-bit I2C slave address.
register required 0x00 – 0xFF Register to write.
hex_bytes required hex string Data bytes as a hex string (no 0x prefix, no spaces).
ev2300 write_block 0x55 0x44 0203    # write two bytes [0x02, 0x03] to register 0x44
ev2300 write_block 0x55 0x00 AABB    # write two bytes [0xAA, 0xBB] to register 0x00

Note

The hex string must be an even number of characters. Each pair of characters is one byte: AABB = [0xAA, 0xBB].


ev2300 send_byte

Send a single command byte to an I2C address with no register or data (SMBus Send Byte protocol).

ev2300 send_byte <i2c_addr> <command>
Parameter Required Values Description
i2c_addr required 0x00 – 0x7F 7-bit I2C slave address.
command required 0x00 – 0xFF Command byte to send.
ev2300 send_byte 0x55 0x12    # send command byte 0x12 to device at 0x55

ev2300 scan

Probe all 256 registers (0x00 – 0xFF) on a given I2C address using word reads, and list every register that responds successfully.

ev2300 scan <i2c_addr>
Parameter Required Values Description
i2c_addr required 0x00 – 0x7F 7-bit I2C slave address to scan.
ev2300 scan 0x08    # scan all registers on device at 0x08 (BQ76920)

Prints every readable register with its value:

Found 38 readable registers:
  0x00: 0x0080 (128)        # SYS_STAT (CC_READY high)
  0x04: 0x0010 (16)         # SYS_CTRL1 (ADC_EN)
  0x05: 0x0040 (64)         # SYS_CTRL2 (CC_EN)
  0x09: 0x00AC (172)        # OV_TRIP
  0x0A: 0x0097 (151)        # UV_TRIP
  0x0B: 0x0019 (25)         # CC_CFG
  0x0C: 0x24BB (9403)       # VC1_HI/LO (cell 1 ~3.51 V)
  0x2A: 0x2DEC (11756)      # BAT_HI/LO (pack ~17.59 V)
  ...

Warning

This sends 256 read transactions, which takes several seconds. Some devices may respond poorly to reads on undefined registers — use with caution on unfamiliar hardware.


ev2300 probe

Send an arbitrary HID command code for low-level debugging and reverse engineering. This bypasses the standard read/write helpers and sends a raw EV2300 protocol packet.

ev2300 probe <cmd_code> [i2c_addr] [register]
Parameter Required Values Description
cmd_code required 0x00 – 0xFF EV2300 protocol command code.
i2c_addr optional 0x00 – 0x7F I2C address (default: 0x00).
register optional 0x00 – 0xFF Register (default: 0x00).
ev2300 probe 0x01 0x08 0x0B    # manual read_word of CC_CFG
ev2300 probe 0x31              # firmware version (BQ76920_Bridge extension cmd)

Shows the raw response: 0x01: resp=0x41 [AA 41 06 2C 00 E4 0C ...]

Note

cmd 0x31 (BRIDGE_CMD_GET_VERSION) returns ASCII MAJOR.MINOR.PATCH+githash — only on the open-source BQ76920_Bridge replacement, not real EV2300 hardware.

Note

Standard command codes: 0x01 = read word, 0x02 = read block, 0x03 = read byte, 0x04 = write word, 0x05 = write block, 0x06 = send byte, 0x07 = write byte.


ev2300 state

ev2300 state <on|off|safe|reset>
Value Effect
on No-op (EV2300 has no output to enable)
off No-op
safe No-op — EV2300 is a passive adapter
reset Disconnect and reconnect the USB HID session

BQ76920 quickstart

End-to-end flow for ESET 453 lab 5 with the BQ76920 EVM (HPA002, default I2C address 0x08):

# 1. Make sure the EVM is powered (PSU at 18 V on BAT+/BAT-) and the
#    cell-sim DIP switches are CLOSED, otherwise UV trips immediately.
psu set 2 18.0 0.5
psu chan 2 on

# 2. Wait for the BQ to ACK -- press the BOOT button on the EVM during this
#    window. The BQ76920 stays in SHIP mode after every POR event until the
#    TS1 pin is pulsed above V_BOOT (datasheet SLUSBK2I sec 8.4.2 SHIP Mode).
ev2300 wait_for_bq 30

# 3. Verify init -- CC_CFG MUST read 0x19 (datasheet SLUSBK2I sec 8.5
#    Register Maps: "CC_CFG ... Must be programmed to 0x19"). If it
#    reads 0x00, see the troubleshooting page (sticky-init bug workaround).
ev2300 read_byte 0x08 0x0B

# 4. Read live measurements
ev2300 read_word 0x08 0x0C    # cell 1 voltage
ev2300 read_word 0x08 0x2A    # pack voltage
ev2300 read_byte 0x08 0x00    # SYS_STAT (any latched faults)

Expected values with the cell-sim divider closed and 18 V on BAT+:

Register Value Meaning
0x0B CC_CFG 0x19 datasheet-required init value (host or bridge must write)
0x04 SYS_CTRL1 0x10 ADC_EN set
0x05 SYS_CTRL2 0x40 CC_EN set
0x00 SYS_STAT 0x80 only CC_READY (no faults)
0x0C VC1 (word) ~0x24BB ~3.51 V per cell with GAIN=369 uV/LSB
0x2A BAT (word) ~0x2DEC ~17.59 V pack (PSU 18 V minus cable + ADC offset)

Setup

The EV2300 is detected automatically when plugged in via USB. No VISA drivers are needed.

Requirements:

  • EV2300A must have firmware loaded (not in bootloader mode). If the REPL reports "bootloader mode", flash firmware using TI bqStudio.
  • On Windows: no extra drivers needed — the built-in HID driver is used.
  • On Linux: the user must have permission to access /dev/hidrawN. Create a udev rule:
# /etc/udev/rules.d/99-ev2300.rules
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0451", ATTRS{idProduct}=="0036", MODE="0666"

Then reload: sudo udevadm control --reload-rules && sudo udevadm trigger

  • On macOS: install hidapi: pip install hidapi

Troubleshooting

"Device error (0x46)" or I2C communication failures

This usually means the EV2300 USB-to-I2C bridge is in a bad state. Common causes:

  • The fuel gauge / BQ EVM was powered on after the EV2300 was connected
  • A previous session left the I2C bus in a stuck state
  • Another program (e.g. BQ Studio) had the EV2300 open

Recovery steps (also available via ev2300 fix in the REPL):

  1. Make sure the BQ EVM board is powered (e.g. PSU set to 18V) — do not turn the PSU off, the BQ chip needs to stay powered
  2. Press the BOOT button on the BQ EVM board — this resets both the EV2300 bridge and the fuel gauge IC
  3. In the REPL, disconnect the adapter: disconnect ev2300
  4. Re-scan to pick it back up: scan
  5. Retry your command

Shortcut: ev2300 cycle bundles steps 3–4 with an interactive BOOT-press prompt and an explicit reminder that the PSU should stay on. Use it when you hit Write failed or wedged I2C state during a session:

eset> ev2300 cycle
[INFO] Disconnecting EV2300 (PSU untouched -- BQ EVM stays powered)...
[WARNING] ACTION REQUIRED -- press the BOOT button on the BQ76920 EVM now.
Press Enter to reconnect the EV2300...
[INFO] Reconnecting EV2300...
[SUCCESS] EV2300 reconnected. Try your read/write commands again.

If it still doesn't work, unplug the EV2300 USB cable, plug it back in, then run disconnect ev2300, scan, and retry.

Why does disconnect-and-reconnect fix it?

The EV2300's firmware has an internal USB-to-I2C state machine that can get stuck after a NACK, bus contention, or timing glitch from the BQ slave. Once stuck, every subsequent HID write returns "Write failed" because the firmware refuses to start a new transaction on top of the wedged one. Host-side flush_input() only drains the OS-side HID receive buffer — it does not reach into the EV2300 firmware to clear its stuck state.

Closing the OS-side HID handle (disconnect) and opening it fresh (scan or ev2300 cycle) signals the EV2300 firmware to clear its internal state machine. Combined with a BOOT-button press on the BQ EVM (which hard-resets the slave so it ACKs cleanly on the next transaction), this clears almost every wedged-bus condition without touching the PSU.

"connect failed" during scan

If the EV2300 is detected but cannot be opened, another program likely has it locked. Close BQ Studio, bqStudio, or any other TI tool that may be using the EV2300, then run scan again.

Best practices

  • Power on the BQ EVM board before launching the REPL
  • If using a BQ EVM with a BOOT button, press it once after power-on to ensure a clean I2C bus
  • Close BQ Studio before starting the REPL — only one program can hold the EV2300 HID handle at a time

Tips & Gotchas

Things that trip students up most often on the EV2300.

TI EV2300

  • HID interface. Communicates over USB HID, not VISA. No NI-VISA required.
  • I2C address format. Addresses are 7-bit. Use hex: ev2300 read_word 0x08 0x09.
  • Bus recovery. If I2C communication hangs, use ev2300 fix to attempt bus recovery.
  • Slow for bulk transfers. HID has limited bandwidth. Block reads of large registers take time.