initial
This commit is contained in:
commit
accbed7d3b
4 changed files with 496 additions and 0 deletions
327
i2c_interfaces.py
Normal file
327
i2c_interfaces.py
Normal file
|
@ -0,0 +1,327 @@
|
|||
from abc import abstractmethod
|
||||
from time import sleep
|
||||
|
||||
|
||||
class I2CInterface:
|
||||
@abstractmethod
|
||||
def read(self, addr: int, length: int) -> bytes:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def write(self, addr: int, data: bytes) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def write_read(self, addr: int, data: bytes, length: int) -> bytes:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def open(self) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def close(self) -> None:
|
||||
pass
|
||||
|
||||
class I2CException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class JLinkSwdI2CInterface(I2CInterface):
|
||||
GPIO_BASE = {
|
||||
'PA': 0x40010800,
|
||||
'PB': 0x40010C00,
|
||||
'PC': 0x40011000,
|
||||
'PD': 0x40011400,
|
||||
'PE': 0x40011800,
|
||||
}
|
||||
|
||||
GPIO_REGS = {
|
||||
'CRL': 0x00,
|
||||
'CRH': 0x04,
|
||||
'IDR': 0x08,
|
||||
'ODR': 0x0C,
|
||||
'BSRR': 0x10,
|
||||
'BRR': 0x14,
|
||||
'LCKR': 0x18
|
||||
}
|
||||
|
||||
BIT_TIME = (1/100000) / 2
|
||||
|
||||
def __init__(self, jlink_serial=None, chip_id='STM32F105RB', sda='PB7', scl='PB6') -> None:
|
||||
super().__init__()
|
||||
from pylink import JLink
|
||||
self.jlink = JLink()
|
||||
if not self.jlink.connected():
|
||||
self.jlink.open(serial_no=jlink_serial)
|
||||
self.chip_id = chip_id
|
||||
|
||||
self.sda = (JLinkSwdI2CInterface.GPIO_BASE[sda.upper()[0:2]], int(sda[2]))
|
||||
self.scl = (JLinkSwdI2CInterface.GPIO_BASE[scl.upper()[0:2]], int(scl[2]))
|
||||
|
||||
def open(self) -> None:
|
||||
from pylink.enums import JLinkInterfaces
|
||||
super().open()
|
||||
if not self.jlink.target_connected():
|
||||
self.jlink.set_tif(JLinkInterfaces.SWD)
|
||||
self.jlink.connect(self.chip_id)
|
||||
assert self.jlink.halt(), 'STM32 could not be halted'
|
||||
self._set_pin_od(self.scl)
|
||||
self._set_pin_od(self.sda)
|
||||
|
||||
def close(self) -> None:
|
||||
if self.jlink.target_connected():
|
||||
if not self.jlink.halted():
|
||||
self.jlink.halt()
|
||||
self.jlink.reset(halt=False)
|
||||
self.jlink.close()
|
||||
return super().close()
|
||||
|
||||
def read(self, addr: int, length: int) -> bytes:
|
||||
self._send_start()
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
ack = self._send_byte((addr << 1) | 1)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
if not ack:
|
||||
raise I2CInterface.I2CException('addr not acked')
|
||||
data = []
|
||||
for _ in range(length-1):
|
||||
data.append(self._recv_byte())
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
data.append(self._recv_byte(ack=False))
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
self._send_stop()
|
||||
return bytes(data)
|
||||
|
||||
def write(self, addr: int, data: bytes) -> None:
|
||||
self._send_start()
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
ack = self._send_byte(addr << 1)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
if not ack:
|
||||
raise I2CInterface.I2CException('addr not acked')
|
||||
for b in data:
|
||||
ack = self._send_byte(b)
|
||||
if not ack:
|
||||
raise I2CInterface.I2CException('data not acked')
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
self._send_stop()
|
||||
|
||||
def write_read(self, addr: int, data: bytes, length: int) -> bytes:
|
||||
self._send_start()
|
||||
ack = self._send_byte(addr << 1)
|
||||
if not ack:
|
||||
raise I2CInterface.I2CException('W addr not acked')
|
||||
for b in data:
|
||||
ack = self._send_byte(b)
|
||||
if not ack:
|
||||
raise I2CInterface.I2CException('data not acked')
|
||||
self._send_start()
|
||||
ack = self._send_byte((addr << 1) | 1)
|
||||
if not ack:
|
||||
raise I2CInterface.I2CException('R addr not acked')
|
||||
data = []
|
||||
for _ in range(length):
|
||||
data.append(self._recv_byte())
|
||||
self._send_stop()
|
||||
|
||||
@classmethod
|
||||
def _bit_sleep(cls):
|
||||
# pass
|
||||
sleep(cls.BIT_TIME)
|
||||
|
||||
def _rmw(self, addr, offset, size, val) -> None:
|
||||
mask = ((2**size)-1) << offset
|
||||
nmask = 0xFFFFFFFF ^ mask
|
||||
reg_val = self.jlink.memory_read32(addr, 1)[0]
|
||||
reg_val &= nmask
|
||||
reg_val |= (val << offset) & mask
|
||||
self.jlink.memory_write32(addr, [reg_val])
|
||||
|
||||
def _set_pin_od(self, pin) -> None:
|
||||
cr = JLinkSwdI2CInterface.GPIO_REGS['CRH'] if pin[1] >= 8 else JLinkSwdI2CInterface.GPIO_REGS['CRL']
|
||||
pin_off = 4 * (pin[1]-8 if pin[1] >= 8 else pin[1])
|
||||
self._rmw(pin[0] + cr, pin_off, 4, 0b1000)
|
||||
self._set_pin(pin, True)
|
||||
self._rmw(pin[0] + cr, pin_off, 4, 0b0110)
|
||||
|
||||
def _set_pin(self, pin, state: bool):
|
||||
reg = JLinkSwdI2CInterface.GPIO_REGS['BSRR'] if state else JLinkSwdI2CInterface.GPIO_REGS['BRR']
|
||||
mask = 1 << pin[1]
|
||||
self.jlink.memory_write16(pin[0] + reg, [mask])
|
||||
|
||||
def _get_pin(self, pin) -> bool:
|
||||
mask = 1 << pin[1]
|
||||
reg_val = self.jlink.memory_read16(pin[0] + JLinkSwdI2CInterface.GPIO_REGS['IDR'], 1)[0]
|
||||
return bool(reg_val & mask)
|
||||
|
||||
def _send_start(self) -> None:
|
||||
self._set_pin(self.sda, True)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
self._set_pin(self.scl, True)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
self._set_pin(self.sda, False)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
self._set_pin(self.scl, False)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
|
||||
def _send_byte(self, val) -> bool:
|
||||
self._set_pin(self.scl, False)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
for i in range(8):
|
||||
bit = bool(val & (0x80 >> i))
|
||||
self._set_pin(self.sda, bit)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
self._set_pin(self.scl, True)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
self._set_pin(self.scl, False)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
self._set_pin(self.sda, True)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
self._set_pin(self.scl, True)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
ack = not self._get_pin(self.sda)
|
||||
self._set_pin(self.scl, False)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
return ack
|
||||
|
||||
def _recv_byte(self, ack=True) -> int:
|
||||
val = 0
|
||||
self._set_pin(self.scl, False)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
self._set_pin(self.sda, True)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
for i in range(8):
|
||||
self._set_pin(self.scl, True)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
bit = self._get_pin(self.sda)
|
||||
if bit:
|
||||
val |= (0x80 >> i)
|
||||
self._set_pin(self.scl, False)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
self._set_pin(self.sda, not ack)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
self._set_pin(self.scl, True)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
self._set_pin(self.scl, False)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
return val
|
||||
|
||||
def _send_stop(self):
|
||||
self._set_pin(self.scl, False)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
self._set_pin(self.sda, False)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
self._set_pin(self.scl, True)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
self._set_pin(self.sda, True)
|
||||
JLinkSwdI2CInterface._bit_sleep()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# class JLinkSwdI2CInterface_I2C(I2CInterface):
|
||||
# def __init__(self, jlink_serial=None) -> None:
|
||||
# super().__init__()
|
||||
# from pylink import JLink
|
||||
# self.jlink = JLink()
|
||||
# if not self.jlink.connected():
|
||||
# self.jlink.open(serial_no=jlink_serial)
|
||||
# self.chip_id = 'STM32F105RB'
|
||||
|
||||
# def open(self) -> None:
|
||||
# from pylink.enums import JLinkInterfaces
|
||||
# super().open()
|
||||
# if not self.jlink.target_connected():
|
||||
# self.jlink.set_tif(JLinkInterfaces.SWD)
|
||||
# self.jlink.connect(self.chip_id)
|
||||
# assert self.jlink.halt(), 'STM32 could not be halted'
|
||||
|
||||
# self._rmw(0x40010C00,24,8,0b10001000)
|
||||
# self.
|
||||
# self._rmw(0x40010C00,24,8,0b11101110)
|
||||
|
||||
|
||||
# def close(self) -> None:
|
||||
# if self.jlink.target_connected():
|
||||
# if not self.jlink.halted():
|
||||
# self.jlink.halt()
|
||||
# self.jlink.reset(halt=False)
|
||||
# self.jlink.close()
|
||||
# return super().close()
|
||||
|
||||
# def read(self, addr: int, length: int) -> bytes:
|
||||
# self._send_start()
|
||||
# JLinkSwdI2CInterface._bit_sleep()
|
||||
# ack = self._send_byte((addr << 1) | 1)
|
||||
# JLinkSwdI2CInterface._bit_sleep()
|
||||
# if not ack:
|
||||
# raise I2CInterface.I2CException('addr not acked')
|
||||
# data = []
|
||||
# for _ in range(length-1):
|
||||
# data.append(self._recv_byte())
|
||||
# JLinkSwdI2CInterface._bit_sleep()
|
||||
# data.append(self._recv_byte(ack=False))
|
||||
# JLinkSwdI2CInterface._bit_sleep()
|
||||
# self._send_stop()
|
||||
# return bytes(data)
|
||||
|
||||
# def write(self, addr: int, data: bytes) -> None:
|
||||
# self._send_start()
|
||||
# JLinkSwdI2CInterface._bit_sleep()
|
||||
# ack = self._send_byte(addr << 1)
|
||||
# JLinkSwdI2CInterface._bit_sleep()
|
||||
# if not ack:
|
||||
# raise I2CInterface.I2CException('addr not acked')
|
||||
# for b in data:
|
||||
# ack = self._send_byte(b)
|
||||
# if not ack:
|
||||
# raise I2CInterface.I2CException('data not acked')
|
||||
# JLinkSwdI2CInterface._bit_sleep()
|
||||
# self._send_stop()
|
||||
|
||||
# def write_read(self, addr: int, data: bytes, length: int) -> bytes:
|
||||
# self._send_start()
|
||||
# ack = self._send_byte(addr << 1)
|
||||
# if not ack:
|
||||
# raise I2CInterface.I2CException('W addr not acked')
|
||||
# for b in data:
|
||||
# ack = self._send_byte(b)
|
||||
# if not ack:
|
||||
# raise I2CInterface.I2CException('data not acked')
|
||||
# self._send_start()
|
||||
# ack = self._send_byte((addr << 1) | 1)
|
||||
# if not ack:
|
||||
# raise I2CInterface.I2CException('R addr not acked')
|
||||
# data = []
|
||||
# for _ in range(length):
|
||||
# data.append(self._recv_byte())
|
||||
# self._send_stop()
|
||||
|
||||
# def _rmw(self, addr, offset, size, val) -> None:
|
||||
# mask = ((2**size)-1) << offset
|
||||
# nmask = 0xFFFFFFFF ^ mask
|
||||
# reg_val = self.jlink.memory_read32(addr, 1)[0]
|
||||
# reg_val &= nmask
|
||||
# reg_val |= (val << offset) & mask
|
||||
# self.jlink.memory_write32(addr, [reg_val])
|
||||
|
||||
# def _set_pin_af_od(self, pin) -> None:
|
||||
# cr = 4 if pin[1] >= 8 else 0
|
||||
# pin_off = 4 * (pin[1]-8 if pin[1] >= 8 else pin[1])
|
||||
# self._rmw(pin[0] + cr, pin_off, 4, 0b1000)
|
||||
# self._set_pin(pin, True)
|
||||
# self._rmw(pin[0] + cr, pin_off, 4, 0b1110)
|
||||
|
9
tw8836.py
Normal file
9
tw8836.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from i2c_interfaces import I2CInterface
|
||||
from tw88xx import TW88xx
|
||||
|
||||
|
||||
class TW8836(TW88xx):
|
||||
ADDR = 0x45
|
||||
|
||||
def __init__(self, i2c: I2CInterface) -> None:
|
||||
super().__init__(i2c)
|
100
tw88xx.py
Normal file
100
tw88xx.py
Normal file
|
@ -0,0 +1,100 @@
|
|||
from i2c_interfaces import I2CInterface
|
||||
|
||||
|
||||
class TW88xx:
|
||||
|
||||
class Reg:
|
||||
SPI_FLASH_MODE_CONTROL_0 = 0X4C0
|
||||
SPI_FLASH_MODE_CONTROL_1 = 0X4C1
|
||||
DMA_CONTROL = 0X4C3
|
||||
FLASH_BUSY_CONTROL = 0X4C4
|
||||
WAIT_CONTROL = 0X4C5
|
||||
DMA_PAGE = 0X4C6
|
||||
DMA_INDEX = 0X4C7
|
||||
DMA_LENGTH_MID_BYTE = 0X4C8
|
||||
DMA_LENGTH_LOW_BYTE = 0X4C9
|
||||
DMA_COMMAND_BUFFER = 0X4CA # LEN 5
|
||||
CLOCK_SWITCH_WAIT_COUNTER_VALUE = 0X4CF
|
||||
DMA_READWRITE_BUFFER = 0X4D0 # LEN 8
|
||||
SPI_FLASH_STATUS_COMMAND = 0X4D8
|
||||
SPI_FLASH_BUSY_CONTROL = 0X4D9
|
||||
DMA_LENGTH_HIGH_BYTE = 0X4DA
|
||||
|
||||
def __init__(self, i2c: I2CInterface) -> None:
|
||||
self.i2c = i2c
|
||||
self.page = None
|
||||
|
||||
def _set_page(self, page: int) -> None:
|
||||
self.i2c.write(self.ADDR, [0xFF, page])
|
||||
|
||||
def write_reg(self, reg: int, data: int) -> None:
|
||||
page = reg >> 8
|
||||
if not self.page or self.page != page:
|
||||
self._set_page(page)
|
||||
self.page = page
|
||||
self.i2c.write(self.ADDR, [reg & 0xFF, data])
|
||||
# print(f'{reg:03x} = {data:02x}')
|
||||
|
||||
def read_reg(self, reg: int):
|
||||
page = reg >> 8
|
||||
if not self.page or self.page != page:
|
||||
self._set_page(page)
|
||||
self.page = page
|
||||
self.i2c.write(self.ADDR, [reg & 0xFF])
|
||||
return self.i2c.read(self.ADDR, 1)[0]
|
||||
|
||||
def execute_flash_cmd(self, cmd: bytes, read_len=0, read_mode='slow', data=None) -> bytes:
|
||||
# def execute_flash_cmd(self, cmd: bytes, read_len=0, read_mode='slow') -> bytes:
|
||||
self.write_reg(TW88xx.Reg.SPI_FLASH_MODE_CONTROL_0, 0x00)
|
||||
self.write_reg(TW88xx.Reg.DMA_CONTROL, 0x40 + len(cmd))
|
||||
|
||||
self.write_reg(TW88xx.Reg.DMA_PAGE, 0x04)
|
||||
self.write_reg(TW88xx.Reg.DMA_INDEX, 0xD0)
|
||||
|
||||
self.write_reg(TW88xx.Reg.DMA_LENGTH_MID_BYTE, 0)
|
||||
self.write_reg(TW88xx.Reg.DMA_LENGTH_LOW_BYTE, read_len)
|
||||
self.write_reg(TW88xx.Reg.DMA_LENGTH_HIGH_BYTE, 0)
|
||||
|
||||
for i in range(len(cmd)):
|
||||
self.write_reg(TW88xx.Reg.DMA_COMMAND_BUFFER + i, cmd[i])
|
||||
|
||||
if data:
|
||||
for i in range(len(data)):
|
||||
self.write_reg(TW88xx.Reg.DMA_READWRITE_BUFFER + i, data[i])
|
||||
|
||||
self.write_reg(TW88xx.Reg.FLASH_BUSY_CONTROL, 0x01)
|
||||
while True:
|
||||
busy = self.read_reg(TW88xx.Reg.FLASH_BUSY_CONTROL)
|
||||
# print(f'busy: {busy:02x}')
|
||||
if (busy & 0x01) == 0:
|
||||
break
|
||||
|
||||
data = []
|
||||
for i in range(read_len):
|
||||
data.append(self.read_reg(TW88xx.Reg.DMA_READWRITE_BUFFER + i))
|
||||
return bytes(data)
|
||||
|
||||
def prepare_flash_read(self, cmd, read_len):
|
||||
self.write_reg(TW88xx.Reg.DMA_CONTROL, 0x40 + len(cmd))
|
||||
|
||||
self.write_reg(TW88xx.Reg.DMA_PAGE, 0x04)
|
||||
self.write_reg(TW88xx.Reg.DMA_INDEX, 0xD0)
|
||||
|
||||
self.write_reg(TW88xx.Reg.DMA_LENGTH_MID_BYTE, 0)
|
||||
self.write_reg(TW88xx.Reg.DMA_LENGTH_LOW_BYTE, read_len)
|
||||
self.write_reg(TW88xx.Reg.DMA_LENGTH_HIGH_BYTE, 0)
|
||||
|
||||
for i in range(len(cmd)):
|
||||
self.write_reg(TW88xx.Reg.DMA_COMMAND_BUFFER + i, cmd[i])
|
||||
|
||||
def do_flash_cmd(self, read_len):
|
||||
self.write_reg(TW88xx.Reg.FLASH_BUSY_CONTROL, 0x01)
|
||||
while True:
|
||||
busy = self.read_reg(TW88xx.Reg.FLASH_BUSY_CONTROL)
|
||||
if (busy & 0x01) == 0:
|
||||
break
|
||||
|
||||
data = []
|
||||
for i in range(read_len):
|
||||
data.append(self.read_reg(TW88xx.Reg.DMA_READWRITE_BUFFER + i))
|
||||
return bytes(data)
|
60
tw88xx_util.py
Normal file
60
tw88xx_util.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
import argparse
|
||||
|
||||
from tw88xx import TW88xx
|
||||
parser = argparse.ArgumentParser(description='TW88xx debug utility.')
|
||||
|
||||
parser.add_argument('command',
|
||||
help='the command to execute',
|
||||
choices=('dump_flash', 'get_id')
|
||||
)
|
||||
parser.add_argument('-f', '--flash_file',
|
||||
help='file to read from / write into'
|
||||
)
|
||||
parser.add_argument('-i', '--interface',
|
||||
help='the i2c interface to use',
|
||||
choices=('devi2c', 'swd')
|
||||
)
|
||||
parser.add_argument('-j', '--jlink',
|
||||
help='J-Link (serial) to use for SWD',
|
||||
default=None
|
||||
)
|
||||
parser.add_argument('-t', '--type',
|
||||
help='TX88xx type',
|
||||
choices=('tw8836'),
|
||||
default='tw8836'
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.interface == 'swd':
|
||||
from i2c_interfaces import JLinkSwdI2CInterface
|
||||
i2c = JLinkSwdI2CInterface(jlink_serial=args.jlink)
|
||||
|
||||
i2c.open()
|
||||
if args.type == 'tw8836':
|
||||
from tw8836 import TW8836
|
||||
tw88xx = TW8836(i2c)
|
||||
|
||||
if args.command == 'get_id':
|
||||
id = tw88xx.read_reg(0x000)
|
||||
print(f'{id:02x}')
|
||||
|
||||
flash_id = tw88xx.execute_flash_cmd(cmd=[0x9f], read_len=3)
|
||||
print(flash_id.hex())
|
||||
|
||||
elif args.command == 'dump_flash':
|
||||
# tw88xx.prepare_flash_read(cmd=[0x03, 0x1a, 0x9d, 0x84], read_len=8)
|
||||
tw88xx.execute_flash_cmd(cmd=[0xE9], read_len=0)
|
||||
data=[]
|
||||
for block_start in range(0x1a9d84,0x1a9d84+480*480,8):
|
||||
block = tw88xx.execute_flash_cmd(cmd=[0x03, (block_start>>16) & 0xFF, (block_start>>8) & 0xFF, block_start& 0xFF], read_len=8, data=[0x5a]*8)
|
||||
data.extend(block)
|
||||
print(f'{block_start:06x}: {block.hex()}')
|
||||
|
||||
print(bytes(data).hex())
|
||||
# tw88xx.execute_flash_cmd(cmd=[0xB7], read_len=0)
|
||||
# block = tw88xx.execute_flash_cmd(cmd=[0x03, 0x00, 0x00, 0x00], read_len=8)
|
||||
# print(block.hex())
|
||||
# block = tw88xx.execute_flash_cmd(cmd=[0x03, 0x00, 0x00, 0x08], read_len=8)
|
||||
# print(block.hex())
|
||||
# tw88xx.execute_flash_cmd(cmd=[0xE9], read_len=0)
|
Loading…
Reference in a new issue