AZIC Education
Component ReferencePIC Microcontrollers

PIC16F1704 — I2C Bridge Controller

PIC16F1704 microcontroller used as an I2C bridge in Antminer hash boards — protocol specification, command reference, and troubleshooting procedures.

Overview

The PIC16F1704 is a Microchip PIC microcontroller used in Antminer hash boards as an I2C bridge between the control board and the hash board's on-board peripherals. It sits on the main I2C bus (accessible from the control board via the signal connector) and provides bridged access to devices on the hash board's internal I2C bus, including temperature sensors (LM75A, NCT218) and the AT24C02 EEPROM.

The PIC acts as a protocol translator — it receives commands from the control board in a custom packet format, executes the corresponding I2C transactions on the internal bus, and returns the results. This architecture isolates the hash board's I2C bus from the control board's bus, preventing bus contention and allowing the PIC to manage timing-sensitive operations like voltage enable sequencing.

Specifications

ParameterValue
ManufacturerMicrochip
CorePIC (8-bit RISC)
Flash Memory4 KB
RAM512 bytes
Max Clock32 MHz (internal oscillator)
I2CMSSP module (master and slave capable)
Supply Voltage2.3V to 5.5V
PackageSOIC-14, TSSOP-14, QFN-16
I2C Address0x20-0x27 (shifts across resets)

I2C Address Behavior

Critical: The PIC16F1704 I2C address is not fixed. It can appear at any address in the range 0x20 to 0x27, and the address may change after a power cycle or reset. The EEPROM address shifts in sync, maintaining a fixed offset of +0x30 (e.g., PIC at 0x21 means EEPROM at 0x51).

When initializing communication with a hash board, always scan the full 0x20-0x27 range to locate the PIC. Do not hard-code a single address.

PIC AddressEEPROM Address
0x200x50
0x210x51
0x220x52
0x230x53
0x240x54
0x250x55
0x260x56
0x270x57

Communication Protocol

The PIC uses a custom packet-based protocol over I2C. The control board writes command packets to the PIC, waits for processing, then reads response packets.

Packet Format

Transmit (Control Board to PIC):

[55 AA] [LEN] [CMD] [param1] [param2] ... [CHECKSUM]

Receive (PIC to Control Board):

[LEN] [CMD] [STATUS] [data1] [data2] ... [CHECKSUM]

Field Descriptions

FieldSizeDescription
Sync2 bytes0x55 0xAA — marks the start of a command packet
LEN1 byteTotal packet length from LEN to CHECKSUM (inclusive)
CMD1 byteCommand code
STATUS1 byteResponse status (0x01 = success in most commands)
Params/DataN bytesCommand-specific parameters or response data
CHECKSUM1 byteSum of all bytes from LEN to last param/data byte, masked to 8 bits (& 0xFF)

Checksum Calculation

The checksum covers all bytes from LEN to the last parameter byte (not including the sync bytes 55 AA):

def calculate_checksum(packet_without_sync):
    """Calculate PIC protocol checksum.

    packet_without_sync: bytes from LEN to last param (before checksum)
    """
    return sum(packet_without_sync) & 0xFF

# Example: CMD 0x15 (voltage enable), params [0x01, 0x00]
# Packet: [55 AA] [05] [15] [01] [00] [checksum]
# LEN=0x05, CMD=0x15, param1=0x01, param2=0x00
# Checksum = (0x05 + 0x15 + 0x01 + 0x00) & 0xFF = 0x1B

Command Reference

CMD 0x15 — Voltage Enable

Enables or disables the hash board voltage rails. This is the primary command used during initialization to power on the ASIC chips.

Transmit:

[55 AA] [05] [15] [enable] [0x00] [checksum]
ParameterValueDescription
enable0x01Enable voltage output
enable0x00Disable voltage output

Response:

[05] [15] [STATUS] [00] [checksum]

CMD 0x15 is the confirmed voltage enable command. Earlier documentation incorrectly listed CMD 0x04 as the voltage enable command, but testing has confirmed 0x15 is correct.

CMD 0x16 — Status Query

Reads the current status of the hash board (voltage state, fault flags).

Transmit:

[55 AA] [03] [16] [checksum]

Response:

[06] [16] [STATUS] [data1] [data2] [checksum]

Typical successful response: 06 16 01 00 00 1D

CMD 0x3C — Sensor Read

Reads data from a device on the hash board's internal I2C bus (temperature sensors, EEPROM).

Transmit:

[55 AA] [06] [3C] [target_addr] [nbytes] [register] [checksum]
ParameterDescription
target_addrI2C address of the target device (e.g., 0x48 for LM75A)
nbytesNumber of bytes to read from the target
registerRegister address to read from on the target device

Response:

[LEN] [3C] [STATUS] [byte1] [byte2] ... [checksum]

Example — Read temperature from LM75A at 0x48:

# TX: Read 2 bytes from register 0x00 at device 0x48
tx = bytes([0x55, 0xAA, 0x06, 0x3C, 0x48, 0x02, 0x00])
checksum = (0x06 + 0x3C + 0x48 + 0x02 + 0x00) & 0xFF  # = 0x8C
tx += bytes([checksum])

# Wait 50ms for PIC to complete the I2C transaction
time.sleep(0.05)

# RX: [07] [3C] [01] [temp_msb] [temp_lsb] [checksum]
# temp_msb=0x19, temp_lsb=0x00 → 25.0°C

CMD 0x3B — Sensor Read (Alternate)

An alternate sensor read command observed in sniffed I2C traffic.

Transmit:

[55 AA] [06] [3B] [target_addr] [0x00] [0x00] [checksum]

CMD 0x07 — Date Read

Reads the manufacturing date or calibration date stored in the PIC's internal memory.

Transmit:

[55 AA] [03] [07] [checksum]

DANGER: Reading more than 2 bytes from CMD 0x07 can hang the PIC, requiring a hardware reset via the RST_N pin. Limit reads to exactly 2 bytes and implement a timeout. If the PIC becomes unresponsive after a CMD 0x07 call, toggle RST_N LOW for 10ms then HIGH to reset it.

CMD 0x06 — Hardware Info

Reads hardware version and configuration information.

Transmit:

[55 AA] [03] [06] [checksum]

Critical: Byte-by-Byte I2C Reads

The PIC16F1704 requires byte-by-byte I2C reads. Each response byte must be read as a separate I2C transaction with its own Start condition, address byte, NACK, and Stop condition.

Multi-byte reads (reading N bytes in a single I2C transaction) cause a shift register underrun in the PIC, resulting in bit-shifted garbage data. This is a hardware limitation of the PIC's MSSP module when operating as an I2C slave.

Correct (byte-by-byte):

response = []
for i in range(expected_length):
    i2c_start()
    i2c_write(pic_addr << 1 | 1)  # Read mode
    byte = i2c_read_nack()         # Read one byte, NACK
    i2c_stop()
    response.append(byte)

Incorrect (multi-byte — will produce garbage):

# DO NOT DO THIS
i2c_start()
i2c_write(pic_addr << 1 | 1)
for i in range(expected_length - 1):
    response.append(i2c_read_ack())   # ACK to continue
response.append(i2c_read_nack())      # NACK to end
i2c_stop()

Initialization Sequence

The standard initialization sequence for communicating with a hash board via the PIC bridge:

Scan for PIC Address

Scan I2C addresses 0x20 through 0x27. The first address that ACKs is the PIC.

Dump EEPROM

Calculate EEPROM address (PIC address + 0x30). Use CMD 0x3C to read the EEPROM contents for board identification.

Read Date (CMD 0x07)

Read the manufacturing/calibration date. Limit to 2 bytes. Implement a 100ms timeout.

Read Hardware Info (CMD 0x06)

Read the hardware version and configuration.

Enable Voltage (CMD 0x15)

Send CMD 0x15 with enable=0x01 to power on the ASIC voltage domains. Wait 500ms for voltage rails to stabilize before proceeding with ASIC enumeration.

Begin Sensor Monitoring

Start periodic temperature sensor reads using CMD 0x3C, targeting the LM75A or NCT218 sensors at their respective addresses.

Common Failure Modes

1. PIC Not Responding

Symptoms: No device found at any address in the 0x20-0x27 range during I2C scan. Hash board appears completely dead to the control board.

Causes:

  • Failed PIC16F1704 IC
  • Missing 3.3V power supply (check LDO regulator)
  • Broken I2C SDA or SCL trace from the connector to the PIC
  • PIC hung from a previous CMD 0x07 failure — try RST_N reset before assuming IC failure

Diagnosis:

  1. Verify 3.3V at the PIC's VDD pin
  2. Check SDA and SCL lines for stuck-LOW condition
  3. Toggle RST_N (if accessible) to attempt a reset
  4. If power and bus are good but PIC still does not respond, the IC has failed

2. Wrong I2C Address After Reset

Symptoms: PIC was at 0x20, after a reset it is now at 0x23. Software that hard-coded the previous address fails.

Causes: Normal behavior — the PIC address is not persistent across resets.

Fix: Always scan the full 0x20-0x27 range after any reset or power cycle. Never hard-code the PIC address.

3. Checksum Errors in Responses

Symptoms: Received data has incorrect checksums, or data appears garbled.

Causes:

  • Multi-byte I2C reads (see the byte-by-byte requirement above)
  • I2C bus noise from high-current switching on the board
  • Marginal pull-up resistor values on the I2C bus

Fix: Ensure byte-by-byte reads. Add proper decoupling capacitors near the PIC. Verify I2C pull-up resistors are present and within the correct range (typically 4.7K to 10K ohms).

4. Sensor Reads Return Stale Data

Symptoms: Temperature readings do not change, or all sensors return the same value.

Causes:

  • PIC is not completing the internal I2C transaction (internal bus issue)
  • Target sensor has failed (PIC returns cached or default data)
  • Insufficient wait time between CMD 0x3C and reading the response (need at least 50ms)

Fix: Increase the delay after CMD 0x3C to 100ms. Test sensors individually to identify which one is failing. Check the PIC's internal I2C bus (SDA/SCL between PIC and sensors) for continuity.

Replacement Notes

  • The PIC16F1704 in SOIC-14 or TSSOP-14 is straightforward to desolder and replace with hot air
  • The replacement PIC must be programmed with the correct firmware before installation — a blank PIC will not function as an I2C bridge
  • PIC firmware is typically locked (code protection enabled) — you cannot read the firmware from a working PIC to copy it to a replacement
  • Sources for programmed replacement PICs: donor boards (salvaged from other failed hash boards with different failures), aftermarket suppliers
  • After installation, verify the PIC responds on I2C (scan 0x20-0x27) and can execute CMD 0x16 (status query)

Found In These Miners