Skip to content

Commit

Permalink
Landungsbruecke: A bit of rework on the ID EEPROM handling
Browse files Browse the repository at this point in the history
Mostly refactoring but also
* `Landungsbruecke.eeprom_mc` changed to `id_eeprom_mc`
* `Landungsbruecke.eeprom_drv` changed to `id_eeprom_drv`
  • Loading branch information
trinamic-bp committed Jan 23, 2025
1 parent 18cdcd0 commit 00d8891
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 125 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
lb = Landungsbruecke(interface)

print("ID EEPROM content:")
print("Mc: ", lb.eeprom_drv.read_id_info())
print("Drv:", lb.eeprom_mc.read_id_info())
print("Mc: ", lb.id_eeprom_mc.read_id_info())
print("Drv:", lb.id_eeprom_drv.read_id_info())

print("Board IDs:")
print(lb.get_board_ids())
Expand Down
21 changes: 21 additions & 0 deletions examples/modules/Landungsbruecke/write_mc_id_eeprom.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
################################################################################
# Copyright © 2024 Analog Devices Inc. All Rights Reserved.
# This software is proprietary to Analog Devices, Inc. and its licensors.
################################################################################
"""Please do not use unless you know what you are doing!"""

from pytrinamic.connections import ConnectionManager
from pytrinamic.modules import Landungsbruecke


cm = ConnectionManager()

with cm.connect() as interface:
lb = Landungsbruecke(interface)

lb.id_eeprom_mc.write_id_info(
description="TMC4671",
board_id=13, # Check out Landungsbruecke.mc_id_names for a list of IDs
hw_major_version=1, # Board has printed version 1.2 on it
hw_minor_version=2, # Board has printed version 1.2 on it
)
109 changes: 0 additions & 109 deletions pytrinamic/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,112 +27,3 @@ def to_signed_16(x):
"""Convert any unsigned integer to a 16 bit signed integer."""
m = x & 0x0000ffff
return (m ^ 0x00008000) - 0x00008000


class EEPROM:
"""
This class provides basic access to an EEPROM.
All accesses are in little-endian byte order. No alignment of addresses is
required.
This class is designed for usage with the Evalsystem ID eeproms but can be
used with any other EEPROM implementation providing the proper access
functions.
Possible extensions:
- Add big-endian support
- Add minimum alignment requirement support
"""

# Addresses for Evalsystem ID EEPROM
ADDR_DESCRIPTION = 0
ADDR_ID = 16
ADDR_HW_VERSION_MAJOR = 18
ADDR_HW_VERSION_MINOR = 19
ADDR_MAGIC_NUMBER = 20
# Magic number (little-endian) for the Evalsystem ID EEPROM
MAGIC_NUMBER = 0x3412

"""
For initialization two functions need to be provided.
A 32 bit read function (little endian) and an 8 bit write function.
"""
def __init__(self, read32func, write8func):
if not callable(read32func):
raise ValueError("EEPROM class requires a callable read function")

if not callable(write8func):
raise ValueError("EEPROM class requires a callable write function")

self._read32func = read32func
self._write8func = write8func

def read_byte(self, address):
return self._read32func(address) & 0xFF

def read_short(self, address):
return self._read32func(address) & 0xFFFF

def read_int(self, address):
return self._read32func(address)

def read_ascii(self, address, length):
text = ""
for i in range(address, address+length, 4):
data = self.read_int(i)
text += chr((data >> 0) & 0xFF)
text += chr((data >> 8) & 0xFF)
text += chr((data >> 16) & 0xFF)
text += chr((data >> 24) & 0xFF)

# In case we read more than needed, cut away excess characters
text = text[0:length]

return text

def read_id_info(self):
# Check magic number
if self.read_short(self.ADDR_MAGIC_NUMBER) != self.MAGIC_NUMBER:
return None

desc = self.read_ascii(self.ADDR_DESCRIPTION, 16)
board_id = self.read_short(self.ADDR_ID)
hw_major = self.read_byte(self.ADDR_HW_VERSION_MAJOR)
hw_minor = self.read_byte(self.ADDR_HW_VERSION_MINOR)

return {"description": desc.strip('\x00'), "id": board_id, "hw_major": hw_major, "hw_minor": hw_minor}

def write_byte(self, address, value):
self._write8func(address, value)

def write_short(self, address, value):
for i in range(2):
self._write8func(address + i, value >> (i*8))

def write_int(self, address, value):
for i in range(4):
self._write8func(address + i, value >> (i*8))

def write_ascii(self, address, text):
for i, c in enumerate(text):
self._write8func(address + i, ord(c))

def write_id_info(self, description, board_id, hw_major_version, hw_minor_version):
if type(description) != str:
raise TypeError("Description must be a string")

if len(description) > 16:
raise ValueError("Description cannot be longer than 16 characters")

if not(type(board_id) == type(hw_major_version) == type(hw_minor_version) == int):
raise TypeError("Board ID and Hardware versions must be integers")

# Pad the string with zeros if necessary
description += "\x00" * (16-len(description))

self.write_ascii(self.ADDR_DESCRIPTION, description)
self.write_short(self.ADDR_ID, board_id)
self.write_byte(self.ADDR_HW_VERSION_MAJOR, hw_major_version)
self.write_byte(self.ADDR_HW_VERSION_MINOR, hw_minor_version)
self.write_short(self.ADDR_MAGIC_NUMBER, self.MAGIC_NUMBER)
136 changes: 122 additions & 14 deletions pytrinamic/modules/Landungsbruecke.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,126 @@
################################################################################

from pytrinamic.tmcl import TMCLCommand
from pytrinamic.helpers import EEPROM


class IDEEPROM:
"""
This class represents an ID-EEPROM of an EVAL board that is connected to a Landungsbruecke.
All accesses are in little-endian byte order. No alignment of addresses is
required.
This class is designed for usage with the Evalsystem ID EEPROM but can be
used with any other EEPROM implementation providing the proper access
functions.
Possible extensions:
- Add big-endian support
- Add minimum alignment requirement support
"""

# Addresses for Evalsystem ID EEPROM
ADDR_DESCRIPTION = 0
ADDR_ID = 16
ADDR_HW_VERSION_MAJOR = 18
ADDR_HW_VERSION_MINOR = 19
ADDR_MAGIC_NUMBER = 20
# Magic number (little-endian) for the Evalsystem ID EEPROM
MAGIC_NUMBER = 0x3412

"""
For initialization two functions need to be provided.
A 32 bit read function (little endian) and an 8 bit write function.
"""
def __init__(self, read32func, write8func):
if not callable(read32func):
raise ValueError("IDEEPROM class requires a callable read function")

if not callable(write8func):
raise ValueError("IDEEPROM class requires a callable write function")

self._read32func = read32func
self._write8func = write8func

def read_byte(self, address):
return self._read32func(address) & 0xFF

def read_short(self, address):
return self._read32func(address) & 0xFFFF

def read_int(self, address):
return self._read32func(address)

def read_ascii(self, address, length):
text = ""
for i in range(address, address+length, 4):
data = self.read_int(i)
text += chr((data >> 0) & 0xFF)
text += chr((data >> 8) & 0xFF)
text += chr((data >> 16) & 0xFF)
text += chr((data >> 24) & 0xFF)

# In case we read more than needed, cut away excess characters
text = text[0:length]

return text

def read_id_info(self):
# Check magic number
if self.read_short(self.ADDR_MAGIC_NUMBER) != self.MAGIC_NUMBER:
return None

desc = self.read_ascii(self.ADDR_DESCRIPTION, 16)
board_id = self.read_short(self.ADDR_ID)
hw_major = self.read_byte(self.ADDR_HW_VERSION_MAJOR)
hw_minor = self.read_byte(self.ADDR_HW_VERSION_MINOR)

return {"description": desc.strip('\x00'), "id": board_id, "hw_major": hw_major, "hw_minor": hw_minor}

def write_byte(self, address, value):
self._write8func(address, value)

def write_short(self, address, value):
for i in range(2):
self._write8func(address + i, value >> (i*8))

def write_int(self, address, value):
for i in range(4):
self._write8func(address + i, value >> (i*8))

def write_ascii(self, address, text):
for i, c in enumerate(text):
self._write8func(address + i, ord(c))

def write_id_info(self, description, board_id, hw_major_version, hw_minor_version):
if type(description) != str:
raise TypeError("Description must be a string")

if len(description) > 16:
raise ValueError("Description cannot be longer than 16 characters")

if not(type(board_id) == type(hw_major_version) == type(hw_minor_version) == int):
raise TypeError("Board ID and Hardware versions must be integers")

# Pad the string with zeros if necessary
description += "\x00" * (16-len(description))

self.write_ascii(self.ADDR_DESCRIPTION, description)
self.write_short(self.ADDR_ID, board_id)
self.write_byte(self.ADDR_HW_VERSION_MAJOR, hw_major_version)
self.write_byte(self.ADDR_HW_VERSION_MINOR, hw_minor_version)
self.write_short(self.ADDR_MAGIC_NUMBER, self.MAGIC_NUMBER)


class Landungsbruecke:
def __init__(self, connection):
self.__connection = connection
self._connection = connection

self._mcId = 0
self._drvId = 0

self.eeprom_mc = EEPROM(self._read_mc_eeprom, self._write_mc_eeprom)
self.eeprom_drv = EEPROM(self._read_drv_eeprom, self._write_drv_eeprom)
self.id_eeprom_mc = IDEEPROM(self._read_mc_eeprom, self._write_mc_eeprom)
self.id_eeprom_drv = IDEEPROM(self._read_drv_eeprom, self._write_drv_eeprom)

def get_board_ids(self):
"""
Expand All @@ -27,7 +135,7 @@ def get_board_ids(self):
This does not start a detection.
Returns a tuple of IDs: (drvId, mcId)
"""
value = self.__connection.get_global_parameter(self.GP.BoardAssignment, 0)
value = self._connection.get_global_parameter(self.GP.BoardAssignment, 0)

drvStatus = (value >> 24) & 0xFF
drvId = (value >> 16) & 0xFF
Expand All @@ -45,7 +153,7 @@ def detect_board_ids(self):
"""
Start an IDDetection and read out the IDs of the detected boards.
"""
while not self.__connection.send(TMCLCommand.ASSIGNMENT, 0, 0, 0).is_valid():
while not self._connection.send(TMCLCommand.ASSIGNMENT, 0, 0, 0).is_valid():
pass

return self.get_board_ids()
Expand All @@ -54,40 +162,40 @@ def get_board_names(self):
board_ids = self.get_board_ids()

try:
mc_name = self.mcIdNames[board_ids[0]]
mc_name = self.mc_id_names[board_ids[0]]
except KeyError:
mc_name = str(board_ids[0])

try:
drv_name = self.drvIdNames[board_ids[1]]
drv_name = self.drv_id_names[board_ids[1]]
except KeyError:
drv_name = str(board_ids[1])

return mc_name, drv_name

def _read_mc_eeprom(self, address):
reply = self.__connection.send(TMCLCommand.TMCL_UF1, 1, 0, address)
reply = self._connection.send(TMCLCommand.TMCL_UF1, 1, 0, address)

if not reply.is_valid():
raise RuntimeError("Failed to read driver ID EEPROM")

return reply.value

def _write_mc_eeprom(self, address, value):
self.__connection.send(TMCLCommand.TMCL_UF2, 1, value, address)
self._connection.send(TMCLCommand.TMCL_UF2, 1, value, address)

def _read_drv_eeprom(self, address):
reply = self.__connection.send(TMCLCommand.TMCL_UF1, 2, 0, address)
reply = self._connection.send(TMCLCommand.TMCL_UF1, 2, 0, address)

if not reply.is_valid():
raise RuntimeError("Failed to read driver ID EEPROM")

return reply.value

def _write_drv_eeprom(self, address, value):
self.__connection.send(TMCLCommand.TMCL_UF2, 2, value & 0xFF, address)
self._connection.send(TMCLCommand.TMCL_UF2, 2, value & 0xFF, address)

mcIdNames = {
mc_id_names = {
0 : "None",
2 : "TMC5031",
4 : "TMC4361",
Expand All @@ -109,7 +217,7 @@ def _write_drv_eeprom(self, address, value):
31 : "TMC5271",
}

drvIdNames = {
drv_id_names = {
0 : "None",
1 : "TMC2660",
3 : "TMC2130",
Expand Down

0 comments on commit 00d8891

Please sign in to comment.