Skip to content

Commit

Permalink
New driver for Adafruit DRV2605L
Browse files Browse the repository at this point in the history
Cleanup, added more tests for DRV2605L driver

Fixed documentation + formatting
  • Loading branch information
erkkah committed Feb 20, 2017
1 parent 3650241 commit c889755
Show file tree
Hide file tree
Showing 2 changed files with 338 additions and 0 deletions.
258 changes: 258 additions & 0 deletions drivers/i2c/drv2605l_driver.go
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)
}
80 changes: 80 additions & 0 deletions drivers/i2c/drv2605l_driver_test.go
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,
})
}

0 comments on commit c889755

Please sign in to comment.