forked from hybridgroup/gobot
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Cleanup, added more tests for DRV2605L driver Fixed documentation + formatting
- Loading branch information
Showing
2 changed files
with
338 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,258 @@ | ||
package i2c | ||
|
||
import ( | ||
"gobot.io/x/gobot" | ||
) | ||
|
||
// DRV2605Mode - operating mode | ||
type DRV2605Mode uint8 | ||
|
||
// Operating modes, for use in SetMode() | ||
const ( | ||
DRV2605ModeIntTrig DRV2605Mode = 0x00 | ||
DRV2605ModeExtTrigEdge = 0x01 | ||
DRV2605ModeExtTrigLvl = 0x02 | ||
DRV2605ModePWMAnalog = 0x03 | ||
DRV2605ModeAudioVibe = 0x04 | ||
DRV2605ModeRealtime = 0x05 | ||
DRV2605ModeDiagnose = 0x06 | ||
DRV2605ModeAutocal = 0x07 | ||
) | ||
|
||
const ( | ||
drv2605Address = 0x5A | ||
|
||
drv2605RegStatus = 0x00 | ||
drv2605RegMode = 0x01 | ||
|
||
drv2605Standby = 0x40 | ||
|
||
drv2605RegRTPin = 0x02 | ||
drv2605RegLibrary = 0x03 | ||
drv2605RegWaveSeq1 = 0x04 | ||
drv2605RegWaveSeq2 = 0x05 | ||
drv2605RegWaveSeq3 = 0x06 | ||
drv2605RegWaveSeq4 = 0x07 | ||
drv2605RegWaveSeq5 = 0x08 | ||
drv2605RegWaveSeq6 = 0x09 | ||
drv2605RegWaveSeq7 = 0x0A | ||
drv2605RegWaveSeq8 = 0x0B | ||
|
||
drv2605RegGo = 0x0C | ||
drv2605RegOverdrive = 0x0D | ||
drv2605RegSustainPos = 0x0E | ||
drv2605RegSustainNeg = 0x0F | ||
drv2605RegBreak = 0x10 | ||
drv2605RegAudioCtrl = 0x11 | ||
drv2605RegAudioMinLevel = 0x12 | ||
drv2605RegAudioMaxLevel = 0x13 | ||
drv2605RegAudioMinDrive = 0x14 | ||
drv2605RegAudioMaxDrive = 0x15 | ||
drv2605RegRatedV = 0x16 | ||
drv2605RegClampV = 0x17 | ||
drv2605RegAutocalComp = 0x18 | ||
drv2605RegAutocalEmp = 0x19 | ||
drv2605RegFeedback = 0x1A | ||
drv2605RegControl1 = 0x1B | ||
drv2605RegControl2 = 0x1C | ||
drv2605RegControl3 = 0x1D | ||
drv2605RegControl4 = 0x1E | ||
drv2605RegVBat = 0x21 | ||
drv2605RegLRAResoPeriod = 0x22 | ||
) | ||
|
||
// DRV2605LDriver is the gobot driver for the TI/Adafruit DRV2605L Haptic Controller | ||
// Device datasheet: http://www.ti.com/lit/ds/symlink/drv2605l.pdf | ||
// Inspired by the Adafruit Python driver by Sean Mealin. | ||
// | ||
// Basic use: | ||
// | ||
// haptic := i2c.NewDRV2605Driver(adaptor) | ||
// haptic.SetSequence([]byte{1, 13}) | ||
// haptic.Go() | ||
// | ||
type DRV2605LDriver struct { | ||
name string | ||
connection I2c | ||
} | ||
|
||
// NewDRV2605LDriver creates a new driver with the i2c interface for the DRV2605L device. | ||
func NewDRV2605LDriver(c I2c) *DRV2605LDriver { | ||
return &DRV2605LDriver{ | ||
name: "DRV2605L", | ||
connection: c, | ||
} | ||
} | ||
|
||
// Name returns the name of the device. | ||
func (d *DRV2605LDriver) Name() string { | ||
return d.name | ||
} | ||
|
||
// SetName sets the name of the device. | ||
func (d *DRV2605LDriver) SetName(name string) { | ||
d.name = name | ||
} | ||
|
||
// Connection returns the connection of the device. | ||
func (d *DRV2605LDriver) Connection() gobot.Connection { | ||
return d.connection.(gobot.Connection) | ||
} | ||
|
||
// Start initializes the device. | ||
func (d *DRV2605LDriver) Start() (err error) { | ||
if err := d.initialize(); err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
func (d *DRV2605LDriver) readByteRegister(reg int) (val uint8, err error) { | ||
var bytes []uint8 | ||
if bytes, err = d.connection.I2cRead(drv2605Address, reg); err != nil { | ||
return 0, err | ||
} | ||
return bytes[0], nil | ||
} | ||
|
||
func (d *DRV2605LDriver) writeByteRegister(reg uint8, val uint8) (err error) { | ||
err = d.connection.I2cWrite(drv2605Address, []byte{reg, val}) | ||
return err | ||
} | ||
|
||
func (d *DRV2605LDriver) writeByteRegisters(regValPairs [][2]uint8) (err error) { | ||
for _, rv := range regValPairs { | ||
reg, val := rv[0], rv[1] | ||
if err = d.connection.I2cWrite(drv2605Address, []uint8{reg, val}); err != nil { | ||
break | ||
} | ||
} | ||
return err | ||
} | ||
|
||
func (d *DRV2605LDriver) initialize() (err error) { | ||
if err = d.connection.I2cStart(drv2605Address); err != nil { | ||
return err | ||
} | ||
|
||
feedback, err := d.readByteRegister(drv2605RegFeedback) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
control, err := d.readByteRegister(drv2605RegControl3) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = d.writeByteRegisters([][2]uint8{ | ||
// leave standby, enter "internal trig" mode | ||
{drv2605RegMode, 0}, | ||
// turn off real-time play | ||
{drv2605RegRTPin, 0}, | ||
// init wave sequencer with "strong click" | ||
{drv2605RegWaveSeq1, 1}, | ||
{drv2605RegWaveSeq1, 0}, | ||
// no physical parameter tweaks | ||
{drv2605RegSustainPos, 0}, | ||
{drv2605RegSustainNeg, 0}, | ||
{drv2605RegBreak, 0}, | ||
// set up ERM open loop | ||
{drv2605RegFeedback, feedback & 0x7f}, | ||
{drv2605RegControl3, control | 0x20}, | ||
}) | ||
|
||
return err | ||
} | ||
|
||
// SetMode sets the device in one of the eight modes as described in the | ||
// datasheet. Defaults to mode 0, internal trig. | ||
func (d *DRV2605LDriver) SetMode(newMode DRV2605Mode) (err error) { | ||
mode, err := d.readByteRegister(drv2605RegMode) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// clear mode bits (lower three bits) | ||
mode &= 0xf1 | ||
// set new mode bits | ||
mode |= uint8(newMode) | ||
|
||
err = d.writeByteRegister(drv2605RegMode, mode) | ||
|
||
return err | ||
} | ||
|
||
// SetStandbyMode controls device low power mode | ||
func (d *DRV2605LDriver) SetStandbyMode(standby bool) (err error) { | ||
modeVal, err := d.readByteRegister(drv2605RegMode) | ||
if err != nil { | ||
return err | ||
} | ||
if standby { | ||
modeVal |= drv2605Standby | ||
} else { | ||
modeVal &= 0xFF ^ drv2605Standby | ||
} | ||
|
||
err = d.writeByteRegister(drv2605RegMode, modeVal) | ||
|
||
return err | ||
} | ||
|
||
// SelectLibrary selects which waveform library to play from, 1-7. | ||
// See datasheet for more info. | ||
func (d *DRV2605LDriver) SelectLibrary(library uint8) (err error) { | ||
err = d.writeByteRegister(drv2605RegLibrary, library&0x7) | ||
return err | ||
} | ||
|
||
// GetPauseWaveform returns a special waveform ID used in SetSequence() to encode | ||
// pauses between waveforms. Time is specified in tens of milliseconds | ||
// ranging from 0ms (delayTime10MS = 0) to 1270ms (delayTime10MS = 127). | ||
// Times out of range are clipped to fit. | ||
func (d *DRV2605LDriver) GetPauseWaveform(delayTime10MS uint8) (pauseID uint8) { | ||
if delayTime10MS > 127 { | ||
delayTime10MS = 127 | ||
} | ||
|
||
return delayTime10MS | 0x80 | ||
} | ||
|
||
// SetSequence sets the sequence of waveforms to be played by the sequencer, | ||
// specified by waveform id as described in the datasheet. | ||
// The sequencer can play at most 8 waveforms in sequence, longer | ||
// sequences will be truncated. | ||
// A waveform id of zero marks the end of the sequence. | ||
// Pauses can be encoded using GetPauseWaveform(). | ||
func (d *DRV2605LDriver) SetSequence(waveforms []uint8) (err error) { | ||
if len(waveforms) < 8 { | ||
waveforms = append(waveforms, 0) | ||
} | ||
if len(waveforms) > 8 { | ||
waveforms = waveforms[0:8] | ||
} | ||
for i, w := range waveforms { | ||
if err = d.writeByteRegister(uint8(drv2605RegWaveSeq1+i), w); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// Go plays the current sequence of waveforms. | ||
func (d *DRV2605LDriver) Go() (err error) { | ||
err = d.writeByteRegister(drv2605RegGo, 1) | ||
return err | ||
} | ||
|
||
// Halt halts the device. | ||
func (d *DRV2605LDriver) Halt() (err error) { | ||
// stop playback | ||
if err = d.writeByteRegister(drv2605RegGo, 0); err != nil { | ||
return err | ||
} | ||
|
||
// enter standby | ||
return d.writeByteRegister(drv2605RegMode, 1) | ||
} |
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,80 @@ | ||
package i2c | ||
|
||
import ( | ||
"bytes" | ||
"encoding/binary" | ||
"testing" | ||
|
||
"gobot.io/x/gobot" | ||
"gobot.io/x/gobot/gobottest" | ||
) | ||
|
||
var _ gobot.Driver = (*DRV2605LDriver)(nil) | ||
|
||
// --------- HELPERS | ||
|
||
func initTestDriverAndAdaptor() (*DRV2605LDriver, *i2cTestAdaptor) { | ||
adaptor := newI2cTestAdaptor() | ||
return NewDRV2605LDriver(adaptor), adaptor | ||
} | ||
|
||
// --------- TESTS | ||
|
||
func TestDRV2605LDriver(t *testing.T) { | ||
d, _ := initTestDriverAndAdaptor() | ||
gobottest.Refute(t, d.Connection(), nil) | ||
} | ||
|
||
func TestDRV2605LDriverStart(t *testing.T) { | ||
d, adaptor := initTestDriverAndAdaptor() | ||
|
||
adaptor.i2cReadImpl = func() ([]byte, error) { | ||
buf := new(bytes.Buffer) | ||
binary.Write(buf, binary.LittleEndian, uint8(42)) | ||
return buf.Bytes(), nil | ||
} | ||
|
||
gobottest.Assert(t, d.Start(), nil) | ||
} | ||
|
||
func TestDRV2605LDriverHalt(t *testing.T) { | ||
d, adaptor := initTestDriverAndAdaptor() | ||
|
||
gobottest.Assert(t, d.Halt(), nil) | ||
gobottest.Assert(t, adaptor.written, []byte{drv2605RegGo, 0, drv2605RegMode, 1}) | ||
} | ||
|
||
func TestDRV2605LDriverGetPause(t *testing.T) { | ||
d, _ := initTestDriverAndAdaptor() | ||
|
||
gobottest.Assert(t, d.GetPauseWaveform(0), uint8(0x80)) | ||
gobottest.Assert(t, d.GetPauseWaveform(1), uint8(0x81)) | ||
gobottest.Assert(t, d.GetPauseWaveform(128), d.GetPauseWaveform(127)) | ||
} | ||
|
||
func TestDRV2605LDriverSequenceTermination(t *testing.T) { | ||
d, adaptor := initTestDriverAndAdaptor() | ||
|
||
gobottest.Assert(t, d.SetSequence([]byte{1, 2}), nil) | ||
gobottest.Assert(t, adaptor.written, []byte{ | ||
drv2605RegWaveSeq1, 1, | ||
drv2605RegWaveSeq2, 2, | ||
drv2605RegWaveSeq3, 0, | ||
}) | ||
} | ||
|
||
func TestDRV2605LDriverSequenceTruncation(t *testing.T) { | ||
d, adaptor := initTestDriverAndAdaptor() | ||
|
||
gobottest.Assert(t, d.SetSequence([]byte{1, 2, 3, 4, 5, 6, 7, 8, 99, 100}), nil) | ||
gobottest.Assert(t, adaptor.written, []byte{ | ||
drv2605RegWaveSeq1, 1, | ||
drv2605RegWaveSeq2, 2, | ||
drv2605RegWaveSeq3, 3, | ||
drv2605RegWaveSeq4, 4, | ||
drv2605RegWaveSeq5, 5, | ||
drv2605RegWaveSeq6, 6, | ||
drv2605RegWaveSeq7, 7, | ||
drv2605RegWaveSeq8, 8, | ||
}) | ||
} |