From 00d8891531bd6afb14f8721cc59e69460dd2ad54 Mon Sep 17 00:00:00 2001 From: bp Date: Thu, 23 Jan 2025 13:27:48 +0100 Subject: [PATCH] Landungsbruecke: A bit of rework on the ID EEPROM handling Mostly refactoring but also * `Landungsbruecke.eeprom_mc` changed to `id_eeprom_mc` * `Landungsbruecke.eeprom_drv` changed to `id_eeprom_drv` --- .../connected_eval_information.py | 4 +- .../Landungsbruecke/write_mc_id_eeprom.py | 21 +++ pytrinamic/helpers.py | 109 -------------- pytrinamic/modules/Landungsbruecke.py | 136 ++++++++++++++++-- 4 files changed, 145 insertions(+), 125 deletions(-) create mode 100644 examples/modules/Landungsbruecke/write_mc_id_eeprom.py diff --git a/examples/modules/Landungsbruecke/connected_eval_information.py b/examples/modules/Landungsbruecke/connected_eval_information.py index ff0f4b0c..241713f5 100644 --- a/examples/modules/Landungsbruecke/connected_eval_information.py +++ b/examples/modules/Landungsbruecke/connected_eval_information.py @@ -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()) diff --git a/examples/modules/Landungsbruecke/write_mc_id_eeprom.py b/examples/modules/Landungsbruecke/write_mc_id_eeprom.py new file mode 100644 index 00000000..55c303a2 --- /dev/null +++ b/examples/modules/Landungsbruecke/write_mc_id_eeprom.py @@ -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 + ) diff --git a/pytrinamic/helpers.py b/pytrinamic/helpers.py index ce002ba8..a0b7f0ae 100644 --- a/pytrinamic/helpers.py +++ b/pytrinamic/helpers.py @@ -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) diff --git a/pytrinamic/modules/Landungsbruecke.py b/pytrinamic/modules/Landungsbruecke.py index f7178695..94336df8 100644 --- a/pytrinamic/modules/Landungsbruecke.py +++ b/pytrinamic/modules/Landungsbruecke.py @@ -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): """ @@ -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 @@ -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() @@ -54,19 +162,19 @@ 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") @@ -74,10 +182,10 @@ def _read_mc_eeprom(self, address): 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") @@ -85,9 +193,9 @@ def _read_drv_eeprom(self, address): 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", @@ -109,7 +217,7 @@ def _write_drv_eeprom(self, address, value): 31 : "TMC5271", } - drvIdNames = { + drv_id_names = { 0 : "None", 1 : "TMC2660", 3 : "TMC2130",