Funpack4-3:TMC4361A-BOB & TMC2160-BOB 驱动步进电机
使用TMC4361A-BOB&TMC2160-BOB板卡连接步进电机,使用ESP32S3结合circuitpython实现电机定速旋转、指定角度旋转、指定圈数旋转、正反转切换功能。
标签
Funpack活动
步进电机
TMC4361
TMC2160
StreakingJerry
更新2026-02-02
170

项目描述

这个项目使用TMC4361A-BOB&TMC2160-BOB板卡连接步进电机,使用ESP32S3结合circuitpython实现电机定速旋转、指定角度旋转、指定圈数旋转、正反转切换功能。

软件流程图及各功能对应的主要代码片段及说明

系统硬件框图如下:

image.png

ESP32-S3通过SPI连接TMC4361和TMC2160,并进行模式配置以及对TMC4361进行控制。


我使用的开发环境是circuitpython,这样开发起来比较方便,可以快速测试寄存器写入读取的效果。


寄存器配置分为两部分,第一部分是TMC4361。由于配置/控制/通信都使用SPI,因此我写了一个驱动来完成对TMC4361的控制:

import time
import digitalio


# TMC4361 Registers (from TMC4361A Datasheet and TMC4361_Arduino library)
# General Configuration Registers
TMC4361_GENERAL_CONFIG_REGISTER = 0x00
TMC4361_REFERENCE_CONFIG_REGISTER = 0x01
TMC4361_START_CONFIG_REGISTER = 0x02
TMC4361_INPUT_FILTER_REGISTER = 0x03
TMC4361_SPIOUT_CONF_REGISTER = 0x04
TMC4361_CURRENT_CONF_REGISTER = 0x05
TMC4361_SCALE_VALUES_REGISTER = 0x06
TMC4361_ENCODER_INPUT_CONFIG_REGISTER = 0x07
TMC4361_ENC_IN_DATA = 0x08
TMC4361_ENC_OUT_DATA = 0x09
TMC4361_STEP_CONF_REGISTER = 0x0A
TMC4361_SPI_STATUS_SELECTION = 0x0B
TMC4361_EVENT_CLEAR_CONF_REGISTER = 0x0C
TMC4361_INTERRUPT_CONFIG_REGISTER = 0x0D
TMC4361_EVENTS_REGISTER = 0x0E
TMC4361_STATUS_REGISTER = 0x0F


# Ramp Generator Registers
TMC4361_STP_LENGTH_ADD = 0x10 # Also contains DIR_SETUP_TIME (bits 31:16)
TMC4361_START_OUT_ADD_REGISTER = 0x11
TMC4361_GEAR_RATIO_REGISTER = 0x12
TMC4361_START_DELAY_REGISTER = 0x13
TMC4361_STDBY_DELAY_REGISTER = 0x15
TMC4361_FREEWHEEL_DELAY_REGISTER = 0x16
TMC4361_VRDV_SCALE_LIMIT_REGISTER = 0x17
TMC4361_UP_SCALE_DELAY_REGISTER = 0x18
TMC4361_HOLD_SCALE_DELAY_REGISTER = 0x19
TMC4361_DRV_SCALE_DELAY_REGISTER = 0x1A
TMC4361_BOOST_TIME_REGISTER = 0x1B
TMC4361_CLOSE_LOOP_REGISTER = 0x1C
TMC4361_DAC_ADDR_REGISTER = 0x1D
TMC4361_HOME_SAFETY_MARGIN_REGISTER = 0x1E
TMC4361_PWM_FREQ_CHOPSYNC_REGISTER = 0x1F
TMC4361_RAMP_MODE_REGISTER = 0x20
TMC4361_X_ACTUAL_REGISTER = 0x21
TMC4361_V_ACTUAL_REGISTER = 0x22
TMC4361_A_ACTUAL_REGISTER = 0x23
TMC4361_V_MAX_REGISTER = 0x24
TMC4361_V_START_REGISTER = 0x25
TMC4361_V_STOP_REGISTER = 0x26
TMC4361_V_BREAK_REGISTER = 0x27
TMC4361_A_MAX_REGISTER = 0x28
TMC4361_D_MAX_REGISTER = 0x29
TMC4361_A_START_REGISTER = 0x2A
TMC4361_D_FINAL_REGISTER = 0x2B
TMC4361_D_STOP_REGISTER = 0x2C
TMC4361_BOW_1_REGISTER = 0x2D
TMC4361_BOW_2_REGISTER = 0x2E
TMC4361_BOW_3_REGISTER = 0x2F
TMC4361_BOW_4_REGISTER = 0x30
TMC4361_CLK_FREQ_REGISTER = 0x31
TMC4361_POSITION_COMPARE_REGISTER = 0x32
TMC4361_VIRTUAL_STOP_LEFT_REGISTER = 0x33
TMC4361_VIRTUAL_STOP_RIGHT_REGISTER = 0x34
TMC4361_X_HOME_REGISTER = 0x35
TMC4361_X_LATCH_REGISTER = 0x36
TMC4361_X_TARGET_REGISTER = 0x37
TMC4361_X_TARGET_PIPE_0_REGSISTER = 0x38


# Shadow Registers
TMC4361_SH_V_MAX_REGISTER = 0x40
TMC4361_SH_A_MAX_REGISTER = 0x41
TMC4361_SH_D_MAX_REGISTER = 0x42
TMC4361_SH_VBREAK_REGISTER = 0x45
TMC4361_SH_V_START_REGISTER = 0x46
TMC4361_SH_V_STOP_REGISTER = 0x47
TMC4361_SH_BOW_1_REGISTER = 0x48
TMC4361_SH_BOW_2_REGISTER = 0x49
TMC4361_SH_BOW_3_REGISTER = 0x4A
TMC4361_SH_BOW_4_REGISTER = 0x4B
TMC4361_SH_RAMP_MODE_REGISTER = 0x4C
TMC4361_D_FREEZE_REGISTER = 0x4E
TMC4361_RESET_CLK_GATING_REGISTER = 0x4F


# Closed-Loop Registers
TMC4361_ENCODER_POSITION_REGISTER = 0x50
TMC4361_CL_MAX_VEL_REGISTER = 0x51
TMC4361_CL_MAX_DEVIATION_REGISTER = 0x52
TMC4361_CL_OFFSET_REGISTER = 0x53
TMC4361_ENCODER_INPUT_RESOLUTION_REGISTER = 0x54
TMC4361_ENCODER_COMPENSATION_REGISTER = 0x55
TMC4361_ENCODER_DEVIATION_REGISTER = 0x56
TMC4361_CL_CURRENT_SCALE_REGISTER = 0x57
TMC4361_CL_SELECTION_REGISTER = 0x58
TMC4361_CL_BETA_REGISTER = 0x59
TMC4361_CL_GAMMA_REGISTER = 0x5A
TMC4361_CL_PHI_REGISTER = 0x5B
TMC4361_CL_DELTA_REGISTER = 0x5C
TMC4361_CL_START_CURRENT_REGISTER = 0x5D
TMC4361_CL_HOLD_CURRENT_REGISTER = 0x5E
TMC4361_CL_RUN_CURRENT_REGISTER = 0x5F
TMC4361_CL_ACCEL_CURRENT_REGISTER = 0x60
TMC4361_CL_DECCEL_CURRENT_REGISTER = 0x61
TMC4361_CL_MAX_CURRENT_REGISTER = 0x62
TMC4361_CL_MIN_CURRENT_REGISTER = 0x63
TMC4361_CL_VEL_THRESHOLD_REGISTER = 0x64
TMC4361_CL_POS_THRESHOLD_REGISTER = 0x65
TMC4361_CL_HOME_THRESHOLD_REGISTER = 0x66
TMC4361_CL_STALL_THRESHOLD_REGISTER = 0x67
TMC4361_CL_FILTER_REGISTER = 0x68
TMC4361_CL_OFFSET_COMP_REGISTER = 0x69
TMC4361_CL_PHI_COMP_REGISTER = 0x6A
TMC4361_CL_DELTA_COMP_REGISTER = 0x6B


# Cover Registers for SPI Passthrough
TMC4361_COVER_LOW_REGISTER = 0x6C
TMC4361_COVER_HIGH_REGISTER = 0x6D


# Version Register
TMC4361_VERSION_REGISTER = 0x7F


class TMC4361:
    # Enum-like classes for RampMode, RampType, EventType, FlagType
    class RampMode:
        VELOCITY_MODE = 0x00
        POSITIONING_MODE = (0x01 << 2) # Bit 2 of RAMP_MODE_REGISTER


    class RampType:
        HOLD_RAMP = 0x00
        TRAPEZOIDAL_RAMP = 0x01
        S_SHAPED_RAMP = 0x02


    class EventType:
        TARGET_REACHED = 0
        POS_COMP_REACHED = 1
        VEL_REACHED = 2
        VEL_STATE_ZERO = 3
        VEL_STATE_POS = 4
        VEL_STATE_NEG = 5
        RAMP_STATE_ACCEL_ZERO = 6
        RAMP_STATE_ACCEL_POS = 7
        RAMP_STATE_ACCEL_NEG = 8
        MAX_PHASE_TRAP = 9
        FROZEN = 10
        STOPL = 11
        STOPR = 12
        VSTOPL_ACTIVE = 13
        VSTOPR_ACTIVE = 14
        HOME_ERROR = 15
        XLATCH_DONE = 16
        FS_ACTIVE = 17
        ENC_FAIL = 18
        N_ACTIVE = 19
        ENC_DONE = 20
        SER_ENC_DATA_FAIL = 21
        SER_DATA_DONE = 23
        SERIAL_ENC_FLAG = 24
        COVER_DONE = 25
        ENC_VEL_ZERO = 26
        CL_MAX = 27
        CL_FIT = 28
        STOP_ON_STALL_EV = 29
        MOTOR_EV = 30
        RST_EV = 31


    class FlagType:
        TARGET_REACHED_F = 0
        POS_COMP_REACHED_F = 1
        VEL_REACHED_F = 2
        VEL_STATE_F0 = 3
        VEL_STATE_F1 = 4
        RAMP_STATE_F0 = 5
        RAMP_STATE_F1 = 6
        STOPL_ACTIVE_F = 7
        STOPR_ACTIVE_F = 8
        VSTOPL_ACTIVE_F = 9
        VSTOPR_ACTIVE_F = 10
        ACTIVE_STALL_F = 11
        HOME_ERROR_F = 12
        FS_ACTIVE_F = 13
        ENC_FAIL_F = 14
        N_ACTIVE_F = 15
        ENC_LATCH_F = 16


    def __init__(self, spi_bus, cs_pin, int_pin=None, start_pin=None, rst_pin=None):
        self._spi = spi_bus
        self._cs = digitalio.DigitalInOut(cs_pin)
        self._cs.direction = digitalio.Direction.OUTPUT
        self._cs.value = True  # CS high initially


        self._int_pin = None
        if int_pin:
            self._int_pin = digitalio.DigitalInOut(int_pin)
            self._int_pin.direction = digitalio.Direction.INPUT


        self._start_pin = None
        if start_pin:
            self._start_pin = digitalio.DigitalInOut(start_pin)
            self._start_pin.direction = digitalio.Direction.INPUT


        self._rst_pin = None
        if rst_pin:
            self._rst_pin = digitalio.DigitalInOut(rst_pin)
            self._rst_pin.direction = digitalio.Direction.OUTPUT
            self._rst_pin.value = True # RST high initially


        self._clock_freq = 0 # Will be set in begin()
        self._spi_status = 0


        self._default_step_length = 5 # us
        self._default_dir_setup_time = 5 # us


    def begin(self, clock_freq):
        self._clock_freq = clock_freq


        # Reset the chip
        self.reset()


        # Write clock frequency to the chip
        self.write_register(TMC4361_CLK_FREQ_REGISTER, self._clock_freq)


        # Set output timings
        self.set_output_timings(self._default_step_length, self._default_dir_setup_time)


        # Protect all events from automatic clearing when reading EVENTS register.
        # This way individual events can be checked at the price of reading the register once per check.
        self.write_register(TMC4361_EVENT_CLEAR_CONF_REGISTER, 0xFFFFFFFF)
        self.clear_events()


    def _spi_transfer(self, address, data):
        # TMC4361 expects 1 byte address + 4 bytes data
        write_buffer = bytearray(5)
        read_buffer = bytearray(5)


        write_buffer[0] = address
        write_buffer[1] = (data >> 24) & 0xFF
        write_buffer[2] = (data >> 16) & 0xFF
        write_buffer[3] = (data >> 8) & 0xFF
        write_buffer[4] = data & 0xFF


        self._cs.value = False  # Select chip
        self._spi.try_lock()
        self._spi.write_readinto(write_buffer, read_buffer) # Perform full duplex transfer
        self._spi.unlock()
        self._cs.value = True   # Deselect chip


        self._spi_status = read_buffer[0] # First byte of read_buffer is the status byte


        # Basic error checking based on SPI status byte (refer to TMC4361A datasheet for details)
        # This is a simplified check, more detailed error handling can be added.
        # if self._spi_status & 0x01: # Check for general error flag (example, actual bit might vary)
        #     print(f"Warning: TMC4361 SPI communication error detected. Status: {self._spi_status:02X}")


        # The returned data is the 32-bit value from the chip, starting from the second byte of read_buffer
        # 组合32位数据
        value = (read_buffer[1] << 24) | (read_buffer[2] << 16) | (read_buffer[3] << 8) | read_buffer[4]
       
        # 检查最高位是否为1(负数)
        if value & 0x80000000:
            # 进行符号扩展,将32位有符号整数转换为Python的负数表示
            value = value - 0x100000000
        return value


    def write_register(self, address, data):
        # Write operation: MSB of address is 1
        self._spi_transfer(address | 0x80, data)


    def read_register(self, address):
        # Read operation: MSB of address is 0
        # Dummy call to load the read address
        self._spi_transfer(address & 0x7F, 0)
        # Actual read
        return self._spi_transfer(address & 0x7F, 0)


    def set_register_bit(self, address, bit):
        value = self.read_register(address)
        value |= (1 << bit) # Set the bit
        self.write_register(address, value)


    def clear_register_bit(self, address, bit):
        value = self.read_register(address)
        value &= ~(1 << bit) # Clear the bit
        self.write_register(address, value)


    def read_register_bit(self, address, bit):
        value = self.read_register(address)
        return bool((value >> bit) & 1)


    def reset(self):
        if self._rst_pin:
            self._rst_pin.value = False
            time.sleep(0.002) # 2ms delay
            self._rst_pin.value = True
        else:
            # Software reset
            self.write_register(TMC4361_RESET_CLK_GATING_REGISTER, 0x52535400) # Magic value + 00 for 32-bit


    def check_flag(self, flag):
        """
        Checks the state of a specific flag in the TMC4361 STATUS_REGISTER.


        Args:
            flag (TMC4361.FlagType): The flag to check.


        Returns:
            bool: True if the flag is set, False otherwise.
        """
        return self.read_register_bit(TMC4361_STATUS_REGISTER, flag)


    def is_target_reached(self):
        """
        Checks if the target position has been reached.


        Returns:
            bool: True if the target position is reached, False otherwise.
        """
        return self.check_flag(self.FlagType.TARGET_REACHED_F)


    def clear_events(self):
        """
        Clears all events in the TMC4361 EVENTS_REGISTER.
        """
        # To clear events, write 1 to the corresponding bit in the EVENTS register.
        # Writing 0 has no effect. So, writing 0xFFFFFFFF clears all events.
        self.write_register(TMC4361_EVENTS_REGISTER, 0xFFFFFFFF)


    def check_event(self, event):
        """
        Checks if a specific event has occurred and clears it if it has.


        Args:
            event (TMC4361.EventType): The event to check.


        Returns:
            bool: True if the event has occurred, False otherwise.
        """
        # Read the current state of the EVENTS register
        events_register_value = self.read_register(TMC4361_EVENTS_REGISTER)
       
        # Check if the specific event bit is set
        event_occurred = bool((events_register_value >> event) & 1)
       
        if event_occurred:
            # Clear the event by writing 1 to its bit in the EVENTS register
            self.write_register(TMC4361_EVENTS_REGISTER, (1 << event))
           
        return event_occurred


    def poll_interrupt_pin(self):
        """
        Checks the state of the interrupt pin (INTR) if it's configured.
        Since CircuitPython does not support true hardware interrupts (ISRs),
        this method allows polling the pin's state to detect events.


        Returns:
            bool: True if the interrupt pin is high (active, depending on configuration),
                  False otherwise. Returns None if int_pin is not configured.
        """
        if self._int_pin:
            return self._int_pin.value
        return None


    def set_outputs_polarity(self, step_inverted, dir_inverted):
        """
        Sets the polarity of the step and direction outputs.


        Args:
            step_inverted (bool): If True, LOW indicates an active step. If False, HIGH indicates an active step.
            dir_inverted (bool): If True, HIGH indicates negative direction. If False, HIGH indicates positive direction.
        """
        general_config_reg = self.read_register(TMC4361_GENERAL_CONFIG_REGISTER)
        if step_inverted:
            general_config_reg |= (1 << 3) # Set bit 3 (STPINV)
        else:
            general_config_reg &= ~(1 << 3) # Clear bit 3 (STPINV)
        if dir_inverted:
            general_config_reg |= (1 << 5) # Set bit 5 (DIRINV)
        else:
            general_config_reg &= ~(1 << 5) # Clear bit 5 (DIRINV)
        self.write_register(TMC4361_GENERAL_CONFIG_REGISTER, general_config_reg)


    def set_ramp_mode(self, mode, type):
        self.write_register(TMC4361_RAMP_MODE_REGISTER, mode | type)


    def get_current_position(self):
        return self.read_register(TMC4361_X_ACTUAL_REGISTER)


    def set_current_position(self, position):
        self.write_register(TMC4361_X_ACTUAL_REGISTER, position)


    def get_current_speed(self):
        # Speed is returned as a 32-bit signed integer
        val = self.read_register(TMC4361_V_ACTUAL_REGISTER)
        return val


    def get_current_acceleration(self):
        # Acceleration is returned as a 32-bit signed integer
        val = self.read_register(TMC4361_A_ACTUAL_REGISTER)
        return self._fixed_point_to_float(val, 2)


    def _float_to_fixed_point(self, value, decimal_places):
        # Equivalent to Arduino's floatToFixedPoint
        scaled_value = int(float(value) * (1 << decimal_places))
        return scaled_value


    def _fixed_point_to_float(self, value, decimal_places):
        # Equivalent to Arduino's fixedPointToFloat
        return float(value) / (1 << decimal_places)


    def set_max_speed(self, speed):
        # VMAX is 23+8 fixed point (23 integer, 8 decimal)
        val = self._float_to_fixed_point(speed, 8)
        self.write_register(TMC4361_V_MAX_REGISTER, val)


    def set_output_timings(self, step_length_us, dir_setup_time_us):
        """
        Sets the step pulse length and direction setup time.
        These values are written to the TMC4361_STP_LENGTH_ADD register (0x10).
        step_length_us: Step pulse length in microseconds.
        dir_setup_time_us: Direction setup time in microseconds.
        """
        # Convert microseconds to clock cycles
        # Assuming clock_freq is in Hz, so clock_period_us = 1,000,000 / clock_freq
        # clock_freq is typically 16MHz (16,000,000 Hz)
        # clock_period_us = 1,000,000 / 16,000,000 = 0.0625 us
        # clock_cycles = us / clock_period_us = us * clock_freq / 1,000,000


        if self._clock_freq == 0:
            print("Error: Clock frequency not set. Call begin() first.")
            return


        step_length_cycles = int(step_length_us * self._clock_freq / 1_000_000)
        dir_setup_cycles = int(dir_setup_time_us * self._clock_freq / 1_000_000)


        # STP_LENGTH_ADD (bits 15:0) and DIR_SETUP_TIME (bits 31:16) are in the same register 0x10
        value = (dir_setup_cycles << 16) | (step_length_cycles & 0xFFFF)
        self.write_register(TMC4361_STP_LENGTH_ADD, value)


    def set_ramp_speeds(self, v_start, v_stop, v_break):
        """
        Configures the start, stop, and break velocities for the ramp generator.
        VSTART, VSTOP, VBREAK are 23+8 fixed point (23 integer, 8 decimal).
        """
        self.write_register(TMC4361_V_START_REGISTER, self._float_to_fixed_point(v_start, 8))
        self.write_register(TMC4361_V_STOP_REGISTER, self._float_to_fixed_point(v_stop, 8))
        self.write_register(TMC4361_V_BREAK_REGISTER, self._float_to_fixed_point(v_break, 8))


    def set_accelerations(self, max_accel, max_decel, start_accel, final_decel):
        """
        Configures the maximum acceleration, deceleration, start acceleration, and final deceleration.
        AMAX, DMAX, ASTART, DFINAL are 22+2 fixed point (22 integer, 2 decimal).
        """
        self.write_register(TMC4361_A_MAX_REGISTER, self._float_to_fixed_point(max_accel, 2))
        self.write_register(TMC4361_D_MAX_REGISTER, self._float_to_fixed_point(max_decel, 2))
        self.write_register(TMC4361_A_START_REGISTER, self._float_to_fixed_point(start_accel, 2))
        self.write_register(TMC4361_D_FINAL_REGISTER, self._float_to_fixed_point(final_decel, 2))


    def set_bow_values(self, bow1, bow2, bow3, bow4):
        """
        Configures the four BOW values for S-shaped ramps.
        BOW values are 24+0 fixed point (24 integer, 0 decimal).
        """
        self.write_register(TMC4361_BOW_1_REGISTER, int(bow1))
        self.write_register(TMC4361_BOW_2_REGISTER, int(bow2))
        self.write_register(TMC4361_BOW_3_REGISTER, int(bow3))
        self.write_register(TMC4361_BOW_4_REGISTER, int(bow4))


    def set_virtual_stop_switches(self, enable_left, enable_right, left_position, right_position):
        """
        Configures and enables/disables virtual stop switches.


        Args:
            enable_left (bool): True to enable left virtual stop switch.
            enable_right (bool): True to enable right virtual stop switch.
            left_position (int): Position for the left virtual stop switch.
            right_position (int): Position for the right virtual stop switch.
        """
        # Configure GENERAL_CONFIG_REGISTER bits for virtual stop switch enable
        general_config_reg = self.read_register(TMC4361_GENERAL_CONFIG_REGISTER)
        if enable_left:
            general_config_reg |= (1 << 16) # Set VIRT_STOP_LEFT_EN bit (Bit 16)
        else:
            general_config_reg &= ~(1 << 16) # Clear VIRT_STOP_LEFT_EN bit


        if enable_right:
            general_config_reg |= (1 << 17) # Set VIRT_STOP_RIGHT_EN bit (Bit 17)
        else:
            general_config_reg &= ~(1 << 17) # Clear VIRT_STOP_RIGHT_EN bit
        self.write_register(TMC4361_GENERAL_CONFIG_REGISTER, general_config_reg)


        # Set virtual stop positions
        self.write_register(TMC4361_VIRTUAL_STOP_LEFT_REGISTER, left_position)
        self.write_register(TMC4361_VIRTUAL_STOP_RIGHT_REGISTER, right_position)


    def enable_closed_loop(self, enable):
        """
        Enables or disables the closed-loop operation of the TMC4361.


        Args:
            enable (bool): True to enable closed-loop, False to disable.
        """
        if enable:
            self.set_register_bit(TMC4361_GENERAL_CONFIG_REGISTER, 1) # Set CL_EN bit (Bit 1 of GENERAL_CONFIG_REGISTER)
        else:
            self.clear_register_bit(TMC4361_GENERAL_CONFIG_REGISTER, 1) # Clear CL_EN bit


    def set_encoder_config(self, resolution, mode, direction=0):
        """
        Configures the encoder input for the TMC4361.


        Args:
            resolution (int): Encoder resolution in steps per revolution.
            mode (int): Encoder input mode (e.g., ABN, SSI, SPI). Refer to datasheet for specific values.
                        Bits 10:11 of GENERAL_CONFIG_REGISTER (SERIAL_ENC_IN_MODE).
            direction (int): Encoder direction (0 for normal, 1 for inverted). Bit 12 of GENERAL_CONFIG_REGISTER (ENC_DIR).
        """
        # Set encoder resolution
        self.write_register(TMC4361_ENCODER_INPUT_RESOLUTION_REGISTER, resolution)


        # Configure encoder mode and direction in GENERAL_CONFIG_REGISTER
        general_config_reg = self.read_register(TMC4361_GENERAL_CONFIG_REGISTER)
        # Clear existing mode bits (10:11)
        general_config_reg &= ~((0x03 << 10))
        # Set new mode
        general_config_reg |= (mode << 10)
        # Set encoder direction
        if direction == 1:
            general_config_reg |= (1 << 12) # Set ENC_DIR bit
        else:
            general_config_reg &= ~(1 << 12) # Clear ENC_DIR bit
        self.write_register(TMC4361_GENERAL_CONFIG_REGISTER, general_config_reg)


    def set_closed_loop_current_scaling(self, start_current, hold_current, run_current, accel_current, decel_current, max_current, min_current):
        """
        Configures the current scaling for closed-loop operation.
        These values are typically 8-bit values (0-255) representing a percentage or scaled value.


        Args:
            start_current (int): Start current for closed-loop (0-255).
            hold_current (int): Hold current for closed-loop (0-255).
            run_current (int): Run current for closed-loop (0-255).
            accel_current (int): Acceleration current for closed-loop (0-255).
            decel_current (int): Deceleration current for closed-loop (0-255).
            max_current (int): Maximum current for closed-loop (0-255).
            min_current (int): Minimum current for closed-loop (0-255).
        """
        self.write_register(TMC4361_CL_START_CURRENT_REGISTER, start_current & 0xFF)
        self.write_register(TMC4361_CL_HOLD_CURRENT_REGISTER, hold_current & 0xFF)
        self.write_register(TMC4361_CL_RUN_CURRENT_REGISTER, run_current & 0xFF)
        self.write_register(TMC4361_CL_ACCEL_CURRENT_REGISTER, accel_current & 0xFF)
        self.write_register(TMC4361_CL_DECCEL_CURRENT_REGISTER, decel_current & 0xFF)
        self.write_register(TMC4361_CL_MAX_CURRENT_REGISTER, max_current & 0xFF)
        self.write_register(TMC4361_CL_MIN_CURRENT_REGISTER, min_current & 0xFF)


    def set_closed_loop_thresholds(self, vel_threshold, pos_threshold, home_threshold, stall_threshold):
        """
        Configures various thresholds for closed-loop operation.


        Args:
            vel_threshold (int): Velocity threshold for closed-loop.
            pos_threshold (int): Position threshold for closed-loop.
            home_threshold (int): Home threshold for closed-loop.
            stall_threshold (int): Stall threshold for closed-loop.
        """
        self.write_register(TMC4361_CL_VEL_THRESHOLD_REGISTER, vel_threshold)
        self.write_register(TMC4361_CL_POS_THRESHOLD_REGISTER, pos_threshold)
        self.write_register(TMC4361_CL_HOME_THRESHOLD_REGISTER, home_threshold)
        self.write_register(TMC4361_CL_STALL_THRESHOLD_REGISTER, stall_threshold)


    def set_closed_loop_gains(self, beta, gamma, phi, delta):
        """
        Configures the closed-loop control gains (Beta, Gamma, Phi, Delta).
        These are typically fixed-point values, refer to datasheet for scaling.


        Args:
            beta (float): Beta gain value.
            gamma (float): Gamma gain value.
            phi (float): Phi gain value.
            delta (float): Delta gain value.
        """
        # Assuming 8 decimal places for these gains as per typical Trinamic fixed-point usage
        self.write_register(TMC4361_CL_BETA_REGISTER, self._float_to_fixed_point(beta, 8))
        self.write_register(TMC4361_CL_GAMMA_REGISTER, self._float_to_fixed_point(gamma, 8))
        self.write_register(TMC4361_CL_PHI_REGISTER, self._float_to_fixed_point(phi, 8))
        self.write_register(TMC4361_CL_DELTA_REGISTER, self._float_to_fixed_point(delta, 8))


    def get_encoder_position(self):
        """
        Reads the current encoder position.


        Returns:
            int: The current encoder position.
        """
        return self.read_register(TMC4361_ENCODER_POSITION_REGISTER)


    def get_encoder_deviation(self):
        """
        Reads the current encoder deviation.


        Returns:
            int: The current encoder deviation.
        """
        return self.read_register(TMC4361_ENCODER_DEVIATION_REGISTER)


    def get_closed_loop_status(self):
        """
        Reads the closed-loop status register.


        Returns:
            int: The raw value of the CL_SELECTION_REGISTER.
        """
        return self.read_register(TMC4361_CL_SELECTION_REGISTER)


    def set_spi_output_config(self, address, data_length, mode):
        """
        Configures the SPI output interface (SPIOUT_CONF_REGISTER) for communicating with motor drivers like TMC2160.


        Args:
            address (int): The address of the motor driver (e.g., 0 for single driver).
            data_length (int): The length of the SPI datagram in bits (e.g., 40 for TMC2160).
            mode (int): The SPI output mode (e.g., 0 for normal, 1 for 2-wire, etc.). Refer to datasheet.
        """
        # SPIOUT_CONF_REGISTER (0x04)
        # Bits 0-7: SPI_OUT_ADDRESS
        # Bits 8-15: SPI_OUT_LENGTH
        # Bits 16-17: SPI_OUT_MODE
        config_value = (address & 0xFF) | ((data_length & 0xFF) << 8) | ((mode & 0x03) << 16)
        self.write_register(TMC4361_SPIOUT_CONF_REGISTER, config_value)


    def spi_passthrough_transfer(self, motor_driver_address, write_data=None):
        """
        Performs a SPI passthrough transfer to a connected motor driver (e.g., TMC2160) via TMC4361A.
        This allows the microcontroller to directly communicate with the motor driver through the TMC4361A.


        Args:
            motor_driver_address (int): The address of the motor driver (e.g., 0 for TMC2160). This is the
                                        TMC2160's internal register address, not the chip address.
            write_data (int, optional): The 32-bit data to write to the motor driver. If None, performs a read.


        Returns:
            int: The 32-bit data read from the motor driver, or 0 if a write operation was performed.
                 Returns the status byte of the TMC4361A after the transfer.
        """
        # The TMC4361A acts as a bridge for SPI communication to the motor driver.
        # To write to the motor driver, we write the 32-bit data to TMC4361_COVER_LOW_REGISTER (0x6C)
        # and the motor driver's register address (with R/W bit) to TMC4361_COVER_HIGH_REGISTER (0x6D).
        # The TMC4361A then handles the actual SPI transfer to the motor driver.
        # For reading, we write the motor driver's register address (with R/W bit) to TMC4361_COVER_HIGH_REGISTER,
        # and then read the response from TMC4361_COVER_LOW_REGISTER.


        # TMC2160 SPI datagram: [5-bit address][3-bit R/W][32-bit data]
        # TMC4361A's COVER_HIGH_REGISTER (0x6D) expects the 8-bit address (including R/W bit) in its lower byte.
        # TMC4361A's COVER_LOW_REGISTER (0x6C) expects/returns the 32-bit data.


        # Prepare the 8-bit address for the motor driver (TMC2160)
        # The TMC2160 register address is 7 bits, plus 1 bit for R/W (MSB of the 8-bit address).
        # Write operation: MSB of TMC2160 register address is 1
        # Read operation: MSB of TMC2160 register address is 0


        if write_data is not None:
            # Write operation to TMC2160
            # Set MSB of motor_driver_address to 1 for write
            tmc2160_addr_rw = motor_driver_address | 0x80
            self.write_register(TMC4361_COVER_LOW_REGISTER, write_data) # Write 32-bit data
            self.write_register(TMC4361_COVER_HIGH_REGISTER, tmc2160_addr_rw) # Write 8-bit address (with R/W)
            # The TMC4361A's SPI status byte will reflect the status of this passthrough operation.
            return self._spi_status
        else:
            # Read operation from TMC2160
            # Set MSB of motor_driver_address to 0 for read
            tmc2160_addr_rw = motor_driver_address & 0x7F
            # Dummy write to trigger the read from TMC2160 via TMC4361A
            self.write_register(TMC4361_COVER_HIGH_REGISTER, tmc2160_addr_rw) # Write 8-bit address (with R/W)
            # Read the response from TMC4361_COVER_LOW_REGISTER
            read_value = self.read_register(TMC4361_COVER_LOW_REGISTER)
            return read_value


    def set_closed_loop_max_velocity(self, max_vel):
        """
        Sets the maximum velocity for closed-loop operation.


        Args:
            max_vel (float): Maximum velocity in steps/s. (Uses 8 decimal places for fixed point)
        """
        self.write_register(TMC4361_CL_MAX_VEL_REGISTER, self._float_to_fixed_point(max_vel, 8))


    def get_closed_loop_max_velocity(self):
        """
        Reads the maximum velocity for closed-loop operation.


        Returns:
            float: Maximum velocity in steps/s.
        """
        return self._fixed_point_to_float(self.read_register(TMC4361_CL_MAX_VEL_REGISTER), 8)


    def set_closed_loop_max_deviation(self, max_dev):
        """
        Sets the maximum deviation for closed-loop operation.


        Args:
            max_dev (int): Maximum deviation in encoder increments.
        """
        self.write_register(TMC4361_CL_MAX_DEVIATION_REGISTER, max_dev)


    def get_closed_loop_max_deviation(self):
        """
        Reads the maximum deviation for closed-loop operation.


        Returns:
            int: Maximum deviation in encoder increments.
        """
        return self.read_register(TMC4361_CL_MAX_DEVIATION_REGISTER)


    def set_closed_loop_offset(self, offset):
        """
        Sets the closed-loop offset.


        Args:
            offset (int): Offset value.
        """
        self.write_register(TMC4361_CL_OFFSET_REGISTER, offset)


    def get_closed_loop_offset(self):
        """
        Reads the closed-loop offset.


        Returns:
            int: Offset value.
        """
        return self.read_register(TMC4361_CL_OFFSET_REGISTER)


    def set_encoder_compensation(self, compensation):
        """
        Sets the encoder compensation value.


        Args:
            compensation (int): Encoder compensation value.
        """
        self.write_register(TMC4361_ENCODER_COMPENSATION_REGISTER, compensation)


    def get_encoder_compensation(self):
        """
        Reads the encoder compensation value.


        Returns:
            int: Encoder compensation value.
        """
        return self.read_register(TMC4361_ENCODER_COMPENSATION_REGISTER)


    def set_closed_loop_filter(self, filter_value):
        """
        Sets the closed-loop filter value.


        Args:
            filter_value (int): Filter value.
        """
        self.write_register(TMC4361_CL_FILTER_REGISTER, filter_value)


    def get_closed_loop_filter(self):
        """
        Reads the closed-loop filter value.


        Returns:
            int: Filter value.
        """
        return self.read_register(TMC4361_CL_FILTER_REGISTER)


    def set_closed_loop_offset_compensation(self, offset_comp):
        """
        Sets the closed-loop offset compensation value.


        Args:
            offset_comp (int): Offset compensation value.
        """
        self.write_register(TMC4361_CL_OFFSET_COMP_REGISTER, offset_comp)


    def get_closed_loop_offset_compensation(self):
        """
        Reads the closed-loop offset compensation value.


        Returns:
            int: Offset compensation value.
        """
        return self.read_register(TMC4361_CL_OFFSET_COMP_REGISTER)


    def set_closed_loop_phi_compensation(self, phi_comp):
        """
        Sets the closed-loop phi compensation value.


        Args:
            phi_comp (int): Phi compensation value.
        """
        self.write_register(TMC4361_CL_PHI_COMP_REGISTER, phi_comp)


    def get_closed_loop_phi_compensation(self):
        """
        Reads the closed-loop phi compensation value.


        Returns:
            int: Phi compensation value.
        """
        return self.read_register(TMC4361_CL_PHI_COMP_REGISTER)


    def set_closed_loop_delta_compensation(self, delta_comp):
        """
        Sets the closed-loop delta compensation value.


        Args:
            delta_comp (int): Delta compensation value.
        """
        self.write_register(TMC4361_CL_DELTA_COMP_REGISTER, delta_comp)


    def get_closed_loop_delta_compensation(self):
        """
        Reads the closed-loop delta compensation value.


        Returns:
            int: Delta compensation value.
        """
        return self.read_register(TMC4361_CL_DELTA_COMP_REGISTER)


    def set_six_point_ramp(self, v_start, v_stop, v_break, a_max, d_max, a_start, d_final, bow1, bow2, bow3, bow4):
        """
        Configures the TMC4361 for SixPoint™ ramp mode.
        This mode provides fine control over the acceleration and deceleration profiles.


        Args:
            v_start (float): Velocity at ramp start (steps/s). Uses 8 decimal places.
            v_stop (float): Velocity at ramp end (steps/s). Uses 8 decimal places.
            v_break (float): Velocity at which acceleration/deceleration changes (steps/s). Uses 8 decimal places.
            a_max (float): Maximum acceleration (steps/s^2). Uses 2 decimal places.
            d_max (float): Maximum deceleration (steps/s^2). Uses 2 decimal places.
            a_start (float): Acceleration at ramp start or below VBREAK (steps/s^2). Uses 2 decimal places.
            d_final (float): Deceleration at ramp end or below VBREAK (steps/s^2). Uses 2 decimal places.
            bow1 (int): First bow value (integer).
            bow2 (int): Second bow value (integer).
            bow3 (int): Third bow value (integer).
            bow4 (int): Fourth bow value (integer).
        """
        # Set ramp mode to SixPoint (which is b'01 for RAMPMODE(1:0) in velocity or positioning mode)
        # The distinction from trapezoidal is made by setting VSTART, VSTOP, and BOW values.
        # For positioning mode, RAMPMODE(2) should be 1, for velocity mode, 0.
        self.set_ramp_mode(self.RampMode.POSITIONING_MODE, self.RampType.TRAPEZOIDAL_RAMP) # SixPoint uses Trapezoidal ramp type


        self.set_ramp_speeds(v_start, v_stop, v_break)
        self.set_accelerations(a_max, d_max, a_start, d_final)
        self.set_bow_values(bow1, bow2, bow3, bow4)


    def get_status_flags(self):
        """
        Reads the raw 32-bit STATUS_FLAGS register.


        Returns:
            int: The raw 32-bit value of the STATUS_FLAGS register.
        """
        return self.read_register(TMC4361_STATUS_REGISTER)


    def get_events(self):
        """
        Reads the raw 32-bit EVENTS register. Events are typically cleared after reading
        unless configured otherwise via set_event_clear_config().


        Returns:
            int: The raw 32-bit value of the EVENTS register.
        """
        return self.read_register(TMC4361_EVENTS_REGISTER)


    def set_event_clear_config(self, event_mask):
        """
        Configures which events are NOT automatically cleared when the EVENTS register is read.
        By default, all events are cleared upon reading. Setting a bit in event_mask to 1
        for a specific event will prevent that event from being cleared automatically.


        Args:
            event_mask (int): A 32-bit mask where each bit corresponds to an event.
                              Set a bit to 1 to include that event in the SPI status byte.
        """
        self.write_register(TMC4361_EVENT_CLEAR_CONF_REGISTER, event_mask)


    def set_spi_status_selection(self, selection_mask):
        """
        Selects up to 8 events to be included in the SPI status byte (first byte of SPI response).
        The LSBs of the selection_mask correspond to the events that will be mapped to the
        SPI status bits. If more than 8 bits are set, only the first 8 (LSB to MSB) are used.


        Args:
            selection_mask (int): A 32-bit mask where each bit corresponds to an event.
                                  Set a bit to 1 to include that event in the SPI status byte.
        """
        self.write_register(TMC4361_SPI_STATUS_SELECTION, selection_mask)


    def set_interrupt_config(self, interrupt_mask):
        """
        Configures which events will trigger the INTR output pin.
        The INTR pin will go active if any of the selected events occur.


        Args:
            interrupt_mask (int): A 32-bit mask where each bit corresponds to an event.
                                  Set a bit to 1 to enable that event to trigger the INTR pin.
        """
        self.write_register(TMC4361_INTERRUPT_CONFIG_REGISTER, interrupt_mask)


    def get_spi_status_byte(self):
        """
        Returns the last received SPI status byte from the TMC4361A.
        This byte contains general status information and up to 8 selected events.


        Returns:
            int: The 8-bit SPI status byte.
        """
        return self._spi_status


    def decode_spi_status(self, spi_status_byte):
        """
        Decodes the SPI status byte into a dictionary of common status flags.
        Note: The exact meaning of bits in the SPI status byte depends on the
        TMC4361A configuration (SPI_STATUS_SELECTION register).
        This function provides a general interpretation based on common usage.


        Args:
            spi_status_byte (int): The 8-bit SPI status byte to decode.


        Returns:
            dict: A dictionary containing decoded status flags.
        """
        decoded_status = {
            "reset_flag": bool(spi_status_byte & (1 << 0)), # General Reset Flag
            "error_flag": bool(spi_status_byte & (1 << 1)), # General Error Flag
            "standstill_flag": bool(spi_status_byte & (1 << 2)), # Motor is at standstill
            "velocity_reached_flag": bool(spi_status_byte & (1 << 3)), # Target velocity reached
            "position_reached_flag": bool(spi_status_byte & (1 << 4)), # Target position reached
            "home_reached_flag": bool(spi_status_byte & (1 << 5)), # Home position reached
            "event_flag": bool(spi_status_byte & (1 << 6)), # Generic event occurred (check EVENTS register)
            "driver_error_flag": bool(spi_status_byte & (1 << 7)), # Error from connected motor driver
        }
        return decoded_status


    def get_driver_error_flags(self):
        """
        Reads the driver error flags from the TMC4361A STATUS_REGISTER.
        These flags indicate errors from the connected motor driver (e.g., TMC2160).


        Returns:
            dict: A dictionary containing decoded driver error flags.
        """
        status_reg = self.get_status_flags()
        # Based on TMC4361A Datasheet, Table 11, page 24, STATUS_FLAGS (0x0F) contains 32 status flags.
        # Driver error flags are typically in the higher bits of the STATUS_FLAGS register.
        # For TMC2160, common flags like SG_RESULT, OTW, OTP, UV_CP, short circuit are reported.
        # The exact bit mapping for driver errors needs to be confirmed from the combined system datasheet (TMC4361A + specific driver).
        # For now, using placeholder bits as in the original code, but this needs verification.
        driver_error_flags = {
            "driver_sg_error": bool(status_reg & (1 << 24)), # StallGuard error (example)
            "driver_ot_error": bool(status_reg & (1 << 25)), # Overtemperature error (example)
            "driver_uv_error": bool(status_reg & (1 << 26)), # Undervoltage error (example)
            "driver_short_error": bool(status_reg & (1 << 27)), # Short circuit error (example)
            # ... add more as per datasheet
        }
        return driver_error_flags


    def get_diagnostic_info(self):
        """
        Gathers comprehensive diagnostic information from the TMC4361A.


        Returns:
            dict: A dictionary containing various diagnostic data.
        """
        diag_info = {
            "spi_status_byte": self.get_spi_status_byte(),
            "decoded_spi_status": self.decode_spi_status(self.get_spi_status_byte()),
            "status_flags_raw": self.get_status_flags(),
            "events_raw": self.get_events(),
            "current_position": self.get_current_position(),
            "current_speed": self.get_current_speed(),
            "current_acceleration": self.get_current_acceleration(),
            "encoder_position": self.get_encoder_position(),
            "encoder_deviation": self.get_encoder_deviation(),
            "closed_loop_status_raw": self.get_closed_loop_status(),
            "driver_error_flags": self.get_driver_error_flags(),
            "version": self.read_register(TMC4361_VERSION_REGISTER)
        }
        return diag_info


    def read_registers_batch(self, addresses):
        """
        Reads multiple registers in a single SPI transaction to optimize performance.
        Note: This method performs a series of read operations. For each read, the TMC4361
        will return the data from the *previous* read request. Therefore, to get the data
        for N registers, N+1 SPI transfers are typically needed, where the last transfer
        is a dummy read to get the data from the Nth register.


        Args:
            addresses (list): A list of register addresses (integers) to read.


        Returns:
            list: A list of integer values corresponding to the data read from each register.
                  The order of returned values matches the order of requested addresses.
        """
        read_values = []
        all_raw_reads = []


        # Perform N read requests. The data returned by each _spi_transfer is from the *previous* request.
        # The first element in all_raw_reads will be garbage (from the previous command or reset state).
        for address in addresses:
            all_raw_reads.append(self._spi_transfer(address & 0x7F, 0))


        # Perform one final dummy read to get the data for the last requested address
        # Reading from a known safe address like GENERAL_CONFIG_REGISTER (0x00) is common.
        all_raw_reads.append(self._spi_transfer(TMC4361_GENERAL_CONFIG_REGISTER, 0))


        # The actual data for addresses[i] is in all_raw_reads[i+1].
        # So, we slice from index 1 to get the actual data for the requested addresses.
        return all_raw_reads[1:]


    def set_target_position(self, position):
        """设置目标位置"""
        self.write_register(TMC4361_X_TARGET_REGISTER, position)


    def get_target_position(self):
        """获取目标位置"""
        return self.read_register(TMC4361_X_TARGET_REGISTER)


    def stop(self):
        """停止运动(通过将最大速度设置为0)"""
        self.set_max_speed(0.0)


    def set_left_virtual_limit(self, position):
        """设置左虚拟限位位置"""
        self.write_register(TMC4361_VIRTUAL_STOP_LEFT_REGISTER, position)


    def get_left_virtual_limit(self):
        """获取左虚拟限位位置"""
        return self.read_register(TMC4361_VIRTUAL_STOP_LEFT_REGISTER)


    def set_right_virtual_limit(self, position):
        """设置右虚拟限位位置"""
        self.write_register(TMC4361_VIRTUAL_STOP_RIGHT_REGISTER, position)


    def get_right_virtual_limit(self):
        """获取右虚拟限位位置"""
        return self.read_register(TMC4361_VIRTUAL_STOP_RIGHT_REGISTER)


    def set_home_position(self, position):
        """设置home位置"""
        self.write_register(TMC4361_X_HOME_REGISTER, position)


    def get_home_position(self):
        """获取home位置"""
        return self.read_register(TMC4361_X_HOME_REGISTER)


    def latch_position(self):
        """锁存当前位置到X_LATCH寄存器"""
        # 触发位置锁存
        self.set_register_bit(TMC4361_GENERAL_CONFIG_REGISTER, 8)  # 设置XLATCH_EN
       
    def get_latched_position(self):
        """获取锁存的位置"""
        return self.read_register(TMC4361_X_LATCH_REGISTER)


    def set_position_compare(self, position):
        """设置位置比较值"""
        self.write_register(TMC4361_POSITION_COMPARE_REGISTER, position)


    def get_position_compare(self):
        """获取位置比较值"""
        return self.read_register(TMC4361_POSITION_COMPARE_REGISTER)


    def set_gear_ratio(self, numerator, denominator):
        """设置齿轮比"""
        # 齿轮比 = numerator/denominator
        gear_ratio_value = ((numerator & 0xFFFF) << 16) | (denominator & 0xFFFF)
        self.write_register(TMC4361_GEAR_RATIO_REGISTER, gear_ratio_value)


    def get_gear_ratio(self):
        """获取齿轮比"""
        gear_ratio_value = self.read_register(TMC4361_GEAR_RATIO_REGISTER)
        numerator = (gear_ratio_value >> 16) & 0xFFFF
        denominator = gear_ratio_value & 0xFFFF
        return numerator, denominator


    def set_start_delay(self, delay_cycles):
        """设置启动延迟(时钟周期数)"""
        self.write_register(TMC4361_START_DELAY_REGISTER, delay_cycles)


    def get_start_delay(self):
        """获取启动延迟"""
        return self.read_register(TMC4361_START_DELAY_REGISTER)


    def set_standby_delay(self, delay_cycles):
        """设置待机延迟(时钟周期数)"""
        self.write_register(TMC4361_STDBY_DELAY_REGISTER, delay_cycles)


    def get_standby_delay(self):
        """获取待机延迟"""
        return self.read_register(TMC4361_STDBY_DELAY_REGISTER)


    def set_freewheel_delay(self, delay_cycles):
        """设置自由轮延迟(时钟周期数)"""
        self.write_register(TMC4361_FREEWHEEL_DELAY_REGISTER, delay_cycles)


    def get_freewheel_delay(self):
        """获取自由轮延迟"""
        return self.read_register(TMC4361_FREEWHEEL_DELAY_REGISTER)


    def set_scale_limit(self, limit):
        """设置比例限制"""
        self.write_register(TMC4361_VRDV_SCALE_LIMIT_REGISTER, limit)


    def get_scale_limit(self):
        """获取比例限制"""
        return self.read_register(TMC4361_VRDV_SCALE_LIMIT_REGISTER)


    def set_upscale_delay(self, delay_cycles):
        """设置上比例延迟(时钟周期数)"""
        self.write_register(TMC4361_UP_SCALE_DELAY_REGISTER, delay_cycles)


    def get_upscale_delay(self):
        """获取上比例延迟"""
        return self.read_register(TMC4361_UP_SCALE_DELAY_REGISTER)


    def set_holdscale_delay(self, delay_cycles):
        """设置保持比例延迟(时钟周期数)"""
        self.write_register(TMC4361_HOLD_SCALE_DELAY_REGISTER, delay_cycles)


    def get_holdscale_delay(self):
        """获取保持比例延迟"""
        return self.read_register(TMC4361_HOLD_SCALE_DELAY_REGISTER)


    def set_drvscale_delay(self, delay_cycles):
        """设置驱动比例延迟(时钟周期数)"""
        self.write_register(TMC4361_DRV_SCALE_DELAY_REGISTER, delay_cycles)


    def get_drvscale_delay(self):
        """获取驱动比例延迟"""
        return self.read_register(TMC4361_DRV_SCALE_DELAY_REGISTER)


    def set_boost_time(self, time_cycles):
        """设置加速时间(时钟周期数)"""
        self.write_register(TMC4361_BOOST_TIME_REGISTER, time_cycles)


    def get_boost_time(self):
        """获取加速时间"""
        return self.read_register(TMC4361_BOOST_TIME_REGISTER)


    def set_home_safety_margin(self, margin):
        """设置home安全边距"""
        self.write_register(TMC4361_HOME_SAFETY_MARGIN_REGISTER, margin)


    def get_home_safety_margin(self):
        """获取home安全边距"""
        return self.read_register(TMC4361_HOME_SAFETY_MARGIN_REGISTER)


    def set_pwm_freq_chopsync(self, pwm_freq, chopsync):
        """设置PWM频率和斩波同步"""
        value = ((pwm_freq & 0x1F) << 8) | (chopsync & 0xFF)
        self.write_register(TMC4361_PWM_FREQ_CHOPSYNC_REGISTER, value)


    def get_pwm_freq_chopsync(self):
        """获取PWM频率和斩波同步设置"""
        value = self.read_register(TMC4361_PWM_FREQ_CHOPSYNC_REGISTER)
        pwm_freq = (value >> 8) & 0x1F
        chopsync = value & 0xFF
        return pwm_freq, chopsync


    def set_d_freeze(self, value):
        """设置D冻结寄存器"""
        self.write_register(TMC4361_D_FREEZE_REGISTER, value)


    def get_d_freeze(self):
        """获取D冻结寄存器值"""
        return self.read_register(TMC4361_D_FREEZE_REGISTER)


    def set_shadow_registers(self, v_max=None, a_max=None, d_max=None, v_break=None,
                            v_start=None, v_stop=None, bow1=None, bow2=None, bow3=None,
                            bow4=None, ramp_mode=None):
        """设置影子寄存器值"""
        if v_max is not None:
            self.write_register(TMC4361_SH_V_MAX_REGISTER, self._float_to_fixed_point(v_max, 8))
        if a_max is not None:
            self.write_register(TMC4361_SH_A_MAX_REGISTER, self._float_to_fixed_point(a_max, 2))
        if d_max is not None:
            self.write_register(TMC4361_SH_D_MAX_REGISTER, self._float_to_fixed_point(d_max, 2))
        if v_break is not None:
            self.write_register(TMC4361_SH_VBREAK_REGISTER, self._float_to_fixed_point(v_break, 8))
        if v_start is not None:
            self.write_register(TMC4361_SH_V_START_REGISTER, self._float_to_fixed_point(v_start, 8))
        if v_stop is not None:
            self.write_register(TMC4361_SH_V_STOP_REGISTER, self._float_to_fixed_point(v_stop, 8))
        if bow1 is not None:
            self.write_register(TMC4361_SH_BOW_1_REGISTER, int(bow1))
        if bow2 is not None:
            self.write_register(TMC4361_SH_BOW_2_REGISTER, int(bow2))
        if bow3 is not None:
            self.write_register(TMC4361_SH_BOW_3_REGISTER, int(bow3))
        if bow4 is not None:
            self.write_register(TMC4361_SH_BOW_4_REGISTER, int(bow4))
        if ramp_mode is not None:
            self.write_register(TMC4361_SH_RAMP_MODE_REGISTER, ramp_mode)


    def update_shadow_registers(self):
        """更新影子寄存器到活动寄存器"""
        # 通过设置D_FREEZE寄存器来触发影子寄存器更新
        self.set_register_bit(TMC4361_D_FREEZE_REGISTER, 14)  # 设置UPDATE_SHADOW


    def set_reference_config(self, config):
        """设置参考配置"""
        self.write_register(TMC4361_REFERENCE_CONFIG_REGISTER, config)


    def get_reference_config(self):
        """获取参考配置"""
        return self.read_register(TMC4361_REFERENCE_CONFIG_REGISTER)


    def set_start_config(self, config):
        """设置启动配置"""
        self.write_register(TMC4361_START_CONFIG_REGISTER, config)


    def get_start_config(self):
        """获取启动配置"""
        return self.read_register(TMC4361_START_CONFIG_REGISTER)


    def set_current_config(self, config):
        """设置电流配置"""
        self.write_register(TMC4361_CURRENT_CONF_REGISTER, config)


    def get_current_config(self):
        """获取电流配置"""
        return self.read_register(TMC4361_CURRENT_CONF_REGISTER)


    def set_scale_values(self, values):
        """设置比例值"""
        self.write_register(TMC4361_SCALE_VALUES_REGISTER, values)


    def get_scale_values(self):
        """获取比例值"""
        return self.read_register(TMC4361_SCALE_VALUES_REGISTER)


    def set_encoder_input_config(self, config):
        """设置编码器输入配置"""
        self.write_register(TMC4361_ENCODER_INPUT_CONFIG_REGISTER, config)


    def get_encoder_input_config(self):
        """获取编码器输入配置"""
        return self.read_register(TMC4361_ENCODER_INPUT_CONFIG_REGISTER)


    def set_step_config(self, config):
        """设置步进配置"""
        self.write_register(TMC4361_STEP_CONF_REGISTER, config)


    def get_step_config(self):
        """获取步进配置"""
        return self.read_register(TMC4361_STEP_CONF_REGISTER)


    def set_encoder_in_data(self, data):
        """设置编码器输入数据"""
        self.write_register(TMC4361_ENC_IN_DATA, data)


    def get_encoder_in_data(self):
        """获取编码器输入数据"""
        return self.read_register(TMC4361_ENC_IN_DATA)


    def set_encoder_out_data(self, data):
        """设置编码器输出数据"""
        self.write_register(TMC4361_ENC_OUT_DATA, data)


    def get_encoder_out_data(self):
        """获取编码器输出数据"""
        return self.read_register(TMC4361_ENC_OUT_DATA)


    def set_dac_addr(self, addr):
        """设置DAC地址"""
        self.write_register(TMC4361_DAC_ADDR_REGISTER, addr)


    def get_dac_addr(self):
        """获取DAC地址"""
        return self.read_register(TMC4361_DAC_ADDR_REGISTER)


    def set_close_loop_config(self, config):
        """设置闭环配置"""
        self.write_register(TMC4361_CLOSE_LOOP_REGISTER, config)


    def get_close_loop_config(self):
        """获取闭环配置"""
        return self.read_register(TMC4361_CLOSE_LOOP_REGISTER)


    def set_cl_max_velocity(self, max_vel):
        """设置闭环最大速度"""
        self.write_register(TMC4361_CL_MAX_VEL_REGISTER, self._float_to_fixed_point(max_vel, 8))


    def get_cl_max_velocity(self):
        """获取闭环最大速度"""
        return self._fixed_point_to_float(self.read_register(TMC4361_CL_MAX_VEL_REGISTER), 8)


    def set_cl_max_deviation(self, max_dev):
        """设置闭环最大偏差"""
        self.write_register(TMC4361_CL_MAX_DEVIATION_REGISTER, max_dev)


    def get_cl_max_deviation(self):
        """获取闭环最大偏差"""
        return self.read_register(TMC4361_CL_MAX_DEVIATION_REGISTER)


    def set_cl_offset(self, offset):
        """设置闭环偏移"""
        self.write_register(TMC4361_CL_OFFSET_REGISTER, offset)


    def get_cl_offset(self):
        """获取闭环偏移"""
        return self.read_register(TMC4361_CL_OFFSET_REGISTER)


    def set_cl_selection(self, selection):
        """设置闭环选择"""
        self.write_register(TMC4361_CL_SELECTION_REGISTER, selection)


    def get_cl_selection(self):
        """获取闭环选择"""
        return self.read_register(TMC4361_CL_SELECTION_REGISTER)


    def set_cl_beta(self, beta):
        """设置闭环Beta参数"""
        self.write_register(TMC4361_CL_BETA_REGISTER, self._float_to_fixed_point(beta, 8))


    def get_cl_beta(self):
        """获取闭环Beta参数"""
        return self._fixed_point_to_float(self.read_register(TMC4361_CL_BETA_REGISTER), 8)


    def set_cl_gamma(self, gamma):
        """设置闭环Gamma参数"""
        self.write_register(TMC4361_CL_GAMMA_REGISTER, self._float_to_fixed_point(gamma, 8))


    def get_cl_gamma(self):
        """获取闭环Gamma参数"""
        return self._fixed_point_to_float(self.read_register(TMC4361_CL_GAMMA_REGISTER), 8)


    def set_cl_phi(self, phi):
        """设置闭环Phi参数"""
        self.write_register(TMC4361_CL_PHI_REGISTER, self._float_to_fixed_point(phi, 8))


    def get_cl_phi(self):
        """获取闭环Phi参数"""
        return self._fixed_point_to_float(self.read_register(TMC4361_CL_PHI_REGISTER), 8)


    def set_cl_delta(self, delta):
        """设置闭环Delta参数"""
        self.write_register(TMC4361_CL_DELTA_REGISTER, self._float_to_fixed_point(delta, 8))


    def get_cl_delta(self):
        """获取闭环Delta参数"""
        return self._fixed_point_to_float(self.read_register(TMC4361_CL_DELTA_REGISTER), 8)


    def set_cl_current_scale(self, scale):
        """设置闭环电流比例"""
        self.write_register(TMC4361_CL_CURRENT_SCALE_REGISTER, scale)


    def get_cl_current_scale(self):
        """获取闭环电流比例"""
        return self.read_register(TMC4361_CL_CURRENT_SCALE_REGISTER)


    def set_cl_start_current(self, current):
        """设置闭环启动电流"""
        self.write_register(TMC4361_CL_START_CURRENT_REGISTER, current)


    def get_cl_start_current(self):
        """获取闭环启动电流"""
        return self.read_register(TMC4361_CL_START_CURRENT_REGISTER)


    def set_cl_hold_current(self, current):
        """设置闭环保持电流"""
        self.write_register(TMC4361_CL_HOLD_CURRENT_REGISTER, current)


    def get_cl_hold_current(self):
        """获取闭环保持电流"""
        return self.read_register(TMC4361_CL_HOLD_CURRENT_REGISTER)


    def set_cl_run_current(self, current):
        """设置闭环运行电流"""
        self.write_register(TMC4361_CL_RUN_CURRENT_REGISTER, current)


    def get_cl_run_current(self):
        """获取闭环运行电流"""
        return self.read_register(TMC4361_CL_RUN_CURRENT_REGISTER)


    def set_cl_accel_current(self, current):
        """设置闭环加速电流"""
        self.write_register(TMC4361_CL_ACCEL_CURRENT_REGISTER, current)


    def get_cl_accel_current(self):
        """获取闭环加速电流"""
        return self.read_register(TMC4361_CL_ACCEL_CURRENT_REGISTER)


    def set_cl_deccel_current(self, current):
        """设置闭环减速电流"""
        self.write_register(TMC4361_CL_DECCEL_CURRENT_REGISTER, current)


    def get_cl_deccel_current(self):
        """获取闭环减速电流"""
        return self.read_register(TMC4361_CL_DECCEL_CURRENT_REGISTER)


    def set_cl_max_current(self, current):
        """设置闭环最大电流"""
        self.write_register(TMC4361_CL_MAX_CURRENT_REGISTER, current)


    def get_cl_max_current(self):
        """获取闭环最大电流"""
        return self.read_register(TMC4361_CL_MAX_CURRENT_REGISTER)


    def set_cl_min_current(self, current):
        """设置闭环最小电流"""
        self.write_register(TMC4361_CL_MIN_CURRENT_REGISTER, current)


    def get_cl_min_current(self):
        """获取闭环最小电流"""
        return self.read_register(TMC4361_CL_MIN_CURRENT_REGISTER)


    def set_cl_vel_threshold(self, threshold):
        """设置闭环速度阈值"""
        self.write_register(TMC4361_CL_VEL_THRESHOLD_REGISTER, threshold)


    def get_cl_vel_threshold(self):
        """获取闭环速度阈值"""
        return self.read_register(TMC4361_CL_VEL_THRESHOLD_REGISTER)


    def set_cl_pos_threshold(self, threshold):
        """设置闭环位置阈值"""
        self.write_register(TMC4361_CL_POS_THRESHOLD_REGISTER, threshold)


    def get_cl_pos_threshold(self):
        """获取闭环位置阈值"""
        return self.read_register(TMC4361_CL_POS_THRESHOLD_REGISTER)


    def set_cl_home_threshold(self, threshold):
        """设置闭环home阈值"""
        self.write_register(TMC4361_CL_HOME_THRESHOLD_REGISTER, threshold)


    def get_cl_home_threshold(self):
        """获取闭环home阈值"""
        return self.read_register(TMC4361_CL_HOME_THRESHOLD_REGISTER)


    def set_cl_stall_threshold(self, threshold):
        """设置闭环失速阈值"""
        self.write_register(TMC4361_CL_STALL_THRESHOLD_REGISTER, threshold)


    def get_cl_stall_threshold(self):
        """获取闭环失速阈值"""
        return self.read_register(TMC4361_CL_STALL_THRESHOLD_REGISTER)


    def set_cl_filter(self, filter_value):
        """设置闭环滤波器"""
        self.write_register(TMC4361_CL_FILTER_REGISTER, filter_value)


    def get_cl_filter(self):
        """获取闭环滤波器"""
        return self.read_register(TMC4361_CL_FILTER_REGISTER)


    def set_cl_offset_comp(self, offset_comp):
        """设置闭环偏移补偿"""
        self.write_register(TMC4361_CL_OFFSET_COMP_REGISTER, offset_comp)


    def get_cl_offset_comp(self):
        """获取闭环偏移补偿"""
        return self.read_register(TMC4361_CL_OFFSET_COMP_REGISTER)


    def set_cl_phi_comp(self, phi_comp):
        """设置闭环Phi补偿"""
        self.write_register(TMC4361_CL_PHI_COMP_REGISTER, phi_comp)


    def get_cl_phi_comp(self):
        """获取闭环Phi补偿"""
        return self.read_register(TMC4361_CL_PHI_COMP_REGISTER)


    def set_cl_delta_comp(self, delta_comp):
        """设置闭环Delta补偿"""
        self.write_register(TMC4361_CL_DELTA_COMP_REGISTER, delta_comp)


    def get_cl_delta_comp(self):
        """获取闭环Delta补偿"""
        return self.read_register(TMC4361_CL_DELTA_COMP_REGISTER)


    def set_cover_low(self, value):
        """设置COVER低寄存器"""
        self.write_register(TMC4361_COVER_LOW_REGISTER, value)


    def get_cover_low(self):
        """获取COVER低寄存器"""
        return self.read_register(TMC4361_COVER_LOW_REGISTER)


    def set_cover_high(self, value):
        """设置COVER高寄存器"""
        self.write_register(TMC4361_COVER_HIGH_REGISTER, value)


    def get_cover_high(self):
        """获取COVER高寄存器"""
        return self.read_register(TMC4361_COVER_HIGH_REGISTER)


    def get_version(self):
        """获取芯片版本"""
        return self.read_register(TMC4361_VERSION_REGISTER)
   
    # 在TMC4361类中添加或修改以下方法


    def spi_passthrough_transfer_v2(self, address, data=None):
        """
        改进的SPI passthrough传输方法,更接近直接控制TMC2160的方式
       
        Args:
            address: TMC2160寄存器地址
            data: 要写入的数据(None表示读取操作)
       
        Returns:
            读取的数据或状态
        """
        if data is not None:
            # 写操作:地址字节的最高位为1
            address_byte = (address | 0x80) & 0xFF
            # 准备数据字节
            data_bytes = [
                (data >> 24) & 0xFF,
                (data >> 16) & 0xFF,
                (data >> 8) & 0xFF,
                data & 0xFF
            ]
           
            # 通过TMC4361COVER寄存器进行传输
            # 首先写入数据到COVER_LOW
            self.write_register(TMC4361_COVER_LOW_REGISTER, data)
           
            # 然后写入地址到COVER_HIGH(触发传输)
            # TMC4361期望COVER_HIGH的低8位包含TMC2160的地址字节
            self.write_register(TMC4361_COVER_HIGH_REGISTER, address_byte)
           
            return 0  # 写操作返回0
        else:
            # 读操作:地址字节的最高位为0
            address_byte = address & 0x7F
           
            # 首先写入地址到COVER_HIGH(触发读取)
            self.write_register(TMC4361_COVER_HIGH_REGISTER, address_byte)
           
            # 然后从COVER_LOW读取数据
            # 需要等待一段时间让传输完成
            time.sleep(0.001)
           
            return self.read_register(TMC4361_COVER_LOW_REGISTER)


    # 或者,尝试使用更直接的方法,绕过TMC4361的passthrough机制
    def direct_tmc2160_transfer(self, address, data=None):
        """
        直接通过TMC4361SPI接口与TMC2160通信
        这种方法假设TMC4361SPI接口可以直接访问TMC2160
       
        Args:
            address: TMC2160寄存器地址
            data: 要写入的数据(None表示读取操作)
       
        Returns:
            读取的数据或状态
        """
        # 设置TMC4361SPI输出配置
        self.write_register(TMC4361_SPIOUT_CONF_REGISTER, 0x00000028)  # 地址=0,长度=40位,模式=0
       
        if data is not None:
            # 写操作
            # TMC2160 SPI数据格式: 1字节地址 + 4字节数据
            # 地址格式: [0:4] = 地址, [5] = 1(), [6:7] = 00
            address_byte = (address & 0x1F) | 0x80  # 设置写位
           
            # 准备完整的数据包 (5字节)
            write_data = (address_byte << 32) | (data & 0xFFFFFFFF)
           
            # 通过SPI发送
            return self._spi_transfer(0x00, write_data)  # 使用任意地址,数据包含完整的数据包
        else:
            # 读操作
            # TMC2160 SPI数据格式: 1字节地址 + 4字节数据
            # 地址格式: [0:4] = 地址, [5] = 0(), [6:7] = 00
            address_byte = address & 0x1F  # 清除写位
           
            # 准备地址字节 (高位) 和空数据 (低位)
            write_data = address_byte << 32
           
            # 通过SPI发送并接收
            result = self._spi_transfer(0x00, write_data)
           
            # 提取返回的数据 (32)
            return result & 0xFFFFFFFF


TMC2160的部分比较简单,只需要配置好所需的电流,微步以及驱动模式即可。因此这部分我就没有写单独的驱动,直接在主程序中完成。

# 初始化TMC2160
spi_bus = busio.SPI(board.GPIO18, MOSI=board.GPIO15, MISO=board.GPIO16)
cs_pin = digitalio.DigitalInOut(board.GPIO17)  # CSN引脚
tmc2160_device = SPIDevice(spi_bus, cs_pin, baudrate=100000, phase=1, polarity=1)


# tmc2160寄存器读/写函数
def read_reg(register):
    # 读操作: 地址字节 = 0x80 | register (最高位为1)
    address_byte = (0x80 | register) & 0xFF
    data_bytes = bytearray(4)
    with tmc2160_device as spi:
        # 发送地址字节并读取4字节数据
        spi.write(bytearray([address_byte]))
        spi.readinto(data_bytes)
    return int.from_bytes(data_bytes, 'big')


def write_reg(register, data):
    # 写操作: 地址字节 = 0x80 | register (最高位为0)
    address_byte = (0x80 | register) & 0xFF
    data_bytes = data.to_bytes(4, 'big')
    with tmc2160_device as spi:
        # 发送地址字节和4字节数据
        spi.write(bytearray([address_byte, data_bytes[0], data_bytes[1], data_bytes[2], data_bytes[3]]))


# 尝试读取IOIN寄存器以验证SPI通信
print("Attempting to read IOIN register...")
ioin_value = read_reg(0x04)  # IOIN寄存器地址为0x04
print("IOIN register value: 0x{:08X}".format(ioin_value))


# 配置电流寄存器 (IHOLD_IRUN, 地址0x10)
# IRUN(bit 8-4)=10 (约1A), IHOLD(bit 3-0)=5 (0.5A), IHOLDDELAY=6
write_reg(0x10, 0x000A0506)
print("Configured IHOLD_IRUN")


# 配置斩波器寄存器 (CHOPCONF, 地址0x6C)
# TOFF=3 (使能斩波器), MRES=4 (16微步)
write_reg(0x6C, 0x000100C3)
print("Configured CHOPCONF")


# 关键配置:启用外部STEP/DIR模式 (GCONF, 地址0x00)
# 设置bit 3 (step_dir_mode)1
write_reg(0x00, 0x00000008)
print("Configured GCONF for STEP/DIR mode")


# 读取DRV_STATUS寄存器(0x6F)进行诊断
drv_status = read_reg(0x6F)
print("DRV_STATUS: 0x{:08X}".format(drv_status))
# 分析DRV_STATUS值:
# - bit 31 (stst): 1表示电机待机,0表示运动
# - bit 0 (ola): A相开路
# - bit 1 (olb): B相开路
# - bit 4 (s2ga): A相短接到地


功能展示及说明

控制部分分成两部分,第一部分是位置控制,第二部分是速度控制。主循环会打印出来当前驱动的状态。

# 设置TMC4361的运动参数
round = 360 / 1.8 * 16 * 16
tmc.set_accelerations(round, round, 0, 0)


# 设置目标位置
tmc.set_ramp_mode(TMC4361.TMC4361.RampMode.POSITIONING_MODE, TMC4361.TMC4361.RampType.TRAPEZOIDAL_RAMP)
tmc.set_max_speed(round)
target = int(round * 3)
tmc.set_target_position(target)
print("Target position set")
# 设置目标速度
tmc.set_ramp_mode(TMC4361.TMC4361.RampMode.VELOCITY_MODE, TMC4361.TMC4361.RampType.TRAPEZOIDAL_RAMP)
tmc.set_max_speed(round * -1)
print("Target velocity set")
# 主循环
while True:
    current_speed = tmc.get_current_speed()
    current_position = tmc.get_current_position()
    target_reached = tmc.is_target_reached()
   
    print(f"Speed: {current_speed}, Position: {current_position}, Target Reached: {target_reached}")
   
    if target_reached:
        print("Target reached!")
        # if current_position >= 0:
        #     tmc.set_target_position(-target)
        # else:
        #     tmc.set_target_position(target)
   
    time.sleep(0.1)

接线上如文章最开始的硬件框图所示,TMC4361和TMC2160之间通过STEP和DIR连接,TMC4361使用脉冲来控制TMC2160。单片机通过SPI连接TMC4361和TMC2160,对它们进行配置。

具体演示可以参考视频。

对本活动的心得体会

时隔一年再次参加电子森林的活动,活动依旧创意十足,诚意满满!

附件下载
TMC4361_CircuitPython.py
code.py
团队介绍
个人
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号