-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tic: Add I²C support for Tic Stepper Motor Controllers (#78)
* tic: Add I²C support for Tic Stepper Motor Controllers (#1) * Initialise buffers as arrays instead of slices * Remove redundant step modes and improve docs * Make Offset type private * Remove shadowed variable
- Loading branch information
Showing
6 changed files
with
2,349 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// Copyright 2024 The Periph Authors. All rights reserved. | ||
// Use of this source code is governed under the Apache License, Version 2.0 | ||
// that can be found in the LICENSE file. | ||
|
||
// Package tic interfaces with Tic Stepper Motor Controllers via I²C. | ||
// | ||
// # More Details | ||
// | ||
// See https://www.pololu.com/category/212/tic-stepper-motor-controllers for | ||
// more details about the device range. | ||
// | ||
// # Product Pages | ||
// | ||
// Tic T500: https://www.pololu.com/product/3134 | ||
// | ||
// Tic T834: https://www.pololu.com/product/3132 | ||
// | ||
// Tic T825: https://www.pololu.com/product/3130 | ||
// | ||
// Tic T249: https://www.pololu.com/product/3138 | ||
// | ||
// Tic 36v4: https://www.pololu.com/product/3140 | ||
package tic |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// Copyright 2024 The Periph Authors. All rights reserved. | ||
// Use of this source code is governed under the Apache License, Version 2.0 | ||
// that can be found in the LICENSE file. | ||
|
||
package tic_test | ||
|
||
import ( | ||
"log" | ||
"time" | ||
|
||
"periph.io/x/conn/v3/i2c/i2creg" | ||
"periph.io/x/conn/v3/physic" | ||
"periph.io/x/devices/v3/tic" | ||
"periph.io/x/host/v3" | ||
) | ||
|
||
func Example() { | ||
// Make sure periph is initialized. | ||
if _, err := host.Init(); err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// Open default I²C bus. | ||
bus, err := i2creg.Open("") | ||
if err != nil { | ||
log.Fatalf("failed to open I²C: %v", err) | ||
} | ||
defer bus.Close() | ||
|
||
// Create a new motor controller. | ||
dev, err := tic.NewI2C(bus, tic.Tic36v4, tic.I2CAddr) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// Set the current limit with respect to the motor. | ||
if err := dev.SetCurrentLimit(1000 * physic.MilliAmpere); err != nil { | ||
log.Fatalf("failed to set current limit: %v", err) | ||
} | ||
|
||
// The "Exit safe start" command is required before the motor can move. | ||
if err := dev.ExitSafeStart(); err != nil { | ||
log.Fatalf("failed to exit safe start: err %v", err) | ||
} | ||
|
||
// Set the target velocity to 200 microsteps per second. | ||
if err := dev.SetTargetVelocity(2000000); err != nil { | ||
log.Fatalf("failed to set target velocity: err %v", err) | ||
} | ||
|
||
// Use a ticker to frequently send commands before the timeout period | ||
// elapses (1000ms default). | ||
ticker := time.NewTicker(900 * time.Millisecond) | ||
defer ticker.Stop() | ||
|
||
// Stop after 3 seconds. | ||
stop := time.After(3 * time.Second) | ||
|
||
for { | ||
select { | ||
case <-stop: | ||
return | ||
case <-ticker.C: | ||
// Any command sent to the Tic will reset the timeout. However, | ||
// this can be done explicitly using ResetCommandTimeout(). | ||
if err := dev.ResetCommandTimeout(); err != nil { | ||
log.Fatalf("failed to reset command timeout: %v", err) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
// Copyright 2024 The Periph Authors. All rights reserved. | ||
// Use of this source code is governed under the Apache License, Version 2.0 | ||
// that can be found in the LICENSE file. | ||
|
||
package tic | ||
|
||
import ( | ||
"encoding/binary" | ||
) | ||
|
||
// getVar8 reads an 8 bit value from the Tic at a given register offset. | ||
func (d *Dev) getVar8(offset offset) (uint8, error) { | ||
const length = 1 | ||
buffer, err := d.getSegment(cmdGetVariable, offset, length) | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
return buffer[0], nil | ||
} | ||
|
||
// getVar16 reads a 16 bit value from the Tic at a given register offset. | ||
func (d *Dev) getVar16(offset offset) (uint16, error) { | ||
const length = 2 | ||
buffer, err := d.getSegment(cmdGetVariable, offset, length) | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
return binary.LittleEndian.Uint16(buffer), nil | ||
} | ||
|
||
// getVar32 reads a 32 bit value from the Tic at a given register offset. | ||
func (d *Dev) getVar32(offset offset) (uint32, error) { | ||
const length = 4 | ||
buffer, err := d.getSegment(cmdGetVariable, offset, length) | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
return binary.LittleEndian.Uint32(buffer), nil | ||
} | ||
|
||
// commandQuick sends a command without additional data. | ||
func (d *Dev) commandQuick(cmd command) error { | ||
writeBuf := [1]byte{uint8(cmd)} | ||
err := d.c.Tx(writeBuf[:], nil) | ||
return err | ||
} | ||
|
||
// commandW7 sends a command with a 7 bit value. The MSB of val is ignored. | ||
func (d *Dev) commandW7(cmd command, val uint8) error { | ||
writeBuf := [2]byte{byte(cmd), val & 0x7F} | ||
err := d.c.Tx(writeBuf[:], nil) | ||
return err | ||
} | ||
|
||
// commandW32 sends a command with a 32 bit value. | ||
func (d *Dev) commandW32(cmd command, val uint32) error { | ||
writeBuf := [5]byte{byte(cmd)} | ||
writeBuf[0] = byte(cmd) | ||
binary.LittleEndian.PutUint32(writeBuf[1:], val) // write the uint32 value | ||
|
||
err := d.c.Tx(writeBuf[:], nil) | ||
return err | ||
} | ||
|
||
// getSegment sends a command and receives "length" bytes back. | ||
func (d *Dev) getSegment( | ||
cmd command, offset offset, length uint, | ||
) ([]byte, error) { | ||
// Transmit command and offset value | ||
writeBuf := [2]byte{byte(cmd), byte(offset)} | ||
err := d.c.Tx(writeBuf[:], nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Read the requested number of bytes | ||
readBuf := make([]byte, length) | ||
err = d.c.Tx(nil, readBuf) | ||
return readBuf, err | ||
} |
Oops, something went wrong.