tw88xx_utils/i2c_interfaces.py
Patrick Moessler accbed7d3b initial
2022-01-09 18:20:49 +01:00

327 lines
10 KiB
Python

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)