Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added TMC5160 support #725

Merged
merged 10 commits into from
Jan 18, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
module tinygo.org/x/drivers

go 1.18
go 1.22.1
deadprogram marked this conversation as resolved.
Show resolved Hide resolved

toolchain go1.23.3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here as above.


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
)
8 changes: 6 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,25 @@ 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=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
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=
Expand Down
201 changes: 201 additions & 0 deletions tmc5160/README.MD
Original file line number Diff line number Diff line change
@@ -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.
134 changes: 134 additions & 0 deletions tmc5160/SPIcomm.go
Original file line number Diff line number Diff line change
@@ -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])
deadprogram marked this conversation as resolved.
Show resolved Hide resolved
// 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
}
Loading
Loading