Skip to content

Commit

Permalink
i2c: update PCA9685 driver to use same protocol as Adafruit Python lib
Browse files Browse the repository at this point in the history
Signed-off-by: Ron Evans <[email protected]>
  • Loading branch information
deadprogram committed May 22, 2019
1 parent 58db149 commit b3574b3
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 13 deletions.
81 changes: 74 additions & 7 deletions drivers/i2c/pca9685_driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const pca9685Address = 0x40

const (
PCA9685_MODE1 = 0x00
PCA9685_MODE2 = 0x01
PCA9685_PRESCALE = 0xFE
PCA9685_SUBADR1 = 0x02
PCA9685_SUBADR2 = 0x03
Expand All @@ -23,6 +24,12 @@ const (
PCA9685_ALLLED_ON_H = 0xFB
PCA9685_ALLLED_OFF_L = 0xFC
PCA9685_ALLLED_OFF_H = 0xFD

PCA9685_RESTART = 0x80
PCA9685_SLEEP = 0x10
PCA9685_ALLCALL = 0x01
PCA9685_INVRT = 0x10
PCA9685_OUTDRV = 0x04
)

// PCA9685Driver is a Gobot Driver for the PCA9685 16-channel 12-bit PWM/Servo controller.
Expand Down Expand Up @@ -101,14 +108,35 @@ func (p *PCA9685Driver) Start() (err error) {
return err
}

if _, err := p.connection.Write([]byte{PCA9685_MODE1, 0x00}); err != nil {
if err := p.SetAllPWM(0, 0); err != nil {
return err
}

if _, err := p.connection.Write([]byte{PCA9685_MODE2, PCA9685_OUTDRV}); err != nil {
return err
}

if _, err := p.connection.Write([]byte{PCA9685_MODE1, PCA9685_ALLCALL}); err != nil {
return err
}

time.Sleep(5 * time.Millisecond)

if _, err := p.connection.Write([]byte{byte(PCA9685_MODE1)}); err != nil {
return err
}
oldmode, err := p.connection.ReadByte()
if err != nil {
return err
}
oldmode = oldmode &^ byte(PCA9685_SLEEP)

if _, err := p.connection.Write([]byte{PCA9685_ALLLED_OFF_H, 0x10}); err != nil {
if _, err := p.connection.Write([]byte{PCA9685_MODE1, oldmode}); err != nil {
return err
}

time.Sleep(5 * time.Millisecond)

return
}

Expand All @@ -127,7 +155,46 @@ func (p *PCA9685Driver) Halt() (err error) {
// Most typically you set "on" to a zero value, and then set "off" to your desired duty.
//
func (p *PCA9685Driver) SetPWM(channel int, on uint16, off uint16) (err error) {
if _, err := p.connection.Write([]byte{byte(PCA9685_LED0_ON_L + 4*channel), byte(on), byte(on >> 8), byte(off), byte(off >> 8)}); err != nil {
if _, err := p.connection.Write([]byte{byte(PCA9685_LED0_ON_L + 4*channel), byte(on) & 0xFF}); err != nil {
return err
}

if _, err := p.connection.Write([]byte{byte(PCA9685_LED0_ON_H + 4*channel), byte(on >> 8)}); err != nil {
return err
}

if _, err := p.connection.Write([]byte{byte(PCA9685_LED0_OFF_L + 4*channel), byte(off) & 0xFF}); err != nil {
return err
}

if _, err := p.connection.Write([]byte{byte(PCA9685_LED0_OFF_H + 4*channel), byte(off >> 8)}); err != nil {
return err
}

return
}

// SetAllPWM sets all channels to a pwm value from 0-4096.
// Params:
// on uint16 - the time to start the pulse
// off uint16 - the time to stop the pulse
//
// Most typically you set "on" to a zero value, and then set "off" to your desired duty.
//
func (p *PCA9685Driver) SetAllPWM(on uint16, off uint16) (err error) {
if _, err := p.connection.Write([]byte{byte(PCA9685_ALLLED_ON_L), byte(on) & 0xFF}); err != nil {
return err
}

if _, err := p.connection.Write([]byte{byte(PCA9685_ALLLED_ON_H), byte(on >> 8)}); err != nil {
return err
}

if _, err := p.connection.Write([]byte{byte(PCA9685_ALLLED_OFF_L), byte(off) & 0xFF}); err != nil {
return err
}

if _, err := p.connection.Write([]byte{byte(PCA9685_ALLLED_OFF_H), byte(off >> 8)}); err != nil {
return err
}

Expand All @@ -149,8 +216,7 @@ func (p *PCA9685Driver) SetPWMFreq(freq float32) error {
if _, err := p.connection.Write([]byte{byte(PCA9685_MODE1)}); err != nil {
return err
}
data := make([]byte, 1)
oldmode, err := p.connection.Read(data)
oldmode, err := p.connection.ReadByte()
if err != nil {
return err
}
Expand All @@ -170,9 +236,10 @@ func (p *PCA9685Driver) SetPWMFreq(freq float32) error {
return err
}

time.Sleep(100 * time.Millisecond)
time.Sleep(5 * time.Millisecond)

// Enable response to All Call address, enable auto-increment, clear restart
if _, err := p.connection.Write([]byte{byte(PCA9685_MODE1), byte(oldmode | 0xa1)}); err != nil {
if _, err := p.connection.Write([]byte{byte(PCA9685_MODE1), byte(oldmode | 0x80)}); err != nil {
return err
}

Expand Down
42 changes: 36 additions & 6 deletions drivers/i2c/pca9685_driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,11 @@ func TestPCA9685DriverOptions(t *testing.T) {

// Methods
func TestPCA9685DriverStart(t *testing.T) {
pca := initTestPCA9685Driver()

pca, adaptor := initTestPCA9685DriverWithStubbedAdaptor()
adaptor.i2cReadImpl = func(b []byte) (int, error) {
copy(b, []byte{0x01})
return 1, nil
}
gobottest.Assert(t, pca.Start(), nil)
}

Expand All @@ -72,21 +75,32 @@ func TestPCA9685DriverStartWriteError(t *testing.T) {
}

func TestPCA9685DriverHalt(t *testing.T) {
pca := initTestPCA9685Driver()
pca, adaptor := initTestPCA9685DriverWithStubbedAdaptor()
adaptor.i2cReadImpl = func(b []byte) (int, error) {
copy(b, []byte{0x01})
return 1, nil
}
gobottest.Assert(t, pca.Start(), nil)
gobottest.Assert(t, pca.Halt(), nil)
}

func TestPCA9685DriverSetPWM(t *testing.T) {
pca := initTestPCA9685Driver()
pca, adaptor := initTestPCA9685DriverWithStubbedAdaptor()
adaptor.i2cReadImpl = func(b []byte) (int, error) {
copy(b, []byte{0x01})
return 1, nil
}
gobottest.Assert(t, pca.Start(), nil)
gobottest.Assert(t, pca.SetPWM(0, 0, 256), nil)
}

func TestPCA9685DriverSetPWMError(t *testing.T) {
pca, adaptor := initTestPCA9685DriverWithStubbedAdaptor()
adaptor.i2cReadImpl = func(b []byte) (int, error) {
copy(b, []byte{0x01})
return 1, nil
}
gobottest.Assert(t, pca.Start(), nil)

adaptor.i2cWriteImpl = func([]byte) (int, error) {
return 0, errors.New("write error")
}
Expand All @@ -95,6 +109,10 @@ func TestPCA9685DriverSetPWMError(t *testing.T) {

func TestPCA9685DriverSetPWMFreq(t *testing.T) {
pca, adaptor := initTestPCA9685DriverWithStubbedAdaptor()
adaptor.i2cReadImpl = func(b []byte) (int, error) {
copy(b, []byte{0x01})
return 1, nil
}
gobottest.Assert(t, pca.Start(), nil)

adaptor.i2cReadImpl = func(b []byte) (int, error) {
Expand All @@ -106,6 +124,10 @@ func TestPCA9685DriverSetPWMFreq(t *testing.T) {

func TestPCA9685DriverSetPWMFreqReadError(t *testing.T) {
pca, adaptor := initTestPCA9685DriverWithStubbedAdaptor()
adaptor.i2cReadImpl = func(b []byte) (int, error) {
copy(b, []byte{0x01})
return 1, nil
}
gobottest.Assert(t, pca.Start(), nil)

adaptor.i2cReadImpl = func(b []byte) (int, error) {
Expand All @@ -116,6 +138,10 @@ func TestPCA9685DriverSetPWMFreqReadError(t *testing.T) {

func TestPCA9685DriverSetPWMFreqWriteError(t *testing.T) {
pca, adaptor := initTestPCA9685DriverWithStubbedAdaptor()
adaptor.i2cReadImpl = func(b []byte) (int, error) {
copy(b, []byte{0x01})
return 1, nil
}
gobottest.Assert(t, pca.Start(), nil)

adaptor.i2cWriteImpl = func([]byte) (int, error) {
Expand All @@ -131,7 +157,11 @@ func TestPCA9685DriverSetName(t *testing.T) {
}

func TestPCA9685DriverCommands(t *testing.T) {
pca := initTestPCA9685Driver()
pca, adaptor := initTestPCA9685DriverWithStubbedAdaptor()
adaptor.i2cReadImpl = func(b []byte) (int, error) {
copy(b, []byte{0x01})
return 1, nil
}
pca.Start()

err := pca.Command("PwmWrite")(map[string]interface{}{"pin": "1", "val": "1"})
Expand Down

0 comments on commit b3574b3

Please sign in to comment.