mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
Added TMC5160 support (#725)
TMC5160: Added TMC5160 support * Added example code for tmc5160 and smoke test * Update go.mod * Updated mod file * Cleaned up commented out code and updated readme. * ran go fmt * Fixed setrampspeed func * Removed spi1 pin setup in example.go. Renamed SPIComm to spicomm.go * Removed unused test file * Removed commented line
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
// 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{
|
||||
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)
|
||||
}
|
||||
@@ -1,20 +1,23 @@
|
||||
module tinygo.org/x/drivers
|
||||
|
||||
go 1.18
|
||||
go 1.22.1
|
||||
|
||||
toolchain go1.23.1
|
||||
|
||||
require (
|
||||
github.com/eclipse/paho.mqtt.golang v1.2.0
|
||||
github.com/frankban/quicktest v1.10.2
|
||||
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
|
||||
)
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
# 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
|
||||
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
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
## 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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
|
||||
rx := make([]byte, 5)
|
||||
|
||||
// Perform the SPI transaction
|
||||
err := spi.Tx(tx, rx)
|
||||
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])
|
||||
|
||||
return rxData, nil
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
//go:build tinygo
|
||||
|
||||
package tmc5160
|
||||
|
||||
import (
|
||||
"github.com/orsinium-labs/tinymath"
|
||||
"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)
|
||||
rampMode.SetMode(PositioningMode)
|
||||
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
|
||||
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)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
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 {
|
||||
registers := []uint8{
|
||||
GCONF, CHOPCONF, GSTAT, DRV_STATUS, FACTORY_CONF, IOIN, LOST_STEPS, MSCNT,
|
||||
MSCURACT, OTP_READ, PWM_SCALE, PWM_AUTO, TSTEP,
|
||||
}
|
||||
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",
|
||||
}
|
||||
for _, reg := range registers {
|
||||
// Fetch the register name from the map
|
||||
regName, exists := registerNames[reg]
|
||||
if !exists {
|
||||
regName = "Unknown Register"
|
||||
}
|
||||
val, err := driver.ReadRegister(reg)
|
||||
if err != nil {
|
||||
println("Error reading register", regName, err)
|
||||
return err
|
||||
}
|
||||
println("Register", regName, "Value:", val)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
//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 {
|
||||
if comm.uart == (machine.UART{}) {
|
||||
return CustomError("UART not initialized")
|
||||
}
|
||||
err := comm.uart.Configure(machine.UARTConfig{
|
||||
BaudRate: 115200,
|
||||
})
|
||||
if err != nil {
|
||||
return CustomError("Failed to configure UART")
|
||||
}
|
||||
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 {
|
||||
|
||||
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
|
||||
}
|
||||
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
|
||||
}()
|
||||
|
||||
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
|
||||
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
|
||||
}()
|
||||
select {
|
||||
case readBuffer := <-done:
|
||||
checksum := byte(0)
|
||||
for i := 0; i < 7; i++ {
|
||||
checksum ^= readBuffer[i]
|
||||
}
|
||||
if checksum != readBuffer[7] {
|
||||
return 0, CustomError("checksum error")
|
||||
}
|
||||
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")
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user