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
pull/81/head v3.7.2
Zac 2 years ago committed by GitHub
parent 536214b3ab
commit 5bc0352f3a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -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

@ -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)
}
}
}
}

@ -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
}

@ -0,0 +1,347 @@
// 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 (
"bytes"
"testing"
"periph.io/x/conn/v3/i2c"
"periph.io/x/conn/v3/i2c/i2ctest"
)
func TestGetVar8(t *testing.T) {
for _, test := range []struct {
name string
offset offset
ops []i2ctest.IO
want uint8
expectErr bool
}{
{
name: "success",
offset: 0xAA,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xA1, 0xAA}},
{Addr: I2CAddr, R: []byte{0xAB}},
},
want: 0xAB,
expectErr: false,
},
{
name: "no bytes received",
offset: 0xAA,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xA1, 0xAA}},
{Addr: I2CAddr, R: []byte{}},
},
expectErr: true,
},
} {
t.Run(test.name, func(t *testing.T) {
b := i2ctest.Playback{
Ops: test.ops,
DontPanic: true,
}
defer b.Close()
dev := Dev{
c: &i2c.Dev{Bus: &b, Addr: I2CAddr},
variant: Tic36v4,
}
got, err := dev.getVar8(test.offset)
if test.expectErr {
if err == nil {
t.Fatalf("expected error, got: %v", err)
}
} else {
if err != nil {
t.Fatal(err)
}
if got != test.want {
t.Fatalf("wanted: %d, got: %d", test.want, got)
}
}
})
}
}
func TestGetVar16(t *testing.T) {
for _, test := range []struct {
name string
offset offset
ops []i2ctest.IO
want uint16
expectErr bool
}{
{
name: "success",
offset: 0xAA,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xA1, 0xAA}},
{Addr: I2CAddr, R: []byte{0xCD, 0xAB}},
},
want: 0xABCD,
expectErr: false,
},
{
name: "no bytes received",
offset: 0xAA,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xA1, 0xAA}},
{Addr: I2CAddr, R: []byte{}},
},
expectErr: true,
},
} {
t.Run(test.name, func(t *testing.T) {
b := i2ctest.Playback{
Ops: test.ops,
DontPanic: true,
}
defer b.Close()
dev := Dev{
c: &i2c.Dev{Bus: &b, Addr: I2CAddr},
variant: Tic36v4,
}
got, err := dev.getVar16(test.offset)
if test.expectErr {
if err == nil {
t.Fatalf("expected error, got: %v", err)
}
} else {
if err != nil {
t.Fatal(err)
}
if got != test.want {
t.Fatalf("wanted: %d, got: %d", test.want, got)
}
}
})
}
}
func TestGetVar32(t *testing.T) {
for _, test := range []struct {
name string
offset offset
ops []i2ctest.IO
want uint32
expectErr bool
}{
{
name: "success",
offset: 0xAA,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xA1, 0xAA}},
{Addr: I2CAddr, R: []byte{0xEF, 0xBE, 0xAD, 0xDE}},
},
want: 0xDEADBEEF,
expectErr: false,
},
{
name: "no bytes received",
offset: 0xAA,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xA1, 0xAA}},
{Addr: I2CAddr, R: []byte{}},
},
expectErr: true,
},
} {
t.Run(test.name, func(t *testing.T) {
b := i2ctest.Playback{
Ops: test.ops,
DontPanic: true,
}
defer b.Close()
dev := Dev{
c: &i2c.Dev{Bus: &b, Addr: I2CAddr},
variant: Tic36v4,
}
got, err := dev.getVar32(test.offset)
if test.expectErr {
if err == nil {
t.Fatalf("expected error, got: %v", err)
}
} else {
if err != nil {
t.Fatal(err)
}
if got != test.want {
t.Fatalf("wanted: %d, got: %d", test.want, got)
}
}
})
}
}
func TestCommandQuick(t *testing.T) {
const cmd = 0xAA
b := i2ctest.Playback{
Ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{cmd}},
},
DontPanic: true,
}
defer b.Close()
dev := Dev{
c: &i2c.Dev{Bus: &b, Addr: I2CAddr},
variant: Tic36v4,
}
err := dev.commandQuick(cmd)
if err != nil {
t.Error(err)
}
}
func TestCommandW7(t *testing.T) {
for _, test := range []struct {
name string
cmd command
val uint8
ops []i2ctest.IO
}{
{
name: "success",
cmd: 0xAA,
val: 0x0B,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xAA, 0x0B}},
},
},
{
name: "val MSB truncated",
cmd: 0xAA,
val: 0b1111_1111,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xAA, 0b0111_1111}},
},
},
} {
b := i2ctest.Playback{
Ops: test.ops,
DontPanic: true,
}
defer b.Close()
dev := Dev{
c: &i2c.Dev{Bus: &b, Addr: I2CAddr},
variant: Tic36v4,
}
err := dev.commandW7(test.cmd, test.val)
if err != nil {
t.Fatal(err)
}
}
}
func TestCommandW32(t *testing.T) {
const (
cmd = 0xAA
val uint32 = 0xBBCCDDEE
)
b := i2ctest.Playback{
Ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xAA, 0xEE, 0xDD, 0xCC, 0xBB}},
},
DontPanic: true,
}
defer b.Close()
dev := Dev{
c: &i2c.Dev{Bus: &b, Addr: I2CAddr},
variant: Tic36v4,
}
err := dev.commandW32(cmd, val)
if err != nil {
t.Error(err)
}
}
func TestGetSegment(t *testing.T) {
for _, test := range []struct {
name string
cmd command
offset offset
length uint
want []uint8
ops []i2ctest.IO
expectErr bool
}{
{
name: "read 1 byte",
cmd: 0xAA,
offset: 0xBB,
length: 1,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xAA, 0xBB}},
{Addr: I2CAddr, R: []byte{0xCC}},
},
want: []byte{0xCC},
expectErr: false,
},
{
name: "read 4 bytes",
cmd: 0xAA,
offset: 0xBB,
length: 4,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xAA, 0xBB}},
{Addr: I2CAddr, R: []byte{0xCC, 0xDD, 0xEE, 0xFF}},
},
want: []byte{0xCC, 0xDD, 0xEE, 0xFF},
expectErr: false,
},
{
name: "invalid length",
cmd: 0xAA,
offset: 0xBB,
length: 0,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xAA, 0xBB}},
},
expectErr: true,
},
} {
t.Run(test.name, func(t *testing.T) {
b := i2ctest.Playback{
Ops: test.ops,
DontPanic: true,
}
defer b.Close()
dev := Dev{
c: &i2c.Dev{Bus: &b, Addr: I2CAddr},
variant: Tic36v4,
}
got, err := dev.getSegment(test.cmd, test.offset, test.length)
if test.expectErr {
if err == nil {
t.Fatalf("expected error, got: %v", err)
}
} else {
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(got, test.want) {
t.Fatalf("wanted: %d, got: %d", test.want, got)
}
}
})
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,486 @@
// 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 (
"errors"
"testing"
"periph.io/x/conn/v3/i2c"
"periph.io/x/conn/v3/i2c/i2ctest"
"periph.io/x/conn/v3/physic"
)
func TestNewI2C(t *testing.T) {
for _, test := range []struct {
name string
variant Variant
ops []i2ctest.IO
expectErr bool
}{
{
name: "success",
variant: TicT500,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xA1, 0x49}},
{Addr: I2CAddr, R: []byte{0x00}},
},
expectErr: false,
},
{
name: "invalid variant",
variant: Variant("periph"),
expectErr: true,
},
{
name: "connection failure",
variant: TicT500,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xA1, 0x49}},
},
expectErr: true,
},
} {
t.Run(test.name, func(t *testing.T) {
b := i2ctest.Playback{
Ops: test.ops,
DontPanic: true,
}
defer b.Close()
_, err := NewI2C(&b, test.variant, I2CAddr)
if test.expectErr && err == nil {
t.Fatalf("expected error, got: %v", err)
} else if !test.expectErr && err != nil {
t.Fatal(err)
}
})
}
}
func TestGetTargetPosition(t *testing.T) {
for _, test := range []struct {
name string
ops []i2ctest.IO
want int32
expectErr error
}{
{
name: "success",
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xA1, 0x09}},
{Addr: I2CAddr, R: []byte{byte(PlanningModeTargetPosition)}},
{Addr: I2CAddr, W: []byte{0xA1, 0x0A}},
{Addr: I2CAddr, R: []byte{0xEE, 0xDB, 0xEA, 0x0D}},
},
want: 0xDEADBEE,
expectErr: nil,
},
{
name: "incorrect planning mode",
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xA1, 0x09}},
{Addr: I2CAddr, R: []byte{byte(PlanningModeTargetVelocity)}},
{Addr: I2CAddr, W: []byte{0xA1, 0x0A}},
{Addr: I2CAddr, R: []byte{0xEE, 0xDB, 0xEA, 0x0D}},
},
expectErr: ErrIncorrectPlanningMode,
},
} {
t.Run(test.name, func(t *testing.T) {
b := i2ctest.Playback{
Ops: test.ops,
DontPanic: true,
}
defer b.Close()
dev := Dev{
c: &i2c.Dev{Bus: &b, Addr: I2CAddr},
variant: TicT825,
}
got, err := dev.GetTargetPosition()
if !errors.Is(err, test.expectErr) {
t.Fatalf("expected error: %v, got: %v", test.expectErr, err)
}
if got != test.want {
t.Fatalf("wanted: %d, got: %d", test.want, got)
}
})
}
}
func TestSetStepMode(t *testing.T) {
for _, test := range []struct {
name string
variant Variant
mode StepMode
ops []i2ctest.IO
expectErr error
}{
{
name: "success",
variant: TicT825,
mode: StepModeFull,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0x94, 0x00}},
},
expectErr: nil,
},
{
name: "invalid step mode",
variant: TicT825,
mode: StepMode(0xFF),
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0x94, 0xFF}},
},
expectErr: ErrInvalidSetting,
},
{
name: "unsupported variant",
variant: TicT500,
mode: StepModeMicrostep256,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0x94, 0x09}},
},
expectErr: ErrUnsupportedVariant,
},
} {
t.Run(test.name, func(t *testing.T) {
b := i2ctest.Playback{
Ops: test.ops,
DontPanic: true,
}
defer b.Close()
dev := Dev{
c: &i2c.Dev{Bus: &b, Addr: I2CAddr},
variant: test.variant,
}
err := dev.SetStepMode(test.mode)
if !errors.Is(err, test.expectErr) {
t.Fatalf("expected error: %v, got: %v", test.expectErr, err)
}
})
}
}
func TestSetDecayMode(t *testing.T) {
for _, test := range []struct {
name string
variant Variant
mode DecayMode
ops []i2ctest.IO
expectErr error
}{
{
name: "success",
variant: TicT825,
mode: DecayModeMixed,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0x92, 0x00}},
},
expectErr: nil,
},
{
name: "invalid decay mode",
variant: TicT825,
mode: DecayMode(0xFF),
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0x94, 0xFF}},
},
expectErr: ErrInvalidSetting,
},
{
name: "unsupported decay mode",
variant: TicT825,
mode: DecayModeMixed75,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0x94, 0x04}},
},
expectErr: ErrUnsupportedVariant,
},
{
name: "unsupported variant",
variant: TicT500,
mode: DecayModeMixed,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0x94, 0x00}},
},
expectErr: ErrUnsupportedVariant,
},
} {
t.Run(test.name, func(t *testing.T) {
b := i2ctest.Playback{
Ops: test.ops,
DontPanic: true,
}
defer b.Close()
dev := Dev{
c: &i2c.Dev{Bus: &b, Addr: I2CAddr},
variant: test.variant,
}
err := dev.SetDecayMode(test.mode)
if !errors.Is(err, test.expectErr) {
t.Fatalf("expected error: %v, got: %v", test.expectErr, err)
}
})
}
}
func TestGetCurrentLimit(t *testing.T) {
for _, test := range []struct {
name string
variant Variant
ops []i2ctest.IO
want physic.ElectricCurrent
}{
{
name: "T500 success",
variant: TicT500,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xA1, 0x4A}},
{Addr: I2CAddr, R: []byte{0x09}},
},
want: 1092 * physic.MilliAmpere,
},
{
name: "T249 success",
variant: TicT249,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xA1, 0x4A}},
{Addr: I2CAddr, R: []byte{0x0A}},
},
want: 400 * physic.MilliAmpere,
},
{
name: "36v4 success",
variant: Tic36v4,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xA1, 0x4A}},
{Addr: I2CAddr, R: []byte{0x0A}},
},
want: 716 * physic.MilliAmpere,
},
{
name: "T825 success",
variant: TicT825,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xA1, 0x4A}},
{Addr: I2CAddr, R: []byte{0x0A}},
},
want: 320 * physic.MilliAmpere,
},
} {
t.Run(test.name, func(t *testing.T) {
b := i2ctest.Playback{
Ops: test.ops,
DontPanic: true,
}
defer b.Close()
dev := Dev{
c: &i2c.Dev{Bus: &b, Addr: I2CAddr},
variant: test.variant,
}
got, err := dev.GetCurrentLimit()
if err != nil {
t.Fatal(err)
}
if got != test.want {
t.Fatalf("wanted: %v, got: %v", test.want, got)
}
})
}
}
func TestSetCurrentLimit(t *testing.T) {
for _, test := range []struct {
name string
variant Variant
limit physic.ElectricCurrent
ops []i2ctest.IO
want int32
}{
{
name: "T500 success",
variant: TicT500,
limit: 500 * physic.MilliAmpere,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0x91, 0x04}},
},
},
{
name: "36v4 success",
variant: Tic36v4,
limit: 500 * physic.MilliAmpere,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0x91, 0x06}},
},
},
{
name: "36v4 lower limit",
variant: Tic36v4,
limit: 10 * physic.NanoAmpere,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0x91, 0x00}},
},
},
{
name: "36v4 upper limit",
variant: Tic36v4,
limit: 10 * physic.Ampere,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0x91, 0x7F}},
},
},
} {
t.Run(test.name, func(t *testing.T) {
b := i2ctest.Playback{
Ops: test.ops,
DontPanic: true,
}
defer b.Close()
dev := Dev{
c: &i2c.Dev{Bus: &b, Addr: I2CAddr},
variant: test.variant,
}
err := dev.SetCurrentLimit(test.limit)
if err != nil {
t.Fatal(err)
}
})
}
}
func TestGetEnergized(t *testing.T) {
for _, test := range []struct {
name string
ops []i2ctest.IO
want bool
}{
{
name: "device energized",
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xA1, 0x01}},
{Addr: I2CAddr, R: []byte{0x01}},
},
want: true,
},
{
name: "device not energized",
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xA1, 0x01}},
{Addr: I2CAddr, R: []byte{0x00}},
},
want: false,
},
} {
t.Run(test.name, func(t *testing.T) {
b := i2ctest.Playback{
Ops: test.ops,
DontPanic: true,
}
defer b.Close()
dev := Dev{
c: &i2c.Dev{Bus: &b, Addr: I2CAddr},
variant: Tic36v4,
}
got, err := dev.IsEnergized()
if err != nil {
t.Fatal(err)
}
if got != test.want {
t.Fatalf("wanted: %t, got: %t", test.want, got)
}
})
}
}
func TestGetErrorsOccurred(t *testing.T) {
const want uint32 = 0xAABBCCDD
b := i2ctest.Playback{
Ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xA2, 0x04}},
{Addr: I2CAddr, R: []byte{0xDD, 0xCC, 0xBB, 0xAA}},
},
DontPanic: true,
}
defer b.Close()
dev := Dev{
c: &i2c.Dev{Bus: &b, Addr: I2CAddr},
variant: Tic36v4,
}
got, err := dev.GetErrorsOccurred()
if err != nil {
t.Error(err)
}
if got != want {
t.Errorf("wanted: %d, got: %d", want, got)
}
}
func TestGetPinState(t *testing.T) {
for _, test := range []struct {
name string
pin Pin
ops []i2ctest.IO
want PinState
expectErr error
}{
{
name: "success",
pin: PinRX,
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xA1, 0x48}},
{Addr: I2CAddr, R: []byte{0x80}},
},
want: PinStateOutputLow,
expectErr: nil,
},
{
name: "invalid pin",
pin: Pin(0xFF),
ops: []i2ctest.IO{
{Addr: I2CAddr, W: []byte{0xA1, 0x48}},
},
expectErr: ErrInvalidSetting,
},
} {
t.Run(test.name, func(t *testing.T) {
b := i2ctest.Playback{
Ops: test.ops,
DontPanic: true,
}
defer b.Close()
dev := Dev{
c: &i2c.Dev{Bus: &b, Addr: I2CAddr},
variant: Tic36v4,
}
got, err := dev.GetPinState(test.pin)
if !errors.Is(err, test.expectErr) {
t.Fatalf("expected error: %v, got: %v", test.expectErr, err)
}
if got != test.want {
t.Fatalf("wanted: %d, got: %d", test.want, got)
}
})
}
}
Loading…
Cancel
Save