"""
SC18IM704 UART to I2C Bridge Controller
- Controls AD5252 digital potentiometer via I2C
- Implements full logging for all serial transactions
"""
import serial
import time
from datetime import datetime
# ========== Configuration ==========
UART_PORT = 'COM8' # Serial port for SC18IM704
BAUDRATE = 9600 # UART baud rate (default for SC18IM704 is 9600)
I2C_ADDR_AD5252 = 0x2C # AD5252 default I2C address (7-bit)
# SC18IM704 command set (UART protocol) - UPDATED
CMD_STOP = 0x50 # STOP
CMD_I2C_START = 0x53 # I2C START
CMD_READ_REG = 0x52 # Read internal register
CMD_WRITE_REG = 0x57 # Write internal register
CMD_READ_GPIO = 0x49 # Read GPIO
CMD_WRITE_GPIO = 0x4F # Write GPIO
CMD_POWER_DOWN = 0x5A # Power down
# SC18IM704 register addresses
REG_BRG0 = 0x00
REG_BRG1 = 0x01
REG_GPIO_CONF1 = 0x02
REG_GPIO_CONF2 = 0x03
REG_GPIO_STATE = 0x04
REG_I2C_ADDR = 0x06
REG_I2C_CLK_L = 0x07
REG_I2C_CLK_H = 0x08
REG_I2C_TIMEOUT = 0x09
REG_I2C_STAT = 0x0A
# I2C status codes
I2C_STAT_OK = 0xF0
I2C_STAT_NACK_ADDR = 0xF1
I2C_STAT_NACK_DATA = 0xF2
I2C_STAT_TIMEOUT = 0xF8
# ========== Helper Functions ==========
def log_transaction(direction, data):
"""Log serial transactions with timestamp"""
timestamp = datetime.now().strftime("%H:%M:%S.%f")[:-3]
hex_data = ' '.join(f"{b:02X}" for b in data)
print(f"[{timestamp}] {direction}: {hex_data}")
def write_internal_reg(ser, reg_addr, value):
"""
Write to SC18IM704 internal register.
Protocol: [CMD_WRITE_REG, reg_addr, value, CMD_STOP]
"""
cmd = bytes([CMD_WRITE_REG, reg_addr, value, CMD_STOP])
log_transaction("TX", cmd)
ser.write(cmd)
time.sleep(0.01)
def read_internal_reg(ser, reg_addr):
"""
Read from SC18IM704 internal register.
Protocol: [CMD_READ_REG, reg_addr, CMD_STOP]
Returns: value (int) or None
"""
cmd = bytes([CMD_READ_REG, reg_addr, CMD_STOP])
log_transaction("TX", cmd)
ser.write(cmd)
value = ser.read(1)
if value:
log_transaction("RX", value)
return value[0]
else:
print(f"Failed to read register 0x{reg_addr:02X}")
return None
def init_sc18im704(ser, i2c_freq=100_000, osc_freq=15_000_000):
"""
Initialize SC18IM704 bridge
- Sets I2C speed to desired frequency (Hz)
- osc_freq: oscillator frequency in Hz (default 15MHz)
- i2c_freq: desired I2C frequency in Hz (default 100kHz)
"""
clk_div = int(osc_freq / (5 * i2c_freq))
if clk_div > 0xFFFF:
clk_div = 0xFFFF # Clamp to 16-bit max
clk_div_l = clk_div & 0xFF
clk_div_h = (clk_div >> 8) & 0xFF
write_internal_reg(ser, REG_I2C_CLK_L, clk_div_l)
write_internal_reg(ser, REG_I2C_CLK_H, clk_div_h)
# No response expected for register write
def write_i2c(ser, addr, data):
"""
I2C write operation using SC18IM704 protocol:
[CMD_I2C_START, addr_w, num_bytes, data..., CMD_STOP]
"""
addr_w = (addr << 1) & 0xFE # W=0
num_bytes = len(data)
cmd = bytes([CMD_I2C_START, addr_w, num_bytes] + list(data) + [CMD_STOP])
log_transaction("TX", cmd)
ser.write(cmd)
time.sleep(0.05)
# Read status
status_str = read_i2c_status(ser)
if status_str != "I2C OK":
print(f"I2C Write Error: {status_str}")
def read_i2c(ser, addr, length):
"""
I2C read operation using SC18IM704 protocol:
[CMD_I2C_START, addr_r, num_bytes, CMD_STOP]
"""
addr_r = (addr << 1) | 0x01 # R=1
cmd = bytes([CMD_I2C_START, addr_r, length, CMD_STOP])
log_transaction("TX", cmd)
ser.write(cmd)
time.sleep(0.05)
response = ser.read(length)
status_str = read_i2c_status(ser)
if status_str != "I2C OK":
print(f"I2C Read Error: {status_str}")
return None
if response:
log_transaction("RX", response)
return response
else:
print("I2C Read Error: No data received")
return None
def read_after_write_i2c(ser, addr, write_data, read_length):
"""
I2C repeated start (read after write) using SC18IM704 protocol:
[CMD_I2C_START, addr_w, num_bytes, data..., CMD_I2C_START, addr_r, read_length, CMD_STOP]
"""
addr_w = (addr << 1) & 0xFE
addr_r = (addr << 1) | 0x01
cmd = bytes(
[CMD_I2C_START, addr_w, len(write_data)] +
list(write_data) +
[CMD_I2C_START, addr_r, read_length, CMD_STOP]
)
log_transaction("TX", cmd)
ser.write(cmd)
time.sleep(0.05)
response = ser.read(read_length)
status_str = read_i2c_status(ser)
if status_str != "I2C OK":
print(f"I2C Read-after-Write Error: {status_str}")
return None
if response:
log_transaction("RX", response)
return response
else:
print("I2C Read-after-Write Error: No data received")
return None
def read_sc18im704_version(ser):
"""
Reads the SC18IM704 firmware version string.
Sends ASCII 'V' (0x56) and 'P' (0x50), expects a 16-byte null-terminated string.
"""
cmd = bytes([0x56, 0x50]) # 'V', 'P'
log_transaction("TX", cmd)
ser.write(cmd)
version = ser.read(16)
if version:
log_transaction("RX", version)
# Remove null terminator and decode
version_str = version.rstrip(b'\x00').decode(errors='replace')
print(f"SC18IM704 Version: {version_str}")
return version_str
else:
print("Failed to read SC18IM704 version.")
return None
def read_i2c_status(ser):
"""
Reads the SC18IM704 I2C status register and returns a human-readable status.
"""
code = read_internal_reg(ser, REG_I2C_STAT)
if code is None:
print("Failed to read I2C status register.")
return None
if code == I2C_STAT_OK:
return "I2C OK"
elif code == I2C_STAT_NACK_ADDR:
return "I2C NACK on address"
elif code == I2C_STAT_NACK_DATA:
return "I2C NACK on data"
elif code == I2C_STAT_TIMEOUT:
return "I2C TIMEOUT"
else:
return f"Unknown status: 0x{code:02X}"
# ========== AD5252 Operations ==========
def write_rdac3(ser, value):
"""
Write to AD5252's RDAC3 register (address 0x03)
- Performs I2C write: [0x03, value]
"""
if not 0 <= value <= 255:
raise ValueError("RDAC value must be 0-255")
print(f"\n[AD5252] Writing RDAC3: 0x{value:02X}")
write_i2c(ser, I2C_ADDR_AD5252, [0x03, value])
def read_rdac3(ser):
"""
Read from AD5252's RDAC3 register using repeated start
"""
print("\n[AD5252] Reading RDAC3...")
data = read_after_write_i2c(ser, I2C_ADDR_AD5252, [0x03], 1)
return data[0] if data else None
def read_whoami(ser):
raise NotImplementedError
# ========== Main Program ==========
def main():
print("===== SC18IM704 I2C Bridge Controller =====")
print(f"Config: Port={UART_PORT}, Baud={BAUDRATE}")
print(f"Target: AD5252 @ 0x{I2C_ADDR_AD5252:02X}\n")
try:
with serial.Serial(UART_PORT, BAUDRATE, timeout=1) as ser:
# Read SC18IM704 firmware version
read_sc18im704_version(ser)
print("\n")
# Initialize bridge
print("[SC18IM704] Initializing...")
init_sc18im704(ser)
# 2. Write test value to RDAC3
test_value = 0x80
write_rdac3(ser, test_value)
# 3. Verify write operation
rdac_val = read_rdac3(ser)
if rdac_val is not None:
print(f"RDAC3 Value: 0x{rdac_val:02X}")
print("Verification:", "PASS" if rdac_val == test_value else "FAIL")
else:
print("Error reading RDAC3!")
# 4. Write test value to RDAC3
test_value = 0xde
write_rdac3(ser, test_value)
# 5. Verify write operation
rdac_val = read_rdac3(ser)
if rdac_val is not None:
print(f"RDAC3 Value: 0x{rdac_val:02X}")
print("Verification:", "PASS" if rdac_val == test_value else "FAIL")
else:
print("Error reading RDAC3!")
except serial.SerialException as e:
print(f"\nSerial port error: {e}")
except Exception as e:
print(f"\nUnexpected error: {e}")
if __name__ == "__main__":
main()