From c8071bab9f526ffe614c9ceb25d5d14e74e7fda5 Mon Sep 17 00:00:00 2001 From: hkeni Date: Mon, 9 Dec 2024 09:41:07 -0500 Subject: [PATCH 01/10] Added TMC5160 support --- go.mod | 9 +- go.sum | 8 +- tmc5160/README.MD | 201 ++++ tmc5160/SPIcomm.go | 134 +++ tmc5160/address.go | 82 ++ tmc5160/func_test.go | 61 ++ tmc5160/helpers.go | 56 ++ tmc5160/registers.go | 2080 ++++++++++++++++++++++++++++++++++++++++++ tmc5160/stepper.go | 105 +++ tmc5160/tmc5160.go | 194 ++++ tmc5160/uartcomm.go | 115 +++ tmc5160/utils.go | 13 + 12 files changed, 3053 insertions(+), 5 deletions(-) create mode 100644 tmc5160/README.MD create mode 100644 tmc5160/SPIcomm.go create mode 100644 tmc5160/address.go create mode 100644 tmc5160/func_test.go create mode 100644 tmc5160/helpers.go create mode 100644 tmc5160/registers.go create mode 100644 tmc5160/stepper.go create mode 100644 tmc5160/tmc5160.go create mode 100644 tmc5160/uartcomm.go create mode 100644 tmc5160/utils.go diff --git a/go.mod b/go.mod index 0fa30762a..fc1dc70b6 100644 --- a/go.mod +++ b/go.mod @@ -1,20 +1,23 @@ module tinygo.org/x/drivers -go 1.18 +go 1.22.1 + +toolchain go1.23.3 require ( github.com/eclipse/paho.mqtt.golang v1.2.0 github.com/frankban/quicktest v1.10.2 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 + github.com/orsinium-labs/tinymath v1.1.0 github.com/soypat/natiu-mqtt v0.5.1 + golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d golang.org/x/net v0.7.0 tinygo.org/x/tinyfont v0.3.0 tinygo.org/x/tinyterm v0.1.0 ) require ( - github.com/google/go-cmp v0.5.2 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/kr/pretty v0.2.1 // indirect github.com/kr/text v0.1.0 // indirect - golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect ) diff --git a/go.sum b/go.sum index 7f866dd02..0d18cdf7c 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,9 @@ github.com/eclipse/paho.mqtt.golang v1.2.0 h1:1F8mhG9+aO5/xpdtFkW4SxOJB67ukuDC3t github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= github.com/frankban/quicktest v1.10.2 h1:19ARM85nVi4xH7xPXuc5eM/udya5ieh7b/Sv+d844Tk= github.com/frankban/quicktest v1.10.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= @@ -12,12 +13,15 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/orsinium-labs/tinymath v1.1.0 h1:KomdsyLHB7vE3f1nRAJF2dyf1m/gnM2HxfTeV1vS5UA= +github.com/orsinium-labs/tinymath v1.1.0/go.mod h1:WPXX6ei3KSXG7JfA03a+ekCYaY9SWN4I+JRl2p6ck+A= github.com/soypat/natiu-mqtt v0.5.1 h1:rwaDmlvjzD2+3MCOjMZc4QEkDkNwDzbct2TJbpz+TPc= github.com/soypat/natiu-mqtt v0.5.1/go.mod h1:xEta+cwop9izVCW7xOx2W+ct9PRMqr0gNVkvBPnQTc4= github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= +golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0= +golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= tinygo.org/x/drivers v0.14.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI= tinygo.org/x/drivers v0.15.1/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI= diff --git a/tmc5160/README.MD b/tmc5160/README.MD new file mode 100644 index 000000000..a8b4fdb20 --- /dev/null +++ b/tmc5160/README.MD @@ -0,0 +1,201 @@ +# TMC5160 Driver for Go (TinyGo) + +This repository provides a Go-based driver for the **TMC5160** stepper motor driver, implemented for both **SPI** and **UART** communication modes. The driver allows you to easily interface with the TMC5160 to configure and control stepper motors. + +## Table of Contents + +- [Installation](#installation) +- [Communication Modes](#communication-modes) + - [SPI Mode](#spi-mode) + - [UART Mode](#uart-mode) +- [Usage Example](#usage-example) + - [Setting and Getting Modes](#setting-and-getting-modes) + - [Reading and Writing Registers](#reading-and-writing-registers) +- [API Reference](#api-reference) +- [License](#license) + +## Installation + +To use the TMC5160 driver, you'll need to have **TinyGo** installed. You can install TinyGo by following the [official installation guide](https://tinygo.org/getting-started/). + +### Dependencies + +- **machine**: To interface with hardware on platforms like Raspberry Pi, STM32, etc. +- **TinyGo**: A Go compiler for embedded systems. + +Add the module + +```bash +go get github.com/amken3d/tinygo_tmc5160/tmc5160 +``` +Alternate method is to use the tinygo official drivers repo + +```aiignore +import "tinygo.org/x/drivers/tmc5160" +``` +### Communication Modes + +The TMC5160 supports two communication modes for controlling the motor: + +**SPI Mode** + +To communicate with the TMC5160 in SPI mode, you'll need to configure the SPI bus and the chip-select (CS) pin. This allows full-speed communication between your microcontroller and the TMC5160. +SPI Setup + +In SPI mode, you must configure the SPI interface on your microcontroller. Here's how to set up SPI communication for the TMC5160. + +```go +spi := machine.SPI1 +csPin := machine.GPIO13 +spi.Configure(machine.SPIConfig{ +SCK: machine.GPIO10, +SDI: machine.GPIO11, +SDO: machine.GPIO12, +Frequency: 5000000, +Mode: 3, +LSBFirst: false, +}) + +csPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) +``` +**Sending Commands via SPI** + +The driver supports reading and writing registers using the SPIComm interface, which is initialized with the configured SPI bus and CS pins + +```go +comm := tmc5160.NewSPIComm(*spi, csPins) +driver := tmc5160.NewTMC5160(comm, driverIndex) +driver.WriteRegister(tmc5160.GCONF, value) + +``` + +**UART Mode** + +Alternatively, you can use UART mode to communicate with the TMC5160. UART mode is useful for cases where SPI is not available or when the TMC5160 is used in multi-driver configurations with limited SPI pins. +UART Setup + +In UART mode, you will need to configure the UART interface with the appropriate baud rate and settings: + +```go +uart := machine.UART0 +uart.Configure(machine.UARTConfig{ + BaudRate: 115200, +}) +``` +#### Sending Commands via UART + +The UART communication is handled through the UARTComm struct, which wraps the UART interface. + +```go +comm := tmc5160.NewUARTComm(uart, 0x01) +driver := tmc5160.NewTMC5160(comm, 0) +driver.WriteRegister(tmc5160.GCONF, 0x01) +``` + +## Usage Example + +Here’s a simple example of how to use the TMC5160 driver with SPI and UART modes: + +```aiignore +package main + +import ( + "fmt" + "machine" + "time" + "tmc5160" +) + +func main() { + // SPI setup + spi := machine.SPI1 + csPin := machine.GPIO13 + spi.Configure(machine.SPIConfig{ + SCK: machine.GPIO10, + SDI: machine.GPIO11, + SDO: machine.GPIO12, + Frequency: 5000000, + Mode: 3, + LSBFirst: false, + }) + + csPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) + csPins := map[uint8]machine.Pin{0: csPin} + + comm := tmc5160.NewSPIComm(*spi, csPins) + driver := tmc5160.NewTMC5160(comm, 0) + + // Setting and getting mode + rampMode := tmc5160.NewRAMPMODE(comm) + rampMode.SetMode(tmc5160.PositioningMode) + mode, err := rampMode.GetMode() + if err != nil { + fmt.Println("Error getting mode:", err) + } else { + fmt.Println("Current Mode:", mode) + } + + // Read GCONF register + GCONF := tmc5160.NewGCONF() + gconfVal, err := driver.ReadRegister(tmc5160.GCONF) + GCONF.Unpack(gconfVal) + fmt.Println("GCONF:", GCONF) +} + +``` +## Reading and Writing Registers + +You can easily read and write registers using the WriteRegister and ReadRegister methods: + +```aiignore +// Write a value to a register +err := driver.WriteRegister(tmc5160.GCONF, 0x01) +if err != nil { + fmt.Println("Error writing register:", err) +} + +// Read a register +value, err := driver.ReadRegister(tmc5160.GCONF) +if err != nil { + fmt.Println("Error reading register:", err) +} else { + fmt.Println("Read value from GCONF:", value) +} + +``` + +## API Reference + + NewSPIComm(spi machine.SPI, csPins map[uint8]machine.Pin) *SPIComm + +Creates a new SPI communication interface for the TMC5160. + + NewUARTComm(uart machine.UART, address uint8) *UARTComm + +Creates a new UART communication interface for the TMC5160. + + NewTMC5160(comm RegisterComm, address uint8) *TMC5160 + +Creates a new instance of the TMC5160 driver. + + WriteRegister(register uint8, value uint32) error + +Writes a value to the specified register. + + ReadRegister(register uint8) (uint32, error) + +Reads a value from the specified register. + +## License + +This project is licensed under the MIT License - see the LICENSE file for details. + + +### Key Sections: + +1. **Installation**: Explains how to install TinyGo and clone the repository. +2. **Communication Modes**: Describes how to set up SPI and UART communication with the TMC5160. +3. **Usage Example**: Provides an example of how to use the driver, including how to set modes and read/write registers. +4. **API Reference**: Lists functions available in the TMC5160 driver, such as `WriteRegister`, `ReadRegister`, and constructors like `NewSPIComm`. + +This README provides a comprehensive overview and guide on how to use the TMC5160 driver in both SPI and UART communication modes. diff --git a/tmc5160/SPIcomm.go b/tmc5160/SPIcomm.go new file mode 100644 index 000000000..34bae0640 --- /dev/null +++ b/tmc5160/SPIcomm.go @@ -0,0 +1,134 @@ +//go:build tinygo + +package tmc5160 + +import ( + "machine" + "time" +) + +// CustomError is a lightweight error type used for TinyGo compatibility. +type CustomError string + +func (e CustomError) Error() string { + return string(e) +} + +// SPIComm implements RegisterComm for SPI-based communication +type SPIComm struct { + spi machine.SPI + CsPins map[uint8]machine.Pin // Map to store CS pin for each Driver by its address +} + +// NewSPIComm creates a new SPIComm instance. +func NewSPIComm(spi machine.SPI, csPins map[uint8]machine.Pin) *SPIComm { + return &SPIComm{ + spi: spi, + CsPins: csPins, + } +} + +// Setup initializes the SPI communication with the Driver and configures all CS pins. +func (comm *SPIComm) Setup() error { + // Check if SPI is initialized + if comm.spi == (machine.SPI{}) { + return CustomError("SPI not initialized") + } + + // Configure all CS pins (make them output and set them high) + for _, csPin := range comm.CsPins { + csPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) + csPin.High() // Set all CS pins high initially + } + + // Configure the SPI interface with the desired settings + err := comm.spi.Configure(machine.SPIConfig{ + LSBFirst: false, + Mode: 3, + }) + if err != nil { + return CustomError("Failed to configure SPI") + } + + return nil +} + +// WriteRegister sends a register write command to the TMC5160. +func (comm *SPIComm) WriteRegister(register uint8, value uint32, driverAddress uint8) error { + // Assert the chip select pin (set CS low to start communication) + csPin, exists := comm.CsPins[driverAddress] + if !exists { + return CustomError("Invalid driver address") + } + csPin.Low() + + // Set the register address with WRITE_ACCESS (0x80) + addressWithWriteAccess := register | 0x80 + + // Send the address and the data to write (split into 4 bytes) + _, err := spiTransfer40(&comm.spi, addressWithWriteAccess, value) + if err != nil { + csPin.High() + return CustomError("Failed to write register") + } + + // Deassert the chip select pin (set CS high to end communication) + csPin.High() + + return nil +} + +// ReadRegister sends a register read command to the TMC5160. +func (comm *SPIComm) ReadRegister(register uint8, driverAddress uint8) (uint32, error) { + // Assert the chip select pin (set CS low to start communication) + csPin, exists := comm.CsPins[driverAddress] + if !exists { + return 0, CustomError("Invalid driver address") + } + csPin.Low() + + // Step 1: Send a dummy write operation to begin the read sequence + _, err := spiTransfer40(&comm.spi, register, 0x00) // Send dummy data + if err != nil { + csPin.High() + return 0, CustomError("Failed to send dummy write") + } + csPin.High() + time.Sleep(176 * time.Nanosecond) + csPin.Low() + // Step 2: Send the register read request again to get the actual value + response, err := spiTransfer40(&comm.spi, register, 0x00) // Send again to get actual register data + if err != nil { + csPin.High() + return 0, CustomError("Failed to read register") + } + + // Deassert the chip select pin (set CS high to end communication) + csPin.High() + + return response, nil +} + +func spiTransfer40(spi *machine.SPI, register uint8, txData uint32) (uint32, error) { + // Prepare the 5-byte buffer for transmission (1 byte address + 4 bytes data) + tx := []byte{ + register, // Address byte + byte(txData >> 24), // Upper 8 bits of data + byte(txData >> 16), // Middle 8 bits of data + byte(txData >> 8), // Next 8 bits of data + byte(txData), // Lower 8 bits of data + } + //println("Sending", tx[0], tx[1], tx[2], tx[3], tx[4]) + rx := make([]byte, 5) + + // Perform the SPI transaction + err := spi.Tx(tx, rx) + if err != nil { + return 0, err + } + //println("Received", rx[0], rx[1], rx[2], rx[3], rx[4]) + // Combine the received bytes into a 32-bit response, ignore the address byte + rxData := uint32(rx[1])<<24 | uint32(rx[2])<<16 | uint32(rx[3])<<8 | uint32(rx[4]) + + return rxData, nil +} diff --git a/tmc5160/address.go b/tmc5160/address.go new file mode 100644 index 000000000..473626c27 --- /dev/null +++ b/tmc5160/address.go @@ -0,0 +1,82 @@ +package tmc5160 + +// Driver Register addresses +const ( + GCONF uint8 = 0x00 // Global configuration flags + GSTAT uint8 = 0x01 // Global status flags + IFCNT = 0x02 // UART transmission counter + SLAVECONF = 0x03 // UART slave configuration + IOIN = 0x04 // Read input / write output pins + X_COMPARE = 0x05 // Position comparison register + OTP_PROG = 0x06 // OTP programming register + OTP_READ = 0x07 // OTP read register + FACTORY_CONF = 0x08 // Factory configuration (clock trim) + SHORT_CONF = 0x09 // Short detector configuration + DRV_CONF = 0x0A // Driver configuration + GLOBAL_SCALER = 0x0B // Global scaling of motor current + OFFSET_READ = 0x0C // Offset calibration results + + /* Velocity dependent driver feature control registers */ + IHOLD_IRUN = 0x10 // Driver current control + TPOWERDOWN = 0x11 // Delay before power down + TSTEP = 0x12 // Actual time between microsteps + TPWMTHRS = 0x13 // Upper velocity for stealthChop voltage PWM mode + TCOOLTHRS = 0x14 // Lower threshold velocity for switching on smart energy coolStep and stallGuard feature + THIGH = 0x15 // Velocity threshold for switching into a different chopper mode and fullstepping + + /* Ramp generator motion control registers */ + RAMPMODE = 0x20 // Driving mode (Velocity, Positioning, Hold) + XACTUAL = 0x21 // Actual motor position + VACTUAL = 0x22 // Actual motor velocity from ramp generator + VSTART = 0x23 // Motor start velocity + A_1 = 0x24 // First acceleration between VSTART and V1 + V_1 = 0x25 // First acceleration/deceleration phase target velocity + AMAX = 0x26 // Second acceleration between V1 and VMAX + VMAX = 0x27 // Target velocity in velocity mode + DMAX = 0x28 // Deceleration between VMAX and V1 + D_1 = 0x2A // Deceleration between V1 and VSTOP + //Attention: Do not set 0 in positioning mode, even if V1=0! + VSTOP = 0x2B // Motor stop velocity + //Attention: Set VSTOP > VSTART! + //Attention: Do not set 0 in positioning mode, minimum 10 recommend! + TZEROWAIT = 0x2C // Waiting time after ramping down to zero velocity before next movement or direction inversion can start. + XTARGET = 0x2D // Target position for ramp mode + + /* Ramp generator driver feature control registers */ + VDCMIN = 0x33 // Velocity threshold for enabling automatic commutation dcStep + SW_MODE = 0x34 // Switch mode configuration + RAMP_STAT = 0x35 // Ramp status and switch event status + XLATCH = 0x36 // Ramp generator latch position upon programmable switch event + + /* Encoder registers */ + ENCMODE = 0x38 // Encoder configuration and use of N channel + X_ENC = 0x39 // Actual encoder position + ENC_CONST = 0x3A // Accumulation constant + ENC_STATUS = 0x3B // Encoder status information + ENC_LATCH = 0x3C // Encoder position latched on N event + ENC_DEVIATION = 0x3D // Maximum number of steps deviation between encoder counter and XACTUAL for deviation warning + + /* Motor driver registers */ + MSLUT0 uint8 = 0x60 // 32 bits + MSLUT1 uint8 = 0x61 // 32 bits + MSLUT2 uint8 = 0x62 // 32 bits + MSLUT3 uint8 = 0x63 // 32 bits + MSLUT4 uint8 = 0x64 // 32 bits + MSLUT5 uint8 = 0x65 // 32 bits + MSLUT6 uint8 = 0x66 // 32 bits + MSLUT7 uint8 = 0x67 // 32 bits + MSLUTSEL = 0x68 // Look up table segmentation definition + MSLUTSTART = 0x69 // Absolute current at microstep table entries 0 and 256 + MSCNT = 0x6A // Actual position in the microstep table + MSCURACT = 0x6B // Actual microstep current + CHOPCONF = 0x6C // Chopper and driver configuration + COOLCONF = 0x6D // coolStep smart current control register and stallGuard2 configuration + DCCTRL = 0x6E // dcStep automatic commutation configuration register + DRV_STATUS = 0x6F // stallGuard2 value and driver error flags + PWMCONF = 0x70 // stealthChop voltage PWM mode chopper configuration + PWM_SCALE = 0x71 // Results of stealthChop amplitude regulator. + PWM_AUTO = 0x72 // Automatically determined PWM config values + LOST_STEPS = 0x73 // Number of input steps skipped due to dcStep. only with SD_MODE = 1 + expectedVersion = 0x03 + DEFAULT_F_CLK = 12000000 +) diff --git a/tmc5160/func_test.go b/tmc5160/func_test.go new file mode 100644 index 000000000..72a1450a2 --- /dev/null +++ b/tmc5160/func_test.go @@ -0,0 +1,61 @@ +//go:build test + +package tmc5160 + +import ( + "log" + "testing" +) + +func TestCurrentVelocityToVMAX(t *testing.T) { + stepper := NewDefaultStepper() + log.Printf("Current Velocity = %f", stepper.VelocitySPS) + // Expected output based on the formula + expectedVMAX := 1398 + + result := stepper.CurrentVelocityToVMAX() + + if result != uint32(expectedVMAX) { + t.Errorf("CurrentVelocityToVMAX() = %d; expected %v", result, expectedVMAX) + } +} + +func TestDesiredVelocityToVMAX(t *testing.T) { + stepper := NewDefaultStepper() + + // Test with a desired velocity of 1500 steps per second + desiredVelocity := float32(51200.0) + expectedVMAX := 71583 + + result := stepper.DesiredVelocityToVMAX(desiredVelocity) + + if result != uint32(expectedVMAX) { + t.Errorf("DesiredVelocityToVMAX() = %d; expected %d", result, expectedVMAX) + } +} + +func TestDesiredAccelToAMAX(t *testing.T) { + stepper := NewDefaultStepper() + + // Test desired velocity 1000 steps per second, and desired acceleration 1.5 sec from 0 to VMAX + dVel := float32(51200.0) + dAcc := float32(1.50) + expectedAMAX := 521 + result := stepper.DesiredAccelToAMAX(dAcc, dVel) + if result != uint32(expectedAMAX) { + t.Errorf("DesiredAccelToAMAX() = %d; expected %d", result, expectedAMAX) + } +} + +func TestVMAXToTSTEP(t *testing.T) { + stepper := NewDefaultStepper() + // Test with a threshold speed of 1000 Hz + thrsSpeed := uint32(5145) + expectedTSTEP := uint32(204) + + result := stepper.VMAXToTSTEP(thrsSpeed) + + if result != (expectedTSTEP) { + t.Errorf("VMAXToTSTEP() = %d; expected %d", result, expectedTSTEP) + } +} diff --git a/tmc5160/helpers.go b/tmc5160/helpers.go new file mode 100644 index 000000000..3ffa1da4b --- /dev/null +++ b/tmc5160/helpers.go @@ -0,0 +1,56 @@ +package tmc5160 + +import ( + "github.com/orsinium-labs/tinymath" + "golang.org/x/exp/constraints" +) + +// VelocityToVMAX calculates the VMAX register value from the current stepper velocity which is in microsteps per tRef (i.e 1/clock speed) +func (stepper *Stepper) CurrentVelocityToVMAX() uint32 { + tref := float32(16777216) / (float32(stepper.Fclk) * 1000000) + r := stepper.VelocitySPS * stepper.GearRatio * float32(tref) + return constrain(uint32(r), 0, maxVMAX) // VMAX register value cannot exceed maxVMAX +} +func (stepper *Stepper) DesiredVelocityToVMAX(v float32) uint32 { + tref := 16777216 / (float32(stepper.Fclk) * 1000000) + r := tinymath.Round(v * stepper.GearRatio * tref) + return constrain(uint32(r), 0, maxVMAX) // VMAX register value cannot exceed maxVMAX +} + +func (stepper *Stepper) DesiredAccelToAMAX(dacc float32, dVel float32) uint32 { + dVelToVMAX := stepper.DesiredVelocityToVMAX(dVel) + _a := uint64(dVelToVMAX) * 131072 + _b := float32(_a) / dacc + _c := _b / float32(uint32(stepper.Fclk)*1000000) + return uint32(_c) + +} + +// Convert threshold speed (Hz) to internal TSTEP value +func (stepper *Stepper) DesiredSpeedToTSTEP(thrsSpeed uint32) uint32 { + if thrsSpeed < 0 { + return 0 + } + _a := stepper.DesiredVelocityToVMAX(float32(thrsSpeed)) + _b := float32(16777216 / _a) + _c := float32(stepper.MSteps) / float32(256) + _d := uint32(_b * _c) + return constrain(_d, 0, 1048575) +} + +func (stepper *Stepper) VMAXToTSTEP(vmax uint32) uint32 { + _b := float32(16777216 / vmax) + _c := float32(stepper.MSteps) / float32(256) + _d := tinymath.Round(_b * _c) + return constrain(uint32(_d), 0, 1048575) +} + +// Constrain function to limit values to a specific range (supports multiple types). +func constrain[T constraints.Ordered](value, min, max T) T { + if value < min { + return min + } else if value > max { + return max + } + return value +} diff --git a/tmc5160/registers.go b/tmc5160/registers.go new file mode 100644 index 000000000..d4d13a1ec --- /dev/null +++ b/tmc5160/registers.go @@ -0,0 +1,2080 @@ +package tmc5160 + +import ( + math "github.com/orsinium-labs/tinymath" +) + +// RegisterComm defines an interface for reading from and writing to hardware registers. +type RegisterComm interface { + ReadRegister(register uint8, driverIndex uint8) (uint32, error) + WriteRegister(register uint8, value uint32, driverIndex uint8) error +} + +// 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) + 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) +} + +// Register and methods to pack and unpack +// Base Register struct +type Register struct { + RegisterAddr uint8 + Bytes uint32 +} + +// Common New function for creating a new register instance +func NewRegister(addr uint8) *Register { + return &Register{ + RegisterAddr: addr, + } +} + +// Common Pack method: for subclasses to implement their packing logic +func (r *Register) Pack() uint32 { + return r.Bytes // Default, should be overridden in register-specific structs +} + +// Common Unpack method: for subclasses to implement their unpacking logic +func (r *Register) Unpack(registerValue uint32) { + r.Bytes = registerValue // Default, should be overridden in register-specific structs +} + +// Common GetAddress method +func (r *Register) GetAddress() uint8 { + return r.RegisterAddr +} + +// Common Read method (assuming the communication interface is implemented) +func (r *Register) Read(comm RegisterComm, driverIndex uint8) (uint32, error) { + return ReadRegister(comm, driverIndex, r.RegisterAddr) +} + +// Common Write method +func (r *Register) Write(comm RegisterComm, driverIndex uint8, value uint32) error { + return WriteRegister(comm, r.RegisterAddr, driverIndex, value) +} + +// GCONF Register bit fields' masks and shifts +const ( + // Recalibrate: Zero crossing recalibration during driver disable + GCONF_Recalibrate_Mask = 1 << 0 + // Faststandstill: Timeout for step execution until standstill detection + GCONF_Faststandstill_Mask = 1 << 1 + // Enable PWM mode for StealthChop + GCONF_EnPwmMode_Mask = 1 << 2 + // Enable step input filtering for StealthChop optimization + GCONF_MultistepFilt_Mask = 1 << 3 + // Motor direction + GCONF_Shaft_Mask = 1 << 4 + // Error flags on DIAG0 pin + GCONF_Diag0Error_Mask = 1 << 5 + // Enable DIAG0 for Over temperature warning + GCONF_Diag0Otpw_Mask = 1 << 6 + // Enable DIAG0 for stall step detection + GCONF_Diag0StallStep_Mask = 1 << 7 + // Enable DIAG1 for stall direction + GCONF_Diag1StallDir_Mask = 1 << 8 + // Enable DIAG1 for index position + GCONF_Diag1Index_Mask = 1 << 9 + // Enable DIAG1 for chopper on state + GCONF_Diag1Onstate_Mask = 1 << 10 + // Enable DIAG1 for skipped steps + GCONF_Diag1StepsSkipped_Mask = 1 << 11 + // Enable DIAG0 push-pull output + GCONF_Diag0IntPushPull_Mask = 1 << 12 + // Enable DIAG1 push-pull output + GCONF_Diag1PosCompPushPull_Mask = 1 << 13 + // Small hysteresis for step frequency comparison + GCONF_SmallHysteresis_Mask = 1 << 14 + // Enable emergency stop + GCONF_StopEnable_Mask = 1 << 15 + // Direct motor coil current and polarity control + GCONF_DirectMode_Mask = 1 << 16 + // Test mode (not for normal use) + GCONF_TestMode_Mask = 1 << 17 +) + +// GCONF Register structure +type GCONF_Register struct { + Register + // Fields corresponding to individual settings in GCONF register + Recalibrate bool + Faststandstill bool + EnPwmMode bool + MultistepFilt bool + Shaft bool + Diag0Error bool + Diag0Otpw bool + Diag0StallStep bool + Diag1StallDir bool + Diag1Index bool + Diag1Onstate bool + Diag1StepsSkipped bool + Diag0IntPushPull bool + Diag1PosCompPushPull bool + SmallHysteresis bool + StopEnable bool + DirectMode bool + TestMode bool +} + +// NewGCONF initializes a new GCONF register instance +func NewGCONF() *GCONF_Register { + return &GCONF_Register{ + Register: Register{ + RegisterAddr: GCONF, // GSTAT register address + }, + } +} + +// Pack the fields into a single 32-bit register value +func (g *GCONF_Register) Pack() uint32 { + var registerValue uint32 + + // Use bitwise OR to set individual bits based on the field values + if g.Recalibrate { + registerValue |= GCONF_Recalibrate_Mask + } + if g.Faststandstill { + registerValue |= GCONF_Faststandstill_Mask + } + if g.EnPwmMode { + registerValue |= GCONF_EnPwmMode_Mask + } + if g.MultistepFilt { + registerValue |= GCONF_MultistepFilt_Mask + } + if g.Shaft { + registerValue |= GCONF_Shaft_Mask + } + if g.Diag0Error { + registerValue |= GCONF_Diag0Error_Mask + } + if g.Diag0Otpw { + registerValue |= GCONF_Diag0Otpw_Mask + } + if g.Diag0StallStep { + registerValue |= GCONF_Diag0StallStep_Mask + } + if g.Diag1StallDir { + registerValue |= GCONF_Diag1StallDir_Mask + } + if g.Diag1Index { + registerValue |= GCONF_Diag1Index_Mask + } + if g.Diag1Onstate { + registerValue |= GCONF_Diag1Onstate_Mask + } + if g.Diag1StepsSkipped { + registerValue |= GCONF_Diag1StepsSkipped_Mask + } + if g.Diag0IntPushPull { + registerValue |= GCONF_Diag0IntPushPull_Mask + } + if g.Diag1PosCompPushPull { + registerValue |= GCONF_Diag1PosCompPushPull_Mask + } + if g.SmallHysteresis { + registerValue |= GCONF_SmallHysteresis_Mask + } + if g.StopEnable { + registerValue |= GCONF_StopEnable_Mask + } + if g.DirectMode { + registerValue |= GCONF_DirectMode_Mask + } + if g.TestMode { + registerValue |= GCONF_TestMode_Mask + } + return registerValue +} + +// Unpack a 32-bit register value into individual fields +func (g *GCONF_Register) Unpack(registerValue uint32) { + g.Recalibrate = (registerValue & GCONF_Recalibrate_Mask) != 0 + g.Faststandstill = (registerValue & GCONF_Faststandstill_Mask) != 0 + g.EnPwmMode = (registerValue & GCONF_EnPwmMode_Mask) != 0 + g.MultistepFilt = (registerValue & GCONF_MultistepFilt_Mask) != 0 + g.Shaft = (registerValue & GCONF_Shaft_Mask) != 0 + g.Diag0Error = (registerValue & GCONF_Diag0Error_Mask) != 0 + g.Diag0Otpw = (registerValue & GCONF_Diag0Otpw_Mask) != 0 + g.Diag0StallStep = (registerValue & GCONF_Diag0StallStep_Mask) != 0 + g.Diag1StallDir = (registerValue & GCONF_Diag1StallDir_Mask) != 0 + g.Diag1Index = (registerValue & GCONF_Diag1Index_Mask) != 0 + g.Diag1Onstate = (registerValue & GCONF_Diag1Onstate_Mask) != 0 + g.Diag1StepsSkipped = (registerValue & GCONF_Diag1StepsSkipped_Mask) != 0 + g.Diag0IntPushPull = (registerValue & GCONF_Diag0IntPushPull_Mask) != 0 + g.Diag1PosCompPushPull = (registerValue & GCONF_Diag1PosCompPushPull_Mask) != 0 + g.SmallHysteresis = (registerValue & GCONF_SmallHysteresis_Mask) != 0 + g.StopEnable = (registerValue & GCONF_StopEnable_Mask) != 0 + g.DirectMode = (registerValue & GCONF_DirectMode_Mask) != 0 + g.TestMode = (registerValue & GCONF_TestMode_Mask) != 0 +} + +// Example Register: GSTAT +type GSTAT_Register struct { + Register + Reset bool + DrvErr bool + UvCp bool +} + +// NewGSTAT creates a new GSTAT register instance +func NewGSTAT() *GSTAT_Register { + return &GSTAT_Register{ + Register: Register{ + RegisterAddr: GSTAT, // GSTAT register address + }, + } +} + +// Pack method for GSTAT: overrides the base Pack +func (g *GSTAT_Register) Pack() uint32 { + var registerValue uint32 + if g.Reset { + registerValue |= 1 << 0 + } + if g.DrvErr { + registerValue |= 1 << 1 + } + if g.UvCp { + registerValue |= 1 << 2 + } + return registerValue +} + +// Unpack method for GSTAT: overrides the base Unpack +func (g *GSTAT_Register) Unpack(registerValue uint32) { + g.Reset = (registerValue & (1 << 0)) != 0 + g.DrvErr = (registerValue & (1 << 1)) != 0 + g.UvCp = (registerValue & (1 << 2)) != 0 +} + +// IOIN_Register struct to represent the IOIN register +type IOIN_Register struct { + Register + ReflStep bool + RefrDir bool + EncbDcenCfg4 bool + EncaDcinCfg5 bool + DrvEnn bool + EncNDcoCfg6 bool + SdMode bool + SwcompIn bool + Version uint8 +} + +// NewIOIN creates a new IOIN register instance +func NewIOIN() *IOIN_Register { + return &IOIN_Register{ + Register: Register{ + RegisterAddr: IOIN, + }, + } +} + +// Pack method for IOIN: overrides the base Pack +func (i *IOIN_Register) Pack() uint32 { + var registerValue uint32 + + // Set individual bits based on the field values + if i.ReflStep { + registerValue |= 1 << 0 + } + if i.RefrDir { + registerValue |= 1 << 1 + } + if i.EncbDcenCfg4 { + registerValue |= 1 << 2 + } + if i.EncaDcinCfg5 { + registerValue |= 1 << 3 + } + if i.DrvEnn { + registerValue |= 1 << 4 + } + if i.EncNDcoCfg6 { + registerValue |= 1 << 5 + } + if i.SdMode { + registerValue |= 1 << 6 + } + if i.SwcompIn { + registerValue |= 1 << 7 + } + // Handle the version field (8 bits, starting at bit 24) + registerValue |= uint32(i.Version) << 24 + + return registerValue +} + +// Unpack method for IOIN: overrides the base Unpack +func (i *IOIN_Register) Unpack(registerValue uint32) { + i.ReflStep = (registerValue & (1 << 0)) != 0 + i.RefrDir = (registerValue & (1 << 1)) != 0 + i.EncbDcenCfg4 = (registerValue & (1 << 2)) != 0 + i.EncaDcinCfg5 = (registerValue & (1 << 3)) != 0 + i.DrvEnn = (registerValue & (1 << 4)) != 0 + i.EncNDcoCfg6 = (registerValue & (1 << 5)) != 0 + i.SdMode = (registerValue & (1 << 6)) != 0 + i.SwcompIn = (registerValue & (1 << 7)) != 0 + // Extract the version field (8 bits, starting at bit 24) + i.Version = uint8((registerValue >> 24) & 0xFF) +} + +// SHORT_CONF_Register struct to represent the SHORT_CONF register +type SHORT_CONF_Register struct { + Register + S2vsLevel uint8 // Short to VS detector sensitivity (4 bits) + S2gLevel uint8 // Short to GND detector sensitivity (4 bits) + ShortFilter uint8 // Spike filtering bandwidth for short detection (2 bits) + ShortDelay bool // Short detection delay (1 bit) +} + +// NewSHORT_CONF creates a new SHORT_CONF register instance +func NewSHORT_CONF() *SHORT_CONF_Register { + return &SHORT_CONF_Register{ + Register: Register{ + RegisterAddr: SHORT_CONF, + }, + } +} + +// Pack method for SHORT_CONF: overrides the base Pack +func (s *SHORT_CONF_Register) Pack() uint32 { + var registerValue uint32 + + // Pack each field using bitwise operations + registerValue |= uint32(s.S2vsLevel&0xF) << 0 // S2vsLevel: 4 bits + registerValue |= uint32(s.S2gLevel&0xF) << 8 // S2gLevel: 4 bits + registerValue |= uint32(s.ShortFilter&0x3) << 16 // ShortFilter: 2 bits + if s.ShortDelay { + registerValue |= 1 << 18 // ShortDelay: 1 bit + } + return registerValue +} + +// Unpack method for SHORT_CONF: overrides the base Unpack +func (s *SHORT_CONF_Register) Unpack(registerValue uint32) { + // Unpack each field using bitwise operations + s.S2vsLevel = uint8((registerValue >> 0) & 0xF) // Extract 4 bits for S2vsLevel + s.S2gLevel = uint8((registerValue >> 8) & 0xF) // Extract 4 bits for S2gLevel + s.ShortFilter = uint8((registerValue >> 16) & 0x3) // Extract 2 bits for ShortFilter + s.ShortDelay = (registerValue & (1 << 18)) != 0 // Extract 1 bit for ShortDelay +} + +// DRV_CONF_Register struct to represent the DRV_CONF register +type DRV_CONF_Register struct { + Register + BBMTime uint8 // Break before make delay (5 bits) + BBMClks uint8 // Digital BBM Time in clock cycles (4 bits) + OTSelect uint8 // Over temperature level selection for bridge disable (2 bits) + DrvStrength uint8 // Gate drivers current selection (2 bits) + FiltIsense uint8 // Filter time constant of sense amplifier (2 bits) +} + +// NewDRV_CONF creates a new DRV_CONF register instance +func NewDRV_CONF() *DRV_CONF_Register { + return &DRV_CONF_Register{ + Register: Register{ + RegisterAddr: DRV_CONF, + }, + } +} + +// Pack method for DRV_CONF: overrides the base Pack +func (d *DRV_CONF_Register) Pack() uint32 { + var registerValue uint32 + + // Pack each field using bitwise operations + registerValue |= uint32(d.BBMTime&0x1F) << 0 // BBMTime: 5 bits + registerValue |= uint32(d.BBMClks&0xF) << 8 // BBMClks: 4 bits + registerValue |= uint32(d.OTSelect&0x3) << 16 // OTSelect: 2 bits + registerValue |= uint32(d.DrvStrength&0x3) << 18 // DrvStrength: 2 bits + registerValue |= uint32(d.FiltIsense&0x3) << 20 // FiltIsense: 2 bits + + return registerValue +} + +// Unpack method for DRV_CONF: overrides the base Unpack +func (d *DRV_CONF_Register) Unpack(registerValue uint32) { + // Unpack each field using bitwise operations + d.BBMTime = uint8((registerValue >> 0) & 0x1F) // Extract 5 bits for BBMTime + d.BBMClks = uint8((registerValue >> 8) & 0xF) // Extract 4 bits for BBMClks + d.OTSelect = uint8((registerValue >> 16) & 0x3) // Extract 2 bits for OTSelect + d.DrvStrength = uint8((registerValue >> 18) & 0x3) // Extract 2 bits for DrvStrength + d.FiltIsense = uint8((registerValue >> 20) & 0x3) // Extract 2 bits for FiltIsense +} + +// OFFSET_READ_Register struct to represent the OFFSET_READ register +type OFFSET_READ_Register struct { + Register + PhaseB uint8 // Phase B offset (8 bits) + PhaseA uint8 // Phase A offset (8 bits) +} + +// NewOFFSET_READ creates a new OFFSET_READ register instance +func NewOFFSET_READ() *OFFSET_READ_Register { + return &OFFSET_READ_Register{ + Register: Register{ + RegisterAddr: OFFSET_READ, + }, + } +} + +// Pack method for OFFSET_READ: overrides the base Pack +func (o *OFFSET_READ_Register) Pack() uint32 { + var registerValue uint32 + + // Pack each field using bitwise operations + registerValue |= uint32(o.PhaseB&0xFF) << 0 // PhaseB: 8 bits + registerValue |= uint32(o.PhaseA&0xFF) << 8 // PhaseA: 8 bits + + return registerValue +} + +// Unpack method for OFFSET_READ: overrides the base Unpack +func (o *OFFSET_READ_Register) Unpack(registerValue uint32) { + // Unpack each field using bitwise operations + o.PhaseB = uint8((registerValue >> 0) & 0xFF) // Extract 8 bits for PhaseB + o.PhaseA = uint8((registerValue >> 8) & 0xFF) // Extract 8 bits for PhaseA +} + +// IHOLD_IRUN_Register struct to represent the IHOLD_IRUN register +type IHOLD_IRUN_Register struct { + Register + Ihold uint8 // Standstill current (5 bits) + Irun uint8 // Motor run current (5 bits) + IholdDelay uint8 // Motor power down delay (4 bits) +} + +// NewIHOLD_IRUN creates a new IHOLD_IRUN register instance +func NewIHOLD_IRUN() *IHOLD_IRUN_Register { + return &IHOLD_IRUN_Register{ + Register: Register{ + RegisterAddr: IHOLD_IRUN, + }, + } +} + +// Pack method for IHOLD_IRUN: overrides the base Pack +func (i *IHOLD_IRUN_Register) Pack() uint32 { + var registerValue uint32 + + // Pack each field using bitwise operations + registerValue |= uint32(i.Ihold&0x1F) << 0 // Ihold: 5 bits + registerValue |= uint32(i.Irun&0x1F) << 8 // Irun: 5 bits + registerValue |= uint32(i.IholdDelay&0xF) << 16 // IholdDelay: 4 bits + + return registerValue +} + +// Unpack method for IHOLD_IRUN: overrides the base Unpack +func (i *IHOLD_IRUN_Register) Unpack(registerValue uint32) { + // Unpack each field using bitwise operations + i.Ihold = uint8((registerValue >> 0) & 0x1F) // Extract 5 bits for Ihold + i.Irun = uint8((registerValue >> 8) & 0x1F) // Extract 5 bits for Irun + i.IholdDelay = uint8((registerValue >> 16) & 0xF) // Extract 4 bits for IholdDelay +} + +// SW_MODE_Register struct to represent the SW_MODE register +type SW_MODE_Register struct { + Register + StopLEnable bool // Enable automatic motor stop during active left reference switch input + StopREnable bool // Enable automatic motor stop during active right reference switch input + PolStopL bool // Sets the active polarity of the left reference switch input + PolStopR bool // Sets the active polarity of the right reference switch input + SwapLR bool // Swap the left and right reference switch inputs + LatchLActive bool // Activate latching of the position to XLATCH upon an active going edge on REFL + LatchLInactive bool // Activate latching of the position to XLATCH upon an inactive going edge on REFL + LatchRActive bool // Activate latching of the position to XLATCH upon an active going edge on REFR + LatchRInactive bool // Activate latching of the position to XLATCH upon an inactive going edge on REFR + EnLatchEncoder bool // Latch encoder position to ENC_LATCH upon reference switch event + SgStop bool // Enable stop by stallGuard2 + EnSoftStop bool // Enable soft stop upon a stop event +} + +// NewSW_MODE creates a new SW_MODE register instance +func NewSW_MODE() *SW_MODE_Register { + return &SW_MODE_Register{ + Register: Register{ + RegisterAddr: SW_MODE, + }, + } +} + +// Pack method for SW_MODE: overrides the base Pack +func (s *SW_MODE_Register) Pack() uint32 { + var registerValue uint32 + + // Pack each field using bitwise operations + if s.StopLEnable { + registerValue |= 1 << 0 + } + if s.StopREnable { + registerValue |= 1 << 1 + } + if s.PolStopL { + registerValue |= 1 << 2 + } + if s.PolStopR { + registerValue |= 1 << 3 + } + if s.SwapLR { + registerValue |= 1 << 4 + } + if s.LatchLActive { + registerValue |= 1 << 5 + } + if s.LatchLInactive { + registerValue |= 1 << 6 + } + if s.LatchRActive { + registerValue |= 1 << 7 + } + if s.LatchRInactive { + registerValue |= 1 << 8 + } + if s.EnLatchEncoder { + registerValue |= 1 << 9 + } + if s.SgStop { + registerValue |= 1 << 10 + } + if s.EnSoftStop { + registerValue |= 1 << 11 + } + + return registerValue +} + +// Unpack method for SW_MODE: overrides the base Unpack +func (s *SW_MODE_Register) Unpack(registerValue uint32) { + // Unpack each field using bitwise operations + s.StopLEnable = (registerValue & (1 << 0)) != 0 + s.StopREnable = (registerValue & (1 << 1)) != 0 + s.PolStopL = (registerValue & (1 << 2)) != 0 + s.PolStopR = (registerValue & (1 << 3)) != 0 + s.SwapLR = (registerValue & (1 << 4)) != 0 + s.LatchLActive = (registerValue & (1 << 5)) != 0 + s.LatchLInactive = (registerValue & (1 << 6)) != 0 + s.LatchRActive = (registerValue & (1 << 7)) != 0 + s.LatchRInactive = (registerValue & (1 << 8)) != 0 + s.EnLatchEncoder = (registerValue & (1 << 9)) != 0 + s.SgStop = (registerValue & (1 << 10)) != 0 + s.EnSoftStop = (registerValue & (1 << 11)) != 0 +} + +// RAMP_STAT_Register struct to represent the RAMP_STAT register +type RAMP_STAT_Register struct { + Register + StatusStopL bool // Reference switch left status (1=active) + StatusStopR bool // Reference switch right status (1=active) + StatusLatchL bool // Latch left ready (enable position latching) + StatusLatchR bool // Latch right ready (enable position latching) + EventStopL bool // Active stop left condition due to stop switch + EventStopR bool // Active stop right condition due to stop switch + EventStopSG bool // Active StallGuard2 stop event + EventPosReached bool // Target position reached + VelocityReached bool // Target velocity reached + PositionReached bool // Target position reached + VZero bool // Actual velocity is 0 + TZeroWaitActive bool // TZEROWAIT is active after motor stop + SecondMove bool // Automatic ramp required moving back in opposite direction + StatusSG bool // Active stallGuard2 input +} + +// NewRAMP_STAT creates a new RAMP_STAT register instance +func NewRAMP_STAT() *RAMP_STAT_Register { + return &RAMP_STAT_Register{ + Register: Register{ + RegisterAddr: RAMP_STAT, + }, + } +} + +// Pack method for RAMP_STAT: overrides the base Pack +func (r *RAMP_STAT_Register) Pack() uint32 { + var registerValue uint32 + + // Pack each field using bitwise operations + if r.StatusStopL { + registerValue |= 1 << 0 + } + if r.StatusStopR { + registerValue |= 1 << 1 + } + if r.StatusLatchL { + registerValue |= 1 << 2 + } + if r.StatusLatchR { + registerValue |= 1 << 3 + } + if r.EventStopL { + registerValue |= 1 << 4 + } + if r.EventStopR { + registerValue |= 1 << 5 + } + if r.EventStopSG { + registerValue |= 1 << 6 + } + if r.EventPosReached { + registerValue |= 1 << 7 + } + if r.VelocityReached { + registerValue |= 1 << 8 + } + if r.PositionReached { + registerValue |= 1 << 9 + } + if r.VZero { + registerValue |= 1 << 10 + } + if r.TZeroWaitActive { + registerValue |= 1 << 11 + } + if r.SecondMove { + registerValue |= 1 << 12 + } + if r.StatusSG { + registerValue |= 1 << 13 + } + + return registerValue +} + +// Unpack method for RAMP_STAT: overrides the base Unpack +func (r *RAMP_STAT_Register) Unpack(registerValue uint32) { + // Unpack each field using bitwise operations + r.StatusStopL = (registerValue & (1 << 0)) != 0 + r.StatusStopR = (registerValue & (1 << 1)) != 0 + r.StatusLatchL = (registerValue & (1 << 2)) != 0 + r.StatusLatchR = (registerValue & (1 << 3)) != 0 + r.EventStopL = (registerValue & (1 << 4)) != 0 + r.EventStopR = (registerValue & (1 << 5)) != 0 + r.EventStopSG = (registerValue & (1 << 6)) != 0 + r.EventPosReached = (registerValue & (1 << 7)) != 0 + r.VelocityReached = (registerValue & (1 << 8)) != 0 + r.PositionReached = (registerValue & (1 << 9)) != 0 + r.VZero = (registerValue & (1 << 10)) != 0 + r.TZeroWaitActive = (registerValue & (1 << 11)) != 0 + r.SecondMove = (registerValue & (1 << 12)) != 0 + r.StatusSG = (registerValue & (1 << 13)) != 0 +} + +// ENCMODE_Register struct to represent the ENCMODE register +type ENCMODE_Register struct { + Register + PolA bool // Required A polarity for an N channel event + PolB bool // Required B polarity for an N channel event + PolN bool // Defines active polarity of N (0=low active, 1=high active) + IgnoreAB bool // Ignore A and B polarity for N channel event + ClrCont bool // Always latch or latch and clear X_ENC upon an N event + ClrOnce bool // Latch or latch and clear X_ENC on the next N event + Sensitivity uint8 // N channel event sensitivity (2 bits) + ClrEncX bool // Clear encoder counter X_ENC upon N-event + LatchXAct bool // Also latch XACTUAL position together with X_ENC + EncSelDecimal bool // Encoder prescaler divisor binary mode (0) / decimal mode (1) +} + +// NewENCMODE creates a new ENCMODE register instance +func NewENCMODE() *ENCMODE_Register { + return &ENCMODE_Register{ + Register: Register{ + RegisterAddr: ENCMODE, + }, + } +} + +// Pack method for ENCMODE: overrides the base Pack +func (e *ENCMODE_Register) Pack() uint32 { + var registerValue uint32 + + // Pack each field using bitwise operations + if e.PolA { + registerValue |= 1 << 0 + } + if e.PolB { + registerValue |= 1 << 1 + } + if e.PolN { + registerValue |= 1 << 2 + } + if e.IgnoreAB { + registerValue |= 1 << 3 + } + if e.ClrCont { + registerValue |= 1 << 4 + } + if e.ClrOnce { + registerValue |= 1 << 5 + } + registerValue |= uint32(e.Sensitivity&0x3) << 6 // Sensitivity: 2 bits + if e.ClrEncX { + registerValue |= 1 << 8 + } + if e.LatchXAct { + registerValue |= 1 << 9 + } + if e.EncSelDecimal { + registerValue |= 1 << 10 + } + + return registerValue +} + +// Unpack method for ENCMODE: overrides the base Unpack +func (e *ENCMODE_Register) Unpack(registerValue uint32) { + // Unpack each field using bitwise operations + e.PolA = (registerValue & (1 << 0)) != 0 + e.PolB = (registerValue & (1 << 1)) != 0 + e.PolN = (registerValue & (1 << 2)) != 0 + e.IgnoreAB = (registerValue & (1 << 3)) != 0 + e.ClrCont = (registerValue & (1 << 4)) != 0 + e.ClrOnce = (registerValue & (1 << 5)) != 0 + e.Sensitivity = uint8((registerValue >> 6) & 0x3) // Extract 2 bits for Sensitivity + e.ClrEncX = (registerValue & (1 << 8)) != 0 + e.LatchXAct = (registerValue & (1 << 9)) != 0 + e.EncSelDecimal = (registerValue & (1 << 10)) != 0 +} + +// ENC_STATUS_Register struct to represent the ENC_STATUS register +type ENC_STATUS_Register struct { + Register + NEvent bool // N event detected + DeviationWarn bool // Deviation between X_ACTUAL and X_ENC detected +} + +// NewENC_STATUS creates a new ENC_STATUS register instance +func NewENC_STATUS() *ENC_STATUS_Register { + return &ENC_STATUS_Register{ + Register: Register{ + RegisterAddr: ENC_STATUS, + }, + } +} + +// Pack method for ENC_STATUS: overrides the base Pack +func (e *ENC_STATUS_Register) Pack() uint32 { + var registerValue uint32 + + // Pack each field using bitwise operations + if e.NEvent { + registerValue |= 1 << 0 + } + if e.DeviationWarn { + registerValue |= 1 << 1 + } + + return registerValue +} + +// Unpack method for ENC_STATUS: overrides the base Unpack +func (e *ENC_STATUS_Register) Unpack(registerValue uint32) { + // Unpack each field using bitwise operations + e.NEvent = (registerValue & (1 << 0)) != 0 + e.DeviationWarn = (registerValue & (1 << 1)) != 0 +} + +// CHOPCONF_Register struct to represent the CHOPCONF register +type CHOPCONF_Register struct { + Register + Toff uint8 // Off time setting (4 bits) + HstrtTfd uint8 // Hysteresis start value or fast decay time setting (3 bits) + HendOffset uint8 // Hysteresis low value or sine wave offset (4 bits) + Tfd3 bool // Fast decay time setting bit 3 + Disfdcc bool // Disable current comparator usage for fast decay termination + Rndtf bool // Enable random modulation of chopper TOFF time + Chm bool // Chopper mode (0=standard, 1=constant off time with fast decay) + Tbl uint8 // Comparator blank time select (2 bits) + Vsense bool // Select resistor voltage sensitivity (low or high) + Vhighfs bool // Enable fullstep switching when VHIGH is exceeded + Vhighchm bool // Enable switching to chm=1 and fd=0 when VHIGH is exceeded + Tpfd uint8 // Passive fast decay time (4 bits) + Mres uint8 // Microstep resolution (4 bits) + Intpol bool // Enable interpolation to 256 microsteps + Dedge bool // Enable double edge step pulses + Diss2g bool // Disable short to GND protection + Diss2vs bool // Disable short to supply protection +} + +// NewCHOPCONF creates a new CHOPCONF register instance +func NewCHOPCONF() *CHOPCONF_Register { + return &CHOPCONF_Register{ + Register: Register{ + RegisterAddr: CHOPCONF, + }, + } +} + +// Pack method for CHOPCONF: overrides the base Pack +func (c *CHOPCONF_Register) Pack() uint32 { + var registerValue uint32 + + // Pack each field using bitwise operations + registerValue |= uint32(c.Toff&0xF) << 0 // Toff: 4 bits + registerValue |= uint32(c.HstrtTfd&0x7) << 4 // HstrtTfd: 3 bits + registerValue |= uint32(c.HendOffset&0xF) << 7 // HendOffset: 4 bits + if c.Tfd3 { + registerValue |= 1 << 11 // Tfd3: 1 bit + } + if c.Disfdcc { + registerValue |= 1 << 12 // Disfdcc: 1 bit + } + if c.Rndtf { + registerValue |= 1 << 13 // Rndtf: 1 bit + } + if c.Chm { + registerValue |= 1 << 14 // Chm: 1 bit + } + registerValue |= uint32(c.Tbl&0x3) << 15 // Tbl: 2 bits + if c.Vsense { + registerValue |= 1 << 17 // Vsense: 1 bit + } + if c.Vhighfs { + registerValue |= 1 << 18 // Vhighfs: 1 bit + } + if c.Vhighchm { + registerValue |= 1 << 19 // Vhighchm: 1 bit + } + registerValue |= uint32(c.Tpfd&0xF) << 20 // Tpfd: 4 bits + registerValue |= uint32(c.Mres&0xF) << 24 // Mres: 4 bits + if c.Intpol { + registerValue |= 1 << 28 // Intpol: 1 bit + } + if c.Dedge { + registerValue |= 1 << 29 // Dedge: 1 bit + } + if c.Diss2g { + registerValue |= 1 << 30 // Diss2g: 1 bit + } + if c.Diss2vs { + registerValue |= 1 << 31 // Diss2vs: 1 bit + } + + return registerValue +} + +// Unpack method for CHOPCONF: overrides the base Unpack +func (c *CHOPCONF_Register) Unpack(registerValue uint32) { + // Unpack each field using bitwise operations + c.Toff = uint8((registerValue >> 0) & 0xF) // Extract 4 bits for Toff + c.HstrtTfd = uint8((registerValue >> 4) & 0x7) // Extract 3 bits for HstrtTfd + c.HendOffset = uint8((registerValue >> 7) & 0xF) // Extract 4 bits for HendOffset + c.Tfd3 = (registerValue & (1 << 11)) != 0 // Extract 1 bit for Tfd3 + c.Disfdcc = (registerValue & (1 << 12)) != 0 // Extract 1 bit for Disfdcc + c.Rndtf = (registerValue & (1 << 13)) != 0 // Extract 1 bit for Rndtf + c.Chm = (registerValue & (1 << 14)) != 0 // Extract 1 bit for Chm + c.Tbl = uint8((registerValue >> 15) & 0x3) // Extract 2 bits for Tbl + c.Vsense = (registerValue & (1 << 17)) != 0 // Extract 1 bit for Vsense + c.Vhighfs = (registerValue & (1 << 18)) != 0 // Extract 1 bit for Vhighfs + c.Vhighchm = (registerValue & (1 << 19)) != 0 // Extract 1 bit for Vhighchm + c.Tpfd = uint8((registerValue >> 20) & 0xF) // Extract 4 bits for Tpfd + c.Mres = uint8((registerValue >> 24) & 0xF) // Extract 4 bits for Mres + c.Intpol = (registerValue & (1 << 28)) != 0 // Extract 1 bit for Intpol + c.Dedge = (registerValue & (1 << 29)) != 0 // Extract 1 bit for Dedge + c.Diss2g = (registerValue & (1 << 30)) != 0 // Extract 1 bit for Diss2g + c.Diss2vs = (registerValue & (1 << 31)) != 0 // Extract 1 bit for Diss2vs +} + +// COOLCONF_Register struct to represent the COOLCONF register +type COOLCONF_Register struct { + Register + Semin uint8 // Minimum stallGuard2 value for smart current control (4 bits) + Seup uint8 // Current increment step width (2 bits) + Semax uint8 // stallGuard2 hysteresis value for smart current control (4 bits) + Sedn uint8 // Current decrement step speed (2 bits) + Seimin bool // Minimum current for smart current control (1 bit) + Sgt uint8 // stallGuard2 threshold value (7 bits) + Sfilt bool // Enable stallGuard2 filter (1 bit) +} + +// NewCOOLCONF creates a new COOLCONF register instance +func NewCOOLCONF() *COOLCONF_Register { + return &COOLCONF_Register{ + Register: Register{ + RegisterAddr: COOLCONF, + }, + } +} + +// Pack method for COOLCONF: overrides the base Pack +func (c *COOLCONF_Register) Pack() uint32 { + var registerValue uint32 + + // Pack each field using bitwise operations + registerValue |= uint32(c.Semin&0xF) << 0 // Semin: 4 bits + registerValue |= uint32(c.Seup&0x3) << 5 // Seup: 2 bits + registerValue |= uint32(c.Semax&0xF) << 8 // Semax: 4 bits + registerValue |= uint32(c.Sedn&0x3) << 13 // Sedn: 2 bits + if c.Seimin { + registerValue |= 1 << 15 // Seimin: 1 bit + } + registerValue |= uint32(c.Sgt&0x7F) << 16 // Sgt: 7 bits + if c.Sfilt { + registerValue |= 1 << 24 // Sfilt: 1 bit + } + + return registerValue +} + +// Unpack method for COOLCONF: overrides the base Unpack +func (c *COOLCONF_Register) Unpack(registerValue uint32) { + // Unpack each field using bitwise operations + c.Semin = uint8((registerValue >> 0) & 0xF) // Extract 4 bits for Semin + c.Seup = uint8((registerValue >> 5) & 0x3) // Extract 2 bits for Seup + c.Semax = uint8((registerValue >> 8) & 0xF) // Extract 4 bits for Semax + c.Sedn = uint8((registerValue >> 13) & 0x3) // Extract 2 bits for Sedn + c.Seimin = (registerValue & (1 << 15)) != 0 // Extract 1 bit for Seimin + c.Sgt = uint8((registerValue >> 16) & 0x7F) // Extract 7 bits for Sgt + c.Sfilt = (registerValue & (1 << 24)) != 0 // Extract 1 bit for Sfilt +} + +// DCCTRL_Register struct to represent the DCCTRL register +type DCCTRL_Register struct { + Register + DcTime uint16 // Upper PWM on time limit for commutation (10 bits) + DcSg uint8 // Max. PWM on time for step loss detection using dcStep (8 bits) +} + +// NewDCCTRL creates a new DCCTRL register instance +func NewDCCTRL() *DCCTRL_Register { + return &DCCTRL_Register{ + Register: Register{ + RegisterAddr: DCCTRL, + }, + } +} + +// Pack method for DCCTRL: overrides the base Pack +func (d *DCCTRL_Register) Pack() uint32 { + var registerValue uint32 + + // Pack each field using bitwise operations + registerValue |= uint32(d.DcTime&0x3FF) << 0 // DcTime: 10 bits + registerValue |= uint32(d.DcSg&0xFF) << 16 // DcSg: 8 bits + + return registerValue +} + +// Unpack method for DCCTRL: overrides the base Unpack +func (d *DCCTRL_Register) Unpack(registerValue uint32) { + // Unpack each field using bitwise operations + d.DcTime = uint16((registerValue >> 0) & 0x3FF) // Extract 10 bits for DcTime + d.DcSg = uint8((registerValue >> 16) & 0xFF) // Extract 8 bits for DcSg +} + +// DRV_STATUS_Register struct to represent the DRV_STATUS register +type DRV_STATUS_Register struct { + Register + SgResult uint16 // stallGuard2 result or motor temperature estimation in standstill (9 bits) + S2vsa bool // Short to supply indicator phase A + S2vsb bool // Short to supply indicator phase B + Stealth bool // stealthChop indicator + FsActive bool // Full step active indicator + CsActual uint8 // Actual motor current / smart energy current (5 bits) + StallGuard bool // stallGuard2 status + Ot bool // Overtemperature flag + Otpw bool // Overtemperature pre-warning flag + S2ga bool // Short to ground indicator phase A + S2gb bool // Short to ground indicator phase B + Ola bool // Open load indicator phase A + Olb bool // Open load indicator phase B + Stst bool // Standstill indicator +} + +// NewDRV_STATUS creates a new DRV_STATUS register instance +func NewDRV_STATUS() *DRV_STATUS_Register { + return &DRV_STATUS_Register{ + Register: Register{ + RegisterAddr: DRV_STATUS, + }, + } +} + +// Pack method for DRV_STATUS: overrides the base Pack +func (d *DRV_STATUS_Register) Pack() uint32 { + var registerValue uint32 + + // Pack each field using bitwise operations + registerValue |= uint32(d.SgResult&0x1FF) << 0 // SgResult: 9 bits + if d.S2vsa { + registerValue |= 1 << 12 // S2vsa: 1 bit + } + if d.S2vsb { + registerValue |= 1 << 13 // S2vsb: 1 bit + } + if d.Stealth { + registerValue |= 1 << 14 // Stealth: 1 bit + } + if d.FsActive { + registerValue |= 1 << 15 // FsActive: 1 bit + } + registerValue |= uint32(d.CsActual&0x1F) << 16 // CsActual: 5 bits + if d.StallGuard { + registerValue |= 1 << 24 // StallGuard: 1 bit + } + if d.Ot { + registerValue |= 1 << 25 // Ot: 1 bit + } + if d.Otpw { + registerValue |= 1 << 26 // Otpw: 1 bit + } + if d.S2ga { + registerValue |= 1 << 27 // S2ga: 1 bit + } + if d.S2gb { + registerValue |= 1 << 28 // S2gb: 1 bit + } + if d.Ola { + registerValue |= 1 << 29 // Ola: 1 bit + } + if d.Olb { + registerValue |= 1 << 30 // Olb: 1 bit + } + if d.Stst { + registerValue |= 1 << 31 // Stst: 1 bit + } + + return registerValue +} + +// Unpack method for DRV_STATUS: overrides the base Unpack +func (d *DRV_STATUS_Register) Unpack(registerValue uint32) { + // Unpack each field using bitwise operations + d.SgResult = uint16((registerValue >> 0) & 0x1FF) // Extract 9 bits for SgResult + d.S2vsa = (registerValue & (1 << 12)) != 0 // Extract 1 bit for S2vsa + d.S2vsb = (registerValue & (1 << 13)) != 0 // Extract 1 bit for S2vsb + d.Stealth = (registerValue & (1 << 14)) != 0 // Extract 1 bit for Stealth + d.FsActive = (registerValue & (1 << 15)) != 0 // Extract 1 bit for FsActive + d.CsActual = uint8((registerValue >> 16) & 0x1F) // Extract 5 bits for CsActual + d.StallGuard = (registerValue & (1 << 24)) != 0 // Extract 1 bit for StallGuard + d.Ot = (registerValue & (1 << 25)) != 0 // Extract 1 bit for Ot + d.Otpw = (registerValue & (1 << 26)) != 0 // Extract 1 bit for Otpw + d.S2ga = (registerValue & (1 << 27)) != 0 // Extract 1 bit for S2ga + d.S2gb = (registerValue & (1 << 28)) != 0 // Extract 1 bit for S2gb + d.Ola = (registerValue & (1 << 29)) != 0 // Extract 1 bit for Ola + d.Olb = (registerValue & (1 << 30)) != 0 // Extract 1 bit for Olb + d.Stst = (registerValue & (1 << 31)) != 0 // Extract 1 bit for Stst +} + +// PWMCONF_Register struct to represent the PWMCONF register +type PWMCONF_Register struct { + Register + PwmOfs uint8 // User defined PWM amplitude offset (8 bits) + PwmGrad uint8 // User defined PWM amplitude gradient (8 bits) + PwmFreq uint8 // PWM frequency selection (2 bits) + PwmAutoscale bool // Enable PWM automatic amplitude scaling (1 bit) + PwmAutograd bool // PWM automatic gradient adaptation (1 bit) + Freewheel uint8 // Standstill option when motor current setting is zero (2 bits) + PwmReg uint8 // Regulation loop gradient (4 bits) + PwmLim uint8 // PWM automatic scale amplitude limit when switching on (4 bits) +} + +// NewPWMCONF creates a new PWMCONF register instance +func NewPWMCONF() *PWMCONF_Register { + return &PWMCONF_Register{ + Register: Register{ + RegisterAddr: PWMCONF, + }, + } +} + +// Pack method for PWMCONF: overrides the base Pack +func (p *PWMCONF_Register) Pack() uint32 { + var registerValue uint32 + + // Pack each field using bitwise operations + registerValue |= uint32(p.PwmOfs&0xFF) << 0 // PwmOfs: 8 bits + registerValue |= uint32(p.PwmGrad&0xFF) << 8 // PwmGrad: 8 bits + registerValue |= uint32(p.PwmFreq&0x3) << 16 // PwmFreq: 2 bits + if p.PwmAutoscale { + registerValue |= 1 << 18 // PwmAutoscale: 1 bit + } + if p.PwmAutograd { + registerValue |= 1 << 19 // PwmAutograd: 1 bit + } + registerValue |= uint32(p.Freewheel&0x3) << 20 // Freewheel: 2 bits + registerValue |= uint32(p.PwmReg&0xF) << 24 // PwmReg: 4 bits + registerValue |= uint32(p.PwmLim&0xF) << 28 // PwmLim: 4 bits + + return registerValue +} + +// Unpack method for PWMCONF: overrides the base Unpack +func (p *PWMCONF_Register) Unpack(registerValue uint32) { + // Unpack each field using bitwise operations + p.PwmOfs = uint8((registerValue >> 0) & 0xFF) // Extract 8 bits for PwmOfs + p.PwmGrad = uint8((registerValue >> 8) & 0xFF) // Extract 8 bits for PwmGrad + p.PwmFreq = uint8((registerValue >> 16) & 0x3) // Extract 2 bits for PwmFreq + p.PwmAutoscale = (registerValue & (1 << 18)) != 0 // Extract 1 bit for PwmAutoscale + p.PwmAutograd = (registerValue & (1 << 19)) != 0 // Extract 1 bit for PwmAutograd + p.Freewheel = uint8((registerValue >> 20) & 0x3) // Extract 2 bits for Freewheel + p.PwmReg = uint8((registerValue >> 24) & 0xF) // Extract 4 bits for PwmReg + p.PwmLim = uint8((registerValue >> 28) & 0xF) // Extract 4 bits for PwmLim +} + +// PWM_SCALE_Register struct to represent the PWM_SCALE register +type PWM_SCALE_Register struct { + Register + PwmScaleSum uint8 // Actual PWM duty cycle (8 bits) + PwmScaleAuto uint16 // Result of the automatic amplitude regulation based on current measurement (9 bits) +} + +// NewPWM_SCALE creates a new PWM_SCALE register instance +func NewPWM_SCALE() *PWM_SCALE_Register { + return &PWM_SCALE_Register{ + Register: Register{ + RegisterAddr: PWM_SCALE, + }, + } +} + +// Pack method for PWM_SCALE: overrides the base Pack +func (p *PWM_SCALE_Register) Pack() uint32 { + var registerValue uint32 + + // Pack each field using bitwise operations + registerValue |= uint32(p.PwmScaleSum&0xFF) << 0 // PwmScaleSum: 8 bits + registerValue |= uint32(p.PwmScaleAuto&0x1FF) << 16 // PwmScaleAuto: 9 bits + + return registerValue +} + +// Unpack method for PWM_SCALE: overrides the base Unpack +func (p *PWM_SCALE_Register) Unpack(registerValue uint32) { + // Unpack each field using bitwise operations + p.PwmScaleSum = uint8((registerValue >> 0) & 0xFF) // Extract 8 bits for PwmScaleSum + p.PwmScaleAuto = uint16((registerValue >> 16) & 0x1FF) // Extract 9 bits for PwmScaleAuto +} + +// PWM_AUTO_Register struct to represent the PWM_AUTO register +type PWM_AUTO_Register struct { + Register + PwmOfsAuto uint8 // Automatically determined offset value (8 bits) + PwmGradAuto uint8 // Automatically determined gradient value (8 bits) +} + +// NewPWM_AUTO creates a new PWM_AUTO register instance +func NewPWM_AUTO() *PWM_AUTO_Register { + return &PWM_AUTO_Register{ + Register: Register{ + RegisterAddr: PWM_AUTO, + }, + } +} + +// Pack method for PWM_AUTO: overrides the base Pack +func (p *PWM_AUTO_Register) Pack() uint32 { + var registerValue uint32 + + // Pack each field using bitwise operations + registerValue |= uint32(p.PwmOfsAuto&0xFF) << 0 // PwmOfsAuto: 8 bits + registerValue |= uint32(p.PwmGradAuto&0xFF) << 16 // PwmGradAuto: 8 bits + + return registerValue +} + +// Unpack method for PWM_AUTO: overrides the base Unpack +func (p *PWM_AUTO_Register) Unpack(registerValue uint32) { + // Unpack each field using bitwise operations + p.PwmOfsAuto = uint8((registerValue >> 0) & 0xFF) // Extract 8 bits for PwmOfsAuto + p.PwmGradAuto = uint8((registerValue >> 16) & 0xFF) // Extract 8 bits for PwmGradAuto +} + +// MSCNT_Register struct to represent the MSCNT register (10-bit value) +type MSCNT_Register struct { + Register + Value uint16 // Microstep counter value (10 bits) +} + +// NewMSCNT creates a new MSCNT register instance +func NewMSCNT() *MSCNT_Register { + return &MSCNT_Register{ + Register: Register{ + RegisterAddr: MSCNT, + }, + } +} + +// Pack method for MSCNT: combines the 10-bit value into a 16-bit value +func (m *MSCNT_Register) Pack() uint16 { + return m.Value & 0x3FF // Mask the value to ensure it is within the 10-bit range (0-1023) +} + +// Unpack method for MSCNT: extracts the 10-bit value from a 16-bit value +func (m *MSCNT_Register) Unpack(registerValue uint16) { + m.Value = registerValue & 0x3FF // Mask to extract the 10-bit value (0-1023) +} + +// VDCMIN_Register struct for VDCMIN register (23 bits) +type VDCMIN_Register struct { + Register + Value uint32 // 23-bit value +} + +// NewVDCMIN creates a new VDCMIN register instance +func NewVDCMIN() *VDCMIN_Register { + return &VDCMIN_Register{ + Register: Register{ + RegisterAddr: VDCMIN, + }, + } +} + +// Pack method for VDCMIN: packs the 23-bit value into a 32-bit value +func (v *VDCMIN_Register) Pack() uint32 { + return v.Value & 0x7FFFFF // Mask to 23 bits +} + +// Unpack method for VDCMIN: unpacks the 23-bit value from a 32-bit value +func (v *VDCMIN_Register) Unpack(registerValue uint32) { + v.Value = registerValue & 0x7FFFFF // Mask to 23 bits +} + +// XLATCH_Register struct for XLATCH register (32 bits) +type XLATCH_Register struct { + Register + Value uint32 // 32-bit value +} + +// NewXLATCH creates a new XLATCH register instance +func NewXLATCH() *XLATCH_Register { + return &XLATCH_Register{ + Register: Register{ + RegisterAddr: XLATCH, + }, + } +} + +// Pack method for XLATCH: directly returns the 32-bit value +func (x *XLATCH_Register) Pack() uint32 { + return x.Value // No mask needed, since it's 32 bits +} + +// Unpack method for XLATCH: unpacks the 32-bit value from a 32-bit register +func (x *XLATCH_Register) Unpack(registerValue uint32) { + x.Value = registerValue // Direct assignment since it's 32 bits +} + +// RAMPMODE_Register struct for RAMPMODE register (2 bits) +type RAMPMODE_Register struct { + Register + mode RampMode // Mode is now an enum-like type + comm RegisterComm + driverIndex uint8 +} +type RampMode uint8 + +const ( + PositioningMode RampMode = iota // 0 + VelocityPositiveMode // 1 + VelocityNegativeMode // 2 + HoldMode // 3 +) + +func NewRAMPMODE(comm RegisterComm, driverIndex uint8) *RAMPMODE_Register { + return &RAMPMODE_Register{ + Register: Register{ + RegisterAddr: RAMPMODE, + }, + driverIndex: driverIndex, + comm: comm, + mode: PositioningMode, // Default to Positioning Mode + } +} + +// SetMode sets the mode of the RAMPMODE register +func (r *RAMPMODE_Register) SetMode(mode RampMode) error { + r.mode = mode + registerValue := r.Pack() + return r.comm.WriteRegister(r.RegisterAddr, uint32(registerValue), r.driverIndex) +} + +// GetMode returns the current mode of the RAMPMODE register +func (r *RAMPMODE_Register) GetMode() (RampMode, error) { + registerValue, err := r.comm.ReadRegister(r.RegisterAddr, r.driverIndex) + if err != nil { + return 0, err //Defaults to Postioning Mode + } + + // Unpack the register value to get the mode + r.Unpack(uint8(registerValue)) + return r.mode, nil + +} + +// Pack method for RAMPMODE: packs the mode value into a single byte (now using enums) +func (r *RAMPMODE_Register) Pack() uint8 { + return uint8(r.mode) // Simply cast the mode to uint8 +} + +// Unpack method for RAMPMODE: unpacks the mode value from a byte +func (r *RAMPMODE_Register) Unpack(registerValue uint8) { + r.mode = RampMode(registerValue & 0x03) // Mask to 2 bits +} + +// String method to display the mode as a string (useful for logging or debugging) +func (r RampMode) String() string { + switch r { + case PositioningMode: + return "Positioning Mode" + case VelocityPositiveMode: + return "Velocity Mode (Positive VMAX)" + case VelocityNegativeMode: + return "Velocity Mode (Negative VMAX)" + case HoldMode: + return "Hold Mode" + default: + return "Unknown Mode" + } +} + +// XACTUAL_Register struct for XACTUAL register (32 bits) +type XACTUAL_Register struct { + Register + Value uint32 // 32-bit value +} + +// NewXACTUAL creates a new XACTUAL register instance +func NewXACTUAL() *XACTUAL_Register { + return &XACTUAL_Register{ + Register: Register{ + RegisterAddr: XACTUAL, + }, + } +} + +// Pack method for XACTUAL: returns the 32-bit value +func (x *XACTUAL_Register) Pack() uint32 { + return x.Value // 32 bits, no masking needed +} + +// Unpack method for XACTUAL: unpacks the 32-bit value +func (x *XACTUAL_Register) Unpack(registerValue uint32) { + x.Value = registerValue // Direct assignment since it's 32 bits +} + +// VACTUAL_Register struct for VACTUAL register (24 bits) +type VACTUAL_Register struct { + Register + Value uint32 // 24-bit value (stored in a 32-bit field) +} + +// NewVACTUAL creates a new VACTUAL register instance +func NewVACTUAL() *VACTUAL_Register { + return &VACTUAL_Register{ + Register: Register{ + RegisterAddr: VACTUAL, + }, + } +} + +// Pack method for VACTUAL: packs the 24-bit value into a 32-bit value +func (v *VACTUAL_Register) Pack() uint32 { + return v.Value & 0xFFFFFF // Mask to 24 bits +} + +// Unpack method for VACTUAL: unpacks the 24-bit value from a 32-bit value +func (v *VACTUAL_Register) Unpack(registerValue uint32) { + v.Value = registerValue & 0xFFFFFF // Mask to 24 bits +} + +// VSTART_Register struct for VSTART register (18 bits) +type VSTART_Register struct { + Register + Value uint32 // 18-bit value +} + +// NewVSTART creates a new VSTART register instance +func NewVSTART() *VSTART_Register { + return &VSTART_Register{ + Register: Register{ + RegisterAddr: VSTART, + }, + } +} + +// Pack method for VSTART: packs the 18-bit value into a 16-bit value +func (v *VSTART_Register) Pack() uint32 { + return v.Value & 0x3FFFF // Mask to 18 bits +} + +// Unpack method for VSTART: unpacks the 18-bit value from a 16-bit value +func (v *VSTART_Register) Unpack(registerValue uint32) { + v.Value = registerValue & 0x3FFFF // Mask to 18 bits +} + +// A1_Register struct for A1 register (16 bits) +type A1_Register struct { + Register + Value uint16 // 16-bit value +} + +// NewA1 creates a new A1 register instance +func NewA1() *A1_Register { + return &A1_Register{ + Register: Register{ + RegisterAddr: A_1, + }, + } +} + +// Pack method for A1: returns the 16-bit value +func (a *A1_Register) Pack() uint16 { + return a.Value // 16 bits, no masking needed +} + +// Unpack method for A1: unpacks the 16-bit value +func (a *A1_Register) Unpack(registerValue uint16) { + a.Value = registerValue // Direct assignment since it's 16 bits +} + +// V1_Register struct for V1 register (20 bits) +type V1_Register struct { + Register + Value uint32 // 20-bit value (stored in a 32-bit field) +} + +// NewV1 creates a new V1 register instance +func NewV1() *V1_Register { + return &V1_Register{ + Register: Register{ + RegisterAddr: V_1, + }, + } +} + +// Pack method for V1: packs the 20-bit value into a 32-bit value +func (v *V1_Register) Pack() uint32 { + return v.Value & 0xFFFFF // Mask to 20 bits +} + +// Unpack method for V1: unpacks the 20-bit value from a 32-bit value +func (v *V1_Register) Unpack(registerValue uint32) { + v.Value = registerValue & 0xFFFFF // Mask to 20 bits +} + +// AMAX_Register struct for AMAX register (16 bits) +type AMAX_Register struct { + Register + Value uint16 // 16-bit value +} + +// NewAMAX creates a new AMAX register instance +func NewAMAX() *AMAX_Register { + return &AMAX_Register{ + Register: Register{ + RegisterAddr: AMAX, + }, + } +} + +// Pack method for AMAX: returns the 16-bit value +func (a *AMAX_Register) Pack() uint16 { + return a.Value // 16 bits, no masking needed +} + +// Unpack method for AMAX: unpacks the 16-bit value +func (a *AMAX_Register) Unpack(registerValue uint16) { + a.Value = registerValue // Direct assignment since it's 16 bits +} + +// VMAX_Register struct for VMAX register (23 bits) +type VMAX_Register struct { + Register + Value uint32 // 23-bit value (stored in a 32-bit field) +} + +// NewVMAX creates a new VMAX register instance +func NewVMAX() *VMAX_Register { + return &VMAX_Register{ + Register: Register{ + RegisterAddr: VMAX, + }, + } +} + +// Pack method for VMAX: packs the 23-bit value into a 32-bit value +func (v *VMAX_Register) Pack() uint32 { + return v.Value & 0x7FFFFF // Mask to 23 bits +} + +// Unpack method for VMAX: unpacks the 23-bit value from a 32-bit value +func (v *VMAX_Register) Unpack(registerValue uint32) { + v.Value = registerValue & 0x7FFFFF // Mask to 23 bits +} + +// D1_Register struct for D1 register (16 bits) +type D1_Register struct { + Register + Value uint16 // 16-bit value +} + +// NewD1 creates a new D1 register instance +func NewD1() *D1_Register { + return &D1_Register{ + Register: Register{ + RegisterAddr: D_1, + }, + } +} + +// Pack method for D1: returns the 16-bit value +func (d *D1_Register) Pack() uint16 { + return d.Value // 16 bits, no masking needed +} + +// Unpack method for D1: unpacks the 16-bit value +func (d *D1_Register) Unpack(registerValue uint16) { + d.Value = registerValue // Direct assignment since it's 16 bits +} + +// VSTOP_Register struct for VSTOP register (18 bits) +type VSTOP_Register struct { + Register + Value uint32 // 18-bit value (stored in a 32-bit field) +} + +// NewVSTOP creates a new VSTOP register instance +func NewVSTOP() *VSTOP_Register { + return &VSTOP_Register{ + Register: Register{ + RegisterAddr: VSTOP, + }, + } +} + +// Pack method for VSTOP: packs the 18-bit value into a 32-bit value +func (v *VSTOP_Register) Pack() uint32 { + return v.Value & 0x3FFFF // Mask to 18 bits +} + +// Unpack method for VSTOP: unpacks the 18-bit value from a 32-bit value +func (v *VSTOP_Register) Unpack(registerValue uint32) { + v.Value = registerValue & 0x3FFFF // Mask to 18 bits +} + +// TZEROWAIT_Register struct for TZEROWAIT register (16 bits) +type TZEROWAIT_Register struct { + Register + Value uint16 // 16-bit value +} + +// NewTZEROWAIT creates a new TZEROWAIT register instance +func NewTZEROWAIT() *TZEROWAIT_Register { + return &TZEROWAIT_Register{ + Register: Register{ + RegisterAddr: TZEROWAIT, + }, + } +} + +// Pack method for TZEROWAIT: returns the 16-bit value +func (t *TZEROWAIT_Register) Pack() uint16 { + return t.Value // 16 bits, no masking needed +} + +// Unpack method for TZEROWAIT: unpacks the 16-bit value +func (t *TZEROWAIT_Register) Unpack(registerValue uint16) { + t.Value = registerValue // Direct assignment since it's 16 bits +} + +// XTARGET_Register struct for XTARGET register (32 bits) +type XTARGET_Register struct { + Register + Value uint32 // 32-bit value +} + +// NewXTARGET creates a new XTARGET register instance +func NewXTARGET() *XTARGET_Register { + return &XTARGET_Register{ + Register: Register{ + RegisterAddr: XTARGET, + }, + } +} + +// Pack method for XTARGET: returns the 32-bit value +func (x *XTARGET_Register) Pack() uint32 { + return x.Value // 32 bits, no masking needed +} + +// Unpack method for XTARGET: unpacks the 32-bit value +func (x *XTARGET_Register) Unpack(registerValue uint32) { + x.Value = registerValue // Direct assignment since it's 32 bits +} + +// X_COMPARE_Register struct for X_COMPARE register (32 bits) +type X_COMPARE_Register struct { + Register + Value uint32 // 32-bit value for position comparison +} + +// NewX_COMPARE creates a new X_COMPARE register instance +func NewX_COMPARE() *X_COMPARE_Register { + return &X_COMPARE_Register{ + Register: Register{ + RegisterAddr: X_COMPARE, + }, + } +} + +// Pack method for X_COMPARE: returns the 32-bit value +func (x *X_COMPARE_Register) Pack() uint32 { + return x.Value // 32 bits, no masking needed +} + +// Unpack method for X_COMPARE: unpacks the 32-bit value +func (x *X_COMPARE_Register) Unpack(registerValue uint32) { + x.Value = registerValue // Direct assignment since it's 32 bits +} + +// GLOBAL_SCALER_Register struct for GLOBAL SCALER register (8 bits) +type GLOBAL_SCALER_Register struct { + Register + Value uint8 // 8-bit value for global motor current scaling +} + +// NewGLOBAL_SCALER creates a new GLOBAL_SCALER register instance +func NewGLOBAL_SCALER() *GLOBAL_SCALER_Register { + return &GLOBAL_SCALER_Register{ + Register: Register{ + RegisterAddr: GLOBAL_SCALER, + }, + } +} + +// Pack method for GLOBAL_SCALER: returns the 8-bit value +func (g *GLOBAL_SCALER_Register) Pack() uint8 { + return g.Value // 8 bits, no masking needed +} + +// Unpack method for GLOBAL_SCALER: unpacks the 8-bit value +func (g *GLOBAL_SCALER_Register) Unpack(registerValue uint8) { + g.Value = registerValue // Direct assignment since it's 8 bits +} + +// TPOWERDOWN_Register struct for TPOWERDOWN register (8 bits) +type TPOWERDOWN_Register struct { + Register + Value uint8 // 8-bit value for time delay after standstill +} + +// NewTPOWERDOWN creates a new TPOWERDOWN register instance +func NewTPOWERDOWN() *TPOWERDOWN_Register { + return &TPOWERDOWN_Register{ + Register: Register{ + RegisterAddr: TPOWERDOWN, + }, + } +} + +// Pack method for TPOWERDOWN: returns the 8-bit value +func (t *TPOWERDOWN_Register) Pack() uint8 { + return t.Value // 8 bits, no masking needed +} + +// Unpack method for TPOWERDOWN: unpacks the 8-bit value +func (t *TPOWERDOWN_Register) Unpack(registerValue uint8) { + t.Value = registerValue // Direct assignment since it's 8 bits +} + +// PWMTHRS_Register struct for PWMTHRS register (20 bits) +type PWMTHRS_Register struct { + Register + Value uint32 // 20-bit value (stored in a 32-bit field) +} + +// NewPWMTHRS creates a new PWMTHRS register instance +func NewPWMTHRS() *PWMTHRS_Register { + return &PWMTHRS_Register{ + Register: Register{ + RegisterAddr: TPWMTHRS, + }, + } +} + +// Pack method for PWMTHRS: packs the 20-bit value into a 32-bit value +func (p *PWMTHRS_Register) Pack() uint32 { + return p.Value & 0xFFFFF // Mask to 20 bits +} + +// Unpack method for PWMTHRS: unpacks the 20-bit value from a 32-bit value +func (p *PWMTHRS_Register) Unpack(registerValue uint32) { + p.Value = registerValue & 0xFFFFF // Mask to 20 bits +} + +// TCOOLTHRS_Register struct for TCOOLTHRS register (20 bits) +type TCOOLTHRS_Register struct { + Register + Value uint32 // 20-bit value (stored in a 32-bit field) +} + +// NewTCOOLTHRS creates a new TCOOLTHRS register instance +func NewTCOOLTHRS() *TCOOLTHRS_Register { + return &TCOOLTHRS_Register{ + Register: Register{ + RegisterAddr: TCOOLTHRS, + }, + } +} + +// Pack method for TCOOLTHRS: packs the 20-bit value into a 32-bit value +func (t *TCOOLTHRS_Register) Pack() uint32 { + return t.Value & 0xFFFFF // Mask to 20 bits +} + +// Unpack method for TCOOLTHRS: unpacks the 20-bit value from a 32-bit value +func (t *TCOOLTHRS_Register) Unpack(registerValue uint32) { + t.Value = registerValue & 0xFFFFF // Mask to 20 bits +} + +// THIGH_Register struct for THIGH register (16 bits) +type THIGH_Register struct { + Register + Value uint16 // 16-bit value +} + +// NewTHIGH creates a new THIGH register instance +func NewTHIGH() *THIGH_Register { + return &THIGH_Register{ + Register: Register{ + RegisterAddr: THIGH, + }, + } +} + +// Pack method for THIGH: returns the 16-bit value +func (t *THIGH_Register) Pack() uint16 { + return t.Value // 16 bits, no masking needed +} + +// Unpack method for THIGH: unpacks the 16-bit value +func (t *THIGH_Register) Unpack(registerValue uint16) { + t.Value = registerValue // Direct assignment since it's 16 bits +} + +// DMAX_Register struct for DMAX register (16 bits) +type DMAX_Register struct { + Register + Value uint16 // 16-bit value for deceleration between VMAX and VSTOP +} + +// NewDMAX creates a new DMAX register instance +func NewDMAX() *DMAX_Register { + return &DMAX_Register{ + Register: Register{ + RegisterAddr: DMAX, + }, + } +} + +// Pack method for DMAX: returns the 16-bit value +func (d *DMAX_Register) Pack() uint16 { + return d.Value // 16 bits, no masking needed +} + +// Unpack method for DMAX: unpacks the 16-bit value +func (d *DMAX_Register) Unpack(registerValue uint16) { + d.Value = registerValue // Direct assignment since it's 16 bits +} + +// TSTEP_Register struct for TSTEP register (20 bits) +type TSTEP_Register struct { + Register + Value uint32 // 20-bit value (stored in a 32-bit field) +} + +// NewTSTEP creates a new TSTEP register instance +func NewTSTEP() *TSTEP_Register { + return &TSTEP_Register{ + Register: Register{ + RegisterAddr: TSTEP, + }, + } +} + +// Pack method for TSTEP: packs the 20-bit value into a 32-bit value +func (t *TSTEP_Register) Pack() uint32 { + return t.Value & 0xFFFFF // Mask to 20 bits +} + +// Unpack method for TSTEP: unpacks the 20-bit value from a 32-bit value +func (t *TSTEP_Register) Unpack(registerValue uint32) { + t.Value = registerValue & 0xFFFFF // Mask to 20 bits +} + +// X_ENC_Register struct for X_ENC register (32 bits) +type X_ENC_Register struct { + Register + Value int32 // 32-bit signed value for actual encoder position +} + +// NewX_ENC creates a new X_ENC register instance +func NewX_ENC() *X_ENC_Register { + return &X_ENC_Register{ + Register: Register{ + RegisterAddr: X_ENC, + }, + } +} + +// Pack method for X_ENC: returns the 32-bit signed value +func (x *X_ENC_Register) Pack() int32 { + return x.Value // 32 bits, no masking needed for signed integer +} + +// Unpack method for X_ENC: unpacks the 32-bit signed value +func (x *X_ENC_Register) Unpack(registerValue int32) { + x.Value = registerValue // Direct assignment since it's 32 bits signed integer +} + +// ENC_CONST_Register struct for ENC_CONST register (32 bits) +type ENC_CONST_Register struct { + Register + Value int32 // 32-bit signed accumulation constant +} + +// NewENC_CONST creates a new ENC_CONST register instance +func NewENC_CONST() *ENC_CONST_Register { + return &ENC_CONST_Register{ + Register: Register{ + RegisterAddr: ENC_CONST, + }, + } +} + +// Pack method for ENC_CONST: returns the 32-bit signed accumulation constant +func (e *ENC_CONST_Register) Pack() int32 { + return e.Value // 32 bits, no masking needed for signed integer +} + +// Unpack method for ENC_CONST: unpacks the 32-bit signed accumulation constant +func (e *ENC_CONST_Register) Unpack(registerValue int32) { + e.Value = registerValue // Direct assignment since it's 32 bits signed integer +} + +// ENC_LATCH_Register struct for ENC_LATCH register (32 bits) +type ENC_LATCH_Register struct { + Register + Value int32 // 32-bit signed value for encoder position latched on N event +} + +// NewENC_LATCH creates a new ENC_LATCH register instance +func NewENC_LATCH() *ENC_LATCH_Register { + return &ENC_LATCH_Register{ + Register: Register{ + RegisterAddr: ENC_LATCH, + }, + } +} + +// Pack method for ENC_LATCH: returns the 32-bit signed value +func (e *ENC_LATCH_Register) Pack() int32 { + return e.Value // 32 bits, no masking needed for signed integer +} + +// Unpack method for ENC_LATCH: unpacks the 32-bit signed value +func (e *ENC_LATCH_Register) Unpack(registerValue int32) { + e.Value = registerValue // Direct assignment since it's 32 bits signed integer +} + +// ENC_DEVIATION_Register struct for ENC_DEVIATION register (20 bits) +type ENC_DEVIATION_Register struct { + Register + Value uint32 // 20-bit unsigned value for maximum deviation +} + +// NewENC_DEVIATION creates a new ENC_DEVIATION register instance +func NewENC_DEVIATION() *ENC_DEVIATION_Register { + return &ENC_DEVIATION_Register{ + Register: Register{ + RegisterAddr: ENC_DEVIATION, + }, + } +} + +// Pack method for ENC_DEVIATION: packs the 20-bit value into a 32-bit value +func (e *ENC_DEVIATION_Register) Pack() uint32 { + return e.Value & 0xFFFFF // Mask to 20 bits +} + +// Unpack method for ENC_DEVIATION: unpacks the 20-bit value from a 32-bit value +func (e *ENC_DEVIATION_Register) Unpack(registerValue uint32) { + e.Value = registerValue & 0xFFFFF // Mask to 20 bits +} + +// MSCURACT_Register struct for MSCURACT register (18 bits) +type MSCURACT_Register struct { + Register + CUR_B int16 // 9-bit signed value for motor phase B (sine wave) + CUR_A int16 // 9-bit signed value for motor phase A (cosine wave) +} + +// NewMSCURACT creates a new MSCURACT register instance +func NewMSCURACT() *MSCURACT_Register { + return &MSCURACT_Register{ + Register: Register{ + RegisterAddr: MSCURACT, + }, + } +} + +// Pack method for MSCURACT: packs the 9-bit signed values for CUR_B and CUR_A into a 32-bit value +func (m *MSCURACT_Register) Pack() uint32 { + return uint32(m.CUR_A<<16 | m.CUR_B) // Combine CUR_A and CUR_B into a 32-bit value +} + +// Unpack method for MSCURACT: unpacks the 32-bit value into CUR_B and CUR_A +func (m *MSCURACT_Register) Unpack(registerValue uint32) { + m.CUR_B = int16(registerValue & 0x1FF) // Mask to get the lower 9 bits for CUR_B + m.CUR_A = int16((registerValue >> 16) & 0x1FF) // Mask to get the next 9 bits for CUR_A +} + +// LOST_STEPS_Register struct for LOST_STEPS register (20 bits) +type LOST_STEPS_Register struct { + Register + Value uint32 // 20-bit unsigned value for lost steps count +} + +// NewLOST_STEPS creates a new LOST_STEPS register instance +func NewLOST_STEPS() *LOST_STEPS_Register { + return &LOST_STEPS_Register{ + Register: Register{ + RegisterAddr: LOST_STEPS, + }, + } +} + +// Pack method for LOST_STEPS: returns the 20-bit value +func (l *LOST_STEPS_Register) Pack() uint32 { + return l.Value & 0xFFFFF // Mask to 20 bits +} + +// Unpack method for LOST_STEPS: unpacks the 20-bit value from a 32-bit value +func (l *LOST_STEPS_Register) Unpack(registerValue uint32) { + l.Value = registerValue & 0xFFFFF // Mask to 20 bits +} + +// MSLUTSEL_Register struct for MSLUTSEL register (32 bits) +type MSLUTSEL_Register struct { + Register + X3 uint8 // 3-bit value for LUT segment 3 start + X2 uint8 // 3-bit value for LUT segment 2 start + X1 uint8 // 3-bit value for LUT segment 1 start + W3 uint8 // 2-bit value for LUT width control W3 + W2 uint8 // 2-bit value for LUT width control W2 + W1 uint8 // 2-bit value for LUT width control W1 + W0 uint8 // 2-bit value for LUT width control W0 +} + +// NewMSLUTSEL creates a new MSLUTSEL register instance +func NewMSLUTSEL() *MSLUTSEL_Register { + return &MSLUTSEL_Register{ + Register: Register{ + RegisterAddr: MSLUTSEL, + }, + } +} + +// Pack method for MSLUTSEL: combines all the fields into a 32-bit value +func (m *MSLUTSEL_Register) Pack() uint32 { + return uint32(m.X3<<27 | m.X2<<24 | m.X1<<21 | m.W3<<18 | m.W2<<16 | m.W1<<14 | m.W0<<12) // Combine fields into a 32-bit value +} + +// Unpack method for MSLUTSEL: unpacks the 32-bit value into individual fields +func (m *MSLUTSEL_Register) Unpack(registerValue uint32) { + m.X3 = uint8((registerValue >> 27) & 0x07) // Extract the 3 bits for X3 + m.X2 = uint8((registerValue >> 24) & 0x07) // Extract the 3 bits for X2 + m.X1 = uint8((registerValue >> 21) & 0x07) // Extract the 3 bits for X1 + m.W3 = uint8((registerValue >> 18) & 0x03) // Extract the 2 bits for W3 + m.W2 = uint8((registerValue >> 16) & 0x03) // Extract the 2 bits for W2 + m.W1 = uint8((registerValue >> 14) & 0x03) // Extract the 2 bits for W1 + m.W0 = uint8((registerValue >> 12) & 0x03) // Extract the 2 bits for W0 +} + +// MSLUT_Register struct for MSLUT register (32 bits) +type MSLUT_Register struct { + Register + Value uint32 // 32-bit value for microstep table entry +} + +// NewMSLUT creates a new MSLUT register instance +func NewMSLUT() *MSLUT_Register { + return &MSLUT_Register{ + Register: Register{ + RegisterAddr: MSLUT0, + }, + } +} + +// Pack method for MSLUT: returns the 32-bit value for the microstep entry +func (m *MSLUT_Register) Pack() uint32 { + return m.Value // 32 bits, no masking needed +} + +// Unpack method for MSLUT: unpacks the 32-bit value into the microstep entry +func (m *MSLUT_Register) Unpack(registerValue uint32) { + m.Value = registerValue // Direct assignment since it's 32 bits +} + +// MSLUTSTART_Register struct for MSLUTSTART register (16 bits) +type MSLUTSTART_Register struct { + Register + START_SIN int8 // 8-bit signed value for the absolute current at microstep entry 0 + START_SIN90 int8 // 8-bit signed value for the absolute current at microstep entry 256 +} + +// NewMSLUTSTART creates a new MSLUTSTART register instance +func NewMSLUTSTART() *MSLUTSTART_Register { + return &MSLUTSTART_Register{ + Register: Register{ + RegisterAddr: MSLUTSTART, + }, + } +} + +// Pack method for MSLUTSTART: combines START_SIN and START_SIN90 into a 16-bit value +func (m *MSLUTSTART_Register) Pack() uint16 { + return uint16(m.START_SIN) | (uint16(m.START_SIN90) << 8) // Combine the 8-bit values into a 16-bit value +} + +// Unpack method for MSLUTSTART: unpacks the 16-bit value into START_SIN and START_SIN90 +func (m *MSLUTSTART_Register) Unpack(registerValue uint16) { + m.START_SIN = int8(registerValue & 0xFF) // Extract the lower 8 bits for START_SIN + m.START_SIN90 = int8((registerValue >> 8) & 0xFF) // Extract the upper 8 bits for START_SIN90 +} + +// Function to calculate the sine wave values for the microstep table +func calculateSineWaveTable() []int { + // Create a slice to store the sine wave table + table := make([]int, 256) + + // Loop through each table index (i) + for i := 0; i < 256; i++ { + // Calculate the sine value and scale it by 248 + sineValue := 248 * math.Sin(2*math.Pi*float32(i)/1024) + + // Round the result and subtract 1 + roundedValue := int(math.Round(sineValue)) - 1 + + // Store the value in the table + table[i] = roundedValue + } + + return table +} diff --git a/tmc5160/stepper.go b/tmc5160/stepper.go new file mode 100644 index 000000000..62e1bde5a --- /dev/null +++ b/tmc5160/stepper.go @@ -0,0 +1,105 @@ +package tmc5160 + +const maxVMAX = 8388096 + +// PowerStageParameters represents the power stage parameters +type PowerStageParameters struct { + drvStrength uint8 + bbmTime uint8 + bbmClks uint8 +} + +// MotorParameters represents the motor parameters +type MotorParameters struct { + globalScaler uint16 + ihold uint8 + irun uint8 + iholddelay uint8 + pwmGradInitial uint16 + pwmOfsInitial uint16 + freewheeling uint8 +} + +// MotorDirection defines motor direction constants +type MotorDirection uint8 + +const ( + Clockwise MotorDirection = iota + CounterClockwise +) + +const ( + // Common stepper motor angles + StepAngle_1_8 = 1.8 + StepAngle_0_9 = 0.9 + StepAngle_0_72 = 0.72 + StepAngle_1_2 = 1.2 + StepAngle_0_48 = 0.48 + + // Common microstepping options + Step_1 uint8 = 1 + Step_2 uint8 = 2 + Step_4 uint8 = 4 + Step_8 uint8 = 8 + Step_16 uint8 = 16 + Step_32 uint8 = 32 + Step_64 uint8 = 64 + Step_128 uint8 = 128 +) + +const ( + DefaultAngle float32 = StepAngle_1_8 + DefaultGearRatio float32 = 1.0 + DefaultVSupply float32 = 12.0 + DefaultRCoil float32 = 1.2 + DefaultLCoil float32 = 0.005 + DefaultIPeak float32 = 2.0 + DefaultRSense float32 = 0.1 + DefaultFclk uint8 = 12 + DefaultStep_256 = 256 +) + +type Stepper struct { + Angle float32 + GearRatio float32 + VelocitySPS float32 // Velocity in Steps per sec + VSupply float32 + RCoil float32 + LCoil float32 + IPeak float32 + RSense float32 + MSteps uint8 + Fclk uint8 //Clock in Mhz + +} + +// NewStepper function initializes a Stepper with default values used for testing +func NewDefaultStepper() Stepper { + return Stepper{ + Angle: StepAngle_1_8, // Default to 1.8 degrees + GearRatio: 1.0, // Default to no reduction (1:1) + VSupply: 12.0, // Default 12V supply + RCoil: 1.2, // Default coil resistance (1.2 ohms) + LCoil: 0.005, // Default coil inductance (5 mH) + IPeak: 2.0, // Default peak current (2A) + RSense: 0.1, // Default sense resistance (0.1 ohms) + MSteps: Step_16, // Default 16 Microsteps + Fclk: DefaultFclk, + } +} + +// NewStepper initializes a Stepper with user-defined values +func NewStepper(angle float32, gearRatio, vSupply, rCoil, lCoil, iPeak, rSense float32, mSteps uint8, fclk uint8) Stepper { + return Stepper{ + Angle: angle, // User-defined stepper angle (e.g., StepAngle_1_8) + GearRatio: gearRatio, // User-defined gear ratio + VSupply: vSupply, // User-defined supply voltage + RCoil: rCoil, // User-defined coil resistance + LCoil: lCoil, // User-defined coil inductance + IPeak: iPeak, // User-defined peak current + RSense: rSense, // User-defined sense resistance + MSteps: mSteps, // User-defined microstepping setting + Fclk: fclk, // User-defined clock frequency in MHz + + } +} diff --git a/tmc5160/tmc5160.go b/tmc5160/tmc5160.go new file mode 100644 index 000000000..74f04c879 --- /dev/null +++ b/tmc5160/tmc5160.go @@ -0,0 +1,194 @@ +//go:build tinygo + +package tmc5160 + +import "machine" + +type Driver struct { + comm RegisterComm + address uint8 + enablePin machine.Pin + stepper Stepper +} + +func NewDriver(comm RegisterComm, address uint8, enablePin machine.Pin, stepper Stepper) *Driver { + return &Driver{ + comm: comm, + address: address, + enablePin: enablePin, + stepper: stepper, + } +} + +// WriteRegister sends a register write command to the Driver. +func (driver *Driver) 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 Driver and returns the read value. +func (driver *Driver) 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) +} + +// Begin initializes the Driver driver with power and motor parameters +func (driver *Driver) Begin(powerParams PowerStageParameters, motorParams MotorParameters, stepperDirection MotorDirection) bool { + // Clear the reset and charge pump undervoltage flags + gstat := NewGSTAT() + gstat.Reset = true + gstat.UvCp = true + err := driver.WriteRegister(GSTAT, gstat.Pack()) + if err != nil { + return false + } + + // Configure driver settings + drvConf := NewDRV_CONF() + drvConf.DrvStrength = constrain(powerParams.drvStrength, 0, 3) + drvConf.BBMTime = constrain(powerParams.bbmTime, 0, 24) + drvConf.BBMClks = constrain(powerParams.bbmClks, 0, 15) + err = driver.WriteRegister(DRV_CONF, drvConf.Pack()) + if err != nil { + return false + } + + // Set global scaler + err = driver.WriteRegister(GLOBAL_SCALER, uint32(constrain(motorParams.globalScaler, 32, 256))) + if err != nil { + return false + } + + // Set initial currents and delay + iholdrun := NewIHOLD_IRUN() + iholdrun.Ihold = constrain(motorParams.ihold, 0, 31) + iholdrun.Ihold = constrain(motorParams.irun, 0, 31) + iholdrun.IholdDelay = 7 + err = driver.WriteRegister(IHOLD_IRUN, iholdrun.Pack()) + if err != nil { + return false + } + + // Set PWM configuration values + pwmconf := NewPWMCONF() + err = driver.WriteRegister(PWMCONF, 0xC40C001E) + if err != nil { + return false + } // Reset default value pwm_ofs = 196,pwm_grad = 12,pwm_freq = 0, pwm_autoscale = false, pwm_autograd = false,freewheel = 3 + pwmconf.PwmAutoscale = false // Temporarily set to false for setting OFS and GRAD values + _fclk := int(driver.stepper.Fclk) * 1000000 + if _fclk > DEFAULT_F_CLK { + pwmconf.PwmFreq = 0 + } else { + pwmconf.PwmFreq = 0b01 // Recommended: 35kHz with internal 12MHz clock + } + pwmconf.PwmGrad = uint8(motorParams.pwmGradInitial) + pwmconf.PwmOfs = uint8(motorParams.pwmOfsInitial) + pwmconf.Freewheel = motorParams.freewheeling + err = driver.WriteRegister(PWMCONF, pwmconf.Pack()) + if err != nil { + return false + } + + // Enable PWM auto-scaling and gradient adjustment + pwmconf.PwmAutoscale = true + pwmconf.PwmAutograd = true + err = driver.WriteRegister(PWMCONF, pwmconf.Pack()) + if err != nil { + return false + } + + // Recommended chop configuration settings + _chopConf := NewCHOPCONF() + _chopConf.Toff = 5 + _chopConf.Tbl = 2 + _chopConf.HstrtTfd = 4 + _chopConf.HendOffset = 0 + err = driver.WriteRegister(CHOPCONF, _chopConf.Pack()) + if err != nil { + return false + } + rampMode := NewRAMPMODE(driver.comm, driver.address) + // Use position mode + rampMode.SetMode(PositioningMode) + + // Set StealthChop PWM mode and shaft direction + gconf := NewGCONF() + gconf.EnPwmMode = true // Enable stealthChop PWM mode + gconf.Shaft = stepperDirection == Clockwise + err = driver.WriteRegister(GCONF, gconf.Pack()) + if err != nil { + return false + } + + // Set default start, stop, threshold speeds + setRampSpeeds(0.0, 0.1, 0.0) // Start, stop, threshold speeds + + // Set default D1 (must not be = 0 in positioning mode even with V1=0) + err = driver.WriteRegister(D_1, 100) + if err != nil { + return false + } + + return false +} + +func setRampSpeeds(startSpeed float32, stopSpeed float32, transitionSpeed float32) { + //TODO + println("Ramp set to: startSpeed:", startSpeed, "stopSpeed:", stopSpeed, "transitionSpeed:", transitionSpeed) +} + +// setMaxSpeed sets the maximum speed to 0 (placeholder function) +func setMaxSpeed(speed uint32) { + // This is a placeholder function that sets the speed + // Implement the actual logic to set the maximum speed register value + println("Max Speed set to:", speed) +} + +// Dump_TMC reads multiple registers from the Driver and logs their values with their names. +func (driver *Driver) Dump_TMC() error { + registers := []uint8{ + GCONF, CHOPCONF, GSTAT, DRV_STATUS, FACTORY_CONF, IOIN, LOST_STEPS, MSCNT, + MSCURACT, OTP_READ, PWM_SCALE, PWM_AUTO, TSTEP, + } + // Map register address to their names + registerNames := map[uint8]string{ + GCONF: "GCONF", + CHOPCONF: "CHOPCONF", + GSTAT: "GSTAT", + DRV_STATUS: "DRV_STATUS", + FACTORY_CONF: "FACTORY_CONF", + IOIN: "IOIN", + LOST_STEPS: "LOST_STEPS", + MSCNT: "MSCNT", + MSCURACT: "MSCURACT", + OTP_READ: "OTP_READ", + PWM_SCALE: "PWM_SCALE", + PWM_AUTO: "PWM_AUTO", + TSTEP: "TSTEP", + } + // Loop through the register list and read each register + for _, reg := range registers { + // Fetch the register name from the map + regName, exists := registerNames[reg] + if !exists { + regName = "Unknown Register" + } + //println("Reading reg:", regName) + val, err := driver.ReadRegister(reg) + if err != nil { + println("Error reading register", regName, err) + return err + } + // Log the value in the desired format + println("Register", regName, "Value:", val) + } + + return nil +} diff --git a/tmc5160/uartcomm.go b/tmc5160/uartcomm.go new file mode 100644 index 000000000..e37ba5122 --- /dev/null +++ b/tmc5160/uartcomm.go @@ -0,0 +1,115 @@ +//go:build uart + +package tmc5160 + +import ( + "machine" + "time" +) + +// UARTComm implements RegisterComm for UART-based communication with Driver. +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 Driver. +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 Driver. +func (comm *UARTComm) WriteRegister(register uint8, value uint32, driverIndex uint8) error { + // Prepare the data packet (sync byte + address + register + data + checksum) + buffer := []byte{ + 0x05, // Sync byte + comm.address, // Slave address + register | 0x80, // Write command (MSB set 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 Driver + 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 Driver. +func (comm *UARTComm) ReadRegister(register uint8, driverIndex uint8) (uint32, error) { + // Prepare the read command (sync byte + address + register + checksum) + var writeBuffer [4]byte + writeBuffer[0] = 0x05 // Sync byte + writeBuffer[1] = comm.address // 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) // Prepare the buffer to read 8 bytes + 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 (register data starts at byte 3) + 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/tmc5160/utils.go b/tmc5160/utils.go new file mode 100644 index 000000000..ce2dade9f --- /dev/null +++ b/tmc5160/utils.go @@ -0,0 +1,13 @@ +package tmc5160 + +func ToHex(value uint32) string { + hexChars := "0123456789ABCDEF" + result := "" + + for i := 0; i < 8; i++ { // 8 nibbles for a 32-bit number + nibble := (value >> (28 - i*4)) & 0xF + result += string(hexChars[nibble]) + } + + return "0x" + result +} From 9d77cddc27a28cb2c7397a0d70828d43dd1a772e Mon Sep 17 00:00:00 2001 From: hkeni Date: Tue, 10 Dec 2024 21:19:44 -0500 Subject: [PATCH 02/10] Added example code for tmc5160 and smoke test --- examples/tmc5160/main.go | 64 ++++++++++++++++++++++++++++++++++++++++ smoketest.sh | 1 + 2 files changed, 65 insertions(+) create mode 100644 examples/tmc5160/main.go diff --git a/examples/tmc5160/main.go b/examples/tmc5160/main.go new file mode 100644 index 000000000..4159e4e44 --- /dev/null +++ b/examples/tmc5160/main.go @@ -0,0 +1,64 @@ +// Connects to SPI1 on a RP2040 (Pico) +package main + +import ( + "machine" + + "tinygo.org/x/drivers/tmc5160" +) + +func main() { + // Step 1. Setup your protocol. SPI setup shown below + spi := machine.SPI1 + spi.Configure(machine.SPIConfig{ + SCK: machine.GPIO10, + SDI: machine.GPIO11, + SDO: machine.GPIO12, + Frequency: 12000000, // Upto 12 MHZ is pretty stable. Reduce to 5 or 6 Mhz if you are experiencing issues + Mode: 3, + LSBFirst: false, + }) + // Step 2. Set up all associated Pins + csPin0 := machine.GPIO13 + csPin0.Configure(machine.PinConfig{Mode: machine.PinOutput}) + enn0 := machine.GPIO18 + enn0.Configure(machine.PinConfig{Mode: machine.PinOutput}) + + // csPins is a map of all chip select pins in a multi driver setup. + //Only one pin csPin0 mapped to "0"is shown in this example, but add more mappings as required + csPins := map[uint8]machine.Pin{0: csPin0} + //bind csPin to driverAdddress + driverAddress := uint8(0) // Let's assume we are working with driver at address 0x01 + // Step 3. Bind the communication interface to the protocol + comm := tmc5160.NewSPIComm(*spi, csPins) + // Step 4. Define your stepper like this below + //stepper := tmc5160.NewStepper(angle , gearRatio vSupply rCoil , lCoil , iPeak , rSense , mSteps, fclk ) + stepper := tmc5160.NewDefaultStepper() // Default Stepper should be used only for testing. + // Step 5. Instantiate your driver + driver := tmc5160.NewDriver( + comm, + driverAddress, + enn0, + stepper) + + // Setting and getting mode + rampMode := tmc5160.NewRAMPMODE(comm, driverAddress) + err := rampMode.SetMode(tmc5160.PositioningMode) + if err != nil { + return + } + mode, err := rampMode.GetMode() + if err != nil { + println("Error getting mode:", err) + } else { + println("Current Mode:", mode) + } + + // Read GCONF register + GCONF := tmc5160.NewGCONF() + gconfVal, err := driver.ReadRegister(tmc5160.GCONF) + // Uppack the register to get all the bits and bytes of the register + GCONF.Unpack(gconfVal) + //E.g. MultiStepFlit is retrieved from the GCONF register + println("GCONF:MultiStepFlit:", GCONF.MultistepFilt) +} diff --git a/smoketest.sh b/smoketest.sh index c89ba0410..92b031d19 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.hex -target=pico ./examples/tmc5160/main.go # network examples (espat) tinygo build -size short -o ./build/test.hex -target=challenger-rp2040 ./examples/net/ntpclient/ # network examples (wifinina) From f8507b5f52c9a79bef2d88e8a85c4b4dd990d6b5 Mon Sep 17 00:00:00 2001 From: Amken USA Date: Wed, 11 Dec 2024 14:55:40 -0500 Subject: [PATCH 03/10] Update go.mod --- go.mod | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index fc1dc70b6..75cd6b635 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module tinygo.org/x/drivers -go 1.22.1 +go 1.21 -toolchain go1.23.3 +toolchain go1.21 require ( github.com/eclipse/paho.mqtt.golang v1.2.0 From 5ef87d56de1375525b5e22c9330f3ff74bb9ae09 Mon Sep 17 00:00:00 2001 From: Amken USA <166057890+amken3d@users.noreply.github.com> Date: Wed, 11 Dec 2024 16:55:44 +0000 Subject: [PATCH 04/10] Updated mod file --- go.mod | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 75cd6b635..530f060e4 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module tinygo.org/x/drivers -go 1.21 +go 1.22.1 -toolchain go1.21 +toolchain go1.23.1 require ( github.com/eclipse/paho.mqtt.golang v1.2.0 From 09ac2c44060b0da0830a036b4ae51d6e3998dd01 Mon Sep 17 00:00:00 2001 From: Amken USA <166057890+amken3d@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:17:24 +0000 Subject: [PATCH 05/10] Cleaned up commented out code and updated readme. --- tmc5160/README.MD | 110 ++++++++++++++++++++++++-------------------- tmc5160/SPIcomm.go | 2 +- tmc5160/tmc5160.go | 18 -------- tmc5160/uartcomm.go | 18 +------- 4 files changed, 63 insertions(+), 85 deletions(-) diff --git a/tmc5160/README.MD b/tmc5160/README.MD index a8b4fdb20..b0b4916a0 100644 --- a/tmc5160/README.MD +++ b/tmc5160/README.MD @@ -26,13 +26,9 @@ To use the TMC5160 driver, you'll need to have **TinyGo** installed. You can ins Add the module ```bash -go get github.com/amken3d/tinygo_tmc5160/tmc5160 -``` -Alternate method is to use the tinygo official drivers repo - -```aiignore import "tinygo.org/x/drivers/tmc5160" ``` + ### Communication Modes The TMC5160 supports two communication modes for controlling the motor: @@ -97,51 +93,72 @@ driver.WriteRegister(tmc5160.GCONF, 0x01) Here’s a simple example of how to use the TMC5160 driver with SPI and UART modes: ```aiignore +// Connects to SPI1 on a RP2040 (Pico) package main import ( - "fmt" - "machine" - "time" - "tmc5160" + "machine" + + "tinygo.org/x/drivers/tmc5160" ) func main() { - // SPI setup - spi := machine.SPI1 - csPin := machine.GPIO13 - spi.Configure(machine.SPIConfig{ - SCK: machine.GPIO10, - SDI: machine.GPIO11, - SDO: machine.GPIO12, - Frequency: 5000000, - Mode: 3, - LSBFirst: false, - }) - - csPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - csPins := map[uint8]machine.Pin{0: csPin} - - comm := tmc5160.NewSPIComm(*spi, csPins) - driver := tmc5160.NewTMC5160(comm, 0) - - // Setting and getting mode - rampMode := tmc5160.NewRAMPMODE(comm) - rampMode.SetMode(tmc5160.PositioningMode) - mode, err := rampMode.GetMode() - if err != nil { - fmt.Println("Error getting mode:", err) - } else { - fmt.Println("Current Mode:", mode) - } - - // Read GCONF register - GCONF := tmc5160.NewGCONF() - gconfVal, err := driver.ReadRegister(tmc5160.GCONF) - GCONF.Unpack(gconfVal) - fmt.Println("GCONF:", GCONF) + // Step 1. Setup your protocol. SPI setup shown below + spi := machine.SPI1 + spi.Configure(machine.SPIConfig{ + SCK: machine.GPIO10, + SDI: machine.GPIO11, + SDO: machine.GPIO12, + Frequency: 12000000, // Upto 12 MHZ is pretty stable. Reduce to 5 or 6 Mhz if you are experiencing issues + Mode: 3, + LSBFirst: false, + }) + // Step 2. Set up all associated Pins + csPin0 := machine.GPIO13 + csPin0.Configure(machine.PinConfig{Mode: machine.PinOutput}) + enn0 := machine.GPIO18 + enn0.Configure(machine.PinConfig{Mode: machine.PinOutput}) + + // csPins is a map of all chip select pins in a multi driver setup. + //Only one pin csPin0 mapped to "0"is shown in this example, but add more mappings as required + csPins := map[uint8]machine.Pin{0: csPin0} + //bind csPin to driverAdddress + driverAddress := uint8(0) // Let's assume we are working with driver at address 0x01 + // Step 3. Bind the communication interface to the protocol + comm := tmc5160.NewSPIComm(*spi, csPins) + // Step 4. Define your stepper like this below + //stepper := tmc5160.NewStepper(angle , gearRatio vSupply rCoil , lCoil , iPeak , rSense , mSteps, fclk ) + stepper := tmc5160.NewDefaultStepper() // Default Stepper should be used only for testing. + // Step 5. Instantiate your driver + driver := tmc5160.NewDriver( + comm, + driverAddress, + enn0, + stepper) + + // Setting and getting mode + rampMode := tmc5160.NewRAMPMODE(comm, driverAddress) + err := rampMode.SetMode(tmc5160.PositioningMode) + if err != nil { + return + } + mode, err := rampMode.GetMode() + if err != nil { + println("Error getting mode:", err) + } else { + println("Current Mode:", mode) + } + + // Read GCONF register + GCONF := tmc5160.NewGCONF() + gconfVal, err := driver.ReadRegister(tmc5160.GCONF) + // Uppack the register to get all the bits and bytes of the register + GCONF.Unpack(gconfVal) + //E.g. MultiStepFlit is retrieved from the GCONF register + println("GCONF:MultiStepFlit:", GCONF.MultistepFilt) } + ``` ## Reading and Writing Registers @@ -188,14 +205,7 @@ Reads a value from the specified register. ## License -This project is licensed under the MIT License - see the LICENSE file for details. - +This project is licensed under the MIT License -### Key Sections: -1. **Installation**: Explains how to install TinyGo and clone the repository. -2. **Communication Modes**: Describes how to set up SPI and UART communication with the TMC5160. -3. **Usage Example**: Provides an example of how to use the driver, including how to set modes and read/write registers. -4. **API Reference**: Lists functions available in the TMC5160 driver, such as `WriteRegister`, `ReadRegister`, and constructors like `NewSPIComm`. -This README provides a comprehensive overview and guide on how to use the TMC5160 driver in both SPI and UART communication modes. diff --git a/tmc5160/SPIcomm.go b/tmc5160/SPIcomm.go index 34bae0640..17c32a585 100644 --- a/tmc5160/SPIcomm.go +++ b/tmc5160/SPIcomm.go @@ -126,7 +126,7 @@ func spiTransfer40(spi *machine.SPI, register uint8, txData uint32) (uint32, err if err != nil { return 0, err } - //println("Received", rx[0], rx[1], rx[2], rx[3], rx[4]) + // Combine the received bytes into a 32-bit response, ignore the address byte rxData := uint32(rx[1])<<24 | uint32(rx[2])<<16 | uint32(rx[3])<<8 | uint32(rx[4]) diff --git a/tmc5160/tmc5160.go b/tmc5160/tmc5160.go index 74f04c879..f8fa5714a 100644 --- a/tmc5160/tmc5160.go +++ b/tmc5160/tmc5160.go @@ -115,10 +115,7 @@ func (driver *Driver) Begin(powerParams PowerStageParameters, motorParams MotorP return false } rampMode := NewRAMPMODE(driver.comm, driver.address) - // Use position mode rampMode.SetMode(PositioningMode) - - // Set StealthChop PWM mode and shaft direction gconf := NewGCONF() gconf.EnPwmMode = true // Enable stealthChop PWM mode gconf.Shaft = stepperDirection == Clockwise @@ -139,17 +136,6 @@ func (driver *Driver) Begin(powerParams PowerStageParameters, motorParams MotorP return false } -func setRampSpeeds(startSpeed float32, stopSpeed float32, transitionSpeed float32) { - //TODO - println("Ramp set to: startSpeed:", startSpeed, "stopSpeed:", stopSpeed, "transitionSpeed:", transitionSpeed) -} - -// setMaxSpeed sets the maximum speed to 0 (placeholder function) -func setMaxSpeed(speed uint32) { - // This is a placeholder function that sets the speed - // Implement the actual logic to set the maximum speed register value - println("Max Speed set to:", speed) -} // Dump_TMC reads multiple registers from the Driver and logs their values with their names. func (driver *Driver) Dump_TMC() error { @@ -157,7 +143,6 @@ func (driver *Driver) Dump_TMC() error { GCONF, CHOPCONF, GSTAT, DRV_STATUS, FACTORY_CONF, IOIN, LOST_STEPS, MSCNT, MSCURACT, OTP_READ, PWM_SCALE, PWM_AUTO, TSTEP, } - // Map register address to their names registerNames := map[uint8]string{ GCONF: "GCONF", CHOPCONF: "CHOPCONF", @@ -173,20 +158,17 @@ func (driver *Driver) Dump_TMC() error { PWM_AUTO: "PWM_AUTO", TSTEP: "TSTEP", } - // Loop through the register list and read each register for _, reg := range registers { // Fetch the register name from the map regName, exists := registerNames[reg] if !exists { regName = "Unknown Register" } - //println("Reading reg:", regName) val, err := driver.ReadRegister(reg) if err != nil { println("Error reading register", regName, err) return err } - // Log the value in the desired format println("Register", regName, "Value:", val) } diff --git a/tmc5160/uartcomm.go b/tmc5160/uartcomm.go index e37ba5122..37321a593 100644 --- a/tmc5160/uartcomm.go +++ b/tmc5160/uartcomm.go @@ -23,26 +23,22 @@ func NewUARTComm(uart machine.UART, address uint8) *UARTComm { // Setup initializes the UART communication with the Driver. 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 Driver. +// Prepare the data packet (sync byte + address + register + data + checksum) func (comm *UARTComm) WriteRegister(register uint8, value uint32, driverIndex uint8) error { - // Prepare the data packet (sync byte + address + register + data + checksum) + buffer := []byte{ 0x05, // Sync byte comm.address, // Slave address @@ -52,8 +48,6 @@ func (comm *UARTComm) WriteRegister(register uint8, value uint32, driverIndex ui 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 @@ -68,7 +62,6 @@ func (comm *UARTComm) WriteRegister(register uint8, value uint32, driverIndex ui done <- nil }() - // Implementing timeout using a 100ms timer select { case err := <-done: return err @@ -85,8 +78,6 @@ func (comm *UARTComm) ReadRegister(register uint8, driverIndex uint8) (uint32, e writeBuffer[1] = comm.address // 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[:]) @@ -94,11 +85,8 @@ func (comm *UARTComm) ReadRegister(register uint8, driverIndex uint8) (uint32, e 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] @@ -106,8 +94,6 @@ func (comm *UARTComm) ReadRegister(register uint8, driverIndex uint8) (uint32, e if checksum != readBuffer[7] { return 0, CustomError("checksum error") } - - // Return the value from the register (register data starts at byte 3) 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") From 55290a1c2b98d03beaf6f3a202d4392daabda866 Mon Sep 17 00:00:00 2001 From: Amken USA <166057890+amken3d@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:29:59 +0000 Subject: [PATCH 06/10] ran go fmt --- tmc5160/SPIcomm.go | 2 +- tmc5160/tmc5160.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tmc5160/SPIcomm.go b/tmc5160/SPIcomm.go index 17c32a585..94f38767e 100644 --- a/tmc5160/SPIcomm.go +++ b/tmc5160/SPIcomm.go @@ -126,7 +126,7 @@ func spiTransfer40(spi *machine.SPI, register uint8, txData uint32) (uint32, err if err != nil { return 0, err } - + // Combine the received bytes into a 32-bit response, ignore the address byte rxData := uint32(rx[1])<<24 | uint32(rx[2])<<16 | uint32(rx[3])<<8 | uint32(rx[4]) diff --git a/tmc5160/tmc5160.go b/tmc5160/tmc5160.go index f8fa5714a..e49cfdac0 100644 --- a/tmc5160/tmc5160.go +++ b/tmc5160/tmc5160.go @@ -136,7 +136,6 @@ func (driver *Driver) Begin(powerParams PowerStageParameters, motorParams MotorP return false } - // Dump_TMC reads multiple registers from the Driver and logs their values with their names. func (driver *Driver) Dump_TMC() error { registers := []uint8{ From 5cf8a2e52c92fb7e008b0c168c0f0c007a4e08a6 Mon Sep 17 00:00:00 2001 From: hkeni Date: Thu, 12 Dec 2024 10:53:32 -0500 Subject: [PATCH 07/10] Fixed setrampspeed func --- tmc5160/tmc5160.go | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/tmc5160/tmc5160.go b/tmc5160/tmc5160.go index e49cfdac0..0bf2b109d 100644 --- a/tmc5160/tmc5160.go +++ b/tmc5160/tmc5160.go @@ -2,7 +2,10 @@ package tmc5160 -import "machine" +import ( + "github.com/orsinium-labs/tinymath" + "machine" +) type Driver struct { comm RegisterComm @@ -125,7 +128,7 @@ func (driver *Driver) Begin(powerParams PowerStageParameters, motorParams MotorP } // Set default start, stop, threshold speeds - setRampSpeeds(0.0, 0.1, 0.0) // Start, stop, threshold speeds + driver.setRampSpeeds(0.0, 0.1, 0.0) // Start, stop, threshold speeds // Set default D1 (must not be = 0 in positioning mode even with V1=0) err = driver.WriteRegister(D_1, 100) @@ -135,6 +138,22 @@ func (driver *Driver) Begin(powerParams PowerStageParameters, motorParams MotorP return false } +func (driver *Driver) setRampSpeeds(startSpeed float32, stopSpeed float32, transitionSpeed float32) { + str := driver.stepper.DesiredSpeedToTSTEP(uint32(startSpeed)) + stp := driver.stepper.DesiredSpeedToTSTEP(uint32(stopSpeed)) + ts := driver.stepper.DesiredSpeedToTSTEP(uint32(transitionSpeed)) + driver.WriteRegister(VSTART, uint32(tinymath.Min(0x3FFFF, float32(str)))) + driver.WriteRegister(VSTOP, uint32(tinymath.Min(0x3FFFF, float32(stp)))) + driver.WriteRegister(V_1, uint32(tinymath.Min(0xFFFFF, float32(ts)))) + println("Ramp set to: startSpeed:", startSpeed, "stopSpeed:", stopSpeed, "transitionSpeed:", transitionSpeed) +} + +// setMaxSpeed sets the maximum speed to 0 (placeholder function) +func setMaxSpeed(speed uint32) { + // This is a placeholder function that sets the speed + // Implement the actual logic to set the maximum speed register value + println("Max Speed set to:", speed) +} // Dump_TMC reads multiple registers from the Driver and logs their values with their names. func (driver *Driver) Dump_TMC() error { From 70d09e8e3c3351b6efea78c87814c5a72b2ee3ff Mon Sep 17 00:00:00 2001 From: hkeni Date: Sat, 14 Dec 2024 01:23:23 -0500 Subject: [PATCH 08/10] Removed spi1 pin setup in example.go. Renamed SPIComm to spicomm.go --- examples/tmc5160/main.go | 3 --- tmc5160/{SPIcomm.go => spicomm.go} | 0 2 files changed, 3 deletions(-) rename tmc5160/{SPIcomm.go => spicomm.go} (100%) diff --git a/examples/tmc5160/main.go b/examples/tmc5160/main.go index 4159e4e44..309ea14b3 100644 --- a/examples/tmc5160/main.go +++ b/examples/tmc5160/main.go @@ -11,9 +11,6 @@ func main() { // Step 1. Setup your protocol. SPI setup shown below spi := machine.SPI1 spi.Configure(machine.SPIConfig{ - SCK: machine.GPIO10, - SDI: machine.GPIO11, - SDO: machine.GPIO12, Frequency: 12000000, // Upto 12 MHZ is pretty stable. Reduce to 5 or 6 Mhz if you are experiencing issues Mode: 3, LSBFirst: false, diff --git a/tmc5160/SPIcomm.go b/tmc5160/spicomm.go similarity index 100% rename from tmc5160/SPIcomm.go rename to tmc5160/spicomm.go From e9577522b837de5cb2a98f1cab1c37bc3f726011 Mon Sep 17 00:00:00 2001 From: hkeni Date: Tue, 17 Dec 2024 12:16:46 -0500 Subject: [PATCH 09/10] Removed unused test file --- tmc5160/func_test.go | 61 -------------------------------------------- 1 file changed, 61 deletions(-) delete mode 100644 tmc5160/func_test.go diff --git a/tmc5160/func_test.go b/tmc5160/func_test.go deleted file mode 100644 index 72a1450a2..000000000 --- a/tmc5160/func_test.go +++ /dev/null @@ -1,61 +0,0 @@ -//go:build test - -package tmc5160 - -import ( - "log" - "testing" -) - -func TestCurrentVelocityToVMAX(t *testing.T) { - stepper := NewDefaultStepper() - log.Printf("Current Velocity = %f", stepper.VelocitySPS) - // Expected output based on the formula - expectedVMAX := 1398 - - result := stepper.CurrentVelocityToVMAX() - - if result != uint32(expectedVMAX) { - t.Errorf("CurrentVelocityToVMAX() = %d; expected %v", result, expectedVMAX) - } -} - -func TestDesiredVelocityToVMAX(t *testing.T) { - stepper := NewDefaultStepper() - - // Test with a desired velocity of 1500 steps per second - desiredVelocity := float32(51200.0) - expectedVMAX := 71583 - - result := stepper.DesiredVelocityToVMAX(desiredVelocity) - - if result != uint32(expectedVMAX) { - t.Errorf("DesiredVelocityToVMAX() = %d; expected %d", result, expectedVMAX) - } -} - -func TestDesiredAccelToAMAX(t *testing.T) { - stepper := NewDefaultStepper() - - // Test desired velocity 1000 steps per second, and desired acceleration 1.5 sec from 0 to VMAX - dVel := float32(51200.0) - dAcc := float32(1.50) - expectedAMAX := 521 - result := stepper.DesiredAccelToAMAX(dAcc, dVel) - if result != uint32(expectedAMAX) { - t.Errorf("DesiredAccelToAMAX() = %d; expected %d", result, expectedAMAX) - } -} - -func TestVMAXToTSTEP(t *testing.T) { - stepper := NewDefaultStepper() - // Test with a threshold speed of 1000 Hz - thrsSpeed := uint32(5145) - expectedTSTEP := uint32(204) - - result := stepper.VMAXToTSTEP(thrsSpeed) - - if result != (expectedTSTEP) { - t.Errorf("VMAXToTSTEP() = %d; expected %d", result, expectedTSTEP) - } -} From 1c92aec11e43451d18ed1370c4365ed01293e098 Mon Sep 17 00:00:00 2001 From: hkeni Date: Thu, 19 Dec 2024 14:42:47 -0500 Subject: [PATCH 10/10] Removed commented line --- tmc5160/spicomm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmc5160/spicomm.go b/tmc5160/spicomm.go index 94f38767e..4127f0fef 100644 --- a/tmc5160/spicomm.go +++ b/tmc5160/spicomm.go @@ -118,7 +118,7 @@ func spiTransfer40(spi *machine.SPI, register uint8, txData uint32) (uint32, err byte(txData >> 8), // Next 8 bits of data byte(txData), // Lower 8 bits of data } - //println("Sending", tx[0], tx[1], tx[2], tx[3], tx[4]) + rx := make([]byte, 5) // Perform the SPI transaction