diff --git a/examples/tmc2209/main.go b/examples/tmc2209/main.go new file mode 100644 index 000000000..fdb74ba9e --- /dev/null +++ b/examples/tmc2209/main.go @@ -0,0 +1,35 @@ +package main + +import ( + "machine" + + "tinygo.org/x/drivers/tmc2209" +) + +func main() { + uart := machine.UART0 + comm := tmc2209.NewUARTComm(*uart, 0) + // Create an instance of the TMC2209 with UART communication + tmc := tmc2209.NewTMC2209(comm, 0x00) // Replace 0x00 with the appropriate address + + // Set up the TMC2209 driver + err := tmc.Setup() + if err != nil { + println("Failed to set up TMC2209: ", err) + } + + // Write to a register (example: setting a register value) + err = tmc.WriteRegister(0x10, 0x12345678) // Replace 0x10 with the register address and 0x12345678 with the value + if err != nil { + println("Failed to write register:", err) + } + + // Read from a register (example: reading a register value) + value, err := tmc.ReadRegister(0x10) + if err != nil { + println("Failed to read register: ", err) + } + + // Output the read value + println("Register value: ", value) +} diff --git a/go.mod b/go.mod index 530f060e4..7762179eb 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,11 @@ module tinygo.org/x/drivers + go 1.22.1 toolchain go1.23.1 + require ( github.com/eclipse/paho.mqtt.golang v1.2.0 github.com/frankban/quicktest v1.10.2 diff --git a/smoketest.sh b/smoketest.sh index ea74e86cd..50a644067 100755 --- a/smoketest.sh +++ b/smoketest.sh @@ -136,6 +136,7 @@ tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mpu9150/mai tinygo build -size short -o ./build/test.hex -target=macropad-rp2040 ./examples/sh1106/macropad_spi tinygo build -size short -o ./build/test.hex -target=macropad-rp2040 ./examples/encoders/quadrature-interrupt tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mcp9808/main.go +tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/tmc2209/main.go tinygo build -size short -o ./build/test.hex -target=pico ./examples/tmc5160/main.go tinygo build -size short -o ./build/test.uf2 -target=nicenano ./examples/sharpmem/main.go # network examples (espat) diff --git a/tmc2209/README.md b/tmc2209/README.md new file mode 100644 index 000000000..7e67ede7b --- /dev/null +++ b/tmc2209/README.md @@ -0,0 +1,163 @@ +# TMC2209 Go Package + +This package provides a lightweight interface to communicate with the TMC2209 stepper motor driver via UART. It is designed to be used with microcontrollers and supports both default UART communication and custom interface implementations for flexibility. +Currently this package only implements the communications with TMC2209. Functions to handle specific operations such as EnableStealthChop() etc. will need to be implemented by the user. +## Features +- **UART Communication:** Standard communication through UART for controlling the TMC2209 driver. +- **Custom Communication Interfaces:** Allows custom implementations of communication interfaces (e.g., USB, SPI, etc.) via the `RegisterComm` interface. The usecase for this is if you have host system that can "talk" to the microcontroller connected to the TMC2209 +- **Error Handling:** Lightweight error handling for TinyGo compatibility. +- **Register Access:** Provides methods to read and write registers on the TMC2209. + +## Setup + +### Prerequisites +- **TinyGo**: This package is optimized for TinyGo, which is suitable for running Go on microcontrollers and embedded systems. +- **UART Communication**: For microcontrollers with UART support, such as ESP32, STM32, and Raspberry Pi Pico. + + +## Usage +### Using with Microcontrollers (Default UART) + +To use the package with a microcontroller, you need to initialize the UART communication and configure the TMC2209 driver. +Example: + +```aiignore +package main + +import ( + "fmt" + "log" + "github.com/yourusername/tmc2209" + "machine" +) + +func main() { + uart := machine.UART0 + + // Create an instance of the TMC2209 with UART communication + tmc := tmc2209.NewTMC2209(uart, 0x00) // Replace 0x00 with the appropriate address + + // Set up the TMC2209 driver + err := tmc.Setup() + if err != nil { + log.Fatalf("Failed to set up TMC2209: %v", err) + } + + // Write to a register (example: setting a register value) + err = tmc.WriteRegister(0x10, 0x12345678) // Replace 0x10 with the register address and 0x12345678 with the value + if err != nil { + log.Fatalf("Failed to write register: %v", err) + } + + // Read from a register (example: reading a register value) + value, err := tmc.ReadRegister(0x10) + if err != nil { + log.Fatalf("Failed to read register: %v", err) + } + + // Output the read value + fmt.Printf("Register value: 0x%X\n", value) +} + +``` +## Microcontroller Notes + +- **TinyGo Support:** This code is optimized for use with TinyGo on supported microcontrollers. +- **UART Configuration:** Ensure the UART instance is configured correctly for your microcontroller. +- The machine.UART0 in the example is for the default UART on TinyGo-compatible devices like the Raspberry Pi Pico. Check your microcontroller's documentation for the correct UART instance and pin configuration. + +## 2. Custom Interface Implementation + +If you want to use a custom communication interface (e.g., USB, SPI, etc.), you can implement the RegisterComm interface. +Custom Interface Example (e.g., USB Communication): + +``` +package main + +import ( + "fmt" + "log" + "github.com/yourusername/tmc2209" + "yourcustompackage" // Custom package for communication +) + +type CustomComm struct { + // Implement your custom communication method here +} + +func (c *CustomComm) ReadRegister(register uint8, driverIndex uint8) (uint32, error) { + // Implement the register read logic using your custom interface (USB, SPI, etc.) + // For example, send the register read command over USB and read the response + return 0, nil +} + +func (c *CustomComm) WriteRegister(register uint8, value uint32, driverIndex uint8) error { + // Implement the register write logic using your custom interface (USB, SPI, etc.) + // For example, send the register write command over USB + return nil +} + +func main() { + // Create an instance of the TMC2209 with your custom communication interface + customComm := &CustomComm{} + tmc := tmc2209.NewTMC2209(customComm, 0x00) // Replace 0x00 with the appropriate address + + // Set up the TMC2209 driver + err := tmc.Setup() + if err != nil { + log.Fatalf("Failed to set up TMC2209: %v", err) + } + + // Write to a register (example: setting a register value) + err = tmc.WriteRegister(0x10, 0x12345678) // Replace 0x10 with the register address and 0x12345678 with the value + if err != nil { + log.Fatalf("Failed to write register: %v", err) + } + + // Read from a register (example: reading a register value) + value, err := tmc.ReadRegister(0x10) + if err != nil { + log.Fatalf("Failed to read register: %v", err) + } + + // Output the read value + fmt.Printf("Register value: 0x%X\n", value) +} + +``` + +### Custom Interface Notes: + +- RegisterComm Interface: Your custom communication interface (e.g., USB, SPI) should implement the RegisterComm interface. This ensures that the TMC2209 driver can interact with your custom interface for reading and writing registers. +- Error Handling: Implement proper error handling in your custom interface methods (ReadRegister and WriteRegister). + +#### Functions +``` +NewTMC2209(comm RegisterComm, address uint8) *TMC2209 +``` +Creates a new TMC2209 instance with the provided communication interface (comm) and driver address (address). + +```Setup() error``` + +Initializes the communication interface. This is required before interacting with the TMC2209 driver. The method checks if the communication interface is UART and sets it up accordingly. + +```WriteRegister(register uint8, value uint32) error``` + +Writes a value to a specific register on the TMC2209 driver. + +```ReadRegister(register uint8) (uint32, error)``` + +Reads a value from a specific register on the TMC2209 driver. + +### Error Handling + +This package uses a lightweight custom error type (CustomError) to ensure compatibility with TinyGo and reduce external dependencies. +Example Error: + +```CustomError("communication interface not set")``` + + +Created by Amken3d +info@amken3d.us + + diff --git a/tmc2209/address.go b/tmc2209/address.go new file mode 100644 index 000000000..d637f45f4 --- /dev/null +++ b/tmc2209/address.go @@ -0,0 +1,1409 @@ +package tmc2209 + +import "log" + +// TMC2209 Register addresses +const ( + GCONF = 0x00 + GSTAT = 0x01 + IFCNT = 0x02 + IOIN = 0x06 + IHOLD_IRUN = 0x10 + TPOWERDOWN = 0x11 + TSTEP = 0x12 + TPWMTHRS = 0x13 + TCOOLTHRS = 0x14 + VACTUAL = 0x22 + SGTHRS = 0x40 + SG_RESULT = 0x41 + COOLCONF = 0x42 + MSCNT = 0x6A + MSCURACT = 0x6B + CHOPCONF = 0x6C + DRV_STATUS = 0x6F + PWMCONF = 0x70 + PWM_SCALE = 0x71 + PWM_AUTO = 0x72 + expectedVersion = 0x03 +) + +type RegisterComm interface { + ReadRegister(register uint8, driverIndex uint8) (uint32, error) + WriteRegister(register uint8, value uint32, driverIndex uint8) error +} + +// Register is an interface that all register structs will implement for generic access +type Register interface { + Pack() uint32 + Unpack(value uint32) + GetAddress() uint8 +} + +// NewRegister is a generic function to initialize any register with a given address +func NewRegister(registerAddr uint8) Register { + switch registerAddr { + case IOIN: + return NewIoin() + case PWMCONF: + return NewPWMConf() + case GCONF: + return NewGconf() + case GSTAT: + return NewGstat() + case IFCNT: + return NewIfcnt() + case IHOLD_IRUN: + return NewIholdIrun() + case TPOWERDOWN: + return NewTpowerdown() + case TSTEP: + return NewTstep() + case TPWMTHRS: + return NewTpwmthrs() + case TCOOLTHRS: + return NewTcoolthrs() + case VACTUAL: + return NewVactual() + case SGTHRS: + return NewSgthrs() + case SG_RESULT: + return NewSgResult() + case COOLCONF: + return NewCoolConf() + case MSCNT: + return NewMscnt() + case MSCURACT: + return NewMscuract() + case CHOPCONF: + return NewChopconf() + case DRV_STATUS: + return NewDrvStatus() + case PWM_SCALE: + return NewPwmScale() + case PWM_AUTO: + return NewPwmAuto() + default: + return nil + } +} + +// ReadRegister function using the register constants +func ReadRegister(comm RegisterComm, driverIndex uint8, register uint8) (uint32, error) { + // Read the register value using the comm interface + + value, err := comm.ReadRegister(register, driverIndex) + log.Printf("Request read ", register, driverIndex, value) + if err != nil { + return 0, err + } + return value, nil +} + +// WriteRegister function using the register constants +func WriteRegister(comm RegisterComm, register uint8, driverIndex uint8, value uint32) error { + // Write the value to the register using the comm interface + return comm.WriteRegister(register, value, driverIndex) +} + +// Ioin represents the fields of the IOIN register (0x06) in the TMC2209 +// +// The IOIN register provides access to various inputs and control signals +// that the driver uses to determine its current state. This register contains +// bits related to motor driver status, step input, direction, diagnostics, +// and other control signals. +// +// The structure represents each field in the IOIN register and provides +// methods to pack and unpack these fields into the `Bytes` field (a 32-bit +// packed representation of all fields) for easier access and manipulation. +// +// Fields: +// - Enn: Enables the driver. 1 = Driver enabled, 0 = Driver disabled. +// - Reserved0: Reserved bit, must be set to 0. +// - Ms1: Microstep setting, first bit of the microstep configuration. +// - Ms2: Microstep setting, second bit of the microstep configuration. +// - Diag: Diagnostics flag. Used for fault detection and error reporting. +// - Reserved1: Reserved bit, must be set to 0. +// - PdnSerial: Power-down (sleep) state for the UART interface. 1 = Power down, 0 = Active. +// - Step: Step input signal. 1 = Step signal active, 0 = No step signal. +// - SpreadEn: SpreadCycle enable. 1 = Enable SpreadCycle, 0 = Disable SpreadCycle. +// - Dir: Direction input. 1 = Reverse direction, 0 = Forward direction. +// - Reserved2: Reserved bits, must be set to 0. +// - Version: Driver version information. The version of the IOIN register in the TMC2209 chip. +// +// `Bytes`: A 32-bit value that stores the packed representation of the IOIN register. +// The `Bytes` field is used to manipulate the register's value as a single 32-bit value, +// allowing you to read and write it more efficiently. +type Ioin struct { + Enn uint32 // 1-bit field: Driver enable status (1 = enabled, 0 = disabled) + Reserved0 uint32 // 1-bit field: Reserved, should always be 0 + Ms1 uint32 // 1-bit field: Microstep setting (first bit) + Ms2 uint32 // 1-bit field: Microstep setting (second bit) + Diag uint32 // 1-bit field: Diagnostics flag (error reporting) + Reserved1 uint32 // 1-bit field: Reserved, should always be 0 + PdnSerial uint32 // 1-bit field: Power-down state for the UART interface (1 = power down, 0 = active) + Step uint32 // 1-bit field: Step signal input (1 = active, 0 = inactive) + SpreadEn uint32 // 1-bit field: SpreadCycle enable (1 = enabled, 0 = disabled) + Dir uint32 // 1-bit field: Direction input (1 = reverse, 0 = forward) + Reserved2 uint32 // 14-bit field: Reserved bits, should always be 0 + Version uint32 // 8-bit field: Version information for the driver + Bytes uint32 // 32-bit field: Packed representation of the IOIN register (all fields packed into a single 32-bit value) + RegisterAddr uint8 // The address of the register, in this case, IOIN (0x06) +} + +// Pack the individual fields into the Bytes field (a single 32-bit value). +// This method combines all the individual fields (like Enn, Ms1, etc.) +// into a packed 32-bit value that can be written to the register. +func (ioin *Ioin) Pack() uint32 { + ioin.Bytes = (ioin.Enn & 0x01) | // Enn field (1 bit) + ((ioin.Reserved0 & 0x01) << 1) | // Reserved0 field (1 bit) + ((ioin.Ms1 & 0x01) << 2) | // Ms1 field (1 bit) + ((ioin.Ms2 & 0x01) << 3) | // Ms2 field (1 bit) + ((ioin.Diag & 0x01) << 4) | // Diag field (1 bit) + ((ioin.Reserved1 & 0x01) << 5) | // Reserved1 field (1 bit) + ((ioin.PdnSerial & 0x01) << 6) | // PdnSerial field (1 bit) + ((ioin.Step & 0x01) << 7) | // Step field (1 bit) + ((ioin.SpreadEn & 0x01) << 8) | // SpreadEn field (1 bit) + ((ioin.Dir & 0x01) << 9) | // Dir field (1 bit) + ((ioin.Reserved2 & 0x3FFF) << 10) | // Reserved2 field (14 bits) + ((ioin.Version & 0xFF) << 24) // Version field (8 bits) + return ioin.Bytes +} + +// Unpack the Bytes field into the individual fields. +// This method takes the packed 32-bit value from the Bytes field and extracts +// the individual register fields into their corresponding variables. +func (ioin *Ioin) Unpack(uint32) { + ioin.Enn = ioin.Bytes & 0x01 + ioin.Reserved0 = (ioin.Bytes >> 1) & 0x01 + ioin.Ms1 = (ioin.Bytes >> 2) & 0x01 + ioin.Ms2 = (ioin.Bytes >> 3) & 0x01 + ioin.Diag = (ioin.Bytes >> 4) & 0x01 + ioin.Reserved1 = (ioin.Bytes >> 5) & 0x01 + ioin.PdnSerial = (ioin.Bytes >> 6) & 0x01 + ioin.Step = (ioin.Bytes >> 7) & 0x01 + ioin.SpreadEn = (ioin.Bytes >> 8) & 0x01 + ioin.Dir = (ioin.Bytes >> 9) & 0x01 + ioin.Reserved2 = (ioin.Bytes >> 10) & 0x3FFF + ioin.Version = (ioin.Bytes >> 24) & 0xFF +} +func (ioin *Ioin) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, ioin.RegisterAddr) +} +func (ioin *Ioin) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, ioin.RegisterAddr, driverIndex, value) +} +func (ioin *Ioin) GetAddress() uint8 { + return ioin.RegisterAddr +} +func NewIoin() *Ioin { + return &Ioin{ + RegisterAddr: IOIN, + } +} + +// PWMConf represents the fields in the TMC2209 PWMCONF register. +// +// This register controls the Pulse Width Modulation (PWM) configuration for the stepper motor. +// It defines parameters related to the motor's driving mechanism, including PWM offset, +// gradient, frequency, and scaling. These parameters allow fine control over the motor's +// behavior, particularly in the context of energy saving modes like StealthChop. +// +// The fields are as follows: +// - **PwmOfs** (8 bits): The offset for PWM, affecting the motor's drive current. +// - **PwmGrad** (8 bits): The gradient applied to the PWM waveform. It determines the +// rise and fall of the PWM signal, influencing motor smoothness and efficiency. +// - **PwmFreq** (2 bits): PWM frequency control, affecting the switching rate of the motor. +// This value influences the motor's operating frequency and noise characteristics. +// - **PwmAutoscale** (1 bit): This flag enables automatic scaling of the PWM amplitude +// based on the motor’s load. When enabled, the motor adjusts its drive based on current +// requirements, improving energy efficiency and reducing heat generation. +// - **PwmAutograd** (1 bit): Similar to `PwmAutoscale`, but focuses on the gradient +// of the PWM signal, adjusting for the motor's behavior dynamically to maintain efficient +// operation under varying load conditions. +// - **Freewheel** (2 bits): Determines the state of the motor's freewheel functionality, +// which allows the motor to coast when not actively driven, thereby reducing power consumption. +// - **PwmReg** (4 bits): Register for fine-tuning the PWM signal’s behavior. It is typically used +// for customizing the PWM waveform for optimal performance in different conditions. +// - **PwmLim** (4 bits): PWM limit for the motor’s current control. It provides an upper +// bound for the current drive to prevent overheating or excessive current draw. +// +// The `PWMCONF` register allows for precise control over the motor's electrical characteristics, +// contributing to better performance and energy efficiency, particularly in StealthChop mode. +type PWMConf struct { + PwmOfs uint32 // 8 bits + PwmGrad uint32 // 8 bits + PwmFreq uint32 // 2 bits + PwmAutoscale uint32 // 1 bit + PwmAutograd uint32 // 1 bit + Freewheel uint32 // 2 bits + PwmReg uint32 // 4 bits + PwmLim uint32 // 4 bits + Bytes uint32 // 32-bit packed representation of all fields + RegisterAddr uint8 +} + +func (pwm *PWMConf) GetAddress() uint8 { + return pwm.RegisterAddr +} + +// NewPWMConf Initialize PWMConf with register address +func NewPWMConf() *PWMConf { + return &PWMConf{ + RegisterAddr: PWMCONF, + } +} + +// Pack Method to pack the fields into the Bytes field +func (pwm *PWMConf) Pack() uint32 { + // Pack the individual fields into the Bytes field + pwm.Bytes = (pwm.PwmOfs & 0xFF) | + ((pwm.PwmGrad & 0xFF) << 8) | + ((pwm.PwmFreq & 0x03) << 16) | + ((pwm.PwmAutoscale & 0x01) << 18) | + ((pwm.PwmAutograd & 0x01) << 19) | + ((pwm.Freewheel & 0x03) << 20) | + ((pwm.PwmReg & 0x0F) << 24) | + ((pwm.PwmLim & 0x0F) << 28) // PWM_LIM is 4 bits and goes in the last 4 bits + return pwm.Bytes +} + +// Unpack Method to unpack the Bytes field into individual fields +func (pwm *PWMConf) Unpack(uint32) { + // Unpack the Bytes field into individual fields + pwm.PwmOfs = pwm.Bytes & 0xFF + pwm.PwmGrad = (pwm.Bytes >> 8) & 0xFF + pwm.PwmFreq = (pwm.Bytes >> 16) & 0x03 + pwm.PwmAutoscale = (pwm.Bytes >> 18) & 0x01 + pwm.PwmAutograd = (pwm.Bytes >> 19) & 0x01 + pwm.Freewheel = (pwm.Bytes >> 20) & 0x03 + pwm.PwmReg = (pwm.Bytes >> 24) & 0x0F + pwm.PwmLim = (pwm.Bytes >> 28) & 0x0F +} +func (pwm *PWMConf) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, pwm.RegisterAddr) +} +func (pwm *PWMConf) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, driverIndex, pwm.RegisterAddr, value) +} + +// Chopconf represents the fields in the TMC2209 CHOPCONF register. +// +// The CHOPCONF register configures the chopping control for the stepper motor. +// It defines parameters related to the current waveform, step behavior, and +// microstepping resolution. These parameters directly affect the motor's efficiency, +// torque output, and smoothness, especially when operating in StealthChop mode. +// +// The fields are as follows: +// - **Toff** (4 bits): The Off-time parameter for the chopping waveform. +// It sets the duration of the 'off' period of the waveform, which controls how long +// the motor driver remains idle between each pulse. Adjusting this value affects motor efficiency +// and noise characteristics. Lower values allow faster switching and more responsive motors, +// while higher values can improve efficiency at the cost of motor speed. +// - **Hstrt** (3 bits): The Hysteresis start value. This parameter controls the start +// of the hysteresis in the chopping process. It determines when the current will be limited +// based on the hysteresis function. The higher the value, the more gradual the transition +// between phases. +// - **Hend** (4 bits): The Hysteresis end value. This value defines the end of the hysteresis +// region, where the current is stabilized. Like `Hstrt`, higher values result in a smoother +// transition in current regulation and lower torque ripple. +// - **Tbl** (2 bits): The Table selection for the internal chopping table, affecting +// the switching behavior and optimization of the motor’s current profile. +// This setting is typically tuned for different load conditions or step modes. +// - **Vsense** (1 bit): This field selects the sense resistor mode. If enabled, the driver +// uses the internal sense resistors for current sensing, which enables more precise current +// regulation and monitoring, enhancing the motor’s performance and efficiency. +// - **Mres** (4 bits): The microstepping resolution. This value controls how many steps the motor +// takes per full rotation. Higher values allow for finer stepping, leading to smoother motion +// but at the cost of reduced torque at higher microstep resolutions. +// - **Intpol** (1 bit): This field enables interpolation in the motor driver, which improves +// smoothness by interpolating intermediate microsteps between full steps. It helps reduce +// torque ripple and improve overall motor performance. +// - **Dedge** (1 bit): This parameter enables the detection of a specific edge (rising or falling) +// in the step pulse, which is used to control the timing of motor transitions. +// It is generally used for fine-tuning performance or reducing noise during operation. +// - **Diss2g** (1 bit): This field, when enabled, disables the second MOSFET during idle phases +// in the motor operation, further improving efficiency and reducing heat generation. +// - **Diss2vs** (1 bit): Similar to `Diss2g`, this parameter disables the second MOSFET during +// specific phases of operation to reduce losses, increase efficiency, and improve motor control +// in certain scenarios, particularly during low-speed or low-power operation. +// +// The `CHOPCONF` register plays a crucial role in fine-tuning motor operation, ensuring +// smooth motion, efficient power consumption, and minimizing torque ripple in the system. +// These settings are particularly useful when transitioning between operating modes like StealthChop. +type Chopconf struct { + Toff uint32 + Hstrt uint32 + Hend uint32 + Tbl uint32 + Vsense uint32 + Mres uint32 + Intpol uint32 + Dedge uint32 + Diss2g uint32 + Diss2vs uint32 + Bytes uint32 // The packed 32-bit value + RegisterAddr uint8 +} + +func (chopconf *Chopconf) GetAddress() uint8 { + return chopconf.RegisterAddr +} + +// Pack the individual fields into the Bytes field (a single 32-bit value). +func (chopconf *Chopconf) Pack() uint32 { + chopconf.Bytes = (chopconf.Toff & 0x0F) | + ((chopconf.Hstrt & 0x07) << 4) | + ((chopconf.Hend & 0x0F) << 7) | + ((chopconf.Tbl & 0x03) << 15) | + ((chopconf.Vsense & 0x01) << 17) | + ((chopconf.Mres & 0x0F) << 24) | + ((chopconf.Intpol & 0x01) << 28) | + ((chopconf.Dedge & 0x01) << 29) | + ((chopconf.Diss2g & 0x01) << 30) | + ((chopconf.Diss2vs & 0x01) << 31) + return chopconf.Bytes +} + +// Unpack the Bytes field into the individual fields. +func (chopconf *Chopconf) Unpack(uint32) { + chopconf.Toff = chopconf.Bytes & 0x0F + chopconf.Hstrt = (chopconf.Bytes >> 4) & 0x07 + chopconf.Hend = (chopconf.Bytes >> 7) & 0x0F + chopconf.Tbl = (chopconf.Bytes >> 15) & 0x03 + chopconf.Vsense = (chopconf.Bytes >> 17) & 0x01 + chopconf.Mres = (chopconf.Bytes >> 24) & 0x0F + chopconf.Intpol = (chopconf.Bytes >> 28) & 0x01 + chopconf.Dedge = (chopconf.Bytes >> 29) & 0x01 + chopconf.Diss2g = (chopconf.Bytes >> 30) & 0x01 + chopconf.Diss2vs = (chopconf.Bytes >> 31) & 0x01 +} +func NewChopconf() *Chopconf { + return &Chopconf{ + RegisterAddr: CHOPCONF, + } +} +func (chopconf *Chopconf) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, chopconf.RegisterAddr) +} +func (chopconf *Chopconf) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, chopconf.RegisterAddr, driverIndex, value) +} + +// Gstat represents the fields in the TMC2209 GSTAT register. +type Gstat struct { + Reset uint32 + DrvErr uint32 + UvCp uint32 + Reserved uint32 + Bytes uint32 // The packed 32-bit value + RegisterAddr uint8 +} + +func (gstat *Gstat) GetAddress() uint8 { + return gstat.RegisterAddr +} + +// Pack the individual fields into the Bytes field (a single 32-bit value). +func (gstat *Gstat) Pack() uint32 { + gstat.Bytes = (gstat.Reset & 0x01) | + ((gstat.DrvErr & 0x01) << 1) | + ((gstat.UvCp & 0x01) << 2) | + ((gstat.Reserved & 0x1FFFFF) << 3) // 21 bits reserved + return gstat.Bytes +} + +// Unpack the Bytes field into the individual fields. +func (gstat *Gstat) Unpack(uint32) { + gstat.Reset = gstat.Bytes & 0x01 + gstat.DrvErr = (gstat.Bytes >> 1) & 0x01 + gstat.UvCp = (gstat.Bytes >> 2) & 0x01 + gstat.Reserved = (gstat.Bytes >> 3) & 0x1FFFFF +} +func NewGstat() *Gstat { + return &Gstat{ + RegisterAddr: GSTAT, + } +} +func (gstat *Gstat) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, gstat.RegisterAddr) +} +func (gstat *Gstat) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, gstat.RegisterAddr, driverIndex, value) +} + +// Gconf represents the fields in the TMC2209 GCONF register. +// +// The GCONF register configures global settings for the motor driver. +// It controls various aspects of the operation, including stepper driver modes, +// fault detection, and other motor control behaviors that influence motor performance, +// efficiency, and system diagnostics. The register also enables or disables certain features +// based on specific application requirements. +// +// The fields are as follows: +// - **IScaleAnalog** (1 bit): This field controls the scaling of the analog current sense. +// If enabled, it adjusts the scaling of the motor current detection circuitry, improving +// the precision of current regulation. This is important for accurate motor control and +// better torque handling, especially at low speeds. +// - **InternalRsense** (1 bit): When enabled, this field indicates that the internal sense +// resistors are used for current sensing. This allows for improved accuracy in current +// measurement and feedback, resulting in better overall motor performance and energy efficiency. +// - **EnSpreadcycle** (1 bit): This field enables SpreadCycle operation mode. When enabled, +// it switches the motor driver to operate in SpreadCycle mode, which is the traditional +// chopper operation mode. This mode typically results in higher efficiency and more precise +// current control, but it may produce more noise compared to StealthChop mode. +// - **Shaft** (1 bit): This field enables the detection of a shaft (step) in the system, which +// can be used to detect whether the motor is moving or has completed a cycle. It is used +// to improve system control and diagnostics in motor-driven applications. +// - **IndexOtpw** (1 bit): This field, when set, allows detection of the over-temperature +// warning flag (otpw), indicating that the motor or the driver has exceeded safe operating +// temperatures. If enabled, the system can take actions like reducing speed or stopping the motor +// to avoid damage due to overheating. +// - **IndexStep** (1 bit): This field enables indexing of the motor steps. When enabled, it +// allows for step detection, which is helpful in stepper motors where exact step counts are critical. +// This setting ensures accurate positioning of the motor in precise step operations. +// - **PdnDisable** (1 bit): This field enables or disables the power-down functionality for +// the driver. When enabled, the driver will power down the motor and enter a low-power state. +// This is typically used to reduce energy consumption when the motor is not active or when the +// system is in idle mode. This helps save power during periods of non-operation. +// - **MstepRegSelect** (1 bit): This field allows the selection of the multistep regulation +// mode. When enabled, the driver uses the multistep algorithm to control current regulation, +// which can help reduce torque ripple and improve motor smoothness, particularly in applications +// requiring high precision or smooth motion. +// - **MultistepFilt** (1 bit): When enabled, this field activates the multistep filtering. +// This feature is used to reduce the effect of noise and fluctuations in the current waveform. +// It smooths out the power supply and motor performance, particularly at higher speeds or during +// high-load conditions. +// - **Reserved** (21 bits): These reserved bits are unused and should always be set to zero. +// They ensure backward compatibility and alignment with future versions of the register. +// They have no effect on the system's operation. +type Gconf struct { + IScaleAnalog uint32 + InternalRsense uint32 + EnSpreadcycle uint32 + Shaft uint32 + IndexOtpw uint32 + IndexStep uint32 + PdnDisable uint32 + MstepRegSelect uint32 + MultistepFilt uint32 + Reserved uint32 + Bytes uint32 // The packed 32-bit value + RegisterAddr uint8 +} + +func (gconf *Gconf) GetAddress() uint8 { + return gconf.RegisterAddr +} + +// Pack the individual fields into the Bytes field (a single 32-bit value). +func (gconf *Gconf) Pack() uint32 { + gconf.Bytes = (gconf.IScaleAnalog & 0x01) | + ((gconf.InternalRsense & 0x01) << 1) | + ((gconf.EnSpreadcycle & 0x01) << 2) | + ((gconf.Shaft & 0x01) << 3) | + ((gconf.IndexOtpw & 0x01) << 4) | + ((gconf.IndexStep & 0x01) << 5) | + ((gconf.PdnDisable & 0x01) << 6) | + ((gconf.MstepRegSelect & 0x01) << 7) | + ((gconf.MultistepFilt & 0x01) << 8) | + ((gconf.Reserved & 0x1FFFFF) << 9) // 21 bits reserved + return gconf.Bytes +} + +// Unpack the Bytes field into the individual fields. +func (gconf *Gconf) Unpack(uint32) { + gconf.IScaleAnalog = gconf.Bytes & 0x01 + gconf.InternalRsense = (gconf.Bytes >> 1) & 0x01 + gconf.EnSpreadcycle = (gconf.Bytes >> 2) & 0x01 + gconf.Shaft = (gconf.Bytes >> 3) & 0x01 + gconf.IndexOtpw = (gconf.Bytes >> 4) & 0x01 + gconf.IndexStep = (gconf.Bytes >> 5) & 0x01 + gconf.PdnDisable = (gconf.Bytes >> 6) & 0x01 + gconf.MstepRegSelect = (gconf.Bytes >> 7) & 0x01 + gconf.MultistepFilt = (gconf.Bytes >> 8) & 0x01 + gconf.Reserved = (gconf.Bytes >> 9) & 0x1FFFFF +} +func NewGconf() *Gconf { + return &Gconf{ + RegisterAddr: GCONF, + } +} +func (gconf *Gconf) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, gconf.RegisterAddr, driverIndex) +} +func (gconf *Gconf) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, gconf.RegisterAddr, driverIndex, value) +} + +// Ifcnt represents the fields in the TMC2209 IFCNT register. +// +// The IFCNT register is used to monitor the input frequency of the step signal. It holds the +// count of the number of steps that have been processed by the driver and provides +// valuable information about the frequency of step pulses being received. This can be useful +// for diagnostic purposes, to measure the actual step rate, or to check the system’s input +// signal behavior. It helps in monitoring the stepper signal's integrity and rate of pulses +// generated by the control system. +// +// The field is as follows: +// - **Ifcnt** (8 bits): This 8-bit field holds the count of the received step pulses. The count +// is incremented on each step pulse the driver receives and represents the number of steps +// processed by the motor driver. The counter resets at the start of each pulse cycle. This +// field can be used to verify the input signal's frequency and detect potential issues +// such as missed or delayed pulses. Monitoring this register can help optimize the stepper +// signal for better accuracy and performance. +type Ifcnt struct { + Ifcnt uint32 // 8-bit interface counter + Reserved uint32 // Reserved bits, here represented as uint32 for simplicity + Bytes uint32 // The packed 32-bit value + RegisterAddr uint8 +} + +func (ifcnt *Ifcnt) GetAddress() uint8 { + return ifcnt.RegisterAddr +} + +// Pack the individual fields into the Bytes field (a single 32-bit value). +func (ifcnt *Ifcnt) Pack() uint32 { + ifcnt.Bytes = (ifcnt.Ifcnt & 0xFF) | // 8 bits for the interface counter + ((ifcnt.Reserved & 0xFFFFFF) << 8) // Remaining bits for reserved (24 bits) + return ifcnt.Bytes +} + +// Unpack the Bytes field into the individual fields. +func (ifcnt *Ifcnt) Unpack(uint32) { + ifcnt.Ifcnt = ifcnt.Bytes & 0xFF + ifcnt.Reserved = (ifcnt.Bytes >> 8) & 0xFFFFFF +} + +// Initialize IFCNT with register address +func NewIfcnt() *Ifcnt { + return &Ifcnt{ + RegisterAddr: IFCNT, // IFCNT register address + } +} +func (ifcnt *Ifcnt) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, ifcnt.RegisterAddr) +} +func (ifcnt *Ifcnt) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, ifcnt.RegisterAddr, driverIndex, value) +} + +// IholdIrun represents the fields in the TMC2209 IHOLD_IRUN register. +// +// The IHOLD_IRUN register is used to configure the current settings for the motor during +// different phases of operation. It specifically defines two current settings: the hold current +// and the run current. +// +// - **Ihold** (5 bits): This field defines the current value for holding the motor in place when +// it is idle. The value can be set between 0 and 31, with 0 representing the lowest possible current, +// and 31 representing the maximum hold current. The hold current is typically lower than the run current +// to minimize energy consumption when the motor is not actively moving. +// +// - **Iruns** (5 bits): This field sets the current for the motor when it is running (i.e., moving). +// The value can range from 0 to 31, and higher values represent stronger currents for increased torque +// when the motor is under load. The run current is used when the motor is actively engaged in motion. +// +// - **Iholddelay** (4 bits): This field defines the delay in microsteps before transitioning between +// the hold current and the run current. This delay allows for smoother transitions when starting or stopping +// the motor, preventing sudden jumps in current that could cause issues with the stepper system. +// +// This register is critical for efficient motor control, allowing dynamic management of motor currents to optimize +// energy consumption and torque output. Tuning the `Ihold` and `Iruns` settings can significantly impact motor +// performance and efficiency, especially in applications where energy efficiency is important. +type IholdIrun struct { + Ihold uint32 // 5 bits for hold current + Irun uint32 // 5 bits for run current + Iholddelay uint32 // 4 bits for hold delay + Bytes uint32 // The packed 32-bit value + RegisterAddr uint8 // Register address +} + +func (iholdIrun *IholdIrun) GetAddress() uint8 { + return iholdIrun.RegisterAddr +} + +// Pack the individual fields into the Bytes field (a single 32-bit value). +func (iholdIrun *IholdIrun) Pack() uint32 { + iholdIrun.Bytes = (iholdIrun.Ihold & 0x1F) | // 5 bits for IHOLD + ((iholdIrun.Irun & 0x1F) << 5) | // 5 bits for IRUN + ((iholdIrun.Iholddelay & 0x0F) << 10) // 4 bits for IHOLDD_DELAY + return iholdIrun.Bytes +} + +// Unpack the Bytes field into the individual fields. +func (iholdIrun *IholdIrun) Unpack(uint32) { + iholdIrun.Ihold = iholdIrun.Bytes & 0x1F + iholdIrun.Irun = (iholdIrun.Bytes >> 5) & 0x1F + iholdIrun.Iholddelay = (iholdIrun.Bytes >> 10) & 0x0F +} + +// Initialize IHOLD_IRUN with register address +func NewIholdIrun() *IholdIrun { + return &IholdIrun{ + RegisterAddr: IHOLD_IRUN, // IHOLD_IRUN register address + } +} +func (iholdIrun *IholdIrun) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, iholdIrun.RegisterAddr) +} +func (iholdIrun *IholdIrun) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, iholdIrun.RegisterAddr, driverIndex, value) +} + +// Tpwmthrs represents the fields in the TMC2209 TPWMTHRS register. +// +// The TPWMTHRS register is used to set the threshold for transitioning between +// traditional stepper driving (full stepping) and the use of PWM (Pulse Width Modulation) +// for controlling the stepper motor. This register allows for better current control +// and smoother operation, especially at higher speeds. +// +// - **Tpwmthrs** (20 bits): This field defines the threshold value for switching +// from traditional stepper driving to PWM control. The motor will operate with +// standard stepping until the velocity exceeds the value set in this register, +// at which point it will switch to PWM mode. This allows for improved efficiency +// and smoother operation at higher speeds. +// +// The value for the `Tpwmthrs` field is typically set based on the specific motor and +// the desired speed range for the application. A higher threshold means the motor +// will operate in traditional stepping mode at lower speeds, while a lower threshold +// will enable PWM control at lower speeds for better motor control and efficiency. +type Tpwmthrs struct { + Threshold uint32 // 32-bit threshold value + Bytes uint32 // The packed 32-bit value + RegisterAddr uint8 // Register address +} + +func (tpwmthrs *Tpwmthrs) GetAddress() uint8 { + return tpwmthrs.RegisterAddr +} + +// Pack the individual fields into the Bytes field (a single 32-bit value). +func (tpwmthrs *Tpwmthrs) Pack() uint32 { + tpwmthrs.Bytes = tpwmthrs.Threshold & 0xFFFFFFFF // 32-bit threshold value + return tpwmthrs.Bytes +} + +// Unpack the Bytes field into the individual fields. +func (tpwmthrs *Tpwmthrs) Unpack(uint32) { + tpwmthrs.Threshold = tpwmthrs.Bytes & 0xFFFFFFFF +} + +// NewTpwmthrs Initialize TPWMTHRS with register address +func NewTpwmthrs() *Tpwmthrs { + return &Tpwmthrs{ + RegisterAddr: TPWMTHRS, // TPWMTHRS register address + } +} +func (tpwmthrs *Tpwmthrs) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, tpwmthrs.RegisterAddr) +} +func (tpwmthrs *Tpwmthrs) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, tpwmthrs.RegisterAddr, driverIndex, value) +} + +// Vactual represents the fields in the TMC2209 VACTUAL register. +// +// The VACTUAL register provides the actual velocity of the motor in terms of the +// stepper's step clock. It is used to monitor the real-time motor speed and is +// an important part of controlling the motor's behavior during operation. +// +// - **Vactual** (20 bits): This field holds the current actual velocity of the motor. +// It represents the stepper's velocity in terms of the step clock, providing feedback +// on how fast the motor is turning. The value in this register is updated regularly +// based on the microstepping and the actual motor speed. +// +// The `VACTUAL` register is particularly useful for closed-loop control systems, +// where the actual motor speed needs to be compared against the desired speed +// (set in other registers) to make adjustments. This can help in fine-tuning the +// motor's behavior for smoother operation and more accurate performance. +type Vactual struct { + Velocity uint32 // 32-bit velocity value + Bytes uint32 // The packed 32-bit value + RegisterAddr uint8 // Register address +} + +func (vactual *Vactual) GetAddress() uint8 { + return vactual.RegisterAddr +} + +// Pack the individual fields into the Bytes field (a single 32-bit value). +func (vactual *Vactual) Pack() uint32 { + vactual.Bytes = vactual.Velocity & 0xFFFFFFFF // 32-bit velocity value + return vactual.Bytes +} + +// Unpack the Bytes field into the individual fields. +func (vactual *Vactual) Unpack(uint32) { + vactual.Velocity = vactual.Bytes & 0xFFFFFFFF +} + +// Initialize VACTUAL with register address +func NewVactual() *Vactual { + return &Vactual{ + RegisterAddr: VACTUAL, // VACTUAL register address + } +} +func (vactual *Vactual) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, vactual.RegisterAddr) +} +func (vactual *Vactual) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, vactual.RegisterAddr, driverIndex, value) +} + +// Tcoolthrs represents the fields in the TMC2209 TCOOLTHRS register. +// +// The TCOOLTHRS register sets the lower threshold velocity for switching on +// smart energy CoolStep and StallGuard output to the DIAG pin. This helps +// manage the motor's energy usage by enabling CoolStep at higher velocities +// and StallGuard at lower velocities. +// +// - **TCOOLTHRS** (20 bits): The threshold velocity (in units of steps per +// microstep) used to switch between CoolStep and StallGuard modes. The motor +// operates in CoolStep mode if the velocity exceeds this threshold and uses +// StallGuard if the velocity falls below it. +// +// This register is important for optimizing motor performance, especially +// in applications that require energy efficiency. It ensures that the motor +// does not use unnecessary energy at low speeds while still being able to detect stalls +// and adjust torque at higher speeds. CoolStep allows the driver to dynamically adjust +// current levels based on the motor's actual load, reducing power consumption. +type Tcoolthrs struct { + Velocity uint32 // 20 bits for velocity + Bytes uint32 // Packed 32-bit value + RegisterAddr uint8 +} + +func (tcoolthrs *Tcoolthrs) GetAddress() uint8 { + return tcoolthrs.RegisterAddr +} + +// Initialize TCOOLTHRS with register address +func NewTcoolthrs() *Tcoolthrs { + return &Tcoolthrs{ + RegisterAddr: TCOOLTHRS, + } +} + +// Pack the individual fields into the Bytes field (a single 32-bit value). +func (tcoolthrs *Tcoolthrs) Pack() uint32 { + tcoolthrs.Bytes = tcoolthrs.Velocity & 0xFFFFF // Keep only the lower 20 bits + return tcoolthrs.Bytes +} + +// Unpack the Bytes field into the individual fields. +func (tcoolthrs *Tcoolthrs) Unpack(uint32) { + tcoolthrs.Velocity = tcoolthrs.Bytes & 0xFFFFF +} +func (tcoolthrs *Tcoolthrs) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, tcoolthrs.RegisterAddr) +} +func (tcoolthrs *Tcoolthrs) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, tcoolthrs.RegisterAddr, driverIndex, value) +} + +// Sgthrs represents the fields in the TMC2209 SGTHRS register. +// +// The SGTHRS register sets the detection threshold for stall detection in the motor. +// It compares the StallGuard result (SG_RESULT) to twice the value set in this register. +// If the SG_RESULT value falls below the threshold (SG_RESULT < SGTHRS * 2), a stall +// is detected and can trigger a response such as a warning or motor shutdown. +// +// - **SGTHRS** (8 bits): This value sets the detection threshold for the StallGuard feature. +// The result of the StallGuard measurement (SG_RESULT) is compared to this threshold value +// multiplied by 2. When the SG_RESULT value is less than this threshold, a stall is detected. +// +// The `SGTHRS` register is part of the StallGuard feature, which allows the driver to +// detect and respond to motor stalls. StallGuard provides real-time feedback on the motor's +// status by measuring the motor’s back EMF to detect any irregularities that might indicate +// a stall condition. This can help in preventing mechanical damage by responding to stalls early. +type Sgthrs struct { + Threshold uint32 // 8 bits for threshold value + Bytes uint32 // Packed 32-bit value + RegisterAddr uint8 +} + +func (sgthrs *Sgthrs) GetAddress() uint8 { + return sgthrs.RegisterAddr +} + +// Initialize SGTHRS with register address +func NewSgthrs() *Sgthrs { + return &Sgthrs{ + RegisterAddr: SGTHRS, + } +} + +// Pack the individual fields into the Bytes field (a single 32-bit value). +func (sgthrs *Sgthrs) Pack() uint32 { + sgthrs.Bytes = sgthrs.Threshold & 0xFF // Keep only the lower 8 bits + return sgthrs.Bytes +} + +// Unpack the Bytes field into the individual fields. +func (sgthrs *Sgthrs) Unpack(uint32) { + sgthrs.Threshold = sgthrs.Bytes & 0xFF +} +func (sgthrs *Sgthrs) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, sgthrs.RegisterAddr) +} +func (sgthrs *Sgthrs) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, sgthrs.RegisterAddr, driverIndex, value) +} + +// SgResult represents the fields in the TMC2209 SG_RESULT register. +// +// The SG_RESULT register stores the result of the StallGuard measurement, which is used +// to detect motor stalls. The StallGuard feature measures the motor's back electromotive force +// (back EMF) to assess the motor's load and detect irregularities that could indicate a stall. +// +// - **SG_RESULT** (10 bits): The register holds the result of the StallGuard measurement, +// which provides information about the motor's load and stall condition. A higher value in +// SG_RESULT generally means the motor is operating normally, while a lower value indicates +// the motor might be experiencing a stall or a mechanical blockage. +// +// The value in the SG_RESULT register is used in conjunction with the `SGTHRS` register +// (StallGuard Threshold) to determine if a stall condition has occurred. If the SG_RESULT +// value is below twice the value set in the `SGTHRS` register, a stall is considered to have occurred. +// +// The SG_RESULT register is particularly useful for motor stall detection, enabling the driver +// to protect the motor from damage caused by excessive load or mechanical binding by halting the motor's operation. +type SgResult struct { + Result uint32 // 10 bits for the result + Bytes uint32 // Packed 32-bit value + RegisterAddr uint8 +} + +func (sgResult *SgResult) GetAddress() uint8 { + return sgResult.RegisterAddr +} + +// NewSgResult Initialize SG_RESULT with register address +func NewSgResult() *SgResult { + return &SgResult{ + RegisterAddr: SG_RESULT, + } +} + +// Pack the individual fields into the Bytes field (a single 32-bit value). +func (sgResult *SgResult) Pack() uint32 { + sgResult.Bytes = sgResult.Result & 0x3FF // Keep only the lower 10 bits + return sgResult.Bytes +} + +// Unpack the Bytes field into the individual fields. +func (sgResult *SgResult) Unpack(uint32) { + sgResult.Result = sgResult.Bytes & 0x3FF +} +func (sgResult *SgResult) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, sgResult.RegisterAddr) +} +func (sgResult *SgResult) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, sgResult.RegisterAddr, driverIndex, value) +} + +// CoolConf represents the fields in the TMC2209 COOLCONF register. +// +// The COOLCONF register controls various settings related to the CoolStep feature, +// which automatically adjusts the motor's current based on the load to optimize power consumption +// and minimize heat generation. +// +// - **SEMIN** (5 bits): The minimum current value for the CoolStep algorithm to be enabled. If the +// motor current falls below this value, CoolStep will reduce the current. The SEMIN field helps to +// control the minimum threshold for current scaling. +// +// - **SEUP** (2 bits): The step-up value for the current when CoolStep detects an increase in load. +// It defines the amount by which the motor current is increased when the load increases and the motor +// is at risk of stalling. It helps to balance current efficiency and motor performance. +// +// - **SEMAX** (5 bits): The maximum current value for CoolStep. This value sets the upper threshold +// for the current when the motor is under heavy load. It ensures that the motor can handle the load +// by increasing the current when necessary, while still maintaining efficiency. +// +// - **SEDN** (2 bits): The step-down value for the current when CoolStep detects a decrease in load. +// It helps to lower the current consumption when the motor is no longer under heavy load, optimizing +// energy efficiency. +// +// - **SEIMIN** (1 bit): Enables or disables the current scaling for low loads. If enabled, the motor +// current will be reduced under low load conditions, improving energy efficiency. If disabled, the +// motor current remains constant at the preset level, regardless of the load. +// +// The COOLCONF register allows for fine-tuning of the motor current scaling behavior based on the load, +// helping to optimize motor efficiency and reduce power consumption and heat generation. +type CoolConf struct { + Semin uint32 // 1 bit + Sedn uint32 // 2 bits (sedn0, sedn1) + Semax uint32 // 4 bits (semax0 to semax3) + Seup uint32 // 3 bits (seup0, seup1, seup2) + Semin2 uint32 // 6 bits (semin0 to semin5) + CoolStepEnable uint32 // 1 bit + Reserved uint32 // Reserved 10 bits + Bytes uint32 // The packed 32-bit value + RegisterAddr uint8 // The register address (COOLCONF) +} + +func (coolConf *CoolConf) GetAddress() uint8 { + //TODO implement me + panic("implement me") +} + +// Initialize COOLCONF with register address +func NewCoolConf() *CoolConf { + return &CoolConf{ + RegisterAddr: COOLCONF, + } +} + +// Pack the individual fields into the Bytes field (a single 32-bit value). +func (coolConf *CoolConf) Pack() uint32 { + coolConf.Bytes = (coolConf.Semin & 0x01) | + ((coolConf.Sedn & 0x03) << 1) | + ((coolConf.Semax & 0x0F) << 3) | + ((coolConf.Seup & 0x07) << 7) | + ((coolConf.Semin2 & 0x3F) << 10) | + ((coolConf.CoolStepEnable & 0x01) << 16) | + ((coolConf.Reserved & 0x3FF) << 17) // Reserve 10 bits for reserved fields + return coolConf.Bytes +} + +// Unpack the Bytes field into the individual fields. +func (coolConf *CoolConf) Unpack(uint32) { + coolConf.Semin = coolConf.Bytes & 0x01 + coolConf.Sedn = (coolConf.Bytes >> 1) & 0x03 + coolConf.Semax = (coolConf.Bytes >> 3) & 0x0F + coolConf.Seup = (coolConf.Bytes >> 7) & 0x07 + coolConf.Semin2 = (coolConf.Bytes >> 10) & 0x3F + coolConf.CoolStepEnable = (coolConf.Bytes >> 16) & 0x01 + coolConf.Reserved = (coolConf.Bytes >> 17) & 0x3FF +} +func (coolConf *CoolConf) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, coolConf.RegisterAddr) +} +func (coolConf *CoolConf) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, coolConf.RegisterAddr, driverIndex, value) +} + +// DrvStatus represents the fields in the TMC2209 DRV_STATUS register. +// +// The DRV_STATUS register provides information about the driver’s status, +// including over-temperature warnings, short-circuit detection, and current status. +// +// - **OTPW** (1 bit): Overtemperature warning. If this bit is set to 1, it indicates that the +// temperature of the driver has exceeded the safe operating threshold. This can be used to +// trigger protective actions, such as reducing the current or stopping the motor to prevent damage. +// +// - **OT** (1 bit): Overtemperature fault. If this bit is set to 1, it indicates that the driver +// has overheated and is currently in a fault condition due to excessive temperature. It typically +// results in disabling the motor or reducing motor power until the temperature drops to a safe level. +// +// - **S2GA** (1 bit): Short-to-Ground on A phase (phase A of the motor). This bit is set to 1 +// if a short-circuit is detected between the motor's phase A and ground. This protection feature +// helps to safeguard the driver and motor from short circuits that could lead to damage. +// +// - **S2GB** (1 bit): Short-to-Ground on B phase (phase B of the motor). This bit is set to 1 +// if a short-circuit is detected between the motor's phase B and ground. Similar to the previous +// bit, this provides protection against short-circuits for the B phase. +// +// - **S2VSA** (1 bit): Short-to-VCC on A phase (phase A of the motor). If this bit is set to 1, +// it indicates a short-circuit between phase A of the motor and the power supply voltage (VCC). +// This condition could be hazardous and requires corrective actions. +// +// - **S2VSB** (1 bit): Short-to-VCC on B phase (phase B of the motor). Similar to S2VSA, this +// bit is set when a short-circuit is detected between phase B of the motor and VCC. +// +// - **OLA** (1 bit): Overcurrent fault on A phase. This bit is set to 1 if the current through +// phase A exceeds the configured threshold, indicating an overcurrent condition. It may indicate +// a problem with the motor or wiring that requires attention. +// +// - **OLB** (1 bit): Overcurrent fault on B phase. Similar to OLA, this bit indicates an overcurrent +// fault in phase B, signaling an issue that needs to be addressed to prevent damage to the system. +// +// - **T120** (1 bit): Timeout fault for phase A (motor A). If this bit is set, it indicates that +// the motor did not receive any signal or is stuck for too long, possibly due to a failure in the +// motor or wiring. It indicates that the system has detected an abnormal motor condition. +// +// - **T143** (1 bit): Timeout fault for phase B (motor B). Similar to T120, this bit indicates +// an issue with phase B, such as the motor not receiving a signal within the expected timeframe. +// +// - **T150** (1 bit): Timeout fault for both phases A and B. This bit is set if both phases are +// experiencing a timeout condition, which could be due to an issue with the motor or control signals. +// +// - **T157** (1 bit): Timeout fault due to thermal shutdown. If this bit is set, it indicates that +// the driver has shut down due to an overtemperature condition, preventing further damage to the system. +// +// - **CS_ACTUAL** (5 bits): This field provides the actual current setting value for the motor. +// It is used to monitor the current being supplied to the motor in real-time, helping to detect any +// discrepancies or performance issues during operation. +// +// - **STEALTH** (1 bit): StealthChop status. When set to 1, this bit indicates that the StealthChop +// mode is active, which is a feature used to reduce motor noise and improve efficiency at low speeds. +// If set to 0, StealthChop is not active, and the driver may be using a more standard operating mode. +// +// - **STST** (1 bit): Step status. If set to 1, this bit indicates that the motor is currently +// moving or stepping. It can be used to detect if the motor is active or idle at any given time. +type DrvStatus struct { + Stst uint32 // Standstill indicator + Stealth uint32 // StealthChop indicator + CsActual uint32 // Actual motor current / smart energy current + T157 uint32 // 157°C comparator + T150 uint32 // 150°C comparator + T143 uint32 // 143°C comparator + T120 uint32 // 120°C comparator + Olb uint32 // Open load indicator phase B + Ola uint32 // Open load indicator phase A + S2vsb uint32 // Low-side short indicator phase B + S2vsa uint32 // Low-side short indicator phase A + S2gb uint32 // Short to ground indicator phase B + S2ga uint32 // Short to ground indicator phase A + Ot uint32 // Overtemperature flag + Otpw uint32 // Overtemperature pre-warning flag + Reserved uint32 // Reserved bits + Bytes uint32 // The packed 32-bit value + RegisterAddr uint8 // Register address +} + +func (drvStatus *DrvStatus) GetAddress() uint8 { + return drvStatus.RegisterAddr +} + +// Initialize DRV_STATUS with register address +func NewDrvStatus() *DrvStatus { + return &DrvStatus{ + RegisterAddr: DRV_STATUS, + } +} + +// Pack the individual fields into the Bytes field (a single 32-bit value). +func (drvStatus *DrvStatus) Pack() uint32 { + drvStatus.Bytes = (drvStatus.Stst & 0x01) | + ((drvStatus.Stealth & 0x01) << 1) | + ((drvStatus.CsActual & 0xFFFF) << 2) | // Actual current in bits 16-31 + ((drvStatus.T157 & 0x01) << 18) | + ((drvStatus.T150 & 0x01) << 19) | + ((drvStatus.T143 & 0x01) << 20) | + ((drvStatus.T120 & 0x01) << 21) | + ((drvStatus.Olb & 0x01) << 22) | + ((drvStatus.Ola & 0x01) << 23) | + ((drvStatus.S2vsb & 0x01) << 24) | + ((drvStatus.S2vsa & 0x01) << 25) | + ((drvStatus.S2gb & 0x01) << 26) | + ((drvStatus.S2ga & 0x01) << 27) | + ((drvStatus.Ot & 0x01) << 28) | + ((drvStatus.Otpw & 0x01) << 29) | + ((drvStatus.Reserved & 0x7FF) << 30) // Reserved bits + return drvStatus.Bytes +} + +// Unpack the Bytes field into the individual fields. +func (drvStatus *DrvStatus) Unpack(uint32) { + drvStatus.Stst = drvStatus.Bytes & 0x01 + drvStatus.Stealth = (drvStatus.Bytes >> 1) & 0x01 + drvStatus.CsActual = (drvStatus.Bytes >> 2) & 0xFFFF + drvStatus.T157 = (drvStatus.Bytes >> 18) & 0x01 + drvStatus.T150 = (drvStatus.Bytes >> 19) & 0x01 + drvStatus.T143 = (drvStatus.Bytes >> 20) & 0x01 + drvStatus.T120 = (drvStatus.Bytes >> 21) & 0x01 + drvStatus.Olb = (drvStatus.Bytes >> 22) & 0x01 + drvStatus.Ola = (drvStatus.Bytes >> 23) & 0x01 + drvStatus.S2vsb = (drvStatus.Bytes >> 24) & 0x01 + drvStatus.S2vsa = (drvStatus.Bytes >> 25) & 0x01 + drvStatus.S2gb = (drvStatus.Bytes >> 26) & 0x01 + drvStatus.S2ga = (drvStatus.Bytes >> 27) & 0x01 + drvStatus.Ot = (drvStatus.Bytes >> 28) & 0x01 + drvStatus.Otpw = (drvStatus.Bytes >> 29) & 0x01 + drvStatus.Reserved = (drvStatus.Bytes >> 30) & 0x7FF +} +func (drvStatus *DrvStatus) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, drvStatus.RegisterAddr) +} +func (drvStatus *DrvStatus) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, drvStatus.RegisterAddr, driverIndex, value) +} + +// PwmScale represents the fields in the TMC2209 PWM_SCALE register. +// +// The PWM_SCALE register provides information related to the current PWM scaling +// values for the motor driver. It is used to determine the effective current +// supplied to the motor and the PWM (Pulse Width Modulation) duty cycle. +// +// - **PwmScaleSum** (8 bits): This field provides the total PWM scaling value used +// for the motor driver. It represents the summed scaling value of both motor phases. +// A higher value typically means more power is delivered to the motor. +// +// - **PwmScaleAuto** (9 bits): This field contains the automatically calculated +// PWM scaling value. It adjusts the power delivery dynamically based on motor +// load and thermal conditions to optimize performance and efficiency. +// The value in this field provides an indication of the current level of scaling +// that the driver is using in the automatic mode, based on the real-time motor conditions. +type PwmScale struct { + PwmScaleSum uint32 // 8-bit PWM duty cycle + PwmScaleAuto int32 // 9-bit signed offset (-255 to +255) + Bytes uint32 // The packed 32-bit value + RegisterAddr uint8 // Register address +} + +func (pwm *PwmScale) GetAddress() uint8 { + return pwm.RegisterAddr +} + +// NewPwmScale Initialize PwmScale with register address +func NewPwmScale() *PwmScale { + return &PwmScale{ + RegisterAddr: PWM_SCALE, // PWM_SCALE register address + } +} + +// Pack the individual fields into the Bytes field (a single 32-bit value). +func (pwm *PwmScale) Pack() uint32 { + pwm.Bytes = (pwm.PwmScaleSum & 0xFF) | + ((uint32(pwm.PwmScaleAuto) & 0x1FF) << 8) // 9 bits for PWM_SCALE_AUTO + return pwm.Bytes +} + +// Unpack the Bytes field into the individual fields. +func (pwm *PwmScale) Unpack(uint32) { + pwm.PwmScaleSum = pwm.Bytes & 0xFF + pwm.PwmScaleAuto = int32((pwm.Bytes >> 8) & 0x1FF) // 9-bit signed value +} +func (pwm *PwmScale) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, pwm.RegisterAddr) +} +func (pwm *PwmScale) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, pwm.RegisterAddr, driverIndex, value) +} + +// PwmAuto represents the fields in the TMC2209 PWM_AUTO register. +// +// The PWM_AUTO register is used to configure and monitor the automatic scaling and +// adjustment of PWM settings in the TMC2209 motor driver. This register helps to +// fine-tune motor control behavior based on real-time operating conditions. +// +// - **PwmOfsAuto** (8 bits): This field contains the automatically adjusted PWM +// offset value. The PWM offset is used to set the initial duty cycle for the PWM +// signal. Adjusting this field allows for fine-tuning of motor behavior in +// specific scenarios, such as optimizing torque or minimizing motor heating. +// +// - **PwmGradAuto** (8 bits): This field holds the automatically adjusted PWM +// gradient value. The gradient determines the rate at which the PWM duty cycle +// increases or decreases over time. A higher gradient value can lead to faster +// transitions in power delivery, which is useful for certain motor acceleration +// profiles or applications requiring smooth power changes. +type PwmAuto struct { + PwmOfsAuto int32 // 8-bit signed offset value (-255 to +255) + PwmGradAuto int32 // 8-bit automatically determined gradient value (-255 to +255) + Bytes uint32 // The packed 32-bit value + RegisterAddr uint8 // Register address +} + +func (pwm *PwmAuto) GetAddress() uint8 { + return pwm.RegisterAddr +} + +// Initialize PwmAuto with register address +func NewPwmAuto() *PwmAuto { + return &PwmAuto{ + RegisterAddr: PWM_AUTO, // PWM_AUTO register address + } +} + +// Pack the individual fields into the Bytes field (a single 32-bit value). +func (pwm *PwmAuto) Pack() uint32 { + pwm.Bytes = (uint32(pwm.PwmOfsAuto) & 0xFF) | + ((uint32(pwm.PwmGradAuto) & 0xFF) << 8) // 8 bits for each value + return pwm.Bytes +} + +// Unpack the Bytes field into the individual fields. +func (pwm *PwmAuto) Unpack(uint32) { + pwm.PwmOfsAuto = int32(pwm.Bytes & 0xFF) + pwm.PwmGradAuto = int32((pwm.Bytes >> 8) & 0xFF) +} +func (pwm *PwmAuto) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, pwm.RegisterAddr) +} +func (pwm *PwmAuto) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, pwm.RegisterAddr, driverIndex, value) +} + +// Tpowerdown represents the fields in the TMC2209 TPOWERDOWN register. +// +// The TPOWERDOWN register is used to configure the time delay before the driver +// enters the power-down state after the motor has been idle. This is useful for +// energy-saving purposes and to reduce the overall power consumption when the +// motor is not being actively driven. +// +// - **Tpowerdown** (8 bits): This field specifies the time delay (in microseconds) +// before the TMC2209 enters the power-down mode. The power-down mode reduces the +// current drawn by the motor and motor driver, which helps save energy when the +// motor is not in use. The delay is adjustable to balance between responsiveness +// and power consumption. +type Tpowerdown struct { + DelayTime uint32 // Delay time from standstill detection to motor current power-down (8 bits) + RegisterAddr uint8 // Register address + Bytes uint32 // The packed 32-bit value +} + +func (tpd *Tpowerdown) GetAddress() uint8 { + return tpd.RegisterAddr +} + +// Initialize Tpowerdown with register address +func NewTpowerdown() *Tpowerdown { + return &Tpowerdown{ + RegisterAddr: TPOWERDOWN, // TPOWERDOWN register address + } +} + +// Pack the DelayTime field into the Bytes field (a single 8-bit value). +func (tpd *Tpowerdown) Pack() uint32 { + tpd.Bytes = tpd.DelayTime & 0xFF + return tpd.Bytes +} + +// Unpack the Bytes field into the DelayTime field. +func (tpd *Tpowerdown) Unpack(uint32) { + tpd.DelayTime = tpd.Bytes & 0xFF +} +func (tpd *Tpowerdown) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, tpd.RegisterAddr) +} +func (tpd *Tpowerdown) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, tpd.RegisterAddr, driverIndex, value) +} + +// Tstep represents the fields in the TMC2209 TSTEP register. +// +// The TSTEP register configures the inter-step duration, which is the time between +// the steps sent to the motor driver. This is important for controlling the speed +// of the motor, and is typically used to set the stepping frequency. +// +// - **Tstep** (24 bits): This field specifies the duration of time between each step +// signal, measured in microseconds. The value determines how quickly the motor steps, +// effectively controlling the motor's speed. Smaller values result in faster stepping +// (higher speed), while larger values reduce the stepping frequency (lower speed). +// +// The `TSTEP` register is critical for applications that require precise motor control, +// especially for variable-speed applications. The duration set in `TSTEP` should be +// chosen based on the desired motor speed and the capabilities of the motor driver. +type Tstep struct { + StepTime uint32 // Time between 1/256 microsteps (20 bits) + RegisterAddr uint8 // Register address + Bytes uint32 // The packed 32-bit value +} + +func (tstep *Tstep) GetAddress() uint8 { + return tstep.RegisterAddr +} + +// Initialize Tstep with register address +func NewTstep() *Tstep { + return &Tstep{ + RegisterAddr: TSTEP, // TSTEP register address + } +} + +// Pack the StepTime field into the Bytes field (a single 20-bit value). +func (tstep *Tstep) Pack() uint32 { + tstep.Bytes = tstep.StepTime & 0xFFFFF // 20 bits for TSTEP + return tstep.Bytes +} + +// Unpack the Bytes field into the StepTime field. +func (tstep *Tstep) Unpack(uint32) { + tstep.StepTime = tstep.Bytes & 0xFFFFF +} +func (tstep *Tstep) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, tstep.RegisterAddr) +} +func (tstep *Tstep) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, tstep.RegisterAddr, driverIndex, value) +} + +// Mscnt represents the Microstep Counter Register (0x6A) in the TMC2209 +// +// This register provides the actual microstep position within the microstep table. +// The value of MSCNT allows determination of the motor position within the electrical wave, +// which is essential for accurately controlling the motor's position during operation. +// +// Fields: +// - The register contains a 10-bit value indicating the actual position in the microstep table. +// - Range: 0 to 1023 (0x000 to 0x3FF) +type Mscnt struct { + Position uint32 + RegisterAddr uint8 +} + +func (mscnt *Mscnt) GetAddress() uint8 { + return mscnt.RegisterAddr +} + +// Pack the Position value (10-bit) into the Bytes field +func (mscnt *Mscnt) Pack() uint32 { + mscnt.Position = mscnt.Position & 0x03FF // Limit to 10 bits (0x3FF) + return mscnt.Position +} + +// Unpack the Bytes field into the Position field. +func (mscnt *Mscnt) Unpack(uint32) { + mscnt.Position = mscnt.Position & 0x03FF +} + +// NewMscnt initializes a new Mscnt struct with the correct register address. +func NewMscnt() *Mscnt { + return &Mscnt{ + RegisterAddr: MSCNT, // MSCNT register address + } +} +func (mscnt *Mscnt) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, mscnt.RegisterAddr) +} +func (mscnt *Mscnt) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, mscnt.RegisterAddr, driverIndex, value) +} + +// Mscuract MSCURACT represents the Microstep Current Register (0x6B) in the TMC2209 +// +// This register provides the actual current values for motor phases A and B as read from +// the internal sine wave table. The values are not scaled by the current setting but provide +// a raw value for the motor's electrical waves. +// +// Fields: +// - CUR_B (bits 0-7): The actual microstep current for motor phase B (signed value in the range +/-255). +// - CUR_A (bits 24-16): The actual microstep current for motor phase A (signed value in the range +/-255). +type Mscuract struct { + // Microstep current for phase B (sine wave) + CurB uint32 + // Microstep current for phase A (cosine wave) + CurA uint32 + // Register address + RegisterAddr uint8 +} + +func (mscuract *Mscuract) GetAddress() uint8 { + return mscuract.RegisterAddr +} + +// Pack the individual fields into the Bytes field (a single 32-bit value). +func (mscuract *Mscuract) Pack() uint32 { + mscuract.CurB = mscuract.CurB & 0xFF // Limit to 8 bits for CUR_B + mscuract.CurA = mscuract.CurA & 0xFF // Limit to 8 bits for CUR_A + return mscuract.CurA +} + +// Unpack the Bytes field into the individual fields. +func (mscuract *Mscuract) Unpack(uint32) { + mscuract.CurB = mscuract.CurB & 0xFF // Extract CUR_B (8 bits) + mscuract.CurA = (mscuract.CurA >> 16) & 0xFF // Extract CUR_A (8 bits) +} + +// NewMscuract initializes a new Mscuract struct with the correct register address. +func NewMscuract() *Mscuract { + return &Mscuract{ + RegisterAddr: MSCURACT, + } +} +func (mscuract *Mscuract) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, mscuract.RegisterAddr) +} +func (mscuract *Mscuract) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, mscuract.RegisterAddr, driverIndex, value) +} diff --git a/tmc2209/current.go b/tmc2209/current.go new file mode 100644 index 000000000..1224e6fd7 --- /dev/null +++ b/tmc2209/current.go @@ -0,0 +1,42 @@ +package tmc2209 + +func Constrain(value, low, high uint32) uint32 { + if value < low { + return low + } + if value > high { + return high + } + return value +} + +func SetRunCurrent(percent uint8) { + _ = PercentToCurrentSetting(percent) + + // Set the run current register to runCurrent value +} + +func SetHoldCurrent(percent uint8) { + _ = PercentToCurrentSetting(percent) + // Set the hold current register to holdCurrent value +} +func PercentToCurrentSetting(percent uint8) uint8 { + constrainedPercent := Constrain(uint32(percent), 0, 100) + return uint8(Map(constrainedPercent, 0, 100, 0, 255)) +} + +func CurrentSettingToPercent(currentSetting uint8) uint8 { + return uint8(Map(uint32(currentSetting), 0, 255, 0, 100)) +} + +func PercentToHoldDelaySetting(percent uint8) uint8 { + constrainedPercent := Constrain(uint32(percent), 0, 100) + return uint8(Map(constrainedPercent, 0, 100, 0, 255)) +} + +func HoldDelaySettingToPercent(holdDelaySetting uint8) uint8 { + return uint8(Map(uint32(holdDelaySetting), 0, 255, 0, 100)) +} +func Map(value, fromLow, fromHigh, toLow, toHigh uint32) uint32 { + return (value-fromLow)*(toHigh-toLow)/(fromHigh-fromLow) + toLow +} diff --git a/tmc2209/motor_config.go b/tmc2209/motor_config.go new file mode 100644 index 000000000..c828e770b --- /dev/null +++ b/tmc2209/motor_config.go @@ -0,0 +1,37 @@ +package tmc2209 + +func SetMicrostepsPerStep(microsteps uint16) uint8 { + exponent := uint8(0) + microstepsShifted := microsteps >> 1 + + for microstepsShifted > 0 { + microstepsShifted = microstepsShifted >> 1 + exponent++ + } + + SetMicrostepsPerStepPowerOfTwo(exponent) + return exponent +} + +func SetMicrostepsPerStepPowerOfTwo(exponent uint8) { + switch exponent { + case 0: + // Set MRES_001 + case 1: + // Set MRES_002 + case 2: + // Set MRES_004 + case 3: + // Set MRES_008 + case 4: + // Set MRES_016 + case 5: + // Set MRES_032 + case 6: + // Set MRES_064 + case 7: + // Set MRES_128 + default: + // Set MRES_256 + } +} diff --git a/tmc2209/stealthchop.go b/tmc2209/stealthchop.go new file mode 100644 index 000000000..b9e1fe31d --- /dev/null +++ b/tmc2209/stealthchop.go @@ -0,0 +1,25 @@ +package tmc2209 + +func EnableStealthChop() { + // Set StealthChop enabled in the global config register +} + +func DisableStealthChop() { + // Set StealthChop disabled in the global config register +} + +func EnableCoolStep(lowerThreshold, upperThreshold uint8) { + // Enable CoolStep with specified thresholds +} + +func DisableCoolStep() { + // Disable CoolStep feature +} + +func EnableAutomaticCurrentScaling() { + // Enable Automatic Current Scaling +} + +func DisableAutomaticCurrentScaling() { + // Disable Automatic Current Scaling +} diff --git a/tmc2209/tmc2209.go b/tmc2209/tmc2209.go new file mode 100644 index 000000000..c00ec26c9 --- /dev/null +++ b/tmc2209/tmc2209.go @@ -0,0 +1,56 @@ +//go:build tinygo + +package tmc2209 + +import ( + "log" +) + +// TMC2209 represents a single TMC2209 stepper motor driver on the communication line. +type TMC2209 struct { + comm RegisterComm + address uint8 +} + +// NewTMC2209 creates a new instance of the TMC2209 driver for a specific address. +func NewTMC2209(comm RegisterComm, address uint8) *TMC2209 { + return &TMC2209{ + comm: comm, + address: address, + } +} + +// Setup initializes the communication interface with the TMC2209. +func (driver *TMC2209) Setup() error { + // Check if comm is of type *UARTComm + if uartComm, ok := driver.comm.(*UARTComm); ok { + // Call Setup only if comm is a *UARTComm + err := uartComm.Setup() + if err != nil { + return CustomError("Failed to setup UART communication: " + err.Error()) + } + } else { + // If it's not a UARTComm, log that it's using a different communication method + log.Println("Using a non-UART communication method") + } + + return nil +} + +// WriteRegister sends a register write command to the TMC2209. +func (driver *TMC2209) WriteRegister(reg uint8, value uint32) error { + if driver.comm == nil { + return CustomError("communication interface not set") + } + // Use the communication interface (RegisterComm) to write the register + return driver.comm.WriteRegister(reg, value, driver.address) +} + +// ReadRegister sends a register read command to the TMC2209 and returns the read value. +func (driver *TMC2209) ReadRegister(reg uint8) (uint32, error) { + if driver.comm == nil { + return 0, CustomError("communication interface not set") + } + // Use the communication interface (RegisterComm) to read the register + return driver.comm.ReadRegister(reg, driver.address) +} diff --git a/tmc2209/uartcomm.go b/tmc2209/uartcomm.go new file mode 100644 index 000000000..e99947fca --- /dev/null +++ b/tmc2209/uartcomm.go @@ -0,0 +1,120 @@ +//go:build tinygo + +package tmc2209 + +import ( + "machine" + "time" +) + +// CustomError is a lightweight error type used for TinyGo compatibility. +type CustomError string + +func (e CustomError) Error() string { + return string(e) +} + +// UARTComm implements RegisterComm for UART-based communication +type UARTComm struct { + uart machine.UART + address uint8 +} + +// NewUARTComm creates a new UARTComm instance. +func NewUARTComm(uart machine.UART, address uint8) *UARTComm { + return &UARTComm{ + uart: uart, + address: address, + } +} + +// Setup initializes the UART communication with the TMC2209. +func (comm *UARTComm) Setup() error { + // Check if UART is initialized + if comm.uart == (machine.UART{}) { + return CustomError("UART not initialized") + } + + // Configure the UART interface with the desired baud rate and settings + err := comm.uart.Configure(machine.UARTConfig{ + BaudRate: 115200, + }) + if err != nil { + return CustomError("Failed to configure UART") + } + + // No built-in timeout in TinyGo, so timeout will be handled in the read/write methods + return nil +} + +// WriteRegister sends a register write command to the TMC2209 with a timeout. +func (comm *UARTComm) WriteRegister(register uint8, value uint32, driverIndex uint8) error { + buffer := []byte{ + 0x05, // Sync byte + comm.address, // Slave address + register | 0x80, // Write command (set MSB to 1 for write) + byte((value >> 24) & 0xFF), // MSB of value + byte((value >> 16) & 0xFF), // Middle byte + byte((value >> 8) & 0xFF), // Next byte + byte(value & 0xFF), // LSB of value + } + + // Calculate checksum by XORing all bytes + checksum := byte(0) + for _, b := range buffer[:7] { + checksum ^= b + } + buffer[7] = checksum // Set checksum byte + + // Write the data to the TMC2209 + done := make(chan error, 1) + + go func() { + comm.uart.Write(buffer) + done <- nil + }() + + // Implementing timeout using a 100ms timer + select { + case err := <-done: + return err + case <-time.After(100 * time.Millisecond): // Timeout after 100ms + return CustomError("write timeout") + } +} + +// ReadRegister sends a register read command to the TMC2209 with a timeout. +func (comm *UARTComm) ReadRegister(register uint8, driverIndex uint8) (uint32, error) { + var writeBuffer [4]byte + writeBuffer[0] = 0x05 // Sync byte + writeBuffer[1] = 0x00 // Slave address + writeBuffer[2] = register & 0x7F // Read command (MSB clear for read) + writeBuffer[3] = writeBuffer[0] ^ writeBuffer[1] ^ writeBuffer[2] // Checksum + + // Send the read command + done := make(chan []byte, 1) + go func() { + comm.uart.Write(writeBuffer[:]) + readBuffer := make([]byte, 8) + comm.uart.Read(readBuffer) + done <- readBuffer + }() + + // Implementing timeout using a 100ms timer + select { + case readBuffer := <-done: + // Validate checksum + checksum := byte(0) + for i := 0; i < 7; i++ { + checksum ^= readBuffer[i] + } + if checksum != readBuffer[7] { + return 0, CustomError("checksum error") + } + + // Return the value from the register + return uint32(readBuffer[3])<<24 | uint32(readBuffer[4])<<16 | uint32(readBuffer[5])<<8 | uint32(readBuffer[6]), nil + case <-time.After(100 * time.Millisecond): // Timeout after 100ms + return 0, CustomError("read timeout") + } +} diff --git a/tmc2209/utils.go b/tmc2209/utils.go new file mode 100644 index 000000000..4aca83229 --- /dev/null +++ b/tmc2209/utils.go @@ -0,0 +1,73 @@ +package tmc2209 + +import "log" + +func CalculateCRC(data []byte) uint8 { + crc := uint8(0) + for _, byte := range data { + for i := 0; i < 8; i++ { + if (crc>>7)^(byte&0x01) == 1 { + crc = (crc << 1) ^ 0x07 + } else { + crc = crc << 1 + } + byte >>= 1 + } + } + return crc +} + +// VerifyCommunication checks the communication with the TMC2209 by reading the version register (IOIN). +// It returns true if the communication is successful (i.e., the version matches the expected version). +// VerifyCommunication verifies the communication with the TMC2209 by reading the version register (IOIN). +// It explicitly resets the struct and de-references it after the check to ensure memory is managed manually. +func VerifyCommunication(comm RegisterComm, driverIndex uint8) bool { + var io *Ioin + if io == nil { + io = NewIoin() // Initialize the struct if not already initialized + } else { + *io = Ioin{} + } + _, err := ReadRegister(comm, driverIndex, io.GetAddress()) + if err != nil { + return false + } + if io.Version == expectedVersion { + io = nil + return true + } + io = nil + return false +} + +// CheckErrorStatus verifies the communication and checks for error flags in the TMC2209 driver status. +// It explicitly resets the struct and de-references it when done to ensure memory is managed manually. +func CheckErrorStatus(comm RegisterComm, driverIndex uint8) bool { + var d *DrvStatus + if d == nil { + d = NewDrvStatus() + } else { + *d = DrvStatus{} + } + _, err := d.Read(comm, driverIndex) + if err != nil { + return false + } + errorFlags := d.Ola | d.S2vsa | d.S2vsb | d.Ot | d.S2ga | d.S2gb | d.Olb + if errorFlags != 0 { + log.Printf("TMC2209 Error Detected: %X", errorFlags) + return false + } + d = nil + return true +} + +// GetInterfaceTransmissionCount reads the IFCNT register to check for UART transmission status +func GetInterfaceTransmissionCount(comm RegisterComm, driverIndex uint8) (uint32, error) { + ifcnt := NewIfcnt() + _, err := ReadRegister(comm, driverIndex, ifcnt.GetAddress()) + if err != nil { + return 0, err + } + return ifcnt.Bytes, nil +} diff --git a/tmc2209/velocity.go b/tmc2209/velocity.go new file mode 100644 index 000000000..d3c1bb6e1 --- /dev/null +++ b/tmc2209/velocity.go @@ -0,0 +1,12 @@ +package tmc2209 + +func MoveAtVelocity(microstepsPerPeriod int32) { + // Write velocity to the relevant register +} +func MoveUsingStepDirInterface() { + // Set the interface to use step/direction interface +} +func GetVelocity() uint32 { + // Read the actual velocity register + return 0 // Placeholder return value +}